Ignore non-active fullscreen windows for shelf state.
[chromium-blink-merge.git] / content / browser / renderer_host / render_widget_host_view_mac_unittest.mm
blob69252d4f8120f2afeaef34ae2f7e9dc13080906c
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/strings/utf_string_conversions.h"
10 #include "content/browser/browser_thread_impl.h"
11 #include "content/browser/renderer_host/render_widget_host_delegate.h"
12 #include "content/common/gpu/gpu_messages.h"
13 #include "content/common/input_messages.h"
14 #include "content/common/view_messages.h"
15 #include "content/public/browser/notification_types.h"
16 #include "content/public/browser/render_widget_host_view_mac_delegate.h"
17 #include "content/public/test/mock_render_process_host.h"
18 #include "content/public/test/test_browser_context.h"
19 #include "content/public/test/test_utils.h"
20 #include "content/test/test_render_view_host.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "ui/base/test/cocoa_test_event_utils.h"
24 #import "ui/base/test/ui_cocoa_test_helper.h"
26 // Declare things that are part of the 10.7 SDK.
27 #if !defined(MAC_OS_X_VERSION_10_7) || \
28     MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
29 enum {
30   NSEventPhaseNone        = 0, // event not associated with a phase.
31   NSEventPhaseBegan       = 0x1 << 0,
32   NSEventPhaseStationary  = 0x1 << 1,
33   NSEventPhaseChanged     = 0x1 << 2,
34   NSEventPhaseEnded       = 0x1 << 3,
35   NSEventPhaseCancelled   = 0x1 << 4,
37 typedef NSUInteger NSEventPhase;
39 @interface NSEvent (LionAPI)
40 - (NSEventPhase)phase;
41 @end
43 #endif  // 10.7
45 // Helper class with methods used to mock -[NSEvent phase], used by
46 // |MockScrollWheelEventWithPhase()|.
47 @interface MockPhaseMethods : NSObject {
50 - (NSEventPhase)phaseBegan;
51 - (NSEventPhase)phaseChanged;
52 - (NSEventPhase)phaseEnded;
53 @end
55 @implementation MockPhaseMethods
57 - (NSEventPhase)phaseBegan {
58   return NSEventPhaseBegan;
60 - (NSEventPhase)phaseChanged {
61   return NSEventPhaseChanged;
63 - (NSEventPhase)phaseEnded {
64   return NSEventPhaseEnded;
67 @end
69 @interface MockRenderWidgetHostViewMacDelegate
70     : NSObject<RenderWidgetHostViewMacDelegate> {
71   BOOL unhandledWheelEventReceived_;
74 @property(nonatomic) BOOL unhandledWheelEventReceived;
76 - (void)gotUnhandledWheelEvent;
77 @end
79 @implementation MockRenderWidgetHostViewMacDelegate
81 @synthesize unhandledWheelEventReceived = unhandledWheelEventReceived_;
83 - (void)gotUnhandledWheelEvent {
84   unhandledWheelEventReceived_ = true;
87 @end
89 namespace content {
91 namespace {
93 class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate {
94  public:
95   MockRenderWidgetHostDelegate() {}
96   virtual ~MockRenderWidgetHostDelegate() {}
99 class MockRenderWidgetHostImpl : public RenderWidgetHostImpl {
100  public:
101   MockRenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,
102                            RenderProcessHost* process,
103                            int routing_id)
104       : RenderWidgetHostImpl(delegate, process, routing_id, false) {
105   }
107   MOCK_METHOD0(Focus, void());
108   MOCK_METHOD0(Blur, void());
111 // Generates the |length| of composition rectangle vector and save them to
112 // |output|. It starts from |origin| and each rectangle contains |unit_size|.
113 void GenerateCompositionRectArray(const gfx::Point& origin,
114                                   const gfx::Size& unit_size,
115                                   size_t length,
116                                   const std::vector<size_t>& break_points,
117                                   std::vector<gfx::Rect>* output) {
118   DCHECK(output);
119   output->clear();
121   std::queue<int> break_point_queue;
122   for (size_t i = 0; i < break_points.size(); ++i)
123     break_point_queue.push(break_points[i]);
124   break_point_queue.push(length);
125   size_t next_break_point = break_point_queue.front();
126   break_point_queue.pop();
128   gfx::Rect current_rect(origin, unit_size);
129   for (size_t i = 0; i < length; ++i) {
130     if (i == next_break_point) {
131       current_rect.set_x(origin.x());
132       current_rect.set_y(current_rect.y() + current_rect.height());
133       next_break_point = break_point_queue.front();
134       break_point_queue.pop();
135     }
136     output->push_back(current_rect);
137     current_rect.set_x(current_rect.right());
138   }
141 gfx::Rect GetExpectedRect(const gfx::Point& origin,
142                           const gfx::Size& size,
143                           const gfx::Range& range,
144                           int line_no) {
145   return gfx::Rect(
146       origin.x() + range.start() * size.width(),
147       origin.y() + line_no * size.height(),
148       range.length() * size.width(),
149       size.height());
152 // Returns NSScrollWheel event that mocks -phase. |mockPhaseSelector| should
153 // correspond to a method in |MockPhaseMethods| that returns the desired phase.
154 NSEvent* MockScrollWheelEventWithPhase(SEL mockPhaseSelector, int32_t delta) {
155   CGEventRef cg_event =
156       CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitLine, 1, delta, 0);
157   NSEvent* event = [NSEvent eventWithCGEvent:cg_event];
158   CFRelease(cg_event);
159   method_setImplementation(
160       class_getInstanceMethod([NSEvent class], @selector(phase)),
161       [MockPhaseMethods instanceMethodForSelector:mockPhaseSelector]);
162   return event;
165 }  // namespace
167 class RenderWidgetHostViewMacTest : public RenderViewHostImplTestHarness {
168  public:
169   RenderWidgetHostViewMacTest() : old_rwhv_(NULL), rwhv_mac_(NULL) {}
171   virtual void SetUp() {
172     RenderViewHostImplTestHarness::SetUp();
174     // TestRenderViewHost's destruction assumes that its view is a
175     // TestRenderWidgetHostView, so store its view and reset it back to the
176     // stored view in |TearDown()|.
177     old_rwhv_ = rvh()->GetView();
179     // Owned by its |cocoa_view()|, i.e. |rwhv_cocoa_|.
180     rwhv_mac_ = static_cast<RenderWidgetHostViewMac*>(
181         RenderWidgetHostView::CreateViewForWidget(rvh()));
182     rwhv_cocoa_.reset([rwhv_mac_->cocoa_view() retain]);
183   }
184   virtual void TearDown() {
185     // Make sure the rwhv_mac_ is gone once the superclass's |TearDown()| runs.
186     rwhv_cocoa_.reset();
187     pool_.Recycle();
188     base::MessageLoop::current()->RunUntilIdle();
189     pool_.Recycle();
191     // See comment in SetUp().
192     test_rvh()->SetView(old_rwhv_);
194     RenderViewHostImplTestHarness::TearDown();
195   }
196  protected:
197  private:
198   // This class isn't derived from PlatformTest.
199   base::mac::ScopedNSAutoreleasePool pool_;
201   RenderWidgetHostView* old_rwhv_;
203  protected:
204   RenderWidgetHostViewMac* rwhv_mac_;
205   base::scoped_nsobject<RenderWidgetHostViewCocoa> rwhv_cocoa_;
207  private:
208   DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewMacTest);
211 TEST_F(RenderWidgetHostViewMacTest, Basic) {
214 TEST_F(RenderWidgetHostViewMacTest, AcceptsFirstResponder) {
215   // The RWHVCocoa should normally accept first responder status.
216   EXPECT_TRUE([rwhv_cocoa_.get() acceptsFirstResponder]);
218   // Unless we tell it not to.
219   rwhv_mac_->SetTakesFocusOnlyOnMouseDown(true);
220   EXPECT_FALSE([rwhv_cocoa_.get() acceptsFirstResponder]);
222   // But we can set things back to the way they were originally.
223   rwhv_mac_->SetTakesFocusOnlyOnMouseDown(false);
224   EXPECT_TRUE([rwhv_cocoa_.get() acceptsFirstResponder]);
227 TEST_F(RenderWidgetHostViewMacTest, TakesFocusOnMouseDown) {
228   base::scoped_nsobject<CocoaTestHelperWindow> window(
229       [[CocoaTestHelperWindow alloc] init]);
230   [[window contentView] addSubview:rwhv_cocoa_.get()];
232   // Even if the RWHVCocoa disallows first responder, clicking on it gives it
233   // focus.
234   [window setPretendIsKeyWindow:YES];
235   [window makeFirstResponder:nil];
236   ASSERT_NE(rwhv_cocoa_.get(), [window firstResponder]);
238   rwhv_mac_->SetTakesFocusOnlyOnMouseDown(true);
239   EXPECT_FALSE([rwhv_cocoa_.get() acceptsFirstResponder]);
241   std::pair<NSEvent*, NSEvent*> clicks =
242       cocoa_test_event_utils::MouseClickInView(rwhv_cocoa_.get(), 1);
243   [rwhv_cocoa_.get() mouseDown:clicks.first];
244   EXPECT_EQ(rwhv_cocoa_.get(), [window firstResponder]);
247 TEST_F(RenderWidgetHostViewMacTest, Fullscreen) {
248   rwhv_mac_->InitAsFullscreen(NULL);
249   EXPECT_TRUE(rwhv_mac_->pepper_fullscreen_window());
251   // Break the reference cycle caused by pepper_fullscreen_window() without
252   // an <esc> event. See comment in
253   // release_pepper_fullscreen_window_for_testing().
254   rwhv_mac_->release_pepper_fullscreen_window_for_testing();
257 // Verify that escape key down in fullscreen mode suppressed the keyup event on
258 // the parent.
259 TEST_F(RenderWidgetHostViewMacTest, FullscreenCloseOnEscape) {
260   // Use our own RWH since we need to destroy it.
261   MockRenderWidgetHostDelegate delegate;
262   TestBrowserContext browser_context;
263   MockRenderProcessHost* process_host =
264       new MockRenderProcessHost(&browser_context);
265   // Owned by its |cocoa_view()|.
266   RenderWidgetHostImpl* rwh = new RenderWidgetHostImpl(
267       &delegate, process_host, MSG_ROUTING_NONE, false);
268   RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>(
269       RenderWidgetHostView::CreateViewForWidget(rwh));
271   view->InitAsFullscreen(rwhv_mac_);
273   WindowedNotificationObserver observer(
274       NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
275       Source<RenderWidgetHost>(rwh));
276   EXPECT_FALSE([rwhv_mac_->cocoa_view() suppressNextEscapeKeyUp]);
278   // Escape key down. Should close window and set |suppressNextEscapeKeyUp| on
279   // the parent.
280   [view->cocoa_view() keyEvent:
281       cocoa_test_event_utils::KeyEventWithKeyCode(53, 27, NSKeyDown, 0)];
282   observer.Wait();
283   EXPECT_TRUE([rwhv_mac_->cocoa_view() suppressNextEscapeKeyUp]);
285   // Escape key up on the parent should clear |suppressNextEscapeKeyUp|.
286   [rwhv_mac_->cocoa_view() keyEvent:
287       cocoa_test_event_utils::KeyEventWithKeyCode(53, 27, NSKeyUp, 0)];
288   EXPECT_FALSE([rwhv_mac_->cocoa_view() suppressNextEscapeKeyUp]);
291 // Test that command accelerators which destroy the fullscreen window
292 // don't crash when forwarded via the window's responder machinery.
293 TEST_F(RenderWidgetHostViewMacTest, AcceleratorDestroy) {
294   // Use our own RWH since we need to destroy it.
295   MockRenderWidgetHostDelegate delegate;
296   TestBrowserContext browser_context;
297   MockRenderProcessHost* process_host =
298       new MockRenderProcessHost(&browser_context);
299   // Owned by its |cocoa_view()|.
300   RenderWidgetHostImpl* rwh = new RenderWidgetHostImpl(
301       &delegate, process_host, MSG_ROUTING_NONE, false);
302   RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>(
303       RenderWidgetHostView::CreateViewForWidget(rwh));
305   view->InitAsFullscreen(rwhv_mac_);
307   WindowedNotificationObserver observer(
308       NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
309       Source<RenderWidgetHost>(rwh));
311   // Command-ESC will destroy the view, while the window is still in
312   // |-performKeyEquivalent:|.  There are other cases where this can
313   // happen, Command-ESC is the easiest to trigger.
314   [[view->cocoa_view() window] performKeyEquivalent:
315       cocoa_test_event_utils::KeyEventWithKeyCode(
316           53, 27, NSKeyDown, NSCommandKeyMask)];
317   observer.Wait();
320 TEST_F(RenderWidgetHostViewMacTest, GetFirstRectForCharacterRangeCaretCase) {
321   const base::string16 kDummyString = UTF8ToUTF16("hogehoge");
322   const size_t kDummyOffset = 0;
324   gfx::Rect caret_rect(10, 11, 0, 10);
325   gfx::Range caret_range(0, 0);
326   ViewHostMsg_SelectionBounds_Params params;
328   NSRect rect;
329   NSRange actual_range;
330   rwhv_mac_->SelectionChanged(kDummyString, kDummyOffset, caret_range);
331   params.anchor_rect = params.focus_rect = caret_rect;
332   params.anchor_dir = params.focus_dir = blink::WebTextDirectionLeftToRight;
333   rwhv_mac_->SelectionBoundsChanged(params);
334   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
335         caret_range.ToNSRange(),
336         &rect,
337         &actual_range));
338   EXPECT_EQ(caret_rect, gfx::Rect(NSRectToCGRect(rect)));
339   EXPECT_EQ(caret_range, gfx::Range(actual_range));
341   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
342         gfx::Range(0, 1).ToNSRange(),
343         &rect,
344         &actual_range));
345   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
346         gfx::Range(1, 1).ToNSRange(),
347         &rect,
348         &actual_range));
349   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
350         gfx::Range(2, 3).ToNSRange(),
351         &rect,
352         &actual_range));
354   // Caret moved.
355   caret_rect = gfx::Rect(20, 11, 0, 10);
356   caret_range = gfx::Range(1, 1);
357   params.anchor_rect = params.focus_rect = caret_rect;
358   rwhv_mac_->SelectionChanged(kDummyString, kDummyOffset, caret_range);
359   rwhv_mac_->SelectionBoundsChanged(params);
360   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
361         caret_range.ToNSRange(),
362         &rect,
363         &actual_range));
364   EXPECT_EQ(caret_rect, gfx::Rect(NSRectToCGRect(rect)));
365   EXPECT_EQ(caret_range, gfx::Range(actual_range));
367   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
368         gfx::Range(0, 0).ToNSRange(),
369         &rect,
370         &actual_range));
371   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
372         gfx::Range(1, 2).ToNSRange(),
373         &rect,
374         &actual_range));
375   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
376         gfx::Range(2, 3).ToNSRange(),
377         &rect,
378         &actual_range));
380   // No caret.
381   caret_range = gfx::Range(1, 2);
382   rwhv_mac_->SelectionChanged(kDummyString, kDummyOffset, caret_range);
383   params.anchor_rect = caret_rect;
384   params.focus_rect = gfx::Rect(30, 11, 0, 10);
385   rwhv_mac_->SelectionBoundsChanged(params);
386   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
387         gfx::Range(0, 0).ToNSRange(),
388         &rect,
389         &actual_range));
390   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
391         gfx::Range(0, 1).ToNSRange(),
392         &rect,
393         &actual_range));
394   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
395         gfx::Range(1, 1).ToNSRange(),
396         &rect,
397         &actual_range));
398   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
399         gfx::Range(1, 2).ToNSRange(),
400         &rect,
401         &actual_range));
402   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
403         gfx::Range(2, 2).ToNSRange(),
404         &rect,
405         &actual_range));
408 TEST_F(RenderWidgetHostViewMacTest, UpdateCompositionSinglelineCase) {
409   const gfx::Point kOrigin(10, 11);
410   const gfx::Size kBoundsUnit(10, 20);
412   NSRect rect;
413   // Make sure not crashing by passing NULL pointer instead of |actual_range|.
414   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
415       gfx::Range(0, 0).ToNSRange(),
416       &rect,
417       NULL));
419   // If there are no update from renderer, always returned caret position.
420   NSRange actual_range;
421   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
422       gfx::Range(0, 0).ToNSRange(),
423       &rect,
424       &actual_range));
425   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
426       gfx::Range(0, 1).ToNSRange(),
427       &rect,
428       &actual_range));
429   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
430       gfx::Range(1, 0).ToNSRange(),
431       &rect,
432       &actual_range));
433   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
434       gfx::Range(1, 1).ToNSRange(),
435       &rect,
436       &actual_range));
437   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
438       gfx::Range(1, 2).ToNSRange(),
439       &rect,
440       &actual_range));
442   // If the firstRectForCharacterRange is failed in renderer, empty rect vector
443   // is sent. Make sure this does not crash.
444   rwhv_mac_->ImeCompositionRangeChanged(gfx::Range(10, 12),
445                                         std::vector<gfx::Rect>());
446   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
447       gfx::Range(10, 11).ToNSRange(),
448       &rect,
449       NULL));
451   const int kCompositionLength = 10;
452   std::vector<gfx::Rect> composition_bounds;
453   const int kCompositionStart = 3;
454   const gfx::Range kCompositionRange(kCompositionStart,
455                                     kCompositionStart + kCompositionLength);
456   GenerateCompositionRectArray(kOrigin,
457                                kBoundsUnit,
458                                kCompositionLength,
459                                std::vector<size_t>(),
460                                &composition_bounds);
461   rwhv_mac_->ImeCompositionRangeChanged(kCompositionRange, composition_bounds);
463   // Out of range requests will return caret position.
464   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
465       gfx::Range(0, 0).ToNSRange(),
466       &rect,
467       &actual_range));
468   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
469       gfx::Range(1, 1).ToNSRange(),
470       &rect,
471       &actual_range));
472   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
473       gfx::Range(1, 2).ToNSRange(),
474       &rect,
475       &actual_range));
476   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
477       gfx::Range(2, 2).ToNSRange(),
478       &rect,
479       &actual_range));
480   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
481       gfx::Range(13, 14).ToNSRange(),
482       &rect,
483       &actual_range));
484   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
485       gfx::Range(14, 15).ToNSRange(),
486       &rect,
487       &actual_range));
489   for (int i = 0; i <= kCompositionLength; ++i) {
490     for (int j = 0; j <= kCompositionLength - i; ++j) {
491       const gfx::Range range(i, i + j);
492       const gfx::Rect expected_rect = GetExpectedRect(kOrigin,
493                                                       kBoundsUnit,
494                                                       range,
495                                                       0);
496       const NSRange request_range = gfx::Range(
497           kCompositionStart + range.start(),
498           kCompositionStart + range.end()).ToNSRange();
499       EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
500             request_range,
501             &rect,
502             &actual_range));
503       EXPECT_EQ(gfx::Range(request_range), gfx::Range(actual_range));
504       EXPECT_EQ(expected_rect, gfx::Rect(NSRectToCGRect(rect)));
506       // Make sure not crashing by passing NULL pointer instead of
507       // |actual_range|.
508       EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
509             request_range,
510             &rect,
511             NULL));
512     }
513   }
516 TEST_F(RenderWidgetHostViewMacTest, UpdateCompositionMultilineCase) {
517   const gfx::Point kOrigin(10, 11);
518   const gfx::Size kBoundsUnit(10, 20);
519   NSRect rect;
521   const int kCompositionLength = 30;
522   std::vector<gfx::Rect> composition_bounds;
523   const gfx::Range kCompositionRange(0, kCompositionLength);
524   // Set breaking point at 10 and 20.
525   std::vector<size_t> break_points;
526   break_points.push_back(10);
527   break_points.push_back(20);
528   GenerateCompositionRectArray(kOrigin,
529                                kBoundsUnit,
530                                kCompositionLength,
531                                break_points,
532                                &composition_bounds);
533   rwhv_mac_->ImeCompositionRangeChanged(kCompositionRange, composition_bounds);
535   // Range doesn't contain line breaking point.
536   gfx::Range range;
537   range = gfx::Range(5, 8);
538   NSRange actual_range;
539   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
540                                                              &rect,
541                                                              &actual_range));
542   EXPECT_EQ(range, gfx::Range(actual_range));
543   EXPECT_EQ(
544       GetExpectedRect(kOrigin, kBoundsUnit, range, 0),
545       gfx::Rect(NSRectToCGRect(rect)));
546   range = gfx::Range(15, 18);
547   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
548                                                              &rect,
549                                                              &actual_range));
550   EXPECT_EQ(range, gfx::Range(actual_range));
551   EXPECT_EQ(
552       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 8), 1),
553       gfx::Rect(NSRectToCGRect(rect)));
554   range = gfx::Range(25, 28);
555   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
556                                                              &rect,
557                                                              &actual_range));
558   EXPECT_EQ(range, gfx::Range(actual_range));
559   EXPECT_EQ(
560       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 8), 2),
561       gfx::Rect(NSRectToCGRect(rect)));
563   // Range contains line breaking point.
564   range = gfx::Range(8, 12);
565   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
566                                                              &rect,
567                                                              &actual_range));
568   EXPECT_EQ(gfx::Range(8, 10), gfx::Range(actual_range));
569   EXPECT_EQ(
570       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(8, 10), 0),
571       gfx::Rect(NSRectToCGRect(rect)));
572   range = gfx::Range(18, 22);
573   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
574                                                              &rect,
575                                                              &actual_range));
576   EXPECT_EQ(gfx::Range(18, 20), gfx::Range(actual_range));
577   EXPECT_EQ(
578       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(8, 10), 1),
579       gfx::Rect(NSRectToCGRect(rect)));
581   // Start point is line breaking point.
582   range = gfx::Range(10, 12);
583   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
584                                                              &rect,
585                                                              &actual_range));
586   EXPECT_EQ(gfx::Range(10, 12), gfx::Range(actual_range));
587   EXPECT_EQ(
588       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 2), 1),
589       gfx::Rect(NSRectToCGRect(rect)));
590   range = gfx::Range(20, 22);
591   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
592                                                              &rect,
593                                                              &actual_range));
594   EXPECT_EQ(gfx::Range(20, 22), gfx::Range(actual_range));
595   EXPECT_EQ(
596       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 2), 2),
597       gfx::Rect(NSRectToCGRect(rect)));
599   // End point is line breaking point.
600   range = gfx::Range(5, 10);
601   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
602                                                              &rect,
603                                                              &actual_range));
604   EXPECT_EQ(gfx::Range(5, 10), gfx::Range(actual_range));
605   EXPECT_EQ(
606       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 10), 0),
607       gfx::Rect(NSRectToCGRect(rect)));
608   range = gfx::Range(15, 20);
609   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
610                                                              &rect,
611                                                              &actual_range));
612   EXPECT_EQ(gfx::Range(15, 20), gfx::Range(actual_range));
613   EXPECT_EQ(
614       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 10), 1),
615       gfx::Rect(NSRectToCGRect(rect)));
617   // Start and end point are same line breaking point.
618   range = gfx::Range(10, 10);
619   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
620                                                              &rect,
621                                                              &actual_range));
622   EXPECT_EQ(gfx::Range(10, 10), gfx::Range(actual_range));
623   EXPECT_EQ(
624       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 0), 1),
625       gfx::Rect(NSRectToCGRect(rect)));
626   range = gfx::Range(20, 20);
627   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
628                                                              &rect,
629                                                              &actual_range));
630   EXPECT_EQ(gfx::Range(20, 20), gfx::Range(actual_range));
631   EXPECT_EQ(
632       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 0), 2),
633       gfx::Rect(NSRectToCGRect(rect)));
635   // Start and end point are different line breaking point.
636   range = gfx::Range(10, 20);
637   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
638                                                              &rect,
639                                                              &actual_range));
640   EXPECT_EQ(gfx::Range(10, 20), gfx::Range(actual_range));
641   EXPECT_EQ(
642       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 10), 1),
643       gfx::Rect(NSRectToCGRect(rect)));
646 // Verify that |SetActive()| calls |RenderWidgetHostImpl::Blur()| and
647 // |RenderWidgetHostImp::Focus()|.
648 TEST_F(RenderWidgetHostViewMacTest, BlurAndFocusOnSetActive) {
649   MockRenderWidgetHostDelegate delegate;
650   TestBrowserContext browser_context;
651   MockRenderProcessHost* process_host =
652       new MockRenderProcessHost(&browser_context);
654   // Owned by its |cocoa_view()|.
655   MockRenderWidgetHostImpl* rwh = new MockRenderWidgetHostImpl(
656       &delegate, process_host, MSG_ROUTING_NONE);
657   RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>(
658       RenderWidgetHostView::CreateViewForWidget(rwh));
660   base::scoped_nsobject<CocoaTestHelperWindow> window(
661       [[CocoaTestHelperWindow alloc] init]);
662   [[window contentView] addSubview:view->cocoa_view()];
664   EXPECT_CALL(*rwh, Focus());
665   [window makeFirstResponder:view->cocoa_view()];
666   testing::Mock::VerifyAndClearExpectations(rwh);
668   EXPECT_CALL(*rwh, Blur());
669   view->SetActive(false);
670   testing::Mock::VerifyAndClearExpectations(rwh);
672   EXPECT_CALL(*rwh, Focus());
673   view->SetActive(true);
674   testing::Mock::VerifyAndClearExpectations(rwh);
676   // Unsetting first responder should blur.
677   EXPECT_CALL(*rwh, Blur());
678   [window makeFirstResponder:nil];
679   testing::Mock::VerifyAndClearExpectations(rwh);
681   // |SetActive()| shoud not focus if view is not first responder.
682   EXPECT_CALL(*rwh, Focus()).Times(0);
683   view->SetActive(true);
684   testing::Mock::VerifyAndClearExpectations(rwh);
686   // Clean up.
687   rwh->Shutdown();
690 TEST_F(RenderWidgetHostViewMacTest, ScrollWheelEndEventDelivery) {
691   // This tests Lion+ functionality, so don't run the test pre-Lion.
692   if (!base::mac::IsOSLionOrLater())
693     return;
695   // Initialize the view associated with a MockRenderWidgetHostImpl, rather than
696   // the MockRenderProcessHost that is set up by the test harness which mocks
697   // out |OnMessageReceived()|.
698   TestBrowserContext browser_context;
699   MockRenderProcessHost* process_host =
700       new MockRenderProcessHost(&browser_context);
701   MockRenderWidgetHostDelegate delegate;
702   MockRenderWidgetHostImpl* host = new MockRenderWidgetHostImpl(
703       &delegate, process_host, MSG_ROUTING_NONE);
704   RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>(
705       RenderWidgetHostView::CreateViewForWidget(host));
707   // Send an initial wheel event with NSEventPhaseBegan to the view.
708   NSEvent* event1 = MockScrollWheelEventWithPhase(@selector(phaseBegan), 0);
709   [view->cocoa_view() scrollWheel:event1];
710   ASSERT_EQ(1U, process_host->sink().message_count());
712   // Send an ACK for the first wheel event, so that the queue will be flushed.
713   scoped_ptr<IPC::Message> response(new InputHostMsg_HandleInputEvent_ACK(
714       0, blink::WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED,
715       ui::LatencyInfo()));
716   host->OnMessageReceived(*response);
718   // Post the NSEventPhaseEnded wheel event to NSApp and check whether the
719   // render view receives it.
720   NSEvent* event2 = MockScrollWheelEventWithPhase(@selector(phaseEnded), 0);
721   [NSApp postEvent:event2 atStart:NO];
722   base::MessageLoop::current()->RunUntilIdle();
723   ASSERT_EQ(2U, process_host->sink().message_count());
725   // Clean up.
726   host->Shutdown();
729 TEST_F(RenderWidgetHostViewMacTest, IgnoreEmptyUnhandledWheelEvent) {
730   // This tests Lion+ functionality, so don't run the test pre-Lion.
731   if (!base::mac::IsOSLionOrLater())
732     return;
734   // Initialize the view associated with a MockRenderWidgetHostImpl, rather than
735   // the MockRenderProcessHost that is set up by the test harness which mocks
736   // out |OnMessageReceived()|.
737   TestBrowserContext browser_context;
738   MockRenderProcessHost* process_host =
739       new MockRenderProcessHost(&browser_context);
740   MockRenderWidgetHostDelegate delegate;
741   MockRenderWidgetHostImpl* host = new MockRenderWidgetHostImpl(
742       &delegate, process_host, MSG_ROUTING_NONE);
743   RenderWidgetHostViewMac* view = static_cast<RenderWidgetHostViewMac*>(
744       RenderWidgetHostView::CreateViewForWidget(host));
746   // Add a delegate to the view.
747   base::scoped_nsobject<MockRenderWidgetHostViewMacDelegate> view_delegate(
748       [[MockRenderWidgetHostViewMacDelegate alloc] init]);
749   view->SetDelegate(view_delegate.get());
751   // Send an initial wheel event for scrolling by 3 lines.
752   NSEvent* event1 = MockScrollWheelEventWithPhase(@selector(phaseBegan), 3);
753   [view->cocoa_view() scrollWheel:event1];
754   ASSERT_EQ(1U, process_host->sink().message_count());
755   process_host->sink().ClearMessages();
757   // Indicate that the wheel event was unhandled.
758   scoped_ptr<IPC::Message> response1(new InputHostMsg_HandleInputEvent_ACK(0,
759       blink::WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_NOT_CONSUMED,
760       ui::LatencyInfo()));
761   host->OnMessageReceived(*response1);
763   // Check that the view delegate got an unhandled wheel event.
764   ASSERT_EQ(YES, view_delegate.get().unhandledWheelEventReceived);
765   view_delegate.get().unhandledWheelEventReceived = NO;
767   // Send another wheel event, this time for scrolling by 0 lines (empty event).
768   NSEvent* event2 = MockScrollWheelEventWithPhase(@selector(phaseChanged), 0);
769   [view->cocoa_view() scrollWheel:event2];
770   ASSERT_EQ(1U, process_host->sink().message_count());
772   // Indicate that the wheel event was also unhandled.
773   scoped_ptr<IPC::Message> response2(new InputHostMsg_HandleInputEvent_ACK(0,
774       blink::WebInputEvent::MouseWheel, INPUT_EVENT_ACK_STATE_NOT_CONSUMED,
775       ui::LatencyInfo()));
776   host->OnMessageReceived(*response2);
778   // Check that the view delegate ignored the empty unhandled wheel event.
779   ASSERT_EQ(NO, view_delegate.get().unhandledWheelEventReceived);
781   // Clean up.
782   host->Shutdown();
785 }  // namespace content