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"
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"
59 using blink::WebGestureEvent
;
60 using blink::WebInputEvent
;
61 using blink::WebMouseEvent
;
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;
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),
84 RegisterCommandHandler(
85 devtools::DOM::setFileInputFiles::kName
,
87 &RendererOverridesHandler::GrantPermissionsForSetFileInputFiles
,
88 base::Unretained(this)));
89 RegisterCommandHandler(
90 devtools::Network::canEmulateNetworkConditions::kName
,
92 &RendererOverridesHandler::CanEmulateNetworkConditions
,
93 base::Unretained(this)));
94 RegisterCommandHandler(
95 devtools::Network::clearBrowserCache::kName
,
97 &RendererOverridesHandler::ClearBrowserCache
,
98 base::Unretained(this)));
99 RegisterCommandHandler(
100 devtools::Network::clearBrowserCookies::kName
,
102 &RendererOverridesHandler::ClearBrowserCookies
,
103 base::Unretained(this)));
104 RegisterCommandHandler(
105 devtools::Page::enable::kName
,
107 &RendererOverridesHandler::PageEnable
, base::Unretained(this)));
108 RegisterCommandHandler(
109 devtools::Page::disable::kName
,
111 &RendererOverridesHandler::PageDisable
, base::Unretained(this)));
112 RegisterCommandHandler(
113 devtools::Page::handleJavaScriptDialog::kName
,
115 &RendererOverridesHandler::PageHandleJavaScriptDialog
,
116 base::Unretained(this)));
117 RegisterCommandHandler(
118 devtools::Page::navigate::kName
,
120 &RendererOverridesHandler::PageNavigate
,
121 base::Unretained(this)));
122 RegisterCommandHandler(
123 devtools::Page::reload::kName
,
125 &RendererOverridesHandler::PageReload
,
126 base::Unretained(this)));
127 RegisterCommandHandler(
128 devtools::Page::getNavigationHistory::kName
,
130 &RendererOverridesHandler::PageGetNavigationHistory
,
131 base::Unretained(this)));
132 RegisterCommandHandler(
133 devtools::Page::navigateToHistoryEntry::kName
,
135 &RendererOverridesHandler::PageNavigateToHistoryEntry
,
136 base::Unretained(this)));
137 RegisterCommandHandler(
138 devtools::Page::setTouchEmulationEnabled::kName
,
140 &RendererOverridesHandler::PageSetTouchEmulationEnabled
,
141 base::Unretained(this)));
142 RegisterCommandHandler(
143 devtools::Page::canEmulate::kName
,
145 &RendererOverridesHandler::PageCanEmulate
,
146 base::Unretained(this)));
147 RegisterCommandHandler(
148 devtools::Page::canScreencast::kName
,
150 &RendererOverridesHandler::PageCanScreencast
,
151 base::Unretained(this)));
152 RegisterCommandHandler(
153 devtools::Page::startScreencast::kName
,
155 &RendererOverridesHandler::PageStartScreencast
,
156 base::Unretained(this)));
157 RegisterCommandHandler(
158 devtools::Page::stopScreencast::kName
,
160 &RendererOverridesHandler::PageStopScreencast
,
161 base::Unretained(this)));
162 RegisterCommandHandler(
163 devtools::Page::queryUsageAndQuota::kName
,
165 &RendererOverridesHandler::PageQueryUsageAndQuota
,
166 base::Unretained(this)));
167 RegisterCommandHandler(
168 devtools::Page::setColorPickerEnabled::kName
,
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())
200 NotifyScreencastVisibility(visible
);
203 void RendererOverridesHandler::SetRenderViewHost(
204 RenderViewHostImpl
* host
) {
208 UpdateTouchEventEmulationState();
209 if (color_picker_enabled_
)
210 host
->AddMouseEventCallback(mouse_event_callback_
);
213 void RendererOverridesHandler::ClearRenderViewHost() {
215 host_
->RemoveMouseEventCallback(mouse_event_callback_
);
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
) {
236 if (!host_
|| !host_
->GetView())
239 last_frame_time_
= base::TimeTicks::Now();
241 RenderWidgetHostViewBase
* view
= static_cast<RenderWidgetHostViewBase
*>(
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
);
252 int quality
= kDefaultScreenshotQuality
;
254 double max_width
= -1;
255 double max_height
= -1;
256 base::DictionaryValue
* params
= screencast_command_
->params();
258 params
->GetString(devtools::Page::startScreencast::kParamFormat
,
260 params
->GetInteger(devtools::Page::startScreencast::kParamQuality
,
262 params
->GetDouble(devtools::Page::startScreencast::kParamMaxWidth
,
264 params
->GetDouble(devtools::Page::startScreencast::kParamMaxHeight
,
268 blink::WebScreenInfo screen_info
;
269 view
->GetScreenInfo(&screen_info
);
270 double device_scale_factor
= screen_info
.deviceScaleFactor
;
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());
283 if (quality
< 0 || quality
> 100)
284 quality
= kDefaultScreenshotQuality
;
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_
),
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
;
310 devtools::DOM::setFileInputFiles::kParamFiles
;
311 if (!params
|| !params
->GetList(param
, &file_list
))
312 return command
->InvalidParamResponse(param
);
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
));
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.
362 scoped_refptr
<DevToolsProtocol::Response
>
363 RendererOverridesHandler::PageDisable(
364 scoped_refptr
<DevToolsProtocol::Command
> command
) {
365 page_domain_enabled_
= false;
367 // Fall through to the renderer.
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
;
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
;
389 return command
->InternalErrorResponse("Could not connect to view");
391 WebContents
* web_contents
= WebContents::FromRenderViewHost(host_
);
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();
408 const char* param
= devtools::Page::navigate::kParamUrl
;
409 if (!params
|| !params
->GetString(param
, &url
))
410 return command
->InvalidParamResponse(param
);
413 if (!gurl
.is_valid())
414 return command
->InternalErrorResponse("Cannot navigate to invalid URL");
417 return command
->InternalErrorResponse("Could not connect to view");
419 WebContents
* web_contents
= WebContents::FromRenderViewHost(host_
);
421 web_contents
->GetController()
422 .LoadURL(gurl
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
423 // Fall through into the renderer.
427 return command
->InternalErrorResponse("No WebContents to navigate");
430 scoped_refptr
<DevToolsProtocol::Response
>
431 RendererOverridesHandler::PageReload(
432 scoped_refptr
<DevToolsProtocol::Command
> command
) {
434 return command
->InternalErrorResponse("Could not connect to view");
436 WebContents
* web_contents
= WebContents::FromRenderViewHost(host_
);
438 // Override only if it is crashed.
439 if (!web_contents
->IsCrashed())
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
) {
452 return command
->InternalErrorResponse("Could not connect to view");
453 WebContents
* web_contents
= WebContents::FromRenderViewHost(host_
);
455 base::DictionaryValue
* result
= new base::DictionaryValue();
456 NavigationController
& controller
= web_contents
->GetController();
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
,
473 entries
->Append(entry_value
);
476 devtools::Page::getNavigationHistory::kResponseEntries
,
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
;
489 if (!params
|| !params
->GetInteger(param
, &entry_id
)) {
490 return command
->InvalidParamResponse(param
);
494 return command
->InternalErrorResponse("Could not connect to view");
496 WebContents
* web_contents
= WebContents::FromRenderViewHost(host_
);
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
,
522 touch_emulation_enabled_
= enabled
;
523 UpdateTouchEventEmulationState();
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);
536 if (WebContents
* web_contents
= WebContents::FromRenderViewHost(host_
)) {
539 !web_contents
->GetVisibleURL().SchemeIs(kChromeDevToolsScheme
));
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);
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();
565 return command
->InternalErrorResponse("Could not connect to view");
566 bool visible
= !host_
->is_hidden();
567 NotifyScreencastVisibility(visible
);
569 if (has_last_compositor_frame_metadata_
)
570 InnerSwapCompositorFrame();
572 host_
->Send(new ViewMsg_ForceRedraw(host_
->GetRoutingID(), 0));
574 // Pass through to the renderer.
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.
588 void RendererOverridesHandler::ScreencastFrameCaptured(
589 const std::string
& format
,
591 const cc::CompositorFrameMetadata
& metadata
,
593 const SkBitmap
& bitmap
) {
595 if (capture_retry_count_
) {
596 --capture_retry_count_
;
597 base::MessageLoop::current()->PostDelayedTask(
599 base::Bind(&RendererOverridesHandler::InnerSwapCompositorFrame
,
600 weak_factory_
.GetWeakPtr()),
601 base::TimeDelta::FromMilliseconds(kFrameRateThresholdMs
));
606 std::vector
<unsigned char> data
;
607 SkAutoLockPixels
lock_image(bitmap
);
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
,
622 bitmap
.width() * bitmap
.bytesPerPixel(),
631 std::string base_64_data
;
633 base::StringPiece(reinterpret_cast<char*>(&data
[0]), data
.size()),
636 base::DictionaryValue
* response
= new base::DictionaryValue();
637 response
->SetString(devtools::Page::screencastFrame::kParamData
,
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
*>(
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
,
704 SendNotification(devtools::Page::screencastFrame::kName
, response
);
707 // Quota and Usage ------------------------------------------
711 typedef base::Callback
<void(scoped_ptr
<base::DictionaryValue
>)>
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
,
722 response_data
->Set(devtools::Page::queryUsageAndQuota::kResponseUsage
,
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
,
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
);
742 void DidGetQuotaValue(base::DictionaryValue
* dictionary
,
743 const std::string
& item_name
,
744 const base::Closure
& barrier
,
745 storage::QuotaStatusCode status
,
747 if (status
== storage::kQuotaStatusOk
)
748 dictionary
->SetDouble(item_name
, value
);
752 void DidGetUsageAndQuotaForWebApps(base::DictionaryValue
* quota
,
753 const std::string
& item_name
,
754 const base::Closure
& barrier
,
755 storage::QuotaStatusCode status
,
757 int64 quota_in_bytes
) {
758 if (status
== storage::kQuotaStatusOk
)
759 quota
->SetDouble(item_name
, quota_in_bytes
);
763 std::string
GetStorageTypeName(storage::StorageType 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
:
778 std::string
GetQuotaClientName(storage::QuotaClient::ID 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
;
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
)
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(
823 base::Bind(&QueryUsageAndQuotaCompletedOnIOThread
,
824 base::Passed("a
),
825 base::Passed(&usage
),
827 std::string host
= net::GetHostOrSpecFromURL(security_origin
);
829 quota_manager
->GetUsageAndQuotaForWebApps(
831 storage::kStorageTypeTemporary
,
832 base::Bind(&DidGetUsageAndQuotaForWebApps
,
834 std::string(devtools::Page::Quota::kParamTemporary
),
837 quota_manager
->GetPersistentHostQuota(
839 base::Bind(&DidGetQuotaValue
, quota_raw_ptr
,
840 std::string(devtools::Page::Quota::kParamPersistent
),
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
])) {
852 quota_manager
->GetHostUsage(
853 host
, type
, kQuotaClients
[i
],
854 base::Bind(&DidGetHostUsage
, (*iter
).second
,
855 GetQuotaClientName(kQuotaClients
[i
]),
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
,
871 return command
->InvalidParamResponse(
872 devtools::Page::queryUsageAndQuota::kParamSecurityOrigin
);
875 ResponseCallback callback
= base::Bind(
876 &RendererOverridesHandler::PageQueryUsageAndQuotaCompleted
,
877 weak_factory_
.GetWeakPtr(),
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
,
889 &QueryUsageAndQuotaOnIOThread
,
891 GURL(security_origin
),
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
) {
905 capture_retry_count_
= kCaptureRetryLimit
;
906 base::DictionaryValue
* params
= new base::DictionaryValue();
908 devtools::Page::screencastVisibilityChanged::kParamVisible
, visible
);
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
)
933 color_picker_enabled_
= enabled
;
939 host_
->AddMouseEventCallback(mouse_event_callback_
);
940 UpdateColorPickerFrame();
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() {
956 RenderWidgetHostViewBase
* view
=
957 static_cast<RenderWidgetHostViewBase
*>(host_
->GetView());
961 gfx::Size size
= view
->GetViewBounds().size();
962 view
->CopyFromCompositingSurface(
963 gfx::Rect(size
), size
,
964 base::Bind(&RendererOverridesHandler::ColorPickerFrameUpdated
,
965 weak_factory_
.GetWeakPtr()),
969 void RendererOverridesHandler::ResetColorPickerFrame() {
970 color_picker_frame_
.reset();
975 void RendererOverridesHandler::ColorPickerFrameUpdated(
977 const SkBitmap
& bitmap
) {
978 if (!color_picker_enabled_
)
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())
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()) {
1001 SkAutoLockPixels
lock_image(color_picker_frame_
);
1002 SkColor color
= color_picker_frame_
.getColor(last_cursor_x_
,
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();
1017 void RendererOverridesHandler::UpdateColorPickerCursor() {
1018 if (!host_
|| color_picker_frame_
.drawsNothing())
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()) {
1026 RenderWidgetHostViewBase
* view
= static_cast<RenderWidgetHostViewBase
*>(
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;
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;
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
);
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
,
1073 canvas
->drawLine(kHotspotOffset
, kHotspotOffset
+ kHotspotRadius
,
1074 kHotspotOffset
, kHotspotOffset
+ 2 * kHotspotRadius
,
1076 canvas
->drawLine(kHotspotOffset
- 2 * kHotspotRadius
, kHotspotOffset
,
1077 kHotspotOffset
- kHotspotRadius
, kHotspotOffset
,
1079 canvas
->drawLine(kHotspotOffset
+ kHotspotRadius
, kHotspotOffset
,
1080 kHotspotOffset
+ 2 * kHotspotRadius
, kHotspotOffset
,
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;
1091 clip_path
.addOval(SkRect::MakeXYWH(padding
, padding
, kDiameter
, kDiameter
));
1093 canvas
->clipPath(clip_path
, SkRegion::kIntersect_Op
, true);
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
);
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
);
1123 paint
.setStrokeWidth(2);
1124 paint
.setColor(SK_ColorDKGRAY
);
1125 paint
.setAntiAlias(true);
1126 canvas
->drawCircle(kCursorSize
/ 2, kCursorSize
/ 2, kDiameter
/ 2, paint
);
1129 result
.allocN32Pixels(kCursorSize
* device_scale_factor
,
1130 kCursorSize
* device_scale_factor
);
1131 canvas
->readPixels(&result
, 0, 0);
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
);
1142 cursor_info
.external_handle
= 0;
1145 cursor
.InitFromCursorInfo(cursor_info
);
1147 host_
->SetCursor(cursor
);
1150 void RendererOverridesHandler::UpdateTouchEventEmulationState() {
1153 bool enabled
= touch_emulation_enabled_
|| screencast_command_
.get();
1154 host_
->SetTouchEventEmulationEnabled(enabled
);
1155 WebContentsImpl
* web_contents
= static_cast<WebContentsImpl
*>(
1156 WebContents::FromRenderViewHost(host_
));
1158 web_contents
->SetForceDisableOverscrollContent(enabled
);
1161 } // namespace content