From 21500fb53f4dc186ffd1099d5492c711e097c8a4 Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Mon, 10 Aug 2015 16:40:58 -0700 Subject: [PATCH 1/2] Fixes #2235. Manages logical information in shady distribution more directly by capturing it explicitly when needed and not whenever distribution is run. --- src/lib/dom-api.html | 8 +++++++- src/mini/shady.html | 18 ++++++++++-------- test/unit/polymer-dom-shadow.html | 6 ++++++ test/unit/polymer-dom.html | 6 ++++++ test/unit/polymer-dom.js | 28 ++++++++++++++++++++++++++++ test/unit/shady.html | 2 +- 6 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/lib/dom-api.html b/src/lib/dom-api.html index b18e69607c..5282130508 100644 --- a/src/lib/dom-api.html +++ b/src/lib/dom-api.html @@ -223,8 +223,14 @@ }, _updateInsertionPoints: function(host) { - host.shadyRoot._insertionPoints = + var i$ = host.shadyRoot._insertionPoints = factory(host.shadyRoot).querySelectorAll(CONTENT); + // ensure 's and their parents have logical dom info. + for (var i=0, c; i < i$.length; i++) { + c = i$[i]; + saveLightChildrenIfNeeded(c); + saveLightChildrenIfNeeded(factory(c).parentNode); + } }, // a node is in a shadyRoot, is a shadyRoot, diff --git a/src/mini/shady.html b/src/mini/shady.html index 8d96fc7b57..cfa5f938cc 100644 --- a/src/mini/shady.html +++ b/src/mini/shady.html @@ -53,11 +53,19 @@ this.shadyRoot._isShadyRoot = true; this.shadyRoot._dirtyRoots = []; // capture insertion point list - this.shadyRoot._insertionPoints = !this._notes || + var i$ = this.shadyRoot._insertionPoints = !this._notes || this._notes._hasContent ? this.shadyRoot.querySelectorAll('content') : []; - // save logical tree info for shadyRoot. + // save logical tree info + // a. for shadyRoot + // b. for insertion points (fallback) + // c. for parents of insertion points saveLightChildrenIfNeeded(this.shadyRoot); + for (var i=0, c; i < i$.length; i++) { + c = i$[i]; + saveLightChildrenIfNeeded(c); + saveLightChildrenIfNeeded(c.parentNode); + } this.shadyRoot.host = this; }, @@ -406,9 +414,6 @@ } // remove child from its old parent first remove(newChild); - // make sure we never lose logical DOM information: - // if the parentNode doesn't have lightChildren, save that information now. - saveLightChildrenIfNeeded(parentNode); // insert it into the real DOM nativeInsertBefore.call(parentNode, newChild, refChild || null); newChild._composedParent = parentNode; @@ -417,9 +422,6 @@ function remove(node) { var parentNode = getComposedParent(node); if (parentNode) { - // make sure we never lose logical DOM information: - // if the parentNode doesn't have lightChildren, save that information now. - saveLightChildrenIfNeeded(parentNode); node._composedParent = null; // remove it from the real DOM nativeRemoveChild.call(parentNode, node); diff --git a/test/unit/polymer-dom-shadow.html b/test/unit/polymer-dom-shadow.html index 11f78de268..8e704b3ac0 100644 --- a/test/unit/polymer-dom-shadow.html +++ b/test/unit/polymer-dom-shadow.html @@ -47,6 +47,12 @@ +
+ + 1 + 2 +
+ diff --git a/test/unit/polymer-dom.html b/test/unit/polymer-dom.html index 6b8240d4ff..16671e5a7d 100644 --- a/test/unit/polymer-dom.html +++ b/test/unit/polymer-dom.html @@ -47,6 +47,12 @@ +
+ + 1 + 2 +
+ diff --git a/test/unit/polymer-dom.js b/test/unit/polymer-dom.js index 5ba436b50d..9d26612fa9 100644 --- a/test/unit/polymer-dom.js +++ b/test/unit/polymer-dom.js @@ -424,6 +424,34 @@ suite('Polymer.dom', function() { assert.equal(Polymer.dom(rere.root).querySelectorAll('span').length, 0); }); + test('appendChild interacts with unmanaged parent tree', function() { + var container = document.querySelector('#container'); + var echo = Polymer.dom(container).firstElementChild; + assert.equal(echo.localName, 'x-echo'); + var s1 = Polymer.dom(echo).nextElementSibling; + assert.equal(s1.textContent, '1'); + var s2 = Polymer.dom(s1).nextElementSibling; + assert.equal(s2.textContent, '2'); + assert.equal(Polymer.dom(container).children.length, 3); + Polymer.dom(echo).appendChild(s1); + Polymer.dom.flush(); + assert.equal(Polymer.dom(container).children.length, 2); + assert.equal(Polymer.dom(echo).nextElementSibling, s2); + Polymer.dom(echo).appendChild(s2); + Polymer.dom.flush(); + assert.equal(Polymer.dom(container).children.length, 1); + assert.equal(Polymer.dom(echo).nextElementSibling, null); + Polymer.dom(container).appendChild(s1); + Polymer.dom.flush(); + assert.equal(Polymer.dom(container).children.length, 2); + assert.equal(Polymer.dom(echo).nextElementSibling, s1); + Polymer.dom(container).appendChild(s2); + Polymer.dom.flush(); + assert.equal(Polymer.dom(container).children.length, 3); + assert.equal(Polymer.dom(echo).nextElementSibling, s1); + assert.equal(Polymer.dom(s1).nextElementSibling, s2); + }); + test('distribute (forced)', function() { var rere = Polymer.dom(testElement.root).querySelector('x-rereproject'); var re = Polymer.dom(rere.root).querySelector('x-reproject'); diff --git a/test/unit/shady.html b/test/unit/shady.html index 9b3538f1f4..7bd9f45118 100644 --- a/test/unit/shady.html +++ b/test/unit/shady.html @@ -473,7 +473,7 @@ } function updateRootInsertionPoints(root) { - root._insertionPoints = root.querySelectorAll('content'); + Polymer.dom(root.host)._updateInsertionPoints(root.host); } function getComposedHTML(node) { From 45cb1506a610a4f4421f84ac9e0d690902537335 Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Mon, 10 Aug 2015 18:34:22 -0700 Subject: [PATCH 2/2] Add logical info iff an element being added is an insertion point; do not add logical info for any element in a shady root. --- src/lib/dom-api.html | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/lib/dom-api.html b/src/lib/dom-api.html index 5282130508..929c1ac912 100644 --- a/src/lib/dom-api.html +++ b/src/lib/dom-api.html @@ -65,11 +65,15 @@ // 3. node is (host of container needs distribution) appendChild: function(node) { var handled; + // if a is added, make sure it's parent has logical info. + this._ensureContentLogicalInfo(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)) { @@ -86,9 +90,10 @@ return this.appendChild(node); } var handled; + // if a is added, make sure it's parent has logical info. + this._ensureContentLogicalInfo(node); this._removeNodeFromHost(node, true); if (this._nodeIsInLogicalTree(this.node)) { - saveLightChildrenIfNeeded(this.node); var children = this.childNodes; var index = children.indexOf(ref_node); if (index < 0) { @@ -98,6 +103,8 @@ this._addLogicalInfo(node, this.node, index); 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)) { @@ -125,6 +132,8 @@ if (this._nodeIsInLogicalTree(this.node)) { this._removeNodeFromHost(node); handled = this._maybeDistribute(node, this.node); + } else { + this._removeNodeFromHost(node); } if (!handled) { // if removing from a shadyRoot, remove form host instead @@ -237,10 +246,23 @@ // or has a lightParent _nodeIsInLogicalTree: function(node) { return Boolean((node._lightParent !== undefined) || node._isShadyRoot || - this._ownerShadyRootForNode(node) || 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