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.
5 #include "chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/memory/linked_ptr.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/memory/scoped_vector.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/spellchecker/spellcheck_factory.h"
21 #include "chrome/browser/spellchecker/spellcheck_service.h"
22 #include "chrome/common/pref_names.h"
23 #include "content/public/browser/browser_context.h"
24 #include "content/public/browser/notification_source.h"
26 namespace extensions
{
28 namespace language_settings_private
= api::language_settings_private
;
30 LanguageSettingsPrivateDelegate::LanguageSettingsPrivateDelegate(
31 content::BrowserContext
* context
)
32 : custom_dictionary_(nullptr),
34 listening_spellcheck_(false),
35 profile_added_(false) {
36 // Register with the event router so we know when renderers are listening to
37 // our events. We first check and see if there *is* an event router, because
38 // some unit tests try to create all context services, but don't initialize
39 // the event router first.
40 EventRouter
* event_router
= EventRouter::Get(context_
);
44 event_router
->RegisterObserver(this,
45 language_settings_private::OnSpellcheckDictionariesChanged::kEventName
);
46 event_router
->RegisterObserver(this,
47 language_settings_private::OnCustomDictionaryChanged::kEventName
);
49 // SpellcheckService cannot be created until Profile::DoFinalInit() has been
50 // called. http://crbug.com/171406
51 notification_registrar_
.Add(this,
52 chrome::NOTIFICATION_PROFILE_ADDED
,
53 content::Source
<Profile
>(Profile::FromBrowserContext(context_
)));
55 pref_change_registrar_
.Init(Profile::FromBrowserContext(context_
)->
58 StartOrStopListeningForSpellcheckChanges();
61 LanguageSettingsPrivateDelegate::~LanguageSettingsPrivateDelegate() {
62 DCHECK(!listening_spellcheck_
);
63 pref_change_registrar_
.RemoveAll();
64 notification_registrar_
.RemoveAll();
67 LanguageSettingsPrivateDelegate
* LanguageSettingsPrivateDelegate::Create(
68 content::BrowserContext
* context
) {
69 return new LanguageSettingsPrivateDelegate(context
);
72 ScopedVector
<language_settings_private::SpellcheckDictionaryStatus
>
73 LanguageSettingsPrivateDelegate::GetHunspellDictionaryStatuses() {
74 ScopedVector
<language_settings_private::SpellcheckDictionaryStatus
> statuses
;
75 for (const auto& dictionary
: GetHunspellDictionaries()) {
78 scoped_ptr
<language_settings_private::SpellcheckDictionaryStatus
> status(
79 new language_settings_private::SpellcheckDictionaryStatus());
80 status
->language_code
= dictionary
->GetLanguage();
81 status
->is_ready
= dictionary
->IsReady();
82 if (!status
->is_ready
) {
83 if (dictionary
->IsDownloadInProgress())
84 status
->is_downloading
.reset(new bool(true));
85 if (dictionary
->IsDownloadFailure())
86 status
->download_failed
.reset(new bool(true));
88 statuses
.push_back(status
.Pass());
90 return statuses
.Pass();
93 void LanguageSettingsPrivateDelegate::Shutdown() {
94 // Unregister with the event router. We first check and see if there *is* an
95 // event router, because some unit tests try to shutdown all context services,
96 // but didn't initialize the event router first.
97 EventRouter
* event_router
= EventRouter::Get(context_
);
99 event_router
->UnregisterObserver(this);
101 if (listening_spellcheck_
) {
102 RemoveDictionaryObservers();
103 listening_spellcheck_
= false;
107 void LanguageSettingsPrivateDelegate::OnListenerAdded(
108 const EventListenerInfo
& details
) {
109 // Start listening to spellcheck change events.
110 if (details
.event_name
==
111 language_settings_private::OnSpellcheckDictionariesChanged::kEventName
||
112 details
.event_name
==
113 language_settings_private::OnCustomDictionaryChanged::kEventName
) {
114 StartOrStopListeningForSpellcheckChanges();
118 void LanguageSettingsPrivateDelegate::OnListenerRemoved(
119 const EventListenerInfo
& details
) {
120 // Stop listening to events if there are no more listeners.
121 StartOrStopListeningForSpellcheckChanges();
124 void LanguageSettingsPrivateDelegate::Observe(
126 const content::NotificationSource
& source
,
127 const content::NotificationDetails
& details
) {
128 profile_added_
= true;
129 StartOrStopListeningForSpellcheckChanges();
132 void LanguageSettingsPrivateDelegate::OnHunspellDictionaryInitialized(
133 const std::string
& language
) {
134 BroadcastDictionariesChangedEvent();
137 void LanguageSettingsPrivateDelegate::OnHunspellDictionaryDownloadBegin(
138 const std::string
& language
) {
139 BroadcastDictionariesChangedEvent();
142 void LanguageSettingsPrivateDelegate::OnHunspellDictionaryDownloadSuccess(
143 const std::string
& language
) {
144 BroadcastDictionariesChangedEvent();
147 void LanguageSettingsPrivateDelegate::OnHunspellDictionaryDownloadFailure(
148 const std::string
& language
) {
149 BroadcastDictionariesChangedEvent();
152 void LanguageSettingsPrivateDelegate::OnCustomDictionaryLoaded() {
155 void LanguageSettingsPrivateDelegate::OnCustomDictionaryChanged(
156 const SpellcheckCustomDictionary::Change
& change
) {
157 std::vector
<std::string
> to_add(change
.to_add().begin(),
158 change
.to_add().end());
159 std::vector
<std::string
> to_remove(change
.to_remove().begin(),
160 change
.to_remove().end());
161 scoped_ptr
<base::ListValue
> args(
162 language_settings_private::OnCustomDictionaryChanged::Create(
164 scoped_ptr
<Event
> extension_event(new Event(
165 events::LANGUAGE_SETTINGS_PRIVATE_ON_CUSTOM_DICTIONARY_CHANGED
,
166 language_settings_private::OnCustomDictionaryChanged::kEventName
,
168 EventRouter::Get(context_
)->BroadcastEvent(extension_event
.Pass());
171 void LanguageSettingsPrivateDelegate::RefreshDictionaries(
172 bool was_listening
, bool should_listen
) {
176 RemoveDictionaryObservers();
177 hunspell_dictionaries_
.clear();
178 SpellcheckService
* service
= SpellcheckServiceFactory::GetForContext(
180 if (!custom_dictionary_
)
181 custom_dictionary_
= service
->GetCustomDictionary();
183 const ScopedVector
<SpellcheckHunspellDictionary
>& dictionaries(
184 service
->GetHunspellDictionaries());
185 for (const auto& dictionary
: dictionaries
) {
186 hunspell_dictionaries_
.push_back(dictionary
->AsWeakPtr());
188 dictionary
->AddObserver(this);
192 const LanguageSettingsPrivateDelegate::WeakDictionaries
&
193 LanguageSettingsPrivateDelegate::GetHunspellDictionaries() {
194 // If there are no hunspell dictionaries, or the first is invalid, refresh.
195 if (!hunspell_dictionaries_
.size() || !hunspell_dictionaries_
.front())
196 RefreshDictionaries(listening_spellcheck_
, listening_spellcheck_
);
197 return hunspell_dictionaries_
;
200 void LanguageSettingsPrivateDelegate::
201 StartOrStopListeningForSpellcheckChanges() {
202 EventRouter
* event_router
= EventRouter::Get(context_
);
204 event_router
->HasEventListener(language_settings_private::
205 OnSpellcheckDictionariesChanged::kEventName
) ||
206 event_router
->HasEventListener(language_settings_private::
207 OnCustomDictionaryChanged::kEventName
);
209 if (should_listen
&& !listening_spellcheck_
) {
210 // Update and observe the hunspell dictionaries.
211 RefreshDictionaries(listening_spellcheck_
, should_listen
);
212 // Observe the dictionaries preference.
213 pref_change_registrar_
.Add(prefs::kSpellCheckDictionaries
, base::Bind(
214 &LanguageSettingsPrivateDelegate::OnSpellcheckDictionariesChanged
,
215 base::Unretained(this)));
216 // Observe the dictionary of custom words.
217 if (custom_dictionary_
)
218 custom_dictionary_
->AddObserver(this);
219 } else if (!should_listen
&& listening_spellcheck_
) {
220 // Stop observing any dictionaries that still exist.
221 RemoveDictionaryObservers();
222 hunspell_dictionaries_
.clear();
223 pref_change_registrar_
.Remove(prefs::kSpellCheckDictionaries
);
224 if (custom_dictionary_
)
225 custom_dictionary_
->RemoveObserver(this);
228 listening_spellcheck_
= should_listen
;
231 void LanguageSettingsPrivateDelegate::OnSpellcheckDictionariesChanged() {
232 RefreshDictionaries(listening_spellcheck_
, listening_spellcheck_
);
233 BroadcastDictionariesChangedEvent();
236 void LanguageSettingsPrivateDelegate::BroadcastDictionariesChangedEvent() {
237 std::vector
<linked_ptr
<language_settings_private::SpellcheckDictionaryStatus
>>
239 ScopedVector
<language_settings_private::SpellcheckDictionaryStatus
> statuses
=
240 GetHunspellDictionaryStatuses();
242 for (language_settings_private::SpellcheckDictionaryStatus
* status
: statuses
)
243 broadcast_statuses
.push_back(make_linked_ptr(status
));
244 statuses
.weak_clear();
246 scoped_ptr
<base::ListValue
> args(
247 language_settings_private::OnSpellcheckDictionariesChanged::Create(
248 broadcast_statuses
));
249 scoped_ptr
<extensions::Event
> extension_event(new extensions::Event(
250 events::LANGUAGE_SETTINGS_PRIVATE_ON_SPELLCHECK_DICTIONARIES_CHANGED
,
251 language_settings_private::OnSpellcheckDictionariesChanged::kEventName
,
253 EventRouter::Get(context_
)->BroadcastEvent(extension_event
.Pass());
256 void LanguageSettingsPrivateDelegate::RemoveDictionaryObservers() {
257 for (const auto& dictionary
: hunspell_dictionaries_
) {
259 dictionary
->RemoveObserver(this);
263 } // namespace extensions