| Index: LayoutTests/fast/dom/custom/processing-stack-recursion.html
|
| diff --git a/LayoutTests/fast/dom/custom/processing-stack-recursion.html b/LayoutTests/fast/dom/custom/processing-stack-recursion.html
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..49e861b872d458272f1331ff69d71e756e18a646
|
| --- /dev/null
|
| +++ b/LayoutTests/fast/dom/custom/processing-stack-recursion.html
|
| @@ -0,0 +1,157 @@
|
| +<!DOCTYPE html>
|
| +<script src="../../../resources/testharness.js"></script>
|
| +<script src="../../../resources/testharnessreport.js"></script>
|
| +<body>
|
| +<template id="expected">A entered
|
| +A removing parent node
|
| + A left
|
| + A inserting parent node
|
| + A entered
|
| + A removing parent node
|
| + A left
|
| + A inserting parent node
|
| + A entered
|
| + B entered
|
| + B left
|
| + B entered
|
| + B setting attribute on C
|
| + C entered
|
| + C left
|
| + C entered
|
| + C left
|
| + C entered
|
| + C@by null->b
|
| + C setting attribute on B
|
| + B left
|
| + B entered
|
| + B@by null->c
|
| + B setting attribute on A
|
| + A@by null->b
|
| + done
|
| + done
|
| + done
|
| + done
|
| + done
|
| + done
|
| +done</template>
|
| +<script>
|
| +test(function () {
|
| + // Helpers for logging
|
| + var buffer = [];
|
| + var indentation = '';
|
| + function log(msg) {
|
| + buffer.push(indentation + msg);
|
| + }
|
| + function indented(msg) {
|
| + log(msg);
|
| + indentation += ' ';
|
| + }
|
| + function unindent() {
|
| + indentation = indentation.substring(3);
|
| + log('done');
|
| + }
|
| +
|
| + // This tests recursion and the processing stack. Specifically:
|
| + //
|
| + // (1) Scheduling callbacks for an element that has callbacks
|
| + // scheduled at an outer level of recursion, but that have not
|
| + // begun to be processed yet.
|
| + //
|
| + // (2) Scheduling callbacks for an element that is in the middle
|
| + // of processing callbacks at an outer level of recursion.
|
| + //
|
| + // (3) Scheduling callbacks for an element that exhaustively
|
| + // processed callbacks at an outer level of recursion.
|
| + //
|
| + // appendChild and remove are used on a subtree containing
|
| + // multiple custom elements. In this way it is possible to
|
| + // schedule callbacks for multiple custom elements with one DOM
|
| + // call.
|
| + //
|
| + // The test creates this tree:
|
| + //
|
| + // <div>
|
| + // <x-a></x-a>
|
| + // <x-b></x-b>
|
| + // <x-c></x-c>
|
| + // </div>
|
| + //
|
| + // x-a pushes its parent in and out of the document, thus scheduling
|
| + // work for x-b and x-c at every level of recursion.
|
| + //
|
| + // Then x-b processes half its queue before setting an attribute
|
| + // on x-c. This tests case (1) because x-c has not begun its queue
|
| + // yet.
|
| + //
|
| + // x-c turns around and sets and attribute on x-b. This tests case
|
| + // (2) because x-b is half way through processing its queue.
|
| + //
|
| + // x-b turns around and sets an attribute on x-a. This tests case
|
| + // (3) because x-a has finished processing its queue.
|
| +
|
| + var protoA = Object.create(HTMLElement.prototype);
|
| + var n = 0;
|
| + protoA.enteredDocumentCallback = function () {
|
| + log('A entered');
|
| + n++;
|
| + if (n < 3) {
|
| + indented('A removing parent node');
|
| + this.parentNode.remove();
|
| + unindent();
|
| + }
|
| + };
|
| + protoA.leftDocumentCallback = function () {
|
| + log('A left');
|
| + indented('A inserting parent node');
|
| + document.body.appendChild(this.parentNode);
|
| + unindent();
|
| + };
|
| + protoA.attributeChangedCallback = function (name, oldValue, newValue) {
|
| + log('A@' + name + ' ' + oldValue + '->' + newValue);
|
| + };
|
| + var A = document.register('x-a', {prototype: protoA});
|
| +
|
| + var protoB = Object.create(HTMLElement.prototype);
|
| + var m = 0;
|
| + protoB.enteredDocumentCallback = function () {
|
| + log('B entered');
|
| + m++;
|
| + if (m == 2) {
|
| + indented('B setting attribute on C');
|
| + this.parentNode.querySelector('x-c').setAttribute('by', 'b');
|
| + unindent();
|
| + }
|
| + };
|
| + protoB.leftDocumentCallback = function () {
|
| + log('B left');
|
| + };
|
| + protoB.attributeChangedCallback = function (name, oldValue, newValue) {
|
| + log('B@' + name + ' ' + oldValue + '->' + newValue);
|
| + indented('B setting attribute on A');
|
| + this.parentNode.querySelector('x-a').setAttribute('by', 'b');
|
| + unindent();
|
| + };
|
| + var B = document.register('x-b', {prototype: protoB});
|
| +
|
| + var protoC = Object.create(HTMLElement.prototype);
|
| + protoC.enteredDocumentCallback = function () {
|
| + log('C entered');
|
| + };
|
| + protoC.leftDocumentCallback = function () {
|
| + log('C left');
|
| + };
|
| + protoC.attributeChangedCallback = function (name, oldValue, newValue) {
|
| + log('C@' + name + ' ' + oldValue + '->' + newValue);
|
| + indented('C setting attribute on B');
|
| + this.parentNode.querySelector('x-b').setAttribute('by', 'c');
|
| + unindent();
|
| + };
|
| + var C = document.register('x-c', {prototype: protoC});
|
| +
|
| + var div = document.createElement('div');
|
| + div.innerHTML = '<div><x-a></x-a><x-b></x-b><x-c></x-c></div>';
|
| + document.body.appendChild(div);
|
| +
|
| + assert_equals(buffer.join('\n'), expected.content.textContent, 'should have generated an identical log');
|
| +}, 'recursively scheduled callbacks');
|
| +</script>
|
|
|