Flutter Engine
platform_view.cc
Go to the documentation of this file.
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 "flow/embedded_views.h"
6 #define RAPIDJSON_HAS_STDSTRING 1
7 
8 #include "platform_view.h"
9 
10 #include <algorithm>
11 #include <cstring>
12 #include <limits>
13 #include <sstream>
14 
15 #include "flutter/fml/logging.h"
16 #include "flutter/fml/make_copyable.h"
17 #include "flutter/lib/ui/window/pointer_data.h"
18 #include "flutter/lib/ui/window/window.h"
19 #include "flutter/shell/platform/common/client_wrapper/include/flutter/encodable_value.h"
20 #include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_message_codec.h"
21 #include "third_party/rapidjson/include/rapidjson/document.h"
22 #include "third_party/rapidjson/include/rapidjson/stringbuffer.h"
23 #include "third_party/rapidjson/include/rapidjson/writer.h"
24 
25 #include "logging.h"
27 #include "vsync_waiter.h"
28 
29 namespace flutter_runner {
30 
31 static constexpr char kFlutterPlatformChannel[] = "flutter/platform";
32 static constexpr char kTextInputChannel[] = "flutter/textinput";
33 static constexpr char kKeyEventChannel[] = "flutter/keyevent";
34 static constexpr char kAccessibilityChannel[] = "flutter/accessibility";
35 static constexpr char kFlutterPlatformViewsChannel[] = "flutter/platform_views";
36 static constexpr char kFuchsiaShaderWarmupChannel[] = "fuchsia/shader_warmup";
37 
38 // FL(77): Terminate engine if Fuchsia system FIDL connections have error.
39 template <class T>
40 void SetInterfaceErrorHandler(fidl::InterfacePtr<T>& interface,
41  std::string name) {
42  interface.set_error_handler([name](zx_status_t status) {
43  FML_LOG(ERROR) << "Interface error on: " << name << ", status: " << status;
44  });
45 }
46 template <class T>
47 void SetInterfaceErrorHandler(fidl::Binding<T>& binding, std::string name) {
48  binding.set_error_handler([name](zx_status_t status) {
49  FML_LOG(ERROR) << "Binding error on: " << name << ", status: " << status;
50  });
51 }
52 
55  flutter::TaskRunners task_runners,
56  fuchsia::ui::views::ViewRef view_ref,
57  std::shared_ptr<flutter::ExternalViewEmbedder> external_view_embedder,
58  fidl::InterfaceHandle<fuchsia::ui::input::ImeService> ime_service,
59  fidl::InterfaceHandle<fuchsia::ui::input3::Keyboard> keyboard,
60  fidl::InterfaceHandle<fuchsia::ui::pointer::TouchSource> touch_source,
61  fidl::InterfaceHandle<fuchsia::ui::views::Focuser> focuser,
62  fidl::InterfaceHandle<fuchsia::ui::views::ViewRefFocused> view_ref_focused,
63  OnEnableWireframe wireframe_enabled_callback,
64  OnUpdateView on_update_view_callback,
65  OnCreateSurface on_create_surface_callback,
66  OnSemanticsNodeUpdate on_semantics_node_update_callback,
67  OnRequestAnnounce on_request_announce_callback,
68  OnShaderWarmup on_shader_warmup,
69  AwaitVsyncCallback await_vsync_callback,
71  await_vsync_for_secondary_callback_callback)
72  : flutter::PlatformView(delegate, std::move(task_runners)),
73  external_view_embedder_(external_view_embedder),
74  focus_delegate_(
75  std::make_shared<FocusDelegate>(std::move(view_ref_focused),
76  std::move(focuser))),
77  pointer_delegate_(
78  std::make_shared<PointerDelegate>(std::move(touch_source))),
79  ime_client_(this),
80  text_sync_service_(ime_service.Bind()),
81  keyboard_listener_binding_(this),
82  keyboard_(keyboard.Bind()),
83  wireframe_enabled_callback_(std::move(wireframe_enabled_callback)),
84  on_update_view_callback_(std::move(on_update_view_callback)),
85  on_create_surface_callback_(std::move(on_create_surface_callback)),
86  on_semantics_node_update_callback_(
87  std::move(on_semantics_node_update_callback)),
88  on_request_announce_callback_(std::move(on_request_announce_callback)),
89  on_shader_warmup_(std::move(on_shader_warmup)),
90  await_vsync_callback_(await_vsync_callback),
91  await_vsync_for_secondary_callback_callback_(
92  await_vsync_for_secondary_callback_callback),
93  weak_factory_(this) {
94  // Register all error handlers.
95  SetInterfaceErrorHandler(ime_, "Input Method Editor");
97  SetInterfaceErrorHandler(text_sync_service_, "Text Sync Service");
100 
101  // Configure keyboard listener.
102  keyboard_->AddListener(std::move(view_ref),
103  keyboard_listener_binding_.NewBinding(), [] {});
104 
105  // Begin watching for focus changes.
106  focus_delegate_->WatchLoop([weak = weak_factory_.GetWeakPtr()](bool focused) {
107  if (!weak) {
108  FML_LOG(WARNING) << "PlatformView use-after-free attempted. Ignoring.";
109  return;
110  }
111 
112  // Ensure last_text_state_ is set to make sure Flutter actually wants
113  // an IME.
114  if (focused && weak->last_text_state_) {
115  weak->ActivateIme();
116  } else if (!focused) {
117  weak->DeactivateIme();
118  }
119  });
120 
121  // Begin watching for pointer events.
122  pointer_delegate_->WatchLoop([weak = weak_factory_.GetWeakPtr()](
123  std::vector<flutter::PointerData> events) {
124  if (!weak) {
125  FML_LOG(WARNING) << "PlatformView use-after-free attempted. Ignoring.";
126  return;
127  }
128 
129  if (events.size() == 0) {
130  return; // No work, bounce out.
131  }
132 
133  // If pixel ratio hasn't been set, use a default value of 1.
134  const float pixel_ratio = weak->view_pixel_ratio_.value_or(1.f);
135  auto packet = std::make_unique<flutter::PointerDataPacket>(events.size());
136  for (size_t i = 0; i < events.size(); ++i) {
137  auto& event = events[i];
138  // Translate logical to physical coordinates, as per flutter::PointerData
139  // contract. Done here because pixel ratio comes from the graphics API.
140  event.physical_x = event.physical_x * pixel_ratio;
141  event.physical_y = event.physical_y * pixel_ratio;
142  packet->SetPointerData(i, event);
143  }
144  weak->DispatchPointerDataPacket(std::move(packet));
145  });
146 
147  // Finally! Register the native platform message handlers.
149 }
150 
151 PlatformView::~PlatformView() = default;
152 
156  this, std::placeholders::_1);
159  this, std::placeholders::_1);
162  std::placeholders::_1);
165  this, std::placeholders::_1);
168  on_shader_warmup_, std::placeholders::_1);
169 }
170 
171 // |fuchsia::ui::input::InputMethodEditorClient|
173  fuchsia::ui::input::TextInputState state,
174  std::unique_ptr<fuchsia::ui::input::InputEvent> input_event) {
175  rapidjson::Document document;
176  auto& allocator = document.GetAllocator();
177  rapidjson::Value encoded_state(rapidjson::kObjectType);
178  encoded_state.AddMember("text", state.text, allocator);
179  encoded_state.AddMember("selectionBase", state.selection.base, allocator);
180  encoded_state.AddMember("selectionExtent", state.selection.extent, allocator);
181  switch (state.selection.affinity) {
182  case fuchsia::ui::input::TextAffinity::UPSTREAM:
183  encoded_state.AddMember("selectionAffinity",
184  rapidjson::Value("TextAffinity.upstream"),
185  allocator);
186  break;
187  case fuchsia::ui::input::TextAffinity::DOWNSTREAM:
188  encoded_state.AddMember("selectionAffinity",
189  rapidjson::Value("TextAffinity.downstream"),
190  allocator);
191  break;
192  }
193  encoded_state.AddMember("selectionIsDirectional", true, allocator);
194  encoded_state.AddMember("composingBase", state.composing.start, allocator);
195  encoded_state.AddMember("composingExtent", state.composing.end, allocator);
196 
197  rapidjson::Value args(rapidjson::kArrayType);
198  args.PushBack(current_text_input_client_, allocator);
199  args.PushBack(encoded_state, allocator);
200 
201  document.SetObject();
202  document.AddMember("method",
203  rapidjson::Value("TextInputClient.updateEditingState"),
204  allocator);
205  document.AddMember("args", args, allocator);
206 
207  rapidjson::StringBuffer buffer;
208  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
209  document.Accept(writer);
210 
211  const uint8_t* data = reinterpret_cast<const uint8_t*>(buffer.GetString());
212  DispatchPlatformMessage(std::make_unique<flutter::PlatformMessage>(
213  kTextInputChannel, // channel
214  fml::MallocMapping::Copy(data, buffer.GetSize()), // message
215  nullptr) // response
216  );
218  std::make_unique<fuchsia::ui::input::TextInputState>(state);
219 }
220 
221 // |fuchsia::ui::input::InputMethodEditorClient|
222 void PlatformView::OnAction(fuchsia::ui::input::InputMethodAction action) {
223  rapidjson::Document document;
224  auto& allocator = document.GetAllocator();
225 
226  rapidjson::Value args(rapidjson::kArrayType);
227  args.PushBack(current_text_input_client_, allocator);
228 
229  // Done is currently the only text input action defined by Flutter.
230  args.PushBack("TextInputAction.done", allocator);
231 
232  document.SetObject();
233  document.AddMember(
234  "method", rapidjson::Value("TextInputClient.performAction"), allocator);
235  document.AddMember("args", args, allocator);
236 
237  rapidjson::StringBuffer buffer;
238  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
239  document.Accept(writer);
240 
241  const uint8_t* data = reinterpret_cast<const uint8_t*>(buffer.GetString());
242  DispatchPlatformMessage(std::make_unique<flutter::PlatformMessage>(
243  kTextInputChannel, // channel
244  fml::MallocMapping::Copy(data, buffer.GetSize()), // message
245  nullptr) // response
246  );
247 }
248 
250  fuchsia::ui::input::PointerEventPhase phase) {
251  switch (phase) {
252  case fuchsia::ui::input::PointerEventPhase::ADD:
254  case fuchsia::ui::input::PointerEventPhase::HOVER:
256  case fuchsia::ui::input::PointerEventPhase::DOWN:
258  case fuchsia::ui::input::PointerEventPhase::MOVE:
260  case fuchsia::ui::input::PointerEventPhase::UP:
262  case fuchsia::ui::input::PointerEventPhase::REMOVE:
264  case fuchsia::ui::input::PointerEventPhase::CANCEL:
266  default:
268  }
269 }
270 
272  fuchsia::ui::input::PointerEventType type) {
273  switch (type) {
274  case fuchsia::ui::input::PointerEventType::TOUCH:
276  case fuchsia::ui::input::PointerEventType::MOUSE:
278  default:
280  }
281 }
282 
283 // TODO(SCN-1278): Remove this.
284 // Turns two floats (high bits, low bits) into a 64-bit uint.
285 static trace_flow_id_t PointerTraceHACK(float fa, float fb) {
286  uint32_t ia, ib;
287  memcpy(&ia, &fa, sizeof(uint32_t));
288  memcpy(&ib, &fb, sizeof(uint32_t));
289  return (((uint64_t)ia) << 32) | ib;
290 }
291 
292 // For certain scenarios that must avoid floating-point drift, compute a
293 // coordinate that falls within the logical view bounding box.
294 std::array<float, 2> PlatformView::ClampToViewSpace(const float x,
295  const float y) const {
296  if (!view_logical_size_.has_value() || !view_logical_origin_.has_value()) {
297  return {x, y}; // If we can't do anything, return the original values.
298  }
299 
300  const auto origin = view_logical_origin_.value();
301  const auto size = view_logical_size_.value();
302  const float min_x = origin[0];
303  const float max_x = origin[0] + size[0];
304  const float min_y = origin[1];
305  const float max_y = origin[1] + size[1];
306  if (min_x <= x && x < max_x && min_y <= y && y < max_y) {
307  return {x, y}; // No clamping to perform.
308  }
309 
310  // View boundary is [min_x, max_x) x [min_y, max_y). Note that min is
311  // inclusive, but max is exclusive - so we subtract epsilon.
312  const float max_x_inclusive = max_x - std::numeric_limits<float>::epsilon();
313  const float max_y_inclusive = max_y - std::numeric_limits<float>::epsilon();
314  const float& clamped_x = std::clamp(x, min_x, max_x_inclusive);
315  const float& clamped_y = std::clamp(y, min_y, max_y_inclusive);
316  FML_LOG(INFO) << "Clamped (" << x << ", " << y << ") to (" << clamped_x
317  << ", " << clamped_y << ").";
318  return {clamped_x, clamped_y};
319 }
320 
322  const fuchsia::ui::input::PointerEvent& pointer) {
323  TRACE_EVENT0("flutter", "PlatformView::OnHandlePointerEvent");
324 
325  // TODO(SCN-1278): Use proper trace_id for tracing flow.
326  trace_flow_id_t trace_id =
327  PointerTraceHACK(pointer.radius_major, pointer.radius_minor);
328  TRACE_FLOW_END("input", "dispatch_event_to_client", trace_id);
329 
330  const float pixel_ratio =
331  view_pixel_ratio_.has_value() ? *view_pixel_ratio_ : 0.f;
332 
333  flutter::PointerData pointer_data;
334  pointer_data.Clear();
335  pointer_data.time_stamp = pointer.event_time / 1000;
336  pointer_data.change = GetChangeFromPointerEventPhase(pointer.phase);
337  pointer_data.kind = GetKindFromPointerType(pointer.type);
338  pointer_data.device = pointer.pointer_id;
339  // Pointer events are in logical pixels, so scale to physical.
340  pointer_data.physical_x = pointer.x * pixel_ratio;
341  pointer_data.physical_y = pointer.y * pixel_ratio;
342  // Buttons are single bit values starting with kMousePrimaryButton = 1.
343  pointer_data.buttons = static_cast<uint64_t>(pointer.buttons);
344 
345  switch (pointer_data.change) {
347  // Make the pointer start in the view space, despite numerical drift.
348  auto clamped_pointer = ClampToViewSpace(pointer.x, pointer.y);
349  pointer_data.physical_x = clamped_pointer[0] * pixel_ratio;
350  pointer_data.physical_y = clamped_pointer[1] * pixel_ratio;
351 
352  down_pointers_.insert(pointer_data.device);
353  break;
354  }
357  down_pointers_.erase(pointer_data.device);
358  break;
360  if (down_pointers_.count(pointer_data.device) == 0) {
362  }
363  break;
365  if (down_pointers_.count(pointer_data.device) != 0) {
366  FML_DLOG(ERROR) << "Received add event for down pointer.";
367  }
368  break;
370  if (down_pointers_.count(pointer_data.device) != 0) {
371  FML_DLOG(ERROR) << "Received remove event for down pointer.";
372  }
373  break;
375  if (down_pointers_.count(pointer_data.device) != 0) {
376  FML_DLOG(ERROR) << "Received hover event for down pointer.";
377  }
378  break;
379  }
380 
381  auto packet = std::make_unique<flutter::PointerDataPacket>(1);
382  packet->SetPointerData(0, pointer_data);
383  DispatchPointerDataPacket(std::move(packet));
384  return true;
385 }
386 
387 // |fuchsia::ui:input3::KeyboardListener|
389  fuchsia::ui::input3::KeyEvent key_event,
390  fuchsia::ui::input3::KeyboardListener::OnKeyEventCallback callback) {
391  const char* type = nullptr;
392  switch (key_event.type()) {
393  case fuchsia::ui::input3::KeyEventType::PRESSED:
394  type = "keydown";
395  break;
396  case fuchsia::ui::input3::KeyEventType::RELEASED:
397  type = "keyup";
398  break;
399  case fuchsia::ui::input3::KeyEventType::SYNC:
400  // What, if anything, should happen here?
401  case fuchsia::ui::input3::KeyEventType::CANCEL:
402  // What, if anything, should happen here?
403  default:
404  break;
405  }
406  if (type == nullptr) {
407  FML_DLOG(ERROR) << "Unknown key event phase.";
408  callback(fuchsia::ui::input3::KeyEventStatus::NOT_HANDLED);
409  return;
410  }
411  keyboard_translator_.ConsumeEvent(std::move(key_event));
412 
413  rapidjson::Document document;
414  auto& allocator = document.GetAllocator();
415  document.SetObject();
416  document.AddMember("type", rapidjson::Value(type, strlen(type)), allocator);
417  document.AddMember("keymap", rapidjson::Value("fuchsia"), allocator);
418  document.AddMember("hidUsage", keyboard_translator_.LastHIDUsage(),
419  allocator);
420  document.AddMember("codePoint", keyboard_translator_.LastCodePoint(),
421  allocator);
422  document.AddMember("modifiers", keyboard_translator_.Modifiers(), allocator);
423  rapidjson::StringBuffer buffer;
424  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
425  document.Accept(writer);
426 
427  const uint8_t* data = reinterpret_cast<const uint8_t*>(buffer.GetString());
428  DispatchPlatformMessage(std::make_unique<flutter::PlatformMessage>(
429  kKeyEventChannel, // channel
430  fml::MallocMapping::Copy(data, buffer.GetSize()), // data
431  nullptr) // response
432  );
433  callback(fuchsia::ui::input3::KeyEventStatus::HANDLED);
434 }
435 
438 
439  text_sync_service_->GetInputMethodEditor(
440  fuchsia::ui::input::KeyboardType::TEXT, // keyboard type
441  fuchsia::ui::input::InputMethodAction::DONE, // input method action
442  *last_text_state_, // initial state
443  ime_client_.NewBinding(), // client
444  ime_.NewRequest() // editor
445  );
446 }
447 
449  if (ime_) {
450  text_sync_service_->HideKeyboard();
451  ime_ = nullptr;
452  }
453  if (ime_client_.is_bound()) {
454  ime_client_.Unbind();
455  }
456 }
457 
458 // |flutter::PlatformView|
459 std::unique_ptr<flutter::VsyncWaiter> PlatformView::CreateVSyncWaiter() {
460  return std::make_unique<flutter_runner::VsyncWaiter>(
462  task_runners_);
463 }
464 
465 // |flutter::PlatformView|
466 std::unique_ptr<flutter::Surface> PlatformView::CreateRenderingSurface() {
468 }
469 
470 // |flutter::PlatformView|
471 std::shared_ptr<flutter::ExternalViewEmbedder>
474 }
475 
476 // |flutter::PlatformView|
478  std::unique_ptr<flutter::PlatformMessage> message) {
479  if (!message) {
480  return;
481  }
482  const std::string channel = message->channel();
483  auto found = platform_message_handlers_.find(channel);
484  if (found == platform_message_handlers_.end()) {
485  const bool already_errored = unregistered_channels_.count(channel);
486  if (!already_errored) {
487  FML_LOG(INFO)
488  << "Platform view received message on channel '" << message->channel()
489  << "' with no registered handler. And empty response will be "
490  "generated. Please implement the native message handler. This "
491  "message will appear only once per channel.";
492  unregistered_channels_.insert(channel);
493  }
495  return;
496  }
497  auto response = message->response();
498  bool response_handled = found->second(std::move(message));
499 
500  // Ensure all responses are completed.
501  if (response && !response_handled) {
502  // response_handled should be true if the response was completed.
503  FML_DCHECK(!response->is_complete());
504  response->CompleteEmpty();
505  }
506 }
507 
508 // |flutter::PlatformView|
511  if (enabled) {
512  SetAccessibilityFeatures(static_cast<int32_t>(
514  } else {
516  }
517 }
518 
519 // |flutter::PlatformView|
523  const float pixel_ratio =
524  view_pixel_ratio_.has_value() ? *view_pixel_ratio_ : 0.f;
525 
526  on_semantics_node_update_callback_(update, pixel_ratio);
527 }
528 
529 // Channel handler for kAccessibilityChannel
531  std::unique_ptr<flutter::PlatformMessage> message) {
532  FML_DCHECK(message->channel() == kAccessibilityChannel);
533 
534  const flutter::StandardMessageCodec& standard_message_codec =
536  std::unique_ptr<flutter::EncodableValue> decoded =
537  standard_message_codec.DecodeMessage(message->data().GetMapping(),
538  message->data().GetSize());
539 
540  flutter::EncodableMap map = std::get<flutter::EncodableMap>(*decoded);
541  std::string type =
542  std::get<std::string>(map.at(flutter::EncodableValue("type")));
543  if (type == "announce") {
544  flutter::EncodableMap data_map = std::get<flutter::EncodableMap>(
545  map.at(flutter::EncodableValue("data")));
546  std::string text =
547  std::get<std::string>(data_map.at(flutter::EncodableValue("message")));
548 
550  }
551 
552  // Complete with an empty response.
553  return false;
554 }
555 
556 // Channel handler for kFlutterPlatformChannel
558  std::unique_ptr<flutter::PlatformMessage> message) {
559  FML_DCHECK(message->channel() == kFlutterPlatformChannel);
560 
561  // Fuchsia does not handle any platform messages at this time.
562 
563  // Complete with an empty response.
564  return false;
565 }
566 
567 // Channel handler for kTextInputChannel
569  std::unique_ptr<flutter::PlatformMessage> message) {
570  FML_DCHECK(message->channel() == kTextInputChannel);
571  const auto& data = message->data();
572  rapidjson::Document document;
573  document.Parse(reinterpret_cast<const char*>(data.GetMapping()),
574  data.GetSize());
575  if (document.HasParseError() || !document.IsObject()) {
576  return false;
577  }
578  auto root = document.GetObject();
579  auto method = root.FindMember("method");
580  if (method == root.MemberEnd() || !method->value.IsString()) {
581  return false;
582  }
583 
584  if (method->value == "TextInput.show") {
585  if (ime_) {
586  text_sync_service_->ShowKeyboard();
587  }
588  } else if (method->value == "TextInput.hide") {
589  if (ime_) {
590  text_sync_service_->HideKeyboard();
591  }
592  } else if (method->value == "TextInput.setClient") {
594  DeactivateIme();
595  auto args = root.FindMember("args");
596  if (args == root.MemberEnd() || !args->value.IsArray() ||
597  args->value.Size() != 2)
598  return false;
599  const auto& configuration = args->value[1];
600  if (!configuration.IsObject()) {
601  return false;
602  }
603  // TODO(abarth): Read the keyboard type from the configuration.
604  current_text_input_client_ = args->value[0].GetInt();
605 
606  auto initial_text_input_state = fuchsia::ui::input::TextInputState{};
607  initial_text_input_state.text = "";
608  last_text_state_ = std::make_unique<fuchsia::ui::input::TextInputState>(
609  initial_text_input_state);
610  ActivateIme();
611  } else if (method->value == "TextInput.setEditingState") {
612  if (ime_) {
613  auto args_it = root.FindMember("args");
614  if (args_it == root.MemberEnd() || !args_it->value.IsObject()) {
615  return false;
616  }
617  const auto& args = args_it->value;
618  fuchsia::ui::input::TextInputState state;
619  state.text = "";
620  // TODO(abarth): Deserialize state.
621  auto text = args.FindMember("text");
622  if (text != args.MemberEnd() && text->value.IsString())
623  state.text = text->value.GetString();
624  auto selection_base = args.FindMember("selectionBase");
625  if (selection_base != args.MemberEnd() && selection_base->value.IsInt())
626  state.selection.base = selection_base->value.GetInt();
627  auto selection_extent = args.FindMember("selectionExtent");
628  if (selection_extent != args.MemberEnd() &&
629  selection_extent->value.IsInt())
630  state.selection.extent = selection_extent->value.GetInt();
631  auto selection_affinity = args.FindMember("selectionAffinity");
632  if (selection_affinity != args.MemberEnd() &&
633  selection_affinity->value.IsString() &&
634  selection_affinity->value == "TextAffinity.upstream")
635  state.selection.affinity = fuchsia::ui::input::TextAffinity::UPSTREAM;
636  else
637  state.selection.affinity = fuchsia::ui::input::TextAffinity::DOWNSTREAM;
638  // We ignore selectionIsDirectional because that concept doesn't exist on
639  // Fuchsia.
640  auto composing_base = args.FindMember("composingBase");
641  if (composing_base != args.MemberEnd() && composing_base->value.IsInt())
642  state.composing.start = composing_base->value.GetInt();
643  auto composing_extent = args.FindMember("composingExtent");
644  if (composing_extent != args.MemberEnd() &&
645  composing_extent->value.IsInt())
646  state.composing.end = composing_extent->value.GetInt();
647  ime_->SetState(std::move(state));
648  }
649  } else if (method->value == "TextInput.clearClient") {
651  last_text_state_ = nullptr;
652  DeactivateIme();
653  } else {
654  FML_DLOG(ERROR) << "Unknown " << message->channel() << " method "
655  << method->value.GetString();
656  }
657  // Complete with an empty response.
658  return false;
659 }
660 
662  std::unique_ptr<flutter::PlatformMessage> message) {
663  FML_DCHECK(message->channel() == kFlutterPlatformViewsChannel);
664  const auto& data = message->data();
665  rapidjson::Document document;
666  document.Parse(reinterpret_cast<const char*>(data.GetMapping()),
667  data.GetSize());
668  if (document.HasParseError() || !document.IsObject()) {
669  FML_LOG(ERROR) << "Could not parse document";
670  return false;
671  }
672  auto root = document.GetObject();
673  auto method_member = root.FindMember("method");
674  if (method_member == root.MemberEnd() || !method_member->value.IsString()) {
675  return false;
676  }
677  std::string method(method_member->value.GetString());
678 
679  if (method == "View.enableWireframe") {
680  auto args_it = root.FindMember("args");
681  if (args_it == root.MemberEnd() || !args_it->value.IsObject()) {
682  FML_LOG(ERROR) << "No arguments found.";
683  return false;
684  }
685  const auto& args = args_it->value;
686 
687  auto enable = args.FindMember("enable");
688  if (!enable->value.IsBool()) {
689  FML_LOG(ERROR) << "Argument 'enable' is not a bool";
690  return false;
691  }
692 
693  wireframe_enabled_callback_(enable->value.GetBool());
694  } else if (method == "View.create") {
695  auto args_it = root.FindMember("args");
696  if (args_it == root.MemberEnd() || !args_it->value.IsObject()) {
697  FML_LOG(ERROR) << "No arguments found.";
698  return false;
699  }
700  const auto& args = args_it->value;
701 
702  auto view_id = args.FindMember("viewId");
703  if (!view_id->value.IsUint64()) {
704  FML_LOG(ERROR) << "Argument 'viewId' is not a int64";
705  return false;
706  }
707 
708  auto hit_testable = args.FindMember("hitTestable");
709  if (!hit_testable->value.IsBool()) {
710  FML_LOG(ERROR) << "Argument 'hitTestable' is not a bool";
711  return false;
712  }
713 
714  auto focusable = args.FindMember("focusable");
715  if (!focusable->value.IsBool()) {
716  FML_LOG(ERROR) << "Argument 'focusable' is not a bool";
717  return false;
718  }
719 
720  auto on_view_created = fml::MakeCopyable(
721  [platform_task_runner = task_runners_.GetPlatformTaskRunner(),
722  message = std::move(message)]() {
723  // The client is waiting for view creation. Send an empty response
724  // back to signal the view was created.
725  if (message->response()) {
726  message->response()->Complete(std::make_unique<fml::DataMapping>(
727  std::vector<uint8_t>({'[', '0', ']'})));
728  }
729  });
730  OnCreateView(std::move(on_view_created), view_id->value.GetUint64(),
731  hit_testable->value.GetBool(), focusable->value.GetBool());
732  return true;
733  } else if (method == "View.update") {
734  auto args_it = root.FindMember("args");
735  if (args_it == root.MemberEnd() || !args_it->value.IsObject()) {
736  FML_LOG(ERROR) << "No arguments found.";
737  return false;
738  }
739  const auto& args = args_it->value;
740 
741  auto view_id = args.FindMember("viewId");
742  if (!view_id->value.IsUint64()) {
743  FML_LOG(ERROR) << "Argument 'viewId' is not a int64";
744  return false;
745  }
746 
747  auto hit_testable = args.FindMember("hitTestable");
748  if (!hit_testable->value.IsBool()) {
749  FML_LOG(ERROR) << "Argument 'hitTestable' is not a bool";
750  return false;
751  }
752 
753  auto focusable = args.FindMember("focusable");
754  if (!focusable->value.IsBool()) {
755  FML_LOG(ERROR) << "Argument 'focusable' is not a bool";
756  return false;
757  }
758 
759  SkRect view_occlusion_hint_raw = SkRect::MakeEmpty();
760  auto view_occlusion_hint = args.FindMember("viewOcclusionHintLTRB");
761  if (view_occlusion_hint != args.MemberEnd()) {
762  if (view_occlusion_hint->value.IsArray()) {
763  const auto& view_occlusion_hint_array =
764  view_occlusion_hint->value.GetArray();
765  if (view_occlusion_hint_array.Size() == 4) {
766  bool parse_error = false;
767  for (int i = 0; i < 4; i++) {
768  auto& array_val = view_occlusion_hint_array[i];
769  if (!array_val.IsDouble()) {
770  FML_LOG(ERROR) << "Argument 'viewOcclusionHintLTRB' element " << i
771  << " is not a double";
772  parse_error = true;
773  break;
774  }
775  }
776 
777  if (!parse_error) {
778  view_occlusion_hint_raw =
779  SkRect::MakeLTRB(view_occlusion_hint_array[0].GetDouble(),
780  view_occlusion_hint_array[1].GetDouble(),
781  view_occlusion_hint_array[2].GetDouble(),
782  view_occlusion_hint_array[3].GetDouble());
783  }
784  } else {
785  FML_LOG(ERROR)
786  << "Argument 'viewOcclusionHintLTRB' expected size 4; got "
787  << view_occlusion_hint_array.Size();
788  }
789  } else {
790  FML_LOG(ERROR)
791  << "Argument 'viewOcclusionHintLTRB' is not a double array";
792  }
793  } else {
794  FML_LOG(WARNING) << "Argument 'viewOcclusionHintLTRB' is missing";
795  }
796 
798  view_id->value.GetUint64(), view_occlusion_hint_raw,
799  hit_testable->value.GetBool(), focusable->value.GetBool());
800  if (message->response()) {
801  message->response()->Complete(std::make_unique<fml::DataMapping>(
802  std::vector<uint8_t>({'[', '0', ']'})));
803  return true;
804  }
805  } else if (method == "View.dispose") {
806  auto args_it = root.FindMember("args");
807  if (args_it == root.MemberEnd() || !args_it->value.IsObject()) {
808  FML_LOG(ERROR) << "No arguments found.";
809  return false;
810  }
811  const auto& args = args_it->value;
812 
813  auto view_id = args.FindMember("viewId");
814  if (!view_id->value.IsUint64()) {
815  FML_LOG(ERROR) << "Argument 'viewId' is not a int64";
816  return false;
817  }
818 
819  OnDisposeView(view_id->value.GetUint64());
820  if (message->response()) {
821  message->response()->Complete(std::make_unique<fml::DataMapping>(
822  std::vector<uint8_t>({'[', '0', ']'})));
823  return true;
824  }
825  } else if (method.rfind("View.focus", 0) == 0) {
826  return focus_delegate_->HandlePlatformMessage(root, message->response());
827  } else {
828  FML_DLOG(ERROR) << "Unknown " << message->channel() << " method " << method;
829  }
830  // Complete with an empty response by default.
831  return false;
832 }
833 
835  OnShaderWarmup on_shader_warmup,
836  std::unique_ptr<flutter::PlatformMessage> message) {
837  FML_DCHECK(message->channel() == kFuchsiaShaderWarmupChannel);
838 
839  if (!on_shader_warmup) {
840  FML_LOG(ERROR) << "No shader warmup callback set!";
841  std::string result = "[0]";
842  message->response()->Complete(
843  std::make_unique<fml::DataMapping>(std::vector<uint8_t>(
844  (const uint8_t*)result.c_str(),
845  (const uint8_t*)result.c_str() + result.length())));
846  return true;
847  }
848 
849  const auto& data = message->data();
850  rapidjson::Document document;
851  document.Parse(reinterpret_cast<const char*>(data.GetMapping()),
852  data.GetSize());
853  if (document.HasParseError() || !document.IsObject()) {
854  FML_LOG(ERROR) << "Could not parse document";
855  return false;
856  }
857  auto root = document.GetObject();
858  auto method = root.FindMember("method");
859  if (method == root.MemberEnd() || !method->value.IsString() ||
860  method->value != "WarmupSkps") {
861  FML_LOG(ERROR) << "Invalid method name";
862  return false;
863  }
864 
865  auto args_it = root.FindMember("args");
866  if (args_it == root.MemberEnd() || !args_it->value.IsObject()) {
867  FML_LOG(ERROR) << "No arguments found.";
868  return false;
869  }
870 
871  auto shaders_it = root["args"].FindMember("shaders");
872  if (shaders_it == root["args"].MemberEnd() || !shaders_it->value.IsArray()) {
873  FML_LOG(ERROR) << "No shaders found.";
874  return false;
875  }
876 
877  auto width_it = root["args"].FindMember("width");
878  auto height_it = root["args"].FindMember("height");
879  if (width_it == root["args"].MemberEnd() || !width_it->value.IsNumber()) {
880  FML_LOG(ERROR) << "Invalid width";
881  return false;
882  }
883  if (height_it == root["args"].MemberEnd() || !height_it->value.IsNumber()) {
884  FML_LOG(ERROR) << "Invalid height";
885  return false;
886  }
887  auto width = width_it->value.GetUint64();
888  auto height = height_it->value.GetUint64();
889 
890  std::vector<std::string> skp_paths;
891  const auto& shaders = shaders_it->value;
892  for (rapidjson::Value::ConstValueIterator itr = shaders.Begin();
893  itr != shaders.End(); ++itr) {
894  skp_paths.push_back((*itr).GetString());
895  }
896 
897  auto completion_callback = [response =
898  message->response()](uint num_successes) {
899  std::ostringstream result_stream;
900  result_stream << "[" << num_successes << "]";
901 
902  std::string result(result_stream.str());
903 
904  response->Complete(std::make_unique<fml::DataMapping>(std::vector<uint8_t>(
905  (const uint8_t*)result.c_str(),
906  (const uint8_t*)result.c_str() + result.length())));
907  };
908 
909  on_shader_warmup(skp_paths, completion_callback, width, height);
910  // The response has already been completed by us.
911  return true;
912 }
913 
914 } // namespace flutter_runner
static constexpr char kKeyEventChannel[]
std::optional< float > view_pixel_ratio_
void SetInterfaceErrorHandler(fidl::InterfacePtr< T > &interface, std::string name)
bool HandleFlutterTextInputChannelPlatformMessage(std::unique_ptr< flutter::PlatformMessage > message)
std::function< void(const std::vector< std::string > &, std::function< void(uint32_t)>, uint64_t, uint64_t)> OnShaderWarmup
Definition: platform_view.h:52
fit::function< void(std::string)> OnRequestAnnounce
Definition: platform_view.h:45
G_BEGIN_DECLS FlValue * args
std::shared_ptr< FocusDelegate > focus_delegate_
fuchsia::ui::input::InputMethodEditorPtr ime_
KeyCallType type
static constexpr char kFlutterPlatformChannel[]
bool HandleAccessibilityChannelPlatformMessage(std::unique_ptr< flutter::PlatformMessage > message)
~PlatformView() override
Destroys the platform view. The platform view is owned by the shell and will be destroyed by the same...
void HandlePlatformMessage(std::unique_ptr< flutter::PlatformMessage > message) override
#define DEBUG_CHECK(condition, tag, message)
Definition: inlines.h:19
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:90
#define FML_DCHECK(condition)
Definition: logging.h:86
static flutter::PointerData::Change GetChangeFromPointerEventPhase(fuchsia::ui::input::PointerEventPhase phase)
uint32_t LastCodePoint()
Definition: keyboard.cc:300
std::shared_ptr< flutter::ExternalViewEmbedder > external_view_embedder_
fit::function< std::unique_ptr< flutter::Surface >()> OnCreateSurface
Definition: platform_view.h:42
std::unique_ptr< flutter::VsyncWaiter > CreateVSyncWaiter() override
Invoked by the shell to obtain a platform specific vsync waiter. It is optional for platforms to over...
OnCreateSurface on_create_surface_callback_
const TaskRunners task_runners_
#define TRACE_FLOW_END(category, name, id)
Definition: trace_event.h:136
OnSemanticsNodeUpdate on_semantics_node_update_callback_
std::function< void(FireCallbackCallback)> AwaitVsyncForSecondaryCallbackCallback
Definition: vsync_waiter.h:25
void OnKeyEvent(fuchsia::ui::input3::KeyEvent key_event, fuchsia::ui::input3::KeyboardListener::OnKeyEventCallback callback) override
virtual void SetAccessibilityFeatures(int32_t flags)
Used by the embedder to specify the features to enable in the accessibility tree generated by the iso...
Definition: ref_ptr.h:252
std::function< void(FireCallbackCallback)> AwaitVsyncCallback
Definition: vsync_waiter.h:22
fml::RefPtr< fml::TaskRunner > GetPlatformTaskRunner() const
Definition: task_runners.cc:30
std::unordered_map< int32_t, SemanticsNode > SemanticsNodeUpdates
GAsyncResult * result
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
AwaitVsyncCallback await_vsync_callback_
void OnAction(fuchsia::ui::input::InputMethodAction action) override
static flutter::PointerData::DeviceKind GetKindFromPointerType(fuchsia::ui::input::PointerEventType type)
std::optional< std::array< float, 2 > > view_logical_origin_
#define FML_LOG(severity)
Definition: logging.h:65
static constexpr char kAccessibilityChannel[]
virtual void OnCreateView(ViewCallback on_view_created, int64_t view_id_raw, bool hit_testable, bool focusable)=0
bool HandleFlutterPlatformChannelPlatformMessage(std::unique_ptr< flutter::PlatformMessage > message)
FlutterKeyEvent key_event
FlKeyEvent FlKeyResponderAsyncCallback callback
static constexpr char kTextInputChannel[]
void DispatchPointerDataPacket(std::unique_ptr< PointerDataPacket > packet)
Dispatches pointer events from the embedder to the framework. Each pointer data packet may contain mu...
AwaitVsyncForSecondaryCallbackCallback await_vsync_for_secondary_callback_callback_
fit::function< void(int64_t, SkRect, bool, bool)> OnUpdateView
Definition: platform_view.h:41
static const StandardMessageCodec & GetInstance(const StandardCodecSerializer *serializer=nullptr)
FlKeyEvent * event
std::unique_ptr< fuchsia::ui::input::TextInputState > last_text_state_
static constexpr char kFuchsiaShaderWarmupChannel[]
fidl::Binding< fuchsia::ui::input3::KeyboardListener > keyboard_listener_binding_
std::map< std::string, std::function< bool(std::unique_ptr< flutter::PlatformMessage >)> > platform_message_handlers_
fit::function< void(flutter::SemanticsNodeUpdates, float)> OnSemanticsNodeUpdate
Definition: platform_view.h:44
std::set< std::string > unregistered_channels_
internal::CopyableLambda< T > MakeCopyable(T lambda)
Definition: make_copyable.h:57
std::set< int > down_pointers_
virtual void SetSemanticsEnabled(bool enabled)
Used by embedder to notify the running isolate hosted by the engine on the UI thread that the accessi...
void DidUpdateState(fuchsia::ui::input::TextInputState state, std::unique_ptr< fuchsia::ui::input::InputEvent > event) override
void DispatchPlatformMessage(std::unique_ptr< PlatformMessage > message)
Used by embedders to dispatch a platform message to a running root isolate hosted by the engine...
SemanticsAction action
fidl::Binding< fuchsia::ui::input::InputMethodEditorClient > ime_client_
OnEnableWireframe wireframe_enabled_callback_
OnRequestAnnounce on_request_announce_callback_
bool OnHandlePointerEvent(const fuchsia::ui::input::PointerEvent &pointer)
int32_t width
static trace_flow_id_t PointerTraceHACK(float fa, float fb)
int32_t height
static MallocMapping Copy(const T *begin, const T *end)
Definition: mapping.h:147
static bool HandleFuchsiaShaderWarmupChannelPlatformMessage(OnShaderWarmup on_shader_warmup, std::unique_ptr< flutter::PlatformMessage > message)
static constexpr char kFlutterPlatformViewsChannel[]
#define LOG_TAG
Definition: logging.h:11
std::array< float, 2 > ClampToViewSpace(const float x, const float y) const
fuchsia::ui::input::ImeServicePtr text_sync_service_
std::shared_ptr< flutter::ExternalViewEmbedder > CreateExternalViewEmbedder() override
const char * name
Definition: fuchsia.cc:50
void SetSemanticsEnabled(bool enabled) override
Used by embedder to notify the running isolate hosted by the engine on the UI thread that the accessi...
std::u16string text
static const uint8_t buffer[]
OnUpdateView on_update_view_callback_
fidl::Binding< fuchsia::ui::input3::Keyboard > keyboard_
#define FML_DLOG(severity)
Definition: logging.h:85
std::shared_ptr< PointerDelegate > pointer_delegate_
std::optional< std::array< float, 2 > > view_logical_size_
std::map< EncodableValue, EncodableValue > EncodableMap
fuchsia::ui::input3::KeyboardPtr keyboard_
std::unordered_map< int32_t, CustomAccessibilityAction > CustomAccessibilityActionUpdates
PlatformView(flutter::PlatformView::Delegate &delegate, flutter::TaskRunners task_runners, fuchsia::ui::views::ViewRef view_ref, std::shared_ptr< flutter::ExternalViewEmbedder > external_view_embedder, fidl::InterfaceHandle< fuchsia::ui::input::ImeService > ime_service, fidl::InterfaceHandle< fuchsia::ui::input3::Keyboard > keyboard, fidl::InterfaceHandle< fuchsia::ui::pointer::TouchSource > touch_source, fidl::InterfaceHandle< fuchsia::ui::views::Focuser > focuser, fidl::InterfaceHandle< fuchsia::ui::views::ViewRefFocused > view_ref_focused, 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)
Used to forward events from the platform view to interested subsystems. This forwarding is done by th...
Definition: platform_view.h:53
bool HandleFlutterPlatformViewsChannelPlatformMessage(std::unique_ptr< flutter::PlatformMessage > message)
bool ConsumeEvent(fuchsia::ui::input3::KeyEvent event)
Definition: keyboard.cc:174
virtual void HandlePlatformMessage(std::unique_ptr< PlatformMessage > message)
Overridden by embedders to perform actions in response to platform messages sent from the framework t...
fit::function< void(bool)> OnEnableWireframe
Definition: platform_view.h:39
std::unique_ptr< flutter::Surface > CreateRenderingSurface() override
fml::WeakPtrFactory< PlatformView > weak_factory_
AtkStateType state
virtual void OnDisposeView(int64_t view_id_raw)=0
void UpdateSemantics(flutter::SemanticsNodeUpdates update, flutter::CustomAccessibilityActionUpdates actions) override
Used by the framework to tell the embedder to apply the specified semantics node updates. The default implementation of this method does nothing.
OnShaderWarmup on_shader_warmup_