spellcheck_hunspell_dictionary.cc revision 9ab5563a3196760eb381d102cbb2bc0f7abc6a50
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)
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Close the platform file.
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CloseDictionary(base::PlatformFile file) {
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::ClosePlatformFile(file);
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Dictionary file information to be passed between the FILE and UI threads.
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)struct DictionaryFile {
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DictionaryFile() : descriptor(base::kInvalidPlatformFileValue) {
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ~DictionaryFile() {
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (descriptor != base::kInvalidPlatformFileValue) {
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserThread::PostTask(
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          BrowserThread::FILE,
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          FROM_HERE,
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          base::Bind(&CloseDictionary, descriptor));
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      descriptor = base::kInvalidPlatformFileValue;
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The desired location of the dictionary file, whether or not it exists.
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath path;
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The file descriptor/handle for the dictionary file.
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::PlatformFile descriptor;
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(DictionaryFile);
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Figures out the location for the dictionary, verifies its contents, and opens
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// it. The default_dictionary_file can either come from the standard list of
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// hunspell dictionaries (determined in InitializeDictionaryLocation), or it
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// can be passed in via an extension. In either case, the file is checked for
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// existence so that it's not re-downloaded.
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// For systemwide installations on Windows, the default directory may not
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// have permissions for download. In that case, the alternate directory for
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// download is chrome::DIR_USER_DATA.
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Returns a scoped pointer to avoid leaking the file descriptor if the caller
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// has been destroyed.
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)scoped_ptr<DictionaryFile> OpenDictionaryFile(
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    scoped_ptr<DictionaryFile> file) {
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if defined(OS_WIN)
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Check if the dictionary exists in the fallback location. If so, use it
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // rather than downloading anew.
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath user_dir;
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  PathService::Get(chrome::DIR_USER_DATA, &user_dir);
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath fallback = user_dir.Append(file->path.BaseName());
837dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (!base::PathExists(file->path) && base::PathExists(fallback))
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    file->path = fallback;
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Read the dictionary file and scan its data to check for corruption. The
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // scoping closes the memory-mapped file before it is opened or deleted.
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool bdict_is_valid;
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  {
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::MemoryMappedFile map;
927dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    bdict_is_valid = base::PathExists(file->path) &&
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        map.Initialize(file->path) &&
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        hunspell::BDict::Verify(reinterpret_cast<const char*>(map.data()),
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                map.length());
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (bdict_is_valid) {
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    file->descriptor = base::CreatePlatformFile(
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        file->path,
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::PLATFORM_FILE_READ | base::PLATFORM_FILE_OPEN,
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        NULL,
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        NULL);
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
1047dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    base::DeleteFile(file->path, false);
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return file.Pass();
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Gets the default location for the dictionary file. The default place where
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// the spellcheck dictionary resides is chrome::DIR_APP_DICTIONARIES.
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Returns a scoped pointer to avoid leaking the file descriptor if the caller
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// has been destroyed.
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)scoped_ptr<DictionaryFile> InitializeDictionaryLocation(
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const std::string& language) {
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  scoped_ptr<DictionaryFile> file(new DictionaryFile);
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Initialize the BDICT path. Initialization should be in the FILE thread
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // because it checks if there is a "Dictionaries" directory and create it.
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::FilePath dict_dir;
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  PathService::Get(chrome::DIR_APP_DICTIONARIES, &dict_dir);
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  file->path = chrome::spellcheck_common::GetVersionedFileName(
124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      language, dict_dir);
125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return OpenDictionaryFile(file.Pass());
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Saves |data| to file at |path|. Returns true on successful save, otherwise
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// returns false.
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool SaveDictionaryData(scoped_ptr<std::string> data,
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        const base::FilePath& path) {
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  size_t bytes_written =
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      file_util::WriteFile(path, data->data(), data->length());
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (bytes_written != data->length()) {
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bool success = false;
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if defined(OS_WIN)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::FilePath dict_dir;
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    PathService::Get(chrome::DIR_USER_DATA, &dict_dir);
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::FilePath fallback_file_path =
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        dict_dir.Append(path.BaseName());
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bytes_written =
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        file_util::WriteFile(fallback_file_path, data->data(), data->length());
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (bytes_written == data->length())
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      success = true;
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!success) {
1517dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      base::DeleteFile(path, false);
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return false;
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return true;
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SpellcheckHunspellDictionary::SpellcheckHunspellDictionary(
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& language,
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    net::URLRequestContextGetter* request_context_getter,
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SpellcheckService* spellcheck_service)
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : language_(language),
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      use_platform_spellchecker_(false),
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      request_context_getter_(request_context_getter),
168c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_ptr_factory_(this),
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      spellcheck_service_(spellcheck_service),
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      download_status_(DOWNLOAD_NONE),
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      dictionary_file_(new DictionaryFile) {
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SpellcheckHunspellDictionary::~SpellcheckHunspellDictionary() {
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpellcheckHunspellDictionary::Load() {
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_MACOSX)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (spellcheck_mac::SpellCheckerAvailable() &&
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      spellcheck_mac::PlatformSupportsLanguage(language_)) {
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    use_platform_spellchecker_ = true;
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    spellcheck_mac::SetLanguage(language_);
18590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    base::MessageLoop::current()->PostTask(FROM_HERE,
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::Bind(
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            &SpellcheckHunspellDictionary::InformListenersOfInitialization,
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            weak_ptr_factory_.GetWeakPtr()));
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif  // OS_MACOSX
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  BrowserThread::PostTaskAndReplyWithResult(
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserThread::FILE,
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FROM_HERE,
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&InitializeDictionaryLocation, language_),
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          &SpellcheckHunspellDictionary::InitializeDictionaryLocationComplete,
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          weak_ptr_factory_.GetWeakPtr()));
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckHunspellDictionary::RetryDownloadDictionary(
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      net::URLRequestContextGetter* request_context_getter) {
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  request_context_getter_ = request_context_getter;
206c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DownloadDictionary(GetDictionaryURL());
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool SpellcheckHunspellDictionary::IsReady() const {
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return GetDictionaryFile() !=
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::kInvalidPlatformFileValue || IsUsingPlatformChecker();
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const base::PlatformFile&
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SpellcheckHunspellDictionary::GetDictionaryFile() const {
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return dictionary_file_->descriptor;
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const std::string& SpellcheckHunspellDictionary::GetLanguage() const {
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return language_;
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool SpellcheckHunspellDictionary::IsUsingPlatformChecker() const {
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return use_platform_spellchecker_;
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckHunspellDictionary::AddObserver(Observer* observer) {
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  observers_.AddObserver(observer);
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckHunspellDictionary::RemoveObserver(Observer* observer) {
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  observers_.RemoveObserver(observer);
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool SpellcheckHunspellDictionary::IsDownloadInProgress() {
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return download_status_ == DOWNLOAD_IN_PROGRESS;
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool SpellcheckHunspellDictionary::IsDownloadFailure() {
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return download_status_ == DOWNLOAD_FAILED;
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpellcheckHunspellDictionary::OnURLFetchComplete(
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const net::URLFetcher* source) {
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(source);
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<net::URLFetcher> fetcher_destructor(fetcher_.release());
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((source->GetResponseCode() / 100) != 2) {
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Initialize will not try to download the file a second time.
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    InformListenersOfDownloadFailure();
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Basic sanity check on the dictionary. There's a small chance of 200 status
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // code for a body that represents some form of failure.
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<std::string> data(new std::string);
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  source->GetResponseAsString(data.get());
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (data->size() < 4 || data->compare(0, 4, "BDic") != 0) {
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    InformListenersOfDownloadFailure();
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // To prevent corrupted dictionary data from causing a renderer crash, scan
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the dictionary data and verify it is sane before save it to a file.
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(rlp): Adding metrics to RecordDictionaryCorruptionStats
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!hunspell::BDict::Verify(data->data(), data->size())) {
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Let PostTaskAndReply caller send to InformListenersOfInitialization
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // through SaveDictionaryDataComplete().
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SaveDictionaryDataComplete(false);
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  BrowserThread::PostTaskAndReplyWithResult<bool>(
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserThread::FILE,
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FROM_HERE,
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&SaveDictionaryData,
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 base::Passed(&data),
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 dictionary_file_->path),
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&SpellcheckHunspellDictionary::SaveDictionaryDataComplete,
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 weak_ptr_factory_.GetWeakPtr()));
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
286c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)GURL SpellcheckHunspellDictionary::GetDictionaryURL() {
287c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  static const char kDownloadServerUrl[] =
288c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      "http://cache.pack.google.com/edgedl/chrome/dict/";
289c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::string bdict_file = dictionary_file_->path.BaseName().MaybeAsASCII();
290c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
291c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(!bdict_file.empty());
292c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
293c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return GURL(std::string(kDownloadServerUrl) +
294c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              StringToLowerASCII(bdict_file));
295c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
296c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
297c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void SpellcheckHunspellDictionary::DownloadDictionary(GURL url) {
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(request_context_getter_);
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  download_status_ = DOWNLOAD_IN_PROGRESS;
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FOR_EACH_OBSERVER(Observer, observers_, OnHunspellDictionaryDownloadBegin());
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
304868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  fetcher_.reset(net::URLFetcher::Create(url, net::URLFetcher::GET, this));
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fetcher_->SetRequestContext(request_context_getter_);
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fetcher_->SetLoadFlags(
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES);
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fetcher_->Start();
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Attempt downloading the dictionary only once.
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_context_getter_ = NULL;
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckHunspellDictionary::InitializeDictionaryLocationComplete(
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_ptr<DictionaryFile> file) {
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  dictionary_file_ = file.Pass();
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (dictionary_file_->descriptor == base::kInvalidPlatformFileValue) {
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Notify browser tests that this dictionary is corrupted. Skip downloading
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // the dictionary in browser tests.
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // TODO(rouslan): Remove this test-only case.
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (spellcheck_service_->SignalStatusEvent(
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          SpellcheckService::BDICT_CORRUPTED)) {
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      request_context_getter_ = NULL;
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (request_context_getter_) {
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Download from the UI thread to check that |request_context_getter_| is
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // still valid.
331c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DownloadDictionary(GetDictionaryURL());
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  InformListenersOfInitialization();
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckHunspellDictionary::SaveDictionaryDataComplete(
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bool dictionary_saved) {
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (dictionary_saved) {
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    download_status_ = DOWNLOAD_NONE;
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    FOR_EACH_OBSERVER(Observer,
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      observers_,
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      OnHunspellDictionaryDownloadSuccess());
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Load();
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    InformListenersOfDownloadFailure();
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    InformListenersOfInitialization();
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckHunspellDictionary::InformListenersOfInitialization() {
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FOR_EACH_OBSERVER(Observer, observers_, OnHunspellDictionaryInitialized());
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellcheckHunspellDictionary::InformListenersOfDownloadFailure() {
3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  download_status_ = DOWNLOAD_FAILED;
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FOR_EACH_OBSERVER(Observer,
3622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    observers_,
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    OnHunspellDictionaryDownloadFailure());
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
365