1 // Copyright (c) 2013 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/command_line.h" 6 #include "base/message_loop/message_loop_proxy.h" 7 #include "base/path_service.h" 8 #include "base/run_loop.h" 9 #include "content/browser/gpu/compositor_util.h" 10 #include "content/browser/gpu/gpu_data_manager_impl.h" 11 #include "content/browser/renderer_host/dip_util.h" 12 #include "content/browser/renderer_host/render_widget_host_impl.h" 13 #include "content/browser/renderer_host/render_widget_host_view_base.h" 14 #include "content/public/browser/gpu_data_manager.h" 15 #include "content/public/browser/render_view_host.h" 16 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" 17 #include "content/public/browser/web_contents.h" 18 #include "content/public/common/content_paths.h" 19 #include "content/public/common/content_switches.h" 20 #include "content/public/common/url_constants.h" 21 #include "content/public/test/browser_test_utils.h" 22 #include "content/public/test/content_browser_test.h" 23 #include "content/public/test/content_browser_test_utils.h" 24 #include "content/shell/browser/shell.h" 25 #include "media/base/video_frame.h" 26 #include "media/filters/skcanvas_video_renderer.h" 27 #include "net/base/filename_util.h" 28 #include "third_party/skia/include/core/SkBitmap.h" 29 #include "third_party/skia/include/core/SkCanvas.h" 30 #include "ui/base/layout.h" 31 #include "ui/base/ui_base_switches.h" 32 #include "ui/gfx/geometry/size_conversions.h" 33 #include "ui/gfx/switches.h" 34 #include "ui/gl/gl_switches.h" 35 36 #if defined(OS_WIN) 37 #include "base/win/windows_version.h" 38 #include "ui/gfx/win/dpi.h" 39 #endif 40 41 namespace content { 42 namespace { 43 44 // Convenience macro: Short-circuit a pass for the tests where platform support 45 // for forced-compositing mode (or disabled-compositing mode) is lacking. 46 #define SET_UP_SURFACE_OR_PASS_TEST(wait_message) \ 47 if (!SetUpSourceSurface(wait_message)) { \ 48 LOG(WARNING) \ 49 << ("Blindly passing this test: This platform does not support " \ 50 "forced compositing (or forced-disabled compositing) mode."); \ 51 return; \ 52 } 53 54 // Common base class for browser tests. This is subclassed twice: Once to test 55 // the browser in forced-compositing mode, and once to test with compositing 56 // mode disabled. 57 class RenderWidgetHostViewBrowserTest : public ContentBrowserTest { 58 public: 59 RenderWidgetHostViewBrowserTest() 60 : frame_size_(400, 300), 61 callback_invoke_count_(0), 62 frames_captured_(0) {} 63 64 virtual void SetUpOnMainThread() OVERRIDE { 65 ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &test_dir_)); 66 } 67 68 // Attempts to set up the source surface. Returns false if unsupported on the 69 // current platform. 70 virtual bool SetUpSourceSurface(const char* wait_message) = 0; 71 72 int callback_invoke_count() const { 73 return callback_invoke_count_; 74 } 75 76 int frames_captured() const { 77 return frames_captured_; 78 } 79 80 const gfx::Size& frame_size() const { 81 return frame_size_; 82 } 83 84 const base::FilePath& test_dir() const { 85 return test_dir_; 86 } 87 88 RenderViewHost* GetRenderViewHost() const { 89 RenderViewHost* const rvh = shell()->web_contents()->GetRenderViewHost(); 90 CHECK(rvh); 91 return rvh; 92 } 93 94 RenderWidgetHostImpl* GetRenderWidgetHost() const { 95 RenderWidgetHostImpl* const rwh = RenderWidgetHostImpl::From( 96 shell()->web_contents()->GetRenderWidgetHostView()-> 97 GetRenderWidgetHost()); 98 CHECK(rwh); 99 return rwh; 100 } 101 102 RenderWidgetHostViewBase* GetRenderWidgetHostView() const { 103 return static_cast<RenderWidgetHostViewBase*>( 104 GetRenderViewHost()->GetView()); 105 } 106 107 // Callback when using CopyFromBackingStore() API. 108 void FinishCopyFromBackingStore(const base::Closure& quit_closure, 109 bool frame_captured, 110 const SkBitmap& bitmap) { 111 ++callback_invoke_count_; 112 if (frame_captured) { 113 ++frames_captured_; 114 EXPECT_FALSE(bitmap.empty()); 115 } 116 if (!quit_closure.is_null()) 117 quit_closure.Run(); 118 } 119 120 // Callback when using CopyFromCompositingSurfaceToVideoFrame() API. 121 void FinishCopyFromCompositingSurface(const base::Closure& quit_closure, 122 bool frame_captured) { 123 ++callback_invoke_count_; 124 if (frame_captured) 125 ++frames_captured_; 126 if (!quit_closure.is_null()) 127 quit_closure.Run(); 128 } 129 130 // Callback when using frame subscriber API. 131 void FrameDelivered(const scoped_refptr<base::MessageLoopProxy>& loop, 132 base::Closure quit_closure, 133 base::TimeTicks timestamp, 134 bool frame_captured) { 135 ++callback_invoke_count_; 136 if (frame_captured) 137 ++frames_captured_; 138 if (!quit_closure.is_null()) 139 loop->PostTask(FROM_HERE, quit_closure); 140 } 141 142 // Copy one frame using the CopyFromBackingStore API. 143 void RunBasicCopyFromBackingStoreTest() { 144 SET_UP_SURFACE_OR_PASS_TEST(NULL); 145 146 // Repeatedly call CopyFromBackingStore() since, on some platforms (e.g., 147 // Windows), the operation will fail until the first "present" has been 148 // made. 149 int count_attempts = 0; 150 while (true) { 151 ++count_attempts; 152 base::RunLoop run_loop; 153 GetRenderViewHost()->CopyFromBackingStore( 154 gfx::Rect(), 155 frame_size(), 156 base::Bind( 157 &RenderWidgetHostViewBrowserTest::FinishCopyFromBackingStore, 158 base::Unretained(this), 159 run_loop.QuitClosure()), 160 kN32_SkColorType); 161 run_loop.Run(); 162 163 if (frames_captured()) 164 break; 165 else 166 GiveItSomeTime(); 167 } 168 169 EXPECT_EQ(count_attempts, callback_invoke_count()); 170 EXPECT_EQ(1, frames_captured()); 171 } 172 173 protected: 174 // Waits until the source is available for copying. 175 void WaitForCopySourceReady() { 176 while (!GetRenderWidgetHostView()->IsSurfaceAvailableForCopy()) 177 GiveItSomeTime(); 178 } 179 180 // Run the current message loop for a short time without unwinding the current 181 // call stack. 182 static void GiveItSomeTime() { 183 base::RunLoop run_loop; 184 base::MessageLoop::current()->PostDelayedTask( 185 FROM_HERE, 186 run_loop.QuitClosure(), 187 base::TimeDelta::FromMilliseconds(10)); 188 run_loop.Run(); 189 } 190 191 private: 192 const gfx::Size frame_size_; 193 base::FilePath test_dir_; 194 int callback_invoke_count_; 195 int frames_captured_; 196 }; 197 198 enum CompositingMode { 199 GL_COMPOSITING, 200 SOFTWARE_COMPOSITING, 201 }; 202 203 class CompositingRenderWidgetHostViewBrowserTest 204 : public RenderWidgetHostViewBrowserTest, 205 public testing::WithParamInterface<CompositingMode> { 206 public: 207 explicit CompositingRenderWidgetHostViewBrowserTest() 208 : compositing_mode_(GetParam()) {} 209 210 virtual void SetUp() OVERRIDE { 211 if (compositing_mode_ == SOFTWARE_COMPOSITING) 212 UseSoftwareCompositing(); 213 RenderWidgetHostViewBrowserTest::SetUp(); 214 } 215 216 virtual GURL TestUrl() { 217 return net::FilePathToFileURL( 218 test_dir().AppendASCII("rwhv_compositing_animation.html")); 219 } 220 221 virtual bool SetUpSourceSurface(const char* wait_message) OVERRIDE { 222 content::DOMMessageQueue message_queue; 223 NavigateToURL(shell(), TestUrl()); 224 if (wait_message != NULL) { 225 std::string result(wait_message); 226 if (!message_queue.WaitForMessage(&result)) { 227 EXPECT_TRUE(false) << "WaitForMessage " << result << " failed."; 228 return false; 229 } 230 } 231 232 // A frame might not be available yet. So, wait for it. 233 WaitForCopySourceReady(); 234 return true; 235 } 236 237 private: 238 const CompositingMode compositing_mode_; 239 240 DISALLOW_COPY_AND_ASSIGN(CompositingRenderWidgetHostViewBrowserTest); 241 }; 242 243 class FakeFrameSubscriber : public RenderWidgetHostViewFrameSubscriber { 244 public: 245 FakeFrameSubscriber( 246 RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback) 247 : callback_(callback) { 248 } 249 250 virtual bool ShouldCaptureFrame(const gfx::Rect& damage_rect, 251 base::TimeTicks present_time, 252 scoped_refptr<media::VideoFrame>* storage, 253 DeliverFrameCallback* callback) OVERRIDE { 254 // Only allow one frame capture to be made. Otherwise, the compositor could 255 // start multiple captures, unbounded, and eventually its own limiter logic 256 // will begin invoking |callback| with a |false| result. This flakes out 257 // the unit tests, since they receive a "failed" callback before the later 258 // "success" callbacks. 259 if (callback_.is_null()) 260 return false; 261 *storage = media::VideoFrame::CreateBlackFrame(gfx::Size(100, 100)); 262 *callback = callback_; 263 callback_.Reset(); 264 return true; 265 } 266 267 private: 268 DeliverFrameCallback callback_; 269 }; 270 271 // Disable tests for Android and IOS as these platforms have incomplete 272 // implementation. 273 #if !defined(OS_ANDROID) && !defined(OS_IOS) 274 275 // The CopyFromBackingStore() API should work on all platforms when compositing 276 // is enabled. 277 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTest, 278 CopyFromBackingStore) { 279 RunBasicCopyFromBackingStoreTest(); 280 } 281 282 // Tests that the callback passed to CopyFromBackingStore is always called, 283 // even when the RenderWidgetHost is deleting in the middle of an async copy. 284 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTest, 285 CopyFromBackingStore_CallbackDespiteDelete) { 286 SET_UP_SURFACE_OR_PASS_TEST(NULL); 287 288 base::RunLoop run_loop; 289 GetRenderViewHost()->CopyFromBackingStore( 290 gfx::Rect(), 291 frame_size(), 292 base::Bind(&RenderWidgetHostViewBrowserTest::FinishCopyFromBackingStore, 293 base::Unretained(this), 294 run_loop.QuitClosure()), 295 kN32_SkColorType); 296 // Delete the surface before the callback is run. 297 GetRenderWidgetHostView()->AcceleratedSurfaceRelease(); 298 run_loop.Run(); 299 300 EXPECT_EQ(1, callback_invoke_count()); 301 } 302 303 // Tests that the callback passed to CopyFromCompositingSurfaceToVideoFrame is 304 // always called, even when the RenderWidgetHost is deleting in the middle of 305 // an async copy. 306 // 307 // Test is flaky on Win. http://crbug.com/276783 308 #if defined(OS_WIN) || (defined(OS_CHROMEOS) && !defined(NDEBUG)) 309 #define MAYBE_CopyFromCompositingSurface_CallbackDespiteDelete \ 310 DISABLED_CopyFromCompositingSurface_CallbackDespiteDelete 311 #else 312 #define MAYBE_CopyFromCompositingSurface_CallbackDespiteDelete \ 313 CopyFromCompositingSurface_CallbackDespiteDelete 314 #endif 315 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTest, 316 MAYBE_CopyFromCompositingSurface_CallbackDespiteDelete) { 317 SET_UP_SURFACE_OR_PASS_TEST(NULL); 318 RenderWidgetHostViewBase* const view = GetRenderWidgetHostView(); 319 if (!view->CanCopyToVideoFrame()) { 320 LOG(WARNING) << 321 ("Blindly passing this test: CopyFromCompositingSurfaceToVideoFrame() " 322 "not supported on this platform."); 323 return; 324 } 325 326 base::RunLoop run_loop; 327 scoped_refptr<media::VideoFrame> dest = 328 media::VideoFrame::CreateBlackFrame(frame_size()); 329 view->CopyFromCompositingSurfaceToVideoFrame( 330 gfx::Rect(view->GetViewBounds().size()), dest, base::Bind( 331 &RenderWidgetHostViewBrowserTest::FinishCopyFromCompositingSurface, 332 base::Unretained(this), run_loop.QuitClosure())); 333 // Delete the surface before the callback is run. 334 view->AcceleratedSurfaceRelease(); 335 run_loop.Run(); 336 337 EXPECT_EQ(1, callback_invoke_count()); 338 } 339 340 // Test basic frame subscription functionality. We subscribe, and then run 341 // until at least one DeliverFrameCallback has been invoked. 342 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTest, 343 FrameSubscriberTest) { 344 SET_UP_SURFACE_OR_PASS_TEST(NULL); 345 RenderWidgetHostViewBase* const view = GetRenderWidgetHostView(); 346 if (!view->CanSubscribeFrame()) { 347 LOG(WARNING) << ("Blindly passing this test: Frame subscription not " 348 "supported on this platform."); 349 return; 350 } 351 352 base::RunLoop run_loop; 353 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber( 354 new FakeFrameSubscriber( 355 base::Bind(&RenderWidgetHostViewBrowserTest::FrameDelivered, 356 base::Unretained(this), 357 base::MessageLoopProxy::current(), 358 run_loop.QuitClosure()))); 359 view->BeginFrameSubscription(subscriber.Pass()); 360 run_loop.Run(); 361 view->EndFrameSubscription(); 362 363 EXPECT_LE(1, callback_invoke_count()); 364 EXPECT_LE(1, frames_captured()); 365 } 366 367 // Test that we can copy twice from an accelerated composited page. 368 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTest, CopyTwice) { 369 SET_UP_SURFACE_OR_PASS_TEST(NULL); 370 RenderWidgetHostViewBase* const view = GetRenderWidgetHostView(); 371 if (!view->CanCopyToVideoFrame()) { 372 LOG(WARNING) << ("Blindly passing this test: " 373 "CopyFromCompositingSurfaceToVideoFrame() not supported " 374 "on this platform."); 375 return; 376 } 377 378 base::RunLoop run_loop; 379 scoped_refptr<media::VideoFrame> first_output = 380 media::VideoFrame::CreateBlackFrame(frame_size()); 381 ASSERT_TRUE(first_output.get()); 382 scoped_refptr<media::VideoFrame> second_output = 383 media::VideoFrame::CreateBlackFrame(frame_size()); 384 ASSERT_TRUE(second_output.get()); 385 view->CopyFromCompositingSurfaceToVideoFrame( 386 gfx::Rect(view->GetViewBounds().size()), 387 first_output, 388 base::Bind(&RenderWidgetHostViewBrowserTest::FrameDelivered, 389 base::Unretained(this), 390 base::MessageLoopProxy::current(), 391 base::Closure(), 392 base::TimeTicks::Now())); 393 view->CopyFromCompositingSurfaceToVideoFrame( 394 gfx::Rect(view->GetViewBounds().size()), 395 second_output, 396 base::Bind(&RenderWidgetHostViewBrowserTest::FrameDelivered, 397 base::Unretained(this), 398 base::MessageLoopProxy::current(), 399 run_loop.QuitClosure(), 400 base::TimeTicks::Now())); 401 run_loop.Run(); 402 403 EXPECT_EQ(2, callback_invoke_count()); 404 EXPECT_EQ(2, frames_captured()); 405 } 406 407 class CompositingRenderWidgetHostViewBrowserTestTabCapture 408 : public CompositingRenderWidgetHostViewBrowserTest { 409 public: 410 CompositingRenderWidgetHostViewBrowserTestTabCapture() 411 : expected_copy_from_compositing_surface_result_(false), 412 allowable_error_(0), 413 test_url_("data:text/html,<!doctype html>") {} 414 415 virtual void SetUp() OVERRIDE { 416 EnablePixelOutput(); 417 CompositingRenderWidgetHostViewBrowserTest::SetUp(); 418 } 419 420 void CopyFromCompositingSurfaceCallback(base::Closure quit_callback, 421 bool result, 422 const SkBitmap& bitmap) { 423 EXPECT_EQ(expected_copy_from_compositing_surface_result_, result); 424 if (!result) { 425 quit_callback.Run(); 426 return; 427 } 428 429 const SkBitmap& expected_bitmap = 430 expected_copy_from_compositing_surface_bitmap_; 431 EXPECT_EQ(expected_bitmap.width(), bitmap.width()); 432 EXPECT_EQ(expected_bitmap.height(), bitmap.height()); 433 EXPECT_EQ(expected_bitmap.colorType(), bitmap.colorType()); 434 SkAutoLockPixels expected_bitmap_lock(expected_bitmap); 435 SkAutoLockPixels bitmap_lock(bitmap); 436 int fails = 0; 437 for (int i = 0; i < bitmap.width() && fails < 10; ++i) { 438 for (int j = 0; j < bitmap.height() && fails < 10; ++j) { 439 if (!exclude_rect_.IsEmpty() && exclude_rect_.Contains(i, j)) 440 continue; 441 442 SkColor expected_color = expected_bitmap.getColor(i, j); 443 SkColor color = bitmap.getColor(i, j); 444 int expected_alpha = SkColorGetA(expected_color); 445 int alpha = SkColorGetA(color); 446 int expected_red = SkColorGetR(expected_color); 447 int red = SkColorGetR(color); 448 int expected_green = SkColorGetG(expected_color); 449 int green = SkColorGetG(color); 450 int expected_blue = SkColorGetB(expected_color); 451 int blue = SkColorGetB(color); 452 EXPECT_NEAR(expected_alpha, alpha, allowable_error_) 453 << "expected_color: " << std::hex << expected_color 454 << " color: " << color 455 << " Failed at " << std::dec << i << ", " << j 456 << " Failure " << ++fails; 457 EXPECT_NEAR(expected_red, red, allowable_error_) 458 << "expected_color: " << std::hex << expected_color 459 << " color: " << color 460 << " Failed at " << std::dec << i << ", " << j 461 << " Failure " << ++fails; 462 EXPECT_NEAR(expected_green, green, allowable_error_) 463 << "expected_color: " << std::hex << expected_color 464 << " color: " << color 465 << " Failed at " << std::dec << i << ", " << j 466 << " Failure " << ++fails; 467 EXPECT_NEAR(expected_blue, blue, allowable_error_) 468 << "expected_color: " << std::hex << expected_color 469 << " color: " << color 470 << " Failed at " << std::dec << i << ", " << j 471 << " Failure " << ++fails; 472 } 473 } 474 EXPECT_LT(fails, 10); 475 476 quit_callback.Run(); 477 } 478 479 void CopyFromCompositingSurfaceCallbackForVideo( 480 scoped_refptr<media::VideoFrame> video_frame, 481 base::Closure quit_callback, 482 bool result) { 483 EXPECT_EQ(expected_copy_from_compositing_surface_result_, result); 484 if (!result) { 485 quit_callback.Run(); 486 return; 487 } 488 489 media::SkCanvasVideoRenderer video_renderer; 490 491 SkBitmap bitmap; 492 bitmap.allocN32Pixels(video_frame->visible_rect().width(), 493 video_frame->visible_rect().height()); 494 // Don't clear the canvas because drawing a video frame by Src mode. 495 SkCanvas canvas(bitmap); 496 video_renderer.Copy(video_frame, &canvas); 497 498 CopyFromCompositingSurfaceCallback(quit_callback, 499 result, 500 bitmap); 501 } 502 503 void SetExpectedCopyFromCompositingSurfaceResult(bool result, 504 const SkBitmap& bitmap) { 505 expected_copy_from_compositing_surface_result_ = result; 506 expected_copy_from_compositing_surface_bitmap_ = bitmap; 507 } 508 509 void SetAllowableError(int amount) { allowable_error_ = amount; } 510 void SetExcludeRect(gfx::Rect exclude) { exclude_rect_ = exclude; } 511 512 virtual GURL TestUrl() OVERRIDE { 513 return GURL(test_url_); 514 } 515 516 void SetTestUrl(std::string url) { test_url_ = url; } 517 518 // Loads a page two boxes side-by-side, each half the width of 519 // |html_rect_size|, and with different background colors. The test then 520 // copies from |copy_rect| region of the page into a bitmap of size 521 // |output_size|, and examines the resulting bitmap/VideoFrame. 522 // Note that |output_size| may not have the same size as |copy_rect| (e.g. 523 // when the output is scaled). 524 void PerformTestWithLeftRightRects(const gfx::Size& html_rect_size, 525 const gfx::Rect& copy_rect, 526 const gfx::Size& output_size, 527 bool video_frame) { 528 const gfx::Size box_size(html_rect_size.width() / 2, 529 html_rect_size.height()); 530 SetTestUrl(base::StringPrintf( 531 "data:text/html,<!doctype html>" 532 "<div class='left'>" 533 " <div class='right'></div>" 534 "</div>" 535 "<style>" 536 "body { padding: 0; margin: 0; }" 537 ".left { position: absolute;" 538 " background: #0ff;" 539 " width: %dpx;" 540 " height: %dpx;" 541 "}" 542 ".right { position: absolute;" 543 " left: %dpx;" 544 " background: #ff0;" 545 " width: %dpx;" 546 " height: %dpx;" 547 "}" 548 "</style>" 549 "<script>" 550 " domAutomationController.setAutomationId(0);" 551 " domAutomationController.send(\"DONE\");" 552 "</script>", 553 box_size.width(), 554 box_size.height(), 555 box_size.width(), 556 box_size.width(), 557 box_size.height())); 558 559 SET_UP_SURFACE_OR_PASS_TEST("\"DONE\""); 560 if (!ShouldContinueAfterTestURLLoad()) 561 return; 562 563 RenderWidgetHostViewBase* rwhvp = GetRenderWidgetHostView(); 564 if (video_frame && !rwhvp->CanCopyToVideoFrame()) { 565 // This should only happen on Mac when using the software compositor. 566 // Otherwise, raise an error. This can be removed when Mac is moved to a 567 // browser compositor. 568 // http://crbug.com/314190 569 #if defined(OS_MACOSX) 570 if (!content::GpuDataManager::GetInstance()->GpuAccessAllowed(NULL)) { 571 LOG(WARNING) << ("Blindly passing this test because copying to " 572 "video frames is not supported on this platform."); 573 return; 574 } 575 #endif 576 NOTREACHED(); 577 } 578 579 // The page is loaded in the renderer, wait for a new frame to arrive. 580 uint32 frame = rwhvp->RendererFrameNumber(); 581 while (!GetRenderWidgetHost()->ScheduleComposite()) 582 GiveItSomeTime(); 583 while (rwhvp->RendererFrameNumber() == frame) 584 GiveItSomeTime(); 585 586 SkBitmap expected_bitmap; 587 SetupLeftRightBitmap(output_size, &expected_bitmap); 588 SetExpectedCopyFromCompositingSurfaceResult(true, expected_bitmap); 589 590 base::RunLoop run_loop; 591 if (video_frame) { 592 // Allow pixel differences as long as we have the right idea. 593 SetAllowableError(0x10); 594 // Exclude the middle two columns which are blended between the two sides. 595 SetExcludeRect( 596 gfx::Rect(output_size.width() / 2 - 1, 0, 2, output_size.height())); 597 598 scoped_refptr<media::VideoFrame> video_frame = 599 media::VideoFrame::CreateFrame(media::VideoFrame::YV12, 600 output_size, 601 gfx::Rect(output_size), 602 output_size, 603 base::TimeDelta()); 604 605 base::Callback<void(bool success)> callback = 606 base::Bind(&CompositingRenderWidgetHostViewBrowserTestTabCapture:: 607 CopyFromCompositingSurfaceCallbackForVideo, 608 base::Unretained(this), 609 video_frame, 610 run_loop.QuitClosure()); 611 rwhvp->CopyFromCompositingSurfaceToVideoFrame(copy_rect, 612 video_frame, 613 callback); 614 } else { 615 if (IsDelegatedRendererEnabled()) { 616 if (!content::GpuDataManager::GetInstance() 617 ->CanUseGpuBrowserCompositor()) { 618 // Skia rendering can cause color differences, particularly in the 619 // middle two columns. 620 SetAllowableError(2); 621 SetExcludeRect(gfx::Rect( 622 output_size.width() / 2 - 1, 0, 2, output_size.height())); 623 } 624 } 625 626 base::Callback<void(bool, const SkBitmap&)> callback = 627 base::Bind(&CompositingRenderWidgetHostViewBrowserTestTabCapture:: 628 CopyFromCompositingSurfaceCallback, 629 base::Unretained(this), 630 run_loop.QuitClosure()); 631 rwhvp->CopyFromCompositingSurface(copy_rect, 632 output_size, 633 callback, 634 kN32_SkColorType); 635 } 636 run_loop.Run(); 637 } 638 639 // Sets up |bitmap| to have size |copy_size|. It floods the left half with 640 // #0ff and the right half with #ff0. 641 void SetupLeftRightBitmap(const gfx::Size& copy_size, SkBitmap* bitmap) { 642 bitmap->allocN32Pixels(copy_size.width(), copy_size.height()); 643 // Left half is #0ff. 644 bitmap->eraseARGB(255, 0, 255, 255); 645 // Right half is #ff0. 646 { 647 SkAutoLockPixels lock(*bitmap); 648 for (int i = 0; i < copy_size.width() / 2; ++i) { 649 for (int j = 0; j < copy_size.height(); ++j) { 650 *(bitmap->getAddr32(copy_size.width() / 2 + i, j)) = 651 SkColorSetARGB(255, 255, 255, 0); 652 } 653 } 654 } 655 } 656 657 protected: 658 virtual bool ShouldContinueAfterTestURLLoad() { 659 return true; 660 } 661 662 private: 663 bool expected_copy_from_compositing_surface_result_; 664 SkBitmap expected_copy_from_compositing_surface_bitmap_; 665 int allowable_error_; 666 gfx::Rect exclude_rect_; 667 std::string test_url_; 668 }; 669 670 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, 671 CopyFromCompositingSurface_Origin_Unscaled) { 672 gfx::Rect copy_rect(400, 300); 673 gfx::Size output_size = copy_rect.size(); 674 gfx::Size html_rect_size(400, 300); 675 bool video_frame = false; 676 PerformTestWithLeftRightRects(html_rect_size, 677 copy_rect, 678 output_size, 679 video_frame); 680 } 681 682 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, 683 CopyFromCompositingSurface_Origin_Scaled) { 684 gfx::Rect copy_rect(400, 300); 685 gfx::Size output_size(200, 100); 686 gfx::Size html_rect_size(400, 300); 687 bool video_frame = false; 688 PerformTestWithLeftRightRects(html_rect_size, 689 copy_rect, 690 output_size, 691 video_frame); 692 } 693 694 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, 695 CopyFromCompositingSurface_Cropped_Unscaled) { 696 // Grab 60x60 pixels from the center of the tab contents. 697 gfx::Rect copy_rect(400, 300); 698 copy_rect = gfx::Rect(copy_rect.CenterPoint() - gfx::Vector2d(30, 30), 699 gfx::Size(60, 60)); 700 gfx::Size output_size = copy_rect.size(); 701 gfx::Size html_rect_size(400, 300); 702 bool video_frame = false; 703 PerformTestWithLeftRightRects(html_rect_size, 704 copy_rect, 705 output_size, 706 video_frame); 707 } 708 709 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, 710 CopyFromCompositingSurface_Cropped_Scaled) { 711 // Grab 60x60 pixels from the center of the tab contents. 712 gfx::Rect copy_rect(400, 300); 713 copy_rect = gfx::Rect(copy_rect.CenterPoint() - gfx::Vector2d(30, 30), 714 gfx::Size(60, 60)); 715 gfx::Size output_size(20, 10); 716 gfx::Size html_rect_size(400, 300); 717 bool video_frame = false; 718 PerformTestWithLeftRightRects(html_rect_size, 719 copy_rect, 720 output_size, 721 video_frame); 722 } 723 724 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, 725 CopyFromCompositingSurface_ForVideoFrame) { 726 // Grab 90x60 pixels from the center of the tab contents. 727 gfx::Rect copy_rect(400, 300); 728 copy_rect = gfx::Rect(copy_rect.CenterPoint() - gfx::Vector2d(45, 30), 729 gfx::Size(90, 60)); 730 gfx::Size output_size = copy_rect.size(); 731 gfx::Size html_rect_size(400, 300); 732 bool video_frame = true; 733 PerformTestWithLeftRightRects(html_rect_size, 734 copy_rect, 735 output_size, 736 video_frame); 737 } 738 739 IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, 740 CopyFromCompositingSurface_ForVideoFrame_Scaled) { 741 // Grab 90x60 pixels from the center of the tab contents. 742 gfx::Rect copy_rect(400, 300); 743 copy_rect = gfx::Rect(copy_rect.CenterPoint() - gfx::Vector2d(45, 30), 744 gfx::Size(90, 60)); 745 // Scale to 30 x 20 (preserve aspect ratio). 746 gfx::Size output_size(30, 20); 747 gfx::Size html_rect_size(400, 300); 748 bool video_frame = true; 749 PerformTestWithLeftRightRects(html_rect_size, 750 copy_rect, 751 output_size, 752 video_frame); 753 } 754 755 class CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI 756 : public CompositingRenderWidgetHostViewBrowserTestTabCapture { 757 public: 758 CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI() {} 759 760 protected: 761 virtual void SetUpCommandLine(base::CommandLine* cmd) OVERRIDE { 762 CompositingRenderWidgetHostViewBrowserTestTabCapture::SetUpCommandLine(cmd); 763 cmd->AppendSwitchASCII(switches::kForceDeviceScaleFactor, 764 base::StringPrintf("%f", scale())); 765 } 766 767 virtual bool ShouldContinueAfterTestURLLoad() OVERRIDE { 768 // Short-circuit a pass for platforms where setting up high-DPI fails. 769 const float actual_scale_factor = 770 GetScaleFactorForView(GetRenderWidgetHostView()); 771 if (actual_scale_factor != scale()) { 772 LOG(WARNING) << "Blindly passing this test; unable to force device scale " 773 << "factor: seems to be " << actual_scale_factor 774 << " but expected " << scale(); 775 return false; 776 } 777 VLOG(1) << ("Successfully forced device scale factor. Moving forward with " 778 "this test! :-)"); 779 return true; 780 } 781 782 static float scale() { return 2.0f; } 783 784 private: 785 DISALLOW_COPY_AND_ASSIGN( 786 CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI); 787 }; 788 789 // ImageSkia (related to ResourceBundle) implementation crashes the process on 790 // Windows when this content_browsertest forces a device scale factor. 791 // http://crbug.com/399349 792 // 793 // These tests are flaky on ChromeOS builders. See http://crbug.com/406018. 794 #if defined(OS_WIN) || defined(OS_CHROMEOS) 795 #define MAYBE_CopyToBitmap_EntireRegion DISABLED_CopyToBitmap_EntireRegion 796 #define MAYBE_CopyToBitmap_CenterRegion DISABLED_CopyToBitmap_CenterRegion 797 #define MAYBE_CopyToBitmap_ScaledResult DISABLED_CopyToBitmap_ScaledResult 798 #define MAYBE_CopyToVideoFrame_EntireRegion \ 799 DISABLED_CopyToVideoFrame_EntireRegion 800 #define MAYBE_CopyToVideoFrame_CenterRegion \ 801 DISABLED_CopyToVideoFrame_CenterRegion 802 #define MAYBE_CopyToVideoFrame_ScaledResult \ 803 DISABLED_CopyToVideoFrame_ScaledResult 804 #else 805 #define MAYBE_CopyToBitmap_EntireRegion CopyToBitmap_EntireRegion 806 #define MAYBE_CopyToBitmap_CenterRegion CopyToBitmap_CenterRegion 807 #define MAYBE_CopyToBitmap_ScaledResult CopyToBitmap_ScaledResult 808 #define MAYBE_CopyToVideoFrame_EntireRegion CopyToVideoFrame_EntireRegion 809 #define MAYBE_CopyToVideoFrame_CenterRegion CopyToVideoFrame_CenterRegion 810 #define MAYBE_CopyToVideoFrame_ScaledResult CopyToVideoFrame_ScaledResult 811 #endif 812 813 IN_PROC_BROWSER_TEST_P( 814 CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI, 815 MAYBE_CopyToBitmap_EntireRegion) { 816 gfx::Size html_rect_size(200, 150); 817 gfx::Rect copy_rect(200, 150); 818 // Scale the output size so that, internally, scaling is not occurring. 819 gfx::Size output_size = 820 gfx::ToRoundedSize(gfx::ScaleSize(copy_rect.size(), scale())); 821 bool video_frame = false; 822 PerformTestWithLeftRightRects(html_rect_size, 823 copy_rect, 824 output_size, 825 video_frame); 826 } 827 828 IN_PROC_BROWSER_TEST_P( 829 CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI, 830 MAYBE_CopyToBitmap_CenterRegion) { 831 gfx::Size html_rect_size(200, 150); 832 // Grab 90x60 pixels from the center of the tab contents. 833 gfx::Rect copy_rect = 834 gfx::Rect(gfx::Rect(html_rect_size).CenterPoint() - gfx::Vector2d(45, 30), 835 gfx::Size(90, 60)); 836 // Scale the output size so that, internally, scaling is not occurring. 837 gfx::Size output_size = 838 gfx::ToRoundedSize(gfx::ScaleSize(copy_rect.size(), scale())); 839 bool video_frame = false; 840 PerformTestWithLeftRightRects(html_rect_size, 841 copy_rect, 842 output_size, 843 video_frame); 844 } 845 846 IN_PROC_BROWSER_TEST_P( 847 CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI, 848 MAYBE_CopyToBitmap_ScaledResult) { 849 gfx::Size html_rect_size(200, 100); 850 gfx::Rect copy_rect(200, 100); 851 // Output is being down-scaled since output_size is in phyiscal pixels. 852 gfx::Size output_size(200, 100); 853 bool video_frame = false; 854 PerformTestWithLeftRightRects(html_rect_size, 855 copy_rect, 856 output_size, 857 video_frame); 858 } 859 860 IN_PROC_BROWSER_TEST_P( 861 CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI, 862 MAYBE_CopyToVideoFrame_EntireRegion) { 863 gfx::Size html_rect_size(200, 150); 864 gfx::Rect copy_rect(200, 150); 865 // Scale the output size so that, internally, scaling is not occurring. 866 gfx::Size output_size = 867 gfx::ToRoundedSize(gfx::ScaleSize(copy_rect.size(), scale())); 868 bool video_frame = true; 869 PerformTestWithLeftRightRects(html_rect_size, 870 copy_rect, 871 output_size, 872 video_frame); 873 } 874 875 IN_PROC_BROWSER_TEST_P( 876 CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI, 877 MAYBE_CopyToVideoFrame_CenterRegion) { 878 gfx::Size html_rect_size(200, 150); 879 // Grab 90x60 pixels from the center of the tab contents. 880 gfx::Rect copy_rect = 881 gfx::Rect(gfx::Rect(html_rect_size).CenterPoint() - gfx::Vector2d(45, 30), 882 gfx::Size(90, 60)); 883 // Scale the output size so that, internally, scaling is not occurring. 884 gfx::Size output_size = 885 gfx::ToRoundedSize(gfx::ScaleSize(copy_rect.size(), scale())); 886 bool video_frame = true; 887 PerformTestWithLeftRightRects(html_rect_size, 888 copy_rect, 889 output_size, 890 video_frame); 891 } 892 893 IN_PROC_BROWSER_TEST_P( 894 CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI, 895 MAYBE_CopyToVideoFrame_ScaledResult) { 896 gfx::Size html_rect_size(200, 100); 897 gfx::Rect copy_rect(200, 100); 898 // Output is being down-scaled since output_size is in phyiscal pixels. 899 gfx::Size output_size(200, 100); 900 bool video_frame = true; 901 PerformTestWithLeftRightRects(html_rect_size, 902 copy_rect, 903 output_size, 904 video_frame); 905 } 906 907 INSTANTIATE_TEST_CASE_P(GLAndSoftwareCompositing, 908 CompositingRenderWidgetHostViewBrowserTest, 909 testing::Values(GL_COMPOSITING, SOFTWARE_COMPOSITING)); 910 INSTANTIATE_TEST_CASE_P(GLAndSoftwareCompositing, 911 CompositingRenderWidgetHostViewBrowserTestTabCapture, 912 testing::Values(GL_COMPOSITING, SOFTWARE_COMPOSITING)); 913 INSTANTIATE_TEST_CASE_P( 914 GLAndSoftwareCompositing, 915 CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI, 916 testing::Values(GL_COMPOSITING, SOFTWARE_COMPOSITING)); 917 918 #endif // !defined(OS_ANDROID) && !defined(OS_IOS) 919 920 } // namespace 921 } // namespace content 922