| 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::requestCheckingFor):
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 -36 / +76 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
        , m_rootEditableElement(m_range->startContainer()->rootEditableElement())
56
    {
57
    }
58
59
    int sequence() const { return m_sequence; }
60
    Range* range() const { return m_range.get(); }
61
    const String& text() const { return m_text; }
62
    TextCheckingTypeMask mask() const { return m_mask; }
63
    Element* rootEditableElement() const { return m_rootEditableElement; }
64
65
private:
66
    int m_sequence;
67
    RefPtr<Range> m_range;
68
    String m_text;
69
    TextCheckingTypeMask m_mask;
70
    Element* m_rootEditableElement;
71
};
72
48
SpellChecker::SpellChecker(Frame* frame)
73
SpellChecker::SpellChecker(Frame* frame)
49
    : m_frame(frame)
74
    : m_frame(frame)
50
    , m_requestSequence(0)
75
    , m_lastRequestedSequence(0)
76
    , m_timerToProcessQueuedRequest(this, &SpellChecker::timerFiredToProcessQueuedRequest)
51
{
77
{
52
}
78
}
53
79
Lines 63-87 TextCheckerClient* SpellChecker::client() const a/Source/WebCore/editing/SpellChecker.cpp_sec2
63
    return page->editorClient()->textChecker();
89
    return page->editorClient()->textChecker();
64
}
90
}
65
91
66
bool SpellChecker::initRequest(PassRefPtr<Range> range)
92
PassRefPtr<SpellChecker::SpellCheckRequest> SpellChecker::createRequest(TextCheckingTypeMask mask, PassRefPtr<Range> range)
67
{
93
{
68
    ASSERT(canCheckAsynchronously(range.get()));
94
    ASSERT(canCheckAsynchronously(range.get()));
69
95
70
    String text = range->text();
96
    String text = range->text();
71
    if (!text.length())
97
    if (!text.length())
72
        return false;
98
        return PassRefPtr<SpellCheckRequest>();
73
99
74
    m_requestRange = range;
100
    return adoptRef(new SpellCheckRequest(++m_lastRequestedSequence, range, text, mask));
75
    m_requestText = text;
76
    m_requestSequence++;
77
78
    return true;
79
}
101
}
80
102
81
void SpellChecker::clearRequest()
103
void SpellChecker::timerFiredToProcessQueuedRequest(Timer<SpellChecker>*)
82
{
104
{
83
    m_requestRange.clear();
105
    ASSERT(!m_requestQueue.isEmpty());
84
    m_requestText = String();
106
    if (m_requestQueue.isEmpty())
107
        return;
108
109
    invokeRequest(m_requestQueue.takeFirst());
85
}
110
}
86
111
87
bool SpellChecker::isAsynchronousEnabled() const
112
bool SpellChecker::isAsynchronousEnabled() const
Lines 91-107 bool SpellChecker::isAsynchronousEnabled() const a/Source/WebCore/editing/SpellChecker.cpp_sec3
91
116
92
bool SpellChecker::canCheckAsynchronously(Range* range) const
117
bool SpellChecker::canCheckAsynchronously(Range* range) const
93
{
118
{
94
    return client() && isCheckable(range) && isAsynchronousEnabled() && !isBusy();
119
    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
}
120
}
106
121
107
bool SpellChecker::isCheckable(Range* range) const
122
bool SpellChecker::isCheckable(Range* range) const
Lines 114-129 void SpellChecker::requestCheckingFor(TextCheckingTypeMask mask, PassRefPtr<Rang a/Source/WebCore/editing/SpellChecker.cpp_sec4
114
    if (!canCheckAsynchronously(range.get()))
129
    if (!canCheckAsynchronously(range.get()))
115
        return;
130
        return;
116
131
117
    doRequestCheckingFor(mask, range);
132
    RefPtr<SpellCheckRequest> request(createRequest(mask, range));
133
    if (!request)
134
        return;
135
136
    if (m_timerToProcessQueuedRequest.isActive() || m_processingRequest) {
137
        enqueueRequest(request.release());
138
        return;
139
    }
140
141
    invokeRequest(request.release());
118
}
142
}
119
143
120
void SpellChecker::doRequestCheckingFor(TextCheckingTypeMask mask, PassRefPtr<Range> range)
144
void SpellChecker::invokeRequest(PassRefPtr<SpellCheckRequest> request)
121
{
145
{
122
    ASSERT(canCheckAsynchronously(range.get()));
146
    ASSERT(!m_processingRequest);
147
148
    client()->requestCheckingOfString(this, request->sequence(), request->mask(), request->text());
149
    m_processingRequest = request;
150
}
123
151
124
    if (!initRequest(range))
152
void SpellChecker::enqueueRequest(PassRefPtr<SpellCheckRequest> request)
153
{
154
    ASSERT(request);
155
156
    for (RequestQueue::iterator it = m_requestQueue.begin(); it != m_requestQueue.end(); ++it) {
157
        if (request->rootEditableElement() != (*it)->rootEditableElement())
158
            continue;
159
160
        *it = request;
125
        return;
161
        return;
126
    client()->requestCheckingOfString(this, m_requestSequence, mask, m_requestText);
162
    }
163
164
    m_requestQueue.append(request);
127
}
165
}
128
166
129
static bool forwardIterator(PositionIterator& iterator, int distance)
167
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.
197
// Currenntly ignoring TextCheckingResult::details but should be handled. See Bug 56368.
160
void SpellChecker::didCheck(int sequence, const Vector<TextCheckingResult>& results)
198
void SpellChecker::didCheck(int sequence, const Vector<TextCheckingResult>& results)
161
{
199
{
162
    if (!isValid(sequence))
200
    ASSERT(m_processingRequest);
163
        return;
164
201
165
    if (!isCheckable(m_requestRange.get())) {
202
    ASSERT(m_processingRequest->sequence() == sequence);
166
        clearRequest();
203
    if (m_processingRequest->sequence() != sequence) {
204
        m_requestQueue.clear();
167
        return;
205
        return;
168
    }
206
    }
169
207
170
    int startOffset = 0;
208
    int startOffset = 0;
171
    PositionIterator start = m_requestRange->startPosition();
209
    PositionIterator start = m_processingRequest->range()->startPosition();
172
    for (size_t i = 0; i < results.size(); ++i) {
210
    for (size_t i = 0; i < results.size(); ++i) {
173
        if (results[i].type != TextCheckingTypeSpelling && results[i].type != TextCheckingTypeGrammar)
211
        if (results[i].type != TextCheckingTypeSpelling && results[i].type != TextCheckingTypeGrammar)
174
            continue;
212
            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
224
        // 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
225
        // JavaScript applications, retrieve the words in the specified region and compare them with
188
        // the original ones.
226
        // the original ones.
189
        RefPtr<Range> range = Range::create(m_requestRange->ownerDocument(), start, end);
227
        RefPtr<Range> range = Range::create(m_processingRequest->range()->ownerDocument(), start, end);
190
        // FIXME: Use textContent() compatible string conversion.
228
        // FIXME: Use textContent() compatible string conversion.
191
        String destination = range->text();
229
        String destination = range->text();
192
        String source = m_requestText.substring(results[i].location, results[i].length);
230
        String source = m_processingRequest->text().substring(results[i].location, results[i].length);
193
        if (destination == source)
231
        if (destination == source)
194
            m_requestRange->ownerDocument()->markers()->addMarker(range.get(), toMarkerType(results[i].type));
232
            m_processingRequest->range()->ownerDocument()->markers()->addMarker(range.get(), toMarkerType(results[i].type));
195
233
196
        startOffset = results[i].location;
234
        startOffset = results[i].location;
197
    }
235
    }
198
236
199
    clearRequest();
237
    m_processingRequest.clear();
238
    if (!m_requestQueue.isEmpty())
239
        m_timerToProcessQueuedRequest.startOneShot(0);
200
}
240
}
201
241
202
242
- a/Source/WebCore/editing/SpellChecker.h -9 / +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;
60
    void doRequestCheckingFor(TextCheckingTypeMask, PassRefPtr<Range>);
59
60
    bool canCheckAsynchronously(Range*) const;
61
    PassRefPtr<SpellCheckRequest> createRequest(TextCheckingTypeMask, PassRefPtr<Range>);
61
    TextCheckerClient* client() const;
62
    TextCheckerClient* client() const;
63
    void timerFiredToProcessQueuedRequest(Timer<SpellChecker>*);
64
    void invokeRequest(PassRefPtr<SpellCheckRequest>);
65
    void enqueueRequest(PassRefPtr<SpellCheckRequest>);
62
66
63
    Frame* m_frame;
67
    Frame* m_frame;
68
    int m_lastRequestedSequence;
69
70
    Timer<SpellChecker> m_timerToProcessQueuedRequest;
64
71
65
    RefPtr<Range> m_requestRange;
72
    RefPtr<SpellCheckRequest> m_processingRequest;
66
    String m_requestText;
73
    RequestQueue m_requestQueue;
67
    int m_requestSequence;
68
};
74
};
69
75
70
} // namespace WebCore
76
} // 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 +188 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 source1 = document.createElement("div");
22
source1.innerHTML = "foo bar";
23
testRoot.appendChild(source1);
24
25
var source2 = document.createElement("div");
26
source2.innerHTML = "zz apple orange";
27
testRoot.appendChild(source2);
28
29
function createInput(testRoot) {
30
    var e = document.createElement('input');
31
    e.setAttribute("type", "text");
32
    testRoot.appendChild(e);
33
34
    return e;
35
}
36
37
function createTextArea(testRoot) {
38
    var e = document.createElement("textarea");
39
    testRoot.appendChild(e);
40
41
    return e;
42
}
43
44
function createContentEditable(testRoot) {
45
    var e = document.createElement("div");
46
    e.setAttribute("contentEditable", "true");
47
    testRoot.appendChild(e);
48
49
    return e;
50
}
51
52
var destinations = [
53
    createInput(testRoot),
54
    createTextArea(testRoot),
55
    createContentEditable(testRoot),
56
    createInput(testRoot),
57
    createTextArea(testRoot),
58
    createContentEditable(testRoot),
59
    createInput(testRoot),
60
    createTextArea(testRoot),
61
    createContentEditable(testRoot),
62
];
63
64
var sel = window.getSelection();
65
66
var tests = [];
67
for (var i = 0; i < destinations.length; ++i) {
68
    var t = function(i) {
69
        return function() { verify(source2, destinations[i], ["zz"]); }
70
    }(i);
71
    tests.push(t);
72
}
73
74
function verifyIfAny()
75
{
76
    var next = tests.shift();
77
    if (next) {
78
        next();
79
        return;
80
    }
81
82
    testRoot.style.display = "none";
83
    if (window.layoutTestController) {
84
        layoutTestController.setAsynchronousSpellCheckingEnabled(false);
85
        layoutTestController.notifyDone();
86
    }
87
}
88
89
function findFirstTextNode(node)
90
{
91
    function iterToFindFirstTextNode(node)
92
    {
93
        if (node instanceof Text)
94
            return node;
95
96
        var childNodes = node.childNodes;
97
        for (var i = 0; i < childNodes.length; ++i) {
98
            var n = iterToFindFirstTextNode(childNodes[i]);
99
            if (n)
100
                return n;
101
        }
102
103
        return null;
104
    }
105
106
107
    if (node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement)
108
        return iterToFindFirstTextNode(internals.shadowRoot(node));
109
    else
110
        return iterToFindFirstTextNode(node);
111
}
112
113
function verifyMarker(node, expectedMarked)
114
{
115
    if (!window.layoutTestController || !window.internals)
116
        return false;
117
118
    var textNode = findFirstTextNode(node);
119
120
    var num = internals.markerCountForNode(textNode, "spelling");
121
    if (num != expectedMarked.length)
122
        return false;
123
    for (var i = 0; i < num; ++i) {
124
        var range = internals.markerRangeForNode(textNode, "spelling", i);
125
        if (range.toString() != expectedMarked[i])
126
            return false;
127
    }
128
129
    return true;
130
}
131
132
function copyAndPaste(source, dest)
133
{
134
    sel.selectAllChildren(source);
135
    document.execCommand("Copy");
136
137
    if (dest instanceof HTMLInputElement || dest instanceof HTMLTextAreaElement) {
138
        dest.value = "";
139
        dest.focus();
140
    } else {
141
        dest.innerHTML = "";
142
        sel.selectAllChildren(dest);
143
    }
144
    document.execCommand("Paste");
145
}
146
147
function verify(source, dest, expectedMarked)
148
{
149
    var nretry = 10;
150
    var nsleep = 1;
151
    function trial() {
152
        var verified = verifyMarker(dest, expectedMarked);
153
        if (verified) {
154
            testPassed(dest.tagName + " has a marker on '" + source.innerHTML + "'");
155
            verifyIfAny();
156
            return;
157
        }
158
159
        nretry--;
160
        if (0 == nretry) {
161
            testFailed(dest.tagName + " should have a marker on for '" + source.innerHTML + "'");
162
            verifyIfAny();
163
            return;
164
        }
165
166
        nsleep *= 2;
167
        window.setTimeout(trial, nsleep);
168
    };
169
    trial();
170
}
171
172
173
// paste "foo bar"
174
for (var i = 0; i < destinations.length; ++i)
175
    copyAndPaste(source1, destinations[i]);
176
177
// paste "zz apple orange"
178
for (var i = 0; i < destinations.length; ++i)
179
    copyAndPaste(source2, destinations[i]);
180
181
verifyIfAny();
182
183
var successfullyParsed = true;
184
185
</script>
186
<script src="../../fast/js/resources/js-test-post.js"></script>
187
</body>
188
</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