Skip to content

Commit

Permalink
Merge pull request #2051 from Polymer/fix-1888
Browse files Browse the repository at this point in the history
Fixes #1888
  • Loading branch information
kevinpschaaf committed Jul 8, 2015
2 parents 578b8ae + 2968a2d commit c263ad5
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 14 deletions.
47 changes: 44 additions & 3 deletions src/lib/dom-api.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
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;

var dirtyRoots = [];

Expand Down Expand Up @@ -205,8 +207,8 @@

_tryRemoveUndistributedNode: function(node) {
if (this.node.shadyRoot) {
if (node.parentNode) {
nativeRemoveChild.call(node.parentNode, node);
if (node._composedParent) {
nativeRemoveChild.call(node._composedParent, node);
}
return true;
}
Expand All @@ -220,7 +222,7 @@
// a node is in a shadyRoot, is a shadyRoot,
// or has a lightParent
_nodeIsInLogicalTree: function(node) {
return Boolean(node._lightParent || node._isShadyRoot ||
return Boolean((node._lightParent !== undefined) || node._isShadyRoot ||
this._ownerShadyRootForNode(node) ||
node.shadyRoot);
},
Expand Down Expand Up @@ -440,6 +442,35 @@
if (this._parentNeedsDistribution(this.parentNode)) {
this._lazyDistribute(this.parentNode);
}
},

cloneNode: function(deep) {
var n = nativeCloneNode.call(this.node, false);
if (deep) {
var c$ = this.childNodes;
var d = factory(n);
for (var i=0, nc; i < c$.length; i++) {
nc = factory(c$[i]).cloneNode(true);
d.appendChild(nc);
}
}
return n;
},

importNode: function(externalNode, deep) {
// for convenience use this node's ownerDoc if the node isn't a document
var doc = this.node instanceof HTMLDocument ? this.node :
this.node.ownerDocument;
var n = nativeImportNode.call(doc, externalNode, false);
if (deep) {
var c$ = factory(externalNode).childNodes;
var d = factory(n);
for (var i=0, nc; i < c$.length; i++) {
nc = factory(doc).importNode(c$[i], true);
d.appendChild(nc);
}
}
return n;
}

};
Expand Down Expand Up @@ -646,6 +677,16 @@
}
};

DomApi.prototype.cloneNode = function(deep) {
return this.node.cloneNode(deep);
}

DomApi.prototype.importNode = function(externalNode, deep) {
var doc = this.node instanceof HTMLDocument ? this.node :
this.node.ownerDocument;
return doc.importNode(externalNode, deep);
}

DomApi.prototype.getDestinationInsertionPoints = function() {
var n$ = this.node.getDestinationInsertionPoints();
return n$ ? Array.prototype.slice.call(n$) : [];
Expand Down
46 changes: 36 additions & 10 deletions src/lib/experimental/patch-dom.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@

var baseFinishDistribute = Polymer.Base._finishDistribute;

// NOTE: any manipulation of a node must occur in a patched parent
// so that the parent can cleanup the node's composed and logical
// information. We patch an element's parent's to make this rule more
// likely to be satisfied.
// Also note that any use of qS/qSA must be done in a patched node.
Polymer.Base._finishDistribute = function() {
baseFinishDistribute.call(this);
if (!this.__patched) {
Expand All @@ -32,6 +37,13 @@
Array.prototype.forEach.call(this.childNodes, function(c) {
Polymer.dom(c);
});
// TODO(sorvell): ensure top element's parents are wrapped (helped A2
// since it uses qSA on the fragment containing stamped custom elements)
// note that getOwnerRoot will patch all parents but there should be an
// explicit easier way to do this.
if (!this.dataHost) {
Polymer.dom(this).getOwnerRoot();
}
}
};

Expand All @@ -41,16 +53,17 @@

var nativeShadow = Polymer.Settings.useShadow;

var excluded = [document, document.head, document.body];
var excluded = ['head'];

Polymer.telemetry.patched = 0;

// experimental: support patching selected native api.
Polymer.DomApi.ctor.prototype.patch = function() {
Polymer.DomApi.ctor.prototype.patch = function(force) {
if (nativeShadow || this.node.__patched ||
(excluded.indexOf(this.node) >= 0)) {
(this.node.localName && excluded.indexOf(this.node.localName) >= 0)) {
return;
}

getComposedChildren(this.node);
saveLightChildrenIfNeeded(this.node);
if (!this.node._lightParent) {
Expand All @@ -59,12 +72,11 @@
if (!this.node._composedParent) {
this.node._composedParent = this.node.parentNode;
}
// TODO(sorvell): correctly patch text nodes.
if (this.node.nodeType === Node.TEXT_NODE) {
return;
// TODO(sorvell): correctly patch non-element nodes.
if (this.node.nodeType !== Node.TEXT_NODE) {
this.node.__patched = true;
patchImpl.patch(this.node);
}
patchImpl.patch(this.node);
this.node.__patched = true;
};

Polymer.DomApi.ctor.prototype.unpatch = function() {
Expand All @@ -88,7 +100,8 @@
Node.prototype, 'textContent')),

methods: ['appendChild', 'insertBefore', 'removeChild', 'replaceChild',
'querySelector', 'querySelectorAll', 'getDestinationInsertionPoints'],
'querySelector', 'querySelectorAll', 'getDestinationInsertionPoints',
'cloneNode', 'importNode'],
// <content>: getDistributedNodes

accessors: ['parentNode', 'childNodes',
Expand All @@ -114,7 +127,7 @@

patchPrototype: function(node) {
var name = node.is || (node.getAttribute && node.getAttribute('is')) ||
node.localName;
node.localName || node.nodeType;
var proto = this.protoCache[name];
if (!proto) {
proto = this.protoCache[name] = this.createPatchedPrototype(node);
Expand Down Expand Up @@ -144,6 +157,7 @@

patchMethod: function(obj, name) {
var orig = obj[name];
obj['_$' + name + '$_'] = orig;
obj[name] = function() {
log && console.log(this, name, arguments);
return factory(this)[name].apply(this.__domApi, arguments);
Expand Down Expand Up @@ -202,6 +216,18 @@

};

Polymer.DomApi.getLightChildren = function(node) {
var children = node._lightChildren;
return children ? children : Polymer.DomApi.getComposedChildren(node);
}

Polymer.Base.instanceTemplate = function(template) {
var m = document._$importNode$_ || document.importNode;
var dom =
m.call(document, template._content || template.content, true);
return dom;
}

Polymer.DomApi.patchImpl = patchImpl;

})();
Expand Down
5 changes: 4 additions & 1 deletion test/smoke/patch/patch-dom.html
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@

var nd;

before(function() {
suiteSetup(function() {
nd = document.querySelector('x-test-no-distribute');
});

Expand Down Expand Up @@ -396,14 +396,17 @@
// set / unset `test` attr and see if it distributes properly
child.setAttribute('test', '');
d.distributeContent();
Polymer.dom.flush();
testWithAttr();
//
child.removeAttribute('test');
d.distributeContent();
Polymer.dom.flush();
testNoAttr();
//
child.setAttribute('test', '');
d.distributeContent();
Polymer.dom.flush();
testWithAttr();
});

Expand Down
8 changes: 8 additions & 0 deletions test/unit/polymer-dom-elements.html
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,12 @@
</x-select-attr>
</template>
<script>Polymer({is: 'x-compose-select-attr'});</script>
</dom-module>


<dom-module id="x-clonate">
<template><span>[</span><content></content><span>]</span></template>
<script>Polymer({
is: 'x-clonate'
});</script>
</dom-module>
44 changes: 44 additions & 0 deletions test/unit/polymer-dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,50 @@ suite('Polymer.dom', function() {
assert.isTrue(Array.isArray(Polymer.dom(document.body).childNodes));
});

test('Polymer.dom cloneNode shallow', function() {
var a = document.createElement('div');
a.innerHTML = '<x-clonate><span>1</span><span>2</span></x-clonate>';
var b = Polymer.dom(Polymer.dom(a).firstElementChild).cloneNode();
Polymer.dom(document.body).appendChild(b);
assert.equal(Polymer.dom(b).childNodes.length, 0, 'shallow copy has incorrect children');
if (b.shadyRoot) {
assert.equal(b.children.length, 2, 'shallow copy has incorrect composed children');
}
});

test('Polymer.dom cloneNode deep', function() {
var a = document.createElement('div');
a.innerHTML = '<x-clonate><span>1</span><span>2</span></x-clonate>';
var b = Polymer.dom(a).cloneNode(true);
Polymer.dom(document.body).appendChild(b);
assert.equal(Polymer.dom(b.firstElementChild).childNodes.length, 2, 'deep copy has incorrect children');
if (b.shadyRoot) {
assert.equal(b.children.length, 4, 'deep copy has incorrect composed children');
}
});

test('Polymer.dom importNode shallow', function() {
var a = document.createElement('div');
a.innerHTML = '<x-clonate><span>1</span><span>2</span></x-clonate>';
var b = Polymer.dom(document).importNode(Polymer.dom(a).firstElementChild);
Polymer.dom(document.body).appendChild(b);
assert.equal(Polymer.dom(b).childNodes.length, 0, 'shallow import has incorrect children');
if (b.shadyRoot) {
assert.equal(b.children.length, 2, 'shallow import has incorrect composed children');
}
});

test('Polymer.dom importNode deep', function() {
var a = document.createElement('div');
a.innerHTML = '<x-clonate><span>1</span><span>2</span></x-clonate>';
var b = Polymer.dom(document).importNode(a, true);
Polymer.dom(document.body).appendChild(b);
assert.equal(Polymer.dom(b.firstElementChild).childNodes.length, 2, 'deep copy has incorrect children');
if (b.shadyRoot) {
assert.equal(b.children.length, 4, 'deep copy has incorrect composed children');
}
});

});

suite('Polymer.dom accessors', function() {
Expand Down

0 comments on commit c263ad5

Please sign in to comment.