Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / renderer / spellchecker / hunspell_engine.cc
blob34934fc010668804ee2e5fc65d017185aeba7d4e
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/renderer/spellchecker/hunspell_engine.h"
7 #include <algorithm>
8 #include <iterator>
10 #include "base/files/memory_mapped_file.h"
11 #include "base/time/time.h"
12 #include "chrome/common/spellcheck_common.h"
13 #include "chrome/common/spellcheck_messages.h"
14 #include "content/public/renderer/render_thread.h"
15 #include "third_party/hunspell/src/hunspell/hunspell.hxx"
17 using content::RenderThread;
19 namespace {
20 // Maximum length of words we actually check.
21 // 64 is the observed limits for OSX system checker.
22 const size_t kMaxCheckedLen = 64;
24 // Maximum length of words we provide suggestions for.
25 // 24 is the observed limits for OSX system checker.
26 const size_t kMaxSuggestLen = 24;
28 static_assert(kMaxCheckedLen <= size_t(MAXWORDLEN),
29 "MaxCheckedLen too long");
30 static_assert(kMaxSuggestLen <= kMaxCheckedLen,
31 "MaxSuggestLen too long");
32 } // namespace
34 #if !defined(USE_BROWSER_SPELLCHECKER)
35 SpellingEngine* CreateNativeSpellingEngine() {
36 return new HunspellEngine();
38 #endif
40 HunspellEngine::HunspellEngine()
41 : hunspell_enabled_(false),
42 initialized_(false),
43 dictionary_requested_(false) {
44 // Wait till we check the first word before doing any initializing.
47 HunspellEngine::~HunspellEngine() {
50 void HunspellEngine::Init(base::File file) {
51 initialized_ = true;
52 hunspell_.reset();
53 bdict_file_.reset();
54 file_ = file.Pass();
55 hunspell_enabled_ = file_.IsValid();
56 // Delay the actual initialization of hunspell until it is needed.
59 void HunspellEngine::InitializeHunspell() {
60 if (hunspell_.get())
61 return;
63 bdict_file_.reset(new base::MemoryMappedFile);
65 if (bdict_file_->Initialize(file_.Pass())) {
66 hunspell_.reset(new Hunspell(bdict_file_->data(), bdict_file_->length()));
67 } else {
68 NOTREACHED() << "Could not mmap spellchecker dictionary.";
72 bool HunspellEngine::CheckSpelling(const base::string16& word_to_check,
73 int tag) {
74 // Assume all words that cannot be checked are valid. Since Chrome can't
75 // offer suggestions on them, either, there's no point in flagging them to
76 // the user.
77 bool word_correct = true;
78 std::string word_to_check_utf8(base::UTF16ToUTF8(word_to_check));
80 // Limit the size of checked words.
81 if (word_to_check_utf8.length() <= kMaxCheckedLen) {
82 // If |hunspell_| is NULL here, an error has occurred, but it's better
83 // to check rather than crash.
84 if (hunspell_.get()) {
85 // |hunspell_->spell| returns 0 if the word is misspelled.
86 word_correct = (hunspell_->spell(word_to_check_utf8.c_str()) != 0);
90 return word_correct;
93 void HunspellEngine::FillSuggestionList(
94 const base::string16& wrong_word,
95 std::vector<base::string16>* optional_suggestions) {
96 std::string wrong_word_utf8(base::UTF16ToUTF8(wrong_word));
97 if (wrong_word_utf8.length() > kMaxSuggestLen)
98 return;
100 // If |hunspell_| is NULL here, an error has occurred, but it's better
101 // to check rather than crash.
102 // TODO(groby): Technically, it's not. We should track down the issue.
103 if (!hunspell_.get())
104 return;
106 char** suggestions = NULL;
107 int number_of_suggestions =
108 hunspell_->suggest(&suggestions, wrong_word_utf8.c_str());
110 // Populate the vector of WideStrings.
111 for (int i = 0; i < number_of_suggestions; ++i) {
112 if (i < chrome::spellcheck_common::kMaxSuggestions)
113 optional_suggestions->push_back(base::UTF8ToUTF16(suggestions[i]));
114 free(suggestions[i]);
116 if (suggestions != NULL)
117 free(suggestions);
120 bool HunspellEngine::InitializeIfNeeded() {
121 if (!initialized_ && !dictionary_requested_) {
122 // RenderThread will not exist in test.
123 if (RenderThread::Get())
124 RenderThread::Get()->Send(new SpellCheckHostMsg_RequestDictionary);
125 dictionary_requested_ = true;
126 return true;
129 // Don't initialize if hunspell is disabled.
130 if (file_.IsValid())
131 InitializeHunspell();
133 return !initialized_;
136 bool HunspellEngine::IsEnabled() {
137 return hunspell_enabled_;