blob: a6cb5997dc7b0e39b18dec3a352f739070c6d5bf [file] [log] [blame]
// Copyright 2016 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/views/mus/mus_client.h"
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/single_thread_task_runner.h"
#include "services/service_manager/public/cpp/connector.h"
#include "services/ui/public/cpp/gpu/gpu.h"
#include "services/ui/public/cpp/input_devices/input_device_client.h"
#include "services/ui/public/cpp/property_type_converters.h"
#include "services/ui/public/interfaces/constants.mojom.h"
#include "services/ui/public/interfaces/window_manager.mojom.h"
#include "ui/aura/env.h"
#include "ui/aura/mus/capture_synchronizer.h"
#include "ui/aura/mus/mus_context_factory.h"
#include "ui/aura/mus/property_converter.h"
#include "ui/aura/mus/window_tree_client.h"
#include "ui/aura/mus/window_tree_host_mus.h"
#include "ui/aura/mus/window_tree_host_mus_init_params.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/mojo/clipboard_client.h"
#include "ui/views/mus/aura_init.h"
#include "ui/views/mus/ax_remote_host.h"
#include "ui/views/mus/desktop_window_tree_host_mus.h"
#include "ui/views/mus/mus_property_mirror.h"
#include "ui/views/mus/pointer_watcher_event_router.h"
#include "ui/views/mus/screen_mus.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/wm/core/shadow_types.h"
#include "ui/wm/core/wm_state.h"
#if defined(USE_OZONE)
#include "ui/base/cursor/ozone/cursor_data_factory_ozone.h"
#endif
// Widget::InitParams::Type must match that of ui::mojom::WindowType.
#define WINDOW_TYPES_MATCH(NAME) \
static_assert( \
static_cast<int32_t>(views::Widget::InitParams::TYPE_##NAME) == \
static_cast<int32_t>(ui::mojom::WindowType::NAME), \
"Window type constants must match")
WINDOW_TYPES_MATCH(WINDOW);
WINDOW_TYPES_MATCH(PANEL);
WINDOW_TYPES_MATCH(WINDOW_FRAMELESS);
WINDOW_TYPES_MATCH(CONTROL);
WINDOW_TYPES_MATCH(POPUP);
WINDOW_TYPES_MATCH(MENU);
WINDOW_TYPES_MATCH(TOOLTIP);
WINDOW_TYPES_MATCH(BUBBLE);
WINDOW_TYPES_MATCH(DRAG);
// ui::mojom::WindowType::UNKNOWN does not correspond to a value in
// Widget::InitParams::Type.
namespace views {
// static
MusClient* MusClient::instance_ = nullptr;
MusClient::InitParams::InitParams() = default;
MusClient::InitParams::~InitParams() = default;
MusClient::MusClient(const InitParams& params) : identity_(params.identity) {
DCHECK(!instance_);
DCHECK(aura::Env::GetInstance());
instance_ = this;
#if defined(USE_OZONE)
// If we're in a mus client, we aren't going to have all of ozone initialized
// even though we're in an ozone build. All the hard coded USE_OZONE ifdefs
// that handle cursor code expect that there will be a CursorFactoryOzone
// instance. Partially initialize the ozone cursor internals here, like we
// partially initialize other ozone subsystems in
// ChromeBrowserMainExtraPartsViews.
if (params.create_cursor_factory)
cursor_factory_ozone_ = std::make_unique<ui::CursorDataFactoryOzone>();
#endif
property_converter_ = std::make_unique<aura::PropertyConverter>();
property_converter_->RegisterPrimitiveProperty(
::wm::kShadowElevationKey,
ui::mojom::WindowManager::kShadowElevation_Property,
aura::PropertyConverter::CreateAcceptAnyValueCallback());
if (params.create_wm_state)
wm_state_ = std::make_unique<wm::WMState>();
service_manager::Connector* connector = params.connector;
if (!params.window_tree_client) {
// If this process is running in the WindowService, then discardable memory
// should have already been created.
const bool create_discardable_memory = !params.running_in_ws_process;
owned_window_tree_client_ =
aura::WindowTreeClient::CreateForWindowTreeFactory(
connector, this, create_discardable_memory,
std::move(params.io_task_runner));
window_tree_client_ = owned_window_tree_client_.get();
aura::Env::GetInstance()->SetWindowTreeClient(window_tree_client_);
} else {
window_tree_client_ = params.window_tree_client;
}
pointer_watcher_event_router_ =
std::make_unique<PointerWatcherEventRouter>(window_tree_client_);
if (connector && !params.running_in_ws_process) {
input_device_client_ = std::make_unique<ui::InputDeviceClient>();
ui::mojom::InputDeviceServerPtr input_device_server;
connector->BindInterface(ui::mojom::kServiceName, &input_device_server);
input_device_client_->Connect(std::move(input_device_server));
screen_ = std::make_unique<ScreenMus>(this);
display::Screen::SetScreenInstance(screen_.get());
// NOTE: this deadlocks if |running_in_ws_process| is true (because the main
// thread is running the WindowService).
window_tree_client_->WaitForDisplays();
ui::mojom::ClipboardHostPtr clipboard_host_ptr;
connector->BindInterface(ui::mojom::kServiceName, &clipboard_host_ptr);
ui::Clipboard::SetClipboardForCurrentThread(
std::make_unique<ui::ClipboardClient>(std::move(clipboard_host_ptr)));
if (params.use_accessibility_host) {
ax_remote_host_ = std::make_unique<AXRemoteHost>();
ax_remote_host_->Init(connector);
}
}
ViewsDelegate::GetInstance()->set_native_widget_factory(
base::Bind(&MusClient::CreateNativeWidget, base::Unretained(this)));
ViewsDelegate::GetInstance()->set_desktop_window_tree_host_factory(base::Bind(
&MusClient::CreateDesktopWindowTreeHost, base::Unretained(this)));
}
MusClient::~MusClient() {
// Tear down accessibility before WindowTreeClient to ensure window tree
// cleanup doesn't trigger accessibility events.
ax_remote_host_.reset();
// ~WindowTreeClient calls back to us (we're its delegate), destroy it while
// we are still valid.
owned_window_tree_client_.reset();
window_tree_client_ = nullptr;
ui::OSExchangeDataProviderFactory::SetFactory(nullptr);
ui::Clipboard::DestroyClipboardForCurrentThread();
if (ViewsDelegate::GetInstance()) {
ViewsDelegate::GetInstance()->set_native_widget_factory(
ViewsDelegate::NativeWidgetFactory());
ViewsDelegate::GetInstance()->set_desktop_window_tree_host_factory(
ViewsDelegate::DesktopWindowTreeHostFactory());
}
if (screen_) {
display::Screen::SetScreenInstance(nullptr);
screen_.reset();
}
DCHECK_EQ(instance_, this);
instance_ = nullptr;
DCHECK(aura::Env::GetInstance());
}
// static
bool MusClient::ShouldCreateDesktopNativeWidgetAura(
const Widget::InitParams& init_params) {
const bool from_window_service =
(init_params.context &&
init_params.context->env()->mode() == aura::Env::Mode::LOCAL) ||
(init_params.parent &&
init_params.parent->env()->mode() == aura::Env::Mode::LOCAL);
// |from_window_service| is true if the aura::Env has a mode of LOCAL. If
// the mode is LOCAL there are two envs, one used by the window service
// (LOCAL), and the other for non-window-service code. Windows created with
// LOCAL should use NativeWidgetAura (which happens if false is returned
// here).
if (from_window_service)
return false;
// TYPE_CONTROL and child widgets require a NativeWidgetAura.
return init_params.type != Widget::InitParams::TYPE_CONTROL &&
!init_params.child;
}
// static
bool MusClient::ShouldMakeWidgetWindowsTranslucent(
const Widget::InitParams& params) {
// |TYPE_WINDOW| and |TYPE_PANEL| are forced to translucent so that the
// window manager can draw the client decorations.
return params.opacity == Widget::InitParams::TRANSLUCENT_WINDOW ||
params.type == Widget::InitParams::TYPE_WINDOW ||
params.type == Widget::InitParams::TYPE_PANEL;
}
// static
std::map<std::string, std::vector<uint8_t>>
MusClient::ConfigurePropertiesFromParams(
const Widget::InitParams& init_params) {
using PrimitiveType = aura::PropertyConverter::PrimitiveType;
using WindowManager = ui::mojom::WindowManager;
using TransportType = std::vector<uint8_t>;
std::map<std::string, TransportType> properties = init_params.mus_properties;
// Widget::InitParams::Type matches ui::mojom::WindowType.
properties[WindowManager::kWindowType_InitProperty] =
mojo::ConvertTo<TransportType>(static_cast<int32_t>(init_params.type));
properties[WindowManager::kFocusable_InitProperty] =
mojo::ConvertTo<TransportType>(init_params.CanActivate());
properties[WindowManager::kTranslucent_InitProperty] =
mojo::ConvertTo<TransportType>(
ShouldMakeWidgetWindowsTranslucent(init_params));
if (!init_params.bounds.IsEmpty()) {
properties[WindowManager::kBounds_InitProperty] =
mojo::ConvertTo<TransportType>(init_params.bounds);
}
if (!init_params.name.empty()) {
properties[WindowManager::kName_Property] =
mojo::ConvertTo<TransportType>(init_params.name);
}
properties[WindowManager::kAlwaysOnTop_Property] =
mojo::ConvertTo<TransportType>(
static_cast<PrimitiveType>(init_params.keep_on_top));
properties[WindowManager::kRemoveStandardFrame_InitProperty] =
mojo::ConvertTo<TransportType>(init_params.remove_standard_frame);
if (init_params.corner_radius) {
properties[WindowManager::kWindowCornerRadius_Property] =
mojo::ConvertTo<TransportType>(
static_cast<PrimitiveType>(*init_params.corner_radius));
}
if (!Widget::RequiresNonClientView(init_params.type))
return properties;
if (init_params.delegate) {
if (properties.count(WindowManager::kResizeBehavior_Property) == 0) {
properties[WindowManager::kResizeBehavior_Property] =
mojo::ConvertTo<TransportType>(static_cast<PrimitiveType>(
init_params.delegate->GetResizeBehavior()));
}
if (init_params.delegate->ShouldShowWindowTitle()) {
properties[WindowManager::kWindowTitleShown_Property] =
mojo::ConvertTo<TransportType>(static_cast<PrimitiveType>(
init_params.delegate->ShouldShowWindowTitle()));
}
if (!init_params.delegate->GetWindowTitle().empty()) {
properties[WindowManager::kWindowTitle_Property] =
mojo::ConvertTo<TransportType>(
init_params.delegate->GetWindowTitle());
}
// TODO(crbug.com/667566): Support additional scales or gfx::Image[Skia].
gfx::ImageSkia app_icon = init_params.delegate->GetWindowAppIcon();
SkBitmap app_bitmap = app_icon.GetRepresentation(1.f).sk_bitmap();
if (!app_bitmap.isNull()) {
properties[WindowManager::kAppIcon_Property] =
mojo::ConvertTo<TransportType>(app_bitmap);
}
// TODO(crbug.com/667566): Support additional scales or gfx::Image[Skia].
gfx::ImageSkia window_icon = init_params.delegate->GetWindowIcon();
SkBitmap window_bitmap = window_icon.GetRepresentation(1.f).sk_bitmap();
if (!window_bitmap.isNull()) {
properties[WindowManager::kWindowIcon_Property] =
mojo::ConvertTo<TransportType>(window_bitmap);
}
}
return properties;
}
NativeWidget* MusClient::CreateNativeWidget(
const Widget::InitParams& init_params,
internal::NativeWidgetDelegate* delegate) {
if (!ShouldCreateDesktopNativeWidgetAura(init_params)) {
// A null return value results in creating NativeWidgetAura.
return nullptr;
}
DesktopNativeWidgetAura* native_widget =
new DesktopNativeWidgetAura(delegate);
if (init_params.desktop_window_tree_host) {
native_widget->SetDesktopWindowTreeHost(
base::WrapUnique(init_params.desktop_window_tree_host));
} else {
native_widget->SetDesktopWindowTreeHost(
CreateDesktopWindowTreeHost(init_params, delegate, native_widget));
}
return native_widget;
}
void MusClient::OnWidgetInitDone(Widget* widget) {
// Start tracking the widget for accessibility.
if (ax_remote_host_)
ax_remote_host_->StartMonitoringWidget(widget);
}
void MusClient::OnCaptureClientSet(
aura::client::CaptureClient* capture_client) {
pointer_watcher_event_router_->AttachToCaptureClient(capture_client);
window_tree_client_->capture_synchronizer()->AttachToCaptureClient(
capture_client);
}
void MusClient::OnCaptureClientUnset(
aura::client::CaptureClient* capture_client) {
pointer_watcher_event_router_->DetachFromCaptureClient(capture_client);
window_tree_client_->capture_synchronizer()->DetachFromCaptureClient(
capture_client);
}
void MusClient::AddObserver(MusClientObserver* observer) {
observer_list_.AddObserver(observer);
}
void MusClient::RemoveObserver(MusClientObserver* observer) {
observer_list_.RemoveObserver(observer);
}
void MusClient::SetMusPropertyMirror(
std::unique_ptr<MusPropertyMirror> mirror) {
mus_property_mirror_ = std::move(mirror);
}
void MusClient::CloseAllWidgets() {
for (aura::Window* root : window_tree_client_->GetRoots()) {
Widget* widget = Widget::GetWidgetForNativeView(root);
if (widget)
widget->CloseNow();
}
}
std::unique_ptr<DesktopWindowTreeHost> MusClient::CreateDesktopWindowTreeHost(
const Widget::InitParams& init_params,
internal::NativeWidgetDelegate* delegate,
DesktopNativeWidgetAura* desktop_native_widget_aura) {
std::map<std::string, std::vector<uint8_t>> mus_properties =
ConfigurePropertiesFromParams(init_params);
aura::WindowTreeHostMusInitParams window_tree_host_init_params =
aura::CreateInitParamsForTopLevel(MusClient::Get()->window_tree_client(),
std::move(mus_properties));
return std::make_unique<DesktopWindowTreeHostMus>(
std::move(window_tree_host_init_params), delegate,
desktop_native_widget_aura);
}
void MusClient::OnEmbed(
std::unique_ptr<aura::WindowTreeHostMus> window_tree_host) {
NOTREACHED();
}
void MusClient::OnLostConnection(aura::WindowTreeClient* client) {}
void MusClient::OnEmbedRootDestroyed(
aura::WindowTreeHostMus* window_tree_host) {
static_cast<DesktopWindowTreeHostMus*>(window_tree_host)
->ServerDestroyedWindow();
}
void MusClient::OnPointerEventObserved(const ui::PointerEvent& event,
int64_t display_id,
aura::Window* target) {
pointer_watcher_event_router_->OnPointerEventObserved(event, display_id,
target);
}
void MusClient::OnDisplaysChanged(
std::vector<ui::mojom::WsDisplayPtr> ws_displays,
int64_t primary_display_id,
int64_t internal_display_id,
int64_t display_id_for_new_windows) {
if (screen_) {
screen_->OnDisplaysChanged(std::move(ws_displays), primary_display_id,
internal_display_id, display_id_for_new_windows);
}
}
void MusClient::OnWindowManagerFrameValuesChanged() {
for (auto& observer : observer_list_)
observer.OnWindowManagerFrameValuesChanged();
}
aura::PropertyConverter* MusClient::GetPropertyConverter() {
return property_converter_.get();
}
aura::Window* MusClient::GetWindowAtScreenPoint(const gfx::Point& point) {
for (aura::Window* root : window_tree_client_->GetRoots()) {
aura::WindowTreeHost* window_tree_host = root->GetHost();
if (!window_tree_host)
continue;
// TODO: this likely gets z-order wrong. http://crbug.com/663606.
gfx::Point relative_point(point);
window_tree_host->ConvertScreenInPixelsToDIP(&relative_point);
if (gfx::Rect(root->bounds().size()).Contains(relative_point))
return root->GetEventHandlerForPoint(relative_point);
}
return nullptr;
}
} // namespace views