1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/profiles/profile_io_data.h"
6
7#include <string>
8
9#include "base/basictypes.h"
10#include "base/command_line.h"
11#include "base/compiler_specific.h"
12#include "base/logging.h"
13#include "base/stl_util-inl.h"
14#include "base/string_number_conversions.h"
15#include "chrome/browser/browser_process.h"
16#include "chrome/browser/custom_handlers/protocol_handler_registry.h"
17#include "chrome/browser/extensions/user_script_master.h"
18#include "chrome/browser/io_thread.h"
19#include "chrome/browser/net/chrome_cookie_notification_details.h"
20#include "chrome/browser/net/chrome_cookie_policy.h"
21#include "chrome/browser/net/chrome_dns_cert_provenance_checker_factory.h"
22#include "chrome/browser/net/chrome_net_log.h"
23#include "chrome/browser/net/chrome_network_delegate.h"
24#include "chrome/browser/net/pref_proxy_config_service.h"
25#include "chrome/browser/net/proxy_service_factory.h"
26#include "chrome/browser/prefs/pref_service.h"
27#include "chrome/browser/profiles/profile.h"
28#include "chrome/common/chrome_switches.h"
29#include "chrome/common/pref_names.h"
30#include "content/browser/browser_thread.h"
31#include "content/browser/resource_context.h"
32#include "content/common/notification_service.h"
33#include "net/http/http_util.h"
34#include "net/proxy/proxy_config_service_fixed.h"
35#include "net/proxy/proxy_script_fetcher_impl.h"
36#include "net/proxy/proxy_service.h"
37#include "webkit/database/database_tracker.h"
38
39namespace {
40
41// ----------------------------------------------------------------------------
42// CookieMonster::Delegate implementation
43// ----------------------------------------------------------------------------
44class ChromeCookieMonsterDelegate : public net::CookieMonster::Delegate {
45 public:
46  explicit ChromeCookieMonsterDelegate(Profile* profile) {
47    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
48    profile_getter_ = new ProfileGetter(profile);
49  }
50
51  // net::CookieMonster::Delegate implementation.
52  virtual void OnCookieChanged(
53      const net::CookieMonster::CanonicalCookie& cookie,
54      bool removed,
55      net::CookieMonster::Delegate::ChangeCause cause) {
56    BrowserThread::PostTask(
57        BrowserThread::UI, FROM_HERE,
58        NewRunnableMethod(this,
59            &ChromeCookieMonsterDelegate::OnCookieChangedAsyncHelper,
60            cookie,
61            removed,
62            cause));
63  }
64
65 private:
66  // This class allows us to safely access the Profile pointer. The Delegate
67  // itself cannot observe the PROFILE_DESTROYED notification, since it cannot
68  // guarantee to be deleted on the UI thread and therefore unregister from
69  // the notifications. All methods of ProfileGetter must be invoked on the UI
70  // thread.
71  class ProfileGetter
72      : public base::RefCountedThreadSafe<ProfileGetter,
73                                          BrowserThread::DeleteOnUIThread>,
74        public NotificationObserver {
75   public:
76    explicit ProfileGetter(Profile* profile) : profile_(profile) {
77      DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
78      registrar_.Add(this,
79                     NotificationType::PROFILE_DESTROYED,
80                     Source<Profile>(profile_));
81    }
82
83    // NotificationObserver implementation.
84    void Observe(NotificationType type,
85                 const NotificationSource& source,
86                 const NotificationDetails& details) {
87      DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
88      if (NotificationType::PROFILE_DESTROYED == type) {
89        Profile* profile = Source<Profile>(source).ptr();
90        if (profile_ == profile)
91          profile_ = NULL;
92      }
93    }
94
95    Profile* get() {
96      DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
97      return profile_;
98    }
99
100   private:
101    friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
102    friend class DeleteTask<ProfileGetter>;
103
104    virtual ~ProfileGetter() {}
105
106    NotificationRegistrar registrar_;
107
108    Profile* profile_;
109  };
110
111  virtual ~ChromeCookieMonsterDelegate() {}
112
113  void OnCookieChangedAsyncHelper(
114      const net::CookieMonster::CanonicalCookie& cookie,
115      bool removed,
116      net::CookieMonster::Delegate::ChangeCause cause) {
117    if (profile_getter_->get()) {
118      ChromeCookieDetails cookie_details(&cookie, removed, cause);
119      NotificationService::current()->Notify(
120          NotificationType::COOKIE_CHANGED,
121          Source<Profile>(profile_getter_->get()),
122          Details<ChromeCookieDetails>(&cookie_details));
123    }
124  }
125
126  scoped_refptr<ProfileGetter> profile_getter_;
127};
128
129}  // namespace
130
131void ProfileIOData::InitializeProfileParams(Profile* profile) {
132  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
133  PrefService* pref_service = profile->GetPrefs();
134
135  scoped_ptr<ProfileParams> params(new ProfileParams);
136  params->is_incognito = profile->IsOffTheRecord();
137  params->clear_local_state_on_exit =
138      pref_service->GetBoolean(prefs::kClearSiteDataOnExit);
139
140  params->appcache_service = profile->GetAppCacheService();
141
142  // Set up Accept-Language and Accept-Charset header values
143  params->accept_language = net::HttpUtil::GenerateAcceptLanguageHeader(
144      pref_service->GetString(prefs::kAcceptLanguages));
145  std::string default_charset = pref_service->GetString(prefs::kDefaultCharset);
146  params->accept_charset =
147      net::HttpUtil::GenerateAcceptCharsetHeader(default_charset);
148
149  // At this point, we don't know the charset of the referring page
150  // where a url request originates from. This is used to get a suggested
151  // filename from Content-Disposition header made of raw 8bit characters.
152  // Down the road, it can be overriden if it becomes known (for instance,
153  // when download request is made through the context menu in a web page).
154  // At the moment, it'll remain 'undeterministic' when a user
155  // types a URL in the omnibar or click on a download link in a page.
156  // For the latter, we need a change on the webkit-side.
157  // We initialize it to the default charset here and a user will
158  // have an *arguably* better default charset for interpreting a raw 8bit
159  // C-D header field.  It means the native OS codepage fallback in
160  // net_util::GetSuggestedFilename is unlikely to be taken.
161  params->referrer_charset = default_charset;
162
163  params->io_thread = g_browser_process->io_thread();
164
165  params->host_content_settings_map = profile->GetHostContentSettingsMap();
166  params->host_zoom_map = profile->GetHostZoomMap();
167  params->transport_security_state = profile->GetTransportSecurityState();
168
169  if (profile->GetUserScriptMaster()) {
170    params->user_script_dir_path =
171        profile->GetUserScriptMaster()->user_script_dir();
172  }
173
174  params->ssl_config_service = profile->GetSSLConfigService();
175  params->cookie_monster_delegate = new ChromeCookieMonsterDelegate(profile);
176  params->database_tracker = profile->GetDatabaseTracker();
177  params->appcache_service = profile->GetAppCacheService();
178  params->blob_storage_context = profile->GetBlobStorageContext();
179  params->file_system_context = profile->GetFileSystemContext();
180  params->extension_info_map = profile->GetExtensionInfoMap();
181  params->prerender_manager = profile->GetPrerenderManager();
182  params->protocol_handler_registry = profile->GetProtocolHandlerRegistry();
183
184  params->proxy_config_service.reset(
185      ProxyServiceFactory::CreateProxyConfigService(
186          profile->GetProxyConfigTracker()));
187  params->profile_id = profile->GetRuntimeId();
188  profile_params_.reset(params.release());
189}
190
191ProfileIOData::RequestContext::RequestContext() {}
192ProfileIOData::RequestContext::~RequestContext() {}
193
194ProfileIOData::ProfileParams::ProfileParams()
195    : is_incognito(false),
196      clear_local_state_on_exit(false),
197      profile_id(Profile::kInvalidProfileId) {}
198ProfileIOData::ProfileParams::~ProfileParams() {}
199
200ProfileIOData::ProfileIOData(bool is_incognito)
201    : initialized_(false),
202      ALLOW_THIS_IN_INITIALIZER_LIST(resource_context_(this)) {
203  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
204}
205
206ProfileIOData::~ProfileIOData() {
207  // If we have never initialized ProfileIOData, then Handle may hold the only
208  // reference to it. The important thing is to make sure it hasn't been
209  // initialized yet, because the lazily initialized variables are supposed to
210  // live on the IO thread.
211  if (BrowserThread::CurrentlyOn(BrowserThread::UI))
212    DCHECK(!initialized_);
213  else
214    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
215}
216
217scoped_refptr<ChromeURLRequestContext>
218ProfileIOData::GetMainRequestContext() const {
219  LazyInitialize();
220  scoped_refptr<RequestContext> context = main_request_context_;
221  context->set_profile_io_data(this);
222  main_request_context_ = NULL;
223  return context;
224}
225
226scoped_refptr<ChromeURLRequestContext>
227ProfileIOData::GetMediaRequestContext() const {
228  LazyInitialize();
229  scoped_refptr<ChromeURLRequestContext> context =
230      AcquireMediaRequestContext();
231  DCHECK(context);
232  return context;
233}
234
235scoped_refptr<ChromeURLRequestContext>
236ProfileIOData::GetExtensionsRequestContext() const {
237  LazyInitialize();
238  scoped_refptr<RequestContext> context =
239      extensions_request_context_;
240  context->set_profile_io_data(this);
241  extensions_request_context_ = NULL;
242  return context;
243}
244
245scoped_refptr<ChromeURLRequestContext>
246ProfileIOData::GetIsolatedAppRequestContext(
247    scoped_refptr<ChromeURLRequestContext> main_context,
248    const std::string& app_id) const {
249  LazyInitialize();
250  scoped_refptr<ChromeURLRequestContext> context =
251      AcquireIsolatedAppRequestContext(main_context, app_id);
252  DCHECK(context);
253  return context;
254}
255
256const content::ResourceContext& ProfileIOData::GetResourceContext() const {
257  return resource_context_;
258}
259
260ProfileIOData::ResourceContext::ResourceContext(const ProfileIOData* io_data)
261    : io_data_(io_data) {
262  DCHECK(io_data);
263}
264
265ProfileIOData::ResourceContext::~ResourceContext() {}
266
267void ProfileIOData::ResourceContext::EnsureInitialized() const {
268  io_data_->LazyInitialize();
269}
270
271void ProfileIOData::LazyInitialize() const {
272  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
273  if (initialized_)
274    return;
275  DCHECK(profile_params_.get());
276
277  IOThread* const io_thread = profile_params_->io_thread;
278  IOThread::Globals* const io_thread_globals = io_thread->globals();
279  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
280
281  // Create the common request contexts.
282  main_request_context_ = new RequestContext;
283  extensions_request_context_ = new RequestContext;
284
285  profile_params_->appcache_service->set_request_context(main_request_context_);
286
287  // Create objects pointed to by URLRequestContext.
288  cookie_policy_.reset(
289      new ChromeCookiePolicy(profile_params_->host_content_settings_map));
290
291  network_delegate_.reset(new ChromeNetworkDelegate(
292        io_thread_globals->extension_event_router_forwarder.get(),
293        profile_params_->profile_id,
294        &enable_referrers_,
295        profile_params_->protocol_handler_registry));
296
297  dns_cert_checker_.reset(
298      CreateDnsCertProvenanceChecker(io_thread_globals->dnsrr_resolver.get(),
299                                     main_request_context_));
300
301  proxy_service_ =
302      ProxyServiceFactory::CreateProxyService(
303          io_thread->net_log(),
304          io_thread_globals->proxy_script_fetcher_context.get(),
305          profile_params_->proxy_config_service.release(),
306          command_line);
307
308  // Take ownership over these parameters.
309  database_tracker_ = profile_params_->database_tracker;
310  appcache_service_ = profile_params_->appcache_service;
311  blob_storage_context_ = profile_params_->blob_storage_context;
312  file_system_context_ = profile_params_->file_system_context;
313
314  resource_context_.set_host_resolver(io_thread_globals->host_resolver.get());
315  resource_context_.set_request_context(main_request_context_);
316  resource_context_.set_database_tracker(database_tracker_);
317  resource_context_.set_appcache_service(appcache_service_);
318  resource_context_.set_blob_storage_context(blob_storage_context_);
319  resource_context_.set_file_system_context(file_system_context_);
320
321  LazyInitializeInternal(profile_params_.get());
322
323  profile_params_.reset();
324  initialized_ = true;
325}
326
327void ProfileIOData::ApplyProfileParamsToContext(
328    ChromeURLRequestContext* context) const {
329  context->set_is_incognito(profile_params_->is_incognito);
330  context->set_accept_language(profile_params_->accept_language);
331  context->set_accept_charset(profile_params_->accept_charset);
332  context->set_referrer_charset(profile_params_->referrer_charset);
333  context->set_user_script_dir_path(profile_params_->user_script_dir_path);
334  context->set_host_content_settings_map(
335      profile_params_->host_content_settings_map);
336  context->set_host_zoom_map(profile_params_->host_zoom_map);
337  context->set_transport_security_state(
338      profile_params_->transport_security_state);
339  context->set_ssl_config_service(profile_params_->ssl_config_service);
340  context->set_appcache_service(profile_params_->appcache_service);
341  context->set_blob_storage_context(profile_params_->blob_storage_context);
342  context->set_file_system_context(profile_params_->file_system_context);
343  context->set_extension_info_map(profile_params_->extension_info_map);
344  context->set_prerender_manager(profile_params_->prerender_manager);
345}
346
347void ProfileIOData::ShutdownOnUIThread() {
348  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
349  enable_referrers_.Destroy();
350}
351