@dasl/tile-host #
A hosted page that runs one DASL tile, for embedders that
cannot run @dasl/tile-loader themselves: a native app's
WebView, or a site that would rather drop a tile into an iframe than ship the
loader. The package is the page (public/), the Express router that serves it,
and the message schema the page and its embedder speak.
https://webtil.es/host/?tile=at://did:plc:…/ing.dasl.masl/3mcjwwoqjqs2v
The tile runs exactly as it would in a browser page: the mothership in the host
page frames the shuttle from the loading server on a fresh random origin, the
worker there answers the tile's requests from its own resources, and every
response carries the policy from @dasl/tile-lexicon. The
host page adds nothing to what the tile can reach; it only adds a way to be
told what the tile is doing.
The page #
GET /host/?tile=<url>[&load=<loading server>]
tile: the tile to run. Anat://…/ing.dasl.masl/…record, or thehttps://URL of a.tileCAR file (served withAccess-Control-Allow-Origin, as a PDS serves its blobs: the page fetches it from its own origin).load: the loading server (load.webtil.es, or a full origin such ashttp://load.localhost:4321off https). Defaults toload.<host of the page>, which is right when the page is mounted on the site that runs the loading server.
The tile fills the viewport: the embedder owns the size. The manifest's
sizing is reported (below) so the embedder can pick a sensible one.
Messages #
Every message is the JSON of an object with tilesHost: 1, so an embedder can
tell them from anything else on the channel. Types and parsers are in
@dasl/tile-host/messages, which has no dependencies and no DOM.
Up, from the page, through window.ReactNativeWebView.postMessage when a
React Native WebView injected it, and through window.parent.postMessage when
the page is framed:
event |
fields | when |
|---|---|---|
manifest |
url, name, description, sizing |
the tile is known; always first |
load |
the tile's runtime is up and the tile document is loading | |
title |
title |
the tile set a title |
error |
message |
the tile could not be loaded (the page shows the message too) |
protocol |
protocol, kind, payload |
a tiles protocol message from the tile |
Down, from the embedder, as a message event on the page (what
webView.postMessage() does in react-native-webview, on window on iOS and
document on Android) or postMessage from the parent window:
action |
fields | what |
|---|---|---|
send-protocol |
protocol, kind, payload |
a tiles-protocol message down to the tile |
send-data |
payload |
shorthand for tp-data payload |
Commands are accepted from the WebView channel (events with no source) and the parent window only. The shuttle and the tile are windows of the page's own making and cannot speak for the embedder.
In a React Native app #
With react-native-webview:
<WebView
source={{uri: `https://webtil.es/host/?tile=${encodeURIComponent(uri)}`}}
onMessage={e => {
const ev = parseTileHostEvent(e.nativeEvent.data)
if (ev?.event === 'manifest') setHeight(heightFor(ev.sizing))
if (ev?.event === 'load') setLoading(false)
if (ev?.event === 'error') showError(ev.message)
}}
javaScriptEnabled
domStorageEnabled
limitsNavigationsToAppBoundDomains
onShouldStartLoadWithRequest={req => req.isTopFrame ? req.url.startsWith(HOST_ORIGIN) : true}
onOpenWindow={e => Linking.openURL(e.nativeEvent.targetUrl)}
setSupportMultipleWindows
scrollEnabled={false}
/>
What the embedder has to get right:
- iOS: App-Bound Domains. WKWebView only provides service workers to
app-bound domains, and the shuttle needs one. List the host page's domain
(which the loading server's random subdomains fall under,
webtil.esfor the page above) in the app'sWKAppBoundDomainsand setlimitsNavigationsToAppBoundDomainson the WebView. Without it the tile never loads. Up to ten domains fit the list. - Android needs nothing special;
domStorageEnabledis prudent. - Navigation. Only the host page belongs in the top frame. The shuttle and
the tile are subframes; a link a tile opens leaves the sandbox
(
allow-popups-to-escape-sandbox) and should go to the system browser, which is whatonShouldStartLoadWithRequestandonOpenWindowabove arrange. - WebRTC. The loader spends Chromium's peer-connection budget before a tile loads, which covers Android's WebView. WebKit has no such cap, so on iOS, as in Safari, WebRTC egress from a tile stays open. A host that can block it at the engine level should.
- Treat events as data.
name,titleand protocol payloads come from the tile.
In an iframe #
<iframe src="https://webtil.es/host/?tile=at://…" sandbox="allow-scripts allow-same-origin allow-popups allow-forms" title="…"></iframe>
The same events arrive as message events on the parent window, with the
iframe as source; commands go to iframe.contentWindow.postMessage(json, origin).
Hosting #
import express from 'express';
import { createTileLoadingRouter } from '@dasl/tile-server';
import { createTileHostRouter } from '@dasl/tile-host';
const app = express();
app.set('trust proxy', 'loopback');
app.use(createTileLoadingRouter('example.site')); // load.example.site, *.example.site
app.use('/host', createTileHostRouter()); // https://example.site/host/
The router serves public/ with a CSP that confines the page (script-src 'self', connect-src and frame-src open to https: and http: by default, see
TileHostOptions to narrow them) and without frame-ancestors or
X-Frame-Options, since being embedded is the point. Mount it before any
static middleware that sets X-Frame-Options on the site. tileHostDir is the
directory, for serving it another way.
Developing #
npm run build compiles the router and the message module with tsc and bundles
the page script, loader included, into public/host.js (not committed). npm test runs the message parsers in Node and the page end to end in a real
Chromium (Google Chrome, Chromium or Brave in /Applications, or CHROME_PATH;
skipped without one): a loading server on *.localhost, the page on 127.0.0.1,
a CAR tile on a third origin, and a stand-in for the React Native channel.
Part of DASL tiles #
@dasl/tile-loader— load tiles in the browser@dasl/tile-server— the browser tile-loading server@dasl/tile-lexicon— MASL schema, types and the tile policy
License #
Apache-2.0