OLD | NEW |
| (Empty) |
1 // Copyright 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. | |
4 | |
5 #include "ash/wm/solo_window_tracker.h" | |
6 | |
7 #include "ash/ash_constants.h" | |
8 #include "ash/ash_switches.h" | |
9 #include "ash/root_window_controller.h" | |
10 #include "ash/screen_ash.h" | |
11 #include "ash/shell.h" | |
12 #include "ash/shell_window_ids.h" | |
13 #include "ash/test/ash_test_base.h" | |
14 #include "ash/wm/window_resizer.h" | |
15 #include "ash/wm/window_state.h" | |
16 #include "base/memory/scoped_ptr.h" | |
17 #include "ui/aura/client/aura_constants.h" | |
18 #include "ui/aura/root_window.h" | |
19 #include "ui/aura/test/event_generator.h" | |
20 #include "ui/aura/window.h" | |
21 #include "ui/aura/window_observer.h" | |
22 #include "ui/base/hit_test.h" | |
23 #include "ui/gfx/screen.h" | |
24 | |
25 namespace ash { | |
26 | |
27 namespace { | |
28 | |
29 class WindowRepaintChecker : public aura::WindowObserver { | |
30 public: | |
31 explicit WindowRepaintChecker(aura::Window* window) | |
32 : window_(window), | |
33 is_paint_scheduled_(false) { | |
34 window_->AddObserver(this); | |
35 } | |
36 | |
37 virtual ~WindowRepaintChecker() { | |
38 if (window_) | |
39 window_->RemoveObserver(this); | |
40 } | |
41 | |
42 bool IsPaintScheduledAndReset() { | |
43 bool result = is_paint_scheduled_; | |
44 is_paint_scheduled_ = false; | |
45 return result; | |
46 } | |
47 | |
48 private: | |
49 // aura::WindowObserver overrides: | |
50 virtual void OnWindowPaintScheduled(aura::Window* window, | |
51 const gfx::Rect& region) OVERRIDE { | |
52 is_paint_scheduled_ = true; | |
53 } | |
54 virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE { | |
55 DCHECK_EQ(window_, window); | |
56 window_ = NULL; | |
57 } | |
58 | |
59 aura::Window* window_; | |
60 bool is_paint_scheduled_; | |
61 | |
62 DISALLOW_COPY_AND_ASSIGN(WindowRepaintChecker); | |
63 }; | |
64 | |
65 } // namespace | |
66 | |
67 class SoloWindowTrackerTest : public test::AshTestBase { | |
68 public: | |
69 SoloWindowTrackerTest() { | |
70 } | |
71 virtual ~SoloWindowTrackerTest() { | |
72 } | |
73 | |
74 // Helpers methods to create test windows in the primary root window. | |
75 aura::Window* CreateWindowInPrimary() { | |
76 aura::Window* window = new aura::Window(NULL); | |
77 window->SetType(ui::wm::WINDOW_TYPE_NORMAL); | |
78 window->Init(ui::LAYER_TEXTURED); | |
79 window->SetBounds(gfx::Rect(100, 100)); | |
80 ParentWindowInPrimaryRootWindow(window); | |
81 return window; | |
82 } | |
83 aura::Window* CreateAlwaysOnTopWindowInPrimary() { | |
84 aura::Window* window = new aura::Window(NULL); | |
85 window->SetType(ui::wm::WINDOW_TYPE_NORMAL); | |
86 window->Init(ui::LAYER_TEXTURED); | |
87 window->SetBounds(gfx::Rect(100, 100)); | |
88 window->SetProperty(aura::client::kAlwaysOnTopKey, true); | |
89 ParentWindowInPrimaryRootWindow(window); | |
90 return window; | |
91 } | |
92 aura::Window* CreatePanelWindowInPrimary() { | |
93 aura::Window* window = new aura::Window(NULL); | |
94 window->SetType(ui::wm::WINDOW_TYPE_PANEL); | |
95 window->Init(ui::LAYER_TEXTURED); | |
96 window->SetBounds(gfx::Rect(100, 100)); | |
97 ParentWindowInPrimaryRootWindow(window); | |
98 return window; | |
99 } | |
100 | |
101 // Drag |window| to the dock. | |
102 void DockWindow(aura::Window* window) { | |
103 // Because the tests use windows without delegates, | |
104 // aura::test::EventGenerator cannot be used. | |
105 gfx::Point drag_to = | |
106 ash::ScreenAsh::GetDisplayBoundsInParent(window).top_right(); | |
107 scoped_ptr<WindowResizer> resizer(CreateWindowResizer( | |
108 window, | |
109 window->bounds().origin(), | |
110 HTCAPTION, | |
111 aura::client::WINDOW_MOVE_SOURCE_MOUSE)); | |
112 resizer->Drag(drag_to, 0); | |
113 resizer->CompleteDrag(); | |
114 EXPECT_EQ(internal::kShellWindowId_DockedContainer, | |
115 window->parent()->id()); | |
116 } | |
117 | |
118 // Drag |window| out of the dock. | |
119 void UndockWindow(aura::Window* window) { | |
120 gfx::Point drag_to = | |
121 ash::ScreenAsh::GetDisplayWorkAreaBoundsInParent(window).top_right() - | |
122 gfx::Vector2d(10, 0); | |
123 scoped_ptr<WindowResizer> resizer(CreateWindowResizer( | |
124 window, | |
125 window->bounds().origin(), | |
126 HTCAPTION, | |
127 aura::client::WINDOW_MOVE_SOURCE_MOUSE)); | |
128 resizer->Drag(drag_to, 0); | |
129 resizer->CompleteDrag(); | |
130 EXPECT_NE(internal::kShellWindowId_DockedContainer, | |
131 window->parent()->id()); | |
132 } | |
133 | |
134 // Returns the primary display. | |
135 gfx::Display GetPrimaryDisplay() { | |
136 return ash::Shell::GetInstance()->GetScreen()->GetPrimaryDisplay(); | |
137 } | |
138 | |
139 // Returns the secondary display. | |
140 gfx::Display GetSecondaryDisplay() { | |
141 return ScreenAsh::GetSecondaryDisplay(); | |
142 } | |
143 | |
144 // Returns the window which uses the solo header, if any, on the primary | |
145 // display. | |
146 aura::Window* GetWindowWithSoloHeaderInPrimary() { | |
147 return GetWindowWithSoloHeader(Shell::GetPrimaryRootWindow()); | |
148 } | |
149 | |
150 // Returns the window which uses the solo header, if any, in |root|. | |
151 aura::Window* GetWindowWithSoloHeader(aura::Window* root) { | |
152 SoloWindowTracker* solo_window_tracker = | |
153 internal::GetRootWindowController(root)->solo_window_tracker(); | |
154 return solo_window_tracker ? | |
155 solo_window_tracker->GetWindowWithSoloHeader() : NULL; | |
156 } | |
157 | |
158 private: | |
159 DISALLOW_COPY_AND_ASSIGN(SoloWindowTrackerTest); | |
160 }; | |
161 | |
162 TEST_F(SoloWindowTrackerTest, Basic) { | |
163 scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); | |
164 w1->Show(); | |
165 | |
166 // We only have one window, so it should use a solo header. | |
167 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); | |
168 | |
169 // Create a second window. | |
170 scoped_ptr<aura::Window> w2(CreateWindowInPrimary()); | |
171 w2->Show(); | |
172 | |
173 // Now there are two windows, so we should not use solo headers. | |
174 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); | |
175 | |
176 // Hide one window. Solo should be enabled. | |
177 w2->Hide(); | |
178 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); | |
179 | |
180 // Show that window. Solo should be disabled. | |
181 w2->Show(); | |
182 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); | |
183 | |
184 // Minimize the first window. Solo should be enabled. | |
185 wm::GetWindowState(w1.get())->Minimize(); | |
186 EXPECT_EQ(w2.get(), GetWindowWithSoloHeaderInPrimary()); | |
187 | |
188 // Close the minimized window. | |
189 w1.reset(); | |
190 EXPECT_EQ(w2.get(), GetWindowWithSoloHeaderInPrimary()); | |
191 | |
192 // Open an always-on-top window (which lives in a different container). | |
193 scoped_ptr<aura::Window> w3(CreateAlwaysOnTopWindowInPrimary()); | |
194 w3->Show(); | |
195 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); | |
196 | |
197 // Close the always-on-top window. | |
198 w3.reset(); | |
199 EXPECT_EQ(w2.get(), GetWindowWithSoloHeaderInPrimary()); | |
200 } | |
201 | |
202 // Test that docked windows never use the solo header and that the presence of a | |
203 // docked window prevents all other windows from the using the solo window | |
204 // header. | |
205 TEST_F(SoloWindowTrackerTest, DockedWindow) { | |
206 if (!switches::UseDockedWindows() || !SupportsHostWindowResize()) | |
207 return; | |
208 | |
209 scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); | |
210 w1->Show(); | |
211 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); | |
212 | |
213 DockWindow(w1.get()); | |
214 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); | |
215 | |
216 UndockWindow(w1.get()); | |
217 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); | |
218 | |
219 scoped_ptr<aura::Window> w2(CreateWindowInPrimary()); | |
220 w2->Show(); | |
221 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); | |
222 | |
223 DockWindow(w2.get()); | |
224 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); | |
225 | |
226 wm::GetWindowState(w2.get())->Minimize(); | |
227 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); | |
228 } | |
229 | |
230 // Panels should not "count" for computing solo window headers, and the panel | |
231 // itself should never use the solo header. | |
232 TEST_F(SoloWindowTrackerTest, Panel) { | |
233 scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); | |
234 w1->Show(); | |
235 | |
236 // We only have one window, so it should use a solo header. | |
237 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); | |
238 | |
239 // Create a panel window. | |
240 scoped_ptr<aura::Window> w2(CreatePanelWindowInPrimary()); | |
241 w2->Show(); | |
242 | |
243 // Despite two windows, the first window should still be considered "solo" | |
244 // because panels aren't included in the computation. | |
245 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); | |
246 | |
247 // Even after closing the first window, the panel is still not considered | |
248 // solo. | |
249 w1.reset(); | |
250 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); | |
251 } | |
252 | |
253 // Modal dialogs should not use solo headers. | |
254 TEST_F(SoloWindowTrackerTest, Modal) { | |
255 scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); | |
256 w1->Show(); | |
257 | |
258 // We only have one window, so it should use a solo header. | |
259 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); | |
260 | |
261 // Create a fake modal window. | |
262 scoped_ptr<aura::Window> w2(CreateWindowInPrimary()); | |
263 w2->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); | |
264 w2->Show(); | |
265 | |
266 // Despite two windows, the first window should still be considered "solo" | |
267 // because modal windows aren't included in the computation. | |
268 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); | |
269 } | |
270 | |
271 // Constrained windows should not use solo headers. | |
272 TEST_F(SoloWindowTrackerTest, Constrained) { | |
273 scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); | |
274 w1->Show(); | |
275 | |
276 // We only have one window, so it should use a solo header. | |
277 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); | |
278 | |
279 // Create a fake constrained window. | |
280 scoped_ptr<aura::Window> w2(CreateWindowInPrimary()); | |
281 w2->SetProperty(aura::client::kConstrainedWindowKey, true); | |
282 w2->Show(); | |
283 | |
284 // Despite two windows, the first window should still be considered "solo" | |
285 // because constrained windows aren't included in the computation. | |
286 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); | |
287 } | |
288 | |
289 // Non-drawing windows should not affect the solo computation. | |
290 TEST_F(SoloWindowTrackerTest, NotDrawn) { | |
291 aura::Window* w = CreateWindowInPrimary(); | |
292 w->Show(); | |
293 | |
294 // We only have one window, so it should use a solo header. | |
295 EXPECT_EQ(w, GetWindowWithSoloHeaderInPrimary()); | |
296 | |
297 // Create non-drawing window similar to DragDropTracker. | |
298 aura::Window* not_drawn = new aura::Window(NULL); | |
299 not_drawn->SetType(ui::wm::WINDOW_TYPE_NORMAL); | |
300 not_drawn->Init(ui::LAYER_NOT_DRAWN); | |
301 ParentWindowInPrimaryRootWindow(not_drawn); | |
302 not_drawn->Show(); | |
303 | |
304 // Despite two windows, the first window should still be considered "solo" | |
305 // because non-drawing windows aren't included in the computation. | |
306 EXPECT_EQ(w, GetWindowWithSoloHeaderInPrimary()); | |
307 } | |
308 | |
309 TEST_F(SoloWindowTrackerTest, MultiDisplay) { | |
310 if (!SupportsMultipleDisplays()) | |
311 return; | |
312 | |
313 UpdateDisplay("1000x600,600x400"); | |
314 | |
315 scoped_ptr<aura::Window> w1(CreateWindowInPrimary()); | |
316 w1->SetBoundsInScreen(gfx::Rect(0, 0, 100, 100), GetPrimaryDisplay()); | |
317 w1->Show(); | |
318 WindowRepaintChecker checker1(w1.get()); | |
319 scoped_ptr<aura::Window> w2(CreateWindowInPrimary()); | |
320 w2->SetBoundsInScreen(gfx::Rect(0, 0, 100, 100), GetPrimaryDisplay()); | |
321 w2->Show(); | |
322 WindowRepaintChecker checker2(w2.get()); | |
323 | |
324 // Now there are two windows in the same display, so we should not use solo | |
325 // headers. | |
326 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); | |
327 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); | |
328 | |
329 // Moves the second window to the secondary display. Both w1/w2 should be | |
330 // solo. | |
331 w2->SetBoundsInScreen(gfx::Rect(1200, 0, 100, 100), | |
332 ScreenAsh::GetSecondaryDisplay()); | |
333 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); | |
334 EXPECT_EQ(w2.get(), GetWindowWithSoloHeader(w2->GetRootWindow())); | |
335 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); | |
336 EXPECT_TRUE(checker2.IsPaintScheduledAndReset()); | |
337 | |
338 // Open two more windows in the primary display. | |
339 scoped_ptr<aura::Window> w3(CreateWindowInPrimary()); | |
340 w3->SetBoundsInScreen(gfx::Rect(0, 0, 100, 100), GetPrimaryDisplay()); | |
341 w3->Show(); | |
342 scoped_ptr<aura::Window> w4(CreateWindowInPrimary()); | |
343 w4->SetBoundsInScreen(gfx::Rect(0, 0, 100, 100), GetPrimaryDisplay()); | |
344 w4->Show(); | |
345 | |
346 // Because the primary display has three windows w1, w3, and w4, they | |
347 // shouldn't be solo. w2 should be solo. | |
348 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); | |
349 EXPECT_EQ(w2.get(), GetWindowWithSoloHeader(w2->GetRootWindow())); | |
350 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); | |
351 | |
352 // Move w4 to the secondary display. Now w2 shouldn't be solo anymore. | |
353 w4->SetBoundsInScreen(gfx::Rect(1200, 0, 100, 100), GetSecondaryDisplay()); | |
354 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); | |
355 EXPECT_EQ(NULL, GetWindowWithSoloHeader(w2->GetRootWindow())); | |
356 EXPECT_TRUE(checker2.IsPaintScheduledAndReset()); | |
357 | |
358 // Moves w3 to the secondary display too. Now w1 should be solo again. | |
359 w3->SetBoundsInScreen(gfx::Rect(1200, 0, 100, 100), GetSecondaryDisplay()); | |
360 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); | |
361 EXPECT_EQ(NULL, GetWindowWithSoloHeader(w2->GetRootWindow())); | |
362 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); | |
363 | |
364 // Change w3's state to maximize. Doesn't affect w1. | |
365 wm::GetWindowState(w3.get())->Maximize(); | |
366 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); | |
367 EXPECT_EQ(NULL, GetWindowWithSoloHeader(w2->GetRootWindow())); | |
368 | |
369 // Close w3 and w4. | |
370 w3.reset(); | |
371 w4.reset(); | |
372 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); | |
373 EXPECT_EQ(w2.get(), GetWindowWithSoloHeader(w2->GetRootWindow())); | |
374 EXPECT_TRUE(checker2.IsPaintScheduledAndReset()); | |
375 | |
376 // Move w2 back to the primary display. | |
377 w2->SetBoundsInScreen(gfx::Rect(0, 0, 100, 100), GetPrimaryDisplay()); | |
378 EXPECT_EQ(w1->GetRootWindow(), w2->GetRootWindow()); | |
379 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); | |
380 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); | |
381 EXPECT_TRUE(checker2.IsPaintScheduledAndReset()); | |
382 | |
383 // Close w2. | |
384 w2.reset(); | |
385 EXPECT_EQ(w1.get(), GetWindowWithSoloHeaderInPrimary()); | |
386 EXPECT_TRUE(checker1.IsPaintScheduledAndReset()); | |
387 } | |
388 | |
389 TEST_F(SoloWindowTrackerTest, ChildWindowVisibility) { | |
390 aura::Window* w = CreateWindowInPrimary(); | |
391 w->Show(); | |
392 | |
393 // We only have one window, so it should use a solo header. | |
394 EXPECT_EQ(w, GetWindowWithSoloHeaderInPrimary()); | |
395 | |
396 // Create a child window. This should not affect the solo-ness of |w1|. | |
397 aura::Window* child = new aura::Window(NULL); | |
398 child->SetType(ui::wm::WINDOW_TYPE_CONTROL); | |
399 child->Init(ui::LAYER_TEXTURED); | |
400 child->SetBounds(gfx::Rect(100, 100)); | |
401 w->AddChild(child); | |
402 child->Show(); | |
403 EXPECT_EQ(w, GetWindowWithSoloHeaderInPrimary()); | |
404 | |
405 // Changing the visibility of |child| should not affect the solo-ness of |w1|. | |
406 child->Hide(); | |
407 EXPECT_EQ(w, GetWindowWithSoloHeaderInPrimary()); | |
408 } | |
409 | |
410 TEST_F(SoloWindowTrackerTest, CreateAndDeleteSingleWindow) { | |
411 // Ensure that creating/deleting a window works well and doesn't cause | |
412 // crashes. See crbug.com/155634 | |
413 scoped_ptr<aura::Window> w(CreateWindowInPrimary()); | |
414 w->Show(); | |
415 | |
416 // We only have one window, so it should use a solo header. | |
417 EXPECT_EQ(w.get(), GetWindowWithSoloHeaderInPrimary()); | |
418 | |
419 // Close the window. | |
420 w.reset(); | |
421 EXPECT_EQ(NULL, GetWindowWithSoloHeaderInPrimary()); | |
422 | |
423 // Recreate another window again. | |
424 w.reset(CreateWindowInPrimary()); | |
425 w->Show(); | |
426 EXPECT_EQ(w.get(), GetWindowWithSoloHeaderInPrimary()); | |
427 } | |
428 | |
429 } // namespace ash | |
OLD | NEW |