1 // Copyright 2015 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/view_tree_host_impl.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "components/mus/connection_manager.h"
9 #include "components/mus/display_manager.h"
10 #include "components/mus/focus_controller.h"
11 #include "components/mus/public/cpp/types.h"
12 #include "components/mus/view_tree_host_delegate.h"
13 #include "components/mus/view_tree_impl.h"
14 #include "mojo/common/common_type_converters.h"
15 #include "mojo/converters/geometry/geometry_type_converters.h"
19 ViewTreeHostImpl::ViewTreeHostImpl(
20 mojo::ViewTreeHostClientPtr client
,
21 ConnectionManager
* connection_manager
,
23 mojo::ApplicationImpl
* app_impl
,
24 const scoped_refptr
<GpuState
>& gpu_state
,
25 const scoped_refptr
<SurfacesState
>& surfaces_state
)
27 connection_manager_(connection_manager
),
28 client_(client
.Pass()),
29 event_dispatcher_(this),
30 display_manager_(DisplayManager::Create(is_headless
,
34 focus_controller_(new FocusController(this)) {
35 display_manager_
->Init(this);
37 client_
.set_connection_error_handler(
38 base::Bind(&ViewTreeHostImpl::OnClientClosed
, base::Unretained(this)));
42 ViewTreeHostImpl::~ViewTreeHostImpl() {}
44 void ViewTreeHostImpl::Init(ViewTreeHostDelegate
* delegate
) {
46 if (delegate_
&& root_
)
47 delegate_
->OnDisplayInitialized();
50 ViewTreeImpl
* ViewTreeHostImpl::GetViewTree() {
51 return delegate_
? delegate_
->GetViewTree() : nullptr;
54 bool ViewTreeHostImpl::IsViewAttachedToRoot(const ServerView
* view
) const {
55 return root_
->Contains(view
) && view
!= root_
.get();
58 bool ViewTreeHostImpl::SchedulePaintIfInViewport(const ServerView
* view
,
59 const gfx::Rect
& bounds
) {
60 if (root_
->Contains(view
)) {
61 display_manager_
->SchedulePaint(view
, bounds
);
67 const mojo::ViewportMetrics
& ViewTreeHostImpl::GetViewportMetrics() const {
68 return display_manager_
->GetViewportMetrics();
71 void ViewTreeHostImpl::SetFocusedView(ServerView
* new_focused_view
) {
72 ServerView
* old_focused_view
= focus_controller_
->GetFocusedView();
73 if (old_focused_view
== new_focused_view
)
75 DCHECK(root_view()->Contains(new_focused_view
));
76 focus_controller_
->SetFocusedView(new_focused_view
);
77 // TODO(beng): have the FocusController notify us via FocusControllerDelegate.
78 OnFocusChanged(old_focused_view
, new_focused_view
);
81 ServerView
* ViewTreeHostImpl::GetFocusedView() {
82 return focus_controller_
->GetFocusedView();
85 void ViewTreeHostImpl::DestroyFocusController() {
86 focus_controller_
.reset();
89 void ViewTreeHostImpl::UpdateTextInputState(ServerView
* view
,
90 const ui::TextInputState
& state
) {
91 // Do not need to update text input for unfocused views.
92 if (!display_manager_
|| focus_controller_
->GetFocusedView() != view
)
94 display_manager_
->UpdateTextInputState(state
);
97 void ViewTreeHostImpl::SetImeVisibility(ServerView
* view
, bool visible
) {
98 // Do not need to show or hide IME for unfocused view.
99 if (focus_controller_
->GetFocusedView() != view
)
101 display_manager_
->SetImeVisibility(visible
);
104 void ViewTreeHostImpl::OnAccelerator(uint32_t accelerator_id
,
105 mojo::EventPtr event
) {
106 client()->OnAccelerator(accelerator_id
, event
.Pass());
109 void ViewTreeHostImpl::DispatchInputEventToView(const ServerView
* target
,
110 mojo::EventPtr event
) {
111 // If the view is an embed root, forward to the embedded view, not the owner.
112 ViewTreeImpl
* connection
=
113 connection_manager_
->GetConnectionWithRoot(target
->id());
115 connection
= connection_manager_
->GetConnection(target
->id().connection_id
);
116 connection
->client()->OnViewInputEvent(ViewIdToTransportId(target
->id()),
118 base::Bind(&base::DoNothing
));
121 void ViewTreeHostImpl::SetSize(mojo::SizePtr size
) {
122 display_manager_
->SetViewportSize(size
.To
<gfx::Size
>());
125 void ViewTreeHostImpl::SetTitle(const mojo::String
& title
) {
126 display_manager_
->SetTitle(title
.To
<base::string16
>());
129 void ViewTreeHostImpl::AddAccelerator(uint32_t id
,
130 mojo::KeyboardCode keyboard_code
,
131 mojo::EventFlags flags
) {
132 event_dispatcher_
.AddAccelerator(id
, keyboard_code
, flags
);
135 void ViewTreeHostImpl::RemoveAccelerator(uint32_t id
) {
136 event_dispatcher_
.RemoveAccelerator(id
);
139 void ViewTreeHostImpl::OnClientClosed() {
140 // |display_manager_.reset()| destroys the display-manager first, and then
141 // sets |display_manager_| to nullptr. However, destroying |display_manager_|
142 // can destroy the corresponding ViewTreeHostConnection, and |this|. So
143 // setting it to nullptr afterwards in reset() ends up writing on free'd
144 // memory. So transfer over to a local scoped_ptr<> before destroying it.
145 scoped_ptr
<DisplayManager
> temp
= display_manager_
.Pass();
148 ServerView
* ViewTreeHostImpl::GetRootView() {
152 void ViewTreeHostImpl::OnEvent(ViewId id
, mojo::EventPtr event
) {
153 ServerView
* view
= connection_manager_
->GetView(id
);
154 // TODO(fsamuel): This should be a DCHECK but currently we use stale
155 // information to decide where to route input events. This should be fixed
156 // once we implement a UI scheduler.
158 DispatchInputEventToView(view
, event
.Pass());
161 event_dispatcher_
.OnEvent(event
.Pass());
164 void ViewTreeHostImpl::OnDisplayClosed() {
166 delegate_
->OnDisplayClosed();
169 void ViewTreeHostImpl::OnViewportMetricsChanged(
170 const mojo::ViewportMetrics
& old_metrics
,
171 const mojo::ViewportMetrics
& new_metrics
) {
173 root_
.reset(connection_manager_
->CreateServerView(
174 RootViewId(connection_manager_
->GetAndAdvanceNextHostId())));
175 root_
->SetBounds(gfx::Rect(new_metrics
.size_in_pixels
.To
<gfx::Size
>()));
176 root_
->SetVisible(true);
178 delegate_
->OnDisplayInitialized();
180 root_
->SetBounds(gfx::Rect(new_metrics
.size_in_pixels
.To
<gfx::Size
>()));
182 // TODO(fsamuel): We shouldn't broadcast this to all connections but only
183 // those within a window root.
184 connection_manager_
->ProcessViewportMetricsChanged(old_metrics
, new_metrics
);
187 void ViewTreeHostImpl::OnFocusChanged(ServerView
* old_focused_view
,
188 ServerView
* new_focused_view
) {
189 // There are up to four connections that need to be notified:
190 // . the connection containing |old_focused_view|.
191 // . the connection with |old_focused_view| as its root.
192 // . the connection containing |new_focused_view|.
193 // . the connection with |new_focused_view| as its root.
194 // Some of these connections may be the same. The following takes care to
195 // notify each only once.
196 ViewTreeImpl
* owning_connection_old
= nullptr;
197 ViewTreeImpl
* embedded_connection_old
= nullptr;
199 if (old_focused_view
) {
200 owning_connection_old
= connection_manager_
->GetConnection(
201 old_focused_view
->id().connection_id
);
202 if (owning_connection_old
) {
203 owning_connection_old
->ProcessFocusChanged(old_focused_view
,
206 embedded_connection_old
=
207 connection_manager_
->GetConnectionWithRoot(old_focused_view
->id());
208 if (embedded_connection_old
) {
209 DCHECK_NE(owning_connection_old
, embedded_connection_old
);
210 embedded_connection_old
->ProcessFocusChanged(old_focused_view
,
214 ViewTreeImpl
* owning_connection_new
= nullptr;
215 ViewTreeImpl
* embedded_connection_new
= nullptr;
216 if (new_focused_view
) {
217 owning_connection_new
= connection_manager_
->GetConnection(
218 new_focused_view
->id().connection_id
);
219 if (owning_connection_new
&&
220 owning_connection_new
!= owning_connection_old
&&
221 owning_connection_new
!= embedded_connection_old
) {
222 owning_connection_new
->ProcessFocusChanged(old_focused_view
,
225 embedded_connection_new
=
226 connection_manager_
->GetConnectionWithRoot(new_focused_view
->id());
227 if (embedded_connection_new
&&
228 embedded_connection_new
!= owning_connection_old
&&
229 embedded_connection_new
!= embedded_connection_old
) {
230 DCHECK_NE(owning_connection_new
, embedded_connection_new
);
231 embedded_connection_new
->ProcessFocusChanged(old_focused_view
,
236 // Ensure that we always notify the root connection of a focus change.
237 ViewTreeImpl
* root_tree
= GetViewTree();
238 if (root_tree
!= owning_connection_old
&&
239 root_tree
!= embedded_connection_old
&&
240 root_tree
!= owning_connection_new
&&
241 root_tree
!= embedded_connection_new
) {
242 root_tree
->ProcessFocusChanged(old_focused_view
, new_focused_view
);
245 UpdateTextInputState(new_focused_view
, new_focused_view
->text_input_state());