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