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