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