renderer_overrides_handler.cc revision c2db58bd994c04d98e4ee2cd7565b71548655fe3
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 "content/browser/devtools/renderer_overrides_handler.h"
6
7#include <string>
8
9#include "base/base64.h"
10#include "base/bind.h"
11#include "base/bind_helpers.h"
12#include "base/files/file_path.h"
13#include "base/strings/string16.h"
14#include "base/values.h"
15#include "content/browser/child_process_security_policy_impl.h"
16#include "content/browser/devtools/devtools_protocol_constants.h"
17#include "content/browser/devtools/devtools_tracing_handler.h"
18#include "content/browser/renderer_host/render_view_host_delegate.h"
19#include "content/port/browser/render_widget_host_view_port.h"
20#include "content/public/browser/browser_thread.h"
21#include "content/public/browser/devtools_agent_host.h"
22#include "content/public/browser/javascript_dialog_manager.h"
23#include "content/public/browser/navigation_controller.h"
24#include "content/public/browser/render_process_host.h"
25#include "content/public/browser/render_view_host.h"
26#include "content/public/browser/render_widget_host_view.h"
27#include "content/public/browser/web_contents.h"
28#include "content/public/browser/web_contents_delegate.h"
29#include "content/public/common/page_transition_types.h"
30#include "content/public/common/referrer.h"
31#include "ui/gfx/codec/jpeg_codec.h"
32#include "ui/gfx/codec/png_codec.h"
33#include "ui/gfx/size_conversions.h"
34#include "ui/snapshot/snapshot.h"
35#include "url/gurl.h"
36
37using base::TimeTicks;
38
39namespace {
40
41static const char kPng[] = "png";
42static const char kJpeg[] = "jpeg";
43static int kDefaultScreenshotQuality = 80;
44
45}  // namespace
46
47namespace content {
48
49RendererOverridesHandler::RendererOverridesHandler(DevToolsAgentHost* agent)
50    : agent_(agent),
51      weak_factory_(this) {
52  RegisterCommandHandler(
53      devtools::DOM::setFileInputFiles::kName,
54      base::Bind(
55          &RendererOverridesHandler::GrantPermissionsForSetFileInputFiles,
56          base::Unretained(this)));
57  RegisterCommandHandler(
58      devtools::Page::handleJavaScriptDialog::kName,
59      base::Bind(
60          &RendererOverridesHandler::PageHandleJavaScriptDialog,
61          base::Unretained(this)));
62  RegisterCommandHandler(
63      devtools::Page::navigate::kName,
64      base::Bind(
65          &RendererOverridesHandler::PageNavigate,
66          base::Unretained(this)));
67  RegisterCommandHandler(
68      devtools::Page::captureScreenshot::kName,
69      base::Bind(
70          &RendererOverridesHandler::PageCaptureScreenshot,
71          base::Unretained(this)));
72}
73
74RendererOverridesHandler::~RendererOverridesHandler() {}
75
76scoped_refptr<DevToolsProtocol::Response>
77RendererOverridesHandler::GrantPermissionsForSetFileInputFiles(
78    scoped_refptr<DevToolsProtocol::Command> command) {
79  base::DictionaryValue* params = command->params();
80  base::ListValue* file_list = NULL;
81  const char* param =
82      devtools::DOM::setFileInputFiles::kParamFiles;
83  if (!params || !params->GetList(param, &file_list))
84    return command->InvalidParamResponse(param);
85  RenderViewHost* host = agent_->GetRenderViewHost();
86  if (!host)
87    return NULL;
88
89  for (size_t i = 0; i < file_list->GetSize(); ++i) {
90    base::FilePath::StringType file;
91    if (!file_list->GetString(i, &file))
92      return command->InvalidParamResponse(param);
93    ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
94        host->GetProcess()->GetID(), base::FilePath(file));
95  }
96  return NULL;
97}
98
99scoped_refptr<DevToolsProtocol::Response>
100RendererOverridesHandler::PageHandleJavaScriptDialog(
101    scoped_refptr<DevToolsProtocol::Command> command) {
102  base::DictionaryValue* params = command->params();
103  const char* paramAccept =
104      devtools::Page::handleJavaScriptDialog::kParamAccept;
105  bool accept;
106  if (!params || !params->GetBoolean(paramAccept, &accept))
107    return command->InvalidParamResponse(paramAccept);
108  string16 prompt_override;
109  string16* prompt_override_ptr = &prompt_override;
110  if (!params || !params->GetString(
111      devtools::Page::handleJavaScriptDialog::kParamPromptText,
112      prompt_override_ptr)) {
113    prompt_override_ptr = NULL;
114  }
115
116  RenderViewHost* host = agent_->GetRenderViewHost();
117  if (host) {
118    WebContents* web_contents = host->GetDelegate()->GetAsWebContents();
119    if (web_contents) {
120      JavaScriptDialogManager* manager =
121          web_contents->GetDelegate()->GetJavaScriptDialogManager();
122      if (manager && manager->HandleJavaScriptDialog(
123              web_contents, accept, prompt_override_ptr)) {
124        return NULL;
125      }
126    }
127  }
128  return command->InternalErrorResponse("No JavaScript dialog to handle");
129}
130
131scoped_refptr<DevToolsProtocol::Response>
132RendererOverridesHandler::PageNavigate(
133    scoped_refptr<DevToolsProtocol::Command> command) {
134  base::DictionaryValue* params = command->params();
135  std::string url;
136  const char* param = devtools::Page::navigate::kParamUrl;
137  if (!params || !params->GetString(param, &url))
138    return command->InvalidParamResponse(param);
139  GURL gurl(url);
140  if (!gurl.is_valid()) {
141    return command->InternalErrorResponse("Cannot navigate to invalid URL");
142  }
143  RenderViewHost* host = agent_->GetRenderViewHost();
144  if (host) {
145    WebContents* web_contents = host->GetDelegate()->GetAsWebContents();
146    if (web_contents) {
147      web_contents->GetController()
148          .LoadURL(gurl, Referrer(), PAGE_TRANSITION_TYPED, std::string());
149      return command->SuccessResponse(new base::DictionaryValue());
150    }
151  }
152  return command->InternalErrorResponse("No WebContents to navigate");
153}
154
155scoped_refptr<DevToolsProtocol::Response>
156RendererOverridesHandler::PageCaptureScreenshot(
157    scoped_refptr<DevToolsProtocol::Command> command) {
158  // Parse input parameters.
159  std::string format;
160  int quality = kDefaultScreenshotQuality;
161  double scale = 1;
162  base::DictionaryValue* params = command->params();
163  if (params) {
164    params->GetString(devtools::Page::captureScreenshot::kParamFormat,
165                      &format);
166    params->GetInteger(devtools::Page::captureScreenshot::kParamQuality,
167                       &quality);
168    params->GetDouble(devtools::Page::captureScreenshot::kParamScale,
169                      &scale);
170  }
171  if (format.empty())
172    format = kPng;
173  if (quality < 0 || quality > 100)
174    quality = kDefaultScreenshotQuality;
175  if (scale <= 0 || scale > 1)
176    scale = 1;
177
178  RenderViewHost* host = agent_->GetRenderViewHost();
179  gfx::Rect view_bounds = host->GetView()->GetViewBounds();
180
181  // Grab screen pixels if available for current platform.
182  // TODO(pfeldman): support format, scale and quality in ui::GrabViewSnapshot.
183  std::vector<unsigned char> png;
184  bool is_unscaled_png = scale == 1 && format == kPng;
185  if (is_unscaled_png && ui::GrabViewSnapshot(host->GetView()->GetNativeView(),
186                                              &png, view_bounds)) {
187    std::string base64_data;
188    bool success = base::Base64Encode(
189        base::StringPiece(reinterpret_cast<char*>(&*png.begin()), png.size()),
190        &base64_data);
191    if (success) {
192      base::DictionaryValue* result = new base::DictionaryValue();
193      result->SetString(
194          devtools::Page::captureScreenshot::kResponseData, base64_data);
195      return command->SuccessResponse(result);
196    }
197    return command->InternalErrorResponse("Unable to base64encode screenshot");
198  }
199
200  // Fallback to copying from compositing surface.
201  RenderWidgetHostViewPort* view_port =
202      RenderWidgetHostViewPort::FromRWHV(host->GetView());
203
204  gfx::Size snapshot_size = gfx::ToFlooredSize(
205      gfx::ScaleSize(view_bounds.size(), scale));
206  view_port->CopyFromCompositingSurface(
207      view_bounds, snapshot_size,
208      base::Bind(&RendererOverridesHandler::ScreenshotCaptured,
209                 weak_factory_.GetWeakPtr(), command, format, quality, scale));
210  return command->AsyncResponsePromise();
211}
212
213void RendererOverridesHandler::ScreenshotCaptured(
214    scoped_refptr<DevToolsProtocol::Command> command,
215    const std::string& format,
216    int quality,
217    double scale,
218    bool success,
219    const SkBitmap& bitmap) {
220  if (!success) {
221    SendRawMessage(
222        command->InternalErrorResponse("Unable to capture screenshot")->
223            Serialize());
224    return;
225  }
226
227  std::vector<unsigned char> data;
228  SkAutoLockPixels lock_image(bitmap);
229  bool encoded;
230  if (format == kPng) {
231    encoded = gfx::PNGCodec::Encode(
232        reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
233        gfx::PNGCodec::FORMAT_SkBitmap,
234        gfx::Size(bitmap.width(), bitmap.height()),
235        bitmap.width() * bitmap.bytesPerPixel(),
236        false, std::vector<gfx::PNGCodec::Comment>(), &data);
237  } else if (format == kJpeg) {
238    encoded = gfx::JPEGCodec::Encode(
239        reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
240        gfx::JPEGCodec::FORMAT_SkBitmap,
241        bitmap.width(),
242        bitmap.height(),
243        bitmap.width() * bitmap.bytesPerPixel(),
244        quality, &data);
245  } else {
246    encoded = false;
247  }
248
249  if (!encoded) {
250    SendRawMessage(
251        command->InternalErrorResponse("Unable to encode screenshot")->
252            Serialize());
253    return;
254  }
255
256  std::string base_64_data;
257  if (!base::Base64Encode(base::StringPiece(
258                             reinterpret_cast<char*>(&data[0]),
259                             data.size()),
260                          &base_64_data)) {
261    SendRawMessage(
262        command->InternalErrorResponse("Unable to base64 encode screenshot")->
263            Serialize());
264    return;
265  }
266
267  base::DictionaryValue* response = new base::DictionaryValue();
268  response->SetString(
269      devtools::Page::captureScreenshot::kResponseData, base_64_data);
270  SendRawMessage(command->SuccessResponse(response)->Serialize());
271}
272
273}  // namespace content
274