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