1 // Copyright 2019 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/aura/window_tree_host_platform.h"
6
7 #include "ui/aura/test/aura_test_base.h"
8 #include "ui/aura/window_tree_host_observer.h"
9 #include "ui/platform_window/stub/stub_window.h"
10
11 namespace aura {
12 namespace {
13
14 using WindowTreeHostPlatformTest = test::AuraTestBase;
15
16 // Trivial WindowTreeHostPlatform implementation that installs a StubWindow as
17 // the PlatformWindow.
18 class TestWindowTreeHost : public WindowTreeHostPlatform {
19 public:
TestWindowTreeHost()20 TestWindowTreeHost() {
21 SetPlatformWindow(std::make_unique<ui::StubWindow>(this));
22 CreateCompositor();
23 }
24
platform_window()25 ui::PlatformWindow* platform_window() {
26 return WindowTreeHostPlatform::platform_window();
27 }
28
29 private:
30 DISALLOW_COPY_AND_ASSIGN(TestWindowTreeHost);
31 };
32
33 // WindowTreeHostObserver that tracks calls to
34 // OnHostWill/DidProcessBoundsChange. Additionally, this triggers a bounds
35 // change from within OnHostResized(). Such a scenario happens in production
36 // code.
37 class TestWindowTreeHostObserver : public aura::WindowTreeHostObserver {
38 public:
TestWindowTreeHostObserver(WindowTreeHostPlatform * host,ui::PlatformWindow * platform_window)39 TestWindowTreeHostObserver(WindowTreeHostPlatform* host,
40 ui::PlatformWindow* platform_window)
41 : host_(host), platform_window_(platform_window) {
42 host_->AddObserver(this);
43 }
~TestWindowTreeHostObserver()44 ~TestWindowTreeHostObserver() override { host_->RemoveObserver(this); }
45
on_host_did_process_bounds_change_count() const46 int on_host_did_process_bounds_change_count() const {
47 return on_host_did_process_bounds_change_count_;
48 }
49
on_host_will_process_bounds_change_count() const50 int on_host_will_process_bounds_change_count() const {
51 return on_host_will_process_bounds_change_count_;
52 }
53
54 // aura::WindowTreeHostObserver:
OnHostResized(WindowTreeHost * host)55 void OnHostResized(WindowTreeHost* host) override {
56 if (!should_change_bounds_in_on_resized_)
57 return;
58
59 should_change_bounds_in_on_resized_ = false;
60 gfx::Rect bounds = platform_window_->GetBounds();
61 bounds.set_x(bounds.x() + 1);
62 host_->SetBoundsInPixels(bounds);
63 }
OnHostWillProcessBoundsChange(WindowTreeHost * host)64 void OnHostWillProcessBoundsChange(WindowTreeHost* host) override {
65 ++on_host_will_process_bounds_change_count_;
66 }
OnHostDidProcessBoundsChange(WindowTreeHost * host)67 void OnHostDidProcessBoundsChange(WindowTreeHost* host) override {
68 ++on_host_did_process_bounds_change_count_;
69 }
70
71 private:
72 WindowTreeHostPlatform* host_;
73 ui::PlatformWindow* platform_window_;
74 bool should_change_bounds_in_on_resized_ = true;
75 int on_host_will_process_bounds_change_count_ = 0;
76 int on_host_did_process_bounds_change_count_ = 0;
77
78 DISALLOW_COPY_AND_ASSIGN(TestWindowTreeHostObserver);
79 };
80
81 // Regression test for https://crbug.com/958449
TEST_F(WindowTreeHostPlatformTest,HostWillProcessBoundsChangeRecursion)82 TEST_F(WindowTreeHostPlatformTest, HostWillProcessBoundsChangeRecursion) {
83 TestWindowTreeHost host;
84 TestWindowTreeHostObserver observer(&host, host.platform_window());
85 // This call triggers a recursive bounds change. That is, this results in
86 // WindowTreePlatform::OnBoundsChanged() indirectly calling back into
87 // WindowTreePlatform::OnBoundsChanged(). In such a scenario the observer
88 // should be notified only once (see comment in
89 // WindowTreeHostPlatform::OnBoundsChanged() for details).
90 host.SetBoundsInPixels(gfx::Rect(1, 2, 3, 4));
91 EXPECT_EQ(1, observer.on_host_did_process_bounds_change_count());
92 EXPECT_EQ(1, observer.on_host_will_process_bounds_change_count());
93 }
94
95 } // namespace
96 } // namespace aura
97