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

[ShadyDOM] Add option for on-demand patching #189

Merged
merged 14 commits into from
Oct 11, 2019
Merged

Conversation

sorvell
Copy link
Collaborator

@sorvell sorvell commented Aug 20, 2019

Fixes #187, adds on-demand patching option to ShadyDOM. Use by setting ShadyDOM = {noPatch: 'on-demand'}.

@sorvell sorvell marked this pull request as ready for review August 27, 2019 01:28
@sorvell sorvell requested a review from dfreedm as a code owner August 27, 2019 01:28
Steven Orvell added 8 commits August 30, 2019 08:55
Can be used when code should be compatible with noPatch = true and noPatch = 'on-demand' for better performance for all APIs except:
* `appendChild` and `insertBefore` (when the node is being moved from a location where it was logically positioned in the DOM)
* when setting `className`/`class`
* when calling `querySelector|All`
* when setting `textContent` or `innerHTML`.
This is so big because every call to `wrap` was changed to `wrapIfNeeded` when possible.
Ensure `ShadyDOM.wrapIfNeeded` wraps when `on-demand` patching is used on browsers without descriptors since these browsers don't support on-demand patching.
* @param {AccessorType} type
*/
function patchNode(node, type) {
if (utils.settings.hasDescriptors && utils.settings.noPatch) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Pull these out to const in the module since this is a hot path?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

function patchNode(node, type) {
if (utils.settings.hasDescriptors && utils.settings.noPatch) {
patchNodeProto(node);
} {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this supposed to be } else { ??

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

* @enum {number}
*/
const AccessorType = {
inside: 1,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are we sure these get inlined and not dereferenced?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

@@ -71,16 +71,66 @@ const getPatchPrototype = (name) => window[name] && window[name].prototype;
// CustomElements polyfill *only* cares about these accessors.
const disallowedNativePatches = utils.settings.hasDescriptors ? null : ['innerHTML', 'textContent'];

/**
* Patch a group of accessors on an object only if it exists or if the `force`
Copy link
Collaborator

Choose a reason for hiding this comment

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

force argument removed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed comment.

}
}

const PROTO_IS_PATCHED = utils.SHADY_PREFIX + 'patchedProto';

const patchedProtos = new Map();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add comment that this is making a pre-patched proto for TextNode, since any other instance patching is guaranteed to be an Element, that way patchNodeProto can assume it just needs to apply the element patches if there's not a pre-patched proto, eliminating a nodeType check.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also, let's add Comment, ProcessingInstruction, and CDATASection to the pre-patched list.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

return attachShadow(this, options);
}
const root = attachShadow(this, options);
// TODO(sorvell): Workaround for CE not seeing shadowRoot in augmented
Copy link
Collaborator

Choose a reason for hiding this comment

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

"augmented" means "on-demand"? Why wasn't this a problem in regular noPatch mode?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Only in on-demand patching mode since we're only patching here. Added better comment.

// noPatch mode. CE's attachShadow patch is overwritten by this patch
// and cannot set its own special tracking for shadowRoot. It does this
// to be able to see closed shadowRoots.
this['__CE_shadowRoot'] = root;
Copy link
Collaborator

Choose a reason for hiding this comment

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

CE polyfill also does internals.patchNode(shadowRoot); in its attachShadow patch -- this isn't needed in noPatch/on-demand because ShadyDOM's innerHTML/textContent instance patches would cause CE reactions via removeChild/appendChild?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's right.

// `getRootNode`, `isConnected`, `slot`, `assignedSlot`, `assignedNodes`.
'wrapIfNeeded': utils.settings.noPatch === true ||
(utils.settings.noPatch === 'on-demand' && !utils.settings.hasDescriptors) ?
wrap : patch,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's review the logic for what wrap and wrapIfNeeded get set to together (and cross-check against Polymer's versions)...

@@ -121,6 +138,9 @@ if (utils.settings.inUse) {
applyPatches();
// Patch click event behavior only if we're patching
patchClick()
} else if (utils.settings.patchOnDemand) {
// in noPatch, do patch `attachShadow` and `shadowRoot`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// in noPatch, do patch `attachShadow` and `shadowRoot`
// in "on-demand" patching, do patch `attachShadow` and `shadowRoot`. This
// unconditional patch is what kicks off all other on-demand patching

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Clarified.

Copy link
Collaborator

@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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[ShadyDOM] Add "on demand" patching mode
3 participants