blob: 1890b1efbece9fd7173d154c0b919377e74c690b [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]04fba9a92008-10-28 17:25:2520#include "chrome/browser/greasemonkey_master.h"
initial.commit09911bf2008-07-26 23:55:2921#include "chrome/browser/history/history.h"
22#include "chrome/browser/navigation_controller.h"
23#include "chrome/browser/profile_manager.h"
24#include "chrome/browser/render_process_host.h"
25#include "chrome/browser/spellchecker.h"
26#include "chrome/browser/tab_restore_service.h"
27#include "chrome/browser/template_url_fetcher.h"
28#include "chrome/browser/template_url_model.h"
29#include "chrome/browser/visitedlink_master.h"
30#include "chrome/browser/webdata/web_data_service.h"
31#include "chrome/common/chrome_constants.h"
32#include "chrome/common/chrome_paths.h"
33#include "chrome/common/chrome_switches.h"
34#include "chrome/common/net/cookie_monster_sqlite.h"
35#include "chrome/common/notification_service.h"
36#include "chrome/common/pref_names.h"
37#include "chrome/common/pref_service.h"
38#include "chrome/common/resource_bundle.h"
39#include "net/base/cookie_monster.h"
40#include "net/base/cookie_policy.h"
41#include "net/http/http_cache.h"
[email protected]928fb582008-08-11 15:40:2342#include "net/proxy/proxy_service.h"
initial.commit09911bf2008-07-26 23:55:2943#include "net/url_request/url_request_context.h"
44#include "webkit/glue/webkit_glue.h"
45
[email protected]e1acf6f2008-10-27 20:43:3346using base::Time;
47using base::TimeDelta;
48
initial.commit09911bf2008-07-26 23:55:2949// Delay, in milliseconds, before we explicitly create the SessionService.
50static const int kCreateSessionServiceDelayMS = 500;
51
52// A pointer to the request context for the default profile. See comments on
53// Profile::GetDefaultRequestContext.
54URLRequestContext* Profile::default_request_context_;
55
56//static
57void Profile::RegisterUserPrefs(PrefService* prefs) {
[email protected]430d3f72008-10-27 17:56:5558 prefs->RegisterBooleanPref(prefs::kSearchSuggestEnabled, true);
initial.commit09911bf2008-07-26 23:55:2959 prefs->RegisterBooleanPref(prefs::kSessionExitedCleanly, true);
60 prefs->RegisterBooleanPref(prefs::kSafeBrowsingEnabled, true);
[email protected]e7244d82008-10-29 18:13:2661 prefs->RegisterLocalizedStringPref(prefs::kSpellCheckDictionary,
62 IDS_SPELLCHECK_DICTIONARY);
63 prefs->RegisterBooleanPref(prefs::kEnableSpellCheck, true);
initial.commit09911bf2008-07-26 23:55:2964}
65
66//static
67Profile* Profile::CreateProfile(const std::wstring& path) {
68 return new ProfileImpl(path);
69}
70
71//static
72URLRequestContext* Profile::GetDefaultRequestContext() {
73 return default_request_context_;
74}
75
76
77// Sets up proxy info if it was specified, otherwise returns NULL. The
78// returned pointer MUST be deleted by the caller if non-NULL.
[email protected]928fb582008-08-11 15:40:2379static net::ProxyInfo* CreateProxyInfo(const CommandLine& command_line) {
80 net::ProxyInfo* proxy_info = NULL;
initial.commit09911bf2008-07-26 23:55:2981
82 if (command_line.HasSwitch(switches::kProxyServer)) {
[email protected]928fb582008-08-11 15:40:2383 proxy_info = new net::ProxyInfo();
initial.commit09911bf2008-07-26 23:55:2984 const std::wstring& proxy_server =
85 command_line.GetSwitchValue(switches::kProxyServer);
[email protected]82f954e2008-08-12 05:11:3886 proxy_info->UseNamedProxy(WideToASCII(proxy_server));
initial.commit09911bf2008-07-26 23:55:2987 }
88
89 return proxy_info;
90}
91
92// Releases the URLRequestContext and sends out a notification about it.
93// Note: runs on IO thread.
94static void ReleaseURLRequestContext(URLRequestContext* context) {
95 NotificationService::current()->Notify(NOTIFY_URL_REQUEST_CONTEXT_RELEASED,
96 Source<URLRequestContext>(context),
97 NotificationService::NoDetails());
98 context->Release();
99}
100
101// A context for URLRequests issued relative to this profile.
102class ProfileImpl::RequestContext : public URLRequestContext,
103 public NotificationObserver {
104 public:
105 // |cookie_store_path| is the local disk path at which the cookie store
106 // is persisted.
107 RequestContext(const std::wstring& cookie_store_path,
108 const std::wstring& disk_cache_path,
109 PrefService* prefs)
110 : prefs_(prefs) {
111 cookie_store_ = NULL;
112
113 // setup user agent
[email protected]f5db58d2008-09-24 20:48:29114 user_agent_ = webkit_glue::GetUserAgent();
initial.commit09911bf2008-07-26 23:55:29115 // set up Accept-Language and Accept-Charset header values
116 // TODO(jungshik) : This may slow down http requests. Perhaps,
117 // we have to come up with a better way to set up these values.
118 accept_language_ = WideToASCII(prefs_->GetString(prefs::kAcceptLanguages));
119 accept_charset_ = WideToASCII(prefs_->GetString(prefs::kDefaultCharset));
120 accept_charset_ += ",*,utf-8";
121
122 CommandLine command_line;
123
[email protected]928fb582008-08-11 15:40:23124 scoped_ptr<net::ProxyInfo> proxy_info(CreateProxyInfo(command_line));
initial.commit09911bf2008-07-26 23:55:29125 net::HttpCache* cache =
126 new net::HttpCache(proxy_info.get(), disk_cache_path, 0);
127
128 bool record_mode = chrome::kRecordModeEnabled &&
129 CommandLine().HasSwitch(switches::kRecordMode);
130 bool playback_mode = CommandLine().HasSwitch(switches::kPlaybackMode);
131
132 if (record_mode || playback_mode) {
133 // Don't use existing cookies and use an in-memory store.
[email protected]8ac1a752008-07-31 19:40:37134 cookie_store_ = new net::CookieMonster();
initial.commit09911bf2008-07-26 23:55:29135 cache->set_mode(
136 record_mode ? net::HttpCache::RECORD : net::HttpCache::PLAYBACK);
137 }
138 http_transaction_factory_ = cache;
139
140 // setup cookie store
141 if (!cookie_store_) {
142 DCHECK(!cookie_store_path.empty());
143 cookie_db_.reset(new SQLitePersistentCookieStore(
144 cookie_store_path, g_browser_process->db_thread()->message_loop()));
[email protected]8ac1a752008-07-31 19:40:37145 cookie_store_ = new net::CookieMonster(cookie_db_.get());
initial.commit09911bf2008-07-26 23:55:29146 }
147
[email protected]8ac1a752008-07-31 19:40:37148 cookie_policy_.SetType(net::CookiePolicy::FromInt(
149 prefs_->GetInteger(prefs::kCookieBehavior)));
initial.commit09911bf2008-07-26 23:55:29150
151 // The first request context to be created is the one for the default
152 // profile - at least until we support multiple profiles.
153 if (!default_request_context_)
154 default_request_context_ = this;
[email protected]3dd1f6d52008-09-15 18:28:09155 NotificationService::current()->Notify(
156 NOTIFY_DEFAULT_REQUEST_CONTEXT_AVAILABLE,
157 NotificationService::AllSources(), NotificationService::NoDetails());
initial.commit09911bf2008-07-26 23:55:29158
159 // Register for notifications about prefs.
160 prefs_->AddPrefObserver(prefs::kAcceptLanguages, this);
161 prefs_->AddPrefObserver(prefs::kCookieBehavior, this);
162 }
163
164 // NotificationObserver implementation.
165 virtual void Observe(NotificationType type,
166 const NotificationSource& source,
167 const NotificationDetails& details) {
168 if (NOTIFY_PREF_CHANGED == type) {
169 std::wstring* pref_name_in = Details<std::wstring>(details).ptr();
170 PrefService* prefs = Source<PrefService>(source).ptr();
171 DCHECK(pref_name_in && prefs);
172 if (*pref_name_in == prefs::kAcceptLanguages) {
173 std::string accept_language =
174 WideToASCII(prefs->GetString(prefs::kAcceptLanguages));
175 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
176 NewRunnableMethod(this,
177 &RequestContext::OnAcceptLanguageChange,
178 accept_language));
179 } else if (*pref_name_in == prefs::kCookieBehavior) {
[email protected]8ac1a752008-07-31 19:40:37180 net::CookiePolicy::Type type = net::CookiePolicy::FromInt(
181 prefs_->GetInteger(prefs::kCookieBehavior));
initial.commit09911bf2008-07-26 23:55:29182 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
183 NewRunnableMethod(this,
184 &RequestContext::OnCookiePolicyChange,
185 type));
186 }
187 }
188 }
189
190 // Since ProfileImpl::RequestContext will be destroyed on IO thread, but all
191 // PrefService observers are needed to clear in before destroying ProfileImpl.
192 // So we use to CleanupBeforeDestroy to do this thing. This function need to
193 // be called on destructor of ProfileImpl.
194 void CleanupBeforeDestroy() {
195 // Unregister for pref notifications.
196 prefs_->RemovePrefObserver(prefs::kAcceptLanguages, this);
197 prefs_->RemovePrefObserver(prefs::kCookieBehavior, this);
198 prefs_ = NULL;
199 }
200
201 void OnAcceptLanguageChange(std::string accept_language) {
202 DCHECK(MessageLoop::current() ==
203 ChromeThread::GetMessageLoop(ChromeThread::IO));
204 accept_language_ = accept_language;
205 }
206
[email protected]8ac1a752008-07-31 19:40:37207 void OnCookiePolicyChange(net::CookiePolicy::Type type) {
initial.commit09911bf2008-07-26 23:55:29208 DCHECK(MessageLoop::current() ==
209 ChromeThread::GetMessageLoop(ChromeThread::IO));
210 cookie_policy_.SetType(type);
211 }
212
213 virtual ~RequestContext() {
214 DCHECK(NULL == prefs_);
215 delete cookie_store_;
216 delete http_transaction_factory_;
217
218 if (default_request_context_ == this)
219 default_request_context_ = NULL;
220 }
221
222 private:
223 scoped_ptr<SQLitePersistentCookieStore> cookie_db_;
224 PrefService* prefs_;
225};
226
227////////////////////////////////////////////////////////////////////////////////
228//
229// An URLRequestContext proxy for OffTheRecord. This request context is
230// really a proxy to the original profile's request context but set
231// is_off_the_record_ to true.
232//
233// TODO(ACW). Do we need to share the FtpAuthCache with the real request context
234// see bug 974328
235//
236// TODO(jackson): http://b/issue?id=1197350 Remove duplicated code from above.
237//
238////////////////////////////////////////////////////////////////////////////////
239class OffTheRecordRequestContext : public URLRequestContext,
240 public NotificationObserver {
241 public:
242 explicit OffTheRecordRequestContext(Profile* profile) {
243 DCHECK(!profile->IsOffTheRecord());
244 prefs_ = profile->GetPrefs();
245 DCHECK(prefs_);
246
247 // The OffTheRecordRequestContext is owned by the OffTheRecordProfileImpl
248 // which is itself owned by the original profile. We reference the original
249 // context to make sure it doesn't go away when we delete the object graph.
250 original_context_ = profile->GetRequestContext();
251
252 CommandLine command_line;
[email protected]928fb582008-08-11 15:40:23253 scoped_ptr<net::ProxyInfo> proxy_info(CreateProxyInfo(command_line));
initial.commit09911bf2008-07-26 23:55:29254
255 http_transaction_factory_ = new net::HttpCache(NULL, 0);
[email protected]8ac1a752008-07-31 19:40:37256 cookie_store_ = new net::CookieMonster;
257 cookie_policy_.SetType(net::CookiePolicy::FromInt(
258 prefs_->GetInteger(prefs::kCookieBehavior)));
initial.commit09911bf2008-07-26 23:55:29259 user_agent_ = original_context_->user_agent();
260 accept_language_ = original_context_->accept_language();
261 accept_charset_ = original_context_->accept_charset();
262 is_off_the_record_ = true;
263
264 // Register for notifications about prefs.
265 prefs_->AddPrefObserver(prefs::kAcceptLanguages, this);
266 prefs_->AddPrefObserver(prefs::kCookieBehavior, this);
267 }
268
269 // Since OffTheRecordProfileImpl maybe be destroyed after destroying
270 // PrefService, but all PrefService observers are needed to clear in
271 // before destroying PrefService. So we use to CleanupBeforeDestroy
272 // to do this thing. This function need to be called on destructor
273 // of ProfileImpl.
274 void CleanupBeforeDestroy() {
275 // Unregister for pref notifications.
276 prefs_->RemovePrefObserver(prefs::kAcceptLanguages, this);
277 prefs_->RemovePrefObserver(prefs::kCookieBehavior, this);
278 prefs_ = NULL;
279 }
280
281 // NotificationObserver implementation.
282 virtual void Observe(NotificationType type,
283 const NotificationSource& source,
284 const NotificationDetails& details) {
285 if (NOTIFY_PREF_CHANGED == type) {
286 std::wstring* pref_name_in = Details<std::wstring>(details).ptr();
287 PrefService* prefs = Source<PrefService>(source).ptr();
288 DCHECK(pref_name_in && prefs);
289 if (*pref_name_in == prefs::kAcceptLanguages) {
290 std::string accept_language =
291 WideToASCII(prefs->GetString(prefs::kAcceptLanguages));
292 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
293 NewRunnableMethod(
294 this,
295 &OffTheRecordRequestContext::OnAcceptLanguageChange,
296 accept_language));
297 } else if (*pref_name_in == prefs::kCookieBehavior) {
[email protected]8ac1a752008-07-31 19:40:37298 net::CookiePolicy::Type type = net::CookiePolicy::FromInt(
299 prefs_->GetInteger(prefs::kCookieBehavior));
initial.commit09911bf2008-07-26 23:55:29300 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
301 NewRunnableMethod(this,
302 &OffTheRecordRequestContext::OnCookiePolicyChange,
303 type));
304 }
305 }
306 }
307
308 void OnAcceptLanguageChange(std::string accept_language) {
309 DCHECK(MessageLoop::current() ==
310 ChromeThread::GetMessageLoop(ChromeThread::IO));
311 accept_language_ = accept_language;
312 }
313
[email protected]8ac1a752008-07-31 19:40:37314 void OnCookiePolicyChange(net::CookiePolicy::Type type) {
initial.commit09911bf2008-07-26 23:55:29315 DCHECK(MessageLoop::current() ==
316 ChromeThread::GetMessageLoop(ChromeThread::IO));
317 cookie_policy_.SetType(type);
318 }
319
320 virtual ~OffTheRecordRequestContext() {
321 DCHECK(NULL == prefs_);
322 delete cookie_store_;
323 delete http_transaction_factory_;
324 // The OffTheRecordRequestContext simply act as a proxy to the real context.
325 // There is nothing else to delete.
326 }
327
328 private:
329 // The original (non off the record) URLRequestContext.
330 scoped_refptr<URLRequestContext> original_context_;
331 PrefService* prefs_;
332
333 DISALLOW_EVIL_CONSTRUCTORS(OffTheRecordRequestContext);
334};
335
336////////////////////////////////////////////////////////////////////////////////
337//
338// OffTheRecordProfileImpl is a profile subclass that wraps an existing profile
339// to make it suitable for the off the record mode.
340//
341////////////////////////////////////////////////////////////////////////////////
342class OffTheRecordProfileImpl : public Profile,
343 public NotificationObserver {
344 public:
345 explicit OffTheRecordProfileImpl(Profile* real_profile)
346 : profile_(real_profile),
347 start_time_(Time::Now()) {
348 request_context_ = new OffTheRecordRequestContext(real_profile);
349 request_context_->AddRef();
350 // Register for browser close notifications so we can detect when the last
351 // off-the-record window is closed, in which case we can clean our states
352 // (cookies, downloads...).
353 NotificationService::current()->AddObserver(
354 this, NOTIFY_BROWSER_CLOSED, NotificationService::AllSources());
355 }
356
357 virtual ~OffTheRecordProfileImpl() {
358 if (request_context_) {
359 request_context_->CleanupBeforeDestroy();
360 // Clean up request context on IO thread.
361 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
362 NewRunnableFunction(&ReleaseURLRequestContext, request_context_));
363 request_context_ = NULL;
364 }
365 NotificationService::current()->RemoveObserver(
366 this, NOTIFY_BROWSER_CLOSED, NotificationService::AllSources());
367 }
368
369 virtual std::wstring GetPath() { return profile_->GetPath(); }
370
371 virtual bool IsOffTheRecord() {
372 return true;
373 }
374
375 virtual Profile* GetOffTheRecordProfile() {
376 return this;
377 }
378
379 virtual Profile* GetOriginalProfile() {
380 return profile_;
381 }
382
383 virtual VisitedLinkMaster* GetVisitedLinkMaster() {
384 return profile_->GetVisitedLinkMaster();
385 }
386
[email protected]04fba9a92008-10-28 17:25:25387 virtual GreasemonkeyMaster* GetGreasemonkeyMaster() {
388 return profile_->GetGreasemonkeyMaster();
389 }
390
initial.commit09911bf2008-07-26 23:55:29391 virtual HistoryService* GetHistoryService(ServiceAccessType sat) {
392 if (sat == EXPLICIT_ACCESS) {
393 return profile_->GetHistoryService(sat);
394 } else {
395 NOTREACHED() << "This profile is OffTheRecord";
396 return NULL;
397 }
398 }
399
initial.commit09911bf2008-07-26 23:55:29400 virtual WebDataService* GetWebDataService(ServiceAccessType sat) {
401 if (sat == EXPLICIT_ACCESS) {
402 return profile_->GetWebDataService(sat);
403 } else {
404 NOTREACHED() << "This profile is OffTheRecord";
405 return NULL;
406 }
407 }
408
409 virtual PrefService* GetPrefs() {
410 return profile_->GetPrefs();
411 }
412
413 virtual TemplateURLModel* GetTemplateURLModel() {
414 return profile_->GetTemplateURLModel();
415 }
416
417 virtual TemplateURLFetcher* GetTemplateURLFetcher() {
418 return profile_->GetTemplateURLFetcher();
419 }
420
421 virtual DownloadManager* GetDownloadManager() {
422 if (!download_manager_.get()) {
423 scoped_refptr<DownloadManager> dlm(new DownloadManager);
424 dlm->Init(this);
425 download_manager_.swap(dlm);
426 }
427 return download_manager_.get();
428 }
429
430 virtual bool HasCreatedDownloadManager() const {
431 return (download_manager_.get() != NULL);
432 }
433
434 virtual URLRequestContext* GetRequestContext() {
435 return request_context_;
436 }
437
438 virtual SessionService* GetSessionService() {
439 // Don't save any sessions when off the record.
440 return NULL;
441 }
442
443 virtual void ShutdownSessionService() {
444 // We don't allow a session service, nothing to do.
445 }
446
447 virtual bool HasSessionService() const {
448 // We never have a session service.
449 return false;
450 }
451
452 virtual std::wstring GetName() {
453 return profile_->GetName();
454 }
455
456 virtual void SetName(const std::wstring& name) {
457 profile_->SetName(name);
458 }
459
460 virtual std::wstring GetID() {
461 return profile_->GetID();
462 }
463
464 virtual void SetID(const std::wstring& id) {
465 profile_->SetID(id);
466 }
467
initial.commit09911bf2008-07-26 23:55:29468 virtual bool DidLastSessionExitCleanly() {
469 return profile_->DidLastSessionExitCleanly();
470 }
471
[email protected]d8e41ed2008-09-11 15:22:32472 virtual BookmarkModel* GetBookmarkModel() {
473 return profile_->GetBookmarkModel();
initial.commit09911bf2008-07-26 23:55:29474 }
475
[email protected]3a453fa2008-08-15 18:46:34476#ifdef CHROME_PERSONALIZATION
[email protected]57d3d0a2008-09-24 00:50:07477 virtual ProfilePersonalization* GetProfilePersonalization() {
[email protected]3a453fa2008-08-15 18:46:34478 return profile_->GetProfilePersonalization();
479 }
480#endif
481
initial.commit09911bf2008-07-26 23:55:29482 virtual bool IsSameProfile(Profile* profile) {
483 if (profile == static_cast<Profile*>(this))
484 return true;
485 return profile == profile_;
486 }
487
488 virtual Time GetStartTime() const {
489 return start_time_;
490 }
491
492 virtual TabRestoreService* GetTabRestoreService() {
493 return NULL;
494 }
495
[email protected]e7244d82008-10-29 18:13:26496 virtual void ResetTabRestoreService() {
[email protected]20930852008-10-15 19:30:41497 }
498
[email protected]e7244d82008-10-29 18:13:26499 virtual void ReinitializeSpellChecker() {
500 profile_->ReinitializeSpellChecker();
initial.commit09911bf2008-07-26 23:55:29501 }
502
503 virtual SpellChecker* GetSpellChecker() {
504 return profile_->GetSpellChecker();
505 }
506
507 virtual void MarkAsCleanShutdown() {
508 }
509
510 virtual void ExitedOffTheRecordMode() {
511 // Drop our download manager so we forget about all the downloads made
512 // in off-the-record mode.
513 download_manager_ = NULL;
514 }
515
516 virtual void Observe(NotificationType type,
517 const NotificationSource& source,
518 const NotificationDetails& details) {
519 DCHECK_EQ(NOTIFY_BROWSER_CLOSED, type);
520 // We are only interested in OTR browser closing.
521 if (Source<Browser>(source)->profile() != this)
522 return;
523
524 // Let's check if we still have an Off The Record window opened.
525 // Note that we check against 1 as this notification is sent before the
526 // browser window is actually removed from the list.
527 if (BrowserList::GetBrowserCount(this) <= 1)
528 ExitedOffTheRecordMode();
529 }
530
531 private:
532 // The real underlying profile.
533 Profile* profile_;
534
535 // A proxy to the real request context.
536 OffTheRecordRequestContext* request_context_;
537
538 // The download manager that only stores downloaded items in memory.
539 scoped_refptr<DownloadManager> download_manager_;
540
541 // Time we were started.
542 Time start_time_;
543
544 DISALLOW_EVIL_CONSTRUCTORS(OffTheRecordProfileImpl);
545};
546
547ProfileImpl::ProfileImpl(const std::wstring& path)
548 : path_(path),
549 off_the_record_(false),
550 history_service_created_(false),
551 created_web_data_service_(false),
552 created_download_manager_(false),
553 request_context_(NULL),
initial.commit09911bf2008-07-26 23:55:29554 start_time_(Time::Now()),
555 spellchecker_(NULL),
[email protected]3a453fa2008-08-15 18:46:34556#ifdef CHROME_PERSONALIZATION
557 personalization_(NULL),
558#endif
initial.commit09911bf2008-07-26 23:55:29559 shutdown_session_service_(false) {
560 DCHECK(!path.empty()) << "Using an empty path will attempt to write " <<
561 "profile files to the root directory!";
[email protected]2d316662008-09-03 18:18:14562 create_session_service_timer_.Start(
563 TimeDelta::FromMilliseconds(kCreateSessionServiceDelayMS), this,
564 &ProfileImpl::EnsureSessionServiceCreated);
[email protected]e7244d82008-10-29 18:13:26565 PrefService* prefs = GetPrefs();
566 prefs->AddPrefObserver(prefs::kSpellCheckDictionary, this);
567 prefs->AddPrefObserver(prefs::kEnableSpellCheck, this);
initial.commit09911bf2008-07-26 23:55:29568}
569
570ProfileImpl::~ProfileImpl() {
571 tab_restore_service_.reset();
572
573 StopCreateSessionServiceTimer();
574 // TemplateURLModel schedules a task on the WebDataService from its
575 // destructor. Delete it first to ensure the task gets scheduled before we
576 // shut down the database.
577 template_url_model_.reset();
578
579 // The download manager queries the history system and should be deleted
580 // before the history is shutdown so it can properly cancel all requests.
581 download_manager_ = NULL;
582
[email protected]e7244d82008-10-29 18:13:26583 // Remove pref observers.
584 PrefService* prefs = GetPrefs();
585 prefs->RemovePrefObserver(prefs::kSpellCheckDictionary, this);
586 prefs->RemovePrefObserver(prefs::kEnableSpellCheck, this);
587
[email protected]3a453fa2008-08-15 18:46:34588#ifdef CHROME_PERSONALIZATION
[email protected]57d3d0a2008-09-24 00:50:07589 personalization_.reset();
[email protected]3a453fa2008-08-15 18:46:34590#endif
591
initial.commit09911bf2008-07-26 23:55:29592 // Both HistoryService and WebDataService maintain threads for background
593 // processing. Its possible each thread still has tasks on it that have
594 // increased the ref count of the service. In such a situation, when we
595 // decrement the refcount, it won't be 0, and the threads/databases aren't
596 // properly shut down. By explicitly calling Cleanup/Shutdown we ensure the
597 // databases are properly closed.
598 if (web_data_service_.get())
599 web_data_service_->Shutdown();
600
601 if (history_service_.get())
602 history_service_->Cleanup();
603
604 // The I/O thread may be NULL during testing.
[email protected]ab820df2008-08-26 05:55:10605 base::Thread* io_thread = g_browser_process->io_thread();
initial.commit09911bf2008-07-26 23:55:29606
607 if (spellchecker_) {
608 // The spellchecker must be deleted on the I/O thread. During testing, we
609 // don't have an I/O thread.
610 if (io_thread)
611 io_thread->message_loop()->ReleaseSoon(FROM_HERE, spellchecker_);
612 else
613 spellchecker_->Release();
614 }
615
616 if (request_context_) {
617 request_context_->CleanupBeforeDestroy();
618 // Clean up request context on IO thread.
619 io_thread->message_loop()->PostTask(FROM_HERE,
620 NewRunnableFunction(&ReleaseURLRequestContext, request_context_));
621 request_context_ = NULL;
622 }
623
[email protected]d8e41ed2008-09-11 15:22:32624 // HistoryService may call into the BookmarkModel, as such we need to
625 // delete HistoryService before the BookmarkModel. The destructor for
[email protected]90ef13132008-08-27 03:27:46626 // HistoryService will join with HistoryService's backend thread so that
627 // by the time the destructor has finished we're sure it will no longer call
[email protected]d8e41ed2008-09-11 15:22:32628 // into the BookmarkModel.
[email protected]90ef13132008-08-27 03:27:46629 history_service_ = NULL;
630 bookmark_bar_model_.reset();
631
initial.commit09911bf2008-07-26 23:55:29632 MarkAsCleanShutdown();
633}
634
635std::wstring ProfileImpl::GetPath() {
636 return path_;
637}
638
639bool ProfileImpl::IsOffTheRecord() {
640 return false;
641}
642
643Profile* ProfileImpl::GetOffTheRecordProfile() {
644 if (!off_the_record_profile_.get()) {
645 scoped_ptr<OffTheRecordProfileImpl> p(new OffTheRecordProfileImpl(this));
646 off_the_record_profile_.swap(p);
647 }
648 return off_the_record_profile_.get();
649}
650
651Profile* ProfileImpl::GetOriginalProfile() {
652 return this;
653}
654
655static void BroadcastNewHistoryTable(SharedMemory* table_memory) {
656 if (!table_memory)
657 return;
658
659 // send to all RenderProcessHosts
660 for (RenderProcessHost::iterator i = RenderProcessHost::begin();
661 i != RenderProcessHost::end(); i++) {
662 if (!i->second->channel())
663 continue;
664
665 SharedMemoryHandle new_table;
666 HANDLE process = i->second->process();
667 if (!process) {
668 // process can be null if it's started with the --single-process flag.
669 process = GetCurrentProcess();
670 }
671
672 table_memory->ShareToProcess(process, &new_table);
673 IPC::Message* msg = new ViewMsg_VisitedLink_NewTable(new_table);
674 i->second->channel()->Send(msg);
675 }
676}
677
678VisitedLinkMaster* ProfileImpl::GetVisitedLinkMaster() {
679 if (!visited_link_master_.get()) {
680 scoped_ptr<VisitedLinkMaster> visited_links(
681 new VisitedLinkMaster(g_browser_process->file_thread(),
682 BroadcastNewHistoryTable, this));
683 if (!visited_links->Init())
684 return NULL;
685 visited_link_master_.swap(visited_links);
686 }
687
688 return visited_link_master_.get();
689}
690
[email protected]04fba9a92008-10-28 17:25:25691GreasemonkeyMaster* ProfileImpl::GetGreasemonkeyMaster() {
692 if (!greasemonkey_master_.get()) {
693 std::wstring script_dir_str;
694 PathService::Get(chrome::DIR_USER_SCRIPTS, &script_dir_str);
695 FilePath script_dir(script_dir_str);
696 greasemonkey_master_ =
697 new GreasemonkeyMaster(g_browser_process->file_thread()->message_loop(),
698 script_dir);
699 }
700
701 return greasemonkey_master_.get();
702}
703
initial.commit09911bf2008-07-26 23:55:29704PrefService* ProfileImpl::GetPrefs() {
705 if (!prefs_.get()) {
706 prefs_.reset(new PrefService(GetPrefFilePath()));
707
708 // The Profile class and ProfileManager class may read some prefs so
709 // register known prefs as soon as possible.
710 Profile::RegisterUserPrefs(prefs_.get());
711 ProfileManager::RegisterUserPrefs(prefs_.get());
712
713 // The last session exited cleanly if there is no pref for
714 // kSessionExitedCleanly or the value for kSessionExitedCleanly is true.
715 last_session_exited_cleanly_ =
716 prefs_->GetBoolean(prefs::kSessionExitedCleanly);
717 // Mark the session as open.
718 prefs_->SetBoolean(prefs::kSessionExitedCleanly, false);
719 // Make sure we save to disk that the session has opened.
720 prefs_->ScheduleSavePersistentPrefs(g_browser_process->file_thread());
721 }
722
723 return prefs_.get();
724}
725
726std::wstring ProfileImpl::GetPrefFilePath() {
727 std::wstring pref_file_path = path_;
728 file_util::AppendToPath(&pref_file_path, chrome::kPreferencesFilename);
729 return pref_file_path;
730}
731
732URLRequestContext* ProfileImpl::GetRequestContext() {
733 if (!request_context_) {
734 std::wstring cookie_path = GetPath();
735 file_util::AppendToPath(&cookie_path, chrome::kCookieFilename);
736 std::wstring cache_path = GetPath();
737 file_util::AppendToPath(&cache_path, chrome::kCacheDirname);
[email protected]3dd1f6d52008-09-15 18:28:09738 request_context_ =
739 new ProfileImpl::RequestContext(cookie_path, cache_path, GetPrefs());
initial.commit09911bf2008-07-26 23:55:29740 request_context_->AddRef();
741
742 DCHECK(request_context_->cookie_store());
743 }
744
745 return request_context_;
746}
747
748HistoryService* ProfileImpl::GetHistoryService(ServiceAccessType sat) {
749 if (!history_service_created_) {
[email protected]90ef13132008-08-27 03:27:46750 history_service_created_ = true;
initial.commit09911bf2008-07-26 23:55:29751 scoped_refptr<HistoryService> history(new HistoryService(this));
[email protected]d8e41ed2008-09-11 15:22:32752 if (!history->Init(GetPath(), GetBookmarkModel()))
initial.commit09911bf2008-07-26 23:55:29753 return NULL;
754 history_service_.swap(history);
initial.commit09911bf2008-07-26 23:55:29755
756 // Send out the notification that the history service was created.
757 NotificationService::current()->
758 Notify(NOTIFY_HISTORY_CREATED, Source<Profile>(this),
759 Details<HistoryService>(history_service_.get()));
760 }
761 return history_service_.get();
762}
763
initial.commit09911bf2008-07-26 23:55:29764TemplateURLModel* ProfileImpl::GetTemplateURLModel() {
765 if (!template_url_model_.get())
766 template_url_model_.reset(new TemplateURLModel(this));
767 return template_url_model_.get();
768}
769
770TemplateURLFetcher* ProfileImpl::GetTemplateURLFetcher() {
771 if (!template_url_fetcher_.get())
772 template_url_fetcher_.reset(new TemplateURLFetcher(this));
773 return template_url_fetcher_.get();
774}
775
776WebDataService* ProfileImpl::GetWebDataService(ServiceAccessType sat) {
777 if (!created_web_data_service_)
778 CreateWebDataService();
779 return web_data_service_.get();
780}
781
782void ProfileImpl::CreateWebDataService() {
783 DCHECK(!created_web_data_service_ && web_data_service_.get() == NULL);
784 created_web_data_service_ = true;
785 scoped_refptr<WebDataService> wds(new WebDataService());
786 if (!wds->Init(GetPath()))
787 return;
788 web_data_service_.swap(wds);
789}
790
791DownloadManager* ProfileImpl::GetDownloadManager() {
792 if (!created_download_manager_) {
793 scoped_refptr<DownloadManager> dlm(new DownloadManager);
794 dlm->Init(this);
795 created_download_manager_ = true;
796 download_manager_.swap(dlm);
797 }
798 return download_manager_.get();
799}
800
801bool ProfileImpl::HasCreatedDownloadManager() const {
802 return created_download_manager_;
803}
804
805SessionService* ProfileImpl::GetSessionService() {
806 if (!session_service_.get() && !shutdown_session_service_) {
807 session_service_ = new SessionService(this);
808 session_service_->ResetFromCurrentBrowsers();
809 }
810 return session_service_.get();
811}
812
813void ProfileImpl::ShutdownSessionService() {
814 if (shutdown_session_service_)
815 return;
816
817 // We're about to exit, force creation of the session service if it hasn't
818 // been created yet. We do this to ensure session state matches the point in
819 // time the user exited.
820 GetSessionService();
821 shutdown_session_service_ = true;
822 session_service_ = NULL;
823}
824
825bool ProfileImpl::HasSessionService() const {
826 return (session_service_.get() != NULL);
827}
828
829std::wstring ProfileImpl::GetName() {
830 return GetPrefs()->GetString(prefs::kProfileName);
831}
832void ProfileImpl::SetName(const std::wstring& name) {
833 GetPrefs()->SetString(prefs::kProfileName, name);
834}
835
836std::wstring ProfileImpl::GetID() {
837 return GetPrefs()->GetString(prefs::kProfileID);
838}
839void ProfileImpl::SetID(const std::wstring& id) {
840 GetPrefs()->SetString(prefs::kProfileID, id);
841}
842
initial.commit09911bf2008-07-26 23:55:29843bool ProfileImpl::DidLastSessionExitCleanly() {
844 // last_session_exited_cleanly_ is set when the preferences are loaded. Force
845 // it to be set by asking for the prefs.
846 GetPrefs();
847 return last_session_exited_cleanly_;
848}
849
[email protected]d8e41ed2008-09-11 15:22:32850BookmarkModel* ProfileImpl::GetBookmarkModel() {
[email protected]90ef13132008-08-27 03:27:46851 if (!bookmark_bar_model_.get()) {
[email protected]d8e41ed2008-09-11 15:22:32852 bookmark_bar_model_.reset(new BookmarkModel(this));
[email protected]90ef13132008-08-27 03:27:46853 bookmark_bar_model_->Load();
854 }
initial.commit09911bf2008-07-26 23:55:29855 return bookmark_bar_model_.get();
856}
857
858bool ProfileImpl::IsSameProfile(Profile* profile) {
859 if (profile == static_cast<Profile*>(this))
860 return true;
861 OffTheRecordProfileImpl* otr_profile = off_the_record_profile_.get();
862 return otr_profile && profile == static_cast<Profile*>(otr_profile);
863}
864
865Time ProfileImpl::GetStartTime() const {
866 return start_time_;
867}
868
869TabRestoreService* ProfileImpl::GetTabRestoreService() {
870 if (!tab_restore_service_.get())
871 tab_restore_service_.reset(new TabRestoreService(this));
872 return tab_restore_service_.get();
873}
874
875void ProfileImpl::ResetTabRestoreService() {
876 tab_restore_service_.reset(NULL);
877}
878
[email protected]20930852008-10-15 19:30:41879// To be run in the IO thread to notify all resource message filters that the
880// spellchecker has changed.
881class NotifySpellcheckerChangeTask : public Task {
882 public:
883 NotifySpellcheckerChangeTask(
[email protected]e7244d82008-10-29 18:13:26884 Profile* profile,
885 const SpellcheckerReinitializedDetails& spellchecker)
[email protected]20930852008-10-15 19:30:41886 : profile_(profile),
887 spellchecker_(spellchecker) {
888 }
889
890 private:
891 void Run(void) {
892 NotificationService::current()->Notify(
893 NOTIFY_SPELLCHECKER_REINITIALIZED,
894 Source<Profile>(profile_),
895 Details<SpellcheckerReinitializedDetails>(&spellchecker_));
896 }
897
898 Profile* profile_;
899 SpellcheckerReinitializedDetails spellchecker_;
900};
901
[email protected]e7244d82008-10-29 18:13:26902void ProfileImpl::InitializeSpellChecker(bool need_to_broadcast) {
[email protected]20930852008-10-15 19:30:41903 // The I/O thread may be NULL during testing.
904 base::Thread* io_thread = g_browser_process->io_thread();
905 if (spellchecker_) {
906 // The spellchecker must be deleted on the I/O thread.
907 // A dummy variable to aid in logical clarity.
908 SpellChecker* last_spellchecker = spellchecker_;
909
910 if (io_thread)
911 io_thread->message_loop()->ReleaseSoon(FROM_HERE, last_spellchecker);
912 else // during testing, we don't have an I/O thread
913 last_spellchecker->Release();
[email protected]20930852008-10-15 19:30:41914 }
915
[email protected]20930852008-10-15 19:30:41916 // Retrieve the (perhaps updated recently) dictionary name from preferences.
917 PrefService* prefs = GetPrefs();
[email protected]e7244d82008-10-29 18:13:26918 bool enable_spellcheck = prefs->GetBoolean(prefs::kEnableSpellCheck);
[email protected]20930852008-10-15 19:30:41919
[email protected]e7244d82008-10-29 18:13:26920 if (enable_spellcheck) {
921 std::wstring dict_dir;
922 PathService::Get(chrome::DIR_APP_DICTIONARIES, &dict_dir);
923 std::wstring language = prefs->GetString(prefs::kSpellCheckDictionary);
924
925 // Note that, as the object pointed to by previously by spellchecker_
926 // is being deleted in the io thread, the spellchecker_ can be made to point
927 // to a new object (RE-initialized) in parallel in this UI thread.
928 spellchecker_ = new SpellChecker(dict_dir, language, GetRequestContext(),
929 L"");
930 spellchecker_->AddRef(); // Manual refcounting.
931 } else {
932 spellchecker_ = NULL;
933 }
[email protected]20930852008-10-15 19:30:41934
935 if (need_to_broadcast && io_thread) { // Notify resource message filters.
936 SpellcheckerReinitializedDetails scoped_spellchecker;
937 scoped_spellchecker.spellchecker = spellchecker_;
[email protected]e7244d82008-10-29 18:13:26938 if (io_thread) {
939 io_thread->message_loop()->PostTask(
940 FROM_HERE,
941 new NotifySpellcheckerChangeTask(this, scoped_spellchecker));
942 }
[email protected]20930852008-10-15 19:30:41943 }
944}
945
[email protected]e7244d82008-10-29 18:13:26946void ProfileImpl::ReinitializeSpellChecker() {
947 InitializeSpellChecker(true);
948}
949
initial.commit09911bf2008-07-26 23:55:29950SpellChecker* ProfileImpl::GetSpellChecker() {
951 if (!spellchecker_) {
[email protected]20930852008-10-15 19:30:41952 // This is where spellchecker gets initialized. Note that this is being
953 // initialized in the ui_thread. However, this is not a problem as long as
954 // it is *used* in the io thread.
955 // TODO (sidchat) One day, change everything so that spellchecker gets
956 // initialized in the IO thread itself.
[email protected]e7244d82008-10-29 18:13:26957 InitializeSpellChecker(false);
initial.commit09911bf2008-07-26 23:55:29958 }
[email protected]20930852008-10-15 19:30:41959
initial.commit09911bf2008-07-26 23:55:29960 return spellchecker_;
961}
962
963void ProfileImpl::MarkAsCleanShutdown() {
964 if (prefs_.get()) {
965 // The session cleanly exited, set kSessionExitedCleanly appropriately.
966 prefs_->SetBoolean(prefs::kSessionExitedCleanly, true);
967
968 // NOTE: If you change what thread this writes on, be sure and update
969 // ChromeFrame::EndSession().
970 prefs_->SavePersistentPrefs(g_browser_process->file_thread());
971 }
972}
973
[email protected]e7244d82008-10-29 18:13:26974void ProfileImpl::Observe(NotificationType type,
975 const NotificationSource& source,
976 const NotificationDetails& details) {
977 if (NOTIFY_PREF_CHANGED == type) {
978 std::wstring* pref_name_in = Details<std::wstring>(details).ptr();
979 PrefService* prefs = Source<PrefService>(source).ptr();
980 DCHECK(pref_name_in && prefs);
981 if (*pref_name_in == prefs::kSpellCheckDictionary ||
982 *pref_name_in == prefs::kEnableSpellCheck) {
983 InitializeSpellChecker(true);
984 }
985 }
986}
987
initial.commit09911bf2008-07-26 23:55:29988void ProfileImpl::StopCreateSessionServiceTimer() {
[email protected]2d316662008-09-03 18:18:14989 create_session_service_timer_.Stop();
initial.commit09911bf2008-07-26 23:55:29990}
[email protected]3a453fa2008-08-15 18:46:34991
992#ifdef CHROME_PERSONALIZATION
[email protected]57d3d0a2008-09-24 00:50:07993ProfilePersonalization* ProfileImpl::GetProfilePersonalization() {
994 if (!personalization_.get())
995 personalization_.reset(
996 Personalization::CreateProfilePersonalization(this));
997 return personalization_.get();
[email protected]3a453fa2008-08-15 18:46:34998}
license.botbf09a502008-08-24 00:55:55999#endif