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