Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite Custom Element Construction Algorithm #403

Closed
rniwa opened this issue Mar 1, 2016 · 4 comments · Fixed by #405
Closed

Rewrite Custom Element Construction Algorithm #403

rniwa opened this issue Mar 1, 2016 · 4 comments · Fixed by #405

Comments

@rniwa
Copy link
Collaborator

rniwa commented Mar 1, 2016

Here are steps to construct a custom element as agreed during Jan F2F as I promised to write down [1] [2]:

Modify http://w3c.github.io/webcomponents/spec/custom/#dfn-element-definition as follows:
The element definition describes a custom element and consists of:

  • custom element type,
  • local name,
  • namespace,
  • custom element interface,
  • lifecycle callbacks, and
  • element construction stack.

Each element construction stack is initially empty, and each entry is an instance of Element or a "AlreadyConstructed" marker.

Non-Normative Note: We need a stack per element definition to allow construction of other custom elements inside a custom element's constructor. Without such a stack per element definition, we would end up walking through entries in the stack to find the "right" entry. Implementors are free to take such an approach to minimize the memory usage, etc..., but there are a lot of edge cases that need to be taken care of, and it's not a great way to spec an interoperable behavior.

Custom Element Construction Algorithm

Input

  • NAME, the custom element name.
  • DOCUMENT, the owner document for new custom element.
  • EXOTIC-TARGET, the target Element to be constructed / upgraded.

Output

  • ELEMENT, new custom element instance.
  • ERROR, could be either "None", "NotFound", "InvalidStateError", or an ECMAScript exception.
  1. Let ERROR be "None".
  2. Let REGISTRY be the (custom element) registry of DOCUMENT.
  3. If DOCUMENT is an HTML document, let NAME be converted to ASCII lowercase.
  4. Let DEFINITION be the element definition of with the local name, NAME, in REGISTRY.
  5. If there is no matching definition, set ERROR to "NotFound" and terminate these steps.
  6. Otherwise, push a new entry, EXOTIC-TARGET, to the element construction stack of DEFINITION.
  7. Invoke the [[Construct]] internal method [3] on custom element interface of DEFINITION.
  8. Pop the entry from the element construction stack of DEFINITION.
  9. If the [[Construct]] invocation resulted in an exception, set ERROR to the raised exception, and terminate these steps.
  10. Otherwise, let ELEMENT be the result of the invocation.
  11. If ELEMENT is not the same Object value as EXOTIC-TARGET, set ERROR to "InvalidStateError", and terminate these steps.

Non-Normative Note: we can modify step 4 to support non-HTML elements in the future. In step 11, ELEMENT can be different from EXOTIC-TARGET if the custom element's constructor instantiates another instance of the same custom element before calling super().

HTMLElement constructor

Non-Normative Note: HTMLElement's constructor is called via super() call inside the custom element constructor.

  1. Let TARGET be GetNewTarget(). [4]
  2. Let DOCUMENT be the associated document of the global object (the result of GetGlobalObject() [5]).
  3. Let REGISTRY be the (custom element) registry of DOCUMENT.
  4. Let DEFINITION be the element definition with the element interface, TARGET, in REGISTRY.
  5. If there is no matching definition, throw TypeError and terminate these steps.
  6. Let NAME be the local name of DEFINITION.
  7. If the element construction stack of DEFINITION is empty,
    1. Return a new element that implements HTMLElement, with no attributes, namespace set to the HTML namespace,
      local name set to NAME, and node document set to DOCUMENT.
  8. Otherwise, let INSTANCE be the last entry in the element construction stack (i.e. in LIFO).
  9. If INSTANCE is a "AlreadyConstructed" marker, throw InvalidStateError and terminate these steps.
  10. Otherwise, replace the last entry in the element construction stack with a "AlreadyConstructed" marker.
  11. Return INSTANCE.

Non-Normative Note: step 7.1. is like step 4 in createElement [6] and happens when author script instantiates a custom element without going through DOM. e.g. "new X". Checks in Step 9 and 10 are needed when author scripts constructs invokes super() multiple times inside a custom element constructor. Step 9 is sufficient for the Custom Element Construction Algorithm to fail because it checks the exception in step 9. Step 5 could throw NotSupportedError instead if people would prefer that.

[1] https://github.com/w3c/WebPlatformWG/blob/gh-pages/meetings/25janWC.md
[2] https://www.w3.org/2016/01/25-webapps-minutes.html
[3] http://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-construct-argumentslist-newtarget
[4] http://www.ecma-international.org/ecma-262/6.0/#sec-getnewtarget
[5] http://www.ecma-international.org/ecma-262/6.0/#sec-getglobalobject
[6] https://dom.spec.whatwg.org/#dom-document-createelement

@rniwa
Copy link
Collaborator Author

rniwa commented Mar 1, 2016

Also see #366.

@domenic
Copy link
Collaborator

domenic commented Mar 1, 2016

This overall LGTM. Thanks a lot for doing the work. Minor comments below:


custom element interface,

Nit: I'd make this "custom element constructor".

ERROR, could be either "None", "NotFound", "InvalidStateError", or an ECMAScript exception.

We'll need to remember to handle these while parsing.

Invoke the [[Construct]] internal method [3] on custom element interface of DEFINITION.

This isn't quite right. You want to do Construct(custom element constructor), using https://tc39.github.io/ecma262/#sec-construct

Let TARGET be GetNewTarget(). [4]

You can't use GetNewTarget() inside engine code I believe. The ES spec always instead just references NewTarget, e.g. https://tc39.github.io/ecma262/#sec-object-value

Same for GetGlobalObject(). You'll need to use entry settings object's global object or something like that.

local name set to NAME

set to DEFINITION's name, presumably


I'd like to take point on incorporating this into the spec since I can do the minor tweaks necessary to make this conform to ES style (which I think is probably necessary for such low-level operations). I'll work on a PR.

@rniwa
Copy link
Collaborator Author

rniwa commented Mar 1, 2016

All those nits & comments make sense to me. It would be great if you can make a PR since that'll guarantee that at least three people look at this stuff. Preferably someone from Mozilla or Microsoft like @bzbarsky, @annevk, & @travisleithead should review your final PR request.

@domenic
Copy link
Collaborator

domenic commented Mar 1, 2016

Yeah I am in the middle of working on the PR. It ended up being really big :).

domenic added a commit that referenced this issue Mar 1, 2016
Notable changes:

- Implemented HTMLElement constructor using @rniwa's algorithm from #403.
- Rewrote element upgrading to use @rniwa's algorithm from #403, and incorporated it into the rest of the upgrading considerations.
- Got rid of the ability to extend SVGElement, thus allowing us to remove most mentions of namespaces from the spec.
- Removed createdCallback.
- Rewrote "registering elements":
  - Uses defineElement instead of registerElement
  - Preserves the constructor instead of grabbing the .prototype property and synthesizing a new constructor
  - No longer spread out over four separate algorithms plus registerElement; everything is now inline under defineElement
  - More precise about exactly how to get the custom element prototype and callbacks
- Rewrote createElement and createElementNS to be replacements instead of patches, and to call the author-supplied constructor.
- Removed the "All Algorithms in One Diagram" section since so many algorithms changed or were inlined into their callers.
- Removed the "Custom Elements and ECMAScript 6" section since it is very obsolete and does not reflect our current thinking.
- New and rewritten algorithms do not use the unorthodox INPUTS/OUTPUTS blocks, or capitalized variable names. This is kind of a nice marker of new vs. old content.

Notable things *not* substantially changed:

- Parser changes are not specced still, besides to say that they should construct the element using its constructor.
- Lifecycle callbacks were not changed, except for removing createdCallback.
- Type extensions were not removed (yet?); it seems better to have a modernized version of them that we atomically remove.
- Registries were not made available everywhere.

Closes #403. Closes #365. Closes #283. Closes #185. Closes #170. Closes #169. Closes #167. Closes #163. Closes #162. Closes #161. Closes #158. Clsoes #137 (modulo the fact that #165 is still open). Closes #134. Closes #133.
domenic added a commit that referenced this issue Mar 1, 2016
Notable changes:

- Implemented HTMLElement constructor using @rniwa's algorithm from #403.
- Rewrote element upgrading to use @rniwa's algorithm from #403, and incorporated it into the rest of the upgrading considerations.
- Got rid of the ability to extend SVGElement, thus allowing us to remove most mentions of namespaces from the spec.
- Removed createdCallback.
- Rewrote "registering elements":
  - Uses defineElement instead of registerElement
  - Preserves the constructor instead of grabbing the .prototype property and synthesizing a new constructor
  - No longer spread out over four separate algorithms plus registerElement; everything is now inline under defineElement
  - More precise about exactly how to get the custom element prototype and callbacks
- Rewrote createElement and createElementNS to be replacements instead of patches, and to call the author-supplied constructor.
- Removed the "All Algorithms in One Diagram" section since so many algorithms changed or were inlined into their callers.
- Removed the "Custom Elements and ECMAScript 6" section since it is very obsolete and does not reflect our current thinking.
- New and rewritten algorithms do not use the unorthodox INPUTS/OUTPUTS blocks, or capitalized variable names. This is kind of a nice marker of new vs. old content.

Notable things *not* substantially changed:

- Parser changes are not specced still, besides to say that they should construct the element using its constructor.
- Lifecycle callbacks were not changed, except for removing createdCallback.
- Type extensions were not removed (yet?); it seems better to have a modernized version of them that we atomically remove.
- Registries were not made available everywhere.

Closes #403. Closes #365. Closes #283. Closes #185. Closes #170. Closes #169. Closes #167. Closes #163. Closes #162. Closes #161. Closes #158. Clsoes #137 (modulo the fact that #165 is still open). Closes #134. Closes #133.
domenic added a commit that referenced this issue Mar 1, 2016
Notable changes:

- Implemented HTMLElement constructor using @rniwa's algorithm from #403.
- Rewrote element upgrading to use @rniwa's algorithm from #403, and incorporated it into the rest of the upgrading considerations.
- Got rid of the ability to extend SVGElement, thus allowing us to remove most mentions of namespaces from the spec.
- Removed createdCallback.
- Rewrote "registering elements":
  - Uses defineElement instead of registerElement
  - Preserves the constructor instead of grabbing the .prototype property and synthesizing a new constructor
  - No longer spread out over four separate algorithms plus registerElement; everything is now inline under defineElement
  - More precise about exactly how to get the custom element prototype and callbacks
- Rewrote createElement and createElementNS to be replacements instead of patches, and to call the author-supplied constructor.
- Removed the "All Algorithms in One Diagram" section since so many algorithms changed or were inlined into their callers.
- Removed the "Custom Elements and ECMAScript 6" section since it is very obsolete and does not reflect our current thinking.
- New and rewritten algorithms do not use the unorthodox INPUTS/OUTPUTS blocks, or capitalized variable names. This is kind of a nice marker of new vs. old content.

Notable things *not* substantially changed:

- Parser changes are not specced still, besides to say that they should construct the element using its constructor.
- Lifecycle callbacks were not changed, except for removing createdCallback.
- Type extensions were not removed (yet?); it seems better to have a modernized version of them that we atomically remove.
- Registries were not made available everywhere.

Closes #403. Closes #365. Closes #283. Closes #185. Closes #170. Closes #169. Closes #167. Closes #163. Closes #162. Closes #161. Closes #158. Closes #137 (modulo the fact that #165 is still open). Closes #134. Closes #133.
domenic added a commit that referenced this issue Mar 1, 2016
Notable changes:

- Implemented HTMLElement constructor using @rniwa's algorithm from #403.
- Rewrote element upgrading to use @rniwa's algorithm from #403, and incorporated it into the rest of the upgrading considerations.
- Got rid of the ability to extend SVGElement, thus allowing us to remove most mentions of namespaces from the spec.
- Removed createdCallback.
- Rewrote "registering elements":
  - Uses defineElement instead of registerElement
  - Preserves the constructor instead of grabbing the .prototype property and synthesizing a new constructor
  - No longer spread out over four separate algorithms plus registerElement; everything is now inline under defineElement
  - More precise about exactly how to get the custom element prototype and callbacks
- Rewrote createElement and createElementNS to be replacements instead of patches, and to call the author-supplied constructor.
- Removed the "All Algorithms in One Diagram" section since so many algorithms changed or were inlined into their callers.
- Removed the "Custom Elements and ECMAScript 6" section since it is very obsolete and does not reflect our current thinking.
- New and rewritten algorithms do not use the unorthodox INPUTS/OUTPUTS blocks, or capitalized variable names. This is kind of a nice marker of new vs. old content.

Notable things *not* substantially changed:

- Parser changes are not specced still, besides to say that they should construct the element using its constructor.
- Lifecycle callbacks were not changed, except for removing createdCallback.
- Type extensions were not removed (yet?); it seems better to have a modernized version of them that we atomically remove.
- Registries were not made available everywhere.

Closes #403. Closes #365. Closes #283. Closes #185. Closes #170. Closes #169. Closes #167. Closes #163. Closes #162. Closes #161. Closes #158. Closes #137 (modulo the fact that #165 is still open). Closes #134. Closes #133.
domenic added a commit that referenced this issue Mar 3, 2016
Notable changes:

- Implemented HTMLElement constructor using @rniwa's algorithm from #403.
- Rewrote element upgrading to use @rniwa's algorithm from #403, and incorporated it into the rest of the upgrading considerations.
- Got rid of the ability to extend SVGElement, thus allowing us to remove most mentions of namespaces from the spec.
- Removed createdCallback.
- Rewrote "registering elements":
  - Uses defineElement instead of registerElement
  - Preserves the constructor instead of grabbing the .prototype property and synthesizing a new constructor
  - No longer spread out over four separate algorithms plus registerElement; everything is now inline under defineElement
  - More precise about exactly how to get the custom element prototype and callbacks
- Rewrote createElement and createElementNS to be replacements instead of patches, and to call the author-supplied constructor.
- Removed the "All Algorithms in One Diagram" section since so many algorithms changed or were inlined into their callers.
- Removed the "Custom Elements and ECMAScript 6" section since it is very obsolete and does not reflect our current thinking.
- New and rewritten algorithms do not use the unorthodox INPUTS/OUTPUTS blocks, or capitalized variable names. This is kind of a nice marker of new vs. old content.
- I have taken over as spec editor for custom elements

Notable things *not* substantially changed:

- Parser changes are not specced still, besides to say that they should construct the element using its constructor.
- Lifecycle callbacks were not changed, except for removing createdCallback.
- Type extensions were not removed (yet?); it seems better to have a modernized version of them that we atomically remove.
- Registries were not made available everywhere.

Closes #403. Closes #365. Closes #283. Closes #185. Closes #170. Closes #169. Closes #167. Closes #163. Closes #162. Closes #161. Closes #158. Closes #137 (modulo the fact that #165 is still open). Closes #134. Closes #133.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants