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