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/renderer/pepper/pepper_url_loader_host.h" 6 7 #include "content/renderer/pepper/pepper_plugin_instance_impl.h" 8 #include "content/renderer/pepper/renderer_ppapi_host_impl.h" 9 #include "content/renderer/pepper/url_request_info_util.h" 10 #include "content/renderer/pepper/url_response_info_util.h" 11 #include "net/base/net_errors.h" 12 #include "ppapi/c/pp_errors.h" 13 #include "ppapi/host/dispatch_host_message.h" 14 #include "ppapi/host/host_message_context.h" 15 #include "ppapi/host/ppapi_host.h" 16 #include "ppapi/proxy/ppapi_messages.h" 17 #include "ppapi/shared_impl/ppapi_globals.h" 18 #include "third_party/WebKit/public/platform/WebURLError.h" 19 #include "third_party/WebKit/public/platform/WebURLLoader.h" 20 #include "third_party/WebKit/public/platform/WebURLRequest.h" 21 #include "third_party/WebKit/public/platform/WebURLResponse.h" 22 #include "third_party/WebKit/public/web/WebDocument.h" 23 #include "third_party/WebKit/public/web/WebElement.h" 24 #include "third_party/WebKit/public/web/WebFrame.h" 25 #include "third_party/WebKit/public/web/WebKit.h" 26 #include "third_party/WebKit/public/web/WebPluginContainer.h" 27 #include "third_party/WebKit/public/web/WebSecurityOrigin.h" 28 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h" 29 30 using WebKit::WebFrame; 31 using WebKit::WebString; 32 using WebKit::WebURL; 33 using WebKit::WebURLError; 34 using WebKit::WebURLLoader; 35 using WebKit::WebURLLoaderOptions; 36 using WebKit::WebURLRequest; 37 using WebKit::WebURLResponse; 38 39 #ifdef _MSC_VER 40 // Do not warn about use of std::copy with raw pointers. 41 #pragma warning(disable : 4996) 42 #endif 43 44 namespace content { 45 46 PepperURLLoaderHost::PepperURLLoaderHost(RendererPpapiHostImpl* host, 47 bool main_document_loader, 48 PP_Instance instance, 49 PP_Resource resource) 50 : ResourceHost(host->GetPpapiHost(), instance, resource), 51 renderer_ppapi_host_(host), 52 main_document_loader_(main_document_loader), 53 has_universal_access_(false), 54 bytes_sent_(0), 55 total_bytes_to_be_sent_(-1), 56 bytes_received_(0), 57 total_bytes_to_be_received_(-1), 58 pending_response_(false), 59 weak_factory_(this) { 60 DCHECK((main_document_loader && !resource) || 61 (!main_document_loader && resource)); 62 } 63 64 PepperURLLoaderHost::~PepperURLLoaderHost() { 65 // Normally deleting this object will delete the loader which will implicitly 66 // cancel the load. But this won't happen for the main document loader. So it 67 // would be nice to issue a Close() here. 68 // 69 // However, the PDF plugin will cancel the document load and then close the 70 // resource (which is reasonable). It then makes a second request to load the 71 // document so it can set the "want progress" flags (which is unreasonable -- 72 // we should probably provide download progress on document loads). 73 // 74 // But a Close() on the main document (even if the request is already 75 // canceled) will cancel all pending subresources, of which the second 76 // request is one, and the load will fail. Even if we fixed the PDF reader to 77 // change the timing or to send progress events to avoid the second request, 78 // we don't want to cancel other loads when the main one is closed. 79 // 80 // "Leaking" the main document load here by not closing it will only affect 81 // plugins handling main document loads (which are very few, mostly only PDF) 82 // that dereference without explicitly closing the main document load (which 83 // PDF doesn't do -- it explicitly closes it before issuing the second 84 // request). And the worst thing that will happen is that any remaining data 85 // will get queued inside WebKit. 86 if (main_document_loader_) { 87 // The PluginInstance has a non-owning pointer to us. 88 PepperPluginInstanceImpl* instance_object = 89 renderer_ppapi_host_->GetPluginInstanceImpl(pp_instance()); 90 if (instance_object) { 91 DCHECK(instance_object->document_loader() == this); 92 instance_object->set_document_loader(NULL); 93 } 94 } 95 96 // There is a path whereby the destructor for the loader_ member can 97 // invoke InstanceWasDeleted() upon this URLLoaderResource, thereby 98 // re-entering the scoped_ptr destructor with the same scoped_ptr object 99 // via loader_.reset(). Be sure that loader_ is first NULL then destroy 100 // the scoped_ptr. See http://crbug.com/159429. 101 scoped_ptr<WebKit::WebURLLoader> for_destruction_only(loader_.release()); 102 } 103 104 int32_t PepperURLLoaderHost::OnResourceMessageReceived( 105 const IPC::Message& msg, 106 ppapi::host::HostMessageContext* context) { 107 IPC_BEGIN_MESSAGE_MAP(PepperURLLoaderHost, msg) 108 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 109 PpapiHostMsg_URLLoader_Open, 110 OnHostMsgOpen) 111 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 112 PpapiHostMsg_URLLoader_SetDeferLoading, 113 OnHostMsgSetDeferLoading) 114 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( 115 PpapiHostMsg_URLLoader_Close, 116 OnHostMsgClose); 117 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( 118 PpapiHostMsg_URLLoader_GrantUniversalAccess, 119 OnHostMsgGrantUniversalAccess) 120 IPC_END_MESSAGE_MAP() 121 return PP_ERROR_FAILED; 122 } 123 124 void PepperURLLoaderHost::willSendRequest( 125 WebURLLoader* loader, 126 WebURLRequest& new_request, 127 const WebURLResponse& redirect_response) { 128 DCHECK(out_of_order_replies_.empty()); 129 if (!request_data_.follow_redirects) { 130 SaveResponse(redirect_response); 131 SetDefersLoading(true); 132 } 133 } 134 135 void PepperURLLoaderHost::didSendData( 136 WebURLLoader* loader, 137 unsigned long long bytes_sent, 138 unsigned long long total_bytes_to_be_sent) { 139 // TODO(darin): Bounds check input? 140 bytes_sent_ = static_cast<int64_t>(bytes_sent); 141 total_bytes_to_be_sent_ = static_cast<int64_t>(total_bytes_to_be_sent); 142 UpdateProgress(); 143 } 144 145 void PepperURLLoaderHost::didReceiveResponse(WebURLLoader* loader, 146 const WebURLResponse& response) { 147 // Sets -1 if the content length is unknown. Send before issuing callback. 148 total_bytes_to_be_received_ = response.expectedContentLength(); 149 UpdateProgress(); 150 151 SaveResponse(response); 152 } 153 154 void PepperURLLoaderHost::didDownloadData(WebURLLoader* loader, 155 int data_length) { 156 bytes_received_ += data_length; 157 UpdateProgress(); 158 } 159 160 void PepperURLLoaderHost::didReceiveData(WebURLLoader* loader, 161 const char* data, 162 int data_length, 163 int encoded_data_length) { 164 // Note that |loader| will be NULL for document loads. 165 bytes_received_ += data_length; 166 UpdateProgress(); 167 168 PpapiPluginMsg_URLLoader_SendData* message = 169 new PpapiPluginMsg_URLLoader_SendData; 170 message->WriteData(data, data_length); 171 SendUpdateToPlugin(message); 172 } 173 174 void PepperURLLoaderHost::didFinishLoading(WebURLLoader* loader, 175 double finish_time) { 176 // Note that |loader| will be NULL for document loads. 177 SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_FinishedLoading(PP_OK)); 178 } 179 180 void PepperURLLoaderHost::didFail(WebURLLoader* loader, 181 const WebURLError& error) { 182 // Note that |loader| will be NULL for document loads. 183 int32_t pp_error = PP_ERROR_FAILED; 184 if (error.domain.equals(WebString::fromUTF8(net::kErrorDomain))) { 185 // TODO(bbudge): Extend pp_errors.h to cover interesting network errors 186 // from the net error domain. 187 switch (error.reason) { 188 case net::ERR_ACCESS_DENIED: 189 case net::ERR_NETWORK_ACCESS_DENIED: 190 pp_error = PP_ERROR_NOACCESS; 191 break; 192 } 193 } else { 194 // It's a WebKit error. 195 pp_error = PP_ERROR_NOACCESS; 196 } 197 SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_FinishedLoading(pp_error)); 198 } 199 200 void PepperURLLoaderHost::DidConnectPendingHostToResource() { 201 for (size_t i = 0; i < pending_replies_.size(); i++) 202 host()->SendUnsolicitedReply(pp_resource(), *pending_replies_[i]); 203 pending_replies_.clear(); 204 } 205 206 int32_t PepperURLLoaderHost::OnHostMsgOpen( 207 ppapi::host::HostMessageContext* context, 208 const ppapi::URLRequestInfoData& request_data) { 209 // An "Open" isn't a resource Call so has no reply, but failure to open 210 // implies a load failure. To make it harder to forget to send the load 211 // failed reply from the open handler, we instead catch errors and convert 212 // them to load failed messages. 213 int32_t ret = InternalOnHostMsgOpen(context, request_data); 214 DCHECK(ret != PP_OK_COMPLETIONPENDING); 215 216 if (ret != PP_OK) 217 SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_FinishedLoading(ret)); 218 return PP_OK; 219 } 220 221 // Since this is wrapped by OnHostMsgOpen, we can return errors here and they 222 // will be translated into a FinishedLoading call automatically. 223 int32_t PepperURLLoaderHost::InternalOnHostMsgOpen( 224 ppapi::host::HostMessageContext* context, 225 const ppapi::URLRequestInfoData& request_data) { 226 // Main document loads are already open, so don't allow people to open them 227 // again. 228 if (main_document_loader_) 229 return PP_ERROR_INPROGRESS; 230 231 // Create a copy of the request data since CreateWebURLRequest will populate 232 // the file refs. 233 ppapi::URLRequestInfoData filled_in_request_data = request_data; 234 235 if (URLRequestRequiresUniversalAccess(filled_in_request_data) && 236 !has_universal_access_) { 237 ppapi::PpapiGlobals::Get()->LogWithSource( 238 pp_instance(), PP_LOGLEVEL_ERROR, std::string(), 239 "PPB_URLLoader.Open: The URL you're requesting is " 240 " on a different security origin than your plugin. To request " 241 " cross-origin resources, see " 242 " PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS."); 243 return PP_ERROR_NOACCESS; 244 } 245 246 if (loader_.get()) 247 return PP_ERROR_INPROGRESS; 248 249 WebFrame* frame = GetFrame(); 250 if (!frame) 251 return PP_ERROR_FAILED; 252 253 WebURLRequest web_request; 254 if (!CreateWebURLRequest(&filled_in_request_data, frame, &web_request)) 255 return PP_ERROR_FAILED; 256 257 web_request.setTargetType(WebURLRequest::TargetIsObject); 258 web_request.setRequestorProcessID(renderer_ppapi_host_->GetPluginPID()); 259 260 WebURLLoaderOptions options; 261 if (has_universal_access_) { 262 options.allowCredentials = true; 263 options.crossOriginRequestPolicy = 264 WebURLLoaderOptions::CrossOriginRequestPolicyAllow; 265 } else { 266 // All other HTTP requests are untrusted. 267 options.untrustedHTTP = true; 268 if (filled_in_request_data.allow_cross_origin_requests) { 269 // Allow cross-origin requests with access control. The request specifies 270 // if credentials are to be sent. 271 options.allowCredentials = filled_in_request_data.allow_credentials; 272 options.crossOriginRequestPolicy = 273 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl; 274 } else { 275 // Same-origin requests can always send credentials. 276 options.allowCredentials = true; 277 } 278 } 279 280 loader_.reset(frame->createAssociatedURLLoader(options)); 281 if (!loader_.get()) 282 return PP_ERROR_FAILED; 283 284 // Don't actually save the request until we know we're going to load. 285 request_data_ = filled_in_request_data; 286 loader_->loadAsynchronously(web_request, this); 287 288 // Although the request is technically pending, this is not a "Call" message 289 // so we don't return COMPLETIONPENDING. 290 return PP_OK; 291 } 292 293 int32_t PepperURLLoaderHost::OnHostMsgSetDeferLoading( 294 ppapi::host::HostMessageContext* context, 295 bool defers_loading) { 296 SetDefersLoading(defers_loading); 297 return PP_OK; 298 } 299 300 int32_t PepperURLLoaderHost::OnHostMsgClose( 301 ppapi::host::HostMessageContext* context) { 302 Close(); 303 return PP_OK; 304 } 305 306 int32_t PepperURLLoaderHost::OnHostMsgGrantUniversalAccess( 307 ppapi::host::HostMessageContext* context) { 308 // Only plugins with private permission can bypass same origin. 309 if (!host()->permissions().HasPermission(ppapi::PERMISSION_PRIVATE)) 310 return PP_ERROR_FAILED; 311 has_universal_access_ = true; 312 return PP_OK; 313 } 314 315 void PepperURLLoaderHost::SendUpdateToPlugin(IPC::Message* message) { 316 // We must send messages to the plugin in the order that the responses are 317 // received from webkit, even when the host isn't ready to send messages or 318 // when the host performs an asynchronous operation. 319 // 320 // Only {FinishedLoading, ReceivedResponse, SendData} have ordering 321 // contraints; all other messages are immediately added to pending_replies_. 322 // 323 // Accepted orderings for {FinishedLoading, ReceivedResponse, SendData} are: 324 // - {ReceivedResponse, SendData (zero or more times), FinishedLoading} 325 // - {FinishedLoading (when status != PP_OK)} 326 if (message->type() == PpapiPluginMsg_URLLoader_SendData::ID || 327 message->type() == PpapiPluginMsg_URLLoader_FinishedLoading::ID) { 328 // Messages that must be sent after ReceivedResponse. 329 if (pending_response_) { 330 out_of_order_replies_.push_back(message); 331 } else { 332 SendOrderedUpdateToPlugin(message); 333 } 334 } else if (message->type() == PpapiPluginMsg_URLLoader_ReceivedResponse::ID) { 335 // Allow SendData and FinishedLoading into the ordered queue. 336 DCHECK(pending_response_); 337 SendOrderedUpdateToPlugin(message); 338 for (size_t i = 0; i < out_of_order_replies_.size(); i++) 339 SendOrderedUpdateToPlugin(out_of_order_replies_[i]); 340 // SendOrderedUpdateToPlugin destroys the messages for us. 341 out_of_order_replies_.weak_clear(); 342 pending_response_ = false; 343 } else { 344 // Messages without ordering constraints. 345 SendOrderedUpdateToPlugin(message); 346 } 347 } 348 349 void PepperURLLoaderHost::SendOrderedUpdateToPlugin(IPC::Message* message) { 350 if (pp_resource() == 0) { 351 pending_replies_.push_back(message); 352 } else { 353 host()->SendUnsolicitedReply(pp_resource(), *message); 354 delete message; 355 } 356 } 357 358 void PepperURLLoaderHost::Close() { 359 if (loader_.get()) 360 loader_->cancel(); 361 else if (main_document_loader_) 362 GetFrame()->stopLoading(); 363 } 364 365 WebKit::WebFrame* PepperURLLoaderHost::GetFrame() { 366 PepperPluginInstance* instance_object = 367 renderer_ppapi_host_->GetPluginInstance(pp_instance()); 368 if (!instance_object) 369 return NULL; 370 return instance_object->GetContainer()->element().document().frame(); 371 } 372 373 void PepperURLLoaderHost::SetDefersLoading(bool defers_loading) { 374 if (loader_.get()) 375 loader_->setDefersLoading(defers_loading); 376 377 // TODO(brettw) bug 96770: We need a way to set the defers loading flag on 378 // main document loads (when the loader_ is null). 379 } 380 381 void PepperURLLoaderHost::SaveResponse(const WebURLResponse& response) { 382 // When we're the main document loader, we send the response data up front, 383 // so we don't want to trigger any callbacks in the plugin which aren't 384 // expected. We should not be getting redirects so the response sent 385 // up-front should be valid (plugin document loads happen after all 386 // redirects are processed since WebKit has to know the MIME type). 387 if (!main_document_loader_) { 388 // We note when there's a callback in flight for a response to ensure that 389 // messages we send to the plugin are not sent out of order. See 390 // SendUpdateToPlugin() for more details. 391 DCHECK(!pending_response_); 392 pending_response_ = true; 393 394 DataFromWebURLResponse( 395 pp_instance(), 396 response, 397 base::Bind(&PepperURLLoaderHost::DidDataFromWebURLResponse, 398 weak_factory_.GetWeakPtr())); 399 } 400 } 401 402 void PepperURLLoaderHost::DidDataFromWebURLResponse( 403 const ppapi::URLResponseInfoData& data) { 404 SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_ReceivedResponse(data)); 405 } 406 407 void PepperURLLoaderHost::UpdateProgress() { 408 bool record_download = request_data_.record_download_progress; 409 bool record_upload = request_data_.record_upload_progress; 410 if (record_download || record_upload) { 411 // Here we go through some effort to only send the exact information that 412 // the requestor wanted in the request flags. It would be just as 413 // efficient to send all of it, but we don't want people to rely on 414 // getting download progress when they happen to set the upload progress 415 // flag. 416 ppapi::proxy::ResourceMessageReplyParams params; 417 SendUpdateToPlugin(new PpapiPluginMsg_URLLoader_UpdateProgress( 418 record_upload ? bytes_sent_ : -1, 419 record_upload ? total_bytes_to_be_sent_ : -1, 420 record_download ? bytes_received_ : -1, 421 record_download ? total_bytes_to_be_received_ : -1)); 422 } 423 } 424 425 } // namespace content 426