blob: 5a6ed0885c77359df5ade277389a82dc5bd0fb42 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/aura/window_tree_host_platform.h"
#include "base/memory/raw_ptr.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "ui/aura/test/aura_test_base.h"
#include "ui/aura/window_tree_host_observer.h"
#include "ui/base/ui_base_features.h"
#include "ui/platform_window/stub/stub_window.h"
namespace aura {
namespace {
class WindowTreeHostPlatformTest : public test::AuraTestBase {
public:
WindowTreeHostPlatformTest() = default;
// test::AuraTestBase:
void SetUp() override {
test::AuraTestBase::SetUp();
#if BUILDFLAG(IS_WIN)
scoped_feature_list_.InitAndDisableFeature(
features::kApplyNativeOcclusionToCompositor);
#endif
}
private:
#if BUILDFLAG(IS_WIN)
base::test::ScopedFeatureList scoped_feature_list_;
#endif
};
// Trivial WindowTreeHostPlatform implementation that installs a StubWindow as
// the PlatformWindow.
class TestWindowTreeHost : public WindowTreeHostPlatform {
public:
TestWindowTreeHost() {
SetPlatformWindow(std::make_unique<ui::StubWindow>(this));
CreateCompositor();
}
TestWindowTreeHost(const TestWindowTreeHost&) = delete;
TestWindowTreeHost& operator=(const TestWindowTreeHost&) = delete;
ui::PlatformWindow* platform_window() {
return WindowTreeHostPlatform::platform_window();
}
};
// WindowTreeHostObserver that tracks calls to
// OnHostWill/DidProcessBoundsChange. Additionally, this triggers a bounds
// change from within OnHostResized(). Such a scenario happens in production
// code.
class TestWindowTreeHostObserver : public WindowTreeHostObserver {
public:
TestWindowTreeHostObserver(WindowTreeHostPlatform* host,
ui::PlatformWindow* platform_window)
: host_(host), platform_window_(platform_window) {
host_->AddObserver(this);
}
TestWindowTreeHostObserver(const TestWindowTreeHostObserver&) = delete;
TestWindowTreeHostObserver& operator=(const TestWindowTreeHostObserver&) =
delete;
~TestWindowTreeHostObserver() override { host_->RemoveObserver(this); }
int on_host_did_process_bounds_change_count() const {
return on_host_did_process_bounds_change_count_;
}
int on_host_will_process_bounds_change_count() const {
return on_host_will_process_bounds_change_count_;
}
// WindowTreeHostObserver:
void OnHostResized(WindowTreeHost* host) override {
if (!should_change_bounds_in_on_resized_)
return;
should_change_bounds_in_on_resized_ = false;
gfx::Rect bounds = platform_window_->GetBounds();
bounds.set_x(bounds.x() + 1);
host_->SetBoundsInPixels(bounds);
}
void OnHostWillProcessBoundsChange(WindowTreeHost* host) override {
++on_host_will_process_bounds_change_count_;
}
void OnHostDidProcessBoundsChange(WindowTreeHost* host) override {
++on_host_did_process_bounds_change_count_;
}
private:
raw_ptr<WindowTreeHostPlatform> host_;
raw_ptr<ui::PlatformWindow> platform_window_;
bool should_change_bounds_in_on_resized_ = true;
int on_host_will_process_bounds_change_count_ = 0;
int on_host_did_process_bounds_change_count_ = 0;
};
// Regression test for https://crbug.com/958449
TEST_F(WindowTreeHostPlatformTest, HostWillProcessBoundsChangeRecursion) {
TestWindowTreeHost host;
TestWindowTreeHostObserver observer(&host, host.platform_window());
// This call triggers a recursive bounds change. That is, this results in
// WindowTreePlatform::OnBoundsChanged() indirectly calling back into
// WindowTreePlatform::OnBoundsChanged(). In such a scenario the observer
// should be notified only once (see comment in
// WindowTreeHostPlatform::OnBoundsChanged() for details).
host.SetBoundsInPixels(gfx::Rect(1, 2, 3, 4));
EXPECT_EQ(1, observer.on_host_did_process_bounds_change_count());
EXPECT_EQ(1, observer.on_host_will_process_bounds_change_count());
}
// Deletes WindowTreeHostPlatform from OnHostMovedInPixels().
class DeleteHostWindowTreeHostObserver : public WindowTreeHostObserver {
public:
explicit DeleteHostWindowTreeHostObserver(
std::unique_ptr<TestWindowTreeHost> host)
: host_(std::move(host)) {
host_->AddObserver(this);
}
DeleteHostWindowTreeHostObserver(const DeleteHostWindowTreeHostObserver&) =
delete;
DeleteHostWindowTreeHostObserver& operator=(
const DeleteHostWindowTreeHostObserver&) = delete;
~DeleteHostWindowTreeHostObserver() override = default;
TestWindowTreeHost* host() { return host_.get(); }
// WindowTreeHostObserver:
void OnHostMovedInPixels(WindowTreeHost* host,
const gfx::Point& new_origin_in_pixels) override {
host_->RemoveObserver(this);
host_.reset();
}
private:
std::unique_ptr<TestWindowTreeHost> host_;
};
// Verifies WindowTreeHostPlatform can be safely deleted when calling
// OnHostMovedInPixels().
// Regression test for https://crbug.com/1185482
TEST_F(WindowTreeHostPlatformTest, DeleteHostFromOnHostMovedInPixels) {
std::unique_ptr<TestWindowTreeHost> host =
std::make_unique<TestWindowTreeHost>();
DeleteHostWindowTreeHostObserver observer(std::move(host));
observer.host()->SetBoundsInPixels(gfx::Rect(1, 2, 3, 4));
EXPECT_EQ(nullptr, observer.host());
}
} // namespace
} // namespace aura