Commit 498b4b5e authored by Andrew Comminos's avatar Andrew Comminos Committed by Commit Bot

[cc] Associate layers with their owning frame's document element ID

To enable impl-side frame attribution, add an association between layers
and their containing frames. This element ID may be used later to look
up the document via its associated DOM node ID.

Bug: 910421
Change-Id: I5a70d2f42f94d72f76d9c4eb49cd6ee691e1f0f6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2077038Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarStefan Zager <szager@chromium.org>
Commit-Queue: Andrew Comminos <acomminos@fb.com>
Cr-Commit-Position: refs/heads/master@{#749508}
parent 16204ab2
...@@ -217,6 +217,11 @@ class CC_EXPORT InputHandler { ...@@ -217,6 +217,11 @@ class CC_EXPORT InputHandler {
const gfx::PointF& mouse_position) = 0; const gfx::PointF& mouse_position) = 0;
virtual void MouseLeave() = 0; virtual void MouseLeave() = 0;
// Returns frame_element_id from the layer hit by the given point.
// If the hit test failed, an invalid element ID is returned.
virtual ElementId FindFrameElementIdAtPoint(
const gfx::PointF& mouse_position) = 0;
// Requests a callback to UpdateRootLayerStateForSynchronousInputHandler() // Requests a callback to UpdateRootLayerStateForSynchronousInputHandler()
// giving the current root scroll and page scale information. // giving the current root scroll and page scale information.
virtual void RequestUpdateForSynchronousInputHandler() = 0; virtual void RequestUpdateForSynchronousInputHandler() = 0;
......
...@@ -52,6 +52,7 @@ struct SameSizeAsLayer : public base::RefCounted<SameSizeAsLayer> { ...@@ -52,6 +52,7 @@ struct SameSizeAsLayer : public base::RefCounted<SameSizeAsLayer> {
Region non_fast_scrollable_region; Region non_fast_scrollable_region;
TouchActionRegion touch_action_region; TouchActionRegion touch_action_region;
ElementId element_id; ElementId element_id;
ElementId frame_element_id;
} inputs; } inputs;
void* layer_tree_inputs; void* layer_tree_inputs;
int int_fields[6]; int int_fields[6];
...@@ -1369,6 +1370,7 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { ...@@ -1369,6 +1370,7 @@ void Layer::PushPropertiesTo(LayerImpl* layer) {
layer->UnionUpdateRect(inputs_.update_rect); layer->UnionUpdateRect(inputs_.update_rect);
layer->SetHasWillChangeTransformHint(has_will_change_transform_hint()); layer->SetHasWillChangeTransformHint(has_will_change_transform_hint());
layer->SetFrameElementId(inputs_.frame_element_id);
layer->SetNeedsPushProperties(); layer->SetNeedsPushProperties();
// debug_info_->invalidations, if exist, will be cleared in the function. // debug_info_->invalidations, if exist, will be cleared in the function.
...@@ -1478,6 +1480,13 @@ void Layer::SetHasWillChangeTransformHint(bool has_will_change) { ...@@ -1478,6 +1480,13 @@ void Layer::SetHasWillChangeTransformHint(bool has_will_change) {
SetNeedsCommit(); SetNeedsCommit();
} }
void Layer::SetFrameElementId(ElementId frame_element_id) {
if (inputs_.frame_element_id == frame_element_id)
return;
inputs_.frame_element_id = frame_element_id;
SetNeedsCommit();
}
void Layer::SetTrilinearFiltering(bool trilinear_filtering) { void Layer::SetTrilinearFiltering(bool trilinear_filtering) {
auto& inputs = EnsureLayerTreeInputs(); auto& inputs = EnsureLayerTreeInputs();
if (inputs.trilinear_filtering == trilinear_filtering) if (inputs.trilinear_filtering == trilinear_filtering)
......
...@@ -570,6 +570,9 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { ...@@ -570,6 +570,9 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> {
return inputs_.has_will_change_transform_hint; return inputs_.has_will_change_transform_hint;
} }
void SetFrameElementId(ElementId frame_element_id);
ElementId frame_element_id() const { return inputs_.frame_element_id; }
// For layer tree mode only. // For layer tree mode only.
// Sets or gets if trilinear filtering should be used to scaling the contents // Sets or gets if trilinear filtering should be used to scaling the contents
// of this layer and its subtree. When set the layer and its subtree will be // of this layer and its subtree. When set the layer and its subtree will be
...@@ -870,6 +873,8 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> { ...@@ -870,6 +873,8 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> {
TouchActionRegion touch_action_region; TouchActionRegion touch_action_region;
ElementId element_id; ElementId element_id;
// ElementId of the document that this layer was created by.
ElementId frame_element_id;
}; };
// These inputs are used in layer tree mode (ui compositor) only. Most of them // These inputs are used in layer tree mode (ui compositor) only. Most of them
......
...@@ -365,6 +365,7 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) { ...@@ -365,6 +365,7 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) {
// depend on it. Referencing element id on a layer is // depend on it. Referencing element id on a layer is
// deprecated. http://crbug.com/709137 // deprecated. http://crbug.com/709137
layer->SetElementId(element_id_); layer->SetElementId(element_id_);
layer->SetFrameElementId(frame_element_id_);
layer->has_transform_node_ = has_transform_node_; layer->has_transform_node_ = has_transform_node_;
layer->offset_to_transform_parent_ = offset_to_transform_parent_; layer->offset_to_transform_parent_ = offset_to_transform_parent_;
......
...@@ -175,6 +175,11 @@ class CC_EXPORT LayerImpl { ...@@ -175,6 +175,11 @@ class CC_EXPORT LayerImpl {
void SetElementId(ElementId element_id); void SetElementId(ElementId element_id);
ElementId element_id() const { return element_id_; } ElementId element_id() const { return element_id_; }
void SetFrameElementId(ElementId frame_element_id) {
frame_element_id_ = frame_element_id;
}
ElementId frame_element_id() const { return frame_element_id_; }
bool IsAffectedByPageScale() const; bool IsAffectedByPageScale() const;
bool Is3dSorted() const { return GetSortingContextId() != 0; } bool Is3dSorted() const { return GetSortingContextId() != 0; }
...@@ -518,6 +523,8 @@ class CC_EXPORT LayerImpl { ...@@ -518,6 +523,8 @@ class CC_EXPORT LayerImpl {
TransformTree& GetTransformTree() const; TransformTree& GetTransformTree() const;
ElementId element_id_; ElementId element_id_;
// Element ID of the document containing this layer.
ElementId frame_element_id_;
// Rect indicating what was repainted/updated during update. // Rect indicating what was repainted/updated during update.
// Note that plugin layers bypass this and leave it empty. // Note that plugin layers bypass this and leave it empty.
// This is in the layer's space. // This is in the layer's space.
......
...@@ -5102,6 +5102,13 @@ void LayerTreeHostImpl::MouseLeave() { ...@@ -5102,6 +5102,13 @@ void LayerTreeHostImpl::MouseLeave() {
scroll_element_id_mouse_currently_over_ = ElementId(); scroll_element_id_mouse_currently_over_ = ElementId();
} }
ElementId LayerTreeHostImpl::FindFrameElementIdAtPoint(
const gfx::PointF& viewport_point) {
gfx::PointF device_viewport_point = gfx::ScalePoint(
gfx::PointF(viewport_point), active_tree_->device_scale_factor());
return active_tree_->FindFrameElementIdAtPoint(device_viewport_point);
}
void LayerTreeHostImpl::PinchGestureBegin() { void LayerTreeHostImpl::PinchGestureBegin() {
pinch_gesture_active_ = true; pinch_gesture_active_ = true;
client_->RenewTreePriority(); client_->RenewTreePriority();
......
...@@ -283,6 +283,11 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler, ...@@ -283,6 +283,11 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
const gfx::Point& viewport_point) override; const gfx::Point& viewport_point) override;
void MouseLeave() override; void MouseLeave() override;
// Returns frame_element_id from the layer hit by the given point.
// If the hit test failed, an invalid element ID is returned.
ElementId FindFrameElementIdAtPoint(
const gfx::PointF& viewport_point) override;
void PinchGestureBegin() override; void PinchGestureBegin() override;
void PinchGestureUpdate(float magnify_delta, void PinchGestureUpdate(float magnify_delta,
const gfx::Point& anchor) override; const gfx::Point& anchor) override;
......
...@@ -2247,6 +2247,37 @@ LayerTreeImpl::FindLayersHitByPointInNonFastScrollableRegion( ...@@ -2247,6 +2247,37 @@ LayerTreeImpl::FindLayersHitByPointInNonFastScrollableRegion(
return layers; return layers;
} }
struct HitTestFramedVisibleScrollableOrTouchableFunctor {
bool operator()(LayerImpl* layer) const {
return layer->HitTestable() && layer->frame_element_id();
}
};
ElementId LayerTreeImpl::FindFrameElementIdAtPoint(
const gfx::PointF& screen_space_point) {
if (layer_list_.empty())
return {};
if (!UpdateDrawProperties())
return {};
FindClosestMatchingLayerState state;
FindClosestMatchingLayer(screen_space_point, layer_list_[0].get(),
HitTestFramedVisibleScrollableOrTouchableFunctor(),
&state);
if (auto* layer = state.closest_match) {
// TODO(https://crbug.com/1058870): Permit hit testing only if the framed
// element hit has a simple mask/clip. We don't have enough information
// about complex masks/clips on the impl-side to do accurate hit testing.
bool layer_hit_test_region_is_masked =
property_trees()->effect_tree.HitTestMayBeAffectedByMask(
layer->effect_tree_index());
if (!layer_hit_test_region_is_masked)
return layer->frame_element_id();
}
return {};
}
void LayerTreeImpl::RegisterSelection(const LayerSelection& selection) { void LayerTreeImpl::RegisterSelection(const LayerSelection& selection) {
if (selection_ == selection) if (selection_ == selection)
return; return;
......
...@@ -590,6 +590,12 @@ class CC_EXPORT LayerTreeImpl { ...@@ -590,6 +590,12 @@ class CC_EXPORT LayerTreeImpl {
std::vector<const LayerImpl*> FindLayersHitByPointInNonFastScrollableRegion( std::vector<const LayerImpl*> FindLayersHitByPointInNonFastScrollableRegion(
const gfx::PointF& screen_space_point); const gfx::PointF& screen_space_point);
// Returns the ElementId representing a frame's document at the given point.
// In cases where cc doesn't have enough information to perform accurate
// attribution (e.g. in the presence of a complex clip), kInvalidElementId is
// returned.
ElementId FindFrameElementIdAtPoint(const gfx::PointF& screen_space_point);
void RegisterSelection(const LayerSelection& selection); void RegisterSelection(const LayerSelection& selection);
bool HandleVisibilityChanged() const { return handle_visibility_changed_; } bool HandleVisibilityChanged() const { return handle_visibility_changed_; }
......
...@@ -2549,6 +2549,91 @@ TEST_F(LayerTreeImplTest, ElementIdToAnimationMapsTrackOnlyOnSyncTree) { ...@@ -2549,6 +2549,91 @@ TEST_F(LayerTreeImplTest, ElementIdToAnimationMapsTrackOnlyOnSyncTree) {
EXPECT_EQ(filter_map.size(), 1u); EXPECT_EQ(filter_map.size(), 1u);
} }
TEST_F(LayerTreeImplTest, FrameElementIdHitTestSimple) {
LayerImpl* frame_layer = AddLayer<LayerImpl>();
frame_layer->SetBounds(gfx::Size(50, 50));
frame_layer->SetDrawsContent(true);
frame_layer->SetHitTestable(true);
frame_layer->SetFrameElementId(ElementId(0x10));
CopyProperties(root_layer(), frame_layer);
UpdateDrawProperties(host_impl().active_tree());
EXPECT_EQ(host_impl().FindFrameElementIdAtPoint(gfx::PointF(10, 10)),
ElementId(0x10));
}
TEST_F(LayerTreeImplTest, FrameElementIdHitTestOverlap) {
LayerImpl* frame_layer = AddLayer<LayerImpl>();
frame_layer->SetBounds(gfx::Size(50, 50));
frame_layer->SetHitTestable(true);
frame_layer->SetFrameElementId(ElementId(0x10));
CopyProperties(root_layer(), frame_layer);
LayerImpl* occluding_frame_layer = AddLayer<LayerImpl>();
occluding_frame_layer->SetBounds(gfx::Size(50, 50));
occluding_frame_layer->SetHitTestable(true);
occluding_frame_layer->SetFrameElementId(ElementId(0x20));
CopyProperties(root_layer(), occluding_frame_layer);
occluding_frame_layer->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
UpdateDrawProperties(host_impl().active_tree());
EXPECT_EQ(host_impl().FindFrameElementIdAtPoint(gfx::PointF(30, 30)),
ElementId(0x20));
}
TEST_F(LayerTreeImplTest, FrameElementIdHitTestOverlapSimpleClip) {
LayerImpl* frame_layer = AddLayer<LayerImpl>();
frame_layer->SetBounds(gfx::Size(50, 50));
frame_layer->SetHitTestable(true);
frame_layer->SetFrameElementId(ElementId(0x10));
CopyProperties(root_layer(), frame_layer);
LayerImpl* clipped_frame_layer = AddLayer<LayerImpl>();
clipped_frame_layer->SetBounds(gfx::Size(50, 50));
clipped_frame_layer->SetHitTestable(true);
clipped_frame_layer->SetFrameElementId(ElementId(0x20));
CopyProperties(root_layer(), clipped_frame_layer);
clipped_frame_layer->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
// Create a clip excluding the overlapped region.
auto& clip_node = CreateClipNode(clipped_frame_layer);
clip_node.clip = gfx::RectF(40, 40, 10, 10);
UpdateDrawProperties(host_impl().active_tree());
// Ensure that the overlapping (clipped) layer isn't targeted.
EXPECT_EQ(host_impl().FindFrameElementIdAtPoint(gfx::PointF(30, 30)),
ElementId(0x10));
}
TEST_F(LayerTreeImplTest, FrameElementIdHitTestOverlapRoundedCorners) {
LayerImpl* frame_layer = AddLayer<LayerImpl>();
frame_layer->SetBounds(gfx::Size(50, 50));
frame_layer->SetHitTestable(true);
frame_layer->SetFrameElementId(ElementId(0x10));
CopyProperties(root_layer(), frame_layer);
LayerImpl* rounded_frame_layer = AddLayer<LayerImpl>();
rounded_frame_layer->SetBounds(gfx::Size(50, 50));
rounded_frame_layer->SetHitTestable(true);
rounded_frame_layer->SetFrameElementId(ElementId(0x20));
CopyProperties(root_layer(), rounded_frame_layer);
rounded_frame_layer->SetOffsetToTransformParent(gfx::Vector2dF(25, 25));
// Add rounded corners to the layer, which are unable to be hit tested by the
// simple quad-based logic.
CreateEffectNode(rounded_frame_layer).rounded_corner_bounds =
gfx::RRectF(25, 25, 50, 50, 5);
UpdateDrawProperties(host_impl().active_tree());
// The lookup should bail out in the presence of a complex clip/mask on the
// target chain.
EXPECT_FALSE(host_impl().FindFrameElementIdAtPoint(gfx::PointF(30, 30)));
}
class LayerTreeImplOcclusionSettings : public LayerListSettings { class LayerTreeImplOcclusionSettings : public LayerListSettings {
public: public:
explicit LayerTreeImplOcclusionSettings(bool enabled) { explicit LayerTreeImplOcclusionSettings(bool enabled) {
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include "third_party/blink/renderer/core/html/media/html_video_element.h" #include "third_party/blink/renderer/core/html/media/html_video_element.h"
#include "third_party/blink/renderer/core/html_names.h" #include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/layout/geometry/transform_state.h" #include "third_party/blink/renderer/core/layout/geometry/transform_state.h"
#include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
#include "third_party/blink/renderer/core/layout/layout_embedded_content.h" #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
#include "third_party/blink/renderer/core/layout/layout_embedded_object.h" #include "third_party/blink/renderer/core/layout/layout_embedded_object.h"
#include "third_party/blink/renderer/core/layout/layout_html_canvas.h" #include "third_party/blink/renderer/core/layout/layout_html_canvas.h"
...@@ -228,6 +229,26 @@ std::unique_ptr<GraphicsLayer> CompositedLayerMapping::CreateGraphicsLayer( ...@@ -228,6 +229,26 @@ std::unique_ptr<GraphicsLayer> CompositedLayerMapping::CreateGraphicsLayer(
static_cast<int>(DOMNodeIds::IdForNode(owning_node))); static_cast<int>(DOMNodeIds::IdForNode(owning_node)));
} }
// Attempt to associate each layer with the frame owner's element ID.
Document* owner = nullptr;
if (GetLayoutObject().IsLayoutEmbeddedContent()) {
auto& embedded = ToLayoutEmbeddedContent(GetLayoutObject());
if (auto* frame_view =
DynamicTo<LocalFrameView>(embedded.GetEmbeddedContentView())) {
owner = frame_view->GetFrame().GetDocument();
} else {
// Ignore remote and plugin frames.
}
} else {
owner = &GetLayoutObject().GetDocument();
}
if (owner) {
graphics_layer->CcLayer()->SetFrameElementId(
CompositorElementIdFromUniqueObjectId(
DOMNodeIds::IdForNode(owner),
CompositorElementIdNamespace::kDOMNodeId));
}
return graphics_layer; return graphics_layer;
} }
......
...@@ -10,7 +10,9 @@ ...@@ -10,7 +10,9 @@
#include "cc/trees/property_tree.h" #include "cc/trees/property_tree.h"
#include "cc/trees/scroll_node.h" #include "cc/trees/scroll_node.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/dom/dom_node_ids.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
#include "third_party/blink/renderer/core/layout/layout_box_model_object.h" #include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
#include "third_party/blink/renderer/core/layout/layout_image.h" #include "third_party/blink/renderer/core/layout/layout_image.h"
#include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/layout/layout_view.h"
...@@ -1827,4 +1829,42 @@ TEST_F(CompositedLayerMappingTest, IsolationClippingContainer) { ...@@ -1827,4 +1829,42 @@ TEST_F(CompositedLayerMappingTest, IsolationClippingContainer) {
EXPECT_EQ(squash_container_a_layer->ClippingContainer(), isolation_a_object); EXPECT_EQ(squash_container_a_layer->ClippingContainer(), isolation_a_object);
} }
TEST_F(CompositedLayerMappingTest, FrameAttribution) {
SetBodyInnerHTML(R"HTML(
<div id='child' style='will-change: transform;'></div>
<iframe id='subframe' style='will-change: transform;'></iframe>
)HTML");
UpdateAllLifecyclePhasesForTest();
// Ensure that we correctly attribute child layers in the main frame to their
// containing document.
Element* child = GetDocument().getElementById("child");
PaintLayer* child_paint_layer =
ToLayoutBoxModelObject(child->GetLayoutObject())->Layer();
auto* child_layer = child_paint_layer->GraphicsLayerBacking()->CcLayer();
EXPECT_TRUE(child_layer->frame_element_id());
EXPECT_EQ(child_layer->frame_element_id(),
CompositorElementIdFromUniqueObjectId(
DOMNodeIds::IdForNode(&GetDocument()),
CompositorElementIdNamespace::kDOMNodeId));
// Test that a layerized subframe's element ID is that of its containing
// document.
auto* subframe =
To<HTMLFrameOwnerElement>(GetDocument().getElementById("subframe"));
EXPECT_TRUE(subframe);
PaintLayer* subframe_paint_layer =
ToLayoutBoxModelObject(subframe->GetLayoutObject())->Layer();
auto* subframe_layer =
subframe_paint_layer->GraphicsLayerBacking()->CcLayer();
EXPECT_TRUE(subframe_layer->frame_element_id());
EXPECT_EQ(subframe_layer->frame_element_id(),
CompositorElementIdFromUniqueObjectId(
DOMNodeIds::IdForNode(subframe->contentDocument()),
CompositorElementIdNamespace::kDOMNodeId));
}
} // namespace blink } // namespace blink
...@@ -25,8 +25,9 @@ enum class CompositorElementIdNamespace { ...@@ -25,8 +25,9 @@ enum class CompositorElementIdNamespace {
kEffectClipPath, kEffectClipPath,
kVerticalScrollbar, kVerticalScrollbar,
kHorizontalScrollbar, kHorizontalScrollbar,
kDOMNodeId,
// The following values are for internal usage only. // The following values are for internal usage only.
kMax = kHorizontalScrollbar, kMax = kDOMNodeId,
// A sentinel to indicate the maximum representable namespace id // A sentinel to indicate the maximum representable namespace id
// (the maximum is one less than this value). // (the maximum is one less than this value).
kMaxRepresentable = 1 << kCompositorNamespaceBitCount kMaxRepresentable = 1 << kCompositorNamespaceBitCount
......
...@@ -165,6 +165,11 @@ class MockInputHandler : public cc::InputHandler { ...@@ -165,6 +165,11 @@ class MockInputHandler : public cc::InputHandler {
void MouseLeave() override {} void MouseLeave() override {}
cc::ElementId FindFrameElementIdAtPoint(
const gfx::PointF& mouse_position) override {
return cc::ElementId();
}
cc::InputHandlerPointerResult MouseMoveAt( cc::InputHandlerPointerResult MouseMoveAt(
const gfx::Point& mouse_position) override { const gfx::Point& mouse_position) override {
return cc::InputHandlerPointerResult(); return cc::InputHandlerPointerResult();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment