cygprofile: increase timeouts to allow showing web contents
[chromium-blink-merge.git] / components / view_manager / public / cpp / lib / view.cc
blob8de46f18ccd9e480a0dd43c6ea3e0e3310759afd
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/view_manager/public/cpp/view.h"
7 #include <set>
8 #include <string>
10 #include "components/view_manager/public/cpp/lib/view_private.h"
11 #include "components/view_manager/public/cpp/lib/view_tree_client_impl.h"
12 #include "components/view_manager/public/cpp/view_observer.h"
13 #include "components/view_manager/public/cpp/view_surface.h"
14 #include "components/view_manager/public/cpp/view_tracker.h"
15 #include "mojo/application/public/cpp/service_provider_impl.h"
17 namespace mojo {
19 namespace {
21 void NotifyViewTreeChangeAtReceiver(
22 View* receiver,
23 const ViewObserver::TreeChangeParams& params,
24 bool change_applied) {
25 ViewObserver::TreeChangeParams local_params = params;
26 local_params.receiver = receiver;
27 if (change_applied) {
28 FOR_EACH_OBSERVER(ViewObserver,
29 *ViewPrivate(receiver).observers(),
30 OnTreeChanged(local_params));
31 } else {
32 FOR_EACH_OBSERVER(ViewObserver,
33 *ViewPrivate(receiver).observers(),
34 OnTreeChanging(local_params));
38 void NotifyViewTreeChangeUp(
39 View* start_at,
40 const ViewObserver::TreeChangeParams& params,
41 bool change_applied) {
42 for (View* current = start_at; current; current = current->parent())
43 NotifyViewTreeChangeAtReceiver(current, params, change_applied);
46 void NotifyViewTreeChangeDown(
47 View* start_at,
48 const ViewObserver::TreeChangeParams& params,
49 bool change_applied) {
50 NotifyViewTreeChangeAtReceiver(start_at, params, change_applied);
51 View::Children::const_iterator it = start_at->children().begin();
52 for (; it != start_at->children().end(); ++it)
53 NotifyViewTreeChangeDown(*it, params, change_applied);
56 void NotifyViewTreeChange(
57 const ViewObserver::TreeChangeParams& params,
58 bool change_applied) {
59 NotifyViewTreeChangeDown(params.target, params, change_applied);
60 if (params.old_parent)
61 NotifyViewTreeChangeUp(params.old_parent, params, change_applied);
62 if (params.new_parent)
63 NotifyViewTreeChangeUp(params.new_parent, params, change_applied);
66 class ScopedTreeNotifier {
67 public:
68 ScopedTreeNotifier(View* target, View* old_parent, View* new_parent) {
69 params_.target = target;
70 params_.old_parent = old_parent;
71 params_.new_parent = new_parent;
72 NotifyViewTreeChange(params_, false);
74 ~ScopedTreeNotifier() {
75 NotifyViewTreeChange(params_, true);
78 private:
79 ViewObserver::TreeChangeParams params_;
81 MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedTreeNotifier);
84 void RemoveChildImpl(View* child, View::Children* children) {
85 View::Children::iterator it =
86 std::find(children->begin(), children->end(), child);
87 if (it != children->end()) {
88 children->erase(it);
89 ViewPrivate(child).ClearParent();
93 class ScopedOrderChangedNotifier {
94 public:
95 ScopedOrderChangedNotifier(View* view,
96 View* relative_view,
97 OrderDirection direction)
98 : view_(view),
99 relative_view_(relative_view),
100 direction_(direction) {
101 FOR_EACH_OBSERVER(ViewObserver,
102 *ViewPrivate(view_).observers(),
103 OnViewReordering(view_, relative_view_, direction_));
105 ~ScopedOrderChangedNotifier() {
106 FOR_EACH_OBSERVER(ViewObserver,
107 *ViewPrivate(view_).observers(),
108 OnViewReordered(view_, relative_view_, direction_));
111 private:
112 View* view_;
113 View* relative_view_;
114 OrderDirection direction_;
116 MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedOrderChangedNotifier);
119 // Returns true if the order actually changed.
120 bool ReorderImpl(View::Children* children,
121 View* view,
122 View* relative,
123 OrderDirection direction) {
124 DCHECK(relative);
125 DCHECK_NE(view, relative);
126 DCHECK_EQ(view->parent(), relative->parent());
128 const size_t child_i =
129 std::find(children->begin(), children->end(), view) - children->begin();
130 const size_t target_i =
131 std::find(children->begin(), children->end(), relative) -
132 children->begin();
133 if ((direction == ORDER_DIRECTION_ABOVE && child_i == target_i + 1) ||
134 (direction == ORDER_DIRECTION_BELOW && child_i + 1 == target_i)) {
135 return false;
138 ScopedOrderChangedNotifier notifier(view, relative, direction);
140 const size_t dest_i = direction == ORDER_DIRECTION_ABOVE
141 ? (child_i < target_i ? target_i : target_i + 1)
142 : (child_i < target_i ? target_i - 1 : target_i);
143 children->erase(children->begin() + child_i);
144 children->insert(children->begin() + dest_i, view);
146 return true;
149 class ScopedSetBoundsNotifier {
150 public:
151 ScopedSetBoundsNotifier(View* view,
152 const Rect& old_bounds,
153 const Rect& new_bounds)
154 : view_(view),
155 old_bounds_(old_bounds),
156 new_bounds_(new_bounds) {
157 FOR_EACH_OBSERVER(ViewObserver,
158 *ViewPrivate(view_).observers(),
159 OnViewBoundsChanging(view_, old_bounds_, new_bounds_));
161 ~ScopedSetBoundsNotifier() {
162 FOR_EACH_OBSERVER(ViewObserver,
163 *ViewPrivate(view_).observers(),
164 OnViewBoundsChanged(view_, old_bounds_, new_bounds_));
167 private:
168 View* view_;
169 const Rect old_bounds_;
170 const Rect new_bounds_;
172 MOJO_DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier);
175 // Some operations are only permitted in the connection that created the view.
176 bool OwnsView(ViewTreeConnection* connection, View* view) {
177 return !connection ||
178 static_cast<ViewTreeClientImpl*>(connection)->OwnsView(view->id());
181 } // namespace
183 ////////////////////////////////////////////////////////////////////////////////
184 // View, public:
186 void View::Destroy() {
187 if (!OwnsView(connection_, this))
188 return;
190 if (connection_)
191 static_cast<ViewTreeClientImpl*>(connection_)->DestroyView(id_);
192 while (!children_.empty()) {
193 View* child = children_.front();
194 if (!OwnsView(connection_, child)) {
195 ViewPrivate(child).ClearParent();
196 children_.erase(children_.begin());
197 } else {
198 child->Destroy();
199 DCHECK(std::find(children_.begin(), children_.end(), child) ==
200 children_.end());
203 LocalDestroy();
206 void View::SetBounds(const Rect& bounds) {
207 if (!OwnsView(connection_, this))
208 return;
210 if (bounds_.Equals(bounds))
211 return;
213 if (connection_)
214 static_cast<ViewTreeClientImpl*>(connection_)->SetBounds(id_, bounds);
215 LocalSetBounds(bounds_, bounds);
218 void View::SetVisible(bool value) {
219 if (visible_ == value)
220 return;
222 if (connection_)
223 static_cast<ViewTreeClientImpl*>(connection_)->SetVisible(id_, value);
224 LocalSetVisible(value);
227 scoped_ptr<mojo::ViewSurface> View::RequestSurface() {
228 mojo::SurfacePtr surface;
229 mojo::SurfaceClientPtr client;
230 mojo::InterfaceRequest<SurfaceClient> client_request = GetProxy(&client);
231 static_cast<ViewTreeClientImpl*>(connection_)->RequestSurface(
232 id_, GetProxy(&surface), client.Pass());
233 return make_scoped_ptr(new mojo::ViewSurface(surface.PassInterface(),
234 client_request.Pass()));
237 void View::SetSharedProperty(const std::string& name,
238 const std::vector<uint8_t>* value) {
239 std::vector<uint8_t> old_value;
240 std::vector<uint8_t>* old_value_ptr = nullptr;
241 auto it = properties_.find(name);
242 if (it != properties_.end()) {
243 old_value = it->second;
244 old_value_ptr = &old_value;
246 if (value && old_value == *value)
247 return;
248 } else if (!value) {
249 // This property isn't set in |properties_| and |value| is NULL, so there's
250 // no change.
251 return;
254 if (value) {
255 properties_[name] = *value;
256 } else if (it != properties_.end()) {
257 properties_.erase(it);
260 // TODO: add test coverage of this (450303).
261 if (connection_) {
262 Array<uint8_t> transport_value;
263 if (value) {
264 transport_value.resize(value->size());
265 if (value->size())
266 memcpy(&transport_value.front(), &(value->front()), value->size());
268 static_cast<ViewTreeClientImpl*>(connection_)->SetProperty(
269 id_, name, transport_value.Pass());
272 FOR_EACH_OBSERVER(
273 ViewObserver, observers_,
274 OnViewSharedPropertyChanged(this, name, old_value_ptr, value));
277 bool View::IsDrawn() const {
278 if (!visible_)
279 return false;
280 return parent_ ? parent_->IsDrawn() : drawn_;
283 void View::SetAccessPolicy(uint32_t policy_bitmask) {
284 if (connection_) {
285 static_cast<ViewTreeClientImpl*>(connection_)
286 ->SetAccessPolicy(id_, policy_bitmask);
290 void View::AddObserver(ViewObserver* observer) {
291 observers_.AddObserver(observer);
294 void View::RemoveObserver(ViewObserver* observer) {
295 observers_.RemoveObserver(observer);
298 const View* View::GetRoot() const {
299 const View* root = this;
300 for (const View* parent = this; parent; parent = parent->parent())
301 root = parent;
302 return root;
305 void View::AddChild(View* child) {
306 // TODO(beng): not necessarily valid to all connections, but possibly to the
307 // embeddee in an embedder-embeddee relationship.
308 if (connection_)
309 CHECK_EQ(child->connection(), connection_);
310 LocalAddChild(child);
311 if (connection_)
312 static_cast<ViewTreeClientImpl*>(connection_)->AddChild(child->id(), id_);
315 void View::RemoveChild(View* child) {
316 // TODO(beng): not necessarily valid to all connections, but possibly to the
317 // embeddee in an embedder-embeddee relationship.
318 if (connection_)
319 CHECK_EQ(child->connection(), connection_);
320 LocalRemoveChild(child);
321 if (connection_) {
322 static_cast<ViewTreeClientImpl*>(connection_)->RemoveChild(child->id(),
323 id_);
327 void View::MoveToFront() {
328 if (!parent_ || parent_->children_.back() == this)
329 return;
330 Reorder(parent_->children_.back(), ORDER_DIRECTION_ABOVE);
333 void View::MoveToBack() {
334 if (!parent_ || parent_->children_.front() == this)
335 return;
336 Reorder(parent_->children_.front(), ORDER_DIRECTION_BELOW);
339 void View::Reorder(View* relative, OrderDirection direction) {
340 if (!LocalReorder(relative, direction))
341 return;
342 if (connection_) {
343 static_cast<ViewTreeClientImpl*>(connection_)->Reorder(id_, relative->id(),
344 direction);
348 bool View::Contains(View* child) const {
349 if (!child)
350 return false;
351 if (child == this)
352 return true;
353 if (connection_)
354 CHECK_EQ(child->connection(), connection_);
355 for (View* p = child->parent(); p; p = p->parent()) {
356 if (p == this)
357 return true;
359 return false;
362 View* View::GetChildById(Id id) {
363 if (id == id_)
364 return this;
365 // TODO(beng): this could be improved depending on how we decide to own views.
366 Children::const_iterator it = children_.begin();
367 for (; it != children_.end(); ++it) {
368 View* view = (*it)->GetChildById(id);
369 if (view)
370 return view;
372 return NULL;
375 void View::SetTextInputState(TextInputStatePtr state) {
376 if (connection_) {
377 static_cast<ViewTreeClientImpl*>(connection_)
378 ->SetViewTextInputState(id_, state.Pass());
382 void View::SetImeVisibility(bool visible, TextInputStatePtr state) {
383 // SetImeVisibility() shouldn't be used if the view is not editable.
384 DCHECK(state.is_null() || state->type != TEXT_INPUT_TYPE_NONE);
385 if (connection_) {
386 static_cast<ViewTreeClientImpl*>(connection_)
387 ->SetImeVisibility(id_, visible, state.Pass());
391 void View::SetFocus() {
392 if (connection_)
393 static_cast<ViewTreeClientImpl*>(connection_)->SetFocus(id_);
396 bool View::HasFocus() const {
397 return connection_ && connection_->GetFocusedView() == this;
400 void View::Embed(ViewTreeClientPtr client) {
401 if (PrepareForEmbed())
402 static_cast<ViewTreeClientImpl*>(connection_)->Embed(id_, client.Pass());
405 ////////////////////////////////////////////////////////////////////////////////
406 // View, protected:
408 namespace {
410 ViewportMetricsPtr CreateEmptyViewportMetrics() {
411 ViewportMetricsPtr metrics = ViewportMetrics::New();
412 metrics->size_in_pixels = Size::New();
413 // TODO(vtl): The |.Pass()| below is only needed due to an MSVS bug; remove it
414 // once that's fixed.
415 return metrics.Pass();
418 } // namespace
420 View::View()
421 : connection_(NULL),
422 id_(static_cast<Id>(-1)),
423 parent_(NULL),
424 viewport_metrics_(CreateEmptyViewportMetrics()),
425 visible_(true),
426 drawn_(false) {
429 View::~View() {
430 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroying(this));
431 if (parent_)
432 parent_->LocalRemoveChild(this);
434 // We may still have children. This can happen if the embedder destroys the
435 // root while we're still alive.
436 while (!children_.empty()) {
437 View* child = children_.front();
438 LocalRemoveChild(child);
439 DCHECK(children_.empty() || children_.front() != child);
442 // TODO(beng): It'd be better to do this via a destruction observer in the
443 // ViewTreeClientImpl.
444 if (connection_)
445 static_cast<ViewTreeClientImpl*>(connection_)->RemoveView(id_);
447 // Clear properties.
448 for (auto& pair : prop_map_) {
449 if (pair.second.deallocator)
450 (*pair.second.deallocator)(pair.second.value);
452 prop_map_.clear();
454 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroyed(this));
456 if (connection_ && connection_->GetRoot() == this)
457 static_cast<ViewTreeClientImpl*>(connection_)->OnRootDestroyed(this);
460 ////////////////////////////////////////////////////////////////////////////////
461 // View, private:
463 View::View(ViewTreeConnection* connection, Id id)
464 : connection_(connection),
465 id_(id),
466 parent_(nullptr),
467 viewport_metrics_(CreateEmptyViewportMetrics()),
468 visible_(false),
469 drawn_(false) {
472 int64 View::SetLocalPropertyInternal(const void* key,
473 const char* name,
474 PropertyDeallocator deallocator,
475 int64 value,
476 int64 default_value) {
477 int64 old = GetLocalPropertyInternal(key, default_value);
478 if (value == default_value) {
479 prop_map_.erase(key);
480 } else {
481 Value prop_value;
482 prop_value.name = name;
483 prop_value.value = value;
484 prop_value.deallocator = deallocator;
485 prop_map_[key] = prop_value;
487 FOR_EACH_OBSERVER(ViewObserver, observers_,
488 OnViewLocalPropertyChanged(this, key, old));
489 return old;
492 int64 View::GetLocalPropertyInternal(const void* key,
493 int64 default_value) const {
494 std::map<const void*, Value>::const_iterator iter = prop_map_.find(key);
495 if (iter == prop_map_.end())
496 return default_value;
497 return iter->second.value;
500 void View::LocalDestroy() {
501 delete this;
504 void View::LocalAddChild(View* child) {
505 ScopedTreeNotifier notifier(child, child->parent(), this);
506 if (child->parent())
507 RemoveChildImpl(child, &child->parent_->children_);
508 children_.push_back(child);
509 child->parent_ = this;
512 void View::LocalRemoveChild(View* child) {
513 DCHECK_EQ(this, child->parent());
514 ScopedTreeNotifier notifier(child, this, NULL);
515 RemoveChildImpl(child, &children_);
518 bool View::LocalReorder(View* relative, OrderDirection direction) {
519 return ReorderImpl(&parent_->children_, this, relative, direction);
522 void View::LocalSetBounds(const Rect& old_bounds,
523 const Rect& new_bounds) {
524 DCHECK(old_bounds.x == bounds_.x);
525 DCHECK(old_bounds.y == bounds_.y);
526 DCHECK(old_bounds.width == bounds_.width);
527 DCHECK(old_bounds.height == bounds_.height);
528 ScopedSetBoundsNotifier notifier(this, old_bounds, new_bounds);
529 bounds_ = new_bounds;
532 void View::LocalSetViewportMetrics(const ViewportMetrics& old_metrics,
533 const ViewportMetrics& new_metrics) {
534 // TODO(eseidel): We could check old_metrics against viewport_metrics_.
535 viewport_metrics_ = new_metrics.Clone();
536 FOR_EACH_OBSERVER(
537 ViewObserver, observers_,
538 OnViewViewportMetricsChanged(this, old_metrics, new_metrics));
541 void View::LocalSetDrawn(bool value) {
542 if (drawn_ == value)
543 return;
545 // As IsDrawn() is derived from |visible_| and |drawn_|, only send drawn
546 // notification is the value of IsDrawn() is really changing.
547 if (IsDrawn() == value) {
548 drawn_ = value;
549 return;
551 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDrawnChanging(this));
552 drawn_ = value;
553 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDrawnChanged(this));
556 void View::LocalSetVisible(bool visible) {
557 if (visible_ == visible)
558 return;
560 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewVisibilityChanging(this));
561 visible_ = visible;
562 NotifyViewVisibilityChanged(this);
565 void View::NotifyViewVisibilityChanged(View* target) {
566 if (!NotifyViewVisibilityChangedDown(target)) {
567 return; // |this| has been deleted.
569 NotifyViewVisibilityChangedUp(target);
572 bool View::NotifyViewVisibilityChangedAtReceiver(View* target) {
573 // |this| may be deleted during a call to OnViewVisibilityChanged() on one
574 // of the observers. We create an local observer for that. In that case we
575 // exit without further access to any members.
576 ViewTracker tracker;
577 tracker.Add(this);
578 FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewVisibilityChanged(target));
579 return tracker.Contains(this);
582 bool View::NotifyViewVisibilityChangedDown(View* target) {
583 if (!NotifyViewVisibilityChangedAtReceiver(target))
584 return false; // |this| was deleted.
585 std::set<const View*> child_already_processed;
586 bool child_destroyed = false;
587 do {
588 child_destroyed = false;
589 for (View::Children::const_iterator it = children_.begin();
590 it != children_.end(); ++it) {
591 if (!child_already_processed.insert(*it).second)
592 continue;
593 if (!(*it)->NotifyViewVisibilityChangedDown(target)) {
594 // |*it| was deleted, |it| is invalid and |children_| has changed. We
595 // exit the current for-loop and enter a new one.
596 child_destroyed = true;
597 break;
600 } while (child_destroyed);
601 return true;
604 void View::NotifyViewVisibilityChangedUp(View* target) {
605 // Start with the parent as we already notified |this|
606 // in NotifyViewVisibilityChangedDown.
607 for (View* view = parent(); view; view = view->parent()) {
608 bool ret = view->NotifyViewVisibilityChangedAtReceiver(target);
609 DCHECK(ret);
613 bool View::PrepareForEmbed() {
614 if (!OwnsView(connection_, this) &&
615 !static_cast<ViewTreeClientImpl*>(connection_)->is_embed_root()) {
616 return false;
619 while (!children_.empty())
620 RemoveChild(children_[0]);
621 return true;
624 } // namespace mojo