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"
10 #include "base/files/memory_mapped_file.h"
11 #include "base/metrics/histogram.h"
12 #include "base/time/time.h"
13 #include "chrome/common/spellcheck_common.h"
14 #include "chrome/common/spellcheck_messages.h"
15 #include "content/public/renderer/render_thread.h"
16 #include "third_party/hunspell/src/hunspell/hunspell.hxx"
18 using base::TimeTicks
;
19 using content::RenderThread
;
22 // Maximum length of words we actually check.
23 // 64 is the observed limits for OSX system checker.
24 const size_t kMaxCheckedLen
= 64;
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;
30 COMPILE_ASSERT(kMaxCheckedLen
<= size_t(MAXWORDLEN
), MaxCheckedLen_too_long
);
31 COMPILE_ASSERT(kMaxSuggestLen
<= kMaxCheckedLen
, MaxSuggestLen_too_long
);
34 #if !defined(OS_MACOSX)
35 SpellingEngine
* CreateNativeSpellingEngine() {
36 return new HunspellEngine();
40 HunspellEngine::HunspellEngine()
41 : file_(base::kInvalidPlatformFileValue
),
43 dictionary_requested_(false) {
44 // Wait till we check the first word before doing any initializing.
47 HunspellEngine::~HunspellEngine() {
50 void HunspellEngine::Init(base::PlatformFile file
) {
55 // Delay the actual initialization of hunspell until it is needed.
58 void HunspellEngine::InitializeHunspell() {
62 bdict_file_
.reset(new base::MemoryMappedFile
);
64 // TODO(rvargas): This object should not keep file_ after passing it to
66 if (bdict_file_
->Initialize(base::File(file_
))) {
67 TimeTicks debug_start_time
= base::Histogram::DebugNow();
70 new Hunspell(bdict_file_
->data(), bdict_file_
->length()));
72 DHISTOGRAM_TIMES("Spellcheck.InitTime",
73 base::Histogram::DebugNow() - debug_start_time
);
75 NOTREACHED() << "Could not mmap spellchecker dictionary.";
79 bool HunspellEngine::CheckSpelling(const base::string16
& word_to_check
,
81 // Assume all words that cannot be checked are valid. Since Chrome can't
82 // offer suggestions on them, either, there's no point in flagging them to
84 bool word_correct
= true;
85 std::string
word_to_check_utf8(base::UTF16ToUTF8(word_to_check
));
87 // Limit the size of checked words.
88 if (word_to_check_utf8
.length() <= kMaxCheckedLen
) {
89 // If |hunspell_| is NULL here, an error has occurred, but it's better
90 // to check rather than crash.
91 if (hunspell_
.get()) {
92 // |hunspell_->spell| returns 0 if the word is misspelled.
93 word_correct
= (hunspell_
->spell(word_to_check_utf8
.c_str()) != 0);
100 void HunspellEngine::FillSuggestionList(
101 const base::string16
& wrong_word
,
102 std::vector
<base::string16
>* optional_suggestions
) {
103 std::string
wrong_word_utf8(base::UTF16ToUTF8(wrong_word
));
104 if (wrong_word_utf8
.length() > kMaxSuggestLen
)
107 // If |hunspell_| is NULL here, an error has occurred, but it's better
108 // to check rather than crash.
109 // TODO(groby): Technically, it's not. We should track down the issue.
110 if (!hunspell_
.get())
113 char** suggestions
= NULL
;
114 int number_of_suggestions
=
115 hunspell_
->suggest(&suggestions
, wrong_word_utf8
.c_str());
117 // Populate the vector of WideStrings.
118 for (int i
= 0; i
< number_of_suggestions
; ++i
) {
119 if (i
< chrome::spellcheck_common::kMaxSuggestions
)
120 optional_suggestions
->push_back(base::UTF8ToUTF16(suggestions
[i
]));
121 free(suggestions
[i
]);
123 if (suggestions
!= NULL
)
127 bool HunspellEngine::InitializeIfNeeded() {
128 if (!initialized_
&& !dictionary_requested_
) {
129 // RenderThread will not exist in test.
130 if (RenderThread::Get())
131 RenderThread::Get()->Send(new SpellCheckHostMsg_RequestDictionary
);
132 dictionary_requested_
= true;
136 // Don't initialize if hunspell is disabled.
137 if (file_
!= base::kInvalidPlatformFileValue
)
138 InitializeHunspell();
140 return !initialized_
;
143 bool HunspellEngine::IsEnabled() {
144 return file_
!= base::kInvalidPlatformFileValue
;