Home | History | Annotate | Download | only in devtools
      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 
     37 using base::TimeTicks;
     38 
     39 namespace {
     40 
     41 static const char kPng[] = "png";
     42 static const char kJpeg[] = "jpeg";
     43 static int kDefaultScreenshotQuality = 80;
     44 
     45 }  // namespace
     46 
     47 namespace content {
     48 
     49 RendererOverridesHandler::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 
     74 RendererOverridesHandler::~RendererOverridesHandler() {}
     75 
     76 scoped_refptr<DevToolsProtocol::Response>
     77 RendererOverridesHandler::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 
     99 scoped_refptr<DevToolsProtocol::Response>
    100 RendererOverridesHandler::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 
    131 scoped_refptr<DevToolsProtocol::Response>
    132 RendererOverridesHandler::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 
    155 scoped_refptr<DevToolsProtocol::Response>
    156 RendererOverridesHandler::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 
    213 void 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