1// Copyright 2014 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 "ui/views/view_targeter.h"
6
7#include "ui/events/event_targeter.h"
8#include "ui/events/event_utils.h"
9#include "ui/gfx/path.h"
10#include "ui/views/masked_targeter_delegate.h"
11#include "ui/views/test/views_test_base.h"
12#include "ui/views/view_targeter.h"
13#include "ui/views/view_targeter_delegate.h"
14#include "ui/views/widget/root_view.h"
15
16namespace views {
17
18// A derived class of View used for testing purposes.
19class TestingView : public View, public ViewTargeterDelegate {
20 public:
21  TestingView() : can_process_events_within_subtree_(true) {}
22  virtual ~TestingView() {}
23
24  // Reset all test state.
25  void Reset() { can_process_events_within_subtree_ = true; }
26
27  void set_can_process_events_within_subtree(bool can_process) {
28    can_process_events_within_subtree_ = can_process;
29  }
30
31  // A call-through function to ViewTargeterDelegate::DoesIntersectRect().
32  bool TestDoesIntersectRect(const View* target, const gfx::Rect& rect) const {
33    return DoesIntersectRect(target, rect);
34  }
35
36  // View:
37  virtual bool CanProcessEventsWithinSubtree() const OVERRIDE {
38    return can_process_events_within_subtree_;
39  }
40
41 private:
42  // Value to return from CanProcessEventsWithinSubtree().
43  bool can_process_events_within_subtree_;
44
45  DISALLOW_COPY_AND_ASSIGN(TestingView);
46};
47
48// A derived class of View having a triangular-shaped hit test mask.
49class TestMaskedView : public View, public MaskedTargeterDelegate {
50 public:
51  TestMaskedView() {}
52  virtual ~TestMaskedView() {}
53
54  // A call-through function to MaskedTargeterDelegate::DoesIntersectRect().
55  bool TestDoesIntersectRect(const View* target, const gfx::Rect& rect) const {
56    return DoesIntersectRect(target, rect);
57  }
58
59 private:
60  // MaskedTargeterDelegate:
61  virtual bool GetHitTestMask(gfx::Path* mask) const OVERRIDE {
62    DCHECK(mask);
63    SkScalar w = SkIntToScalar(width());
64    SkScalar h = SkIntToScalar(height());
65
66    // Create a triangular mask within the bounds of this View.
67    mask->moveTo(w / 2, 0);
68    mask->lineTo(w, h);
69    mask->lineTo(0, h);
70    mask->close();
71    return true;
72  }
73
74  DISALLOW_COPY_AND_ASSIGN(TestMaskedView);
75};
76
77namespace test {
78
79// TODO(tdanderson): Clean up this test suite by moving common code/state into
80//                   ViewTargeterTest and overriding SetUp(), TearDown(), etc.
81//                   See crbug.com/355680.
82class ViewTargeterTest : public ViewsTestBase {
83 public:
84  ViewTargeterTest() {}
85  virtual ~ViewTargeterTest() {}
86
87  void SetGestureHandler(internal::RootView* root_view, View* handler) {
88    root_view->gesture_handler_ = handler;
89  }
90
91  void SetGestureHandlerSetBeforeProcessing(internal::RootView* root_view,
92                                            bool set) {
93    root_view->gesture_handler_set_before_processing_ = set;
94  }
95
96 private:
97  DISALLOW_COPY_AND_ASSIGN(ViewTargeterTest);
98};
99
100namespace {
101
102gfx::Point ConvertPointFromWidgetToView(View* view, const gfx::Point& p) {
103  gfx::Point tmp(p);
104  View::ConvertPointToTarget(view->GetWidget()->GetRootView(), view, &tmp);
105  return tmp;
106}
107
108gfx::Rect ConvertRectFromWidgetToView(View* view, const gfx::Rect& r) {
109  gfx::Rect tmp(r);
110  tmp.set_origin(ConvertPointFromWidgetToView(view, r.origin()));
111  return tmp;
112}
113
114}  // namespace
115
116// Verifies that the the functions ViewTargeter::FindTargetForEvent()
117// and ViewTargeter::FindNextBestTarget() are implemented correctly
118// for key events.
119TEST_F(ViewTargeterTest, ViewTargeterForKeyEvents) {
120  Widget widget;
121  Widget::InitParams init_params =
122      CreateParams(Widget::InitParams::TYPE_POPUP);
123  init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
124  widget.Init(init_params);
125
126  View* content = new View;
127  View* child = new View;
128  View* grandchild = new View;
129
130  widget.SetContentsView(content);
131  content->AddChildView(child);
132  child->AddChildView(grandchild);
133
134  grandchild->SetFocusable(true);
135  grandchild->RequestFocus();
136
137  internal::RootView* root_view =
138      static_cast<internal::RootView*>(widget.GetRootView());
139  ui::EventTargeter* targeter = root_view->targeter();
140
141  ui::KeyEvent key_event('a', ui::VKEY_A, ui::EF_NONE);
142
143  // The focused view should be the initial target of the event.
144  ui::EventTarget* current_target = targeter->FindTargetForEvent(root_view,
145                                                                 &key_event);
146  EXPECT_EQ(grandchild, static_cast<View*>(current_target));
147
148  // Verify that FindNextBestTarget() will return the parent view of the
149  // argument (and NULL if the argument has no parent view).
150  current_target = targeter->FindNextBestTarget(grandchild, &key_event);
151  EXPECT_EQ(child, static_cast<View*>(current_target));
152  current_target = targeter->FindNextBestTarget(child, &key_event);
153  EXPECT_EQ(content, static_cast<View*>(current_target));
154  current_target = targeter->FindNextBestTarget(content, &key_event);
155  EXPECT_EQ(widget.GetRootView(), static_cast<View*>(current_target));
156  current_target = targeter->FindNextBestTarget(widget.GetRootView(),
157                                                &key_event);
158  EXPECT_EQ(NULL, static_cast<View*>(current_target));
159}
160
161// Verifies that the the functions ViewTargeter::FindTargetForEvent()
162// and ViewTargeter::FindNextBestTarget() are implemented correctly
163// for scroll events.
164TEST_F(ViewTargeterTest, ViewTargeterForScrollEvents) {
165  Widget widget;
166  Widget::InitParams init_params =
167      CreateParams(Widget::InitParams::TYPE_POPUP);
168  init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
169  init_params.bounds = gfx::Rect(0, 0, 200, 200);
170  widget.Init(init_params);
171
172  // The coordinates used for SetBounds() are in the parent coordinate space.
173  View* content = new View;
174  content->SetBounds(0, 0, 100, 100);
175  View* child = new View;
176  child->SetBounds(50, 50, 20, 20);
177  View* grandchild = new View;
178  grandchild->SetBounds(0, 0, 5, 5);
179
180  widget.SetContentsView(content);
181  content->AddChildView(child);
182  child->AddChildView(grandchild);
183
184  internal::RootView* root_view =
185      static_cast<internal::RootView*>(widget.GetRootView());
186  ui::EventTargeter* targeter = root_view->targeter();
187
188  // The event falls within the bounds of |child| and |content| but not
189  // |grandchild|, so |child| should be the initial target for the event.
190  ui::ScrollEvent scroll(ui::ET_SCROLL,
191                         gfx::Point(60, 60),
192                         ui::EventTimeForNow(),
193                         0,
194                         0, 3,
195                         0, 3,
196                         2);
197  ui::EventTarget* current_target = targeter->FindTargetForEvent(root_view,
198                                                                 &scroll);
199  EXPECT_EQ(child, static_cast<View*>(current_target));
200
201  // Verify that FindNextBestTarget() will return the parent view of the
202  // argument (and NULL if the argument has no parent view).
203  current_target = targeter->FindNextBestTarget(child, &scroll);
204  EXPECT_EQ(content, static_cast<View*>(current_target));
205  current_target = targeter->FindNextBestTarget(content, &scroll);
206  EXPECT_EQ(widget.GetRootView(), static_cast<View*>(current_target));
207  current_target = targeter->FindNextBestTarget(widget.GetRootView(),
208                                                &scroll);
209  EXPECT_EQ(NULL, static_cast<View*>(current_target));
210
211  // The event falls outside of the original specified bounds of |content|,
212  // |child|, and |grandchild|. But since |content| is the contents view,
213  // and contents views are resized to fill the entire area of the root
214  // view, the event's initial target should still be |content|.
215  scroll = ui::ScrollEvent(ui::ET_SCROLL,
216                           gfx::Point(150, 150),
217                           ui::EventTimeForNow(),
218                           0,
219                           0, 3,
220                           0, 3,
221                           2);
222  current_target = targeter->FindTargetForEvent(root_view, &scroll);
223  EXPECT_EQ(content, static_cast<View*>(current_target));
224}
225
226// Convenience to make constructing a GestureEvent simpler.
227class GestureEventForTest : public ui::GestureEvent {
228 public:
229  GestureEventForTest(ui::EventType type, int x, int y)
230      : GestureEvent(x,
231                     y,
232                     0,
233                     base::TimeDelta(),
234                     ui::GestureEventDetails(type)) {}
235
236  GestureEventForTest(ui::GestureEventDetails details, int x, int y)
237      : GestureEvent(x, y, 0, base::TimeDelta(), details) {}
238};
239
240// Verifies that the the functions ViewTargeter::FindTargetForEvent()
241// and ViewTargeter::FindNextBestTarget() are implemented correctly
242// for gesture events.
243TEST_F(ViewTargeterTest, ViewTargeterForGestureEvents) {
244  Widget widget;
245  Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_POPUP);
246  init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
247  init_params.bounds = gfx::Rect(0, 0, 200, 200);
248  widget.Init(init_params);
249
250  // The coordinates used for SetBounds() are in the parent coordinate space.
251  View* content = new View;
252  content->SetBounds(0, 0, 100, 100);
253  View* child = new View;
254  child->SetBounds(50, 50, 20, 20);
255  View* grandchild = new View;
256  grandchild->SetBounds(0, 0, 5, 5);
257
258  widget.SetContentsView(content);
259  content->AddChildView(child);
260  child->AddChildView(grandchild);
261
262  internal::RootView* root_view =
263      static_cast<internal::RootView*>(widget.GetRootView());
264  ui::EventTargeter* targeter = root_view->targeter();
265
266  // Define some gesture events for testing.
267  gfx::Rect bounding_box(gfx::Point(46, 46), gfx::Size(8, 8));
268  gfx::Point center_point(bounding_box.CenterPoint());
269  ui::GestureEventDetails details(ui::ET_GESTURE_TAP);
270  details.set_bounding_box(bounding_box);
271  GestureEventForTest tap(details, center_point.x(), center_point.y());
272  details = ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN);
273  details.set_bounding_box(bounding_box);
274  GestureEventForTest scroll_begin(details, center_point.x(), center_point.y());
275  details = ui::GestureEventDetails(ui::ET_GESTURE_END);
276  details.set_bounding_box(bounding_box);
277  GestureEventForTest end(details, center_point.x(), center_point.y());
278
279  // Assume that the view currently handling gestures has been set as
280  // |grandchild| by a previous gesture event. Thus subsequent TAP and
281  // SCROLL_BEGIN events should be initially targeted to |grandchild|, and
282  // re-targeting should be prohibited for TAP but permitted for
283  // GESTURE_SCROLL_BEGIN (which should be re-targeted to the parent of
284  // |grandchild|).
285  SetGestureHandlerSetBeforeProcessing(root_view, true);
286  SetGestureHandler(root_view, grandchild);
287  EXPECT_EQ(grandchild, targeter->FindTargetForEvent(root_view, &tap));
288  EXPECT_EQ(NULL, targeter->FindNextBestTarget(grandchild, &tap));
289  EXPECT_EQ(grandchild, targeter->FindTargetForEvent(root_view, &scroll_begin));
290  EXPECT_EQ(child, targeter->FindNextBestTarget(grandchild, &scroll_begin));
291
292  // GESTURE_END events should be targeted to the existing gesture handler,
293  // but re-targeting should be prohibited.
294  EXPECT_EQ(grandchild, targeter->FindTargetForEvent(root_view, &end));
295  EXPECT_EQ(NULL, targeter->FindNextBestTarget(grandchild, &end));
296
297  // Assume that the view currently handling gestures is still set as
298  // |grandchild|, but this was not done by a previous gesture. Thus we are
299  // in the process of finding the View to which subsequent gestures will be
300  // dispatched, so TAP and SCROLL_BEGIN events should be re-targeted up
301  // the ancestor chain.
302  SetGestureHandlerSetBeforeProcessing(root_view, false);
303  EXPECT_EQ(child, targeter->FindNextBestTarget(grandchild, &tap));
304  EXPECT_EQ(child, targeter->FindNextBestTarget(grandchild, &scroll_begin));
305
306  // GESTURE_END events are not permitted to be re-targeted up the ancestor
307  // chain; they are only ever targeted in the case where the gesture handler
308  // was established by a previous gesture.
309  EXPECT_EQ(NULL, targeter->FindNextBestTarget(grandchild, &end));
310
311  // Assume that the default gesture handler was set by the previous gesture,
312  // but that this handler is currently NULL. No gesture events should be
313  // re-targeted in this case (regardless of the view that is passed in to
314  // FindNextBestTarget() as the previous target).
315  SetGestureHandler(root_view, NULL);
316  SetGestureHandlerSetBeforeProcessing(root_view, true);
317  EXPECT_EQ(NULL, targeter->FindNextBestTarget(child, &tap));
318  EXPECT_EQ(NULL, targeter->FindNextBestTarget(NULL, &tap));
319  EXPECT_EQ(NULL, targeter->FindNextBestTarget(content, &scroll_begin));
320  EXPECT_EQ(NULL, targeter->FindNextBestTarget(content, &end));
321
322  // Reset the locations of the gesture events to be in the root view
323  // coordinate space since we are about to call FindTargetForEvent()
324  // again (calls to FindTargetForEvent() and FindNextBestTarget()
325  // mutate the location of the gesture events to be in the coordinate
326  // space of the returned view).
327  details = ui::GestureEventDetails(ui::ET_GESTURE_TAP);
328  details.set_bounding_box(bounding_box);
329  tap = GestureEventForTest(details, center_point.x(), center_point.y());
330  details = ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN);
331  details.set_bounding_box(bounding_box);
332  scroll_begin =
333      GestureEventForTest(details, center_point.x(), center_point.y());
334  details = ui::GestureEventDetails(ui::ET_GESTURE_END);
335  details.set_bounding_box(bounding_box);
336  end = GestureEventForTest(details, center_point.x(), center_point.y());
337
338  // If no default gesture handler is currently set, targeting should be
339  // performed using the location of the gesture event for a TAP and a
340  // SCROLL_BEGIN.
341  SetGestureHandlerSetBeforeProcessing(root_view, false);
342  EXPECT_EQ(grandchild, targeter->FindTargetForEvent(root_view, &tap));
343  EXPECT_EQ(grandchild, targeter->FindTargetForEvent(root_view, &scroll_begin));
344
345  // If no default gesture handler is currently set, GESTURE_END events
346  // should never be targeted or re-targeted to any View.
347  EXPECT_EQ(NULL, targeter->FindTargetForEvent(root_view, &end));
348  EXPECT_EQ(NULL, targeter->FindNextBestTarget(NULL, &end));
349  EXPECT_EQ(NULL, targeter->FindNextBestTarget(child, &end));
350}
351
352// Tests that calls to FindTargetForEvent() and FindNextBestTarget() change
353// the location of a gesture event to be in the correct coordinate space.
354TEST_F(ViewTargeterTest, GestureEventCoordinateConversion) {
355  Widget widget;
356  Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_POPUP);
357  init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
358  init_params.bounds = gfx::Rect(0, 0, 200, 200);
359  widget.Init(init_params);
360
361  // The coordinates used for SetBounds() are in the parent coordinate space.
362  View* content = new View;
363  content->SetBounds(0, 0, 100, 100);
364  View* child = new View;
365  child->SetBounds(50, 50, 20, 20);
366  View* grandchild = new View;
367  grandchild->SetBounds(5, 5, 10, 10);
368  View* great_grandchild = new View;
369  great_grandchild->SetBounds(3, 3, 4, 4);
370
371  widget.SetContentsView(content);
372  content->AddChildView(child);
373  child->AddChildView(grandchild);
374  grandchild->AddChildView(great_grandchild);
375
376  internal::RootView* root_view =
377      static_cast<internal::RootView*>(widget.GetRootView());
378  ui::EventTargeter* targeter = root_view->targeter();
379
380  // Define a GESTURE_TAP event with a bounding box centered at (60, 60)
381  // in root view coordinates with width and height of 4.
382  gfx::Rect bounding_box(gfx::Point(58, 58), gfx::Size(4, 4));
383  gfx::Point center_point(bounding_box.CenterPoint());
384  ui::GestureEventDetails details(ui::ET_GESTURE_TAP);
385  details.set_bounding_box(bounding_box);
386  GestureEventForTest tap(details, center_point.x(), center_point.y());
387
388  // Calculate the location of the gesture in each of the different
389  // coordinate spaces.
390  gfx::Point location_in_root(center_point);
391  EXPECT_EQ(gfx::Point(60, 60), location_in_root);
392  gfx::Point location_in_great_grandchild(
393      ConvertPointFromWidgetToView(great_grandchild, location_in_root));
394  EXPECT_EQ(gfx::Point(2, 2), location_in_great_grandchild);
395  gfx::Point location_in_grandchild(
396      ConvertPointFromWidgetToView(grandchild, location_in_root));
397  EXPECT_EQ(gfx::Point(5, 5), location_in_grandchild);
398  gfx::Point location_in_child(
399      ConvertPointFromWidgetToView(child, location_in_root));
400  EXPECT_EQ(gfx::Point(10, 10), location_in_child);
401  gfx::Point location_in_content(
402      ConvertPointFromWidgetToView(content, location_in_root));
403  EXPECT_EQ(gfx::Point(60, 60), location_in_content);
404
405  // Verify the location of |tap| is in screen coordinates.
406  EXPECT_EQ(gfx::Point(60, 60), tap.location());
407
408  // The initial target should be |great_grandchild| and the location of
409  // the event should be changed into the coordinate space of the target.
410  EXPECT_EQ(great_grandchild, targeter->FindTargetForEvent(root_view, &tap));
411  EXPECT_EQ(location_in_great_grandchild, tap.location());
412  SetGestureHandler(root_view, great_grandchild);
413
414  // The next target should be |grandchild| and the location of
415  // the event should be changed into the coordinate space of the target.
416  EXPECT_EQ(grandchild, targeter->FindNextBestTarget(great_grandchild, &tap));
417  EXPECT_EQ(location_in_grandchild, tap.location());
418  SetGestureHandler(root_view, grandchild);
419
420  // The next target should be |child| and the location of
421  // the event should be changed into the coordinate space of the target.
422  EXPECT_EQ(child, targeter->FindNextBestTarget(grandchild, &tap));
423  EXPECT_EQ(location_in_child, tap.location());
424  SetGestureHandler(root_view, child);
425
426  // The next target should be |content| and the location of
427  // the event should be changed into the coordinate space of the target.
428  EXPECT_EQ(content, targeter->FindNextBestTarget(child, &tap));
429  EXPECT_EQ(location_in_content, tap.location());
430  SetGestureHandler(root_view, content);
431
432  // The next target should be |root_view| and the location of
433  // the event should be changed into the coordinate space of the target.
434  EXPECT_EQ(widget.GetRootView(), targeter->FindNextBestTarget(content, &tap));
435  EXPECT_EQ(location_in_root, tap.location());
436  SetGestureHandler(root_view, widget.GetRootView());
437
438  // The next target should be NULL and the location of the event should
439  // remain unchanged.
440  EXPECT_EQ(NULL, targeter->FindNextBestTarget(widget.GetRootView(), &tap));
441  EXPECT_EQ(location_in_root, tap.location());
442}
443
444// Tests that the functions ViewTargeterDelegate::DoesIntersectRect()
445// and MaskedTargeterDelegate::DoesIntersectRect() work as intended when
446// called on views which are derived from ViewTargeterDelegate.
447// Also verifies that ViewTargeterDelegate::DoesIntersectRect() can
448// be called from the ViewTargeter installed on RootView.
449TEST_F(ViewTargeterTest, DoesIntersectRect) {
450  Widget widget;
451  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
452  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
453  params.bounds = gfx::Rect(0, 0, 650, 650);
454  widget.Init(params);
455
456  internal::RootView* root_view =
457      static_cast<internal::RootView*>(widget.GetRootView());
458  ViewTargeter* view_targeter = root_view->targeter();
459
460  // The coordinates used for SetBounds() are in the parent coordinate space.
461  TestingView v2;
462  TestMaskedView v1, v3;
463  v1.SetBounds(0, 0, 200, 200);
464  v2.SetBounds(300, 0, 300, 300);
465  v3.SetBounds(0, 0, 100, 100);
466  root_view->AddChildView(&v1);
467  root_view->AddChildView(&v2);
468  v2.AddChildView(&v3);
469
470  // The coordinates used below are in the local coordinate space of the
471  // view that is passed in as an argument.
472
473  // Hit tests against |v1|, which has a hit test mask.
474  EXPECT_TRUE(v1.TestDoesIntersectRect(&v1, gfx::Rect(0, 0, 200, 200)));
475  EXPECT_TRUE(v1.TestDoesIntersectRect(&v1, gfx::Rect(-10, -10, 110, 12)));
476  EXPECT_TRUE(v1.TestDoesIntersectRect(&v1, gfx::Rect(112, 142, 1, 1)));
477  EXPECT_FALSE(v1.TestDoesIntersectRect(&v1, gfx::Rect(0, 0, 20, 20)));
478  EXPECT_FALSE(v1.TestDoesIntersectRect(&v1, gfx::Rect(-10, -10, 90, 12)));
479  EXPECT_FALSE(v1.TestDoesIntersectRect(&v1, gfx::Rect(150, 49, 1, 1)));
480
481  // Hit tests against |v2|, which does not have a hit test mask.
482  EXPECT_TRUE(v2.TestDoesIntersectRect(&v2, gfx::Rect(0, 0, 200, 200)));
483  EXPECT_TRUE(v2.TestDoesIntersectRect(&v2, gfx::Rect(-10, 250, 60, 60)));
484  EXPECT_TRUE(v2.TestDoesIntersectRect(&v2, gfx::Rect(250, 250, 1, 1)));
485  EXPECT_FALSE(v2.TestDoesIntersectRect(&v2, gfx::Rect(-10, 250, 7, 7)));
486  EXPECT_FALSE(v2.TestDoesIntersectRect(&v2, gfx::Rect(-1, -1, 1, 1)));
487
488  // Hit tests against |v3|, which has a hit test mask and is a child of |v2|.
489  EXPECT_TRUE(v3.TestDoesIntersectRect(&v3, gfx::Rect(0, 0, 50, 50)));
490  EXPECT_TRUE(v3.TestDoesIntersectRect(&v3, gfx::Rect(90, 90, 1, 1)));
491  EXPECT_FALSE(v3.TestDoesIntersectRect(&v3, gfx::Rect(10, 125, 50, 50)));
492  EXPECT_FALSE(v3.TestDoesIntersectRect(&v3, gfx::Rect(110, 110, 1, 1)));
493
494  // Verify that hit-testing is performed correctly when using the
495  // call-through function ViewTargeter::DoesIntersectRect().
496  EXPECT_TRUE(view_targeter->DoesIntersectRect(root_view,
497                                               gfx::Rect(0, 0, 50, 50)));
498  EXPECT_FALSE(view_targeter->DoesIntersectRect(root_view,
499                                                gfx::Rect(-20, -20, 10, 10)));
500}
501
502// Tests that calls made directly on the hit-testing methods in View
503// (HitTestPoint(), HitTestRect(), etc.) return the correct values.
504TEST_F(ViewTargeterTest, HitTestCallsOnView) {
505  // The coordinates in this test are in the coordinate space of the root view.
506  Widget* widget = new Widget;
507  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
508  widget->Init(params);
509  View* root_view = widget->GetRootView();
510  root_view->SetBoundsRect(gfx::Rect(0, 0, 500, 500));
511
512  // |v1| has no hit test mask. No ViewTargeter is installed on |v1|, which
513  // means that View::HitTestRect() will call into the targeter installed on
514  // the root view instead when we hit test against |v1|.
515  gfx::Rect v1_bounds = gfx::Rect(0, 0, 100, 100);
516  TestingView* v1 = new TestingView();
517  v1->SetBoundsRect(v1_bounds);
518  root_view->AddChildView(v1);
519
520  // |v2| has a triangular hit test mask. Install a ViewTargeter on |v2| which
521  // will be called into by View::HitTestRect().
522  gfx::Rect v2_bounds = gfx::Rect(105, 0, 100, 100);
523  TestMaskedView* v2 = new TestMaskedView();
524  v2->SetBoundsRect(v2_bounds);
525  root_view->AddChildView(v2);
526  ViewTargeter* view_targeter = new ViewTargeter(v2);
527  v2->SetEventTargeter(make_scoped_ptr(view_targeter));
528
529  gfx::Point v1_centerpoint = v1_bounds.CenterPoint();
530  gfx::Point v2_centerpoint = v2_bounds.CenterPoint();
531  gfx::Point v1_origin = v1_bounds.origin();
532  gfx::Point v2_origin = v2_bounds.origin();
533  gfx::Rect r1(10, 10, 110, 15);
534  gfx::Rect r2(106, 1, 98, 98);
535  gfx::Rect r3(0, 0, 300, 300);
536  gfx::Rect r4(115, 342, 200, 10);
537
538  // Test calls into View::HitTestPoint().
539  EXPECT_TRUE(
540      v1->HitTestPoint(ConvertPointFromWidgetToView(v1, v1_centerpoint)));
541  EXPECT_TRUE(
542      v2->HitTestPoint(ConvertPointFromWidgetToView(v2, v2_centerpoint)));
543
544  EXPECT_TRUE(v1->HitTestPoint(ConvertPointFromWidgetToView(v1, v1_origin)));
545  EXPECT_FALSE(v2->HitTestPoint(ConvertPointFromWidgetToView(v2, v2_origin)));
546
547  // Test calls into View::HitTestRect().
548  EXPECT_TRUE(v1->HitTestRect(ConvertRectFromWidgetToView(v1, r1)));
549  EXPECT_FALSE(v2->HitTestRect(ConvertRectFromWidgetToView(v2, r1)));
550
551  EXPECT_FALSE(v1->HitTestRect(ConvertRectFromWidgetToView(v1, r2)));
552  EXPECT_TRUE(v2->HitTestRect(ConvertRectFromWidgetToView(v2, r2)));
553
554  EXPECT_TRUE(v1->HitTestRect(ConvertRectFromWidgetToView(v1, r3)));
555  EXPECT_TRUE(v2->HitTestRect(ConvertRectFromWidgetToView(v2, r3)));
556
557  EXPECT_FALSE(v1->HitTestRect(ConvertRectFromWidgetToView(v1, r4)));
558  EXPECT_FALSE(v2->HitTestRect(ConvertRectFromWidgetToView(v2, r4)));
559
560  // Test calls into View::GetEventHandlerForPoint().
561  EXPECT_EQ(v1, root_view->GetEventHandlerForPoint(v1_centerpoint));
562  EXPECT_EQ(v2, root_view->GetEventHandlerForPoint(v2_centerpoint));
563
564  EXPECT_EQ(v1, root_view->GetEventHandlerForPoint(v1_origin));
565  EXPECT_EQ(root_view, root_view->GetEventHandlerForPoint(v2_origin));
566
567  // Test calls into View::GetTooltipHandlerForPoint().
568  EXPECT_EQ(v1, root_view->GetTooltipHandlerForPoint(v1_centerpoint));
569  EXPECT_EQ(v2, root_view->GetTooltipHandlerForPoint(v2_centerpoint));
570
571  EXPECT_EQ(v1, root_view->GetTooltipHandlerForPoint(v1_origin));
572  EXPECT_EQ(root_view, root_view->GetTooltipHandlerForPoint(v2_origin));
573
574  EXPECT_FALSE(v1->GetTooltipHandlerForPoint(v2_origin));
575
576  widget->CloseNow();
577}
578
579}  // namespace test
580}  // namespace views
581