A test that mutation happens when asynchronous spell checking is in process.
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 16 Dec 2011 09:04:50 +0000 (09:04 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 16 Dec 2011 09:04:50 +0000 (09:04 +0000)
https://bugs.webkit.org/show_bug.cgi?id=72940

Patch by Shinya Kawanaka <shinyak@google.com> on 2011-12-16
Reviewed by Hajime Morita.

Added a test that mutation happens when spellchecking.
This test confirms crash won't happen, and how markers are used.

* editing/spelling/spellcheck-async-mutation-expected.txt: Added.
* editing/spelling/spellcheck-async-mutation.html: Added.
* platform/gtk/Skipped:
* platform/mac-leopard/Skipped:
* platform/qt/Skipped:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@103044 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/editing/spelling/spellcheck-async-mutation-expected.txt [new file with mode: 0644]
LayoutTests/editing/spelling/spellcheck-async-mutation.html [new file with mode: 0644]
LayoutTests/platform/gtk/Skipped
LayoutTests/platform/mac-leopard/Skipped
LayoutTests/platform/qt/Skipped

index 86efef8..bfe1446 100644 (file)
@@ -1,3 +1,19 @@
+2011-12-16  Shinya Kawanaka  <shinyak@google.com>
+
+        A test that mutation happens when asynchronous spell checking is in process.
+        https://bugs.webkit.org/show_bug.cgi?id=72940
+
+        Reviewed by Hajime Morita.
+
+        Added a test that mutation happens when spellchecking.
+        This test confirms crash won't happen, and how markers are used.
+
+        * editing/spelling/spellcheck-async-mutation-expected.txt: Added.
+        * editing/spelling/spellcheck-async-mutation.html: Added.
+        * platform/gtk/Skipped:
+        * platform/mac-leopard/Skipped:
+        * platform/qt/Skipped:
+
 2011-12-16  Hajime Morrita <morrita@chromium.org>
 
         Marking some as fail.
diff --git a/LayoutTests/editing/spelling/spellcheck-async-mutation-expected.txt b/LayoutTests/editing/spelling/spellcheck-async-mutation-expected.txt
new file mode 100644 (file)
index 0000000..bd548d8
--- /dev/null
@@ -0,0 +1,45 @@
+Test for asynchronous spellchecking in case DOM mutation happens. This test checks crash won't happen if DOM mutations happened.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+Test Start: 1
+PASS requestId is >= lastRequestId + 1
+PASS Request has been processed.
+
+Test Start: 2
+PASS requestId is >= lastRequestId + 1
+PASS Request has been processed.
+
+Test Start: 3
+PASS requestId is >= lastRequestId + 1
+PASS Request has been processed.
+
+Test Start: 4
+PASS requestId is >= lastRequestId + 1
+PASS Request has been processed.
+
+Test Start: 5
+PASS requestId is >= lastRequestId + 1
+PASS Request has been processed.
+
+Test Start: 6
+PASS requestId is >= lastRequestId + 1
+PASS Request has been processed.
+
+Test Start: 7
+PASS requestId is >= lastRequestId + 1
+PASS Request has been processed.
+
+Test Start: 8
+PASS requestId is >= lastRequestId + 1
+PASS Request has been processed.
+
+Test Start: 9
+PASS requestId is >= lastRequestId + 1
+PASS Request has been processed.
+zz zz zz
+
diff --git a/LayoutTests/editing/spelling/spellcheck-async-mutation.html b/LayoutTests/editing/spelling/spellcheck-async-mutation.html
new file mode 100644 (file)
index 0000000..f006e39
--- /dev/null
@@ -0,0 +1,243 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+<script src="../editing.js"></script>
+<style>
+.editing {
+    border: 2px solid red;
+    padding: 6px;
+    font-size: 18px;
+}
+</style>
+</head>
+<body>
+<pre id="description"></pre>
+<pre id="console"></pre>
+
+<div id="source">zz zz zz</textarea>
+
+<div id="container"></div>
+
+<div id="move-target"></div>
+
+<script>
+description(
+    "Test for asynchronous spellchecking in case DOM mutation happens. " +
+    "This test checks crash won't happen if DOM mutations happened."
+);
+
+if (window.layoutTestController) {
+    layoutTestController.dumpAsText();
+    layoutTestController.setAsynchronousSpellCheckingEnabled(true);
+    layoutTestController.waitUntilDone();
+}
+
+if (window.internals)
+    internals.setUnifiedTextCheckingEnabled(document, true);
+
+var sourceIds = ['source'];
+var destElems = ['textarea', 'input', 'contenteditable'];
+var tweaks = ['delete', 'move', 'mutate'];
+
+var testData = [];
+for (var i = 0; i < sourceIds.length; ++i) {
+    for (var j = 0; j < destElems.length; ++j) {
+        for (var k = 0; k < tweaks.length; ++k) {
+            testData.push({
+                sourceId: sourceIds[i],
+                destElem: destElems[j],
+                tweak: tweaks[k]
+            });
+        }
+    }
+}
+
+var sel = window.getSelection();
+
+function removeAllChildren(elem) {
+    while (elem.firstChild)
+        elem.removeChild(elem.firstChild);
+}
+
+var testNo = 0;
+function doTestIfAny() {
+    // Clean up first.
+    removeAllChildren(document.getElementById('container'));
+    removeAllChildren(document.getElementById('move-target'));
+
+    var next = testData.shift();
+    if (next)
+        return window.setTimeout(function(){ doTest(++testNo, next); }, 0);
+
+    // No more tests. Tear down.
+    removeAllChildren(document.getElementById('container'));
+    removeAllChildren(document.getElementById('move-target'));
+
+    if (window.internals)
+        internals.setUnifiedTextCheckingEnabled(document, false);
+
+    if (window.layoutTestController) {
+        layoutTestController.setAsynchronousSpellCheckingEnabled(false);
+        layoutTestController.notifyDone();
+    }
+}
+
+var requestId;
+var lastRequestId;
+function doTest(testNo, testData) {
+    function createElement(kind) {
+        if (kind == 'textarea' || kind == 'input')
+            return document.createElement(kind);
+
+        var div = document.createElement('div');
+        div.setAttribute('contenteditable', true);
+        return div;
+    }
+
+    debug("");
+    debug("Test Start: " + testNo);
+
+    var source = document.getElementById(testData.sourceId);
+    var destination = createElement(testData.destElem);
+    document.getElementById('container').appendChild(destination);
+
+    if (window.internals)
+        lastRequestId = internals.lastSpellCheckRequestSequence(document);
+
+    // A spellcheck request will be invoked.
+    doCopyAndPaste(source, destination);
+
+    setTimeout(function() {
+        if (window.internals)
+            requestId = internals.lastSpellCheckRequestSequence(document);
+        shouldBeGreaterThanOrEqual('requestId', 'lastRequestId + 1');
+
+        // Then, tweak
+        tweak(destination, testData.tweak);
+
+        waitForSpellCheckRequestDone(requestId, destination, testData.tweak, 10, 1);
+    }, 0);
+}
+
+function doCopyAndPaste(source, dest) {
+    function focusOn(elem) {
+        if (elem instanceof HTMLInputElement || elem instanceof HTMLTextAreaElement)
+            elem.focus();
+        else
+            sel.selectAllChildren(elem);
+    }
+
+    sel.selectAllChildren(source);
+    document.execCommand("Copy");
+
+    focusOn(dest);
+    document.execCommand("Paste");
+}
+
+function tweak(elem, kind) {
+    switch (kind) {
+    case 'delete':
+        elem.parentNode.removeChild(elem);
+        return;
+    case 'move':
+        var target = document.getElementById('move-target');
+        target.appendChild(elem);
+        return;
+    case 'mutate':
+        if (elem instanceof HTMLInputElement || elem instanceof HTMLTextAreaElement)
+            elem.value = 'zzz';
+        else
+            elem.innerHTML = 'zzz';
+        return;
+    default:
+        testFailed('Unknown kind of tweak');
+        return;
+    }
+}
+
+function waitForSpellCheckRequestDone(requestId, destination, tweakKind, restTry, nsleep) {
+    // No more try.
+    if (restTry <= 0) {
+        testFailed('Failed verification');
+        setTimeout(doTestIfAny, 0);
+        return;
+    }
+
+    if (window.internals)
+        var lastProcessedId = internals.lastSpellCheckProcessedSequence(document);
+
+    if (requestId != lastProcessedId) {
+        setTimeout(function() {
+            waitForSpellCheckRequestDone(requestId, destination, tweakKind, restTry - 1, nsleep * 2);
+        }, nsleep);
+        return;
+    }
+
+    if (verifyExistenceOfMarkers(destination, tweakKind)) {
+        testPassed('Request has been processed.');
+    } else {
+        testFailed('Request has been processed but we detected unexpected marker location.');
+    }
+
+    setTimeout(doTestIfAny, 0);
+}
+
+function findFirstTextNode(node)
+{
+    function iterToFindFirstTextNode(node)
+    {
+        if (node instanceof Text)
+            return node;
+
+        var childNodes = node.childNodes;
+        for (var i = 0; i < childNodes.length; ++i) {
+            var n = iterToFindFirstTextNode(childNodes[i]);
+            if (n)
+                return n;
+        }
+
+        return null;
+    }
+
+    if (node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement)
+        return iterToFindFirstTextNode(internals.shadowRoot(node));
+    else
+        return iterToFindFirstTextNode(node);
+}
+
+function verifyExistenceOfMarkers(elem, tweakKind) {
+    if (!window.internals)
+        return true;
+
+    switch (tweakKind) {
+    case 'delete':
+        return true;
+    case 'move':
+        // In move, marker should be there.
+        var markerNum = internals.markerCountForNode(findFirstTextNode(elem), "spelling");
+        if (markerNum != 3)
+            return false;
+        for (var i = 0; i < 3; ++i) {
+            var range = internals.markerRangeForNode(findFirstTextNode(elem), "spelling", i);
+            if (range.toString() != "zz")
+                return false;
+        }
+        return true;
+    case 'mutate':
+        // In mutation, there aren't markers.
+        return internals.markerCountForNode(findFirstTextNode(elem), "spelling") == 0;
+    default:
+        testFailed('Unknown kind of tweak');
+        return true;
+    }
+}
+
+doTestIfAny();
+
+var successfullyParsed = true;
+</script>
+<script src="../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
index a0995dd..62fe2c1 100644 (file)
@@ -1220,6 +1220,7 @@ editing/spelling/spelling-backspace-between-lines.html
 editing/spelling/spellcheck-paste.html
 editing/spelling/spellcheck-queue.html
 editing/spelling/spellcheck-sequencenum.html
+editing/spelling/spellcheck-async-mutation.html
 
 # For https://bugs.webkit.org/show_bug.cgi?id=50758
 # These require DRT setSerializeHTTPLoads implementation to be reliable.
index dc29cce..629b3dc 100644 (file)
@@ -96,6 +96,7 @@ http/tests/media/video-seekable-stall.html
 
 # Available 10.6 or later
 editing/spelling/spellcheck-paste.html
+editing/spelling/spellcheck-async-mutation.html
 editing/spelling/grammar-paste.html
 fast/html/set-text-direction.html
 
index b87f659..43aec05 100644 (file)
@@ -1007,6 +1007,7 @@ editing/spelling/spelling-attribute-at-child.html
 editing/spelling/spellcheck-paste.html
 editing/spelling/spellcheck-queue.html
 editing/spelling/spellcheck-sequencenum.html
+editing/spelling/spellcheck-async-mutation.html
 
 # [Qt][GTK] editing/spelling/spellcheck-async.html fails
 # https://bugs.webkit.org/show_bug.cgi?id=73003