blob: b04cbd18a2b5c145464c414f2e96cfed923b9060 [file] [log] [blame]
// Copyright (c) 2012 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.h"
#include <stddef.h>
#include <algorithm>
#include <utility>
#include "base/auto_reset.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/containers/contains.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "cc/mojo_embedder/async_layer_tree_frame_sink.h"
#include "cc/trees/layer_tree_frame_sink.h"
#include "components/viz/common/features.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "components/viz/host/host_frame_sink_manager.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "third_party/skia/include/core/SkPath.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/capture_client.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/client/event_client.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/client/visibility_client.h"
#include "ui/aura/client/window_stacking_client.h"
#include "ui/aura/env.h"
#include "ui/aura/layout_manager.h"
#include "ui/aura/scoped_keyboard_hook.h"
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_observer.h"
#include "ui/aura/window_occlusion_tracker.h"
#include "ui/aura/window_targeter.h"
#include "ui/aura/window_tracker.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/layout.h"
#include "ui/base/metadata/base_type_conversion.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/ui_base_features.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/events/event_target_iterator.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/scoped_canvas.h"
DEFINE_ENUM_CONVERTERS(
aura::client::WindowType,
{aura::client::WINDOW_TYPE_UNKNOWN, u"WINDOW_TYPE_UNKNOWN"},
{aura::client::WINDOW_TYPE_NORMAL, u"WINDOW_TYPE_NORMAL"},
{aura::client::WINDOW_TYPE_POPUP, u"WINDOW_TYPE_POPUP"},
{aura::client::WINDOW_TYPE_CONTROL, u"WINDOW_TYPE_CONTROL"},
{aura::client::WINDOW_TYPE_MENU, u"WINDOW_TYPE_MENU"},
{aura::client::WINDOW_TYPE_TOOLTIP, u"WINDOW_TYPE_TOOLTIP"})
DEFINE_ENUM_CONVERTERS(aura::Window::OcclusionState,
{aura::Window::OcclusionState::UNKNOWN, u"UNKNOWN"},
{aura::Window::OcclusionState::VISIBLE, u"VISIBLE"},
{aura::Window::OcclusionState::OCCLUDED, u"OCCLUDED"},
{aura::Window::OcclusionState::HIDDEN, u"HIDDEN"})
namespace ui {
namespace metadata {
template <>
struct TypeConverter<viz::SurfaceId> : public BaseTypeConverter<true> {
static std::u16string ToString(const viz::SurfaceId& source_value);
static absl::optional<viz::SurfaceId> FromString(
const std::u16string& source_value);
static ValidStrings GetValidStrings();
};
// static
std::u16string TypeConverter<viz::SurfaceId>::ToString(
const viz::SurfaceId& source_value) {
return source_value.is_valid() ? base::UTF8ToUTF16(source_value.ToString())
: u"<none>";
}
// static
absl::optional<viz::SurfaceId> TypeConverter<viz::SurfaceId>::FromString(
const std::u16string& source_value) {
return absl::nullopt;
}
// static
ValidStrings TypeConverter<viz::SurfaceId>::GetValidStrings() {
return ValidStrings();
}
} // namespace metadata
} // namespace ui
namespace {
// This enum is used to construct a unique property changed callback key from
// the address of the `bounds_` field by using these values as an offset.
enum BoundsCallbackIndex : int {
kBoundsX,
kBoundsY,
kBoundsWidth,
kBoundsHeight,
};
} // namespace
namespace aura {
namespace {
static const char* kExo = "Exo";
class ScopedCursorHider {
public:
explicit ScopedCursorHider(Window* window)
: window_(window), hid_cursor_(false) {
if (!window_->IsRootWindow())
return;
const bool cursor_is_in_bounds = window_->GetBoundsInScreen().Contains(
Env::GetInstance()->last_mouse_location());
client::CursorClient* cursor_client = client::GetCursorClient(window_);
if (cursor_is_in_bounds && cursor_client &&
cursor_client->IsCursorVisible()) {
cursor_client->HideCursor();
hid_cursor_ = true;
}
}
~ScopedCursorHider() {
if (!window_->IsRootWindow())
return;
// Update the device scale factor of the cursor client only when the last
// mouse location is on this root window.
if (hid_cursor_) {
client::CursorClient* cursor_client = client::GetCursorClient(window_);
if (cursor_client) {
const display::Display& display =
display::Screen::GetScreen()->GetDisplayNearestWindow(window_);
cursor_client->SetDisplay(display);
cursor_client->ShowCursor();
}
}
}
private:
Window* window_;
bool hid_cursor_;
DISALLOW_COPY_AND_ASSIGN(ScopedCursorHider);
};
} // namespace
Window::Window(WindowDelegate* delegate, client::WindowType type)
: type_(type),
delegate_(delegate),
event_targeting_policy_(
aura::EventTargetingPolicy::kTargetAndDescendants),
restore_event_targeting_policy_(
aura::EventTargetingPolicy::kTargetAndDescendants),
// Don't notify newly added observers during notification. This causes
// problems for code that adds an observer as part of an observer
// notification (such as the workspace code).
observers_(base::ObserverListPolicy::EXISTING_ONLY) {
SetTargetHandler(delegate_);
}
Window::~Window() {
is_destroying_ = true;
WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;
if (layer()->owner() == this)
layer()->CompleteAllAnimations();
// Let the delegate know we're in the processing of destroying.
if (delegate_)
delegate_->OnWindowDestroying(this);
for (WindowObserver& observer : observers_)
observer.OnWindowDestroying(this);
// While we are being destroyed, our target handler may also be in the
// process of destruction or already destroyed, so do not forward any
// input events at the ui::EP_TARGET phase.
SetTargetHandler(nullptr);
// TODO(beng): See comment in window_event_dispatcher.h. This shouldn't be
// necessary but unfortunately is right now due to ordering
// peculiarities. WED must be notified _after_ other observers
// are notified of pending teardown but before the hierarchy
// is actually torn down.
WindowTreeHost* host = GetHost();
if (host)
host->dispatcher()->OnPostNotifiedWindowDestroying(this);
// The window should have already had its state cleaned up in
// WindowEventDispatcher::OnWindowHidden(), but there have been some crashes
// involving windows being destroyed without being hidden first. See
// crbug.com/342040. This should help us debug the issue. TODO(tdresser):
// remove this once we determine why we have windows that are destroyed
// without being hidden.
bool window_incorrectly_cleaned_up = CleanupGestureState();
CHECK(!window_incorrectly_cleaned_up);
// Then destroy the children.
RemoveOrDestroyChildren();
// The window needs to be removed from the parent before calling the
// WindowDestroyed callbacks of delegate and the observers.
if (parent_)
parent_->RemoveChild(this);
if (delegate_)
delegate_->OnWindowDestroyed(this);
for (WindowObserver& observer : observers_) {
RemoveObserver(&observer);
observer.OnWindowDestroyed(this);
}
// Delete the LayoutManager before properties. This way if the LayoutManager
// depends upon properties existing the properties are still valid.
layout_manager_.reset();
ClearProperties();
// The layer will either be destroyed by |layer_owner_|'s dtor, or by whoever
// acquired it.
layer()->set_delegate(nullptr);
DestroyLayer();
// If SetEmbedFrameSinkId() was called by client code, then we assume client
// code is taking care of invalidating.
if (frame_sink_id_.is_valid() && !embeds_external_client_) {
auto* context_factory = Env::GetInstance()->context_factory();
auto* host_frame_sink_manager = context_factory->GetHostFrameSinkManager();
host_frame_sink_manager->InvalidateFrameSinkId(frame_sink_id_);
}
}
void Window::Init(ui::LayerType layer_type) {
WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;
SetLayer(std::make_unique<ui::Layer>(layer_type));
layer()->SetVisible(false);
layer()->set_delegate(this);
UpdateLayerName();
layer()->SetFillsBoundsOpaquely(!transparent_);
Env::GetInstance()->NotifyWindowInitialized(this);
}
int Window::GetId() const {
return id_;
}
void Window::SetId(int id) {
if (id == id_)
return;
id_ = id;
TriggerChangedCallback(&id_);
}
client::WindowType Window::GetType() const {
return type_;
}
void Window::SetType(client::WindowType type) {
// Cannot change type after the window is initialized.
DCHECK(!layer());
if (type == type_)
return;
type_ = type;
TriggerChangedCallback(&type_);
}
const std::string& Window::GetName() const {
std::string* name = GetProperty(client::kNameKey);
return name ? *name : base::EmptyString();
}
void Window::SetName(const std::string& name) {
if (name == GetName())
return;
SetProperty(client::kNameKey, name);
if (layer())
UpdateLayerName();
TriggerChangedCallback(client::kNameKey);
}
const std::u16string& Window::GetTitle() const {
std::u16string* title = GetProperty(client::kTitleKey);
return title ? *title : base::EmptyString16();
}
void Window::SetTitle(const std::u16string& title) {
if (title == GetTitle())
return;
SetProperty(client::kTitleKey, title);
for (WindowObserver& observer : observers_)
observer.OnWindowTitleChanged(this);
}
bool Window::GetTransparent() const {
return transparent_;
}
void Window::SetTransparent(bool transparent) {
if (transparent == transparent_)
return;
transparent_ = transparent;
if (layer())
layer()->SetFillsBoundsOpaquely(!transparent_);
TriggerChangedCallback(&transparent_);
}
void Window::SetFillsBoundsCompletely(bool fills_bounds) {
layer()->SetFillsBoundsCompletely(fills_bounds);
}
Window* Window::GetRootWindow() {
return const_cast<Window*>(
static_cast<const Window*>(this)->GetRootWindow());
}
const Window* Window::GetRootWindow() const {
return IsRootWindow() ? this : parent_ ? parent_->GetRootWindow() : nullptr;
}
WindowTreeHost* Window::GetHost() {
return const_cast<WindowTreeHost*>(const_cast<const Window*>(this)->
GetHost());
}
const WindowTreeHost* Window::GetHost() const {
const Window* root_window = GetRootWindow();
return root_window ? root_window->host_ : nullptr;
}
void Window::Show() {
DCHECK_EQ(visible_, layer()->GetTargetVisibility());
// It is not allowed that a window is visible but the layers alpha is fully
// transparent since the window would still be considered to be active but
// could not be seen.
DCHECK(!visible_ || layer()->GetTargetOpacity() > 0.0f);
SetVisibleInternal(true);
}
void Window::Hide() {
// RootWindow::OnVisibilityChanged will call ReleaseCapture.
SetVisibleInternal(false);
}
bool Window::IsVisible() const {
// Layer visibility can be inconsistent with window visibility, for example
// when a Window is hidden, we want this function to return false immediately
// after, even though the client may decide to animate the hide effect (and
// so the layer will be visible for some time after Hide() is called).
return visible_ ? layer()->IsDrawn() : false;
}
Window::OcclusionState Window::GetOcclusionState() const {
return occlusion_state_;
}
ScopedWindowCaptureRequest Window::MakeWindowCapturable() {
DCHECK(!IsRootWindow()) << "Root windows can already be captured using their "
"FrameSinkId; no need to call this.";
return ScopedWindowCaptureRequest(this);
}
gfx::Rect Window::GetBoundsInRootWindow() const {
if (!GetRootWindow())
return bounds();
gfx::Rect bounds_in_root(bounds().size());
ConvertRectToTarget(this, GetRootWindow(), &bounds_in_root);
return bounds_in_root;
}
gfx::Rect Window::GetActualBoundsInRootWindow() const {
if (!GetRootWindow())
return bounds();
gfx::Rect bounds_in_root(bounds().size());
gfx::PointF origin_f = gfx::PointF(bounds_in_root.origin());
ui::Layer::ConvertPointToLayer(layer(), GetRootWindow()->layer(),
/*use_target_transform=*/false, &origin_f);
bounds_in_root.set_origin(gfx::ToFlooredPoint(origin_f));
return bounds_in_root;
}
const gfx::Transform& Window::transform() const {
return layer()->transform();
}
gfx::Rect Window::GetBoundsInScreen() const {
gfx::Rect bounds(GetBoundsInRootWindow());
const Window* root = GetRootWindow();
if (root) {
aura::client::ScreenPositionClient* screen_position_client =
aura::client::GetScreenPositionClient(root);
if (screen_position_client) {
gfx::Point origin = bounds.origin();
screen_position_client->ConvertPointToScreen(root, &origin);
bounds.set_origin(origin);
}
}
return bounds;
}
gfx::Rect Window::GetActualBoundsInScreen() const {
gfx::Rect bounds(GetActualBoundsInRootWindow());
const Window* root = GetRootWindow();
if (root) {
gfx::Point origin_in_screen = root->GetBoundsInScreen().origin();
origin_in_screen += bounds.OffsetFromOrigin();
bounds.set_origin(origin_in_screen);
}
return bounds;
}
void Window::SetTransform(const gfx::Transform& transform) {
WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;
for (WindowObserver& observer : observers_)
observer.OnWindowTargetTransformChanging(this, transform);
layer()->SetTransform(transform);
}
void Window::SetLayoutManager(LayoutManager* layout_manager) {
if (layout_manager == layout_manager_.get())
return;
layout_manager_.reset(layout_manager);
if (!layout_manager)
return;
// If we're changing to a new layout manager, ensure it is aware of all the
// existing child windows.
for (Windows::const_iterator it = children_.begin();
it != children_.end();
++it)
layout_manager_->OnWindowAddedToLayout(*it);
}
std::unique_ptr<WindowTargeter> Window::SetEventTargeter(
std::unique_ptr<WindowTargeter> targeter) {
std::unique_ptr<WindowTargeter> old_targeter = std::move(targeter_);
if (old_targeter)
old_targeter->OnInstalled(nullptr);
targeter_ = std::move(targeter);
if (targeter_)
targeter_->OnInstalled(this);
return old_targeter;
}
void Window::SetBounds(const gfx::Rect& new_bounds) {
if (parent_ && parent_->layout_manager()) {
parent_->layout_manager()->SetChildBounds(this, new_bounds);
} else {
// Ensure we don't go smaller than our minimum bounds.
gfx::Rect final_bounds(new_bounds);
if (delegate_) {
const gfx::Size& min_size = delegate_->GetMinimumSize();
final_bounds.set_width(std::max(min_size.width(), final_bounds.width()));
final_bounds.set_height(std::max(min_size.height(),
final_bounds.height()));
}
SetBoundsInternal(final_bounds);
}
}
void Window::SetBoundsInScreen(const gfx::Rect& new_bounds_in_screen,
const display::Display& dst_display) {
aura::client::ScreenPositionClient* screen_position_client = nullptr;
Window* root = GetRootWindow();
if (root)
screen_position_client = aura::client::GetScreenPositionClient(root);
if (screen_position_client)
screen_position_client->SetBounds(this, new_bounds_in_screen, dst_display);
else
SetBounds(new_bounds_in_screen);
}
gfx::Rect Window::GetTargetBounds() const {
return layer() ? layer()->GetTargetBounds() : bounds();
}
void Window::ScheduleDraw() {
layer()->ScheduleDraw();
}
void Window::SchedulePaintInRect(const gfx::Rect& rect) {
layer()->SchedulePaint(rect);
}
void Window::StackChildAtTop(Window* child) {
if (children_.size() <= 1 || child == children_.back())
return; // In the front already.
StackChildAbove(child, children_.back());
}
void Window::StackChildAbove(Window* child, Window* target) {
StackChildRelativeTo(child, target, STACK_ABOVE);
}
void Window::StackChildAtBottom(Window* child) {
if (children_.size() <= 1 || child == children_.front())
return; // At the bottom already.
StackChildBelow(child, children_.front());
}
void Window::StackChildBelow(Window* child, Window* target) {
StackChildRelativeTo(child, target, STACK_BELOW);
}
void Window::AddChild(Window* child) {
WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;
DCHECK(layer()) << "Parent has not been Init()ed yet.";
DCHECK(child->layer()) << "Child has not been Init()ed yt.";
WindowObserver::HierarchyChangeParams params;
params.target = child;
params.new_parent = this;
params.old_parent = child->parent();
params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING;
NotifyWindowHierarchyChange(params);
Window* old_root = child->GetRootWindow();
DCHECK(!base::Contains(children_, child));
if (child->parent())
child->parent()->RemoveChildImpl(child, this);
child->parent_ = this;
layer()->Add(child->layer());
children_.push_back(child);
if (layout_manager_)
layout_manager_->OnWindowAddedToLayout(child);
for (WindowObserver& observer : observers_)
observer.OnWindowAdded(child);
child->OnParentChanged();
Window* root_window = GetRootWindow();
if (root_window && old_root != root_window) {
root_window->GetHost()->dispatcher()->OnWindowAddedToRootWindow(child);
child->NotifyAddedToRootWindow();
}
params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED;
NotifyWindowHierarchyChange(params);
}
void Window::RemoveChild(Window* child) {
WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;
WindowObserver::HierarchyChangeParams params;
params.target = child;
params.new_parent = nullptr;
params.old_parent = this;
params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING;
NotifyWindowHierarchyChange(params);
RemoveChildImpl(child, nullptr);
params.phase = WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED;
NotifyWindowHierarchyChange(params);
}
bool Window::Contains(const Window* other) const {
for (const Window* parent = other; parent; parent = parent->parent_) {
if (parent == this)
return true;
}
return false;
}
Window* Window::GetChildById(int id) {
return const_cast<Window*>(const_cast<const Window*>(this)->GetChildById(id));
}
const Window* Window::GetChildById(int id) const {
Windows::const_iterator i;
for (i = children_.begin(); i != children_.end(); ++i) {
if ((*i)->GetId() == id)
return *i;
const Window* result = (*i)->GetChildById(id);
if (result)
return result;
}
return nullptr;
}
// static
void Window::ConvertPointToTarget(const Window* source,
const Window* target,
gfx::PointF* point) {
if (!source)
return;
if (source->GetRootWindow() != target->GetRootWindow()) {
client::ScreenPositionClient* source_client =
client::GetScreenPositionClient(source->GetRootWindow());
// |source_client| can be nullptr in tests.
if (source_client)
source_client->ConvertPointToScreen(source, point);
client::ScreenPositionClient* target_client =
client::GetScreenPositionClient(target->GetRootWindow());
// |target_client| can be nullptr in tests.
if (target_client)
target_client->ConvertPointFromScreen(target, point);
} else {
ui::Layer::ConvertPointToLayer(source->layer(), target->layer(),
/*use_target_transform=*/true, point);
}
}
// static
void Window::ConvertPointToTarget(const Window* source,
const Window* target,
gfx::Point* point) {
gfx::PointF point_float(*point);
ConvertPointToTarget(source, target, &point_float);
*point = gfx::ToFlooredPoint(point_float);
}
// static
void Window::ConvertRectToTarget(const Window* source,
const Window* target,
gfx::Rect* rect) {
DCHECK(rect);
gfx::Point origin = rect->origin();
ConvertPointToTarget(source, target, &origin);
rect->set_origin(origin);
}
// static
void Window::ConvertNativePointToTargetHost(const Window* source,
const Window* target,
gfx::PointF* point) {
if (!source || !target)
return;
if (source->GetHost() == target->GetHost())
return;
point->Offset(-target->GetHost()->GetBoundsInPixels().x(),
-target->GetHost()->GetBoundsInPixels().y());
}
// static
void Window::ConvertNativePointToTargetHost(const Window* source,
const Window* target,
gfx::Point* point) {
gfx::PointF point_float(*point);
ConvertNativePointToTargetHost(source, target, &point_float);
*point = gfx::ToFlooredPoint(point_float);
}
void Window::MoveCursorTo(const gfx::Point& point_in_window) {
Window* root_window = GetRootWindow();
DCHECK(root_window);
gfx::Point point_in_root(point_in_window);
ConvertPointToTarget(this, root_window, &point_in_root);
root_window->GetHost()->MoveCursorToLocationInDIP(point_in_root);
}
gfx::NativeCursor Window::GetCursor(const gfx::Point& point) const {
return delegate_ ? delegate_->GetCursor(point) : gfx::kNullCursor;
}
void Window::AddObserver(WindowObserver* observer) {
observers_.AddObserver(observer);
}
void Window::RemoveObserver(WindowObserver* observer) {
observers_.RemoveObserver(observer);
}
bool Window::HasObserver(const WindowObserver* observer) const {
return observers_.HasObserver(observer);
}
void Window::SetEventTargetingPolicy(EventTargetingPolicy policy) {
// If the event targeting is blocked on the window, do not allow change event
// targeting policy until all event targeting blockers are removed from the
// window.
if (event_targeting_blocker_count_ > 0) {
restore_event_targeting_policy_ = policy;
return;
}
#if DCHECK_IS_ON()
const bool old_window_accepts_events =
(event_targeting_policy_ == EventTargetingPolicy::kTargetOnly) ||
(event_targeting_policy_ == EventTargetingPolicy::kTargetAndDescendants);
const bool new_window_accepts_events =
(policy == EventTargetingPolicy::kTargetOnly) ||
(policy == EventTargetingPolicy::kTargetAndDescendants);
if (new_window_accepts_events != old_window_accepts_events)
DCHECK(!created_layer_tree_frame_sink_);
#endif
if (event_targeting_policy_ == policy)
return;
event_targeting_policy_ = policy;
layer()->SetAcceptEvents(policy != EventTargetingPolicy::kNone);
}
bool Window::ContainsPointInRoot(const gfx::Point& point_in_root) const {
const Window* root_window = GetRootWindow();
if (!root_window)
return false;
gfx::Point local_point(point_in_root);
ConvertPointToTarget(root_window, this, &local_point);
return gfx::Rect(GetTargetBounds().size()).Contains(local_point);
}
bool Window::ContainsPoint(const gfx::Point& local_point) const {
return gfx::Rect(bounds().size()).Contains(local_point);
}
Window* Window::GetEventHandlerForPoint(const gfx::Point& local_point) {
if (!IsVisible())
return nullptr;
if (!HitTest(local_point))
return nullptr;
for (Windows::const_reverse_iterator it = children_.rbegin(),
rend = children_.rend();
it != rend; ++it) {
Window* child = *it;
if (child->event_targeting_policy_ == EventTargetingPolicy::kNone) {
continue;
}
// The client may not allow events to be processed by certain subtrees.
client::EventClient* client = client::GetEventClient(GetRootWindow());
if (client && !client->GetCanProcessEventsWithinSubtree(child))
continue;
if (delegate_ && !delegate_->ShouldDescendIntoChildForEventHandling(
child, local_point)) {
continue;
}
gfx::Point point_in_child_coords(local_point);
ConvertPointToTarget(this, child, &point_in_child_coords);
Window* match = child->GetEventHandlerForPoint(point_in_child_coords);
if (!match)
continue;
switch (child->event_targeting_policy_) {
case EventTargetingPolicy::kTargetOnly:
if (child->delegate_)
return child;
break;
case EventTargetingPolicy::kTargetAndDescendants:
return match;
case EventTargetingPolicy::kDescendantsOnly:
if (match != child)
return match;
break;
case EventTargetingPolicy::kNone:
NOTREACHED(); // This case is handled early on.
}
}
return delegate_ ? this : nullptr;
}
Window* Window::GetToplevelWindow() {
Window* topmost_window_with_delegate = nullptr;
for (aura::Window* window = this; window != nullptr;
window = window->parent()) {
if (window->delegate())
topmost_window_with_delegate = window;
}
return topmost_window_with_delegate;
}
void Window::Focus() {
client::FocusClient* client = client::GetFocusClient(this);
DCHECK(client);
client->FocusWindow(this);
}
bool Window::HasFocus() const {
client::FocusClient* client = client::GetFocusClient(this);
return client && client->GetFocusedWindow() == this;
}
bool Window::CanFocus() const {
if (IsRootWindow())
return IsVisible();
// NOTE: as part of focusing the window the ActivationClient may make the
// window visible (by way of making a hidden ancestor visible). For this
// reason we can't check visibility here and assume the client is doing it.
if (!parent_ || (delegate_ && !delegate_->CanFocus()))
return false;
// The client may forbid certain windows from receiving focus at a given point
// in time.
client::EventClient* client = client::GetEventClient(GetRootWindow());
if (client && !client->GetCanProcessEventsWithinSubtree(this))
return false;
return parent_->CanFocus();
}
void Window::SetCapture() {
if (!IsVisible())
return;
Window* root_window = GetRootWindow();
if (!root_window)
return;
client::CaptureClient* capture_client = client::GetCaptureClient(root_window);
if (!capture_client)
return;
capture_client->SetCapture(this);
}
void Window::ReleaseCapture() {
Window* root_window = GetRootWindow();
if (!root_window)
return;
client::CaptureClient* capture_client = client::GetCaptureClient(root_window);
if (!capture_client)
return;
capture_client->ReleaseCapture(this);
}
bool Window::HasCapture() {
Window* root_window = GetRootWindow();
if (!root_window)
return false;
client::CaptureClient* capture_client = client::GetCaptureClient(root_window);
return capture_client && capture_client->GetCaptureWindow() == this;
}
std::unique_ptr<ScopedKeyboardHook> Window::CaptureSystemKeyEvents(
absl::optional<base::flat_set<ui::DomCode>> dom_codes) {
Window* root_window = GetRootWindow();
if (!root_window)
return nullptr;
WindowTreeHost* host = root_window->GetHost();
if (!host)
return nullptr;
return host->CaptureSystemKeyEvents(std::move(dom_codes));
}
// {Set,Get,Clear}Property are implemented in class_property.h.
void Window::SetNativeWindowProperty(const char* key, void* value) {
SetPropertyInternal(key, key, nullptr, reinterpret_cast<int64_t>(value), 0);
}
void* Window::GetNativeWindowProperty(const char* key) const {
return reinterpret_cast<void*>(GetPropertyInternal(key, 0,
/*search_parent=*/false));
}
void Window::OnDeviceScaleFactorChanged(float old_device_scale_factor,
float new_device_scale_factor) {
if (!IsRootWindow() && last_device_scale_factor_ != new_device_scale_factor &&
IsEmbeddingExternalContent()) {
last_device_scale_factor_ = new_device_scale_factor;
parent_local_surface_id_allocator_->GenerateId();
if (frame_sink_)
frame_sink_->SetLocalSurfaceId(GetCurrentLocalSurfaceId());
}
ScopedCursorHider hider(this);
if (delegate_) {
delegate_->OnDeviceScaleFactorChanged(old_device_scale_factor,
new_device_scale_factor);
}
}
void Window::UpdateVisualState() {
if (delegate_)
delegate_->UpdateVisualState();
}
#if DCHECK_IS_ON()
std::string Window::GetDebugInfo() const {
std::string name = GetName();
if (name.empty())
name = "Unknown";
std::string layer_state = "NoLayer";
if (layer()) {
layer_state = base::StringPrintf(
"%s opacity=%.1f",
layer()->GetTargetVisibility() ? "LayerVisible" : "LayerHidden",
layer()->opacity());
}
return base::StringPrintf(
"%s<%d> bounds=%s %s %s occlusion_state=%s", name.c_str(), GetId(),
bounds().ToString().c_str(), visible_ ? "WindowVisible" : "WindowHidden",
layer_state.c_str(),
base::UTF16ToUTF8(OcclusionStateToString(occlusion_state_)).c_str());
}
std::string Window::GetWindowHierarchy(int depth) const {
std::string hierarchy =
base::StringPrintf("%*s%s\n", depth * 2, "", GetDebugInfo().c_str());
for (Window* child : children_)
hierarchy += child->GetWindowHierarchy(depth + 1);
return hierarchy;
}
void Window::PrintWindowHierarchy(int depth) const {
VLOG(0) << GetWindowHierarchy(depth);
}
#endif
void Window::RemoveOrDestroyChildren() {
while (!children_.empty()) {
Window* child = children_[0];
if (child->owned_by_parent_) {
delete child;
// Deleting the child so remove it from out children_ list.
DCHECK(!base::Contains(children_, child));
} else {
// Even if we can't delete the child, we still need to remove it from the
// parent so that relevant bookkeeping (parent_ back-pointers etc) are
// updated.
RemoveChild(child);
}
}
}
void Window::AfterPropertyChange(const void* key, int64_t old_value) {
for (WindowObserver& observer : observers_)
observer.OnWindowPropertyChanged(this, key, old_value);
}
///////////////////////////////////////////////////////////////////////////////
// Window, private:
void Window::SetEmbedFrameSinkIdImpl(const viz::FrameSinkId& frame_sink_id) {
UnregisterFrameSinkId();
DCHECK(frame_sink_id.is_valid());
frame_sink_id_ = frame_sink_id;
RegisterFrameSinkId();
}
bool Window::HitTest(const gfx::Point& local_point) {
gfx::Rect local_bounds(bounds().size());
if (!delegate_ || !delegate_->HasHitTestMask())
return local_bounds.Contains(local_point);
SkPath mask;
delegate_->GetHitTestMask(&mask);
SkRegion clip_region;
clip_region.setRect({local_bounds.x(), local_bounds.y(), local_bounds.width(),
local_bounds.height()});
SkRegion mask_region;
return mask_region.setPath(mask, clip_region) &&
mask_region.contains(local_point.x(), local_point.y());
}
void Window::SetBoundsInternal(const gfx::Rect& new_bounds) {
gfx::Rect old_bounds = GetTargetBounds();
// Always need to set the layer's bounds -- even if it is to the same thing.
// This may cause important side effects such as stopping animation.
layer()->SetBounds(new_bounds);
// If we are currently not the layer's delegate, we will not get bounds
// changed notification from the layer (this typically happens after animating
// hidden). We must notify ourselves.
if (layer()->delegate() != this) {
OnLayerBoundsChanged(old_bounds,
ui::PropertyChangeReason::NOT_FROM_ANIMATION);
}
}
void Window::SetVisibleInternal(bool visible) {
if (visible == layer()->GetTargetVisibility())
return; // No change.
WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;
for (WindowObserver& observer : observers_)
observer.OnWindowVisibilityChanging(this, visible);
client::VisibilityClient* visibility_client =
client::GetVisibilityClient(this);
if (visibility_client)
visibility_client->UpdateLayerVisibility(this, visible);
else
layer()->SetVisible(visible);
visible_ = visible;
SchedulePaint();
if (parent_ && parent_->layout_manager_)
parent_->layout_manager_->OnChildWindowVisibilityChanged(this, visible);
if (delegate_)
delegate_->OnWindowTargetVisibilityChanged(visible);
NotifyWindowVisibilityChanged(this, visible);
}
void Window::SetOcclusionInfo(OcclusionState occlusion_state,
const SkRegion& occluded_region) {
if (occlusion_state == occlusion_state_ &&
occluded_region_in_root_ == occluded_region) {
return;
}
occlusion_state_ = occlusion_state;
occluded_region_in_root_ = occluded_region;
if (delegate_)
delegate_->OnWindowOcclusionChanged(occlusion_state);
for (WindowObserver& observer : observers_)
observer.OnWindowOcclusionChanged(this);
}
void Window::SchedulePaint() {
SchedulePaintInRect(gfx::Rect(0, 0, bounds().width(), bounds().height()));
}
void Window::Paint(const ui::PaintContext& context) {
if (delegate_)
delegate_->OnPaint(context);
}
void Window::RemoveChildImpl(Window* child, Window* new_parent) {
if (layout_manager_)
layout_manager_->OnWillRemoveWindowFromLayout(child);
for (WindowObserver& observer : observers_)
observer.OnWillRemoveWindow(child);
Window* root_window = child->GetRootWindow();
Window* new_root_window = new_parent ? new_parent->GetRootWindow() : nullptr;
if (root_window && root_window != new_root_window)
child->NotifyRemovingFromRootWindow(new_root_window);
if (child->OwnsLayer())
layer()->Remove(child->layer());
child->parent_ = nullptr;
auto i = std::find(children_.begin(), children_.end(), child);
DCHECK(i != children_.end());
children_.erase(i);
child->OnParentChanged();
if (layout_manager_)
layout_manager_->OnWindowRemovedFromLayout(child);
for (WindowObserver& observer : observers_)
observer.OnWindowRemoved(child);
}
void Window::OnParentChanged() {
for (WindowObserver& observer : observers_)
observer.OnWindowParentChanged(this, parent_);
}
void Window::StackChildRelativeTo(Window* child,
Window* target,
StackDirection direction) {
DCHECK_NE(child, target);
DCHECK(child);
DCHECK(target);
DCHECK_EQ(this, child->parent());
DCHECK_EQ(this, target->parent());
WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;
client::WindowStackingClient* stacking_client =
client::GetWindowStackingClient();
if (stacking_client &&
!stacking_client->AdjustStacking(&child, &target, &direction))
return;
const size_t child_i =
std::find(children_.begin(), children_.end(), child) - children_.begin();
const size_t target_i =
std::find(children_.begin(), children_.end(), target) - children_.begin();
DCHECK_LT(child_i, children_.size()) << "Child was not in list of children!";
DCHECK_LT(target_i, children_.size())
<< "Target was not in list of children!";
// Don't move the child if it is already in the right place.
if ((direction == STACK_ABOVE && child_i == target_i + 1) ||
(direction == STACK_BELOW && child_i + 1 == target_i))
return;
const size_t dest_i =
direction == STACK_ABOVE ?
(child_i < target_i ? target_i : target_i + 1) :
(child_i < target_i ? target_i - 1 : target_i);
children_.erase(children_.begin() + child_i);
children_.insert(children_.begin() + dest_i, child);
StackChildLayerRelativeTo(child, target, direction);
child->OnStackingChanged();
}
void Window::StackChildLayerRelativeTo(Window* child,
Window* target,
StackDirection direction) {
DCHECK(layer() && child->layer() && target->layer());
if (direction == STACK_ABOVE)
layer()->StackAbove(child->layer(), target->layer());
else
layer()->StackBelow(child->layer(), target->layer());
}
void Window::OnStackingChanged() {
for (WindowObserver& observer : observers_)
observer.OnWindowStackingChanged(this);
}
void Window::NotifyRemovingFromRootWindow(Window* new_root) {
if (frame_sink_id_.is_valid())
UnregisterFrameSinkId();
for (WindowObserver& observer : observers_)
observer.OnWindowRemovingFromRootWindow(this, new_root);
for (Window::Windows::const_iterator it = children_.begin();
it != children_.end(); ++it) {
(*it)->NotifyRemovingFromRootWindow(new_root);
}
}
void Window::NotifyAddedToRootWindow() {
if (frame_sink_id_.is_valid())
RegisterFrameSinkId();
for (WindowObserver& observer : observers_)
observer.OnWindowAddedToRootWindow(this);
for (Window::Windows::const_iterator it = children_.begin();
it != children_.end(); ++it) {
(*it)->NotifyAddedToRootWindow();
}
}
void Window::NotifyWindowHierarchyChange(
const WindowObserver::HierarchyChangeParams& params) {
params.target->NotifyWindowHierarchyChangeDown(params);
switch (params.phase) {
case WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING:
if (params.old_parent)
params.old_parent->NotifyWindowHierarchyChangeUp(params);
break;
case WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED:
if (params.new_parent)
params.new_parent->NotifyWindowHierarchyChangeUp(params);
break;
}
}
void Window::NotifyWindowHierarchyChangeDown(
const WindowObserver::HierarchyChangeParams& params) {
NotifyWindowHierarchyChangeAtReceiver(params);
for (Window::Windows::const_iterator it = children_.begin();
it != children_.end(); ++it) {
(*it)->NotifyWindowHierarchyChangeDown(params);
}
}
void Window::NotifyWindowHierarchyChangeUp(
const WindowObserver::HierarchyChangeParams& params) {
for (Window* window = this; window; window = window->parent())
window->NotifyWindowHierarchyChangeAtReceiver(params);
}
void Window::NotifyWindowHierarchyChangeAtReceiver(
const WindowObserver::HierarchyChangeParams& params) {
WindowObserver::HierarchyChangeParams local_params = params;
local_params.receiver = this;
switch (params.phase) {
case WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGING:
for (WindowObserver& observer : observers_)
observer.OnWindowHierarchyChanging(local_params);
break;
case WindowObserver::HierarchyChangeParams::HIERARCHY_CHANGED:
for (WindowObserver& observer : observers_)
observer.OnWindowHierarchyChanged(local_params);
break;
}
}
void Window::NotifyWindowVisibilityChanged(aura::Window* target,
bool visible) {
if (!NotifyWindowVisibilityChangedDown(target, visible))
return; // |this| has been deleted.
NotifyWindowVisibilityChangedUp(target, visible);
}
bool Window::NotifyWindowVisibilityChangedAtReceiver(aura::Window* target,
bool visible) {
// |this| may be deleted during a call to OnWindowVisibilityChanged() on one
// of the observers. We create an local observer for that. In that case we
// exit without further access to any members.
WindowTracker tracker;
tracker.Add(this);
for (WindowObserver& observer : observers_)
observer.OnWindowVisibilityChanged(target, visible);
return tracker.Contains(this);
}
bool Window::NotifyWindowVisibilityChangedDown(aura::Window* target,
bool visible) {
if (!NotifyWindowVisibilityChangedAtReceiver(target, visible))
return false; // |this| was deleted.
WindowTracker this_tracker;
this_tracker.Add(this);
// Copy |children_| in case iterating mutates |children_|, or destroys an
// existing child.
WindowTracker children(children_);
while (!this_tracker.windows().empty() && !children.windows().empty())
children.Pop()->NotifyWindowVisibilityChangedDown(target, visible);
const bool this_still_valid = !this_tracker.windows().empty();
return this_still_valid;
}
void Window::NotifyWindowVisibilityChangedUp(aura::Window* target,
bool visible) {
// Start with the parent as we already notified |this|
// in NotifyWindowVisibilityChangedDown.
for (Window* window = parent(); window; window = window->parent()) {
bool ret = window->NotifyWindowVisibilityChangedAtReceiver(target, visible);
DCHECK(ret);
}
}
bool Window::CleanupGestureState() {
// If it's in the process already, nothing has to be done. Reentrant can
// happen through some event handlers for CancelActiveTouches().
if (cleaning_up_gesture_state_)
return false;
base::AutoReset<bool> in_cleanup(&cleaning_up_gesture_state_, true);
bool state_modified = false;
Env* env = Env::GetInstance();
state_modified |= env->gesture_recognizer()->CancelActiveTouches(this);
state_modified |= env->gesture_recognizer()->CleanupStateForConsumer(this);
// Potentially event handlers for CancelActiveTouches() within
// CleanupGestureState may change the window hierarchy (or reorder the
// |children_|), and therefore iterating over |children_| is not safe. Use
// WindowTracker to track the list of children.
WindowTracker children(children_);
while (!children.windows().empty()) {
Window* child = children.Pop();
state_modified |= child->CleanupGestureState();
}
return state_modified;
}
std::unique_ptr<cc::LayerTreeFrameSink> Window::CreateLayerTreeFrameSink() {
// Currently we don't have a need for both SetEmbedFrameSinkId() and
// this function be called.
DCHECK(!embeds_external_client_);
auto* context_factory = Env::GetInstance()->context_factory();
auto* host_frame_sink_manager = context_factory->GetHostFrameSinkManager();
if (!frame_sink_id_.is_valid()) {
auto frame_sink_id = context_factory->AllocateFrameSinkId();
host_frame_sink_manager->RegisterFrameSinkId(
frame_sink_id, this, viz::ReportFirstSurfaceActivation::kYes);
SetEmbedFrameSinkIdImpl(frame_sink_id);
}
// For creating a async frame sink which connects to the viz display
// compositor.
mojo::PendingRemote<viz::mojom::CompositorFrameSink> sink_remote;
mojo::PendingReceiver<viz::mojom::CompositorFrameSink> sink_receiver =
sink_remote.InitWithNewPipeAndPassReceiver();
mojo::PendingRemote<viz::mojom::CompositorFrameSinkClient> client_remote;
mojo::PendingReceiver<viz::mojom::CompositorFrameSinkClient> client_receiver =
client_remote.InitWithNewPipeAndPassReceiver();
host_frame_sink_manager->CreateCompositorFrameSink(
frame_sink_id_, std::move(sink_receiver), std::move(client_remote));
cc::mojo_embedder::AsyncLayerTreeFrameSink::InitParams params;
params.gpu_memory_buffer_manager =
Env::GetInstance()->context_factory()->GetGpuMemoryBufferManager();
params.pipes.compositor_frame_sink_remote = std::move(sink_remote);
params.pipes.client_receiver = std::move(client_receiver);
params.client_name = kExo;
auto frame_sink =
std::make_unique<cc::mojo_embedder::AsyncLayerTreeFrameSink>(
nullptr /* context_provider */, nullptr /* worker_context_provider */,
&params);
frame_sink_ = frame_sink->GetWeakPtr();
AllocateLocalSurfaceId();
DCHECK(GetLocalSurfaceId().is_valid());
#if DCHECK_IS_ON()
created_layer_tree_frame_sink_ = true;
#endif
return std::move(frame_sink);
}
viz::SurfaceId Window::GetSurfaceId() {
return viz::SurfaceId(GetFrameSinkId(), GetLocalSurfaceId());
}
viz::SurfaceId Window::GetSurfaceId() const {
return const_cast<Window*>(this)->GetSurfaceId();
}
void Window::AllocateLocalSurfaceId() {
if (!parent_local_surface_id_allocator_) {
parent_local_surface_id_allocator_ =
std::make_unique<viz::ParentLocalSurfaceIdAllocator>();
}
parent_local_surface_id_allocator_->GenerateId();
UpdateLocalSurfaceId();
}
viz::ScopedSurfaceIdAllocator Window::GetSurfaceIdAllocator(
base::OnceCallback<void()> allocation_task) {
return viz::ScopedSurfaceIdAllocator(parent_local_surface_id_allocator_.get(),
std::move(allocation_task));
}
const viz::LocalSurfaceId& Window::GetLocalSurfaceId() {
if (!parent_local_surface_id_allocator_)
AllocateLocalSurfaceId();
return GetCurrentLocalSurfaceId();
}
void Window::InvalidateLocalSurfaceId() {
if (!parent_local_surface_id_allocator_)
return;
parent_local_surface_id_allocator_->Invalidate();
}
void Window::UpdateLocalSurfaceIdFromEmbeddedClient(
const absl::optional<viz::LocalSurfaceId>&
embedded_client_local_surface_id) {
if (embedded_client_local_surface_id) {
parent_local_surface_id_allocator_->UpdateFromChild(
*embedded_client_local_surface_id);
UpdateLocalSurfaceId();
} else {
AllocateLocalSurfaceId();
}
}
const viz::FrameSinkId& Window::GetFrameSinkId() const {
if (IsRootWindow()) {
DCHECK(host_);
auto* compositor = host_->compositor();
DCHECK(compositor);
return compositor->frame_sink_id();
}
return frame_sink_id_;
}
void Window::SetEmbedFrameSinkId(const viz::FrameSinkId& frame_sink_id) {
SetEmbedFrameSinkIdImpl(frame_sink_id);
embeds_external_client_ = true;
}
void Window::TrackOcclusionState() {
Env::GetInstance()->GetWindowOcclusionTracker()->Track(this);
}
bool Window::RequiresDoubleTapGestureEvents() const {
return delegate_ && delegate_->RequiresDoubleTapGestureEvents();
}
// static
const std::u16string Window::OcclusionStateToString(OcclusionState state) {
return ui::metadata::TypeConverter<OcclusionState>::ToString(state);
}
void Window::SetOpaqueRegionsForOcclusion(
const std::vector<gfx::Rect>& opaque_regions_for_occlusion) {
// Only transparent windows should try to set opaque regions for occlusion.
DCHECK(GetTransparent() || opaque_regions_for_occlusion.empty());
if (opaque_regions_for_occlusion == opaque_regions_for_occlusion_)
return;
opaque_regions_for_occlusion_ = opaque_regions_for_occlusion;
for (auto& observer : observers_)
observer.OnWindowOpaqueRegionsForOcclusionChanged(this);
}
void Window::NotifyResizeLoopStarted() {
for (auto& observer : observers_)
observer.OnResizeLoopStarted(this);
}
void Window::NotifyResizeLoopEnded() {
for (auto& observer : observers_)
observer.OnResizeLoopEnded(this);
}
void Window::OnPaintLayer(const ui::PaintContext& context) {
Paint(context);
}
void Window::OnLayerBoundsChanged(const gfx::Rect& old_bounds,
ui::PropertyChangeReason reason) {
WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;
bounds_ = layer()->bounds();
if (!IsRootWindow() && old_bounds.size() != bounds_.size() &&
IsEmbeddingExternalContent()) {
parent_local_surface_id_allocator_->GenerateId();
if (frame_sink_) {
frame_sink_->SetLocalSurfaceId(GetCurrentLocalSurfaceId());
}
}
if (layout_manager_)
layout_manager_->OnWindowResized();
if (delegate_)
delegate_->OnBoundsChanged(old_bounds, bounds_);
for (auto& observer : observers_)
observer.OnWindowBoundsChanged(this, old_bounds, bounds_, reason);
// Trigger the changed notification for each of the bounds "properties".
if (old_bounds.x() != bounds_.x())
TriggerChangedCallback(&bounds_ + kBoundsX);
if (old_bounds.y() != bounds_.y())
TriggerChangedCallback(&bounds_ + kBoundsY);
if (old_bounds.width() != bounds_.width())
TriggerChangedCallback(&bounds_ + kBoundsWidth);
if (old_bounds.height() != bounds_.height())
TriggerChangedCallback(&bounds_ + kBoundsHeight);
}
void Window::OnLayerOpacityChanged(ui::PropertyChangeReason reason) {
WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;
for (WindowObserver& observer : observers_)
observer.OnWindowOpacitySet(this, reason);
}
void Window::OnLayerAlphaShapeChanged() {
WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;
for (WindowObserver& observer : observers_)
observer.OnWindowAlphaShapeSet(this);
}
void Window::OnLayerFillsBoundsOpaquelyChanged(
ui::PropertyChangeReason reason) {
// Let observers know that this window's transparent status has changed.
// Transparent status can affect the occlusion computed for windows.
WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;
// Non-transparent windows should not have opaque regions for occlusion set.
if (!GetTransparent())
DCHECK(opaque_regions_for_occlusion_.empty());
for (WindowObserver& observer : observers_)
observer.OnWindowTransparentChanged(this, reason);
}
void Window::OnLayerTransformed(const gfx::Transform& old_transform,
ui::PropertyChangeReason reason) {
WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;
for (WindowObserver& observer : observers_)
observer.OnWindowTransformed(this, reason);
}
bool Window::CanAcceptEvent(const ui::Event& event) {
// The client may forbid certain windows from receiving events at a given
// point in time.
client::EventClient* client = client::GetEventClient(GetRootWindow());
if (client && !client->GetCanProcessEventsWithinSubtree(this))
return false;
// We need to make sure that a touch cancel event and any gesture events it
// creates can always reach the window. This ensures that we receive a valid
// touch / gesture stream.
if (event.IsEndingEvent())
return true;
if (!IsVisible())
return false;
// The top-most window can always process an event.
if (!parent_)
return true;
// For located events (i.e. mouse, touch etc.), an assumption is made that
// windows that don't have a default event-handler cannot process the event
// (see more in GetWindowForPoint()). This assumption is not made for key
// events.
return event.IsKeyEvent() || target_handler();
}
ui::EventTarget* Window::GetParentTarget() {
if (IsRootWindow()) {
return client::GetEventClient(this)
? client::GetEventClient(this)->GetToplevelEventTarget()
: Env::GetInstance();
}
return parent_;
}
std::unique_ptr<ui::EventTargetIterator> Window::GetChildIterator() const {
return std::make_unique<ui::EventTargetIteratorPtrImpl<Window>>(children());
}
ui::EventTargeter* Window::GetEventTargeter() {
return targeter_.get();
}
void Window::ConvertEventToTarget(const ui::EventTarget* target,
ui::LocatedEvent* event) const {
event->ConvertLocationToTarget(this, static_cast<const Window*>(target));
}
gfx::PointF Window::GetScreenLocationF(const ui::LocatedEvent& event) const {
DCHECK_EQ(this, event.target());
gfx::PointF screen_location(event.root_location_f());
const Window* root = GetRootWindow();
auto* screen_position_client = aura::client::GetScreenPositionClient(root);
if (screen_position_client)
screen_position_client->ConvertPointToScreen(root, &screen_location);
return screen_location;
}
std::unique_ptr<ui::Layer> Window::ReleaseLayer() {
if (number_of_capture_requests_) {
// Before we release our own layer, if this window was marked for capture,
// we need to reset the SubtreeCaptureId on that layer as it will no longer
// be associated with us.
DCHECK(subtree_capture_id_.is_valid());
if (layer())
layer()->SetSubtreeCaptureId(viz::SubtreeCaptureId());
}
return LayerOwner::ReleaseLayer();
}
std::unique_ptr<ui::Layer> Window::RecreateLayer() {
WindowOcclusionTracker::ScopedPause pause_occlusion_tracking;
ui::LayerAnimator* const animator = layer()->GetAnimator();
const bool was_animating_opacity =
animator->IsAnimatingProperty(ui::LayerAnimationElement::OPACITY);
const bool was_animating_transform =
animator->IsAnimatingProperty(ui::LayerAnimationElement::TRANSFORM);
std::unique_ptr<ui::Layer> old_layer = LayerOwner::RecreateLayer();
// If a frame sink is attached to the window, then allocate a new surface
// id when layers are recreated, so the old layer contents are not affected
// by a frame sent to the frame sink.
if (GetFrameSinkId().is_valid() && old_layer)
AllocateLocalSurfaceId();
// The old layer subtree must no longer be capturable.
// Note that we don't need to worry about the newly recreated layer since
// Window::SetLayer() will have taken care of it already.
if (number_of_capture_requests_ && old_layer)
old_layer->SetSubtreeCaptureId(viz::SubtreeCaptureId());
// Observers are guaranteed to be notified when an opacity or transform
// animation ends.
if (was_animating_opacity) {
for (WindowObserver& observer : observers_) {
observer.OnWindowOpacitySet(this,
ui::PropertyChangeReason::FROM_ANIMATION);
}
}
if (was_animating_transform) {
for (WindowObserver& observer : observers_) {
observer.OnWindowTransformed(this,
ui::PropertyChangeReason::FROM_ANIMATION);
}
}
for (WindowObserver& observer : observers_)
observer.OnWindowLayerRecreated(this);
return old_layer;
}
void Window::SetLayer(std::unique_ptr<ui::Layer> alayer) {
LayerOwner::SetLayer(std::move(alayer));
if (number_of_capture_requests_) {
// If this window was marked for capture before, then the new layer that we
// own now should be given the current SubtreeCaptureId that we have.
DCHECK(subtree_capture_id_.is_valid());
if (layer())
layer()->SetSubtreeCaptureId(subtree_capture_id_);
}
}
void Window::OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) {
DCHECK_EQ(surface_info.id().frame_sink_id(), GetFrameSinkId());
layer()->SetShowSurface(surface_info.id(), bounds().size(), SK_ColorWHITE,
cc::DeadlinePolicy::UseDefaultDeadline(),
false /* stretch_content_to_fill_bounds */);
}
void Window::OnFrameTokenChanged(uint32_t frame_token,
base::TimeTicks activation_time) {}
void Window::UpdateLayerName() {
DCHECK(layer());
std::string layer_name(GetName());
if (layer_name.empty())
layer_name = "Unnamed Window";
if (id_ != -1)
layer_name += " " + base::NumberToString(id_);
layer()->SetName(layer_name);
}
void Window::RegisterFrameSinkId() {
DCHECK(frame_sink_id_.is_valid());
if (registered_frame_sink_id_ || disable_frame_sink_id_registration_)
return;
if (auto* compositor = layer()->GetCompositor()) {
compositor->AddChildFrameSink(frame_sink_id_);
registered_frame_sink_id_ = true;
}
}
void Window::UnregisterFrameSinkId() {
if (!registered_frame_sink_id_)
return;
registered_frame_sink_id_ = false;
if (auto* compositor = layer()->GetCompositor())
compositor->RemoveChildFrameSink(frame_sink_id_);
}
void Window::UpdateLocalSurfaceId() {
last_device_scale_factor_ = ui::GetScaleFactorForNativeView(this);
if (frame_sink_) {
frame_sink_->SetLocalSurfaceId(GetCurrentLocalSurfaceId());
}
}
const viz::LocalSurfaceId& Window::GetCurrentLocalSurfaceId() const {
return parent_local_surface_id_allocator_->GetCurrentLocalSurfaceId();
}
bool Window::IsEmbeddingExternalContent() const {
return parent_local_surface_id_allocator_.get() != nullptr;
}
void Window::OnScopedWindowCaptureRequestAdded() {
if (++number_of_capture_requests_ == 1) {
DCHECK(!subtree_capture_id_.is_valid());
DCHECK(!layer() || !layer()->GetSubtreeCaptureId().is_valid());
subtree_capture_id_ =
Env::GetInstance()->context_factory()->AllocateSubtreeCaptureId();
if (layer())
layer()->SetSubtreeCaptureId(subtree_capture_id_);
}
DCHECK(subtree_capture_id_.is_valid());
}
void Window::OnScopedWindowCaptureRequestRemoved() {
DCHECK(subtree_capture_id_.is_valid());
DCHECK(number_of_capture_requests_);
--number_of_capture_requests_;
DCHECK_GE(number_of_capture_requests_, 0);
if (number_of_capture_requests_ == 0) {
subtree_capture_id_ = viz::SubtreeCaptureId();
if (layer())
layer()->SetSubtreeCaptureId(subtree_capture_id_);
}
}
int Window::GetHeight() const {
return bounds().height();
}
int Window::GetWidth() const {
return bounds().width();
}
int Window::GetX() const {
return bounds().x();
}
int Window::GetY() const {
return bounds().y();
}
void Window::SetHeight(int height) {
if (height == bounds().height())
return;
// Bounds changed notification is done within OnLayerBoundsChanged.
// Here and below.
SetBounds({bounds().x(), bounds().y(), bounds().width(), height});
}
void Window::SetWidth(int width) {
if (width == bounds().width())
return;
SetBounds({bounds().x(), bounds().y(), width, bounds().height()});
}
void Window::SetX(int x) {
if (x == bounds().x())
return;
SetBounds({x, bounds().y(), bounds().width(), bounds().height()});
}
void Window::SetY(int y) {
if (y == bounds().y())
return;
SetBounds({bounds().x(), y, bounds().width(), bounds().height()});
}
bool Window::GetCapture() const {
return const_cast<Window*>(this)->HasCapture();
}
bool Window::GetVisible() const {
return IsVisible();
}
void Window::SetVisible(bool visible) {
if (visible == IsVisible())
return;
if (visible)
Show();
else
Hide();
// Changed notification is handled in SetVisibleInternal().
}
BEGIN_METADATA_BASE(Window)
ADD_READONLY_PROPERTY_METADATA(gfx::Rect, ActualBoundsInRootWindow)
ADD_READONLY_PROPERTY_METADATA(gfx::Rect, ActualBoundsInScreen)
ADD_READONLY_PROPERTY_METADATA(gfx::Rect, BoundsInRootWindow)
ADD_READONLY_PROPERTY_METADATA(gfx::Rect, BoundsInScreen)
ADD_READONLY_PROPERTY_METADATA(bool, Capture)
ADD_PROPERTY_METADATA(int, Height)
ADD_PROPERTY_METADATA(int, Width)
ADD_PROPERTY_METADATA(int, X)
ADD_PROPERTY_METADATA(int, Y)
ADD_READONLY_PROPERTY_METADATA(Window::OcclusionState, OcclusionState)
ADD_READONLY_PROPERTY_METADATA(viz::SurfaceId, SurfaceId)
ADD_PROPERTY_METADATA(int, Id)
ADD_PROPERTY_METADATA(std::string, Name)
ADD_PROPERTY_METADATA(std::u16string, Title)
ADD_PROPERTY_METADATA(bool, Transparent)
ADD_PROPERTY_METADATA(client::WindowType, Type)
ADD_PROPERTY_METADATA(bool, Visible)
END_METADATA
} // namespace aura