ce1a9efd6d0e9208f68cb9ccb4bf1bdd3831bcc6
[WebKit-https.git] / Source / WebCore / editing / SpellChecker.cpp
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 "Editing.h"
32 #include "Editor.h"
33 #include "Frame.h"
34 #include "Page.h"
35 #include "PositionIterator.h"
36 #include "RenderObject.h"
37 #include "Settings.h"
38 #include "TextCheckerClient.h"
39 #include "TextCheckingHelper.h"
40
41 namespace WebCore {
42
43 SpellCheckRequest::SpellCheckRequest(Ref<Range>&& checkingRange, Ref<Range>&& automaticReplacementRange, Ref<Range>&& paragraphRange, const String& text, OptionSet<TextCheckingType> mask, TextCheckingProcessType processType)
44     : m_checkingRange(WTFMove(checkingRange))
45     , m_automaticReplacementRange(WTFMove(automaticReplacementRange))
46     , m_paragraphRange(WTFMove(paragraphRange))
47     , m_rootEditableElement(m_checkingRange->startContainer().rootEditableElement())
48     , m_requestData(unrequestedTextCheckingSequence, text, mask, processType)
49 {
50 }
51
52 SpellCheckRequest::~SpellCheckRequest() = default;
53
54 RefPtr<SpellCheckRequest> SpellCheckRequest::create(OptionSet<TextCheckingType> textCheckingOptions, TextCheckingProcessType processType, Ref<Range>&& checkingRange, Ref<Range>&& automaticReplacementRange, Ref<Range>&& paragraphRange)
55 {
56     String text = checkingRange->text();
57     if (!text.length())
58         return nullptr;
59
60     return adoptRef(*new SpellCheckRequest(WTFMove(checkingRange), WTFMove(automaticReplacementRange), WTFMove(paragraphRange), text, textCheckingOptions, processType));
61 }
62
63 const TextCheckingRequestData& SpellCheckRequest::data() const
64 {
65     return m_requestData;
66 }
67
68 void SpellCheckRequest::didSucceed(const Vector<TextCheckingResult>& results)
69 {
70     if (!m_checker)
71         return;
72
73     Ref<SpellCheckRequest> protectedThis(*this);
74     m_checker->didCheckSucceed(m_requestData.sequence(), results);
75     m_checker = nullptr;
76 }
77
78 void SpellCheckRequest::didCancel()
79 {
80     if (!m_checker)
81         return;
82
83     Ref<SpellCheckRequest> protectedThis(*this);
84     m_checker->didCheckCancel(m_requestData.sequence());
85     m_checker = nullptr;
86 }
87
88 void SpellCheckRequest::setCheckerAndSequence(SpellChecker* requester, int sequence)
89 {
90     ASSERT(!m_checker);
91     ASSERT(m_requestData.sequence() == unrequestedTextCheckingSequence);
92     m_checker = requester;
93     m_requestData.m_sequence = sequence;
94 }
95
96 void SpellCheckRequest::requesterDestroyed()
97 {
98     m_checker = nullptr;
99 }
100
101 SpellChecker::SpellChecker(Frame& frame)
102     : m_frame(frame)
103     , m_lastRequestSequence(0)
104     , m_lastProcessedSequence(0)
105     , m_timerToProcessQueuedRequest(*this, &SpellChecker::timerFiredToProcessQueuedRequest)
106 {
107 }
108
109 SpellChecker::~SpellChecker()
110 {
111     if (m_processingRequest)
112         m_processingRequest->requesterDestroyed();
113     for (auto& queue : m_requestQueue)
114         queue->requesterDestroyed();
115 }
116
117 TextCheckerClient* SpellChecker::client() const
118 {
119     Page* page = m_frame.page();
120     if (!page)
121         return nullptr;
122     return page->editorClient().textChecker();
123 }
124
125 void SpellChecker::timerFiredToProcessQueuedRequest()
126 {
127     ASSERT(!m_requestQueue.isEmpty());
128     if (m_requestQueue.isEmpty())
129         return;
130
131     invokeRequest(m_requestQueue.takeFirst());
132 }
133
134 bool SpellChecker::isAsynchronousEnabled() const
135 {
136     return m_frame.settings().asynchronousSpellCheckingEnabled();
137 }
138
139 bool SpellChecker::canCheckAsynchronously(Range& range) const
140 {
141     return client() && isCheckable(range) && isAsynchronousEnabled();
142 }
143
144 bool SpellChecker::isCheckable(Range& range) const
145 {
146     if (!range.firstNode() || !range.firstNode()->renderer())
147         return false;
148     const Node& node = range.startContainer();
149     if (is<Element>(node) && !downcast<Element>(node).isSpellCheckingEnabled())
150         return false;
151     return true;
152 }
153
154 void SpellChecker::requestCheckingFor(Ref<SpellCheckRequest>&& request)
155 {
156     if (!canCheckAsynchronously(request->paragraphRange()))
157         return;
158
159     ASSERT(request->data().sequence() == unrequestedTextCheckingSequence);
160     int sequence = ++m_lastRequestSequence;
161     if (sequence == unrequestedTextCheckingSequence)
162         sequence = ++m_lastRequestSequence;
163
164     request->setCheckerAndSequence(this, sequence);
165
166     if (m_timerToProcessQueuedRequest.isActive() || m_processingRequest) {
167         enqueueRequest(WTFMove(request));
168         return;
169     }
170
171     invokeRequest(WTFMove(request));
172 }
173
174 void SpellChecker::invokeRequest(Ref<SpellCheckRequest>&& request)
175 {
176     ASSERT(!m_processingRequest);
177     if (!client())
178         return;
179     m_processingRequest = WTFMove(request);
180     client()->requestCheckingOfString(*m_processingRequest, m_frame.selection().selection());
181 }
182
183 void SpellChecker::enqueueRequest(Ref<SpellCheckRequest>&& request)
184 {
185     for (auto& queue : m_requestQueue) {
186         if (request->rootEditableElement() != queue->rootEditableElement())
187             continue;
188
189         queue = WTFMove(request);
190         return;
191     }
192
193     m_requestQueue.append(WTFMove(request));
194 }
195
196 void SpellChecker::didCheck(int sequence, const Vector<TextCheckingResult>& results)
197 {
198     ASSERT(m_processingRequest);
199     ASSERT(m_processingRequest->data().sequence() == sequence);
200     if (m_processingRequest->data().sequence() != sequence) {
201         m_requestQueue.clear();
202         return;
203     }
204
205     m_frame.editor().markAndReplaceFor(*m_processingRequest, results);
206
207     if (m_lastProcessedSequence < sequence)
208         m_lastProcessedSequence = sequence;
209
210     m_processingRequest = nullptr;
211     if (!m_requestQueue.isEmpty())
212         m_timerToProcessQueuedRequest.startOneShot(0_s);
213 }
214
215 void SpellChecker::didCheckSucceed(int sequence, const Vector<TextCheckingResult>& results)
216 {
217     TextCheckingRequestData requestData = m_processingRequest->data();
218     if (requestData.sequence() == sequence) {
219         OptionSet<DocumentMarker::MarkerType> markerTypes;
220         if (requestData.checkingTypes().contains(TextCheckingType::Spelling))
221             markerTypes |= DocumentMarker::Spelling;
222         if (requestData.checkingTypes().contains(TextCheckingType::Grammar))
223             markerTypes |= DocumentMarker::Grammar;
224         if (!markerTypes.isEmpty())
225             m_frame.document()->markers().removeMarkers(&m_processingRequest->checkingRange(), markerTypes);
226     }
227     didCheck(sequence, results);
228 }
229
230 void SpellChecker::didCheckCancel(int sequence)
231 {
232     didCheck(sequence, Vector<TextCheckingResult>());
233 }
234
235 } // namespace WebCore