Skip to content

Commit

Permalink
Compute and include the offset of the sticky box to its enclosing com…
Browse files Browse the repository at this point in the history
…posited layer.

When the main thread moves a sticky object we try to determine how far it was moved
in cc/ to know how much more we still have to move it. However, when the sticky
object is in a composited layer we need to know the expected offset from that layer
rather than from the scroll ancestor.

BUG=649102
TEST=LayerTreeHostCommonTest.StickyPositionCompositedContainer, ScrollingCoordinatorTest.fastScrollingForStickyPosition
CQ_INCLUDE_TRYBOTS=master.tryserver.blink:linux_precise_blink_rel

Review-Url: https://codereview.chromium.org/2401903002
Cr-Commit-Position: refs/heads/master@{#424451}
  • Loading branch information
flackr authored and Commit bot committed Oct 11, 2016
1 parent 13741f1 commit 68e1c29
Show file tree
Hide file tree
Showing 11 changed files with 317 additions and 2 deletions.
4 changes: 4 additions & 0 deletions cc/blink/web_layer_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,8 @@ ToWebLayerStickyPositionConstraint(
web_constraint.rightOffset = constraint.right_offset;
web_constraint.topOffset = constraint.top_offset;
web_constraint.bottomOffset = constraint.bottom_offset;
web_constraint.parentRelativeStickyBoxOffset =
constraint.parent_relative_sticky_box_offset;
web_constraint.scrollContainerRelativeStickyBoxRect =
constraint.scroll_container_relative_sticky_box_rect;
web_constraint.scrollContainerRelativeContainingBlockRect =
Expand All @@ -390,6 +392,8 @@ static cc::LayerStickyPositionConstraint ToStickyPositionConstraint(
constraint.right_offset = web_constraint.rightOffset;
constraint.top_offset = web_constraint.topOffset;
constraint.bottom_offset = web_constraint.bottomOffset;
constraint.parent_relative_sticky_box_offset =
web_constraint.parentRelativeStickyBoxOffset;
constraint.scroll_container_relative_sticky_box_rect =
web_constraint.scrollContainerRelativeStickyBoxRect;
constraint.scroll_container_relative_containing_block_rect =
Expand Down
2 changes: 1 addition & 1 deletion cc/layers/layer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,7 @@ void Layer::SetPosition(const gfx::PointF& position) {
transform_tree_index());
sticky_data->main_thread_offset =
position.OffsetFromOrigin() -
sticky_data->constraints.scroll_container_relative_sticky_box_rect
sticky_data->constraints.parent_relative_sticky_box_offset
.OffsetFromOrigin();
}
transform_node->needs_local_transform_update = true;
Expand Down
8 changes: 8 additions & 0 deletions cc/layers/layer_sticky_position_constraint.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ LayerStickyPositionConstraint::LayerStickyPositionConstraint(
right_offset(other.right_offset),
top_offset(other.top_offset),
bottom_offset(other.bottom_offset),
parent_relative_sticky_box_offset(
other.parent_relative_sticky_box_offset),
scroll_container_relative_sticky_box_rect(
other.scroll_container_relative_sticky_box_rect),
scroll_container_relative_containing_block_rect(
Expand All @@ -46,6 +48,8 @@ void LayerStickyPositionConstraint::ToProtobuf(
proto->set_right_offset(right_offset);
proto->set_top_offset(top_offset);
proto->set_bottom_offset(bottom_offset);
PointToProto(parent_relative_sticky_box_offset,
proto->mutable_parent_relative_sticky_box_offset());
RectToProto(scroll_container_relative_sticky_box_rect,
proto->mutable_scroll_container_relative_sticky_box_rect());
RectToProto(scroll_container_relative_containing_block_rect,
Expand All @@ -63,6 +67,8 @@ void LayerStickyPositionConstraint::FromProtobuf(
right_offset = proto.right_offset();
top_offset = proto.top_offset();
bottom_offset = proto.bottom_offset();
parent_relative_sticky_box_offset =
ProtoToPoint(proto.parent_relative_sticky_box_offset());
scroll_container_relative_sticky_box_rect =
ProtoToRect(proto.scroll_container_relative_sticky_box_rect());
scroll_container_relative_containing_block_rect =
Expand All @@ -81,6 +87,8 @@ bool LayerStickyPositionConstraint::operator==(
left_offset == other.left_offset &&
right_offset == other.right_offset && top_offset == other.top_offset &&
bottom_offset == other.bottom_offset &&
parent_relative_sticky_box_offset ==
other.parent_relative_sticky_box_offset &&
scroll_container_relative_sticky_box_rect ==
other.scroll_container_relative_sticky_box_rect &&
scroll_container_relative_containing_block_rect ==
Expand Down
5 changes: 5 additions & 0 deletions cc/layers/layer_sticky_position_constraint.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ struct CC_EXPORT LayerStickyPositionConstraint {
float top_offset;
float bottom_offset;

// The layout offset of the sticky box relative to its containing layer.
// This is used to detect the sticky offset the main thread has applied
// to the layer.
gfx::Point parent_relative_sticky_box_offset;

// The rectangle corresponding to original layout position of the sticky box
// relative to the scroll ancestor. The sticky box is only offset once the
// scroll has passed its initial position (e.g. top_offset will only push
Expand Down
2 changes: 2 additions & 0 deletions cc/proto/layer_sticky_position_constraint.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

syntax = "proto2";

import "point.proto";
import "rect.proto";
import "transform.proto";

Expand All @@ -23,6 +24,7 @@ message LayerStickyPositionConstraint {
optional float top_offset = 8;
optional float bottom_offset = 9;

optional Point parent_relative_sticky_box_offset = 12;
optional Rect scroll_container_relative_sticky_box_rect = 10;
optional Rect scroll_container_relative_containing_block_rect = 11;
};
110 changes: 110 additions & 0 deletions cc/trees/layer_tree_host_common_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6793,6 +6793,7 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionTop) {
sticky_position.is_sticky = true;
sticky_position.is_anchored_top = true;
sticky_position.top_offset = 10.0f;
sticky_position.parent_relative_sticky_box_offset = gfx::Point(10, 20);
sticky_position.scroll_container_relative_sticky_box_rect =
gfx::Rect(10, 20, 10, 10);
sticky_position.scroll_container_relative_containing_block_rect =
Expand Down Expand Up @@ -6864,6 +6865,7 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionBottom) {
sticky_position.is_sticky = true;
sticky_position.is_anchored_bottom = true;
sticky_position.bottom_offset = 10.0f;
sticky_position.parent_relative_sticky_box_offset = gfx::Point(0, 150);
sticky_position.scroll_container_relative_sticky_box_rect =
gfx::Rect(0, 150, 10, 10);
sticky_position.scroll_container_relative_containing_block_rect =
Expand Down Expand Up @@ -6935,6 +6937,7 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionLeftRight) {
sticky_position.is_anchored_right = true;
sticky_position.left_offset = 10.f;
sticky_position.right_offset = 10.f;
sticky_position.parent_relative_sticky_box_offset = gfx::Point(145, 0);
sticky_position.scroll_container_relative_sticky_box_rect =
gfx::Rect(145, 0, 10, 10);
sticky_position.scroll_container_relative_containing_block_rect =
Expand Down Expand Up @@ -7037,6 +7040,7 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionMainThreadUpdates) {
sticky_position.is_sticky = true;
sticky_position.is_anchored_top = true;
sticky_position.top_offset = 10.0f;
sticky_position.parent_relative_sticky_box_offset = gfx::Point(10, 20);
sticky_position.scroll_container_relative_sticky_box_rect =
gfx::Rect(10, 20, 10, 10);
sticky_position.scroll_container_relative_containing_block_rect =
Expand Down Expand Up @@ -7108,6 +7112,110 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionMainThreadUpdates) {
sticky_pos_impl->ScreenSpaceTransform().To2dTranslation());
}

// This tests the main thread updates with a composited sticky container. In
// this case the position received from main is relative to the container but
// the constraint rects are relative to the ancestor scroller.
TEST_F(LayerTreeHostCommonTest, StickyPositionCompositedContainer) {
scoped_refptr<Layer> root = Layer::Create();
scoped_refptr<Layer> container = Layer::Create();
scoped_refptr<Layer> scroller = Layer::Create();
scoped_refptr<Layer> sticky_container = Layer::Create();
scoped_refptr<Layer> sticky_pos = Layer::Create();
root->AddChild(container);
container->AddChild(scroller);
scroller->AddChild(sticky_container);
sticky_container->AddChild(sticky_pos);
host()->SetRootLayer(root);
scroller->SetScrollClipLayerId(container->id());

LayerStickyPositionConstraint sticky_position;
sticky_position.is_sticky = true;
sticky_position.is_anchored_top = true;
sticky_position.top_offset = 10.0f;
// The sticky position layer is only offset by (0, 10) from its parent
// layer, this position is used to determine the offset applied by the main
// thread.
sticky_position.parent_relative_sticky_box_offset = gfx::Point(0, 10);
sticky_position.scroll_container_relative_sticky_box_rect =
gfx::Rect(20, 30, 10, 10);
sticky_position.scroll_container_relative_containing_block_rect =
gfx::Rect(20, 20, 30, 30);
sticky_pos->SetStickyPositionConstraint(sticky_position);

root->SetBounds(gfx::Size(100, 100));
container->SetBounds(gfx::Size(100, 100));
scroller->SetBounds(gfx::Size(1000, 1000));
sticky_container->SetPosition(gfx::PointF(20, 20));
sticky_container->SetBounds(gfx::Size(30, 30));
sticky_pos->SetBounds(gfx::Size(10, 10));
sticky_pos->SetPosition(gfx::PointF(0, 10));

ExecuteCalculateDrawProperties(root.get());
host()->host_impl()->CreatePendingTree();
host()->CommitAndCreatePendingTree();
host()->host_impl()->ActivateSyncTree();
LayerTreeImpl* layer_tree_impl = host()->host_impl()->active_tree();

LayerImpl* root_impl = layer_tree_impl->LayerById(root->id());
LayerImpl* scroller_impl = layer_tree_impl->LayerById(scroller->id());
LayerImpl* sticky_pos_impl = layer_tree_impl->LayerById(sticky_pos->id());

ExecuteCalculateDrawProperties(root_impl);
EXPECT_VECTOR2DF_EQ(
gfx::Vector2dF(20.f, 30.f),
sticky_pos_impl->ScreenSpaceTransform().To2dTranslation());

// Scroll less than sticking point, sticky element should move with scroll as
// we haven't gotten to the initial sticky item location yet.
SetScrollOffsetDelta(scroller_impl, gfx::Vector2dF(0.f, 5.f));
ExecuteCalculateDrawProperties(root_impl);
EXPECT_VECTOR2DF_EQ(
gfx::Vector2dF(20.f, 25.f),
sticky_pos_impl->ScreenSpaceTransform().To2dTranslation());

// Scroll past the sticking point, the Y coordinate should now be clamped.
SetScrollOffsetDelta(scroller_impl, gfx::Vector2dF(0.f, 25.f));
ExecuteCalculateDrawProperties(root_impl);
EXPECT_VECTOR2DF_EQ(
gfx::Vector2dF(20.f, 10.f),
sticky_pos_impl->ScreenSpaceTransform().To2dTranslation());

// Now the main thread commits the new position of the sticky element.
scroller->SetScrollOffset(gfx::ScrollOffset(0, 25));
sticky_pos->SetPosition(gfx::PointF(0, 15));
ExecuteCalculateDrawProperties(root.get());
host()->host_impl()->CreatePendingTree();
host()->CommitAndCreatePendingTree();
host()->host_impl()->ActivateSyncTree();
layer_tree_impl = host()->host_impl()->active_tree();
root_impl = layer_tree_impl->LayerById(root->id());
scroller_impl = layer_tree_impl->LayerById(scroller->id());
sticky_pos_impl = layer_tree_impl->LayerById(sticky_pos->id());

// The element should still be where it was before. We reset the delta to
// (0, 0) because we have synced a scroll offset of (0, 25) from the main
// thread.
SetScrollOffsetDelta(scroller_impl, gfx::Vector2dF(0.f, 0.f));
ExecuteCalculateDrawProperties(root_impl);
EXPECT_VECTOR2DF_EQ(
gfx::Vector2dF(20.f, 10.f),
sticky_pos_impl->ScreenSpaceTransform().To2dTranslation());

// And if we scroll a little further it remains there.
SetScrollOffsetDelta(scroller_impl, gfx::Vector2dF(0.f, 5.f));
ExecuteCalculateDrawProperties(root_impl);
EXPECT_VECTOR2DF_EQ(
gfx::Vector2dF(20.f, 10.f),
sticky_pos_impl->ScreenSpaceTransform().To2dTranslation());

// And hits the bottom of the container.
SetScrollOffsetDelta(scroller_impl, gfx::Vector2dF(0.f, 10.f));
ExecuteCalculateDrawProperties(root_impl);
EXPECT_VECTOR2DF_EQ(
gfx::Vector2dF(20.f, 5.f),
sticky_pos_impl->ScreenSpaceTransform().To2dTranslation());
}

// A transform on a sticky element should not affect its sticky position.
TEST_F(LayerTreeHostCommonTest, StickyPositionScaledStickyBox) {
scoped_refptr<Layer> root = Layer::Create();
Expand All @@ -7127,6 +7235,7 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionScaledStickyBox) {
sticky_position.is_sticky = true;
sticky_position.is_anchored_top = true;
sticky_position.top_offset = 0.0f;
sticky_position.parent_relative_sticky_box_offset = gfx::Point(0, 20);
sticky_position.scroll_container_relative_sticky_box_rect =
gfx::Rect(0, 20, 10, 10);
sticky_position.scroll_container_relative_containing_block_rect =
Expand Down Expand Up @@ -7205,6 +7314,7 @@ TEST_F(LayerTreeHostCommonTest, StickyPositionScaledContainer) {
sticky_position.is_sticky = true;
sticky_position.is_anchored_top = true;
sticky_position.top_offset = 0.0f;
sticky_position.parent_relative_sticky_box_offset = gfx::Point(0, 20);
sticky_position.scroll_container_relative_sticky_box_rect =
gfx::Rect(0, 20, 10, 10);
sticky_position.scroll_container_relative_containing_block_rect =
Expand Down
2 changes: 1 addition & 1 deletion cc/trees/property_tree_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@ bool AddTransformNodeIfNeeded(
sticky_data->scroll_ancestor = GetScrollParentId(data_from_ancestor, layer);
sticky_data->main_thread_offset =
layer->position().OffsetFromOrigin() -
sticky_data->constraints.scroll_container_relative_sticky_box_rect
sticky_data->constraints.parent_relative_sticky_box_offset
.OffsetFromOrigin();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,17 @@ void CompositedLayerMapping::updateStickyConstraints(
const StickyPositionScrollingConstraints& constraints =
ancestorOverflowLayer->getScrollableArea()->stickyConstraintsMap().get(
&m_owningLayer);

// Find the layout offset of the unshifted sticky box within its enclosing
// layer.
LayoutPoint enclosingLayerOffset;
m_owningLayer.enclosingLayerWithCompositedLayerMapping(ExcludeSelf)
->convertToLayerCoords(m_owningLayer.ancestorOverflowLayer(),
enclosingLayerOffset);
FloatPoint stickyBoxOffset =
constraints.scrollContainerRelativeStickyBoxRect().location();
stickyBoxOffset.moveBy(FloatPoint(-enclosingLayerOffset));

webConstraint.isSticky = true;
webConstraint.isAnchoredLeft =
constraints.anchorEdges() &
Expand All @@ -321,6 +332,8 @@ void CompositedLayerMapping::updateStickyConstraints(
webConstraint.rightOffset = constraints.rightOffset();
webConstraint.topOffset = constraints.topOffset();
webConstraint.bottomOffset = constraints.bottomOffset();
webConstraint.parentRelativeStickyBoxOffset =
roundedIntPoint(stickyBoxOffset);
webConstraint.scrollContainerRelativeStickyBoxRect =
enclosingIntRect(constraints.scrollContainerRelativeStickyBoxRect());
webConstraint.scrollContainerRelativeContainingBlockRect = enclosingIntRect(
Expand Down
Loading

0 comments on commit 68e1c29

Please sign in to comment.