git ssb

0+

farewellutopia-dev / patchboot



Tree: 54f45b9e3a2114fd3c234a8de046349c9a3dab1c

Files: 54f45b9e3a2114fd3c234a8de046349c9a3dab1c / src / components / AppRunner.js

5651 bytesRaw
1/* provides a web-component to execute a PatchBoot app. It is passed the app msg rather than the blob to support
2 * different app formats in future.
3 */
4
5import { default as pull, paraMap, collect } from 'pull-stream'
6import MRPC from 'muxrpc'
7import Util from '../Util.js';
8
9class AppRunner extends HTMLElement {
10 constructor() {
11 super();
12 }
13 connectedCallback() {
14 const runnerArea = this.attachShadow({ mode: 'open' })
15
16 const util = new Util(this.sbot)
17
18 const getClassicAppFrameContent = () => {
19 const blobId = this.app.link
20 return util.dereferenceUriOrSigil(blobId).then(code => {
21 function utf8_to_b64(str) {
22 return btoa(unescape(encodeURIComponent(str)));
23 }
24 return `
25 <!DOCTYPE html>
26 <html>
27 <head>
28 <title>Patchboot app</title>
29 </head>
30 <body>
31
32 <div id="patchboot-app" style="min-width: min-content;"></div>
33
34 <script type="module">
35 import {default as ssbConnect, pull} from './scuttle-shell-browser-consumer.js'
36 ssbConnect().then(sbot => {
37 window.sbot = sbot
38 window.root = document.getElementById('patchboot-app')
39 window.pull = pull
40 window.root.innerHTML = ''
41 const script = document.createElement('script')
42 script.defer = true
43 script.src = 'data:text/javascript;base64,${utf8_to_b64(code)}'
44 document.head.append(script)
45 },
46 error => {
47 console.log('An error occured', error)
48 })
49
50 </script>
51
52 </body>
53 </html>
54 `
55 })
56
57 }
58
59 const addBaseUrl = (htmlString) => htmlString.replace('<head>',`<head><base href="${this.app.link}">`)
60
61 const getWebappContent = () => {
62 const link = this.app.link
63 // because of same originy policy we cab't just use original link
64 return util.dereferenceUriOrSigil(link).then(content => {
65 content = (link.startsWith('&') || link.startsWith('ssb')) ? content : addBaseUrl(content)
66 return content
67 })
68 }
69
70 const getAppFrameContent = () => {
71 if (this.app.type === 'patchboot-app') {
72 return getClassicAppFrameContent()
73 } else if (this.app.type === 'patchboot-webapp') {
74 return getWebappContent()
75 } else {
76 throw new Error('unsupported: ' + this.app.type)
77 }
78 }
79
80 const createIFrame = () => {
81 const iFrame = document.createElement('iframe')
82 runnerArea.appendChild(iFrame) // has to appended before contentWindow is accessed
83 iFrame.style = "width: 100%; height: 100%; border: none;"
84 return getAppFrameContent().then(iFrameContent => {
85 iFrame.contentWindow.document.open()
86 iFrame.contentWindow.document.write(iFrameContent)
87 iFrame.contentWindow.document.close()
88 return iFrame
89 })
90 }
91
92 createIFrame().then(iFrame => {
93 console.log(iFrame)
94
95 this.dispatchEvent(new Event('loaded'))
96
97 let messageDataCallback = null
98 let messageDataBuffer = []
99
100 const fromPage = function read(abort, cb) {
101 if (messageDataBuffer.length > 0) {
102 const data = messageDataBuffer[0]
103 messageDataBuffer = messageDataBuffer.splice(1)
104 cb(null, data)
105 } else {
106 messageDataCallback = cb
107 }
108
109 }
110
111 function ping() {
112 iFrame.contentWindow.postMessage({
113 direction: "from-content-script",
114 action: 'ping'
115 }, '*');
116 }
117
118 iFrame.contentWindow.addEventListener("message", (event) => {
119 if (event.data && event.data.direction === "from-page-script") {
120 if (event.data.action === "ping") {
121 ping()
122 } else {
123 //new Uint8Array(event.data.message) is not accepted by muxrpc
124 const asBuffer = Buffer.from(event.data.message)
125 if (messageDataCallback) {
126 const _messageDataCallback = messageDataCallback
127 messageDataCallback = null
128 _messageDataCallback(null, asBuffer)
129 } else {
130 console.log('buffering....')
131 messageDataBuffer.push(asBuffer)
132 }
133 }
134 }
135 })
136 const toPage = function (source) {
137 source(null, function more(end, data) {
138 iFrame.contentWindow.postMessage({
139 direction: "from-content-script",
140 message: data
141 }, '*');
142 source(null, more)
143 })
144 }
145 iFrame.contentWindow.addEventListener('load', () => this.dispatchEvent(new CustomEvent('ready')))
146 /*function logger(text) {
147 return pull.map((v) => {
148 console.log(text,v)
149 console.log(new TextDecoder("utf-8").decode(v))
150 return v
151 })
152 }*/
153 this.sbot.manifest().then(manifest => {
154 //console.log('manifest', JSON.stringify(manifest))
155 const asyncManifest = asyncifyManifest(manifest)
156 const server = MRPC(null, asyncManifest)(this.sbot)
157 const serverStream = server.createStream(() => { console.log('closed') })
158 pull(fromPage, serverStream, toPage)
159 })
160 })
161
162 }
163}
164
165function asyncifyManifest(manifest) {
166 if (typeof manifest !== 'object') return manifest
167 let asyncified = {}
168 for (let k in manifest) {
169 var value = manifest[k]
170 // Rewrite re-exported sync methods as async,
171 if (value === 'sync') {
172 value = 'async'
173 }
174 asyncified[k] = value
175 }
176 return asyncified
177}
178
179customElements.define("app-runner", AppRunner)
180

Built with git-ssb-web