Created
December 22, 2015 17:06
-
-
Save anaisbetts/066118920dd99a197a44 to your computer and use it in GitHub Desktop.
ExecuteJavaScript returning a result
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Electron boilerplate</title> | |
<link rel="stylesheet" href="index.css"> | |
<script> | |
window.foo = {}; | |
window.foo.bar = { | |
baz: function(a,b) { | |
return a + b + 5; | |
}, | |
bazAsync: function(a,b) { | |
return new Promise((resolve) => { | |
setTimeout(() => { | |
resolve(a + b + 5); | |
}, 1000); | |
}) | |
} | |
}; | |
require('./execute-js-func').init(); | |
</script> | |
</head> | |
<body> | |
<div class="container"> | |
<header> | |
<h1>Electron boilerplate</h1> | |
</header> | |
<section class="main"></section> | |
<footer></footer> | |
</div> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const electron = require('electron'); | |
const app = electron.app; | |
const d = require('debug')('index'); | |
import {getRemoteMethod} from './execute-js-func'; | |
// report crashes to the Electron project | |
require('crash-reporter').start(); | |
// adds debug features like hotkeys for triggering dev tools and reload | |
require('electron-debug')(); | |
// prevent window being garbage collected | |
let mainWindow; | |
function onClosed() { | |
// dereference the window | |
// for multiple windows store them in an array | |
mainWindow = null; | |
} | |
function createMainWindow() { | |
const win = new electron.BrowserWindow({ | |
width: 600, | |
height: 400 | |
}); | |
win.loadURL(`file://${__dirname}/example.html`); | |
win.on('closed', onClosed); | |
d("Create!"); | |
win.webContents.on('did-finish-load', async function() { | |
d("Loaded!"); | |
try { | |
let m = getRemoteMethod(win, 'foo.bar.baz'); | |
let result = await m(5, 7); | |
d(result); | |
m = getRemoteMethod(win, 'foo.bar.bazAsync'); | |
result = await m(5, 7); | |
d(result); | |
} catch (e) { | |
console.log(e.message); | |
console.log(e.stack); | |
} | |
}); | |
return win; | |
} | |
app.on('window-all-closed', () => { | |
if (process.platform !== 'darwin') { | |
app.quit(); | |
} | |
}); | |
app.on('activate', () => { | |
if (!mainWindow) { | |
mainWindow = createMainWindow(); | |
} | |
}); | |
app.on('ready', () => { | |
mainWindow = createMainWindow(); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import uuid from 'node-uuid'; | |
import {Observable, Disposable} from 'rx'; | |
const requestChannel = 'execute-javascript-request'; | |
const responseChannel = 'execute-javascript-response'; | |
let isBrowser = (process.type === 'browser'); | |
let ipc = require('electron')[isBrowser ? 'ipcMain' : 'ipcRenderer']; | |
const d = require(isBrowser ? 'debug' : 'debug/browser')('execute-js-func'); | |
function listenToIpc(channel) { | |
return Observable.create((subj) => { | |
let listener = (event, args) => { | |
d(`Got an event for ${channel}: ${JSON.stringify(args)}`); | |
subj.onNext(args); | |
}; | |
d(`Setting up listener! ${channel}`); | |
ipc.on(channel, listener); | |
return Disposable.create(() => | |
ipc.removeListener(channel, listener)); | |
}); | |
} | |
function getSendMethod(windowOrWebView) { | |
return ('webContents' in windowOrWebView) ? | |
(...a) => { | |
d(`webContents send: ${JSON.stringify(a)}`); | |
windowOrWebView.webContents.send(...a); | |
} : | |
(...a) => { | |
d(`webView send: ${JSON.stringify(a)}`); | |
windowOrWebView.send(...a); | |
}; | |
} | |
function listenerForId(id) { | |
return listenToIpc(responseChannel) | |
.where((receive) => receive.id === id) | |
.take(1) | |
.flatMap((receive) => { | |
if (receive.error) { | |
let e = new Error(receive.error.message); | |
e.stack = receive.error.stack; | |
return Observable.throw(e); | |
} | |
return Observable.return(receive.result); | |
}) | |
.timeout(5 * 1000); | |
} | |
export function getRemoteMethod(windowOrWebView, pathToObject) { | |
return function(...args) { | |
return executeJavaScriptMethod(windowOrWebView, pathToObject, ...args); | |
}; | |
} | |
export function remoteEval(windowOrWebView, str) { | |
let send = getSendMethod(windowOrWebView); | |
let toSend = { id: uuid.v4(), eval: str }; | |
let ret = listenerForId(toSend.id).toPromise(); | |
d(`Sending: ${JSON.stringify(toSend)}`); | |
send(requestChannel, toSend); | |
return ret; | |
} | |
export function executeJavaScriptMethod(windowOrWebView, pathToObject, ...args) { | |
let send = getSendMethod(windowOrWebView); | |
if (!pathToObject.match(/^[a-zA-Z0-9\.]+$/)) { | |
return Promise.reject(new Error('pathToObject must be of the form foo.bar.baz')); | |
} | |
let toSend = { args, id: uuid.v4(), path: pathToObject }; | |
let ret = listenerForId(toSend.id).toPromise(); | |
d(`Sending: ${JSON.stringify(toSend)}`); | |
send(requestChannel, toSend); | |
return ret; | |
} | |
function objectAndParentGivenPath(path) { | |
let obj = window; | |
let parent = obj; | |
for (let part of path.split('.')) { | |
parent = obj; | |
obj = obj[part]; | |
} | |
d(`parent: ${parent}, obj: ${obj}`); | |
if (typeof(parent) !== 'object') { | |
throw new Error(`Couldn't access part of the object window.${path}`); | |
} | |
return [parent, obj]; | |
} | |
async function evalRemoteMethod(path, args) { | |
let [parent, obj] = objectAndParentGivenPath(path); | |
let result = obj; | |
if (typeof(obj) === 'function') { | |
d("obj is function!"); | |
let res = obj.apply(parent, args); | |
result = res; | |
if (typeof(res) === 'object' && 'then' in res) { | |
d("result is Promise!"); | |
result = await res; | |
} | |
} | |
return result; | |
} | |
export function init() { | |
let listener = async function(e, receive) { | |
d(`Got Message! ${JSON.stringify(receive)}`); | |
try { | |
if (receive.eval) { | |
receive.result = eval(receive.eval); | |
} else { | |
receive.result = await evalRemoteMethod(receive.path, receive.args); | |
} | |
d(`Replying! ${JSON.stringify(receive)}`); | |
e.sender.send(responseChannel, receive); | |
} catch(err) { | |
receive.error = { | |
message: err.message, | |
stack: err.stack | |
}; | |
d(`Failed! ${JSON.stringify(receive)}`); | |
e.sender.send(responseChannel, receive); | |
} | |
}; | |
d("Set up listener!"); | |
ipc.on('execute-javascript-request', listener); | |
return Disposable.create(() => ipc.removeListener('execute-javascript-request', listener)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment