DevTools: Protocol handler generator for content
[chromium-blink-merge.git] / content / browser / devtools / renderer_overrides_handler.cc
blobe0b45daa0d88a0b169da7508f9e6868ce0e5b13e
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.
5 #include "content/browser/devtools/renderer_overrides_handler.h"
7 #include <map>
8 #include <string>
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/thread_task_runner_handle.h"
17 #include "base/values.h"
18 #include "content/browser/child_process_security_policy_impl.h"
19 #include "content/browser/devtools/devtools_protocol_constants.h"
20 #include "content/browser/devtools/devtools_tracing_handler.h"
21 #include "content/browser/renderer_host/dip_util.h"
22 #include "content/browser/renderer_host/render_view_host_delegate.h"
23 #include "content/browser/renderer_host/render_view_host_impl.h"
24 #include "content/browser/renderer_host/render_widget_host_view_base.h"
25 #include "content/browser/web_contents/web_contents_impl.h"
26 #include "content/common/cursors/webcursor.h"
27 #include "content/common/view_messages.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/content_browser_client.h"
30 #include "content/public/browser/devtools_agent_host.h"
31 #include "content/public/browser/javascript_dialog_manager.h"
32 #include "content/public/browser/navigation_controller.h"
33 #include "content/public/browser/navigation_entry.h"
34 #include "content/public/browser/render_process_host.h"
35 #include "content/public/browser/render_view_host.h"
36 #include "content/public/browser/render_widget_host_view.h"
37 #include "content/public/browser/storage_partition.h"
38 #include "content/public/browser/web_contents.h"
39 #include "content/public/browser/web_contents_delegate.h"
40 #include "content/public/common/content_client.h"
41 #include "content/public/common/referrer.h"
42 #include "content/public/common/url_constants.h"
43 #include "ipc/ipc_sender.h"
44 #include "net/base/net_util.h"
45 #include "storage/browser/quota/quota_manager.h"
46 #include "third_party/WebKit/public/platform/WebCursorInfo.h"
47 #include "third_party/WebKit/public/platform/WebScreenInfo.h"
48 #include "third_party/WebKit/public/web/WebInputEvent.h"
49 #include "third_party/skia/include/core/SkCanvas.h"
50 #include "ui/base/page_transition_types.h"
51 #include "ui/gfx/codec/jpeg_codec.h"
52 #include "ui/gfx/codec/png_codec.h"
53 #include "ui/gfx/display.h"
54 #include "ui/gfx/screen.h"
55 #include "ui/gfx/size_conversions.h"
56 #include "ui/snapshot/snapshot.h"
57 #include "url/gurl.h"
59 using blink::WebGestureEvent;
60 using blink::WebInputEvent;
61 using blink::WebMouseEvent;
63 namespace content {
65 namespace {
67 static const char kPng[] = "png";
68 static const char kJpeg[] = "jpeg";
69 static int kDefaultScreenshotQuality = 80;
70 static int kFrameRateThresholdMs = 100;
71 static int kCaptureRetryLimit = 2;
73 } // namespace
75 RendererOverridesHandler::RendererOverridesHandler()
76 : page_domain_enabled_(false),
77 has_last_compositor_frame_metadata_(false),
78 capture_retry_count_(0),
79 touch_emulation_enabled_(false),
80 color_picker_enabled_(false),
81 last_cursor_x_(-1),
82 last_cursor_y_(-1),
83 weak_factory_(this) {
84 RegisterCommandHandler(
85 devtools::DOM::setFileInputFiles::kName,
86 base::Bind(
87 &RendererOverridesHandler::GrantPermissionsForSetFileInputFiles,
88 base::Unretained(this)));
89 RegisterCommandHandler(
90 devtools::Network::canEmulateNetworkConditions::kName,
91 base::Bind(
92 &RendererOverridesHandler::CanEmulateNetworkConditions,
93 base::Unretained(this)));
94 RegisterCommandHandler(
95 devtools::Network::clearBrowserCache::kName,
96 base::Bind(
97 &RendererOverridesHandler::ClearBrowserCache,
98 base::Unretained(this)));
99 RegisterCommandHandler(
100 devtools::Network::clearBrowserCookies::kName,
101 base::Bind(
102 &RendererOverridesHandler::ClearBrowserCookies,
103 base::Unretained(this)));
104 RegisterCommandHandler(
105 devtools::Page::enable::kName,
106 base::Bind(
107 &RendererOverridesHandler::PageEnable, base::Unretained(this)));
108 RegisterCommandHandler(
109 devtools::Page::disable::kName,
110 base::Bind(
111 &RendererOverridesHandler::PageDisable, base::Unretained(this)));
112 RegisterCommandHandler(
113 devtools::Page::handleJavaScriptDialog::kName,
114 base::Bind(
115 &RendererOverridesHandler::PageHandleJavaScriptDialog,
116 base::Unretained(this)));
117 RegisterCommandHandler(
118 devtools::Page::navigate::kName,
119 base::Bind(
120 &RendererOverridesHandler::PageNavigate,
121 base::Unretained(this)));
122 RegisterCommandHandler(
123 devtools::Page::reload::kName,
124 base::Bind(
125 &RendererOverridesHandler::PageReload,
126 base::Unretained(this)));
127 RegisterCommandHandler(
128 devtools::Page::getNavigationHistory::kName,
129 base::Bind(
130 &RendererOverridesHandler::PageGetNavigationHistory,
131 base::Unretained(this)));
132 RegisterCommandHandler(
133 devtools::Page::navigateToHistoryEntry::kName,
134 base::Bind(
135 &RendererOverridesHandler::PageNavigateToHistoryEntry,
136 base::Unretained(this)));
137 RegisterCommandHandler(
138 devtools::Page::setTouchEmulationEnabled::kName,
139 base::Bind(
140 &RendererOverridesHandler::PageSetTouchEmulationEnabled,
141 base::Unretained(this)));
142 RegisterCommandHandler(
143 devtools::Page::canEmulate::kName,
144 base::Bind(
145 &RendererOverridesHandler::PageCanEmulate,
146 base::Unretained(this)));
147 RegisterCommandHandler(
148 devtools::Page::canScreencast::kName,
149 base::Bind(
150 &RendererOverridesHandler::PageCanScreencast,
151 base::Unretained(this)));
152 RegisterCommandHandler(
153 devtools::Page::startScreencast::kName,
154 base::Bind(
155 &RendererOverridesHandler::PageStartScreencast,
156 base::Unretained(this)));
157 RegisterCommandHandler(
158 devtools::Page::stopScreencast::kName,
159 base::Bind(
160 &RendererOverridesHandler::PageStopScreencast,
161 base::Unretained(this)));
162 RegisterCommandHandler(
163 devtools::Page::queryUsageAndQuota::kName,
164 base::Bind(
165 &RendererOverridesHandler::PageQueryUsageAndQuota,
166 base::Unretained(this)));
167 RegisterCommandHandler(
168 devtools::Page::setColorPickerEnabled::kName,
169 base::Bind(
170 &RendererOverridesHandler::PageSetColorPickerEnabled,
171 base::Unretained(this)));
172 mouse_event_callback_ = base::Bind(
173 &RendererOverridesHandler::HandleMouseEvent,
174 base::Unretained(this));
177 RendererOverridesHandler::~RendererOverridesHandler() {}
179 void RendererOverridesHandler::OnClientDetached() {
180 touch_emulation_enabled_ = false;
181 screencast_command_ = NULL;
182 UpdateTouchEventEmulationState();
183 SetColorPickerEnabled(false);
186 void RendererOverridesHandler::OnSwapCompositorFrame(
187 const cc::CompositorFrameMetadata& frame_metadata) {
188 last_compositor_frame_metadata_ = frame_metadata;
189 has_last_compositor_frame_metadata_ = true;
191 if (screencast_command_.get())
192 InnerSwapCompositorFrame();
193 if (color_picker_enabled_)
194 UpdateColorPickerFrame();
197 void RendererOverridesHandler::OnVisibilityChanged(bool visible) {
198 if (!screencast_command_.get())
199 return;
200 NotifyScreencastVisibility(visible);
203 void RendererOverridesHandler::SetRenderViewHost(
204 RenderViewHostImpl* host) {
205 host_ = host;
206 if (!host)
207 return;
208 UpdateTouchEventEmulationState();
209 if (color_picker_enabled_)
210 host->AddMouseEventCallback(mouse_event_callback_);
213 void RendererOverridesHandler::ClearRenderViewHost() {
214 if (host_)
215 host_->RemoveMouseEventCallback(mouse_event_callback_);
216 host_ = NULL;
217 ResetColorPickerFrame();
220 void RendererOverridesHandler::DidAttachInterstitialPage() {
221 if (page_domain_enabled_)
222 SendNotification(devtools::Page::interstitialShown::kName, NULL);
225 void RendererOverridesHandler::DidDetachInterstitialPage() {
226 if (page_domain_enabled_)
227 SendNotification(devtools::Page::interstitialHidden::kName, NULL);
230 void RendererOverridesHandler::InnerSwapCompositorFrame() {
231 if ((base::TimeTicks::Now() - last_frame_time_).InMilliseconds() <
232 kFrameRateThresholdMs) {
233 return;
236 if (!host_ || !host_->GetView())
237 return;
239 last_frame_time_ = base::TimeTicks::Now();
241 RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
242 host_->GetView());
243 // TODO(vkuzkokov): do not use previous frame metadata.
244 cc::CompositorFrameMetadata& metadata = last_compositor_frame_metadata_;
246 gfx::SizeF viewport_size_dip = gfx::ScaleSize(
247 metadata.scrollable_viewport_size, metadata.page_scale_factor);
248 gfx::SizeF screen_size_dip = gfx::ScaleSize(view->GetPhysicalBackingSize(),
249 1 / metadata.device_scale_factor);
251 std::string format;
252 int quality = kDefaultScreenshotQuality;
253 double scale = 1;
254 double max_width = -1;
255 double max_height = -1;
256 base::DictionaryValue* params = screencast_command_->params();
257 if (params) {
258 params->GetString(devtools::Page::startScreencast::kParamFormat,
259 &format);
260 params->GetInteger(devtools::Page::startScreencast::kParamQuality,
261 &quality);
262 params->GetDouble(devtools::Page::startScreencast::kParamMaxWidth,
263 &max_width);
264 params->GetDouble(devtools::Page::startScreencast::kParamMaxHeight,
265 &max_height);
268 blink::WebScreenInfo screen_info;
269 view->GetScreenInfo(&screen_info);
270 double device_scale_factor = screen_info.deviceScaleFactor;
272 if (max_width > 0) {
273 double max_width_dip = max_width / device_scale_factor;
274 scale = std::min(scale, max_width_dip / screen_size_dip.width());
276 if (max_height > 0) {
277 double max_height_dip = max_height / device_scale_factor;
278 scale = std::min(scale, max_height_dip / screen_size_dip.height());
281 if (format.empty())
282 format = kPng;
283 if (quality < 0 || quality > 100)
284 quality = kDefaultScreenshotQuality;
285 if (scale <= 0)
286 scale = 0.1;
288 gfx::Size snapshot_size_dip(gfx::ToRoundedSize(
289 gfx::ScaleSize(viewport_size_dip, scale)));
291 if (snapshot_size_dip.width() > 0 && snapshot_size_dip.height() > 0) {
292 gfx::Rect viewport_bounds_dip(gfx::ToRoundedSize(viewport_size_dip));
293 view->CopyFromCompositingSurface(
294 viewport_bounds_dip, snapshot_size_dip,
295 base::Bind(&RendererOverridesHandler::ScreencastFrameCaptured,
296 weak_factory_.GetWeakPtr(),
297 format, quality, last_compositor_frame_metadata_),
298 kN32_SkColorType);
302 // DOM agent handlers --------------------------------------------------------
304 scoped_refptr<DevToolsProtocol::Response>
305 RendererOverridesHandler::GrantPermissionsForSetFileInputFiles(
306 scoped_refptr<DevToolsProtocol::Command> command) {
307 base::DictionaryValue* params = command->params();
308 base::ListValue* file_list = NULL;
309 const char* param =
310 devtools::DOM::setFileInputFiles::kParamFiles;
311 if (!params || !params->GetList(param, &file_list))
312 return command->InvalidParamResponse(param);
313 if (!host_)
314 return NULL;
316 for (size_t i = 0; i < file_list->GetSize(); ++i) {
317 base::FilePath::StringType file;
318 if (!file_list->GetString(i, &file))
319 return command->InvalidParamResponse(param);
320 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
321 host_->GetProcess()->GetID(), base::FilePath(file));
323 return NULL;
327 // Network agent handlers ----------------------------------------------------
329 scoped_refptr<DevToolsProtocol::Response>
330 RendererOverridesHandler::CanEmulateNetworkConditions(
331 scoped_refptr<DevToolsProtocol::Command> command) {
332 base::DictionaryValue* result = new base::DictionaryValue();
333 result->SetBoolean(devtools::kResult, false);
334 return command->SuccessResponse(result);
337 scoped_refptr<DevToolsProtocol::Response>
338 RendererOverridesHandler::ClearBrowserCache(
339 scoped_refptr<DevToolsProtocol::Command> command) {
340 GetContentClient()->browser()->ClearCache(host_);
341 return command->SuccessResponse(NULL);
344 scoped_refptr<DevToolsProtocol::Response>
345 RendererOverridesHandler::ClearBrowserCookies(
346 scoped_refptr<DevToolsProtocol::Command> command) {
347 GetContentClient()->browser()->ClearCookies(host_);
348 return command->SuccessResponse(NULL);
352 // Page agent handlers -------------------------------------------------------
354 scoped_refptr<DevToolsProtocol::Response>
355 RendererOverridesHandler::PageEnable(
356 scoped_refptr<DevToolsProtocol::Command> command) {
357 page_domain_enabled_ = true;
358 // Fall through to the renderer.
359 return NULL;
362 scoped_refptr<DevToolsProtocol::Response>
363 RendererOverridesHandler::PageDisable(
364 scoped_refptr<DevToolsProtocol::Command> command) {
365 page_domain_enabled_ = false;
366 OnClientDetached();
367 // Fall through to the renderer.
368 return NULL;
371 scoped_refptr<DevToolsProtocol::Response>
372 RendererOverridesHandler::PageHandleJavaScriptDialog(
373 scoped_refptr<DevToolsProtocol::Command> command) {
374 base::DictionaryValue* params = command->params();
375 const char* paramAccept =
376 devtools::Page::handleJavaScriptDialog::kParamAccept;
377 bool accept = false;
378 if (!params || !params->GetBoolean(paramAccept, &accept))
379 return command->InvalidParamResponse(paramAccept);
380 base::string16 prompt_override;
381 base::string16* prompt_override_ptr = &prompt_override;
382 if (!params || !params->GetString(
383 devtools::Page::handleJavaScriptDialog::kParamPromptText,
384 prompt_override_ptr)) {
385 prompt_override_ptr = NULL;
388 if (!host_)
389 return command->InternalErrorResponse("Could not connect to view");
391 WebContents* web_contents = WebContents::FromRenderViewHost(host_);
392 if (web_contents) {
393 JavaScriptDialogManager* manager =
394 web_contents->GetDelegate()->GetJavaScriptDialogManager();
395 if (manager && manager->HandleJavaScriptDialog(
396 web_contents, accept, prompt_override_ptr)) {
397 return command->SuccessResponse(new base::DictionaryValue());
400 return command->InternalErrorResponse("No JavaScript dialog to handle");
403 scoped_refptr<DevToolsProtocol::Response>
404 RendererOverridesHandler::PageNavigate(
405 scoped_refptr<DevToolsProtocol::Command> command) {
406 base::DictionaryValue* params = command->params();
407 std::string url;
408 const char* param = devtools::Page::navigate::kParamUrl;
409 if (!params || !params->GetString(param, &url))
410 return command->InvalidParamResponse(param);
412 GURL gurl(url);
413 if (!gurl.is_valid())
414 return command->InternalErrorResponse("Cannot navigate to invalid URL");
416 if (!host_)
417 return command->InternalErrorResponse("Could not connect to view");
419 WebContents* web_contents = WebContents::FromRenderViewHost(host_);
420 if (web_contents) {
421 web_contents->GetController()
422 .LoadURL(gurl, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
423 // Fall through into the renderer.
424 return NULL;
427 return command->InternalErrorResponse("No WebContents to navigate");
430 scoped_refptr<DevToolsProtocol::Response>
431 RendererOverridesHandler::PageReload(
432 scoped_refptr<DevToolsProtocol::Command> command) {
433 if (!host_)
434 return command->InternalErrorResponse("Could not connect to view");
436 WebContents* web_contents = WebContents::FromRenderViewHost(host_);
437 if (web_contents) {
438 // Override only if it is crashed.
439 if (!web_contents->IsCrashed())
440 return NULL;
442 web_contents->GetController().Reload(false);
443 return command->SuccessResponse(NULL);
445 return command->InternalErrorResponse("No WebContents to reload");
448 scoped_refptr<DevToolsProtocol::Response>
449 RendererOverridesHandler::PageGetNavigationHistory(
450 scoped_refptr<DevToolsProtocol::Command> command) {
451 if (!host_)
452 return command->InternalErrorResponse("Could not connect to view");
453 WebContents* web_contents = WebContents::FromRenderViewHost(host_);
454 if (web_contents) {
455 base::DictionaryValue* result = new base::DictionaryValue();
456 NavigationController& controller = web_contents->GetController();
457 result->SetInteger(
458 devtools::Page::getNavigationHistory::kResponseCurrentIndex,
459 controller.GetCurrentEntryIndex());
460 base::ListValue* entries = new base::ListValue();
461 for (int i = 0; i != controller.GetEntryCount(); ++i) {
462 const NavigationEntry* entry = controller.GetEntryAtIndex(i);
463 base::DictionaryValue* entry_value = new base::DictionaryValue();
464 entry_value->SetInteger(
465 devtools::Page::NavigationEntry::kParamId,
466 entry->GetUniqueID());
467 entry_value->SetString(
468 devtools::Page::NavigationEntry::kParamUrl,
469 entry->GetURL().spec());
470 entry_value->SetString(
471 devtools::Page::NavigationEntry::kParamTitle,
472 entry->GetTitle());
473 entries->Append(entry_value);
475 result->Set(
476 devtools::Page::getNavigationHistory::kResponseEntries,
477 entries);
478 return command->SuccessResponse(result);
480 return command->InternalErrorResponse("No WebContents to navigate");
483 scoped_refptr<DevToolsProtocol::Response>
484 RendererOverridesHandler::PageNavigateToHistoryEntry(
485 scoped_refptr<DevToolsProtocol::Command> command) {
486 base::DictionaryValue* params = command->params();
487 const char* param = devtools::Page::navigateToHistoryEntry::kParamEntryId;
488 int entry_id = 0;
489 if (!params || !params->GetInteger(param, &entry_id)) {
490 return command->InvalidParamResponse(param);
493 if (!host_)
494 return command->InternalErrorResponse("Could not connect to view");
496 WebContents* web_contents = WebContents::FromRenderViewHost(host_);
497 if (web_contents) {
498 NavigationController& controller = web_contents->GetController();
499 for (int i = 0; i != controller.GetEntryCount(); ++i) {
500 if (controller.GetEntryAtIndex(i)->GetUniqueID() == entry_id) {
501 controller.GoToIndex(i);
502 return command->SuccessResponse(new base::DictionaryValue());
505 return command->InvalidParamResponse(param);
507 return command->InternalErrorResponse("No WebContents to navigate");
510 scoped_refptr<DevToolsProtocol::Response>
511 RendererOverridesHandler::PageSetTouchEmulationEnabled(
512 scoped_refptr<DevToolsProtocol::Command> command) {
513 base::DictionaryValue* params = command->params();
514 bool enabled = false;
515 if (!params || !params->GetBoolean(
516 devtools::Page::setTouchEmulationEnabled::kParamEnabled,
517 &enabled)) {
518 // Pass to renderer.
519 return NULL;
522 touch_emulation_enabled_ = enabled;
523 UpdateTouchEventEmulationState();
525 // Pass to renderer.
526 return NULL;
529 scoped_refptr<DevToolsProtocol::Response>
530 RendererOverridesHandler::PageCanEmulate(
531 scoped_refptr<DevToolsProtocol::Command> command) {
532 base::DictionaryValue* result = new base::DictionaryValue();
533 #if defined(OS_ANDROID)
534 result->SetBoolean(devtools::kResult, false);
535 #else
536 if (WebContents* web_contents = WebContents::FromRenderViewHost(host_)) {
537 result->SetBoolean(
538 devtools::kResult,
539 !web_contents->GetVisibleURL().SchemeIs(kChromeDevToolsScheme));
540 } else {
541 result->SetBoolean(devtools::kResult, true);
543 #endif // defined(OS_ANDROID)
544 return command->SuccessResponse(result);
547 scoped_refptr<DevToolsProtocol::Response>
548 RendererOverridesHandler::PageCanScreencast(
549 scoped_refptr<DevToolsProtocol::Command> command) {
550 base::DictionaryValue* result = new base::DictionaryValue();
551 #if defined(OS_ANDROID)
552 result->SetBoolean(devtools::kResult, true);
553 #else
554 result->SetBoolean(devtools::kResult, false);
555 #endif // defined(OS_ANDROID)
556 return command->SuccessResponse(result);
559 scoped_refptr<DevToolsProtocol::Response>
560 RendererOverridesHandler::PageStartScreencast(
561 scoped_refptr<DevToolsProtocol::Command> command) {
562 screencast_command_ = command;
563 UpdateTouchEventEmulationState();
564 if (!host_)
565 return command->InternalErrorResponse("Could not connect to view");
566 bool visible = !host_->is_hidden();
567 NotifyScreencastVisibility(visible);
568 if (visible) {
569 if (has_last_compositor_frame_metadata_)
570 InnerSwapCompositorFrame();
571 else
572 host_->Send(new ViewMsg_ForceRedraw(host_->GetRoutingID(), 0));
574 // Pass through to the renderer.
575 return NULL;
578 scoped_refptr<DevToolsProtocol::Response>
579 RendererOverridesHandler::PageStopScreencast(
580 scoped_refptr<DevToolsProtocol::Command> command) {
581 last_frame_time_ = base::TimeTicks();
582 screencast_command_ = NULL;
583 UpdateTouchEventEmulationState();
584 // Pass through to the renderer.
585 return NULL;
588 void RendererOverridesHandler::ScreencastFrameCaptured(
589 const std::string& format,
590 int quality,
591 const cc::CompositorFrameMetadata& metadata,
592 bool success,
593 const SkBitmap& bitmap) {
594 if (!success) {
595 if (capture_retry_count_) {
596 --capture_retry_count_;
597 base::MessageLoop::current()->PostDelayedTask(
598 FROM_HERE,
599 base::Bind(&RendererOverridesHandler::InnerSwapCompositorFrame,
600 weak_factory_.GetWeakPtr()),
601 base::TimeDelta::FromMilliseconds(kFrameRateThresholdMs));
603 return;
606 std::vector<unsigned char> data;
607 SkAutoLockPixels lock_image(bitmap);
608 bool encoded;
609 if (format == kPng) {
610 encoded = gfx::PNGCodec::Encode(
611 reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
612 gfx::PNGCodec::FORMAT_SkBitmap,
613 gfx::Size(bitmap.width(), bitmap.height()),
614 bitmap.width() * bitmap.bytesPerPixel(),
615 false, std::vector<gfx::PNGCodec::Comment>(), &data);
616 } else if (format == kJpeg) {
617 encoded = gfx::JPEGCodec::Encode(
618 reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
619 gfx::JPEGCodec::FORMAT_SkBitmap,
620 bitmap.width(),
621 bitmap.height(),
622 bitmap.width() * bitmap.bytesPerPixel(),
623 quality, &data);
624 } else {
625 encoded = false;
628 if (!encoded)
629 return;
631 std::string base_64_data;
632 base::Base64Encode(
633 base::StringPiece(reinterpret_cast<char*>(&data[0]), data.size()),
634 &base_64_data);
636 base::DictionaryValue* response = new base::DictionaryValue();
637 response->SetString(devtools::Page::screencastFrame::kParamData,
638 base_64_data);
640 // Consider metadata empty in case it has no device scale factor.
641 if (metadata.device_scale_factor != 0) {
642 base::DictionaryValue* response_metadata = new base::DictionaryValue();
644 RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
645 host_->GetView());
646 if (!view)
647 return;
649 gfx::SizeF viewport_size_dip = gfx::ScaleSize(
650 metadata.scrollable_viewport_size, metadata.page_scale_factor);
651 gfx::SizeF screen_size_dip = gfx::ScaleSize(
652 view->GetPhysicalBackingSize(), 1 / metadata.device_scale_factor);
654 response_metadata->SetDouble(
655 devtools::Page::ScreencastFrameMetadata::kParamDeviceScaleFactor,
656 metadata.device_scale_factor);
657 response_metadata->SetDouble(
658 devtools::Page::ScreencastFrameMetadata::kParamPageScaleFactor,
659 metadata.page_scale_factor);
660 response_metadata->SetDouble(
661 devtools::Page::ScreencastFrameMetadata::kParamPageScaleFactorMin,
662 metadata.min_page_scale_factor);
663 response_metadata->SetDouble(
664 devtools::Page::ScreencastFrameMetadata::kParamPageScaleFactorMax,
665 metadata.max_page_scale_factor);
666 response_metadata->SetDouble(
667 devtools::Page::ScreencastFrameMetadata::kParamOffsetTop,
668 metadata.location_bar_content_translation.y());
669 response_metadata->SetDouble(
670 devtools::Page::ScreencastFrameMetadata::kParamOffsetBottom,
671 screen_size_dip.height() -
672 metadata.location_bar_content_translation.y() -
673 viewport_size_dip.height());
675 base::DictionaryValue* viewport = new base::DictionaryValue();
676 viewport->SetDouble(devtools::DOM::Rect::kParamX,
677 metadata.root_scroll_offset.x());
678 viewport->SetDouble(devtools::DOM::Rect::kParamY,
679 metadata.root_scroll_offset.y());
680 viewport->SetDouble(devtools::DOM::Rect::kParamWidth,
681 metadata.scrollable_viewport_size.width());
682 viewport->SetDouble(devtools::DOM::Rect::kParamHeight,
683 metadata.scrollable_viewport_size.height());
684 response_metadata->Set(
685 devtools::Page::ScreencastFrameMetadata::kParamViewport, viewport);
687 response_metadata->SetDouble(
688 devtools::Page::ScreencastFrameMetadata::kParamDeviceWidth,
689 screen_size_dip.width());
690 response_metadata->SetDouble(
691 devtools::Page::ScreencastFrameMetadata::kParamDeviceHeight,
692 screen_size_dip.height());
693 response_metadata->SetDouble(
694 devtools::Page::ScreencastFrameMetadata::kParamScrollOffsetX,
695 metadata.root_scroll_offset.x());
696 response_metadata->SetDouble(
697 devtools::Page::ScreencastFrameMetadata::kParamScrollOffsetY,
698 metadata.root_scroll_offset.y());
700 response->Set(devtools::Page::screencastFrame::kParamMetadata,
701 response_metadata);
704 SendNotification(devtools::Page::screencastFrame::kName, response);
707 // Quota and Usage ------------------------------------------
709 namespace {
711 typedef base::Callback<void(scoped_ptr<base::DictionaryValue>)>
712 ResponseCallback;
714 void QueryUsageAndQuotaCompletedOnIOThread(
715 scoped_ptr<base::DictionaryValue> quota,
716 scoped_ptr<base::DictionaryValue> usage,
717 ResponseCallback callback) {
719 scoped_ptr<base::DictionaryValue> response_data(new base::DictionaryValue);
720 response_data->Set(devtools::Page::queryUsageAndQuota::kResponseQuota,
721 quota.release());
722 response_data->Set(devtools::Page::queryUsageAndQuota::kResponseUsage,
723 usage.release());
725 BrowserThread::PostTask(
726 BrowserThread::UI, FROM_HERE,
727 base::Bind(callback, base::Passed(&response_data)));
730 void DidGetHostUsage(
731 base::ListValue* list,
732 const std::string& client_id,
733 const base::Closure& barrier,
734 int64 value) {
735 base::DictionaryValue* usage_item = new base::DictionaryValue;
736 usage_item->SetString(devtools::Page::UsageItem::kParamId, client_id);
737 usage_item->SetDouble(devtools::Page::UsageItem::kParamValue, value);
738 list->Append(usage_item);
739 barrier.Run();
742 void DidGetQuotaValue(base::DictionaryValue* dictionary,
743 const std::string& item_name,
744 const base::Closure& barrier,
745 storage::QuotaStatusCode status,
746 int64 value) {
747 if (status == storage::kQuotaStatusOk)
748 dictionary->SetDouble(item_name, value);
749 barrier.Run();
752 void DidGetUsageAndQuotaForWebApps(base::DictionaryValue* quota,
753 const std::string& item_name,
754 const base::Closure& barrier,
755 storage::QuotaStatusCode status,
756 int64 used_bytes,
757 int64 quota_in_bytes) {
758 if (status == storage::kQuotaStatusOk)
759 quota->SetDouble(item_name, quota_in_bytes);
760 barrier.Run();
763 std::string GetStorageTypeName(storage::StorageType type) {
764 switch (type) {
765 case storage::kStorageTypeTemporary:
766 return devtools::Page::Usage::kParamTemporary;
767 case storage::kStorageTypePersistent:
768 return devtools::Page::Usage::kParamPersistent;
769 case storage::kStorageTypeSyncable:
770 return devtools::Page::Usage::kParamSyncable;
771 case storage::kStorageTypeQuotaNotManaged:
772 case storage::kStorageTypeUnknown:
773 NOTREACHED();
775 return "";
778 std::string GetQuotaClientName(storage::QuotaClient::ID id) {
779 switch (id) {
780 case storage::QuotaClient::kFileSystem:
781 return devtools::Page::UsageItem::Id::kEnumFilesystem;
782 case storage::QuotaClient::kDatabase:
783 return devtools::Page::UsageItem::Id::kEnumDatabase;
784 case storage::QuotaClient::kAppcache:
785 return devtools::Page::UsageItem::Id::kEnumAppcache;
786 case storage::QuotaClient::kIndexedDatabase:
787 return devtools::Page::UsageItem::Id::kEnumIndexeddatabase;
788 default:
789 NOTREACHED();
791 return "";
794 void QueryUsageAndQuotaOnIOThread(
795 scoped_refptr<storage::QuotaManager> quota_manager,
796 const GURL& security_origin,
797 const ResponseCallback& callback) {
798 scoped_ptr<base::DictionaryValue> quota(new base::DictionaryValue);
799 scoped_ptr<base::DictionaryValue> usage(new base::DictionaryValue);
801 static storage::QuotaClient::ID kQuotaClients[] = {
802 storage::QuotaClient::kFileSystem, storage::QuotaClient::kDatabase,
803 storage::QuotaClient::kAppcache, storage::QuotaClient::kIndexedDatabase};
805 static const size_t kStorageTypeCount = storage::kStorageTypeUnknown;
806 std::map<storage::StorageType, base::ListValue*> storage_type_lists;
808 for (size_t i = 0; i != kStorageTypeCount; i++) {
809 const storage::StorageType type = static_cast<storage::StorageType>(i);
810 if (type == storage::kStorageTypeQuotaNotManaged)
811 continue;
812 storage_type_lists[type] = new base::ListValue;
813 usage->Set(GetStorageTypeName(type), storage_type_lists[type]);
816 const int kExpectedResults =
817 2 + arraysize(kQuotaClients) * storage_type_lists.size();
818 base::DictionaryValue* quota_raw_ptr = quota.get();
820 // Takes ownership on usage and quota.
821 base::Closure barrier = BarrierClosure(
822 kExpectedResults,
823 base::Bind(&QueryUsageAndQuotaCompletedOnIOThread,
824 base::Passed(&quota),
825 base::Passed(&usage),
826 callback));
827 std::string host = net::GetHostOrSpecFromURL(security_origin);
829 quota_manager->GetUsageAndQuotaForWebApps(
830 security_origin,
831 storage::kStorageTypeTemporary,
832 base::Bind(&DidGetUsageAndQuotaForWebApps,
833 quota_raw_ptr,
834 std::string(devtools::Page::Quota::kParamTemporary),
835 barrier));
837 quota_manager->GetPersistentHostQuota(
838 host,
839 base::Bind(&DidGetQuotaValue, quota_raw_ptr,
840 std::string(devtools::Page::Quota::kParamPersistent),
841 barrier));
843 for (size_t i = 0; i != arraysize(kQuotaClients); i++) {
844 std::map<storage::StorageType, base::ListValue*>::const_iterator iter;
845 for (iter = storage_type_lists.begin();
846 iter != storage_type_lists.end(); ++iter) {
847 const storage::StorageType type = (*iter).first;
848 if (!quota_manager->IsTrackingHostUsage(type, kQuotaClients[i])) {
849 barrier.Run();
850 continue;
852 quota_manager->GetHostUsage(
853 host, type, kQuotaClients[i],
854 base::Bind(&DidGetHostUsage, (*iter).second,
855 GetQuotaClientName(kQuotaClients[i]),
856 barrier));
861 } // namespace
863 scoped_refptr<DevToolsProtocol::Response>
864 RendererOverridesHandler::PageQueryUsageAndQuota(
865 scoped_refptr<DevToolsProtocol::Command> command) {
866 base::DictionaryValue* params = command->params();
867 std::string security_origin;
868 if (!params || !params->GetString(
869 devtools::Page::queryUsageAndQuota::kParamSecurityOrigin,
870 &security_origin)) {
871 return command->InvalidParamResponse(
872 devtools::Page::queryUsageAndQuota::kParamSecurityOrigin);
875 ResponseCallback callback = base::Bind(
876 &RendererOverridesHandler::PageQueryUsageAndQuotaCompleted,
877 weak_factory_.GetWeakPtr(),
878 command);
880 if (!host_)
881 return command->InternalErrorResponse("Could not connect to view");
883 scoped_refptr<storage::QuotaManager> quota_manager =
884 host_->GetProcess()->GetStoragePartition()->GetQuotaManager();
886 BrowserThread::PostTask(
887 BrowserThread::IO, FROM_HERE,
888 base::Bind(
889 &QueryUsageAndQuotaOnIOThread,
890 quota_manager,
891 GURL(security_origin),
892 callback));
894 return command->AsyncResponsePromise();
897 void RendererOverridesHandler::PageQueryUsageAndQuotaCompleted(
898 scoped_refptr<DevToolsProtocol::Command> command,
899 scoped_ptr<base::DictionaryValue> response_data) {
900 SendAsyncResponse(command->SuccessResponse(response_data.release()));
903 void RendererOverridesHandler::NotifyScreencastVisibility(bool visible) {
904 if (visible)
905 capture_retry_count_ = kCaptureRetryLimit;
906 base::DictionaryValue* params = new base::DictionaryValue();
907 params->SetBoolean(
908 devtools::Page::screencastVisibilityChanged::kParamVisible, visible);
909 SendNotification(
910 devtools::Page::screencastVisibilityChanged::kName, params);
913 scoped_refptr<DevToolsProtocol::Response>
914 RendererOverridesHandler::PageSetColorPickerEnabled(
915 scoped_refptr<DevToolsProtocol::Command> command) {
916 base::DictionaryValue* params = command->params();
917 bool color_picker_enabled = false;
918 if (!params || !params->GetBoolean(
919 devtools::Page::setColorPickerEnabled::kParamEnabled,
920 &color_picker_enabled)) {
921 return command->InvalidParamResponse(
922 devtools::Page::setColorPickerEnabled::kParamEnabled);
925 SetColorPickerEnabled(color_picker_enabled);
926 return command->SuccessResponse(NULL);
929 void RendererOverridesHandler::SetColorPickerEnabled(bool enabled) {
930 if (color_picker_enabled_ == enabled)
931 return;
933 color_picker_enabled_ = enabled;
935 if (!host_)
936 return;
938 if (enabled) {
939 host_->AddMouseEventCallback(mouse_event_callback_);
940 UpdateColorPickerFrame();
941 } else {
942 host_->RemoveMouseEventCallback(mouse_event_callback_);
943 ResetColorPickerFrame();
945 WebCursor pointer_cursor;
946 WebCursor::CursorInfo cursor_info;
947 cursor_info.type = blink::WebCursorInfo::TypePointer;
948 pointer_cursor.InitFromCursorInfo(cursor_info);
949 host_->SetCursor(pointer_cursor);
953 void RendererOverridesHandler::UpdateColorPickerFrame() {
954 if (!host_)
955 return;
956 RenderWidgetHostViewBase* view =
957 static_cast<RenderWidgetHostViewBase*>(host_->GetView());
958 if (!view)
959 return;
961 gfx::Size size = view->GetViewBounds().size();
962 view->CopyFromCompositingSurface(
963 gfx::Rect(size), size,
964 base::Bind(&RendererOverridesHandler::ColorPickerFrameUpdated,
965 weak_factory_.GetWeakPtr()),
966 kN32_SkColorType);
969 void RendererOverridesHandler::ResetColorPickerFrame() {
970 color_picker_frame_.reset();
971 last_cursor_x_ = -1;
972 last_cursor_y_ = -1;
975 void RendererOverridesHandler::ColorPickerFrameUpdated(
976 bool succeeded,
977 const SkBitmap& bitmap) {
978 if (!color_picker_enabled_)
979 return;
981 if (succeeded) {
982 color_picker_frame_ = bitmap;
983 UpdateColorPickerCursor();
987 bool RendererOverridesHandler::HandleMouseEvent(
988 const blink::WebMouseEvent& event) {
989 last_cursor_x_ = event.x;
990 last_cursor_y_ = event.y;
991 if (color_picker_frame_.drawsNothing())
992 return true;
994 if (event.button == blink::WebMouseEvent::ButtonLeft &&
995 event.type == blink::WebInputEvent::MouseDown) {
996 if (last_cursor_x_ < 0 || last_cursor_x_ >= color_picker_frame_.width() ||
997 last_cursor_y_ < 0 || last_cursor_y_ >= color_picker_frame_.height()) {
998 return true;
1001 SkAutoLockPixels lock_image(color_picker_frame_);
1002 SkColor color = color_picker_frame_.getColor(last_cursor_x_,
1003 last_cursor_y_);
1004 base::DictionaryValue* color_dict = new base::DictionaryValue();
1005 color_dict->SetInteger("r", SkColorGetR(color));
1006 color_dict->SetInteger("g", SkColorGetG(color));
1007 color_dict->SetInteger("b", SkColorGetB(color));
1008 color_dict->SetInteger("a", SkColorGetA(color));
1009 base::DictionaryValue* response = new base::DictionaryValue();
1010 response->Set(devtools::Page::colorPicked::kParamColor, color_dict);
1011 SendNotification(devtools::Page::colorPicked::kName, response);
1013 UpdateColorPickerCursor();
1014 return true;
1017 void RendererOverridesHandler::UpdateColorPickerCursor() {
1018 if (!host_ || color_picker_frame_.drawsNothing())
1019 return;
1021 if (last_cursor_x_ < 0 || last_cursor_x_ >= color_picker_frame_.width() ||
1022 last_cursor_y_ < 0 || last_cursor_y_ >= color_picker_frame_.height()) {
1023 return;
1026 RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
1027 host_->GetView());
1028 if (!view)
1029 return;
1031 // Due to platform limitations, we are using two different cursors
1032 // depending on the platform. Mac and Win have large cursors with two circles
1033 // for original spot and its magnified projection; Linux gets smaller (64 px)
1034 // magnified projection only with centered hotspot.
1035 // Mac Retina requires cursor to be > 120px in order to render smoothly.
1037 #if defined(OS_LINUX)
1038 const float kCursorSize = 63;
1039 const float kDiameter = 63;
1040 const float kHotspotOffset = 32;
1041 const float kHotspotRadius = 0;
1042 const float kPixelSize = 9;
1043 #else
1044 const float kCursorSize = 150;
1045 const float kDiameter = 110;
1046 const float kHotspotOffset = 25;
1047 const float kHotspotRadius = 5;
1048 const float kPixelSize = 10;
1049 #endif
1051 blink::WebScreenInfo screen_info;
1052 view->GetScreenInfo(&screen_info);
1053 double device_scale_factor = screen_info.deviceScaleFactor;
1055 skia::RefPtr<SkCanvas> canvas = skia::AdoptRef(SkCanvas::NewRasterN32(
1056 kCursorSize * device_scale_factor,
1057 kCursorSize * device_scale_factor));
1058 canvas->scale(device_scale_factor, device_scale_factor);
1059 canvas->translate(0.5f, 0.5f);
1061 SkPaint paint;
1063 // Paint original spot with cross.
1064 if (kHotspotRadius) {
1065 paint.setStrokeWidth(1);
1066 paint.setAntiAlias(false);
1067 paint.setColor(SK_ColorDKGRAY);
1068 paint.setStyle(SkPaint::kStroke_Style);
1070 canvas->drawLine(kHotspotOffset, kHotspotOffset - 2 * kHotspotRadius,
1071 kHotspotOffset, kHotspotOffset - kHotspotRadius,
1072 paint);
1073 canvas->drawLine(kHotspotOffset, kHotspotOffset + kHotspotRadius,
1074 kHotspotOffset, kHotspotOffset + 2 * kHotspotRadius,
1075 paint);
1076 canvas->drawLine(kHotspotOffset - 2 * kHotspotRadius, kHotspotOffset,
1077 kHotspotOffset - kHotspotRadius, kHotspotOffset,
1078 paint);
1079 canvas->drawLine(kHotspotOffset + kHotspotRadius, kHotspotOffset,
1080 kHotspotOffset + 2 * kHotspotRadius, kHotspotOffset,
1081 paint);
1083 paint.setStrokeWidth(2);
1084 paint.setAntiAlias(true);
1085 canvas->drawCircle(kHotspotOffset, kHotspotOffset, kHotspotRadius, paint);
1088 // Clip circle for magnified projection.
1089 float padding = (kCursorSize - kDiameter) / 2;
1090 SkPath clip_path;
1091 clip_path.addOval(SkRect::MakeXYWH(padding, padding, kDiameter, kDiameter));
1092 clip_path.close();
1093 canvas->clipPath(clip_path, SkRegion::kIntersect_Op, true);
1095 // Project pixels.
1096 int pixel_count = kDiameter / kPixelSize;
1097 SkRect src_rect = SkRect::MakeXYWH(last_cursor_x_ - pixel_count / 2,
1098 last_cursor_y_ - pixel_count / 2,
1099 pixel_count, pixel_count);
1100 SkRect dst_rect = SkRect::MakeXYWH(padding, padding, kDiameter, kDiameter);
1101 canvas->drawBitmapRectToRect(color_picker_frame_, &src_rect, dst_rect);
1103 // Paint grid.
1104 paint.setStrokeWidth(1);
1105 paint.setAntiAlias(false);
1106 paint.setColor(SK_ColorGRAY);
1107 for (int i = 0; i < pixel_count; ++i) {
1108 canvas->drawLine(padding + i * kPixelSize, padding,
1109 padding + i * kPixelSize, kCursorSize - padding, paint);
1110 canvas->drawLine(padding, padding + i * kPixelSize,
1111 kCursorSize - padding, padding + i * kPixelSize, paint);
1114 // Paint central pixel in red.
1115 SkRect pixel = SkRect::MakeXYWH((kCursorSize - kPixelSize) / 2,
1116 (kCursorSize - kPixelSize) / 2,
1117 kPixelSize, kPixelSize);
1118 paint.setColor(SK_ColorRED);
1119 paint.setStyle(SkPaint::kStroke_Style);
1120 canvas->drawRect(pixel, paint);
1122 // Paint outline.
1123 paint.setStrokeWidth(2);
1124 paint.setColor(SK_ColorDKGRAY);
1125 paint.setAntiAlias(true);
1126 canvas->drawCircle(kCursorSize / 2, kCursorSize / 2, kDiameter / 2, paint);
1128 SkBitmap result;
1129 result.allocN32Pixels(kCursorSize * device_scale_factor,
1130 kCursorSize * device_scale_factor);
1131 canvas->readPixels(&result, 0, 0);
1133 WebCursor cursor;
1134 WebCursor::CursorInfo cursor_info;
1135 cursor_info.type = blink::WebCursorInfo::TypeCustom;
1136 cursor_info.image_scale_factor = device_scale_factor;
1137 cursor_info.custom_image = result;
1138 cursor_info.hotspot =
1139 gfx::Point(kHotspotOffset * device_scale_factor,
1140 kHotspotOffset * device_scale_factor);
1141 #if defined(OS_WIN)
1142 cursor_info.external_handle = 0;
1143 #endif
1145 cursor.InitFromCursorInfo(cursor_info);
1146 DCHECK(host_);
1147 host_->SetCursor(cursor);
1150 void RendererOverridesHandler::UpdateTouchEventEmulationState() {
1151 if (!host_)
1152 return;
1153 bool enabled = touch_emulation_enabled_ || screencast_command_.get();
1154 host_->SetTouchEventEmulationEnabled(enabled);
1155 WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
1156 WebContents::FromRenderViewHost(host_));
1157 if (web_contents)
1158 web_contents->SetForceDisableOverscrollContent(enabled);
1161 } // namespace content