1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
|
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/test_runner/pixel_dump.h"
#include <memory>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "components/test_runner/layout_test_runtime_flags.h"
// FIXME: Including platform_canvas.h here is a layering violation.
#include "skia/ext/platform_canvas.h"
#include "third_party/WebKit/public/platform/Platform.h"
#include "third_party/WebKit/public/platform/WebCompositeAndReadbackAsyncCallback.h"
#include "third_party/WebKit/public/platform/WebImage.h"
#include "third_party/WebKit/public/platform/WebMockClipboard.h"
#include "third_party/WebKit/public/platform/WebPoint.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebPagePopup.h"
#include "third_party/WebKit/public/web/WebPrintParams.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "ui/gfx/geometry/point.h"
namespace test_runner {
namespace {
struct PixelsDumpRequest {
PixelsDumpRequest(blink::WebView* web_view,
const LayoutTestRuntimeFlags& layout_test_runtime_flags,
const base::Callback<void(const SkBitmap&)>& callback)
: web_view(web_view),
layout_test_runtime_flags(layout_test_runtime_flags),
callback(callback) {}
blink::WebView* web_view;
const LayoutTestRuntimeFlags& layout_test_runtime_flags;
base::Callback<void(const SkBitmap&)> callback;
};
class CaptureCallback : public blink::WebCompositeAndReadbackAsyncCallback {
public:
CaptureCallback(const base::Callback<void(const SkBitmap&)>& callback);
virtual ~CaptureCallback();
void set_wait_for_popup(bool wait) { wait_for_popup_ = wait; }
void set_popup_position(const gfx::Point& position) {
popup_position_ = position;
}
// WebCompositeAndReadbackAsyncCallback implementation.
void didCompositeAndReadback(const SkBitmap& bitmap) override;
private:
base::Callback<void(const SkBitmap&)> callback_;
SkBitmap main_bitmap_;
bool wait_for_popup_;
gfx::Point popup_position_;
};
void DrawSelectionRect(const PixelsDumpRequest& dump_request,
SkCanvas* canvas) {
// See if we need to draw the selection bounds rect. Selection bounds
// rect is the rect enclosing the (possibly transformed) selection.
// The rect should be drawn after everything is laid out and painted.
if (!dump_request.layout_test_runtime_flags.dump_selection_rect())
return;
// If there is a selection rect - draw a red 1px border enclosing rect
blink::WebRect wr = dump_request.web_view->mainFrame()->selectionBoundsRect();
if (wr.isEmpty())
return;
// Render a red rectangle bounding selection rect
SkPaint paint;
paint.setColor(0xFFFF0000); // Fully opaque red
paint.setStyle(SkPaint::kStroke_Style);
paint.setFlags(SkPaint::kAntiAlias_Flag);
paint.setStrokeWidth(1.0f);
SkIRect rect; // Bounding rect
rect.set(wr.x, wr.y, wr.x + wr.width, wr.y + wr.height);
canvas->drawIRect(rect, paint);
}
void CapturePixelsForPrinting(std::unique_ptr<PixelsDumpRequest> dump_request) {
dump_request->web_view->updateAllLifecyclePhases();
blink::WebSize page_size_in_pixels = dump_request->web_view->size();
blink::WebFrame* web_frame = dump_request->web_view->mainFrame();
int page_count = web_frame->printBegin(page_size_in_pixels);
int totalHeight = page_count * (page_size_in_pixels.height + 1) - 1;
bool is_opaque = false;
std::unique_ptr<SkCanvas> canvas = skia::TryCreateBitmapCanvas(
page_size_in_pixels.width, totalHeight, is_opaque);
if (!canvas) {
LOG(ERROR) << "Failed to create canvas width="
<< page_size_in_pixels.width << " height=" << totalHeight;
dump_request->callback.Run(SkBitmap());
return;
}
web_frame->printPagesWithBoundaries(canvas.get(), page_size_in_pixels);
web_frame->printEnd();
DrawSelectionRect(*dump_request, canvas.get());
const SkBitmap bitmap = skia::ReadPixels(canvas.get());
dump_request->callback.Run(bitmap);
}
CaptureCallback::CaptureCallback(
const base::Callback<void(const SkBitmap&)>& callback)
: callback_(callback), wait_for_popup_(false) {}
CaptureCallback::~CaptureCallback() {}
void CaptureCallback::didCompositeAndReadback(const SkBitmap& bitmap) {
TRACE_EVENT2("shell", "CaptureCallback::didCompositeAndReadback", "x",
bitmap.info().width(), "y", bitmap.info().height());
if (!wait_for_popup_) {
callback_.Run(bitmap);
delete this;
return;
}
if (main_bitmap_.isNull()) {
bitmap.deepCopyTo(&main_bitmap_);
return;
}
SkCanvas canvas(main_bitmap_);
canvas.drawBitmap(bitmap, popup_position_.x(), popup_position_.y());
callback_.Run(main_bitmap_);
delete this;
}
void DidCapturePixelsAsync(std::unique_ptr<PixelsDumpRequest> dump_request,
const SkBitmap& bitmap) {
SkCanvas canvas(bitmap);
DrawSelectionRect(*dump_request, &canvas);
if (!dump_request->callback.is_null())
dump_request->callback.Run(bitmap);
}
} // namespace
void DumpPixelsAsync(blink::WebView* web_view,
const LayoutTestRuntimeFlags& layout_test_runtime_flags,
float device_scale_factor_for_test,
const base::Callback<void(const SkBitmap&)>& callback) {
TRACE_EVENT0("shell", "WebViewTestProxyBase::CapturePixelsAsync");
DCHECK(!callback.is_null());
DCHECK(!layout_test_runtime_flags.dump_drag_image());
std::unique_ptr<PixelsDumpRequest> pixels_request(
new PixelsDumpRequest(web_view, layout_test_runtime_flags, callback));
if (layout_test_runtime_flags.is_printing()) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&CapturePixelsForPrinting,
base::Passed(std::move(pixels_request))));
return;
}
CaptureCallback* capture_callback = new CaptureCallback(base::Bind(
&DidCapturePixelsAsync, base::Passed(std::move(pixels_request))));
web_view->compositeAndReadbackAsync(capture_callback);
if (blink::WebPagePopup* popup = web_view->pagePopup()) {
capture_callback->set_wait_for_popup(true);
blink::WebPoint position = popup->positionRelativeToOwner();
position.x *= device_scale_factor_for_test;
position.y *= device_scale_factor_for_test;
capture_callback->set_popup_position(position);
popup->compositeAndReadbackAsync(capture_callback);
}
}
void CopyImageAtAndCapturePixels(
blink::WebView* web_view,
int x,
int y,
const base::Callback<void(const SkBitmap&)>& callback) {
DCHECK(!callback.is_null());
uint64_t sequence_number =
blink::Platform::current()->clipboard()->sequenceNumber(
blink::WebClipboard::Buffer());
// TODO(lukasza): Support image capture in OOPIFs for
// https://crbug.com/477150.
web_view->mainFrame()->toWebLocalFrame()->copyImageAt(blink::WebPoint(x, y));
if (sequence_number ==
blink::Platform::current()->clipboard()->sequenceNumber(
blink::WebClipboard::Buffer())) {
SkBitmap emptyBitmap;
callback.Run(emptyBitmap);
return;
}
blink::WebImage image = static_cast<blink::WebMockClipboard*>(
blink::Platform::current()->clipboard())
->readRawImage(blink::WebClipboard::Buffer());
const SkBitmap& bitmap = image.getSkBitmap();
SkAutoLockPixels autoLock(bitmap);
callback.Run(bitmap);
}
} // namespace test_runner
|