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