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
18namespace flutter {
19namespace {
20
21void 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
30void ScheduleFrame(Dart_NativeArguments args) {
31 UIDartState::ThrowIfUIOperationsProhibited();
32 UIDartState::Current()->platform_configuration()->client()->ScheduleFrame();
33}
34
35void 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
47void 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
60void 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
72void 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
82void 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
105Dart_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
137void _SendPlatformMessage(Dart_NativeArguments args) {
138 tonic::DartCallStatic(&SendPlatformMessage, args);
139}
140
141void 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
159void _RespondToPlatformMessage(Dart_NativeArguments args) {
160 tonic::DartCallStatic(&RespondToPlatformMessage, args);
161}
162
163void 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
181Dart_Handle ToByteData(const std::vector<uint8_t>& buffer) {
182 return tonic::DartByteData::Create(buffer.data(), buffer.size());
183}
184
185} // namespace
186
187PlatformConfigurationClient::~PlatformConfigurationClient() {}
188
189PlatformConfiguration::PlatformConfiguration(
190 PlatformConfigurationClient* client)
191 : client_(client) {}
192
193PlatformConfiguration::~PlatformConfiguration() {}
194
195void 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
201void 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
215void 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
229void 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
242void 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
254void 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
266void 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
297void 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
318void 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
337void 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
363void 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
377void 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
392Dart_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
406static 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
413void 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