Skip to content

Commit

Permalink
Merge branch '2.x' into perf-opt
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinpschaaf committed Nov 6, 2018
2 parents c274670 + 8882c3d commit 5bd4afd
Show file tree
Hide file tree
Showing 16 changed files with 1,362 additions and 102 deletions.
20 changes: 19 additions & 1 deletion externs/closure-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -1416,4 +1416,22 @@ Polymer_DisableUpgradeMixin.prototype.connectedCallback = function(){};
/**
* @override
*/
Polymer_DisableUpgradeMixin.prototype.disconnectedCallback = function(){};
Polymer_DisableUpgradeMixin.prototype.disconnectedCallback = function(){};
/**
* @interface
*/
function Polymer_LegacyDataMixin(){}
/**
* @param {string} property Property that should trigger the effect
* @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES
* @param {Object=} effect Effect metadata object
* @return {void}
*/
Polymer_LegacyDataMixin.prototype._addPropertyEffect = function(property, type, effect){};
/**
* @param {Object} templateInfo Template metadata to add effect to
* @param {string} prop Property that should trigger the effect
* @param {Object=} effect Effect metadata object
* @return {void}
*/
Polymer_LegacyDataMixin._addTemplatePropertyEffect = function(templateInfo, prop, effect){};
3 changes: 3 additions & 0 deletions lib/elements/dom-bind.html
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@

constructor() {
super();
if (Polymer.strictTemplatePolicy) {
throw new Error(`strictTemplatePolicy: dom-bind not allowed`);
}
this.root = null;
this.$ = null;
this.__children = null;
Expand Down
8 changes: 6 additions & 2 deletions lib/elements/dom-if.html
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,12 @@
if (c$ && c$.length) {
// use first child parent, for case when dom-if may have been detached
let parent = c$[0].parentNode;
for (let i=0, n; (i<c$.length) && (n=c$[i]); i++) {
parent.removeChild(n);
// Instance children may be disconnected from parents when dom-if
// detaches if a tree was innerHTML'ed
if (parent) {
for (let i=0, n; (i<c$.length) && (n=c$[i]); i++) {
parent.removeChild(n);
}
}
}
this.__instance = null;
Expand Down
18 changes: 13 additions & 5 deletions lib/elements/dom-module.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@

let modules = {};
let lcModules = {};
function setModule(id, module) {
// store id separate from lowercased id so that
// in all cases mixedCase id will stored distinctly
// and lowercase version is a fallback
modules[id] = lcModules[id.toLowerCase()] = module;
}
function findModule(id) {
return modules[id] || lcModules[id.toLowerCase()];
}
Expand Down Expand Up @@ -124,12 +130,14 @@
register(id) {
id = id || this.id;
if (id) {
// Under strictTemplatePolicy, reject and null out any re-registered
// dom-module since it is ambiguous whether first-in or last-in is trusted
if (Polymer.strictTemplatePolicy && findModule(id) !== undefined) {
setModule(id, null);
throw new Error(`strictTemplatePolicy: dom-module ${id} re-registered`);
}
this.id = id;
// store id separate from lowercased id so that
// in all cases mixedCase id will stored distinctly
// and lowercase version is a fallback
modules[id] = this;
lcModules[id.toLowerCase()] = this;
setModule(id, this);
styleOutsideTemplateCheck(this);
}
}
Expand Down
26 changes: 10 additions & 16 deletions lib/legacy/class.html
Original file line number Diff line number Diff line change
Expand Up @@ -230,22 +230,6 @@
return observers;
}

/**
* @return {HTMLTemplateElement} template for this class
*/
static get template() {
// get template first from any imperative set in `info._template`
return info._template ||
// next look in dom-module associated with this element's is.
Polymer.DomModule && Polymer.DomModule.import(this.is, 'template') ||
// next look for superclass template (note: use superclass symbol
// to ensure correct `this.is`)
Base.template ||
// finally fall back to `_template` in element's prototype.
this.prototype._template ||
null;
}

/**
* @return {void}
*/
Expand Down Expand Up @@ -499,6 +483,7 @@
*
* @param {!PolymerInit} info Object containing Polymer metadata and functions
* to become class methods.
* @template T
* @param {function(T):T} mixin Optional mixin to apply to legacy base class
* before extending with Polymer metaprogramming.
* @return {function(new:HTMLElement)} Generated class
Expand All @@ -513,6 +498,15 @@
klass = GenerateClassFromInfo(info, klass, info.behaviors);
// decorate klass with registration info
klass.is = info.is;
// To match 1.x behavior, `_template` supplied on a behavior should take
// precedence over dom-module lookup for `is`; this also prevents
// dom-module injection under strictTemplatePolicy for an element that
// would normally get its template from a behavior.
// TODO(sorvell): Remove once "flattened behaviors" lands, since both
// `_template` and `is` will be on the same class after that change
if (klass.prototype._template !== undefined) {
klass.prototype._template = klass.prototype._template;
}
return klass;
};

Expand Down
171 changes: 171 additions & 0 deletions lib/legacy/legacy-data-mixin.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
<!--
@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
-->

<link rel="import" href="class.html">
<link rel="import" href="../../polymer.html">
<link rel="import" href="../utils/mixin.html">
<link rel="import" href="../utils/templatize.html">

<script>
(function() {
'use strict';

const UndefinedArgumentError = class extends Error {
constructor(message, arg) {
super(message);
this.arg = arg;
this.name = this.constructor.name;
// Affordances for ensuring instanceof works after babel ES5 compilation
// TODO(kschaaf): Remove after polymer CLI updates to newer Babel that
// sets the constructor/prototype correctly for subclassed builtins
this.constructor = UndefinedArgumentError;
this.__proto__ = UndefinedArgumentError.prototype;
}
};

/**
* Wraps effect functions to catch `UndefinedArgumentError`s and warn.
*
* @param {Object=} effect Effect metadata object
* @param {Object=} fnName Name of user function, if known
* @return {?Object} Effect metadata object
*/
function wrapEffect(effect, fnName) {
if (effect && effect.fn) {
const fn = effect.fn;
effect.fn = function() {
try {
fn.apply(this, arguments);
} catch (e) {
if (e instanceof UndefinedArgumentError) {
console.warn(`Argument '${e.arg}'${fnName ?` for method '${fnName}'` : ''} was undefined. Ensure it has an undefined check.`);
} else {
throw e;
}
}
};
}
return effect;
}

/**
* Mixin to selectively add back Polymer 1.x's `undefined` rules
* governing when observers & computing functions run based
* on all arguments being defined (reference https://www.polymer-project.org/1.0/docs/devguide/observers#multi-property-observers).
*
* When loaded, all legacy elements (defined with `Polymer({...})`)
* will have the mixin applied. The mixin only restores legacy data handling
* if `_legacyUndefinedCheck: true` is set on the element's prototype.
*
* This mixin is intended for use to help migration from Polymer 1.x to
* 2.x+ by allowing legacy code to work while identifying observers and
* computing functions that need undefined checks to work without
* the mixin in Polymer 2.
*
* @mixinFunction
* @polymer
* @summary Mixin to selectively add back Polymer 1.x's `undefined` rules
* governing when observers & computing functions run.
*/
Polymer.LegacyDataMixin = Polymer.dedupingMixin(superClass => {

/**
* @polymer
* @mixinClass
* @implements {Polymer_LegacyDataMixin}
*/
class LegacyDataMixin extends superClass {
/**
* Overrides `Polyer.PropertyEffects` to add `undefined` argument
* checking to match Polymer 1.x style rules
*
* @param {!Array<!MethodArg>} args Array of argument metadata
* @param {string} path Property/path name that triggered the method effect
* @param {Object} props Bag of current property changes
* @return {Array<*>} Array of argument values
* @private
*/
_marshalArgs(args, path, props) {
const vals = super._marshalArgs(args, path, props);
// Per legacy data rules, single-property observers (whether in `properties`
// and in `observers`) are called regardless of whether their argument is
// undefined or not. Multi-property observers must have all arguments defined
if (this._legacyUndefinedCheck && vals.length > 1) {
for (let i=0; i<vals.length; i++) {
if (vals[i] === undefined) {
// Break out of effect's control flow; will be caught in
// wrapped property effect function below
const name = args[i].name;
throw new UndefinedArgumentError(`Argument '${name}' is undefined. Ensure it has an undefined check.`, name);
}
}
}
return vals;
}

/**
* Overrides `Polyer.PropertyEffects` to wrap effect functions to
* catch `UndefinedArgumentError`s and warn.
*
* @param {string} property Property that should trigger the effect
* @param {string} type Effect type, from this.PROPERTY_EFFECT_TYPES
* @param {Object=} effect Effect metadata object
* @return {void}
* @protected
*/
_addPropertyEffect(property, type, effect) {
return super._addPropertyEffect(property, type,
wrapEffect(effect, effect && effect.info && effect.info.methodName));
}

/**
* Overrides `Polyer.PropertyEffects` to wrap effect functions to
* catch `UndefinedArgumentError`s and warn.
*
* @param {Object} templateInfo Template metadata to add effect to
* @param {string} prop Property that should trigger the effect
* @param {Object=} effect Effect metadata object
* @return {void}
* @protected
*/
static _addTemplatePropertyEffect(templateInfo, prop, effect) {
return super._addTemplatePropertyEffect(templateInfo, prop, wrapEffect(effect));
}

}

return LegacyDataMixin;

});

// LegacyDataMixin is applied to base class _before_ metaprogramming, to
// ensure override of _addPropertyEffect et.al. are used by metaprogramming
// performed in _finalizeClass
const Class = Polymer.Class;
Polymer.Class = (info, mixin) => Class(info,
superClass => mixin ?
mixin(Polymer.LegacyDataMixin(superClass)) :
Polymer.LegacyDataMixin(superClass)
);

// Apply LegacyDataMixin to Templatizer instances as well, and defer
// runtime switch to the root's host (_methodHost)
Polymer.Templatize.mixin =
Polymer.dedupingMixin(superClass => class extends Polymer.LegacyDataMixin(superClass) {
get _legacyUndefinedCheck() {
return this._methodHost && this._methodHost._legacyUndefinedCheck;
}
});

console.info('LegacyDataMixin will be applied to all legacy elements.\n' +
'Set `_legacyUndefinedCheck: true` to enable.');

})();
</script>
Loading

0 comments on commit 5bd4afd

Please sign in to comment.