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