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 <map>
      8 #include <string>
      9 
     10 #include "base/barrier_closure.h"
     11 #include "base/base64.h"
     12 #include "base/bind.h"
     13 #include "base/bind_helpers.h"
     14 #include "base/files/file_path.h"
     15 #include "base/strings/string16.h"
     16 #include "base/values.h"
     17 #include "content/browser/child_process_security_policy_impl.h"
     18 #include "content/browser/devtools/devtools_protocol_constants.h"
     19 #include "content/browser/devtools/devtools_tracing_handler.h"
     20 #include "content/browser/renderer_host/dip_util.h"
     21 #include "content/browser/renderer_host/render_view_host_delegate.h"
     22 #include "content/browser/renderer_host/render_view_host_impl.h"
     23 #include "content/common/view_messages.h"
     24 #include "content/port/browser/render_widget_host_view_port.h"
     25 #include "content/public/browser/browser_thread.h"
     26 #include "content/public/browser/devtools_agent_host.h"
     27 #include "content/public/browser/javascript_dialog_manager.h"
     28 #include "content/public/browser/navigation_controller.h"
     29 #include "content/public/browser/navigation_entry.h"
     30 #include "content/public/browser/render_process_host.h"
     31 #include "content/public/browser/render_view_host.h"
     32 #include "content/public/browser/render_widget_host_view.h"
     33 #include "content/public/browser/storage_partition.h"
     34 #include "content/public/browser/web_contents.h"
     35 #include "content/public/browser/web_contents_delegate.h"
     36 #include "content/public/common/content_client.h"
     37 #include "content/public/common/page_transition_types.h"
     38 #include "content/public/common/referrer.h"
     39 #include "ipc/ipc_sender.h"
     40 #include "net/base/net_util.h"
     41 #include "third_party/WebKit/public/web/WebInputEvent.h"
     42 #include "ui/gfx/codec/jpeg_codec.h"
     43 #include "ui/gfx/codec/png_codec.h"
     44 #include "ui/gfx/size_conversions.h"
     45 #include "ui/snapshot/snapshot.h"
     46 #include "url/gurl.h"
     47 #include "webkit/browser/quota/quota_manager.h"
     48 
     49 using blink::WebGestureEvent;
     50 using blink::WebInputEvent;
     51 using blink::WebMouseEvent;
     52 
     53 namespace content {
     54 
     55 namespace {
     56 
     57 static const char kPng[] = "png";
     58 static const char kJpeg[] = "jpeg";
     59 static int kDefaultScreenshotQuality = 80;
     60 static int kFrameRateThresholdMs = 100;
     61 static int kCaptureRetryLimit = 2;
     62 
     63 void ParseGenericInputParams(base::DictionaryValue* params,
     64                              WebInputEvent* event) {
     65   int modifiers = 0;
     66   if (params->GetInteger(devtools::Input::dispatchMouseEvent::kParamModifiers,
     67                          &modifiers)) {
     68     if (modifiers & 1)
     69       event->modifiers |= WebInputEvent::AltKey;
     70     if (modifiers & 2)
     71       event->modifiers |= WebInputEvent::ControlKey;
     72     if (modifiers & 4)
     73       event->modifiers |= WebInputEvent::MetaKey;
     74     if (modifiers & 8)
     75       event->modifiers |= WebInputEvent::ShiftKey;
     76   }
     77 
     78   params->GetDouble(devtools::Input::dispatchMouseEvent::kParamTimestamp,
     79                     &event->timeStampSeconds);
     80 }
     81 
     82 }  // namespace
     83 
     84 RendererOverridesHandler::RendererOverridesHandler(DevToolsAgentHost* agent)
     85     : agent_(agent),
     86       capture_retry_count_(0),
     87       weak_factory_(this) {
     88   RegisterCommandHandler(
     89       devtools::DOM::setFileInputFiles::kName,
     90       base::Bind(
     91           &RendererOverridesHandler::GrantPermissionsForSetFileInputFiles,
     92           base::Unretained(this)));
     93   RegisterCommandHandler(
     94       devtools::Page::disable::kName,
     95       base::Bind(
     96           &RendererOverridesHandler::PageDisable, base::Unretained(this)));
     97   RegisterCommandHandler(
     98       devtools::Page::handleJavaScriptDialog::kName,
     99       base::Bind(
    100           &RendererOverridesHandler::PageHandleJavaScriptDialog,
    101           base::Unretained(this)));
    102   RegisterCommandHandler(
    103       devtools::Page::navigate::kName,
    104       base::Bind(
    105           &RendererOverridesHandler::PageNavigate,
    106           base::Unretained(this)));
    107   RegisterCommandHandler(
    108       devtools::Page::reload::kName,
    109       base::Bind(
    110           &RendererOverridesHandler::PageReload,
    111           base::Unretained(this)));
    112   RegisterCommandHandler(
    113       devtools::Page::getNavigationHistory::kName,
    114       base::Bind(
    115           &RendererOverridesHandler::PageGetNavigationHistory,
    116           base::Unretained(this)));
    117   RegisterCommandHandler(
    118       devtools::Page::navigateToHistoryEntry::kName,
    119       base::Bind(
    120           &RendererOverridesHandler::PageNavigateToHistoryEntry,
    121           base::Unretained(this)));
    122   RegisterCommandHandler(
    123       devtools::Page::captureScreenshot::kName,
    124       base::Bind(
    125           &RendererOverridesHandler::PageCaptureScreenshot,
    126           base::Unretained(this)));
    127   RegisterCommandHandler(
    128       devtools::Page::canScreencast::kName,
    129       base::Bind(
    130           &RendererOverridesHandler::PageCanScreencast,
    131           base::Unretained(this)));
    132   RegisterCommandHandler(
    133       devtools::Page::startScreencast::kName,
    134       base::Bind(
    135           &RendererOverridesHandler::PageStartScreencast,
    136           base::Unretained(this)));
    137   RegisterCommandHandler(
    138       devtools::Page::stopScreencast::kName,
    139       base::Bind(
    140           &RendererOverridesHandler::PageStopScreencast,
    141           base::Unretained(this)));
    142   RegisterCommandHandler(
    143       devtools::Page::queryUsageAndQuota::kName,
    144       base::Bind(
    145           &RendererOverridesHandler::PageQueryUsageAndQuota,
    146           base::Unretained(this)));
    147   RegisterCommandHandler(
    148       devtools::Input::dispatchMouseEvent::kName,
    149       base::Bind(
    150           &RendererOverridesHandler::InputDispatchMouseEvent,
    151           base::Unretained(this)));
    152   RegisterCommandHandler(
    153       devtools::Input::dispatchGestureEvent::kName,
    154       base::Bind(
    155           &RendererOverridesHandler::InputDispatchGestureEvent,
    156           base::Unretained(this)));
    157 }
    158 
    159 RendererOverridesHandler::~RendererOverridesHandler() {}
    160 
    161 void RendererOverridesHandler::OnClientDetached() {
    162   screencast_command_ = NULL;
    163 }
    164 
    165 void RendererOverridesHandler::OnSwapCompositorFrame(
    166     const cc::CompositorFrameMetadata& frame_metadata) {
    167   last_compositor_frame_metadata_ = frame_metadata;
    168 
    169   if (screencast_command_)
    170     InnerSwapCompositorFrame();
    171 }
    172 
    173 void RendererOverridesHandler::OnVisibilityChanged(bool visible) {
    174   if (!screencast_command_)
    175     return;
    176   NotifyScreencastVisibility(visible);
    177 }
    178 
    179 void RendererOverridesHandler::InnerSwapCompositorFrame() {
    180   if ((base::TimeTicks::Now() - last_frame_time_).InMilliseconds() <
    181           kFrameRateThresholdMs) {
    182     return;
    183   }
    184 
    185   RenderViewHost* host = agent_->GetRenderViewHost();
    186   if (!host->GetView())
    187     return;
    188 
    189   last_frame_time_ = base::TimeTicks::Now();
    190   std::string format;
    191   int quality = kDefaultScreenshotQuality;
    192   double scale = 1;
    193   ParseCaptureParameters(screencast_command_.get(), &format, &quality, &scale);
    194 
    195   RenderWidgetHostViewPort* view_port =
    196       RenderWidgetHostViewPort::FromRWHV(host->GetView());
    197 
    198   gfx::Rect view_bounds = host->GetView()->GetViewBounds();
    199   gfx::Size snapshot_size = gfx::ToFlooredSize(
    200       gfx::ScaleSize(view_bounds.size(), scale));
    201 
    202   view_port->CopyFromCompositingSurface(
    203       view_bounds, snapshot_size,
    204       base::Bind(&RendererOverridesHandler::ScreenshotCaptured,
    205                  weak_factory_.GetWeakPtr(),
    206                  scoped_refptr<DevToolsProtocol::Command>(), format, quality,
    207                  last_compositor_frame_metadata_));
    208 }
    209 
    210 void RendererOverridesHandler::ParseCaptureParameters(
    211     DevToolsProtocol::Command* command,
    212     std::string* format,
    213     int* quality,
    214     double* scale) {
    215   *quality = kDefaultScreenshotQuality;
    216   *scale = 1;
    217   double max_width = -1;
    218   double max_height = -1;
    219   base::DictionaryValue* params = command->params();
    220   if (params) {
    221     params->GetString(devtools::Page::captureScreenshot::kParamFormat,
    222                       format);
    223     params->GetInteger(devtools::Page::captureScreenshot::kParamQuality,
    224                        quality);
    225     params->GetDouble(devtools::Page::captureScreenshot::kParamMaxWidth,
    226                       &max_width);
    227     params->GetDouble(devtools::Page::captureScreenshot::kParamMaxHeight,
    228                       &max_height);
    229   }
    230 
    231   RenderViewHost* host = agent_->GetRenderViewHost();
    232   CHECK(host->GetView());
    233   gfx::Rect view_bounds = host->GetView()->GetViewBounds();
    234   if (max_width > 0)
    235     *scale = std::min(*scale, max_width / view_bounds.width());
    236   if (max_height > 0)
    237     *scale = std::min(*scale, max_height / view_bounds.height());
    238 
    239   if (format->empty())
    240     *format = kPng;
    241   if (*quality < 0 || *quality > 100)
    242     *quality = kDefaultScreenshotQuality;
    243   if (*scale <= 0)
    244     *scale = 0.1;
    245   if (*scale > 5)
    246     *scale = 5;
    247 }
    248 
    249 // DOM agent handlers  --------------------------------------------------------
    250 
    251 scoped_refptr<DevToolsProtocol::Response>
    252 RendererOverridesHandler::GrantPermissionsForSetFileInputFiles(
    253     scoped_refptr<DevToolsProtocol::Command> command) {
    254   base::DictionaryValue* params = command->params();
    255   base::ListValue* file_list = NULL;
    256   const char* param =
    257       devtools::DOM::setFileInputFiles::kParamFiles;
    258   if (!params || !params->GetList(param, &file_list))
    259     return command->InvalidParamResponse(param);
    260   RenderViewHost* host = agent_->GetRenderViewHost();
    261   if (!host)
    262     return NULL;
    263 
    264   for (size_t i = 0; i < file_list->GetSize(); ++i) {
    265     base::FilePath::StringType file;
    266     if (!file_list->GetString(i, &file))
    267       return command->InvalidParamResponse(param);
    268     ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
    269         host->GetProcess()->GetID(), base::FilePath(file));
    270   }
    271   return NULL;
    272 }
    273 
    274 
    275 // Page agent handlers  -------------------------------------------------------
    276 
    277 scoped_refptr<DevToolsProtocol::Response>
    278 RendererOverridesHandler::PageDisable(
    279     scoped_refptr<DevToolsProtocol::Command> command) {
    280   screencast_command_ = NULL;
    281   return NULL;
    282 }
    283 
    284 scoped_refptr<DevToolsProtocol::Response>
    285 RendererOverridesHandler::PageHandleJavaScriptDialog(
    286     scoped_refptr<DevToolsProtocol::Command> command) {
    287   base::DictionaryValue* params = command->params();
    288   const char* paramAccept =
    289       devtools::Page::handleJavaScriptDialog::kParamAccept;
    290   bool accept;
    291   if (!params || !params->GetBoolean(paramAccept, &accept))
    292     return command->InvalidParamResponse(paramAccept);
    293   base::string16 prompt_override;
    294   base::string16* prompt_override_ptr = &prompt_override;
    295   if (!params || !params->GetString(
    296       devtools::Page::handleJavaScriptDialog::kParamPromptText,
    297       prompt_override_ptr)) {
    298     prompt_override_ptr = NULL;
    299   }
    300 
    301   RenderViewHost* host = agent_->GetRenderViewHost();
    302   if (host) {
    303     WebContents* web_contents = host->GetDelegate()->GetAsWebContents();
    304     if (web_contents) {
    305       JavaScriptDialogManager* manager =
    306           web_contents->GetDelegate()->GetJavaScriptDialogManager();
    307       if (manager && manager->HandleJavaScriptDialog(
    308               web_contents, accept, prompt_override_ptr)) {
    309         return NULL;
    310       }
    311     }
    312   }
    313   return command->InternalErrorResponse("No JavaScript dialog to handle");
    314 }
    315 
    316 scoped_refptr<DevToolsProtocol::Response>
    317 RendererOverridesHandler::PageNavigate(
    318     scoped_refptr<DevToolsProtocol::Command> command) {
    319   base::DictionaryValue* params = command->params();
    320   std::string url;
    321   const char* param = devtools::Page::navigate::kParamUrl;
    322   if (!params || !params->GetString(param, &url))
    323     return command->InvalidParamResponse(param);
    324   GURL gurl(url);
    325   if (!gurl.is_valid()) {
    326     return command->InternalErrorResponse("Cannot navigate to invalid URL");
    327   }
    328   RenderViewHost* host = agent_->GetRenderViewHost();
    329   if (host) {
    330     WebContents* web_contents = host->GetDelegate()->GetAsWebContents();
    331     if (web_contents) {
    332       web_contents->GetController()
    333           .LoadURL(gurl, Referrer(), PAGE_TRANSITION_TYPED, std::string());
    334       return command->SuccessResponse(new base::DictionaryValue());
    335     }
    336   }
    337   return command->InternalErrorResponse("No WebContents to navigate");
    338 }
    339 
    340 scoped_refptr<DevToolsProtocol::Response>
    341 RendererOverridesHandler::PageReload(
    342     scoped_refptr<DevToolsProtocol::Command> command) {
    343   RenderViewHost* host = agent_->GetRenderViewHost();
    344   if (host) {
    345     WebContents* web_contents = host->GetDelegate()->GetAsWebContents();
    346     if (web_contents) {
    347       // Override only if it is crashed.
    348       if (!web_contents->IsCrashed())
    349         return NULL;
    350 
    351       web_contents->GetController().Reload(false);
    352       return command->SuccessResponse(NULL);
    353     }
    354   }
    355   return command->InternalErrorResponse("No WebContents to reload");
    356 }
    357 
    358 scoped_refptr<DevToolsProtocol::Response>
    359 RendererOverridesHandler::PageGetNavigationHistory(
    360     scoped_refptr<DevToolsProtocol::Command> command) {
    361   RenderViewHost* host = agent_->GetRenderViewHost();
    362   if (host) {
    363     WebContents* web_contents = host->GetDelegate()->GetAsWebContents();
    364     if (web_contents) {
    365       base::DictionaryValue* result = new base::DictionaryValue();
    366       NavigationController& controller = web_contents->GetController();
    367       result->SetInteger(
    368           devtools::Page::getNavigationHistory::kResponseCurrentIndex,
    369           controller.GetCurrentEntryIndex());
    370       ListValue* entries = new ListValue();
    371       for (int i = 0; i != controller.GetEntryCount(); ++i) {
    372         const NavigationEntry* entry = controller.GetEntryAtIndex(i);
    373         base::DictionaryValue* entry_value = new base::DictionaryValue();
    374         entry_value->SetInteger(
    375             devtools::Page::NavigationEntry::kParamId,
    376             entry->GetUniqueID());
    377         entry_value->SetString(
    378             devtools::Page::NavigationEntry::kParamUrl,
    379             entry->GetURL().spec());
    380         entry_value->SetString(
    381             devtools::Page::NavigationEntry::kParamTitle,
    382             entry->GetTitle());
    383         entries->Append(entry_value);
    384       }
    385       result->Set(
    386           devtools::Page::getNavigationHistory::kResponseEntries,
    387           entries);
    388       return command->SuccessResponse(result);
    389     }
    390   }
    391   return command->InternalErrorResponse("No WebContents to navigate");
    392 }
    393 
    394 scoped_refptr<DevToolsProtocol::Response>
    395 RendererOverridesHandler::PageNavigateToHistoryEntry(
    396     scoped_refptr<DevToolsProtocol::Command> command) {
    397   int entry_id;
    398 
    399   base::DictionaryValue* params = command->params();
    400   const char* param = devtools::Page::navigateToHistoryEntry::kParamEntryId;
    401   if (!params || !params->GetInteger(param, &entry_id)) {
    402     return command->InvalidParamResponse(param);
    403   }
    404 
    405   RenderViewHost* host = agent_->GetRenderViewHost();
    406   if (host) {
    407     WebContents* web_contents = host->GetDelegate()->GetAsWebContents();
    408     if (web_contents) {
    409       NavigationController& controller = web_contents->GetController();
    410       for (int i = 0; i != controller.GetEntryCount(); ++i) {
    411         if (controller.GetEntryAtIndex(i)->GetUniqueID() == entry_id) {
    412           controller.GoToIndex(i);
    413           return command->SuccessResponse(new base::DictionaryValue());
    414         }
    415       }
    416       return command->InvalidParamResponse(param);
    417     }
    418   }
    419   return command->InternalErrorResponse("No WebContents to navigate");
    420 }
    421 
    422 scoped_refptr<DevToolsProtocol::Response>
    423 RendererOverridesHandler::PageCaptureScreenshot(
    424     scoped_refptr<DevToolsProtocol::Command> command) {
    425   RenderViewHost* host = agent_->GetRenderViewHost();
    426   if (!host->GetView())
    427     return command->InternalErrorResponse("Unable to access the view");
    428 
    429   std::string format;
    430   int quality = kDefaultScreenshotQuality;
    431   double scale = 1;
    432   ParseCaptureParameters(command.get(), &format, &quality, &scale);
    433 
    434   gfx::Rect view_bounds = host->GetView()->GetViewBounds();
    435   gfx::Size snapshot_size = gfx::ToFlooredSize(
    436       gfx::ScaleSize(view_bounds.size(), scale));
    437 
    438   // Grab screen pixels if available for current platform.
    439   // TODO(pfeldman): support format, scale and quality in ui::GrabViewSnapshot.
    440   std::vector<unsigned char> png;
    441   bool is_unscaled_png = scale == 1 && format == kPng;
    442   if (is_unscaled_png && ui::GrabViewSnapshot(host->GetView()->GetNativeView(),
    443                                               &png,
    444                                               gfx::Rect(snapshot_size))) {
    445     std::string base64_data;
    446     base::Base64Encode(
    447         base::StringPiece(reinterpret_cast<char*>(&*png.begin()), png.size()),
    448         &base64_data);
    449     base::DictionaryValue* result = new base::DictionaryValue();
    450     result->SetString(
    451         devtools::Page::captureScreenshot::kResponseData, base64_data);
    452     return command->SuccessResponse(result);
    453   }
    454 
    455   // Fallback to copying from compositing surface.
    456   RenderWidgetHostViewPort* view_port =
    457       RenderWidgetHostViewPort::FromRWHV(host->GetView());
    458 
    459   view_port->CopyFromCompositingSurface(
    460       view_bounds, snapshot_size,
    461       base::Bind(&RendererOverridesHandler::ScreenshotCaptured,
    462                  weak_factory_.GetWeakPtr(), command, format, quality,
    463                  last_compositor_frame_metadata_));
    464   return command->AsyncResponsePromise();
    465 }
    466 
    467 scoped_refptr<DevToolsProtocol::Response>
    468 RendererOverridesHandler::PageCanScreencast(
    469     scoped_refptr<DevToolsProtocol::Command> command) {
    470   base::DictionaryValue* result = new base::DictionaryValue();
    471 #if defined(OS_ANDROID)
    472   result->SetBoolean(devtools::kResult, true);
    473 #else
    474   result->SetBoolean(devtools::kResult, false);
    475 #endif  // defined(OS_ANDROID)
    476   return command->SuccessResponse(result);
    477 }
    478 
    479 scoped_refptr<DevToolsProtocol::Response>
    480 RendererOverridesHandler::PageStartScreencast(
    481     scoped_refptr<DevToolsProtocol::Command> command) {
    482   screencast_command_ = command;
    483   RenderViewHostImpl* host = static_cast<RenderViewHostImpl*>(
    484       agent_->GetRenderViewHost());
    485   bool visible = !host->is_hidden();
    486   NotifyScreencastVisibility(visible);
    487   if (visible)
    488     InnerSwapCompositorFrame();
    489   return command->SuccessResponse(NULL);
    490 }
    491 
    492 scoped_refptr<DevToolsProtocol::Response>
    493 RendererOverridesHandler::PageStopScreencast(
    494     scoped_refptr<DevToolsProtocol::Command> command) {
    495   last_frame_time_ = base::TimeTicks();
    496   screencast_command_ = NULL;
    497   return command->SuccessResponse(NULL);
    498 }
    499 
    500 void RendererOverridesHandler::ScreenshotCaptured(
    501     scoped_refptr<DevToolsProtocol::Command> command,
    502     const std::string& format,
    503     int quality,
    504     const cc::CompositorFrameMetadata& metadata,
    505     bool success,
    506     const SkBitmap& bitmap) {
    507   if (!success) {
    508     if (command) {
    509       SendAsyncResponse(
    510           command->InternalErrorResponse("Unable to capture screenshot"));
    511     } else if (capture_retry_count_) {
    512       --capture_retry_count_;
    513       base::MessageLoop::current()->PostDelayedTask(
    514           FROM_HERE,
    515           base::Bind(&RendererOverridesHandler::InnerSwapCompositorFrame,
    516                      weak_factory_.GetWeakPtr()),
    517           base::TimeDelta::FromMilliseconds(kFrameRateThresholdMs));
    518     }
    519     return;
    520   }
    521 
    522   std::vector<unsigned char> data;
    523   SkAutoLockPixels lock_image(bitmap);
    524   bool encoded;
    525   if (format == kPng) {
    526     encoded = gfx::PNGCodec::Encode(
    527         reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
    528         gfx::PNGCodec::FORMAT_SkBitmap,
    529         gfx::Size(bitmap.width(), bitmap.height()),
    530         bitmap.width() * bitmap.bytesPerPixel(),
    531         false, std::vector<gfx::PNGCodec::Comment>(), &data);
    532   } else if (format == kJpeg) {
    533     encoded = gfx::JPEGCodec::Encode(
    534         reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
    535         gfx::JPEGCodec::FORMAT_SkBitmap,
    536         bitmap.width(),
    537         bitmap.height(),
    538         bitmap.width() * bitmap.bytesPerPixel(),
    539         quality, &data);
    540   } else {
    541     encoded = false;
    542   }
    543 
    544   if (!encoded) {
    545     if (command) {
    546       SendAsyncResponse(
    547           command->InternalErrorResponse("Unable to encode screenshot"));
    548     }
    549     return;
    550   }
    551 
    552   std::string base_64_data;
    553   base::Base64Encode(
    554       base::StringPiece(reinterpret_cast<char*>(&data[0]), data.size()),
    555       &base_64_data);
    556 
    557   base::DictionaryValue* response = new base::DictionaryValue();
    558   response->SetString(devtools::Page::screencastFrame::kParamData,
    559                       base_64_data);
    560 
    561   // Consider metadata empty in case it has no device scale factor.
    562   if (metadata.device_scale_factor != 0) {
    563     base::DictionaryValue* response_metadata = new base::DictionaryValue();
    564 
    565     response_metadata->SetDouble(
    566         devtools::Page::ScreencastFrameMetadata::kParamDeviceScaleFactor,
    567         metadata.device_scale_factor);
    568     response_metadata->SetDouble(
    569         devtools::Page::ScreencastFrameMetadata::kParamPageScaleFactor,
    570         metadata.page_scale_factor);
    571     response_metadata->SetDouble(
    572         devtools::Page::ScreencastFrameMetadata::kParamPageScaleFactorMin,
    573         metadata.min_page_scale_factor);
    574     response_metadata->SetDouble(
    575         devtools::Page::ScreencastFrameMetadata::kParamPageScaleFactorMax,
    576         metadata.max_page_scale_factor);
    577     response_metadata->SetDouble(
    578         devtools::Page::ScreencastFrameMetadata::kParamOffsetTop,
    579         metadata.location_bar_content_translation.y());
    580     response_metadata->SetDouble(
    581         devtools::Page::ScreencastFrameMetadata::kParamOffsetBottom,
    582         metadata.overdraw_bottom_height);
    583 
    584     base::DictionaryValue* viewport = new base::DictionaryValue();
    585     viewport->SetDouble(devtools::DOM::Rect::kParamX,
    586                         metadata.root_scroll_offset.x());
    587     viewport->SetDouble(devtools::DOM::Rect::kParamY,
    588                         metadata.root_scroll_offset.y());
    589     viewport->SetDouble(devtools::DOM::Rect::kParamWidth,
    590                         metadata.viewport_size.width());
    591     viewport->SetDouble(devtools::DOM::Rect::kParamHeight,
    592                         metadata.viewport_size.height());
    593     response_metadata->Set(
    594         devtools::Page::ScreencastFrameMetadata::kParamViewport, viewport);
    595 
    596     if (command) {
    597       response->Set(devtools::Page::captureScreenshot::kResponseMetadata,
    598                     response_metadata);
    599     } else {
    600       response->Set(devtools::Page::screencastFrame::kParamMetadata,
    601                     response_metadata);
    602     }
    603   }
    604 
    605   if (command) {
    606     SendAsyncResponse(command->SuccessResponse(response));
    607   } else {
    608     SendNotification(devtools::Page::screencastFrame::kName, response);
    609   }
    610 }
    611 
    612 // Quota and Usage ------------------------------------------
    613 
    614 namespace {
    615 
    616 typedef base::Callback<void(scoped_ptr<base::DictionaryValue>)>
    617     ResponseCallback;
    618 
    619 void QueryUsageAndQuotaCompletedOnIOThread(
    620     scoped_ptr<base::DictionaryValue> quota,
    621     scoped_ptr<base::DictionaryValue> usage,
    622     ResponseCallback callback) {
    623 
    624   scoped_ptr<base::DictionaryValue> response_data(new base::DictionaryValue);
    625   response_data->Set(devtools::Page::queryUsageAndQuota::kResponseQuota,
    626                      quota.release());
    627   response_data->Set(devtools::Page::queryUsageAndQuota::kResponseUsage,
    628                      usage.release());
    629 
    630   BrowserThread::PostTask(
    631       BrowserThread::UI, FROM_HERE,
    632       base::Bind(callback, base::Passed(&response_data)));
    633 }
    634 
    635 void DidGetHostUsage(
    636     base::ListValue* list,
    637     const std::string& client_id,
    638     const base::Closure& barrier,
    639     int64 value) {
    640   base::DictionaryValue* usage_item = new base::DictionaryValue;
    641   usage_item->SetString(devtools::Page::UsageItem::kParamId, client_id);
    642   usage_item->SetDouble(devtools::Page::UsageItem::kParamValue, value);
    643   list->Append(usage_item);
    644   barrier.Run();
    645 }
    646 
    647 void DidGetQuotaValue(
    648     base::DictionaryValue* dictionary,
    649     const std::string& item_name,
    650     const base::Closure& barrier,
    651     quota::QuotaStatusCode status,
    652     int64 value) {
    653   if (status == quota::kQuotaStatusOk)
    654     dictionary->SetDouble(item_name, value);
    655   barrier.Run();
    656 }
    657 
    658 void DidGetUsageAndQuotaForWebApps(
    659     base::DictionaryValue* quota,
    660     const std::string& item_name,
    661     const base::Closure& barrier,
    662     quota::QuotaStatusCode status,
    663     int64 used_bytes,
    664     int64 quota_in_bytes) {
    665   if (status == quota::kQuotaStatusOk)
    666     quota->SetDouble(item_name, quota_in_bytes);
    667   barrier.Run();
    668 }
    669 
    670 std::string GetStorageTypeName(quota::StorageType type) {
    671   switch (type) {
    672     case quota::kStorageTypeTemporary:
    673       return devtools::Page::Usage::kParamTemporary;
    674     case quota::kStorageTypePersistent:
    675       return devtools::Page::Usage::kParamPersistent;
    676     case quota::kStorageTypeSyncable:
    677       return devtools::Page::Usage::kParamSyncable;
    678     case quota::kStorageTypeQuotaNotManaged:
    679     case quota::kStorageTypeUnknown:
    680       NOTREACHED();
    681   }
    682   return "";
    683 }
    684 
    685 std::string GetQuotaClientName(quota::QuotaClient::ID id) {
    686   switch (id) {
    687     case quota::QuotaClient::kFileSystem:
    688       return devtools::Page::UsageItem::Id::kEnumFilesystem;
    689     case quota::QuotaClient::kDatabase:
    690       return devtools::Page::UsageItem::Id::kEnumDatabase;
    691     case quota::QuotaClient::kAppcache:
    692       return devtools::Page::UsageItem::Id::kEnumAppcache;
    693     case quota::QuotaClient::kIndexedDatabase:
    694       return devtools::Page::UsageItem::Id::kEnumIndexeddatabase;
    695     default:
    696       NOTREACHED();
    697       return "";
    698   }
    699 }
    700 
    701 void QueryUsageAndQuotaOnIOThread(
    702     scoped_refptr<quota::QuotaManager> quota_manager,
    703     const GURL& security_origin,
    704     const ResponseCallback& callback) {
    705   scoped_ptr<base::DictionaryValue> quota(new base::DictionaryValue);
    706   scoped_ptr<base::DictionaryValue> usage(new base::DictionaryValue);
    707 
    708   static quota::QuotaClient::ID kQuotaClients[] = {
    709       quota::QuotaClient::kFileSystem,
    710       quota::QuotaClient::kDatabase,
    711       quota::QuotaClient::kAppcache,
    712       quota::QuotaClient::kIndexedDatabase
    713   };
    714 
    715   static const size_t kStorageTypeCount = quota::kStorageTypeUnknown;
    716   std::map<quota::StorageType, base::ListValue*> storage_type_lists;
    717 
    718   for (size_t i = 0; i != kStorageTypeCount; i++) {
    719     const quota::StorageType type = static_cast<quota::StorageType>(i);
    720     if (type == quota::kStorageTypeQuotaNotManaged)
    721       continue;
    722     storage_type_lists[type] = new base::ListValue;
    723     usage->Set(GetStorageTypeName(type), storage_type_lists[type]);
    724   }
    725 
    726   const int kExpectedResults =
    727       2 + arraysize(kQuotaClients) * storage_type_lists.size();
    728   base::DictionaryValue* quota_raw_ptr = quota.get();
    729 
    730   // Takes ownership on usage and quota.
    731   base::Closure barrier = BarrierClosure(
    732       kExpectedResults,
    733       base::Bind(&QueryUsageAndQuotaCompletedOnIOThread,
    734                  base::Passed(&quota),
    735                  base::Passed(&usage),
    736                  callback));
    737   std::string host = net::GetHostOrSpecFromURL(security_origin);
    738 
    739   quota_manager->GetUsageAndQuotaForWebApps(
    740       security_origin,
    741       quota::kStorageTypeTemporary,
    742       base::Bind(&DidGetUsageAndQuotaForWebApps, quota_raw_ptr,
    743                  std::string(devtools::Page::Quota::kParamTemporary), barrier));
    744 
    745   quota_manager->GetPersistentHostQuota(
    746       host,
    747       base::Bind(&DidGetQuotaValue, quota_raw_ptr,
    748                  std::string(devtools::Page::Quota::kParamPersistent),
    749                  barrier));
    750 
    751   for (size_t i = 0; i != arraysize(kQuotaClients); i++) {
    752     std::map<quota::StorageType, base::ListValue*>::const_iterator iter;
    753     for (iter = storage_type_lists.begin();
    754          iter != storage_type_lists.end(); ++iter) {
    755       const quota::StorageType type = (*iter).first;
    756       if (!quota_manager->IsTrackingHostUsage(type, kQuotaClients[i])) {
    757         barrier.Run();
    758         continue;
    759       }
    760       quota_manager->GetHostUsage(
    761           host, type, kQuotaClients[i],
    762           base::Bind(&DidGetHostUsage, (*iter).second,
    763                      GetQuotaClientName(kQuotaClients[i]),
    764                      barrier));
    765     }
    766   }
    767 }
    768 
    769 } // namespace
    770 
    771 scoped_refptr<DevToolsProtocol::Response>
    772 RendererOverridesHandler::PageQueryUsageAndQuota(
    773     scoped_refptr<DevToolsProtocol::Command> command) {
    774   base::DictionaryValue* params = command->params();
    775   std::string security_origin;
    776   if (!params || !params->GetString(
    777       devtools::Page::queryUsageAndQuota::kParamSecurityOrigin,
    778       &security_origin)) {
    779     return command->InvalidParamResponse(
    780         devtools::Page::queryUsageAndQuota::kParamSecurityOrigin);
    781   }
    782 
    783   ResponseCallback callback = base::Bind(
    784       &RendererOverridesHandler::PageQueryUsageAndQuotaCompleted,
    785       weak_factory_.GetWeakPtr(),
    786       command);
    787 
    788   scoped_refptr<quota::QuotaManager> quota_manager =
    789       agent_->GetRenderViewHost()->GetProcess()->
    790           GetStoragePartition()->GetQuotaManager();
    791 
    792   BrowserThread::PostTask(
    793       BrowserThread::IO, FROM_HERE,
    794       base::Bind(
    795           &QueryUsageAndQuotaOnIOThread,
    796           quota_manager,
    797           GURL(security_origin),
    798           callback));
    799 
    800   return command->AsyncResponsePromise();
    801 }
    802 
    803 void RendererOverridesHandler::PageQueryUsageAndQuotaCompleted(
    804     scoped_refptr<DevToolsProtocol::Command> command,
    805     scoped_ptr<base::DictionaryValue> response_data) {
    806   SendAsyncResponse(command->SuccessResponse(response_data.release()));
    807 }
    808 
    809 void RendererOverridesHandler::NotifyScreencastVisibility(bool visible) {
    810   if (visible)
    811     capture_retry_count_ = kCaptureRetryLimit;
    812   base::DictionaryValue* params = new base::DictionaryValue();
    813   params->SetBoolean(
    814       devtools::Page::screencastVisibilityChanged::kParamVisible, visible);
    815   SendNotification(
    816       devtools::Page::screencastVisibilityChanged::kName, params);
    817 }
    818 
    819 // Input agent handlers  ------------------------------------------------------
    820 
    821 scoped_refptr<DevToolsProtocol::Response>
    822 RendererOverridesHandler::InputDispatchMouseEvent(
    823     scoped_refptr<DevToolsProtocol::Command> command) {
    824   base::DictionaryValue* params = command->params();
    825   if (!params)
    826     return NULL;
    827 
    828   bool device_space = false;
    829   if (!params->GetBoolean(
    830           devtools::Input::dispatchMouseEvent::kParamDeviceSpace,
    831           &device_space) ||
    832       !device_space) {
    833     return NULL;
    834   }
    835 
    836   RenderViewHost* host = agent_->GetRenderViewHost();
    837   blink::WebMouseEvent mouse_event;
    838   ParseGenericInputParams(params, &mouse_event);
    839 
    840   std::string type;
    841   if (params->GetString(devtools::Input::dispatchMouseEvent::kParamType,
    842                         &type)) {
    843     if (type ==
    844         devtools::Input::dispatchMouseEvent::Type::kEnumMousePressed)
    845       mouse_event.type = WebInputEvent::MouseDown;
    846     else if (type ==
    847         devtools::Input::dispatchMouseEvent::Type::kEnumMouseReleased)
    848       mouse_event.type = WebInputEvent::MouseUp;
    849     else if (type ==
    850         devtools::Input::dispatchMouseEvent::Type::kEnumMouseMoved)
    851       mouse_event.type = WebInputEvent::MouseMove;
    852     else
    853       return NULL;
    854   } else {
    855     return NULL;
    856   }
    857 
    858   if (!params->GetInteger(devtools::Input::dispatchMouseEvent::kParamX,
    859                           &mouse_event.x) ||
    860       !params->GetInteger(devtools::Input::dispatchMouseEvent::kParamY,
    861                           &mouse_event.y)) {
    862     return NULL;
    863   }
    864 
    865   mouse_event.windowX = mouse_event.x;
    866   mouse_event.windowY = mouse_event.y;
    867   mouse_event.globalX = mouse_event.x;
    868   mouse_event.globalY = mouse_event.y;
    869 
    870   params->GetInteger(devtools::Input::dispatchMouseEvent::kParamClickCount,
    871                      &mouse_event.clickCount);
    872 
    873   std::string button;
    874   if (!params->GetString(devtools::Input::dispatchMouseEvent::kParamButton,
    875                          &button)) {
    876     return NULL;
    877   }
    878 
    879   if (button == "none") {
    880     mouse_event.button = WebMouseEvent::ButtonNone;
    881   } else if (button == "left") {
    882     mouse_event.button = WebMouseEvent::ButtonLeft;
    883     mouse_event.modifiers |= WebInputEvent::LeftButtonDown;
    884   } else if (button == "middle") {
    885     mouse_event.button = WebMouseEvent::ButtonMiddle;
    886     mouse_event.modifiers |= WebInputEvent::MiddleButtonDown;
    887   } else if (button == "right") {
    888     mouse_event.button = WebMouseEvent::ButtonRight;
    889     mouse_event.modifiers |= WebInputEvent::RightButtonDown;
    890   } else {
    891     return NULL;
    892   }
    893 
    894   host->ForwardMouseEvent(mouse_event);
    895   return command->SuccessResponse(NULL);
    896 }
    897 
    898 scoped_refptr<DevToolsProtocol::Response>
    899 RendererOverridesHandler::InputDispatchGestureEvent(
    900     scoped_refptr<DevToolsProtocol::Command> command) {
    901   base::DictionaryValue* params = command->params();
    902   if (!params)
    903     return NULL;
    904 
    905   RenderViewHostImpl* host = static_cast<RenderViewHostImpl*>(
    906       agent_->GetRenderViewHost());
    907   blink::WebGestureEvent event;
    908   ParseGenericInputParams(params, &event);
    909 
    910   std::string type;
    911   if (params->GetString(devtools::Input::dispatchGestureEvent::kParamType,
    912                         &type)) {
    913     if (type ==
    914         devtools::Input::dispatchGestureEvent::Type::kEnumScrollBegin)
    915       event.type = WebInputEvent::GestureScrollBegin;
    916     else if (type ==
    917         devtools::Input::dispatchGestureEvent::Type::kEnumScrollUpdate)
    918       event.type = WebInputEvent::GestureScrollUpdate;
    919     else if (type ==
    920         devtools::Input::dispatchGestureEvent::Type::kEnumScrollEnd)
    921       event.type = WebInputEvent::GestureScrollEnd;
    922     else if (type ==
    923         devtools::Input::dispatchGestureEvent::Type::kEnumTapDown)
    924       event.type = WebInputEvent::GestureTapDown;
    925     else if (type ==
    926         devtools::Input::dispatchGestureEvent::Type::kEnumTap)
    927       event.type = WebInputEvent::GestureTap;
    928     else if (type ==
    929         devtools::Input::dispatchGestureEvent::Type::kEnumPinchBegin)
    930       event.type = WebInputEvent::GesturePinchBegin;
    931     else if (type ==
    932         devtools::Input::dispatchGestureEvent::Type::kEnumPinchUpdate)
    933       event.type = WebInputEvent::GesturePinchUpdate;
    934     else if (type ==
    935         devtools::Input::dispatchGestureEvent::Type::kEnumPinchEnd)
    936       event.type = WebInputEvent::GesturePinchEnd;
    937     else
    938       return NULL;
    939   } else {
    940     return NULL;
    941   }
    942 
    943   if (!params->GetInteger(devtools::Input::dispatchGestureEvent::kParamX,
    944                           &event.x) ||
    945       !params->GetInteger(devtools::Input::dispatchGestureEvent::kParamY,
    946                           &event.y)) {
    947     return NULL;
    948   }
    949   event.globalX = event.x;
    950   event.globalY = event.y;
    951 
    952   if (type == "scrollUpdate") {
    953     int dx;
    954     int dy;
    955     if (!params->GetInteger(
    956             devtools::Input::dispatchGestureEvent::kParamDeltaX, &dx) ||
    957         !params->GetInteger(
    958             devtools::Input::dispatchGestureEvent::kParamDeltaY, &dy)) {
    959       return NULL;
    960     }
    961     event.data.scrollUpdate.deltaX = dx;
    962     event.data.scrollUpdate.deltaY = dy;
    963   }
    964 
    965   if (type == "pinchUpdate") {
    966     double scale;
    967     if (!params->GetDouble(
    968         devtools::Input::dispatchGestureEvent::kParamPinchScale,
    969         &scale)) {
    970       return NULL;
    971     }
    972     event.data.pinchUpdate.scale = static_cast<float>(scale);
    973   }
    974 
    975   host->ForwardGestureEvent(event);
    976   return command->SuccessResponse(NULL);
    977 }
    978 
    979 }  // namespace content
    980