blob: dc11e91d6fdd2d726a83e35c2b7b3274d06f080b [file] [log] [blame]
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This file contains an implementation of the ResourceLoaderBridge class.
// The class is implemented using URLRequest, meaning it is a "simple" version
// that directly issues requests. The more complicated one used in the
// browser uses IPC.
//
// Because URLRequest only provides an asynchronous resource loading API, this
// file makes use of URLRequest from a background IO thread. Requests for
// cookies and synchronously loaded resources result in the main thread of the
// application blocking until the IO thread completes the operation. (See
// GetCookies and SyncLoad)
//
// Main thread IO thread
// ----------- ---------
// ResourceLoaderBridge <---o---------> RequestProxy (normal case)
// \ -> URLRequest
// o-------> SyncRequestProxy (synchronous case)
// -> URLRequest
// SetCookie <------------------------> CookieSetter
// -> net_util::SetCookie
// GetCookies <-----------------------> CookieGetter
// -> net_util::GetCookies
//
// NOTE: The implementation in this file may be used to have WebKit fetch
// resources in-process. For example, it is handy for building a single-
// process WebKit embedding (e.g., test_shell) that can use URLRequest to
// perform URL loads. See renderer/resource_dispatcher.h for details on an
// alternate implementation that defers fetching to another process.
#include "webkit/tools/test_shell/simple_resource_loader_bridge.h"
#include "base/file_path.h"
#include "base/logging.h"
#include "base/message_loop.h"
#if defined(OS_MACOSX) || defined(OS_WIN)
#include "base/nss_util.h"
#endif
#include "base/ref_counted.h"
#include "base/time.h"
#include "base/timer.h"
#include "base/thread.h"
#include "base/waitable_event.h"
#include "net/base/cookie_store.h"
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
#include "net/base/static_cookie_policy.h"
#include "net/base/upload_data.h"
#include "net/http/http_cache.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "net/proxy/proxy_service.h"
#if defined(OS_WIN)
#include "net/socket/ssl_client_socket_nss_factory.h"
#endif
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_job.h"
#include "webkit/appcache/appcache_interfaces.h"
#include "webkit/blob/blob_storage_controller.h"
#include "webkit/blob/blob_url_request_job.h"
#include "webkit/glue/resource_loader_bridge.h"
#include "webkit/tools/test_shell/simple_appcache_system.h"
#include "webkit/tools/test_shell/simple_socket_stream_bridge.h"
#include "webkit/tools/test_shell/test_shell_request_context.h"
#include "webkit/tools/test_shell/test_shell_webblobregistry_impl.h"
using webkit_glue::ResourceLoaderBridge;
using net::StaticCookiePolicy;
using net::HttpResponseHeaders;
namespace {
struct TestShellRequestContextParams {
TestShellRequestContextParams(
const FilePath& in_cache_path,
net::HttpCache::Mode in_cache_mode,
bool in_no_proxy)
: cache_path(in_cache_path),
cache_mode(in_cache_mode),
no_proxy(in_no_proxy),
accept_all_cookies(false) {}
FilePath cache_path;
net::HttpCache::Mode cache_mode;
bool no_proxy;
bool accept_all_cookies;
};
static URLRequestJob* BlobURLRequestJobFactory(URLRequest* request,
const std::string& scheme) {
webkit_blob::BlobStorageController* blob_storage_controller =
static_cast<TestShellRequestContext*>(request->context())->
blob_storage_controller();
return new webkit_blob::BlobURLRequestJob(
request,
blob_storage_controller->GetBlobDataFromUrl(request->url()),
NULL);
}
TestShellRequestContextParams* g_request_context_params = NULL;
URLRequestContext* g_request_context = NULL;
base::Thread* g_cache_thread = NULL;
//-----------------------------------------------------------------------------
class IOThread : public base::Thread {
public:
IOThread() : base::Thread("IOThread") {
}
~IOThread() {
// We cannot rely on our base class to stop the thread since we want our
// CleanUp function to run.
Stop();
}
virtual void Init() {
if (g_request_context_params) {
g_request_context = new TestShellRequestContext(
g_request_context_params->cache_path,
g_request_context_params->cache_mode,
g_request_context_params->no_proxy);
SetAcceptAllCookies(g_request_context_params->accept_all_cookies);
delete g_request_context_params;
g_request_context_params = NULL;
} else {
g_request_context = new TestShellRequestContext();
SetAcceptAllCookies(false);
}
g_request_context->AddRef();
SimpleAppCacheSystem::InitializeOnIOThread(g_request_context);
SimpleSocketStreamBridge::InitializeOnIOThread(g_request_context);
TestShellWebBlobRegistryImpl::InitializeOnIOThread(
static_cast<TestShellRequestContext*>(g_request_context)->
blob_storage_controller());
URLRequest::RegisterProtocolFactory("blob", &BlobURLRequestJobFactory);
}
virtual void CleanUp() {
SimpleSocketStreamBridge::Cleanup();
TestShellWebBlobRegistryImpl::Cleanup();
if (g_request_context) {
g_request_context->Release();
g_request_context = NULL;
}
}
void SetAcceptAllCookies(bool accept_all_cookies) {
StaticCookiePolicy::Type policy_type = accept_all_cookies ?
StaticCookiePolicy::ALLOW_ALL_COOKIES :
StaticCookiePolicy::BLOCK_THIRD_PARTY_COOKIES;
static_cast<StaticCookiePolicy*>(g_request_context->cookie_policy())->
set_type(policy_type);
}
};
IOThread* g_io_thread = NULL;
//-----------------------------------------------------------------------------
struct RequestParams {
std::string method;
GURL url;
GURL first_party_for_cookies;
GURL referrer;
std::string headers;
int load_flags;
ResourceType::Type request_type;
int appcache_host_id;
scoped_refptr<net::UploadData> upload;
};
// The interval for calls to RequestProxy::MaybeUpdateUploadProgress
static const int kUpdateUploadProgressIntervalMsec = 100;
// The RequestProxy does most of its work on the IO thread. The Start and
// Cancel methods are proxied over to the IO thread, where an URLRequest object
// is instantiated.
class RequestProxy : public URLRequest::Delegate,
public base::RefCountedThreadSafe<RequestProxy> {
public:
// Takes ownership of the params.
RequestProxy()
: buf_(new net::IOBuffer(kDataSize)),
last_upload_position_(0) {
}
void DropPeer() {
peer_ = NULL;
}
void Start(ResourceLoaderBridge::Peer* peer, RequestParams* params) {
peer_ = peer;
owner_loop_ = MessageLoop::current();
// proxy over to the io thread
g_io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
this, &RequestProxy::AsyncStart, params));
}
void Cancel() {
// proxy over to the io thread
g_io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
this, &RequestProxy::AsyncCancel));
}
protected:
friend class base::RefCountedThreadSafe<RequestProxy>;
virtual ~RequestProxy() {
// If we have a request, then we'd better be on the io thread!
DCHECK(!request_.get() ||
MessageLoop::current() == g_io_thread->message_loop());
}
// --------------------------------------------------------------------------
// The following methods are called on the owner's thread in response to
// various URLRequest callbacks. The event hooks, defined below, trigger
// these methods asynchronously.
void NotifyReceivedRedirect(const GURL& new_url,
const ResourceLoaderBridge::ResponseInfo& info) {
bool has_new_first_party_for_cookies = false;
GURL new_first_party_for_cookies;
if (peer_ && peer_->OnReceivedRedirect(new_url, info,
&has_new_first_party_for_cookies,
&new_first_party_for_cookies)) {
g_io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
this, &RequestProxy::AsyncFollowDeferredRedirect,
has_new_first_party_for_cookies, new_first_party_for_cookies));
} else {
Cancel();
}
}
void NotifyReceivedResponse(const ResourceLoaderBridge::ResponseInfo& info,
bool content_filtered) {
if (peer_)
peer_->OnReceivedResponse(info, content_filtered);
}
void NotifyReceivedData(int bytes_read) {
if (!peer_)
return;
// Make a local copy of buf_, since AsyncReadData reuses it.
scoped_array<char> buf_copy(new char[bytes_read]);
memcpy(buf_copy.get(), buf_->data(), bytes_read);
// Continue reading more data into buf_
// Note: Doing this before notifying our peer ensures our load events get
// dispatched in a manner consistent with DumpRenderTree (and also avoids a
// race condition). If the order of the next 2 functions were reversed, the
// peer could generate new requests in reponse to the received data, which
// when run on the io thread, could race against this function in doing
// another InvokeLater. See bug 769249.
g_io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
this, &RequestProxy::AsyncReadData));
peer_->OnReceivedData(buf_copy.get(), bytes_read);
}
void NotifyCompletedRequest(const URLRequestStatus& status,
const std::string& security_info) {
if (peer_) {
peer_->OnCompletedRequest(status, security_info);
DropPeer(); // ensure no further notifications
}
}
void NotifyUploadProgress(uint64 position, uint64 size) {
if (peer_)
peer_->OnUploadProgress(position, size);
}
// --------------------------------------------------------------------------
// The following methods are called on the io thread. They correspond to
// actions performed on the owner's thread.
void AsyncStart(RequestParams* params) {
// Might need to resolve the blob references in the upload data.
if (params->upload) {
static_cast<TestShellRequestContext*>(g_request_context)->
blob_storage_controller()->ResolveBlobReferencesInUploadData(
params->upload.get());
}
request_.reset(new URLRequest(params->url, this));
request_->set_method(params->method);
request_->set_first_party_for_cookies(params->first_party_for_cookies);
request_->set_referrer(params->referrer.spec());
net::HttpRequestHeaders headers;
headers.AddHeadersFromString(params->headers);
request_->SetExtraRequestHeaders(headers);
request_->set_load_flags(params->load_flags);
request_->set_upload(params->upload.get());
request_->set_context(g_request_context);
SimpleAppCacheSystem::SetExtraRequestInfo(
request_.get(), params->appcache_host_id, params->request_type);
request_->Start();
if (request_->has_upload() &&
params->load_flags & net::LOAD_ENABLE_UPLOAD_PROGRESS) {
upload_progress_timer_.Start(
base::TimeDelta::FromMilliseconds(kUpdateUploadProgressIntervalMsec),
this, &RequestProxy::MaybeUpdateUploadProgress);
}
delete params;
}
void AsyncCancel() {
// This can be null in cases where the request is already done.
if (!request_.get())
return;
request_->Cancel();
Done();
}
void AsyncFollowDeferredRedirect(bool has_new_first_party_for_cookies,
const GURL& new_first_party_for_cookies) {
// This can be null in cases where the request is already done.
if (!request_.get())
return;
if (has_new_first_party_for_cookies)
request_->set_first_party_for_cookies(new_first_party_for_cookies);
request_->FollowDeferredRedirect();
}
void AsyncReadData() {
// This can be null in cases where the request is already done.
if (!request_.get())
return;
if (request_->status().is_success()) {
int bytes_read;
if (request_->Read(buf_, kDataSize, &bytes_read) && bytes_read) {
OnReceivedData(bytes_read);
} else if (!request_->status().is_io_pending()) {
Done();
} // else wait for OnReadCompleted
} else {
Done();
}
}
// --------------------------------------------------------------------------
// The following methods are event hooks (corresponding to URLRequest
// callbacks) that run on the IO thread. They are designed to be overridden
// by the SyncRequestProxy subclass.
virtual void OnReceivedRedirect(
const GURL& new_url,
const ResourceLoaderBridge::ResponseInfo& info,
bool* defer_redirect) {
*defer_redirect = true; // See AsyncFollowDeferredRedirect
owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
this, &RequestProxy::NotifyReceivedRedirect, new_url, info));
}
virtual void OnReceivedResponse(
const ResourceLoaderBridge::ResponseInfo& info,
bool content_filtered) {
owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
this, &RequestProxy::NotifyReceivedResponse, info, content_filtered));
}
virtual void OnReceivedData(int bytes_read) {
owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
this, &RequestProxy::NotifyReceivedData, bytes_read));
}
virtual void OnCompletedRequest(const URLRequestStatus& status,
const std::string& security_info) {
owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
this, &RequestProxy::NotifyCompletedRequest, status, security_info));
}
// --------------------------------------------------------------------------
// URLRequest::Delegate implementation:
virtual void OnReceivedRedirect(URLRequest* request,
const GURL& new_url,
bool* defer_redirect) {
DCHECK(request->status().is_success());
ResourceLoaderBridge::ResponseInfo info;
PopulateResponseInfo(request, &info);
OnReceivedRedirect(new_url, info, defer_redirect);
}
virtual void OnResponseStarted(URLRequest* request) {
if (request->status().is_success()) {
ResourceLoaderBridge::ResponseInfo info;
PopulateResponseInfo(request, &info);
OnReceivedResponse(info, false);
AsyncReadData(); // start reading
} else {
Done();
}
}
virtual void OnSSLCertificateError(URLRequest* request,
int cert_error,
net::X509Certificate* cert) {
// Allow all certificate errors.
request->ContinueDespiteLastError();
}
virtual void OnReadCompleted(URLRequest* request, int bytes_read) {
if (request->status().is_success() && bytes_read > 0) {
OnReceivedData(bytes_read);
} else {
Done();
}
}
// --------------------------------------------------------------------------
// Helpers and data:
void Done() {
if (upload_progress_timer_.IsRunning()) {
MaybeUpdateUploadProgress();
upload_progress_timer_.Stop();
}
DCHECK(request_.get());
OnCompletedRequest(request_->status(), std::string());
request_.reset(); // destroy on the io thread
}
// Called on the IO thread.
void MaybeUpdateUploadProgress() {
// If a redirect is received upload is cancelled in URLRequest, we should
// try to stop the |upload_progress_timer_| timer and return.
if (!request_->has_upload()) {
if (upload_progress_timer_.IsRunning())
upload_progress_timer_.Stop();
return;
}
uint64 size = request_->get_upload()->GetContentLength();
uint64 position = request_->GetUploadProgress();
if (position == last_upload_position_)
return; // no progress made since last time
const uint64 kHalfPercentIncrements = 200;
const base::TimeDelta kOneSecond = base::TimeDelta::FromMilliseconds(1000);
uint64 amt_since_last = position - last_upload_position_;
base::TimeDelta time_since_last = base::TimeTicks::Now() -
last_upload_ticks_;
bool is_finished = (size == position);
bool enough_new_progress = (amt_since_last > (size /
kHalfPercentIncrements));
bool too_much_time_passed = time_since_last > kOneSecond;
if (is_finished || enough_new_progress || too_much_time_passed) {
owner_loop_->PostTask(FROM_HERE, NewRunnableMethod(
this, &RequestProxy::NotifyUploadProgress, position, size));
last_upload_ticks_ = base::TimeTicks::Now();
last_upload_position_ = position;
}
}
void PopulateResponseInfo(URLRequest* request,
ResourceLoaderBridge::ResponseInfo* info) const {
info->request_time = request->request_time();
info->response_time = request->response_time();
info->headers = request->response_headers();
request->GetMimeType(&info->mime_type);
request->GetCharset(&info->charset);
info->content_length = request->GetExpectedContentSize();
SimpleAppCacheSystem::GetExtraResponseInfo(
request,
&info->appcache_id,
&info->appcache_manifest_url);
}
scoped_ptr<URLRequest> request_;
// Size of our async IO data buffers
static const int kDataSize = 16*1024;
// read buffer for async IO
scoped_refptr<net::IOBuffer> buf_;
MessageLoop* owner_loop_;
// This is our peer in WebKit (implemented as ResourceHandleInternal). We do
// not manage its lifetime, and we may only access it from the owner's
// message loop (owner_loop_).
ResourceLoaderBridge::Peer* peer_;
// Timer used to pull upload progress info.
base::RepeatingTimer<RequestProxy> upload_progress_timer_;
// Info used to determine whether or not to send an upload progress update.
uint64 last_upload_position_;
base::TimeTicks last_upload_ticks_;
};
//-----------------------------------------------------------------------------
class SyncRequestProxy : public RequestProxy {
public:
explicit SyncRequestProxy(ResourceLoaderBridge::SyncLoadResponse* result)
: result_(result), event_(true, false) {
}
void WaitForCompletion() {
if (!event_.Wait())
NOTREACHED();
}
// --------------------------------------------------------------------------
// Event hooks that run on the IO thread:
virtual void OnReceivedRedirect(
const GURL& new_url,
const ResourceLoaderBridge::ResponseInfo& info,
bool* defer_redirect) {
// TODO(darin): It would be much better if this could live in WebCore, but
// doing so requires API changes at all levels. Similar code exists in
// WebCore/platform/network/cf/ResourceHandleCFNet.cpp :-(
if (new_url.GetOrigin() != result_->url.GetOrigin()) {
DLOG(WARNING) << "Cross origin redirect denied";
Cancel();
return;
}
result_->url = new_url;
}
virtual void OnReceivedResponse(
const ResourceLoaderBridge::ResponseInfo& info,
bool content_filtered) {
*static_cast<ResourceLoaderBridge::ResponseInfo*>(result_) = info;
}
virtual void OnReceivedData(int bytes_read) {
result_->data.append(buf_->data(), bytes_read);
AsyncReadData(); // read more (may recurse)
}
virtual void OnCompletedRequest(const URLRequestStatus& status,
const std::string& security_info) {
result_->status = status;
event_.Signal();
}
private:
ResourceLoaderBridge::SyncLoadResponse* result_;
base::WaitableEvent event_;
};
//-----------------------------------------------------------------------------
class ResourceLoaderBridgeImpl : public ResourceLoaderBridge {
public:
ResourceLoaderBridgeImpl(
const webkit_glue::ResourceLoaderBridge::RequestInfo& request_info)
: params_(new RequestParams),
proxy_(NULL) {
DCHECK(!request_info.download_to_file); // Not implemented yet!
params_->method = request_info.method;
params_->url = request_info.url;
params_->first_party_for_cookies = request_info.first_party_for_cookies;
params_->referrer = request_info.referrer;
params_->headers = request_info.headers;
params_->load_flags = request_info.load_flags;
params_->request_type = request_info.request_type;
params_->appcache_host_id = request_info.appcache_host_id;
}
virtual ~ResourceLoaderBridgeImpl() {
if (proxy_) {
proxy_->DropPeer();
// Let the proxy die on the IO thread
g_io_thread->message_loop()->ReleaseSoon(FROM_HERE, proxy_);
}
}
// --------------------------------------------------------------------------
// ResourceLoaderBridge implementation:
virtual void AppendDataToUpload(const char* data, int data_len) {
DCHECK(params_.get());
if (!params_->upload)
params_->upload = new net::UploadData();
params_->upload->AppendBytes(data, data_len);
}
virtual void AppendFileRangeToUpload(
const FilePath& file_path,
uint64 offset,
uint64 length,
const base::Time& expected_modification_time) {
DCHECK(params_.get());
if (!params_->upload)
params_->upload = new net::UploadData();
params_->upload->AppendFileRange(file_path, offset, length,
expected_modification_time);
}
virtual void AppendBlobToUpload(const GURL& blob_url) {
DCHECK(params_.get());
if (!params_->upload)
params_->upload = new net::UploadData();
params_->upload->AppendBlob(blob_url);
}
virtual void SetUploadIdentifier(int64 identifier) {
DCHECK(params_.get());
if (!params_->upload)
params_->upload = new net::UploadData();
params_->upload->set_identifier(identifier);
}
virtual bool Start(Peer* peer) {
DCHECK(!proxy_);
if (!SimpleResourceLoaderBridge::EnsureIOThread())
return false;
proxy_ = new RequestProxy();
proxy_->AddRef();
proxy_->Start(peer, params_.release());
return true; // Any errors will be reported asynchronously.
}
virtual void Cancel() {
DCHECK(proxy_);
proxy_->Cancel();
}
virtual void SetDefersLoading(bool value) {
// TODO(darin): implement me
}
virtual void SyncLoad(SyncLoadResponse* response) {
DCHECK(!proxy_);
if (!SimpleResourceLoaderBridge::EnsureIOThread())
return;
// this may change as the result of a redirect
response->url = params_->url;
proxy_ = new SyncRequestProxy(response);
proxy_->AddRef();
proxy_->Start(NULL, params_.release());
static_cast<SyncRequestProxy*>(proxy_)->WaitForCompletion();
}
private:
// Ownership of params_ is transfered to the proxy when the proxy is created.
scoped_ptr<RequestParams> params_;
// The request proxy is allocated when we start the request, and then it
// sticks around until this ResourceLoaderBridge is destroyed.
RequestProxy* proxy_;
};
//-----------------------------------------------------------------------------
class CookieSetter : public base::RefCountedThreadSafe<CookieSetter> {
public:
void Set(const GURL& url, const std::string& cookie) {
DCHECK(MessageLoop::current() == g_io_thread->message_loop());
g_request_context->cookie_store()->SetCookie(url, cookie);
}
private:
friend class base::RefCountedThreadSafe<CookieSetter>;
~CookieSetter() {}
};
class CookieGetter : public base::RefCountedThreadSafe<CookieGetter> {
public:
CookieGetter() : event_(false, false) {
}
void Get(const GURL& url) {
result_ = g_request_context->cookie_store()->GetCookies(url);
event_.Signal();
}
std::string GetResult() {
if (!event_.Wait())
NOTREACHED();
return result_;
}
private:
friend class base::RefCountedThreadSafe<CookieGetter>;
~CookieGetter() {}
base::WaitableEvent event_;
std::string result_;
};
} // anonymous namespace
//-----------------------------------------------------------------------------
namespace webkit_glue {
// Factory function.
ResourceLoaderBridge* ResourceLoaderBridge::Create(
const webkit_glue::ResourceLoaderBridge::RequestInfo& request_info) {
return new ResourceLoaderBridgeImpl(request_info);
}
// Issue the proxy resolve request on the io thread, and wait
// for the result.
bool FindProxyForUrl(const GURL& url, std::string* proxy_list) {
DCHECK(g_request_context);
scoped_refptr<net::SyncProxyServiceHelper> sync_proxy_service(
new net::SyncProxyServiceHelper(g_io_thread->message_loop(),
g_request_context->proxy_service()));
net::ProxyInfo proxy_info;
int rv = sync_proxy_service->ResolveProxy(url, &proxy_info,
net::BoundNetLog());
if (rv == net::OK) {
*proxy_list = proxy_info.ToPacString();
}
return rv == net::OK;
}
} // namespace webkit_glue
//-----------------------------------------------------------------------------
// static
void SimpleResourceLoaderBridge::Init(
const FilePath& cache_path,
net::HttpCache::Mode cache_mode,
bool no_proxy) {
// Make sure to stop any existing IO thread since it may be using the
// current request context.
Shutdown();
DCHECK(!g_request_context_params);
DCHECK(!g_request_context);
DCHECK(!g_io_thread);
g_request_context_params = new TestShellRequestContextParams(
cache_path, cache_mode, no_proxy);
}
// static
void SimpleResourceLoaderBridge::Shutdown() {
if (g_io_thread) {
delete g_io_thread;
g_io_thread = NULL;
DCHECK(g_cache_thread);
delete g_cache_thread;
g_cache_thread = NULL;
DCHECK(!g_request_context) << "should have been nulled by thread dtor";
} else {
delete g_request_context_params;
g_request_context_params = NULL;
}
}
// static
void SimpleResourceLoaderBridge::SetCookie(const GURL& url,
const GURL& first_party_for_cookies,
const std::string& cookie) {
// Proxy to IO thread to synchronize w/ network loading.
if (!EnsureIOThread()) {
NOTREACHED();
return;
}
scoped_refptr<CookieSetter> cookie_setter = new CookieSetter();
g_io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
cookie_setter.get(), &CookieSetter::Set, url, cookie));
}
// static
std::string SimpleResourceLoaderBridge::GetCookies(
const GURL& url, const GURL& first_party_for_cookies) {
// Proxy to IO thread to synchronize w/ network loading
if (!EnsureIOThread()) {
NOTREACHED();
return std::string();
}
scoped_refptr<CookieGetter> getter = new CookieGetter();
g_io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
getter.get(), &CookieGetter::Get, url));
return getter->GetResult();
}
// static
bool SimpleResourceLoaderBridge::EnsureIOThread() {
if (g_io_thread)
return true;
#if defined(OS_WIN)
// Use NSS for SSL on Windows. TODO(wtc): this should eventually be hidden
// inside DefaultClientSocketFactory::CreateSSLClientSocket.
net::ClientSocketFactory::SetSSLClientSocketFactory(
net::SSLClientSocketNSSFactory);
#endif
#if defined(OS_MACOSX) || defined(OS_WIN)
// We want to be sure to init NSPR on the main thread.
base::EnsureNSPRInit();
#endif
// Create the cache thread. We want the cache thread to outlive the IO thread,
// so its lifetime is bonded to the IO thread lifetime.
DCHECK(!g_cache_thread);
g_cache_thread = new base::Thread("cache");
CHECK(g_cache_thread->StartWithOptions(
base::Thread::Options(MessageLoop::TYPE_IO, 0)));
g_io_thread = new IOThread();
base::Thread::Options options;
options.message_loop_type = MessageLoop::TYPE_IO;
return g_io_thread->StartWithOptions(options);
}
// static
void SimpleResourceLoaderBridge::SetAcceptAllCookies(bool accept_all_cookies) {
if (g_request_context_params) {
g_request_context_params->accept_all_cookies = accept_all_cookies;
DCHECK(!g_request_context);
DCHECK(!g_io_thread);
} else {
g_io_thread->SetAcceptAllCookies(accept_all_cookies);
}
}
// static
scoped_refptr<base::MessageLoopProxy>
SimpleResourceLoaderBridge::GetCacheThread() {
return g_cache_thread->message_loop_proxy();
}
// static
scoped_refptr<base::MessageLoopProxy>
SimpleResourceLoaderBridge::GetIoThread() {
if (!EnsureIOThread()) {
LOG(DFATAL) << "Failed to create IO thread.";
return NULL;
}
return g_io_thread->message_loop_proxy();
}