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_impl_io_data.h"
6
7#include "base/command_line.h"
8#include "base/file_util.h"
9#include "base/logging.h"
10#include "base/stl_util-inl.h"
11#include "chrome/browser/io_thread.h"
12#include "chrome/browser/net/chrome_net_log.h"
13#include "chrome/browser/net/chrome_network_delegate.h"
14#include "chrome/browser/net/sqlite_persistent_cookie_store.h"
15#include "chrome/common/chrome_constants.h"
16#include "chrome/common/chrome_switches.h"
17#include "chrome/common/pref_names.h"
18#include "chrome/common/url_constants.h"
19#include "content/browser/browser_thread.h"
20#include "content/browser/resource_context.h"
21#include "net/ftp/ftp_network_layer.h"
22#include "net/http/http_cache.h"
23
24ProfileImplIOData::Handle::Handle(Profile* profile)
25    : io_data_(new ProfileImplIOData),
26      profile_(profile),
27      initialized_(false) {
28  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
29  DCHECK(profile);
30}
31
32ProfileImplIOData::Handle::~Handle() {
33  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
34  if (main_request_context_getter_)
35    main_request_context_getter_->CleanupOnUIThread();
36  if (media_request_context_getter_)
37    media_request_context_getter_->CleanupOnUIThread();
38  if (extensions_request_context_getter_)
39    extensions_request_context_getter_->CleanupOnUIThread();
40
41  // Clean up all isolated app request contexts.
42  for (ChromeURLRequestContextGetterMap::iterator iter =
43           app_request_context_getter_map_.begin();
44       iter != app_request_context_getter_map_.end();
45       ++iter) {
46    iter->second->CleanupOnUIThread();
47  }
48
49  io_data_->ShutdownOnUIThread();
50}
51
52void ProfileImplIOData::Handle::Init(const FilePath& cookie_path,
53                                     const FilePath& cache_path,
54                                     int cache_max_size,
55                                     const FilePath& media_cache_path,
56                                     int media_cache_max_size,
57                                     const FilePath& extensions_cookie_path,
58                                     const FilePath& app_path) {
59  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
60  DCHECK(!io_data_->lazy_params_.get());
61  LazyParams* lazy_params = new LazyParams;
62
63  lazy_params->cookie_path = cookie_path;
64  lazy_params->cache_path = cache_path;
65  lazy_params->cache_max_size = cache_max_size;
66  lazy_params->media_cache_path = media_cache_path;
67  lazy_params->media_cache_max_size = media_cache_max_size;
68  lazy_params->extensions_cookie_path = extensions_cookie_path;
69
70  io_data_->lazy_params_.reset(lazy_params);
71
72  // Keep track of isolated app path separately so we can use it on demand.
73  io_data_->app_path_ = app_path;
74}
75
76const content::ResourceContext&
77ProfileImplIOData::Handle::GetResourceContext() const {
78  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
79  LazyInitialize();
80  return io_data_->GetResourceContext();
81}
82
83scoped_refptr<ChromeURLRequestContextGetter>
84ProfileImplIOData::Handle::GetMainRequestContextGetter() const {
85  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
86  LazyInitialize();
87  if (!main_request_context_getter_) {
88    main_request_context_getter_ =
89        ChromeURLRequestContextGetter::CreateOriginal(
90            profile_, io_data_);
91  }
92  return main_request_context_getter_;
93}
94
95scoped_refptr<ChromeURLRequestContextGetter>
96ProfileImplIOData::Handle::GetMediaRequestContextGetter() const {
97  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
98  LazyInitialize();
99  if (!media_request_context_getter_) {
100    media_request_context_getter_ =
101        ChromeURLRequestContextGetter::CreateOriginalForMedia(
102            profile_, io_data_);
103  }
104  return media_request_context_getter_;
105}
106
107scoped_refptr<ChromeURLRequestContextGetter>
108ProfileImplIOData::Handle::GetExtensionsRequestContextGetter() const {
109  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
110  LazyInitialize();
111  if (!extensions_request_context_getter_) {
112    extensions_request_context_getter_ =
113        ChromeURLRequestContextGetter::CreateOriginalForExtensions(
114            profile_, io_data_);
115  }
116  return extensions_request_context_getter_;
117}
118
119scoped_refptr<ChromeURLRequestContextGetter>
120ProfileImplIOData::Handle::GetIsolatedAppRequestContextGetter(
121    const std::string& app_id) const {
122  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
123  DCHECK(!app_id.empty());
124  LazyInitialize();
125
126  // Keep a map of request context getters, one per requested app ID.
127  ChromeURLRequestContextGetterMap::iterator iter =
128      app_request_context_getter_map_.find(app_id);
129  if (iter != app_request_context_getter_map_.end())
130    return iter->second;
131
132  ChromeURLRequestContextGetter* context =
133      ChromeURLRequestContextGetter::CreateOriginalForIsolatedApp(
134          profile_, io_data_, app_id);
135  app_request_context_getter_map_[app_id] = context;
136
137  return context;
138}
139
140void ProfileImplIOData::Handle::LazyInitialize() const {
141  if (!initialized_) {
142    io_data_->InitializeProfileParams(profile_);
143    ChromeNetworkDelegate::InitializeReferrersEnabled(
144        io_data_->enable_referrers(), profile_->GetPrefs());
145    initialized_ = true;
146  }
147}
148
149ProfileImplIOData::LazyParams::LazyParams()
150    : cache_max_size(0),
151      media_cache_max_size(0) {}
152ProfileImplIOData::LazyParams::~LazyParams() {}
153
154ProfileImplIOData::ProfileImplIOData()
155    : ProfileIOData(false),
156      clear_local_state_on_exit_(false) {}
157ProfileImplIOData::~ProfileImplIOData() {
158  STLDeleteValues(&app_http_factory_map_);
159}
160
161void ProfileImplIOData::LazyInitializeInternal(
162    ProfileParams* profile_params) const {
163  // Keep track of clear_local_state_on_exit for isolated apps.
164  clear_local_state_on_exit_ = profile_params->clear_local_state_on_exit;
165
166  ChromeURLRequestContext* main_context = main_request_context();
167  ChromeURLRequestContext* extensions_context = extensions_request_context();
168  media_request_context_ = new RequestContext;
169
170  IOThread* const io_thread = profile_params->io_thread;
171  IOThread::Globals* const io_thread_globals = io_thread->globals();
172  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
173  bool record_mode = chrome::kRecordModeEnabled &&
174                     command_line.HasSwitch(switches::kRecordMode);
175  bool playback_mode = command_line.HasSwitch(switches::kPlaybackMode);
176
177  // Initialize context members.
178
179  ApplyProfileParamsToContext(main_context);
180  ApplyProfileParamsToContext(media_request_context_);
181  ApplyProfileParamsToContext(extensions_context);
182
183  main_context->set_cookie_policy(cookie_policy());
184  media_request_context_->set_cookie_policy(cookie_policy());
185  extensions_context->set_cookie_policy(cookie_policy());
186
187  main_context->set_net_log(io_thread->net_log());
188  media_request_context_->set_net_log(io_thread->net_log());
189  extensions_context->set_net_log(io_thread->net_log());
190
191  main_context->set_network_delegate(network_delegate());
192  media_request_context_->set_network_delegate(network_delegate());
193
194  main_context->set_host_resolver(
195      io_thread_globals->host_resolver.get());
196  media_request_context_->set_host_resolver(
197      io_thread_globals->host_resolver.get());
198  main_context->set_cert_verifier(
199      io_thread_globals->cert_verifier.get());
200  media_request_context_->set_cert_verifier(
201      io_thread_globals->cert_verifier.get());
202  main_context->set_dnsrr_resolver(
203      io_thread_globals->dnsrr_resolver.get());
204  media_request_context_->set_dnsrr_resolver(
205      io_thread_globals->dnsrr_resolver.get());
206  main_context->set_http_auth_handler_factory(
207      io_thread_globals->http_auth_handler_factory.get());
208  media_request_context_->set_http_auth_handler_factory(
209      io_thread_globals->http_auth_handler_factory.get());
210
211  main_context->set_dns_cert_checker(dns_cert_checker());
212  media_request_context_->set_dns_cert_checker(dns_cert_checker());
213
214  main_context->set_proxy_service(proxy_service());
215  media_request_context_->set_proxy_service(proxy_service());
216
217  net::HttpCache::DefaultBackend* main_backend =
218      new net::HttpCache::DefaultBackend(
219          net::DISK_CACHE,
220          lazy_params_->cache_path,
221          lazy_params_->cache_max_size,
222          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE));
223  net::HttpCache* main_cache = new net::HttpCache(
224      main_context->host_resolver(),
225      main_context->cert_verifier(),
226      main_context->dnsrr_resolver(),
227      main_context->dns_cert_checker(),
228      main_context->proxy_service(),
229      main_context->ssl_config_service(),
230      main_context->http_auth_handler_factory(),
231      main_context->network_delegate(),
232      main_context->net_log(),
233      main_backend);
234
235  net::HttpCache::DefaultBackend* media_backend =
236      new net::HttpCache::DefaultBackend(
237          net::MEDIA_CACHE, lazy_params_->media_cache_path,
238          lazy_params_->media_cache_max_size,
239          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE));
240  net::HttpNetworkSession* main_network_session = main_cache->GetSession();
241  net::HttpCache* media_cache =
242      new net::HttpCache(main_network_session, media_backend);
243
244  scoped_refptr<net::CookieStore> cookie_store = NULL;
245  if (record_mode || playback_mode) {
246    // Don't use existing cookies and use an in-memory store.
247    cookie_store = new net::CookieMonster(
248        NULL, profile_params->cookie_monster_delegate);
249    main_cache->set_mode(
250        record_mode ? net::HttpCache::RECORD : net::HttpCache::PLAYBACK);
251  }
252
253  // setup cookie store
254  if (!cookie_store) {
255    DCHECK(!lazy_params_->cookie_path.empty());
256
257    scoped_refptr<SQLitePersistentCookieStore> cookie_db =
258        new SQLitePersistentCookieStore(lazy_params_->cookie_path);
259    cookie_db->SetClearLocalStateOnExit(
260        profile_params->clear_local_state_on_exit);
261    cookie_store =
262        new net::CookieMonster(cookie_db.get(),
263                               profile_params->cookie_monster_delegate);
264  }
265
266  net::CookieMonster* extensions_cookie_store =
267      new net::CookieMonster(
268          new SQLitePersistentCookieStore(
269              lazy_params_->extensions_cookie_path), NULL);
270  // Enable cookies for devtools and extension URLs.
271  const char* schemes[] = {chrome::kChromeDevToolsScheme,
272                           chrome::kExtensionScheme};
273  extensions_cookie_store->SetCookieableSchemes(schemes, 2);
274
275  main_context->set_cookie_store(cookie_store);
276  media_request_context_->set_cookie_store(cookie_store);
277  extensions_context->set_cookie_store(
278      extensions_cookie_store);
279
280  main_http_factory_.reset(main_cache);
281  media_http_factory_.reset(media_cache);
282  main_context->set_http_transaction_factory(main_cache);
283  media_request_context_->set_http_transaction_factory(media_cache);
284
285  main_context->set_ftp_transaction_factory(
286      new net::FtpNetworkLayer(io_thread_globals->host_resolver.get()));
287
288  lazy_params_.reset();
289}
290
291scoped_refptr<ProfileIOData::RequestContext>
292ProfileImplIOData::InitializeAppRequestContext(
293    scoped_refptr<ChromeURLRequestContext> main_context,
294    const std::string& app_id) const {
295  scoped_refptr<ProfileIOData::RequestContext> context = new RequestContext;
296
297  // Copy most state from the main context.
298  context->CopyFrom(main_context);
299
300  FilePath app_path = app_path_.AppendASCII(app_id);
301  FilePath cookie_path = app_path.Append(chrome::kCookieFilename);
302  FilePath cache_path = app_path.Append(chrome::kCacheDirname);
303  // TODO(creis): Determine correct cache size.
304  int cache_max_size = 0;
305
306  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
307  bool record_mode = chrome::kRecordModeEnabled &&
308                     command_line.HasSwitch(switches::kRecordMode);
309  bool playback_mode = command_line.HasSwitch(switches::kPlaybackMode);
310
311  // Use a separate HTTP disk cache for isolated apps.
312  net::HttpCache::DefaultBackend* app_backend =
313      new net::HttpCache::DefaultBackend(
314          net::DISK_CACHE,
315          cache_path,
316          cache_max_size,
317          BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE));
318  net::HttpNetworkSession* main_network_session =
319      main_http_factory_->GetSession();
320  net::HttpCache* app_http_cache =
321      new net::HttpCache(main_network_session, app_backend);
322
323  scoped_refptr<net::CookieStore> cookie_store = NULL;
324  if (record_mode || playback_mode) {
325    // Don't use existing cookies and use an in-memory store.
326    // TODO(creis): We should have a cookie delegate for notifying the cookie
327    // extensions API, but we need to update it to understand isolated apps
328    // first.
329    cookie_store = new net::CookieMonster(NULL, NULL);
330    app_http_cache->set_mode(
331        record_mode ? net::HttpCache::RECORD : net::HttpCache::PLAYBACK);
332  }
333
334  // Use an app-specific cookie store.
335  if (!cookie_store) {
336    DCHECK(!cookie_path.empty());
337
338    scoped_refptr<SQLitePersistentCookieStore> cookie_db =
339        new SQLitePersistentCookieStore(cookie_path);
340    cookie_db->SetClearLocalStateOnExit(clear_local_state_on_exit_);
341    // TODO(creis): We should have a cookie delegate for notifying the cookie
342    // extensions API, but we need to update it to understand isolated apps
343    // first.
344    cookie_store = new net::CookieMonster(cookie_db.get(), NULL);
345  }
346
347  context->set_cookie_store(cookie_store);
348
349  // Keep track of app_http_cache to delete it when we go away.
350  DCHECK(!app_http_factory_map_[app_id]);
351  app_http_factory_map_[app_id] = app_http_cache;
352  context->set_http_transaction_factory(app_http_cache);
353
354  return context;
355}
356
357scoped_refptr<ChromeURLRequestContext>
358ProfileImplIOData::AcquireMediaRequestContext() const {
359  DCHECK(media_request_context_);
360  scoped_refptr<ChromeURLRequestContext> context = media_request_context_;
361  media_request_context_->set_profile_io_data(this);
362  media_request_context_ = NULL;
363  return context;
364}
365
366scoped_refptr<ChromeURLRequestContext>
367ProfileImplIOData::AcquireIsolatedAppRequestContext(
368    scoped_refptr<ChromeURLRequestContext> main_context,
369    const std::string& app_id) const {
370  // We create per-app contexts on demand, unlike the others above.
371  scoped_refptr<RequestContext> app_request_context =
372      InitializeAppRequestContext(main_context, app_id);
373  DCHECK(app_request_context);
374  app_request_context->set_profile_io_data(this);
375  return app_request_context;
376}
377