Re: Defining a constructor for Element and friends

2015-01-19 Thread Simon Pieters

On Fri, 16 Jan 2015 20:08:30 +0100, Domenic Denicola d...@domenic.me wrote:


From: Anne van Kesteren [mailto:ann...@annevk.nl]

How can that work if the custom element constructor needs to look in  
the registry to find its name? Pick a name at random?


Nah, it just automatically starts acting like HTMLQuoteElement: the  
localName option becomes required. See


https://github.com/domenic/element-constructors/blob/5e6e00bb2bb525f04c8c796e467f103c8aa0bcf7/element-constructors.js#L229-L233

https://github.com/domenic/element-constructors/blob/5e6e00bb2bb525f04c8c796e467f103c8aa0bcf7/element-constructors.js#L51-L54


Consider if HTML adds a new element that uses the same interface as  
another element, let's say foobar, so that both foobar and data use  
HTMLDataElement. When this happens, new HTMLDataElement() starts throwing?


Similarly, if HTML is changed such that an element changes to use a more  
specific interface, e.g. ruby changes from HTMLElement to  
HTMLRubyElement, then new HTMLElement('ruby') will start throwing?


If so, it seems it removes some flexibility with how HTML uses interfaces.  
In particular many elements use HTMLElement and it should be possible to  
change them to use a more specific interface. How do you envision to solve  
this? Should we assign element-specific interfaces to all post-HTML4  
elements now, just in case? Or make new HTMLElement('ruby') create an  
HTMLRubyElement? Something else?


--
Simon Pieters
Opera Software



Re: Defining a constructor for Element and friends

2015-01-19 Thread Anne van Kesteren
On Mon, Jan 19, 2015 at 11:01 AM, Simon Pieters sim...@opera.com wrote:
 If so, it seems it removes some flexibility with how HTML uses interfaces.
 In particular many elements use HTMLElement and it should be possible to
 change them to use a more specific interface. How do you envision to solve
 this? Should we assign element-specific interfaces to all post-HTML4
 elements now, just in case? Or make new HTMLElement('ruby') create an
 HTMLRubyElement? Something else?

You are correct that the forward compatibility aspects of Domenic's
proposal are lacking. I think we should introduce classes on a
one-per-element basis. And we should probably lock down some of the
generic interfaces or at least restrict them to only allow elements
with dashes in their name.


-- 
https://annevankesteren.nl/



RE: Defining a constructor for Element and friends

2015-01-16 Thread Domenic Denicola
From: Anne van Kesteren [mailto:ann...@annevk.nl] 

 How can that work if the custom element constructor needs to look in the 
 registry to find its name? Pick a name at random?

Nah, it just automatically starts acting like HTMLQuoteElement: the localName 
option becomes required. See 

https://github.com/domenic/element-constructors/blob/5e6e00bb2bb525f04c8c796e467f103c8aa0bcf7/element-constructors.js#L229-L233

https://github.com/domenic/element-constructors/blob/5e6e00bb2bb525f04c8c796e467f103c8aa0bcf7/element-constructors.js#L51-L54



RE: Defining a constructor for Element and friends

2015-01-15 Thread Domenic Denicola
I've updated my element constructors sketch at 
https://github.com/domenic/element-constructors/blob/master/element-constructors.js
 with a design that means no subclasses of HTMLElement (including the built-in 
elements) need to override their constructor or [Symbol.species](). It also 
uses an options argument for the constructors so it is more extensible in the 
future (e.g. Yehuda's attributes (or was it properties?) argument).

It mostly doesn't delve into custom elements, but does contain a small sample 
to illustrate how their don't need to override the constructor or 
[Symbol.species] either, and how their constructor works exactly the same as 
that of e.g. HTMLParagraphElement.

One interesting thing that falls out of the design is that it's trivial to 
allow a custom element class to be registered for multiple names; it requires 
no more work on the part of the class author than writing a class that 
corresponds to a single name, and is painless to use for consumers.

-Original Message-
From: Domenic Denicola [mailto:d...@domenic.me] 
Sent: Friday, January 9, 2015 20:01
To: Anne van Kesteren; WebApps WG; www-...@w3.org
Subject: RE: Defining a constructor for Element and friends

OK, so I've thought about this a lot, and there was some discussion on an 
unfortunately-TC39-private thread that I want to put out in the open. In [1] I 
outlined some initial thoughts, but that was actually a thread on a different 
topic, and my thinking has evolved.
 
[1]: http://lists.w3.org/Archives/Public/public-webapps/2015JanMar/0035.html

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

One primary concern of mine is the one you mention:

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

I do not really think this is acceptable, and furthermore I think it is 
avoidable.

In the private thread Boris suggested a design where you can do `new 
Element(localName, namespace, prefix)`. This seems necessary to explain how 
`createElementNS` works, so we do want that. He also suggested the following 
invariants:

1.  The localName and namespace of an element determine its set of internal 
slots.
2.  The return value of `new Foo` has `Foo.prototype` as the prototype.

I agree we should preserve these invariants, but added a few more to do with 
keeping the existing (localName, namespace) - constructor links solid.

I've outlined the added invariants in the readme of the above repo. Other 
points of interest:

- Explainer for a very-recently-agreed-upon ES6 feature that helps support the 
design: 
https://github.com/domenic/element-constructors/blob/master/new-target-explainer.md
- Jump straight to the code: 
https://github.com/domenic/element-constructors/blob/master/element-constructors.js
- Jump straight to the examples of what works and what doesn't: 
https://github.com/domenic/element-constructors/blob/master/element-constructors.js#L194

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 but am playing it safe until 
someone with better understanding tells me one way or the other. See 
https://github.com/domenic/element-constructors/issues/1 for that discussion.

---

As for how this applies to custom elements, in the private thread Boris asked: 

 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).

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.

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.

The logical extension of this, then, is that if after that 
`document.registerElement` call I do `document.body.innerHTML = my-q 
cite=fooblah/my-q` I'd really like to see 
`document.querySelector(my-q).cite` return `foo`.

However this idea that we'd like custom elements which inherit from 

Re: Defining a constructor for Element and friends

2015-01-14 Thread Anne van Kesteren
On Wed, Jan 14, 2015 at 5:26 AM, Domenic Denicola d...@domenic.me wrote:
 How do you propose having a private constructor API?

Same as how we explain Window, Navigator, and friends, no? By
requiring that UAs pass in a magic token web developers cannot get a
hold of.


 This is one of those only makes sense to a C++ programmer things.

I thought we got past that reasoning a while ago.


-- 
https://annevankesteren.nl/



Re: Defining a constructor for Element and friends

2015-01-13 Thread Bjoern Hoehrmann
* Domenic Denicola wrote:
From: Bjoern Hoehrmann [mailto:derhoe...@gmx.net] 
 I know that this a major concern to you, but my impression is that few 
 if any other people regard that as anything more than nice to have, 
 especially if you equate explaining with having a public API for it.

How do you propose having a private constructor API?

How do you propose instances of the objects even existing at all, if
there is no constructor that creates them?

This is one of those only makes sense to a C++ programmer things.

I think it is misleading to describe something as a design goal if it
is not widely accepted as a design goal, and my impression is that this
is not widely accepted as a design goal. I also think it is entirely
normal to deal with objects you have no way of creating on your own.
-- 
Björn Höhrmann · mailto:bjo...@hoehrmann.de · http://bjoern.hoehrmann.de
D-10243 Berlin · PGP Pub. KeyID: 0xA4357E78 · http://www.bjoernsworld.de
 Available for hire in Berlin (early 2015)  · http://www.websitedev.de/ 



RE: Defining a constructor for Element and friends

2015-01-13 Thread Domenic Denicola
From: Bjoern Hoehrmann [mailto:derhoe...@gmx.net] 

 I know that this a major concern to you, but my impression is that few if any 
 other people regard that as anything more than nice to have, especially if 
 you equate explaining with having a public API for it.

How do you propose having a private constructor API?

How do you propose instances of the objects even existing at all, if there is 
no constructor that creates them?

This is one of those only makes sense to a C++ programmer things.



Re: Defining a constructor for Element and friends

2015-01-13 Thread Bjoern Hoehrmann
* Domenic Denicola wrote:
That kind of breaks the design goal that we be able to explain how 
everything you see in the DOM was constructed. How did the parser (or 
document.createElement(NS)) create a HTMLUnknownElement, if the 
constructor for HTMLUnknownElement doesn't work?

I know that this a major concern to you, but my impression is that few
if any other people regard that as anything more than nice to have,
especially if you equate explaining with having a public API for it.
-- 
Björn Höhrmann · mailto:bjo...@hoehrmann.de · http://bjoern.hoehrmann.de
D-10243 Berlin · PGP Pub. KeyID: 0xA4357E78 · http://www.bjoernsworld.de
 Available for hire in Berlin (early 2015)  · http://www.websitedev.de/ 



Re: Defining a constructor for Element and friends

2015-01-13 Thread Ryosuke Niwa

 On Jan 13, 2015, at 10:22 AM, Boris Zbarsky bzbar...@mit.edu wrote:
 
 On 1/13/15 1:18 PM, Ryosuke Niwa wrote:
 I agree. It's unusual for a constructor of a super class to automatically 
 instantiate an arbitrary subclass based on its arguments. And we usually 
 solve that convenience problem by introducing a factory class/function.
 
 While true, I do think there's a problem here.  Consider this:
 
  var element = new HTMLElement(somename);
 
 OK, so the web author is not being very forward-compatible in that they're 
 not using a tag name with a - in it.  But then they put it in the DOM and 
 it acts just like a span, and they're happy with that.

Shouldn't we throw in this case because the concert type of somename is 
HTMLUnknownElement?

 Then we want to add a somename tag in the spec, and suddenly this JS 
 throws.  This is a different order of breakage than what you get from just 
 having new semantics for the somename tag.
...
 In any case, it's a bit of a niggling worry for me because it can increase 
 the chance that adding things to HTML breaks websites.

I think if we threw an exception on every attempt to create an element with a 
name without - (as they're HTMLUnknownElement anyway), then we can probably 
mitigate this forward compatibility issue.

Hopefully, authors won't be creating HTMLUnknownElement all that often...

- R. Niwa


Re: Defining a constructor for Element and friends

2015-01-13 Thread Boris Zbarsky

On 1/13/15 12:05 PM, Domenic Denicola wrote:

From: Boris Zbarsky [mailto:bzbar...@mit.edu]

   var x = new Element(a, http://www.w3.org/1999/xhtml;)


The idea is that your above example throws, preserving the invariant.


That sounds annoying

Presumably this means that document.createElement(NS) looks up the 
registry constructor and calls it, instead of calling the Element 
constructor and having _that_ delegate to the registry.


But it also means that user-space code that has to create an HTML 
element generically now has to go through document.createElement instead 
of being able to do |new HTMLElement(a)|, right?


I agree that this does allow us to preserve the invariant in question, 
but I question whether it's worth it.



I was assuming non-exotic [[HasInstance]]


Ah, yeah, with Web IDL that's not a good assumption


but I agree it's ambiguous given that. I meant prototype chain. Probably I also 
implicitly meant internal slots.


Those aren't the same thing at all, right?  The prototype chain has 
absolutely nothing to do with internal slots, unless we're assuming some 
sort of vanilla untouched tate of the world.



The one piece of terminology that I think we have so far that I understand is what it 
means for an object to implement an interface.
At least Web IDL has a normative requirement on such a thing being defined 
(e.g. see http://heycam.github.io/webidl/#es-operations step 4 of the 
behavior), presumably in terms of some sort of branding.


Heh, I don't really understand what that means; I indeed noticed that Web IDL 
uses it without defining it.


Right now the concept of what it means for a platform object to 
implement an interface is sort of up to the implementation.  The Web 
IDL spec assumes that for each platform object there is a set of 
interfaces it implements, subject to some constraints described in 
http://heycam.github.io/webidl/#es-platform-objects, but even basic 
restrictions like if A inherits from B and the platform object 
implements A then it must implement B don't seem to be explicitly 
stated We should really fix that.



I too would guess that it's branding-related.


That's certainly how implementations treat it in practice.


Note that in any sensible scheme I can think of, subclass instances (for a 
subclass that calls super() in the constructor) also get the brands of their 
superclass.


Yes, absolutely.


So it makes sense to me to talk about things implementing Element but not any interface 
that has Element as an inherited interface.  That would correspond to is an Element 
but not any specific subclass of Element.  Could use a shorter way of saying it, 
for sure.


Don't think this attempt at pinning down terminology works for user-defined 
subclasses of Element. E.g. as far as I can tell, `new (class X extends Element 
{})()` has only the Element brand but no other brand (since X didn't install 
any brands itself). But I would say that it's an own-instance of X instead of 
an own-instance of Element.


OK.  That makes sense; I'm not trying to redefine the concept of 
own-instance.  I'm just trying to figure out what the right concepts 
are to define.


The concept of implements an interface has to do with branding and 
guaranteed existence of internal slots and whatnot.  The concept of has 
Element as its primary interface also promises something about the 
initial value of __proto__.


The concept of own-instance, if we define it as you did above (was 
created by) would also guarantee certain things about branding and 
internal slots.  It would also make some guarantees about the prototype, 
assuming no one sets __proto__ after that...


Really, this idea of primary interface and your idea of own-instance 
seem fairly similar, right?  Except that primary interface can only 
refer to Web IDL interfaces, not user-defined subclasses... or something.


-Boris



Re: Defining a constructor for Element and friends

2015-01-13 Thread Boris Zbarsky

On 1/13/15 1:18 PM, Ryosuke Niwa wrote:

I agree. It's unusual for a constructor of a super class to automatically 
instantiate an arbitrary subclass based on its arguments. And we usually solve 
that convenience problem by introducing a factory class/function.


While true, I do think there's a problem here.  Consider this:

  var element = new HTMLElement(somename);

OK, so the web author is not being very forward-compatible in that 
they're not using a tag name with a - in it.  But then they put it in 
the DOM and it acts just like a span, and they're happy with that.


Then we want to add a somename tag in the spec, and suddenly this JS 
throws.  This is a different order of breakage than what you get from 
just having new semantics for the somename tag.


In some cases, this is a problem no matter what; e.g. if somename is 
actually img or canvas then the layout is totally different too, not 
just the semantics.  But there are other cases where the layout is not 
that different from a vanilla inline though maybe we don't really 
plan to add any more of those?


In any case, it's a bit of a niggling worry for me because it can 
increase the chance that adding things to HTML breaks websites.


-Boris




RE: Defining a constructor for Element and friends

2015-01-13 Thread Domenic Denicola
From: Boris Zbarsky [mailto:bzbar...@mit.edu] 

 Terminology: In what follows I use 'own-instances of X' to mean objects 
 where obj.constructor === X,

 That doesn't make much sense to me as a useful test, since it's pretty simple 
 to produce, say, an HTMLParagraphElement instance on the web that has 
 whatever .constructor value you desire, right?  Unless the intent is to talk 
 about this in some setup where no one has messed with any of the objects or 
 something.

 I guess the intent here is that we want obj to have been constructed via X in 
 some sense?  Modulo whatever the story is for the things that have 
 NamedConstructors.

Right, I was being imprecise. I am not sure how to make it precise. Maybe 
something like was created via `new X` assuming `X` doesn't use 
return-override and we buy in to the story where all instances are created via 
constructors (even those originating from the browser instead of the author).

 Anyway, modulo exactly what this definition should be, let's talk about the 
 proposed the constructor of an element determines its set of internal slots 
 invariant.  I'm OK with that if we include constructor arguments.  
 Otherwise, I don't see how it can make sense.  In particular, say someone 
 does:

   var x = new Element(a, http://www.w3.org/1999/xhtml;)

 or whatever argument order we do.  Per invariant 1 in your document, this 
 should get the internal slots of an HTMLAnchorElement, right?  Per invariant 
 2, x.constructor == Element, and in particular x.__proto__ == 
 Element.prototype.  So suddenly we have an HTMLAnchorElement as an 
 own-instance of Element, which I think violates your invariant 3.

The idea is that your above example throws, preserving the invariant. Since 
there is already an entry in the registry for (a, HTML_NS), and it's not 
Element, the constructor fails. (Except when invoked as part of a super() chain 
from the actual HTMLAnchorElement constructor.) Details:

- 
https://github.com/domenic/element-constructors/blob/88dbec40494aefc03825e00ff1bfc8d5e3f02f1e/element-constructors.js#L62-L66
- 
https://github.com/domenic/element-constructors/blob/88dbec40494aefc03825e00ff1bfc8d5e3f02f1e/element-constructors.js#L211-L215

 Moving on to invariant 4, is that instances in terms on instanceof (which 
 can be almost tautologically true, given what Web IDL says about 
 [[HasInstance]] on interface objects), or in terms of what the proto chain 
 looks like, or something else?  In particular, the x defined above doesn't 
 have HTMLElement.prototype on its proto chain, but is instanceof 
 HTMLElement...

I was assuming non-exotic [[HasInstance]], but I agree it's ambiguous given 
that. I meant prototype chain. Probably I also implicitly meant internal slots.

 The one piece of terminology that I think we have so far that I understand is 
 what it means for an object to implement an interface. 
 At least Web IDL has a normative requirement on such a thing being defined 
 (e.g. see http://heycam.github.io/webidl/#es-operations step 4 of the 
 behavior), presumably in terms of some sort of branding.

Heh, I don't really understand what that means; I indeed noticed that Web IDL 
uses it without defining it.

I too would guess that it's branding-related. Note that in any sensible scheme 
I can think of, subclass instances (for a subclass that calls super() in the 
constructor) also get the brands of their superclass.

 So it makes sense to me to talk about things implementing Element but not any 
 interface that has Element as an inherited interface.  That would correspond 
 to is an Element but not any specific subclass of Element.  Could use a 
 shorter way of saying it, for sure.

Don't think this attempt at pinning down terminology works for user-defined 
subclasses of Element. E.g. as far as I can tell, `new (class X extends Element 
{})()` has only the Element brand but no other brand (since X didn't install 
any brands itself). But I would say that it's an own-instance of X instead of 
an own-instance of Element.


RE: Defining a constructor for Element and friends

2015-01-13 Thread Domenic Denicola
From: Boris Zbarsky [mailto:bzbar...@mit.edu] 

 But it also means that user-space code that has to create an HTML element 
 generically now has to go through document.createElement instead of being 
 able to do |new HTMLElement(a)|, right?

That seems totally fine to me though. The idea of a string-based factory for 
when you don't know what constructor you want to use has precedent all over 
software design.

 Those aren't the same thing at all, right?  The prototype chain has 
 absolutely nothing to do with internal slots, unless we're assuming some sort 
 of vanilla untouched tate of the world.

Agreed. However, in a normal situation---where all constructors in the chain 
call super() appropriately, and nobody __proto__-munges, and so on---they 
should be the same. That's why I'm saying that implicitly it was probably also 
part of what I was thinking when writing that.

 Really, this idea of primary interface and your idea of own-instance seem 
 fairly similar, right?  Except that primary interface can only refer to Web 
 IDL interfaces, not user-defined subclasses... or something.

Yeah, that sounds about right. Honestly, own-interface was just my attempt at 
capturing a JavaScript concept that I work with pretty often (this over here 
is a Foo; this over here is a Bar).



Re: Defining a constructor for Element and friends

2015-01-13 Thread Ryosuke Niwa

 On Jan 13, 2015, at 9:25 AM, Domenic Denicola d...@domenic.me wrote:
 
 From: Boris Zbarsky [mailto:bzbar...@mit.edu] 
 
 But it also means that user-space code that has to create an HTML element 
 generically now has to go through document.createElement instead of being 
 able to do |new HTMLElement(a)|, right?
 
 That seems totally fine to me though. The idea of a string-based factory for 
 when you don't know what constructor you want to use has precedent all over 
 software design.

I agree. It's unusual for a constructor of a super class to automatically 
instantiate an arbitrary subclass based on its arguments. And we usually solve 
that convenience problem by introducing a factory class/function.

- R. Niwa


Re: Defining a constructor for Element and friends

2015-01-13 Thread Ryosuke Niwa

 On Jan 13, 2015, at 8:26 PM, Domenic Denicola d...@domenic.me wrote:
 
 From: Bjoern Hoehrmann [mailto:derhoe...@gmx.net] 
 
 I know that this a major concern to you, but my impression is that few if 
 any other people regard that as anything more than nice to have, 
 especially if you equate explaining with having a public API for it.
 
 How do you propose having a private constructor API?

I don't think we need to make the constructor of HTMLUnknownElement private.  
It certainly isn't today.  We just need to throw whenever it's called.

 How do you propose instances of the objects even existing at all, if there is 
 no constructor that creates them?
 
 This is one of those only makes sense to a C++ programmer things.

We can model it as a constructor that takes a private symbol only the DOM 
implementation has access to, and throws whenever this symbol is not passed in 
as an argument.

- R. Niwa


RE: Defining a constructor for Element and friends

2015-01-13 Thread Domenic Denicola
From: Ryosuke Niwa [mailto:rn...@apple.com] 

 Shouldn't we throw in this case because the concert type of somename is 
 HTMLUnknownElement?

Yes, that's exactly the current design. Hidden a bit:

https://github.com/domenic/element-constructors/blob/master/element-constructors.js#L4

This still leaves the potential hazard of someone doing `new 
HTMLUnknownElement(somename)` and their code breaking later once someone 
becomes a real tag... hopefully the Unknown is a bit more of a deterrent 
though?

(It'd be nice if HTMLElement weren't a global and you had to do `import 
HTMLElement from html/parser-internals` or something. Ah well.)



Re: Defining a constructor for Element and friends

2015-01-13 Thread Ryosuke Niwa

On Jan 13, 2015, at 11:31 AM, Boris Zbarsky bzbar...@mit.edu wrote:

 On 1/13/15 1:33 PM, Ryosuke Niwa wrote:
 Shouldn't we throw in this case because the concert type of somename is 
 HTMLUnknownElement?
 
 Oh, hmm.
 
 Yes, I guess so.  That's very non-obvious to an author.
 
 Even less obvious because for some tag names using the HTMLElement 
 constructor is in fact correct.
 
 The end result here is really something that ends up all self-consistent and 
 preserving our invariants and the obvious reaction to it will be WAT?.  Not 
 least because the actual interface used by various HTML elements is pretty 
 random.
 
 Want a basefont?  HTMLElement.  Want a bgsound?  HTMLUnknownElement.  
 Want a rb?  HTMLUnknownElement.  Want a big?  HTMLElement…

Indeed the developer ergonomics here is terrible :(

 I think if we threw an exception on every attempt to create an element with 
 a name without - (as they're HTMLUnknownElement anyway)
 
 I'm not sure I follow.  How do you propose an s be constructed via 
 constructor, say?  What about address?

Sorry, I meant that only non-standard (i.e. unknown) HTML elements.  If we're 
disallowing upgrades as we've been arguing, then we should probably disallow 
HTMLUnknownElements altogether regardless of whether they contain - in their 
names or not.

- R. Niwa




Re: Defining a constructor for Element and friends

2015-01-13 Thread Boris Zbarsky

On 1/13/15 1:33 PM, Ryosuke Niwa wrote:

Shouldn't we throw in this case because the concert type of somename is 
HTMLUnknownElement?


Oh, hmm.

Yes, I guess so.  That's very non-obvious to an author.

Even less obvious because for some tag names using the HTMLElement 
constructor is in fact correct.


The end result here is really something that ends up all self-consistent 
and preserving our invariants and the obvious reaction to it will be 
WAT?.  Not least because the actual interface used by various HTML 
elements is pretty random.


Want a basefont?  HTMLElement.  Want a bgsound?  HTMLUnknownElement. 
 Want a rb?  HTMLUnknownElement.  Want a big?  HTMLElement...



I think if we threw an exception on every attempt to create an element with a name 
without - (as they're HTMLUnknownElement anyway)


I'm not sure I follow.  How do you propose an s be constructed via 
constructor, say?  What about address?


-Boris



Re: Defining a constructor for Element and friends

2015-01-13 Thread Ryosuke Niwa

On Jan 13, 2015, at 10:45 AM, Domenic Denicola d...@domenic.me wrote:

 From: Ryosuke Niwa [mailto:rn...@apple.com] 
 
 Shouldn't we throw in this case because the concert type of somename is 
 HTMLUnknownElement?
 
 Yes, that's exactly the current design. Hidden a bit:
 
 https://github.com/domenic/element-constructors/blob/master/element-constructors.js#L4
 
 This still leaves the potential hazard of someone doing `new 
 HTMLUnknownElement(somename)` and their code breaking later once someone 
 becomes a real tag... hopefully the Unknown is a bit more of a deterrent 
 though?

Or, we could always throw an exception in the constructor of HTMLUnknownElement 
so that nobody could do it.  It would mean that libraries and frameworks that 
do support custom elements without - would have to use document.createElement 
but that might be a good thing since they wouldn't be doing that in the first 
place.

- R. Niwa




RE: Defining a constructor for Element and friends

2015-01-13 Thread Domenic Denicola
From: Ryosuke Niwa [mailto:rn...@apple.com] 

 Or, we could always throw an exception in the constructor of 
 HTMLUnknownElement so that nobody could do it.  It would mean that libraries 
 and frameworks that do support custom elements without - would have to use 
 document.createElement but that might be a good thing since they wouldn't be 
 doing that in the first place.

That kind of breaks the design goal that we be able to explain how everything 
you see in the DOM was constructed. How did the parser (or 
document.createElement(NS)) create a HTMLUnknownElement, if the constructor for 
HTMLUnknownElement doesn't work?



Re: Defining a constructor for Element and friends

2015-01-13 Thread Ryosuke Niwa
On Jan 13, 2015, at 10:53 AM, Domenic Denicola d...@domenic.me wrote:

 From: Ryosuke Niwa [mailto:rn...@apple.com] 
 
 Or, we could always throw an exception in the constructor of 
 HTMLUnknownElement so that nobody could do it.  It would mean that libraries 
 and frameworks that do support custom elements without - would have to use 
 document.createElement but that might be a good thing since they wouldn't be 
 doing that in the first place.
 
 That kind of breaks the design goal that we be able to explain how everything 
 you see in the DOM was constructed. How did the parser (or 
 document.createElement(NS)) create a HTMLUnknownElement, if the constructor 
 for HTMLUnknownElement doesn't work?


I didn't know that we had such a design goal.  In general, backwards and 
forwards compatibilities are much more important than design purity.

- R. Niwa




Re: Defining a constructor for Element and friends

2015-01-12 Thread Boris Zbarsky

On 1/11/15 2:33 PM, Domenic Denicola wrote:

Terminology: In what follows I use 'own-instances of X' to mean objects where 
obj.constructor === X,


That doesn't make much sense to me as a useful test, since it's pretty 
simple to produce, say, an HTMLParagraphElement instance on the web that 
has whatever .constructor value you desire, right?  Unless the intent is 
to talk about this in some setup where no one has messed with any of the 
objects or something.


I guess the intent here is that we want obj to have been constructed via 
X in some sense?  Modulo whatever the story is for the things that have 
NamedConstructors.



as distance from 'instances of X' which means objects for which obj instanceof 
X.


OK.

Anyway, modulo exactly what this definition should be, let's talk about 
the proposed the constructor of an element determines its set of 
internal slots invariant.  I'm OK with that if we include constructor 
arguments.  Otherwise, I don't see how it can make sense.  In 
particular, say someone does:


  var x = new Element(a, http://www.w3.org/1999/xhtml;)

or whatever argument order we do.  Per invariant 1 in your document, 
this should get the internal slots of an HTMLAnchorElement, right?  Per 
invariant 2, x.constructor == Element, and in particular x.__proto__ == 
Element.prototype.  So suddenly we have an HTMLAnchorElement as an 
own-instance of Element, which I think violates your invariant 3.


Moving on to invariant 4, is that instances in terms on instanceof 
(which can be almost tautologically true, given what Web IDL says about 
[[HasInstance]] on interface objects), or in terms of what the proto 
chain looks like, or something else?  In particular, the x defined 
above doesn't have HTMLElement.prototype on its proto chain, but is 
instanceof HTMLElement...



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


I'm just quoting Anne :). My interpretation is that the (object representing 
the) element is an own-instance of Element.


This _really_ requires pinning down what exactly own-instance means. 
Let's get our terminology in order.  ;)


The one piece of terminology that I think we have so far that I 
understand is what it means for an object to implement an interface. 
At least Web IDL has a normative requirement on such a thing being 
defined (e.g. see http://heycam.github.io/webidl/#es-operations step 4 
of the behavior), presumably in terms of some sort of branding.


So it makes sense to me to talk about things implementing Element but 
not any interface that has Element as an inherited interface.  That 
would correspond to is an Element but not any specific subclass of 
Element.  Could use a shorter way of saying it, for sure.


-Boris



RE: Defining a constructor for Element and friends

2015-01-11 Thread Domenic Denicola
From: Boris Zbarsky [mailto:bzbar...@mit.edu] 

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

Explained at the top:

Terminology: In what follows I use 'own-instances of X' to mean objects where 
obj.constructor === X, as distance from 'instances of X' which means objects 
for which obj instanceof X.

 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.

I'm just quoting Anne :). My interpretation is that the (object representing 
the) element is an own-instance of Element.

  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?

Ah, right, of course. I am skipping a few steps and my steps are wrong, so my 
concern wasn't well-founded. My vision was that MyImg had local name my-img via 
some custom element stuff, as with the my-q example below. I agree that it 
works though as stated.

 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.)

Right, so I should have actually written `class MyQ extends HTMLQuoteElement { 
constructor(document) { super(q, document); } }`. My repo's .js file covers 
the case of HTMLQuoteElement specifically to illustrate how to deal with 
classes that cover more than one local name.

And yeah, I agree it works again.

The other stuff, which is really about custom elements, I'll spin off into a 
new thread.


Re: Defining a constructor for Element and friends

2015-01-09 Thread Boris Zbarsky

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=fooblah/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 

RE: Defining a constructor for Element and friends

2015-01-09 Thread Domenic Denicola
OK, so I've thought about this a lot, and there was some discussion on an 
unfortunately-TC39-private thread that I want to put out in the open. In [1] I 
outlined some initial thoughts, but that was actually a thread on a different 
topic, and my thinking has evolved.
 
[1]: http://lists.w3.org/Archives/Public/public-webapps/2015JanMar/0035.html

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

One primary concern of mine is the one you mention:

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

I do not really think this is acceptable, and furthermore I think it is 
avoidable.

In the private thread Boris suggested a design where you can do `new 
Element(localName, namespace, prefix)`. This seems necessary to explain how 
`createElementNS` works, so we do want that. He also suggested the following 
invariants:

1.  The localName and namespace of an element determine its set of internal 
slots.
2.  The return value of `new Foo` has `Foo.prototype` as the prototype.

I agree we should preserve these invariants, but added a few more to do with 
keeping the existing (localName, namespace) - constructor links solid.

I've outlined the added invariants in the readme of the above repo. Other 
points of interest:

- Explainer for a very-recently-agreed-upon ES6 feature that helps support the 
design: 
https://github.com/domenic/element-constructors/blob/master/new-target-explainer.md
- Jump straight to the code: 
https://github.com/domenic/element-constructors/blob/master/element-constructors.js
- Jump straight to the examples of what works and what doesn't: 
https://github.com/domenic/element-constructors/blob/master/element-constructors.js#L194

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 but am playing it safe until 
someone with better understanding tells me one way or the other. See 
https://github.com/domenic/element-constructors/issues/1 for that discussion.

---

As for how this applies to custom elements, in the private thread Boris asked: 

 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).

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.

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.

The logical extension of this, then, is that if after that 
`document.registerElement` call I do `document.body.innerHTML = my-q 
cite=fooblah/my-q` I'd really like to see 
`document.querySelector(my-q).cite` return `foo`.

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, 
which seems quite hard to work around. So maybe it is not a good idea? On the 
other hand upgrading might be borked no matter what, i.e. it might not be 
possible at all to make upgraded elements behave anything like 
parsed-from-scratch elements. (I am planning to think harder about the 
upgrading problem but I am not hopeful.)



Re: Defining a constructor for Element and friends

2015-01-07 Thread Boris Zbarsky

On 1/7/15 6:17 AM, Anne van Kesteren wrote:

The main tricky thing here I think is whether it is acceptable to have
an element whose name is a, namespace is the HTML namespace, and
interface is Element.


That depends on what you mean by interface is Element.

If you mean that it has all the internal slots HTMLAnchorElement has but 
its prototype is Element.prototype, I think that may be fine.  Libraries 
might get confused if you pass them elements like this, but that just 
comes down to don't create elements like this as a guideline, right?


If you mean not having the internal slots HTMLAnchorElement has, then 
that would involve a good bit of both specification and implementation 
work.  Specifically:


1)  Pretty much the entire HTML spec is written in terms of tag names, 
and the operations it performs often assume some sort of state being 
stored on elements with those tag names.  Conceptually this is being 
stored in internal slots (though of course in actual implementations 
slots can mean hashtable entries in some hashtable or whatnot). 
Significant spec work would need to happen to deal with situations where 
the element has some tagname but not the corresponding internal slots.


2)  In specifications, there are assumptions about particular tag names 
having particular internal slots.  For example, you often get code like 
this (not actual code in either one, but intended to get the flavor 
across) at least in WebKit and Gecko:


  void doWhatever(Element* element) {
if (element-isHTML()  element-tagName() == input) {
  HTMLInputElement* input = static_castHTMLInputElement*(element);
  // Do stuff with input here.
}
  }

If we can have HTML elements which have the input tag name but aren't 
represented by a subclass of the C++ HTMLInputElement in the above code, 
you get a security bug.  So codebases would have to be audited for all 
instances of this and similar patterns.  I just did a quick check in 
Gecko, and we're looking at at least 500 callsites just in C++.  There 
are probably more in privileged JavaScript that make assumptions about 
things based on tagname...


This is why the custom elements spec ended up with the is=... business 
for extending nontrivial HTML elements.  :(


So just to check, which of these two invariant-violations are we talking 
about here?



If we can break that invariant it seems rather easy to build the
hierarchy. The HTMLElement constructor would only take a local name
and always have a null prefix and HTML namespace.


I think that's fine in a world where we still create an 
HTMLAnchorElement under the hood if you do new HTMLElement('a') but 
just give it HTMLElement.prototype as the proto.



And HTMLAnchorElement would always be a. HTMLQuoteElement could accept
an enum and we could even add dedicated constructors for q and
blockquote (provided the web allows).


Yeah, this would make sense to me.

-Boris



Re: Defining a constructor for Element and friends

2015-01-07 Thread Anne van Kesteren
On Wed, Jan 7, 2015 at 2:32 PM, Boris Zbarsky bzbar...@mit.edu wrote:
 If you mean not having the internal slots HTMLAnchorElement has, then that
 would involve a good bit of both specification and implementation work.

That is what I meant. Otherwise in order to support new Element()
you'd have to support an ever growing set of more specific objects as
well and layering is out of the window.

However, that does indeed seem like a lot of work and it's not clear
whether that actually pays off in the end :-(


-- 
https://annevankesteren.nl/



Re: Defining a constructor for Element and friends

2015-01-07 Thread Boris Zbarsky

On 1/7/15 9:51 AM, Anne van Kesteren wrote:

That is what I meant. Otherwise in order to support new Element()
you'd have to support an ever growing set of more specific objects as
well and layering is out of the window.


Do you mean layering of implementations or specifications?  For 
specifications, here's one way this could work with reasonable layering. 
 DOM provides the following bits:


1)  A registry mapping (namespace, localname) pairs to abstract 
operations that allocate an object.


2)  Abstract operations that can be used by specifications built on top 
of DOM to register abstract operations in this registry.


3)  An abstract operation that takes a list of internal slot names and 
returns an object which has those internal slots, plus the internal 
slots all elements have, plus probably the ordinary object internal 
slots from ES6, depending on whether Web IDL assumes these are ordinary 
objects.  I thought ES6 had this sort of abstract operation already, but 
I don't see anything like it; in any case the only issue here is that 
this requires implementations of DOM and specifications built on top of 
it to agree internally on what internal slot means for elements, I agree.


Specifications that define elements on top of DOM provide the following 
bits:


4)  An abstract operation that creates an uninitialized version of their 
element, via calling the thing defined in #3.


5)  Registration of the abstract operation defined in #4 with the 
registry defined in #1, whether that happens at global-setup time or 
when the element definition is encountered or whatever.


An implementation that wants to just implement core DOM but not things 
built on on top of it can skip all of this machinery.  An implementation 
that wants to support DOM but not everything on top of it (e.g. support 
MathML but not HTML or something) just supports the bits it wants and 
the registry ends up not having stuff in it that it has in other 
implementations.  Seems ok to me.


Now some questions:

* Did I cover your concern about have to support an ever growing set of 
more specific objects?  If not, can you explain what the concern is?


* Without a mechanism like the above, how would one go about supporting 
document.createElement(NS) as it exists on the web.



However, that does indeed seem like a lot of work and it's not clear
whether that actually pays off in the end :-(


That's hard to say without knowing what the desired payoff is.

-Boris