Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(776)

Unified Diff: cc/layers/layer_impl.cc

Issue 23983047: Pinch/Zoom Infrastructure & Plumbing CL (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add fix for empty scroll-layer bounds. Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: cc/layers/layer_impl.cc
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 74a9b8468d79c673288fb29cb86459972b99d2c9..676a800891ecf634c7b1bccb84b306316cf1550c 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -41,7 +41,7 @@ LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, int id)
anchor_point_(0.5f, 0.5f),
anchor_point_z_(0.f),
scroll_offset_delegate_(NULL),
- scrollable_(false),
+ scroll_clip_layer_(NULL),
should_scroll_on_main_thread_(false),
have_wheel_event_handlers_(false),
user_scrollable_horizontal_(true),
@@ -63,9 +63,7 @@ LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, int id)
opacity_(1.0),
blend_mode_(SkXfermode::kSrcOver_Mode),
draw_depth_(0.f),
- current_draw_mode_(DRAW_MODE_NONE),
- horizontal_scrollbar_layer_(NULL),
- vertical_scrollbar_layer_(NULL) {
+ current_draw_mode_(DRAW_MODE_NONE) {
DCHECK_GT(layer_id_, 0);
DCHECK(layer_tree_impl_);
layer_tree_impl_->RegisterLayer(this);
@@ -359,7 +357,7 @@ void LayerImpl::SetSentScrollDelta(gfx::Vector2d sent_scroll_delta) {
gfx::Vector2dF LayerImpl::ScrollBy(gfx::Vector2dF scroll) {
DCHECK(scrollable());
gfx::Vector2dF min_delta = -scroll_offset_;
- gfx::Vector2dF max_delta = max_scroll_offset_ - scroll_offset_;
+ gfx::Vector2dF max_delta = MaxScrollOffset() - scroll_offset_;
// Clamp new_delta so that position + delta stays within scroll bounds.
gfx::Vector2dF new_delta = (ScrollDelta() + scroll);
new_delta.SetToMax(min_delta);
@@ -367,9 +365,14 @@ gfx::Vector2dF LayerImpl::ScrollBy(gfx::Vector2dF scroll) {
gfx::Vector2dF unscrolled =
ScrollDelta() + scroll - new_delta;
SetScrollDelta(new_delta);
+
return unscrolled;
}
+void LayerImpl::SetScrollClipLayer(int scroll_clip_layer_id) {
+ scroll_clip_layer_ = layer_tree_impl()->LayerById(scroll_clip_layer_id);
+}
+
void LayerImpl::ApplySentScrollDeltasFromAbortedCommit() {
// Pending tree never has sent scroll deltas
DCHECK(layer_tree_impl()->IsActiveTree());
@@ -459,7 +462,8 @@ InputHandler::ScrollStatus LayerImpl::TryScroll(
return InputHandler::ScrollIgnored;
}
- if (max_scroll_offset_.x() <= 0 && max_scroll_offset_.y() <= 0) {
+ gfx::Vector2d max_scroll_offset = MaxScrollOffset();
+ if (max_scroll_offset.x() <= 0 && max_scroll_offset.y() <= 0) {
TRACE_EVENT0("cc",
"LayerImpl::tryScroll: Ignored. Technically scrollable,"
" but has no affordance in either direction.");
@@ -531,15 +535,14 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) {
layer->SetSublayerTransform(sublayer_transform_);
layer->SetTransform(transform_);
- layer->SetScrollable(scrollable_);
+ layer->SetScrollClipLayer(scroll_clip_layer_ ? scroll_clip_layer_->id()
+ : Layer::INVALID_ID);
layer->set_user_scrollable_horizontal(user_scrollable_horizontal_);
layer->set_user_scrollable_vertical(user_scrollable_vertical_);
layer->SetScrollOffsetAndDelta(
scroll_offset_, layer->ScrollDelta() - layer->sent_scroll_delta());
layer->SetSentScrollDelta(gfx::Vector2d());
- layer->SetMaxScrollOffset(max_scroll_offset_);
-
LayerImpl* scroll_parent = NULL;
if (scroll_parent_)
scroll_parent = layer->layer_tree_impl()->LayerById(scroll_parent_->id());
@@ -612,8 +615,8 @@ base::DictionaryValue* LayerImpl::LayerTreeAsJson() const {
result->SetDouble("Opacity", opacity());
result->SetBoolean("ContentsOpaque", contents_opaque_);
- if (scrollable_)
- result->SetBoolean("Scrollable", scrollable_);
+ if (scrollable())
+ result->SetBoolean("Scrollable", true);
if (have_wheel_event_handlers_)
result->SetBoolean("WheelHandler", have_wheel_event_handlers_);
@@ -721,6 +724,7 @@ void LayerImpl::SetBounds(gfx::Size bounds) {
bounds_ = bounds;
+ ScrollbarParametersDidChange();
if (masks_to_bounds())
NoteLayerPropertyChangedForSubtree();
else
@@ -988,44 +992,6 @@ void LayerImpl::CalculateContentsScale(
*content_bounds = this->content_bounds();
}
-void LayerImpl::UpdateScrollbarPositions() {
- gfx::Vector2dF current_offset = scroll_offset_ + ScrollDelta();
-
- gfx::RectF viewport(PointAtOffsetFromOrigin(current_offset), bounds_);
- gfx::SizeF scrollable_size(max_scroll_offset_.x() + bounds_.width(),
- max_scroll_offset_.y() + bounds_.height());
- if (horizontal_scrollbar_layer_) {
- horizontal_scrollbar_layer_->SetCurrentPos(current_offset.x());
- horizontal_scrollbar_layer_->SetMaximum(max_scroll_offset_.x());
- horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(
- viewport.width() / scrollable_size.width());
- }
- if (vertical_scrollbar_layer_) {
- vertical_scrollbar_layer_->SetCurrentPos(current_offset.y());
- vertical_scrollbar_layer_->SetMaximum(max_scroll_offset_.y());
- vertical_scrollbar_layer_->SetVisibleToTotalLengthRatio(
- viewport.height() / scrollable_size.height());
- }
-
- if (current_offset == last_scroll_offset_)
- return;
- last_scroll_offset_ = current_offset;
-
- if (scrollbar_animation_controller_) {
- bool should_animate = scrollbar_animation_controller_->DidScrollUpdate(
- layer_tree_impl_->CurrentPhysicalTimeTicks());
- if (should_animate)
- layer_tree_impl_->StartScrollbarAnimation();
- }
-
- // Get the current_offset_.y() value for a sanity-check on scrolling
- // benchmark metrics. Specifically, we want to make sure
- // BasicMouseWheelSmoothScrollGesture has proper scroll curves.
- if (layer_tree_impl()->IsActiveTree()) {
- TRACE_COUNTER_ID1("gpu", "scroll_offset_y", this->id(), current_offset.y());
- }
-}
-
void LayerImpl::SetScrollOffsetDelegate(
LayerScrollOffsetDelegate* scroll_offset_delegate) {
// Having both a scroll parent and a scroll offset delegate is unsupported.
@@ -1036,10 +1002,8 @@ void LayerImpl::SetScrollOffsetDelegate(
}
gfx::Vector2dF total_offset = TotalScrollOffset();
scroll_offset_delegate_ = scroll_offset_delegate;
- if (scroll_offset_delegate_) {
- scroll_offset_delegate_->SetMaxScrollOffset(max_scroll_offset_);
+ if (scroll_offset_delegate_)
scroll_offset_delegate_->SetTotalScrollOffset(total_offset);
- }
}
bool LayerImpl::IsExternalFlingActive() const {
@@ -1055,6 +1019,8 @@ void LayerImpl::SetScrollOffsetAndDelta(gfx::Vector2d scroll_offset,
gfx::Vector2dF scroll_delta) {
bool changed = false;
+ last_scroll_offset_ = scroll_offset;
+
if (scroll_offset_ != scroll_offset) {
changed = true;
scroll_offset_ = scroll_offset;
@@ -1089,7 +1055,7 @@ void LayerImpl::SetScrollOffsetAndDelta(gfx::Vector2d scroll_offset,
if (changed) {
NoteLayerPropertyChangedForSubtree();
- UpdateScrollbarPositions();
+ ScrollbarParametersDidChange();
}
}
@@ -1125,16 +1091,161 @@ void LayerImpl::DidBeginTracing() {}
void LayerImpl::DidLoseOutputSurface() {}
-void LayerImpl::SetMaxScrollOffset(gfx::Vector2d max_scroll_offset) {
- if (max_scroll_offset_ == max_scroll_offset)
+gfx::Vector2d LayerImpl::MaxScrollOffset() const {
aelias_OOO_until_Jul13 2014/01/16 03:44:04 Just checking: have you verified this logic also w
wjmaclean 2014/01/16 15:07:32 I'm not sure that I understand what you mean by "i
+ if (!scroll_clip_layer_)
+ return gfx::Vector2d();
+
+ LayerImpl const* page_scale_layer = layer_tree_impl()->page_scale_layer();
+ DCHECK(this != page_scale_layer);
+ DCHECK(scroll_clip_layer_);
+ DCHECK(this != layer_tree_impl()->InnerViewportScrollLayer()
+ || IsContainerForFixedPositionLayers());
+
+ // TODO(wjmaclean) Find out why Blink sometimes gives us empty bounds on the
+ // scroll layer.
+ //
+ // For now we will use the content layer bounds as a fallback.
+ // It's apparently a known issue that Blink sometimes gives us zero bounds on
+ // the scroll layer, but as the contents layer has no way to directly notify
+ // when its bounds have changed, and since the content layer bounds can be
+ // updated after the scroll layer bounds in a tree sync, it seems better to
+ // only use them as a fall-back.
+ gfx::Size scaled_scroll_bounds(bounds());
+ if (scaled_scroll_bounds.IsEmpty()) {
+ // TODO(wjmaclean) Should we add some metrics collection here to see how
+ // often we end up with empty scroll bounds, and on which platforms?
+ if (children().size())
+ scaled_scroll_bounds = children()[0]->bounds();
+ }
+
+ float scale_factor = 1.f;
+ LayerImpl const* last_layer = 0;
+ for (LayerImpl const* current_layer = this;
+ current_layer != scroll_clip_layer_->parent();
aelias_OOO_until_Jul13 2014/01/16 19:49:26 As one thing that appears like a bug here, you're
wjmaclean 2014/01/16 20:13:36 I wondered about that ... we can exclude the clip
+ current_layer = current_layer->parent()) {
+ float current_layer_scale = 1.f;
+
+ const gfx::Transform& layer_transform = current_layer->transform();
aelias_OOO_until_Jul13 2014/01/16 03:44:04 We don't need to take the other transforms into co
wjmaclean 2014/01/16 15:07:32 You're assuming that this scrolling mechanism is o
aelias_OOO_until_Jul13 2014/01/16 19:49:26 OK, I read this as trying to compensate for CSS sc
+ if (current_layer == page_scale_layer) {
+ DCHECK(layer_transform.IsIdentity());
+ current_layer_scale = layer_tree_impl()->total_page_scale_factor();
+ } else {
+ // TODO(wjmaclean) Should we allow for translation too?
+ if (layer_transform.IsScale()) {
+ gfx::Vector2dF layer_scale = layer_transform.Scale();
+ // TODO(wjmaclean) Allow for non-isotropic scales.
+ DCHECK(layer_scale.x() == layer_scale.y());
+ current_layer_scale = layer_scale.x();
+ }
+ }
+
+ scale_factor *= current_layer_scale;
+ last_layer = current_layer;
+ }
+ DCHECK(last_layer == scroll_clip_layer_);
+
+ scaled_scroll_bounds.SetSize(
+ scale_factor * scaled_scroll_bounds.width(),
+ scale_factor * scaled_scroll_bounds.height());
+
+ gfx::RectF clip_rect(gfx::PointF(), scroll_clip_layer_->bounds());
+ if (this == layer_tree_impl()->InnerViewportScrollLayer())
aelias_OOO_until_Jul13 2014/01/16 19:49:26 If we are going to go down the path of generality
wjmaclean 2014/01/16 20:13:36 Yes, we had identified this special case as a weak
aelias_OOO_until_Jul13 2014/01/16 20:36:38 If you try that, you're going to run into the prob
wjmaclean 2014/01/16 20:39:41 Agreed ... I believe that's why it's there for now
+ clip_rect =
+ gfx::RectF(gfx::PointF(), layer_tree_impl()->ScrollableViewportSize());
+ gfx::Vector2dF max_offset(
+ scaled_scroll_bounds.width() - scroll_clip_layer_->bounds().width(),
+ scaled_scroll_bounds.height() - scroll_clip_layer_->bounds().height());
+ // We need the final scroll offset to be in CSS coords.
+ max_offset.Scale(1 / scale_factor);
aelias_OOO_until_Jul13 2014/01/16 03:44:04 The old UpdateMaxScrollOffset() logic was simpler
wjmaclean 2014/01/16 15:07:32 Again, I think the more general model we want to s
+ return gfx::Vector2d(max_offset.x(), max_offset.y());
+}
+
+gfx::Vector2dF LayerImpl::ClampScrollToMaxScrollOffset() {
+ gfx::Vector2dF max_offset = MaxScrollOffset();
+ gfx::Vector2dF old_offset = TotalScrollOffset();
+ gfx::Vector2dF clamped_offset = old_offset;
+
+ clamped_offset.SetToMin(max_offset);
+ clamped_offset.SetToMax(gfx::Vector2d());
+ gfx::Vector2dF delta = clamped_offset - old_offset;
+ if (!delta.IsZero())
+ ScrollBy(delta);
+
+ return delta;
+}
+
+void LayerImpl::SetScrollbarPosition(ScrollbarLayerImplBase* scrollbar_layer,
+ LayerImpl* scrollbar_clip_layer) const {
+ DCHECK(scrollbar_layer);
+ LayerImpl* page_scale_layer = layer_tree_impl()->page_scale_layer();
+
+ DCHECK(this != page_scale_layer);
+ DCHECK(scrollbar_clip_layer);
+ DCHECK(this != layer_tree_impl()->InnerViewportScrollLayer()
+ || IsContainerForFixedPositionLayers());
+ gfx::RectF clip_rect(gfx::PointF(), scrollbar_clip_layer->bounds());
+
+ // See comment in MaxScrollOffset() regarding the use of the content layer
+ // bounds here.
+ gfx::RectF scroll_rect(gfx::PointF(), bounds());
+ if (scroll_rect.IsEmpty()) {
+ if (children().size())
+ scroll_rect = gfx::RectF(children()[0]->bounds());
+ }
+
+ if (scroll_rect.size().IsEmpty())
return;
- max_scroll_offset_ = max_scroll_offset;
- if (scroll_offset_delegate_)
- scroll_offset_delegate_->SetMaxScrollOffset(max_scroll_offset_);
+ // TODO(wjmaclean) This computation is nearly identical to the one in
+ // MaxScrollOffset. Find some way to combine these.
+ gfx::Vector2dF current_offset;
aelias_OOO_until_Jul13 2014/01/16 03:44:04 Likewise, I don't think any scaling is needed here
+ LayerImpl const* last_layer = 0;
+ for (LayerImpl const* current_layer = this;
+ current_layer != scrollbar_clip_layer->parent();
+ current_layer = current_layer->parent()) {
+ const gfx::Transform& layer_transform = current_layer->transform();
+ if (current_layer == page_scale_layer) {
+ DCHECK(layer_transform.IsIdentity());
+ float scale_factor = layer_tree_impl()->total_page_scale_factor();
+ current_offset.Scale(scale_factor);
+ scroll_rect.Scale(scale_factor);
+ } else {
+ gfx::Vector2dF new_offset =
+ current_layer->scroll_offset() + current_layer->ScrollDelta();
+ if (layer_transform.IsScale()) {
+ gfx::Vector2dF layer_scale = layer_transform.Scale();
+ DCHECK(layer_scale.x() == layer_scale.y());
+ new_offset.Scale(layer_scale.x(), layer_scale.y());
+ }
+ current_offset += new_offset;
+ }
+ last_layer = current_layer;
+ }
+ DCHECK(last_layer == scrollbar_clip_layer);
+
+ scrollbar_layer->SetVerticalAdjust(layer_tree_impl()->VerticalAdjust(this));
+ if (scrollbar_layer->orientation() == HORIZONTAL) {
+ float visible_ratio = clip_rect.width() / scroll_rect.width();
+ scrollbar_layer->SetCurrentPos(current_offset.x());
+ scrollbar_layer->SetMaximum(scroll_rect.width() - clip_rect.width());
+ scrollbar_layer->SetVisibleToTotalLengthRatio(visible_ratio);
+ } else {
+ float visible_ratio = clip_rect.height() / scroll_rect.height();
+ scrollbar_layer->SetCurrentPos(current_offset.y());
+ scrollbar_layer->SetMaximum(scroll_rect.height() - clip_rect.height());
+ scrollbar_layer->SetVisibleToTotalLengthRatio(visible_ratio);
+ }
- layer_tree_impl()->set_needs_update_draw_properties();
- UpdateScrollbarPositions();
+ // TODO(wjmaclean) The scrollbar animator for the pinch-zoom scrollbars should
+ // activate for every scroll on the main frame, not just the scrolls that move
+ // the pinch virtual viewport (i.e. trigger from either inner or outer
+ // viewport).
+ if (scrollbar_animation_controller_) {
+ bool should_animate = scrollbar_animation_controller_->DidScrollUpdate(
+ layer_tree_impl_->CurrentPhysicalTimeTicks());
+ if (should_animate)
+ layer_tree_impl_->StartScrollbarAnimation();
+ }
}
void LayerImpl::DidBecomeActive() {
@@ -1143,8 +1254,7 @@ void LayerImpl::DidBecomeActive() {
return;
}
- bool need_scrollbar_animation_controller = horizontal_scrollbar_layer_ ||
- vertical_scrollbar_layer_;
+ bool need_scrollbar_animation_controller = scrollable() && scrollbars_;
if (!need_scrollbar_animation_controller) {
scrollbar_animation_controller_.reset();
return;
@@ -1177,18 +1287,50 @@ void LayerImpl::DidBecomeActive() {
break;
}
}
-void LayerImpl::SetHorizontalScrollbarLayer(
- ScrollbarLayerImplBase* scrollbar_layer) {
- horizontal_scrollbar_layer_ = scrollbar_layer;
- if (horizontal_scrollbar_layer_)
- horizontal_scrollbar_layer_->set_scroll_layer_id(id());
+
+void LayerImpl::ClearScrollbars() {
+ if (!scrollbars_)
+ return;
+
+ scrollbars_.reset(NULL);
+}
+
+void LayerImpl::AddScrollbar(ScrollbarLayerImplBase* layer) {
+ DCHECK(layer);
+ if (!scrollbars_)
+ scrollbars_.reset(new ScrollbarSet());
+
+ scrollbars_->insert(layer);
+}
+
+void LayerImpl::RemoveScrollbar(ScrollbarLayerImplBase* layer) {
+ DCHECK(scrollbars_);
+ DCHECK(layer);
+
+ scrollbars_->erase(layer);
+ if (scrollbars_->empty())
+ scrollbars_.reset();
+}
+
+bool LayerImpl::HasScrollbar(ScrollbarOrientation orientation) const {
+ if (!scrollbars_)
+ return false;
+
+ for (ScrollbarSet::iterator it = scrollbars_->begin();
+ it != scrollbars_->end(); ++it)
+ if ((*it)->orientation() == orientation)
+ return true;
+
+ return false;
}
-void LayerImpl::SetVerticalScrollbarLayer(
- ScrollbarLayerImplBase* scrollbar_layer) {
- vertical_scrollbar_layer_ = scrollbar_layer;
- if (vertical_scrollbar_layer_)
- vertical_scrollbar_layer_->set_scroll_layer_id(id());
+void LayerImpl::ScrollbarParametersDidChange() {
+ if (!scrollbars_)
+ return;
+
+ for (ScrollbarSet::iterator it = scrollbars_->begin();
+ it != scrollbars_->end(); ++it)
+ (*it)->ScrollbarParametersDidChange();
}
void LayerImpl::AsValueInto(base::DictionaryValue* state) const {

Powered by Google App Engine
This is Rietveld 408576698