// Copyright (c) 2012 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/profiles/profile_io_data.h" #include #include "base/basictypes.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/logging.h" #include "base/stl_util.h" #include "base/string_number_conversions.h" #include "base/string_util.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/content_settings/cookie_settings.h" #include "chrome/browser/content_settings/host_content_settings_map.h" #include "chrome/browser/custom_handlers/protocol_handler_registry.h" #include "chrome/browser/download/download_service.h" #include "chrome/browser/download/download_service_factory.h" #include "chrome/browser/extensions/extension_info_map.h" #include "chrome/browser/extensions/extension_protocols.h" #include "chrome/browser/io_thread.h" #include "chrome/browser/media/media_internals.h" #include "chrome/browser/net/chrome_cookie_notification_details.h" #include "chrome/browser/net/chrome_fraudulent_certificate_reporter.h" #include "chrome/browser/net/chrome_net_log.h" #include "chrome/browser/net/chrome_network_delegate.h" #include "chrome/browser/net/proxy_service_factory.h" #include "chrome/browser/notifications/desktop_notification_service_factory.h" #include "chrome/browser/policy/url_blacklist_manager.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/transport_security_persister.h" #include "chrome/browser/ui/webui/chrome_url_data_manager_backend.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "content/browser/renderer_host/resource_dispatcher_host.h" #include "content/browser/renderer_host/resource_dispatcher_host_request_info.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/host_zoom_map.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/resource_context.h" #include "net/base/origin_bound_cert_service.h" #include "net/http/http_transaction_factory.h" #include "net/http/http_util.h" #include "net/proxy/proxy_config_service_fixed.h" #include "net/proxy/proxy_script_fetcher_impl.h" #include "net/proxy/proxy_service.h" #include "net/url_request/url_request.h" #include "webkit/blob/blob_data.h" #include "webkit/blob/blob_url_request_job_factory.h" #include "webkit/fileapi/file_system_url_request_job_factory.h" #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/gview_request_interceptor.h" #include "chrome/browser/chromeos/proxy_config_service_impl.h" #endif // defined(OS_CHROMEOS) using content::BrowserContext; using content::BrowserThread; using content::ResourceContext; namespace { // ---------------------------------------------------------------------------- // CookieMonster::Delegate implementation // ---------------------------------------------------------------------------- class ChromeCookieMonsterDelegate : public net::CookieMonster::Delegate { public: explicit ChromeCookieMonsterDelegate( const base::Callback& profile_getter) : profile_getter_(profile_getter) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } // net::CookieMonster::Delegate implementation. virtual void OnCookieChanged( const net::CookieMonster::CanonicalCookie& cookie, bool removed, net::CookieMonster::Delegate::ChangeCause cause) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ChromeCookieMonsterDelegate::OnCookieChangedAsyncHelper, this, cookie, removed, cause)); } private: virtual ~ChromeCookieMonsterDelegate() {} void OnCookieChangedAsyncHelper( const net::CookieMonster::CanonicalCookie& cookie, bool removed, net::CookieMonster::Delegate::ChangeCause cause) { Profile* profile = profile_getter_.Run(); if (profile) { ChromeCookieDetails cookie_details(&cookie, removed, cause); content::NotificationService::current()->Notify( chrome::NOTIFICATION_COOKIE_CHANGED, content::Source(profile), content::Details(&cookie_details)); } } const base::Callback profile_getter_; }; class ProtocolHandlerRegistryInterceptor : public net::URLRequestJobFactory::Interceptor { public: explicit ProtocolHandlerRegistryInterceptor( ProtocolHandlerRegistry* protocol_handler_registry) : protocol_handler_registry_(protocol_handler_registry) { DCHECK(protocol_handler_registry_); } virtual ~ProtocolHandlerRegistryInterceptor() {} virtual net::URLRequestJob* MaybeIntercept( net::URLRequest* request) const OVERRIDE { return protocol_handler_registry_->MaybeCreateJob(request); } virtual bool WillHandleProtocol(const std::string& protocol) const { return protocol_handler_registry_->IsHandledProtocolIO(protocol); } virtual net::URLRequestJob* MaybeInterceptRedirect( const GURL& url, net::URLRequest* request) const OVERRIDE { return NULL; } virtual net::URLRequestJob* MaybeInterceptResponse( net::URLRequest* request) const OVERRIDE { return NULL; } private: const scoped_refptr protocol_handler_registry_; DISALLOW_COPY_AND_ASSIGN(ProtocolHandlerRegistryInterceptor); }; class ChromeBlobProtocolHandler : public webkit_blob::BlobProtocolHandler { public: ChromeBlobProtocolHandler( webkit_blob::BlobStorageController* blob_storage_controller, base::MessageLoopProxy* loop_proxy) : webkit_blob::BlobProtocolHandler(blob_storage_controller, loop_proxy) {} virtual ~ChromeBlobProtocolHandler() {} private: virtual scoped_refptr LookupBlobData(net::URLRequest* request) const { ResourceDispatcherHostRequestInfo* info = ResourceDispatcherHost::InfoForRequest(request); if (!info) return NULL; return info->requested_blob_data(); } DISALLOW_COPY_AND_ASSIGN(ChromeBlobProtocolHandler); }; Profile* GetProfileOnUI(ProfileManager* profile_manager, Profile* profile) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(profile); if (profile_manager->IsValidProfile(profile)) return profile; return NULL; } } // namespace void ProfileIOData::InitializeOnUIThread(Profile* profile) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); PrefService* pref_service = profile->GetPrefs(); scoped_ptr params(new ProfileParams); params->path = profile->GetPath(); params->is_incognito = profile->IsOffTheRecord(); params->clear_local_state_on_exit = pref_service->GetBoolean(prefs::kClearSiteDataOnExit); // Set up Accept-Language and Accept-Charset header values params->accept_language = net::HttpUtil::GenerateAcceptLanguageHeader( pref_service->GetString(prefs::kAcceptLanguages)); std::string default_charset = pref_service->GetString(prefs::kGlobalDefaultCharset); params->accept_charset = net::HttpUtil::GenerateAcceptCharsetHeader(default_charset); // At this point, we don't know the charset of the referring page // where a url request originates from. This is used to get a suggested // filename from Content-Disposition header made of raw 8bit characters. // Down the road, it can be overriden if it becomes known (for instance, // when download request is made through the context menu in a web page). // At the moment, it'll remain 'undeterministic' when a user // types a URL in the omnibar or click on a download link in a page. // For the latter, we need a change on the webkit-side. // We initialize it to the default charset here and a user will // have an *arguably* better default charset for interpreting a raw 8bit // C-D header field. It means the native OS codepage fallback in // net_util::GetSuggestedFilename is unlikely to be taken. params->referrer_charset = default_charset; params->io_thread = g_browser_process->io_thread(); params->host_content_settings_map = profile->GetHostContentSettingsMap(); params->cookie_settings = CookieSettings::Factory::GetForProfile(profile); params->ssl_config_service = profile->GetSSLConfigService(); base::Callback profile_getter = base::Bind(&GetProfileOnUI, g_browser_process->profile_manager(), profile); params->cookie_monster_delegate = new ChromeCookieMonsterDelegate(profile_getter); params->extension_info_map = profile->GetExtensionInfoMap(); params->notification_service = DesktopNotificationServiceFactory::GetForProfile(profile); params->protocol_handler_registry = profile->GetProtocolHandlerRegistry(); ChromeProxyConfigService* proxy_config_service = ProxyServiceFactory::CreateProxyConfigService(true); params->proxy_config_service.reset(proxy_config_service); profile->GetProxyConfigTracker()->SetChromeProxyConfigService( proxy_config_service); params->profile = profile; profile_params_.reset(params.release()); // The URLBlacklistManager has to be created on the UI thread to register // observers of |pref_service|, and it also has to clean up on // ShutdownOnUIThread to release these observers on the right thread. // Don't pass it in |profile_params_| to make sure it is correctly cleaned up, // in particular when this ProfileIOData isn't |initialized_| during deletion. #if defined(ENABLE_CONFIGURATION_POLICY) url_blacklist_manager_.reset(new policy::URLBlacklistManager(pref_service)); #endif initialized_on_UI_thread_ = true; // We need to make sure that content initializes its own data structures that // are associated with each ResourceContext because we might post this // object to the IO thread after this function. BrowserContext::EnsureResourceContextInitialized(profile); } ProfileIOData::AppRequestContext::AppRequestContext() {} ProfileIOData::AppRequestContext::~AppRequestContext() {} void ProfileIOData::AppRequestContext::SetCookieStore( net::CookieStore* cookie_store) { cookie_store_ = cookie_store; set_cookie_store(cookie_store); } void ProfileIOData::AppRequestContext::SetHttpTransactionFactory( net::HttpTransactionFactory* http_factory) { http_factory_.reset(http_factory); set_http_transaction_factory(http_factory); } ProfileIOData::ProfileParams::ProfileParams() : is_incognito(false), clear_local_state_on_exit(false), io_thread(NULL), notification_service(NULL), profile(NULL) {} ProfileIOData::ProfileParams::~ProfileParams() {} ProfileIOData::ProfileIOData(bool is_incognito) : initialized_(false), ALLOW_THIS_IN_INITIALIZER_LIST(resource_context_(this)), initialized_on_UI_thread_(false) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } ProfileIOData::~ProfileIOData() { if (BrowserThread::IsMessageLoopValid(BrowserThread::IO)) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); } // static ProfileIOData* ProfileIOData::FromResourceContext( content::ResourceContext* rc) { return (static_cast(rc))->io_data_; } // static bool ProfileIOData::IsHandledProtocol(const std::string& scheme) { DCHECK_EQ(scheme, StringToLowerASCII(scheme)); static const char* const kProtocolList[] = { chrome::kExtensionScheme, chrome::kChromeUIScheme, chrome::kChromeDevToolsScheme, #if defined(OS_CHROMEOS) chrome::kMetadataScheme, #endif // defined(OS_CHROMEOS) chrome::kBlobScheme, chrome::kFileSystemScheme, }; for (size_t i = 0; i < arraysize(kProtocolList); ++i) { if (scheme == kProtocolList[i]) return true; } return net::URLRequest::IsHandledProtocol(scheme); } bool ProfileIOData::IsHandledURL(const GURL& url) { if (!url.is_valid()) { // We handle error cases. return true; } return IsHandledProtocol(url.scheme()); } content::ResourceContext* ProfileIOData::GetResourceContext() const { return &resource_context_; } ChromeURLDataManagerBackend* ProfileIOData::GetChromeURLDataManagerBackend() const { LazyInitialize(); return chrome_url_data_manager_backend_.get(); } scoped_refptr ProfileIOData::GetMainRequestContext() const { LazyInitialize(); return main_request_context_; } scoped_refptr ProfileIOData::GetMediaRequestContext() const { LazyInitialize(); scoped_refptr context = AcquireMediaRequestContext(); DCHECK(context); return context; } scoped_refptr ProfileIOData::GetExtensionsRequestContext() const { LazyInitialize(); return extensions_request_context_; } scoped_refptr ProfileIOData::GetIsolatedAppRequestContext( scoped_refptr main_context, const std::string& app_id) const { LazyInitialize(); scoped_refptr context; if (ContainsKey(app_request_context_map_, app_id)) { context = app_request_context_map_[app_id]; } else { context = AcquireIsolatedAppRequestContext(main_context, app_id); app_request_context_map_[app_id] = context; } DCHECK(context); return context; } ExtensionInfoMap* ProfileIOData::GetExtensionInfoMap() const { return extension_info_map_; } HostContentSettingsMap* ProfileIOData::GetHostContentSettingsMap() const { return host_content_settings_map_; } CookieSettings* ProfileIOData::GetCookieSettings() const { return cookie_settings_; } DesktopNotificationService* ProfileIOData::GetNotificationService() const { return notification_service_; } ProfileIOData::ResourceContext::ResourceContext(ProfileIOData* io_data) : io_data_(io_data) { DCHECK(io_data); } ProfileIOData::ResourceContext::~ResourceContext() {} void ProfileIOData::ResourceContext::EnsureInitialized() { io_data_->LazyInitialize(); } net::HostResolver* ProfileIOData::ResourceContext::GetHostResolver() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); EnsureInitialized(); return host_resolver_; } net::URLRequestContext* ProfileIOData::ResourceContext::GetRequestContext() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); EnsureInitialized(); return request_context_; } MediaObserver* ProfileIOData::ResourceContext::GetMediaObserver() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); EnsureInitialized(); return media_observer_; } void ProfileIOData::LazyInitialize() const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (initialized_) return; // TODO(jhawkins): Remove once crbug.com/102004 is fixed. CHECK(initialized_on_UI_thread_); // TODO(jhawkins): Return to DCHECK once crbug.com/102004 is fixed. CHECK(profile_params_.get()); IOThread* const io_thread = profile_params_->io_thread; IOThread::Globals* const io_thread_globals = io_thread->globals(); const CommandLine& command_line = *CommandLine::ForCurrentProcess(); // Create the common request contexts. main_request_context_ = new ChromeURLRequestContext; extensions_request_context_ = new ChromeURLRequestContext; chrome_url_data_manager_backend_.reset(new ChromeURLDataManagerBackend); network_delegate_.reset(new ChromeNetworkDelegate( io_thread_globals->extension_event_router_forwarder.get(), profile_params_->extension_info_map, url_blacklist_manager_.get(), profile_params_->profile, &enable_referrers_)); fraudulent_certificate_reporter_.reset( new chrome_browser_net::ChromeFraudulentCertificateReporter( main_request_context_)); proxy_service_.reset( ProxyServiceFactory::CreateProxyService( io_thread->net_log(), io_thread_globals->proxy_script_fetcher_context.get(), profile_params_->proxy_config_service.release(), command_line)); transport_security_state_.reset(new net::TransportSecurityState( command_line.GetSwitchValueASCII(switches::kHstsHosts))); transport_security_persister_.reset( new TransportSecurityPersister(transport_security_state_.get(), profile_params_->path, profile_params_->is_incognito)); // NOTE(willchan): Keep these protocol handlers in sync with // ProfileIOData::IsHandledProtocol(). job_factory_.reset(new net::URLRequestJobFactory); if (profile_params_->protocol_handler_registry) { job_factory_->AddInterceptor( new ProtocolHandlerRegistryInterceptor( profile_params_->protocol_handler_registry)); } bool set_protocol = job_factory_->SetProtocolHandler( chrome::kExtensionScheme, CreateExtensionProtocolHandler(profile_params_->is_incognito, profile_params_->extension_info_map)); DCHECK(set_protocol); set_protocol = job_factory_->SetProtocolHandler( chrome::kChromeUIScheme, ChromeURLDataManagerBackend::CreateProtocolHandler( chrome_url_data_manager_backend_.get(), ResourceContext::GetAppCacheService(&resource_context_), ResourceContext::GetBlobStorageController(&resource_context_))); DCHECK(set_protocol); set_protocol = job_factory_->SetProtocolHandler( chrome::kChromeDevToolsScheme, CreateDevToolsProtocolHandler(chrome_url_data_manager_backend_.get())); DCHECK(set_protocol); set_protocol = job_factory_->SetProtocolHandler( chrome::kBlobScheme, new ChromeBlobProtocolHandler( ResourceContext::GetBlobStorageController(&resource_context_), BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE))); DCHECK(set_protocol); set_protocol = job_factory_->SetProtocolHandler( chrome::kFileSystemScheme, CreateFileSystemProtocolHandler( ResourceContext::GetFileSystemContext(&resource_context_), BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE))); DCHECK(set_protocol); #if defined(OS_CHROMEOS) && !defined(GOOGLE_CHROME_BUILD) // Install the GView request interceptor that will redirect requests // of compatible documents (PDF, etc) to the GView document viewer. const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); if (parsed_command_line.HasSwitch(switches::kEnableGView)) job_factory_->AddInterceptor(new chromeos::GViewRequestInterceptor); #endif // defined(OS_CHROMEOS) && !defined(GOOGLE_CHROME_BUILD) // Take ownership over these parameters. host_content_settings_map_ = profile_params_->host_content_settings_map; cookie_settings_ = profile_params_->cookie_settings; notification_service_ = profile_params_->notification_service; extension_info_map_ = profile_params_->extension_info_map; resource_context_.host_resolver_ = io_thread_globals->host_resolver.get(); resource_context_.request_context_ = main_request_context_; resource_context_.media_observer_ = io_thread_globals->media.media_internals.get(); LazyInitializeInternal(profile_params_.get()); profile_params_.reset(); initialized_ = true; } void ProfileIOData::ApplyProfileParamsToContext( ChromeURLRequestContext* context) const { context->set_is_incognito(profile_params_->is_incognito); context->set_accept_language(profile_params_->accept_language); context->set_accept_charset(profile_params_->accept_charset); context->set_referrer_charset(profile_params_->referrer_charset); context->set_ssl_config_service(profile_params_->ssl_config_service); } void ProfileIOData::ShutdownOnUIThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); enable_referrers_.Destroy(); clear_local_state_on_exit_.Destroy(); safe_browsing_enabled_.Destroy(); session_startup_pref_.Destroy(); #if defined(ENABLE_CONFIGURATION_POLICY) if (url_blacklist_manager_.get()) url_blacklist_manager_->ShutdownOnUIThread(); #endif BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind( &ResourceDispatcherHost::CancelRequestsForContext, base::Unretained(ResourceDispatcherHost::Get()), &resource_context_)); bool posted = BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, this); if (!posted) delete this; } void ProfileIOData::set_origin_bound_cert_service( net::OriginBoundCertService* origin_bound_cert_service) const { origin_bound_cert_service_.reset(origin_bound_cert_service); }