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