blob: e04cfb041b117837b25731129ed830418c68b582 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/scoped_vector.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/spellchecker/spellcheck_factory.h"
#include "chrome/browser/spellchecker/spellcheck_service.h"
#include "components/prefs/pref_service.h"
#include "components/spellcheck/browser/pref_names.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/notification_source.h"
namespace extensions {
namespace language_settings_private = api::language_settings_private;
LanguageSettingsPrivateDelegate::LanguageSettingsPrivateDelegate(
content::BrowserContext* context)
: custom_dictionary_(nullptr),
context_(context),
listening_spellcheck_(false),
profile_added_(false) {
// Register with the event router so we know when renderers are listening to
// our events. We first check and see if there *is* an event router, because
// some unit tests try to create all context services, but don't initialize
// the event router first.
EventRouter* event_router = EventRouter::Get(context_);
if (!event_router)
return;
event_router->RegisterObserver(this,
language_settings_private::OnSpellcheckDictionariesChanged::kEventName);
event_router->RegisterObserver(this,
language_settings_private::OnCustomDictionaryChanged::kEventName);
// SpellcheckService cannot be created until Profile::DoFinalInit() has been
// called. http://crbug.com/171406
notification_registrar_.Add(this,
chrome::NOTIFICATION_PROFILE_ADDED,
content::Source<Profile>(Profile::FromBrowserContext(context_)));
pref_change_registrar_.Init(Profile::FromBrowserContext(context_)->
GetPrefs());
StartOrStopListeningForSpellcheckChanges();
}
LanguageSettingsPrivateDelegate::~LanguageSettingsPrivateDelegate() {
DCHECK(!listening_spellcheck_);
pref_change_registrar_.RemoveAll();
notification_registrar_.RemoveAll();
}
LanguageSettingsPrivateDelegate* LanguageSettingsPrivateDelegate::Create(
content::BrowserContext* context) {
return new LanguageSettingsPrivateDelegate(context);
}
std::vector<language_settings_private::SpellcheckDictionaryStatus>
LanguageSettingsPrivateDelegate::GetHunspellDictionaryStatuses() {
std::vector<language_settings_private::SpellcheckDictionaryStatus> statuses;
for (const auto& dictionary : GetHunspellDictionaries()) {
if (!dictionary)
continue;
language_settings_private::SpellcheckDictionaryStatus status;
status.language_code = dictionary->GetLanguage();
status.is_ready = dictionary->IsReady();
if (!status.is_ready) {
if (dictionary->IsDownloadInProgress())
status.is_downloading.reset(new bool(true));
if (dictionary->IsDownloadFailure())
status.download_failed.reset(new bool(true));
}
statuses.push_back(std::move(status));
}
return statuses;
}
void LanguageSettingsPrivateDelegate::Shutdown() {
// Unregister with the event router. We first check and see if there *is* an
// event router, because some unit tests try to shutdown all context services,
// but didn't initialize the event router first.
EventRouter* event_router = EventRouter::Get(context_);
if (event_router)
event_router->UnregisterObserver(this);
if (listening_spellcheck_) {
RemoveDictionaryObservers();
listening_spellcheck_ = false;
}
}
void LanguageSettingsPrivateDelegate::OnListenerAdded(
const EventListenerInfo& details) {
// Start listening to spellcheck change events.
if (details.event_name ==
language_settings_private::OnSpellcheckDictionariesChanged::kEventName ||
details.event_name ==
language_settings_private::OnCustomDictionaryChanged::kEventName) {
StartOrStopListeningForSpellcheckChanges();
}
}
void LanguageSettingsPrivateDelegate::OnListenerRemoved(
const EventListenerInfo& details) {
// Stop listening to events if there are no more listeners.
StartOrStopListeningForSpellcheckChanges();
}
void LanguageSettingsPrivateDelegate::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
profile_added_ = true;
StartOrStopListeningForSpellcheckChanges();
}
void LanguageSettingsPrivateDelegate::OnHunspellDictionaryInitialized(
const std::string& language) {
BroadcastDictionariesChangedEvent();
}
void LanguageSettingsPrivateDelegate::OnHunspellDictionaryDownloadBegin(
const std::string& language) {
BroadcastDictionariesChangedEvent();
}
void LanguageSettingsPrivateDelegate::OnHunspellDictionaryDownloadSuccess(
const std::string& language) {
BroadcastDictionariesChangedEvent();
}
void LanguageSettingsPrivateDelegate::OnHunspellDictionaryDownloadFailure(
const std::string& language) {
BroadcastDictionariesChangedEvent();
}
void LanguageSettingsPrivateDelegate::OnCustomDictionaryLoaded() {
}
void LanguageSettingsPrivateDelegate::OnCustomDictionaryChanged(
const SpellcheckCustomDictionary::Change& change) {
std::vector<std::string> to_add(change.to_add().begin(),
change.to_add().end());
std::vector<std::string> to_remove(change.to_remove().begin(),
change.to_remove().end());
std::unique_ptr<base::ListValue> args(
language_settings_private::OnCustomDictionaryChanged::Create(to_add,
to_remove));
std::unique_ptr<Event> extension_event(new Event(
events::LANGUAGE_SETTINGS_PRIVATE_ON_CUSTOM_DICTIONARY_CHANGED,
language_settings_private::OnCustomDictionaryChanged::kEventName,
std::move(args)));
EventRouter::Get(context_)->BroadcastEvent(std::move(extension_event));
}
void LanguageSettingsPrivateDelegate::RefreshDictionaries(
bool was_listening, bool should_listen) {
if (!profile_added_)
return;
if (was_listening)
RemoveDictionaryObservers();
hunspell_dictionaries_.clear();
SpellcheckService* service = SpellcheckServiceFactory::GetForContext(
context_);
if (!custom_dictionary_)
custom_dictionary_ = service->GetCustomDictionary();
const ScopedVector<SpellcheckHunspellDictionary>& dictionaries(
service->GetHunspellDictionaries());
for (auto* dictionary : dictionaries) {
hunspell_dictionaries_.push_back(dictionary->AsWeakPtr());
if (should_listen)
dictionary->AddObserver(this);
}
}
const LanguageSettingsPrivateDelegate::WeakDictionaries&
LanguageSettingsPrivateDelegate::GetHunspellDictionaries() {
// If there are no hunspell dictionaries, or the first is invalid, refresh.
if (hunspell_dictionaries_.empty() || !hunspell_dictionaries_.front())
RefreshDictionaries(listening_spellcheck_, listening_spellcheck_);
return hunspell_dictionaries_;
}
void LanguageSettingsPrivateDelegate::
StartOrStopListeningForSpellcheckChanges() {
EventRouter* event_router = EventRouter::Get(context_);
bool should_listen =
event_router->HasEventListener(language_settings_private::
OnSpellcheckDictionariesChanged::kEventName) ||
event_router->HasEventListener(language_settings_private::
OnCustomDictionaryChanged::kEventName);
if (should_listen && !listening_spellcheck_) {
// Update and observe the hunspell dictionaries.
RefreshDictionaries(listening_spellcheck_, should_listen);
// Observe the dictionaries preference.
pref_change_registrar_.Add(
spellcheck::prefs::kSpellCheckDictionaries,
base::Bind(
&LanguageSettingsPrivateDelegate::OnSpellcheckDictionariesChanged,
base::Unretained(this)));
// Observe the dictionary of custom words.
if (custom_dictionary_)
custom_dictionary_->AddObserver(this);
} else if (!should_listen && listening_spellcheck_) {
// Stop observing any dictionaries that still exist.
RemoveDictionaryObservers();
hunspell_dictionaries_.clear();
pref_change_registrar_.Remove(spellcheck::prefs::kSpellCheckDictionaries);
if (custom_dictionary_)
custom_dictionary_->RemoveObserver(this);
}
listening_spellcheck_ = should_listen;
}
void LanguageSettingsPrivateDelegate::OnSpellcheckDictionariesChanged() {
RefreshDictionaries(listening_spellcheck_, listening_spellcheck_);
BroadcastDictionariesChangedEvent();
}
void LanguageSettingsPrivateDelegate::BroadcastDictionariesChangedEvent() {
std::vector<language_settings_private::SpellcheckDictionaryStatus> statuses =
GetHunspellDictionaryStatuses();
std::unique_ptr<base::ListValue> args(
language_settings_private::OnSpellcheckDictionariesChanged::Create(
statuses));
std::unique_ptr<extensions::Event> extension_event(new extensions::Event(
events::LANGUAGE_SETTINGS_PRIVATE_ON_SPELLCHECK_DICTIONARIES_CHANGED,
language_settings_private::OnSpellcheckDictionariesChanged::kEventName,
std::move(args)));
EventRouter::Get(context_)->BroadcastEvent(std::move(extension_event));
}
void LanguageSettingsPrivateDelegate::RemoveDictionaryObservers() {
for (const auto& dictionary : hunspell_dictionaries_) {
if (dictionary)
dictionary->RemoveObserver(this);
}
}
} // namespace extensions