From 306cc8130e4cab12d6e05eec6097e1ad85220c50 Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Wed, 18 Nov 2015 17:00:15 -0800 Subject: [PATCH] Use linked-list for element tree traversal. Factor Polymer.DomApi into shadow/shady modules. --- src/lib/dom-api-classlist.html | 16 +- src/lib/dom-api-shadow.html | 134 +++++ src/lib/dom-api-shady.html | 690 +++++++++++++++++++++++ src/lib/dom-api.html | 872 +---------------------------- src/lib/dom-tree-api.html | 158 ++++++ src/lib/style-util.html | 2 +- src/lib/template/dom-bind.html | 2 +- src/mini/shady.html | 97 +--- test/smoke/nextSibling.html | 42 ++ test/unit/polymer-dom-content.html | 8 +- test/unit/polymer-dom.js | 21 +- test/unit/shady.html | 48 +- 12 files changed, 1127 insertions(+), 963 deletions(-) create mode 100644 src/lib/dom-api-shadow.html create mode 100644 src/lib/dom-api-shady.html create mode 100644 src/lib/dom-tree-api.html create mode 100644 test/smoke/nextSibling.html diff --git a/src/lib/dom-api-classlist.html b/src/lib/dom-api-classlist.html index d4ce7b7f38..80d4720814 100644 --- a/src/lib/dom-api-classlist.html +++ b/src/lib/dom-api-classlist.html @@ -14,6 +14,8 @@ var DomApi = Polymer.DomApi.ctor; + var useShadow = Polymer.Settings.useShadow; + /** * DomApi.classList allows maniuplation of `classList` compatible with * Polymer.dom. The general usage is @@ -36,20 +38,28 @@ } DomApi.ClassList.prototype = { + add: function() { this.node.classList.add.apply(this.node.classList, arguments); - this.domApi._distributeParent(); + this._distributeParent(); }, remove: function() { this.node.classList.remove.apply(this.node.classList, arguments); - this.domApi._distributeParent(); + this._distributeParent(); }, toggle: function() { this.node.classList.toggle.apply(this.node.classList, arguments); - this.domApi._distributeParent(); + this._distributeParent(); + }, + + _distributeParent: function() { + if (!useShadow) { + this.domApi._distributeParent(); + } }, + contains: function() { return this.node.classList.contains.apply(this.node.classList, arguments); diff --git a/src/lib/dom-api-shadow.html b/src/lib/dom-api-shadow.html new file mode 100644 index 0000000000..bcd76df482 --- /dev/null +++ b/src/lib/dom-api-shadow.html @@ -0,0 +1,134 @@ + + \ No newline at end of file diff --git a/src/lib/dom-api-shady.html b/src/lib/dom-api-shady.html new file mode 100644 index 0000000000..2e76eb5889 --- /dev/null +++ b/src/lib/dom-api-shady.html @@ -0,0 +1,690 @@ + + + + \ No newline at end of file diff --git a/src/lib/dom-api.html b/src/lib/dom-api.html index d85d9ad259..4a1bab92f2 100644 --- a/src/lib/dom-api.html +++ b/src/lib/dom-api.html @@ -21,24 +21,14 @@ 'use strict'; var Settings = Polymer.Settings; - var getInnerHTML = Polymer.domInnerHTML.getInnerHTML; - 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 DomApi = function(node) { + this.node = needsToWrap ? DomApi.wrap(node) : node; + }; // ensure nodes are wrapped if SD polyfill is present var needsToWrap = Settings.hasShadow && !Settings.nativeShadow; - var wrap = window.wrap ? window.wrap : function(node) { return node; }; - - var DomApi = function(node) { - this.node = needsToWrap ? wrap(node) : node; - if (this.patch) { - this.patch(); - } - }; + DomApi.wrap = window.wrap ? window.wrap : function(node) { return node; }; DomApi.prototype = { @@ -68,400 +58,6 @@ return n === this.node; }, - _lazyDistribute: function(host) { - // note: only try to distribute if the root is not clean; this ensures - // we don't distribute before initial distribution - if (host.shadyRoot && host.shadyRoot._distributionClean) { - host.shadyRoot._distributionClean = false; - Polymer.dom.addDebouncer(host.debounce('_distribute', - host._distributeContent)); - } - }, - - 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 (host of container needs distribution) - _addNode: function(node, ref_node) { - this._removeNodeFromParent(node); - var addedInsertionPoint; - var root = this.getOwnerRoot(); - // if a is added, make sure it's parent has logical info. - 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); - // if not distributing and not adding to host, do a fast path addition - if (!this._maybeDistribute(node, this.node) && - !this._tryRemoveUndistributedNode(node)) { - if (ref_node) { - // if ref_node is 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); - if (ref_node) { - nativeInsertBefore.call(container, node, ref_node); - } else { - nativeAppendChild.call(container, node); - } - } - if (addedInsertionPoint) { - this._updateInsertionPoints(root.host); - } - this.notifyObserver(); - return node; - }, - - /** - Removes the given `node` from the element's `lightChildren`. - This method also performs dom composition. - */ - removeChild: function(node) { - if (factory(node).parentNode !== this.node) { - 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 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. - // undistributed nodes. - if (container === node.parentNode) { - removeFromComposedParent(container, node); - nativeRemoveChild.call(container, node); - } - } - this.notifyObserver(); - return node; - }, - - replaceChild: function(node, ref_node) { - this.insertBefore(node, ref_node); - this.removeChild(ref_node); - return node; - }, - - _hasCachedOwnerRoot: function(node) { - return Boolean(node._ownerShadyRoot !== undefined); - }, - - getOwnerRoot: function() { - return this._ownerShadyRootForNode(this.node); - }, - - _ownerShadyRootForNode: function(node) { - if (!node) { - return; - } - if (node._ownerShadyRoot === undefined) { - var root; - if (node._isShadyRoot) { - root = node; - } else { - var parent = Polymer.dom(node).parentNode; - if (parent) { - root = parent._isShadyRoot ? parent : - this._ownerShadyRootForNode(parent); - } else { - root = null; - } - } - node._ownerShadyRoot = root; - } - return node._ownerShadyRoot; - }, - - _maybeDistribute: function(node, parent) { - // TODO(sorvell): technically we should check non-fragment nodes for - // children but since this case is assumed to be exceedingly - // rare, we avoid the cost and will address with some specific api - // when the need arises. For now, the user must call - // 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); - var wrappedContent = fragContent && - (Polymer.dom(fragContent).parentNode.nodeType !== - Node.DOCUMENT_FRAGMENT_NODE); - var hasContent = fragContent || (node.localName === CONTENT); - // There are 2 possible cases where a distribution may need to occur: - // 1. being inserted (the host of the shady root where - // content is inserted needs distribution) - // 2. children being inserted into parent with a shady root (parent - // needs distribution) - if (hasContent) { - var root = this._ownerShadyRootForNode(parent); - if (root) { - var host = root.host; - // note, insertion point list update is handled after node - // mutations are complete - this._lazyDistribute(host); - } - } - var parentNeedsDist = this._parentNeedsDistribution(parent); - if (parentNeedsDist) { - this._lazyDistribute(parent); - } - // 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); - }, - - /* note: parent argument is required since node may have an out - of date parent at this point; returns true if a 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'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 has logical children - _nodeHasLogicalChildren: function(node) { - return Boolean(node._lightChildren !== undefined); - }, - - _parentNeedsDistribution: function(parent) { - return parent && parent.shadyRoot && hasInsertionPoint(parent.shadyRoot); - }, - - _removeNodeFromParent: function(node) { - // note: we may need to notify and not have logical info so fallback - // to composed parentNode. - var parent = node._lightParent || node.parentNode; - if (parent && hasDomApi(parent)) { - factory(parent).notifyObserver(); - } - 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; - 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); - } - this._removeLogicalInfo(node, parent); - } - this._removeOwnerShadyRoot(node); - if (root && hostNeedsDist) { - this._updateInsertionPoints(root.host); - this._lazyDistribute(root.host); - } else if (ensureComposedRemoval) { - removeFromComposedParent(getComposedParent(node), node); - } - }, - - _removeDistributedChildren: function(root, container) { - var hostNeedsDist; - var ip$ = root._insertionPoints; - for (var i=0; i= 0 ? i : children.length, 0, node); - } - - function removeFromComposedParent(parent, node) { - node._composedParent = null; - if (parent) { - var children = getComposedChildren(parent); - var i = children.indexOf(node); - if (i >= 0) { - children.splice(i, 1); - } - } - } - - function saveLightChildrenIfNeeded(node) { - // 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 - // source of truth for the light children of the element. This element's - // actual children will be treated as the rendered state once lightChildren - // is populated. - if (!node._lightChildren) { - var c$ = arrayCopyChildNodes(node); - for (var i=0, l=c$.length, child; (i \ No newline at end of file diff --git a/src/lib/dom-tree-api.html b/src/lib/dom-tree-api.html new file mode 100644 index 0000000000..4e1812c240 --- /dev/null +++ b/src/lib/dom-tree-api.html @@ -0,0 +1,158 @@ + + + + \ No newline at end of file diff --git a/src/lib/style-util.html b/src/lib/style-util.html index afc0c5ab1d..9473b3eeaa 100644 --- a/src/lib/style-util.html +++ b/src/lib/style-util.html @@ -112,7 +112,7 @@ var cssText = ''; // if element is a template, get content from its .content var content = element.content || element; - var e$ = Polymer.DomApi.arrayCopy( + var e$ = Polymer.TreeApi.arrayCopy( content.querySelectorAll(this.MODULE_STYLES_SELECTOR)); for (var i=0, e; i < e$.length; i++) { e = e$[i]; diff --git a/src/lib/template/dom-bind.html b/src/lib/template/dom-bind.html index 8b0aeb81d1..025e6b11fb 100644 --- a/src/lib/template/dom-bind.html +++ b/src/lib/template/dom-bind.html @@ -154,7 +154,7 @@ this._prepBindings(); this._prepPropertyInfo(); Polymer.Base._initFeatures.call(this); - this._children = Polymer.DomApi.arrayCopyChildNodes(this.root); + this._children = Polymer.TreeApi.arrayCopyChildNodes(this.root); } this._insertChildren(); this.fire('dom-change'); diff --git a/src/mini/shady.html b/src/mini/shady.html index 4734678e15..b38d8ad9fb 100644 --- a/src/mini/shady.html +++ b/src/mini/shady.html @@ -8,7 +8,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt --> + + + @@ -21,7 +24,8 @@ Implements a pared down version of ShadowDOM's scoping, which is easy to polyfill across browsers. */ - var hasDomApi = Polymer.DomApi.hasDomApi; + var DomApi = Polymer.DomApi; + var TreeApi = Polymer.TreeApi; Polymer.Base._addFeature({ @@ -34,7 +38,7 @@ _poolContent: function() { if (this._useContent) { // capture lightChildren to help reify dom scoping - saveLightChildrenIfNeeded(this); + TreeApi.Logical.saveChildNodes(this); } }, @@ -47,7 +51,7 @@ // removed from document by shadyDOM distribution // so we ensure this here if (!this.dataHost) { - upgradeLightChildren(this._lightChildren); + upgradeLogicalChildren(TreeApi.Logical.getChildNodes(this)); } } }, @@ -66,11 +70,11 @@ // a. for shadyRoot // b. for insertion points (fallback) // c. for parents of insertion points - saveLightChildrenIfNeeded(this.shadyRoot); + TreeApi.Logical.saveChildNodes(this.shadyRoot); for (var i=0, c; i < i$.length; i++) { c = i$[i]; - saveLightChildrenIfNeeded(c); - saveLightChildrenIfNeeded(c.parentNode); + TreeApi.Logical.saveChildNodes(c); + TreeApi.Logical.saveChildNodes(c.parentNode); } this.shadyRoot.host = this; }, @@ -122,7 +126,7 @@ }, _beginDistribute: function() { - if (this._useContent && hasInsertionPoint(this.shadyRoot)) { + if (this._useContent && DomApi.hasInsertionPoint(this.shadyRoot)) { // reset distributions this._resetDistribution(); // compute which nodes should be distributed where @@ -147,18 +151,14 @@ // so that attachment that provokes additional distribution (e.g. // adding something to your parentNode) works this.shadyRoot._distributionClean = true; - if (hasInsertionPoint(this.shadyRoot)) { + if (DomApi.hasInsertionPoint(this.shadyRoot)) { this._composeTree(); // NOTE: send a signal to insertion points that we have distributed // which informs effective children observers notifyContentObservers(this.shadyRoot); } else { if (!this.shadyRoot._hasDistributed) { - this.textContent = ''; - // reset composed children here in case they may have already - // been set (this shouldn't happen but can if dependency ordering - // is incorrect and as a result upgrade order is unexpected) - this._composedChildren = null; + TreeApi.Composed.clearChildNodes(this); this.appendChild(this.shadyRoot); } else { // simplified non-tree walk composition @@ -188,7 +188,7 @@ // Alternatively we could just polyfill it somewhere. // Note that the arguments are reversed from what you might expect. node = node || this; - return matchesSelector.call(node, selector); + return DomApi.matchesSelector.call(node, selector); }, // Many of the following methods are all conceptually static, but they are @@ -196,7 +196,7 @@ _resetDistribution: function() { // light children - var children = getLightChildren(this); + var children = TreeApi.Logical.getChildNodes(this); for (var i = 0; i < children.length; i++) { var child = children[i]; if (child._destinationInsertionPoints) { @@ -218,7 +218,7 @@ // these with the "content root" to arrive at the composed tree. _collectPool: function() { var pool = []; - var children = getLightChildren(this); + var children = TreeApi.Logical.getChildNodes(this); for (var i = 0; i < children.length; i++) { var child = children[i]; if (isInsertionPoint(child)) { @@ -264,7 +264,7 @@ } // Fallback content if nothing was distributed here if (!anyDistributed) { - var children = getLightChildren(content); + var children = TreeApi.Logical.getChildNodes(content); for (var j = 0; j < children.length; j++) { distributeNodeInto(children[j], content); } @@ -277,7 +277,7 @@ this._updateChildNodes(this, this._composeNode(this)); var p$ = this.shadyRoot._insertionPoints; for (var i=0, l=p$.length, p, parent; (i + + + + annotations + + + + + + + + + + + + + + + diff --git a/test/unit/polymer-dom-content.html b/test/unit/polymer-dom-content.html index 202d636835..0ab9a3efd2 100644 --- a/test/unit/polymer-dom-content.html +++ b/test/unit/polymer-dom-content.html @@ -948,7 +948,7 @@ assert.equal(host.$.distWrapper.children[1], s2); assert.equal(host.$.distWrapper.children[2], s3); assert.equal(host.$.distWrapper.children[3], s0); - var composedChildren = host.$.distWrapper._composedChildren; + var composedChildren = Polymer.TreeApi.Composed.getChildNodes(host.$.distWrapper); assert.equal(composedChildren.length, 4); assert.equal(composedChildren[0], s1); assert.equal(composedChildren[1], s2); @@ -961,7 +961,7 @@ Polymer.dom.flush(); if (host.shadyRoot) { assert.equal(host.$.distWrapper.children.length, 1); - var composedChildren = host.$.distWrapper._composedChildren; + var composedChildren = Polymer.TreeApi.Composed.getChildNodes(host.$.distWrapper); assert.equal(composedChildren.length, 1); assert.equal(composedChildren[0], s0); } @@ -987,7 +987,7 @@ assert.equal(host.$.distWrapper.children[1], s2); assert.equal(host.$.distWrapper.children[2], s3); assert.equal(host.$.distWrapper.children[3], s0); - var composedChildren = host.$.distWrapper._composedChildren; + var composedChildren = Polymer.TreeApi.Composed.getChildNodes(host.$.distWrapper); assert.equal(composedChildren.length, 4); assert.equal(composedChildren[0], s1); assert.equal(composedChildren[1], s2); @@ -1000,7 +1000,7 @@ Polymer.dom.flush(); if (host.shadyRoot) { assert.equal(host.$.distWrapper.children.length, 1); - var composedChildren = host.$.distWrapper._composedChildren; + var composedChildren = Polymer.TreeApi.Composed.getChildNodes(host.$.distWrapper); assert.equal(composedChildren.length, 1); assert.equal(composedChildren[0], s0); } diff --git a/test/unit/polymer-dom.js b/test/unit/polymer-dom.js index de6380db14..60bd2bd5cc 100644 --- a/test/unit/polymer-dom.js +++ b/test/unit/polymer-dom.js @@ -489,19 +489,19 @@ suite('Polymer.dom', function() { assert.equal(Polymer.dom(rere).querySelector('#light'), s); assert.equal(Polymer.dom(s).parentNode, rere); if (rere.shadyRoot) { - assert.notEqual(s._composedParent, rere); + assert.notEqual(Polymer.TreeApi.Composed.getParentNode(s), rere); } Polymer.dom(testElement).flush(); if (rere.shadyRoot) { - assert.equal(s._composedParent, p); + assert.equal(Polymer.TreeApi.Composed.getParentNode(s), p); } Polymer.dom(rere).removeChild(s); if (rere.shadyRoot) { - assert.equal(s._composedParent, p); + assert.equal(Polymer.TreeApi.Composed.getParentNode(s), p); } Polymer.dom(testElement).flush(); if (rere.shadyRoot) { - assert.equal(s._composedParent, null); + assert.equal(Polymer.TreeApi.Composed.getParentNode(s), null); } }); @@ -713,7 +713,7 @@ suite('Polymer.dom accessors', function() { assert.equal(Polymer.dom(testElement).textContent, 'Hello World', 'textContent getter incorrect'); if (testElement.shadyRoot) { Polymer.dom.flush(); - assert.equal(testElement._composedChildren[1].textContent, 'Hello World', 'text content setter incorrect'); + assert.equal(Polymer.TreeApi.Composed.getChildNodes(testElement)[1].textContent, 'Hello World', 'text content setter incorrect'); } testElement = document.createElement('x-commented'); assert.equal(Polymer.dom(testElement.root).textContent, '[]', 'text content getter with comment incorrect'); @@ -737,9 +737,10 @@ suite('Polymer.dom accessors', function() { assert.equal(Polymer.dom(testElement).innerHTML , '
Hello World
2
3
', 'innerHTML getter incorrect'); if (testElement.shadyRoot) { Polymer.dom.flush(); - assert.equal(testElement._composedChildren[1], added, 'innerHTML setter composed incorrectly'); - assert.equal(testElement._composedChildren[2].textContent, '2', 'innerHTML setter composed incorrectly'); - assert.equal(testElement._composedChildren[3].textContent, '3', 'innerHTML setter composed incorrectly'); + var children = Polymer.TreeApi.Composed.getChildNodes(testElement); + assert.equal(children[1], added, 'innerHTML setter composed incorrectly'); + assert.equal(children[2].textContent, '2', 'innerHTML setter composed incorrectly'); + assert.equal(children[3].textContent, '3', 'innerHTML setter composed incorrectly'); } }); @@ -804,13 +805,13 @@ suite('Polymer.dom non-distributed elements', function() { function testNoAttr() { assert.equal(Polymer.dom(child).getDestinationInsertionPoints()[0], d.$.notTestContent, 'child not distributed logically'); if (shady) { - assert.equal(child._composedParent, d.$.notTestContainer, 'child not rendered in composed dom'); + assert.equal(Polymer.TreeApi.Composed.getParentNode(child), d.$.notTestContainer, 'child not rendered in composed dom'); } } function testWithAttr() { assert.equal(Polymer.dom(child).getDestinationInsertionPoints()[0], d.$.testContent, 'child not distributed logically'); if (shady) { - assert.equal(child._composedParent, d.$.testContainer, 'child not rendered in composed dom'); + assert.equal(Polymer.TreeApi.Composed.getParentNode(child), d.$.testContainer, 'child not rendered in composed dom'); } } // test with x-distribute diff --git a/test/unit/shady.html b/test/unit/shady.html index 7bd9f45118..a7ed04a793 100644 --- a/test/unit/shady.html +++ b/test/unit/shady.html @@ -135,14 +135,14 @@ assert.strictEqual(getComposedHTML(host), 'a: b: '); - assertArrayEqual(host._lightChildren, [a]); - assert.strictEqual(a._lightParent, host); - assertArrayEqual(host.shadyRoot._lightChildren, [p]); - assert.strictEqual(p._lightParent, host.shadyRoot); - assertArrayEqual(p._lightChildren, [b, content]); - assert.strictEqual(b._lightParent, p); - assert.strictEqual(content._lightParent, p); - assertArrayEqual(p.shadyRoot._lightChildren, + assertArrayEqual(host.__childNodes, [a]); + assert.strictEqual(a.__parentNode, host); + assertArrayEqual(host.shadyRoot.__childNodes, [p]); + assert.strictEqual(p.__parentNode, host.shadyRoot); + assertArrayEqual(p.__childNodes, [b, content]); + assert.strictEqual(b.__parentNode, p); + assert.strictEqual(content.__parentNode, p); + assertArrayEqual(p.shadyRoot.__childNodes, [textNodeA, contentA, textNodeB, contentB]); } @@ -165,7 +165,7 @@ distributeContentNow(host); assert.strictEqual(getComposedHTML(host), ''); - host._lightChildren = []; + host.__childNodes = []; distributeContentNow(host); assert.strictEqual(getComposedHTML(host), 'fallback'); }); @@ -180,11 +180,11 @@ distributeContentNow(host); assert.strictEqual(getComposedHTML(host), 'Helloafter'); - host.shadyRoot._lightChildren[1].textContent = ''; + host.shadyRoot.__childNodes[1].textContent = ''; distributeContentNow(host); assert.strictEqual(getComposedHTML(host), 'Hello'); - host.shadyRoot._lightChildren = []; + host.shadyRoot.__childNodes = []; distributeContentNow(host); assert.strictEqual(getComposedHTML(host), ''); }); @@ -203,11 +203,11 @@ distributeContentNow(host); assert.strictEqual(getComposedHTML(host), ''); - host.shadyRoot.firstChild._lightChildren = []; + host.shadyRoot.firstChild.__childNodes = []; distributeContentNow(host); assert.strictEqual(getComposedHTML(host), ''); - host.shadyRoot._lightChildren = []; + host.shadyRoot.__childNodes = []; distributeContentNow(host); assert.strictEqual(getComposedHTML(host), ''); }); @@ -225,7 +225,7 @@ distributeContentNow(host); assert.strictEqual(getComposedHTML(host), ''); - host._lightChildren = []; + host.__childNodes = []; distributeContentNow(host); assert.strictEqual(getComposedHTML(host), 'fallback'); }); @@ -265,11 +265,11 @@ distributeContentNow(host); assert.strictEqual(getComposedHTML(host), 'Hello'); - host.shadyRoot._lightChildren.splice(1, 1); // remove b + host.shadyRoot.__childNodes.splice(1, 1); // remove b distributeContentNow(host); assert.strictEqual(getComposedHTML(host), 'Hello'); - host.shadyRoot._lightChildren = []; // remove a + host.shadyRoot.__childNodes = []; // remove a distributeContentNow(host); assert.strictEqual(getComposedHTML(host), ''); }); @@ -368,7 +368,7 @@ assert.strictEqual(getComposedHTML(host), 'Hello'); var b = document.createElement('b'); - host._lightChildren[0] = b; + host.__childNodes[0] = b; distributeContentNow(host); assert.strictEqual(getComposedHTML(host), ''); }); @@ -437,12 +437,16 @@ }); function syncLightDOM(n) { - if (n._lightChildren) { + if (n.__childNodes) { var c$ = n.__patched ? n._composedChildren || [] : Array.prototype.slice.call(n.childNodes); - c$.forEach(function(c) { - if (n._lightChildren.indexOf(c) < 0) { - c._lightParent = n; - n._lightChildren.push(c); + n.__firstChild = c$[0]; + n.__lastChild = c$[c$.length-1]; + c$.forEach(function(c, i) { + if (n.__childNodes.indexOf(c) < 0) { + c.__parentNode = n; + c.__previousSibling = c$[i-1]; + c.__nextSibling = c$[i+1]; + n.__childNodes.push(c); } }); }