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 // If a user goes from single language to multi-language spellchecking with
68 // spellchecking disabled the dictionaries preference should be blanked.
69 if (!prefs
->GetBoolean(prefs::kEnableContinuousSpellcheck
) &&
70 chrome::spellcheck_common::IsMultilingualSpellcheckEnabled()) {
71 dictionaries_pref
.SetValue(std::vector
<std::string
>());
72 prefs
->SetBoolean(prefs::kEnableContinuousSpellcheck
, true);
75 // If a user goes back to single language spellchecking make sure there is
76 // only one language in the dictionaries preference.
77 if (!chrome::spellcheck_common::IsMultilingualSpellcheckEnabled() &&
78 dictionaries_pref
.GetValue().size() > 1) {
79 dictionaries_pref
.SetValue(
80 std::vector
<std::string
>(1, first_of_dictionaries
));
83 std::string language_code
;
84 std::string country_code
;
85 chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
86 first_of_dictionaries
,
89 feedback_sender_
.reset(new spellcheck::FeedbackSender(
90 context
->GetRequestContext(), language_code
, country_code
));
92 pref_change_registrar_
.Add(
93 prefs::kEnableAutoSpellCorrect
,
94 base::Bind(&SpellcheckService::OnEnableAutoSpellCorrectChanged
,
95 base::Unretained(this)));
96 pref_change_registrar_
.Add(
97 prefs::kSpellCheckDictionaries
,
98 base::Bind(&SpellcheckService::OnSpellCheckDictionariesChanged
,
99 base::Unretained(this)));
100 pref_change_registrar_
.Add(
101 prefs::kSpellCheckUseSpellingService
,
102 base::Bind(&SpellcheckService::OnUseSpellingServiceChanged
,
103 base::Unretained(this)));
105 pref_change_registrar_
.Add(
106 prefs::kEnableContinuousSpellcheck
,
107 base::Bind(&SpellcheckService::InitForAllRenderers
,
108 base::Unretained(this)));
110 OnSpellCheckDictionariesChanged();
112 custom_dictionary_
.reset(new SpellcheckCustomDictionary(context_
->GetPath()));
113 custom_dictionary_
->AddObserver(this);
114 custom_dictionary_
->Load();
117 content::NOTIFICATION_RENDERER_PROCESS_CREATED
,
118 content::NotificationService::AllSources());
121 SpellcheckService::~SpellcheckService() {
122 // Remove pref observers
123 pref_change_registrar_
.RemoveAll();
126 base::WeakPtr
<SpellcheckService
> SpellcheckService::GetWeakPtr() {
127 return weak_ptr_factory_
.GetWeakPtr();
130 #if !defined(OS_MACOSX)
132 size_t SpellcheckService::GetSpellCheckLanguages(
133 base::SupportsUserData
* context
,
134 std::vector
<std::string
>* languages
) {
135 PrefService
* prefs
= user_prefs::UserPrefs::Get(context
);
136 StringPrefMember accept_languages_pref
;
137 accept_languages_pref
.Init(prefs::kAcceptLanguages
, prefs
);
139 std::vector
<std::string
> accept_languages
= base::SplitString(
140 accept_languages_pref
.GetValue(), ",",
141 base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
143 StringListPrefMember dictionaries_pref
;
144 dictionaries_pref
.Init(prefs::kSpellCheckDictionaries
, prefs
);
145 *languages
= dictionaries_pref
.GetValue();
146 size_t enabled_spellcheck_languages
= languages
->size();
148 for (std::vector
<std::string
>::const_iterator i
= accept_languages
.begin();
149 i
!= accept_languages
.end(); ++i
) {
150 std::string language
=
151 chrome::spellcheck_common::GetCorrespondingSpellCheckLanguage(*i
);
152 if (!language
.empty() &&
153 std::find(languages
->begin(), languages
->end(), language
) ==
155 languages
->push_back(language
);
159 return enabled_spellcheck_languages
;
164 bool SpellcheckService::SignalStatusEvent(
165 SpellcheckService::EventType status_type
) {
166 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
170 g_status_type
= status_type
;
171 g_status_event
->Signal();
175 void SpellcheckService::StartRecordingMetrics(bool spellcheck_enabled
) {
176 metrics_
.reset(new SpellCheckHostMetrics());
177 metrics_
->RecordEnabledStats(spellcheck_enabled
);
178 OnUseSpellingServiceChanged();
181 void SpellcheckService::InitForRenderer(content::RenderProcessHost
* process
) {
182 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
184 content::BrowserContext
* context
= process
->GetBrowserContext();
185 if (SpellcheckServiceFactory::GetForContext(context
) != this)
188 PrefService
* prefs
= user_prefs::UserPrefs::Get(context
);
189 std::vector
<SpellCheckBDictLanguage
> bdict_languages
;
191 for (const auto& hunspell_dictionary
: hunspell_dictionaries_
) {
192 bdict_languages
.push_back(SpellCheckBDictLanguage());
193 bdict_languages
.back().language
= hunspell_dictionary
->GetLanguage();
194 bdict_languages
.back().file
=
195 hunspell_dictionary
->GetDictionaryFile().IsValid()
196 ? IPC::GetFileHandleForProcess(
197 hunspell_dictionary
->GetDictionaryFile().GetPlatformFile(),
198 process
->GetHandle(), false)
199 : IPC::InvalidPlatformFileForTransit();
202 process
->Send(new SpellCheckMsg_Init(
203 bdict_languages
, custom_dictionary_
->GetWords(),
204 prefs
->GetBoolean(prefs::kEnableAutoSpellCorrect
)));
205 process
->Send(new SpellCheckMsg_EnableSpellCheck(
206 prefs
->GetBoolean(prefs::kEnableContinuousSpellcheck
)));
209 SpellCheckHostMetrics
* SpellcheckService::GetMetrics() const {
210 return metrics_
.get();
213 SpellcheckCustomDictionary
* SpellcheckService::GetCustomDictionary() {
214 return custom_dictionary_
.get();
217 const ScopedVector
<SpellcheckHunspellDictionary
>&
218 SpellcheckService::GetHunspellDictionaries() {
219 return hunspell_dictionaries_
;
222 spellcheck::FeedbackSender
* SpellcheckService::GetFeedbackSender() {
223 return feedback_sender_
.get();
226 bool SpellcheckService::LoadExternalDictionary(std::string language
,
229 DictionaryFormat format
) {
233 bool SpellcheckService::UnloadExternalDictionary(std::string path
) {
237 void SpellcheckService::Observe(int type
,
238 const content::NotificationSource
& source
,
239 const content::NotificationDetails
& details
) {
240 DCHECK(type
== content::NOTIFICATION_RENDERER_PROCESS_CREATED
);
241 content::RenderProcessHost
* process
=
242 content::Source
<content::RenderProcessHost
>(source
).ptr();
243 InitForRenderer(process
);
246 void SpellcheckService::OnCustomDictionaryLoaded() {
247 InitForAllRenderers();
250 void SpellcheckService::OnCustomDictionaryChanged(
251 const SpellcheckCustomDictionary::Change
& dictionary_change
) {
252 for (content::RenderProcessHost::iterator
i(
253 content::RenderProcessHost::AllHostsIterator());
254 !i
.IsAtEnd(); i
.Advance()) {
255 i
.GetCurrentValue()->Send(new SpellCheckMsg_CustomDictionaryChanged(
256 dictionary_change
.to_add(),
257 dictionary_change
.to_remove()));
261 void SpellcheckService::OnHunspellDictionaryInitialized(
262 const std::string
& language
) {
263 InitForAllRenderers();
266 void SpellcheckService::OnHunspellDictionaryDownloadBegin(
267 const std::string
& language
) {
270 void SpellcheckService::OnHunspellDictionaryDownloadSuccess(
271 const std::string
& language
) {
274 void SpellcheckService::OnHunspellDictionaryDownloadFailure(
275 const std::string
& language
) {
279 void SpellcheckService::AttachStatusEvent(base::WaitableEvent
* status_event
) {
280 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
282 g_status_event
= status_event
;
286 SpellcheckService::EventType
SpellcheckService::GetStatusEvent() {
287 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
288 return g_status_type
;
291 void SpellcheckService::InitForAllRenderers() {
292 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
293 for (content::RenderProcessHost::iterator
i(
294 content::RenderProcessHost::AllHostsIterator());
295 !i
.IsAtEnd(); i
.Advance()) {
296 content::RenderProcessHost
* process
= i
.GetCurrentValue();
297 if (process
&& process
->GetHandle())
298 InitForRenderer(process
);
302 void SpellcheckService::OnEnableAutoSpellCorrectChanged() {
303 bool enabled
= pref_change_registrar_
.prefs()->GetBoolean(
304 prefs::kEnableAutoSpellCorrect
);
305 for (content::RenderProcessHost::iterator
i(
306 content::RenderProcessHost::AllHostsIterator());
307 !i
.IsAtEnd(); i
.Advance()) {
308 content::RenderProcessHost
* process
= i
.GetCurrentValue();
309 process
->Send(new SpellCheckMsg_EnableAutoSpellCorrect(enabled
));
313 void SpellcheckService::OnSpellCheckDictionariesChanged() {
314 for (auto& hunspell_dictionary
: hunspell_dictionaries_
)
315 hunspell_dictionary
->RemoveObserver(this);
317 PrefService
* prefs
= user_prefs::UserPrefs::Get(context_
);
320 const base::ListValue
* dictionary_values
=
321 prefs
->GetList(prefs::kSpellCheckDictionaries
);
323 hunspell_dictionaries_
.clear();
324 for (const base::Value
* dictionary_value
: *dictionary_values
) {
325 std::string dictionary
;
326 dictionary_value
->GetAsString(&dictionary
);
327 hunspell_dictionaries_
.push_back(new SpellcheckHunspellDictionary(
328 dictionary
, context_
->GetRequestContext(), this));
329 hunspell_dictionaries_
.back()->AddObserver(this);
330 hunspell_dictionaries_
.back()->Load();
333 std::string feedback_language
;
334 dictionary_values
->GetString(0, &feedback_language
);
335 std::string language_code
;
336 std::string country_code
;
337 chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
338 feedback_language
, &language_code
, &country_code
);
339 feedback_sender_
->OnLanguageCountryChange(language_code
, country_code
);
340 UpdateFeedbackSenderState();
343 void SpellcheckService::OnUseSpellingServiceChanged() {
344 bool enabled
= pref_change_registrar_
.prefs()->GetBoolean(
345 prefs::kSpellCheckUseSpellingService
);
347 metrics_
->RecordSpellingServiceStats(enabled
);
348 UpdateFeedbackSenderState();
351 void SpellcheckService::UpdateFeedbackSenderState() {
352 if (SpellingServiceClient::IsAvailable(
353 context_
, SpellingServiceClient::SPELLCHECK
)) {
354 feedback_sender_
->StartFeedbackCollection();
356 feedback_sender_
->StopFeedbackCollection();