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(FakeLayerTreeHostClient* 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 InitializeSingleThreaded(client); 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 739