summaryrefslogtreecommitdiffstats
path: root/webkit/glue/resource_handle_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'webkit/glue/resource_handle_win.cc')
-rw-r--r--webkit/glue/resource_handle_win.cc764
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