From d5e0043abcb2c557fbadc5f7535033a5a03e28c1 Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Tue, 6 Nov 2018 15:59:23 -0800 Subject: [PATCH] Manually merge changes from #5418 --- lib/legacy/class.js | 154 ++++---- lib/legacy/legacy-element-mixin.js | 7 +- test/unit/behaviors.html | 562 ++++++++++++++------------ test/unit/mixin-behaviors.html | 611 ++++++++++++++++------------- 4 files changed, 733 insertions(+), 601 deletions(-) diff --git a/lib/legacy/class.js b/lib/legacy/class.js index a4c4d576da..05ad6b2f3b 100644 --- a/lib/legacy/class.js +++ b/lib/legacy/class.js @@ -20,20 +20,20 @@ const metaProps = { attributeChanged: true, }; -const noBehaviorCopyProps = Object.assign({ +const excludeProps = Object.assign({ behaviors: true }, metaProps); -const memoizedProps = Object.assign({ +const lifecycleProps = Object.assign({ listeners: true, hostAttributes: true }, metaProps); function copyProperties(source, target) { for (let p in source) { - // NOTE: cannot copy `noBehaviorCopyProps` methods onto prototype at least because + // NOTE: cannot copy `excludeProps` methods onto prototype at least because // `super.ready` must be called and is not included in the user fn. - if (!(p in noBehaviorCopyProps)) { + if (!(p in excludeProps)) { let pd = Object.getOwnPropertyDescriptor(source, p); if (pd) { Object.defineProperty(target, p, pd); @@ -91,24 +91,18 @@ export function mixinBehaviors(behaviors, klass) { // If lifecycle is called (super then me), order is // (1) C.created, (2) A.created, (3) B.created, (4) element.created // (again same as 1.x) -function copyBehaviorProperties(behaviors, klass) { - const meta = {}; - const superMeta = klass.prototype.__behaviorMetaProps; - if (behaviors) { - klass.prototype.__behaviorMetaProps = meta; - for (let i=0; i= 0; i--) { const hostAttributes = list[i]; @@ -322,6 +327,7 @@ function GenerateClassFromInfo(info, Base, behaviors) { } } } + super._ensureAttributes(); } /** @@ -329,49 +335,41 @@ function GenerateClassFromInfo(info, Base, behaviors) { */ ready() { super.ready(); - let list = this.__behaviorMetaProps.ready; + let list = lifecycle.ready; if (list) { for (let i=0; i < list.length; i++) { list[i].call(this); } } - if (info.ready) { - info.ready.call(this); - } } /** * @return {void} */ attached() { - let list = this.__behaviorMetaProps.attached; + super.attached(); + let list = lifecycle.attached; if (list) { for (let i=0; i < list.length; i++) { list[i].call(this); } } - if (info.attached) { - info.attached.call(this); - } } /** * @return {void} */ detached() { - let list = this.__behaviorMetaProps.detached; + super.detached(); + let list = lifecycle.detached; if (list) { for (let i=0; i < list.length; i++) { list[i].call(this); } } - if (info.detached) { - info.detached.call(this); - } } /** - * * Implements native Custom Elements `attributeChangedCallback` to * set an attribute value to a property via `_attributeToProperty`. * @@ -381,20 +379,17 @@ function GenerateClassFromInfo(info, Base, behaviors) { * @return {void} */ attributeChanged(name, old, value) { - let list = this.__behaviorMetaProps.attributeChanged; + super.attributeChanged(); + let list = lifecycle.attributeChanged; if (list) { for (let i=0; i < list.length; i++) { list[i].call(this, name, old, value); } } - if (info.attributeChanged) { - info.attributeChanged.call(this, name, old, value); - } } - } - // apply behaviors + // apply behaviors, note actual copying is done lazily at first instance creation if (behaviors) { // NOTE: ensure the behavior is extending a class with // legacy element api. This is necessary since behaviors expect to be able @@ -402,12 +397,11 @@ function GenerateClassFromInfo(info, Base, behaviors) { if (!Array.isArray(behaviors)) { behaviors = [behaviors]; } - let superBehaviors = PolymerGenerated.prototype.behaviors; + let superBehaviors = Base.prototype.behaviors; // get flattened, deduped list of behaviors *not* already on super class - behaviors = flattenBehaviors(behaviors, null, superBehaviors); + behaviorList = flattenBehaviors(behaviors, null, superBehaviors); PolymerGenerated.prototype.behaviors = superBehaviors ? - superBehaviors.concat(behaviors) : behaviors; - PolymerGenerated.prototype.__behaviors = behaviors; + superBehaviors.concat(behaviors) : behaviorList; } PolymerGenerated.generatedFrom = info; diff --git a/lib/legacy/legacy-element-mixin.js b/lib/legacy/legacy-element-mixin.js index d3ffdcba7e..188106bf50 100644 --- a/lib/legacy/legacy-element-mixin.js +++ b/lib/legacy/legacy-element-mixin.js @@ -92,7 +92,12 @@ export const LegacyElementMixin = dedupingMixin((base) => { } static _finalizeClass() { - this.prototype._registered(); + // Note, call `_registered` only if this specific prototype has + // an implementation; this ensures `_registered` is not called + // on extenders that do not implement it. + if (this.prototype.hasOwnProperty('_registered')) { + this.prototype._registered(); + } super._finalizeClass(); } diff --git a/test/unit/behaviors.html b/test/unit/behaviors.html index 50a03860bb..0451a95407 100644 --- a/test/unit/behaviors.html +++ b/test/unit/behaviors.html @@ -30,316 +30,348 @@ @@ -396,6 +428,12 @@ + + + + + }; + + import { mixinBehaviors } from '../../lib/legacy/class.js'; + import { PolymerElement } from '../../polymer-element.js'; + customElements.define('single-behavior', + mixinBehaviors(window.BehaviorA, PolymerElement)); + + import { mixinBehaviors } from '../../lib/legacy/class.js'; + import { PolymerElement } from '../../polymer-element.js'; + customElements.define('lifecycle-behavior', + mixinBehaviors([window.LifeCycleBehavior1, + window.LifeCycleBehavior2], PolymerElement)); + + - constructor() { - super(); - } + - - - - + customElements.define('extended-behaviors', extended); + + window.registerBehavior2 ={ + prop2: true, + registered: function() { + this.registeredCount++; + } + }; + + window.registerBehavior3 ={ + prop3: true, + registered: function() { + this.registeredCount++; + } + }; + + class BehaviorRegistered extends mixinBehaviors([ + window.registerBehavior1, + window.registerBehavior2, + window.registerBehavior3 + ], PolymerElement) { + + static get is() { return 'behavior-registered';} + + _initializeProperties() { + super._initializeProperties(); + this.registeredCount++; + } + } + + customElements.define(BehaviorRegistered.is, BehaviorRegistered); + + class BehaviorRegisteredExt extends BehaviorRegistered { + static get is() { return 'behavior-registered-ext';} + } + + customElements.define(BehaviorRegisteredExt.is, BehaviorRegisteredExt); + + + + + + + + + + +