summaryrefslogtreecommitdiffstats
path: root/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html
diff options
context:
space:
mode:
Diffstat (limited to 'dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html')
-rw-r--r--dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html402
1 files changed, 402 insertions, 0 deletions
diff --git a/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html b/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html
new file mode 100644
index 000000000..9db9afbf4
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html
@@ -0,0 +1,402 @@
+<!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", {is: "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>