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/occlusion_tracker.h"
     28 #include "cc/trees/single_thread_proxy.h"
     29 #include "cc/trees/tree_synchronizer.h"
     30 #include "testing/gmock/include/gmock/gmock.h"
     31 #include "testing/gtest/include/gtest/gtest.h"
     32 
     33 namespace cc {
     34 namespace {
     35 
     36 LayerImpl* LayerImplForScrollAreaAndScrollbar(FakeLayerTreeHost* host,
     37                                               scoped_ptr<Scrollbar> scrollbar,
     38                                               bool reverse_order,
     39                                               bool use_solid_color_scrollbar,
     40                                               int thumb_thickness,
     41                                               int track_start) {
     42   scoped_refptr<Layer> layer_tree_root = Layer::Create();
     43   scoped_refptr<Layer> child1 = Layer::Create();
     44   scoped_refptr<Layer> child2;
     45   if (use_solid_color_scrollbar) {
     46     const bool kIsLeftSideVerticalScrollbar = false;
     47     child2 = SolidColorScrollbarLayer::Create(scrollbar->Orientation(),
     48                                               thumb_thickness,
     49                                               track_start,
     50                                               kIsLeftSideVerticalScrollbar,
     51                                               child1->id());
     52   } else {
     53     child2 = PaintedScrollbarLayer::Create(scrollbar.Pass(), child1->id());
     54   }
     55   child2->ToScrollbarLayer()->SetClipLayer(layer_tree_root->id());
     56   layer_tree_root->AddChild(child1);
     57   layer_tree_root->InsertChild(child2, reverse_order ? 0 : 1);
     58   host->SetRootLayer(layer_tree_root);
     59   return host->CommitAndCreateLayerImplTree();
     60 }
     61 
     62 TEST(ScrollbarLayerTest, ResolveScrollLayerPointer) {
     63   scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
     64   scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
     65   LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
     66       host.get(), scrollbar.Pass(), false, false, 0, 0);
     67 
     68   LayerImpl* cc_child1 = layer_impl_tree_root->children()[0];
     69   PaintedScrollbarLayerImpl* cc_child2 =
     70       static_cast<PaintedScrollbarLayerImpl*>(
     71           layer_impl_tree_root->children()[1]);
     72 
     73   EXPECT_EQ(cc_child1->scrollbars()->size(), 1UL);
     74   EXPECT_EQ(*(cc_child1->scrollbars()->begin()), cc_child2);
     75 }
     76 
     77 TEST(ScrollbarLayerTest, ResolveScrollLayerPointer_ReverseOrder) {
     78   scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
     79   scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
     80   LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
     81       host.get(), scrollbar.Pass(), true, false, 0, 0);
     82 
     83   PaintedScrollbarLayerImpl* cc_child1 =
     84       static_cast<PaintedScrollbarLayerImpl*>(
     85           layer_impl_tree_root->children()[0]);
     86   LayerImpl* cc_child2 = layer_impl_tree_root->children()[1];
     87 
     88   EXPECT_EQ(cc_child2->scrollbars()->size(), 1UL);
     89   EXPECT_EQ(*(cc_child2->scrollbars()->begin()), cc_child1);
     90 }
     91 
     92 TEST(ScrollbarLayerTest, ShouldScrollNonOverlayOnMainThread) {
     93   scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
     94 
     95   // Create and attach a non-overlay scrollbar.
     96   scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
     97   LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
     98       host.get(), scrollbar.Pass(), false, false, 0, 0);
     99   PaintedScrollbarLayerImpl* scrollbar_layer_impl =
    100       static_cast<PaintedScrollbarLayerImpl*>(
    101           layer_impl_tree_root->children()[1]);
    102 
    103   // When the scrollbar is not an overlay scrollbar, the scroll should be
    104   // responded to on the main thread as the compositor does not yet implement
    105   // scrollbar scrolling.
    106   EXPECT_EQ(InputHandler::ScrollOnMainThread,
    107             scrollbar_layer_impl->TryScroll(gfx::Point(0, 0),
    108                                             InputHandler::Gesture));
    109 
    110   // Create and attach an overlay scrollbar.
    111   scrollbar.reset(new FakeScrollbar(false, false, true));
    112 
    113   layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
    114       host.get(), scrollbar.Pass(), false, false, 0, 0);
    115   scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(
    116       layer_impl_tree_root->children()[1]);
    117 
    118   // The user shouldn't be able to drag an overlay scrollbar and the scroll
    119   // may be handled in the compositor.
    120   EXPECT_EQ(InputHandler::ScrollIgnored,
    121             scrollbar_layer_impl->TryScroll(gfx::Point(0, 0),
    122                                             InputHandler::Gesture));
    123 }
    124 
    125 TEST(PaintedScrollbarLayerTest, ScrollOffsetSynchronization) {
    126   scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
    127 
    128   scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
    129   scoped_refptr<Layer> layer_tree_root = Layer::Create();
    130   scoped_refptr<Layer> scroll_layer = Layer::Create();
    131   scoped_refptr<Layer> content_layer = Layer::Create();
    132   scoped_refptr<Layer> scrollbar_layer =
    133       PaintedScrollbarLayer::Create(scrollbar.Pass(), layer_tree_root->id());
    134 
    135   // Choose bounds to give max_scroll_offset = (30, 50).
    136   layer_tree_root->SetBounds(gfx::Size(70, 150));
    137   scroll_layer->SetScrollClipLayerId(layer_tree_root->id());
    138   scroll_layer->SetScrollOffset(gfx::Vector2d(10, 20));
    139   scroll_layer->SetBounds(gfx::Size(100, 200));
    140   content_layer->SetBounds(gfx::Size(100, 200));
    141 
    142   host->SetRootLayer(layer_tree_root);
    143   layer_tree_root->AddChild(scroll_layer);
    144   scroll_layer->AddChild(content_layer);
    145   layer_tree_root->AddChild(scrollbar_layer);
    146   scrollbar_layer->ToScrollbarLayer()->SetScrollLayer(scroll_layer->id());
    147   scrollbar_layer->ToScrollbarLayer()->SetClipLayer(layer_tree_root->id());
    148 
    149   layer_tree_root->SavePaintProperties();
    150   content_layer->SavePaintProperties();
    151 
    152   LayerImpl* layer_impl_tree_root = host->CommitAndCreateLayerImplTree();
    153 
    154   ScrollbarLayerImplBase* cc_scrollbar_layer =
    155       static_cast<PaintedScrollbarLayerImpl*>(
    156           layer_impl_tree_root->children()[1]);
    157 
    158   EXPECT_EQ(10.f, cc_scrollbar_layer->current_pos());
    159   EXPECT_EQ(30, cc_scrollbar_layer->maximum());
    160 
    161   layer_tree_root->SetBounds(gfx::Size(700, 1500));
    162   layer_tree_root->SavePaintProperties();
    163   scroll_layer->SetBounds(gfx::Size(1000, 2000));
    164   scroll_layer->SetScrollOffset(gfx::Vector2d(100, 200));
    165   scroll_layer->SavePaintProperties();
    166   content_layer->SetBounds(gfx::Size(1000, 2000));
    167   content_layer->SavePaintProperties();
    168 
    169   ScrollbarAnimationController* scrollbar_controller =
    170       layer_impl_tree_root->scrollbar_animation_controller();
    171   layer_impl_tree_root = host->CommitAndCreateLayerImplTree();
    172   EXPECT_EQ(scrollbar_controller,
    173             layer_impl_tree_root->scrollbar_animation_controller());
    174 
    175   EXPECT_EQ(100.f, cc_scrollbar_layer->current_pos());
    176   EXPECT_EQ(300, cc_scrollbar_layer->maximum());
    177 
    178   LayerImpl* scroll_layer_impl = layer_impl_tree_root->children()[0];
    179   scroll_layer_impl->ScrollBy(gfx::Vector2d(12, 34));
    180 
    181   EXPECT_EQ(112.f, cc_scrollbar_layer->current_pos());
    182   EXPECT_EQ(300, cc_scrollbar_layer->maximum());
    183 }
    184 
    185 #define UPDATE_AND_EXTRACT_LAYER_POINTERS()                         \
    186   do {                                                              \
    187     scrollbar_layer->UpdateThumbAndTrackGeometry();                 \
    188     root_clip_layer_impl = host->CommitAndCreateLayerImplTree();    \
    189     root_layer_impl = root_clip_layer_impl->children()[0];          \
    190     scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>( \
    191         root_layer_impl->children()[1]);                            \
    192     scrollbar_layer_impl->ScrollbarParametersDidChange();           \
    193   } while (false)
    194 
    195 TEST(ScrollbarLayerTest, ThumbRect) {
    196   scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
    197   scoped_refptr<Layer> root_clip_layer = Layer::Create();
    198   scoped_refptr<Layer> root_layer = Layer::Create();
    199   scoped_refptr<Layer> content_layer = Layer::Create();
    200   scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer =
    201       FakePaintedScrollbarLayer::Create(false, true, root_layer->id());
    202 
    203   root_layer->SetScrollClipLayerId(root_clip_layer->id());
    204   // Give the root-clip a size that will result in MaxScrollOffset = (80, 0).
    205   root_clip_layer->SetBounds(gfx::Size(20, 50));
    206   root_layer->SetBounds(gfx::Size(100, 50));
    207   content_layer->SetBounds(gfx::Size(100, 50));
    208 
    209   host->SetRootLayer(root_clip_layer);
    210   root_clip_layer->AddChild(root_layer);
    211   root_layer->AddChild(content_layer);
    212   root_layer->AddChild(scrollbar_layer);
    213 
    214   root_layer->SetScrollOffset(gfx::Vector2d(0, 0));
    215   scrollbar_layer->SetBounds(gfx::Size(70, 10));
    216   scrollbar_layer->SetScrollLayer(root_layer->id());
    217   scrollbar_layer->SetClipLayer(root_clip_layer->id());
    218   scrollbar_layer->fake_scrollbar()->set_location(gfx::Point(20, 10));
    219   scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10));
    220   scrollbar_layer->fake_scrollbar()->set_thumb_thickness(10);
    221   scrollbar_layer->fake_scrollbar()->set_thumb_length(4);
    222   scrollbar_layer->UpdateThumbAndTrackGeometry();
    223   LayerImpl* root_clip_layer_impl = NULL;
    224   LayerImpl* root_layer_impl = NULL;
    225   PaintedScrollbarLayerImpl* scrollbar_layer_impl = NULL;
    226 
    227   // Thumb is at the edge of the scrollbar (should be inset to
    228   // the start of the track within the scrollbar layer's
    229   // position).
    230   UPDATE_AND_EXTRACT_LAYER_POINTERS();
    231   EXPECT_EQ(gfx::Rect(10, 0, 4, 10).ToString(),
    232             scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
    233 
    234   // Under-scroll (thumb position should clamp and be unchanged).
    235   root_layer->SetScrollOffset(gfx::Vector2d(-5, 0));
    236 
    237   UPDATE_AND_EXTRACT_LAYER_POINTERS();
    238   EXPECT_EQ(gfx::Rect(10, 0, 4, 10).ToString(),
    239             scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
    240 
    241   // Over-scroll (thumb position should clamp on the far side).
    242   root_layer->SetScrollOffset(gfx::Vector2d(85, 0));
    243 
    244   UPDATE_AND_EXTRACT_LAYER_POINTERS();
    245   EXPECT_EQ(gfx::Rect(56, 0, 4, 10).ToString(),
    246             scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
    247 
    248   // Change thumb thickness and length.
    249   scrollbar_layer->fake_scrollbar()->set_thumb_thickness(4);
    250   scrollbar_layer->fake_scrollbar()->set_thumb_length(6);
    251 
    252   UPDATE_AND_EXTRACT_LAYER_POINTERS();
    253   EXPECT_EQ(gfx::Rect(54, 0, 6, 4).ToString(),
    254             scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
    255 
    256   // Shrink the scrollbar layer to cover only the track.
    257   scrollbar_layer->SetBounds(gfx::Size(50, 10));
    258   scrollbar_layer->fake_scrollbar()->set_location(gfx::Point(30, 10));
    259   scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10));
    260 
    261   UPDATE_AND_EXTRACT_LAYER_POINTERS();
    262   EXPECT_EQ(gfx::Rect(44, 0, 6, 4).ToString(),
    263             scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
    264 
    265   // Shrink the track in the non-scrolling dimension so that it only covers the
    266   // middle third of the scrollbar layer (this does not affect the thumb
    267   // position).
    268   scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 12, 50, 6));
    269 
    270   UPDATE_AND_EXTRACT_LAYER_POINTERS();
    271   EXPECT_EQ(gfx::Rect(44, 0, 6, 4).ToString(),
    272             scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
    273 }
    274 
    275 TEST(ScrollbarLayerTest, SolidColorDrawQuads) {
    276   const int kThumbThickness = 3;
    277   const int kTrackStart = 0;
    278   const int kTrackLength = 100;
    279 
    280   LayerTreeSettings layer_tree_settings;
    281   scoped_ptr<FakeLayerTreeHost> host =
    282       FakeLayerTreeHost::Create(layer_tree_settings);
    283 
    284   scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, true));
    285   LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
    286       host.get(), scrollbar.Pass(), false, true, kThumbThickness, kTrackStart);
    287   ScrollbarLayerImplBase* scrollbar_layer_impl =
    288       static_cast<SolidColorScrollbarLayerImpl*>(
    289           layer_impl_tree_root->children()[1]);
    290   scrollbar_layer_impl->SetBounds(gfx::Size(kTrackLength, kThumbThickness));
    291   scrollbar_layer_impl->SetCurrentPos(10.f);
    292   scrollbar_layer_impl->SetMaximum(100);
    293   scrollbar_layer_impl->SetVisibleToTotalLengthRatio(0.4f);
    294 
    295   // Thickness should be overridden to 3.
    296   {
    297     MockOcclusionTracker<LayerImpl> occlusion_tracker;
    298     scoped_ptr<RenderPass> render_pass = RenderPass::Create();
    299     MockQuadCuller quad_culler(render_pass.get(), &occlusion_tracker);
    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(6, 0, 40, 3), quads[0]->rect);
    307   }
    308 
    309   // Contents scale should scale the draw quad.
    310   scrollbar_layer_impl->draw_properties().contents_scale_x = 2.f;
    311   scrollbar_layer_impl->draw_properties().contents_scale_y = 2.f;
    312   {
    313     MockOcclusionTracker<LayerImpl> occlusion_tracker;
    314     scoped_ptr<RenderPass> render_pass = RenderPass::Create();
    315     MockQuadCuller quad_culler(render_pass.get(), &occlusion_tracker);
    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(12, 0, 80, 6), quads[0]->rect);
    323   }
    324   scrollbar_layer_impl->draw_properties().contents_scale_x = 1.f;
    325   scrollbar_layer_impl->draw_properties().contents_scale_y = 1.f;
    326 
    327   // For solid color scrollbars, position and size should reflect the
    328   // current viewport state.
    329   scrollbar_layer_impl->SetVisibleToTotalLengthRatio(0.2f);
    330   {
    331     MockOcclusionTracker<LayerImpl> occlusion_tracker;
    332     scoped_ptr<RenderPass> render_pass = RenderPass::Create();
    333     MockQuadCuller quad_culler(render_pass.get(), &occlusion_tracker);
    334     AppendQuadsData data;
    335     scrollbar_layer_impl->AppendQuads(&quad_culler, &data);
    336 
    337     const QuadList& quads = quad_culler.quad_list();
    338     ASSERT_EQ(1u, quads.size());
    339     EXPECT_EQ(DrawQuad::SOLID_COLOR, quads[0]->material);
    340     EXPECT_RECT_EQ(gfx::Rect(8, 0, 20, 3), quads[0]->rect);
    341   }
    342 }
    343 
    344 TEST(ScrollbarLayerTest, LayerDrivenSolidColorDrawQuads) {
    345   const int kThumbThickness = 3;
    346   const int kTrackStart = 0;
    347   const int kTrackLength = 10;
    348 
    349   LayerTreeSettings layer_tree_settings;
    350   scoped_ptr<FakeLayerTreeHost> host =
    351       FakeLayerTreeHost::Create(layer_tree_settings);
    352 
    353   scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, true));
    354 
    355   {
    356     scoped_refptr<Layer> layer_tree_root = Layer::Create();
    357     scoped_refptr<Layer> scroll_layer = Layer::Create();
    358     scroll_layer->SetScrollClipLayerId(layer_tree_root->id());
    359     scoped_refptr<Layer> child1 = Layer::Create();
    360     scoped_refptr<Layer> child2;
    361     const bool kIsLeftSideVerticalScrollbar = false;
    362     child2 = SolidColorScrollbarLayer::Create(scrollbar->Orientation(),
    363                                               kThumbThickness,
    364                                               kTrackStart,
    365                                               kIsLeftSideVerticalScrollbar,
    366                                               child1->id());
    367     child2->ToScrollbarLayer()->SetScrollLayer(scroll_layer->id());
    368     child2->ToScrollbarLayer()->SetClipLayer(layer_tree_root->id());
    369     scroll_layer->AddChild(child1);
    370     scroll_layer->InsertChild(child2, 1);
    371     layer_tree_root->AddChild(scroll_layer);
    372     host->SetRootLayer(layer_tree_root);
    373   }
    374   LayerImpl* layer_impl_tree_root = host->CommitAndCreateLayerImplTree();
    375   LayerImpl* scroll_layer_impl = layer_impl_tree_root->children()[0];
    376 
    377   ScrollbarLayerImplBase* scrollbar_layer_impl =
    378       static_cast<PaintedScrollbarLayerImpl*>(scroll_layer_impl->children()[1]);
    379 
    380   // Choose layer bounds to give max_scroll_offset = (8, 8).
    381   layer_impl_tree_root->SetBounds(gfx::Size(2, 2));
    382   scroll_layer_impl->SetBounds(gfx::Size(10, 10));
    383   scroll_layer_impl->ScrollBy(gfx::Vector2dF(4.f, 0.f));
    384 
    385   scrollbar_layer_impl->SetBounds(gfx::Size(kTrackLength, kThumbThickness));
    386   scrollbar_layer_impl->SetCurrentPos(4.f);
    387   scrollbar_layer_impl->SetMaximum(8);
    388 
    389   {
    390     MockOcclusionTracker<LayerImpl> occlusion_tracker;
    391     scoped_ptr<RenderPass> render_pass = RenderPass::Create();
    392     MockQuadCuller quad_culler(render_pass.get(), &occlusion_tracker);
    393 
    394     AppendQuadsData data;
    395     scrollbar_layer_impl->AppendQuads(&quad_culler, &data);
    396 
    397     const QuadList& quads = quad_culler.quad_list();
    398     ASSERT_EQ(1u, quads.size());
    399     EXPECT_EQ(DrawQuad::SOLID_COLOR, quads[0]->material);
    400     EXPECT_RECT_EQ(gfx::Rect(3, 0, 3, 3), quads[0]->rect);
    401   }
    402 }
    403 
    404 class ScrollbarLayerSolidColorThumbTest : public testing::Test {
    405  public:
    406   ScrollbarLayerSolidColorThumbTest() {
    407     LayerTreeSettings layer_tree_settings;
    408     host_impl_.reset(new FakeLayerTreeHostImpl(
    409         layer_tree_settings, &proxy_, &shared_bitmap_manager_));
    410 
    411     const int kThumbThickness = 3;
    412     const int kTrackStart = 0;
    413     const bool kIsLeftSideVerticalScrollbar = false;
    414     const bool kIsOverlayScrollbar = false;
    415 
    416     horizontal_scrollbar_layer_ =
    417         SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(),
    418                                              1,
    419                                              HORIZONTAL,
    420                                              kThumbThickness,
    421                                              kTrackStart,
    422                                              kIsLeftSideVerticalScrollbar,
    423                                              kIsOverlayScrollbar);
    424     vertical_scrollbar_layer_ =
    425         SolidColorScrollbarLayerImpl::Create(host_impl_->active_tree(),
    426                                              2,
    427                                              VERTICAL,
    428                                              kThumbThickness,
    429                                              kTrackStart,
    430                                              kIsLeftSideVerticalScrollbar,
    431                                              kIsOverlayScrollbar);
    432   }
    433 
    434  protected:
    435   FakeImplProxy proxy_;
    436   TestSharedBitmapManager shared_bitmap_manager_;
    437   scoped_ptr<FakeLayerTreeHostImpl> host_impl_;
    438   scoped_ptr<SolidColorScrollbarLayerImpl> horizontal_scrollbar_layer_;
    439   scoped_ptr<SolidColorScrollbarLayerImpl> vertical_scrollbar_layer_;
    440 };
    441 
    442 TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbLength) {
    443   horizontal_scrollbar_layer_->SetCurrentPos(0);
    444   horizontal_scrollbar_layer_->SetMaximum(10);
    445 
    446   // Simple case - one third of the scrollable area is visible, so the thumb
    447   // should be one third as long as the track.
    448   horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.33f);
    449   horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
    450   EXPECT_EQ(33, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width());
    451 
    452   // The thumb's length should never be less than its thickness.
    453   horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.01f);
    454   horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
    455   EXPECT_EQ(3, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width());
    456 }
    457 
    458 TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbPosition) {
    459   horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
    460   horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.1f);
    461 
    462   horizontal_scrollbar_layer_->SetCurrentPos(0);
    463   horizontal_scrollbar_layer_->SetMaximum(100);
    464   EXPECT_EQ(0, horizontal_scrollbar_layer_->ComputeThumbQuadRect().x());
    465   EXPECT_EQ(10, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width());
    466 
    467   horizontal_scrollbar_layer_->SetCurrentPos(100);
    468   // The thumb is 10px long and the track is 100px, so the maximum thumb
    469   // position is 90px.
    470   EXPECT_EQ(90, horizontal_scrollbar_layer_->ComputeThumbQuadRect().x());
    471 
    472   horizontal_scrollbar_layer_->SetCurrentPos(80);
    473   // The scroll position is 80% of the maximum, so the thumb's position should
    474   // be at 80% of its maximum or 72px.
    475   EXPECT_EQ(72, horizontal_scrollbar_layer_->ComputeThumbQuadRect().x());
    476 }
    477 
    478 TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbVerticalAdjust) {
    479   SolidColorScrollbarLayerImpl* layers[2] =
    480       { horizontal_scrollbar_layer_.get(), vertical_scrollbar_layer_.get() };
    481   for (size_t i = 0; i < 2; ++i) {
    482     layers[i]->SetVisibleToTotalLengthRatio(0.2f);
    483     layers[i]->SetCurrentPos(25);
    484     layers[i]->SetMaximum(100);
    485   }
    486   layers[0]->SetBounds(gfx::Size(100, 3));
    487   layers[1]->SetBounds(gfx::Size(3, 100));
    488 
    489   EXPECT_RECT_EQ(gfx::RectF(20.f, 0.f, 20.f, 3.f),
    490                  horizontal_scrollbar_layer_->ComputeThumbQuadRect());
    491   EXPECT_RECT_EQ(gfx::RectF(0.f, 20.f, 3.f, 20.f),
    492                  vertical_scrollbar_layer_->ComputeThumbQuadRect());
    493 
    494   horizontal_scrollbar_layer_->SetVerticalAdjust(10.f);
    495   vertical_scrollbar_layer_->SetVerticalAdjust(10.f);
    496 
    497   // The vertical adjustment factor has two effects:
    498   // 1.) Moves the horizontal scrollbar down
    499   // 2.) Increases the vertical scrollbar's effective track length which both
    500   // increases the thumb's length and its position within the track.
    501   EXPECT_RECT_EQ(gfx::Rect(20.f, 10.f, 20.f, 3.f),
    502                  horizontal_scrollbar_layer_->ComputeThumbQuadRect());
    503   EXPECT_RECT_EQ(gfx::Rect(0.f, 22, 3.f, 22.f),
    504                  vertical_scrollbar_layer_->ComputeThumbQuadRect());
    505 }
    506 
    507 class ScrollbarLayerTestMaxTextureSize : public LayerTreeTest {
    508  public:
    509   ScrollbarLayerTestMaxTextureSize() {}
    510 
    511   void SetScrollbarBounds(const gfx::Size& bounds) { bounds_ = bounds; }
    512 
    513   virtual void BeginTest() OVERRIDE {
    514     scroll_layer_ = Layer::Create();
    515     layer_tree_host()->root_layer()->AddChild(scroll_layer_);
    516 
    517     scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
    518     scrollbar_layer_ =
    519         PaintedScrollbarLayer::Create(scrollbar.Pass(), scroll_layer_->id());
    520     scrollbar_layer_->SetScrollLayer(scroll_layer_->id());
    521     scrollbar_layer_->SetLayerTreeHost(layer_tree_host());
    522     scrollbar_layer_->SetBounds(bounds_);
    523     layer_tree_host()->root_layer()->AddChild(scrollbar_layer_);
    524 
    525     PostSetNeedsCommitToMainThread();
    526   }
    527 
    528   virtual void DidCommitAndDrawFrame() OVERRIDE {
    529     const int kMaxTextureSize =
    530         layer_tree_host()->GetRendererCapabilities().max_texture_size;
    531 
    532     // Check first that we're actually testing something.
    533     EXPECT_GT(scrollbar_layer_->bounds().width(), kMaxTextureSize);
    534 
    535     EXPECT_EQ(scrollbar_layer_->content_bounds().width(),
    536               kMaxTextureSize - 1);
    537     EXPECT_EQ(scrollbar_layer_->content_bounds().height(),
    538               kMaxTextureSize - 1);
    539 
    540     EndTest();
    541   }
    542 
    543   virtual void AfterTest() OVERRIDE {}
    544 
    545  private:
    546   scoped_refptr<PaintedScrollbarLayer> scrollbar_layer_;
    547   scoped_refptr<Layer> scroll_layer_;
    548   gfx::Size bounds_;
    549 };
    550 
    551 TEST_F(ScrollbarLayerTestMaxTextureSize, DirectRenderer) {
    552   scoped_ptr<TestWebGraphicsContext3D> context =
    553       TestWebGraphicsContext3D::Create();
    554   int max_size = 0;
    555   context->getIntegerv(GL_MAX_TEXTURE_SIZE, &max_size);
    556   SetScrollbarBounds(gfx::Size(max_size + 100, max_size + 100));
    557   RunTest(true, false, true);
    558 }
    559 
    560 TEST_F(ScrollbarLayerTestMaxTextureSize, DelegatingRenderer) {
    561   scoped_ptr<TestWebGraphicsContext3D> context =
    562       TestWebGraphicsContext3D::Create();
    563   int max_size = 0;
    564   context->getIntegerv(GL_MAX_TEXTURE_SIZE, &max_size);
    565   SetScrollbarBounds(gfx::Size(max_size + 100, max_size + 100));
    566   RunTest(true, true, true);
    567 }
    568 
    569 class MockLayerTreeHost : public LayerTreeHost {
    570  public:
    571   MockLayerTreeHost(FakeLayerTreeHostClient* client,
    572                     const LayerTreeSettings& settings)
    573       : LayerTreeHost(client, NULL, settings),
    574         next_id_(1),
    575         total_ui_resource_created_(0),
    576         total_ui_resource_deleted_(0) {
    577     InitializeSingleThreaded(client);
    578   }
    579 
    580   virtual UIResourceId CreateUIResource(UIResourceClient* content) OVERRIDE {
    581     total_ui_resource_created_++;
    582     UIResourceId nid = next_id_++;
    583     ui_resource_bitmap_map_.insert(
    584         std::make_pair(nid, content->GetBitmap(nid, false)));
    585     return nid;
    586   }
    587 
    588   // Deletes a UI resource.  May safely be called more than once.
    589   virtual void DeleteUIResource(UIResourceId id) OVERRIDE {
    590     UIResourceBitmapMap::iterator iter = ui_resource_bitmap_map_.find(id);
    591     if (iter != ui_resource_bitmap_map_.end()) {
    592       ui_resource_bitmap_map_.erase(iter);
    593       total_ui_resource_deleted_++;
    594     }
    595   }
    596 
    597   size_t UIResourceCount() { return ui_resource_bitmap_map_.size(); }
    598   int TotalUIResourceDeleted() { return total_ui_resource_deleted_; }
    599   int TotalUIResourceCreated() { return total_ui_resource_created_; }
    600 
    601   gfx::Size ui_resource_size(UIResourceId id) {
    602     UIResourceBitmapMap::iterator iter = ui_resource_bitmap_map_.find(id);
    603     if (iter != ui_resource_bitmap_map_.end())
    604       return iter->second.GetSize();
    605     return gfx::Size();
    606   }
    607 
    608   UIResourceBitmap* ui_resource_bitmap(UIResourceId id) {
    609     UIResourceBitmapMap::iterator iter = ui_resource_bitmap_map_.find(id);
    610     if (iter != ui_resource_bitmap_map_.end())
    611       return &iter->second;
    612     return NULL;
    613   }
    614 
    615  private:
    616   typedef base::hash_map<UIResourceId, UIResourceBitmap>
    617       UIResourceBitmapMap;
    618   UIResourceBitmapMap ui_resource_bitmap_map_;
    619 
    620   int next_id_;
    621   int total_ui_resource_created_;
    622   int total_ui_resource_deleted_;
    623 };
    624 
    625 
    626 class ScrollbarLayerTestResourceCreation : public testing::Test {
    627  public:
    628   ScrollbarLayerTestResourceCreation()
    629       : fake_client_(FakeLayerTreeHostClient::DIRECT_3D) {}
    630 
    631   void TestResourceUpload(int num_updates,
    632                           size_t expected_resources,
    633                           int expected_created,
    634                           int expected_deleted,
    635                           bool use_solid_color_scrollbar) {
    636     layer_tree_host_.reset(
    637         new MockLayerTreeHost(&fake_client_, layer_tree_settings_));
    638 
    639     scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, false));
    640     scoped_refptr<Layer> layer_tree_root = Layer::Create();
    641     scoped_refptr<Layer> content_layer = Layer::Create();
    642     scoped_refptr<Layer> scrollbar_layer;
    643     if (use_solid_color_scrollbar) {
    644       const int kThumbThickness = 3;
    645       const int kTrackStart = 0;
    646       const bool kIsLeftSideVerticalScrollbar = false;
    647       scrollbar_layer =
    648           SolidColorScrollbarLayer::Create(scrollbar->Orientation(),
    649                                            kThumbThickness,
    650                                            kTrackStart,
    651                                            kIsLeftSideVerticalScrollbar,
    652                                            layer_tree_root->id());
    653     } else {
    654       scrollbar_layer = PaintedScrollbarLayer::Create(scrollbar.Pass(),
    655                                                       layer_tree_root->id());
    656     }
    657     layer_tree_root->AddChild(content_layer);
    658     layer_tree_root->AddChild(scrollbar_layer);
    659 
    660     layer_tree_host_->SetRootLayer(layer_tree_root);
    661 
    662     scrollbar_layer->SetIsDrawable(true);
    663     scrollbar_layer->SetBounds(gfx::Size(100, 100));
    664     layer_tree_root->SetScrollOffset(gfx::Vector2d(10, 20));
    665     layer_tree_root->SetBounds(gfx::Size(100, 200));
    666     content_layer->SetBounds(gfx::Size(100, 200));
    667     scrollbar_layer->draw_properties().content_bounds = gfx::Size(100, 200);
    668     scrollbar_layer->draw_properties().visible_content_rect =
    669         gfx::Rect(0, 0, 100, 200);
    670     scrollbar_layer->CreateRenderSurface();
    671     scrollbar_layer->draw_properties().render_target = scrollbar_layer.get();
    672 
    673     testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
    674     EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get());
    675 
    676     ResourceUpdateQueue queue;
    677     gfx::Rect screen_space_clip_rect;
    678     OcclusionTracker<Layer> occlusion_tracker(screen_space_clip_rect);
    679 
    680     scrollbar_layer->SavePaintProperties();
    681     for (int update_counter = 0; update_counter < num_updates; update_counter++)
    682       scrollbar_layer->Update(&queue, &occlusion_tracker);
    683 
    684     // A non-solid-color scrollbar should have requested two textures.
    685     EXPECT_EQ(expected_resources, layer_tree_host_->UIResourceCount());
    686     EXPECT_EQ(expected_created, layer_tree_host_->TotalUIResourceCreated());
    687     EXPECT_EQ(expected_deleted, layer_tree_host_->TotalUIResourceDeleted());
    688 
    689     testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
    690 
    691     scrollbar_layer->ClearRenderSurface();
    692   }
    693 
    694  protected:
    695   FakeLayerTreeHostClient fake_client_;
    696   LayerTreeSettings layer_tree_settings_;
    697   scoped_ptr<MockLayerTreeHost> layer_tree_host_;
    698 };
    699 
    700 TEST_F(ScrollbarLayerTestResourceCreation, ResourceUpload) {
    701   bool use_solid_color_scrollbars = false;
    702   TestResourceUpload(0, 0, 0, 0, use_solid_color_scrollbars);
    703   int num_updates[3] = {1, 5, 10};
    704   for (int j = 0; j < 3; j++) {
    705     TestResourceUpload(num_updates[j],
    706                        2,
    707                        num_updates[j] * 2,
    708                        (num_updates[j] - 1) * 2,
    709                        use_solid_color_scrollbars);
    710   }
    711 }
    712 
    713 TEST_F(ScrollbarLayerTestResourceCreation, SolidColorNoResourceUpload) {
    714   bool use_solid_color_scrollbars = true;
    715   TestResourceUpload(0, 0, 0, 0, use_solid_color_scrollbars);
    716   TestResourceUpload(1, 0, 0, 0, use_solid_color_scrollbars);
    717 }
    718 
    719 class ScaledScrollbarLayerTestResourceCreation : public testing::Test {
    720  public:
    721   ScaledScrollbarLayerTestResourceCreation()
    722       : fake_client_(FakeLayerTreeHostClient::DIRECT_3D) {}
    723 
    724   void TestResourceUpload(const float test_scale) {
    725     layer_tree_host_.reset(
    726         new MockLayerTreeHost(&fake_client_, layer_tree_settings_));
    727 
    728     gfx::Point scrollbar_location(0, 185);
    729     scoped_refptr<Layer> layer_tree_root = Layer::Create();
    730     scoped_refptr<Layer> content_layer = Layer::Create();
    731     scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer =
    732         FakePaintedScrollbarLayer::Create(false, true, layer_tree_root->id());
    733 
    734     layer_tree_root->AddChild(content_layer);
    735     layer_tree_root->AddChild(scrollbar_layer);
    736 
    737     layer_tree_host_->SetRootLayer(layer_tree_root);
    738 
    739     scrollbar_layer->SetIsDrawable(true);
    740     scrollbar_layer->SetBounds(gfx::Size(100, 15));
    741     scrollbar_layer->SetPosition(scrollbar_location);
    742     layer_tree_root->SetBounds(gfx::Size(100, 200));
    743     content_layer->SetBounds(gfx::Size(100, 200));
    744     gfx::SizeF scaled_size =
    745         gfx::ScaleSize(scrollbar_layer->bounds(), test_scale, test_scale);
    746     gfx::PointF scaled_location =
    747         gfx::ScalePoint(scrollbar_layer->position(), test_scale, test_scale);
    748     scrollbar_layer->draw_properties().content_bounds =
    749         gfx::Size(scaled_size.width(), scaled_size.height());
    750     scrollbar_layer->draw_properties().contents_scale_x = test_scale;
    751     scrollbar_layer->draw_properties().contents_scale_y = test_scale;
    752     scrollbar_layer->draw_properties().visible_content_rect =
    753         gfx::Rect(scaled_location.x(),
    754                   scaled_location.y(),
    755                   scaled_size.width(),
    756                   scaled_size.height());
    757     scrollbar_layer->CreateRenderSurface();
    758     scrollbar_layer->draw_properties().render_target = scrollbar_layer.get();
    759 
    760     testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
    761     EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get());
    762 
    763     ResourceUpdateQueue queue;
    764     gfx::Rect screen_space_clip_rect;
    765     OcclusionTracker<Layer> occlusion_tracker(screen_space_clip_rect);
    766     scrollbar_layer->SavePaintProperties();
    767     scrollbar_layer->Update(&queue, &occlusion_tracker);
    768 
    769     // Verify that we have not generated any content uploads that are larger
    770     // than their destination textures.
    771 
    772     gfx::Size track_size = layer_tree_host_->ui_resource_size(
    773         scrollbar_layer->track_resource_id());
    774     gfx::Size thumb_size = layer_tree_host_->ui_resource_size(
    775         scrollbar_layer->thumb_resource_id());
    776 
    777     EXPECT_LE(track_size.width(), scrollbar_layer->content_bounds().width());
    778     EXPECT_LE(track_size.height(), scrollbar_layer->content_bounds().height());
    779     EXPECT_LE(thumb_size.width(), scrollbar_layer->content_bounds().width());
    780     EXPECT_LE(thumb_size.height(), scrollbar_layer->content_bounds().height());
    781 
    782     testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
    783 
    784     scrollbar_layer->ClearRenderSurface();
    785   }
    786 
    787  protected:
    788   FakeLayerTreeHostClient fake_client_;
    789   LayerTreeSettings layer_tree_settings_;
    790   scoped_ptr<MockLayerTreeHost> layer_tree_host_;
    791 };
    792 
    793 TEST_F(ScaledScrollbarLayerTestResourceCreation, ScaledResourceUpload) {
    794   // Pick a test scale that moves the scrollbar's (non-zero) position to
    795   // a non-pixel-aligned location.
    796   TestResourceUpload(.041f);
    797   TestResourceUpload(1.41f);
    798   TestResourceUpload(4.1f);
    799 }
    800 
    801 class ScaledScrollbarLayerTestScaledRasterization : public testing::Test {
    802  public:
    803   ScaledScrollbarLayerTestScaledRasterization()
    804       : fake_client_(FakeLayerTreeHostClient::DIRECT_3D) {}
    805 
    806   void TestScale(const gfx::Rect scrollbar_rect, const float test_scale) {
    807     layer_tree_host_.reset(
    808         new MockLayerTreeHost(&fake_client_, layer_tree_settings_));
    809 
    810     bool paint_during_update = true;
    811     bool has_thumb = false;
    812     scoped_refptr<Layer> layer_tree_root = Layer::Create();
    813     scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer =
    814         FakePaintedScrollbarLayer::Create(paint_during_update,
    815                                           has_thumb,
    816                                           layer_tree_root->id());
    817 
    818     layer_tree_root->AddChild(scrollbar_layer);
    819 
    820     layer_tree_host_->SetRootLayer(layer_tree_root);
    821 
    822     scrollbar_layer->SetBounds(scrollbar_rect.size());
    823     scrollbar_layer->SetPosition(scrollbar_rect.origin());
    824     scrollbar_layer->fake_scrollbar()->set_location(scrollbar_rect.origin());
    825     scrollbar_layer->fake_scrollbar()->set_track_rect(scrollbar_rect);
    826     gfx::SizeF scaled_size =
    827         gfx::ScaleSize(scrollbar_layer->bounds(), test_scale, test_scale);
    828     gfx::PointF scaled_location =
    829         gfx::ScalePoint(scrollbar_layer->position(), test_scale, test_scale);
    830     scrollbar_layer->draw_properties().content_bounds =
    831         gfx::Size(scaled_size.width(), scaled_size.height());
    832     scrollbar_layer->draw_properties().contents_scale_x = test_scale;
    833     scrollbar_layer->draw_properties().contents_scale_y = test_scale;
    834     scrollbar_layer->draw_properties().visible_content_rect =
    835         gfx::Rect(scaled_location.x(),
    836                   scaled_location.y(),
    837                   scaled_size.width(),
    838                   scaled_size.height());
    839 
    840     ResourceUpdateQueue queue;
    841     gfx::Rect screen_space_clip_rect;
    842     OcclusionTracker<Layer> occlusion_tracker(screen_space_clip_rect);
    843     scrollbar_layer->SavePaintProperties();
    844 
    845     scrollbar_layer->Update(&queue, &occlusion_tracker);
    846 
    847     UIResourceBitmap* bitmap = layer_tree_host_->ui_resource_bitmap(
    848         scrollbar_layer->track_resource_id());
    849 
    850     DCHECK(bitmap);
    851 
    852     AutoLockUIResourceBitmap locked_bitmap(*bitmap);
    853 
    854     const SkColor* pixels =
    855         reinterpret_cast<const SkColor*>(locked_bitmap.GetPixels());
    856     SkColor color = argb_to_skia(
    857         scrollbar_layer->fake_scrollbar()->paint_fill_color());
    858     int width = bitmap->GetSize().width();
    859     int height = bitmap->GetSize().height();
    860 
    861     // Make sure none of the corners of the bitmap were inadvertently clipped.
    862     EXPECT_EQ(color, pixels[0])
    863         << "Top left pixel doesn't match scrollbar color.";
    864 
    865     EXPECT_EQ(color, pixels[width - 1])
    866         << "Top right pixel doesn't match scrollbar color.";
    867 
    868     EXPECT_EQ(color, pixels[width * (height - 1)])
    869         << "Bottom left pixel doesn't match scrollbar color.";
    870 
    871     EXPECT_EQ(color, pixels[width * height - 1])
    872         << "Bottom right pixel doesn't match scrollbar color.";
    873   }
    874 
    875  protected:
    876   // On Android, Skia uses ABGR
    877   static SkColor argb_to_skia(SkColor c) {
    878       return (SkColorGetA(c) << SK_A32_SHIFT) |
    879              (SkColorGetR(c) << SK_R32_SHIFT) |
    880              (SkColorGetG(c) << SK_G32_SHIFT) |
    881              (SkColorGetB(c) << SK_B32_SHIFT);
    882   }
    883 
    884   FakeLayerTreeHostClient fake_client_;
    885   LayerTreeSettings layer_tree_settings_;
    886   scoped_ptr<MockLayerTreeHost> layer_tree_host_;
    887 };
    888 
    889 TEST_F(ScaledScrollbarLayerTestScaledRasterization, TestLostPrecisionInClip) {
    890   // Try rasterization at coordinates and scale that caused problematic
    891   // rounding and clipping errors.
    892   // Vertical Scrollbars.
    893   TestScale(gfx::Rect(1240, 0, 15, 1333), 2.7754839f);
    894   TestScale(gfx::Rect(1240, 0, 15, 677), 2.46677136f);
    895 
    896   // Horizontal Scrollbars.
    897   TestScale(gfx::Rect(0, 1240, 1333, 15), 2.7754839f);
    898   TestScale(gfx::Rect(0, 1240, 677, 15), 2.46677136f);
    899 }
    900 
    901 }  // namespace
    902 }  // namespace cc
    903