[fuchsia][a11y] Set explicit hit regions in flatland embedder (flutter/engine#37338)

This commit is contained in:
Alexander Brusher
2022-11-18 10:16:14 -08:00
committed by GitHub
parent 6710befd88
commit 42fb1ed9d7
6 changed files with 458 additions and 78 deletions

View File

@@ -93,8 +93,9 @@ void FlatlandExternalViewEmbedder::PrerollCompositeEmbeddedView(
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 @@ void FlatlandExternalViewEmbedder::BeginFrame(
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 @@ void FlatlandExternalViewEmbedder::SubmitFrame(
}
}
// 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 @@ void FlatlandExternalViewEmbedder::SubmitFrame(
? 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 @@ void FlatlandExternalViewEmbedder::SubmitFrame(
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();
}
}

View File

@@ -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 @@ class FlatlandExternalViewEmbedder final
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{};

View File

@@ -850,7 +850,7 @@ void FakeFlatland::SetHitRegions(
auto& transform = found_transform->second;
FML_CHECK(transform);
transform->num_hit_regions = regions.size();
transform->hit_regions = std::move(regions);
}
void FakeFlatland::Clear() {

View File

@@ -72,7 +72,7 @@ std::shared_ptr<FakeTransform> CloneFakeTransform(
.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 @@ bool FakeTransform::operator==(const FakeTransform& other) const {
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 {

View File

@@ -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 @@ inline bool operator==(const fuchsia::ui::composition::ImageProperties& a,
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 @@ struct FakeTransform {
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 {

View File

@@ -202,6 +202,15 @@ Matcher<fuchsia::ui::composition::ViewportProperties> IsViewportProperties(
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 @@ Matcher<FakeGraph> IsFlutterGraph(
/*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<FakeGraph> IsFlutterGraph(
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 @@ Matcher<std::shared_ptr<FakeTransform>> IsImageLayer(
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 @@ Matcher<std::shared_ptr<FakeTransform>> IsViewportLayer(
/* 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 @@ TEST_F(FlatlandExternalViewEmbedderTest, SimpleScene) {
// 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 @@ TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView) {
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 @@ TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView) {
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 @@ TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView) {
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 @@ TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView_NoOverlay) {
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 @@ TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView_NoOverlay) {
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 @@ TEST_F(FlatlandExternalViewEmbedderTest,
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 @@ TEST_F(FlatlandExternalViewEmbedderTest,
});
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