// 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)); }