1 // Copyright (c) 2012 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.
5 #include "chrome/browser/spellchecker/spellcheck_service.h"
7 #include "base/logging.h"
8 #include "base/prefs/pref_member.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/strings/string_split.h"
11 #include "base/supports_user_data.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "chrome/browser/spellchecker/feedback_sender.h"
14 #include "chrome/browser/spellchecker/spellcheck_factory.h"
15 #include "chrome/browser/spellchecker/spellcheck_host_metrics.h"
16 #include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h"
17 #include "chrome/browser/spellchecker/spellcheck_platform.h"
18 #include "chrome/browser/spellchecker/spelling_service_client.h"
19 #include "chrome/common/pref_names.h"
20 #include "chrome/common/spellcheck_bdict_language.h"
21 #include "chrome/common/spellcheck_common.h"
22 #include "chrome/common/spellcheck_messages.h"
23 #include "components/user_prefs/user_prefs.h"
24 #include "content/public/browser/browser_context.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/notification_service.h"
27 #include "content/public/browser/notification_types.h"
28 #include "content/public/browser/render_process_host.h"
29 #include "ipc/ipc_platform_file.h"
31 using content::BrowserThread
;
33 // TODO(rlp): I do not like globals, but keeping these for now during
35 // An event used by browser tests to receive status events from this class and
36 // its derived classes.
37 base::WaitableEvent
* g_status_event
= NULL
;
38 SpellcheckService::EventType g_status_type
=
39 SpellcheckService::BDICT_NOTINITIALIZED
;
41 SpellcheckService::SpellcheckService(content::BrowserContext
* context
)
43 weak_ptr_factory_(this) {
44 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
45 PrefService
* prefs
= user_prefs::UserPrefs::Get(context
);
46 pref_change_registrar_
.Init(prefs
);
47 StringListPrefMember dictionaries_pref
;
48 dictionaries_pref
.Init(prefs::kSpellCheckDictionaries
, prefs
);
49 std::string first_of_dictionaries
;
50 if (!dictionaries_pref
.GetValue().empty())
51 first_of_dictionaries
= dictionaries_pref
.GetValue().front();
53 // For preference migration, set the new preference kSpellCheckDictionaries
54 // to be the same as the old kSpellCheckDictionary.
55 StringPrefMember single_dictionary_pref
;
56 single_dictionary_pref
.Init(prefs::kSpellCheckDictionary
, prefs
);
57 std::string single_dictionary
= single_dictionary_pref
.GetValue();
59 if (first_of_dictionaries
.empty() && !single_dictionary
.empty()) {
60 first_of_dictionaries
= single_dictionary
;
61 dictionaries_pref
.SetValue(
62 std::vector
<std::string
>(1, first_of_dictionaries
));
65 single_dictionary_pref
.SetValue("");
67 std::string language_code
;
68 std::string country_code
;
69 chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
70 first_of_dictionaries
,
73 feedback_sender_
.reset(new spellcheck::FeedbackSender(
74 context
->GetRequestContext(), language_code
, country_code
));
76 pref_change_registrar_
.Add(
77 prefs::kEnableAutoSpellCorrect
,
78 base::Bind(&SpellcheckService::OnEnableAutoSpellCorrectChanged
,
79 base::Unretained(this)));
80 pref_change_registrar_
.Add(
81 prefs::kSpellCheckDictionaries
,
82 base::Bind(&SpellcheckService::OnSpellCheckDictionariesChanged
,
83 base::Unretained(this)));
84 if (!chrome::spellcheck_common::IsMultilingualSpellcheckEnabled()) {
85 pref_change_registrar_
.Add(
86 prefs::kSpellCheckUseSpellingService
,
87 base::Bind(&SpellcheckService::OnUseSpellingServiceChanged
,
88 base::Unretained(this)));
91 pref_change_registrar_
.Add(
92 prefs::kEnableContinuousSpellcheck
,
93 base::Bind(&SpellcheckService::InitForAllRenderers
,
94 base::Unretained(this)));
96 OnSpellCheckDictionariesChanged();
98 custom_dictionary_
.reset(new SpellcheckCustomDictionary(context_
->GetPath()));
99 custom_dictionary_
->AddObserver(this);
100 custom_dictionary_
->Load();
103 content::NOTIFICATION_RENDERER_PROCESS_CREATED
,
104 content::NotificationService::AllSources());
107 SpellcheckService::~SpellcheckService() {
108 // Remove pref observers
109 pref_change_registrar_
.RemoveAll();
112 base::WeakPtr
<SpellcheckService
> SpellcheckService::GetWeakPtr() {
113 return weak_ptr_factory_
.GetWeakPtr();
116 #if !defined(OS_MACOSX)
118 size_t SpellcheckService::GetSpellCheckLanguages(
119 base::SupportsUserData
* context
,
120 std::vector
<std::string
>* languages
) {
121 PrefService
* prefs
= user_prefs::UserPrefs::Get(context
);
122 StringPrefMember accept_languages_pref
;
123 accept_languages_pref
.Init(prefs::kAcceptLanguages
, prefs
);
125 std::vector
<std::string
> accept_languages
= base::SplitString(
126 accept_languages_pref
.GetValue(), ",",
127 base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
129 StringListPrefMember dictionaries_pref
;
130 dictionaries_pref
.Init(prefs::kSpellCheckDictionaries
, prefs
);
131 *languages
= dictionaries_pref
.GetValue();
132 size_t enabled_spellcheck_languages
= languages
->size();
134 for (std::vector
<std::string
>::const_iterator i
= accept_languages
.begin();
135 i
!= accept_languages
.end(); ++i
) {
136 std::string language
=
137 chrome::spellcheck_common::GetCorrespondingSpellCheckLanguage(*i
);
138 if (!language
.empty() &&
139 std::find(languages
->begin(), languages
->end(), language
) ==
141 languages
->push_back(language
);
145 return enabled_spellcheck_languages
;
150 bool SpellcheckService::SignalStatusEvent(
151 SpellcheckService::EventType status_type
) {
152 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
156 g_status_type
= status_type
;
157 g_status_event
->Signal();
161 void SpellcheckService::StartRecordingMetrics(bool spellcheck_enabled
) {
162 metrics_
.reset(new SpellCheckHostMetrics());
163 metrics_
->RecordEnabledStats(spellcheck_enabled
);
164 OnUseSpellingServiceChanged();
167 void SpellcheckService::InitForRenderer(content::RenderProcessHost
* process
) {
168 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
170 content::BrowserContext
* context
= process
->GetBrowserContext();
171 if (SpellcheckServiceFactory::GetForContext(context
) != this)
174 PrefService
* prefs
= user_prefs::UserPrefs::Get(context
);
175 std::vector
<SpellCheckBDictLanguage
> bdict_languages
;
177 for (const auto& hunspell_dictionary
: hunspell_dictionaries_
) {
178 bdict_languages
.push_back(SpellCheckBDictLanguage());
179 bdict_languages
.back().language
= hunspell_dictionary
->GetLanguage();
180 bdict_languages
.back().file
=
181 hunspell_dictionary
->GetDictionaryFile().IsValid()
182 ? IPC::GetFileHandleForProcess(
183 hunspell_dictionary
->GetDictionaryFile().GetPlatformFile(),
184 process
->GetHandle(), false)
185 : IPC::InvalidPlatformFileForTransit();
188 process
->Send(new SpellCheckMsg_Init(
189 bdict_languages
, custom_dictionary_
->GetWords(),
190 prefs
->GetBoolean(prefs::kEnableAutoSpellCorrect
)));
191 process
->Send(new SpellCheckMsg_EnableSpellCheck(
192 prefs
->GetBoolean(prefs::kEnableContinuousSpellcheck
)));
195 SpellCheckHostMetrics
* SpellcheckService::GetMetrics() const {
196 return metrics_
.get();
199 SpellcheckCustomDictionary
* SpellcheckService::GetCustomDictionary() {
200 return custom_dictionary_
.get();
203 const ScopedVector
<SpellcheckHunspellDictionary
>&
204 SpellcheckService::GetHunspellDictionaries() {
205 return hunspell_dictionaries_
;
208 spellcheck::FeedbackSender
* SpellcheckService::GetFeedbackSender() {
209 return feedback_sender_
.get();
212 bool SpellcheckService::LoadExternalDictionary(std::string language
,
215 DictionaryFormat format
) {
219 bool SpellcheckService::UnloadExternalDictionary(std::string path
) {
223 void SpellcheckService::Observe(int type
,
224 const content::NotificationSource
& source
,
225 const content::NotificationDetails
& details
) {
226 DCHECK(type
== content::NOTIFICATION_RENDERER_PROCESS_CREATED
);
227 content::RenderProcessHost
* process
=
228 content::Source
<content::RenderProcessHost
>(source
).ptr();
229 InitForRenderer(process
);
232 void SpellcheckService::OnCustomDictionaryLoaded() {
233 InitForAllRenderers();
236 void SpellcheckService::OnCustomDictionaryChanged(
237 const SpellcheckCustomDictionary::Change
& dictionary_change
) {
238 for (content::RenderProcessHost::iterator
i(
239 content::RenderProcessHost::AllHostsIterator());
240 !i
.IsAtEnd(); i
.Advance()) {
241 i
.GetCurrentValue()->Send(new SpellCheckMsg_CustomDictionaryChanged(
242 dictionary_change
.to_add(),
243 dictionary_change
.to_remove()));
247 void SpellcheckService::OnHunspellDictionaryInitialized() {
248 InitForAllRenderers();
251 void SpellcheckService::OnHunspellDictionaryDownloadBegin() {
254 void SpellcheckService::OnHunspellDictionaryDownloadSuccess() {
257 void SpellcheckService::OnHunspellDictionaryDownloadFailure() {
261 void SpellcheckService::AttachStatusEvent(base::WaitableEvent
* status_event
) {
262 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
264 g_status_event
= status_event
;
268 SpellcheckService::EventType
SpellcheckService::GetStatusEvent() {
269 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
270 return g_status_type
;
273 void SpellcheckService::InitForAllRenderers() {
274 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
275 for (content::RenderProcessHost::iterator
i(
276 content::RenderProcessHost::AllHostsIterator());
277 !i
.IsAtEnd(); i
.Advance()) {
278 content::RenderProcessHost
* process
= i
.GetCurrentValue();
279 if (process
&& process
->GetHandle())
280 InitForRenderer(process
);
284 void SpellcheckService::OnEnableAutoSpellCorrectChanged() {
285 bool enabled
= pref_change_registrar_
.prefs()->GetBoolean(
286 prefs::kEnableAutoSpellCorrect
);
287 for (content::RenderProcessHost::iterator
i(
288 content::RenderProcessHost::AllHostsIterator());
289 !i
.IsAtEnd(); i
.Advance()) {
290 content::RenderProcessHost
* process
= i
.GetCurrentValue();
291 process
->Send(new SpellCheckMsg_EnableAutoSpellCorrect(enabled
));
295 void SpellcheckService::OnSpellCheckDictionariesChanged() {
296 for (auto& hunspell_dictionary
: hunspell_dictionaries_
)
297 hunspell_dictionary
->RemoveObserver(this);
299 PrefService
* prefs
= user_prefs::UserPrefs::Get(context_
);
302 const base::ListValue
* dictionary_values
=
303 prefs
->GetList(prefs::kSpellCheckDictionaries
);
305 hunspell_dictionaries_
.clear();
306 for (const base::Value
* dictionary_value
: *dictionary_values
) {
307 std::string dictionary
;
308 dictionary_value
->GetAsString(&dictionary
);
309 hunspell_dictionaries_
.push_back(new SpellcheckHunspellDictionary(
310 dictionary
, context_
->GetRequestContext(), this));
311 hunspell_dictionaries_
.back()->AddObserver(this);
312 hunspell_dictionaries_
.back()->Load();
315 std::string feedback_language
;
316 dictionary_values
->GetString(0, &feedback_language
);
317 std::string language_code
;
318 std::string country_code
;
319 chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
320 feedback_language
, &language_code
, &country_code
);
321 feedback_sender_
->OnLanguageCountryChange(language_code
, country_code
);
322 UpdateFeedbackSenderState();
325 void SpellcheckService::OnUseSpellingServiceChanged() {
326 bool enabled
= pref_change_registrar_
.prefs()->GetBoolean(
327 prefs::kSpellCheckUseSpellingService
);
329 metrics_
->RecordSpellingServiceStats(enabled
);
330 UpdateFeedbackSenderState();
333 void SpellcheckService::UpdateFeedbackSenderState() {
334 if (SpellingServiceClient::IsAvailable(
335 context_
, SpellingServiceClient::SPELLCHECK
)) {
336 feedback_sender_
->StartFeedbackCollection();
338 feedback_sender_
->StopFeedbackCollection();