Skip to content

Instantly share code, notes, and snippets.

@supersha
Forked from JamesMGreene/0 - README.md
Created September 23, 2015 02:47
Show Gist options
  • Save supersha/60032e4af4ffdaf0f025 to your computer and use it in GitHub Desktop.
Save supersha/60032e4af4ffdaf0f025 to your computer and use it in GitHub Desktop.
PhantomJS: Conceptual implementation of a short-term shim to establish the window.postMessage API for IPC

Topic

Providing an inter-process communication (IPC) mechanism such that a WebPage can explicitly signal back to PhantomJS as a push, thus eliminating/minimizing the need for users to setup polling functions, etc.

In PhantomJS 1.6, @detro added a WebPage onCallback handler that could be triggered from a webpage client by invoking the specially attached window.callPhantom function. However, @ariya expressed some discontent with this approach and so the three of us began discussing utilizing an existing API for cross-domain messaging instead: window.postMessage. (See Discussion for more info.)

Problems

PhantomJS currently only allows for a single handler per signal (a separate problem I'm working on over here). As such, automatically attaching internal handler may prevent users from attaching their own handlers for onInitialized (that's bad) and onCallback (acceptable, as it's deprecated).

However, I think this could easily be worked around by having a different name for the actual internal handler (i.e. not utilizing page.onInitialized) but then have that handler include a check for a user-set page.onInitialized handler function and invoke it if it exists.

Related Links

Discussion

PhantomJS issue #224

window.postMessage documentation

window.postMessage articles and demos

// Override some functionality within the "create" function of the "webpage" core module to immediately attach signal handlers
// within "create":
page.onInitialized = function() {
page.evaluate(function() {
// TODO: This needs to happen in frames as well!
// Override the window.postMessage function
var _postMessage = window.postMessage;
window.postMessage = function(message, targetOrigin) {
if (targetOrigin === "PhantomJS") {
window.callPhantom(message);
}
else {
_postMessage.call(window, message, targetOrigin);
}
};
});
};
page.onCallback = function(message) {
if (typeof page.onMessage === "function") {
page.onMessage(message);
}
};
function(window) {
var page = require('webpage').create();
// NOTE: Alternatively, we could explode this MessageEvent object into separate arguments
page.onMessage = function(messageEvent) {
// Documentation:
// messageEvent = {
// data: "hi", /* message object of any [typical] data type: String, Object, Array, etc. */
// origin: "http://my.page.com/", /* string representing the sender window's web domain */
// source: null /* Window reference object... I assume this can't be proxied back into Phantom,
// nor do we probably care to unless we want to enable sending replies */
// }
console.log(messageEvent.data);
};
page.open("http://davidwalsh.name/demo/window-post-message.php", function(status) {
// Let some messages come through and then quit
window.setTimeout(function() {
phantom.exit(status === "success");
}, 5000);
});
})(this);
<!DOCTYPE html>
<html>
<head>
<title>Test window.postMessage API</title>
<style type="text/css">
h2 {
font-style: italic;
color: red;
}
</style>
</head>
<body>
<div>
<h1>Testing window.postMessage API</h1>
<h2>Open your console....</h2>
</div>
<script type="text/javascript">
(function() {
var thisOrigin = window.location.protocol + "//" + window.location.host,
onMessage = function(messageEvent) {
if (messageEvent.origin === thisOrigin) {
console.log("Accepted! Data: '" + messageEvent.data + "'");
}
else {
console.error("Rejected! '" + messageEvent.origin + "' !== '" + thisOrigin + "'");
}
};
window.addEventListener("message", onMessage, false);
var intervalId = window.setInterval(function() {
window.postMessage("Do you accept?", thisOrigin);
}, 1000);
var timeoutId = window.setTimeout(function() {
window.clearTimeout(timeoutId);
window.clearInterval(intervalId);
window.removeEventListener("message", onMessage, false);
// NOTE: Any non-URI value other than "*" will throw an exception
//var originForPhantomJS = "PhantomJS";
var originForPhantomJS = "http://phantom.js/";
window.postMessage("DONE", originForPhantomJS);
}, 10000);
})();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment