| Index: third_party/WebKit/LayoutTests/custom-elements/spec/parsing.html
|
| diff --git a/third_party/WebKit/LayoutTests/custom-elements/spec/parsing.html b/third_party/WebKit/LayoutTests/custom-elements/spec/parsing.html
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e6b3842870b1a0607f3fa1db488604b4d26bccd6
|
| --- /dev/null
|
| +++ b/third_party/WebKit/LayoutTests/custom-elements/spec/parsing.html
|
| @@ -0,0 +1,285 @@
|
| +<!DOCTYPE html>
|
| +<script src="../../resources/testharness.js"></script>
|
| +<script src="../../resources/testharnessreport.js"></script>
|
| +<script src="resources/custom-elements-helpers.js"></script>
|
| +<body>
|
| +<script>
|
| +'use strict';
|
| +
|
| +// Looks up the preceeding element (which should be a template
|
| +// element) and creates a Promise test. The test name is taken from
|
| +// the template's data-test attribute.
|
| +//
|
| +// The content of the template is loaded into an iframe. On load, f
|
| +// is passed the frame's content window to run assertions.
|
| +function test_with_content(f) {
|
| + let t = document.currentScript.previousElementSibling;
|
| + test_with_window(f, t.dataset.test, t.innerHTML);
|
| +}
|
| +
|
| +// Searches the document for an iframe with the specified content window.
|
| +function findFrameWithWindow(w) {
|
| + return Array.prototype.find.call(document.querySelectorAll('iframe'), (f) => {
|
| + return f.contentWindow === w;
|
| + });
|
| +}
|
| +
|
| +test_with_window((w) => {
|
| + assert_equals(findFrameWithWindow(w).contentWindow, w,
|
| + 'should find the frame with this window');
|
| + assert_equals(findFrameWithWindow(window), undefined,
|
| + 'should return undefined if there is no such frame');
|
| +}, 'sanity check the findFrameWithWindow function');
|
| +</script>
|
| +
|
| +<template data-test="the parser synchronously creates elements">
|
| + <script>
|
| + 'use strict';
|
| +
|
| + window.invocations = [];
|
| + customElements.define('a-a', class extends HTMLElement {
|
| + constructor() {
|
| + super();
|
| + invocations.push('constructor');
|
| + }
|
| + static get observedAttributes() { return ['x']; }
|
| + attributeChangedCallback(name, oldValue, newValue, nsuri) {
|
| + invocations.push(`${name}="${newValue}"`);
|
| + }
|
| + connectedCallback() {
|
| + invocations.push('connected');
|
| + }
|
| + });
|
| + </script>
|
| + <a-a x="y">
|
| + <script>
|
| + 'use strict';
|
| +
|
| + invocations.push('script');
|
| + </script>
|
| + </a-a>
|
| +</template>
|
| +<script>
|
| +'use strict';
|
| +
|
| +test_with_content((w) => {
|
| + assert_array_equals(w.invocations,
|
| + ['constructor', 'x="y"', 'connected', 'script']);
|
| +});
|
| +</script>
|
| +
|
| +<template data-test="element creation failure produces unknown element">
|
| + <script>
|
| + 'use strict';
|
| +
|
| + customElements.define('a-a', class extends HTMLElement {
|
| + constructor() {
|
| + super();
|
| +
|
| + // Returning this different, in-use element causes element
|
| + // creation to fail in
|
| + // https://dom.spec.whatwg.org/#concept-create-element steps
|
| + // 6.4-9, eg: "If result has children then then throw a
|
| + // NotSupportedError."
|
| + return document.documentElement;
|
| + }
|
| + });
|
| + </script>
|
| + <a-a>
|
| +</template>
|
| +<script>
|
| +'use strict';
|
| +
|
| +test_with_content((w) => {
|
| + let e = w.document.querySelector('a-a');
|
| + assert_true(e.matches(':not(:defined)'));
|
| + assert_equals(Object.getPrototypeOf(e), w.HTMLUnknownElement.prototype);
|
| +});
|
| +</script>
|
| +
|
| +<template data-test="modify tree during creation">
|
| + <script>
|
| + 'use strict';
|
| +
|
| + customElements.define('a-a', class extends HTMLElement {
|
| + constructor() {
|
| + super();
|
| + document.querySelector('b').remove();
|
| + }
|
| + });
|
| + </script>
|
| + <b>
|
| + <a-a>
|
| + </b>
|
| +</template>
|
| +<script>
|
| +'use strict';
|
| +
|
| +test_with_content((w) => {
|
| + assert_equals(w.document.querySelectorAll('b').length, 0);
|
| +});
|
| +</script>
|
| +
|
| +<template data-test="destructive writes are blocked during construction">
|
| + <script>
|
| + // Custom element constructors do not set the insertion point, which
|
| + // makes document.write() "destructive." However they increment the
|
| + // ignore-destructive-writes counter, which blocks document.write.
|
| + // https://html.spec.whatwg.org/#create-an-element-for-the-token
|
| + // https://github.com/whatwg/html/issues/1630
|
| + // https://html.spec.whatwg.org/#document.write()
|
| + 'use strict';
|
| +
|
| + window.invocations = [];
|
| + customElements.define('a-a', class extends HTMLElement {
|
| + constructor() {
|
| + super();
|
| + invocations.push('constructor');
|
| + document.write(
|
| + `<script>'use strict'; invocations.push('written');</scr${'i'}pt>`);
|
| + }
|
| + connectedCallback() {
|
| + invocations.push('connected');
|
| + }
|
| + });
|
| + </script>
|
| + <a-a>
|
| + <script>
|
| + 'use strict';
|
| + invocations.push('parsed');
|
| + </script>
|
| +</template>
|
| +<script>
|
| +'use strict';
|
| +
|
| +test_with_content((w) => {
|
| + assert_array_equals(
|
| + w.invocations,
|
| + ['constructor', 'connected', 'parsed'],
|
| + 'the destructive document.write content should have been ignored');
|
| +});
|
| +</script>
|
| +
|
| +<template data-test="non-destructive writes are not blocked">
|
| + <script>
|
| + // Script running sets the insertion point, which makes makes
|
| + // document.write() "non-destructive." Custom elements do not block
|
| + // non-destructive writes.
|
| + // https://html.spec.whatwg.org/#create-an-element-for-the-token
|
| + // https://html.spec.whatwg.org/#document.write()
|
| + 'use strict';
|
| +
|
| + window.invocations = [];
|
| + customElements.define('a-a', class extends HTMLElement {
|
| + constructor() {
|
| + super();
|
| + invocations.push('constructor');
|
| + document.write(
|
| + `<script>'use strict'; invocations.push('written');</scr${'i'}pt>`);
|
| + }
|
| + connectedCallback() {
|
| + invocations.push('connected');
|
| + }
|
| + });
|
| + document.write('<a-a>');
|
| + invocations.push('post write');
|
| + </script>
|
| + <script>
|
| + 'use strict';
|
| + invocations.push('parsed');
|
| + </script>
|
| +</template>
|
| +<script>
|
| +'use strict';
|
| +
|
| +test_with_content((w) => {
|
| + assert_array_equals(
|
| + w.invocations,
|
| + ['constructor', 'connected', 'post write', 'written', 'parsed'],
|
| + 'the non-destructive document.write content should have been inserted');
|
| +});
|
| +</script>
|
| +
|
| +<template data-test="innerHTML is not blocked by custom element constructors">
|
| + <script>
|
| + 'use strict';
|
| +
|
| + window.invocations = [];
|
| + customElements.define('a-a', class extends HTMLElement {
|
| + constructor() {
|
| + super();
|
| + invocations.push(`construct ${this.id}`);
|
| + if (!this.id) {
|
| + // If the ID attribute is not set, this was created
|
| + // synchronously by the parser. Adding children at this point
|
| + // would cause creation to fail, so embiggen the previous
|
| + // element instead.
|
| + document.querySelector('span').innerHTML = `<a-a id="r">`;
|
| + }
|
| + }
|
| + connectedCallback() {
|
| + invocations.push(`connected ${this.parentNode.localName}/${this.id}`);
|
| + }
|
| + });
|
| + </script>
|
| + <span></span>
|
| + <a-a id="q"></a-a>
|
| + <script>
|
| + 'use strict';
|
| + invocations.push('parsed');
|
| + </script>
|
| +</template>
|
| +<script>
|
| +'use strict';
|
| +
|
| +test_with_content((w) => {
|
| + assert_array_equals(
|
| + w.invocations,
|
| + ['construct ', 'construct r', 'connected span/r', 'connected body/q',
|
| + 'parsed'],
|
| + 'custom element constructors should not block innerHTML');
|
| +});
|
| +</script>
|
| +
|
| +
|
| +<template data-test="parsing without a browsing context should not create custom elements">
|
| + <body>
|
| + <script>
|
| + 'use strict';
|
| +
|
| + let f = parent.findFrameWithWindow(window);
|
| + f.invocations = [];
|
| +
|
| + customElements.define('a-a', class extends HTMLElement {
|
| + constructor() {
|
| + super();
|
| + f.invocations.push(this);
|
| + }
|
| + });
|
| + </script>
|
| + <a-a></a-a>
|
| + <script>
|
| + f.detached = document.implementation.createHTMLDocument();
|
| + f.detached.documentElement.appendChild(document.body);
|
| + </script>
|
| + <a-a></a-a>
|
| +</template>
|
| +<script>
|
| +'use strict';
|
| +
|
| +test_with_content((w) => {
|
| + let f = findFrameWithWindow(w);
|
| + assert_array_equals(f.invocations,
|
| + [f.detached.querySelector('a-a:first-of-type')],
|
| + 'one element should have been constructed');
|
| + assert_true(f.invocations[0].matches(':defined'),
|
| + 'the element should have been created successfully');
|
| +
|
| + let elements = f.detached.querySelectorAll('a-a');
|
| + console.log(f.invocations[0].parentNode);
|
| + assert_equals(elements.length, 2,
|
| + 'two elements should have been created');
|
| + assert_equals(Object.getPrototypeOf(elements[1]), w.HTMLElement.prototype,
|
| + 'the second element should be un-upgraded, not failed');
|
| +});
|
| +</script>
|
|
|