| // 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 "flow/embedded_views.h" | |
| #include "pointer_injector_delegate.h" | |
| #define RAPIDJSON_HAS_STDSTRING 1 | |
| #include "platform_view.h" | |
| #include <algorithm> | |
| #include <cstring> | |
| #include <limits> | |
| #include <sstream> | |
| #include "flutter/fml/logging.h" | |
| #include "flutter/fml/make_copyable.h" | |
| #include "flutter/lib/ui/window/pointer_data.h" | |
| #include "flutter/lib/ui/window/window.h" | |
| #include "flutter/shell/platform/common/client_wrapper/include/flutter/encodable_value.h" | |
| #include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_message_codec.h" | |
| #include "third_party/rapidjson/include/rapidjson/document.h" | |
| #include "third_party/rapidjson/include/rapidjson/stringbuffer.h" | |
| #include "third_party/rapidjson/include/rapidjson/writer.h" | |
| #include "logging.h" | |
| #include "runtime/dart/utils/inlines.h" | |
| #include "text_delegate.h" | |
| #include "vsync_waiter.h" | |
| namespace flutter_runner { | |
| static constexpr char kFlutterPlatformChannel[] = "flutter/platform"; | |
| static constexpr char kAccessibilityChannel[] = "flutter/accessibility"; | |
| static constexpr char kFlutterPlatformViewsChannel[] = "flutter/platform_views"; | |
| static constexpr char kFuchsiaShaderWarmupChannel[] = "fuchsia/shader_warmup"; | |
| PlatformView::PlatformView( | |
| bool is_flatland, | |
| flutter::PlatformView::Delegate& delegate, | |
| flutter::TaskRunners task_runners, | |
| fuchsia::ui::views::ViewRef view_ref, | |
| std::shared_ptr<flutter::ExternalViewEmbedder> external_view_embedder, | |
| fuchsia::ui::input::ImeServiceHandle ime_service, | |
| fuchsia::ui::input3::KeyboardHandle keyboard, | |
| fuchsia::ui::pointer::TouchSourceHandle touch_source, | |
| fuchsia::ui::pointer::MouseSourceHandle mouse_source, | |
| fuchsia::ui::views::FocuserHandle focuser, | |
| fuchsia::ui::views::ViewRefFocusedHandle view_ref_focused, | |
| fuchsia::ui::pointerinjector::RegistryHandle pointerinjector_registry, | |
| OnEnableWireframe wireframe_enabled_callback, | |
| OnUpdateView on_update_view_callback, | |
| OnCreateSurface on_create_surface_callback, | |
| OnSemanticsNodeUpdate on_semantics_node_update_callback, | |
| OnRequestAnnounce on_request_announce_callback, | |
| OnShaderWarmup on_shader_warmup, | |
| AwaitVsyncCallback await_vsync_callback, | |
| AwaitVsyncForSecondaryCallbackCallback | |
| await_vsync_for_secondary_callback_callback) | |
| : flutter::PlatformView(delegate, std::move(task_runners)), | |
| external_view_embedder_(external_view_embedder), | |
| focus_delegate_( | |
| std::make_shared<FocusDelegate>(std::move(view_ref_focused), | |
| std::move(focuser))), | |
| pointer_delegate_( | |
| std::make_shared<PointerDelegate>(std::move(touch_source), | |
| std::move(mouse_source))), | |
| wireframe_enabled_callback_(std::move(wireframe_enabled_callback)), | |
| on_update_view_callback_(std::move(on_update_view_callback)), | |
| on_create_surface_callback_(std::move(on_create_surface_callback)), | |
| on_semantics_node_update_callback_( | |
| std::move(on_semantics_node_update_callback)), | |
| on_request_announce_callback_(std::move(on_request_announce_callback)), | |
| on_shader_warmup_(std::move(on_shader_warmup)), | |
| await_vsync_callback_(await_vsync_callback), | |
| await_vsync_for_secondary_callback_callback_( | |
| await_vsync_for_secondary_callback_callback), | |
| weak_factory_(this) { | |
| fuchsia::ui::views::ViewRef view_ref_clone; | |
| fidl::Clone(view_ref, &view_ref_clone); | |
| text_delegate_ = | |
| std::make_unique<TextDelegate>( | |
| std::move(view_ref), std::move(ime_service), std::move(keyboard), | |
| [weak = weak_factory_.GetWeakPtr()]( | |
| std::unique_ptr<flutter::PlatformMessage> message) { | |
| if (!weak) { | |
| FML_LOG(WARNING) | |
| << "PlatformView use-after-free attempted. Ignoring."; | |
| } | |
| weak->delegate_.OnPlatformViewDispatchPlatformMessage( | |
| std::move(message)); | |
| }); | |
| // Begin watching for focus changes. | |
| focus_delegate_->WatchLoop([weak = weak_factory_.GetWeakPtr()](bool focused) { | |
| if (!weak) { | |
| FML_LOG(WARNING) << "PlatformView use-after-free attempted. Ignoring."; | |
| return; | |
| } | |
| // Ensure last_text_state_ is set to make sure Flutter actually wants | |
| // an IME. | |
| if (focused && weak->text_delegate_->HasTextState()) { | |
| weak->text_delegate_->ActivateIme(); | |
| } else if (!focused) { | |
| weak->text_delegate_->DeactivateIme(); | |
| } | |
| }); | |
| // Begin watching for pointer events. | |
| pointer_delegate_->WatchLoop([weak = weak_factory_.GetWeakPtr()]( | |
| std::vector<flutter::PointerData> events) { | |
| if (!weak) { | |
| FML_LOG(WARNING) << "PlatformView use-after-free attempted. Ignoring."; | |
| return; | |
| } | |
| if (events.empty()) { | |
| return; // No work, bounce out. | |
| } | |
| // If pixel ratio hasn't been set, use a default value of 1. | |
| const float pixel_ratio = weak->view_pixel_ratio_.value_or(1.f); | |
| auto packet = std::make_unique<flutter::PointerDataPacket>(events.size()); | |
| for (size_t i = 0; i < events.size(); ++i) { | |
| auto& event = events[i]; | |
| // Translate logical to physical coordinates, as per | |
| // flutter::PointerData contract. Done here because pixel ratio comes | |
| // from the graphics API. | |
| event.physical_x = event.physical_x * pixel_ratio; | |
| event.physical_y = event.physical_y * pixel_ratio; | |
| packet->SetPointerData(i, event); | |
| } | |
| weak->DispatchPointerDataPacket(std::move(packet)); | |
| }); | |
| // Configure the pointer injector delegate. | |
| pointer_injector_delegate_ = std::make_unique<PointerInjectorDelegate>( | |
| std::move(pointerinjector_registry), std::move(view_ref_clone), | |
| is_flatland); | |
| // Finally! Register the native platform message handlers. | |
| RegisterPlatformMessageHandlers(); | |
| } | |
| PlatformView::~PlatformView() = default; | |
| void PlatformView::RegisterPlatformMessageHandlers() { | |
| platform_message_handlers_[kFlutterPlatformChannel] = | |
| std::bind(&PlatformView::HandleFlutterPlatformChannelPlatformMessage, | |
| this, std::placeholders::_1); | |
| platform_message_handlers_[kTextInputChannel] = | |
| std::bind(&TextDelegate::HandleFlutterTextInputChannelPlatformMessage, | |
| text_delegate_.get(), std::placeholders::_1); | |
| platform_message_handlers_[kAccessibilityChannel] = | |
| std::bind(&PlatformView::HandleAccessibilityChannelPlatformMessage, this, | |
| std::placeholders::_1); | |
| platform_message_handlers_[kFlutterPlatformViewsChannel] = | |
| std::bind(&PlatformView::HandleFlutterPlatformViewsChannelPlatformMessage, | |
| this, std::placeholders::_1); | |
| platform_message_handlers_[kFuchsiaShaderWarmupChannel] = | |
| std::bind(&HandleFuchsiaShaderWarmupChannelPlatformMessage, | |
| on_shader_warmup_, std::placeholders::_1); | |
| } | |
| static flutter::PointerData::Change GetChangeFromPointerEventPhase( | |
| fuchsia::ui::input::PointerEventPhase phase) { | |
| switch (phase) { | |
| case fuchsia::ui::input::PointerEventPhase::ADD: | |
| return flutter::PointerData::Change::kAdd; | |
| case fuchsia::ui::input::PointerEventPhase::HOVER: | |
| return flutter::PointerData::Change::kHover; | |
| case fuchsia::ui::input::PointerEventPhase::DOWN: | |
| return flutter::PointerData::Change::kDown; | |
| case fuchsia::ui::input::PointerEventPhase::MOVE: | |
| return flutter::PointerData::Change::kMove; | |
| case fuchsia::ui::input::PointerEventPhase::UP: | |
| return flutter::PointerData::Change::kUp; | |
| case fuchsia::ui::input::PointerEventPhase::REMOVE: | |
| return flutter::PointerData::Change::kRemove; | |
| case fuchsia::ui::input::PointerEventPhase::CANCEL: | |
| return flutter::PointerData::Change::kCancel; | |
| default: | |
| return flutter::PointerData::Change::kCancel; | |
| } | |
| } | |
| static flutter::PointerData::DeviceKind GetKindFromPointerType( | |
| fuchsia::ui::input::PointerEventType type) { | |
| switch (type) { | |
| case fuchsia::ui::input::PointerEventType::TOUCH: | |
| return flutter::PointerData::DeviceKind::kTouch; | |
| case fuchsia::ui::input::PointerEventType::MOUSE: | |
| return flutter::PointerData::DeviceKind::kMouse; | |
| default: | |
| return flutter::PointerData::DeviceKind::kTouch; | |
| } | |
| } | |
| // TODO(SCN-1278): Remove this. | |
| // Turns two floats (high bits, low bits) into a 64-bit uint. | |
| static trace_flow_id_t PointerTraceHACK(float fa, float fb) { | |
| uint32_t ia, ib; | |
| memcpy(&ia, &fa, sizeof(uint32_t)); | |
| memcpy(&ib, &fb, sizeof(uint32_t)); | |
| return (((uint64_t)ia) << 32) | ib; | |
| } | |
| // For certain scenarios that must avoid floating-point drift, compute a | |
| // coordinate that falls within the logical view bounding box. | |
| std::array<float, 2> PlatformView::ClampToViewSpace(const float x, | |
| const float y) const { | |
| if (!view_logical_size_.has_value() || !view_logical_origin_.has_value()) { | |
| return {x, y}; // If we can't do anything, return the original values. | |
| } | |
| const auto origin = view_logical_origin_.value(); | |
| const auto size = view_logical_size_.value(); | |
| const float min_x = origin[0]; | |
| const float max_x = origin[0] + size[0]; | |
| const float min_y = origin[1]; | |
| const float max_y = origin[1] + size[1]; | |
| if (min_x <= x && x < max_x && min_y <= y && y < max_y) { | |
| return {x, y}; // No clamping to perform. | |
| } | |
| // View boundary is [min_x, max_x) x [min_y, max_y). Note that min is | |
| // inclusive, but max is exclusive - so we subtract epsilon. | |
| const float max_x_inclusive = max_x - std::numeric_limits<float>::epsilon(); | |
| const float max_y_inclusive = max_y - std::numeric_limits<float>::epsilon(); | |
| const float& clamped_x = std::clamp(x, min_x, max_x_inclusive); | |
| const float& clamped_y = std::clamp(y, min_y, max_y_inclusive); | |
| FML_LOG(INFO) << "Clamped (" << x << ", " << y << ") to (" << clamped_x | |
| << ", " << clamped_y << ")."; | |
| return {clamped_x, clamped_y}; | |
| } | |
| bool PlatformView::OnHandlePointerEvent( | |
| const fuchsia::ui::input::PointerEvent& pointer) { | |
| TRACE_EVENT0("flutter", "PlatformView::OnHandlePointerEvent"); | |
| // TODO(SCN-1278): Use proper trace_id for tracing flow. | |
| trace_flow_id_t trace_id = | |
| PointerTraceHACK(pointer.radius_major, pointer.radius_minor); | |
| TRACE_FLOW_END("input", "dispatch_event_to_client", trace_id); | |
| const float pixel_ratio = | |
| view_pixel_ratio_.has_value() ? *view_pixel_ratio_ : 0.f; | |
| flutter::PointerData pointer_data; | |
| pointer_data.Clear(); | |
| pointer_data.time_stamp = pointer.event_time / 1000; | |
| pointer_data.change = GetChangeFromPointerEventPhase(pointer.phase); | |
| pointer_data.kind = GetKindFromPointerType(pointer.type); | |
| pointer_data.device = pointer.pointer_id; | |
| // Pointer events are in logical pixels, so scale to physical. | |
| pointer_data.physical_x = pointer.x * pixel_ratio; | |
| pointer_data.physical_y = pointer.y * pixel_ratio; | |
| // Buttons are single bit values starting with kMousePrimaryButton = 1. | |
| pointer_data.buttons = static_cast<uint64_t>(pointer.buttons); | |
| switch (pointer_data.change) { | |
| case flutter::PointerData::Change::kDown: { | |
| // Make the pointer start in the view space, despite numerical drift. | |
| auto clamped_pointer = ClampToViewSpace(pointer.x, pointer.y); | |
| pointer_data.physical_x = clamped_pointer[0] * pixel_ratio; | |
| pointer_data.physical_y = clamped_pointer[1] * pixel_ratio; | |
| down_pointers_.insert(pointer_data.device); | |
| break; | |
| } | |
| case flutter::PointerData::Change::kCancel: | |
| case flutter::PointerData::Change::kUp: | |
| down_pointers_.erase(pointer_data.device); | |
| break; | |
| case flutter::PointerData::Change::kMove: | |
| if (down_pointers_.count(pointer_data.device) == 0) { | |
| pointer_data.change = flutter::PointerData::Change::kHover; | |
| } | |
| break; | |
| case flutter::PointerData::Change::kAdd: | |
| if (down_pointers_.count(pointer_data.device) != 0) { | |
| FML_LOG(ERROR) << "Received add event for down pointer."; | |
| } | |
| break; | |
| case flutter::PointerData::Change::kRemove: | |
| if (down_pointers_.count(pointer_data.device) != 0) { | |
| FML_LOG(ERROR) << "Received remove event for down pointer."; | |
| } | |
| break; | |
| case flutter::PointerData::Change::kHover: | |
| if (down_pointers_.count(pointer_data.device) != 0) { | |
| FML_LOG(ERROR) << "Received hover event for down pointer."; | |
| } | |
| break; | |
| case flutter::PointerData::Change::kPanZoomStart: | |
| case flutter::PointerData::Change::kPanZoomUpdate: | |
| case flutter::PointerData::Change::kPanZoomEnd: | |
| FML_DLOG(ERROR) << "Unexpectedly received pointer pan/zoom event"; | |
| break; | |
| } | |
| auto packet = std::make_unique<flutter::PointerDataPacket>(1); | |
| packet->SetPointerData(0, pointer_data); | |
| DispatchPointerDataPacket(std::move(packet)); | |
| return true; | |
| } | |
| // |flutter::PlatformView| | |
| std::unique_ptr<flutter::VsyncWaiter> PlatformView::CreateVSyncWaiter() { | |
| return std::make_unique<flutter_runner::VsyncWaiter>( | |
| await_vsync_callback_, await_vsync_for_secondary_callback_callback_, | |
| task_runners_); | |
| } | |
| // |flutter::PlatformView| | |
| std::unique_ptr<flutter::Surface> PlatformView::CreateRenderingSurface() { | |
| return on_create_surface_callback_ ? on_create_surface_callback_() : nullptr; | |
| } | |
| // |flutter::PlatformView| | |
| std::shared_ptr<flutter::ExternalViewEmbedder> | |
| PlatformView::CreateExternalViewEmbedder() { | |
| return external_view_embedder_; | |
| } | |
| // |flutter::PlatformView| | |
| void PlatformView::HandlePlatformMessage( | |
| std::unique_ptr<flutter::PlatformMessage> message) { | |
| if (!message) { | |
| return; | |
| } | |
| const std::string channel = message->channel(); | |
| auto found = platform_message_handlers_.find(channel); | |
| if (found == platform_message_handlers_.end()) { | |
| const bool already_errored = unregistered_channels_.count(channel); | |
| if (!already_errored) { | |
| FML_LOG(INFO) | |
| << "Platform view received message on channel '" << message->channel() | |
| << "' with no registered handler. An empty response will be " | |
| "generated. Please implement the native message handler. This " | |
| "message will appear only once per channel."; | |
| unregistered_channels_.insert(channel); | |
| } | |
| flutter::PlatformView::HandlePlatformMessage(std::move(message)); | |
| return; | |
| } | |
| auto response = message->response(); | |
| bool response_handled = found->second(std::move(message)); | |
| // Ensure all responses are completed. | |
| if (response && !response_handled) { | |
| // response_handled should be true if the response was completed. | |
| FML_DCHECK(!response->is_complete()); | |
| response->CompleteEmpty(); | |
| } | |
| } | |
| // |flutter::PlatformView| | |
| void PlatformView::SetSemanticsEnabled(bool enabled) { | |
| flutter::PlatformView::SetSemanticsEnabled(enabled); | |
| if (enabled) { | |
| SetAccessibilityFeatures(static_cast<int32_t>( | |
| flutter::AccessibilityFeatureFlag::kAccessibleNavigation)); | |
| } else { | |
| SetAccessibilityFeatures(0); | |
| } | |
| } | |
| // |flutter::PlatformView| | |
| void PlatformView::UpdateSemantics( | |
| flutter::SemanticsNodeUpdates update, | |
| flutter::CustomAccessibilityActionUpdates actions) { | |
| const float pixel_ratio = | |
| view_pixel_ratio_.has_value() ? *view_pixel_ratio_ : 0.f; | |
| on_semantics_node_update_callback_(update, pixel_ratio); | |
| } | |
| // Channel handler for kAccessibilityChannel | |
| bool PlatformView::HandleAccessibilityChannelPlatformMessage( | |
| std::unique_ptr<flutter::PlatformMessage> message) { | |
| FML_DCHECK(message->channel() == kAccessibilityChannel); | |
| const flutter::StandardMessageCodec& standard_message_codec = | |
| flutter::StandardMessageCodec::GetInstance(nullptr); | |
| std::unique_ptr<flutter::EncodableValue> decoded = | |
| standard_message_codec.DecodeMessage(message->data().GetMapping(), | |
| message->data().GetSize()); | |
| flutter::EncodableMap map = std::get<flutter::EncodableMap>(*decoded); | |
| std::string type = | |
| std::get<std::string>(map.at(flutter::EncodableValue("type"))); | |
| if (type == "announce") { | |
| flutter::EncodableMap data_map = std::get<flutter::EncodableMap>( | |
| map.at(flutter::EncodableValue("data"))); | |
| std::string text = | |
| std::get<std::string>(data_map.at(flutter::EncodableValue("message"))); | |
| on_request_announce_callback_(text); | |
| } | |
| // Complete with an empty response. | |
| return false; | |
| } | |
| // Channel handler for kFlutterPlatformChannel | |
| bool PlatformView::HandleFlutterPlatformChannelPlatformMessage( | |
| std::unique_ptr<flutter::PlatformMessage> message) { | |
| FML_DCHECK(message->channel() == kFlutterPlatformChannel); | |
| // Fuchsia does not handle any platform messages at this time. | |
| // Complete with an empty response. | |
| return false; | |
| } | |
| bool PlatformView::HandleFlutterPlatformViewsChannelPlatformMessage( | |
| std::unique_ptr<flutter::PlatformMessage> message) { | |
| FML_DCHECK(message->channel() == kFlutterPlatformViewsChannel); | |
| const auto& data = message->data(); | |
| rapidjson::Document document; | |
| document.Parse(reinterpret_cast<const char*>(data.GetMapping()), | |
| data.GetSize()); | |
| if (document.HasParseError() || !document.IsObject()) { | |
| FML_LOG(ERROR) << "Could not parse document"; | |
| return false; | |
| } | |
| auto root = document.GetObject(); | |
| auto method_member = root.FindMember("method"); | |
| if (method_member == root.MemberEnd() || !method_member->value.IsString()) { | |
| return false; | |
| } | |
| std::string method(method_member->value.GetString()); | |
| if (method == "View.enableWireframe") { | |
| auto args_it = root.FindMember("args"); | |
| if (args_it == root.MemberEnd() || !args_it->value.IsObject()) { | |
| FML_LOG(ERROR) << "No arguments found."; | |
| return false; | |
| } | |
| const auto& args = args_it->value; | |
| auto enable = args.FindMember("enable"); | |
| if (!enable->value.IsBool()) { | |
| FML_LOG(ERROR) << "Argument 'enable' is not a bool"; | |
| return false; | |
| } | |
| wireframe_enabled_callback_(enable->value.GetBool()); | |
| } else if (method == "View.create") { | |
| auto args_it = root.FindMember("args"); | |
| if (args_it == root.MemberEnd() || !args_it->value.IsObject()) { | |
| FML_LOG(ERROR) << "No arguments found."; | |
| return false; | |
| } | |
| const auto& args = args_it->value; | |
| auto view_id = args.FindMember("viewId"); | |
| if (!view_id->value.IsUint64()) { | |
| FML_LOG(ERROR) << "Argument 'viewId' is not a int64"; | |
| return false; | |
| } | |
| auto hit_testable = args.FindMember("hitTestable"); | |
| if (!hit_testable->value.IsBool()) { | |
| FML_LOG(ERROR) << "Argument 'hitTestable' is not a bool"; | |
| return false; | |
| } | |
| auto focusable = args.FindMember("focusable"); | |
| if (!focusable->value.IsBool()) { | |
| FML_LOG(ERROR) << "Argument 'focusable' is not a bool"; | |
| return false; | |
| } | |
| auto on_view_created = fml::MakeCopyable( | |
| [platform_task_runner = task_runners_.GetPlatformTaskRunner(), | |
| message = std::move(message)]() { | |
| // The client is waiting for view creation. Send an empty response | |
| // back to signal the view was created. | |
| if (message->response()) { | |
| message->response()->Complete(std::make_unique<fml::DataMapping>( | |
| std::vector<uint8_t>({'[', '0', ']'}))); | |
| } | |
| }); | |
| OnCreateView(std::move(on_view_created), view_id->value.GetUint64(), | |
| hit_testable->value.GetBool(), focusable->value.GetBool()); | |
| return true; | |
| } else if (method == "View.update") { | |
| auto args_it = root.FindMember("args"); | |
| if (args_it == root.MemberEnd() || !args_it->value.IsObject()) { | |
| FML_LOG(ERROR) << "No arguments found."; | |
| return false; | |
| } | |
| const auto& args = args_it->value; | |
| auto view_id = args.FindMember("viewId"); | |
| if (!view_id->value.IsUint64()) { | |
| FML_LOG(ERROR) << "Argument 'viewId' is not a int64"; | |
| return false; | |
| } | |
| auto hit_testable = args.FindMember("hitTestable"); | |
| if (!hit_testable->value.IsBool()) { | |
| FML_LOG(ERROR) << "Argument 'hitTestable' is not a bool"; | |
| return false; | |
| } | |
| auto focusable = args.FindMember("focusable"); | |
| if (!focusable->value.IsBool()) { | |
| FML_LOG(ERROR) << "Argument 'focusable' is not a bool"; | |
| return false; | |
| } | |
| SkRect view_occlusion_hint_raw = SkRect::MakeEmpty(); | |
| auto view_occlusion_hint = args.FindMember("viewOcclusionHintLTRB"); | |
| if (view_occlusion_hint != args.MemberEnd()) { | |
| if (view_occlusion_hint->value.IsArray()) { | |
| const auto& view_occlusion_hint_array = | |
| view_occlusion_hint->value.GetArray(); | |
| if (view_occlusion_hint_array.Size() == 4) { | |
| bool parse_error = false; | |
| for (int i = 0; i < 4; i++) { | |
| auto& array_val = view_occlusion_hint_array[i]; | |
| if (!array_val.IsDouble()) { | |
| FML_LOG(ERROR) << "Argument 'viewOcclusionHintLTRB' element " << i | |
| << " is not a double"; | |
| parse_error = true; | |
| break; | |
| } | |
| } | |
| if (!parse_error) { | |
| view_occlusion_hint_raw = | |
| SkRect::MakeLTRB(view_occlusion_hint_array[0].GetDouble(), | |
| view_occlusion_hint_array[1].GetDouble(), | |
| view_occlusion_hint_array[2].GetDouble(), | |
| view_occlusion_hint_array[3].GetDouble()); | |
| } | |
| } else { | |
| FML_LOG(ERROR) | |
| << "Argument 'viewOcclusionHintLTRB' expected size 4; got " | |
| << view_occlusion_hint_array.Size(); | |
| } | |
| } else { | |
| FML_LOG(ERROR) | |
| << "Argument 'viewOcclusionHintLTRB' is not a double array"; | |
| } | |
| } else { | |
| FML_LOG(WARNING) << "Argument 'viewOcclusionHintLTRB' is missing"; | |
| } | |
| on_update_view_callback_( | |
| view_id->value.GetUint64(), view_occlusion_hint_raw, | |
| hit_testable->value.GetBool(), focusable->value.GetBool()); | |
| if (message->response()) { | |
| message->response()->Complete(std::make_unique<fml::DataMapping>( | |
| std::vector<uint8_t>({'[', '0', ']'}))); | |
| return true; | |
| } | |
| } else if (method == "View.dispose") { | |
| auto args_it = root.FindMember("args"); | |
| if (args_it == root.MemberEnd() || !args_it->value.IsObject()) { | |
| FML_LOG(ERROR) << "No arguments found."; | |
| return false; | |
| } | |
| const auto& args = args_it->value; | |
| auto view_id = args.FindMember("viewId"); | |
| if (!view_id->value.IsUint64()) { | |
| FML_LOG(ERROR) << "Argument 'viewId' is not a int64"; | |
| return false; | |
| } | |
| OnDisposeView(view_id->value.GetUint64()); | |
| if (message->response()) { | |
| message->response()->Complete(std::make_unique<fml::DataMapping>( | |
| std::vector<uint8_t>({'[', '0', ']'}))); | |
| return true; | |
| } | |
| } else if (method.rfind("View.focus", 0) == 0) { | |
| return focus_delegate_->HandlePlatformMessage(root, message->response()); | |
| } else if (method.rfind(PointerInjectorDelegate::kPointerInjectorMethodPrefix, | |
| 0) == 0) { | |
| return pointer_injector_delegate_->HandlePlatformMessage( | |
| root, message->response()); | |
| } else { | |
| FML_LOG(ERROR) << "Unknown " << message->channel() << " method " << method; | |
| } | |
| // Complete with an empty response by default. | |
| return false; | |
| } | |
| bool PlatformView::HandleFuchsiaShaderWarmupChannelPlatformMessage( | |
| OnShaderWarmup on_shader_warmup, | |
| std::unique_ptr<flutter::PlatformMessage> message) { | |
| FML_DCHECK(message->channel() == kFuchsiaShaderWarmupChannel); | |
| if (!on_shader_warmup) { | |
| FML_LOG(ERROR) << "No shader warmup callback set!"; | |
| std::string result = "[0]"; | |
| message->response()->Complete( | |
| std::make_unique<fml::DataMapping>(std::vector<uint8_t>( | |
| (const uint8_t*)result.c_str(), | |
| (const uint8_t*)result.c_str() + result.length()))); | |
| return true; | |
| } | |
| const auto& data = message->data(); | |
| rapidjson::Document document; | |
| document.Parse(reinterpret_cast<const char*>(data.GetMapping()), | |
| data.GetSize()); | |
| if (document.HasParseError() || !document.IsObject()) { | |
| FML_LOG(ERROR) << "Could not parse document"; | |
| return false; | |
| } | |
| auto root = document.GetObject(); | |
| auto method = root.FindMember("method"); | |
| if (method == root.MemberEnd() || !method->value.IsString() || | |
| method->value != "WarmupSkps") { | |
| FML_LOG(ERROR) << "Invalid method name"; | |
| return false; | |
| } | |
| auto args_it = root.FindMember("args"); | |
| if (args_it == root.MemberEnd() || !args_it->value.IsObject()) { | |
| FML_LOG(ERROR) << "No arguments found."; | |
| return false; | |
| } | |
| auto shaders_it = root["args"].FindMember("shaders"); | |
| if (shaders_it == root["args"].MemberEnd() || !shaders_it->value.IsArray()) { | |
| FML_LOG(ERROR) << "No shaders found."; | |
| return false; | |
| } | |
| auto width_it = root["args"].FindMember("width"); | |
| auto height_it = root["args"].FindMember("height"); | |
| if (width_it == root["args"].MemberEnd() || !width_it->value.IsNumber()) { | |
| FML_LOG(ERROR) << "Invalid width"; | |
| return false; | |
| } | |
| if (height_it == root["args"].MemberEnd() || !height_it->value.IsNumber()) { | |
| FML_LOG(ERROR) << "Invalid height"; | |
| return false; | |
| } | |
| auto width = width_it->value.GetUint64(); | |
| auto height = height_it->value.GetUint64(); | |
| std::vector<std::string> skp_paths; | |
| const auto& shaders = shaders_it->value; | |
| for (rapidjson::Value::ConstValueIterator itr = shaders.Begin(); | |
| itr != shaders.End(); ++itr) { | |
| skp_paths.push_back((*itr).GetString()); | |
| } | |
| auto completion_callback = [response = | |
| message->response()](uint num_successes) { | |
| std::ostringstream result_stream; | |
| result_stream << "[" << num_successes << "]"; | |
| std::string result(result_stream.str()); | |
| response->Complete(std::make_unique<fml::DataMapping>(std::vector<uint8_t>( | |
| (const uint8_t*)result.c_str(), | |
| (const uint8_t*)result.c_str() + result.length()))); | |
| }; | |
| on_shader_warmup(skp_paths, completion_callback, width, height); | |
| // The response has already been completed by us. | |
| return true; | |
| } | |
| } // namespace flutter_runner |