Skip to content

Instantly share code, notes, and snippets.

@nvx
Forked from anonymous/ipc.js
Last active August 29, 2015 14:23
Show Gist options
  • Save nvx/4d06b04ce11355c126c4 to your computer and use it in GitHub Desktop.
Save nvx/4d06b04ce11355c126c4 to your computer and use it in GitHub Desktop.
ipc_frame_location = "https://change.this.to.location.of/ipcframe.html"
/*
Cross-tab, cross-domain messaging
Author: shig
License: Public Domain
Example:
Firstly, create a small HTML file to be loaded as an iframe,
and put it it on a web server in the same directory as this script.
Change ipc_frame_location above to point to the file:
<html><head>
<script type="application/javascript" src="ipc.js"></script>
<script type="application/javascript"> _ipc_init_iframe() </script>
</head><body></body></html>
To get two pages to talk to each other, one listens, and then other connects to
the listener.
Example listner code:
ipc = new CrossTabIPC()
ipc.whenReady.then(function() {
goatport = ipc.listen('goat')
goatport.onmessage = function(e) {
console.log("goatlistener received ",e.data)
e.target.postMessage("pong")
}
})
Other Page:
ipc = new CrossTabIPC();
ipc.whenReady.then(function() {
ipc.subscribe('goat').then(function(mp) {
mp.onmessage = function(e) { console.log('from goat:',e) }
mp.postMessage("Hello there goat")
})
.catch(function(){ console.log('subscription fail') })
})
*/
/*
Usage:
new CrostTabIPC()
CrossTabIPC.whenReady is a Promise that will succeed once the instance is
ready to use
CrossTabIPC.listen(channelname) returns a MessagePort object
CrossTabIPC.subscribe(channelname) returns a Promise that will succeed iff
a different window has already called CrossTabIPC.listen() with the same channel
name. The success callback of the promise will be passed a MessagePort object,
the counterpart of the one returned from the original listen call.
For more details on using MessagePorts see :
https://developer.mozilla.org/en-US/docs/Web/API/MessagePort
*/
/*
Implementation:
To talk talk between multiple windows, we need to use a SharedWorker.
Unfortunately, a ShardWorker won't work across origins.
To get around this restriction, we get both windows to open up an invisible iframe
to the same page. We can send messages cross-origin to iframes, and the iframes
can communicate cross-window because both iframes are in the same domain. Messages
can then pass along this chain:
window 1 (domain A) ShaerdWorker window 2 (domain C)
------------------------ (domain B) -----------------------
| .......... | ------------- | .......... |
| ipc.js====: iframe: :========| ipc.js |======: iframe: :===ipc.js |
| : domain B : | ------------- | : domain B : |
| : ipc.js : | | : ipc.js : |
| .......... | | .......... |
------------------------ -----------------------
The iframe code just passes any message events down to the SharedWorker
CrossTabIPC.listen() creates a MessageChannel, and then posts a message
['listen', channelname] down to via the iframe to the SharedWorker, and attaches
one of the MessagePorts. It returns the other MessagePort to the caller.
The SharedWorker waits for any 'listen' messages, and stores the attached MessagePort
When the SharedWorker receives a similar 'subscribe' message, it responds with the
stored MessagePort, which is returned (through a different MessagePort) to the caller
*/
/*--------------------------------------------------------------------------
* window-side implementation
*/
function CrossTabIPC(onReady) {
this.whenReady = new Promise((function (resolve,reject) {
this._on_iframe_load = resolve
}).bind(this))
this.initDocumentIPC()
}
CrossTabIPC.prototype._embed_iframe = function (callback) {
var iframe = document.createElement('iframe')
iframe.src = ipc_frame_location
iframe.width = 0
iframe.height = 0
iframe.style.display = 'none'
iframe.id = "ipciframe"
iframe.onload = this._on_iframe_load.bind(this)
document.head.appendChild(iframe)
this._embed_iframe = iframe;
}
CrossTabIPC.prototype.initDocumentIPC = function() {
this._embed_iframe()
}
CrossTabIPC.prototype.listen = function(name) {
var channel = new MessageChannel()
this._embed_iframe.contentWindow.postMessage(['listen',name],'*',[channel.port2])
return channel.port1
}
CrossTabIPC.prototype.subscribe = function(name) {
var promise = new Promise((function(resolve,reject) {
var channel = new MessageChannel()
channel.port1.onmessage = (function(e) {
if (e.data = 'success') {
resolve(e.ports[0])
} else {
reject()
}
})
this._embed_iframe.contentWindow.postMessage(['subscribe',name],'*',[channel.port2])
}).bind(this))
return promise
}
/*--------------------------------------------------------------------------
* iframe implementation
*/
var _ipc_iframe_sharedworker;
function _ipc_iframe_receive_message(event) {
ipc_iframe_sharedworker.port.postMessage(event.data,event.ports)
}
function _ipc_init_iframe() {
window.addEventListener("message", _ipc_iframe_receive_message, false);
ipc_iframe_sharedworker = new SharedWorker("ipc.js")
ipc_iframe_sharedworker.port.start()
}
/*--------------------------------------------------------------------------
* shared worker implemenation
*/
function _ipc_sharedworker_onportmessage(e) {
if (e.data[0] == 'listen') {
console.log('listen',e.data[1])
self.listeners[e.data[1]] = e.ports[0]
}
if (e.data[0] == 'subscribe') {
console.log('subscribe',e.data[1])
if (typeof(self.listeners[e.data[1]]) != 'undefined') {
e.ports[0].postMessage('success',[
self.listeners[e.data[1]]
])
} else {
e.ports[0].postMessage('failed');
}
}
}
function _ipc_sharedworker_onconnect(e) {
e.ports[0].onmessage = _ipc_sharedworker_onportmessage
}
function _ipc_sharedworker_onmessage(e) {
console.log("_ipc_sharedworker_onmessage",e)
}
function _ipc_init_sharedworker() {
self.listeners = {}
self.onconnect = _ipc_sharedworker_onconnect
self.onmessage = _ipc_sharedworker_onmessage
}
/* entrypoint for sharedworker
*
* If called from a Worker, we'll have importScripts available,
* but won't anywhere else
*/
if (typeof importScripts !== 'undefined') {
_ipc_init_sharedworker()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment