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