Consistently use TreeApi.Composed api for composed dom manipulation; use TreeApi.Logical methods to get node leaves. Avoid making a Polymer.dom when TreeApi.Logical can provide the needed info.

This commit is contained in:
Steven Orvell 2015-12-09 17:11:44 -08:00
parent 8272d5e2bf
commit 5033fdb29c
5 changed files with 91 additions and 77 deletions

View File

@ -56,7 +56,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
_distributeParent: function() {
if (!useShadow) {
this.domApi._distributeParent();
this.domApi._maybeDistributeParent();
}
},

View File

@ -25,9 +25,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
return;
}
var nativeInsertBefore = Element.prototype.insertBefore;
var nativeRemoveChild = Element.prototype.removeChild;
var nativeAppendChild = Element.prototype.appendChild;
var nativeCloneNode = Element.prototype.cloneNode;
var nativeImportNode = Document.prototype.importNode;
@ -73,11 +70,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
// if adding to a shadyRoot, add to host instead
var container = this.node._isShadyRoot ? this.node.host : this.node;
if (ref_node) {
nativeInsertBefore.call(container, node, ref_node);
TreeApi.Composed.insertBefore(container, node, ref_node);
} else {
nativeAppendChild.call(container, node);
TreeApi.Composed.appendChild(container, node);
}
TreeApi.Composed.recordInsertBefore(container, node, ref_node);
}
this.notifyObserver();
return node;
@ -105,8 +101,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
This method also performs dom composition.
*/
removeChild: function(node) {
if (dom(node).parentNode !== this.node) {
console.warn('The node to be removed is not a child of this node',
if (TreeApi.Logical.getParentNode(node) !== this.node) {
throw Error('The node to be removed is not a child of this node: ' +
node);
}
if (!this._removeNode(node)) {
@ -115,8 +111,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
// not guaranteed to physically be in container; e.g.
// undistributed nodes.
if (container === node.parentNode) {
nativeRemoveChild.call(container, node);
TreeApi.Composed.recordRemoveChild(container, node);
TreeApi.Composed.removeChild(container, node);
}
}
this.notifyObserver();
@ -128,8 +123,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
// note that it's possible for both the node's host and its parent
// to require distribution... both cases are handled here.
_removeNode: function(node) {
TreeApi.Composed.recordRemoveChild(
TreeApi.Composed.getParentNode(node), node);
// important that we want to do this only if the node has a logical parent
var logicalParent = TreeApi.Logical.hasParentNode(node) &&
TreeApi.Logical.getParentNode(node);
@ -137,7 +130,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
var root = this._ownerShadyRootForNode(node);
if (logicalParent) {
// distribute node's parent iff needed
distributed = dom(node)._distributeParent();
distributed = dom(node)._maybeDistributeParent();
TreeApi.Logical.recordRemoveChild(node, logicalParent);
// remove node from root and distribute it iff needed
if (root && this._removeDistributedChildren(root, node)) {
@ -175,7 +168,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
if (node._isShadyRoot) {
root = node;
} else {
var parent = Polymer.dom(node).parentNode;
var parent = TreeApi.Logical.getParentNode(node);
if (parent) {
root = parent._isShadyRoot ? parent :
this._ownerShadyRootForNode(parent);
@ -196,9 +189,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
// distributeContent(true), which updates insertion points manually
// and forces distribution.
var fragContent = (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) &&
!node.__noContent && Polymer.dom(node).querySelector(CONTENT);
!node.__noContent && dom(node).querySelector(CONTENT);
var wrappedContent = fragContent &&
(Polymer.dom(fragContent).parentNode.nodeType !==
(TreeApi.Logical.getParentNode(fragContent).nodeType !==
Node.DOCUMENT_FRAGMENT_NODE);
var hasContent = fragContent || (node.localName === CONTENT);
// There are 2 possible cases where a distribution may need to occur:
@ -234,7 +227,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
!node.__noContent) {
var c$ = dom(node).querySelectorAll(CONTENT);
for (var i=0, n, np, na; (i<c$.length) && (n=c$[i]); i++) {
np = dom(n).parentNode;
np = TreeApi.Logical.getParentNode(n);
// don't allow node's parent to be fragment itself
if (np === node) {
np = parent;
@ -254,7 +247,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
if (this.node.shadyRoot) {
var parent = TreeApi.Composed.getParentNode(node);
if (parent) {
nativeRemoveChild.call(parent, node);
TreeApi.Composed.removeChild(parent, node);
}
return true;
}
@ -267,7 +260,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
for (var i=0, c; i < i$.length; i++) {
c = i$[i];
TreeApi.Logical.saveChildNodes(c);
TreeApi.Logical.saveChildNodes(dom(c).parentNode);
TreeApi.Logical.saveChildNodes(TreeApi.Logical.getParentNode(c));
}
},
@ -301,8 +294,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
var node = dc$[j];
var parent = node.parentNode;
if (parent) {
nativeRemoveChild.call(parent, node);
TreeApi.Composed.recordRemoveChild(parent, node);
TreeApi.Composed.removeChild(parent, node);
}
}
}
@ -315,14 +307,14 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
if (node == container) {
return true;
}
node = dom(node).parentNode;
node = TreeApi.Logical.getParentNode(node);
}
},
_removeOwnerShadyRoot: function(node) {
// optimization: only reset the tree if node is actually in a root
if (this._hasCachedOwnerRoot(node)) {
var c$ = dom(node).childNodes;
var c$ = TreeApi.Logical.getChildNodes(node);
for (var i=0, l=c$.length, n; (i<l) && (n=c$[i]); i++) {
this._removeOwnerShadyRoot(n);
}
@ -358,7 +350,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
_query: function(matcher, node) {
node = node || this.node;
var list = [];
this._queryElements(dom(node).childNodes, matcher, list);
this._queryElements(TreeApi.Logical.getChildNodes(node), matcher, list);
return list;
},
@ -374,7 +366,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
if (matcher(node)) {
list.push(node);
}
this._queryElements(dom(node).childNodes, matcher, list);
this._queryElements(TreeApi.Logical.getChildNodes(node), matcher, list);
},
getDestinationInsertionPoints: function() {
@ -393,15 +385,15 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
setAttribute: function(name, value) {
this.node.setAttribute(name, value);
this._distributeParent();
this._maybeDistributeParent();
},
removeAttribute: function(name) {
this.node.removeAttribute(name);
this._distributeParent();
this._maybeDistributeParent();
},
_distributeParent: function() {
_maybeDistributeParent: function() {
if (this._nodeNeedsDistribution(this.parentNode)) {
this._lazyDistribute(this.parentNode);
return true;
@ -427,7 +419,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
this.node.ownerDocument;
var n = nativeImportNode.call(doc, externalNode, false);
if (deep) {
var c$ = dom(externalNode).childNodes;
var c$ = TreeApi.Logical.getChildNodes(externalNode);
var d = dom(n);
for (var i=0, nc; i < c$.length; i++) {
nc = dom(doc).importNode(c$[i], true);
@ -455,45 +447,48 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
children: {
get: function() {
return Array.prototype.filter.call(this.childNodes, function(n) {
return (n.nodeType === Node.ELEMENT_NODE);
});
if (TreeApi.Logical.hasChildNodes(this.node)) {
return Array.prototype.filter.call(this.childNodes, function(n) {
return (n.nodeType === Node.ELEMENT_NODE);
});
} else {
return TreeApi.arrayCopyChildren(this.node);
}
},
configurable: true
},
parentNode: {
get: function() {
return this.node.__parentNode ||
TreeApi.Composed.getParentNode(this.node);
return TreeApi.Logical.getParentNode(this.node);
},
configurable: true
},
firstChild: {
get: function() {
return this.node.__firstChild || this.node.firstChild;
return TreeApi.Logical.getFirstChild(this.node);
},
configurable: true
},
lastChild: {
get: function() {
return this.node.__lastChild || this.node.lastChild;
return TreeApi.Logical.getLastChild(this.node);
},
configurable: true
},
nextSibling: {
get: function() {
return this.node.__nextSibling || this.node.nextSibling;
return TreeApi.Logical.getNextSibling(this.node);
},
configurable: true
},
previousSibling: {
get: function() {
return this.node.__previousSibling || this.node.previousSibling;
return TreeApi.Logical.getPreviousSibling(this.node);
},
configurable: true
},

View File

@ -85,7 +85,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
var c$ = this.childNodes;
for (var i=0, l=c$.length, c; (i<l) && (c=c$[i]); i++) {
if (c.localName === CONTENT) {
var d$ = DomApi.factory(c).getDistributedNodes();
var d$ = dom(c).getDistributedNodes();
for (var j=0; j < d$.length; j++) {
list.push(d$[j]);
}
@ -140,7 +140,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
var CONTENT = DomApi.CONTENT = 'content';
DomApi.factory = function(node, patch) {
var dom = DomApi.factory = function(node, patch) {
node = node || document;
if (!node.__domApi) {
node.__domApi = new DomApi(node, patch);

View File

@ -16,6 +16,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
// native add/remove
var nativeInsertBefore = Element.prototype.insertBefore;
var nativeAppendChild = Element.prototype.appendChild;
var nativeRemoveChild = Element.prototype.removeChild;
/**
@ -63,6 +64,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
getChildNodes: function(node) {
// note: we're distinguishing here between undefined and false-y:
// hasChildNodes uses undefined check to see if this element has logical
// children; the false-y check indicates whether or not we should rebuild
// the cached childNodes array.
if (this.hasChildNodes(node)) {
if (!node.__childNodes) {
node.__childNodes = [];
@ -80,7 +85,23 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
getParentNode: function(node) {
return node.__parentNode || node.parentNode;
return node.__parentNode || TreeApi.Composed.getParentNode(node);
},
getFirstChild: function(node) {
return node.__firstChild || TreeApi.Composed.getFirstChild(node);
},
getLastChild: function(node) {
return node.__lastChild || TreeApi.Composed.getLastChild(node);
},
getNextSibling: function(node) {
return node.__nextSibling || TreeApi.Composed.getNextSibling(node);
},
getPreviousSibling: function(node) {
return node.__previousSibling || TreeApi.Composed.getPreviousSibling(node);
},
// Capture the list of light children. It's important to do this before we
@ -119,20 +140,23 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
_linkNode: function(node, container, ref_node) {
// update node <-> ref_node
// update ref_node.previousSibling <-> node
node.__previousSibling = ref_node ? ref_node.__previousSibling :
container.__lastChild;
if (node.__previousSibling) {
node.__previousSibling.__nextSibling = node;
}
// update node <-> ref_node
node.__nextSibling = ref_node;
if (ref_node) {
ref_node.__previousSibling = node;
if (node.__nextSibling) {
node.__nextSibling.__previousSibling = node;
}
// update node <-> container
node.__parentNode = container;
if (ref_node && ref_node === container.__firstChild) {
container.__firstChild = node;
if (ref_node) {
if (ref_node === container.__firstChild) {
container.__firstChild = node;
}
} else {
container.__lastChild = node;
if (!container.__firstChild) {
@ -182,13 +206,20 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
return node.parentNode;
},
recordInsertBefore: function(parent, node, ref_node) {
getFirstChild: function(node) {
return node.firstChild;
},
recordRemoveChild: function(parent, node) {
getLastChild: function(node) {
return node.lastChild;
},
recordParentNode: function(node, parent) {
getNextSibling: function(node) {
return node.nextSibling;
},
getPreviousSibling: function(node) {
return node.previousSibling;
},
// composed tracking needs to reset composed children here in case
@ -200,24 +231,15 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
},
insertBefore: function(parentNode, newChild, refChild) {
var newChildParent = this.getParentNode(newChild);
if (newChildParent !== parentNode) {
this.recordRemoveChild(newChildParent, newChild);
}
// remove child from its old parent first
this.removeChild(newChild);
// insert it into the real DOM
nativeInsertBefore.call(parentNode, newChild, refChild || null);
this.recordParentNode(newChild, parentNode);
return nativeInsertBefore.call(parentNode, newChild, refChild || null);
},
removeChild: function(node) {
var parentNode = this.getParentNode(node);
if (parentNode) {
this.recordParentNode(node, null);
// remove it from the real DOM
nativeRemoveChild.call(parentNode, node);
}
appendChild: function(parentNode, newChild) {
return nativeAppendChild.call(parentNode, newChild);
},
removeChild: function(parentNode, node) {
return nativeRemoveChild.call(parentNode, node);
}
};

View File

@ -121,22 +121,19 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
*/
distributeContent: function(updateInsertionPoints) {
if (this.shadyRoot) {
var dom = Polymer.dom(this);
if (updateInsertionPoints) {
dom._updateInsertionPoints(this);
}
this.shadyRoot._invalidInsertionPoints =
this.shadyRoot._invalidInsertionPoints || updateInsertionPoints;
// Distribute the host that's the top of this element's distribution
// tree. Distributing that host will *always* distibute this element.
var host = getTopDistributingHost(this);
dom._lazyDistribute(host);
Polymer.dom(this)._lazyDistribute(host);
}
},
_distributeContent: function() {
if (this._useContent && !this.shadyRoot._distributionClean) {
if (this.shadyRoot._invalidInsertionPoints) {
var dom = Polymer.dom(this);
dom._updateInsertionPoints(this);
Polymer.dom(this)._updateInsertionPoints(this);
this.shadyRoot._invalidInsertionPoints = false;
}
// logically distribute self
@ -298,7 +295,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
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++) {
parent = Polymer.dom(p).parentNode;
parent = TreeApi.Logical.getParentNode(p);
if (!parent._useContent && (parent !== this) &&
(parent !== this.shadyRoot)) {
this._updateChildNodes(parent, this._composeNode(parent));
@ -340,7 +337,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
// then schedules its previous host for distribution resulting in
// the node being removed here.
if (TreeApi.Composed.getParentNode(n) === container) {
TreeApi.Composed.removeChild(n);
TreeApi.Composed.removeChild(container, n);
}
composed.splice(s.index + d, 1);
}
@ -452,10 +449,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
// Return true if a host's children includes
// an insertion point that selects selectively
function hostNeedsRedistribution(host) {
var c$ = Polymer.dom(host).children;
var c$ = TreeApi.Logical.getChildNodes(host);
for (var i=0, c; i < c$.length; i++) {
c = c$[i];
if (c.localName === 'content') {
if (c.localName && c.localName === 'content') {
return host.domHost;
}
}