ExtensionSyncService cleanup: process uninstalls in one step
[chromium-blink-merge.git] / chrome / renderer / spellchecker / spellcheck.cc
blobe28b5ab58a4db7ee722bfe9ad0679312750b3ce2
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.
5 #include "chrome/renderer/spellchecker/spellcheck.h"
7 #include <algorithm>
9 #include "base/basictypes.h"
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "chrome/common/spellcheck_common.h"
16 #include "chrome/common/spellcheck_messages.h"
17 #include "chrome/common/spellcheck_result.h"
18 #include "chrome/renderer/spellchecker/spellcheck_language.h"
19 #include "chrome/renderer/spellchecker/spellcheck_provider.h"
20 #include "content/public/renderer/render_thread.h"
21 #include "content/public/renderer/render_view.h"
22 #include "content/public/renderer/render_view_visitor.h"
23 #include "third_party/WebKit/public/platform/WebString.h"
24 #include "third_party/WebKit/public/platform/WebVector.h"
25 #include "third_party/WebKit/public/web/WebTextCheckingCompletion.h"
26 #include "third_party/WebKit/public/web/WebTextCheckingResult.h"
27 #include "third_party/WebKit/public/web/WebTextDecorationType.h"
28 #include "third_party/WebKit/public/web/WebView.h"
30 using blink::WebVector;
31 using blink::WebString;
32 using blink::WebTextCheckingResult;
33 using blink::WebTextDecorationType;
35 namespace {
37 class UpdateSpellcheckEnabled : public content::RenderViewVisitor {
38 public:
39 explicit UpdateSpellcheckEnabled(bool enabled) : enabled_(enabled) {}
40 bool Visit(content::RenderView* render_view) override;
42 private:
43 bool enabled_; // New spellcheck-enabled state.
44 DISALLOW_COPY_AND_ASSIGN(UpdateSpellcheckEnabled);
47 bool UpdateSpellcheckEnabled::Visit(content::RenderView* render_view) {
48 SpellCheckProvider* provider = SpellCheckProvider::Get(render_view);
49 DCHECK(provider);
50 provider->EnableSpellcheck(enabled_);
51 return true;
54 class DocumentMarkersCollector : public content::RenderViewVisitor {
55 public:
56 DocumentMarkersCollector() {}
57 ~DocumentMarkersCollector() override {}
58 const std::vector<uint32>& markers() const { return markers_; }
59 bool Visit(content::RenderView* render_view) override;
61 private:
62 std::vector<uint32> markers_;
63 DISALLOW_COPY_AND_ASSIGN(DocumentMarkersCollector);
66 bool DocumentMarkersCollector::Visit(content::RenderView* render_view) {
67 if (!render_view || !render_view->GetWebView())
68 return true;
69 WebVector<uint32> markers;
70 render_view->GetWebView()->spellingMarkers(&markers);
71 for (size_t i = 0; i < markers.size(); ++i)
72 markers_.push_back(markers[i]);
73 // Visit all render views.
74 return true;
77 class DocumentMarkersRemover : public content::RenderViewVisitor {
78 public:
79 explicit DocumentMarkersRemover(const std::set<std::string>& words);
80 ~DocumentMarkersRemover() override {}
81 bool Visit(content::RenderView* render_view) override;
83 private:
84 WebVector<WebString> words_;
85 DISALLOW_COPY_AND_ASSIGN(DocumentMarkersRemover);
88 DocumentMarkersRemover::DocumentMarkersRemover(
89 const std::set<std::string>& words)
90 : words_(words.size()) {
91 std::transform(words.begin(), words.end(), words_.begin(),
92 [](const std::string& w) { return WebString::fromUTF8(w); });
95 bool DocumentMarkersRemover::Visit(content::RenderView* render_view) {
96 if (render_view && render_view->GetWebView())
97 render_view->GetWebView()->removeSpellingMarkersUnderWords(words_);
98 return true;
101 bool IsApostrophe(base::char16 c) {
102 const base::char16 kApostrophe = 0x27;
103 const base::char16 kRightSingleQuotationMark = 0x2019;
104 return c == kApostrophe || c == kRightSingleQuotationMark;
107 // Makes sure that the apostrophes in the |spelling_suggestion| are the same
108 // type as in the |misspelled_word| and in the same order. Ignore differences in
109 // the number of apostrophes.
110 void PreserveOriginalApostropheTypes(const base::string16& misspelled_word,
111 base::string16* spelling_suggestion) {
112 auto it = spelling_suggestion->begin();
113 for (const base::char16& c : misspelled_word) {
114 if (IsApostrophe(c)) {
115 it = std::find_if(it, spelling_suggestion->end(), IsApostrophe);
116 if (it == spelling_suggestion->end())
117 return;
119 *it++ = c;
124 } // namespace
126 class SpellCheck::SpellcheckRequest {
127 public:
128 SpellcheckRequest(const base::string16& text,
129 blink::WebTextCheckingCompletion* completion)
130 : text_(text), completion_(completion) {
131 DCHECK(completion);
133 ~SpellcheckRequest() {}
135 base::string16 text() { return text_; }
136 blink::WebTextCheckingCompletion* completion() { return completion_; }
138 private:
139 base::string16 text_; // Text to be checked in this task.
141 // The interface to send the misspelled ranges to WebKit.
142 blink::WebTextCheckingCompletion* completion_;
144 DISALLOW_COPY_AND_ASSIGN(SpellcheckRequest);
148 // Initializes SpellCheck object.
149 // spellcheck_enabled_ currently MUST be set to true, due to peculiarities of
150 // the initialization sequence.
151 // Since it defaults to true, newly created SpellCheckProviders will enable
152 // spellchecking. After the first word is typed, the provider requests a check,
153 // which in turn triggers the delayed initialization sequence in SpellCheck.
154 // This does send a message to the browser side, which triggers the creation
155 // of the SpellcheckService. That does create the observer for the preference
156 // responsible for enabling/disabling checking, which allows subsequent changes
157 // to that preference to be sent to all SpellCheckProviders.
158 // Setting |spellcheck_enabled_| to false by default prevents that mechanism,
159 // and as such the SpellCheckProviders will never be notified of different
160 // values.
161 // TODO(groby): Simplify this.
162 SpellCheck::SpellCheck()
163 : auto_spell_correct_turned_on_(false),
164 spellcheck_enabled_(true) {
165 languages_.push_back(new SpellcheckLanguage());
168 SpellCheck::~SpellCheck() {
171 bool SpellCheck::OnControlMessageReceived(const IPC::Message& message) {
172 bool handled = true;
173 IPC_BEGIN_MESSAGE_MAP(SpellCheck, message)
174 IPC_MESSAGE_HANDLER(SpellCheckMsg_Init, OnInit)
175 IPC_MESSAGE_HANDLER(SpellCheckMsg_CustomDictionaryChanged,
176 OnCustomDictionaryChanged)
177 IPC_MESSAGE_HANDLER(SpellCheckMsg_EnableAutoSpellCorrect,
178 OnEnableAutoSpellCorrect)
179 IPC_MESSAGE_HANDLER(SpellCheckMsg_EnableSpellCheck, OnEnableSpellCheck)
180 IPC_MESSAGE_HANDLER(SpellCheckMsg_RequestDocumentMarkers,
181 OnRequestDocumentMarkers)
182 IPC_MESSAGE_UNHANDLED(handled = false)
183 IPC_END_MESSAGE_MAP()
185 return handled;
188 void SpellCheck::OnInit(IPC::PlatformFileForTransit bdict_file,
189 const std::set<std::string>& custom_words,
190 const std::string& language,
191 bool auto_spell_correct) {
192 Init(IPC::PlatformFileForTransitToFile(bdict_file), custom_words, language);
193 auto_spell_correct_turned_on_ = auto_spell_correct;
194 #if !defined(OS_MACOSX)
195 PostDelayedSpellCheckTask(pending_request_param_.release());
196 #endif
199 void SpellCheck::OnCustomDictionaryChanged(
200 const std::set<std::string>& words_added,
201 const std::set<std::string>& words_removed) {
202 custom_dictionary_.OnCustomDictionaryChanged(words_added, words_removed);
203 if (words_added.empty())
204 return;
205 DocumentMarkersRemover markersRemover(words_added);
206 content::RenderView::ForEach(&markersRemover);
209 void SpellCheck::OnEnableAutoSpellCorrect(bool enable) {
210 auto_spell_correct_turned_on_ = enable;
213 void SpellCheck::OnEnableSpellCheck(bool enable) {
214 spellcheck_enabled_ = enable;
215 UpdateSpellcheckEnabled updater(enable);
216 content::RenderView::ForEach(&updater);
219 void SpellCheck::OnRequestDocumentMarkers() {
220 DocumentMarkersCollector collector;
221 content::RenderView::ForEach(&collector);
222 content::RenderThread::Get()->Send(
223 new SpellCheckHostMsg_RespondDocumentMarkers(collector.markers()));
226 // TODO(groby): Make sure we always have a spelling engine, even before Init()
227 // is called.
228 void SpellCheck::Init(base::File file,
229 const std::set<std::string>& custom_words,
230 const std::string& language) {
231 languages_.front()->Init(file.Pass(), language);
232 custom_dictionary_.Init(custom_words);
235 bool SpellCheck::SpellCheckWord(
236 const base::char16* in_word,
237 int in_word_len,
238 int tag,
239 int* misspelling_start,
240 int* misspelling_len,
241 std::vector<base::string16>* optional_suggestions) {
242 DCHECK(in_word_len >= 0);
243 DCHECK(misspelling_start && misspelling_len) << "Out vars must be given.";
245 // Do nothing if we need to delay initialization. (Rather than blocking,
246 // report the word as correctly spelled.)
247 if (InitializeIfNeeded())
248 return true;
250 return languages_.front()->SpellCheckWord(in_word, in_word_len, tag,
251 misspelling_start, misspelling_len,
252 optional_suggestions);
255 bool SpellCheck::SpellCheckParagraph(
256 const base::string16& text,
257 WebVector<WebTextCheckingResult>* results) {
258 #if !defined(OS_MACOSX)
259 // Mac has its own spell checker, so this method will not be used.
260 DCHECK(results);
261 std::vector<WebTextCheckingResult> textcheck_results;
262 size_t length = text.length();
263 size_t offset = 0;
265 // Spellcheck::SpellCheckWord() automatically breaks text into words and
266 // checks the spellings of the extracted words. This function sets the
267 // position and length of the first misspelled word and returns false when
268 // the text includes misspelled words. Therefore, we just repeat calling the
269 // function until it returns true to check the whole text.
270 int misspelling_start = 0;
271 int misspelling_length = 0;
272 while (offset <= length) {
273 if (SpellCheckWord(&text[offset],
274 length - offset,
276 &misspelling_start,
277 &misspelling_length,
278 NULL)) {
279 results->assign(textcheck_results);
280 return true;
283 if (!custom_dictionary_.SpellCheckWord(
284 text, misspelling_start + offset, misspelling_length)) {
285 base::string16 replacement;
286 textcheck_results.push_back(WebTextCheckingResult(
287 blink::WebTextDecorationTypeSpelling,
288 misspelling_start + offset,
289 misspelling_length,
290 replacement));
292 offset += misspelling_start + misspelling_length;
294 results->assign(textcheck_results);
295 return false;
296 #else
297 // This function is only invoked for spell checker functionality that runs
298 // on the render thread. OSX builds don't have that.
299 NOTREACHED();
300 return true;
301 #endif
304 base::string16 SpellCheck::GetAutoCorrectionWord(const base::string16& word,
305 int tag) {
306 base::string16 autocorrect_word;
307 if (!auto_spell_correct_turned_on_)
308 return autocorrect_word; // Return the empty string.
310 int word_length = static_cast<int>(word.size());
311 if (word_length < 2 ||
312 word_length > chrome::spellcheck_common::kMaxAutoCorrectWordSize)
313 return autocorrect_word;
315 if (InitializeIfNeeded())
316 return autocorrect_word;
318 base::char16 misspelled_word[
319 chrome::spellcheck_common::kMaxAutoCorrectWordSize + 1];
320 const base::char16* word_char = word.c_str();
321 for (int i = 0; i <= chrome::spellcheck_common::kMaxAutoCorrectWordSize;
322 ++i) {
323 if (i >= word_length)
324 misspelled_word[i] = 0;
325 else
326 misspelled_word[i] = word_char[i];
329 // Swap adjacent characters and spellcheck.
330 int misspelling_start, misspelling_len;
331 for (int i = 0; i < word_length - 1; i++) {
332 // Swap.
333 std::swap(misspelled_word[i], misspelled_word[i + 1]);
335 // Check spelling.
336 misspelling_start = misspelling_len = 0;
337 SpellCheckWord(misspelled_word, word_length, tag, &misspelling_start,
338 &misspelling_len, NULL);
340 // Make decision: if only one swap produced a valid word, then we want to
341 // return it. If we found two or more, we don't do autocorrection.
342 if (misspelling_len == 0) {
343 if (autocorrect_word.empty()) {
344 autocorrect_word.assign(misspelled_word);
345 } else {
346 autocorrect_word.clear();
347 break;
351 // Restore the swapped characters.
352 std::swap(misspelled_word[i], misspelled_word[i + 1]);
354 return autocorrect_word;
357 #if !defined(OS_MACOSX) // OSX uses its own spell checker
358 void SpellCheck::RequestTextChecking(
359 const base::string16& text,
360 blink::WebTextCheckingCompletion* completion) {
361 // Clean up the previous request before starting a new request.
362 if (pending_request_param_.get())
363 pending_request_param_->completion()->didCancelCheckingText();
365 pending_request_param_.reset(new SpellcheckRequest(
366 text, completion));
367 // We will check this text after we finish loading the hunspell dictionary.
368 if (InitializeIfNeeded())
369 return;
371 PostDelayedSpellCheckTask(pending_request_param_.release());
373 #endif
375 bool SpellCheck::InitializeIfNeeded() {
376 return languages_.front()->InitializeIfNeeded();
379 #if !defined(OS_MACOSX) // OSX doesn't have |pending_request_param_|
380 void SpellCheck::PostDelayedSpellCheckTask(SpellcheckRequest* request) {
381 if (!request)
382 return;
384 base::ThreadTaskRunnerHandle::Get()->PostTask(
385 FROM_HERE, base::Bind(&SpellCheck::PerformSpellCheck, AsWeakPtr(),
386 base::Owned(request)));
388 #endif
390 #if !defined(OS_MACOSX) // Mac uses its platform engine instead.
391 void SpellCheck::PerformSpellCheck(SpellcheckRequest* param) {
392 DCHECK(param);
394 if (!languages_.front()->IsEnabled()) {
395 param->completion()->didCancelCheckingText();
396 } else {
397 WebVector<blink::WebTextCheckingResult> results;
398 SpellCheckParagraph(param->text(), &results);
399 param->completion()->didFinishCheckingText(results);
402 #endif
404 void SpellCheck::CreateTextCheckingResults(
405 ResultFilter filter,
406 int line_offset,
407 const base::string16& line_text,
408 const std::vector<SpellCheckResult>& spellcheck_results,
409 WebVector<WebTextCheckingResult>* textcheck_results) {
410 DCHECK(!line_text.empty());
412 std::vector<WebTextCheckingResult> results;
413 for (const SpellCheckResult& spellcheck_result : spellcheck_results) {
414 DCHECK_LE(static_cast<size_t>(spellcheck_result.location),
415 line_text.length());
416 DCHECK_LE(static_cast<size_t>(spellcheck_result.location +
417 spellcheck_result.length),
418 line_text.length());
420 const base::string16& misspelled_word =
421 line_text.substr(spellcheck_result.location, spellcheck_result.length);
422 base::string16 replacement = spellcheck_result.replacement;
423 SpellCheckResult::Decoration decoration = spellcheck_result.decoration;
425 // Ignore words in custom dictionary.
426 if (custom_dictionary_.SpellCheckWord(misspelled_word, 0,
427 misspelled_word.length())) {
428 continue;
431 // Use the same types of appostrophes as in the mispelled word.
432 PreserveOriginalApostropheTypes(misspelled_word, &replacement);
434 // Ignore misspellings due the typographical apostrophe.
435 if (misspelled_word == replacement)
436 continue;
438 if (filter == USE_NATIVE_CHECKER) {
439 // Double-check misspelled words with out spellchecker and attach grammar
440 // markers to them if our spellchecker tells us they are correct words,
441 // i.e. they are probably contextually-misspelled words.
442 int unused_misspelling_start = 0;
443 int unused_misspelling_length = 0;
444 if (decoration == SpellCheckResult::SPELLING &&
445 SpellCheckWord(misspelled_word.c_str(), misspelled_word.length(), 0,
446 &unused_misspelling_start, &unused_misspelling_length,
447 nullptr)) {
448 decoration = SpellCheckResult::GRAMMAR;
452 results.push_back(WebTextCheckingResult(
453 static_cast<WebTextDecorationType>(decoration),
454 line_offset + spellcheck_result.location, spellcheck_result.length,
455 replacement, spellcheck_result.hash));
458 textcheck_results->assign(results);