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/net/chrome_url_request_context.h"
6
7#include "base/message_loop.h"
8#include "base/message_loop_proxy.h"
9#include "chrome/browser/browser_process.h"
10#include "chrome/browser/io_thread.h"
11#include "chrome/browser/profiles/profile.h"
12#include "chrome/browser/profiles/profile_io_data.h"
13#include "chrome/browser/ui/webui/chrome_url_data_manager_backend.h"
14#include "chrome/common/pref_names.h"
15#include "content/browser/browser_thread.h"
16#include "content/common/notification_service.h"
17#include "net/base/cookie_store.h"
18#include "net/ftp/ftp_transaction_factory.h"
19#include "net/http/http_transaction_factory.h"
20#include "net/http/http_util.h"
21#include "webkit/glue/webkit_glue.h"
22
23#if defined(USE_NSS)
24#include "net/ocsp/nss_ocsp.h"
25#endif
26
27class ChromeURLRequestContextFactory {
28 public:
29  ChromeURLRequestContextFactory() {}
30  virtual ~ChromeURLRequestContextFactory() {}
31
32  // Called to create a new instance (will only be called once).
33  virtual scoped_refptr<ChromeURLRequestContext> Create() = 0;
34
35 protected:
36  DISALLOW_COPY_AND_ASSIGN(ChromeURLRequestContextFactory);
37};
38
39namespace {
40
41// ----------------------------------------------------------------------------
42// Helper factories
43// ----------------------------------------------------------------------------
44
45// Factory that creates the main ChromeURLRequestContext.
46class FactoryForMain : public ChromeURLRequestContextFactory {
47 public:
48  explicit FactoryForMain(const ProfileIOData* profile_io_data)
49      : profile_io_data_(profile_io_data) {}
50
51  virtual scoped_refptr<ChromeURLRequestContext> Create() {
52    return profile_io_data_->GetMainRequestContext();
53  }
54
55 private:
56  const scoped_refptr<const ProfileIOData> profile_io_data_;
57};
58
59// Factory that creates the ChromeURLRequestContext for extensions.
60class FactoryForExtensions : public ChromeURLRequestContextFactory {
61 public:
62  explicit FactoryForExtensions(const ProfileIOData* profile_io_data)
63      : profile_io_data_(profile_io_data) {}
64
65  virtual scoped_refptr<ChromeURLRequestContext> Create() {
66    return profile_io_data_->GetExtensionsRequestContext();
67  }
68
69 private:
70  const scoped_refptr<const ProfileIOData> profile_io_data_;
71};
72
73// Factory that creates the ChromeURLRequestContext for a given isolated app.
74class FactoryForIsolatedApp : public ChromeURLRequestContextFactory {
75 public:
76  FactoryForIsolatedApp(const ProfileIOData* profile_io_data,
77                        const std::string& app_id,
78                        ChromeURLRequestContextGetter* main_context)
79      : profile_io_data_(profile_io_data),
80        app_id_(app_id),
81        main_request_context_getter_(main_context) {}
82
83  virtual scoped_refptr<ChromeURLRequestContext> Create() {
84    // We will copy most of the state from the main request context.
85    return profile_io_data_->GetIsolatedAppRequestContext(
86        main_request_context_getter_->GetIOContext(), app_id_);
87  }
88
89 private:
90  const scoped_refptr<const ProfileIOData> profile_io_data_;
91  const std::string app_id_;
92  scoped_refptr<ChromeURLRequestContextGetter>
93      main_request_context_getter_;
94};
95
96// Factory that creates the ChromeURLRequestContext for media.
97class FactoryForMedia : public ChromeURLRequestContextFactory {
98 public:
99  explicit FactoryForMedia(const ProfileIOData* profile_io_data)
100      : profile_io_data_(profile_io_data) {
101  }
102
103  virtual scoped_refptr<ChromeURLRequestContext> Create() {
104    return profile_io_data_->GetMediaRequestContext();
105  }
106
107 private:
108  const scoped_refptr<const ProfileIOData> profile_io_data_;
109};
110
111}  // namespace
112
113// ----------------------------------------------------------------------------
114// ChromeURLRequestContextGetter
115// ----------------------------------------------------------------------------
116
117ChromeURLRequestContextGetter::ChromeURLRequestContextGetter(
118    Profile* profile,
119    ChromeURLRequestContextFactory* factory)
120    : io_thread_(g_browser_process->io_thread()),
121      factory_(factory),
122      url_request_context_(NULL) {
123  DCHECK(factory);
124  DCHECK(profile);
125  RegisterPrefsObserver(profile);
126}
127
128ChromeURLRequestContextGetter::~ChromeURLRequestContextGetter() {
129  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
130
131  DCHECK(registrar_.IsEmpty()) << "Probably didn't call CleanupOnUIThread";
132
133  // Either we already transformed the factory into a net::URLRequestContext, or
134  // we still have a pending factory.
135  DCHECK((factory_.get() && !url_request_context_.get()) ||
136         (!factory_.get() && url_request_context_.get()));
137
138  if (url_request_context_)
139    io_thread_->UnregisterURLRequestContextGetter(this);
140
141  // The scoped_refptr / scoped_ptr destructors take care of releasing
142  // |factory_| and |url_request_context_| now.
143}
144
145// Lazily create a ChromeURLRequestContext using our factory.
146net::URLRequestContext* ChromeURLRequestContextGetter::GetURLRequestContext() {
147  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
148
149  if (!url_request_context_) {
150    DCHECK(factory_.get());
151    url_request_context_ = factory_->Create();
152    if (is_main()) {
153      url_request_context_->set_is_main(true);
154#if defined(USE_NSS)
155      // TODO(ukai): find a better way to set the net::URLRequestContext for
156      // OCSP.
157      net::SetURLRequestContextForOCSP(url_request_context_);
158#endif
159    }
160
161    factory_.reset();
162    io_thread_->RegisterURLRequestContextGetter(this);
163  }
164
165  return url_request_context_;
166}
167
168void ChromeURLRequestContextGetter::ReleaseURLRequestContext() {
169  DCHECK(url_request_context_);
170  url_request_context_ = NULL;
171}
172
173net::CookieStore* ChromeURLRequestContextGetter::DONTUSEME_GetCookieStore() {
174  // If we are running on the IO thread this is real easy.
175  if (BrowserThread::CurrentlyOn(BrowserThread::IO))
176    return GetURLRequestContext()->cookie_store();
177
178  // If we aren't running on the IO thread, we cannot call
179  // GetURLRequestContext(). Instead we will post a task to the IO loop
180  // and wait for it to complete.
181
182  base::WaitableEvent completion(false, false);
183  net::CookieStore* result = NULL;
184
185  BrowserThread::PostTask(
186      BrowserThread::IO, FROM_HERE,
187      NewRunnableMethod(this,
188          &ChromeURLRequestContextGetter::GetCookieStoreAsyncHelper,
189          &completion,
190          &result));
191
192  completion.Wait();
193  DCHECK(result);
194  return result;
195}
196
197scoped_refptr<base::MessageLoopProxy>
198ChromeURLRequestContextGetter::GetIOMessageLoopProxy() const {
199  return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
200}
201
202// static
203ChromeURLRequestContextGetter* ChromeURLRequestContextGetter::CreateOriginal(
204    Profile* profile,
205    const ProfileIOData* profile_io_data) {
206  DCHECK(!profile->IsOffTheRecord());
207  return new ChromeURLRequestContextGetter(
208      profile,
209      new FactoryForMain(profile_io_data));
210}
211
212// static
213ChromeURLRequestContextGetter*
214ChromeURLRequestContextGetter::CreateOriginalForMedia(
215    Profile* profile, const ProfileIOData* profile_io_data) {
216  DCHECK(!profile->IsOffTheRecord());
217  return new ChromeURLRequestContextGetter(
218      profile,
219      new FactoryForMedia(profile_io_data));
220}
221
222// static
223ChromeURLRequestContextGetter*
224ChromeURLRequestContextGetter::CreateOriginalForExtensions(
225    Profile* profile, const ProfileIOData* profile_io_data) {
226  DCHECK(!profile->IsOffTheRecord());
227  return new ChromeURLRequestContextGetter(
228      profile,
229      new FactoryForExtensions(profile_io_data));
230}
231
232// static
233ChromeURLRequestContextGetter*
234ChromeURLRequestContextGetter::CreateOriginalForIsolatedApp(
235    Profile* profile,
236    const ProfileIOData* profile_io_data,
237    const std::string& app_id) {
238  DCHECK(!profile->IsOffTheRecord());
239  ChromeURLRequestContextGetter* main_context =
240      static_cast<ChromeURLRequestContextGetter*>(profile->GetRequestContext());
241  return new ChromeURLRequestContextGetter(
242      profile,
243      new FactoryForIsolatedApp(profile_io_data, app_id, main_context));
244}
245
246// static
247ChromeURLRequestContextGetter*
248ChromeURLRequestContextGetter::CreateOffTheRecord(
249    Profile* profile, const ProfileIOData* profile_io_data) {
250  DCHECK(profile->IsOffTheRecord());
251  return new ChromeURLRequestContextGetter(
252      profile, new FactoryForMain(profile_io_data));
253}
254
255// static
256ChromeURLRequestContextGetter*
257ChromeURLRequestContextGetter::CreateOffTheRecordForExtensions(
258    Profile* profile, const ProfileIOData* profile_io_data) {
259  DCHECK(profile->IsOffTheRecord());
260  return new ChromeURLRequestContextGetter(
261      profile, new FactoryForExtensions(profile_io_data));
262}
263
264// static
265ChromeURLRequestContextGetter*
266ChromeURLRequestContextGetter::CreateOffTheRecordForIsolatedApp(
267    Profile* profile,
268    const ProfileIOData* profile_io_data,
269    const std::string& app_id) {
270  DCHECK(profile->IsOffTheRecord());
271  ChromeURLRequestContextGetter* main_context =
272      static_cast<ChromeURLRequestContextGetter*>(profile->GetRequestContext());
273  return new ChromeURLRequestContextGetter(
274      profile,
275      new FactoryForIsolatedApp(profile_io_data, app_id, main_context));
276}
277
278void ChromeURLRequestContextGetter::CleanupOnUIThread() {
279  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
280  // Unregister for pref notifications.
281  DCHECK(!registrar_.IsEmpty()) << "Called more than once!";
282  registrar_.RemoveAll();
283}
284
285// NotificationObserver implementation.
286void ChromeURLRequestContextGetter::Observe(
287    NotificationType type,
288    const NotificationSource& source,
289    const NotificationDetails& details) {
290  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
291
292  if (NotificationType::PREF_CHANGED == type) {
293    std::string* pref_name_in = Details<std::string>(details).ptr();
294    PrefService* prefs = Source<PrefService>(source).ptr();
295    DCHECK(pref_name_in && prefs);
296    if (*pref_name_in == prefs::kAcceptLanguages) {
297      std::string accept_language =
298          prefs->GetString(prefs::kAcceptLanguages);
299      BrowserThread::PostTask(
300          BrowserThread::IO, FROM_HERE,
301          NewRunnableMethod(
302              this,
303              &ChromeURLRequestContextGetter::OnAcceptLanguageChange,
304              accept_language));
305    } else if (*pref_name_in == prefs::kDefaultCharset) {
306      std::string default_charset =
307          prefs->GetString(prefs::kDefaultCharset);
308      BrowserThread::PostTask(
309          BrowserThread::IO, FROM_HERE,
310          NewRunnableMethod(
311              this,
312              &ChromeURLRequestContextGetter::OnDefaultCharsetChange,
313              default_charset));
314    } else if (*pref_name_in == prefs::kClearSiteDataOnExit) {
315      bool clear_site_data =
316          prefs->GetBoolean(prefs::kClearSiteDataOnExit);
317      BrowserThread::PostTask(
318          BrowserThread::IO, FROM_HERE,
319          NewRunnableMethod(
320              this,
321              &ChromeURLRequestContextGetter::OnClearSiteDataOnExitChange,
322              clear_site_data));
323    }
324  } else {
325    NOTREACHED();
326  }
327}
328
329void ChromeURLRequestContextGetter::RegisterPrefsObserver(Profile* profile) {
330  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
331
332  registrar_.Init(profile->GetPrefs());
333  registrar_.Add(prefs::kAcceptLanguages, this);
334  registrar_.Add(prefs::kDefaultCharset, this);
335  registrar_.Add(prefs::kClearSiteDataOnExit, this);
336}
337
338void ChromeURLRequestContextGetter::OnAcceptLanguageChange(
339    const std::string& accept_language) {
340  GetIOContext()->OnAcceptLanguageChange(accept_language);
341}
342
343void ChromeURLRequestContextGetter::OnDefaultCharsetChange(
344    const std::string& default_charset) {
345  GetIOContext()->OnDefaultCharsetChange(default_charset);
346}
347
348void ChromeURLRequestContextGetter::OnClearSiteDataOnExitChange(
349    bool clear_site_data) {
350  GetURLRequestContext()->cookie_store()->GetCookieMonster()->
351      SetClearPersistentStoreOnExit(clear_site_data);
352}
353
354void ChromeURLRequestContextGetter::GetCookieStoreAsyncHelper(
355    base::WaitableEvent* completion,
356    net::CookieStore** result) {
357  // Note that CookieStore is refcounted, yet we do not add a reference.
358  *result = GetURLRequestContext()->cookie_store();
359  completion->Signal();
360}
361
362// ----------------------------------------------------------------------------
363// ChromeURLRequestContext
364// ----------------------------------------------------------------------------
365
366ChromeURLRequestContext::ChromeURLRequestContext()
367    : is_incognito_(false) {
368  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
369}
370
371void ChromeURLRequestContext::CopyFrom(ChromeURLRequestContext* other) {
372  URLRequestContext::CopyFrom(other);
373
374  // Copy ChromeURLRequestContext parameters.
375  set_user_script_dir_path(other->user_script_dir_path());
376  set_appcache_service(other->appcache_service());
377  set_host_content_settings_map(other->host_content_settings_map());
378  set_host_zoom_map(other->host_zoom_map_);
379  set_blob_storage_context(other->blob_storage_context());
380  set_file_system_context(other->file_system_context());
381  set_extension_info_map(other->extension_info_map_);
382  set_prerender_manager(other->prerender_manager());
383  // ChromeURLDataManagerBackend is unique per context.
384  set_is_incognito(other->is_incognito());
385}
386
387ChromeURLDataManagerBackend*
388    ChromeURLRequestContext::GetChromeURLDataManagerBackend() {
389  if (!chrome_url_data_manager_backend_.get())
390    chrome_url_data_manager_backend_.reset(new ChromeURLDataManagerBackend());
391  return chrome_url_data_manager_backend_.get();
392}
393
394ChromeURLRequestContext::~ChromeURLRequestContext() {
395  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
396
397  if (appcache_service_.get() && appcache_service_->request_context() == this)
398    appcache_service_->set_request_context(NULL);
399
400#if defined(USE_NSS)
401  if (is_main()) {
402    net::URLRequestContext* ocsp_context = net::GetURLRequestContextForOCSP();
403    if (ocsp_context) {
404      DCHECK_EQ(this, ocsp_context);
405      // We are releasing the net::URLRequestContext used by OCSP handlers.
406      net::SetURLRequestContextForOCSP(NULL);
407    }
408  }
409#endif
410
411  NotificationService::current()->Notify(
412      NotificationType::URL_REQUEST_CONTEXT_RELEASED,
413      Source<net::URLRequestContext>(this),
414      NotificationService::NoDetails());
415
416  // cookie_policy_'s lifetime is auto-managed by chrome_cookie_policy_.  We
417  // null this out here to avoid a dangling reference to chrome_cookie_policy_
418  // when ~net::URLRequestContext runs.
419  set_cookie_policy(NULL);
420}
421
422const std::string& ChromeURLRequestContext::GetUserAgent(
423    const GURL& url) const {
424  return webkit_glue::GetUserAgent(url);
425}
426
427void ChromeURLRequestContext::OnAcceptLanguageChange(
428    const std::string& accept_language) {
429  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
430  set_accept_language(
431      net::HttpUtil::GenerateAcceptLanguageHeader(accept_language));
432}
433
434void ChromeURLRequestContext::OnDefaultCharsetChange(
435    const std::string& default_charset) {
436  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
437  set_referrer_charset(default_charset);
438  set_accept_charset(
439      net::HttpUtil::GenerateAcceptCharsetHeader(default_charset));
440}
441