Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / views / controls / native / native_view_host_aura_unittest.cc
blob16053ede1697e982f7d78b9974faf2b093273800
1 // Copyright (c) 2013 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 "ui/views/controls/native/native_view_host_aura.h"
7 #include "base/basictypes.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "ui/aura/client/aura_constants.h"
10 #include "ui/aura/window.h"
11 #include "ui/base/cursor/cursor.h"
12 #include "ui/events/event_utils.h"
13 #include "ui/views/controls/native/native_view_host.h"
14 #include "ui/views/controls/native/native_view_host_test_base.h"
15 #include "ui/views/view.h"
16 #include "ui/views/view_constants_aura.h"
17 #include "ui/views/widget/widget.h"
19 namespace views {
21 // Observer watching for window visibility and bounds change events. This is
22 // used to verify that the child and clipping window operations are done in the
23 // right order.
24 class NativeViewHostWindowObserver : public aura::WindowObserver {
25 public:
26 enum EventType {
27 EVENT_NONE,
28 EVENT_SHOWN,
29 EVENT_HIDDEN,
30 EVENT_BOUNDS_CHANGED,
31 EVENT_DESTROYED,
34 struct EventDetails {
35 EventType type;
36 aura::Window* window;
37 gfx::Rect bounds;
38 bool operator!=(const EventDetails& rhs) {
39 return type != rhs.type || window != rhs.window || bounds != rhs.bounds;
43 NativeViewHostWindowObserver() {}
44 ~NativeViewHostWindowObserver() override {}
46 const std::vector<EventDetails>& events() const { return events_; }
48 // aura::WindowObserver overrides
49 void OnWindowVisibilityChanged(aura::Window* window, bool visible) override {
50 EventDetails event;
51 event.type = visible ? EVENT_SHOWN : EVENT_HIDDEN;
52 event.window = window;
53 event.bounds = window->GetBoundsInRootWindow();
55 // Dedupe events as a single Hide() call can result in several
56 // notifications.
57 if (events_.size() == 0u || events_.back() != event)
58 events_.push_back(event);
61 void OnWindowBoundsChanged(aura::Window* window,
62 const gfx::Rect& old_bounds,
63 const gfx::Rect& new_bounds) override {
64 EventDetails event;
65 event.type = EVENT_BOUNDS_CHANGED;
66 event.window = window;
67 event.bounds = window->GetBoundsInRootWindow();
68 events_.push_back(event);
71 void OnWindowDestroyed(aura::Window* window) override {
72 EventDetails event = { EVENT_DESTROYED, window, gfx::Rect() };
73 events_.push_back(event);
76 private:
77 std::vector<EventDetails> events_;
78 gfx::Rect bounds_at_visibility_changed_;
80 DISALLOW_COPY_AND_ASSIGN(NativeViewHostWindowObserver);
83 class NativeViewHostAuraTest : public test::NativeViewHostTestBase {
84 public:
85 NativeViewHostAuraTest() {
88 NativeViewHostAura* native_host() {
89 return static_cast<NativeViewHostAura*>(GetNativeWrapper());
92 Widget* child() {
93 return child_.get();
96 aura::Window* clipping_window() { return &(native_host()->clipping_window_); }
98 void CreateHost() {
99 CreateTopLevel();
100 CreateTestingHost();
101 child_.reset(CreateChildForHost(toplevel()->GetNativeView(),
102 toplevel()->GetRootView(),
103 new View,
104 host()));
107 private:
108 scoped_ptr<Widget> child_;
110 DISALLOW_COPY_AND_ASSIGN(NativeViewHostAuraTest);
113 // Verifies NativeViewHostAura stops observing native view on destruction.
114 TEST_F(NativeViewHostAuraTest, StopObservingNativeViewOnDestruct) {
115 CreateHost();
116 aura::Window* child_win = child()->GetNativeView();
117 NativeViewHostAura* aura_host = native_host();
119 EXPECT_TRUE(child_win->HasObserver(aura_host));
120 DestroyHost();
121 EXPECT_FALSE(child_win->HasObserver(aura_host));
124 // Tests that the kHostViewKey is correctly set and cleared.
125 TEST_F(NativeViewHostAuraTest, HostViewPropertyKey) {
126 // Create the NativeViewHost and attach a NativeView.
127 CreateHost();
128 aura::Window* child_win = child()->GetNativeView();
129 EXPECT_EQ(host(), child_win->GetProperty(views::kHostViewKey));
130 EXPECT_EQ(host()->GetWidget()->GetNativeView(),
131 child_win->GetProperty(aura::client::kHostWindowKey));
132 EXPECT_EQ(host(), clipping_window()->GetProperty(views::kHostViewKey));
134 host()->Detach();
135 EXPECT_FALSE(child_win->GetProperty(views::kHostViewKey));
136 EXPECT_FALSE(child_win->GetProperty(aura::client::kHostWindowKey));
137 EXPECT_TRUE(clipping_window()->GetProperty(views::kHostViewKey));
139 host()->Attach(child_win);
140 EXPECT_EQ(host(), child_win->GetProperty(views::kHostViewKey));
141 EXPECT_EQ(host()->GetWidget()->GetNativeView(),
142 child_win->GetProperty(aura::client::kHostWindowKey));
143 EXPECT_EQ(host(), clipping_window()->GetProperty(views::kHostViewKey));
145 DestroyHost();
146 EXPECT_FALSE(child_win->GetProperty(views::kHostViewKey));
147 EXPECT_FALSE(child_win->GetProperty(aura::client::kHostWindowKey));
150 // Tests that the NativeViewHost reports the cursor set on its native view.
151 TEST_F(NativeViewHostAuraTest, CursorForNativeView) {
152 CreateHost();
154 toplevel()->SetCursor(ui::kCursorHand);
155 child()->SetCursor(ui::kCursorWait);
156 ui::MouseEvent move_event(ui::ET_MOUSE_MOVED, gfx::Point(0, 0),
157 gfx::Point(0, 0), ui::EventTimeForNow(), 0, 0);
159 EXPECT_EQ(ui::kCursorWait, host()->GetCursor(move_event).native_type());
161 DestroyHost();
164 // Test that destroying the top level widget before destroying the attached
165 // NativeViewHost works correctly. Specifically the associated NVH should be
166 // destroyed and there shouldn't be any errors.
167 TEST_F(NativeViewHostAuraTest, DestroyWidget) {
168 ResetHostDestroyedCount();
169 CreateHost();
170 ReleaseHost();
171 EXPECT_EQ(0, host_destroyed_count());
172 DestroyTopLevel();
173 EXPECT_EQ(1, host_destroyed_count());
176 // Test that the fast resize path places the clipping and content windows were
177 // they are supposed to be.
178 TEST_F(NativeViewHostAuraTest, FastResizePath) {
179 CreateHost();
180 toplevel()->SetBounds(gfx::Rect(20, 20, 100, 100));
182 // Without fast resize, the clipping window should size to the native view
183 // with the native view positioned at the origin of the clipping window and
184 // the clipping window positioned where the native view was requested.
185 host()->set_fast_resize(false);
186 native_host()->ShowWidget(5, 10, 100, 100);
187 EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(),
188 host()->native_view()->bounds().ToString());
189 EXPECT_EQ(gfx::Rect(5, 10, 100, 100).ToString(),
190 clipping_window()->bounds().ToString());
192 // With fast resize, the native view should remain the same size but be
193 // clipped the requested size.
194 host()->set_fast_resize(true);
195 native_host()->ShowWidget(10, 25, 50, 50);
196 EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(),
197 host()->native_view()->bounds().ToString());
198 EXPECT_EQ(gfx::Rect(10, 25, 50, 50).ToString(),
199 clipping_window()->bounds().ToString());
201 // Turning off fast resize should make the native view start resizing again.
202 host()->set_fast_resize(false);
203 native_host()->ShowWidget(10, 25, 50, 50);
204 EXPECT_EQ(gfx::Rect(0, 0, 50, 50).ToString(),
205 host()->native_view()->bounds().ToString());
206 EXPECT_EQ(gfx::Rect(10, 25, 50, 50).ToString(),
207 clipping_window()->bounds().ToString());
209 DestroyHost();
212 // Test installing and uninstalling a clip.
213 TEST_F(NativeViewHostAuraTest, InstallClip) {
214 CreateHost();
215 toplevel()->SetBounds(gfx::Rect(20, 20, 100, 100));
217 // Without a clip, the clipping window should always be positioned at the
218 // requested coordinates with the native view positioned at the origin of the
219 // clipping window.
220 native_host()->ShowWidget(10, 20, 100, 100);
221 EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(),
222 host()->native_view()->bounds().ToString());
223 EXPECT_EQ(gfx::Rect(10, 20, 100, 100).ToString(),
224 clipping_window()->bounds().ToString());
226 // Clip to the bottom right quarter of the native view.
227 native_host()->InstallClip(60, 70, 50, 50);
228 native_host()->ShowWidget(10, 20, 100, 100);
229 EXPECT_EQ(gfx::Rect(-50, -50, 100, 100).ToString(),
230 host()->native_view()->bounds().ToString());
231 EXPECT_EQ(gfx::Rect(60, 70, 50, 50).ToString(),
232 clipping_window()->bounds().ToString());
234 // Clip to the center of the native view.
235 native_host()->InstallClip(35, 45, 50, 50);
236 native_host()->ShowWidget(10, 20, 100, 100);
237 EXPECT_EQ(gfx::Rect(-25, -25, 100, 100).ToString(),
238 host()->native_view()->bounds().ToString());
239 EXPECT_EQ(gfx::Rect(35, 45, 50, 50).ToString(),
240 clipping_window()->bounds().ToString());
242 // Uninstalling the clip should make the clipping window match the native view
243 // again.
244 native_host()->UninstallClip();
245 native_host()->ShowWidget(10, 20, 100, 100);
246 EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(),
247 host()->native_view()->bounds().ToString());
248 EXPECT_EQ(gfx::Rect(10, 20, 100, 100).ToString(),
249 clipping_window()->bounds().ToString());
251 DestroyHost();
254 // Ensure native view is parented to the root window after detaching. This is
255 // a regression test for http://crbug.com/389261.
256 TEST_F(NativeViewHostAuraTest, ParentAfterDetach) {
257 CreateHost();
258 aura::Window* child_win = child()->GetNativeView();
259 aura::Window* root_window = child_win->GetRootWindow();
260 aura::WindowTreeHost* child_win_tree_host = child_win->GetHost();
262 NativeViewHostWindowObserver test_observer;
263 child_win->AddObserver(&test_observer);
265 host()->Detach();
266 EXPECT_EQ(root_window, child_win->GetRootWindow());
267 EXPECT_EQ(child_win_tree_host, child_win->GetHost());
269 DestroyHost();
271 // The window is detached, so no longer associated with any Widget hierarchy.
272 // The root window still owns it, but the test harness checks for orphaned
273 // windows during TearDown().
274 DestroyTopLevel();
275 EXPECT_EQ(0u, test_observer.events().size());
276 delete child_win;
277 ASSERT_EQ(1u, test_observer.events().size());
278 EXPECT_EQ(NativeViewHostWindowObserver::EVENT_DESTROYED,
279 test_observer.events().back().type);
282 // Ensure the clipping window is hidden before setting the native view's bounds.
283 // This is a regression test for http://crbug.com/388699.
284 TEST_F(NativeViewHostAuraTest, RemoveClippingWindowOrder) {
285 CreateHost();
286 toplevel()->SetBounds(gfx::Rect(20, 20, 100, 100));
287 native_host()->ShowWidget(10, 20, 100, 100);
289 NativeViewHostWindowObserver test_observer;
290 clipping_window()->AddObserver(&test_observer);
292 aura::Window* child_win = child()->GetNativeView();
293 child_win->AddObserver(&test_observer);
295 host()->Detach();
297 ASSERT_EQ(3u, test_observer.events().size());
298 EXPECT_EQ(NativeViewHostWindowObserver::EVENT_HIDDEN,
299 test_observer.events()[0].type);
300 EXPECT_EQ(clipping_window(), test_observer.events()[0].window);
301 EXPECT_EQ(NativeViewHostWindowObserver::EVENT_BOUNDS_CHANGED,
302 test_observer.events()[1].type);
303 EXPECT_EQ(child()->GetNativeView(), test_observer.events()[1].window);
304 EXPECT_EQ(NativeViewHostWindowObserver::EVENT_HIDDEN,
305 test_observer.events()[2].type);
306 EXPECT_EQ(child()->GetNativeView(), test_observer.events()[2].window);
308 clipping_window()->RemoveObserver(&test_observer);
309 child()->GetNativeView()->RemoveObserver(&test_observer);
311 DestroyHost();
312 delete child_win; // See comments in ParentAfterDetach.
315 // Ensure the native view receives the correct bounds notification when it is
316 // attached. This is a regression test for https://crbug.com/399420.
317 TEST_F(NativeViewHostAuraTest, Attach) {
318 CreateHost();
319 host()->Detach();
321 child()->GetNativeView()->SetBounds(gfx::Rect(0, 0, 0, 0));
322 toplevel()->SetBounds(gfx::Rect(0, 0, 100, 100));
323 host()->SetBounds(10, 10, 80, 80);
325 NativeViewHostWindowObserver test_observer;
326 child()->GetNativeView()->AddObserver(&test_observer);
328 host()->Attach(child()->GetNativeView());
330 ASSERT_EQ(3u, test_observer.events().size());
331 EXPECT_EQ(NativeViewHostWindowObserver::EVENT_BOUNDS_CHANGED,
332 test_observer.events()[0].type);
333 EXPECT_EQ(child()->GetNativeView(), test_observer.events()[0].window);
334 EXPECT_EQ(gfx::Rect(10, 10, 80, 80).ToString(),
335 test_observer.events()[0].bounds.ToString());
336 EXPECT_EQ(NativeViewHostWindowObserver::EVENT_SHOWN,
337 test_observer.events()[1].type);
338 EXPECT_EQ(child()->GetNativeView(), test_observer.events()[1].window);
339 EXPECT_EQ(gfx::Rect(10, 10, 80, 80).ToString(),
340 test_observer.events()[1].bounds.ToString());
341 EXPECT_EQ(NativeViewHostWindowObserver::EVENT_SHOWN,
342 test_observer.events()[2].type);
343 EXPECT_EQ(clipping_window(), test_observer.events()[2].window);
344 EXPECT_EQ(gfx::Rect(10, 10, 80, 80).ToString(),
345 test_observer.events()[2].bounds.ToString());
347 child()->GetNativeView()->RemoveObserver(&test_observer);
348 DestroyHost();
351 // Ensure the clipping window is hidden with the native view. This is a
352 // regression test for https://crbug.com/408877.
353 TEST_F(NativeViewHostAuraTest, SimpleShowAndHide) {
354 CreateHost();
356 toplevel()->SetBounds(gfx::Rect(20, 20, 100, 100));
357 toplevel()->Show();
359 host()->SetBounds(10, 10, 80, 80);
360 EXPECT_TRUE(clipping_window()->IsVisible());
361 EXPECT_TRUE(child()->IsVisible());
363 host()->SetVisible(false);
364 EXPECT_FALSE(clipping_window()->IsVisible());
365 EXPECT_FALSE(child()->IsVisible());
367 DestroyHost();
368 DestroyTopLevel();
371 } // namespace views