blob: 9eee4a9a19d1a8b7ba2d9ea7313efbf84cc8686a [file] [log] [blame]
// 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 "extensions/browser/app_window/app_window.h"
#include "extensions/browser/app_window/native_app_window.h"
#include "extensions/shell/browser/shell_app_delegate.h"
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/layout_manager.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tracker.h"
#include "ui/aura/window_tree_host.h"
#include "ui/display/display.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/wm/core/default_screen_position_client.h"
namespace extensions {
namespace {
// A simple layout manager that makes each new window fill its parent.
class FillLayout : public aura::LayoutManager {
public:
FillLayout(aura::Window* owner) : owner_(owner) { DCHECK(owner_); }
~FillLayout() override = default;
private:
// aura::LayoutManager:
void OnWindowResized() override {
// Size the owner's immediate child windows.
aura::WindowTracker children_tracker(owner_->children());
while (!children_tracker.windows().empty()) {
aura::Window* child = children_tracker.Pop();
child->SetBounds(gfx::Rect(owner_->bounds().size()));
}
}
void OnWindowAddedToLayout(aura::Window* child) override {
DCHECK_EQ(owner_, child->parent());
// Create a rect at 0,0 with the size of the parent.
gfx::Size parent_size = child->parent()->bounds().size();
child->SetBounds(gfx::Rect(parent_size));
}
void OnWillRemoveWindowFromLayout(aura::Window* child) override {}
void OnWindowRemovedFromLayout(aura::Window* child) override {}
void OnChildWindowVisibilityChanged(aura::Window* child,
bool visible) override {}
void SetChildBounds(aura::Window* child,
const gfx::Rect& requested_bounds) override {
SetChildBoundsDirect(child, requested_bounds);
}
aura::Window* owner_; // Not owned.
DISALLOW_COPY_AND_ASSIGN(FillLayout);
};
// A simple screen positioning client that translates bounds to screen
// coordinates using the offset of the root window in screen coordinates.
class ScreenPositionClient : public wm::DefaultScreenPositionClient {
public:
ScreenPositionClient() = default;
~ScreenPositionClient() override = default;
// wm::DefaultScreenPositionClient:
void SetBounds(aura::Window* window,
const gfx::Rect& bounds,
const display::Display& display) override {
aura::Window* root_window = window->GetRootWindow();
DCHECK(window);
// Convert the window's origin to its root window's coordinates.
gfx::Point origin = bounds.origin();
aura::Window::ConvertPointToTarget(window->parent(), root_window, &origin);
// Translate the origin by the root window's offset in screen coordinates.
gfx::Point host_origin = GetOriginInScreen(root_window);
origin.Offset(-host_origin.x(), -host_origin.y());
window->SetBounds(gfx::Rect(origin, bounds.size()));
}
private:
DISALLOW_COPY_AND_ASSIGN(ScreenPositionClient);
};
} // namespace
RootWindowController::RootWindowController(
DesktopDelegate* desktop_delegate,
const gfx::Rect& bounds,
content::BrowserContext* browser_context)
: desktop_delegate_(desktop_delegate),
browser_context_(browser_context),
screen_position_client_(std::make_unique<ScreenPositionClient>()) {
DCHECK(desktop_delegate_);
DCHECK(browser_context_);
host_.reset(aura::WindowTreeHost::Create(bounds));
host_->InitHost();
host_->window()->Show();
aura::client::SetWindowParentingClient(host_->window(), this);
aura::client::SetScreenPositionClient(host_->window(),
screen_position_client_.get());
// Ensure the window fills the display.
host_->window()->SetLayoutManager(new FillLayout(host_->window()));
host_->AddObserver(this);
host_->Show();
}
RootWindowController::~RootWindowController() {
CloseAppWindows();
DestroyWindowTreeHost();
}
void RootWindowController::AddAppWindow(AppWindow* app_window,
gfx::NativeWindow window) {
if (app_windows_.empty()) {
// Start observing for OnAppWindowRemoved.
AppWindowRegistry* registry = AppWindowRegistry::Get(browser_context_);
registry->AddObserver(this);
}
app_windows_.push_back(app_window);
aura::Window* root_window = host_->window();
root_window->AddChild(window);
}
void RootWindowController::RemoveAppWindow(AppWindow* app_window) {
host_->window()->RemoveChild(app_window->GetNativeWindow());
app_windows_.remove(app_window);
if (app_windows_.empty())
AppWindowRegistry::Get(browser_context_)->RemoveObserver(this);
}
void RootWindowController::CloseAppWindows() {
if (app_windows_.empty())
return;
// Remove the observer before closing windows to avoid triggering
// OnAppWindowRemoved, which would mutate |app_windows_|.
AppWindowRegistry::Get(browser_context_)->RemoveObserver(this);
for (AppWindow* app_window : app_windows_)
app_window->GetBaseWindow()->Close(); // Close() deletes |app_window|.
app_windows_.clear();
}
void RootWindowController::UpdateSize(const gfx::Size& size) {
host_->SetBoundsInPixels(gfx::Rect(size));
}
aura::Window* RootWindowController::GetDefaultParent(aura::Window* window,
const gfx::Rect& bounds) {
return host_->window();
}
void RootWindowController::OnHostCloseRequested(aura::WindowTreeHost* host) {
DCHECK_EQ(host_.get(), host);
CloseAppWindows();
// The ShellDesktopControllerAura will delete us.
desktop_delegate_->CloseRootWindowController(this);
}
void RootWindowController::OnAppWindowRemoved(AppWindow* window) {
if (app_windows_.empty())
return;
// If we created this AppWindow, remove it from our list so we don't try to
// close it again later.
app_windows_.remove(window);
// Close when all AppWindows are closed.
if (app_windows_.empty()) {
AppWindowRegistry::Get(browser_context_)->RemoveObserver(this);
desktop_delegate_->CloseRootWindowController(this);
}
}
void RootWindowController::DestroyWindowTreeHost() {
host_->RemoveObserver(this);
host_.reset();
}
} // namespace extensions