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