Mac: Make pinch less sensitive
[chromium-blink-merge.git] / content / browser / renderer_host / render_widget_host_view_mac_unittest.mm
blob6fb79d187f3ea963ab6917fb8740e47f8690179c
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 <Cocoa/Cocoa.h>
9 #include "base/mac/mac_util.h"
10 #include "base/mac/scoped_nsautorelease_pool.h"
11 #include "base/mac/sdk_forward_declarations.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "content/browser/browser_thread_impl.h"
14 #include "content/browser/compositor/test/no_transport_image_transport_factory.h"
15 #include "content/browser/frame_host/render_widget_host_view_guest.h"
16 #include "content/browser/gpu/compositor_util.h"
17 #include "content/browser/renderer_host/render_widget_host_delegate.h"
18 #include "content/common/gpu/gpu_messages.h"
19 #include "content/common/input_messages.h"
20 #include "content/common/view_messages.h"
21 #include "content/public/browser/notification_types.h"
22 #include "content/public/browser/render_widget_host_view_mac_delegate.h"
23 #include "content/public/test/mock_render_process_host.h"
24 #include "content/public/test/test_browser_context.h"
25 #include "content/public/test/test_utils.h"
26 #include "content/test/test_render_view_host.h"
27 #include "testing/gmock/include/gmock/gmock.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #import "third_party/ocmock/OCMock/OCMock.h"
30 #import "third_party/ocmock/ocmock_extensions.h"
31 #include "ui/events/test/cocoa_test_event_utils.h"
32 #import "ui/gfx/test/ui_cocoa_test_helper.h"
34 // Helper class with methods used to mock -[NSEvent phase], used by
35 // |MockScrollWheelEventWithPhase()|.
36 @interface MockPhaseMethods : NSObject {
39 - (NSEventPhase)phaseBegan;
40 - (NSEventPhase)phaseChanged;
41 - (NSEventPhase)phaseEnded;
42 @end
44 @implementation MockPhaseMethods
46 - (NSEventPhase)phaseBegan {
47   return NSEventPhaseBegan;
49 - (NSEventPhase)phaseChanged {
50   return NSEventPhaseChanged;
52 - (NSEventPhase)phaseEnded {
53   return NSEventPhaseEnded;
56 @end
58 @interface MockRenderWidgetHostViewMacDelegate
59     : NSObject<RenderWidgetHostViewMacDelegate> {
60   BOOL unhandledWheelEventReceived_;
63 @property(nonatomic) BOOL unhandledWheelEventReceived;
65 @end
67 @implementation MockRenderWidgetHostViewMacDelegate
69 @synthesize unhandledWheelEventReceived = unhandledWheelEventReceived_;
71 - (void)rendererHandledWheelEvent:(const blink::WebMouseWheelEvent&)event
72                          consumed:(BOOL)consumed {
73   if (!consumed)
74     unhandledWheelEventReceived_ = true;
76 - (void)touchesBeganWithEvent:(NSEvent*)event {}
77 - (void)touchesMovedWithEvent:(NSEvent*)event {}
78 - (void)touchesCancelledWithEvent:(NSEvent*)event {}
79 - (void)touchesEndedWithEvent:(NSEvent*)event {}
80 - (void)beginGestureWithEvent:(NSEvent*)event {}
81 - (void)endGestureWithEvent:(NSEvent*)event {}
82 - (BOOL)canRubberbandLeft:(NSView*)view {
83   return true;
85 - (BOOL)canRubberbandRight:(NSView*)view {
86   return true;
89 @end
91 namespace content {
93 namespace {
95 id MockGestureEvent(
96     NSEventType type, NSTimeInterval timestamp, double magnification) {
97   id event = [OCMockObject mockForClass:[NSEvent class]];
98   NSPoint locationInWindow = NSMakePoint(0, 0);
99   CGFloat deltaX = 0;
100   CGFloat deltaY = 0;
101   NSUInteger modifierFlags = 0;
102   [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(type)] type];
103   [(NSEvent*)[[event stub]
104       andReturnValue:OCMOCK_VALUE(locationInWindow)] locationInWindow];
105   [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(deltaX)] deltaX];
106   [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(deltaY)] deltaY];
107   [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(timestamp)] timestamp];
108   [(NSEvent*)[[event stub]
109       andReturnValue:OCMOCK_VALUE(modifierFlags)] modifierFlags];
110   [(NSEvent*)[[event stub]
111       andReturnValue:OCMOCK_VALUE(magnification)] magnification];
112   return event;
115 class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate {
116  public:
117   MockRenderWidgetHostDelegate() {}
118   ~MockRenderWidgetHostDelegate() override {}
121 class MockRenderWidgetHostImpl : public RenderWidgetHostImpl {
122  public:
123   MockRenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,
124                            RenderProcessHost* process,
125                            int routing_id)
126       : RenderWidgetHostImpl(delegate, process, routing_id, false) {
127   }
129   MOCK_METHOD0(Focus, void());
130   MOCK_METHOD0(Blur, void());
133 // Generates the |length| of composition rectangle vector and save them to
134 // |output|. It starts from |origin| and each rectangle contains |unit_size|.
135 void GenerateCompositionRectArray(const gfx::Point& origin,
136                                   const gfx::Size& unit_size,
137                                   size_t length,
138                                   const std::vector<size_t>& break_points,
139                                   std::vector<gfx::Rect>* output) {
140   DCHECK(output);
141   output->clear();
143   std::queue<int> break_point_queue;
144   for (size_t i = 0; i < break_points.size(); ++i)
145     break_point_queue.push(break_points[i]);
146   break_point_queue.push(length);
147   size_t next_break_point = break_point_queue.front();
148   break_point_queue.pop();
150   gfx::Rect current_rect(origin, unit_size);
151   for (size_t i = 0; i < length; ++i) {
152     if (i == next_break_point) {
153       current_rect.set_x(origin.x());
154       current_rect.set_y(current_rect.y() + current_rect.height());
155       next_break_point = break_point_queue.front();
156       break_point_queue.pop();
157     }
158     output->push_back(current_rect);
159     current_rect.set_x(current_rect.right());
160   }
163 gfx::Rect GetExpectedRect(const gfx::Point& origin,
164                           const gfx::Size& size,
165                           const gfx::Range& range,
166                           int line_no) {
167   return gfx::Rect(
168       origin.x() + range.start() * size.width(),
169       origin.y() + line_no * size.height(),
170       range.length() * size.width(),
171       size.height());
174 // Returns NSScrollWheel event that mocks -phase. |mockPhaseSelector| should
175 // correspond to a method in |MockPhaseMethods| that returns the desired phase.
176 NSEvent* MockScrollWheelEventWithPhase(SEL mockPhaseSelector, int32_t delta) {
177   CGEventRef cg_event =
178       CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitLine, 1, delta, 0);
179   NSEvent* event = [NSEvent eventWithCGEvent:cg_event];
180   CFRelease(cg_event);
181   method_setImplementation(
182       class_getInstanceMethod([NSEvent class], @selector(phase)),
183       [MockPhaseMethods instanceMethodForSelector:mockPhaseSelector]);
184   return event;
187 }  // namespace
189 class RenderWidgetHostViewMacTest : public RenderViewHostImplTestHarness {
190  public:
191   RenderWidgetHostViewMacTest() : old_rwhv_(NULL), rwhv_mac_(NULL) {}
193   void SetUp() override {
194     RenderViewHostImplTestHarness::SetUp();
195     if (IsDelegatedRendererEnabled()) {
196       ImageTransportFactory::InitializeForUnitTests(
197           scoped_ptr<ImageTransportFactory>(
198               new NoTransportImageTransportFactory));
199     }
201     // TestRenderViewHost's destruction assumes that its view is a
202     // TestRenderWidgetHostView, so store its view and reset it back to the
203     // stored view in |TearDown()|.
204     old_rwhv_ = rvh()->GetView();
206     // Owned by its |cocoa_view()|, i.e. |rwhv_cocoa_|.
207     rwhv_mac_ = new RenderWidgetHostViewMac(rvh(), false);
208     rwhv_cocoa_.reset([rwhv_mac_->cocoa_view() retain]);
209   }
210   void TearDown() override {
211     // Make sure the rwhv_mac_ is gone once the superclass's |TearDown()| runs.
212     rwhv_cocoa_.reset();
213     RecycleAndWait();
215     // See comment in SetUp().
216     test_rvh()->SetView(static_cast<RenderWidgetHostViewBase*>(old_rwhv_));
218     if (IsDelegatedRendererEnabled())
219       ImageTransportFactory::Terminate();
220     RenderViewHostImplTestHarness::TearDown();
221   }
223   void RecycleAndWait() {
224     pool_.Recycle();
225     base::MessageLoop::current()->RunUntilIdle();
226     pool_.Recycle();
227   }
228  protected:
229  private:
230   // This class isn't derived from PlatformTest.
231   base::mac::ScopedNSAutoreleasePool pool_;
233   RenderWidgetHostView* old_rwhv_;
235  protected:
236   RenderWidgetHostViewMac* rwhv_mac_;
237   base::scoped_nsobject<RenderWidgetHostViewCocoa> rwhv_cocoa_;
239  private:
240   DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewMacTest);
243 TEST_F(RenderWidgetHostViewMacTest, Basic) {
246 TEST_F(RenderWidgetHostViewMacTest, AcceptsFirstResponder) {
247   // The RWHVCocoa should normally accept first responder status.
248   EXPECT_TRUE([rwhv_cocoa_.get() acceptsFirstResponder]);
251 TEST_F(RenderWidgetHostViewMacTest, Fullscreen) {
252   rwhv_mac_->InitAsFullscreen(NULL);
253   EXPECT_TRUE(rwhv_mac_->pepper_fullscreen_window());
255   // Break the reference cycle caused by pepper_fullscreen_window() without
256   // an <esc> event. See comment in
257   // release_pepper_fullscreen_window_for_testing().
258   rwhv_mac_->release_pepper_fullscreen_window_for_testing();
261 // Verify that escape key down in fullscreen mode suppressed the keyup event on
262 // the parent.
263 TEST_F(RenderWidgetHostViewMacTest, FullscreenCloseOnEscape) {
264   // Use our own RWH since we need to destroy it.
265   MockRenderWidgetHostDelegate delegate;
266   TestBrowserContext browser_context;
267   MockRenderProcessHost* process_host =
268       new MockRenderProcessHost(&browser_context);
269   // Owned by its |cocoa_view()|.
270   RenderWidgetHostImpl* rwh = new RenderWidgetHostImpl(
271       &delegate, process_host, MSG_ROUTING_NONE, false);
272   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(rwh, false);
274   view->InitAsFullscreen(rwhv_mac_);
276   WindowedNotificationObserver observer(
277       NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
278       Source<RenderWidgetHost>(rwh));
279   EXPECT_FALSE([rwhv_mac_->cocoa_view() suppressNextEscapeKeyUp]);
281   // Escape key down. Should close window and set |suppressNextEscapeKeyUp| on
282   // the parent.
283   [view->cocoa_view() keyEvent:
284       cocoa_test_event_utils::KeyEventWithKeyCode(53, 27, NSKeyDown, 0)];
285   observer.Wait();
286   EXPECT_TRUE([rwhv_mac_->cocoa_view() suppressNextEscapeKeyUp]);
288   // Escape key up on the parent should clear |suppressNextEscapeKeyUp|.
289   [rwhv_mac_->cocoa_view() keyEvent:
290       cocoa_test_event_utils::KeyEventWithKeyCode(53, 27, NSKeyUp, 0)];
291   EXPECT_FALSE([rwhv_mac_->cocoa_view() suppressNextEscapeKeyUp]);
294 // Test that command accelerators which destroy the fullscreen window
295 // don't crash when forwarded via the window's responder machinery.
296 TEST_F(RenderWidgetHostViewMacTest, AcceleratorDestroy) {
297   // Use our own RWH since we need to destroy it.
298   MockRenderWidgetHostDelegate delegate;
299   TestBrowserContext browser_context;
300   MockRenderProcessHost* process_host =
301       new MockRenderProcessHost(&browser_context);
302   // Owned by its |cocoa_view()|.
303   RenderWidgetHostImpl* rwh = new RenderWidgetHostImpl(
304       &delegate, process_host, MSG_ROUTING_NONE, false);
305   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(rwh, false);
307   view->InitAsFullscreen(rwhv_mac_);
309   WindowedNotificationObserver observer(
310       NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
311       Source<RenderWidgetHost>(rwh));
313   // Command-ESC will destroy the view, while the window is still in
314   // |-performKeyEquivalent:|.  There are other cases where this can
315   // happen, Command-ESC is the easiest to trigger.
316   [[view->cocoa_view() window] performKeyEquivalent:
317       cocoa_test_event_utils::KeyEventWithKeyCode(
318           53, 27, NSKeyDown, NSCommandKeyMask)];
319   observer.Wait();
322 TEST_F(RenderWidgetHostViewMacTest, GetFirstRectForCharacterRangeCaretCase) {
323   const base::string16 kDummyString = base::UTF8ToUTF16("hogehoge");
324   const size_t kDummyOffset = 0;
326   gfx::Rect caret_rect(10, 11, 0, 10);
327   gfx::Range caret_range(0, 0);
328   ViewHostMsg_SelectionBounds_Params params;
330   NSRect rect;
331   NSRange actual_range;
332   rwhv_mac_->SelectionChanged(kDummyString, kDummyOffset, caret_range);
333   params.anchor_rect = params.focus_rect = caret_rect;
334   params.anchor_dir = params.focus_dir = blink::WebTextDirectionLeftToRight;
335   rwhv_mac_->SelectionBoundsChanged(params);
336   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
337         caret_range.ToNSRange(),
338         &rect,
339         &actual_range));
340   EXPECT_EQ(caret_rect, gfx::Rect(NSRectToCGRect(rect)));
341   EXPECT_EQ(caret_range, gfx::Range(actual_range));
343   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
344         gfx::Range(0, 1).ToNSRange(),
345         &rect,
346         &actual_range));
347   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
348         gfx::Range(1, 1).ToNSRange(),
349         &rect,
350         &actual_range));
351   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
352         gfx::Range(2, 3).ToNSRange(),
353         &rect,
354         &actual_range));
356   // Caret moved.
357   caret_rect = gfx::Rect(20, 11, 0, 10);
358   caret_range = gfx::Range(1, 1);
359   params.anchor_rect = params.focus_rect = caret_rect;
360   rwhv_mac_->SelectionChanged(kDummyString, kDummyOffset, caret_range);
361   rwhv_mac_->SelectionBoundsChanged(params);
362   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
363         caret_range.ToNSRange(),
364         &rect,
365         &actual_range));
366   EXPECT_EQ(caret_rect, gfx::Rect(NSRectToCGRect(rect)));
367   EXPECT_EQ(caret_range, gfx::Range(actual_range));
369   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
370         gfx::Range(0, 0).ToNSRange(),
371         &rect,
372         &actual_range));
373   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
374         gfx::Range(1, 2).ToNSRange(),
375         &rect,
376         &actual_range));
377   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
378         gfx::Range(2, 3).ToNSRange(),
379         &rect,
380         &actual_range));
382   // No caret.
383   caret_range = gfx::Range(1, 2);
384   rwhv_mac_->SelectionChanged(kDummyString, kDummyOffset, caret_range);
385   params.anchor_rect = caret_rect;
386   params.focus_rect = gfx::Rect(30, 11, 0, 10);
387   rwhv_mac_->SelectionBoundsChanged(params);
388   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
389         gfx::Range(0, 0).ToNSRange(),
390         &rect,
391         &actual_range));
392   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
393         gfx::Range(0, 1).ToNSRange(),
394         &rect,
395         &actual_range));
396   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
397         gfx::Range(1, 1).ToNSRange(),
398         &rect,
399         &actual_range));
400   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
401         gfx::Range(1, 2).ToNSRange(),
402         &rect,
403         &actual_range));
404   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
405         gfx::Range(2, 2).ToNSRange(),
406         &rect,
407         &actual_range));
410 TEST_F(RenderWidgetHostViewMacTest, UpdateCompositionSinglelineCase) {
411   const gfx::Point kOrigin(10, 11);
412   const gfx::Size kBoundsUnit(10, 20);
414   NSRect rect;
415   // Make sure not crashing by passing NULL pointer instead of |actual_range|.
416   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
417       gfx::Range(0, 0).ToNSRange(),
418       &rect,
419       NULL));
421   // If there are no update from renderer, always returned caret position.
422   NSRange actual_range;
423   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
424       gfx::Range(0, 0).ToNSRange(),
425       &rect,
426       &actual_range));
427   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
428       gfx::Range(0, 1).ToNSRange(),
429       &rect,
430       &actual_range));
431   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
432       gfx::Range(1, 0).ToNSRange(),
433       &rect,
434       &actual_range));
435   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
436       gfx::Range(1, 1).ToNSRange(),
437       &rect,
438       &actual_range));
439   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
440       gfx::Range(1, 2).ToNSRange(),
441       &rect,
442       &actual_range));
444   // If the firstRectForCharacterRange is failed in renderer, empty rect vector
445   // is sent. Make sure this does not crash.
446   rwhv_mac_->ImeCompositionRangeChanged(gfx::Range(10, 12),
447                                         std::vector<gfx::Rect>());
448   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
449       gfx::Range(10, 11).ToNSRange(),
450       &rect,
451       NULL));
453   const int kCompositionLength = 10;
454   std::vector<gfx::Rect> composition_bounds;
455   const int kCompositionStart = 3;
456   const gfx::Range kCompositionRange(kCompositionStart,
457                                     kCompositionStart + kCompositionLength);
458   GenerateCompositionRectArray(kOrigin,
459                                kBoundsUnit,
460                                kCompositionLength,
461                                std::vector<size_t>(),
462                                &composition_bounds);
463   rwhv_mac_->ImeCompositionRangeChanged(kCompositionRange, composition_bounds);
465   // Out of range requests will return caret position.
466   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
467       gfx::Range(0, 0).ToNSRange(),
468       &rect,
469       &actual_range));
470   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
471       gfx::Range(1, 1).ToNSRange(),
472       &rect,
473       &actual_range));
474   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
475       gfx::Range(1, 2).ToNSRange(),
476       &rect,
477       &actual_range));
478   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
479       gfx::Range(2, 2).ToNSRange(),
480       &rect,
481       &actual_range));
482   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
483       gfx::Range(13, 14).ToNSRange(),
484       &rect,
485       &actual_range));
486   EXPECT_FALSE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
487       gfx::Range(14, 15).ToNSRange(),
488       &rect,
489       &actual_range));
491   for (int i = 0; i <= kCompositionLength; ++i) {
492     for (int j = 0; j <= kCompositionLength - i; ++j) {
493       const gfx::Range range(i, i + j);
494       const gfx::Rect expected_rect = GetExpectedRect(kOrigin,
495                                                       kBoundsUnit,
496                                                       range,
497                                                       0);
498       const NSRange request_range = gfx::Range(
499           kCompositionStart + range.start(),
500           kCompositionStart + range.end()).ToNSRange();
501       EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
502             request_range,
503             &rect,
504             &actual_range));
505       EXPECT_EQ(gfx::Range(request_range), gfx::Range(actual_range));
506       EXPECT_EQ(expected_rect, gfx::Rect(NSRectToCGRect(rect)));
508       // Make sure not crashing by passing NULL pointer instead of
509       // |actual_range|.
510       EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(
511             request_range,
512             &rect,
513             NULL));
514     }
515   }
518 TEST_F(RenderWidgetHostViewMacTest, UpdateCompositionMultilineCase) {
519   const gfx::Point kOrigin(10, 11);
520   const gfx::Size kBoundsUnit(10, 20);
521   NSRect rect;
523   const int kCompositionLength = 30;
524   std::vector<gfx::Rect> composition_bounds;
525   const gfx::Range kCompositionRange(0, kCompositionLength);
526   // Set breaking point at 10 and 20.
527   std::vector<size_t> break_points;
528   break_points.push_back(10);
529   break_points.push_back(20);
530   GenerateCompositionRectArray(kOrigin,
531                                kBoundsUnit,
532                                kCompositionLength,
533                                break_points,
534                                &composition_bounds);
535   rwhv_mac_->ImeCompositionRangeChanged(kCompositionRange, composition_bounds);
537   // Range doesn't contain line breaking point.
538   gfx::Range range;
539   range = gfx::Range(5, 8);
540   NSRange actual_range;
541   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
542                                                              &rect,
543                                                              &actual_range));
544   EXPECT_EQ(range, gfx::Range(actual_range));
545   EXPECT_EQ(
546       GetExpectedRect(kOrigin, kBoundsUnit, range, 0),
547       gfx::Rect(NSRectToCGRect(rect)));
548   range = gfx::Range(15, 18);
549   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
550                                                              &rect,
551                                                              &actual_range));
552   EXPECT_EQ(range, gfx::Range(actual_range));
553   EXPECT_EQ(
554       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 8), 1),
555       gfx::Rect(NSRectToCGRect(rect)));
556   range = gfx::Range(25, 28);
557   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
558                                                              &rect,
559                                                              &actual_range));
560   EXPECT_EQ(range, gfx::Range(actual_range));
561   EXPECT_EQ(
562       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 8), 2),
563       gfx::Rect(NSRectToCGRect(rect)));
565   // Range contains line breaking point.
566   range = gfx::Range(8, 12);
567   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
568                                                              &rect,
569                                                              &actual_range));
570   EXPECT_EQ(gfx::Range(8, 10), gfx::Range(actual_range));
571   EXPECT_EQ(
572       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(8, 10), 0),
573       gfx::Rect(NSRectToCGRect(rect)));
574   range = gfx::Range(18, 22);
575   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
576                                                              &rect,
577                                                              &actual_range));
578   EXPECT_EQ(gfx::Range(18, 20), gfx::Range(actual_range));
579   EXPECT_EQ(
580       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(8, 10), 1),
581       gfx::Rect(NSRectToCGRect(rect)));
583   // Start point is line breaking point.
584   range = gfx::Range(10, 12);
585   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
586                                                              &rect,
587                                                              &actual_range));
588   EXPECT_EQ(gfx::Range(10, 12), gfx::Range(actual_range));
589   EXPECT_EQ(
590       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 2), 1),
591       gfx::Rect(NSRectToCGRect(rect)));
592   range = gfx::Range(20, 22);
593   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
594                                                              &rect,
595                                                              &actual_range));
596   EXPECT_EQ(gfx::Range(20, 22), gfx::Range(actual_range));
597   EXPECT_EQ(
598       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 2), 2),
599       gfx::Rect(NSRectToCGRect(rect)));
601   // End point is line breaking point.
602   range = gfx::Range(5, 10);
603   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
604                                                              &rect,
605                                                              &actual_range));
606   EXPECT_EQ(gfx::Range(5, 10), gfx::Range(actual_range));
607   EXPECT_EQ(
608       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 10), 0),
609       gfx::Rect(NSRectToCGRect(rect)));
610   range = gfx::Range(15, 20);
611   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
612                                                              &rect,
613                                                              &actual_range));
614   EXPECT_EQ(gfx::Range(15, 20), gfx::Range(actual_range));
615   EXPECT_EQ(
616       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(5, 10), 1),
617       gfx::Rect(NSRectToCGRect(rect)));
619   // Start and end point are same line breaking point.
620   range = gfx::Range(10, 10);
621   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
622                                                              &rect,
623                                                              &actual_range));
624   EXPECT_EQ(gfx::Range(10, 10), gfx::Range(actual_range));
625   EXPECT_EQ(
626       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 0), 1),
627       gfx::Rect(NSRectToCGRect(rect)));
628   range = gfx::Range(20, 20);
629   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
630                                                              &rect,
631                                                              &actual_range));
632   EXPECT_EQ(gfx::Range(20, 20), gfx::Range(actual_range));
633   EXPECT_EQ(
634       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 0), 2),
635       gfx::Rect(NSRectToCGRect(rect)));
637   // Start and end point are different line breaking point.
638   range = gfx::Range(10, 20);
639   EXPECT_TRUE(rwhv_mac_->GetCachedFirstRectForCharacterRange(range.ToNSRange(),
640                                                              &rect,
641                                                              &actual_range));
642   EXPECT_EQ(gfx::Range(10, 20), gfx::Range(actual_range));
643   EXPECT_EQ(
644       GetExpectedRect(kOrigin, kBoundsUnit, gfx::Range(0, 10), 1),
645       gfx::Rect(NSRectToCGRect(rect)));
648 // Verify that |SetActive()| calls |RenderWidgetHostImpl::Blur()| and
649 // |RenderWidgetHostImp::Focus()|.
650 TEST_F(RenderWidgetHostViewMacTest, BlurAndFocusOnSetActive) {
651   MockRenderWidgetHostDelegate delegate;
652   TestBrowserContext browser_context;
653   MockRenderProcessHost* process_host =
654       new MockRenderProcessHost(&browser_context);
656   // Owned by its |cocoa_view()|.
657   MockRenderWidgetHostImpl* rwh = new MockRenderWidgetHostImpl(
658       &delegate, process_host, MSG_ROUTING_NONE);
659   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(rwh, false);
661   base::scoped_nsobject<CocoaTestHelperWindow> window(
662       [[CocoaTestHelperWindow alloc] init]);
663   [[window contentView] addSubview:view->cocoa_view()];
665   EXPECT_CALL(*rwh, Focus());
666   [window makeFirstResponder:view->cocoa_view()];
667   testing::Mock::VerifyAndClearExpectations(rwh);
669   EXPECT_CALL(*rwh, Blur());
670   view->SetActive(false);
671   testing::Mock::VerifyAndClearExpectations(rwh);
673   EXPECT_CALL(*rwh, Focus());
674   view->SetActive(true);
675   testing::Mock::VerifyAndClearExpectations(rwh);
677   // Unsetting first responder should blur.
678   EXPECT_CALL(*rwh, Blur());
679   [window makeFirstResponder:nil];
680   testing::Mock::VerifyAndClearExpectations(rwh);
682   // |SetActive()| shoud not focus if view is not first responder.
683   EXPECT_CALL(*rwh, Focus()).Times(0);
684   view->SetActive(true);
685   testing::Mock::VerifyAndClearExpectations(rwh);
687   // Clean up.
688   rwh->Shutdown();
691 TEST_F(RenderWidgetHostViewMacTest, ScrollWheelEndEventDelivery) {
692   // This tests Lion+ functionality, so don't run the test pre-Lion.
693   if (!base::mac::IsOSLionOrLater())
694     return;
696   // Initialize the view associated with a MockRenderWidgetHostImpl, rather than
697   // the MockRenderProcessHost that is set up by the test harness which mocks
698   // out |OnMessageReceived()|.
699   TestBrowserContext browser_context;
700   MockRenderProcessHost* process_host =
701       new MockRenderProcessHost(&browser_context);
702   MockRenderWidgetHostDelegate delegate;
703   MockRenderWidgetHostImpl* host = new MockRenderWidgetHostImpl(
704       &delegate, process_host, MSG_ROUTING_NONE);
705   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(host, false);
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   InputHostMsg_HandleInputEvent_ACK_Params ack;
714   ack.type = blink::WebInputEvent::MouseWheel;
715   ack.state = INPUT_EVENT_ACK_STATE_CONSUMED;
716   scoped_ptr<IPC::Message> response(
717       new InputHostMsg_HandleInputEvent_ACK(0, ack));
718   host->OnMessageReceived(*response);
720   // Post the NSEventPhaseEnded wheel event to NSApp and check whether the
721   // render view receives it.
722   NSEvent* event2 = MockScrollWheelEventWithPhase(@selector(phaseEnded), 0);
723   [NSApp postEvent:event2 atStart:NO];
724   base::MessageLoop::current()->RunUntilIdle();
725   ASSERT_EQ(2U, process_host->sink().message_count());
727   // Clean up.
728   host->Shutdown();
731 TEST_F(RenderWidgetHostViewMacTest, IgnoreEmptyUnhandledWheelEvent) {
732   // This tests Lion+ functionality, so don't run the test pre-Lion.
733   if (!base::mac::IsOSLionOrLater())
734     return;
736   // Initialize the view associated with a MockRenderWidgetHostImpl, rather than
737   // the MockRenderProcessHost that is set up by the test harness which mocks
738   // out |OnMessageReceived()|.
739   TestBrowserContext browser_context;
740   MockRenderProcessHost* process_host =
741       new MockRenderProcessHost(&browser_context);
742   MockRenderWidgetHostDelegate delegate;
743   MockRenderWidgetHostImpl* host = new MockRenderWidgetHostImpl(
744       &delegate, process_host, MSG_ROUTING_NONE);
745   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(host, false);
747   // Add a delegate to the view.
748   base::scoped_nsobject<MockRenderWidgetHostViewMacDelegate> view_delegate(
749       [[MockRenderWidgetHostViewMacDelegate alloc] init]);
750   view->SetDelegate(view_delegate.get());
752   // Send an initial wheel event for scrolling by 3 lines.
753   NSEvent* event1 = MockScrollWheelEventWithPhase(@selector(phaseBegan), 3);
754   [view->cocoa_view() scrollWheel:event1];
755   ASSERT_EQ(1U, process_host->sink().message_count());
756   process_host->sink().ClearMessages();
758   // Indicate that the wheel event was unhandled.
759   InputHostMsg_HandleInputEvent_ACK_Params unhandled_ack;
760   unhandled_ack.type = blink::WebInputEvent::MouseWheel;
761   unhandled_ack.state = INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
762   scoped_ptr<IPC::Message> response1(
763       new InputHostMsg_HandleInputEvent_ACK(0, unhandled_ack));
764   host->OnMessageReceived(*response1);
766   // Check that the view delegate got an unhandled wheel event.
767   ASSERT_EQ(YES, view_delegate.get().unhandledWheelEventReceived);
768   view_delegate.get().unhandledWheelEventReceived = NO;
770   // Send another wheel event, this time for scrolling by 0 lines (empty event).
771   NSEvent* event2 = MockScrollWheelEventWithPhase(@selector(phaseChanged), 0);
772   [view->cocoa_view() scrollWheel:event2];
773   ASSERT_EQ(1U, process_host->sink().message_count());
775   // Indicate that the wheel event was also unhandled.
776   scoped_ptr<IPC::Message> response2(
777       new InputHostMsg_HandleInputEvent_ACK(0, unhandled_ack));
778   host->OnMessageReceived(*response2);
780   // Check that the view delegate ignored the empty unhandled wheel event.
781   ASSERT_EQ(NO, view_delegate.get().unhandledWheelEventReceived);
783   // Clean up.
784   host->Shutdown();
787 // Tests that when view initiated shutdown happens (i.e. RWHView is deleted
788 // before RWH), we clean up properly and don't leak the RWHVGuest.
789 TEST_F(RenderWidgetHostViewMacTest, GuestViewDoesNotLeak) {
790   MockRenderWidgetHostDelegate delegate;
791   TestBrowserContext browser_context;
792   MockRenderProcessHost* process_host =
793       new MockRenderProcessHost(&browser_context);
795   // Owned by its |cocoa_view()|.
796   MockRenderWidgetHostImpl* rwh = new MockRenderWidgetHostImpl(
797       &delegate, process_host, MSG_ROUTING_NONE);
798   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(rwh, true);
800   // Add a delegate to the view.
801   base::scoped_nsobject<MockRenderWidgetHostViewMacDelegate> view_delegate(
802       [[MockRenderWidgetHostViewMacDelegate alloc] init]);
803   view->SetDelegate(view_delegate.get());
805   base::WeakPtr<RenderWidgetHostViewBase> guest_rwhv_weak =
806       (new RenderWidgetHostViewGuest(
807            rwh, NULL, view->GetWeakPtr()))->GetWeakPtr();
809   // Remove the cocoa_view() so |view| also goes away before |rwh|.
810   {
811     base::scoped_nsobject<RenderWidgetHostViewCocoa> rwhv_cocoa;
812     rwhv_cocoa.reset([view->cocoa_view() retain]);
813   }
814   RecycleAndWait();
816   // Clean up.
817   rwh->Shutdown();
819   // Let |guest_rwhv_weak| have a chance to delete itself.
820   base::RunLoop run_loop;
821   content::BrowserThread::PostTask(
822       content::BrowserThread::UI, FROM_HERE, run_loop.QuitClosure());
823   run_loop.Run();
825   ASSERT_FALSE(guest_rwhv_weak.get());
828 // Tests setting background transparency. See also (disabled on Mac)
829 // RenderWidgetHostTest.Background. This test has some additional checks for
830 // Mac.
831 TEST_F(RenderWidgetHostViewMacTest, Background) {
832   TestBrowserContext browser_context;
833   MockRenderProcessHost* process_host =
834       new MockRenderProcessHost(&browser_context);
835   MockRenderWidgetHostDelegate delegate;
836   MockRenderWidgetHostImpl* host = new MockRenderWidgetHostImpl(
837       &delegate, process_host, MSG_ROUTING_NONE);
838   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(host, false);
840   EXPECT_TRUE(view->GetBackgroundOpaque());
841   EXPECT_TRUE([view->cocoa_view() isOpaque]);
843   view->SetBackgroundColor(SK_ColorTRANSPARENT);
844   EXPECT_FALSE(view->GetBackgroundOpaque());
845   EXPECT_FALSE([view->cocoa_view() isOpaque]);
847   const IPC::Message* set_background;
848   set_background = process_host->sink().GetUniqueMessageMatching(
849       ViewMsg_SetBackgroundOpaque::ID);
850   ASSERT_TRUE(set_background);
851   Tuple<bool> sent_background;
852   ViewMsg_SetBackgroundOpaque::Read(set_background, &sent_background);
853   EXPECT_FALSE(get<0>(sent_background));
855   // Try setting it back.
856   process_host->sink().ClearMessages();
857   view->SetBackgroundColor(SK_ColorWHITE);
858   EXPECT_TRUE(view->GetBackgroundOpaque());
859   EXPECT_TRUE([view->cocoa_view() isOpaque]);
860   set_background = process_host->sink().GetUniqueMessageMatching(
861       ViewMsg_SetBackgroundOpaque::ID);
862   ASSERT_TRUE(set_background);
863   ViewMsg_SetBackgroundOpaque::Read(set_background, &sent_background);
864   EXPECT_TRUE(get<0>(sent_background));
866   host->Shutdown();
869 TEST_F(RenderWidgetHostViewMacTest, PinchThresholding) {
870   // This tests Lion+ functionality, so don't run the test pre-Lion.
871   if (!base::mac::IsOSLionOrLater())
872     return;
874   // Initialize the view associated with a MockRenderWidgetHostImpl, rather than
875   // the MockRenderProcessHost that is set up by the test harness which mocks
876   // out |OnMessageReceived()|.
877   TestBrowserContext browser_context;
878   MockRenderProcessHost* process_host =
879       new MockRenderProcessHost(&browser_context);
880   MockRenderWidgetHostDelegate delegate;
881   MockRenderWidgetHostImpl* host = new MockRenderWidgetHostImpl(
882       &delegate, process_host, MSG_ROUTING_NONE);
883   RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(host, false);
885   // We'll use this IPC message to ack events.
886   InputHostMsg_HandleInputEvent_ACK_Params ack;
887   ack.type = blink::WebInputEvent::GesturePinchUpdate;
888   ack.state = INPUT_EVENT_ACK_STATE_CONSUMED;
889   scoped_ptr<IPC::Message> response(
890       new InputHostMsg_HandleInputEvent_ACK(0, ack));
892   // Do a gesture that crosses the threshold.
893   {
894     NSEvent* pinchBeginEvent =
895         MockGestureEvent(NSEventTypeBeginGesture, 100.1, 0);
896     NSEvent* pinchUpdateEvents[3] = {
897         MockGestureEvent(NSEventTypeMagnify, 100.2, 0.25),
898         MockGestureEvent(NSEventTypeMagnify, 100.3, 0.25),
899         MockGestureEvent(NSEventTypeMagnify, 100.4, 0.25),
900     };
901     NSEvent* pinchEndEvent =
902         MockGestureEvent(NSEventTypeEndGesture, 100.5, 0);
904     // No messages are sent for the pinch begin and the first update event.
905     [view->cocoa_view() beginGestureWithEvent:pinchBeginEvent];
906     [view->cocoa_view() magnifyWithEvent:pinchUpdateEvents[0]];
907     ASSERT_EQ(0U, process_host->sink().message_count());
909     // The second update event crosses the threshold of 0.4, and so a begin
910     // and update are sent.
911     [view->cocoa_view() magnifyWithEvent:pinchUpdateEvents[1]];
912     ASSERT_EQ(2U, process_host->sink().message_count());
913     host->OnMessageReceived(*response);
915     // The third update only causes one event to be sent.
916     [view->cocoa_view() magnifyWithEvent:pinchUpdateEvents[2]];
917     ASSERT_EQ(3U, process_host->sink().message_count());
918     host->OnMessageReceived(*response);
920     // As does the end.
921     [view->cocoa_view() endGestureWithEvent:pinchEndEvent];
922     ASSERT_EQ(4U, process_host->sink().message_count());
924     process_host->sink().ClearMessages();
925   }
927   // Do a gesture that doesn't cross the threshold, but happens within 1 second,
928   // so it should be sent to the renderer.
929   {
930     NSEvent* pinchBeginEvent =
931         MockGestureEvent(NSEventTypeBeginGesture, 101.0, 0);
932     NSEvent* pinchUpdateEvent =
933         MockGestureEvent(NSEventTypeMagnify, 101.1, 0.25);
934     NSEvent* pinchEndEvent =
935         MockGestureEvent(NSEventTypeEndGesture, 101.2, 0);
937     // No message comes for the begin event.
938     [view->cocoa_view() beginGestureWithEvent:pinchBeginEvent];
939     ASSERT_EQ(0U, process_host->sink().message_count());
941     // Two messages come for the first update event.
942     [view->cocoa_view() magnifyWithEvent:pinchUpdateEvent];
943     ASSERT_EQ(2U, process_host->sink().message_count());
944     host->OnMessageReceived(*response);
946     // The end event sends one message.
947     [view->cocoa_view() endGestureWithEvent:pinchEndEvent];
948     ASSERT_EQ(3U, process_host->sink().message_count());
950     process_host->sink().ClearMessages();
951   }
953   // Do a gesture that doesn't cross the threshold and happens more than one
954   // second later.
955   {
956     NSEvent* pinchBeginEvent =
957         MockGestureEvent(NSEventTypeBeginGesture, 103.0, 0);
958     NSEvent* pinchUpdateEvent =
959         MockGestureEvent(NSEventTypeMagnify, 103.1, 0.25);
960     NSEvent* pinchEndEvent =
961         MockGestureEvent(NSEventTypeEndGesture, 103.2, 0);
963     // No message comes for the begin event.
964     [view->cocoa_view() beginGestureWithEvent:pinchBeginEvent];
965     ASSERT_EQ(0U, process_host->sink().message_count());
967     // Two messages come for the first update event.
968     [view->cocoa_view() magnifyWithEvent:pinchUpdateEvent];
969     ASSERT_EQ(0U, process_host->sink().message_count());
971     // As does the end.
972     [view->cocoa_view() endGestureWithEvent:pinchEndEvent];
973     ASSERT_EQ(0U, process_host->sink().message_count());
975     process_host->sink().ClearMessages();
976   }
978   // Clean up.
979   host->Shutdown();
983 }  // namespace content