| Differences between
and this patch
- a/Source/WebCore/ChangeLog +34 lines
Lines 1-3 a/Source/WebCore/ChangeLog_sec1
1
2011-12-01  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
        If there is a request having the same root editable element, the older request is replaced by newer request.
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::SpellCheckRequest::rootEditableElement):
21
        (WebCore::SpellChecker::SpellChecker):
22
        (WebCore::SpellChecker::createRequest):
23
        (WebCore::SpellChecker::timerFiredToProcessQueuedRequest):
24
          When timer is fired, queued request is processed if any.
25
        (WebCore::SpellChecker::canCheckAsynchronously):
26
        (WebCore::SpellChecker::doRequestCheckingFor):
27
          When the spellchecker is processing another request, the latest request is queued.
28
        (WebCore::SpellChecker::invokeRequest):
29
        (WebCore::SpellChecker::enqueueRequest):
30
          Enqueues a request. If there is an older request whose root editable element is the same as the request,
31
          it will be replaced.
32
        (WebCore::SpellChecker::didCheck):
33
        * editing/SpellChecker.h:
34
1
2011-11-30  Leo Yang  <leo.yang@torchmobile.com.cn>
35
2011-11-30  Leo Yang  <leo.yang@torchmobile.com.cn>
2
36
3
        [BlackBerry] Add 2 cpp files to the BlackBerry build system
37
        [BlackBerry] Add 2 cpp files to the BlackBerry build system
- a/Source/WebCore/editing/SpellChecker.cpp -33 / +79 lines
Lines 45-53 a/Source/WebCore/editing/SpellChecker.cpp_sec1
45
45
46
namespace WebCore {
46
namespace WebCore {
47
47
48
class SpellChecker::SpellCheckRequest : public RefCounted<SpellChecker::SpellCheckRequest> {
49
public:
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
    Element* rootEditableElement() const { return range()->startContainer()->rootEditableElement(); }
63
64
private:
65
    int m_sequence;
66
    RefPtr<Range> m_range;
67
    String m_text;
68
    TextCheckingTypeMask m_mask;
69
};
70
48
SpellChecker::SpellChecker(Frame* frame)
71
SpellChecker::SpellChecker(Frame* frame)
49
    : m_frame(frame)
72
    : m_frame(frame)
50
    , m_requestSequence(0)
73
    , m_lastRequestedSequence(0)
74
    , m_timerToProcessQueuedRequest(this, &SpellChecker::timerFiredToProcessQueuedRequest)
51
{
75
{
52
}
76
}
53
77
Lines 63-87 TextCheckerClient* SpellChecker::client() const a/Source/WebCore/editing/SpellChecker.cpp_sec2
63
    return page->editorClient()->textChecker();
87
    return page->editorClient()->textChecker();
64
}
88
}
65
89
66
bool SpellChecker::initRequest(PassRefPtr<Range> range)
90
PassRefPtr<SpellChecker::SpellCheckRequest> SpellChecker::createRequest(TextCheckingTypeMask mask, PassRefPtr<Range> range)
67
{
91
{
68
    ASSERT(canCheckAsynchronously(range.get()));
92
    ASSERT(canCheckAsynchronously(range.get()));
69
93
70
    String text = range->text();
94
    String text = range->text();
71
    if (!text.length())
95
    if (!text.length())
72
        return false;
96
        return PassRefPtr<SpellCheckRequest>();
73
97
74
    m_requestRange = range;
98
    return adoptRef(new SpellCheckRequest(++m_lastRequestedSequence, range, text, mask));
75
    m_requestText = text;
76
    m_requestSequence++;
77
78
    return true;
79
}
99
}
80
100
81
void SpellChecker::clearRequest()
101
void SpellChecker::timerFiredToProcessQueuedRequest(Timer<SpellChecker>*)
82
{
102
{
83
    m_requestRange.clear();
103
    ASSERT(!m_requestQueue.isEmpty());
84
    m_requestText = String();
104
    if (m_requestQueue.isEmpty())
105
        return;
106
107
    invokeRequest(m_requestQueue.takeFirst());
85
}
108
}
86
109
87
bool SpellChecker::isAsynchronousEnabled() const
110
bool SpellChecker::isAsynchronousEnabled() const
Lines 91-107 bool SpellChecker::isAsynchronousEnabled() const a/Source/WebCore/editing/SpellChecker.cpp_sec3
91
114
92
bool SpellChecker::canCheckAsynchronously(Range* range) const
115
bool SpellChecker::canCheckAsynchronously(Range* range) const
93
{
116
{
94
    return client() && isCheckable(range) && isAsynchronousEnabled() && !isBusy();
117
    return client() && isCheckable(range) && isAsynchronousEnabled();
95
}
96
97
bool SpellChecker::isBusy() const
98
{
99
    return m_requestRange.get();
100
}
101
102
bool SpellChecker::isValid(int sequence) const
103
{
104
    return m_requestRange.get() && m_requestText.length() && m_requestSequence == sequence;
105
}
118
}
106
119
107
bool SpellChecker::isCheckable(Range* range) const
120
bool SpellChecker::isCheckable(Range* range) const
Lines 121-129 void SpellChecker::doRequestCheckingFor(TextCheckingTypeMask mask, PassRefPtr<Ra a/Source/WebCore/editing/SpellChecker.cpp_sec4
121
{
134
{
122
    ASSERT(canCheckAsynchronously(range.get()));
135
    ASSERT(canCheckAsynchronously(range.get()));
123
136
124
    if (!initRequest(range))
137
    RefPtr<SpellCheckRequest> request(createRequest(mask, range));
138
    if (!request)
139
        return;
140
141
    if (m_timerToProcessQueuedRequest.isActive() || m_processingRequest) {
142
        enqueueRequest(request.release());
125
        return;
143
        return;
126
    client()->requestCheckingOfString(this, m_requestSequence, mask, m_requestText);
144
    }
145
146
    invokeRequest(request.release());
147
}
148
149
void SpellChecker::invokeRequest(PassRefPtr<SpellCheckRequest> request)
150
{
151
    ASSERT(!m_processingRequest);
152
153
    client()->requestCheckingOfString(this, request->sequence(), request->mask(), request->text());
154
    m_processingRequest = request;
155
}
156
157
void SpellChecker::enqueueRequest(PassRefPtr<SpellCheckRequest> request)
158
{
159
    ASSERT(request);
160
161
    for (RequestQueue::iterator it = m_requestQueue.begin(); it != m_requestQueue.end(); ++it) {
162
        if (request->rootEditableElement() != (*it)->rootEditableElement())
163
            continue;
164
165
        fprintf(stderr, "REMOVED!\n");
166
        *it = request;
167
        return;
168
    }
169
170
    m_requestQueue.append(request);
127
}
171
}
128
172
129
static bool forwardIterator(PositionIterator& iterator, int distance)
173
static bool forwardIterator(PositionIterator& iterator, int distance)
Lines 159-174 static DocumentMarker::MarkerType toMarkerType(TextCheckingType type) a/Source/WebCore/editing/SpellChecker.cpp_sec5
159
// Currenntly ignoring TextCheckingResult::details but should be handled. See Bug 56368.
203
// Currenntly ignoring TextCheckingResult::details but should be handled. See Bug 56368.
160
void SpellChecker::didCheck(int sequence, const Vector<TextCheckingResult>& results)
204
void SpellChecker::didCheck(int sequence, const Vector<TextCheckingResult>& results)
161
{
205
{
162
    if (!isValid(sequence))
206
    ASSERT(m_processingRequest);
163
        return;
164
207
165
    if (!isCheckable(m_requestRange.get())) {
208
    ASSERT(m_processingRequest->sequence() == sequence);
166
        clearRequest();
209
    if (m_processingRequest->sequence() != sequence) {
210
        m_requestQueue.clear();
167
        return;
211
        return;
168
    }
212
    }
169
213
170
    int startOffset = 0;
214
    int startOffset = 0;
171
    PositionIterator start = m_requestRange->startPosition();
215
    PositionIterator start = m_processingRequest->range()->startPosition();
172
    for (size_t i = 0; i < results.size(); ++i) {
216
    for (size_t i = 0; i < results.size(); ++i) {
173
        if (results[i].type != TextCheckingTypeSpelling && results[i].type != TextCheckingTypeGrammar)
217
        if (results[i].type != TextCheckingTypeSpelling && results[i].type != TextCheckingTypeGrammar)
174
            continue;
218
            continue;
Lines 186-202 void SpellChecker::didCheck(int sequence, const Vector<TextCheckingResult>& resu a/Source/WebCore/editing/SpellChecker.cpp_sec6
186
        // spellings in the background. To avoid adding markers to the words modified by users or
230
        // spellings in the background. To avoid adding markers to the words modified by users or
187
        // JavaScript applications, retrieve the words in the specified region and compare them with
231
        // JavaScript applications, retrieve the words in the specified region and compare them with
188
        // the original ones.
232
        // the original ones.
189
        RefPtr<Range> range = Range::create(m_requestRange->ownerDocument(), start, end);
233
        RefPtr<Range> range = Range::create(m_processingRequest->range()->ownerDocument(), start, end);
190
        // FIXME: Use textContent() compatible string conversion.
234
        // FIXME: Use textContent() compatible string conversion.
191
        String destination = range->text();
235
        String destination = range->text();
192
        String source = m_requestText.substring(results[i].location, results[i].length);
236
        String source = m_processingRequest->text().substring(results[i].location, results[i].length);
193
        if (destination == source)
237
        if (destination == source)
194
            m_requestRange->ownerDocument()->markers()->addMarker(range.get(), toMarkerType(results[i].type));
238
            m_processingRequest->range()->ownerDocument()->markers()->addMarker(range.get(), toMarkerType(results[i].type));
195
239
196
        startOffset = results[i].location;
240
        startOffset = results[i].location;
197
    }
241
    }
198
242
199
    clearRequest();
243
    m_processingRequest.clear();
244
    if (!m_requestQueue.isEmpty())
245
        m_timerToProcessQueuedRequest.startOneShot(0);
200
}
246
}
201
247
202
248
- a/Source/WebCore/editing/SpellChecker.h -8 / +15 lines
Lines 28-33 a/Source/WebCore/editing/SpellChecker.h_sec1
28
28
29
#include "PlatformString.h"
29
#include "PlatformString.h"
30
#include "TextChecking.h"
30
#include "TextChecking.h"
31
#include "Timer.h"
32
#include <wtf/Deque.h>
31
#include <wtf/RefPtr.h>
33
#include <wtf/RefPtr.h>
32
#include <wtf/Noncopyable.h>
34
#include <wtf/Noncopyable.h>
33
#include <wtf/Vector.h>
35
#include <wtf/Vector.h>
Lines 47-70 public: a/Source/WebCore/editing/SpellChecker.h_sec2
47
    ~SpellChecker();
49
    ~SpellChecker();
48
50
49
    bool isAsynchronousEnabled() const;
51
    bool isAsynchronousEnabled() const;
50
    bool canCheckAsynchronously(Range*) const;
51
    bool isBusy() const;
52
    bool isValid(int sequence) const;
53
    bool isCheckable(Range*) const;
52
    bool isCheckable(Range*) const;
54
    void requestCheckingFor(TextCheckingTypeMask, PassRefPtr<Range>);
53
    void requestCheckingFor(TextCheckingTypeMask, PassRefPtr<Range>);
55
    void didCheck(int sequence, const Vector<TextCheckingResult>&);
54
    void didCheck(int sequence, const Vector<TextCheckingResult>&);
56
55
57
private:
56
private:
58
    bool initRequest(PassRefPtr<Range>);
57
    class SpellCheckRequest;
59
    void clearRequest();
58
    typedef Deque<RefPtr<SpellCheckRequest> > RequestQueue;
59
60
    bool canCheckAsynchronously(Range*) const;
61
    PassRefPtr<SpellCheckRequest> createRequest(TextCheckingTypeMask, PassRefPtr<Range>);
60
    void doRequestCheckingFor(TextCheckingTypeMask, PassRefPtr<Range>);
62
    void doRequestCheckingFor(TextCheckingTypeMask, PassRefPtr<Range>);
61
    TextCheckerClient* client() const;
63
    TextCheckerClient* client() const;
64
    void timerFiredToProcessQueuedRequest(Timer<SpellChecker>*);
65
    void invokeRequest(PassRefPtr<SpellCheckRequest>);
66
    void enqueueRequest(PassRefPtr<SpellCheckRequest>);
62
67
63
    Frame* m_frame;
68
    Frame* m_frame;
69
    int m_lastRequestedSequence;
70
71
    Timer<SpellChecker> m_timerToProcessQueuedRequest;
64
72
65
    RefPtr<Range> m_requestRange;
73
    RefPtr<SpellCheckRequest> m_processingRequest;
66
    String m_requestText;
74
    RequestQueue m_requestQueue;
67
    int m_requestSequence;
68
};
75
};
69
76
70
} // namespace WebCore
77
} // namespace WebCore
- a/LayoutTests/ChangeLog +14 lines
Lines 1-3 a/LayoutTests/ChangeLog_sec1
1
2011-12-01  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
1
2011-11-30  Vincent Scheib  <scheib@chromium.org>
15
2011-11-30  Vincent Scheib  <scheib@chromium.org>
2
16
3
        Rebasing many SVG text & pixel tests due to r101517 Add new renderer for SVGRectElement. Differences are primarily 1 pixel bounds differences
17
        Rebasing many SVG text & pixel tests due to r101517 Add new renderer for SVGRectElement. Differences are primarily 1 pixel bounds differences
- a/LayoutTests/editing/spelling/spellcheck-queue-expected.txt +18 lines
Line 0 a/LayoutTests/editing/spelling/spellcheck-queue-expected.txt_sec1
1
For Bug 72939: Asynchronous SpellChecker should consider multiple requests.
2
3
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
4
5
6
PASS successfullyParsed is true
7
8
TEST COMPLETE
9
PASS INPUT has a marker on 'zz apple orange'
10
PASS TEXTAREA has a marker on 'zz apple orange'
11
PASS DIV has a marker on 'zz apple orange'
12
PASS INPUT has a marker on 'zz apple orange'
13
PASS TEXTAREA has a marker on 'zz apple orange'
14
PASS DIV has a marker on 'zz apple orange'
15
PASS INPUT has a marker on 'zz apple orange'
16
PASS TEXTAREA has a marker on 'zz apple orange'
17
PASS DIV has a marker on 'zz apple orange'
18
- a/LayoutTests/editing/spelling/spellcheck-queue.html +173 lines
Line 0 a/LayoutTests/editing/spelling/spellcheck-queue.html_sec1
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>
11
description('For Bug 72939: Asynchronous SpellChecker should consider multiple requests.');
12
13
if (window.layoutTestController) {
14
    layoutTestController.waitUntilDone();
15
    layoutTestController.setAsynchronousSpellCheckingEnabled(true);
16
}
17
18
var testRoot = document.createElement("div");
19
document.body.insertBefore(testRoot, document.body.firstChild);
20
21
var source = document.createElement("div");
22
source.innerHTML = "zz apple orange";
23
testRoot.appendChild(source);
24
25
function createInput(testRoot) {
26
    var e = document.createElement('input');
27
    e.setAttribute("type", "text");
28
    testRoot.appendChild(e);
29
30
    return e;
31
}
32
33
function createTextArea(testRoot) {
34
    var e = document.createElement("textarea");
35
    testRoot.appendChild(e);
36
37
    return e;
38
}
39
40
function createContentEditable(testRoot) {
41
    var e = document.createElement("div");
42
    e.setAttribute("contentEditable", "true");
43
    testRoot.appendChild(e);
44
45
    return e;
46
}
47
48
var destinations = [
49
    createInput(testRoot),
50
    createTextArea(testRoot),
51
    createContentEditable(testRoot),
52
    createInput(testRoot),
53
    createTextArea(testRoot),
54
    createContentEditable(testRoot),
55
    createInput(testRoot),
56
    createTextArea(testRoot),
57
    createContentEditable(testRoot),
58
];
59
60
var sel = window.getSelection();
61
62
var tests = [];
63
for (var i = 0; i < destinations.length; ++i) {
64
    var t = function(i) {
65
        return function() { verify(source, destinations[i], ["zz"]); }
66
    }(i);
67
    tests.push(t);
68
}
69
70
function verifyIfAny()
71
{
72
    var next = tests.shift();
73
    if (next) {
74
        next();
75
        return;
76
    }
77
78
    testRoot.style.display = "none";
79
    if (window.layoutTestController) {
80
        layoutTestController.setAsynchronousSpellCheckingEnabled(false);
81
        layoutTestController.notifyDone();
82
    }
83
}
84
85
function findFirstTextNode(node)
86
{
87
    if (node.nodeType == 3) // text node?
88
        return node;
89
90
    var childNodes = node.childNodes;
91
    for (var i = 0; i < childNodes.length; ++i) {
92
        var n = findFirstTextNode(childNodes[i]);
93
        if (n)
94
            return n;
95
    }
96
97
    return null;
98
}
99
100
function verifyMarker(node, expectedMarked)
101
{
102
    if (!window.layoutTestController || !window.internals)
103
        return false;
104
105
    var textNode = null;
106
    if (node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement) {
107
        var shadow = internals.shadowRoot(node);
108
        textNode = findFirstTextNode(shadow);
109
    } else
110
        textNode = findFirstTextNode(node);
111
112
    var num = internals.markerCountForNode(textNode, "spelling");
113
    if (num != expectedMarked.length)
114
        return false;
115
    for (var i = 0; i < num; ++i) {
116
        var range = internals.markerRangeForNode(textNode, "spelling", i);
117
        if (range.toString() != expectedMarked[i])
118
            return false;
119
    }
120
121
    return true;
122
}
123
124
function copyAndPaste(source, dest)
125
{
126
    sel.selectAllChildren(source);
127
    document.execCommand("Copy");
128
129
    if (dest instanceof HTMLInputElement || dest instanceof HTMLTextAreaElement) {
130
        dest.value = "";
131
        dest.focus();
132
    } else {
133
        dest.innerHTML = "";
134
        sel.selectAllChildren(dest);
135
    }
136
    document.execCommand("Paste");
137
}
138
139
function verify(source, dest, expectedMarked)
140
{
141
    var nretry = 10;
142
    var nsleep = 1;
143
    function trial() {
144
        var verified = verifyMarker(dest, expectedMarked);
145
        if (verified) {
146
            testPassed(dest.tagName + " has a marker on '" + source.innerHTML + "'");
147
            verifyIfAny();
148
            return;
149
        }
150
151
        nretry--;
152
        if (0 == nretry) {
153
            testFailed(dest.tagName + " should have a marker on for '" + source.innerHTML + "'");
154
            verifyIfAny();
155
            return;
156
        }
157
158
        nsleep *= 2;
159
        window.setTimeout(trial, nsleep);
160
    };
161
    trial();
162
}
163
164
for (var i = 0; i < destinations.length; ++i)
165
    copyAndPaste(source, destinations[i]);
166
verifyIfAny();
167
168
var successfullyParsed = true;
169
170
</script>
171
<script src="../../fast/js/resources/js-test-post.js"></script>
172
</body>
173
</html>
- a/LayoutTests/platform/gtk/Skipped +1 lines
Lines 1216-1221 media/event-attributes.html a/LayoutTests/platform/gtk/Skipped_sec1
1216
# https://bugs.webkit.org/show_bug.cgi?id=50740
1216
# https://bugs.webkit.org/show_bug.cgi?id=50740
1217
editing/spelling/spelling-backspace-between-lines.html
1217
editing/spelling/spelling-backspace-between-lines.html
1218
editing/spelling/spellcheck-paste.html
1218
editing/spelling/spellcheck-paste.html
1219
editing/spelling/spellcheck-queue.html
1219
1220
1220
# For https://bugs.webkit.org/show_bug.cgi?id=50758
1221
# For https://bugs.webkit.org/show_bug.cgi?id=50758
1221
# These require DRT setSerializeHTTPLoads implementation to be reliable.
1222
# These require DRT setSerializeHTTPLoads implementation to be reliable.
- a/LayoutTests/platform/qt/Skipped +1 lines
Lines 1001-1006 editing/spelling/spelling-attribute-at-child.html a/LayoutTests/platform/qt/Skipped_sec1
1001
1001
1002
# EditorClient::requestCheckingOfString() is not implemented
1002
# EditorClient::requestCheckingOfString() is not implemented
1003
editing/spelling/spellcheck-paste.html
1003
editing/spelling/spellcheck-paste.html
1004
editing/spelling/spellcheck-queue.html
1004
1005
1005
# [Qt][GTK] editing/spelling/spellcheck-async.html fails
1006
# [Qt][GTK] editing/spelling/spellcheck-async.html fails
1006
# https://bugs.webkit.org/show_bug.cgi?id=73003
1007
# https://bugs.webkit.org/show_bug.cgi?id=73003

Return to Bug 72939