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