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 37namespace blink { 38 39SpellCheckRequest::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 57SpellCheckRequest::~SpellCheckRequest() 58{ 59} 60 61void 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 71PassRefPtrWillBeRawPtr<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 91const TextCheckingRequestData& SpellCheckRequest::data() const 92{ 93 return m_requestData; 94} 95 96void 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 105void 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 114void 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) 123void SpellCheckRequest::requesterDestroyed() 124{ 125 m_requester = nullptr; 126} 127#endif 128 129SpellCheckRequester::SpellCheckRequester(LocalFrame& frame) 130 : m_frame(&frame) 131 , m_lastRequestSequence(0) 132 , m_lastProcessedSequence(0) 133 , m_timerToProcessQueuedRequest(this, &SpellCheckRequester::timerFiredToProcessQueuedRequest) 134{ 135} 136 137SpellCheckRequester::~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 147TextCheckerClient& SpellCheckRequester::client() const 148{ 149 return frame().spellChecker().textChecker(); 150} 151 152void 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 161bool SpellCheckRequester::isAsynchronousEnabled() const 162{ 163 return frame().settings() && frame().settings()->asynchronousSpellCheckingEnabled(); 164} 165 166bool SpellCheckRequester::canCheckAsynchronously(Range* range) const 167{ 168 return isCheckable(range) && isAsynchronousEnabled(); 169} 170 171bool 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 181void 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 201void SpellCheckRequester::cancelCheck() 202{ 203 if (m_processingRequest) 204 m_processingRequest->didCancel(); 205} 206 207void SpellCheckRequester::invokeRequest(PassRefPtrWillBeRawPtr<SpellCheckRequest> request) 208{ 209 ASSERT(!m_processingRequest); 210 m_processingRequest = request; 211 client().requestCheckingOfString(m_processingRequest); 212} 213 214void 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 240void 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 259void 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 273void SpellCheckRequester::didCheckCancel(int sequence) 274{ 275 Vector<TextCheckingResult> results; 276 didCheck(sequence, results); 277} 278 279void 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