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/spellcheck.h" 6 7 #include "base/bind.h" 8 #include "base/message_loop/message_loop_proxy.h" 9 #include "base/strings/utf_string_conversions.h" 10 #include "chrome/common/render_messages.h" 11 #include "chrome/common/spellcheck_common.h" 12 #include "chrome/common/spellcheck_messages.h" 13 #include "chrome/common/spellcheck_result.h" 14 #include "chrome/renderer/spellchecker/spellcheck_language.h" 15 #include "chrome/renderer/spellchecker/spellcheck_provider.h" 16 #include "content/public/renderer/render_thread.h" 17 #include "content/public/renderer/render_view.h" 18 #include "content/public/renderer/render_view_visitor.h" 19 #include "third_party/WebKit/public/web/WebTextCheckingCompletion.h" 20 #include "third_party/WebKit/public/web/WebTextCheckingResult.h" 21 #include "third_party/WebKit/public/web/WebView.h" 22 23 using WebKit::WebVector; 24 using WebKit::WebTextCheckingResult; 25 using WebKit::WebTextCheckingType; 26 27 namespace { 28 29 class UpdateSpellcheckEnabled : public content::RenderViewVisitor { 30 public: 31 explicit UpdateSpellcheckEnabled(bool enabled) : enabled_(enabled) {} 32 virtual bool Visit(content::RenderView* render_view) OVERRIDE; 33 34 private: 35 bool enabled_; // New spellcheck-enabled state. 36 DISALLOW_COPY_AND_ASSIGN(UpdateSpellcheckEnabled); 37 }; 38 39 bool UpdateSpellcheckEnabled::Visit(content::RenderView* render_view) { 40 SpellCheckProvider* provider = SpellCheckProvider::Get(render_view); 41 DCHECK(provider); 42 provider->EnableSpellcheck(enabled_); 43 return true; 44 } 45 46 class DocumentMarkersCollector : public content::RenderViewVisitor { 47 public: 48 DocumentMarkersCollector() {} 49 virtual ~DocumentMarkersCollector() {} 50 const std::vector<uint32>& markers() const { return markers_; } 51 virtual bool Visit(content::RenderView* render_view) OVERRIDE; 52 53 private: 54 std::vector<uint32> markers_; 55 DISALLOW_COPY_AND_ASSIGN(DocumentMarkersCollector); 56 }; 57 58 bool DocumentMarkersCollector::Visit(content::RenderView* render_view) { 59 if (!render_view || !render_view->GetWebView()) 60 return true; 61 WebVector<uint32> markers; 62 render_view->GetWebView()->spellingMarkers(&markers); 63 for (size_t i = 0; i < markers.size(); ++i) 64 markers_.push_back(markers[i]); 65 // Visit all render views. 66 return true; 67 } 68 69 } // namespace 70 71 class SpellCheck::SpellcheckRequest { 72 public: 73 SpellcheckRequest(const string16& text, 74 WebKit::WebTextCheckingCompletion* completion) 75 : text_(text), completion_(completion) { 76 DCHECK(completion); 77 } 78 ~SpellcheckRequest() {} 79 80 string16 text() { return text_; } 81 WebKit::WebTextCheckingCompletion* completion() { return completion_; } 82 83 private: 84 string16 text_; // Text to be checked in this task. 85 86 // The interface to send the misspelled ranges to WebKit. 87 WebKit::WebTextCheckingCompletion* completion_; 88 89 DISALLOW_COPY_AND_ASSIGN(SpellcheckRequest); 90 }; 91 92 93 // Initializes SpellCheck object. 94 // spellcheck_enabled_ currently MUST be set to true, due to peculiarities of 95 // the initialization sequence. 96 // Since it defaults to true, newly created SpellCheckProviders will enable 97 // spellchecking. After the first word is typed, the provider requests a check, 98 // which in turn triggers the delayed initialization sequence in SpellCheck. 99 // This does send a message to the browser side, which triggers the creation 100 // of the SpellcheckService. That does create the observer for the preference 101 // responsible for enabling/disabling checking, which allows subsequent changes 102 // to that preference to be sent to all SpellCheckProviders. 103 // Setting |spellcheck_enabled_| to false by default prevents that mechanism, 104 // and as such the SpellCheckProviders will never be notified of different 105 // values. 106 // TODO(groby): Simplify this. 107 SpellCheck::SpellCheck() 108 : auto_spell_correct_turned_on_(false), 109 spellcheck_enabled_(true) { 110 } 111 112 SpellCheck::~SpellCheck() { 113 } 114 115 bool SpellCheck::OnControlMessageReceived(const IPC::Message& message) { 116 bool handled = true; 117 IPC_BEGIN_MESSAGE_MAP(SpellCheck, message) 118 IPC_MESSAGE_HANDLER(SpellCheckMsg_Init, OnInit) 119 IPC_MESSAGE_HANDLER(SpellCheckMsg_CustomDictionaryChanged, 120 OnCustomDictionaryChanged) 121 IPC_MESSAGE_HANDLER(SpellCheckMsg_EnableAutoSpellCorrect, 122 OnEnableAutoSpellCorrect) 123 IPC_MESSAGE_HANDLER(SpellCheckMsg_EnableSpellCheck, OnEnableSpellCheck) 124 IPC_MESSAGE_HANDLER(SpellCheckMsg_RequestDocumentMarkers, 125 OnRequestDocumentMarkers) 126 IPC_MESSAGE_UNHANDLED(handled = false) 127 IPC_END_MESSAGE_MAP() 128 129 return handled; 130 } 131 132 void SpellCheck::OnInit(IPC::PlatformFileForTransit bdict_file, 133 const std::set<std::string>& custom_words, 134 const std::string& language, 135 bool auto_spell_correct) { 136 Init(IPC::PlatformFileForTransitToPlatformFile(bdict_file), 137 custom_words, language); 138 auto_spell_correct_turned_on_ = auto_spell_correct; 139 #if !defined(OS_MACOSX) 140 PostDelayedSpellCheckTask(pending_request_param_.release()); 141 #endif 142 } 143 144 void SpellCheck::OnCustomDictionaryChanged( 145 const std::vector<std::string>& words_added, 146 const std::vector<std::string>& words_removed) { 147 custom_dictionary_.OnCustomDictionaryChanged(words_added, words_removed); 148 } 149 150 void SpellCheck::OnEnableAutoSpellCorrect(bool enable) { 151 auto_spell_correct_turned_on_ = enable; 152 } 153 154 void SpellCheck::OnEnableSpellCheck(bool enable) { 155 spellcheck_enabled_ = enable; 156 UpdateSpellcheckEnabled updater(enable); 157 content::RenderView::ForEach(&updater); 158 } 159 160 void SpellCheck::OnRequestDocumentMarkers() { 161 DocumentMarkersCollector collector; 162 content::RenderView::ForEach(&collector); 163 content::RenderThread::Get()->Send( 164 new SpellCheckHostMsg_RespondDocumentMarkers(collector.markers())); 165 } 166 167 // TODO(groby): Make sure we always have a spelling engine, even before Init() 168 // is called. 169 void SpellCheck::Init(base::PlatformFile file, 170 const std::set<std::string>& custom_words, 171 const std::string& language) { 172 spellcheck_.Init(file, language); 173 custom_dictionary_.Init(custom_words); 174 } 175 176 bool SpellCheck::SpellCheckWord( 177 const char16* in_word, 178 int in_word_len, 179 int tag, 180 int* misspelling_start, 181 int* misspelling_len, 182 std::vector<string16>* optional_suggestions) { 183 DCHECK(in_word_len >= 0); 184 DCHECK(misspelling_start && misspelling_len) << "Out vars must be given."; 185 186 // Do nothing if we need to delay initialization. (Rather than blocking, 187 // report the word as correctly spelled.) 188 if (InitializeIfNeeded()) 189 return true; 190 191 return spellcheck_.SpellCheckWord(in_word, in_word_len, 192 tag, 193 misspelling_start, misspelling_len, 194 optional_suggestions); 195 } 196 197 bool SpellCheck::SpellCheckParagraph( 198 const string16& text, 199 WebVector<WebTextCheckingResult>* results) { 200 #if !defined(OS_MACOSX) 201 // Mac has its own spell checker, so this method will not be used. 202 DCHECK(results); 203 std::vector<WebTextCheckingResult> textcheck_results; 204 size_t length = text.length(); 205 size_t offset = 0; 206 207 // Spellcheck::SpellCheckWord() automatically breaks text into words and 208 // checks the spellings of the extracted words. This function sets the 209 // position and length of the first misspelled word and returns false when 210 // the text includes misspelled words. Therefore, we just repeat calling the 211 // function until it returns true to check the whole text. 212 int misspelling_start = 0; 213 int misspelling_length = 0; 214 while (offset <= length) { 215 if (SpellCheckWord(&text[offset], 216 length - offset, 217 0, 218 &misspelling_start, 219 &misspelling_length, 220 NULL)) { 221 results->assign(textcheck_results); 222 return true; 223 } 224 225 if (!custom_dictionary_.SpellCheckWord( 226 text, misspelling_start + offset, misspelling_length)) { 227 string16 replacement; 228 textcheck_results.push_back(WebTextCheckingResult( 229 WebKit::WebTextCheckingTypeSpelling, 230 misspelling_start + offset, 231 misspelling_length, 232 replacement)); 233 } 234 offset += misspelling_start + misspelling_length; 235 } 236 results->assign(textcheck_results); 237 return false; 238 #else 239 // This function is only invoked for spell checker functionality that runs 240 // on the render thread. OSX builds don't have that. 241 NOTREACHED(); 242 return true; 243 #endif 244 } 245 246 string16 SpellCheck::GetAutoCorrectionWord(const string16& word, int tag) { 247 string16 autocorrect_word; 248 if (!auto_spell_correct_turned_on_) 249 return autocorrect_word; // Return the empty string. 250 251 int word_length = static_cast<int>(word.size()); 252 if (word_length < 2 || 253 word_length > chrome::spellcheck_common::kMaxAutoCorrectWordSize) 254 return autocorrect_word; 255 256 if (InitializeIfNeeded()) 257 return autocorrect_word; 258 259 char16 misspelled_word[ 260 chrome::spellcheck_common::kMaxAutoCorrectWordSize + 1]; 261 const char16* word_char = word.c_str(); 262 for (int i = 0; i <= chrome::spellcheck_common::kMaxAutoCorrectWordSize; 263 ++i) { 264 if (i >= word_length) 265 misspelled_word[i] = 0; 266 else 267 misspelled_word[i] = word_char[i]; 268 } 269 270 // Swap adjacent characters and spellcheck. 271 int misspelling_start, misspelling_len; 272 for (int i = 0; i < word_length - 1; i++) { 273 // Swap. 274 std::swap(misspelled_word[i], misspelled_word[i + 1]); 275 276 // Check spelling. 277 misspelling_start = misspelling_len = 0; 278 SpellCheckWord(misspelled_word, word_length, tag, &misspelling_start, 279 &misspelling_len, NULL); 280 281 // Make decision: if only one swap produced a valid word, then we want to 282 // return it. If we found two or more, we don't do autocorrection. 283 if (misspelling_len == 0) { 284 if (autocorrect_word.empty()) { 285 autocorrect_word.assign(misspelled_word); 286 } else { 287 autocorrect_word.clear(); 288 break; 289 } 290 } 291 292 // Restore the swapped characters. 293 std::swap(misspelled_word[i], misspelled_word[i + 1]); 294 } 295 return autocorrect_word; 296 } 297 298 #if !defined(OS_MACOSX) // OSX uses its own spell checker 299 void SpellCheck::RequestTextChecking( 300 const string16& text, 301 WebKit::WebTextCheckingCompletion* completion) { 302 // Clean up the previous request before starting a new request. 303 if (pending_request_param_.get()) 304 pending_request_param_->completion()->didCancelCheckingText(); 305 306 pending_request_param_.reset(new SpellcheckRequest( 307 text, completion)); 308 // We will check this text after we finish loading the hunspell dictionary. 309 if (InitializeIfNeeded()) 310 return; 311 312 PostDelayedSpellCheckTask(pending_request_param_.release()); 313 } 314 #endif 315 316 bool SpellCheck::InitializeIfNeeded() { 317 return spellcheck_.InitializeIfNeeded(); 318 } 319 320 #if !defined(OS_MACOSX) // OSX doesn't have |pending_request_param_| 321 void SpellCheck::PostDelayedSpellCheckTask(SpellcheckRequest* request) { 322 if (!request) 323 return; 324 325 base::MessageLoopProxy::current()->PostTask(FROM_HERE, 326 base::Bind(&SpellCheck::PerformSpellCheck, 327 AsWeakPtr(), 328 base::Owned(request))); 329 } 330 #endif 331 332 #if !defined(OS_MACOSX) // Mac uses its native engine instead. 333 void SpellCheck::PerformSpellCheck(SpellcheckRequest* param) { 334 DCHECK(param); 335 336 if (!spellcheck_.IsEnabled()) { 337 param->completion()->didCancelCheckingText(); 338 } else { 339 WebVector<WebKit::WebTextCheckingResult> results; 340 SpellCheckParagraph(param->text(), &results); 341 param->completion()->didFinishCheckingText(results); 342 } 343 } 344 #endif 345 346 void SpellCheck::CreateTextCheckingResults( 347 ResultFilter filter, 348 int line_offset, 349 const string16& line_text, 350 const std::vector<SpellCheckResult>& spellcheck_results, 351 WebVector<WebTextCheckingResult>* textcheck_results) { 352 // Double-check misspelled words with our spellchecker and attach grammar 353 // markers to them if our spellchecker tells they are correct words, i.e. they 354 // are probably contextually-misspelled words. 355 const char16* text = line_text.c_str(); 356 std::vector<WebTextCheckingResult> list; 357 for (size_t i = 0; i < spellcheck_results.size(); ++i) { 358 WebTextCheckingType type = 359 static_cast<WebTextCheckingType>(spellcheck_results[i].type); 360 int word_location = spellcheck_results[i].location; 361 int word_length = spellcheck_results[i].length; 362 int misspelling_start = 0; 363 int misspelling_length = 0; 364 if (type == WebKit::WebTextCheckingTypeSpelling && 365 filter == USE_NATIVE_CHECKER) { 366 if (SpellCheckWord(text + word_location, word_length, 0, 367 &misspelling_start, &misspelling_length, NULL)) { 368 type = WebKit::WebTextCheckingTypeGrammar; 369 } 370 } 371 if (!custom_dictionary_.SpellCheckWord( 372 line_text, word_location, word_length)) { 373 list.push_back(WebTextCheckingResult( 374 type, 375 word_location + line_offset, 376 word_length, 377 spellcheck_results[i].replacement, 378 spellcheck_results[i].hash)); 379 } 380 } 381 textcheck_results->assign(list); 382 } 383