summaryrefslogtreecommitdiffstats
path: root/cc/trees
diff options
context:
space:
mode:
Diffstat (limited to 'cc/trees')
-rw-r--r--cc/trees/layer_tree_host.cc4
-rw-r--r--cc/trees/layer_tree_host_common_unittest.cc9
-rw-r--r--cc/trees/layer_tree_host_impl.cc13
-rw-r--r--cc/trees/layer_tree_host_impl_unittest.cc27
-rw-r--r--cc/trees/layer_tree_impl.cc6
-rw-r--r--cc/trees/layer_tree_impl.h8
-rw-r--r--cc/trees/property_tree.cc197
-rw-r--r--cc/trees/property_tree.h31
-rw-r--r--cc/trees/property_tree_builder.cc9
-rw-r--r--cc/trees/property_tree_unittest.cc7
-rw-r--r--cc/trees/tree_synchronizer_unittest.cc115
11 files changed, 399 insertions, 27 deletions
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 268e93b..d5f9b58 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -495,6 +495,10 @@ void LayerTreeHost::FinishCommitOnImplThread(LayerTreeHostImpl* host_impl) {
// properties, which updates property tree indices.
sync_tree->UpdatePropertyTreeScrollingAndAnimationFromMainThread();
+ // This must happen after synchronizing property trees and after pushing
+ // properties, which updates the clobber_active_value flag.
+ sync_tree->UpdatePropertyTreeScrollOffset(&property_trees_);
+
micro_benchmark_controller_.ScheduleImplBenchmarks(host_impl);
// We don't track changes to effect tree on main thread. But, to preserve any
// change tracking done on active tree's effect tree, we copy it to the main
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc
index fae38ae..d2c1f55 100644
--- a/cc/trees/layer_tree_host_common_unittest.cc
+++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -10056,7 +10056,8 @@ TEST_F(LayerTreeHostCommonTest, ScrollTreeBuilderTest) {
// Property tree root
ScrollTree scroll_tree = host()->property_trees()->scroll_tree;
- ScrollTree expected_scroll_tree;
+ PropertyTrees property_trees;
+ ScrollTree expected_scroll_tree = property_trees.scroll_tree;
ScrollNode* property_tree_root = expected_scroll_tree.Node(0);
property_tree_root->id = kRootPropertyTreeNodeId;
property_tree_root->parent_id = kInvalidPropertyTreeNodeId;
@@ -10139,6 +10140,12 @@ TEST_F(LayerTreeHostCommonTest, ScrollTreeBuilderTest) {
scroll_parent5.data.transform_id = parent5->transform_tree_index();
expected_scroll_tree.Insert(scroll_parent5, 1);
+ expected_scroll_tree.synced_scroll_offset(parent2->id())
+ ->PushFromMainThread(gfx::ScrollOffset(0, 0));
+ expected_scroll_tree.synced_scroll_offset(child7->id())
+ ->PushFromMainThread(gfx::ScrollOffset(0, 0));
+ expected_scroll_tree.synced_scroll_offset(grand_child11->id())
+ ->PushFromMainThread(gfx::ScrollOffset(0, 0));
expected_scroll_tree.set_needs_update(false);
EXPECT_EQ(expected_scroll_tree, scroll_tree);
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index dfa3c3f..c7bcf2c 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -3249,16 +3249,9 @@ static void CollectScrollDeltas(ScrollAndScaleSet* scroll_info,
if (!root_layer)
return;
- for (auto* layer : *root_layer->layer_tree_impl()) {
- gfx::ScrollOffset scroll_delta = layer->PullDeltaForMainThread();
-
- if (!scroll_delta.IsZero()) {
- LayerTreeHostCommon::ScrollUpdateInfo scroll;
- scroll.layer_id = layer->id();
- scroll.scroll_delta = gfx::Vector2d(scroll_delta.x(), scroll_delta.y());
- scroll_info->scrolls.push_back(scroll);
- }
- }
+ return root_layer->layer_tree_impl()
+ ->property_trees()
+ ->scroll_tree.CollectScrollDeltas(scroll_info);
}
scoped_ptr<ScrollAndScaleSet> LayerTreeHostImpl::ProcessScrollDeltas() {
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 32e461186..8ecfcdf 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -2033,7 +2033,7 @@ TEST_F(LayerTreeHostImplTest, PinchGesture) {
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale,
max_page_scale);
scroll_layer->SetScrollDelta(gfx::Vector2d());
- scroll_layer->PullDeltaForMainThread();
+ scroll_layer->synced_scroll_offset()->PullDeltaForMainThread();
scroll_layer->PushScrollOffsetFromMainThread(gfx::ScrollOffset(50, 50));
float page_scale_delta = 0.1f;
@@ -2056,7 +2056,7 @@ TEST_F(LayerTreeHostImplTest, PinchGesture) {
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale,
max_page_scale);
scroll_layer->SetScrollDelta(gfx::Vector2d());
- scroll_layer->PullDeltaForMainThread();
+ scroll_layer->synced_scroll_offset()->PullDeltaForMainThread();
scroll_layer->PushScrollOffsetFromMainThread(gfx::ScrollOffset(20, 20));
float page_scale_delta = 1.f;
@@ -2079,7 +2079,7 @@ TEST_F(LayerTreeHostImplTest, PinchGesture) {
host_impl_->active_tree()->PushPageScaleFromMainThread(1.f, min_page_scale,
max_page_scale);
scroll_layer->SetScrollDelta(gfx::Vector2d());
- scroll_layer->PullDeltaForMainThread();
+ scroll_layer->synced_scroll_offset()->PullDeltaForMainThread();
scroll_layer->PushScrollOffsetFromMainThread(gfx::ScrollOffset(20, 20));
float page_scale_delta = 1.f;
@@ -2104,7 +2104,7 @@ TEST_F(LayerTreeHostImplTest, PinchGesture) {
{
host_impl_->active_tree()->PushPageScaleFromMainThread(0.5f, 0.5f, 4.f);
scroll_layer->SetScrollDelta(gfx::Vector2d());
- scroll_layer->PullDeltaForMainThread();
+ scroll_layer->synced_scroll_offset()->PullDeltaForMainThread();
scroll_layer->PushScrollOffsetFromMainThread(gfx::ScrollOffset(0, 0));
host_impl_->ScrollBegin(BeginState(gfx::Point(0, 0)).get(),
@@ -10192,12 +10192,27 @@ TEST_F(LayerTreeHostImplTest, JitterTest) {
LayerTreeImpl* pending_tree = host_impl_->pending_tree();
pending_tree->PushPageScaleFromMainThread(1.f, 1.f, 1.f);
+ LayerImpl* last_scrolled_layer = pending_tree->LayerById(
+ host_impl_->active_tree()->LastScrolledLayerId());
+
+ // When building property trees from impl side, the builder uses the scroll
+ // offset of layer_impl to initialize the scroll offset in scroll tree:
+ // scroll_tree.synced_scroll_offset.PushFromMainThread(
+ // layer->CurrentScrollOffset()).
+ // However, layer_impl does not store scroll_offset, so it is using scroll
+ // tree's scroll offset to initialize itself. Usually this approach works
+ // because this is a simple assignment. However if scroll_offset's pending
+ // delta is not zero, the delta would be counted twice.
+ // This hacking here is to restore the damaged scroll offset.
+ gfx::ScrollOffset pending_base =
+ last_scrolled_layer->synced_scroll_offset()->PendingBase();
pending_tree->property_trees()->needs_rebuild = true;
pending_tree->BuildPropertyTreesForTesting();
+ last_scrolled_layer->synced_scroll_offset()->PushFromMainThread(
+ pending_base);
+
pending_tree->set_needs_update_draw_properties();
pending_tree->UpdateDrawProperties(false);
- LayerImpl* last_scrolled_layer = pending_tree->LayerById(
- host_impl_->active_tree()->LastScrolledLayerId());
float jitter =
LayerTreeHostCommon::CalculateFrameJitter(last_scrolled_layer);
// There should not be any jitter measured till we hit the fixed point hits
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 78cd744..89567a5 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -320,6 +320,7 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) {
LayerImpl* layer = target_tree->CurrentlyScrollingLayer();
target_tree->SetPropertyTrees(property_trees_);
target_tree->SetCurrentlyScrollingLayer(layer);
+ target_tree->UpdatePropertyTreeScrollOffset(&property_trees_);
if (next_activation_forces_redraw_) {
target_tree->ForceRedrawNextActivation();
@@ -687,10 +688,7 @@ void LayerTreeImpl::ApplySentScrollAndScaleDeltasFromAbortedCommit() {
if (!root_layer())
return;
- LayerTreeHostCommon::CallFunctionForSubtree(
- root_layer(), [](LayerImpl* layer) {
- layer->ApplySentScrollDeltasFromAbortedCommit();
- });
+ property_trees()->scroll_tree.ApplySentScrollDeltasFromAbortedCommit();
}
void LayerTreeImpl::SetViewportLayersFromIds(
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index 07ab83e..9f20b13 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -135,8 +135,10 @@ class CC_EXPORT LayerTreeImpl {
void SetRootLayer(scoped_ptr<LayerImpl>);
scoped_ptr<LayerImpl> DetachLayerTree();
- void SetPropertyTrees(const PropertyTrees& property_trees) {
+ void SetPropertyTrees(const PropertyTrees property_trees) {
property_trees_ = property_trees;
+ property_trees_.is_main_thread = false;
+ property_trees_.is_active = IsActiveTree();
property_trees_.transform_tree.set_source_to_parent_updates_allowed(false);
}
PropertyTrees* property_trees() { return &property_trees_; }
@@ -216,6 +218,10 @@ class CC_EXPORT LayerTreeImpl {
}
void UpdatePropertyTreeScrollingAndAnimationFromMainThread();
+ void UpdatePropertyTreeScrollOffset(PropertyTrees* property_trees) {
+ property_trees_.scroll_tree.UpdateScrollOffsetMap(
+ &property_trees->scroll_tree.scroll_offset_map(), this);
+ }
void SetPageScaleOnActiveTree(float active_page_scale);
void PushPageScaleFromMainThread(float page_scale_factor,
float min_page_scale_factor,
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index 27819d9..4738e01 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -10,11 +10,15 @@
#include "base/logging.h"
#include "cc/base/math_util.h"
#include "cc/input/main_thread_scrolling_reason.h"
+#include "cc/layers/layer_impl.h"
#include "cc/proto/gfx_conversions.h"
#include "cc/proto/property_tree.pb.h"
#include "cc/proto/scroll_offset.pb.h"
+#include "cc/proto/synced_property_conversions.h"
#include "cc/proto/transform.pb.h"
#include "cc/proto/vector2df.pb.h"
+#include "cc/trees/layer_tree_host_common.h"
+#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/property_tree.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
@@ -1302,13 +1306,46 @@ void EffectTree::FromProtobuf(const proto::PropertyTree& proto) {
PropertyTree::FromProtobuf(proto);
}
-ScrollTree::ScrollTree() : currently_scrolling_node_id_(-1) {}
+ScrollTree::ScrollTree()
+ : currently_scrolling_node_id_(-1),
+ layer_id_to_scroll_offset_map_(ScrollTree::ScrollOffsetMap()) {}
ScrollTree::~ScrollTree() {}
+ScrollTree& ScrollTree::operator=(const ScrollTree& from) {
+ PropertyTree::operator=(from);
+ currently_scrolling_node_id_ = -1;
+ // layer_id_to_scroll_offset_map_ is intentionally omitted in operator=,
+ // because we do not want to simply copy the map when property tree is
+ // propagating from pending to active.
+ // In the main to pending case, we do want to copy it, but this can be done by
+ // calling UpdateScrollOffsetMap after the assignment;
+ // In the other case, we want pending and active property trees to share the
+ // same map.
+ return *this;
+}
+
bool ScrollTree::operator==(const ScrollTree& other) const {
- return PropertyTree::operator==(other) &&
- CurrentlyScrollingNode() == other.CurrentlyScrollingNode();
+ const ScrollTree::ScrollOffsetMap& other_scroll_offset_map =
+ other.scroll_offset_map();
+ if (layer_id_to_scroll_offset_map_.size() != other_scroll_offset_map.size())
+ return false;
+
+ for (auto map_entry : layer_id_to_scroll_offset_map_) {
+ int key = map_entry.first;
+ if (other_scroll_offset_map.find(key) == other_scroll_offset_map.end() ||
+ map_entry.second != layer_id_to_scroll_offset_map_.at(key))
+ return false;
+ }
+
+ bool is_currently_scrolling_node_equal =
+ (currently_scrolling_node_id_ == -1)
+ ? (!other.CurrentlyScrollingNode())
+ : (other.CurrentlyScrollingNode() &&
+ currently_scrolling_node_id_ ==
+ other.CurrentlyScrollingNode()->id);
+
+ return PropertyTree::operator==(other) && is_currently_scrolling_node_equal;
}
void ScrollTree::ToProtobuf(proto::PropertyTree* proto) const {
@@ -1319,6 +1356,14 @@ void ScrollTree::ToProtobuf(proto::PropertyTree* proto) const {
proto::ScrollTreeData* data = proto->mutable_scroll_tree_data();
data->set_currently_scrolling_node_id(currently_scrolling_node_id_);
+ for (auto i : layer_id_to_scroll_offset_map_) {
+ data->add_layer_id_to_scroll_offset_map();
+ proto::ScrollOffsetMapEntry* entry =
+ data->mutable_layer_id_to_scroll_offset_map(
+ data->layer_id_to_scroll_offset_map_size() - 1);
+ entry->set_layer_id(i.first);
+ SyncedScrollOffsetToProto(*i.second.get(), entry->mutable_scroll_offset());
+ }
}
void ScrollTree::FromProtobuf(const proto::PropertyTree& proto) {
@@ -1329,6 +1374,24 @@ void ScrollTree::FromProtobuf(const proto::PropertyTree& proto) {
const proto::ScrollTreeData& data = proto.scroll_tree_data();
currently_scrolling_node_id_ = data.currently_scrolling_node_id();
+
+ for (int i = 0; i < data.layer_id_to_scroll_offset_map_size(); ++i) {
+ const proto::ScrollOffsetMapEntry entry =
+ data.layer_id_to_scroll_offset_map(i);
+ layer_id_to_scroll_offset_map_[entry.layer_id()] = new SyncedScrollOffset();
+ ProtoToSyncedScrollOffset(
+ entry.scroll_offset(),
+ layer_id_to_scroll_offset_map_[entry.layer_id()].get());
+ }
+}
+
+void ScrollTree::clear() {
+ PropertyTree<ScrollNode>::clear();
+
+ if (property_trees()->is_main_thread) {
+ currently_scrolling_node_id_ = -1;
+ layer_id_to_scroll_offset_map_.clear();
+ }
}
gfx::ScrollOffset ScrollTree::MaxScrollOffset(int scroll_node_id) const {
@@ -1414,12 +1477,130 @@ gfx::Transform ScrollTree::ScreenSpaceTransform(int scroll_node_id) const {
return screen_space_transform;
}
+// TODO(sunxd): Make this function private once scroll offset access is fully
+// directed to scroll tree.
+SyncedScrollOffset* ScrollTree::synced_scroll_offset(int layer_id) {
+ if (layer_id_to_scroll_offset_map_.find(layer_id) ==
+ layer_id_to_scroll_offset_map_.end()) {
+ layer_id_to_scroll_offset_map_[layer_id] = new SyncedScrollOffset;
+ }
+ return layer_id_to_scroll_offset_map_[layer_id].get();
+}
+
+gfx::ScrollOffset ScrollTree::PullDeltaForMainThread(
+ SyncedScrollOffset* scroll_offset) {
+ // TODO(miletus): Remove all this temporary flooring machinery when
+ // Blink fully supports fractional scrolls.
+ gfx::ScrollOffset current_offset =
+ scroll_offset->Current(property_trees()->is_active);
+ gfx::ScrollOffset current_delta = property_trees()->is_active
+ ? scroll_offset->Delta()
+ : scroll_offset->PendingDelta().get();
+ gfx::ScrollOffset floored_delta(floor(current_delta.x()),
+ floor(current_delta.y()));
+ gfx::ScrollOffset diff_delta = floored_delta - current_delta;
+ gfx::ScrollOffset tmp_offset = current_offset + diff_delta;
+ scroll_offset->SetCurrent(tmp_offset);
+ gfx::ScrollOffset delta = scroll_offset->PullDeltaForMainThread();
+ scroll_offset->SetCurrent(current_offset);
+ return delta;
+}
+
+void ScrollTree::CollectScrollDeltas(ScrollAndScaleSet* scroll_info) {
+ for (auto map_entry : layer_id_to_scroll_offset_map_) {
+ gfx::ScrollOffset scroll_delta =
+ PullDeltaForMainThread(map_entry.second.get());
+
+ if (!scroll_delta.IsZero()) {
+ LayerTreeHostCommon::ScrollUpdateInfo scroll;
+ scroll.layer_id = map_entry.first;
+ scroll.scroll_delta = gfx::Vector2d(scroll_delta.x(), scroll_delta.y());
+ scroll_info->scrolls.push_back(scroll);
+ }
+ }
+}
+
+void ScrollTree::UpdateScrollOffsetMapEntry(
+ int key,
+ ScrollTree::ScrollOffsetMap* new_scroll_offset_map,
+ LayerTreeImpl* layer_tree_impl) {
+ bool changed = false;
+ // If we are pushing scroll offset from main to pending tree, we create a new
+ // instance of synced scroll offset; if we are pushing from pending to active,
+ // we reuse the pending tree's value in the map.
+ if (!property_trees()->is_active) {
+ changed = synced_scroll_offset(key)->PushFromMainThread(
+ new_scroll_offset_map->at(key)->PendingBase());
+
+ if (new_scroll_offset_map->at(key)->clobber_active_value()) {
+ synced_scroll_offset(key)->set_clobber_active_value();
+ }
+ if (changed)
+ layer_tree_impl->LayerById(key)->DidUpdateScrollOffset();
+ } else {
+ layer_id_to_scroll_offset_map_[key] = new_scroll_offset_map->at(key);
+ changed |= synced_scroll_offset(key)->PushPendingToActive();
+ if (changed)
+ layer_tree_impl->LayerById(key)->DidUpdateScrollOffset();
+ }
+}
+
+void ScrollTree::UpdateScrollOffsetMap(
+ ScrollTree::ScrollOffsetMap* new_scroll_offset_map,
+ LayerTreeImpl* layer_tree_impl) {
+ if (layer_tree_impl && layer_tree_impl->root_layer()) {
+ DCHECK(!property_trees()->is_main_thread);
+ for (auto map_entry = layer_id_to_scroll_offset_map_.begin();
+ map_entry != layer_id_to_scroll_offset_map_.end();) {
+ int key = map_entry->first;
+ if (new_scroll_offset_map->find(key) != new_scroll_offset_map->end()) {
+ UpdateScrollOffsetMapEntry(key, new_scroll_offset_map, layer_tree_impl);
+ ++map_entry;
+ } else {
+ map_entry = layer_id_to_scroll_offset_map_.erase(map_entry);
+ }
+ }
+
+ for (auto& map_entry : *new_scroll_offset_map) {
+ int key = map_entry.first;
+ if (layer_id_to_scroll_offset_map_.find(key) ==
+ layer_id_to_scroll_offset_map_.end())
+ UpdateScrollOffsetMapEntry(key, new_scroll_offset_map, layer_tree_impl);
+ }
+ }
+}
+
+ScrollTree::ScrollOffsetMap& ScrollTree::scroll_offset_map() {
+ return layer_id_to_scroll_offset_map_;
+}
+
+const ScrollTree::ScrollOffsetMap& ScrollTree::scroll_offset_map() const {
+ return layer_id_to_scroll_offset_map_;
+}
+
+void ScrollTree::ApplySentScrollDeltasFromAbortedCommit() {
+ DCHECK(property_trees()->is_active);
+ for (auto& map_entry : layer_id_to_scroll_offset_map_)
+ map_entry.second->AbortCommit();
+}
+
+bool ScrollTree::SetScrollOffset(int layer_id,
+ const gfx::ScrollOffset& scroll_offset) {
+ if (property_trees()->is_main_thread)
+ return synced_scroll_offset(layer_id)->PushFromMainThread(scroll_offset);
+ else if (property_trees()->is_active)
+ return synced_scroll_offset(layer_id)->SetCurrent(scroll_offset);
+ return false;
+}
+
PropertyTrees::PropertyTrees()
: needs_rebuild(true),
non_root_surfaces_enabled(true),
changed(false),
full_tree_damaged(false),
- sequence_number(0) {
+ sequence_number(0),
+ is_main_thread(true),
+ is_active(false) {
transform_tree.SetPropertyTrees(this);
effect_tree.SetPropertyTrees(this);
clip_tree.SetPropertyTrees(this);
@@ -1434,6 +1615,8 @@ bool PropertyTrees::operator==(const PropertyTrees& other) const {
scroll_tree == other.scroll_tree &&
needs_rebuild == other.needs_rebuild && changed == other.changed &&
full_tree_damaged == other.full_tree_damaged &&
+ is_main_thread == other.is_main_thread &&
+ is_active == other.is_active &&
non_root_surfaces_enabled == other.non_root_surfaces_enabled &&
sequence_number == other.sequence_number;
}
@@ -1448,6 +1631,8 @@ PropertyTrees& PropertyTrees::operator=(const PropertyTrees& from) {
full_tree_damaged = from.full_tree_damaged;
non_root_surfaces_enabled = from.non_root_surfaces_enabled;
sequence_number = from.sequence_number;
+ is_main_thread = from.is_main_thread;
+ is_active = from.is_active;
inner_viewport_container_bounds_delta_ =
from.inner_viewport_container_bounds_delta();
outer_viewport_container_bounds_delta_ =
@@ -1472,6 +1657,8 @@ void PropertyTrees::ToProtobuf(proto::PropertyTrees* proto) const {
proto->set_changed(changed);
proto->set_full_tree_damaged(full_tree_damaged);
proto->set_non_root_surfaces_enabled(non_root_surfaces_enabled);
+ proto->set_is_main_thread(is_main_thread);
+ proto->set_is_active(is_active);
// TODO(khushalsagar): Consider using the sequence number to decide if
// property trees need to be serialized again for a commit. See crbug/555370.
@@ -1490,6 +1677,8 @@ void PropertyTrees::FromProtobuf(const proto::PropertyTrees& proto) {
full_tree_damaged = proto.full_tree_damaged();
non_root_surfaces_enabled = proto.non_root_surfaces_enabled();
sequence_number = proto.sequence_number();
+ is_main_thread = proto.is_main_thread();
+ is_active = proto.is_active();
transform_tree.SetPropertyTrees(this);
effect_tree.SetPropertyTrees(this);
diff --git a/cc/trees/property_tree.h b/cc/trees/property_tree.h
index c35ec8d..c57ad75 100644
--- a/cc/trees/property_tree.h
+++ b/cc/trees/property_tree.h
@@ -7,9 +7,11 @@
#include <stddef.h>
+#include <unordered_map>
#include <vector>
#include "cc/base/cc_export.h"
+#include "cc/base/synced_property.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/scroll_offset.h"
#include "ui/gfx/transform.h"
@@ -27,6 +29,9 @@ class TransformTreeData;
class TreeNode;
}
+class LayerTreeImpl;
+struct ScrollAndScaleSet;
+
// ------------------------------*IMPORTANT*---------------------------------
// Each class declared here has a corresponding proto defined in
// cc/proto/property_tree.proto. When making any changes to a class structure
@@ -34,6 +39,8 @@ class TreeNode;
// change to its proto and the ToProtobuf and FromProtobuf methods for that
// class.
+typedef SyncedProperty<AdditionGroup<gfx::ScrollOffset>> SyncedScrollOffset;
+
template <typename T>
struct CC_EXPORT TreeNode {
TreeNode() : id(-1), parent_id(-1), owner_id(-1), data() {}
@@ -551,11 +558,17 @@ class CC_EXPORT ScrollTree final : public PropertyTree<ScrollNode> {
ScrollTree();
~ScrollTree() override;
+ ScrollTree& operator=(const ScrollTree& from);
bool operator==(const ScrollTree& other) const;
void ToProtobuf(proto::PropertyTree* proto) const;
void FromProtobuf(const proto::PropertyTree& proto);
+ void clear() override;
+
+ typedef std::unordered_map<int, scoped_refptr<SyncedScrollOffset>>
+ ScrollOffsetMap;
+
gfx::ScrollOffset MaxScrollOffset(int scroll_node_id) const;
gfx::Size scroll_clip_layer_bounds(int scroll_node_id) const;
ScrollNode* CurrentlyScrollingNode();
@@ -563,8 +576,24 @@ class CC_EXPORT ScrollTree final : public PropertyTree<ScrollNode> {
void set_currently_scrolling_node(int scroll_node_id);
gfx::Transform ScreenSpaceTransform(int scroll_node_id) const;
+ // synced_scroll_offset is supposed to be called by Layer/LayerImpl only
+ SyncedScrollOffset* synced_scroll_offset(int layer_id);
+ void CollectScrollDeltas(ScrollAndScaleSet* scroll_info);
+ void UpdateScrollOffsetMap(ScrollOffsetMap* new_scroll_offset_map,
+ LayerTreeImpl* layer_tree_impl);
+ ScrollOffsetMap& scroll_offset_map();
+ const ScrollOffsetMap& scroll_offset_map() const;
+ void ApplySentScrollDeltasFromAbortedCommit();
+ bool SetScrollOffset(int layer_id, const gfx::ScrollOffset& scroll_offset);
+
private:
int currently_scrolling_node_id_;
+ ScrollOffsetMap layer_id_to_scroll_offset_map_;
+
+ gfx::ScrollOffset PullDeltaForMainThread(SyncedScrollOffset* scroll_offset);
+ void UpdateScrollOffsetMapEntry(int key,
+ ScrollOffsetMap* new_scroll_offset_map,
+ LayerTreeImpl* layer_tree_impl);
};
class CC_EXPORT PropertyTrees final {
@@ -596,6 +625,8 @@ class CC_EXPORT PropertyTrees final {
// individual nodes.
bool full_tree_damaged;
int sequence_number;
+ bool is_main_thread;
+ bool is_active;
enum ResetFlags { EFFECT_TREE, TRANSFORM_TREE, ALL_TREES };
void SetInnerViewportContainerBoundsDelta(gfx::Vector2dF bounds_delta);
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index 976e076..14338a4 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -715,6 +715,11 @@ void AddScrollNodeIfNeeded(
node.data.main_thread_scrolling_reasons;
data_for_children->scroll_tree_parent_created_by_uninheritable_criteria =
scroll_node_uninheritable_criteria;
+
+ if (node.data.scrollable) {
+ data_for_children->scroll_tree->synced_scroll_offset(layer->id())
+ ->PushFromMainThread(layer->CurrentScrollOffset());
+ }
}
layer->SetScrollTreeIndex(data_for_children->scroll_tree_parent);
@@ -959,6 +964,8 @@ void PropertyTreeBuilder::BuildPropertyTrees(
const gfx::Rect& viewport,
const gfx::Transform& device_transform,
PropertyTrees* property_trees) {
+ property_trees->is_main_thread = true;
+ property_trees->is_active = false;
SkColor color = root_layer->layer_tree_host()->background_color();
if (SkColorGetA(color) != 255)
color = SkColorSetA(color, 255);
@@ -981,6 +988,8 @@ void PropertyTreeBuilder::BuildPropertyTrees(
const gfx::Rect& viewport,
const gfx::Transform& device_transform,
PropertyTrees* property_trees) {
+ property_trees->is_main_thread = false;
+ property_trees->is_active = root_layer->IsActive();
SkColor color = root_layer->layer_tree_impl()->background_color();
if (SkColorGetA(color) != 255)
color = SkColorSetA(color, 255);
diff --git a/cc/trees/property_tree_unittest.cc b/cc/trees/property_tree_unittest.cc
index cf682a9..0d70672 100644
--- a/cc/trees/property_tree_unittest.cc
+++ b/cc/trees/property_tree_unittest.cc
@@ -260,7 +260,9 @@ TEST(PropertyTreeSerializationTest, ScrollNodeSerialization) {
}
TEST(PropertyTreeSerializationTest, ScrollTreeSerialization) {
- ScrollTree original;
+ PropertyTrees property_trees;
+ property_trees.is_main_thread = true;
+ ScrollTree& original = property_trees.scroll_tree;
ScrollNode second;
second.data.scrollable = true;
second.data.bounds = gfx::Size(15, 15);
@@ -270,6 +272,9 @@ TEST(PropertyTreeSerializationTest, ScrollTreeSerialization) {
original.Insert(second, 0);
original.Insert(third, 1);
+ original.set_currently_scrolling_node(1);
+ original.synced_scroll_offset(1)->PushFromMainThread(gfx::ScrollOffset(1, 2));
+
proto::PropertyTree proto;
original.ToProtobuf(&proto);
ScrollTree result;
diff --git a/cc/trees/tree_synchronizer_unittest.cc b/cc/trees/tree_synchronizer_unittest.cc
index 95f5f9c..417cda5 100644
--- a/cc/trees/tree_synchronizer_unittest.cc
+++ b/cc/trees/tree_synchronizer_unittest.cc
@@ -219,6 +219,24 @@ class TreeSynchronizerTest : public testing::Test {
TestTaskGraphRunner task_graph_runner_;
scoped_ptr<FakeLayerTreeHost> host_;
LayerSettings layer_settings_;
+
+ bool is_equal(ScrollTree::ScrollOffsetMap map,
+ ScrollTree::ScrollOffsetMap other) {
+ if (map.size() != other.size())
+ return false;
+ for (auto& map_entry : map) {
+ if (other.find(map_entry.first) == other.end())
+ return false;
+ SyncedScrollOffset& from_map = *map_entry.second.get();
+ SyncedScrollOffset& from_other = *other[map_entry.first].get();
+ if (from_map.PendingBase() != from_other.PendingBase() ||
+ from_map.ActiveBase() != from_other.ActiveBase() ||
+ from_map.Delta() != from_other.Delta() ||
+ from_map.PendingDelta().get() != from_other.PendingDelta().get())
+ return false;
+ }
+ return true;
+ }
};
// Attempts to synchronizes a null tree. This should not crash, and should
@@ -800,5 +818,102 @@ TEST_F(TreeSynchronizerTest, SynchronizeCurrentlyScrollingNode) {
host_impl->active_tree()->CurrentlyScrollingLayer()->id());
}
+TEST_F(TreeSynchronizerTest, SynchronizeScrollTreeScrollOffsetMap) {
+ host_->InitializeSingleThreaded(&client_, base::ThreadTaskRunnerHandle::Get(),
+ nullptr);
+ LayerTreeSettings settings;
+ FakeLayerTreeHostImplClient client;
+ FakeImplTaskRunnerProvider task_runner_provider;
+ FakeRenderingStatsInstrumentation stats_instrumentation;
+ TestSharedBitmapManager shared_bitmap_manager;
+ TestTaskGraphRunner task_graph_runner;
+ FakeLayerTreeHostImpl* host_impl = host_->host_impl();
+ host_impl->CreatePendingTree();
+
+ scoped_refptr<Layer> layer_tree_root = Layer::Create(layer_settings_);
+ scoped_refptr<Layer> scroll_clip_layer = Layer::Create(layer_settings_);
+ scoped_refptr<Layer> scroll_layer = Layer::Create(layer_settings_);
+ scoped_refptr<Layer> transient_scroll_clip_layer =
+ Layer::Create(layer_settings_);
+ scoped_refptr<Layer> transient_scroll_layer = Layer::Create(layer_settings_);
+
+ layer_tree_root->AddChild(transient_scroll_clip_layer);
+ transient_scroll_clip_layer->AddChild(transient_scroll_layer);
+ transient_scroll_layer->AddChild(scroll_clip_layer);
+ scroll_clip_layer->AddChild(scroll_layer);
+
+ transient_scroll_layer->SetScrollClipLayerId(
+ transient_scroll_clip_layer->id());
+ scroll_layer->SetScrollClipLayerId(scroll_clip_layer->id());
+ transient_scroll_layer->SetScrollOffset(gfx::ScrollOffset(1, 2));
+ scroll_layer->SetScrollOffset(gfx::ScrollOffset(10, 20));
+
+ host_->SetRootLayer(layer_tree_root);
+ host_->BuildPropertyTreesForTesting();
+ host_->CommitAndCreatePendingTree();
+ host_impl->ActivateSyncTree();
+
+ ExpectTreesAreIdentical(layer_tree_root.get(),
+ host_impl->active_tree()->root_layer(),
+ host_impl->active_tree());
+
+ // After the initial commit, scroll_offset_map in scroll_tree is expected to
+ // have one entry for scroll_layer and one entry for transient_scroll_layer,
+ // the pending base and active base must be the same at this stage.
+ ScrollTree::ScrollOffsetMap scroll_offset_map;
+ scroll_offset_map[scroll_layer->id()] = new SyncedScrollOffset;
+ scroll_offset_map[transient_scroll_layer->id()] = new SyncedScrollOffset;
+ scroll_offset_map[scroll_layer->id()]->PushFromMainThread(
+ scroll_layer->scroll_offset());
+ scroll_offset_map[scroll_layer->id()]->PushPendingToActive();
+ scroll_offset_map[transient_scroll_layer->id()]->PushFromMainThread(
+ transient_scroll_layer->scroll_offset());
+ scroll_offset_map[transient_scroll_layer->id()]->PushPendingToActive();
+ EXPECT_TRUE(
+ is_equal(scroll_offset_map, host_impl->active_tree()
+ ->property_trees()
+ ->scroll_tree.scroll_offset_map()));
+
+ // Set ScrollOffset active delta: gfx::ScrollOffset(10, 10)
+ LayerImpl* scroll_layer_impl =
+ host_impl->active_tree()->LayerById(scroll_layer->id());
+ ScrollTree& scroll_tree =
+ host_impl->active_tree()->property_trees()->scroll_tree;
+ scroll_tree.synced_scroll_offset(scroll_layer_impl->id())
+ ->SetCurrent(gfx::ScrollOffset(20, 30));
+
+ // Pull ScrollOffset delta for main thread, and change offset on main thread
+ scoped_ptr<ScrollAndScaleSet> scroll_info(new ScrollAndScaleSet());
+ scroll_tree.CollectScrollDeltas(scroll_info.get());
+ host_->proxy()->SetNeedsCommit();
+ host_->ApplyScrollAndScale(scroll_info.get());
+ EXPECT_EQ(gfx::ScrollOffset(20, 30), scroll_layer->scroll_offset());
+ scroll_layer->SetScrollOffset(gfx::ScrollOffset(100, 100));
+
+ // More update to ScrollOffset active delta: gfx::ScrollOffset(20, 20)
+ scroll_tree.synced_scroll_offset(scroll_layer_impl->id())
+ ->SetCurrent(gfx::ScrollOffset(40, 50));
+ host_impl->active_tree()->SetCurrentlyScrollingLayer(scroll_layer_impl);
+
+ // Make one layer unscrollable so that scroll tree topology changes
+ transient_scroll_layer->SetScrollClipLayerId(Layer::INVALID_ID);
+ host_->BuildPropertyTreesForTesting();
+
+ host_impl->CreatePendingTree();
+ host_->CommitAndCreatePendingTree();
+ host_impl->ActivateSyncTree();
+
+ EXPECT_EQ(scroll_layer->id(),
+ host_impl->active_tree()->CurrentlyScrollingLayer()->id());
+ scroll_offset_map.erase(transient_scroll_layer->id());
+ scroll_offset_map[scroll_layer->id()]->SetCurrent(gfx::ScrollOffset(20, 30));
+ scroll_offset_map[scroll_layer->id()]->PullDeltaForMainThread();
+ scroll_offset_map[scroll_layer->id()]->SetCurrent(gfx::ScrollOffset(40, 50));
+ scroll_offset_map[scroll_layer->id()]->PushFromMainThread(
+ gfx::ScrollOffset(100, 100));
+ scroll_offset_map[scroll_layer->id()]->PushPendingToActive();
+ EXPECT_TRUE(is_equal(scroll_offset_map, scroll_tree.scroll_offset_map()));
+}
+
} // namespace
} // namespace cc