Skip to content

Commit

Permalink
Merge branch 'legacy-undefined-noBatch' into legacy-undefined-noBatch…
Browse files Browse the repository at this point in the history
…-disableUpgrade
  • Loading branch information
Steven Orvell committed Jul 12, 2019
2 parents 2c264c6 + e4bb039 commit 42f00cd
Show file tree
Hide file tree
Showing 22 changed files with 1,231 additions and 260 deletions.
384 changes: 384 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

541 changes: 449 additions & 92 deletions lib/elements/dom-if.js

Large diffs are not rendered by default.

12 changes: 10 additions & 2 deletions lib/elements/dom-repeat.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ export class DomRepeat extends domRepeatBase {
this.__ctor = null;
this.__isDetached = true;
this.template = null;
/** @type {TemplateInfo} */
this._templateInfo;
}

/**
Expand Down Expand Up @@ -339,9 +341,15 @@ export class DomRepeat extends domRepeatBase {
// until ready, since won't have its template content handed back to
// it until then
if (!this.__ctor) {
let template = this.template = /** @type {HTMLTemplateElement} */(this.querySelector('template'));
// When `removeNestedTemplates` is true, the "template" is the element
// itself, which has been given a `_templateInfo` property
const thisAsTemplate = /** @type {!HTMLTemplateElement} */ (
/** @type {!HTMLElement} */ (this));
let template = this.template = thisAsTemplate._templateInfo ?
thisAsTemplate :
/** @type {!HTMLTemplateElement} */ (this.querySelector('template'));
if (!template) {
// // Wait until childList changes and template should be there by then
// Wait until childList changes and template should be there by then
let observer = new MutationObserver(() => {
if (this.querySelector('template')) {
observer.disconnect();
Expand Down
8 changes: 4 additions & 4 deletions lib/mixins/dir-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ const DIR_INSTANCES = [];
/** @type {?MutationObserver} */
let observer = null;

let DOCUMENT_DIR = '';
let documentDir = '';

function getRTL() {
DOCUMENT_DIR = document.documentElement.getAttribute('dir');
documentDir = document.documentElement.getAttribute('dir');
}

/**
Expand All @@ -43,13 +43,13 @@ function getRTL() {
function setRTL(instance) {
if (!instance.__autoDirOptOut) {
const el = /** @type {!HTMLElement} */(instance);
el.setAttribute('dir', DOCUMENT_DIR);
el.setAttribute('dir', documentDir);
}
}

function updateDirection() {
getRTL();
DOCUMENT_DIR = document.documentElement.getAttribute('dir');
documentDir = document.documentElement.getAttribute('dir');
for (let i = 0; i < DIR_INSTANCES.length; i++) {
setRTL(DIR_INSTANCES[i]);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/mixins/element-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { wrap } from '../utils/wrap.js';
* Current Polymer version in Semver notation.
* @type {string} Semver notation of the current version of Polymer.
*/
export const version = '3.2.0';
export const version = '3.3.0';

export const builtCSS = window.ShadyCSS && window.ShadyCSS['cssBuild'];

Expand Down Expand Up @@ -693,7 +693,7 @@ export const ElementMixin = dedupingMixin(base => {
n.shadowRoot.appendChild(dom);
}
if (syncInitialRender && window.ShadyDOM) {
ShadyDOM.flushInitial(n.shadowRoot);
window.ShadyDOM.flushInitial(n.shadowRoot);
}
return n.shadowRoot;
}
Expand Down
190 changes: 139 additions & 51 deletions lib/mixins/property-effects.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { camelToDashCase, dashToCamelCase } from '../utils/case-map.js';
import { PropertyAccessors } from './property-accessors.js';
/* for annotated effects */
import { TemplateStamp } from './template-stamp.js';
import { sanitizeDOMValue, legacyUndefined, legacyNoBatch, legacyNotifyOrder, orderedComputed } from '../utils/settings.js';
import { sanitizeDOMValue, legacyUndefined, legacyNoBatch, legacyNotifyOrder, orderedComputed, removeNestedTemplates, fastDomIf } from '../utils/settings.js';

// Monotonically increasing unique ID used for de-duping effects triggered
// from multiple properties in the same turn
Expand Down Expand Up @@ -539,7 +539,7 @@ function getComputedOrder(inst) {
if (!ordered) {
ordered = new Map();
const effects = inst[TYPES.COMPUTE];
const {counts, ready} = dependencyCounts(inst);
let {counts, ready, total} = dependencyCounts(inst);
let curr;
while ((curr = ready.shift())) {
ordered.set(curr, ordered.size);
Expand All @@ -548,12 +548,17 @@ function getComputedOrder(inst) {
computedByCurr.forEach(fx => {
// Note `methodInfo` is where the computed property name is stored
const computedProp = fx.info.methodInfo;
--total;
if (--counts[computedProp] === 0) {
ready.push(computedProp);
}
});
}
}
if (total !== 0) {
const el = /** @type {HTMLElement} */ (inst);
console.warn(`Computed graph for ${el.localName} incomplete; circular?`);
}
inst.constructor.__orderedComputedDeps = ordered;
}
return ordered;
Expand All @@ -563,7 +568,8 @@ function getComputedOrder(inst) {
* Generates a map of property-to-dependency count (`counts`, where "dependency
* count" is the number of dependencies a given property has assuming it is a
* computed property, otherwise 0). It also returns a pre-populated list of
* `ready` properties that have no dependencies.
* `ready` properties that have no dependencies and a `total` count, which is
* used for error-checking the graph.
*
* Used by `orderedComputed: true` computed property algorithm.
*
Expand All @@ -574,20 +580,25 @@ function getComputedOrder(inst) {
* dependencies.
*/
function dependencyCounts(inst) {
const props = inst.constructor._properties;
const infoForComputed = inst[COMPUTE_INFO];
const counts = {};
const computedDeps = inst[TYPES.COMPUTE];
const ready = [];
for (let p in props) {
let total = 0;
// Count dependencies for each computed property
for (let p in infoForComputed) {
const info = infoForComputed[p];
if (info) {
// Be sure to add the method name itself in case of "dynamic functions"
counts[p] = info.args.length + (info.dynamicFn ? 1 : 0);
} else {
// Be sure to add the method name itself in case of "dynamic functions"
total += counts[p] =
info.args.filter(a => !a.literal).length + (info.dynamicFn ? 1 : 0);
}
// Build list of ready properties (that aren't themselves computed)
for (let p in computedDeps) {
if (!infoForComputed[p]) {
ready.push(p);
}
}
return {counts, ready};
return {counts, ready, total};
}

/**
Expand Down Expand Up @@ -1274,9 +1285,6 @@ function upper(name) {
* @appliesMixin PropertyAccessors
* @summary Element class mixin that provides meta-programming for Polymer's
* template binding and data observation system.
* @template T
* @param {function(new:T)} superClass Class to apply mixin to.
* @return {function(new:T)} superClass with mixin applied.
*/
export const PropertyEffects = dedupingMixin(superClass => {

Expand Down Expand Up @@ -1350,9 +1358,6 @@ export const PropertyEffects = dedupingMixin(superClass => {
this.__templateInfo;
}

/**
* @return {!Object<string, string>} Effect prototype property name map.
*/
get PROPERTY_EFFECT_TYPES() {
return TYPES;
}
Expand Down Expand Up @@ -1978,11 +1983,23 @@ export const PropertyEffects = dedupingMixin(superClass => {
if (this[TYPES.PROPAGATE]) {
runEffects(this, this[TYPES.PROPAGATE], changedProps, oldProps, hasPaths);
}
let templateInfo = this.__templateInfo;
while (templateInfo) {
if (this.__templateInfo) {
this._runEffectsForTemplate(this.__templateInfo, changedProps, oldProps, hasPaths);
}
}

_runEffectsForTemplate(templateInfo, changedProps, oldProps, hasPaths) {
const baseRunEffects = (changedProps, hasPaths) => {
runEffects(this, templateInfo.propertyEffects, changedProps, oldProps,
hasPaths, templateInfo.nodeList);
templateInfo = templateInfo.nextTemplateInfo;
for (let info=templateInfo.firstChild; info; info=info.nextSibling) {
this._runEffectsForTemplate(info, changedProps, oldProps, hasPaths);
}
};
if (templateInfo.runEffects) {
templateInfo.runEffects(baseRunEffects, changedProps, hasPaths);
} else {
baseRunEffects(changedProps, hasPaths);
}
}

Expand Down Expand Up @@ -2675,7 +2692,7 @@ export const PropertyEffects = dedupingMixin(superClass => {
*/
_bindTemplate(template, instanceBinding) {
let templateInfo = this.constructor._parseTemplate(template);
let wasPreBound = this.__templateInfo == templateInfo;
let wasPreBound = this.__preBoundTemplateInfo == templateInfo;
// Optimization: since this is called twice for proto-bound templates,
// don't attempt to recreate accessors if this template was pre-bound
if (!wasPreBound) {
Expand All @@ -2685,17 +2702,39 @@ export const PropertyEffects = dedupingMixin(superClass => {
}
if (instanceBinding) {
// For instance-time binding, create instance of template metadata
// and link into list of templates if necessary
// and link into tree of templates if necessary
templateInfo = /** @type {!TemplateInfo} */(Object.create(templateInfo));
templateInfo.wasPreBound = wasPreBound;
if (!wasPreBound && this.__templateInfo) {
let last = this.__templateInfoLast || this.__templateInfo;
this.__templateInfoLast = last.nextTemplateInfo = templateInfo;
templateInfo.previousTemplateInfo = last;
return templateInfo;
if (!this.__templateInfo) {
// Set the info to the root of the tree
this.__templateInfo = templateInfo;
} else {
// Append this template info onto the end of its parent template's
// list, which will determine the tree structure via which property
// effects are run; if this template was not nested in another
// template, use the root template (the first stamped one) as the
// parent. Note, `parent` is the `templateInfo` instance for this
// template's parent (containing) template, which was set up in
// `applyTemplateContent`. While a given template's `parent` is set
// apriori, it is only added to the parent's child list at the point
// that it is being bound, since a template may or may not ever be
// stamped, and may be stamped more than once (in which case instances
// of the template info will be in the tree under its parent more than
// once).
const parent = templateInfo.parent || this.__templateInfo;
const previous = parent.lastChild;
parent.lastChild = templateInfo;
templateInfo.previousSibling = previous;
if (previous) {
previous.nextSibling = templateInfo;
} else {
parent.firstChild = templateInfo;
}
}
} else {
this.__preBoundTemplateInfo = templateInfo;
}
return this.__templateInfo = templateInfo;
return templateInfo;
}

/**
Expand Down Expand Up @@ -2736,17 +2775,20 @@ export const PropertyEffects = dedupingMixin(superClass => {
* in the main element template.
*
* @param {!HTMLTemplateElement} template Template to stamp
* @param {TemplateInfo=} templateInfo Optional bound template info associated
* with the template to be stamped; if omitted the template will be
* automatically bound.
* @return {!StampedTemplate} Cloned template content
* @override
* @protected
*/
_stampTemplate(template) {
_stampTemplate(template, templateInfo) {
templateInfo = templateInfo || /** @type {!TemplateInfo} */(this._bindTemplate(template, true));
// Ensures that created dom is `_enqueueClient`'d to this element so
// that it can be flushed on next call to `_flushProperties`
hostStack.beginHosting(this);
let dom = super._stampTemplate(template);
let dom = super._stampTemplate(template, templateInfo);
hostStack.endHosting(this);
let templateInfo = /** @type {!TemplateInfo} */(this._bindTemplate(template, true));
// Add template-instance-specific data to instanced templateInfo
templateInfo.nodeList = dom.nodeList;
// Capture child nodes to allow unstamping of non-prototypical templates
Expand All @@ -2759,10 +2801,17 @@ export const PropertyEffects = dedupingMixin(superClass => {
dom.templateInfo = templateInfo;
// Setup compound storage, 2-way listeners, and dataHost for bindings
setupBindings(this, templateInfo);
// Flush properties into template nodes if already booted
if (this.__dataReady) {
runEffects(this, templateInfo.propertyEffects, this.__data, null,
false, templateInfo.nodeList);
// Flush properties into template nodes; the check on `__dataClientsReady`
// ensures we don't needlessly run effects for an element's initial
// prototypical template stamping since they will happen as a part of the
// first call to `_propertiesChanged`. This flag is set to true
// after running the initial propagate effects, and immediately before
// flushing clients. Since downstream clients could cause stamping on
// this host (e.g. a fastDomIf `dom-if` being forced to render
// synchronously), this flag ensures effects for runtime-stamped templates
// are run at this point during the initial element boot-up.
if (this.__dataClientsReady) {
this._runEffectsForTemplate(templateInfo, this.__data, null, false);
}
return dom;
}
Expand All @@ -2778,25 +2827,27 @@ export const PropertyEffects = dedupingMixin(superClass => {
* @protected
*/
_removeBoundDom(dom) {
// Unlink template info
// Unlink template info; Note that while the child is unlinked from its
// parent list, a template's `parent` reference is never removed, since
// this is is determined by the tree structure and applied at
// `applyTemplateContent` time.
let templateInfo = dom.templateInfo;
if (templateInfo.previousTemplateInfo) {
templateInfo.previousTemplateInfo.nextTemplateInfo =
templateInfo.nextTemplateInfo;
}
if (templateInfo.nextTemplateInfo) {
templateInfo.nextTemplateInfo.previousTemplateInfo =
templateInfo.previousTemplateInfo;
const {previousSibling, nextSibling, parent} = templateInfo;
if (previousSibling) {
previousSibling.nextSibling = nextSibling;
} else if (parent) {
parent.firstChild = nextSibling;
}
if (this.__templateInfoLast == templateInfo) {
this.__templateInfoLast = templateInfo.previousTemplateInfo;
if (nextSibling) {
nextSibling.previousSibling = previousSibling;
} else if (parent) {
parent.lastChild = previousSibling;
}
templateInfo.previousTemplateInfo = templateInfo.nextTemplateInfo = null;
// Remove stamped nodes
let nodes = templateInfo.childNodes;
for (let i=0; i<nodes.length; i++) {
let node = nodes[i];
node.parentNode.removeChild(node);
wrap(wrap(node).parentNode).removeChild(node);
}
}

Expand Down Expand Up @@ -2929,12 +2980,49 @@ export const PropertyEffects = dedupingMixin(superClass => {
// Change back to just super.methodCall()
let noted = propertyEffectsBase._parseTemplateNestedTemplate.call(
this, node, templateInfo, nodeInfo);
const parent = node.parentNode;
const nestedTemplateInfo = nodeInfo.templateInfo;
const isDomIf = parent.localName === 'dom-if';
const isDomRepeat = parent.localName === 'dom-repeat';
// Remove nested template and redirect its host bindings & templateInfo
// onto the parent (dom-if/repeat element)'s nodeInfo
if (removeNestedTemplates && (isDomIf || isDomRepeat)) {
parent.removeChild(node);
// Use the parent's nodeInfo (for the dom-if/repeat) to record the
// templateInfo, and use that for any host property bindings below
nodeInfo = nodeInfo.parentInfo;
nodeInfo.templateInfo = nestedTemplateInfo;
// Ensure the parent dom-if/repeat is noted since it now may have host
// bindings; it may not have been if it did not have its own bindings
nodeInfo.noted = true;
noted = false;
}
// Merge host props into outer template and add bindings
let hostProps = nodeInfo.templateInfo.hostProps;
let mode = '{';
for (let source in hostProps) {
let parts = [{ mode, source, dependencies: [source], hostProp: true }];
addBinding(this, templateInfo, nodeInfo, 'property', '_host_' + source, parts);
let hostProps = nestedTemplateInfo.hostProps;
if (fastDomIf && isDomIf) {
// `fastDomIf` mode uses runtime-template stamping to add accessors/
// effects to properties used in its template; as such we don't need to
// tax the host element with `_host_` bindings for the `dom-if`.
// However, in the event it is nested in a `dom-repeat`, it is still
// important that its host properties are added to the
// TemplateInstance's `hostProps` so that they are forwarded to the
// TemplateInstance.
if (hostProps) {
templateInfo.hostProps =
Object.assign(templateInfo.hostProps || {}, hostProps);
// Ensure the dom-if is noted so that it has a __dataHost, since
// `fastDomIf` uses the host for runtime template stamping; note this
// was already ensured above in the `removeNestedTemplates` case
if (!removeNestedTemplates) {
nodeInfo.parentInfo.noted = true;
}
}
} else {
let mode = '{';
for (let source in hostProps) {
let parts = [{ mode, source, dependencies: [source], hostProp: true }];
addBinding(this, templateInfo, nodeInfo, 'property', '_host_' + source, parts);
}
}
return noted;
}
Expand Down
Loading

0 comments on commit 42f00cd

Please sign in to comment.