scrollbar_layer_unittest.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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/debug/test_web_graphics_context_3d.h"
8#include "cc/layers/append_quads_data.h"
9#include "cc/layers/painted_scrollbar_layer.h"
10#include "cc/layers/painted_scrollbar_layer_impl.h"
11#include "cc/layers/scrollbar_layer_interface.h"
12#include "cc/layers/solid_color_scrollbar_layer.h"
13#include "cc/layers/solid_color_scrollbar_layer_impl.h"
14#include "cc/quads/solid_color_draw_quad.h"
15#include "cc/resources/resource_update_queue.h"
16#include "cc/test/fake_impl_proxy.h"
17#include "cc/test/fake_layer_tree_host.h"
18#include "cc/test/fake_layer_tree_host_client.h"
19#include "cc/test/fake_layer_tree_host_impl.h"
20#include "cc/test/fake_painted_scrollbar_layer.h"
21#include "cc/test/fake_scrollbar.h"
22#include "cc/test/geometry_test_utils.h"
23#include "cc/test/layer_tree_test.h"
24#include "cc/test/mock_quad_culler.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->horizontal_scrollbar_layer(), cc_child2);
70}
71
72TEST(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
86TEST(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
119TEST(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
172TEST(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
264TEST(ScrollbarLayerTest, SolidColorDrawQuads) {
265  const int kThumbThickness = 3;
266  const int kTrackLength = 100;
267
268  LayerTreeSettings layer_tree_settings;
269  layer_tree_settings.solid_color_scrollbar_thickness_dip = kThumbThickness;
270  scoped_ptr<FakeLayerTreeHost> host =
271      FakeLayerTreeHost::Create(layer_tree_settings);
272
273  scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, true));
274  LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
275      host.get(), scrollbar.Pass(), false, true, kThumbThickness);
276  ScrollbarLayerImplBase* scrollbar_layer_impl =
277      static_cast<SolidColorScrollbarLayerImpl*>(
278          layer_impl_tree_root->children()[1]);
279  scrollbar_layer_impl->SetBounds(gfx::Size(kTrackLength, kThumbThickness));
280  scrollbar_layer_impl->SetCurrentPos(10.f);
281  scrollbar_layer_impl->SetMaximum(100);
282  scrollbar_layer_impl->SetVisibleToTotalLengthRatio(0.4f);
283
284  // Thickness should be overridden to 3.
285  {
286    MockQuadCuller quad_culler;
287    AppendQuadsData data;
288    scrollbar_layer_impl->AppendQuads(&quad_culler, &data);
289
290    const QuadList& quads = quad_culler.quad_list();
291    ASSERT_EQ(1u, quads.size());
292    EXPECT_EQ(DrawQuad::SOLID_COLOR, quads[0]->material);
293    EXPECT_RECT_EQ(gfx::Rect(6, 0, 40, 3), quads[0]->rect);
294  }
295
296  // Contents scale should scale the draw quad.
297  scrollbar_layer_impl->draw_properties().contents_scale_x = 2.f;
298  scrollbar_layer_impl->draw_properties().contents_scale_y = 2.f;
299  {
300    MockQuadCuller quad_culler;
301    AppendQuadsData data;
302    scrollbar_layer_impl->AppendQuads(&quad_culler, &data);
303
304    const QuadList& quads = quad_culler.quad_list();
305    ASSERT_EQ(1u, quads.size());
306    EXPECT_EQ(DrawQuad::SOLID_COLOR, quads[0]->material);
307    EXPECT_RECT_EQ(gfx::Rect(12, 0, 80, 6), quads[0]->rect);
308  }
309  scrollbar_layer_impl->draw_properties().contents_scale_x = 1.f;
310  scrollbar_layer_impl->draw_properties().contents_scale_y = 1.f;
311
312  // For solid color scrollbars, position and size should reflect the
313  // current viewport state.
314  scrollbar_layer_impl->SetVisibleToTotalLengthRatio(0.2f);
315  {
316    MockQuadCuller quad_culler;
317    AppendQuadsData data;
318    scrollbar_layer_impl->AppendQuads(&quad_culler, &data);
319
320    const QuadList& quads = quad_culler.quad_list();
321    ASSERT_EQ(1u, quads.size());
322    EXPECT_EQ(DrawQuad::SOLID_COLOR, quads[0]->material);
323    EXPECT_RECT_EQ(gfx::Rect(8, 0, 20, 3), quads[0]->rect);
324  }
325}
326
327TEST(ScrollbarLayerTest, LayerDrivenSolidColorDrawQuads) {
328  const int kThumbThickness = 3;
329  const int kTrackLength = 10;
330
331  LayerTreeSettings layer_tree_settings;
332  layer_tree_settings.solid_color_scrollbar_thickness_dip = 3;
333  scoped_ptr<FakeLayerTreeHost> host =
334      FakeLayerTreeHost::Create(layer_tree_settings);
335
336  scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, true));
337  LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
338      host.get(), scrollbar.Pass(), false, true, kThumbThickness);
339  ScrollbarLayerImplBase* scrollbar_layer_impl =
340      static_cast<PaintedScrollbarLayerImpl*>(
341          layer_impl_tree_root->children()[1]);
342
343  scrollbar_layer_impl->SetBounds(gfx::Size(kTrackLength, kThumbThickness));
344  scrollbar_layer_impl->SetCurrentPos(4.f);
345  scrollbar_layer_impl->SetMaximum(8);
346
347  layer_impl_tree_root->SetScrollable(true);
348  layer_impl_tree_root->SetHorizontalScrollbarLayer(scrollbar_layer_impl);
349  layer_impl_tree_root->SetMaxScrollOffset(gfx::Vector2d(8, 8));
350  layer_impl_tree_root->SetBounds(gfx::Size(2, 2));
351  layer_impl_tree_root->ScrollBy(gfx::Vector2dF(4.f, 0.f));
352
353  {
354    MockQuadCuller quad_culler;
355    AppendQuadsData data;
356    scrollbar_layer_impl->AppendQuads(&quad_culler, &data);
357
358    const QuadList& quads = quad_culler.quad_list();
359    ASSERT_EQ(1u, quads.size());
360    EXPECT_EQ(DrawQuad::SOLID_COLOR, quads[0]->material);
361    EXPECT_RECT_EQ(gfx::Rect(3, 0, 3, 3), quads[0]->rect);
362  }
363}
364
365class ScrollbarLayerSolidColorThumbTest : public testing::Test {
366 public:
367  ScrollbarLayerSolidColorThumbTest() {
368    LayerTreeSettings layer_tree_settings;
369    layer_tree_settings.solid_color_scrollbars = true;
370    host_impl_.reset(new FakeLayerTreeHostImpl(layer_tree_settings, &proxy_));
371
372    const int kThumbThickness = 3;
373    const bool kIsLeftSideVerticalScrollbar = false;
374
375    horizontal_scrollbar_layer_ = SolidColorScrollbarLayerImpl::Create(
376        host_impl_->active_tree(), 1, HORIZONTAL, kThumbThickness,
377        kIsLeftSideVerticalScrollbar);
378    vertical_scrollbar_layer_ = SolidColorScrollbarLayerImpl::Create(
379        host_impl_->active_tree(), 2, VERTICAL, kThumbThickness,
380        kIsLeftSideVerticalScrollbar);
381  }
382
383 protected:
384  FakeImplProxy proxy_;
385  scoped_ptr<FakeLayerTreeHostImpl> host_impl_;
386  scoped_ptr<SolidColorScrollbarLayerImpl> horizontal_scrollbar_layer_;
387  scoped_ptr<SolidColorScrollbarLayerImpl> vertical_scrollbar_layer_;
388};
389
390TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbLength) {
391  horizontal_scrollbar_layer_->SetCurrentPos(0);
392  horizontal_scrollbar_layer_->SetMaximum(10);
393
394  // Simple case - one third of the scrollable area is visible, so the thumb
395  // should be one third as long as the track.
396  horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.33f);
397  horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
398  EXPECT_EQ(33, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width());
399
400  // The thumb's length should never be less than its thickness.
401  horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.01f);
402  horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
403  EXPECT_EQ(3, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width());
404}
405
406TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbPosition) {
407  horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
408  horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.1f);
409
410  horizontal_scrollbar_layer_->SetCurrentPos(0);
411  horizontal_scrollbar_layer_->SetMaximum(100);
412  EXPECT_EQ(0, horizontal_scrollbar_layer_->ComputeThumbQuadRect().x());
413  EXPECT_EQ(10, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width());
414
415  horizontal_scrollbar_layer_->SetCurrentPos(100);
416  // The thumb is 10px long and the track is 100px, so the maximum thumb
417  // position is 90px.
418  EXPECT_EQ(90, horizontal_scrollbar_layer_->ComputeThumbQuadRect().x());
419
420  horizontal_scrollbar_layer_->SetCurrentPos(80);
421  // The scroll position is 80% of the maximum, so the thumb's position should
422  // be at 80% of its maximum or 72px.
423  EXPECT_EQ(72, horizontal_scrollbar_layer_->ComputeThumbQuadRect().x());
424}
425
426TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbVerticalAdjust) {
427  SolidColorScrollbarLayerImpl* layers[2] =
428      { horizontal_scrollbar_layer_.get(), vertical_scrollbar_layer_.get() };
429  for (size_t i = 0; i < 2; ++i) {
430    layers[i]->SetVisibleToTotalLengthRatio(0.2f);
431    layers[i]->SetCurrentPos(25);
432    layers[i]->SetMaximum(100);
433  }
434  layers[0]->SetBounds(gfx::Size(100, 3));
435  layers[1]->SetBounds(gfx::Size(3, 100));
436
437  EXPECT_RECT_EQ(gfx::RectF(20.f, 0.f, 20.f, 3.f),
438                 horizontal_scrollbar_layer_->ComputeThumbQuadRect());
439  EXPECT_RECT_EQ(gfx::RectF(0.f, 20.f, 3.f, 20.f),
440                 vertical_scrollbar_layer_->ComputeThumbQuadRect());
441
442  horizontal_scrollbar_layer_->SetVerticalAdjust(10.f);
443  vertical_scrollbar_layer_->SetVerticalAdjust(10.f);
444
445  // The vertical adjustment factor has two effects:
446  // 1.) Moves the horizontal scrollbar down
447  // 2.) Increases the vertical scrollbar's effective track length which both
448  // increases the thumb's length and its position within the track.
449  EXPECT_RECT_EQ(gfx::Rect(20.f, 10.f, 20.f, 3.f),
450                 horizontal_scrollbar_layer_->ComputeThumbQuadRect());
451  EXPECT_RECT_EQ(gfx::Rect(0.f, 22, 3.f, 22.f),
452                 vertical_scrollbar_layer_->ComputeThumbQuadRect());
453}
454
455class ScrollbarLayerTestMaxTextureSize : public LayerTreeTest {
456 public:
457  ScrollbarLayerTestMaxTextureSize() {}
458
459  void SetScrollbarBounds(gfx::Size bounds) { bounds_ = bounds; }
460
461  virtual void BeginTest() OVERRIDE {
462    scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
463    scrollbar_layer_ = PaintedScrollbarLayer::Create(scrollbar.Pass(), 1);
464    scrollbar_layer_->SetLayerTreeHost(layer_tree_host());
465    scrollbar_layer_->SetBounds(bounds_);
466    layer_tree_host()->root_layer()->AddChild(scrollbar_layer_);
467
468    scroll_layer_ = Layer::Create();
469    scrollbar_layer_->SetScrollLayerId(scroll_layer_->id());
470    layer_tree_host()->root_layer()->AddChild(scroll_layer_);
471
472    PostSetNeedsCommitToMainThread();
473  }
474
475  virtual void DidCommitAndDrawFrame() OVERRIDE {
476    const int kMaxTextureSize =
477        layer_tree_host()->GetRendererCapabilities().max_texture_size;
478
479    // Check first that we're actually testing something.
480    EXPECT_GT(scrollbar_layer_->bounds().width(), kMaxTextureSize);
481
482    EXPECT_EQ(scrollbar_layer_->content_bounds().width(),
483              kMaxTextureSize - 1);
484    EXPECT_EQ(scrollbar_layer_->content_bounds().height(),
485              kMaxTextureSize - 1);
486
487    EndTest();
488  }
489
490  virtual void AfterTest() OVERRIDE {}
491
492 private:
493  scoped_refptr<PaintedScrollbarLayer> scrollbar_layer_;
494  scoped_refptr<Layer> scroll_layer_;
495  gfx::Size bounds_;
496};
497
498TEST_F(ScrollbarLayerTestMaxTextureSize, DirectRenderer) {
499  scoped_ptr<TestWebGraphicsContext3D> context =
500      TestWebGraphicsContext3D::Create();
501  int max_size = 0;
502  context->getIntegerv(GL_MAX_TEXTURE_SIZE, &max_size);
503  SetScrollbarBounds(gfx::Size(max_size + 100, max_size + 100));
504  RunTest(true, false, true);
505}
506
507TEST_F(ScrollbarLayerTestMaxTextureSize, DelegatingRenderer) {
508  scoped_ptr<TestWebGraphicsContext3D> context =
509      TestWebGraphicsContext3D::Create();
510  int max_size = 0;
511  context->getIntegerv(GL_MAX_TEXTURE_SIZE, &max_size);
512  SetScrollbarBounds(gfx::Size(max_size + 100, max_size + 100));
513  RunTest(true, true, true);
514}
515
516class MockLayerTreeHost : public LayerTreeHost {
517 public:
518  MockLayerTreeHost(LayerTreeHostClient* client,
519                    const LayerTreeSettings& settings)
520      : LayerTreeHost(client, settings),
521        next_id_(1),
522        total_ui_resource_created_(0),
523        total_ui_resource_deleted_(0) {
524    Initialize(NULL);
525  }
526
527  virtual UIResourceId CreateUIResource(UIResourceClient* content) OVERRIDE {
528    total_ui_resource_created_++;
529    UIResourceId nid = next_id_++;
530    ui_resource_bitmap_map_.insert(
531        std::make_pair(nid, content->GetBitmap(nid, false)));
532    return nid;
533  }
534
535  // Deletes a UI resource.  May safely be called more than once.
536  virtual void DeleteUIResource(UIResourceId id) OVERRIDE {
537    UIResourceBitmapMap::iterator iter = ui_resource_bitmap_map_.find(id);
538    if (iter != ui_resource_bitmap_map_.end()) {
539      ui_resource_bitmap_map_.erase(iter);
540      total_ui_resource_deleted_++;
541    }
542  }
543
544  size_t UIResourceCount() { return ui_resource_bitmap_map_.size(); }
545  int TotalUIResourceDeleted() { return total_ui_resource_deleted_; }
546  int TotalUIResourceCreated() { return total_ui_resource_created_; }
547
548  gfx::Size ui_resource_size(UIResourceId id) {
549    UIResourceBitmapMap::iterator iter = ui_resource_bitmap_map_.find(id);
550    if (iter != ui_resource_bitmap_map_.end())
551      return iter->second.GetSize();
552    return gfx::Size();
553  }
554
555 private:
556  typedef base::hash_map<UIResourceId, UIResourceBitmap>
557      UIResourceBitmapMap;
558  UIResourceBitmapMap ui_resource_bitmap_map_;
559
560  int next_id_;
561  int total_ui_resource_created_;
562  int total_ui_resource_deleted_;
563};
564
565
566class ScrollbarLayerTestResourceCreation : public testing::Test {
567 public:
568  ScrollbarLayerTestResourceCreation()
569      : fake_client_(FakeLayerTreeHostClient::DIRECT_3D) {}
570
571  void TestResourceUpload(int num_updates,
572                          size_t expected_resources,
573                          int expected_created,
574                          int expected_deleted,
575                          bool use_solid_color_scrollbar) {
576    layer_tree_host_.reset(
577        new MockLayerTreeHost(&fake_client_, layer_tree_settings_));
578
579    scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, false));
580    scoped_refptr<Layer> layer_tree_root = Layer::Create();
581    scoped_refptr<Layer> content_layer = Layer::Create();
582    scoped_refptr<Layer> scrollbar_layer;
583    if (use_solid_color_scrollbar) {
584      const int kThumbThickness = 3;
585      const bool kIsLeftSideVerticalScrollbar = false;
586      scrollbar_layer =
587          SolidColorScrollbarLayer::Create(scrollbar->Orientation(),
588                                           kThumbThickness,
589                                           kIsLeftSideVerticalScrollbar,
590                                           layer_tree_root->id());
591    } else {
592      scrollbar_layer = PaintedScrollbarLayer::Create(scrollbar.Pass(),
593                                                      layer_tree_root->id());
594    }
595    layer_tree_root->AddChild(content_layer);
596    layer_tree_root->AddChild(scrollbar_layer);
597
598    layer_tree_host_->InitializeOutputSurfaceIfNeeded();
599    layer_tree_host_->SetRootLayer(layer_tree_root);
600
601    scrollbar_layer->SetIsDrawable(true);
602    scrollbar_layer->SetBounds(gfx::Size(100, 100));
603    layer_tree_root->SetScrollOffset(gfx::Vector2d(10, 20));
604    layer_tree_root->SetMaxScrollOffset(gfx::Vector2d(30, 50));
605    layer_tree_root->SetBounds(gfx::Size(100, 200));
606    content_layer->SetBounds(gfx::Size(100, 200));
607    scrollbar_layer->draw_properties().content_bounds = gfx::Size(100, 200);
608    scrollbar_layer->draw_properties().visible_content_rect =
609        gfx::Rect(0, 0, 100, 200);
610    scrollbar_layer->CreateRenderSurface();
611    scrollbar_layer->draw_properties().render_target = scrollbar_layer.get();
612
613    testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
614    EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get());
615
616    ResourceUpdateQueue queue;
617    OcclusionTracker occlusion_tracker(gfx::Rect(), false);
618
619    scrollbar_layer->SavePaintProperties();
620    for (int update_counter = 0; update_counter < num_updates; update_counter++)
621      scrollbar_layer->Update(&queue, &occlusion_tracker);
622
623    // A non-solid-color scrollbar should have requested two textures.
624    EXPECT_EQ(expected_resources, layer_tree_host_->UIResourceCount());
625    EXPECT_EQ(expected_created, layer_tree_host_->TotalUIResourceCreated());
626    EXPECT_EQ(expected_deleted, layer_tree_host_->TotalUIResourceDeleted());
627
628    testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
629
630    scrollbar_layer->ClearRenderSurface();
631  }
632
633 protected:
634  FakeLayerTreeHostClient fake_client_;
635  LayerTreeSettings layer_tree_settings_;
636  scoped_ptr<MockLayerTreeHost> layer_tree_host_;
637};
638
639TEST_F(ScrollbarLayerTestResourceCreation, ResourceUpload) {
640  bool use_solid_color_scrollbars = false;
641  TestResourceUpload(0, 0, 0, 0, use_solid_color_scrollbars);
642  int num_updates[3] = {1, 5, 10};
643  for (int j = 0; j < 3; j++) {
644    TestResourceUpload(num_updates[j],
645                       2,
646                       num_updates[j] * 2,
647                       (num_updates[j] - 1) * 2,
648                       use_solid_color_scrollbars);
649  }
650}
651
652TEST_F(ScrollbarLayerTestResourceCreation, SolidColorNoResourceUpload) {
653  bool use_solid_color_scrollbars = true;
654  TestResourceUpload(0, 0, 0, 0, use_solid_color_scrollbars);
655  TestResourceUpload(1, 0, 0, 0, use_solid_color_scrollbars);
656}
657
658class ScaledScrollbarLayerTestResourceCreation : public testing::Test {
659 public:
660  ScaledScrollbarLayerTestResourceCreation()
661      : fake_client_(FakeLayerTreeHostClient::DIRECT_3D) {}
662
663  void TestResourceUpload(const float test_scale) {
664    layer_tree_host_.reset(
665        new MockLayerTreeHost(&fake_client_, layer_tree_settings_));
666
667    gfx::Point scrollbar_location(0, 185);
668    scoped_refptr<Layer> layer_tree_root = Layer::Create();
669    scoped_refptr<Layer> content_layer = Layer::Create();
670    scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer =
671        FakePaintedScrollbarLayer::Create(false, true, layer_tree_root->id());
672
673    layer_tree_root->AddChild(content_layer);
674    layer_tree_root->AddChild(scrollbar_layer);
675
676    layer_tree_host_->InitializeOutputSurfaceIfNeeded();
677    layer_tree_host_->SetRootLayer(layer_tree_root);
678
679    scrollbar_layer->SetIsDrawable(true);
680    scrollbar_layer->SetBounds(gfx::Size(100, 15));
681    scrollbar_layer->SetPosition(scrollbar_location);
682    layer_tree_root->SetBounds(gfx::Size(100, 200));
683    content_layer->SetBounds(gfx::Size(100, 200));
684    gfx::SizeF scaled_size =
685        gfx::ScaleSize(scrollbar_layer->bounds(), test_scale, test_scale);
686    gfx::PointF scaled_location =
687        gfx::ScalePoint(scrollbar_layer->position(), test_scale, test_scale);
688    scrollbar_layer->draw_properties().content_bounds =
689        gfx::Size(scaled_size.width(), scaled_size.height());
690    scrollbar_layer->draw_properties().contents_scale_x = test_scale;
691    scrollbar_layer->draw_properties().contents_scale_y = test_scale;
692    scrollbar_layer->draw_properties().visible_content_rect =
693        gfx::Rect(scaled_location.x(),
694                  scaled_location.y(),
695                  scaled_size.width(),
696                  scaled_size.height());
697    scrollbar_layer->CreateRenderSurface();
698    scrollbar_layer->draw_properties().render_target = scrollbar_layer.get();
699
700    testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
701    EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get());
702
703    ResourceUpdateQueue queue;
704    OcclusionTracker occlusion_tracker(gfx::Rect(), false);
705    scrollbar_layer->SavePaintProperties();
706    scrollbar_layer->Update(&queue, &occlusion_tracker);
707
708    // Verify that we have not generated any content uploads that are larger
709    // than their destination textures.
710
711    gfx::Size track_size = layer_tree_host_->ui_resource_size(
712        scrollbar_layer->track_resource_id());
713    gfx::Size thumb_size = layer_tree_host_->ui_resource_size(
714        scrollbar_layer->thumb_resource_id());
715
716    EXPECT_LE(track_size.width(), scrollbar_layer->content_bounds().width());
717    EXPECT_LE(track_size.height(), scrollbar_layer->content_bounds().height());
718    EXPECT_LE(thumb_size.width(), scrollbar_layer->content_bounds().width());
719    EXPECT_LE(thumb_size.height(), scrollbar_layer->content_bounds().height());
720
721    testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
722
723    scrollbar_layer->ClearRenderSurface();
724  }
725
726 protected:
727  FakeLayerTreeHostClient fake_client_;
728  LayerTreeSettings layer_tree_settings_;
729  scoped_ptr<MockLayerTreeHost> layer_tree_host_;
730};
731
732TEST_F(ScaledScrollbarLayerTestResourceCreation, ScaledResourceUpload) {
733  // Pick a test scale that moves the scrollbar's (non-zero) position to
734  // a non-pixel-aligned location.
735  TestResourceUpload(.041f);
736  TestResourceUpload(1.41f);
737  TestResourceUpload(4.1f);
738}
739
740}  // namespace
741}  // namespace cc
742