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