#16: Custom Elements

Visit on Github.

Opened Oct 24, 2013

LC As of http://www.w3.org/TR/2013/WD-custom-elements-20131024/ Edcopy: https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html

Discussions

Comment by @annevk Oct 24, 2013 (See Github)

See also #13.

It seems that monkey patching http://dom.spec.whatwg.org/ as done in this specification is very bad.

Comment by @domenic Jan 29, 2014 (See Github)

There's something going on here around "nanotasks" http://w3c.github.io/webcomponents/spec/custom/#enqueuing-and-invoking-callbacks

@annevk had a conversation with @dglazkov in #whatwg about it that may have cleared things up, but I had a hard time following along. It's definitely worth figuring out what's going on there though.

Comment by @domenic Jan 29, 2014 (See Github)

This line in particular is very worrying:

When transitioning back from the user agent code to script, pop the element queue from the processing stack and invoke callbacks in that queue.

Since that is pretty much the role of the event loop, and having it superceded by one line of prose seems... bad.

Comment by @dglazkov Jan 29, 2014 (See Github)

pretty much the role of the event loop

Well, no. Transitions from UA code to script occur much more frequently. Anytime you call a DOM method, you're leaving script and going into UA code, and back. Here's a simple example:

div.innerHTML = "<foo-bar></foo-bar>";
div.querySelector("foo-bar").doSomething();

The user of <foo-bar> expects the createCallback to be called synchronously. We can't do that, since invoking script in the middle of a parser is a Bad Idea (tm). That's what this machinery is for. It separates enqueuing callbacks from invoking callbacks, and provides a safe/consistent mechanism to invoke the callbacks in a way that appears synchronous to the user.

In the example above, the createCallback is enqueued as the elements are constructed and is invoked just before innerHTML returns.

This type of write barrier will be especially necessary when we try to make DOM self-hosted. There are many places where the UA needs to ensure that the user code doesn't run.

The bug with all the gory details: https://www.w3.org/Bugs/Public/show_bug.cgi?id=22459

Comment by @annevk Jan 29, 2014 (See Github)

Running callbacks just before you return from a large number of methods and getters that previously did not have that behavior is a new concept. I guess we might need it, but I would like us to be much more clear and explicit about it.

Comment by @dglazkov Jan 29, 2014 (See Github)

I would assert that the concept of running callbacks as an effect of executing a method/getter is not new. This happens all the time in JS frameworks. What's new is a mechanism that enables web platform to do this without jeopardizing integrity of DOM operations.

I wonder if the right way to define it is as a separate DOM write barrier abstraction that could be used to provide "synchronous on the outside, asynchronous on the inside" semantics. I could give it a shot. WDYT?

Comment by @domenic Jan 29, 2014 (See Github)

I wonder if the right way to define it is as a separate DOM write barrier abstraction that could be used to provide "synchronous on the outside, asynchronous on the inside" semantics.

I am having a hard time understanding this; it seems to contradict the earlier description of the situation as simply running some callbacks before returning (which seems like it has nothing to do with asynchronicity).

I know it's a lot to ask, but could you provide some JS code samples to explain what exactly is going on here? E.g., if we were to implement this behavior purely in JS, by monkeypatching a bunch of DOM methods, what would that look like?

Comment by @dglazkov Jan 29, 2014 (See Github)

Sure. First, the setup:

class Queue() {
constructor() {
   this.queue = [];
}
enqueue(callback) {
    this.queue.push(callback);
}
invoke() {
    this.queue.forEach(function(callback) {
        callback();
    });
}
}

var queue = new Queue();

Now, let's suppose innerHTML enqueues callbacks by calling queue.enqueue(callback):

class Element {
...
set innerHTML(value) {
    // per http://domparsing.spec.whatwg.org/#dom-element-innerhtml
    ...
    // when HTML parser creates a new element, it enqueues the created callback
    // per http://w3c.github.io/webcomponents/spec/custom/#parsing
    queue.enqueue(newlyBornElementsCreatedCallback);
    ...
    // just before returning, we drain the queue
    queue.invoke();
}
...   
}

From the perspective of the HTML parser, the operation is asynchronous -- it only enqueues the callbacks. From the perspective of the user of Element.innerHTML, the operation is synchronous -- the callbacks were invoked as a result of calling the setter. Hence "asynchronous on the inside, synchronous on the ouside".

Comment by @domenic Jan 29, 2014 (See Github)

Ah! Thank you, that clears things up immensely. However, I'd encourage you to not use the term "asynchronous" for this, as traditionally in JS we use that to refer to defering something outside of the current event loop tick. This behavior would definitely be called synchronous, at least how we normally use the terms in JS.

With that cleared up, I think my concerns reduce to match those of @annevk, namely that the blanket monkey-patching of "when transitioning back from the user agent code to script" is a bit more wishy-washy than we'd hope for. I'll leave it to @annevk as Master of the DOM to say what the best way of addressing that is though, whether via the write-barrier abstraction you mention or something else.

Comment by @annevk Jan 29, 2014 (See Github)

I think making it explicit in the binding layer (IDL) as I suggested on public-webapps would make the most sense conceptually.

Comment by @mnot Sep 30, 2014 (See Github)

Discussed at London F2F; needs to be transferred to a bug. @domenic to do.

Comment by @mnot Jan 8, 2015 (See Github)

Discussed in NYC; dominic following up.

Comment by @domenic Jan 14, 2015 (See Github)

Found the bug, was filed already https://www.w3.org/Bugs/Public/show_bug.cgi?id=24579 and has recently come up again on public-webapps.