Flutter Engine
The Flutter Engine
external_view_embedder_unittests.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 "flutter/shell/platform/fuchsia/flutter/external_view_embedder.h"
6
7#include <fuchsia/math/cpp/fidl.h>
8#include <fuchsia/sysmem/cpp/fidl.h>
9#include <fuchsia/ui/composition/cpp/fidl.h>
10#include <fuchsia/ui/views/cpp/fidl.h>
11#include <lib/async-testing/test_loop.h>
12#include <lib/zx/event.h>
13#include <lib/zx/eventpair.h>
14
15#include <cstdint>
16#include <functional>
17#include <memory>
18#include <optional>
19#include <string>
20#include <vector>
21
22#include "flutter/flow/embedded_views.h"
23#include "flutter/fml/logging.h"
24#include "flutter/fml/time/time_delta.h"
25#include "flutter/fml/time/time_point.h"
29
32#include "flutter/shell/platform/fuchsia/flutter/surface_producer.h"
33
34#include "gmock/gmock.h" // For EXPECT_THAT and matchers
35#include "gtest/gtest.h"
36
37using fuchsia::scenic::scheduling::FramePresentedInfo;
38using fuchsia::scenic::scheduling::FuturePresentationTimes;
39using fuchsia::scenic::scheduling::PresentReceivedInfo;
40using ::testing::_;
41using ::testing::AllOf;
43using ::testing::ElementsAre;
44using ::testing::Eq;
45using ::testing::Field;
46using ::testing::FieldsAre;
48using ::testing::Matcher;
49using ::testing::Pointee;
50using ::testing::Property;
51using ::testing::SizeIs;
52using ::testing::VariantWith;
53
55namespace {
56
57constexpr static fuchsia::ui::composition::BlendMode kFirstLayerBlendMode{
59constexpr static fuchsia::ui::composition::BlendMode kUpperLayerBlendMode{
60 fuchsia::ui::composition::BlendMode::SRC_OVER};
61constexpr static int64_t kImplicitViewId = 0;
62
63class FakeSurfaceProducerSurface : public SurfaceProducerSurface {
64 public:
65 explicit FakeSurfaceProducerSurface(
66 fidl::InterfaceRequest<fuchsia::sysmem::BufferCollectionToken>
67 sysmem_token_request,
68 fuchsia::ui::composition::BufferCollectionImportToken buffer_import_token,
69 const SkISize& size)
70 : sysmem_token_request_(std::move(sysmem_token_request)),
71 buffer_import_token_(std::move(buffer_import_token)),
73 zx_status_t acquire_status = zx::event::create(0, &acquire_fence_);
74 if (acquire_status != ZX_OK) {
76 << "FakeSurfaceProducerSurface: Failed to create acquire event";
77 }
78 zx_status_t release_status = zx::event::create(0, &release_fence_);
79 if (release_status != ZX_OK) {
81 << "FakeSurfaceProducerSurface: Failed to create release event";
82 }
83 }
84 ~FakeSurfaceProducerSurface() override {}
85
86 bool IsValid() const override { return true; }
87
88 SkISize GetSize() const override {
89 return SkISize::Make(surface_->width(), surface_->height());
90 }
91
92 void SetImageId(uint32_t image_id) override { image_id_ = image_id; }
93 uint32_t GetImageId() override { return image_id_; }
94
95 sk_sp<SkSurface> GetSkiaSurface() const override { return surface_; }
96
97 fuchsia::ui::composition::BufferCollectionImportToken
98 GetBufferCollectionImportToken() override {
99 return std::move(buffer_import_token_);
100 }
101
102 zx::event GetAcquireFence() override {
103 zx::event fence;
104 acquire_fence_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
105 return fence;
106 }
107
108 zx::event GetReleaseFence() override {
109 zx::event fence;
110 release_fence_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
111 return fence;
112 }
113
114 void SetReleaseImageCallback(
115 ReleaseImageCallback release_image_callback) override {}
116
117 size_t AdvanceAndGetAge() override { return 0; }
118 bool FlushSessionAcquireAndReleaseEvents() override { return true; }
119 void SignalWritesFinished(
120 const std::function<void(void)>& on_writes_committed) override {}
121
122 private:
123 fidl::InterfaceRequest<fuchsia::sysmem::BufferCollectionToken>
124 sysmem_token_request_;
125 fuchsia::ui::composition::BufferCollectionImportToken buffer_import_token_;
126 zx::event acquire_fence_;
127 zx::event release_fence_;
128
130 uint32_t image_id_{0};
131};
132
133class FakeSurfaceProducer : public SurfaceProducer {
134 public:
135 explicit FakeSurfaceProducer(
136 fuchsia::ui::composition::AllocatorHandle flatland_allocator)
137 : flatland_allocator_(flatland_allocator.Bind()) {}
138 ~FakeSurfaceProducer() override = default;
139
140 // |SurfaceProducer|
141 GrDirectContext* gr_context() const override { return nullptr; }
142
143 // |SurfaceProducer|
144 std::unique_ptr<SurfaceProducerSurface> ProduceOffscreenSurface(
145 const SkISize& size) override {
146 return nullptr;
147 }
148
149 // |SurfaceProducer|
150 std::unique_ptr<SurfaceProducerSurface> ProduceSurface(
151 const SkISize& size) override {
152 auto [buffer_export_token, buffer_import_token] =
154 fuchsia::sysmem::BufferCollectionTokenHandle sysmem_token;
155 auto sysmem_token_request = sysmem_token.NewRequest();
156
157 fuchsia::ui::composition::RegisterBufferCollectionArgs
158 buffer_collection_args;
159 buffer_collection_args.set_export_token(std::move(buffer_export_token));
160 buffer_collection_args.set_buffer_collection_token(std::move(sysmem_token));
161 buffer_collection_args.set_usage(
162 fuchsia::ui::composition::RegisterBufferCollectionUsage::DEFAULT);
163 flatland_allocator_->RegisterBufferCollection(
164 std::move(buffer_collection_args),
165 [](fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result
166 result) {
167 if (result.is_err()) {
168 FAIL()
169 << "fuhsia::ui::composition::RegisterBufferCollection error: "
170 << static_cast<uint32_t>(result.err());
171 }
172 });
173
174 return std::make_unique<FakeSurfaceProducerSurface>(
175 std::move(sysmem_token_request), std::move(buffer_import_token), size);
176 }
177
178 // |SurfaceProducer|
179 void SubmitSurfaces(
180 std::vector<std::unique_ptr<SurfaceProducerSurface>> surfaces) override {}
181
182 fuchsia::ui::composition::AllocatorPtr flatland_allocator_;
183};
184
185Matcher<fuchsia::ui::composition::ImageProperties> IsImageProperties(
186 const fuchsia::math::SizeU& size) {
187 return AllOf(
188 Property("has_size", &fuchsia::ui::composition::ImageProperties::has_size,
189 true),
191}
192
193Matcher<fuchsia::ui::composition::ViewportProperties> IsViewportProperties(
194 const fuchsia::math::SizeU& logical_size,
195 const fuchsia::math::Inset& inset) {
196 return AllOf(
197 Property("has_logical_size",
198 &fuchsia::ui::composition::ViewportProperties::has_logical_size,
199 true),
200 Property("logical_size",
201 &fuchsia::ui::composition::ViewportProperties::logical_size,
202 logical_size),
203 Property("has_inset",
204 &fuchsia::ui::composition::ViewportProperties::has_inset, true),
206 inset));
207}
208
209Matcher<fuchsia::ui::composition::HitRegion> IsHitRegion(
210 const float x,
211 const float y,
212 const float width,
213 const float height,
214 const fuchsia::ui::composition::HitTestInteraction hit_test) {
215 return FieldsAre(FieldsAre(x, y, width, height), hit_test);
216}
217
218Matcher<FakeGraph> IsEmptyGraph() {
219 return FieldsAre(IsEmpty(), IsEmpty(), Eq(nullptr), Eq(std::nullopt));
220}
221
222Matcher<FakeGraph> IsFlutterGraph(
223 const fuchsia::ui::composition::ParentViewportWatcherPtr&
224 parent_viewport_watcher,
225 const fuchsia::ui::views::ViewportCreationToken& viewport_creation_token,
226 const fuchsia::ui::views::ViewRef& view_ref,
227 std::vector<Matcher<std::shared_ptr<FakeTransform>>> layer_matchers = {},
228 fuchsia::math::VecF scale = FakeTransform::kDefaultScale) {
229 auto viewport_token_koids = GetKoids(viewport_creation_token);
230 auto view_ref_koids = GetKoids(view_ref);
231 auto watcher_koids = GetKoids(parent_viewport_watcher);
232
233 return FieldsAre(
234 /*content_map*/ _, /*transform_map*/ _,
235 Pointee(FieldsAre(
238 /*clip_bounds*/ _, FakeTransform::kDefaultOpacity,
239 /*children*/ ElementsAreArray(layer_matchers),
240 /*content*/ Eq(nullptr), /*hit_regions*/ _)),
241 Eq(FakeView{
242 .view_token = viewport_token_koids.second,
243 .view_ref = view_ref_koids.first,
244 .view_ref_control = view_ref_koids.second,
245 .view_ref_focused = ZX_KOID_INVALID,
246 .focuser = ZX_KOID_INVALID,
247 .touch_source = ZX_KOID_INVALID,
248 .mouse_source = ZX_KOID_INVALID,
249 .parent_viewport_watcher = watcher_koids.second,
250 }));
251}
252
253Matcher<std::shared_ptr<FakeTransform>> IsImageLayer(
254 const fuchsia::math::SizeU& layer_size,
256 std::vector<Matcher<fuchsia::ui::composition::HitRegion>>
257 hit_region_matchers) {
258 return Pointee(FieldsAre(
261 /*clip_bounds*/ _, FakeTransform::kDefaultOpacity,
262 /*children*/ IsEmpty(),
263 /*content*/
264 Pointee(VariantWith<FakeImage>(FieldsAre(
265 /*id*/ _, IsImageProperties(layer_size),
267 FakeImage::kDefaultOpacity, blend_mode,
268 /*buffer_import_token*/ _, /*vmo_index*/ 0))),
269 /* hit_regions*/ ElementsAreArray(hit_region_matchers)));
270}
271
272Matcher<std::shared_ptr<FakeTransform>> IsViewportLayer(
273 const fuchsia::ui::views::ViewCreationToken& view_token,
274 const fuchsia::math::SizeU& view_logical_size,
275 const fuchsia::math::Inset& view_inset,
276 const fuchsia::math::Vec& view_translation,
277 const fuchsia::math::VecF& view_scale,
278 const float view_opacity) {
279 return Pointee(FieldsAre(
280 /* id */ _, view_translation, view_scale,
281 FakeTransform::kDefaultOrientation, /*clip_bounds*/ _, view_opacity,
282 /*children*/ IsEmpty(),
283 /*content*/
284 Pointee(VariantWith<FakeViewport>(FieldsAre(
285 /* id */ _, IsViewportProperties(view_logical_size, view_inset),
286 /* viewport_token */ GetKoids(view_token).second,
287 /* child_view_watcher */ _))),
288 /*hit_regions*/ _));
289}
290
291Matcher<std::shared_ptr<FakeTransform>> IsClipTransformLayer(
292 const fuchsia::math::Vec& transform_translation,
293 const fuchsia::math::VecF& transform_scale,
294 std::optional<fuchsia::math::Rect> clip_bounds,
295 Matcher<std::shared_ptr<FakeTransform>> viewport_matcher) {
296 return Pointee(FieldsAre(
297 /* id */ _, transform_translation, transform_scale,
298 FakeTransform::kDefaultOrientation, /*clip_bounds*/ clip_bounds,
300 /*children*/ ElementsAre(viewport_matcher),
301 /*content*/ _,
302 /*hit_regions*/ _));
303}
304
305Matcher<std::shared_ptr<FakeTransform>> IsInputShield() {
306 return Pointee(AllOf(
307 // Must not clip the hit region.
308 Field("clip_bounds", &FakeTransform::clip_bounds, Eq(std::nullopt)),
309 // Hit region must be "infinite".
310 Field("hit_regions", &FakeTransform::hit_regions,
312}
313
314fuchsia::ui::composition::OnNextFrameBeginValues WithPresentCredits(
315 uint32_t additional_present_credits) {
316 fuchsia::ui::composition::OnNextFrameBeginValues values;
317 values.set_additional_present_credits(additional_present_credits);
318 fuchsia::scenic::scheduling::PresentationInfo info_1;
319 info_1.set_presentation_time(123);
320 std::vector<fuchsia::scenic::scheduling::PresentationInfo> infos;
321 infos.push_back(std::move(info_1));
322 values.set_future_presentation_infos(std::move(infos));
323 return values;
324}
325
326void DrawSimpleFrame(ExternalViewEmbedder& external_view_embedder,
327 SkISize frame_size,
328 float frame_dpr,
329 std::function<void(flutter::DlCanvas*)> draw_callback) {
330 external_view_embedder.BeginFrame(nullptr, nullptr);
331 external_view_embedder.PrepareFlutterView(frame_size, frame_dpr);
332 {
333 flutter::DlCanvas* root_canvas = external_view_embedder.GetRootCanvas();
334 external_view_embedder.PostPrerollAction(nullptr);
335 draw_callback(root_canvas);
336 }
337 external_view_embedder.EndFrame(false, nullptr);
339 framebuffer_info.supports_readback = true;
340 external_view_embedder.SubmitFlutterView(
341 kImplicitViewId, nullptr, nullptr,
342 std::make_unique<flutter::SurfaceFrame>(
343 nullptr, std::move(framebuffer_info),
344 [](const flutter::SurfaceFrame& surface_frame,
345 flutter::DlCanvas* canvas) { return true; },
346 frame_size));
347}
348
349void DrawFrameWithView(
350 ExternalViewEmbedder& external_view_embedder,
351 SkISize frame_size,
352 float frame_dpr,
353 int view_id,
354 flutter::EmbeddedViewParams& view_params,
355 std::function<void(flutter::DlCanvas*)> background_draw_callback,
356 std::function<void(flutter::DlCanvas*)> overlay_draw_callback) {
357 external_view_embedder.BeginFrame(nullptr, nullptr);
358 external_view_embedder.PrepareFlutterView(frame_size, frame_dpr);
359 {
360 flutter::DlCanvas* root_canvas = external_view_embedder.GetRootCanvas();
361 external_view_embedder.PrerollCompositeEmbeddedView(
362 view_id, std::make_unique<flutter::EmbeddedViewParams>(view_params));
363 external_view_embedder.PostPrerollAction(nullptr);
364 background_draw_callback(root_canvas);
365 flutter::DlCanvas* overlay_canvas =
366 external_view_embedder.CompositeEmbeddedView(view_id);
367 overlay_draw_callback(overlay_canvas);
368 }
369 external_view_embedder.EndFrame(false, nullptr);
371 framebuffer_info.supports_readback = true;
372 external_view_embedder.SubmitFlutterView(
373 kImplicitViewId, nullptr, nullptr,
374 std::make_unique<flutter::SurfaceFrame>(
375 nullptr, std::move(framebuffer_info),
376 [](const flutter::SurfaceFrame& surface_frame,
377 flutter::DlCanvas* canvas) { return true; },
378 frame_size));
379}
380
381}; // namespace
382
383class ExternalViewEmbedderTest : public ::testing::Test {
384 protected:
386 : session_subloop_(loop_.StartNewLoop()),
387 flatland_connection_(CreateFlatlandConnection()),
388 fake_surface_producer_(
389 std::make_shared<FakeSurfaceProducer>(CreateFlatlandAllocator())) {}
390 ~ExternalViewEmbedderTest() override = default;
391
392 async::TestLoop& loop() { return loop_; }
393
394 std::shared_ptr<FakeSurfaceProducer> fake_surface_producer() {
395 return fake_surface_producer_;
396 }
397
398 FakeFlatland& fake_flatland() { return fake_flatland_; }
399
400 std::shared_ptr<FlatlandConnection> flatland_connection() {
401 return flatland_connection_;
402 }
403
404 private:
405 fuchsia::ui::composition::AllocatorHandle CreateFlatlandAllocator() {
406 FML_CHECK(!fake_flatland_.is_allocator_connected());
407 fuchsia::ui::composition::AllocatorHandle flatland_allocator =
408 fake_flatland_.ConnectAllocator(session_subloop_->dispatcher());
409
410 return flatland_allocator;
411 }
412
413 std::shared_ptr<FlatlandConnection> CreateFlatlandConnection() {
414 FML_CHECK(!fake_flatland_.is_flatland_connected());
415 fuchsia::ui::composition::FlatlandHandle flatland =
416 fake_flatland_.ConnectFlatland(session_subloop_->dispatcher());
417
418 const auto test_name =
419 ::testing::UnitTest::GetInstance()->current_test_info()->name();
420 return std::make_shared<FlatlandConnection>(
421 std::move(test_name), std::move(flatland),
422 /*error_callback=*/[] { FAIL(); }, /*ofpe_callback=*/[](auto...) {});
423 }
424
425 // Primary loop and subloop for the FakeFlatland instance to process its
426 // messages. The subloop allocates its own zx_port_t, allowing us to use a
427 // separate port for each end of the message channel, rather than sharing a
428 // single one. Dual ports allow messages and responses to be intermingled,
429 // which is how production code behaves; this improves test realism.
430 async::TestLoop loop_;
431 std::unique_ptr<async::LoopInterface> session_subloop_;
432
433 FakeFlatland fake_flatland_;
434
435 std::shared_ptr<FlatlandConnection> flatland_connection_;
436 std::shared_ptr<FakeSurfaceProducer> fake_surface_producer_;
437};
438
440 fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher;
441 fuchsia::ui::views::ViewportCreationToken viewport_creation_token;
442 fuchsia::ui::views::ViewCreationToken view_creation_token;
443 fuchsia::ui::views::ViewRef view_ref_clone;
444 auto view_creation_token_status = zx::channel::create(
445 0u, &viewport_creation_token.value, &view_creation_token.value);
446 ASSERT_EQ(view_creation_token_status, ZX_OK);
447
448 fuchsia::ui::views::ViewRefControl view_ref_control;
449 fuchsia::ui::views::ViewRef view_ref;
450 auto status = zx::eventpair::create(
451 /*options*/ 0u, &view_ref_control.reference, &view_ref.reference);
452 ASSERT_EQ(status, ZX_OK);
453 view_ref_control.reference.replace(
454 ZX_DEFAULT_EVENTPAIR_RIGHTS & (~ZX_RIGHT_DUPLICATE),
455 &view_ref_control.reference);
456 view_ref.reference.replace(ZX_RIGHTS_BASIC, &view_ref.reference);
457 view_ref.Clone(&view_ref_clone);
458
459 ExternalViewEmbedder external_view_embedder(
460 std::move(view_creation_token),
461 fuchsia::ui::views::ViewIdentityOnCreation{
462 .view_ref = std::move(view_ref),
463 .view_ref_control = std::move(view_ref_control),
464 },
465 fuchsia::ui::composition::ViewBoundProtocols{},
466 parent_viewport_watcher.NewRequest(), flatland_connection(),
467 fake_surface_producer());
468 EXPECT_THAT(fake_flatland().graph(), IsEmptyGraph());
469
470 // Pump the loop; the graph should still be empty because nothing called
471 // `Present` yet.
472 loop().RunUntilIdle();
473 EXPECT_THAT(fake_flatland().graph(), IsEmptyGraph());
474
475 // Pump the loop; the contents of the initial `Present` should be processed.
476 flatland_connection()->Present();
477 loop().RunUntilIdle();
478 EXPECT_THAT(fake_flatland().graph(),
479 IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
480 view_ref_clone));
481}
482
484 fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher;
485 fuchsia::ui::views::ViewportCreationToken viewport_creation_token;
486 fuchsia::ui::views::ViewCreationToken view_creation_token;
487 fuchsia::ui::views::ViewRef view_ref_clone;
488 auto view_creation_token_status = zx::channel::create(
489 0u, &viewport_creation_token.value, &view_creation_token.value);
490 ASSERT_EQ(view_creation_token_status, ZX_OK);
491
492 fuchsia::ui::views::ViewRefControl view_ref_control;
493 fuchsia::ui::views::ViewRef view_ref;
494 auto status = zx::eventpair::create(
495 /*options*/ 0u, &view_ref_control.reference, &view_ref.reference);
496 ASSERT_EQ(status, ZX_OK);
497 view_ref_control.reference.replace(
498 ZX_DEFAULT_EVENTPAIR_RIGHTS & (~ZX_RIGHT_DUPLICATE),
499 &view_ref_control.reference);
500 view_ref.reference.replace(ZX_RIGHTS_BASIC, &view_ref.reference);
501 view_ref.Clone(&view_ref_clone);
502
503 // Create the `ExternalViewEmbedder` and pump the message loop until
504 // the initial scene graph is setup.
505 ExternalViewEmbedder external_view_embedder(
506 std::move(view_creation_token),
507 fuchsia::ui::views::ViewIdentityOnCreation{
508 .view_ref = std::move(view_ref),
509 .view_ref_control = std::move(view_ref_control),
510 },
511 fuchsia::ui::composition::ViewBoundProtocols{},
512 parent_viewport_watcher.NewRequest(), flatland_connection(),
513 fake_surface_producer());
514 flatland_connection()->Present();
515 loop().RunUntilIdle();
516 fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
517 loop().RunUntilIdle();
518 EXPECT_THAT(fake_flatland().graph(),
519 IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
520 view_ref_clone));
521
522 // Draw the scene. The scene graph shouldn't change yet.
523 const SkISize frame_size_signed = SkISize::Make(512, 512);
524 const fuchsia::math::SizeU frame_size{
525 static_cast<uint32_t>(frame_size_signed.width()),
526 static_cast<uint32_t>(frame_size_signed.height())};
527 DrawSimpleFrame(external_view_embedder, frame_size_signed, 1.f,
528 [](flutter::DlCanvas* canvas) {
529 const SkISize layer_size = canvas->GetBaseLayerSize();
530 const SkSize canvas_size =
531 SkSize::Make(layer_size.width(), layer_size.height());
532 flutter::DlPaint rect_paint;
534 canvas->Translate(canvas_size.width() / 4.f,
535 canvas_size.height() / 2.f);
536 canvas->DrawRect(
537 SkRect::MakeWH(canvas_size.width() / 32.f,
538 canvas_size.height() / 32.f),
539 rect_paint);
540 });
541 EXPECT_THAT(fake_flatland().graph(),
542 IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
543 view_ref_clone));
544
545 // Pump the message loop. The scene updates should propagate to flatland.
546 loop().RunUntilIdle();
547
548 EXPECT_THAT(
549 fake_flatland().graph(),
550 IsFlutterGraph(
551 parent_viewport_watcher, viewport_creation_token, view_ref_clone,
552 /*layers*/
553 {IsImageLayer(
554 frame_size, kFirstLayerBlendMode,
555 {IsHitRegion(
556 /* x */ 128.f,
557 /* y */ 256.f,
558 /* width */ 16.f,
559 /* height */ 16.f,
560 /* hit_test */
561 fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
562}
563
565 fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher;
566 fuchsia::ui::views::ViewportCreationToken viewport_creation_token;
567 fuchsia::ui::views::ViewCreationToken view_creation_token;
568 fuchsia::ui::views::ViewRef view_ref_clone;
569 auto view_creation_token_status = zx::channel::create(
570 0u, &viewport_creation_token.value, &view_creation_token.value);
571 ASSERT_EQ(view_creation_token_status, ZX_OK);
572
573 fuchsia::ui::views::ViewRefControl view_ref_control;
574 fuchsia::ui::views::ViewRef view_ref;
575 auto status = zx::eventpair::create(
576 /*options*/ 0u, &view_ref_control.reference, &view_ref.reference);
577 ASSERT_EQ(status, ZX_OK);
578 view_ref_control.reference.replace(
579 ZX_DEFAULT_EVENTPAIR_RIGHTS & (~ZX_RIGHT_DUPLICATE),
580 &view_ref_control.reference);
581 view_ref.reference.replace(ZX_RIGHTS_BASIC, &view_ref.reference);
582 view_ref.Clone(&view_ref_clone);
583
584 // Create the `ExternalViewEmbedder` and pump the message loop until
585 // the initial scene graph is setup.
586 ExternalViewEmbedder external_view_embedder(
587 std::move(view_creation_token),
588 fuchsia::ui::views::ViewIdentityOnCreation{
589 .view_ref = std::move(view_ref),
590 .view_ref_control = std::move(view_ref_control),
591 },
592 fuchsia::ui::composition::ViewBoundProtocols{},
593 parent_viewport_watcher.NewRequest(), flatland_connection(),
594 fake_surface_producer());
595 flatland_connection()->Present();
596 loop().RunUntilIdle();
597 fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
598 loop().RunUntilIdle();
599 EXPECT_THAT(fake_flatland().graph(),
600 IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
601 view_ref_clone));
602
603 // Create the view before drawing the scene.
604 const SkSize child_view_size_signed = SkSize::Make(256.f, 512.f);
605 const fuchsia::math::SizeU child_view_size{
606 static_cast<uint32_t>(child_view_size_signed.width()),
607 static_cast<uint32_t>(child_view_size_signed.height())};
608 auto [child_view_token, child_viewport_token] = ViewTokenPair::New();
609 const uint32_t child_view_id = child_viewport_token.value.get();
610
611 const int kOpacity = 200;
612 const float kOpacityFloat = 200 / 255.0f;
613 const fuchsia::math::VecF kScale{3.0f, 4.0f};
614
615 auto matrix = SkMatrix::I();
616 matrix.setScaleX(kScale.x);
617 matrix.setScaleY(kScale.y);
618
619 auto mutators_stack = flutter::MutatorsStack();
620 mutators_stack.PushOpacity(kOpacity);
621 mutators_stack.PushTransform(matrix);
622
623 flutter::EmbeddedViewParams child_view_params(matrix, child_view_size_signed,
624 mutators_stack);
625 external_view_embedder.CreateView(
626 child_view_id, []() {},
627 [](fuchsia::ui::composition::ContentId,
628 fuchsia::ui::composition::ChildViewWatcherHandle) {});
629 const SkRect child_view_occlusion_hint = SkRect::MakeLTRB(1, 2, 3, 4);
630 const fuchsia::math::Inset child_view_inset{
631 static_cast<int32_t>(child_view_occlusion_hint.top()),
632 static_cast<int32_t>(child_view_occlusion_hint.right()),
633 static_cast<int32_t>(child_view_occlusion_hint.bottom()),
634 static_cast<int32_t>(child_view_occlusion_hint.left())};
635 external_view_embedder.SetViewProperties(
636 child_view_id, child_view_occlusion_hint, /*hit_testable=*/false,
637 /*focusable=*/false);
638
639 // We must take into account the effect of DPR on the view scale.
640 const float kDPR = 2.0f;
641 const float kInvDPR = 1.f / kDPR;
642
643 // Draw the scene. The scene graph shouldn't change yet.
644 const SkISize frame_size_signed = SkISize::Make(512, 512);
645 const fuchsia::math::SizeU frame_size{
646 static_cast<uint32_t>(frame_size_signed.width()),
647 static_cast<uint32_t>(frame_size_signed.height())};
648 DrawFrameWithView(
649 external_view_embedder, frame_size_signed, kDPR, child_view_id,
650 child_view_params,
651 [](flutter::DlCanvas* canvas) {
652 const SkISize layer_size = canvas->GetBaseLayerSize();
653 const SkSize canvas_size =
654 SkSize::Make(layer_size.width(), layer_size.height());
655 flutter::DlPaint rect_paint;
657 canvas->Translate(canvas_size.width() / 4.f,
658 canvas_size.height() / 2.f);
659 canvas->DrawRect(SkRect::MakeWH(canvas_size.width() / 32.f,
660 canvas_size.height() / 32.f),
661 rect_paint);
662 },
663 [](flutter::DlCanvas* canvas) {
664 const SkISize layer_size = canvas->GetBaseLayerSize();
665 const SkSize canvas_size =
666 SkSize::Make(layer_size.width(), layer_size.height());
667 flutter::DlPaint rect_paint;
668 rect_paint.setColor(flutter::DlColor::kRed());
669 canvas->Translate(canvas_size.width() * 3.f / 4.f,
670 canvas_size.height() / 2.f);
671 canvas->DrawRect(SkRect::MakeWH(canvas_size.width() / 32.f,
672 canvas_size.height() / 32.f),
673 rect_paint);
674 });
675 EXPECT_THAT(fake_flatland().graph(),
676 IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
677 view_ref_clone));
678
679 // Pump the message loop. The scene updates should propagate to flatland.
680 loop().RunUntilIdle();
681 fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
682 loop().RunUntilIdle();
683
684 EXPECT_THAT(
685 fake_flatland().graph(),
686 IsFlutterGraph(
687 parent_viewport_watcher, viewport_creation_token,
688 view_ref_clone, /*layers*/
689 {IsImageLayer(
690 frame_size, kFirstLayerBlendMode,
691 {IsHitRegion(
692 /* x */ 128.f,
693 /* y */ 256.f,
694 /* width */ 16.f,
695 /* height */ 16.f,
696 /* hit_test */
697 fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
698 IsViewportLayer(child_view_token, child_view_size, child_view_inset,
699 {0, 0}, kScale, kOpacityFloat),
700 IsImageLayer(
701 frame_size, kUpperLayerBlendMode,
702 {IsHitRegion(
703 /* x */ 384.f,
704 /* y */ 256.f,
705 /* width */ 16.f,
706 /* height */ 16.f,
707 /* hit_test */
708 fuchsia::ui::composition::HitTestInteraction::DEFAULT)})},
709 {kInvDPR, kInvDPR}));
710
711 // Destroy the view. The scene graph shouldn't change yet.
712 external_view_embedder.DestroyView(
713 child_view_id, [](fuchsia::ui::composition::ContentId) {});
714 EXPECT_THAT(
715 fake_flatland().graph(),
716 IsFlutterGraph(
717 parent_viewport_watcher, viewport_creation_token,
718 view_ref_clone, /*layers*/
719 {IsImageLayer(
720 frame_size, kFirstLayerBlendMode,
721 {IsHitRegion(
722 /* x */ 128.f,
723 /* y */ 256.f,
724 /* width */ 16.f,
725 /* height */ 16.f,
726 /* hit_test */
727 fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
728 IsViewportLayer(child_view_token, child_view_size, child_view_inset,
729 {0, 0}, kScale, kOpacityFloat),
730 IsImageLayer(
731 frame_size, kUpperLayerBlendMode,
732 {IsHitRegion(
733 /* x */ 384.f,
734 /* y */ 256.f,
735 /* width */ 16.f,
736 /* height */ 16.f,
737 /* hit_test */
738 fuchsia::ui::composition::HitTestInteraction::DEFAULT)})},
739 {kInvDPR, kInvDPR}));
740
741 // Draw another frame without the view. The scene graph shouldn't change yet.
742 DrawSimpleFrame(external_view_embedder, frame_size_signed, 1.f,
743 [](flutter::DlCanvas* canvas) {
744 const SkISize layer_size = canvas->GetBaseLayerSize();
745 const SkSize canvas_size =
746 SkSize::Make(layer_size.width(), layer_size.height());
747 flutter::DlPaint rect_paint;
749 canvas->Translate(canvas_size.width() / 4.f,
750 canvas_size.height() / 2.f);
751 canvas->DrawRect(
752 SkRect::MakeWH(canvas_size.width() / 32.f,
753 canvas_size.height() / 32.f),
754 rect_paint);
755 });
756 EXPECT_THAT(
757 fake_flatland().graph(),
758 IsFlutterGraph(
759 parent_viewport_watcher, viewport_creation_token,
760 view_ref_clone, /*layers*/
761 {IsImageLayer(
762 frame_size, kFirstLayerBlendMode,
763 {IsHitRegion(
764 /* x */ 128.f,
765 /* y */ 256.f,
766 /* width */ 16.f,
767 /* height */ 16.f,
768 /* hit_test */
769 fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
770 IsViewportLayer(child_view_token, child_view_size, child_view_inset,
771 {0, 0}, kScale, kOpacityFloat),
772 IsImageLayer(
773 frame_size, kUpperLayerBlendMode,
774 {IsHitRegion(
775 /* x */ 384.f,
776 /* y */ 256.f,
777 /* width */ 16.f,
778 /* height */ 16.f,
779 /* hit_test */
780 fuchsia::ui::composition::HitTestInteraction::DEFAULT)})},
781 {kInvDPR, kInvDPR}));
782
783 // Pump the message loop. The scene updates should propagate to flatland.
784 loop().RunUntilIdle();
785 EXPECT_THAT(
786 fake_flatland().graph(),
787 IsFlutterGraph(
788 parent_viewport_watcher, viewport_creation_token,
789 view_ref_clone, /*layers*/
790 {IsImageLayer(
791 frame_size, kFirstLayerBlendMode,
792 {IsHitRegion(
793 /* x */ 128.f,
794 /* y */ 256.f,
795 /* width */ 16.f,
796 /* height */ 16.f,
797 /* hit_test */
798 fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
799}
800
801TEST_F(ExternalViewEmbedderTest, SceneWithOneClippedView) {
802 fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher;
803 fuchsia::ui::views::ViewportCreationToken viewport_creation_token;
804 fuchsia::ui::views::ViewCreationToken view_creation_token;
805 fuchsia::ui::views::ViewRef view_ref_clone;
806 auto view_creation_token_status = zx::channel::create(
807 0u, &viewport_creation_token.value, &view_creation_token.value);
808 ASSERT_EQ(view_creation_token_status, ZX_OK);
809
810 fuchsia::ui::views::ViewRefControl view_ref_control;
811 fuchsia::ui::views::ViewRef view_ref;
812 auto status = zx::eventpair::create(
813 /*options*/ 0u, &view_ref_control.reference, &view_ref.reference);
814 ASSERT_EQ(status, ZX_OK);
815 view_ref_control.reference.replace(
816 ZX_DEFAULT_EVENTPAIR_RIGHTS & (~ZX_RIGHT_DUPLICATE),
817 &view_ref_control.reference);
818 view_ref.reference.replace(ZX_RIGHTS_BASIC, &view_ref.reference);
819 view_ref.Clone(&view_ref_clone);
820
821 // Create the `ExternalViewEmbedder` and pump the message loop until
822 // the initial scene graph is setup.
823 ExternalViewEmbedder external_view_embedder(
824 std::move(view_creation_token),
825 fuchsia::ui::views::ViewIdentityOnCreation{
826 .view_ref = std::move(view_ref),
827 .view_ref_control = std::move(view_ref_control),
828 },
829 fuchsia::ui::composition::ViewBoundProtocols{},
830 parent_viewport_watcher.NewRequest(), flatland_connection(),
831 fake_surface_producer());
832 flatland_connection()->Present();
833 loop().RunUntilIdle();
834 fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
835 loop().RunUntilIdle();
836 EXPECT_THAT(fake_flatland().graph(),
837 IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
838 view_ref_clone));
839
840 // Create the view before drawing the scene.
841 const SkSize child_view_size_signed = SkSize::Make(256.f, 512.f);
842 const fuchsia::math::SizeU child_view_size{
843 static_cast<uint32_t>(child_view_size_signed.width()),
844 static_cast<uint32_t>(child_view_size_signed.height())};
845 auto [child_view_token, child_viewport_token] = ViewTokenPair::New();
846 const uint32_t child_view_id = child_viewport_token.value.get();
847
848 const int kOpacity = 200;
849 const float kOpacityFloat = 200 / 255.0f;
850 const fuchsia::math::VecF kScale{3.0f, 4.0f};
851 const int kTranslateX = 10;
852 const int kTranslateY = 20;
853
854 auto matrix = SkMatrix::I();
855 matrix.setScaleX(kScale.x);
856 matrix.setScaleY(kScale.y);
857 matrix.setTranslateX(kTranslateX);
858 matrix.setTranslateY(kTranslateY);
859
861 SkRect::MakeXYWH(30, 40, child_view_size_signed.width() - 50,
862 child_view_size_signed.height() - 60);
863 fuchsia::math::Rect kClipInMathRect = {
864 static_cast<int32_t>(kClipRect.x()), static_cast<int32_t>(kClipRect.y()),
865 static_cast<int32_t>(kClipRect.width()),
866 static_cast<int32_t>(kClipRect.height())};
867
868 auto mutators_stack = flutter::MutatorsStack();
869 mutators_stack.PushOpacity(kOpacity);
870 mutators_stack.PushTransform(matrix);
871 mutators_stack.PushClipRect(kClipRect);
872
873 flutter::EmbeddedViewParams child_view_params(matrix, child_view_size_signed,
874 mutators_stack);
875 external_view_embedder.CreateView(
876 child_view_id, []() {},
877 [](fuchsia::ui::composition::ContentId,
878 fuchsia::ui::composition::ChildViewWatcherHandle) {});
879 const SkRect child_view_occlusion_hint = SkRect::MakeLTRB(1, 2, 3, 4);
880 const fuchsia::math::Inset child_view_inset{
881 static_cast<int32_t>(child_view_occlusion_hint.top()),
882 static_cast<int32_t>(child_view_occlusion_hint.right()),
883 static_cast<int32_t>(child_view_occlusion_hint.bottom()),
884 static_cast<int32_t>(child_view_occlusion_hint.left())};
885 external_view_embedder.SetViewProperties(
886 child_view_id, child_view_occlusion_hint, /*hit_testable=*/false,
887 /*focusable=*/false);
888
889 // We must take into account the effect of DPR on the view scale.
890 const float kDPR = 2.0f;
891 const float kInvDPR = 1.f / kDPR;
892
893 // Draw the scene. The scene graph shouldn't change yet.
894 const SkISize frame_size_signed = SkISize::Make(512, 512);
895 const fuchsia::math::SizeU frame_size{
896 static_cast<uint32_t>(frame_size_signed.width()),
897 static_cast<uint32_t>(frame_size_signed.height())};
898 DrawFrameWithView(
899 external_view_embedder, frame_size_signed, kDPR, child_view_id,
900 child_view_params,
901 [](flutter::DlCanvas* canvas) {
902 const SkISize layer_size = canvas->GetBaseLayerSize();
903 const SkSize canvas_size =
904 SkSize::Make(layer_size.width(), layer_size.height());
905 flutter::DlPaint rect_paint;
907 canvas->Translate(canvas_size.width() / 4.f,
908 canvas_size.height() / 2.f);
909 canvas->DrawRect(SkRect::MakeWH(canvas_size.width() / 32.f,
910 canvas_size.height() / 32.f),
911 rect_paint);
912 },
913 [](flutter::DlCanvas* canvas) {
914 const SkISize layer_size = canvas->GetBaseLayerSize();
915 const SkSize canvas_size =
916 SkSize::Make(layer_size.width(), layer_size.height());
917 flutter::DlPaint rect_paint;
918 rect_paint.setColor(flutter::DlColor::kRed());
919 canvas->Translate(canvas_size.width() * 3.f / 4.f,
920 canvas_size.height() / 2.f);
921 canvas->DrawRect(SkRect::MakeWH(canvas_size.width() / 32.f,
922 canvas_size.height() / 32.f),
923 rect_paint);
924 });
925 EXPECT_THAT(fake_flatland().graph(),
926 IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
927 view_ref_clone));
928
929 // Pump the message loop. The scene updates should propagate to flatland.
930 loop().RunUntilIdle();
931 fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
932 loop().RunUntilIdle();
933
934 EXPECT_THAT(
935 fake_flatland().graph(),
936 IsFlutterGraph(
937 parent_viewport_watcher, viewport_creation_token,
938 view_ref_clone, /*layers*/
939 {IsImageLayer(
940 frame_size, kFirstLayerBlendMode,
941 {IsHitRegion(
942 /* x */ 128.f,
943 /* y */ 256.f,
944 /* width */ 16.f,
945 /* height */ 16.f,
946 /* hit_test */
947 fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
948 IsClipTransformLayer(
949 {kTranslateX, kTranslateY}, kScale, kClipInMathRect,
950 IsViewportLayer(child_view_token, child_view_size,
951 child_view_inset, {0, 0},
952 FakeTransform::kDefaultScale, kOpacityFloat)),
953 IsImageLayer(
954 frame_size, kUpperLayerBlendMode,
955 {IsHitRegion(
956 /* x */ 384.f,
957 /* y */ 256.f,
958 /* width */ 16.f,
959 /* height */ 16.f,
960 /* hit_test */
961 fuchsia::ui::composition::HitTestInteraction::DEFAULT)})},
962 {kInvDPR, kInvDPR}));
963
964 // Draw another frame with view, but get rid of the clips this time. This
965 // should remove all ClipTransformLayer instances.
966 auto new_matrix = SkMatrix::I();
967 new_matrix.setScaleX(kScale.x);
968 new_matrix.setScaleY(kScale.y);
969 auto new_mutators_stack = flutter::MutatorsStack();
970 new_mutators_stack.PushOpacity(kOpacity);
971 new_mutators_stack.PushTransform(new_matrix);
972 flutter::EmbeddedViewParams new_child_view_params(
973 new_matrix, child_view_size_signed, new_mutators_stack);
974 DrawFrameWithView(
975 external_view_embedder, frame_size_signed, kDPR, child_view_id,
976 new_child_view_params,
977 [](flutter::DlCanvas* canvas) {
978 const SkISize layer_size = canvas->GetBaseLayerSize();
979 const SkSize canvas_size =
980 SkSize::Make(layer_size.width(), layer_size.height());
981 flutter::DlPaint rect_paint;
983 canvas->Translate(canvas_size.width() / 4.f,
984 canvas_size.height() / 2.f);
985 canvas->DrawRect(SkRect::MakeWH(canvas_size.width() / 32.f,
986 canvas_size.height() / 32.f),
987 rect_paint);
988 },
989 [](flutter::DlCanvas* canvas) {
990 const SkISize layer_size = canvas->GetBaseLayerSize();
991 const SkSize canvas_size =
992 SkSize::Make(layer_size.width(), layer_size.height());
993 flutter::DlPaint rect_paint;
994 rect_paint.setColor(flutter::DlColor::kRed());
995 canvas->Translate(canvas_size.width() * 3.f / 4.f,
996 canvas_size.height() / 2.f);
997 canvas->DrawRect(SkRect::MakeWH(canvas_size.width() / 32.f,
998 canvas_size.height() / 32.f),
999 rect_paint);
1000 });
1001 loop().RunUntilIdle();
1002 fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
1003 loop().RunUntilIdle();
1004 EXPECT_THAT(
1005 fake_flatland().graph(),
1006 IsFlutterGraph(
1007 parent_viewport_watcher, viewport_creation_token,
1008 view_ref_clone, /*layers*/
1009 {IsImageLayer(
1010 frame_size, kFirstLayerBlendMode,
1011 {IsHitRegion(
1012 /* x */ 128.f,
1013 /* y */ 256.f,
1014 /* width */ 16.f,
1015 /* height */ 16.f,
1016 /* hit_test */
1017 fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
1018 IsViewportLayer(child_view_token, child_view_size, child_view_inset,
1019 {0, 0}, kScale, kOpacityFloat),
1020 IsImageLayer(
1021 frame_size, kUpperLayerBlendMode,
1022 {IsHitRegion(
1023 /* x */ 384.f,
1024 /* y */ 256.f,
1025 /* width */ 16.f,
1026 /* height */ 16.f,
1027 /* hit_test */
1028 fuchsia::ui::composition::HitTestInteraction::DEFAULT)})},
1029 {kInvDPR, kInvDPR}));
1030
1031 // Destroy the view and draw another frame without the view.
1032 external_view_embedder.DestroyView(
1033 child_view_id, [](fuchsia::ui::composition::ContentId) {});
1034 DrawSimpleFrame(external_view_embedder, frame_size_signed, 1.f,
1035 [](flutter::DlCanvas* canvas) {
1036 const SkISize layer_size = canvas->GetBaseLayerSize();
1037 const SkSize canvas_size =
1038 SkSize::Make(layer_size.width(), layer_size.height());
1039 flutter::DlPaint rect_paint;
1040 rect_paint.setColor(flutter::DlColor::kGreen());
1041 canvas->Translate(canvas_size.width() / 4.f,
1042 canvas_size.height() / 2.f);
1043 canvas->DrawRect(
1044 SkRect::MakeWH(canvas_size.width() / 32.f,
1045 canvas_size.height() / 32.f),
1046 rect_paint);
1047 });
1048 loop().RunUntilIdle();
1049 EXPECT_THAT(
1050 fake_flatland().graph(),
1051 IsFlutterGraph(
1052 parent_viewport_watcher, viewport_creation_token,
1053 view_ref_clone, /*layers*/
1054 {IsImageLayer(
1055 frame_size, kFirstLayerBlendMode,
1056 {IsHitRegion(
1057 /* x */ 128.f,
1058 /* y */ 256.f,
1059 /* width */ 16.f,
1060 /* height */ 16.f,
1061 /* hit_test */
1062 fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
1063}
1064
1065TEST_F(ExternalViewEmbedderTest, SceneWithOneView_NoOverlay) {
1066 fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher;
1067 fuchsia::ui::views::ViewportCreationToken viewport_creation_token;
1068 fuchsia::ui::views::ViewCreationToken view_creation_token;
1069 fuchsia::ui::views::ViewRef view_ref_clone;
1070 auto view_creation_token_status = zx::channel::create(
1071 0u, &viewport_creation_token.value, &view_creation_token.value);
1072 ASSERT_EQ(view_creation_token_status, ZX_OK);
1073
1074 fuchsia::ui::views::ViewRefControl view_ref_control;
1075 fuchsia::ui::views::ViewRef view_ref;
1076 auto status = zx::eventpair::create(
1077 /*options*/ 0u, &view_ref_control.reference, &view_ref.reference);
1078 ASSERT_EQ(status, ZX_OK);
1079 view_ref_control.reference.replace(
1080 ZX_DEFAULT_EVENTPAIR_RIGHTS & (~ZX_RIGHT_DUPLICATE),
1081 &view_ref_control.reference);
1082 view_ref.reference.replace(ZX_RIGHTS_BASIC, &view_ref.reference);
1083 view_ref.Clone(&view_ref_clone);
1084
1085 // Create the `ExternalViewEmbedder` and pump the message loop until
1086 // the initial scene graph is setup.
1087 ExternalViewEmbedder external_view_embedder(
1088 std::move(view_creation_token),
1089 fuchsia::ui::views::ViewIdentityOnCreation{
1090 .view_ref = std::move(view_ref),
1091 .view_ref_control = std::move(view_ref_control),
1092 },
1093 fuchsia::ui::composition::ViewBoundProtocols{},
1094 parent_viewport_watcher.NewRequest(), flatland_connection(),
1095 fake_surface_producer());
1096 flatland_connection()->Present();
1097 loop().RunUntilIdle();
1098 fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
1099 loop().RunUntilIdle();
1100 EXPECT_THAT(fake_flatland().graph(),
1101 IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
1102 view_ref_clone));
1103
1104 // Create the view before drawing the scene.
1105 const SkSize child_view_size_signed = SkSize::Make(256.f, 512.f);
1106 const fuchsia::math::SizeU child_view_size{
1107 static_cast<uint32_t>(child_view_size_signed.width()),
1108 static_cast<uint32_t>(child_view_size_signed.height())};
1109 auto [child_view_token, child_viewport_token] = ViewTokenPair::New();
1110 const uint32_t child_view_id = child_viewport_token.value.get();
1111
1112 const int kOpacity = 125;
1113 const float kOpacityFloat = 125 / 255.0f;
1114 const fuchsia::math::VecF kScale{2.f, 3.0f};
1115
1116 auto matrix = SkMatrix::I();
1117 matrix.setScaleX(kScale.x);
1118 matrix.setScaleY(kScale.y);
1119
1120 auto mutators_stack = flutter::MutatorsStack();
1121 mutators_stack.PushOpacity(kOpacity);
1122 mutators_stack.PushTransform(matrix);
1123
1124 flutter::EmbeddedViewParams child_view_params(matrix, child_view_size_signed,
1125 mutators_stack);
1126 external_view_embedder.CreateView(
1127 child_view_id, []() {},
1128 [](fuchsia::ui::composition::ContentId,
1129 fuchsia::ui::composition::ChildViewWatcherHandle) {});
1130
1131 // Draw the scene. The scene graph shouldn't change yet.
1132 const SkISize frame_size_signed = SkISize::Make(512, 512);
1133 const fuchsia::math::SizeU frame_size{
1134 static_cast<uint32_t>(frame_size_signed.width()),
1135 static_cast<uint32_t>(frame_size_signed.height())};
1136 DrawFrameWithView(
1137 external_view_embedder, frame_size_signed, 1.f, child_view_id,
1138 child_view_params,
1139 [](flutter::DlCanvas* canvas) {
1140 const SkISize layer_size = canvas->GetBaseLayerSize();
1141 const SkSize canvas_size =
1142 SkSize::Make(layer_size.width(), layer_size.height());
1143 flutter::DlPaint rect_paint;
1144 rect_paint.setColor(flutter::DlColor::kGreen());
1145 canvas->Translate(canvas_size.width() / 4.f,
1146 canvas_size.height() / 2.f);
1147 canvas->DrawRect(SkRect::MakeWH(canvas_size.width() / 32.f,
1148 canvas_size.height() / 32.f),
1149 rect_paint);
1150 },
1151 [](flutter::DlCanvas* canvas) {});
1152 EXPECT_THAT(fake_flatland().graph(),
1153 IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
1154 view_ref_clone));
1155
1156 // Pump the message loop. The scene updates should propagate to flatland.
1157 loop().RunUntilIdle();
1158 fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
1159 loop().RunUntilIdle();
1160 EXPECT_THAT(
1161 fake_flatland().graph(),
1162 IsFlutterGraph(
1163 parent_viewport_watcher, viewport_creation_token,
1164 view_ref_clone, /*layers*/
1165 {IsImageLayer(
1166 frame_size, kFirstLayerBlendMode,
1167 {IsHitRegion(
1168 /* x */ 128.f,
1169 /* y */ 256.f,
1170 /* width */ 16.f,
1171 /* height */ 16.f,
1172 /* hit_test */
1173 fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
1174 IsViewportLayer(child_view_token, child_view_size,
1176 kOpacityFloat)}));
1177
1178 // Destroy the view. The scene graph shouldn't change yet.
1179 external_view_embedder.DestroyView(
1180 child_view_id, [](fuchsia::ui::composition::ContentId) {});
1181 EXPECT_THAT(
1182 fake_flatland().graph(),
1183 IsFlutterGraph(
1184 parent_viewport_watcher, viewport_creation_token,
1185 view_ref_clone, /*layers*/
1186 {IsImageLayer(
1187 frame_size, kFirstLayerBlendMode,
1188 {IsHitRegion(
1189 /* x */ 128.f,
1190 /* y */ 256.f,
1191 /* width */ 16.f,
1192 /* height */ 16.f,
1193 /* hit_test */
1194 fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
1195 IsViewportLayer(child_view_token, child_view_size,
1197 kOpacityFloat)}));
1198
1199 // Draw another frame without the view. The scene graph shouldn't change yet.
1200 DrawSimpleFrame(external_view_embedder, frame_size_signed, 1.f,
1201 [](flutter::DlCanvas* canvas) {
1202 const SkISize layer_size = canvas->GetBaseLayerSize();
1203 const SkSize canvas_size =
1204 SkSize::Make(layer_size.width(), layer_size.height());
1205 flutter::DlPaint rect_paint;
1206 rect_paint.setColor(flutter::DlColor::kGreen());
1207 canvas->Translate(canvas_size.width() / 4.f,
1208 canvas_size.height() / 2.f);
1209 canvas->DrawRect(
1210 SkRect::MakeWH(canvas_size.width() / 32.f,
1211 canvas_size.height() / 32.f),
1212 rect_paint);
1213 });
1214
1215 EXPECT_THAT(
1216 fake_flatland().graph(),
1217 IsFlutterGraph(
1218 parent_viewport_watcher, viewport_creation_token,
1219 view_ref_clone, /*layers*/
1220 {IsImageLayer(
1221 frame_size, kFirstLayerBlendMode,
1222 {IsHitRegion(
1223 /* x */ 128.f,
1224 /* y */ 256.f,
1225 /* width */ 16.f,
1226 /* height */ 16.f,
1227 /* hit_test */
1228 fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
1229 IsViewportLayer(child_view_token, child_view_size,
1231 kOpacityFloat)}));
1232
1233 // Pump the message loop. The scene updates should propagate to flatland.
1234 loop().RunUntilIdle();
1235 EXPECT_THAT(
1236 fake_flatland().graph(),
1237 IsFlutterGraph(
1238 parent_viewport_watcher, viewport_creation_token,
1239 view_ref_clone, /*layers*/
1240 {IsImageLayer(
1241 frame_size, kFirstLayerBlendMode,
1242 {IsHitRegion(
1243 /* x */ 128.f,
1244 /* y */ 256.f,
1245 /* width */ 16.f,
1246 /* height */ 16.f,
1247 /* hit_test */
1248 fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
1249}
1250
1251TEST_F(ExternalViewEmbedderTest, SceneWithOneView_DestroyBeforeDrawing) {
1252 fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher;
1253 fuchsia::ui::views::ViewportCreationToken viewport_creation_token;
1254 fuchsia::ui::views::ViewCreationToken view_creation_token;
1255 fuchsia::ui::views::ViewRef view_ref_clone;
1256 auto view_creation_token_status = zx::channel::create(
1257 0u, &viewport_creation_token.value, &view_creation_token.value);
1258 ASSERT_EQ(view_creation_token_status, ZX_OK);
1259
1260 fuchsia::ui::views::ViewRefControl view_ref_control;
1261 fuchsia::ui::views::ViewRef view_ref;
1262 auto status = zx::eventpair::create(
1263 /*options*/ 0u, &view_ref_control.reference, &view_ref.reference);
1264 ASSERT_EQ(status, ZX_OK);
1265 view_ref_control.reference.replace(
1266 ZX_DEFAULT_EVENTPAIR_RIGHTS & (~ZX_RIGHT_DUPLICATE),
1267 &view_ref_control.reference);
1268 view_ref.reference.replace(ZX_RIGHTS_BASIC, &view_ref.reference);
1269 view_ref.Clone(&view_ref_clone);
1270
1271 // Create the `ExternalViewEmbedder` and pump the message loop until
1272 // the initial scene graph is setup.
1273 ExternalViewEmbedder external_view_embedder(
1274 std::move(view_creation_token),
1275 fuchsia::ui::views::ViewIdentityOnCreation{
1276 .view_ref = std::move(view_ref),
1277 .view_ref_control = std::move(view_ref_control),
1278 },
1279 fuchsia::ui::composition::ViewBoundProtocols{},
1280 parent_viewport_watcher.NewRequest(), flatland_connection(),
1281 fake_surface_producer());
1282 flatland_connection()->Present();
1283 loop().RunUntilIdle();
1284 fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
1285 loop().RunUntilIdle();
1286 EXPECT_THAT(fake_flatland().graph(),
1287 IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
1288 view_ref_clone));
1289
1290 // Create the view before drawing the scene.
1291 auto [child_view_token, child_viewport_token] = ViewTokenPair::New();
1292 const uint32_t child_view_id = child_viewport_token.value.get();
1293 external_view_embedder.CreateView(
1294 child_view_id, []() {},
1295 [](fuchsia::ui::composition::ContentId,
1296 fuchsia::ui::composition::ChildViewWatcherHandle) {});
1297
1298 // Draw the scene without the view. The scene graph shouldn't change yet.
1299 const SkISize frame_size_signed = SkISize::Make(512, 512);
1300 const fuchsia::math::SizeU frame_size{
1301 static_cast<uint32_t>(frame_size_signed.width()),
1302 static_cast<uint32_t>(frame_size_signed.height())};
1303 DrawSimpleFrame(external_view_embedder, frame_size_signed, 1.f,
1304 [](flutter::DlCanvas* canvas) {
1305 const SkISize layer_size = canvas->GetBaseLayerSize();
1306 const SkSize canvas_size =
1307 SkSize::Make(layer_size.width(), layer_size.height());
1308 flutter::DlPaint rect_paint;
1309 rect_paint.setColor(flutter::DlColor().kGreen());
1310 canvas->Translate(canvas_size.width() / 4.f,
1311 canvas_size.height() / 2.f);
1312 canvas->DrawRect(
1313 SkRect::MakeWH(canvas_size.width() / 32.f,
1314 canvas_size.height() / 32.f),
1315 rect_paint);
1316 });
1317
1318 // Pump the message loop. The scene updates should propagate to flatland.
1319 loop().RunUntilIdle();
1320 fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
1321 loop().RunUntilIdle();
1322 EXPECT_THAT(
1323 fake_flatland().graph(),
1324 IsFlutterGraph(
1325 parent_viewport_watcher, viewport_creation_token, view_ref_clone,
1326 /*layers*/
1327 {IsImageLayer(
1328 frame_size, kFirstLayerBlendMode,
1329 {IsHitRegion(
1330 /* x */ 128.f,
1331 /* y */ 256.f,
1332 /* width */ 16.f,
1333 /* height */ 16.f,
1334 /* hit_test */
1335 fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
1336
1337 // Destroy the view. The scene graph shouldn't change yet.
1338 external_view_embedder.DestroyView(
1339 child_view_id, [](fuchsia::ui::composition::ContentId) {});
1340 EXPECT_THAT(
1341 fake_flatland().graph(),
1342 IsFlutterGraph(
1343 parent_viewport_watcher, viewport_creation_token, view_ref_clone,
1344 /*layers*/
1345 {IsImageLayer(
1346 frame_size, kFirstLayerBlendMode,
1347 {IsHitRegion(
1348 /* x */ 128.f,
1349 /* y */ 256.f,
1350 /* width */ 16.f,
1351 /* height */ 16.f,
1352 /* hit_test */
1353 fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
1354
1355 // Draw another frame without the view and change the size. The scene graph
1356 // shouldn't change yet.
1357 const SkISize new_frame_size_signed = SkISize::Make(256, 256);
1358 const fuchsia::math::SizeU new_frame_size{
1359 static_cast<uint32_t>(new_frame_size_signed.width()),
1360 static_cast<uint32_t>(new_frame_size_signed.height())};
1361 DrawSimpleFrame(external_view_embedder, new_frame_size_signed, 1.f,
1362 [](flutter::DlCanvas* canvas) {
1363 const SkISize layer_size = canvas->GetBaseLayerSize();
1364 const SkSize canvas_size =
1365 SkSize::Make(layer_size.width(), layer_size.height());
1366 flutter::DlPaint rect_paint;
1367 rect_paint.setColor(flutter::DlColor::kGreen());
1368 canvas->Translate(canvas_size.width() / 4.f,
1369 canvas_size.height() / 2.f);
1370 canvas->DrawRect(
1371 SkRect::MakeWH(canvas_size.width() / 32.f,
1372 canvas_size.height() / 32.f),
1373 rect_paint);
1374 });
1375 EXPECT_THAT(
1376 fake_flatland().graph(),
1377 IsFlutterGraph(
1378 parent_viewport_watcher, viewport_creation_token, view_ref_clone,
1379 /*layers*/
1380 {IsImageLayer(
1381 frame_size, kFirstLayerBlendMode,
1382 {IsHitRegion(
1383 /* x */ 128.f,
1384 /* y */ 256.f,
1385 /* width */ 16.f,
1386 /* height */ 16.f,
1387 /* hit_test */
1388 fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
1389
1390 // Pump the message loop. The scene updates should propagate to flatland.
1391 loop().RunUntilIdle();
1392 EXPECT_THAT(
1393 fake_flatland().graph(),
1394 IsFlutterGraph(
1395 parent_viewport_watcher, viewport_creation_token,
1396 view_ref_clone, /*layers*/
1397 {IsImageLayer(
1398 new_frame_size, kFirstLayerBlendMode,
1399 {IsHitRegion(
1400 /* x */ 64.f,
1401 /* y */ 128.f,
1402 /* width */ 8.f,
1403 /* height */ 8.f,
1404 /* hit_test */
1405 fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
1406}
1407
1408// This test case exercises the scenario in which the view contains two disjoint
1409// regions with painted content; we should generate two separate hit regions
1410// matching the bounds of the painted regions in this case.
1411TEST_F(ExternalViewEmbedderTest, SimpleScene_DisjointHitRegions) {
1412 fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher;
1413 fuchsia::ui::views::ViewportCreationToken viewport_creation_token;
1414 fuchsia::ui::views::ViewCreationToken view_creation_token;
1415 fuchsia::ui::views::ViewRef view_ref_clone;
1416 auto view_creation_token_status = zx::channel::create(
1417 0u, &viewport_creation_token.value, &view_creation_token.value);
1418 ASSERT_EQ(view_creation_token_status, ZX_OK);
1419
1420 fuchsia::ui::views::ViewRefControl view_ref_control;
1421 fuchsia::ui::views::ViewRef view_ref;
1422 auto status = zx::eventpair::create(
1423 /*options*/ 0u, &view_ref_control.reference, &view_ref.reference);
1424 ASSERT_EQ(status, ZX_OK);
1425 view_ref_control.reference.replace(
1426 ZX_DEFAULT_EVENTPAIR_RIGHTS & (~ZX_RIGHT_DUPLICATE),
1427 &view_ref_control.reference);
1428 view_ref.reference.replace(ZX_RIGHTS_BASIC, &view_ref.reference);
1429 view_ref.Clone(&view_ref_clone);
1430
1431 // Create the `ExternalViewEmbedder` and pump the message loop until
1432 // the initial scene graph is setup.
1433 ExternalViewEmbedder external_view_embedder(
1434 std::move(view_creation_token),
1435 fuchsia::ui::views::ViewIdentityOnCreation{
1436 .view_ref = std::move(view_ref),
1437 .view_ref_control = std::move(view_ref_control),
1438 },
1439 fuchsia::ui::composition::ViewBoundProtocols{},
1440 parent_viewport_watcher.NewRequest(), flatland_connection(),
1441 fake_surface_producer());
1442 flatland_connection()->Present();
1443 loop().RunUntilIdle();
1444 fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
1445 loop().RunUntilIdle();
1446 EXPECT_THAT(fake_flatland().graph(),
1447 IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
1448 view_ref_clone));
1449
1450 // Draw the scene. The scene graph shouldn't change yet.
1451 const SkISize frame_size_signed = SkISize::Make(512, 512);
1452 const fuchsia::math::SizeU frame_size{
1453 static_cast<uint32_t>(frame_size_signed.width()),
1454 static_cast<uint32_t>(frame_size_signed.height())};
1455 DrawSimpleFrame(
1456 external_view_embedder, frame_size_signed, 1.f,
1457 [](flutter::DlCanvas* canvas) {
1458 const SkISize layer_size = canvas->GetBaseLayerSize();
1459 const SkSize canvas_size =
1460 SkSize::Make(layer_size.width(), layer_size.height());
1461
1462 SkRect paint_region_1, paint_region_2;
1463
1464 paint_region_1 = SkRect::MakeXYWH(
1465 canvas_size.width() / 4.f, canvas_size.height() / 2.f,
1466 canvas_size.width() / 32.f, canvas_size.height() / 32.f);
1467
1468 flutter::DlPaint rect_paint;
1469 rect_paint.setColor(flutter::DlColor::kGreen());
1470 canvas->DrawRect(paint_region_1, rect_paint);
1471
1472 paint_region_2 = SkRect::MakeXYWH(
1473 canvas_size.width() * 3.f / 4.f, canvas_size.height() / 2.f,
1474 canvas_size.width() / 32.f, canvas_size.height() / 32.f);
1475
1476 rect_paint.setColor(flutter::DlColor::kRed());
1477 canvas->DrawRect(paint_region_2, rect_paint);
1478 });
1479 EXPECT_THAT(fake_flatland().graph(),
1480 IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
1481 view_ref_clone));
1482
1483 // Pump the message loop. The scene updates should propagate to flatland.
1484 loop().RunUntilIdle();
1485
1486 EXPECT_THAT(
1487 fake_flatland().graph(),
1488 IsFlutterGraph(
1489 parent_viewport_watcher, viewport_creation_token, view_ref_clone,
1490 /*layers*/
1491 {IsImageLayer(
1492 frame_size, kFirstLayerBlendMode,
1493 {IsHitRegion(
1494 /* x */ 128.f,
1495 /* y */ 256.f,
1496 /* width */ 16.f,
1497 /* height */ 16.f,
1498 /* hit_test */
1499 fuchsia::ui::composition::HitTestInteraction::DEFAULT),
1500 IsHitRegion(
1501 /* x */ 384.f,
1502 /* y */ 256.f,
1503 /* width */ 16.f,
1504 /* height */ 16.f,
1505 /* hit_test */
1506 fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
1507}
1508
1509// This test case exercises the scenario in which the view contains two
1510// overlapping regions with painted content; we should generate one hit
1511// region matching the union of the bounds of the two painted regions in
1512// this case.
1513TEST_F(ExternalViewEmbedderTest, SimpleScene_OverlappingHitRegions) {
1514 fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher;
1515 fuchsia::ui::views::ViewportCreationToken viewport_creation_token;
1516 fuchsia::ui::views::ViewCreationToken view_creation_token;
1517 fuchsia::ui::views::ViewRef view_ref_clone;
1518 auto view_creation_token_status = zx::channel::create(
1519 0u, &viewport_creation_token.value, &view_creation_token.value);
1520 ASSERT_EQ(view_creation_token_status, ZX_OK);
1521
1522 fuchsia::ui::views::ViewRefControl view_ref_control;
1523 fuchsia::ui::views::ViewRef view_ref;
1524 auto status = zx::eventpair::create(
1525 /*options*/ 0u, &view_ref_control.reference, &view_ref.reference);
1526 ASSERT_EQ(status, ZX_OK);
1527 view_ref_control.reference.replace(
1528 ZX_DEFAULT_EVENTPAIR_RIGHTS & (~ZX_RIGHT_DUPLICATE),
1529 &view_ref_control.reference);
1530 view_ref.reference.replace(ZX_RIGHTS_BASIC, &view_ref.reference);
1531 view_ref.Clone(&view_ref_clone);
1532
1533 // Create the `ExternalViewEmbedder` and pump the message loop until
1534 // the initial scene graph is setup.
1535 ExternalViewEmbedder external_view_embedder(
1536 std::move(view_creation_token),
1537 fuchsia::ui::views::ViewIdentityOnCreation{
1538 .view_ref = std::move(view_ref),
1539 .view_ref_control = std::move(view_ref_control),
1540 },
1541 fuchsia::ui::composition::ViewBoundProtocols{},
1542 parent_viewport_watcher.NewRequest(), flatland_connection(),
1543 fake_surface_producer());
1544 flatland_connection()->Present();
1545 loop().RunUntilIdle();
1546 fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
1547 loop().RunUntilIdle();
1548 EXPECT_THAT(fake_flatland().graph(),
1549 IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
1550 view_ref_clone));
1551
1552 // Draw the scene. The scene graph shouldn't change yet.
1553 const SkISize frame_size_signed = SkISize::Make(512, 512);
1554 const fuchsia::math::SizeU frame_size{
1555 static_cast<uint32_t>(frame_size_signed.width()),
1556 static_cast<uint32_t>(frame_size_signed.height())};
1557 DrawSimpleFrame(
1558 external_view_embedder, frame_size_signed, 1.f,
1559 [](flutter::DlCanvas* canvas) {
1560 const SkISize layer_size = canvas->GetBaseLayerSize();
1561 const SkSize canvas_size =
1562 SkSize::Make(layer_size.width(), layer_size.height());
1563
1564 SkRect paint_region_1, paint_region_2;
1565
1566 paint_region_1 = SkRect::MakeXYWH(
1567 canvas_size.width() / 4.f, canvas_size.height() / 2.f,
1568 3.f * canvas_size.width() / 8.f, canvas_size.height() / 4.f);
1569
1570 flutter::DlPaint rect_paint;
1571 rect_paint.setColor(flutter::DlColor::kGreen());
1572 canvas->DrawRect(paint_region_1, rect_paint);
1573
1574 paint_region_2 = SkRect::MakeXYWH(
1575 canvas_size.width() * 3.f / 8.f, canvas_size.height() / 2.f,
1576 3.f * canvas_size.width() / 8.f, canvas_size.height() / 4.f);
1577
1578 rect_paint.setColor(flutter::DlColor::kRed());
1579 canvas->DrawRect(paint_region_2, rect_paint);
1580 });
1581 EXPECT_THAT(fake_flatland().graph(),
1582 IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
1583 view_ref_clone));
1584
1585 // Pump the message loop. The scene updates should propagate to flatland.
1586 loop().RunUntilIdle();
1587
1588 EXPECT_THAT(
1589 fake_flatland().graph(),
1590 IsFlutterGraph(
1591 parent_viewport_watcher, viewport_creation_token, view_ref_clone,
1592 /*layers*/
1593 {IsImageLayer(
1594 frame_size, kFirstLayerBlendMode,
1595 {IsHitRegion(
1596 /* x */ 128.f,
1597 /* y */ 256.f,
1598 /* width */ 256.f,
1599 /* height */ 128.f,
1600 /* hit_test */
1601 fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
1602}
1603
1604TEST_F(ExternalViewEmbedderTest, ViewportCoveredWithInputInterceptor) {
1605 fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher;
1606 fuchsia::ui::views::ViewportCreationToken viewport_creation_token;
1607 fuchsia::ui::views::ViewCreationToken view_creation_token;
1608 fuchsia::ui::views::ViewRef view_ref_clone;
1609 auto view_creation_token_status = zx::channel::create(
1610 0u, &viewport_creation_token.value, &view_creation_token.value);
1611 ASSERT_EQ(view_creation_token_status, ZX_OK);
1612
1613 fuchsia::ui::views::ViewRefControl view_ref_control;
1614 fuchsia::ui::views::ViewRef view_ref;
1615 auto status = zx::eventpair::create(
1616 /*options*/ 0u, &view_ref_control.reference, &view_ref.reference);
1617 ASSERT_EQ(status, ZX_OK);
1618 view_ref_control.reference.replace(
1619 ZX_DEFAULT_EVENTPAIR_RIGHTS & (~ZX_RIGHT_DUPLICATE),
1620 &view_ref_control.reference);
1621 view_ref.reference.replace(ZX_RIGHTS_BASIC, &view_ref.reference);
1622 view_ref.Clone(&view_ref_clone);
1623
1624 // Create the `ExternalViewEmbedder` and pump the message loop until
1625 // the initial scene graph is setup.
1626 ExternalViewEmbedder external_view_embedder(
1627 std::move(view_creation_token),
1628 fuchsia::ui::views::ViewIdentityOnCreation{
1629 .view_ref = std::move(view_ref),
1630 .view_ref_control = std::move(view_ref_control),
1631 },
1632 fuchsia::ui::composition::ViewBoundProtocols{},
1633 parent_viewport_watcher.NewRequest(), flatland_connection(),
1634 fake_surface_producer(),
1635 /*intercept_all_input=*/true // Enables the interceptor.
1636 );
1637 flatland_connection()->Present();
1638 loop().RunUntilIdle();
1639 fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
1640 loop().RunUntilIdle();
1641 EXPECT_THAT(fake_flatland().graph(),
1642 IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
1643 view_ref_clone, {IsInputShield()}));
1644
1645 // Create the view before drawing the scene.
1646 const SkSize child_view_size_signed = SkSize::Make(256.f, 512.f);
1647 const fuchsia::math::SizeU child_view_size{
1648 static_cast<uint32_t>(child_view_size_signed.width()),
1649 static_cast<uint32_t>(child_view_size_signed.height())};
1650 auto [child_view_token, child_viewport_token] = ViewTokenPair::New();
1651 const uint32_t child_view_id = child_viewport_token.value.get();
1652
1653 const int kOpacity = 200;
1654 const float kOpacityFloat = 200 / 255.0f;
1655 const fuchsia::math::VecF kScale{3.0f, 4.0f};
1656
1657 auto matrix = SkMatrix::I();
1658 matrix.setScaleX(kScale.x);
1659 matrix.setScaleY(kScale.y);
1660
1661 auto mutators_stack = flutter::MutatorsStack();
1662 mutators_stack.PushOpacity(kOpacity);
1663 mutators_stack.PushTransform(matrix);
1664
1665 flutter::EmbeddedViewParams child_view_params(matrix, child_view_size_signed,
1666 mutators_stack);
1667 external_view_embedder.CreateView(
1668 child_view_id, []() {},
1669 [](fuchsia::ui::composition::ContentId,
1670 fuchsia::ui::composition::ChildViewWatcherHandle) {});
1671 const SkRect child_view_occlusion_hint = SkRect::MakeLTRB(1, 2, 3, 4);
1672 const fuchsia::math::Inset child_view_inset{
1673 static_cast<int32_t>(child_view_occlusion_hint.top()),
1674 static_cast<int32_t>(child_view_occlusion_hint.right()),
1675 static_cast<int32_t>(child_view_occlusion_hint.bottom()),
1676 static_cast<int32_t>(child_view_occlusion_hint.left())};
1677 external_view_embedder.SetViewProperties(
1678 child_view_id, child_view_occlusion_hint, /*hit_testable=*/false,
1679 /*focusable=*/false);
1680
1681 // We must take into account the effect of DPR on the view scale.
1682 const float kDPR = 2.0f;
1683 const float kInvDPR = 1.f / kDPR;
1684
1685 // Draw the scene. The scene graph shouldn't change yet.
1686 const SkISize frame_size_signed = SkISize::Make(512, 512);
1687 const fuchsia::math::SizeU frame_size{
1688 static_cast<uint32_t>(frame_size_signed.width()),
1689 static_cast<uint32_t>(frame_size_signed.height())};
1690 DrawFrameWithView(
1691 external_view_embedder, frame_size_signed, kDPR, child_view_id,
1692 child_view_params,
1693 [](flutter::DlCanvas* canvas) {
1694 const SkISize layer_size = canvas->GetBaseLayerSize();
1695 const SkSize canvas_size =
1696 SkSize::Make(layer_size.width(), layer_size.height());
1697 flutter::DlPaint rect_paint;
1698 rect_paint.setColor(flutter::DlColor::kGreen());
1699 canvas->Translate(canvas_size.width() / 4.f,
1700 canvas_size.height() / 2.f);
1701 canvas->DrawRect(SkRect::MakeWH(canvas_size.width() / 32.f,
1702 canvas_size.height() / 32.f),
1703 rect_paint);
1704 },
1705 [](flutter::DlCanvas* canvas) {
1706 const SkISize layer_size = canvas->GetBaseLayerSize();
1707 const SkSize canvas_size =
1708 SkSize::Make(layer_size.width(), layer_size.height());
1709 flutter::DlPaint rect_paint;
1710 rect_paint.setColor(flutter::DlColor::kRed());
1711 canvas->Translate(canvas_size.width() * 3.f / 4.f,
1712 canvas_size.height() / 2.f);
1713 canvas->DrawRect(SkRect::MakeWH(canvas_size.width() / 32.f,
1714 canvas_size.height() / 32.f),
1715 rect_paint);
1716 });
1717 EXPECT_THAT(fake_flatland().graph(),
1718 IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
1719 view_ref_clone, {IsInputShield()}));
1720
1721 // Pump the message loop. The scene updates should propagate to flatland.
1722 loop().RunUntilIdle();
1723 fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
1724 loop().RunUntilIdle();
1725
1726 EXPECT_THAT(
1727 fake_flatland().graph(),
1728 IsFlutterGraph(
1729 parent_viewport_watcher, viewport_creation_token,
1730 view_ref_clone, /*layers*/
1731 {IsImageLayer(
1732 frame_size, kFirstLayerBlendMode,
1733 {IsHitRegion(
1734 /* x */ 128.f,
1735 /* y */ 256.f,
1736 /* width */ 16.f,
1737 /* height */ 16.f,
1738 /* hit_test */
1739 fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
1740 IsViewportLayer(child_view_token, child_view_size, child_view_inset,
1741 {0, 0}, kScale, kOpacityFloat),
1742 IsImageLayer(
1743 frame_size, kUpperLayerBlendMode,
1744 {IsHitRegion(
1745 /* x */ 384.f,
1746 /* y */ 256.f,
1747 /* width */ 16.f,
1748 /* height */ 16.f,
1749 /* hit_test */
1750 fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
1751 IsInputShield()},
1752 {kInvDPR, kInvDPR}));
1753}
1754
1755} // namespace flutter_runner::testing
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
Developer-facing API for rendering anything within the engine.
Definition: dl_canvas.h:38
virtual void DrawRect(const SkRect &rect, const DlPaint &paint)=0
virtual SkISize GetBaseLayerSize() const =0
virtual void Translate(SkScalar tx, SkScalar ty)=0
DlPaint & setColor(DlColor color)
Definition: dl_paint.h:70
std::shared_ptr< FakeSurfaceProducer > fake_surface_producer()
fuchsia::ui::composition::FlatlandHandle ConnectFlatland(async_dispatcher_t *dispatcher=nullptr)
fuchsia::ui::composition::AllocatorHandle ConnectAllocator(async_dispatcher_t *dispatcher=nullptr)
#define FAIL(name, result)
FlKeyEvent * event
GAsyncResult * result
#define FML_LOG(severity)
Definition: logging.h:82
#define FML_CHECK(condition)
Definition: logging.h:85
fuchsia::ui::composition::AllocatorPtr flatland_allocator_
Dart_NativeFunction function
Definition: fuchsia.cc:51
static constexpr FlutterViewId kImplicitViewId
EGLSurface surface_
double y
double x
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
SK_API sk_sp< SkSurface > Null(int width, int height)
bool Contains(const Container &container, const Value &value)
std::pair< zx_koid_t, zx_koid_t > GetKoids(const ZX &kobj)
TEST_F(FocusDelegateTest, WatchCallbackSeries)
static constexpr fuchsia::ui::composition::HitRegion kInfiniteHitRegion
std::function< void()> ReleaseImageCallback
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
TRect< Scalar > Rect
Definition: rect.h:769
BlendMode
Definition: color.h:59
static bool Bind(PassBindingsCacheMTL &pass, ShaderStage stage, size_t bind_index, const BufferView &view)
Definition: ref_ptr.h:256
static SkRect inset(const SkRect &r)
bool EMSCRIPTEN_KEEPALIVE IsEmpty(const SkPath &path)
int32_t height
int32_t width
const Scalar scale
Definition: SkSize.h:16
static constexpr SkISize Make(int32_t w, int32_t h)
Definition: SkSize.h:20
constexpr int32_t width() const
Definition: SkSize.h:36
constexpr int32_t height() const
Definition: SkSize.h:37
constexpr float left() const
Definition: SkRect.h:734
constexpr float top() const
Definition: SkRect.h:741
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
constexpr float right() const
Definition: SkRect.h:748
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646
constexpr float bottom() const
Definition: SkRect.h:755
Definition: SkSize.h:52
static constexpr SkSize Make(SkScalar w, SkScalar h)
Definition: SkSize.h:56
SkScalar width() const
Definition: SkSize.h:76
SkScalar height() const
Definition: SkSize.h:77
static constexpr DlColor kRed()
Definition: dl_color.h:24
static constexpr DlColor kGreen()
Definition: dl_color.h:25
static constexpr fuchsia::math::RectF kDefaultSampleRegion
std::vector< fuchsia::ui::composition::HitRegion > hit_regions
static constexpr fuchsia::math::VecF kDefaultScale
static constexpr fuchsia::math::Vec kDefaultTranslation
std::optional< fuchsia::math::Rect > clip_bounds
static constexpr fuchsia::ui::composition::Orientation kDefaultOrientation
static constexpr fuchsia::math::Inset kDefaultViewportInset
int_closure create
#define ERROR(message)
Definition: elf_loader.cc:260