blob: 848dfc7c72e1ccba819f45479df5cda8bc578529 [file] [log] [blame]
// 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_service.h"
#include "base/platform_file.h"
#include "base/prefs/pref_member.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_split.h"
#include "base/synchronization/waitable_event.h"
#include "chrome/browser/spellchecker/spellcheck_factory.h"
#include "chrome/browser/spellchecker/spellcheck_host_metrics.h"
#include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h"
#include "chrome/browser/spellchecker/spellcheck_platform_mac.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/spellcheck_messages.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
#include "ipc/ipc_platform_file.h"
using content::BrowserThread;
using chrome::spellcheck_common::WordList;
// TODO(rlp): I do not like globals, but keeping these for now during
// transition.
// An event used by browser tests to receive status events from this class and
// its derived classes.
base::WaitableEvent* g_status_event = NULL;
SpellcheckService::EventType g_status_type =
SpellcheckService::BDICT_NOTINITIALIZED;
SpellcheckService::SpellcheckService(content::BrowserContext* context)
: context_(context),
weak_ptr_factory_(this) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
PrefService* prefs = user_prefs::UserPrefs::Get(context);
pref_change_registrar_.Init(prefs);
std::string language_code;
std::string country_code;
chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
prefs->GetString(prefs::kSpellCheckDictionary),
&language_code,
&country_code);
feedback_sender_.reset(new spellcheck::FeedbackSender(
context->GetRequestContext(), language_code, country_code));
pref_change_registrar_.Add(
prefs::kEnableAutoSpellCorrect,
base::Bind(&SpellcheckService::OnEnableAutoSpellCorrectChanged,
base::Unretained(this)));
pref_change_registrar_.Add(
prefs::kSpellCheckDictionary,
base::Bind(&SpellcheckService::OnSpellCheckDictionaryChanged,
base::Unretained(this)));
pref_change_registrar_.Add(
prefs::kSpellCheckUseSpellingService,
base::Bind(&SpellcheckService::OnUseSpellingServiceChanged,
base::Unretained(this)));
pref_change_registrar_.Add(
prefs::kEnableContinuousSpellcheck,
base::Bind(&SpellcheckService::InitForAllRenderers,
base::Unretained(this)));
OnSpellCheckDictionaryChanged();
custom_dictionary_.reset(new SpellcheckCustomDictionary(context_->GetPath()));
custom_dictionary_->AddObserver(this);
custom_dictionary_->Load();
registrar_.Add(this,
content::NOTIFICATION_RENDERER_PROCESS_CREATED,
content::NotificationService::AllSources());
}
SpellcheckService::~SpellcheckService() {
// Remove pref observers
pref_change_registrar_.RemoveAll();
}
// static
int SpellcheckService::GetSpellCheckLanguages(
content::BrowserContext* context,
std::vector<std::string>* languages) {
PrefService* prefs = user_prefs::UserPrefs::Get(context);
StringPrefMember accept_languages_pref;
StringPrefMember dictionary_language_pref;
accept_languages_pref.Init(prefs::kAcceptLanguages, prefs);
dictionary_language_pref.Init(prefs::kSpellCheckDictionary, prefs);
std::string dictionary_language = dictionary_language_pref.GetValue();
// Now scan through the list of accept languages, and find possible mappings
// from this list to the existing list of spell check languages.
std::vector<std::string> accept_languages;
#if defined(OS_MACOSX)
if (spellcheck_mac::SpellCheckerAvailable())
spellcheck_mac::GetAvailableLanguages(&accept_languages);
else
base::SplitString(accept_languages_pref.GetValue(), ',', &accept_languages);
#else
base::SplitString(accept_languages_pref.GetValue(), ',', &accept_languages);
#endif // !OS_MACOSX
GetSpellCheckLanguagesFromAcceptLanguages(
accept_languages, dictionary_language, languages);
for (size_t i = 0; i < languages->size(); ++i) {
if ((*languages)[i] == dictionary_language)
return i;
}
return -1;
}
// static
void SpellcheckService::GetSpellCheckLanguagesFromAcceptLanguages(
const std::vector<std::string>& accept_languages,
const std::string& dictionary_language,
std::vector<std::string>* languages) {
// The current dictionary language should be there.
languages->push_back(dictionary_language);
for (std::vector<std::string>::const_iterator i = accept_languages.begin();
i != accept_languages.end(); ++i) {
std::string language =
chrome::spellcheck_common::GetCorrespondingSpellCheckLanguage(*i);
if (!language.empty() &&
std::find(languages->begin(), languages->end(), language) ==
languages->end()) {
languages->push_back(language);
}
}
}
// static
bool SpellcheckService::SignalStatusEvent(
SpellcheckService::EventType status_type) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!g_status_event)
return false;
g_status_type = status_type;
g_status_event->Signal();
return true;
}
void SpellcheckService::StartRecordingMetrics(bool spellcheck_enabled) {
metrics_.reset(new SpellCheckHostMetrics());
metrics_->RecordEnabledStats(spellcheck_enabled);
OnUseSpellingServiceChanged();
}
void SpellcheckService::InitForRenderer(content::RenderProcessHost* process) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
content::BrowserContext* context = process->GetBrowserContext();
if (SpellcheckServiceFactory::GetForContext(context) != this)
return;
PrefService* prefs = user_prefs::UserPrefs::Get(context);
IPC::PlatformFileForTransit file = IPC::InvalidPlatformFileForTransit();
if (hunspell_dictionary_->GetDictionaryFile() !=
base::kInvalidPlatformFileValue) {
#if defined(OS_POSIX)
file = base::FileDescriptor(hunspell_dictionary_->GetDictionaryFile(),
false);
#elif defined(OS_WIN)
BOOL ok = ::DuplicateHandle(::GetCurrentProcess(),
hunspell_dictionary_->GetDictionaryFile(),
process->GetHandle(),
&file,
0,
false,
DUPLICATE_SAME_ACCESS);
DCHECK(ok) << ::GetLastError();
#endif
}
process->Send(new SpellCheckMsg_Init(
file,
custom_dictionary_->GetWords(),
hunspell_dictionary_->GetLanguage(),
prefs->GetBoolean(prefs::kEnableAutoSpellCorrect)));
process->Send(new SpellCheckMsg_EnableSpellCheck(
prefs->GetBoolean(prefs::kEnableContinuousSpellcheck)));
}
SpellCheckHostMetrics* SpellcheckService::GetMetrics() const {
return metrics_.get();
}
SpellcheckCustomDictionary* SpellcheckService::GetCustomDictionary() {
return custom_dictionary_.get();
}
SpellcheckHunspellDictionary* SpellcheckService::GetHunspellDictionary() {
return hunspell_dictionary_.get();
}
spellcheck::FeedbackSender* SpellcheckService::GetFeedbackSender() {
return feedback_sender_.get();
}
bool SpellcheckService::LoadExternalDictionary(std::string language,
std::string locale,
std::string path,
DictionaryFormat format) {
return false;
}
bool SpellcheckService::UnloadExternalDictionary(std::string path) {
return false;
}
void SpellcheckService::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK(type == content::NOTIFICATION_RENDERER_PROCESS_CREATED);
content::RenderProcessHost* process =
content::Source<content::RenderProcessHost>(source).ptr();
InitForRenderer(process);
}
void SpellcheckService::OnCustomDictionaryLoaded() {
InitForAllRenderers();
}
void SpellcheckService::OnCustomDictionaryChanged(
const SpellcheckCustomDictionary::Change& dictionary_change) {
for (content::RenderProcessHost::iterator i(
content::RenderProcessHost::AllHostsIterator());
!i.IsAtEnd(); i.Advance()) {
i.GetCurrentValue()->Send(new SpellCheckMsg_CustomDictionaryChanged(
dictionary_change.to_add(),
dictionary_change.to_remove()));
}
}
void SpellcheckService::OnHunspellDictionaryInitialized() {
InitForAllRenderers();
}
void SpellcheckService::OnHunspellDictionaryDownloadBegin() {
}
void SpellcheckService::OnHunspellDictionaryDownloadSuccess() {
}
void SpellcheckService::OnHunspellDictionaryDownloadFailure() {
}
// static
void SpellcheckService::AttachStatusEvent(base::WaitableEvent* status_event) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
g_status_event = status_event;
}
// static
SpellcheckService::EventType SpellcheckService::GetStatusEvent() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return g_status_type;
}
void SpellcheckService::InitForAllRenderers() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
for (content::RenderProcessHost::iterator i(
content::RenderProcessHost::AllHostsIterator());
!i.IsAtEnd(); i.Advance()) {
content::RenderProcessHost* process = i.GetCurrentValue();
if (process && process->GetHandle())
InitForRenderer(process);
}
}
void SpellcheckService::OnEnableAutoSpellCorrectChanged() {
bool enabled = pref_change_registrar_.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));
}
}
void SpellcheckService::OnSpellCheckDictionaryChanged() {
if (hunspell_dictionary_.get())
hunspell_dictionary_->RemoveObserver(this);
PrefService* prefs = user_prefs::UserPrefs::Get(context_);
DCHECK(prefs);
std::string dictionary =
prefs->GetString(prefs::kSpellCheckDictionary);
hunspell_dictionary_.reset(new SpellcheckHunspellDictionary(
dictionary, context_->GetRequestContext(), this));
hunspell_dictionary_->AddObserver(this);
hunspell_dictionary_->Load();
std::string language_code;
std::string country_code;
chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
dictionary, &language_code, &country_code);
feedback_sender_->OnLanguageCountryChange(language_code, country_code);
}
void SpellcheckService::OnUseSpellingServiceChanged() {
bool enabled = pref_change_registrar_.prefs()->GetBoolean(
prefs::kSpellCheckUseSpellingService);
if (metrics_)
metrics_->RecordSpellingServiceStats(enabled);
}