Skip to content

Commit

Permalink
Merge pull request #1320 from Polymer/0.8-shady-opt
Browse files Browse the repository at this point in the history
Shady DOM optimizations: (1) fast path distribution when no insertion po...
  • Loading branch information
frankiefu committed Mar 20, 2015
2 parents b8ab5b8 + c090390 commit c9751ca
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 119 deletions.
132 changes: 75 additions & 57 deletions src/features/mini/shady.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
_prepContent: function() {
// Use this system iff localDom is needed.
this._useContent = this._useContent || Boolean(this._template);
if (this._useContent) {
this._template._hasInsertionPoint =
this._template.content.querySelector('content');
}
},

// called as part of content initialization, prior to template stamping
Expand All @@ -43,6 +47,12 @@
_createLocalRoot: function() {
this.shadyRoot = this.root;
this.shadyRoot._isShadyRoot = true;
// capture insertion point list
// TODO(sorvell): it's faster to do this via native qSA than annotator.
this.shadyRoot._insertionPoints = this._template._hasInsertionPoint ?
this.shadyRoot.querySelectorAll('content') : [];
// save logical tree info for shadyRoot.
saveLightChildrenIfNeeded(this.shadyRoot);
this.shadyRoot.host = this;
},

Expand All @@ -66,7 +76,7 @@
},

_beginDistribute: function() {
if (this._useContent) {
if (this._useContent && hasInsertionPoint(this.shadyRoot)) {
// reset distributions
this._resetDistribution(this.shadyRoot);
// compute which nodes should be distributed where
Expand All @@ -79,8 +89,21 @@
_finishDistribute: function() {
// compose self
if (this._useContent) {
this._composeTree(this);
} else {
if (hasInsertionPoint(this.shadyRoot)) {
this._composeTree();
} else {
if (!this.shadyRoot._hasDistributed) {
this.textContent = '';
this.appendChild(this.shadyRoot);
} else {
// simplified non-tree walk composition
var children = this._composeNode(this);
this._updateChildNodes(this, children);
}
}
this.shadyRoot._hasDistributed = true;
} else if (this.root !== this) {
this.appendChild(this.root);
this.root = this;
}
this._distributionClean = true;
Expand All @@ -100,15 +123,18 @@
// included here as "protected" methods to allow overriding.

_resetDistribution: function(node) {
// light children
var children = getLightChildren(node);
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (isInsertionPoint(child)) {
child._distributedNodes = [];
} else if (child._destinationInsertionPoints) {
if (child._destinationInsertionPoints) {
child._destinationInsertionPoints = undefined;
}
this._resetDistribution(child);
}
// insertion points
var p$ = node._insertionPoints;
for (var j = 0; j < p$.length; j++) {
p$[j]._distributedNodes = [];
}
},

Expand All @@ -132,52 +158,43 @@
// instead elements are distributed into a `content._distributedNodes`
// array where applicable.
_distributePool: function(node, pool) {
// TODO(sorvell): is this the best place to set this?
node._ownerShadyRoot = this.shadyRoot;
var children;
if (isInsertionPoint(node)) {
this.shadyRoot._hasInsertionPoint = true;
// distribute nodes from the pool that this selector matches
var content = node;
var anyDistributed = false;
for (var i = 0; i < pool.length; i++) {
node = pool[i];
// skip nodes that were already used
if (!node) {
continue;
}
// distribute this node if it matches
if (this._matchesContentSelect(node, content)) {
distributeNodeInto(node, content);
// remove this node from the pool
pool[i] = undefined;
// since at least one node matched, we won't need fallback content
anyDistributed = true;
var parent = content.lightParent;
if (parent && parent.shadyRoot &&
parent.shadyRoot._hasInsertionPoint) {
//console.warn('marked dirty', this.is);
parent._distributionClean = false;
}
}
}
// Fallback content if nothing was distributed here
if (!anyDistributed) {
children = getLightChildren(content);
for (var i = 0; i < children.length; i++) {
distributeNodeInto(children[i], content);
var p$ = node._insertionPoints;
for (var i=0, l=p$.length, p; (i<l) && (p=p$[i]); i++) {
this._distributeInsertionPoint(p, pool);
}
},

_distributeInsertionPoint: function(content, pool) {
// distribute nodes from the pool that this selector matches
var anyDistributed = false;
for (var i=0, l=pool.length, node; i < l; i++) {
node=pool[i];
// skip nodes that were already used
if (!node) {
continue;
}
// distribute this node if it matches
if (this._matchesContentSelect(node, content)) {
distributeNodeInto(node, content);
// remove this node from the pool
pool[i] = undefined;
// since at least one node matched, we won't need fallback content
anyDistributed = true;
var parent = content.lightParent;
// dirty a shadyRoot if a change may trigger reprojection!
if (parent && parent.shadyRoot &&
hasInsertionPoint(parent.shadyRoot)) {
parent._distributionClean = false;
}
}
return true;
}
// recursively distribute.
children = getLightChildren(node);
var hasInsertionPoint;
for (var i = 0; i < children.length; i++) {
hasInsertionPoint = this._distributePool(children[i], pool) ||
hasInsertionPoint;
// Fallback content if nothing was distributed here
if (!anyDistributed) {
children = getLightChildren(content);
for (var j = 0; j < children.length; j++) {
distributeNodeInto(children[j], content);
}
}
return hasInsertionPoint;
},

// returns a list of elements that support content distribution
Expand All @@ -188,16 +205,16 @@

// Reify dom such that it is at its correct rendering position
// based on logical distribution.
_composeTree: function(node) {
var children = this._composeNode(node);
for (var i = 0; i < children.length; i++) {
var child = children[i];
// If the child has a content root, let it compose itself.
if (!child._useContent) {
this._composeTree(child);
_composeTree: function() {
this._updateChildNodes(this, this._composeNode(this));
var p$ = this.shadyRoot._insertionPoints;
for (var i=0, l=p$.length, p, parent; (i<l) && (p=p$[i]); i++) {
var parent = p.lightParent || p.parentNode;
if (!parent._useContent && (parent !== this) &&
(parent !== this.shadyRoot)) {
this._updateChildNodes(parent, this._composeNode(parent));
}
}
this._updateChildNodes(node, children);
},

// Returns the list of nodes which should be rendered inside `node`.
Expand Down Expand Up @@ -286,6 +303,7 @@
var saveLightChildrenIfNeeded = Polymer.DomApi.saveLightChildrenIfNeeded;
var getLightChildren = Polymer.DomApi.getLightChildren;
var matchesSelector = Polymer.DomApi.matchesSelector;
var hasInsertionPoint = Polymer.DomApi.hasInsertionPoint;

function distributeNodeInto(child, insertionPoint) {
insertionPoint._distributedNodes.push(child);
Expand Down
63 changes: 54 additions & 9 deletions src/lib/dom-api.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@
var self = this;
this.node.appendChild = function(node) {
return self.appendChild(node);
}
};
this.node.insertBefore = function(node, ref_node) {
return self.insertBefore(node, ref_node);
}
};
this.node.removeChild = function(node) {
return self.removeChild(node);
}
};
},

get childNodes() {
Expand Down Expand Up @@ -162,29 +162,69 @@
}
},

replaceChild: function(node, ref_node) {
this.insertBefore(node, ref_node);
this.removeChild(ref_node);
},

_getOwnerShadyRoot: function() {
return this._ownerShadyRootForNode(this.node);
},

_ownerShadyRootForNode: function(node) {
if (node._ownerShadyRoot === undefined) {
var root;
if (node._isShadyRoot) {
root = node;
} else if (node.host) {
root = node.host.shadyRoot;
} else {
var parent = Polymer.dom(node).parentNode;
if (parent) {
root = parent._isShadyRoot ? parent :
this._ownerShadyRootForNode(parent);
} else {
root = null;
}
}
node._ownerShadyRoot = root;
}
return node._ownerShadyRoot;

},

_maybeDistribute: function(node, parent, host) {
var nodeNeedsDistribute = this._nodeNeedsDistribution(node);
var distribute = this._parentNeedsDistribution(parent) ||
this._nodeNeedsDistribution(node);
nodeNeedsDistribute;
if (nodeNeedsDistribute) {
this._updateInsertionPoints(host);
}
if (distribute) {
this._lazyDistribute(host);
}
return distribute;
},

_updateInsertionPoints: function(host) {
host.shadyRoot._insertionPoints =
factory(host.shadyRoot).querySelectorAll(CONTENT);
},

_nodeIsInLogicalTree: function(node) {
return Boolean(node._isShadyRoot ||
node._ownerShadyRoot ||
this._getOwnerShadyRoot(node) ||
node.shadyRoot);
},

_hostForNode: function(node) {
var root = node.shadyRoot || (node._isShadyRoot ?
node : node._ownerShadyRoot);
node : this._getOwnerShadyRoot(node));
return root && root.host;
},

_parentNeedsDistribution: function(parent) {
return parent.shadyRoot && parent.shadyRoot._hasInsertionPoint;
return parent.shadyRoot && hasInsertionPoint(parent.shadyRoot);
},

// TODO(sorvell): technically we should check non-fragment nodes for
Expand Down Expand Up @@ -235,7 +275,7 @@
children.splice(index, 1);
node.lightParent = null;
// TODO(sorvell): need to clear any children of element?
node._ownerShadyRoot = null;
node._ownerShadyRoot = undefined;
},

_addRootToChildren: function(children, root) {
Expand Down Expand Up @@ -354,7 +394,7 @@
node.__domApi = new DomApi(node, patch);
}
return node.__domApi;
}
};

Polymer.dom = function(obj, patch) {
if (obj instanceof Event) {
Expand Down Expand Up @@ -390,6 +430,10 @@
}
}

function hasInsertionPoint(root) {
return Boolean(root._insertionPoints.length);
}

var p = Element.prototype;
var matchesSelector = p.matches || p.matchesSelector ||
p.mozMatchesSelector || p.msMatchesSelector ||
Expand All @@ -399,6 +443,7 @@
getLightChildren: getLightChildren,
saveLightChildrenIfNeeded: saveLightChildrenIfNeeded,
matchesSelector: matchesSelector,
hasInsertionPoint: hasInsertionPoint,
factory: factory
};

Expand Down
6 changes: 3 additions & 3 deletions src/lib/event-api.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@

get localTarget() {
var current = this.event.currentTarget;
var currentRoot = current && current._ownerShadyRoot;
var currentRoot = current && Polymer.dom(current)._getOwnerShadyRoot();
var p$ = this.path;
for (var i=0; i < p$.length; i++) {
if (p$[i]._ownerShadyRoot === currentRoot) {
if (Polymer.dom(p$[i])._getOwnerShadyRoot() === currentRoot) {
return p$[i];
}
}
Expand Down Expand Up @@ -81,7 +81,7 @@
event.__eventApi = new EventApi(event);
}
return event.__eventApi;
}
};

return {
factory: factory
Expand Down
Loading

0 comments on commit c9751ca

Please sign in to comment.