Sunday, October 21, 2012

Chrome and the Vanishing postMessage Prefix

‹prev | My Chain | next›

After managing to fix a display issue in the code editor used in Gaming JavaScript, I was not pleased to discover that my web worker issue had gotten worse. That is, the dreaded "INVALID_STATE_ERR: DOM Exception 11" has returned only now I cannot get rid of it.

The problem is that the Physijs web worker is generating the DOM exception:
<body></body>
<script src="http://gamingJS.com/52/Three.js"></script>
<script src="http://gamingJS.com/52/Detector.js"></script>
<script src="http://gamingJS.com/52/physi.js"></script>
<script src="http://gamingJS.com/52/ChromeFixes.js"></script>
<script>
// Physics
Physijs.scripts.ammo = 'http://gamingJS.com/52/ammo.js';
Physijs.scripts.worker = 'http://gamingJS.com/52/physijs_worker.js';
The other night, I could make the problem go away by moving the worker code to the root URL (http://gamingJS.com/physijs_worker.js), but this seems to have been the luck of caching. Now it generates an error regardless.

I dig into the Physijs worker code a bit and find that a recent commit tried to make the code more worky for non-Chrome browsers. In the web worker code, it did so by checking for transferable message support before formatting the messages:
 if ( self.webkitPostMessage ) {
    // format message here.
 }
Only webkit supports transferable messages, hence this method of support detection.

Transferable messages are a more efficient mechanism for sending messages from web workers. Instead of copying the entire contents of the message, a pointer to the message is sent. For this to work, the message needs to be a Float32Array instead of a regular array:
 if ( self.webkitPostMessage ) {
  // Transferable messages are supported, take advantage of them with TypedArrays
  worldreport = new Float32Array( /* ... */ );
  collisionreport = new Float32Array( /* ... */ );
 } else {
  // Transferable messages are not supported, send data as normal arrays
  worldreport = [];
  collisionreport = [];
 }
All of that works—except for the manner in which transferable message support is detected.

It turns out that canary Chrome no longer prefixes the postMessage() method, but it still requires the specially constructed message. So checking for webkitPostMessage will not work for future versions of Chrome.

I borrow an HTML5 Rocks transferable message detector to detect transferable support:
var ab = new ArrayBuffer(1);
transferableMessage(ab, [ab]);
var SUPPORT_TRANSFERABLE = (ab.byteLength === 0);
As in the HTML5 Rocks version, I send an array buffer of length 1 via postMessage, to which transferableMessage is assigned. If the message was copied (i.e. it was not transferred), then the buffer will be unchanged. If it was transferred, then the buffer length will be zero.

With that, I can check a boolean for transferable support instead of looking for a method that no longer exists:
if ( SUPPORT_TRANSFERABLE ) {
    // format message here.
 }
That seems to do the trick. It also works in the version of Firefox that I have (16), so hopefully this will work for all cases. I submit a pull request and call it a night.

Day #546

No comments:

Post a Comment