1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "SpellChecker.h"
28
29#include "Document.h"
30#include "DocumentMarkerController.h"
31#include "Editor.h"
32#include "Frame.h"
33#include "HTMLInputElement.h"
34#include "HTMLTextAreaElement.h"
35#include "Node.h"
36#include "Page.h"
37#include "PositionIterator.h"
38#include "RenderObject.h"
39#include "Settings.h"
40#include "TextCheckerClient.h"
41#include "TextCheckingHelper.h"
42#include "htmlediting.h"
43
44namespace WebCore {
45
46SpellCheckRequest::SpellCheckRequest(PassRefPtr<Range> checkingRange, PassRefPtr<Range> paragraphRange, const String& text, TextCheckingTypeMask mask, TextCheckingProcessType processType)
47    : m_checker(0)
48    , m_checkingRange(checkingRange)
49    , m_paragraphRange(paragraphRange)
50    , m_rootEditableElement(m_checkingRange->startContainer()->rootEditableElement())
51    , m_requestData(unrequestedTextCheckingSequence, text, mask, processType)
52{
53}
54
55SpellCheckRequest::~SpellCheckRequest()
56{
57}
58
59// static
60PassRefPtr<SpellCheckRequest> SpellCheckRequest::create(TextCheckingTypeMask textCheckingOptions, TextCheckingProcessType processType, PassRefPtr<Range> checkingRange, PassRefPtr<Range> paragraphRange)
61{
62    ASSERT(checkingRange);
63    ASSERT(paragraphRange);
64
65    String text = checkingRange->text();
66    if (!text.length())
67        return PassRefPtr<SpellCheckRequest>();
68
69    return adoptRef(new SpellCheckRequest(checkingRange, paragraphRange, text, textCheckingOptions, processType));
70}
71
72const TextCheckingRequestData& SpellCheckRequest::data() const
73{
74    return m_requestData;
75}
76
77void SpellCheckRequest::didSucceed(const Vector<TextCheckingResult>& results)
78{
79    if (!m_checker)
80        return;
81    m_checker->didCheckSucceed(m_requestData.sequence(), results);
82    m_checker = 0;
83}
84
85void SpellCheckRequest::didCancel()
86{
87    if (!m_checker)
88        return;
89    m_checker->didCheckCancel(m_requestData.sequence());
90    m_checker = 0;
91}
92
93void SpellCheckRequest::setCheckerAndSequence(SpellChecker* requester, int sequence)
94{
95    ASSERT(!m_checker);
96    ASSERT(m_requestData.sequence() == unrequestedTextCheckingSequence);
97    m_checker = requester;
98    m_requestData.m_sequence = sequence;
99}
100
101void SpellCheckRequest::requesterDestroyed()
102{
103    m_checker = 0;
104}
105
106SpellChecker::SpellChecker(Frame& frame)
107    : m_frame(frame)
108    , m_lastRequestSequence(0)
109    , m_lastProcessedSequence(0)
110    , m_timerToProcessQueuedRequest(this, &SpellChecker::timerFiredToProcessQueuedRequest)
111{
112}
113
114SpellChecker::~SpellChecker()
115{
116    if (m_processingRequest)
117        m_processingRequest->requesterDestroyed();
118    for (RequestQueue::iterator i = m_requestQueue.begin(); i != m_requestQueue.end(); ++i)
119        (*i)->requesterDestroyed();
120}
121
122TextCheckerClient* SpellChecker::client() const
123{
124    Page* page = m_frame.page();
125    if (!page)
126        return 0;
127    return page->editorClient()->textChecker();
128}
129
130void SpellChecker::timerFiredToProcessQueuedRequest(Timer<SpellChecker>*)
131{
132    ASSERT(!m_requestQueue.isEmpty());
133    if (m_requestQueue.isEmpty())
134        return;
135
136    invokeRequest(m_requestQueue.takeFirst());
137}
138
139bool SpellChecker::isAsynchronousEnabled() const
140{
141    return m_frame.settings().asynchronousSpellCheckingEnabled();
142}
143
144bool SpellChecker::canCheckAsynchronously(Range* range) const
145{
146    return client() && isCheckable(range) && isAsynchronousEnabled();
147}
148
149bool SpellChecker::isCheckable(Range* range) const
150{
151    if (!range || !range->firstNode() || !range->firstNode()->renderer())
152        return false;
153    const Node* node = range->startContainer();
154    if (node && node->isElementNode() && !toElement(node)->isSpellCheckingEnabled())
155        return false;
156    return true;
157}
158
159void SpellChecker::requestCheckingFor(PassRefPtr<SpellCheckRequest> request)
160{
161    if (!request || !canCheckAsynchronously(request->paragraphRange().get()))
162        return;
163
164    ASSERT(request->data().sequence() == unrequestedTextCheckingSequence);
165    int sequence = ++m_lastRequestSequence;
166    if (sequence == unrequestedTextCheckingSequence)
167        sequence = ++m_lastRequestSequence;
168
169    request->setCheckerAndSequence(this, sequence);
170
171    if (m_timerToProcessQueuedRequest.isActive() || m_processingRequest) {
172        enqueueRequest(request);
173        return;
174    }
175
176    invokeRequest(request);
177}
178
179void SpellChecker::invokeRequest(PassRefPtr<SpellCheckRequest> request)
180{
181    ASSERT(!m_processingRequest);
182    if (!client())
183        return;
184    m_processingRequest = request;
185    client()->requestCheckingOfString(m_processingRequest);
186}
187
188void SpellChecker::enqueueRequest(PassRefPtr<SpellCheckRequest> request)
189{
190    ASSERT(request);
191
192    for (RequestQueue::iterator it = m_requestQueue.begin(); it != m_requestQueue.end(); ++it) {
193        if (request->rootEditableElement() != (*it)->rootEditableElement())
194            continue;
195
196        *it = request;
197        return;
198    }
199
200    m_requestQueue.append(request);
201}
202
203void SpellChecker::didCheck(int sequence, const Vector<TextCheckingResult>& results)
204{
205    ASSERT(m_processingRequest);
206    ASSERT(m_processingRequest->data().sequence() == sequence);
207    if (m_processingRequest->data().sequence() != sequence) {
208        m_requestQueue.clear();
209        return;
210    }
211
212    m_frame.editor().markAndReplaceFor(m_processingRequest, results);
213
214    if (m_lastProcessedSequence < sequence)
215        m_lastProcessedSequence = sequence;
216
217    m_processingRequest.clear();
218    if (!m_requestQueue.isEmpty())
219        m_timerToProcessQueuedRequest.startOneShot(0);
220}
221
222void SpellChecker::didCheckSucceed(int sequence, const Vector<TextCheckingResult>& results)
223{
224    TextCheckingRequestData requestData = m_processingRequest->data();
225    if (requestData.sequence() == sequence) {
226        unsigned markers = 0;
227        if (requestData.mask() & TextCheckingTypeSpelling)
228            markers |= DocumentMarker::Spelling;
229        if (requestData.mask() & TextCheckingTypeGrammar)
230            markers |= DocumentMarker::Grammar;
231        if (markers)
232            m_frame.document()->markers().removeMarkers(m_processingRequest->checkingRange().get(), markers);
233    }
234    didCheck(sequence, results);
235}
236
237void SpellChecker::didCheckCancel(int sequence)
238{
239    Vector<TextCheckingResult> results;
240    didCheck(sequence, results);
241}
242
243} // namespace WebCore
244