1 // Copyright 2015 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.
4
5 #include "chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.h"
6
7 #include <memory>
8 #include <string>
9 #include <utility>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/spellchecker/spellcheck_factory.h"
19 #include "chrome/browser/spellchecker/spellcheck_service.h"
20 #include "components/prefs/pref_service.h"
21 #include "components/spellcheck/browser/pref_names.h"
22 #include "content/public/browser/browser_context.h"
23 #include "content/public/browser/notification_source.h"
24 #include "content/public/browser/storage_partition.h"
25
26 namespace extensions {
27
28 namespace language_settings_private = api::language_settings_private;
29
LanguageSettingsPrivateDelegate(content::BrowserContext * context)30 LanguageSettingsPrivateDelegate::LanguageSettingsPrivateDelegate(
31 content::BrowserContext* context)
32 : custom_dictionary_(nullptr),
33 context_(context),
34 listening_spellcheck_(false),
35 listening_input_method_(false),
36 profile_added_(false) {
37 // Register with the event router so we know when renderers are listening to
38 // our events. We first check and see if there *is* an event router, because
39 // some unit tests try to create all context services, but don't initialize
40 // the event router first.
41 EventRouter* event_router = EventRouter::Get(context_);
42 if (!event_router)
43 return;
44
45 event_router->RegisterObserver(this,
46 language_settings_private::OnSpellcheckDictionariesChanged::kEventName);
47 event_router->RegisterObserver(this,
48 language_settings_private::OnCustomDictionaryChanged::kEventName);
49 event_router->RegisterObserver(
50 this, language_settings_private::OnInputMethodAdded::kEventName);
51 event_router->RegisterObserver(
52 this, language_settings_private::OnInputMethodRemoved::kEventName);
53
54 // SpellcheckService cannot be created until Profile::DoFinalInit() has been
55 // called. http://crbug.com/171406
56 notification_registrar_.Add(this,
57 chrome::NOTIFICATION_PROFILE_ADDED,
58 content::Source<Profile>(Profile::FromBrowserContext(context_)));
59
60 pref_change_registrar_.Init(Profile::FromBrowserContext(context_)->
61 GetPrefs());
62
63 StartOrStopListeningForSpellcheckChanges();
64 #if defined(OS_CHROMEOS)
65 StartOrStopListeningForInputMethodChanges();
66 #endif // defined(OS_CHROMEOS)
67 }
68
~LanguageSettingsPrivateDelegate()69 LanguageSettingsPrivateDelegate::~LanguageSettingsPrivateDelegate() {
70 DCHECK(!listening_spellcheck_);
71 DCHECK(!listening_input_method_);
72 pref_change_registrar_.RemoveAll();
73 notification_registrar_.RemoveAll();
74 }
75
Create(content::BrowserContext * context)76 LanguageSettingsPrivateDelegate* LanguageSettingsPrivateDelegate::Create(
77 content::BrowserContext* context) {
78 return new LanguageSettingsPrivateDelegate(context);
79 }
80
81 std::vector<language_settings_private::SpellcheckDictionaryStatus>
GetHunspellDictionaryStatuses()82 LanguageSettingsPrivateDelegate::GetHunspellDictionaryStatuses() {
83 std::vector<language_settings_private::SpellcheckDictionaryStatus> statuses;
84 for (const auto& dictionary : GetHunspellDictionaries()) {
85 if (!dictionary)
86 continue;
87 language_settings_private::SpellcheckDictionaryStatus status;
88 status.language_code = dictionary->GetLanguage();
89 status.is_ready = dictionary->IsReady();
90 if (!status.is_ready) {
91 if (dictionary->IsDownloadInProgress())
92 status.is_downloading.reset(new bool(true));
93 if (dictionary->IsDownloadFailure())
94 status.download_failed.reset(new bool(true));
95 }
96 statuses.push_back(std::move(status));
97 }
98 return statuses;
99 }
100
Shutdown()101 void LanguageSettingsPrivateDelegate::Shutdown() {
102 // Unregister with the event router. We first check and see if there *is* an
103 // event router, because some unit tests try to shutdown all context services,
104 // but didn't initialize the event router first.
105 EventRouter* event_router = EventRouter::Get(context_);
106 if (event_router)
107 event_router->UnregisterObserver(this);
108
109 if (listening_spellcheck_) {
110 RemoveDictionaryObservers();
111 listening_spellcheck_ = false;
112 }
113
114 #if defined(OS_CHROMEOS)
115 if (listening_input_method_) {
116 auto* input_method_manager =
117 chromeos::input_method::InputMethodManager::Get();
118 if (input_method_manager)
119 input_method_manager->RemoveObserver(this);
120 listening_input_method_ = false;
121 }
122 #endif // defined(OS_CHROMEOS)
123 }
124
OnListenerAdded(const EventListenerInfo & details)125 void LanguageSettingsPrivateDelegate::OnListenerAdded(
126 const EventListenerInfo& details) {
127 // Start listening to spellcheck change events.
128 if (details.event_name ==
129 language_settings_private::OnSpellcheckDictionariesChanged::kEventName ||
130 details.event_name ==
131 language_settings_private::OnCustomDictionaryChanged::kEventName) {
132 StartOrStopListeningForSpellcheckChanges();
133 return;
134 }
135 #if defined(OS_CHROMEOS)
136 if (details.event_name ==
137 language_settings_private::OnInputMethodAdded::kEventName ||
138 details.event_name ==
139 language_settings_private::OnInputMethodRemoved::kEventName) {
140 StartOrStopListeningForInputMethodChanges();
141 return;
142 }
143 #endif // defined(OS_CHROMEOS)
144 }
145
OnListenerRemoved(const EventListenerInfo & details)146 void LanguageSettingsPrivateDelegate::OnListenerRemoved(
147 const EventListenerInfo& details) {
148 // Stop listening to events if there are no more listeners.
149 StartOrStopListeningForSpellcheckChanges();
150 #if defined(OS_CHROMEOS)
151 StartOrStopListeningForInputMethodChanges();
152 #endif // defined(OS_CHROMEOS)
153 }
154
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)155 void LanguageSettingsPrivateDelegate::Observe(
156 int type,
157 const content::NotificationSource& source,
158 const content::NotificationDetails& details) {
159 profile_added_ = true;
160 StartOrStopListeningForSpellcheckChanges();
161 }
162
163 #if defined(OS_CHROMEOS)
InputMethodChanged(chromeos::input_method::InputMethodManager * manager,Profile * profile,bool show_message)164 void LanguageSettingsPrivateDelegate::InputMethodChanged(
165 chromeos::input_method::InputMethodManager* manager,
166 Profile* profile,
167 bool show_message) {
168 // Nothing to do.
169 }
170
OnInputMethodExtensionAdded(const std::string & extension_id)171 void LanguageSettingsPrivateDelegate::OnInputMethodExtensionAdded(
172 const std::string& extension_id) {
173 std::unique_ptr<base::ListValue> args(
174 language_settings_private::OnInputMethodAdded::Create(extension_id));
175 std::unique_ptr<extensions::Event> extension_event(new extensions::Event(
176 events::LANGUAGE_SETTINGS_PRIVATE_ON_INPUT_METHOD_ADDED,
177 language_settings_private::OnInputMethodAdded::kEventName,
178 std::move(args)));
179 EventRouter::Get(context_)->BroadcastEvent(std::move(extension_event));
180 }
181
OnInputMethodExtensionRemoved(const std::string & extension_id)182 void LanguageSettingsPrivateDelegate::OnInputMethodExtensionRemoved(
183 const std::string& extension_id) {
184 std::unique_ptr<base::ListValue> args(
185 language_settings_private::OnInputMethodRemoved::Create(extension_id));
186 std::unique_ptr<extensions::Event> extension_event(new extensions::Event(
187 events::LANGUAGE_SETTINGS_PRIVATE_ON_INPUT_METHOD_REMOVED,
188 language_settings_private::OnInputMethodRemoved::kEventName,
189 std::move(args)));
190 EventRouter::Get(context_)->BroadcastEvent(std::move(extension_event));
191 }
192 #endif // defined(OS_CHROMEOS)
193
OnHunspellDictionaryInitialized(const std::string & language)194 void LanguageSettingsPrivateDelegate::OnHunspellDictionaryInitialized(
195 const std::string& language) {
196 BroadcastDictionariesChangedEvent();
197 }
198
OnHunspellDictionaryDownloadBegin(const std::string & language)199 void LanguageSettingsPrivateDelegate::OnHunspellDictionaryDownloadBegin(
200 const std::string& language) {
201 BroadcastDictionariesChangedEvent();
202 }
203
OnHunspellDictionaryDownloadSuccess(const std::string & language)204 void LanguageSettingsPrivateDelegate::OnHunspellDictionaryDownloadSuccess(
205 const std::string& language) {
206 BroadcastDictionariesChangedEvent();
207 }
208
OnHunspellDictionaryDownloadFailure(const std::string & language)209 void LanguageSettingsPrivateDelegate::OnHunspellDictionaryDownloadFailure(
210 const std::string& language) {
211 BroadcastDictionariesChangedEvent();
212 }
213
OnCustomDictionaryLoaded()214 void LanguageSettingsPrivateDelegate::OnCustomDictionaryLoaded() {
215 }
216
OnCustomDictionaryChanged(const SpellcheckCustomDictionary::Change & change)217 void LanguageSettingsPrivateDelegate::OnCustomDictionaryChanged(
218 const SpellcheckCustomDictionary::Change& change) {
219 std::vector<std::string> to_add(change.to_add().begin(),
220 change.to_add().end());
221 std::vector<std::string> to_remove(change.to_remove().begin(),
222 change.to_remove().end());
223 std::unique_ptr<base::ListValue> args(
224 language_settings_private::OnCustomDictionaryChanged::Create(to_add,
225 to_remove));
226 std::unique_ptr<Event> extension_event(new Event(
227 events::LANGUAGE_SETTINGS_PRIVATE_ON_CUSTOM_DICTIONARY_CHANGED,
228 language_settings_private::OnCustomDictionaryChanged::kEventName,
229 std::move(args)));
230 EventRouter::Get(context_)->BroadcastEvent(std::move(extension_event));
231 }
232
RefreshDictionaries(bool was_listening,bool should_listen)233 void LanguageSettingsPrivateDelegate::RefreshDictionaries(
234 bool was_listening, bool should_listen) {
235 if (!profile_added_)
236 return;
237 if (was_listening)
238 RemoveDictionaryObservers();
239 hunspell_dictionaries_.clear();
240 SpellcheckService* service = SpellcheckServiceFactory::GetForContext(
241 context_);
242 if (!custom_dictionary_)
243 custom_dictionary_ = service->GetCustomDictionary();
244
245 const std::vector<std::unique_ptr<SpellcheckHunspellDictionary>>&
246 dictionaries(service->GetHunspellDictionaries());
247 for (const auto& dictionary : dictionaries) {
248 hunspell_dictionaries_.push_back(dictionary->AsWeakPtr());
249 if (should_listen)
250 dictionary->AddObserver(this);
251 }
252 }
253
254 const LanguageSettingsPrivateDelegate::WeakDictionaries&
GetHunspellDictionaries()255 LanguageSettingsPrivateDelegate::GetHunspellDictionaries() {
256 // If there are no hunspell dictionaries, or the first is invalid, refresh.
257 if (hunspell_dictionaries_.empty() || !hunspell_dictionaries_.front())
258 RefreshDictionaries(listening_spellcheck_, listening_spellcheck_);
259 return hunspell_dictionaries_;
260 }
261
262 void LanguageSettingsPrivateDelegate::
StartOrStopListeningForSpellcheckChanges()263 StartOrStopListeningForSpellcheckChanges() {
264 EventRouter* event_router = EventRouter::Get(context_);
265 bool should_listen =
266 event_router->HasEventListener(language_settings_private::
267 OnSpellcheckDictionariesChanged::kEventName) ||
268 event_router->HasEventListener(language_settings_private::
269 OnCustomDictionaryChanged::kEventName);
270
271 if (should_listen && !listening_spellcheck_) {
272 // Update and observe the hunspell dictionaries.
273 RefreshDictionaries(listening_spellcheck_, should_listen);
274 // Observe the dictionaries preference.
275 pref_change_registrar_.Add(
276 spellcheck::prefs::kSpellCheckDictionaries,
277 base::Bind(
278 &LanguageSettingsPrivateDelegate::OnSpellcheckDictionariesChanged,
279 base::Unretained(this)));
280 // Observe the dictionary of custom words.
281 if (custom_dictionary_)
282 custom_dictionary_->AddObserver(this);
283 } else if (!should_listen && listening_spellcheck_) {
284 // Stop observing any dictionaries that still exist.
285 RemoveDictionaryObservers();
286 hunspell_dictionaries_.clear();
287 pref_change_registrar_.Remove(spellcheck::prefs::kSpellCheckDictionaries);
288 if (custom_dictionary_)
289 custom_dictionary_->RemoveObserver(this);
290 }
291
292 listening_spellcheck_ = should_listen;
293 }
294
295 #if defined(OS_CHROMEOS)
296 void LanguageSettingsPrivateDelegate::
StartOrStopListeningForInputMethodChanges()297 StartOrStopListeningForInputMethodChanges() {
298 EventRouter* event_router = EventRouter::Get(context_);
299 bool should_listen =
300 event_router->HasEventListener(
301 language_settings_private::OnInputMethodAdded::kEventName) ||
302 event_router->HasEventListener(
303 language_settings_private::OnInputMethodRemoved::kEventName);
304
305 auto* input_method_manager =
306 chromeos::input_method::InputMethodManager::Get();
307 if (input_method_manager) {
308 if (should_listen && !listening_input_method_)
309 input_method_manager->AddObserver(this);
310 else if (!should_listen && listening_input_method_)
311 input_method_manager->RemoveObserver(this);
312 }
313
314 listening_input_method_ = should_listen;
315 }
316 #endif // defined(OS_CHROMEOS)
317
RetryDownloadHunspellDictionary(const std::string & language)318 void LanguageSettingsPrivateDelegate::RetryDownloadHunspellDictionary(
319 const std::string& language) {
320 for (const base::WeakPtr<SpellcheckHunspellDictionary>& dictionary :
321 GetHunspellDictionaries()) {
322 if (dictionary && dictionary->GetLanguage() == language) {
323 dictionary->RetryDownloadDictionary(context_);
324 return;
325 }
326 }
327 }
328
OnSpellcheckDictionariesChanged()329 void LanguageSettingsPrivateDelegate::OnSpellcheckDictionariesChanged() {
330 RefreshDictionaries(listening_spellcheck_, listening_spellcheck_);
331 BroadcastDictionariesChangedEvent();
332 }
333
BroadcastDictionariesChangedEvent()334 void LanguageSettingsPrivateDelegate::BroadcastDictionariesChangedEvent() {
335 std::vector<language_settings_private::SpellcheckDictionaryStatus> statuses =
336 GetHunspellDictionaryStatuses();
337
338 std::unique_ptr<base::ListValue> args(
339 language_settings_private::OnSpellcheckDictionariesChanged::Create(
340 statuses));
341 std::unique_ptr<extensions::Event> extension_event(new extensions::Event(
342 events::LANGUAGE_SETTINGS_PRIVATE_ON_SPELLCHECK_DICTIONARIES_CHANGED,
343 language_settings_private::OnSpellcheckDictionariesChanged::kEventName,
344 std::move(args)));
345 EventRouter::Get(context_)->BroadcastEvent(std::move(extension_event));
346 }
347
RemoveDictionaryObservers()348 void LanguageSettingsPrivateDelegate::RemoveDictionaryObservers() {
349 for (const auto& dictionary : hunspell_dictionaries_) {
350 if (dictionary)
351 dictionary->RemoveObserver(this);
352 }
353 }
354
355 } // namespace extensions
356