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