blob: d8f2f6439f39634af1a99cfe1f150b4771a6a09a [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 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.
initial.commit09911bf2008-07-26 23:55:294
5#include "chrome/browser/profile.h"
6
7#include "base/command_line.h"
[email protected]04fba9a92008-10-28 17:25:258#include "base/file_path.h"
initial.commit09911bf2008-07-26 23:55:299#include "base/file_util.h"
10#include "base/lock.h"
11#include "base/path_service.h"
12#include "base/scoped_ptr.h"
13#include "base/string_util.h"
14#include "base/values.h"
[email protected]e7244d82008-10-29 18:13:2615#include "chrome/app/locales/locale_settings.h"
[email protected]d8e41ed2008-09-11 15:22:3216#include "chrome/browser/bookmarks/bookmark_model.h"
initial.commit09911bf2008-07-26 23:55:2917#include "chrome/browser/browser_list.h"
18#include "chrome/browser/browser_process.h"
[email protected]cdaa8652008-09-13 02:48:5919#include "chrome/browser/download/download_manager.h"
[email protected]6014d672008-12-05 00:38:2520#include "chrome/browser/extensions/extensions_service.h"
[email protected]04fba9a92008-10-28 17:25:2521#include "chrome/browser/greasemonkey_master.h"
initial.commit09911bf2008-07-26 23:55:2922#include "chrome/browser/history/history.h"
23#include "chrome/browser/navigation_controller.h"
24#include "chrome/browser/profile_manager.h"
25#include "chrome/browser/render_process_host.h"
[email protected]169627b2008-12-06 19:30:1926#include "chrome/browser/sessions/session_service.h"
27#include "chrome/browser/sessions/tab_restore_service.h"
initial.commit09911bf2008-07-26 23:55:2928#include "chrome/browser/spellchecker.h"
initial.commit09911bf2008-07-26 23:55:2929#include "chrome/browser/template_url_fetcher.h"
30#include "chrome/browser/template_url_model.h"
31#include "chrome/browser/visitedlink_master.h"
32#include "chrome/browser/webdata/web_data_service.h"
33#include "chrome/common/chrome_constants.h"
34#include "chrome/common/chrome_paths.h"
35#include "chrome/common/chrome_switches.h"
36#include "chrome/common/net/cookie_monster_sqlite.h"
37#include "chrome/common/notification_service.h"
38#include "chrome/common/pref_names.h"
39#include "chrome/common/pref_service.h"
40#include "chrome/common/resource_bundle.h"
41#include "net/base/cookie_monster.h"
42#include "net/base/cookie_policy.h"
43#include "net/http/http_cache.h"
[email protected]928fb582008-08-11 15:40:2344#include "net/proxy/proxy_service.h"
initial.commit09911bf2008-07-26 23:55:2945#include "net/url_request/url_request_context.h"
46#include "webkit/glue/webkit_glue.h"
47
[email protected]e1acf6f2008-10-27 20:43:3348using base::Time;
49using base::TimeDelta;
50
initial.commit09911bf2008-07-26 23:55:2951// Delay, in milliseconds, before we explicitly create the SessionService.
52static const int kCreateSessionServiceDelayMS = 500;
53
54// A pointer to the request context for the default profile. See comments on
55// Profile::GetDefaultRequestContext.
56URLRequestContext* Profile::default_request_context_;
57
58//static
59void Profile::RegisterUserPrefs(PrefService* prefs) {
[email protected]430d3f72008-10-27 17:56:5560 prefs->RegisterBooleanPref(prefs::kSearchSuggestEnabled, true);
initial.commit09911bf2008-07-26 23:55:2961 prefs->RegisterBooleanPref(prefs::kSessionExitedCleanly, true);
62 prefs->RegisterBooleanPref(prefs::kSafeBrowsingEnabled, true);
[email protected]e7244d82008-10-29 18:13:2663 prefs->RegisterLocalizedStringPref(prefs::kSpellCheckDictionary,
64 IDS_SPELLCHECK_DICTIONARY);
65 prefs->RegisterBooleanPref(prefs::kEnableSpellCheck, true);
initial.commit09911bf2008-07-26 23:55:2966}
67
68//static
69Profile* Profile::CreateProfile(const std::wstring& path) {
70 return new ProfileImpl(path);
71}
72
73//static
74URLRequestContext* Profile::GetDefaultRequestContext() {
75 return default_request_context_;
76}
77
78
79// Sets up proxy info if it was specified, otherwise returns NULL. The
80// returned pointer MUST be deleted by the caller if non-NULL.
[email protected]928fb582008-08-11 15:40:2381static net::ProxyInfo* CreateProxyInfo(const CommandLine& command_line) {
82 net::ProxyInfo* proxy_info = NULL;
initial.commit09911bf2008-07-26 23:55:2983
84 if (command_line.HasSwitch(switches::kProxyServer)) {
[email protected]928fb582008-08-11 15:40:2385 proxy_info = new net::ProxyInfo();
initial.commit09911bf2008-07-26 23:55:2986 const std::wstring& proxy_server =
87 command_line.GetSwitchValue(switches::kProxyServer);
[email protected]82f954e2008-08-12 05:11:3888 proxy_info->UseNamedProxy(WideToASCII(proxy_server));
initial.commit09911bf2008-07-26 23:55:2989 }
90
91 return proxy_info;
92}
93
94// Releases the URLRequestContext and sends out a notification about it.
95// Note: runs on IO thread.
96static void ReleaseURLRequestContext(URLRequestContext* context) {
97 NotificationService::current()->Notify(NOTIFY_URL_REQUEST_CONTEXT_RELEASED,
98 Source<URLRequestContext>(context),
99 NotificationService::NoDetails());
100 context->Release();
101}
102
103// A context for URLRequests issued relative to this profile.
104class ProfileImpl::RequestContext : public URLRequestContext,
105 public NotificationObserver {
106 public:
107 // |cookie_store_path| is the local disk path at which the cookie store
108 // is persisted.
109 RequestContext(const std::wstring& cookie_store_path,
110 const std::wstring& disk_cache_path,
111 PrefService* prefs)
112 : prefs_(prefs) {
113 cookie_store_ = NULL;
114
115 // setup user agent
[email protected]f5db58d2008-09-24 20:48:29116 user_agent_ = webkit_glue::GetUserAgent();
initial.commit09911bf2008-07-26 23:55:29117 // set up Accept-Language and Accept-Charset header values
118 // TODO(jungshik) : This may slow down http requests. Perhaps,
119 // we have to come up with a better way to set up these values.
120 accept_language_ = WideToASCII(prefs_->GetString(prefs::kAcceptLanguages));
121 accept_charset_ = WideToASCII(prefs_->GetString(prefs::kDefaultCharset));
122 accept_charset_ += ",*,utf-8";
123
124 CommandLine command_line;
125
[email protected]928fb582008-08-11 15:40:23126 scoped_ptr<net::ProxyInfo> proxy_info(CreateProxyInfo(command_line));
[email protected]63de95b2008-12-10 04:11:27127 proxy_service_ = net::ProxyService::Create(proxy_info.get());
128
initial.commit09911bf2008-07-26 23:55:29129 net::HttpCache* cache =
[email protected]63de95b2008-12-10 04:11:27130 new net::HttpCache(proxy_service_, disk_cache_path, 0);
initial.commit09911bf2008-07-26 23:55:29131
132 bool record_mode = chrome::kRecordModeEnabled &&
133 CommandLine().HasSwitch(switches::kRecordMode);
134 bool playback_mode = CommandLine().HasSwitch(switches::kPlaybackMode);
135
136 if (record_mode || playback_mode) {
137 // Don't use existing cookies and use an in-memory store.
[email protected]8ac1a752008-07-31 19:40:37138 cookie_store_ = new net::CookieMonster();
initial.commit09911bf2008-07-26 23:55:29139 cache->set_mode(
140 record_mode ? net::HttpCache::RECORD : net::HttpCache::PLAYBACK);
141 }
142 http_transaction_factory_ = cache;
143
144 // setup cookie store
145 if (!cookie_store_) {
146 DCHECK(!cookie_store_path.empty());
147 cookie_db_.reset(new SQLitePersistentCookieStore(
148 cookie_store_path, g_browser_process->db_thread()->message_loop()));
[email protected]8ac1a752008-07-31 19:40:37149 cookie_store_ = new net::CookieMonster(cookie_db_.get());
initial.commit09911bf2008-07-26 23:55:29150 }
151
[email protected]8ac1a752008-07-31 19:40:37152 cookie_policy_.SetType(net::CookiePolicy::FromInt(
153 prefs_->GetInteger(prefs::kCookieBehavior)));
initial.commit09911bf2008-07-26 23:55:29154
155 // The first request context to be created is the one for the default
156 // profile - at least until we support multiple profiles.
157 if (!default_request_context_)
158 default_request_context_ = this;
[email protected]3dd1f6d52008-09-15 18:28:09159 NotificationService::current()->Notify(
160 NOTIFY_DEFAULT_REQUEST_CONTEXT_AVAILABLE,
161 NotificationService::AllSources(), NotificationService::NoDetails());
initial.commit09911bf2008-07-26 23:55:29162
163 // Register for notifications about prefs.
164 prefs_->AddPrefObserver(prefs::kAcceptLanguages, this);
165 prefs_->AddPrefObserver(prefs::kCookieBehavior, this);
166 }
167
168 // NotificationObserver implementation.
169 virtual void Observe(NotificationType type,
170 const NotificationSource& source,
171 const NotificationDetails& details) {
172 if (NOTIFY_PREF_CHANGED == type) {
173 std::wstring* pref_name_in = Details<std::wstring>(details).ptr();
174 PrefService* prefs = Source<PrefService>(source).ptr();
175 DCHECK(pref_name_in && prefs);
176 if (*pref_name_in == prefs::kAcceptLanguages) {
177 std::string accept_language =
178 WideToASCII(prefs->GetString(prefs::kAcceptLanguages));
179 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
180 NewRunnableMethod(this,
181 &RequestContext::OnAcceptLanguageChange,
182 accept_language));
183 } else if (*pref_name_in == prefs::kCookieBehavior) {
[email protected]8ac1a752008-07-31 19:40:37184 net::CookiePolicy::Type type = net::CookiePolicy::FromInt(
185 prefs_->GetInteger(prefs::kCookieBehavior));
initial.commit09911bf2008-07-26 23:55:29186 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
187 NewRunnableMethod(this,
188 &RequestContext::OnCookiePolicyChange,
189 type));
190 }
191 }
192 }
193
194 // Since ProfileImpl::RequestContext will be destroyed on IO thread, but all
195 // PrefService observers are needed to clear in before destroying ProfileImpl.
196 // So we use to CleanupBeforeDestroy to do this thing. This function need to
197 // be called on destructor of ProfileImpl.
198 void CleanupBeforeDestroy() {
199 // Unregister for pref notifications.
200 prefs_->RemovePrefObserver(prefs::kAcceptLanguages, this);
201 prefs_->RemovePrefObserver(prefs::kCookieBehavior, this);
202 prefs_ = NULL;
203 }
204
205 void OnAcceptLanguageChange(std::string accept_language) {
206 DCHECK(MessageLoop::current() ==
207 ChromeThread::GetMessageLoop(ChromeThread::IO));
208 accept_language_ = accept_language;
209 }
210
[email protected]8ac1a752008-07-31 19:40:37211 void OnCookiePolicyChange(net::CookiePolicy::Type type) {
initial.commit09911bf2008-07-26 23:55:29212 DCHECK(MessageLoop::current() ==
213 ChromeThread::GetMessageLoop(ChromeThread::IO));
214 cookie_policy_.SetType(type);
215 }
216
217 virtual ~RequestContext() {
218 DCHECK(NULL == prefs_);
219 delete cookie_store_;
220 delete http_transaction_factory_;
[email protected]63de95b2008-12-10 04:11:27221 delete proxy_service_;
initial.commit09911bf2008-07-26 23:55:29222
223 if (default_request_context_ == this)
224 default_request_context_ = NULL;
225 }
226
227 private:
228 scoped_ptr<SQLitePersistentCookieStore> cookie_db_;
229 PrefService* prefs_;
230};
231
232////////////////////////////////////////////////////////////////////////////////
233//
234// An URLRequestContext proxy for OffTheRecord. This request context is
235// really a proxy to the original profile's request context but set
236// is_off_the_record_ to true.
237//
238// TODO(ACW). Do we need to share the FtpAuthCache with the real request context
239// see bug 974328
240//
241// TODO(jackson): http://b/issue?id=1197350 Remove duplicated code from above.
242//
243////////////////////////////////////////////////////////////////////////////////
244class OffTheRecordRequestContext : public URLRequestContext,
245 public NotificationObserver {
246 public:
247 explicit OffTheRecordRequestContext(Profile* profile) {
248 DCHECK(!profile->IsOffTheRecord());
249 prefs_ = profile->GetPrefs();
250 DCHECK(prefs_);
251
252 // The OffTheRecordRequestContext is owned by the OffTheRecordProfileImpl
253 // which is itself owned by the original profile. We reference the original
254 // context to make sure it doesn't go away when we delete the object graph.
255 original_context_ = profile->GetRequestContext();
256
[email protected]63de95b2008-12-10 04:11:27257 // Share the same proxy service as the original profile. This proxy
258 // service's lifespan is dependent on the lifespan of the original profile,
259 // which we reference (see above).
260 proxy_service_ = original_context_->proxy_service();
initial.commit09911bf2008-07-26 23:55:29261
[email protected]63de95b2008-12-10 04:11:27262 http_transaction_factory_ = new net::HttpCache(proxy_service_, 0);
[email protected]8ac1a752008-07-31 19:40:37263 cookie_store_ = new net::CookieMonster;
264 cookie_policy_.SetType(net::CookiePolicy::FromInt(
265 prefs_->GetInteger(prefs::kCookieBehavior)));
initial.commit09911bf2008-07-26 23:55:29266 user_agent_ = original_context_->user_agent();
267 accept_language_ = original_context_->accept_language();
268 accept_charset_ = original_context_->accept_charset();
269 is_off_the_record_ = true;
270
271 // Register for notifications about prefs.
272 prefs_->AddPrefObserver(prefs::kAcceptLanguages, this);
273 prefs_->AddPrefObserver(prefs::kCookieBehavior, this);
274 }
275
276 // Since OffTheRecordProfileImpl maybe be destroyed after destroying
277 // PrefService, but all PrefService observers are needed to clear in
278 // before destroying PrefService. So we use to CleanupBeforeDestroy
279 // to do this thing. This function need to be called on destructor
280 // of ProfileImpl.
281 void CleanupBeforeDestroy() {
282 // Unregister for pref notifications.
283 prefs_->RemovePrefObserver(prefs::kAcceptLanguages, this);
284 prefs_->RemovePrefObserver(prefs::kCookieBehavior, this);
285 prefs_ = NULL;
286 }
287
288 // NotificationObserver implementation.
289 virtual void Observe(NotificationType type,
290 const NotificationSource& source,
291 const NotificationDetails& details) {
292 if (NOTIFY_PREF_CHANGED == type) {
293 std::wstring* pref_name_in = Details<std::wstring>(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 WideToASCII(prefs->GetString(prefs::kAcceptLanguages));
299 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
300 NewRunnableMethod(
301 this,
302 &OffTheRecordRequestContext::OnAcceptLanguageChange,
303 accept_language));
304 } else if (*pref_name_in == prefs::kCookieBehavior) {
[email protected]8ac1a752008-07-31 19:40:37305 net::CookiePolicy::Type type = net::CookiePolicy::FromInt(
306 prefs_->GetInteger(prefs::kCookieBehavior));
initial.commit09911bf2008-07-26 23:55:29307 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
308 NewRunnableMethod(this,
309 &OffTheRecordRequestContext::OnCookiePolicyChange,
310 type));
311 }
312 }
313 }
314
315 void OnAcceptLanguageChange(std::string accept_language) {
316 DCHECK(MessageLoop::current() ==
317 ChromeThread::GetMessageLoop(ChromeThread::IO));
318 accept_language_ = accept_language;
319 }
320
[email protected]8ac1a752008-07-31 19:40:37321 void OnCookiePolicyChange(net::CookiePolicy::Type type) {
initial.commit09911bf2008-07-26 23:55:29322 DCHECK(MessageLoop::current() ==
323 ChromeThread::GetMessageLoop(ChromeThread::IO));
324 cookie_policy_.SetType(type);
325 }
326
327 virtual ~OffTheRecordRequestContext() {
328 DCHECK(NULL == prefs_);
329 delete cookie_store_;
330 delete http_transaction_factory_;
[email protected]63de95b2008-12-10 04:11:27331 // NOTE: do not delete |proxy_service_| as is owned by the original profile.
332
initial.commit09911bf2008-07-26 23:55:29333 // The OffTheRecordRequestContext simply act as a proxy to the real context.
334 // There is nothing else to delete.
335 }
336
337 private:
338 // The original (non off the record) URLRequestContext.
339 scoped_refptr<URLRequestContext> original_context_;
340 PrefService* prefs_;
341
342 DISALLOW_EVIL_CONSTRUCTORS(OffTheRecordRequestContext);
343};
344
345////////////////////////////////////////////////////////////////////////////////
346//
347// OffTheRecordProfileImpl is a profile subclass that wraps an existing profile
348// to make it suitable for the off the record mode.
349//
350////////////////////////////////////////////////////////////////////////////////
351class OffTheRecordProfileImpl : public Profile,
352 public NotificationObserver {
353 public:
354 explicit OffTheRecordProfileImpl(Profile* real_profile)
355 : profile_(real_profile),
356 start_time_(Time::Now()) {
357 request_context_ = new OffTheRecordRequestContext(real_profile);
358 request_context_->AddRef();
359 // Register for browser close notifications so we can detect when the last
360 // off-the-record window is closed, in which case we can clean our states
361 // (cookies, downloads...).
362 NotificationService::current()->AddObserver(
363 this, NOTIFY_BROWSER_CLOSED, NotificationService::AllSources());
364 }
365
366 virtual ~OffTheRecordProfileImpl() {
367 if (request_context_) {
368 request_context_->CleanupBeforeDestroy();
369 // Clean up request context on IO thread.
370 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
371 NewRunnableFunction(&ReleaseURLRequestContext, request_context_));
372 request_context_ = NULL;
373 }
374 NotificationService::current()->RemoveObserver(
375 this, NOTIFY_BROWSER_CLOSED, NotificationService::AllSources());
376 }
377
378 virtual std::wstring GetPath() { return profile_->GetPath(); }
379
380 virtual bool IsOffTheRecord() {
381 return true;
382 }
383
384 virtual Profile* GetOffTheRecordProfile() {
385 return this;
386 }
387
388 virtual Profile* GetOriginalProfile() {
389 return profile_;
390 }
391
392 virtual VisitedLinkMaster* GetVisitedLinkMaster() {
393 return profile_->GetVisitedLinkMaster();
394 }
395
[email protected]6014d672008-12-05 00:38:25396 virtual ExtensionsService* GetExtensionsService() {
397 return profile_->GetExtensionsService();
398 }
399
[email protected]04fba9a92008-10-28 17:25:25400 virtual GreasemonkeyMaster* GetGreasemonkeyMaster() {
401 return profile_->GetGreasemonkeyMaster();
402 }
403
initial.commit09911bf2008-07-26 23:55:29404 virtual HistoryService* GetHistoryService(ServiceAccessType sat) {
405 if (sat == EXPLICIT_ACCESS) {
406 return profile_->GetHistoryService(sat);
407 } else {
408 NOTREACHED() << "This profile is OffTheRecord";
409 return NULL;
410 }
411 }
412
initial.commit09911bf2008-07-26 23:55:29413 virtual WebDataService* GetWebDataService(ServiceAccessType sat) {
414 if (sat == EXPLICIT_ACCESS) {
415 return profile_->GetWebDataService(sat);
416 } else {
417 NOTREACHED() << "This profile is OffTheRecord";
418 return NULL;
419 }
420 }
421
422 virtual PrefService* GetPrefs() {
423 return profile_->GetPrefs();
424 }
425
426 virtual TemplateURLModel* GetTemplateURLModel() {
427 return profile_->GetTemplateURLModel();
428 }
429
430 virtual TemplateURLFetcher* GetTemplateURLFetcher() {
431 return profile_->GetTemplateURLFetcher();
432 }
433
434 virtual DownloadManager* GetDownloadManager() {
435 if (!download_manager_.get()) {
436 scoped_refptr<DownloadManager> dlm(new DownloadManager);
437 dlm->Init(this);
438 download_manager_.swap(dlm);
439 }
440 return download_manager_.get();
441 }
442
443 virtual bool HasCreatedDownloadManager() const {
444 return (download_manager_.get() != NULL);
445 }
446
447 virtual URLRequestContext* GetRequestContext() {
448 return request_context_;
449 }
450
451 virtual SessionService* GetSessionService() {
452 // Don't save any sessions when off the record.
453 return NULL;
454 }
455
456 virtual void ShutdownSessionService() {
457 // We don't allow a session service, nothing to do.
458 }
459
460 virtual bool HasSessionService() const {
461 // We never have a session service.
462 return false;
463 }
464
465 virtual std::wstring GetName() {
466 return profile_->GetName();
467 }
468
469 virtual void SetName(const std::wstring& name) {
470 profile_->SetName(name);
471 }
472
473 virtual std::wstring GetID() {
474 return profile_->GetID();
475 }
476
477 virtual void SetID(const std::wstring& id) {
478 profile_->SetID(id);
479 }
480
initial.commit09911bf2008-07-26 23:55:29481 virtual bool DidLastSessionExitCleanly() {
482 return profile_->DidLastSessionExitCleanly();
483 }
484
[email protected]d8e41ed2008-09-11 15:22:32485 virtual BookmarkModel* GetBookmarkModel() {
486 return profile_->GetBookmarkModel();
initial.commit09911bf2008-07-26 23:55:29487 }
488
[email protected]3a453fa2008-08-15 18:46:34489#ifdef CHROME_PERSONALIZATION
[email protected]57d3d0a2008-09-24 00:50:07490 virtual ProfilePersonalization* GetProfilePersonalization() {
[email protected]3a453fa2008-08-15 18:46:34491 return profile_->GetProfilePersonalization();
492 }
493#endif
494
initial.commit09911bf2008-07-26 23:55:29495 virtual bool IsSameProfile(Profile* profile) {
496 if (profile == static_cast<Profile*>(this))
497 return true;
498 return profile == profile_;
499 }
500
501 virtual Time GetStartTime() const {
502 return start_time_;
503 }
504
505 virtual TabRestoreService* GetTabRestoreService() {
506 return NULL;
507 }
508
[email protected]e7244d82008-10-29 18:13:26509 virtual void ResetTabRestoreService() {
[email protected]20930852008-10-15 19:30:41510 }
511
[email protected]e7244d82008-10-29 18:13:26512 virtual void ReinitializeSpellChecker() {
513 profile_->ReinitializeSpellChecker();
initial.commit09911bf2008-07-26 23:55:29514 }
515
516 virtual SpellChecker* GetSpellChecker() {
517 return profile_->GetSpellChecker();
518 }
519
520 virtual void MarkAsCleanShutdown() {
521 }
522
523 virtual void ExitedOffTheRecordMode() {
524 // Drop our download manager so we forget about all the downloads made
525 // in off-the-record mode.
526 download_manager_ = NULL;
527 }
528
529 virtual void Observe(NotificationType type,
530 const NotificationSource& source,
531 const NotificationDetails& details) {
532 DCHECK_EQ(NOTIFY_BROWSER_CLOSED, type);
533 // We are only interested in OTR browser closing.
534 if (Source<Browser>(source)->profile() != this)
535 return;
536
537 // Let's check if we still have an Off The Record window opened.
538 // Note that we check against 1 as this notification is sent before the
539 // browser window is actually removed from the list.
540 if (BrowserList::GetBrowserCount(this) <= 1)
541 ExitedOffTheRecordMode();
542 }
543
544 private:
545 // The real underlying profile.
546 Profile* profile_;
547
548 // A proxy to the real request context.
549 OffTheRecordRequestContext* request_context_;
550
551 // The download manager that only stores downloaded items in memory.
552 scoped_refptr<DownloadManager> download_manager_;
553
554 // Time we were started.
555 Time start_time_;
556
557 DISALLOW_EVIL_CONSTRUCTORS(OffTheRecordProfileImpl);
558};
559
560ProfileImpl::ProfileImpl(const std::wstring& path)
561 : path_(path),
562 off_the_record_(false),
[email protected]6014d672008-12-05 00:38:25563 extensions_service_(new ExtensionsService(FilePath(path))),
initial.commit09911bf2008-07-26 23:55:29564 history_service_created_(false),
565 created_web_data_service_(false),
566 created_download_manager_(false),
567 request_context_(NULL),
initial.commit09911bf2008-07-26 23:55:29568 start_time_(Time::Now()),
569 spellchecker_(NULL),
[email protected]3a453fa2008-08-15 18:46:34570#ifdef CHROME_PERSONALIZATION
571 personalization_(NULL),
572#endif
initial.commit09911bf2008-07-26 23:55:29573 shutdown_session_service_(false) {
574 DCHECK(!path.empty()) << "Using an empty path will attempt to write " <<
575 "profile files to the root directory!";
[email protected]2d316662008-09-03 18:18:14576 create_session_service_timer_.Start(
577 TimeDelta::FromMilliseconds(kCreateSessionServiceDelayMS), this,
578 &ProfileImpl::EnsureSessionServiceCreated);
[email protected]e7244d82008-10-29 18:13:26579 PrefService* prefs = GetPrefs();
580 prefs->AddPrefObserver(prefs::kSpellCheckDictionary, this);
581 prefs->AddPrefObserver(prefs::kEnableSpellCheck, this);
initial.commit09911bf2008-07-26 23:55:29582}
583
584ProfileImpl::~ProfileImpl() {
[email protected]169627b2008-12-06 19:30:19585 tab_restore_service_ = NULL;
initial.commit09911bf2008-07-26 23:55:29586
587 StopCreateSessionServiceTimer();
588 // TemplateURLModel schedules a task on the WebDataService from its
589 // destructor. Delete it first to ensure the task gets scheduled before we
590 // shut down the database.
591 template_url_model_.reset();
592
593 // The download manager queries the history system and should be deleted
594 // before the history is shutdown so it can properly cancel all requests.
595 download_manager_ = NULL;
596
[email protected]e7244d82008-10-29 18:13:26597 // Remove pref observers.
598 PrefService* prefs = GetPrefs();
599 prefs->RemovePrefObserver(prefs::kSpellCheckDictionary, this);
600 prefs->RemovePrefObserver(prefs::kEnableSpellCheck, this);
601
[email protected]3a453fa2008-08-15 18:46:34602#ifdef CHROME_PERSONALIZATION
[email protected]57d3d0a2008-09-24 00:50:07603 personalization_.reset();
[email protected]3a453fa2008-08-15 18:46:34604#endif
605
initial.commit09911bf2008-07-26 23:55:29606 // Both HistoryService and WebDataService maintain threads for background
607 // processing. Its possible each thread still has tasks on it that have
608 // increased the ref count of the service. In such a situation, when we
609 // decrement the refcount, it won't be 0, and the threads/databases aren't
610 // properly shut down. By explicitly calling Cleanup/Shutdown we ensure the
611 // databases are properly closed.
612 if (web_data_service_.get())
613 web_data_service_->Shutdown();
614
615 if (history_service_.get())
616 history_service_->Cleanup();
617
618 // The I/O thread may be NULL during testing.
[email protected]ab820df2008-08-26 05:55:10619 base::Thread* io_thread = g_browser_process->io_thread();
initial.commit09911bf2008-07-26 23:55:29620
621 if (spellchecker_) {
622 // The spellchecker must be deleted on the I/O thread. During testing, we
623 // don't have an I/O thread.
624 if (io_thread)
625 io_thread->message_loop()->ReleaseSoon(FROM_HERE, spellchecker_);
626 else
627 spellchecker_->Release();
628 }
629
630 if (request_context_) {
631 request_context_->CleanupBeforeDestroy();
632 // Clean up request context on IO thread.
633 io_thread->message_loop()->PostTask(FROM_HERE,
634 NewRunnableFunction(&ReleaseURLRequestContext, request_context_));
635 request_context_ = NULL;
636 }
637
[email protected]d8e41ed2008-09-11 15:22:32638 // HistoryService may call into the BookmarkModel, as such we need to
639 // delete HistoryService before the BookmarkModel. The destructor for
[email protected]90ef13132008-08-27 03:27:46640 // HistoryService will join with HistoryService's backend thread so that
641 // by the time the destructor has finished we're sure it will no longer call
[email protected]d8e41ed2008-09-11 15:22:32642 // into the BookmarkModel.
[email protected]90ef13132008-08-27 03:27:46643 history_service_ = NULL;
644 bookmark_bar_model_.reset();
645
initial.commit09911bf2008-07-26 23:55:29646 MarkAsCleanShutdown();
647}
648
649std::wstring ProfileImpl::GetPath() {
650 return path_;
651}
652
653bool ProfileImpl::IsOffTheRecord() {
654 return false;
655}
656
657Profile* ProfileImpl::GetOffTheRecordProfile() {
658 if (!off_the_record_profile_.get()) {
659 scoped_ptr<OffTheRecordProfileImpl> p(new OffTheRecordProfileImpl(this));
660 off_the_record_profile_.swap(p);
661 }
662 return off_the_record_profile_.get();
663}
664
665Profile* ProfileImpl::GetOriginalProfile() {
666 return this;
667}
668
[email protected]176aa482008-11-14 03:25:15669static void BroadcastNewHistoryTable(base::SharedMemory* table_memory) {
initial.commit09911bf2008-07-26 23:55:29670 if (!table_memory)
671 return;
672
673 // send to all RenderProcessHosts
674 for (RenderProcessHost::iterator i = RenderProcessHost::begin();
675 i != RenderProcessHost::end(); i++) {
676 if (!i->second->channel())
677 continue;
678
[email protected]176aa482008-11-14 03:25:15679 base::SharedMemoryHandle new_table;
[email protected]2f15de42008-11-11 22:35:19680 HANDLE process = i->second->process().handle();
initial.commit09911bf2008-07-26 23:55:29681 if (!process) {
682 // process can be null if it's started with the --single-process flag.
683 process = GetCurrentProcess();
684 }
685
686 table_memory->ShareToProcess(process, &new_table);
687 IPC::Message* msg = new ViewMsg_VisitedLink_NewTable(new_table);
688 i->second->channel()->Send(msg);
689 }
690}
691
692VisitedLinkMaster* ProfileImpl::GetVisitedLinkMaster() {
693 if (!visited_link_master_.get()) {
694 scoped_ptr<VisitedLinkMaster> visited_links(
695 new VisitedLinkMaster(g_browser_process->file_thread(),
696 BroadcastNewHistoryTable, this));
697 if (!visited_links->Init())
698 return NULL;
699 visited_link_master_.swap(visited_links);
700 }
701
702 return visited_link_master_.get();
703}
704
[email protected]6014d672008-12-05 00:38:25705ExtensionsService* ProfileImpl::GetExtensionsService() {
706 return extensions_service_.get();
707}
708
[email protected]04fba9a92008-10-28 17:25:25709GreasemonkeyMaster* ProfileImpl::GetGreasemonkeyMaster() {
710 if (!greasemonkey_master_.get()) {
711 std::wstring script_dir_str;
712 PathService::Get(chrome::DIR_USER_SCRIPTS, &script_dir_str);
713 FilePath script_dir(script_dir_str);
714 greasemonkey_master_ =
715 new GreasemonkeyMaster(g_browser_process->file_thread()->message_loop(),
716 script_dir);
717 }
718
719 return greasemonkey_master_.get();
720}
721
initial.commit09911bf2008-07-26 23:55:29722PrefService* ProfileImpl::GetPrefs() {
723 if (!prefs_.get()) {
724 prefs_.reset(new PrefService(GetPrefFilePath()));
725
726 // The Profile class and ProfileManager class may read some prefs so
727 // register known prefs as soon as possible.
728 Profile::RegisterUserPrefs(prefs_.get());
729 ProfileManager::RegisterUserPrefs(prefs_.get());
730
731 // The last session exited cleanly if there is no pref for
732 // kSessionExitedCleanly or the value for kSessionExitedCleanly is true.
733 last_session_exited_cleanly_ =
734 prefs_->GetBoolean(prefs::kSessionExitedCleanly);
735 // Mark the session as open.
736 prefs_->SetBoolean(prefs::kSessionExitedCleanly, false);
737 // Make sure we save to disk that the session has opened.
738 prefs_->ScheduleSavePersistentPrefs(g_browser_process->file_thread());
739 }
740
741 return prefs_.get();
742}
743
744std::wstring ProfileImpl::GetPrefFilePath() {
745 std::wstring pref_file_path = path_;
746 file_util::AppendToPath(&pref_file_path, chrome::kPreferencesFilename);
747 return pref_file_path;
748}
749
750URLRequestContext* ProfileImpl::GetRequestContext() {
751 if (!request_context_) {
752 std::wstring cookie_path = GetPath();
753 file_util::AppendToPath(&cookie_path, chrome::kCookieFilename);
754 std::wstring cache_path = GetPath();
755 file_util::AppendToPath(&cache_path, chrome::kCacheDirname);
[email protected]3dd1f6d52008-09-15 18:28:09756 request_context_ =
757 new ProfileImpl::RequestContext(cookie_path, cache_path, GetPrefs());
initial.commit09911bf2008-07-26 23:55:29758 request_context_->AddRef();
759
760 DCHECK(request_context_->cookie_store());
761 }
762
763 return request_context_;
764}
765
766HistoryService* ProfileImpl::GetHistoryService(ServiceAccessType sat) {
767 if (!history_service_created_) {
[email protected]90ef13132008-08-27 03:27:46768 history_service_created_ = true;
initial.commit09911bf2008-07-26 23:55:29769 scoped_refptr<HistoryService> history(new HistoryService(this));
[email protected]d8e41ed2008-09-11 15:22:32770 if (!history->Init(GetPath(), GetBookmarkModel()))
initial.commit09911bf2008-07-26 23:55:29771 return NULL;
772 history_service_.swap(history);
initial.commit09911bf2008-07-26 23:55:29773
774 // Send out the notification that the history service was created.
775 NotificationService::current()->
776 Notify(NOTIFY_HISTORY_CREATED, Source<Profile>(this),
777 Details<HistoryService>(history_service_.get()));
778 }
779 return history_service_.get();
780}
781
initial.commit09911bf2008-07-26 23:55:29782TemplateURLModel* ProfileImpl::GetTemplateURLModel() {
783 if (!template_url_model_.get())
784 template_url_model_.reset(new TemplateURLModel(this));
785 return template_url_model_.get();
786}
787
788TemplateURLFetcher* ProfileImpl::GetTemplateURLFetcher() {
789 if (!template_url_fetcher_.get())
790 template_url_fetcher_.reset(new TemplateURLFetcher(this));
791 return template_url_fetcher_.get();
792}
793
794WebDataService* ProfileImpl::GetWebDataService(ServiceAccessType sat) {
795 if (!created_web_data_service_)
796 CreateWebDataService();
797 return web_data_service_.get();
798}
799
800void ProfileImpl::CreateWebDataService() {
801 DCHECK(!created_web_data_service_ && web_data_service_.get() == NULL);
802 created_web_data_service_ = true;
803 scoped_refptr<WebDataService> wds(new WebDataService());
804 if (!wds->Init(GetPath()))
805 return;
806 web_data_service_.swap(wds);
807}
808
809DownloadManager* ProfileImpl::GetDownloadManager() {
810 if (!created_download_manager_) {
811 scoped_refptr<DownloadManager> dlm(new DownloadManager);
812 dlm->Init(this);
813 created_download_manager_ = true;
814 download_manager_.swap(dlm);
815 }
816 return download_manager_.get();
817}
818
819bool ProfileImpl::HasCreatedDownloadManager() const {
820 return created_download_manager_;
821}
822
823SessionService* ProfileImpl::GetSessionService() {
824 if (!session_service_.get() && !shutdown_session_service_) {
825 session_service_ = new SessionService(this);
826 session_service_->ResetFromCurrentBrowsers();
827 }
828 return session_service_.get();
829}
830
831void ProfileImpl::ShutdownSessionService() {
832 if (shutdown_session_service_)
833 return;
834
835 // We're about to exit, force creation of the session service if it hasn't
836 // been created yet. We do this to ensure session state matches the point in
837 // time the user exited.
838 GetSessionService();
839 shutdown_session_service_ = true;
840 session_service_ = NULL;
841}
842
843bool ProfileImpl::HasSessionService() const {
844 return (session_service_.get() != NULL);
845}
846
847std::wstring ProfileImpl::GetName() {
848 return GetPrefs()->GetString(prefs::kProfileName);
849}
850void ProfileImpl::SetName(const std::wstring& name) {
851 GetPrefs()->SetString(prefs::kProfileName, name);
852}
853
854std::wstring ProfileImpl::GetID() {
855 return GetPrefs()->GetString(prefs::kProfileID);
856}
857void ProfileImpl::SetID(const std::wstring& id) {
858 GetPrefs()->SetString(prefs::kProfileID, id);
859}
860
initial.commit09911bf2008-07-26 23:55:29861bool ProfileImpl::DidLastSessionExitCleanly() {
862 // last_session_exited_cleanly_ is set when the preferences are loaded. Force
863 // it to be set by asking for the prefs.
864 GetPrefs();
865 return last_session_exited_cleanly_;
866}
867
[email protected]d8e41ed2008-09-11 15:22:32868BookmarkModel* ProfileImpl::GetBookmarkModel() {
[email protected]90ef13132008-08-27 03:27:46869 if (!bookmark_bar_model_.get()) {
[email protected]d8e41ed2008-09-11 15:22:32870 bookmark_bar_model_.reset(new BookmarkModel(this));
[email protected]90ef13132008-08-27 03:27:46871 bookmark_bar_model_->Load();
872 }
initial.commit09911bf2008-07-26 23:55:29873 return bookmark_bar_model_.get();
874}
875
876bool ProfileImpl::IsSameProfile(Profile* profile) {
877 if (profile == static_cast<Profile*>(this))
878 return true;
879 OffTheRecordProfileImpl* otr_profile = off_the_record_profile_.get();
880 return otr_profile && profile == static_cast<Profile*>(otr_profile);
881}
882
883Time ProfileImpl::GetStartTime() const {
884 return start_time_;
885}
886
887TabRestoreService* ProfileImpl::GetTabRestoreService() {
888 if (!tab_restore_service_.get())
[email protected]169627b2008-12-06 19:30:19889 tab_restore_service_ = new TabRestoreService(this);
initial.commit09911bf2008-07-26 23:55:29890 return tab_restore_service_.get();
891}
892
893void ProfileImpl::ResetTabRestoreService() {
[email protected]169627b2008-12-06 19:30:19894 tab_restore_service_ = NULL;
initial.commit09911bf2008-07-26 23:55:29895}
896
[email protected]20930852008-10-15 19:30:41897// To be run in the IO thread to notify all resource message filters that the
898// spellchecker has changed.
899class NotifySpellcheckerChangeTask : public Task {
900 public:
901 NotifySpellcheckerChangeTask(
[email protected]e7244d82008-10-29 18:13:26902 Profile* profile,
903 const SpellcheckerReinitializedDetails& spellchecker)
[email protected]20930852008-10-15 19:30:41904 : profile_(profile),
905 spellchecker_(spellchecker) {
906 }
907
908 private:
909 void Run(void) {
910 NotificationService::current()->Notify(
911 NOTIFY_SPELLCHECKER_REINITIALIZED,
912 Source<Profile>(profile_),
913 Details<SpellcheckerReinitializedDetails>(&spellchecker_));
914 }
915
916 Profile* profile_;
917 SpellcheckerReinitializedDetails spellchecker_;
918};
919
[email protected]e7244d82008-10-29 18:13:26920void ProfileImpl::InitializeSpellChecker(bool need_to_broadcast) {
[email protected]20930852008-10-15 19:30:41921 // The I/O thread may be NULL during testing.
922 base::Thread* io_thread = g_browser_process->io_thread();
923 if (spellchecker_) {
924 // The spellchecker must be deleted on the I/O thread.
925 // A dummy variable to aid in logical clarity.
926 SpellChecker* last_spellchecker = spellchecker_;
927
928 if (io_thread)
929 io_thread->message_loop()->ReleaseSoon(FROM_HERE, last_spellchecker);
930 else // during testing, we don't have an I/O thread
931 last_spellchecker->Release();
[email protected]20930852008-10-15 19:30:41932 }
933
[email protected]20930852008-10-15 19:30:41934 // Retrieve the (perhaps updated recently) dictionary name from preferences.
935 PrefService* prefs = GetPrefs();
[email protected]e7244d82008-10-29 18:13:26936 bool enable_spellcheck = prefs->GetBoolean(prefs::kEnableSpellCheck);
[email protected]20930852008-10-15 19:30:41937
[email protected]e7244d82008-10-29 18:13:26938 if (enable_spellcheck) {
939 std::wstring dict_dir;
940 PathService::Get(chrome::DIR_APP_DICTIONARIES, &dict_dir);
[email protected]e7244d82008-10-29 18:13:26941 // Note that, as the object pointed to by previously by spellchecker_
942 // is being deleted in the io thread, the spellchecker_ can be made to point
943 // to a new object (RE-initialized) in parallel in this UI thread.
[email protected]4b4d1adc2008-12-10 22:28:58944 spellchecker_ = new SpellChecker(dict_dir,
945 prefs->GetString(prefs::kSpellCheckDictionary), GetRequestContext(),
946 std::wstring());
[email protected]e7244d82008-10-29 18:13:26947 spellchecker_->AddRef(); // Manual refcounting.
948 } else {
949 spellchecker_ = NULL;
950 }
[email protected]20930852008-10-15 19:30:41951
952 if (need_to_broadcast && io_thread) { // Notify resource message filters.
953 SpellcheckerReinitializedDetails scoped_spellchecker;
954 scoped_spellchecker.spellchecker = spellchecker_;
[email protected]e7244d82008-10-29 18:13:26955 if (io_thread) {
956 io_thread->message_loop()->PostTask(
957 FROM_HERE,
958 new NotifySpellcheckerChangeTask(this, scoped_spellchecker));
959 }
[email protected]20930852008-10-15 19:30:41960 }
961}
962
[email protected]e7244d82008-10-29 18:13:26963void ProfileImpl::ReinitializeSpellChecker() {
964 InitializeSpellChecker(true);
965}
966
initial.commit09911bf2008-07-26 23:55:29967SpellChecker* ProfileImpl::GetSpellChecker() {
968 if (!spellchecker_) {
[email protected]20930852008-10-15 19:30:41969 // This is where spellchecker gets initialized. Note that this is being
970 // initialized in the ui_thread. However, this is not a problem as long as
971 // it is *used* in the io thread.
972 // TODO (sidchat) One day, change everything so that spellchecker gets
973 // initialized in the IO thread itself.
[email protected]e7244d82008-10-29 18:13:26974 InitializeSpellChecker(false);
initial.commit09911bf2008-07-26 23:55:29975 }
[email protected]20930852008-10-15 19:30:41976
initial.commit09911bf2008-07-26 23:55:29977 return spellchecker_;
978}
979
980void ProfileImpl::MarkAsCleanShutdown() {
981 if (prefs_.get()) {
982 // The session cleanly exited, set kSessionExitedCleanly appropriately.
983 prefs_->SetBoolean(prefs::kSessionExitedCleanly, true);
984
985 // NOTE: If you change what thread this writes on, be sure and update
986 // ChromeFrame::EndSession().
987 prefs_->SavePersistentPrefs(g_browser_process->file_thread());
988 }
989}
990
[email protected]e7244d82008-10-29 18:13:26991void ProfileImpl::Observe(NotificationType type,
992 const NotificationSource& source,
993 const NotificationDetails& details) {
994 if (NOTIFY_PREF_CHANGED == type) {
995 std::wstring* pref_name_in = Details<std::wstring>(details).ptr();
996 PrefService* prefs = Source<PrefService>(source).ptr();
997 DCHECK(pref_name_in && prefs);
998 if (*pref_name_in == prefs::kSpellCheckDictionary ||
999 *pref_name_in == prefs::kEnableSpellCheck) {
1000 InitializeSpellChecker(true);
1001 }
1002 }
1003}
1004
initial.commit09911bf2008-07-26 23:55:291005void ProfileImpl::StopCreateSessionServiceTimer() {
[email protected]2d316662008-09-03 18:18:141006 create_session_service_timer_.Stop();
initial.commit09911bf2008-07-26 23:55:291007}
[email protected]3a453fa2008-08-15 18:46:341008
1009#ifdef CHROME_PERSONALIZATION
[email protected]57d3d0a2008-09-24 00:50:071010ProfilePersonalization* ProfileImpl::GetProfilePersonalization() {
1011 if (!personalization_.get())
1012 personalization_.reset(
1013 Personalization::CreateProfilePersonalization(this));
1014 return personalization_.get();
[email protected]3a453fa2008-08-15 18:46:341015}
license.botbf09a502008-08-24 00:55:551016#endif