-
Notifications
You must be signed in to change notification settings - Fork 38
Description
I'd like to put this idea forward to get some opinions about it. I work in the ad-serving industry, where I manage scripts that deliver ads to pages (as cross-origin iframes) and libraries for creative developers. My involvement in both these areas drives me to try and improve how the 2 things work together.
Being able to hook parts of the loader for one of these iframes would allow me to provide a lot of convenience for creative developers. This is an off-shoot of a similar problem to the one I talk about in whatwg/html#2161.
Probably oversimplified example:
Parent script
const API_BASE = 'http://location.of/widgetapi';
class Widget {
constructor(manifest) {
this.iframe = document.createElement('iframe');
this.iframe.src = manifest.url;
// Hook the iframe loader to provide "built-in" modules to the widget
this.iframe.loader.hook('@@widget/api', {
resolve: () => `${ API_BASE }/${ manifest.apiVersion }/api.js`
});
this.iframe.loader.hook('@@widget/manifest', {
fetch: () => `export default ${ JSON.stringify(manifest) }`
});
this.iframe.loader.hook('@@widget/foo-component', {
resolve: () => `${ API_BASE }/${ manifest.apiVersion }/components/foo.js`
});
}
appendTo(el) {
el.appendChild(this.iframe);
}
}
let widget = new Widget({
name: 'widgetA',
url: 'http://some.other.com/widgets/widgetA.html',
apiVersion: '^1.0.0'
});Iframe snippet
<script type="module">
import api from '@@widget/api';
import Foo from '@@widget/foo-component';
let foo = new Foo();
api.getLoggedInUser()
.then((user) => foo.bar = user);
</script>This provides a nice application environment for the widget developer without them needing to worry about implementation details. The app environment can give arbitrary data—that it, potentially, already has—to the widget without needing to write clunky message send/receive callbacks or force the widget to make a separate HTTP request for the data.
Other, potentially real-world examples:
Providing an API to advertising creative developers (my main use case):
import { expand } from '@@creative-api';
cta.onclick = () => expand().then(showPage2);eBay could provide an API to sellers for their item descriptions (eBay forbid external scripts in descriptions to prevent user tracking/abuse)
import eBay from '@@eBay';
eBay.getMyOtherItems().then(renderCarousel);Hosted apps/games on facebook no longer require special tokens for interacting with the API:
import FB from '@@facebook';
FB.getUser().then(...);Security
Naturally, security is going to be a concern. Being able to hook and map a window's module loader is potentially dangerous. Concerns can, hopefully, be alleviated by the following restrictions:
instantiatecannot be hooked, so objects cannot be passed between origins. This restriction could potentially be lifted for Transferable data (e.g. buffers) to be returned, but throw on other kinds of data.- An unambiguous prefix could precede the module identifier. This makes the intent explicit and precludes any attempt of overriding modules that the child might try to request. In the examples above, I used the (admittedly ugly) prefix
@@.