summaryrefslogtreecommitdiff
path: root/chromium/components/mus/public/cpp/lib/window.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/mus/public/cpp/lib/window.cc')
-rw-r--r--chromium/components/mus/public/cpp/lib/window.cc891
1 files changed, 891 insertions, 0 deletions
diff --git a/chromium/components/mus/public/cpp/lib/window.cc b/chromium/components/mus/public/cpp/lib/window.cc
new file mode 100644
index 00000000000..83785c4544e
--- /dev/null
+++ b/chromium/components/mus/public/cpp/lib/window.cc
@@ -0,0 +1,891 @@
+// Copyright 2014 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 "components/mus/public/cpp/window.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <set>
+#include <string>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "components/mus/common/transient_window_utils.h"
+#include "components/mus/public/cpp/lib/window_private.h"
+#include "components/mus/public/cpp/property_type_converters.h"
+#include "components/mus/public/cpp/window_observer.h"
+#include "components/mus/public/cpp/window_property.h"
+#include "components/mus/public/cpp/window_surface.h"
+#include "components/mus/public/cpp/window_tracker.h"
+#include "components/mus/public/cpp/window_tree_client.h"
+#include "components/mus/public/interfaces/window_manager.mojom.h"
+#include "ui/display/display.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace mus {
+
+namespace {
+
+void NotifyWindowTreeChangeAtReceiver(
+ Window* receiver,
+ const WindowObserver::TreeChangeParams& params,
+ bool change_applied) {
+ WindowObserver::TreeChangeParams local_params = params;
+ local_params.receiver = receiver;
+ if (change_applied) {
+ FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(receiver).observers(),
+ OnTreeChanged(local_params));
+ } else {
+ FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(receiver).observers(),
+ OnTreeChanging(local_params));
+ }
+}
+
+void NotifyWindowTreeChangeUp(Window* start_at,
+ const WindowObserver::TreeChangeParams& params,
+ bool change_applied) {
+ for (Window* current = start_at; current; current = current->parent())
+ NotifyWindowTreeChangeAtReceiver(current, params, change_applied);
+}
+
+void NotifyWindowTreeChangeDown(Window* start_at,
+ const WindowObserver::TreeChangeParams& params,
+ bool change_applied) {
+ NotifyWindowTreeChangeAtReceiver(start_at, params, change_applied);
+ Window::Children::const_iterator it = start_at->children().begin();
+ for (; it != start_at->children().end(); ++it)
+ NotifyWindowTreeChangeDown(*it, params, change_applied);
+}
+
+void NotifyWindowTreeChange(const WindowObserver::TreeChangeParams& params,
+ bool change_applied) {
+ NotifyWindowTreeChangeDown(params.target, params, change_applied);
+ if (params.old_parent)
+ NotifyWindowTreeChangeUp(params.old_parent, params, change_applied);
+ if (params.new_parent)
+ NotifyWindowTreeChangeUp(params.new_parent, params, change_applied);
+}
+
+class ScopedTreeNotifier {
+ public:
+ ScopedTreeNotifier(Window* target, Window* old_parent, Window* new_parent) {
+ params_.target = target;
+ params_.old_parent = old_parent;
+ params_.new_parent = new_parent;
+ NotifyWindowTreeChange(params_, false);
+ }
+ ~ScopedTreeNotifier() { NotifyWindowTreeChange(params_, true); }
+
+ private:
+ WindowObserver::TreeChangeParams params_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTreeNotifier);
+};
+
+void RemoveChildImpl(Window* child, Window::Children* children) {
+ Window::Children::iterator it =
+ std::find(children->begin(), children->end(), child);
+ if (it != children->end()) {
+ children->erase(it);
+ WindowPrivate(child).ClearParent();
+ }
+}
+
+class OrderChangedNotifier {
+ public:
+ OrderChangedNotifier(Window* window,
+ Window* relative_window,
+ mojom::OrderDirection direction)
+ : window_(window),
+ relative_window_(relative_window),
+ direction_(direction) {}
+
+ ~OrderChangedNotifier() {}
+
+ void NotifyWindowReordering() {
+ FOR_EACH_OBSERVER(
+ WindowObserver, *WindowPrivate(window_).observers(),
+ OnWindowReordering(window_, relative_window_, direction_));
+ }
+
+ void NotifyWindowReordered() {
+ FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(window_).observers(),
+ OnWindowReordered(window_, relative_window_, direction_));
+ }
+
+ private:
+ Window* window_;
+ Window* relative_window_;
+ mojom::OrderDirection direction_;
+
+ DISALLOW_COPY_AND_ASSIGN(OrderChangedNotifier);
+};
+
+class ScopedSetBoundsNotifier {
+ public:
+ ScopedSetBoundsNotifier(Window* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds)
+ : window_(window), old_bounds_(old_bounds), new_bounds_(new_bounds) {
+ FOR_EACH_OBSERVER(
+ WindowObserver, *WindowPrivate(window_).observers(),
+ OnWindowBoundsChanging(window_, old_bounds_, new_bounds_));
+ }
+ ~ScopedSetBoundsNotifier() {
+ FOR_EACH_OBSERVER(WindowObserver, *WindowPrivate(window_).observers(),
+ OnWindowBoundsChanged(window_, old_bounds_, new_bounds_));
+ }
+
+ private:
+ Window* window_;
+ const gfx::Rect old_bounds_;
+ const gfx::Rect new_bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier);
+};
+
+// Some operations are only permitted in the client that created the window.
+bool OwnsWindow(WindowTreeClient* client, Window* window) {
+ return !client || client->OwnsWindow(window);
+}
+
+bool IsClientRoot(Window* window) {
+ return window->window_tree() &&
+ window->window_tree()->GetRoots().count(window) > 0;
+}
+
+bool OwnsWindowOrIsRoot(Window* window) {
+ return OwnsWindow(window->window_tree(), window) || IsClientRoot(window);
+}
+
+void EmptyEmbedCallback(bool result) {}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// Window, public:
+
+void Window::Destroy() {
+ if (!OwnsWindowOrIsRoot(this))
+ return;
+
+ if (client_)
+ client_->DestroyWindow(this);
+ while (!children_.empty()) {
+ Window* child = children_.front();
+ if (!OwnsWindow(client_, child)) {
+ WindowPrivate(child).ClearParent();
+ children_.erase(children_.begin());
+ } else {
+ child->Destroy();
+ DCHECK(std::find(children_.begin(), children_.end(), child) ==
+ children_.end());
+ }
+ }
+ LocalDestroy();
+}
+
+void Window::SetBounds(const gfx::Rect& bounds) {
+ if (!OwnsWindowOrIsRoot(this))
+ return;
+ if (bounds_ == bounds)
+ return;
+ if (client_)
+ client_->SetBounds(this, bounds_, bounds);
+ LocalSetBounds(bounds_, bounds);
+}
+
+gfx::Rect Window::GetBoundsInRoot() const {
+ gfx::Vector2d offset;
+ for (const Window* w = parent(); w != nullptr; w = w->parent())
+ offset += w->bounds().OffsetFromOrigin();
+ return bounds() + offset;
+}
+
+void Window::SetClientArea(
+ const gfx::Insets& client_area,
+ const std::vector<gfx::Rect>& additional_client_areas) {
+ if (!OwnsWindowOrIsRoot(this))
+ return;
+
+ if (client_)
+ client_->SetClientArea(server_id_, client_area,
+ additional_client_areas);
+ LocalSetClientArea(client_area, additional_client_areas);
+}
+
+void Window::SetHitTestMask(const gfx::Rect& mask) {
+ if (!OwnsWindowOrIsRoot(this))
+ return;
+
+ if (hit_test_mask_ && *hit_test_mask_ == mask)
+ return;
+
+ if (client_)
+ client_->SetHitTestMask(server_id_, mask);
+ hit_test_mask_.reset(new gfx::Rect(mask));
+}
+
+void Window::ClearHitTestMask() {
+ if (!OwnsWindowOrIsRoot(this))
+ return;
+
+ if (!hit_test_mask_)
+ return;
+
+ if (client_)
+ client_->ClearHitTestMask(server_id_);
+ hit_test_mask_.reset();
+}
+
+void Window::SetVisible(bool value) {
+ if (visible_ == value)
+ return;
+
+ if (client_)
+ client_->SetVisible(this, value);
+ LocalSetVisible(value);
+}
+
+void Window::SetOpacity(float opacity) {
+ if (client_)
+ client_->SetOpacity(this, opacity);
+ LocalSetOpacity(opacity);
+}
+
+void Window::SetPredefinedCursor(mus::mojom::Cursor cursor_id) {
+ if (cursor_id_ == cursor_id)
+ return;
+
+ if (client_)
+ client_->SetPredefinedCursor(server_id_, cursor_id);
+ LocalSetPredefinedCursor(cursor_id);
+}
+
+bool Window::IsDrawn() const {
+ if (!visible_)
+ return false;
+ return parent_ ? parent_->IsDrawn() : parent_drawn_;
+}
+
+std::unique_ptr<WindowSurface> Window::RequestSurface(mojom::SurfaceType type) {
+ std::unique_ptr<WindowSurfaceBinding> surface_binding;
+ std::unique_ptr<WindowSurface> surface =
+ WindowSurface::Create(&surface_binding);
+ AttachSurface(type, std::move(surface_binding));
+ return surface;
+}
+
+void Window::AttachSurface(
+ mojom::SurfaceType type,
+ std::unique_ptr<WindowSurfaceBinding> surface_binding) {
+ window_tree()->AttachSurface(
+ server_id_, type, std::move(surface_binding->surface_request_),
+ mojo::MakeProxy(std::move(surface_binding->surface_client_)));
+}
+
+void Window::ClearSharedProperty(const std::string& name) {
+ SetSharedPropertyInternal(name, nullptr);
+}
+
+bool Window::HasSharedProperty(const std::string& name) const {
+ return properties_.count(name) > 0;
+}
+
+void Window::AddObserver(WindowObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void Window::RemoveObserver(WindowObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+const Window* Window::GetRoot() const {
+ const Window* root = this;
+ for (const Window* parent = this; parent; parent = parent->parent())
+ root = parent;
+ return root;
+}
+
+void Window::AddChild(Window* child) {
+ // TODO(beng): not necessarily valid to all clients, but possibly to the
+ // embeddee in an embedder-embeddee relationship.
+ if (client_)
+ CHECK_EQ(child->client_, client_);
+ // Roots can not be added as children of other windows.
+ if (window_tree() && window_tree()->IsRoot(child))
+ return;
+ LocalAddChild(child);
+ if (client_)
+ client_->AddChild(this, child->server_id());
+}
+
+void Window::RemoveChild(Window* child) {
+ // TODO(beng): not necessarily valid to all clients, but possibly to the
+ // embeddee in an embedder-embeddee relationship.
+ if (client_)
+ CHECK_EQ(child->client_, client_);
+ LocalRemoveChild(child);
+ if (client_)
+ client_->RemoveChild(this, child->server_id());
+}
+
+void Window::Reorder(Window* relative, mojom::OrderDirection direction) {
+ if (!LocalReorder(relative, direction))
+ return;
+ if (client_)
+ client_->Reorder(this, relative->server_id(), direction);
+}
+
+void Window::MoveToFront() {
+ if (!parent_ || parent_->children_.back() == this)
+ return;
+ Reorder(parent_->children_.back(), mojom::OrderDirection::ABOVE);
+}
+
+void Window::MoveToBack() {
+ if (!parent_ || parent_->children_.front() == this)
+ return;
+ Reorder(parent_->children_.front(), mojom::OrderDirection::BELOW);
+}
+
+bool Window::Contains(const Window* child) const {
+ if (!child)
+ return false;
+ if (child == this)
+ return true;
+ if (client_)
+ CHECK_EQ(child->client_, client_);
+ for (const Window* p = child->parent(); p; p = p->parent()) {
+ if (p == this)
+ return true;
+ }
+ return false;
+}
+
+void Window::AddTransientWindow(Window* transient_window) {
+ // A system modal window cannot become a transient child.
+ DCHECK(!transient_window->is_modal() || transient_window->transient_parent());
+
+ if (client_)
+ CHECK_EQ(transient_window->client_, client_);
+ LocalAddTransientWindow(transient_window);
+ if (client_)
+ client_->AddTransientWindow(this, transient_window->server_id());
+}
+
+void Window::RemoveTransientWindow(Window* transient_window) {
+ if (client_)
+ CHECK_EQ(transient_window->window_tree(), client_);
+ LocalRemoveTransientWindow(transient_window);
+ if (client_)
+ client_->RemoveTransientWindowFromParent(transient_window);
+}
+
+void Window::SetModal() {
+ if (is_modal_)
+ return;
+
+ LocalSetModal();
+ if (client_)
+ client_->SetModal(this);
+}
+
+Window* Window::GetChildByLocalId(int id) {
+ if (id == local_id_)
+ return this;
+ // TODO(beng): this could be improved depending on how we decide to own
+ // windows.
+ for (Window* child : children_) {
+ Window* matching_child = child->GetChildByLocalId(id);
+ if (matching_child)
+ return matching_child;
+ }
+ return nullptr;
+}
+
+void Window::SetTextInputState(mojo::TextInputStatePtr state) {
+ if (client_)
+ client_->SetWindowTextInputState(server_id_, std::move(state));
+}
+
+void Window::SetImeVisibility(bool visible, mojo::TextInputStatePtr state) {
+ // SetImeVisibility() shouldn't be used if the window is not editable.
+ DCHECK(state.is_null() || state->type != mojo::TextInputType::NONE);
+ if (client_)
+ client_->SetImeVisibility(server_id_, visible, std::move(state));
+}
+
+bool Window::HasCapture() const {
+ return client_ && client_->GetCaptureWindow() == this;
+}
+
+void Window::SetCapture() {
+ if (client_)
+ client_->SetCapture(this);
+}
+
+void Window::ReleaseCapture() {
+ if (client_)
+ client_->ReleaseCapture(this);
+}
+
+void Window::SetFocus() {
+ if (client_ && IsDrawn())
+ client_->SetFocus(this);
+}
+
+bool Window::HasFocus() const {
+ return client_ && client_->GetFocusedWindow() == this;
+}
+
+void Window::SetCanFocus(bool can_focus) {
+ if (client_)
+ client_->SetCanFocus(server_id_, can_focus);
+}
+
+void Window::Embed(mus::mojom::WindowTreeClientPtr client, uint32_t flags) {
+ Embed(std::move(client), base::Bind(&EmptyEmbedCallback), flags);
+}
+
+void Window::Embed(mus::mojom::WindowTreeClientPtr client,
+ const EmbedCallback& callback,
+ uint32_t flags) {
+ if (PrepareForEmbed())
+ client_->Embed(server_id_, std::move(client), flags, callback);
+ else
+ callback.Run(false);
+}
+
+void Window::RequestClose() {
+ if (client_)
+ client_->RequestClose(this);
+}
+
+std::string Window::GetName() const {
+ if (HasSharedProperty(mojom::WindowManager::kName_Property))
+ return GetSharedProperty<std::string>(mojom::WindowManager::kName_Property);
+
+ return std::string();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Window, protected:
+
+Window::Window() : Window(nullptr, static_cast<Id>(-1)) {}
+
+Window::~Window() {
+ FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDestroying(this));
+ if (client_)
+ client_->OnWindowDestroying(this);
+
+ if (HasFocus()) {
+ // The focused window is being removed. When this happens the server
+ // advances focus. We don't want to randomly pick a Window to get focus, so
+ // we update local state only, and wait for the next focus change from the
+ // server.
+ client_->LocalSetFocus(nullptr);
+ }
+
+ // Remove from transient parent.
+ if (transient_parent_)
+ transient_parent_->LocalRemoveTransientWindow(this);
+
+ // Remove transient children.
+ while (!transient_children_.empty()) {
+ Window* transient_child = transient_children_.front();
+ LocalRemoveTransientWindow(transient_child);
+ transient_child->LocalDestroy();
+ DCHECK(transient_children_.empty() ||
+ transient_children_.front() != transient_child);
+ }
+
+ if (parent_)
+ parent_->LocalRemoveChild(this);
+
+ // We may still have children. This can happen if the embedder destroys the
+ // root while we're still alive.
+ while (!children_.empty()) {
+ Window* child = children_.front();
+ LocalRemoveChild(child);
+ DCHECK(children_.empty() || children_.front() != child);
+ }
+
+ // Clear properties.
+ for (auto& pair : prop_map_) {
+ if (pair.second.deallocator)
+ (*pair.second.deallocator)(pair.second.value);
+ }
+ prop_map_.clear();
+
+ FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDestroyed(this));
+
+ // Invoke after observers so that can clean up any internal state observers
+ // may have changed.
+ if (window_tree())
+ window_tree()->OnWindowDestroyed(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Window, private:
+
+Window::Window(WindowTreeClient* client, Id id)
+ : client_(client),
+ server_id_(id),
+ parent_(nullptr),
+ stacking_target_(nullptr),
+ transient_parent_(nullptr),
+ is_modal_(false),
+ // Matches aura, see aura::Window for details.
+ observers_(base::ObserverList<WindowObserver>::NOTIFY_EXISTING_ONLY),
+ input_event_handler_(nullptr),
+ visible_(false),
+ opacity_(1.0f),
+ display_id_(display::Display::kInvalidDisplayID),
+ cursor_id_(mojom::Cursor::CURSOR_NULL),
+ parent_drawn_(false) {}
+
+void Window::SetSharedPropertyInternal(const std::string& name,
+ const std::vector<uint8_t>* value) {
+ if (!OwnsWindowOrIsRoot(this))
+ return;
+
+ if (client_) {
+ mojo::Array<uint8_t> transport_value(nullptr);
+ if (value) {
+ transport_value.resize(value->size());
+ if (value->size())
+ memcpy(&transport_value.front(), &(value->front()), value->size());
+ }
+ // TODO: add test coverage of this (450303).
+ client_->SetProperty(this, name, std::move(transport_value));
+ }
+ LocalSetSharedProperty(name, value);
+}
+
+int64_t Window::SetLocalPropertyInternal(const void* key,
+ const char* name,
+ PropertyDeallocator deallocator,
+ int64_t value,
+ int64_t default_value) {
+ int64_t old = GetLocalPropertyInternal(key, default_value);
+ if (value == default_value) {
+ prop_map_.erase(key);
+ } else {
+ Value prop_value;
+ prop_value.name = name;
+ prop_value.value = value;
+ prop_value.deallocator = deallocator;
+ prop_map_[key] = prop_value;
+ }
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowLocalPropertyChanged(this, key, old));
+ return old;
+}
+
+int64_t Window::GetLocalPropertyInternal(const void* key,
+ int64_t default_value) const {
+ std::map<const void*, Value>::const_iterator iter = prop_map_.find(key);
+ if (iter == prop_map_.end())
+ return default_value;
+ return iter->second.value;
+}
+
+void Window::LocalDestroy() {
+ delete this;
+}
+
+void Window::LocalAddChild(Window* child) {
+ ScopedTreeNotifier notifier(child, child->parent(), this);
+ if (child->parent())
+ RemoveChildImpl(child, &child->parent_->children_);
+ children_.push_back(child);
+ child->parent_ = this;
+ child->display_id_ = display_id_;
+}
+
+void Window::LocalRemoveChild(Window* child) {
+ DCHECK_EQ(this, child->parent());
+ ScopedTreeNotifier notifier(child, this, nullptr);
+ RemoveChildImpl(child, &children_);
+}
+
+void Window::LocalAddTransientWindow(Window* transient_window) {
+ if (transient_window->transient_parent())
+ RemoveTransientWindowImpl(transient_window);
+ transient_children_.push_back(transient_window);
+ transient_window->transient_parent_ = this;
+
+ // Restack |transient_window| properly above its transient parent, if they
+ // share the same parent.
+ if (transient_window->parent() == parent())
+ RestackTransientDescendants(this, &GetStackingTarget,
+ &ReorderWithoutNotification);
+
+ // TODO(fsamuel): We might want a notification here.
+}
+
+void Window::LocalRemoveTransientWindow(Window* transient_window) {
+ DCHECK_EQ(this, transient_window->transient_parent());
+ RemoveTransientWindowImpl(transient_window);
+ // TODO(fsamuel): We might want a notification here.
+}
+
+void Window::LocalSetModal() {
+ is_modal_ = true;
+}
+
+bool Window::LocalReorder(Window* relative, mojom::OrderDirection direction) {
+ OrderChangedNotifier notifier(this, relative, direction);
+ return ReorderImpl(this, relative, direction, &notifier);
+}
+
+void Window::LocalSetBounds(const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ // If this client owns the window, then it should be the only one to change
+ // the bounds.
+ DCHECK(!OwnsWindow(client_, this) || old_bounds == bounds_);
+ ScopedSetBoundsNotifier notifier(this, old_bounds, new_bounds);
+ bounds_ = new_bounds;
+}
+
+void Window::LocalSetClientArea(
+ const gfx::Insets& new_client_area,
+ const std::vector<gfx::Rect>& additional_client_areas) {
+ const std::vector<gfx::Rect> old_additional_client_areas =
+ additional_client_areas_;
+ const gfx::Insets old_client_area = client_area_;
+ client_area_ = new_client_area;
+ additional_client_areas_ = additional_client_areas;
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowClientAreaChanged(this, old_client_area,
+ old_additional_client_areas));
+}
+
+void Window::LocalSetDisplay(int64_t display_id) {
+ display_id_ = display_id;
+ // TODO(sad): Notify observers (of this window, and of the descendant windows)
+ // when a window moves from one display into another. https://crbug.com/614887
+}
+
+void Window::LocalSetParentDrawn(bool value) {
+ if (parent_drawn_ == value)
+ return;
+
+ // As IsDrawn() is derived from |visible_| and |parent_drawn_|, only send
+ // drawn notification is the value of IsDrawn() is really changing.
+ if (IsDrawn() == value) {
+ parent_drawn_ = value;
+ return;
+ }
+ FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDrawnChanging(this));
+ parent_drawn_ = value;
+ FOR_EACH_OBSERVER(WindowObserver, observers_, OnWindowDrawnChanged(this));
+}
+
+void Window::LocalSetVisible(bool visible) {
+ if (visible_ == visible)
+ return;
+
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowVisibilityChanging(this));
+ visible_ = visible;
+ NotifyWindowVisibilityChanged(this);
+}
+
+void Window::LocalSetOpacity(float opacity) {
+ if (opacity_ == opacity)
+ return;
+
+ float old_opacity = opacity_;
+ opacity_ = opacity;
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowOpacityChanged(this, old_opacity, opacity_));
+}
+
+void Window::LocalSetPredefinedCursor(mojom::Cursor cursor_id) {
+ if (cursor_id_ == cursor_id)
+ return;
+
+ cursor_id_ = cursor_id;
+ FOR_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowPredefinedCursorChanged(this, cursor_id));
+}
+
+void Window::LocalSetSharedProperty(const std::string& name,
+ const std::vector<uint8_t>* value) {
+ std::vector<uint8_t> old_value;
+ std::vector<uint8_t>* old_value_ptr = nullptr;
+ auto it = properties_.find(name);
+ if (it != properties_.end()) {
+ old_value = it->second;
+ old_value_ptr = &old_value;
+
+ if (value && old_value == *value)
+ return;
+ } else if (!value) {
+ // This property isn't set in |properties_| and |value| is nullptr, so
+ // there's no change.
+ return;
+ }
+
+ if (value) {
+ properties_[name] = *value;
+ } else if (it != properties_.end()) {
+ properties_.erase(it);
+ }
+
+ FOR_EACH_OBSERVER(
+ WindowObserver, observers_,
+ OnWindowSharedPropertyChanged(this, name, old_value_ptr, value));
+}
+
+void Window::NotifyWindowStackingChanged() {
+ if (stacking_target_) {
+ Children::const_iterator window_i = std::find(
+ parent()->children().begin(), parent()->children().end(), this);
+ DCHECK(window_i != parent()->children().end());
+ if (window_i != parent()->children().begin() &&
+ (*(window_i - 1) == stacking_target_))
+ return;
+ }
+ RestackTransientDescendants(this, &GetStackingTarget,
+ &ReorderWithoutNotification);
+}
+
+void Window::NotifyWindowVisibilityChanged(Window* target) {
+ if (!NotifyWindowVisibilityChangedDown(target)) {
+ return; // |this| has been deleted.
+ }
+ NotifyWindowVisibilityChangedUp(target);
+}
+
+bool Window::NotifyWindowVisibilityChangedAtReceiver(Window* target) {
+ // |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_EACH_OBSERVER(WindowObserver, observers_,
+ OnWindowVisibilityChanged(target));
+ return tracker.Contains(this);
+}
+
+bool Window::NotifyWindowVisibilityChangedDown(Window* target) {
+ if (!NotifyWindowVisibilityChangedAtReceiver(target))
+ return false; // |this| was deleted.
+ std::set<const Window*> child_already_processed;
+ bool child_destroyed = false;
+ do {
+ child_destroyed = false;
+ for (Window::Children::const_iterator it = children_.begin();
+ it != children_.end(); ++it) {
+ if (!child_already_processed.insert(*it).second)
+ continue;
+ if (!(*it)->NotifyWindowVisibilityChangedDown(target)) {
+ // |*it| was deleted, |it| is invalid and |children_| has changed. We
+ // exit the current for-loop and enter a new one.
+ child_destroyed = true;
+ break;
+ }
+ }
+ } while (child_destroyed);
+ return true;
+}
+
+void Window::NotifyWindowVisibilityChangedUp(Window* target) {
+ // Start with the parent as we already notified |this|
+ // in NotifyWindowVisibilityChangedDown.
+ for (Window* window = parent(); window; window = window->parent()) {
+ bool ret = window->NotifyWindowVisibilityChangedAtReceiver(target);
+ DCHECK(ret);
+ }
+}
+
+bool Window::PrepareForEmbed() {
+ if (!OwnsWindow(client_, this))
+ return false;
+
+ while (!children_.empty())
+ RemoveChild(children_[0]);
+ return true;
+}
+
+void Window::RemoveTransientWindowImpl(Window* transient_window) {
+ Window::Children::iterator it = std::find(
+ transient_children_.begin(), transient_children_.end(), transient_window);
+ if (it != transient_children_.end()) {
+ transient_children_.erase(it);
+ transient_window->transient_parent_ = nullptr;
+ }
+ // If |transient_window| and its former transient parent share the same
+ // parent, |transient_window| should be restacked properly so it is not among
+ // transient children of its former parent, anymore.
+ if (parent() == transient_window->parent())
+ RestackTransientDescendants(this, &GetStackingTarget,
+ &ReorderWithoutNotification);
+
+ // TOOD(fsamuel): We might want to notify observers here.
+}
+
+// static
+void Window::ReorderWithoutNotification(Window* window,
+ Window* relative,
+ mojom::OrderDirection direction) {
+ ReorderImpl(window, relative, direction, nullptr);
+}
+
+// static
+// Returns true if the order actually changed.
+bool Window::ReorderImpl(Window* window,
+ Window* relative,
+ mojom::OrderDirection direction,
+ OrderChangedNotifier* notifier) {
+ DCHECK(relative);
+ DCHECK_NE(window, relative);
+ DCHECK_EQ(window->parent(), relative->parent());
+ DCHECK(window->parent());
+
+ if (!AdjustStackingForTransientWindows(&window, &relative, &direction,
+ window->stacking_target_))
+ return false;
+
+ const size_t child_i = std::find(window->parent_->children_.begin(),
+ window->parent_->children_.end(), window) -
+ window->parent_->children_.begin();
+ const size_t target_i =
+ std::find(window->parent_->children_.begin(),
+ window->parent_->children_.end(), relative) -
+ window->parent_->children_.begin();
+ if ((direction == mojom::OrderDirection::ABOVE && child_i == target_i + 1) ||
+ (direction == mojom::OrderDirection::BELOW && child_i + 1 == target_i)) {
+ return false;
+ }
+
+ if (notifier)
+ notifier->NotifyWindowReordering();
+
+ const size_t dest_i = direction == mojom::OrderDirection::ABOVE
+ ? (child_i < target_i ? target_i : target_i + 1)
+ : (child_i < target_i ? target_i - 1 : target_i);
+ window->parent_->children_.erase(window->parent_->children_.begin() +
+ child_i);
+ window->parent_->children_.insert(window->parent_->children_.begin() + dest_i,
+ window);
+
+ window->NotifyWindowStackingChanged();
+
+ if (notifier)
+ notifier->NotifyWindowReordered();
+
+ return true;
+}
+
+// static
+Window** Window::GetStackingTarget(Window* window) {
+ return &window->stacking_target_;
+}
+} // namespace mus