[blink-in-js] Migrate resources required for blink-in-js to grd - part 2
[chromium-blink-merge.git] / content / browser / renderer_host / render_widget_host_view_mac_unittest.mm
blob2df6c19e113c35fd2bbc83301303f4e0018c484f
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/gpu/compositor_util.h"
14 #include "content/browser/renderer_host/render_widget_host_delegate.h"
15 #include "content/common/gpu/gpu_messages.h"
16 #include "content/common/input_messages.h"
17 #include "content/common/view_messages.h"
18 #include "content/public/browser/notification_types.h"
19 #include "content/public/browser/render_widget_host_view_mac_delegate.h"
20 #include "content/public/test/mock_render_process_host.h"
21 #include "content/public/test/test_browser_context.h"
22 #include "content/public/test/test_utils.h"
23 #include "content/test/test_render_view_host.h"
24 #include "testing/gmock/include/gmock/gmock.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "ui/events/test/cocoa_test_event_utils.h"
27 #import "ui/gfx/test/ui_cocoa_test_helper.h"
29 // Helper class with methods used to mock -[NSEvent phase], used by
30 // |MockScrollWheelEventWithPhase()|.
31 @interface MockPhaseMethods : NSObject {
34 - (NSEventPhase)phaseBegan;
35 - (NSEventPhase)phaseChanged;
36 - (NSEventPhase)phaseEnded;
37 @end
39 @implementation MockPhaseMethods
41 - (NSEventPhase)phaseBegan {
42   return NSEventPhaseBegan;
44 - (NSEventPhase)phaseChanged {
45   return NSEventPhaseChanged;
47 - (NSEventPhase)phaseEnded {
48   return NSEventPhaseEnded;
51 @end
53 @interface MockRenderWidgetHostViewMacDelegate
54     : NSObject<RenderWidgetHostViewMacDelegate> {
55   BOOL unhandledWheelEventReceived_;
58 @property(nonatomic) BOOL unhandledWheelEventReceived;
60 @end
62 @implementation MockRenderWidgetHostViewMacDelegate
64 @synthesize unhandledWheelEventReceived = unhandledWheelEventReceived_;
66 - (void)rendererHandledWheelEvent:(const blink::WebMouseWheelEvent&)event
67                          consumed:(BOOL)consumed {
68   if (!consumed)
69     unhandledWheelEventReceived_ = true;
71 - (void)touchesBeganWithEvent:(NSEvent*)event {}
72 - (void)touchesMovedWithEvent:(NSEvent*)event {}
73 - (void)touchesCancelledWithEvent:(NSEvent*)event {}
74 - (void)touchesEndedWithEvent:(NSEvent*)event {}
75 - (void)beginGestureWithEvent:(NSEvent*)event {}
76 - (void)endGestureWithEvent:(NSEvent*)event {}
77 - (BOOL)canRubberbandLeft:(NSView*)view {
78   return true;
80 - (BOOL)canRubberbandRight:(NSView*)view {
81   return true;
84 @end
86 namespace content {
88 namespace {
90 class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate {
91  public:
92   MockRenderWidgetHostDelegate() {}
93   virtual ~MockRenderWidgetHostDelegate() {}
96 class MockRenderWidgetHostImpl : public RenderWidgetHostImpl {
97  public:
98   MockRenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,
99                            RenderProcessHost* process,
100                            int routing_id)
101       : RenderWidgetHostImpl(delegate, process, routing_id, false) {
102   }
104   MOCK_METHOD0(Focus, void());
105   MOCK_METHOD0(Blur, void());
108 // Generates the |length| of composition rectangle vector and save them to
109 // |output|. It starts from |origin| and each rectangle contains |unit_size|.
110 void GenerateCompositionRectArray(const gfx::Point& origin,
111                                   const gfx::Size& unit_size,
112                                   size_t length,
113                                   const std::vector<size_t>& break_points,
114                                   std::vector<gfx::Rect>* output) {
115   DCHECK(output);
116   output->clear();
118   std::queue<int> break_point_queue;
119   for (size_t i = 0; i < break_points.size(); ++i)
120     break_point_queue.push(break_points[i]);
121   break_point_queue.push(length);
122   size_t next_break_point = break_point_queue.front();
123   break_point_queue.pop();
125   gfx::Rect current_rect(origin, unit_size);
126   for (size_t i = 0; i < length; ++i) {
127     if (i == next_break_point) {
128       current_rect.set_x(origin.x());
129       current_rect.set_y(current_rect.y() + current_rect.height());
130       next_break_point = break_point_queue.front();
131       break_point_queue.pop();
132     }
133     output->push_back(current_rect);
134     current_rect.set_x(current_rect.right());
135   }
138 gfx::Rect GetExpectedRect(const gfx::Point& origin,
139                           const gfx::Size& size,
140                           const gfx::Range& range,
141                           int line_no) {
142   return gfx::Rect(
143       origin.x() + range.start() * size.width(),
144       origin.y() + line_no * size.height(),
145       range.length() * size.width(),
146       size.height());
149 // Returns NSScrollWheel event that mocks -phase. |mockPhaseSelector| should
150 // correspond to a method in |MockPhaseMethods| that returns the desired phase.
151 NSEvent* MockScrollWheelEventWithPhase(SEL mockPhaseSelector, int32_t delta) {
152   CGEventRef cg_event =
153       CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitLine, 1, delta, 0);
154   NSEvent* event = [NSEvent eventWithCGEvent:cg_event];
155   CFRelease(cg_event);
156   method_setImplementation(
157       class_getInstanceMethod([NSEvent class], @selector(phase)),
158       [MockPhaseMethods instanceMethodForSelector:mockPhaseSelector]);
159   return event;
162 }  // namespace
164 class RenderWidgetHostViewMacTest : public RenderViewHostImplTestHarness {
165  public:
166   RenderWidgetHostViewMacTest() : old_rwhv_(NULL), rwhv_mac_(NULL) {}
168   virtual void SetUp() {
169     RenderViewHostImplTestHarness::SetUp();
170     if (IsDelegatedRendererEnabled()) {
171       ImageTransportFactory::InitializeForUnitTests(
172           scoped_ptr<ImageTransportFactory>(
173               new NoTransportImageTransportFactory));
174     }
176     // TestRenderViewHost's destruction assumes that its view is a
177     // TestRenderWidgetHostView, so store its view and reset it back to the
178     // stored view in |TearDown()|.
179     old_rwhv_ = rvh()->GetView();
181     // Owned by its |cocoa_view()|, i.e. |rwhv_cocoa_|.
182     rwhv_mac_ = new RenderWidgetHostViewMac(rvh());
183     rwhv_cocoa_.reset([rwhv_mac_->cocoa_view() retain]);
184   }
185   virtual void TearDown() {
186     // Make sure the rwhv_mac_ is gone once the superclass's |TearDown()| runs.
187     rwhv_cocoa_.reset();
188     pool_.Recycle();
189     base::MessageLoop::current()->RunUntilIdle();
190     pool_.Recycle();
192     // See comment in SetUp().
193     test_rvh()->SetView(static_cast<RenderWidgetHostViewBase*>(old_rwhv_));
195     if (IsDelegatedRendererEnabled())
196       ImageTransportFactory::Terminate();
197     RenderViewHostImplTestHarness::TearDown();
198   }
199  protected:
200  private:
201   // This class isn't derived from PlatformTest.
202   base::mac::ScopedNSAutoreleasePool pool_;
204   RenderWidgetHostView* old_rwhv_;
206  protected:
207   RenderWidgetHostViewMac* rwhv_mac_;
208   base::scoped_nsobject<RenderWidgetHostViewCocoa> rwhv_cocoa_;
210  private:
211   DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewMacTest);
214 TEST_F(RenderWidgetHostViewMacTest, Basic) {
217 TEST_F(RenderWidgetHostViewMacTest, AcceptsFirstResponder) {
218   // The RWHVCocoa should normally accept first responder status.
219   EXPECT_TRUE([rwhv_cocoa_.get() acceptsFirstResponder]);
221   // Unless we tell it not to.
222   rwhv_mac_->SetTakesFocusOnlyOnMouseDown(true);
223   EXPECT_FALSE([rwhv_cocoa_.get() acceptsFirstResponder]);
225   // But we can set things back to the way they were originally.
226   rwhv_mac_->SetTakesFocusOnlyOnMouseDown(false);
227   EXPECT_TRUE([rwhv_cocoa_.get() acceptsFirstResponder]);
230 TEST_F(RenderWidgetHostViewMacTest, TakesFocusOnMouseDown) {
231   base::scoped_nsobject<CocoaTestHelperWindow> window(
232       [[CocoaTestHelperWindow alloc] init]);
233   [[window contentView] addSubview:rwhv_cocoa_.get()];
235   // Even if the RWHVCocoa disallows first responder, clicking on it gives it
236   // focus.
237   [window setPretendIsKeyWindow:YES];
238   [window makeFirstResponder:nil];
239   ASSERT_NE(rwhv_cocoa_.get(), [window firstResponder]);
241   rwhv_mac_->SetTakesFocusOnlyOnMouseDown(true);
242   EXPECT_FALSE([rwhv_cocoa_.get() acceptsFirstResponder]);
244   std::pair<NSEvent*, NSEvent*> clicks =
245       cocoa_test_event_utils::MouseClickInView(rwhv_cocoa_.get(), 1);
246   [rwhv_cocoa_.get() mouseDown:clicks.first];
247   EXPECT_EQ(rwhv_cocoa_.get(), [window firstResponder]);
250 TEST_F(RenderWidgetHostViewMacTest, Fullscreen) {
251   rwhv_mac_->InitAsFullscreen(NULL);
252   EXPECT_TRUE(rwhv_mac_->pepper_fullscreen_window());
254   // Break the reference cycle caused by pepper_fullscreen_window() without
255   // an <esc> event. See comment in
256   // release_pepper_fullscreen_window_for_testing().
257   rwhv_mac_->release_pepper_fullscreen_window_for_testing();
260 // Verify that escape key down in fullscreen mode suppressed the keyup event on
261 // the parent.
262 TEST_F(RenderWidgetHostViewMacTest, FullscreenCloseOnEscape) {
263   // Use our own RWH since we need to destroy it.
264   MockRenderWidgetHostDelegate delegate;
265   TestBrowserContext browser_context;
266   MockRenderProcessHost* process_host =
267       new MockRenderProcessHost(&browser_context);
268   // Owned by its |cocoa_view()|.
269   RenderWidgetHostImpl* rwh = new RenderWidgetHostImpl(
270       &delegate, process_host, MSG_ROUTING_NONE, false);
271   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(rwh);
273   view->InitAsFullscreen(rwhv_mac_);
275   WindowedNotificationObserver observer(
276       NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
277       Source<RenderWidgetHost>(rwh));
278   EXPECT_FALSE([rwhv_mac_->cocoa_view() suppressNextEscapeKeyUp]);
280   // Escape key down. Should close window and set |suppressNextEscapeKeyUp| on
281   // the parent.
282   [view->cocoa_view() keyEvent:
283       cocoa_test_event_utils::KeyEventWithKeyCode(53, 27, NSKeyDown, 0)];
284   observer.Wait();
285   EXPECT_TRUE([rwhv_mac_->cocoa_view() suppressNextEscapeKeyUp]);
287   // Escape key up on the parent should clear |suppressNextEscapeKeyUp|.
288   [rwhv_mac_->cocoa_view() keyEvent:
289       cocoa_test_event_utils::KeyEventWithKeyCode(53, 27, NSKeyUp, 0)];
290   EXPECT_FALSE([rwhv_mac_->cocoa_view() suppressNextEscapeKeyUp]);
293 // Test that command accelerators which destroy the fullscreen window
294 // don't crash when forwarded via the window's responder machinery.
295 TEST_F(RenderWidgetHostViewMacTest, AcceleratorDestroy) {
296   // Use our own RWH since we need to destroy it.
297   MockRenderWidgetHostDelegate delegate;
298   TestBrowserContext browser_context;
299   MockRenderProcessHost* process_host =
300       new MockRenderProcessHost(&browser_context);
301   // Owned by its |cocoa_view()|.
302   RenderWidgetHostImpl* rwh = new RenderWidgetHostImpl(
303       &delegate, process_host, MSG_ROUTING_NONE, false);
304   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(rwh);
306   view->InitAsFullscreen(rwhv_mac_);
308   WindowedNotificationObserver observer(
309       NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
310       Source<RenderWidgetHost>(rwh));
312   // Command-ESC will destroy the view, while the window is still in
313   // |-performKeyEquivalent:|.  There are other cases where this can
314   // happen, Command-ESC is the easiest to trigger.
315   [[view->cocoa_view() window] performKeyEquivalent:
316       cocoa_test_event_utils::KeyEventWithKeyCode(
317           53, 27, NSKeyDown, NSCommandKeyMask)];
318   observer.Wait();
321 TEST_F(RenderWidgetHostViewMacTest, GetFirstRectForCharacterRangeCaretCase) {
322   const base::string16 kDummyString = base::UTF8ToUTF16("hogehoge");
323   const size_t kDummyOffset = 0;
325   gfx::Rect caret_rect(10, 11, 0, 10);
326   gfx::Range caret_range(0, 0);
327   ViewHostMsg_SelectionBounds_Params params;
329   NSRect rect;
330   NSRange actual_range;
331   rwhv_mac_->SelectionChanged(kDummyString, kDummyOffset, caret_range);
332   params.anchor_rect = params.focus_rect = caret_rect;
333   params.anchor_dir = params.focus_dir = blink::WebTextDirectionLeftToRight;
334   rwhv_mac_->SelectionBoundsChanged(params);
335   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
336         caret_range.ToNSRange(),
337         &rect,
338         &actual_range));
339   EXPECT_EQ(caret_rect, gfx::Rect(NSRectToCGRect(rect)));
340   EXPECT_EQ(caret_range, gfx::Range(actual_range));
342   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
343         gfx::Range(0, 1).ToNSRange(),
344         &rect,
345         &actual_range));
346   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
347         gfx::Range(1, 1).ToNSRange(),
348         &rect,
349         &actual_range));
350   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
351         gfx::Range(2, 3).ToNSRange(),
352         &rect,
353         &actual_range));
355   // Caret moved.
356   caret_rect = gfx::Rect(20, 11, 0, 10);
357   caret_range = gfx::Range(1, 1);
358   params.anchor_rect = params.focus_rect = caret_rect;
359   rwhv_mac_->SelectionChanged(kDummyString, kDummyOffset, caret_range);
360   rwhv_mac_->SelectionBoundsChanged(params);
361   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
362         caret_range.ToNSRange(),
363         &rect,
364         &actual_range));
365   EXPECT_EQ(caret_rect, gfx::Rect(NSRectToCGRect(rect)));
366   EXPECT_EQ(caret_range, gfx::Range(actual_range));
368   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
369         gfx::Range(0, 0).ToNSRange(),
370         &rect,
371         &actual_range));
372   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
373         gfx::Range(1, 2).ToNSRange(),
374         &rect,
375         &actual_range));
376   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
377         gfx::Range(2, 3).ToNSRange(),
378         &rect,
379         &actual_range));
381   // No caret.
382   caret_range = gfx::Range(1, 2);
383   rwhv_mac_->SelectionChanged(kDummyString, kDummyOffset, caret_range);
384   params.anchor_rect = caret_rect;
385   params.focus_rect = gfx::Rect(30, 11, 0, 10);
386   rwhv_mac_->SelectionBoundsChanged(params);
387   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
388         gfx::Range(0, 0).ToNSRange(),
389         &rect,
390         &actual_range));
391   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
392         gfx::Range(0, 1).ToNSRange(),
393         &rect,
394         &actual_range));
395   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
396         gfx::Range(1, 1).ToNSRange(),
397         &rect,
398         &actual_range));
399   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
400         gfx::Range(1, 2).ToNSRange(),
401         &rect,
402         &actual_range));
403   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
404         gfx::Range(2, 2).ToNSRange(),
405         &rect,
406         &actual_range));
409 TEST_F(RenderWidgetHostViewMacTest, UpdateCompositionSinglelineCase) {
410   const gfx::Point kOrigin(10, 11);
411   const gfx::Size kBoundsUnit(10, 20);
413   NSRect rect;
414   // Make sure not crashing by passing NULL pointer instead of |actual_range|.
415   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
416       gfx::Range(0, 0).ToNSRange(),
417       &rect,
418       NULL));
420   // If there are no update from renderer, always returned caret position.
421   NSRange actual_range;
422   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
423       gfx::Range(0, 0).ToNSRange(),
424       &rect,
425       &actual_range));
426   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
427       gfx::Range(0, 1).ToNSRange(),
428       &rect,
429       &actual_range));
430   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
431       gfx::Range(1, 0).ToNSRange(),
432       &rect,
433       &actual_range));
434   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
435       gfx::Range(1, 1).ToNSRange(),
436       &rect,
437       &actual_range));
438   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
439       gfx::Range(1, 2).ToNSRange(),
440       &rect,
441       &actual_range));
443   // If the firstRectForCharacterRange is failed in renderer, empty rect vector
444   // is sent. Make sure this does not crash.
445   rwhv_mac_->ImeCompositionRangeChanged(gfx::Range(10, 12),
446                                         std::vector<gfx::Rect>());
447   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
448       gfx::Range(10, 11).ToNSRange(),
449       &rect,
450       NULL));
452   const int kCompositionLength = 10;
453   std::vector<gfx::Rect> composition_bounds;
454   const int kCompositionStart = 3;
455   const gfx::Range kCompositionRange(kCompositionStart,
456                                     kCompositionStart + kCompositionLength);
457   GenerateCompositionRectArray(kOrigin,
458                                kBoundsUnit,
459                                kCompositionLength,
460                                std::vector<size_t>(),
461                                &composition_bounds);
462   rwhv_mac_->ImeCompositionRangeChanged(kCompositionRange, composition_bounds);
464   // Out of range requests will return caret position.
465   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
466       gfx::Range(0, 0).ToNSRange(),
467       &rect,
468       &actual_range));
469   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
470       gfx::Range(1, 1).ToNSRange(),
471       &rect,
472       &actual_range));
473   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
474       gfx::Range(1, 2).ToNSRange(),
475       &rect,
476       &actual_range));
477   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
478       gfx::Range(2, 2).ToNSRange(),
479       &rect,
480       &actual_range));
481   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
482       gfx::Range(13, 14).ToNSRange(),
483       &rect,
484       &actual_range));
485   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
486       gfx::Range(14, 15).ToNSRange(),
487       &rect,
488       &actual_range));
490   for (int i = 0; i <= kCompositionLength; ++i) {
491     for (int j = 0; j <= kCompositionLength - i; ++j) {
492       const gfx::Range range(i, i + j);
493       const gfx::Rect expected_rect = GetExpectedRect(kOrigin,
494                                                       kBoundsUnit,
495                                                       range,
496                                                       0);
497       const NSRange request_range = gfx::Range(
498           kCompositionStart + range.start(),
499           kCompositionStart + range.end()).ToNSRange();
500       EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
501             request_range,
502             &rect,
503             &actual_range));
504       EXPECT_EQ(gfx::Range(request_range), gfx::Range(actual_range));
505       EXPECT_EQ(expected_rect, gfx::Rect(NSRectToCGRect(rect)));
507       // Make sure not crashing by passing NULL pointer instead of
508       // |actual_range|.
509       EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
510             request_range,
511             &rect,
512             NULL));
513     }
514   }
517 TEST_F(RenderWidgetHostViewMacTest, UpdateCompositionMultilineCase) {
518   const gfx::Point kOrigin(10, 11);
519   const gfx::Size kBoundsUnit(10, 20);
520   NSRect rect;
522   const int kCompositionLength = 30;
523   std::vector<gfx::Rect> composition_bounds;
524   const gfx::Range kCompositionRange(0, kCompositionLength);
525   // Set breaking point at 10 and 20.
526   std::vector<size_t> break_points;
527   break_points.push_back(10);
528   break_points.push_back(20);
529   GenerateCompositionRectArray(kOrigin,
530                                kBoundsUnit,
531                                kCompositionLength,
532                                break_points,
533                                &composition_bounds);
534   rwhv_mac_->ImeCompositionRangeChanged(kCompositionRange, composition_bounds);
536   // Range doesn't contain line breaking point.
537   gfx::Range range;
538   range = gfx::Range(5, 8);
539   NSRange actual_range;
540   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
541                                                              &rect,
542                                                              &actual_range));
543   EXPECT_EQ(range, gfx::Range(actual_range));
544   EXPECT_EQ(
545       GetExpectedRect(kOrigin, kBoundsUnit, range, 0),
546       gfx::Rect(NSRectToCGRect(rect)));
547   range = gfx::Range(15, 18);
548   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
549                                                              &rect,
550                                                              &actual_range));
551   EXPECT_EQ(range, gfx::Range(actual_range));
552   EXPECT_EQ(
553       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 8), 1),
554       gfx::Rect(NSRectToCGRect(rect)));
555   range = gfx::Range(25, 28);
556   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
557                                                              &rect,
558                                                              &actual_range));
559   EXPECT_EQ(range, gfx::Range(actual_range));
560   EXPECT_EQ(
561       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 8), 2),
562       gfx::Rect(NSRectToCGRect(rect)));
564   // Range contains line breaking point.
565   range = gfx::Range(8, 12);
566   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
567                                                              &rect,
568                                                              &actual_range));
569   EXPECT_EQ(gfx::Range(8, 10), gfx::Range(actual_range));
570   EXPECT_EQ(
571       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(8, 10), 0),
572       gfx::Rect(NSRectToCGRect(rect)));
573   range = gfx::Range(18, 22);
574   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
575                                                              &rect,
576                                                              &actual_range));
577   EXPECT_EQ(gfx::Range(18, 20), gfx::Range(actual_range));
578   EXPECT_EQ(
579       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(8, 10), 1),
580       gfx::Rect(NSRectToCGRect(rect)));
582   // Start point is line breaking point.
583   range = gfx::Range(10, 12);
584   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
585                                                              &rect,
586                                                              &actual_range));
587   EXPECT_EQ(gfx::Range(10, 12), gfx::Range(actual_range));
588   EXPECT_EQ(
589       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 2), 1),
590       gfx::Rect(NSRectToCGRect(rect)));
591   range = gfx::Range(20, 22);
592   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
593                                                              &rect,
594                                                              &actual_range));
595   EXPECT_EQ(gfx::Range(20, 22), gfx::Range(actual_range));
596   EXPECT_EQ(
597       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 2), 2),
598       gfx::Rect(NSRectToCGRect(rect)));
600   // End point is line breaking point.
601   range = gfx::Range(5, 10);
602   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
603                                                              &rect,
604                                                              &actual_range));
605   EXPECT_EQ(gfx::Range(5, 10), gfx::Range(actual_range));
606   EXPECT_EQ(
607       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 10), 0),
608       gfx::Rect(NSRectToCGRect(rect)));
609   range = gfx::Range(15, 20);
610   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
611                                                              &rect,
612                                                              &actual_range));
613   EXPECT_EQ(gfx::Range(15, 20), gfx::Range(actual_range));
614   EXPECT_EQ(
615       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 10), 1),
616       gfx::Rect(NSRectToCGRect(rect)));
618   // Start and end point are same line breaking point.
619   range = gfx::Range(10, 10);
620   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
621                                                              &rect,
622                                                              &actual_range));
623   EXPECT_EQ(gfx::Range(10, 10), gfx::Range(actual_range));
624   EXPECT_EQ(
625       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 0), 1),
626       gfx::Rect(NSRectToCGRect(rect)));
627   range = gfx::Range(20, 20);
628   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
629                                                              &rect,
630                                                              &actual_range));
631   EXPECT_EQ(gfx::Range(20, 20), gfx::Range(actual_range));
632   EXPECT_EQ(
633       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 0), 2),
634       gfx::Rect(NSRectToCGRect(rect)));
636   // Start and end point are different line breaking point.
637   range = gfx::Range(10, 20);
638   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
639                                                              &rect,
640                                                              &actual_range));
641   EXPECT_EQ(gfx::Range(10, 20), gfx::Range(actual_range));
642   EXPECT_EQ(
643       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 10), 1),
644       gfx::Rect(NSRectToCGRect(rect)));
647 // Verify that |SetActive()| calls |RenderWidgetHostImpl::Blur()| and
648 // |RenderWidgetHostImp::Focus()|.
649 TEST_F(RenderWidgetHostViewMacTest, BlurAndFocusOnSetActive) {
650   MockRenderWidgetHostDelegate delegate;
651   TestBrowserContext browser_context;
652   MockRenderProcessHost* process_host =
653       new MockRenderProcessHost(&browser_context);
655   // Owned by its |cocoa_view()|.
656   MockRenderWidgetHostImpl* rwh = new MockRenderWidgetHostImpl(
657       &delegate, process_host, MSG_ROUTING_NONE);
658   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(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 = new RenderWidgetHostViewMac(host);
706   // Send an initial wheel event with NSEventPhaseBegan to the view.
707   NSEvent* event1 = MockScrollWheelEventWithPhase(@selector(phaseBegan), 0);
708   [view->cocoa_view() scrollWheel:event1];
709   ASSERT_EQ(1U, process_host->sink().message_count());
711   // Send an ACK for the first wheel event, so that the queue will be flushed.
712   InputHostMsg_HandleInputEvent_ACK_Params ack;
713   ack.type = blink::WebInputEvent::MouseWheel;
714   ack.state = INPUT_EVENT_ACK_STATE_CONSUMED;
715   scoped_ptr<IPC::Message> response(
716       new InputHostMsg_HandleInputEvent_ACK(0, ack));
717   host->OnMessageReceived(*response);
719   // Post the NSEventPhaseEnded wheel event to NSApp and check whether the
720   // render view receives it.
721   NSEvent* event2 = MockScrollWheelEventWithPhase(@selector(phaseEnded), 0);
722   [NSApp postEvent:event2 atStart:NO];
723   base::MessageLoop::current()->RunUntilIdle();
724   ASSERT_EQ(2U, process_host->sink().message_count());
726   // Clean up.
727   host->Shutdown();
730 TEST_F(RenderWidgetHostViewMacTest, IgnoreEmptyUnhandledWheelEvent) {
731   // This tests Lion+ functionality, so don't run the test pre-Lion.
732   if (!base::mac::IsOSLionOrLater())
733     return;
735   // Initialize the view associated with a MockRenderWidgetHostImpl, rather than
736   // the MockRenderProcessHost that is set up by the test harness which mocks
737   // out |OnMessageReceived()|.
738   TestBrowserContext browser_context;
739   MockRenderProcessHost* process_host =
740       new MockRenderProcessHost(&browser_context);
741   MockRenderWidgetHostDelegate delegate;
742   MockRenderWidgetHostImpl* host = new MockRenderWidgetHostImpl(
743       &delegate, process_host, MSG_ROUTING_NONE);
744   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(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   InputHostMsg_HandleInputEvent_ACK_Params unhandled_ack;
759   unhandled_ack.type = blink::WebInputEvent::MouseWheel;
760   unhandled_ack.state = INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
761   scoped_ptr<IPC::Message> response1(
762       new InputHostMsg_HandleInputEvent_ACK(0, unhandled_ack));
763   host->OnMessageReceived(*response1);
765   // Check that the view delegate got an unhandled wheel event.
766   ASSERT_EQ(YES, view_delegate.get().unhandledWheelEventReceived);
767   view_delegate.get().unhandledWheelEventReceived = NO;
769   // Send another wheel event, this time for scrolling by 0 lines (empty event).
770   NSEvent* event2 = MockScrollWheelEventWithPhase(@selector(phaseChanged), 0);
771   [view->cocoa_view() scrollWheel:event2];
772   ASSERT_EQ(1U, process_host->sink().message_count());
774   // Indicate that the wheel event was also unhandled.
775   scoped_ptr<IPC::Message> response2(
776       new InputHostMsg_HandleInputEvent_ACK(0, unhandled_ack));
777   host->OnMessageReceived(*response2);
779   // Check that the view delegate ignored the empty unhandled wheel event.
780   ASSERT_EQ(NO, view_delegate.get().unhandledWheelEventReceived);
782   // Clean up.
783   host->Shutdown();
786 }  // namespace content