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