15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/renderer/spellchecker/spellcheck.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
8b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "base/message_loop/message_loop_proxy.h"
9868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/render_messages.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/spellcheck_common.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/spellcheck_messages.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/spellcheck_result.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/renderer/spellchecker/spellcheck_language.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/renderer/spellchecker/spellcheck_provider.h"
16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/public/renderer/render_thread.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/renderer/render_view.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/renderer/render_view_visitor.h"
197d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "third_party/WebKit/public/web/WebTextCheckingCompletion.h"
207d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "third_party/WebKit/public/web/WebTextCheckingResult.h"
214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "third_party/WebKit/public/web/WebTextDecorationType.h"
227d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "third_party/WebKit/public/web/WebView.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using blink::WebVector;
2503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)using blink::WebString;
26f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using blink::WebTextCheckingResult;
27f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using blink::WebTextDecorationType;
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class UpdateSpellcheckEnabled : public content::RenderViewVisitor {
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public:
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  explicit UpdateSpellcheckEnabled(bool enabled) : enabled_(enabled) {}
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual bool Visit(content::RenderView* render_view) OVERRIDE;
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private:
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool enabled_;  // New spellcheck-enabled state.
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(UpdateSpellcheckEnabled);
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool UpdateSpellcheckEnabled::Visit(content::RenderView* render_view) {
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SpellCheckProvider* provider = SpellCheckProvider::Get(render_view);
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(provider);
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  provider->EnableSpellcheck(enabled_);
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return true;
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class DocumentMarkersCollector : public content::RenderViewVisitor {
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) public:
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DocumentMarkersCollector() {}
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual ~DocumentMarkersCollector() {}
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const std::vector<uint32>& markers() const { return markers_; }
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual bool Visit(content::RenderView* render_view) OVERRIDE;
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) private:
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<uint32> markers_;
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(DocumentMarkersCollector);
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)};
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool DocumentMarkersCollector::Visit(content::RenderView* render_view) {
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!render_view || !render_view->GetWebView())
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return true;
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  WebVector<uint32> markers;
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  render_view->GetWebView()->spellingMarkers(&markers);
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (size_t i = 0; i < markers.size(); ++i)
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    markers_.push_back(markers[i]);
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Visit all render views.
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return true;
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
7103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)class DocumentMarkersRemover : public content::RenderViewVisitor {
7203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) public:
7303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  explicit DocumentMarkersRemover(const std::vector<std::string>& words);
7403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  virtual ~DocumentMarkersRemover() OVERRIDE {}
7503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  virtual bool Visit(content::RenderView* render_view) OVERRIDE;
7603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
7703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) private:
7803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  WebVector<WebString> words_;
7903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(DocumentMarkersRemover);
8003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)};
8103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
8203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)DocumentMarkersRemover::DocumentMarkersRemover(
8303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    const std::vector<std::string>& words)
8403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    : words_(words.size()) {
8503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  for (size_t i = 0; i < words.size(); ++i)
8603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    words_[i] = WebString::fromUTF8(words[i]);
8703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
8803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
8903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)bool DocumentMarkersRemover::Visit(content::RenderView* render_view) {
9003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if (render_view && render_view->GetWebView())
9103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    render_view->GetWebView()->removeSpellingMarkersUnderWords(words_);
9203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return true;
9303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
9403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class SpellCheck::SpellcheckRequest {
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
99a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  SpellcheckRequest(const base::string16& text,
100f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                    blink::WebTextCheckingCompletion* completion)
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      : text_(text), completion_(completion) {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(completion);
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ~SpellcheckRequest() {}
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
106a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 text() { return text_; }
107f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  blink::WebTextCheckingCompletion* completion() { return completion_; }
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
110a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 text_;  // Text to be checked in this task.
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The interface to send the misspelled ranges to WebKit.
113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  blink::WebTextCheckingCompletion* completion_;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(SpellcheckRequest);
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Initializes SpellCheck object.
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// spellcheck_enabled_ currently MUST be set to true, due to peculiarities of
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// the initialization sequence.
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Since it defaults to true, newly created SpellCheckProviders will enable
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// spellchecking. After the first word is typed, the provider requests a check,
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// which in turn triggers the delayed initialization sequence in SpellCheck.
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This does send a message to the browser side, which triggers the creation
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// of the SpellcheckService. That does create the observer for the preference
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// responsible for enabling/disabling checking, which allows subsequent changes
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// to that preference to be sent to all SpellCheckProviders.
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Setting |spellcheck_enabled_| to false by default prevents that mechanism,
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// and as such the SpellCheckProviders will never be notified of different
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// values.
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// TODO(groby): Simplify this.
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SpellCheck::SpellCheck()
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : auto_spell_correct_turned_on_(false),
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      spellcheck_enabled_(true) {
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SpellCheck::~SpellCheck() {
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SpellCheck::OnControlMessageReceived(const IPC::Message& message) {
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool handled = true;
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  IPC_BEGIN_MESSAGE_MAP(SpellCheck, message)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IPC_MESSAGE_HANDLER(SpellCheckMsg_Init, OnInit)
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    IPC_MESSAGE_HANDLER(SpellCheckMsg_CustomDictionaryChanged,
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        OnCustomDictionaryChanged)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IPC_MESSAGE_HANDLER(SpellCheckMsg_EnableAutoSpellCorrect,
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        OnEnableAutoSpellCorrect)
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    IPC_MESSAGE_HANDLER(SpellCheckMsg_EnableSpellCheck, OnEnableSpellCheck)
150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    IPC_MESSAGE_HANDLER(SpellCheckMsg_RequestDocumentMarkers,
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                        OnRequestDocumentMarkers)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IPC_MESSAGE_UNHANDLED(handled = false)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  IPC_END_MESSAGE_MAP()
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return handled;
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpellCheck::OnInit(IPC::PlatformFileForTransit bdict_file,
15990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                        const std::set<std::string>& custom_words,
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        const std::string& language,
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        bool auto_spell_correct) {
162effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  Init(IPC::PlatformFileForTransitToFile(bdict_file),
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       custom_words, language);
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  auto_spell_correct_turned_on_ = auto_spell_correct;
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if !defined(OS_MACOSX)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PostDelayedSpellCheckTask(pending_request_param_.release());
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellCheck::OnCustomDictionaryChanged(
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::vector<std::string>& words_added,
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::vector<std::string>& words_removed) {
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  custom_dictionary_.OnCustomDictionaryChanged(words_added, words_removed);
17403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  if (words_added.empty())
17503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return;
17603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  DocumentMarkersRemover markersRemover(words_added);
17703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  content::RenderView::ForEach(&markersRemover);
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpellCheck::OnEnableAutoSpellCorrect(bool enable) {
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  auto_spell_correct_turned_on_ = enable;
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void SpellCheck::OnEnableSpellCheck(bool enable) {
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  spellcheck_enabled_ = enable;
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UpdateSpellcheckEnabled updater(enable);
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  content::RenderView::ForEach(&updater);
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
190c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void SpellCheck::OnRequestDocumentMarkers() {
191c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DocumentMarkersCollector collector;
192c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  content::RenderView::ForEach(&collector);
193c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  content::RenderThread::Get()->Send(
194c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      new SpellCheckHostMsg_RespondDocumentMarkers(collector.markers()));
195c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
196c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// TODO(groby): Make sure we always have a spelling engine, even before Init()
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// is called.
199effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid SpellCheck::Init(base::File file,
20090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                      const std::set<std::string>& custom_words,
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      const std::string& language) {
202effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  spellcheck_.Init(file.Pass(), language);
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  custom_dictionary_.Init(custom_words);
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SpellCheck::SpellCheckWord(
2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::char16* in_word,
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int in_word_len,
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int tag,
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int* misspelling_start,
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int* misspelling_len,
212a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    std::vector<base::string16>* optional_suggestions) {
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(in_word_len >= 0);
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(misspelling_start && misspelling_len) << "Out vars must be given.";
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Do nothing if we need to delay initialization. (Rather than blocking,
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // report the word as correctly spelled.)
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (InitializeIfNeeded())
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return spellcheck_.SpellCheckWord(in_word, in_word_len,
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                    tag,
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                    misspelling_start, misspelling_len,
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                    optional_suggestions);
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SpellCheck::SpellCheckParagraph(
228a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::string16& text,
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    WebVector<WebTextCheckingResult>* results) {
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if !defined(OS_MACOSX)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Mac has its own spell checker, so this method will not be used.
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(results);
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<WebTextCheckingResult> textcheck_results;
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t length = text.length();
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t offset = 0;
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Spellcheck::SpellCheckWord() automatically breaks text into words and
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // checks the spellings of the extracted words. This function sets the
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // position and length of the first misspelled word and returns false when
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the text includes misspelled words. Therefore, we just repeat calling the
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // function until it returns true to check the whole text.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int misspelling_start = 0;
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int misspelling_length = 0;
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (offset <= length) {
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (SpellCheckWord(&text[offset],
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       length - offset,
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       0,
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       &misspelling_start,
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       &misspelling_length,
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       NULL)) {
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      results->assign(textcheck_results);
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!custom_dictionary_.SpellCheckWord(
2567dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            text, misspelling_start + offset, misspelling_length)) {
257a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      base::string16 replacement;
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      textcheck_results.push_back(WebTextCheckingResult(
259f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          blink::WebTextDecorationTypeSpelling,
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          misspelling_start + offset,
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          misspelling_length,
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          replacement));
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    offset += misspelling_start + misspelling_length;
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  results->assign(textcheck_results);
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
269c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // This function is only invoked for spell checker functionality that runs
270c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // on the render thread. OSX builds don't have that.
271c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  NOTREACHED();
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
276a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)base::string16 SpellCheck::GetAutoCorrectionWord(const base::string16& word,
277a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                                 int tag) {
278a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 autocorrect_word;
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!auto_spell_correct_turned_on_)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return autocorrect_word;  // Return the empty string.
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int word_length = static_cast<int>(word.size());
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (word_length < 2 ||
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      word_length > chrome::spellcheck_common::kMaxAutoCorrectWordSize)
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return autocorrect_word;
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (InitializeIfNeeded())
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return autocorrect_word;
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::char16 misspelled_word[
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chrome::spellcheck_common::kMaxAutoCorrectWordSize + 1];
2925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const base::char16* word_char = word.c_str();
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i <= chrome::spellcheck_common::kMaxAutoCorrectWordSize;
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++i) {
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (i >= word_length)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      misspelled_word[i] = 0;
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      misspelled_word[i] = word_char[i];
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Swap adjacent characters and spellcheck.
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int misspelling_start, misspelling_len;
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < word_length - 1; i++) {
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Swap.
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::swap(misspelled_word[i], misspelled_word[i + 1]);
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Check spelling.
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    misspelling_start = misspelling_len = 0;
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SpellCheckWord(misspelled_word, word_length, tag, &misspelling_start,
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        &misspelling_len, NULL);
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Make decision: if only one swap produced a valid word, then we want to
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // return it. If we found two or more, we don't do autocorrection.
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (misspelling_len == 0) {
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (autocorrect_word.empty()) {
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        autocorrect_word.assign(misspelled_word);
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        autocorrect_word.clear();
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Restore the swapped characters.
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::swap(misspelled_word[i], misspelled_word[i + 1]);
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return autocorrect_word;
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if !defined(OS_MACOSX)  // OSX uses its own spell checker
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpellCheck::RequestTextChecking(
331a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::string16& text,
332f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    blink::WebTextCheckingCompletion* completion) {
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Clean up the previous request before starting a new request.
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (pending_request_param_.get())
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pending_request_param_->completion()->didCancelCheckingText();
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pending_request_param_.reset(new SpellcheckRequest(
338c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      text, completion));
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We will check this text after we finish loading the hunspell dictionary.
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (InitializeIfNeeded())
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PostDelayedSpellCheckTask(pending_request_param_.release());
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool SpellCheck::InitializeIfNeeded() {
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return spellcheck_.InitializeIfNeeded();
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if !defined(OS_MACOSX) // OSX doesn't have |pending_request_param_|
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpellCheck::PostDelayedSpellCheckTask(SpellcheckRequest* request) {
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!request)
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::MessageLoopProxy::current()->PostTask(FROM_HERE,
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&SpellCheck::PerformSpellCheck,
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 AsWeakPtr(),
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 base::Owned(request)));
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if !defined(OS_MACOSX)  // Mac uses its native engine instead.
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpellCheck::PerformSpellCheck(SpellcheckRequest* param) {
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(param);
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!spellcheck_.IsEnabled()) {
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    param->completion()->didCancelCheckingText();
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
370f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    WebVector<blink::WebTextCheckingResult> results;
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SpellCheckParagraph(param->text(), &results);
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    param->completion()->didFinishCheckingText(results);
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SpellCheck::CreateTextCheckingResults(
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ResultFilter filter,
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int line_offset,
380a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::string16& line_text,
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::vector<SpellCheckResult>& spellcheck_results,
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WebVector<WebTextCheckingResult>* textcheck_results) {
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Double-check misspelled words with our spellchecker and attach grammar
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // markers to them if our spellchecker tells they are correct words, i.e. they
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // are probably contextually-misspelled words.
3865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const base::char16* text = line_text.c_str();
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<WebTextCheckingResult> list;
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < spellcheck_results.size(); ++i) {
3894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    SpellCheckResult::Decoration decoration = spellcheck_results[i].decoration;
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int word_location = spellcheck_results[i].location;
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int word_length = spellcheck_results[i].length;
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int misspelling_start = 0;
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int misspelling_length = 0;
3944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if (decoration == SpellCheckResult::SPELLING &&
3952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        filter == USE_NATIVE_CHECKER) {
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (SpellCheckWord(text + word_location, word_length, 0,
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         &misspelling_start, &misspelling_length, NULL)) {
3984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        decoration = SpellCheckResult::GRAMMAR;
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4017dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if (!custom_dictionary_.SpellCheckWord(
4027dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            line_text, word_location, word_length)) {
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      list.push_back(WebTextCheckingResult(
4044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          static_cast<WebTextDecorationType>(decoration),
4052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          word_location + line_offset,
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          word_length,
40790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          spellcheck_results[i].replacement,
40890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          spellcheck_results[i].hash));
4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  textcheck_results->assign(list);
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
413