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 <string> 8 9 #include "base/base64.h" 10 #include "base/bind.h" 11 #include "base/bind_helpers.h" 12 #include "base/files/file_path.h" 13 #include "base/strings/string16.h" 14 #include "base/values.h" 15 #include "content/browser/child_process_security_policy_impl.h" 16 #include "content/browser/devtools/devtools_protocol_constants.h" 17 #include "content/browser/devtools/devtools_tracing_handler.h" 18 #include "content/browser/renderer_host/render_view_host_delegate.h" 19 #include "content/port/browser/render_widget_host_view_port.h" 20 #include "content/public/browser/browser_thread.h" 21 #include "content/public/browser/devtools_agent_host.h" 22 #include "content/public/browser/javascript_dialog_manager.h" 23 #include "content/public/browser/navigation_controller.h" 24 #include "content/public/browser/render_process_host.h" 25 #include "content/public/browser/render_view_host.h" 26 #include "content/public/browser/render_widget_host_view.h" 27 #include "content/public/browser/web_contents.h" 28 #include "content/public/browser/web_contents_delegate.h" 29 #include "content/public/common/page_transition_types.h" 30 #include "content/public/common/referrer.h" 31 #include "ui/gfx/codec/jpeg_codec.h" 32 #include "ui/gfx/codec/png_codec.h" 33 #include "ui/gfx/size_conversions.h" 34 #include "ui/snapshot/snapshot.h" 35 #include "url/gurl.h" 36 37 using base::TimeTicks; 38 39 namespace { 40 41 static const char kPng[] = "png"; 42 static const char kJpeg[] = "jpeg"; 43 static int kDefaultScreenshotQuality = 80; 44 45 } // namespace 46 47 namespace content { 48 49 RendererOverridesHandler::RendererOverridesHandler(DevToolsAgentHost* agent) 50 : agent_(agent), 51 weak_factory_(this) { 52 RegisterCommandHandler( 53 devtools::DOM::setFileInputFiles::kName, 54 base::Bind( 55 &RendererOverridesHandler::GrantPermissionsForSetFileInputFiles, 56 base::Unretained(this))); 57 RegisterCommandHandler( 58 devtools::Page::handleJavaScriptDialog::kName, 59 base::Bind( 60 &RendererOverridesHandler::PageHandleJavaScriptDialog, 61 base::Unretained(this))); 62 RegisterCommandHandler( 63 devtools::Page::navigate::kName, 64 base::Bind( 65 &RendererOverridesHandler::PageNavigate, 66 base::Unretained(this))); 67 RegisterCommandHandler( 68 devtools::Page::captureScreenshot::kName, 69 base::Bind( 70 &RendererOverridesHandler::PageCaptureScreenshot, 71 base::Unretained(this))); 72 } 73 74 RendererOverridesHandler::~RendererOverridesHandler() {} 75 76 scoped_refptr<DevToolsProtocol::Response> 77 RendererOverridesHandler::GrantPermissionsForSetFileInputFiles( 78 scoped_refptr<DevToolsProtocol::Command> command) { 79 base::DictionaryValue* params = command->params(); 80 base::ListValue* file_list = NULL; 81 const char* param = 82 devtools::DOM::setFileInputFiles::kParamFiles; 83 if (!params || !params->GetList(param, &file_list)) 84 return command->InvalidParamResponse(param); 85 RenderViewHost* host = agent_->GetRenderViewHost(); 86 if (!host) 87 return NULL; 88 89 for (size_t i = 0; i < file_list->GetSize(); ++i) { 90 base::FilePath::StringType file; 91 if (!file_list->GetString(i, &file)) 92 return command->InvalidParamResponse(param); 93 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile( 94 host->GetProcess()->GetID(), base::FilePath(file)); 95 } 96 return NULL; 97 } 98 99 scoped_refptr<DevToolsProtocol::Response> 100 RendererOverridesHandler::PageHandleJavaScriptDialog( 101 scoped_refptr<DevToolsProtocol::Command> command) { 102 base::DictionaryValue* params = command->params(); 103 const char* paramAccept = 104 devtools::Page::handleJavaScriptDialog::kParamAccept; 105 bool accept; 106 if (!params || !params->GetBoolean(paramAccept, &accept)) 107 return command->InvalidParamResponse(paramAccept); 108 string16 prompt_override; 109 string16* prompt_override_ptr = &prompt_override; 110 if (!params || !params->GetString( 111 devtools::Page::handleJavaScriptDialog::kParamPromptText, 112 prompt_override_ptr)) { 113 prompt_override_ptr = NULL; 114 } 115 116 RenderViewHost* host = agent_->GetRenderViewHost(); 117 if (host) { 118 WebContents* web_contents = host->GetDelegate()->GetAsWebContents(); 119 if (web_contents) { 120 JavaScriptDialogManager* manager = 121 web_contents->GetDelegate()->GetJavaScriptDialogManager(); 122 if (manager && manager->HandleJavaScriptDialog( 123 web_contents, accept, prompt_override_ptr)) { 124 return NULL; 125 } 126 } 127 } 128 return command->InternalErrorResponse("No JavaScript dialog to handle"); 129 } 130 131 scoped_refptr<DevToolsProtocol::Response> 132 RendererOverridesHandler::PageNavigate( 133 scoped_refptr<DevToolsProtocol::Command> command) { 134 base::DictionaryValue* params = command->params(); 135 std::string url; 136 const char* param = devtools::Page::navigate::kParamUrl; 137 if (!params || !params->GetString(param, &url)) 138 return command->InvalidParamResponse(param); 139 GURL gurl(url); 140 if (!gurl.is_valid()) { 141 return command->InternalErrorResponse("Cannot navigate to invalid URL"); 142 } 143 RenderViewHost* host = agent_->GetRenderViewHost(); 144 if (host) { 145 WebContents* web_contents = host->GetDelegate()->GetAsWebContents(); 146 if (web_contents) { 147 web_contents->GetController() 148 .LoadURL(gurl, Referrer(), PAGE_TRANSITION_TYPED, std::string()); 149 return command->SuccessResponse(new base::DictionaryValue()); 150 } 151 } 152 return command->InternalErrorResponse("No WebContents to navigate"); 153 } 154 155 scoped_refptr<DevToolsProtocol::Response> 156 RendererOverridesHandler::PageCaptureScreenshot( 157 scoped_refptr<DevToolsProtocol::Command> command) { 158 // Parse input parameters. 159 std::string format; 160 int quality = kDefaultScreenshotQuality; 161 double scale = 1; 162 base::DictionaryValue* params = command->params(); 163 if (params) { 164 params->GetString(devtools::Page::captureScreenshot::kParamFormat, 165 &format); 166 params->GetInteger(devtools::Page::captureScreenshot::kParamQuality, 167 &quality); 168 params->GetDouble(devtools::Page::captureScreenshot::kParamScale, 169 &scale); 170 } 171 if (format.empty()) 172 format = kPng; 173 if (quality < 0 || quality > 100) 174 quality = kDefaultScreenshotQuality; 175 if (scale <= 0 || scale > 1) 176 scale = 1; 177 178 RenderViewHost* host = agent_->GetRenderViewHost(); 179 gfx::Rect view_bounds = host->GetView()->GetViewBounds(); 180 181 // Grab screen pixels if available for current platform. 182 // TODO(pfeldman): support format, scale and quality in ui::GrabViewSnapshot. 183 std::vector<unsigned char> png; 184 bool is_unscaled_png = scale == 1 && format == kPng; 185 if (is_unscaled_png && ui::GrabViewSnapshot(host->GetView()->GetNativeView(), 186 &png, view_bounds)) { 187 std::string base64_data; 188 bool success = base::Base64Encode( 189 base::StringPiece(reinterpret_cast<char*>(&*png.begin()), png.size()), 190 &base64_data); 191 if (success) { 192 base::DictionaryValue* result = new base::DictionaryValue(); 193 result->SetString( 194 devtools::Page::captureScreenshot::kResponseData, base64_data); 195 return command->SuccessResponse(result); 196 } 197 return command->InternalErrorResponse("Unable to base64encode screenshot"); 198 } 199 200 // Fallback to copying from compositing surface. 201 RenderWidgetHostViewPort* view_port = 202 RenderWidgetHostViewPort::FromRWHV(host->GetView()); 203 204 gfx::Size snapshot_size = gfx::ToFlooredSize( 205 gfx::ScaleSize(view_bounds.size(), scale)); 206 view_port->CopyFromCompositingSurface( 207 view_bounds, snapshot_size, 208 base::Bind(&RendererOverridesHandler::ScreenshotCaptured, 209 weak_factory_.GetWeakPtr(), command, format, quality, scale)); 210 return command->AsyncResponsePromise(); 211 } 212 213 void RendererOverridesHandler::ScreenshotCaptured( 214 scoped_refptr<DevToolsProtocol::Command> command, 215 const std::string& format, 216 int quality, 217 double scale, 218 bool success, 219 const SkBitmap& bitmap) { 220 if (!success) { 221 SendRawMessage( 222 command->InternalErrorResponse("Unable to capture screenshot")-> 223 Serialize()); 224 return; 225 } 226 227 std::vector<unsigned char> data; 228 SkAutoLockPixels lock_image(bitmap); 229 bool encoded; 230 if (format == kPng) { 231 encoded = gfx::PNGCodec::Encode( 232 reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)), 233 gfx::PNGCodec::FORMAT_SkBitmap, 234 gfx::Size(bitmap.width(), bitmap.height()), 235 bitmap.width() * bitmap.bytesPerPixel(), 236 false, std::vector<gfx::PNGCodec::Comment>(), &data); 237 } else if (format == kJpeg) { 238 encoded = gfx::JPEGCodec::Encode( 239 reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)), 240 gfx::JPEGCodec::FORMAT_SkBitmap, 241 bitmap.width(), 242 bitmap.height(), 243 bitmap.width() * bitmap.bytesPerPixel(), 244 quality, &data); 245 } else { 246 encoded = false; 247 } 248 249 if (!encoded) { 250 SendRawMessage( 251 command->InternalErrorResponse("Unable to encode screenshot")-> 252 Serialize()); 253 return; 254 } 255 256 std::string base_64_data; 257 if (!base::Base64Encode(base::StringPiece( 258 reinterpret_cast<char*>(&data[0]), 259 data.size()), 260 &base_64_data)) { 261 SendRawMessage( 262 command->InternalErrorResponse("Unable to base64 encode screenshot")-> 263 Serialize()); 264 return; 265 } 266 267 base::DictionaryValue* response = new base::DictionaryValue(); 268 response->SetString( 269 devtools::Page::captureScreenshot::kResponseData, base_64_data); 270 SendRawMessage(command->SuccessResponse(response)->Serialize()); 271 } 272 273 } // namespace content 274