Index: content/child/npapi/plugin_url_fetcher.cc |
=================================================================== |
--- content/child/npapi/plugin_url_fetcher.cc (revision 223347) |
+++ content/child/npapi/plugin_url_fetcher.cc (working copy) |
@@ -15,15 +15,64 @@ |
#include "net/base/load_flags.h" |
#include "net/base/net_errors.h" |
#include "net/http/http_response_headers.h" |
+#include "third_party/WebKit/public/platform/WebURLLoaderClient.h" |
+#include "third_party/WebKit/public/platform/WebURLResponse.h" |
+#include "webkit/child/multipart_response_delegate.h" |
+#include "webkit/child/weburlloader_impl.h" |
#include "webkit/common/resource_request_body.h" |
namespace content { |
+namespace { |
+// This class handles individual multipart responses. It is instantiated when |
+// we receive HTTP status code 206 in the HTTP response. This indicates |
+// that the response could have multiple parts each separated by a boundary |
+// specified in the response header. |
+// TODO(jam): this is similar to MultiPartResponseClient in webplugin_impl.cc, |
+// we should remove that other class once we switch to loading from the plugin |
+// process by default. |
+class MultiPartResponseClient : public WebKit::WebURLLoaderClient { |
+ public: |
+ explicit MultiPartResponseClient(PluginStreamUrl* plugin_stream) |
+ : byte_range_lower_bound_(0), plugin_stream_(plugin_stream) {} |
+ |
+ // WebKit::WebURLLoaderClient implementation: |
+ virtual void didReceiveResponse( |
+ WebKit::WebURLLoader* loader, |
+ const WebKit::WebURLResponse& response) OVERRIDE { |
+ int64 byte_range_upper_bound, instance_size; |
+ if (!webkit_glue::MultipartResponseDelegate::ReadContentRanges( |
+ response, &byte_range_lower_bound_, &byte_range_upper_bound, |
+ &instance_size)) { |
+ NOTREACHED(); |
+ } |
+ } |
+ virtual void didReceiveData(WebKit::WebURLLoader* loader, |
+ const char* data, |
+ int data_length, |
+ int encoded_data_length) OVERRIDE { |
+ // TODO(ananta) |
+ // We should defer further loads on multipart resources on the same lines |
+ // as regular resources requested by plugins to prevent reentrancy. |
+ plugin_stream_->DidReceiveData(data, data_length, byte_range_lower_bound_); |
+ byte_range_lower_bound_ += data_length; |
+ } |
+ |
+ private: |
+ // The lower bound of the byte range. |
+ int64 byte_range_lower_bound_; |
+ // The handler for the data. |
+ PluginStreamUrl* plugin_stream_; |
+}; |
+ |
+} // namespace |
+ |
PluginURLFetcher::PluginURLFetcher(PluginStreamUrl* plugin_stream, |
const GURL& url, |
const GURL& first_party_for_cookies, |
const std::string& method, |
- const std::string& post_data, |
+ const char* buf, |
+ unsigned int len, |
const GURL& referrer, |
bool notify_redirects, |
bool is_plugin_src_load, |
@@ -34,7 +83,6 @@ |
url_(url), |
first_party_for_cookies_(first_party_for_cookies), |
method_(method), |
- post_data_(post_data), |
referrer_(referrer), |
notify_redirects_(notify_redirects), |
is_plugin_src_load_(is_plugin_src_load), |
@@ -52,16 +100,23 @@ |
std::vector<char> body; |
if (method == "POST") { |
+ bool content_type_found = false; |
std::vector<std::string> names; |
std::vector<std::string> values; |
- PluginHost::SetPostData( |
- post_data.c_str(), post_data.size(), &names, &values, &body); |
+ PluginHost::SetPostData(buf, len, &names, &values, &body); |
for (size_t i = 0; i < names.size(); ++i) { |
if (!request_info.headers.empty()) |
request_info.headers += "\r\n"; |
- |
request_info.headers += names[i] + ": " + values[i]; |
+ if (LowerCaseEqualsASCII(names[i], "content-type")) |
+ content_type_found = true; |
} |
+ |
+ if (!content_type_found) { |
+ if (!request_info.headers.empty()) |
+ request_info.headers += "\r\n"; |
+ request_info.headers += "Content-Type: application/x-www-form-urlencoded"; |
+ } |
} |
bridge_.reset(ChildThread::current()->resource_dispatcher()->CreateBridge( |
@@ -119,7 +174,8 @@ |
// It's unfortunate that this logic of when a redirect's method changes is |
// in url_request.cc, but weburlloader_impl.cc and this file have to duplicate |
// it instead of passing that information. |
- if (info.headers->response_code() != 307) |
+ int response_code = info.headers->response_code(); |
+ if (response_code != 307) |
method_ = "GET"; |
GURL old_url = url_; |
url_ = new_url; |
@@ -129,7 +185,7 @@ |
// If the plugin does not participate in url redirect notifications then just |
// block cross origin 307 POST redirects. |
if (!notify_redirects_) { |
- if (info.headers->response_code() == 307 && method_ == "POST" && |
+ if (response_code == 307 && method_ == "POST" && |
old_url.GetOrigin() != new_url.GetOrigin()) { |
plugin_stream_->DidFail(resource_id_); // That will delete |this|. |
return false; |
@@ -137,7 +193,7 @@ |
} else { |
// Pause the request while we ask the plugin what to do about the redirect. |
bridge_->SetDefersLoading(true); |
- plugin_stream_->WillSendRequest(url_, info.headers->response_code()); |
+ plugin_stream_->WillSendRequest(url_, response_code); |
} |
return true; |
@@ -145,21 +201,78 @@ |
void PluginURLFetcher::OnReceivedResponse( |
const webkit_glue::ResourceResponseInfo& info) { |
- // TODO(jam): see WebPluginImpl::didReceiveResponse for request_is_seekable |
- bool request_is_seekable = false; |
- uint32 last_modified = 0; |
+ // TODO(jam): THIS LOGIC IS COPIED FROM WebPluginImpl::didReceiveResponse |
+ // GetAllHeaders, and GetResponseInfo until kDirectNPAPIRequests is the |
+ // default and we can remove the old path there. |
+ bool request_is_seekable = true; |
+ DCHECK(!multipart_delegate_.get()); |
+ if (plugin_stream_->seekable()) { |
+ int response_code = info.headers->response_code(); |
+ if (response_code == 206) { |
+ WebKit::WebURLResponse response; |
+ response.initialize(); |
+ webkit_glue::WebURLLoaderImpl::PopulateURLResponse(url_, info, &response); |
+ |
+ std::string multipart_boundary; |
+ if (webkit_glue::MultipartResponseDelegate::ReadMultipartBoundary( |
+ response, &multipart_boundary)) { |
+ plugin_stream_->instance()->webplugin()->DidStartLoading(); |
+ |
+ MultiPartResponseClient* multi_part_response_client = |
+ new MultiPartResponseClient(plugin_stream_); |
+ |
+ multipart_delegate_.reset(new webkit_glue::MultipartResponseDelegate( |
+ multi_part_response_client, NULL, response, multipart_boundary)); |
+ |
+ // Multiple ranges requested, data will be delivered by |
+ // MultipartResponseDelegate. |
+ data_offset_ = 0; |
+ return; |
+ } |
+ |
+ int64 upper_bound = 0, instance_size = 0; |
+ // Single range requested - go through original processing for |
+ // non-multipart requests, but update data offset. |
+ webkit_glue::MultipartResponseDelegate::ReadContentRanges( |
+ response, &data_offset_, &upper_bound, &instance_size); |
+ } else if (response_code == 200) { |
+ // TODO: should we handle this case? We used to but it's not clear that we |
ananta
2013/09/16 18:35:41
Can we add a CHECK here?
jam
2013/09/16 19:31:34
I'm adding UMA stats in https://codereview.chromiu
|
+ // still need to. This was bug 5403, fixed in r7139. |
+ } |
+ } |
+ |
+ // If the length comes in as -1, then it indicates that it was not |
+ // read off the HTTP headers. We replicate Safari webkit behavior here, |
+ // which is to set it to 0. |
+ int expected_length = std::max(static_cast<int>(info.content_length), 0); |
+ |
base::Time temp; |
- if (info.headers->GetLastModifiedValue(&temp)) |
- last_modified = static_cast<uint32>(temp.ToDoubleT()); |
+ uint32 last_modified = 0; |
+ std::string headers; |
+ if (info.headers) { // NULL for data: urls. |
+ if (info.headers->GetLastModifiedValue(&temp)) |
+ last_modified = static_cast<uint32>(temp.ToDoubleT()); |
- std::string headers = info.headers->raw_headers(); |
+ // TODO(darin): Shouldn't we also report HTTP version numbers? |
+ headers = base::StringPrintf("HTTP %d ", info.headers->response_code()); |
+ headers += info.headers->GetStatusText(); |
+ headers += "\n"; |
+ void* iter = NULL; |
+ std::string name, value; |
+ while (info.headers->EnumerateHeaderLines(&iter, &name, &value)) { |
+ // TODO(darin): Should we really exclude headers with an empty value? |
+ if (!name.empty() && !value.empty()) |
+ headers += name + ": " + value + "\n"; |
+ } |
+ } |
+ |
plugin_stream_->DidReceiveResponse(info.mime_type, |
- headers, |
- info.content_length, |
- last_modified, |
- request_is_seekable); |
+ headers, |
+ expected_length, |
+ last_modified, |
+ request_is_seekable); |
} |
void PluginURLFetcher::OnDownloadedData(int len, |
@@ -169,8 +282,12 @@ |
void PluginURLFetcher::OnReceivedData(const char* data, |
int data_length, |
int encoded_data_length) { |
- plugin_stream_->DidReceiveData(data, data_length, data_offset_); |
- data_offset_ += data_length; |
+ if (multipart_delegate_) { |
+ multipart_delegate_->OnReceivedData(data, data_length, encoded_data_length); |
+ } else { |
+ plugin_stream_->DidReceiveData(data, data_length, data_offset_); |
+ data_offset_ += data_length; |
+ } |
} |
void PluginURLFetcher::OnCompletedRequest( |
@@ -178,6 +295,11 @@ |
bool was_ignored_by_handler, |
const std::string& security_info, |
const base::TimeTicks& completion_time) { |
+ if (multipart_delegate_) { |
+ multipart_delegate_->OnCompletedRequest(); |
+ multipart_delegate_.reset(); |
+ } |
+ |
if (error_code == net::OK) { |
plugin_stream_->DidFinishLoading(resource_id_); |
} else { |