| // Copyright 2017 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 "extensions/shell/browser/root_window_controller.h" |
| #include "base/memory/raw_ptr.h" |
| |
| #include <algorithm> |
| #include <list> |
| #include <memory> |
| |
| #include "content/public/browser/browser_context.h" |
| #include "extensions/browser/app_window/app_window.h" |
| #include "extensions/browser/app_window/native_app_window.h" |
| #include "extensions/common/extension_builder.h" |
| #include "extensions/shell/browser/root_window_controller.h" |
| #include "extensions/shell/browser/shell_app_window_client.h" |
| #include "extensions/shell/browser/shell_native_app_window_aura.h" |
| #include "extensions/shell/test/shell_test_base_aura.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/aura/window.h" |
| #include "ui/aura/window_tree_host.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/size.h" |
| |
| namespace extensions { |
| |
| namespace { |
| |
| constexpr gfx::Rect kScreenBounds = gfx::Rect(0, 0, 800, 600); |
| |
| // A fake that creates and exposes RootWindowControllers. |
| class FakeDesktopDelegate : public RootWindowController::DesktopDelegate { |
| public: |
| explicit FakeDesktopDelegate(content::BrowserContext* browser_context) |
| : browser_context_(browser_context) {} |
| |
| FakeDesktopDelegate(const FakeDesktopDelegate&) = delete; |
| FakeDesktopDelegate& operator=(const FakeDesktopDelegate&) = delete; |
| |
| ~FakeDesktopDelegate() override = default; |
| |
| RootWindowController* CreateRootWindowController() { |
| root_window_controllers_.emplace_back( |
| std::make_unique<RootWindowController>(this, kScreenBounds, |
| browser_context_)); |
| return root_window_controllers_.back().get(); |
| } |
| |
| // RootWindowController::DesktopDelegate: |
| void CloseRootWindowController( |
| RootWindowController* root_window_controller) override { |
| auto it = std::find_if(root_window_controllers_.begin(), |
| root_window_controllers_.end(), |
| [&](const auto& candidate) { |
| return candidate.get() == root_window_controller; |
| }); |
| DCHECK(it != root_window_controllers_.end()); |
| root_window_controllers_.erase(it); |
| } |
| |
| auto root_window_controller_count() { |
| return root_window_controllers_.size(); |
| } |
| |
| private: |
| raw_ptr<content::BrowserContext> browser_context_; |
| std::list<std::unique_ptr<RootWindowController>> root_window_controllers_; |
| }; |
| |
| // An AppWindowClient for use without a DesktopController. |
| class TestAppWindowClient : public ShellAppWindowClient { |
| public: |
| TestAppWindowClient() = default; |
| ~TestAppWindowClient() override = default; |
| |
| NativeAppWindow* CreateNativeAppWindow( |
| AppWindow* window, |
| AppWindow::CreateParams* params) override { |
| return new ShellNativeAppWindowAura(window, *params); |
| } |
| }; |
| |
| } // namespace |
| |
| class RootWindowControllerTest : public ShellTestBaseAura { |
| public: |
| RootWindowControllerTest() = default; |
| |
| RootWindowControllerTest(const RootWindowControllerTest&) = delete; |
| RootWindowControllerTest& operator=(const RootWindowControllerTest&) = delete; |
| |
| ~RootWindowControllerTest() override = default; |
| |
| void SetUp() override { |
| ShellTestBaseAura::SetUp(); |
| |
| AppWindowClient::Set(&app_window_client_); |
| extension_ = ExtensionBuilder("Test").Build(); |
| |
| desktop_delegate_ = |
| std::make_unique<FakeDesktopDelegate>(browser_context()); |
| } |
| |
| void TearDown() override { |
| desktop_delegate_.reset(); |
| AppWindowClient::Set(nullptr); |
| ShellTestBaseAura::TearDown(); |
| } |
| |
| protected: |
| // Creates and returns an AppWindow using the RootWindowController. |
| AppWindow* CreateAppWindow(RootWindowController* root) { |
| AppWindow* app_window = |
| AppWindowClient::Get()->CreateAppWindow(browser_context(), extension()); |
| InitAppWindow(app_window); |
| root->AddAppWindow(app_window, app_window->GetNativeWindow()); |
| return app_window; |
| } |
| |
| // Checks that there are |num_expected| AppWindows registered. |
| void ExpectNumAppWindows(size_t expected) { |
| EXPECT_EQ(expected, |
| AppWindowRegistry::Get(browser_context())->app_windows().size()); |
| } |
| |
| // Checks that |root|'s root window has |num_expected| child windows. |
| void ExpectNumChildWindows(size_t expected, RootWindowController* root) { |
| EXPECT_EQ(expected, root->host()->window()->children().size()); |
| } |
| |
| FakeDesktopDelegate* desktop_delegate() { return desktop_delegate_.get(); } |
| const Extension* extension() { return extension_.get(); } |
| |
| private: |
| TestAppWindowClient app_window_client_; |
| |
| scoped_refptr<const Extension> extension_; |
| std::unique_ptr<FakeDesktopDelegate> desktop_delegate_; |
| }; |
| |
| // Tests RootWindowController's basic setup and teardown. |
| TEST_F(RootWindowControllerTest, Basic) { |
| RootWindowController* root_window_controller = |
| desktop_delegate()->CreateRootWindowController(); |
| EXPECT_TRUE(root_window_controller->host()); |
| |
| // The RootWindowController destroys itself when the root window closes. |
| root_window_controller->OnHostCloseRequested(root_window_controller->host()); |
| EXPECT_EQ(0u, desktop_delegate()->root_window_controller_count()); |
| } |
| |
| // Tests the window layout. |
| TEST_F(RootWindowControllerTest, FillLayout) { |
| RootWindowController* root_window_controller = |
| desktop_delegate()->CreateRootWindowController(); |
| |
| root_window_controller->host()->SetBoundsInPixels(gfx::Rect(0, 0, 500, 700)); |
| |
| CreateAppWindow(root_window_controller); |
| ExpectNumAppWindows(1u); |
| ExpectNumChildWindows(1u, root_window_controller); |
| |
| // Test that reshaping the host window also resizes the child window, and |
| // moving the host doesn't affect the child's position relative to the host. |
| root_window_controller->host()->SetBoundsInPixels( |
| gfx::Rect(100, 200, 300, 400)); |
| |
| const aura::Window* root_window = root_window_controller->host()->window(); |
| EXPECT_EQ(gfx::Rect(0, 0, 300, 400), root_window->bounds()); |
| EXPECT_EQ(gfx::Rect(0, 0, 300, 400), root_window->children()[0]->bounds()); |
| |
| // The AppWindow will close on shutdown. |
| } |
| |
| // Tests creating and removing AppWindows. |
| TEST_F(RootWindowControllerTest, AppWindows) { |
| RootWindowController* root_window_controller = |
| desktop_delegate()->CreateRootWindowController(); |
| |
| { |
| // Create some AppWindows. |
| CreateAppWindow(root_window_controller); |
| AppWindow* middle_window = CreateAppWindow(root_window_controller); |
| CreateAppWindow(root_window_controller); |
| |
| ExpectNumAppWindows(3u); |
| ExpectNumChildWindows(3u, root_window_controller); |
| |
| // Close one window, which deletes |middle_window|. |
| middle_window->GetBaseWindow()->Close(); |
| } |
| |
| ExpectNumAppWindows(2u); |
| ExpectNumChildWindows(2u, root_window_controller); |
| |
| // Close all remaining windows. |
| root_window_controller->CloseAppWindows(); |
| ExpectNumAppWindows(0u); |
| } |
| |
| // Tests that a second RootWindowController can be used independently of the |
| // first. |
| TEST_F(RootWindowControllerTest, Multiple) { |
| // Create the longer-lived RootWindowController before the shorter-lived one |
| // is deleted. Otherwise it may be created at the same address, preventing |
| // the test from failing on use-after-free. |
| RootWindowController* longer_lived = |
| desktop_delegate()->CreateRootWindowController(); |
| AppWindow* longer_lived_app_window = CreateAppWindow(longer_lived); |
| ExpectNumAppWindows(1u); |
| |
| { |
| RootWindowController* shorter_lived = |
| desktop_delegate()->CreateRootWindowController(); |
| AppWindow* app_window = CreateAppWindow(shorter_lived); |
| ExpectNumAppWindows(2u); |
| app_window->GetBaseWindow()->Close(); // Deletes the AppWindow. |
| } |
| ExpectNumAppWindows(1u); |
| |
| // The still-living RootWindowController can still be used. |
| AppWindow* longer_lived_app_window2 = CreateAppWindow(longer_lived); |
| ExpectNumAppWindows(2u); |
| longer_lived_app_window->GetBaseWindow()->Close(); |
| ExpectNumAppWindows(1u); |
| longer_lived_app_window2->GetBaseWindow()->Close(); |
| ExpectNumAppWindows(0u); |
| } |
| |
| } // namespace extensions |