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