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