1/*
2 * Copyright (C) 2006, 2007 Apple, Inc.  All rights reserved.
3 * Copyright (C) 2012 Google, Inc.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "web/SpellCheckerClientImpl.h"
29
30#include "core/dom/DocumentMarkerController.h"
31#include "core/editing/Editor.h"
32#include "core/editing/SpellChecker.h"
33#include "core/frame/LocalFrame.h"
34#include "core/frame/Settings.h"
35#include "core/page/Page.h"
36#include "public/web/WebSpellCheckClient.h"
37#include "public/web/WebTextCheckingResult.h"
38#include "web/WebTextCheckingCompletionImpl.h"
39#include "web/WebViewImpl.h"
40
41namespace blink {
42
43SpellCheckerClientImpl::SpellCheckerClientImpl(WebViewImpl* webview)
44    : m_webView(webview)
45    , m_spellCheckThisFieldStatus(SpellCheckAutomatic)
46{
47}
48
49SpellCheckerClientImpl::~SpellCheckerClientImpl()
50{
51}
52
53bool SpellCheckerClientImpl::shouldSpellcheckByDefault()
54{
55    // Spellcheck should be enabled for all editable areas (such as textareas,
56    // contentEditable regions, designMode docs and inputs).
57    if (!m_webView->focusedCoreFrame()->isLocalFrame())
58        return false;
59    const LocalFrame* frame = toLocalFrame(m_webView->focusedCoreFrame());
60    if (!frame)
61        return false;
62    if (frame->spellChecker().isSpellCheckingEnabledInFocusedNode())
63        return true;
64    const Document* document = frame->document();
65    if (!document)
66        return false;
67    const Element* element = document->focusedElement();
68    // If |element| is null, we default to allowing spellchecking. This is done
69    // in order to mitigate the issue when the user clicks outside the textbox,
70    // as a result of which |element| becomes null, resulting in all the spell
71    // check markers being deleted. Also, the LocalFrame will decide not to do
72    // spellchecking if the user can't edit - so returning true here will not
73    // cause any problems to the LocalFrame's behavior.
74    if (!element)
75        return true;
76    const RenderObject* renderer = element->renderer();
77    if (!renderer)
78        return false;
79
80    return true;
81}
82
83bool SpellCheckerClientImpl::isContinuousSpellCheckingEnabled()
84{
85    if (m_spellCheckThisFieldStatus == SpellCheckForcedOff)
86        return false;
87    if (m_spellCheckThisFieldStatus == SpellCheckForcedOn)
88        return true;
89    return shouldSpellcheckByDefault();
90}
91
92void SpellCheckerClientImpl::toggleContinuousSpellChecking()
93{
94    if (isContinuousSpellCheckingEnabled()) {
95        m_spellCheckThisFieldStatus = SpellCheckForcedOff;
96        if (Page* page = m_webView->page()) {
97            for (Frame* frame = page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
98                if (!frame->isLocalFrame())
99                    continue;
100                toLocalFrame(frame)->document()->markers().removeMarkers(DocumentMarker::MisspellingMarkers());
101            }
102        }
103    } else {
104        m_spellCheckThisFieldStatus = SpellCheckForcedOn;
105        if (m_webView->focusedCoreFrame()->isLocalFrame()) {
106            if (LocalFrame* frame = toLocalFrame(m_webView->focusedCoreFrame())) {
107                VisibleSelection frameSelection = frame->selection().selection();
108                // If a selection is in an editable element spell check its content.
109                if (Element* rootEditableElement = frameSelection.rootEditableElement()) {
110                    frame->spellChecker().didBeginEditing(rootEditableElement);
111                }
112            }
113        }
114    }
115}
116
117bool SpellCheckerClientImpl::isGrammarCheckingEnabled()
118{
119    const LocalFrame* frame = toLocalFrame(m_webView->focusedCoreFrame());
120    return frame && frame->settings() && (frame->settings()->asynchronousSpellCheckingEnabled() || frame->settings()->unifiedTextCheckerEnabled());
121}
122
123bool SpellCheckerClientImpl::shouldEraseMarkersAfterChangeSelection(TextCheckingType type) const
124{
125    const Frame* frame = m_webView->focusedCoreFrame();
126    return !frame || !frame->settings() || (!frame->settings()->asynchronousSpellCheckingEnabled() && !frame->settings()->unifiedTextCheckerEnabled());
127}
128
129void SpellCheckerClientImpl::checkSpellingOfString(const String& text, int* misspellingLocation, int* misspellingLength)
130{
131    // SpellCheckWord will write (0, 0) into the output vars, which is what our
132    // caller expects if the word is spelled correctly.
133    int spellLocation = -1;
134    int spellLength = 0;
135
136    // Check to see if the provided text is spelled correctly.
137    if (m_webView->spellCheckClient()) {
138        m_webView->spellCheckClient()->spellCheck(text, spellLocation, spellLength, 0);
139    } else {
140        spellLocation = 0;
141        spellLength = 0;
142    }
143
144    // Note: the Mac code checks if the pointers are null before writing to them,
145    // so we do too.
146    if (misspellingLocation)
147        *misspellingLocation = spellLocation;
148    if (misspellingLength)
149        *misspellingLength = spellLength;
150}
151
152void SpellCheckerClientImpl::requestCheckingOfString(PassRefPtrWillBeRawPtr<TextCheckingRequest> request)
153{
154    if (m_webView->spellCheckClient()) {
155        const String& text = request->data().text();
156        const Vector<uint32_t>& markers = request->data().markers();
157        const Vector<unsigned>& markerOffsets = request->data().offsets();
158        m_webView->spellCheckClient()->requestCheckingOfText(text, markers, markerOffsets, new WebTextCheckingCompletionImpl(request));
159    }
160}
161
162String SpellCheckerClientImpl::getAutoCorrectSuggestionForMisspelledWord(const String& misspelledWord)
163{
164    if (!(isContinuousSpellCheckingEnabled() && m_webView->client()))
165        return String();
166
167    // Do not autocorrect words with capital letters in it except the
168    // first letter. This will remove cases changing "IMB" to "IBM".
169    for (size_t i = 1; i < misspelledWord.length(); i++) {
170        if (u_isupper(static_cast<UChar32>(misspelledWord[i])))
171            return String();
172    }
173
174    if (m_webView->spellCheckClient())
175        return m_webView->spellCheckClient()->autoCorrectWord(WebString(misspelledWord));
176    return String();
177}
178
179void SpellCheckerClientImpl::checkGrammarOfString(const String& text, WTF::Vector<GrammarDetail>& details, int* badGrammarLocation, int* badGrammarLength)
180{
181    if (badGrammarLocation)
182        *badGrammarLocation = -1;
183    if (badGrammarLength)
184        *badGrammarLength = 0;
185
186    if (!m_webView->spellCheckClient())
187        return;
188    WebVector<WebTextCheckingResult> webResults;
189    m_webView->spellCheckClient()->checkTextOfParagraph(text, WebTextCheckingTypeGrammar, &webResults);
190    if (!webResults.size())
191        return;
192
193    // Convert a list of WebTextCheckingResults to a list of GrammarDetails. If
194    // the converted vector of GrammarDetails has grammar errors, we set
195    // badGrammarLocation and badGrammarLength to tell WebKit that the input
196    // text has grammar errors.
197    for (size_t i = 0; i < webResults.size(); ++i) {
198        if (webResults[i].decoration == WebTextDecorationTypeGrammar) {
199            GrammarDetail detail;
200            detail.location = webResults[i].location;
201            detail.length = webResults[i].length;
202            detail.userDescription = webResults[i].replacement;
203            details.append(detail);
204        }
205    }
206    if (!details.size())
207        return;
208    if (badGrammarLocation)
209        *badGrammarLocation = 0;
210    if (badGrammarLength)
211        *badGrammarLength = text.length();
212}
213
214void SpellCheckerClientImpl::updateSpellingUIWithMisspelledWord(const String& misspelledWord)
215{
216    if (m_webView->spellCheckClient())
217        m_webView->spellCheckClient()->updateSpellingUIWithMisspelledWord(WebString(misspelledWord));
218}
219
220void SpellCheckerClientImpl::showSpellingUI(bool show)
221{
222    if (m_webView->spellCheckClient())
223        m_webView->spellCheckClient()->showSpellingUI(show);
224}
225
226bool SpellCheckerClientImpl::spellingUIIsShowing()
227{
228    if (m_webView->spellCheckClient())
229        return m_webView->spellCheckClient()->isShowingSpellingUI();
230    return false;
231}
232
233} // namespace blink
234