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