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