Source/WebCore/ChangeLog

 12011-11-30 Shinya Kawanaka <shinyak@google.com>
 2
 3 Asynchronous SpellChecker should consider multiple requests.
 4 https://bugs.webkit.org/show_bug.cgi?id=72939
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Now SpellChecker saves a request when it is processing the previous spellcheck request.
 9 Currently only the latest request is saved.
 10
 11 Test: editing/spelling/spellcheck-queue.html
 12
 13 * editing/SpellChecker.cpp:
 14 (WebCore::SpellChecker::SpellCheckRequest::SpellCheckRequest):
 15 A structure to have spell check request.
 16 (WebCore::SpellChecker::SpellCheckRequest::sequence):
 17 (WebCore::SpellChecker::SpellCheckRequest::range):
 18 (WebCore::SpellChecker::SpellCheckRequest::text):
 19 (WebCore::SpellChecker::SpellCheckRequest::mask):
 20 (WebCore::SpellChecker::SpellChecker):
 21 (WebCore::SpellChecker::createRequest):
 22 (WebCore::SpellChecker::timerFiredToProcessQueuedRequest):
 23 When timer is fired, queued request is processed if any.
 24 (WebCore::SpellChecker::processQueuedRequest):
 25 Sets a timer.
 26 (WebCore::SpellChecker::isAsynchronousEnabled):
 27 (WebCore::SpellChecker::canCheckAsynchronously):
 28 (WebCore::SpellChecker::doRequestCheckingFor):
 29 When the spellchecker is processing another request, the latest request is queued.
 30 (WebCore::SpellChecker::didCheck):
 31 * editing/SpellChecker.h:
 32
1332011-11-30 Leo Yang <leo.yang@torchmobile.com.cn>
234
335 [BlackBerry] Add 2 cpp files to the BlackBerry build system

Source/WebCore/editing/SpellChecker.cpp

4545
4646namespace WebCore {
4747
 48class SpellChecker::SpellCheckRequest {
 49public:
 50 SpellCheckRequest(int sequence, PassRefPtr<Range> range, const String& text, TextCheckingTypeMask mask)
 51 : m_sequence(sequence)
 52 , m_range(range)
 53 , m_text(text)
 54 , m_mask(mask)
 55 {
 56 }
 57
 58 int sequence() const { return m_sequence; }
 59 Range* range() const { return m_range.get(); }
 60 const String& text() const { return m_text; }
 61 TextCheckingTypeMask mask() const { return m_mask; }
 62
 63private:
 64 int m_sequence;
 65 RefPtr<Range> m_range;
 66 String m_text;
 67 TextCheckingTypeMask m_mask;
 68};
 69
4870SpellChecker::SpellChecker(Frame* frame)
4971 : m_frame(frame)
50  , m_requestSequence(0)
 72 , m_lastRequestedSequence(0)
 73 , m_timerToProcessQueuedRequest(this, &SpellChecker::timerFiredToProcessQueuedRequest)
5174{
5275}
5376

@@TextCheckerClient* SpellChecker::client() const
6386 return page->editorClient()->textChecker();
6487}
6588
66 bool SpellChecker::initRequest(PassRefPtr<Range> range)
 89PassOwnPtr<SpellChecker::SpellCheckRequest> SpellChecker::createRequest(TextCheckingTypeMask mask, PassRefPtr<Range> range)
6790{
6891 ASSERT(canCheckAsynchronously(range.get()));
6992
7093 String text = range->text();
7194 if (!text.length())
72  return false;
73 
74  m_requestRange = range;
75  m_requestText = text;
76  m_requestSequence++;
 95 return PassOwnPtr<SpellCheckRequest>();
7796
78  return true;
 97 return adoptPtr(new SpellCheckRequest(++m_lastRequestedSequence, range, text, mask));
7998}
8099
81 void SpellChecker::clearRequest()
 100void SpellChecker::timerFiredToProcessQueuedRequest(Timer<SpellChecker>*)
82101{
83  m_requestRange.clear();
84  m_requestText = String();
85 }
 102 if (!m_queuedRequest.get())
 103 return;
86104
87 bool SpellChecker::isAsynchronousEnabled() const
88 {
89  return m_frame->settings() && m_frame->settings()->asynchronousSpellCheckingEnabled();
 105 m_processingRequest = m_queuedRequest.release();
 106 client()->requestCheckingOfString(this, m_processingRequest->sequence(), m_processingRequest->mask(), m_processingRequest->text());
90107}
91108
92 bool SpellChecker::canCheckAsynchronously(Range* range) const
 109void SpellChecker::processQueuedRequest()
93110{
94  return client() && isCheckable(range) && isAsynchronousEnabled() && !isBusy();
 111 m_processingRequest.clear();
 112 if (!m_queuedRequest.get())
 113 return;
 114
 115 m_timerToProcessQueuedRequest.startOneShot(0);
95116}
96117
97 bool SpellChecker::isBusy() const
 118bool SpellChecker::isAsynchronousEnabled() const
98119{
99  return m_requestRange.get();
 120 return m_frame->settings() && m_frame->settings()->asynchronousSpellCheckingEnabled();
100121}
101122
102 bool SpellChecker::isValid(int sequence) const
 123bool SpellChecker::canCheckAsynchronously(Range* range) const
103124{
104  return m_requestRange.get() && m_requestText.length() && m_requestSequence == sequence;
 125 return client() && isCheckable(range) && isAsynchronousEnabled();
105126}
106127
107128bool SpellChecker::isCheckable(Range* range) const

@@void SpellChecker::doRequestCheckingFor(TextCheckingTypeMask mask, PassRefPtr<Ra
121142{
122143 ASSERT(canCheckAsynchronously(range.get()));
123144
124  if (!initRequest(range))
 145 OwnPtr<SpellCheckRequest> request(createRequest(mask, range));
 146 if (!request)
125147 return;
126  client()->requestCheckingOfString(this, m_requestSequence, mask, m_requestText);
 148
 149 if (m_processingRequest) {
 150 m_queuedRequest = request.release();
 151 return;
 152 }
 153
 154 client()->requestCheckingOfString(this, request->sequence(), request->mask(), request->text());
 155 m_processingRequest = request.release();
127156}
128157
129158static bool forwardIterator(PositionIterator& iterator, int distance)

@@static DocumentMarker::MarkerType toMarkerType(TextCheckingType type)
159188// Currenntly ignoring TextCheckingResult::details but should be handled. See Bug 56368.
160189void SpellChecker::didCheck(int sequence, const Vector<TextCheckingResult>& results)
161190{
162  if (!isValid(sequence))
163  return;
 191 ASSERT(m_processingRequest);
164192
165  if (!isCheckable(m_requestRange.get())) {
166  clearRequest();
 193 if (m_processingRequest->sequence() != sequence)
167194 return;
168  }
169195
170196 int startOffset = 0;
171  PositionIterator start = m_requestRange->startPosition();
 197 PositionIterator start = m_processingRequest->range()->startPosition();
172198 for (size_t i = 0; i < results.size(); ++i) {
173199 if (results[i].type != TextCheckingTypeSpelling && results[i].type != TextCheckingTypeGrammar)
174200 continue;

@@void SpellChecker::didCheck(int sequence, const Vector<TextCheckingResult>& resu
186212 // spellings in the background. To avoid adding markers to the words modified by users or
187213 // JavaScript applications, retrieve the words in the specified region and compare them with
188214 // the original ones.
189  RefPtr<Range> range = Range::create(m_requestRange->ownerDocument(), start, end);
 215 RefPtr<Range> range = Range::create(m_processingRequest->range()->ownerDocument(), start, end);
190216 // FIXME: Use textContent() compatible string conversion.
191217 String destination = range->text();
192  String source = m_requestText.substring(results[i].location, results[i].length);
 218 String source = m_processingRequest->text().substring(results[i].location, results[i].length);
193219 if (destination == source)
194  m_requestRange->ownerDocument()->markers()->addMarker(range.get(), toMarkerType(results[i].type));
 220 m_processingRequest->range()->ownerDocument()->markers()->addMarker(range.get(), toMarkerType(results[i].type));
195221
196222 startOffset = results[i].location;
197223 }
198224
199  clearRequest();
 225 processQueuedRequest();
200226}
201227
202228

Source/WebCore/editing/SpellChecker.h

2828
2929#include "PlatformString.h"
3030#include "TextChecking.h"
 31#include "Timer.h"
3132#include <wtf/RefPtr.h>
3233#include <wtf/Noncopyable.h>
3334#include <wtf/Vector.h>

@@public:
4748 ~SpellChecker();
4849
4950 bool isAsynchronousEnabled() const;
50  bool canCheckAsynchronously(Range*) const;
51  bool isBusy() const;
52  bool isValid(int sequence) const;
5351 bool isCheckable(Range*) const;
5452 void requestCheckingFor(TextCheckingTypeMask, PassRefPtr<Range>);
5553 void didCheck(int sequence, const Vector<TextCheckingResult>&);
5654
5755private:
58  bool initRequest(PassRefPtr<Range>);
59  void clearRequest();
 56 class SpellCheckRequest;
 57
 58 bool canCheckAsynchronously(Range*) const;
 59 PassOwnPtr<SpellCheckRequest> createRequest(TextCheckingTypeMask, PassRefPtr<Range>);
 60 void processQueuedRequest();
6061 void doRequestCheckingFor(TextCheckingTypeMask, PassRefPtr<Range>);
6162 TextCheckerClient* client() const;
 63 void timerFiredToProcessQueuedRequest(Timer<SpellChecker>*);
6264
6365 Frame* m_frame;
 66 int m_lastRequestedSequence;
 67
 68 Timer<SpellChecker> m_timerToProcessQueuedRequest;
6469
65  RefPtr<Range> m_requestRange;
66  String m_requestText;
67  int m_requestSequence;
 70 OwnPtr<SpellCheckRequest> m_processingRequest;
 71 OwnPtr<SpellCheckRequest> m_queuedRequest;
6872};
6973
7074} // namespace WebCore

LayoutTests/ChangeLog

 12011-11-30 Shinya Kawanaka <shinyak@google.com>
 2
 3 Asynchronous SpellChecker should consider multiple requests.
 4 https://bugs.webkit.org/show_bug.cgi?id=72939
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Tests for multiple spellcheck requests.
 9
 10 * editing/spelling/spellcheck-queue-expected.txt: Added.
 11 * editing/spelling/spellcheck-queue.html: Added.
 12 * platform/gtk/Skipped:
 13 * platform/qt/Skipped:
 14
1152011-11-30 Vincent Scheib <scheib@chromium.org>
216
317 Rebasing many SVG text & pixel tests due to r101517 Add new renderer for SVGRectElement. Differences are primarily 1 pixel bounds differences

LayoutTests/editing/spelling/spellcheck-queue-expected.txt

 1For Bug 72939: Asynchronous SpellChecker should consider multiple requests.
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5
 6PASS successfullyParsed is true
 7
 8TEST COMPLETE
 9PASS INPUT has a marker on 'foo bar'
 10PASS INPUT has a marker on 'foo bar'
 11

LayoutTests/editing/spelling/spellcheck-queue.html

 1<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 2<html>
 3<head>
 4<script src="../../fast/js/resources/js-test-pre.js"></script>
 5<script src="resources/js-test-selection-shared.js"></script>
 6</head>
 7<body>
 8<p id="description"></p>
 9<div id="console"></div>
 10<script>
 11description('For Bug 72939: Asynchronous SpellChecker should consider multiple requests.');
 12
 13if (window.layoutTestController) {
 14 layoutTestController.waitUntilDone();
 15 layoutTestController.setAsynchronousSpellCheckingEnabled(true);
 16}
 17
 18var testRoot = document.createElement("div");
 19document.body.insertBefore(testRoot, document.body.firstChild);
 20
 21
 22var source = document.createElement("div");
 23source.innerHTML = "foo bar";
 24testRoot.appendChild(source);
 25
 26var testInput1 = document.createElement("input");
 27testInput1.setAttribute("type", "text");
 28testRoot.appendChild(testInput1);
 29var testInput2 = document.createElement("input");
 30testInput2.setAttribute("type", "text");
 31testRoot.appendChild(testInput2);
 32var testInput3 = document.createElement("input");
 33testInput3.setAttribute("type", "text");
 34testRoot.appendChild(testInput3);
 35
 36var sel = window.getSelection();
 37
 38// At least, testInput1 and testInput3 should have markers.
 39var tests = [
 40 function() { verify(source, testInput1, [[0, 3]]); },
 41 function() { verify(source, testInput3, [[0, 3]]); }
 42];
 43
 44function verifyIfAny()
 45{
 46 var next = tests.shift();
 47 if (next) {
 48 next();
 49 return;
 50 }
 51
 52 testRoot.style.display = "none";
 53 if (window.layoutTestController) {
 54 layoutTestController.setAsynchronousSpellCheckingEnabled(false);
 55 layoutTestController.notifyDone();
 56 }
 57}
 58
 59function verifyMarker(node, expectedMarked)
 60{
 61 node.focus();
 62
 63 if (!window.layoutTestController)
 64 return;
 65
 66 var ok = true;
 67 for (var i = 0; ok && i < expectedMarked.length; ++i)
 68 ok = ok && layoutTestController.hasSpellingMarker(expectedMarked[i][0], expectedMarked[i][1]);
 69 return ok;
 70}
 71
 72function copyAndPaste(source, dest)
 73{
 74 sel.selectAllChildren(source);
 75 document.execCommand("Copy");
 76 if (dest instanceof HTMLInputElement || dest instanceof HTMLTextAreaElement) {
 77 dest.value = "";
 78 dest.focus();
 79 } else {
 80 dest.innerHTML = "";
 81 sel.selectAllChildren(dest);
 82 }
 83 document.execCommand("Paste");
 84}
 85
 86function verify(source, dest, expectedMarked)
 87{
 88 var nretry = 10;
 89 var nsleep = 1;
 90 function trial() {
 91 var verified = verifyMarker(dest, expectedMarked);
 92 if (verified) {
 93 testPassed(dest.tagName + " has a marker on '" + source.innerHTML + "'");
 94 verifyIfAny();
 95 return;
 96 }
 97
 98 nretry--;
 99 if (0 == nretry) {
 100 testFailed(dest.tagName + " should have a marker on for '" + source.innerHTML + "'");
 101 verifyIfAny();
 102 return;
 103 }
 104
 105 nsleep *= 2;
 106 window.setTimeout(trial, nsleep);
 107 };
 108 trial();
 109}
 110
 111var destinations = [testInput1, testInput2, testInput3];
 112for (var i = 0; i < destinations.length; ++i)
 113 copyAndPaste(source, destinations[i]);
 114verifyIfAny();
 115
 116var successfullyParsed = true;
 117
 118</script>
 119<script src="../../fast/js/resources/js-test-post.js"></script>
 120</body>
 121</html>

LayoutTests/platform/gtk/Skipped

@@media/event-attributes.html
12161216# https://bugs.webkit.org/show_bug.cgi?id=50740
12171217editing/spelling/spelling-backspace-between-lines.html
12181218editing/spelling/spellcheck-paste.html
 1219editing/spelling/spellcheck-queue.html
12191220
12201221# For https://bugs.webkit.org/show_bug.cgi?id=50758
12211222# These require DRT setSerializeHTTPLoads implementation to be reliable.

LayoutTests/platform/qt/Skipped

@@editing/spelling/spelling-attribute-at-child.html
10011001
10021002# EditorClient::requestCheckingOfString() is not implemented
10031003editing/spelling/spellcheck-paste.html
 1004editing/spelling/spellcheck-queue.html
10041005
10051006# [Qt][GTK] editing/spelling/spellcheck-async.html fails
10061007# https://bugs.webkit.org/show_bug.cgi?id=73003