| Index: chrome/browser/spellcheck_host.cc
|
| ===================================================================
|
| --- chrome/browser/spellcheck_host.cc (revision 0)
|
| +++ chrome/browser/spellcheck_host.cc (revision 0)
|
| @@ -0,0 +1,278 @@
|
| +// Copyright (c) 2009 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/spellcheck_host.h"
|
| +
|
| +#include <fcntl.h>
|
| +
|
| +#include "app/l10n_util.h"
|
| +#include "base/file_util.h"
|
| +#include "base/logging.h"
|
| +#include "base/path_service.h"
|
| +#include "base/string_util.h"
|
| +#include "chrome/common/chrome_constants.h"
|
| +#include "chrome/common/chrome_paths.h"
|
| +#include "chrome/common/notification_service.h"
|
| +#include "googleurl/src/gurl.h"
|
| +
|
| +namespace {
|
| +
|
| +static const struct {
|
| + // The language.
|
| + const char* language;
|
| +
|
| + // The corresponding language and region, used by the dictionaries.
|
| + const char* language_region;
|
| +} g_supported_spellchecker_languages[] = {
|
| + // Several languages are not to be included in the spellchecker list:
|
| + // th-TH, hu-HU, bg-BG, uk-UA
|
| + {"ca", "ca-ES"},
|
| + {"cs", "cs-CZ"},
|
| + {"da", "da-DK"},
|
| + {"de", "de-DE"},
|
| + {"el", "el-GR"},
|
| + {"en-AU", "en-AU"},
|
| + {"en-GB", "en-GB"},
|
| + {"en-US", "en-US"},
|
| + {"es", "es-ES"},
|
| + {"et", "et-EE"},
|
| + {"fr", "fr-FR"},
|
| + {"he", "he-IL"},
|
| + {"hi", "hi-IN"},
|
| + {"hr", "hr-HR"},
|
| + {"id", "id-ID"},
|
| + {"it", "it-IT"},
|
| + {"lt", "lt-LT"},
|
| + {"lv", "lv-LV"},
|
| + {"nb", "nb-NO"},
|
| + {"nl", "nl-NL"},
|
| + {"pl", "pl-PL"},
|
| + {"pt-BR", "pt-BR"},
|
| + {"pt-PT", "pt-PT"},
|
| + {"ro", "ro-RO"},
|
| + {"ru", "ru-RU"},
|
| + {"sk", "sk-SK"},
|
| + {"sl", "sl-SI"},
|
| + {"sv", "sv-SE"},
|
| + {"tr", "tr-TR"},
|
| + {"vi", "vi-VN"},
|
| +};
|
| +
|
| +// This function returns the language-region version of language name.
|
| +// e.g. returns hi-IN for hi.
|
| +std::string GetSpellCheckLanguageRegion(const std::string& input_language) {
|
| + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(g_supported_spellchecker_languages);
|
| + ++i) {
|
| + if (g_supported_spellchecker_languages[i].language == input_language) {
|
| + return std::string(
|
| + g_supported_spellchecker_languages[i].language_region);
|
| + }
|
| + }
|
| +
|
| + return input_language;
|
| +}
|
| +
|
| +FilePath GetVersionedFileName(const std::string& input_language,
|
| + const FilePath& dict_dir) {
|
| + // The default dictionary version is 1-2. These versions have been augmented
|
| + // with additional words found by the translation team.
|
| + static const char kDefaultVersionString[] = "-1-2";
|
| +
|
| + // The following dictionaries have either not been augmented with additional
|
| + // words (version 1-1) or have new words, as well as an upgraded dictionary
|
| + // as of Feb 2009 (version 1-3).
|
| + static const struct {
|
| + // The language input.
|
| + const char* language;
|
| +
|
| + // The corresponding version.
|
| + const char* version;
|
| + } special_version_string[] = {
|
| + {"en-AU", "-1-1"},
|
| + {"en-GB", "-1-1"},
|
| + {"es-ES", "-1-1"},
|
| + {"nl-NL", "-1-1"},
|
| + {"ru-RU", "-1-1"},
|
| + {"sv-SE", "-1-1"},
|
| + {"he-IL", "-1-1"},
|
| + {"el-GR", "-1-1"},
|
| + {"hi-IN", "-1-1"},
|
| + {"tr-TR", "-1-1"},
|
| + {"et-EE", "-1-1"},
|
| + {"fr-FR", "-1-4"}, // To fix a crash, fr dictionary was updated to 1.4.
|
| + {"lt-LT", "-1-3"},
|
| + {"pl-PL", "-1-3"}
|
| + };
|
| +
|
| + // Generate the bdict file name using default version string or special
|
| + // version string, depending on the language.
|
| + std::string language = GetSpellCheckLanguageRegion(input_language);
|
| + std::string versioned_bdict_file_name(language + kDefaultVersionString +
|
| + ".bdic");
|
| + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(special_version_string); ++i) {
|
| + if (language == special_version_string[i].language) {
|
| + versioned_bdict_file_name =
|
| + language + special_version_string[i].version + ".bdic";
|
| + break;
|
| + }
|
| + }
|
| +
|
| + return dict_dir.AppendASCII(versioned_bdict_file_name);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// Constructed on UI thread.
|
| +SpellCheckHost::SpellCheckHost(Observer* observer,
|
| + const std::string& language,
|
| + URLRequestContextGetter* request_context_getter)
|
| + : observer_(observer),
|
| + language_(language),
|
| + tried_to_download_(false),
|
| + request_context_getter_(request_context_getter) {
|
| + DCHECK(observer_);
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| +
|
| + // TODO(estade): for Windows, we need to fall back to DIR_USER_DATA if
|
| + // DIR_APP_DICTIONARIES is not writeable.
|
| + FilePath dict_dir;
|
| + PathService::Get(chrome::DIR_APP_DICTIONARIES, &dict_dir);
|
| + bdict_file_ = GetVersionedFileName(language, dict_dir);
|
| +
|
| + FilePath personal_file_directory;
|
| + PathService::Get(chrome::DIR_USER_DATA, &personal_file_directory);
|
| + custom_dictionary_file_ =
|
| + personal_file_directory.Append(chrome::kCustomDictionaryFileName);
|
| +
|
| + ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE,
|
| + NewRunnableMethod(this, &SpellCheckHost::Initialize));
|
| +}
|
| +
|
| +SpellCheckHost::~SpellCheckHost() {
|
| + if (fd_.fd != -1)
|
| + close(fd_.fd);
|
| +}
|
| +
|
| +void SpellCheckHost::UnsetObserver() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| +
|
| + observer_ = NULL;
|
| +}
|
| +
|
| +void SpellCheckHost::AddWord(const std::string& word) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| +
|
| + custom_words_.push_back(word);
|
| + ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE,
|
| + NewRunnableMethod(this,
|
| + &SpellCheckHost::WriteWordToCustomDictionary, word));
|
| + NotificationService::current()->Notify(
|
| + NotificationType::SPELLCHECK_WORD_ADDED,
|
| + Source<SpellCheckHost>(this), NotificationService::NoDetails());
|
| +}
|
| +
|
| +void SpellCheckHost::Initialize() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
|
| +
|
| + if (!observer_)
|
| + return;
|
| +
|
| + // We set |auto_close| to false because we don't want IPC to close the fd.
|
| + // We will close it manually in the destructor.
|
| + fd_ = base::FileDescriptor(open(bdict_file_.value().c_str(), O_RDONLY),
|
| + false);
|
| +
|
| + // File didn't exist. Download it.
|
| + if (fd_.fd == -1 && !tried_to_download_) {
|
| + DownloadDictionary();
|
| + return;
|
| + }
|
| +
|
| + if (fd_.fd != -1) {
|
| + // Load custom dictionary.
|
| + std::string contents;
|
| + file_util::ReadFileToString(custom_dictionary_file_, &contents);
|
| + std::vector<std::string> list_of_words;
|
| + SplitString(contents, '\n', &list_of_words);
|
| + for (size_t i = 0; i < list_of_words.size(); ++i)
|
| + custom_words_.push_back(list_of_words[i]);
|
| + }
|
| +
|
| + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(this,
|
| + &SpellCheckHost::InformObserverOfInitialization));
|
| +}
|
| +
|
| +void SpellCheckHost::InformObserverOfInitialization() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| +
|
| + if (observer_)
|
| + observer_->SpellCheckHostInitialized();
|
| +}
|
| +
|
| +void SpellCheckHost::DownloadDictionary() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
|
| +
|
| + // Determine URL of file to download.
|
| + static const char kDownloadServerUrl[] =
|
| + "http://cache.pack.google.com/edgedl/chrome/dict/";
|
| + GURL url = GURL(std::string(kDownloadServerUrl) + WideToUTF8(
|
| + l10n_util::ToLower(bdict_file_.BaseName().ToWStringHack())));
|
| + fetcher_.reset(new URLFetcher(url, URLFetcher::GET, this));
|
| + fetcher_->set_request_context(request_context_getter_);
|
| + tried_to_download_ = true;
|
| + fetcher_->Start();
|
| +}
|
| +
|
| +void SpellCheckHost::WriteWordToCustomDictionary(const std::string& word) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
|
| +
|
| + // Stored in UTF-8.
|
| + std::string word_to_add(word + "\n");
|
| + FILE* f = file_util::OpenFile(custom_dictionary_file_, "a+");
|
| + if (f != NULL)
|
| + fputs(word_to_add.c_str(), f);
|
| + file_util::CloseFile(f);
|
| +}
|
| +
|
| +void SpellCheckHost::OnURLFetchComplete(const URLFetcher* source,
|
| + const GURL& url,
|
| + const URLRequestStatus& status,
|
| + int response_code,
|
| + const ResponseCookies& cookies,
|
| + const std::string& data) {
|
| + DCHECK(source);
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
|
| +
|
| + if ((response_code / 100) != 2) {
|
| + // Initialize will not try to download the file a second time.
|
| + LOG(ERROR) << "Failure to download dictionary.";
|
| + Initialize();
|
| + return;
|
| + }
|
| +
|
| + // Basic sanity check on the dictionary.
|
| + // There's the small chance that we might see a 200 status code for a body
|
| + // that represents some form of failure.
|
| + if (data.size() < 4 || data[0] != 'B' || data[1] != 'D' || data[2] != 'i' ||
|
| + data[3] != 'c') {
|
| + LOG(ERROR) << "Failure to download dictionary.";
|
| + Initialize();
|
| + return;
|
| + }
|
| +
|
| + size_t bytes_written =
|
| + file_util::WriteFile(bdict_file_, data.data(), data.length());
|
| + if (bytes_written != data.length()) {
|
| + LOG(ERROR) << "Failure to save dictionary.";
|
| + // To avoid trying to load a partially saved dictionary, shortcut the
|
| + // Initialize() call.
|
| + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(this,
|
| + &SpellCheckHost::InformObserverOfInitialization));
|
| + return;
|
| + }
|
| +
|
| + Initialize();
|
| +}
|
|
|
| Property changes on: chrome/browser/spellcheck_host.cc
|
| ___________________________________________________________________
|
| Added: svn:mergeinfo
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|