Updates ported from perf-opt branch

Merging manually from https://github.com/Polymer/polymer/pull/5418.
This commit is contained in:
Steven Orvell
2018-10-31 19:21:04 -07:00
parent b6ae42d4c5
commit a08c9840d6
9 changed files with 249 additions and 206 deletions

View File

@@ -11,7 +11,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
import { LegacyElementMixin } from './legacy-element-mixin.js'; import { LegacyElementMixin } from './legacy-element-mixin.js';
import { DomModule } from '../elements/dom-module.js'; import { DomModule } from '../elements/dom-module.js';
let metaProps = { const metaProps = {
attached: true, attached: true,
detached: true, detached: true,
ready: true, ready: true,
@@ -19,15 +19,22 @@ let metaProps = {
beforeRegister: true, beforeRegister: true,
registered: true, registered: true,
attributeChanged: true, attributeChanged: true,
// meta objects
behaviors: true
}; };
const noBehaviorCopyProps = Object.assign({
behaviors: true
}, metaProps);
const memoizedProps = Object.assign({
listeners: true,
hostAttributes: true
}, metaProps);
function copyProperties(source, target) { function copyProperties(source, target) {
for (let p in source) { for (let p in source) {
// NOTE: cannot copy `metaProps` methods onto prototype at least because // NOTE: cannot copy `noBehaviorCopyProps` methods onto prototype at least because
// `super.ready` must be called and is not included in the user fn. // `super.ready` must be called and is not included in the user fn.
if (!(p in metaProps)) { if (!(p in noBehaviorCopyProps)) {
let pd = Object.getOwnPropertyDescriptor(source, p); let pd = Object.getOwnPropertyDescriptor(source, p);
if (pd) { if (pd) {
Object.defineProperty(target, p, pd); Object.defineProperty(target, p, pd);
@@ -36,7 +43,6 @@ function copyProperties(source, target) {
} }
} }
// TODO(sorvell): this breaks `Polymer.mixinBehaviors`; should fix via factoring
/** /**
* Applies a "legacy" behavior or array of behaviors to the provided class. * Applies a "legacy" behavior or array of behaviors to the provided class.
* *
@@ -53,37 +59,7 @@ function copyProperties(source, target) {
* @suppress {invalidCasts, checkTypes} * @suppress {invalidCasts, checkTypes}
*/ */
export function mixinBehaviors(behaviors, klass) { export function mixinBehaviors(behaviors, klass) {
if (behaviors) { return GenerateClassFromInfo({}, LegacyElementMixin(klass), behaviors);
klass = applyBehaviors(behaviors, klass);
}
// provides behaviors functionality
return GenerateClassFromInfo({}, klass);
}
function applyBehaviors(behaviors, klass) {
if (!behaviors) {
klass = /** @type {HTMLElement} */(klass); // eslint-disable-line no-self-assign
return klass;
}
// NOTE: ensure the behavior is extending a class with
// legacy element api. This is necessary since behaviors expect to be able
// to access 1.x legacy api.
klass = class extends LegacyElementMixin(klass) {};
if (!Array.isArray(behaviors)) {
behaviors = [behaviors];
}
let superBehaviors = klass.prototype.behaviors;
// get flattened, deduped list of behaviors *not* already on super class
behaviors = flattenBehaviors(behaviors, null, superBehaviors);
// mixin new behaviors
klass = _applyBehaviors(behaviors, klass);
if (superBehaviors) {
behaviors = superBehaviors.concat(behaviors);
}
// Set behaviors on prototype for BC...
klass.prototype.behaviors = behaviors;
return klass;
} }
// NOTE: // NOTE:
@@ -116,15 +92,26 @@ function applyBehaviors(behaviors, klass) {
// If lifecycle is called (super then me), order is // If lifecycle is called (super then me), order is
// (1) C.created, (2) A.created, (3) B.created, (4) element.created // (1) C.created, (2) A.created, (3) B.created, (4) element.created
// (again same as 1.x) // (again same as 1.x)
function _applyBehaviors(behaviors, klass) { function copyBehaviorProperties(behaviors, klass) {
for (let i=0; i<behaviors.length; i++) { const meta = {};
let b = behaviors[i]; const superMeta = klass.prototype.__behaviorMetaProps;
if (b) { if (behaviors) {
Array.isArray(b) ? _applyBehaviors(b, klass) : klass.prototype.__behaviorMetaProps = meta;
copyProperties(b, klass.prototype); for (let i=0; i<behaviors.length; i++) {
copyProperties(behaviors[i], klass.prototype);
memoizeBehaviorMetaProps(meta, behaviors[i], superMeta);
}
}
klass.prototype.__behaviorMetaProps = meta;
}
function memoizeBehaviorMetaProps(meta, behavior, superMeta) {
for (let p in memoizedProps) {
if (behavior[p]) {
meta[p] = meta[p] || (superMeta && superMeta[p] ? superMeta[p].slice() : []);
meta[p].push(behavior[p]);
} }
} }
return klass;
} }
/** /**
@@ -202,20 +189,20 @@ function mergeProperties(a, b) {
/** /**
* @param {!PolymerInit} info Polymer info object * @param {!PolymerInit} info Polymer info object
* @param {function(new:HTMLElement)} Base base class to extend with info object * @param {function(new:HTMLElement)} Base base class to extend with info object
* @param {Object} behaviors behaviors to copy into the element
* @return {function(new:HTMLElement)} Generated class * @return {function(new:HTMLElement)} Generated class
* @suppress {checkTypes} * @suppress {checkTypes}
* @private * @private
*/ */
function GenerateClassFromInfo(info, Base) { function GenerateClassFromInfo(info, Base, behaviors) {
/** @private */ /** @private */
class PolymerGenerated extends Base { class PolymerGenerated extends Base {
static get properties() { static get properties() {
const properties = {}; const properties = {};
if (this.prototype.behaviors) { if (this.prototype.__behaviors) {
for (let i=0, b; i < this.prototype.behaviors.length; i++) { for (let i=0, b; i < this.prototype.__behaviors.length; i++) {
b = this.prototype.behaviors[i]; b = this.prototype.__behaviors[i];
mergeProperties(properties, b.properties); mergeProperties(properties, b.properties);
} }
} }
@@ -225,9 +212,9 @@ function GenerateClassFromInfo(info, Base) {
static get observers() { static get observers() {
let observers = []; let observers = [];
if (this.prototype.behaviors) { if (this.prototype.__behaviors) {
for (let i=0, b; i < this.prototype.behaviors.length; i++) { for (let i=0, b; i < this.prototype.__behaviors.length; i++) {
b = this.prototype.behaviors[i]; b = this.prototype.__behaviors[i];
if (b.observers) { if (b.observers) {
observers = observers.concat(b.observers); observers = observers.concat(b.observers);
} }
@@ -243,12 +230,10 @@ function GenerateClassFromInfo(info, Base) {
* @return {void} * @return {void}
*/ */
created() { created() {
if (this.behaviors) { const list = this.__behaviorMetaProps.created;
for (let i=0, b; i < this.behaviors.length; i++) { if (list) {
b = this.behaviors[i]; for (let i=0; i < list.length; i++) {
if (b.created) { list[i].call(this);
b.created.call(this);
}
} }
} }
if (info.created) { if (info.created) {
@@ -259,30 +244,39 @@ function GenerateClassFromInfo(info, Base) {
/** /**
* @return {void} * @return {void}
*/ */
// Called on element prototype
_registered() { _registered() {
/* NOTE: `beforeRegister` is called here for bc, but the behavior /* NOTE: `beforeRegister` is called here for bc, but the behavior
is different than in 1.x. In 1.0, the method was called *after* is different than in 1.x. In 1.0, the method was called *after*
mixing prototypes together but *before* processing of meta-objects. mixing prototypes together but *before* processing of meta-objects.
However, dynamic effects can still be set here and can be done either However, dynamic effects can still be set here and can be done either
in `beforeRegister` or `registered`. It is no longer possible to set in `beforeRegister` or `registered`. It is no longer possible to set
`is` in `beforeRegister` as you could in 1.x. `is` in `beforeRegister` as you could in 1.x.
*/ */
if (this.behaviors) { const proto = this;
for (let i=0, b; i < this.behaviors.length; i++) { if (proto.hasOwnProperty('__behaviors')) {
b = this.behaviors[i]; copyBehaviorProperties(proto.__behaviors, proto.constructor);
if (b.beforeRegister) { }
b.beforeRegister.call(Object.getPrototypeOf(this)); proto.__behaviorMetaProps = proto.__behaviorMetaProps || {};
} copyProperties(info, proto);
if (b.registered) { // Note, previously these were interleaved.
b.registered.call(Object.getPrototypeOf(this)); let list = proto.__behaviorMetaProps.beforeRegister;
} if (list) {
for (let i=0; i < list.length; i++) {
list[i].call(proto);
}
}
list = proto.__behaviorMetaProps.registered;
if (list) {
for (let i=0; i < list.length; i++) {
list[i].call(proto);
} }
} }
if (info.beforeRegister) { if (info.beforeRegister) {
info.beforeRegister.call(Object.getPrototypeOf(this)); info.beforeRegister.call(proto);
} }
if (info.registered) { if (info.registered) {
info.registered.call(Object.getPrototypeOf(this)); info.registered.call(proto);
} }
} }
@@ -290,12 +284,13 @@ function GenerateClassFromInfo(info, Base) {
* @return {void} * @return {void}
*/ */
_applyListeners() { _applyListeners() {
if (this.behaviors) { const list = this.__behaviorMetaProps.listeners;
for (let i=0, b; i < this.behaviors.length; i++) { if (list) {
b = this.behaviors[i]; for (let i=0; i < list.length; i++) {
if (b.listeners) { const listeners = list[i];
for (let l in b.listeners) { if (listeners) {
this._addMethodEventListenerToNode(this, l, b.listeners[l]); for (let l in listeners) {
this._addMethodEventListenerToNode(this, l, listeners[l]);
} }
} }
} }
@@ -319,14 +314,13 @@ function GenerateClassFromInfo(info, Base) {
this._ensureAttribute(a, info.hostAttributes[a]); this._ensureAttribute(a, info.hostAttributes[a]);
} }
} }
if (this.behaviors) { const list = this.__behaviorMetaProps.hostAttributes;
for (let i=this.behaviors.length-1, b; i >= 0; i--) { if (list) {
b = this.behaviors[i]; for (let i=list.length-1; i >= 0; i--) {
if (b.hostAttributes) { const hostAttributes = list[i];
for (let a in b.hostAttributes) { for (let a in hostAttributes) {
this._ensureAttribute(a, b.hostAttributes[a]); this._ensureAttribute(a, hostAttributes[a]);
} }
}
} }
} }
} }
@@ -336,12 +330,10 @@ function GenerateClassFromInfo(info, Base) {
*/ */
ready() { ready() {
super.ready(); super.ready();
if (this.behaviors) { let list = this.__behaviorMetaProps.ready;
for (let i=0, b; i < this.behaviors.length; i++) { if (list) {
b = this.behaviors[i]; for (let i=0; i < list.length; i++) {
if (b.ready) { list[i].call(this);
b.ready.call(this);
}
} }
} }
if (info.ready) { if (info.ready) {
@@ -353,12 +345,10 @@ function GenerateClassFromInfo(info, Base) {
* @return {void} * @return {void}
*/ */
attached() { attached() {
if (this.behaviors) { let list = this.__behaviorMetaProps.attached;
for (let i=0, b; i < this.behaviors.length; i++) { if (list) {
b = this.behaviors[i]; for (let i=0; i < list.length; i++) {
if (b.attached) { list[i].call(this);
b.attached.call(this);
}
} }
} }
if (info.attached) { if (info.attached) {
@@ -370,12 +360,10 @@ function GenerateClassFromInfo(info, Base) {
* @return {void} * @return {void}
*/ */
detached() { detached() {
if (this.behaviors) { let list = this.__behaviorMetaProps.detached;
for (let i=0, b; i < this.behaviors.length; i++) { if (list) {
b = this.behaviors[i]; for (let i=0; i < list.length; i++) {
if (b.detached) { list[i].call(this);
b.detached.call(this);
}
} }
} }
if (info.detached) { if (info.detached) {
@@ -384,6 +372,7 @@ function GenerateClassFromInfo(info, Base) {
} }
/** /**
*
* Implements native Custom Elements `attributeChangedCallback` to * Implements native Custom Elements `attributeChangedCallback` to
* set an attribute value to a property via `_attributeToProperty`. * set an attribute value to a property via `_attributeToProperty`.
* *
@@ -393,24 +382,37 @@ function GenerateClassFromInfo(info, Base) {
* @return {void} * @return {void}
*/ */
attributeChanged(name, old, value) { attributeChanged(name, old, value) {
if (this.behaviors) { let list = this.__behaviorMetaProps.attributeChanged;
for (let i=0, b; i < this.behaviors.length; i++) { if (list) {
b = this.behaviors[i]; for (let i=0; i < list.length; i++) {
if (b.attributeChanged) { list[i].call(this, name, old, value);
b.attributeChanged.call(this, name, old, value);
}
} }
} }
if (info.attributeChanged) { if (info.attributeChanged) {
info.attributeChanged.call(this, name, old, value); info.attributeChanged.call(this, name, old, value);
} }
} }
}
// apply behaviors
if (behaviors) {
// NOTE: ensure the behavior is extending a class with
// legacy element api. This is necessary since behaviors expect to be able
// to access 1.x legacy api.
if (!Array.isArray(behaviors)) {
behaviors = [behaviors];
}
let superBehaviors = PolymerGenerated.prototype.behaviors;
// get flattened, deduped list of behaviors *not* already on super class
behaviors = flattenBehaviors(behaviors, null, superBehaviors);
PolymerGenerated.prototype.behaviors = superBehaviors ?
superBehaviors.concat(behaviors) : behaviors;
PolymerGenerated.prototype.__behaviors = behaviors;
} }
PolymerGenerated.generatedFrom = info; PolymerGenerated.generatedFrom = info;
copyProperties(info, PolymerGenerated.prototype);
return PolymerGenerated; return PolymerGenerated;
} }
@@ -486,14 +488,11 @@ function GenerateClassFromInfo(info, Base) {
*/ */
export const Class = function(info, mixin) { export const Class = function(info, mixin) {
if (!info) { if (!info) {
console.warn(`Polymer's Class function requires \`info\` argument`); console.warn('Polymer.Class requires `info` argument');
} }
let klass = mixin ? mixin(LegacyElementMixin(HTMLElement)) : let klass = mixin ? mixin(LegacyElementMixin(HTMLElement)) :
LegacyElementMixin(HTMLElement); LegacyElementMixin(HTMLElement);
if (info.behaviors) { klass = GenerateClassFromInfo(info, klass, info.behaviors);
klass = applyBehaviors(info.behaviors, klass);
}
klass = GenerateClassFromInfo(info, klass);
// decorate klass with registration info // decorate klass with registration info
klass.is = info.is; klass.is = info.is;
return klass; return klass;

View File

@@ -77,11 +77,6 @@ export const LegacyElementMixin = dedupingMixin((base) => {
this.__boundListeners; this.__boundListeners;
/** @type {Object<string, Function>} */ /** @type {Object<string, Function>} */
this._debouncers; this._debouncers;
// Ensure listeners are applied immediately so that they are
// added before declarative event listeners. This allows an element to
// decorate itself via an event prior to any declarative listeners
// seeing the event. Note, this ensures compatibility with 1.x ordering.
this._applyListeners();
} }
/** /**
@@ -96,6 +91,11 @@ export const LegacyElementMixin = dedupingMixin((base) => {
return this.prototype.importMeta; return this.prototype.importMeta;
} }
static _finalizeClass() {
this.prototype._registered();
super._finalizeClass();
}
/** /**
* Legacy callback called during the `constructor`, for overriding * Legacy callback called during the `constructor`, for overriding
* by the user. * by the user.
@@ -178,14 +178,14 @@ export const LegacyElementMixin = dedupingMixin((base) => {
* @suppress {invalidCasts} * @suppress {invalidCasts}
*/ */
_initializeProperties() { _initializeProperties() {
let proto = Object.getPrototypeOf(this);
if (!proto.hasOwnProperty('__hasRegisterFinished')) {
proto.__hasRegisterFinished = true;
this._registered();
}
super._initializeProperties(); super._initializeProperties();
this.root = /** @type {HTMLElement} */(this); this.root = /** @type {HTMLElement} */(this);
this.created(); this.created();
// Ensure listeners are applied immediately so that they are
// added before declarative event listeners. This allows an element to
// decorate itself via an event prior to any declarative listeners
// seeing the event. Note, this ensures compatibility with 1.x ordering.
this._applyListeners();
} }
/** /**

View File

@@ -16,7 +16,7 @@ import { pathFromUrl, resolveCss, resolveUrl } from '../utils/resolve-url.js';
import { DomModule } from '../elements/dom-module.js'; import { DomModule } from '../elements/dom-module.js';
import { PropertyEffects } from './property-effects.js'; import { PropertyEffects } from './property-effects.js';
import { PropertiesMixin } from './properties-mixin.js'; import { PropertiesMixin } from './properties-mixin.js';
import { skipStyleIncludesAndUrls } from '../utils/settings.js'; import { legacyOptimizations } from '../utils/settings.js';
/** /**
* Current Polymer version in Semver notation. * Current Polymer version in Semver notation.
@@ -24,6 +24,8 @@ import { skipStyleIncludesAndUrls } from '../utils/settings.js';
*/ */
export const version = '3.0.5'; export const version = '3.0.5';
const builtCSS = window.ShadyCSS && window.ShadyCSS['cssBuild'];
/** /**
* Element class mixin that provides the core API for Polymer's meta-programming * Element class mixin that provides the core API for Polymer's meta-programming
* features including template stamping, data-binding, attribute deserialization, * features including template stamping, data-binding, attribute deserialization,
@@ -253,7 +255,7 @@ export const ElementMixin = dedupingMixin(base => {
* @private * @private
*/ */
function processElementStyles(klass, template, is, baseURI) { function processElementStyles(klass, template, is, baseURI) {
if (!skipStyleIncludesAndUrls) { if (!builtCSS) {
const templateStyles = template.content.querySelectorAll('style'); const templateStyles = template.content.querySelectorAll('style');
const stylesWithImports = stylesFromTemplate(template); const stylesWithImports = stylesFromTemplate(template);
// insert styles from <link rel="import" type="css"> at the top of the template // insert styles from <link rel="import" type="css"> at the top of the template
@@ -335,10 +337,6 @@ export const ElementMixin = dedupingMixin(base => {
*/ */
static _finalizeClass() { static _finalizeClass() {
super._finalizeClass(); super._finalizeClass();
if (this.hasOwnProperty(
JSCompiler_renameProperty('is', this)) && this.is) {
register(this.prototype);
}
const observers = ownObservers(this); const observers = ownObservers(this);
if (observers) { if (observers) {
this.createObservers(observers, this._properties); this.createObservers(observers, this._properties);
@@ -349,7 +347,7 @@ export const ElementMixin = dedupingMixin(base => {
if (typeof template === 'string') { if (typeof template === 'string') {
console.error('template getter must return HTMLTemplateElement'); console.error('template getter must return HTMLTemplateElement');
template = null; template = null;
} else { } else if (!legacyOptimizations) {
template = template.cloneNode(true); template = template.cloneNode(true);
} }
} }
@@ -524,7 +522,6 @@ export const ElementMixin = dedupingMixin(base => {
* @suppress {invalidCasts} * @suppress {invalidCasts}
*/ */
_initializeProperties() { _initializeProperties() {
instanceCount++;
this.constructor.finalize(); this.constructor.finalize();
// note: finalize template when we have access to `localName` to // note: finalize template when we have access to `localName` to
// avoid dependence on `is` for polyfilling styling. // avoid dependence on `is` for polyfilling styling.
@@ -743,46 +740,6 @@ export const ElementMixin = dedupingMixin(base => {
return PolymerElement; return PolymerElement;
}); });
/**
* Total number of Polymer element instances created.
* @type {number}
*/
export let instanceCount = 0;
/**
* Array of Polymer element classes that have been finalized.
* @type {Array<PolymerElement>}
*/
export const registrations = [];
/**
* @param {!PolymerElementConstructor} prototype Element prototype to log
* @this {this}
* @private
*/
function _regLog(prototype) {
console.log('[' + prototype.is + ']: registered');
}
/**
* Registers a class prototype for telemetry purposes.
* @param {HTMLElement} prototype Element prototype to register
* @this {this}
* @protected
*/
export function register(prototype) {
registrations.push(prototype);
}
/**
* Logs all elements registered with an `is` to the console.
* @public
* @this {this}
*/
export function dumpRegistrations() {
registrations.forEach(_regLog);
}
/** /**
* When using the ShadyCSS scoping and custom property shim, causes all * When using the ShadyCSS scoping and custom property shim, causes all
* shimmed `styles` (via `custom-style`) in the document (and its subtree) * shimmed `styles` (via `custom-style`) in the document (and its subtree)

View File

@@ -10,6 +10,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
import '../utils/boot.js'; import '../utils/boot.js';
import { dedupingMixin } from '../utils/mixin.js'; import { dedupingMixin } from '../utils/mixin.js';
import { register, incrementInstanceCount } from '../utils/telemetry.js';
import { PropertiesChanged } from './properties-changed.js'; import { PropertiesChanged } from './properties-changed.js';
/** /**
@@ -114,8 +115,12 @@ export const PropertiesMixin = dedupingMixin(superClass => {
* @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do * @suppress {missingProperties} Interfaces in closure do not inherit statics, but classes do
*/ */
static get observedAttributes() { static get observedAttributes() {
const props = this._properties; if (!this.hasOwnProperty('__observedAttributes')) {
return props ? Object.keys(props).map(p => this.attributeNameForProperty(p)) : []; register(this.prototype);
const props = this._properties;
this.__observedAttributes = props ? Object.keys(props).map(p => this.attributeNameForProperty(p)) : [];
}
return this.__observedAttributes;
} }
/** /**
@@ -189,6 +194,7 @@ export const PropertiesMixin = dedupingMixin(superClass => {
* @return {void} * @return {void}
*/ */
_initializeProperties() { _initializeProperties() {
incrementInstanceCount();
this.constructor.finalize(); this.constructor.finalize();
super._initializeProperties(); super._initializeProperties();
} }

View File

@@ -10,6 +10,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
import '../utils/boot.js'; import '../utils/boot.js';
import { dedupingMixin } from '../utils/mixin.js'; import { dedupingMixin } from '../utils/mixin.js';
import { legacyOptimizations } from '../utils/settings.js';
const walker = document.createTreeWalker(document);
// 1.x backwards-compatible auto-wrapper for template type extensions // 1.x backwards-compatible auto-wrapper for template type extensions
// This is a clear layering violation and gives favored-nation status to // This is a clear layering violation and gives favored-nation status to
@@ -45,7 +48,8 @@ function findTemplateNode(root, nodeInfo) {
if (parent) { if (parent) {
// note: marginally faster than indexing via childNodes // note: marginally faster than indexing via childNodes
// (http://jsperf.com/childnodes-lookup) // (http://jsperf.com/childnodes-lookup)
for (let n=parent.firstChild, i=0; n; n=n.nextSibling) { walker.currentNode = parent;
for (let n=walker.firstChild(), i=0; n; n=walker.nextSibling()) {
if (nodeInfo.parentIndex === i++) { if (nodeInfo.parentIndex === i++) {
return n; return n;
} }
@@ -200,7 +204,7 @@ export const TemplateStamp = dedupingMixin(
if (!template._templateInfo) { if (!template._templateInfo) {
let templateInfo = template._templateInfo = {}; let templateInfo = template._templateInfo = {};
templateInfo.nodeInfoList = []; templateInfo.nodeInfoList = [];
templateInfo.stripWhiteSpace = templateInfo.stripWhiteSpace = legacyOptimizations ||
(outerTemplateInfo && outerTemplateInfo.stripWhiteSpace) || (outerTemplateInfo && outerTemplateInfo.stripWhiteSpace) ||
template.hasAttribute('strip-whitespace'); template.hasAttribute('strip-whitespace');
this._parseTemplateContent(template, templateInfo, {parent: null}); this._parseTemplateContent(template, templateInfo, {parent: null});
@@ -234,7 +238,8 @@ export const TemplateStamp = dedupingMixin(
// For ShadyDom optimization, indicating there is an insertion point // For ShadyDom optimization, indicating there is an insertion point
templateInfo.hasInsertionPoint = true; templateInfo.hasInsertionPoint = true;
} }
if (element.firstChild) { walker.currentNode = element;
if (walker.firstChild()) {
noted = this._parseTemplateChildNodes(element, templateInfo, nodeInfo) || noted; noted = this._parseTemplateChildNodes(element, templateInfo, nodeInfo) || noted;
} }
if (element.hasAttributes && element.hasAttributes()) { if (element.hasAttributes && element.hasAttributes()) {
@@ -260,7 +265,8 @@ export const TemplateStamp = dedupingMixin(
if (root.localName === 'script' || root.localName === 'style') { if (root.localName === 'script' || root.localName === 'style') {
return; return;
} }
for (let node=root.firstChild, parentIndex=0, next; node; node=next) { walker.currentNode = root;
for (let node=walker.firstChild(), parentIndex=0, next; node; node=next) {
// Wrap templates // Wrap templates
if (node.localName == 'template') { if (node.localName == 'template') {
node = wrapTemplateExtension(node); node = wrapTemplateExtension(node);
@@ -269,12 +275,13 @@ export const TemplateStamp = dedupingMixin(
// text nodes to be inexplicably split =( // text nodes to be inexplicably split =(
// note that root.normalize() should work but does not so we do this // note that root.normalize() should work but does not so we do this
// manually. // manually.
next = node.nextSibling; walker.currentNode = node;
next = walker.nextSibling();
if (node.nodeType === Node.TEXT_NODE) { if (node.nodeType === Node.TEXT_NODE) {
let /** Node */ n = next; let /** Node */ n = next;
while (n && (n.nodeType === Node.TEXT_NODE)) { while (n && (n.nodeType === Node.TEXT_NODE)) {
node.textContent += n.textContent; node.textContent += n.textContent;
next = n.nextSibling; next = walker.nextSibling();
root.removeChild(n); root.removeChild(n);
n = next; n = next;
} }
@@ -289,7 +296,8 @@ export const TemplateStamp = dedupingMixin(
childInfo.infoIndex = templateInfo.nodeInfoList.push(/** @type {!NodeInfo} */(childInfo)) - 1; childInfo.infoIndex = templateInfo.nodeInfoList.push(/** @type {!NodeInfo} */(childInfo)) - 1;
} }
// Increment if not removed // Increment if not removed
if (node.parentNode) { walker.currentNode = node;
if (walker.parentNode()) {
parentIndex++; parentIndex++;
} }
} }

View File

@@ -128,15 +128,16 @@ export const setAllowTemplateFromDomModule = function(allowDomModule) {
* If no includes or relative urls are used in styles, these steps can be * If no includes or relative urls are used in styles, these steps can be
* skipped as an optimization. * skipped as an optimization.
*/ */
export let skipStyleIncludesAndUrls = false; export let legacyOptimizations = false;
/** /**
* Sets `setSkipRewriteStyleUrls` globally for all elements * Sets `legacyOptimizations` globally for all elements to enable optimizations
* when only legacy based elements are used.
* *
* @param {boolean} skipIncludesAndUrls enable or disable skipping style * @param {boolean} useLegacyOptimizations enable or disable legacy optimizations
* includes and url rewriting * includes and url rewriting
* @return {void} * @return {void}
*/ */
export const setSkipStyleIncludesAndUrls = function(skipIncludesAndUrls) { export const setLegacyOptimizations = function(useLegacyOptimizations) {
skipStyleIncludesAndUrls = skipIncludesAndUrls; legacyOptimizations = useLegacyOptimizations;
}; };

53
lib/utils/telemetry.js Normal file
View File

@@ -0,0 +1,53 @@
/**
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
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
*/
/**
* Total number of Polymer element instances created.
* @type {number}
*/
export let instanceCount = 0;
export function incrementInstanceCount() {
instanceCount++;
}
/**
* Array of Polymer element classes that have been finalized.
* @type {Array<PolymerElement>}
*/
export const registrations = [];
/**
* @param {!PolymerElementConstructor} prototype Element prototype to log
* @this {this}
* @private
*/
function _regLog(prototype) {
console.log('[' + prototype.is + ']: registered');
}
/**
* Registers a class prototype for telemetry purposes.
* @param {HTMLElement} prototype Element prototype to register
* @this {this}
* @protected
*/
export function register(prototype) {
registrations.push(prototype);
}
/**
* Logs all elements registered with an `is` to the console.
* @public
* @this {this}
*/
export function dumpRegistrations() {
registrations.forEach(_regLog);
}

View File

@@ -93,13 +93,17 @@ Polymer({
type: String type: String
} }
}, },
listeners: {
foo: 'fooHandler'
},
created() { created() {
this.hasCreated = true; this.hasCreated = true;
this.prop = 'enabled!'; this.prop = 'enabled!';
}, },
ready() { ready() {
this.enabled = true; this.enabled = true;
} },
fooHandler() {}
}); });
Polymer({ Polymer({
@@ -241,30 +245,39 @@ suite('disable-upgrade-legacy', function() {
assert.ok(el.$.enabledEl.enabled); assert.ok(el.$.enabledEl.enabled);
assert.ok(el.$.enabledEl.$.element); assert.ok(el.$.enabledEl.$.element);
assert.equal(el.$.enabledEl.$.element.textContent, 'enabled!'); assert.equal(el.$.enabledEl.$.element.textContent, 'enabled!');
el.$.enabledEl.fooHandler = sinon.spy();
el.$.enabledEl.fire('foo');
assert.equal(el.$.enabledEl.fooHandler.callCount, 1);
assert.notOk(el.$.disabledEl.hasCreated); assert.notOk(el.$.disabledEl.hasCreated);
assert.notOk(el.$.disabledEl.enabled); assert.notOk(el.$.disabledEl.enabled);
assert.notOk(el.$.disabledEl.$); assert.notOk(el.$.disabledEl.$);
el.$.disabledEl.fooHandler = sinon.spy();
el.$.disabledEl.fire('foo');
assert.equal(el.$.disabledEl.fooHandler.callCount, 0);
assert.notOk(el.$.disabledBoundEl.hasCreated); assert.notOk(el.$.disabledBoundEl.hasCreated);
assert.notOk(el.$.disabledBoundEl.enabled); assert.notOk(el.$.disabledBoundEl.enabled);
assert.notOk(el.$.disabledBoundEl.$); assert.notOk(el.$.disabledBoundEl.$);
el.$.disabledBoundEl.fooHandler = sinon.spy();
el.$.disabledBoundEl.fire('foo');
assert.equal(el.$.disabledBoundEl.fooHandler.callCount, 0);
}); });
test('elements upgrade when `disable-upgrade` removed', function() { test('elements upgrade when `disable-upgrade` removed', function() {
assert.notOk(el.$.disabledEl.hasCreated);
assert.notOk(el.$.disabledEl.enabled);
assert.notOk(el.$.disabledEl.$);
assert.notOk(el.$.disabledBoundEl.hasCreated);
assert.notOk(el.$.disabledBoundEl.enabled);
assert.notOk(el.$.disabledBoundEl.$);
el.enable(); el.enable();
assert.ok(el.$.disabledEl.hasCreated); assert.ok(el.$.disabledEl.hasCreated);
assert.ok(el.$.disabledEl.enabled); assert.ok(el.$.disabledEl.enabled);
assert.ok(el.$.disabledEl.$.element); assert.ok(el.$.disabledEl.$.element);
assert.equal(el.$.disabledEl.$.element.textContent, 'enabled!'); assert.equal(el.$.disabledEl.$.element.textContent, 'enabled!');
el.$.disabledEl.fooHandler = sinon.spy();
el.$.disabledEl.fire('foo');
assert.equal(el.$.disabledEl.fooHandler.callCount, 1);
assert.ok(el.$.disabledBoundEl.hasCreated); assert.ok(el.$.disabledBoundEl.hasCreated);
assert.ok(el.$.disabledBoundEl.enabled); assert.ok(el.$.disabledBoundEl.enabled);
assert.ok(el.$.disabledBoundEl.$.element); assert.ok(el.$.disabledBoundEl.$.element);
assert.equal(el.$.disabledBoundEl.$.element.textContent, 'enabled!'); assert.equal(el.$.disabledBoundEl.$.element.textContent, 'enabled!');
el.$.disabledBoundEl.fooHandler = sinon.spy();
el.$.disabledBoundEl.fire('foo');
assert.equal(el.$.disabledBoundEl.fooHandler.callCount, 1);
}); });

View File

@@ -256,12 +256,12 @@ customElements.define('multi-behaviors',
import { mixinBehaviors } from '../../lib/legacy/class.js'; import { mixinBehaviors } from '../../lib/legacy/class.js';
import { PolymerElement } from '../../polymer-element.js'; import { PolymerElement } from '../../polymer-element.js';
customElements.define('nested-behaviors', customElements.define('nested-behaviors',
class extends mixinBehaviors( class extends mixinBehaviors([window.BehaviorD, window.LifeCycleBehavior1], mixinBehaviors(
[ [
[window.BehaviorB, [window.BehaviorC, window.BehaviorB], window.BehaviorA], [window.BehaviorB, [window.BehaviorC, window.BehaviorB], window.BehaviorA, window.LifeCycleBehavior2],
[window.BehaviorD] ], PolymerElement)) {
], PolymerElement) { }
}); );
</script> </script>
</dom-module> </dom-module>
@@ -355,7 +355,7 @@ customElements.define(BehaviorRegisteredExt.is, BehaviorRegisteredExt);
<test-fixture id="nested"> <test-fixture id="nested">
<template> <template>
<nested-behaviors></nested-behaviors> <nested-behaviors foo="foo"></nested-behaviors>
</template> </template>
</test-fixture> </test-fixture>
@@ -554,7 +554,13 @@ suite('nested-behaviors element', function() {
}); });
test('nested-behavior dedups', function() { test('nested-behavior dedups', function() {
assert.equal(el.behaviors.length, 4); assert.equal(el.behaviors.length, 6);
});
test('nested-behavior lifecycle', function() {
assert.equal(el._calledCreated, 2, 'created call count wrong');
assert.equal(el._calledAttached, 2, 'attached call count wrong');
assert.equal(el._calledAttributeChanged, 1, 'attributeChanged call count wrong');
}); });
test('nested-behavior overrides ordering', function() { test('nested-behavior overrides ordering', function() {