diff options
Diffstat (limited to 'webkit/glue/resource_handle_win.cc')
-rw-r--r-- | webkit/glue/resource_handle_win.cc | 764 |
1 files changed, 764 insertions, 0 deletions
diff --git a/webkit/glue/resource_handle_win.cc b/webkit/glue/resource_handle_win.cc new file mode 100644 index 0000000..1646f0a --- /dev/null +++ b/webkit/glue/resource_handle_win.cc @@ -0,0 +1,764 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This file replaces WebCore/platform/network/win/ResourceHandleWin.cpp with a +// platform-neutral implementation that simply defers almost entirely to +// ResouceLoaderBridge. +// +// This uses the same ResourceHandle.h header file that the rest of WebKit +// uses, allowing us to avoid complicated changes. Our specific things are +// added on ResourceHandleInternal. The ResourceHandle owns the +// ResourceHandleInternal and passes off almost all processing to it. +// +// The WebKit version of this code keeps the ResourceHandle AddRef'd when +// there are any callbacks. This prevents the callbacks from occuring into +// destroyed objects. However, our destructors should always stop callbacks +// from happening, making this (hopefully) unnecessary. +// +// We preserve this behavior for safety. A client could count on this behavior +// and fire off a request, release it, and wait for callbacks to get the data +// as long as it doesn't care about canceling the request. Although this is +// dumb, we support it. We use pending_ to indicate this extra AddRef, which +// is done in start() and released in OnCompletedRequest. + +#include "config.h" + +#pragma warning(push, 0) +#include "CString.h" +#include "DocLoader.h" +#include "FormData.h" +#include "FrameLoader.h" +#include "LogWin.h" +#include "Page.h" +#include "ResourceError.h" +#include "ResourceHandle.h" +#include "ResourceHandleClient.h" +#include "ResourceHandleWin.h" // for Platform{Response,Data}Struct +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#pragma warning(pop) + +#undef LOG +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/time.h" +#include "base/string_util.h" +#include "base/string_tokenizer.h" +#include "webkit/glue/feed_preview.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/multipart_response_delegate.h" +#include "webkit/glue/resource_loader_bridge.h" +#include "webkit/glue/webframe_impl.h" +#include "net/base/data_url.h" +#include "net/base/net_errors.h" +#include "net/base/net_util.h" +#include "net/base/load_flags.h" + +using webkit_glue::ResourceLoaderBridge; +using net::HttpResponseHeaders; + +namespace { + +// Extracts the information from a data: url. +bool GetInfoFromDataUrl(const GURL& url, + ResourceLoaderBridge::ResponseInfo* info, + std::string* data, URLRequestStatus* status) { + std::string mime_type; + std::string charset; + if (DataURL::Parse(url, &mime_type, &charset, data)) { + info->request_time = Time::Now(); + info->response_time = Time::Now(); + info->mime_type.swap(mime_type); + info->charset.swap(charset); + *status = URLRequestStatus(URLRequestStatus::SUCCESS, 0); + return true; + } + + *status = URLRequestStatus(URLRequestStatus::FAILED, net::ERR_INVALID_URL); + return false; +} + +} // namespace + +namespace WebCore { + +static void ExtractInfoFromHeaders(const HttpResponseHeaders* headers, + HTTPHeaderMap* header_map, + int* status_code, + String* status_text, + long long* expected_content_length) { + *status_code = headers->response_code(); + + // Set the status text (the returned status line is normalized). + const std::string& status = headers->GetStatusLine(); + StringTokenizer status_tokenizer(status, " "); + if (status_tokenizer.GetNext() && // identifies "HTTP/1.1" + status_tokenizer.GetNext() && // identifies "200" + status_tokenizer.GetNext()) // identifies first word of status text + *status_text = webkit_glue::StdStringToString( + std::string(status_tokenizer.token_begin(), status.end())); + + // Set the content length. + std::string length_val; + if (headers->EnumerateHeader(NULL, "content-length", &length_val)) + *expected_content_length = StringToInt64(length_val); + + // Build up the header map. Take care with duplicate headers. + void* iter = NULL; + std::string name, value; + while (headers->EnumerateHeaderLines(&iter, &name, &value)) { + String name_str = webkit_glue::StdStringToString(name); + String value_str = webkit_glue::StdStringToString(value); + + pair<HTTPHeaderMap::iterator, bool> result = + header_map->add(name_str, value_str); + if (!result.second) + result.first->second += ", " + value_str; + } +} + +static ResourceResponse MakeResourceResponse( + const KURL& kurl, + const ResourceLoaderBridge::ResponseInfo& info) { + int status_code = 0; + long long expected_content_length = info.content_length; + String status_text; + HTTPHeaderMap header_map; + + // It's okay if there are no headers + if (info.headers) + ExtractInfoFromHeaders(info.headers, + &header_map, + &status_code, + &status_text, + &expected_content_length); + + // TODO(darin): We should leverage HttpResponseHeaders for this, and this + // should be using the same code as ResourceDispatcherHost. + std::wstring suggested_filename; + if (info.headers) { + std::string disp_val; + if (info.headers->EnumerateHeader(NULL, "content-disposition", &disp_val)) { + suggested_filename = net_util::GetSuggestedFilename( + webkit_glue::KURLToGURL(kurl), disp_val, std::wstring()); + } + } + + ResourceResponse response(kurl, + webkit_glue::StdStringToString(info.mime_type), + expected_content_length, + webkit_glue::StdStringToString(info.charset), + webkit_glue::StdWStringToString(suggested_filename)); + + if (info.headers) { + Time time_val; + if (info.headers->GetLastModifiedValue(&time_val)) + response.setLastModifiedDate(time_val.ToTimeT()); + + // Compute expiration date + TimeDelta freshness_lifetime = + info.headers->GetFreshnessLifetime(info.response_time); + if (freshness_lifetime != TimeDelta()) { + Time now = Time::Now(); + TimeDelta current_age = + info.headers->GetCurrentAge(info.request_time, info.response_time, + now); + time_val = now + freshness_lifetime - current_age; + + response.setExpirationDate(time_val.ToTimeT()); + } else { + // WebKit uses 0 as a special expiration date that means never expire. + // 1 is a small enough value to let it always expire. + response.setExpirationDate(1); + } + } + + response.setHTTPStatusCode(status_code); + response.setHTTPStatusText(status_text); + response.setSecurityInfo(webkit_glue::StdStringToCString(info.security_info)); + + // WebKit doesn't provide a way for us to set expected content length after + // calling the constructor, so we parse the headers first and then swap in + // our HTTP header map. Ideally we would like a setter for expected content + // length (perhaps by abstracting ResourceResponse interface into + // ResourceResponseBase) but that would require forking. + const_cast<HTTPHeaderMap*>(&response.httpHeaderFields())->swap(header_map); + + return response; +} + +class ResourceHandleInternal : public ResourceLoaderBridge::Peer { + public: + ResourceHandleInternal(ResourceHandle* job, const ResourceRequest& r, + ResourceHandleClient* c); + ~ResourceHandleInternal(); + + // If the response parameter is null, then an asynchronous load is started. + bool Start(ResourceLoaderBridge::SyncLoadResponse* response); + + // Used to cancel an asynchronous load. + void Cancel(); + + // Used to suspend/resume an asynchronous load. + void SetDefersLoading(bool value); + + // ResourceLoaderBridge::Peer implementation + virtual void OnReceivedRedirect(const GURL& new_url); + virtual void OnReceivedResponse( + const ResourceLoaderBridge::ResponseInfo& info); + virtual void OnReceivedData(const char* data, int len); + virtual void OnCompletedRequest(const URLRequestStatus& status); + virtual std::string GetURLForDebugging(); + + // Handles a data: url internally instead of calling the bridge. + void HandleDataUrl(); + + // This is the bridge implemented by the embedder. + // The bridge is kept alive as long as the request is valid and we + // are ready for callbacks. + scoped_ptr<ResourceLoaderBridge> bridge_; + + // The resource loader that owns us + ResourceHandle* job_; + + // This is the object that receives various status messages (such as when the + // loader has received data). See definition for the exact messages that are + // sent to it. + ResourceHandleClient* client_; + + ResourceRequest request_; + + // Runnable Method Factory used to invoke later HandleDataUrl(). + ScopedRunnableMethodFactory<ResourceHandleInternal> data_url_factory_; + + int load_flags_; + + private: + // Set to true when we're waiting for data from the bridge, also indicating + // we have addrefed our job. + bool pending_; + + // Expected content length of the response + long long expected_content_length_; + + // NULL unless we are handling a multipart/x-mixed-replace request + scoped_ptr<MultipartResponseDelegate> multipart_delegate_; + + // NULL unless we are handling a feed:// request. + scoped_ptr<FeedClientProxy> feed_client_proxy_; +}; + +ResourceHandleInternal::ResourceHandleInternal(ResourceHandle* job, + const ResourceRequest& r, + ResourceHandleClient* c) + : job_(job), + client_(c), + request_(r), + load_flags_(net::LOAD_NORMAL), + pending_(false), + expected_content_length_(-1), + multipart_delegate_(NULL), +#pragma warning(suppress: 4355) // can use this + data_url_factory_(this) { +} + +ResourceHandleInternal::~ResourceHandleInternal() { + DCHECK(!pending_); +} + +void ResourceHandleInternal::HandleDataUrl() { + ResourceLoaderBridge::ResponseInfo info; + URLRequestStatus status; + std::string data; + + if (GetInfoFromDataUrl(webkit_glue::KURLToGURL(request_.url()), &info, &data, + &status)) { + OnReceivedResponse(info); + + if (data.size()) + OnReceivedData(data.c_str(), data.size()); + } + + OnCompletedRequest(status); + + // We are done using the object. ResourceHandle and ResourceHandleInternal + // might be destroyed now. + job_->deref(); +} + +bool ResourceHandleInternal::Start( + ResourceLoaderBridge::SyncLoadResponse* sync_load_response) { + DCHECK(!bridge_.get()); + + // The WebFrame is the Frame's FrameWinClient + WebFrameImpl* webframe = + request_.frame() ? WebFrameImpl::FromFrame(request_.frame()) : NULL; + + CString method = request_.httpMethod().latin1(); + GURL referrer(webkit_glue::StringToStdWString(request_.httpReferrer())); + + // Compute the URL of the load. + GURL url = webkit_glue::KURLToGURL(request_.url()); + if (url.SchemeIs("feed:")) { + // Feed URLs are special, they actually mean "http". + url_canon::Replacements<char> replacements; + replacements.SetScheme("http", url_parse::Component(0, 4)); + url = url.ReplaceComponents(replacements); + + // Replace our client with a client that understands previewing feeds + // and forwards the feeds along to the original client. + feed_client_proxy_.reset(new FeedClientProxy(client_)); + client_ = feed_client_proxy_.get(); + } + + // Inherit the policy URL from the request's frame. However, if the request + // is for a main frame, the current document's policyBaseURL is the old + // document, so we leave policyURL empty to indicate that the request is a + // first-party request. + GURL policy_url; + if (request_.resourceType() != ResourceType::MAIN_FRAME && + request_.frame() && request_.frame()->document()) { + policy_url = GURL(webkit_glue::StringToStdWString( + request_.frame()->document()->policyBaseURL())); + } + + // Translate the table of request headers to a formatted string blob + String headerBuf; + const HTTPHeaderMap& headerMap = request_.httpHeaderFields(); + + // In some cases, WebCore doesn't add an Accept header, but not having the + // header confuses some web servers. See bug 808613. + // Note: headerMap uses case-insenstive keys, so this will find Accept as + // as well. + if (!headerMap.contains("accept")) + request_.addHTTPHeaderField("Accept", "*/*"); + + const String crlf(L"\r\n"); + const String sep(L": "); + for (HTTPHeaderMap::const_iterator it = headerMap.begin(); + it != headerMap.end(); ++it) { + // Skip over referrer headers found in the header map because we already + // pulled it out as a separate parameter. We likewise prune the UA since + // that will be added back by the network layer. + if (equalIgnoringCase((*it).first, "referer") || + equalIgnoringCase((*it).first, "user-agent")) + continue; + // WinInet dies if blank headers are set. TODO(darin): Is this still an + // issue now that we are using WinHTTP? + if ((*it).first.isEmpty()) { + webframe->frame()->page()->chrome()->addMessageToConsole( + JSMessageSource, + ErrorMessageLevel, + "Refused to set blank header", + 1, + String()); + continue; + } + if (!headerBuf.isEmpty()) + headerBuf.append(crlf); + headerBuf.append((*it).first + sep + (*it).second); + } + + switch (request_.cachePolicy()) { + case ReloadIgnoringCacheData: + // Required by LayoutTests/http/tests/misc/refresh-headers.php + load_flags_ |= net::LOAD_VALIDATE_CACHE; + break; + case ReturnCacheDataElseLoad: + load_flags_ |= net::LOAD_PREFERRING_CACHE; + break; + case ReturnCacheDataDontLoad: + load_flags_ |= net::LOAD_ONLY_FROM_CACHE; + break; + } + + // TODO(jcampan): in the non out-of-process plugin case the request does not + // have a origin_pid. Find a better place to set this. + int origin_pid = request_.originPid(); + if (origin_pid == 0) + origin_pid = ::GetCurrentProcessId(); + + bool mixed_content = + webkit_glue::KURLToGURL(request_.mainDocumentURL()).SchemeIsSecure() && + !url.SchemeIsSecure(); + + if (url.SchemeIs("data")) { + if (sync_load_response) { + // This is a sync load. Do the work now. + sync_load_response->url = url; + std::string data; + GetInfoFromDataUrl(sync_load_response->url, sync_load_response, + &sync_load_response->data, + &sync_load_response->status); + } else { + pending_ = true; + job_->ref(); // to be released when we get a OnCompletedRequest. + job_->ref(); // to be released when HandleDataUrl is completed. + MessageLoop::current()->PostTask(FROM_HERE, + data_url_factory_.NewRunnableMethod( + &ResourceHandleInternal::HandleDataUrl)); + } + return true; + } + + // TODO(darin): is latin1 really correct here? It is if the strings are + // already ASCII (i.e., if they are already escaped properly). + // TODO(brettw) this should take parameter encoding into account when + // creating the GURLs. + bridge_.reset(ResourceLoaderBridge::Create( + webframe, + webkit_glue::CStringToStdString(method), + url, + policy_url, + referrer, + webkit_glue::CStringToStdString(headerBuf.latin1()), + load_flags_, + origin_pid, + request_.resourceType(), + mixed_content)); + if (!bridge_.get()) + return false; + + if (request_.httpBody()) { + // GET and HEAD requests shouldn't have http bodies. + DCHECK(method != "GET" && method != "HEAD"); + const Vector<FormDataElement>& elements = request_.httpBody()->elements(); + size_t n = elements.size(); + for (size_t i = 0; i < n; ++i) { + const FormDataElement& e = elements[static_cast<unsigned>(i)]; + if (e.m_type == FormDataElement::data) { + if (e.m_data.size() > 0) { + // WebKit sometimes gives up empty data to append. These aren't + // necessary so we just optimize those out here. + bridge_->AppendDataToUpload(e.m_data.data(), + static_cast<int>(e.m_data.size())); + } + } else { + bridge_->AppendFileToUpload( + webkit_glue::StringToStdWString(e.m_filename)); + } + } + } + + if (sync_load_response) { + bridge_->SyncLoad(sync_load_response); + return true; + } + + bool rv = bridge_->Start(this); + if (rv) { + pending_ = true; + job_->ref(); // to be released when we get a OnCompletedRequest. + } else { + bridge_.reset(); + } + + return rv; +} + +void ResourceHandleInternal::Cancel() { + // The bridge will still send OnCompletedRequest, which will deref() us, + // so we don't do that here. + if (bridge_.get()) + bridge_->Cancel(); + + // Ensure that we do not notify the multipart delegate anymore as it has + // its own pointer to the client. + multipart_delegate_.reset(); + + // Do not make any further calls to the client. + client_ = NULL; +} + +void ResourceHandleInternal::SetDefersLoading(bool value) { + if (bridge_.get()) + bridge_->SetDefersLoading(value); +} + +// ResourceLoaderBridge::Peer impl -------------------------------------------- + +void ResourceHandleInternal::OnReceivedRedirect(const GURL& new_url) { + DCHECK(pending_); + + KURL url = webkit_glue::GURLToKURL(new_url); + + // TODO(darin): need a way to properly initialize a ResourceResponse + ResourceResponse response(request_.url(), String(), -1, String(), String()); + + ResourceRequest new_request(url); + + // TODO(darin): we need to setup new_request to reflect the fact that we + // for example drop the httpBody when following a POST request that is + // redirected to a GET request. + + if (client_) + client_->willSendRequest(job_, new_request, response); + + // + // TODO(darin): since new_request is sent as a mutable reference, it is + // possible that willSendRequest may expect to be able to modify it. + // + // andresca on #webkit confirms that that is intentional, so we'll need + // to rework the ResourceLoaderBridge to give us control over what URL + // is really loaded (and with what headers) when a redirect is encountered. + // + + request_ = new_request; +} + +void ResourceHandleInternal::OnReceivedResponse( + const ResourceLoaderBridge::ResponseInfo& info) { + DCHECK(pending_); + + // TODO(darin): need a way to properly initialize a ResourceResponse + ResourceResponse response = MakeResourceResponse(request_.url(), info); + + expected_content_length_ = response.expectedContentLength(); + + if (client_) + client_->didReceiveResponse(job_, response); + + // we may have been cancelled after didReceiveResponse, which would leave us + // without a client and therefore without much need to do multipart handling. + + DCHECK(!multipart_delegate_.get()); + if (client_ && info.headers && response.isMultipart()) { + std::string content_type; + info.headers->EnumerateHeader(NULL, "content-type", &content_type); + + std::string boundary = net_util::GetHeaderParamValue(content_type, + "boundary"); + TrimString(boundary, " \"", &boundary); + // If there's no boundary, just handle the request normally. In the gecko + // code, nsMultiMixedConv::OnStartRequest throws an exception. + if (!boundary.empty()) { + multipart_delegate_.reset(new MultipartResponseDelegate(client_, job_, + response, boundary)); + } + } + + // TODO(darin): generate willCacheResponse callback. debug mac webkit to + // determine when it should be called. +} + +void ResourceHandleInternal::OnReceivedData(const char* data, int data_len) { + DCHECK(pending_); + + if (client_) { + // TODO(darin): figure out what to pass for lengthReceived. from reading + // the loader code, it looks like this is supposed to be the content-length + // value, but it seems really wacky to include that here! we have to debug + // webkit on mac to figure out what this should be. + + // TODO(jackson): didReceiveData expects an int, but an expected content + // length is an int64, so we do our best to fit it inside an int. The only + // code that cares currently about this value is the Inspector, so beware + // that the Inspector's network panel might under-represent the size of + // some resources if they're larger than a gigabyte. + int lengthReceived = static_cast<int>(expected_content_length_); + if (lengthReceived != expected_content_length_) // overflow occurred + lengthReceived = -1; + + if (!multipart_delegate_.get()) { + client_->didReceiveData(job_, data, data_len, lengthReceived); + } else { + // AddData will make the appropriate calls to client_->didReceiveData + // and client_->didReceiveResponse + multipart_delegate_->OnReceivedData(data, data_len); + } + } +} + +void ResourceHandleInternal::OnCompletedRequest( + const URLRequestStatus& status) { + if (multipart_delegate_.get()) { + multipart_delegate_->OnCompletedRequest(); + multipart_delegate_.reset(NULL); + } + + pending_ = false; + + if (client_) { + if (status.status() != URLRequestStatus::SUCCESS) { + int error_code; + if (status.status() == URLRequestStatus::HANDLED_EXTERNALLY) { + // By marking this request as aborted we insure that we don't navigate + // to an error page. + error_code = net::ERR_ABORTED; + } else { + error_code = status.os_error(); + } + // TODO(tc): fill in these fields properly + ResourceError error(net::kErrorDomain, + error_code, + request_.url().string(), + String() /*localized description*/); + client_->didFail(job_, error); + } else { + client_->didFinishLoading(job_); + } + } + + job_->deref(); // may destroy our owner and hence |this| +} + +std::string ResourceHandleInternal::GetURLForDebugging() { + return webkit_glue::CStringToStdString(request_.url().string().latin1()); +} + +// ResourceHandle ------------------------------------------------------------- + +ResourceHandle::ResourceHandle(const ResourceRequest& request, + ResourceHandleClient* client, + bool defersLoading, + bool shouldContentSniff, + bool mightDownloadFromHandle) +#pragma warning(suppress: 4355) // it's okay to pass |this| here! + : d(new ResourceHandleInternal(this, request, client)) { + // TODO(darin): figure out what to do with the two bool params +} + +PassRefPtr<ResourceHandle> ResourceHandle::create(const ResourceRequest& request, + ResourceHandleClient* client, + Frame* deprecated, + bool defersLoading, + bool shouldContentSniff, + bool mightDownloadFromHandle) { + RefPtr<ResourceHandle> newHandle( + new ResourceHandle(request, client, defersLoading, shouldContentSniff, + mightDownloadFromHandle)); + + if (newHandle->start(NULL)) + return newHandle.release(); + + return NULL; +} + +const ResourceRequest& ResourceHandle::request() const { + return d->request_; +} + +ResourceHandleClient* ResourceHandle::client() const { + return d->client_; +} + +void ResourceHandle::setClient(ResourceHandleClient* client) { + d->client_ = client; +} + +void ResourceHandle::setDefersLoading(bool value) { + d->SetDefersLoading(value); +} + +bool ResourceHandle::start(Frame* deprecated) { + return d->Start(NULL); +} + +void ResourceHandle::clearAuthentication() { + // TODO(darin): do something here. it looks like the ResourceLoader calls + // this method when it is canceled. i have no idea why it does this. +} + +void ResourceHandle::cancel() { + d->Cancel(); +} + +ResourceHandle::~ResourceHandle() { +} + +PassRefPtr<SharedBuffer> ResourceHandle::bufferedData() { + return NULL; +} + +/*static*/ bool ResourceHandle::loadsBlocked() { + return false; // this seems to be related to sync XMLHttpRequest... +} + +/*static*/ bool ResourceHandle::supportsBufferedData() { + return false; // the loader will buffer manually if it needs to +} + +/*static*/ void ResourceHandle::loadResourceSynchronously( + const ResourceRequest& request, ResourceError& error, + ResourceResponse& response, Vector<char>& data, Frame*) { + + RefPtr<ResourceHandle> handle = + new ResourceHandle(request, NULL, false, false, false); + + ResourceLoaderBridge::SyncLoadResponse sync_load_response; + if (!handle->d->Start(&sync_load_response)) { + // TODO(darin): what should the error code really be? + error = ResourceError(net::kErrorDomain, + net::ERR_FAILED, + request.url().string(), + String() /* localized description */); + return; + } + + KURL kurl = webkit_glue::GURLToKURL(sync_load_response.url); + + // TODO(tc): If there's an error during the load, we don't set the response. + // For file loads, we may want to include a more descriptive status code or + // status text. + const URLRequestStatus::Status& status = sync_load_response.status.status(); + if (status != URLRequestStatus::SUCCESS && + status != URLRequestStatus::HANDLED_EXTERNALLY) { + error = ResourceError(net::kErrorDomain, + sync_load_response.status.os_error(), + kurl.string(), + String() /* localized description */); + return; + } + + response = MakeResourceResponse(kurl, sync_load_response); + + data.clear(); + data.append(sync_load_response.data.data(), + sync_load_response.data.size()); +} + +// static +bool ResourceHandle::willLoadFromCache(ResourceRequest& request) { + // + // This method is used to determine if a POST request can be repeated from + // cache, but you cannot really know until you actually try to read from the + // cache. Even if we checked now, something else could come along and wipe + // out the cache entry by the time we fetch it. + // + // So, we always say yes here, which allows us to generate an ERR_CACHE_MISS + // if the request cannot be serviced from cache. We force the 'DontLoad' + // cache policy at this point to ensure that we never hit the network for + // this request. + // + DCHECK(request.httpMethod() == "POST"); + request.setCachePolicy(ReturnCacheDataDontLoad); + return true; +} + +} // namespace WebCore |