Home | History | Annotate | Download | only in layers
      1 // Copyright 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/containers/hash_tables.h"
      6 #include "cc/animation/scrollbar_animation_controller.h"
      7 #include "cc/layers/append_quads_data.h"
      8 #include "cc/layers/painted_scrollbar_layer.h"
      9 #include "cc/layers/painted_scrollbar_layer_impl.h"
     10 #include "cc/layers/scrollbar_layer_interface.h"
     11 #include "cc/layers/solid_color_scrollbar_layer.h"
     12 #include "cc/layers/solid_color_scrollbar_layer_impl.h"
     13 #include "cc/quads/solid_color_draw_quad.h"
     14 #include "cc/resources/resource_update_queue.h"
     15 #include "cc/test/fake_impl_proxy.h"
     16 #include "cc/test/fake_layer_tree_host.h"
     17 #include "cc/test/fake_layer_tree_host_client.h"
     18 #include "cc/test/fake_layer_tree_host_impl.h"
     19 #include "cc/test/fake_painted_scrollbar_layer.h"
     20 #include "cc/test/fake_scrollbar.h"
     21 #include "cc/test/geometry_test_utils.h"
     22 #include "cc/test/layer_tree_test.h"
     23 #include "cc/test/mock_quad_culler.h"
     24 #include "cc/test/test_web_graphics_context_3d.h"
     25 #include "cc/trees/layer_tree_host.h"
     26 #include "cc/trees/layer_tree_impl.h"
     27 #include "cc/trees/single_thread_proxy.h"
     28 #include "cc/trees/tree_synchronizer.h"
     29 #include "testing/gmock/include/gmock/gmock.h"
     30 #include "testing/gtest/include/gtest/gtest.h"
     31 
     32 namespace cc {
     33 namespace {
     34 
     35 LayerImpl* LayerImplForScrollAreaAndScrollbar(
     36     FakeLayerTreeHost* host,
     37     scoped_ptr<Scrollbar> scrollbar,
     38     bool reverse_order,
     39     bool use_solid_color_scrollbar,
     40     int thumb_thickness) {
     41   scoped_refptr<Layer> layer_tree_root = Layer::Create();
     42   scoped_refptr<Layer> child1 = Layer::Create();
     43   scoped_refptr<Layer> child2;
     44   if (use_solid_color_scrollbar) {
     45     const bool kIsLeftSideVerticalScrollbar = false;
     46     child2 = SolidColorScrollbarLayer::Create(
     47         scrollbar->Orientation(), thumb_thickness,
     48         kIsLeftSideVerticalScrollbar, child1->id());
     49   } else {
     50     child2 = PaintedScrollbarLayer::Create(scrollbar.Pass(), child1->id());
     51   }
     52   layer_tree_root->AddChild(child1);
     53   layer_tree_root->InsertChild(child2, reverse_order ? 0 : 1);
     54   host->SetRootLayer(layer_tree_root);
     55   return host->CommitAndCreateLayerImplTree();
     56 }
     57 
     58 TEST(ScrollbarLayerTest, ResolveScrollLayerPointer) {
     59   scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
     60   scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
     61   LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
     62       host.get(), scrollbar.Pass(), false, false, 0);
     63 
     64   LayerImpl* cc_child1 = layer_impl_tree_root->children()[0];
     65   PaintedScrollbarLayerImpl* cc_child2 =
     66       static_cast<PaintedScrollbarLayerImpl*>(
     67           layer_impl_tree_root->children()[1]);
     68 
     69   EXPECT_EQ(cc_child1->horizontal_scrollbar_layer(), cc_child2);
     70 }
     71 
     72 TEST(ScrollbarLayerTest, ResolveScrollLayerPointer_ReverseOrder) {
     73   scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
     74   scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
     75   LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
     76       host.get(), scrollbar.Pass(), true, false, 0);
     77 
     78   PaintedScrollbarLayerImpl* cc_child1 =
     79       static_cast<PaintedScrollbarLayerImpl*>(
     80           layer_impl_tree_root->children()[0]);
     81   LayerImpl* cc_child2 = layer_impl_tree_root->children()[1];
     82 
     83   EXPECT_EQ(cc_child2->horizontal_scrollbar_layer(), cc_child1);
     84 }
     85 
     86 TEST(ScrollbarLayerTest, ShouldScrollNonOverlayOnMainThread) {
     87   scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
     88 
     89   // Create and attach a non-overlay scrollbar.
     90   scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
     91   LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
     92       host.get(), scrollbar.Pass(), false, false, 0);
     93   PaintedScrollbarLayerImpl* scrollbar_layer_impl =
     94       static_cast<PaintedScrollbarLayerImpl*>(
     95           layer_impl_tree_root->children()[1]);
     96 
     97   // When the scrollbar is not an overlay scrollbar, the scroll should be
     98   // responded to on the main thread as the compositor does not yet implement
     99   // scrollbar scrolling.
    100   EXPECT_EQ(InputHandler::ScrollOnMainThread,
    101             scrollbar_layer_impl->TryScroll(gfx::Point(0, 0),
    102                                             InputHandler::Gesture));
    103 
    104   // Create and attach an overlay scrollbar.
    105   scrollbar.reset(new FakeScrollbar(false, false, true));
    106 
    107   layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
    108       host.get(), scrollbar.Pass(), false, false, 0);
    109   scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(
    110       layer_impl_tree_root->children()[1]);
    111 
    112   // The user shouldn't be able to drag an overlay scrollbar and the scroll
    113   // may be handled in the compositor.
    114   EXPECT_EQ(InputHandler::ScrollIgnored,
    115             scrollbar_layer_impl->TryScroll(gfx::Point(0, 0),
    116                                             InputHandler::Gesture));
    117 }
    118 
    119 TEST(PaintedScrollbarLayerTest, ScrollOffsetSynchronization) {
    120   scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
    121 
    122   scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
    123   scoped_refptr<Layer> layer_tree_root = Layer::Create();
    124   scoped_refptr<Layer> content_layer = Layer::Create();
    125   scoped_refptr<Layer> scrollbar_layer =
    126       PaintedScrollbarLayer::Create(scrollbar.Pass(), layer_tree_root->id());
    127 
    128   layer_tree_root->SetScrollable(true);
    129   layer_tree_root->SetScrollOffset(gfx::Vector2d(10, 20));
    130   layer_tree_root->SetMaxScrollOffset(gfx::Vector2d(30, 50));
    131   layer_tree_root->SetBounds(gfx::Size(100, 200));
    132   content_layer->SetBounds(gfx::Size(100, 200));
    133 
    134   host->SetRootLayer(layer_tree_root);
    135   layer_tree_root->AddChild(content_layer);
    136   layer_tree_root->AddChild(scrollbar_layer);
    137 
    138   layer_tree_root->SavePaintProperties();
    139   content_layer->SavePaintProperties();
    140 
    141   LayerImpl* layer_impl_tree_root = host->CommitAndCreateLayerImplTree();
    142 
    143   ScrollbarLayerImplBase* cc_scrollbar_layer =
    144       static_cast<PaintedScrollbarLayerImpl*>(
    145           layer_impl_tree_root->children()[1]);
    146 
    147   EXPECT_EQ(10.f, cc_scrollbar_layer->current_pos());
    148   EXPECT_EQ(30, cc_scrollbar_layer->maximum());
    149 
    150   layer_tree_root->SetScrollOffset(gfx::Vector2d(100, 200));
    151   layer_tree_root->SetMaxScrollOffset(gfx::Vector2d(300, 500));
    152   layer_tree_root->SetBounds(gfx::Size(1000, 2000));
    153   layer_tree_root->SavePaintProperties();
    154   content_layer->SetBounds(gfx::Size(1000, 2000));
    155   content_layer->SavePaintProperties();
    156 
    157   ScrollbarAnimationController* scrollbar_controller =
    158       layer_impl_tree_root->scrollbar_animation_controller();
    159   layer_impl_tree_root = host->CommitAndCreateLayerImplTree();
    160   EXPECT_EQ(scrollbar_controller,
    161             layer_impl_tree_root->scrollbar_animation_controller());
    162 
    163   EXPECT_EQ(100.f, cc_scrollbar_layer->current_pos());
    164   EXPECT_EQ(300, cc_scrollbar_layer->maximum());
    165 
    166   layer_impl_tree_root->ScrollBy(gfx::Vector2d(12, 34));
    167 
    168   EXPECT_EQ(112.f, cc_scrollbar_layer->current_pos());
    169   EXPECT_EQ(300, cc_scrollbar_layer->maximum());
    170 }
    171 
    172 TEST(ScrollbarLayerTest, ThumbRect) {
    173   scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
    174   scoped_refptr<Layer> root_layer = Layer::Create();
    175   scoped_refptr<Layer> content_layer = Layer::Create();
    176   scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer =
    177       FakePaintedScrollbarLayer::Create(false, true, root_layer->id());
    178 
    179   root_layer->SetScrollable(true);
    180   root_layer->SetMaxScrollOffset(gfx::Vector2d(80, 0));
    181   root_layer->SetBounds(gfx::Size(100, 50));
    182   content_layer->SetBounds(gfx::Size(100, 50));
    183 
    184   host->SetRootLayer(root_layer);
    185   root_layer->AddChild(content_layer);
    186   root_layer->AddChild(scrollbar_layer);
    187 
    188   root_layer->SetScrollOffset(gfx::Vector2d(0, 0));
    189   scrollbar_layer->SetBounds(gfx::Size(70, 10));
    190   scrollbar_layer->fake_scrollbar()->set_location(gfx::Point(20, 10));
    191   scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10));
    192   scrollbar_layer->fake_scrollbar()->set_thumb_thickness(10);
    193   scrollbar_layer->fake_scrollbar()->set_thumb_length(4);
    194   scrollbar_layer->UpdateThumbAndTrackGeometry();
    195   LayerImpl* root_layer_impl = NULL;
    196   PaintedScrollbarLayerImpl* scrollbar_layer_impl = NULL;
    197 
    198   // Thumb is at the edge of the scrollbar (should be inset to
    199   // the start of the track within the scrollbar layer's
    200   // position).
    201   scrollbar_layer->UpdateThumbAndTrackGeometry();
    202   root_layer_impl = host->CommitAndCreateLayerImplTree();
    203   scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(
    204       root_layer_impl->children()[1]);
    205   EXPECT_EQ(gfx::Rect(10, 0, 4, 10).ToString(),
    206             scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
    207 
    208   // Under-scroll (thumb position should clamp and be unchanged).
    209   root_layer->SetScrollOffset(gfx::Vector2d(-5, 0));
    210 
    211   scrollbar_layer->UpdateThumbAndTrackGeometry();
    212   root_layer_impl = host->CommitAndCreateLayerImplTree();
    213   scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(
    214       root_layer_impl->children()[1]);
    215   EXPECT_EQ(gfx::Rect(10, 0, 4, 10).ToString(),
    216             scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
    217 
    218   // Over-scroll (thumb position should clamp on the far side).
    219   root_layer->SetScrollOffset(gfx::Vector2d(85, 0));
    220 
    221   scrollbar_layer->UpdateThumbAndTrackGeometry();
    222   root_layer_impl = host->CommitAndCreateLayerImplTree();
    223   scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(
    224       root_layer_impl->children()[1]);
    225   EXPECT_EQ(gfx::Rect(56, 0, 4, 10).ToString(),
    226             scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
    227 
    228   // Change thumb thickness and length.
    229   scrollbar_layer->fake_scrollbar()->set_thumb_thickness(4);
    230   scrollbar_layer->fake_scrollbar()->set_thumb_length(6);
    231 
    232   scrollbar_layer->UpdateThumbAndTrackGeometry();
    233   root_layer_impl = host->CommitAndCreateLayerImplTree();
    234   scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(
    235       root_layer_impl->children()[1]);
    236   EXPECT_EQ(gfx::Rect(54, 0, 6, 4).ToString(),
    237             scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
    238 
    239   // Shrink the scrollbar layer to cover only the track.
    240   scrollbar_layer->SetBounds(gfx::Size(50, 10));
    241   scrollbar_layer->fake_scrollbar()->set_location(gfx::Point(30, 10));
    242   scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10));
    243 
    244   scrollbar_layer->UpdateThumbAndTrackGeometry();
    245   root_layer_impl = host->CommitAndCreateLayerImplTree();
    246   scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(
    247       root_layer_impl->children()[1]);
    248   EXPECT_EQ(gfx::Rect(44, 0, 6, 4).ToString(),
    249             scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
    250 
    251   // Shrink the track in the non-scrolling dimension so that it only covers the
    252   // middle third of the scrollbar layer (this does not affect the thumb
    253   // position).
    254   scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 12, 50, 6));
    255 
    256   scrollbar_layer->UpdateThumbAndTrackGeometry();
    257   root_layer_impl = host->CommitAndCreateLayerImplTree();
    258   scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(
    259       root_layer_impl->children()[1]);
    260   EXPECT_EQ(gfx::Rect(44, 0, 6, 4).ToString(),
    261             scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
    262 }
    263 
    264 TEST(ScrollbarLayerTest, SolidColorDrawQuads) {
    265   const int kThumbThickness = 3;
    266   const int kTrackLength = 100;
    267 
    268   LayerTreeSettings layer_tree_settings;
    269   scoped_ptr<FakeLayerTreeHost> host =
    270       FakeLayerTreeHost::Create(layer_tree_settings);
    271 
    272   scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, true));
    273   LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
    274       host.get(), scrollbar.Pass(), false, true, kThumbThickness);
    275   ScrollbarLayerImplBase* scrollbar_layer_impl =
    276       static_cast<SolidColorScrollbarLayerImpl*>(
    277           layer_impl_tree_root->children()[1]);
    278   scrollbar_layer_impl->SetBounds(gfx::Size(kTrackLength, kThumbThickness));
    279   scrollbar_layer_impl->SetCurrentPos(10.f);
    280   scrollbar_layer_impl->SetMaximum(100);
    281   scrollbar_layer_impl->SetVisibleToTotalLengthRatio(0.4f);
    282 
    283   // Thickness should be overridden to 3.
    284   {
    285     MockQuadCuller quad_culler;
    286     AppendQuadsData data;
    287     scrollbar_layer_impl->AppendQuads(&quad_culler, &data);
    288 
    289     const QuadList& quads = quad_culler.quad_list();
    290     ASSERT_EQ(1u, quads.size());
    291     EXPECT_EQ(DrawQuad::SOLID_COLOR, quads[0]->material);
    292     EXPECT_RECT_EQ(gfx::Rect(6, 0, 40, 3), quads[0]->rect);
    293   }
    294 
    295   // Contents scale should scale the draw quad.
    296   scrollbar_layer_impl->draw_properties().contents_scale_x = 2.f;
    297   scrollbar_layer_impl->draw_properties().contents_scale_y = 2.f;
    298   {
    299     MockQuadCuller quad_culler;
    300     AppendQuadsData data;
    301     scrollbar_layer_impl->AppendQuads(&quad_culler, &data);
    302 
    303     const QuadList& quads = quad_culler.quad_list();
    304     ASSERT_EQ(1u, quads.size());
    305     EXPECT_EQ(DrawQuad::SOLID_COLOR, quads[0]->material);
    306     EXPECT_RECT_EQ(gfx::Rect(12, 0, 80, 6), quads[0]->rect);
    307   }
    308   scrollbar_layer_impl->draw_properties().contents_scale_x = 1.f;
    309   scrollbar_layer_impl->draw_properties().contents_scale_y = 1.f;
    310 
    311   // For solid color scrollbars, position and size should reflect the
    312   // current viewport state.
    313   scrollbar_layer_impl->SetVisibleToTotalLengthRatio(0.2f);
    314   {
    315     MockQuadCuller quad_culler;
    316     AppendQuadsData data;
    317     scrollbar_layer_impl->AppendQuads(&quad_culler, &data);
    318 
    319     const QuadList& quads = quad_culler.quad_list();
    320     ASSERT_EQ(1u, quads.size());
    321     EXPECT_EQ(DrawQuad::SOLID_COLOR, quads[0]->material);
    322     EXPECT_RECT_EQ(gfx::Rect(8, 0, 20, 3), quads[0]->rect);
    323   }
    324 }
    325 
    326 TEST(ScrollbarLayerTest, LayerDrivenSolidColorDrawQuads) {
    327   const int kThumbThickness = 3;
    328   const int kTrackLength = 10;
    329 
    330   LayerTreeSettings layer_tree_settings;
    331   scoped_ptr<FakeLayerTreeHost> host =
    332       FakeLayerTreeHost::Create(layer_tree_settings);
    333 
    334   scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, true));
    335   LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
    336       host.get(), scrollbar.Pass(), false, true, kThumbThickness);
    337   ScrollbarLayerImplBase* scrollbar_layer_impl =
    338       static_cast<PaintedScrollbarLayerImpl*>(
    339           layer_impl_tree_root->children()[1]);
    340 
    341   scrollbar_layer_impl->SetBounds(gfx::Size(kTrackLength, kThumbThickness));
    342   scrollbar_layer_impl->SetCurrentPos(4.f);
    343   scrollbar_layer_impl->SetMaximum(8);
    344 
    345   layer_impl_tree_root->SetScrollable(true);
    346   layer_impl_tree_root->SetHorizontalScrollbarLayer(scrollbar_layer_impl);
    347   layer_impl_tree_root->SetMaxScrollOffset(gfx::Vector2d(8, 8));
    348   layer_impl_tree_root->SetBounds(gfx::Size(2, 2));
    349   layer_impl_tree_root->ScrollBy(gfx::Vector2dF(4.f, 0.f));
    350 
    351   {
    352     MockQuadCuller quad_culler;
    353     AppendQuadsData data;
    354     scrollbar_layer_impl->AppendQuads(&quad_culler, &data);
    355 
    356     const QuadList& quads = quad_culler.quad_list();
    357     ASSERT_EQ(1u, quads.size());
    358     EXPECT_EQ(DrawQuad::SOLID_COLOR, quads[0]->material);
    359     EXPECT_RECT_EQ(gfx::Rect(3, 0, 3, 3), quads[0]->rect);
    360   }
    361 }
    362 
    363 class ScrollbarLayerSolidColorThumbTest : public testing::Test {
    364  public:
    365   ScrollbarLayerSolidColorThumbTest() {
    366     LayerTreeSettings layer_tree_settings;
    367     host_impl_.reset(new FakeLayerTreeHostImpl(layer_tree_settings, &proxy_));
    368 
    369     const int kThumbThickness = 3;
    370     const bool kIsLeftSideVerticalScrollbar = false;
    371 
    372     horizontal_scrollbar_layer_ = SolidColorScrollbarLayerImpl::Create(
    373         host_impl_->active_tree(), 1, HORIZONTAL, kThumbThickness,
    374         kIsLeftSideVerticalScrollbar);
    375     vertical_scrollbar_layer_ = SolidColorScrollbarLayerImpl::Create(
    376         host_impl_->active_tree(), 2, VERTICAL, kThumbThickness,
    377         kIsLeftSideVerticalScrollbar);
    378   }
    379 
    380  protected:
    381   FakeImplProxy proxy_;
    382   scoped_ptr<FakeLayerTreeHostImpl> host_impl_;
    383   scoped_ptr<SolidColorScrollbarLayerImpl> horizontal_scrollbar_layer_;
    384   scoped_ptr<SolidColorScrollbarLayerImpl> vertical_scrollbar_layer_;
    385 };
    386 
    387 TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbLength) {
    388   horizontal_scrollbar_layer_->SetCurrentPos(0);
    389   horizontal_scrollbar_layer_->SetMaximum(10);
    390 
    391   // Simple case - one third of the scrollable area is visible, so the thumb
    392   // should be one third as long as the track.
    393   horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.33f);
    394   horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
    395   EXPECT_EQ(33, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width());
    396 
    397   // The thumb's length should never be less than its thickness.
    398   horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.01f);
    399   horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
    400   EXPECT_EQ(3, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width());
    401 }
    402 
    403 TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbPosition) {
    404   horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
    405   horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.1f);
    406 
    407   horizontal_scrollbar_layer_->SetCurrentPos(0);
    408   horizontal_scrollbar_layer_->SetMaximum(100);
    409   EXPECT_EQ(0, horizontal_scrollbar_layer_->ComputeThumbQuadRect().x());
    410   EXPECT_EQ(10, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width());
    411 
    412   horizontal_scrollbar_layer_->SetCurrentPos(100);
    413   // The thumb is 10px long and the track is 100px, so the maximum thumb
    414   // position is 90px.
    415   EXPECT_EQ(90, horizontal_scrollbar_layer_->ComputeThumbQuadRect().x());
    416 
    417   horizontal_scrollbar_layer_->SetCurrentPos(80);
    418   // The scroll position is 80% of the maximum, so the thumb's position should
    419   // be at 80% of its maximum or 72px.
    420   EXPECT_EQ(72, horizontal_scrollbar_layer_->ComputeThumbQuadRect().x());
    421 }
    422 
    423 TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbVerticalAdjust) {
    424   SolidColorScrollbarLayerImpl* layers[2] =
    425       { horizontal_scrollbar_layer_.get(), vertical_scrollbar_layer_.get() };
    426   for (size_t i = 0; i < 2; ++i) {
    427     layers[i]->SetVisibleToTotalLengthRatio(0.2f);
    428     layers[i]->SetCurrentPos(25);
    429     layers[i]->SetMaximum(100);
    430   }
    431   layers[0]->SetBounds(gfx::Size(100, 3));
    432   layers[1]->SetBounds(gfx::Size(3, 100));
    433 
    434   EXPECT_RECT_EQ(gfx::RectF(20.f, 0.f, 20.f, 3.f),
    435                  horizontal_scrollbar_layer_->ComputeThumbQuadRect());
    436   EXPECT_RECT_EQ(gfx::RectF(0.f, 20.f, 3.f, 20.f),
    437                  vertical_scrollbar_layer_->ComputeThumbQuadRect());
    438 
    439   horizontal_scrollbar_layer_->SetVerticalAdjust(10.f);
    440   vertical_scrollbar_layer_->SetVerticalAdjust(10.f);
    441 
    442   // The vertical adjustment factor has two effects:
    443   // 1.) Moves the horizontal scrollbar down
    444   // 2.) Increases the vertical scrollbar's effective track length which both
    445   // increases the thumb's length and its position within the track.
    446   EXPECT_RECT_EQ(gfx::Rect(20.f, 10.f, 20.f, 3.f),
    447                  horizontal_scrollbar_layer_->ComputeThumbQuadRect());
    448   EXPECT_RECT_EQ(gfx::Rect(0.f, 22, 3.f, 22.f),
    449                  vertical_scrollbar_layer_->ComputeThumbQuadRect());
    450 }
    451 
    452 class ScrollbarLayerTestMaxTextureSize : public LayerTreeTest {
    453  public:
    454   ScrollbarLayerTestMaxTextureSize() {}
    455 
    456   void SetScrollbarBounds(gfx::Size bounds) { bounds_ = bounds; }
    457 
    458   virtual void BeginTest() OVERRIDE {
    459     scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
    460     scrollbar_layer_ = PaintedScrollbarLayer::Create(scrollbar.Pass(), 1);
    461     scrollbar_layer_->SetLayerTreeHost(layer_tree_host());
    462     scrollbar_layer_->SetBounds(bounds_);
    463     layer_tree_host()->root_layer()->AddChild(scrollbar_layer_);
    464 
    465     scroll_layer_ = Layer::Create();
    466     scrollbar_layer_->SetScrollLayerId(scroll_layer_->id());
    467     layer_tree_host()->root_layer()->AddChild(scroll_layer_);
    468 
    469     PostSetNeedsCommitToMainThread();
    470   }
    471 
    472   virtual void DidCommitAndDrawFrame() OVERRIDE {
    473     const int kMaxTextureSize =
    474         layer_tree_host()->GetRendererCapabilities().max_texture_size;
    475 
    476     // Check first that we're actually testing something.
    477     EXPECT_GT(scrollbar_layer_->bounds().width(), kMaxTextureSize);
    478 
    479     EXPECT_EQ(scrollbar_layer_->content_bounds().width(),
    480               kMaxTextureSize - 1);
    481     EXPECT_EQ(scrollbar_layer_->content_bounds().height(),
    482               kMaxTextureSize - 1);
    483 
    484     EndTest();
    485   }
    486 
    487   virtual void AfterTest() OVERRIDE {}
    488 
    489  private:
    490   scoped_refptr<PaintedScrollbarLayer> scrollbar_layer_;
    491   scoped_refptr<Layer> scroll_layer_;
    492   gfx::Size bounds_;
    493 };
    494 
    495 TEST_F(ScrollbarLayerTestMaxTextureSize, DirectRenderer) {
    496   scoped_ptr<TestWebGraphicsContext3D> context =
    497       TestWebGraphicsContext3D::Create();
    498   int max_size = 0;
    499   context->getIntegerv(GL_MAX_TEXTURE_SIZE, &max_size);
    500   SetScrollbarBounds(gfx::Size(max_size + 100, max_size + 100));
    501   RunTest(true, false, true);
    502 }
    503 
    504 TEST_F(ScrollbarLayerTestMaxTextureSize, DelegatingRenderer) {
    505   scoped_ptr<TestWebGraphicsContext3D> context =
    506       TestWebGraphicsContext3D::Create();
    507   int max_size = 0;
    508   context->getIntegerv(GL_MAX_TEXTURE_SIZE, &max_size);
    509   SetScrollbarBounds(gfx::Size(max_size + 100, max_size + 100));
    510   RunTest(true, true, true);
    511 }
    512 
    513 class MockLayerTreeHost : public LayerTreeHost {
    514  public:
    515   MockLayerTreeHost(FakeLayerTreeHostClient* client,
    516                     const LayerTreeSettings& settings)
    517       : LayerTreeHost(client, NULL, settings),
    518         next_id_(1),
    519         total_ui_resource_created_(0),
    520         total_ui_resource_deleted_(0) {
    521     InitializeSingleThreaded(client);
    522   }
    523 
    524   virtual UIResourceId CreateUIResource(UIResourceClient* content) OVERRIDE {
    525     total_ui_resource_created_++;
    526     UIResourceId nid = next_id_++;
    527     ui_resource_bitmap_map_.insert(
    528         std::make_pair(nid, content->GetBitmap(nid, false)));
    529     return nid;
    530   }
    531 
    532   // Deletes a UI resource.  May safely be called more than once.
    533   virtual void DeleteUIResource(UIResourceId id) OVERRIDE {
    534     UIResourceBitmapMap::iterator iter = ui_resource_bitmap_map_.find(id);
    535     if (iter != ui_resource_bitmap_map_.end()) {
    536       ui_resource_bitmap_map_.erase(iter);
    537       total_ui_resource_deleted_++;
    538     }
    539   }
    540 
    541   size_t UIResourceCount() { return ui_resource_bitmap_map_.size(); }
    542   int TotalUIResourceDeleted() { return total_ui_resource_deleted_; }
    543   int TotalUIResourceCreated() { return total_ui_resource_created_; }
    544 
    545   gfx::Size ui_resource_size(UIResourceId id) {
    546     UIResourceBitmapMap::iterator iter = ui_resource_bitmap_map_.find(id);
    547     if (iter != ui_resource_bitmap_map_.end())
    548       return iter->second.GetSize();
    549     return gfx::Size();
    550   }
    551 
    552  private:
    553   typedef base::hash_map<UIResourceId, UIResourceBitmap>
    554       UIResourceBitmapMap;
    555   UIResourceBitmapMap ui_resource_bitmap_map_;
    556 
    557   int next_id_;
    558   int total_ui_resource_created_;
    559   int total_ui_resource_deleted_;
    560 };
    561 
    562 
    563 class ScrollbarLayerTestResourceCreation : public testing::Test {
    564  public:
    565   ScrollbarLayerTestResourceCreation()
    566       : fake_client_(FakeLayerTreeHostClient::DIRECT_3D) {}
    567 
    568   void TestResourceUpload(int num_updates,
    569                           size_t expected_resources,
    570                           int expected_created,
    571                           int expected_deleted,
    572                           bool use_solid_color_scrollbar) {
    573     layer_tree_host_.reset(
    574         new MockLayerTreeHost(&fake_client_, layer_tree_settings_));
    575 
    576     scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, false));
    577     scoped_refptr<Layer> layer_tree_root = Layer::Create();
    578     scoped_refptr<Layer> content_layer = Layer::Create();
    579     scoped_refptr<Layer> scrollbar_layer;
    580     if (use_solid_color_scrollbar) {
    581       const int kThumbThickness = 3;
    582       const bool kIsLeftSideVerticalScrollbar = false;
    583       scrollbar_layer =
    584           SolidColorScrollbarLayer::Create(scrollbar->Orientation(),
    585                                            kThumbThickness,
    586                                            kIsLeftSideVerticalScrollbar,
    587                                            layer_tree_root->id());
    588     } else {
    589       scrollbar_layer = PaintedScrollbarLayer::Create(scrollbar.Pass(),
    590                                                       layer_tree_root->id());
    591     }
    592     layer_tree_root->AddChild(content_layer);
    593     layer_tree_root->AddChild(scrollbar_layer);
    594 
    595     layer_tree_host_->InitializeOutputSurfaceIfNeeded();
    596     layer_tree_host_->SetRootLayer(layer_tree_root);
    597 
    598     scrollbar_layer->SetIsDrawable(true);
    599     scrollbar_layer->SetBounds(gfx::Size(100, 100));
    600     layer_tree_root->SetScrollOffset(gfx::Vector2d(10, 20));
    601     layer_tree_root->SetMaxScrollOffset(gfx::Vector2d(30, 50));
    602     layer_tree_root->SetBounds(gfx::Size(100, 200));
    603     content_layer->SetBounds(gfx::Size(100, 200));
    604     scrollbar_layer->draw_properties().content_bounds = gfx::Size(100, 200);
    605     scrollbar_layer->draw_properties().visible_content_rect =
    606         gfx::Rect(0, 0, 100, 200);
    607     scrollbar_layer->CreateRenderSurface();
    608     scrollbar_layer->draw_properties().render_target = scrollbar_layer.get();
    609 
    610     testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
    611     EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get());
    612 
    613     ResourceUpdateQueue queue;
    614     OcclusionTracker occlusion_tracker(gfx::Rect(), false);
    615 
    616     scrollbar_layer->SavePaintProperties();
    617     for (int update_counter = 0; update_counter < num_updates; update_counter++)
    618       scrollbar_layer->Update(&queue, &occlusion_tracker);
    619 
    620     // A non-solid-color scrollbar should have requested two textures.
    621     EXPECT_EQ(expected_resources, layer_tree_host_->UIResourceCount());
    622     EXPECT_EQ(expected_created, layer_tree_host_->TotalUIResourceCreated());
    623     EXPECT_EQ(expected_deleted, layer_tree_host_->TotalUIResourceDeleted());
    624 
    625     testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
    626 
    627     scrollbar_layer->ClearRenderSurface();
    628   }
    629 
    630  protected:
    631   FakeLayerTreeHostClient fake_client_;
    632   LayerTreeSettings layer_tree_settings_;
    633   scoped_ptr<MockLayerTreeHost> layer_tree_host_;
    634 };
    635 
    636 TEST_F(ScrollbarLayerTestResourceCreation, ResourceUpload) {
    637   bool use_solid_color_scrollbars = false;
    638   TestResourceUpload(0, 0, 0, 0, use_solid_color_scrollbars);
    639   int num_updates[3] = {1, 5, 10};
    640   for (int j = 0; j < 3; j++) {
    641     TestResourceUpload(num_updates[j],
    642                        2,
    643                        num_updates[j] * 2,
    644                        (num_updates[j] - 1) * 2,
    645                        use_solid_color_scrollbars);
    646   }
    647 }
    648 
    649 TEST_F(ScrollbarLayerTestResourceCreation, SolidColorNoResourceUpload) {
    650   bool use_solid_color_scrollbars = true;
    651   TestResourceUpload(0, 0, 0, 0, use_solid_color_scrollbars);
    652   TestResourceUpload(1, 0, 0, 0, use_solid_color_scrollbars);
    653 }
    654 
    655 class ScaledScrollbarLayerTestResourceCreation : public testing::Test {
    656  public:
    657   ScaledScrollbarLayerTestResourceCreation()
    658       : fake_client_(FakeLayerTreeHostClient::DIRECT_3D) {}
    659 
    660   void TestResourceUpload(const float test_scale) {
    661     layer_tree_host_.reset(
    662         new MockLayerTreeHost(&fake_client_, layer_tree_settings_));
    663 
    664     gfx::Point scrollbar_location(0, 185);
    665     scoped_refptr<Layer> layer_tree_root = Layer::Create();
    666     scoped_refptr<Layer> content_layer = Layer::Create();
    667     scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer =
    668         FakePaintedScrollbarLayer::Create(false, true, layer_tree_root->id());
    669 
    670     layer_tree_root->AddChild(content_layer);
    671     layer_tree_root->AddChild(scrollbar_layer);
    672 
    673     layer_tree_host_->InitializeOutputSurfaceIfNeeded();
    674     layer_tree_host_->SetRootLayer(layer_tree_root);
    675 
    676     scrollbar_layer->SetIsDrawable(true);
    677     scrollbar_layer->SetBounds(gfx::Size(100, 15));
    678     scrollbar_layer->SetPosition(scrollbar_location);
    679     layer_tree_root->SetBounds(gfx::Size(100, 200));
    680     content_layer->SetBounds(gfx::Size(100, 200));
    681     gfx::SizeF scaled_size =
    682         gfx::ScaleSize(scrollbar_layer->bounds(), test_scale, test_scale);
    683     gfx::PointF scaled_location =
    684         gfx::ScalePoint(scrollbar_layer->position(), test_scale, test_scale);
    685     scrollbar_layer->draw_properties().content_bounds =
    686         gfx::Size(scaled_size.width(), scaled_size.height());
    687     scrollbar_layer->draw_properties().contents_scale_x = test_scale;
    688     scrollbar_layer->draw_properties().contents_scale_y = test_scale;
    689     scrollbar_layer->draw_properties().visible_content_rect =
    690         gfx::Rect(scaled_location.x(),
    691                   scaled_location.y(),
    692                   scaled_size.width(),
    693                   scaled_size.height());
    694     scrollbar_layer->CreateRenderSurface();
    695     scrollbar_layer->draw_properties().render_target = scrollbar_layer.get();
    696 
    697     testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
    698     EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get());
    699 
    700     ResourceUpdateQueue queue;
    701     OcclusionTracker occlusion_tracker(gfx::Rect(), false);
    702     scrollbar_layer->SavePaintProperties();
    703     scrollbar_layer->Update(&queue, &occlusion_tracker);
    704 
    705     // Verify that we have not generated any content uploads that are larger
    706     // than their destination textures.
    707 
    708     gfx::Size track_size = layer_tree_host_->ui_resource_size(
    709         scrollbar_layer->track_resource_id());
    710     gfx::Size thumb_size = layer_tree_host_->ui_resource_size(
    711         scrollbar_layer->thumb_resource_id());
    712 
    713     EXPECT_LE(track_size.width(), scrollbar_layer->content_bounds().width());
    714     EXPECT_LE(track_size.height(), scrollbar_layer->content_bounds().height());
    715     EXPECT_LE(thumb_size.width(), scrollbar_layer->content_bounds().width());
    716     EXPECT_LE(thumb_size.height(), scrollbar_layer->content_bounds().height());
    717 
    718     testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
    719 
    720     scrollbar_layer->ClearRenderSurface();
    721   }
    722 
    723  protected:
    724   FakeLayerTreeHostClient fake_client_;
    725   LayerTreeSettings layer_tree_settings_;
    726   scoped_ptr<MockLayerTreeHost> layer_tree_host_;
    727 };
    728 
    729 TEST_F(ScaledScrollbarLayerTestResourceCreation, ScaledResourceUpload) {
    730   // Pick a test scale that moves the scrollbar's (non-zero) position to
    731   // a non-pixel-aligned location.
    732   TestResourceUpload(.041f);
    733   TestResourceUpload(1.41f);
    734   TestResourceUpload(4.1f);
    735 }
    736 
    737 }  // namespace
    738 }  // namespace cc
    739