Commit 50106bf5 authored by jam@chromium.org's avatar jam@chromium.org

Support byte range requests when routing resource requests directly through the browser process.

This also fixes a number of issues I saw when testing Netflix:
-fix http status header not reaching plugin
-send a Content-Type header if one isn't specified by the plugin for POSTs
-null check info->headers, needed for data: URLs

This doesn't support responding to 200s after a byte-range request. I'm gathering stats now to see how often this happens.

BUG=286074
R=ananta@chromium.org, jschuh@chromium.org

Review URL: https://codereview.chromium.org/23830007

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@223483 0039d316-1c4b-4281-b951-d872f2087c98
parent de9c2085
......@@ -11,7 +11,9 @@
#include "base/platform_file.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "content/browser/plugin_process_host.h"
#include "content/browser/site_instance_impl.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/bindings_policy.h"
......@@ -802,6 +804,17 @@ bool ChildProcessSecurityPolicyImpl::CanAccessCookiesForOrigin(
bool ChildProcessSecurityPolicyImpl::CanSendCookiesForOrigin(int child_id,
const GURL& gurl) {
for (PluginProcessHostIterator iter; !iter.Done(); ++iter) {
if (iter.GetData().process_type == child_id) {
if (iter.GetData().process_type == PROCESS_TYPE_PLUGIN) {
// NPAPI plugin processes are unsandboxed and so are trusted. Plugins
// can make request to any origin.
return true;
}
break;
}
}
base::AutoLock lock(lock_);
SecurityStateMap::iterator state = security_state_.find(child_id);
if (state == security_state_.end())
......
......@@ -989,7 +989,7 @@ void ResourceDispatcherHostImpl::BeginRequest(
if (request_data.request_body.get()) {
webkit_blob::BlobStorageContext* blob_context = NULL;
if (filter_->blob_storage_context())
blob_context = filter_->blob_storage_context()->context(),
blob_context = filter_->blob_storage_context()->context();
request->set_upload(UploadDataStreamBuilder::Build(
request_data.request_body.get(),
blob_context,
......
......@@ -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 @@ PluginURLFetcher::PluginURLFetcher(PluginStreamUrl* plugin_stream,
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,15 +100,22 @@ PluginURLFetcher::PluginURLFetcher(PluginStreamUrl* plugin_stream,
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";
}
}
......@@ -119,7 +174,8 @@ bool PluginURLFetcher::OnReceivedRedirect(
// 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 @@ bool PluginURLFetcher::OnReceivedRedirect(
// 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 @@ bool PluginURLFetcher::OnReceivedRedirect(
} 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,19 +201,76 @@ bool PluginURLFetcher::OnReceivedRedirect(
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
// 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;
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,
expected_length,
last_modified,
request_is_seekable);
}
......@@ -169,8 +282,12 @@ void PluginURLFetcher::OnDownloadedData(int len,
void PluginURLFetcher::OnReceivedData(const char* data,
int data_length,
int encoded_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 @@ void PluginURLFetcher::OnCompletedRequest(
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 {
......
......@@ -12,6 +12,7 @@
#include "webkit/child/resource_loader_bridge.h"
namespace webkit_glue {
class MultipartResponseDelegate;
class ResourceLoaderBridge;
}
......@@ -25,7 +26,8 @@ class PluginURLFetcher : public webkit_glue::ResourceLoaderBridge::Peer {
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,
......@@ -63,12 +65,13 @@ class PluginURLFetcher : public webkit_glue::ResourceLoaderBridge::Peer {
GURL url_;
GURL first_party_for_cookies_;
std::string method_;
std::string post_data_;
GURL referrer_;
bool notify_redirects_;
bool is_plugin_src_load_;
unsigned long resource_id_;
int data_offset_;
int64 data_offset_;
scoped_ptr<webkit_glue::MultipartResponseDelegate> multipart_delegate_;
scoped_ptr<webkit_glue::ResourceLoaderBridge> bridge_;
......
......@@ -90,6 +90,9 @@ class WebPlugin {
const char* range_info,
int range_request_id) = 0;
virtual void DidStartLoading() = 0;
virtual void DidStopLoading() = 0;
// Returns true iff in incognito mode.
virtual bool IsOffTheRecord() = 0;
......
......@@ -136,7 +136,8 @@ class WebPluginDelegate {
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,
......
......@@ -305,7 +305,8 @@ void WebPluginDelegateImpl::FetchURL(unsigned long resource_id,
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,
......@@ -317,7 +318,7 @@ void WebPluginDelegateImpl::FetchURL(unsigned long resource_id,
resource_id, url, std::string(), notify_id);
plugin_stream->SetPluginURLFetcher(new PluginURLFetcher(
plugin_stream, url, first_party_for_cookies, method, post_data,
plugin_stream, url, first_party_for_cookies, method, buf, len,
referrer, notify_redirects, is_plugin_src_load, origin_pid,
render_view_id, resource_id));
}
......
......@@ -122,7 +122,8 @@ class WebPluginDelegateImpl : public WebPluginDelegate {
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,
......
......@@ -58,7 +58,7 @@ IPC_STRUCT_BEGIN(PluginMsg_FetchURL_Params)
IPC_STRUCT_MEMBER(GURL, url)
IPC_STRUCT_MEMBER(GURL, first_party_for_cookies)
IPC_STRUCT_MEMBER(std::string, method)
IPC_STRUCT_MEMBER(std::string, post_data)
IPC_STRUCT_MEMBER(std::vector<char>, post_data)
IPC_STRUCT_MEMBER(GURL, referrer)
IPC_STRUCT_MEMBER(bool, notify_redirect)
IPC_STRUCT_MEMBER(bool, is_plugin_src_load)
......@@ -277,6 +277,9 @@ IPC_MESSAGE_ROUTED3(PluginHostMsg_InitiateHTTPRangeRequest,
std::string /* range_info */,
int /* range_request_id */)
IPC_MESSAGE_ROUTED0(PluginHostMsg_DidStartLoading)
IPC_MESSAGE_ROUTED0(PluginHostMsg_DidStopLoading)
IPC_MESSAGE_ROUTED2(PluginHostMsg_DeferResourceLoading,
unsigned long /* resource_id */,
bool /* defer */)
......
......@@ -425,12 +425,17 @@ void WebPluginDelegateStub::OnHTTPRangeRequestReply(
void WebPluginDelegateStub::OnFetchURL(
const PluginMsg_FetchURL_Params& params) {
const char* data = NULL;
if (params.post_data.size())
data = &params.post_data[0];
delegate_->FetchURL(params.resource_id,
params.notify_id,
params.url,
params.first_party_for_cookies,
params.method,
params.post_data,
data,
params.post_data.size(),
params.referrer,
params.notify_redirect,
params.is_plugin_src_load,
......
......@@ -620,6 +620,14 @@ void WebPluginProxy::InitiateHTTPRangeRequest(
route_id_, url, range_info, range_request_id));
}
void WebPluginProxy::DidStartLoading() {
Send(new PluginHostMsg_DidStartLoading(route_id_));
}
void WebPluginProxy::DidStopLoading() {
Send(new PluginHostMsg_DidStopLoading(route_id_));
}
void WebPluginProxy::SetDeferResourceLoading(unsigned long resource_id,
bool defer) {
Send(new PluginHostMsg_DeferResourceLoading(route_id_, resource_id, defer));
......
......@@ -86,6 +86,8 @@ class WebPluginProxy : public WebPlugin,
virtual void CancelDocumentLoad() OVERRIDE;
virtual void InitiateHTTPRangeRequest(
const char* url, const char* range_info, int range_request_id) OVERRIDE;
virtual void DidStartLoading() OVERRIDE;
virtual void DidStopLoading() OVERRIDE;
virtual void SetDeferResourceLoading(unsigned long resource_id,
bool defer) OVERRIDE;
virtual bool IsOffTheRecord() OVERRIDE;
......
......@@ -450,6 +450,8 @@ bool WebPluginDelegateProxy::OnMessageReceived(const IPC::Message& msg) {
IPC_MESSAGE_HANDLER(PluginHostMsg_CancelDocumentLoad, OnCancelDocumentLoad)
IPC_MESSAGE_HANDLER(PluginHostMsg_InitiateHTTPRangeRequest,
OnInitiateHTTPRangeRequest)
IPC_MESSAGE_HANDLER(PluginHostMsg_DidStartLoading, OnDidStartLoading)
IPC_MESSAGE_HANDLER(PluginHostMsg_DidStopLoading, OnDidStopLoading)
IPC_MESSAGE_HANDLER(PluginHostMsg_DeferResourceLoading,
OnDeferResourceLoading)
IPC_MESSAGE_HANDLER(PluginHostMsg_URLRedirectResponse,
......@@ -1123,7 +1125,8 @@ void WebPluginDelegateProxy::FetchURL(unsigned long resource_id,
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,
......@@ -1135,7 +1138,10 @@ void WebPluginDelegateProxy::FetchURL(unsigned long resource_id,
params.url = url;
params.first_party_for_cookies = first_party_for_cookies;
params.method = method;
params.post_data = post_data;
if (len) {
params.post_data.resize(len);
memcpy(&params.post_data.front(), buf, len);
}
params.referrer = referrer;
params.notify_redirect = notify_redirects;
params.is_plugin_src_load = is_plugin_src_load;
......@@ -1171,6 +1177,14 @@ void WebPluginDelegateProxy::OnInitiateHTTPRangeRequest(
url.c_str(), range_info.c_str(), range_request_id);
}
void WebPluginDelegateProxy::OnDidStartLoading() {
plugin_->DidStartLoading();
}
void WebPluginDelegateProxy::OnDidStopLoading() {
plugin_->DidStopLoading();
}
void WebPluginDelegateProxy::OnDeferResourceLoading(unsigned long resource_id,
bool defer) {
plugin_->SetDeferResourceLoading(resource_id, defer);
......
......@@ -128,7 +128,8 @@ class WebPluginDelegateProxy
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,
......@@ -170,6 +171,8 @@ class WebPluginDelegateProxy
void OnInitiateHTTPRangeRequest(const std::string& url,
const std::string& range_info,
int range_request_id);
void OnDidStartLoading();
void OnDidStopLoading();
void OnDeferResourceLoading(unsigned long resource_id, bool defer);
void OnURLRedirectResponse(bool allow, int resource_id);
void OnCheckIfRunInsecureContent(const GURL& url, bool* result);
......
......@@ -96,9 +96,7 @@ namespace {
class MultiPartResponseClient : public WebURLLoaderClient {
public:
explicit MultiPartResponseClient(WebPluginResourceClient* resource_client)
: resource_client_(resource_client) {
Clear();
}
: byte_range_lower_bound_(0), resource_client_(resource_client) {}
virtual void willSendRequest(
WebURLLoader*, WebURLRequest&, const WebURLResponse&) {}
......@@ -109,17 +107,14 @@ class MultiPartResponseClient : public WebURLLoaderClient {
// response.
virtual void didReceiveResponse(
WebURLLoader*, const WebURLResponse& response) {
int64 instance_size;
int64 byte_range_upper_bound, instance_size;
if (!MultipartResponseDelegate::ReadContentRanges(
response,
&byte_range_lower_bound_,
&byte_range_upper_bound_,
&byte_range_upper_bound,
&instance_size)) {
NOTREACHED();
return;
}
resource_response_ = response;
}
// Receives individual part data from a multipart response.
......@@ -138,18 +133,9 @@ class MultiPartResponseClient : public WebURLLoaderClient {
virtual void didFinishLoading(WebURLLoader*, double finishTime) {}
virtual void didFail(WebURLLoader*, const WebURLError&) {}
void Clear() {
resource_response_.reset();
byte_range_lower_bound_ = 0;
byte_range_upper_bound_ = 0;
}
private:
WebURLResponse resource_response_;
// The lower bound of the byte range.
int64 byte_range_lower_bound_;
// The upper bound of the byte range.
int64 byte_range_upper_bound_;
// The handler for the data.
WebPluginResourceClient* resource_client_;
};
......@@ -947,6 +933,8 @@ void WebPluginImpl::didSendData(WebURLLoader* loader,
void WebPluginImpl::didReceiveResponse(WebURLLoader* loader,
const WebURLResponse& response) {
// TODO(jam): THIS LOGIC IS COPIED IN PluginURLFetcher::OnReceivedResponse
// until kDirectNPAPIRequests is the default and we can remove this old path.
static const int kHttpPartialResponseStatusCode = 206;
static const int kHttpResponseSuccessStatusCode = 200;
......@@ -1081,10 +1069,7 @@ void WebPluginImpl::didFinishLoading(WebURLLoader* loader, double finishTime) {
if (index != multi_part_response_map_.end()) {
delete (*index).second;
multi_part_response_map_.erase(index);
if (render_view_.get()) {
// TODO(darin): Make is_loading_ be a counter!
render_view_->didStopLoading();
}
DidStopLoading();
}
loader->setDefersLoading(true);
WebPluginResourceClient* resource_client = client_info->client;
......@@ -1228,8 +1213,8 @@ void WebPluginImpl::HandleURLRequestInternal(const char* url,
GURL first_party_for_cookies = webframe_->document().firstPartyForCookies();
delegate_->FetchURL(resource_id, notify_id, complete_url,
first_party_for_cookies, method, std::string(buf, len),
referrer, notify_redirects, is_plugin_src_load, 0,
first_party_for_cookies, method, buf, len, referrer,
notify_redirects, is_plugin_src_load, 0,
render_view_->routing_id());
} else {
WebPluginResourceClient* resource_client = delegate_->CreateResourceClient(
......@@ -1333,6 +1318,20 @@ void WebPluginImpl::InitiateHTTPRangeRequest(
load_manually_ ? NO_REFERRER : PLUGIN_SRC, false, false);
}
void WebPluginImpl::DidStartLoading() {
if (render_view_.get()) {
// TODO(darin): Make is_loading_ be a counter!
render_view_->didStartLoading();
}
}
void WebPluginImpl::DidStopLoading() {
if (render_view_.get()) {
// TODO(darin): Make is_loading_ be a counter!
render_view_->didStopLoading();
}
}
void WebPluginImpl::SetDeferResourceLoading(unsigned long resource_id,
bool defer) {
std::vector<ClientInfo>::iterator client_index = clients_.begin();
......@@ -1373,10 +1372,7 @@ bool WebPluginImpl::HandleHttpMultipartResponse(
return false;
}
if (render_view_.get()) {
// TODO(darin): Make is_loading_ be a counter!
render_view_->didStartLoading();
}
DidStartLoading();
MultiPartResponseClient* multi_part_response_client =
new MultiPartResponseClient(client);
......
......@@ -125,6 +125,8 @@ class WebPluginImpl : public WebPlugin,
virtual void InitiateHTTPRangeRequest(const char* url,
const char* range_info,
int pending_request_id) OVERRIDE;
virtual void DidStartLoading() OVERRIDE;
virtual void DidStopLoading() OVERRIDE;
virtual bool IsOffTheRecord() OVERRIDE;
virtual void SetDeferResourceLoading(unsigned long resource_id,
bool defer) OVERRIDE;
......
This diff is collapsed.
......@@ -7,10 +7,12 @@
#include "base/memory/ref_counted.h"
#include "third_party/WebKit/public/platform/WebURLLoader.h"
#include "webkit/child/webkit_child_export.h"
namespace webkit_glue {
class WebKitPlatformSupportImpl;
struct ResourceResponseInfo;
class WebURLLoaderImpl : public WebKit::WebURLLoader {
public:
......@@ -19,6 +21,10 @@ class WebURLLoaderImpl : public WebKit::WebURLLoader {
static WebKit::WebURLError CreateError(const WebKit::WebURL& unreachable_url,
int reason);
WEBKIT_CHILD_EXPORT static void PopulateURLResponse(
const GURL& url,
const ResourceResponseInfo& info,
WebKit::WebURLResponse* response);
// WebURLLoader methods:
virtual void loadSynchronously(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment