| OLD | NEW |
| (Empty) |
| 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/child/npapi/plugin_url_fetcher.h" | |
| 6 | |
| 7 #include "base/memory/scoped_ptr.h" | |
| 8 #include "content/child/child_thread_impl.h" | |
| 9 #include "content/child/multipart_response_delegate.h" | |
| 10 #include "content/child/npapi/plugin_host.h" | |
| 11 #include "content/child/npapi/plugin_instance.h" | |
| 12 #include "content/child/npapi/plugin_stream_url.h" | |
| 13 #include "content/child/npapi/webplugin.h" | |
| 14 #include "content/child/npapi/webplugin_resource_client.h" | |
| 15 #include "content/child/plugin_messages.h" | |
| 16 #include "content/child/request_extra_data.h" | |
| 17 #include "content/child/request_info.h" | |
| 18 #include "content/child/resource_dispatcher.h" | |
| 19 #include "content/child/web_url_loader_impl.h" | |
| 20 #include "content/common/resource_request_body.h" | |
| 21 #include "content/common/service_worker/service_worker_types.h" | |
| 22 #include "content/public/common/resource_response_info.h" | |
| 23 #include "net/base/load_flags.h" | |
| 24 #include "net/base/net_errors.h" | |
| 25 #include "net/http/http_response_headers.h" | |
| 26 #include "net/url_request/redirect_info.h" | |
| 27 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h" | |
| 28 #include "third_party/WebKit/public/platform/WebURLResponse.h" | |
| 29 | |
| 30 namespace content { | |
| 31 namespace { | |
| 32 | |
| 33 // This class handles individual multipart responses. It is instantiated when | |
| 34 // we receive HTTP status code 206 in the HTTP response. This indicates | |
| 35 // that the response could have multiple parts each separated by a boundary | |
| 36 // specified in the response header. | |
| 37 // TODO(jam): this is similar to MultiPartResponseClient in webplugin_impl.cc, | |
| 38 // we should remove that other class once we switch to loading from the plugin | |
| 39 // process by default. | |
| 40 class MultiPartResponseClient : public blink::WebURLLoaderClient { | |
| 41 public: | |
| 42 explicit MultiPartResponseClient(PluginStreamUrl* plugin_stream) | |
| 43 : byte_range_lower_bound_(0), plugin_stream_(plugin_stream) {} | |
| 44 | |
| 45 // blink::WebURLLoaderClient implementation: | |
| 46 void didReceiveResponse(blink::WebURLLoader* loader, | |
| 47 const blink::WebURLResponse& response) override { | |
| 48 int64 byte_range_upper_bound, instance_size; | |
| 49 if (!MultipartResponseDelegate::ReadContentRanges(response, | |
| 50 &byte_range_lower_bound_, | |
| 51 &byte_range_upper_bound, | |
| 52 &instance_size)) { | |
| 53 NOTREACHED(); | |
| 54 } | |
| 55 } | |
| 56 void didReceiveData(blink::WebURLLoader* loader, | |
| 57 const char* data, | |
| 58 int data_length, | |
| 59 int encoded_data_length) override { | |
| 60 // TODO(ananta) | |
| 61 // We should defer further loads on multipart resources on the same lines | |
| 62 // as regular resources requested by plugins to prevent reentrancy. | |
| 63 int64 data_offset = byte_range_lower_bound_; | |
| 64 byte_range_lower_bound_ += data_length; | |
| 65 plugin_stream_->DidReceiveData(data, data_length, data_offset); | |
| 66 // DANGER: this instance may be deleted at this point. | |
| 67 } | |
| 68 | |
| 69 private: | |
| 70 // The lower bound of the byte range. | |
| 71 int64 byte_range_lower_bound_; | |
| 72 // The handler for the data. | |
| 73 PluginStreamUrl* plugin_stream_; | |
| 74 }; | |
| 75 | |
| 76 } // namespace | |
| 77 | |
| 78 PluginURLFetcher::PluginURLFetcher(PluginStreamUrl* plugin_stream, | |
| 79 const GURL& url, | |
| 80 const GURL& first_party_for_cookies, | |
| 81 const std::string& method, | |
| 82 const char* buf, | |
| 83 unsigned int len, | |
| 84 const Referrer& referrer, | |
| 85 const std::string& range, | |
| 86 bool notify_redirects, | |
| 87 bool is_plugin_src_load, | |
| 88 int origin_pid, | |
| 89 int render_frame_id, | |
| 90 int render_view_id, | |
| 91 unsigned long resource_id, | |
| 92 bool copy_stream_data) | |
| 93 : plugin_stream_(plugin_stream), | |
| 94 url_(url), | |
| 95 first_party_for_cookies_(first_party_for_cookies), | |
| 96 referrer_(referrer), | |
| 97 notify_redirects_(notify_redirects), | |
| 98 is_plugin_src_load_(is_plugin_src_load), | |
| 99 origin_pid_(origin_pid), | |
| 100 render_frame_id_(render_frame_id), | |
| 101 render_view_id_(render_view_id), | |
| 102 resource_id_(resource_id), | |
| 103 copy_stream_data_(copy_stream_data), | |
| 104 data_offset_(0), | |
| 105 pending_failure_notification_(false), | |
| 106 request_id_(-1), | |
| 107 weak_factory_(this) { | |
| 108 RequestInfo request_info; | |
| 109 request_info.method = method; | |
| 110 request_info.url = url; | |
| 111 request_info.first_party_for_cookies = first_party_for_cookies; | |
| 112 request_info.referrer = referrer; | |
| 113 request_info.load_flags = net::LOAD_NORMAL; | |
| 114 request_info.requestor_pid = origin_pid; | |
| 115 request_info.request_type = RESOURCE_TYPE_OBJECT; | |
| 116 request_info.routing_id = render_view_id; | |
| 117 // ServiceWorker is disabled for NPAPI. | |
| 118 request_info.skip_service_worker = true; | |
| 119 | |
| 120 RequestExtraData extra_data; | |
| 121 extra_data.set_render_frame_id(render_frame_id); | |
| 122 extra_data.set_is_main_frame(false); | |
| 123 request_info.extra_data = &extra_data; | |
| 124 | |
| 125 std::vector<char> body; | |
| 126 if (method == "POST") { | |
| 127 bool content_type_found = false; | |
| 128 std::vector<std::string> names; | |
| 129 std::vector<std::string> values; | |
| 130 PluginHost::SetPostData(buf, len, &names, &values, &body); | |
| 131 for (size_t i = 0; i < names.size(); ++i) { | |
| 132 if (!request_info.headers.empty()) | |
| 133 request_info.headers += "\r\n"; | |
| 134 request_info.headers += names[i] + ": " + values[i]; | |
| 135 if (base::LowerCaseEqualsASCII(names[i], "content-type")) | |
| 136 content_type_found = true; | |
| 137 } | |
| 138 | |
| 139 if (!content_type_found) { | |
| 140 if (!request_info.headers.empty()) | |
| 141 request_info.headers += "\r\n"; | |
| 142 request_info.headers += "Content-Type: application/x-www-form-urlencoded"; | |
| 143 } | |
| 144 } else { | |
| 145 if (!range.empty()) | |
| 146 request_info.headers = std::string("Range: ") + range; | |
| 147 } | |
| 148 | |
| 149 scoped_refptr<ResourceRequestBody> request_body = new ResourceRequestBody; | |
| 150 if (!body.empty()) | |
| 151 request_body->AppendBytes(&body[0], body.size()); | |
| 152 | |
| 153 request_id_ = ChildThreadImpl::current()->resource_dispatcher()->StartAsync( | |
| 154 request_info, request_body.get(), this); | |
| 155 | |
| 156 // TODO(jam): range requests | |
| 157 } | |
| 158 | |
| 159 PluginURLFetcher::~PluginURLFetcher() { | |
| 160 if (request_id_ >= 0) { | |
| 161 ChildThreadImpl::current()->resource_dispatcher()->RemovePendingRequest( | |
| 162 request_id_); | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 void PluginURLFetcher::Cancel() { | |
| 167 ChildThreadImpl::current()->resource_dispatcher()->Cancel(request_id_); | |
| 168 | |
| 169 // Due to races and nested event loops, PluginURLFetcher may still receive | |
| 170 // events from the bridge before being destroyed. Do not forward additional | |
| 171 // events back to the plugin, via either |plugin_stream_| or | |
| 172 // |multipart_delegate_| which has its own pointer via | |
| 173 // MultiPartResponseClient. | |
| 174 if (multipart_delegate_) | |
| 175 multipart_delegate_->Cancel(); | |
| 176 plugin_stream_ = NULL; | |
| 177 } | |
| 178 | |
| 179 void PluginURLFetcher::URLRedirectResponse(bool allow) { | |
| 180 if (!plugin_stream_) | |
| 181 return; | |
| 182 | |
| 183 if (allow) { | |
| 184 ChildThreadImpl::current()->resource_dispatcher()->SetDefersLoading( | |
| 185 request_id_, false); | |
| 186 } else { | |
| 187 ChildThreadImpl::current()->resource_dispatcher()->Cancel(request_id_); | |
| 188 plugin_stream_->DidFail(resource_id_); // That will delete |this|. | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 void PluginURLFetcher::OnUploadProgress(uint64 position, uint64 size) { | |
| 193 } | |
| 194 | |
| 195 bool PluginURLFetcher::OnReceivedRedirect( | |
| 196 const net::RedirectInfo& redirect_info, | |
| 197 const ResourceResponseInfo& info) { | |
| 198 if (!plugin_stream_) | |
| 199 return false; | |
| 200 | |
| 201 // TODO(jam): THIS LOGIC IS COPIED FROM WebPluginImpl::willSendRequest until | |
| 202 // kDirectNPAPIRequests is the default and we can remove the old path there. | |
| 203 | |
| 204 // Currently this check is just to catch an https -> http redirect when | |
| 205 // loading the main plugin src URL. Longer term, we could investigate | |
| 206 // firing mixed diplay or scripting issues for subresource loads | |
| 207 // initiated by plugins. | |
| 208 if (is_plugin_src_load_ && | |
| 209 !plugin_stream_->instance()->webplugin()->CheckIfRunInsecureContent( | |
| 210 redirect_info.new_url)) { | |
| 211 plugin_stream_->DidFail(resource_id_); // That will delete |this|. | |
| 212 return false; | |
| 213 } | |
| 214 | |
| 215 GURL old_url = url_; | |
| 216 url_ = redirect_info.new_url; | |
| 217 first_party_for_cookies_ = redirect_info.new_first_party_for_cookies; | |
| 218 | |
| 219 // If the plugin does not participate in url redirect notifications then just | |
| 220 // block cross origin 307 POST redirects. | |
| 221 if (!notify_redirects_) { | |
| 222 if (redirect_info.status_code == 307 && | |
| 223 redirect_info.new_method == "POST" && | |
| 224 old_url.GetOrigin() != url_.GetOrigin()) { | |
| 225 plugin_stream_->DidFail(resource_id_); // That will delete |this|. | |
| 226 return false; | |
| 227 } | |
| 228 } else { | |
| 229 // Pause the request while we ask the plugin what to do about the redirect. | |
| 230 ChildThreadImpl::current()->resource_dispatcher()->SetDefersLoading( | |
| 231 request_id_, true); | |
| 232 plugin_stream_->WillSendRequest(url_, redirect_info.status_code); | |
| 233 } | |
| 234 | |
| 235 return true; | |
| 236 } | |
| 237 | |
| 238 void PluginURLFetcher::OnReceivedResponse(const ResourceResponseInfo& info) { | |
| 239 if (!plugin_stream_) | |
| 240 return; | |
| 241 | |
| 242 // TODO(jam): THIS LOGIC IS COPIED FROM WebPluginImpl::didReceiveResponse | |
| 243 // GetAllHeaders, and GetResponseInfo until kDirectNPAPIRequests is the | |
| 244 // default and we can remove the old path there. | |
| 245 | |
| 246 bool request_is_seekable = true; | |
| 247 DCHECK(!multipart_delegate_.get()); | |
| 248 if (plugin_stream_->seekable()) { | |
| 249 int response_code = info.headers->response_code(); | |
| 250 if (response_code == 206) { | |
| 251 blink::WebURLResponse response; | |
| 252 response.initialize(); | |
| 253 WebURLLoaderImpl::PopulateURLResponse(url_, info, &response, | |
| 254 false /* report_security_info */); | |
| 255 | |
| 256 std::string multipart_boundary; | |
| 257 if (MultipartResponseDelegate::ReadMultipartBoundary( | |
| 258 response, &multipart_boundary)) { | |
| 259 plugin_stream_->instance()->webplugin()->DidStartLoading(); | |
| 260 | |
| 261 MultiPartResponseClient* multi_part_response_client = | |
| 262 new MultiPartResponseClient(plugin_stream_); | |
| 263 | |
| 264 multipart_delegate_.reset(new MultipartResponseDelegate( | |
| 265 multi_part_response_client, NULL, response, multipart_boundary)); | |
| 266 | |
| 267 // Multiple ranges requested, data will be delivered by | |
| 268 // MultipartResponseDelegate. | |
| 269 data_offset_ = 0; | |
| 270 return; | |
| 271 } | |
| 272 | |
| 273 int64 upper_bound = 0, instance_size = 0; | |
| 274 // Single range requested - go through original processing for | |
| 275 // non-multipart requests, but update data offset. | |
| 276 MultipartResponseDelegate::ReadContentRanges( | |
| 277 response, &data_offset_, &upper_bound, &instance_size); | |
| 278 } else if (response_code == 200) { | |
| 279 // TODO: should we handle this case? We used to but it's not clear that we | |
| 280 // still need to. This was bug 5403, fixed in r7139. | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 // If the length comes in as -1, then it indicates that it was not | |
| 285 // read off the HTTP headers. We replicate Safari webkit behavior here, | |
| 286 // which is to set it to 0. | |
| 287 int expected_length = std::max(static_cast<int>(info.content_length), 0); | |
| 288 | |
| 289 base::Time temp; | |
| 290 uint32 last_modified = 0; | |
| 291 std::string headers; | |
| 292 if (info.headers.get()) { // NULL for data: urls. | |
| 293 if (info.headers->GetLastModifiedValue(&temp)) | |
| 294 last_modified = static_cast<uint32>(temp.ToDoubleT()); | |
| 295 | |
| 296 // TODO(darin): Shouldn't we also report HTTP version numbers? | |
| 297 int response_code = info.headers->response_code(); | |
| 298 headers = base::StringPrintf("HTTP %d ", response_code); | |
| 299 headers += info.headers->GetStatusText(); | |
| 300 headers += "\n"; | |
| 301 | |
| 302 void* iter = NULL; | |
| 303 std::string name, value; | |
| 304 while (info.headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
| 305 // TODO(darin): Should we really exclude headers with an empty value? | |
| 306 if (!name.empty() && !value.empty()) | |
| 307 headers += name + ": " + value + "\n"; | |
| 308 } | |
| 309 | |
| 310 // Bug http://b/issue?id=925559. The flash plugin would not handle the HTTP | |
| 311 // error codes in the stream header and as a result, was unaware of the fate | |
| 312 // of the HTTP requests issued via NPN_GetURLNotify. Webkit and FF destroy | |
| 313 // the stream and invoke the NPP_DestroyStream function on the plugin if the | |
| 314 // HTTPrequest fails. | |
| 315 if ((url_.SchemeIs(url::kHttpScheme) || url_.SchemeIs(url::kHttpsScheme)) && | |
| 316 (response_code < 100 || response_code >= 400)) { | |
| 317 pending_failure_notification_ = true; | |
| 318 } | |
| 319 } | |
| 320 | |
| 321 plugin_stream_->DidReceiveResponse(info.mime_type, | |
| 322 headers, | |
| 323 expected_length, | |
| 324 last_modified, | |
| 325 request_is_seekable); | |
| 326 } | |
| 327 | |
| 328 void PluginURLFetcher::OnDownloadedData(int len, | |
| 329 int encoded_data_length) { | |
| 330 } | |
| 331 | |
| 332 void PluginURLFetcher::OnReceivedData(scoped_ptr<ReceivedData> data) { | |
| 333 const char* payload = data->payload(); | |
| 334 int data_length = data->length(); | |
| 335 int encoded_data_length = data->encoded_length(); | |
| 336 if (!plugin_stream_) | |
| 337 return; | |
| 338 | |
| 339 if (multipart_delegate_) { | |
| 340 multipart_delegate_->OnReceivedData(payload, data_length, | |
| 341 encoded_data_length); | |
| 342 } else { | |
| 343 int64 offset = data_offset_; | |
| 344 data_offset_ += data_length; | |
| 345 | |
| 346 if (copy_stream_data_) { | |
| 347 // QuickTime writes to this memory, and since we got it from | |
| 348 // ResourceDispatcher it's not mapped for write access in this process. | |
| 349 // http://crbug.com/308466. | |
| 350 scoped_ptr<char[]> data_copy(new char[data_length]); | |
| 351 memcpy(data_copy.get(), payload, data_length); | |
| 352 plugin_stream_->DidReceiveData(data_copy.get(), data_length, offset); | |
| 353 } else { | |
| 354 plugin_stream_->DidReceiveData(payload, data_length, offset); | |
| 355 } | |
| 356 // DANGER: this instance may be deleted at this point. | |
| 357 } | |
| 358 } | |
| 359 | |
| 360 void PluginURLFetcher::OnCompletedRequest( | |
| 361 int error_code, | |
| 362 bool was_ignored_by_handler, | |
| 363 bool stale_copy_in_cache, | |
| 364 const std::string& security_info, | |
| 365 const base::TimeTicks& completion_time, | |
| 366 int64 total_transfer_size) { | |
| 367 if (!plugin_stream_) | |
| 368 return; | |
| 369 | |
| 370 if (multipart_delegate_) { | |
| 371 multipart_delegate_->OnCompletedRequest(); | |
| 372 multipart_delegate_.reset(); | |
| 373 } | |
| 374 | |
| 375 if (error_code == net::OK) { | |
| 376 plugin_stream_->DidFinishLoading(resource_id_); | |
| 377 } else { | |
| 378 plugin_stream_->DidFail(resource_id_); | |
| 379 } | |
| 380 } | |
| 381 | |
| 382 void PluginURLFetcher::OnReceivedCompletedResponse( | |
| 383 const content::ResourceResponseInfo& info, | |
| 384 scoped_ptr<ReceivedData> data, | |
| 385 int error_code, | |
| 386 bool was_ignored_by_handler, | |
| 387 bool stale_copy_in_cache, | |
| 388 const std::string& security_info, | |
| 389 const base::TimeTicks& completion_time, | |
| 390 int64 total_transfer_size) { | |
| 391 // |this| can be deleted on each callback. |weak_this| is placed here to | |
| 392 // detect the deletion. | |
| 393 base::WeakPtr<PluginURLFetcher> weak_this = weak_factory_.GetWeakPtr(); | |
| 394 OnReceivedResponse(info); | |
| 395 | |
| 396 if (!weak_this) | |
| 397 return; | |
| 398 if (data) | |
| 399 OnReceivedData(data.Pass()); | |
| 400 | |
| 401 if (!weak_this) | |
| 402 return; | |
| 403 OnCompletedRequest(error_code, was_ignored_by_handler, stale_copy_in_cache, | |
| 404 security_info, completion_time, total_transfer_size); | |
| 405 } | |
| 406 } // namespace content | |
| OLD | NEW |