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"
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"
21 void NotifyViewTreeChangeAtReceiver(
23 const ViewObserver::TreeChangeParams
& params
,
24 bool change_applied
) {
25 ViewObserver::TreeChangeParams local_params
= params
;
26 local_params
.receiver
= receiver
;
28 FOR_EACH_OBSERVER(ViewObserver
,
29 *ViewPrivate(receiver
).observers(),
30 OnTreeChanged(local_params
));
32 FOR_EACH_OBSERVER(ViewObserver
,
33 *ViewPrivate(receiver
).observers(),
34 OnTreeChanging(local_params
));
38 void NotifyViewTreeChangeUp(
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(
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
{
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);
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()) {
89 ViewPrivate(child
).ClearParent();
93 class ScopedOrderChangedNotifier
{
95 ScopedOrderChangedNotifier(View
* view
,
97 OrderDirection direction
)
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_
));
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
,
123 OrderDirection direction
) {
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
) -
133 if ((direction
== ORDER_DIRECTION_ABOVE
&& child_i
== target_i
+ 1) ||
134 (direction
== ORDER_DIRECTION_BELOW
&& child_i
+ 1 == target_i
)) {
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
);
149 class ScopedSetBoundsNotifier
{
151 ScopedSetBoundsNotifier(View
* view
,
152 const Rect
& old_bounds
,
153 const Rect
& new_bounds
)
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_
));
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());
183 ////////////////////////////////////////////////////////////////////////////////
186 void View::Destroy() {
187 if (!OwnsView(connection_
, this))
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());
199 DCHECK(std::find(children_
.begin(), children_
.end(), child
) ==
206 void View::SetBounds(const Rect
& bounds
) {
207 if (!OwnsView(connection_
, this))
210 if (bounds_
.Equals(bounds
))
214 static_cast<ViewTreeClientImpl
*>(connection_
)->SetBounds(id_
, bounds
);
215 LocalSetBounds(bounds_
, bounds
);
218 void View::SetVisible(bool value
) {
219 if (visible_
== value
)
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
)
249 // This property isn't set in |properties_| and |value| is NULL, so there's
255 properties_
[name
] = *value
;
256 } else if (it
!= properties_
.end()) {
257 properties_
.erase(it
);
260 // TODO: add test coverage of this (450303).
262 Array
<uint8_t> transport_value
;
264 transport_value
.resize(value
->size());
266 memcpy(&transport_value
.front(), &(value
->front()), value
->size());
268 static_cast<ViewTreeClientImpl
*>(connection_
)->SetProperty(
269 id_
, name
, transport_value
.Pass());
273 ViewObserver
, observers_
,
274 OnViewSharedPropertyChanged(this, name
, old_value_ptr
, value
));
277 bool View::IsDrawn() const {
280 return parent_
? parent_
->IsDrawn() : drawn_
;
283 void View::SetAccessPolicy(uint32_t policy_bitmask
) {
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())
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.
309 CHECK_EQ(child
->connection(), connection_
);
310 LocalAddChild(child
);
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.
319 CHECK_EQ(child
->connection(), connection_
);
320 LocalRemoveChild(child
);
322 static_cast<ViewTreeClientImpl
*>(connection_
)->RemoveChild(child
->id(),
327 void View::MoveToFront() {
328 if (!parent_
|| parent_
->children_
.back() == this)
330 Reorder(parent_
->children_
.back(), ORDER_DIRECTION_ABOVE
);
333 void View::MoveToBack() {
334 if (!parent_
|| parent_
->children_
.front() == this)
336 Reorder(parent_
->children_
.front(), ORDER_DIRECTION_BELOW
);
339 void View::Reorder(View
* relative
, OrderDirection direction
) {
340 if (!LocalReorder(relative
, direction
))
343 static_cast<ViewTreeClientImpl
*>(connection_
)->Reorder(id_
, relative
->id(),
348 bool View::Contains(View
* child
) const {
354 CHECK_EQ(child
->connection(), connection_
);
355 for (View
* p
= child
->parent(); p
; p
= p
->parent()) {
362 View
* View::GetChildById(Id id
) {
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
);
375 void View::SetTextInputState(TextInputStatePtr state
) {
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
);
386 static_cast<ViewTreeClientImpl
*>(connection_
)
387 ->SetImeVisibility(id_
, visible
, state
.Pass());
391 void View::SetFocus() {
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 ////////////////////////////////////////////////////////////////////////////////
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();
422 id_(static_cast<Id
>(-1)),
424 viewport_metrics_(CreateEmptyViewportMetrics()),
430 FOR_EACH_OBSERVER(ViewObserver
, observers_
, OnViewDestroying(this));
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.
445 static_cast<ViewTreeClientImpl
*>(connection_
)->RemoveView(id_
);
448 for (auto& pair
: prop_map_
) {
449 if (pair
.second
.deallocator
)
450 (*pair
.second
.deallocator
)(pair
.second
.value
);
454 FOR_EACH_OBSERVER(ViewObserver
, observers_
, OnViewDestroyed(this));
456 if (connection_
&& connection_
->GetRoot() == this)
457 static_cast<ViewTreeClientImpl
*>(connection_
)->OnRootDestroyed(this);
460 ////////////////////////////////////////////////////////////////////////////////
463 View::View(ViewTreeConnection
* connection
, Id id
)
464 : connection_(connection
),
467 viewport_metrics_(CreateEmptyViewportMetrics()),
472 int64
View::SetLocalPropertyInternal(const void* key
,
474 PropertyDeallocator deallocator
,
476 int64 default_value
) {
477 int64 old
= GetLocalPropertyInternal(key
, default_value
);
478 if (value
== default_value
) {
479 prop_map_
.erase(key
);
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
));
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() {
504 void View::LocalAddChild(View
* child
) {
505 ScopedTreeNotifier
notifier(child
, child
->parent(), this);
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();
537 ViewObserver
, observers_
,
538 OnViewViewportMetricsChanged(this, old_metrics
, new_metrics
));
541 void View::LocalSetDrawn(bool value
) {
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
) {
551 FOR_EACH_OBSERVER(ViewObserver
, observers_
, OnViewDrawnChanging(this));
553 FOR_EACH_OBSERVER(ViewObserver
, observers_
, OnViewDrawnChanged(this));
556 void View::LocalSetVisible(bool visible
) {
557 if (visible_
== visible
)
560 FOR_EACH_OBSERVER(ViewObserver
, observers_
, OnViewVisibilityChanging(this));
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.
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;
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
)
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;
600 } while (child_destroyed
);
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
);
613 bool View::PrepareForEmbed() {
614 if (!OwnsView(connection_
, this) &&
615 !static_cast<ViewTreeClientImpl
*>(connection_
)->is_embed_root()) {
619 while (!children_
.empty())
620 RemoveChild(children_
[0]);