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"
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"
22 void NotifyViewTreeChangeAtReceiver(
24 const ViewObserver::TreeChangeParams
& params
,
25 bool change_applied
) {
26 ViewObserver::TreeChangeParams local_params
= params
;
27 local_params
.receiver
= receiver
;
29 FOR_EACH_OBSERVER(ViewObserver
, *ViewPrivate(receiver
).observers(),
30 OnTreeChanged(local_params
));
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
{
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); }
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()) {
83 ViewPrivate(child
).ClearParent();
87 class ScopedOrderChangedNotifier
{
89 ScopedOrderChangedNotifier(View
* 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_
));
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
,
113 mojo::OrderDirection direction
) {
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
) -
123 if ((direction
== mojo::ORDER_DIRECTION_ABOVE
&& child_i
== target_i
+ 1) ||
124 (direction
== mojo::ORDER_DIRECTION_BELOW
&& child_i
+ 1 == target_i
)) {
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
);
139 class ScopedSetBoundsNotifier
{
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_
));
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
) {}
171 ////////////////////////////////////////////////////////////////////////////////
174 void View::Destroy() {
175 if (!OwnsView(connection_
, this))
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());
187 DCHECK(std::find(children_
.begin(), children_
.end(), child
) ==
194 void View::SetBounds(const mojo::Rect
& bounds
) {
195 if (!OwnsView(connection_
, this))
198 if (bounds_
.Equals(bounds
))
202 static_cast<ViewTreeClientImpl
*>(connection_
)->SetBounds(id_
, bounds
);
203 LocalSetBounds(bounds_
, bounds
);
206 void View::SetVisible(bool value
) {
207 if (visible_
== value
)
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
=
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
)
238 // This property isn't set in |properties_| and |value| is NULL, so there's
244 properties_
[name
] = *value
;
245 } else if (it
!= properties_
.end()) {
246 properties_
.erase(it
);
249 // TODO: add test coverage of this (450303).
251 mojo::Array
<uint8_t> transport_value
;
253 transport_value
.resize(value
->size());
255 memcpy(&transport_value
.front(), &(value
->front()), value
->size());
257 static_cast<ViewTreeClientImpl
*>(connection_
)
258 ->SetProperty(id_
, name
, transport_value
.Pass());
262 ViewObserver
, observers_
,
263 OnViewSharedPropertyChanged(this, name
, old_value_ptr
, value
));
266 bool View::IsDrawn() const {
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())
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.
291 CHECK_EQ(child
->connection(), connection_
);
292 LocalAddChild(child
);
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.
301 CHECK_EQ(child
->connection(), connection_
);
302 LocalRemoveChild(child
);
304 static_cast<ViewTreeClientImpl
*>(connection_
)
305 ->RemoveChild(child
->id(), id_
);
309 void View::MoveToFront() {
310 if (!parent_
|| parent_
->children_
.back() == this)
312 Reorder(parent_
->children_
.back(), mojo::ORDER_DIRECTION_ABOVE
);
315 void View::MoveToBack() {
316 if (!parent_
|| parent_
->children_
.front() == this)
318 Reorder(parent_
->children_
.front(), mojo::ORDER_DIRECTION_BELOW
);
321 void View::Reorder(View
* relative
, mojo::OrderDirection direction
) {
322 if (!LocalReorder(relative
, direction
))
325 static_cast<ViewTreeClientImpl
*>(connection_
)
326 ->Reorder(id_
, relative
->id(), direction
);
330 bool View::Contains(View
* child
) const {
336 CHECK_EQ(child
->connection(), connection_
);
337 for (View
* p
= child
->parent(); p
; p
= p
->parent()) {
344 View
* View::GetChildById(Id id
) {
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
);
357 void View::SetTextInputState(mojo::TextInputStatePtr state
) {
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
);
368 static_cast<ViewTreeClientImpl
*>(connection_
)
369 ->SetImeVisibility(id_
, visible
, state
.Pass());
373 void View::SetFocus() {
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
);
394 callback
.Run(false, 0);
398 ////////////////////////////////////////////////////////////////////////////////
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();
415 id_(static_cast<Id
>(-1)),
417 viewport_metrics_(CreateEmptyViewportMetrics()),
422 FOR_EACH_OBSERVER(ViewObserver
, observers_
, OnViewDestroying(this));
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.
437 static_cast<ViewTreeClientImpl
*>(connection_
)->RemoveView(id_
);
440 for (auto& pair
: prop_map_
) {
441 if (pair
.second
.deallocator
)
442 (*pair
.second
.deallocator
)(pair
.second
.value
);
446 FOR_EACH_OBSERVER(ViewObserver
, observers_
, OnViewDestroyed(this));
448 if (connection_
&& connection_
->GetRoot() == this)
449 static_cast<ViewTreeClientImpl
*>(connection_
)->OnRootDestroyed(this);
452 ////////////////////////////////////////////////////////////////////////////////
455 View::View(ViewTreeConnection
* connection
, Id id
)
456 : connection_(connection
),
459 viewport_metrics_(CreateEmptyViewportMetrics()),
463 int64
View::SetLocalPropertyInternal(const void* key
,
465 PropertyDeallocator deallocator
,
467 int64 default_value
) {
468 int64 old
= GetLocalPropertyInternal(key
, default_value
);
469 if (value
== default_value
) {
470 prop_map_
.erase(key
);
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
));
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() {
495 void View::LocalAddChild(View
* child
) {
496 ScopedTreeNotifier
notifier(child
, child
->parent(), this);
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();
528 ViewObserver
, observers_
,
529 OnViewViewportMetricsChanged(this, old_metrics
, new_metrics
));
532 void View::LocalSetDrawn(bool value
) {
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
) {
542 FOR_EACH_OBSERVER(ViewObserver
, observers_
, OnViewDrawnChanging(this));
544 FOR_EACH_OBSERVER(ViewObserver
, observers_
, OnViewDrawnChanged(this));
547 void View::LocalSetVisible(bool visible
) {
548 if (visible_
== visible
)
551 FOR_EACH_OBSERVER(ViewObserver
, observers_
, OnViewVisibilityChanging(this));
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.
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;
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
)
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;
591 } while (child_destroyed
);
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
);
604 bool View::PrepareForEmbed() {
605 if (!OwnsView(connection_
, this) &&
606 !static_cast<ViewTreeClientImpl
*>(connection_
)->is_embed_root()) {
610 while (!children_
.empty())
611 RemoveChild(children_
[0]);