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