From 18ba9ae0d904ac5dc8c8c2e1b747a9f72d896655 Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Thu, 14 Feb 2019 16:38:56 -0800 Subject: [PATCH 1/2] Add `classList` to `Polymer.dom` when `ShadyDOM.noPatch` is used Fixes #5488 --- lib/legacy/polymer.dom.js | 4 + test/unit/polymer-dom-nopatch.html | 138 +++++++++++++++++++++++++++++ test/unit/polymer-dom.html | 138 +++++++++++++++++++++++++++++ 3 files changed, 280 insertions(+) diff --git a/lib/legacy/polymer.dom.js b/lib/legacy/polymer.dom.js index 442d428147..cf5c1ddc66 100644 --- a/lib/legacy/polymer.dom.js +++ b/lib/legacy/polymer.dom.js @@ -395,6 +395,10 @@ if (window['ShadyDOM'] && window['ShadyDOM']['inUse'] && window['ShadyDOM']['noP } }); + forwardReadOnlyProperties(Wrapper.prototype, [ + 'classList' + ]); + DomApiImpl = Wrapper; Object.defineProperties(EventApi.prototype, { diff --git a/test/unit/polymer-dom-nopatch.html b/test/unit/polymer-dom-nopatch.html index 54017de804..6a7e697110 100644 --- a/test/unit/polymer-dom-nopatch.html +++ b/test/unit/polymer-dom-nopatch.html @@ -355,6 +355,144 @@ assert.equal(useNativeCSSProperties, Boolean(!window.ShadyCSS || window.ShadyCSS.nativeCss)); }); +}); + +suite('forwarded native api', function() { + + let el; + + setup(function() { + el = document.createElement('x-container-slot'); + document.body.appendChild(el); + }); + + teardown(function() { + document.body.removeChild(el); + }); + + test('accessors are available', function() { + const d = dom(el); + assert.isDefined(d.parentNode); + assert.isDefined(d.firstChild); + assert.isDefined(d.lastChild); + assert.isDefined(d.nextSibling); + assert.isDefined(d.previousSibling); + assert.isDefined(d.firstElementChild); + assert.isDefined(d.lastElementChild); + assert.isDefined(d.nextElementSibling); + assert.isDefined(d.previousElementSibling); + assert.isDefined(d.childNodes); + assert.isDefined(d.children); + assert.isDefined(d.classList); + assert.isDefined(d.textContent); + assert.isDefined(d.innerHTML); + }); + + test('cloneNode', function() { + const clone = dom(el).cloneNode(el); + assert.ok(clone); + assert.equal(clone.localName, 'x-container-slot'); + }); + + test('appendChild', function() { + const d1 = document.createElement('div'); + dom(el).appendChild(d1); + assert.equal(dom(el).firstChild, d1); + assert.equal(dom(d1).parentNode, el); + }); + + test('insertBefore', function() { + const d1 = document.createElement('div'); + const d2 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).insertBefore(d2, d1); + assert.equal(dom(d2).nextSibling, d1); + }); + + test('removeChild', function() { + const d1 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).removeChild(d1); + assert.equal(dom(d1).parentNode, null); + }); + + test('replaceChild', function() { + const d1 = document.createElement('div'); + const d2 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).replaceChild(d2, d1); + assert.equal(dom(d1).parentNode, null); + assert.equal(dom(el).firstChild, d2); + assert.equal(dom(d2).parentNode, el); + }); + + test('replaceChild', function() { + dom(el).setAttribute('foo', 'foo'); + assert.equal(el.getAttribute('foo'), 'foo'); + }); + + test('replaceChild', function() { + dom(el).setAttribute('foo', 'foo'); + dom(el).removeAttribute('foo'); + assert.isFalse(el.hasAttribute('foo')); + }); + + test('querySelector', function() { + const d1 = document.createElement('div'); + dom(el).appendChild(d1); + const query = dom(el).querySelector('div'); + assert.equal(query, d1); + }); + + test('querySelectorAll', function() { + const d1 = document.createElement('div'); + const d2 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).appendChild(d2); + const query = dom(el).querySelectorAll('div'); + assert.equal(query[0], d1); + assert.equal(query[1], d2); + assert.equal(query.length, 2); + }); + + test('tree accessors', function() { + const d1 = document.createElement('div'); + const d2 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).appendChild(d2); + const pel = dom(el); + const pd1 = dom(d1); + const pd2 = dom(d2); + assert.equal(pd1.parentNode, el); + assert.equal(pel.firstChild, d1); + assert.equal(pel.lastChild, d2); + assert.equal(pel.firstElementChild, d1); + assert.equal(pel.lastElementChild, d2); + assert.equal(pd1.nextSibling, d2); + assert.equal(pd2.previousSibling, d1); + assert.equal(pd1.nextElementSibling, d2); + assert.equal(pd2.previousElementSibling, d1); + assert.equal(pel.childNodes[0], d1); + assert.equal(pel.childNodes[1], d2); + assert.equal(pel.children[0], d1); + assert.equal(pel.children[1], d2); + }); + + test('innerHTML', function() { + dom(el).innerHTML = '
'; + assert.equal(dom(el).firstChild.localName, 'div'); + }); + + test('textContent', function() { + dom(el).innerHTML = 'hi'; + assert.equal(dom(dom(el).firstChild).textContent, 'hi'); + }); + + test('classList', function() { + dom(el).classList.add('foo'); + assert.equal(el.className, 'foo'); + }); + }); diff --git a/test/unit/polymer-dom.html b/test/unit/polymer-dom.html index 8bea7e1c1f..30cc025654 100644 --- a/test/unit/polymer-dom.html +++ b/test/unit/polymer-dom.html @@ -342,6 +342,144 @@ assert.equal(useNativeCSSProperties, Boolean(!window.ShadyCSS || window.ShadyCSS.nativeCss)); }); +}); + +suite('forwarded native api', function() { + + let el; + + setup(function() { + el = document.createElement('x-container-slot'); + document.body.appendChild(el); + }); + + teardown(function() { + document.body.removeChild(el); + }); + + test('accessors are available', function() { + const d = dom(el); + assert.isDefined(d.parentNode); + assert.isDefined(d.firstChild); + assert.isDefined(d.lastChild); + assert.isDefined(d.nextSibling); + assert.isDefined(d.previousSibling); + assert.isDefined(d.firstElementChild); + assert.isDefined(d.lastElementChild); + assert.isDefined(d.nextElementSibling); + assert.isDefined(d.previousElementSibling); + assert.isDefined(d.childNodes); + assert.isDefined(d.children); + assert.isDefined(d.classList); + assert.isDefined(d.textContent); + assert.isDefined(d.innerHTML); + }); + + test('cloneNode', function() { + const clone = dom(el).cloneNode(el); + assert.ok(clone); + assert.equal(clone.localName, 'x-container-slot'); + }); + + test('appendChild', function() { + const d1 = document.createElement('div'); + dom(el).appendChild(d1); + assert.equal(dom(el).firstChild, d1); + assert.equal(dom(d1).parentNode, el); + }); + + test('insertBefore', function() { + const d1 = document.createElement('div'); + const d2 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).insertBefore(d2, d1); + assert.equal(dom(d2).nextSibling, d1); + }); + + test('removeChild', function() { + const d1 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).removeChild(d1); + assert.equal(dom(d1).parentNode, null); + }); + + test('replaceChild', function() { + const d1 = document.createElement('div'); + const d2 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).replaceChild(d2, d1); + assert.equal(dom(d1).parentNode, null); + assert.equal(dom(el).firstChild, d2); + assert.equal(dom(d2).parentNode, el); + }); + + test('replaceChild', function() { + dom(el).setAttribute('foo', 'foo'); + assert.equal(el.getAttribute('foo'), 'foo'); + }); + + test('replaceChild', function() { + dom(el).setAttribute('foo', 'foo'); + dom(el).removeAttribute('foo'); + assert.isFalse(el.hasAttribute('foo')); + }); + + test('querySelector', function() { + const d1 = document.createElement('div'); + dom(el).appendChild(d1); + const query = dom(el).querySelector('div'); + assert.equal(query, d1); + }); + + test('querySelectorAll', function() { + const d1 = document.createElement('div'); + const d2 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).appendChild(d2); + const query = dom(el).querySelectorAll('div'); + assert.equal(query[0], d1); + assert.equal(query[1], d2); + assert.equal(query.length, 2); + }); + + test('tree accessors', function() { + const d1 = document.createElement('div'); + const d2 = document.createElement('div'); + dom(el).appendChild(d1); + dom(el).appendChild(d2); + const pel = dom(el); + const pd1 = dom(d1); + const pd2 = dom(d2); + assert.equal(pd1.parentNode, el); + assert.equal(pel.firstChild, d1); + assert.equal(pel.lastChild, d2); + assert.equal(pel.firstElementChild, d1); + assert.equal(pel.lastElementChild, d2); + assert.equal(pd1.nextSibling, d2); + assert.equal(pd2.previousSibling, d1); + assert.equal(pd1.nextElementSibling, d2); + assert.equal(pd2.previousElementSibling, d1); + assert.equal(pel.childNodes[0], d1); + assert.equal(pel.childNodes[1], d2); + assert.equal(pel.children[0], d1); + assert.equal(pel.children[1], d2); + }); + + test('innerHTML', function() { + dom(el).innerHTML = '
'; + assert.equal(dom(el).firstChild.localName, 'div'); + }); + + test('textContent', function() { + dom(el).innerHTML = 'hi'; + assert.equal(dom(dom(el).firstChild).textContent, 'hi'); + }); + + test('classList', function() { + dom(el).classList.add('foo'); + assert.equal(el.className, 'foo'); + }); + }); From 764a233c2744ddb8e13ec9dd0fb89d539478cb23 Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Wed, 20 Feb 2019 09:11:36 -0800 Subject: [PATCH 2/2] Added comment based on review feedback. --- lib/legacy/polymer.dom.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/legacy/polymer.dom.js b/lib/legacy/polymer.dom.js index cf5c1ddc66..6d7f0eb667 100644 --- a/lib/legacy/polymer.dom.js +++ b/lib/legacy/polymer.dom.js @@ -395,6 +395,8 @@ if (window['ShadyDOM'] && window['ShadyDOM']['inUse'] && window['ShadyDOM']['noP } }); + // Note, `classList` is here only for legacy compatibility since it does not + // trigger distribution in v1 Shadow DOM. forwardReadOnlyProperties(Wrapper.prototype, [ 'classList' ]); @@ -420,12 +422,17 @@ if (window['ShadyDOM'] && window['ShadyDOM']['inUse'] && window['ShadyDOM']['noP } else { + // Methods that can provoke distribution or must return the logical, not + // composed tree. forwardMethods(DomApiNative.prototype, [ 'cloneNode', 'appendChild', 'insertBefore', 'removeChild', 'replaceChild', 'setAttribute', 'removeAttribute', 'querySelector', 'querySelectorAll' ]); + // Properties that should return the logical, not composed tree. Note, `classList` + // is here only for legacy compatibility since it does not trigger distribution + // in v1 Shadow DOM. forwardReadOnlyProperties(DomApiNative.prototype, [ 'parentNode', 'firstChild', 'lastChild', 'nextSibling', 'previousSibling', 'firstElementChild',