-
Notifications
You must be signed in to change notification settings - Fork 391
Description
follow up from #895
This issue has been discussed briefly multiple times (#716, #865 (comment)), without a clear consensus. We could not get to it during the recent F2F meeting, and instead we decided to try to resolved via an issue.
After reviewing the current state of the spec, and current implementations, I now believe that what @rniwa was saying was correct (not a surprise). Scoped Custom Element Registries are fundamentally incompatible with moving custom element instances between documents. Here is my take on this:
Current state of affairs
-
Moving custom element instances between documents is not very common, but still possible. After creating a custom element somehow, in another document, the element can be moved into a new document without too much hazard (note: it is not hazard free, but the risk is pretty small).
-
Moving custom element declarations between documents is not disallowed, but the creation of instances via document.createElement will throw, and creation of those via
newis not different from point 1. -
Instances moved are hazardous because they could incur on invalid operations, e.g.: an instance that when connected, attempt to populate its shadow root (which is a common operation) where elements used by the shadow are not registered in the destination document (new owner document).
-
Invariant: Any element created from markup or
document.createElementmust be registered in the document itself, and must produce an instance of the corresponding HTMLElement.
The problem with Scoped Registry
When an instance is moved (see 3), and any element created from markup or shadowRoot.createElement, from that point on, must produce an instance of the ownerDocument's corresponding HTMLElement. This is the fundamental problem, because that registry was most likely created and populated by the declaration, or instantiation of the custom element. E.g. of such broken code:
const registry = new CustomElementRegistry();
registry.define('x-child', SomeConstructor);
export class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open', registry });
}
connectedCallback() {
this.shadowRoot.innerHTML = '<x-child></x-child>'
}
}Proposed solution
This proposed solution assumes that moving custom elements with scoped registries is going to be a very very rare situation:
A. Use the same protection mechanism used today by the global registry. Fail with A newly constructed custom element belongs to a wrong document (Safari) or a similar error when needed.
B. Allow a new registry to be set into an existing shadow root (a setter on ShadowRoot.prototype.registry), that way the authors expecting elements to be moved between documents can do the extra work of redefining the registry for their necessary elements when an instances are moved around.