| <!DOCTYPE html> |
| <html> |
| <head> |
| <script src="../../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." |
| ); |
| |
| var jsTestIsAsync = true; |
| |
| if (window.internals) { |
| internals.settings.setAsynchronousSpellCheckingEnabled(true); |
| internals.settings.setUnifiedTextCheckerEnabled(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.testRunner) |
| finishJSTest(); |
| } |
| |
| 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(); |
| |
| // A spellcheck request will be invoked. |
| doCopyAndPaste(source, destination); |
| |
| setTimeout(function() { |
| if (window.internals) |
| requestId = internals.lastSpellCheckRequestSequence(); |
| 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(); |
| |
| 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="../../resources/js-test-post.js"></script> |
| </body> |
| </html> |