OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "chrome/renderer/spellchecker/hunspell_engine.h" | |
6 | |
7 #include <stddef.h> | |
8 #include <algorithm> | |
9 #include <iterator> | |
10 #include <utility> | |
11 | |
12 #include "base/files/memory_mapped_file.h" | |
13 #include "base/time/time.h" | |
14 #include "chrome/common/spellcheck_common.h" | |
15 #include "chrome/common/spellcheck_messages.h" | |
16 #include "content/public/renderer/render_thread.h" | |
17 #include "third_party/hunspell/src/hunspell/hunspell.hxx" | |
18 | |
19 using content::RenderThread; | |
20 | |
21 namespace { | |
22 // Maximum length of words we actually check. | |
23 // 64 is the observed limits for OSX system checker. | |
24 const size_t kMaxCheckedLen = 64; | |
25 | |
26 // Maximum length of words we provide suggestions for. | |
27 // 24 is the observed limits for OSX system checker. | |
28 const size_t kMaxSuggestLen = 24; | |
29 | |
30 static_assert(kMaxCheckedLen <= size_t(MAXWORDLEN), | |
31 "MaxCheckedLen too long"); | |
32 static_assert(kMaxSuggestLen <= kMaxCheckedLen, | |
33 "MaxSuggestLen too long"); | |
34 } // namespace | |
35 | |
36 #if !defined(USE_BROWSER_SPELLCHECKER) | |
37 SpellingEngine* CreateNativeSpellingEngine() { | |
38 return new HunspellEngine(); | |
39 } | |
40 #endif | |
41 | |
42 HunspellEngine::HunspellEngine() | |
43 : hunspell_enabled_(false), | |
44 initialized_(false), | |
45 dictionary_requested_(false) { | |
46 // Wait till we check the first word before doing any initializing. | |
47 } | |
48 | |
49 HunspellEngine::~HunspellEngine() { | |
50 } | |
51 | |
52 void HunspellEngine::Init(base::File file) { | |
53 initialized_ = true; | |
54 hunspell_.reset(); | |
55 bdict_file_.reset(); | |
56 file_ = std::move(file); | |
57 hunspell_enabled_ = file_.IsValid(); | |
58 // Delay the actual initialization of hunspell until it is needed. | |
59 } | |
60 | |
61 void HunspellEngine::InitializeHunspell() { | |
62 if (hunspell_.get()) | |
63 return; | |
64 | |
65 bdict_file_.reset(new base::MemoryMappedFile); | |
66 | |
67 if (bdict_file_->Initialize(std::move(file_))) { | |
68 hunspell_.reset(new Hunspell(bdict_file_->data(), bdict_file_->length())); | |
69 } else { | |
70 NOTREACHED() << "Could not mmap spellchecker dictionary."; | |
71 } | |
72 } | |
73 | |
74 bool HunspellEngine::CheckSpelling(const base::string16& word_to_check, | |
75 int tag) { | |
76 // Assume all words that cannot be checked are valid. Since Chrome can't | |
77 // offer suggestions on them, either, there's no point in flagging them to | |
78 // the user. | |
79 bool word_correct = true; | |
80 std::string word_to_check_utf8(base::UTF16ToUTF8(word_to_check)); | |
81 | |
82 // Limit the size of checked words. | |
83 if (word_to_check_utf8.length() <= kMaxCheckedLen) { | |
84 // If |hunspell_| is NULL here, an error has occurred, but it's better | |
85 // to check rather than crash. | |
86 if (hunspell_.get()) { | |
87 // |hunspell_->spell| returns 0 if the word is misspelled. | |
88 word_correct = (hunspell_->spell(word_to_check_utf8.c_str()) != 0); | |
89 } | |
90 } | |
91 | |
92 return word_correct; | |
93 } | |
94 | |
95 void HunspellEngine::FillSuggestionList( | |
96 const base::string16& wrong_word, | |
97 std::vector<base::string16>* optional_suggestions) { | |
98 std::string wrong_word_utf8(base::UTF16ToUTF8(wrong_word)); | |
99 if (wrong_word_utf8.length() > kMaxSuggestLen) | |
100 return; | |
101 | |
102 // If |hunspell_| is NULL here, an error has occurred, but it's better | |
103 // to check rather than crash. | |
104 // TODO(groby): Technically, it's not. We should track down the issue. | |
105 if (!hunspell_.get()) | |
106 return; | |
107 | |
108 char** suggestions = NULL; | |
109 int number_of_suggestions = | |
110 hunspell_->suggest(&suggestions, wrong_word_utf8.c_str()); | |
111 | |
112 // Populate the vector of WideStrings. | |
113 for (int i = 0; i < number_of_suggestions; ++i) { | |
114 if (i < chrome::spellcheck_common::kMaxSuggestions) | |
115 optional_suggestions->push_back(base::UTF8ToUTF16(suggestions[i])); | |
116 free(suggestions[i]); | |
117 } | |
118 if (suggestions != NULL) | |
119 free(suggestions); | |
120 } | |
121 | |
122 bool HunspellEngine::InitializeIfNeeded() { | |
123 if (!initialized_ && !dictionary_requested_) { | |
124 // RenderThread will not exist in test. | |
125 if (RenderThread::Get()) | |
126 RenderThread::Get()->Send(new SpellCheckHostMsg_RequestDictionary); | |
127 dictionary_requested_ = true; | |
128 return true; | |
129 } | |
130 | |
131 // Don't initialize if hunspell is disabled. | |
132 if (file_.IsValid()) | |
133 InitializeHunspell(); | |
134 | |
135 return !initialized_; | |
136 } | |
137 | |
138 bool HunspellEngine::IsEnabled() { | |
139 return hunspell_enabled_; | |
140 } | |
OLD | NEW |