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) 33 SpellingEngine* CreateNativeSpellingEngine() { 34 return new HunspellEngine(); 35 } 36 #endif 37 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 45 HunspellEngine::~HunspellEngine() { 46 } 47 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 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 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 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 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 134 bool HunspellEngine::IsEnabled() { 135 return hunspell_enabled_; 136 } 137