1 // Copyright 2016 The Chromium Authors
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/aura/window_tree_host.h"
7 #include "base/containers/contains.h"
8 #include "base/test/bind.h"
9 #include "base/test/scoped_feature_list.h"
10 #include "build/build_config.h"
11 #include "build/chromeos_buildflags.h"
12 #include "ui/aura/native_window_occlusion_tracker.h"
13 #include "ui/aura/test/aura_test_base.h"
14 #include "ui/aura/test/aura_test_utils.h"
15 #include "ui/aura/test/test_screen.h"
16 #include "ui/aura/test/window_event_dispatcher_test_api.h"
17 #include "ui/aura/window.h"
18 #include "ui/aura/window_tree_host_platform.h"
19 #include "ui/base/ime/input_method.h"
20 #include "ui/base/ui_base_features.h"
21 #include "ui/compositor/compositor.h"
22 #include "ui/compositor/layer.h"
23 #include "ui/compositor/test/draw_waiter_for_test.h"
24 #include "ui/events/event_rewriter.h"
25 #include "ui/events/keycodes/dom/dom_code.h"
26 #include "ui/events/test/test_event_rewriter.h"
27 #include "ui/platform_window/stub/stub_window.h"
30 #include "ui/aura/native_window_occlusion_tracker_win.h"
37 // A convenient wrapper that makes it easy to invoke this method inside an
38 // EXPECT_EQ statement.
39 gfx::Point ConvertDIPToPixels(const WindowTreeHost* host, gfx::Point point) {
40 host->ConvertDIPToPixels(&point);
44 // A convenient wrapper that makes it easy to invoke this method inside an
45 // EXPECT_EQ statement.
46 gfx::PointF ConvertDIPToPixels(const WindowTreeHost* host, gfx::PointF point) {
47 host->ConvertDIPToPixels(&point);
51 // A convenient wrapper that makes it easy to invoke this method inside an
52 // EXPECT_EQ statement.
53 gfx::Point ConvertPixelsToDIP(const WindowTreeHost* host, gfx::Point point) {
54 host->ConvertPixelsToDIP(&point);
58 // A convenient wrapper that makes it easy to invoke this method inside an
59 // EXPECT_EQ statement.
60 gfx::PointF ConvertPixelsToDIP(const WindowTreeHost* host, gfx::PointF point) {
61 host->ConvertPixelsToDIP(&point);
67 using WindowTreeHostTest = test::AuraTestBase;
69 TEST_F(WindowTreeHostTest, DPIWindowSize) {
70 constexpr gfx::Rect starting_bounds(
71 aura::test::AuraTestHelper::kDefaultHostSize);
73 EXPECT_EQ(starting_bounds.size(), host()->compositor()->size());
74 EXPECT_EQ(starting_bounds, host()->GetBoundsInPixels());
75 EXPECT_EQ(starting_bounds, root_window()->bounds());
77 test_screen()->SetDeviceScaleFactor(1.5f);
78 EXPECT_EQ(starting_bounds, host()->GetBoundsInPixels());
79 // Size should be rounded up after scaling.
80 EXPECT_EQ(gfx::Rect(0, 0, 534, 400), root_window()->bounds());
82 gfx::Transform transform;
83 transform.Translate(0, -1.1f);
84 host()->SetRootTransform(transform);
85 EXPECT_EQ(gfx::Rect(0, 1, 534, 401), root_window()->bounds());
87 EXPECT_EQ(starting_bounds, host()->GetBoundsInPixels());
88 EXPECT_EQ(gfx::Rect(0, 1, 534, 401), root_window()->bounds());
91 TEST_F(WindowTreeHostTest,
92 ShouldHaveExactRootWindowBoundsWithDisplayRotation1xScale) {
93 test_screen()->SetDeviceScaleFactor(1.f);
95 host()->SetBoundsInPixels(gfx::Rect(0, 0, 400, 300));
96 test_screen()->SetDisplayRotation(display::Display::ROTATE_0);
97 EXPECT_EQ(host()->GetBoundsInPixels(), gfx::Rect(0, 0, 400, 300));
98 EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().rotation(),
99 display::Display::ROTATE_0);
100 EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().GetSizeInPixel(),
101 gfx::Size(400, 300));
102 EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().bounds(),
103 gfx::Rect(0, 0, 400, 300));
104 EXPECT_EQ(gfx::Rect(400, 300), host()->window()->bounds());
106 host()->SetBoundsInPixels(gfx::Rect(0, 0, 400, 300));
107 test_screen()->SetDisplayRotation(display::Display::ROTATE_90);
108 EXPECT_EQ(host()->GetBoundsInPixels(), gfx::Rect(0, 0, 400, 300));
109 EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().rotation(),
110 display::Display::ROTATE_90);
111 EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().GetSizeInPixel(),
112 gfx::Size(300, 400));
113 EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().bounds(),
114 gfx::Rect(0, 0, 300, 400));
115 EXPECT_EQ(gfx::Rect(300, 400), host()->window()->bounds());
117 host()->SetBoundsInPixels(gfx::Rect(0, 0, 400, 300));
118 test_screen()->SetDisplayRotation(display::Display::ROTATE_180);
119 EXPECT_EQ(host()->GetBoundsInPixels(), gfx::Rect(0, 0, 400, 300));
120 EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().rotation(),
121 display::Display::ROTATE_180);
122 EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().GetSizeInPixel(),
123 gfx::Size(400, 300));
124 EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().bounds(),
125 gfx::Rect(0, 0, 400, 300));
126 EXPECT_EQ(gfx::Rect(400, 300), host()->window()->bounds());
128 host()->SetBoundsInPixels(gfx::Rect(0, 0, 400, 300));
129 test_screen()->SetDisplayRotation(display::Display::ROTATE_270);
130 EXPECT_EQ(host()->GetBoundsInPixels(), gfx::Rect(0, 0, 400, 300));
131 EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().rotation(),
132 display::Display::ROTATE_270);
133 EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().GetSizeInPixel(),
134 gfx::Size(300, 400));
135 EXPECT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().bounds(),
136 gfx::Rect(0, 0, 300, 400));
137 EXPECT_EQ(gfx::Rect(300, 400), host()->window()->bounds());
140 #if BUILDFLAG(IS_CHROMEOS_ASH)
141 TEST_F(WindowTreeHostTest, HoldPointerMovesOnChildResizing) {
142 aura::WindowEventDispatcher* dispatcher = host()->dispatcher();
144 aura::test::WindowEventDispatcherTestApi dispatcher_api(dispatcher);
146 EXPECT_FALSE(dispatcher_api.HoldingPointerMoves());
148 // Signal to the ui::Compositor that a child is resizing. This will
149 // immediately trigger input throttling.
150 host()->compositor()->OnChildResizing();
152 // Pointer moves should be throttled until the next commit. This has the
153 // effect of prioritizing the resize event above other operations in aura.
154 EXPECT_TRUE(dispatcher_api.HoldingPointerMoves());
156 // Wait for a CompositorFrame to be activated.
157 ui::DrawWaiterForTest::WaitForCompositingEnded(host()->compositor());
159 // Pointer moves should be routed normally after commit.
160 EXPECT_FALSE(dispatcher_api.HoldingPointerMoves());
164 #if !BUILDFLAG(IS_CHROMEOS_ASH)
165 // Tests if scale factor changes take effect. Previously a scale factor change
166 // wouldn't take effect without a bounds change. For context see
167 // https://crbug.com/1087626
168 TEST_F(WindowTreeHostTest, ShouldHandleTextScale) {
169 constexpr gfx::Rect starting_bounds(
170 aura::test::AuraTestHelper::kDefaultHostSize);
171 auto asserter = [&](float test_scale_factor) {
172 test_screen()->SetDeviceScaleFactor(test_scale_factor, false);
174 EXPECT_EQ(starting_bounds, host()->GetBoundsInPixels());
175 // Size should be rounded up after scaling.
177 gfx::ScaleToEnclosingRect(starting_bounds, 1.0f / test_scale_factor),
178 root_window()->bounds());
179 EXPECT_EQ(test_scale_factor, host()->device_scale_factor());
188 TEST_F(WindowTreeHostTest, NoRewritesPostIME) {
189 ui::test::TestEventRewriter event_rewriter;
190 host()->AddEventRewriter(&event_rewriter);
192 ui::KeyEvent key_event('A', ui::VKEY_A, ui::DomCode::NONE, 0);
193 ui::EventDispatchDetails details =
194 host()->GetInputMethod()->DispatchKeyEvent(&key_event);
195 ASSERT_TRUE(!details.dispatcher_destroyed && !details.target_destroyed);
196 EXPECT_EQ(0, event_rewriter.events_seen());
198 host()->RemoveEventRewriter(&event_rewriter);
201 TEST_F(WindowTreeHostTest, ConvertDIPToPixelsShouldRespectScaleFactor) {
202 const int width_in_pixels = 400;
203 const int height_in_pixels = 300;
204 const int width_in_dip = 200;
205 const int height_in_dip = 150;
207 host()->SetBoundsInPixels(gfx::Rect(0, 0, width_in_pixels, height_in_pixels));
208 test_screen()->SetDisplayRotation(display::Display::ROTATE_0);
210 test_screen()->SetDeviceScaleFactor(2.f);
212 EXPECT_EQ(ConvertDIPToPixels(host(), gfx::Point(0, 0)), gfx::Point(0, 0));
213 EXPECT_EQ(ConvertDIPToPixels(host(), gfx::Point(width_in_dip, 0)),
214 gfx::Point(width_in_pixels, 0));
215 EXPECT_EQ(ConvertDIPToPixels(host(), gfx::Point(0, height_in_dip)),
216 gfx::Point(0, height_in_pixels));
219 TEST_F(WindowTreeHostTest, ConvertDIPToPixelsShouldRespectRotation) {
220 const int width_in_pixels = 400;
221 const int height_in_pixels = 300;
222 const int width_in_dip = 300;
223 const int height_in_dip = 400;
225 host()->SetBoundsInPixels(gfx::Rect(0, 0, width_in_pixels, height_in_pixels));
226 test_screen()->SetDeviceScaleFactor(1.f);
228 test_screen()->SetDisplayRotation(display::Display::ROTATE_90);
230 EXPECT_EQ(ConvertDIPToPixels(host(), gfx::Point(0, 0)),
231 gfx::Point(width_in_pixels, 0));
232 EXPECT_EQ(ConvertDIPToPixels(host(), gfx::Point(width_in_dip, 0)),
233 gfx::Point(width_in_pixels, height_in_pixels));
234 EXPECT_EQ(ConvertDIPToPixels(host(), gfx::Point(width_in_dip, height_in_dip)),
235 gfx::Point(0, height_in_pixels));
236 EXPECT_EQ(ConvertDIPToPixels(host(), gfx::Point(0, height_in_dip)),
240 TEST_F(WindowTreeHostTest, ConvertDIPToPixelsShouldWorkWithPointF) {
241 host()->SetBoundsInPixels(gfx::Rect(0, 0, 400, 400));
242 test_screen()->SetDisplayRotation(display::Display::ROTATE_0);
243 test_screen()->SetDeviceScaleFactor(2.f);
245 EXPECT_EQ(ConvertDIPToPixels(host(), gfx::PointF(5.3f, 0)),
246 gfx::PointF(10.6f, 0));
249 TEST_F(WindowTreeHostTest, ConvertPixelsToDIPShouldRespectScaleFactor) {
250 const int width_in_pixels = 400;
251 const int height_in_pixels = 300;
252 const int width_in_dip = 200;
253 const int height_in_dip = 150;
255 host()->SetBoundsInPixels(gfx::Rect(0, 0, width_in_pixels, height_in_pixels));
256 test_screen()->SetDisplayRotation(display::Display::ROTATE_0);
258 test_screen()->SetDeviceScaleFactor(2.f);
260 EXPECT_EQ(ConvertPixelsToDIP(host(), gfx::Point(0, 0)), gfx::Point(0, 0));
261 EXPECT_EQ(ConvertPixelsToDIP(host(), gfx::Point(width_in_pixels, 0)),
262 gfx::Point(width_in_dip, 0));
263 EXPECT_EQ(ConvertPixelsToDIP(host(), gfx::Point(0, height_in_pixels)),
264 gfx::Point(0, height_in_dip));
267 TEST_F(WindowTreeHostTest, ConvertPixelsToDIPShouldRespectRotation) {
268 const int width_in_pixels = 400;
269 const int height_in_pixels = 300;
270 const int width_in_dip = 300;
271 const int height_in_dip = 400;
273 host()->SetBoundsInPixels(gfx::Rect(0, 0, width_in_pixels, height_in_pixels));
274 test_screen()->SetDeviceScaleFactor(1.f);
276 test_screen()->SetDisplayRotation(display::Display::ROTATE_90);
278 EXPECT_EQ(ConvertPixelsToDIP(host(), gfx::Point(0, 0)),
279 gfx::Point(0, height_in_dip));
280 EXPECT_EQ(ConvertPixelsToDIP(host(), gfx::Point(width_in_pixels, 0)),
283 ConvertPixelsToDIP(host(), gfx::Point(width_in_pixels, height_in_pixels)),
284 gfx::Point(width_in_dip, 0));
285 EXPECT_EQ(ConvertPixelsToDIP(host(), gfx::Point(0, height_in_pixels)),
286 gfx::Point(width_in_dip, height_in_dip));
289 TEST_F(WindowTreeHostTest, ConvertPixelsToDIPShouldWorkWithPointF) {
290 host()->SetBoundsInPixels(gfx::Rect(0, 0, 400, 400));
291 test_screen()->SetDisplayRotation(display::Display::ROTATE_0);
292 test_screen()->SetDeviceScaleFactor(2.f);
294 EXPECT_EQ(ConvertPixelsToDIP(host(), gfx::PointF(10.6f, 0)),
295 gfx::PointF(5.3f, 0));
298 class TestWindow : public ui::StubWindow {
300 explicit TestWindow(ui::PlatformWindowDelegate* delegate)
301 : StubWindow(delegate, false, gfx::Rect(400, 600)) {}
303 TestWindow(const TestWindow&) = delete;
304 TestWindow& operator=(const TestWindow&) = delete;
306 ~TestWindow() override {}
310 void Close() override {
311 // It is possible for the window to receive capture-change messages during
312 // destruction, for example on Windows (see crbug.com/770670).
313 delegate()->OnLostCapture();
317 class TestWindowTreeHost : public WindowTreeHostPlatform {
319 TestWindowTreeHost() {
320 SetPlatformWindow(std::make_unique<TestWindow>(this));
324 TestWindowTreeHost(const TestWindowTreeHost&) = delete;
325 TestWindowTreeHost& operator=(const TestWindowTreeHost&) = delete;
328 TEST_F(WindowTreeHostTest, LostCaptureDuringTearDown) {
329 #if BUILDFLAG(IS_WIN)
330 base::test::ScopedFeatureList scoped_feature_list;
331 scoped_feature_list.InitAndDisableFeature(
332 features::kApplyNativeOcclusionToCompositor);
334 TestWindowTreeHost host;
337 #if BUILDFLAG(IS_WIN)
338 class WindowTreeHostWithReleaseTest : public test::AuraTestBase {
341 void SetUp() override {
342 // Disable the headless check as the bots run with CHROME_HEADLESS set.
343 NativeWindowOcclusionTracker::SetHeadlessCheckEnabled(false);
344 scoped_feature_list_.InitWithFeaturesAndParameters(
346 {features::kCalculateNativeWinOcclusion, {}},
347 {features::kApplyNativeOcclusionToCompositor,
348 {{features::kApplyNativeOcclusionToCompositorType,
349 features::kApplyNativeOcclusionToCompositorTypeRelease}}},
352 AuraTestBase::SetUp();
355 void TearDown() override {
356 test::AuraTestBase::TearDown();
357 NativeWindowOcclusionTracker::SetHeadlessCheckEnabled(true);
361 base::test::ScopedFeatureList scoped_feature_list_;
364 cc::Layer* ccLayerFromUiLayer(ui::Layer* layer) {
365 return static_cast<ui::LayerAnimationDelegate*>(layer)->GetCcLayer();
368 bool WaitForFrame(WindowTreeHost* host) {
369 base::RunLoop run_loop;
370 bool got_frame = false;
371 host->compositor()->RequestPresentationTimeForNextFrame(
372 base::BindLambdaForTesting(
373 [&](const gfx::PresentationFeedback& feedback) {
381 TEST_F(WindowTreeHostWithReleaseTest, ToggleOccluded) {
383 // This tests needs to drive native occlusion. If native occlusion is
384 // used, it'll conflict with this test.
385 NativeWindowOcclusionTracker::DisableNativeWindowOcclusionTracking(host());
386 ASSERT_TRUE(NativeWindowOcclusionTracker::
387 IsNativeWindowOcclusionTrackingAlwaysEnabled(host()));
388 cc::Layer* host_window_cc_layer =
389 ccLayerFromUiLayer(host()->window()->layer());
390 const cc::Layer* compositor_root_layer = host_window_cc_layer->parent();
391 EXPECT_NE(nullptr, compositor_root_layer);
392 host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
393 // The compositor shouldn't actually hide immediately, it needs a frame to
395 EXPECT_TRUE(host()->compositor()->IsVisible());
396 EXPECT_EQ(nullptr, host_window_cc_layer->parent());
397 ASSERT_TRUE(WaitForFrame(host()));
398 EXPECT_FALSE(host()->compositor()->IsVisible());
399 host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
400 EXPECT_TRUE(host()->compositor()->IsVisible());
401 EXPECT_EQ(compositor_root_layer, host_window_cc_layer->parent());
404 TEST_F(WindowTreeHostWithReleaseTest, ShowWhileTransitioningToHidden) {
406 // This tests needs to drive native occlusion. If native occlusion is
407 // used, it'll conflict with this test.
408 NativeWindowOcclusionTracker::DisableNativeWindowOcclusionTracking(host());
409 ASSERT_TRUE(NativeWindowOcclusionTracker::
410 IsNativeWindowOcclusionTrackingAlwaysEnabled(host()));
411 cc::Layer* host_window_cc_layer =
412 ccLayerFromUiLayer(host()->window()->layer());
413 const cc::Layer* compositor_root_layer = host_window_cc_layer->parent();
414 EXPECT_NE(nullptr, compositor_root_layer);
415 host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
416 // The compositor shouldn't actually hide immediately, it needs a frame to
418 EXPECT_TRUE(host()->compositor()->IsVisible());
419 EXPECT_EQ(nullptr, host_window_cc_layer->parent());
420 host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
421 EXPECT_TRUE(host()->compositor()->IsVisible());
422 EXPECT_EQ(compositor_root_layer, host_window_cc_layer->parent());
425 TEST_F(WindowTreeHostWithReleaseTest, VideoCaptureLockForcesVisible) {
426 ASSERT_TRUE(NativeWindowOcclusionTracker::
427 IsNativeWindowOcclusionTrackingAlwaysEnabled(host()));
428 // This tests needs to drive native occlusion. If native occlusion is
429 // used, it'll conflict with this test.
430 NativeWindowOcclusionTracker::DisableNativeWindowOcclusionTracking(host());
432 host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
433 ASSERT_TRUE(WaitForFrame(host()));
434 EXPECT_FALSE(host()->compositor()->IsVisible());
435 std::unique_ptr<WindowTreeHost::VideoCaptureLock> lock =
436 host()->CreateVideoCaptureLock();
437 EXPECT_TRUE(host()->compositor()->IsVisible());
438 host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
439 EXPECT_TRUE(host()->compositor()->IsVisible());
440 host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
441 EXPECT_TRUE(host()->compositor()->IsVisible());
442 ASSERT_TRUE(WaitForFrame(host()));
443 EXPECT_TRUE(host()->compositor()->IsVisible());
445 ASSERT_TRUE(WaitForFrame(host()));
446 EXPECT_FALSE(host()->compositor()->IsVisible());
447 host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
448 EXPECT_TRUE(host()->compositor()->IsVisible());
449 ASSERT_TRUE(WaitForFrame(host()));
450 EXPECT_TRUE(host()->compositor()->IsVisible());
453 class WindowTreeHostWithThrottleTest : public test::AuraTestBase {
456 void SetUp() override {
457 // Disable the headless check as the bots run with CHROME_HEADLESS set.
458 NativeWindowOcclusionTracker::SetHeadlessCheckEnabled(false);
459 scoped_feature_list_.InitWithFeaturesAndParameters(
461 {features::kCalculateNativeWinOcclusion, {}},
462 {features::kApplyNativeOcclusionToCompositor,
463 {{features::kApplyNativeOcclusionToCompositorType,
464 features::kApplyNativeOcclusionToCompositorTypeThrottle}}},
467 AuraTestBase::SetUp();
470 void TearDown() override {
471 test::AuraTestBase::TearDown();
472 NativeWindowOcclusionTracker::SetHeadlessCheckEnabled(true);
476 base::test::ScopedFeatureList scoped_feature_list_;
479 TEST_F(WindowTreeHostWithThrottleTest, DISABLED_Basic) {
481 EXPECT_TRUE(host()->compositor()->IsVisible());
482 EXPECT_TRUE(test::GetThrottledHosts().empty());
483 host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
484 EXPECT_TRUE(host()->compositor()->IsVisible());
485 EXPECT_TRUE(base::Contains(test::GetThrottledHosts(), host()));
486 host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
487 EXPECT_TRUE(test::GetThrottledHosts().empty());
488 EXPECT_TRUE(host()->compositor()->IsVisible());
491 TEST_F(WindowTreeHostWithThrottleTest, DISABLED_CallHideDirectly) {
493 EXPECT_TRUE(host()->compositor()->IsVisible());
494 EXPECT_TRUE(test::GetThrottledHosts().empty());
495 host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
496 EXPECT_TRUE(host()->compositor()->IsVisible());
497 EXPECT_TRUE(base::Contains(test::GetThrottledHosts(), host()));
499 EXPECT_TRUE(test::GetThrottledHosts().empty());
500 EXPECT_FALSE(host()->compositor()->IsVisible());
503 #endif // BUILDFLAG(IS_WIN)