Unregister from GCM when the only GCM app is removed
[chromium-blink-merge.git] / chrome / renderer / spellchecker / spellcheck.cc
blob8d3a0491c750b5c7ef5ffc58079936a6e4f260ca
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 DocumentMarkersRemover : public content::RenderViewVisitor {
49 public:
50 explicit DocumentMarkersRemover(const std::vector<std::string>& words);
51 ~DocumentMarkersRemover() override {}
52 bool Visit(content::RenderView* render_view) override;
54 private:
55 WebVector<WebString> words_;
56 DISALLOW_COPY_AND_ASSIGN(DocumentMarkersRemover);
59 DocumentMarkersRemover::DocumentMarkersRemover(
60 const std::vector<std::string>& words)
61 : words_(words.size()) {
62 for (size_t i = 0; i < words.size(); ++i)
63 words_[i] = WebString::fromUTF8(words[i]);
66 bool DocumentMarkersRemover::Visit(content::RenderView* render_view) {
67 if (render_view && render_view->GetWebView())
68 render_view->GetWebView()->removeSpellingMarkersUnderWords(words_);
69 return true;
72 } // namespace
74 class SpellCheck::SpellcheckRequest {
75 public:
76 SpellcheckRequest(const base::string16& text,
77 blink::WebTextCheckingCompletion* completion)
78 : text_(text), completion_(completion) {
79 DCHECK(completion);
81 ~SpellcheckRequest() {}
83 base::string16 text() { return text_; }
84 blink::WebTextCheckingCompletion* completion() { return completion_; }
86 private:
87 base::string16 text_; // Text to be checked in this task.
89 // The interface to send the misspelled ranges to WebKit.
90 blink::WebTextCheckingCompletion* completion_;
92 DISALLOW_COPY_AND_ASSIGN(SpellcheckRequest);
96 // Initializes SpellCheck object.
97 // spellcheck_enabled_ currently MUST be set to true, due to peculiarities of
98 // the initialization sequence.
99 // Since it defaults to true, newly created SpellCheckProviders will enable
100 // spellchecking. After the first word is typed, the provider requests a check,
101 // which in turn triggers the delayed initialization sequence in SpellCheck.
102 // This does send a message to the browser side, which triggers the creation
103 // of the SpellcheckService. That does create the observer for the preference
104 // responsible for enabling/disabling checking, which allows subsequent changes
105 // to that preference to be sent to all SpellCheckProviders.
106 // Setting |spellcheck_enabled_| to false by default prevents that mechanism,
107 // and as such the SpellCheckProviders will never be notified of different
108 // values.
109 // TODO(groby): Simplify this.
110 SpellCheck::SpellCheck()
111 : auto_spell_correct_turned_on_(false),
112 spellcheck_enabled_(true) {
115 SpellCheck::~SpellCheck() {
118 bool SpellCheck::OnControlMessageReceived(const IPC::Message& message) {
119 bool handled = true;
120 IPC_BEGIN_MESSAGE_MAP(SpellCheck, message)
121 IPC_MESSAGE_HANDLER(SpellCheckMsg_Init, OnInit)
122 IPC_MESSAGE_HANDLER(SpellCheckMsg_CustomDictionaryChanged,
123 OnCustomDictionaryChanged)
124 IPC_MESSAGE_HANDLER(SpellCheckMsg_EnableAutoSpellCorrect,
125 OnEnableAutoSpellCorrect)
126 IPC_MESSAGE_HANDLER(SpellCheckMsg_EnableSpellCheck, OnEnableSpellCheck)
127 IPC_MESSAGE_UNHANDLED(handled = false)
128 IPC_END_MESSAGE_MAP()
130 return handled;
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::PlatformFileForTransitToFile(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
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 if (words_added.empty())
150 return;
151 DocumentMarkersRemover markersRemover(words_added);
152 content::RenderView::ForEach(&markersRemover);
155 void SpellCheck::OnEnableAutoSpellCorrect(bool enable) {
156 auto_spell_correct_turned_on_ = enable;
159 void SpellCheck::OnEnableSpellCheck(bool enable) {
160 spellcheck_enabled_ = enable;
161 UpdateSpellcheckEnabled updater(enable);
162 content::RenderView::ForEach(&updater);
165 // TODO(groby): Make sure we always have a spelling engine, even before Init()
166 // is called.
167 void SpellCheck::Init(base::File file,
168 const std::set<std::string>& custom_words,
169 const std::string& language) {
170 spellcheck_.Init(file.Pass(), language);
171 custom_dictionary_.Init(custom_words);
174 bool SpellCheck::SpellCheckWord(
175 const base::char16* in_word,
176 int in_word_len,
177 int tag,
178 int* misspelling_start,
179 int* misspelling_len,
180 std::vector<base::string16>* optional_suggestions) {
181 DCHECK(in_word_len >= 0);
182 DCHECK(misspelling_start && misspelling_len) << "Out vars must be given.";
184 // Do nothing if we need to delay initialization. (Rather than blocking,
185 // report the word as correctly spelled.)
186 if (InitializeIfNeeded())
187 return true;
189 return spellcheck_.SpellCheckWord(in_word, in_word_len,
190 tag,
191 misspelling_start, misspelling_len,
192 optional_suggestions);
195 bool SpellCheck::SpellCheckParagraph(
196 const base::string16& text,
197 WebVector<WebTextCheckingResult>* results) {
198 #if !defined(OS_MACOSX)
199 // Mac has its own spell checker, so this method will not be used.
200 DCHECK(results);
201 std::vector<WebTextCheckingResult> textcheck_results;
202 size_t length = text.length();
203 size_t offset = 0;
205 // Spellcheck::SpellCheckWord() automatically breaks text into words and
206 // checks the spellings of the extracted words. This function sets the
207 // position and length of the first misspelled word and returns false when
208 // the text includes misspelled words. Therefore, we just repeat calling the
209 // function until it returns true to check the whole text.
210 int misspelling_start = 0;
211 int misspelling_length = 0;
212 while (offset <= length) {
213 if (SpellCheckWord(&text[offset],
214 length - offset,
216 &misspelling_start,
217 &misspelling_length,
218 NULL)) {
219 results->assign(textcheck_results);
220 return true;
223 if (!custom_dictionary_.SpellCheckWord(
224 text, misspelling_start + offset, misspelling_length)) {
225 base::string16 replacement;
226 textcheck_results.push_back(WebTextCheckingResult(
227 blink::WebTextDecorationTypeSpelling,
228 misspelling_start + offset,
229 misspelling_length,
230 replacement));
232 offset += misspelling_start + misspelling_length;
234 results->assign(textcheck_results);
235 return false;
236 #else
237 // This function is only invoked for spell checker functionality that runs
238 // on the render thread. OSX builds don't have that.
239 NOTREACHED();
240 return true;
241 #endif
244 base::string16 SpellCheck::GetAutoCorrectionWord(const base::string16& word,
245 int tag) {
246 base::string16 autocorrect_word;
247 if (!auto_spell_correct_turned_on_)
248 return autocorrect_word; // Return the empty string.
250 int word_length = static_cast<int>(word.size());
251 if (word_length < 2 ||
252 word_length > chrome::spellcheck_common::kMaxAutoCorrectWordSize)
253 return autocorrect_word;
255 if (InitializeIfNeeded())
256 return autocorrect_word;
258 base::char16 misspelled_word[
259 chrome::spellcheck_common::kMaxAutoCorrectWordSize + 1];
260 const base::char16* word_char = word.c_str();
261 for (int i = 0; i <= chrome::spellcheck_common::kMaxAutoCorrectWordSize;
262 ++i) {
263 if (i >= word_length)
264 misspelled_word[i] = 0;
265 else
266 misspelled_word[i] = word_char[i];
269 // Swap adjacent characters and spellcheck.
270 int misspelling_start, misspelling_len;
271 for (int i = 0; i < word_length - 1; i++) {
272 // Swap.
273 std::swap(misspelled_word[i], misspelled_word[i + 1]);
275 // Check spelling.
276 misspelling_start = misspelling_len = 0;
277 SpellCheckWord(misspelled_word, word_length, tag, &misspelling_start,
278 &misspelling_len, NULL);
280 // Make decision: if only one swap produced a valid word, then we want to
281 // return it. If we found two or more, we don't do autocorrection.
282 if (misspelling_len == 0) {
283 if (autocorrect_word.empty()) {
284 autocorrect_word.assign(misspelled_word);
285 } else {
286 autocorrect_word.clear();
287 break;
291 // Restore the swapped characters.
292 std::swap(misspelled_word[i], misspelled_word[i + 1]);
294 return autocorrect_word;
297 #if !defined(OS_MACOSX) // OSX uses its own spell checker
298 void SpellCheck::RequestTextChecking(
299 const base::string16& text,
300 blink::WebTextCheckingCompletion* completion) {
301 // Clean up the previous request before starting a new request.
302 if (pending_request_param_.get())
303 pending_request_param_->completion()->didCancelCheckingText();
305 pending_request_param_.reset(new SpellcheckRequest(
306 text, completion));
307 // We will check this text after we finish loading the hunspell dictionary.
308 if (InitializeIfNeeded())
309 return;
311 PostDelayedSpellCheckTask(pending_request_param_.release());
313 #endif
315 bool SpellCheck::InitializeIfNeeded() {
316 return spellcheck_.InitializeIfNeeded();
319 #if !defined(OS_MACOSX) // OSX doesn't have |pending_request_param_|
320 void SpellCheck::PostDelayedSpellCheckTask(SpellcheckRequest* request) {
321 if (!request)
322 return;
324 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
325 base::Bind(&SpellCheck::PerformSpellCheck,
326 AsWeakPtr(),
327 base::Owned(request)));
329 #endif
331 #if !defined(OS_MACOSX) // Mac uses its native engine instead.
332 void SpellCheck::PerformSpellCheck(SpellcheckRequest* param) {
333 DCHECK(param);
335 if (!spellcheck_.IsEnabled()) {
336 param->completion()->didCancelCheckingText();
337 } else {
338 WebVector<blink::WebTextCheckingResult> results;
339 SpellCheckParagraph(param->text(), &results);
340 param->completion()->didFinishCheckingText(results);
343 #endif
345 void SpellCheck::CreateTextCheckingResults(
346 ResultFilter filter,
347 int line_offset,
348 const base::string16& line_text,
349 const std::vector<SpellCheckResult>& spellcheck_results,
350 WebVector<WebTextCheckingResult>* textcheck_results) {
351 // Double-check misspelled words with our spellchecker and attach grammar
352 // markers to them if our spellchecker tells they are correct words, i.e. they
353 // are probably contextually-misspelled words.
354 const base::char16* text = line_text.c_str();
355 std::vector<WebTextCheckingResult> list;
356 for (size_t i = 0; i < spellcheck_results.size(); ++i) {
357 SpellCheckResult::Decoration decoration = spellcheck_results[i].decoration;
358 int word_location = spellcheck_results[i].location;
359 int word_length = spellcheck_results[i].length;
360 int misspelling_start = 0;
361 int misspelling_length = 0;
362 if (decoration == SpellCheckResult::SPELLING &&
363 filter == USE_NATIVE_CHECKER) {
364 if (SpellCheckWord(text + word_location, word_length, 0,
365 &misspelling_start, &misspelling_length, NULL)) {
366 decoration = SpellCheckResult::GRAMMAR;
369 if (!custom_dictionary_.SpellCheckWord(
370 line_text, word_location, word_length)) {
371 list.push_back(WebTextCheckingResult(
372 static_cast<WebTextDecorationType>(decoration),
373 word_location + line_offset,
374 word_length,
375 spellcheck_results[i].replacement,
376 spellcheck_results[i].hash));
379 textcheck_results->assign(list);