Skip to content

Commit

Permalink
Merge pull request #2293 from Polymer/fix-2253,2283
Browse files Browse the repository at this point in the history
Fixes #2253 and fixes #2283
  • Loading branch information
frankiefu committed Aug 19, 2015
2 parents 36b9fe0 + 36072be commit a455c23
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 80 deletions.
149 changes: 76 additions & 73 deletions src/lib/dom-api.html
Original file line number Diff line number Diff line change
Expand Up @@ -57,64 +57,59 @@
}
},

// cases in which we may not be able to just do standard appendChild
appendChild: function(node) {
return this._addNode(node);
},

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

// cases in which we may not be able to just do standard native call
// 1. container has a shadyRoot (needsDistribution IFF the shadyRoot
// has an insertion point)
// 2. container is a shadyRoot (don't distribute, instead set
// container to container.host.
// 3. node is <content> (host of container needs distribution)
appendChild: function(node) {
var handled;
// if a <content> is added, make sure it's parent has logical info.
this._ensureContentLogicalInfo(node);
_addNode: function(node, ref_node) {
this._removeNodeFromHost(node, true);
if (this._nodeIsInLogicalTree(this.node)) {
this._addLogicalInfo(node, this.node);
this._addNodeToHost(node);
handled = this._maybeDistribute(node, this.node);
} else {
this._addNodeToHost(node);
}
// if not distributing and not adding to host, do a fast path addition
if (!handled && !this._tryRemoveUndistributedNode(node)) {
// if adding to a shadyRoot, add to host instead
var container = this.node._isShadyRoot ? this.node.host : this.node;
addToComposedParent(container, node);
nativeAppendChild.call(container, node);
}
return node;
},

insertBefore: function(node, ref_node) {
if (!ref_node) {
return this.appendChild(node);
}
var handled;
var addedInsertionPoint;
var root = this.getOwnerRoot();
// if a <content> is added, make sure it's parent has logical info.
this._ensureContentLogicalInfo(node);
this._removeNodeFromHost(node, true);
if (this._nodeIsInLogicalTree(this.node)) {
var children = this.childNodes;
var index = children.indexOf(ref_node);
if (index < 0) {
throw Error('The ref_node to be inserted before is not a child ' +
'of this node');
if (root) {
addedInsertionPoint = this._maybeAddInsertionPoint(node, this.node);
}
if (this._nodeHasLogicalChildren(this.node)) {
if (ref_node) {
var children = this.childNodes;
var index = children.indexOf(ref_node);
if (index < 0) {
throw Error('The ref_node to be inserted before is not a child ' +
'of this node');
}
}
this._addLogicalInfo(node, this.node, index);
this._addNodeToHost(node);
handled = this._maybeDistribute(node, this.node);
} else {
this._addNodeToHost(node);
}
this._addNodeToHost(node);
// if not distributing and not adding to host, do a fast path addition
if (!handled && !this._tryRemoveUndistributedNode(node)) {
// if ref_node is <content> replace with first distributed node
ref_node = ref_node.localName === CONTENT ?
this._firstComposedNode(ref_node) : ref_node;
if (!this._maybeDistribute(node, this.node) &&
!this._tryRemoveUndistributedNode(node)) {
if (ref_node) {
// if ref_node is <content> replace with first distributed node
ref_node = ref_node.localName === CONTENT ?
this._firstComposedNode(ref_node) : ref_node;
}
// if adding to a shadyRoot, add to host instead
var container = this.node._isShadyRoot ? this.node.host : this.node;
addToComposedParent(container, node, ref_node);
nativeInsertBefore.call(container, node, ref_node);
if (ref_node) {
nativeInsertBefore.call(container, node, ref_node);
} else {
nativeAppendChild.call(container, node);
}
}
if (addedInsertionPoint) {
this._updateInsertionPoints(root.host);
}
return node;
},
Expand All @@ -128,14 +123,8 @@
console.warn('The node to be removed is not a child of this node',
node);
}
var handled;
if (this._nodeIsInLogicalTree(this.node)) {
this._removeNodeFromHost(node);
handled = this._maybeDistribute(node, this.node);
} else {
this._removeNodeFromHost(node);
}
if (!handled) {
this._removeNodeFromHost(node);
if (!this._maybeDistribute(node, this.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 Down Expand Up @@ -206,7 +195,8 @@
var root = this._ownerShadyRootForNode(parent);
if (root) {
var host = root.host;
this._updateInsertionPoints(host);
// note, insertion point list update is handled after node
// mutations are complete
this._lazyDistribute(host);
}
}
Expand All @@ -222,6 +212,30 @@
return parentNeedsDist || (hasContent && !wrappedContent);
},

/* note: parent argument is required since node may have an out
of date parent at this point; returns true if a <content> is being added */
_maybeAddInsertionPoint: function(node, parent) {
var added;
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE &&
!node.__noContent) {
var c$ = factory(node).querySelectorAll(CONTENT);
for (var i=0, n, np, na; (i<c$.length) && (n=c$[i]); i++) {
np = factory(n).parentNode;
// don't allow node's parent to be fragment itself
if (np === node) {
np = parent;
}
na = this._maybeAddInsertionPoint(n, np);
added = added || na;
}
} else if (node.localName === CONTENT) {
saveLightChildrenIfNeeded(parent);
saveLightChildrenIfNeeded(node);
added = true;
}
return added;
},

_tryRemoveUndistributedNode: function(node) {
if (this.node.shadyRoot) {
if (node._composedParent) {
Expand All @@ -242,25 +256,9 @@
}
},

// a node is in a shadyRoot, is a shadyRoot,
// or has a lightParent
_nodeIsInLogicalTree: function(node) {
return Boolean((node._lightParent !== undefined) || node._isShadyRoot ||
node.shadyRoot);
},

_ensureContentLogicalInfo: function(node) {
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
saveLightChildrenIfNeeded(this.node);
var c$ = Array.prototype.slice.call(node.childNodes);
for (var i=0, n; (i<c$.length) && (n=c$[i]); i++) {
this._ensureContentLogicalInfo(n);
}
} else if (node.localName === CONTENT) {
// should be parent not this.node, but this is before parent is set.
saveLightChildrenIfNeeded(this.node);
saveLightChildrenIfNeeded(node);
}
// a node has logical children
_nodeHasLogicalChildren: function(node) {
return Boolean(node._lightChildren !== undefined);
},

_parentNeedsDistribution: function(parent) {
Expand All @@ -270,11 +268,16 @@
// 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;
var parent = node._lightParent;
if (parent) {
// distribute node's parent iff needed
factory(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);
Expand Down Expand Up @@ -938,7 +941,7 @@
}

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

var p = Element.prototype;
Expand Down
59 changes: 52 additions & 7 deletions test/unit/polymer-dom-content.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<dom-module id="x-dist">
<template>
x-dist
<div id="distWrapper"><content></content></div>
<div id="distWrapper"><content id="content"></content></div>
</template>
<script>
HTMLImports.whenReady(function() {
Expand Down Expand Up @@ -62,10 +62,16 @@

<dom-module id="x-dynamic-content">
<template>
<div id="container">
<template is="dom-if" id="domif">
<content select=".insert" id="dynamicContent"></content>
</template>
<div>
<div>
<div>
<div id="container">
<template is="dom-if" id="domif">
<content select=".insert" id="dynamicContent"></content>
</template>
</div>
</div>
</div>
</div>
</template>
<script>
Expand Down Expand Up @@ -832,6 +838,45 @@
}
});

test('moving children between distributing hosts', function() {
var h1 = document.createElement('x-dist');
var h2 = document.createElement('x-dist');
document.body.appendChild(h1);
document.body.appendChild(h2);
Polymer.dom.flush();
var d = document.createElement('div');
Polymer.dom(h1).appendChild(d);
Polymer.dom.flush();
assert.equal(Polymer.dom(h1).childNodes.length, 1);
assert.equal(Polymer.dom(h1).firstElementChild, d);
assert.deepEqual(Polymer.dom(h1.$.content).getDistributedNodes(), [d]);
assert.equal(Polymer.dom(h2).childNodes.length, 0);
assert.deepEqual(Polymer.dom(h2.$.content).getDistributedNodes().length, 0);
Polymer.dom(h2).appendChild(d);
Polymer.dom.flush();
assert.equal(Polymer.dom(h2).childNodes.length, 1);
assert.equal(Polymer.dom(h2).firstElementChild, d);
assert.deepEqual(Polymer.dom(h2.$.content).getDistributedNodes(), [d]);
assert.equal(Polymer.dom(h1).childNodes.length, 0);
assert.deepEqual(Polymer.dom(h1.$.content).getDistributedNodes().length, 0);
Polymer.dom(h1).appendChild(d);
Polymer.dom.flush();
assert.equal(Polymer.dom(h1).childNodes.length, 1);
assert.equal(Polymer.dom(h1).firstElementChild, d);
assert.deepEqual(Polymer.dom(h1.$.content).getDistributedNodes(), [d]);
assert.equal(Polymer.dom(h2).childNodes.length, 0);
assert.deepEqual(Polymer.dom(h2.$.content).getDistributedNodes().length, 0);
Polymer.dom(h2).appendChild(d);
Polymer.dom.flush();
assert.equal(Polymer.dom(h2).childNodes.length, 1);
assert.equal(Polymer.dom(h2).firstElementChild, d);
assert.deepEqual(Polymer.dom(h2.$.content).getDistributedNodes(), [d]);
assert.equal(Polymer.dom(h1).childNodes.length, 0);
assert.deepEqual(Polymer.dom(h1.$.content).getDistributedNodes().length, 0);
document.body.removeChild(h1);
document.body.removeChild(h2);
});

});

suite('multi-content mutations', function() {
Expand Down Expand Up @@ -911,7 +956,7 @@
var div = document.createElement('div');
div.classList.add('insert');
Polymer.dom(el).appendChild(div);
assert(!el.querySelector('#container .insert'));
assert.ok(!el.querySelector('#container .insert'));
el.$.domif.if = true;
el.$.domif.render();
Polymer.dom.flush();
Expand Down Expand Up @@ -939,7 +984,7 @@
var div = document.createElement('div');
div.classList.add('insert');
Polymer.dom(el).appendChild(div);
assert(!el.querySelector('#redistContainer .insert'));
assert.ok(!el.querySelector('#redistContainer .insert'));
el.$.redistDomif.if = true;
el.$.redistContainer.$.domif.if = true;
el.$.redistDomif.render();
Expand Down
29 changes: 29 additions & 0 deletions test/unit/polymer-dom-elements.html
Original file line number Diff line number Diff line change
Expand Up @@ -299,4 +299,33 @@
<dom-module id="x-commented">
<template><span>[</span><!--comment--><content></content></span><span>]</span></content></template>
<script>Polymer({is: 'x-commented'});</script>
</dom-module>


<dom-module id="polymer-dom-repeat">
<template>
<div>
<div>
<div>
<div id="container">
<template is="dom-repeat" items="{{items}}">
<div>stuff</div>
</template>
</div>
</div>
</div>
</div>
</template>
<script>
Polymer({
is: 'polymer-dom-repeat',
properties: {
items: {
value: function() {
return ['a', 'b', 'c', 'd', 'e'];
}
}
}
});
</script>
</dom-module>
26 changes: 26 additions & 0 deletions test/unit/polymer-dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ suite('Polymer.dom', function() {
assert(Polymer.dom(p).querySelectorAll('content').length, 1);
});

test('querySelectorAll with dom-repeat', function() {
var el = document.createElement('polymer-dom-repeat');
document.body.appendChild(el);
Polymer.dom.flush();
assert.equal(Polymer.dom(el.$.container).querySelectorAll('*').length, 6, 'querySelectorAll finds repeated elements');
document.body.removeChild(el);
})

test('querySelector document', function() {
assert.ok(Polymer.dom().querySelector('body'));
});
Expand Down Expand Up @@ -424,6 +432,24 @@ suite('Polymer.dom', function() {
assert.equal(Polymer.dom(rere.root).querySelectorAll('span').length, 0);
});

test('mutations using fragments without logical dom', function() {
var d = document.createElement('div');
document.body.appendChild(d);
assert.equal(Polymer.dom(d).childNodes.length, 0);
var frag = document.createDocumentFragment();
var c = document.createElement('div');
frag.appendChild(c);
Polymer.dom(d).appendChild(frag);
assert.equal(Polymer.dom(d).childNodes.length, 1);
assert.equal(Polymer.dom(d).firstChild, c);
var c1 = document.createElement('div');
frag.appendChild(c1);
Polymer.dom(d).appendChild(frag);
assert.equal(Polymer.dom(d).childNodes.length, 2);
assert.equal(Polymer.dom(d).firstChild, c);
assert.equal(Polymer.dom(d).lastChild, c1);
});

test('appendChild interacts with unmanaged parent tree', function() {
var container = document.querySelector('#container');
var echo = Polymer.dom(container).firstElementChild;
Expand Down

0 comments on commit a455c23

Please sign in to comment.