[Session restore] Rename group name Enabled to Restore.
[chromium-blink-merge.git] / content / browser / renderer_host / render_widget_host_view_mac_unittest.mm
blobdc48cc1e99e663606210f3b54b7f887243482b3a
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/renderer_host/render_widget_host_view_mac.h"
7 #include "base/mac/mac_util.h"
8 #include "base/mac/scoped_nsautorelease_pool.h"
9 #include "base/mac/sdk_forward_declarations.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "content/browser/browser_thread_impl.h"
12 #include "content/browser/compositor/test/no_transport_image_transport_factory.h"
13 #include "content/browser/frame_host/render_widget_host_view_guest.h"
14 #include "content/browser/gpu/compositor_util.h"
15 #include "content/browser/renderer_host/render_widget_host_delegate.h"
16 #include "content/common/gpu/gpu_messages.h"
17 #include "content/common/input_messages.h"
18 #include "content/common/view_messages.h"
19 #include "content/public/browser/notification_types.h"
20 #include "content/public/browser/render_widget_host_view_mac_delegate.h"
21 #include "content/public/test/mock_render_process_host.h"
22 #include "content/public/test/test_browser_context.h"
23 #include "content/public/test/test_utils.h"
24 #include "content/test/test_render_view_host.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27 #include "ui/events/test/cocoa_test_event_utils.h"
28 #import "ui/gfx/test/ui_cocoa_test_helper.h"
30 // Helper class with methods used to mock -[NSEvent phase], used by
31 // |MockScrollWheelEventWithPhase()|.
32 @interface MockPhaseMethods : NSObject {
35 - (NSEventPhase)phaseBegan;
36 - (NSEventPhase)phaseChanged;
37 - (NSEventPhase)phaseEnded;
38 @end
40 @implementation MockPhaseMethods
42 - (NSEventPhase)phaseBegan {
43   return NSEventPhaseBegan;
45 - (NSEventPhase)phaseChanged {
46   return NSEventPhaseChanged;
48 - (NSEventPhase)phaseEnded {
49   return NSEventPhaseEnded;
52 @end
54 @interface MockRenderWidgetHostViewMacDelegate
55     : NSObject<RenderWidgetHostViewMacDelegate> {
56   BOOL unhandledWheelEventReceived_;
59 @property(nonatomic) BOOL unhandledWheelEventReceived;
61 @end
63 @implementation MockRenderWidgetHostViewMacDelegate
65 @synthesize unhandledWheelEventReceived = unhandledWheelEventReceived_;
67 - (void)rendererHandledWheelEvent:(const blink::WebMouseWheelEvent&)event
68                          consumed:(BOOL)consumed {
69   if (!consumed)
70     unhandledWheelEventReceived_ = true;
72 - (void)touchesBeganWithEvent:(NSEvent*)event {}
73 - (void)touchesMovedWithEvent:(NSEvent*)event {}
74 - (void)touchesCancelledWithEvent:(NSEvent*)event {}
75 - (void)touchesEndedWithEvent:(NSEvent*)event {}
76 - (void)beginGestureWithEvent:(NSEvent*)event {}
77 - (void)endGestureWithEvent:(NSEvent*)event {}
78 - (BOOL)canRubberbandLeft:(NSView*)view {
79   return true;
81 - (BOOL)canRubberbandRight:(NSView*)view {
82   return true;
85 @end
87 namespace content {
89 namespace {
91 class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate {
92  public:
93   MockRenderWidgetHostDelegate() {}
94   ~MockRenderWidgetHostDelegate() override {}
97 class MockRenderWidgetHostImpl : public RenderWidgetHostImpl {
98  public:
99   MockRenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,
100                            RenderProcessHost* process,
101                            int routing_id)
102       : RenderWidgetHostImpl(delegate, process, routing_id, false) {
103   }
105   MOCK_METHOD0(Focus, void());
106   MOCK_METHOD0(Blur, void());
109 // Generates the |length| of composition rectangle vector and save them to
110 // |output|. It starts from |origin| and each rectangle contains |unit_size|.
111 void GenerateCompositionRectArray(const gfx::Point& origin,
112                                   const gfx::Size& unit_size,
113                                   size_t length,
114                                   const std::vector<size_t>& break_points,
115                                   std::vector<gfx::Rect>* output) {
116   DCHECK(output);
117   output->clear();
119   std::queue<int> break_point_queue;
120   for (size_t i = 0; i < break_points.size(); ++i)
121     break_point_queue.push(break_points[i]);
122   break_point_queue.push(length);
123   size_t next_break_point = break_point_queue.front();
124   break_point_queue.pop();
126   gfx::Rect current_rect(origin, unit_size);
127   for (size_t i = 0; i < length; ++i) {
128     if (i == next_break_point) {
129       current_rect.set_x(origin.x());
130       current_rect.set_y(current_rect.y() + current_rect.height());
131       next_break_point = break_point_queue.front();
132       break_point_queue.pop();
133     }
134     output->push_back(current_rect);
135     current_rect.set_x(current_rect.right());
136   }
139 gfx::Rect GetExpectedRect(const gfx::Point& origin,
140                           const gfx::Size& size,
141                           const gfx::Range& range,
142                           int line_no) {
143   return gfx::Rect(
144       origin.x() + range.start() * size.width(),
145       origin.y() + line_no * size.height(),
146       range.length() * size.width(),
147       size.height());
150 // Returns NSScrollWheel event that mocks -phase. |mockPhaseSelector| should
151 // correspond to a method in |MockPhaseMethods| that returns the desired phase.
152 NSEvent* MockScrollWheelEventWithPhase(SEL mockPhaseSelector, int32_t delta) {
153   CGEventRef cg_event =
154       CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitLine, 1, delta, 0);
155   NSEvent* event = [NSEvent eventWithCGEvent:cg_event];
156   CFRelease(cg_event);
157   method_setImplementation(
158       class_getInstanceMethod([NSEvent class], @selector(phase)),
159       [MockPhaseMethods instanceMethodForSelector:mockPhaseSelector]);
160   return event;
163 }  // namespace
165 class RenderWidgetHostViewMacTest : public RenderViewHostImplTestHarness {
166  public:
167   RenderWidgetHostViewMacTest() : old_rwhv_(NULL), rwhv_mac_(NULL) {}
169   void SetUp() override {
170     RenderViewHostImplTestHarness::SetUp();
171     if (IsDelegatedRendererEnabled()) {
172       ImageTransportFactory::InitializeForUnitTests(
173           scoped_ptr<ImageTransportFactory>(
174               new NoTransportImageTransportFactory));
175     }
177     // TestRenderViewHost's destruction assumes that its view is a
178     // TestRenderWidgetHostView, so store its view and reset it back to the
179     // stored view in |TearDown()|.
180     old_rwhv_ = rvh()->GetView();
182     // Owned by its |cocoa_view()|, i.e. |rwhv_cocoa_|.
183     rwhv_mac_ = new RenderWidgetHostViewMac(rvh(), false);
184     rwhv_cocoa_.reset([rwhv_mac_->cocoa_view() retain]);
185   }
186   void TearDown() override {
187     // Make sure the rwhv_mac_ is gone once the superclass's |TearDown()| runs.
188     rwhv_cocoa_.reset();
189     RecycleAndWait();
191     // See comment in SetUp().
192     test_rvh()->SetView(static_cast<RenderWidgetHostViewBase*>(old_rwhv_));
194     if (IsDelegatedRendererEnabled())
195       ImageTransportFactory::Terminate();
196     RenderViewHostImplTestHarness::TearDown();
197   }
199   void RecycleAndWait() {
200     pool_.Recycle();
201     base::MessageLoop::current()->RunUntilIdle();
202     pool_.Recycle();
203   }
204  protected:
205  private:
206   // This class isn't derived from PlatformTest.
207   base::mac::ScopedNSAutoreleasePool pool_;
209   RenderWidgetHostView* old_rwhv_;
211  protected:
212   RenderWidgetHostViewMac* rwhv_mac_;
213   base::scoped_nsobject<RenderWidgetHostViewCocoa> rwhv_cocoa_;
215  private:
216   DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewMacTest);
219 TEST_F(RenderWidgetHostViewMacTest, Basic) {
222 TEST_F(RenderWidgetHostViewMacTest, AcceptsFirstResponder) {
223   // The RWHVCocoa should normally accept first responder status.
224   EXPECT_TRUE([rwhv_cocoa_.get() acceptsFirstResponder]);
227 TEST_F(RenderWidgetHostViewMacTest, Fullscreen) {
228   rwhv_mac_->InitAsFullscreen(NULL);
229   EXPECT_TRUE(rwhv_mac_->pepper_fullscreen_window());
231   // Break the reference cycle caused by pepper_fullscreen_window() without
232   // an <esc> event. See comment in
233   // release_pepper_fullscreen_window_for_testing().
234   rwhv_mac_->release_pepper_fullscreen_window_for_testing();
237 // Verify that escape key down in fullscreen mode suppressed the keyup event on
238 // the parent.
239 TEST_F(RenderWidgetHostViewMacTest, FullscreenCloseOnEscape) {
240   // Use our own RWH since we need to destroy it.
241   MockRenderWidgetHostDelegate delegate;
242   TestBrowserContext browser_context;
243   MockRenderProcessHost* process_host =
244       new MockRenderProcessHost(&browser_context);
245   // Owned by its |cocoa_view()|.
246   RenderWidgetHostImpl* rwh = new RenderWidgetHostImpl(
247       &delegate, process_host, MSG_ROUTING_NONE, false);
248   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(rwh, false);
250   view->InitAsFullscreen(rwhv_mac_);
252   WindowedNotificationObserver observer(
253       NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
254       Source<RenderWidgetHost>(rwh));
255   EXPECT_FALSE([rwhv_mac_->cocoa_view() suppressNextEscapeKeyUp]);
257   // Escape key down. Should close window and set |suppressNextEscapeKeyUp| on
258   // the parent.
259   [view->cocoa_view() keyEvent:
260       cocoa_test_event_utils::KeyEventWithKeyCode(53, 27, NSKeyDown, 0)];
261   observer.Wait();
262   EXPECT_TRUE([rwhv_mac_->cocoa_view() suppressNextEscapeKeyUp]);
264   // Escape key up on the parent should clear |suppressNextEscapeKeyUp|.
265   [rwhv_mac_->cocoa_view() keyEvent:
266       cocoa_test_event_utils::KeyEventWithKeyCode(53, 27, NSKeyUp, 0)];
267   EXPECT_FALSE([rwhv_mac_->cocoa_view() suppressNextEscapeKeyUp]);
270 // Test that command accelerators which destroy the fullscreen window
271 // don't crash when forwarded via the window's responder machinery.
272 TEST_F(RenderWidgetHostViewMacTest, AcceleratorDestroy) {
273   // Use our own RWH since we need to destroy it.
274   MockRenderWidgetHostDelegate delegate;
275   TestBrowserContext browser_context;
276   MockRenderProcessHost* process_host =
277       new MockRenderProcessHost(&browser_context);
278   // Owned by its |cocoa_view()|.
279   RenderWidgetHostImpl* rwh = new RenderWidgetHostImpl(
280       &delegate, process_host, MSG_ROUTING_NONE, false);
281   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(rwh, false);
283   view->InitAsFullscreen(rwhv_mac_);
285   WindowedNotificationObserver observer(
286       NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
287       Source<RenderWidgetHost>(rwh));
289   // Command-ESC will destroy the view, while the window is still in
290   // |-performKeyEquivalent:|.  There are other cases where this can
291   // happen, Command-ESC is the easiest to trigger.
292   [[view->cocoa_view() window] performKeyEquivalent:
293       cocoa_test_event_utils::KeyEventWithKeyCode(
294           53, 27, NSKeyDown, NSCommandKeyMask)];
295   observer.Wait();
298 TEST_F(RenderWidgetHostViewMacTest, GetFirstRectForCharacterRangeCaretCase) {
299   const base::string16 kDummyString = base::UTF8ToUTF16("hogehoge");
300   const size_t kDummyOffset = 0;
302   gfx::Rect caret_rect(10, 11, 0, 10);
303   gfx::Range caret_range(0, 0);
304   ViewHostMsg_SelectionBounds_Params params;
306   NSRect rect;
307   NSRange actual_range;
308   rwhv_mac_->SelectionChanged(kDummyString, kDummyOffset, caret_range);
309   params.anchor_rect = params.focus_rect = caret_rect;
310   params.anchor_dir = params.focus_dir = blink::WebTextDirectionLeftToRight;
311   rwhv_mac_->SelectionBoundsChanged(params);
312   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
313         caret_range.ToNSRange(),
314         &rect,
315         &actual_range));
316   EXPECT_EQ(caret_rect, gfx::Rect(NSRectToCGRect(rect)));
317   EXPECT_EQ(caret_range, gfx::Range(actual_range));
319   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
320         gfx::Range(0, 1).ToNSRange(),
321         &rect,
322         &actual_range));
323   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
324         gfx::Range(1, 1).ToNSRange(),
325         &rect,
326         &actual_range));
327   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
328         gfx::Range(2, 3).ToNSRange(),
329         &rect,
330         &actual_range));
332   // Caret moved.
333   caret_rect = gfx::Rect(20, 11, 0, 10);
334   caret_range = gfx::Range(1, 1);
335   params.anchor_rect = params.focus_rect = caret_rect;
336   rwhv_mac_->SelectionChanged(kDummyString, kDummyOffset, caret_range);
337   rwhv_mac_->SelectionBoundsChanged(params);
338   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
339         caret_range.ToNSRange(),
340         &rect,
341         &actual_range));
342   EXPECT_EQ(caret_rect, gfx::Rect(NSRectToCGRect(rect)));
343   EXPECT_EQ(caret_range, gfx::Range(actual_range));
345   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
346         gfx::Range(0, 0).ToNSRange(),
347         &rect,
348         &actual_range));
349   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
350         gfx::Range(1, 2).ToNSRange(),
351         &rect,
352         &actual_range));
353   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
354         gfx::Range(2, 3).ToNSRange(),
355         &rect,
356         &actual_range));
358   // No caret.
359   caret_range = gfx::Range(1, 2);
360   rwhv_mac_->SelectionChanged(kDummyString, kDummyOffset, caret_range);
361   params.anchor_rect = caret_rect;
362   params.focus_rect = gfx::Rect(30, 11, 0, 10);
363   rwhv_mac_->SelectionBoundsChanged(params);
364   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
365         gfx::Range(0, 0).ToNSRange(),
366         &rect,
367         &actual_range));
368   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
369         gfx::Range(0, 1).ToNSRange(),
370         &rect,
371         &actual_range));
372   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
373         gfx::Range(1, 1).ToNSRange(),
374         &rect,
375         &actual_range));
376   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
377         gfx::Range(1, 2).ToNSRange(),
378         &rect,
379         &actual_range));
380   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
381         gfx::Range(2, 2).ToNSRange(),
382         &rect,
383         &actual_range));
386 TEST_F(RenderWidgetHostViewMacTest, UpdateCompositionSinglelineCase) {
387   const gfx::Point kOrigin(10, 11);
388   const gfx::Size kBoundsUnit(10, 20);
390   NSRect rect;
391   // Make sure not crashing by passing NULL pointer instead of |actual_range|.
392   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
393       gfx::Range(0, 0).ToNSRange(),
394       &rect,
395       NULL));
397   // If there are no update from renderer, always returned caret position.
398   NSRange actual_range;
399   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
400       gfx::Range(0, 0).ToNSRange(),
401       &rect,
402       &actual_range));
403   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
404       gfx::Range(0, 1).ToNSRange(),
405       &rect,
406       &actual_range));
407   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
408       gfx::Range(1, 0).ToNSRange(),
409       &rect,
410       &actual_range));
411   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
412       gfx::Range(1, 1).ToNSRange(),
413       &rect,
414       &actual_range));
415   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
416       gfx::Range(1, 2).ToNSRange(),
417       &rect,
418       &actual_range));
420   // If the firstRectForCharacterRange is failed in renderer, empty rect vector
421   // is sent. Make sure this does not crash.
422   rwhv_mac_->ImeCompositionRangeChanged(gfx::Range(10, 12),
423                                         std::vector<gfx::Rect>());
424   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
425       gfx::Range(10, 11).ToNSRange(),
426       &rect,
427       NULL));
429   const int kCompositionLength = 10;
430   std::vector<gfx::Rect> composition_bounds;
431   const int kCompositionStart = 3;
432   const gfx::Range kCompositionRange(kCompositionStart,
433                                     kCompositionStart + kCompositionLength);
434   GenerateCompositionRectArray(kOrigin,
435                                kBoundsUnit,
436                                kCompositionLength,
437                                std::vector<size_t>(),
438                                &composition_bounds);
439   rwhv_mac_->ImeCompositionRangeChanged(kCompositionRange, composition_bounds);
441   // Out of range requests will return caret position.
442   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
443       gfx::Range(0, 0).ToNSRange(),
444       &rect,
445       &actual_range));
446   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
447       gfx::Range(1, 1).ToNSRange(),
448       &rect,
449       &actual_range));
450   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
451       gfx::Range(1, 2).ToNSRange(),
452       &rect,
453       &actual_range));
454   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
455       gfx::Range(2, 2).ToNSRange(),
456       &rect,
457       &actual_range));
458   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
459       gfx::Range(13, 14).ToNSRange(),
460       &rect,
461       &actual_range));
462   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
463       gfx::Range(14, 15).ToNSRange(),
464       &rect,
465       &actual_range));
467   for (int i = 0; i <= kCompositionLength; ++i) {
468     for (int j = 0; j <= kCompositionLength - i; ++j) {
469       const gfx::Range range(i, i + j);
470       const gfx::Rect expected_rect = GetExpectedRect(kOrigin,
471                                                       kBoundsUnit,
472                                                       range,
473                                                       0);
474       const NSRange request_range = gfx::Range(
475           kCompositionStart + range.start(),
476           kCompositionStart + range.end()).ToNSRange();
477       EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
478             request_range,
479             &rect,
480             &actual_range));
481       EXPECT_EQ(gfx::Range(request_range), gfx::Range(actual_range));
482       EXPECT_EQ(expected_rect, gfx::Rect(NSRectToCGRect(rect)));
484       // Make sure not crashing by passing NULL pointer instead of
485       // |actual_range|.
486       EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
487             request_range,
488             &rect,
489             NULL));
490     }
491   }
494 TEST_F(RenderWidgetHostViewMacTest, UpdateCompositionMultilineCase) {
495   const gfx::Point kOrigin(10, 11);
496   const gfx::Size kBoundsUnit(10, 20);
497   NSRect rect;
499   const int kCompositionLength = 30;
500   std::vector<gfx::Rect> composition_bounds;
501   const gfx::Range kCompositionRange(0, kCompositionLength);
502   // Set breaking point at 10 and 20.
503   std::vector<size_t> break_points;
504   break_points.push_back(10);
505   break_points.push_back(20);
506   GenerateCompositionRectArray(kOrigin,
507                                kBoundsUnit,
508                                kCompositionLength,
509                                break_points,
510                                &composition_bounds);
511   rwhv_mac_->ImeCompositionRangeChanged(kCompositionRange, composition_bounds);
513   // Range doesn't contain line breaking point.
514   gfx::Range range;
515   range = gfx::Range(5, 8);
516   NSRange actual_range;
517   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
518                                                              &rect,
519                                                              &actual_range));
520   EXPECT_EQ(range, gfx::Range(actual_range));
521   EXPECT_EQ(
522       GetExpectedRect(kOrigin, kBoundsUnit, range, 0),
523       gfx::Rect(NSRectToCGRect(rect)));
524   range = gfx::Range(15, 18);
525   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
526                                                              &rect,
527                                                              &actual_range));
528   EXPECT_EQ(range, gfx::Range(actual_range));
529   EXPECT_EQ(
530       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 8), 1),
531       gfx::Rect(NSRectToCGRect(rect)));
532   range = gfx::Range(25, 28);
533   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
534                                                              &rect,
535                                                              &actual_range));
536   EXPECT_EQ(range, gfx::Range(actual_range));
537   EXPECT_EQ(
538       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 8), 2),
539       gfx::Rect(NSRectToCGRect(rect)));
541   // Range contains line breaking point.
542   range = gfx::Range(8, 12);
543   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
544                                                              &rect,
545                                                              &actual_range));
546   EXPECT_EQ(gfx::Range(8, 10), gfx::Range(actual_range));
547   EXPECT_EQ(
548       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(8, 10), 0),
549       gfx::Rect(NSRectToCGRect(rect)));
550   range = gfx::Range(18, 22);
551   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
552                                                              &rect,
553                                                              &actual_range));
554   EXPECT_EQ(gfx::Range(18, 20), gfx::Range(actual_range));
555   EXPECT_EQ(
556       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(8, 10), 1),
557       gfx::Rect(NSRectToCGRect(rect)));
559   // Start point is line breaking point.
560   range = gfx::Range(10, 12);
561   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
562                                                              &rect,
563                                                              &actual_range));
564   EXPECT_EQ(gfx::Range(10, 12), gfx::Range(actual_range));
565   EXPECT_EQ(
566       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 2), 1),
567       gfx::Rect(NSRectToCGRect(rect)));
568   range = gfx::Range(20, 22);
569   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
570                                                              &rect,
571                                                              &actual_range));
572   EXPECT_EQ(gfx::Range(20, 22), gfx::Range(actual_range));
573   EXPECT_EQ(
574       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 2), 2),
575       gfx::Rect(NSRectToCGRect(rect)));
577   // End point is line breaking point.
578   range = gfx::Range(5, 10);
579   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
580                                                              &rect,
581                                                              &actual_range));
582   EXPECT_EQ(gfx::Range(5, 10), gfx::Range(actual_range));
583   EXPECT_EQ(
584       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 10), 0),
585       gfx::Rect(NSRectToCGRect(rect)));
586   range = gfx::Range(15, 20);
587   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
588                                                              &rect,
589                                                              &actual_range));
590   EXPECT_EQ(gfx::Range(15, 20), gfx::Range(actual_range));
591   EXPECT_EQ(
592       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 10), 1),
593       gfx::Rect(NSRectToCGRect(rect)));
595   // Start and end point are same line breaking point.
596   range = gfx::Range(10, 10);
597   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
598                                                              &rect,
599                                                              &actual_range));
600   EXPECT_EQ(gfx::Range(10, 10), gfx::Range(actual_range));
601   EXPECT_EQ(
602       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 0), 1),
603       gfx::Rect(NSRectToCGRect(rect)));
604   range = gfx::Range(20, 20);
605   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
606                                                              &rect,
607                                                              &actual_range));
608   EXPECT_EQ(gfx::Range(20, 20), gfx::Range(actual_range));
609   EXPECT_EQ(
610       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 0), 2),
611       gfx::Rect(NSRectToCGRect(rect)));
613   // Start and end point are different line breaking point.
614   range = gfx::Range(10, 20);
615   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
616                                                              &rect,
617                                                              &actual_range));
618   EXPECT_EQ(gfx::Range(10, 20), gfx::Range(actual_range));
619   EXPECT_EQ(
620       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 10), 1),
621       gfx::Rect(NSRectToCGRect(rect)));
624 // Verify that |SetActive()| calls |RenderWidgetHostImpl::Blur()| and
625 // |RenderWidgetHostImp::Focus()|.
626 TEST_F(RenderWidgetHostViewMacTest, BlurAndFocusOnSetActive) {
627   MockRenderWidgetHostDelegate delegate;
628   TestBrowserContext browser_context;
629   MockRenderProcessHost* process_host =
630       new MockRenderProcessHost(&browser_context);
632   // Owned by its |cocoa_view()|.
633   MockRenderWidgetHostImpl* rwh = new MockRenderWidgetHostImpl(
634       &delegate, process_host, MSG_ROUTING_NONE);
635   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(rwh, false);
637   base::scoped_nsobject<CocoaTestHelperWindow> window(
638       [[CocoaTestHelperWindow alloc] init]);
639   [[window contentView] addSubview:view->cocoa_view()];
641   EXPECT_CALL(*rwh, Focus());
642   [window makeFirstResponder:view->cocoa_view()];
643   testing::Mock::VerifyAndClearExpectations(rwh);
645   EXPECT_CALL(*rwh, Blur());
646   view->SetActive(false);
647   testing::Mock::VerifyAndClearExpectations(rwh);
649   EXPECT_CALL(*rwh, Focus());
650   view->SetActive(true);
651   testing::Mock::VerifyAndClearExpectations(rwh);
653   // Unsetting first responder should blur.
654   EXPECT_CALL(*rwh, Blur());
655   [window makeFirstResponder:nil];
656   testing::Mock::VerifyAndClearExpectations(rwh);
658   // |SetActive()| shoud not focus if view is not first responder.
659   EXPECT_CALL(*rwh, Focus()).Times(0);
660   view->SetActive(true);
661   testing::Mock::VerifyAndClearExpectations(rwh);
663   // Clean up.
664   rwh->Shutdown();
667 TEST_F(RenderWidgetHostViewMacTest, ScrollWheelEndEventDelivery) {
668   // This tests Lion+ functionality, so don't run the test pre-Lion.
669   if (!base::mac::IsOSLionOrLater())
670     return;
672   // Initialize the view associated with a MockRenderWidgetHostImpl, rather than
673   // the MockRenderProcessHost that is set up by the test harness which mocks
674   // out |OnMessageReceived()|.
675   TestBrowserContext browser_context;
676   MockRenderProcessHost* process_host =
677       new MockRenderProcessHost(&browser_context);
678   MockRenderWidgetHostDelegate delegate;
679   MockRenderWidgetHostImpl* host = new MockRenderWidgetHostImpl(
680       &delegate, process_host, MSG_ROUTING_NONE);
681   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(host, false);
683   // Send an initial wheel event with NSEventPhaseBegan to the view.
684   NSEvent* event1 = MockScrollWheelEventWithPhase(@selector(phaseBegan), 0);
685   [view->cocoa_view() scrollWheel:event1];
686   ASSERT_EQ(1U, process_host->sink().message_count());
688   // Send an ACK for the first wheel event, so that the queue will be flushed.
689   InputHostMsg_HandleInputEvent_ACK_Params ack;
690   ack.type = blink::WebInputEvent::MouseWheel;
691   ack.state = INPUT_EVENT_ACK_STATE_CONSUMED;
692   scoped_ptr<IPC::Message> response(
693       new InputHostMsg_HandleInputEvent_ACK(0, ack));
694   host->OnMessageReceived(*response);
696   // Post the NSEventPhaseEnded wheel event to NSApp and check whether the
697   // render view receives it.
698   NSEvent* event2 = MockScrollWheelEventWithPhase(@selector(phaseEnded), 0);
699   [NSApp postEvent:event2 atStart:NO];
700   base::MessageLoop::current()->RunUntilIdle();
701   ASSERT_EQ(2U, process_host->sink().message_count());
703   // Clean up.
704   host->Shutdown();
707 TEST_F(RenderWidgetHostViewMacTest, IgnoreEmptyUnhandledWheelEvent) {
708   // This tests Lion+ functionality, so don't run the test pre-Lion.
709   if (!base::mac::IsOSLionOrLater())
710     return;
712   // Initialize the view associated with a MockRenderWidgetHostImpl, rather than
713   // the MockRenderProcessHost that is set up by the test harness which mocks
714   // out |OnMessageReceived()|.
715   TestBrowserContext browser_context;
716   MockRenderProcessHost* process_host =
717       new MockRenderProcessHost(&browser_context);
718   MockRenderWidgetHostDelegate delegate;
719   MockRenderWidgetHostImpl* host = new MockRenderWidgetHostImpl(
720       &delegate, process_host, MSG_ROUTING_NONE);
721   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(host, false);
723   // Add a delegate to the view.
724   base::scoped_nsobject<MockRenderWidgetHostViewMacDelegate> view_delegate(
725       [[MockRenderWidgetHostViewMacDelegate alloc] init]);
726   view->SetDelegate(view_delegate.get());
728   // Send an initial wheel event for scrolling by 3 lines.
729   NSEvent* event1 = MockScrollWheelEventWithPhase(@selector(phaseBegan), 3);
730   [view->cocoa_view() scrollWheel:event1];
731   ASSERT_EQ(1U, process_host->sink().message_count());
732   process_host->sink().ClearMessages();
734   // Indicate that the wheel event was unhandled.
735   InputHostMsg_HandleInputEvent_ACK_Params unhandled_ack;
736   unhandled_ack.type = blink::WebInputEvent::MouseWheel;
737   unhandled_ack.state = INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
738   scoped_ptr<IPC::Message> response1(
739       new InputHostMsg_HandleInputEvent_ACK(0, unhandled_ack));
740   host->OnMessageReceived(*response1);
742   // Check that the view delegate got an unhandled wheel event.
743   ASSERT_EQ(YES, view_delegate.get().unhandledWheelEventReceived);
744   view_delegate.get().unhandledWheelEventReceived = NO;
746   // Send another wheel event, this time for scrolling by 0 lines (empty event).
747   NSEvent* event2 = MockScrollWheelEventWithPhase(@selector(phaseChanged), 0);
748   [view->cocoa_view() scrollWheel:event2];
749   ASSERT_EQ(1U, process_host->sink().message_count());
751   // Indicate that the wheel event was also unhandled.
752   scoped_ptr<IPC::Message> response2(
753       new InputHostMsg_HandleInputEvent_ACK(0, unhandled_ack));
754   host->OnMessageReceived(*response2);
756   // Check that the view delegate ignored the empty unhandled wheel event.
757   ASSERT_EQ(NO, view_delegate.get().unhandledWheelEventReceived);
759   // Clean up.
760   host->Shutdown();
763 // Tests that when view initiated shutdown happens (i.e. RWHView is deleted
764 // before RWH), we clean up properly and don't leak the RWHVGuest.
765 TEST_F(RenderWidgetHostViewMacTest, GuestViewDoesNotLeak) {
766   MockRenderWidgetHostDelegate delegate;
767   TestBrowserContext browser_context;
768   MockRenderProcessHost* process_host =
769       new MockRenderProcessHost(&browser_context);
771   // Owned by its |cocoa_view()|.
772   MockRenderWidgetHostImpl* rwh = new MockRenderWidgetHostImpl(
773       &delegate, process_host, MSG_ROUTING_NONE);
774   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(rwh, true);
776   // Add a delegate to the view.
777   base::scoped_nsobject<MockRenderWidgetHostViewMacDelegate> view_delegate(
778       [[MockRenderWidgetHostViewMacDelegate alloc] init]);
779   view->SetDelegate(view_delegate.get());
781   base::WeakPtr<RenderWidgetHostViewBase> guest_rwhv_weak =
782       (new RenderWidgetHostViewGuest(
783            rwh, NULL, view->GetWeakPtr()))->GetWeakPtr();
785   // Remove the cocoa_view() so |view| also goes away before |rwh|.
786   {
787     base::scoped_nsobject<RenderWidgetHostViewCocoa> rwhv_cocoa;
788     rwhv_cocoa.reset([view->cocoa_view() retain]);
789   }
790   RecycleAndWait();
792   // Clean up.
793   rwh->Shutdown();
795   // Let |guest_rwhv_weak| have a chance to delete itself.
796   base::RunLoop run_loop;
797   content::BrowserThread::PostTask(
798       content::BrowserThread::UI, FROM_HERE, run_loop.QuitClosure());
799   run_loop.Run();
801   ASSERT_FALSE(guest_rwhv_weak.get());
804 // Tests setting background transparency. See also (disabled on Mac)
805 // RenderWidgetHostTest.Background. This test has some additional checks for
806 // Mac.
807 TEST_F(RenderWidgetHostViewMacTest, Background) {
808   TestBrowserContext browser_context;
809   MockRenderProcessHost* process_host =
810       new MockRenderProcessHost(&browser_context);
811   MockRenderWidgetHostDelegate delegate;
812   MockRenderWidgetHostImpl* host = new MockRenderWidgetHostImpl(
813       &delegate, process_host, MSG_ROUTING_NONE);
814   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(host, false);
816   EXPECT_TRUE(view->GetBackgroundOpaque());
817   EXPECT_TRUE([view->cocoa_view() isOpaque]);
819   view->SetBackgroundColor(SK_ColorTRANSPARENT);
820   EXPECT_FALSE(view->GetBackgroundOpaque());
821   EXPECT_FALSE([view->cocoa_view() isOpaque]);
823   const IPC::Message* set_background;
824   set_background = process_host->sink().GetUniqueMessageMatching(
825       ViewMsg_SetBackgroundOpaque::ID);
826   ASSERT_TRUE(set_background);
827   Tuple<bool> sent_background;
828   ViewMsg_SetBackgroundOpaque::Read(set_background, &sent_background);
829   EXPECT_FALSE(get<0>(sent_background));
831   // Try setting it back.
832   process_host->sink().ClearMessages();
833   view->SetBackgroundColor(SK_ColorWHITE);
834   EXPECT_TRUE(view->GetBackgroundOpaque());
835   EXPECT_TRUE([view->cocoa_view() isOpaque]);
836   set_background = process_host->sink().GetUniqueMessageMatching(
837       ViewMsg_SetBackgroundOpaque::ID);
838   ASSERT_TRUE(set_background);
839   ViewMsg_SetBackgroundOpaque::Read(set_background, &sent_background);
840   EXPECT_TRUE(get<0>(sent_background));
842   host->Shutdown();
845 }  // namespace content