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