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