blob: 3dd106fc21ef29eebb34ce347e0e9b99fcc0d43a [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"
8#include "base/file_util.h"
9#include "base/lock.h"
10#include "base/path_service.h"
11#include "base/scoped_ptr.h"
12#include "base/string_util.h"
13#include "base/values.h"
[email protected]d8e41ed2008-09-11 15:22:3214#include "chrome/browser/bookmarks/bookmark_model.h"
initial.commit09911bf2008-07-26 23:55:2915#include "chrome/browser/browser_list.h"
16#include "chrome/browser/browser_process.h"
[email protected]cdaa8652008-09-13 02:48:5917#include "chrome/browser/download/download_manager.h"
initial.commit09911bf2008-07-26 23:55:2918#include "chrome/browser/history/history.h"
19#include "chrome/browser/navigation_controller.h"
20#include "chrome/browser/profile_manager.h"
21#include "chrome/browser/render_process_host.h"
22#include "chrome/browser/spellchecker.h"
23#include "chrome/browser/tab_restore_service.h"
24#include "chrome/browser/template_url_fetcher.h"
25#include "chrome/browser/template_url_model.h"
26#include "chrome/browser/visitedlink_master.h"
27#include "chrome/browser/webdata/web_data_service.h"
28#include "chrome/common/chrome_constants.h"
29#include "chrome/common/chrome_paths.h"
30#include "chrome/common/chrome_switches.h"
31#include "chrome/common/net/cookie_monster_sqlite.h"
32#include "chrome/common/notification_service.h"
33#include "chrome/common/pref_names.h"
34#include "chrome/common/pref_service.h"
35#include "chrome/common/resource_bundle.h"
36#include "net/base/cookie_monster.h"
37#include "net/base/cookie_policy.h"
38#include "net/http/http_cache.h"
[email protected]928fb582008-08-11 15:40:2339#include "net/proxy/proxy_service.h"
initial.commit09911bf2008-07-26 23:55:2940#include "net/url_request/url_request_context.h"
41#include "webkit/glue/webkit_glue.h"
42
43// Delay, in milliseconds, before we explicitly create the SessionService.
44static const int kCreateSessionServiceDelayMS = 500;
45
46// A pointer to the request context for the default profile. See comments on
47// Profile::GetDefaultRequestContext.
48URLRequestContext* Profile::default_request_context_;
49
50//static
51void Profile::RegisterUserPrefs(PrefService* prefs) {
52 prefs->RegisterBooleanPref(prefs::kSessionExitedCleanly, true);
53 prefs->RegisterBooleanPref(prefs::kSafeBrowsingEnabled, true);
54}
55
56//static
57Profile* Profile::CreateProfile(const std::wstring& path) {
58 return new ProfileImpl(path);
59}
60
61//static
62URLRequestContext* Profile::GetDefaultRequestContext() {
63 return default_request_context_;
64}
65
66
67// Sets up proxy info if it was specified, otherwise returns NULL. The
68// returned pointer MUST be deleted by the caller if non-NULL.
[email protected]928fb582008-08-11 15:40:2369static net::ProxyInfo* CreateProxyInfo(const CommandLine& command_line) {
70 net::ProxyInfo* proxy_info = NULL;
initial.commit09911bf2008-07-26 23:55:2971
72 if (command_line.HasSwitch(switches::kProxyServer)) {
[email protected]928fb582008-08-11 15:40:2373 proxy_info = new net::ProxyInfo();
initial.commit09911bf2008-07-26 23:55:2974 const std::wstring& proxy_server =
75 command_line.GetSwitchValue(switches::kProxyServer);
[email protected]82f954e2008-08-12 05:11:3876 proxy_info->UseNamedProxy(WideToASCII(proxy_server));
initial.commit09911bf2008-07-26 23:55:2977 }
78
79 return proxy_info;
80}
81
82// Releases the URLRequestContext and sends out a notification about it.
83// Note: runs on IO thread.
84static void ReleaseURLRequestContext(URLRequestContext* context) {
85 NotificationService::current()->Notify(NOTIFY_URL_REQUEST_CONTEXT_RELEASED,
86 Source<URLRequestContext>(context),
87 NotificationService::NoDetails());
88 context->Release();
89}
90
91// A context for URLRequests issued relative to this profile.
92class ProfileImpl::RequestContext : public URLRequestContext,
93 public NotificationObserver {
94 public:
95 // |cookie_store_path| is the local disk path at which the cookie store
96 // is persisted.
97 RequestContext(const std::wstring& cookie_store_path,
98 const std::wstring& disk_cache_path,
99 PrefService* prefs)
100 : prefs_(prefs) {
101 cookie_store_ = NULL;
102
103 // setup user agent
104 user_agent_ = webkit_glue::GetDefaultUserAgent();
105 // set up Accept-Language and Accept-Charset header values
106 // TODO(jungshik) : This may slow down http requests. Perhaps,
107 // we have to come up with a better way to set up these values.
108 accept_language_ = WideToASCII(prefs_->GetString(prefs::kAcceptLanguages));
109 accept_charset_ = WideToASCII(prefs_->GetString(prefs::kDefaultCharset));
110 accept_charset_ += ",*,utf-8";
111
112 CommandLine command_line;
113
[email protected]928fb582008-08-11 15:40:23114 scoped_ptr<net::ProxyInfo> proxy_info(CreateProxyInfo(command_line));
initial.commit09911bf2008-07-26 23:55:29115 net::HttpCache* cache =
116 new net::HttpCache(proxy_info.get(), disk_cache_path, 0);
117
118 bool record_mode = chrome::kRecordModeEnabled &&
119 CommandLine().HasSwitch(switches::kRecordMode);
120 bool playback_mode = CommandLine().HasSwitch(switches::kPlaybackMode);
121
122 if (record_mode || playback_mode) {
123 // Don't use existing cookies and use an in-memory store.
[email protected]8ac1a752008-07-31 19:40:37124 cookie_store_ = new net::CookieMonster();
initial.commit09911bf2008-07-26 23:55:29125 cache->set_mode(
126 record_mode ? net::HttpCache::RECORD : net::HttpCache::PLAYBACK);
127 }
128 http_transaction_factory_ = cache;
129
130 // setup cookie store
131 if (!cookie_store_) {
132 DCHECK(!cookie_store_path.empty());
133 cookie_db_.reset(new SQLitePersistentCookieStore(
134 cookie_store_path, g_browser_process->db_thread()->message_loop()));
[email protected]8ac1a752008-07-31 19:40:37135 cookie_store_ = new net::CookieMonster(cookie_db_.get());
initial.commit09911bf2008-07-26 23:55:29136 }
137
[email protected]8ac1a752008-07-31 19:40:37138 cookie_policy_.SetType(net::CookiePolicy::FromInt(
139 prefs_->GetInteger(prefs::kCookieBehavior)));
initial.commit09911bf2008-07-26 23:55:29140
141 // The first request context to be created is the one for the default
142 // profile - at least until we support multiple profiles.
143 if (!default_request_context_)
144 default_request_context_ = this;
145
146 // Register for notifications about prefs.
147 prefs_->AddPrefObserver(prefs::kAcceptLanguages, this);
148 prefs_->AddPrefObserver(prefs::kCookieBehavior, this);
149 }
150
151 // NotificationObserver implementation.
152 virtual void Observe(NotificationType type,
153 const NotificationSource& source,
154 const NotificationDetails& details) {
155 if (NOTIFY_PREF_CHANGED == type) {
156 std::wstring* pref_name_in = Details<std::wstring>(details).ptr();
157 PrefService* prefs = Source<PrefService>(source).ptr();
158 DCHECK(pref_name_in && prefs);
159 if (*pref_name_in == prefs::kAcceptLanguages) {
160 std::string accept_language =
161 WideToASCII(prefs->GetString(prefs::kAcceptLanguages));
162 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
163 NewRunnableMethod(this,
164 &RequestContext::OnAcceptLanguageChange,
165 accept_language));
166 } else if (*pref_name_in == prefs::kCookieBehavior) {
[email protected]8ac1a752008-07-31 19:40:37167 net::CookiePolicy::Type type = net::CookiePolicy::FromInt(
168 prefs_->GetInteger(prefs::kCookieBehavior));
initial.commit09911bf2008-07-26 23:55:29169 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
170 NewRunnableMethod(this,
171 &RequestContext::OnCookiePolicyChange,
172 type));
173 }
174 }
175 }
176
177 // Since ProfileImpl::RequestContext will be destroyed on IO thread, but all
178 // PrefService observers are needed to clear in before destroying ProfileImpl.
179 // So we use to CleanupBeforeDestroy to do this thing. This function need to
180 // be called on destructor of ProfileImpl.
181 void CleanupBeforeDestroy() {
182 // Unregister for pref notifications.
183 prefs_->RemovePrefObserver(prefs::kAcceptLanguages, this);
184 prefs_->RemovePrefObserver(prefs::kCookieBehavior, this);
185 prefs_ = NULL;
186 }
187
188 void OnAcceptLanguageChange(std::string accept_language) {
189 DCHECK(MessageLoop::current() ==
190 ChromeThread::GetMessageLoop(ChromeThread::IO));
191 accept_language_ = accept_language;
192 }
193
[email protected]8ac1a752008-07-31 19:40:37194 void OnCookiePolicyChange(net::CookiePolicy::Type type) {
initial.commit09911bf2008-07-26 23:55:29195 DCHECK(MessageLoop::current() ==
196 ChromeThread::GetMessageLoop(ChromeThread::IO));
197 cookie_policy_.SetType(type);
198 }
199
200 virtual ~RequestContext() {
201 DCHECK(NULL == prefs_);
202 delete cookie_store_;
203 delete http_transaction_factory_;
204
205 if (default_request_context_ == this)
206 default_request_context_ = NULL;
207 }
208
209 private:
210 scoped_ptr<SQLitePersistentCookieStore> cookie_db_;
211 PrefService* prefs_;
212};
213
214////////////////////////////////////////////////////////////////////////////////
215//
216// An URLRequestContext proxy for OffTheRecord. This request context is
217// really a proxy to the original profile's request context but set
218// is_off_the_record_ to true.
219//
220// TODO(ACW). Do we need to share the FtpAuthCache with the real request context
221// see bug 974328
222//
223// TODO(jackson): http://b/issue?id=1197350 Remove duplicated code from above.
224//
225////////////////////////////////////////////////////////////////////////////////
226class OffTheRecordRequestContext : public URLRequestContext,
227 public NotificationObserver {
228 public:
229 explicit OffTheRecordRequestContext(Profile* profile) {
230 DCHECK(!profile->IsOffTheRecord());
231 prefs_ = profile->GetPrefs();
232 DCHECK(prefs_);
233
234 // The OffTheRecordRequestContext is owned by the OffTheRecordProfileImpl
235 // which is itself owned by the original profile. We reference the original
236 // context to make sure it doesn't go away when we delete the object graph.
237 original_context_ = profile->GetRequestContext();
238
239 CommandLine command_line;
[email protected]928fb582008-08-11 15:40:23240 scoped_ptr<net::ProxyInfo> proxy_info(CreateProxyInfo(command_line));
initial.commit09911bf2008-07-26 23:55:29241
242 http_transaction_factory_ = new net::HttpCache(NULL, 0);
[email protected]8ac1a752008-07-31 19:40:37243 cookie_store_ = new net::CookieMonster;
244 cookie_policy_.SetType(net::CookiePolicy::FromInt(
245 prefs_->GetInteger(prefs::kCookieBehavior)));
initial.commit09911bf2008-07-26 23:55:29246 user_agent_ = original_context_->user_agent();
247 accept_language_ = original_context_->accept_language();
248 accept_charset_ = original_context_->accept_charset();
249 is_off_the_record_ = true;
250
251 // Register for notifications about prefs.
252 prefs_->AddPrefObserver(prefs::kAcceptLanguages, this);
253 prefs_->AddPrefObserver(prefs::kCookieBehavior, this);
254 }
255
256 // Since OffTheRecordProfileImpl maybe be destroyed after destroying
257 // PrefService, but all PrefService observers are needed to clear in
258 // before destroying PrefService. So we use to CleanupBeforeDestroy
259 // to do this thing. This function need to be called on destructor
260 // of ProfileImpl.
261 void CleanupBeforeDestroy() {
262 // Unregister for pref notifications.
263 prefs_->RemovePrefObserver(prefs::kAcceptLanguages, this);
264 prefs_->RemovePrefObserver(prefs::kCookieBehavior, this);
265 prefs_ = NULL;
266 }
267
268 // NotificationObserver implementation.
269 virtual void Observe(NotificationType type,
270 const NotificationSource& source,
271 const NotificationDetails& details) {
272 if (NOTIFY_PREF_CHANGED == type) {
273 std::wstring* pref_name_in = Details<std::wstring>(details).ptr();
274 PrefService* prefs = Source<PrefService>(source).ptr();
275 DCHECK(pref_name_in && prefs);
276 if (*pref_name_in == prefs::kAcceptLanguages) {
277 std::string accept_language =
278 WideToASCII(prefs->GetString(prefs::kAcceptLanguages));
279 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
280 NewRunnableMethod(
281 this,
282 &OffTheRecordRequestContext::OnAcceptLanguageChange,
283 accept_language));
284 } else if (*pref_name_in == prefs::kCookieBehavior) {
[email protected]8ac1a752008-07-31 19:40:37285 net::CookiePolicy::Type type = net::CookiePolicy::FromInt(
286 prefs_->GetInteger(prefs::kCookieBehavior));
initial.commit09911bf2008-07-26 23:55:29287 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
288 NewRunnableMethod(this,
289 &OffTheRecordRequestContext::OnCookiePolicyChange,
290 type));
291 }
292 }
293 }
294
295 void OnAcceptLanguageChange(std::string accept_language) {
296 DCHECK(MessageLoop::current() ==
297 ChromeThread::GetMessageLoop(ChromeThread::IO));
298 accept_language_ = accept_language;
299 }
300
[email protected]8ac1a752008-07-31 19:40:37301 void OnCookiePolicyChange(net::CookiePolicy::Type type) {
initial.commit09911bf2008-07-26 23:55:29302 DCHECK(MessageLoop::current() ==
303 ChromeThread::GetMessageLoop(ChromeThread::IO));
304 cookie_policy_.SetType(type);
305 }
306
307 virtual ~OffTheRecordRequestContext() {
308 DCHECK(NULL == prefs_);
309 delete cookie_store_;
310 delete http_transaction_factory_;
311 // The OffTheRecordRequestContext simply act as a proxy to the real context.
312 // There is nothing else to delete.
313 }
314
315 private:
316 // The original (non off the record) URLRequestContext.
317 scoped_refptr<URLRequestContext> original_context_;
318 PrefService* prefs_;
319
320 DISALLOW_EVIL_CONSTRUCTORS(OffTheRecordRequestContext);
321};
322
323////////////////////////////////////////////////////////////////////////////////
324//
325// OffTheRecordProfileImpl is a profile subclass that wraps an existing profile
326// to make it suitable for the off the record mode.
327//
328////////////////////////////////////////////////////////////////////////////////
329class OffTheRecordProfileImpl : public Profile,
330 public NotificationObserver {
331 public:
332 explicit OffTheRecordProfileImpl(Profile* real_profile)
333 : profile_(real_profile),
334 start_time_(Time::Now()) {
335 request_context_ = new OffTheRecordRequestContext(real_profile);
336 request_context_->AddRef();
337 // Register for browser close notifications so we can detect when the last
338 // off-the-record window is closed, in which case we can clean our states
339 // (cookies, downloads...).
340 NotificationService::current()->AddObserver(
341 this, NOTIFY_BROWSER_CLOSED, NotificationService::AllSources());
342 }
343
344 virtual ~OffTheRecordProfileImpl() {
345 if (request_context_) {
346 request_context_->CleanupBeforeDestroy();
347 // Clean up request context on IO thread.
348 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
349 NewRunnableFunction(&ReleaseURLRequestContext, request_context_));
350 request_context_ = NULL;
351 }
352 NotificationService::current()->RemoveObserver(
353 this, NOTIFY_BROWSER_CLOSED, NotificationService::AllSources());
354 }
355
356 virtual std::wstring GetPath() { return profile_->GetPath(); }
357
358 virtual bool IsOffTheRecord() {
359 return true;
360 }
361
362 virtual Profile* GetOffTheRecordProfile() {
363 return this;
364 }
365
366 virtual Profile* GetOriginalProfile() {
367 return profile_;
368 }
369
370 virtual VisitedLinkMaster* GetVisitedLinkMaster() {
371 return profile_->GetVisitedLinkMaster();
372 }
373
374 virtual HistoryService* GetHistoryService(ServiceAccessType sat) {
375 if (sat == EXPLICIT_ACCESS) {
376 return profile_->GetHistoryService(sat);
377 } else {
378 NOTREACHED() << "This profile is OffTheRecord";
379 return NULL;
380 }
381 }
382
initial.commit09911bf2008-07-26 23:55:29383 virtual WebDataService* GetWebDataService(ServiceAccessType sat) {
384 if (sat == EXPLICIT_ACCESS) {
385 return profile_->GetWebDataService(sat);
386 } else {
387 NOTREACHED() << "This profile is OffTheRecord";
388 return NULL;
389 }
390 }
391
392 virtual PrefService* GetPrefs() {
393 return profile_->GetPrefs();
394 }
395
396 virtual TemplateURLModel* GetTemplateURLModel() {
397 return profile_->GetTemplateURLModel();
398 }
399
400 virtual TemplateURLFetcher* GetTemplateURLFetcher() {
401 return profile_->GetTemplateURLFetcher();
402 }
403
404 virtual DownloadManager* GetDownloadManager() {
405 if (!download_manager_.get()) {
406 scoped_refptr<DownloadManager> dlm(new DownloadManager);
407 dlm->Init(this);
408 download_manager_.swap(dlm);
409 }
410 return download_manager_.get();
411 }
412
413 virtual bool HasCreatedDownloadManager() const {
414 return (download_manager_.get() != NULL);
415 }
416
417 virtual URLRequestContext* GetRequestContext() {
418 return request_context_;
419 }
420
421 virtual SessionService* GetSessionService() {
422 // Don't save any sessions when off the record.
423 return NULL;
424 }
425
426 virtual void ShutdownSessionService() {
427 // We don't allow a session service, nothing to do.
428 }
429
430 virtual bool HasSessionService() const {
431 // We never have a session service.
432 return false;
433 }
434
435 virtual std::wstring GetName() {
436 return profile_->GetName();
437 }
438
439 virtual void SetName(const std::wstring& name) {
440 profile_->SetName(name);
441 }
442
443 virtual std::wstring GetID() {
444 return profile_->GetID();
445 }
446
447 virtual void SetID(const std::wstring& id) {
448 profile_->SetID(id);
449 }
450
451 virtual void RegisterNavigationController(NavigationController* controller) {
452 profile_->RegisterNavigationController(controller);
453 }
454
455 virtual void UnregisterNavigationController(NavigationController*
456 controller) {
457 profile_->UnregisterNavigationController(controller);
458 }
459
460 virtual const Profile::ProfileControllerSet& GetNavigationControllers() {
461 return profile_->GetNavigationControllers();
462 }
463
464 virtual bool DidLastSessionExitCleanly() {
465 return profile_->DidLastSessionExitCleanly();
466 }
467
[email protected]d8e41ed2008-09-11 15:22:32468 virtual BookmarkModel* GetBookmarkModel() {
469 return profile_->GetBookmarkModel();
initial.commit09911bf2008-07-26 23:55:29470 }
471
[email protected]3a453fa2008-08-15 18:46:34472#ifdef CHROME_PERSONALIZATION
473 virtual ProfilePersonalization GetProfilePersonalization() {
474 return profile_->GetProfilePersonalization();
475 }
476#endif
477
initial.commit09911bf2008-07-26 23:55:29478 virtual bool IsSameProfile(Profile* profile) {
479 if (profile == static_cast<Profile*>(this))
480 return true;
481 return profile == profile_;
482 }
483
484 virtual Time GetStartTime() const {
485 return start_time_;
486 }
487
488 virtual TabRestoreService* GetTabRestoreService() {
489 return NULL;
490 }
491
492 virtual void ResetTabRestoreService() {
493 }
494
495 virtual SpellChecker* GetSpellChecker() {
496 return profile_->GetSpellChecker();
497 }
498
499 virtual void MarkAsCleanShutdown() {
500 }
501
502 virtual void ExitedOffTheRecordMode() {
503 // Drop our download manager so we forget about all the downloads made
504 // in off-the-record mode.
505 download_manager_ = NULL;
506 }
507
508 virtual void Observe(NotificationType type,
509 const NotificationSource& source,
510 const NotificationDetails& details) {
511 DCHECK_EQ(NOTIFY_BROWSER_CLOSED, type);
512 // We are only interested in OTR browser closing.
513 if (Source<Browser>(source)->profile() != this)
514 return;
515
516 // Let's check if we still have an Off The Record window opened.
517 // Note that we check against 1 as this notification is sent before the
518 // browser window is actually removed from the list.
519 if (BrowserList::GetBrowserCount(this) <= 1)
520 ExitedOffTheRecordMode();
521 }
522
523 private:
524 // The real underlying profile.
525 Profile* profile_;
526
527 // A proxy to the real request context.
528 OffTheRecordRequestContext* request_context_;
529
530 // The download manager that only stores downloaded items in memory.
531 scoped_refptr<DownloadManager> download_manager_;
532
533 // Time we were started.
534 Time start_time_;
535
536 DISALLOW_EVIL_CONSTRUCTORS(OffTheRecordProfileImpl);
537};
538
539ProfileImpl::ProfileImpl(const std::wstring& path)
540 : path_(path),
541 off_the_record_(false),
542 history_service_created_(false),
543 created_web_data_service_(false),
544 created_download_manager_(false),
545 request_context_(NULL),
initial.commit09911bf2008-07-26 23:55:29546 start_time_(Time::Now()),
547 spellchecker_(NULL),
[email protected]3a453fa2008-08-15 18:46:34548#ifdef CHROME_PERSONALIZATION
549 personalization_(NULL),
550#endif
initial.commit09911bf2008-07-26 23:55:29551 shutdown_session_service_(false) {
552 DCHECK(!path.empty()) << "Using an empty path will attempt to write " <<
553 "profile files to the root directory!";
[email protected]2d316662008-09-03 18:18:14554 create_session_service_timer_.Start(
555 TimeDelta::FromMilliseconds(kCreateSessionServiceDelayMS), this,
556 &ProfileImpl::EnsureSessionServiceCreated);
initial.commit09911bf2008-07-26 23:55:29557}
558
559ProfileImpl::~ProfileImpl() {
560 tab_restore_service_.reset();
561
562 StopCreateSessionServiceTimer();
563 // TemplateURLModel schedules a task on the WebDataService from its
564 // destructor. Delete it first to ensure the task gets scheduled before we
565 // shut down the database.
566 template_url_model_.reset();
567
568 // The download manager queries the history system and should be deleted
569 // before the history is shutdown so it can properly cancel all requests.
570 download_manager_ = NULL;
571
[email protected]3a453fa2008-08-15 18:46:34572#ifdef CHROME_PERSONALIZATION
573 Personalization::CleanupProfilePersonalization(personalization_);
574 personalization_ = NULL;
575#endif
576
initial.commit09911bf2008-07-26 23:55:29577 // Both HistoryService and WebDataService maintain threads for background
578 // processing. Its possible each thread still has tasks on it that have
579 // increased the ref count of the service. In such a situation, when we
580 // decrement the refcount, it won't be 0, and the threads/databases aren't
581 // properly shut down. By explicitly calling Cleanup/Shutdown we ensure the
582 // databases are properly closed.
583 if (web_data_service_.get())
584 web_data_service_->Shutdown();
585
586 if (history_service_.get())
587 history_service_->Cleanup();
588
589 // The I/O thread may be NULL during testing.
[email protected]ab820df2008-08-26 05:55:10590 base::Thread* io_thread = g_browser_process->io_thread();
initial.commit09911bf2008-07-26 23:55:29591
592 if (spellchecker_) {
593 // The spellchecker must be deleted on the I/O thread. During testing, we
594 // don't have an I/O thread.
595 if (io_thread)
596 io_thread->message_loop()->ReleaseSoon(FROM_HERE, spellchecker_);
597 else
598 spellchecker_->Release();
599 }
600
601 if (request_context_) {
602 request_context_->CleanupBeforeDestroy();
603 // Clean up request context on IO thread.
604 io_thread->message_loop()->PostTask(FROM_HERE,
605 NewRunnableFunction(&ReleaseURLRequestContext, request_context_));
606 request_context_ = NULL;
607 }
608
[email protected]d8e41ed2008-09-11 15:22:32609 // HistoryService may call into the BookmarkModel, as such we need to
610 // delete HistoryService before the BookmarkModel. The destructor for
[email protected]90ef13132008-08-27 03:27:46611 // HistoryService will join with HistoryService's backend thread so that
612 // by the time the destructor has finished we're sure it will no longer call
[email protected]d8e41ed2008-09-11 15:22:32613 // into the BookmarkModel.
[email protected]90ef13132008-08-27 03:27:46614 history_service_ = NULL;
615 bookmark_bar_model_.reset();
616
initial.commit09911bf2008-07-26 23:55:29617 MarkAsCleanShutdown();
618}
619
620std::wstring ProfileImpl::GetPath() {
621 return path_;
622}
623
624bool ProfileImpl::IsOffTheRecord() {
625 return false;
626}
627
628Profile* ProfileImpl::GetOffTheRecordProfile() {
629 if (!off_the_record_profile_.get()) {
630 scoped_ptr<OffTheRecordProfileImpl> p(new OffTheRecordProfileImpl(this));
631 off_the_record_profile_.swap(p);
632 }
633 return off_the_record_profile_.get();
634}
635
636Profile* ProfileImpl::GetOriginalProfile() {
637 return this;
638}
639
640static void BroadcastNewHistoryTable(SharedMemory* table_memory) {
641 if (!table_memory)
642 return;
643
644 // send to all RenderProcessHosts
645 for (RenderProcessHost::iterator i = RenderProcessHost::begin();
646 i != RenderProcessHost::end(); i++) {
647 if (!i->second->channel())
648 continue;
649
650 SharedMemoryHandle new_table;
651 HANDLE process = i->second->process();
652 if (!process) {
653 // process can be null if it's started with the --single-process flag.
654 process = GetCurrentProcess();
655 }
656
657 table_memory->ShareToProcess(process, &new_table);
658 IPC::Message* msg = new ViewMsg_VisitedLink_NewTable(new_table);
659 i->second->channel()->Send(msg);
660 }
661}
662
663VisitedLinkMaster* ProfileImpl::GetVisitedLinkMaster() {
664 if (!visited_link_master_.get()) {
665 scoped_ptr<VisitedLinkMaster> visited_links(
666 new VisitedLinkMaster(g_browser_process->file_thread(),
667 BroadcastNewHistoryTable, this));
668 if (!visited_links->Init())
669 return NULL;
670 visited_link_master_.swap(visited_links);
671 }
672
673 return visited_link_master_.get();
674}
675
676PrefService* ProfileImpl::GetPrefs() {
677 if (!prefs_.get()) {
678 prefs_.reset(new PrefService(GetPrefFilePath()));
679
680 // The Profile class and ProfileManager class may read some prefs so
681 // register known prefs as soon as possible.
682 Profile::RegisterUserPrefs(prefs_.get());
683 ProfileManager::RegisterUserPrefs(prefs_.get());
684
685 // The last session exited cleanly if there is no pref for
686 // kSessionExitedCleanly or the value for kSessionExitedCleanly is true.
687 last_session_exited_cleanly_ =
688 prefs_->GetBoolean(prefs::kSessionExitedCleanly);
689 // Mark the session as open.
690 prefs_->SetBoolean(prefs::kSessionExitedCleanly, false);
691 // Make sure we save to disk that the session has opened.
692 prefs_->ScheduleSavePersistentPrefs(g_browser_process->file_thread());
693 }
694
695 return prefs_.get();
696}
697
698std::wstring ProfileImpl::GetPrefFilePath() {
699 std::wstring pref_file_path = path_;
700 file_util::AppendToPath(&pref_file_path, chrome::kPreferencesFilename);
701 return pref_file_path;
702}
703
704URLRequestContext* ProfileImpl::GetRequestContext() {
705 if (!request_context_) {
706 std::wstring cookie_path = GetPath();
707 file_util::AppendToPath(&cookie_path, chrome::kCookieFilename);
708 std::wstring cache_path = GetPath();
709 file_util::AppendToPath(&cache_path, chrome::kCacheDirname);
710 request_context_ = new ProfileImpl::RequestContext(cookie_path, cache_path, GetPrefs());
711 request_context_->AddRef();
712
713 DCHECK(request_context_->cookie_store());
714 }
715
716 return request_context_;
717}
718
719HistoryService* ProfileImpl::GetHistoryService(ServiceAccessType sat) {
720 if (!history_service_created_) {
[email protected]90ef13132008-08-27 03:27:46721 history_service_created_ = true;
initial.commit09911bf2008-07-26 23:55:29722 scoped_refptr<HistoryService> history(new HistoryService(this));
[email protected]d8e41ed2008-09-11 15:22:32723 if (!history->Init(GetPath(), GetBookmarkModel()))
initial.commit09911bf2008-07-26 23:55:29724 return NULL;
725 history_service_.swap(history);
initial.commit09911bf2008-07-26 23:55:29726
727 // Send out the notification that the history service was created.
728 NotificationService::current()->
729 Notify(NOTIFY_HISTORY_CREATED, Source<Profile>(this),
730 Details<HistoryService>(history_service_.get()));
731 }
732 return history_service_.get();
733}
734
initial.commit09911bf2008-07-26 23:55:29735TemplateURLModel* ProfileImpl::GetTemplateURLModel() {
736 if (!template_url_model_.get())
737 template_url_model_.reset(new TemplateURLModel(this));
738 return template_url_model_.get();
739}
740
741TemplateURLFetcher* ProfileImpl::GetTemplateURLFetcher() {
742 if (!template_url_fetcher_.get())
743 template_url_fetcher_.reset(new TemplateURLFetcher(this));
744 return template_url_fetcher_.get();
745}
746
747WebDataService* ProfileImpl::GetWebDataService(ServiceAccessType sat) {
748 if (!created_web_data_service_)
749 CreateWebDataService();
750 return web_data_service_.get();
751}
752
753void ProfileImpl::CreateWebDataService() {
754 DCHECK(!created_web_data_service_ && web_data_service_.get() == NULL);
755 created_web_data_service_ = true;
756 scoped_refptr<WebDataService> wds(new WebDataService());
757 if (!wds->Init(GetPath()))
758 return;
759 web_data_service_.swap(wds);
760}
761
762DownloadManager* ProfileImpl::GetDownloadManager() {
763 if (!created_download_manager_) {
764 scoped_refptr<DownloadManager> dlm(new DownloadManager);
765 dlm->Init(this);
766 created_download_manager_ = true;
767 download_manager_.swap(dlm);
768 }
769 return download_manager_.get();
770}
771
772bool ProfileImpl::HasCreatedDownloadManager() const {
773 return created_download_manager_;
774}
775
776SessionService* ProfileImpl::GetSessionService() {
777 if (!session_service_.get() && !shutdown_session_service_) {
778 session_service_ = new SessionService(this);
779 session_service_->ResetFromCurrentBrowsers();
780 }
781 return session_service_.get();
782}
783
784void ProfileImpl::ShutdownSessionService() {
785 if (shutdown_session_service_)
786 return;
787
788 // We're about to exit, force creation of the session service if it hasn't
789 // been created yet. We do this to ensure session state matches the point in
790 // time the user exited.
791 GetSessionService();
792 shutdown_session_service_ = true;
793 session_service_ = NULL;
794}
795
796bool ProfileImpl::HasSessionService() const {
797 return (session_service_.get() != NULL);
798}
799
800std::wstring ProfileImpl::GetName() {
801 return GetPrefs()->GetString(prefs::kProfileName);
802}
803void ProfileImpl::SetName(const std::wstring& name) {
804 GetPrefs()->SetString(prefs::kProfileName, name);
805}
806
807std::wstring ProfileImpl::GetID() {
808 return GetPrefs()->GetString(prefs::kProfileID);
809}
810void ProfileImpl::SetID(const std::wstring& id) {
811 GetPrefs()->SetString(prefs::kProfileID, id);
812}
813
814void ProfileImpl::RegisterNavigationController(
815 NavigationController* controller) {
816 controllers_.insert(controller);
817}
818
819void ProfileImpl::UnregisterNavigationController(
820 NavigationController* controller) {
821 controllers_.erase(controller);
822}
823
824const Profile::ProfileControllerSet& ProfileImpl::GetNavigationControllers() {
825 return controllers_;
826}
827
828bool ProfileImpl::DidLastSessionExitCleanly() {
829 // last_session_exited_cleanly_ is set when the preferences are loaded. Force
830 // it to be set by asking for the prefs.
831 GetPrefs();
832 return last_session_exited_cleanly_;
833}
834
[email protected]d8e41ed2008-09-11 15:22:32835BookmarkModel* ProfileImpl::GetBookmarkModel() {
[email protected]90ef13132008-08-27 03:27:46836 if (!bookmark_bar_model_.get()) {
[email protected]d8e41ed2008-09-11 15:22:32837 bookmark_bar_model_.reset(new BookmarkModel(this));
[email protected]90ef13132008-08-27 03:27:46838 bookmark_bar_model_->Load();
839 }
initial.commit09911bf2008-07-26 23:55:29840 return bookmark_bar_model_.get();
841}
842
843bool ProfileImpl::IsSameProfile(Profile* profile) {
844 if (profile == static_cast<Profile*>(this))
845 return true;
846 OffTheRecordProfileImpl* otr_profile = off_the_record_profile_.get();
847 return otr_profile && profile == static_cast<Profile*>(otr_profile);
848}
849
850Time ProfileImpl::GetStartTime() const {
851 return start_time_;
852}
853
854TabRestoreService* ProfileImpl::GetTabRestoreService() {
855 if (!tab_restore_service_.get())
856 tab_restore_service_.reset(new TabRestoreService(this));
857 return tab_restore_service_.get();
858}
859
860void ProfileImpl::ResetTabRestoreService() {
861 tab_restore_service_.reset(NULL);
862}
863
864SpellChecker* ProfileImpl::GetSpellChecker() {
865 if (!spellchecker_) {
866 // Don't check for an error here. In the extremely unlikely case that this
867 // fails, the spellchecker just won't find the file. This prevents callers
868 // from having to check for NULL return values from this function.
869 std::wstring dict_dir;
870 PathService::Get(chrome::DIR_APP_DICTIONARIES, &dict_dir);
871
872 // Retrieve the dictionary name from preferences or the language DLL.
873 // When we retrieve it from the language DLL.
874 PrefService* prefs = GetPrefs();
875 std::wstring dictionary_name = prefs->GetString(
876 prefs::kSpellCheckDictionary);
877
878 spellchecker_ = new SpellChecker(dict_dir, dictionary_name,
879 GetRequestContext());
880 spellchecker_->AddRef(); // Manual refcounting.
881 }
882 return spellchecker_;
883}
884
885void ProfileImpl::MarkAsCleanShutdown() {
886 if (prefs_.get()) {
887 // The session cleanly exited, set kSessionExitedCleanly appropriately.
888 prefs_->SetBoolean(prefs::kSessionExitedCleanly, true);
889
890 // NOTE: If you change what thread this writes on, be sure and update
891 // ChromeFrame::EndSession().
892 prefs_->SavePersistentPrefs(g_browser_process->file_thread());
893 }
894}
895
896void ProfileImpl::StopCreateSessionServiceTimer() {
[email protected]2d316662008-09-03 18:18:14897 create_session_service_timer_.Stop();
initial.commit09911bf2008-07-26 23:55:29898}
[email protected]3a453fa2008-08-15 18:46:34899
900#ifdef CHROME_PERSONALIZATION
901ProfilePersonalization ProfileImpl::GetProfilePersonalization() {
902 if (!personalization_)
903 personalization_ = Personalization::CreateProfilePersonalization(this);
904 return personalization_;
905}
license.botbf09a502008-08-24 00:55:55906#endif