| // Copyright 2025 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/viz/service/layers/layer_context_impl.h" |
| |
| #include <algorithm> |
| #include <cmath> |
| #include <limits> |
| #include <memory> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| |
| #include "base/memory/raw_ptr.h" |
| #include "base/types/expected.h" |
| #include "cc/layers/mirror_layer_impl.h" |
| #include "cc/layers/nine_patch_layer_impl.h" |
| #include "cc/layers/nine_patch_thumb_scrollbar_layer_impl.h" |
| #include "cc/layers/painted_scrollbar_layer_impl.h" |
| #include "cc/layers/solid_color_scrollbar_layer_impl.h" |
| #include "cc/layers/surface_layer_impl.h" |
| #include "cc/layers/texture_layer_impl.h" |
| #include "cc/layers/ui_resource_layer_impl.h" |
| #include "cc/layers/view_transition_content_layer_impl.h" |
| #include "cc/trees/layer_tree_host_impl.h" |
| #include "cc/trees/layer_tree_impl.h" |
| #include "cc/trees/property_tree.h" |
| #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h" |
| #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h" |
| #include "components/viz/service/layers/layer_context_impl_base_unittest.h" |
| #include "components/viz/test/fake_compositor_frame_sink_client.h" |
| #include "gpu/GLES2/gl2extchromium.h" |
| #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom.h" |
| #include "services/viz/public/mojom/compositing/layer_context.mojom.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gfx/geometry/rect.h" |
| |
| namespace viz { |
| namespace { |
| |
| class LayerContextImplUpdateDisplayTreeUIResourceRequestTest |
| : public LayerContextImplTest, |
| public ::testing::WithParamInterface<std::tuple<gfx::Size, bool>> {}; |
| |
| TEST_P(LayerContextImplUpdateDisplayTreeUIResourceRequestTest, ResourceSize) { |
| const gfx::Size resource_size = std::get<0>(GetParam()); |
| const bool is_valid = std::get<1>(GetParam()); |
| |
| auto update = CreateDefaultUpdate(); |
| auto request = mojom::TransferableUIResourceRequest::New(); |
| request->type = mojom::TransferableUIResourceRequest::Type::kCreate; |
| request->uid = 42; |
| request->transferable_resource = MakeFakeResource(resource_size); |
| update->ui_resource_requests.push_back(std::move(request)); |
| |
| auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update)); |
| |
| if (is_valid) { |
| EXPECT_TRUE(result.has_value()); |
| EXPECT_NE(layer_context_impl_->host_impl()->ResourceIdForUIResource( |
| /*uid=*/42), |
| kInvalidResourceId); |
| } else { |
| ASSERT_FALSE(result.has_value()); |
| EXPECT_EQ(result.error(), |
| "Invalid dimensions for transferable UI resource."); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| UIResourceRequestDimensions, |
| LayerContextImplUpdateDisplayTreeUIResourceRequestTest, |
| ::testing::Values(std::make_tuple(gfx::Size(10, 10), true), |
| std::make_tuple(gfx::Size(0, 10), false), |
| std::make_tuple(gfx::Size(10, 0), false)), |
| [](const testing::TestParamInfo< |
| LayerContextImplUpdateDisplayTreeUIResourceRequestTest::ParamType>& |
| info) { |
| std::stringstream name; |
| name << (std::get<1>(info.param) ? "Valid" : "Invalid") << "_" |
| << info.index; |
| return name.str(); |
| }); |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeUIResourceRequestTest, |
| CreateWithNullTransferableResourceFails) { |
| auto update = CreateDefaultUpdate(); |
| auto request = mojom::TransferableUIResourceRequest::New(); |
| request->type = mojom::TransferableUIResourceRequest::Type::kCreate; |
| request->uid = 42; |
| request->transferable_resource = std::nullopt; // Explicitly null |
| update->ui_resource_requests.push_back(std::move(request)); |
| |
| auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update)); |
| |
| ASSERT_FALSE(result.has_value()); |
| EXPECT_EQ(result.error(), |
| "Invalid transferable resource in UI resource creation"); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeUIResourceRequestTest, |
| CreateWithEmptyTransferableResourceFails) { |
| auto update = CreateDefaultUpdate(); |
| auto request = mojom::TransferableUIResourceRequest::New(); |
| request->type = mojom::TransferableUIResourceRequest::Type::kCreate; |
| request->uid = 43; |
| |
| // A default-constructed TransferableResource has id = kInvalidResourceId, |
| // making it empty. |
| request->transferable_resource = TransferableResource(); |
| ASSERT_TRUE(request->transferable_resource->is_empty()); |
| |
| update->ui_resource_requests.push_back(std::move(request)); |
| |
| auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update)); |
| |
| ASSERT_FALSE(result.has_value()); |
| EXPECT_EQ(result.error(), |
| "Invalid transferable resource in UI resource creation"); |
| } |
| |
| class LayerContextImplUpdateDisplayTreeTilingTest |
| : public LayerContextImplTest, |
| public ::testing::WithParamInterface<std::tuple<gfx::Size, bool>> {}; |
| |
| TEST_P(LayerContextImplUpdateDisplayTreeTilingTest, TileSize) { |
| const gfx::Size tile_size = std::get<0>(GetParam()); |
| const bool is_valid = std::get<1>(GetParam()); |
| auto update = CreateDefaultUpdate(); |
| int layer_id = |
| AddDefaultLayerToUpdate(update.get(), cc::mojom::LayerType::kTileDisplay); |
| |
| auto tiling = mojom::Tiling::New(); |
| tiling->layer_id = layer_id; |
| tiling->scale_key = 1.0f; |
| tiling->raster_scale = gfx::Vector2dF(1.0f, 1.0f); |
| tiling->tile_size = tile_size; |
| tiling->tiling_rect = gfx::Rect(100, 100); |
| |
| auto tile = mojom::Tile::New(); |
| tile->column_index = 0; |
| tile->row_index = 0; |
| tile->contents = mojom::TileContents::NewSolidColor(SkColors::kRed); |
| tiling->tiles.push_back(std::move(tile)); |
| |
| update->tilings.push_back(std::move(tiling)); |
| |
| auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update)); |
| |
| if (is_valid) { |
| EXPECT_TRUE(result.has_value()); |
| cc::LayerTreeImpl* active_tree = |
| layer_context_impl_->host_impl()->active_tree(); |
| ASSERT_TRUE(active_tree); |
| cc::LayerImpl* layer_impl = active_tree->LayerById(layer_id); |
| ASSERT_TRUE(layer_impl); |
| ASSERT_EQ(layer_impl->GetLayerType(), cc::mojom::LayerType::kTileDisplay); |
| const auto* tile_display_layer = |
| static_cast<const cc::TileDisplayLayerImpl*>(layer_impl); |
| EXPECT_NE(nullptr, |
| tile_display_layer->GetTilingForTesting(/*scale_key=*/1.0)); |
| } else { |
| ASSERT_FALSE(result.has_value()); |
| EXPECT_EQ(result.error(), "Invalid tile_size dimensions in Tiling"); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| TileSize, |
| LayerContextImplUpdateDisplayTreeTilingTest, |
| ::testing::Values(std::make_tuple(gfx::Size(10, 10), true), |
| std::make_tuple(gfx::Size(0, 10), false), |
| std::make_tuple(gfx::Size(10, 0), false)), |
| [](const testing::TestParamInfo< |
| LayerContextImplUpdateDisplayTreeTilingTest::ParamType>& info) { |
| std::stringstream name; |
| name << (std::get<1>(info.param) ? "Valid" : "Invalid") << "_" |
| << info.index; |
| return name.str(); |
| }); |
| |
| TEST_F(LayerContextImplTest, DrawModeIsGpuForwardedViaSettings) { |
| auto settings = mojom::LayerContextSettings::New(); |
| settings->draw_mode_is_gpu = true; |
| RecreateLayerContextImplWithSettings(std::move(settings)); |
| cc::LayerTreeHostImpl* host_impl = layer_context_impl_->host_impl(); |
| EXPECT_TRUE(host_impl->settings().display_tree_draw_mode_is_gpu); |
| |
| settings = mojom::LayerContextSettings::New(); |
| settings->draw_mode_is_gpu = false; |
| RecreateLayerContextImplWithSettings(std::move(settings)); |
| host_impl = layer_context_impl_->host_impl(); |
| EXPECT_FALSE(host_impl->settings().display_tree_draw_mode_is_gpu); |
| } |
| |
| TEST_F(LayerContextImplTest, EnableEdgeAntiAliasingForwardedViaSettings) { |
| auto settings = mojom::LayerContextSettings::New(); |
| settings->enable_edge_anti_aliasing = true; |
| RecreateLayerContextImplWithSettings(std::move(settings)); |
| cc::LayerTreeHostImpl* host_impl = layer_context_impl_->host_impl(); |
| EXPECT_TRUE(host_impl->settings().enable_edge_anti_aliasing); |
| |
| settings = mojom::LayerContextSettings::New(); |
| settings->enable_edge_anti_aliasing = false; |
| RecreateLayerContextImplWithSettings(std::move(settings)); |
| host_impl = layer_context_impl_->host_impl(); |
| EXPECT_FALSE(host_impl->settings().enable_edge_anti_aliasing); |
| } |
| |
| TEST_F(LayerContextImplTest, TransferableUIResourceLifecycleAndEdgeCases) { |
| cc::LayerTreeHostImpl* host_impl = layer_context_impl_->host_impl(); |
| |
| // Initial state: No resources. |
| EXPECT_EQ(host_impl->ResourceIdForUIResource(1), kInvalidResourceId); |
| EXPECT_EQ(host_impl->ResourceIdForUIResource(2), kInvalidResourceId); |
| EXPECT_EQ(host_impl->ResourceIdForUIResource(3), kInvalidResourceId); |
| |
| // Test Case 1: Create UIResource 1. Verify it exists. |
| auto update1 = CreateDefaultUpdate(); |
| update1->ui_resource_requests.push_back(CreateUIResourceRequest( |
| 1, mojom::TransferableUIResourceRequest::Type::kCreate)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| EXPECT_NE(host_impl->ResourceIdForUIResource(1), kInvalidResourceId); |
| EXPECT_EQ(host_impl->ResourceIdForUIResource(2), kInvalidResourceId); |
| |
| // Test Case 2: Create UIResource 2. Verify both 1 and 2 exist. |
| auto update2 = CreateDefaultUpdate(); |
| update2->ui_resource_requests.push_back(CreateUIResourceRequest( |
| 2, mojom::TransferableUIResourceRequest::Type::kCreate)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_NE(host_impl->ResourceIdForUIResource(1), kInvalidResourceId); |
| EXPECT_NE(host_impl->ResourceIdForUIResource(2), kInvalidResourceId); |
| |
| // Test Case 3: Remove UIResource 1. Verify 1 is gone, 2 exists. |
| auto update3 = CreateDefaultUpdate(); |
| update3->ui_resource_requests.push_back(CreateUIResourceRequest( |
| 1, mojom::TransferableUIResourceRequest::Type::kDelete)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| EXPECT_EQ(host_impl->ResourceIdForUIResource(1), kInvalidResourceId); |
| EXPECT_NE(host_impl->ResourceIdForUIResource(2), kInvalidResourceId); |
| |
| // Test Case 4: Edge Case - Try to remove UIResource 1 again (already |
| // removed). Verify no crash and 2 still exists. |
| auto update4 = CreateDefaultUpdate(); |
| update4->ui_resource_requests.push_back(CreateUIResourceRequest( |
| 1, mojom::TransferableUIResourceRequest::Type::kDelete)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update4)).has_value()); |
| EXPECT_EQ(host_impl->ResourceIdForUIResource(1), kInvalidResourceId); |
| EXPECT_NE(host_impl->ResourceIdForUIResource(2), kInvalidResourceId); |
| |
| // Test Case 5: Edge Case - Try to remove UIResource 3 (never created). |
| // Verify no crash and 2 still exists. |
| auto update5 = CreateDefaultUpdate(); |
| update5->ui_resource_requests.push_back(CreateUIResourceRequest( |
| 3, mojom::TransferableUIResourceRequest::Type::kDelete)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update5)).has_value()); |
| EXPECT_EQ(host_impl->ResourceIdForUIResource(3), kInvalidResourceId); |
| EXPECT_NE(host_impl->ResourceIdForUIResource(2), kInvalidResourceId); |
| |
| // Test Case 6: Edge Case - Try to create UIResource 2 again (duplicate |
| // create). Verify 2 still exists (it's replaced). |
| ResourceId old_resource_2_id = host_impl->ResourceIdForUIResource(2); |
| auto update6 = CreateDefaultUpdate(); |
| update6->ui_resource_requests.push_back(CreateUIResourceRequest( |
| 2, mojom::TransferableUIResourceRequest::Type::kCreate)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update6)).has_value()); |
| EXPECT_NE(host_impl->ResourceIdForUIResource(2), kInvalidResourceId); |
| // The resource ID might change upon re-creation. |
| EXPECT_NE(host_impl->ResourceIdForUIResource(2), old_resource_2_id); |
| |
| // Test Case 7: Remove UIResource 2. Verify it's gone. |
| auto update7 = CreateDefaultUpdate(); |
| update7->ui_resource_requests.push_back(CreateUIResourceRequest( |
| 2, mojom::TransferableUIResourceRequest::Type::kDelete)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update7)).has_value()); |
| EXPECT_EQ(host_impl->ResourceIdForUIResource(2), kInvalidResourceId); |
| |
| // Test Case 8: Operations within the same update. |
| // Create UID 10, then Delete UID 10. Should result in no resource 10. |
| // Delete UID 11 (non-existent), then Create UID 11. Should result in |
| // resource 11. Create UID 12, then Create UID 12 again. Should result in |
| // resource 12. |
| auto update8 = CreateDefaultUpdate(); |
| update8->ui_resource_requests.push_back(CreateUIResourceRequest( |
| 10, mojom::TransferableUIResourceRequest::Type::kCreate)); |
| update8->ui_resource_requests.push_back(CreateUIResourceRequest( |
| 10, mojom::TransferableUIResourceRequest::Type::kDelete)); |
| update8->ui_resource_requests.push_back(CreateUIResourceRequest( |
| 11, mojom::TransferableUIResourceRequest::Type::kDelete)); |
| update8->ui_resource_requests.push_back(CreateUIResourceRequest( |
| 11, mojom::TransferableUIResourceRequest::Type::kCreate)); |
| update8->ui_resource_requests.push_back(CreateUIResourceRequest( |
| 12, mojom::TransferableUIResourceRequest::Type::kCreate)); |
| update8->ui_resource_requests.push_back(CreateUIResourceRequest( |
| 12, mojom::TransferableUIResourceRequest::Type::kCreate)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update8)).has_value()); |
| EXPECT_EQ(host_impl->ResourceIdForUIResource(10), kInvalidResourceId); |
| EXPECT_NE(host_impl->ResourceIdForUIResource(11), kInvalidResourceId); |
| EXPECT_NE(host_impl->ResourceIdForUIResource(12), kInvalidResourceId); |
| |
| // Cleanup remaining resources |
| auto update_cleanup = CreateDefaultUpdate(); |
| update_cleanup->ui_resource_requests.push_back(CreateUIResourceRequest( |
| 11, mojom::TransferableUIResourceRequest::Type::kDelete)); |
| update_cleanup->ui_resource_requests.push_back(CreateUIResourceRequest( |
| 12, mojom::TransferableUIResourceRequest::Type::kDelete)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update_cleanup)) |
| .has_value()); |
| EXPECT_EQ(host_impl->ResourceIdForUIResource(11), kInvalidResourceId); |
| EXPECT_EQ(host_impl->ResourceIdForUIResource(12), kInvalidResourceId); |
| } |
| |
| class LayerContextImplLayerLifecycleTest : public LayerContextImplTest { |
| public: |
| static std::string GetLayerImplName(cc::mojom::LayerType type) { |
| switch (type) { |
| case cc::mojom::LayerType::kLayer: |
| return "LayerImpl"; |
| case cc::mojom::LayerType::kMirror: |
| return "MirrorLayerImpl"; |
| case cc::mojom::LayerType::kNinePatch: |
| return "NinePatchLayerImpl"; |
| case cc::mojom::LayerType::kNinePatchThumbScrollbar: |
| return "NinePatchThumbScrollbarLayerImpl"; |
| case cc::mojom::LayerType::kPaintedScrollbar: |
| return "PaintedScrollbarLayerImpl"; |
| case cc::mojom::LayerType::kSolidColor: |
| return "SolidColorLayerImpl"; |
| case cc::mojom::LayerType::kSolidColorScrollbar: |
| return "SolidColorScrollbarLayerImpl"; |
| case cc::mojom::LayerType::kSurface: |
| return "SurfaceLayerImpl"; |
| case cc::mojom::LayerType::kTexture: |
| return "TextureLayerImpl"; |
| case cc::mojom::LayerType::kUIResource: |
| return "UIResourceLayerImpl"; |
| case cc::mojom::LayerType::kTileDisplay: |
| return "TileDisplayLayerImpl"; |
| case cc::mojom::LayerType::kViewTransitionContent: |
| return "ViewTransitionContentLayerImpl"; |
| default: |
| return "UnknownLayerType"; |
| } |
| } |
| |
| protected: |
| void VerifyLayerExists(int layer_id, bool should_exist) { |
| if (should_exist) { |
| EXPECT_NE(nullptr, GetLayerFromActiveTree(layer_id)) |
| << "Layer " << layer_id << " should exist."; |
| } else { |
| EXPECT_EQ(nullptr, GetLayerFromActiveTree(layer_id)) |
| << "Layer " << layer_id << " should not exist."; |
| } |
| } |
| |
| void VerifyLayerBounds(int layer_id, const gfx::Size& expected_bounds) { |
| cc::LayerImpl* layer = GetLayerFromActiveTree(layer_id); |
| ASSERT_NE(nullptr, layer) << "Layer " << layer_id << " not found."; |
| EXPECT_EQ(expected_bounds, layer->bounds()); |
| } |
| |
| void VerifyLayerOrder(const std::vector<int>& expected_order) { |
| cc::LayerTreeImpl* active_tree = |
| layer_context_impl_->host_impl()->active_tree(); |
| ASSERT_EQ(expected_order.size(), active_tree->NumLayers()); |
| size_t i = 0; |
| for (cc::LayerImpl* layer : *active_tree) { |
| ASSERT_LT(i, expected_order.size()); |
| EXPECT_EQ(expected_order[i], layer->id()) << "Mismatch at index " << i; |
| i++; |
| } |
| } |
| }; |
| |
| class LayerContextImplUpdateDisplayTreeUIResourceLayerTest |
| : public LayerContextImplLayerLifecycleTest { |
| protected: |
| cc::UIResourceLayerImpl* GetUIResourceLayerFromActiveTree(int layer_id) { |
| cc::LayerImpl* layer = GetLayerFromActiveTree(layer_id); |
| if (layer && layer->GetLayerType() == cc::mojom::LayerType::kUIResource) { |
| return static_cast<cc::UIResourceLayerImpl*>(layer); |
| } |
| return nullptr; |
| } |
| }; |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeUIResourceLayerTest, |
| CreateAndUpdateUIResourceLayer) { |
| const cc::UIResourceId kUpdatedUIResourceId = 321; |
| const gfx::Size kUpdatedUIResourceImageBounds(50, 100); |
| const gfx::PointF kUpdatedUIResourceUVTopLeft = gfx::PointF(0.1f, 0.2f); |
| const gfx::PointF kUpdatedUIResourceUVBottomRight = gfx::PointF(0.9f, 0.8f); |
| |
| // 1. Create the layer with default properties. |
| auto update = CreateDefaultUpdate(); |
| int layer_id = |
| AddDefaultLayerToUpdate(update.get(), cc::mojom::LayerType::kUIResource); |
| |
| auto ui_resource_extra = mojom::UIResourceLayerExtra::New(); |
| ui_resource_extra->ui_resource_id = kDefaultUIResourceId; |
| ui_resource_extra->image_bounds = kDefaultUIResourceImageBounds; |
| ui_resource_extra->uv_top_left = kDefaultUIResourceUVTopLeft; |
| ui_resource_extra->uv_bottom_right = kDefaultUIResourceUVBottomRight; |
| update->layers.back()->layer_extra = |
| mojom::LayerExtra::NewUiResourceLayerExtra(std::move(ui_resource_extra)); |
| |
| auto result1 = layer_context_impl_->DoUpdateDisplayTree(std::move(update)); |
| ASSERT_TRUE(result1.has_value()); |
| |
| cc::UIResourceLayerImpl* layer_impl = |
| GetUIResourceLayerFromActiveTree(layer_id); |
| ASSERT_TRUE(layer_impl); |
| |
| // Verify initial default properties. |
| EXPECT_EQ(layer_impl->ui_resource_id(), kDefaultUIResourceId); |
| EXPECT_EQ(layer_impl->image_bounds(), kDefaultUIResourceImageBounds); |
| EXPECT_EQ(layer_impl->uv_top_left(), kDefaultUIResourceUVTopLeft); |
| EXPECT_EQ(layer_impl->uv_bottom_right(), kDefaultUIResourceUVBottomRight); |
| |
| // 2. Update some properties of the layer. |
| auto update2 = CreateDefaultUpdate(); |
| auto ui_resource_extra2 = mojom::UIResourceLayerExtra::New(); |
| ui_resource_extra2->ui_resource_id = kUpdatedUIResourceId; |
| ui_resource_extra2->image_bounds = kUpdatedUIResourceImageBounds; |
| ui_resource_extra2->uv_top_left = kUpdatedUIResourceUVTopLeft; |
| ui_resource_extra2->uv_bottom_right = kUpdatedUIResourceUVBottomRight; |
| |
| auto layer_update2 = |
| CreateManualLayer(layer_id, cc::mojom::LayerType::kUIResource); |
| layer_update2->layer_extra = |
| mojom::LayerExtra::NewUiResourceLayerExtra(std::move(ui_resource_extra2)); |
| update2->layers.push_back(std::move(layer_update2)); |
| |
| auto result2 = layer_context_impl_->DoUpdateDisplayTree(std::move(update2)); |
| ASSERT_TRUE(result2.has_value()); |
| |
| EXPECT_EQ(layer_impl->ui_resource_id(), kUpdatedUIResourceId); |
| EXPECT_EQ(layer_impl->image_bounds(), kUpdatedUIResourceImageBounds); |
| EXPECT_EQ(layer_impl->uv_top_left(), kUpdatedUIResourceUVTopLeft); |
| EXPECT_EQ(layer_impl->uv_bottom_right(), kUpdatedUIResourceUVBottomRight); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeUIResourceLayerTest, |
| UpdateUIResourceLayerWithInvalidIdFails) { |
| constexpr int kLayerId = 2; |
| const cc::UIResourceId kValidUIResourceId = kDefaultUIResourceId; |
| const cc::UIResourceId kInvalidUIResourceId = 0; |
| |
| // Initial update: Create UIResourceLayer with a valid resource ID. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kUIResource, |
| kLayerId); |
| auto ui_resource_extra1 = mojom::UIResourceLayerExtra::New(); |
| ui_resource_extra1->ui_resource_id = kValidUIResourceId; |
| ui_resource_extra1->image_bounds = kDefaultUIResourceImageBounds; |
| update1->layers.back()->layer_extra = |
| mojom::LayerExtra::NewUiResourceLayerExtra(std::move(ui_resource_extra1)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| |
| cc::UIResourceLayerImpl* ui_resource_layer_impl = |
| static_cast<cc::UIResourceLayerImpl*>(GetLayerFromActiveTree(kLayerId)); |
| ASSERT_NE(nullptr, ui_resource_layer_impl); |
| EXPECT_EQ(ui_resource_layer_impl->ui_resource_id(), kValidUIResourceId); |
| |
| // Second update: Attempt to update with an invalid resource ID. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = |
| CreateManualLayer(kLayerId, cc::mojom::LayerType::kUIResource); |
| auto ui_resource_extra2 = mojom::UIResourceLayerExtra::New(); |
| ui_resource_extra2->ui_resource_id = kInvalidUIResourceId; // Invalid ID |
| layer_props2->layer_extra = |
| mojom::LayerExtra::NewUiResourceLayerExtra(std::move(ui_resource_extra2)); |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| auto result2 = layer_context_impl_->DoUpdateDisplayTree(std::move(update2)); |
| ASSERT_FALSE(result2.has_value()); |
| EXPECT_EQ(result2.error(), "Invalid ui_resource_id for UIResourceLayerImpl"); |
| EXPECT_EQ(ui_resource_layer_impl->ui_resource_id(), kValidUIResourceId); |
| } |
| |
| class LayerContextImplUpdateDisplayTreeNinePatchLayerTest |
| : public LayerContextImplLayerLifecycleTest { |
| protected: |
| cc::NinePatchLayerImpl* GetNinePatchLayerFromActiveTree(int layer_id) { |
| cc::LayerImpl* layer = GetLayerFromActiveTree(layer_id); |
| if (layer && layer->GetLayerType() == cc::mojom::LayerType::kNinePatch) { |
| return static_cast<cc::NinePatchLayerImpl*>(layer); |
| } |
| return nullptr; |
| } |
| }; |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeNinePatchLayerTest, |
| CreateAndUpdateNinePatchLayer) { |
| auto update = CreateDefaultUpdate(); |
| const gfx::Rect kUpdatedNinePatchAperture(11, 12, 13, 14); |
| const gfx::Rect kUpdatedNinePatchBorder(15, 16, 17, 18); |
| const gfx::Rect kUpdatedNinePatchLayerOcclusion(19, 20, 21, 22); |
| const bool kUpdatedNinePatchFillCenter = true; |
| const cc::UIResourceId kUpdatedNinePatchUIResourceId = 456; |
| const gfx::Size kUpdatedNinePatchImageBounds(300, 400); |
| const gfx::PointF kUpdatedNinePatchUVTopLeft(0.1f, 0.2f); |
| const gfx::PointF kUpdatedNinePatchUVBottomRight(0.8f, 0.9f); |
| int layer_id = |
| AddDefaultLayerToUpdate(update.get(), cc::mojom::LayerType::kNinePatch); |
| |
| auto nine_patch_extra = mojom::NinePatchLayerExtra::New(); |
| nine_patch_extra->image_aperture = kDefaultNinePatchAperture; |
| nine_patch_extra->border = kDefaultNinePatchBorder; |
| nine_patch_extra->layer_occlusion = kDefaultNinePatchLayerOcclusion; |
| nine_patch_extra->fill_center = kDefaultNinePatchFillCenter; |
| nine_patch_extra->ui_resource_id = kDefaultNinePatchUIResourceId; |
| nine_patch_extra->image_bounds = kDefaultNinePatchImageBounds; |
| nine_patch_extra->uv_top_left = kDefaultNinePatchUVTopLeft; |
| nine_patch_extra->uv_bottom_right = kDefaultNinePatchUVBottomRight; |
| update->layers.back()->layer_extra = |
| mojom::LayerExtra::NewNinePatchLayerExtra(std::move(nine_patch_extra)); |
| |
| auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update)); |
| ASSERT_TRUE(result.has_value()); |
| |
| cc::NinePatchLayerImpl* nine_patch_layer = |
| GetNinePatchLayerFromActiveTree(layer_id); |
| ASSERT_TRUE(nine_patch_layer); |
| |
| EXPECT_EQ(nine_patch_layer->quad_generator().image_aperture(), |
| kDefaultNinePatchAperture); |
| EXPECT_EQ(nine_patch_layer->quad_generator().border(), |
| kDefaultNinePatchBorder); |
| EXPECT_EQ(nine_patch_layer->quad_generator().output_occlusion(), |
| kDefaultNinePatchLayerOcclusion); |
| EXPECT_EQ(nine_patch_layer->quad_generator().fill_center(), |
| kDefaultNinePatchFillCenter); |
| EXPECT_EQ(nine_patch_layer->ui_resource_id(), kDefaultNinePatchUIResourceId); |
| EXPECT_EQ(nine_patch_layer->image_bounds(), kDefaultNinePatchImageBounds); |
| EXPECT_EQ(nine_patch_layer->uv_top_left(), kDefaultNinePatchUVTopLeft); |
| EXPECT_EQ(nine_patch_layer->uv_bottom_right(), |
| kDefaultNinePatchUVBottomRight); |
| |
| // Update all NinePatchLayerExtra properties. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = |
| CreateManualLayer(layer_id, cc::mojom::LayerType::kNinePatch); |
| auto nine_patch_extra2 = mojom::NinePatchLayerExtra::New(); |
| nine_patch_extra2->image_aperture = kUpdatedNinePatchAperture; |
| nine_patch_extra2->border = kUpdatedNinePatchBorder; |
| nine_patch_extra2->layer_occlusion = kUpdatedNinePatchLayerOcclusion; |
| nine_patch_extra2->fill_center = kUpdatedNinePatchFillCenter; |
| nine_patch_extra2->ui_resource_id = kUpdatedNinePatchUIResourceId; |
| nine_patch_extra2->image_bounds = kUpdatedNinePatchImageBounds; |
| nine_patch_extra2->uv_top_left = kUpdatedNinePatchUVTopLeft; |
| nine_patch_extra2->uv_bottom_right = kUpdatedNinePatchUVBottomRight; |
| layer_props2->layer_extra = |
| mojom::LayerExtra::NewNinePatchLayerExtra(std::move(nine_patch_extra2)); |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| |
| EXPECT_EQ(nine_patch_layer->quad_generator().image_aperture(), |
| kUpdatedNinePatchAperture); |
| EXPECT_EQ(nine_patch_layer->quad_generator().border(), |
| kUpdatedNinePatchBorder); |
| EXPECT_EQ(nine_patch_layer->quad_generator().output_occlusion(), |
| kUpdatedNinePatchLayerOcclusion); |
| EXPECT_EQ(nine_patch_layer->quad_generator().fill_center(), |
| kUpdatedNinePatchFillCenter); |
| EXPECT_EQ(nine_patch_layer->ui_resource_id(), kUpdatedNinePatchUIResourceId); |
| EXPECT_EQ(nine_patch_layer->image_bounds(), kUpdatedNinePatchImageBounds); |
| EXPECT_EQ(nine_patch_layer->uv_top_left(), kUpdatedNinePatchUVTopLeft); |
| EXPECT_EQ(nine_patch_layer->uv_bottom_right(), |
| kUpdatedNinePatchUVBottomRight); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeNinePatchLayerTest, |
| UpdateNinePatchLayerWithInvalidUIResourceIdFails) { |
| constexpr int kLayerId = 2; |
| const cc::UIResourceId kValidUIResourceId = kDefaultNinePatchUIResourceId; |
| const cc::UIResourceId kInvalidUIResourceId = 0; |
| |
| // Initial update: Create NinePatchLayer with a valid resource ID. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kNinePatch, |
| kLayerId); |
| auto nine_patch_extra1 = mojom::NinePatchLayerExtra::New(); |
| nine_patch_extra1->ui_resource_id = kValidUIResourceId; |
| // Set other required fields for a valid NinePatchLayer |
| nine_patch_extra1->image_aperture = kDefaultNinePatchAperture; |
| nine_patch_extra1->border = kDefaultNinePatchBorder; |
| update1->layers.back()->layer_extra = |
| mojom::LayerExtra::NewNinePatchLayerExtra(std::move(nine_patch_extra1)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| |
| cc::NinePatchLayerImpl* nine_patch_layer_impl = |
| GetNinePatchLayerFromActiveTree(kLayerId); |
| ASSERT_NE(nullptr, nine_patch_layer_impl); |
| EXPECT_EQ(nine_patch_layer_impl->ui_resource_id(), kValidUIResourceId); |
| |
| // Second update: Attempt to update with an invalid resource ID. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = |
| CreateManualLayer(kLayerId, cc::mojom::LayerType::kNinePatch); |
| auto nine_patch_extra2 = mojom::NinePatchLayerExtra::New(); |
| nine_patch_extra2->ui_resource_id = kInvalidUIResourceId; // Invalid ID |
| layer_props2->layer_extra = |
| mojom::LayerExtra::NewNinePatchLayerExtra(std::move(nine_patch_extra2)); |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| auto result2 = layer_context_impl_->DoUpdateDisplayTree(std::move(update2)); |
| ASSERT_FALSE(result2.has_value()); |
| EXPECT_EQ(result2.error(), "Invalid ui_resource_id for NinePatchLayerImpl"); |
| EXPECT_EQ(nine_patch_layer_impl->ui_resource_id(), kValidUIResourceId); |
| } |
| |
| TEST_F(LayerContextImplLayerLifecycleTest, LayerLifecycleAndEdgeCases) { |
| constexpr int kLayerId1 = 2; // Start after default root layer (ID 1). |
| constexpr int kLayerId2 = 3; |
| constexpr int kLayerId3 = 4; |
| constexpr int kNonExistentLayerId = 99; |
| |
| // Test Case 1: Basic Layer Lifecycle (Create, Update Bounds, Remove) |
| // Update 1: Create Layer ID kLayerId1. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kLayer, |
| kLayerId1); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| VerifyLayerExists(kLayerId1, true); |
| VerifyLayerBounds(kLayerId1, kDefaultLayerBounds); // Default bounds |
| |
| // Update 2: Update bounds of Layer ID kLayerId1. |
| auto update2 = CreateDefaultUpdate(); |
| const gfx::Size kUpdatedBounds1(20, 20); |
| update2->layers.push_back(CreateManualLayer( |
| kLayerId1, cc::mojom::LayerType::kLayer, kUpdatedBounds1)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| VerifyLayerExists(kLayerId1, true); |
| VerifyLayerBounds(kLayerId1, kUpdatedBounds1); |
| |
| // Update 3: Remove Layer ID kLayerId1. |
| auto update3 = CreateDefaultUpdate(); |
| RemoveLayerInUpdate(update3.get(), kLayerId1); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| VerifyLayerExists(kLayerId1, false); |
| |
| // Test Case 2: Multiple Layers and Interleaved Operations |
| // Update 4: Re-Create Layer ID kLayerId1. |
| auto update4 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update4.get(), cc::mojom::LayerType::kLayer, |
| kLayerId1); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update4)).has_value()); |
| VerifyLayerExists(kLayerId1, true); |
| VerifyLayerOrder({1, kLayerId1}); |
| |
| // Update 5: Create Layer ID kLayerId2. |
| auto update5 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update5.get(), cc::mojom::LayerType::kLayer, |
| kLayerId2); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update5)).has_value()); |
| VerifyLayerExists(kLayerId1, true); |
| VerifyLayerExists(kLayerId2, true); |
| VerifyLayerOrder({1, kLayerId1, kLayerId2}); |
| |
| // Update 6: Update Layer ID kLayerId1. |
| auto update6 = CreateDefaultUpdate(); |
| const gfx::Size kUpdatedBounds2(30, 30); |
| update6->layers.push_back(CreateManualLayer( |
| kLayerId1, cc::mojom::LayerType::kLayer, kUpdatedBounds2)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update6)).has_value()); |
| VerifyLayerBounds(kLayerId1, kUpdatedBounds2); |
| VerifyLayerBounds(kLayerId2, kDefaultLayerBounds); // Unaffected |
| |
| // Update 7: Remove Layer ID kLayerId1. |
| auto update7 = CreateDefaultUpdate(); |
| RemoveLayerInUpdate(update7.get(), kLayerId1); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update7)).has_value()); |
| VerifyLayerExists(kLayerId1, false); |
| VerifyLayerExists(kLayerId2, true); |
| VerifyLayerOrder({1, kLayerId2}); |
| |
| // Update 8: Remove Layer ID kLayerId2. |
| auto update8 = CreateDefaultUpdate(); |
| RemoveLayerInUpdate(update8.get(), kLayerId2); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update8)).has_value()); |
| VerifyLayerExists(kLayerId2, false); |
| VerifyLayerOrder({1}); |
| |
| // Test Case 3: Updating a Never Existing Layer should fail |
| // Update 9: Create kLayerId1 again. |
| auto update9 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update9.get(), cc::mojom::LayerType::kLayer, |
| kLayerId1); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update9)).has_value()); |
| VerifyLayerExists(kLayerId1, true); |
| |
| // Update 10: Attempt to update kNonExistentLayerId. |
| auto update10 = CreateDefaultUpdate(); |
| const gfx::Size kUpdatedBounds3(5, 5); |
| update10->layers.push_back( |
| CreateManualLayer(kNonExistentLayerId, cc::mojom::LayerType::kLayer, |
| kUpdatedBounds3)); // Update non-existent |
| auto result10 = layer_context_impl_->DoUpdateDisplayTree(std::move(update10)); |
| ASSERT_FALSE(result10.has_value()); |
| EXPECT_EQ(result10.error(), "Invalid layer ID"); |
| VerifyLayerExists(kLayerId1, true); // Unaffected |
| VerifyLayerBounds( |
| kLayerId1, |
| kDefaultLayerBounds); // Should be reset to default or last valid |
| VerifyLayerExists(kNonExistentLayerId, false); |
| VerifyLayerOrder({1, kLayerId1}); |
| |
| // Test Case 4: Updating on Previously Removed Layer shoulf fail |
| // Update 11: Remove kLayerId1. |
| auto update11 = CreateDefaultUpdate(); |
| RemoveLayerInUpdate(update11.get(), kLayerId1); |
| EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update11)) |
| .has_value()); |
| VerifyLayerExists(kLayerId1, false); |
| |
| // Update 12: Attempt to update kLayerId1 (removed). |
| auto update12 = CreateDefaultUpdate(); |
| const gfx::Size kUpdatedBounds4(40, 40); |
| update12->layers.push_back(CreateManualLayer( |
| kLayerId1, cc::mojom::LayerType::kLayer, kUpdatedBounds4)); |
| auto result12 = layer_context_impl_->DoUpdateDisplayTree(std::move(update12)); |
| ASSERT_FALSE(result12.has_value()); |
| EXPECT_EQ(result12.error(), "Invalid layer ID"); |
| |
| VerifyLayerExists(kLayerId1, false); // Should not be re-created |
| |
| // Test Case 5: Duplicate or non existent layer IDs in the Layer Order should |
| // fail. Update 13: Create kLayerId1 again. |
| auto update13 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update13.get(), cc::mojom::LayerType::kLayer, |
| kLayerId1); |
| EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update13)) |
| .has_value()); |
| VerifyLayerExists(kLayerId1, true); |
| VerifyLayerBounds(kLayerId1, kDefaultLayerBounds); |
| VerifyLayerOrder({1, kLayerId1}); |
| |
| // Update 14: Try to add another instance of kLayerId1 with different bounds. |
| auto update14 = CreateDefaultUpdate(); |
| const gfx::Size kUpdatedBounds5(50, 50); |
| update14->layers.push_back(CreateManualLayer( |
| kLayerId1, cc::mojom::LayerType::kLayer, kUpdatedBounds5)); |
| update14->layer_order = layer_order_; |
| update14->layer_order->push_back(kLayerId1); |
| |
| auto result14 = layer_context_impl_->DoUpdateDisplayTree(std::move(update14)); |
| ASSERT_FALSE(result14.has_value()); |
| EXPECT_EQ(result14.error(), "Invalid or duplicate layer ID"); |
| VerifyLayerExists(kLayerId1, true); |
| VerifyLayerBounds(kLayerId1, kUpdatedBounds5); // Layer should be updated |
| VerifyLayerOrder({1, kLayerId1}); // Layer Order should not update |
| |
| // Update 15: Try to add a Non Existent layer to Layer Order |
| auto update15 = CreateDefaultUpdate(); |
| update15->layer_order = layer_order_; |
| update15->layer_order->push_back(kNonExistentLayerId); |
| |
| auto result15 = layer_context_impl_->DoUpdateDisplayTree(std::move(update15)); |
| ASSERT_FALSE(result15.has_value()); |
| EXPECT_EQ(result15.error(), "Invalid or duplicate layer ID"); |
| |
| // Test Case 7: Invalid Property Tree Indices on Creation |
| // Update 16: Try to send a layer update with a1valid transform node index |
| auto update16 = CreateDefaultUpdate(); |
| update16->layers.push_back( |
| CreateManualLayer(kLayerId2, cc::mojom::LayerType::kLayer, |
| kDefaultLayerBounds, /*transform_idx=*/999)); |
| update16->layer_order = layer_order_; |
| update16->layer_order->push_back(kLayerId2); |
| EXPECT_FALSE(layer_context_impl_->DoUpdateDisplayTree(std::move(update16)) |
| .has_value()); |
| VerifyLayerExists(kLayerId2, false); // Should not have been created |
| |
| // Test Case 8: Layer Order Manipulation |
| // Update 17: Re-Create 1, kLayerId1, kLayerId2. Order [1, kLayerId1, |
| // kLayerId2] |
| auto update17 = CreateDefaultUpdate(); |
| layer_order_.clear(); |
| AddDefaultLayerToUpdate(update17.get(), cc::mojom::LayerType::kLayer, 1); |
| AddDefaultLayerToUpdate(update17.get(), cc::mojom::LayerType::kLayer, |
| kLayerId1); |
| AddDefaultLayerToUpdate(update17.get(), cc::mojom::LayerType::kLayer, |
| kLayerId2); |
| EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update17)) |
| .has_value()); |
| VerifyLayerOrder({1, kLayerId1, kLayerId2}); |
| |
| // Update 18: Change order to [1, kLayerId2, kLayerId1] |
| auto update18 = CreateDefaultUpdate(); |
| layer_order_.clear(); |
| layer_order_.push_back(1); |
| layer_order_.push_back(kLayerId2); |
| layer_order_.push_back(kLayerId1); |
| update18->layer_order = layer_order_; |
| EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update18)) |
| .has_value()); |
| VerifyLayerOrder({1, kLayerId2, kLayerId1}); |
| |
| // Update 19: Create kLayerId3. Order [1, kLayerId2, kLayerId3, kLayerId1] |
| auto update19 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update19.get(), cc::mojom::LayerType::kLayer, |
| kLayerId3); // kLayerId3 |
| layer_order_.clear(); |
| layer_order_.push_back(1); |
| layer_order_.push_back(kLayerId2); |
| layer_order_.push_back(kLayerId3); |
| layer_order_.push_back(kLayerId1); |
| update19->layer_order = layer_order_; |
| EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update19)) |
| .has_value()); |
| VerifyLayerOrder({1, kLayerId2, kLayerId3, kLayerId1}); |
| |
| // Update 20: Remove kLayerId2. Order [1, kLayerId3, kLayerId1] |
| auto update20 = CreateDefaultUpdate(); |
| layer_order_.clear(); |
| layer_order_.push_back(1); |
| layer_order_.push_back(kLayerId3); |
| layer_order_.push_back(kLayerId1); |
| update20->layer_order = layer_order_; |
| EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update20)) |
| .has_value()); |
| VerifyLayerExists(kLayerId2, false); |
| VerifyLayerOrder({1, kLayerId3, kLayerId1}); |
| } |
| |
| TEST_F(LayerContextImplLayerLifecycleTest, CreateLayersOfAllTypes) { |
| auto update = CreateDefaultUpdate(); |
| |
| // Test a subset of layer types that have distinct LayerImpl classes or |
| // specific handling in CreateLayer. |
| const std::vector<cc::mojom::LayerType> types_to_test = { |
| cc::mojom::LayerType::kLayer, |
| cc::mojom::LayerType::kMirror, |
| cc::mojom::LayerType::kNinePatchThumbScrollbar, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| cc::mojom::LayerType::kTileDisplay, |
| cc::mojom::LayerType::kSolidColor, |
| cc::mojom::LayerType::kSolidColorScrollbar, |
| cc::mojom::LayerType::kSurface, |
| cc::mojom::LayerType::kTexture, |
| cc::mojom::LayerType::kUIResource, |
| cc::mojom::LayerType::kViewTransitionContent, |
| // Add other relevant types here. |
| }; |
| |
| std::vector<int> layer_ids; |
| for (cc::mojom::LayerType type : types_to_test) { |
| int layer_id = next_layer_id_++; |
| layer_ids.push_back(layer_id); |
| auto layer = CreateManualLayer(layer_id, type); |
| switch (type) { |
| case cc::mojom::LayerType::kMirror: { |
| auto extra = mojom::MirrorLayerExtra::New(); |
| // Mirroring the root layer (ID 1) by default for simplicity. |
| extra->mirrored_layer_id = 1; |
| layer->layer_extra = |
| mojom::LayerExtra::NewMirrorLayerExtra(std::move(extra)); |
| break; |
| } |
| case cc::mojom::LayerType::kNinePatchThumbScrollbar: { |
| auto extra = mojom::NinePatchThumbScrollbarLayerExtra::New(); |
| extra->scrollbar_base_extra = mojom::ScrollbarLayerBaseExtra::New(); |
| layer->layer_extra = |
| mojom::LayerExtra::NewNinePatchThumbScrollbarLayerExtra( |
| std::move(extra)); |
| break; |
| } |
| case cc::mojom::LayerType::kPaintedScrollbar: { |
| auto extra = mojom::PaintedScrollbarLayerExtra::New(); |
| extra->scrollbar_base_extra = mojom::ScrollbarLayerBaseExtra::New(); |
| layer->layer_extra = |
| mojom::LayerExtra::NewPaintedScrollbarLayerExtra(std::move(extra)); |
| break; |
| } |
| case cc::mojom::LayerType::kSolidColorScrollbar: { |
| auto extra = mojom::SolidColorScrollbarLayerExtra::New(); |
| extra->scrollbar_base_extra = mojom::ScrollbarLayerBaseExtra::New(); |
| layer->layer_extra = |
| mojom::LayerExtra::NewSolidColorScrollbarLayerExtra( |
| std::move(extra)); |
| break; |
| } |
| case cc::mojom::LayerType::kSurface: { |
| auto extra = mojom::SurfaceLayerExtra::New(); |
| extra->surface_range = kDefaultSurfaceRange; |
| extra->deadline_in_frames = 0u; |
| layer->layer_extra = |
| mojom::LayerExtra::NewSurfaceLayerExtra(std::move(extra)); |
| break; |
| } |
| case cc::mojom::LayerType::kTexture: { |
| auto extra = mojom::TextureLayerExtra::New(); |
| // TextureLayer can have an optional TransferableResource. |
| // For this basic creation test, leaving it null is fine. |
| layer->layer_extra = |
| mojom::LayerExtra::NewTextureLayerExtra(std::move(extra)); |
| break; |
| } |
| case cc::mojom::LayerType::kViewTransitionContent: { |
| auto extra = mojom::ViewTransitionContentLayerExtra::New(); |
| extra->resource_id = ViewTransitionElementResourceId( |
| blink::ViewTransitionToken(), 1, false); |
| layer->layer_extra = |
| mojom::LayerExtra::NewViewTransitionContentLayerExtra( |
| std::move(extra)); |
| break; |
| } |
| default: |
| // No layer_extra needed for other types in this test. |
| break; |
| } |
| update->layers.push_back(std::move(layer)); |
| layer_order_.push_back(layer_id); |
| } |
| update->layer_order = layer_order_; |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update)).has_value()); |
| |
| for (size_t i = 0; i < types_to_test.size(); ++i) { |
| VerifyLayerExists(layer_ids[i], true); |
| cc::LayerImpl* layer = GetLayerFromActiveTree(layer_ids[i]); |
| ASSERT_NE(nullptr, layer); |
| EXPECT_EQ(layer->GetLayerType(), types_to_test[i]); |
| } |
| } |
| |
| TEST_F(LayerContextImplLayerLifecycleTest, UpdateMultipleLayerProperties) { |
| const gfx::Size kUpdatedBounds(50, 50); |
| |
| auto update = CreateDefaultUpdate(); |
| int layer_id1 = AddDefaultLayerToUpdate(update.get()); |
| int layer_id2 = AddDefaultLayerToUpdate(update.get()); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update)).has_value()); |
| |
| auto update_props = CreateDefaultUpdate(); |
| auto layer1_props = CreateManualLayer(layer_id1); |
| layer1_props->bounds = kUpdatedBounds; |
| layer1_props->contents_opaque = true; |
| layer1_props->contents_opaque_for_text = true; |
| // If contents_opaque is true, safe_opaque_background_color must be opaque. |
| layer1_props->safe_opaque_background_color = SkColors::kRed; |
| layer1_props->background_color = SkColors::kRed; |
| layer1_props->transform_tree_index = cc::kRootPropertyNodeId; |
| update_props->layers.push_back(std::move(layer1_props)); |
| |
| auto layer2_props = CreateManualLayer(layer_id2); |
| layer2_props->is_drawable = false; |
| layer2_props->clip_tree_index = cc::kSecondaryRootPropertyNodeId; |
| layer2_props->effect_tree_index = cc::kRootPropertyNodeId; |
| update_props->layers.push_back(std::move(layer2_props)); |
| |
| EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update_props)) |
| .has_value()); |
| |
| cc::LayerImpl* layer1_impl = GetLayerFromActiveTree(layer_id1); |
| ASSERT_NE(nullptr, layer1_impl); |
| EXPECT_EQ(layer1_impl->bounds(), kUpdatedBounds); |
| EXPECT_TRUE(layer1_impl->contents_opaque()); |
| EXPECT_TRUE(layer1_impl->contents_opaque_for_text()); |
| EXPECT_EQ(layer1_impl->background_color(), SkColors::kRed); |
| EXPECT_EQ(layer1_impl->transform_tree_index(), cc::kRootPropertyNodeId); |
| |
| cc::LayerImpl* layer2_impl = GetLayerFromActiveTree(layer_id2); |
| ASSERT_NE(nullptr, layer2_impl); |
| EXPECT_FALSE(layer2_impl->draws_content()); |
| EXPECT_EQ(layer2_impl->clip_tree_index(), cc::kSecondaryRootPropertyNodeId); |
| EXPECT_EQ(layer2_impl->effect_tree_index(), cc::kRootPropertyNodeId); |
| } |
| |
| TEST_F(LayerContextImplLayerLifecycleTest, ReorderLayers) { |
| auto update = CreateDefaultUpdate(); |
| int layer_id1 = AddDefaultLayerToUpdate(update.get()); |
| int layer_id2 = AddDefaultLayerToUpdate(update.get()); |
| int layer_id3 = AddDefaultLayerToUpdate(update.get()); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update)).has_value()); |
| VerifyLayerOrder({1, layer_id1, layer_id2, layer_id3}); |
| |
| // Move layer_id1 to the end. |
| auto update_reorder1 = CreateDefaultUpdate(); |
| layer_order_ = {1, layer_id2, layer_id3, layer_id1}; |
| update_reorder1->layer_order = layer_order_; |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update_reorder1)) |
| .has_value()); |
| VerifyLayerOrder({1, layer_id2, layer_id3, layer_id1}); |
| |
| // Move layer_id3 to the beginning (after root). |
| auto update_reorder2 = CreateDefaultUpdate(); |
| layer_order_ = {1, layer_id3, layer_id2, layer_id1}; |
| update_reorder2->layer_order = layer_order_; |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update_reorder2)) |
| .has_value()); |
| VerifyLayerOrder({1, layer_id3, layer_id2, layer_id1}); |
| } |
| |
| TEST_F(LayerContextImplLayerLifecycleTest, RemoveLayers) { |
| auto update = CreateDefaultUpdate(); |
| int layer_id1 = AddDefaultLayerToUpdate(update.get()); |
| int layer_id2 = AddDefaultLayerToUpdate(update.get()); |
| int layer_id3 = AddDefaultLayerToUpdate(update.get()); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update)).has_value()); |
| VerifyLayerOrder({1, layer_id1, layer_id2, layer_id3}); |
| |
| // Remove from the middle. |
| auto update_remove1 = CreateDefaultUpdate(); |
| RemoveLayerInUpdate(update_remove1.get(), layer_id2); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update_remove1)) |
| .has_value()); |
| VerifyLayerExists(layer_id2, false); |
| VerifyLayerOrder({1, layer_id1, layer_id3}); |
| |
| // Remove from the beginning (after root). |
| auto update_remove2 = CreateDefaultUpdate(); |
| RemoveLayerInUpdate(update_remove2.get(), layer_id1); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update_remove2)) |
| .has_value()); |
| VerifyLayerExists(layer_id1, false); |
| VerifyLayerOrder({1, layer_id3}); |
| |
| // Remove from the end. |
| auto update_remove3 = CreateDefaultUpdate(); |
| RemoveLayerInUpdate(update_remove3.get(), layer_id3); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update_remove3)) |
| .has_value()); |
| VerifyLayerExists(layer_id3, false); |
| VerifyLayerOrder({1}); |
| } |
| |
| TEST_F(LayerContextImplLayerLifecycleTest, LayerPropertyChangedFlags) { |
| auto update = CreateDefaultUpdate(); |
| int layer_id = AddDefaultLayerToUpdate(update.get()); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update)).has_value()); |
| |
| // Test layer_property_changed_not_from_property_trees |
| auto update_flag1 = CreateDefaultUpdate(); |
| auto layer_props1 = CreateManualLayer(layer_id); |
| layer_props1->layer_property_changed_not_from_property_trees = true; |
| update_flag1->layers.push_back(std::move(layer_props1)); |
| EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update_flag1)) |
| .has_value()); |
| cc::LayerImpl* layer_impl_flag1 = GetLayerFromActiveTree(layer_id); |
| ASSERT_NE(nullptr, layer_impl_flag1); |
| EXPECT_TRUE(layer_impl_flag1->LayerPropertyChangedNotFromPropertyTrees()); |
| |
| // Test layer_property_changed_from_property_trees |
| auto update_flag2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(layer_id); |
| layer_props2->layer_property_changed_from_property_trees = true; |
| update_flag2->layers.push_back(std::move(layer_props2)); |
| EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update_flag2)) |
| .has_value()); |
| // This flag is reset after processing, so we can't directly verify it here |
| // without more complex state tracking or inspecting internal LayerImpl |
| // states that are affected by it. For now, we ensure the update passes. |
| // A more thorough test would involve checking if draw properties were |
| // actually updated. |
| ASSERT_NE(nullptr, GetLayerFromActiveTree(layer_id)); |
| } |
| |
| TEST_F(LayerContextImplLayerLifecycleTest, RareProperties) { |
| auto update = CreateDefaultUpdate(); |
| int layer_id = AddDefaultLayerToUpdate(update.get()); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update)).has_value()); |
| |
| const auto kFirstId = RegionCaptureCropId::CreateRandom(); |
| const auto kSecondId = RegionCaptureCropId::CreateRandom(); |
| const RegionCaptureBounds kLayerBounds{ |
| {{kFirstId, gfx::Rect{0, 0, 250, 250}}, {kSecondId, gfx::Rect{}}}}; |
| |
| auto update_rare = CreateDefaultUpdate(); |
| auto layer_props = CreateManualLayer(layer_id); |
| layer_props->rare_properties = mojom::RareProperties::New(); |
| layer_props->rare_properties->filter_quality = |
| cc::PaintFlags::FilterQuality::kMedium; |
| layer_props->rare_properties->dynamic_range_limit = |
| cc::PaintFlags::DynamicRangeLimitMixture(1.f, 0.5f); |
| layer_props->rare_properties->capture_bounds = kLayerBounds; |
| update_rare->layers.push_back(std::move(layer_props)); |
| |
| EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update_rare)) |
| .has_value()); |
| |
| cc::LayerImpl* layer_impl_rare = GetLayerFromActiveTree(layer_id); |
| ASSERT_NE(nullptr, layer_impl_rare); |
| EXPECT_EQ(layer_impl_rare->GetFilterQuality(), |
| cc::PaintFlags::FilterQuality::kMedium); |
| EXPECT_EQ(layer_impl_rare->GetDynamicRangeLimit(), |
| cc::PaintFlags::DynamicRangeLimitMixture(1.f, 0.5f)); |
| ASSERT_TRUE(layer_impl_rare->capture_bounds()); |
| EXPECT_EQ(*layer_impl_rare->capture_bounds(), kLayerBounds); |
| } |
| |
| TEST_F(LayerContextImplLayerLifecycleTest, ContentsOpaqueFlags) { |
| ResetTestState(); |
| auto update = CreateDefaultUpdate(); |
| int layer_id = AddDefaultLayerToUpdate(update.get()); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update)).has_value()); |
| |
| // Valid: contents_opaque = true, contents_opaque_for_text = true |
| auto update_valid1 = CreateDefaultUpdate(); |
| auto layer_props_valid1 = CreateManualLayer(layer_id); |
| layer_props_valid1->contents_opaque = true; |
| layer_props_valid1->contents_opaque_for_text = true; |
| // If contents_opaque is true, safe_opaque_background_color must be opaque. |
| layer_props_valid1->safe_opaque_background_color = SkColors::kRed; |
| update_valid1->layers.push_back(std::move(layer_props_valid1)); |
| EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update_valid1)) |
| .has_value()); |
| cc::LayerImpl* layer_impl_valid1 = GetLayerFromActiveTree(layer_id); |
| ASSERT_NE(nullptr, layer_impl_valid1); |
| EXPECT_TRUE(layer_impl_valid1->contents_opaque()); |
| EXPECT_TRUE(layer_impl_valid1->contents_opaque_for_text()); |
| |
| // Invalid: contents_opaque = true, contents_opaque_for_text = false |
| auto update_invalid = CreateDefaultUpdate(); |
| auto layer_props_invalid = CreateManualLayer(layer_id); |
| layer_props_invalid->contents_opaque = true; |
| layer_props_invalid->contents_opaque_for_text = false; |
| // This would also be invalid if contents_opaque_for_text was not opaque. |
| layer_props_invalid->safe_opaque_background_color = SkColors::kGreen; |
| update_invalid->layers.push_back(std::move(layer_props_invalid)); |
| auto result_invalid = |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update_invalid)); |
| ASSERT_FALSE(result_invalid.has_value()); |
| EXPECT_EQ(result_invalid.error(), |
| "Invalid contents_opaque_for_text: cannot be false if " |
| "contents_opaque is true."); |
| // Verify properties remain from the last valid update |
| cc::LayerImpl* layer_impl_invalid = GetLayerFromActiveTree(layer_id); |
| ASSERT_NE(nullptr, layer_impl_invalid); |
| EXPECT_TRUE(layer_impl_invalid->contents_opaque()); |
| EXPECT_TRUE(layer_impl_invalid->contents_opaque_for_text()); |
| |
| // Valid: contents_opaque = false, contents_opaque_for_text = true |
| auto update_valid2 = CreateDefaultUpdate(); |
| auto layer_props_valid2 = CreateManualLayer(layer_id); |
| layer_props_valid2->contents_opaque = false; |
| layer_props_valid2->contents_opaque_for_text = true; |
| // If contents_opaque is false, safe_opaque_background_color must be |
| // transparent. |
| layer_props_valid2->safe_opaque_background_color = SkColors::kTransparent; |
| update_valid2->layers.push_back(std::move(layer_props_valid2)); |
| EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update_valid2)) |
| .has_value()); |
| cc::LayerImpl* layer_impl_valid2 = GetLayerFromActiveTree(layer_id); |
| ASSERT_NE(nullptr, layer_impl_valid2); |
| EXPECT_FALSE(layer_impl_valid2->contents_opaque()); |
| EXPECT_TRUE(layer_impl_valid2->contents_opaque_for_text()); |
| |
| // Valid: contents_opaque = false, contents_opaque_for_text = false |
| auto update_valid3 = CreateDefaultUpdate(); |
| auto layer_props_valid3 = CreateManualLayer(layer_id); |
| layer_props_valid3->contents_opaque = false; |
| layer_props_valid3->contents_opaque_for_text = false; |
| // If contents_opaque is false, safe_opaque_background_color must be |
| // transparent. |
| layer_props_valid3->safe_opaque_background_color = SkColors::kTransparent; |
| update_valid3->layers.push_back(std::move(layer_props_valid3)); |
| EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update_valid3)) |
| .has_value()); |
| cc::LayerImpl* layer_impl_valid3 = GetLayerFromActiveTree(layer_id); |
| ASSERT_NE(nullptr, layer_impl_valid3); |
| EXPECT_FALSE(layer_impl_valid3->contents_opaque()); |
| EXPECT_FALSE(layer_impl_valid3->contents_opaque_for_text()); |
| } |
| |
| const cc::mojom::LayerType kLayerTypesWithSpecificExtras[] = { |
| cc::mojom::LayerType::kMirror, |
| cc::mojom::LayerType::kNinePatch, |
| cc::mojom::LayerType::kNinePatchThumbScrollbar, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| cc::mojom::LayerType::kSolidColorScrollbar, |
| cc::mojom::LayerType::kSurface, |
| cc::mojom::LayerType::kTexture, |
| cc::mojom::LayerType::kTileDisplay, |
| cc::mojom::LayerType::kUIResource, |
| cc::mojom::LayerType::kViewTransitionContent, |
| }; |
| |
| TEST_F(LayerContextImplLayerLifecycleTest, MissingLayerExtra) { |
| for (cc::mojom::LayerType type : kLayerTypesWithSpecificExtras) { |
| SCOPED_TRACE(testing::Message() |
| << "Testing LayerType: " << GetLayerImplName(type)); |
| ResetTestState(); |
| // Create a valid root layer first. |
| auto initial_update = CreateDefaultUpdate(); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(initial_update)) |
| .has_value()); |
| |
| auto update_missing_extra = CreateDefaultUpdate(); |
| int layer_id = next_layer_id_++; |
| // Create the layer manually without setting layer_extra. |
| auto layer = CreateManualLayer(layer_id, type); |
| // Ensure layer_extra is indeed null. |
| layer->layer_extra = nullptr; |
| |
| update_missing_extra->layers.push_back(std::move(layer)); |
| update_missing_extra->layer_order = layer_order_; |
| update_missing_extra->layer_order->push_back(layer_id); |
| |
| auto result = layer_context_impl_->DoUpdateDisplayTree( |
| std::move(update_missing_extra)); |
| ASSERT_FALSE(result.has_value()); |
| EXPECT_EQ(result.error(), |
| "Invalid layer_extra type for " + GetLayerImplName(type)); |
| } |
| } |
| |
| class LayerContextImplLayerExtraTypeValidationTest |
| : public LayerContextImplLayerLifecycleTest, |
| public testing::WithParamInterface<cc::mojom::LayerType> {}; |
| |
| TEST_P(LayerContextImplLayerExtraTypeValidationTest, MismatchedLayerExtra) { |
| constexpr int kLayerId = 2; |
| cc::mojom::LayerType layer_type_under_test = GetParam(); |
| |
| for (cc::mojom::LayerType mismatching_extra_provider_type : |
| kLayerTypesWithSpecificExtras) { |
| if (layer_type_under_test == mismatching_extra_provider_type) { |
| continue; |
| } |
| |
| SCOPED_TRACE(testing::Message() |
| << "LayerTypeUnderTest: " |
| << GetLayerImplName(layer_type_under_test) |
| << ", MismatchingExtraProviderType: " |
| << GetLayerImplName(mismatching_extra_provider_type)); |
| |
| // Test Creation with mismatched extra |
| ResetTestState(); |
| auto update_create = CreateDefaultUpdate(); |
| auto layer_props_create = |
| CreateManualLayer(kLayerId, layer_type_under_test); |
| layer_props_create->layer_extra = |
| CreateDefaultLayerExtra(mismatching_extra_provider_type); |
| update_create->layers.push_back(std::move(layer_props_create)); |
| update_create->layer_order = {1, kLayerId}; // Root layer (1) + test layer |
| |
| auto result_create = |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update_create)); |
| ASSERT_FALSE(result_create.has_value()); |
| EXPECT_THAT(result_create.error(), |
| testing::StartsWith("Invalid layer_extra type for " + |
| GetLayerImplName(layer_type_under_test))); |
| |
| // Test Update with mismatched extra |
| ResetTestState(); |
| auto initial_update = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(initial_update.get(), layer_type_under_test, |
| kLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(initial_update)) |
| .has_value()); |
| |
| auto update_props = CreateDefaultUpdate(); |
| auto layer_props_update = |
| CreateManualLayer(kLayerId, layer_type_under_test); |
| layer_props_update->layer_extra = |
| CreateDefaultLayerExtra(mismatching_extra_provider_type); |
| update_props->layers.push_back(std::move(layer_props_update)); |
| |
| auto result_update = |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update_props)); |
| ASSERT_FALSE(result_update.has_value()); |
| EXPECT_THAT(result_update.error(), |
| testing::StartsWith("Invalid layer_extra type for " + |
| GetLayerImplName(layer_type_under_test))); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| AllLayerTypesRequiringExtra, |
| LayerContextImplLayerExtraTypeValidationTest, |
| testing::ValuesIn(kLayerTypesWithSpecificExtras), |
| [](const testing::TestParamInfo< |
| LayerContextImplLayerExtraTypeValidationTest::ParamType>& info) { |
| return LayerContextImplLayerLifecycleTest::GetLayerImplName(info.param); |
| }); |
| |
| TEST_F(LayerContextImplLayerLifecycleTest, |
| UpdateExistingLayerWithInvalidPropertyTreeIndicesFails) { |
| constexpr int kLayerId = 2; |
| constexpr int kValidIndex = 1; // Assumes root (0) and secondary_root (1) |
| constexpr int kInvalidIndex = 99; // An index that will be out of bounds. |
| |
| // Setup: Create a layer with valid indices and small property trees. |
| auto setup_update = CreateDefaultUpdate(); |
| |
| AddDefaultLayerToUpdate(setup_update.get(), cc::mojom::LayerType::kLayer, |
| kLayerId); |
| // Set initial valid indices for the layer. |
| setup_update->layers.back()->transform_tree_index = kValidIndex; |
| setup_update->layers.back()->clip_tree_index = kValidIndex; |
| setup_update->layers.back()->effect_tree_index = kValidIndex; |
| setup_update->layers.back()->scroll_tree_index = kValidIndex; |
| |
| EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(setup_update)) |
| .has_value()); |
| VerifyLayerExists(kLayerId, true); |
| |
| // Test Case 1: Update with invalid transform_tree_index. |
| auto update_invalid_transform = CreateDefaultUpdate(); |
| update_invalid_transform->layers.push_back(CreateManualLayer( |
| kLayerId, cc::mojom::LayerType::kLayer, kDefaultLayerBounds, |
| kInvalidIndex, kValidIndex, kValidIndex, kValidIndex)); |
| auto result_transform = layer_context_impl_->DoUpdateDisplayTree( |
| std::move(update_invalid_transform)); |
| ASSERT_FALSE(result_transform.has_value()); |
| EXPECT_THAT(result_transform.error(), |
| testing::StartsWith("Invalid transform tree ID")); |
| |
| // Test Case 2: Update with invalid clip_tree_index. |
| auto update_invalid_clip = CreateDefaultUpdate(); |
| update_invalid_clip->layers.push_back(CreateManualLayer( |
| kLayerId, cc::mojom::LayerType::kLayer, kDefaultLayerBounds, kValidIndex, |
| kInvalidIndex, kValidIndex, kValidIndex)); |
| auto result_clip = |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update_invalid_clip)); |
| ASSERT_FALSE(result_clip.has_value()); |
| EXPECT_THAT(result_clip.error(), testing::StartsWith("Invalid clip tree ID")); |
| |
| // Test Case 3: Update with invalid effect_tree_index. |
| auto update_invalid_effect = CreateDefaultUpdate(); |
| update_invalid_effect->layers.push_back(CreateManualLayer( |
| kLayerId, cc::mojom::LayerType::kLayer, kDefaultLayerBounds, kValidIndex, |
| kValidIndex, kInvalidIndex, kValidIndex)); |
| auto result_effect = layer_context_impl_->DoUpdateDisplayTree( |
| std::move(update_invalid_effect)); |
| ASSERT_FALSE(result_effect.has_value()); |
| EXPECT_THAT(result_effect.error(), |
| testing::StartsWith("Invalid effect tree ID")); |
| |
| // Test Case 4: Update with invalid scroll_tree_index. |
| auto update_invalid_scroll = CreateDefaultUpdate(); |
| update_invalid_scroll->layers.push_back(CreateManualLayer( |
| kLayerId, cc::mojom::LayerType::kLayer, kDefaultLayerBounds, kValidIndex, |
| kValidIndex, kValidIndex, kInvalidIndex)); |
| auto result_scroll = layer_context_impl_->DoUpdateDisplayTree( |
| std::move(update_invalid_scroll)); |
| ASSERT_FALSE(result_scroll.has_value()); |
| EXPECT_THAT(result_scroll.error(), |
| testing::StartsWith("Invalid scroll tree ID")); |
| |
| // Verify layer properties remain from the last successful update. |
| cc::LayerImpl* layer_impl_after_invalid = GetLayerFromActiveTree(kLayerId); |
| ASSERT_NE(nullptr, layer_impl_after_invalid); |
| EXPECT_EQ(layer_impl_after_invalid->transform_tree_index(), kValidIndex); |
| EXPECT_EQ(layer_impl_after_invalid->clip_tree_index(), kValidIndex); |
| EXPECT_EQ(layer_impl_after_invalid->effect_tree_index(), kValidIndex); |
| EXPECT_EQ(layer_impl_after_invalid->scroll_tree_index(), kValidIndex); |
| } |
| |
| // Test fixture for base LayerImpl property updates (those directly on |
| // mojom::Layer). |
| class LayerContextImplUpdateDisplayTreeBaseLayerPropertiesTest |
| : public LayerContextImplLayerLifecycleTest {}; |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeBaseLayerPropertiesTest, |
| UpdateLayerWithMismatchedTypeFails) { |
| constexpr int kLayerId = 2; |
| constexpr cc::mojom::LayerType kInitialType = |
| cc::mojom::LayerType::kSolidColor; |
| constexpr cc::mojom::LayerType kUpdatedType = cc::mojom::LayerType::kTexture; |
| |
| // Initial update: Create a layer of kInitialType. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), kInitialType, kLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| |
| cc::LayerImpl* layer_impl = GetLayerFromActiveTree(kLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->GetLayerType(), kInitialType); |
| |
| // Second update: Attempt to update the layer with kUpdatedType. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kLayerId, kUpdatedType); |
| layer_props2->layer_extra = CreateDefaultLayerExtra(kUpdatedType); |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| auto result2 = layer_context_impl_->DoUpdateDisplayTree(std::move(update2)); |
| ASSERT_FALSE(result2.has_value()); |
| EXPECT_EQ(result2.error(), "Incorrect layer type used in Layer update."); |
| |
| // Verify the layer type in the tree remains kInitialType. |
| EXPECT_EQ(layer_impl->GetLayerType(), kInitialType); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeBaseLayerPropertiesTest, |
| UpdateSafeOpaqueBackgroundColor) { |
| constexpr int kLayerId = 2; |
| const SkColor4f kDefaultColor = SkColors::kTransparent; // Default from mojom |
| const SkColor4f kColor1 = SkColors::kRed; |
| const SkColor4f kColor2 = SkColors::kGreen; |
| |
| // Initial update: Create with default color. |
| // Default contents_opaque is false. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kLayer, |
| kLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::LayerImpl* layer_impl = GetLayerFromActiveTree(kLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_FALSE(layer_impl->contents_opaque()); |
| EXPECT_EQ(layer_impl->safe_opaque_background_color(), kDefaultColor); |
| |
| // Second update: Update color (still transparent, contents_opaque=false). |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kLayerId); |
| layer_props2->contents_opaque = false; |
| layer_props2->contents_opaque_for_text = false; |
| layer_props2->safe_opaque_background_color = SkColors::kTransparent; |
| update2->layers.push_back(std::move(layer_props2)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->safe_opaque_background_color(), SkColors::kTransparent); |
| |
| // Third update: Set contents_opaque = true, safe_opaque_background_color = |
| // opaque red. |
| auto update3 = CreateDefaultUpdate(); |
| auto layer_props3 = CreateManualLayer(kLayerId); |
| layer_props3->contents_opaque = true; |
| layer_props3->contents_opaque_for_text = true; |
| layer_props3->safe_opaque_background_color = kColor2; |
| update3->layers.push_back(std::move(layer_props3)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| EXPECT_TRUE(layer_impl->contents_opaque()); |
| EXPECT_EQ(layer_impl->safe_opaque_background_color(), kColor2); |
| |
| // Fourth update: Update color again (opaque green, contents_opaque=true). |
| auto update4 = CreateDefaultUpdate(); |
| auto layer_props4 = CreateManualLayer(kLayerId); |
| layer_props4->contents_opaque = true; |
| layer_props4->contents_opaque_for_text = true; |
| layer_props4->safe_opaque_background_color = kColor2; |
| update4->layers.push_back(std::move(layer_props4)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update4)).has_value()); |
| EXPECT_EQ(layer_impl->safe_opaque_background_color(), kColor2); |
| |
| // Fifth update: Attempt invalid update: contents_opaque = true, but |
| // safe_opaque_background_color is transparent. |
| auto update5 = CreateDefaultUpdate(); |
| auto layer_props5 = CreateManualLayer(kLayerId); |
| layer_props5->contents_opaque = true; |
| layer_props5->contents_opaque_for_text = true; |
| layer_props5->safe_opaque_background_color = SkColors::kTransparent; |
| update5->layers.push_back(std::move(layer_props5)); |
| auto result5 = layer_context_impl_->DoUpdateDisplayTree(std::move(update5)); |
| ASSERT_FALSE(result5.has_value()); |
| EXPECT_THAT(result5.error(), |
| testing::StartsWith("Invalid safe_opaque_background_color")); |
| // Color should remain kColor2 from the last successful update. |
| EXPECT_EQ(layer_impl->safe_opaque_background_color(), kColor2); |
| EXPECT_TRUE(layer_impl->contents_opaque()); |
| |
| // Sixth update: Attempt invalid update: contents_opaque = false, but |
| // safe_opaque_background_color is opaque (and not transparent). |
| auto update6 = CreateDefaultUpdate(); |
| auto layer_props6 = CreateManualLayer(kLayerId); |
| layer_props6->contents_opaque = false; |
| layer_props6->contents_opaque_for_text = false; |
| layer_props6->safe_opaque_background_color = kColor1; // Opaque red |
| update6->layers.push_back(std::move(layer_props6)); |
| auto result6 = layer_context_impl_->DoUpdateDisplayTree(std::move(update6)); |
| ASSERT_FALSE(result6.has_value()); |
| EXPECT_THAT(result6.error(), |
| testing::StartsWith("Invalid safe_opaque_background_color")); |
| // Color should remain kColor2 from the last successful update. |
| EXPECT_EQ(layer_impl->safe_opaque_background_color(), kColor2); |
| EXPECT_TRUE(layer_impl->contents_opaque()); |
| |
| // Seventh update: Valid update: contents_opaque = false, |
| // safe_opaque_background_color is transparent. |
| auto update7 = CreateDefaultUpdate(); |
| auto layer_props7 = CreateManualLayer(kLayerId); |
| layer_props7->contents_opaque = false; |
| layer_props7->contents_opaque_for_text = false; |
| layer_props7->safe_opaque_background_color = SkColors::kTransparent; |
| update7->layers.push_back(std::move(layer_props7)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update7)).has_value()); |
| EXPECT_FALSE(layer_impl->contents_opaque()); |
| EXPECT_EQ(layer_impl->safe_opaque_background_color(), SkColors::kTransparent); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeBaseLayerPropertiesTest, UpdateRect) { |
| constexpr int kLayerId = 2; |
| const gfx::Rect kInitialRect; // Default from mojom |
| const gfx::Rect kRect1(10, 10, 5, 5); |
| const gfx::Rect kRect2(0, 0, 12, 12); |
| const gfx::Rect kExpectedUnion = gfx::UnionRects(kRect1, kRect2); |
| |
| // Initial update: Create with default (empty) update_rect. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kLayer, |
| kLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::LayerImpl* layer_impl = GetLayerFromActiveTree(kLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->update_rect(), kInitialRect); |
| |
| // Second update: Set update_rect to kRect1. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kLayerId); |
| layer_props2->update_rect = kRect1; |
| update2->layers.push_back(std::move(layer_props2)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| // LayerImpl::UnionUpdateRect is called, so it unions with its current value. |
| // Since it was empty, it becomes kRect1. |
| EXPECT_EQ(layer_impl->update_rect(), kRect1); |
| |
| // Third update: Set update_rect to kRect2. It should be unioned. |
| auto update3 = CreateDefaultUpdate(); |
| auto layer_props3 = CreateManualLayer(kLayerId); |
| layer_props3->update_rect = kRect2; |
| update3->layers.push_back(std::move(layer_props3)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| EXPECT_EQ(layer_impl->update_rect(), kExpectedUnion); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeBaseLayerPropertiesTest, |
| UpdateHitTestOpaqueness) { |
| constexpr int kLayerId = 2; |
| const cc::HitTestOpaqueness kDefaultValue = |
| cc::HitTestOpaqueness::kTransparent; // Default from mojom |
| const cc::HitTestOpaqueness kValue1 = cc::HitTestOpaqueness::kOpaque; |
| const cc::HitTestOpaqueness kValue2 = cc::HitTestOpaqueness::kMixed; |
| |
| // Initial update: Create with default value. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kLayer, |
| kLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::LayerImpl* layer_impl = GetLayerFromActiveTree(kLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->hit_test_opaqueness(), kDefaultValue); |
| |
| // Second update: Update value. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kLayerId); |
| layer_props2->hit_test_opaqueness = kValue1; |
| update2->layers.push_back(std::move(layer_props2)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->hit_test_opaqueness(), kValue1); |
| |
| // Third update: Update value again. |
| auto update3 = CreateDefaultUpdate(); |
| auto layer_props3 = CreateManualLayer(kLayerId); |
| layer_props3->hit_test_opaqueness = kValue2; |
| update3->layers.push_back(std::move(layer_props3)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| EXPECT_EQ(layer_impl->hit_test_opaqueness(), kValue2); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeBaseLayerPropertiesTest, |
| UpdateElementId) { |
| constexpr int kLayerId = 2; |
| const cc::ElementId kDefaultValue; // Default from mojom |
| const cc::ElementId kValue1(123); |
| const cc::ElementId kValue2(456); |
| |
| // Initial update: Create with default value. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kLayer, |
| kLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::LayerImpl* layer_impl = GetLayerFromActiveTree(kLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->element_id(), kDefaultValue); |
| |
| // Second update: Update value. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kLayerId); |
| layer_props2->element_id = kValue1; |
| update2->layers.push_back(std::move(layer_props2)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->element_id(), kValue1); |
| |
| // Third update: Update value again. |
| auto update3 = CreateDefaultUpdate(); |
| auto layer_props3 = CreateManualLayer(kLayerId); |
| layer_props3->element_id = kValue2; |
| update3->layers.push_back(std::move(layer_props3)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| EXPECT_EQ(layer_impl->element_id(), kValue2); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeBaseLayerPropertiesTest, |
| UpdateOffsetToTransformParent) { |
| constexpr int kLayerId = 2; |
| const gfx::Vector2dF kDefaultValue; // Default from mojom |
| const gfx::Vector2dF kValue1(10.f, 20.f); |
| const gfx::Vector2dF kValue2(-5.f, 15.f); |
| |
| // Initial update: Create with default value. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kLayer, |
| kLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::LayerImpl* layer_impl = GetLayerFromActiveTree(kLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->offset_to_transform_parent(), kDefaultValue); |
| |
| // Second update: Update value. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kLayerId); |
| layer_props2->offset_to_transform_parent = kValue1; |
| update2->layers.push_back(std::move(layer_props2)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->offset_to_transform_parent(), kValue1); |
| |
| // Third update: Update value again. |
| auto update3 = CreateDefaultUpdate(); |
| auto layer_props3 = CreateManualLayer(kLayerId); |
| layer_props3->offset_to_transform_parent = kValue2; |
| update3->layers.push_back(std::move(layer_props3)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| EXPECT_EQ(layer_impl->offset_to_transform_parent(), kValue2); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeBaseLayerPropertiesTest, |
| UpdateShouldCheckBackfaceVisibility) { |
| constexpr int kLayerId = 2; |
| constexpr bool kDefaultValue = false; // Default from mojom |
| constexpr bool kValue1 = true; |
| |
| // Initial update: Create with default value. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kLayer, |
| kLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::LayerImpl* layer_impl = GetLayerFromActiveTree(kLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->should_check_backface_visibility(), kDefaultValue); |
| |
| // Second update: Update value to true. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kLayerId); |
| layer_props2->should_check_backface_visibility = kValue1; |
| update2->layers.push_back(std::move(layer_props2)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->should_check_backface_visibility(), kValue1); |
| |
| // Third update: Update value back to false. |
| auto update3 = CreateDefaultUpdate(); |
| auto layer_props3 = CreateManualLayer(kLayerId); |
| layer_props3->should_check_backface_visibility = kDefaultValue; |
| update3->layers.push_back(std::move(layer_props3)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| EXPECT_EQ(layer_impl->should_check_backface_visibility(), kDefaultValue); |
| } |
| |
| // Test fixture for TileDisplayLayerImpl specific property updates. |
| class LayerContextImplUpdateDisplayTreeTileDisplayLayerPropertiesTest |
| : public LayerContextImplLayerLifecycleTest {}; |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeTileDisplayLayerPropertiesTest, |
| UpdateTileDisplayLayerProperties) { |
| constexpr int kLayerId = 2; |
| const SkColor4f kSolidColor = SkColors::kMagenta; |
| |
| // Initial update: Create TileDisplayLayer with default properties. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kTileDisplay, |
| kLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| |
| cc::LayerImpl* layer_impl_base = GetLayerFromActiveTree(kLayerId); |
| ASSERT_NE(nullptr, layer_impl_base); |
| ASSERT_EQ(layer_impl_base->GetLayerType(), |
| cc::mojom::LayerType::kTileDisplay); |
| auto* tile_display_layer_impl = |
| static_cast<cc::TileDisplayLayerImpl*>(layer_impl_base); |
| |
| EXPECT_FALSE(tile_display_layer_impl->solid_color_for_testing().has_value()); |
| EXPECT_FALSE(tile_display_layer_impl->is_backdrop_filter_mask()); |
| |
| // Second update: Set solid_color and is_backdrop_filter_mask. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = |
| CreateManualLayer(kLayerId, cc::mojom::LayerType::kTileDisplay); |
| auto tile_extra2 = mojom::TileDisplayLayerExtra::New(); |
| tile_extra2->solid_color = kSolidColor; |
| tile_extra2->is_backdrop_filter_mask = true; |
| layer_props2->layer_extra = |
| mojom::LayerExtra::NewTileDisplayLayerExtra(std::move(tile_extra2)); |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| |
| EXPECT_TRUE(tile_display_layer_impl->solid_color_for_testing().has_value()); |
| EXPECT_EQ(tile_display_layer_impl->solid_color_for_testing().value(), |
| kSolidColor); |
| EXPECT_TRUE(tile_display_layer_impl->is_backdrop_filter_mask()); |
| |
| // Third update: Clear solid_color and set is_backdrop_filter_mask to false. |
| auto update3 = CreateDefaultUpdate(); |
| auto layer_props3 = |
| CreateManualLayer(kLayerId, cc::mojom::LayerType::kTileDisplay); |
| auto tile_extra3 = mojom::TileDisplayLayerExtra::New(); |
| tile_extra3->solid_color = std::nullopt; |
| tile_extra3->is_backdrop_filter_mask = false; |
| layer_props3->layer_extra = |
| mojom::LayerExtra::NewTileDisplayLayerExtra(std::move(tile_extra3)); |
| update3->layers.push_back(std::move(layer_props3)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| |
| EXPECT_FALSE(tile_display_layer_impl->solid_color_for_testing().has_value()); |
| EXPECT_FALSE(tile_display_layer_impl->is_backdrop_filter_mask()); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeTileDisplayLayerPropertiesTest, |
| UpdateIsDirectlyCompositedImage) { |
| constexpr int kLayerId = 2; |
| |
| // Initial update: Create TileDisplayLayer with default properties. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kTileDisplay, |
| kLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| |
| cc::LayerImpl* layer_impl_base = GetLayerFromActiveTree(kLayerId); |
| ASSERT_NE(nullptr, layer_impl_base); |
| ASSERT_EQ(layer_impl_base->GetLayerType(), |
| cc::mojom::LayerType::kTileDisplay); |
| auto* tile_display_layer_impl = |
| static_cast<cc::TileDisplayLayerImpl*>(layer_impl_base); |
| |
| EXPECT_FALSE(tile_display_layer_impl->IsDirectlyCompositedImage()); |
| |
| // Second update: Set is_directly_composited_image to true. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = |
| CreateManualLayer(kLayerId, cc::mojom::LayerType::kTileDisplay); |
| auto tile_extra2 = mojom::TileDisplayLayerExtra::New(); |
| tile_extra2->is_directly_composited_image = true; |
| layer_props2->layer_extra = |
| mojom::LayerExtra::NewTileDisplayLayerExtra(std::move(tile_extra2)); |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| |
| EXPECT_TRUE(tile_display_layer_impl->IsDirectlyCompositedImage()); |
| |
| // Third update: Set is_directly_composited_image to false. |
| auto update3 = CreateDefaultUpdate(); |
| auto layer_props3 = |
| CreateManualLayer(kLayerId, cc::mojom::LayerType::kTileDisplay); |
| auto tile_extra3 = mojom::TileDisplayLayerExtra::New(); |
| tile_extra3->is_directly_composited_image = false; |
| layer_props3->layer_extra = |
| mojom::LayerExtra::NewTileDisplayLayerExtra(std::move(tile_extra3)); |
| update3->layers.push_back(std::move(layer_props3)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| |
| EXPECT_FALSE(tile_display_layer_impl->IsDirectlyCompositedImage()); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeTileDisplayLayerPropertiesTest, |
| UpdateNearestNeighbor) { |
| constexpr int kLayerId = 2; |
| |
| // Initial update: Create TileDisplayLayer with default properties. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kTileDisplay, |
| kLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| |
| cc::LayerImpl* layer_impl_base = GetLayerFromActiveTree(kLayerId); |
| ASSERT_NE(nullptr, layer_impl_base); |
| ASSERT_EQ(layer_impl_base->GetLayerType(), |
| cc::mojom::LayerType::kTileDisplay); |
| auto* tile_display_layer_impl = |
| static_cast<cc::TileDisplayLayerImpl*>(layer_impl_base); |
| |
| EXPECT_FALSE(tile_display_layer_impl->nearest_neighbor()); |
| |
| // Second update: Set nearest_neighbor to true. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = |
| CreateManualLayer(kLayerId, cc::mojom::LayerType::kTileDisplay); |
| auto tile_extra2 = mojom::TileDisplayLayerExtra::New(); |
| tile_extra2->nearest_neighbor = true; |
| layer_props2->layer_extra = |
| mojom::LayerExtra::NewTileDisplayLayerExtra(std::move(tile_extra2)); |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| |
| EXPECT_TRUE(tile_display_layer_impl->nearest_neighbor()); |
| |
| // Third update: Set nearest_neighbor to false. |
| auto update3 = CreateDefaultUpdate(); |
| auto layer_props3 = |
| CreateManualLayer(kLayerId, cc::mojom::LayerType::kTileDisplay); |
| auto tile_extra3 = mojom::TileDisplayLayerExtra::New(); |
| tile_extra3->nearest_neighbor = false; |
| layer_props3->layer_extra = |
| mojom::LayerExtra::NewTileDisplayLayerExtra(std::move(tile_extra3)); |
| update3->layers.push_back(std::move(layer_props3)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| |
| EXPECT_FALSE(tile_display_layer_impl->nearest_neighbor()); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeTilingTest, TilingAndTileLifecycle) { |
| constexpr int kLayerId = 2; |
| constexpr float kScaleKey1 = 1.0f; |
| constexpr float kScaleKey2 = 2.0f; |
| const gfx::Size kTileSize1(64, 64); |
| const gfx::Size kTileSize2(128, 128); |
| const gfx::Rect kTilingRect1(0, 0, 200, 200); |
| const gfx::Rect kTilingRect2(0, 0, 400, 400); |
| const cc::TileIndex kTileIndex1(0, 0); |
| const cc::TileIndex kTileIndex2(1, 1); |
| const ResourceId kResourceId1(23); |
| const ResourceId kResourceId2(45); |
| |
| // Initial update: Create TileDisplayLayer. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kTileDisplay, |
| kLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| |
| cc::LayerTreeImpl* active_tree = |
| layer_context_impl_->host_impl()->active_tree(); |
| ASSERT_TRUE(active_tree); |
| cc::LayerImpl* layer_impl_base = active_tree->LayerById(kLayerId); |
| ASSERT_NE(nullptr, layer_impl_base); |
| ASSERT_EQ(layer_impl_base->GetLayerType(), |
| cc::mojom::LayerType::kTileDisplay); |
| auto* tile_display_layer_impl = |
| static_cast<cc::TileDisplayLayerImpl*>(layer_impl_base); |
| |
| // Test Case 1: Create a new Tiling with a SolidColor Tile. |
| auto update_create_tiling = CreateDefaultUpdate(); |
| auto tiling1 = mojom::Tiling::New(); |
| tiling1->layer_id = kLayerId; |
| tiling1->scale_key = kScaleKey1; |
| tiling1->raster_scale = gfx::Vector2dF(kScaleKey1, kScaleKey1); |
| tiling1->tile_size = kTileSize1; |
| tiling1->tiling_rect = kTilingRect1; |
| auto tile1_solid = mojom::Tile::New(); |
| tile1_solid->column_index = kTileIndex1.i; |
| tile1_solid->row_index = kTileIndex1.j; |
| tile1_solid->contents = |
| mojom::TileContents::NewSolidColor(SkColors::kMagenta); |
| tiling1->tiles.push_back(std::move(tile1_solid)); |
| update_create_tiling->tilings.push_back(std::move(tiling1)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update_create_tiling)) |
| .has_value()); |
| const cc::TileDisplayLayerImpl::Tiling* tiling_impl1 = |
| tile_display_layer_impl->GetTilingForTesting(kScaleKey1); |
| ASSERT_NE(nullptr, tiling_impl1); |
| EXPECT_EQ(tiling_impl1->tile_size(), kTileSize1); |
| EXPECT_EQ(tiling_impl1->tiling_rect(), kTilingRect1); |
| ASSERT_NE(nullptr, tiling_impl1->TileAt(kTileIndex1)); |
| EXPECT_TRUE(tiling_impl1->TileAt(kTileIndex1)->solid_color().has_value()); |
| EXPECT_EQ(tiling_impl1->TileAt(kTileIndex1)->solid_color().value(), |
| SkColors::kMagenta); |
| |
| // Test Case 2: Update existing Tiling (tile_size) and add a Resource Tile. |
| auto update_update_tiling = CreateDefaultUpdate(); |
| auto tiling1_updated = mojom::Tiling::New(); |
| tiling1_updated->layer_id = kLayerId; |
| tiling1_updated->scale_key = kScaleKey1; // Same key to update |
| tiling1_updated->raster_scale = gfx::Vector2dF(kScaleKey1, kScaleKey1); |
| tiling1_updated->tile_size = kTileSize2; // New tile size |
| tiling1_updated->tiling_rect = kTilingRect1; |
| // Add a new resource tile |
| auto tile2_resource = mojom::Tile::New(); |
| tile2_resource->column_index = kTileIndex2.i; |
| tile2_resource->row_index = kTileIndex2.j; |
| auto resource_contents = mojom::TileResource::New(); |
| resource_contents->resource = MakeFakeResource(kTileSize2); |
| resource_contents->resource.id = kResourceId1; |
| resource_contents->is_checkered = false; |
| tile2_resource->contents = |
| mojom::TileContents::NewResource(std::move(resource_contents)); |
| tiling1_updated->tiles.push_back(std::move(tile2_resource)); |
| update_update_tiling->tilings.push_back(std::move(tiling1_updated)); |
| |
| auto result = |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update_update_tiling)); |
| EXPECT_TRUE(result.has_value()) << result.error(); |
| ASSERT_EQ(tiling_impl1, |
| tile_display_layer_impl->GetTilingForTesting( |
| kScaleKey1)); // Should still be the same tiling object |
| EXPECT_EQ(tiling_impl1->tile_size(), kTileSize2); // Updated |
| // Previous tiles should still exist after a tile_size change |
| EXPECT_NE(nullptr, tiling_impl1->TileAt(kTileIndex1)); |
| ASSERT_NE(nullptr, tiling_impl1->TileAt(kTileIndex2)); |
| EXPECT_TRUE(tiling_impl1->TileAt(kTileIndex2)->resource().has_value()); |
| |
| // Test Case 3: Add a second Tiling with a MissingReason Tile. |
| auto update_add_tiling2 = CreateDefaultUpdate(); |
| auto tiling2 = mojom::Tiling::New(); |
| tiling2->layer_id = kLayerId; |
| tiling2->scale_key = kScaleKey2; |
| tiling2->raster_scale = gfx::Vector2dF(kScaleKey2, kScaleKey2); |
| tiling2->tile_size = kTileSize1; |
| tiling2->tiling_rect = kTilingRect2; |
| auto tile3_missing = mojom::Tile::New(); |
| tile3_missing->column_index = kTileIndex1.i; |
| tile3_missing->row_index = kTileIndex1.j; |
| tile3_missing->contents = mojom::TileContents::NewMissingReason( |
| cc::mojom::MissingTileReason::kResourceNotReady); |
| tiling2->tiles.push_back(std::move(tile3_missing)); |
| update_add_tiling2->tilings.push_back(std::move(tiling2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update_add_tiling2)) |
| .has_value()); |
| const cc::TileDisplayLayerImpl::Tiling* tiling_impl2 = |
| tile_display_layer_impl->GetTilingForTesting(kScaleKey2); |
| ASSERT_NE(nullptr, tiling_impl2); |
| EXPECT_EQ(tiling_impl2->tile_size(), kTileSize1); |
| ASSERT_NE(nullptr, tiling_impl2->TileAt(kTileIndex1)); |
| EXPECT_TRUE(std::holds_alternative<cc::TileDisplayLayerImpl::NoContents>( |
| tiling_impl2->TileAt(kTileIndex1)->contents())); |
| EXPECT_EQ(std::get<cc::TileDisplayLayerImpl::NoContents>( |
| tiling_impl2->TileAt(kTileIndex1)->contents()) |
| .reason, |
| cc::mojom::MissingTileReason::kResourceNotReady); |
| |
| // Test Case 4: Explicitly delete a Tiling (tiling_impl1). |
| auto update_delete_tiling1 = CreateDefaultUpdate(); |
| auto tiling1_deleted_marker = mojom::Tiling::New(); |
| tiling1_deleted_marker->layer_id = kLayerId; |
| tiling1_deleted_marker->scale_key = kScaleKey1; |
| tiling1_deleted_marker->is_deleted = true; |
| update_delete_tiling1->tilings.push_back(std::move(tiling1_deleted_marker)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update_delete_tiling1)) |
| .has_value()); |
| EXPECT_EQ(nullptr, tile_display_layer_impl->GetTilingForTesting(kScaleKey1)); |
| EXPECT_NE(nullptr, tile_display_layer_impl->GetTilingForTesting(kScaleKey2)); |
| |
| // Test Case 5: Implicitly delete a Tiling by removing all its tiles. |
| // (tiling_impl2 currently has one kMissingReason tile). |
| // Send an update for tiling_impl2 that marks its only tile as deleted. |
| auto update_empty_tiling2 = CreateDefaultUpdate(); |
| auto tiling2_empty_update = mojom::Tiling::New(); |
| tiling2_empty_update->layer_id = kLayerId; |
| tiling2_empty_update->scale_key = kScaleKey2; |
| tiling2_empty_update->raster_scale = gfx::Vector2dF(kScaleKey2, kScaleKey2); |
| tiling2_empty_update->tile_size = kTileSize1; // Keep same tile size |
| tiling2_empty_update->tiling_rect = kTilingRect2; |
| auto tile_deleted_marker = mojom::Tile::New(); |
| tile_deleted_marker->column_index = kTileIndex1.i; |
| tile_deleted_marker->row_index = kTileIndex1.j; |
| tile_deleted_marker->contents = mojom::TileContents::NewMissingReason( |
| cc::mojom::MissingTileReason::kTileDeleted); // Mark for deletion |
| tiling2_empty_update->tiles.push_back(std::move(tile_deleted_marker)); |
| update_empty_tiling2->tilings.push_back(std::move(tiling2_empty_update)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update_empty_tiling2)) |
| .has_value()); |
| EXPECT_EQ(nullptr, tile_display_layer_impl->GetTilingForTesting(kScaleKey2)); |
| |
| // Test Case 6: Update a tile within a tiling (change its content type). |
| // First, re-add tiling1 with a solid color tile. |
| auto update_recreate_tiling1 = CreateDefaultUpdate(); |
| auto tiling1_recreate = mojom::Tiling::New(); |
| tiling1_recreate->layer_id = kLayerId; |
| tiling1_recreate->scale_key = kScaleKey1; |
| tiling1_recreate->raster_scale = gfx::Vector2dF(kScaleKey1, kScaleKey1); |
| tiling1_recreate->tile_size = kTileSize1; |
| tiling1_recreate->tiling_rect = kTilingRect1; |
| auto tile_initial_solid = mojom::Tile::New(); |
| tile_initial_solid->column_index = kTileIndex1.i; |
| tile_initial_solid->row_index = kTileIndex1.j; |
| tile_initial_solid->contents = |
| mojom::TileContents::NewSolidColor(SkColors::kBlue); |
| tiling1_recreate->tiles.push_back(std::move(tile_initial_solid)); |
| update_recreate_tiling1->tilings.push_back(std::move(tiling1_recreate)); |
| EXPECT_TRUE(layer_context_impl_ |
| ->DoUpdateDisplayTree(std::move(update_recreate_tiling1)) |
| .has_value()); |
| tiling_impl1 = tile_display_layer_impl->GetTilingForTesting(kScaleKey1); |
| ASSERT_NE(nullptr, tiling_impl1); |
| ASSERT_NE(nullptr, tiling_impl1->TileAt(kTileIndex1)); |
| EXPECT_TRUE(tiling_impl1->TileAt(kTileIndex1)->solid_color().has_value()); |
| |
| // Now, update that tile to be a resource tile. |
| auto update_change_tile_type = CreateDefaultUpdate(); |
| auto tiling1_tile_update = mojom::Tiling::New(); |
| tiling1_tile_update->layer_id = kLayerId; |
| tiling1_tile_update->scale_key = kScaleKey1; |
| tiling1_tile_update->raster_scale = gfx::Vector2dF(kScaleKey1, kScaleKey1); |
| tiling1_tile_update->tile_size = kTileSize1; // Keep same tile size |
| tiling1_tile_update->tiling_rect = kTilingRect1; |
| auto tile_updated_to_resource = mojom::Tile::New(); |
| tile_updated_to_resource->column_index = kTileIndex1.i; |
| tile_updated_to_resource->row_index = kTileIndex1.j; |
| auto resource_contents_updated = mojom::TileResource::New(); |
| resource_contents_updated->resource = MakeFakeResource(kTileSize1); |
| resource_contents_updated->resource.id = kResourceId2; |
| resource_contents_updated->is_checkered = true; |
| tile_updated_to_resource->contents = |
| mojom::TileContents::NewResource(std::move(resource_contents_updated)); |
| tiling1_tile_update->tiles.push_back(std::move(tile_updated_to_resource)); |
| update_change_tile_type->tilings.push_back(std::move(tiling1_tile_update)); |
| |
| EXPECT_TRUE(layer_context_impl_ |
| ->DoUpdateDisplayTree(std::move(update_change_tile_type)) |
| .has_value()); |
| ASSERT_NE(nullptr, tiling_impl1->TileAt(kTileIndex1)); |
| EXPECT_FALSE(tiling_impl1->TileAt(kTileIndex1)->solid_color().has_value()); |
| EXPECT_TRUE(tiling_impl1->TileAt(kTileIndex1)->resource().has_value()); |
| EXPECT_TRUE(tiling_impl1->TileAt(kTileIndex1)->resource()->is_checkered); |
| |
| // Test Case 7: Attempt to add a tile with an invalid resource ID. |
| auto update_invalid_resource_tile = CreateDefaultUpdate(); |
| auto tiling1_invalid_resource_update = mojom::Tiling::New(); |
| tiling1_invalid_resource_update->layer_id = kLayerId; |
| tiling1_invalid_resource_update->scale_key = kScaleKey1; |
| tiling1_invalid_resource_update->raster_scale = |
| gfx::Vector2dF(kScaleKey1, kScaleKey1); |
| tiling1_invalid_resource_update->tile_size = kTileSize1; |
| tiling1_invalid_resource_update->tiling_rect = kTilingRect1; |
| auto tile_invalid_resource = mojom::Tile::New(); |
| tile_invalid_resource->column_index = kTileIndex1.i; // Use existing index |
| tile_invalid_resource->row_index = kTileIndex1.j; |
| auto invalid_resource_contents = mojom::TileResource::New(); |
| invalid_resource_contents->resource.id = kInvalidResourceId; // Invalid ID |
| invalid_resource_contents->resource.size = kTileSize1; |
| tile_invalid_resource->contents = |
| mojom::TileContents::NewResource(std::move(invalid_resource_contents)); |
| tiling1_invalid_resource_update->tiles.push_back( |
| std::move(tile_invalid_resource)); |
| update_invalid_resource_tile->tilings.push_back( |
| std::move(tiling1_invalid_resource_update)); |
| auto update_invalid_resource_tile_result = |
| layer_context_impl_->DoUpdateDisplayTree( |
| std::move(update_invalid_resource_tile)); |
| ASSERT_FALSE(update_invalid_resource_tile_result.has_value()); |
| EXPECT_EQ(update_invalid_resource_tile_result.error(), |
| "Invalid tile resource"); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeTilingTest, |
| TilingForNonTileDisplayLayerFails) { |
| constexpr int kNonTileDisplayLayerId = 2; |
| |
| // Initial update: Create a regular Layer (not TileDisplayLayer). |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kLayer, |
| kNonTileDisplayLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| |
| // Attempt to send tiling data for this non-TileDisplayLayer. |
| auto update_tiling = CreateDefaultUpdate(); |
| auto tiling = mojom::Tiling::New(); |
| tiling->layer_id = kNonTileDisplayLayerId; // ID of a non-TileDisplayLayer |
| tiling->scale_key = 1.0f; |
| tiling->raster_scale = gfx::Vector2dF(1.0f, 1.0f); |
| tiling->tile_size = gfx::Size(64, 64); |
| tiling->tiling_rect = gfx::Rect(100, 100); |
| auto tile = mojom::Tile::New(); |
| tile->column_index = 0; |
| tile->row_index = 0; |
| tile->contents = mojom::TileContents::NewSolidColor(SkColors::kRed); |
| tiling->tiles.push_back(std::move(tile)); |
| update_tiling->tilings.push_back(std::move(tiling)); |
| |
| auto result = |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update_tiling)); |
| ASSERT_FALSE(result.has_value()); |
| EXPECT_EQ(result.error(), "Invalid tile update"); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeTilingTest, |
| TilingWithInvalidLayerIdFails) { |
| constexpr int kInvalidLayerId = 999; // An ID that doesn't exist. |
| |
| auto update_tiling = CreateDefaultUpdate(); |
| auto tiling = mojom::Tiling::New(); |
| tiling->layer_id = kInvalidLayerId; |
| tiling->scale_key = 1.0f; |
| // Other tiling properties are set to valid defaults for this test. |
| tiling->raster_scale = gfx::Vector2dF(1.0f, 1.0f); |
| tiling->tile_size = gfx::Size(64, 64); |
| tiling->tiling_rect = gfx::Rect(100, 100); |
| update_tiling->tilings.push_back(std::move(tiling)); |
| |
| // No specific error message for invalid layer ID in tiling, it's handled by |
| // layer_impl being null. The update should still pass, but no tiling occurs. |
| EXPECT_TRUE(layer_context_impl_->DoUpdateDisplayTree(std::move(update_tiling)) |
| .has_value()); |
| // We can't directly verify that no tiling happened for the invalid ID without |
| // more complex state inspection, but the update itself shouldn't crash. |
| } |
| |
| class LayerContextImplUpdateDisplayTreeTextureLayerTest |
| : public LayerContextImplLayerLifecycleTest { |
| protected: |
| cc::TextureLayerImpl* GetTextureLayerFromActiveTree(int layer_id) { |
| cc::LayerImpl* layer = GetLayerFromActiveTree(layer_id); |
| if (layer && layer->GetLayerType() == cc::mojom::LayerType::kTexture) { |
| return static_cast<cc::TextureLayerImpl*>(layer); |
| } |
| return nullptr; |
| } |
| }; |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeTextureLayerTest, UpdateUVRect) { |
| constexpr int kTextureLayerId = 2; |
| const gfx::PointF kUpdatedUVTopLeft(0.1f, 0.2f); |
| const gfx::PointF kUpdatedUVBottomRight(0.8f, 0.9f); |
| |
| // Initial update: Create TextureLayer. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kTexture, |
| kTextureLayerId); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| VerifyLayerExists(kTextureLayerId, true); |
| |
| cc::TextureLayerImpl* texture_layer_impl = |
| GetTextureLayerFromActiveTree(kTextureLayerId); |
| ASSERT_NE(nullptr, texture_layer_impl); |
| |
| EXPECT_EQ(texture_layer_impl->uv_top_left(), kDefaultUVTopLeft); |
| EXPECT_EQ(texture_layer_impl->uv_bottom_right(), kDefaultUVBottomRight); |
| |
| // Second update: Update UV rect. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props = |
| CreateManualLayer(kTextureLayerId, cc::mojom::LayerType::kTexture); |
| auto& texture_extra = layer_props->layer_extra->get_texture_layer_extra(); |
| texture_extra->uv_top_left = kUpdatedUVTopLeft; |
| texture_extra->uv_bottom_right = kUpdatedUVBottomRight; |
| update2->layers.push_back(std::move(layer_props)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| ASSERT_NE(nullptr, texture_layer_impl); |
| EXPECT_EQ(texture_layer_impl->uv_top_left(), kUpdatedUVTopLeft); |
| EXPECT_EQ(texture_layer_impl->uv_bottom_right(), kUpdatedUVBottomRight); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeTextureLayerTest, |
| UpdateBlendBackgroundColor) { |
| constexpr int kTextureLayerId = 2; |
| constexpr bool kUpdatedBlendBackgroundColor = true; |
| |
| // Initial update: Create TextureLayer with default blend_background_color. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kTexture, |
| kTextureLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| VerifyLayerExists(kTextureLayerId, true); |
| |
| cc::TextureLayerImpl* texture_layer_impl = |
| GetTextureLayerFromActiveTree(kTextureLayerId); |
| ASSERT_NE(nullptr, texture_layer_impl); |
| EXPECT_EQ(texture_layer_impl->blend_background_color(), |
| kDefaultBlendBackgroundColor); |
| |
| // Second update: Update blend_background_color to true. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = |
| CreateManualLayer(kTextureLayerId, cc::mojom::LayerType::kTexture); |
| auto& texture_extra2 = layer_props2->layer_extra->get_texture_layer_extra(); |
| texture_extra2->blend_background_color = kUpdatedBlendBackgroundColor; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| ASSERT_NE(nullptr, texture_layer_impl); |
| EXPECT_EQ(texture_layer_impl->blend_background_color(), |
| kUpdatedBlendBackgroundColor); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeTextureLayerTest, |
| UpdateForceTextureToOpaque) { |
| constexpr int kTextureLayerId = 2; |
| constexpr bool kUpdatedForceTextureToOpaque = true; |
| |
| // Initial update: Create TextureLayer with default |
| // kDefaultForceTextureToOpaque. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kTexture, |
| kTextureLayerId); |
| // Default is false. |
| // No need to explicitly set texture_extra1->force_texture_to_opaque = false; |
| // as it's the default from CreateDefaultLayerExtra. |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| VerifyLayerExists(kTextureLayerId, true); |
| |
| cc::TextureLayerImpl* texture_layer_impl = |
| GetTextureLayerFromActiveTree(kTextureLayerId); |
| ASSERT_NE(nullptr, texture_layer_impl); |
| EXPECT_EQ(texture_layer_impl->force_texture_to_opaque(), |
| kDefaultForceTextureToOpaque); |
| |
| // Second update: Update force_texture_to_opaque to true. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = |
| CreateManualLayer(kTextureLayerId, cc::mojom::LayerType::kTexture); |
| auto& texture_extra2 = layer_props2->layer_extra->get_texture_layer_extra(); |
| texture_extra2->force_texture_to_opaque = kUpdatedForceTextureToOpaque; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| ASSERT_NE(nullptr, texture_layer_impl); |
| EXPECT_EQ(texture_layer_impl->force_texture_to_opaque(), |
| kUpdatedForceTextureToOpaque); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeTextureLayerTest, |
| UpdateTransferableResource) { |
| constexpr int kTextureLayerId = 2; |
| const gfx::Size kResourceSize1(10, 10); |
| const gfx::Size kResourceSize2(12, 12); |
| |
| // Initial update: Create TextureLayer without a resource. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kTexture, |
| kTextureLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| VerifyLayerExists(kTextureLayerId, true); |
| |
| cc::TextureLayerImpl* texture_layer_impl = |
| GetTextureLayerFromActiveTree(kTextureLayerId); |
| ASSERT_NE(nullptr, texture_layer_impl); |
| EXPECT_TRUE(texture_layer_impl->transferable_resource().is_empty()); |
| |
| // Second update: Set transferable_resource1. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer( |
| kTextureLayerId, cc::mojom::LayerType::kTexture, kResourceSize1); |
| auto& texture_extra2 = layer_props2->layer_extra->get_texture_layer_extra(); |
| TransferableResource resource1 = MakeFakeResource(kResourceSize1); |
| texture_extra2->transferable_resource = resource1; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| // The TransferableResource matches what we sent. |
| EXPECT_EQ(texture_layer_impl->transferable_resource(), resource1); |
| |
| // Third update: Set transferable_resource2 (different resource). |
| auto update3 = CreateDefaultUpdate(); |
| auto layer_props3 = CreateManualLayer( |
| kTextureLayerId, cc::mojom::LayerType::kTexture, kResourceSize2); |
| auto& texture_extra3 = layer_props3->layer_extra->get_texture_layer_extra(); |
| TransferableResource resource2 = MakeFakeResource(kResourceSize2); |
| texture_extra3->transferable_resource = resource2; |
| update3->layers.push_back(std::move(layer_props3)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| EXPECT_EQ(texture_layer_impl->transferable_resource(), resource2); |
| |
| // Fourth update: Clear the resource. |
| auto update4 = CreateDefaultUpdate(); |
| auto layer_props4 = CreateManualLayer( |
| kTextureLayerId, cc::mojom::LayerType::kTexture, kResourceSize1); |
| // Clearing has to be via an explicit empty resource. |
| auto& texture_extra4 = layer_props4->layer_extra->get_texture_layer_extra(); |
| texture_extra4->transferable_resource = TransferableResource(); |
| update4->layers.push_back(std::move(layer_props4)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update4)).has_value()); |
| EXPECT_TRUE(texture_layer_impl->transferable_resource().is_empty()); |
| } |
| |
| class LayerContextImplUpdateDisplayTreeSurfaceLayerTest |
| : public LayerContextImplLayerLifecycleTest { |
| protected: |
| cc::SurfaceLayerImpl* GetSurfaceLayerFromActiveTree(int layer_id) { |
| cc::LayerImpl* layer = GetLayerFromActiveTree(layer_id); |
| if (layer && layer->GetLayerType() == cc::mojom::LayerType::kSurface) { |
| return static_cast<cc::SurfaceLayerImpl*>(layer); |
| } |
| return nullptr; |
| } |
| }; |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeSurfaceLayerTest, |
| UpdateBooleanProperties) { |
| constexpr int kSurfaceLayerId = 2; |
| |
| // Initial update: Create SurfaceLayer with default boolean values. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kSurface, |
| kSurfaceLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| VerifyLayerExists(kSurfaceLayerId, true); |
| |
| cc::SurfaceLayerImpl* layer_impl = |
| GetSurfaceLayerFromActiveTree(kSurfaceLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| |
| // Defaults should be false from CreateDefaultLayerExtra. |
| EXPECT_EQ(layer_impl->stretch_content_to_fill_bounds(), |
| kDefaultStretchContentToFillBounds); |
| EXPECT_EQ(layer_impl->surface_hit_testable(), kDefaultSurfaceHitTestable); |
| EXPECT_EQ(layer_impl->has_pointer_events_none(), |
| kDefaultHasPointerEventsNone); |
| EXPECT_EQ(layer_impl->is_reflection(), kDefaultIsReflection); |
| EXPECT_EQ(layer_impl->override_child_paint_flags(), |
| kDefaultOverrideChildPaintFlags); |
| |
| // Second update: Update all boolean properties to true. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = |
| CreateManualLayer(kSurfaceLayerId, cc::mojom::LayerType::kSurface); |
| auto& surface_extra2 = layer_props2->layer_extra->get_surface_layer_extra(); |
| surface_extra2->stretch_content_to_fill_bounds = true; |
| surface_extra2->surface_hit_testable = true; |
| surface_extra2->has_pointer_events_none = true; |
| surface_extra2->is_reflection = true; |
| surface_extra2->override_child_paint_flags = true; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_TRUE(layer_impl->stretch_content_to_fill_bounds()); |
| EXPECT_TRUE(layer_impl->surface_hit_testable()); |
| EXPECT_TRUE(layer_impl->has_pointer_events_none()); |
| EXPECT_TRUE(layer_impl->is_reflection()); |
| EXPECT_TRUE(layer_impl->override_child_paint_flags()); |
| |
| // Third update: Update all boolean properties back to false. |
| auto update3 = CreateDefaultUpdate(); |
| auto layer_props3 = |
| CreateManualLayer(kSurfaceLayerId, cc::mojom::LayerType::kSurface); |
| auto& surface_extra3 = layer_props3->layer_extra->get_surface_layer_extra(); |
| surface_extra3->stretch_content_to_fill_bounds = false; |
| surface_extra3->surface_hit_testable = false; |
| surface_extra3->has_pointer_events_none = false; |
| surface_extra3->is_reflection = false; |
| surface_extra3->override_child_paint_flags = false; |
| update3->layers.push_back(std::move(layer_props3)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_FALSE(layer_impl->stretch_content_to_fill_bounds()); |
| EXPECT_FALSE(layer_impl->surface_hit_testable()); |
| EXPECT_FALSE(layer_impl->has_pointer_events_none()); |
| EXPECT_FALSE(layer_impl->is_reflection()); |
| EXPECT_FALSE(layer_impl->override_child_paint_flags()); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeSurfaceLayerTest, |
| UpdateSurfaceRangeAndDeadline) { |
| constexpr int kSurfaceLayerId = 2; |
| constexpr uint32_t kUpdatedDeadlineInFrames = 5u; |
| |
| // Initial update: Create SurfaceLayer with default range and deadline. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kSurface, |
| kSurfaceLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::SurfaceLayerImpl* layer_impl = |
| GetSurfaceLayerFromActiveTree(kSurfaceLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->range(), kDefaultSurfaceRange); |
| EXPECT_EQ(layer_impl->deadline_in_frames(), kDefaultDeadlineInFrames); |
| |
| // Second update: Update surface_range and deadline_in_frames. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = |
| CreateManualLayer(kSurfaceLayerId, cc::mojom::LayerType::kSurface); |
| auto& surface_extra2 = layer_props2->layer_extra->get_surface_layer_extra(); |
| LocalSurfaceId new_lsi(4, base::UnguessableToken::CreateForTesting(5, 6)); |
| surface_extra2->surface_range = |
| SurfaceRange(std::nullopt, SurfaceId(kDefaultFrameSinkId, new_lsi)); |
| surface_extra2->deadline_in_frames = kUpdatedDeadlineInFrames; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->range().end(), SurfaceId(kDefaultFrameSinkId, new_lsi)); |
| EXPECT_EQ(layer_impl->deadline_in_frames(), kUpdatedDeadlineInFrames); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeSurfaceLayerTest, |
| UpdateWillDrawNeedsReset) { |
| constexpr int kSurfaceLayerId = 2; |
| |
| // Initial update: Create SurfaceLayer with default will_draw_needs_reset. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kSurface, |
| kSurfaceLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::SurfaceLayerImpl* layer_impl1 = |
| GetSurfaceLayerFromActiveTree(kSurfaceLayerId); |
| ASSERT_NE(nullptr, layer_impl1); |
| EXPECT_EQ(layer_impl1->will_draw_needs_reset(), kDefaultWillDrawNeedsReset); |
| |
| // Second update: Set will_draw_needs_reset to true. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = |
| CreateManualLayer(kSurfaceLayerId, cc::mojom::LayerType::kSurface); |
| auto& surface_extra2 = layer_props2->layer_extra->get_surface_layer_extra(); |
| surface_extra2->will_draw_needs_reset = true; |
| update2->layers.push_back(std::move(layer_props2)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| cc::SurfaceLayerImpl* layer_impl2 = |
| GetSurfaceLayerFromActiveTree(kSurfaceLayerId); |
| ASSERT_NE(nullptr, layer_impl2); |
| EXPECT_TRUE(layer_impl2->will_draw_needs_reset()); |
| } |
| |
| // Test fixture for ScrollbarLayerImplBase specific property updates, |
| // parameterized by scrollbar layer type. |
| class LayerContextImplUpdateDisplayTreeScrollbarLayerBaseTest |
| : public LayerContextImplLayerLifecycleTest, |
| public testing::WithParamInterface<cc::mojom::LayerType> { |
| protected: |
| cc::ScrollbarLayerImplBase* GetScrollbarLayerBaseFromActiveTree( |
| int layer_id) { |
| cc::LayerImpl* layer = GetLayerFromActiveTree(layer_id); |
| if (layer && layer->IsScrollbarLayer()) { |
| return static_cast<cc::ScrollbarLayerImplBase*>(layer); |
| } |
| return nullptr; |
| } |
| |
| mojom::ScrollbarLayerBaseExtra* GetScrollbarBaseExtra( |
| mojom::LayerExtra& layer_extra) { |
| switch (GetParam()) { |
| case cc::mojom::LayerType::kSolidColorScrollbar: |
| return layer_extra.get_solid_color_scrollbar_layer_extra() |
| ->scrollbar_base_extra.get(); |
| case cc::mojom::LayerType::kPaintedScrollbar: |
| return layer_extra.get_painted_scrollbar_layer_extra() |
| ->scrollbar_base_extra.get(); |
| case cc::mojom::LayerType::kNinePatchThumbScrollbar: |
| return layer_extra.get_nine_patch_thumb_scrollbar_layer_extra() |
| ->scrollbar_base_extra.get(); |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| const gfx::Size kDefaultScrollbarLayerBounds = gfx::Size(10, 100); |
| }; |
| |
| TEST_P(LayerContextImplUpdateDisplayTreeScrollbarLayerBaseTest, |
| InitialIsHorizontalOrientation) { |
| constexpr int kScrollbarLayerId1 = 2; |
| constexpr int kScrollbarLayerId2 = 3; |
| |
| // Test 1: Initial is_horizontal_orientation setting is respected. |
| auto update1 = CreateDefaultUpdate(); |
| auto layer_props1 = CreateManualLayer(kScrollbarLayerId1, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId2, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| |
| GetScrollbarBaseExtra(*layer_props1->layer_extra)->is_horizontal_orientation = |
| false; |
| GetScrollbarBaseExtra(*layer_props2->layer_extra)->is_horizontal_orientation = |
| true; |
| |
| update1->layers.push_back(std::move(layer_props1)); |
| update1->layers.push_back(std::move(layer_props2)); |
| layer_order_.push_back(kScrollbarLayerId1); |
| layer_order_.push_back(kScrollbarLayerId2); |
| update1->layer_order = layer_order_; |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::ScrollbarLayerImplBase* layer_impl1 = |
| GetScrollbarLayerBaseFromActiveTree(kScrollbarLayerId1); |
| cc::ScrollbarLayerImplBase* layer_impl2 = |
| GetScrollbarLayerBaseFromActiveTree(kScrollbarLayerId2); |
| ASSERT_NE(nullptr, layer_impl1); |
| ASSERT_NE(nullptr, layer_impl2); |
| EXPECT_EQ(layer_impl1->orientation(), cc::ScrollbarOrientation::kVertical); |
| EXPECT_EQ(layer_impl2->orientation(), cc::ScrollbarOrientation::kHorizontal); |
| |
| // Test 2: Updating the is_horizontal_orientation property on an |
| // existing layer has no effect. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props3 = CreateManualLayer(kScrollbarLayerId1, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| auto layer_props4 = CreateManualLayer(kScrollbarLayerId2, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| |
| // Try to swap the values |
| GetScrollbarBaseExtra(*layer_props3->layer_extra)->is_horizontal_orientation = |
| true; |
| GetScrollbarBaseExtra(*layer_props4->layer_extra)->is_horizontal_orientation = |
| false; |
| |
| update2->layers.push_back(std::move(layer_props3)); |
| update2->layers.push_back(std::move(layer_props4)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| |
| EXPECT_EQ(layer_impl1->orientation(), cc::ScrollbarOrientation::kVertical); |
| EXPECT_EQ(layer_impl2->orientation(), cc::ScrollbarOrientation::kHorizontal); |
| } |
| |
| TEST_P(LayerContextImplUpdateDisplayTreeScrollbarLayerBaseTest, |
| InitialIsLeftSideVerticalScrollbar) { |
| constexpr int kScrollbarLayerId1 = 2; |
| constexpr int kScrollbarLayerId2 = 3; |
| |
| // Test 1: Initial is_left_side_vertical_scrollbar setting is respected. |
| auto update1 = CreateDefaultUpdate(); |
| auto layer_props1 = CreateManualLayer(kScrollbarLayerId1, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId2, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| GetScrollbarBaseExtra(*layer_props1->layer_extra) |
| ->is_left_side_vertical_scrollbar = false; |
| GetScrollbarBaseExtra(*layer_props2->layer_extra) |
| ->is_left_side_vertical_scrollbar = true; |
| |
| update1->layers.push_back(std::move(layer_props1)); |
| update1->layers.push_back(std::move(layer_props2)); |
| layer_order_.push_back(kScrollbarLayerId1); |
| layer_order_.push_back(kScrollbarLayerId2); |
| update1->layer_order = layer_order_; |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::ScrollbarLayerImplBase* layer_impl1 = |
| GetScrollbarLayerBaseFromActiveTree(kScrollbarLayerId1); |
| cc::ScrollbarLayerImplBase* layer_impl2 = |
| GetScrollbarLayerBaseFromActiveTree(kScrollbarLayerId2); |
| ASSERT_NE(nullptr, layer_impl1); |
| ASSERT_NE(nullptr, layer_impl2); |
| EXPECT_EQ(layer_impl1->is_left_side_vertical_scrollbar(), false); |
| EXPECT_EQ(layer_impl2->is_left_side_vertical_scrollbar(), true); |
| |
| // Test 2: Updating the is_left_side_vertical_scrollbar property on an |
| // existing layer has no effect. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props3 = CreateManualLayer(kScrollbarLayerId1, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| auto layer_props4 = CreateManualLayer(kScrollbarLayerId2, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| |
| // Try to swap the values. |
| GetScrollbarBaseExtra(*layer_props3->layer_extra) |
| ->is_left_side_vertical_scrollbar = true; |
| GetScrollbarBaseExtra(*layer_props4->layer_extra) |
| ->is_left_side_vertical_scrollbar = false; |
| |
| update2->layers.push_back(std::move(layer_props3)); |
| update2->layers.push_back(std::move(layer_props4)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| |
| EXPECT_EQ(layer_impl1->is_left_side_vertical_scrollbar(), false); |
| EXPECT_EQ(layer_impl2->is_left_side_vertical_scrollbar(), true); |
| } |
| |
| TEST_P(LayerContextImplUpdateDisplayTreeScrollbarLayerBaseTest, |
| InitialIsOverlayScrollbar) { |
| constexpr int kScrollbarLayerId1 = 2; |
| constexpr int kScrollbarLayerId2 = 3; |
| |
| // Test 1: Initial is_overlay_scrollbar setting is respected. |
| auto update1 = CreateDefaultUpdate(); |
| auto layer_props1 = CreateManualLayer(kScrollbarLayerId1, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId2, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| |
| GetScrollbarBaseExtra(*layer_props1->layer_extra)->is_overlay_scrollbar = |
| false; |
| GetScrollbarBaseExtra(*layer_props2->layer_extra)->is_overlay_scrollbar = |
| true; |
| |
| update1->layers.push_back(std::move(layer_props1)); |
| update1->layers.push_back(std::move(layer_props2)); |
| layer_order_.push_back(kScrollbarLayerId1); |
| layer_order_.push_back(kScrollbarLayerId2); |
| update1->layer_order = layer_order_; |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::ScrollbarLayerImplBase* layer_impl1 = |
| GetScrollbarLayerBaseFromActiveTree(kScrollbarLayerId1); |
| cc::ScrollbarLayerImplBase* layer_impl2 = |
| GetScrollbarLayerBaseFromActiveTree(kScrollbarLayerId2); |
| ASSERT_NE(nullptr, layer_impl1); |
| ASSERT_NE(nullptr, layer_impl2); |
| |
| EXPECT_FALSE(layer_impl1->is_overlay_scrollbar()); |
| EXPECT_TRUE(layer_impl2->is_overlay_scrollbar()); |
| |
| // Test 2: Updating is_overlay_scrollbar mode is respected. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props3 = CreateManualLayer(kScrollbarLayerId1, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| auto layer_props4 = CreateManualLayer(kScrollbarLayerId2, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| |
| // Try to swap the values |
| GetScrollbarBaseExtra(*layer_props3->layer_extra)->is_overlay_scrollbar = |
| true; |
| GetScrollbarBaseExtra(*layer_props4->layer_extra)->is_overlay_scrollbar = |
| false; |
| |
| update2->layers.push_back(std::move(layer_props3)); |
| update2->layers.push_back(std::move(layer_props4)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| |
| EXPECT_TRUE(layer_impl1->is_overlay_scrollbar()); |
| EXPECT_FALSE(layer_impl2->is_overlay_scrollbar()); |
| } |
| |
| TEST_P(LayerContextImplUpdateDisplayTreeScrollbarLayerBaseTest, |
| UpdateScrollElementId) { |
| constexpr int kScrollbarLayerId = 2; |
| const cc::ElementId kInitialScrollElementId = kDefaultScrollElementId; |
| const cc::ElementId kUpdatedScrollElementId1 = cc::ElementId(12345); |
| const cc::ElementId kUpdatedScrollElementId2 = cc::ElementId(54321); |
| |
| // Initial update: Create with default scroll_element_id. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), GetParam(), kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::ScrollbarLayerImplBase* layer_impl = |
| GetScrollbarLayerBaseFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->scroll_element_id(), kInitialScrollElementId); |
| |
| // Second update: Update scroll_element_id. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| GetScrollbarBaseExtra(*layer_props2->layer_extra)->scroll_element_id = |
| kUpdatedScrollElementId1; |
| update2->layers.push_back(std::move(layer_props2)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->scroll_element_id(), kUpdatedScrollElementId1); |
| |
| // Third update: Update scroll_element_id again. |
| auto update3 = CreateDefaultUpdate(); |
| auto layer_props3 = CreateManualLayer(kScrollbarLayerId, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| GetScrollbarBaseExtra(*layer_props3->layer_extra)->scroll_element_id = |
| kUpdatedScrollElementId2; |
| update3->layers.push_back(std::move(layer_props3)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| EXPECT_EQ(layer_impl->scroll_element_id(), kUpdatedScrollElementId2); |
| } |
| |
| TEST_P(LayerContextImplUpdateDisplayTreeScrollbarLayerBaseTest, |
| UpdateIsWebTest) { |
| constexpr int kScrollbarLayerId = 2; |
| |
| // Initial update: Create with default is_web_test (false). |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), GetParam(), kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::ScrollbarLayerImplBase* layer_impl = |
| GetScrollbarLayerBaseFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->is_web_test(), kDefaultIsWebTest); |
| |
| // Second update: Set is_web_test to true. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| GetScrollbarBaseExtra(*layer_props2->layer_extra)->is_web_test = true; |
| update2->layers.push_back(std::move(layer_props2)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_TRUE(layer_impl->is_web_test()); |
| |
| // Third update: Set is_web_test back to false. |
| auto update3 = CreateDefaultUpdate(); |
| auto layer_props3 = CreateManualLayer(kScrollbarLayerId, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| GetScrollbarBaseExtra(*layer_props3->layer_extra)->is_web_test = false; |
| update3->layers.push_back(std::move(layer_props3)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| EXPECT_FALSE(layer_impl->is_web_test()); |
| } |
| |
| TEST_P(LayerContextImplUpdateDisplayTreeScrollbarLayerBaseTest, |
| UpdateThumbThicknessScaleFactor) { |
| constexpr int kScrollbarLayerId = 2; |
| constexpr float kUpdatedFactor = 0.5f; |
| |
| // Initial update. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), GetParam(), kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::ScrollbarLayerImplBase* layer_impl = |
| GetScrollbarLayerBaseFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->thumb_thickness_scale_factor(), |
| kDefaultThumbThicknessScaleFactor); |
| |
| // Second update. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| GetScrollbarBaseExtra(*layer_props2->layer_extra) |
| ->thumb_thickness_scale_factor = kUpdatedFactor; |
| update2->layers.push_back(std::move(layer_props2)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->thumb_thickness_scale_factor(), kUpdatedFactor); |
| } |
| |
| TEST_P(LayerContextImplUpdateDisplayTreeScrollbarLayerBaseTest, |
| UpdateCurrentPosAndLengthsAndVerticalAdjustAndTickmarks) { |
| constexpr int kScrollbarLayerId = 2; |
| constexpr float kUpdatedCurrentPos = 10.f; |
| constexpr float kUpdatedClipLayerLength = 100.f; |
| constexpr float kUpdatedScrollLayerLength = 200.f; |
| constexpr float kUpdatedVerticalAdjust = 5.f; |
| |
| // Initial update. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), GetParam(), kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::ScrollbarLayerImplBase* layer_impl = |
| GetScrollbarLayerBaseFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->current_pos(), kDefaultCurrentPos); |
| EXPECT_EQ(layer_impl->clip_layer_length(), kDefaultClipLayerLength); |
| EXPECT_EQ(layer_impl->scroll_layer_length(), kDefaultScrollLayerLength); |
| EXPECT_EQ(layer_impl->vertical_adjust(), kDefaultVerticalAdjust); |
| EXPECT_EQ(layer_impl->has_find_in_page_tickmarks(), |
| kDefaultHasFindInPageTickmarks); |
| |
| // Second update. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, GetParam(), |
| kDefaultScrollbarLayerBounds); |
| mojom::ScrollbarLayerBaseExtra* base_extra2 = |
| GetScrollbarBaseExtra(*layer_props2->layer_extra); |
| base_extra2->current_pos = kUpdatedCurrentPos; |
| base_extra2->clip_layer_length = kUpdatedClipLayerLength; |
| base_extra2->scroll_layer_length = kUpdatedScrollLayerLength; |
| base_extra2->vertical_adjust = kUpdatedVerticalAdjust; |
| base_extra2->has_find_in_page_tickmarks = true; |
| update2->layers.push_back(std::move(layer_props2)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->current_pos(), kUpdatedCurrentPos); |
| EXPECT_EQ(layer_impl->clip_layer_length(), kUpdatedClipLayerLength); |
| EXPECT_EQ(layer_impl->scroll_layer_length(), kUpdatedScrollLayerLength); |
| EXPECT_EQ(layer_impl->vertical_adjust(), kUpdatedVerticalAdjust); |
| EXPECT_TRUE(layer_impl->has_find_in_page_tickmarks()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| AllScrollbarTypes, |
| LayerContextImplUpdateDisplayTreeScrollbarLayerBaseTest, |
| testing::Values(cc::mojom::LayerType::kSolidColorScrollbar, |
| cc::mojom::LayerType::kNinePatchThumbScrollbar, |
| cc::mojom::LayerType::kPaintedScrollbar), |
| [](const testing::TestParamInfo< |
| LayerContextImplUpdateDisplayTreeScrollbarLayerBaseTest::ParamType>& |
| info) { |
| switch (info.param) { |
| case cc::mojom::LayerType::kSolidColorScrollbar: |
| return "SolidColorScrollbar"; |
| case cc::mojom::LayerType::kPaintedScrollbar: |
| return "PaintedScrollbar"; |
| case cc::mojom::LayerType::kNinePatchThumbScrollbar: |
| return "NinePatchThumbScrollbar"; |
| default: |
| return "UnknownScrollbarType"; |
| } |
| }); |
| |
| // Test fixture for SolidColorScrollbarLayerImpl specific property updates. |
| class LayerContextImplUpdateDisplayTreeSolidColorScrollbarLayerTest |
| : public LayerContextImplUpdateDisplayTreeScrollbarLayerBaseTest { |
| protected: |
| cc::SolidColorScrollbarLayerImpl* GetSolidColorScrollbarLayerFromActiveTree( |
| int layer_id) { |
| cc::LayerImpl* layer = GetLayerFromActiveTree(layer_id); |
| if (!layer || |
| layer->GetLayerType() != cc::mojom::LayerType::kSolidColorScrollbar) { |
| return nullptr; |
| } |
| return static_cast<cc::SolidColorScrollbarLayerImpl*>(layer); |
| } |
| }; |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeSolidColorScrollbarLayerTest, |
| InitialThumbThickness) { |
| constexpr int kScrollbarLayerId = 2; |
| constexpr int kInitialThumbThickness = 5; |
| constexpr int kUpdatedThumbThickness = 10; |
| |
| // Test 1: Create SolidColorScrollbarLayer with a specific |
| // thumb_thickness. |
| auto update1 = CreateDefaultUpdate(); |
| auto layer_props1 = CreateManualLayer( |
| kScrollbarLayerId, cc::mojom::LayerType::kSolidColorScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra1 = |
| layer_props1->layer_extra->get_solid_color_scrollbar_layer_extra(); |
| scrollbar_extra1->thumb_thickness = kInitialThumbThickness; |
| update1->layers.push_back(std::move(layer_props1)); |
| // AddDefaultLayerToUpdate normally handles this. Since we used |
| // CreateManualLayer, update the layer_order here. |
| layer_order_.push_back(kScrollbarLayerId); |
| update1->layer_order = layer_order_; |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::SolidColorScrollbarLayerImpl* layer_impl = |
| GetSolidColorScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->thumb_thickness(), kInitialThumbThickness); |
| |
| // Test 2: Updating the thumb_thickness should have no effect. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer( |
| kScrollbarLayerId, cc::mojom::LayerType::kSolidColorScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_solid_color_scrollbar_layer_extra(); |
| scrollbar_extra2->thumb_thickness = kUpdatedThumbThickness; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| |
| EXPECT_EQ(layer_impl->thumb_thickness(), kInitialThumbThickness); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeSolidColorScrollbarLayerTest, |
| InitialTrackStart) { |
| constexpr int kScrollbarLayerId = 2; |
| constexpr int kInitialTrackStart = 2; |
| constexpr int kUpdatedTrackStart = 4; |
| |
| // Test 1: Create SolidColorScrollbarLayer with a specific track_start. |
| auto update1 = CreateDefaultUpdate(); |
| auto layer_props1 = CreateManualLayer( |
| kScrollbarLayerId, cc::mojom::LayerType::kSolidColorScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra1 = |
| layer_props1->layer_extra->get_solid_color_scrollbar_layer_extra(); |
| scrollbar_extra1->track_start = kInitialTrackStart; |
| update1->layers.push_back(std::move(layer_props1)); |
| // AddDefaultLayerToUpdate normally handles this. Since we used |
| // CreateManualLayer, update the layer_order here. |
| layer_order_.push_back(kScrollbarLayerId); |
| update1->layer_order = layer_order_; |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::SolidColorScrollbarLayerImpl* layer_impl = |
| GetSolidColorScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->track_start(), kInitialTrackStart); |
| |
| // Test 2: Updating the track_start should have no effect. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer( |
| kScrollbarLayerId, cc::mojom::LayerType::kSolidColorScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_solid_color_scrollbar_layer_extra(); |
| scrollbar_extra2->track_start = kUpdatedTrackStart; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->track_start(), kInitialTrackStart); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeSolidColorScrollbarLayerTest, |
| UpdateColor) { |
| constexpr int kScrollbarLayerId = 2; |
| const SkColor4f kUpdatedScrollbarColor = SkColors::kRed; |
| |
| // Initial update: Create SolidColorScrollbarLayer with default color. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kSolidColorScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::SolidColorScrollbarLayerImpl* layer_impl = |
| GetSolidColorScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->color(), kDefaultSolidColorScrollbarColor); // Default |
| |
| // Second update: Update color. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer( |
| kScrollbarLayerId, cc::mojom::LayerType::kSolidColorScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_solid_color_scrollbar_layer_extra(); |
| scrollbar_extra2->color = kUpdatedScrollbarColor; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->color(), kUpdatedScrollbarColor); |
| } |
| |
| // Test fixture for NinePatchThumbScrollbarLayerImpl specific property updates. |
| class LayerContextImplUpdateDisplayTreeNinePatchThumbScrollbarLayerTest |
| : public LayerContextImplUpdateDisplayTreeScrollbarLayerBaseTest { |
| protected: |
| cc::NinePatchThumbScrollbarLayerImpl* |
| GetNinePatchThumbScrollbarLayerFromActiveTree(int layer_id) { |
| cc::LayerImpl* layer = GetLayerFromActiveTree(layer_id); |
| if (!layer || layer->GetLayerType() != |
| cc::mojom::LayerType::kNinePatchThumbScrollbar) { |
| return nullptr; |
| } |
| return static_cast<cc::NinePatchThumbScrollbarLayerImpl*>(layer); |
| } |
| }; |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeNinePatchThumbScrollbarLayerTest, |
| UpdateThumbThicknessAndLength) { |
| constexpr int kScrollbarLayerId = 2; |
| |
| // Initial update: Create with default thickness and length. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kNinePatchThumbScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::NinePatchThumbScrollbarLayerImpl* layer_impl = |
| GetNinePatchThumbScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->thumb_thickness(), |
| kDefaultNinePatchThumbScrollbarThumbThickness); |
| EXPECT_EQ(layer_impl->thumb_length(), |
| kDefaultNinePatchThumbScrollbarThumbLength); |
| |
| // Second update: Update thumb_thickness and thumb_length. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer( |
| kScrollbarLayerId, cc::mojom::LayerType::kNinePatchThumbScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_nine_patch_thumb_scrollbar_layer_extra(); |
| scrollbar_extra2->thumb_thickness = 5; |
| scrollbar_extra2->thumb_length = 20; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->thumb_thickness(), 5); |
| EXPECT_EQ(layer_impl->thumb_length(), 20); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeNinePatchThumbScrollbarLayerTest, |
| UpdateTrackStartAndLength) { |
| constexpr int kScrollbarLayerId = 2; |
| |
| // Initial update: Create with default track_start and track_length. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kNinePatchThumbScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::NinePatchThumbScrollbarLayerImpl* layer_impl = |
| GetNinePatchThumbScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->track_start(), |
| kDefaultNinePatchThumbScrollbarTrackStart); |
| EXPECT_EQ(layer_impl->track_length(), |
| kDefaultNinePatchThumbScrollbarTrackLength); |
| |
| // Second update: Update track_start and track_length. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer( |
| kScrollbarLayerId, cc::mojom::LayerType::kNinePatchThumbScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_nine_patch_thumb_scrollbar_layer_extra(); |
| scrollbar_extra2->track_start = 2; |
| scrollbar_extra2->track_length = 90; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->track_start(), 2); |
| EXPECT_EQ(layer_impl->track_length(), 90); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeNinePatchThumbScrollbarLayerTest, |
| UpdateImageBoundsAndAperture) { |
| constexpr int kScrollbarLayerId = 2; |
| |
| // Initial update: Create with default image_bounds and aperture. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kNinePatchThumbScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::NinePatchThumbScrollbarLayerImpl* layer_impl = |
| GetNinePatchThumbScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->image_bounds(), |
| kDefaultNinePatchThumbScrollbarImageBounds); |
| EXPECT_EQ(layer_impl->aperture(), kDefaultNinePatchThumbScrollbarAperture); |
| |
| // Second update: Update image_bounds and aperture. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer( |
| kScrollbarLayerId, cc::mojom::LayerType::kNinePatchThumbScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_nine_patch_thumb_scrollbar_layer_extra(); |
| scrollbar_extra2->image_bounds = gfx::Size(30, 30); |
| scrollbar_extra2->aperture = gfx::Rect(5, 5, 20, 20); |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->image_bounds(), gfx::Size(30, 30)); |
| EXPECT_EQ(layer_impl->aperture(), gfx::Rect(5, 5, 20, 20)); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeNinePatchThumbScrollbarLayerTest, |
| UpdateUIResourceIds) { |
| constexpr int kScrollbarLayerId = 2; |
| constexpr cc::UIResourceId kThumbId = 101; |
| constexpr cc::UIResourceId kTrackId = 102; |
| |
| // Initial update: Create with default resource IDs. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kNinePatchThumbScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::NinePatchThumbScrollbarLayerImpl* layer_impl = |
| GetNinePatchThumbScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->thumb_ui_resource_id(), |
| kDefaultNinePatchThumbScrollbarThumbUIResourceId); |
| EXPECT_EQ(layer_impl->track_and_buttons_ui_resource_id(), |
| kDefaultNinePatchThumbScrollbarTrackAndButtonsUIResourceId); |
| |
| // Second update: Update resource IDs. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer( |
| kScrollbarLayerId, cc::mojom::LayerType::kNinePatchThumbScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_nine_patch_thumb_scrollbar_layer_extra(); |
| scrollbar_extra2->thumb_ui_resource_id = kThumbId; |
| scrollbar_extra2->track_and_buttons_ui_resource_id = kTrackId; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->thumb_ui_resource_id(), kThumbId); |
| EXPECT_EQ(layer_impl->track_and_buttons_ui_resource_id(), kTrackId); |
| } |
| |
| // Test fixture for PaintedScrollbarLayerImpl specific property updates. |
| class LayerContextImplUpdateDisplayTreePaintedScrollbarLayerTest |
| : public LayerContextImplUpdateDisplayTreeScrollbarLayerBaseTest { |
| protected: |
| cc::PaintedScrollbarLayerImpl* GetPaintedScrollbarLayerFromActiveTree( |
| int layer_id) { |
| cc::LayerImpl* layer = GetLayerFromActiveTree(layer_id); |
| if (!layer || |
| layer->GetLayerType() != cc::mojom::LayerType::kPaintedScrollbar) { |
| return nullptr; |
| } |
| return static_cast<cc::PaintedScrollbarLayerImpl*>(layer); |
| } |
| }; |
| |
| TEST_F(LayerContextImplUpdateDisplayTreePaintedScrollbarLayerTest, |
| UpdateInternalContentsScaleAndBounds) { |
| constexpr int kScrollbarLayerId = 2; |
| constexpr float kUpdatedInternalContentsScale = 1.5f; |
| const gfx::Size kUpdatedInternalContentBounds(15, 150); |
| |
| // Initial update: Create with default values. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::PaintedScrollbarLayerImpl* layer_impl = |
| GetPaintedScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->internal_contents_scale(), |
| kDefaultPaintedScrollbarInternalContentsScale); |
| EXPECT_EQ(layer_impl->internal_content_bounds(), |
| kDefaultPaintedScrollbarInternalContentBounds); |
| |
| // Second update: Update internal_contents_scale and internal_content_bounds. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_painted_scrollbar_layer_extra(); |
| scrollbar_extra2->internal_contents_scale = kUpdatedInternalContentsScale; |
| scrollbar_extra2->internal_content_bounds = kUpdatedInternalContentBounds; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->internal_contents_scale(), |
| kUpdatedInternalContentsScale); |
| EXPECT_EQ(layer_impl->internal_content_bounds(), |
| kUpdatedInternalContentBounds); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreePaintedScrollbarLayerTest, |
| UpdateThumbThicknessAndLength) { |
| constexpr int kScrollbarLayerId = 2; |
| constexpr int kUpdatedThumbThickness = 5; |
| constexpr int kUpdatedThumbLength = 20; |
| |
| // Initial update. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::PaintedScrollbarLayerImpl* layer_impl = |
| GetPaintedScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->thumb_thickness(), |
| kDefaultPaintedScrollbarThumbThickness); |
| EXPECT_EQ(layer_impl->thumb_length(), kDefaultPaintedScrollbarThumbLength); |
| |
| // Second update. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_painted_scrollbar_layer_extra(); |
| scrollbar_extra2->thumb_thickness = kUpdatedThumbThickness; |
| scrollbar_extra2->thumb_length = kUpdatedThumbLength; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->thumb_thickness(), kUpdatedThumbThickness); |
| EXPECT_EQ(layer_impl->thumb_length(), kUpdatedThumbLength); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreePaintedScrollbarLayerTest, |
| UpdateButtonAndTrackRects) { |
| constexpr int kScrollbarLayerId = 2; |
| const gfx::Rect kUpdatedBackButtonRect(0, 0, 10, 10); |
| const gfx::Rect kUpdatedForwardButtonRect(0, 90, 10, 10); |
| const gfx::Rect kUpdatedTrackRect(0, 10, 10, 80); |
| |
| // Initial update. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::PaintedScrollbarLayerImpl* layer_impl = |
| GetPaintedScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->back_button_rect(), |
| kDefaultPaintedScrollbarBackButtonRect); |
| EXPECT_EQ(layer_impl->forward_button_rect(), |
| kDefaultPaintedScrollbarForwardButtonRect); |
| EXPECT_EQ(layer_impl->track_rect(), kDefaultPaintedScrollbarTrackRect); |
| |
| // Second update. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_painted_scrollbar_layer_extra(); |
| scrollbar_extra2->back_button_rect = kUpdatedBackButtonRect; |
| scrollbar_extra2->forward_button_rect = kUpdatedForwardButtonRect; |
| scrollbar_extra2->track_rect = kUpdatedTrackRect; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->back_button_rect(), kUpdatedBackButtonRect); |
| EXPECT_EQ(layer_impl->forward_button_rect(), kUpdatedForwardButtonRect); |
| EXPECT_EQ(layer_impl->track_rect(), kUpdatedTrackRect); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreePaintedScrollbarLayerTest, |
| UpdatePaintedOpacityAndThumbColor) { |
| constexpr int kScrollbarLayerId = 2; |
| constexpr float kUpdatedPaintedOpacity = 0.5f; |
| const SkColor4f kUpdatedThumbColor = SkColors::kGreen; |
| |
| // Initial update. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::PaintedScrollbarLayerImpl* layer_impl = |
| GetPaintedScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->painted_opacity(), |
| kDefaultPaintedScrollbarPaintedOpacity); |
| EXPECT_EQ(layer_impl->thumb_color(), kDefaultPaintedScrollbarThumbColor); |
| |
| // Second update. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_painted_scrollbar_layer_extra(); |
| scrollbar_extra2->painted_opacity = kUpdatedPaintedOpacity; |
| scrollbar_extra2->thumb_color = kUpdatedThumbColor; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->painted_opacity(), kUpdatedPaintedOpacity); |
| ASSERT_TRUE(layer_impl->thumb_color().has_value()); |
| EXPECT_EQ(layer_impl->thumb_color().value(), kUpdatedThumbColor); |
| |
| // Third update: Clearing thumb_color is not supported by |
| // PaintedScrollbarLayerImpl. Check that it has no effect. |
| auto update3 = CreateDefaultUpdate(); |
| auto layer_props3 = CreateManualLayer(kScrollbarLayerId, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra3 = |
| layer_props3->layer_extra->get_painted_scrollbar_layer_extra(); |
| scrollbar_extra3->thumb_color = std::nullopt; // Explicitly clear |
| update3->layers.push_back(std::move(layer_props3)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| EXPECT_TRUE(layer_impl->thumb_color().has_value()); |
| EXPECT_EQ(layer_impl->thumb_color().value(), kUpdatedThumbColor); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreePaintedScrollbarLayerTest, |
| UpdateJumpOnTrackClick) { |
| constexpr int kScrollbarLayerId = 2; |
| constexpr bool kUpdatedJumpOnTrackClick = true; |
| |
| // Initial update. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::PaintedScrollbarLayerImpl* layer_impl = |
| GetPaintedScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->jump_on_track_click(), |
| kDefaultPaintedScrollbarJumpOnTrackClick); |
| |
| // Second update. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_painted_scrollbar_layer_extra(); |
| scrollbar_extra2->jump_on_track_click = kUpdatedJumpOnTrackClick; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_TRUE(layer_impl->jump_on_track_click()); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreePaintedScrollbarLayerTest, |
| UpdateSupportsDragSnapBack) { |
| constexpr int kScrollbarLayerId = 2; |
| constexpr bool kUpdatedSupportsDragSnapBack = true; |
| |
| // Initial update. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::PaintedScrollbarLayerImpl* layer_impl = |
| GetPaintedScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->supports_drag_snap_back(), |
| kDefaultPaintedScrollbarSupportsDragSnapBack); |
| |
| // Second update. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_painted_scrollbar_layer_extra(); |
| scrollbar_extra2->supports_drag_snap_back = kUpdatedSupportsDragSnapBack; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_TRUE(layer_impl->supports_drag_snap_back()); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreePaintedScrollbarLayerTest, |
| UpdateUIResourceIds) { |
| constexpr int kScrollbarLayerId = 2; |
| constexpr cc::UIResourceId kThumbId = 101; |
| constexpr cc::UIResourceId kTrackId = 102; |
| |
| // Initial update. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::PaintedScrollbarLayerImpl* layer_impl = |
| GetPaintedScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->thumb_ui_resource_id(), |
| kDefaultPaintedScrollbarThumbUIResourceId); |
| EXPECT_EQ(layer_impl->track_and_buttons_ui_resource_id(), |
| kDefaultPaintedScrollbarTrackAndButtonsUIResourceId); |
| |
| // Second update. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_painted_scrollbar_layer_extra(); |
| scrollbar_extra2->thumb_ui_resource_id = kThumbId; |
| scrollbar_extra2->track_and_buttons_ui_resource_id = kTrackId; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->thumb_ui_resource_id(), kThumbId); |
| EXPECT_EQ(layer_impl->track_and_buttons_ui_resource_id(), kTrackId); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreePaintedScrollbarLayerTest, |
| UpdateUsesNinePatchTrackAndButtons) { |
| constexpr int kScrollbarLayerId = 2; |
| constexpr bool kUpdatedUsesNinePatch = true; |
| |
| // Initial update. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::PaintedScrollbarLayerImpl* layer_impl = |
| GetPaintedScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->uses_nine_patch_track_and_buttons(), |
| kDefaultPaintedScrollbarUsesNinePatchTrackAndButtons); |
| |
| // Second update. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_painted_scrollbar_layer_extra(); |
| scrollbar_extra2->uses_nine_patch_track_and_buttons = kUpdatedUsesNinePatch; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_TRUE(layer_impl->uses_nine_patch_track_and_buttons()); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreePaintedScrollbarLayerTest, |
| UpdateTrackAndButtonsImageBoundsAndAperture) { |
| constexpr int kScrollbarLayerId = 2; |
| const gfx::Size kUpdatedImageBounds(50, 50); |
| const gfx::Rect kUpdatedAperture(10, 10, 30, 30); |
| |
| // Initial update. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::PaintedScrollbarLayerImpl* layer_impl = |
| GetPaintedScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->track_and_buttons_image_bounds(), |
| kDefaultPaintedScrollbarTrackAndButtonsImageBounds); |
| EXPECT_EQ(layer_impl->track_and_buttons_aperture(), |
| kDefaultPaintedScrollbarTrackAndButtonsAperture); |
| |
| // Second update. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_painted_scrollbar_layer_extra(); |
| scrollbar_extra2->track_and_buttons_image_bounds = kUpdatedImageBounds; |
| scrollbar_extra2->track_and_buttons_aperture = kUpdatedAperture; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->track_and_buttons_image_bounds(), kUpdatedImageBounds); |
| EXPECT_EQ(layer_impl->track_and_buttons_aperture(), kUpdatedAperture); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreePaintedScrollbarLayerTest, |
| UpdateScrollElementId) { |
| constexpr int kScrollbarLayerId = 2; |
| const cc::ElementId kScrollElementId1 = cc::ElementId(12345); |
| const cc::ElementId kScrollElementId2 = cc::ElementId(54321); |
| |
| // Initial update. |
| auto update1 = CreateDefaultUpdate(); |
| auto layer_props1 = CreateManualLayer(kScrollbarLayerId, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| gfx::Size(10, 100)); |
| auto& scrollbar_extra1 = |
| layer_props1->layer_extra->get_painted_scrollbar_layer_extra(); |
| scrollbar_extra1->scrollbar_base_extra->scroll_element_id = kScrollElementId1; |
| update1->layers.push_back(std::move(layer_props1)); |
| layer_order_.push_back(kScrollbarLayerId); |
| update1->layer_order = layer_order_; |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::PaintedScrollbarLayerImpl* layer_impl = |
| GetPaintedScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->scroll_element_id(), kScrollElementId1); |
| |
| // Second update. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_painted_scrollbar_layer_extra(); |
| scrollbar_extra2->scrollbar_base_extra->scroll_element_id = kScrollElementId2; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->scroll_element_id(), kScrollElementId2); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreePaintedScrollbarLayerTest, |
| UpdateIsWebTest) { |
| constexpr int kScrollbarLayerId = 2; |
| constexpr bool kUpdatedIsWebTest = true; |
| |
| // Initial update. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::PaintedScrollbarLayerImpl* layer_impl = |
| GetPaintedScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_FALSE(layer_impl->is_web_test()); // Default |
| |
| // Second update. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_painted_scrollbar_layer_extra(); |
| scrollbar_extra2->scrollbar_base_extra->is_web_test = kUpdatedIsWebTest; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_TRUE(layer_impl->is_web_test()); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreePaintedScrollbarLayerTest, |
| UpdateThumbThicknessScaleFactor) { |
| constexpr int kScrollbarLayerId = 2; |
| constexpr float kUpdatedThumbThicknessScaleFactor = 0.5f; |
| |
| // Initial update. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::PaintedScrollbarLayerImpl* layer_impl = |
| GetPaintedScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->thumb_thickness_scale_factor(), 0.f); // Default |
| |
| // Second update. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_painted_scrollbar_layer_extra(); |
| scrollbar_extra2->scrollbar_base_extra->thumb_thickness_scale_factor = |
| kUpdatedThumbThicknessScaleFactor; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->thumb_thickness_scale_factor(), |
| kUpdatedThumbThicknessScaleFactor); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreePaintedScrollbarLayerTest, |
| UpdateCurrentPosAndLengths) { |
| constexpr int kScrollbarLayerId = 2; |
| constexpr float kUpdatedCurrentPos = 10.f; |
| constexpr float kUpdatedClipLayerLength = 100.f; |
| constexpr float kUpdatedScrollLayerLength = 200.f; |
| |
| // Initial update. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::PaintedScrollbarLayerImpl* layer_impl = |
| GetPaintedScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->current_pos(), 0.f); |
| EXPECT_EQ(layer_impl->clip_layer_length(), 0.f); |
| EXPECT_EQ(layer_impl->scroll_layer_length(), 0.f); |
| |
| // Second update. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_painted_scrollbar_layer_extra(); |
| scrollbar_extra2->scrollbar_base_extra->current_pos = kUpdatedCurrentPos; |
| scrollbar_extra2->scrollbar_base_extra->clip_layer_length = |
| kUpdatedClipLayerLength; |
| scrollbar_extra2->scrollbar_base_extra->scroll_layer_length = |
| kUpdatedScrollLayerLength; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->current_pos(), kUpdatedCurrentPos); |
| EXPECT_EQ(layer_impl->clip_layer_length(), kUpdatedClipLayerLength); |
| EXPECT_EQ(layer_impl->scroll_layer_length(), kUpdatedScrollLayerLength); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreePaintedScrollbarLayerTest, |
| UpdateVerticalAdjustAndHasFindInPageTickmarks) { |
| constexpr int kScrollbarLayerId = 2; |
| constexpr float kUpdatedVerticalAdjust = 5.f; |
| constexpr bool kUpdatedHasFindInPageTickmarks = true; |
| |
| // Initial update. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kScrollbarLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::PaintedScrollbarLayerImpl* layer_impl = |
| GetPaintedScrollbarLayerFromActiveTree(kScrollbarLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->vertical_adjust(), 0.f); |
| EXPECT_FALSE(layer_impl->has_find_in_page_tickmarks()); |
| |
| // Second update. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer(kScrollbarLayerId, |
| cc::mojom::LayerType::kPaintedScrollbar, |
| kDefaultScrollbarLayerBounds); |
| auto& scrollbar_extra2 = |
| layer_props2->layer_extra->get_painted_scrollbar_layer_extra(); |
| scrollbar_extra2->scrollbar_base_extra->vertical_adjust = |
| kUpdatedVerticalAdjust; |
| scrollbar_extra2->scrollbar_base_extra->has_find_in_page_tickmarks = |
| kUpdatedHasFindInPageTickmarks; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->vertical_adjust(), kUpdatedVerticalAdjust); |
| EXPECT_TRUE(layer_impl->has_find_in_page_tickmarks()); |
| } |
| |
| // Test fixture for MirrorLayerImpl specific property updates. |
| class LayerContextImplUpdateDisplayTreeMirrorLayerTest |
| : public LayerContextImplLayerLifecycleTest { |
| protected: |
| cc::MirrorLayerImpl* GetMirrorLayerFromActiveTree(int layer_id) { |
| cc::LayerImpl* layer = GetLayerFromActiveTree(layer_id); |
| if (!layer || layer->GetLayerType() != cc::mojom::LayerType::kMirror) { |
| return nullptr; |
| } |
| return static_cast<cc::MirrorLayerImpl*>(layer); |
| } |
| }; |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeMirrorLayerTest, |
| UpdateMirroredLayerId) { |
| constexpr int kMirrorLayerId = 2; |
| constexpr int kMirroredLayerId1 = 1; // Mirroring the root layer by default. |
| constexpr int kMirroredLayerId2 = 3; |
| |
| // Initial update: Create MirrorLayer with default mirrored_layer_id. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kMirror, |
| kMirrorLayerId); |
| // Create the layer that will be mirrored. |
| AddDefaultLayerToUpdate(update1.get(), cc::mojom::LayerType::kLayer, |
| kMirroredLayerId2); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::MirrorLayerImpl* layer_impl = |
| GetMirrorLayerFromActiveTree(kMirrorLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->mirrored_layer_id(), |
| kDefaultMirrorLayerMirroredLayerId); |
| |
| // Second update: Update mirrored_layer_id to kMirroredLayerId1. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = |
| CreateManualLayer(kMirrorLayerId, cc::mojom::LayerType::kMirror); |
| auto& mirror_extra2 = layer_props2->layer_extra->get_mirror_layer_extra(); |
| mirror_extra2->mirrored_layer_id = kMirroredLayerId1; |
| update2->layers.push_back(std::move(layer_props2)); |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->mirrored_layer_id(), kMirroredLayerId1); |
| |
| // Third update: Update mirrored_layer_id to kMirroredLayerId2. |
| auto update3 = CreateDefaultUpdate(); |
| auto layer_props3 = |
| CreateManualLayer(kMirrorLayerId, cc::mojom::LayerType::kMirror); |
| auto& mirror_extra3 = layer_props3->layer_extra->get_mirror_layer_extra(); |
| mirror_extra3->mirrored_layer_id = kMirroredLayerId2; |
| update3->layers.push_back(std::move(layer_props3)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| EXPECT_EQ(layer_impl->mirrored_layer_id(), kMirroredLayerId2); |
| } |
| |
| // Test fixture for ViewTransitionContentLayerImpl specific property updates. |
| class LayerContextImplUpdateDisplayTreeViewTransitionContentLayerTest |
| : public LayerContextImplLayerLifecycleTest { |
| protected: |
| cc::ViewTransitionContentLayerImpl* |
| GetViewTransitionContentLayerFromActiveTree(int layer_id) { |
| cc::LayerImpl* layer = GetLayerFromActiveTree(layer_id); |
| if (!layer || |
| layer->GetLayerType() != cc::mojom::LayerType::kViewTransitionContent) { |
| return nullptr; |
| } |
| return static_cast<cc::ViewTransitionContentLayerImpl*>(layer); |
| } |
| }; |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeViewTransitionContentLayerTest, |
| InitialResourceId) { |
| constexpr int kVTContentLayerId = 2; |
| const ViewTransitionElementResourceId kInitialResourceId( |
| blink::ViewTransitionToken(), 3, true); |
| const ViewTransitionElementResourceId kAttemptedUpdateResourceId1( |
| blink::ViewTransitionToken(), 1, false); |
| |
| // Initial update: Create with a specific, non-default resource_id. |
| auto update1 = CreateDefaultUpdate(); |
| auto layer_props1 = CreateManualLayer( |
| kVTContentLayerId, cc::mojom::LayerType::kViewTransitionContent); |
| auto& vt_extra1 = |
| layer_props1->layer_extra->get_view_transition_content_layer_extra(); |
| vt_extra1->resource_id = kInitialResourceId; |
| update1->layers.push_back(std::move(layer_props1)); |
| // AddDefaultLayerToUpdate normally handles this. Since we used |
| // CreateManualLayer, update the layer_order here. |
| layer_order_.push_back(kVTContentLayerId); |
| update1->layer_order = layer_order_; |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::ViewTransitionContentLayerImpl* layer_impl = |
| GetViewTransitionContentLayerFromActiveTree(kVTContentLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->ViewTransitionResourceId(), kInitialResourceId); |
| |
| // Second update: Attempt to update resource_id. This should have no effect |
| // as resource_id is a constructor parameter for the Impl layer. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer( |
| kVTContentLayerId, cc::mojom::LayerType::kViewTransitionContent); |
| // We need to set the resource_id on the update struct, even though it won't |
| // change the impl layer, to reflect what the client might send. |
| auto& vt_extra2 = |
| layer_props2->layer_extra->get_view_transition_content_layer_extra(); |
| vt_extra2->resource_id = kAttemptedUpdateResourceId1; |
| update2->layers.push_back(std::move(layer_props2)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| // Expect the resource_id to remain the initially set one. |
| EXPECT_EQ(layer_impl->ViewTransitionResourceId(), kInitialResourceId); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeViewTransitionContentLayerTest, |
| UpdateIsLiveContentLayer) { |
| constexpr int kVTContentLayerId = 2; |
| constexpr bool kInitialIsLiveContentLayer = true; |
| constexpr bool kAttemptedUpdateIsLiveContentLayer = false; |
| |
| // Initial update: Create with a specific, non-default is_live_content_layer. |
| auto update1 = CreateDefaultUpdate(); |
| auto layer_props1 = CreateManualLayer( |
| kVTContentLayerId, cc::mojom::LayerType::kViewTransitionContent); |
| auto& vt_extra1 = |
| layer_props1->layer_extra->get_view_transition_content_layer_extra(); |
| vt_extra1->is_live_content_layer = kInitialIsLiveContentLayer; |
| update1->layers.push_back(std::move(layer_props1)); |
| // AddDefaultLayerToUpdate normally handles this. Since we used |
| // CreateManualLayer, update the layer_order here. |
| layer_order_.push_back(kVTContentLayerId); |
| update1->layer_order = layer_order_; |
| |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::ViewTransitionContentLayerImpl* layer_impl = |
| GetViewTransitionContentLayerFromActiveTree(kVTContentLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->is_live_content_layer(), kInitialIsLiveContentLayer); |
| |
| // Second update: Attempt to update is_live_content_layer. This should have |
| // no effect as is_live_content_layer is a constructor parameter for the Impl |
| // layer. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer( |
| kVTContentLayerId, cc::mojom::LayerType::kViewTransitionContent); |
| auto& vt_extra2 = |
| layer_props2->layer_extra->get_view_transition_content_layer_extra(); |
| // We need to set the is_live_content_layer on the update struct, even though |
| // it won't change the impl layer, to reflect what the client might send. |
| vt_extra2->is_live_content_layer = kAttemptedUpdateIsLiveContentLayer; |
| update2->layers.push_back(std::move(layer_props2)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| // Expect the is_live_content_layer to remain the initially set one. |
| EXPECT_EQ(layer_impl->is_live_content_layer(), kInitialIsLiveContentLayer); |
| } |
| |
| TEST_F(LayerContextImplUpdateDisplayTreeViewTransitionContentLayerTest, |
| UpdateMaxExtentsRect) { |
| constexpr int kVTContentLayerId = 2; |
| const gfx::RectF kMaxExtentsRect1(10.f, 10.f, 80.f, 80.f); |
| const gfx::RectF kMaxExtentsRect2(5.f, 5.f, 90.f, 90.f); |
| |
| // Initial update: Create with default max_extents_rect. |
| auto update1 = CreateDefaultUpdate(); |
| AddDefaultLayerToUpdate(update1.get(), |
| cc::mojom::LayerType::kViewTransitionContent, |
| kVTContentLayerId); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update1)).has_value()); |
| cc::ViewTransitionContentLayerImpl* layer_impl = |
| GetViewTransitionContentLayerFromActiveTree(kVTContentLayerId); |
| ASSERT_NE(nullptr, layer_impl); |
| EXPECT_EQ(layer_impl->max_extents_rect(), |
| kDefaultViewTransitionContentLayerMaxExtentsRect); |
| |
| // Second update: Update max_extents_rect. |
| auto update2 = CreateDefaultUpdate(); |
| auto layer_props2 = CreateManualLayer( |
| kVTContentLayerId, cc::mojom::LayerType::kViewTransitionContent); |
| auto& vt_extra2 = |
| layer_props2->layer_extra->get_view_transition_content_layer_extra(); |
| vt_extra2->max_extents_rect = kMaxExtentsRect1; |
| update2->layers.push_back(std::move(layer_props2)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update2)).has_value()); |
| EXPECT_EQ(layer_impl->max_extents_rect(), kMaxExtentsRect1); |
| |
| // Third update: Update max_extents_rect again. |
| auto update3 = CreateDefaultUpdate(); |
| auto layer_props3 = CreateManualLayer( |
| kVTContentLayerId, cc::mojom::LayerType::kViewTransitionContent); |
| auto& vt_extra3 = |
| layer_props3->layer_extra->get_view_transition_content_layer_extra(); |
| vt_extra3->max_extents_rect = kMaxExtentsRect2; |
| update3->layers.push_back(std::move(layer_props3)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update3)).has_value()); |
| EXPECT_EQ(layer_impl->max_extents_rect(), kMaxExtentsRect2); |
| |
| // Fourth update: Update max_extents_rect back to default (empty). |
| auto update4 = CreateDefaultUpdate(); |
| auto layer_props4 = CreateManualLayer( |
| kVTContentLayerId, cc::mojom::LayerType::kViewTransitionContent); |
| // Default is already set by CreateDefaultLayerExtra. |
| update4->layers.push_back(std::move(layer_props4)); |
| EXPECT_TRUE( |
| layer_context_impl_->DoUpdateDisplayTree(std::move(update4)).has_value()); |
| EXPECT_EQ(layer_impl->max_extents_rect(), |
| kDefaultViewTransitionContentLayerMaxExtentsRect); |
| } |
| |
| TEST_F(LayerContextImplTest, UpdateDisplayTreeWithTargetLocalSurfaceId) { |
| auto update = CreateDefaultUpdate(); |
| const LocalSurfaceId target_local_surface_id( |
| 1, base::UnguessableToken::CreateForTesting(2, 3)); |
| update->target_local_surface_id = target_local_surface_id; |
| |
| auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update)); |
| EXPECT_TRUE(result.has_value()); |
| EXPECT_EQ(layer_context_impl_->host_impl()->target_local_surface_id(), |
| target_local_surface_id); |
| } |
| |
| } // namespace |
| } // namespace viz |