systemjs/lib/scriptLoader.js
Matthew Justin Bauer 18e6945646 Enable setting properties on script element.
The script loader will use load metadata to set script properties. This allows what is in the script tag to be set in meta.

There are two primary use cases: CSP nonce and subresource integrity.

Users of SystemJS wanting to take advantage of CSP can set a "nonce" value in their CSP config. Setting the meta attribute "nonce" for plugins and formats that cannot use CSP allows safely bypassing CSP. Note that this will only work in places where "document" is available. See the test-csp-inline.html file for a full example.

In addition, subresource checking mentions in #639 is also implemented. In the same way setting "integrity" in the meta should make this possible. See test-csp.html for a full example.

Post Rebase Notes:
* Corrected indention.
* Fix load.metadata issue.
* Fixed test-csp-inline.html stuff.
* White-list correct properties.
* Reword error messages.
2015-08-15 16:03:17 -05:00

108 lines
2.9 KiB
JavaScript

/*
* Script tag fetch
*
* When load.metadata.scriptLoad is true, we load via script tag injection.
*/
(function() {
if (typeof document != 'undefined')
var head = document.getElementsByTagName('head')[0];
// call this functione everytime a wrapper executes
var curSystem;
// System clobbering protection for Traceur
SystemJSLoader.prototype.onScriptLoad = function() {
__global.System = curSystem;
};
function webWorkerImport(loader, load) {
return new Promise(function(resolve, reject) {
if (load.metadata.integrity)
reject(new Error('Subresource integrity checking is not supported in web workers.'));
try {
importScripts(load.address);
}
catch(e) {
reject(e);
}
loader.onScriptLoad(load);
// if nothing registered, then something went wrong
if (!load.metadata.registered)
reject(load.address + ' did not call System.register or AMD define');
resolve('');
});
}
// override fetch to use script injection
hook('fetch', function(fetch) {
return function(load) {
var loader = this;
if (!load.metadata.scriptLoad || (!isBrowser && !isWorker))
return fetch.call(this, load);
if (isWorker)
return webWorkerImport(loader, load);
return new Promise(function(resolve, reject) {
var s = document.createElement('script');
s.async = true;
function complete(evt) {
if (s.readyState && s.readyState != 'loaded' && s.readyState != 'complete')
return;
cleanup();
// this runs synchronously after execution
// we now need to tell the wrapper handlers that
// this load record has just executed
loader.onScriptLoad(load);
// if nothing registered, then something went wrong
if (!load.metadata.registered)
reject(load.address + ' did not call System.register or AMD define');
resolve('');
}
function error(evt) {
cleanup();
reject(new Error('Unable to load script ' + load.address));
}
if (s.attachEvent) {
s.attachEvent('onreadystatechange', complete);
}
else {
s.addEventListener('load', complete, false);
s.addEventListener('error', error, false);
}
curSystem = __global.System;
__global.System = loader;
s.src = load.address;
if (load.metadata.integrity)
s.integrity = load.metadata.integrity;
if (load.metadata.nonce)
s.nonce = load.metadata.nonce;
head.appendChild(s);
function cleanup() {
if (s.detachEvent)
s.detachEvent('onreadystatechange', complete);
else {
s.removeEventListener('load', complete, false);
s.removeEventListener('error', error, false);
}
head.removeChild(s);
}
});
};
});
})();