custom-elements-helpers.js (9697B)
1 function create_window_in_test(t, srcdoc) { 2 let p = new Promise((resolve) => { 3 let f = document.createElement('iframe'); 4 f.srcdoc = srcdoc ? srcdoc : ''; 5 f.onload = (event) => { 6 let w = f.contentWindow; 7 t.add_cleanup(() => f.remove()); 8 resolve(w); 9 }; 10 document.body.appendChild(f); 11 }); 12 return p; 13 } 14 15 function create_window_in_test_async(test, mime, doc) { 16 return new Promise((resolve) => { 17 let iframe = document.createElement('iframe'); 18 blob = new Blob([doc], {type: mime}); 19 iframe.src = URL.createObjectURL(blob); 20 iframe.onload = (event) => { 21 let contentWindow = iframe.contentWindow; 22 test.add_cleanup(() => iframe.remove()); 23 resolve(contentWindow); 24 }; 25 document.body.appendChild(iframe); 26 }); 27 } 28 29 function test_with_window(f, name, srcdoc) { 30 promise_test((t) => { 31 return create_window_in_test(t, srcdoc) 32 .then((w) => { 33 f(w, w.document); 34 }); 35 }, name); 36 } 37 38 function define_custom_element_in_window(window, name, observedAttributes) { 39 let log = []; 40 41 class CustomElement extends window.HTMLElement { 42 constructor() { 43 super(); 44 log.push(create_constructor_log(this)); 45 } 46 attributeChangedCallback(...args) { 47 log.push(create_attribute_changed_callback_log(this, ...args)); 48 } 49 connectedCallback() { log.push(create_connected_callback_log(this)); } 50 disconnectedCallback() { log.push(create_disconnected_callback_log(this)); } 51 adoptedCallback(oldDocument, newDocument) { log.push({type: 'adopted', element: this, oldDocument: oldDocument, newDocument: newDocument}); } 52 } 53 CustomElement.observedAttributes = observedAttributes; 54 55 window.customElements.define(name, CustomElement); 56 57 return { 58 name: name, 59 class: CustomElement, 60 takeLog: function () { 61 let currentLog = log; log = []; 62 currentLog.types = () => currentLog.map((entry) => entry.type); 63 currentLog.last = () => currentLog[currentLog.length - 1]; 64 return currentLog; 65 } 66 }; 67 } 68 69 function create_constructor_log(element) { 70 return {type: 'constructed', element: element}; 71 } 72 73 function assert_constructor_log_entry(log, element) { 74 assert_equals(log.type, 'constructed'); 75 assert_equals(log.element, element); 76 } 77 78 function create_connected_callback_log(element) { 79 return {type: 'connected', element: element}; 80 } 81 82 function assert_connected_log_entry(log, element) { 83 assert_equals(log.type, 'connected'); 84 assert_equals(log.element, element); 85 } 86 87 function create_disconnected_callback_log(element) { 88 return {type: 'disconnected', element: element}; 89 } 90 91 function assert_disconnected_log_entry(log, element) { 92 assert_equals(log.type, 'disconnected'); 93 assert_equals(log.element, element); 94 } 95 96 function assert_adopted_log_entry(log, element) { 97 assert_equals(log.type, 'adopted'); 98 assert_equals(log.element, element); 99 } 100 101 function create_adopted_callback_log(element) { 102 return {type: 'adopted', element: element}; 103 } 104 105 function create_attribute_changed_callback_log(element, name, oldValue, newValue, namespace) { 106 return { 107 type: 'attributeChanged', 108 element: element, 109 name: name, 110 namespace: namespace, 111 oldValue: oldValue, 112 newValue: newValue, 113 actualValue: element.getAttributeNS(namespace, name) 114 }; 115 } 116 117 function assert_attribute_log_entry(log, expected) { 118 assert_equals(log.type, 'attributeChanged'); 119 assert_equals(log.name, expected.name); 120 assert_equals(log.oldValue, expected.oldValue); 121 assert_equals(log.newValue, expected.newValue); 122 assert_equals(log.actualValue, expected.newValue); 123 assert_equals(log.namespace, expected.namespace); 124 } 125 126 127 function define_new_custom_element(observedAttributes) { 128 let log = []; 129 let name = 'custom-element-' + define_new_custom_element._element_number++; 130 131 class CustomElement extends HTMLElement { 132 constructor() { 133 super(); 134 log.push({type: 'constructed', element: this}); 135 } 136 attributeChangedCallback(...args) { 137 log.push(create_attribute_changed_callback_log(this, ...args)); 138 } 139 connectedCallback() { log.push({type: 'connected', element: this}); } 140 disconnectedCallback() { log.push({type: 'disconnected', element: this}); } 141 adoptedCallback(oldDocument, newDocument) { log.push({type: 'adopted', element: this, oldDocument: oldDocument, newDocument: newDocument}); } 142 } 143 CustomElement.observedAttributes = observedAttributes; 144 145 customElements.define(name, CustomElement); 146 147 return { 148 name: name, 149 class: CustomElement, 150 takeLog: function () { 151 let currentLog = log; log = []; 152 currentLog.types = () => currentLog.map((entry) => entry.type); 153 currentLog.last = () => currentLog[currentLog.length - 1]; 154 return currentLog; 155 } 156 }; 157 } 158 define_new_custom_element._element_number = 1; 159 160 function define_build_in_custom_element(observedAttributes, extendedElement, extendsOption) { 161 let log = []; 162 let name = 'custom-element-' + define_build_in_custom_element._element_number++; 163 164 class CustomElement extends extendedElement { 165 constructor() { 166 super(); 167 log.push({type: 'constructed', element: this}); 168 } 169 attributeChangedCallback(...args) { 170 log.push(create_attribute_changed_callback_log(this, ...args)); 171 } 172 connectedCallback() { log.push({type: 'connected', element: this}); } 173 disconnectedCallback() { log.push({type: 'disconnected', element: this}); } 174 adoptedCallback(oldDocument, newDocument) { log.push({type: 'adopted', element: this, oldDocument: oldDocument, newDocument: newDocument}); } 175 } 176 CustomElement.observedAttributes = observedAttributes; 177 customElements.define(name, CustomElement, { extends: extendsOption}); 178 179 return { 180 name: name, 181 class: CustomElement, 182 takeLog: function () { 183 let currentLog = log; log = []; 184 currentLog.types = () => currentLog.map((entry) => entry.type); 185 currentLog.last = () => currentLog[currentLog.length - 1]; 186 return currentLog; 187 } 188 }; 189 } 190 define_build_in_custom_element._element_number = 1; 191 192 function document_types() { 193 return [ 194 { 195 name: 'the document', 196 create: function () { return Promise.resolve(document); }, 197 isOwner: true, 198 hasBrowsingContext: true, 199 }, 200 { 201 name: 'the document of the template elements', 202 create: function () { 203 return new Promise(function (resolve) { 204 var template = document.createElementNS('http://www.w3.org/1999/xhtml', 'template'); 205 var doc = template.content.ownerDocument; 206 if (!doc.documentElement) 207 doc.appendChild(doc.createElement('html')); 208 resolve(doc); 209 }); 210 }, 211 hasBrowsingContext: false, 212 }, 213 { 214 name: 'a new document', 215 create: function () { 216 return new Promise(function (resolve) { 217 var doc = new Document(); 218 doc.appendChild(doc.createElement('html')); 219 resolve(doc); 220 }); 221 }, 222 hasBrowsingContext: false, 223 }, 224 { 225 name: 'a cloned document', 226 create: function () { 227 return new Promise(function (resolve) { 228 var doc = document.cloneNode(false); 229 doc.appendChild(doc.createElement('html')); 230 resolve(doc); 231 }); 232 }, 233 hasBrowsingContext: false, 234 }, 235 { 236 name: 'a document created by createHTMLDocument', 237 create: function () { 238 return Promise.resolve(document.implementation.createHTMLDocument()); 239 }, 240 hasBrowsingContext: false, 241 }, 242 { 243 name: 'an HTML document created by createDocument', 244 create: function () { 245 return Promise.resolve(document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null)); 246 }, 247 hasBrowsingContext: false, 248 }, 249 { 250 name: 'the document of an iframe', 251 create: function () { 252 return new Promise(function (resolve, reject) { 253 var iframe = document.createElement('iframe'); 254 iframe.onload = function () { resolve(iframe.contentDocument); } 255 iframe.onerror = function () { reject('Failed to load an empty iframe'); } 256 document.body.appendChild(iframe); 257 }); 258 }, 259 hasBrowsingContext: true, 260 }, 261 { 262 name: 'an HTML document fetched by XHR', 263 create: function () { 264 return new Promise(function (resolve, reject) { 265 var xhr = new XMLHttpRequest(); 266 xhr.open('GET', 'resources/empty-html-document.html'); 267 xhr.overrideMimeType('text/xml'); 268 xhr.onload = function () { resolve(xhr.responseXML); } 269 xhr.onerror = function () { reject('Failed to fetch the document'); } 270 xhr.send(); 271 }); 272 }, 273 hasBrowsingContext: false, 274 } 275 ]; 276 }