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