Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 8e481f4

Browse files
committed
[fuchsia][scenic] Use infinite hit region
fxbug.dev/118729
1 parent 87bdde8 commit 8e481f4

File tree

5 files changed

+195
-21
lines changed

5 files changed

+195
-21
lines changed

shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc

+6-17
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@
1313
namespace flutter_runner {
1414
namespace {
1515

16-
// Since the flatland hit-region can be transformed (rotated, scaled or
17-
// translated), we must ensure that the size of the hit-region will not cause
18-
// overflows on operations (like FLT_MAX would).
19-
constexpr float kMaxHitRegionSize = 1'000'000.f;
20-
2116
void AttachClipTransformChild(
2217
FlatlandConnection* flatland,
2318
FlatlandExternalViewEmbedder::ClipTransform* parent_clip_transform,
@@ -59,19 +54,13 @@ FlatlandExternalViewEmbedder::FlatlandExternalViewEmbedder(
5954
if (intercept_all_input) {
6055
input_interceptor_transform_ = flatland_->NextTransformId();
6156
flatland_->flatland()->CreateTransform(*input_interceptor_transform_);
57+
flatland_->flatland()->SetInfiniteHitRegion(
58+
*input_interceptor_transform_,
59+
fuchsia::ui::composition::HitTestInteraction::SEMANTICALLY_INVISIBLE);
6260

6361
flatland_->flatland()->AddChild(root_transform_id_,
6462
*input_interceptor_transform_);
6563
child_transforms_.emplace_back(*input_interceptor_transform_);
66-
67-
// Attach full-screen hit testing shield. Note that since the hit-region
68-
// may be transformed (translated, rotated), we do not want to set
69-
// width/height to FLT_MAX. This will cause a numeric overflow.
70-
flatland_->flatland()->SetHitRegions(
71-
*input_interceptor_transform_,
72-
{{{0, 0, kMaxHitRegionSize, kMaxHitRegionSize},
73-
fuchsia::ui::composition::HitTestInteraction::
74-
SEMANTICALLY_INVISIBLE}});
7564
}
7665
}
7766

@@ -453,9 +442,9 @@ void FlatlandExternalViewEmbedder::SubmitFrame(
453442
flatland_layer_index++;
454443
}
455444

456-
// Set up the input interceptor at the top of the
457-
// scene, if applicable. It will capture all input, and any unwanted input
458-
// will be reinjected into embedded views.
445+
// Set up the input interceptor at the top of the scene, if applicable. It
446+
// will capture all input, and any unwanted input will be reinjected into
447+
// embedded views.
459448
if (input_interceptor_transform_.has_value()) {
460449
flatland_->flatland()->AddChild(root_transform_id_,
461450
*input_interceptor_transform_);

shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland.cc

+23
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,29 @@ void FakeFlatland::SetHitRegions(
853853
transform->hit_regions = std::move(regions);
854854
}
855855

856+
void FakeFlatland::SetInfiniteHitRegion(
857+
fuchsia::ui::composition::TransformId transform_id,
858+
fuchsia::ui::composition::HitTestInteraction hit_test) {
859+
if (transform_id.value == 0) {
860+
// TODO(fxb/85619): Raise a FlatlandError here
861+
FML_CHECK(false)
862+
<< "FakeFlatland::SetTranslation: TransformId 0 is invalid.";
863+
return;
864+
}
865+
866+
auto found_transform = pending_graph_.transform_map.find(transform_id.value);
867+
if (found_transform == pending_graph_.transform_map.end()) {
868+
// TODO(fxb/85619): Raise a FlatlandError here
869+
FML_CHECK(false) << "FakeFlatland::SetTranslation: TransformId "
870+
<< transform_id.value << " does not exist.";
871+
return;
872+
}
873+
874+
auto& transform = found_transform->second;
875+
ZX_ASSERT(transform);
876+
transform->hit_regions = {kInfiniteHitRegion};
877+
}
878+
856879
void FakeFlatland::Clear() {
857880
parents_map_.clear();
858881
pending_graph_.Clear();

shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland.h

+5
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,11 @@ class FakeFlatland
310310
fuchsia::ui::composition::TransformId transform_id,
311311
std::vector<fuchsia::ui::composition::HitRegion> regions) override;
312312

313+
// |fuchsia::ui::composition::Flatland|
314+
void SetInfiniteHitRegion(
315+
fuchsia::ui::composition::TransformId transform_id,
316+
fuchsia::ui::composition::HitTestInteraction hit_test) override;
317+
313318
// |fuchsia::ui::composition::Flatland|
314319
void Clear() override;
315320

shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland_types.h

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <zircon/types.h>
1717

1818
#include <algorithm>
19+
#include <cfloat>
1920
#include <cstdint>
2021
#include <optional>
2122
#include <unordered_map>
@@ -138,6 +139,8 @@ namespace flutter_runner::testing {
138139

139140
constexpr static fuchsia::ui::composition::TransformId kInvalidTransformId{0};
140141
constexpr static fuchsia::ui::composition::ContentId kInvalidContentId{0};
142+
constexpr static fuchsia::ui::composition::HitRegion kInfiniteHitRegion = {
143+
.region = {-FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX}};
141144

142145
// Convenience structure which allows clients to easily create a valid
143146
// `ViewCreationToken` / `ViewportCreationToken` pair for use with Flatland

shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc

+158-4
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ using fuchsia::scenic::scheduling::FuturePresentationTimes;
3838
using fuchsia::scenic::scheduling::PresentReceivedInfo;
3939
using ::testing::_;
4040
using ::testing::AllOf;
41+
using ::testing::Contains;
4142
using ::testing::ElementsAre;
4243
using ::testing::Eq;
44+
using ::testing::Field;
4345
using ::testing::FieldsAre;
4446
using ::testing::IsEmpty;
4547
using ::testing::Matcher;
@@ -297,6 +299,16 @@ Matcher<std::shared_ptr<FakeTransform>> IsClipTransformLayer(
297299
/*content*/ _,
298300
/*hit_regions*/ _));
299301
}
302+
303+
Matcher<std::shared_ptr<FakeTransform>> IsInputShield() {
304+
return Pointee(AllOf(
305+
// Must not clip the hit region.
306+
Field("clip_bounds", &FakeTransform::clip_bounds, Eq(std::nullopt)),
307+
// Hit region must be "infinite".
308+
Field("hit_regions", &FakeTransform::hit_regions,
309+
Contains(flutter_runner::testing::kInfiniteHitRegion))));
310+
}
311+
300312
fuchsia::ui::composition::OnNextFrameBeginValues WithPresentCredits(
301313
uint32_t additional_present_credits) {
302314
fuchsia::ui::composition::OnNextFrameBeginValues values;
@@ -396,15 +408,18 @@ class FlatlandExternalViewEmbedderTest : public ::testing::Test {
396408
fuchsia::ui::composition::FlatlandHandle flatland =
397409
fake_flatland_.ConnectFlatland(session_subloop_->dispatcher());
398410

399-
auto test_name =
411+
const auto test_name =
400412
::testing::UnitTest::GetInstance()->current_test_info()->name();
413+
const auto max_frames_in_flight = 1;
414+
const auto vsync_offset = fml::TimeDelta::Zero();
401415
return std::make_shared<FlatlandConnection>(
402-
std::move(test_name), std::move(flatland), []() { FAIL(); },
403-
[](auto...) {}, 1, fml::TimeDelta::Zero());
416+
std::move(test_name), std::move(flatland),
417+
/*error_callback=*/[] { FAIL(); }, /*ofpe_callback=*/[](auto...) {},
418+
max_frames_in_flight, vsync_offset);
404419
}
405420

406421
// Primary loop and subloop for the FakeFlatland instance to process its
407-
// messages. The subloop allocates it's own zx_port_t, allowing us to use a
422+
// messages. The subloop allocates its own zx_port_t, allowing us to use a
408423
// separate port for each end of the message channel, rather than sharing a
409424
// single one. Dual ports allow messages and responses to be intermingled,
410425
// which is how production code behaves; this improves test realism.
@@ -1476,4 +1491,143 @@ TEST_F(FlatlandExternalViewEmbedderTest, SimpleScene_OverlappingHitRegions) {
14761491
fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
14771492
}
14781493

1494+
TEST_F(FlatlandExternalViewEmbedderTest, ViewportCoveredWithInputInterceptor) {
1495+
fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher;
1496+
fuchsia::ui::views::ViewportCreationToken viewport_creation_token;
1497+
fuchsia::ui::views::ViewCreationToken view_creation_token;
1498+
fuchsia::ui::views::ViewRef view_ref;
1499+
auto view_creation_token_status = zx::channel::create(
1500+
0u, &viewport_creation_token.value, &view_creation_token.value);
1501+
ASSERT_EQ(view_creation_token_status, ZX_OK);
1502+
auto view_ref_pair = scenic::ViewRefPair::New();
1503+
view_ref_pair.view_ref.Clone(&view_ref);
1504+
1505+
// Create the `FlatlandExternalViewEmbedder` and pump the message loop until
1506+
// the initial scene graph is setup.
1507+
FlatlandExternalViewEmbedder external_view_embedder(
1508+
std::move(view_creation_token),
1509+
fuchsia::ui::views::ViewIdentityOnCreation{
1510+
.view_ref = std::move(view_ref_pair.view_ref),
1511+
.view_ref_control = std::move(view_ref_pair.control_ref),
1512+
},
1513+
fuchsia::ui::composition::ViewBoundProtocols{},
1514+
parent_viewport_watcher.NewRequest(), flatland_connection(),
1515+
fake_surface_producer(),
1516+
/*intercept_all_input=*/true // Enables the interceptor.
1517+
);
1518+
flatland_connection()->Present();
1519+
loop().RunUntilIdle();
1520+
fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
1521+
loop().RunUntilIdle();
1522+
EXPECT_THAT(fake_flatland().graph(),
1523+
IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
1524+
view_ref, {IsInputShield()}));
1525+
1526+
// Create the view before drawing the scene.
1527+
const SkSize child_view_size_signed = SkSize::Make(256.f, 512.f);
1528+
const fuchsia::math::SizeU child_view_size{
1529+
static_cast<uint32_t>(child_view_size_signed.width()),
1530+
static_cast<uint32_t>(child_view_size_signed.height())};
1531+
auto [child_view_token, child_viewport_token] = ViewTokenPair::New();
1532+
const uint32_t child_view_id = child_viewport_token.value.get();
1533+
1534+
const int kOpacity = 200;
1535+
const float kOpacityFloat = 200 / 255.0f;
1536+
const fuchsia::math::VecF kScale{3.0f, 4.0f};
1537+
1538+
auto matrix = SkMatrix::I();
1539+
matrix.setScaleX(kScale.x);
1540+
matrix.setScaleY(kScale.y);
1541+
1542+
auto mutators_stack = flutter::MutatorsStack();
1543+
mutators_stack.PushOpacity(kOpacity);
1544+
mutators_stack.PushTransform(matrix);
1545+
1546+
flutter::EmbeddedViewParams child_view_params(matrix, child_view_size_signed,
1547+
mutators_stack);
1548+
external_view_embedder.CreateView(
1549+
child_view_id, []() {},
1550+
[](fuchsia::ui::composition::ContentId,
1551+
fuchsia::ui::composition::ChildViewWatcherHandle) {});
1552+
const SkRect child_view_occlusion_hint = SkRect::MakeLTRB(1, 2, 3, 4);
1553+
const fuchsia::math::Inset child_view_inset{
1554+
static_cast<int32_t>(child_view_occlusion_hint.top()),
1555+
static_cast<int32_t>(child_view_occlusion_hint.right()),
1556+
static_cast<int32_t>(child_view_occlusion_hint.bottom()),
1557+
static_cast<int32_t>(child_view_occlusion_hint.left())};
1558+
external_view_embedder.SetViewProperties(
1559+
child_view_id, child_view_occlusion_hint, /*hit_testable=*/false,
1560+
/*focusable=*/false);
1561+
1562+
// We must take into account the effect of DPR on the view scale.
1563+
const float kDPR = 2.0f;
1564+
const float kInvDPR = 1.f / kDPR;
1565+
1566+
// Draw the scene. The scene graph shouldn't change yet.
1567+
const SkISize frame_size_signed = SkISize::Make(512, 512);
1568+
const fuchsia::math::SizeU frame_size{
1569+
static_cast<uint32_t>(frame_size_signed.width()),
1570+
static_cast<uint32_t>(frame_size_signed.height())};
1571+
DrawFrameWithView(
1572+
external_view_embedder, frame_size_signed, kDPR, child_view_id,
1573+
child_view_params,
1574+
[](SkCanvas* canvas) {
1575+
const SkSize canvas_size = SkSize::Make(canvas->imageInfo().width(),
1576+
canvas->imageInfo().height());
1577+
SkPaint rect_paint;
1578+
rect_paint.setColor(SK_ColorGREEN);
1579+
canvas->translate(canvas_size.width() / 4.f,
1580+
canvas_size.height() / 2.f);
1581+
canvas->drawRect(SkRect::MakeWH(canvas_size.width() / 32.f,
1582+
canvas_size.height() / 32.f),
1583+
rect_paint);
1584+
},
1585+
[](SkCanvas* canvas) {
1586+
const SkSize canvas_size = SkSize::Make(canvas->imageInfo().width(),
1587+
canvas->imageInfo().height());
1588+
SkPaint rect_paint;
1589+
rect_paint.setColor(SK_ColorRED);
1590+
canvas->translate(canvas_size.width() * 3.f / 4.f,
1591+
canvas_size.height() / 2.f);
1592+
canvas->drawRect(SkRect::MakeWH(canvas_size.width() / 32.f,
1593+
canvas_size.height() / 32.f),
1594+
rect_paint);
1595+
});
1596+
EXPECT_THAT(fake_flatland().graph(),
1597+
IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
1598+
view_ref, {IsInputShield()}));
1599+
1600+
// Pump the message loop. The scene updates should propagate to flatland.
1601+
loop().RunUntilIdle();
1602+
fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
1603+
loop().RunUntilIdle();
1604+
1605+
EXPECT_THAT(
1606+
fake_flatland().graph(),
1607+
IsFlutterGraph(
1608+
parent_viewport_watcher, viewport_creation_token, view_ref, /*layers*/
1609+
{IsImageLayer(
1610+
frame_size, kFirstLayerBlendMode,
1611+
{IsHitRegion(
1612+
/* x */ 128.f,
1613+
/* y */ 256.f,
1614+
/* width */ 16.f,
1615+
/* height */ 16.f,
1616+
/* hit_test */
1617+
fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
1618+
IsViewportLayer(child_view_token, child_view_size, child_view_inset,
1619+
{0, 0}, kScale, kOpacityFloat),
1620+
IsImageLayer(
1621+
frame_size, kUpperLayerBlendMode,
1622+
{IsHitRegion(
1623+
/* x */ 384.f,
1624+
/* y */ 256.f,
1625+
/* width */ 16.f,
1626+
/* height */ 16.f,
1627+
/* hit_test */
1628+
fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
1629+
IsInputShield()},
1630+
{kInvDPR, kInvDPR}));
1631+
}
1632+
14791633
} // namespace flutter_runner::testing

0 commit comments

Comments
 (0)