From fa293fab0459466e785e631c0887d1a0bf08b626 Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Mon, 26 Aug 2019 18:27:48 -0700 Subject: [PATCH 01/20] Add support for ShadyDOM on-demand patching --- lib/elements/dom-if.js | 16 ++++++++-------- lib/elements/dom-repeat.js | 6 +++--- lib/legacy/class.js | 10 ++++++++-- lib/mixins/element-mixin.js | 4 ++-- lib/utils/flush.js | 2 +- lib/utils/wrap.js | 4 ++++ 6 files changed, 26 insertions(+), 16 deletions(-) diff --git a/lib/elements/dom-if.js b/lib/elements/dom-if.js index 2b4f23c70d..6612f34d5a 100644 --- a/lib/elements/dom-if.js +++ b/lib/elements/dom-if.js @@ -14,7 +14,7 @@ import { Debouncer } from '../utils/debounce.js'; import { enqueueDebouncer, flush } from '../utils/flush.js'; import { microTask } from '../utils/async.js'; import { root } from '../utils/path.js'; -import { wrap } from '../utils/wrap.js'; +import { wrap, wrapLocal } from '../utils/wrap.js'; import { hideElementsGlobally } from '../utils/hide-template-controls.js'; /** @@ -122,9 +122,9 @@ export class DomIf extends PolymerElement { */ disconnectedCallback() { super.disconnectedCallback(); - const parent = wrap(this).parentNode; + const parent = wrapLocal(this).parentNode; if (!parent || (parent.nodeType == Node.DOCUMENT_FRAGMENT_NODE && - !wrap(parent).host)) { + !wrapLocal(parent).host)) { this.__teardownInstance(); } } @@ -178,7 +178,7 @@ export class DomIf extends PolymerElement { } __ensureInstance() { - let parentNode = wrap(this).parentNode; + let parentNode = wrapLocal(this).parentNode; // Guard against element being detached while render was queued if (parentNode) { if (!this.__ctor) { @@ -223,16 +223,16 @@ export class DomIf extends PolymerElement { } if (!this.__instance) { this.__instance = new this.__ctor(); - wrap(parentNode).insertBefore(this.__instance.root, this); + wrapLocal(parentNode).insertBefore(this.__instance.root, this); } else { this.__syncHostProperties(); let c$ = this.__instance.children; if (c$ && c$.length) { // Detect case where dom-if was re-attached in new position - let lastChild = wrap(this).previousSibling; + let lastChild = wrapLocal(this).previousSibling; if (lastChild !== c$[c$.length-1]) { for (let i=0, n; (i { * @return {ShadowRoot} node to which the dom has been attached. */ _attachDom(dom) { - const n = wrap(this); + const n = wrapLocal(this); if (n.attachShadow) { if (dom) { if (!n.shadowRoot) { diff --git a/lib/utils/flush.js b/lib/utils/flush.js index cad0fb645c..8b8f88371c 100644 --- a/lib/utils/flush.js +++ b/lib/utils/flush.js @@ -21,7 +21,7 @@ export {enqueueDebouncer}; export const flush = function() { let shadyDOM, debouncers; do { - shadyDOM = window.ShadyDOM && ShadyDOM.flush(); + shadyDOM = window.ShadyDOM && ShadyDOM.flush && ShadyDOM.flush(); if (window.ShadyCSS && window.ShadyCSS.ScopingShim) { window.ShadyCSS.ScopingShim.flush(); } diff --git a/lib/utils/wrap.js b/lib/utils/wrap.js index ce2dbe8701..8c3a932ab2 100644 --- a/lib/utils/wrap.js +++ b/lib/utils/wrap.js @@ -22,3 +22,7 @@ export const wrap = (window['ShadyDOM'] && window['ShadyDOM']['noPatch'] && wind window['ShadyDOM']['wrap'] : (window['ShadyDOM'] ? (n) => ShadyDOM['patch'](n) : (n) => n); +// Wrapping a local operation (e.g. appendChild) is only needed iff noPatch is +// explicitly true, and on-demand patching is not used. +export const wrapLocal = (window['ShadyDOM'] && window['ShadyDOM']['noPatch'] === true && window['ShadyDOM']['wrap']) ? +window['ShadyDOM']['wrap'] : (n) => n; \ No newline at end of file From 0699a436a23dfb2c23bbbc211d662424898a8772 Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Fri, 30 Aug 2019 16:32:17 -0700 Subject: [PATCH 02/20] Adds support for `customElements.polyfillDefineLazy` This is a polyfill optimization which defers class generation until first instance. --- lib/legacy/polymer-fn.js | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/lib/legacy/polymer-fn.js b/lib/legacy/polymer-fn.js index de019be6a9..a02249cd45 100644 --- a/lib/legacy/polymer-fn.js +++ b/lib/legacy/polymer-fn.js @@ -8,9 +8,22 @@ Code distributed by Google as part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ import { Class } from './class.js'; +import { legacyOptimizations } from '../utils/settings.js'; import '../utils/boot.js'; +const generateClass = (info) => { + // if input is a `class` (aka a function with a prototype), use the prototype + // remember that the `constructor` will never be called + let klass; + if (typeof info === 'function') { + klass = info; + } else { + klass = Polymer.Class(info); + } + return klass; +} + /** * Legacy class factory and registration helper for defining Polymer * elements. @@ -31,16 +44,16 @@ import '../utils/boot.js'; * @suppress {duplicate, invalidCasts, checkTypes} */ const Polymer = function(info) { - // if input is a `class` (aka a function with a prototype), use the prototype - // remember that the `constructor` will never be called - let klass; - if (typeof info === 'function') { - klass = info; + // Note, when polyfillDefineLazy is used, the class is not returned from this function. Elements that require this should set `_needsCtor` to true to opt + // out of this optimization. In extended library code, only + // `iron-a11y-announcer` and/ `paper-menu-button` need this. + if (legacyOptimizations && customElements.polyfillDefineLazy && !info._needsCtor) { + customElements.polyfillDefineLazy(info.is, () => generateClass(info)); } else { - klass = Polymer.Class(info); + const klass = generateClass(info); + customElements.define(klass.is, /** @type {!HTMLElement} */(klass)); + return klass; } - customElements.define(klass.is, /** @type {!HTMLElement} */(klass)); - return klass; }; Polymer.Class = Class; From 2498031875b297824c6230447b601b42c643837d Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Tue, 3 Sep 2019 10:05:35 -0700 Subject: [PATCH 03/20] Adds `lazyDefine` setting for enabling `customElements.polyfillLazyDefine` when the `Polymer` function is used. --- lib/legacy/polymer-fn.js | 4 ++-- lib/utils/settings.js | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/legacy/polymer-fn.js b/lib/legacy/polymer-fn.js index a02249cd45..6aac579abe 100644 --- a/lib/legacy/polymer-fn.js +++ b/lib/legacy/polymer-fn.js @@ -8,7 +8,7 @@ Code distributed by Google as part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ import { Class } from './class.js'; -import { legacyOptimizations } from '../utils/settings.js'; +import { lazyDefine } from '../utils/settings.js'; import '../utils/boot.js'; @@ -47,7 +47,7 @@ const Polymer = function(info) { // Note, when polyfillDefineLazy is used, the class is not returned from this function. Elements that require this should set `_needsCtor` to true to opt // out of this optimization. In extended library code, only // `iron-a11y-announcer` and/ `paper-menu-button` need this. - if (legacyOptimizations && customElements.polyfillDefineLazy && !info._needsCtor) { + if (lazyDefine && customElements.polyfillDefineLazy && !info._needsCtor) { customElements.polyfillDefineLazy(info.is, () => generateClass(info)); } else { const klass = generateClass(info); diff --git a/lib/utils/settings.js b/lib/utils/settings.js index 9e9fa3aec1..5d40830248 100644 --- a/lib/utils/settings.js +++ b/lib/utils/settings.js @@ -316,3 +316,24 @@ export let fastDomIf = window.Polymer && window.Polymer.fastDomIf || false; export const setFastDomIf = function(useFastDomIf) { fastDomIf = useFastDomIf; }; + +/** + * Setting to enable `customElements.polyfillLazyDefine` when the Polymer function + * is used to define elements. When in use, the `Polymer` function will not + * return the defined element class. + */ +export let lazyDefine = + window.Polymer && window.Polymer.lazyDefine || false; + +/** + * Sets `lazyDefine` globally for all elements to enable + * `customElements.polyfillLazyDefine` when the `Polymer` function is used + * to define elements. + * + * @param {boolean} useLazyDefine enable or disable legacy optimizations + * includes and url rewriting + * @return {void} + */ +export const setLazyDefine = function(useLazyDefine) { + lazyDefine = useLazyDefine; +}; From 078cb3449b652de789401c5a4381c55d49e24643 Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Tue, 3 Sep 2019 10:06:59 -0700 Subject: [PATCH 04/20] lint fixes --- lib/legacy/class.js | 2 +- lib/legacy/polymer-fn.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/legacy/class.js b/lib/legacy/class.js index cdf09a928b..1a0a440812 100644 --- a/lib/legacy/class.js +++ b/lib/legacy/class.js @@ -459,7 +459,7 @@ function GenerateClassFromInfo(info, Base, behaviors) { let LegacyBase = HTMLElement; if (window.ShadyDOM && window.ShadyDOM.patchOnDemand) { - LegacyBase = class extends HTMLElement {} + LegacyBase = class extends HTMLElement {}; ShadyDOM.patchElementProto(LegacyBase.prototype); } diff --git a/lib/legacy/polymer-fn.js b/lib/legacy/polymer-fn.js index 6aac579abe..fd546550ea 100644 --- a/lib/legacy/polymer-fn.js +++ b/lib/legacy/polymer-fn.js @@ -22,7 +22,7 @@ const generateClass = (info) => { klass = Polymer.Class(info); } return klass; -} +}; /** * Legacy class factory and registration helper for defining Polymer From e0c4e82f18f2bc41e2f2ac2ec69274e56877b36f Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Wed, 4 Sep 2019 17:05:31 -0700 Subject: [PATCH 05/20] Change based on review: rename `wrapLocal` to `wrapIfNeeded` --- lib/elements/dom-if.js | 18 +++++++++--------- lib/elements/dom-repeat.js | 6 +++--- lib/mixins/element-mixin.js | 4 ++-- lib/utils/wrap.js | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/elements/dom-if.js b/lib/elements/dom-if.js index 827c2f4860..545e5ad8c8 100644 --- a/lib/elements/dom-if.js +++ b/lib/elements/dom-if.js @@ -14,7 +14,7 @@ import { Debouncer } from '../utils/debounce.js'; import { enqueueDebouncer, flush } from '../utils/flush.js'; import { microTask } from '../utils/async.js'; import { root } from '../utils/path.js'; -import { wrap, wrapLocal } from '../utils/wrap.js'; +import { wrap, wrapIfNeeded } from '../utils/wrap.js'; import { hideElementsGlobally } from '../utils/hide-template-controls.js'; import { fastDomIf, strictTemplatePolicy } from '../utils/settings.js'; import { showHideChildren } from '../utils/templatize.js'; @@ -111,9 +111,9 @@ class DomIfBase extends PolymerElement { */ disconnectedCallback() { super.disconnectedCallback(); - const parent = wrapLocal(this).parentNode; + const parent = wrapIfNeeded(this).parentNode; if (!parent || (parent.nodeType == Node.DOCUMENT_FRAGMENT_NODE && - !wrapLocal(parent).host)) { + !wrapIfNeeded(parent).host)) { this.__teardownInstance(); } } @@ -186,7 +186,7 @@ class DomIfBase extends PolymerElement { * @return {boolean} True if the instance was created, false otherwise. */ __ensureInstance() { - let parentNode = wrapLocal(this).parentNode; + let parentNode = wrapIfNeeded(this).parentNode; if (!this.__hasInstance()) { // Guard against element being detached while render was queued if (!parentNode) { @@ -202,10 +202,10 @@ class DomIfBase extends PolymerElement { let children = this.__getInstanceNodes(); if (children && children.length) { // Detect case where dom-if was re-attached in new position - let lastChild = wrapLocal(this).previousSibling; + let lastChild = wrapIfNeeded(this).previousSibling; if (lastChild !== children[children.length-1]) { for (let i=0, n; (i { * @return {ShadowRoot} node to which the dom has been attached. */ _attachDom(dom) { - const n = wrapLocal(this); + const n = wrapIfNeeded(this); if (n.attachShadow) { if (dom) { if (!n.shadowRoot) { diff --git a/lib/utils/wrap.js b/lib/utils/wrap.js index 8c3a932ab2..e30df70ef6 100644 --- a/lib/utils/wrap.js +++ b/lib/utils/wrap.js @@ -24,5 +24,5 @@ export const wrap = (window['ShadyDOM'] && window['ShadyDOM']['noPatch'] && wind // Wrapping a local operation (e.g. appendChild) is only needed iff noPatch is // explicitly true, and on-demand patching is not used. -export const wrapLocal = (window['ShadyDOM'] && window['ShadyDOM']['noPatch'] === true && window['ShadyDOM']['wrap']) ? +export const wrapIfNeeded = (window['ShadyDOM'] && window['ShadyDOM']['noPatch'] === true && window['ShadyDOM']['wrap']) ? window['ShadyDOM']['wrap'] : (n) => n; \ No newline at end of file From 31c810edb029b2670d815471a50cf35bb201420c Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Wed, 4 Sep 2019 18:13:57 -0700 Subject: [PATCH 06/20] Use `wrapIfNeeded` where possible. --- lib/elements/dom-bind.js | 4 ++-- lib/legacy/legacy-element-mixin.js | 16 ++++++++-------- lib/legacy/polymer.dom.js | 14 +++++++------- lib/mixins/disable-upgrade-mixin.js | 4 ++-- lib/mixins/properties-changed.js | 4 ++-- lib/mixins/property-effects.js | 14 +++++++------- lib/utils/flattened-nodes-observer.js | 14 +++++++------- lib/utils/gestures.js | 4 ++-- lib/utils/scope-subtree.js | 6 +++--- lib/utils/templatize.js | 8 ++++---- 10 files changed, 44 insertions(+), 44 deletions(-) diff --git a/lib/elements/dom-bind.js b/lib/elements/dom-bind.js index 33e7293b09..fa59962532 100644 --- a/lib/elements/dom-bind.js +++ b/lib/elements/dom-bind.js @@ -13,7 +13,7 @@ import { PropertyEffects } from '../mixins/property-effects.js'; import { OptionalMutableData } from '../mixins/mutable-data.js'; import { GestureEventListeners } from '../mixins/gesture-event-listeners.js'; import { strictTemplatePolicy } from '../utils/settings.js'; -import { wrap } from '../utils/wrap.js'; +import { wrapIfNeeded } from '../utils/wrap.js'; import { hideElementsGlobally } from '../utils/hide-template-controls.js'; /** @@ -96,7 +96,7 @@ export class DomBind extends domBindBase { } __insertChildren() { - wrap(wrap(this).parentNode).insertBefore(this.root, this); + wrapIfNeeded(wrapIfNeeded(this).parentNode).insertBefore(this.root, this); } __removeChildren() { diff --git a/lib/legacy/legacy-element-mixin.js b/lib/legacy/legacy-element-mixin.js index 963e496853..d00dd36834 100644 --- a/lib/legacy/legacy-element-mixin.js +++ b/lib/legacy/legacy-element-mixin.js @@ -19,7 +19,7 @@ import { setTouchAction } from '../utils/gestures.js'; import { Debouncer } from '../utils/debounce.js'; import { timeOut, microTask } from '../utils/async.js'; import { get } from '../utils/path.js'; -import { wrap } from '../utils/wrap.js'; +import { wrapIfNeeded } from '../utils/wrap.js'; import { scopeSubtree } from '../utils/scope-subtree.js'; let styleInterface = window.ShadyCSS; @@ -436,7 +436,7 @@ export const LegacyElementMixin = dedupingMixin((base) => { }); event.detail = detail; let node = options.node || this; - wrap(node).dispatchEvent(event); + wrapIfNeeded(node).dispatchEvent(event); return event; } @@ -537,7 +537,7 @@ export const LegacyElementMixin = dedupingMixin((base) => { * @override */ get domHost() { - let root = wrap(this).getRootNode(); + let root = wrapIfNeeded(this).getRootNode(); return (root instanceof DocumentFragment) ? /** @type {ShadowRoot} */ (root).host : root; } @@ -702,8 +702,8 @@ export const LegacyElementMixin = dedupingMixin((base) => { */ isLightDescendant(node) { const thisNode = /** @type {Node} */ (this); - return thisNode !== node && wrap(thisNode).contains(node) && - wrap(thisNode).getRootNode() === wrap(node).getRootNode(); + return thisNode !== node && wrapIfNeeded(thisNode).contains(node) && + wrapIfNeeded(thisNode).getRootNode() === wrapIfNeeded(node).getRootNode(); } /** @@ -714,7 +714,7 @@ export const LegacyElementMixin = dedupingMixin((base) => { * @override */ isLocalDescendant(node) { - return this.root === wrap(node).getRootNode(); + return this.root === wrapIfNeeded(node).getRootNode(); } /** @@ -908,10 +908,10 @@ export const LegacyElementMixin = dedupingMixin((base) => { bool = !node.hasAttribute(name); } if (bool) { - wrap(node).setAttribute(name, ''); + wrapIfNeeded(node).setAttribute(name, ''); return true; } else { - wrap(node).removeAttribute(name); + wrapIfNeeded(node).removeAttribute(name); return false; } } diff --git a/lib/legacy/polymer.dom.js b/lib/legacy/polymer.dom.js index ff4fac8820..46f13ebb45 100644 --- a/lib/legacy/polymer.dom.js +++ b/lib/legacy/polymer.dom.js @@ -8,7 +8,7 @@ Code distributed by Google as part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ import '../utils/boot.js'; -import { wrap } from '../utils/wrap.js'; +import { wrap, wrapIfNeeded } from '../utils/wrap.js'; import '../utils/settings.js'; import { FlattenedNodesObserver } from '../utils/flattened-nodes-observer.js'; export { flush, enqueueDebouncer as addDebouncer } from '../utils/flush.js'; @@ -97,7 +97,7 @@ class DomApiNative { * @override */ deepContains(node) { - if (wrap(this.node).contains(node)) { + if (wrapIfNeeded(this.node).contains(node)) { return true; } let n = node; @@ -105,7 +105,7 @@ class DomApiNative { // walk from node to `this` or `document` while (n && n !== doc && n !== this.node) { // use logical parentnode, or native ShadowRoot host - n = wrap(n).parentNode || wrap(n).host; + n = wrapIfNeeded(n).parentNode || wrapIfNeeded(n).host; } return n === this.node; } @@ -120,7 +120,7 @@ class DomApiNative { * @override */ getOwnerRoot() { - return wrap(this.node).getRootNode(); + return wrapIfNeeded(this.node).getRootNode(); } /** @@ -132,7 +132,7 @@ class DomApiNative { */ getDistributedNodes() { return (this.node.localName === 'slot') ? - wrap(this.node).assignedNodes({flatten: true}) : + wrapIfNeeded(this.node).assignedNodes({flatten: true}) : []; } @@ -144,10 +144,10 @@ class DomApiNative { */ getDestinationInsertionPoints() { let ip$ = []; - let n = wrap(this.node).assignedSlot; + let n = wrapIfNeeded(this.node).assignedSlot; while (n) { ip$.push(n); - n = wrap(n).assignedSlot; + n = wrapIfNeeded(n).assignedSlot; } return ip$; } diff --git a/lib/mixins/disable-upgrade-mixin.js b/lib/mixins/disable-upgrade-mixin.js index 868cfc49d2..7ef96af44b 100644 --- a/lib/mixins/disable-upgrade-mixin.js +++ b/lib/mixins/disable-upgrade-mixin.js @@ -11,7 +11,7 @@ */ import { ElementMixin } from './element-mixin.js'; import { dedupingMixin } from '../utils/mixin.js'; -import { wrap } from '../utils/wrap.js'; +import { wrapIfNeeded } from '../utils/wrap.js'; const DISABLED_ATTR = 'disable-upgrade'; @@ -131,7 +131,7 @@ export const DisableUpgradeMixin = dedupingMixin((base) => { if (this.__isUpgradeDisabled && value == null) { super._initializeProperties(); this.__isUpgradeDisabled = false; - if (wrap(this).isConnected) { + if (wrapIfNeeded(this).isConnected) { super.connectedCallback(); } } diff --git a/lib/mixins/properties-changed.js b/lib/mixins/properties-changed.js index 854b5b72f8..d3ad2a7997 100644 --- a/lib/mixins/properties-changed.js +++ b/lib/mixins/properties-changed.js @@ -11,7 +11,7 @@ import '../utils/boot.js'; import { dedupingMixin } from '../utils/mixin.js'; import { microTask } from '../utils/async.js'; -import { wrap } from '../utils/wrap.js'; +import { wrapIfNeeded } from '../utils/wrap.js'; /** @const {!AsyncInterface} */ const microtask = microTask; @@ -517,7 +517,7 @@ export const PropertiesChanged = dedupingMixin( _valueToNodeAttribute(node, value, attribute) { const str = this._serializeValue(value); if (attribute === 'class' || attribute === 'name' || attribute === 'slot') { - node = /** @type {?Element} */(wrap(node)); + node = /** @type {?Element} */(wrapIfNeeded(node)); } if (str === undefined) { node.removeAttribute(attribute); diff --git a/lib/mixins/property-effects.js b/lib/mixins/property-effects.js index 2e2cddb150..411323c5c8 100644 --- a/lib/mixins/property-effects.js +++ b/lib/mixins/property-effects.js @@ -11,7 +11,7 @@ */ import '../utils/boot.js'; -import { wrap } from '../utils/wrap.js'; +import { wrapIfNeeded } from '../utils/wrap.js'; import { dedupingMixin } from '../utils/mixin.js'; import { root, isAncestor, isDescendant, get, translate, isPath, set, normalize } from '../utils/path.js'; /* for notify, reflect */ @@ -310,7 +310,7 @@ function dispatchNotifyEvent(inst, eventName, value, path) { if (path) { detail.path = path; } - wrap(/** @type {!HTMLElement} */(inst)).dispatchEvent(new CustomEvent(eventName, { detail })); + wrapIfNeeded(/** @type {!HTMLElement} */(inst)).dispatchEvent(new CustomEvent(eventName, { detail })); } /** @@ -926,7 +926,7 @@ function setupCompoundStorage(node, binding) { // We may also want to consider doing this for `textContent` and // `innerHTML`. if (target === 'className') { - node = wrap(node); + node = wrapIfNeeded(node); } node[target] = binding.literal; } @@ -1623,7 +1623,7 @@ export const PropertyEffects = dedupingMixin(superClass => { if (value !== node[prop] || typeof value == 'object') { // Note, className needs style scoping so this needs wrapping. if (prop === 'className') { - node = /** @type {!Node} */(wrap(node)); + node = /** @type {!Node} */(wrapIfNeeded(node)); } node[prop] = value; } @@ -2710,7 +2710,7 @@ export const PropertyEffects = dedupingMixin(superClass => { * create and link an instance of the template metadata associated with a * particular stamping. * - * @override + * @override * @param {!HTMLTemplateElement} template Template containing binding * bindings * @param {boolean=} instanceBinding When false (default), performs @@ -2720,7 +2720,7 @@ export const PropertyEffects = dedupingMixin(superClass => { * list of bound templates. * @return {!TemplateInfo} Template metadata object; for `runtimeBinding`, * this is an instance of the prototypical template info - * @protected + * @protected * @suppress {missingProperties} go/missingfnprops */ _bindTemplate(template, instanceBinding) { @@ -2881,7 +2881,7 @@ export const PropertyEffects = dedupingMixin(superClass => { let nodes = templateInfo.childNodes; for (let i=0; i { if (isSlot(node)) { node = /** @type {!HTMLSlotElement} */(node); // eslint-disable-line no-self-assign - return wrap(node).assignedNodes({flatten: true}); + return wrapIfNeeded(node).assignedNodes({flatten: true}); } else { return [node]; } @@ -147,9 +147,9 @@ export let FlattenedNodesObserver = class { connect() { if (isSlot(this._target)) { this._listenSlots([this._target]); - } else if (wrap(this._target).children) { + } else if (wrapIfNeeded(this._target).children) { this._listenSlots( - /** @type {!NodeList} */ (wrap(this._target).children)); + /** @type {!NodeList} */ (wrapIfNeeded(this._target).children)); if (window.ShadyDOM) { this._shadyChildrenObserver = window.ShadyDOM.observeChildren(this._target, (mutations) => { @@ -178,9 +178,9 @@ export let FlattenedNodesObserver = class { disconnect() { if (isSlot(this._target)) { this._unlistenSlots([this._target]); - } else if (wrap(this._target).children) { + } else if (wrapIfNeeded(this._target).children) { this._unlistenSlots( - /** @type {!NodeList} */ (wrap(this._target).children)); + /** @type {!NodeList} */ (wrapIfNeeded(this._target).children)); if (window.ShadyDOM && this._shadyChildrenObserver) { window.ShadyDOM.unobserveChildren(this._shadyChildrenObserver); this._shadyChildrenObserver = null; diff --git a/lib/utils/gestures.js b/lib/utils/gestures.js index 24a620c784..3e992b0ddf 100644 --- a/lib/utils/gestures.js +++ b/lib/utils/gestures.js @@ -26,7 +26,7 @@ import './boot.js'; import { timeOut, microTask } from './async.js'; import { Debouncer } from './debounce.js'; import { passiveTouchGestures, cancelSyntheticClickEvents } from './settings.js'; -import { wrap } from './wrap.js'; +import { wrapIfNeeded } from './wrap.js'; // detect native touch action support let HAS_NATIVE_TA = typeof document.head.style.touchAction === 'string'; @@ -666,7 +666,7 @@ export function setTouchAction(node, value) { function _fire(target, type, detail) { let ev = new Event(type, { bubbles: true, cancelable: true, composed: true }); ev.detail = detail; - wrap(/** @type {!Node} */(target)).dispatchEvent(ev); + wrapIfNeeded(/** @type {!Node} */(target)).dispatchEvent(ev); // forward `preventDefault` in a clean way if (ev.defaultPrevented) { let preventer = detail.preventer || detail.sourceEvent; diff --git a/lib/utils/scope-subtree.js b/lib/utils/scope-subtree.js index f99a7fb724..b3341bd09d 100644 --- a/lib/utils/scope-subtree.js +++ b/lib/utils/scope-subtree.js @@ -9,7 +9,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN */ import './boot.js'; -import {wrap} from './wrap.js'; +import {wrapIfNeeded} from './wrap.js'; const ShadyDOM = window.ShadyDOM; const ShadyCSS = window.ShadyCSS; @@ -22,7 +22,7 @@ const ShadyCSS = window.ShadyCSS; * @return {boolean} True if node is in scope */ function sameScope(node, scope) { - return wrap(node).getRootNode() === scope; + return wrapIfNeeded(node).getRootNode() === scope; } /** @@ -50,7 +50,7 @@ export function scopeSubtree(container, shouldObserve = false) { } // capture correct scope for container const containerScope = ScopingShim['scopeForNode'](container); - const root = wrap(container).getRootNode(); + const root = wrapIfNeeded(container).getRootNode(); const scopify = (node) => { if (!sameScope(node, root)) { diff --git a/lib/utils/templatize.js b/lib/utils/templatize.js index 4e42ae6fe1..731699d852 100644 --- a/lib/utils/templatize.js +++ b/lib/utils/templatize.js @@ -50,7 +50,7 @@ import './boot.js'; import { PropertyEffects } from '../mixins/property-effects.js'; import { MutableData } from '../mixins/mutable-data.js'; import { strictTemplatePolicy, legacyWarnings } from './settings.js'; -import { wrap } from './wrap.js'; +import { wrap, wrapIfNeeded } from './wrap.js'; // Base class for HTMLTemplateElement extension that has property effects // machinery for propagating host properties to children. This is an ES5 @@ -121,11 +121,11 @@ export function showHideChildren(hide, children) { } else if (n.localName === 'slot') { if (hide) { n.__polymerReplaced__ = document.createComment('hidden-slot'); - wrap(wrap(n).parentNode).replaceChild(n.__polymerReplaced__, n); + wrap(wrapIfNeeded(n).parentNode).replaceChild(n.__polymerReplaced__, n); } else { const replace = n.__polymerReplaced__; if (replace) { - wrap(wrap(replace).parentNode).replaceChild(n, replace); + wrap(wrapIfNeeded(replace).parentNode).replaceChild(n, replace); } } } @@ -682,7 +682,7 @@ export function modelForElement(template, node) { } else { // Still in a template scope, keep going up until // a __templatizeInstance is found - node = wrap(node).parentNode; + node = wrapIfNeeded(node).parentNode; } } return null; From a7f9c0faa47c848e80a7ed491f7c2a31e999c677 Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Wed, 4 Sep 2019 18:24:51 -0700 Subject: [PATCH 07/20] More wrapIfNeeded. --- lib/elements/dom-if.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/elements/dom-if.js b/lib/elements/dom-if.js index 545e5ad8c8..ce2c8cfbc2 100644 --- a/lib/elements/dom-if.js +++ b/lib/elements/dom-if.js @@ -570,7 +570,7 @@ class DomIfLegacy extends DomIfBase { // Instance children may be disconnected from parents when dom-if // detaches if a tree was innerHTML'ed if (parent) { - parent = wrap(parent); + parent = wrapIfNeeded(parent); for (let i=0, n; (i Date: Wed, 4 Sep 2019 18:27:55 -0700 Subject: [PATCH 08/20] Use wrap in dom-repeat. --- lib/elements/dom-repeat.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/elements/dom-repeat.js b/lib/elements/dom-repeat.js index c899592690..75e369fe6b 100644 --- a/lib/elements/dom-repeat.js +++ b/lib/elements/dom-repeat.js @@ -347,11 +347,11 @@ export class DomRepeat extends domRepeatBase { /** @type {!HTMLElement} */ (this)); let template = this.template = thisAsTemplate._templateInfo ? thisAsTemplate : - /** @type {!HTMLTemplateElement} */ (this.querySelector('template')); + /** @type {!HTMLTemplateElement} */ (wrap(this).querySelector('template')); if (!template) { // Wait until childList changes and template should be there by then let observer = new MutationObserver(() => { - if (this.querySelector('template')) { + if (wrap(this).querySelector('template')) { observer.disconnect(); this.__render(); } else { From 4422d13e75fd7ff0dd8bac7db5ce64fd6cdda22d Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Thu, 5 Sep 2019 11:41:07 -0700 Subject: [PATCH 09/20] Refinements to `wrap` * Fixes an issue when instance patching is required * Now uses ShadyDOM.wrapIfNeeded. --- lib/elements/dom-if.js | 4 ++-- lib/elements/dom-repeat.js | 5 +++-- lib/mixins/property-effects.js | 6 +++--- lib/utils/templatize.js | 4 ++-- lib/utils/wrap.js | 19 ++++++++++++------- test/unit/shady.html | 20 +++++++++++++------- 6 files changed, 35 insertions(+), 23 deletions(-) diff --git a/lib/elements/dom-if.js b/lib/elements/dom-if.js index ce2c8cfbc2..c003f3c1ac 100644 --- a/lib/elements/dom-if.js +++ b/lib/elements/dom-if.js @@ -151,11 +151,11 @@ class DomIfBase extends PolymerElement { let template = thisAsTemplate._templateInfo ? thisAsTemplate : /** @type {!HTMLTemplateElement} */ - (wrap(thisAsTemplate).querySelector('template')); + (thisAsTemplate.querySelector('template')); if (!template) { // Wait until childList changes and template should be there by then let observer = new MutationObserver(() => { - if (wrap(this).querySelector('template')) { + if (this.querySelector('template')) { observer.disconnect(); this.__render(); } else { diff --git a/lib/elements/dom-repeat.js b/lib/elements/dom-repeat.js index 75e369fe6b..3459c3027a 100644 --- a/lib/elements/dom-repeat.js +++ b/lib/elements/dom-repeat.js @@ -347,11 +347,11 @@ export class DomRepeat extends domRepeatBase { /** @type {!HTMLElement} */ (this)); let template = this.template = thisAsTemplate._templateInfo ? thisAsTemplate : - /** @type {!HTMLTemplateElement} */ (wrap(this).querySelector('template')); + /** @type {!HTMLTemplateElement} */ (this.querySelector('template')); if (!template) { // Wait until childList changes and template should be there by then let observer = new MutationObserver(() => { - if (wrap(this).querySelector('template')) { + if (this.querySelector('template')) { observer.disconnect(); this.__render(); } else { @@ -595,6 +595,7 @@ export class DomRepeat extends domRepeatBase { __detachInstance(idx) { let inst = this.__instances[idx]; + // Note, use slow `wrap` to ensure any removed DOM is properly undistributed. const wrappedRoot = wrap(inst.root); for (let i=0; i { if (value !== node[prop] || typeof value == 'object') { // Note, className needs style scoping so this needs wrapping. if (prop === 'className') { - node = /** @type {!Node} */(wrapIfNeeded(node)); + node = /** @type {!Node} */(wrap(node)); } node[prop] = value; } diff --git a/lib/utils/templatize.js b/lib/utils/templatize.js index 731699d852..2683b87f58 100644 --- a/lib/utils/templatize.js +++ b/lib/utils/templatize.js @@ -121,11 +121,11 @@ export function showHideChildren(hide, children) { } else if (n.localName === 'slot') { if (hide) { n.__polymerReplaced__ = document.createComment('hidden-slot'); - wrap(wrapIfNeeded(n).parentNode).replaceChild(n.__polymerReplaced__, n); + wrapIfNeeded(wrapIfNeeded(n).parentNode).replaceChild(n.__polymerReplaced__, n); } else { const replace = n.__polymerReplaced__; if (replace) { - wrap(wrapIfNeeded(replace).parentNode).replaceChild(n, replace); + wrapIfNeeded(wrapIfNeeded(replace).parentNode).replaceChild(n, replace); } } } diff --git a/lib/utils/wrap.js b/lib/utils/wrap.js index e30df70ef6..4c99d4eb0f 100644 --- a/lib/utils/wrap.js +++ b/lib/utils/wrap.js @@ -18,11 +18,16 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN * of legacy (Polymer.dom) API. * @type {function(Node):Node} */ -export const wrap = (window['ShadyDOM'] && window['ShadyDOM']['noPatch'] && window['ShadyDOM']['wrap']) ? - window['ShadyDOM']['wrap'] : - (window['ShadyDOM'] ? (n) => ShadyDOM['patch'](n) : (n) => n); +export const wrap = window['ShadyDOM'] && window['ShadyDOM']['wrap'] ? + window['ShadyDOM']['wrap'] : n => n; -// Wrapping a local operation (e.g. appendChild) is only needed iff noPatch is -// explicitly true, and on-demand patching is not used. -export const wrapIfNeeded = (window['ShadyDOM'] && window['ShadyDOM']['noPatch'] === true && window['ShadyDOM']['wrap']) ? -window['ShadyDOM']['wrap'] : (n) => n; \ No newline at end of file +/** + * Wraps the node only when in `ShadyDOM` is in `noPatch` mode and not in + * `on-demand` patching mode. This is more optimal than calling `wrap` for all + * operations 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`. + */ +export const wrapIfNeeded = window['ShadyDOM'] && window['ShadyDOM']['wrapIfNeeded'] ? + window['ShadyDOM']['wrapIfNeeded'] : n => n; \ No newline at end of file diff --git a/test/unit/shady.html b/test/unit/shady.html index e53ca50f69..a0f0cde48e 100644 --- a/test/unit/shady.html +++ b/test/unit/shady.html @@ -152,7 +152,6 @@ document.body.removeChild(host); }); - suite('Mutate light DOM', function() { var host; @@ -381,9 +380,9 @@ child.shadowRoot.innerHTML = ''; var childLocalSub = child.shadowRoot.lastChild; ShadyDOM.flush(); - assert.deepEqual(host.root.querySelectorAll('span#main'), [hostLocalMain]); - assert.deepEqual(host.root.querySelectorAll('div#sub'), [childLightSub]); - assert.deepEqual(child.root.querySelectorAll('span#sub'), [childLocalSub]); + testQSA(host.root.querySelectorAll('span#main'), [hostLocalMain]); + testQSA(host.root.querySelectorAll('div#sub'), [childLightSub]); + testQSA(child.root.querySelectorAll('span#sub'), [childLocalSub]); }); test('querySelectorAll (light dom)', function() { @@ -396,9 +395,9 @@ var childLightSub = getComposedChildAtIndex(child, 100) ; child.shadowRoot.innerHTML = ''; ShadyDOM.flush(); - assert.deepEqual(host.querySelectorAll('div#main'), [hostLightMain]); - assert.deepEqual(host.querySelectorAll('#sub'), []); - assert.deepEqual(child.querySelectorAll('div#sub'), [childLightSub]); + testQSA(host.querySelectorAll('div#main'), [hostLightMain]); + testQSA(host.querySelectorAll('#sub'), []); + testQSA(child.querySelectorAll('div#sub'), [childLightSub]); }); }); @@ -475,6 +474,13 @@ assert.equal(a[i], b[i], msg); } } + +function testQSA(qsaResult, checkArray) { + assert.equal(qsaResult.length, checkArray.length); + for (let i = 0; i < checkArray.length; i++) { + assert.equal(qsaResult[i], checkArray[i]); + } +} From 19abf83a94b754781c46fe231c932a131977dee7 Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Mon, 16 Sep 2019 12:27:18 -0700 Subject: [PATCH 10/20] Lint fixes and added wrapping comment. --- lib/elements/dom-if.js | 2 +- lib/mixins/property-effects.js | 4 ++++ lib/utils/templatize.js | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/elements/dom-if.js b/lib/elements/dom-if.js index c003f3c1ac..f1c145b7bf 100644 --- a/lib/elements/dom-if.js +++ b/lib/elements/dom-if.js @@ -14,7 +14,7 @@ import { Debouncer } from '../utils/debounce.js'; import { enqueueDebouncer, flush } from '../utils/flush.js'; import { microTask } from '../utils/async.js'; import { root } from '../utils/path.js'; -import { wrap, wrapIfNeeded } from '../utils/wrap.js'; +import { wrapIfNeeded } from '../utils/wrap.js'; import { hideElementsGlobally } from '../utils/hide-template-controls.js'; import { fastDomIf, strictTemplatePolicy } from '../utils/settings.js'; import { showHideChildren } from '../utils/templatize.js'; diff --git a/lib/mixins/property-effects.js b/lib/mixins/property-effects.js index dbc597e6ba..1117926ada 100644 --- a/lib/mixins/property-effects.js +++ b/lib/mixins/property-effects.js @@ -926,6 +926,8 @@ function setupCompoundStorage(node, binding) { // We may also want to consider doing this for `textContent` and // `innerHTML`. if (target === 'className') { + // This needs to *always* wrap since an unpatched node may need + // its className scoped. node = wrap(node); } node[target] = binding.literal; @@ -1623,6 +1625,8 @@ export const PropertyEffects = dedupingMixin(superClass => { if (value !== node[prop] || typeof value == 'object') { // Note, className needs style scoping so this needs wrapping. if (prop === 'className') { + // This needs to *always* wrap since an unpatched node may need + // its className scoped. node = /** @type {!Node} */(wrap(node)); } node[prop] = value; diff --git a/lib/utils/templatize.js b/lib/utils/templatize.js index 2683b87f58..74d5f36794 100644 --- a/lib/utils/templatize.js +++ b/lib/utils/templatize.js @@ -50,7 +50,7 @@ import './boot.js'; import { PropertyEffects } from '../mixins/property-effects.js'; import { MutableData } from '../mixins/mutable-data.js'; import { strictTemplatePolicy, legacyWarnings } from './settings.js'; -import { wrap, wrapIfNeeded } from './wrap.js'; +import { wrapIfNeeded } from './wrap.js'; // Base class for HTMLTemplateElement extension that has property effects // machinery for propagating host properties to children. This is an ES5 From 39f9853f63067d7fa6c6d5a2be93fa745ef16a0e Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Mon, 16 Sep 2019 17:23:15 -0700 Subject: [PATCH 11/20] Add noPatch == on-demand tests --- lib/legacy/polymer.dom.js | 2 +- lib/mixins/properties-changed.js | 5 +++-- test/runner.html | 4 +++- test/unit/polymer-dom-nopatch.html | 4 ++-- test/unit/styling-scoped-nopatch.html | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/legacy/polymer.dom.js b/lib/legacy/polymer.dom.js index 46f13ebb45..660fc9fd59 100644 --- a/lib/legacy/polymer.dom.js +++ b/lib/legacy/polymer.dom.js @@ -120,7 +120,7 @@ class DomApiNative { * @override */ getOwnerRoot() { - return wrapIfNeeded(this.node).getRootNode(); + return wrap(this.node).getRootNode(); } /** diff --git a/lib/mixins/properties-changed.js b/lib/mixins/properties-changed.js index d3ad2a7997..ce153fb5db 100644 --- a/lib/mixins/properties-changed.js +++ b/lib/mixins/properties-changed.js @@ -11,7 +11,7 @@ import '../utils/boot.js'; import { dedupingMixin } from '../utils/mixin.js'; import { microTask } from '../utils/async.js'; -import { wrapIfNeeded } from '../utils/wrap.js'; +import { wrap } from '../utils/wrap.js'; /** @const {!AsyncInterface} */ const microtask = microTask; @@ -517,7 +517,8 @@ export const PropertiesChanged = dedupingMixin( _valueToNodeAttribute(node, value, attribute) { const str = this._serializeValue(value); if (attribute === 'class' || attribute === 'name' || attribute === 'slot') { - node = /** @type {?Element} */(wrapIfNeeded(node)); + // Note, must always wrap here since this may be an unpatched node. + node = /** @type {?Element} */(wrap(node)); } if (str === undefined) { node.removeAttribute(attribute); diff --git a/test/runner.html b/test/runner.html index f6975ce734..56993de3b1 100644 --- a/test/runner.html +++ b/test/runner.html @@ -80,6 +80,7 @@ 'unit/array-selector.html', 'unit/polymer-dom.html', 'unit/polymer-dom-nopatch.html', + 'unit/polymer-dom-nopatch.html?noPatch=on-demand', 'unit/polymer-dom-observeNodes.html', 'unit/flattened-nodes-observer.html', // TODO: substitute for equivalent es6 import tests @@ -98,7 +99,8 @@ 'unit/legacy-undefined.html', // 'unit/multi-style.html' 'unit/class-properties.html', - 'unit/styling-scoped-nopatch.html' + 'unit/styling-scoped-nopatch.html', + 'unit/styling-scoped-nopatch.html?noPatch=on-demand' ]; function combinations(suites, flags) { diff --git a/test/unit/polymer-dom-nopatch.html b/test/unit/polymer-dom-nopatch.html index 96faf2dfd1..23268dfd76 100644 --- a/test/unit/polymer-dom-nopatch.html +++ b/test/unit/polymer-dom-nopatch.html @@ -12,7 +12,7 @@ @@ -204,7 +204,7 @@ let p = el.$.scoped; while (p) { nodes.push(p); - p = dom(p).parentNode || ShadyDOM.wrap(p).host; + p = dom(p).parentNode || ShadyDOM.wrapIfNeeded(p).host; } nodes.push(window); const path = dom(e).path; diff --git a/test/unit/styling-scoped-nopatch.html b/test/unit/styling-scoped-nopatch.html index 2d9426c995..f68bbd136b 100644 --- a/test/unit/styling-scoped-nopatch.html +++ b/test/unit/styling-scoped-nopatch.html @@ -12,7 +12,7 @@ From d545c8f6bda4e6a932590114b41562922ee4533e Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Tue, 17 Sep 2019 16:25:38 -0700 Subject: [PATCH 12/20] ShadyDOM on-demand optimization: ensure all elements that mixin ElementMixn are pre-patched. --- lib/legacy/class.js | 10 ++-------- lib/mixins/element-mixin.js | 8 ++++++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/legacy/class.js b/lib/legacy/class.js index 1a0a440812..9db14be0be 100644 --- a/lib/legacy/class.js +++ b/lib/legacy/class.js @@ -457,12 +457,6 @@ function GenerateClassFromInfo(info, Base, behaviors) { return PolymerGenerated; } -let LegacyBase = HTMLElement; -if (window.ShadyDOM && window.ShadyDOM.patchOnDemand) { - LegacyBase = class extends HTMLElement {}; - ShadyDOM.patchElementProto(LegacyBase.prototype); -} - /** * Generates a class that extends `LegacyElement` based on the * provided info object. Metadata objects on the `info` object @@ -537,8 +531,8 @@ export const Class = function(info, mixin) { if (!info) { console.warn('Polymer.Class requires `info` argument'); } - let klass = mixin ? mixin(LegacyElementMixin(LegacyBase)) : - LegacyElementMixin(LegacyBase); + let klass = mixin ? mixin(LegacyElementMixin(HTMLElement)) : + LegacyElementMixin(HTMLElement); klass = GenerateClassFromInfo(info, klass, info.behaviors); // decorate klass with registration info klass.is = klass.prototype.is = info.is; diff --git a/lib/mixins/element-mixin.js b/lib/mixins/element-mixin.js index d31a939cbf..43ac6a36c5 100644 --- a/lib/mixins/element-mixin.js +++ b/lib/mixins/element-mixin.js @@ -100,6 +100,14 @@ export const builtCSS = window.ShadyCSS && window.ShadyCSS['cssBuild']; * @return {function(new:T)} superClass with mixin applied. */ export const ElementMixin = dedupingMixin(base => { + + // Optimizations for ShadyDOM's on-demand patching that pre-patches + // elements to work with ShadyDOM. + if (window.ShadyDOM && window.ShadyDOM.patchOnDemand) { + base = class extends base {}; + ShadyDOM.patchElementProto(base.prototype); + } + /** * @constructor * @implements {Polymer_PropertyEffects} From 55aa8c8cc5ad8341dfbad5caefc1b3f91673075a Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Wed, 18 Sep 2019 10:35:44 -0700 Subject: [PATCH 13/20] Add comment about wrapping events. --- lib/mixins/template-stamp.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/mixins/template-stamp.js b/lib/mixins/template-stamp.js index ee4bc903ce..e65855bbd1 100644 --- a/lib/mixins/template-stamp.js +++ b/lib/mixins/template-stamp.js @@ -552,6 +552,10 @@ export const TemplateStamp = dedupingMixin( * @override */ _addEventListenerToNode(node, eventName, handler) { + // TODO(sorvell): When ShadyDOM is in noPatch mode, events will not be + // properly scoped without using `ShadyDOM.wrap` here. If this becomes + // an issue, use wrap here. This hasn't yet come up as an issue and is + // an extremely slightly perf negative. node.addEventListener(eventName, handler); } From 3bc3cc361a241fb413dd6328752d15b569693264 Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Fri, 20 Sep 2019 09:27:38 -0700 Subject: [PATCH 14/20] `getRootNode` must be wrapped --- lib/utils/scope-subtree.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/utils/scope-subtree.js b/lib/utils/scope-subtree.js index b3341bd09d..f99a7fb724 100644 --- a/lib/utils/scope-subtree.js +++ b/lib/utils/scope-subtree.js @@ -9,7 +9,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN */ import './boot.js'; -import {wrapIfNeeded} from './wrap.js'; +import {wrap} from './wrap.js'; const ShadyDOM = window.ShadyDOM; const ShadyCSS = window.ShadyCSS; @@ -22,7 +22,7 @@ const ShadyCSS = window.ShadyCSS; * @return {boolean} True if node is in scope */ function sameScope(node, scope) { - return wrapIfNeeded(node).getRootNode() === scope; + return wrap(node).getRootNode() === scope; } /** @@ -50,7 +50,7 @@ export function scopeSubtree(container, shouldObserve = false) { } // capture correct scope for container const containerScope = ScopingShim['scopeForNode'](container); - const root = wrapIfNeeded(container).getRootNode(); + const root = wrap(container).getRootNode(); const scopify = (node) => { if (!sameScope(node, root)) { From 107dcc76b4254ea1bc4bfe358d60c1867cf1fbb2 Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Thu, 10 Oct 2019 16:05:11 -0700 Subject: [PATCH 15/20] Run all tests with ShadyDOM on-demand setting. --- test/runner.html | 6 +-- test/unit/dom-if.html | 8 ++++ test/unit/dom-repeat.html | 6 +++ test/unit/flattened-nodes-observer.html | 5 ++- test/unit/polymer-dom-nopatch.html | 8 ++-- test/unit/shady-content.html | 24 +++++----- test/unit/shady-dynamic.html | 60 +++++++++++++++---------- test/unit/shady-events.html | 12 +++++ test/unit/shady.html | 12 +++++ 9 files changed, 96 insertions(+), 45 deletions(-) diff --git a/test/runner.html b/test/runner.html index a224f21c73..1a3e2c90e0 100644 --- a/test/runner.html +++ b/test/runner.html @@ -79,7 +79,6 @@ 'unit/array-selector.html', 'unit/polymer-dom.html', 'unit/polymer-dom-nopatch.html', - 'unit/polymer-dom-nopatch.html?noPatch=on-demand', 'unit/polymer-dom-observeNodes.html', 'unit/flattened-nodes-observer.html', // TODO: substitute for equivalent es6 import tests @@ -98,8 +97,7 @@ 'unit/legacy-undefined.html', // 'unit/multi-style.html' 'unit/class-properties.html', - 'unit/styling-scoped-nopatch.html', - 'unit/styling-scoped-nopatch.html?noPatch=on-demand' + 'unit/styling-scoped-nopatch.html' ]; function combinations(suites, flags) { @@ -123,6 +121,8 @@ if (Element.prototype.attachShadow && Node.prototype.getRootNode) { flags.push('wc-shadydom=true'); } + // always test ShadyDOM on-demand patching + flags.push('wc-shadydom=true&wc-noPatch=on-demand'); // Both sd and ce are supported, force both polyfills if (flags.length === 2) { // ce + sd becomes a single test iteration. diff --git a/test/unit/dom-if.html b/test/unit/dom-if.html index 09d578a3e7..b4c698ed14 100644 --- a/test/unit/dom-if.html +++ b/test/unit/dom-if.html @@ -101,6 +101,11 @@ @@ -193,7 +193,7 @@ test('localTarget, rootTarget, path', function() { // skip if noPatch is not available - if (!window.ShadyDOM.wrap) { + if (!window.ShadyDOM || !window.ShadyDOM.wrap) { this.skip(); } var el = fixture('scoped'); @@ -204,7 +204,7 @@ let p = el.$.scoped; while (p) { nodes.push(p); - p = dom(p).parentNode || ShadyDOM.wrapIfNeeded(p).host; + p = dom(p).parentNode || window.ShadyDOM.wrapIfNeeded(p).host; } nodes.push(window); const path = dom(e).path; @@ -240,7 +240,7 @@ ShadyDOM.wrap(focusableInShadow.$.focusable).focus(); var rootNode = ShadyDOM.wrap(focusableInShadow).getRootNode(); assert.equal(dom(rootNode).activeElement, focusableInShadow); - assert.equal(dom(dom(focusableInShadow).shadowRoot).activeElement, focusableInShadow.$.focusable); + assert.equal(dom(ShadyDOM.wrap(focusableInShadow).shadowRoot).activeElement, focusableInShadow.$.focusable); }); }); diff --git a/test/unit/shady-content.html b/test/unit/shady-content.html index 969c65888a..75b27a2124 100644 --- a/test/unit/shady-content.html +++ b/test/unit/shady-content.html @@ -950,7 +950,7 @@ assert.equal(h1.firstElementChild, d); assert.deepEqual(h1.$.content.assignedNodes({flatten: true}), [d]); assert.equal(h2.childNodes.length, 0); - h2.appendChild(d); + ShadyDOM.wrap(h2).appendChild(d); ShadyDOM.flush(); assert.equal(h2.childNodes.length, 1); assert.equal(h2.firstChild, d); @@ -962,7 +962,7 @@ assert.equal(h1.firstElementChild, d); assert.deepEqual(h1.$.content.assignedNodes({flatten: true}), [d]); assert.equal(h2.childNodes.length, 0); - h2.appendChild(d); + ShadyDOM.wrap(h2).appendChild(d); ShadyDOM.flush(); assert.equal(h2.childNodes.length, 1); assert.equal(h2.firstChild, d); @@ -984,7 +984,7 @@ assert.equal(h1.firstElementChild, d); assert.deepEqual(h1.$.content.assignedNodes({flatten: true}), [d]); assert.equal(h2.childNodes.length, 0); - h2.appendChild(d); + ShadyDOM.wrap(h2).appendChild(d); ShadyDOM.flush(); assert.equal(h2.childNodes.length, 1); assert.equal(h2.firstChild, d); @@ -996,7 +996,7 @@ assert.equal(h1.firstElementChild, d); assert.deepEqual(h1.$.content.assignedNodes({flatten: true}), [d]); assert.equal(h2.childNodes.length, 0); - h2.appendChild(d); + ShadyDOM.wrap(h2).appendChild(d); ShadyDOM.flush(); assert.equal(h2.childNodes.length, 1); assert.equal(h2.firstChild, d); @@ -1096,7 +1096,7 @@ assert.equal(h1.firstElementChild, d); assert.deepEqual(h1.$.content.assignedNodes({flatten: true}), [d]); assert.equal(h2.childNodes.length, 0); - h2.appendChild(d); + ShadyDOM.wrap(h2).appendChild(d); ShadyDOM.flush(); assert.equal(h2.childNodes.length, 1); assert.equal(h2.firstChild, d); @@ -1108,7 +1108,7 @@ assert.equal(h1.firstElementChild, d); assert.deepEqual(h1.$.content.assignedNodes({flatten: true}), [d]); assert.equal(h2.childNodes.length, 0); - h2.appendChild(d); + ShadyDOM.wrap(h2).appendChild(d); ShadyDOM.flush(); assert.equal(h2.childNodes.length, 1); assert.equal(h2.firstChild, d); @@ -1130,7 +1130,7 @@ assert.equal(h1.firstElementChild, d); assert.deepEqual(h1.$.content.assignedNodes({flatten: true}), [d]); assert.equal(h2.childNodes.length, 0); - h2.appendChild(d); + ShadyDOM.wrap(h2).appendChild(d); ShadyDOM.flush(); assert.equal(h2.childNodes.length, 1); assert.equal(h2.firstChild, d); @@ -1142,7 +1142,7 @@ assert.equal(h1.firstElementChild, d); assert.deepEqual(h1.$.content.assignedNodes({flatten: true}), [d]); assert.equal(h2.childNodes.length, 0); - h2.appendChild(d); + ShadyDOM.wrap(h2).appendChild(d); ShadyDOM.flush(); assert.equal(h2.childNodes.length, 1); assert.equal(h2.firstChild, d); @@ -1163,7 +1163,7 @@ assert.equal(h1.firstElementChild, d); assert.deepEqual(h1.$.content.assignedNodes({flatten: true}), [d]); assert.equal(h2.childNodes.length, 0); - h2.appendChild(d); + ShadyDOM.wrap(h2).appendChild(d); ShadyDOM.flush(); assert.equal(h2.childNodes.length, 1); assert.equal(h2.firstChild, d); @@ -1175,7 +1175,7 @@ assert.equal(h1.firstElementChild, d); assert.deepEqual(h1.$.content.assignedNodes({flatten: true}), [d]); assert.equal(h2.childNodes.length, 0); - h2.appendChild(d); + ShadyDOM.wrap(h2).appendChild(d); ShadyDOM.flush(); assert.equal(h2.childNodes.length, 1); assert.equal(h2.firstChild, d); @@ -1197,7 +1197,7 @@ assert.equal(h1.firstElementChild, d); assert.deepEqual(h1.$.content.assignedNodes({flatten: true}), [d]); assert.equal(h2.childNodes.length, 0); - h2.appendChild(d); + ShadyDOM.wrap(h2).appendChild(d); ShadyDOM.flush(); assert.equal(h2.childNodes.length, 1); assert.equal(h2.firstChild, d); @@ -1209,7 +1209,7 @@ assert.equal(h1.firstElementChild, d); assert.deepEqual(h1.$.content.assignedNodes({flatten: true}), [d]); assert.equal(h2.childNodes.length, 0); - h2.appendChild(d); + ShadyDOM.wrap(h2).appendChild(d); ShadyDOM.flush(); assert.equal(h2.childNodes.length, 1); assert.equal(h2.firstChild, d); diff --git a/test/unit/shady-dynamic.html b/test/unit/shady-dynamic.html index 6c8d32240e..c16197343b 100644 --- a/test/unit/shady-dynamic.html +++ b/test/unit/shady-dynamic.html @@ -778,7 +778,7 @@ function createEnabledElement(tag) { var e = document.createElement(tag); document.body.appendChild(e); - document.body.removeChild(e); + ShadyDOM.wrap(document.body).removeChild(e); return e; } @@ -1025,6 +1025,10 @@ }); test('localDom.insertBefore first element results in minimal change', function() { + // Skip due to need to re-order polyfills + if (window.ShadyDOM && window.ShadyDOM.patchOnDemand) { + this.skip(); + } var children = testElement.root.childNodes; var rere = testElement.root.querySelector('x-rereproject'); assert.equal(rere.attachedCount, 1); @@ -1171,8 +1175,8 @@ test('querySelector', function() { var test = document.querySelector('x-test'); - var rere = document.querySelector('x-rereproject'); - var projected = document.querySelector('#projected'); + var rere = ShadyDOM.wrap(document).querySelector('x-rereproject'); + var projected = ShadyDOM.wrap(document).querySelector('#projected'); assert.ok(test); assert.notOk(rere); assert.notOk(projected); @@ -1211,6 +1215,10 @@ }); test('event.target', function() { + // Skip due to need to re-order polyfills + if (window.ShadyDOM && window.ShadyDOM.patchOnDemand) { + this.skip(); + } var e = document.querySelector('x-outer'); var b = document.querySelector('x-outer button'); b.dispatchEvent(new Event('click', {bubbles: true})); @@ -1275,6 +1283,10 @@ }); test('styling: flush causes attached and re-flushes if necessary', function(done) { + // Skip due to need to re-order polyfills + if (window.ShadyDOM && window.ShadyDOM.patchOnDemand) { + this.skip(); + } var a = document.createElement('x-attach1'); document.body.appendChild(a); flush(); @@ -1888,67 +1900,67 @@ var c2 = createEnabledElement('x-compose'); c1.$.project.appendChild(test); flush(); - assert.equal(test.getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root'); c2.$.project.appendChild(test); flush(); - assert.equal(test.getRootNode(), c2.root, 'getOwnerRoot not correctly reset when element moved to different root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), c2.root, 'getOwnerRoot not correctly reset when element moved to different root'); c1.appendChild(test); - assert.equal(test.getRootNode(), c1, 'getOwnerRoot incorrect for child moved from a root to no root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), c1, 'getOwnerRoot incorrect for child moved from a root to no root'); }); test('getRootNode when out of tree', function() { var test = document.createElement('div'); - assert.equal(test.getRootNode(), test, 'getOwnerRoot incorrect when not in root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), test, 'getOwnerRoot incorrect when not in root'); var c1 = createEnabledElement('x-compose'); var project = c1.$.project; project.appendChild(test); flush(); - assert.equal(test.getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root'); project.removeChild(test); flush(); - assert.equal(test.getRootNode(), test, 'getOwnerRoot incorrect for child moved from a root to no root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), test, 'getOwnerRoot incorrect for child moved from a root to no root'); project.appendChild(test); flush(); - assert.equal(test.getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root'); }); test('getRootNode when out of tree and adding subtree', function() { var container = document.createDocumentFragment(); var test = document.createElement('div'); container.appendChild(test); - assert.equal(test.getRootNode(), container, 'getOwnerRoot incorrect when not in root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), container, 'getOwnerRoot incorrect when not in root'); var c1 = createEnabledElement('x-compose'); var project = c1.$.project; project.appendChild(container); flush(); - assert.equal(test.getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root'); project.removeChild(test); flush(); - assert.equal(test.getRootNode(), test, 'getOwnerRoot incorrect for child moved from a root to no root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), test, 'getOwnerRoot incorrect for child moved from a root to no root'); project.appendChild(test); flush(); - assert.equal(test.getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root'); }); test('getRootNode, subtree', function() { var test = document.createElement('div'); var testChild = document.createElement('div'); test.appendChild(testChild); - assert.equal(test.getRootNode(), test, 'getOwnerRoot incorrect when not in root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), test, 'getOwnerRoot incorrect when not in root'); var c1 = createEnabledElement('x-compose'); var project = c1.$.project; project.appendChild(test); flush(); - assert.equal(test.getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root'); - assert.equal(testChild.getRootNode(), c1.root, 'getOwnerRoot incorrect for sub-child added to element in root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root'); + assert.equal(ShadyDOM.wrap(testChild).getRootNode(), c1.root, 'getOwnerRoot incorrect for sub-child added to element in root'); project.removeChild(test); flush(); - assert.equal(test.getRootNode(), test, 'getOwnerRoot incorrect for child moved from a root to no root'); - assert.equal(testChild.getRootNode(), test, 'getOwnerRoot incorrect for sub-child moved from a root to no root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), test, 'getOwnerRoot incorrect for child moved from a root to no root'); + assert.equal(ShadyDOM.wrap(testChild).getRootNode(), test, 'getOwnerRoot incorrect for sub-child moved from a root to no root'); project.appendChild(test); flush(); - assert.equal(test.getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root'); - assert.equal(testChild.getRootNode(), c1.root, 'getOwnerRoot incorrect for sub-child added to element in root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root'); + assert.equal(ShadyDOM.wrap(testChild).getRootNode(), c1.root, 'getOwnerRoot incorrect for sub-child added to element in root'); }); test('getRootNode (paper-ripple use case)', function() { @@ -1960,12 +1972,12 @@ var c2 = createEnabledElement('x-compose'); c1.$.project.appendChild(test); flush(); - assert.equal(test.getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), c1.root, 'getOwnerRoot incorrect for child added to element in root'); c2.$.project.appendChild(test); flush(); - assert.equal(test.getRootNode(), c2.root, 'getOwnerRoot not correctly reset when element moved to different root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), c2.root, 'getOwnerRoot not correctly reset when element moved to different root'); c1.appendChild(test); - assert.equal(test.getRootNode(), c1, 'getOwnerRoot incorrect for child moved from a root to no root'); + assert.equal(ShadyDOM.wrap(test).getRootNode(), c1, 'getOwnerRoot incorrect for child moved from a root to no root'); }); test('getDestinationInsertionPoints on non-distributable element', function() { diff --git a/test/unit/shady-events.html b/test/unit/shady-events.html index 3a416c6195..2a02a8efe4 100644 --- a/test/unit/shady-events.html +++ b/test/unit/shady-events.html @@ -211,6 +211,10 @@ }); test('composed focus and blur events retarget up tree', function() { + // Skip due to need to wrap event handler + if (window.ShadyDOM && window.ShadyDOM.patchOnDemand) { + this.skip(); + } var el = fixture('focus'); el.fireComposed(); assert.equal(el.events.length, 2); @@ -232,6 +236,10 @@ }); test('composed relatedTarget retargets', function() { + // Skip due to need to wrap event handler + if (window.ShadyDOM && window.ShadyDOM.patchOnDemand) { + this.skip(); + } var els = fixture('relatedtarget'); var a = els[0]; var b = els[1]; @@ -254,6 +262,10 @@ }); test('capturing event listeners fire correctly for focus and blur', function() { + // Skip due to need to wrap event handler + if (window.ShadyDOM && window.ShadyDOM.patchOnDemand) { + this.skip(); + } var el = fixture('focus'); var timeStamp; el.addEventListener('focus', function(e) { diff --git a/test/unit/shady.html b/test/unit/shady.html index a0f0cde48e..387d4b9b18 100644 --- a/test/unit/shady.html +++ b/test/unit/shady.html @@ -111,6 +111,10 @@ test('Reproject', function() { + // Skip due to need to re-order polyfills + if (window.ShadyDOM && window.ShadyDOM.patchOnDemand) { + this.skip(); + } var host = document.createElement('x-content-test'); document.body.appendChild(host); host.innerHTML = ''; @@ -368,6 +372,10 @@ }); test('querySelectorAll (shadowRoot)', function() { + // Skip due to need to re-order polyfills + if (window.ShadyDOM && window.ShadyDOM.patchOnDemand) { + this.skip(); + } host.innerHTML = '
'; host.shadowRoot.innerHTML = '' + ''; @@ -386,6 +394,10 @@ }); test('querySelectorAll (light dom)', function() { + // Skip due to need to re-order polyfills + if (window.ShadyDOM && window.ShadyDOM.patchOnDemand) { + this.skip(); + } host.innerHTML = '
'; var hostLightMain = getComposedChildAtIndex(host, 0); host.shadowRoot.innerHTML = '' + From d0acc283fb5570be1f973d0b8f66302e5ee64e7a Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Thu, 10 Oct 2019 17:10:08 -0700 Subject: [PATCH 16/20] Add tests for `lazyDefine` setting --- test/runner.html | 3 +- test/unit/lazy-define.html | 96 +++++++++++++++++++++++++++ test/unit/styling-scoped-nopatch.html | 2 +- 3 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 test/unit/lazy-define.html diff --git a/test/runner.html b/test/runner.html index 1a3e2c90e0..318e8be86e 100644 --- a/test/runner.html +++ b/test/runner.html @@ -97,7 +97,8 @@ 'unit/legacy-undefined.html', // 'unit/multi-style.html' 'unit/class-properties.html', - 'unit/styling-scoped-nopatch.html' + 'unit/styling-scoped-nopatch.html', + 'unit/lazy-define.html' ]; function combinations(suites, flags) { diff --git a/test/unit/lazy-define.html b/test/unit/lazy-define.html new file mode 100644 index 0000000000..0867b0b456 --- /dev/null +++ b/test/unit/lazy-define.html @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/unit/styling-scoped-nopatch.html b/test/unit/styling-scoped-nopatch.html index f68bbd136b..2e02085146 100644 --- a/test/unit/styling-scoped-nopatch.html +++ b/test/unit/styling-scoped-nopatch.html @@ -12,7 +12,7 @@ From 13186e788fcf4bee3a08f4325362df80087db23c Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Thu, 10 Oct 2019 17:45:33 -0700 Subject: [PATCH 17/20] wrapIfNeeded fall back to wrap --- lib/utils/wrap.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/wrap.js b/lib/utils/wrap.js index 4c99d4eb0f..1896676a74 100644 --- a/lib/utils/wrap.js +++ b/lib/utils/wrap.js @@ -30,4 +30,4 @@ export const wrap = window['ShadyDOM'] && window['ShadyDOM']['wrap'] ? * when setting `textContent` or `innerHTML`. */ export const wrapIfNeeded = window['ShadyDOM'] && window['ShadyDOM']['wrapIfNeeded'] ? - window['ShadyDOM']['wrapIfNeeded'] : n => n; \ No newline at end of file + window['ShadyDOM']['wrapIfNeeded'] : wrap; \ No newline at end of file From 94eea4a4e196fd8e18f495a2022302d29528d79b Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Thu, 10 Oct 2019 18:00:51 -0700 Subject: [PATCH 18/20] Use wrap instead of ShadyDOM.wrap --- test/unit/dom-if.html | 9 ++--- test/unit/dom-repeat.html | 8 ++-- test/unit/flattened-nodes-observer.html | 3 +- test/unit/polymer-dom-nopatch.html | 9 +++-- test/unit/shady-content.html | 25 +++++++------ test/unit/shady-dynamic.html | 49 +++++++++++++------------ 6 files changed, 52 insertions(+), 51 deletions(-) diff --git a/test/unit/dom-if.html b/test/unit/dom-if.html index b4c698ed14..6ebb71ba10 100644 --- a/test/unit/dom-if.html +++ b/test/unit/dom-if.html @@ -101,10 +101,9 @@