Chromium Code Reviews| 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..1fc70d6a4bed21940ffa4642378d54c0ef0d5316 |
| --- /dev/null |
| +++ b/LayoutTests/fast/dom/custom/processing-stack-recursion.html |
| @@ -0,0 +1,155 @@ |
| +<!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 |
| + B set attribute on A |
| + C set attribute on B |
| + B set attribute on C |
| + A inserted parent node |
| + A removed parent node |
| + A inserted parent node |
| +A removed parent node</template> |
| +<script> |
| +test(function () { |
| + // Helpers for logging |
| + var buffer = []; |
| + var indentation = ''; |
| + function log(msg) { |
| + buffer.push(indentation + msg); |
| + } |
| + function indent() { |
| + indentation += ' '; |
| + } |
| + function unindent() { |
| + indentation = indentation.substring(3); |
| + } |
|
Yuta Kitamura
2013/07/08 03:40:49
I see a lot of instances of:
- "log(...); indent()
|
| + |
| + // 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) { |
| + log('A removing parent node'); indent(); |
| + this.parentNode.remove(); |
| + unindent(); log('A removed parent node'); |
| + } |
| + }; |
| + protoA.leftDocumentCallback = function () { |
| + log('A left'); |
| + log('A inserting parent node'); indent(); |
| + document.body.appendChild(this.parentNode); |
| + unindent(); log('A inserted parent node'); |
| + }; |
| + 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) { |
| + log('B setting attribute on C'); indent(); |
| + this.parentNode.querySelector('x-c').setAttribute('by', 'b'); |
| + unindent(); log('B set attribute on C'); |
| + } |
| + }; |
| + protoB.leftDocumentCallback = function () { |
| + log('B left'); |
| + }; |
| + protoB.attributeChangedCallback = function (name, oldValue, newValue) { |
| + log('B@' + name + ' ' + oldValue + '->' + newValue); |
| + log('B setting attribute on A'); indent(); |
| + this.parentNode.querySelector('x-a').setAttribute('by', 'b'); |
| + unindent(); log('B set attribute on A'); |
| + }; |
| + 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); |
| + log('C setting attribute on B'); indent(); |
| + this.parentNode.querySelector('x-b').setAttribute('by', 'c'); |
| + unindent(); log('C set attribute on B'); |
| + }; |
| + 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> |