DevTools: Fix for Page.captureScreenshot

from https://chromiumcodereview.appspot.com/190693002/

This patch makes Page.captureScreenshot synchronize with the renderer.
When fixed implementation hits Stable it will be possible to use Page.captureScreenshot in Telemetry instead of window.chrome.gpuBenchmarking.beginWindowSnapshotPNG and remove the latter.

BUG=242405

Review URL: https://codereview.chromium.org/311313003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278472 0039d316-1c4b-4281-b951-d872f2087c98
parent a6c3afba
......@@ -263,20 +263,6 @@ void RendererOverridesHandler::ParseCaptureParameters(
*scale = 5;
}
base::DictionaryValue* RendererOverridesHandler::CreateScreenshotResponse(
const std::vector<unsigned char>& png_data) {
std::string base_64_data;
base::Base64Encode(
base::StringPiece(reinterpret_cast<const char*>(&png_data[0]),
png_data.size()),
&base_64_data);
base::DictionaryValue* response = new base::DictionaryValue();
response->SetString(
devtools::Page::captureScreenshot::kResponseData, base_64_data);
return response;
}
// DOM agent handlers --------------------------------------------------------
scoped_refptr<DevToolsProtocol::Response>
......@@ -470,43 +456,37 @@ RendererOverridesHandler::PageNavigateToHistoryEntry(
scoped_refptr<DevToolsProtocol::Response>
RendererOverridesHandler::PageCaptureScreenshot(
scoped_refptr<DevToolsProtocol::Command> command) {
RenderViewHost* host = agent_->GetRenderViewHost();
RenderViewHostImpl* host = static_cast<RenderViewHostImpl*>(
agent_->GetRenderViewHost());
if (!host->GetView())
return command->InternalErrorResponse("Unable to access the view");
gfx::Rect view_bounds = host->GetView()->GetViewBounds();
gfx::Rect snapshot_bounds(view_bounds.size());
gfx::Size snapshot_size = snapshot_bounds.size();
std::vector<unsigned char> png_data;
if (ui::GrabViewSnapshot(host->GetView()->GetNativeView(),
&png_data,
snapshot_bounds)) {
if (png_data.size())
return command->SuccessResponse(CreateScreenshotResponse(png_data));
else
return command->InternalErrorResponse("Unable to capture screenshot");
}
ui::GrabViewSnapshotAsync(
host->GetView()->GetNativeView(),
snapshot_bounds,
base::ThreadTaskRunnerHandle::Get(),
host->GetSnapshotFromBrowser(
base::Bind(&RendererOverridesHandler::ScreenshotCaptured,
weak_factory_.GetWeakPtr(), command));
weak_factory_.GetWeakPtr(), command));
return command->AsyncResponsePromise();
}
void RendererOverridesHandler::ScreenshotCaptured(
scoped_refptr<DevToolsProtocol::Command> command,
scoped_refptr<base::RefCountedBytes> png_data) {
if (png_data) {
SendAsyncResponse(
command->SuccessResponse(CreateScreenshotResponse(png_data->data())));
} else {
const unsigned char* png_data,
size_t png_size) {
if (!png_data || !png_size) {
SendAsyncResponse(
command->InternalErrorResponse("Unable to capture screenshot"));
return;
}
std::string base_64_data;
base::Base64Encode(
base::StringPiece(reinterpret_cast<const char*>(png_data), png_size),
&base_64_data);
base::DictionaryValue* response = new base::DictionaryValue();
response->SetString(devtools::Page::screencastFrame::kParamData,
base_64_data);
SendAsyncResponse(command->SuccessResponse(response));
}
scoped_refptr<DevToolsProtocol::Response>
......
......@@ -44,9 +44,6 @@ class CONTENT_EXPORT RendererOverridesHandler
void ParseCaptureParameters(DevToolsProtocol::Command* command,
std::string* format, int* quality,
double* scale);
base::DictionaryValue* CreateScreenshotResponse(
const std::vector<unsigned char>& png_data);
// DOM domain.
scoped_refptr<DevToolsProtocol::Response>
GrantPermissionsForSetFileInputFiles(
......@@ -84,7 +81,8 @@ class CONTENT_EXPORT RendererOverridesHandler
void ScreenshotCaptured(
scoped_refptr<DevToolsProtocol::Command> command,
scoped_refptr<base::RefCountedBytes> png_data);
const unsigned char* png_data,
size_t png_size);
void ScreencastFrameCaptured(
const std::string& format,
......
......@@ -2,37 +2,30 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/basictypes.h"
#include "base/base64.h"
#include "base/command_line.h"
#include "base/json/json_reader.h"
#include "base/memory/scoped_ptr.h"
#include "content/browser/devtools/renderer_overrides_handler.h"
#include "content/browser/devtools/devtools_protocol.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/devtools_client_host.h"
#include "content/public/browser/devtools_manager.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/compositor/compositor_switches.h"
#include "ui/gfx/codec/png_codec.h"
namespace content {
class RendererOverridesHandlerTest : public ContentBrowserTest {
class RendererOverridesHandlerTest : public ContentBrowserTest,
public DevToolsClientHost {
protected:
scoped_refptr<DevToolsProtocol::Response> SendCommand(
const std::string& method,
base::DictionaryValue* params) {
scoped_ptr<RendererOverridesHandler> handler(CreateHandler());
scoped_refptr<DevToolsProtocol::Command> command(
DevToolsProtocol::CreateCommand(1, method, params));
return handler->HandleCommand(command);
}
void SendAsyncCommand(const std::string& method,
base::DictionaryValue* params) {
scoped_ptr<RendererOverridesHandler> handler(CreateHandler());
scoped_refptr<DevToolsProtocol::Command> command(
DevToolsProtocol::CreateCommand(1, method, params));
scoped_refptr<DevToolsProtocol::Response> response =
handler->HandleCommand(command);
EXPECT_TRUE(response->is_async_promise());
void SendCommand(const std::string& method,
base::DictionaryValue* params) {
EXPECT_TRUE(DevToolsManager::GetInstance()->DispatchOnInspectorBackend(this,
DevToolsProtocol::CreateCommand(1, method, params)->Serialize()));
base::MessageLoop::current()->Run();
}
......@@ -64,30 +57,40 @@ class RendererOverridesHandlerTest : public ContentBrowserTest {
scoped_ptr<base::DictionaryValue> result_;
private:
RendererOverridesHandler* CreateHandler() {
RenderViewHost* rvh = shell()->web_contents()->GetRenderViewHost();
DevToolsAgentHost* agent = DevToolsAgentHost::GetOrCreateFor(rvh).get();
scoped_ptr<RendererOverridesHandler> handler(
new RendererOverridesHandler(agent));
handler->SetNotifier(base::Bind(
&RendererOverridesHandlerTest::OnMessageSent, base::Unretained(this)));
return handler.release();
virtual void SetUpOnMainThread() OVERRIDE {
DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
DevToolsAgentHost::GetOrCreateFor(
shell()->web_contents()->GetRenderViewHost()).get(),
this);
}
virtual void TearDownOnMainThread() OVERRIDE {
DevToolsManager::GetInstance()->ClientHostClosing(this);
}
void OnMessageSent(const std::string& message) {
virtual void DispatchOnInspectorFrontend(
const std::string& message) OVERRIDE {
scoped_ptr<base::DictionaryValue> root(
static_cast<base::DictionaryValue*>(base::JSONReader::Read(message)));
base::DictionaryValue* result;
root->GetDictionary("result", &result);
EXPECT_TRUE(root->GetDictionary("result", &result));
result_.reset(result->DeepCopy());
base::MessageLoop::current()->QuitNow();
}
virtual void InspectedContentsClosing() OVERRIDE {
EXPECT_TRUE(false);
}
virtual void ReplacedWithAnotherClient() OVERRIDE {
EXPECT_TRUE(false);
}
};
IN_PROC_BROWSER_TEST_F(RendererOverridesHandlerTest, QueryUsageAndQuota) {
base::DictionaryValue* params = new base::DictionaryValue();
params->SetString("securityOrigin", "http://example.com");
SendAsyncCommand("Page.queryUsageAndQuota", params);
SendCommand("Page.queryUsageAndQuota", params);
EXPECT_TRUE(HasValue("quota.persistent"));
EXPECT_TRUE(HasValue("quota.temporary"));
......@@ -98,4 +101,38 @@ IN_PROC_BROWSER_TEST_F(RendererOverridesHandlerTest, QueryUsageAndQuota) {
EXPECT_TRUE(HasListItem("usage.persistent", "id", "filesystem"));
}
class CaptureScreenshotTest : public RendererOverridesHandlerTest {
private:
#if !defined(OS_ANDROID)
virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE {
command_line->AppendSwitch(switches::kEnablePixelOutputInTests);
}
#endif
};
// Does not link on Android
#if defined(OS_ANDROID)
#define MAYBE_CaptureScreenshot DISABLED_CaptureScreenshot
#else
#define MAYBE_CaptureScreenshot CaptureScreenshot
#endif
IN_PROC_BROWSER_TEST_F(CaptureScreenshotTest, MAYBE_CaptureScreenshot) {
shell()->LoadURL(GURL("about:blank"));
EXPECT_TRUE(content::ExecuteScript(
shell()->web_contents()->GetRenderViewHost(),
"document.body.style.background = '#123456'"));
SendCommand("Page.captureScreenshot", new base::DictionaryValue());
std::string base64;
EXPECT_TRUE(result_->GetString("data", &base64));
std::string png;
EXPECT_TRUE(base::Base64Decode(base64, &png));
SkBitmap bitmap;
gfx::PNGCodec::Decode(reinterpret_cast<const unsigned char*>(png.data()),
png.size(), &bitmap);
SkColor color(bitmap.getColor(0, 0));
EXPECT_EQ(0x12U, SkColorGetR(color));
EXPECT_EQ(0x34U, SkColorGetG(color));
EXPECT_EQ(0x56U, SkColorGetB(color));
}
} // namespace content
......@@ -192,7 +192,8 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,
allow_privileged_mouse_lock_(false),
has_touch_handler_(false),
weak_factory_(this),
last_input_number_(static_cast<int64>(GetProcess()->GetID()) << 32) {
last_input_number_(static_cast<int64>(GetProcess()->GetID()) << 32),
next_browser_snapshot_id_(0) {
CHECK(delegate_);
if (routing_id_ == MSG_ROUTING_NONE) {
routing_id_ = process_->GetNextRoutingID();
......@@ -1162,6 +1163,13 @@ void RenderWidgetHostImpl::InvalidateScreenInfo() {
screen_info_.reset();
}
void RenderWidgetHostImpl::GetSnapshotFromBrowser(
const base::Callback<void(const unsigned char*,size_t)> callback) {
int id = next_browser_snapshot_id_++;
pending_browser_snapshots_.insert(std::make_pair(id, callback));
Send(new ViewMsg_ForceRedraw(GetRoutingID(), id));
}
void RenderWidgetHostImpl::OnSelectionChanged(const base::string16& text,
size_t offset,
const gfx::Range& range) {
......@@ -2157,6 +2165,12 @@ void RenderWidgetHostImpl::ComputeTouchLatency(
void RenderWidgetHostImpl::FrameSwapped(const ui::LatencyInfo& latency_info) {
ui::LatencyInfo::LatencyComponent window_snapshot_component;
if (latency_info.FindLatency(ui::WINDOW_OLD_SNAPSHOT_FRAME_NUMBER_COMPONENT,
GetLatencyComponentId(),
&window_snapshot_component)) {
WindowOldSnapshotReachedScreen(
static_cast<int>(window_snapshot_component.sequence_number));
}
if (latency_info.FindLatency(ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT,
GetLatencyComponentId(),
&window_snapshot_component)) {
......@@ -2216,7 +2230,7 @@ void RenderWidgetHostImpl::WindowSnapshotAsyncCallback(
routing_id, snapshot_id, snapshot_size, png_data->data()));
}
void RenderWidgetHostImpl::WindowSnapshotReachedScreen(int snapshot_id) {
void RenderWidgetHostImpl::WindowOldSnapshotReachedScreen(int snapshot_id) {
DCHECK(base::MessageLoopForUI::IsCurrent());
std::vector<unsigned char> png;
......@@ -2252,6 +2266,53 @@ void RenderWidgetHostImpl::WindowSnapshotReachedScreen(int snapshot_id) {
snapshot_size));
}
void RenderWidgetHostImpl::WindowSnapshotReachedScreen(int snapshot_id) {
DCHECK(base::MessageLoopForUI::IsCurrent());
gfx::Rect view_bounds = GetView()->GetViewBounds();
gfx::Rect snapshot_bounds(view_bounds.size());
std::vector<unsigned char> png;
if (ui::GrabViewSnapshot(
GetView()->GetNativeView(), &png, snapshot_bounds)) {
OnSnapshotDataReceived(snapshot_id, &png.front(), png.size());
return;
}
ui::GrabViewSnapshotAsync(
GetView()->GetNativeView(),
snapshot_bounds,
base::ThreadTaskRunnerHandle::Get(),
base::Bind(&RenderWidgetHostImpl::OnSnapshotDataReceivedAsync,
weak_factory_.GetWeakPtr(),
snapshot_id));
}
void RenderWidgetHostImpl::OnSnapshotDataReceived(int snapshot_id,
const unsigned char* data,
size_t size) {
// Any pending snapshots with a lower ID than the one received are considered
// to be implicitly complete, and returned the same snapshot data.
PendingSnapshotMap::iterator it = pending_browser_snapshots_.begin();
while(it != pending_browser_snapshots_.end()) {
if (it->first <= snapshot_id) {
it->second.Run(data, size);
pending_browser_snapshots_.erase(it++);
} else {
++it;
}
}
}
void RenderWidgetHostImpl::OnSnapshotDataReceivedAsync(
int snapshot_id,
scoped_refptr<base::RefCountedBytes> png_data) {
if (png_data)
OnSnapshotDataReceived(snapshot_id, png_data->front(), png_data->size());
else
OnSnapshotDataReceived(snapshot_id, NULL, 0);
}
// static
void RenderWidgetHostImpl::CompositorFrameDrawn(
const std::vector<ui::LatencyInfo>& latency_info) {
......@@ -2262,7 +2323,8 @@ void RenderWidgetHostImpl::CompositorFrameDrawn(
b != latency_info[i].latency_components.end();
++b) {
if (b->first.first == ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT ||
b->first.first == ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT) {
b->first.first == ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT ||
b->first.first == ui::WINDOW_OLD_SNAPSHOT_FRAME_NUMBER_COMPONENT) {
// Matches with GetLatencyComponentId
int routing_id = b->first.second & 0xffffffff;
int process_id = (b->first.second >> 32) & 0xffffffff;
......@@ -2286,7 +2348,8 @@ void RenderWidgetHostImpl::AddLatencyInfoComponentIds(
latency_info->latency_components.begin();
while (lc != latency_info->latency_components.end()) {
ui::LatencyComponentType component_type = lc->first.first;
if (component_type == ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT) {
if (component_type == ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT ||
component_type == ui::WINDOW_OLD_SNAPSHOT_FRAME_NUMBER_COMPONENT) {
// Generate a new component entry with the correct component ID
ui::LatencyInfo::LatencyMap::key_type key =
std::make_pair(component_type, GetLatencyComponentId());
......
......@@ -192,6 +192,11 @@ class CONTENT_EXPORT RenderWidgetHostImpl
virtual void AccessibilityHitTest(const gfx::Point& point) OVERRIDE;
virtual void AccessibilityFatalError() OVERRIDE;
// Forces redraw in the renderer and when the update reaches the browser
// grabs snapshot from the compositor. Returns PNG-encoded snapshot.
void GetSnapshotFromBrowser(
const base::Callback<void(const unsigned char*,size_t)> callback);
const NativeWebKeyboardEvent* GetLastKeyboardEvent() const;
// Notification that the screen info has changed.
......@@ -693,8 +698,18 @@ class CONTENT_EXPORT RenderWidgetHostImpl
// which may get in recursive loops).
void DelayedAutoResized();
void WindowOldSnapshotReachedScreen(int snapshot_id);
void WindowSnapshotReachedScreen(int snapshot_id);
void OnSnapshotDataReceived(int snapshot_id,
const unsigned char* png,
size_t size);
void OnSnapshotDataReceivedAsync(
int snapshot_id,
scoped_refptr<base::RefCountedBytes> png_data);
// Send a message to the renderer process to change the accessibility mode.
void SetAccessibilityMode(AccessibilityMode AccessibilityMode);
......@@ -862,6 +877,11 @@ class CONTENT_EXPORT RenderWidgetHostImpl
int64 last_input_number_;
int next_browser_snapshot_id_;
typedef std::map<int,
base::Callback<void(const unsigned char*, size_t)> > PendingSnapshotMap;
PendingSnapshotMap pending_browser_snapshots_;
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostImpl);
};
......
......@@ -2069,6 +2069,12 @@ void RenderWidgetHostViewMac::AddPendingLatencyInfo(
NULL)) {
should_defer = true;
}
if (latency_info[i].FindLatency(
ui::WINDOW_OLD_SNAPSHOT_FRAME_NUMBER_COMPONENT,
render_widget_host_->GetLatencyComponentId(),
NULL)) {
should_defer = true;
}
}
if (should_defer) {
// Multiple pending screenshot requests will work, but if every frame
......@@ -2113,8 +2119,7 @@ void RenderWidgetHostViewMac::TickPendingLatencyInfoDelay() {
base::Bind(&RenderWidgetHostViewMac::TickPendingLatencyInfoDelay,
pending_latency_info_delay_weak_ptr_factory_.GetWeakPtr()));
[compositing_iosurface_layer_ gotNewFrame];
}
if (software_layer_) {
} else {
// In software mode there is not an explicit setNeedsDisplay/display loop,
// so just wait a pretend-vsync at 60 Hz.
base::MessageLoop::current()->PostDelayedTask(
......
......@@ -989,6 +989,10 @@ IPC_MESSAGE_ROUTED2(ViewMsg_ReclaimCompositorResources,
IPC_MESSAGE_ROUTED0(ViewMsg_SelectWordAroundCaret)
// Sent by the browser to ask the renderer to redraw.
IPC_MESSAGE_ROUTED1(ViewMsg_ForceRedraw,
int /* request_id */)
// -----------------------------------------------------------------------------
// Messages sent from the renderer to the browser.
......
......@@ -1117,6 +1117,7 @@ bool RenderViewImpl::OnMessageReceived(const IPC::Message& message) {
OnReleaseDisambiguationPopupBitmap)
IPC_MESSAGE_HANDLER(ViewMsg_WindowSnapshotCompleted,
OnWindowSnapshotCompleted)
IPC_MESSAGE_HANDLER(ViewMsg_ForceRedraw, OnForceRedraw)
IPC_MESSAGE_HANDLER(ViewMsg_SelectWordAroundCaret, OnSelectWordAroundCaret)
#if defined(OS_ANDROID)
IPC_MESSAGE_HANDLER(InputMsg_ActivateNearestFindResult,
......@@ -1332,6 +1333,19 @@ bool RenderViewImpl::SendAndRunNestedMessageLoop(IPC::SyncMessage* message) {
void RenderViewImpl::GetWindowSnapshot(const WindowSnapshotCallback& callback) {
int id = next_snapshot_id_++;
pending_snapshots_.insert(std::make_pair(id, callback));
ui::LatencyInfo latency_info;
latency_info.AddLatencyNumber(ui::WINDOW_OLD_SNAPSHOT_FRAME_NUMBER_COMPONENT,
0,
id);
scoped_ptr<cc::SwapPromiseMonitor> latency_info_swap_promise_monitor;
if (RenderWidgetCompositor* rwc = compositor()) {
latency_info_swap_promise_monitor =
rwc->CreateLatencyInfoSwapPromiseMonitor(&latency_info).Pass();
}
ScheduleCompositeWithForcedRedraw();
}
void RenderViewImpl::OnForceRedraw(int id) {
ui::LatencyInfo latency_info;
latency_info.AddLatencyNumber(ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT,
0,
......
......@@ -753,6 +753,7 @@ class CONTENT_EXPORT RenderViewImpl
void OnDisownOpener();
void OnWindowSnapshotCompleted(const int snapshot_id,
const gfx::Size& size, const std::vector<unsigned char>& png);
void OnForceRedraw(int request_id);
void OnSelectWordAroundCaret();
#if defined(OS_ANDROID)
void OnActivateNearestFindResult(int request_id, float x, float y);
......
......@@ -26,6 +26,7 @@ const char* GetComponentName(ui::LatencyComponentType type) {
CASE_TYPE(INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT);
CASE_TYPE(INPUT_EVENT_LATENCY_ACKED_TOUCH_COMPONENT);
CASE_TYPE(WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT);
CASE_TYPE(WINDOW_OLD_SNAPSHOT_FRAME_NUMBER_COMPONENT);
CASE_TYPE(INPUT_EVENT_LATENCY_TERMINATED_MOUSE_COMPONENT);
CASE_TYPE(INPUT_EVENT_LATENCY_TERMINATED_TOUCH_COMPONENT);
CASE_TYPE(INPUT_EVENT_LATENCY_TERMINATED_GESTURE_COMPONENT);
......
......@@ -44,6 +44,10 @@ enum LatencyComponentType {
// Frame number when a window snapshot was requested. The snapshot
// is taken when the rendering results actually reach the screen.
WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT,
// Frame number for a snapshot requested via
// gpuBenchmarking.beginWindowSnapshotPNG
// TODO(vkuzkokov): remove when patch adding this hits Stable
WINDOW_OLD_SNAPSHOT_FRAME_NUMBER_COMPONENT,
// ---------------------------TERMINAL COMPONENT-----------------------------
// TERMINAL COMPONENT is when we show the latency end in chrome://tracing.
// Timestamp when the mouse event is acked from renderer and it does not
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment