12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/spellchecker/spellcheck_service.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/prefs/pref_member.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/prefs/pref_service.h"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_split.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/synchronization/waitable_event.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/spellchecker/spellcheck_factory.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/spellchecker/spellcheck_host_metrics.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/spellchecker/spellcheck_platform_mac.h"
15f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/spellchecker/spelling_service_client.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/pref_names.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/spellcheck_messages.h"
18d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "components/user_prefs/user_prefs.h"
19d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "content/public/browser/browser_context.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/browser_thread.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/notification_service.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/notification_types.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/render_process_host.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ipc/ipc_platform_file.h"
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using content::BrowserThread;
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using chrome::spellcheck_common::WordList;
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// TODO(rlp): I do not like globals, but keeping these for now during
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// transition.
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// An event used by browser tests to receive status events from this class and
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// its derived classes.
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)base::WaitableEvent* g_status_event = NULL;
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SpellcheckService::EventType g_status_type =
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SpellcheckService::BDICT_NOTINITIALIZED;
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
37d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)SpellcheckService::SpellcheckService(content::BrowserContext* context)
38d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    : context_(context),
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_ptr_factory_(this) {
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
41d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  PrefService* prefs = user_prefs::UserPrefs::Get(context);
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  pref_change_registrar_.Init(prefs);
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  std::string language_code;
4590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  std::string country_code;
4690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
4790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      prefs->GetString(prefs::kSpellCheckDictionary),
4890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      &language_code,
4990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      &country_code);
5090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  feedback_sender_.reset(new spellcheck::FeedbackSender(
51d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      context->GetRequestContext(), language_code, country_code));
5290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  pref_change_registrar_.Add(
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      prefs::kEnableAutoSpellCorrect,
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&SpellcheckService::OnEnableAutoSpellCorrectChanged,
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 base::Unretained(this)));
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  pref_change_registrar_.Add(
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      prefs::kSpellCheckDictionary,
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&SpellcheckService::OnSpellCheckDictionaryChanged,
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 base::Unretained(this)));
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) pref_change_registrar_.Add(
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     prefs::kSpellCheckUseSpellingService,
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)     base::Bind(&SpellcheckService::OnUseSpellingServiceChanged,
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                base::Unretained(this)));
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  pref_change_registrar_.Add(
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      prefs::kEnableContinuousSpellcheck,
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&SpellcheckService::InitForAllRenderers,
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 base::Unretained(this)));
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  OnSpellCheckDictionaryChanged();
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  custom_dictionary_.reset(new SpellcheckCustomDictionary(context_->GetPath()));
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  custom_dictionary_->AddObserver(this);
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  custom_dictionary_->Load();
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
76868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  registrar_.Add(this,
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 content::NOTIFICATION_RENDERER_PROCESS_CREATED,
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 content::NotificationService::AllSources());
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SpellcheckService::~SpellcheckService() {
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Remove pref observers
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  pref_change_registrar_.RemoveAll();
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int SpellcheckService::GetSpellCheckLanguages(
88d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    content::BrowserContext* context,
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::vector<std::string>* languages) {
90d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  PrefService* prefs = user_prefs::UserPrefs::Get(context);
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  StringPrefMember accept_languages_pref;
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  StringPrefMember dictionary_language_pref;
93d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  accept_languages_pref.Init(prefs::kAcceptLanguages, prefs);
94d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  dictionary_language_pref.Init(prefs::kSpellCheckDictionary, prefs);
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string dictionary_language = dictionary_language_pref.GetValue();
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Now scan through the list of accept languages, and find possible mappings
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // from this list to the existing list of spell check languages.
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<std::string> accept_languages;
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if defined(OS_MACOSX)
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (spellcheck_mac::SpellCheckerAvailable())
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    spellcheck_mac::GetAvailableLanguages(&accept_languages);
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  else
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::SplitString(accept_languages_pref.GetValue(), ',', &accept_languages);
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#else
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::SplitString(accept_languages_pref.GetValue(), ',', &accept_languages);
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif  // !OS_MACOSX
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GetSpellCheckLanguagesFromAcceptLanguages(
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      accept_languages, dictionary_language, languages);
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < languages->size(); ++i) {
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if ((*languages)[i] == dictionary_language)
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return i;
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return -1;
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckService::GetSpellCheckLanguagesFromAcceptLanguages(
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::vector<std::string>& accept_languages,
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& dictionary_language,
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::vector<std::string>* languages) {
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The current dictionary language should be there.
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  languages->push_back(dictionary_language);
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (std::vector<std::string>::const_iterator i = accept_languages.begin();
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       i != accept_languages.end(); ++i) {
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string language =
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        chrome::spellcheck_common::GetCorrespondingSpellCheckLanguage(*i);
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!language.empty() &&
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        std::find(languages->begin(), languages->end(), language) ==
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        languages->end()) {
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      languages->push_back(language);
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool SpellcheckService::SignalStatusEvent(
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SpellcheckService::EventType status_type) {
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!g_status_event)
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  g_status_type = status_type;
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  g_status_event->Signal();
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return true;
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckService::StartRecordingMetrics(bool spellcheck_enabled) {
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  metrics_.reset(new SpellCheckHostMetrics());
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  metrics_->RecordEnabledStats(spellcheck_enabled);
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  OnUseSpellingServiceChanged();
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckService::InitForRenderer(content::RenderProcessHost* process) {
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
161d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  content::BrowserContext* context = process->GetBrowserContext();
162d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  if (SpellcheckServiceFactory::GetForContext(context) != this)
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
165d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  PrefService* prefs = user_prefs::UserPrefs::Get(context);
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  IPC::PlatformFileForTransit file = IPC::InvalidPlatformFileForTransit();
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (hunspell_dictionary_->GetDictionaryFile().IsValid()) {
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    file = IPC::GetFileHandleForProcess(
17046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        hunspell_dictionary_->GetDictionaryFile().GetPlatformFile(),
17146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        process->GetHandle(), false);
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  process->Send(new SpellCheckMsg_Init(
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      file,
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      custom_dictionary_->GetWords(),
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      hunspell_dictionary_->GetLanguage(),
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      prefs->GetBoolean(prefs::kEnableAutoSpellCorrect)));
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  process->Send(new SpellCheckMsg_EnableSpellCheck(
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      prefs->GetBoolean(prefs::kEnableContinuousSpellcheck)));
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SpellCheckHostMetrics* SpellcheckService::GetMetrics() const {
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return metrics_.get();
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SpellcheckCustomDictionary* SpellcheckService::GetCustomDictionary() {
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return custom_dictionary_.get();
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SpellcheckHunspellDictionary* SpellcheckService::GetHunspellDictionary() {
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return hunspell_dictionary_.get();
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
19590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)spellcheck::FeedbackSender* SpellcheckService::GetFeedbackSender() {
19690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  return feedback_sender_.get();
197c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
198c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
199c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool SpellcheckService::LoadExternalDictionary(std::string language,
200c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                               std::string locale,
201c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                               std::string path,
202c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                               DictionaryFormat format) {
203c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return false;
204c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
205c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
206c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool SpellcheckService::UnloadExternalDictionary(std::string path) {
207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return false;
208c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
209c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckService::Observe(int type,
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                const content::NotificationSource& source,
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                const content::NotificationDetails& details) {
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(type == content::NOTIFICATION_RENDERER_PROCESS_CREATED);
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  content::RenderProcessHost* process =
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content::Source<content::RenderProcessHost>(source).ptr();
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  InitForRenderer(process);
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckService::OnCustomDictionaryLoaded() {
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  InitForAllRenderers();
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckService::OnCustomDictionaryChanged(
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const SpellcheckCustomDictionary::Change& dictionary_change) {
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (content::RenderProcessHost::iterator i(
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          content::RenderProcessHost::AllHostsIterator());
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       !i.IsAtEnd(); i.Advance()) {
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    i.GetCurrentValue()->Send(new SpellCheckMsg_CustomDictionaryChanged(
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        dictionary_change.to_add(),
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        dictionary_change.to_remove()));
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckService::OnHunspellDictionaryInitialized() {
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  InitForAllRenderers();
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckService::OnHunspellDictionaryDownloadBegin() {
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckService::OnHunspellDictionaryDownloadSuccess() {
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckService::OnHunspellDictionaryDownloadFailure() {
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckService::AttachStatusEvent(base::WaitableEvent* status_event) {
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  g_status_event = status_event;
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SpellcheckService::EventType SpellcheckService::GetStatusEvent() {
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return g_status_type;
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckService::InitForAllRenderers() {
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (content::RenderProcessHost::iterator i(
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          content::RenderProcessHost::AllHostsIterator());
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       !i.IsAtEnd(); i.Advance()) {
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    content::RenderProcessHost* process = i.GetCurrentValue();
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (process && process->GetHandle())
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      InitForRenderer(process);
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckService::OnEnableAutoSpellCorrectChanged() {
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool enabled = pref_change_registrar_.prefs()->GetBoolean(
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      prefs::kEnableAutoSpellCorrect);
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (content::RenderProcessHost::iterator i(
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           content::RenderProcessHost::AllHostsIterator());
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       !i.IsAtEnd(); i.Advance()) {
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    content::RenderProcessHost* process = i.GetCurrentValue();
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    process->Send(new SpellCheckMsg_EnableAutoSpellCorrect(enabled));
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckService::OnSpellCheckDictionaryChanged() {
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (hunspell_dictionary_.get())
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    hunspell_dictionary_->RemoveObserver(this);
285d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  PrefService* prefs = user_prefs::UserPrefs::Get(context_);
286d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  DCHECK(prefs);
287d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
28890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  std::string dictionary =
289d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      prefs->GetString(prefs::kSpellCheckDictionary);
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  hunspell_dictionary_.reset(new SpellcheckHunspellDictionary(
291d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      dictionary, context_->GetRequestContext(), this));
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  hunspell_dictionary_->AddObserver(this);
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  hunspell_dictionary_->Load();
29490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  std::string language_code;
29590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  std::string country_code;
29690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  chrome::spellcheck_common::GetISOLanguageCountryCodeFromLocale(
29790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      dictionary, &language_code, &country_code);
29890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  feedback_sender_->OnLanguageCountryChange(language_code, country_code);
299f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  UpdateFeedbackSenderState();
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckService::OnUseSpellingServiceChanged() {
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool enabled = pref_change_registrar_.prefs()->GetBoolean(
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      prefs::kSpellCheckUseSpellingService);
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (metrics_)
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    metrics_->RecordSpellingServiceStats(enabled);
307f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  UpdateFeedbackSenderState();
308f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
309f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
310f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void SpellcheckService::UpdateFeedbackSenderState() {
311f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (SpellingServiceClient::IsAvailable(
312f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          context_, SpellingServiceClient::SPELLCHECK)) {
313f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    feedback_sender_->StartFeedbackCollection();
314f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  } else {
315f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    feedback_sender_->StopFeedbackCollection();
316f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
318