Skip to content

Instantly share code, notes, and snippets.

@justinfagnani
Last active November 10, 2017 19:26
Show Gist options
  • Save justinfagnani/fea819776d77eee5a108a3bcf873e7f4 to your computer and use it in GitHub Desktop.
Save justinfagnani/fea819776d77eee5a108a3bcf873e7f4 to your computer and use it in GitHub Desktop.
Untrusted 3rd Party Web Components
<html>
<body>
<!-- Define the tag name to use for an isolated/untrusted component -->
<define
name="facespace-share"
isolated
constructor="ShareButton"
src="facespace.com/modules/share-button.js">
</define>
<style>
/* This styles a part in the isolated component */
facespace-share::part(site-logo) {
background: url(application.com/logo.png);
}
</style>
<article id="1234">
<!-- This is a proxy to a ShareButton instance in an isolated realm -->
<facespace-share url="application.com/1234"></facespace-share>
</article>
<article id="5678">
<facespace-share url="application.com/5678"></facespace-share>
</article>
</body>
</html>
/**
* This module is untrusted by the application, vended by FaceSpace.
*
* HTMLElement in this realm acts as a Proxy to the instance in the application
* so that getAttribute(), dispatchEvent() work across realms.
*/
export class ShareButton extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
}
async onClick() {
// This reads from the proxy instance in the untrusting document
const url = this.getAttribute('url');
const postUrl = await this.share(url);
// Fires an event on the untrusting document's proxy instance
// CrossRealmEvent is a CustomEvent subclass that's allowed to be
// refired into another origin. It's like postMessage() to a
// specific element instance.
// `detail` is cloned with structure clone
this.dispatchEvent(new CrossRealmEvent('facespace-shared', {
detail: {
postUrl: postUrl;
},
composed: true,
targetOrigin: '*',
}));
}
render() {
return html`
<button on-click=${e => this.onClick()}>
<!-- this part is exposed for styling to the including document -->
<div class="site-logo" part="site-logo"></div>
Share
</button>
`;
}
}
// The name registered here doesn't affect the including document, but it's
// neccessary because in this realm there will instances of ShareButton
// and they need tag names
customElements.define('facespace-share-button', ShareButton);
<!--
You could also define an element decalratively in an HTML module
-->
<define name="my-element">
<template shadowmode="open">
<button>
<!-- this part is exposed for styling to the including document -->
<div class="site-logo" part="site-logo"></div>
Share
</button>
</template>
</define>
<!--
^ this requires a <define> + <template> to make an includable chunk
of HTML, but we could make short-hands just using <template>
-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment