Created
June 28, 2015 11:10
-
-
Save anonymous/5ce403cec757c72c8e4c to your computer and use it in GitHub Desktop.
This file contains 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
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