Mandoline UI Process: Update namespaces and file names
[chromium-blink-merge.git] / components / mus / public / cpp / lib / view.cc
blob88228eba945e901ba770e8277d8ac192a16d60c7
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/mus/public/cpp/view.h"
7 #include <set>
8 #include <string>
10 #include "base/bind.h"
11 #include "components/mus/public/cpp/lib/view_private.h"
12 #include "components/mus/public/cpp/lib/view_tree_client_impl.h"
13 #include "components/mus/public/cpp/view_observer.h"
14 #include "components/mus/public/cpp/view_surface.h"
15 #include "components/mus/public/cpp/view_tracker.h"
16 #include "mojo/application/public/cpp/service_provider_impl.h"
18 namespace mus {
20 namespace {
22 void NotifyViewTreeChangeAtReceiver(
23 View* receiver,
24 const ViewObserver::TreeChangeParams& params,
25 bool change_applied) {
26 ViewObserver::TreeChangeParams local_params = params;
27 local_params.receiver = receiver;
28 if (change_applied) {
29 FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(receiver).observers(),
30 OnTreeChanged(local_params));
31 } else {
32 FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(receiver).observers(),
33 OnTreeChanging(local_params));
37 void NotifyViewTreeChangeUp(View* start_at,
38 const ViewObserver::TreeChangeParams& params,
39 bool change_applied) {
40 for (View* current = start_at; current; current = current->parent())
41 NotifyViewTreeChangeAtReceiver(current, params, change_applied);
44 void NotifyViewTreeChangeDown(View* start_at,
45 const ViewObserver::TreeChangeParams& params,
46 bool change_applied) {
47 NotifyViewTreeChangeAtReceiver(start_at, params, change_applied);
48 View::Children::const_iterator it = start_at->children().begin();
49 for (; it != start_at->children().end(); ++it)
50 NotifyViewTreeChangeDown(*it, params, change_applied);
53 void NotifyViewTreeChange(const ViewObserver::TreeChangeParams& params,
54 bool change_applied) {
55 NotifyViewTreeChangeDown(params.target, params, change_applied);
56 if (params.old_parent)
57 NotifyViewTreeChangeUp(params.old_parent, params, change_applied);
58 if (params.new_parent)
59 NotifyViewTreeChangeUp(params.new_parent, params, change_applied);
62 class ScopedTreeNotifier {
63 public:
64 ScopedTreeNotifier(View* target, View* old_parent, View* new_parent) {
65 params_.target = target;
66 params_.old_parent = old_parent;
67 params_.new_parent = new_parent;
68 NotifyViewTreeChange(params_, false);
70 ~ScopedTreeNotifier() { NotifyViewTreeChange(params_, true); }
72 private:
73 ViewObserver::TreeChangeParams params_;
75 MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedTreeNotifier);
78 void RemoveChildImpl(View* child, View::Children* children) {
79 View::Children::iterator it =
80 std::find(children->begin(), children->end(), child);
81 if (it != children->end()) {
82 children->erase(it);
83 ViewPrivate(child).ClearParent();
87 class ScopedOrderChangedNotifier {
88 public:
89 ScopedOrderChangedNotifier(View* view,
90 View* relative_view,
91 mojo::OrderDirection direction)
92 : view_(view), relative_view_(relative_view), direction_(direction) {
93 FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(view_).observers(),
94 OnViewReordering(view_, relative_view_, direction_));
96 ~ScopedOrderChangedNotifier() {
97 FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(view_).observers(),
98 OnViewReordered(view_, relative_view_, direction_));
101 private:
102 View* view_;
103 View* relative_view_;
104 mojo::OrderDirection direction_;
106 MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedOrderChangedNotifier);
109 // Returns true if the order actually changed.
110 bool ReorderImpl(View::Children* children,
111 View* view,
112 View* relative,
113 mojo::OrderDirection direction) {
114 DCHECK(relative);
115 DCHECK_NE(view, relative);
116 DCHECK_EQ(view->parent(), relative->parent());
118 const size_t child_i =
119 std::find(children->begin(), children->end(), view) - children->begin();
120 const size_t target_i =
121 std::find(children->begin(), children->end(), relative) -
122 children->begin();
123 if ((direction == mojo::ORDER_DIRECTION_ABOVE && child_i == target_i + 1) ||
124 (direction == mojo::ORDER_DIRECTION_BELOW && child_i + 1 == target_i)) {
125 return false;
128 ScopedOrderChangedNotifier notifier(view, relative, direction);
130 const size_t dest_i = direction == mojo::ORDER_DIRECTION_ABOVE
131 ? (child_i < target_i ? target_i : target_i + 1)
132 : (child_i < target_i ? target_i - 1 : target_i);
133 children->erase(children->begin() + child_i);
134 children->insert(children->begin() + dest_i, view);
136 return true;
139 class ScopedSetBoundsNotifier {
140 public:
141 ScopedSetBoundsNotifier(View* view,
142 const mojo::Rect& old_bounds,
143 const mojo::Rect& new_bounds)
144 : view_(view), old_bounds_(old_bounds), new_bounds_(new_bounds) {
145 FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(view_).observers(),
146 OnViewBoundsChanging(view_, old_bounds_, new_bounds_));
148 ~ScopedSetBoundsNotifier() {
149 FOR_EACH_OBSERVER(ViewObserver, *ViewPrivate(view_).observers(),
150 OnViewBoundsChanged(view_, old_bounds_, new_bounds_));
153 private:
154 View* view_;
155 const mojo::Rect old_bounds_;
156 const mojo::Rect new_bounds_;
158 MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier);
161 // Some operations are only permitted in the connection that created the view.
162 bool OwnsView(ViewTreeConnection* connection, View* view) {
163 return !connection ||
164 static_cast<ViewTreeClientImpl*>(connection)->OwnsView(view->id());
167 void EmptyEmbedCallback(bool result, ConnectionSpecificId connection_id) {}
169 } // namespace
171 ////////////////////////////////////////////////////////////////////////////////
172 // View, public:
174 void View::Destroy() {
175 if (!OwnsView(connection_, this))
176 return;
178 if (connection_)
179 static_cast<ViewTreeClientImpl*>(connection_)->DestroyView(id_);
180 while (!children_.empty()) {
181 View* child = children_.front();
182 if (!OwnsView(connection_, child)) {
183 ViewPrivate(child).ClearParent();
184 children_.erase(children_.begin());
185 } else {
186 child->Destroy();
187 DCHECK(std::find(children_.begin(), children_.end(), child) ==
188 children_.end());
191 LocalDestroy();
194 void View::SetBounds(const mojo::Rect& bounds) {
195 if (!OwnsView(connection_, this))
196 return;
198 if (bounds_.Equals(bounds))
199 return;
201 if (connection_)
202 static_cast<ViewTreeClientImpl*>(connection_)->SetBounds(id_, bounds);
203 LocalSetBounds(bounds_, bounds);
206 void View::SetVisible(bool value) {
207 if (visible_ == value)
208 return;
210 if (connection_)
211 static_cast<ViewTreeClientImpl*>(connection_)->SetVisible(id_, value);
212 LocalSetVisible(value);
215 scoped_ptr<ViewSurface> View::RequestSurface() {
216 mojo::SurfacePtr surface;
217 mojo::SurfaceClientPtr client;
218 mojo::InterfaceRequest<mojo::SurfaceClient> client_request =
219 GetProxy(&client);
220 static_cast<ViewTreeClientImpl*>(connection_)
221 ->RequestSurface(id_, GetProxy(&surface), client.Pass());
222 return make_scoped_ptr(
223 new ViewSurface(surface.PassInterface(), client_request.Pass()));
226 void View::SetSharedProperty(const std::string& name,
227 const std::vector<uint8_t>* value) {
228 std::vector<uint8_t> old_value;
229 std::vector<uint8_t>* old_value_ptr = nullptr;
230 auto it = properties_.find(name);
231 if (it != properties_.end()) {
232 old_value = it->second;
233 old_value_ptr = &old_value;
235 if (value && old_value == *value)
236 return;
237 } else if (!value) {
238 // This property isn't set in |properties_| and |value| is NULL, so there's
239 // no change.
240 return;
243 if (value) {
244 properties_[name] = *value;
245 } else if (it != properties_.end()) {
246 properties_.erase(it);
249 // TODO: add test coverage of this (450303).
250 if (connection_) {
251 mojo::Array<uint8_t> transport_value;
252 if (value) {
253 transport_value.resize(value->size());
254 if (value->size())
255 memcpy(&transport_value.front(), &(value->front()), value->size());
257 static_cast<ViewTreeClientImpl*>(connection_)
258 ->SetProperty(id_, name, transport_value.Pass());
261 FOR_EACH_OBSERVER(
262 ViewObserver, observers_,
263 OnViewSharedPropertyChanged(this, name, old_value_ptr, value));
266 bool View::IsDrawn() const {
267 if (!visible_)
268 return false;
269 return parent_ ? parent_->IsDrawn() : drawn_;
272 void View::AddObserver(ViewObserver* observer) {
273 observers_.AddObserver(observer);
276 void View::RemoveObserver(ViewObserver* observer) {
277 observers_.RemoveObserver(observer);
280 const View* View::GetRoot() const {
281 const View* root = this;
282 for (const View* parent = this; parent; parent = parent->parent())
283 root = parent;
284 return root;
287 void View::AddChild(View* child) {
288 // TODO(beng): not necessarily valid to all connections, but possibly to the
289 // embeddee in an embedder-embeddee relationship.
290 if (connection_)
291 CHECK_EQ(child->connection(), connection_);
292 LocalAddChild(child);
293 if (connection_)
294 static_cast<ViewTreeClientImpl*>(connection_)->AddChild(child->id(), id_);
297 void View::RemoveChild(View* child) {
298 // TODO(beng): not necessarily valid to all connections, but possibly to the
299 // embeddee in an embedder-embeddee relationship.
300 if (connection_)
301 CHECK_EQ(child->connection(), connection_);
302 LocalRemoveChild(child);
303 if (connection_) {
304 static_cast<ViewTreeClientImpl*>(connection_)
305 ->RemoveChild(child->id(), id_);
309 void View::MoveToFront() {
310 if (!parent_ || parent_->children_.back() == this)
311 return;
312 Reorder(parent_->children_.back(), mojo::ORDER_DIRECTION_ABOVE);
315 void View::MoveToBack() {
316 if (!parent_ || parent_->children_.front() == this)
317 return;
318 Reorder(parent_->children_.front(), mojo::ORDER_DIRECTION_BELOW);
321 void View::Reorder(View* relative, mojo::OrderDirection direction) {
322 if (!LocalReorder(relative, direction))
323 return;
324 if (connection_) {
325 static_cast<ViewTreeClientImpl*>(connection_)
326 ->Reorder(id_, relative->id(), direction);
330 bool View::Contains(View* child) const {
331 if (!child)
332 return false;
333 if (child == this)
334 return true;
335 if (connection_)
336 CHECK_EQ(child->connection(), connection_);
337 for (View* p = child->parent(); p; p = p->parent()) {
338 if (p == this)
339 return true;
341 return false;
344 View* View::GetChildById(Id id) {
345 if (id == id_)
346 return this;
347 // TODO(beng): this could be improved depending on how we decide to own views.
348 Children::const_iterator it = children_.begin();
349 for (; it != children_.end(); ++it) {
350 View* view = (*it)->GetChildById(id);
351 if (view)
352 return view;
354 return NULL;
357 void View::SetTextInputState(mojo::TextInputStatePtr state) {
358 if (connection_) {
359 static_cast<ViewTreeClientImpl*>(connection_)
360 ->SetViewTextInputState(id_, state.Pass());
364 void View::SetImeVisibility(bool visible, mojo::TextInputStatePtr state) {
365 // SetImeVisibility() shouldn't be used if the view is not editable.
366 DCHECK(state.is_null() || state->type != mojo::TEXT_INPUT_TYPE_NONE);
367 if (connection_) {
368 static_cast<ViewTreeClientImpl*>(connection_)
369 ->SetImeVisibility(id_, visible, state.Pass());
373 void View::SetFocus() {
374 if (connection_)
375 static_cast<ViewTreeClientImpl*>(connection_)->SetFocus(id_);
378 bool View::HasFocus() const {
379 return connection_ && connection_->GetFocusedView() == this;
382 void View::Embed(mojo::ViewTreeClientPtr client) {
383 Embed(client.Pass(), mojo::ViewTree::ACCESS_POLICY_DEFAULT,
384 base::Bind(&EmptyEmbedCallback));
387 void View::Embed(mojo::ViewTreeClientPtr client,
388 uint32_t policy_bitmask,
389 const EmbedCallback& callback) {
390 if (PrepareForEmbed()) {
391 static_cast<ViewTreeClientImpl*>(connection_)
392 ->Embed(id_, client.Pass(), policy_bitmask, callback);
393 } else {
394 callback.Run(false, 0);
398 ////////////////////////////////////////////////////////////////////////////////
399 // View, protected:
401 namespace {
403 mojo::ViewportMetricsPtr CreateEmptyViewportMetrics() {
404 mojo::ViewportMetricsPtr metrics = mojo::ViewportMetrics::New();
405 metrics->size_in_pixels = mojo::Size::New();
406 // TODO(vtl): The |.Pass()| below is only needed due to an MSVS bug; remove it
407 // once that's fixed.
408 return metrics.Pass();
411 } // namespace
413 View::View()
414 : connection_(NULL),
415 id_(static_cast<Id>(-1)),
416 parent_(NULL),
417 viewport_metrics_(CreateEmptyViewportMetrics()),
418 visible_(true),
419 drawn_(false) {}
421 View::~View() {
422 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroying(this));
423 if (parent_)
424 parent_->LocalRemoveChild(this);
426 // We may still have children. This can happen if the embedder destroys the
427 // root while we're still alive.
428 while (!children_.empty()) {
429 View* child = children_.front();
430 LocalRemoveChild(child);
431 DCHECK(children_.empty() || children_.front() != child);
434 // TODO(beng): It'd be better to do this via a destruction observer in the
435 // ViewTreeClientImpl.
436 if (connection_)
437 static_cast<ViewTreeClientImpl*>(connection_)->RemoveView(id_);
439 // Clear properties.
440 for (auto& pair : prop_map_) {
441 if (pair.second.deallocator)
442 (*pair.second.deallocator)(pair.second.value);
444 prop_map_.clear();
446 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroyed(this));
448 if (connection_ && connection_->GetRoot() == this)
449 static_cast<ViewTreeClientImpl*>(connection_)->OnRootDestroyed(this);
452 ////////////////////////////////////////////////////////////////////////////////
453 // View, private:
455 View::View(ViewTreeConnection* connection, Id id)
456 : connection_(connection),
457 id_(id),
458 parent_(nullptr),
459 viewport_metrics_(CreateEmptyViewportMetrics()),
460 visible_(false),
461 drawn_(false) {}
463 int64 View::SetLocalPropertyInternal(const void* key,
464 const char* name,
465 PropertyDeallocator deallocator,
466 int64 value,
467 int64 default_value) {
468 int64 old = GetLocalPropertyInternal(key, default_value);
469 if (value == default_value) {
470 prop_map_.erase(key);
471 } else {
472 Value prop_value;
473 prop_value.name = name;
474 prop_value.value = value;
475 prop_value.deallocator = deallocator;
476 prop_map_[key] = prop_value;
478 FOR_EACH_OBSERVER(ViewObserver, observers_,
479 OnViewLocalPropertyChanged(this, key, old));
480 return old;
483 int64 View::GetLocalPropertyInternal(const void* key,
484 int64 default_value) const {
485 std::map<const void*, Value>::const_iterator iter = prop_map_.find(key);
486 if (iter == prop_map_.end())
487 return default_value;
488 return iter->second.value;
491 void View::LocalDestroy() {
492 delete this;
495 void View::LocalAddChild(View* child) {
496 ScopedTreeNotifier notifier(child, child->parent(), this);
497 if (child->parent())
498 RemoveChildImpl(child, &child->parent_->children_);
499 children_.push_back(child);
500 child->parent_ = this;
503 void View::LocalRemoveChild(View* child) {
504 DCHECK_EQ(this, child->parent());
505 ScopedTreeNotifier notifier(child, this, NULL);
506 RemoveChildImpl(child, &children_);
509 bool View::LocalReorder(View* relative, mojo::OrderDirection direction) {
510 return ReorderImpl(&parent_->children_, this, relative, direction);
513 void View::LocalSetBounds(const mojo::Rect& old_bounds,
514 const mojo::Rect& new_bounds) {
515 DCHECK(old_bounds.x == bounds_.x);
516 DCHECK(old_bounds.y == bounds_.y);
517 DCHECK(old_bounds.width == bounds_.width);
518 DCHECK(old_bounds.height == bounds_.height);
519 ScopedSetBoundsNotifier notifier(this, old_bounds, new_bounds);
520 bounds_ = new_bounds;
523 void View::LocalSetViewportMetrics(const mojo::ViewportMetrics& old_metrics,
524 const mojo::ViewportMetrics& new_metrics) {
525 // TODO(eseidel): We could check old_metrics against viewport_metrics_.
526 viewport_metrics_ = new_metrics.Clone();
527 FOR_EACH_OBSERVER(
528 ViewObserver, observers_,
529 OnViewViewportMetricsChanged(this, old_metrics, new_metrics));
532 void View::LocalSetDrawn(bool value) {
533 if (drawn_ == value)
534 return;
536 // As IsDrawn() is derived from |visible_| and |drawn_|, only send drawn
537 // notification is the value of IsDrawn() is really changing.
538 if (IsDrawn() == value) {
539 drawn_ = value;
540 return;
542 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDrawnChanging(this));
543 drawn_ = value;
544 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDrawnChanged(this));
547 void View::LocalSetVisible(bool visible) {
548 if (visible_ == visible)
549 return;
551 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewVisibilityChanging(this));
552 visible_ = visible;
553 NotifyViewVisibilityChanged(this);
556 void View::NotifyViewVisibilityChanged(View* target) {
557 if (!NotifyViewVisibilityChangedDown(target)) {
558 return; // |this| has been deleted.
560 NotifyViewVisibilityChangedUp(target);
563 bool View::NotifyViewVisibilityChangedAtReceiver(View* target) {
564 // |this| may be deleted during a call to OnViewVisibilityChanged() on one
565 // of the observers. We create an local observer for that. In that case we
566 // exit without further access to any members.
567 ViewTracker tracker;
568 tracker.Add(this);
569 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewVisibilityChanged(target));
570 return tracker.Contains(this);
573 bool View::NotifyViewVisibilityChangedDown(View* target) {
574 if (!NotifyViewVisibilityChangedAtReceiver(target))
575 return false; // |this| was deleted.
576 std::set<const View*> child_already_processed;
577 bool child_destroyed = false;
578 do {
579 child_destroyed = false;
580 for (View::Children::const_iterator it = children_.begin();
581 it != children_.end(); ++it) {
582 if (!child_already_processed.insert(*it).second)
583 continue;
584 if (!(*it)->NotifyViewVisibilityChangedDown(target)) {
585 // |*it| was deleted, |it| is invalid and |children_| has changed. We
586 // exit the current for-loop and enter a new one.
587 child_destroyed = true;
588 break;
591 } while (child_destroyed);
592 return true;
595 void View::NotifyViewVisibilityChangedUp(View* target) {
596 // Start with the parent as we already notified |this|
597 // in NotifyViewVisibilityChangedDown.
598 for (View* view = parent(); view; view = view->parent()) {
599 bool ret = view->NotifyViewVisibilityChangedAtReceiver(target);
600 DCHECK(ret);
604 bool View::PrepareForEmbed() {
605 if (!OwnsView(connection_, this) &&
606 !static_cast<ViewTreeClientImpl*>(connection_)->is_embed_root()) {
607 return false;
610 while (!children_.empty())
611 RemoveChild(children_[0]);
612 return true;
615 } // namespace mus