Skip to content

Instantly share code, notes, and snippets.

@JamesMGreene
Created September 13, 2012 18:48
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save JamesMGreene/3716654 to your computer and use it in GitHub Desktop.
Save JamesMGreene/3716654 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>
@JamesMGreene
Copy link
Author

cc: @detro, @ariya

@brettz9
Copy link

brettz9 commented Mar 21, 2016

Can I ask whether PhantomJS now supports window.postMessage to and from iframe content? I got it working for workers, but not an iframe...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment