This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 13984 - Need a way to object detect which binary formats are supported before connecting
Summary: Need a way to object detect which binary formats are supported before connecting
Status: RESOLVED WORKSFORME
Alias: None
Product: WebAppsWG
Classification: Unclassified
Component: WebSocket API (editor: Ian Hickson) (show other bugs)
Version: unspecified
Hardware: PC Linux
: P2 normal
Target Milestone: ---
Assignee: Ian 'Hixie' Hickson
QA Contact: public-webapps-bugzilla
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-08-31 23:50 UTC by Joel Martin
Modified: 2011-09-14 19:17 UTC (History)
6 users (show)

See Also:


Attachments

Description Joel Martin 2011-08-31 23:50:36 UTC
The API needs to define a way for Javascript to object detect whether the WebSocket API supports sending and receiving of binary data and this needs to be exposed prior to initiating a connection (e.g. because sub-protocol and extension settings may depend on whether binary data can be sent or not).

Chrome 14 and 15 and Firefox 6/7 support the new protocol and other updates to the new API but don't actually support binary data sending/receiving yet.

Until the object is instantiated, the WebSocket objects looks the same for the old API as for the new API.

I tried the following hack:

    var wstest = new WebSocket('ws://localhost:57111');
    if (typeof(wstest.binaryType) !== "undefined") {
        ...  // binary
    } else {
        ...  // text only
    }
    wstest.close();
    wstest = null;

However, that is insufficient for a few reasons:

- the spec allows for the UA to initiate a connection immediately in the background (e.g. Chrome does this) which means an async error is trigger by failure to connect.

- the spec is not crystal clear about what the behavior of binaryType is in the case where binary data is not in fact supported. For example, in Chrome 14 and 15 binaryType is visible and set to "blob" by default and can be set to "arraybuffer". However, binary data is not in fact supported.

- it's just plain hideous.

My suggestions:

- In addition to the readyState constants (CLOSED, CLOSING, etc), the uninstantiated WebSocket object should also have the following constants: BLOB and ARRAYBUFFER which are equal to "blob" and "arraybuffer" respectively. These should only appear in the WebSocket object if those data types are actually supported for sending and receiving. This is backwards compatible with the current definition (i.e. you can do ws.binaryType = "blob"; or ws.binaryType = WebSocket.BLOB);

- The spec should clarify that the binaryType field should only appear if binary data is in fact supported (or alternately, it is there but should be empty and throw an error if an attempt is made to set it).
Comment 1 Jonas Sicking (Not reading bugmail) 2011-08-31 23:58:33 UTC
We're not going to unprefix our implementation in firefox until it supports binary data. Sucks that chrome choose to not prefix though.

Possibly chrome could add some specific API so that you can do the needed detection there. I really don't want to clutter the spec just because chrome chose not to prefix as they should have.
Comment 2 Joel Martin 2011-09-01 00:12:09 UTC
Jonas, yes, that sucks about Chrome and I filed a bug requesting they fix their existing implementations in 14 and 15 (https://bugs.webkit.org/process_bug.cgi). 

But if even Chrome prefixed, that still wouldn't address the primary issue of there being no way to detect binary support prior to instantiating the object. The older Hixie implementation has been in the wild for 2 years (in Chrome and in web-socket-js). The new API AND protocol look the same until you instantiate it which means you have no change to set sub-protocols and extensions based on whether the support is there or not.

This affects me directly with noVNC/websockify. noVNC sends/receives binary data so until the binary support exists, it must base64 encode it. This applies to the older Hixie protocol implementations and to the HyBi implementations that are connected to incomplete APIs. noVNC negotiates with websockify using the sub-protocol whether websockify should send binary data or base64 encode it as text (unfortunately with VNC, the server speaks first so the only good opportunity is during the handshake via sub-protocol).

It doesn't have to be my proposal, but there needs to be SOME way of determining binary data support before initiating a connection.
Comment 3 Simon Pieters 2011-09-01 07:33:22 UTC
(In reply to comment #0)
> The API needs to define a way for Javascript to object detect whether the
> WebSocket API supports sending and receiving of binary data and this needs to
> be exposed prior to initiating a connection (e.g. because sub-protocol and
> extension settings may depend on whether binary data can be sent or not).

The binaryType attribute can be used to check support for binary data.

> Chrome 14 and 15 and Firefox 6/7 support the new protocol and other updates to
> the new API but don't actually support binary data sending/receiving yet.
> 
> Until the object is instantiated, the WebSocket objects looks the same for the
> old API as for the new API.
> 
> I tried the following hack:
> 
>     var wstest = new WebSocket('ws://localhost:57111');
>     if (typeof(wstest.binaryType) !== "undefined") {
>         ...  // binary
>     } else {
>         ...  // text only
>     }
>     wstest.close();
>     wstest = null;

I'm not sure I follow what you're trying to do here. This will immediately close the connection. If you just want to check if the property exists on the prototype, check the prototype directly:

var binarySupported = typeof WebSocket.prototype.binaryType != 'undefined';

> However, that is insufficient for a few reasons:
> 
> - the spec allows for the UA to initiate a connection immediately in the
> background (e.g. Chrome does this) which means an async error is trigger by
> failure to connect.

I don't see why this is a problem.

> - the spec is not crystal clear about what the behavior of binaryType is in the
> case where binary data is not in fact supported. For example, in Chrome 14 and
> 15 binaryType is visible and set to "blob" by default and can be set to
> "arraybuffer". However, binary data is not in fact supported.

That Chrome ships half-baked implementations that breaks object detection is a problem with Chrome, not with the spec.

> - it's just plain hideous.
> 
> My suggestions:
> 
> - In addition to the readyState constants (CLOSED, CLOSING, etc), the
> uninstantiated WebSocket object should also have the following constants: BLOB
> and ARRAYBUFFER which are equal to "blob" and "arraybuffer" respectively. These
> should only appear in the WebSocket object if those data types are actually
> supported for sending and receiving. This is backwards compatible with the
> current definition (i.e. you can do ws.binaryType = "blob"; or ws.binaryType =
> WebSocket.BLOB);

I don't see how this helps you. Chrome 14 and 15 don't support these constants, so doesn't help you with detecting. Chrome 16 might implement the constants still without actually supporting binary data. Would you then propose new constants, like "BLOB_AND_I_REALLY_MEAN_IT_THIS_TIME"?

> - The spec should clarify that the binaryType field should only appear if
> binary data is in fact supported (or alternately, it is there but should be
> empty and throw an error if an attempt is made to set it).

I think this is obvious. Browsers should not break object detection. At Opera we are very careful to keep object detection working (for instance we made sure to make the "WebSocket" property invisible when websockets are disabled so that it can be checked for object detection). Trying to work around browsers that break object detection by introducing new features that can be checked doesn't solve the problem and just bloats the language. Instead I suggest you take up this problem with the Chrome team so that they are more careful in the future to keep object detection working.
Comment 4 Simon Pieters 2011-09-01 07:38:13 UTC
(In reply to comment #3)
> var binarySupported = typeof WebSocket.prototype.binaryType != 'undefined';

Alternatively:

var binarySupported = 'binaryType' in WebSocket.prototype;
Comment 5 Joel Martin 2011-09-01 14:55:19 UTC
Since the spec is not clear, the existence of binaryType (and other properties) on WebSocket is currently an implementation detail. For example in Chrome 15:

    > 'binaryType' in WebSocket.prototype
    false
    > var wstest = new WebSocket('ws://localhost:57111');
    > 'binaryType' in wstest
    true
    > wstest.binaryType
    'blob'

On Chrome, only constants and methods are visible in the prototype; binaryType, readyState, bufferedAmount, protocol, etc are not visible in the prototype. In firefox other properties are visible in the prototype (and I assume the same is true in Opera since you are suggesting it).

Certainly the best implementation might be to make these visible in the prototype, but IMO the spec is unclear here and should be made explicit if that is the expectation. Or perhaps this is required according to WebIDL (I admit I'm not up on that spec). If so, that's more weight I could add to the WebKit bug I filed in favor of making it invisible until it is supported and then making it visible in the prototype.

Anyways, another benefit of my proposal is that having the binary types explicitly mentioned (and visible prior to instantiation) is that "blob" and "arraytype" may not be the only binary types supported in the future and it would be really nice to be able to introspect what is supported without having to test by setting binaryType on an instantiated object and trap thrown DOM errors.

For example, I think supporting normal arrays of 0-255 and binaryStrings (ala FileReader) would be a useful future extension to the API.

As a near-future example, in web-socket-js (Flash polyfill/shim for WebSocket) it will probably support "array" (normal array of 0-255), "binarystring", and "arraybuffer"  but not "blob". Most browsers where web-socket-js will be used won't have ArrayBuffers or Blobs but it's still useful to have some way to send/receive binary data. ArrayBuffer was trivial to implement so I added it for the cases where people want to force web-socket-js instead of the native implementations (e.g. in Chrome 5-15 and firefox 6/7 to get binary support).

In addition, since web-socket-js is supporting older browsers, having the binaryType discoverable by setting binaryType and catching errors is not really workable (no guarantee of setters).

So yes, I hope WebKit fixes their implementation already in the wild. But I still think it would be really useful to have a standard way to properly introspect the binary data types supported by WebSocket. Adding two constants to the WebSocket spec doesn't seem like much clutter to me for the corresponding benefit. Adding constants to the prototype was just the first idea that came to mind. Perhaps there is another way of accomplishing the same thing (detect which binary types before instantiating) that is considered more HTML5ish?

I suppose this doesn't have to be done in the spec right now. Since web-socket-js is used in a lot of the cases where WebSocket is actually being used right now (Socket.IO, pusherapp, noVNC, etc), I suppose this functionality could be provided to most WebSocket developers by having the Javascript loader for web-socket-js update the native WebSocket prototype to add these constants based on browser version detection (sigh). Obviously that's nasty, but better than every developer who wants to use WebSocket binary support implementing their own detection hacks. Better still is for this issue to be solved cleanly in the spec :-)
Comment 6 Joel Martin 2011-09-01 15:11:09 UTC
It's looking unlikely that WebKit will fix this issue in existing releases :-<

https://bugs.webkit.org/show_bug.cgi?id=67335

I understand the reticence to design the spec to be bug compatible with existing implementations. However, I think the benefits of adding binary type introspection to the spec is valuable enough on it's own. The fact that it would make the in-the-wild WebKit bug a non-issue (if it's addressed quickly) is an added bonus.
Comment 7 Simon Pieters 2011-09-01 15:19:29 UTC
(In reply to comment #5)
> Since the spec is not clear, the existence of binaryType (and other properties)
> on WebSocket is currently an implementation detail. For example in Chrome 15:
> 
>     > 'binaryType' in WebSocket.prototype
>     false
>     > var wstest = new WebSocket('ws://localhost:57111');
>     > 'binaryType' in wstest
>     true
>     > wstest.binaryType
>     'blob'

Aha. That certainly makes detection less elegant indeed.

> On Chrome, only constants and methods are visible in the prototype; binaryType,
> readyState, bufferedAmount, protocol, etc are not visible in the prototype. In
> firefox other properties are visible in the prototype (and I assume the same is
> true in Opera since you are suggesting it).

Yes.

> Certainly the best implementation might be to make these visible in the
> prototype, but IMO the spec is unclear here and should be made explicit if that
> is the expectation. Or perhaps this is required according to WebIDL (I admit
> I'm not up on that spec). If so, that's more weight I could add to the WebKit
> bug I filed in favor of making it invisible until it is supported and then
> making it visible in the prototype.

The relevant part of WebIDL is http://dev.w3.org/2006/webapi/WebIDL/#es-attributes

It seems to say that it can either be on the prototype or on the object. I'm not sure why it does that. I'll file a bug on WebIDL.

> Anyways, another benefit of my proposal is that having the binary types
> explicitly mentioned (and visible prior to instantiation) is that "blob" and
> "arraytype" may not be the only binary types supported in the future and it
> would be really nice to be able to introspect what is supported without having
> to test by setting binaryType on an instantiated object and trap thrown DOM
> errors.

That's fair enough.
Comment 8 Simon Pieters 2011-09-02 06:27:05 UTC
(In reply to comment #7)
> The relevant part of WebIDL is
> http://dev.w3.org/2006/webapi/WebIDL/#es-attributes
> 
> It seems to say that it can either be on the prototype or on the object. I'm
> not sure why it does that. I'll file a bug on WebIDL.

This turned out to be a mis-edit and is now fixed.
Comment 10 Joel Martin 2011-09-13 14:16:13 UTC
I've filed a webkit bug on the WebIDL attribute issue: https://bugs.webkit.org/show_bug.cgi?id=68002

I've also re-opened the webkit WebSocket.binaryType specific issue: https://bugs.webkit.org/show_bug.cgi?id=67335

Perhaps others could weigh in soon so that there is some weight to make sure that a stable version of Chrome/WebKit doesn't go out the door missing the ability to do proper binary type object detection.

---

I still think it would be useful to be able to introspect WHICH binary types are supported. It seems pretty likely that at some point in the future we may want to support more than just blob and arraybuffer and it would be really nice to be able to object detect what the supported set is.

Is there any objection to constants on the prototype apart from increasing the size of the API definition? Perhaps a suggested alternative if you don't like my proposal?
Comment 11 Ian 'Hixie' Hickson 2011-09-14 18:06:59 UTC
The current state of matters is apparently just a Chrome bug, so there's nothing we can do in the spec to fix this today.

Going forward, if we add a new binary type then we can add a way to detect for it then. In the meantime, in compliant browsers the code in comment 4 is sufficient.
Comment 12 Joel Martin 2011-09-14 19:17:17 UTC
Okay. I was hoping to have a mechanism defined sooner rather than later so that web-socket-js could be used in situations where the browser doesn't have arraybuffer or blob support (but might still want to be able to send binary data). This means I will have to reconcile things later when we finally do have an official method. <sigh>

Ian (and Simon, Jonas, etc), perhaps you could weigh in on the two webkit bugs I referenced in comment #10. I'm not sure my voice bears enough weight to convince Chrome/WebKit devs that Chrome/WebKit is not compliant with the WebIDL property/attribute behavior (e.g. see this response here https://bugs.webkit.org/show_bug.cgi?id=67335#c5).