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