Skip to content
This repository has been archived by the owner on Sep 20, 2019. It is now read-only.

Adds attachShadow({shadyUpgradeFragment: documentFragment}) #316

Merged
merged 25 commits into from
Apr 13, 2019

Conversation

sorvell
Copy link
Contributor

@sorvell sorvell commented Feb 14, 2019

This API is being added as an optimization for creating and initially populating a ShadowRoot. Instead of:

element.attachShadow({mode: 'open'});
element.shadowRoot.appendChild(aDocFragment);

it's now faster to do:

element.attachShadow({mode: 'open', shadyUpgradeFragment: aDocFragment});
element.shadowRoot.appendChild(aDocFragment);

This removes the need to create an extra document fragment and optimizes the copying of nodes into the shadowRoot. Note that when this is used, the passed in documentFragment becomes the shadowRoot.

Steven Orvell added 11 commits January 22, 2019 17:46
* As an optimization for creation and initial population of a ShadowRoot, adds the optional `upgrade` static method. Note, the `fragment` dom must already have any necessary style scoping applied.

* Also removes initial default property sets from `ShadyData` and `ShadyRoot` which is empirically slightly faster.

* slightly optimizes capturing logical data by not accessing `childNodes`
This replaces but is the same as `ShadowRoot.upgrade`. Prefer putting bespoke API on non-standard object.

Also adds some slight type refinement.
Remove`.only` that was mistakenly skipping tests. Slight re-organization of a few tests
Since `appendChild` is not used, the custom elements patch that calls `connectedCallback` will not run. Therefore, do not use this when custom elements are being polyfilled.
Performing `appendChild` with a documentFragment with a patched prototype appears to blow up IE.
Object.defineProperty(window['CustomElementRegistry'].prototype, 'define', {
value: function(name, constructor) {
//Object.defineProperty(window['CustomElementRegistry'].prototype, 'define', {
window.customElements.define = function(name, constructor) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're keeping this change, let's add a comment for why... did something else patch the instance rather than the prototype?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, ShadyCSS patches this directly. Changing is path of least resistance.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "fast" (tearoff) native shim also patches the instance. So nevermind, let's just align on instance patching for now.

let c$ = node[utils.SHADY_PREFIX + 'childNodes'];
for (let i=0; i < c$.length; i++) {
linkNode(c$[i], container, ref_node);
const first = node[utils.NATIVE_PREFIX + 'firstChild'] || null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is || null required? Native should always return null, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, removing.

if (!root && nodeData.firstChild !== undefined) {
return;
}
root = root || node;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confusing to call this root here, it's really parent. L110 could just be sd.parentNode = root || node.

It might be good to call the root argument adoptedRoot or something like that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to adoptedParent

@@ -307,7 +306,7 @@ export const NodePatches = utils.getOwnPropertyDescriptors({
});
}
// if a slot is added, must render containing root.
if (this.localName === 'slot' || slotsAdded.length) {
if (slotsAdded.length) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can this.localName === 'slot' be removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was incorrect. This says "the node we're inserting into is a slot" and in this case a slot was not added. What it was meant to trap was node.localName but this is not needed because the treeVisitor checks node.

src/shady-data.js Show resolved Hide resolved
src/shadydom.js Outdated
@@ -82,9 +84,23 @@ if (utils.settings.inUse) {
// access that requires Shadow DOM behavior to be proxied via `ShadyDOM.wrap`.
'noPatch': utils.settings.noPatch,
'nativeMethods': nativeMethods,
'nativeTree': nativeTree
'nativeTree': nativeTree,
'upgrade': (fragment, host, options) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

upgrade just sounds too much like the CE concept... can we just call it attachDom?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

@kevinpschaaf kevinpschaaf changed the title Adds ShadyDOM.upgrade(fragment, host, options) Adds ShadyDOM.attachShadow(fragment, host, options) Feb 20, 2019
@kevinpschaaf kevinpschaaf changed the title Adds ShadyDOM.attachShadow(fragment, host, options) Adds ShadyDOM.attachDom(fragment, host, options) Feb 20, 2019
src/utils.js Outdated
settings.IS_IE = IS_IE;

let _canUpgrade;
// do not use `upgrade` when custom elements is polyfilled or on IE.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Elminiate the bail-out on CE polyfill by detecting shadowRoot.appendChild(shadowRoot) and doing the attachDom work there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IE blows up if we patch the documentFragment.

Optimizations were moved in situ: attachDOM puts the fragment on the host and attachShadow and appendChild use that.

This requires making sure `attachShadow` and `appendChild` are called. The code now special cases inside of those patches.
@dfreedm dfreedm added this to In Progress in Polyfills Mar 18, 2019
@dfreedm dfreedm moved this from In Progress to Sprint Backlog in Polyfills Mar 18, 2019
@dfreedm dfreedm moved this from Sprint Backlog to Backlog in Polyfills Mar 18, 2019
Steven Orvell added 3 commits April 4, 2019 16:12
* ensures a shadowRoot itself does not maintain a `__noInsertionPoint` flag after initial creation when ShadyDOM.attachDom is used.
* adds small optimization to prevent text nodes from being unscoped
@@ -440,7 +440,7 @@ export function addEventListener(type, fnOrObj, optionsOrCapture) {
const wrapperFn = function(e) {
// Support `once` option.
if (once) {
this[utils.SHADY_PREFIX + 'removeEventListener'](type, fnOrObj, nativeEventOptions);
this[utils.SHADY_PREFIX + 'removeEventListener'](type, fnOrObj, optionsOrCapture);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not following this change...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nativeEventOptions should only be passed to native methods. It's important to pass the actual options to the removeEventListener so we can find the wrapper function to remove.

src/shadydom.js Outdated
@@ -82,7 +84,16 @@ if (utils.settings.inUse) {
// access that requires Shadow DOM behavior to be proxied via `ShadyDOM.wrap`.
'noPatch': utils.settings.noPatch,
'nativeMethods': nativeMethods,
'nativeTree': nativeTree
'nativeTree': nativeTree,
'attachDom': (fragment, host, options) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: eliminate attachDom and instead have attachShadow take another option, such that this pattern triggers the same logic:

this.attachShadow({mode: 'open', shadyUseFragment: fragment});
this.shadowRoot.appendChild(fragment);

@sorvell sorvell changed the title Adds ShadyDOM.attachDom(fragment, host, options) Adds attachShadow({shadyUpgradeFragment: documentFragment}) Apr 13, 2019
Polyfills automation moved this from Backlog to In Progress Apr 13, 2019
Copy link
Contributor

@kevinpschaaf kevinpschaaf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@kevinpschaaf kevinpschaaf merged commit 4447ac7 into master Apr 13, 2019
Polyfills automation moved this from In Progress to Done Apr 13, 2019
@kevinpschaaf kevinpschaaf deleted the shadowRoot-upgrade branch April 13, 2019 01:06
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
No open projects
Polyfills
  
Done
Development

Successfully merging this pull request may close these issues.

None yet

3 participants