shaped_app_window_targeter_unittest.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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 "chrome/browser/ui/views/apps/shaped_app_window_targeter.h"
6
7#include "chrome/browser/ui/views/apps/chrome_native_app_window_views.h"
8#include "ui/aura/test/aura_test_base.h"
9#include "ui/aura/window.h"
10#include "ui/aura/window_event_dispatcher.h"
11#include "ui/views/controls/webview/webview.h"
12#include "ui/wm/core/easy_resize_window_targeter.h"
13
14class ShapedAppWindowTargeterTest : public aura::test::AuraTestBase {
15 public:
16  ShapedAppWindowTargeterTest()
17      : web_view_(NULL) {
18  }
19
20  virtual ~ShapedAppWindowTargeterTest() {}
21
22  views::Widget* widget() { return widget_.get(); }
23
24  apps::NativeAppWindow* app_window() { return &app_window_; }
25  ChromeNativeAppWindowViews* app_window_views() { return &app_window_; }
26
27 protected:
28  virtual void SetUp() OVERRIDE {
29    aura::test::AuraTestBase::SetUp();
30    widget_.reset(new views::Widget);
31    views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
32    params.remove_standard_frame = true;
33    params.bounds = gfx::Rect(30, 30, 100, 100);
34    params.context = root_window();
35    params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
36    widget_->Init(params);
37
38    app_window_.set_web_view_for_testing(&web_view_);
39    app_window_.set_window_for_testing(widget_.get());
40
41    widget_->Show();
42  }
43
44  virtual void TearDown() OVERRIDE {
45    widget_.reset();
46    aura::test::AuraTestBase::TearDown();
47  }
48
49 private:
50  views::WebView web_view_;
51  scoped_ptr<views::Widget> widget_;
52  ChromeNativeAppWindowViews app_window_;
53
54  DISALLOW_COPY_AND_ASSIGN(ShapedAppWindowTargeterTest);
55};
56
57TEST_F(ShapedAppWindowTargeterTest, HitTestBasic) {
58  aura::Window* window = widget()->GetNativeWindow();
59  {
60    // Without any custom shapes, the event should be targeted correctly to the
61    // window.
62    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
63                        gfx::Point(40, 40), gfx::Point(40, 40),
64                        ui::EF_NONE, ui::EF_NONE);
65    ui::EventDispatchDetails details =
66        event_processor()->OnEventFromSource(&move);
67    ASSERT_FALSE(details.dispatcher_destroyed);
68    EXPECT_EQ(window, move.target());
69  }
70
71  scoped_ptr<SkRegion> region(new SkRegion);
72  region->op(SkIRect::MakeXYWH(40, 0, 20, 100), SkRegion::kUnion_Op);
73  region->op(SkIRect::MakeXYWH(0, 40, 100, 20), SkRegion::kUnion_Op);
74  app_window()->UpdateShape(region.Pass());
75  {
76    // With the custom shape, the events that don't fall within the custom shape
77    // will go through to the root window.
78    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
79                        gfx::Point(40, 40), gfx::Point(40, 40),
80                        ui::EF_NONE, ui::EF_NONE);
81    ui::EventDispatchDetails details =
82        event_processor()->OnEventFromSource(&move);
83    ASSERT_FALSE(details.dispatcher_destroyed);
84    EXPECT_EQ(root_window(), move.target());
85
86    // But events within the shape will still reach the window.
87    ui::MouseEvent move2(ui::ET_MOUSE_MOVED,
88                         gfx::Point(80, 80), gfx::Point(80, 80),
89                         ui::EF_NONE, ui::EF_NONE);
90    details = event_processor()->OnEventFromSource(&move2);
91    ASSERT_FALSE(details.dispatcher_destroyed);
92    EXPECT_EQ(window, move2.target());
93  }
94}
95
96TEST_F(ShapedAppWindowTargeterTest, HitTestOnlyForShapedWindow) {
97  // Install a window-targeter on the root window that allows a window to
98  // receive events outside of its bounds. Verify that this window-targeter is
99  // active unless the window has a custom shape.
100  gfx::Insets inset(-30, -30, -30, -30);
101  root_window()->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
102      new wm::EasyResizeWindowTargeter(root_window(), inset, inset)));
103
104  aura::Window* window = widget()->GetNativeWindow();
105  {
106    // Without any custom shapes, an event within the window bounds should be
107    // targeted correctly to the window.
108    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
109                        gfx::Point(40, 40), gfx::Point(40, 40),
110                        ui::EF_NONE, ui::EF_NONE);
111    ui::EventDispatchDetails details =
112        event_processor()->OnEventFromSource(&move);
113    ASSERT_FALSE(details.dispatcher_destroyed);
114    EXPECT_EQ(window, move.target());
115  }
116  {
117    // Without any custom shapes, an event that falls just outside the window
118    // bounds should also be targeted correctly to the window, because of the
119    // targeter installed on the root-window.
120    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
121                        gfx::Point(10, 10), gfx::Point(10, 10),
122                        ui::EF_NONE, ui::EF_NONE);
123    ui::EventDispatchDetails details =
124        event_processor()->OnEventFromSource(&move);
125    ASSERT_FALSE(details.dispatcher_destroyed);
126    EXPECT_EQ(window, move.target());
127  }
128
129  scoped_ptr<SkRegion> region(new SkRegion);
130  region->op(SkIRect::MakeXYWH(40, 0, 20, 100), SkRegion::kUnion_Op);
131  region->op(SkIRect::MakeXYWH(0, 40, 100, 20), SkRegion::kUnion_Op);
132  app_window()->UpdateShape(region.Pass());
133  {
134    // With the custom shape, the events that don't fall within the custom shape
135    // will go through to the root window.
136    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
137                        gfx::Point(10, 10), gfx::Point(10, 10),
138                        ui::EF_NONE, ui::EF_NONE);
139    ui::EventDispatchDetails details =
140        event_processor()->OnEventFromSource(&move);
141    ASSERT_FALSE(details.dispatcher_destroyed);
142    EXPECT_EQ(root_window(), move.target());
143  }
144
145  // Remove the custom shape. This should restore the behaviour of targeting the
146  // app window for events just outside its bounds.
147  app_window()->UpdateShape(scoped_ptr<SkRegion>());
148  {
149    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
150                        gfx::Point(10, 10), gfx::Point(10, 10),
151                        ui::EF_NONE, ui::EF_NONE);
152    ui::EventDispatchDetails details =
153        event_processor()->OnEventFromSource(&move);
154    ASSERT_FALSE(details.dispatcher_destroyed);
155    EXPECT_EQ(window, move.target());
156  }
157}
158
159// Tests targeting of events on a window with an EasyResizeWindowTargeter
160// installed on its container.
161TEST_F(ShapedAppWindowTargeterTest, ResizeInsetsWithinBounds) {
162  aura::Window* window = widget()->GetNativeWindow();
163  {
164    // An event in the center of the window should always have
165    // |window| as its target.
166    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
167                        gfx::Point(80, 80), gfx::Point(80, 80),
168                        ui::EF_NONE, ui::EF_NONE);
169    ui::EventDispatchDetails details =
170        event_processor()->OnEventFromSource(&move);
171    ASSERT_FALSE(details.dispatcher_destroyed);
172    EXPECT_EQ(window, move.target());
173  }
174  {
175    // Without an EasyResizeTargeter on the container, an event
176    // inside the window and within 5px of an edge should have
177    // |window| as its target.
178    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
179                        gfx::Point(32, 37), gfx::Point(32, 37),
180                        ui::EF_NONE, ui::EF_NONE);
181    ui::EventDispatchDetails details =
182        event_processor()->OnEventFromSource(&move);
183    ASSERT_FALSE(details.dispatcher_destroyed);
184    EXPECT_EQ(window, move.target());
185  }
186
187  // The EasyResizeTargeter specifies an inset of 5px within the window.
188  app_window_views()->InstallEasyResizeTargeterOnContainer();
189
190  {
191    // An event in the center of the window should always have
192    // |window| as its target.
193    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
194                        gfx::Point(80, 80), gfx::Point(80, 80),
195                        ui::EF_NONE, ui::EF_NONE);
196    ui::EventDispatchDetails details =
197        event_processor()->OnEventFromSource(&move);
198    ASSERT_FALSE(details.dispatcher_destroyed);
199    EXPECT_EQ(window, move.target());
200  }
201  {
202    // With an EasyResizeTargeter on the container, an event
203    // inside the window and within 5px of an edge should have
204    // root_window() as its target.
205    ui::MouseEvent move(ui::ET_MOUSE_MOVED,
206                        gfx::Point(32, 37), gfx::Point(32, 37),
207                        ui::EF_NONE, ui::EF_NONE);
208    ui::EventDispatchDetails details =
209        event_processor()->OnEventFromSource(&move);
210    ASSERT_FALSE(details.dispatcher_destroyed);
211    EXPECT_EQ(root_window(), move.target());
212  }
213}
214