Flutter Engine
platform_view_unittest.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 "gfx_platform_view.h"
6 
7 #include <fuchsia/ui/gfx/cpp/fidl.h>
8 #include <fuchsia/ui/input/cpp/fidl.h>
9 #include <fuchsia/ui/input3/cpp/fidl.h>
10 #include <fuchsia/ui/input3/cpp/fidl_test_base.h>
11 #include <fuchsia/ui/scenic/cpp/fidl.h>
12 #include <fuchsia/ui/views/cpp/fidl.h>
13 #include <lib/async-loop/cpp/loop.h>
14 #include <lib/async-loop/default.h>
15 #include <lib/async/default.h>
16 #include <lib/fidl/cpp/binding_set.h>
17 #include <lib/ui/scenic/cpp/view_ref_pair.h>
18 
19 #include <memory>
20 #include <ostream>
21 #include <string>
22 #include <vector>
23 
24 #include "flutter/flow/embedded_views.h"
25 #include "flutter/lib/ui/window/platform_message.h"
26 #include "flutter/lib/ui/window/pointer_data.h"
27 #include "flutter/lib/ui/window/viewport_metrics.h"
28 #include "flutter/shell/common/context_options.h"
29 #include "flutter/shell/platform/fuchsia/flutter/gfx_platform_view.h"
30 #include "gmock/gmock.h"
31 #include "gtest/gtest.h"
32 
33 #include "platform/assert.h"
34 #include "surface.h"
35 #include "task_runner_adapter.h"
36 #include "tests/fakes/focuser.h"
41 
42 namespace flutter_runner::testing {
43 namespace {
44 
45 class MockExternalViewEmbedder : public flutter::ExternalViewEmbedder {
46  public:
47  SkCanvas* GetRootCanvas() override { return nullptr; }
48  std::vector<SkCanvas*> GetCurrentCanvases() override {
49  return std::vector<SkCanvas*>();
50  }
51 
52  void CancelFrame() override {}
53  void BeginFrame(
54  SkISize frame_size,
55  GrDirectContext* context,
56  double device_pixel_ratio,
57  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override {}
58  void SubmitFrame(GrDirectContext* context,
59  std::unique_ptr<flutter::SurfaceFrame> frame) override {
60  return;
61  }
62 
63  void PrerollCompositeEmbeddedView(
64  int view_id,
65  std::unique_ptr<flutter::EmbeddedViewParams> params) override {}
66  SkCanvas* CompositeEmbeddedView(int view_id) override { return nullptr; }
67 };
68 
69 class MockPlatformViewDelegate : public flutter::PlatformView::Delegate {
70  public:
71  void Reset() {
72  message_ = nullptr;
73  metrics_ = flutter::ViewportMetrics{};
74  semantics_features_ = 0;
75  semantics_enabled_ = false;
76  pointer_packets_.clear();
77  }
78 
79  // |flutter::PlatformView::Delegate|
80  void OnPlatformViewCreated(std::unique_ptr<flutter::Surface> surface) {
81  ASSERT_EQ(surface_.get(), nullptr);
82 
83  surface_ = std::move(surface);
84  }
85  // |flutter::PlatformView::Delegate|
86  void OnPlatformViewDestroyed() {}
87  // |flutter::PlatformView::Delegate|
88  void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) {}
89  // |flutter::PlatformView::Delegate|
90  void OnPlatformViewSetViewportMetrics(
91  const flutter::ViewportMetrics& metrics) {
92  metrics_ = metrics;
93  }
94  // |flutter::PlatformView::Delegate|
95  void OnPlatformViewDispatchPlatformMessage(
96  std::unique_ptr<flutter::PlatformMessage> message) {
97  message_ = std::move(message);
98  }
99  // |flutter::PlatformView::Delegate|
100  void OnPlatformViewDispatchPointerDataPacket(
101  std::unique_ptr<flutter::PointerDataPacket> packet) {
102  pointer_packets_.push_back(std::move(packet));
103  }
104  // |flutter::PlatformView::Delegate|
105  void OnPlatformViewDispatchKeyDataPacket(
106  std::unique_ptr<flutter::KeyDataPacket> packet,
107  std::function<void(bool)> callback) {}
108  // |flutter::PlatformView::Delegate|
109  void OnPlatformViewDispatchSemanticsAction(int32_t id,
112  // |flutter::PlatformView::Delegate|
113  void OnPlatformViewSetSemanticsEnabled(bool enabled) {
114  semantics_enabled_ = enabled;
115  }
116  // |flutter::PlatformView::Delegate|
117  void OnPlatformViewSetAccessibilityFeatures(int32_t flags) {
118  semantics_features_ = flags;
119  }
120  // |flutter::PlatformView::Delegate|
121  void OnPlatformViewRegisterTexture(
122  std::shared_ptr<flutter::Texture> texture) {}
123  // |flutter::PlatformView::Delegate|
124  void OnPlatformViewUnregisterTexture(int64_t texture_id) {}
125  // |flutter::PlatformView::Delegate|
126  void OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) {}
127  // |flutter::PlatformView::Delegate|
128  std::unique_ptr<std::vector<std::string>> ComputePlatformViewResolvedLocale(
129  const std::vector<std::string>& supported_locale_data) {
130  return nullptr;
131  }
132  // |flutter::PlatformView::Delegate|
134  intptr_t loading_unit_id,
135  std::unique_ptr<const fml::Mapping> snapshot_data,
136  std::unique_ptr<const fml::Mapping> snapshot_instructions) {}
137  // |flutter::PlatformView::Delegate|
138  void LoadDartDeferredLibraryError(intptr_t loading_unit_id,
139  const std::string error_message,
140  bool transient) {}
141  // |flutter::PlatformView::Delegate|
142  void UpdateAssetResolverByType(
143  std::unique_ptr<flutter::AssetResolver> updated_asset_resolver,
145 
146  flutter::Surface* surface() const { return surface_.get(); }
147  flutter::PlatformMessage* message() const { return message_.get(); }
148  const flutter::ViewportMetrics& metrics() const { return metrics_; }
149  int32_t semantics_features() const { return semantics_features_; }
150  bool semantics_enabled() const { return semantics_enabled_; }
151  const std::vector<std::unique_ptr<flutter::PointerDataPacket>>&
152  pointer_packets() const {
153  return pointer_packets_;
154  }
155  std::vector<std::unique_ptr<flutter::PointerDataPacket>>
156  TakePointerDataPackets() {
157  auto tmp = std::move(pointer_packets_);
158  pointer_packets_.clear();
159  return tmp;
160  }
161 
162  private:
163  std::unique_ptr<flutter::Surface> surface_;
164  std::unique_ptr<flutter::PlatformMessage> message_;
165  flutter::ViewportMetrics metrics_;
166  std::vector<std::unique_ptr<flutter::PointerDataPacket>> pointer_packets_;
167  int32_t semantics_features_ = 0;
168  bool semantics_enabled_ = false;
169 };
170 
171 class MockResponse : public flutter::PlatformMessageResponse {
172  public:
173  MOCK_METHOD1(Complete, void(std::unique_ptr<fml::Mapping> data));
174  MOCK_METHOD0(CompleteEmpty, void());
175 };
176 
177 class TestPlatformMessageResponse : public flutter::PlatformMessageResponse {
178  public:
179  TestPlatformMessageResponse() {}
180  void Complete(std::unique_ptr<fml::Mapping> data) override {
181  result_string = std::string(
182  reinterpret_cast<const char*>(data->GetMapping()), data->GetSize());
183  is_complete_ = true;
184  }
185  void CompleteEmpty() override { is_complete_ = true; }
186  std::string result_string;
187  FML_DISALLOW_COPY_AND_ASSIGN(TestPlatformMessageResponse);
188 };
189 
190 class MockKeyboard : public fuchsia::ui::input3::testing::Keyboard_TestBase {
191  public:
192  explicit MockKeyboard(
193  fidl::InterfaceRequest<fuchsia::ui::input3::Keyboard> keyboard)
194  : keyboard_(this, std::move(keyboard)) {}
195  ~MockKeyboard() = default;
196 
197  void AddListener(
198  fuchsia::ui::views::ViewRef view_ref,
199  fidl::InterfaceHandle<fuchsia::ui::input3::KeyboardListener> listener,
200  AddListenerCallback callback) override {
201  FML_CHECK(!listener_.is_bound());
202 
203  listener_ = listener.Bind();
204  view_ref_ = std::move(view_ref);
205 
206  callback();
207  }
208 
209  void NotImplemented_(const std::string& name) override { FAIL(); }
210 
211  fidl::Binding<fuchsia::ui::input3::Keyboard> keyboard_;
212  fuchsia::ui::input3::KeyboardListenerPtr listener_{};
213  fuchsia::ui::views::ViewRef view_ref_{};
214 
215  FML_DISALLOW_COPY_AND_ASSIGN(MockKeyboard);
216 };
217 
218 // Used to construct partial instances of PlatformView for testing. The
219 // PlatformView constructor has many parameters, not all of which need to
220 // be filled out for each test. The builder allows you to initialize only
221 // those that matter to your specific test. Not all builder methods are
222 // provided: if you find some that are missing, feel free to add them.
223 class PlatformViewBuilder {
224  public:
225  PlatformViewBuilder(flutter::PlatformView::Delegate& delegate,
226  flutter::TaskRunners task_runners)
227  : delegate_(delegate),
228  task_runners_(task_runners),
229  view_ref_pair_(scenic::ViewRefPair::New()) {}
230 
231  PlatformViewBuilder& SetExternalViewEmbedder(
232  std::shared_ptr<flutter::ExternalViewEmbedder> embedder) {
233  external_external_view_embedder_ = embedder;
234  return *this;
235  }
236 
237  PlatformViewBuilder& SetImeService(
238  fidl::InterfaceHandle<fuchsia::ui::input::ImeService> ime_service) {
239  ime_service_ = std::move(ime_service);
240  return *this;
241  }
242 
243  PlatformViewBuilder& SetKeyboard(
244  fidl::InterfaceHandle<fuchsia::ui::input3::Keyboard> keyboard) {
245  keyboard_ = std::move(keyboard);
246  return *this;
247  }
248 
249  PlatformViewBuilder& SetTouchSource(
250  fidl::InterfaceHandle<fuchsia::ui::pointer::TouchSource> touch_source) {
251  touch_source_ = std::move(touch_source);
252  return *this;
253  }
254 
255  PlatformViewBuilder& SetMouseSource(
256  fidl::InterfaceHandle<fuchsia::ui::pointer::MouseSource> mouse_source) {
257  mouse_source_ = std::move(mouse_source);
258  return *this;
259  }
260 
261  PlatformViewBuilder& SetFocuser(
262  fidl::InterfaceHandle<fuchsia::ui::views::Focuser> focuser) {
263  focuser_ = std::move(focuser);
264  return *this;
265  }
266 
267  PlatformViewBuilder& SetViewRefFocused(
268  fidl::InterfaceHandle<fuchsia::ui::views::ViewRefFocused>
269  view_ref_focused) {
270  view_ref_focused_ = std::move(view_ref_focused);
271  return *this;
272  }
273 
274  PlatformViewBuilder& SetSessionListenerRequest(
275  fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener> request) {
276  session_listener_request_ = std::move(request);
277  return *this;
278  }
279 
280  PlatformViewBuilder& SetEnableWireframeCallback(OnEnableWireframe callback) {
281  wireframe_enabled_callback_ = std::move(callback);
282  return *this;
283  }
284 
285  PlatformViewBuilder& SetCreateViewCallback(OnCreateGfxView callback) {
286  on_create_view_callback_ = std::move(callback);
287  return *this;
288  }
289 
290  PlatformViewBuilder& SetUpdateViewCallback(OnUpdateView callback) {
291  on_update_view_callback_ = std::move(callback);
292  return *this;
293  }
294 
295  PlatformViewBuilder& SetDestroyViewCallback(OnDestroyGfxView callback) {
296  on_destroy_view_callback_ = std::move(callback);
297  return *this;
298  }
299 
300  PlatformViewBuilder& SetCreateSurfaceCallback(OnCreateSurface callback) {
301  on_create_surface_callback_ = std::move(callback);
302  return *this;
303  }
304 
305  PlatformViewBuilder& SetShaderWarmupCallback(OnShaderWarmup callback) {
306  on_shader_warmup_callback_ = std::move(callback);
307  return *this;
308  }
309 
310  // Once Build is called, the instance is no longer usable.
311  GfxPlatformView Build() {
312  EXPECT_FALSE(std::exchange(built_, true))
313  << "Build() was already called, this buider is good for one use only.";
314  return GfxPlatformView(
315  delegate_, task_runners_, std::move(view_ref_pair_.view_ref),
316  external_external_view_embedder_, std::move(ime_service_),
317  std::move(keyboard_), std::move(touch_source_),
318  std::move(mouse_source_), std::move(focuser_),
319  std::move(view_ref_focused_), std::move(session_listener_request_),
320  std::move(on_session_listener_error_callback_),
321  std::move(wireframe_enabled_callback_),
322  std::move(on_create_view_callback_),
323  std::move(on_update_view_callback_),
324  std::move(on_destroy_view_callback_),
325  std::move(on_create_surface_callback_),
326  std::move(on_semantics_node_update_callback_),
327  std::move(on_request_announce_callback_),
328  std::move(on_shader_warmup_callback_), [](auto...) {}, [](auto...) {});
329  }
330 
331  private:
332  PlatformViewBuilder() = delete;
333 
336  scenic::ViewRefPair view_ref_pair_;
337 
338  std::shared_ptr<flutter::ExternalViewEmbedder>
339  external_external_view_embedder_;
340  fidl::InterfaceHandle<fuchsia::ui::input::ImeService> ime_service_;
341  fidl::InterfaceHandle<fuchsia::ui::input3::Keyboard> keyboard_;
342  fidl::InterfaceHandle<fuchsia::ui::pointer::TouchSource> touch_source_;
343  fidl::InterfaceHandle<fuchsia::ui::pointer::MouseSource> mouse_source_;
344  fidl::InterfaceHandle<fuchsia::ui::views::ViewRefFocused> view_ref_focused_;
345  fidl::InterfaceHandle<fuchsia::ui::views::Focuser> focuser_;
346  fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener>
347  session_listener_request_;
348  fit::closure on_session_listener_error_callback_;
349  OnEnableWireframe wireframe_enabled_callback_;
350  OnCreateGfxView on_create_view_callback_;
351  OnUpdateView on_update_view_callback_;
352  OnDestroyGfxView on_destroy_view_callback_;
353  OnCreateSurface on_create_surface_callback_;
354  OnSemanticsNodeUpdate on_semantics_node_update_callback_;
355  OnRequestAnnounce on_request_announce_callback_;
356  OnShaderWarmup on_shader_warmup_callback_;
357 
358  bool built_{false};
359 };
360 
361 std::string ToString(const fml::Mapping& mapping) {
362  return std::string(mapping.GetMapping(),
363  mapping.GetMapping() + mapping.GetSize());
364 }
365 
366 // Stolen from pointer_data_packet_converter_unittests.cc.
367 void UnpackPointerPacket(std::vector<flutter::PointerData>& output, // NOLINT
368  std::unique_ptr<flutter::PointerDataPacket> packet) {
369  size_t kBytesPerPointerData =
371  auto buffer = packet->data();
372  size_t buffer_length = buffer.size();
373 
374  for (size_t i = 0; i < buffer_length / kBytesPerPointerData; i++) {
375  flutter::PointerData pointer_data;
376  memcpy(&pointer_data, &buffer[i * kBytesPerPointerData],
377  sizeof(flutter::PointerData));
378  output.push_back(pointer_data);
379  }
380  packet.reset();
381 }
382 
383 } // namespace
384 
385 class PlatformViewTests : public ::testing::Test {
386  protected:
387  PlatformViewTests() : loop_(&kAsyncLoopConfigAttachToCurrentThread) {}
388 
389  async_dispatcher_t* dispatcher() { return loop_.dispatcher(); }
390 
392  loop_.RunUntilIdle();
393  loop_.ResetQuit();
394  }
395 
396  fuchsia::ui::input3::KeyEvent MakeEvent(
398  std::optional<fuchsia::ui::input3::Modifiers> modifiers,
399  fuchsia::input::Key key) {
400  fuchsia::ui::input3::KeyEvent event;
401  event.set_timestamp(++event_timestamp_);
402  event.set_type(event_type);
403  if (modifiers.has_value()) {
404  event.set_modifiers(modifiers.value());
405  }
406  event.set_key(key);
407  return event;
408  }
409 
410  private:
411  async::Loop loop_;
412 
413  uint64_t event_timestamp_{42};
414 
416 };
417 
418 // This test makes sure that the PlatformView always completes a platform
419 // message request, even for error conditions or if the request is malformed.
420 TEST_F(PlatformViewTests, InvalidPlatformMessageRequest) {
421  MockPlatformViewDelegate delegate;
422  flutter::TaskRunners task_runners =
423  flutter::TaskRunners("test_runners", nullptr, nullptr, nullptr, nullptr);
424 
425  FakeViewRefFocused vrf;
426  fidl::BindingSet<fuchsia::ui::views::ViewRefFocused> vrf_bindings;
427  auto vrf_handle = vrf_bindings.AddBinding(&vrf);
428 
430  PlatformViewBuilder(delegate, std::move(task_runners))
431  .SetViewRefFocused(std::move(vrf_handle))
432  .Build();
433 
434  // Cast platform_view to its base view so we can have access to the public
435  // "HandlePlatformMessage" function.
436  auto base_view = static_cast<flutter::PlatformView*>(&platform_view);
437  EXPECT_TRUE(base_view);
438 
439  // Invalid platform channel.
440  auto response1 = FakePlatformMessageResponse::Create();
441  base_view->HandlePlatformMessage(response1->WithMessage(
442  "flutter/invalid", "{\"method\":\"Invalid.invalidMethod\"}"));
443 
444  // Invalid json.
445  auto response2 = FakePlatformMessageResponse::Create();
446  base_view->HandlePlatformMessage(
447  response2->WithMessage("flutter/platform_views", "{Invalid JSON"));
448 
449  // Invalid method.
450  auto response3 = FakePlatformMessageResponse::Create();
451  base_view->HandlePlatformMessage(response3->WithMessage(
452  "flutter/platform_views", "{\"method\":\"View.focus.invalidMethod\"}"));
453 
454  // Missing arguments.
455  auto response4 = FakePlatformMessageResponse::Create();
456  base_view->HandlePlatformMessage(response4->WithMessage(
457  "flutter/platform_views", "{\"method\":\"View.update\"}"));
458  auto response5 = FakePlatformMessageResponse::Create();
459  base_view->HandlePlatformMessage(
460  response5->WithMessage("flutter/platform_views",
461  "{\"method\":\"View.update\",\"args\":{"
462  "\"irrelevantField\":\"irrelevantValue\"}}"));
463 
464  // Wrong argument types.
465  auto response6 = FakePlatformMessageResponse::Create();
466  base_view->HandlePlatformMessage(response6->WithMessage(
467  "flutter/platform_views",
468  "{\"method\":\"View.update\",\"args\":{\"viewId\":false,\"hitTestable\":"
469  "123,\"focusable\":\"yes\"}}"));
470 
471  // Run the event loop and check our responses.
472  RunLoopUntilIdle();
473  response1->ExpectCompleted("");
474  response2->ExpectCompleted("");
475  response3->ExpectCompleted("");
476  response4->ExpectCompleted("");
477  response5->ExpectCompleted("");
478  response6->ExpectCompleted("");
479 }
480 
481 // This test makes sure that the PlatformView correctly returns a Surface
482 // instance that can surface the provided gr_context and external_view_embedder.
483 TEST_F(PlatformViewTests, CreateSurfaceTest) {
484  MockPlatformViewDelegate delegate;
485 
486  flutter::TaskRunners task_runners =
487  flutter::TaskRunners("test_runners", // label
488  nullptr, // platform
490  async_get_default_dispatcher()), // raster
491  nullptr, // ui
492  nullptr // io
493  );
494 
495  // Test create surface callback function.
496  sk_sp<GrDirectContext> gr_context = GrDirectContext::MakeMock(
497  nullptr,
499  std::shared_ptr<MockExternalViewEmbedder> external_view_embedder =
500  std::make_shared<MockExternalViewEmbedder>();
501  auto CreateSurfaceCallback = [&external_view_embedder, gr_context]() {
502  return std::make_unique<flutter_runner::Surface>(
503  "PlatformViewTest", external_view_embedder, gr_context.get());
504  };
505 
507  PlatformViewBuilder(delegate, std::move(task_runners))
508  .SetCreateSurfaceCallback(CreateSurfaceCallback)
509  .SetExternalViewEmbedder(external_view_embedder)
510  .Build();
511  platform_view.NotifyCreated();
512 
513  RunLoopUntilIdle();
514 
515  EXPECT_EQ(gr_context.get(), delegate.surface()->GetContext());
516  EXPECT_EQ(external_view_embedder.get(),
517  platform_view.CreateExternalViewEmbedder().get());
518 }
519 
520 // This test makes sure that the PlatformView correctly registers Scenic
521 // MetricsEvents sent to it via FIDL, correctly parses the metrics it receives,
522 // and calls the SetViewportMetrics callback with the appropriate parameters.
524  constexpr float invalid_pixel_ratio = -0.75f;
525  constexpr float valid_pixel_ratio = 0.75f;
526  constexpr float invalid_max_bound = -0.75f;
527  constexpr float valid_max_bound = 0.75f;
528 
529  MockPlatformViewDelegate delegate;
530  EXPECT_EQ(delegate.metrics(), flutter::ViewportMetrics());
531 
532  fuchsia::ui::scenic::SessionListenerPtr session_listener;
533  std::vector<fuchsia::ui::scenic::Event> events;
534  flutter::TaskRunners task_runners("test_runners", nullptr, nullptr, nullptr,
535  nullptr);
537  PlatformViewBuilder(delegate, std::move(task_runners))
538  .SetSessionListenerRequest(session_listener.NewRequest())
539  .Build();
540  RunLoopUntilIdle();
541  EXPECT_EQ(delegate.metrics(), flutter::ViewportMetrics());
542 
543  // Test updating with an invalid pixel ratio. The final metrics should be
544  // unchanged.
545  events.clear();
546  events.emplace_back(fuchsia::ui::scenic::Event::WithGfx(
547  fuchsia::ui::gfx::Event::WithMetrics(fuchsia::ui::gfx::MetricsEvent{
548  .node_id = 0,
549  .metrics =
550  fuchsia::ui::gfx::Metrics{
551  .scale_x = invalid_pixel_ratio,
552  .scale_y = 1.f,
553  .scale_z = 1.f,
554  },
555  })));
556  session_listener->OnScenicEvent(std::move(events));
557  RunLoopUntilIdle();
558  EXPECT_EQ(delegate.metrics(), flutter::ViewportMetrics());
559 
560  // Test updating with an invalid size. The final metrics should be unchanged.
561  events.clear();
562  events.emplace_back(
563  fuchsia::ui::scenic::Event::WithGfx(
564  fuchsia::ui::gfx::Event::WithViewPropertiesChanged(
565  fuchsia::ui::gfx::ViewPropertiesChangedEvent{
566  .view_id = 0,
567  .properties =
568  fuchsia::ui::gfx::ViewProperties{
569  .bounding_box =
570  fuchsia::ui::gfx::BoundingBox{
571  .min =
572  fuchsia::ui::gfx::vec3{
573  .x = 0.f,
574  .y = 0.f,
575  .z = 0.f,
576  },
577  .max =
578  fuchsia::ui::gfx::vec3{
579  .x = invalid_max_bound,
580  .y = invalid_max_bound,
581  .z = invalid_max_bound,
582  },
583  },
584  },
585  })));
586  session_listener->OnScenicEvent(std::move(events));
587  RunLoopUntilIdle();
588  EXPECT_EQ(delegate.metrics(), flutter::ViewportMetrics());
589 
590  // Test updating the size only. The final metrics should be unchanged until
591  // both pixel ratio and size are updated.
592  events.clear();
593  events.emplace_back(
594  fuchsia::ui::scenic::Event::WithGfx(
595  fuchsia::ui::gfx::Event::WithViewPropertiesChanged(
596  fuchsia::ui::gfx::ViewPropertiesChangedEvent{
597  .view_id = 0,
598  .properties =
599  fuchsia::ui::gfx::ViewProperties{
600  .bounding_box =
601  fuchsia::ui::gfx::BoundingBox{
602  .min =
603  fuchsia::ui::gfx::vec3{
604  .x = 0.f,
605  .y = 0.f,
606  .z = 0.f,
607  },
608  .max =
609  fuchsia::ui::gfx::vec3{
610  .x = valid_max_bound,
611  .y = valid_max_bound,
612  .z = valid_max_bound,
613  },
614  },
615  },
616  })));
617  session_listener->OnScenicEvent(std::move(events));
618  RunLoopUntilIdle();
619  EXPECT_EQ(delegate.metrics(), flutter::ViewportMetrics());
620 
621  // Test updating the pixel ratio only. The final metrics should change now.
622  events.clear();
623  events.emplace_back(fuchsia::ui::scenic::Event::WithGfx(
624  fuchsia::ui::gfx::Event::WithMetrics(fuchsia::ui::gfx::MetricsEvent{
625  .node_id = 0,
626  .metrics =
627  fuchsia::ui::gfx::Metrics{
628  .scale_x = valid_pixel_ratio,
629  .scale_y = 1.f,
630  .scale_z = 1.f,
631  },
632  })));
633  session_listener->OnScenicEvent(std::move(events));
634  RunLoopUntilIdle();
635  EXPECT_EQ(delegate.metrics(),
637  valid_pixel_ratio, valid_pixel_ratio * valid_max_bound,
638  valid_pixel_ratio * valid_max_bound, -1.0));
639 }
640 
641 // This test makes sure that the PlatformView correctly registers semantics
642 // settings changes applied to it and calls the SetSemanticsEnabled /
643 // SetAccessibilityFeatures callbacks with the appropriate parameters.
644 TEST_F(PlatformViewTests, ChangesAccessibilitySettings) {
645  MockPlatformViewDelegate delegate;
646  flutter::TaskRunners task_runners =
647  flutter::TaskRunners("test_runners", nullptr, nullptr, nullptr, nullptr);
648 
649  EXPECT_FALSE(delegate.semantics_enabled());
650  EXPECT_EQ(delegate.semantics_features(), 0);
651 
653  PlatformViewBuilder(delegate, std::move(task_runners)).Build();
654 
655  RunLoopUntilIdle();
656 
657  platform_view.SetSemanticsEnabled(true);
658 
659  EXPECT_TRUE(delegate.semantics_enabled());
660  EXPECT_EQ(delegate.semantics_features(),
661  static_cast<int32_t>(
663 
664  platform_view.SetSemanticsEnabled(false);
665 
666  EXPECT_FALSE(delegate.semantics_enabled());
667  EXPECT_EQ(delegate.semantics_features(), 0);
668 }
669 
670 // This test makes sure that the PlatformView forwards messages on the
671 // "flutter/platform_views" channel for EnableWireframe.
672 TEST_F(PlatformViewTests, EnableWireframeTest) {
673  MockPlatformViewDelegate delegate;
674  flutter::TaskRunners task_runners =
675  flutter::TaskRunners("test_runners", nullptr, nullptr, nullptr, nullptr);
676 
677  // Test wireframe callback function. If the message sent to the platform
678  // view was properly handled and parsed, this function should be called,
679  // setting |wireframe_enabled| to true.
680  bool wireframe_enabled = false;
681  auto EnableWireframeCallback = [&wireframe_enabled](bool should_enable) {
682  wireframe_enabled = should_enable;
683  };
684 
686  PlatformViewBuilder(delegate, std::move(task_runners))
687  .SetEnableWireframeCallback(EnableWireframeCallback)
688  .Build();
689 
690  // Cast platform_view to its base view so we can have access to the public
691  // "HandlePlatformMessage" function.
692  auto base_view = static_cast<flutter::PlatformView*>(&platform_view);
693  EXPECT_TRUE(base_view);
694 
695  // JSON for the message to be passed into the PlatformView.
696  const uint8_t txt[] =
697  "{"
698  " \"method\":\"View.enableWireframe\","
699  " \"args\": {"
700  " \"enable\":true"
701  " }"
702  "}";
703 
704  std::unique_ptr<flutter::PlatformMessage> message =
705  std::make_unique<flutter::PlatformMessage>(
706  "flutter/platform_views", fml::MallocMapping::Copy(txt, sizeof(txt)),
708  base_view->HandlePlatformMessage(std::move(message));
709 
710  RunLoopUntilIdle();
711 
712  EXPECT_TRUE(wireframe_enabled);
713 }
714 
715 // This test makes sure that the PlatformView forwards messages on the
716 // "flutter/platform_views" channel for Createview.
717 TEST_F(PlatformViewTests, CreateViewTest) {
718  MockPlatformViewDelegate delegate;
719  flutter::TaskRunners task_runners =
720  flutter::TaskRunners("test_runners", // label
722  async_get_default_dispatcher()), // platform
723  nullptr, // raster
724  nullptr, // ui
725  nullptr // io
726  );
727 
728  // Test wireframe callback function. If the message sent to the platform
729  // view was properly handled and parsed, this function should be called,
730  // setting |wireframe_enabled| to true.
731  bool create_view_called = false;
732  auto CreateViewCallback = [&create_view_called](
733  int64_t view_id,
734  flutter_runner::ViewCallback on_view_created,
735  flutter_runner::GfxViewIdCallback on_view_bound,
736  bool hit_testable, bool focusable) {
737  create_view_called = true;
738  on_view_created();
739  on_view_bound(0);
740  };
741 
743  PlatformViewBuilder(delegate, std::move(task_runners))
744  .SetCreateViewCallback(CreateViewCallback)
745  .Build();
746 
747  // Cast platform_view to its base view so we can have access to the public
748  // "HandlePlatformMessage" function.
749  auto base_view = static_cast<flutter::PlatformView*>(&platform_view);
750  EXPECT_TRUE(base_view);
751 
752  // JSON for the message to be passed into the PlatformView.
753  const uint8_t txt[] =
754  "{"
755  " \"method\":\"View.create\","
756  " \"args\": {"
757  " \"viewId\":42,"
758  " \"hitTestable\":true,"
759  " \"focusable\":true"
760  " }"
761  "}";
762 
763  std::unique_ptr<flutter::PlatformMessage> message =
764  std::make_unique<flutter::PlatformMessage>(
765  "flutter/platform_views", fml::MallocMapping::Copy(txt, sizeof(txt)),
767  base_view->HandlePlatformMessage(std::move(message));
768 
769  RunLoopUntilIdle();
770 
771  EXPECT_TRUE(create_view_called);
772 }
773 
774 // This test makes sure that the PlatformView forwards messages on the
775 // "flutter/platform_views" channel for UpdateView.
776 TEST_F(PlatformViewTests, UpdateViewTest) {
777  MockPlatformViewDelegate delegate;
778  flutter::TaskRunners task_runners =
779  flutter::TaskRunners("test_runners", nullptr, nullptr, nullptr, nullptr);
780 
781  std::optional<SkRect> occlusion_hint_for_test;
782  std::optional<bool> hit_testable_for_test;
783  std::optional<bool> focusable_for_test;
784  auto UpdateViewCallback = [&occlusion_hint_for_test, &hit_testable_for_test,
785  &focusable_for_test](
786  int64_t view_id, SkRect occlusion_hint,
787  bool hit_testable, bool focusable) {
788  occlusion_hint_for_test = occlusion_hint;
789  hit_testable_for_test = hit_testable;
790  focusable_for_test = focusable;
791  };
792 
794  PlatformViewBuilder(delegate, std::move(task_runners))
795  .SetUpdateViewCallback(UpdateViewCallback)
796  .Build();
797 
798  // Cast platform_view to its base view so we can have access to the public
799  // "HandlePlatformMessage" function.
800  auto base_view = static_cast<flutter::PlatformView*>(&platform_view);
801  EXPECT_TRUE(base_view);
802 
803  // Send a basic message.
804  const uint8_t json[] =
805  "{"
806  " \"method\":\"View.update\","
807  " \"args\": {"
808  " \"viewId\":42,"
809  " \"hitTestable\":true,"
810  " \"focusable\":true"
811  " }"
812  "}";
813  std::unique_ptr<flutter::PlatformMessage> message =
814  std::make_unique<flutter::PlatformMessage>(
815  "flutter/platform_views",
816  fml::MallocMapping::Copy(json, sizeof(json)),
818  base_view->HandlePlatformMessage(std::move(message));
819 
820  RunLoopUntilIdle();
821  ASSERT_TRUE(occlusion_hint_for_test.has_value());
822  ASSERT_TRUE(hit_testable_for_test.has_value());
823  ASSERT_TRUE(focusable_for_test.has_value());
824  EXPECT_EQ(occlusion_hint_for_test.value(), SkRect::MakeEmpty());
825  EXPECT_EQ(hit_testable_for_test.value(), true);
826  EXPECT_EQ(focusable_for_test.value(), true);
827 
828  // Reset for the next message.
829  occlusion_hint_for_test.reset();
830  hit_testable_for_test.reset();
831  focusable_for_test.reset();
832 
833  // Send another basic message.
834  const uint8_t json_false[] =
835  "{"
836  " \"method\":\"View.update\","
837  " \"args\": {"
838  " \"viewId\":42,"
839  " \"hitTestable\":false,"
840  " \"focusable\":false"
841  " }"
842  "}";
843  std::unique_ptr<flutter::PlatformMessage> message_false =
844  std::make_unique<flutter::PlatformMessage>(
845  "flutter/platform_views",
846  fml::MallocMapping::Copy(json_false, sizeof(json_false)),
848  base_view->HandlePlatformMessage(std::move(message_false));
849  RunLoopUntilIdle();
850  ASSERT_TRUE(occlusion_hint_for_test.has_value());
851  ASSERT_TRUE(hit_testable_for_test.has_value());
852  ASSERT_TRUE(focusable_for_test.has_value());
853  EXPECT_EQ(occlusion_hint_for_test.value(), SkRect::MakeEmpty());
854  EXPECT_EQ(hit_testable_for_test.value(), false);
855  EXPECT_EQ(focusable_for_test.value(), false);
856 
857  // Reset for the next message.
858  occlusion_hint_for_test.reset();
859  hit_testable_for_test.reset();
860  focusable_for_test.reset();
861 
862  // Send a message including an occlusion hint.
863  const uint8_t json_occlusion_hint[] =
864  "{"
865  " \"method\":\"View.update\","
866  " \"args\": {"
867  " \"viewId\":42,"
868  " \"hitTestable\":true,"
869  " \"focusable\":true,"
870  " \"viewOcclusionHintLTRB\":[0.1,0.2,0.3,0.4]"
871  " }"
872  "}";
873  std::unique_ptr<flutter::PlatformMessage> message_occlusion_hint =
874  std::make_unique<flutter::PlatformMessage>(
875  "flutter/platform_views",
876  fml::MallocMapping::Copy(json_occlusion_hint,
877  sizeof(json_occlusion_hint)),
879  base_view->HandlePlatformMessage(std::move(message_occlusion_hint));
880  RunLoopUntilIdle();
881  ASSERT_TRUE(occlusion_hint_for_test.has_value());
882  ASSERT_TRUE(hit_testable_for_test.has_value());
883  ASSERT_TRUE(focusable_for_test.has_value());
884  EXPECT_EQ(occlusion_hint_for_test.value(),
885  SkRect::MakeLTRB(0.1, 0.2, 0.3, 0.4));
886  EXPECT_EQ(hit_testable_for_test.value(), true);
887  EXPECT_EQ(focusable_for_test.value(), true);
888 }
889 
890 // This test makes sure that the PlatformView forwards messages on the
891 // "flutter/platform_views" channel for DestroyView.
892 TEST_F(PlatformViewTests, DestroyViewTest) {
893  MockPlatformViewDelegate delegate;
894  flutter::TaskRunners task_runners =
895  flutter::TaskRunners("test_runners", // label
897  async_get_default_dispatcher()), // platform
898  nullptr, // raster
899  nullptr, // ui
900  nullptr // io
901  );
902 
903  // Test wireframe callback function. If the message sent to the platform
904  // view was properly handled and parsed, this function should be called,
905  // setting |wireframe_enabled| to true.
906  bool destroy_view_called = false;
907  auto DestroyViewCallback =
908  [&destroy_view_called](
909  int64_t view_id, flutter_runner::GfxViewIdCallback on_view_unbound) {
910  destroy_view_called = true;
911  on_view_unbound(0);
912  };
913 
915  PlatformViewBuilder(delegate, std::move(task_runners))
916  .SetDestroyViewCallback(DestroyViewCallback)
917  .Build();
918 
919  // Cast platform_view to its base view so we can have access to the public
920  // "HandlePlatformMessage" function.
921  auto base_view = static_cast<flutter::PlatformView*>(&platform_view);
922  EXPECT_TRUE(base_view);
923 
924  // JSON for the message to be passed into the PlatformView.
925  const uint8_t txt[] =
926  "{"
927  " \"method\":\"View.dispose\","
928  " \"args\": {"
929  " \"viewId\":42"
930  " }"
931  "}";
932 
933  std::unique_ptr<flutter::PlatformMessage> message =
934  std::make_unique<flutter::PlatformMessage>(
935  "flutter/platform_views", fml::MallocMapping::Copy(txt, sizeof(txt)),
937  base_view->HandlePlatformMessage(std::move(message));
938 
939  RunLoopUntilIdle();
940 
941  EXPECT_TRUE(destroy_view_called);
942 }
943 
944 // This test makes sure that the PlatformView forwards messages on the
945 // "flutter/platform_views" channel for ViewConnected, ViewDisconnected, and
946 // ViewStateChanged events.
947 TEST_F(PlatformViewTests, ViewEventsTest) {
948  constexpr int64_t kViewId = 33;
949  constexpr scenic::ResourceId kViewHolderId = 42;
950  MockPlatformViewDelegate delegate;
951 
952  fuchsia::ui::scenic::SessionListenerPtr session_listener;
953  std::vector<fuchsia::ui::scenic::Event> events;
954  flutter::TaskRunners task_runners =
955  flutter::TaskRunners("test_runners", // label
957  async_get_default_dispatcher()), // platform
959  async_get_default_dispatcher()), // raster
961  async_get_default_dispatcher()), // ui
962  nullptr // io
963  );
964 
965  auto on_create_view =
966  [kViewId](int64_t view_id, flutter_runner::ViewCallback on_view_created,
967  flutter_runner::GfxViewIdCallback on_view_bound,
968  bool hit_testable, bool focusable) {
969  ASSERT_EQ(view_id, kViewId);
970  on_view_created();
971  on_view_bound(kViewHolderId);
972  };
973 
975  PlatformViewBuilder(delegate, std::move(task_runners))
976  .SetSessionListenerRequest(session_listener.NewRequest())
977  .SetCreateViewCallback(on_create_view)
978  .Build();
979  RunLoopUntilIdle();
980  ASSERT_EQ(delegate.message(), nullptr);
981 
982  // Create initial view for testing.
983  std::ostringstream create_view_message;
984  create_view_message << "{"
985  << " \"method\":\"View.create\","
986  << " \"args\":{"
987  << " \"viewId\":" << kViewId << ","
988  << " \"hitTestable\":true,"
989  << " \"focusable\":true"
990  << " }"
991  << "}";
992  std::string create_view_call = create_view_message.str();
993  static_cast<flutter::PlatformView*>(&platform_view)
994  ->HandlePlatformMessage(std::make_unique<flutter::PlatformMessage>(
995  "flutter/platform_views",
996  fml::MallocMapping::Copy(create_view_call.c_str(),
997  create_view_call.size()),
999  RunLoopUntilIdle();
1000 
1001  // ViewConnected event.
1002  delegate.Reset();
1003  events.clear();
1004  events.emplace_back(fuchsia::ui::scenic::Event::WithGfx(
1005  fuchsia::ui::gfx::Event::WithViewConnected(
1006  fuchsia::ui::gfx::ViewConnectedEvent{
1007  .view_holder_id = kViewHolderId,
1008  })));
1009  session_listener->OnScenicEvent(std::move(events));
1010  RunLoopUntilIdle();
1011 
1012  flutter::PlatformMessage* view_connected_msg = delegate.message();
1013  ASSERT_NE(view_connected_msg, nullptr);
1014  std::ostringstream view_connected_expected_out;
1015  view_connected_expected_out
1016  << "{"
1017  << "\"method\":\"View.viewConnected\","
1018  << "\"args\":{"
1019  << " \"viewId\":" << kViewId // ViewHolderToken handle
1020  << " }"
1021  << "}";
1022  EXPECT_EQ(view_connected_expected_out.str(),
1023  ToString(view_connected_msg->data()));
1024 
1025  // ViewDisconnected event.
1026  delegate.Reset();
1027  events.clear();
1028  events.emplace_back(fuchsia::ui::scenic::Event::WithGfx(
1029  fuchsia::ui::gfx::Event::WithViewDisconnected(
1030  fuchsia::ui::gfx::ViewDisconnectedEvent{
1031  .view_holder_id = kViewHolderId,
1032  })));
1033  session_listener->OnScenicEvent(std::move(events));
1034  RunLoopUntilIdle();
1035 
1036  flutter::PlatformMessage* view_disconnected_msg = delegate.message();
1037  ASSERT_NE(view_disconnected_msg, nullptr);
1038  std::ostringstream view_disconnected_expected_out;
1039  view_disconnected_expected_out
1040  << "{"
1041  << "\"method\":\"View.viewDisconnected\","
1042  << "\"args\":{"
1043  << " \"viewId\":" << kViewId // ViewHolderToken handle
1044  << " }"
1045  << "}";
1046  EXPECT_EQ(view_disconnected_expected_out.str(),
1047  ToString(view_disconnected_msg->data()));
1048 
1049  // ViewStateChanged event.
1050  delegate.Reset();
1051  events.clear();
1052  events.emplace_back(fuchsia::ui::scenic::Event::WithGfx(
1053  fuchsia::ui::gfx::Event::WithViewStateChanged(
1054  fuchsia::ui::gfx::ViewStateChangedEvent{
1055  .view_holder_id = kViewHolderId,
1056  .state =
1057  fuchsia::ui::gfx::ViewState{
1058  .is_rendering = true,
1059  },
1060  })));
1061  session_listener->OnScenicEvent(std::move(events));
1062  RunLoopUntilIdle();
1063 
1064  flutter::PlatformMessage* view_state_changed_msg = delegate.message();
1065  ASSERT_NE(view_state_changed_msg, nullptr);
1066  std::ostringstream view_state_changed_expected_out;
1067  view_state_changed_expected_out
1068  << "{"
1069  << "\"method\":\"View.viewStateChanged\","
1070  << "\"args\":{"
1071  << " \"viewId\":" << kViewId << "," // ViewHolderToken
1072  << " \"is_rendering\":true," // IsViewRendering
1073  << " \"state\":true" // IsViewRendering
1074  << " }"
1075  << "}";
1076  EXPECT_EQ(view_state_changed_expected_out.str(),
1077  ToString(view_state_changed_msg->data()));
1078 }
1079 
1080 // This test makes sure that the PlatformView forwards messages on the
1081 // "flutter/platform_views" channel for View.focus.getCurrent and
1082 // View.focus.getNext.
1083 TEST_F(PlatformViewTests, GetFocusStatesTest) {
1084  MockPlatformViewDelegate delegate;
1085  flutter::TaskRunners task_runners =
1086  flutter::TaskRunners("test_runners", nullptr, nullptr, nullptr, nullptr);
1087 
1088  FakeViewRefFocused vrf;
1089  fidl::BindingSet<fuchsia::ui::views::ViewRefFocused> vrf_bindings;
1090  auto vrf_handle = vrf_bindings.AddBinding(&vrf);
1091 
1093  PlatformViewBuilder(delegate, std::move(task_runners))
1094  .SetViewRefFocused(std::move(vrf_handle))
1095  .Build();
1096 
1097  // Cast platform_view to its base view so we can have access to the public
1098  // "HandlePlatformMessage" function.
1099  auto base_view = static_cast<flutter::PlatformView*>(&platform_view);
1100  EXPECT_TRUE(base_view);
1101 
1102  std::vector<bool> vrf_states{false, true, true, false,
1103  true, false, true, true};
1104 
1105  for (std::size_t i = 0; i < vrf_states.size(); ++i) {
1106  // View.focus.getNext should complete with the next focus state.
1107  auto response1 = FakePlatformMessageResponse::Create();
1108  base_view->HandlePlatformMessage(response1->WithMessage(
1109  "flutter/platform_views", "{\"method\":\"View.focus.getNext\"}"));
1110  // Duplicate View.focus.getNext requests should complete empty.
1111  auto response2 = FakePlatformMessageResponse::Create();
1112  base_view->HandlePlatformMessage(response2->WithMessage(
1113  "flutter/platform_views", "{\"method\":\"View.focus.getNext\"}"));
1114 
1115  // Post watch events and make sure the hanging get is invoked each time.
1116  RunLoopUntilIdle();
1117  EXPECT_EQ(vrf.times_watched, i + 1);
1118 
1119  // Dispatch the next vrf event.
1120  vrf.ScheduleCallback(vrf_states[i]);
1121  RunLoopUntilIdle();
1122 
1123  // Make sure View.focus.getCurrent completes with the current focus state.
1124  auto response3 = FakePlatformMessageResponse::Create();
1125  base_view->HandlePlatformMessage(response3->WithMessage(
1126  "flutter/platform_views", "{\"method\":\"View.focus.getCurrent\"}"));
1127  // Duplicate View.focus.getCurrent are allowed.
1128  auto response4 = FakePlatformMessageResponse::Create();
1129  base_view->HandlePlatformMessage(response4->WithMessage(
1130  "flutter/platform_views", "{\"method\":\"View.focus.getCurrent\"}"));
1131 
1132  // Run event loop and check our results.
1133  RunLoopUntilIdle();
1134  response1->ExpectCompleted(vrf_states[i] ? "[true]" : "[false]");
1135  response2->ExpectCompleted("[null]");
1136  response3->ExpectCompleted(vrf_states[i] ? "[true]" : "[false]");
1137  response4->ExpectCompleted(vrf_states[i] ? "[true]" : "[false]");
1138  }
1139 }
1140 
1141 // This test makes sure that the PlatformView forwards messages on the
1142 // "flutter/platform_views" channel for View.focus.request.
1143 TEST_F(PlatformViewTests, RequestFocusTest) {
1144  MockPlatformViewDelegate delegate;
1145  flutter::TaskRunners task_runners =
1146  flutter::TaskRunners("test_runners", nullptr, nullptr, nullptr, nullptr);
1147 
1148  FakeFocuser focuser;
1149  fidl::BindingSet<fuchsia::ui::views::Focuser> focuser_bindings;
1150  auto focuser_handle = focuser_bindings.AddBinding(&focuser);
1151 
1153  PlatformViewBuilder(delegate, std::move(task_runners))
1154  .SetFocuser(std::move(focuser_handle))
1155  .Build();
1156 
1157  // Cast platform_view to its base view so we can have access to the public
1158  // "HandlePlatformMessage" function.
1159  auto base_view = static_cast<flutter::PlatformView*>(&platform_view);
1160  EXPECT_TRUE(base_view);
1161 
1162  // This "Mock" ViewRef serves as the target for the RequestFocus operation.
1163  auto mock_view_ref_pair = scenic::ViewRefPair::New();
1164 
1165  // JSON for the message to be passed into the PlatformView.
1166  std::ostringstream message;
1167  message << "{"
1168  << " \"method\":\"View.focus.request\","
1169  << " \"args\": {"
1170  << " \"viewRef\":"
1171  << mock_view_ref_pair.view_ref.reference.get() << " }"
1172  << "}";
1173 
1174  // Dispatch the plaform message request.
1175  auto response = FakePlatformMessageResponse::Create();
1176  base_view->HandlePlatformMessage(
1177  response->WithMessage("flutter/platform_views", message.str()));
1178  RunLoopUntilIdle();
1179 
1180  response->ExpectCompleted("[0]");
1181  EXPECT_TRUE(focuser.request_focus_called());
1182 }
1183 
1184 // This test makes sure that the PlatformView correctly replies with an error
1185 // response when a View.focus.request call fails.
1186 TEST_F(PlatformViewTests, RequestFocusFailTest) {
1187  MockPlatformViewDelegate delegate;
1188  flutter::TaskRunners task_runners =
1189  flutter::TaskRunners("test_runners", nullptr, nullptr, nullptr, nullptr);
1190 
1191  FakeFocuser focuser;
1192  focuser.fail_request_focus();
1193  fidl::BindingSet<fuchsia::ui::views::Focuser> focuser_bindings;
1194  auto focuser_handle = focuser_bindings.AddBinding(&focuser);
1195 
1197  PlatformViewBuilder(delegate, std::move(task_runners))
1198  .SetFocuser(std::move(focuser_handle))
1199  .Build();
1200 
1201  // Cast platform_view to its base view so we can have access to the public
1202  // "HandlePlatformMessage" function.
1203  auto base_view = static_cast<flutter::PlatformView*>(&platform_view);
1204  EXPECT_TRUE(base_view);
1205 
1206  // This "Mock" ViewRef serves as the target for the RequestFocus operation.
1207  auto mock_view_ref_pair = scenic::ViewRefPair::New();
1208 
1209  // JSON for the message to be passed into the PlatformView.
1210  std::ostringstream message;
1211  message << "{"
1212  << " \"method\":\"View.focus.request\","
1213  << " \"args\": {"
1214  << " \"viewRef\":"
1215  << mock_view_ref_pair.view_ref.reference.get() << " }"
1216  << "}";
1217 
1218  // Dispatch the plaform message request.
1219  auto response = FakePlatformMessageResponse::Create();
1220  base_view->HandlePlatformMessage(
1221  response->WithMessage("flutter/platform_views", message.str()));
1222  RunLoopUntilIdle();
1223 
1224  response->ExpectCompleted(
1225  "[" +
1226  std::to_string(
1227  static_cast<std::underlying_type_t<fuchsia::ui::views::Error>>(
1228  fuchsia::ui::views::Error::DENIED)) +
1229  "]");
1230  EXPECT_TRUE(focuser.request_focus_called());
1231 }
1232 
1233 // Makes sure that OnKeyEvent is dispatched as a platform message.
1235  struct EventFlow {
1236  fuchsia::ui::input3::KeyEvent event;
1237  fuchsia::ui::input3::KeyEventStatus expected_key_event_status;
1238  std::string expected_platform_message;
1239  };
1240 
1241  MockPlatformViewDelegate delegate;
1242  flutter::TaskRunners task_runners =
1243  flutter::TaskRunners("test_runners", nullptr, nullptr, nullptr, nullptr);
1244 
1245  fidl::InterfaceHandle<fuchsia::ui::input3::Keyboard> keyboard_service;
1246  MockKeyboard keyboard(keyboard_service.NewRequest());
1247 
1249  PlatformViewBuilder(delegate, std::move(task_runners))
1250  .SetKeyboard(std::move(keyboard_service))
1251  .Build();
1252  RunLoopUntilIdle();
1253 
1254  std::vector<EventFlow> events;
1255  // Press A. Get 'a'.
1256  events.emplace_back(EventFlow{
1257  MakeEvent(fuchsia::ui::input3::KeyEventType::PRESSED, std::nullopt,
1258  fuchsia::input::Key::A),
1259  fuchsia::ui::input3::KeyEventStatus::HANDLED,
1260  R"({"type":"keydown","keymap":"fuchsia","hidUsage":4,"codePoint":97,"modifiers":0})",
1261  });
1262  // Release A. Get 'a' release.
1263  events.emplace_back(EventFlow{
1264  MakeEvent(fuchsia::ui::input3::KeyEventType::RELEASED, std::nullopt,
1265  fuchsia::input::Key::A),
1266  fuchsia::ui::input3::KeyEventStatus::HANDLED,
1267  R"({"type":"keyup","keymap":"fuchsia","hidUsage":4,"codePoint":97,"modifiers":0})",
1268  });
1269  // Press CAPS_LOCK. Modifier now active.
1270  events.emplace_back(EventFlow{
1271  MakeEvent(fuchsia::ui::input3::KeyEventType::PRESSED,
1272  fuchsia::ui::input3::Modifiers::CAPS_LOCK,
1273  fuchsia::input::Key::CAPS_LOCK),
1274  fuchsia::ui::input3::KeyEventStatus::HANDLED,
1275  R"({"type":"keydown","keymap":"fuchsia","hidUsage":57,"codePoint":0,"modifiers":1})",
1276  });
1277  // Pres A. Get 'A'.
1278  events.emplace_back(EventFlow{
1279  MakeEvent(fuchsia::ui::input3::KeyEventType::PRESSED, std::nullopt,
1280  fuchsia::input::Key::A),
1281  fuchsia::ui::input3::KeyEventStatus::HANDLED,
1282  R"({"type":"keydown","keymap":"fuchsia","hidUsage":4,"codePoint":65,"modifiers":1})",
1283  });
1284  // Release CAPS_LOCK.
1285  events.emplace_back(EventFlow{
1286  MakeEvent(fuchsia::ui::input3::KeyEventType::RELEASED,
1287  fuchsia::ui::input3::Modifiers::CAPS_LOCK,
1288  fuchsia::input::Key::CAPS_LOCK),
1289  fuchsia::ui::input3::KeyEventStatus::HANDLED,
1290  R"({"type":"keyup","keymap":"fuchsia","hidUsage":57,"codePoint":0,"modifiers":1})",
1291  });
1292  // Press A again. This time get 'A'.
1293  // CAPS_LOCK is latched active even if it was just released.
1294  events.emplace_back(EventFlow{
1295  MakeEvent(fuchsia::ui::input3::KeyEventType::PRESSED, std::nullopt,
1296  fuchsia::input::Key::A),
1297  fuchsia::ui::input3::KeyEventStatus::HANDLED,
1298  R"({"type":"keydown","keymap":"fuchsia","hidUsage":4,"codePoint":65,"modifiers":1})",
1299  });
1300 
1301  for (const auto& event : events) {
1302  fuchsia::ui::input3::KeyEvent e;
1303  event.event.Clone(&e);
1304  fuchsia::ui::input3::KeyEventStatus key_event_status{0u};
1305  keyboard.listener_->OnKeyEvent(
1306  std::move(e),
1307  [&key_event_status](fuchsia::ui::input3::KeyEventStatus status) {
1308  key_event_status = status;
1309  });
1310  RunLoopUntilIdle();
1311 
1312  ASSERT_NOTNULL(delegate.message());
1313  EXPECT_EQ(event.expected_platform_message,
1314  ToString(delegate.message()->data()));
1315  EXPECT_EQ(event.expected_key_event_status, key_event_status);
1316  }
1317 }
1318 
1320  MockPlatformViewDelegate delegate;
1321  flutter::TaskRunners task_runners =
1322  flutter::TaskRunners("test_runners", nullptr, nullptr, nullptr, nullptr);
1323 
1324  uint64_t width = 200;
1325  uint64_t height = 100;
1326  std::vector<std::string> shaders = {"foo.skp", "bar.skp", "baz.skp"};
1327 
1328  OnShaderWarmup on_shader_warmup =
1329  [&](const std::vector<std::string>& shaders_in,
1330  std::function<void(uint32_t)> completion_callback, uint64_t width_in,
1331  uint64_t height_in) {
1332  ASSERT_EQ(shaders.size(), shaders_in.size());
1333  for (size_t i = 0; i < shaders_in.size(); i++) {
1334  ASSERT_EQ(shaders[i], shaders_in[i]);
1335  }
1336  ASSERT_EQ(width, width_in);
1337  ASSERT_EQ(height, height_in);
1338 
1339  completion_callback(shaders_in.size());
1340  };
1341 
1343  PlatformViewBuilder(delegate, std::move(task_runners))
1344  .SetShaderWarmupCallback(on_shader_warmup)
1345  .Build();
1346 
1347  std::ostringstream shaders_array_ostream;
1348  shaders_array_ostream << "[ ";
1349  for (auto it = shaders.begin(); it != shaders.end(); ++it) {
1350  shaders_array_ostream << "\"" << *it << "\"";
1351  if (std::next(it) != shaders.end()) {
1352  shaders_array_ostream << ", ";
1353  }
1354  }
1355  shaders_array_ostream << "]";
1356 
1357  std::string shaders_array_string = shaders_array_ostream.str();
1358 
1359  // Create initial view for testing.
1360  std::ostringstream warmup_shaders_ostream;
1361  warmup_shaders_ostream << "{"
1362  << " \"method\":\"WarmupSkps\","
1363  << " \"args\":{"
1364  << " \"shaders\":" << shaders_array_string << ","
1365  << " \"width\":" << width << ","
1366  << " \"height\":" << height << " }"
1367  << "}\n";
1368  std::string warmup_shaders_string = warmup_shaders_ostream.str();
1369 
1371  new TestPlatformMessageResponse);
1372  static_cast<flutter::PlatformView*>(&platform_view)
1373  ->HandlePlatformMessage(std::make_unique<flutter::PlatformMessage>(
1374  "fuchsia/shader_warmup",
1375  fml::MallocMapping::Copy(warmup_shaders_string.c_str(),
1376  warmup_shaders_string.size()),
1377  response));
1378  RunLoopUntilIdle();
1379  ASSERT_TRUE(response->is_complete());
1380 
1381  std::ostringstream expected_result_ostream;
1382  expected_result_ostream << "[" << shaders.size() << "]";
1383  std::string expected_result_string = expected_result_ostream.str();
1384  EXPECT_EQ(expected_result_string, response->result_string);
1385 }
1386 
1387 TEST_F(PlatformViewTests, TouchSourceLogicalToPhysicalConversion) {
1388  constexpr std::array<std::array<float, 2>, 2> kRect = {{{0, 0}, {20, 20}}};
1389  constexpr std::array<float, 9> kIdentity = {1, 0, 0, 0, 1, 0, 0, 0, 1};
1390  constexpr fuchsia::ui::pointer::TouchInteractionId kIxnOne = {
1391  .device_id = 0u, .pointer_id = 1u, .interaction_id = 2u};
1392  constexpr float valid_pixel_ratio = 2.f;
1393 
1394  MockPlatformViewDelegate delegate;
1395  flutter::TaskRunners task_runners("test_runners", nullptr, nullptr, nullptr,
1396  nullptr);
1397 
1398  fuchsia::ui::scenic::SessionListenerPtr session_listener;
1399  FakeTouchSource touch_server;
1400  fidl::BindingSet<fuchsia::ui::pointer::TouchSource> touch_bindings;
1401  auto touch_handle = touch_bindings.AddBinding(&touch_server);
1403  PlatformViewBuilder(delegate, std::move(task_runners))
1404  .SetSessionListenerRequest(session_listener.NewRequest())
1405  .SetTouchSource(std::move(touch_handle))
1406  .Build();
1407  RunLoopUntilIdle();
1408  EXPECT_EQ(delegate.pointer_packets().size(), 0u);
1409 
1410  // Set logical-to-physical metrics and logical view size
1411  std::vector<fuchsia::ui::scenic::Event> scenic_events;
1412  scenic_events.emplace_back(fuchsia::ui::scenic::Event::WithGfx(
1413  fuchsia::ui::gfx::Event::WithMetrics(fuchsia::ui::gfx::MetricsEvent{
1414  .node_id = 0,
1415  .metrics =
1416  fuchsia::ui::gfx::Metrics{
1417  .scale_x = valid_pixel_ratio,
1418  .scale_y = valid_pixel_ratio,
1419  .scale_z = valid_pixel_ratio,
1420  },
1421  })));
1422  scenic_events.emplace_back(
1423  fuchsia::ui::scenic::Event::WithGfx(
1424  fuchsia::ui::gfx::Event::WithViewPropertiesChanged(
1425  fuchsia::ui::gfx::ViewPropertiesChangedEvent{
1426  .view_id = 0,
1427  .properties =
1428  fuchsia::ui::gfx::ViewProperties{
1429  .bounding_box =
1430  fuchsia::ui::gfx::BoundingBox{
1431  .min =
1432  fuchsia::ui::gfx::vec3{
1433  .x = 0.f,
1434  .y = 0.f,
1435  .z = 0.f,
1436  },
1437  .max =
1438  fuchsia::ui::gfx::vec3{
1439  .x = 20.f,
1440  .y = 20.f,
1441  .z = 20.f,
1442  },
1443  },
1444  },
1445  })));
1446  session_listener->OnScenicEvent(std::move(scenic_events));
1447  RunLoopUntilIdle();
1448  EXPECT_EQ(delegate.metrics(), flutter::ViewportMetrics(2.f, 40.f, 40.f, -1));
1449 
1450  // Inject
1451  std::vector<fuchsia::ui::pointer::TouchEvent> events =
1453  .AddTime(/* in nanoseconds */ 1111789u)
1454  .AddViewParameters(kRect, kRect, kIdentity)
1455  .AddSample(kIxnOne, fuchsia::ui::pointer::EventPhase::ADD,
1456  {10.f, 10.f})
1457  .AddResult(
1458  {.interaction = kIxnOne,
1459  .status = fuchsia::ui::pointer::TouchInteractionStatus::GRANTED})
1460  .BuildAsVector();
1461  touch_server.ScheduleCallback(std::move(events));
1462  RunLoopUntilIdle();
1463 
1464  // Unpack
1465  std::vector<std::unique_ptr<flutter::PointerDataPacket>> packets =
1466  delegate.TakePointerDataPackets();
1467  ASSERT_EQ(packets.size(), 1u);
1468  std::vector<flutter::PointerData> flutter_events;
1469  UnpackPointerPacket(flutter_events, std::move(packets[0]));
1470 
1471  // Examine phases
1472  ASSERT_EQ(flutter_events.size(), 2u);
1473  EXPECT_EQ(flutter_events[0].change, flutter::PointerData::Change::kAdd);
1474  EXPECT_EQ(flutter_events[1].change, flutter::PointerData::Change::kDown);
1475 
1476  // Examine coordinates
1477  // With metrics defined, observe metrics ratio applied.
1478  EXPECT_EQ(flutter_events[0].physical_x, 20.f);
1479  EXPECT_EQ(flutter_events[0].physical_y, 20.f);
1480  EXPECT_EQ(flutter_events[1].physical_x, 20.f);
1481  EXPECT_EQ(flutter_events[1].physical_y, 20.f);
1482 }
1483 
1484 TEST_F(PlatformViewTests, DownPointerNumericNudge) {
1485  constexpr float kSmallDiscrepancy = -0.00003f;
1486 
1487  fuchsia::ui::scenic::SessionListenerPtr session_listener;
1488  MockPlatformViewDelegate delegate;
1489  flutter::TaskRunners task_runners("test_runners", nullptr, nullptr, nullptr,
1490  nullptr);
1492  PlatformViewBuilder(delegate, std::move(task_runners))
1493  .SetSessionListenerRequest(session_listener.NewRequest())
1494  .Build();
1495  RunLoopUntilIdle();
1496  EXPECT_EQ(delegate.pointer_packets().size(), 0u);
1497 
1498  std::vector<fuchsia::ui::scenic::Event> events;
1499  events.emplace_back(
1500  fuchsia::ui::scenic::Event::WithGfx(
1501  fuchsia::ui::gfx::Event::WithViewPropertiesChanged(
1502  fuchsia::ui::gfx::ViewPropertiesChangedEvent{
1503  .view_id = 0,
1504  .properties =
1505  fuchsia::ui::gfx::ViewProperties{
1506  .bounding_box =
1507  fuchsia::ui::gfx::BoundingBox{
1508  .min =
1509  fuchsia::ui::gfx::vec3{
1510  .x = 0.f,
1511  .y = 0.f,
1512  .z = 0.f,
1513  },
1514  .max =
1515  fuchsia::ui::gfx::vec3{
1516  .x = 100.f,
1517  .y = 100.f,
1518  .z = 100.f,
1519  },
1520  },
1521  },
1522  })));
1523  events.emplace_back(fuchsia::ui::scenic::Event::WithGfx(
1524  fuchsia::ui::gfx::Event::WithMetrics(fuchsia::ui::gfx::MetricsEvent{
1525  .node_id = 0,
1526  .metrics =
1527  fuchsia::ui::gfx::Metrics{
1528  .scale_x = 1.f,
1529  .scale_y = 1.f,
1530  .scale_z = 1.f,
1531  },
1532  })));
1533  events.emplace_back(fuchsia::ui::scenic::Event::WithInput(
1534  fuchsia::ui::input::InputEvent::WithPointer(
1535  fuchsia::ui::input::PointerEvent{
1536  .event_time = 1111,
1537  .device_id = 2222,
1538  .pointer_id = 3333,
1539  .type = fuchsia::ui::input::PointerEventType::TOUCH,
1540  .phase = fuchsia::ui::input::PointerEventPhase::ADD,
1541  .x = 50.f,
1542  .y = kSmallDiscrepancy, // floating point inaccuracy
1543  .radius_major = 0.f,
1544  .radius_minor = 0.f,
1545  .buttons = 0u,
1546  })));
1547  events.emplace_back(fuchsia::ui::scenic::Event::WithInput(
1548  fuchsia::ui::input::InputEvent::WithPointer(
1549  fuchsia::ui::input::PointerEvent{
1550  .event_time = 1111,
1551  .device_id = 2222,
1552  .pointer_id = 3333,
1553  .type = fuchsia::ui::input::PointerEventType::TOUCH,
1554  .phase = fuchsia::ui::input::PointerEventPhase::DOWN,
1555  .x = 50.f,
1556  .y = kSmallDiscrepancy, // floating point inaccuracy
1557  .radius_major = 0.f,
1558  .radius_minor = 0.f,
1559  .buttons = 0u,
1560  })));
1561  events.emplace_back(fuchsia::ui::scenic::Event::WithInput(
1562  fuchsia::ui::input::InputEvent::WithPointer(
1563  fuchsia::ui::input::PointerEvent{
1564  .event_time = 1111,
1565  .device_id = 2222,
1566  .pointer_id = 3333,
1567  .type = fuchsia::ui::input::PointerEventType::TOUCH,
1568  .phase = fuchsia::ui::input::PointerEventPhase::MOVE,
1569  .x = 50.f,
1570  .y = kSmallDiscrepancy, // floating point inaccuracy
1571  .radius_major = 0.f,
1572  .radius_minor = 0.f,
1573  .buttons = 0u,
1574  })));
1575  session_listener->OnScenicEvent(std::move(events));
1576  RunLoopUntilIdle();
1577  ASSERT_EQ(delegate.pointer_packets().size(), 3u);
1578 
1579  // Embedder issues pointer data in a bytestream format, PointerDataPacket.
1580  // Use this handy utility to recover data as a C struct, PointerData.
1581  std::vector<std::unique_ptr<flutter::PointerDataPacket>> packets =
1582  delegate.TakePointerDataPackets();
1583  std::vector<flutter::PointerData> add, down, move;
1584  UnpackPointerPacket(add, std::move(packets[0]));
1585  UnpackPointerPacket(down, std::move(packets[1]));
1586  UnpackPointerPacket(move, std::move(packets[2]));
1587 
1588  EXPECT_EQ(add[0].physical_x, 50.f);
1589  EXPECT_EQ(add[0].physical_y, kSmallDiscrepancy);
1590  EXPECT_EQ(down[0].physical_x, 50.f);
1591  EXPECT_EQ(down[0].physical_y, 0.f); // clamping happened
1592  EXPECT_EQ(move[0].physical_x, 50.f);
1593  EXPECT_EQ(move[0].physical_y, kSmallDiscrepancy);
1594 }
1595 
1596 } // namespace flutter_runner::testing
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
void fail_request_focus(bool fail_request=true)
Definition: focuser.h:18
G_BEGIN_DECLS FlValue * args
KeyCallType type
G_BEGIN_DECLS FlTexture * texture
static constexpr int kBytesPerField
Definition: pointer_data.h:14
constexpr fup_TouchIxnId kIxnOne
static constexpr int kPointerDataFieldCount
Definition: pointer_data.h:13
fml::RefPtr< fml::TaskRunner > CreateFMLTaskRunner(async_dispatcher_t *dispatcher)
fit::function< void(int64_t, GfxViewIdCallback)> OnDestroyGfxView
MockDelegate delegate_
constexpr std::array< std::array< float, 2 >, 2 > kRect
std::unique_ptr< flutter::PlatformViewIOS > platform_view
fit::function< std::unique_ptr< flutter::Surface >()> OnCreateSurface
Definition: platform_view.h:42
virtual size_t GetSize() const =0
TouchEventBuilder & AddViewParameters(std::array< std::array< float, 2 >, 2 > view, std::array< std::array< float, 2 >, 2 > viewport, std::array< float, 9 > transform)
Definition: ref_ptr.h:252
Dart_NativeFunction function
Definition: fuchsia.cc:51
int64_t texture_id
virtual const uint8_t * GetMapping() const =0
fuchsia::ui::views::ViewRef view_ref_
TEST_F(FocusDelegateTest, WatchCallbackSeries)
std::string result_string
Platform views are created by the shell on the platform task runner. Unless explicitly specified...
Definition: platform_view.h:46
void ScheduleCallback(std::vector< fuchsia::ui::pointer::TouchEvent > events)
Definition: touch_source.h:29
FlKeyEvent FlKeyResponderAsyncCallback callback
FlutterSemanticsFlag flags
fit::function< void(int64_t, SkRect, bool, bool)> OnUpdateView
Definition: platform_view.h:41
FlKeyEvent * event
static std::string ToString(uint64_t val)
AssetResolverType
Identifies the type of AssetResolver an instance is.
fit::function< void(flutter::SemanticsNodeUpdates, float)> OnSemanticsNodeUpdate
Definition: platform_view.h:44
std::function< void()> closure
Definition: closure.h:14
static sk_sp< DisplayList > Build(size_t g_index, size_t v_index)
Abstract Base Class that represents where we will be rendering content.
Definition: surface.h:18
static fml::RefPtr< FakePlatformMessageResponse > Create()
fit::function< void(int64_t, ViewCallback, GfxViewIdCallback, bool, bool)> OnCreateGfxView
SemanticsAction action
void UnpackPointerPacket(std::vector< PointerData > &output, std::unique_ptr< PointerDataPacket > packet)
static void LoadDartDeferredLibrary(JNIEnv *env, jobject obj, jlong shell_holder, jint jLoadingUnitId, jobjectArray jSearchPaths)
int32_t width
std::function< void()> ViewCallback
void NotifyCreated()
Used by embedders to notify the shell that a platform view has been created. This notification is use...
int32_t height
static MallocMapping Copy(const T *begin, const T *end)
Definition: mapping.h:147
fuchsia::ui::input3::KeyEvent MakeEvent(fuchsia::ui::input3::KeyEventType event_type, std::optional< fuchsia::ui::input3::Modifiers > modifiers, fuchsia::input::Key key)
std::shared_ptr< flutter::ExternalViewEmbedder > CreateExternalViewEmbedder() override
fuchsia::ui::input3::KeyboardListenerPtr listener_
const char * name
Definition: fuchsia.cc:50
#define FML_CHECK(condition)
Definition: logging.h:68
A Mapping like NonOwnedMapping, but uses Free as its release proc.
Definition: mapping.h:129
The context is used to render to a texture or renderbuffer.
constexpr std::array< float, 9 > kIdentity
static const uint8_t buffer[]
const fml::MallocMapping & data() const
TouchEventBuilder & AddTime(zx_time_t time)
TaskRunners task_runners_
TouchEventBuilder & AddSample(fuchsia::ui::pointer::TouchInteractionId id, fuchsia::ui::pointer::EventPhase phase, std::array< float, 2 > position)
fidl::Binding< fuchsia::ui::input3::Keyboard > keyboard_
static void SetViewportMetrics(JNIEnv *env, jobject jcaller, jlong shell_holder, jfloat devicePixelRatio, jint physicalWidth, jint physicalHeight, jint physicalPaddingTop, jint physicalPaddingRight, jint physicalPaddingBottom, jint physicalPaddingLeft, jint physicalViewInsetTop, jint physicalViewInsetRight, jint physicalViewInsetBottom, jint physicalViewInsetLeft, jint systemGestureInsetTop, jint systemGestureInsetRight, jint systemGestureInsetBottom, jint systemGestureInsetLeft, jint physicalTouchSlop)
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: macros.h:27
Used to forward events from the platform view to interested subsystems. This forwarding is done by th...
Definition: platform_view.h:53
GrContextOptions MakeDefaultContextOptions(ContextType type, std::optional< GrBackendApi > api)
Initializes GrContextOptions with values suitable for Flutter. The options can be further tweaked bef...
fit::function< void(bool)> OnEnableWireframe
Definition: platform_view.h:39
std::function< void(scenic::ResourceId)> GfxViewIdCallback
KeyEventType
Definition: key_data.h:19