зеркало из https://github.com/mozilla/gecko-dev.git
403 строки
15 KiB
HTML
403 строки
15 KiB
HTML
<!DOCTYPE HTML>
|
|
<html>
|
|
<!--
|
|
https://bugzilla.mozilla.org/show_bug.cgi?id=783129
|
|
-->
|
|
<head>
|
|
<title>Test for document.registerElement lifecycle callback</title>
|
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
|
</head>
|
|
<body>
|
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
|
|
<div id="container">
|
|
<x-hello id="hello"></x-hello>
|
|
<button id="extbutton" is="x-button"></button>
|
|
</div>
|
|
<script>
|
|
|
|
var container = document.getElementById("container");
|
|
|
|
// Tests callbacks after registering element type that is already in the document.
|
|
// create element in document -> register -> remove from document
|
|
function testRegisterUnresolved() {
|
|
var helloElem = document.getElementById("hello");
|
|
|
|
var createdCallbackCalled = false;
|
|
var attachedCallbackCalled = false;
|
|
var detachedCallbackCalled = false;
|
|
|
|
var p = Object.create(HTMLElement.prototype);
|
|
p.createdCallback = function() {
|
|
is(helloElem.__proto__, p, "Prototype should be adjusted just prior to invoking the created callback.");
|
|
is(createdCallbackCalled, false, "Created callback should only be called once in this tests.");
|
|
is(this, helloElem, "The 'this' value should be the custom element.");
|
|
createdCallbackCalled = true;
|
|
};
|
|
|
|
p.attachedCallback = function() {
|
|
is(createdCallbackCalled, true, "Created callback should be called before attached");
|
|
is(attachedCallbackCalled, false, "attached callback should only be called once in this test.");
|
|
is(this, helloElem, "The 'this' value should be the custom element.");
|
|
attachedCallbackCalled = true;
|
|
};
|
|
|
|
p.detachedCallback = function() {
|
|
is(attachedCallbackCalled, true, "attached callback should be called before detached");
|
|
is(detachedCallbackCalled, false, "detached callback should only be called once in this test.");
|
|
detachedCallbackCalled = true;
|
|
is(this, helloElem, "The 'this' value should be the custom element.");
|
|
runNextTest();
|
|
};
|
|
|
|
p.attributeChangedCallback = function(name, oldValue, newValue) {
|
|
ok(false, "attributeChanged callback should never be called in this test.");
|
|
};
|
|
|
|
document.registerElement("x-hello", { prototype: p });
|
|
is(createdCallbackCalled, true, "created callback should be called when control returns to script from user agent code");
|
|
|
|
// Remove element from document to trigger detached callback.
|
|
container.removeChild(helloElem);
|
|
}
|
|
|
|
// Tests callbacks after registering an extended element type that is already in the document.
|
|
// create element in document -> register -> remove from document
|
|
function testRegisterUnresolvedExtended() {
|
|
var buttonElem = document.getElementById("extbutton");
|
|
|
|
var createdCallbackCalled = false;
|
|
var attachedCallbackCalled = false;
|
|
var detachedCallbackCalled = false;
|
|
|
|
var p = Object.create(HTMLButtonElement.prototype);
|
|
p.createdCallback = function() {
|
|
is(buttonElem.__proto__, p, "Prototype should be adjusted just prior to invoking the created callback.");
|
|
is(createdCallbackCalled, false, "Created callback should only be called once in this tests.");
|
|
is(this, buttonElem, "The 'this' value should be the custom element.");
|
|
createdCallbackCalled = true;
|
|
};
|
|
|
|
p.attachedCallback = function() {
|
|
is(createdCallbackCalled, true, "Created callback should be called before attached");
|
|
is(attachedCallbackCalled, false, "attached callback should only be called once in this test.");
|
|
is(this, buttonElem, "The 'this' value should be the custom element.");
|
|
attachedCallbackCalled = true;
|
|
};
|
|
|
|
p.detachedCallback = function() {
|
|
is(attachedCallbackCalled, true, "attached callback should be called before detached");
|
|
is(detachedCallbackCalled, false, "detached callback should only be called once in this test.");
|
|
detachedCallbackCalled = true;
|
|
is(this, buttonElem, "The 'this' value should be the custom element.");
|
|
runNextTest();
|
|
};
|
|
|
|
p.attributeChangedCallback = function(name, oldValue, newValue) {
|
|
ok(false, "attributeChanged callback should never be called in this test.");
|
|
};
|
|
|
|
document.registerElement("x-button", { prototype: p, extends: "button" });
|
|
is(createdCallbackCalled, true, "created callback should be called when control returns to script from user agent code");
|
|
|
|
// Remove element from document to trigger detached callback.
|
|
container.removeChild(buttonElem);
|
|
}
|
|
|
|
function testInnerHTML() {
|
|
var createdCallbackCalled = false;
|
|
|
|
var p = Object.create(HTMLElement.prototype);
|
|
p.createdCallback = function() {
|
|
is(createdCallbackCalled, false, "created callback should only be called once in this test.");
|
|
createdCallbackCalled = true;
|
|
};
|
|
|
|
document.registerElement("x-inner-html", { prototype: p });
|
|
var div = document.createElement(div);
|
|
div.innerHTML = '<x-inner-html></x-inner-html>';
|
|
is(createdCallbackCalled, true, "created callback should be called after setting innerHTML.");
|
|
runNextTest();
|
|
}
|
|
|
|
function testInnerHTMLExtended() {
|
|
var createdCallbackCalled = false;
|
|
|
|
var p = Object.create(HTMLButtonElement.prototype);
|
|
p.createdCallback = function() {
|
|
is(createdCallbackCalled, false, "created callback should only be called once in this test.");
|
|
createdCallbackCalled = true;
|
|
};
|
|
|
|
document.registerElement("x-inner-html-extended", { prototype: p, extends: "button" });
|
|
var div = document.createElement(div);
|
|
div.innerHTML = '<button is="x-inner-html-extended"></button>';
|
|
is(createdCallbackCalled, true, "created callback should be called after setting innerHTML.");
|
|
runNextTest();
|
|
}
|
|
|
|
function testInnerHTMLUpgrade() {
|
|
var createdCallbackCalled = false;
|
|
|
|
var div = document.createElement(div);
|
|
div.innerHTML = '<x-inner-html-upgrade></x-inner-html-upgrade>';
|
|
|
|
var p = Object.create(HTMLElement.prototype);
|
|
p.createdCallback = function() {
|
|
is(createdCallbackCalled, false, "created callback should only be called once in this test.");
|
|
createdCallbackCalled = true;
|
|
};
|
|
|
|
document.registerElement("x-inner-html-upgrade", { prototype: p });
|
|
is(createdCallbackCalled, true, "created callback should be called after registering.");
|
|
runNextTest();
|
|
}
|
|
|
|
function testInnerHTMLExtendedUpgrade() {
|
|
var createdCallbackCalled = false;
|
|
|
|
var div = document.createElement(div);
|
|
div.innerHTML = '<button is="x-inner-html-extended-upgrade"></button>';
|
|
|
|
var p = Object.create(HTMLButtonElement.prototype);
|
|
p.createdCallback = function() {
|
|
is(createdCallbackCalled, false, "created callback should only be called once in this test.");
|
|
createdCallbackCalled = true;
|
|
};
|
|
|
|
document.registerElement("x-inner-html-extended-upgrade", { prototype: p, extends: "button" });
|
|
is(createdCallbackCalled, true, "created callback should be called after registering.");
|
|
runNextTest();
|
|
}
|
|
|
|
// Test callback when creating element after registering an element type.
|
|
// register -> create element -> insert into document -> remove from document
|
|
function testRegisterResolved() {
|
|
var createdCallbackCalled = false;
|
|
var attachedCallbackCalled = false;
|
|
var detachedCallbackCalled = false;
|
|
|
|
var createdCallbackThis;
|
|
|
|
var p = Object.create(HTMLElement.prototype);
|
|
p.createdCallback = function() {
|
|
is(createdCallbackCalled, false, "Created callback should only be called once in this test.");
|
|
createdCallbackThis = this;
|
|
createdCallbackCalled = true;
|
|
};
|
|
|
|
p.attachedCallback = function() {
|
|
is(createdCallbackCalled, true, "created callback should be called before attached callback.");
|
|
is(attachedCallbackCalled, false, "attached callback should only be called on in this test.");
|
|
is(this, createdElement, "The 'this' value should be the custom element.");
|
|
attachedCallbackCalled = true;
|
|
};
|
|
|
|
p.detachedCallback = function() {
|
|
is(attachedCallbackCalled, true, "attached callback should be called before detached");
|
|
is(detachedCallbackCalled, false, "detached callback should only be called once in this test.");
|
|
is(this, createdElement, "The 'this' value should be the custom element.");
|
|
detachedCallbackCalled = true;
|
|
runNextTest();
|
|
};
|
|
|
|
p.attributeChangedCallback = function() {
|
|
ok(false, "attributeChanged callback should never be called in this test.");
|
|
};
|
|
|
|
document.registerElement("x-resolved", { prototype: p });
|
|
is(createdCallbackCalled, false, "Created callback should not be called when custom element instance has not been created.");
|
|
|
|
var createdElement = document.createElement("x-resolved");
|
|
is(createdCallbackThis, createdElement, "The 'this' value in the created callback should be the custom element.");
|
|
is(createdElement.__proto__, p, "Prototype of custom element should be the registered prototype.");
|
|
|
|
// Insert element into document to trigger attached callback.
|
|
container.appendChild(createdElement);
|
|
|
|
// Remove element from document to trigger detached callback.
|
|
container.removeChild(createdElement);
|
|
}
|
|
|
|
// Callbacks should always be the same ones when registered.
|
|
function testChangingCallback() {
|
|
var p = Object.create(HTMLElement.prototype);
|
|
var callbackCalled = false;
|
|
p.attributeChangedCallback = function(name, oldValue, newValue) {
|
|
is(callbackCalled, false, "Callback should only be called once in this test.");
|
|
callbackCalled = true;
|
|
runNextTest();
|
|
};
|
|
|
|
document.registerElement("x-test-callback", { prototype: p });
|
|
|
|
p.attributeChangedCallback = function(name, oldValue, newValue) {
|
|
ok(false, "Only callbacks at registration should be called.");
|
|
};
|
|
|
|
var elem = document.createElement("x-test-callback");
|
|
elem.setAttribute("foo", "bar");
|
|
}
|
|
|
|
function testAttributeChanged() {
|
|
var createdCallbackCalled = false;
|
|
|
|
var createdElement;
|
|
var createdCallbackThis;
|
|
|
|
var p = Object.create(HTMLElement.prototype);
|
|
p.createdCallback = function() {
|
|
is(createdCallbackCalled, false, "Created callback should only be called once in this test.");
|
|
createdCallbackThis = this;
|
|
createdCallbackCalled = true;
|
|
};
|
|
|
|
// Sequence of callback arguments that we expect from attribute changed callback
|
|
// after changing attributes values in a specific order.
|
|
var expectedCallbackArguments = [
|
|
// [oldValue, newValue]
|
|
[null, "newvalue"], // Setting the attribute value to "newvalue"
|
|
["newvalue", "nextvalue"], // Changing the attribute value from "newvalue" to "nextvalue"
|
|
["nextvalue", ""], // Changing the attribute value from "nextvalue" to empty string
|
|
["", null], // Removing the attribute.
|
|
];
|
|
|
|
p.attributeChangedCallback = function(name, oldValue, newValue) {
|
|
is(createdCallbackCalled, true, "created callback should be called before attribute changed.");
|
|
is(this, createdElement, "The 'this' value should be the custom element.");
|
|
ok(expectedCallbackArguments.length > 0, "Attribute changed callback should not be called more than expected.");
|
|
|
|
is(name, "changeme", "name arugment in attribute changed callback should be the name of the changed attribute.");
|
|
|
|
var expectedArgs = expectedCallbackArguments.shift();
|
|
is(oldValue, expectedArgs[0], "The old value argument should match the expected value.");
|
|
is(newValue, expectedArgs[1], "The new value argument should match the expected value.");
|
|
|
|
if (expectedCallbackArguments.length === 0) {
|
|
// Done with the attribute changed callback test.
|
|
runNextTest();
|
|
}
|
|
};
|
|
|
|
document.registerElement("x-attrchange", { prototype: p });
|
|
|
|
var createdElement = document.createElement("x-attrchange");
|
|
is(createdCallbackThis, createdElement, "The 'this' value in the created callback should be the custom element.");
|
|
createdElement.setAttribute("changeme", "newvalue");
|
|
createdElement.setAttribute("changeme", "nextvalue");
|
|
createdElement.setAttribute("changeme", "");
|
|
createdElement.removeAttribute("changeme");
|
|
}
|
|
|
|
function testAttributeChangedExtended() {
|
|
var p = Object.create(HTMLButtonElement.prototype);
|
|
var callbackCalled = false;
|
|
p.attributeChangedCallback = function(name, oldValue, newValue) {
|
|
is(callbackCalled, false, "Callback should only be called once in this test.");
|
|
callbackCalled = true;
|
|
runNextTest();
|
|
};
|
|
|
|
document.registerElement("x-extended-attribute-change", { prototype: p, extends: "button" });
|
|
|
|
var elem = document.createElement("button", "x-extended-attribute-change");
|
|
elem.setAttribute("foo", "bar");
|
|
}
|
|
|
|
// Creates a custom element that is an upgrade candidate (no registration)
|
|
// and mutate the element in ways that would call callbacks for registered
|
|
// elements.
|
|
function testUpgradeCandidate() {
|
|
var createdElement = document.createElement("x-upgrade-candidate");
|
|
container.appendChild(createdElement);
|
|
createdElement.setAttribute("foo", "bar");
|
|
container.removeChild(createdElement);
|
|
ok(true, "Nothing bad should happen when trying to mutating upgrade candidates.");
|
|
runNextTest();
|
|
}
|
|
|
|
function testNotInDocEnterLeave() {
|
|
var p = Object.create(HTMLElement.prototype);
|
|
|
|
p.attached = function() {
|
|
ok(false, "attached should not be called when not entering the document.");
|
|
};
|
|
|
|
p.detached = function() {
|
|
ok(false, "leaveView should not be called when not leaving the document.");
|
|
};
|
|
|
|
var createdElement = document.createElement("x-destined-for-fragment");
|
|
|
|
document.registerElement("x-destined-for-fragment", { prototype: p });
|
|
|
|
var fragment = new DocumentFragment();
|
|
fragment.appendChild(createdElement);
|
|
fragment.removeChild(createdElement);
|
|
|
|
var divNotInDoc = document.createElement("div");
|
|
divNotInDoc.appendChild(createdElement);
|
|
divNotInDoc.removeChild(createdElement);
|
|
|
|
runNextTest();
|
|
}
|
|
|
|
function testEnterLeaveView() {
|
|
var attachedCallbackCalled = false;
|
|
var detachedCallbackCalled = false;
|
|
|
|
var p = Object.create(HTMLElement.prototype);
|
|
p.attachedCallback = function() {
|
|
is(attachedCallbackCalled, false, "attached callback should only be called on in this test.");
|
|
attachedCallbackCalled = true;
|
|
};
|
|
|
|
p.detachedCallback = function() {
|
|
is(attachedCallbackCalled, true, "attached callback should be called before detached");
|
|
is(detachedCallbackCalled, false, "detached callback should only be called once in this test.");
|
|
detachedCallbackCalled = true;
|
|
runNextTest();
|
|
};
|
|
|
|
var div = document.createElement("div");
|
|
document.registerElement("x-element-in-div", { prototype: p });
|
|
var customElement = document.createElement("x-element-in-div");
|
|
div.appendChild(customElement);
|
|
is(attachedCallbackCalled, false, "Appending a custom element to a node that is not in the document should not call the attached callback.");
|
|
|
|
container.appendChild(div);
|
|
container.removeChild(div);
|
|
}
|
|
|
|
var testFunctions = [
|
|
testRegisterUnresolved,
|
|
testRegisterUnresolvedExtended,
|
|
testInnerHTML,
|
|
testInnerHTMLExtended,
|
|
testInnerHTMLUpgrade,
|
|
testInnerHTMLExtendedUpgrade,
|
|
testRegisterResolved,
|
|
testAttributeChanged,
|
|
testAttributeChangedExtended,
|
|
testUpgradeCandidate,
|
|
testChangingCallback,
|
|
testNotInDocEnterLeave,
|
|
testEnterLeaveView,
|
|
SimpleTest.finish
|
|
];
|
|
|
|
function runNextTest() {
|
|
if (testFunctions.length > 0) {
|
|
var nextTestFunction = testFunctions.shift();
|
|
nextTestFunction();
|
|
}
|
|
}
|
|
|
|
SimpleTest.waitForExplicitFinish();
|
|
|
|
runNextTest();
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|