• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #define RAPIDJSON_HAS_STDSTRING 1
6 
7 #include "platform_view.h"
8 
9 #include <sstream>
10 
11 #include "flutter/fml/logging.h"
12 #include "flutter/lib/ui/compositing/scene_host.h"
13 #include "flutter/lib/ui/window/pointer_data.h"
14 #include "logging.h"
15 #include "rapidjson/document.h"
16 #include "rapidjson/stringbuffer.h"
17 #include "rapidjson/writer.h"
18 #include "runtime/dart/utils/inlines.h"
19 #include "vsync_waiter.h"
20 
21 namespace flutter_runner {
22 
23 namespace {
24 
Add(const fuchsia::ui::gfx::vec3 & a,const fuchsia::ui::gfx::vec3 & b)25 inline fuchsia::ui::gfx::vec3 Add(const fuchsia::ui::gfx::vec3& a,
26                                   const fuchsia::ui::gfx::vec3& b) {
27   return {.x = a.x + b.x, .y = a.y + b.y, .z = a.z + b.z};
28 }
29 
Subtract(const fuchsia::ui::gfx::vec3 & a,const fuchsia::ui::gfx::vec3 & b)30 inline fuchsia::ui::gfx::vec3 Subtract(const fuchsia::ui::gfx::vec3& a,
31                                        const fuchsia::ui::gfx::vec3& b) {
32   return {.x = a.x - b.x, .y = a.y - b.y, .z = a.z - b.z};
33 }
34 
InsetBy(const fuchsia::ui::gfx::BoundingBox & box,const fuchsia::ui::gfx::vec3 & inset_from_min,const fuchsia::ui::gfx::vec3 & inset_from_max)35 inline fuchsia::ui::gfx::BoundingBox InsetBy(
36     const fuchsia::ui::gfx::BoundingBox& box,
37     const fuchsia::ui::gfx::vec3& inset_from_min,
38     const fuchsia::ui::gfx::vec3& inset_from_max) {
39   return {.min = Add(box.min, inset_from_min),
40           .max = Subtract(box.max, inset_from_max)};
41 }
42 
ViewPropertiesLayoutBox(const fuchsia::ui::gfx::ViewProperties & view_properties)43 inline fuchsia::ui::gfx::BoundingBox ViewPropertiesLayoutBox(
44     const fuchsia::ui::gfx::ViewProperties& view_properties) {
45   return InsetBy(view_properties.bounding_box, view_properties.inset_from_min,
46                  view_properties.inset_from_max);
47 }
48 
Max(const fuchsia::ui::gfx::vec3 & v,float min_val)49 inline fuchsia::ui::gfx::vec3 Max(const fuchsia::ui::gfx::vec3& v,
50                                   float min_val) {
51   return {.x = std::max(v.x, min_val),
52           .y = std::max(v.y, min_val),
53           .z = std::max(v.z, min_val)};
54 }
55 
56 }  // end namespace
57 
58 static constexpr char kFlutterPlatformChannel[] = "flutter/platform";
59 static constexpr char kTextInputChannel[] = "flutter/textinput";
60 static constexpr char kKeyEventChannel[] = "flutter/keyevent";
61 static constexpr char kAccessibilityChannel[] = "flutter/accessibility";
62 
63 // FL(77): Terminate engine if Fuchsia system FIDL connections have error.
64 template <class T>
SetInterfaceErrorHandler(fidl::InterfacePtr<T> & interface,std::string name)65 void SetInterfaceErrorHandler(fidl::InterfacePtr<T>& interface,
66                               std::string name) {
67   interface.set_error_handler([name](zx_status_t status) {
68     FML_LOG(ERROR) << "Interface error on: " << name;
69   });
70 }
71 template <class T>
SetInterfaceErrorHandler(fidl::Binding<T> & binding,std::string name)72 void SetInterfaceErrorHandler(fidl::Binding<T>& binding, std::string name) {
73   binding.set_error_handler([name](zx_status_t status) {
74     FML_LOG(ERROR) << "Interface error on: " << name;
75   });
76 }
77 
PlatformView(PlatformView::Delegate & delegate,std::string debug_label,flutter::TaskRunners task_runners,fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> parent_environment_service_provider_handle,fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener> session_listener_request,fit::closure session_listener_error_callback,OnMetricsUpdate session_metrics_did_change_callback,OnSizeChangeHint session_size_change_hint_callback,zx_handle_t vsync_event_handle)78 PlatformView::PlatformView(
79     PlatformView::Delegate& delegate,
80     std::string debug_label,
81     flutter::TaskRunners task_runners,
82     fidl::InterfaceHandle<fuchsia::sys::ServiceProvider>
83         parent_environment_service_provider_handle,
84     fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener>
85         session_listener_request,
86     fit::closure session_listener_error_callback,
87     OnMetricsUpdate session_metrics_did_change_callback,
88     OnSizeChangeHint session_size_change_hint_callback,
89     zx_handle_t vsync_event_handle)
90     : flutter::PlatformView(delegate, std::move(task_runners)),
91       debug_label_(std::move(debug_label)),
92       session_listener_binding_(this, std::move(session_listener_request)),
93       session_listener_error_callback_(
94           std::move(session_listener_error_callback)),
95       metrics_changed_callback_(std::move(session_metrics_did_change_callback)),
96       size_change_hint_callback_(std::move(session_size_change_hint_callback)),
97       ime_client_(this),
98       surface_(std::make_unique<Surface>(debug_label_)),
99       vsync_event_handle_(vsync_event_handle) {
100   // Register all error handlers.
101   SetInterfaceErrorHandler(session_listener_binding_, "SessionListener");
102   SetInterfaceErrorHandler(ime_, "Input Method Editor");
103   SetInterfaceErrorHandler(text_sync_service_, "Text Sync Service");
104   SetInterfaceErrorHandler(clipboard_, "Clipboard");
105   SetInterfaceErrorHandler(parent_environment_service_provider_,
106                            "Parent Environment Service Provider");
107   // Access the clipboard.
108   parent_environment_service_provider_ =
109       parent_environment_service_provider_handle.Bind();
110   parent_environment_service_provider_.get()->ConnectToService(
111       fuchsia::modular::Clipboard::Name_,
112       clipboard_.NewRequest().TakeChannel());
113 
114   parent_environment_service_provider_.get()->ConnectToService(
115       fuchsia::ui::input::ImeService::Name_,
116       text_sync_service_.NewRequest().TakeChannel());
117 
118   // Finally! Register the native platform message handlers.
119   RegisterPlatformMessageHandlers();
120 
121   // TODO(SCN-975): Re-enable.  Likely that Engine should clone the ViewToken
122   // and pass the clone in here.
123   //   view_->GetToken(std::bind(&PlatformView::ConnectSemanticsProvider, this,
124   //                             std::placeholders::_1));
125 }
126 
127 PlatformView::~PlatformView() = default;
128 
RegisterPlatformMessageHandlers()129 void PlatformView::RegisterPlatformMessageHandlers() {
130   platform_message_handlers_[kFlutterPlatformChannel] =
131       std::bind(&PlatformView::HandleFlutterPlatformChannelPlatformMessage,
132                 this, std::placeholders::_1);
133   platform_message_handlers_[kTextInputChannel] =
134       std::bind(&PlatformView::HandleFlutterTextInputChannelPlatformMessage,
135                 this, std::placeholders::_1);
136   platform_message_handlers_[kAccessibilityChannel] =
137       std::bind(&PlatformView::HandleAccessibilityChannelPlatformMessage, this,
138                 std::placeholders::_1);
139 }
140 
OnPropertiesChanged(const fuchsia::ui::gfx::ViewProperties & view_properties)141 void PlatformView::OnPropertiesChanged(
142     const fuchsia::ui::gfx::ViewProperties& view_properties) {
143   fuchsia::ui::gfx::BoundingBox layout_box =
144       ViewPropertiesLayoutBox(view_properties);
145 
146   fuchsia::ui::gfx::vec3 logical_size =
147       Max(Subtract(layout_box.max, layout_box.min), 0.f);
148 
149   metrics_.size.width = logical_size.x;
150   metrics_.size.height = logical_size.y;
151   metrics_.size.depth = logical_size.z;
152   metrics_.padding.left = view_properties.inset_from_min.x;
153   metrics_.padding.top = view_properties.inset_from_min.y;
154   metrics_.padding.front = view_properties.inset_from_min.z;
155   metrics_.padding.right = view_properties.inset_from_max.x;
156   metrics_.padding.bottom = view_properties.inset_from_max.y;
157   metrics_.padding.back = view_properties.inset_from_max.z;
158 
159   FlushViewportMetrics();
160 }
161 
162 // TODO(SCN-975): Re-enable.
163 // void PlatformView::ConnectSemanticsProvider(
164 //     fuchsia::ui::viewsv1token::ViewToken token) {
165 //   semantics_bridge_.SetupEnvironment(
166 //       token.value, parent_environment_service_provider_.get());
167 // }
168 
UpdateViewportMetrics(const fuchsia::ui::gfx::Metrics & metrics)169 void PlatformView::UpdateViewportMetrics(
170     const fuchsia::ui::gfx::Metrics& metrics) {
171   metrics_.scale = metrics.scale_x;
172   metrics_.scale_z = metrics.scale_z;
173 
174   FlushViewportMetrics();
175 }
176 
FlushViewportMetrics()177 void PlatformView::FlushViewportMetrics() {
178   const auto scale = metrics_.scale;
179   const auto scale_z = metrics_.scale_z;
180 
181   SetViewportMetrics({
182       scale,                                // device_pixel_ratio
183       metrics_.size.width * scale,          // physical_width
184       metrics_.size.height * scale,         // physical_height
185       metrics_.size.depth * scale_z,        // physical_depth
186       metrics_.padding.top * scale,         // physical_padding_top
187       metrics_.padding.right * scale,       // physical_padding_right
188       metrics_.padding.bottom * scale,      // physical_padding_bottom
189       metrics_.padding.left * scale,        // physical_padding_left
190       metrics_.view_inset.front * scale_z,  // physical_view_inset_front
191       metrics_.view_inset.back * scale_z,   // physical_view_inset_back
192       metrics_.view_inset.top * scale,      // physical_view_inset_top
193       metrics_.view_inset.right * scale,    // physical_view_inset_right
194       metrics_.view_inset.bottom * scale,   // physical_view_inset_bottom
195       metrics_.view_inset.left * scale      // physical_view_inset_left
196   });
197 }
198 
199 // |fuchsia::ui::input::InputMethodEditorClient|
DidUpdateState(fuchsia::ui::input::TextInputState state,std::unique_ptr<fuchsia::ui::input::InputEvent> input_event)200 void PlatformView::DidUpdateState(
201     fuchsia::ui::input::TextInputState state,
202     std::unique_ptr<fuchsia::ui::input::InputEvent> input_event) {
203   rapidjson::Document document;
204   auto& allocator = document.GetAllocator();
205   rapidjson::Value encoded_state(rapidjson::kObjectType);
206   encoded_state.AddMember("text", state.text, allocator);
207   encoded_state.AddMember("selectionBase", state.selection.base, allocator);
208   encoded_state.AddMember("selectionExtent", state.selection.extent, allocator);
209   switch (state.selection.affinity) {
210     case fuchsia::ui::input::TextAffinity::UPSTREAM:
211       encoded_state.AddMember("selectionAffinity",
212                               rapidjson::Value("TextAffinity.upstream"),
213                               allocator);
214       break;
215     case fuchsia::ui::input::TextAffinity::DOWNSTREAM:
216       encoded_state.AddMember("selectionAffinity",
217                               rapidjson::Value("TextAffinity.downstream"),
218                               allocator);
219       break;
220   }
221   encoded_state.AddMember("selectionIsDirectional", true, allocator);
222   encoded_state.AddMember("composingBase", state.composing.start, allocator);
223   encoded_state.AddMember("composingExtent", state.composing.end, allocator);
224 
225   rapidjson::Value args(rapidjson::kArrayType);
226   args.PushBack(current_text_input_client_, allocator);
227   args.PushBack(encoded_state, allocator);
228 
229   document.SetObject();
230   document.AddMember("method",
231                      rapidjson::Value("TextInputClient.updateEditingState"),
232                      allocator);
233   document.AddMember("args", args, allocator);
234 
235   rapidjson::StringBuffer buffer;
236   rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
237   document.Accept(writer);
238 
239   const uint8_t* data = reinterpret_cast<const uint8_t*>(buffer.GetString());
240   DispatchPlatformMessage(fml::MakeRefCounted<flutter::PlatformMessage>(
241       kTextInputChannel,                                    // channel
242       std::vector<uint8_t>(data, data + buffer.GetSize()),  // message
243       nullptr)                                              // response
244   );
245   last_text_state_ =
246       std::make_unique<fuchsia::ui::input::TextInputState>(state);
247 
248   // Handle keyboard input events for HID keys only.
249   // TODO(SCN-1189): Are we done here?
250   if (input_event && input_event->keyboard().hid_usage != 0) {
251     OnHandleKeyboardEvent(input_event->keyboard());
252   }
253 }
254 
255 // |fuchsia::ui::input::InputMethodEditorClient|
OnAction(fuchsia::ui::input::InputMethodAction action)256 void PlatformView::OnAction(fuchsia::ui::input::InputMethodAction action) {
257   rapidjson::Document document;
258   auto& allocator = document.GetAllocator();
259 
260   rapidjson::Value args(rapidjson::kArrayType);
261   args.PushBack(current_text_input_client_, allocator);
262 
263   // Done is currently the only text input action defined by Flutter.
264   args.PushBack("TextInputAction.done", allocator);
265 
266   document.SetObject();
267   document.AddMember(
268       "method", rapidjson::Value("TextInputClient.performAction"), allocator);
269   document.AddMember("args", args, allocator);
270 
271   rapidjson::StringBuffer buffer;
272   rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
273   document.Accept(writer);
274 
275   const uint8_t* data = reinterpret_cast<const uint8_t*>(buffer.GetString());
276   DispatchPlatformMessage(fml::MakeRefCounted<flutter::PlatformMessage>(
277       kTextInputChannel,                                    // channel
278       std::vector<uint8_t>(data, data + buffer.GetSize()),  // message
279       nullptr)                                              // response
280   );
281 }
282 
OnScenicError(std::string error)283 void PlatformView::OnScenicError(std::string error) {
284   FML_LOG(ERROR) << "Session error: " << error;
285   session_listener_error_callback_();
286 }
287 
OnScenicEvent(std::vector<fuchsia::ui::scenic::Event> events)288 void PlatformView::OnScenicEvent(
289     std::vector<fuchsia::ui::scenic::Event> events) {
290   TRACE_EVENT0("flutter", "PlatformView::OnScenicEvent");
291   for (const auto& event : events) {
292     switch (event.Which()) {
293       case fuchsia::ui::scenic::Event::Tag::kGfx:
294         switch (event.gfx().Which()) {
295           case fuchsia::ui::gfx::Event::Tag::kMetrics: {
296             if (!fidl::Equals(event.gfx().metrics().metrics, scenic_metrics_)) {
297               scenic_metrics_ = std::move(event.gfx().metrics().metrics);
298               metrics_changed_callback_(scenic_metrics_);
299               UpdateViewportMetrics(scenic_metrics_);
300             }
301             break;
302           }
303           case fuchsia::ui::gfx::Event::Tag::kSizeChangeHint: {
304             size_change_hint_callback_(
305                 event.gfx().size_change_hint().width_change_factor,
306                 event.gfx().size_change_hint().height_change_factor);
307             break;
308           }
309           case fuchsia::ui::gfx::Event::Tag::kViewPropertiesChanged: {
310             OnPropertiesChanged(
311                 std::move(event.gfx().view_properties_changed().properties));
312             break;
313           }
314           case fuchsia::ui::gfx::Event::Tag::kViewConnected:
315             OnChildViewConnected(event.gfx().view_connected().view_holder_id);
316             break;
317           case fuchsia::ui::gfx::Event::Tag::kViewDisconnected:
318             OnChildViewDisconnected(
319                 event.gfx().view_disconnected().view_holder_id);
320             break;
321           case fuchsia::ui::gfx::Event::Tag::kViewStateChanged:
322             OnChildViewStateChanged(
323                 event.gfx().view_state_changed().view_holder_id,
324                 event.gfx().view_state_changed().state.is_rendering);
325             break;
326           case fuchsia::ui::gfx::Event::Tag::Invalid:
327             FML_DCHECK(false) << "Flutter PlatformView::OnScenicEvent: Got "
328                                  "an invalid GFX event.";
329             break;
330           default:
331             // We don't care about some event types, so not handling them is OK.
332             break;
333         }
334         break;
335       case fuchsia::ui::scenic::Event::Tag::kInput:
336         switch (event.input().Which()) {
337           case fuchsia::ui::input::InputEvent::Tag::kFocus: {
338             OnHandleFocusEvent(event.input().focus());
339             break;
340           }
341           case fuchsia::ui::input::InputEvent::Tag::kPointer: {
342             OnHandlePointerEvent(event.input().pointer());
343             break;
344           }
345           case fuchsia::ui::input::InputEvent::Tag::kKeyboard: {
346             OnHandleKeyboardEvent(event.input().keyboard());
347             break;
348           }
349           case fuchsia::ui::input::InputEvent::Tag::Invalid: {
350             FML_DCHECK(false)
351                 << "Flutter PlatformView::OnScenicEvent: Got an invalid INPUT "
352                    "event.";
353           }
354         }
355         break;
356       default: {
357         break;
358       }
359     }
360   }
361 }
362 
OnChildViewConnected(scenic::ResourceId view_holder_id)363 void PlatformView::OnChildViewConnected(scenic::ResourceId view_holder_id) {
364   task_runners_.GetUITaskRunner()->PostTask([view_holder_id]() {
365     flutter::SceneHost::OnViewConnected(view_holder_id);
366   });
367 }
368 
OnChildViewDisconnected(scenic::ResourceId view_holder_id)369 void PlatformView::OnChildViewDisconnected(scenic::ResourceId view_holder_id) {
370   task_runners_.GetUITaskRunner()->PostTask([view_holder_id]() {
371     flutter::SceneHost::OnViewDisconnected(view_holder_id);
372   });
373 }
374 
OnChildViewStateChanged(scenic::ResourceId view_holder_id,bool state)375 void PlatformView::OnChildViewStateChanged(scenic::ResourceId view_holder_id,
376                                            bool state) {
377   task_runners_.GetUITaskRunner()->PostTask([view_holder_id, state]() {
378     flutter::SceneHost::OnViewStateChanged(view_holder_id, state);
379   });
380 }
381 
GetChangeFromPointerEventPhase(fuchsia::ui::input::PointerEventPhase phase)382 static flutter::PointerData::Change GetChangeFromPointerEventPhase(
383     fuchsia::ui::input::PointerEventPhase phase) {
384   switch (phase) {
385     case fuchsia::ui::input::PointerEventPhase::ADD:
386       return flutter::PointerData::Change::kAdd;
387     case fuchsia::ui::input::PointerEventPhase::HOVER:
388       return flutter::PointerData::Change::kHover;
389     case fuchsia::ui::input::PointerEventPhase::DOWN:
390       return flutter::PointerData::Change::kDown;
391     case fuchsia::ui::input::PointerEventPhase::MOVE:
392       return flutter::PointerData::Change::kMove;
393     case fuchsia::ui::input::PointerEventPhase::UP:
394       return flutter::PointerData::Change::kUp;
395     case fuchsia::ui::input::PointerEventPhase::REMOVE:
396       return flutter::PointerData::Change::kRemove;
397     case fuchsia::ui::input::PointerEventPhase::CANCEL:
398       return flutter::PointerData::Change::kCancel;
399     default:
400       return flutter::PointerData::Change::kCancel;
401   }
402 }
403 
GetKindFromPointerType(fuchsia::ui::input::PointerEventType type)404 static flutter::PointerData::DeviceKind GetKindFromPointerType(
405     fuchsia::ui::input::PointerEventType type) {
406   switch (type) {
407     case fuchsia::ui::input::PointerEventType::TOUCH:
408       return flutter::PointerData::DeviceKind::kTouch;
409     case fuchsia::ui::input::PointerEventType::MOUSE:
410       return flutter::PointerData::DeviceKind::kMouse;
411     default:
412       return flutter::PointerData::DeviceKind::kTouch;
413   }
414 }
415 
416 // TODO(SCN-1278): Remove this.
417 // Turns two floats (high bits, low bits) into a 64-bit uint.
PointerTraceHACK(float fa,float fb)418 static trace_flow_id_t PointerTraceHACK(float fa, float fb) {
419   uint32_t ia, ib;
420   memcpy(&ia, &fa, sizeof(uint32_t));
421   memcpy(&ib, &fb, sizeof(uint32_t));
422   return (((uint64_t)ia) << 32) | ib;
423 }
424 
OnHandlePointerEvent(const fuchsia::ui::input::PointerEvent & pointer)425 bool PlatformView::OnHandlePointerEvent(
426     const fuchsia::ui::input::PointerEvent& pointer) {
427   TRACE_EVENT0("flutter", "PlatformView::OnHandlePointerEvent");
428 
429   // TODO(SCN-1278): Use proper trace_id for tracing flow.
430   trace_flow_id_t trace_id =
431       PointerTraceHACK(pointer.radius_major, pointer.radius_minor);
432   TRACE_FLOW_END("input", "dispatch_event_to_client", trace_id);
433 
434   flutter::PointerData pointer_data;
435   pointer_data.Clear();
436   pointer_data.time_stamp = pointer.event_time / 1000;
437   pointer_data.change = GetChangeFromPointerEventPhase(pointer.phase);
438   pointer_data.kind = GetKindFromPointerType(pointer.type);
439   pointer_data.device = pointer.pointer_id;
440   pointer_data.physical_x = pointer.x * metrics_.scale;
441   pointer_data.physical_y = pointer.y * metrics_.scale;
442   // Buttons are single bit values starting with kMousePrimaryButton = 1.
443   pointer_data.buttons = static_cast<uint64_t>(pointer.buttons);
444 
445   switch (pointer_data.change) {
446     case flutter::PointerData::Change::kDown:
447       down_pointers_.insert(pointer_data.device);
448       break;
449     case flutter::PointerData::Change::kCancel:
450     case flutter::PointerData::Change::kUp:
451       down_pointers_.erase(pointer_data.device);
452       break;
453     case flutter::PointerData::Change::kMove:
454       if (down_pointers_.count(pointer_data.device) == 0) {
455         pointer_data.change = flutter::PointerData::Change::kHover;
456       }
457       break;
458     case flutter::PointerData::Change::kAdd:
459       if (down_pointers_.count(pointer_data.device) != 0) {
460         FML_DLOG(ERROR) << "Received add event for down pointer.";
461       }
462       break;
463     case flutter::PointerData::Change::kRemove:
464       if (down_pointers_.count(pointer_data.device) != 0) {
465         FML_DLOG(ERROR) << "Received remove event for down pointer.";
466       }
467       break;
468     case flutter::PointerData::Change::kHover:
469       if (down_pointers_.count(pointer_data.device) != 0) {
470         FML_DLOG(ERROR) << "Received hover event for down pointer.";
471       }
472       break;
473   }
474 
475   auto packet = std::make_unique<flutter::PointerDataPacket>(1);
476   packet->SetPointerData(0, pointer_data);
477   DispatchPointerDataPacket(std::move(packet));
478   return true;
479 }
480 
OnHandleKeyboardEvent(const fuchsia::ui::input::KeyboardEvent & keyboard)481 bool PlatformView::OnHandleKeyboardEvent(
482     const fuchsia::ui::input::KeyboardEvent& keyboard) {
483   const char* type = nullptr;
484   if (keyboard.phase == fuchsia::ui::input::KeyboardEventPhase::PRESSED) {
485     type = "keydown";
486   } else if (keyboard.phase == fuchsia::ui::input::KeyboardEventPhase::REPEAT) {
487     type = "keydown";  // TODO change this to keyrepeat
488   } else if (keyboard.phase ==
489              fuchsia::ui::input::KeyboardEventPhase::RELEASED) {
490     type = "keyup";
491   }
492 
493   if (type == nullptr) {
494     FML_DLOG(ERROR) << "Unknown key event phase.";
495     return false;
496   }
497 
498   rapidjson::Document document;
499   auto& allocator = document.GetAllocator();
500   document.SetObject();
501   document.AddMember("type", rapidjson::Value(type, strlen(type)), allocator);
502   document.AddMember("keymap", rapidjson::Value("fuchsia"), allocator);
503   document.AddMember("hidUsage", keyboard.hid_usage, allocator);
504   document.AddMember("codePoint", keyboard.code_point, allocator);
505   document.AddMember("modifiers", keyboard.modifiers, allocator);
506   rapidjson::StringBuffer buffer;
507   rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
508   document.Accept(writer);
509 
510   const uint8_t* data = reinterpret_cast<const uint8_t*>(buffer.GetString());
511   DispatchPlatformMessage(fml::MakeRefCounted<flutter::PlatformMessage>(
512       kKeyEventChannel,                                     // channel
513       std::vector<uint8_t>(data, data + buffer.GetSize()),  // data
514       nullptr)                                              // response
515   );
516 
517   return true;
518 }
519 
OnHandleFocusEvent(const fuchsia::ui::input::FocusEvent & focus)520 bool PlatformView::OnHandleFocusEvent(
521     const fuchsia::ui::input::FocusEvent& focus) {
522   // Ensure last_text_state_ is set to make sure Flutter actually wants an IME.
523   if (focus.focused && last_text_state_ != nullptr) {
524     ActivateIme();
525     return true;
526   } else if (!focus.focused) {
527     DeactivateIme();
528     return true;
529   }
530   return false;
531 }
532 
ActivateIme()533 void PlatformView::ActivateIme() {
534   DEBUG_CHECK(last_text_state_ != nullptr, LOG_TAG, "");
535 
536   text_sync_service_->GetInputMethodEditor(
537       fuchsia::ui::input::KeyboardType::TEXT,       // keyboard type
538       fuchsia::ui::input::InputMethodAction::DONE,  // input method action
539       *last_text_state_,                            // initial state
540       ime_client_.NewBinding(),                     // client
541       ime_.NewRequest()                             // editor
542   );
543 }
544 
DeactivateIme()545 void PlatformView::DeactivateIme() {
546   if (ime_) {
547     text_sync_service_->HideKeyboard();
548     ime_ = nullptr;
549   }
550   if (ime_client_.is_bound()) {
551     ime_client_.Unbind();
552   }
553 }
554 
555 // |flutter::PlatformView|
CreateVSyncWaiter()556 std::unique_ptr<flutter::VsyncWaiter> PlatformView::CreateVSyncWaiter() {
557   return std::make_unique<flutter_runner::VsyncWaiter>(
558       debug_label_, vsync_event_handle_, task_runners_);
559 }
560 
561 // |flutter::PlatformView|
CreateRenderingSurface()562 std::unique_ptr<flutter::Surface> PlatformView::CreateRenderingSurface() {
563   // This platform does not repeatly lose and gain a surface connection. So the
564   // surface is setup once during platform view setup and and returned to the
565   // shell on the initial (and only) |NotifyCreated| call.
566   return std::move(surface_);
567 }
568 
569 // |flutter::PlatformView|
HandlePlatformMessage(fml::RefPtr<flutter::PlatformMessage> message)570 void PlatformView::HandlePlatformMessage(
571     fml::RefPtr<flutter::PlatformMessage> message) {
572   if (!message) {
573     return;
574   }
575   auto found = platform_message_handlers_.find(message->channel());
576   if (found == platform_message_handlers_.end()) {
577     FML_LOG(ERROR)
578         << "Platform view received message on channel '" << message->channel()
579         << "' with no registered handler. And empty response will be "
580            "generated. Please implement the native message handler.";
581     flutter::PlatformView::HandlePlatformMessage(std::move(message));
582     return;
583   }
584   found->second(std::move(message));
585 }
586 
587 // |flutter::PlatformView|
UpdateSemantics(flutter::SemanticsNodeUpdates update,flutter::CustomAccessibilityActionUpdates actions)588 void PlatformView::UpdateSemantics(
589     flutter::SemanticsNodeUpdates update,
590     flutter::CustomAccessibilityActionUpdates actions) {
591   // TODO(MIT-1539): Uncomment/Reimplement following code, to add A11y support.
592   // semantics_bridge_.UpdateSemantics(update);
593 }
594 
595 // Channel handler for kAccessibilityChannel
HandleAccessibilityChannelPlatformMessage(fml::RefPtr<flutter::PlatformMessage> message)596 void PlatformView::HandleAccessibilityChannelPlatformMessage(
597     fml::RefPtr<flutter::PlatformMessage> message) {
598   FML_DCHECK(message->channel() == kAccessibilityChannel);
599 }
600 
601 // Channel handler for kFlutterPlatformChannel
HandleFlutterPlatformChannelPlatformMessage(fml::RefPtr<flutter::PlatformMessage> message)602 void PlatformView::HandleFlutterPlatformChannelPlatformMessage(
603     fml::RefPtr<flutter::PlatformMessage> message) {
604   FML_DCHECK(message->channel() == kFlutterPlatformChannel);
605   const auto& data = message->data();
606   rapidjson::Document document;
607   document.Parse(reinterpret_cast<const char*>(data.data()), data.size());
608   if (document.HasParseError() || !document.IsObject()) {
609     return;
610   }
611 
612   auto root = document.GetObject();
613   auto method = root.FindMember("method");
614   if (method == root.MemberEnd() || !method->value.IsString()) {
615     return;
616   }
617 
618   fml::RefPtr<flutter::PlatformMessageResponse> response = message->response();
619   if (method->value == "Clipboard.setData") {
620     auto text = root["args"]["text"].GetString();
621     clipboard_->Push(text);
622     response->CompleteEmpty();
623   } else if (method->value == "Clipboard.getData") {
624     clipboard_->Peek([response](fidl::StringPtr text) {
625       rapidjson::StringBuffer json_buffer;
626       rapidjson::Writer<rapidjson::StringBuffer> writer(json_buffer);
627       writer.StartArray();
628       writer.StartObject();
629       writer.Key("text");
630       writer.String(text.get());
631       writer.EndObject();
632       writer.EndArray();
633       std::string result = json_buffer.GetString();
634       response->Complete(std::make_unique<fml::DataMapping>(
635           std::vector<uint8_t>{result.begin(), result.end()}));
636     });
637   } else {
638     response->CompleteEmpty();
639   }
640 }
641 
642 // Channel handler for kTextInputChannel
HandleFlutterTextInputChannelPlatformMessage(fml::RefPtr<flutter::PlatformMessage> message)643 void PlatformView::HandleFlutterTextInputChannelPlatformMessage(
644     fml::RefPtr<flutter::PlatformMessage> message) {
645   FML_DCHECK(message->channel() == kTextInputChannel);
646   const auto& data = message->data();
647   rapidjson::Document document;
648   document.Parse(reinterpret_cast<const char*>(data.data()), data.size());
649   if (document.HasParseError() || !document.IsObject()) {
650     return;
651   }
652   auto root = document.GetObject();
653   auto method = root.FindMember("method");
654   if (method == root.MemberEnd() || !method->value.IsString()) {
655     return;
656   }
657 
658   if (method->value == "TextInput.show") {
659     if (ime_) {
660       text_sync_service_->ShowKeyboard();
661     }
662   } else if (method->value == "TextInput.hide") {
663     if (ime_) {
664       text_sync_service_->HideKeyboard();
665     }
666   } else if (method->value == "TextInput.setClient") {
667     current_text_input_client_ = 0;
668     DeactivateIme();
669     auto args = root.FindMember("args");
670     if (args == root.MemberEnd() || !args->value.IsArray() ||
671         args->value.Size() != 2)
672       return;
673     const auto& configuration = args->value[1];
674     if (!configuration.IsObject()) {
675       return;
676     }
677     // TODO(abarth): Read the keyboard type from the configuration.
678     current_text_input_client_ = args->value[0].GetInt();
679 
680     auto initial_text_input_state = fuchsia::ui::input::TextInputState{};
681     initial_text_input_state.text = "";
682     last_text_state_ = std::make_unique<fuchsia::ui::input::TextInputState>(
683         initial_text_input_state);
684     ActivateIme();
685   } else if (method->value == "TextInput.setEditingState") {
686     if (ime_) {
687       auto args_it = root.FindMember("args");
688       if (args_it == root.MemberEnd() || !args_it->value.IsObject()) {
689         return;
690       }
691       const auto& args = args_it->value;
692       fuchsia::ui::input::TextInputState state;
693       state.text = "";
694       // TODO(abarth): Deserialize state.
695       auto text = args.FindMember("text");
696       if (text != args.MemberEnd() && text->value.IsString())
697         state.text = text->value.GetString();
698       auto selection_base = args.FindMember("selectionBase");
699       if (selection_base != args.MemberEnd() && selection_base->value.IsInt())
700         state.selection.base = selection_base->value.GetInt();
701       auto selection_extent = args.FindMember("selectionExtent");
702       if (selection_extent != args.MemberEnd() &&
703           selection_extent->value.IsInt())
704         state.selection.extent = selection_extent->value.GetInt();
705       auto selection_affinity = args.FindMember("selectionAffinity");
706       if (selection_affinity != args.MemberEnd() &&
707           selection_affinity->value.IsString() &&
708           selection_affinity->value == "TextAffinity.upstream")
709         state.selection.affinity = fuchsia::ui::input::TextAffinity::UPSTREAM;
710       else
711         state.selection.affinity = fuchsia::ui::input::TextAffinity::DOWNSTREAM;
712       // We ignore selectionIsDirectional because that concept doesn't exist on
713       // Fuchsia.
714       auto composing_base = args.FindMember("composingBase");
715       if (composing_base != args.MemberEnd() && composing_base->value.IsInt())
716         state.composing.start = composing_base->value.GetInt();
717       auto composing_extent = args.FindMember("composingExtent");
718       if (composing_extent != args.MemberEnd() &&
719           composing_extent->value.IsInt())
720         state.composing.end = composing_extent->value.GetInt();
721       ime_->SetState(std::move(state));
722     }
723   } else if (method->value == "TextInput.clearClient") {
724     current_text_input_client_ = 0;
725     last_text_state_ = nullptr;
726     DeactivateIme();
727   } else {
728     FML_DLOG(ERROR) << "Unknown " << message->channel() << " method "
729                     << method->value.GetString();
730   }
731 }
732 
733 }  // namespace flutter_runner
734