| 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 |