diff --git a/bower.json b/bower.json index b6655cda..a13c6ad7 100644 --- a/bower.json +++ b/bower.json @@ -20,7 +20,7 @@ "url": "https://github.com/Polymer/polymer.git" }, "dependencies": { - "webcomponentsjs": "^0.7.18" + "webcomponentsjs": "^0.7.20" }, "devDependencies": { "web-component-tester": "*" diff --git a/src/lib/dom-api-shadow.html b/src/lib/dom-api-shadow.html index cc73842b..e442b761 100644 --- a/src/lib/dom-api-shadow.html +++ b/src/lib/dom-api-shadow.html @@ -58,6 +58,18 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN Object.defineProperties(DomApi.prototype, { + activeElement: { + get: function() { + var node = DomApi.wrap(this.node); + var activeElement = node.activeElement; + // Prevents `activeElement` from returning elements outside of the + // ShadowRoot, even if they would become descendants of the ShadowRoot + // in the composed tree. See w3c/webcomponents#358. + return node.contains(activeElement) ? activeElement : null; + }, + configurable: true + }, + childNodes: { get: function() { return TreeApi.arrayCopyChildNodes(this.node); diff --git a/src/lib/dom-api-shady.html b/src/lib/dom-api-shady.html index 64e41c1c..e1f62ffe 100644 --- a/src/lib/dom-api-shady.html +++ b/src/lib/dom-api-shady.html @@ -422,6 +422,47 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN Object.defineProperties(DomApi.prototype, { + activeElement: { + get: function() { + var active = document.activeElement; + if (!active) { + return null; + } + var isShadyRoot = !!this.node._isShadyRoot; + if (this.node !== document) { + // If this node isn't a document or shady root, then it doesn't have + // an active element. + if (!isShadyRoot) { + return null; + } + // If this shady root's host is the active element or the active + // element is not a descendant of the host (in the composed tree), + // then it doesn't have an active element. + if (this.node.host === active || + !this.node.host.contains(active)) { + return null; + } + } + // This node is either the document or a shady root of which the active + // element is a (composed) descendant of its host; iterate upwards to + // find the active element's most shallow host within it. + var activeRoot = dom(active).getOwnerRoot(); + while (activeRoot && activeRoot !== this.node) { + active = activeRoot.host; + activeRoot = dom(active).getOwnerRoot(); + } + if (this.node === document) { + // This node is the document, so activeRoot should be null. + return activeRoot ? null : active; + } else { + // This node is a non-document shady root, and it should be + // activeRoot. + return activeRoot === this.node ? active : null; + } + }, + configurable: true + }, + childNodes: { get: function() { var c$ = TreeApi.Logical.getChildNodes(this.node); @@ -569,4 +610,4 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN }; })(); - \ No newline at end of file + diff --git a/test/unit/polymer-dom-elements.html b/test/unit/polymer-dom-elements.html index c0b28546..64ac6b59 100644 --- a/test/unit/polymer-dom-elements.html +++ b/test/unit/polymer-dom-elements.html @@ -357,3 +357,245 @@ }); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/unit/polymer-dom-native-shadow.html b/test/unit/polymer-dom-native-shadow.html index d7b9d116..49b23aaf 100644 --- a/test/unit/polymer-dom-native-shadow.html +++ b/test/unit/polymer-dom-native-shadow.html @@ -56,6 +56,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN + + + + diff --git a/test/unit/polymer-dom-shadow.html b/test/unit/polymer-dom-shadow.html index f1f52678..cc2a5c2c 100644 --- a/test/unit/polymer-dom-shadow.html +++ b/test/unit/polymer-dom-shadow.html @@ -57,6 +57,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN + + + + diff --git a/test/unit/polymer-dom.html b/test/unit/polymer-dom.html index 7899b1c2..7f0e15c5 100644 --- a/test/unit/polymer-dom.html +++ b/test/unit/polymer-dom.html @@ -57,6 +57,10 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN + + + + diff --git a/test/unit/polymer-dom.js b/test/unit/polymer-dom.js index c11a6e8d..ab9f735d 100644 --- a/test/unit/polymer-dom.js +++ b/test/unit/polymer-dom.js @@ -802,6 +802,294 @@ suite('Polymer.dom accessors', function() { assert.equal(testElement.children.length, 3); }); + suite('Polymer.dom activeElement', function() { + var r; + // light + var r_l + // shadow + var r_0; + // light + var r_0_l; + // shadow + var r_0_0; + // light + var r_0_0_l; + // shadow + var r_0_0_l_0; + var r_0_1; + // light + var r_0_1_l; + var r_1; + // light + var r_1_l; + // shadow + var r_1_l_0; + // shadow + var r_1_0; + // light + var r_1_0_l; + var r_1_1; + // light + var r_1_1_l; + + suiteSetup(function() { + r = Polymer.dom(document).querySelector('x-shadow-host-root'); + r_l = Polymer.dom(r).querySelector('x-shadow-host-root-light'); + r_0 = Polymer.dom(r.root).querySelector('x-shadow-host-root-0'); + r_0_l = Polymer.dom(r_0).querySelector('x-shadow-host-root-0-light'); + r_0_0 = Polymer.dom(r_0.root).querySelector('x-shadow-host-root-0-0'); + r_0_0_l = Polymer.dom(r_0_0).querySelector('x-shadow-host-root-0-0-light'); + r_0_0_l_0 = Polymer.dom(r_0_0_l.root).querySelector('x-shadow-host-root-0-0-light-0'); + r_0_1 = Polymer.dom(r_0.root).querySelector('x-shadow-host-root-0-1'); + r_0_1_l = Polymer.dom(r_0_1).querySelector('x-shadow-host-root-0-1-light'); + r_1 = Polymer.dom(r.root).querySelector('x-shadow-host-root-1'); + r_1_l = Polymer.dom(r_1).querySelector('x-shadow-host-root-1-light'); + r_1_l_0 = Polymer.dom(r_1_l.root).querySelector('x-shadow-host-root-1-light-0'); + r_1_0 = Polymer.dom(r_1.root).querySelector('x-shadow-host-root-1-0'); + r_1_0_l = Polymer.dom(r_1_0).querySelector('x-shadow-host-root-1-0-light'); + r_1_1 = Polymer.dom(r_1.root).querySelector('x-shadow-host-root-1-1'); + r_1_1_l = Polymer.dom(r_1_1).querySelector('x-shadow-host-root-1-1-light'); + }); + + test('r.focus()', function() { + r.focus(); + + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, null, 'r.root.activeElement === null'); + assert.equal(Polymer.dom(r_0.root).activeElement, null, 'r_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, null, 'r_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('r_l.focus()', function() { + r_l.focus(); + + assert.equal(Polymer.dom(document).activeElement, r_l, 'document.activeElement === r_l'); + assert.equal(Polymer.dom(r.root).activeElement, null, 'r.root.activeElement === null'); + assert.equal(Polymer.dom(r_0.root).activeElement, null, 'r_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, null, 'r_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('r_0.focus()', function() { + r_0.focus(); + + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, r_0, 'r.root.activeElement === r_0'); + assert.equal(Polymer.dom(r_0.root).activeElement, null, 'r_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, null, 'r_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('r_0_l.focus()', function() { + r_0_l.focus(); + + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, r_0_l, 'r.root.activeElement === r_0_l'); + assert.equal(Polymer.dom(r_0.root).activeElement, null, 'r_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, null, 'r_1.root.activeElement === null'); + + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('r_0_0.focus()', function() { + r_0_0.focus(); + + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, r_0, 'r.root.activeElement === r_0'); + assert.equal(Polymer.dom(r_0.root).activeElement, r_0_0, 'r_0.root.activeElement === r_0_0'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, null, 'r_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('r_0_0_l.focus()', function() { + r_0_0_l.focus(); + + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, r_0, 'r.root.activeElement === r_0'); + assert.equal(Polymer.dom(r_0.root).activeElement, r_0_0_l, 'r_0.root.activeElement === r_0_0_l'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, null, 'r_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('r_0_0_l_0.focus()', function() { + r_0_0_l_0.focus(); + + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, r_0, 'r.root.activeElement === r_0'); + assert.equal(Polymer.dom(r_0.root).activeElement, r_0_0_l, 'r_0.root.activeElement === r_0_0_l'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_0_l.root).activeElement, r_0_0_l_0, 'r_0_0_l.root.activeElement === r_0_0_l_0'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, null, 'r_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('r_0_1.focus()', function() { + r_0_1.focus(); + + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, r_0, 'r.root.activeElement === r_0'); + assert.equal(Polymer.dom(r_0.root).activeElement, r_0_1, 'r_0.root.activeElement === r_0_1'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, null, 'r_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('r_0_1_l.focus()', function() { + r_0_1_l.focus(); + + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, r_0, 'r.root.activeElement === r_0'); + assert.equal(Polymer.dom(r_0.root).activeElement, r_0_1_l, 'r_0.root.activeElement === r_0_1_l'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, null, 'r_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('r_1.focus()', function() { + r_1.focus(); + + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, r_1, 'r.root.activeElement === r_1'); + assert.equal(Polymer.dom(r_0.root).activeElement, null, 'r_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, null, 'r_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('r_1_l.focus()', function() { + r_1_l.focus(); + + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, r_1_l, 'r.root.activeElement === r_1_l'); + assert.equal(Polymer.dom(r_0.root).activeElement, null, 'r_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, null, 'r_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('r_1_l_0.focus()', function() { + r_1_l_0.focus(); + + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, r_1_l, 'r.root.activeElement === r_1_l'); + assert.equal(Polymer.dom(r_0.root).activeElement, null, 'r_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, null, 'r_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_l.root).activeElement, r_1_l_0, 'r_1.root.activeElement === r_1_l_0'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('r_1_0.focus()', function() { + r_1_0.focus(); + + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, r_1, 'r.root.activeElement === r_1'); + assert.equal(Polymer.dom(r_0.root).activeElement, null, 'r_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, r_1_0, 'r_1.root.activeElement === r_1_0'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('r_1_0_l.focus()', function() { + r_1_0_l.focus(); + + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, r_1, 'r.root.activeElement === r_1'); + assert.equal(Polymer.dom(r_0.root).activeElement, null, 'r_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, r_1_0_l, 'r_1.root.activeElement === r_1_0_l'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('r_1_1.focus()', function() { + r_1_1.focus(); + + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, r_1, 'r.root.activeElement === r_1'); + assert.equal(Polymer.dom(r_0.root).activeElement, null, 'r_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, r_1_1, 'r_1.root.activeElement === r_1_1'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('r_1_1_l.focus()', function() { + r_1_1_l.focus(); + + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, r_1, 'r.root.activeElement === r_1'); + assert.equal(Polymer.dom(r_0.root).activeElement, null, 'r_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, r_1_1_l, 'r_1.root.activeElement === r_1_1_l'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('setting activeElement on document has no effect', function() { + r_1_1.focus(); + + Polymer.dom(document).activeElement = "abc"; + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, r_1, 'r.root.activeElement === r_1'); + assert.equal(Polymer.dom(r_0.root).activeElement, null, 'r_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, r_1_1, 'r_1.root.activeElement === r_1_1'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + + test('setting activeElement on a shadow root has no effect', function() { + r_1_1.focus(); + + Polymer.dom(r_1.root).activeElement = "abc"; + assert.equal(Polymer.dom(document).activeElement, r, 'document.activeElement === r'); + assert.equal(Polymer.dom(r.root).activeElement, r_1, 'r.root.activeElement === r_1'); + assert.equal(Polymer.dom(r_0.root).activeElement, null, 'r_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_0.root).activeElement, null, 'r_0_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_0_1.root).activeElement, null, 'r_0_1.root.activeElement === null'); + assert.equal(Polymer.dom(r_1.root).activeElement, r_1_1, 'r_1.root.activeElement === r_1_1'); + assert.equal(Polymer.dom(r_1_0.root).activeElement, null, 'r_1_0.root.activeElement === null'); + assert.equal(Polymer.dom(r_1_1.root).activeElement, null, 'r_1_1.root.activeElement === null'); + }); + }); }); suite('Polymer.dom non-distributed elements', function() {