Skip to content
This repository was archived by the owner on Oct 26, 2021. It is now read-only.

Commit 49a7109

Browse files
committed
Merge remote-tracking branch 'origin/master' into visitedImports-typo
2 parents cc3748a + 398f521 commit 49a7109

File tree

6 files changed

+194
-14
lines changed

6 files changed

+194
-14
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@webcomponents/custom-elements",
3-
"version": "1.0.0",
3+
"version": "1.0.2",
44
"description": "HTML Custom Elements Polyfill",
55
"main": "custom-elements.min.js",
66
"directories": {

src/CustomElementInternals.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,15 @@ export default class CustomElementInternals {
167167
* Reactions in the popped stack are invoked.)
168168
*
169169
* @param {!Node} root
170-
* @param {!Set<Node>=} visitedImports
170+
* @param {{
171+
* visitedImports: (!Set<!Node>|undefined),
172+
* upgrade: (!function(!Element)|undefined),
173+
* }=} options
171174
*/
172-
patchAndUpgradeTree(root, visitedImports = new Set()) {
175+
patchAndUpgradeTree(root, options = {}) {
176+
const visitedImports = options.visitedImports || new Set();
177+
const upgrade = options.upgrade || (element => this.upgradeElement(element));
178+
173179
const elements = [];
174180

175181
const gatherElements = element => {
@@ -205,7 +211,7 @@ export default class CustomElementInternals {
205211
const clonedVisitedImports = new Set(visitedImports);
206212
clonedVisitedImports.delete(importNode);
207213

208-
this.patchAndUpgradeTree(importNode, clonedVisitedImports);
214+
this.patchAndUpgradeTree(importNode, {visitedImports: clonedVisitedImports, upgrade});
209215
});
210216
}
211217
} else {
@@ -224,7 +230,7 @@ export default class CustomElementInternals {
224230
}
225231

226232
for (let i = 0; i < elements.length; i++) {
227-
this.upgradeElement(elements[i]);
233+
upgrade(elements[i]);
228234
}
229235
}
230236

src/CustomElementRegistry.js

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ export default class CustomElementRegistry {
4545

4646
/**
4747
* @private
48-
* @type {!Array<string>}
48+
* @type {!Array<!CustomElementDefinition>}
4949
*/
50-
this._unflushedLocalNames = [];
50+
this._pendingDefinitions = [];
5151

5252
/**
5353
* @private
@@ -121,8 +121,7 @@ export default class CustomElementRegistry {
121121
};
122122

123123
this._internals.setDefinition(localName, definition);
124-
125-
this._unflushedLocalNames.push(localName);
124+
this._pendingDefinitions.push(definition);
126125

127126
// If we've already called the flush callback and it hasn't called back yet,
128127
// don't call it again.
@@ -137,12 +136,64 @@ export default class CustomElementRegistry {
137136
// happen if a flush callback keeps the function it is given and calls it
138137
// multiple times.
139138
if (this._flushPending === false) return;
140-
141139
this._flushPending = false;
142-
this._internals.patchAndUpgradeTree(document);
143140

144-
while (this._unflushedLocalNames.length > 0) {
145-
const localName = this._unflushedLocalNames.shift();
141+
const pendingDefinitions = this._pendingDefinitions;
142+
143+
/**
144+
* Unupgraded elements with definitions that were defined *before* the last
145+
* flush, in document order.
146+
* @type {!Array<!Element>}
147+
*/
148+
const elementsWithStableDefinitions = [];
149+
150+
/**
151+
* A map from `localName`s of definitions that were defined *after* the last
152+
* flush to unupgraded elements matching that definition, in document order.
153+
* @type {!Map<string, !Array<!Element>>}
154+
*/
155+
const elementsWithPendingDefinitions = new Map();
156+
for (let i = 0; i < pendingDefinitions.length; i++) {
157+
elementsWithPendingDefinitions.set(pendingDefinitions[i].localName, []);
158+
}
159+
160+
this._internals.patchAndUpgradeTree(document, {
161+
upgrade: element => {
162+
// Ignore the element if it has already upgraded or failed to upgrade.
163+
if (element.__CE_state !== undefined) return;
164+
165+
const localName = element.localName;
166+
167+
// If there is an applicable pending definition for the element, add the
168+
// element to the list of elements to be upgraded with that definition.
169+
const pendingElements = elementsWithPendingDefinitions.get(localName);
170+
if (pendingElements) {
171+
pendingElements.push(element);
172+
// If there is *any other* applicable definition for the element, add it
173+
// to the list of elements with stable definitions that need to be upgraded.
174+
} else if (this._internals.localNameToDefinition(localName)) {
175+
elementsWithStableDefinitions.push(element);
176+
}
177+
},
178+
});
179+
180+
// Upgrade elements with 'stable' definitions first.
181+
for (let i = 0; i < elementsWithStableDefinitions.length; i++) {
182+
this._internals.upgradeElement(elementsWithStableDefinitions[i]);
183+
}
184+
185+
// Upgrade elements with 'pending' definitions in the order they were defined.
186+
while (pendingDefinitions.length > 0) {
187+
const definition = pendingDefinitions.shift();
188+
const localName = definition.localName;
189+
190+
// Attempt to upgrade all applicable elements.
191+
const pendingUpgradableElements = elementsWithPendingDefinitions.get(definition.localName);
192+
for (let i = 0; i < pendingUpgradableElements.length; i++) {
193+
this._internals.upgradeElement(pendingUpgradableElements[i]);
194+
}
195+
196+
// Resolve any promises created by `whenDefined` for the definition.
146197
const deferred = this._whenDefinedDeferred.get(localName);
147198
if (deferred) {
148199
deferred.resolve(undefined);
@@ -184,7 +235,7 @@ export default class CustomElementRegistry {
184235
// Resolve immediately only if the given local name has a definition *and*
185236
// the full document walk to upgrade elements with that local name has
186237
// already happened.
187-
if (definition && this._unflushedLocalNames.indexOf(localName) === -1) {
238+
if (definition && !this._pendingDefinitions.some(d => d.localName === localName)) {
188239
deferred.resolve(undefined);
189240
}
190241

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<title>customElements#polyfillWrapFlushCallback</title>
5+
<script>
6+
(window.customElements = window.customElements || {}).forcePolyfill = true;
7+
</script>
8+
<script src="../../../../es6-promise/dist/es6-promise.auto.min.js"></script>
9+
<script src="../../../../web-component-tester/browser.js"></script>
10+
<script src="../../../custom-elements.min.js"></script>
11+
</head>
12+
<body>
13+
<script>
14+
/**
15+
* @license
16+
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
17+
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
18+
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
19+
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
20+
* Code distributed by Google as part of the polymer project is also
21+
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
22+
*/
23+
24+
test('Elements are upgraded when created imperatively, even if the flush ' +
25+
'callback has not been called.', function() {
26+
let flush = undefined;
27+
customElements.polyfillWrapFlushCallback(fn => {
28+
flush = fn;
29+
});
30+
31+
class CustomElement0 extends HTMLElement {}
32+
customElements.define('custom-element-0', CustomElement0);
33+
assert.equal(customElements.get('custom-element-0'), CustomElement0);
34+
35+
const element = document.createElement('custom-element-0');
36+
assert.instanceOf(element, CustomElement0);
37+
38+
flush();
39+
});
40+
</script>
41+
</body>
42+
</html>

tests/html/polyfillWrapFlushCallback/index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,7 @@
1010
'./defaultSyncFlush.html',
1111
'./whenDefined_before.html',
1212
'./whenDefined_after.html',
13+
'./upgradeInDefineCallOrder.html',
14+
'./imperativelyCreatedBeforeFlush.html',
1315
]);
1416
</script>
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<title>customElements#polyfillWrapFlushCallback</title>
5+
<script>
6+
(window.customElements = window.customElements || {}).forcePolyfill = true;
7+
</script>
8+
<script src="../../../../es6-promise/dist/es6-promise.auto.min.js"></script>
9+
<script src="../../../../web-component-tester/browser.js"></script>
10+
<script src="../../../custom-elements.min.js"></script>
11+
</head>
12+
<body>
13+
<custom-element-0 id="elt_0_0">
14+
<custom-element-1 id="elt_1_0">
15+
<custom-element-2 id="elt_2_0"></custom-element-2>
16+
<custom-element-0 id="elt_0_1">
17+
</custom-element-0>
18+
</custom-element-1>
19+
<custom-element-2 id="elt_2_1">
20+
<custom-element-2 id="elt_2_2"></custom-element-2>
21+
</custom-element-2>
22+
<custom-element-0 id="elt_0_2">
23+
<custom-element-1 id="elt_1_1">
24+
</custom-element-1>
25+
<custom-element-2 id="elt_2_3"></custom-element-2>
26+
</custom-element-0>
27+
<custom-element-1 id="elt_1_2"></custom-element-1>
28+
</custom-element-0>
29+
<script>
30+
/**
31+
* @license
32+
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
33+
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
34+
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
35+
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
36+
* Code distributed by Google as part of the polymer project is also
37+
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
38+
*/
39+
40+
test('When a flush callback is installed and multiple calls to define are ' +
41+
'made calling the flush callback causes elements to upgrade in define-call ' +
42+
'order and then document order.', function() {
43+
let flush = undefined;
44+
customElements.polyfillWrapFlushCallback(fn => {
45+
flush = fn;
46+
});
47+
48+
const upgradeLog = [];
49+
class LogIDOnConstruct extends HTMLElement {
50+
constructor() {
51+
super();
52+
upgradeLog.push(this.id);
53+
}
54+
}
55+
56+
customElements.define('custom-element-0', class extends LogIDOnConstruct {});
57+
customElements.define('custom-element-1', class extends LogIDOnConstruct {});
58+
customElements.define('custom-element-2', class extends LogIDOnConstruct {});
59+
60+
assert.deepEqual(upgradeLog, []);
61+
62+
flush();
63+
64+
assert.deepEqual(upgradeLog, [
65+
"elt_0_0",
66+
"elt_0_1",
67+
"elt_0_2",
68+
"elt_1_0",
69+
"elt_1_1",
70+
"elt_1_2",
71+
"elt_2_0",
72+
"elt_2_1",
73+
"elt_2_2",
74+
"elt_2_3",
75+
]);
76+
});
77+
</script>
78+
</body>
79+
</html>

0 commit comments

Comments
 (0)