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