Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(42)

Side by Side Diff: third_party/WebKit/LayoutTests/custom-elements/spec/define-element.html

Issue 2306923002: Prevent recursion in critical part of CustomElementRegistry::define. (Closed)
Patch Set: Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | third_party/WebKit/Source/core/dom/custom/CustomElementRegistry.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 <!DOCTYPE html> 1 <!DOCTYPE html>
2 <title>Custom Elements: defineElement</title> 2 <title>Custom Elements: defineElement</title>
3 <link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#cus tomelementsregistry"> 3 <link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#cus tomelementsregistry">
4 <meta name="author" title="Dominic Cooney" href="mailto:dominicc@chromium.org"> 4 <meta name="author" title="Dominic Cooney" href="mailto:dominicc@chromium.org">
5 <script src="../../resources/testharness.js"></script> 5 <script src="../../resources/testharness.js"></script>
6 <script src="../../resources/testharnessreport.js"></script> 6 <script src="../../resources/testharnessreport.js"></script>
7 <script src="resources/custom-elements-helpers.js"></script> 7 <script src="resources/custom-elements-helpers.js"></script>
8 <body> 8 <body>
9 <script> 9 <script>
10 // TODO(dominicc): Merge these tests with 10 // TODO(dominicc): Merge these tests with
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
81 81
82 test_with_window((w) => { 82 test_with_window((w) => {
83 class X extends w.HTMLElement {} 83 class X extends w.HTMLElement {}
84 w.customElements.define('a-a', X); 84 w.customElements.define('a-a', X);
85 assert_throws_dom_exception(w, 'NotSupportedError', () => { 85 assert_throws_dom_exception(w, 'NotSupportedError', () => {
86 w.customElements.define('a-b', X); 86 w.customElements.define('a-b', X);
87 }, 'defining an element with a constructor that is already in the ' + 87 }, 'defining an element with a constructor that is already in the ' +
88 'registry should throw a NotSupportedError'); 88 'registry should throw a NotSupportedError');
89 }, 'Reused constructor'); 89 }, 'Reused constructor');
90 90
91 promise_test((t) => {
92 return Promise.all([create_window_in_test(t), create_window_in_test(t)])
93 .then(([w1, w2]) => {
94 class X extends w2.HTMLElement { };
95 w1.customElements.define('first-name', X);
96 w2.customElements.define('second-name', X);
domenic 2016/09/03 22:50:35 This test is very interesting. Maybe also add a te
97 assert_equals(
98 new X().localName, 'second-name',
99 'the current global object should determine which definition is ' +
100 'operative; because X extends w2.HTMLElement, w2 is operative');
101 });
102 }, 'Reused constructor in a separate registry');
103
91 test_with_window((w) => { 104 test_with_window((w) => {
92 let X = (function () {}).bind({}); 105 let X = (function () {}).bind({});
93 Object.defineProperty(X, 'prototype', { 106 Object.defineProperty(X, 'prototype', {
94 get() { 107 get() {
95 assert_throws_dom_exception(w, 'NotSupportedError', () => { 108 assert_throws_dom_exception(w, 'NotSupportedError', () => {
96 w.customElements.define('second-name', X); 109 w.customElements.define('second-name', X);
97 }, 'defining an element with a constructor that is being defined ' + 110 }, 'defining an element with a constructor that is being defined ' +
98 'should throw a NotSupportedError'); 111 'should throw a NotSupportedError');
99 return new Object(); 112 return new Object();
100 } 113 }
101 }); 114 });
102 w.customElements.define('first-name', X); 115 w.customElements.define('first-name', X);
103 assert_equals(w.customElements.get('first-name'), X, 'the first definition sho uld have worked'); 116 assert_equals(w.customElements.get('first-name'), X, 'the first definition sho uld have worked');
104 }, 'Reused constructor recursively'); 117 }, 'Reused constructor recursively');
105 118
106 test_with_window((w) => { 119 test_with_window((w) => {
120 let X = (function () {}).bind({});
121 Object.defineProperty(X, 'prototype', {
122 get() {
123 assert_throws_dom_exception(w, 'NotSupportedError', () => {
124 w.customElements.define('second-name', class extends HTMLElement { });
125 }, 'defining an element while element definition is running should ' +
126 'throw a NotSupportedError');
127 return new Object();
domenic 2016/09/03 22:50:35 Nit: `new Object()` is better written as `{}`
128 }
129 });
130 w.customElements.define('first-name', X);
131 assert_equals(w.customElements.get('first-name'), X,
132 'the first definition should have worked');
133 }, 'Define while element definition is running');
134
135 promise_test((t) => {
136 return Promise.all([create_window_in_test(t), create_window_in_test(t)])
137 .then(([w1, w2]) => {
138 let X = (function () {}).bind({});
139 class Y extends w2.HTMLElement { };
140 Object.defineProperty(X, 'prototype', {
141 get() {
142 w2.customElements.define('second-name', Y);
143 return new Object();
144 }
145 });
146 w1.customElements.define('first-name', X);
147 assert_equals(w1.customElements.get('first-name'), X,
148 'the first definition should have worked');
149 assert_equals(w2.customElements.get('second-name'), Y,
150 'the first definition should have worked, too');
domenic 2016/09/03 22:50:35 typo: second
151 });
152 }, 'Define while element definition is running in a separate registry');
153
154 test_with_window((w) => {
155 class Y extends w.HTMLElement { };
156 class X extends w.HTMLElement {
157 constructor() {
158 super();
159 w.customElements.define('second-name', Y);
160 }
161 };
162 // the element definition flag while first-name is processed should
163 // be reset before doing upgrades
164 w.customElements.define('first-name', X);
165 assert_equals(w.customElements.get('second-name'), Y,
166 'the second definition should have worked');
167 }, 'Element definition flag reset',
domenic 2016/09/03 22:50:35 A more descriptive test name would be "Element def
168 '<first-name></first-name>');
169
170 test_with_window((w) => {
107 assert_throws(TypeError.prototype, () => { 171 assert_throws(TypeError.prototype, () => {
108 let not_a_constructor = () => {}; 172 let not_a_constructor = () => {};
109 let invalid_name = 'annotation-xml'; 173 let invalid_name = 'annotation-xml';
110 w.customElements.define(invalid_name, not_a_constructor); 174 w.customElements.define(invalid_name, not_a_constructor);
111 }, 'defining an element with an invalid name and invalid constructor ' + 175 }, 'defining an element with an invalid name and invalid constructor ' +
112 'should throw a TypeError for the constructor and not a SyntaxError'); 176 'should throw a TypeError for the constructor and not a SyntaxError');
113 177
114 class C extends w.HTMLElement {} 178 class C extends w.HTMLElement {}
115 w.customElements.define('a-a', C); 179 w.customElements.define('a-a', C);
116 assert_throws_dom_exception(w, 'SYNTAX_ERR', () => { 180 assert_throws_dom_exception(w, 'SYNTAX_ERR', () => {
(...skipping 11 matching lines...) Expand all
128 <p> 192 <p>
129 <a-a id="b"></a-a> 193 <a-a id="b"></a-a>
130 <a-a id="c"></a-a> 194 <a-a id="c"></a-a>
131 </p> 195 </p>
132 <a-a id="d"></a-a> 196 <a-a id="d"></a-a>
133 </a-a>`; 197 </a-a>`;
134 let invocations = []; 198 let invocations = [];
135 class C extends w.HTMLElement { 199 class C extends w.HTMLElement {
136 constructor() { 200 constructor() {
137 super(); 201 super();
138 console.log(this.getAttribute('id'));
139 invocations.push(this); 202 invocations.push(this);
140 } 203 }
141 } 204 }
142 w.customElements.define('a-a', C); 205 w.customElements.define('a-a', C);
143 assert_array_equals(['a', 'b', 'c', 'd'], invocations.map((e) => e.id), 206 assert_array_equals(['a', 'b', 'c', 'd'], invocations.map((e) => e.id),
144 'four elements should have been upgraded in doc order'); 207 'four elements should have been upgraded in doc order');
145 }, 'Upgrade: existing elements'); 208 }, 'Upgrade: existing elements');
146 209
147 test_with_window((w) => { 210 test_with_window((w) => {
148 let doc = w.document; 211 let doc = w.document;
(...skipping 29 matching lines...) Expand all
178 invocations.push(this); 241 invocations.push(this);
179 } 242 }
180 } 243 }
181 w.customElements.define('a-a', C); 244 w.customElements.define('a-a', C);
182 assert_array_equals([a], invocations, 245 assert_array_equals([a], invocations,
183 'the constructor should have been invoked once for the ' + 246 'the constructor should have been invoked once for the ' +
184 'elements in the shadow tree'); 247 'elements in the shadow tree');
185 }, 'Upgrade: shadow tree'); 248 }, 'Upgrade: shadow tree');
186 249
187 // Final step in Step 14 250 // Final step in Step 14
188 // 14. Finally, if the first set of steps threw an exception, then rethrow that exception, 251 // 14. Finally, if the first set of steps threw an exception, then rethrow that exception,
189 // and terminate this algorithm. 252 // and terminate this algorithm.
190 test_with_window((w) => { 253 test_with_window((w) => {
191 class Y extends w.HTMLElement {} 254 class Y extends w.HTMLElement {}
192 let X = (function () {}).bind({}); 255 let X = (function () {}).bind({});
193 Object.defineProperty(X, 'prototype', { 256 Object.defineProperty(X, 'prototype', {
194 get() { throw { name: 42 }; } 257 get() { throw { name: 42 }; }
195 }); 258 });
196 assert_throws({ name: 42 }, () => { 259 assert_throws({ name: 42 }, () => {
197 w.customElements.define('a-a', X); 260 w.customElements.define('a-a', X);
198 }, 'should rethrow constructor exception'); 261 }, 'should rethrow constructor exception');
199 w.customElements.define('a-a', Y); 262 w.customElements.define('a-a', Y);
(...skipping 28 matching lines...) Expand all
228 // Callbacks are defined in reverse order. 291 // Callbacks are defined in reverse order.
229 let callbacks_in_reverse = ['attributeChangedCallback', 'disconnectedCallback', 'connectedCallback']; 292 let callbacks_in_reverse = ['attributeChangedCallback', 'disconnectedCallback', 'connectedCallback'];
230 function F_for_callbacks_in_reverse() {}; 293 function F_for_callbacks_in_reverse() {};
231 callbacks_in_reverse.forEach((callback) => { 294 callbacks_in_reverse.forEach((callback) => {
232 test_with_window((w) => { 295 test_with_window((w) => {
233 Object.defineProperty(F_for_callbacks_in_reverse.prototype, callback, { 296 Object.defineProperty(F_for_callbacks_in_reverse.prototype, callback, {
234 get() { throw { name: callback }; } 297 get() { throw { name: callback }; }
235 }); 298 });
236 assert_throws({ name: callback }, () => { 299 assert_throws({ name: callback }, () => {
237 w.customElements.define('a-a', F_for_callbacks_in_reverse); 300 w.customElements.define('a-a', F_for_callbacks_in_reverse);
238 }, 'Exception from Get(prototype, callback) should be rethrown'); 301 }, 'Exception from Get(prototype, callback) should be rethrown');
239 }, 'Rethrow any exceptions thrown while retrieving ' + callback); 302 }, 'Rethrow any exceptions thrown while retrieving ' + callback);
240 }); 303 });
241 304
242 // 14.4 If connectedCallback is not undefined, and IsCallable(connectedCallback) is false, 305 // 14.4 If connectedCallback is not undefined, and IsCallable(connectedCallback) is false,
243 // then throw a TypeError exception. 306 // then throw a TypeError exception.
244 // 14.6 If disconnectedCallback is not undefined, and IsCallable(disconnectedCal lback) is false, 307 // 14.6 If disconnectedCallback is not undefined, and IsCallable(disconnectedCal lback) is false,
245 // then throw a TypeError exception. 308 // then throw a TypeError exception.
246 // 14.9. If attributeChangedCallback is not undefined, then 309 // 14.9. If attributeChangedCallback is not undefined, then
247 // 1. If IsCallable(attributeChangedCallback) is false, then throw a TypeE rror exception. 310 // 1. If IsCallable(attributeChangedCallback) is false, then throw a TypeE rror exception.
248 callbacks_in_reverse.forEach((callback) => { 311 callbacks_in_reverse.forEach((callback) => {
249 test_with_window((w) => { 312 test_with_window((w) => {
250 function F() {} 313 function F() {}
251 Object.defineProperty(F.prototype, callback, { 314 Object.defineProperty(F.prototype, callback, {
252 get() { return new Object(); } 315 get() { return new Object(); }
253 }); 316 });
254 assert_throws(TypeError.prototype, () => { 317 assert_throws(TypeError.prototype, () => {
255 w.customElements.define('a-a', F); 318 w.customElements.define('a-a', F);
256 }, 'defining an element with a constructor with a callback that is ' + 319 }, 'defining an element with a constructor with a callback that is ' +
257 'not undefined and not callable should throw a TypeError'); 320 'not undefined and not callable should throw a TypeError');
258 }, 'If retrieved callback '+ callback + ' is not undefined and not callable, t hrow TypeError'); 321 }, 'If retrieved callback '+ callback + ' is not undefined and not callable, t hrow TypeError');
259 }); 322 });
260 323
261 // 14.9.2 Let observedAttributesIterable be Get(constructor, "observedAttributes "). 324 // 14.9.2 Let observedAttributesIterable be Get(constructor, "observedAttributes ").
262 // Rethrow any exceptions. 325 // Rethrow any exceptions.
263 test_with_window((w) => { 326 test_with_window((w) => {
264 class X extends w.HTMLElement{ 327 class X extends w.HTMLElement{
265 constructor() { super(); } 328 constructor() { super(); }
266 attributeChangedCallback() {} 329 attributeChangedCallback() {}
267 static get observedAttributes() { throw { name: 'observedAttributes throws' }; } 330 static get observedAttributes() { throw { name: 'observedAttributes throws' }; }
268 } 331 }
269 assert_throws({ name: 'observedAttributes throws' }, () => { 332 assert_throws({ name: 'observedAttributes throws' }, () => {
270 w.customElements.define('a-a', X); 333 w.customElements.define('a-a', X);
271 }, 'Exception from Get(constructor, observedAttributes) should be rethrown'); 334 }, 'Exception from Get(constructor, observedAttributes) should be rethrown');
272 }, 'Rethrow any exceptions thrown while getting observedAttributes'); 335 }, 'Rethrow any exceptions thrown while getting observedAttributes');
273 336
274 // 14.9.3 If observedAttributesIterable is not undefined, then set observedAttri butes 337 // 14.9.3 If observedAttributesIterable is not undefined, then set observedAttri butes
275 // to the result of converting observedAttributesIterable to a sequence<D OMString>. 338 // to the result of converting observedAttributesIterable to a sequence<D OMString>.
276 // Rethrow any exceptions. 339 // Rethrow any exceptions.
277 test_with_window((w) => { 340 test_with_window((w) => {
278 class X extends w.HTMLElement{ 341 class X extends w.HTMLElement{
279 constructor() { super(); } 342 constructor() { super(); }
280 attributeChangedCallback() {} 343 attributeChangedCallback() {}
281 static get observedAttributes() { return new RegExp(); } 344 static get observedAttributes() { return new RegExp(); }
282 } 345 }
283 assert_throws(TypeError.prototype, () => { 346 assert_throws(TypeError.prototype, () => {
284 w.customElements.define('a-a', X); 347 w.customElements.define('a-a', X);
285 }, 'converting RegExp to sequence<DOMString> should throw TypeError'); 348 }, 'converting RegExp to sequence<DOMString> should throw TypeError');
286 }, 'exception thrown while converting observedAttributes to sequence<DOMString> ' + 349 }, 'exception thrown while converting observedAttributes to ' +
287 'should be rethrown'); 350 'sequence<DOMString> should be rethrown');
288 351
289 // 14.9.2 test Get(constructor, observedAttributes) does not throw if 352 // 14.9.2 test Get(constructor, observedAttributes) does not throw if
290 // attributeChangedCallback is undefined. 353 // attributeChangedCallback is undefined.
291 test_with_window((w) => { 354 test_with_window((w) => {
292 let observedAttributes_invoked = false; 355 let observedAttributes_invoked = false;
293 let X = (function () {}).bind({}); 356 let X = (function () {}).bind({});
294 Object.defineProperty(X, 'observedAttributes', { 357 Object.defineProperty(X, 'observedAttributes', {
295 get() { observedAttributes_invoked = true; } 358 get() { observedAttributes_invoked = true; }
296 }); 359 });
297 assert_false( observedAttributes_invoked, 'Get(constructor, observedAttributes ) should not be invoked'); 360 assert_false( observedAttributes_invoked, 'Get(constructor, observedAttributes ) should not be invoked');
298 }, 'Get(constructor, observedAttributes)' + 361 }, 'Get(constructor, observedAttributes) should not execute if ' +
299 'should not execute if attributeChangedCallback is undefined'); 362 'attributeChangedCallback is undefined');
363
364 // TODO(dominicc): I think the order and timing of checks the tests
365 // below exercise has changed. Update these tests. crbug.com/643052
300 366
301 // step 2 367 // step 2
302 // 2. If constructor is an interface object whose corresponding interface either is 368 // 2. If constructor is an interface object whose corresponding interface either is
303 // HTMLElement or has HTMLElement in its set of inherited interfaces, throw 369 // HTMLElement or has HTMLElement in its set of inherited interfaces, throw
304 // a TypeError and abort these steps. 370 // a TypeError and abort these steps.
305 // 3. If name is not a valid custom element name, then throw a "SyntaxError" DOM Exception 371 // 3. If name is not a valid custom element name, then throw a "SyntaxError" DOM Exception
306 // and abort these steps. 372 // and abort these steps.
307 test_with_window((w) => { 373 test_with_window((w) => {
308 let invalid_name = 'annotation-xml'; 374 let invalid_name = 'annotation-xml';
309 // TODO(davaajav): change this to TypeError, when we add a failure expectation to this file 375 // TODO(davaajav): change this to TypeError, when we add a failure expectation to this file
310 assert_throws_dom_exception(w, 'SYNTAX_ERR', () => { 376 assert_throws_dom_exception(w, 'SYNTAX_ERR', () => {
311 w.customElements.define(invalid_name, HTMLElement); 377 w.customElements.define(invalid_name, HTMLElement);
312 }, 'defining a constructor that is an interface object whose interface is HTML Element' + 378 }, 'defining a constructor that is an interface object whose interface is HTML Element' +
313 'should throw TypeError not SyntaxError'); 379 'should throw TypeError not SyntaxError');
314 }, 'Invalid constructor'); 380 }, 'Invalid constructor');
315 381
316 // step 2 382 // step 2
317 test_with_window((w) => { 383 test_with_window((w) => {
318 let invalid_name = 'annotation-xml'; 384 let invalid_name = 'annotation-xml';
319 assert_throws_dom_exception(w, 'SYNTAX_ERR', () => { 385 assert_throws_dom_exception(w, 'SYNTAX_ERR', () => {
320 w.customElements.define(invalid_name, HTMLButtonElement); 386 w.customElements.define(invalid_name, HTMLButtonElement);
321 }, 'defining a constructor that is an interface object who has HTMLElement' + 387 }, 'defining a constructor that is an interface object who has HTMLElement' +
322 'in its set of inhertied interfaces should throw TypeError not SyntaxErro r'); 388 'in its set of inhertied interfaces should throw TypeError not SyntaxError' );
323 }, 'Invalid constructor'); 389 }, 'Invalid constructor');
324 390
325 // step 2 391 // step 2
326 test_with_window((w) => { 392 test_with_window((w) => {
327 let invalid_name = 'annotation-xml'; 393 let invalid_name = 'annotation-xml';
328 assert_throws_dom_exception(w, 'SYNTAX_ERR', () => { 394 assert_throws_dom_exception(w, 'SYNTAX_ERR', () => {
329 w.customElements.define(invalid_name, class extends HTMLElement {}); 395 w.customElements.define(invalid_name, class extends HTMLElement {});
330 }, 'defining author-defined custom element constructor' + 396 }, 'defining author-defined custom element constructor should pass this ' +
331 'should pass this step without throwing TypeError'); 397 'step without throwing TypeError');
332 }, 'Invalid constructor'); 398 }, 'Invalid constructor');
333 </script> 399 </script>
334 </body> 400 </body>
OLDNEW
« no previous file with comments | « no previous file | third_party/WebKit/Source/core/dom/custom/CustomElementRegistry.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698