spellcheck_hunspell_dictionary.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/file_util.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/memory_mapped_file.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
109ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/path_service.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/spellchecker/spellcheck_platform_mac.h"
13eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "chrome/browser/spellchecker/spellcheck_service.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_paths.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/spellcheck_common.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/spellcheck_messages.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_process_host.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/load_flags.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_fetcher.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_context_getter.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/hunspell/google/bdict.h"
23eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "url/gurl.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread;
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
29c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// Close the file.
30c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochvoid CloseDictionary(base::File file) {
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
32c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  file.Close();
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Saves |data| to file at |path|. Returns true on successful save, otherwise
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// returns false.
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool SaveDictionaryData(scoped_ptr<std::string> data,
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        const base::FilePath& path) {
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  size_t bytes_written =
42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      base::WriteFile(path, data->data(), data->length());
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (bytes_written != data->length()) {
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bool success = false;
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if defined(OS_WIN)
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::FilePath dict_dir;
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    PathService::Get(chrome::DIR_USER_DATA, &dict_dir);
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::FilePath fallback_file_path =
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        dict_dir.Append(path.BaseName());
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bytes_written =
51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        base::WriteFile(fallback_file_path, data->data(), data->length());
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (bytes_written == data->length())
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      success = true;
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!success) {
577dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      base::DeleteFile(path, false);
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return false;
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return true;
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
67c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen MurdochSpellcheckHunspellDictionary::DictionaryFile::DictionaryFile() {
68c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch }
69c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
70c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch SpellcheckHunspellDictionary::DictionaryFile::~DictionaryFile() {
71c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (file.IsValid()) {
72c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    BrowserThread::PostTask(
73c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        BrowserThread::FILE,
74c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        FROM_HERE,
75c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        base::Bind(&CloseDictionary, Passed(&file)));
76c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }
77c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
78c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
79c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen MurdochSpellcheckHunspellDictionary::DictionaryFile::DictionaryFile(RValue other)
80c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    : path(other.object->path),
81c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      file(other.object->file.Pass()) {
82c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
83c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
84c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen MurdochSpellcheckHunspellDictionary::DictionaryFile&
85c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen MurdochSpellcheckHunspellDictionary::DictionaryFile::operator=(RValue other) {
86c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (this != other.object) {
87c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    path = other.object->path;
88c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    file = other.object->file.Pass();
89c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }
90c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return *this;
91c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
92c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SpellcheckHunspellDictionary::SpellcheckHunspellDictionary(
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& language,
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    net::URLRequestContextGetter* request_context_getter,
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SpellcheckService* spellcheck_service)
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : language_(language),
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      use_platform_spellchecker_(false),
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      request_context_getter_(request_context_getter),
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      spellcheck_service_(spellcheck_service),
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      download_status_(DOWNLOAD_NONE),
1024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      weak_ptr_factory_(this) {
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SpellcheckHunspellDictionary::~SpellcheckHunspellDictionary() {
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpellcheckHunspellDictionary::Load() {
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_MACOSX)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (spellcheck_mac::SpellCheckerAvailable() &&
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      spellcheck_mac::PlatformSupportsLanguage(language_)) {
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    use_platform_spellchecker_ = true;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    spellcheck_mac::SetLanguage(language_);
11690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    base::MessageLoop::current()->PostTask(FROM_HERE,
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::Bind(
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            &SpellcheckHunspellDictionary::InformListenersOfInitialization,
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            weak_ptr_factory_.GetWeakPtr()));
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif  // OS_MACOSX
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  BrowserThread::PostTaskAndReplyWithResult(
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserThread::FILE,
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FROM_HERE,
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&InitializeDictionaryLocation, language_),
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          &SpellcheckHunspellDictionary::InitializeDictionaryLocationComplete,
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          weak_ptr_factory_.GetWeakPtr()));
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckHunspellDictionary::RetryDownloadDictionary(
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      net::URLRequestContextGetter* request_context_getter) {
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  request_context_getter_ = request_context_getter;
137c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DownloadDictionary(GetDictionaryURL());
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool SpellcheckHunspellDictionary::IsReady() const {
14146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  return GetDictionaryFile().IsValid() || IsUsingPlatformChecker();
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)const base::File& SpellcheckHunspellDictionary::GetDictionaryFile() const {
14546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  return dictionary_file_.file;
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const std::string& SpellcheckHunspellDictionary::GetLanguage() const {
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return language_;
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool SpellcheckHunspellDictionary::IsUsingPlatformChecker() const {
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return use_platform_spellchecker_;
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckHunspellDictionary::AddObserver(Observer* observer) {
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  observers_.AddObserver(observer);
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckHunspellDictionary::RemoveObserver(Observer* observer) {
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  observers_.RemoveObserver(observer);
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool SpellcheckHunspellDictionary::IsDownloadInProgress() {
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return download_status_ == DOWNLOAD_IN_PROGRESS;
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool SpellcheckHunspellDictionary::IsDownloadFailure() {
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return download_status_ == DOWNLOAD_FAILED;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpellcheckHunspellDictionary::OnURLFetchComplete(
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const net::URLFetcher* source) {
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(source);
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<net::URLFetcher> fetcher_destructor(fetcher_.release());
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((source->GetResponseCode() / 100) != 2) {
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Initialize will not try to download the file a second time.
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    InformListenersOfDownloadFailure();
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Basic sanity check on the dictionary. There's a small chance of 200 status
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // code for a body that represents some form of failure.
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<std::string> data(new std::string);
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  source->GetResponseAsString(data.get());
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (data->size() < 4 || data->compare(0, 4, "BDic") != 0) {
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    InformListenersOfDownloadFailure();
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // To prevent corrupted dictionary data from causing a renderer crash, scan
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the dictionary data and verify it is sane before save it to a file.
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(rlp): Adding metrics to RecordDictionaryCorruptionStats
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!hunspell::BDict::Verify(data->data(), data->size())) {
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Let PostTaskAndReply caller send to InformListenersOfInitialization
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // through SaveDictionaryDataComplete().
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SaveDictionaryDataComplete(false);
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  BrowserThread::PostTaskAndReplyWithResult<bool>(
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserThread::FILE,
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FROM_HERE,
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&SaveDictionaryData,
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 base::Passed(&data),
210c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                 dictionary_file_.path),
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&SpellcheckHunspellDictionary::SaveDictionaryDataComplete,
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 weak_ptr_factory_.GetWeakPtr()));
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)GURL SpellcheckHunspellDictionary::GetDictionaryURL() {
216c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  static const char kDownloadServerUrl[] =
217c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      "http://cache.pack.google.com/edgedl/chrome/dict/";
218c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  std::string bdict_file = dictionary_file_.path.BaseName().MaybeAsASCII();
219c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
220c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(!bdict_file.empty());
221c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
222c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return GURL(std::string(kDownloadServerUrl) +
223c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              StringToLowerASCII(bdict_file));
224c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
225c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
226c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void SpellcheckHunspellDictionary::DownloadDictionary(GURL url) {
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(request_context_getter_);
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  download_status_ = DOWNLOAD_IN_PROGRESS;
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FOR_EACH_OBSERVER(Observer, observers_, OnHunspellDictionaryDownloadBegin());
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
233868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  fetcher_.reset(net::URLFetcher::Create(url, net::URLFetcher::GET, this));
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fetcher_->SetRequestContext(request_context_getter_);
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fetcher_->SetLoadFlags(
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES);
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fetcher_->Start();
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Attempt downloading the dictionary only once.
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_context_getter_ = NULL;
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
242c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// The default_dictionary_file can either come from the standard list of
243c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// hunspell dictionaries (determined in InitializeDictionaryLocation), or it
244c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// can be passed in via an extension. In either case, the file is checked for
245c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// existence so that it's not re-downloaded.
246c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// For systemwide installations on Windows, the default directory may not
247c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// have permissions for download. In that case, the alternate directory for
248c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// download is chrome::DIR_USER_DATA.
249c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen MurdochSpellcheckHunspellDictionary::DictionaryFile
250c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen MurdochSpellcheckHunspellDictionary::OpenDictionaryFile(const base::FilePath& path) {
251c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
252c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  DictionaryFile dictionary;
253c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
254c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#if defined(OS_WIN)
255c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // Check if the dictionary exists in the fallback location. If so, use it
256c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // rather than downloading anew.
257c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  base::FilePath user_dir;
258c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  PathService::Get(chrome::DIR_USER_DATA, &user_dir);
259c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  base::FilePath fallback = user_dir.Append(path.BaseName());
260c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (!base::PathExists(path) && base::PathExists(fallback))
261c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    dictionary.path = fallback;
262c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  else
263c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    dictionary.path = path;
264c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#else
265c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  dictionary.path = path;
266c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#endif
267c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
268c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // Read the dictionary file and scan its data to check for corruption. The
269c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // scoping closes the memory-mapped file before it is opened or deleted.
270c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  bool bdict_is_valid;
271c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  {
272c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    base::MemoryMappedFile map;
273c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    bdict_is_valid =
274c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        base::PathExists(dictionary.path) &&
275c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        map.Initialize(dictionary.path) &&
276c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        hunspell::BDict::Verify(reinterpret_cast<const char*>(map.data()),
277c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                                map.length());
278c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }
279c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (bdict_is_valid) {
280c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    dictionary.file.Initialize(dictionary.path,
281c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                               base::File::FLAG_READ | base::File::FLAG_OPEN);
282c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  } else {
283c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    base::DeleteFile(dictionary.path, false);
284c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  }
285c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
286c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return dictionary.Pass();
287c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
288c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
289c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// The default place where the spellcheck dictionary resides is
290c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch// chrome::DIR_APP_DICTIONARIES.
291c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen MurdochSpellcheckHunspellDictionary::DictionaryFile
292c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen MurdochSpellcheckHunspellDictionary::InitializeDictionaryLocation(
293c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    const std::string& language) {
294c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
295c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
296c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // Initialize the BDICT path. Initialization should be in the FILE thread
297c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // because it checks if there is a "Dictionaries" directory and create it.
298c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  base::FilePath dict_dir;
299c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  PathService::Get(chrome::DIR_APP_DICTIONARIES, &dict_dir);
300c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  base::FilePath dict_path =
301c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      chrome::spellcheck_common::GetVersionedFileName(language, dict_dir);
302c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
303c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  return OpenDictionaryFile(dict_path);
304c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch}
305c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckHunspellDictionary::InitializeDictionaryLocationComplete(
307c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    DictionaryFile file) {
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  dictionary_file_ = file.Pass();
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
311c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (!dictionary_file_.file.IsValid()) {
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Notify browser tests that this dictionary is corrupted. Skip downloading
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // the dictionary in browser tests.
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // TODO(rouslan): Remove this test-only case.
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (spellcheck_service_->SignalStatusEvent(
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          SpellcheckService::BDICT_CORRUPTED)) {
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      request_context_getter_ = NULL;
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (request_context_getter_) {
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Download from the UI thread to check that |request_context_getter_| is
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // still valid.
324c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DownloadDictionary(GetDictionaryURL());
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  InformListenersOfInitialization();
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckHunspellDictionary::SaveDictionaryDataComplete(
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bool dictionary_saved) {
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (dictionary_saved) {
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    download_status_ = DOWNLOAD_NONE;
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    FOR_EACH_OBSERVER(Observer,
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      observers_,
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      OnHunspellDictionaryDownloadSuccess());
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Load();
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    InformListenersOfDownloadFailure();
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    InformListenersOfInitialization();
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckHunspellDictionary::InformListenersOfInitialization() {
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FOR_EACH_OBSERVER(Observer, observers_, OnHunspellDictionaryInitialized());
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckHunspellDictionary::InformListenersOfDownloadFailure() {
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  download_status_ = DOWNLOAD_FAILED;
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FOR_EACH_OBSERVER(Observer,
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    observers_,
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    OnHunspellDictionaryDownloadFailure());
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
358