From d892c9e7348770f29b135e7a113ca662c8a1fa19 Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Sun, 19 Jun 2022 18:01:21 +0000 Subject: [PATCH] Add a helper for app <-> browser communication. git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3908 ed5197a5-7fde-0310-b194-c3ffbd925b24 --- core/client.js | 2 +- core/core.js | 4 ++- core/tfrpc.js | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 core/tfrpc.js diff --git a/core/client.js b/core/client.js index 0e48996b..b911aa0d 100644 --- a/core/client.js +++ b/core/client.js @@ -410,7 +410,7 @@ function api_error(error) { if (typeof(error) == 'string') { setStatusMessage('⚠️ ' + error, '#f00'); } else { - setStatusMessage('⚠️ ' + merror.message + '\n' + error.stack, '#f00'); + setStatusMessage('⚠️ ' + error.message + '\n' + error.stack, '#f00'); } } console.log('error', error); diff --git a/core/core.js b/core/core.js index 73cb3d77..f04d3c0c 100644 --- a/core/core.js +++ b/core/core.js @@ -225,6 +225,7 @@ async function getProcessBlob(blobId, key, options) { var id = appObject.files[appSourceName]; var blob = await getBlobOrContent(id); appSource = utf8Decode(blob); + await process.task.loadFile(['/tfrpc.js', await File.readFile('core/tfrpc.js')]); await Promise.all(Object.keys(appObject.files).map(async function(f) { await process.task.loadFile([f, await getBlobOrContent(appObject.files[f])]); })); @@ -264,6 +265,7 @@ var kStaticFiles = [ {uri: '/style.css', type: 'text/css; charset=UTF-8'}, {uri: '/favicon.png', type: 'image/png'}, {uri: '/client.js', type: 'text/javascript; charset=UTF-8'}, + {uri: '/tfrpc.js', type: 'text/javascript; charset=UTF-8', headers: {'Access-Control-Allow-Origin': 'null'}}, {uri: '/robots.txt', type: 'text/plain; charset=UTF-8'}, ]; @@ -285,7 +287,7 @@ async function staticFileHandler(request, response, blobId, uri) { var path = kStaticFiles[i].path || uri.substring(1); var type = kStaticFiles[i].type || guessType(path); var data = await File.readFile("core/" + path); - response.writeHead(200, {"Content-Type": type, "Content-Length": data.byteLength}); + response.writeHead(200, Object.assign({"Content-Type": type, "Content-Length": data.byteLength}, kStaticFiles[i].headers || {})); response.end(data); return; } diff --git a/core/tfrpc.js b/core/tfrpc.js new file mode 100644 index 00000000..79dfc2cc --- /dev/null +++ b/core/tfrpc.js @@ -0,0 +1,78 @@ +const k_is_browser = get_is_browser(); +let g_api = {}; +let g_next_id = 1; +let g_calls = {}; + +function get_is_browser() { + try { return window !== undefined && console !== undefined; } catch { return false; } +} + +if (k_is_browser) { + print = console.log; +} + +function make_rpc(target, prop, receiver) { + return function() { + let id = g_next_id++; + let promise = new Promise(function(resolve, reject) { + g_calls[id] = {resolve: resolve, reject: reject}; + }); + if (k_is_browser) { + window.parent.postMessage({message: 'tfrpc', method: prop, params: [...arguments], id: id}, '*'); + return promise; + } else { + return app.postMessage({message: 'tfrpc', method: prop, params: [...arguments], id: id}).then(x => promise); + } + } +} + +function call_rpc(message) { + if (message && message.message === 'tfrpc') { + if (message.method) { + let method = g_api[message.method]; + if (method) { + let response = {message: 'tfrpc', id: message.id}; + try { + response.result = method(...message.params); + } catch (error) { + response.error = error; + } + if (k_is_browser) { + window.parent.postMessage(response, '*'); + } else { + app.postMessage(response); + } + } else { + throw new Error(message.method + ' not found.'); + } + } else if (message.result !== undefined) { + if (g_calls[message.id]) { + g_calls[message.id].resolve(message.result); + } else { + throw new Error(message.id + ' not found to reply.'); + } + } else if (message.error !== undefined) { + if (g_calls[message.id]) { + g_calls[message.id].reject(message.error); + } else { + throw new Error(message.id + ' not found to reply.'); + } + } + } +} + +if (k_is_browser) { + window.addEventListener('message', function(event) { + call_rpc(event.data); + }); +} else { + core.register('message', function(message) { + call_rpc(message?.message); + }); +} + +export let rpc = new Proxy({}, {get: make_rpc}); + +export function register(method) { + g_api[method.name] = method; +}