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