// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/spellchecker/spellcheck_profile.h" #include "base/file_util.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/string_split.h" #include "base/string_util.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/spellchecker/spellcheck_host.h" #include "chrome/browser/spellchecker/spellcheck_host_metrics.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/pref_names.h" #include "chrome/common/spellcheck_messages.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_process_host.h" using content::BrowserThread; namespace { base::LazyInstance g_empty_list = LAZY_INSTANCE_INITIALIZER; } // namespace SpellCheckProfile::SpellCheckProfile(Profile* profile) : profile_(profile), host_ready_(false), profile_dir_(profile->GetPath()) { PrefService* prefs = profile_->GetPrefs(); pref_change_registrar_.Init(prefs); pref_change_registrar_.Add(prefs::kSpellCheckDictionary, this); pref_change_registrar_.Add(prefs::kEnableSpellCheck, this); pref_change_registrar_.Add(prefs::kEnableAutoSpellCorrect, this); } SpellCheckProfile::~SpellCheckProfile() { // Remove pref observers pref_change_registrar_.RemoveAll(); } SpellCheckHost* SpellCheckProfile::GetHost() { return host_ready_ ? host_.get() : NULL; } void SpellCheckProfile::ReinitializeSpellCheckHost(bool force) { PrefService* pref = profile_->GetPrefs(); SpellCheckProfile::ReinitializeResult result = ReinitializeHostImpl( force, pref->GetBoolean(prefs::kEnableSpellCheck), pref->GetString(prefs::kSpellCheckDictionary), profile_->GetRequestContext()); if (result == SpellCheckProfile::REINITIALIZE_REMOVED_HOST) { // The spellchecker has been disabled. for (content::RenderProcessHost::iterator i( content::RenderProcessHost::AllHostsIterator()); !i.IsAtEnd(); i.Advance()) { content::RenderProcessHost* process = i.GetCurrentValue(); process->Send(new SpellCheckMsg_Init(IPC::InvalidPlatformFileForTransit(), std::vector(), std::string(), false)); } } } SpellCheckHost* SpellCheckProfile::CreateHost( SpellCheckProfileProvider* provider, const std::string& language, net::URLRequestContextGetter* request_context, SpellCheckHostMetrics* metrics) { return SpellCheckHost::Create( provider, language, request_context, metrics); } bool SpellCheckProfile::IsTesting() const { return false; } void SpellCheckProfile::StartRecordingMetrics(bool spellcheck_enabled) { metrics_.reset(new SpellCheckHostMetrics()); metrics_->RecordEnabledStats(spellcheck_enabled); } void SpellCheckProfile::SpellCheckHostInitialized( SpellCheckProfile::CustomWordList* custom_words) { host_ready_ = !!host_.get() && host_->IsReady(); custom_words_.reset(custom_words); if (metrics_.get()) { int count = custom_words_.get() ? custom_words_->size() : 0; metrics_->RecordCustomWordCountStats(count); } } const SpellCheckProfile::CustomWordList& SpellCheckProfile::GetCustomWords() const { return custom_words_.get() ? *custom_words_ : g_empty_list.Get(); } void SpellCheckProfile::CustomWordAddedLocally(const std::string& word) { if (!custom_words_.get()) custom_words_.reset(new CustomWordList()); custom_words_->push_back(word); if (metrics_.get()) metrics_->RecordCustomWordCountStats(custom_words_->size()); } void SpellCheckProfile::LoadCustomDictionary(CustomWordList* custom_words) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE) || IsTesting()); if (!custom_words) return; std::string contents; file_util::ReadFileToString(GetCustomDictionaryPath(), &contents); if (contents.empty()) return; CustomWordList list_of_words; base::SplitString(contents, '\n', &list_of_words); for (size_t i = 0; i < list_of_words.size(); ++i) { if (list_of_words[i] != "") custom_words->push_back(list_of_words[i]); } } void SpellCheckProfile::WriteWordToCustomDictionary(const std::string& word) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE) || IsTesting()); // Stored in UTF-8. DCHECK(IsStringUTF8(word)); std::string word_to_add(word + "\n"); FILE* f = file_util::OpenFile(GetCustomDictionaryPath(), "a+"); if (f) { fputs(word_to_add.c_str(), f); file_util::CloseFile(f); } } void SpellCheckProfile::Shutdown() { if (host_.get()) host_->UnsetProfile(); profile_ = NULL; } void SpellCheckProfile::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { switch (type) { case chrome::NOTIFICATION_PREF_CHANGED: { std::string* pref_name_in = content::Details(details).ptr(); PrefService* prefs = content::Source(source).ptr(); DCHECK(pref_name_in && prefs); if (*pref_name_in == prefs::kSpellCheckDictionary || *pref_name_in == prefs::kEnableSpellCheck) { ReinitializeSpellCheckHost(true); } else if (*pref_name_in == prefs::kEnableAutoSpellCorrect) { bool enabled = prefs->GetBoolean(prefs::kEnableAutoSpellCorrect); for (content::RenderProcessHost::iterator i( content::RenderProcessHost::AllHostsIterator()); !i.IsAtEnd(); i.Advance()) { content::RenderProcessHost* process = i.GetCurrentValue(); process->Send(new SpellCheckMsg_EnableAutoSpellCorrect(enabled)); } } } } } SpellCheckProfile::ReinitializeResult SpellCheckProfile::ReinitializeHostImpl( bool force, bool enable, const std::string& language, net::URLRequestContextGetter* request_context) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || IsTesting()); // If we are already loading the spellchecker, and this is just a hint to // load the spellchecker, do nothing. if (!force && host_.get()) return REINITIALIZE_DID_NOTHING; host_ready_ = false; bool host_deleted = false; if (host_.get()) { host_->UnsetProfile(); host_.reset(NULL); host_deleted = true; } if (enable) { // Retrieve the (perhaps updated recently) dictionary name from preferences. host_.reset(CreateHost(this, language, request_context, metrics_.get())); return REINITIALIZE_CREATED_HOST; } return host_deleted ? REINITIALIZE_REMOVED_HOST : REINITIALIZE_DID_NOTHING; } const FilePath& SpellCheckProfile::GetCustomDictionaryPath() { if (!custom_dictionary_path_.get()) { custom_dictionary_path_.reset( new FilePath(profile_dir_.Append(chrome::kCustomDictionaryFileName))); } return *custom_dictionary_path_; }