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 COMPUTER, 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 COMPUTER, 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 "core/editing/SpellCheckRequester.h" 28 29 #include "core/dom/Document.h" 30 #include "core/dom/DocumentMarkerController.h" 31 #include "core/dom/Node.h" 32 #include "core/editing/SpellChecker.h" 33 #include "core/frame/LocalFrame.h" 34 #include "core/frame/Settings.h" 35 #include "platform/text/TextCheckerClient.h" 36 37 namespace WebCore { 38 39 SpellCheckRequest::SpellCheckRequest( 40 PassRefPtrWillBeRawPtr<Range> checkingRange, 41 PassRefPtrWillBeRawPtr<Range> paragraphRange, 42 const String& text, 43 TextCheckingTypeMask mask, 44 TextCheckingProcessType processType, 45 const Vector<uint32_t>& documentMarkersInRange, 46 const Vector<unsigned>& documentMarkerOffsets, 47 int requestNumber) 48 : m_requester(0) 49 , m_checkingRange(checkingRange) 50 , m_paragraphRange(paragraphRange) 51 , m_rootEditableElement(m_checkingRange->startContainer()->rootEditableElement()) 52 , m_requestData(unrequestedTextCheckingSequence, text, mask, processType, documentMarkersInRange, documentMarkerOffsets) 53 , m_requestNumber(requestNumber) 54 { 55 } 56 57 SpellCheckRequest::~SpellCheckRequest() 58 { 59 } 60 61 // static 62 PassRefPtr<SpellCheckRequest> SpellCheckRequest::create(TextCheckingTypeMask textCheckingOptions, TextCheckingProcessType processType, PassRefPtrWillBeRawPtr<Range> checkingRange, PassRefPtrWillBeRawPtr<Range> paragraphRange, int requestNumber) 63 { 64 ASSERT(checkingRange); 65 ASSERT(paragraphRange); 66 67 String text = checkingRange->text(); 68 if (!text.length()) 69 return PassRefPtr<SpellCheckRequest>(); 70 71 const WillBeHeapVector<DocumentMarker*>& markers = checkingRange->ownerDocument().markers().markersInRange(checkingRange.get(), DocumentMarker::SpellCheckClientMarkers()); 72 Vector<uint32_t> hashes(markers.size()); 73 Vector<unsigned> offsets(markers.size()); 74 for (size_t i = 0; i < markers.size(); i++) { 75 hashes[i] = markers[i]->hash(); 76 offsets[i] = markers[i]->startOffset(); 77 } 78 79 return adoptRef(new SpellCheckRequest(checkingRange, paragraphRange, text, textCheckingOptions, processType, hashes, offsets, requestNumber)); 80 } 81 82 const TextCheckingRequestData& SpellCheckRequest::data() const 83 { 84 return m_requestData; 85 } 86 87 void SpellCheckRequest::didSucceed(const Vector<TextCheckingResult>& results) 88 { 89 if (!m_requester) 90 return; 91 SpellCheckRequester* requester = m_requester; 92 m_requester = 0; 93 requester->didCheckSucceed(m_requestData.sequence(), results); 94 } 95 96 void SpellCheckRequest::didCancel() 97 { 98 if (!m_requester) 99 return; 100 SpellCheckRequester* requester = m_requester; 101 m_requester = 0; 102 requester->didCheckCancel(m_requestData.sequence()); 103 } 104 105 void SpellCheckRequest::setCheckerAndSequence(SpellCheckRequester* requester, int sequence) 106 { 107 ASSERT(!m_requester); 108 ASSERT(m_requestData.sequence() == unrequestedTextCheckingSequence); 109 m_requester = requester; 110 m_requestData.m_sequence = sequence; 111 } 112 113 void SpellCheckRequest::requesterDestroyed() 114 { 115 m_requester = 0; 116 } 117 118 SpellCheckRequester::SpellCheckRequester(LocalFrame& frame) 119 : m_frame(frame) 120 , m_lastRequestSequence(0) 121 , m_lastProcessedSequence(0) 122 , m_timerToProcessQueuedRequest(this, &SpellCheckRequester::timerFiredToProcessQueuedRequest) 123 { 124 } 125 126 SpellCheckRequester::~SpellCheckRequester() 127 { 128 if (m_processingRequest) 129 m_processingRequest->requesterDestroyed(); 130 for (RequestQueue::iterator i = m_requestQueue.begin(); i != m_requestQueue.end(); ++i) 131 (*i)->requesterDestroyed(); 132 } 133 134 TextCheckerClient& SpellCheckRequester::client() const 135 { 136 return m_frame.spellChecker().textChecker(); 137 } 138 139 void SpellCheckRequester::timerFiredToProcessQueuedRequest(Timer<SpellCheckRequester>*) 140 { 141 ASSERT(!m_requestQueue.isEmpty()); 142 if (m_requestQueue.isEmpty()) 143 return; 144 145 invokeRequest(m_requestQueue.takeFirst()); 146 } 147 148 bool SpellCheckRequester::isAsynchronousEnabled() const 149 { 150 return m_frame.settings() && m_frame.settings()->asynchronousSpellCheckingEnabled(); 151 } 152 153 bool SpellCheckRequester::canCheckAsynchronously(Range* range) const 154 { 155 return isCheckable(range) && isAsynchronousEnabled(); 156 } 157 158 bool SpellCheckRequester::isCheckable(Range* range) const 159 { 160 if (!range || !range->firstNode() || !range->firstNode()->renderer()) 161 return false; 162 const Node* node = range->startContainer(); 163 if (node && node->isElementNode() && !toElement(node)->isSpellCheckingEnabled()) 164 return false; 165 return true; 166 } 167 168 void SpellCheckRequester::requestCheckingFor(PassRefPtr<SpellCheckRequest> request) 169 { 170 if (!request || !canCheckAsynchronously(request->paragraphRange().get())) 171 return; 172 173 ASSERT(request->data().sequence() == unrequestedTextCheckingSequence); 174 int sequence = ++m_lastRequestSequence; 175 if (sequence == unrequestedTextCheckingSequence) 176 sequence = ++m_lastRequestSequence; 177 178 request->setCheckerAndSequence(this, sequence); 179 180 if (m_timerToProcessQueuedRequest.isActive() || m_processingRequest) { 181 enqueueRequest(request); 182 return; 183 } 184 185 invokeRequest(request); 186 } 187 188 void SpellCheckRequester::cancelCheck() 189 { 190 if (m_processingRequest) 191 m_processingRequest->didCancel(); 192 } 193 194 void SpellCheckRequester::invokeRequest(PassRefPtr<SpellCheckRequest> request) 195 { 196 ASSERT(!m_processingRequest); 197 m_processingRequest = request; 198 client().requestCheckingOfString(m_processingRequest); 199 } 200 201 void SpellCheckRequester::enqueueRequest(PassRefPtr<SpellCheckRequest> request) 202 { 203 ASSERT(request); 204 bool continuation = false; 205 if (!m_requestQueue.isEmpty()) { 206 RefPtr<SpellCheckRequest> lastRequest = m_requestQueue.last(); 207 // It's a continuation if the number of the last request got incremented in the new one and 208 // both apply to the same editable. 209 continuation = request->rootEditableElement() == lastRequest->rootEditableElement() 210 && request->requestNumber() == lastRequest->requestNumber() + 1; 211 } 212 213 // Spellcheck requests for chunks of text in the same element should not overwrite each other. 214 if (!continuation) { 215 for (RequestQueue::iterator it = m_requestQueue.begin(); it != m_requestQueue.end(); ++it) { 216 if (request->rootEditableElement() != (*it)->rootEditableElement()) 217 continue; 218 219 *it = request; 220 return; 221 } 222 } 223 224 m_requestQueue.append(request); 225 } 226 227 void SpellCheckRequester::didCheck(int sequence, const Vector<TextCheckingResult>& results) 228 { 229 ASSERT(m_processingRequest); 230 ASSERT(m_processingRequest->data().sequence() == sequence); 231 if (m_processingRequest->data().sequence() != sequence) { 232 m_requestQueue.clear(); 233 return; 234 } 235 236 m_frame.spellChecker().markAndReplaceFor(m_processingRequest, results); 237 238 if (m_lastProcessedSequence < sequence) 239 m_lastProcessedSequence = sequence; 240 241 m_processingRequest.clear(); 242 if (!m_requestQueue.isEmpty()) 243 m_timerToProcessQueuedRequest.startOneShot(0, FROM_HERE); 244 } 245 246 void SpellCheckRequester::didCheckSucceed(int sequence, const Vector<TextCheckingResult>& results) 247 { 248 TextCheckingRequestData requestData = m_processingRequest->data(); 249 if (requestData.sequence() == sequence) { 250 DocumentMarker::MarkerTypes markers = DocumentMarker::SpellCheckClientMarkers(); 251 if (!requestData.maskContains(TextCheckingTypeSpelling)) 252 markers.remove(DocumentMarker::Spelling); 253 if (!requestData.maskContains(TextCheckingTypeGrammar)) 254 markers.remove(DocumentMarker::Grammar); 255 m_frame.document()->markers().removeMarkers(m_processingRequest->checkingRange().get(), markers); 256 } 257 didCheck(sequence, results); 258 } 259 260 void SpellCheckRequester::didCheckCancel(int sequence) 261 { 262 Vector<TextCheckingResult> results; 263 didCheck(sequence, results); 264 } 265 266 } // namespace WebCore 267