blob: 6023a2c966c196101414e792912b8f33cb69eff0 [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
[email protected]e1acf6f2008-10-27 20:43:3343using base::Time;
44using base::TimeDelta;
45
initial.commit09911bf2008-07-26 23:55:2946// Delay, in milliseconds, before we explicitly create the SessionService.
47static const int kCreateSessionServiceDelayMS = 500;
48
49// A pointer to the request context for the default profile. See comments on
50// Profile::GetDefaultRequestContext.
51URLRequestContext* Profile::default_request_context_;
52
53//static
54void Profile::RegisterUserPrefs(PrefService* prefs) {
[email protected]430d3f72008-10-27 17:56:5555 prefs->RegisterBooleanPref(prefs::kSearchSuggestEnabled, true);
initial.commit09911bf2008-07-26 23:55:2956 prefs->RegisterBooleanPref(prefs::kSessionExitedCleanly, true);
57 prefs->RegisterBooleanPref(prefs::kSafeBrowsingEnabled, true);
58}
59
60//static
61Profile* Profile::CreateProfile(const std::wstring& path) {
62 return new ProfileImpl(path);
63}
64
65//static
66URLRequestContext* Profile::GetDefaultRequestContext() {
67 return default_request_context_;
68}
69
70
71// Sets up proxy info if it was specified, otherwise returns NULL. The
72// returned pointer MUST be deleted by the caller if non-NULL.
[email protected]928fb582008-08-11 15:40:2373static net::ProxyInfo* CreateProxyInfo(const CommandLine& command_line) {
74 net::ProxyInfo* proxy_info = NULL;
initial.commit09911bf2008-07-26 23:55:2975
76 if (command_line.HasSwitch(switches::kProxyServer)) {
[email protected]928fb582008-08-11 15:40:2377 proxy_info = new net::ProxyInfo();
initial.commit09911bf2008-07-26 23:55:2978 const std::wstring& proxy_server =
79 command_line.GetSwitchValue(switches::kProxyServer);
[email protected]82f954e2008-08-12 05:11:3880 proxy_info->UseNamedProxy(WideToASCII(proxy_server));
initial.commit09911bf2008-07-26 23:55:2981 }
82
83 return proxy_info;
84}
85
86// Releases the URLRequestContext and sends out a notification about it.
87// Note: runs on IO thread.
88static void ReleaseURLRequestContext(URLRequestContext* context) {
89 NotificationService::current()->Notify(NOTIFY_URL_REQUEST_CONTEXT_RELEASED,
90 Source<URLRequestContext>(context),
91 NotificationService::NoDetails());
92 context->Release();
93}
94
95// A context for URLRequests issued relative to this profile.
96class ProfileImpl::RequestContext : public URLRequestContext,
97 public NotificationObserver {
98 public:
99 // |cookie_store_path| is the local disk path at which the cookie store
100 // is persisted.
101 RequestContext(const std::wstring& cookie_store_path,
102 const std::wstring& disk_cache_path,
103 PrefService* prefs)
104 : prefs_(prefs) {
105 cookie_store_ = NULL;
106
107 // setup user agent
[email protected]f5db58d2008-09-24 20:48:29108 user_agent_ = webkit_glue::GetUserAgent();
initial.commit09911bf2008-07-26 23:55:29109 // set up Accept-Language and Accept-Charset header values
110 // TODO(jungshik) : This may slow down http requests. Perhaps,
111 // we have to come up with a better way to set up these values.
112 accept_language_ = WideToASCII(prefs_->GetString(prefs::kAcceptLanguages));
113 accept_charset_ = WideToASCII(prefs_->GetString(prefs::kDefaultCharset));
114 accept_charset_ += ",*,utf-8";
115
116 CommandLine command_line;
117
[email protected]928fb582008-08-11 15:40:23118 scoped_ptr<net::ProxyInfo> proxy_info(CreateProxyInfo(command_line));
initial.commit09911bf2008-07-26 23:55:29119 net::HttpCache* cache =
120 new net::HttpCache(proxy_info.get(), disk_cache_path, 0);
121
122 bool record_mode = chrome::kRecordModeEnabled &&
123 CommandLine().HasSwitch(switches::kRecordMode);
124 bool playback_mode = CommandLine().HasSwitch(switches::kPlaybackMode);
125
126 if (record_mode || playback_mode) {
127 // Don't use existing cookies and use an in-memory store.
[email protected]8ac1a752008-07-31 19:40:37128 cookie_store_ = new net::CookieMonster();
initial.commit09911bf2008-07-26 23:55:29129 cache->set_mode(
130 record_mode ? net::HttpCache::RECORD : net::HttpCache::PLAYBACK);
131 }
132 http_transaction_factory_ = cache;
133
134 // setup cookie store
135 if (!cookie_store_) {
136 DCHECK(!cookie_store_path.empty());
137 cookie_db_.reset(new SQLitePersistentCookieStore(
138 cookie_store_path, g_browser_process->db_thread()->message_loop()));
[email protected]8ac1a752008-07-31 19:40:37139 cookie_store_ = new net::CookieMonster(cookie_db_.get());
initial.commit09911bf2008-07-26 23:55:29140 }
141
[email protected]8ac1a752008-07-31 19:40:37142 cookie_policy_.SetType(net::CookiePolicy::FromInt(
143 prefs_->GetInteger(prefs::kCookieBehavior)));
initial.commit09911bf2008-07-26 23:55:29144
145 // The first request context to be created is the one for the default
146 // profile - at least until we support multiple profiles.
147 if (!default_request_context_)
148 default_request_context_ = this;
[email protected]3dd1f6d52008-09-15 18:28:09149 NotificationService::current()->Notify(
150 NOTIFY_DEFAULT_REQUEST_CONTEXT_AVAILABLE,
151 NotificationService::AllSources(), NotificationService::NoDetails());
initial.commit09911bf2008-07-26 23:55:29152
153 // Register for notifications about prefs.
154 prefs_->AddPrefObserver(prefs::kAcceptLanguages, this);
155 prefs_->AddPrefObserver(prefs::kCookieBehavior, this);
156 }
157
158 // NotificationObserver implementation.
159 virtual void Observe(NotificationType type,
160 const NotificationSource& source,
161 const NotificationDetails& details) {
162 if (NOTIFY_PREF_CHANGED == type) {
163 std::wstring* pref_name_in = Details<std::wstring>(details).ptr();
164 PrefService* prefs = Source<PrefService>(source).ptr();
165 DCHECK(pref_name_in && prefs);
166 if (*pref_name_in == prefs::kAcceptLanguages) {
167 std::string accept_language =
168 WideToASCII(prefs->GetString(prefs::kAcceptLanguages));
169 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
170 NewRunnableMethod(this,
171 &RequestContext::OnAcceptLanguageChange,
172 accept_language));
173 } else if (*pref_name_in == prefs::kCookieBehavior) {
[email protected]8ac1a752008-07-31 19:40:37174 net::CookiePolicy::Type type = net::CookiePolicy::FromInt(
175 prefs_->GetInteger(prefs::kCookieBehavior));
initial.commit09911bf2008-07-26 23:55:29176 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
177 NewRunnableMethod(this,
178 &RequestContext::OnCookiePolicyChange,
179 type));
180 }
181 }
182 }
183
184 // Since ProfileImpl::RequestContext will be destroyed on IO thread, but all
185 // PrefService observers are needed to clear in before destroying ProfileImpl.
186 // So we use to CleanupBeforeDestroy to do this thing. This function need to
187 // be called on destructor of ProfileImpl.
188 void CleanupBeforeDestroy() {
189 // Unregister for pref notifications.
190 prefs_->RemovePrefObserver(prefs::kAcceptLanguages, this);
191 prefs_->RemovePrefObserver(prefs::kCookieBehavior, this);
192 prefs_ = NULL;
193 }
194
195 void OnAcceptLanguageChange(std::string accept_language) {
196 DCHECK(MessageLoop::current() ==
197 ChromeThread::GetMessageLoop(ChromeThread::IO));
198 accept_language_ = accept_language;
199 }
200
[email protected]8ac1a752008-07-31 19:40:37201 void OnCookiePolicyChange(net::CookiePolicy::Type type) {
initial.commit09911bf2008-07-26 23:55:29202 DCHECK(MessageLoop::current() ==
203 ChromeThread::GetMessageLoop(ChromeThread::IO));
204 cookie_policy_.SetType(type);
205 }
206
207 virtual ~RequestContext() {
208 DCHECK(NULL == prefs_);
209 delete cookie_store_;
210 delete http_transaction_factory_;
211
212 if (default_request_context_ == this)
213 default_request_context_ = NULL;
214 }
215
216 private:
217 scoped_ptr<SQLitePersistentCookieStore> cookie_db_;
218 PrefService* prefs_;
219};
220
221////////////////////////////////////////////////////////////////////////////////
222//
223// An URLRequestContext proxy for OffTheRecord. This request context is
224// really a proxy to the original profile's request context but set
225// is_off_the_record_ to true.
226//
227// TODO(ACW). Do we need to share the FtpAuthCache with the real request context
228// see bug 974328
229//
230// TODO(jackson): http://b/issue?id=1197350 Remove duplicated code from above.
231//
232////////////////////////////////////////////////////////////////////////////////
233class OffTheRecordRequestContext : public URLRequestContext,
234 public NotificationObserver {
235 public:
236 explicit OffTheRecordRequestContext(Profile* profile) {
237 DCHECK(!profile->IsOffTheRecord());
238 prefs_ = profile->GetPrefs();
239 DCHECK(prefs_);
240
241 // The OffTheRecordRequestContext is owned by the OffTheRecordProfileImpl
242 // which is itself owned by the original profile. We reference the original
243 // context to make sure it doesn't go away when we delete the object graph.
244 original_context_ = profile->GetRequestContext();
245
246 CommandLine command_line;
[email protected]928fb582008-08-11 15:40:23247 scoped_ptr<net::ProxyInfo> proxy_info(CreateProxyInfo(command_line));
initial.commit09911bf2008-07-26 23:55:29248
249 http_transaction_factory_ = new net::HttpCache(NULL, 0);
[email protected]8ac1a752008-07-31 19:40:37250 cookie_store_ = new net::CookieMonster;
251 cookie_policy_.SetType(net::CookiePolicy::FromInt(
252 prefs_->GetInteger(prefs::kCookieBehavior)));
initial.commit09911bf2008-07-26 23:55:29253 user_agent_ = original_context_->user_agent();
254 accept_language_ = original_context_->accept_language();
255 accept_charset_ = original_context_->accept_charset();
256 is_off_the_record_ = true;
257
258 // Register for notifications about prefs.
259 prefs_->AddPrefObserver(prefs::kAcceptLanguages, this);
260 prefs_->AddPrefObserver(prefs::kCookieBehavior, this);
261 }
262
263 // Since OffTheRecordProfileImpl maybe be destroyed after destroying
264 // PrefService, but all PrefService observers are needed to clear in
265 // before destroying PrefService. So we use to CleanupBeforeDestroy
266 // to do this thing. This function need to be called on destructor
267 // of ProfileImpl.
268 void CleanupBeforeDestroy() {
269 // Unregister for pref notifications.
270 prefs_->RemovePrefObserver(prefs::kAcceptLanguages, this);
271 prefs_->RemovePrefObserver(prefs::kCookieBehavior, this);
272 prefs_ = NULL;
273 }
274
275 // NotificationObserver implementation.
276 virtual void Observe(NotificationType type,
277 const NotificationSource& source,
278 const NotificationDetails& details) {
279 if (NOTIFY_PREF_CHANGED == type) {
280 std::wstring* pref_name_in = Details<std::wstring>(details).ptr();
281 PrefService* prefs = Source<PrefService>(source).ptr();
282 DCHECK(pref_name_in && prefs);
283 if (*pref_name_in == prefs::kAcceptLanguages) {
284 std::string accept_language =
285 WideToASCII(prefs->GetString(prefs::kAcceptLanguages));
286 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
287 NewRunnableMethod(
288 this,
289 &OffTheRecordRequestContext::OnAcceptLanguageChange,
290 accept_language));
291 } else if (*pref_name_in == prefs::kCookieBehavior) {
[email protected]8ac1a752008-07-31 19:40:37292 net::CookiePolicy::Type type = net::CookiePolicy::FromInt(
293 prefs_->GetInteger(prefs::kCookieBehavior));
initial.commit09911bf2008-07-26 23:55:29294 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
295 NewRunnableMethod(this,
296 &OffTheRecordRequestContext::OnCookiePolicyChange,
297 type));
298 }
299 }
300 }
301
302 void OnAcceptLanguageChange(std::string accept_language) {
303 DCHECK(MessageLoop::current() ==
304 ChromeThread::GetMessageLoop(ChromeThread::IO));
305 accept_language_ = accept_language;
306 }
307
[email protected]8ac1a752008-07-31 19:40:37308 void OnCookiePolicyChange(net::CookiePolicy::Type type) {
initial.commit09911bf2008-07-26 23:55:29309 DCHECK(MessageLoop::current() ==
310 ChromeThread::GetMessageLoop(ChromeThread::IO));
311 cookie_policy_.SetType(type);
312 }
313
314 virtual ~OffTheRecordRequestContext() {
315 DCHECK(NULL == prefs_);
316 delete cookie_store_;
317 delete http_transaction_factory_;
318 // The OffTheRecordRequestContext simply act as a proxy to the real context.
319 // There is nothing else to delete.
320 }
321
322 private:
323 // The original (non off the record) URLRequestContext.
324 scoped_refptr<URLRequestContext> original_context_;
325 PrefService* prefs_;
326
327 DISALLOW_EVIL_CONSTRUCTORS(OffTheRecordRequestContext);
328};
329
330////////////////////////////////////////////////////////////////////////////////
331//
332// OffTheRecordProfileImpl is a profile subclass that wraps an existing profile
333// to make it suitable for the off the record mode.
334//
335////////////////////////////////////////////////////////////////////////////////
336class OffTheRecordProfileImpl : public Profile,
337 public NotificationObserver {
338 public:
339 explicit OffTheRecordProfileImpl(Profile* real_profile)
340 : profile_(real_profile),
341 start_time_(Time::Now()) {
342 request_context_ = new OffTheRecordRequestContext(real_profile);
343 request_context_->AddRef();
344 // Register for browser close notifications so we can detect when the last
345 // off-the-record window is closed, in which case we can clean our states
346 // (cookies, downloads...).
347 NotificationService::current()->AddObserver(
348 this, NOTIFY_BROWSER_CLOSED, NotificationService::AllSources());
349 }
350
351 virtual ~OffTheRecordProfileImpl() {
352 if (request_context_) {
353 request_context_->CleanupBeforeDestroy();
354 // Clean up request context on IO thread.
355 g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
356 NewRunnableFunction(&ReleaseURLRequestContext, request_context_));
357 request_context_ = NULL;
358 }
359 NotificationService::current()->RemoveObserver(
360 this, NOTIFY_BROWSER_CLOSED, NotificationService::AllSources());
361 }
362
363 virtual std::wstring GetPath() { return profile_->GetPath(); }
364
365 virtual bool IsOffTheRecord() {
366 return true;
367 }
368
369 virtual Profile* GetOffTheRecordProfile() {
370 return this;
371 }
372
373 virtual Profile* GetOriginalProfile() {
374 return profile_;
375 }
376
377 virtual VisitedLinkMaster* GetVisitedLinkMaster() {
378 return profile_->GetVisitedLinkMaster();
379 }
380
381 virtual HistoryService* GetHistoryService(ServiceAccessType sat) {
382 if (sat == EXPLICIT_ACCESS) {
383 return profile_->GetHistoryService(sat);
384 } else {
385 NOTREACHED() << "This profile is OffTheRecord";
386 return NULL;
387 }
388 }
389
initial.commit09911bf2008-07-26 23:55:29390 virtual WebDataService* GetWebDataService(ServiceAccessType sat) {
391 if (sat == EXPLICIT_ACCESS) {
392 return profile_->GetWebDataService(sat);
393 } else {
394 NOTREACHED() << "This profile is OffTheRecord";
395 return NULL;
396 }
397 }
398
399 virtual PrefService* GetPrefs() {
400 return profile_->GetPrefs();
401 }
402
403 virtual TemplateURLModel* GetTemplateURLModel() {
404 return profile_->GetTemplateURLModel();
405 }
406
407 virtual TemplateURLFetcher* GetTemplateURLFetcher() {
408 return profile_->GetTemplateURLFetcher();
409 }
410
411 virtual DownloadManager* GetDownloadManager() {
412 if (!download_manager_.get()) {
413 scoped_refptr<DownloadManager> dlm(new DownloadManager);
414 dlm->Init(this);
415 download_manager_.swap(dlm);
416 }
417 return download_manager_.get();
418 }
419
420 virtual bool HasCreatedDownloadManager() const {
421 return (download_manager_.get() != NULL);
422 }
423
424 virtual URLRequestContext* GetRequestContext() {
425 return request_context_;
426 }
427
428 virtual SessionService* GetSessionService() {
429 // Don't save any sessions when off the record.
430 return NULL;
431 }
432
433 virtual void ShutdownSessionService() {
434 // We don't allow a session service, nothing to do.
435 }
436
437 virtual bool HasSessionService() const {
438 // We never have a session service.
439 return false;
440 }
441
442 virtual std::wstring GetName() {
443 return profile_->GetName();
444 }
445
446 virtual void SetName(const std::wstring& name) {
447 profile_->SetName(name);
448 }
449
450 virtual std::wstring GetID() {
451 return profile_->GetID();
452 }
453
454 virtual void SetID(const std::wstring& id) {
455 profile_->SetID(id);
456 }
457
initial.commit09911bf2008-07-26 23:55:29458 virtual bool DidLastSessionExitCleanly() {
459 return profile_->DidLastSessionExitCleanly();
460 }
461
[email protected]d8e41ed2008-09-11 15:22:32462 virtual BookmarkModel* GetBookmarkModel() {
463 return profile_->GetBookmarkModel();
initial.commit09911bf2008-07-26 23:55:29464 }
465
[email protected]3a453fa2008-08-15 18:46:34466#ifdef CHROME_PERSONALIZATION
[email protected]57d3d0a2008-09-24 00:50:07467 virtual ProfilePersonalization* GetProfilePersonalization() {
[email protected]3a453fa2008-08-15 18:46:34468 return profile_->GetProfilePersonalization();
469 }
470#endif
471
initial.commit09911bf2008-07-26 23:55:29472 virtual bool IsSameProfile(Profile* profile) {
473 if (profile == static_cast<Profile*>(this))
474 return true;
475 return profile == profile_;
476 }
477
478 virtual Time GetStartTime() const {
479 return start_time_;
480 }
481
482 virtual TabRestoreService* GetTabRestoreService() {
483 return NULL;
484 }
485
[email protected]20930852008-10-15 19:30:41486 virtual void InitializeSpellChecker() {
487 profile_->InitializeSpellChecker();
488 }
489
initial.commit09911bf2008-07-26 23:55:29490 virtual void ResetTabRestoreService() {
491 }
492
493 virtual SpellChecker* GetSpellChecker() {
494 return profile_->GetSpellChecker();
495 }
496
497 virtual void MarkAsCleanShutdown() {
498 }
499
500 virtual void ExitedOffTheRecordMode() {
501 // Drop our download manager so we forget about all the downloads made
502 // in off-the-record mode.
503 download_manager_ = NULL;
504 }
505
506 virtual void Observe(NotificationType type,
507 const NotificationSource& source,
508 const NotificationDetails& details) {
509 DCHECK_EQ(NOTIFY_BROWSER_CLOSED, type);
510 // We are only interested in OTR browser closing.
511 if (Source<Browser>(source)->profile() != this)
512 return;
513
514 // Let's check if we still have an Off The Record window opened.
515 // Note that we check against 1 as this notification is sent before the
516 // browser window is actually removed from the list.
517 if (BrowserList::GetBrowserCount(this) <= 1)
518 ExitedOffTheRecordMode();
519 }
520
521 private:
522 // The real underlying profile.
523 Profile* profile_;
524
525 // A proxy to the real request context.
526 OffTheRecordRequestContext* request_context_;
527
528 // The download manager that only stores downloaded items in memory.
529 scoped_refptr<DownloadManager> download_manager_;
530
531 // Time we were started.
532 Time start_time_;
533
534 DISALLOW_EVIL_CONSTRUCTORS(OffTheRecordProfileImpl);
535};
536
537ProfileImpl::ProfileImpl(const std::wstring& path)
538 : path_(path),
539 off_the_record_(false),
540 history_service_created_(false),
541 created_web_data_service_(false),
542 created_download_manager_(false),
543 request_context_(NULL),
initial.commit09911bf2008-07-26 23:55:29544 start_time_(Time::Now()),
545 spellchecker_(NULL),
[email protected]3a453fa2008-08-15 18:46:34546#ifdef CHROME_PERSONALIZATION
547 personalization_(NULL),
548#endif
initial.commit09911bf2008-07-26 23:55:29549 shutdown_session_service_(false) {
550 DCHECK(!path.empty()) << "Using an empty path will attempt to write " <<
551 "profile files to the root directory!";
[email protected]2d316662008-09-03 18:18:14552 create_session_service_timer_.Start(
553 TimeDelta::FromMilliseconds(kCreateSessionServiceDelayMS), this,
554 &ProfileImpl::EnsureSessionServiceCreated);
initial.commit09911bf2008-07-26 23:55:29555}
556
557ProfileImpl::~ProfileImpl() {
558 tab_restore_service_.reset();
559
560 StopCreateSessionServiceTimer();
561 // TemplateURLModel schedules a task on the WebDataService from its
562 // destructor. Delete it first to ensure the task gets scheduled before we
563 // shut down the database.
564 template_url_model_.reset();
565
566 // The download manager queries the history system and should be deleted
567 // before the history is shutdown so it can properly cancel all requests.
568 download_manager_ = NULL;
569
[email protected]3a453fa2008-08-15 18:46:34570#ifdef CHROME_PERSONALIZATION
[email protected]57d3d0a2008-09-24 00:50:07571 personalization_.reset();
[email protected]3a453fa2008-08-15 18:46:34572#endif
573
initial.commit09911bf2008-07-26 23:55:29574 // Both HistoryService and WebDataService maintain threads for background
575 // processing. Its possible each thread still has tasks on it that have
576 // increased the ref count of the service. In such a situation, when we
577 // decrement the refcount, it won't be 0, and the threads/databases aren't
578 // properly shut down. By explicitly calling Cleanup/Shutdown we ensure the
579 // databases are properly closed.
580 if (web_data_service_.get())
581 web_data_service_->Shutdown();
582
583 if (history_service_.get())
584 history_service_->Cleanup();
585
586 // The I/O thread may be NULL during testing.
[email protected]ab820df2008-08-26 05:55:10587 base::Thread* io_thread = g_browser_process->io_thread();
initial.commit09911bf2008-07-26 23:55:29588
589 if (spellchecker_) {
590 // The spellchecker must be deleted on the I/O thread. During testing, we
591 // don't have an I/O thread.
592 if (io_thread)
593 io_thread->message_loop()->ReleaseSoon(FROM_HERE, spellchecker_);
594 else
595 spellchecker_->Release();
596 }
597
598 if (request_context_) {
599 request_context_->CleanupBeforeDestroy();
600 // Clean up request context on IO thread.
601 io_thread->message_loop()->PostTask(FROM_HERE,
602 NewRunnableFunction(&ReleaseURLRequestContext, request_context_));
603 request_context_ = NULL;
604 }
605
[email protected]d8e41ed2008-09-11 15:22:32606 // HistoryService may call into the BookmarkModel, as such we need to
607 // delete HistoryService before the BookmarkModel. The destructor for
[email protected]90ef13132008-08-27 03:27:46608 // HistoryService will join with HistoryService's backend thread so that
609 // by the time the destructor has finished we're sure it will no longer call
[email protected]d8e41ed2008-09-11 15:22:32610 // into the BookmarkModel.
[email protected]90ef13132008-08-27 03:27:46611 history_service_ = NULL;
612 bookmark_bar_model_.reset();
613
initial.commit09911bf2008-07-26 23:55:29614 MarkAsCleanShutdown();
615}
616
617std::wstring ProfileImpl::GetPath() {
618 return path_;
619}
620
621bool ProfileImpl::IsOffTheRecord() {
622 return false;
623}
624
625Profile* ProfileImpl::GetOffTheRecordProfile() {
626 if (!off_the_record_profile_.get()) {
627 scoped_ptr<OffTheRecordProfileImpl> p(new OffTheRecordProfileImpl(this));
628 off_the_record_profile_.swap(p);
629 }
630 return off_the_record_profile_.get();
631}
632
633Profile* ProfileImpl::GetOriginalProfile() {
634 return this;
635}
636
637static void BroadcastNewHistoryTable(SharedMemory* table_memory) {
638 if (!table_memory)
639 return;
640
641 // send to all RenderProcessHosts
642 for (RenderProcessHost::iterator i = RenderProcessHost::begin();
643 i != RenderProcessHost::end(); i++) {
644 if (!i->second->channel())
645 continue;
646
647 SharedMemoryHandle new_table;
648 HANDLE process = i->second->process();
649 if (!process) {
650 // process can be null if it's started with the --single-process flag.
651 process = GetCurrentProcess();
652 }
653
654 table_memory->ShareToProcess(process, &new_table);
655 IPC::Message* msg = new ViewMsg_VisitedLink_NewTable(new_table);
656 i->second->channel()->Send(msg);
657 }
658}
659
660VisitedLinkMaster* ProfileImpl::GetVisitedLinkMaster() {
661 if (!visited_link_master_.get()) {
662 scoped_ptr<VisitedLinkMaster> visited_links(
663 new VisitedLinkMaster(g_browser_process->file_thread(),
664 BroadcastNewHistoryTable, this));
665 if (!visited_links->Init())
666 return NULL;
667 visited_link_master_.swap(visited_links);
668 }
669
670 return visited_link_master_.get();
671}
672
673PrefService* ProfileImpl::GetPrefs() {
674 if (!prefs_.get()) {
675 prefs_.reset(new PrefService(GetPrefFilePath()));
676
677 // The Profile class and ProfileManager class may read some prefs so
678 // register known prefs as soon as possible.
679 Profile::RegisterUserPrefs(prefs_.get());
680 ProfileManager::RegisterUserPrefs(prefs_.get());
681
682 // The last session exited cleanly if there is no pref for
683 // kSessionExitedCleanly or the value for kSessionExitedCleanly is true.
684 last_session_exited_cleanly_ =
685 prefs_->GetBoolean(prefs::kSessionExitedCleanly);
686 // Mark the session as open.
687 prefs_->SetBoolean(prefs::kSessionExitedCleanly, false);
688 // Make sure we save to disk that the session has opened.
689 prefs_->ScheduleSavePersistentPrefs(g_browser_process->file_thread());
690 }
691
692 return prefs_.get();
693}
694
695std::wstring ProfileImpl::GetPrefFilePath() {
696 std::wstring pref_file_path = path_;
697 file_util::AppendToPath(&pref_file_path, chrome::kPreferencesFilename);
698 return pref_file_path;
699}
700
701URLRequestContext* ProfileImpl::GetRequestContext() {
702 if (!request_context_) {
703 std::wstring cookie_path = GetPath();
704 file_util::AppendToPath(&cookie_path, chrome::kCookieFilename);
705 std::wstring cache_path = GetPath();
706 file_util::AppendToPath(&cache_path, chrome::kCacheDirname);
[email protected]3dd1f6d52008-09-15 18:28:09707 request_context_ =
708 new ProfileImpl::RequestContext(cookie_path, cache_path, GetPrefs());
initial.commit09911bf2008-07-26 23:55:29709 request_context_->AddRef();
710
711 DCHECK(request_context_->cookie_store());
712 }
713
714 return request_context_;
715}
716
717HistoryService* ProfileImpl::GetHistoryService(ServiceAccessType sat) {
718 if (!history_service_created_) {
[email protected]90ef13132008-08-27 03:27:46719 history_service_created_ = true;
initial.commit09911bf2008-07-26 23:55:29720 scoped_refptr<HistoryService> history(new HistoryService(this));
[email protected]d8e41ed2008-09-11 15:22:32721 if (!history->Init(GetPath(), GetBookmarkModel()))
initial.commit09911bf2008-07-26 23:55:29722 return NULL;
723 history_service_.swap(history);
initial.commit09911bf2008-07-26 23:55:29724
725 // Send out the notification that the history service was created.
726 NotificationService::current()->
727 Notify(NOTIFY_HISTORY_CREATED, Source<Profile>(this),
728 Details<HistoryService>(history_service_.get()));
729 }
730 return history_service_.get();
731}
732
initial.commit09911bf2008-07-26 23:55:29733TemplateURLModel* ProfileImpl::GetTemplateURLModel() {
734 if (!template_url_model_.get())
735 template_url_model_.reset(new TemplateURLModel(this));
736 return template_url_model_.get();
737}
738
739TemplateURLFetcher* ProfileImpl::GetTemplateURLFetcher() {
740 if (!template_url_fetcher_.get())
741 template_url_fetcher_.reset(new TemplateURLFetcher(this));
742 return template_url_fetcher_.get();
743}
744
745WebDataService* ProfileImpl::GetWebDataService(ServiceAccessType sat) {
746 if (!created_web_data_service_)
747 CreateWebDataService();
748 return web_data_service_.get();
749}
750
751void ProfileImpl::CreateWebDataService() {
752 DCHECK(!created_web_data_service_ && web_data_service_.get() == NULL);
753 created_web_data_service_ = true;
754 scoped_refptr<WebDataService> wds(new WebDataService());
755 if (!wds->Init(GetPath()))
756 return;
757 web_data_service_.swap(wds);
758}
759
760DownloadManager* ProfileImpl::GetDownloadManager() {
761 if (!created_download_manager_) {
762 scoped_refptr<DownloadManager> dlm(new DownloadManager);
763 dlm->Init(this);
764 created_download_manager_ = true;
765 download_manager_.swap(dlm);
766 }
767 return download_manager_.get();
768}
769
770bool ProfileImpl::HasCreatedDownloadManager() const {
771 return created_download_manager_;
772}
773
774SessionService* ProfileImpl::GetSessionService() {
775 if (!session_service_.get() && !shutdown_session_service_) {
776 session_service_ = new SessionService(this);
777 session_service_->ResetFromCurrentBrowsers();
778 }
779 return session_service_.get();
780}
781
782void ProfileImpl::ShutdownSessionService() {
783 if (shutdown_session_service_)
784 return;
785
786 // We're about to exit, force creation of the session service if it hasn't
787 // been created yet. We do this to ensure session state matches the point in
788 // time the user exited.
789 GetSessionService();
790 shutdown_session_service_ = true;
791 session_service_ = NULL;
792}
793
794bool ProfileImpl::HasSessionService() const {
795 return (session_service_.get() != NULL);
796}
797
798std::wstring ProfileImpl::GetName() {
799 return GetPrefs()->GetString(prefs::kProfileName);
800}
801void ProfileImpl::SetName(const std::wstring& name) {
802 GetPrefs()->SetString(prefs::kProfileName, name);
803}
804
805std::wstring ProfileImpl::GetID() {
806 return GetPrefs()->GetString(prefs::kProfileID);
807}
808void ProfileImpl::SetID(const std::wstring& id) {
809 GetPrefs()->SetString(prefs::kProfileID, id);
810}
811
initial.commit09911bf2008-07-26 23:55:29812bool ProfileImpl::DidLastSessionExitCleanly() {
813 // last_session_exited_cleanly_ is set when the preferences are loaded. Force
814 // it to be set by asking for the prefs.
815 GetPrefs();
816 return last_session_exited_cleanly_;
817}
818
[email protected]d8e41ed2008-09-11 15:22:32819BookmarkModel* ProfileImpl::GetBookmarkModel() {
[email protected]90ef13132008-08-27 03:27:46820 if (!bookmark_bar_model_.get()) {
[email protected]d8e41ed2008-09-11 15:22:32821 bookmark_bar_model_.reset(new BookmarkModel(this));
[email protected]90ef13132008-08-27 03:27:46822 bookmark_bar_model_->Load();
823 }
initial.commit09911bf2008-07-26 23:55:29824 return bookmark_bar_model_.get();
825}
826
827bool ProfileImpl::IsSameProfile(Profile* profile) {
828 if (profile == static_cast<Profile*>(this))
829 return true;
830 OffTheRecordProfileImpl* otr_profile = off_the_record_profile_.get();
831 return otr_profile && profile == static_cast<Profile*>(otr_profile);
832}
833
834Time ProfileImpl::GetStartTime() const {
835 return start_time_;
836}
837
838TabRestoreService* ProfileImpl::GetTabRestoreService() {
839 if (!tab_restore_service_.get())
840 tab_restore_service_.reset(new TabRestoreService(this));
841 return tab_restore_service_.get();
842}
843
844void ProfileImpl::ResetTabRestoreService() {
845 tab_restore_service_.reset(NULL);
846}
847
[email protected]20930852008-10-15 19:30:41848// To be run in the IO thread to notify all resource message filters that the
849// spellchecker has changed.
850class NotifySpellcheckerChangeTask : public Task {
851 public:
852 NotifySpellcheckerChangeTask(
853 Profile* profile, const SpellcheckerReinitializedDetails& spellchecker)
854 : profile_(profile),
855 spellchecker_(spellchecker) {
856 }
857
858 private:
859 void Run(void) {
860 NotificationService::current()->Notify(
861 NOTIFY_SPELLCHECKER_REINITIALIZED,
862 Source<Profile>(profile_),
863 Details<SpellcheckerReinitializedDetails>(&spellchecker_));
864 }
865
866 Profile* profile_;
867 SpellcheckerReinitializedDetails spellchecker_;
868};
869
870void ProfileImpl::InitializeSpellChecker() {
871 bool need_to_broadcast = false;
872
873 // The I/O thread may be NULL during testing.
874 base::Thread* io_thread = g_browser_process->io_thread();
875 if (spellchecker_) {
876 // The spellchecker must be deleted on the I/O thread.
877 // A dummy variable to aid in logical clarity.
878 SpellChecker* last_spellchecker = spellchecker_;
879
880 if (io_thread)
881 io_thread->message_loop()->ReleaseSoon(FROM_HERE, last_spellchecker);
882 else // during testing, we don't have an I/O thread
883 last_spellchecker->Release();
884
885 need_to_broadcast = true;
886 }
887
888 std::wstring dict_dir;
889 PathService::Get(chrome::DIR_APP_DICTIONARIES, &dict_dir);
890
891 // Retrieve the (perhaps updated recently) dictionary name from preferences.
892 PrefService* prefs = GetPrefs();
893 std::wstring language = prefs->GetString(prefs::kSpellCheckDictionary);
894
895 // Note that, as the object pointed to by previously by spellchecker_
896 // is being deleted in the io thread, the spellchecker_ can be made to point
897 // to a new object (RE-initialized) in parallel in this UI thread.
898 spellchecker_ = new SpellChecker(dict_dir, language, GetRequestContext(),
899 L"");
900 spellchecker_->AddRef(); // Manual refcounting.
901
902 if (need_to_broadcast && io_thread) { // Notify resource message filters.
903 SpellcheckerReinitializedDetails scoped_spellchecker;
904 scoped_spellchecker.spellchecker = spellchecker_;
905 io_thread->message_loop()->PostTask(
906 FROM_HERE,
907 new NotifySpellcheckerChangeTask(this, scoped_spellchecker));
908 }
909}
910
initial.commit09911bf2008-07-26 23:55:29911SpellChecker* ProfileImpl::GetSpellChecker() {
912 if (!spellchecker_) {
[email protected]20930852008-10-15 19:30:41913 // This is where spellchecker gets initialized. Note that this is being
914 // initialized in the ui_thread. However, this is not a problem as long as
915 // it is *used* in the io thread.
916 // TODO (sidchat) One day, change everything so that spellchecker gets
917 // initialized in the IO thread itself.
918 InitializeSpellChecker();
initial.commit09911bf2008-07-26 23:55:29919 }
[email protected]20930852008-10-15 19:30:41920
initial.commit09911bf2008-07-26 23:55:29921 return spellchecker_;
922}
923
924void ProfileImpl::MarkAsCleanShutdown() {
925 if (prefs_.get()) {
926 // The session cleanly exited, set kSessionExitedCleanly appropriately.
927 prefs_->SetBoolean(prefs::kSessionExitedCleanly, true);
928
929 // NOTE: If you change what thread this writes on, be sure and update
930 // ChromeFrame::EndSession().
931 prefs_->SavePersistentPrefs(g_browser_process->file_thread());
932 }
933}
934
935void ProfileImpl::StopCreateSessionServiceTimer() {
[email protected]2d316662008-09-03 18:18:14936 create_session_service_timer_.Stop();
initial.commit09911bf2008-07-26 23:55:29937}
[email protected]3a453fa2008-08-15 18:46:34938
939#ifdef CHROME_PERSONALIZATION
[email protected]57d3d0a2008-09-24 00:50:07940ProfilePersonalization* ProfileImpl::GetProfilePersonalization() {
941 if (!personalization_.get())
942 personalization_.reset(
943 Personalization::CreateProfilePersonalization(this));
944 return personalization_.get();
[email protected]3a453fa2008-08-15 18:46:34945}
license.botbf09a502008-08-24 00:55:55946#endif