diff --git a/lib/legacy/class.html b/lib/legacy/class.html index ab052142..cf22f43b 100644 --- a/lib/legacy/class.html +++ b/lib/legacy/class.html @@ -153,24 +153,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN return info.observers; } - /** - * @return {HTMLTemplateElement} template for this class - */ - static get template() { - if (!this.hasOwnProperty(JSCompiler_renameProperty('_template', this))) { - this._template = - // Accept template: _null to short-circuit dom-module lookup - info._template !== undefined ? info._template : - // Look in dom-module associated with this element's is - this._getTemplateFromDomModule() || - // Look up in the chain - Base.template || - // Use any template set on the prototype via registered callback - this.prototype._template; - } - return this._template; - } - /** * @return {void} */ diff --git a/lib/mixins/element-mixin.html b/lib/mixins/element-mixin.html index a1f538d4..b3fd9f99 100644 --- a/lib/mixins/element-mixin.html +++ b/lib/mixins/element-mixin.html @@ -130,7 +130,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN } /** - * Returns a memoized version of the the `observers` array. + * Returns a memoized version of the `observers` array. * @param {PolymerElementConstructor} constructor Element class * @return {Array} Array containing own observers for the given class * @protected @@ -154,7 +154,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN * alter these settings. However, additional `observers` may be added * by subclasses. * - * The info object should may contain property metadata as follows: + * The info object should contain property metadata as follows: * * * `type`: {function} type to which an attribute matching the property * is deserialized. Note the property is camel-cased from a dash-cased @@ -166,7 +166,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN * property 'foo', * * * `computed`: {string} creates a computed property. A computed property - * also automatically is set to `readOnly: true`. The value is calculated + * is also automatically set to `readOnly: true`. The value is calculated * by running a method and arguments parsed from the given string. For * example 'compute(foo)' will compute a given property when the * 'foo' property changes by executing the 'compute' method. This method @@ -277,6 +277,27 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN } /** + * Look up template from dom-module for element + * + * @param {!string} is Element name to look up + * @return {!HTMLTemplateElement} Template found in dom module, or + * undefined if not found + * @protected + */ + function getTemplateFromDomModule(is) { + let template = null; + if (is && Polymer.DomModule) { + template = Polymer.DomModule.import(is, 'template'); + // Under strictTemplatePolicy, require any element with an `is` + // specified to have a dom-module + if (Polymer.strictTemplatePolicy && !template) { + throw new Error(`strictTemplatePolicy: expecting dom-module or null template for ${is}`); + } + } + return template; + } + + /** * @polymer * @mixinClass * @unrestricted @@ -348,30 +369,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN } } - /** - * Look up template from dom-module for element - * - * @param {!string} is Element name to look up - * @return {!HTMLTemplateElement} Template found in dom module, or - * undefined if not found - * @protected - */ - static _getTemplateFromDomModule() { - let template = null; - const is = /** @type {PolymerElementConstructor}*/ (this).is; - // Under strictTemplatePolicy in 3.x+, dom-module lookup is only allowed - // when opted-in via allowTemplateFromDomModule - if (is && Polymer.DomModule) { - template = Polymer.DomModule.import(is, 'template'); - // Under strictTemplatePolicy, require any element with an `is` - // specified to have a dom-module - if (Polymer.strictTemplatePolicy && !template) { - throw new Error(`strictTemplatePolicy: expecting dom-module or null template for ${is}`); - } - } - return template; - } - /** * Returns the template that will be stamped into this element's shadow root. * @@ -410,17 +407,43 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN * @return {HTMLTemplateElement|string} Template to be stamped */ static get template() { + // Explanation of template-related properties: + // - constructor.template (this getter): the template for the class. + // This can come from the prototype (for legacy elements), from a + // dom-module, or from the super class's template (or can be overridden + // altogether by the user) + // - constructor._template: memoized version of constructor.template + // - prototype._template: working template for the element, which will be + // parsed and modified in place. It is a cloned version of + // constructor.template, saved in _finalizeClass(). Note that before + // this getter is called, for legacy elements this could be from a + // _template field on the info object passed to Polymer(), a behavior, + // or set in registered(); once the static getter runs, a clone of it + // will overwrite it on the prototype as the working template. if (!this.hasOwnProperty(JSCompiler_renameProperty('_template', this))) { this._template = + // If user has put template on prototype (e.g. in legacy via registered + // callback or info object), prefer that first + this.prototype.hasOwnProperty(JSCompiler_renameProperty('_template', this.prototype)) ? + this.prototype._template : // Look in dom-module associated with this element's is - this._getTemplateFromDomModule() || + (getTemplateFromDomModule(/** @type {PolymerElementConstructor}*/ (this).is) || // Next look for superclass template (call the super impl this // way so that `this` points to the superclass) - Object.getPrototypeOf(/** @type {PolymerElementConstructor}*/ (this).prototype).constructor.template; + Object.getPrototypeOf(/** @type {PolymerElementConstructor}*/ (this).prototype).constructor.template); } return this._template; } + /** + * Set the template. + * + * @param {!HTMLTemplateElement|string} value Template to set. + */ + static set template(value) { + this._template = value; + } + /** * Path matching the url from which the element was imported. * @@ -428,7 +451,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN * The `importPath` property is also set on element instances and can be * used to create bindings relative to the import path. * - * For elements defined in ES modules, users should implement + * For elements defined in ES modules, users should implement * `static get importMeta() { return import.meta; }`, and the default * implementation of `importPath` will return `import.meta.url`'s path. * For elements defined in HTML imports, this getter will return the path diff --git a/test/unit/strict-template-policy.html b/test/unit/strict-template-policy.html index 4807c176..8596bda4 100644 --- a/test/unit/strict-template-policy.html +++ b/test/unit/strict-template-policy.html @@ -222,7 +222,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN assertThrows(function() { el = document.createElement('trusted-element'); document.getElementById('target').appendChild(el); - }, /expecting dom-module or null template/); + }, /expecting dom-module or null template for trusted-element/); assert.notOk(el && el.shadowRoot); assert.notOk(document.getElementById('injected')); }); @@ -238,7 +238,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN }, /trusted-element re-registered/); const el = document.createElement('trusted-element'); document.getElementById('target').appendChild(el); - assert.notOk(el && el.shadowRoot); + assert.notOk(el.shadowRoot); assert.notOk(document.getElementById('injected')); }); @@ -273,7 +273,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN assertThrows(function() { el = document.createElement('trusted-element-legacy'); document.getElementById('target').appendChild(el); - }, /expecting dom-module or null template/); + }, /expecting dom-module or null template for trusted-element-legacy/); assert.notOk(el && el.shadowRoot); assert.notOk(document.getElementById('injected')); }); @@ -287,9 +287,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN ' `' + ''; }, /trusted-element-legacy re-registered/); - const el = document.createElement('trusted-element'); + const el = document.createElement('trusted-element-legacy'); document.getElementById('target').appendChild(el); - assert.notOk(el && el.shadowRoot); + assert.notOk(el.shadowRoot); assert.notOk(document.getElementById('injected')); }); @@ -303,7 +303,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN Polymer({ is: 'has-no-template-legacy', _template: null - }, /expecting dom-module or null template/); + }); let el = document.createElement('has-no-template-legacy'); document.getElementById('target').appendChild(el); assert.notOk(el.shadowRoot); @@ -331,7 +331,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN }, /expecting dom-module or null template/); }); - test('template helpers in trusted templates work', function() { + test('template helpers in trusted templates work', function() { var el = document.createElement('trusted-templates'); document.getElementById('target').appendChild(el); Polymer.flush(); @@ -352,6 +352,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN }); }); +