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"
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
;
36 class UpdateSpellcheckEnabled
: public content::RenderViewVisitor
{
38 explicit UpdateSpellcheckEnabled(bool enabled
) : enabled_(enabled
) {}
39 bool Visit(content::RenderView
* render_view
) override
;
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
);
49 provider
->EnableSpellcheck(enabled_
);
53 class DocumentMarkersCollector
: public content::RenderViewVisitor
{
55 DocumentMarkersCollector() {}
56 ~DocumentMarkersCollector() override
{}
57 const std::vector
<uint32
>& markers() const { return markers_
; }
58 bool Visit(content::RenderView
* render_view
) override
;
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())
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.
76 class DocumentMarkersRemover
: public content::RenderViewVisitor
{
78 explicit DocumentMarkersRemover(const std::set
<std::string
>& words
);
79 ~DocumentMarkersRemover() override
{}
80 bool Visit(content::RenderView
* render_view
) override
;
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_
);
102 class SpellCheck::SpellcheckRequest
{
104 SpellcheckRequest(const base::string16
& text
,
105 blink::WebTextCheckingCompletion
* completion
)
106 : text_(text
), completion_(completion
) {
109 ~SpellcheckRequest() {}
111 base::string16
text() { return text_
; }
112 blink::WebTextCheckingCompletion
* completion() { return completion_
; }
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
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
) {
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()
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());
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())
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()
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
,
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())
225 return spellcheck_
.SpellCheckWord(in_word
, in_word_len
,
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.
237 std::vector
<WebTextCheckingResult
> textcheck_results
;
238 size_t length
= text
.length();
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
],
255 results
->assign(textcheck_results
);
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
,
268 offset
+= misspelling_start
+ misspelling_length
;
270 results
->assign(textcheck_results
);
273 // This function is only invoked for spell checker functionality that runs
274 // on the render thread. OSX builds don't have that.
280 base::string16
SpellCheck::GetAutoCorrectionWord(const base::string16
& word
,
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
;
299 if (i
>= word_length
)
300 misspelled_word
[i
] = 0;
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
++) {
309 std::swap(misspelled_word
[i
], misspelled_word
[i
+ 1]);
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
);
322 autocorrect_word
.clear();
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(
343 // We will check this text after we finish loading the hunspell dictionary.
344 if (InitializeIfNeeded())
347 PostDelayedSpellCheckTask(pending_request_param_
.release());
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
) {
360 base::ThreadTaskRunnerHandle::Get()->PostTask(
361 FROM_HERE
, base::Bind(&SpellCheck::PerformSpellCheck
, AsWeakPtr(),
362 base::Owned(request
)));
366 #if !defined(OS_MACOSX) // Mac uses its native engine instead.
367 void SpellCheck::PerformSpellCheck(SpellcheckRequest
* param
) {
370 if (!spellcheck_
.IsEnabled()) {
371 param
->completion()->didCancelCheckingText();
373 WebVector
<blink::WebTextCheckingResult
> results
;
374 SpellCheckParagraph(param
->text(), &results
);
375 param
->completion()->didFinishCheckingText(results
);
380 void SpellCheck::CreateTextCheckingResults(
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
,
410 spellcheck_results
[i
].replacement
,
411 spellcheck_results
[i
].hash
));
414 textcheck_results
->assign(list
);