Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / cc / layers / layer_impl.cc
index beb282d..4d56d93 100644 (file)
 #include "cc/trees/layer_tree_settings.h"
 #include "cc/trees/proxy.h"
 #include "ui/gfx/box_f.h"
+#include "ui/gfx/geometry/vector2d_conversions.h"
 #include "ui/gfx/point_conversions.h"
 #include "ui/gfx/quad_f.h"
 #include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/size_conversions.h"
 
 namespace cc {
 LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, int id)
@@ -43,33 +45,32 @@ 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),
       user_scrollable_vertical_(true),
       stacking_order_changed_(false),
       double_sided_(true),
+      should_flatten_transform_(true),
       layer_property_changed_(false),
       masks_to_bounds_(false),
       contents_opaque_(false),
       is_root_for_isolated_group_(false),
-      preserves_3d_(false),
       use_parent_backface_visibility_(false),
       draw_checkerboard_for_missing_tiles_(false),
       draws_content_(false),
       hide_layer_and_subtree_(false),
       force_render_surface_(false),
       is_container_for_fixed_position_layers_(false),
+      is_3d_sorted_(false),
       background_color_(0),
       opacity_(1.0),
       blend_mode_(SkXfermode::kSrcOver_Mode),
       draw_depth_(0.f),
       needs_push_properties_(false),
       num_dependents_need_push_properties_(0),
-      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);
@@ -109,6 +110,10 @@ LayerImpl::~LayerImpl() {
 
   if (clip_parent_)
     clip_parent_->RemoveClipChild(this);
+
+  TRACE_EVENT_OBJECT_DELETED_WITH_ID(
+      TRACE_DISABLED_BY_DEFAULT("cc.debug"),
+      LayerTypeAsString(), this);
 }
 
 void LayerImpl::AddChild(scoped_ptr<LayerImpl> child) {
@@ -238,8 +243,8 @@ void LayerImpl::PassCopyRequests(ScopedPtrVector<CopyOutputRequest>* requests) {
 
 void LayerImpl::TakeCopyRequestsAndTransformToTarget(
     ScopedPtrVector<CopyOutputRequest>* requests) {
-  if (copy_requests_.empty())
-    return;
+  DCHECK(!copy_requests_.empty());
+  DCHECK(layer_tree_impl()->IsActiveTree());
 
   size_t first_inserted_request = requests->size();
   requests->insert_and_take(requests->end(), copy_requests_);
@@ -253,13 +258,11 @@ void LayerImpl::TakeCopyRequestsAndTransformToTarget(
     gfx::Rect request_in_layer_space = request->area();
     gfx::Rect request_in_content_space =
         LayerRectToContentRect(request_in_layer_space);
-    request->set_area(
-        MathUtil::MapClippedRect(draw_properties_.target_space_transform,
-                                 request_in_content_space));
+    request->set_area(MathUtil::MapEnclosingClippedRect(
+        draw_properties_.target_space_transform, request_in_content_space));
   }
 
-  if (layer_tree_impl()->IsActiveTree())
-    layer_tree_impl()->RemoveLayerWithCopyOutputRequest(this);
+  layer_tree_impl()->RemoveLayerWithCopyOutputRequest(this);
 }
 
 void LayerImpl::CreateRenderSurface() {
@@ -368,7 +371,7 @@ ResourceProvider::ResourceId LayerImpl::ContentsResourceId() const {
   return 0;
 }
 
-void LayerImpl::SetSentScrollDelta(gfx::Vector2d sent_scroll_delta) {
+void LayerImpl::SetSentScrollDelta(const gfx::Vector2d& sent_scroll_delta) {
   // Pending tree never has sent scroll deltas
   DCHECK(layer_tree_impl()->IsActiveTree());
 
@@ -381,7 +384,7 @@ void LayerImpl::SetSentScrollDelta(gfx::Vector2d sent_scroll_delta) {
 gfx::Vector2dF LayerImpl::ScrollBy(const 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);
@@ -389,9 +392,14 @@ gfx::Vector2dF LayerImpl::ScrollBy(const 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());
@@ -481,7 +489,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.");
@@ -545,22 +554,20 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) {
   layer->SetPosition(position_);
   layer->SetIsContainerForFixedPositionLayers(
       is_container_for_fixed_position_layers_);
-  layer->SetFixedContainerSizeDelta(fixed_container_size_delta_);
   layer->SetPositionConstraint(position_constraint_);
-  layer->SetPreserves3d(preserves_3d());
+  layer->SetShouldFlattenTransform(should_flatten_transform_);
+  layer->SetIs3dSorted(is_3d_sorted_);
   layer->SetUseParentBackfaceVisibility(use_parent_backface_visibility_);
-  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());
@@ -608,6 +615,30 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) {
   num_dependents_need_push_properties_ = 0;
 }
 
+gfx::Vector2dF LayerImpl::FixedContainerSizeDelta() const {
+  if (!scroll_clip_layer_)
+    return gfx::Vector2dF();
+
+  float scale_delta = layer_tree_impl()->page_scale_delta();
+  float scale = layer_tree_impl()->page_scale_factor();
+
+  gfx::Vector2dF delta_from_scroll = scroll_clip_layer_->BoundsDelta();
+  delta_from_scroll.Scale(1.f / scale);
+
+  // The delta-from-pinch component requires some explanation: A viewport of
+  // size (w,h) will appear to be size (w/s,h/s) under scale s in the content
+  // space. If s -> s' on the impl thread, where s' = s * ds, then the apparent
+  // viewport size change in the content space due to ds is:
+  //
+  // (w/s',h/s') - (w/s,h/s) = (w,h)(1/s' - 1/s) = (w,h)(1 - ds)/(s ds)
+  //
+  gfx::Vector2dF delta_from_pinch =
+      gfx::Rect(scroll_clip_layer_->bounds()).bottom_right() - gfx::PointF();
+  delta_from_pinch.Scale((1.f - scale_delta) / (scale * scale_delta));
+
+  return delta_from_scroll + delta_from_pinch;
+}
+
 base::DictionaryValue* LayerImpl::LayerTreeAsJson() const {
   base::DictionaryValue* result = new base::DictionaryValue;
   result->SetString("LayerType", LayerTypeAsString());
@@ -634,8 +665,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_);
@@ -751,12 +782,32 @@ bool LayerImpl::IsActive() const {
   return layer_tree_impl_->IsActiveTree();
 }
 
-void LayerImpl::SetBounds(gfx::Size bounds) {
+// TODO(wjmaclean) Convert so that bounds returns SizeF.
+gfx::Size LayerImpl::bounds() const {
+  return ToFlooredSize(temporary_impl_bounds_);
+}
+
+void LayerImpl::SetBounds(const gfx::Size& bounds) {
   if (bounds_ == bounds)
     return;
 
   bounds_ = bounds;
+  temporary_impl_bounds_ = bounds;
+
+  ScrollbarParametersDidChange();
+  if (masks_to_bounds())
+    NoteLayerPropertyChangedForSubtree();
+  else
+    NoteLayerPropertyChanged();
+}
+
+void LayerImpl::SetTemporaryImplBounds(const gfx::SizeF& bounds) {
+  if (temporary_impl_bounds_ == bounds)
+    return;
+
+  temporary_impl_bounds_ = bounds;
 
+  ScrollbarParametersDidChange();
   if (masks_to_bounds())
     NoteLayerPropertyChangedForSubtree();
   else
@@ -955,22 +1006,20 @@ void LayerImpl::SetPosition(const gfx::PointF& position) {
   NoteLayerPropertyChangedForSubtree();
 }
 
-void LayerImpl::SetPreserves3d(bool preserves3_d) {
-  if (preserves_3d_ == preserves3_d)
+void LayerImpl::SetShouldFlattenTransform(bool flatten) {
+  if (should_flatten_transform_ == flatten)
     return;
 
-  preserves_3d_ = preserves3_d;
+  should_flatten_transform_ = flatten;
   NoteLayerPropertyChangedForSubtree();
 }
 
-void LayerImpl::SetSublayerTransform(const gfx::Transform& sublayer_transform) {
-  if (sublayer_transform_ == sublayer_transform)
+void LayerImpl::SetIs3dSorted(bool sorted) {
+  if (is_3d_sorted_ == sorted)
     return;
 
-  sublayer_transform_ = sublayer_transform;
-  // Sublayer transform does not affect the current layer; it affects only its
-  // children.
-  NoteLayerPropertyChangedForDescendants();
+  is_3d_sorted_ = sorted;
+  NoteLayerPropertyChangedForSubtree();
 }
 
 void LayerImpl::SetTransform(const gfx::Transform& transform) {
@@ -996,7 +1045,7 @@ void LayerImpl::SetUpdateRect(const gfx::RectF& update_rect) {
   SetNeedsPushProperties();
 }
 
-void LayerImpl::SetContentBounds(gfx::Size content_bounds) {
+void LayerImpl::SetContentBounds(const gfx::Size& content_bounds) {
   if (this->content_bounds() == content_bounds)
     return;
 
@@ -1030,44 +1079,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.
@@ -1078,10 +1089,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 {
@@ -1089,14 +1098,16 @@ bool LayerImpl::IsExternalFlingActive() const {
          scroll_offset_delegate_->IsExternalFlingActive();
 }
 
-void LayerImpl::SetScrollOffset(gfx::Vector2d scroll_offset) {
+void LayerImpl::SetScrollOffset(const gfx::Vector2d& scroll_offset) {
   SetScrollOffsetAndDelta(scroll_offset, ScrollDelta());
 }
 
-void LayerImpl::SetScrollOffsetAndDelta(gfx::Vector2d scroll_offset,
+void LayerImpl::SetScrollOffsetAndDelta(const gfx::Vector2d& scroll_offset,
                                         const gfx::Vector2dF& scroll_delta) {
   bool changed = false;
 
+  last_scroll_offset_ = scroll_offset;
+
   if (scroll_offset_ != scroll_offset) {
     changed = true;
     scroll_offset_ = scroll_offset;
@@ -1131,7 +1142,7 @@ void LayerImpl::SetScrollOffsetAndDelta(gfx::Vector2d scroll_offset,
 
   if (changed) {
     NoteLayerPropertyChangedForSubtree();
-    UpdateScrollbarPositions();
+    ScrollbarParametersDidChange();
   }
 }
 
@@ -1167,17 +1178,148 @@ void LayerImpl::DidBeginTracing() {}
 
 void LayerImpl::ReleaseResources() {}
 
-void LayerImpl::SetMaxScrollOffset(gfx::Vector2d max_scroll_offset) {
-  if (max_scroll_offset_ == max_scroll_offset)
+gfx::Vector2d LayerImpl::MaxScrollOffset() const {
+  if (!scroll_clip_layer_ || bounds().IsEmpty())
+    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());
+
+  gfx::Size scaled_scroll_bounds(bounds());
+
+  float scale_factor = 1.f;
+  for (LayerImpl const* current_layer = this;
+       current_layer != scroll_clip_layer_;
+       current_layer = current_layer->parent()) {
+    DCHECK(current_layer);
+    float current_layer_scale = 1.f;
+
+    const gfx::Transform& layer_transform = current_layer->transform();
+    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?
+      DCHECK(layer_transform.IsScale2d());
+      gfx::Vector2dF layer_scale = layer_transform.Scale2d();
+      // 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;
+  }
+  // TODO(wjmaclean) Once we move to a model where the two-viewport model is
+  // turned on in all builds, remove the next two lines. For now however, the
+  // page scale layer may coincide with the clip layer, and so this is
+  // necessary.
+  if (page_scale_layer == scroll_clip_layer_)
+    scale_factor *= layer_tree_impl()->total_page_scale_factor();
+
+  scaled_scroll_bounds.SetSize(scale_factor * scaled_scroll_bounds.width(),
+                               scale_factor * scaled_scroll_bounds.height());
+
+  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);
+  max_offset.SetToMax(gfx::Vector2dF());
+  return gfx::ToFlooredVector2d(max_offset);
+}
+
+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.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;
+  for (LayerImpl const* current_layer = this;
+       current_layer != scrollbar_clip_layer;
+       current_layer = current_layer->parent()) {
+    DCHECK(current_layer);
+    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 {
+      DCHECK(layer_transform.IsScale2d());
+      gfx::Vector2dF layer_scale = layer_transform.Scale2d();
+      DCHECK(layer_scale.x() == layer_scale.y());
+      gfx::Vector2dF new_offset =
+          current_layer->scroll_offset() + current_layer->ScrollDelta();
+      new_offset.Scale(layer_scale.x(), layer_scale.y());
+      current_offset += new_offset;
+    }
+  }
+  // TODO(wjmaclean) Once we move to a model where the two-viewport model is
+  // turned on in all builds, remove the next two lines. For now however, the
+  // page scale layer may coincide with the clip layer, and so this is
+  // necessary.
+  if (page_scale_layer == scrollbar_clip_layer) {
+    scroll_rect.Scale(layer_tree_impl()->total_page_scale_factor());
+    current_offset.Scale(layer_tree_impl()->total_page_scale_factor());
+  }
+
+  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();
-  SetNeedsPushProperties();
+  // TODO(wjmaclean) Should the rest of this function be deleted?
+  // 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() {
@@ -1186,8 +1328,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;
@@ -1221,18 +1362,53 @@ void LayerImpl::DidBecomeActive() {
   }
 }
 
-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);
+  DCHECK(!scrollbars_ || scrollbars_->find(layer) == scrollbars_->end());
+  if (!scrollbars_)
+    scrollbars_.reset(new ScrollbarSet());
+
+  scrollbars_->insert(layer);
+}
+
+void LayerImpl::RemoveScrollbar(ScrollbarLayerImplBase* layer) {
+  DCHECK(scrollbars_);
+  DCHECK(layer);
+  DCHECK(scrollbars_->find(layer) != scrollbars_->end());
+
+  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::SetNeedsPushProperties() {
@@ -1261,7 +1437,9 @@ void LayerImpl::RemoveDependentNeedsPushProperties() {
 }
 
 void LayerImpl::AsValueInto(base::DictionaryValue* state) const {
-  TracedValue::MakeDictIntoImplicitSnapshot(state, LayerTypeAsString(), this);
+  TracedValue::MakeDictIntoImplicitSnapshotWithCategory(
+      TRACE_DISABLED_BY_DEFAULT("cc.debug"),
+      state, LayerTypeAsString(), this);
   state->SetInteger("layer_id", id());
   state->Set("bounds", MathUtil::AsValue(bounds()).release());
   state->SetInteger("draws_content", DrawsContent());
@@ -1344,5 +1522,4 @@ scoped_ptr<base::Value> LayerImpl::AsValue() const {
 void LayerImpl::RunMicroBenchmark(MicroBenchmarkImpl* benchmark) {
   benchmark->RunOnLayer(this);
 }
-
 }  // namespace cc