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