Index: components/test_runner/pixel_dump.cc |
diff --git a/components/test_runner/pixel_dump.cc b/components/test_runner/pixel_dump.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1b49c178325bd0a49a39c2af9f1cd63807e11009 |
--- /dev/null |
+++ b/components/test_runner/pixel_dump.cc |
@@ -0,0 +1,203 @@ |
+// 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 "base/bind.h" |
+#include "base/bind_helpers.h" |
+#include "base/callback.h" |
+#include "base/logging.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/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/WebClipboard.h" |
+#include "third_party/WebKit/public/platform/WebCompositeAndReadbackAsyncCallback.h" |
+#include "third_party/WebKit/public/platform/WebData.h" |
+#include "third_party/WebKit/public/platform/WebImage.h" |
+#include "third_party/WebKit/public/platform/WebPoint.h" |
+#include "third_party/WebKit/public/web/WebFrame.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(scoped_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; |
+ skia::RefPtr<SkCanvas> canvas(skia::AdoptRef(skia::TryCreateBitmapCanvas( |
+ page_size_in_pixels.width, totalHeight, is_opaque))); |
+ if (!canvas) { |
+ 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(scoped_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", "WebTestProxyBase::CapturePixelsAsync"); |
+ DCHECK(!callback.is_null()); |
+ DCHECK(!layout_test_runtime_flags.dump_drag_image()); |
+ |
+ scoped_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()); |
+ web_view->copyImageAt(blink::WebPoint(x, y)); |
+ if (sequence_number == |
+ blink::Platform::current()->clipboard()->sequenceNumber( |
+ blink::WebClipboard::Buffer())) { |
+ SkBitmap emptyBitmap; |
+ callback.Run(emptyBitmap); |
+ return; |
+ } |
+ |
+ blink::WebData data = blink::Platform::current()->clipboard()->readImage( |
+ blink::WebClipboard::Buffer()); |
+ blink::WebImage image = blink::WebImage::fromData(data, blink::WebSize()); |
+ const SkBitmap& bitmap = image.getSkBitmap(); |
+ SkAutoLockPixels autoLock(bitmap); |
+ callback.Run(bitmap); |
+} |
+ |
+} // namespace test_runner |