Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / renderer / spellchecker / spellcheck.cc
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.
4
5 #include "chrome/renderer/spellchecker/spellcheck.h"
6
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"
23
24 using blink::WebVector;
25 using blink::WebString;
26 using blink::WebTextCheckingResult;
27 using blink::WebTextDecorationType;
28
29 namespace {
30
31 class UpdateSpellcheckEnabled : public content::RenderViewVisitor {
32  public:
33   explicit UpdateSpellcheckEnabled(bool enabled) : enabled_(enabled) {}
34   virtual bool Visit(content::RenderView* render_view) OVERRIDE;
35
36  private:
37   bool enabled_;  // New spellcheck-enabled state.
38   DISALLOW_COPY_AND_ASSIGN(UpdateSpellcheckEnabled);
39 };
40
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;
46 }
47
48 class DocumentMarkersCollector : public content::RenderViewVisitor {
49  public:
50   DocumentMarkersCollector() {}
51   virtual ~DocumentMarkersCollector() {}
52   const std::vector<uint32>& markers() const { return markers_; }
53   virtual bool Visit(content::RenderView* render_view) OVERRIDE;
54
55  private:
56   std::vector<uint32> markers_;
57   DISALLOW_COPY_AND_ASSIGN(DocumentMarkersCollector);
58 };
59
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;
69 }
70
71 class DocumentMarkersRemover : public content::RenderViewVisitor {
72  public:
73   explicit DocumentMarkersRemover(const std::vector<std::string>& words);
74   virtual ~DocumentMarkersRemover() OVERRIDE {}
75   virtual bool Visit(content::RenderView* render_view) OVERRIDE;
76
77  private:
78   WebVector<WebString> words_;
79   DISALLOW_COPY_AND_ASSIGN(DocumentMarkersRemover);
80 };
81
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]);
87 }
88
89 bool DocumentMarkersRemover::Visit(content::RenderView* render_view) {
90   if (render_view && render_view->GetWebView())
91     render_view->GetWebView()->removeSpellingMarkersUnderWords(words_);
92   return true;
93 }
94
95 }  // namespace
96
97 class SpellCheck::SpellcheckRequest {
98  public:
99   SpellcheckRequest(const base::string16& text,
100                     blink::WebTextCheckingCompletion* completion)
101       : text_(text), completion_(completion) {
102     DCHECK(completion);
103   }
104   ~SpellcheckRequest() {}
105
106   base::string16 text() { return text_; }
107   blink::WebTextCheckingCompletion* completion() { return completion_; }
108
109  private:
110   base::string16 text_;  // Text to be checked in this task.
111
112   // The interface to send the misspelled ranges to WebKit.
113   blink::WebTextCheckingCompletion* completion_;
114
115   DISALLOW_COPY_AND_ASSIGN(SpellcheckRequest);
116 };
117
118
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) {
136 }
137
138 SpellCheck::~SpellCheck() {
139 }
140
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()
154
155   return handled;
156 }
157
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
168 }
169
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);
178 }
179
180 void SpellCheck::OnEnableAutoSpellCorrect(bool enable) {
181   auto_spell_correct_turned_on_ = enable;
182 }
183
184 void SpellCheck::OnEnableSpellCheck(bool enable) {
185   spellcheck_enabled_ = enable;
186   UpdateSpellcheckEnabled updater(enable);
187   content::RenderView::ForEach(&updater);
188 }
189
190 void SpellCheck::OnRequestDocumentMarkers() {
191   DocumentMarkersCollector collector;
192   content::RenderView::ForEach(&collector);
193   content::RenderThread::Get()->Send(
194       new SpellCheckHostMsg_RespondDocumentMarkers(collector.markers()));
195 }
196
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);
204 }
205
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.";
215
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;
220
221   return spellcheck_.SpellCheckWord(in_word, in_word_len,
222                                     tag,
223                                     misspelling_start, misspelling_len,
224                                     optional_suggestions);
225 }
226
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;
236
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,
247                        0,
248                        &misspelling_start,
249                        &misspelling_length,
250                        NULL)) {
251       results->assign(textcheck_results);
252       return true;
253     }
254
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));
263     }
264     offset += misspelling_start + misspelling_length;
265   }
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
274 }
275
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.
281
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;
286
287   if (InitializeIfNeeded())
288     return autocorrect_word;
289
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];
299   }
300
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]);
306
307     // Check spelling.
308     misspelling_start = misspelling_len = 0;
309     SpellCheckWord(misspelled_word, word_length, tag, &misspelling_start,
310         &misspelling_len, NULL);
311
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;
320       }
321     }
322
323     // Restore the swapped characters.
324     std::swap(misspelled_word[i], misspelled_word[i + 1]);
325   }
326   return autocorrect_word;
327 }
328
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();
336
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;
342
343   PostDelayedSpellCheckTask(pending_request_param_.release());
344 }
345 #endif
346
347 bool SpellCheck::InitializeIfNeeded() {
348   return spellcheck_.InitializeIfNeeded();
349 }
350
351 #if !defined(OS_MACOSX) // OSX doesn't have |pending_request_param_|
352 void SpellCheck::PostDelayedSpellCheckTask(SpellcheckRequest* request) {
353   if (!request)
354     return;
355
356   base::MessageLoopProxy::current()->PostTask(FROM_HERE,
357       base::Bind(&SpellCheck::PerformSpellCheck,
358                  AsWeakPtr(),
359                  base::Owned(request)));
360 }
361 #endif
362
363 #if !defined(OS_MACOSX)  // Mac uses its native engine instead.
364 void SpellCheck::PerformSpellCheck(SpellcheckRequest* param) {
365   DCHECK(param);
366
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);
373   }
374 }
375 #endif
376
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;
399       }
400     }
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));
409     }
410   }
411   textcheck_results->assign(list);
412 }