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 BroadcastDictionariesChangedEvent();
136 void LanguageSettingsPrivateDelegate::OnHunspellDictionaryDownloadBegin() {
137 BroadcastDictionariesChangedEvent();
140 void LanguageSettingsPrivateDelegate::OnHunspellDictionaryDownloadSuccess() {
141 BroadcastDictionariesChangedEvent();
144 void LanguageSettingsPrivateDelegate::OnHunspellDictionaryDownloadFailure() {
145 BroadcastDictionariesChangedEvent();
148 void LanguageSettingsPrivateDelegate::OnCustomDictionaryLoaded() {
151 void LanguageSettingsPrivateDelegate::OnCustomDictionaryChanged(
152 const SpellcheckCustomDictionary::Change
& change
) {
153 std::vector
<std::string
> to_add(change
.to_add().begin(),
154 change
.to_add().end());
155 std::vector
<std::string
> to_remove(change
.to_remove().begin(),
156 change
.to_remove().end());
157 scoped_ptr
<base::ListValue
> args(
158 language_settings_private::OnCustomDictionaryChanged::Create(
160 scoped_ptr
<Event
> extension_event(new Event(
161 events::LANGUAGE_SETTINGS_PRIVATE_ON_CUSTOM_DICTIONARY_CHANGED
,
162 language_settings_private::OnCustomDictionaryChanged::kEventName
,
164 EventRouter::Get(context_
)->BroadcastEvent(extension_event
.Pass());
167 void LanguageSettingsPrivateDelegate::RefreshDictionaries(
168 bool was_listening
, bool should_listen
) {
172 RemoveDictionaryObservers();
173 hunspell_dictionaries_
.clear();
174 SpellcheckService
* service
= SpellcheckServiceFactory::GetForContext(
176 if (!custom_dictionary_
)
177 custom_dictionary_
= service
->GetCustomDictionary();
179 const ScopedVector
<SpellcheckHunspellDictionary
>& dictionaries(
180 service
->GetHunspellDictionaries());
181 for (const auto& dictionary
: dictionaries
) {
182 hunspell_dictionaries_
.push_back(dictionary
->AsWeakPtr());
184 dictionary
->AddObserver(this);
188 const LanguageSettingsPrivateDelegate::WeakDictionaries
&
189 LanguageSettingsPrivateDelegate::GetHunspellDictionaries() {
190 // If there are no hunspell dictionaries, or the first is invalid, refresh.
191 if (!hunspell_dictionaries_
.size() || !hunspell_dictionaries_
.front())
192 RefreshDictionaries(listening_spellcheck_
, listening_spellcheck_
);
193 return hunspell_dictionaries_
;
196 void LanguageSettingsPrivateDelegate::
197 StartOrStopListeningForSpellcheckChanges() {
198 EventRouter
* event_router
= EventRouter::Get(context_
);
200 event_router
->HasEventListener(language_settings_private::
201 OnSpellcheckDictionariesChanged::kEventName
) ||
202 event_router
->HasEventListener(language_settings_private::
203 OnCustomDictionaryChanged::kEventName
);
205 if (should_listen
&& !listening_spellcheck_
) {
206 // Update and observe the hunspell dictionaries.
207 RefreshDictionaries(listening_spellcheck_
, should_listen
);
208 // Observe the dictionaries preference.
209 pref_change_registrar_
.Add(prefs::kSpellCheckDictionaries
, base::Bind(
210 &LanguageSettingsPrivateDelegate::OnSpellcheckDictionariesChanged
,
211 base::Unretained(this)));
212 // Observe the dictionary of custom words.
213 if (custom_dictionary_
)
214 custom_dictionary_
->AddObserver(this);
215 } else if (!should_listen
&& listening_spellcheck_
) {
216 // Stop observing any dictionaries that still exist.
217 RemoveDictionaryObservers();
218 hunspell_dictionaries_
.clear();
219 pref_change_registrar_
.Remove(prefs::kSpellCheckDictionaries
);
220 if (custom_dictionary_
)
221 custom_dictionary_
->RemoveObserver(this);
224 listening_spellcheck_
= should_listen
;
227 void LanguageSettingsPrivateDelegate::OnSpellcheckDictionariesChanged() {
228 RefreshDictionaries(listening_spellcheck_
, listening_spellcheck_
);
229 BroadcastDictionariesChangedEvent();
232 void LanguageSettingsPrivateDelegate::BroadcastDictionariesChangedEvent() {
233 std::vector
<linked_ptr
<language_settings_private::SpellcheckDictionaryStatus
>>
235 ScopedVector
<language_settings_private::SpellcheckDictionaryStatus
> statuses
=
236 GetHunspellDictionaryStatuses();
238 for (language_settings_private::SpellcheckDictionaryStatus
* status
: statuses
)
239 broadcast_statuses
.push_back(make_linked_ptr(status
));
240 statuses
.weak_clear();
242 scoped_ptr
<base::ListValue
> args(
243 language_settings_private::OnSpellcheckDictionariesChanged::Create(
244 broadcast_statuses
));
245 scoped_ptr
<extensions::Event
> extension_event(new extensions::Event(
246 events::LANGUAGE_SETTINGS_PRIVATE_ON_SPELLCHECK_DICTIONARIES_CHANGED
,
247 language_settings_private::OnSpellcheckDictionariesChanged::kEventName
,
249 EventRouter::Get(context_
)->BroadcastEvent(extension_event
.Pass());
252 void LanguageSettingsPrivateDelegate::RemoveDictionaryObservers() {
253 for (const auto& dictionary
: hunspell_dictionaries_
) {
255 dictionary
->RemoveObserver(this);
259 } // namespace extensions