Re: Should MIDIInput use a callback instead of events?

On 19/02/2013, at 10:16 AM, Dominic Cooney <dominicc@chromium.org> wrote:

> Hi Marcos, Audiophiles,
> 
> On Tue, Feb 19, 2013 at 4:38 PM, Marcos Caceres <w3c@marcosc.com> wrote:
>> Hi Dominic!
>> 
>> On Tuesday, 19 February 2013 at 06:45, Dominic Cooney wrote:
>> 
>> > Greetings Web Audiophiles,
>> >
>> > In reading the Web MIDI spec I note that MIDIInput is declared this way [1]:
>> >
>> > MIDIInput implements EventTarget;
>> >
>> > interface MIDIInput : MIDIPort {
>> > attribute EventHandler onmessage;
>> > };
>> >
>> >
>> > interface MIDIEvent : Event {
>> > readonly attribute double receivedTime;
>> > readonly attribute Uint8Array data;
>> > };
>> >
>> >
>> > There are a number of problems with these definitions.
>> >
>> > 1. MIDIEvent does not specify a constructor, so it is not possible to programmatically create and dispatch a MIDIEvent. That complicates scenarios such as writing unit tests for MIDI input handlers.
>> Yep, we have a bug for this. The Editor has been requesting additional use cases. See:
>> https://github.com/WebAudio/web-midi-api/issues/1
>> 
>> Be great if you could also comment there :)
> 
> Sure. I will do that. It looks like the testing case is already discussed to some extent.
>  
>> >
>> > 2. As noted in the spec, DOM4 Event objects have a lower-resolution timeStamp. I think it is confusing to have two kinds of timestamps.
>> 
>> Yep, I raised the same issue - see discussion at:
>> https://github.com/WebAudio/web-midi-api/issues/4
>> 
>> But we didn't find a way around that.
> 
> Not using DOM4 Events is one way around it.
>  
>> > 3. The Event interface [2] and EventTarget interface [3] bring with them a number of concepts that aren’t useful for this application: related targets (ie the currentTarget property), event phases, event propagation (perhaps), bubbling, cancelation, default handling and capturing.
>> 
>> Right - despite not participating in a DOM tree, the eventing model still seems appropriate.
> 
> I think we misunderstand each other; I think the DOM4 Event model seems inappropriate because many concepts are meaningless to Web MIDI. However this is admittedly true of other uses of DOM4 Events, such as WebSockets. How significant is wiring up multiple listeners to a single MIDIInput?

This is still an issue/under-described use case in the spec. I've also raised this previously (i.e., are these objects singletons?) 


>  
>> >
>> > 4. The style of MIDIInput implements EventTarget is not used by recent specs; these universally use X : EventTarget for some interface X. This old style of EventTarget implementation complicates implementation.
>> 
>> This is my fault and I've been meaning to file a bug to address this, so thanks for bringing it up. The design of the API did not lend itself to support both EventTarget and MIDIPort because MIDIPort does not inherit from EventTarget. Generally, EventTarget sits at the bottom of the inheritance chain, but that would mean MIDIPort would become an EventTarget. We didn't want to make MIDIOutput unnecessarily become and EventTarget; hence, "implements" EventTarget on MIDIInput seemed the right way to go.
>> 
>>  (btw ...it actually does not complicate the implementation - but you are correct in that it's a bit weird - but this is DOM3's fault because it kinda says to do this and it's what browsers have generally done). AFAIK, only FireFox seems to expose the EventTarget interface as per WebIDL (this is something recent) - other browsers don't yet expose EventTarget - like in Chrome, try window.EventTarget … or try searching the prototype chain of any object that inherits EventTarget and you will see it is not there (shock!, I know :)).
> 
> I'm familiar with the implementation of EventTarget in Chrome :) Having both "implements EventTarget" and ": EventTarget" complicates the implementation of wrapping and unwrapping EventTargets. If all EventTargets were at the root of the prototype chain, machinery for the JavaScript wrappers of EventTargets could be simplified.

I agree. As I said, the issue is the current inheritance model. Maybe MIDIInput : EventTarget, but implements MIDIPort? 

>  
>> I don't know how we "fix" this except to make MIDIPort inherit EventTarget. See also for a bit more discussion:
>> http://lists.w3.org/Archives/Public/public-audio/2012OctDec/0658.html
> 
> Thanks for context; I missed this when searching the list. I see this specific question has already been given some consideration.
>  
>> > One possible benefit of using DOM Events is that it permits wiring up multiple listeners. Is that an intended use case of onmessage?
>> No, that's what .addEventListener() is for. onmessage is just the catchall.
> 
> I think I wrote that line too quickly. I meant "is that the intended use case of the events that are handled by the onmessage handler", etc.

I guess the answer is yes.

>  
>> Having a single onmessage is dangerous in that any object can easily override it (so naive library B can break library A - or both library A and B cannot simultaneously rely on onmessage - except if each instance of MIDIInput is unique, but then it just gets complicated). Using addEventListener protects from such conflicts and keeps things relatively simple and safe.
> 
> Is it likely for two libraries to compete for a given MIDIInput object? These are not global objects like window or document.

Could be, if they are singletons. Still not clear if they are or not. Certainly good candidates to be and I think I implemented them that way in my own reference implementation. 

> 
>> > I note that Web Audio’s ScriptProcessorNode has a roughly analogous case: processing buffers of binary audio data in onaudioprocess. ScriptProcessorNode has moved away from using DOM Events.. Given the problems using DOM Events for MIDIInput noted above, I think it would be better for MIDIInput to have a simple callback, perhaps something like:
>> 
>> I don't think the above are "problems" - or at least I'm not seeing/understanding explicit problems. The only thing you mentioned was that it "complicates implementation", but it would be great if you could qualify that with some explicit details (today's browsers don't have a problem with implementing EventTarget - all except FireFox do it, AFAIK). WebIDL implements, as I understand it, just says to copy the methods/attributes from one interface onto another without inheriting from it - behaviour stays the same.
> 
> Here is some elaboration of why "implements EventTarget" is problematic:
> 
> "implements EventTarget" is less friendly to web developers, because they now must do checks like 'addEventListener' in x && 'dispatchEvent' in x && ... to test whether x is an EventTarget (actually an EventTarget-like object.) In comparison, ": EventTarget" means web developers can write x instanceof EventTarget which is easy to understand.

Sure. But in reality, there is no window.EventTarget except in one browser. This even though we've had specifications using EventTarget for about 10 years. Adding EventTarget to the proto chain has not yet happened. To deal with legacy browsers, you still need to do the above duck typing.
> 
> Further, if a web developer wants to hook addEventListener (perhaps to answer the perennial question of "what EventListeners are registered on this object?" in a debugging scenario) in the ": EventTarget" case they can replace one property--EventTarget.prototype.addEventListener--and have a hook at that operates on all event targets. In the "implements EventTarget" case, they have to monkeypatch all interfaces which mix it in.

Sure. But that's pretty rare. Also, browsers are starting to provide better tools for observing events. 
> 
> Regarding the specifics of what is exposed in Chrome, Firefox, etc.--that is where the browser are today. I am working on making Chrome be compatible with the relevant specs. This amounts to catching up to Firefox in some respects.

That's awesome to hear! 

> 
> Having a mixture of "implements EventTarget" and ": EventTarget" complicates the implementation of EventTargets in Chrome because the machinery which converts to and from EventTargets must treat these cases differently. I'm happy to point you to specific FIXMEs and infelicities if you want to dig deeper.

I know you would prefer that we did away with events altogether, but would the solution I proposed above (implements MIDIPort instead of EventTarget) work for you? 

>  
>> > interface MIDIInput {
>> > attribute MIDIInputHandler? onmessage;
>> > }
>> >
>> > callback MIDIInputHandler = void (double receivedTime, Uint8Array data);
>> I personally don't like the above, because (as a JS developer) it would mean having to build my own event dispatcher to then route everything from onmessage to other listeners - I also mentioned the potential for someone else's library to accidentally trash/replace my callback. It is also confusing in that if one finds an "onwhatever" IDL handler attribute on an interface, then it is an expectation that one would also be able to .addEventListener("whatever") on the same interface. Also, if this API grows in the future, it would mean stacking more arguments into the MIDIInputHandler callback (though you are correct in that MIDIEvent will contain a lot of useless garbage inherited from DOM's Event object).
> 
> Yes, if this became a callback, it should not be called on*.
> 
> Regards,
> 
> Dominic

Received on Tuesday, 19 February 2013 10:44:56 UTC