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/window/platform_configuration.h" |
6 | |
7 | #include "flutter/lib/ui/compositing/scene.h" |
8 | #include "flutter/lib/ui/ui_dart_state.h" |
9 | #include "flutter/lib/ui/window/platform_message_response_dart.h" |
10 | #include "flutter/lib/ui/window/window.h" |
11 | #include "third_party/tonic/converter/dart_converter.h" |
12 | #include "third_party/tonic/dart_args.h" |
13 | #include "third_party/tonic/dart_library_natives.h" |
14 | #include "third_party/tonic/dart_microtask_queue.h" |
15 | #include "third_party/tonic/logging/dart_invoke.h" |
16 | #include "third_party/tonic/typed_data/dart_byte_data.h" |
17 | |
18 | namespace flutter { |
19 | namespace { |
20 | |
21 | void DefaultRouteName(Dart_NativeArguments args) { |
22 | UIDartState::ThrowIfUIOperationsProhibited(); |
23 | std::string routeName = UIDartState::Current() |
24 | ->platform_configuration() |
25 | ->client() |
26 | ->DefaultRouteName(); |
27 | Dart_SetReturnValue(args, tonic::StdStringToDart(routeName)); |
28 | } |
29 | |
30 | void ScheduleFrame(Dart_NativeArguments args) { |
31 | UIDartState::ThrowIfUIOperationsProhibited(); |
32 | UIDartState::Current()->platform_configuration()->client()->ScheduleFrame(); |
33 | } |
34 | |
35 | void Render(Dart_NativeArguments args) { |
36 | UIDartState::ThrowIfUIOperationsProhibited(); |
37 | Dart_Handle exception = nullptr; |
38 | Scene* scene = |
39 | tonic::DartConverter<Scene*>::FromArguments(args, 1, exception); |
40 | if (exception) { |
41 | Dart_ThrowException(exception); |
42 | return; |
43 | } |
44 | UIDartState::Current()->platform_configuration()->client()->Render(scene); |
45 | } |
46 | |
47 | void UpdateSemantics(Dart_NativeArguments args) { |
48 | UIDartState::ThrowIfUIOperationsProhibited(); |
49 | Dart_Handle exception = nullptr; |
50 | SemanticsUpdate* update = |
51 | tonic::DartConverter<SemanticsUpdate*>::FromArguments(args, 1, exception); |
52 | if (exception) { |
53 | Dart_ThrowException(exception); |
54 | return; |
55 | } |
56 | UIDartState::Current()->platform_configuration()->client()->UpdateSemantics( |
57 | update); |
58 | } |
59 | |
60 | void SetIsolateDebugName(Dart_NativeArguments args) { |
61 | UIDartState::ThrowIfUIOperationsProhibited(); |
62 | Dart_Handle exception = nullptr; |
63 | const std::string name = |
64 | tonic::DartConverter<std::string>::FromArguments(args, 1, exception); |
65 | if (exception) { |
66 | Dart_ThrowException(exception); |
67 | return; |
68 | } |
69 | UIDartState::Current()->SetDebugName(name); |
70 | } |
71 | |
72 | void SetNeedsReportTimings(Dart_NativeArguments args) { |
73 | UIDartState::ThrowIfUIOperationsProhibited(); |
74 | Dart_Handle exception = nullptr; |
75 | bool value = tonic::DartConverter<bool>::FromArguments(args, 1, exception); |
76 | UIDartState::Current() |
77 | ->platform_configuration() |
78 | ->client() |
79 | ->SetNeedsReportTimings(value); |
80 | } |
81 | |
82 | void ReportUnhandledException(Dart_NativeArguments args) { |
83 | UIDartState::ThrowIfUIOperationsProhibited(); |
84 | |
85 | Dart_Handle exception = nullptr; |
86 | |
87 | auto error_name = |
88 | tonic::DartConverter<std::string>::FromArguments(args, 0, exception); |
89 | if (exception) { |
90 | Dart_ThrowException(exception); |
91 | return; |
92 | } |
93 | |
94 | auto stack_trace = |
95 | tonic::DartConverter<std::string>::FromArguments(args, 1, exception); |
96 | if (exception) { |
97 | Dart_ThrowException(exception); |
98 | return; |
99 | } |
100 | |
101 | UIDartState::Current()->ReportUnhandledException(std::move(error_name), |
102 | std::move(stack_trace)); |
103 | } |
104 | |
105 | Dart_Handle SendPlatformMessage(Dart_Handle window, |
106 | const std::string& name, |
107 | Dart_Handle callback, |
108 | Dart_Handle data_handle) { |
109 | UIDartState* dart_state = UIDartState::Current(); |
110 | |
111 | if (!dart_state->platform_configuration()) { |
112 | return tonic::ToDart( |
113 | "Platform messages can only be sent from the main isolate" ); |
114 | } |
115 | |
116 | fml::RefPtr<PlatformMessageResponse> response; |
117 | if (!Dart_IsNull(callback)) { |
118 | response = fml::MakeRefCounted<PlatformMessageResponseDart>( |
119 | tonic::DartPersistentValue(dart_state, callback), |
120 | dart_state->GetTaskRunners().GetUITaskRunner()); |
121 | } |
122 | if (Dart_IsNull(data_handle)) { |
123 | dart_state->platform_configuration()->client()->HandlePlatformMessage( |
124 | fml::MakeRefCounted<PlatformMessage>(name, response)); |
125 | } else { |
126 | tonic::DartByteData data(data_handle); |
127 | const uint8_t* buffer = static_cast<const uint8_t*>(data.data()); |
128 | dart_state->platform_configuration()->client()->HandlePlatformMessage( |
129 | fml::MakeRefCounted<PlatformMessage>( |
130 | name, std::vector<uint8_t>(buffer, buffer + data.length_in_bytes()), |
131 | response)); |
132 | } |
133 | |
134 | return Dart_Null(); |
135 | } |
136 | |
137 | void _SendPlatformMessage(Dart_NativeArguments args) { |
138 | tonic::DartCallStatic(&SendPlatformMessage, args); |
139 | } |
140 | |
141 | void RespondToPlatformMessage(Dart_Handle window, |
142 | int response_id, |
143 | const tonic::DartByteData& data) { |
144 | if (Dart_IsNull(data.dart_handle())) { |
145 | UIDartState::Current() |
146 | ->platform_configuration() |
147 | ->CompletePlatformMessageEmptyResponse(response_id); |
148 | } else { |
149 | // TODO(engine): Avoid this copy. |
150 | const uint8_t* buffer = static_cast<const uint8_t*>(data.data()); |
151 | UIDartState::Current() |
152 | ->platform_configuration() |
153 | ->CompletePlatformMessageResponse( |
154 | response_id, |
155 | std::vector<uint8_t>(buffer, buffer + data.length_in_bytes())); |
156 | } |
157 | } |
158 | |
159 | void _RespondToPlatformMessage(Dart_NativeArguments args) { |
160 | tonic::DartCallStatic(&RespondToPlatformMessage, args); |
161 | } |
162 | |
163 | void GetPersistentIsolateData(Dart_NativeArguments args) { |
164 | UIDartState::ThrowIfUIOperationsProhibited(); |
165 | |
166 | auto persistent_isolate_data = UIDartState::Current() |
167 | ->platform_configuration() |
168 | ->client() |
169 | ->GetPersistentIsolateData(); |
170 | |
171 | if (!persistent_isolate_data) { |
172 | Dart_SetReturnValue(args, Dart_Null()); |
173 | return; |
174 | } |
175 | |
176 | Dart_SetReturnValue( |
177 | args, tonic::DartByteData::Create(persistent_isolate_data->GetMapping(), |
178 | persistent_isolate_data->GetSize())); |
179 | } |
180 | |
181 | Dart_Handle ToByteData(const std::vector<uint8_t>& buffer) { |
182 | return tonic::DartByteData::Create(buffer.data(), buffer.size()); |
183 | } |
184 | |
185 | } // namespace |
186 | |
187 | PlatformConfigurationClient::~PlatformConfigurationClient() {} |
188 | |
189 | PlatformConfiguration::PlatformConfiguration( |
190 | PlatformConfigurationClient* client) |
191 | : client_(client) {} |
192 | |
193 | PlatformConfiguration::~PlatformConfiguration() {} |
194 | |
195 | void PlatformConfiguration::DidCreateIsolate() { |
196 | library_.Set(tonic::DartState::Current(), |
197 | Dart_LookupLibrary(tonic::ToDart("dart:ui" ))); |
198 | window_.reset(new Window({1.0, 0.0, 0.0})); |
199 | } |
200 | |
201 | void PlatformConfiguration::UpdateLocales( |
202 | const std::vector<std::string>& locales) { |
203 | std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock(); |
204 | if (!dart_state) { |
205 | return; |
206 | } |
207 | tonic::DartState::Scope scope(dart_state); |
208 | tonic::LogIfError(tonic::DartInvokeField( |
209 | library_.value(), "_updateLocales" , |
210 | { |
211 | tonic::ToDart<std::vector<std::string>>(locales), |
212 | })); |
213 | } |
214 | |
215 | void PlatformConfiguration::UpdateUserSettingsData(const std::string& data) { |
216 | std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock(); |
217 | if (!dart_state) { |
218 | return; |
219 | } |
220 | tonic::DartState::Scope scope(dart_state); |
221 | |
222 | tonic::LogIfError(tonic::DartInvokeField(library_.value(), |
223 | "_updateUserSettingsData" , |
224 | { |
225 | tonic::StdStringToDart(data), |
226 | })); |
227 | } |
228 | |
229 | void PlatformConfiguration::UpdateLifecycleState(const std::string& data) { |
230 | std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock(); |
231 | if (!dart_state) { |
232 | return; |
233 | } |
234 | tonic::DartState::Scope scope(dart_state); |
235 | tonic::LogIfError(tonic::DartInvokeField(library_.value(), |
236 | "_updateLifecycleState" , |
237 | { |
238 | tonic::StdStringToDart(data), |
239 | })); |
240 | } |
241 | |
242 | void PlatformConfiguration::UpdateSemanticsEnabled(bool enabled) { |
243 | std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock(); |
244 | if (!dart_state) { |
245 | return; |
246 | } |
247 | tonic::DartState::Scope scope(dart_state); |
248 | UIDartState::ThrowIfUIOperationsProhibited(); |
249 | |
250 | tonic::LogIfError(tonic::DartInvokeField( |
251 | library_.value(), "_updateSemanticsEnabled" , {tonic::ToDart(enabled)})); |
252 | } |
253 | |
254 | void PlatformConfiguration::UpdateAccessibilityFeatures(int32_t values) { |
255 | std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock(); |
256 | if (!dart_state) { |
257 | return; |
258 | } |
259 | tonic::DartState::Scope scope(dart_state); |
260 | |
261 | tonic::LogIfError(tonic::DartInvokeField(library_.value(), |
262 | "_updateAccessibilityFeatures" , |
263 | {tonic::ToDart(values)})); |
264 | } |
265 | |
266 | void PlatformConfiguration::DispatchPlatformMessage( |
267 | fml::RefPtr<PlatformMessage> message) { |
268 | std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock(); |
269 | if (!dart_state) { |
270 | FML_DLOG(WARNING) |
271 | << "Dropping platform message for lack of DartState on channel: " |
272 | << message->channel(); |
273 | return; |
274 | } |
275 | tonic::DartState::Scope scope(dart_state); |
276 | Dart_Handle data_handle = |
277 | (message->hasData()) ? ToByteData(message->data()) : Dart_Null(); |
278 | if (Dart_IsError(data_handle)) { |
279 | FML_DLOG(WARNING) |
280 | << "Dropping platform message because of a Dart error on channel: " |
281 | << message->channel(); |
282 | return; |
283 | } |
284 | |
285 | int response_id = 0; |
286 | if (auto response = message->response()) { |
287 | response_id = next_response_id_++; |
288 | pending_responses_[response_id] = response; |
289 | } |
290 | |
291 | tonic::LogIfError( |
292 | tonic::DartInvokeField(library_.value(), "_dispatchPlatformMessage" , |
293 | {tonic::ToDart(message->channel()), data_handle, |
294 | tonic::ToDart(response_id)})); |
295 | } |
296 | |
297 | void PlatformConfiguration::DispatchSemanticsAction(int32_t id, |
298 | SemanticsAction action, |
299 | std::vector<uint8_t> args) { |
300 | std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock(); |
301 | if (!dart_state) { |
302 | return; |
303 | } |
304 | tonic::DartState::Scope scope(dart_state); |
305 | |
306 | Dart_Handle args_handle = (args.empty()) ? Dart_Null() : ToByteData(args); |
307 | |
308 | if (Dart_IsError(args_handle)) { |
309 | return; |
310 | } |
311 | |
312 | tonic::LogIfError(tonic::DartInvokeField( |
313 | library_.value(), "_dispatchSemanticsAction" , |
314 | {tonic::ToDart(id), tonic::ToDart(static_cast<int32_t>(action)), |
315 | args_handle})); |
316 | } |
317 | |
318 | void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime) { |
319 | std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock(); |
320 | if (!dart_state) { |
321 | return; |
322 | } |
323 | tonic::DartState::Scope scope(dart_state); |
324 | |
325 | int64_t microseconds = (frameTime - fml::TimePoint()).ToMicroseconds(); |
326 | |
327 | tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_beginFrame" , |
328 | { |
329 | Dart_NewInteger(microseconds), |
330 | })); |
331 | |
332 | UIDartState::Current()->FlushMicrotasksNow(); |
333 | |
334 | tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_drawFrame" , {})); |
335 | } |
336 | |
337 | void PlatformConfiguration::ReportTimings(std::vector<int64_t> timings) { |
338 | std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock(); |
339 | if (!dart_state) { |
340 | return; |
341 | } |
342 | tonic::DartState::Scope scope(dart_state); |
343 | |
344 | Dart_Handle data_handle = |
345 | Dart_NewTypedData(Dart_TypedData_kInt64, timings.size()); |
346 | |
347 | Dart_TypedData_Type type; |
348 | void* data = nullptr; |
349 | intptr_t num_acquired = 0; |
350 | FML_CHECK(!Dart_IsError( |
351 | Dart_TypedDataAcquireData(data_handle, &type, &data, &num_acquired))); |
352 | FML_DCHECK(num_acquired == static_cast<int>(timings.size())); |
353 | |
354 | memcpy(data, timings.data(), sizeof(int64_t) * timings.size()); |
355 | FML_CHECK(Dart_TypedDataReleaseData(data_handle)); |
356 | |
357 | tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_reportTimings" , |
358 | { |
359 | data_handle, |
360 | })); |
361 | } |
362 | |
363 | void PlatformConfiguration::CompletePlatformMessageEmptyResponse( |
364 | int response_id) { |
365 | if (!response_id) { |
366 | return; |
367 | } |
368 | auto it = pending_responses_.find(response_id); |
369 | if (it == pending_responses_.end()) { |
370 | return; |
371 | } |
372 | auto response = std::move(it->second); |
373 | pending_responses_.erase(it); |
374 | response->CompleteEmpty(); |
375 | } |
376 | |
377 | void PlatformConfiguration::CompletePlatformMessageResponse( |
378 | int response_id, |
379 | std::vector<uint8_t> data) { |
380 | if (!response_id) { |
381 | return; |
382 | } |
383 | auto it = pending_responses_.find(response_id); |
384 | if (it == pending_responses_.end()) { |
385 | return; |
386 | } |
387 | auto response = std::move(it->second); |
388 | pending_responses_.erase(it); |
389 | response->Complete(std::make_unique<fml::DataMapping>(std::move(data))); |
390 | } |
391 | |
392 | Dart_Handle ComputePlatformResolvedLocale(Dart_Handle supportedLocalesHandle) { |
393 | std::vector<std::string> supportedLocales = |
394 | tonic::DartConverter<std::vector<std::string>>::FromDart( |
395 | supportedLocalesHandle); |
396 | |
397 | std::vector<std::string> results = |
398 | *UIDartState::Current() |
399 | ->platform_configuration() |
400 | ->client() |
401 | ->ComputePlatformResolvedLocale(supportedLocales); |
402 | |
403 | return tonic::DartConverter<std::vector<std::string>>::ToDart(results); |
404 | } |
405 | |
406 | static void _ComputePlatformResolvedLocale(Dart_NativeArguments args) { |
407 | UIDartState::ThrowIfUIOperationsProhibited(); |
408 | Dart_Handle result = |
409 | ComputePlatformResolvedLocale(Dart_GetNativeArgument(args, 1)); |
410 | Dart_SetReturnValue(args, result); |
411 | } |
412 | |
413 | void PlatformConfiguration::RegisterNatives( |
414 | tonic::DartLibraryNatives* natives) { |
415 | natives->Register({ |
416 | {"PlatformConfiguration_defaultRouteName" , DefaultRouteName, 1, true}, |
417 | {"PlatformConfiguration_scheduleFrame" , ScheduleFrame, 1, true}, |
418 | {"PlatformConfiguration_sendPlatformMessage" , _SendPlatformMessage, 4, |
419 | true}, |
420 | {"PlatformConfiguration_respondToPlatformMessage" , |
421 | _RespondToPlatformMessage, 3, true}, |
422 | {"PlatformConfiguration_render" , Render, 2, true}, |
423 | {"PlatformConfiguration_updateSemantics" , UpdateSemantics, 2, true}, |
424 | {"PlatformConfiguration_setIsolateDebugName" , SetIsolateDebugName, 2, |
425 | true}, |
426 | {"PlatformConfiguration_reportUnhandledException" , |
427 | ReportUnhandledException, 2, true}, |
428 | {"PlatformConfiguration_setNeedsReportTimings" , SetNeedsReportTimings, 2, |
429 | true}, |
430 | {"PlatformConfiguration_getPersistentIsolateData" , |
431 | GetPersistentIsolateData, 1, true}, |
432 | {"PlatformConfiguration_computePlatformResolvedLocale" , |
433 | _ComputePlatformResolvedLocale, 2, true}, |
434 | }); |
435 | } |
436 | |
437 | } // namespace flutter |
438 | |