flutter_flutter/lib/ui/window/platform_configuration.cc
Greg Spencer 6bc70e4a11
Reland: Migration to PlatformDispatcher and multi-window (#21932)
This re-lands #20496 and #21780 after fixing the semantics-enabling code that was causing the post-submit web_smoke_test to fail.

Below is the description from the original PR:

This is a PR for converting the dart:ui code in the engine to use a multi-window API. The goal here is to convert from the window singleton to an API that has the concept of multiple windows. Also, I'm matching up the new PlatformDispatcher class to talk directly to the PlatformConfiguration class in the engine. I'm not attempting to actually enable creating multiple windows here, just migrate to an API that has a concept of multiple windows. The multi-window API in this PR currently only ever creates one window.

The design doc for this change is here.

The major changes in this PR:

Move the platfom-specific attributes out of Window, and into the new PlatformDispatcher class that holds all of the platform state, so that the platform code need only update the configuration on this class.
Create FlutterView, FlutterWindow, and SingletonFlutterWindow classes to separate out the concepts of a view (of which there may be multiple in a window), a window (of which there may be multiple on a screen, and they host views), and a window where there is only ever expected to be one (this hosts the entire API of the former Window class, and will eventually be the type of the window singleton).
Next step after this PR lands:

Remove the Window class entirely (it is replaced by SingletonFlutterWindow). Some minor changes in the Framework are needed to switch to using SingletonFlutterWindow directly first.

The Window class still exists in this PR, but will be removed as soon as the framework is converted to point to the SingletonFlutterWindow class instead. They share the same API, just have different names (Window is currently a subclass of SingletonFlutterWindow). The intention is that the Window name will be freed up to use as a widget class name in the framework for managing windows. The singleton called window will remain, and keep the same API it has now.
2020-10-22 14:54:25 -07:00

442 lines
15 KiB
C++

// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/lib/ui/window/platform_configuration.h"
#include <cstring>
#include "flutter/lib/ui/compositing/scene.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "flutter/lib/ui/window/platform_message_response_dart.h"
#include "flutter/lib/ui/window/viewport_metrics.h"
#include "flutter/lib/ui/window/window.h"
#include "third_party/tonic/converter/dart_converter.h"
#include "third_party/tonic/dart_args.h"
#include "third_party/tonic/dart_library_natives.h"
#include "third_party/tonic/dart_microtask_queue.h"
#include "third_party/tonic/logging/dart_invoke.h"
#include "third_party/tonic/typed_data/dart_byte_data.h"
namespace flutter {
namespace {
void DefaultRouteName(Dart_NativeArguments args) {
UIDartState::ThrowIfUIOperationsProhibited();
std::string routeName = UIDartState::Current()
->platform_configuration()
->client()
->DefaultRouteName();
Dart_SetReturnValue(args, tonic::StdStringToDart(routeName));
}
void ScheduleFrame(Dart_NativeArguments args) {
UIDartState::ThrowIfUIOperationsProhibited();
UIDartState::Current()->platform_configuration()->client()->ScheduleFrame();
}
void Render(Dart_NativeArguments args) {
UIDartState::ThrowIfUIOperationsProhibited();
Dart_Handle exception = nullptr;
Scene* scene =
tonic::DartConverter<Scene*>::FromArguments(args, 1, exception);
if (exception) {
Dart_ThrowException(exception);
return;
}
UIDartState::Current()->platform_configuration()->client()->Render(scene);
}
void UpdateSemantics(Dart_NativeArguments args) {
UIDartState::ThrowIfUIOperationsProhibited();
Dart_Handle exception = nullptr;
SemanticsUpdate* update =
tonic::DartConverter<SemanticsUpdate*>::FromArguments(args, 1, exception);
if (exception) {
Dart_ThrowException(exception);
return;
}
UIDartState::Current()->platform_configuration()->client()->UpdateSemantics(
update);
}
void SetIsolateDebugName(Dart_NativeArguments args) {
UIDartState::ThrowIfUIOperationsProhibited();
Dart_Handle exception = nullptr;
const std::string name =
tonic::DartConverter<std::string>::FromArguments(args, 1, exception);
if (exception) {
Dart_ThrowException(exception);
return;
}
UIDartState::Current()->SetDebugName(name);
}
void SetNeedsReportTimings(Dart_NativeArguments args) {
UIDartState::ThrowIfUIOperationsProhibited();
Dart_Handle exception = nullptr;
bool value = tonic::DartConverter<bool>::FromArguments(args, 1, exception);
UIDartState::Current()
->platform_configuration()
->client()
->SetNeedsReportTimings(value);
}
void ReportUnhandledException(Dart_NativeArguments args) {
UIDartState::ThrowIfUIOperationsProhibited();
Dart_Handle exception = nullptr;
auto error_name =
tonic::DartConverter<std::string>::FromArguments(args, 0, exception);
if (exception) {
Dart_ThrowException(exception);
return;
}
auto stack_trace =
tonic::DartConverter<std::string>::FromArguments(args, 1, exception);
if (exception) {
Dart_ThrowException(exception);
return;
}
UIDartState::Current()->ReportUnhandledException(std::move(error_name),
std::move(stack_trace));
}
Dart_Handle SendPlatformMessage(Dart_Handle window,
const std::string& name,
Dart_Handle callback,
Dart_Handle data_handle) {
UIDartState* dart_state = UIDartState::Current();
if (!dart_state->platform_configuration()) {
return tonic::ToDart(
"Platform messages can only be sent from the main isolate");
}
fml::RefPtr<PlatformMessageResponse> response;
if (!Dart_IsNull(callback)) {
response = fml::MakeRefCounted<PlatformMessageResponseDart>(
tonic::DartPersistentValue(dart_state, callback),
dart_state->GetTaskRunners().GetUITaskRunner());
}
if (Dart_IsNull(data_handle)) {
dart_state->platform_configuration()->client()->HandlePlatformMessage(
fml::MakeRefCounted<PlatformMessage>(name, response));
} else {
tonic::DartByteData data(data_handle);
const uint8_t* buffer = static_cast<const uint8_t*>(data.data());
dart_state->platform_configuration()->client()->HandlePlatformMessage(
fml::MakeRefCounted<PlatformMessage>(
name, std::vector<uint8_t>(buffer, buffer + data.length_in_bytes()),
response));
}
return Dart_Null();
}
void _SendPlatformMessage(Dart_NativeArguments args) {
tonic::DartCallStatic(&SendPlatformMessage, args);
}
void RespondToPlatformMessage(Dart_Handle window,
int response_id,
const tonic::DartByteData& data) {
if (Dart_IsNull(data.dart_handle())) {
UIDartState::Current()
->platform_configuration()
->CompletePlatformMessageEmptyResponse(response_id);
} else {
// TODO(engine): Avoid this copy.
const uint8_t* buffer = static_cast<const uint8_t*>(data.data());
UIDartState::Current()
->platform_configuration()
->CompletePlatformMessageResponse(
response_id,
std::vector<uint8_t>(buffer, buffer + data.length_in_bytes()));
}
}
void _RespondToPlatformMessage(Dart_NativeArguments args) {
tonic::DartCallStatic(&RespondToPlatformMessage, args);
}
void GetPersistentIsolateData(Dart_NativeArguments args) {
UIDartState::ThrowIfUIOperationsProhibited();
auto persistent_isolate_data = UIDartState::Current()
->platform_configuration()
->client()
->GetPersistentIsolateData();
if (!persistent_isolate_data) {
Dart_SetReturnValue(args, Dart_Null());
return;
}
Dart_SetReturnValue(
args, tonic::DartByteData::Create(persistent_isolate_data->GetMapping(),
persistent_isolate_data->GetSize()));
}
Dart_Handle ToByteData(const std::vector<uint8_t>& buffer) {
return tonic::DartByteData::Create(buffer.data(), buffer.size());
}
} // namespace
PlatformConfigurationClient::~PlatformConfigurationClient() {}
PlatformConfiguration::PlatformConfiguration(
PlatformConfigurationClient* client)
: client_(client) {}
PlatformConfiguration::~PlatformConfiguration() {}
void PlatformConfiguration::DidCreateIsolate() {
library_.Set(tonic::DartState::Current(),
Dart_LookupLibrary(tonic::ToDart("dart:ui")));
windows_.insert(std::make_pair(0, std::unique_ptr<Window>(new Window{
0, ViewportMetrics{1.0, 0.0, 0.0}})));
}
void PlatformConfiguration::UpdateLocales(
const std::vector<std::string>& locales) {
std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();
if (!dart_state) {
return;
}
tonic::DartState::Scope scope(dart_state);
tonic::LogIfError(tonic::DartInvokeField(
library_.value(), "_updateLocales",
{
tonic::ToDart<std::vector<std::string>>(locales),
}));
}
void PlatformConfiguration::UpdateUserSettingsData(const std::string& data) {
std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();
if (!dart_state) {
return;
}
tonic::DartState::Scope scope(dart_state);
tonic::LogIfError(tonic::DartInvokeField(library_.value(),
"_updateUserSettingsData",
{
tonic::StdStringToDart(data),
}));
}
void PlatformConfiguration::UpdateLifecycleState(const std::string& data) {
std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();
if (!dart_state) {
return;
}
tonic::DartState::Scope scope(dart_state);
tonic::LogIfError(tonic::DartInvokeField(library_.value(),
"_updateLifecycleState",
{
tonic::StdStringToDart(data),
}));
}
void PlatformConfiguration::UpdateSemanticsEnabled(bool enabled) {
std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();
if (!dart_state) {
return;
}
tonic::DartState::Scope scope(dart_state);
UIDartState::ThrowIfUIOperationsProhibited();
tonic::LogIfError(tonic::DartInvokeField(
library_.value(), "_updateSemanticsEnabled", {tonic::ToDart(enabled)}));
}
void PlatformConfiguration::UpdateAccessibilityFeatures(int32_t values) {
std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();
if (!dart_state) {
return;
}
tonic::DartState::Scope scope(dart_state);
tonic::LogIfError(tonic::DartInvokeField(library_.value(),
"_updateAccessibilityFeatures",
{tonic::ToDart(values)}));
}
void PlatformConfiguration::DispatchPlatformMessage(
fml::RefPtr<PlatformMessage> message) {
std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();
if (!dart_state) {
FML_DLOG(WARNING)
<< "Dropping platform message for lack of DartState on channel: "
<< message->channel();
return;
}
tonic::DartState::Scope scope(dart_state);
Dart_Handle data_handle =
(message->hasData()) ? ToByteData(message->data()) : Dart_Null();
if (Dart_IsError(data_handle)) {
FML_DLOG(WARNING)
<< "Dropping platform message because of a Dart error on channel: "
<< message->channel();
return;
}
int response_id = 0;
if (auto response = message->response()) {
response_id = next_response_id_++;
pending_responses_[response_id] = response;
}
tonic::LogIfError(
tonic::DartInvokeField(library_.value(), "_dispatchPlatformMessage",
{tonic::ToDart(message->channel()), data_handle,
tonic::ToDart(response_id)}));
}
void PlatformConfiguration::DispatchSemanticsAction(int32_t id,
SemanticsAction action,
std::vector<uint8_t> args) {
std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();
if (!dart_state) {
return;
}
tonic::DartState::Scope scope(dart_state);
Dart_Handle args_handle = (args.empty()) ? Dart_Null() : ToByteData(args);
if (Dart_IsError(args_handle)) {
return;
}
tonic::LogIfError(tonic::DartInvokeField(
library_.value(), "_dispatchSemanticsAction",
{tonic::ToDart(id), tonic::ToDart(static_cast<int32_t>(action)),
args_handle}));
}
void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime) {
std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();
if (!dart_state) {
return;
}
tonic::DartState::Scope scope(dart_state);
int64_t microseconds = (frameTime - fml::TimePoint()).ToMicroseconds();
tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_beginFrame",
{
Dart_NewInteger(microseconds),
}));
UIDartState::Current()->FlushMicrotasksNow();
tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_drawFrame", {}));
}
void PlatformConfiguration::ReportTimings(std::vector<int64_t> timings) {
std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();
if (!dart_state) {
return;
}
tonic::DartState::Scope scope(dart_state);
Dart_Handle data_handle =
Dart_NewTypedData(Dart_TypedData_kInt64, timings.size());
Dart_TypedData_Type type;
void* data = nullptr;
intptr_t num_acquired = 0;
FML_CHECK(!Dart_IsError(
Dart_TypedDataAcquireData(data_handle, &type, &data, &num_acquired)));
FML_DCHECK(num_acquired == static_cast<int>(timings.size()));
memcpy(data, timings.data(), sizeof(int64_t) * timings.size());
FML_CHECK(Dart_TypedDataReleaseData(data_handle));
tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_reportTimings",
{
data_handle,
}));
}
void PlatformConfiguration::CompletePlatformMessageEmptyResponse(
int response_id) {
if (!response_id) {
return;
}
auto it = pending_responses_.find(response_id);
if (it == pending_responses_.end()) {
return;
}
auto response = std::move(it->second);
pending_responses_.erase(it);
response->CompleteEmpty();
}
void PlatformConfiguration::CompletePlatformMessageResponse(
int response_id,
std::vector<uint8_t> data) {
if (!response_id) {
return;
}
auto it = pending_responses_.find(response_id);
if (it == pending_responses_.end()) {
return;
}
auto response = std::move(it->second);
pending_responses_.erase(it);
response->Complete(std::make_unique<fml::DataMapping>(std::move(data)));
}
Dart_Handle ComputePlatformResolvedLocale(Dart_Handle supportedLocalesHandle) {
std::vector<std::string> supportedLocales =
tonic::DartConverter<std::vector<std::string>>::FromDart(
supportedLocalesHandle);
std::vector<std::string> results =
*UIDartState::Current()
->platform_configuration()
->client()
->ComputePlatformResolvedLocale(supportedLocales);
return tonic::DartConverter<std::vector<std::string>>::ToDart(results);
}
static void _ComputePlatformResolvedLocale(Dart_NativeArguments args) {
UIDartState::ThrowIfUIOperationsProhibited();
Dart_Handle result =
ComputePlatformResolvedLocale(Dart_GetNativeArgument(args, 1));
Dart_SetReturnValue(args, result);
}
void PlatformConfiguration::RegisterNatives(
tonic::DartLibraryNatives* natives) {
natives->Register({
{"PlatformConfiguration_defaultRouteName", DefaultRouteName, 1, true},
{"PlatformConfiguration_scheduleFrame", ScheduleFrame, 1, true},
{"PlatformConfiguration_sendPlatformMessage", _SendPlatformMessage, 4,
true},
{"PlatformConfiguration_respondToPlatformMessage",
_RespondToPlatformMessage, 3, true},
{"PlatformConfiguration_render", Render, 3, true},
{"PlatformConfiguration_updateSemantics", UpdateSemantics, 2, true},
{"PlatformConfiguration_setIsolateDebugName", SetIsolateDebugName, 2,
true},
{"PlatformConfiguration_reportUnhandledException",
ReportUnhandledException, 2, true},
{"PlatformConfiguration_setNeedsReportTimings", SetNeedsReportTimings, 2,
true},
{"PlatformConfiguration_getPersistentIsolateData",
GetPersistentIsolateData, 1, true},
{"PlatformConfiguration_computePlatformResolvedLocale",
_ComputePlatformResolvedLocale, 2, true},
});
}
} // namespace flutter