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