Re: Defining a constructor for Element and friends

On 1/9/15 8:01 PM, Domenic Denicola wrote:
> I was writing up my ideas in an email but it kind of snowballed into something bigger so now it's a repo: https://github.com/domenic/element-constructors

Domenic, thanks for putting this together.

Caveat: I won't get a chance to read through this carefully until 
Monday.  Responses below are based on just what's in the mail.

That said, I do have one question already: what does the term 
"own-instances" mean in that document?

>> whether it is acceptable to have an element whose name is "a", namespace is the HTML namespace, and interface is Element

I'd like to understand what you mean by "interface is Element" here, 
exactly.

> One ugly point of my design is that the constructor signature is `new Element(localName, document, namespace, prefix)`, i.e. I require the document to be passed in. I am not sure this is necessary

It's necessary, but I think the document should be allowed, but not 
required.  As in, the signature should be:

   new Element(localName, namespace, prefix, document).

(though maybe prefix should come after document; hard to say).  If the 
document is undefined, then we get a document as follows: Element is a 
function, so has an associated Realm.  This Realm's [[globalThis]] is a 
Window.  This Window has a .document; use that.

The only reason we need the ability to specify a document at all is that 
there are documents around that are not inside a browsing context (XHR 
responseXML, for example) and we need to be able to create elements that 
have those documents as an ownerDocument.  But those document's don't 
have their own global/Realm and hence don't have separate instances of 
the Element constructor.

I commented on the github issue in a bit less detail.

>> what is the use case for producing something that extends HTMLImageElement (and presumably has its internal slots?) but doesn't have "img" as the tag name and hence will not have anything ever look at those internal slots?
>
> Elsehwere on this thread or some related one IIRC he pointed out code that looks at the local name, finds "img", and casts to the C++ backing representation of HTMLImageElement. So from what I am gathering in his view the parts of the platform that treat <img> elements specially currently work by checking explicitly that something has local name "img" (and HTML namespace).

Yes.  And that's true of not just implementations but also 
specifications.  The entire HTML specification is written in terms of 
local name tests, for example.

>  From a naïve authoring point of view that seems suboptimal. I'd rather be able to do `class MyImg extends HTMLImageElement { constructor(document) { super(document); } }` and have MyImg instances treated specially by the platform in all the ways "img" currently is.

I don't quite see the issue here.  Presumably the HTMLImageElement 
constructor passes "img" as the localName to the HTMLElement 
constructor, so your MyImg would get "img" as the localName, right?

Can you explain what the concern is here?

Now I do think there's an authoring problem where if you want to do a 
<fancyimage> that's treated like <img> by the platform you have a 
problem.  But that doesn't seem to be what you're talking about... or 
are you?

> Or, for an easier example, I'd like to be able to do `class MyQ extends HTMLQuoteElement { constructor(document) { super(document); } }` and have `(new MyQ()).cite` actually work, instead of throw a "cite getter incompatible with MyQ" error because I didn't get the HTMLQuoteElement internal slots.

This should totally work, of course.  Why wouldn't it, exactly?  Given 
the subclassing proposal on the table in ES6 right now, it would work 
splendidly, since the HTMLQuoteElement constructor is what would perform 
the object allocation and it would pass along "q" as the localName. 
(Though actually, HTMLQuoteElement is excitingly complicated, because 
both <q> and <blockquote> would use that constructor, so it would need 
to either require one of those two strings be passed in, or default to 
"q" unless "blockquote" is passed in or something.)

> The logical extension of this, then, is that if after that `document.registerElement` call I do `document.body.innerHTML = <my-q cite="foo">blah</my-q>`

Ah, here we go.  This is the part where the trouble starts, indeed.

This is why custom elements currently uses <q is="my-q"> for creating 
custom element subclasses of things that are more specific than 
HTMLElement.  Yes, it's ugly.  But the alternative is at least major 
rewrite of the HTML spec and at least large parts of Gecko/WebKit/Blink. 
  :( I can't speak to whether Trident is doing a bunch of localName 
checks internally.

> However this idea that we'd like custom elements which inherit from existing elements to have their internal slots ties in to the whole upgrading mess

Right, which you get for free with the <q is="my-q"> setup, since you 
just get the slots for <q> and then the upgrade just has to worry about 
your proto chain...

> i.e. it might not be possible at all to make upgraded elements behave anything like parsed-from-scratch elements.

I fully expect upgraded elements to perform worse than 
parsed-from-scratch elements in practice, because of the deoptimizing 
effects of the dynamic __proto__ mutation involved in the upgrade. 
Maybe JS engines will work on making this deoptimize less, but it's not 
clear to me that it's been a huge priority for them so far.

-Boris

Received on Saturday, 10 January 2015 03:31:50 UTC