Skip to content

Commit

Permalink
Factoring of distribution logic in both add and remove cases.
Browse files Browse the repository at this point in the history
  • Loading branch information
Steven Orvell committed Dec 8, 2015
1 parent dfa6a44 commit 8272d5e
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 84 deletions.
161 changes: 77 additions & 84 deletions src/lib/dom-api-shady.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,7 @@
},

appendChild: function(node) {
return this._addNode(node);
},

insertBefore: function(node, ref_node) {
return this._addNode(node, ref_node);
return this.insertBefore(node);
},

// cases in which we may not be able to just do standard native call
Expand All @@ -57,25 +53,18 @@
// 2. container is a shadyRoot (don't distribute, instead set
// container to container.host.
// 3. node is <content> (host of container needs distribution)
_addNode: function(node, ref_node) {
this._removeNodeFromParent(node);
var addedInsertionPoint;
var root = this.getOwnerRoot();
// if a <content> is added, make sure it's parent has logical info.
if (root) {
addedInsertionPoint = this._maybeAddInsertionPoint(node, this.node);
insertBefore: function(node, ref_node) {
if (ref_node && TreeApi.Logical.getParentNode(ref_node) !== this.node) {
throw Error('The ref_node to be inserted before is not a child ' +
'of this node');
}
if (TreeApi.Logical.hasChildNodes(this.node)) {
if (ref_node && ref_node.__parentNode !== this.node) {
throw Error('The ref_node to be inserted before is not a child ' +
'of this node');
}
TreeApi.Logical.recordInsertBefore(node, this.node, ref_node);
// notify existing parent that this node is being removed.
var parent = TreeApi.Logical.getParentNode(node);
if (parent && DomApi.hasApi(parent)) {
dom(parent).notifyObserver();
}
this._addNodeToHost(node);
// if not distributing and not adding to host, do a fast path addition
if (!this._maybeDistribute(node, this.node) &&
!this._tryRemoveUndistributedNode(node)) {
this._removeNode(node);
if (!this._addNode(node, ref_node)) {
if (ref_node) {
// if ref_node is <content> replace with first distributed node
ref_node = ref_node.localName === CONTENT ?
Expand All @@ -90,13 +79,27 @@
}
TreeApi.Composed.recordInsertBefore(container, node, ref_node);
}
if (addedInsertionPoint) {
this._updateInsertionPoints(root.host);
}
this.notifyObserver();
return node;
},

// Try to add node. Record logical info, track insertion points, perform
// distribution iff needed. Return true if the add is handled.
_addNode: function(node, ref_node) {
var root = this.getOwnerRoot();
if (root) {
root._invalidInsertionPoints =
this._maybeAddInsertionPoint(node, this.node);
this._addNodeToHost(root.host, node);
}
if (TreeApi.Logical.hasChildNodes(this.node)) {
TreeApi.Logical.recordInsertBefore(node, this.node, ref_node);
}
// if not distributing and not adding to host, do a fast path addition
return (this._maybeDistribute(node) ||
this._tryRemoveUndistributedNode(node));
},

/**
Removes the given `node` from the element's `lightChildren`.
This method also performs dom composition.
Expand All @@ -106,8 +109,7 @@
console.warn('The node to be removed is not a child of this node',
node);
}
this._removeNodeFromHost(node);
if (!this._maybeDistribute(node, this.node)) {
if (!this._removeNode(node)) {
// if removing from a shadyRoot, remove form host instead
var container = this.node._isShadyRoot ? this.node.host : this.node;
// not guaranteed to physically be in container; e.g.
Expand All @@ -121,6 +123,35 @@
return node;
},

// Try to remove node: update logical info and perform distribution iff
// needed. Return true if the removal has been handled.
// 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);
var distributed;
var root = this._ownerShadyRootForNode(node);
if (logicalParent) {
// distribute node's parent iff needed
distributed = dom(node)._distributeParent();
TreeApi.Logical.recordRemoveChild(node, logicalParent);
// remove node from root and distribute it iff needed
if (root && this._removeDistributedChildren(root, node)) {
root._invalidInsertionPoints = true;
this._lazyDistribute(root.host);
}
}
this._removeOwnerShadyRoot(node);
if (root) {
this._removeNodeFromHost(root.host, node);
}
return distributed;
},

replaceChild: function(node, ref_node) {
this.insertBefore(node, ref_node);
this.removeChild(ref_node);
Expand Down Expand Up @@ -157,7 +188,7 @@
return node._ownerShadyRoot;
},

_maybeDistribute: function(node, parent) {
_maybeDistribute: function(node) {
// TODO(sorvell): technically we should check non-fragment nodes for
// <content> children but since this case is assumed to be exceedingly
// rare, we avoid the cost and will address with some specific api
Expand All @@ -176,24 +207,23 @@
// 2. children being inserted into parent with a shady root (parent
// needs distribution)
if (hasContent) {
var root = this._ownerShadyRootForNode(parent);
var root = this.getOwnerRoot();
if (root) {
var host = root.host;
// note, insertion point list update is handled after node
// mutations are complete
this._lazyDistribute(host);
this._lazyDistribute(root.host);
}
}
var parentNeedsDist = this._parentNeedsDistribution(parent);
if (parentNeedsDist) {
this._lazyDistribute(parent);
var needsDist = this._nodeNeedsDistribution(this.node);
if (needsDist) {
this._lazyDistribute(this.node);
}
// Return true when distribution will fully handle the composition
// Note that if a content was being inserted that was wrapped by a node,
// and the parent does not need distribution, return false to allow
// the nodes to be added directly, after which children may be
// distributed and composed into the wrapping node(s)
return parentNeedsDist || (hasContent && !wrappedContent);
return needsDist || (hasContent && !wrappedContent);
},

/* note: parent argument is required since node may have an out
Expand Down Expand Up @@ -241,51 +271,21 @@
}
},

_parentNeedsDistribution: function(parent) {
return parent && parent.shadyRoot &&
DomApi.hasInsertionPoint(parent.shadyRoot);
_nodeNeedsDistribution: function(node) {
return node && node.shadyRoot &&
DomApi.hasInsertionPoint(node.shadyRoot);
},

_removeNodeFromParent: function(node) {
// note: we may need to notify and not have logical info so fallback
// to composed parentNode.

var parent = node.__parentNode || node.parentNode;
//var parent = dom(node).parentNode;
// TODO(sorvell): hasDomApi doesn't make sense now
if (parent && DomApi.hasApi(parent)) {
dom(parent).notifyObserver();
// a node being added is always in this same host as this.node.
_addNodeToHost: function(host, node) {
if (host._elementAdd) {
host._elementAdd(node);
}
this._removeNodeFromHost(node, true);
},

// NOTE: if `ensureComposedRemoval` is true then the node should be
// removed from its composed parent.
_removeNodeFromHost: function(node, ensureComposedRemoval) {
// note that it's possible for both the node's host and its parent
// to require distribution... both cases are handled here.
var hostNeedsDist;
var root;
// important that we want to do this only if the node has a logical parent
var parent = node.__parentNode;
if (parent) {
// distribute node's parent iff needed
dom(node)._distributeParent();
root = this._ownerShadyRootForNode(node);
// remove node from root and distribute it iff needed
if (root) {
root.host._elementRemove(node);
hostNeedsDist = this._removeDistributedChildren(root, node);
}
TreeApi.Logical.recordRemoveChild(node, parent);
}
this._removeOwnerShadyRoot(node);
if (root && hostNeedsDist) {
this._updateInsertionPoints(root.host);
this._lazyDistribute(root.host);
} else if (ensureComposedRemoval) {
TreeApi.Composed.recordRemoveChild(
TreeApi.Composed.getParentNode(node), node);
_removeNodeFromHost: function(host, node) {
if (host._elementRemove) {
host._elementRemove(node);
}
},

Expand Down Expand Up @@ -319,14 +319,6 @@
}
},

// a node being added is always in this same host as this.node.
_addNodeToHost: function(node) {
var root = this.getOwnerRoot();
if (root) {
root.host._elementAdd(node);
}
},

_removeOwnerShadyRoot: function(node) {
// optimization: only reset the tree if node is actually in a root
if (this._hasCachedOwnerRoot(node)) {
Expand Down Expand Up @@ -410,8 +402,9 @@
},

_distributeParent: function() {
if (this._parentNeedsDistribution(this.parentNode)) {
if (this._nodeNeedsDistribution(this.parentNode)) {
this._lazyDistribute(this.parentNode);
return true;
}
},

Expand Down
8 changes: 8 additions & 0 deletions src/lib/dom-tree-api.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@
return Boolean(node.__childNodes !== undefined);
},

hasParentNode: function(node) {
return Boolean(node.__parentNode);
},

getChildNodes: function(node) {
if (this.hasChildNodes(node)) {
if (!node.__childNodes) {
Expand All @@ -75,6 +79,10 @@
}
},

getParentNode: function(node) {
return node.__parentNode || node.parentNode;
},

// Capture the list of light children. It's important to do this before we
// start transforming the DOM into "rendered" state.
// Children may be added to this list dynamically. It will be treated as the
Expand Down
5 changes: 5 additions & 0 deletions src/mini/shady.html
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@

_distributeContent: function() {
if (this._useContent && !this.shadyRoot._distributionClean) {
if (this.shadyRoot._invalidInsertionPoints) {
var dom = Polymer.dom(this);
dom._updateInsertionPoints(this);
this.shadyRoot._invalidInsertionPoints = false;
}
// logically distribute self
this._beginDistribute();
this._distributeDirtyRoots();
Expand Down

0 comments on commit 8272d5e

Please sign in to comment.