Skip to content
This repository was archived by the owner on Sep 20, 2019. It is now read-only.

Commit 2be70d3

Browse files
zerodevxTimvdLippe
authored andcommittedJan 25, 2019
Fix loader process flow for no-polyfills cases (#1063)
* Add loader test for async/no-polyfills cases * Run async-loader-no-polyfills test on fully-speced browsers * Fix race condition in runWhenLoadedFns() * Fix process flow for no-polyfills sync/async cases * Add WCT Safari 10 workaround
1 parent 087ee41 commit 2be70d3

File tree

3 files changed

+137
-12
lines changed

3 files changed

+137
-12
lines changed
 

‎tests/async-loader-no-polyfills.html

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<!doctype html>
2+
<!--
3+
@license
4+
Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
5+
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
6+
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
7+
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
8+
Code distributed by Google as part of the polymer project is also
9+
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
10+
-->
11+
<html>
12+
13+
<head>
14+
<!--
15+
This test should be run only for spec-compliant browsers that require no polyfills.
16+
When Loader is used async, we expect `WebComponentsReady` event to fire after all
17+
waitFor() callbacks are resolved.
18+
-->
19+
<title>Test loader async/no-polyfills cases</title>
20+
<meta charset="UTF-8">
21+
<script src="../webcomponents-loader.js" defer></script>
22+
<script src="./wct-config.js"></script>
23+
<script>
24+
WCT.waitForFrameworks = false;
25+
window._wctCallback = function() {};
26+
</script>
27+
<script src="../node_modules/wct-browser-legacy/browser.js"></script>
28+
<script>
29+
// Define mock load function that takes `duration` to resolve.
30+
window.mockLoadCount = 0;
31+
window.mockLoad = function(duration) {
32+
return new Promise(function(resolve) {
33+
window.mockLoadCount++;
34+
setTimeout(resolve, duration);
35+
});
36+
};
37+
</script>
38+
<script type="module">
39+
WebComponents.waitFor(() => {
40+
return mockLoad(1000).then(() => {
41+
window.mockModule1Loaded = true;
42+
});
43+
});
44+
</script>
45+
<script type="module">
46+
WebComponents.waitFor(() => {
47+
return mockLoad(500).then(() => {
48+
window.mockModule2Loaded = true;
49+
})
50+
});
51+
</script>
52+
</head>
53+
54+
<body>
55+
<script>
56+
suite('Loader async/no-polyfills tests', function() {
57+
test('Modules are loaded after WCR fires', function(done) {
58+
var tested = false;
59+
var assertModulesLoaded = function() {
60+
tested = true;
61+
assert.isOk(window.mockModule1Loaded);
62+
assert.isOk(window.mockModule2Loaded);
63+
done();
64+
};
65+
window.addEventListener('WebComponentsReady', assertModulesLoaded);
66+
// An issue with WCT on Safari 10 causes `document.readyState` to be wrongly resolved as `complete`.
67+
// This causes WCR to fire immediately before the event listener is defined, causing a timeout.
68+
// As a workaround, we run a timer to check if WCR has been missed, then run the assertions.
69+
setTimeout(function() {
70+
if (!tested && WebComponents.ready) {
71+
assertModulesLoaded();
72+
}
73+
}, 4000);
74+
});
75+
test('waitFor() callbacks are run exactly once', function() {
76+
// We expect mockLoadCount to be 2, since we defined two `waitFor` cbs.
77+
assert.equal(window.mockLoadCount, 2);
78+
});
79+
});
80+
</script>
81+
</body>
82+
83+
</html>

‎tests/runner.html

+42
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,48 @@
4242
window.suites.push('async-loader-with-modules.html');
4343
} catch (e) {} // eslint-disable-line no-empty
4444
</script>
45+
<script>
46+
// Include `async-loader-no-polyfills` test only for browsers that require no polyfills.
47+
// Feature-detection tests are from `webcomponents-loader.js`.
48+
49+
var polyfills = [];
50+
if (!('attachShadow' in Element.prototype && 'getRootNode' in Element.prototype) ||
51+
(window.ShadyDOM && window.ShadyDOM.force)) {
52+
polyfills.push('sd');
53+
}
54+
if (!window.customElements || window.customElements.forcePolyfill) {
55+
polyfills.push('ce');
56+
}
57+
58+
var needsTemplate = (function() {
59+
// no real <template> because no `content` property (IE and older browsers)
60+
var t = document.createElement('template');
61+
if (!('content' in t)) {
62+
return true;
63+
}
64+
// broken doc fragment (older Edge)
65+
if (!(t.content.cloneNode() instanceof DocumentFragment)) {
66+
return true;
67+
}
68+
// broken <template> cloning (Edge up to at least version 17)
69+
var t2 = document.createElement('template');
70+
t2.content.appendChild(document.createElement('div'));
71+
t.content.appendChild(t2);
72+
var clone = t.cloneNode(true);
73+
return (clone.content.childNodes.length === 0 ||
74+
clone.content.firstChild.content.childNodes.length === 0);
75+
})();
76+
77+
// NOTE: any browser that does not have template or ES6 features
78+
// must load the full suite of polyfills.
79+
if (!window.Promise || !Array.from || !window.URL || !window.Symbol || needsTemplate) {
80+
polyfills = ['sd-ce-pf'];
81+
}
82+
83+
if (!polyfills.length) {
84+
window.suites.push('async-loader-no-polyfills.html');
85+
}
86+
</script>
4587
<script>
4688
WCT.loadSuites(window.suites);
4789
</script>

‎webcomponents-loader.js

+12-12
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
*
1919
* - Synchronous script, no polyfills needed
2020
* - wait for `DOMContentLoaded`
21-
* - run callbacks passed to `waitFor`
22-
* - fire WCR event
21+
* - fire WCR event, as there could not be any callbacks passed to `waitFor`
2322
*
2423
* - Synchronous script, polyfills needed
2524
* - document.write the polyfill bundle
@@ -29,7 +28,9 @@
2928
* - fire WCR event
3029
*
3130
* - Asynchronous script, no polyfills needed
32-
* - fire WCR event, as there could not be any callbacks passed to `waitFor`
31+
* - wait for `DOMContentLoaded`
32+
* - run callbacks passed to `waitFor`
33+
* - fire WCR event
3334
*
3435
* - Asynchronous script, polyfills needed
3536
* - Append the polyfill bundle script
@@ -76,15 +77,13 @@
7677

7778
function runWhenLoadedFns() {
7879
allowUpgrades = false;
79-
var done = function() {
80+
var fnsMap = whenLoadedFns.map(function(fn) {
81+
return fn instanceof Function ? fn() : fn;
82+
});
83+
whenLoadedFns = [];
84+
return Promise.all(fnsMap).then(function() {
8085
allowUpgrades = true;
81-
whenLoadedFns.length = 0;
8286
flushFn && flushFn();
83-
};
84-
return Promise.all(whenLoadedFns.map(function(fn) {
85-
return fn instanceof Function ? fn() : fn;
86-
})).then(function() {
87-
done();
8887
}).catch(function(err) {
8988
console.error(err);
9089
});
@@ -170,9 +169,10 @@
170169
document.head.appendChild(newScript);
171170
}
172171
} else {
173-
polyfillsLoaded = true;
172+
// if readyState is 'complete', script is loaded imperatively on a spec-compliant browser, so just fire WCR
174173
if (document.readyState === 'complete') {
175-
fireEvent()
174+
polyfillsLoaded = true;
175+
fireEvent();
176176
} else {
177177
// this script may come between DCL and load, so listen for both, and cancel load listener if DCL fires
178178
window.addEventListener('load', ready);

0 commit comments

Comments
 (0)
This repository has been archived.