// Copyright (c) 2011 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.
#include "chrome/browser/net/chrome_url_request_context.h"
#include "base/message_loop.h"
#include "base/message_loop_proxy.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/io_thread.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_io_data.h"
#include "chrome/browser/ui/webui/chrome_url_data_manager_backend.h"
#include "chrome/common/pref_names.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_service.h"
#include "net/base/cookie_store.h"
#include "net/ftp/ftp_transaction_factory.h"
#include "net/http/http_transaction_factory.h"
#include "net/http/http_util.h"
#include "webkit/glue/webkit_glue.h"
#if defined(USE_NSS)
#include "net/ocsp/nss_ocsp.h"
#endif
class ChromeURLRequestContextFactory {
public:
ChromeURLRequestContextFactory() {}
virtual ~ChromeURLRequestContextFactory() {}
// Called to create a new instance (will only be called once).
virtual scoped_refptr<ChromeURLRequestContext> Create() = 0;
protected:
DISALLOW_COPY_AND_ASSIGN(ChromeURLRequestContextFactory);
};
namespace {
// ----------------------------------------------------------------------------
// Helper factories
// ----------------------------------------------------------------------------
// Factory that creates the main ChromeURLRequestContext.
class FactoryForMain : public ChromeURLRequestContextFactory {
public:
explicit FactoryForMain(const ProfileIOData* profile_io_data)
: profile_io_data_(profile_io_data) {}
virtual scoped_refptr<ChromeURLRequestContext> Create() {
return profile_io_data_->GetMainRequestContext();
}
private:
const scoped_refptr<const ProfileIOData> profile_io_data_;
};
// Factory that creates the ChromeURLRequestContext for extensions.
class FactoryForExtensions : public ChromeURLRequestContextFactory {
public:
explicit FactoryForExtensions(const ProfileIOData* profile_io_data)
: profile_io_data_(profile_io_data) {}
virtual scoped_refptr<ChromeURLRequestContext> Create() {
return profile_io_data_->GetExtensionsRequestContext();
}
private:
const scoped_refptr<const ProfileIOData> profile_io_data_;
};
// Factory that creates the ChromeURLRequestContext for a given isolated app.
class FactoryForIsolatedApp : public ChromeURLRequestContextFactory {
public:
FactoryForIsolatedApp(const ProfileIOData* profile_io_data,
const std::string& app_id,
ChromeURLRequestContextGetter* main_context)
: profile_io_data_(profile_io_data),
app_id_(app_id),
main_request_context_getter_(main_context) {}
virtual scoped_refptr<ChromeURLRequestContext> Create() {
// We will copy most of the state from the main request context.
return profile_io_data_->GetIsolatedAppRequestContext(
main_request_context_getter_->GetIOContext(), app_id_);
}
private:
const scoped_refptr<const ProfileIOData> profile_io_data_;
const std::string app_id_;
scoped_refptr<ChromeURLRequestContextGetter>
main_request_context_getter_;
};
// Factory that creates the ChromeURLRequestContext for media.
class FactoryForMedia : public ChromeURLRequestContextFactory {
public:
explicit FactoryForMedia(const ProfileIOData* profile_io_data)
: profile_io_data_(profile_io_data) {
}
virtual scoped_refptr<ChromeURLRequestContext> Create() {
return profile_io_data_->GetMediaRequestContext();
}
private:
const scoped_refptr<const ProfileIOData> profile_io_data_;
};
} // namespace
// ----------------------------------------------------------------------------
// ChromeURLRequestContextGetter
// ----------------------------------------------------------------------------
ChromeURLRequestContextGetter::ChromeURLRequestContextGetter(
Profile* profile,
ChromeURLRequestContextFactory* factory)
: io_thread_(g_browser_process->io_thread()),
factory_(factory),
url_request_context_(NULL) {
DCHECK(factory);
DCHECK(profile);
RegisterPrefsObserver(profile);
}
ChromeURLRequestContextGetter::~ChromeURLRequestContextGetter() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(registrar_.IsEmpty()) << "Probably didn't call CleanupOnUIThread";
// Either we already transformed the factory into a net::URLRequestContext, or
// we still have a pending factory.
DCHECK((factory_.get() && !url_request_context_.get()) ||
(!factory_.get() && url_request_context_.get()));
if (url_request_context_)
io_thread_->UnregisterURLRequestContextGetter(this);
// The scoped_refptr / scoped_ptr destructors take care of releasing
// |factory_| and |url_request_context_| now.
}
// Lazily create a ChromeURLRequestContext using our factory.
net::URLRequestContext* ChromeURLRequestContextGetter::GetURLRequestContext() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!url_request_context_) {
DCHECK(factory_.get());
url_request_context_ = factory_->Create();
if (is_main()) {
url_request_context_->set_is_main(true);
#if defined(USE_NSS)
// TODO(ukai): find a better way to set the net::URLRequestContext for
// OCSP.
net::SetURLRequestContextForOCSP(url_request_context_);
#endif
}
factory_.reset();
io_thread_->RegisterURLRequestContextGetter(this);
}
return url_request_context_;
}
void ChromeURLRequestContextGetter::ReleaseURLRequestContext() {
DCHECK(url_request_context_);
url_request_context_ = NULL;
}
net::CookieStore* ChromeURLRequestContextGetter::DONTUSEME_GetCookieStore() {
// If we are running on the IO thread this is real easy.
if (BrowserThread::CurrentlyOn(BrowserThread::IO))
return GetURLRequestContext()->cookie_store();
// If we aren't running on the IO thread, we cannot call
// GetURLRequestContext(). Instead we will post a task to the IO loop
// and wait for it to complete.
base::WaitableEvent completion(false, false);
net::CookieStore* result = NULL;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableMethod(this,
&ChromeURLRequestContextGetter::GetCookieStoreAsyncHelper,
&completion,
&result));
completion.Wait();
DCHECK(result);
return result;
}
scoped_refptr<base::MessageLoopProxy>
ChromeURLRequestContextGetter::GetIOMessageLoopProxy() const {
return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
}
// static
ChromeURLRequestContextGetter* ChromeURLRequestContextGetter::CreateOriginal(
Profile* profile,
const ProfileIOData* profile_io_data) {
DCHECK(!profile->IsOffTheRecord());
return new ChromeURLRequestContextGetter(
profile,
new FactoryForMain(profile_io_data));
}
// static
ChromeURLRequestContextGetter*
ChromeURLRequestContextGetter::CreateOriginalForMedia(
Profile* profile, const ProfileIOData* profile_io_data) {
DCHECK(!profile->IsOffTheRecord());
return new ChromeURLRequestContextGetter(
profile,
new FactoryForMedia(profile_io_data));
}
// static
ChromeURLRequestContextGetter*
ChromeURLRequestContextGetter::CreateOriginalForExtensions(
Profile* profile, const ProfileIOData* profile_io_data) {
DCHECK(!profile->IsOffTheRecord());
return new ChromeURLRequestContextGetter(
profile,
new FactoryForExtensions(profile_io_data));
}
// static
ChromeURLRequestContextGetter*
ChromeURLRequestContextGetter::CreateOriginalForIsolatedApp(
Profile* profile,
const ProfileIOData* profile_io_data,
const std::string& app_id) {
DCHECK(!profile->IsOffTheRecord());
ChromeURLRequestContextGetter* main_context =
static_cast<ChromeURLRequestContextGetter*>(profile->GetRequestContext());
return new ChromeURLRequestContextGetter(
profile,
new FactoryForIsolatedApp(profile_io_data, app_id, main_context));
}
// static
ChromeURLRequestContextGetter*
ChromeURLRequestContextGetter::CreateOffTheRecord(
Profile* profile, const ProfileIOData* profile_io_data) {
DCHECK(profile->IsOffTheRecord());
return new ChromeURLRequestContextGetter(
profile, new FactoryForMain(profile_io_data));
}
// static
ChromeURLRequestContextGetter*
ChromeURLRequestContextGetter::CreateOffTheRecordForExtensions(
Profile* profile, const ProfileIOData* profile_io_data) {
DCHECK(profile->IsOffTheRecord());
return new ChromeURLRequestContextGetter(
profile, new FactoryForExtensions(profile_io_data));
}
// static
ChromeURLRequestContextGetter*
ChromeURLRequestContextGetter::CreateOffTheRecordForIsolatedApp(
Profile* profile,
const ProfileIOData* profile_io_data,
const std::string& app_id) {
DCHECK(profile->IsOffTheRecord());
ChromeURLRequestContextGetter* main_context =
static_cast<ChromeURLRequestContextGetter*>(profile->GetRequestContext());
return new ChromeURLRequestContextGetter(
profile,
new FactoryForIsolatedApp(profile_io_data, app_id, main_context));
}
void ChromeURLRequestContextGetter::CleanupOnUIThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Unregister for pref notifications.
DCHECK(!registrar_.IsEmpty()) << "Called more than once!";
registrar_.RemoveAll();
}
// NotificationObserver implementation.
void ChromeURLRequestContextGetter::Observe(
NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (NotificationType::PREF_CHANGED == type) {
std::string* pref_name_in = Details<std::string>(details).ptr();
PrefService* prefs = Source<PrefService>(source).ptr();
DCHECK(pref_name_in && prefs);
if (*pref_name_in == prefs::kAcceptLanguages) {
std::string accept_language =
prefs->GetString(prefs::kAcceptLanguages);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableMethod(
this,
&ChromeURLRequestContextGetter::OnAcceptLanguageChange,
accept_language));
} else if (*pref_name_in == prefs::kDefaultCharset) {
std::string default_charset =
prefs->GetString(prefs::kDefaultCharset);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableMethod(
this,
&ChromeURLRequestContextGetter::OnDefaultCharsetChange,
default_charset));
} else if (*pref_name_in == prefs::kClearSiteDataOnExit) {
bool clear_site_data =
prefs->GetBoolean(prefs::kClearSiteDataOnExit);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableMethod(
this,
&ChromeURLRequestContextGetter::OnClearSiteDataOnExitChange,
clear_site_data));
}
} else {
NOTREACHED();
}
}
void ChromeURLRequestContextGetter::RegisterPrefsObserver(Profile* profile) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
registrar_.Init(profile->GetPrefs());
registrar_.Add(prefs::kAcceptLanguages, this);
registrar_.Add(prefs::kDefaultCharset, this);
registrar_.Add(prefs::kClearSiteDataOnExit, this);
}
void ChromeURLRequestContextGetter::OnAcceptLanguageChange(
const std::string& accept_language) {
GetIOContext()->OnAcceptLanguageChange(accept_language);
}
void ChromeURLRequestContextGetter::OnDefaultCharsetChange(
const std::string& default_charset) {
GetIOContext()->OnDefaultCharsetChange(default_charset);
}
void ChromeURLRequestContextGetter::OnClearSiteDataOnExitChange(
bool clear_site_data) {
GetURLRequestContext()->cookie_store()->GetCookieMonster()->
SetClearPersistentStoreOnExit(clear_site_data);
}
void ChromeURLRequestContextGetter::GetCookieStoreAsyncHelper(
base::WaitableEvent* completion,
net::CookieStore** result) {
// Note that CookieStore is refcounted, yet we do not add a reference.
*result = GetURLRequestContext()->cookie_store();
completion->Signal();
}
// ----------------------------------------------------------------------------
// ChromeURLRequestContext
// ----------------------------------------------------------------------------
ChromeURLRequestContext::ChromeURLRequestContext()
: is_incognito_(false) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
}
void ChromeURLRequestContext::CopyFrom(ChromeURLRequestContext* other) {
URLRequestContext::CopyFrom(other);
// Copy ChromeURLRequestContext parameters.
set_user_script_dir_path(other->user_script_dir_path());
set_appcache_service(other->appcache_service());
set_host_content_settings_map(other->host_content_settings_map());
set_host_zoom_map(other->host_zoom_map_);
set_blob_storage_context(other->blob_storage_context());
set_file_system_context(other->file_system_context());
set_extension_info_map(other->extension_info_map_);
set_prerender_manager(other->prerender_manager());
// ChromeURLDataManagerBackend is unique per context.
set_is_incognito(other->is_incognito());
}
ChromeURLDataManagerBackend*
ChromeURLRequestContext::GetChromeURLDataManagerBackend() {
if (!chrome_url_data_manager_backend_.get())
chrome_url_data_manager_backend_.reset(new ChromeURLDataManagerBackend());
return chrome_url_data_manager_backend_.get();
}
ChromeURLRequestContext::~ChromeURLRequestContext() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (appcache_service_.get() && appcache_service_->request_context() == this)
appcache_service_->set_request_context(NULL);
#if defined(USE_NSS)
if (is_main()) {
net::URLRequestContext* ocsp_context = net::GetURLRequestContextForOCSP();
if (ocsp_context) {
DCHECK_EQ(this, ocsp_context);
// We are releasing the net::URLRequestContext used by OCSP handlers.
net::SetURLRequestContextForOCSP(NULL);
}
}
#endif
NotificationService::current()->Notify(
NotificationType::URL_REQUEST_CONTEXT_RELEASED,
Source<net::URLRequestContext>(this),
NotificationService::NoDetails());
// cookie_policy_'s lifetime is auto-managed by chrome_cookie_policy_. We
// null this out here to avoid a dangling reference to chrome_cookie_policy_
// when ~net::URLRequestContext runs.
set_cookie_policy(NULL);
}
const std::string& ChromeURLRequestContext::GetUserAgent(
const GURL& url) const {
return webkit_glue::GetUserAgent(url);
}
void ChromeURLRequestContext::OnAcceptLanguageChange(
const std::string& accept_language) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
set_accept_language(
net::HttpUtil::GenerateAcceptLanguageHeader(accept_language));
}
void ChromeURLRequestContext::OnDefaultCharsetChange(
const std::string& default_charset) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
set_referrer_charset(default_charset);
set_accept_charset(
net::HttpUtil::GenerateAcceptCharsetHeader(default_charset));
}