[fuchsia][a11y] Set explicit hit regions in flatland embedder (#37338)
diff --git a/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc b/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc
index f31aecb..2746db7 100644
--- a/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc
+++ b/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc
@@ -93,8 +93,9 @@
zx_handle_t handle = static_cast<zx_handle_t>(view_id);
FML_CHECK(frame_layers_.count(handle) == 0);
- frame_layers_.emplace(std::make_pair(EmbedderLayerId{handle},
- EmbedderLayer(frame_size_, *params)));
+ frame_layers_.emplace(std::make_pair(
+ EmbedderLayerId{handle},
+ EmbedderLayer(frame_size_, *params, flutter::RTreeFactory())));
frame_composition_order_.push_back(handle);
}
@@ -125,8 +126,9 @@
frame_dpr_ = device_pixel_ratio;
// Create the root layer.
- frame_layers_.emplace(
- std::make_pair(kRootLayerId, EmbedderLayer(frame_size, std::nullopt)));
+ frame_layers_.emplace(std::make_pair(
+ kRootLayerId,
+ EmbedderLayer(frame_size, std::nullopt, flutter::RTreeFactory())));
frame_composition_order_.push_back(kRootLayerId);
}
@@ -193,6 +195,19 @@
}
}
+ // Finish recording SkPictures.
+ {
+ TRACE_EVENT0("flutter", "FinishRecordingPictures");
+
+ for (const auto& surface_index : frame_surface_indices) {
+ const auto& layer = frame_layers_.find(surface_index.first);
+ FML_CHECK(layer != frame_layers_.end());
+ layer->second.picture =
+ layer->second.recorder->finishRecordingAsPicture();
+ FML_CHECK(layer->second.picture != nullptr);
+ }
+ }
+
// Submit layers and platform views to Scenic in composition order.
{
TRACE_EVENT0("flutter", "SubmitLayers");
@@ -334,30 +349,43 @@
? fuchsia::ui::composition::BlendMode::SRC
: fuchsia::ui::composition::BlendMode::SRC_OVER);
+ // Set hit regions for this layer; these hit regions correspond to the
+ // portions of the layer on which skia drew content.
+ {
+ FML_CHECK(layer->second.rtree);
+ std::list<SkRect> intersection_rects =
+ layer->second.rtree->searchNonOverlappingDrawnRects(
+ SkRect::Make(layer->second.surface_size));
+
+ std::vector<fuchsia::ui::composition::HitRegion> hit_regions;
+ for (const SkRect& rect : intersection_rects) {
+ hit_regions.emplace_back();
+ auto& new_hit_region = hit_regions.back();
+ new_hit_region.region.x = rect.x();
+ new_hit_region.region.y = rect.y();
+ new_hit_region.region.width = rect.width();
+ new_hit_region.region.height = rect.height();
+ new_hit_region.hit_test =
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT;
+ }
+
+ flatland_->flatland()->SetHitRegions(
+ flatland_layers_[flatland_layer_index].transform_id,
+ std::move(hit_regions));
+ }
+
// Attach the FlatlandLayer to the main scene graph.
flatland_->flatland()->AddChild(
root_transform_id_,
flatland_layers_[flatland_layer_index].transform_id);
child_transforms_.emplace_back(
flatland_layers_[flatland_layer_index].transform_id);
-
- // Attach full-screen hit testing shield. Note that since the hit-region
- // may be transformed (translated, rotated), we do not want to set
- // width/height to FLT_MAX. This will cause a numeric overflow.
- flatland_->flatland()->SetHitRegions(
- flatland_layers_[flatland_layer_index].transform_id,
- {{{0, 0, kMaxHitRegionSize, kMaxHitRegionSize},
- fuchsia::ui::composition::HitTestInteraction::
- SEMANTICALLY_INVISIBLE}});
}
// Reset for the next pass:
flatland_layer_index++;
}
- // TODO(fxbug.dev/104956): Setting per-layer overlay hit region for Flatland
- // external view embedder should match with what is being done in GFX
- // external view embedder.
// Set up the input interceptor at the top of the
// scene, if applicable. It will capture all input, and any unwanted input
// will be reinjected into embedded views.
@@ -396,13 +424,10 @@
const auto& layer = frame_layers_.find(surface_index.first);
FML_CHECK(layer != frame_layers_.end());
- sk_sp<SkPicture> picture =
- layer->second.recorder->finishRecordingAsPicture();
- FML_CHECK(picture != nullptr);
canvas->setMatrix(SkMatrix::I());
canvas->clear(SK_ColorTRANSPARENT);
- canvas->drawPicture(picture);
+ canvas->drawPicture(layer->second.picture);
canvas->flush();
}
}
diff --git a/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h b/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h
index 0600b59..c6d6707 100644
--- a/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h
+++ b/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h
@@ -17,6 +17,7 @@
#include <vector>
#include "flutter/flow/embedded_views.h"
+#include "flutter/flow/rtree.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/macros.h"
#include "flutter/shell/common/canvas_spy.h"
@@ -144,18 +145,29 @@
struct EmbedderLayer {
EmbedderLayer(const SkISize& frame_size,
- std::optional<flutter::EmbeddedViewParams> view_params)
- : embedded_view_params(std::move(view_params)),
+ std::optional<flutter::EmbeddedViewParams> view_params,
+ flutter::RTreeFactory rtree_factory)
+ : rtree(rtree_factory.getInstance()),
+ embedded_view_params(std::move(view_params)),
recorder(std::make_unique<SkPictureRecorder>()),
canvas_spy(std::make_unique<flutter::CanvasSpy>(
- recorder->beginRecording(frame_size.width(),
- frame_size.height()))),
- surface_size(frame_size) {}
+ recorder->beginRecording(SkRect::Make(frame_size),
+ &rtree_factory))),
+ surface_size(frame_size),
+ picture(nullptr) {}
+
+ // Records paint operations applied to this layer's `SkCanvas`.
+ // These records are used to determine which portions of this layer
+ // contain content. The embedder propagates this information to scenic, so
+ // that scenic can accurately decide which portions of this layer may
+ // interact with input.
+ sk_sp<flutter::RTree> rtree;
std::optional<flutter::EmbeddedViewParams> embedded_view_params;
std::unique_ptr<SkPictureRecorder> recorder;
std::unique_ptr<flutter::CanvasSpy> canvas_spy;
SkISize surface_size;
+ sk_sp<SkPicture> picture;
};
using EmbedderLayerId = std::optional<uint32_t>;
constexpr static EmbedderLayerId kRootLayerId = EmbedderLayerId{};
diff --git a/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland.cc b/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland.cc
index a55d89a..4c78208 100644
--- a/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland.cc
+++ b/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland.cc
@@ -850,7 +850,7 @@
auto& transform = found_transform->second;
FML_CHECK(transform);
- transform->num_hit_regions = regions.size();
+ transform->hit_regions = std::move(regions);
}
void FakeFlatland::Clear() {
diff --git a/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland_types.cc b/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland_types.cc
index a265241..777344a 100644
--- a/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland_types.cc
+++ b/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland_types.cc
@@ -72,7 +72,7 @@
.children = CloneFakeTransformVector(
transform->children, transform_cache),
.content = CloneFakeContent(transform->content),
- .num_hit_regions = transform->num_hit_regions,
+ .hit_regions = transform->hit_regions,
}));
FML_CHECK(success);
@@ -136,7 +136,7 @@
return id == other.id && translation == other.translation &&
*clip_bounds == *other.clip_bounds &&
orientation == other.orientation && children == other.children &&
- content == other.content && num_hit_regions == other.num_hit_regions;
+ content == other.content && hit_regions == other.hit_regions;
}
bool FakeGraph::operator==(const FakeGraph& other) const {
diff --git a/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland_types.h b/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland_types.h
index cd2170c..1b0fc40 100644
--- a/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland_types.h
+++ b/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_flatland_types.h
@@ -15,6 +15,7 @@
#include <lib/fidl/cpp/interface_request.h>
#include <zircon/types.h>
+#include <algorithm>
#include <cstdint>
#include <optional>
#include <unordered_map>
@@ -98,6 +99,31 @@
return size_equal;
}
+inline bool operator==(const fuchsia::ui::composition::HitRegion& a,
+ const fuchsia::ui::composition::HitRegion& b) {
+ return a.region == b.region && a.hit_test == b.hit_test;
+}
+
+inline bool operator!=(const fuchsia::ui::composition::HitRegion& a,
+ const fuchsia::ui::composition::HitRegion& b) {
+ return !(a == b);
+}
+
+inline bool operator==(
+ const std::vector<fuchsia::ui::composition::HitRegion>& a,
+ const std::vector<fuchsia::ui::composition::HitRegion>& b) {
+ if (a.size() != b.size())
+ return false;
+
+ for (size_t i = 0; i < a.size(); ++i) {
+ if (a[i] != b[i]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
namespace flutter_runner::testing {
constexpr static fuchsia::ui::composition::TransformId kInvalidTransformId{0};
@@ -194,7 +220,7 @@
std::vector<std::shared_ptr<FakeTransform>> children;
std::shared_ptr<FakeContent> content;
- size_t num_hit_regions;
+ std::vector<fuchsia::ui::composition::HitRegion> hit_regions;
};
struct FakeGraph {
diff --git a/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc b/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc
index fac4884..213efaa 100644
--- a/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc
+++ b/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc
@@ -202,6 +202,15 @@
inset));
}
+Matcher<fuchsia::ui::composition::HitRegion> IsHitRegion(
+ const float x,
+ const float y,
+ const float width,
+ const float height,
+ const fuchsia::ui::composition::HitTestInteraction hit_test) {
+ return FieldsAre(FieldsAre(x, y, width, height), hit_test);
+}
+
Matcher<FakeGraph> IsEmptyGraph() {
return FieldsAre(IsEmpty(), IsEmpty(), Eq(nullptr), Eq(std::nullopt));
}
@@ -224,7 +233,7 @@
/*scale*/ scale, FakeTransform::kDefaultOrientation,
/*clip_bounds*/ _, FakeTransform::kDefaultOpacity,
/*children*/ ElementsAreArray(layer_matchers),
- /*content*/ Eq(nullptr), /*num_hit_regions*/ _)),
+ /*content*/ Eq(nullptr), /*hit_regions*/ _)),
Eq(FakeView{
.view_token = viewport_token_koids.second,
.view_ref = view_ref_koids.first,
@@ -240,7 +249,8 @@
Matcher<std::shared_ptr<FakeTransform>> IsImageLayer(
const fuchsia::math::SizeU& layer_size,
fuchsia::ui::composition::BlendMode blend_mode,
- size_t num_hit_regions) {
+ std::vector<Matcher<fuchsia::ui::composition::HitRegion>>
+ hit_region_matchers) {
return Pointee(FieldsAre(
/*id*/ _, FakeTransform::kDefaultTranslation,
FakeTransform::kDefaultScale, FakeTransform::kDefaultOrientation,
@@ -252,7 +262,7 @@
FakeImage::kDefaultSampleRegion, layer_size,
FakeImage::kDefaultOpacity, blend_mode,
/*buffer_import_token*/ _, /*vmo_index*/ 0))),
- num_hit_regions));
+ /* hit_regions*/ ElementsAreArray(hit_region_matchers)));
}
Matcher<std::shared_ptr<FakeTransform>> IsViewportLayer(
@@ -271,7 +281,7 @@
/* id */ _, IsViewportProperties(view_logical_size, view_inset),
/* viewport_token */ GetKoids(view_token).second,
/* child_view_watcher */ _))),
- /*num_hit_regions*/ 0));
+ /*hit_regions*/ _));
}
fuchsia::ui::composition::OnNextFrameBeginValues WithPresentCredits(
@@ -478,11 +488,21 @@
// Pump the message loop. The scene updates should propagate to flatland.
loop().RunUntilIdle();
+
EXPECT_THAT(
fake_flatland().graph(),
- IsFlutterGraph(parent_viewport_watcher, viewport_creation_token, view_ref,
- /*layers*/
- {IsImageLayer(frame_size, kFirstLayerBlendMode, 1)}));
+ IsFlutterGraph(
+ parent_viewport_watcher, viewport_creation_token, view_ref,
+ /*layers*/
+ {IsImageLayer(
+ frame_size, kFirstLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 128.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
}
TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView) {
@@ -598,10 +618,26 @@
fake_flatland().graph(),
IsFlutterGraph(
parent_viewport_watcher, viewport_creation_token, view_ref, /*layers*/
- {IsImageLayer(frame_size, kFirstLayerBlendMode, 1),
+ {IsImageLayer(
+ frame_size, kFirstLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 128.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
IsViewportLayer(child_view_token, child_view_size, child_view_inset,
{0, 0}, kScale, kOpacityFloat),
- IsImageLayer(frame_size, kUpperLayerBlendMode, 1)},
+ IsImageLayer(
+ frame_size, kUpperLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 384.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)})},
{kInvDPR, kInvDPR}));
// Destroy the view. The scene graph shouldn't change yet.
@@ -611,10 +647,26 @@
fake_flatland().graph(),
IsFlutterGraph(
parent_viewport_watcher, viewport_creation_token, view_ref, /*layers*/
- {IsImageLayer(frame_size, kFirstLayerBlendMode, 1),
+ {IsImageLayer(
+ frame_size, kFirstLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 128.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
IsViewportLayer(child_view_token, child_view_size, child_view_inset,
{0, 0}, kScale, kOpacityFloat),
- IsImageLayer(frame_size, kUpperLayerBlendMode, 1)},
+ IsImageLayer(
+ frame_size, kUpperLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 384.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)})},
{kInvDPR, kInvDPR}));
// Draw another frame without the view. The scene graph shouldn't change yet.
@@ -634,19 +686,43 @@
fake_flatland().graph(),
IsFlutterGraph(
parent_viewport_watcher, viewport_creation_token, view_ref, /*layers*/
- {IsImageLayer(frame_size, kFirstLayerBlendMode, 1),
+ {IsImageLayer(
+ frame_size, kFirstLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 128.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
IsViewportLayer(child_view_token, child_view_size, child_view_inset,
{0, 0}, kScale, kOpacityFloat),
- IsImageLayer(frame_size, kUpperLayerBlendMode, 1)},
+ IsImageLayer(
+ frame_size, kUpperLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 384.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)})},
{kInvDPR, kInvDPR}));
// Pump the message loop. The scene updates should propagate to flatland.
loop().RunUntilIdle();
EXPECT_THAT(
fake_flatland().graph(),
- IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
- view_ref, /*layers*/
- {IsImageLayer(frame_size, kFirstLayerBlendMode, 1)}));
+ IsFlutterGraph(
+ parent_viewport_watcher, viewport_creation_token, view_ref, /*layers*/
+ {IsImageLayer(
+ frame_size, kFirstLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 128.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
}
TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView_NoOverlay) {
@@ -736,24 +812,40 @@
loop().RunUntilIdle();
EXPECT_THAT(
fake_flatland().graph(),
- IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
- view_ref, /*layers*/
- {IsImageLayer(frame_size, kFirstLayerBlendMode, 1),
- IsViewportLayer(child_view_token, child_view_size,
- FakeViewport::kDefaultViewportInset,
- {0, 0}, kScale, kOpacityFloat)}));
+ IsFlutterGraph(
+ parent_viewport_watcher, viewport_creation_token, view_ref, /*layers*/
+ {IsImageLayer(
+ frame_size, kFirstLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 128.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
+ IsViewportLayer(child_view_token, child_view_size,
+ FakeViewport::kDefaultViewportInset, {0, 0}, kScale,
+ kOpacityFloat)}));
// Destroy the view. The scene graph shouldn't change yet.
external_view_embedder.DestroyView(
child_view_id, [](fuchsia::ui::composition::ContentId) {});
EXPECT_THAT(
fake_flatland().graph(),
- IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
- view_ref, /*layers*/
- {IsImageLayer(frame_size, kFirstLayerBlendMode, 1),
- IsViewportLayer(child_view_token, child_view_size,
- FakeViewport::kDefaultViewportInset,
- {0, 0}, kScale, kOpacityFloat)}));
+ IsFlutterGraph(
+ parent_viewport_watcher, viewport_creation_token, view_ref, /*layers*/
+ {IsImageLayer(
+ frame_size, kFirstLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 128.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
+ IsViewportLayer(child_view_token, child_view_size,
+ FakeViewport::kDefaultViewportInset, {0, 0}, kScale,
+ kOpacityFloat)}));
// Draw another frame without the view. The scene graph shouldn't change yet.
DrawSimpleFrame(
@@ -771,20 +863,36 @@
EXPECT_THAT(
fake_flatland().graph(),
- IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
- view_ref, /*layers*/
- {IsImageLayer(frame_size, kFirstLayerBlendMode, 1),
- IsViewportLayer(child_view_token, child_view_size,
- FakeViewport::kDefaultViewportInset,
- {0, 0}, kScale, kOpacityFloat)}));
+ IsFlutterGraph(
+ parent_viewport_watcher, viewport_creation_token, view_ref, /*layers*/
+ {IsImageLayer(
+ frame_size, kFirstLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 128.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)}),
+ IsViewportLayer(child_view_token, child_view_size,
+ FakeViewport::kDefaultViewportInset, {0, 0}, kScale,
+ kOpacityFloat)}));
// Pump the message loop. The scene updates should propagate to flatland.
loop().RunUntilIdle();
EXPECT_THAT(
fake_flatland().graph(),
- IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
- view_ref, /*layers*/
- {IsImageLayer(frame_size, kFirstLayerBlendMode, 1)}));
+ IsFlutterGraph(
+ parent_viewport_watcher, viewport_creation_token, view_ref, /*layers*/
+ {IsImageLayer(
+ frame_size, kFirstLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 128.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
}
TEST_F(FlatlandExternalViewEmbedderTest,
@@ -850,18 +958,36 @@
loop().RunUntilIdle();
EXPECT_THAT(
fake_flatland().graph(),
- IsFlutterGraph(parent_viewport_watcher, viewport_creation_token, view_ref,
- /*layers*/
- {IsImageLayer(frame_size, kFirstLayerBlendMode, 1)}));
+ IsFlutterGraph(
+ parent_viewport_watcher, viewport_creation_token, view_ref,
+ /*layers*/
+ {IsImageLayer(
+ frame_size, kFirstLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 128.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
// Destroy the view. The scene graph shouldn't change yet.
external_view_embedder.DestroyView(
child_view_id, [](fuchsia::ui::composition::ContentId) {});
EXPECT_THAT(
fake_flatland().graph(),
- IsFlutterGraph(parent_viewport_watcher, viewport_creation_token, view_ref,
- /*layers*/
- {IsImageLayer(frame_size, kFirstLayerBlendMode, 1)}));
+ IsFlutterGraph(
+ parent_viewport_watcher, viewport_creation_token, view_ref,
+ /*layers*/
+ {IsImageLayer(
+ frame_size, kFirstLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 128.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
// Draw another frame without the view and change the size. The scene graph
// shouldn't change yet.
@@ -883,17 +1009,208 @@
});
EXPECT_THAT(
fake_flatland().graph(),
- IsFlutterGraph(parent_viewport_watcher, viewport_creation_token, view_ref,
- /*layers*/
- {IsImageLayer(frame_size, kFirstLayerBlendMode, 1)}));
+ IsFlutterGraph(
+ parent_viewport_watcher, viewport_creation_token, view_ref,
+ /*layers*/
+ {IsImageLayer(
+ frame_size, kFirstLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 128.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
// Pump the message loop. The scene updates should propagate to flatland.
loop().RunUntilIdle();
EXPECT_THAT(
fake_flatland().graph(),
- IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
- view_ref, /*layers*/
- {IsImageLayer(new_frame_size, kFirstLayerBlendMode, 1)}));
+ IsFlutterGraph(
+ parent_viewport_watcher, viewport_creation_token, view_ref, /*layers*/
+ {IsImageLayer(
+ new_frame_size, kFirstLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 64.f,
+ /* y */ 128.f,
+ /* width */ 8.f,
+ /* height */ 8.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
+}
+
+// This test case exercises the scenario in which the view contains two disjoint
+// regions with painted content; we should generate two separate hit regions
+// matching the bounds of the painted regions in this case.
+TEST_F(FlatlandExternalViewEmbedderTest, SimpleScene_DisjointHitRegions) {
+ fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher;
+ fuchsia::ui::views::ViewportCreationToken viewport_creation_token;
+ fuchsia::ui::views::ViewCreationToken view_creation_token;
+ fuchsia::ui::views::ViewRef view_ref;
+ auto view_creation_token_status = zx::channel::create(
+ 0u, &viewport_creation_token.value, &view_creation_token.value);
+ ASSERT_EQ(view_creation_token_status, ZX_OK);
+ auto view_ref_pair = scenic::ViewRefPair::New();
+ view_ref_pair.view_ref.Clone(&view_ref);
+
+ // Create the `FlatlandExternalViewEmbedder` and pump the message loop until
+ // the initial scene graph is setup.
+ FlatlandExternalViewEmbedder external_view_embedder(
+ std::move(view_creation_token),
+ fuchsia::ui::views::ViewIdentityOnCreation{
+ .view_ref = std::move(view_ref_pair.view_ref),
+ .view_ref_control = std::move(view_ref_pair.control_ref),
+ },
+ fuchsia::ui::composition::ViewBoundProtocols{},
+ parent_viewport_watcher.NewRequest(), flatland_connection(),
+ fake_surface_producer());
+ flatland_connection()->Present();
+ loop().RunUntilIdle();
+ fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
+ loop().RunUntilIdle();
+ EXPECT_THAT(fake_flatland().graph(),
+ IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
+ view_ref));
+
+ // Draw the scene. The scene graph shouldn't change yet.
+ const SkISize frame_size_signed = SkISize::Make(512, 512);
+ const fuchsia::math::SizeU frame_size{
+ static_cast<uint32_t>(frame_size_signed.width()),
+ static_cast<uint32_t>(frame_size_signed.height())};
+ DrawSimpleFrame(
+ external_view_embedder, frame_size_signed, 1.f, [](SkCanvas* canvas) {
+ const SkSize canvas_size = SkSize::Make(canvas->imageInfo().width(),
+ canvas->imageInfo().height());
+
+ SkRect paint_region_1, paint_region_2;
+
+ paint_region_1 = SkRect::MakeXYWH(
+ canvas_size.width() / 4.f, canvas_size.height() / 2.f,
+ canvas_size.width() / 32.f, canvas_size.height() / 32.f);
+
+ SkPaint rect_paint;
+ rect_paint.setColor(SK_ColorGREEN);
+ canvas->drawRect(paint_region_1, rect_paint);
+
+ paint_region_2 = SkRect::MakeXYWH(
+ canvas_size.width() * 3.f / 4.f, canvas_size.height() / 2.f,
+ canvas_size.width() / 32.f, canvas_size.height() / 32.f);
+
+ rect_paint.setColor(SK_ColorRED);
+ canvas->drawRect(paint_region_2, rect_paint);
+ });
+ EXPECT_THAT(fake_flatland().graph(),
+ IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
+ view_ref));
+
+ // Pump the message loop. The scene updates should propagate to flatland.
+ loop().RunUntilIdle();
+
+ EXPECT_THAT(
+ fake_flatland().graph(),
+ IsFlutterGraph(
+ parent_viewport_watcher, viewport_creation_token, view_ref,
+ /*layers*/
+ {IsImageLayer(
+ frame_size, kFirstLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 128.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT),
+ IsHitRegion(
+ /* x */ 384.f,
+ /* y */ 256.f,
+ /* width */ 16.f,
+ /* height */ 16.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
+}
+
+// This test case exercises the scenario in which the view contains two
+// overlapping regions with painted content; we should generate one hit
+// region matching the union of the bounds of the two painted regions in
+// this case.
+TEST_F(FlatlandExternalViewEmbedderTest, SimpleScene_OverlappingHitRegions) {
+ fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher;
+ fuchsia::ui::views::ViewportCreationToken viewport_creation_token;
+ fuchsia::ui::views::ViewCreationToken view_creation_token;
+ fuchsia::ui::views::ViewRef view_ref;
+ auto view_creation_token_status = zx::channel::create(
+ 0u, &viewport_creation_token.value, &view_creation_token.value);
+ ASSERT_EQ(view_creation_token_status, ZX_OK);
+ auto view_ref_pair = scenic::ViewRefPair::New();
+ view_ref_pair.view_ref.Clone(&view_ref);
+
+ // Create the `FlatlandExternalViewEmbedder` and pump the message loop until
+ // the initial scene graph is setup.
+ FlatlandExternalViewEmbedder external_view_embedder(
+ std::move(view_creation_token),
+ fuchsia::ui::views::ViewIdentityOnCreation{
+ .view_ref = std::move(view_ref_pair.view_ref),
+ .view_ref_control = std::move(view_ref_pair.control_ref),
+ },
+ fuchsia::ui::composition::ViewBoundProtocols{},
+ parent_viewport_watcher.NewRequest(), flatland_connection(),
+ fake_surface_producer());
+ flatland_connection()->Present();
+ loop().RunUntilIdle();
+ fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u));
+ loop().RunUntilIdle();
+ EXPECT_THAT(fake_flatland().graph(),
+ IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
+ view_ref));
+
+ // Draw the scene. The scene graph shouldn't change yet.
+ const SkISize frame_size_signed = SkISize::Make(512, 512);
+ const fuchsia::math::SizeU frame_size{
+ static_cast<uint32_t>(frame_size_signed.width()),
+ static_cast<uint32_t>(frame_size_signed.height())};
+ DrawSimpleFrame(
+ external_view_embedder, frame_size_signed, 1.f, [](SkCanvas* canvas) {
+ const SkSize canvas_size = SkSize::Make(canvas->imageInfo().width(),
+ canvas->imageInfo().height());
+
+ SkRect paint_region_1, paint_region_2;
+
+ paint_region_1 = SkRect::MakeXYWH(
+ canvas_size.width() / 4.f, canvas_size.height() / 2.f,
+ 3.f * canvas_size.width() / 8.f, canvas_size.height() / 4.f);
+
+ SkPaint rect_paint;
+ rect_paint.setColor(SK_ColorGREEN);
+ canvas->drawRect(paint_region_1, rect_paint);
+
+ paint_region_2 = SkRect::MakeXYWH(
+ canvas_size.width() * 3.f / 8.f, canvas_size.height() / 2.f,
+ 3.f * canvas_size.width() / 8.f, canvas_size.height() / 4.f);
+
+ rect_paint.setColor(SK_ColorRED);
+ canvas->drawRect(paint_region_2, rect_paint);
+ });
+ EXPECT_THAT(fake_flatland().graph(),
+ IsFlutterGraph(parent_viewport_watcher, viewport_creation_token,
+ view_ref));
+
+ // Pump the message loop. The scene updates should propagate to flatland.
+ loop().RunUntilIdle();
+
+ EXPECT_THAT(
+ fake_flatland().graph(),
+ IsFlutterGraph(
+ parent_viewport_watcher, viewport_creation_token, view_ref,
+ /*layers*/
+ {IsImageLayer(
+ frame_size, kFirstLayerBlendMode,
+ {IsHitRegion(
+ /* x */ 128.f,
+ /* y */ 256.f,
+ /* width */ 256.f,
+ /* height */ 128.f,
+ /* hit_test */
+ fuchsia::ui::composition::HitTestInteraction::DEFAULT)})}));
}
} // namespace flutter_runner::testing