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