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/lib/view_tree_client_impl.h"
7 #include "components/mus/public/cpp/lib/view_private.h"
8 #include "components/mus/public/cpp/util.h"
9 #include "components/mus/public/cpp/view_observer.h"
10 #include "components/mus/public/cpp/view_tree_connection.h"
11 #include "components/mus/public/cpp/view_tree_delegate.h"
12 #include "mojo/application/public/cpp/application_impl.h"
13 #include "mojo/application/public/cpp/connect.h"
14 #include "mojo/application/public/cpp/service_provider_impl.h"
15 #include "mojo/application/public/interfaces/service_provider.mojom.h"
19 Id
MakeTransportId(ConnectionSpecificId connection_id
,
20 ConnectionSpecificId local_id
) {
21 return (connection_id
<< 16) | local_id
;
24 // Helper called to construct a local view object from transport data.
25 View
* AddViewToConnection(ViewTreeClientImpl
* client
,
27 const mojo::ViewDataPtr
& view_data
) {
28 // We don't use the cto that takes a ViewTreeConnection here, since it will
29 // call back to the service and attempt to create a new view.
30 View
* view
= ViewPrivate::LocalCreate();
31 ViewPrivate
private_view(view
);
32 private_view
.set_connection(client
);
33 private_view
.set_id(view_data
->view_id
);
34 private_view
.set_visible(view_data
->visible
);
35 private_view
.set_drawn(view_data
->drawn
);
36 private_view
.LocalSetViewportMetrics(mojo::ViewportMetrics(),
37 *view_data
->viewport_metrics
);
38 private_view
.set_properties(
39 view_data
->properties
.To
<std::map
<std::string
, std::vector
<uint8_t>>>());
40 client
->AddView(view
);
41 private_view
.LocalSetBounds(mojo::Rect(), *view_data
->bounds
);
43 ViewPrivate(parent
).LocalAddChild(view
);
47 View
* BuildViewTree(ViewTreeClientImpl
* client
,
48 const mojo::Array
<mojo::ViewDataPtr
>& views
,
49 View
* initial_parent
) {
50 std::vector
<View
*> parents
;
52 View
* last_view
= NULL
;
54 parents
.push_back(initial_parent
);
55 for (size_t i
= 0; i
< views
.size(); ++i
) {
56 if (last_view
&& views
[i
]->parent_id
== last_view
->id()) {
57 parents
.push_back(last_view
);
58 } else if (!parents
.empty()) {
59 while (parents
.back()->id() != views
[i
]->parent_id
)
62 View
* view
= AddViewToConnection(
63 client
, !parents
.empty() ? parents
.back() : NULL
, views
[i
]);
71 ViewTreeConnection
* ViewTreeConnection::Create(
72 ViewTreeDelegate
* delegate
,
73 mojo::InterfaceRequest
<mojo::ViewTreeClient
> request
) {
74 return new ViewTreeClientImpl(delegate
, request
.Pass());
77 ViewTreeClientImpl::ViewTreeClientImpl(
78 ViewTreeDelegate
* delegate
,
79 mojo::InterfaceRequest
<mojo::ViewTreeClient
> request
)
84 capture_view_(nullptr),
85 focused_view_(nullptr),
86 activated_view_(nullptr),
87 binding_(this, request
.Pass()),
88 is_embed_root_(false),
89 in_destructor_(false) {}
91 ViewTreeClientImpl::~ViewTreeClientImpl() {
92 in_destructor_
= true;
94 std::vector
<View
*> non_owned
;
95 while (!views_
.empty()) {
96 IdToViewMap::iterator it
= views_
.begin();
97 if (OwnsView(it
->second
->id())) {
98 it
->second
->Destroy();
100 non_owned
.push_back(it
->second
);
105 // Delete the non-owned views last. In the typical case these are roots. The
106 // exception is the window manager and embed roots, which may know about
107 // other random views that it doesn't own.
108 // NOTE: we manually delete as we're a friend.
109 for (size_t i
= 0; i
< non_owned
.size(); ++i
)
112 delegate_
->OnConnectionLost(this);
115 void ViewTreeClientImpl::DestroyView(Id view_id
) {
117 tree_
->DeleteView(view_id
, ActionCompletedCallback());
120 void ViewTreeClientImpl::AddChild(Id child_id
, Id parent_id
) {
122 tree_
->AddView(parent_id
, child_id
, ActionCompletedCallback());
125 void ViewTreeClientImpl::RemoveChild(Id child_id
, Id parent_id
) {
127 tree_
->RemoveViewFromParent(child_id
, ActionCompletedCallback());
130 void ViewTreeClientImpl::Reorder(Id view_id
,
132 mojo::OrderDirection direction
) {
134 tree_
->ReorderView(view_id
, relative_view_id
, direction
,
135 ActionCompletedCallback());
138 bool ViewTreeClientImpl::OwnsView(Id id
) const {
139 return HiWord(id
) == connection_id_
;
142 void ViewTreeClientImpl::SetBounds(Id view_id
, const mojo::Rect
& bounds
) {
144 tree_
->SetViewBounds(view_id
, bounds
.Clone(), ActionCompletedCallback());
147 void ViewTreeClientImpl::SetFocus(Id view_id
) {
148 // In order for us to get here we had to have exposed a view, which implies we
151 tree_
->SetFocus(view_id
);
154 void ViewTreeClientImpl::SetVisible(Id view_id
, bool visible
) {
156 tree_
->SetViewVisibility(view_id
, visible
, ActionCompletedCallback());
159 void ViewTreeClientImpl::SetProperty(Id view_id
,
160 const std::string
& name
,
161 const std::vector
<uint8_t>& data
) {
163 tree_
->SetViewProperty(view_id
, mojo::String(name
),
164 mojo::Array
<uint8_t>::From(data
),
165 ActionCompletedCallback());
168 void ViewTreeClientImpl::SetViewTextInputState(Id view_id
,
169 mojo::TextInputStatePtr state
) {
171 tree_
->SetViewTextInputState(view_id
, state
.Pass());
174 void ViewTreeClientImpl::SetImeVisibility(Id view_id
,
176 mojo::TextInputStatePtr state
) {
178 tree_
->SetImeVisibility(view_id
, visible
, state
.Pass());
181 void ViewTreeClientImpl::Embed(Id view_id
,
182 mojo::ViewTreeClientPtr client
,
183 uint32_t policy_bitmask
,
184 const mojo::ViewTree::EmbedCallback
& callback
) {
186 tree_
->Embed(view_id
, client
.Pass(), policy_bitmask
, callback
);
189 void ViewTreeClientImpl::RequestSurface(
191 mojo::InterfaceRequest
<mojo::Surface
> surface
,
192 mojo::SurfaceClientPtr client
) {
194 tree_
->RequestSurface(view_id
, surface
.Pass(), client
.Pass());
197 void ViewTreeClientImpl::AddView(View
* view
) {
198 DCHECK(views_
.find(view
->id()) == views_
.end());
199 views_
[view
->id()] = view
;
202 void ViewTreeClientImpl::RemoveView(Id view_id
) {
203 if (focused_view_
&& focused_view_
->id() == view_id
)
206 IdToViewMap::iterator it
= views_
.find(view_id
);
207 if (it
!= views_
.end())
211 void ViewTreeClientImpl::OnRootDestroyed(View
* root
) {
212 DCHECK_EQ(root
, root_
);
215 // When the root is gone we can't do anything useful.
220 ////////////////////////////////////////////////////////////////////////////////
221 // ViewTreeClientImpl, ViewTreeConnection implementation:
223 Id
ViewTreeClientImpl::CreateViewOnServer() {
225 const Id view_id
= MakeTransportId(connection_id_
, ++next_id_
);
226 tree_
->CreateView(view_id
, [this](mojo::ErrorCode code
) {
227 OnActionCompleted(code
== mojo::ERROR_CODE_NONE
);
232 View
* ViewTreeClientImpl::GetRoot() {
236 View
* ViewTreeClientImpl::GetViewById(Id id
) {
237 IdToViewMap::const_iterator it
= views_
.find(id
);
238 return it
!= views_
.end() ? it
->second
: NULL
;
241 View
* ViewTreeClientImpl::GetFocusedView() {
242 return focused_view_
;
245 View
* ViewTreeClientImpl::CreateView() {
246 View
* view
= new View(this, CreateViewOnServer());
251 bool ViewTreeClientImpl::IsEmbedRoot() {
252 return is_embed_root_
;
255 ConnectionSpecificId
ViewTreeClientImpl::GetConnectionId() {
256 return connection_id_
;
259 ////////////////////////////////////////////////////////////////////////////////
260 // ViewTreeClientImpl, ViewTreeClient implementation:
262 void ViewTreeClientImpl::OnEmbed(ConnectionSpecificId connection_id
,
263 mojo::ViewDataPtr root_data
,
264 mojo::ViewTreePtr tree
,
266 uint32 access_policy
) {
270 tree_
.set_connection_error_handler([this]() { delete this; });
272 connection_id_
= connection_id
;
274 (access_policy
& mojo::ViewTree::ACCESS_POLICY_EMBED_ROOT
) != 0;
277 root_
= AddViewToConnection(this, nullptr, root_data
);
279 focused_view_
= GetViewById(focused_view_id
);
281 delegate_
->OnEmbed(root_
);
284 void ViewTreeClientImpl::OnEmbeddedAppDisconnected(Id view_id
) {
285 View
* view
= GetViewById(view_id
);
287 FOR_EACH_OBSERVER(ViewObserver
, *ViewPrivate(view
).observers(),
288 OnViewEmbeddedAppDisconnected(view
));
292 void ViewTreeClientImpl::OnUnembed() {
293 delegate_
->OnUnembed();
294 // This will send out the various notifications.
298 void ViewTreeClientImpl::OnViewBoundsChanged(Id view_id
,
299 mojo::RectPtr old_bounds
,
300 mojo::RectPtr new_bounds
) {
301 View
* view
= GetViewById(view_id
);
302 ViewPrivate(view
).LocalSetBounds(*old_bounds
, *new_bounds
);
307 void SetViewportMetricsOnDecendants(View
* root
,
308 const mojo::ViewportMetrics
& old_metrics
,
309 const mojo::ViewportMetrics
& new_metrics
) {
310 ViewPrivate(root
).LocalSetViewportMetrics(old_metrics
, new_metrics
);
311 const View::Children
& children
= root
->children();
312 for (size_t i
= 0; i
< children
.size(); ++i
)
313 SetViewportMetricsOnDecendants(children
[i
], old_metrics
, new_metrics
);
317 void ViewTreeClientImpl::OnViewViewportMetricsChanged(
318 mojo::ViewportMetricsPtr old_metrics
,
319 mojo::ViewportMetricsPtr new_metrics
) {
320 View
* view
= GetRoot();
322 SetViewportMetricsOnDecendants(view
, *old_metrics
, *new_metrics
);
325 void ViewTreeClientImpl::OnViewHierarchyChanged(
329 mojo::Array
<mojo::ViewDataPtr
> views
) {
330 View
* initial_parent
= views
.size() ? GetViewById(views
[0]->parent_id
) : NULL
;
332 const bool was_view_known
= GetViewById(view_id
) != nullptr;
334 BuildViewTree(this, views
, initial_parent
);
336 // If the view was not known, then BuildViewTree() will have created it and
337 // parented the view.
341 View
* new_parent
= GetViewById(new_parent_id
);
342 View
* old_parent
= GetViewById(old_parent_id
);
343 View
* view
= GetViewById(view_id
);
345 ViewPrivate(new_parent
).LocalAddChild(view
);
347 ViewPrivate(old_parent
).LocalRemoveChild(view
);
350 void ViewTreeClientImpl::OnViewReordered(Id view_id
,
352 mojo::OrderDirection direction
) {
353 View
* view
= GetViewById(view_id
);
354 View
* relative_view
= GetViewById(relative_view_id
);
355 if (view
&& relative_view
)
356 ViewPrivate(view
).LocalReorder(relative_view
, direction
);
359 void ViewTreeClientImpl::OnViewDeleted(Id view_id
) {
360 View
* view
= GetViewById(view_id
);
362 ViewPrivate(view
).LocalDestroy();
365 void ViewTreeClientImpl::OnViewVisibilityChanged(Id view_id
, bool visible
) {
366 // TODO(sky): there is a race condition here. If this client and another
367 // client change the visibility at the same time the wrong value may be set.
368 // Deal with this some how.
369 View
* view
= GetViewById(view_id
);
371 ViewPrivate(view
).LocalSetVisible(visible
);
374 void ViewTreeClientImpl::OnViewDrawnStateChanged(Id view_id
, bool drawn
) {
375 View
* view
= GetViewById(view_id
);
377 ViewPrivate(view
).LocalSetDrawn(drawn
);
380 void ViewTreeClientImpl::OnViewSharedPropertyChanged(
382 const mojo::String
& name
,
383 mojo::Array
<uint8_t> new_data
) {
384 View
* view
= GetViewById(view_id
);
386 std::vector
<uint8_t> data
;
387 std::vector
<uint8_t>* data_ptr
= NULL
;
388 if (!new_data
.is_null()) {
389 data
= new_data
.To
<std::vector
<uint8_t>>();
393 view
->SetSharedProperty(name
, data_ptr
);
397 void ViewTreeClientImpl::OnViewInputEvent(
399 mojo::EventPtr event
,
400 const mojo::Callback
<void()>& ack_callback
) {
401 View
* view
= GetViewById(view_id
);
403 FOR_EACH_OBSERVER(ViewObserver
, *ViewPrivate(view
).observers(),
404 OnViewInputEvent(view
, event
));
409 void ViewTreeClientImpl::OnViewFocused(Id focused_view_id
) {
410 View
* focused
= GetViewById(focused_view_id
);
411 View
* blurred
= focused_view_
;
412 // Update |focused_view_| before calling any of the observers, so that the
413 // observers get the correct result from calling |View::HasFocus()|,
414 // |ViewTreeConnection::GetFocusedView()| etc.
415 focused_view_
= focused
;
417 FOR_EACH_OBSERVER(ViewObserver
, *ViewPrivate(blurred
).observers(),
418 OnViewFocusChanged(focused
, blurred
));
421 FOR_EACH_OBSERVER(ViewObserver
, *ViewPrivate(focused
).observers(),
422 OnViewFocusChanged(focused
, blurred
));
426 ////////////////////////////////////////////////////////////////////////////////
427 // ViewTreeClientImpl, private:
429 void ViewTreeClientImpl::OnActionCompleted(bool success
) {
430 if (!change_acked_callback_
.is_null())
431 change_acked_callback_
.Run();
434 mojo::Callback
<void(bool)> ViewTreeClientImpl::ActionCompletedCallback() {
435 return [this](bool success
) { OnActionCompleted(success
); };