1 // Copyright 2013 The Flutter 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.
4
5 #include "flutter/lib/ui/compositing/scene_host.h"
6
7 #include <lib/ui/scenic/cpp/view_token_pair.h>
8 #include <lib/zx/eventpair.h>
9
10 #include "flutter/flow/view_holder.h"
11 #include "flutter/fml/thread_local.h"
12 #include "flutter/lib/ui/ui_dart_state.h"
13
14 namespace {
15
16 using SceneHostBindings = std::unordered_map<zx_koid_t, flutter::SceneHost*>;
17
18 FML_THREAD_LOCAL fml::ThreadLocalUniquePtr<SceneHostBindings>
19 tls_scene_host_bindings;
20
SceneHost_constructor(Dart_NativeArguments args)21 void SceneHost_constructor(Dart_NativeArguments args) {
22 // This UI thread / Isolate contains at least 1 SceneHost. Initialize the
23 // per-Isolate bindings.
24 if (tls_scene_host_bindings.get() == nullptr) {
25 tls_scene_host_bindings.reset(new SceneHostBindings());
26 }
27
28 tonic::DartCallConstructor(&flutter::SceneHost::Create, args);
29 }
30
GetSceneHost(scenic::ResourceId id)31 flutter::SceneHost* GetSceneHost(scenic::ResourceId id) {
32 auto* bindings = tls_scene_host_bindings.get();
33 FML_DCHECK(bindings);
34
35 auto binding = bindings->find(id);
36 if (binding != bindings->end()) {
37 return binding->second;
38 }
39
40 return nullptr;
41 }
42
InvokeDartClosure(const tonic::DartPersistentValue & closure)43 void InvokeDartClosure(const tonic::DartPersistentValue& closure) {
44 auto dart_state = closure.dart_state().lock();
45 if (!dart_state) {
46 return;
47 }
48
49 tonic::DartState::Scope scope(dart_state);
50 auto dart_handle = closure.value();
51
52 FML_DCHECK(dart_handle && !Dart_IsNull(dart_handle) &&
53 Dart_IsClosure(dart_handle));
54 tonic::DartInvoke(dart_handle, {});
55 }
56
57 template <typename T>
InvokeDartFunction(const tonic::DartPersistentValue & function,T & arg)58 void InvokeDartFunction(const tonic::DartPersistentValue& function, T& arg) {
59 auto dart_state = function.dart_state().lock();
60 if (!dart_state) {
61 return;
62 }
63
64 tonic::DartState::Scope scope(dart_state);
65 auto dart_handle = function.value();
66
67 FML_DCHECK(dart_handle && !Dart_IsNull(dart_handle) &&
68 Dart_IsClosure(dart_handle));
69 tonic::DartInvoke(dart_handle, {tonic::ToDart(arg)});
70 }
71
GetKoid(zx_handle_t handle)72 zx_koid_t GetKoid(zx_handle_t handle) {
73 zx_info_handle_basic_t info;
74 zx_status_t status = zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, &info,
75 sizeof(info), nullptr, nullptr);
76 return status == ZX_OK ? info.koid : ZX_KOID_INVALID;
77 }
78
79 } // namespace
80
81 namespace flutter {
82
83 IMPLEMENT_WRAPPERTYPEINFO(ui, SceneHost);
84
85 #define FOR_EACH_BINDING(V) \
86 V(SceneHost, dispose) \
87 V(SceneHost, setProperties) \
88 V(SceneHost, setOpacity)
89
FOR_EACH_BINDING(DART_NATIVE_CALLBACK)90 FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
91
92 void SceneHost::RegisterNatives(tonic::DartLibraryNatives* natives) {
93 natives->Register({{"SceneHost_constructor", SceneHost_constructor, 5, true},
94 FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
95 }
96
Create(fml::RefPtr<zircon::dart::Handle> viewHolderToken,Dart_Handle viewConnectedCallback,Dart_Handle viewDisconnectedCallback,Dart_Handle viewStateChangedCallback)97 fml::RefPtr<SceneHost> SceneHost::Create(
98 fml::RefPtr<zircon::dart::Handle> viewHolderToken,
99 Dart_Handle viewConnectedCallback,
100 Dart_Handle viewDisconnectedCallback,
101 Dart_Handle viewStateChangedCallback) {
102 return fml::MakeRefCounted<SceneHost>(viewHolderToken, viewConnectedCallback,
103 viewDisconnectedCallback,
104 viewStateChangedCallback);
105 }
106
OnViewConnected(scenic::ResourceId id)107 void SceneHost::OnViewConnected(scenic::ResourceId id) {
108 auto* scene_host = GetSceneHost(id);
109
110 if (scene_host && !scene_host->view_connected_callback_.is_empty()) {
111 InvokeDartClosure(scene_host->view_connected_callback_);
112 }
113 }
114
OnViewDisconnected(scenic::ResourceId id)115 void SceneHost::OnViewDisconnected(scenic::ResourceId id) {
116 auto* scene_host = GetSceneHost(id);
117
118 if (scene_host && !scene_host->view_disconnected_callback_.is_empty()) {
119 InvokeDartClosure(scene_host->view_disconnected_callback_);
120 }
121 }
122
OnViewStateChanged(scenic::ResourceId id,bool state)123 void SceneHost::OnViewStateChanged(scenic::ResourceId id, bool state) {
124 auto* scene_host = GetSceneHost(id);
125
126 if (scene_host && !scene_host->view_state_changed_callback_.is_empty()) {
127 InvokeDartFunction(scene_host->view_state_changed_callback_, state);
128 }
129 }
130
SceneHost(fml::RefPtr<zircon::dart::Handle> viewHolderToken,Dart_Handle viewConnectedCallback,Dart_Handle viewDisconnectedCallback,Dart_Handle viewStateChangedCallback)131 SceneHost::SceneHost(fml::RefPtr<zircon::dart::Handle> viewHolderToken,
132 Dart_Handle viewConnectedCallback,
133 Dart_Handle viewDisconnectedCallback,
134 Dart_Handle viewStateChangedCallback)
135 : gpu_task_runner_(
136 UIDartState::Current()->GetTaskRunners().GetGPUTaskRunner()),
137 koid_(GetKoid(viewHolderToken->handle())) {
138 auto dart_state = UIDartState::Current();
139
140 // Initialize callbacks it they are non-null in Dart.
141 if (!Dart_IsNull(viewConnectedCallback)) {
142 view_connected_callback_.Set(dart_state, viewConnectedCallback);
143 }
144 if (!Dart_IsNull(viewDisconnectedCallback)) {
145 view_disconnected_callback_.Set(dart_state, viewDisconnectedCallback);
146 }
147 if (!Dart_IsNull(viewStateChangedCallback)) {
148 view_state_changed_callback_.Set(dart_state, viewStateChangedCallback);
149 }
150
151 // This callback will be posted as a task when the |scenic::ViewHolder|
152 // resource is created and given an id by the GPU thread.
153 auto bind_callback = [scene_host = this](scenic::ResourceId id) {
154 auto* bindings = tls_scene_host_bindings.get();
155 FML_DCHECK(bindings);
156 FML_DCHECK(bindings->find(id) == bindings->end());
157
158 bindings->emplace(std::make_pair(id, scene_host));
159 };
160
161 // Pass the raw handle to the GPU thead; destroying a |zircon::dart::Handle|
162 // on that thread can cause a race condition.
163 gpu_task_runner_->PostTask(
164 [id = koid_,
165 ui_task_runner =
166 UIDartState::Current()->GetTaskRunners().GetUITaskRunner(),
167 raw_handle = viewHolderToken->ReleaseHandle(), bind_callback]() {
168 flutter::ViewHolder::Create(
169 id, std::move(ui_task_runner),
170 scenic::ToViewHolderToken(zx::eventpair(raw_handle)),
171 std::move(bind_callback));
172 });
173 }
174
~SceneHost()175 SceneHost::~SceneHost() {
176 auto* bindings = tls_scene_host_bindings.get();
177 FML_DCHECK(bindings);
178 bindings->erase(koid_);
179
180 gpu_task_runner_->PostTask(
181 [id = koid_]() { flutter::ViewHolder::Destroy(id); });
182 }
183
dispose()184 void SceneHost::dispose() {
185 ClearDartWrapper();
186 }
187
setProperties(double width,double height,double insetTop,double insetRight,double insetBottom,double insetLeft,bool focusable)188 void SceneHost::setProperties(double width,
189 double height,
190 double insetTop,
191 double insetRight,
192 double insetBottom,
193 double insetLeft,
194 bool focusable) {
195 gpu_task_runner_->PostTask([id = koid_, width, height, insetTop, insetRight,
196 insetBottom, insetLeft, focusable]() {
197 auto* view_holder = flutter::ViewHolder::FromId(id);
198 FML_DCHECK(view_holder);
199
200 view_holder->SetProperties(width, height, insetTop, insetRight, insetBottom,
201 insetLeft, focusable);
202 });
203 }
204
setOpacity(double opacity)205 void SceneHost::setOpacity(double opacity) {
206 gpu_task_runner_->PostTask([id = koid_, opacity]() {
207 auto* view_holder = flutter::ViewHolder::FromId(id);
208 FML_DCHECK(view_holder);
209
210 view_holder->SetOpacity(opacity);
211 });
212 }
213
214 } // namespace flutter
215