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