- add sources.
[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_quad_culler.h"
24 #include "cc/test/test_web_graphics_context_3d.h"
25 #include "cc/trees/layer_tree_host.h"
26 #include "cc/trees/layer_tree_impl.h"
27 #include "cc/trees/single_thread_proxy.h"
28 #include "cc/trees/tree_synchronizer.h"
29 #include "testing/gmock/include/gmock/gmock.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31
32 namespace cc {
33 namespace {
34
35 LayerImpl* 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
58 TEST(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
72 TEST(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
86 TEST(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
119 TEST(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
172 TEST(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
264 TEST(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
326 TEST(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
363 class 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
387 TEST_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
403 TEST_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
423 TEST_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
452 class 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
495 TEST_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
504 TEST_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
513 class MockLayerTreeHost : public LayerTreeHost {
514  public:
515   MockLayerTreeHost(LayerTreeHostClient* client,
516                     const LayerTreeSettings& settings)
517       : LayerTreeHost(client, NULL, 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
563 class 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
636 TEST_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
649 TEST_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
655 class 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
729 TEST_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