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