Behavior property copying fixes

* ensure element has `is` on prototype early as this is sometimes checked in user code.
* ensure properties copied onto elements from info/behaviors are forced to configurable so they can be re-configured by later behaviors.
* add `_noAccessors` optimization for faster property copying
This commit is contained in:
Steven Orvell
2018-11-09 18:18:06 -08:00
parent 65a3149b8c
commit 310c7ead07
2 changed files with 43 additions and 7 deletions

View File

@@ -30,7 +30,8 @@ const excludeOnInfo = {
beforeRegister: true, beforeRegister: true,
registered: true, registered: true,
attributeChanged: true, attributeChanged: true,
behaviors: true behaviors: true,
_noAccessors: true
}; };
const excludeOnBehaviors = Object.assign({ const excludeOnBehaviors = Object.assign({
@@ -41,13 +42,19 @@ const excludeOnBehaviors = Object.assign({
}, excludeOnInfo); }, excludeOnInfo);
function copyProperties(source, target, excludeProps) { function copyProperties(source, target, excludeProps) {
const noAccessors = source._noAccessors;
for (let p in source) { for (let p in source) {
// 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 excludeProps)) { if (!(p in excludeProps)) {
let pd = Object.getOwnPropertyDescriptor(source, p); if (noAccessors) {
if (pd) { target[p] = source[p];
Object.defineProperty(target, p, pd); } else {
let pd = Object.getOwnPropertyDescriptor(source, p);
if (pd) {
// ensure property is configurable so that a later behavior can
// re-configure it.
pd.configurable = true;
Object.defineProperty(target, p, pd);
}
} }
} }
} }
@@ -498,6 +505,6 @@ export const Class = function(info, mixin) {
LegacyElementMixin(HTMLElement); LegacyElementMixin(HTMLElement);
klass = GenerateClassFromInfo(info, klass, info.behaviors); klass = GenerateClassFromInfo(info, klass, info.behaviors);
// decorate klass with registration info // decorate klass with registration info
klass.is = info.is; klass.is = klass.prototype.is = info.is;
return klass; return klass;
}; };

View File

@@ -379,6 +379,20 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
behaviors: [window.BehaviorA] behaviors: [window.BehaviorA]
}); });
Polymer({
is: 'no-accessors-behavior',
behaviors: [{
_noAccessors: true,
properties: {
nug: String
},
foo: function() {},
bar: true
}],
_noAccessors: true,
zot: 'zot'
});
</script> </script>
<test-fixture id="single"> <test-fixture id="single">
@@ -447,6 +461,12 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
</template> </template>
</test-fixture> </test-fixture>
<test-fixture id="no-accessors-behavior">
<template>
<no-accessors-behavior></no-accessors-behavior>
</template>
</test-fixture>
<script type="module"> <script type="module">
import { Polymer } from '../../polymer-legacy.js'; import { Polymer } from '../../polymer-legacy.js';
@@ -501,6 +521,15 @@ suite('single behavior element', function() {
assert.notOk(el.listeners); assert.notOk(el.listeners);
}); });
test('properties on objects marked with `_noAccessors` are copied to class', function() {
const el = fixture('no-accessors-behavior');
assert.ok(el.foo);
assert.isTrue(el.bar);
assert.equal(el.zot, 'zot');
el.setAttribute('nug', 'nug');
assert.equal(el.nug, 'nug');
});
}); });
suite('behavior.registered', function() { suite('behavior.registered', function() {