shadow-function
Version:
ioing lib - shadow Function, worker Function
90 lines (81 loc) • 2.87 kB
text/typescript
class BlackDocument {
public root: Element
public methods!: Object
public sandbox!: HTMLIFrameElement
constructor (template: string, root: Element = document.body, csp: string = `default-src 'self';`, type?: string) {
this.root = root
this.create(template, csp, type)
// @ts-ignore
return this.run.bind(this)
}
hashSource () {
return (new Array(8 + Math.round(Math.random() * 8))).join(',').split(',').map(() => {
return String.fromCharCode(97 + Math.round(Math.random() * 25))
}).join('')
}
injectRequestParentAction (csp: string) {
let hash = this.hashSource()
window.addEventListener('message', (data) => {
let result = data.data || {}
let method = result.method
let params = result.params
let postToSandbox = (data: object) => {
if (this.sandbox.contentWindow) {
this.sandbox.contentWindow.postMessage({
data,
key: method + ':' + JSON.stringify(params)
}, '*')
}
}
if (result.hash === hash) {
let func = this.methods[method]
let promise: object | Promise<object>
if (typeof func === 'function') {
promise = func(params)
} else {
promise = func
}
if (promise instanceof Promise) {
promise.then(postToSandbox)
} else {
postToSandbox(promise)
}
}
}, false)
return `
<meta http-equiv="Content-Security-Policy" content="${csp}">
<script>
window.requestParentAction = function (method, params) {
parent.postMessage({ method: method, params: params, hash: '${hash}' }, '${location.origin}')
return new Promise(function (reslove, reject) {
function callback (data) {
if (data && data.data && data.data.data) {
if (method + ':' + JSON.stringify(params) === data.data.key) {
window.removeEventListener('message', callback)
reslove(data.data.data)
}
} else {
reject()
}
}
window.addEventListener('message', callback)
})
}
</script>
`
}
create (template: string, csp: string, type = 'fullscreen') {
let iframe = document.createElement('iframe')
iframe.csp = csp
iframe.src = URL.createObjectURL(new Blob([`${this.injectRequestParentAction(csp)} ;\n ${template}`], { type: 'text/html' }))
iframe.setAttribute('sandbox', 'allow-scripts')
if (type === 'fullscreen') iframe.setAttribute('style', `position: fixed; top: 0; right: 0; bottom: 0; left: 0; width: 100%; height: 100%`)
this.sandbox = iframe
}
run (methods: object) {
this.methods = methods
this.root.appendChild(this.sandbox)
}
}
export { BlackDocument }