0

[LayoutTests] Enable OOPIF pixel dumps behind a flag.

This patch enables OOPIF pixel dumps behind a flag. The reason for the
flag is that this isn't working for 100% of cases yet. Specifically,
selection rects and dialogs windows aren't being captured correctly.

As well, there seem to be some pixel differences between existings tests
which will likely need to be rebaselined.

R=lukasza@chromium.org

Bug: 667551
Change-Id: Icfb98958e5b9d007b1bd3a554b32d55d545e04e6
Reviewed-on: https://chromium-review.googlesource.com/994240
Commit-Queue: vmpstr <vmpstr@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Ken Rockot <rockot@chromium.org>
Reviewed-by: Antoine Labour <piman@chromium.org>
Reviewed-by: Chris Harrelson <chrishtr@chromium.org>
Reviewed-by: Łukasz Anforowicz <lukasza@chromium.org>
Cr-Commit-Position: refs/heads/master@{#551487}
This commit is contained in:
Vladimir Levin
2018-04-17 21:49:01 +00:00
committed by Commit Bot
parent 479d85b53a
commit d29618cbc8
35 changed files with 321 additions and 47 deletions

@ -1063,6 +1063,10 @@ void RenderWidgetCompositor::SynchronouslyCompositeNoRasterForTesting() {
SynchronouslyComposite(false /* raster */, nullptr /* swap_promise */);
}
void RenderWidgetCompositor::CompositeWithRasterForTesting() {
SynchronouslyComposite(true /* raster */, nullptr /* swap_promise */);
}
void RenderWidgetCompositor::SynchronouslyComposite(
bool raster,
std::unique_ptr<cc::SwapPromise> swap_promise) {

@ -152,6 +152,7 @@ class CONTENT_EXPORT RenderWidgetCompositor
void CompositeAndReadbackAsync(
base::OnceCallback<void(const SkBitmap&)> callback) override;
void SynchronouslyCompositeNoRasterForTesting() override;
void CompositeWithRasterForTesting() override;
void SetDeferCommits(bool defer_commits) override;
void RegisterViewportLayers(
const blink::WebLayerTreeView::ViewportLayers& viewport_layers) override;

@ -37,6 +37,10 @@ class CONTENT_EXPORT LayoutTestDependencies {
public:
virtual ~LayoutTestDependencies();
// Returns true if the layout tests should use the display compositor pixel
// dumps.
virtual bool UseDisplayCompositorPixelDump() const = 0;
virtual std::unique_ptr<cc::LayerTreeFrameSink> CreateLayerTreeFrameSink(
int32_t routing_id,
scoped_refptr<gpu::GpuChannelHost> gpu_channel,

@ -2135,10 +2135,18 @@ void RenderThreadImpl::RequestNewLayerTreeFrameSink(
ui::command_buffer_metrics::RENDER_COMPOSITOR_CONTEXT));
if (layout_test_deps_) {
callback.Run(layout_test_deps_->CreateLayerTreeFrameSink(
routing_id, std::move(gpu_channel_host), std::move(context_provider),
std::move(worker_context_provider), GetGpuMemoryBufferManager(), this));
return;
if (!layout_test_deps_->UseDisplayCompositorPixelDump()) {
callback.Run(layout_test_deps_->CreateLayerTreeFrameSink(
routing_id, std::move(gpu_channel_host), std::move(context_provider),
std::move(worker_context_provider), GetGpuMemoryBufferManager(),
this));
return;
} else if (!params.compositor_task_runner) {
// The frame sink provider expects a compositor task runner, but we might
// not have that if we're running layout tests in single threaded mode.
// Set it to be our thread's task runner instead.
params.compositor_task_runner = GetCompositorMainThreadTaskRunner();
}
}
#if defined(OS_ANDROID)
@ -2179,7 +2187,8 @@ std::unique_ptr<cc::SwapPromise>
RenderThreadImpl::RequestCopyOfOutputForLayoutTest(
int32_t routing_id,
std::unique_ptr<viz::CopyOutputRequest> request) {
DCHECK(layout_test_deps_);
DCHECK(layout_test_deps_ &&
!layout_test_deps_->UseDisplayCompositorPixelDump());
return layout_test_deps_->RequestCopyOfOutput(routing_id, std::move(request));
}

@ -45,6 +45,14 @@ source_set("android_shell_descriptors") {
]
}
source_set("layout_test_switches") {
testonly = true
sources = [
"common/layout_test/layout_test_switches.cc",
"common/layout_test/layout_test_switches.h",
]
}
static_library("content_shell_lib") {
testonly = true
sources = [
@ -164,8 +172,6 @@ static_library("content_shell_lib") {
"common/layout_test/layout_test_content_client.h",
"common/layout_test/layout_test_messages.cc",
"common/layout_test/layout_test_messages.h",
"common/layout_test/layout_test_switches.cc",
"common/layout_test/layout_test_switches.h",
"common/leak_detection_result.h",
"common/power_monitor_test_impl.cc",
"common/power_monitor_test_impl.h",
@ -231,6 +237,7 @@ static_library("content_shell_lib") {
]
deps = [
":content_shell_packaged_services_manifest_overlay",
":layout_test_switches",
":mojo_bindings",
":resources",
"//base",

@ -37,6 +37,7 @@ include_rules = [
"+components/url_formatter",
"+components/network_session_configurator/browser",
"+components/viz/common/resources",
"+components/viz/common/switches.h",
"+services/test/echo",
]

@ -16,6 +16,7 @@
#include "build/build_config.h"
#include "cc/base/switches.h"
#include "components/crash/core/common/crash_key.h"
#include "components/viz/common/switches.h"
#include "content/common/content_constants_internal.h"
#include "content/public/browser/browser_main_runner.h"
#include "content/public/common/content_switches.h"
@ -218,6 +219,15 @@ bool ShellMainDelegate::BasicStartupComplete(int* exit_code) {
command_line.AppendSwitch(cc::switches::kDisableThreadedAnimation);
}
// If we're doing a display compositor pixel dump we ensure that
// we complete all stages of compositing before draw. We also can't have
// checker imaging, since it's imcompatible with single threaded compositor
// and display compositor pixel dumps.
if (command_line.HasSwitch(switches::kEnableDisplayCompositorPixelDump)) {
command_line.AppendSwitch(switches::kRunAllCompositorStagesBeforeDraw);
command_line.AppendSwitch(cc::switches::kDisableCheckerImaging);
}
command_line.AppendSwitch(switches::kEnableInbandTextTracks);
command_line.AppendSwitch(switches::kMuteAudio);

@ -69,6 +69,7 @@
#include "content/shell/common/shell_messages.h"
#include "content/shell/renderer/layout_test/blink_test_helpers.h"
#include "content/shell/test_runner/test_common.h"
#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "ui/gfx/codec/png_codec.h"
@ -505,6 +506,12 @@ bool BlinkTestController::ResetAfterLayoutTest() {
should_override_prefs_ = false;
LayoutTestContentBrowserClient::Get()->SetPopupBlockingEnabled(false);
navigation_history_dump_ = "";
pixel_dump_.reset();
actual_pixel_hash_ = "";
main_frame_dump_ = nullptr;
waiting_for_pixel_results_ = false;
waiting_for_main_frame_dump_ = false;
weak_factory_.InvalidateWeakPtrs();
#if defined(OS_ANDROID)
// Re-using the shell's main window on Android causes issues with networking
@ -554,8 +561,8 @@ void BlinkTestController::OnTestFinishedInSecondaryRenderer() {
main_render_view_host->GetRoutingID()));
}
void BlinkTestController::OnInitiateCaptureDump(
bool capture_navigation_history) {
void BlinkTestController::OnInitiateCaptureDump(bool capture_navigation_history,
bool capture_pixels) {
if (test_phase_ != DURING_TEST)
return;
@ -580,6 +587,27 @@ void BlinkTestController::OnInitiateCaptureDump(
}
}
// Ensure to say that we need to wait for main frame dump here, since
// CopyFromSurface call below may synchronously issue the callback, meaning
// that we would report results too early.
waiting_for_main_frame_dump_ = true;
if (capture_pixels) {
DCHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableDisplayCompositorPixelDump));
waiting_for_pixel_results_ = true;
// Trigger compositing on all frames.
CompositeAllFrames();
// Enqueue a copy output request.
auto* rwhv = main_window_->web_contents()->GetRenderWidgetHostView();
rwhv->CopyFromSurface(
gfx::Rect(), gfx::Size(),
base::BindOnce(&BlinkTestController::OnPixelDumpCaptured,
weak_factory_.GetWeakPtr()));
}
RenderFrameHost* rfh = main_window_->web_contents()->GetMainFrame();
printer_->StartStateDump();
GetLayoutTestControlPtr(rfh)->CaptureDump(
@ -587,6 +615,61 @@ void BlinkTestController::OnInitiateCaptureDump(
weak_factory_.GetWeakPtr()));
}
void BlinkTestController::CompositeAllFrames() {
std::vector<Node> node_storage;
Node* root = BuildFrameTree(main_window_->web_contents()->GetAllFrames(),
&node_storage);
mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_calls;
CompositeDepthFirst(root);
}
BlinkTestController::Node* BlinkTestController::BuildFrameTree(
const std::vector<RenderFrameHost*>& frames,
std::vector<Node>* storage) const {
// Ensure we don't reallocate during tree construction.
storage->reserve(frames.size());
// Returns a Node for a given RenderFrameHost, or nullptr if doesn't exist.
auto node_for_frame = [storage](RenderFrameHost* rfh) {
auto it = std::find_if(
storage->begin(), storage->end(),
[rfh](const Node& node) { return node.render_frame_host == rfh; });
return it == storage->end() ? nullptr : &*it;
};
// Add all of the frames to storage.
for (auto* frame : frames) {
DCHECK(!node_for_frame(frame)) << "Frame seen multiple times.";
storage->emplace_back(frame);
}
// Construct a tree rooted at |root|.
Node* root = nullptr;
for (auto* frame : frames) {
Node* node = node_for_frame(frame);
DCHECK(node);
if (!frame->GetParent()) {
DCHECK(!root) << "Multiple roots found.";
root = node;
} else {
Node* parent = node_for_frame(frame->GetParent());
DCHECK(parent);
parent->children.push_back(node);
}
}
DCHECK(root) << "No root found.";
return root;
}
void BlinkTestController::CompositeDepthFirst(Node* node) {
if (!node->render_frame_host->IsRenderFrameLive())
return;
for (auto* child : node->children)
CompositeDepthFirst(child);
GetLayoutTestControlPtr(node->render_frame_host)->CompositeWithRaster();
}
bool BlinkTestController::IsMainWindow(WebContents* web_contents) const {
return main_window_ && web_contents == main_window_->web_contents();
}
@ -871,12 +954,43 @@ void BlinkTestController::OnCleanupFinished() {
void BlinkTestController::OnCaptureDumpCompleted(
mojom::LayoutTestDumpPtr dump) {
if (dump->audio)
OnAudioDump(*dump->audio);
if (dump->layout)
OnTextDump(*dump->layout);
if (!dump->actual_pixel_hash.empty())
OnImageDump(dump->actual_pixel_hash, dump->pixels);
main_frame_dump_ = std::move(dump);
waiting_for_main_frame_dump_ = false;
ReportResults();
}
void BlinkTestController::OnPixelDumpCaptured(const SkBitmap& snapshot) {
DCHECK(!snapshot.drawsNothing());
// The snapshot arrives from the GPU process via shared memory. Because MSan
// can't track initializedness across processes, we must assure it that the
// pixels are in fact initialized.
MSAN_UNPOISON(snapshot.getPixels(), snapshot.computeByteSize());
base::MD5Digest digest;
base::MD5Sum(snapshot.getPixels(), snapshot.computeByteSize(), &digest);
actual_pixel_hash_ = base::MD5DigestToBase16(digest);
pixel_dump_ = snapshot;
waiting_for_pixel_results_ = false;
ReportResults();
}
void BlinkTestController::ReportResults() {
if (waiting_for_pixel_results_ || waiting_for_main_frame_dump_)
return;
if (main_frame_dump_->audio)
OnAudioDump(*main_frame_dump_->audio);
if (main_frame_dump_->layout)
OnTextDump(*main_frame_dump_->layout);
// If we have local pixels, report that. Otherwise report whatever the pixel
// dump received from the renderer contains.
if (pixel_dump_) {
OnImageDump(actual_pixel_hash_, *pixel_dump_);
} else if (!main_frame_dump_->actual_pixel_hash.empty()) {
OnImageDump(main_frame_dump_->actual_pixel_hash, main_frame_dump_->pixels);
}
OnTestFinished();
}
@ -1161,4 +1275,10 @@ void BlinkTestController::HandleLayoutTestControlError(RenderFrameHost* frame) {
layout_test_control_map_.erase(frame);
}
BlinkTestController::Node::Node() = default;
BlinkTestController::Node::Node(RenderFrameHost* host)
: render_frame_host(host) {}
BlinkTestController::Node::Node(Node&& other) = default;
BlinkTestController::Node::~Node() = default;
} // namespace content

@ -140,7 +140,8 @@ class BlinkTestController : public WebContentsObserver,
int sender_process_host_id,
const base::DictionaryValue& changed_layout_test_runtime_flags);
void OnTestFinishedInSecondaryRenderer();
void OnInitiateCaptureDump(bool capture_navigation_history);
void OnInitiateCaptureDump(bool capture_navigation_history,
bool capture_pixels);
void OnInspectSecondaryWindow();
// Makes sure that the potentially new renderer associated with |frame| is 1)
@ -196,6 +197,19 @@ class BlinkTestController : public WebContentsObserver,
CLEAN_UP
};
// Node structure to construct a RenderFrameHost tree.
struct Node {
Node();
explicit Node(RenderFrameHost* host);
Node(Node&& other);
~Node();
RenderFrameHost* render_frame_host = nullptr;
std::vector<Node*> children;
DISALLOW_COPY_AND_ASSIGN(Node);
};
static BlinkTestController* instance_;
Shell* SecondaryWindow();
@ -231,6 +245,13 @@ class BlinkTestController : public WebContentsObserver,
void OnCleanupFinished();
void OnCaptureDumpCompleted(mojom::LayoutTestDumpPtr dump);
void OnPixelDumpCaptured(const SkBitmap& snapshot);
void ReportResults();
void CompositeAllFrames();
Node* BuildFrameTree(const std::vector<RenderFrameHost*>& frames,
std::vector<Node>* storage) const;
void CompositeDepthFirst(Node* node);
std::unique_ptr<BlinkTestResultPrinter> printer_;
@ -299,6 +320,11 @@ class BlinkTestController : public WebContentsObserver,
base::DictionaryValue accumulated_layout_test_runtime_flags_changes_;
std::string navigation_history_dump_;
base::Optional<SkBitmap> pixel_dump_;
std::string actual_pixel_hash_;
mojom::LayoutTestDumpPtr main_frame_dump_;
bool waiting_for_pixel_results_ = false;
bool waiting_for_main_frame_dump_ = false;
// Map from one frame to one mojo pipe.
std::map<RenderFrameHost*, mojom::LayoutTestControlAssociatedPtr>

@ -183,6 +183,10 @@ void LayoutTestContentBrowserClient::AppendExtraCommandLineSwitches(
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kEnableLeakDetection));
}
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableDisplayCompositorPixelDump)) {
command_line->AppendSwitch(switches::kEnableDisplayCompositorPixelDump);
}
}
BrowserMainParts* LayoutTestContentBrowserClient::CreateBrowserMainParts(

@ -255,11 +255,12 @@ void LayoutTestMessageFilter::OnTestFinishedInSecondaryRenderer() {
}
void LayoutTestMessageFilter::OnInitiateCaptureDump(
bool capture_navigation_history) {
bool capture_navigation_history,
bool capture_pixels) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (BlinkTestController::Get()) {
BlinkTestController::Get()->OnInitiateCaptureDump(
capture_navigation_history);
capture_navigation_history, capture_pixels);
}
}

@ -92,7 +92,8 @@ class LayoutTestMessageFilter : public BrowserMessageFilter {
void OnLayoutTestRuntimeFlagsChanged(
const base::DictionaryValue& changed_layout_test_runtime_flags);
void OnTestFinishedInSecondaryRenderer();
void OnInitiateCaptureDump(bool capture_navigation_history);
void OnInitiateCaptureDump(bool capture_navigation_history,
bool capture_pixels);
void OnInspectSecondaryWindow();
int render_process_id_;

@ -51,6 +51,8 @@ struct LayoutTestDump {
interface LayoutTestControl {
CaptureDump() => (LayoutTestDump result);
[Sync] CompositeWithRaster() => ();
// Dumps the frame's contents into a string.
DumpFrameLayout() => (string frame_layout_dump);

@ -44,8 +44,9 @@ IPC_MESSAGE_ROUTED4(LayoutTestHostMsg_SetPermission,
GURL /* embedding_origin */)
IPC_MESSAGE_ROUTED0(LayoutTestHostMsg_ResetPermissions)
IPC_MESSAGE_ROUTED0(LayoutTestHostMsg_InspectSecondaryWindow)
IPC_MESSAGE_ROUTED1(LayoutTestHostMsg_InitiateCaptureDump,
bool /* should dump navigation history */)
IPC_MESSAGE_ROUTED2(LayoutTestHostMsg_InitiateCaptureDump,
bool /* should dump navigation history */,
bool /* should dump pixels */)
// Notifies the browser that one of renderers has changed layout test runtime
// flags (i.e. has set dump_as_text).

@ -4,9 +4,6 @@
#include "content/shell/common/layout_test/layout_test_switches.h"
#include "base/command_line.h"
#include "base/strings/string_split.h"
namespace switches {
// Allow access to external pages during layout tests.
@ -62,4 +59,9 @@ const char kRunLayoutTest[] = "run-layout-test";
// http://dev.chromium.org/blink/runtime-enabled-features.
const char kStableReleaseMode[] = "stable-release-mode";
// Enable pixel dumps via "real" surface readbacks, instead of synchronously
// compositing and reading back pixels.
const char kEnableDisplayCompositorPixelDump[] =
"enable-display-compositor-pixel-dump";
} // namespace switches

@ -31,6 +31,7 @@ extern const char kEnableLeakDetection[];
extern const char kEncodeBinary[];
extern const char kRunLayoutTest[];
extern const char kStableReleaseMode[];
extern const char kEnableDisplayCompositorPixelDump[];
} // namespace switches

@ -572,14 +572,17 @@ void BlinkTestRunner::TestFinished() {
// now, just capture a local layout first.
CaptureLocalLayoutDump();
// TODO(vmpstr): This code should move to the browser, but since again some
// tests seem to be timing dependent, capture a local pixels dump first. Ask
// the browser to initiate a dump.
CaptureLocalPixelsDump();
// tests seem to be timing dependent, capture a local pixels dump first. Note
// that this returns a value indicating if we should defer the pixel dump to
// the browser instead. We want to switch all tests to use this for pixel
// dumps.
bool browser_should_capture_pixels = CaptureLocalPixelsDump();
// Request the browser to send us a callback through which we will return the
// results.
Send(new LayoutTestHostMsg_InitiateCaptureDump(
routing_id(), interfaces->TestRunner()->ShouldDumpBackForwardList()));
routing_id(), interfaces->TestRunner()->ShouldDumpBackForwardList(),
browser_should_capture_pixels));
}
void BlinkTestRunner::CaptureLocalAudioDump() {
@ -615,14 +618,14 @@ void BlinkTestRunner::CaptureLocalLayoutDump() {
}
}
void BlinkTestRunner::CaptureLocalPixelsDump() {
bool BlinkTestRunner::CaptureLocalPixelsDump() {
TRACE_EVENT0("shell", "BlinkTestRunner::CaptureLocalPixelsDump");
test_runner::WebTestInterfaces* interfaces =
LayoutTestRenderThreadObserver::GetInstance()->test_interfaces();
if (!test_config_->enable_pixel_dumping ||
!interfaces->TestRunner()->ShouldGeneratePixelResults() ||
interfaces->TestRunner()->ShouldDumpAsAudio()) {
return;
return false;
}
CHECK(render_view()->GetWebView()->IsAcceleratedCompositingActive());
@ -631,13 +634,16 @@ void BlinkTestRunner::CaptureLocalPixelsDump() {
// with the current, non-swapped-out RenderView.
DCHECK(render_view()->GetWebView()->MainFrame()->IsWebLocalFrame());
// TODO(vmpstr): We should move pixel capturing to the browser in order to
// start implementing OOPIF pixel dump support.
waiting_for_pixels_dump_result_ = true;
interfaces->TestRunner()->DumpPixelsAsync(
render_view()->GetWebView()->MainFrame()->ToWebLocalFrame(),
base::BindOnce(&BlinkTestRunner::OnPixelsDumpCompleted,
base::Unretained(this)));
bool browser_should_capture_pixels =
interfaces->TestRunner()->DumpPixelsAsync(
render_view()->GetWebView()->MainFrame()->ToWebLocalFrame(),
base::BindOnce(&BlinkTestRunner::OnPixelsDumpCompleted,
base::Unretained(this)));
// If the browser should capture pixels, then we shouldn't be waiting for dump
// results.
DCHECK(!browser_should_capture_pixels || !waiting_for_layout_dump_results_);
return browser_should_capture_pixels;
}
void BlinkTestRunner::OnLayoutDumpCompleted(std::string completed_layout_dump) {
@ -671,11 +677,6 @@ void BlinkTestRunner::CaptureDumpComplete() {
if (waiting_for_layout_dump_results_ || waiting_for_pixels_dump_result_)
return;
// Once we captured everything, we can stop loading. Note that we can't stop
// earlier, since partial load tests will have a different pixel output than
// expected.
render_view()->GetWebView()->MainFrame()->StopLoading();
// Abort if the browser didn't ask us for the dump yet.
if (!dump_callback_)
return;

@ -201,7 +201,8 @@ class BlinkTestRunner : public RenderViewObserver,
void CaptureDumpComplete();
void CaptureLocalAudioDump();
void CaptureLocalLayoutDump();
void CaptureLocalPixelsDump();
// Returns true if the browser should capture pixels instead.
bool CaptureLocalPixelsDump();
mojom::LayoutTestBluetoothFakeAdapterSetter&
GetBluetoothFakeAdapterSetter();

@ -13,7 +13,10 @@
#include "content/shell/test_runner/web_test_runner.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/public/web/web_frame_widget.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_page_popup.h"
#include "third_party/blink/public/web/web_view.h"
namespace content {
@ -49,6 +52,23 @@ void LayoutTestRenderFrameObserver::CaptureDump(CaptureDumpCallback callback) {
->CaptureDump(std::move(callback));
}
void LayoutTestRenderFrameObserver::CompositeWithRaster(
CompositeWithRasterCallback callback) {
blink::WebWidget* widget = render_frame()->GetWebFrame()->FrameWidget();
if (widget) {
widget->CompositeWithRasterForTesting();
// TODO(vmpstr): This doesn't embed the popup frame into the browser. We
// likely need to manually readback the pixels here and return them via the
// callback, so that the browser can embed it into the final screenshot. If
// we do this without using WebPagePopup::CompositeWithRasterForTesting(),
// then we can put CompositeWithRasterForTesting() on WebFrameWidgetBase
// instead, and remove the overrides from a bunch of places.
if (blink::WebPagePopup* popup = widget->GetPagePopup())
popup->CompositeWithRasterForTesting();
}
std::move(callback).Run();
}
void LayoutTestRenderFrameObserver::DumpFrameLayout(
DumpFrameLayoutCallback callback) {
std::string dump =

@ -23,6 +23,7 @@ class LayoutTestRenderFrameObserver : public RenderFrameObserver,
void OnDestruct() override;
void CaptureDump(CaptureDumpCallback callback) override;
void CompositeWithRaster(CompositeWithRasterCallback callback) override;
void DumpFrameLayout(DumpFrameLayoutCallback callback) override;
void SetTestConfiguration(mojom::ShellTestConfigurationPtr config) override;
void ReplicateTestConfiguration(

@ -102,6 +102,7 @@ component("test_runner") {
"//components/viz/common",
"//content/public/common",
"//content/public/renderer",
"//content/shell:layout_test_switches",
"//content/test:test_runner_support",
"//device/gamepad/public/cpp:shared_with_blink",
"//gin",

@ -18,6 +18,7 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "content/shell/common/layout_test/layout_test_switches.h"
#include "content/shell/test_runner/layout_and_paint_async_then.h"
#include "content/shell/test_runner/layout_dump.h"
#include "content/shell/test_runner/mock_content_settings_client.h"
@ -1732,7 +1733,7 @@ std::string TestRunner::DumpLayout(blink::WebLocalFrame* frame) {
return ::test_runner::DumpLayout(frame, layout_test_runtime_flags_);
}
void TestRunner::DumpPixelsAsync(
bool TestRunner::DumpPixelsAsync(
blink::WebLocalFrame* frame,
base::OnceCallback<void(const SkBitmap&)> callback) {
if (layout_test_runtime_flags_.dump_drag_image()) {
@ -1743,11 +1744,25 @@ void TestRunner::DumpPixelsAsync(
bitmap.allocN32Pixels(1, 1);
bitmap.eraseColor(0);
std::move(callback).Run(bitmap);
return;
return false;
}
std::move(callback).Run(drag_image_.GetSkBitmap());
return;
return false;
}
// If we need to do a display compositor pixel dump, then delegate that to the
// browser by returning true. Note that printing case can be handled here.
if (!layout_test_runtime_flags_.is_printing() &&
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableDisplayCompositorPixelDump)) {
// Because of the plumbing, we should still call the callback with an empty
// bitmap, but return true so that the browser also captures the pixels.
SkBitmap bitmap;
bitmap.allocN32Pixels(1, 1);
bitmap.eraseColor(0);
std::move(callback).Run(bitmap);
return true;
}
// See if we need to draw the selection bounds rect on top of the snapshot.
@ -1773,6 +1788,7 @@ void TestRunner::DumpPixelsAsync(
test_runner::DumpPixelsAsync(frame, delegate_->GetDeviceScaleFactor(),
std::move(callback));
}
return false;
}
void TestRunner::ReplicateLayoutTestRuntimeFlagsChanges(

@ -88,7 +88,8 @@ class TestRunner : public WebTestRunner {
void GetAudioData(std::vector<unsigned char>* buffer_view) const override;
bool IsRecursiveLayoutDumpRequested() override;
std::string DumpLayout(blink::WebLocalFrame* frame) override;
void DumpPixelsAsync(
// Returns true if the browser should capture the pixels instead.
bool DumpPixelsAsync(
blink::WebLocalFrame* frame,
base::OnceCallback<void(const SkBitmap&)> callback) override;
void ReplicateLayoutTestRuntimeFlagsChanges(

@ -55,7 +55,9 @@ class WebTestRunner {
// Snapshots image of |web_view| using the mode requested by the current test
// and calls |callback| with the result. Caller needs to ensure that
// |web_view| stays alive until |callback| is called.
virtual void DumpPixelsAsync(
// Returns false if the request to capture pixels was processed locally, and
// true if the pixels need to be captured in the browser process instead..
virtual bool DumpPixelsAsync(
blink::WebLocalFrame* frame,
base::OnceCallback<void(const SkBitmap&)> callback) = 0;

@ -602,6 +602,7 @@ static_library("layouttest_support") {
"//content/public/common",
"//content/public/renderer",
"//content/renderer:for_content_tests",
"//content/shell:layout_test_switches",
"//content/shell/test_runner:test_runner",
"//device/bluetooth",
"//device/gamepad/public/cpp:shared_with_blink",

@ -39,6 +39,7 @@
#include "content/renderer/render_view_impl.h"
#include "content/renderer/render_widget.h"
#include "content/renderer/renderer_blink_platform_impl.h"
#include "content/shell/common/layout_test/layout_test_switches.h"
#include "content/shell/common/shell_switches.h"
#include "content/shell/test_runner/test_common.h"
#include "content/shell/test_runner/web_frame_test_proxy.h"
@ -310,6 +311,11 @@ class CopyRequestSwapPromise : public cc::SwapPromise {
class LayoutTestDependenciesImpl : public LayoutTestDependencies,
public viz::TestLayerTreeFrameSinkClient {
public:
bool UseDisplayCompositorPixelDump() const override {
base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
return cmd->HasSwitch(switches::kEnableDisplayCompositorPixelDump);
}
std::unique_ptr<cc::LayerTreeFrameSink> CreateLayerTreeFrameSink(
int32_t routing_id,
scoped_refptr<gpu::GpuChannelHost> gpu_channel,

@ -21,6 +21,10 @@ namespace sync_preferences {
class PrefServiceSyncable;
}
namespace content {
class BlinkTestController;
}
namespace display {
class ForwardingDisplayDelegate;
}
@ -84,6 +88,8 @@ class MOJO_CPP_BINDINGS_EXPORT SyncCallRestrictions {
// For destroying the GL context/surface that draw to a platform window before
// the platform window is destroyed.
friend class viz::HostFrameSinkManager;
// Allow for layout test pixel dumps.
friend class content::BlinkTestController;
// END ALLOWED USAGE.
// BEGIN USAGE THAT NEEDS TO BE FIXED.

@ -157,6 +157,9 @@ class WebLayerTreeView {
// single-threaded mode.
virtual void SynchronouslyCompositeNoRasterForTesting() {}
// Synchronously rasterizes and composites a frame.
virtual void CompositeWithRasterForTesting() {}
// Prevents updates to layer tree from becoming visible.
virtual void SetDeferCommits(bool defer_commits) {}

@ -102,6 +102,9 @@ class WebWidget {
// to the compositor state except rasterization.
virtual void UpdateAllLifecyclePhasesAndCompositeForTesting() {}
// Synchronously rasterizes and composites a frame.
virtual void CompositeWithRasterForTesting() {}
// Called to paint the rectangular region within the WebWidget
// onto the specified canvas at (viewPort.x,viewPort.y).
//

@ -423,6 +423,11 @@ void WebPagePopupImpl::UpdateAllLifecyclePhasesAndCompositeForTesting() {
layer_tree_view_->SynchronouslyCompositeNoRasterForTesting();
}
void WebPagePopupImpl::CompositeWithRasterForTesting() {
if (layer_tree_view_)
layer_tree_view_->CompositeWithRasterForTesting();
}
void WebPagePopupImpl::Paint(WebCanvas* canvas, const WebRect& rect) {
if (!closing_) {
PageWidgetDelegate::Paint(*page_, canvas, rect,

@ -83,6 +83,7 @@ class CORE_EXPORT WebPagePopupImpl final : public WebPagePopup,
void BeginFrame(double last_frame_time_monotonic) override;
void UpdateLifecycle(LifecycleUpdate requested_update) override;
void UpdateAllLifecyclePhasesAndCompositeForTesting() override;
void CompositeWithRasterForTesting() override;
void WillCloseLayerTreeView() override;
void Paint(WebCanvas*, const WebRect&) override;
void Resize(const WebSize&) override;

@ -1880,6 +1880,11 @@ void WebViewImpl::UpdateAllLifecyclePhasesAndCompositeForTesting() {
layer_tree_view_->SynchronouslyCompositeNoRasterForTesting();
}
void WebViewImpl::CompositeWithRasterForTesting() {
// This should not be called directly on WebViewImpl.
NOTREACHED();
}
void WebViewImpl::Paint(WebCanvas* canvas, const WebRect& rect) {
// This should only be used when compositing is not being used for this
// WebView, and it is painting into the recording of its parent.

@ -117,6 +117,7 @@ class CORE_EXPORT WebViewImpl final : public WebView,
void UpdateLifecycle(LifecycleUpdate requested_update) override;
void UpdateAllLifecyclePhasesAndCompositeForTesting() override;
void CompositeWithRasterForTesting() override;
void Paint(WebCanvas*, const WebRect&) override;
#if defined(OS_ANDROID)
void PaintIgnoringCompositing(WebCanvas*, const WebRect&) override;

@ -191,6 +191,11 @@ void WebFrameWidgetBase::DragSourceSystemDragEnded() {
CancelDrag();
}
void WebFrameWidgetBase::CompositeWithRasterForTesting() {
if (auto* layer_tree_view = GetLayerTreeView())
layer_tree_view->CompositeWithRasterForTesting();
}
void WebFrameWidgetBase::CancelDrag() {
// It's possible for us this to be callback while we're not doing a drag if
// it's from a previous page that got unloaded.

@ -97,6 +97,7 @@ class CORE_EXPORT WebFrameWidgetBase
const WebFloatPoint& screen_point,
WebDragOperation) override;
void DragSourceSystemDragEnded() override;
void CompositeWithRasterForTesting() override;
WebLocalFrame* FocusedWebLocalFrameInWidget() const override;