// 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 #include #include #include #include "base/bind.h" #include "base/bind_helpers.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" #include "content/public/browser/storage_partition.h" namespace extensions { namespace language_settings_private = api::language_settings_private; LanguageSettingsPrivateDelegate::LanguageSettingsPrivateDelegate( content::BrowserContext* context) : custom_dictionary_(nullptr), context_(context), listening_spellcheck_(false), listening_input_method_(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); event_router->RegisterObserver( this, language_settings_private::OnInputMethodAdded::kEventName); event_router->RegisterObserver( this, language_settings_private::OnInputMethodRemoved::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::FromBrowserContext(context_))); pref_change_registrar_.Init(Profile::FromBrowserContext(context_)-> GetPrefs()); StartOrStopListeningForSpellcheckChanges(); #if defined(OS_CHROMEOS) StartOrStopListeningForInputMethodChanges(); #endif // defined(OS_CHROMEOS) } LanguageSettingsPrivateDelegate::~LanguageSettingsPrivateDelegate() { DCHECK(!listening_spellcheck_); DCHECK(!listening_input_method_); pref_change_registrar_.RemoveAll(); notification_registrar_.RemoveAll(); } LanguageSettingsPrivateDelegate* LanguageSettingsPrivateDelegate::Create( content::BrowserContext* context) { return new LanguageSettingsPrivateDelegate(context); } std::vector LanguageSettingsPrivateDelegate::GetHunspellDictionaryStatuses() { std::vector 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; } #if defined(OS_CHROMEOS) if (listening_input_method_) { auto* input_method_manager = chromeos::input_method::InputMethodManager::Get(); if (input_method_manager) input_method_manager->RemoveObserver(this); listening_input_method_ = false; } #endif // defined(OS_CHROMEOS) } 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(); return; } #if defined(OS_CHROMEOS) if (details.event_name == language_settings_private::OnInputMethodAdded::kEventName || details.event_name == language_settings_private::OnInputMethodRemoved::kEventName) { StartOrStopListeningForInputMethodChanges(); return; } #endif // defined(OS_CHROMEOS) } void LanguageSettingsPrivateDelegate::OnListenerRemoved( const EventListenerInfo& details) { // Stop listening to events if there are no more listeners. StartOrStopListeningForSpellcheckChanges(); #if defined(OS_CHROMEOS) StartOrStopListeningForInputMethodChanges(); #endif // defined(OS_CHROMEOS) } void LanguageSettingsPrivateDelegate::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { profile_added_ = true; StartOrStopListeningForSpellcheckChanges(); } #if defined(OS_CHROMEOS) void LanguageSettingsPrivateDelegate::InputMethodChanged( chromeos::input_method::InputMethodManager* manager, Profile* profile, bool show_message) { // Nothing to do. } void LanguageSettingsPrivateDelegate::OnInputMethodExtensionAdded( const std::string& extension_id) { std::unique_ptr args( language_settings_private::OnInputMethodAdded::Create(extension_id)); std::unique_ptr extension_event(new extensions::Event( events::LANGUAGE_SETTINGS_PRIVATE_ON_INPUT_METHOD_ADDED, language_settings_private::OnInputMethodAdded::kEventName, std::move(args))); EventRouter::Get(context_)->BroadcastEvent(std::move(extension_event)); } void LanguageSettingsPrivateDelegate::OnInputMethodExtensionRemoved( const std::string& extension_id) { std::unique_ptr args( language_settings_private::OnInputMethodRemoved::Create(extension_id)); std::unique_ptr extension_event(new extensions::Event( events::LANGUAGE_SETTINGS_PRIVATE_ON_INPUT_METHOD_REMOVED, language_settings_private::OnInputMethodRemoved::kEventName, std::move(args))); EventRouter::Get(context_)->BroadcastEvent(std::move(extension_event)); } #endif // defined(OS_CHROMEOS) 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 to_add(change.to_add().begin(), change.to_add().end()); std::vector to_remove(change.to_remove().begin(), change.to_remove().end()); std::unique_ptr args( language_settings_private::OnCustomDictionaryChanged::Create(to_add, to_remove)); std::unique_ptr 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 std::vector>& dictionaries(service->GetHunspellDictionaries()); for (const 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; } #if defined(OS_CHROMEOS) void LanguageSettingsPrivateDelegate:: StartOrStopListeningForInputMethodChanges() { EventRouter* event_router = EventRouter::Get(context_); bool should_listen = event_router->HasEventListener( language_settings_private::OnInputMethodAdded::kEventName) || event_router->HasEventListener( language_settings_private::OnInputMethodRemoved::kEventName); auto* input_method_manager = chromeos::input_method::InputMethodManager::Get(); if (input_method_manager) { if (should_listen && !listening_input_method_) input_method_manager->AddObserver(this); else if (!should_listen && listening_input_method_) input_method_manager->RemoveObserver(this); } listening_input_method_ = should_listen; } #endif // defined(OS_CHROMEOS) void LanguageSettingsPrivateDelegate::RetryDownloadHunspellDictionary( const std::string& language) { for (const base::WeakPtr dictionary : GetHunspellDictionaries()) { if (dictionary && dictionary->GetLanguage() == language) { dictionary->RetryDownloadDictionary(context_); return; } } } void LanguageSettingsPrivateDelegate::OnSpellcheckDictionariesChanged() { RefreshDictionaries(listening_spellcheck_, listening_spellcheck_); BroadcastDictionariesChangedEvent(); } void LanguageSettingsPrivateDelegate::BroadcastDictionariesChangedEvent() { std::vector statuses = GetHunspellDictionaryStatuses(); std::unique_ptr args( language_settings_private::OnSpellcheckDictionariesChanged::Create( statuses)); std::unique_ptr 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