| Index: cc/trees/layer_tree_host_impl_unittest.cc
 | 
| diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
 | 
| index 59b19bb8fa3a8c0d053ff3c0a806c61ecaa66dce..af8d2db3892fd9f4aa4778fff785effcf3a8b0f8 100644
 | 
| --- a/cc/trees/layer_tree_host_impl_unittest.cc
 | 
| +++ b/cc/trees/layer_tree_host_impl_unittest.cc
 | 
| @@ -205,6 +205,9 @@ class LayerTreeHostImplTest : public testing::Test,
 | 
|  
 | 
|    LayerImpl* CreateScrollAndContentsLayers(LayerTreeImpl* layer_tree_impl,
 | 
|                                             const gfx::Size& content_size) {
 | 
| +    const int kInnerViewportScrollLayerId = 2;
 | 
| +    const int kInnerViewportClipLayerId = 4;
 | 
| +    const int kPageScaleLayerId = 5;
 | 
|      scoped_ptr<LayerImpl> root =
 | 
|          LayerImpl::Create(layer_tree_impl, 1);
 | 
|      root->SetBounds(content_size);
 | 
| @@ -213,16 +216,25 @@ class LayerTreeHostImplTest : public testing::Test,
 | 
|      root->SetAnchorPoint(gfx::PointF());
 | 
|  
 | 
|      scoped_ptr<LayerImpl> scroll =
 | 
| -        LayerImpl::Create(layer_tree_impl, 2);
 | 
| +        LayerImpl::Create(layer_tree_impl, kInnerViewportScrollLayerId);
 | 
|      LayerImpl* scroll_layer = scroll.get();
 | 
| -    scroll->SetScrollable(true);
 | 
| +    scroll->SetIsContainerForFixedPositionLayers(true);
 | 
|      scroll->SetScrollOffset(gfx::Vector2d());
 | 
| -    scroll->SetMaxScrollOffset(gfx::Vector2d(content_size.width(),
 | 
| -                                             content_size.height()));
 | 
| +
 | 
| +    scoped_ptr<LayerImpl> clip =
 | 
| +        LayerImpl::Create(layer_tree_impl, kInnerViewportClipLayerId);
 | 
| +    clip->SetBounds(
 | 
| +        gfx::Size(content_size.width() / 2, content_size.height() / 2));
 | 
| +
 | 
| +    scoped_ptr<LayerImpl> page_scale =
 | 
| +        LayerImpl::Create(layer_tree_impl, kPageScaleLayerId);
 | 
| +
 | 
| +    scroll->SetScrollClipLayer(clip->id());
 | 
|      scroll->SetBounds(content_size);
 | 
|      scroll->SetContentBounds(content_size);
 | 
|      scroll->SetPosition(gfx::PointF());
 | 
|      scroll->SetAnchorPoint(gfx::PointF());
 | 
| +    scroll->SetIsContainerForFixedPositionLayers(true);
 | 
|  
 | 
|      scoped_ptr<LayerImpl> contents =
 | 
|          LayerImpl::Create(layer_tree_impl, 3);
 | 
| @@ -233,9 +245,14 @@ class LayerTreeHostImplTest : public testing::Test,
 | 
|      contents->SetAnchorPoint(gfx::PointF());
 | 
|  
 | 
|      scroll->AddChild(contents.Pass());
 | 
| -    root->AddChild(scroll.Pass());
 | 
| +    page_scale->AddChild(scroll.Pass());
 | 
| +    clip->AddChild(page_scale.Pass());
 | 
| +    root->AddChild(clip.Pass());
 | 
|  
 | 
|      layer_tree_impl->SetRootLayer(root.Pass());
 | 
| +    layer_tree_impl->SetViewportLayersFromIds(
 | 
| +        kPageScaleLayerId, kInnerViewportScrollLayerId, Layer::INVALID_ID);
 | 
| +
 | 
|      return scroll_layer;
 | 
|    }
 | 
|  
 | 
| @@ -246,15 +263,19 @@ class LayerTreeHostImplTest : public testing::Test,
 | 
|      return scroll_layer;
 | 
|    }
 | 
|  
 | 
| -  scoped_ptr<LayerImpl> CreateScrollableLayer(int id, const gfx::Size& size) {
 | 
| +  // TODO(wjmaclean) Add clip-layer pointer to parameters.
 | 
| +  scoped_ptr<LayerImpl> CreateScrollableLayer(int id,
 | 
| +                                              const gfx::Size& size,
 | 
| +                                              LayerImpl* clip_layer) {
 | 
| +    DCHECK(clip_layer);
 | 
| +    DCHECK(id != clip_layer->id());
 | 
|      scoped_ptr<LayerImpl> layer =
 | 
|          LayerImpl::Create(host_impl_->active_tree(), id);
 | 
| -    layer->SetScrollable(true);
 | 
| +    layer->SetScrollClipLayer(clip_layer->id());
 | 
|      layer->SetDrawsContent(true);
 | 
|      layer->SetBounds(size);
 | 
|      layer->SetContentBounds(size);
 | 
| -    layer->SetMaxScrollOffset(gfx::Vector2d(size.width() * 2,
 | 
| -                                            size.height() * 2));
 | 
| +    clip_layer->SetBounds(gfx::Size(size.width() / 2, size.height() / 2));
 | 
|      return layer.Pass();
 | 
|    }
 | 
|  
 | 
| @@ -423,15 +444,20 @@ TEST_F(LayerTreeHostImplTest, ScrollDeltaRepeatedScrolls) {
 | 
|    gfx::Vector2d scroll_offset(20, 30);
 | 
|    gfx::Vector2d scroll_delta(11, -15);
 | 
|    {
 | 
| +    scoped_ptr<LayerImpl> root_clip =
 | 
| +        LayerImpl::Create(host_impl_->active_tree(), 2);
 | 
|      scoped_ptr<LayerImpl> root =
 | 
|          LayerImpl::Create(host_impl_->active_tree(), 1);
 | 
| -    root->SetMaxScrollOffset(gfx::Vector2d(100, 100));
 | 
| -    root->SetScrollOffset(scroll_offset);
 | 
| -    root->SetScrollable(true);
 | 
| -    root->ScrollBy(scroll_delta);
 | 
| -    host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
| +    root_clip->SetBounds(gfx::Size(10, 10));
 | 
| +    LayerImpl* root_layer = root.get();
 | 
| +    root_clip->AddChild(root.Pass());
 | 
| +    root_layer->SetBounds(gfx::Size(110, 110));
 | 
| +    root_layer->SetScrollClipLayer(root_clip->id());
 | 
| +    root_layer->SetScrollOffset(scroll_offset);
 | 
| +    root_layer->ScrollBy(scroll_delta);
 | 
| +    host_impl_->active_tree()->SetRootLayer(root_clip.Pass());
 | 
|    }
 | 
| -  LayerImpl* root = host_impl_->active_tree()->root_layer();
 | 
| +  LayerImpl* root = host_impl_->active_tree()->root_layer()->children()[0];
 | 
|  
 | 
|    scoped_ptr<ScrollAndScaleSet> scroll_info;
 | 
|  
 | 
| @@ -722,13 +748,14 @@ TEST_F(LayerTreeHostImplTest, ScrollVerticallyByPageReturnsCorrectValue) {
 | 
|            20,
 | 
|            VERTICAL));
 | 
|    vertical_scrollbar->SetBounds(gfx::Size(15, 1000));
 | 
| -  host_impl_->RootScrollLayer()->SetVerticalScrollbarLayer(
 | 
| +  host_impl_->InnerViewportScrollLayer()->AddScrollbar(
 | 
|        vertical_scrollbar.get());
 | 
|  
 | 
|    // Trying to scroll with a vertical scrollbar will succeed.
 | 
|    EXPECT_TRUE(host_impl_->ScrollVerticallyByPage(
 | 
|        gfx::Point(), SCROLL_FORWARD));
 | 
| -  EXPECT_FLOAT_EQ(875.f, host_impl_->RootScrollLayer()->ScrollDelta().y());
 | 
| +  EXPECT_FLOAT_EQ(875.f,
 | 
| +                  host_impl_->InnerViewportScrollLayer()->ScrollDelta().y());
 | 
|    EXPECT_TRUE(host_impl_->ScrollVerticallyByPage(
 | 
|        gfx::Point(), SCROLL_BACKWARD));
 | 
|  }
 | 
| @@ -744,9 +771,7 @@ TEST_F(LayerTreeHostImplTest, DISABLED_ScrollWithUserUnscrollableLayers) {
 | 
|    LayerImpl* overflow = scroll_layer->children()[0];
 | 
|    overflow->SetBounds(overflow_size);
 | 
|    overflow->SetContentBounds(overflow_size);
 | 
| -  overflow->SetScrollable(true);
 | 
| -  overflow->SetMaxScrollOffset(gfx::Vector2d(overflow_size.width(),
 | 
| -                                             overflow_size.height()));
 | 
| +  overflow->SetScrollClipLayer(scroll_layer->parent()->id());
 | 
|    overflow->SetScrollOffset(gfx::Vector2d());
 | 
|    overflow->SetPosition(gfx::PointF());
 | 
|    overflow->SetAnchorPoint(gfx::PointF());
 | 
| @@ -809,7 +834,7 @@ TEST_F(LayerTreeHostImplTest, ImplPinchZoom) {
 | 
|    host_impl_->SetViewportSize(gfx::Size(50, 50));
 | 
|    DrawFrame();
 | 
|  
 | 
| -  EXPECT_EQ(scroll_layer, host_impl_->RootScrollLayer());
 | 
| +  EXPECT_EQ(scroll_layer, host_impl_->InnerViewportScrollLayer());
 | 
|  
 | 
|    float min_page_scale = 1.f, max_page_scale = 4.f;
 | 
|  
 | 
| @@ -823,6 +848,7 @@ TEST_F(LayerTreeHostImplTest, ImplPinchZoom) {
 | 
|  
 | 
|      float page_scale_delta = 2.f;
 | 
|      host_impl_->ScrollBegin(gfx::Point(50, 50), InputHandler::Gesture);
 | 
| +    host_impl_->PinchGestureBegin();
 | 
|      host_impl_->PinchGestureUpdate(page_scale_delta, gfx::Point(50, 50));
 | 
|      host_impl_->PinchGestureEnd();
 | 
|      host_impl_->ScrollEnd();
 | 
| @@ -834,7 +860,7 @@ TEST_F(LayerTreeHostImplTest, ImplPinchZoom) {
 | 
|      EXPECT_EQ(scroll_info->page_scale_delta, page_scale_delta);
 | 
|  
 | 
|      EXPECT_EQ(gfx::Vector2d(75, 75).ToString(),
 | 
| -              scroll_layer->max_scroll_offset().ToString());
 | 
| +              scroll_layer->MaxScrollOffset().ToString());
 | 
|    }
 | 
|  
 | 
|    // Scrolling after a pinch gesture should always be in local space.  The
 | 
| @@ -873,7 +899,7 @@ TEST_F(LayerTreeHostImplTest, PinchGesture) {
 | 
|    host_impl_->SetViewportSize(gfx::Size(50, 50));
 | 
|    DrawFrame();
 | 
|  
 | 
| -  LayerImpl* scroll_layer = host_impl_->RootScrollLayer();
 | 
| +  LayerImpl* scroll_layer = host_impl_->InnerViewportScrollLayer();
 | 
|    DCHECK(scroll_layer);
 | 
|  
 | 
|    float min_page_scale = 1.f;
 | 
| @@ -993,7 +1019,6 @@ TEST_F(LayerTreeHostImplTest, PinchGesture) {
 | 
|                                                             4.f);
 | 
|      scroll_layer->SetScrollDelta(gfx::Vector2d());
 | 
|      scroll_layer->SetScrollOffset(gfx::Vector2d(0, 0));
 | 
| -    host_impl_->active_tree()->UpdateMaxScrollOffset();
 | 
|  
 | 
|      host_impl_->ScrollBegin(gfx::Point(0, 0), InputHandler::Gesture);
 | 
|      host_impl_->PinchGestureBegin();
 | 
| @@ -1016,7 +1041,7 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimation) {
 | 
|    host_impl_->SetViewportSize(gfx::Size(50, 50));
 | 
|    DrawFrame();
 | 
|  
 | 
| -  LayerImpl* scroll_layer = host_impl_->RootScrollLayer();
 | 
| +  LayerImpl* scroll_layer = host_impl_->InnerViewportScrollLayer();
 | 
|    DCHECK(scroll_layer);
 | 
|  
 | 
|    float min_page_scale = 0.5f;
 | 
| @@ -1086,7 +1111,7 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimationNoOp) {
 | 
|    host_impl_->SetViewportSize(gfx::Size(50, 50));
 | 
|    DrawFrame();
 | 
|  
 | 
| -  LayerImpl* scroll_layer = host_impl_->RootScrollLayer();
 | 
| +  LayerImpl* scroll_layer = host_impl_->InnerViewportScrollLayer();
 | 
|    DCHECK(scroll_layer);
 | 
|  
 | 
|    float min_page_scale = 0.5f;
 | 
| @@ -1166,12 +1191,12 @@ TEST_F(LayerTreeHostImplTest, ScrollbarLinearFadeScheduling) {
 | 
|  
 | 
|    scoped_ptr<LayerImpl> scroll =
 | 
|        LayerImpl::Create(host_impl_->active_tree(), 2);
 | 
| -  scroll->SetScrollable(true);
 | 
| +  scroll->SetScrollClipLayer(root->id());
 | 
|    scroll->SetScrollOffset(gfx::Vector2d());
 | 
| -  scroll->SetMaxScrollOffset(gfx::Vector2d(content_size.width(),
 | 
| -                                           content_size.height()));
 | 
| +  root->SetBounds(viewport_size);
 | 
|    scroll->SetBounds(content_size);
 | 
|    scroll->SetContentBounds(content_size);
 | 
| +  scroll->SetIsContainerForFixedPositionLayers(true);
 | 
|  
 | 
|    scoped_ptr<LayerImpl> contents =
 | 
|        LayerImpl::Create(host_impl_->active_tree(), 3);
 | 
| @@ -1181,13 +1206,15 @@ TEST_F(LayerTreeHostImplTest, ScrollbarLinearFadeScheduling) {
 | 
|  
 | 
|    scoped_ptr<PaintedScrollbarLayerImpl> scrollbar =
 | 
|        PaintedScrollbarLayerImpl::Create(host_impl_->active_tree(), 4, VERTICAL);
 | 
| -  scroll->SetVerticalScrollbarLayer(scrollbar.get());
 | 
| +  scrollbar->SetScrollLayerById(2);
 | 
| +  scrollbar->SetClipLayerById(1);
 | 
|  
 | 
|    scroll->AddChild(contents.Pass());
 | 
|    root->AddChild(scroll.Pass());
 | 
|    root->AddChild(scrollbar.PassAs<LayerImpl>());
 | 
|  
 | 
|    host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
| +  host_impl_->active_tree()->SetViewportLayersFromIds(1, 2, Layer::INVALID_ID);
 | 
|    host_impl_->active_tree()->DidBecomeActive();
 | 
|    DrawFrame();
 | 
|  
 | 
| @@ -1238,7 +1265,7 @@ TEST_F(LayerTreeHostImplTest, ScrollbarLinearFadeScheduling) {
 | 
|  
 | 
|    // Setting the scroll offset outside a scroll should also cause the scrollbar
 | 
|    // to appear and to schedule a fade.
 | 
| -  host_impl_->RootScrollLayer()->SetScrollOffset(gfx::Vector2d(5, 5));
 | 
| +  host_impl_->InnerViewportScrollLayer()->SetScrollOffset(gfx::Vector2d(5, 5));
 | 
|    host_impl_->StartScrollbarAnimation();
 | 
|    EXPECT_LT(base::TimeDelta::FromMilliseconds(19),
 | 
|              requested_scrollbar_animation_delay_);
 | 
| @@ -1272,12 +1299,11 @@ void LayerTreeHostImplTest::SetupMouseMoveAtWithDeviceScale(
 | 
|  
 | 
|    scoped_ptr<LayerImpl> scroll =
 | 
|        LayerImpl::Create(host_impl_->active_tree(), 2);
 | 
| -  scroll->SetScrollable(true);
 | 
| +  scroll->SetScrollClipLayer(root->id());
 | 
|    scroll->SetScrollOffset(gfx::Vector2d());
 | 
| -  scroll->SetMaxScrollOffset(gfx::Vector2d(content_size.width(),
 | 
| -                                           content_size.height()));
 | 
|    scroll->SetBounds(content_size);
 | 
|    scroll->SetContentBounds(content_size);
 | 
| +  scroll->SetIsContainerForFixedPositionLayers(true);
 | 
|  
 | 
|    scoped_ptr<LayerImpl> contents =
 | 
|        LayerImpl::Create(host_impl_->active_tree(), 3);
 | 
| @@ -1292,17 +1318,19 @@ void LayerTreeHostImplTest::SetupMouseMoveAtWithDeviceScale(
 | 
|    scrollbar->SetBounds(gfx::Size(15, viewport_size.height()));
 | 
|    scrollbar->SetContentBounds(gfx::Size(15, viewport_size.height()));
 | 
|    scrollbar->SetPosition(gfx::Point(285, 0));
 | 
| -  scroll->SetVerticalScrollbarLayer(scrollbar.get());
 | 
| +  scrollbar->SetScrollLayerById(2);
 | 
|  
 | 
|    scroll->AddChild(contents.Pass());
 | 
|    root->AddChild(scroll.Pass());
 | 
|    root->AddChild(scrollbar.PassAs<LayerImpl>());
 | 
|  
 | 
|    host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
| +  host_impl_->active_tree()->SetViewportLayersFromIds(1, 2, Layer::INVALID_ID);
 | 
|    host_impl_->active_tree()->DidBecomeActive();
 | 
|    DrawFrame();
 | 
|  
 | 
| -  LayerImpl* root_scroll = host_impl_->active_tree()->RootScrollLayer();
 | 
| +  LayerImpl* root_scroll =
 | 
| +      host_impl_->active_tree()->InnerViewportScrollLayer();
 | 
|    ASSERT_TRUE(root_scroll->scrollbar_animation_controller());
 | 
|    ScrollbarAnimationControllerThinning* scrollbar_animation_controller =
 | 
|        static_cast<ScrollbarAnimationControllerThinning*>(
 | 
| @@ -1759,7 +1787,7 @@ TEST_F(LayerTreeHostImplTest, PrepareToDrawFailsWhenAnimationUsesCheckerboard) {
 | 
|  
 | 
|  TEST_F(LayerTreeHostImplTest, ScrollRootIgnored) {
 | 
|    scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
 | 
| -  root->SetScrollable(false);
 | 
| +  root->SetScrollClipLayer(Layer::INVALID_ID);
 | 
|    host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
|    DrawFrame();
 | 
|  
 | 
| @@ -1777,18 +1805,23 @@ TEST_F(LayerTreeHostImplTest, ScrollNonScrollableRootWithTopControls) {
 | 
|  
 | 
|    CreateHostImpl(settings, CreateOutputSurface());
 | 
|  
 | 
| -  gfx::Size layer_size(5, 5);
 | 
| +  gfx::Size layer_size(10, 10);
 | 
| +  gfx::Size clip_size(5, 5);
 | 
|    scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
 | 
| -  root->SetScrollable(true);
 | 
| -  root->SetMaxScrollOffset(gfx::Vector2d(layer_size.width(),
 | 
| -                                         layer_size.height()));
 | 
| +  scoped_ptr<LayerImpl> root_clip =
 | 
| +      LayerImpl::Create(host_impl_->active_tree(), 2);
 | 
| +  root_clip->SetBounds(clip_size);
 | 
| +  root->SetScrollClipLayer(root_clip->id());
 | 
|    root->SetBounds(layer_size);
 | 
|    root->SetContentBounds(layer_size);
 | 
|    root->SetPosition(gfx::PointF());
 | 
|    root->SetAnchorPoint(gfx::PointF());
 | 
|    root->SetDrawsContent(false);
 | 
| -  host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
| -  host_impl_->active_tree()->FindRootScrollLayer();
 | 
| +  int root_id = root->id();
 | 
| +  root_clip->AddChild(root.Pass());
 | 
| +  host_impl_->active_tree()->SetRootLayer(root_clip.Pass());
 | 
| +  host_impl_->active_tree()->SetViewportLayersFromIds(
 | 
| +      root_id, root_id, Layer::INVALID_ID);
 | 
|    DrawFrame();
 | 
|  
 | 
|    EXPECT_EQ(InputHandler::ScrollStarted,
 | 
| @@ -1810,29 +1843,32 @@ TEST_F(LayerTreeHostImplTest, ScrollNonCompositedRoot) {
 | 
|    // Test the configuration where a non-composited root layer is embedded in a
 | 
|    // scrollable outer layer.
 | 
|    gfx::Size surface_size(10, 10);
 | 
| +  gfx::Size contents_size(20, 20);
 | 
|  
 | 
|    scoped_ptr<LayerImpl> content_layer =
 | 
|        LayerImpl::Create(host_impl_->active_tree(), 1);
 | 
|    content_layer->SetDrawsContent(true);
 | 
|    content_layer->SetPosition(gfx::PointF());
 | 
|    content_layer->SetAnchorPoint(gfx::PointF());
 | 
| -  content_layer->SetBounds(surface_size);
 | 
| -  content_layer->SetContentBounds(gfx::Size(surface_size.width() * 2,
 | 
| -                                            surface_size.height() * 2));
 | 
| +  content_layer->SetBounds(contents_size);
 | 
| +  content_layer->SetContentBounds(contents_size);
 | 
|    content_layer->SetContentsScale(2.f, 2.f);
 | 
|  
 | 
| +  scoped_ptr<LayerImpl> scroll_clip_layer =
 | 
| +      LayerImpl::Create(host_impl_->active_tree(), 3);
 | 
| +  scroll_clip_layer->SetBounds(surface_size);
 | 
| +
 | 
|    scoped_ptr<LayerImpl> scroll_layer =
 | 
|        LayerImpl::Create(host_impl_->active_tree(), 2);
 | 
| -  scroll_layer->SetScrollable(true);
 | 
| -  scroll_layer->SetMaxScrollOffset(gfx::Vector2d(surface_size.width(),
 | 
| -                                                 surface_size.height()));
 | 
| -  scroll_layer->SetBounds(surface_size);
 | 
| -  scroll_layer->SetContentBounds(surface_size);
 | 
| +  scroll_layer->SetScrollClipLayer(3);
 | 
| +  scroll_layer->SetBounds(contents_size);
 | 
| +  scroll_layer->SetContentBounds(contents_size);
 | 
|    scroll_layer->SetPosition(gfx::PointF());
 | 
|    scroll_layer->SetAnchorPoint(gfx::PointF());
 | 
|    scroll_layer->AddChild(content_layer.Pass());
 | 
| +  scroll_clip_layer->AddChild(scroll_layer.Pass());
 | 
|  
 | 
| -  host_impl_->active_tree()->SetRootLayer(scroll_layer.Pass());
 | 
| +  host_impl_->active_tree()->SetRootLayer(scroll_clip_layer.Pass());
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
|    DrawFrame();
 | 
|  
 | 
| @@ -1847,10 +1883,11 @@ TEST_F(LayerTreeHostImplTest, ScrollNonCompositedRoot) {
 | 
|  
 | 
|  TEST_F(LayerTreeHostImplTest, ScrollChildCallsCommitAndRedraw) {
 | 
|    gfx::Size surface_size(10, 10);
 | 
| +  gfx::Size contents_size(20, 20);
 | 
|    scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
 | 
|    root->SetBounds(surface_size);
 | 
| -  root->SetContentBounds(surface_size);
 | 
| -  root->AddChild(CreateScrollableLayer(2, surface_size));
 | 
| +  root->SetContentBounds(contents_size);
 | 
| +  root->AddChild(CreateScrollableLayer(2, contents_size, root.get()));
 | 
|    host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
|    DrawFrame();
 | 
| @@ -1867,7 +1904,7 @@ TEST_F(LayerTreeHostImplTest, ScrollChildCallsCommitAndRedraw) {
 | 
|  TEST_F(LayerTreeHostImplTest, ScrollMissesChild) {
 | 
|    gfx::Size surface_size(10, 10);
 | 
|    scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
 | 
| -  root->AddChild(CreateScrollableLayer(2, surface_size));
 | 
| +  root->AddChild(CreateScrollableLayer(2, surface_size, root.get()));
 | 
|    host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
|    DrawFrame();
 | 
| @@ -1884,7 +1921,8 @@ TEST_F(LayerTreeHostImplTest, ScrollMissesChild) {
 | 
|  TEST_F(LayerTreeHostImplTest, ScrollMissesBackfacingChild) {
 | 
|    gfx::Size surface_size(10, 10);
 | 
|    scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
 | 
| -  scoped_ptr<LayerImpl> child = CreateScrollableLayer(2, surface_size);
 | 
| +  scoped_ptr<LayerImpl> child =
 | 
| +      CreateScrollableLayer(2, surface_size, root.get());
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
|  
 | 
|    gfx::Transform matrix;
 | 
| @@ -1907,14 +1945,21 @@ TEST_F(LayerTreeHostImplTest, ScrollMissesBackfacingChild) {
 | 
|  
 | 
|  TEST_F(LayerTreeHostImplTest, ScrollBlockedByContentLayer) {
 | 
|    gfx::Size surface_size(10, 10);
 | 
| -  scoped_ptr<LayerImpl> content_layer = CreateScrollableLayer(1, surface_size);
 | 
| +  scoped_ptr<LayerImpl> clip_layer =
 | 
| +      LayerImpl::Create(host_impl_->active_tree(), 3);
 | 
| +  scoped_ptr<LayerImpl> content_layer =
 | 
| +      CreateScrollableLayer(1, surface_size, clip_layer.get());
 | 
|    content_layer->SetShouldScrollOnMainThread(true);
 | 
| -  content_layer->SetScrollable(false);
 | 
| +  content_layer->SetScrollClipLayer(Layer::INVALID_ID);
 | 
|  
 | 
| -  scoped_ptr<LayerImpl> scroll_layer = CreateScrollableLayer(2, surface_size);
 | 
| +  // Note: we can use the same clip layer for both since both calls to
 | 
| +  // CreateScrollableLayer() use the same surface size.
 | 
| +  scoped_ptr<LayerImpl> scroll_layer =
 | 
| +      CreateScrollableLayer(2, surface_size, clip_layer.get());
 | 
|    scroll_layer->AddChild(content_layer.Pass());
 | 
| +  clip_layer->AddChild(scroll_layer.Pass());
 | 
|  
 | 
| -  host_impl_->active_tree()->SetRootLayer(scroll_layer.Pass());
 | 
| +  host_impl_->active_tree()->SetRootLayer(clip_layer.Pass());
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
|    DrawFrame();
 | 
|  
 | 
| @@ -1926,21 +1971,30 @@ TEST_F(LayerTreeHostImplTest, ScrollBlockedByContentLayer) {
 | 
|  }
 | 
|  
 | 
|  TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnMainThread) {
 | 
| -  gfx::Size surface_size(10, 10);
 | 
| +  gfx::Size surface_size(20, 20);
 | 
|    float page_scale = 2.f;
 | 
|    scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
 | 
| -  scoped_ptr<LayerImpl> root_scrolling = CreateScrollableLayer(2, surface_size);
 | 
| -  root->AddChild(root_scrolling.Pass());
 | 
| +  scoped_ptr<LayerImpl> root_clip =
 | 
| +      LayerImpl::Create(host_impl_->active_tree(), 2);
 | 
| +  scoped_ptr<LayerImpl> root_scrolling =
 | 
| +      CreateScrollableLayer(3, surface_size, root_clip.get());
 | 
| +  root_scrolling->SetIsContainerForFixedPositionLayers(true);
 | 
| +  root_clip->AddChild(root_scrolling.Pass());
 | 
| +  root->AddChild(root_clip.Pass());
 | 
|    host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
| +  // The behaviour in this test assumes the page scale is applied at a layer
 | 
| +  // above the clip layer.
 | 
| +  host_impl_->active_tree()->SetViewportLayersFromIds(1, 3, Layer::INVALID_ID);
 | 
|    host_impl_->active_tree()->DidBecomeActive();
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
|    DrawFrame();
 | 
|  
 | 
| -  LayerImpl* root_scroll = host_impl_->active_tree()->RootScrollLayer();
 | 
| +  LayerImpl* root_scroll =
 | 
| +      host_impl_->active_tree()->InnerViewportScrollLayer();
 | 
|  
 | 
|    gfx::Vector2d scroll_delta(0, 10);
 | 
|    gfx::Vector2d expected_scroll_delta = scroll_delta;
 | 
| -  gfx::Vector2d expected_max_scroll = root_scroll->max_scroll_offset();
 | 
| +  gfx::Vector2d expected_max_scroll = root_scroll->MaxScrollOffset();
 | 
|    EXPECT_EQ(InputHandler::ScrollStarted,
 | 
|              host_impl_->ScrollBegin(gfx::Point(5, 5),
 | 
|                                      InputHandler::Wheel));
 | 
| @@ -1956,7 +2010,7 @@ TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnMainThread) {
 | 
|    ExpectContains(*scroll_info.get(), root_scroll->id(), expected_scroll_delta);
 | 
|  
 | 
|    // The scroll range should also have been updated.
 | 
| -  EXPECT_EQ(expected_max_scroll, root_scroll->max_scroll_offset());
 | 
| +  EXPECT_EQ(expected_max_scroll, root_scroll->MaxScrollOffset());
 | 
|  
 | 
|    // The page scale delta remains constant because the impl thread did not
 | 
|    // scale.
 | 
| @@ -1964,22 +2018,31 @@ TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnMainThread) {
 | 
|  }
 | 
|  
 | 
|  TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnImplThread) {
 | 
| -  gfx::Size surface_size(10, 10);
 | 
| +  gfx::Size surface_size(20, 20);
 | 
|    float page_scale = 2.f;
 | 
|    scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
 | 
| -  scoped_ptr<LayerImpl> root_scrolling = CreateScrollableLayer(2, surface_size);
 | 
| -  root->AddChild(root_scrolling.Pass());
 | 
| +  scoped_ptr<LayerImpl> root_clip =
 | 
| +      LayerImpl::Create(host_impl_->active_tree(), 2);
 | 
| +  scoped_ptr<LayerImpl> root_scrolling =
 | 
| +      CreateScrollableLayer(3, surface_size, root_clip.get());
 | 
| +  root_scrolling->SetIsContainerForFixedPositionLayers(true);
 | 
| +  root_clip->AddChild(root_scrolling.Pass());
 | 
| +  root->AddChild(root_clip.Pass());
 | 
|    host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
| +  // The behaviour in this test assumes the page scale is applied at a layer
 | 
| +  // above the clip layer.
 | 
| +  host_impl_->active_tree()->SetViewportLayersFromIds(1, 3, Layer::INVALID_ID);
 | 
|    host_impl_->active_tree()->DidBecomeActive();
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
|    host_impl_->active_tree()->SetPageScaleFactorAndLimits(1.f, 1.f, page_scale);
 | 
|    DrawFrame();
 | 
|  
 | 
| -  LayerImpl* root_scroll = host_impl_->active_tree()->RootScrollLayer();
 | 
| +  LayerImpl* root_scroll =
 | 
| +      host_impl_->active_tree()->InnerViewportScrollLayer();
 | 
|  
 | 
|    gfx::Vector2d scroll_delta(0, 10);
 | 
|    gfx::Vector2d expected_scroll_delta = scroll_delta;
 | 
| -  gfx::Vector2d expected_max_scroll = root_scroll->max_scroll_offset();
 | 
| +  gfx::Vector2d expected_max_scroll = root_scroll->MaxScrollOffset();
 | 
|    EXPECT_EQ(InputHandler::ScrollStarted,
 | 
|              host_impl_->ScrollBegin(gfx::Point(5, 5),
 | 
|                                      InputHandler::Wheel));
 | 
| @@ -1999,7 +2062,7 @@ TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnImplThread) {
 | 
|    ExpectContains(*scroll_info.get(), root_scroll->id(), expected_scroll_delta);
 | 
|  
 | 
|    // The scroll range should also have been updated.
 | 
| -  EXPECT_EQ(expected_max_scroll, root_scroll->max_scroll_offset());
 | 
| +  EXPECT_EQ(expected_max_scroll, root_scroll->MaxScrollOffset());
 | 
|  
 | 
|    // The page scale delta should match the new scale on the impl side.
 | 
|    EXPECT_EQ(page_scale, host_impl_->active_tree()->total_page_scale_factor());
 | 
| @@ -2020,9 +2083,12 @@ TEST_F(LayerTreeHostImplTest, PageScaleDeltaAppliedToRootScrollLayerOnly) {
 | 
|    LayerImpl* root = host_impl_->active_tree()->root_layer();
 | 
|    LayerImpl* child = scroll->children()[0];
 | 
|  
 | 
| +  scoped_ptr<LayerImpl> scrollable_child_clip =
 | 
| +      LayerImpl::Create(host_impl_->active_tree(), 6);
 | 
|    scoped_ptr<LayerImpl> scrollable_child =
 | 
| -      CreateScrollableLayer(4, surface_size);
 | 
| -  child->AddChild(scrollable_child.Pass());
 | 
| +      CreateScrollableLayer(7, surface_size, scrollable_child_clip.get());
 | 
| +  scrollable_child_clip->AddChild(scrollable_child.Pass());
 | 
| +  child->AddChild(scrollable_child_clip.Pass());
 | 
|    LayerImpl* grand_child = child->children()[0];
 | 
|  
 | 
|    // Set new page scale on impl thread by pinching.
 | 
| @@ -2063,27 +2129,31 @@ TEST_F(LayerTreeHostImplTest, PageScaleDeltaAppliedToRootScrollLayerOnly) {
 | 
|  }
 | 
|  
 | 
|  TEST_F(LayerTreeHostImplTest, ScrollChildAndChangePageScaleOnMainThread) {
 | 
| -  gfx::Size surface_size(10, 10);
 | 
| +  gfx::Size surface_size(30, 30);
 | 
|    scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
 | 
| +  root->SetBounds(gfx::Size(5, 5));
 | 
|    scoped_ptr<LayerImpl> root_scrolling =
 | 
|        LayerImpl::Create(host_impl_->active_tree(), 2);
 | 
|    root_scrolling->SetBounds(surface_size);
 | 
|    root_scrolling->SetContentBounds(surface_size);
 | 
| -  root_scrolling->SetScrollable(true);
 | 
| +  root_scrolling->SetScrollClipLayer(root->id());
 | 
| +  root_scrolling->SetIsContainerForFixedPositionLayers(true);
 | 
| +  LayerImpl* root_scrolling_ptr = root_scrolling.get();
 | 
|    root->AddChild(root_scrolling.Pass());
 | 
|    int child_scroll_layer_id = 3;
 | 
| -  scoped_ptr<LayerImpl> child_scrolling =
 | 
| -      CreateScrollableLayer(child_scroll_layer_id, surface_size);
 | 
| +  scoped_ptr<LayerImpl> child_scrolling = CreateScrollableLayer(
 | 
| +      child_scroll_layer_id, surface_size, root_scrolling_ptr);
 | 
|    LayerImpl* child = child_scrolling.get();
 | 
| -  root->AddChild(child_scrolling.Pass());
 | 
| +  root_scrolling_ptr->AddChild(child_scrolling.Pass());
 | 
|    host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
| +  host_impl_->active_tree()->SetViewportLayersFromIds(1, 2, Layer::INVALID_ID);
 | 
|    host_impl_->active_tree()->DidBecomeActive();
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
|    DrawFrame();
 | 
|  
 | 
|    gfx::Vector2d scroll_delta(0, 10);
 | 
|    gfx::Vector2d expected_scroll_delta(scroll_delta);
 | 
| -  gfx::Vector2d expected_max_scroll(child->max_scroll_offset());
 | 
| +  gfx::Vector2d expected_max_scroll(child->MaxScrollOffset());
 | 
|    EXPECT_EQ(InputHandler::ScrollStarted,
 | 
|              host_impl_->ScrollBegin(gfx::Point(5, 5),
 | 
|                                      InputHandler::Wheel));
 | 
| @@ -2102,7 +2172,7 @@ TEST_F(LayerTreeHostImplTest, ScrollChildAndChangePageScaleOnMainThread) {
 | 
|        *scroll_info.get(), child_scroll_layer_id, expected_scroll_delta);
 | 
|  
 | 
|    // The scroll range should not have changed.
 | 
| -  EXPECT_EQ(child->max_scroll_offset(), expected_max_scroll);
 | 
| +  EXPECT_EQ(child->MaxScrollOffset(), expected_max_scroll);
 | 
|  
 | 
|    // The page scale delta remains constant because the impl thread did not
 | 
|    // scale.
 | 
| @@ -2114,19 +2184,26 @@ TEST_F(LayerTreeHostImplTest, ScrollChildBeyondLimit) {
 | 
|    // parent layer is scrolled on the axis on which the child was unable to
 | 
|    // scroll.
 | 
|    gfx::Size surface_size(10, 10);
 | 
| -  scoped_ptr<LayerImpl> root = CreateScrollableLayer(1, surface_size);
 | 
| +  gfx::Size content_size(20, 20);
 | 
| +  scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
 | 
| +  root->SetBounds(surface_size);
 | 
|  
 | 
| -  scoped_ptr<LayerImpl> grand_child = CreateScrollableLayer(3, surface_size);
 | 
| -  grand_child->SetScrollOffset(gfx::Vector2d(0, 5));
 | 
| +  scoped_ptr<LayerImpl> grand_child =
 | 
| +      CreateScrollableLayer(3, content_size, root.get());
 | 
|  
 | 
| -  scoped_ptr<LayerImpl> child = CreateScrollableLayer(2, surface_size);
 | 
| -  child->SetScrollOffset(gfx::Vector2d(3, 0));
 | 
| +  scoped_ptr<LayerImpl> child =
 | 
| +      CreateScrollableLayer(2, content_size, root.get());
 | 
| +  LayerImpl* grand_child_layer = grand_child.get();
 | 
|    child->AddChild(grand_child.Pass());
 | 
|  
 | 
| +  LayerImpl* child_layer = child.get();
 | 
|    root->AddChild(child.Pass());
 | 
|    host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
|    host_impl_->active_tree()->DidBecomeActive();
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
| +  grand_child_layer->SetScrollOffset(gfx::Vector2d(0, 5));
 | 
| +  child_layer->SetScrollOffset(gfx::Vector2d(3, 0));
 | 
| +
 | 
|    DrawFrame();
 | 
|    {
 | 
|      gfx::Vector2d scroll_delta(-8, -7);
 | 
| @@ -2152,22 +2229,31 @@ TEST_F(LayerTreeHostImplTest, ScrollChildBeyondLimit) {
 | 
|  TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) {
 | 
|    // Scroll a child layer beyond its maximum scroll range and make sure the
 | 
|    // the scroll doesn't bubble up to the parent layer.
 | 
| -  gfx::Size surface_size(10, 10);
 | 
| +  gfx::Size surface_size(20, 20);
 | 
|    scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
 | 
| -  scoped_ptr<LayerImpl> root_scrolling = CreateScrollableLayer(2, surface_size);
 | 
| +  scoped_ptr<LayerImpl> root_scrolling =
 | 
| +      CreateScrollableLayer(2, surface_size, root.get());
 | 
| +  root_scrolling->SetIsContainerForFixedPositionLayers(true);
 | 
|  
 | 
| -  scoped_ptr<LayerImpl> grand_child = CreateScrollableLayer(4, surface_size);
 | 
| -  grand_child->SetScrollOffset(gfx::Vector2d(0, 2));
 | 
| +  scoped_ptr<LayerImpl> grand_child =
 | 
| +      CreateScrollableLayer(4, surface_size, root.get());
 | 
|  
 | 
| -  scoped_ptr<LayerImpl> child = CreateScrollableLayer(3, surface_size);
 | 
| -  child->SetScrollOffset(gfx::Vector2d(0, 3));
 | 
| +  scoped_ptr<LayerImpl> child =
 | 
| +      CreateScrollableLayer(3, surface_size, root.get());
 | 
| +  LayerImpl* grand_child_layer = grand_child.get();
 | 
|    child->AddChild(grand_child.Pass());
 | 
|  
 | 
| +  LayerImpl* child_layer = child.get();
 | 
|    root_scrolling->AddChild(child.Pass());
 | 
|    root->AddChild(root_scrolling.Pass());
 | 
|    host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
| +  host_impl_->active_tree()->SetViewportLayersFromIds(1, 2, Layer::INVALID_ID);
 | 
|    host_impl_->active_tree()->DidBecomeActive();
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
| +
 | 
| +  grand_child_layer->SetScrollOffset(gfx::Vector2d(0, 2));
 | 
| +  child_layer->SetScrollOffset(gfx::Vector2d(0, 3));
 | 
| +
 | 
|    DrawFrame();
 | 
|    {
 | 
|      gfx::Vector2d scroll_delta(0, -10);
 | 
| @@ -2245,20 +2331,29 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) {
 | 
|      ExpectContains(*scroll_info.get(), grand_child->id(), gfx::Vector2d(0, 4));
 | 
|    }
 | 
|  }
 | 
| -
 | 
|  TEST_F(LayerTreeHostImplTest, ScrollEventBubbling) {
 | 
|    // When we try to scroll a non-scrollable child layer, the scroll delta
 | 
|    // should be applied to one of its ancestors if possible.
 | 
|    gfx::Size surface_size(10, 10);
 | 
|    gfx::Size content_size(20, 20);
 | 
| -  scoped_ptr<LayerImpl> root = CreateScrollableLayer(1, content_size);
 | 
| -  scoped_ptr<LayerImpl> child = CreateScrollableLayer(2, content_size);
 | 
| +  scoped_ptr<LayerImpl> root_clip =
 | 
| +      LayerImpl::Create(host_impl_->active_tree(), 3);
 | 
| +  scoped_ptr<LayerImpl> root =
 | 
| +      CreateScrollableLayer(1, content_size, root_clip.get());
 | 
| +  // Make 'root' the clip layer for child: since they have the same sizes the
 | 
| +  // child will have zero max_scroll_offset and scrolls will bubble.
 | 
| +  scoped_ptr<LayerImpl> child =
 | 
| +      CreateScrollableLayer(2, content_size, root.get());
 | 
| +  child->SetIsContainerForFixedPositionLayers(true);
 | 
| +  root->SetBounds(content_size);
 | 
|  
 | 
| -  child->SetScrollable(false);
 | 
| +  int root_scroll_id = root->id();
 | 
|    root->AddChild(child.Pass());
 | 
| +  root_clip->AddChild(root.Pass());
 | 
|  
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
| -  host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
| +  host_impl_->active_tree()->SetRootLayer(root_clip.Pass());
 | 
| +  host_impl_->active_tree()->SetViewportLayersFromIds(3, 2, Layer::INVALID_ID);
 | 
|    host_impl_->active_tree()->DidBecomeActive();
 | 
|    DrawFrame();
 | 
|    {
 | 
| @@ -2272,18 +2367,22 @@ TEST_F(LayerTreeHostImplTest, ScrollEventBubbling) {
 | 
|      scoped_ptr<ScrollAndScaleSet> scroll_info =
 | 
|          host_impl_->ProcessScrollDeltas();
 | 
|  
 | 
| -    // Only the root should have scrolled.
 | 
| +    // Only the root scroll should have scrolled.
 | 
|      ASSERT_EQ(scroll_info->scrolls.size(), 1u);
 | 
| -    ExpectContains(*scroll_info.get(),
 | 
| -                   host_impl_->active_tree()->root_layer()->id(),
 | 
| -                   scroll_delta);
 | 
| +    ExpectContains(*scroll_info.get(), root_scroll_id, scroll_delta);
 | 
|    }
 | 
|  }
 | 
|  
 | 
|  TEST_F(LayerTreeHostImplTest, ScrollBeforeRedraw) {
 | 
|    gfx::Size surface_size(10, 10);
 | 
| -  host_impl_->active_tree()->SetRootLayer(
 | 
| -      CreateScrollableLayer(1, surface_size));
 | 
| +  scoped_ptr<LayerImpl> root_clip =
 | 
| +      LayerImpl::Create(host_impl_->active_tree(), 1);
 | 
| +  scoped_ptr<LayerImpl> root_scroll =
 | 
| +      CreateScrollableLayer(2, surface_size, root_clip.get());
 | 
| +  root_scroll->SetIsContainerForFixedPositionLayers(true);
 | 
| +  root_clip->AddChild(root_scroll.Pass());
 | 
| +  host_impl_->active_tree()->SetRootLayer(root_clip.Pass());
 | 
| +  host_impl_->active_tree()->SetViewportLayersFromIds(1, 2, Layer::INVALID_ID);
 | 
|    host_impl_->active_tree()->DidBecomeActive();
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
|  
 | 
| @@ -2291,8 +2390,14 @@ TEST_F(LayerTreeHostImplTest, ScrollBeforeRedraw) {
 | 
|    // synchronization.
 | 
|    DrawFrame();
 | 
|    host_impl_->active_tree()->DetachLayerTree();
 | 
| -  host_impl_->active_tree()->SetRootLayer(
 | 
| -      CreateScrollableLayer(2, surface_size));
 | 
| +  scoped_ptr<LayerImpl> root_clip2 =
 | 
| +      LayerImpl::Create(host_impl_->active_tree(), 3);
 | 
| +  scoped_ptr<LayerImpl> root_scroll2 =
 | 
| +      CreateScrollableLayer(4, surface_size, root_clip2.get());
 | 
| +  root_scroll2->SetIsContainerForFixedPositionLayers(true);
 | 
| +  root_clip2->AddChild(root_scroll2.Pass());
 | 
| +  host_impl_->active_tree()->SetRootLayer(root_clip2.Pass());
 | 
| +  host_impl_->active_tree()->SetViewportLayersFromIds(3, 4, Layer::INVALID_ID);
 | 
|    host_impl_->active_tree()->DidBecomeActive();
 | 
|  
 | 
|    // Scrolling should still work even though we did not draw yet.
 | 
| @@ -2345,22 +2450,31 @@ TEST_F(LayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) {
 | 
|  
 | 
|  TEST_F(LayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) {
 | 
|    LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
 | 
| -  int child_layer_id = 4;
 | 
| +  int child_clip_layer_id = 6;
 | 
| +  int child_layer_id = 7;
 | 
|    float child_layer_angle = -20.f;
 | 
|  
 | 
|    // Create a child layer that is rotated to a non-axis-aligned angle.
 | 
| +  scoped_ptr<LayerImpl> clip_layer =
 | 
| +      LayerImpl::Create(host_impl_->active_tree(), child_clip_layer_id);
 | 
|    scoped_ptr<LayerImpl> child = CreateScrollableLayer(
 | 
| -      child_layer_id,
 | 
| -      scroll_layer->content_bounds());
 | 
| +      child_layer_id, scroll_layer->content_bounds(), clip_layer.get());
 | 
|    gfx::Transform rotate_transform;
 | 
|    rotate_transform.Translate(-50.0, -50.0);
 | 
|    rotate_transform.Rotate(child_layer_angle);
 | 
|    rotate_transform.Translate(50.0, 50.0);
 | 
| -  child->SetTransform(rotate_transform);
 | 
| +  clip_layer->SetTransform(rotate_transform);
 | 
|  
 | 
|    // Only allow vertical scrolling.
 | 
| -  child->SetMaxScrollOffset(gfx::Vector2d(0, child->content_bounds().height()));
 | 
| -  scroll_layer->AddChild(child.Pass());
 | 
| +  clip_layer->SetBounds(
 | 
| +      gfx::Size(child->bounds().width(), child->bounds().height() / 2));
 | 
| +  // The rotation depends on the layer's anchor point, and the child layer is a
 | 
| +  // different size than the clip, so make sure the clip layer's anchor lines
 | 
| +  // up over the child.
 | 
| +  clip_layer->SetAnchorPoint(gfx::PointF(0.5, 1.0));
 | 
| +  LayerImpl* child_ptr = child.get();
 | 
| +  clip_layer->AddChild(child.Pass());
 | 
| +  scroll_layer->AddChild(clip_layer.Pass());
 | 
|  
 | 
|    gfx::Size surface_size(50, 50);
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
| @@ -2390,8 +2504,7 @@ TEST_F(LayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) {
 | 
|    }
 | 
|    {
 | 
|      // Now reset and scroll the same amount horizontally.
 | 
| -    scroll_layer->children()[1]->SetScrollDelta(
 | 
| -        gfx::Vector2dF());
 | 
| +    child_ptr->SetScrollDelta(gfx::Vector2dF());
 | 
|      gfx::Vector2d gesture_scroll_delta(10, 0);
 | 
|      EXPECT_EQ(InputHandler::ScrollStarted,
 | 
|                host_impl_->ScrollBegin(gfx::Point(1, 1),
 | 
| @@ -2525,6 +2638,8 @@ TEST_F(LayerTreeHostImplTest, RootLayerScrollOffsetDelegation) {
 | 
|    TestScrollOffsetDelegate scroll_delegate;
 | 
|    host_impl_->SetViewportSize(gfx::Size(10, 20));
 | 
|    LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100));
 | 
| +  LayerImpl* clip_layer = scroll_layer->parent()->parent();
 | 
| +  clip_layer->SetBounds(gfx::Size(10, 20));
 | 
|  
 | 
|    // Setting the delegate results in the current scroll offset being set.
 | 
|    gfx::Vector2dF initial_scroll_delta(10.f, 10.f);
 | 
| @@ -2648,17 +2763,25 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) {
 | 
|    // Scroll child layers beyond their maximum scroll range and make sure root
 | 
|    // overscroll does not accumulate.
 | 
|    gfx::Size surface_size(10, 10);
 | 
| -  scoped_ptr<LayerImpl> root = CreateScrollableLayer(1, surface_size);
 | 
| +  scoped_ptr<LayerImpl> root_clip =
 | 
| +      LayerImpl::Create(host_impl_->active_tree(), 4);
 | 
| +  scoped_ptr<LayerImpl> root =
 | 
| +      CreateScrollableLayer(1, surface_size, root_clip.get());
 | 
|  
 | 
| -  scoped_ptr<LayerImpl> grand_child = CreateScrollableLayer(3, surface_size);
 | 
| -  grand_child->SetScrollOffset(gfx::Vector2d(0, 2));
 | 
| +  scoped_ptr<LayerImpl> grand_child =
 | 
| +      CreateScrollableLayer(3, surface_size, root_clip.get());
 | 
|  
 | 
| -  scoped_ptr<LayerImpl> child = CreateScrollableLayer(2, surface_size);
 | 
| -  child->SetScrollOffset(gfx::Vector2d(0, 3));
 | 
| +  scoped_ptr<LayerImpl> child =
 | 
| +      CreateScrollableLayer(2, surface_size, root_clip.get());
 | 
| +  LayerImpl* grand_child_layer = grand_child.get();
 | 
|    child->AddChild(grand_child.Pass());
 | 
|  
 | 
| +  LayerImpl* child_layer = child.get();
 | 
|    root->AddChild(child.Pass());
 | 
| -  host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
| +  root_clip->AddChild(root.Pass());
 | 
| +  child_layer->SetScrollOffset(gfx::Vector2d(0, 3));
 | 
| +  grand_child_layer->SetScrollOffset(gfx::Vector2d(0, 2));
 | 
| +  host_impl_->active_tree()->SetRootLayer(root_clip.Pass());
 | 
|    host_impl_->active_tree()->DidBecomeActive();
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
|    DrawFrame();
 | 
| @@ -2671,19 +2794,16 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) {
 | 
|      EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
 | 
|      host_impl_->ScrollEnd();
 | 
|  
 | 
| -    LayerImpl* child = host_impl_->active_tree()->root_layer()->children()[0];
 | 
| -    LayerImpl* grand_child = child->children()[0];
 | 
| -
 | 
|      // The next time we scroll we should only scroll the parent, but overscroll
 | 
|      // should still not reach the root layer.
 | 
|      scroll_delta = gfx::Vector2d(0, -30);
 | 
|      EXPECT_EQ(InputHandler::ScrollStarted,
 | 
|                host_impl_->ScrollBegin(gfx::Point(5, 5),
 | 
|                                        InputHandler::NonBubblingGesture));
 | 
| -    EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
 | 
| +    EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child_layer);
 | 
|      EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
 | 
|      host_impl_->ScrollBy(gfx::Point(), scroll_delta);
 | 
| -    EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), child);
 | 
| +    EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), child_layer);
 | 
|      EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
 | 
|      host_impl_->ScrollEnd();
 | 
|  
 | 
| @@ -2695,9 +2815,9 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) {
 | 
|      EXPECT_EQ(InputHandler::ScrollStarted,
 | 
|                host_impl_->ScrollBegin(gfx::Point(5, 5),
 | 
|                                        InputHandler::NonBubblingGesture));
 | 
| -    EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
 | 
| +    EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child_layer);
 | 
|      host_impl_->ScrollBy(gfx::Point(), scroll_delta);
 | 
| -    EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child);
 | 
| +    EXPECT_EQ(host_impl_->CurrentlyScrollingLayer(), grand_child_layer);
 | 
|      EXPECT_EQ(gfx::Vector2dF(), host_impl_->accumulated_root_overscroll());
 | 
|      EXPECT_EQ(gfx::Vector2dF(), host_impl_->current_fling_velocity());
 | 
|      host_impl_->ScrollEnd();
 | 
| @@ -2710,14 +2830,21 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildEventBubbling) {
 | 
|    // be reflected only when it has bubbled up to the root scrolling layer.
 | 
|    gfx::Size surface_size(10, 10);
 | 
|    gfx::Size content_size(20, 20);
 | 
| -  scoped_ptr<LayerImpl> root = CreateScrollableLayer(1, content_size);
 | 
| -  scoped_ptr<LayerImpl> child = CreateScrollableLayer(2, content_size);
 | 
| +  scoped_ptr<LayerImpl> root_clip =
 | 
| +      LayerImpl::Create(host_impl_->active_tree(), 3);
 | 
| +  scoped_ptr<LayerImpl> root =
 | 
| +      CreateScrollableLayer(1, content_size, root_clip.get());
 | 
| +  root->SetIsContainerForFixedPositionLayers(true);
 | 
| +  scoped_ptr<LayerImpl> child =
 | 
| +      CreateScrollableLayer(2, content_size, root_clip.get());
 | 
|  
 | 
| -  child->SetScrollable(false);
 | 
| +  child->SetScrollClipLayer(Layer::INVALID_ID);
 | 
|    root->AddChild(child.Pass());
 | 
| +  root_clip->AddChild(root.Pass());
 | 
|  
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
| -  host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
| +  host_impl_->active_tree()->SetRootLayer(root_clip.Pass());
 | 
| +  host_impl_->active_tree()->SetViewportLayersFromIds(3, 1, Layer::INVALID_ID);
 | 
|    host_impl_->active_tree()->DidBecomeActive();
 | 
|    DrawFrame();
 | 
|    {
 | 
| @@ -2739,7 +2866,9 @@ TEST_F(LayerTreeHostImplTest, OverscrollAlways) {
 | 
|    LayerTreeSettings settings;
 | 
|    CreateHostImpl(settings, CreateOutputSurface());
 | 
|  
 | 
| -  SetupScrollAndContentsLayers(gfx::Size(50, 50));
 | 
| +  LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(50, 50));
 | 
| +  LayerImpl* clip_layer = scroll_layer->parent()->parent();
 | 
| +  clip_layer->SetBounds(gfx::Size(50, 50));
 | 
|    host_impl_->SetViewportSize(gfx::Size(50, 50));
 | 
|    host_impl_->active_tree()->SetPageScaleFactorAndLimits(1.f, 0.5f, 4.f);
 | 
|    DrawFrame();
 | 
| @@ -4820,8 +4949,7 @@ TEST_F(LayerTreeHostImplTest, FarAwayQuadsDontNeedAA) {
 | 
|    root->SetBounds(root_size);
 | 
|  
 | 
|    gfx::Vector2d scroll_offset(100000, 0);
 | 
| -  scrolling_layer->SetScrollable(true);
 | 
| -  scrolling_layer->SetMaxScrollOffset(scroll_offset);
 | 
| +  scrolling_layer->SetScrollClipLayer(root->id());
 | 
|    scrolling_layer->SetScrollOffset(scroll_offset);
 | 
|  
 | 
|    host_impl_->ActivatePendingTree();
 | 
| @@ -5275,13 +5403,21 @@ TEST_F(LayerTreeHostImplTest, TouchFlingShouldNotBubble) {
 | 
|    // bubble).
 | 
|    gfx::Size surface_size(10, 10);
 | 
|    gfx::Size content_size(20, 20);
 | 
| -  scoped_ptr<LayerImpl> root = CreateScrollableLayer(1, content_size);
 | 
| -  scoped_ptr<LayerImpl> child = CreateScrollableLayer(2, content_size);
 | 
| +  scoped_ptr<LayerImpl> root_clip =
 | 
| +      LayerImpl::Create(host_impl_->active_tree(), 3);
 | 
| +  scoped_ptr<LayerImpl> root =
 | 
| +      CreateScrollableLayer(1, content_size, root_clip.get());
 | 
| +  root->SetIsContainerForFixedPositionLayers(true);
 | 
| +  scoped_ptr<LayerImpl> child =
 | 
| +      CreateScrollableLayer(2, content_size, root_clip.get());
 | 
|  
 | 
|    root->AddChild(child.Pass());
 | 
| +  int root_id = root->id();
 | 
| +  root_clip->AddChild(root.Pass());
 | 
|  
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
| -  host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
| +  host_impl_->active_tree()->SetRootLayer(root_clip.Pass());
 | 
| +  host_impl_->active_tree()->SetViewportLayersFromIds(3, 1, Layer::INVALID_ID);
 | 
|    host_impl_->active_tree()->DidBecomeActive();
 | 
|    DrawFrame();
 | 
|    {
 | 
| @@ -5303,8 +5439,7 @@ TEST_F(LayerTreeHostImplTest, TouchFlingShouldNotBubble) {
 | 
|  
 | 
|      // Only the child should have scrolled.
 | 
|      ASSERT_EQ(1u, scroll_info->scrolls.size());
 | 
| -    ExpectNone(*scroll_info.get(),
 | 
| -               host_impl_->active_tree()->root_layer()->id());
 | 
| +    ExpectNone(*scroll_info.get(), root_id);
 | 
|    }
 | 
|  }
 | 
|  
 | 
| @@ -5313,12 +5448,15 @@ TEST_F(LayerTreeHostImplTest, TouchFlingShouldLockToFirstScrolledLayer) {
 | 
|    // the scroll doesn't bubble up to the parent layer.
 | 
|    gfx::Size surface_size(10, 10);
 | 
|    scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl_->active_tree(), 1);
 | 
| -  scoped_ptr<LayerImpl> root_scrolling = CreateScrollableLayer(2, surface_size);
 | 
| +  scoped_ptr<LayerImpl> root_scrolling =
 | 
| +      CreateScrollableLayer(2, surface_size, root.get());
 | 
|  
 | 
| -  scoped_ptr<LayerImpl> grand_child = CreateScrollableLayer(4, surface_size);
 | 
| +  scoped_ptr<LayerImpl> grand_child =
 | 
| +      CreateScrollableLayer(4, surface_size, root.get());
 | 
|    grand_child->SetScrollOffset(gfx::Vector2d(0, 2));
 | 
|  
 | 
| -  scoped_ptr<LayerImpl> child = CreateScrollableLayer(3, surface_size);
 | 
| +  scoped_ptr<LayerImpl> child =
 | 
| +      CreateScrollableLayer(3, surface_size, root.get());
 | 
|    child->SetScrollOffset(gfx::Vector2d(0, 4));
 | 
|    child->AddChild(grand_child.Pass());
 | 
|  
 | 
| @@ -5378,13 +5516,19 @@ TEST_F(LayerTreeHostImplTest, WheelFlingShouldBubble) {
 | 
|    // bubble).
 | 
|    gfx::Size surface_size(10, 10);
 | 
|    gfx::Size content_size(20, 20);
 | 
| -  scoped_ptr<LayerImpl> root = CreateScrollableLayer(1, content_size);
 | 
| -  scoped_ptr<LayerImpl> child = CreateScrollableLayer(2, content_size);
 | 
| +  scoped_ptr<LayerImpl> root_clip =
 | 
| +      LayerImpl::Create(host_impl_->active_tree(), 3);
 | 
| +  scoped_ptr<LayerImpl> root_scroll =
 | 
| +      CreateScrollableLayer(1, content_size, root_clip.get());
 | 
| +  int root_scroll_id = root_scroll->id();
 | 
| +  scoped_ptr<LayerImpl> child =
 | 
| +      CreateScrollableLayer(2, content_size, root_clip.get());
 | 
|  
 | 
| -  root->AddChild(child.Pass());
 | 
| +  root_scroll->AddChild(child.Pass());
 | 
| +  root_clip->AddChild(root_scroll.Pass());
 | 
|  
 | 
|    host_impl_->SetViewportSize(surface_size);
 | 
| -  host_impl_->active_tree()->SetRootLayer(root.Pass());
 | 
| +  host_impl_->active_tree()->SetRootLayer(root_clip.Pass());
 | 
|    host_impl_->active_tree()->DidBecomeActive();
 | 
|    DrawFrame();
 | 
|    {
 | 
| @@ -5405,9 +5549,7 @@ TEST_F(LayerTreeHostImplTest, WheelFlingShouldBubble) {
 | 
|  
 | 
|      // The root should have scrolled.
 | 
|      ASSERT_EQ(2u, scroll_info->scrolls.size());
 | 
| -    ExpectContains(*scroll_info.get(),
 | 
| -                   host_impl_->active_tree()->root_layer()->id(),
 | 
| -                   gfx::Vector2d(0, 10));
 | 
| +    ExpectContains(*scroll_info.get(), root_scroll_id, gfx::Vector2d(0, 10));
 | 
|    }
 | 
|  }
 | 
|  
 | 
| 
 |