blob: 76f2f168b65c1f92cdf4693ece754628cbf43529 [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"
26#include "chrome/browser/spellchecker.h"
27#include "chrome/browser/tab_restore_service.h"
28#include "chrome/browser/template_url_fetcher.h"
29#include "chrome/browser/template_url_model.h"
30#include "chrome/browser/visitedlink_master.h"
31#include "chrome/browser/webdata/web_data_service.h"
32#include "chrome/common/chrome_constants.h"
33#include "chrome/common/chrome_paths.h"
34#include "chrome/common/chrome_switches.h"
35#include "chrome/common/net/cookie_monster_sqlite.h"
36#include "chrome/common/notification_service.h"
37#include "chrome/common/pref_names.h"
38#include "chrome/common/pref_service.h"
39#include "chrome/common/resource_bundle.h"
40#include "net/base/cookie_monster.h"
41#include "net/base/cookie_policy.h"
42#include "net/http/http_cache.h"
[email protected]928fb582008-08-11 15:40:2343#include "net/proxy/proxy_service.h"
initial.commit09911bf2008-07-26 23:55:2944#include "net/url_request/url_request_context.h"
45#include "webkit/glue/webkit_glue.h"
46
[email protected]e1acf6f2008-10-27 20:43:3347using base::Time;
48using base::TimeDelta;
49
initial.commit09911bf2008-07-26 23:55:2950// Delay, in milliseconds, before we explicitly create the SessionService.
51static const int kCreateSessionServiceDelayMS = 500;
52
53// A pointer to the request context for the default profile. See comments on
54// Profile::GetDefaultRequestContext.
55URLRequestContext* Profile::default_request_context_;
56
57//static
58void Profile::RegisterUserPrefs(PrefService* prefs) {
[email protected]430d3f72008-10-27 17:56:5559 prefs->RegisterBooleanPref(prefs::kSearchSuggestEnabled, true);
initial.commit09911bf2008-07-26 23:55:2960 prefs->RegisterBooleanPref(prefs::kSessionExitedCleanly, true);
61 prefs->RegisterBooleanPref(prefs::kSafeBrowsingEnabled, true);
[email protected]e7244d82008-10-29 18:13:2662 prefs->RegisterLocalizedStringPref(prefs::kSpellCheckDictionary,
63 IDS_SPELLCHECK_DICTIONARY);
64 prefs->RegisterBooleanPref(prefs::kEnableSpellCheck, true);
initial.commit09911bf2008-07-26 23:55:2965}
66
67//static
68Profile* Profile::CreateProfile(const std::wstring& path) {
69 return new ProfileImpl(path);
70}
71
72//static
73URLRequestContext* Profile::GetDefaultRequestContext() {
74 return default_request_context_;
75}
76
77
78// Sets up proxy info if it was specified, otherwise returns NULL. The
79// returned pointer MUST be deleted by the caller if non-NULL.
[email protected]928fb582008-08-11 15:40:2380static net::ProxyInfo* CreateProxyInfo(const CommandLine& command_line) {
81 net::ProxyInfo* proxy_info = NULL;
initial.commit09911bf2008-07-26 23:55:2982
83 if (command_line.HasSwitch(switches::kProxyServer)) {
[email protected]928fb582008-08-11 15:40:2384 proxy_info = new net::ProxyInfo();
initial.commit09911bf2008-07-26 23:55:2985 const std::wstring& proxy_server =
86 command_line.GetSwitchValue(switches::kProxyServer);
[email protected]82f954e2008-08-12 05:11:3887 proxy_info->UseNamedProxy(WideToASCII(proxy_server));
initial.commit09911bf2008-07-26 23:55:2988 }
89
90 return proxy_info;
91}
92
93// Releases the URLRequestContext and sends out a notification about it.
94// Note: runs on IO thread.
95static void ReleaseURLRequestContext(URLRequestContext* context) {
96 NotificationService::current()->Notify(NOTIFY_URL_REQUEST_CONTEXT_RELEASED,
97 Source<URLRequestContext>(context),
98 NotificationService::NoDetails());
99 context->Release();
100}
101
102// A context for URLRequests issued relative to this profile.
103class ProfileImpl::RequestContext : public URLRequestContext,
104 public NotificationObserver {
105 public:
106 // |cookie_store_path| is the local disk path at which the cookie store
107 // is persisted.
108 RequestContext(const std::wstring& cookie_store_path,
109 const std::wstring& disk_cache_path,
110 PrefService* prefs)
111 : prefs_(prefs) {
112 cookie_store_ = NULL;
113
114 // setup user agent
[email protected]f5db58d2008-09-24 20:48:29115 user_agent_ = webkit_glue::GetUserAgent();
initial.commit09911bf2008-07-26 23:55:29116 // set up Accept-Language and Accept-Charset header values
117 // TODO(jungshik) : This may slow down http requests. Perhaps,
118 // we have to come up with a better way to set up these values.
119 accept_language_ = WideToASCII(prefs_->GetString(prefs::kAcceptLanguages));
120 accept_charset_ = WideToASCII(prefs_->GetString(prefs::kDefaultCharset));
121 accept_charset_ += ",*,utf-8";
122
123 CommandLine command_line;
124
[email protected]928fb582008-08-11 15:40:23125 scoped_ptr<net::ProxyInfo> proxy_info(CreateProxyInfo(command_line));
initial.commit09911bf2008-07-26 23:55:29126 net::HttpCache* cache =
127 new net::HttpCache(proxy_info.get(), disk_cache_path, 0);
128
129 bool record_mode = chrome::kRecordModeEnabled &&
130 CommandLine().HasSwitch(switches::kRecordMode);
131 bool playback_mode = CommandLine().HasSwitch(switches::kPlaybackMode);
132
133 if (record_mode || playback_mode) {
134 // Don't use existing cookies and use an in-memory store.
[email protected]8ac1a752008-07-31 19:40:37135 cookie_store_ = new net::CookieMonster();
initial.commit09911bf2008-07-26 23:55:29136 cache->set_mode(
137 record_mode ? net::HttpCache::RECORD : net::HttpCache::PLAYBACK);
138 }
139 http_transaction_factory_ = cache;
140
141 // setup cookie store
142 if (!cookie_store_) {
143 DCHECK(!cookie_store_path.empty());
144 cookie_db_.reset(new SQLitePersistentCookieStore(
145 cookie_store_path, g_browser_process->db_thread()->message_loop()));
[email protected]8ac1a752008-07-31 19:40:37146 cookie_store_ = new net::CookieMonster(cookie_db_.get());
initial.commit09911bf2008-07-26 23:55:29147 }
148
[email protected]8ac1a752008-07-31 19:40:37149 cookie_policy_.SetType(net::CookiePolicy::FromInt(
150 prefs_->GetInteger(prefs::kCookieBehavior)));
initial.commit09911bf2008-07-26 23:55:29151
152 // The first request context to be created is the one for the default
153 // profile - at least until we support multiple profiles.
154 if (!default_request_context_)
155 default_request_context_ = this;
[email protected]3dd1f6d52008-09-15 18:28:09156 NotificationService::current()->Notify(
157 NOTIFY_DEFAULT_REQUEST_CONTEXT_AVAILABLE,
158 NotificationService::AllSources(), NotificationService::NoDetails());
initial.commit09911bf2008-07-26 23:55:29159
160 // Register for notifications about prefs.
161 prefs_->AddPrefObserver(prefs::kAcceptLanguages, this);
162 prefs_->AddPrefObserver(prefs::kCookieBehavior, this);
163 }
164
165 // NotificationObserver implementation.
166 virtual void Observe(NotificationType type,
167 const NotificationSource& source,
168 const NotificationDetails& details) {
169 if (NOTIFY_PREF_CHANGED == type) {
170 std::wstring* pref_name_in = Details<std::wstring>(details).ptr();
171 PrefService* prefs = Source<PrefService>(source).ptr();
172 DCHECK(pref_name_in && prefs);
173 if (*pref_name_in == prefs::kAcceptLanguages) {
174 std::string accept_language =
175 WideToASCII(prefs->GetString(prefs::kAcceptLanguages));
176 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
177 NewRunnableMethod(this,
178 &RequestContext::OnAcceptLanguageChange,
179 accept_language));
180 } else if (*pref_name_in == prefs::kCookieBehavior) {
[email protected]8ac1a752008-07-31 19:40:37181 net::CookiePolicy::Type type = net::CookiePolicy::FromInt(
182 prefs_->GetInteger(prefs::kCookieBehavior));
initial.commit09911bf2008-07-26 23:55:29183 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
184 NewRunnableMethod(this,
185 &RequestContext::OnCookiePolicyChange,
186 type));
187 }
188 }
189 }
190
191 // Since ProfileImpl::RequestContext will be destroyed on IO thread, but all
192 // PrefService observers are needed to clear in before destroying ProfileImpl.
193 // So we use to CleanupBeforeDestroy to do this thing. This function need to
194 // be called on destructor of ProfileImpl.
195 void CleanupBeforeDestroy() {
196 // Unregister for pref notifications.
197 prefs_->RemovePrefObserver(prefs::kAcceptLanguages, this);
198 prefs_->RemovePrefObserver(prefs::kCookieBehavior, this);
199 prefs_ = NULL;
200 }
201
202 void OnAcceptLanguageChange(std::string accept_language) {
203 DCHECK(MessageLoop::current() ==
204 ChromeThread::GetMessageLoop(ChromeThread::IO));
205 accept_language_ = accept_language;
206 }
207
[email protected]8ac1a752008-07-31 19:40:37208 void OnCookiePolicyChange(net::CookiePolicy::Type type) {
initial.commit09911bf2008-07-26 23:55:29209 DCHECK(MessageLoop::current() ==
210 ChromeThread::GetMessageLoop(ChromeThread::IO));
211 cookie_policy_.SetType(type);
212 }
213
214 virtual ~RequestContext() {
215 DCHECK(NULL == prefs_);
216 delete cookie_store_;
217 delete http_transaction_factory_;
218
219 if (default_request_context_ == this)
220 default_request_context_ = NULL;
221 }
222
223 private:
224 scoped_ptr<SQLitePersistentCookieStore> cookie_db_;
225 PrefService* prefs_;
226};
227
228////////////////////////////////////////////////////////////////////////////////
229//
230// An URLRequestContext proxy for OffTheRecord. This request context is
231// really a proxy to the original profile's request context but set
232// is_off_the_record_ to true.
233//
234// TODO(ACW). Do we need to share the FtpAuthCache with the real request context
235// see bug 974328
236//
237// TODO(jackson): http://b/issue?id=1197350 Remove duplicated code from above.
238//
239////////////////////////////////////////////////////////////////////////////////
240class OffTheRecordRequestContext : public URLRequestContext,
241 public NotificationObserver {
242 public:
243 explicit OffTheRecordRequestContext(Profile* profile) {
244 DCHECK(!profile->IsOffTheRecord());
245 prefs_ = profile->GetPrefs();
246 DCHECK(prefs_);
247
248 // The OffTheRecordRequestContext is owned by the OffTheRecordProfileImpl
249 // which is itself owned by the original profile. We reference the original
250 // context to make sure it doesn't go away when we delete the object graph.
251 original_context_ = profile->GetRequestContext();
252
253 CommandLine command_line;
[email protected]928fb582008-08-11 15:40:23254 scoped_ptr<net::ProxyInfo> proxy_info(CreateProxyInfo(command_line));
initial.commit09911bf2008-07-26 23:55:29255
256 http_transaction_factory_ = new net::HttpCache(NULL, 0);
[email protected]8ac1a752008-07-31 19:40:37257 cookie_store_ = new net::CookieMonster;
258 cookie_policy_.SetType(net::CookiePolicy::FromInt(
259 prefs_->GetInteger(prefs::kCookieBehavior)));
initial.commit09911bf2008-07-26 23:55:29260 user_agent_ = original_context_->user_agent();
261 accept_language_ = original_context_->accept_language();
262 accept_charset_ = original_context_->accept_charset();
263 is_off_the_record_ = true;
264
265 // Register for notifications about prefs.
266 prefs_->AddPrefObserver(prefs::kAcceptLanguages, this);
267 prefs_->AddPrefObserver(prefs::kCookieBehavior, this);
268 }
269
270 // Since OffTheRecordProfileImpl maybe be destroyed after destroying
271 // PrefService, but all PrefService observers are needed to clear in
272 // before destroying PrefService. So we use to CleanupBeforeDestroy
273 // to do this thing. This function need to be called on destructor
274 // of ProfileImpl.
275 void CleanupBeforeDestroy() {
276 // Unregister for pref notifications.
277 prefs_->RemovePrefObserver(prefs::kAcceptLanguages, this);
278 prefs_->RemovePrefObserver(prefs::kCookieBehavior, this);
279 prefs_ = NULL;
280 }
281
282 // NotificationObserver implementation.
283 virtual void Observe(NotificationType type,
284 const NotificationSource& source,
285 const NotificationDetails& details) {
286 if (NOTIFY_PREF_CHANGED == type) {
287 std::wstring* pref_name_in = Details<std::wstring>(details).ptr();
288 PrefService* prefs = Source<PrefService>(source).ptr();
289 DCHECK(pref_name_in && prefs);
290 if (*pref_name_in == prefs::kAcceptLanguages) {
291 std::string accept_language =
292 WideToASCII(prefs->GetString(prefs::kAcceptLanguages));
293 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
294 NewRunnableMethod(
295 this,
296 &OffTheRecordRequestContext::OnAcceptLanguageChange,
297 accept_language));
298 } else if (*pref_name_in == prefs::kCookieBehavior) {
[email protected]8ac1a752008-07-31 19:40:37299 net::CookiePolicy::Type type = net::CookiePolicy::FromInt(
300 prefs_->GetInteger(prefs::kCookieBehavior));
initial.commit09911bf2008-07-26 23:55:29301 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
302 NewRunnableMethod(this,
303 &OffTheRecordRequestContext::OnCookiePolicyChange,
304 type));
305 }
306 }
307 }
308
309 void OnAcceptLanguageChange(std::string accept_language) {
310 DCHECK(MessageLoop::current() ==
311 ChromeThread::GetMessageLoop(ChromeThread::IO));
312 accept_language_ = accept_language;
313 }
314
[email protected]8ac1a752008-07-31 19:40:37315 void OnCookiePolicyChange(net::CookiePolicy::Type type) {
initial.commit09911bf2008-07-26 23:55:29316 DCHECK(MessageLoop::current() ==
317 ChromeThread::GetMessageLoop(ChromeThread::IO));
318 cookie_policy_.SetType(type);
319 }
320
321 virtual ~OffTheRecordRequestContext() {
322 DCHECK(NULL == prefs_);
323 delete cookie_store_;
324 delete http_transaction_factory_;
325 // The OffTheRecordRequestContext simply act as a proxy to the real context.
326 // There is nothing else to delete.
327 }
328
329 private:
330 // The original (non off the record) URLRequestContext.
331 scoped_refptr<URLRequestContext> original_context_;
332 PrefService* prefs_;
333
334 DISALLOW_EVIL_CONSTRUCTORS(OffTheRecordRequestContext);
335};
336
337////////////////////////////////////////////////////////////////////////////////
338//
339// OffTheRecordProfileImpl is a profile subclass that wraps an existing profile
340// to make it suitable for the off the record mode.
341//
342////////////////////////////////////////////////////////////////////////////////
343class OffTheRecordProfileImpl : public Profile,
344 public NotificationObserver {
345 public:
346 explicit OffTheRecordProfileImpl(Profile* real_profile)
347 : profile_(real_profile),
348 start_time_(Time::Now()) {
349 request_context_ = new OffTheRecordRequestContext(real_profile);
350 request_context_->AddRef();
351 // Register for browser close notifications so we can detect when the last
352 // off-the-record window is closed, in which case we can clean our states
353 // (cookies, downloads...).
354 NotificationService::current()->AddObserver(
355 this, NOTIFY_BROWSER_CLOSED, NotificationService::AllSources());
356 }
357
358 virtual ~OffTheRecordProfileImpl() {
359 if (request_context_) {
360 request_context_->CleanupBeforeDestroy();
361 // Clean up request context on IO thread.
362 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
363 NewRunnableFunction(&ReleaseURLRequestContext, request_context_));
364 request_context_ = NULL;
365 }
366 NotificationService::current()->RemoveObserver(
367 this, NOTIFY_BROWSER_CLOSED, NotificationService::AllSources());
368 }
369
370 virtual std::wstring GetPath() { return profile_->GetPath(); }
371
372 virtual bool IsOffTheRecord() {
373 return true;
374 }
375
376 virtual Profile* GetOffTheRecordProfile() {
377 return this;
378 }
379
380 virtual Profile* GetOriginalProfile() {
381 return profile_;
382 }
383
384 virtual VisitedLinkMaster* GetVisitedLinkMaster() {
385 return profile_->GetVisitedLinkMaster();
386 }
387
[email protected]6014d672008-12-05 00:38:25388 virtual ExtensionsService* GetExtensionsService() {
389 return profile_->GetExtensionsService();
390 }
391
[email protected]04fba9a92008-10-28 17:25:25392 virtual GreasemonkeyMaster* GetGreasemonkeyMaster() {
393 return profile_->GetGreasemonkeyMaster();
394 }
395
initial.commit09911bf2008-07-26 23:55:29396 virtual HistoryService* GetHistoryService(ServiceAccessType sat) {
397 if (sat == EXPLICIT_ACCESS) {
398 return profile_->GetHistoryService(sat);
399 } else {
400 NOTREACHED() << "This profile is OffTheRecord";
401 return NULL;
402 }
403 }
404
initial.commit09911bf2008-07-26 23:55:29405 virtual WebDataService* GetWebDataService(ServiceAccessType sat) {
406 if (sat == EXPLICIT_ACCESS) {
407 return profile_->GetWebDataService(sat);
408 } else {
409 NOTREACHED() << "This profile is OffTheRecord";
410 return NULL;
411 }
412 }
413
414 virtual PrefService* GetPrefs() {
415 return profile_->GetPrefs();
416 }
417
418 virtual TemplateURLModel* GetTemplateURLModel() {
419 return profile_->GetTemplateURLModel();
420 }
421
422 virtual TemplateURLFetcher* GetTemplateURLFetcher() {
423 return profile_->GetTemplateURLFetcher();
424 }
425
426 virtual DownloadManager* GetDownloadManager() {
427 if (!download_manager_.get()) {
428 scoped_refptr<DownloadManager> dlm(new DownloadManager);
429 dlm->Init(this);
430 download_manager_.swap(dlm);
431 }
432 return download_manager_.get();
433 }
434
435 virtual bool HasCreatedDownloadManager() const {
436 return (download_manager_.get() != NULL);
437 }
438
439 virtual URLRequestContext* GetRequestContext() {
440 return request_context_;
441 }
442
443 virtual SessionService* GetSessionService() {
444 // Don't save any sessions when off the record.
445 return NULL;
446 }
447
448 virtual void ShutdownSessionService() {
449 // We don't allow a session service, nothing to do.
450 }
451
452 virtual bool HasSessionService() const {
453 // We never have a session service.
454 return false;
455 }
456
457 virtual std::wstring GetName() {
458 return profile_->GetName();
459 }
460
461 virtual void SetName(const std::wstring& name) {
462 profile_->SetName(name);
463 }
464
465 virtual std::wstring GetID() {
466 return profile_->GetID();
467 }
468
469 virtual void SetID(const std::wstring& id) {
470 profile_->SetID(id);
471 }
472
initial.commit09911bf2008-07-26 23:55:29473 virtual bool DidLastSessionExitCleanly() {
474 return profile_->DidLastSessionExitCleanly();
475 }
476
[email protected]d8e41ed2008-09-11 15:22:32477 virtual BookmarkModel* GetBookmarkModel() {
478 return profile_->GetBookmarkModel();
initial.commit09911bf2008-07-26 23:55:29479 }
480
[email protected]3a453fa2008-08-15 18:46:34481#ifdef CHROME_PERSONALIZATION
[email protected]57d3d0a2008-09-24 00:50:07482 virtual ProfilePersonalization* GetProfilePersonalization() {
[email protected]3a453fa2008-08-15 18:46:34483 return profile_->GetProfilePersonalization();
484 }
485#endif
486
initial.commit09911bf2008-07-26 23:55:29487 virtual bool IsSameProfile(Profile* profile) {
488 if (profile == static_cast<Profile*>(this))
489 return true;
490 return profile == profile_;
491 }
492
493 virtual Time GetStartTime() const {
494 return start_time_;
495 }
496
497 virtual TabRestoreService* GetTabRestoreService() {
498 return NULL;
499 }
500
[email protected]e7244d82008-10-29 18:13:26501 virtual void ResetTabRestoreService() {
[email protected]20930852008-10-15 19:30:41502 }
503
[email protected]e7244d82008-10-29 18:13:26504 virtual void ReinitializeSpellChecker() {
505 profile_->ReinitializeSpellChecker();
initial.commit09911bf2008-07-26 23:55:29506 }
507
508 virtual SpellChecker* GetSpellChecker() {
509 return profile_->GetSpellChecker();
510 }
511
512 virtual void MarkAsCleanShutdown() {
513 }
514
515 virtual void ExitedOffTheRecordMode() {
516 // Drop our download manager so we forget about all the downloads made
517 // in off-the-record mode.
518 download_manager_ = NULL;
519 }
520
521 virtual void Observe(NotificationType type,
522 const NotificationSource& source,
523 const NotificationDetails& details) {
524 DCHECK_EQ(NOTIFY_BROWSER_CLOSED, type);
525 // We are only interested in OTR browser closing.
526 if (Source<Browser>(source)->profile() != this)
527 return;
528
529 // Let's check if we still have an Off The Record window opened.
530 // Note that we check against 1 as this notification is sent before the
531 // browser window is actually removed from the list.
532 if (BrowserList::GetBrowserCount(this) <= 1)
533 ExitedOffTheRecordMode();
534 }
535
536 private:
537 // The real underlying profile.
538 Profile* profile_;
539
540 // A proxy to the real request context.
541 OffTheRecordRequestContext* request_context_;
542
543 // The download manager that only stores downloaded items in memory.
544 scoped_refptr<DownloadManager> download_manager_;
545
546 // Time we were started.
547 Time start_time_;
548
549 DISALLOW_EVIL_CONSTRUCTORS(OffTheRecordProfileImpl);
550};
551
552ProfileImpl::ProfileImpl(const std::wstring& path)
553 : path_(path),
554 off_the_record_(false),
[email protected]6014d672008-12-05 00:38:25555 extensions_service_(new ExtensionsService(FilePath(path))),
initial.commit09911bf2008-07-26 23:55:29556 history_service_created_(false),
557 created_web_data_service_(false),
558 created_download_manager_(false),
559 request_context_(NULL),
initial.commit09911bf2008-07-26 23:55:29560 start_time_(Time::Now()),
561 spellchecker_(NULL),
[email protected]3a453fa2008-08-15 18:46:34562#ifdef CHROME_PERSONALIZATION
563 personalization_(NULL),
564#endif
initial.commit09911bf2008-07-26 23:55:29565 shutdown_session_service_(false) {
566 DCHECK(!path.empty()) << "Using an empty path will attempt to write " <<
567 "profile files to the root directory!";
[email protected]2d316662008-09-03 18:18:14568 create_session_service_timer_.Start(
569 TimeDelta::FromMilliseconds(kCreateSessionServiceDelayMS), this,
570 &ProfileImpl::EnsureSessionServiceCreated);
[email protected]e7244d82008-10-29 18:13:26571 PrefService* prefs = GetPrefs();
572 prefs->AddPrefObserver(prefs::kSpellCheckDictionary, this);
573 prefs->AddPrefObserver(prefs::kEnableSpellCheck, this);
initial.commit09911bf2008-07-26 23:55:29574}
575
576ProfileImpl::~ProfileImpl() {
577 tab_restore_service_.reset();
578
579 StopCreateSessionServiceTimer();
580 // TemplateURLModel schedules a task on the WebDataService from its
581 // destructor. Delete it first to ensure the task gets scheduled before we
582 // shut down the database.
583 template_url_model_.reset();
584
585 // The download manager queries the history system and should be deleted
586 // before the history is shutdown so it can properly cancel all requests.
587 download_manager_ = NULL;
588
[email protected]e7244d82008-10-29 18:13:26589 // Remove pref observers.
590 PrefService* prefs = GetPrefs();
591 prefs->RemovePrefObserver(prefs::kSpellCheckDictionary, this);
592 prefs->RemovePrefObserver(prefs::kEnableSpellCheck, this);
593
[email protected]3a453fa2008-08-15 18:46:34594#ifdef CHROME_PERSONALIZATION
[email protected]57d3d0a2008-09-24 00:50:07595 personalization_.reset();
[email protected]3a453fa2008-08-15 18:46:34596#endif
597
initial.commit09911bf2008-07-26 23:55:29598 // Both HistoryService and WebDataService maintain threads for background
599 // processing. Its possible each thread still has tasks on it that have
600 // increased the ref count of the service. In such a situation, when we
601 // decrement the refcount, it won't be 0, and the threads/databases aren't
602 // properly shut down. By explicitly calling Cleanup/Shutdown we ensure the
603 // databases are properly closed.
604 if (web_data_service_.get())
605 web_data_service_->Shutdown();
606
607 if (history_service_.get())
608 history_service_->Cleanup();
609
610 // The I/O thread may be NULL during testing.
[email protected]ab820df2008-08-26 05:55:10611 base::Thread* io_thread = g_browser_process->io_thread();
initial.commit09911bf2008-07-26 23:55:29612
613 if (spellchecker_) {
614 // The spellchecker must be deleted on the I/O thread. During testing, we
615 // don't have an I/O thread.
616 if (io_thread)
617 io_thread->message_loop()->ReleaseSoon(FROM_HERE, spellchecker_);
618 else
619 spellchecker_->Release();
620 }
621
622 if (request_context_) {
623 request_context_->CleanupBeforeDestroy();
624 // Clean up request context on IO thread.
625 io_thread->message_loop()->PostTask(FROM_HERE,
626 NewRunnableFunction(&ReleaseURLRequestContext, request_context_));
627 request_context_ = NULL;
628 }
629
[email protected]d8e41ed2008-09-11 15:22:32630 // HistoryService may call into the BookmarkModel, as such we need to
631 // delete HistoryService before the BookmarkModel. The destructor for
[email protected]90ef13132008-08-27 03:27:46632 // HistoryService will join with HistoryService's backend thread so that
633 // by the time the destructor has finished we're sure it will no longer call
[email protected]d8e41ed2008-09-11 15:22:32634 // into the BookmarkModel.
[email protected]90ef13132008-08-27 03:27:46635 history_service_ = NULL;
636 bookmark_bar_model_.reset();
637
initial.commit09911bf2008-07-26 23:55:29638 MarkAsCleanShutdown();
639}
640
641std::wstring ProfileImpl::GetPath() {
642 return path_;
643}
644
645bool ProfileImpl::IsOffTheRecord() {
646 return false;
647}
648
649Profile* ProfileImpl::GetOffTheRecordProfile() {
650 if (!off_the_record_profile_.get()) {
651 scoped_ptr<OffTheRecordProfileImpl> p(new OffTheRecordProfileImpl(this));
652 off_the_record_profile_.swap(p);
653 }
654 return off_the_record_profile_.get();
655}
656
657Profile* ProfileImpl::GetOriginalProfile() {
658 return this;
659}
660
[email protected]176aa482008-11-14 03:25:15661static void BroadcastNewHistoryTable(base::SharedMemory* table_memory) {
initial.commit09911bf2008-07-26 23:55:29662 if (!table_memory)
663 return;
664
665 // send to all RenderProcessHosts
666 for (RenderProcessHost::iterator i = RenderProcessHost::begin();
667 i != RenderProcessHost::end(); i++) {
668 if (!i->second->channel())
669 continue;
670
[email protected]176aa482008-11-14 03:25:15671 base::SharedMemoryHandle new_table;
[email protected]2f15de42008-11-11 22:35:19672 HANDLE process = i->second->process().handle();
initial.commit09911bf2008-07-26 23:55:29673 if (!process) {
674 // process can be null if it's started with the --single-process flag.
675 process = GetCurrentProcess();
676 }
677
678 table_memory->ShareToProcess(process, &new_table);
679 IPC::Message* msg = new ViewMsg_VisitedLink_NewTable(new_table);
680 i->second->channel()->Send(msg);
681 }
682}
683
684VisitedLinkMaster* ProfileImpl::GetVisitedLinkMaster() {
685 if (!visited_link_master_.get()) {
686 scoped_ptr<VisitedLinkMaster> visited_links(
687 new VisitedLinkMaster(g_browser_process->file_thread(),
688 BroadcastNewHistoryTable, this));
689 if (!visited_links->Init())
690 return NULL;
691 visited_link_master_.swap(visited_links);
692 }
693
694 return visited_link_master_.get();
695}
696
[email protected]6014d672008-12-05 00:38:25697ExtensionsService* ProfileImpl::GetExtensionsService() {
698 return extensions_service_.get();
699}
700
[email protected]04fba9a92008-10-28 17:25:25701GreasemonkeyMaster* ProfileImpl::GetGreasemonkeyMaster() {
702 if (!greasemonkey_master_.get()) {
703 std::wstring script_dir_str;
704 PathService::Get(chrome::DIR_USER_SCRIPTS, &script_dir_str);
705 FilePath script_dir(script_dir_str);
706 greasemonkey_master_ =
707 new GreasemonkeyMaster(g_browser_process->file_thread()->message_loop(),
708 script_dir);
709 }
710
711 return greasemonkey_master_.get();
712}
713
initial.commit09911bf2008-07-26 23:55:29714PrefService* ProfileImpl::GetPrefs() {
715 if (!prefs_.get()) {
716 prefs_.reset(new PrefService(GetPrefFilePath()));
717
718 // The Profile class and ProfileManager class may read some prefs so
719 // register known prefs as soon as possible.
720 Profile::RegisterUserPrefs(prefs_.get());
721 ProfileManager::RegisterUserPrefs(prefs_.get());
722
723 // The last session exited cleanly if there is no pref for
724 // kSessionExitedCleanly or the value for kSessionExitedCleanly is true.
725 last_session_exited_cleanly_ =
726 prefs_->GetBoolean(prefs::kSessionExitedCleanly);
727 // Mark the session as open.
728 prefs_->SetBoolean(prefs::kSessionExitedCleanly, false);
729 // Make sure we save to disk that the session has opened.
730 prefs_->ScheduleSavePersistentPrefs(g_browser_process->file_thread());
731 }
732
733 return prefs_.get();
734}
735
736std::wstring ProfileImpl::GetPrefFilePath() {
737 std::wstring pref_file_path = path_;
738 file_util::AppendToPath(&pref_file_path, chrome::kPreferencesFilename);
739 return pref_file_path;
740}
741
742URLRequestContext* ProfileImpl::GetRequestContext() {
743 if (!request_context_) {
744 std::wstring cookie_path = GetPath();
745 file_util::AppendToPath(&cookie_path, chrome::kCookieFilename);
746 std::wstring cache_path = GetPath();
747 file_util::AppendToPath(&cache_path, chrome::kCacheDirname);
[email protected]3dd1f6d52008-09-15 18:28:09748 request_context_ =
749 new ProfileImpl::RequestContext(cookie_path, cache_path, GetPrefs());
initial.commit09911bf2008-07-26 23:55:29750 request_context_->AddRef();
751
752 DCHECK(request_context_->cookie_store());
753 }
754
755 return request_context_;
756}
757
758HistoryService* ProfileImpl::GetHistoryService(ServiceAccessType sat) {
759 if (!history_service_created_) {
[email protected]90ef13132008-08-27 03:27:46760 history_service_created_ = true;
initial.commit09911bf2008-07-26 23:55:29761 scoped_refptr<HistoryService> history(new HistoryService(this));
[email protected]d8e41ed2008-09-11 15:22:32762 if (!history->Init(GetPath(), GetBookmarkModel()))
initial.commit09911bf2008-07-26 23:55:29763 return NULL;
764 history_service_.swap(history);
initial.commit09911bf2008-07-26 23:55:29765
766 // Send out the notification that the history service was created.
767 NotificationService::current()->
768 Notify(NOTIFY_HISTORY_CREATED, Source<Profile>(this),
769 Details<HistoryService>(history_service_.get()));
770 }
771 return history_service_.get();
772}
773
initial.commit09911bf2008-07-26 23:55:29774TemplateURLModel* ProfileImpl::GetTemplateURLModel() {
775 if (!template_url_model_.get())
776 template_url_model_.reset(new TemplateURLModel(this));
777 return template_url_model_.get();
778}
779
780TemplateURLFetcher* ProfileImpl::GetTemplateURLFetcher() {
781 if (!template_url_fetcher_.get())
782 template_url_fetcher_.reset(new TemplateURLFetcher(this));
783 return template_url_fetcher_.get();
784}
785
786WebDataService* ProfileImpl::GetWebDataService(ServiceAccessType sat) {
787 if (!created_web_data_service_)
788 CreateWebDataService();
789 return web_data_service_.get();
790}
791
792void ProfileImpl::CreateWebDataService() {
793 DCHECK(!created_web_data_service_ && web_data_service_.get() == NULL);
794 created_web_data_service_ = true;
795 scoped_refptr<WebDataService> wds(new WebDataService());
796 if (!wds->Init(GetPath()))
797 return;
798 web_data_service_.swap(wds);
799}
800
801DownloadManager* ProfileImpl::GetDownloadManager() {
802 if (!created_download_manager_) {
803 scoped_refptr<DownloadManager> dlm(new DownloadManager);
804 dlm->Init(this);
805 created_download_manager_ = true;
806 download_manager_.swap(dlm);
807 }
808 return download_manager_.get();
809}
810
811bool ProfileImpl::HasCreatedDownloadManager() const {
812 return created_download_manager_;
813}
814
815SessionService* ProfileImpl::GetSessionService() {
816 if (!session_service_.get() && !shutdown_session_service_) {
817 session_service_ = new SessionService(this);
818 session_service_->ResetFromCurrentBrowsers();
819 }
820 return session_service_.get();
821}
822
823void ProfileImpl::ShutdownSessionService() {
824 if (shutdown_session_service_)
825 return;
826
827 // We're about to exit, force creation of the session service if it hasn't
828 // been created yet. We do this to ensure session state matches the point in
829 // time the user exited.
830 GetSessionService();
831 shutdown_session_service_ = true;
832 session_service_ = NULL;
833}
834
835bool ProfileImpl::HasSessionService() const {
836 return (session_service_.get() != NULL);
837}
838
839std::wstring ProfileImpl::GetName() {
840 return GetPrefs()->GetString(prefs::kProfileName);
841}
842void ProfileImpl::SetName(const std::wstring& name) {
843 GetPrefs()->SetString(prefs::kProfileName, name);
844}
845
846std::wstring ProfileImpl::GetID() {
847 return GetPrefs()->GetString(prefs::kProfileID);
848}
849void ProfileImpl::SetID(const std::wstring& id) {
850 GetPrefs()->SetString(prefs::kProfileID, id);
851}
852
initial.commit09911bf2008-07-26 23:55:29853bool ProfileImpl::DidLastSessionExitCleanly() {
854 // last_session_exited_cleanly_ is set when the preferences are loaded. Force
855 // it to be set by asking for the prefs.
856 GetPrefs();
857 return last_session_exited_cleanly_;
858}
859
[email protected]d8e41ed2008-09-11 15:22:32860BookmarkModel* ProfileImpl::GetBookmarkModel() {
[email protected]90ef13132008-08-27 03:27:46861 if (!bookmark_bar_model_.get()) {
[email protected]d8e41ed2008-09-11 15:22:32862 bookmark_bar_model_.reset(new BookmarkModel(this));
[email protected]90ef13132008-08-27 03:27:46863 bookmark_bar_model_->Load();
864 }
initial.commit09911bf2008-07-26 23:55:29865 return bookmark_bar_model_.get();
866}
867
868bool ProfileImpl::IsSameProfile(Profile* profile) {
869 if (profile == static_cast<Profile*>(this))
870 return true;
871 OffTheRecordProfileImpl* otr_profile = off_the_record_profile_.get();
872 return otr_profile && profile == static_cast<Profile*>(otr_profile);
873}
874
875Time ProfileImpl::GetStartTime() const {
876 return start_time_;
877}
878
879TabRestoreService* ProfileImpl::GetTabRestoreService() {
880 if (!tab_restore_service_.get())
881 tab_restore_service_.reset(new TabRestoreService(this));
882 return tab_restore_service_.get();
883}
884
885void ProfileImpl::ResetTabRestoreService() {
886 tab_restore_service_.reset(NULL);
887}
888
[email protected]20930852008-10-15 19:30:41889// To be run in the IO thread to notify all resource message filters that the
890// spellchecker has changed.
891class NotifySpellcheckerChangeTask : public Task {
892 public:
893 NotifySpellcheckerChangeTask(
[email protected]e7244d82008-10-29 18:13:26894 Profile* profile,
895 const SpellcheckerReinitializedDetails& spellchecker)
[email protected]20930852008-10-15 19:30:41896 : profile_(profile),
897 spellchecker_(spellchecker) {
898 }
899
900 private:
901 void Run(void) {
902 NotificationService::current()->Notify(
903 NOTIFY_SPELLCHECKER_REINITIALIZED,
904 Source<Profile>(profile_),
905 Details<SpellcheckerReinitializedDetails>(&spellchecker_));
906 }
907
908 Profile* profile_;
909 SpellcheckerReinitializedDetails spellchecker_;
910};
911
[email protected]e7244d82008-10-29 18:13:26912void ProfileImpl::InitializeSpellChecker(bool need_to_broadcast) {
[email protected]20930852008-10-15 19:30:41913 // The I/O thread may be NULL during testing.
914 base::Thread* io_thread = g_browser_process->io_thread();
915 if (spellchecker_) {
916 // The spellchecker must be deleted on the I/O thread.
917 // A dummy variable to aid in logical clarity.
918 SpellChecker* last_spellchecker = spellchecker_;
919
920 if (io_thread)
921 io_thread->message_loop()->ReleaseSoon(FROM_HERE, last_spellchecker);
922 else // during testing, we don't have an I/O thread
923 last_spellchecker->Release();
[email protected]20930852008-10-15 19:30:41924 }
925
[email protected]20930852008-10-15 19:30:41926 // Retrieve the (perhaps updated recently) dictionary name from preferences.
927 PrefService* prefs = GetPrefs();
[email protected]e7244d82008-10-29 18:13:26928 bool enable_spellcheck = prefs->GetBoolean(prefs::kEnableSpellCheck);
[email protected]20930852008-10-15 19:30:41929
[email protected]e7244d82008-10-29 18:13:26930 if (enable_spellcheck) {
931 std::wstring dict_dir;
932 PathService::Get(chrome::DIR_APP_DICTIONARIES, &dict_dir);
933 std::wstring language = prefs->GetString(prefs::kSpellCheckDictionary);
934
935 // Note that, as the object pointed to by previously by spellchecker_
936 // is being deleted in the io thread, the spellchecker_ can be made to point
937 // to a new object (RE-initialized) in parallel in this UI thread.
938 spellchecker_ = new SpellChecker(dict_dir, language, GetRequestContext(),
939 L"");
940 spellchecker_->AddRef(); // Manual refcounting.
941 } else {
942 spellchecker_ = NULL;
943 }
[email protected]20930852008-10-15 19:30:41944
945 if (need_to_broadcast && io_thread) { // Notify resource message filters.
946 SpellcheckerReinitializedDetails scoped_spellchecker;
947 scoped_spellchecker.spellchecker = spellchecker_;
[email protected]e7244d82008-10-29 18:13:26948 if (io_thread) {
949 io_thread->message_loop()->PostTask(
950 FROM_HERE,
951 new NotifySpellcheckerChangeTask(this, scoped_spellchecker));
952 }
[email protected]20930852008-10-15 19:30:41953 }
954}
955
[email protected]e7244d82008-10-29 18:13:26956void ProfileImpl::ReinitializeSpellChecker() {
957 InitializeSpellChecker(true);
958}
959
initial.commit09911bf2008-07-26 23:55:29960SpellChecker* ProfileImpl::GetSpellChecker() {
961 if (!spellchecker_) {
[email protected]20930852008-10-15 19:30:41962 // This is where spellchecker gets initialized. Note that this is being
963 // initialized in the ui_thread. However, this is not a problem as long as
964 // it is *used* in the io thread.
965 // TODO (sidchat) One day, change everything so that spellchecker gets
966 // initialized in the IO thread itself.
[email protected]e7244d82008-10-29 18:13:26967 InitializeSpellChecker(false);
initial.commit09911bf2008-07-26 23:55:29968 }
[email protected]20930852008-10-15 19:30:41969
initial.commit09911bf2008-07-26 23:55:29970 return spellchecker_;
971}
972
973void ProfileImpl::MarkAsCleanShutdown() {
974 if (prefs_.get()) {
975 // The session cleanly exited, set kSessionExitedCleanly appropriately.
976 prefs_->SetBoolean(prefs::kSessionExitedCleanly, true);
977
978 // NOTE: If you change what thread this writes on, be sure and update
979 // ChromeFrame::EndSession().
980 prefs_->SavePersistentPrefs(g_browser_process->file_thread());
981 }
982}
983
[email protected]e7244d82008-10-29 18:13:26984void ProfileImpl::Observe(NotificationType type,
985 const NotificationSource& source,
986 const NotificationDetails& details) {
987 if (NOTIFY_PREF_CHANGED == type) {
988 std::wstring* pref_name_in = Details<std::wstring>(details).ptr();
989 PrefService* prefs = Source<PrefService>(source).ptr();
990 DCHECK(pref_name_in && prefs);
991 if (*pref_name_in == prefs::kSpellCheckDictionary ||
992 *pref_name_in == prefs::kEnableSpellCheck) {
993 InitializeSpellChecker(true);
994 }
995 }
996}
997
initial.commit09911bf2008-07-26 23:55:29998void ProfileImpl::StopCreateSessionServiceTimer() {
[email protected]2d316662008-09-03 18:18:14999 create_session_service_timer_.Stop();
initial.commit09911bf2008-07-26 23:55:291000}
[email protected]3a453fa2008-08-15 18:46:341001
1002#ifdef CHROME_PERSONALIZATION
[email protected]57d3d0a2008-09-24 00:50:071003ProfilePersonalization* ProfileImpl::GetProfilePersonalization() {
1004 if (!personalization_.get())
1005 personalization_.reset(
1006 Personalization::CreateProfilePersonalization(this));
1007 return personalization_.get();
[email protected]3a453fa2008-08-15 18:46:341008}
license.botbf09a502008-08-24 00:55:551009#endif