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
49using blink::WebGestureEvent;
50using blink::WebInputEvent;
51using blink::WebMouseEvent;
52
53namespace content {
54
55namespace {
56
57static const char kPng[] = "png";
58static const char kJpeg[] = "jpeg";
59static int kDefaultScreenshotQuality = 80;
60static int kFrameRateThresholdMs = 100;
61static int kCaptureRetryLimit = 2;
62
63void 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
84RendererOverridesHandler::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
159RendererOverridesHandler::~RendererOverridesHandler() {}
160
161void RendererOverridesHandler::OnClientDetached() {
162  screencast_command_ = NULL;
163}
164
165void RendererOverridesHandler::OnSwapCompositorFrame(
166    const cc::CompositorFrameMetadata& frame_metadata) {
167  last_compositor_frame_metadata_ = frame_metadata;
168
169  if (screencast_command_)
170    InnerSwapCompositorFrame();
171}
172
173void RendererOverridesHandler::OnVisibilityChanged(bool visible) {
174  if (!screencast_command_)
175    return;
176  NotifyScreencastVisibility(visible);
177}
178
179void 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
210void 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
251scoped_refptr<DevToolsProtocol::Response>
252RendererOverridesHandler::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
277scoped_refptr<DevToolsProtocol::Response>
278RendererOverridesHandler::PageDisable(
279    scoped_refptr<DevToolsProtocol::Command> command) {
280  screencast_command_ = NULL;
281  return NULL;
282}
283
284scoped_refptr<DevToolsProtocol::Response>
285RendererOverridesHandler::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
316scoped_refptr<DevToolsProtocol::Response>
317RendererOverridesHandler::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
340scoped_refptr<DevToolsProtocol::Response>
341RendererOverridesHandler::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
358scoped_refptr<DevToolsProtocol::Response>
359RendererOverridesHandler::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
394scoped_refptr<DevToolsProtocol::Response>
395RendererOverridesHandler::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
422scoped_refptr<DevToolsProtocol::Response>
423RendererOverridesHandler::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
467scoped_refptr<DevToolsProtocol::Response>
468RendererOverridesHandler::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
479scoped_refptr<DevToolsProtocol::Response>
480RendererOverridesHandler::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
492scoped_refptr<DevToolsProtocol::Response>
493RendererOverridesHandler::PageStopScreencast(
494    scoped_refptr<DevToolsProtocol::Command> command) {
495  last_frame_time_ = base::TimeTicks();
496  screencast_command_ = NULL;
497  return command->SuccessResponse(NULL);
498}
499
500void 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
614namespace {
615
616typedef base::Callback<void(scoped_ptr<base::DictionaryValue>)>
617    ResponseCallback;
618
619void 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
635void 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
647void 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
658void 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
670std::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
685std::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
701void 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
771scoped_refptr<DevToolsProtocol::Response>
772RendererOverridesHandler::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
803void RendererOverridesHandler::PageQueryUsageAndQuotaCompleted(
804    scoped_refptr<DevToolsProtocol::Command> command,
805    scoped_ptr<base::DictionaryValue> response_data) {
806  SendAsyncResponse(command->SuccessResponse(response_data.release()));
807}
808
809void 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
821scoped_refptr<DevToolsProtocol::Response>
822RendererOverridesHandler::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
898scoped_refptr<DevToolsProtocol::Response>
899RendererOverridesHandler::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