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