From 62e13fd750f519a27fe4cf99a205c3a1202d9937 Mon Sep 17 00:00:00 2001 From: Alexander Marks Date: Mon, 8 Jun 2020 14:50:02 -0700 Subject: [PATCH 01/10] Fix ::part parse bug in element names with >1 dash --- packages/shadycss/src/shadow-parts.js | 4 ++-- .../shadycss/shadow-parts/parsing-formatting.html | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/shadycss/src/shadow-parts.js b/packages/shadycss/src/shadow-parts.js index 876e1bf00..8e9a130d3 100644 --- a/packages/shadycss/src/shadow-parts.js +++ b/packages/shadycss/src/shadow-parts.js @@ -71,9 +71,9 @@ export function parseExportPartsAttribute(attr) { /** * Regular expression to de-compose a ::part rule into interesting pieces. See * parsePartSelector for description of pieces. - * [0 ][1 ][2 ] [3 ] [4 ] + * [0 ][1 ][2 ] [3 ] [4 ] */ -const PART_REGEX = /(.*?)([a-z]+-\w+)([^\s]*?)::part\((.*)?\)(::?.*)?/; +const PART_REGEX = /(.*?)([a-z]+(?:-\w+)+)([^\s]*?)::part\((.*)?\)(::?.*)?/; /** * De-compose a ::part rule into interesting pieces. diff --git a/packages/tests/shadycss/shadow-parts/parsing-formatting.html b/packages/tests/shadycss/shadow-parts/parsing-formatting.html index 2c663f695..c31400f23 100644 --- a/packages/tests/shadycss/shadow-parts/parsing-formatting.html +++ b/packages/tests/shadycss/shadow-parts/parsing-formatting.html @@ -47,6 +47,19 @@ ); }); + test('multiple dashes in custom element name', () => { + assert.deepEqual( + shadowParts.parsePartSelector('my-foo-bar-baz::part(foo)'), + { + combinators: '', + elementName: 'my-foo-bar-baz', + selectors: '', + parts: 'foo', + pseudos: '', + } + ); + }); + test('pseudo-class', () => { assert.deepEqual( shadowParts.parsePartSelector('my-button::part(foo):hover'), From 8159ee4ba42a908d8e1f76aeebbd1242387258a3 Mon Sep 17 00:00:00 2001 From: Alexander Marks Date: Mon, 8 Jun 2020 16:16:58 -0700 Subject: [PATCH 02/10] Fix parsing bug with comma-delim group selectors --- packages/shadycss/src/shadow-parts.js | 44 ++++++------ .../shadow-parts/property-below-provider.html | 2 +- .../shadow-parts/property-selector-group.html | 67 +++++++++++++++++++ .../shadycss/shadow-parts/selector-group.html | 65 ++++++++++++++++++ .../tests/shadycss/shadow-parts/suites.js | 2 + 5 files changed, 159 insertions(+), 21 deletions(-) create mode 100644 packages/tests/shadycss/shadow-parts/property-selector-group.html create mode 100644 packages/tests/shadycss/shadow-parts/selector-group.html diff --git a/packages/shadycss/src/shadow-parts.js b/packages/shadycss/src/shadow-parts.js index 8e9a130d3..5a1593faf 100644 --- a/packages/shadycss/src/shadow-parts.js +++ b/packages/shadycss/src/shadow-parts.js @@ -12,6 +12,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN import {StyleNode} from './css-parse.js'; import StyleInfo from './style-info.js'; import StyleProperties from './style-properties.js'; +import * as StyleUtil from './style-util.js'; import {nativeCssVariables} from './style-settings.js'; /** @@ -546,39 +547,42 @@ export function analyzeTemplatePartRules(providerScope, styleAst) { return; } for (const styleNode of styleAst.rules) { - const selector = styleNode['selector']; - if (!selector || selector.indexOf('::part') === -1) { + const selectorList = styleNode['selector']; + if (!selectorList || selectorList.indexOf('::part') === -1) { // TODO(aomarks) We do the `indexOf` check here to make the case where no // `::part` rules are used is as fast as possible, but we could get away // with just the `parsePartSelector` call we're doing next if the // difference is negligible. Needs benchmarking. continue; } - const parsed = parsePartSelector(selector); - if (parsed === null) { - continue; - } const consumedProperties = findConsumedCustomProperties( styleNode['cssText'] ); if (consumedProperties.length === 0) { continue; } - const {elementName: receiverScope, parts} = parsed; - if (parts.length === 0) { - continue; - } - const key = [providerScope, receiverScope].join(':'); - let entries = partRulesMap.get(key); - if (entries === undefined) { - entries = []; - partRulesMap.set(key, entries); + const selectors = StyleUtil.splitSelectorList(selectorList); + for (const selector of selectors) { + const parsed = parsePartSelector(selector); + if (parsed === null) { + continue; + } + const {elementName: receiverScope, parts} = parsed; + if (parts.length === 0) { + continue; + } + const key = [providerScope, receiverScope].join(':'); + let entries = partRulesMap.get(key); + if (entries === undefined) { + entries = []; + partRulesMap.set(key, entries); + } + entries.push({ + styleNode, + consumedProperties, + partNames: splitPartString(parts), + }); } - entries.push({ - styleNode, - consumedProperties, - partNames: splitPartString(parts), - }); } } diff --git a/packages/tests/shadycss/shadow-parts/property-below-provider.html b/packages/tests/shadycss/shadow-parts/property-below-provider.html index 43d01f905..219c54ae7 100644 --- a/packages/tests/shadycss/shadow-parts/property-below-provider.html +++ b/packages/tests/shadycss/shadow-parts/property-below-provider.html @@ -9,7 +9,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt --> -shadycss/shadow-parts/property-below-providerd +shadycss/shadow-parts/property-below-provider diff --git a/packages/tests/shadycss/shadow-parts/property-selector-group.html b/packages/tests/shadycss/shadow-parts/property-selector-group.html new file mode 100644 index 000000000..6c3f1c05f --- /dev/null +++ b/packages/tests/shadycss/shadow-parts/property-selector-group.html @@ -0,0 +1,67 @@ + + + +shadycss/shadow-parts/property-selector-group + + + + + + + + + + + + + + + + + + + + diff --git a/packages/tests/shadycss/shadow-parts/selector-group.html b/packages/tests/shadycss/shadow-parts/selector-group.html new file mode 100644 index 000000000..cba28d727 --- /dev/null +++ b/packages/tests/shadycss/shadow-parts/selector-group.html @@ -0,0 +1,65 @@ + + + +shadycss/shadow-parts/selector-group + + + + + + + + + + + + + + + + diff --git a/packages/tests/shadycss/shadow-parts/suites.js b/packages/tests/shadycss/shadow-parts/suites.js index b6cdb89fa..f000833b0 100644 --- a/packages/tests/shadycss/shadow-parts/suites.js +++ b/packages/tests/shadycss/shadow-parts/suites.js @@ -24,5 +24,7 @@ export const suites = [ 'property-below-provider.html', 'part-intersection.html', 'property-part-intersection.html', + 'property-selector-group.html', 'property-within-provider.html', + 'selector-group.html', ]; From 8493c66cb70c966fe38b92e46dcb67a2372e7b65 Mon Sep 17 00:00:00 2001 From: Alexander Marks Date: Tue, 9 Jun 2020 15:38:41 -0700 Subject: [PATCH 03/10] Fix bug where comment insert crashed part code --- packages/shadycss/src/shadow-parts.js | 4 ++-- .../tests/shadycss/shadow-parts/insertions.html | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/shadycss/src/shadow-parts.js b/packages/shadycss/src/shadow-parts.js index 5a1593faf..cb39c9b1f 100644 --- a/packages/shadycss/src/shadow-parts.js +++ b/packages/shadycss/src/shadow-parts.js @@ -314,8 +314,8 @@ export function findExportedPartMappings(host) { */ export function onInsertBefore(parentNode, newNode, referenceNode) { /* eslint-enable no-unused-vars */ - if (newNode instanceof Text) { - // No parts in text. + if (newNode instanceof Text || newNode instanceof Comment) { + // No parts in text or comments. return; } if (!parentNode.getRootNode) { diff --git a/packages/tests/shadycss/shadow-parts/insertions.html b/packages/tests/shadycss/shadow-parts/insertions.html index 97022fb9e..8206bd010 100644 --- a/packages/tests/shadycss/shadow-parts/insertions.html +++ b/packages/tests/shadycss/shadow-parts/insertions.html @@ -84,6 +84,11 @@ xb.shadowRoot.appendChild(text); }); + test('appendChild: comment', () => { + const comment = document.createComment('foo'); + xb.shadowRoot.appendChild(comment); + }); + test('insertBefore: part', () => { const pa = document.createElement('div'); pa.setAttribute('part', 'pa'); @@ -114,6 +119,11 @@ xb.shadowRoot.insertBefore(text, null); }); + test('insertBefore: comment', () => { + const comment = document.createComment('foo'); + xb.shadowRoot.insertBefore(comment, null); + }); + test('replaceChild: part', () => { const oldChild = document.createElement('div'); xb.shadowRoot.appendChild(oldChild); @@ -152,6 +162,13 @@ xb.shadowRoot.replaceChild(text, oldChild); }); + test('replaceChild: comment node', () => { + const oldChild = document.createElement('div'); + xb.shadowRoot.appendChild(oldChild); + const comment = document.createComment('foo'); + xb.shadowRoot.replaceChild(comment, oldChild); + }); + test('innerHTML', () => { xb.shadowRoot.innerHTML = `
From f0308c9aa5212343e64a54257a429035f4bc460d Mon Sep 17 00:00:00 2001 From: Alexander Marks Date: Mon, 8 Jun 2020 13:13:06 -0700 Subject: [PATCH 04/10] Support setting and removing part attribute Also use the native method when setting the "shady-part" attribute, instead of going through ShadyDOM. --- packages/shadycss/externs/shadycss-externs.js | 2 + packages/shadycss/src/common-utils.js | 7 + packages/shadycss/src/scoping-shim.js | 40 +++++ packages/shadycss/src/shadow-parts.js | 69 +++++++- packages/shadydom/src/patches/Element.js | 13 ++ .../shadow-parts/attribute-mutations.html | 147 ++++++++++++++++++ .../tests/shadycss/shadow-parts/suites.js | 3 +- 7 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 packages/tests/shadycss/shadow-parts/attribute-mutations.html diff --git a/packages/shadycss/externs/shadycss-externs.js b/packages/shadycss/externs/shadycss-externs.js index e3c3e94cc..b4c5c1dfd 100644 --- a/packages/shadycss/externs/shadycss-externs.js +++ b/packages/shadycss/externs/shadycss-externs.js @@ -10,6 +10,8 @@ * flushCustomStyles: function(), * getComputedStyleValue: function(!Element, string): string, * onInsertBefore: function(!HTMLElement, !HTMLElement, ?HTMLElement): void, + * onSetAttribute: function(!HTMLElement, !string, !string): void, + * onRemoveAttribute: function(!HTMLElement, !string): void, * ScopingShim: (Object|undefined), * ApplyShim: (Object|undefined), * CustomStyleInterface: (Object|undefined), diff --git a/packages/shadycss/src/common-utils.js b/packages/shadycss/src/common-utils.js index 863250be2..b489667db 100644 --- a/packages/shadycss/src/common-utils.js +++ b/packages/shadycss/src/common-utils.js @@ -12,6 +12,13 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN import { MIXIN_MATCH, VAR_ASSIGN } from './common-regex.js'; +/** + * The prefix used by ShadyDOM for the native version of patched methods + * (e.g. `__shady_native_setAttribute`). + * @type {!string} + */ +export const NATIVE_PREFIX = '__shady_native_'; + /** * @param {Element} element * @param {Object=} properties diff --git a/packages/shadycss/src/scoping-shim.js b/packages/shadycss/src/scoping-shim.js index 67c56848d..3ea24678f 100644 --- a/packages/shadycss/src/scoping-shim.js +++ b/packages/shadycss/src/scoping-shim.js @@ -295,6 +295,46 @@ export default class ScopingShim { } } + /* eslint-disable no-unused-vars */ + + /** + * Hook for performing ShadyCSS updates when a relevant attribute is set. + * Note ShadyDOM will only call this function for "part" and "exportparts" + * attributes. + * + * @param {!HTMLElement} element Element + * @param {!string} name Attribute name. + * @param {!string} value Attribute value. + * @return {void} + */ + onSetAttribute(element, name, value) { + if (!disableShadowParts) { + if (name === 'part') { + shadowParts.onSetPartAttribute(element); + } + } + } + + /* eslint-enable no-unused-vars */ + + /** + * Hook for performing ShadyCSS updates when a relevant attribute is removed. + * Note ShadyDOM will only call this function for "part" and "exportparts" + * attributes. + * + * @param {!HTMLElement} element Element + * @param {!string} name Attribute name ("part" or "exportparts") + * @param {!string} value Attribute value. + * @return {void} + */ + onRemoveAttribute(element, name, value) { + if (!disableShadowParts) { + if (name === 'part') { + shadowParts.onRemovePartAttribute(element); + } + } + } + /** * Apply styles for the given element * diff --git a/packages/shadycss/src/shadow-parts.js b/packages/shadycss/src/shadow-parts.js index cb39c9b1f..ada839b11 100644 --- a/packages/shadycss/src/shadow-parts.js +++ b/packages/shadycss/src/shadow-parts.js @@ -14,6 +14,28 @@ import StyleInfo from './style-info.js'; import StyleProperties from './style-properties.js'; import * as StyleUtil from './style-util.js'; import {nativeCssVariables} from './style-settings.js'; +import {NATIVE_PREFIX} from './common-utils.js'; + +/** + * Set the "shady-part" attribute using the native method. + * + * @param {!HTMLElement} element + * @param {!string} value + * @return {void} + */ +function setShadyPartAttribute(element, value) { + element[NATIVE_PREFIX + 'setAttribute']('shady-part', value); +} + +/** + * Remove the "shady-part" attribute using the native method. + * + * @param {!HTMLElement} element + * @return {void} + */ +function removeShadyPartAttribute(element) { + element[NATIVE_PREFIX + 'removeAttribute']('shady-part'); +} /** * Parse a CSS Shadow Parts "part" attribute into an array of part names. @@ -300,6 +322,36 @@ export function findExportedPartMappings(host) { return result; } +/** + * Update the "shady-part" attribute when the "part" attribute is set. + * + * @param {!HTMLElement} element The element. + * @return {void} + */ +export function onSetPartAttribute(element) { + if (!element.getRootNode) { + // TODO(aomarks) We're in noPatch mode. Wrap where needed and add tests. + // https://github.com/webcomponents/polyfills/issues/343 + return; + } + const root = element.getRootNode(); + if (root === document || root === element) { + // Nowhere to receive part styles from. + return; + } + addShadyPartAttributes(root.host, [element]); +} + +/** + * Remove the "shady-part" attribute when the "part" attribute is removed. + * + * @param {!HTMLElement} element The element. + * @return {void} + */ +export function onRemovePartAttribute(element) { + removeShadyPartAttribute(element); +} + /* eslint-disable no-unused-vars */ /** * Add "shady-part" attributes to new nodes on insertion. @@ -346,6 +398,18 @@ export function onInsertBefore(parentNode, newNode, referenceNode) { if (parts.length === 0) { return; } + addShadyPartAttributes(host, parts); +} + +/** + * Add "shady-part" attributes to the given part nodes. All part nodes are + * assumed to be in the same given host. + * + * @param {!HTMLElement} host The host element of the given parts. + * @param {!Array} parts The new or changed part elements. + * @return {void} + */ +export function addShadyPartAttributes(host, parts) { const hostScope = host.localName; const superRoot = host.getRootNode(); const parentScope = @@ -361,6 +425,9 @@ export function onInsertBefore(parentNode, newNode, referenceNode) { const shadyParts = []; const partNames = splitPartString(node.getAttribute('part')); if (partNames.length === 0) { + // Remove the "shady-part" attribute to support the case of "part" getting set to + // the empty string or whitespace. + removeShadyPartAttribute(node); continue; } @@ -390,7 +457,7 @@ export function onInsertBefore(parentNode, newNode, referenceNode) { } } } - node.setAttribute('shady-part', shadyParts.join(' ')); + setShadyPartAttribute(node, shadyParts.join(' ')); if (!nativeCssVariables) { // Find the part rules that match this node and that consume custom diff --git a/packages/shadydom/src/patches/Element.js b/packages/shadydom/src/patches/Element.js index 70839263c..8a1254b2f 100644 --- a/packages/shadydom/src/patches/Element.js +++ b/packages/shadydom/src/patches/Element.js @@ -13,6 +13,7 @@ import {scopeClassAttribute} from '../style-scoping.js'; import {shadyDataForNode} from '../shady-data.js'; import {attachShadow, ownerShadyRootForNode} from '../attach-shadow.js'; import {eventPropertyNamesForElement, wrappedDescriptorForEventProperty} from '../patch-events.js'; +import {getScopingShim} from '../style-scoping.js'; const doc = window.document; @@ -102,6 +103,12 @@ export const ElementPatches = utils.getOwnPropertyDescriptors({ } else if (!scopeClassAttribute(this, attr, value)) { this[utils.NATIVE_PREFIX + 'setAttribute'](attr, value); distributeAttributeChange(this, attr); + if (!utils.disableShadowParts && attr === 'part') { + const shim = getScopingShim(); + if (shim) { + shim['onSetAttribute'](this, attr, value); + } + } } }, @@ -115,6 +122,12 @@ export const ElementPatches = utils.getOwnPropertyDescriptors({ } else if (!scopeClassAttribute(this, attr, '')) { this[utils.NATIVE_PREFIX + 'removeAttribute'](attr); distributeAttributeChange(this, attr); + if (!utils.disableShadowParts && attr === 'part') { + const shim = getScopingShim(); + if (shim) { + shim['onRemoveAttribute'](this, attr); + } + } } else if (this.getAttribute(attr) === '') { // ensure that "class" attribute is fully removed if ShadyCSS does not keep scoping this[utils.NATIVE_PREFIX + 'removeAttribute'](attr); diff --git a/packages/tests/shadycss/shadow-parts/attribute-mutations.html b/packages/tests/shadycss/shadow-parts/attribute-mutations.html new file mode 100644 index 000000000..d54ad2c6c --- /dev/null +++ b/packages/tests/shadycss/shadow-parts/attribute-mutations.html @@ -0,0 +1,147 @@ + + + +shadycss/shadow-parts/attribute-mutations + + + + + + + + + + + + + + + + diff --git a/packages/tests/shadycss/shadow-parts/suites.js b/packages/tests/shadycss/shadow-parts/suites.js index f000833b0..6c3599857 100644 --- a/packages/tests/shadycss/shadow-parts/suites.js +++ b/packages/tests/shadycss/shadow-parts/suites.js @@ -13,6 +13,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN * that we can include it in a few different runner indexes. */ export const suites = [ + 'attribute-mutations.html', 'basic.html', 'disable.html', 'document.html', @@ -20,9 +21,9 @@ export const suites = [ 'find-exported-part-mappings.html', 'insertions.html', 'parsing-formatting.html', + 'part-intersection.html', 'property-above-provider.html', 'property-below-provider.html', - 'part-intersection.html', 'property-part-intersection.html', 'property-selector-group.html', 'property-within-provider.html', From 318c6b0cb8eaa0042a17dd6b363689e181475a4d Mon Sep 17 00:00:00 2001 From: Alexander Marks Date: Mon, 8 Jun 2020 15:20:16 -0700 Subject: [PATCH 05/10] Add test for moving part between shadow roots --- .../shadow-parts/attribute-mutations.html | 2 +- .../shadow-parts/move-shadow-roots.html | 108 ++++++++++++++++++ .../tests/shadycss/shadow-parts/suites.js | 1 + .../tests/shadycss/shadow-parts/test-utils.js | 1 + 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 packages/tests/shadycss/shadow-parts/move-shadow-roots.html diff --git a/packages/tests/shadycss/shadow-parts/attribute-mutations.html b/packages/tests/shadycss/shadow-parts/attribute-mutations.html index d54ad2c6c..6ea1c3064 100644 --- a/packages/tests/shadycss/shadow-parts/attribute-mutations.html +++ b/packages/tests/shadycss/shadow-parts/attribute-mutations.html @@ -39,7 +39,7 @@ + + + + + + + + + + + + + + + + + diff --git a/packages/tests/shadycss/shadow-parts/suites.js b/packages/tests/shadycss/shadow-parts/suites.js index 6c3599857..0b738ac1e 100644 --- a/packages/tests/shadycss/shadow-parts/suites.js +++ b/packages/tests/shadycss/shadow-parts/suites.js @@ -20,6 +20,7 @@ export const suites = [ 'exports.html', 'find-exported-part-mappings.html', 'insertions.html', + 'move-shadow-roots.html', 'parsing-formatting.html', 'part-intersection.html', 'property-above-provider.html', diff --git a/packages/tests/shadycss/shadow-parts/test-utils.js b/packages/tests/shadycss/shadow-parts/test-utils.js index 1388baee3..5f28ec133 100644 --- a/packages/tests/shadycss/shadow-parts/test-utils.js +++ b/packages/tests/shadycss/shadow-parts/test-utils.js @@ -9,6 +9,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN */ export const black = 'rgb(0, 0, 0)'; +export const blackQuad = 'rgba(0, 0, 0, 0)'; export const red = 'rgb(255, 0, 0)'; export const green = 'rgb(0, 128, 0)'; export const blue = 'rgb(0, 0, 255)'; From e53e9d5007b2ea3bcbe55522948cd3f0dfdc3d31 Mon Sep 17 00:00:00 2001 From: Alexander Marks Date: Mon, 8 Jun 2020 18:58:37 -0700 Subject: [PATCH 06/10] Support mutating "exportparts" attribute --- packages/shadycss/externs/shadycss-externs.js | 2 +- packages/shadycss/src/scoping-shim.js | 19 ++- packages/shadycss/src/shadow-parts.js | 83 ++++++++++ packages/shadydom/src/patches/Element.js | 26 ++- .../exportparts-attribute-mutations.html | 155 ++++++++++++++++++ ...ons.html => part-attribute-mutations.html} | 2 +- .../tests/shadycss/shadow-parts/suites.js | 3 +- 7 files changed, 274 insertions(+), 16 deletions(-) create mode 100644 packages/tests/shadycss/shadow-parts/exportparts-attribute-mutations.html rename packages/tests/shadycss/shadow-parts/{attribute-mutations.html => part-attribute-mutations.html} (98%) diff --git a/packages/shadycss/externs/shadycss-externs.js b/packages/shadycss/externs/shadycss-externs.js index b4c5c1dfd..7b38f1737 100644 --- a/packages/shadycss/externs/shadycss-externs.js +++ b/packages/shadycss/externs/shadycss-externs.js @@ -10,7 +10,7 @@ * flushCustomStyles: function(), * getComputedStyleValue: function(!Element, string): string, * onInsertBefore: function(!HTMLElement, !HTMLElement, ?HTMLElement): void, - * onSetAttribute: function(!HTMLElement, !string, !string): void, + * onSetAttribute: function(!HTMLElement, !string, !string, ?string): void, * onRemoveAttribute: function(!HTMLElement, !string): void, * ScopingShim: (Object|undefined), * ApplyShim: (Object|undefined), diff --git a/packages/shadycss/src/scoping-shim.js b/packages/shadycss/src/scoping-shim.js index 3ea24678f..5fea7d3eb 100644 --- a/packages/shadycss/src/scoping-shim.js +++ b/packages/shadycss/src/scoping-shim.js @@ -298,19 +298,22 @@ export default class ScopingShim { /* eslint-disable no-unused-vars */ /** - * Hook for performing ShadyCSS updates when a relevant attribute is set. + * Hook for performing ShadyCSS updates after a relevant attribute is set. * Note ShadyDOM will only call this function for "part" and "exportparts" * attributes. * * @param {!HTMLElement} element Element * @param {!string} name Attribute name. - * @param {!string} value Attribute value. + * @param {!string} value New attribute value. + * @param {?string} oldValue Old attribute value or null if was unset. * @return {void} */ - onSetAttribute(element, name, value) { + onSetAttribute(element, name, newValue, oldValue) { if (!disableShadowParts) { if (name === 'part') { shadowParts.onSetPartAttribute(element); + } else if (name === 'exportparts') { + shadowParts.onSetExportPartsAttribute(element, newValue, oldValue); } } } @@ -318,19 +321,21 @@ export default class ScopingShim { /* eslint-enable no-unused-vars */ /** - * Hook for performing ShadyCSS updates when a relevant attribute is removed. + * Hook for performing ShadyCSS updates after a relevant attribute is removed. * Note ShadyDOM will only call this function for "part" and "exportparts" * attributes. * * @param {!HTMLElement} element Element - * @param {!string} name Attribute name ("part" or "exportparts") - * @param {!string} value Attribute value. + * @param {!string} name Attribute name. + * @param {!string} oldValue Old attribute value. * @return {void} */ - onRemoveAttribute(element, name, value) { + onRemoveAttribute(element, name, oldValue) { if (!disableShadowParts) { if (name === 'part') { shadowParts.onRemovePartAttribute(element); + } else if (name === 'exportparts') { + shadowParts.onRemoveExportPartsAttribute(element, oldValue); } } } diff --git a/packages/shadycss/src/shadow-parts.js b/packages/shadycss/src/shadow-parts.js index ada839b11..95c8c882f 100644 --- a/packages/shadycss/src/shadow-parts.js +++ b/packages/shadycss/src/shadow-parts.js @@ -352,6 +352,89 @@ export function onRemovePartAttribute(element) { removeShadyPartAttribute(element); } +/** + * Update descendant parts when the "exportparts" attribute is set. + * + * @param {!HTMLElement} element The element. + * @param {!string} newValue The new "exportparts" value. + * @param {?string} oldValue The old "exportparts" value or null if was unset. + * @return {void} + */ +export function onSetExportPartsAttribute(element, newValue, oldValue) { + let parsed = parseExportPartsAttribute(newValue); + if (oldValue) { + parsed.push(...parseExportPartsAttribute(oldValue)); + } + refreshShadyPartAttributes( + element, + parsed.map(({inner}) => inner) + ); +} + +/** + * Update descendant parts when the "exportparts" attribute is removed. + * + * @param {!HTMLElement} element The element. + * @param {!string} oldValue The old "exportparts" value. + * @return {void} + */ +export function onRemoveExportPartsAttribute(element, oldValue) { + const parsed = parseExportPartsAttribute(oldValue); + refreshShadyPartAttributes( + element, + parsed.map(({inner}) => inner) + ); +} + +/** + * Update the "shady-parts" attribute for all part nodes that may be affected by + * the given list of part names that have been added or removed, including those + * in descendent scopes via "exportparts" attributes. + * + * @param {!HTMLElement} host The host element. + * @param {!Array} staleNames The part names which have changed. + * @return {void} + */ +function refreshShadyPartAttributes(host, staleNames) { + if (staleNames.length === 0) { + return; + } + const root = host.shadowRoot; + if (!root) { + return; + } + const staleParts = []; + // TODO(aomarks) We've already looked at this shadow root, since an insert + // call must have already happened. It might be worth at least storing a bit + // on each host that has a part or exporter, so that we can skip unneccessary + // querySelectorAll calls here. + for (const partNode of root.querySelectorAll('[part]')) { + for (const name of splitPartString(partNode.getAttribute('part'))) { + if (staleNames.includes(name)) { + staleParts.push(partNode); + break; + } + } + } + if (staleParts.length > 0) { + addShadyPartAttributes(host, staleParts); + } + for (const exporter of root.querySelectorAll('[exportparts]')) { + const staleInnerNames = []; + const parsed = parseExportPartsAttribute( + exporter.getAttribute('exportparts') + ); + for (const {inner, outer} of parsed) { + if (staleNames.includes(outer)) { + staleInnerNames.push(inner); + } + } + if (staleInnerNames.length > 0) { + refreshShadyPartAttributes(exporter, staleInnerNames); + } + } +} + /* eslint-disable no-unused-vars */ /** * Add "shady-part" attributes to new nodes on insertion. diff --git a/packages/shadydom/src/patches/Element.js b/packages/shadydom/src/patches/Element.js index 8a1254b2f..2591cd73d 100644 --- a/packages/shadydom/src/patches/Element.js +++ b/packages/shadydom/src/patches/Element.js @@ -101,14 +101,21 @@ export const ElementPatches = utils.getOwnPropertyDescriptors({ if (this.ownerDocument !== doc) { this[utils.NATIVE_PREFIX + 'setAttribute'](attr, value); } else if (!scopeClassAttribute(this, attr, value)) { + const affectsShadowParts = + !utils.disableShadowParts && + (attr === 'part' || attr === 'exportparts'); + let oldValue; + if (affectsShadowParts) { + oldValue = this.getAttribute(attr); + } this[utils.NATIVE_PREFIX + 'setAttribute'](attr, value); - distributeAttributeChange(this, attr); - if (!utils.disableShadowParts && attr === 'part') { + if (affectsShadowParts) { const shim = getScopingShim(); if (shim) { - shim['onSetAttribute'](this, attr, value); + shim['onSetAttribute'](this, attr, value, oldValue); } } + distributeAttributeChange(this, attr); } }, @@ -120,14 +127,21 @@ export const ElementPatches = utils.getOwnPropertyDescriptors({ if (this.ownerDocument !== doc) { this[utils.NATIVE_PREFIX + 'removeAttribute'](attr); } else if (!scopeClassAttribute(this, attr, '')) { + const affectsShadowParts = + !utils.disableShadowParts && + (attr === 'part' || attr === 'exportparts'); + let oldValue; + if (affectsShadowParts) { + oldValue = this.getAttribute(attr); + } this[utils.NATIVE_PREFIX + 'removeAttribute'](attr); - distributeAttributeChange(this, attr); - if (!utils.disableShadowParts && attr === 'part') { + if (affectsShadowParts) { const shim = getScopingShim(); if (shim) { - shim['onRemoveAttribute'](this, attr); + shim['onRemoveAttribute'](this, attr, oldValue); } } + distributeAttributeChange(this, attr); } else if (this.getAttribute(attr) === '') { // ensure that "class" attribute is fully removed if ShadyCSS does not keep scoping this[utils.NATIVE_PREFIX + 'removeAttribute'](attr); diff --git a/packages/tests/shadycss/shadow-parts/exportparts-attribute-mutations.html b/packages/tests/shadycss/shadow-parts/exportparts-attribute-mutations.html new file mode 100644 index 000000000..0c6956515 --- /dev/null +++ b/packages/tests/shadycss/shadow-parts/exportparts-attribute-mutations.html @@ -0,0 +1,155 @@ + + + +shadycss/shadow-parts/exportparts-attribute-mutations + + + + + + + + + + + + + + + + + + + + diff --git a/packages/tests/shadycss/shadow-parts/attribute-mutations.html b/packages/tests/shadycss/shadow-parts/part-attribute-mutations.html similarity index 98% rename from packages/tests/shadycss/shadow-parts/attribute-mutations.html rename to packages/tests/shadycss/shadow-parts/part-attribute-mutations.html index 6ea1c3064..8d8859178 100644 --- a/packages/tests/shadycss/shadow-parts/attribute-mutations.html +++ b/packages/tests/shadycss/shadow-parts/part-attribute-mutations.html @@ -9,7 +9,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt --> -shadycss/shadow-parts/attribute-mutations +shadycss/shadow-parts/part-attribute-mutations diff --git a/packages/tests/shadycss/shadow-parts/suites.js b/packages/tests/shadycss/shadow-parts/suites.js index 0b738ac1e..1ba7fc3c8 100644 --- a/packages/tests/shadycss/shadow-parts/suites.js +++ b/packages/tests/shadycss/shadow-parts/suites.js @@ -13,15 +13,16 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN * that we can include it in a few different runner indexes. */ export const suites = [ - 'attribute-mutations.html', 'basic.html', 'disable.html', 'document.html', + 'exportparts-attribute-mutations.html', 'exports.html', 'find-exported-part-mappings.html', 'insertions.html', 'move-shadow-roots.html', 'parsing-formatting.html', + 'part-attribute-mutations.html', 'part-intersection.html', 'property-above-provider.html', 'property-below-provider.html', From 3be8dfd1a59fe4efd0c447a6572828eac1fc1211 Mon Sep 17 00:00:00 2001 From: Alexander Marks Date: Tue, 9 Jun 2020 15:47:42 -0700 Subject: [PATCH 07/10] Support inserting and moving "exportparts" nodes --- packages/shadycss/src/shadow-parts.js | 23 ++- .../shadycss/shadow-parts/insert-exports.html | 151 ++++++++++++++++++ .../{insertions.html => insert-parts.html} | 4 +- .../move-shadow-roots-exporter.html | 91 +++++++++++ .../tests/shadycss/shadow-parts/suites.js | 4 +- 5 files changed, 267 insertions(+), 6 deletions(-) create mode 100644 packages/tests/shadycss/shadow-parts/insert-exports.html rename packages/tests/shadycss/shadow-parts/{insertions.html => insert-parts.html} (98%) create mode 100644 packages/tests/shadycss/shadow-parts/move-shadow-roots-exporter.html diff --git a/packages/shadycss/src/shadow-parts.js b/packages/shadycss/src/shadow-parts.js index 95c8c882f..b6cd04431 100644 --- a/packages/shadycss/src/shadow-parts.js +++ b/packages/shadycss/src/shadow-parts.js @@ -478,10 +478,27 @@ export function onInsertBefore(parentNode, newNode, referenceNode) { if (newNode instanceof HTMLElement && newNode.hasAttribute('part')) { parts = [newNode, ...parts]; } - if (parts.length === 0) { - return; + if (parts.length > 0) { + addShadyPartAttributes(host, parts); + } + // Handle "exportparts" nodes moving from one root to another. In this case, + // any descendant part nodes have already been inserted, so we can't rely on + // the upwards-walk that we would normally get from an insert call. We can + // skip this work if we're inserting from a DocumentFragment, though, because + // it's safe to assume in that case that the children have never been + // connected anywhere else. + if (!(newNode instanceof DocumentFragment)) { + let exporters = newNode.querySelectorAll('[exportparts]'); + if (newNode instanceof HTMLElement && newNode.hasAttribute('exportparts')) { + exporters = [newNode, ...exporters]; + } + for (const exporter of exporters) { + const partNames = parseExportPartsAttribute( + exporter.getAttribute('exportparts') + ).map(({inner}) => inner); + refreshShadyPartAttributes(exporter, partNames); + } } - addShadyPartAttributes(host, parts); } /** diff --git a/packages/tests/shadycss/shadow-parts/insert-exports.html b/packages/tests/shadycss/shadow-parts/insert-exports.html new file mode 100644 index 000000000..96e785636 --- /dev/null +++ b/packages/tests/shadycss/shadow-parts/insert-exports.html @@ -0,0 +1,151 @@ + + + +shadycss/shadow-parts/insert-exports + + + + + + + + + + + + + + + + + + + + diff --git a/packages/tests/shadycss/shadow-parts/insertions.html b/packages/tests/shadycss/shadow-parts/insert-parts.html similarity index 98% rename from packages/tests/shadycss/shadow-parts/insertions.html rename to packages/tests/shadycss/shadow-parts/insert-parts.html index 8206bd010..f76f47d82 100644 --- a/packages/tests/shadycss/shadow-parts/insertions.html +++ b/packages/tests/shadycss/shadow-parts/insert-parts.html @@ -9,7 +9,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt --> -shadycss/shadow-parts/insertions +shadycss/shadow-parts/insert parts @@ -36,7 +36,7 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/packages/tests/shadycss/shadow-parts/suites.js b/packages/tests/shadycss/shadow-parts/suites.js index 1ba7fc3c8..5e1404bad 100644 --- a/packages/tests/shadycss/shadow-parts/suites.js +++ b/packages/tests/shadycss/shadow-parts/suites.js @@ -19,8 +19,10 @@ export const suites = [ 'exportparts-attribute-mutations.html', 'exports.html', 'find-exported-part-mappings.html', - 'insertions.html', + 'insert-exports.html', + 'insert-parts.html', 'move-shadow-roots.html', + 'move-shadow-roots-exporter.html', 'parsing-formatting.html', 'part-attribute-mutations.html', 'part-intersection.html', From 7357f4239d6b17b5fc44ea69c07428e29a6d62aa Mon Sep 17 00:00:00 2001 From: Alexander Marks Date: Wed, 10 Jun 2020 14:21:57 -0700 Subject: [PATCH 08/10] Clarifications for #357 review comments --- packages/shadycss/src/shadow-parts.js | 96 ++++++++++++------- packages/shadycss/src/style-transformer.js | 22 +++-- .../exportparts-attribute-mutations.html | 2 +- .../shadow-parts/move-shadow-roots.html | 4 + .../shadow-parts/parsing-formatting.html | 10 +- 5 files changed, 84 insertions(+), 50 deletions(-) diff --git a/packages/shadycss/src/shadow-parts.js b/packages/shadycss/src/shadow-parts.js index b6cd04431..82bde82b9 100644 --- a/packages/shadycss/src/shadow-parts.js +++ b/packages/shadycss/src/shadow-parts.js @@ -70,9 +70,9 @@ export function splitPartString(str) { * Order and duplicates are preserved. */ export function parseExportPartsAttribute(attr) { - const parts = []; - for (const part of attr.split(/\s*,\s*/)) { - const split = part.split(/\s*:\s*/); + const exports = []; + for (const segment of attr.split(/\s*,\s*/)) { + const split = segment.split(/\s*:\s*/); let inner, outer; if (split.length === 1) { // E.g. "foo" @@ -86,9 +86,9 @@ export function parseExportPartsAttribute(attr) { // matches native behavior). continue; } - parts.push({inner, outer}); + exports.push({inner, outer}); } - return parts; + return exports; } /** @@ -124,7 +124,7 @@ const PART_REGEX = /(.*?)([a-z]+(?:-\w+)+)([^\s]*?)::part\((.*)?\)(::?.*)?/; * combinators: !string, * elementName: !string, * selectors: !string, - * parts: !string, + * partNames: !string, * pseudos: !string * }} */ @@ -133,8 +133,14 @@ export function parsePartSelector(selector) { if (match === null) { return null; } - const [, combinators, elementName, selectors, parts, pseudos] = match; - return {combinators, elementName, selectors, parts, pseudos: pseudos || ''}; + const [, combinators, elementName, selectors, partNames, pseudos] = match; + return { + combinators, + elementName, + selectors, + partNames, + pseudos: pseudos || '', + }; } /** @@ -279,15 +285,15 @@ export function findExportedPartMappings(host) { providerScope = superSuperHost.localName; } - const parsed = parseExportPartsAttribute(attr); - if (parsed.length === 0) { + const exports = parseExportPartsAttribute(attr); + if (exports.length === 0) { // This could happen if the attribute was set to something non-empty, but // it was entirely invalid. break; } const newForwarding = {}; - for (const {inner, outer} of parsed) { + for (const {inner, outer} of exports) { let basePart; if (forwarding === undefined) { // We're in the immediate parent scope (since we haven't initialized the @@ -339,7 +345,7 @@ export function onSetPartAttribute(element) { // Nowhere to receive part styles from. return; } - addShadyPartAttributes(root.host, [element]); + styleShadowParts(root.host, [element]); } /** @@ -361,13 +367,13 @@ export function onRemovePartAttribute(element) { * @return {void} */ export function onSetExportPartsAttribute(element, newValue, oldValue) { - let parsed = parseExportPartsAttribute(newValue); + let exports = parseExportPartsAttribute(newValue); if (oldValue) { - parsed.push(...parseExportPartsAttribute(oldValue)); + exports.push(...parseExportPartsAttribute(oldValue)); } refreshShadyPartAttributes( element, - parsed.map(({inner}) => inner) + exports.map(({inner}) => inner) ); } @@ -379,10 +385,10 @@ export function onSetExportPartsAttribute(element, newValue, oldValue) { * @return {void} */ export function onRemoveExportPartsAttribute(element, oldValue) { - const parsed = parseExportPartsAttribute(oldValue); + const exports = parseExportPartsAttribute(oldValue); refreshShadyPartAttributes( element, - parsed.map(({inner}) => inner) + exports.map(({inner}) => inner) ); } @@ -403,7 +409,7 @@ function refreshShadyPartAttributes(host, staleNames) { if (!root) { return; } - const staleParts = []; + const stalePartNodes = []; // TODO(aomarks) We've already looked at this shadow root, since an insert // call must have already happened. It might be worth at least storing a bit // on each host that has a part or exporter, so that we can skip unneccessary @@ -411,20 +417,20 @@ function refreshShadyPartAttributes(host, staleNames) { for (const partNode of root.querySelectorAll('[part]')) { for (const name of splitPartString(partNode.getAttribute('part'))) { if (staleNames.includes(name)) { - staleParts.push(partNode); + stalePartNodes.push(partNode); break; } } } - if (staleParts.length > 0) { - addShadyPartAttributes(host, staleParts); + if (stalePartNodes.length > 0) { + styleShadowParts(host, stalePartNodes); } for (const exporter of root.querySelectorAll('[exportparts]')) { const staleInnerNames = []; - const parsed = parseExportPartsAttribute( + const exports = parseExportPartsAttribute( exporter.getAttribute('exportparts') ); - for (const {inner, outer} of parsed) { + for (const {inner, outer} of exports) { if (staleNames.includes(outer)) { staleInnerNames.push(inner); } @@ -470,16 +476,16 @@ export function onInsertBefore(parentNode, newNode, referenceNode) { // here. return; } - let parts = newNode.querySelectorAll('[part]'); + let partNodes = newNode.querySelectorAll('[part]'); // TODO(aomarks) We should be able to get much better performance over the // querySelectorAll calls here by integrating the part check into the walk // that ShadyDOM already does to find slots. // https://github.com/webcomponents/polyfills/issues/345 if (newNode instanceof HTMLElement && newNode.hasAttribute('part')) { - parts = [newNode, ...parts]; + partNodes = [newNode, ...partNodes]; } - if (parts.length > 0) { - addShadyPartAttributes(host, parts); + if (partNodes.length > 0) { + styleShadowParts(host, partNodes); } // Handle "exportparts" nodes moving from one root to another. In this case, // any descendant part nodes have already been inserted, so we can't rely on @@ -493,23 +499,39 @@ export function onInsertBefore(parentNode, newNode, referenceNode) { exporters = [newNode, ...exporters]; } for (const exporter of exporters) { - const partNames = parseExportPartsAttribute( + const exports = parseExportPartsAttribute( exporter.getAttribute('exportparts') ).map(({inner}) => inner); - refreshShadyPartAttributes(exporter, partNames); + refreshShadyPartAttributes(exporter, exports); } } } /** - * Add "shady-part" attributes to the given part nodes. All part nodes are - * assumed to be in the same given host. + * Enable/update styling of the given part nodes in the given host. All given + * part nodes are assumed to be within the shadow root of the given host. This + * function does the following: + * + * [1] For each given part node, add a "shady-part" attribute with format + * "host-scope:parent-scope:part-name". + * + * [2] Walk upwards through the tree of shadow hosts following "exportparts" + * attributes. For each exporting scope, for each given part node, add an + * additional "shady-part" attribute value with format e.g. + * "parent-scope:grand-parent-scope:aliased-part-name". + * + * [3] If any of these ancestor scopes provide a ::part style rule to any of + * the given parts, and if any of those rules consume a CSS custom property, + * then "adopt" those style rules (by adding them to the host's StyleInfo + * object) and enable per-instance styling, so that CSS custom property values + * are computed at the correct scope (the receiver's scope, not the + * provider's). * * @param {!HTMLElement} host The host element of the given parts. - * @param {!Array} parts The new or changed part elements. + * @param {!Array} partNodes The new or changed part elements. * @return {void} */ -export function addShadyPartAttributes(host, parts) { +export function styleShadowParts(host, partNodes) { const hostScope = host.localName; const superRoot = host.getRootNode(); const parentScope = @@ -521,7 +543,7 @@ export function addShadyPartAttributes(host, parts) { let needsRescoping = false; let newStyleNodes, newProperties; - for (const node of parts) { + for (const node of partNodes) { const shadyParts = []; const partNames = splitPartString(node.getAttribute('part')); if (partNames.length === 0) { @@ -734,8 +756,8 @@ export function analyzeTemplatePartRules(providerScope, styleAst) { if (parsed === null) { continue; } - const {elementName: receiverScope, parts} = parsed; - if (parts.length === 0) { + const {elementName: receiverScope, partNames} = parsed; + if (partNames.length === 0) { continue; } const key = [providerScope, receiverScope].join(':'); @@ -747,7 +769,7 @@ export function analyzeTemplatePartRules(providerScope, styleAst) { entries.push({ styleNode, consumedProperties, - partNames: splitPartString(parts), + partNames: splitPartString(partNames), }); } } diff --git a/packages/shadycss/src/style-transformer.js b/packages/shadycss/src/style-transformer.js index 8c9981023..472409851 100644 --- a/packages/shadycss/src/style-transformer.js +++ b/packages/shadycss/src/style-transformer.js @@ -297,8 +297,11 @@ class StyleTransformer { * @return {string} */ _replaceMatchesPseudo(selector, matches) { - const parts = selector.split(MATCHES_REPLACEMENT); - return matches.reduce((acc, cur, idx) => acc + cur + parts[idx + 1], parts[0]); + const segments = selector.split(MATCHES_REPLACEMENT); + return matches.reduce( + (acc, cur, idx) => acc + cur + segments[idx + 1], + segments[0] + ); } /** @@ -422,13 +425,18 @@ class StyleTransformer { if (parsed === null) { return selector; } - const {combinators, elementName, selectors, parts, pseudos} = parsed; + const {combinators, elementName, selectors, partNames, pseudos} = parsed; // Earlier we did a hacky transform from "part(foo bar)" to "part(foo,bar)" // so that the SIMPLE_SELECTOR regex didn't get confused by spaces. - const partSelector = - formatShadyPartSelector(scope, elementName, parts.replace(',', ' ')); - return (scope === 'document' ? '' : scope + ' ') + - `${combinators}${elementName}${selectors} ${partSelector}${pseudos}`; + const partSelector = formatShadyPartSelector( + scope, + elementName, + partNames.replace(',', ' ') + ); + return ( + (scope === 'document' ? '' : scope + ' ') + + `${combinators}${elementName}${selectors} ${partSelector}${pseudos}` + ); } // :host(...) -> scopeName... diff --git a/packages/tests/shadycss/shadow-parts/exportparts-attribute-mutations.html b/packages/tests/shadycss/shadow-parts/exportparts-attribute-mutations.html index 0c6956515..db06f600f 100644 --- a/packages/tests/shadycss/shadow-parts/exportparts-attribute-mutations.html +++ b/packages/tests/shadycss/shadow-parts/exportparts-attribute-mutations.html @@ -82,7 +82,7 @@ document.body.removeChild(great); }); - test('initial state', () => { + test('verify initial state', () => { assert.equal(style(part).color, red); assert.equal(style(part).backgroundColor, red); assert.equal(style(part).fontStyle, 'normal'); diff --git a/packages/tests/shadycss/shadow-parts/move-shadow-roots.html b/packages/tests/shadycss/shadow-parts/move-shadow-roots.html index 0236b731b..b49809805 100644 --- a/packages/tests/shadycss/shadow-parts/move-shadow-roots.html +++ b/packages/tests/shadycss/shadow-parts/move-shadow-roots.html @@ -92,12 +92,16 @@ assert.equal(style(part).paddingTop, '0px'); assert.equal(style(part).marginTop, '10px'); + // Also confirm removeChild doesn't do anything weird (this is + // semantically equivalent to just calling appendChild). + greenChild.shadowRoot.removeChild(part); redChild.shadowRoot.appendChild(part); assert.equal(style(part).color, red) assert.equal(style(part).backgroundColor, blackQuad); assert.equal(style(part).paddingTop, '10px'); assert.equal(style(part).marginTop, '0px'); + redChild.shadowRoot.removeChild(part); greenChild.shadowRoot.appendChild(part); assert.equal(style(part).color, black); assert.equal(style(part).backgroundColor, green); diff --git a/packages/tests/shadycss/shadow-parts/parsing-formatting.html b/packages/tests/shadycss/shadow-parts/parsing-formatting.html index c31400f23..f5341fd1c 100644 --- a/packages/tests/shadycss/shadow-parts/parsing-formatting.html +++ b/packages/tests/shadycss/shadow-parts/parsing-formatting.html @@ -41,7 +41,7 @@ combinators: '', elementName: 'my-button', selectors: '', - parts: 'foo', + partNames: 'foo', pseudos: '', } ); @@ -54,7 +54,7 @@ combinators: '', elementName: 'my-foo-bar-baz', selectors: '', - parts: 'foo', + partNames: 'foo', pseudos: '', } ); @@ -67,7 +67,7 @@ combinators: '', elementName: 'my-button', selectors: '', - parts: 'foo', + partNames: 'foo', pseudos: ':hover', } ); @@ -80,7 +80,7 @@ combinators: '', elementName: 'my-button', selectors: '', - parts: 'foo', + partNames: 'foo', pseudos: '::before', } ); @@ -95,7 +95,7 @@ combinators: '#parent > ', elementName: 'my-button', selectors: '.fancy', - parts: 'foo bar', + partNames: 'foo bar', pseudos: ':hover', } ); From 5258942d60c7a90886ea99b05ef6119009c73f0c Mon Sep 17 00:00:00 2001 From: Alexander Marks Date: Wed, 10 Jun 2020 14:45:56 -0700 Subject: [PATCH 09/10] Fix incorrect jsdoc param name --- packages/shadycss/src/scoping-shim.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shadycss/src/scoping-shim.js b/packages/shadycss/src/scoping-shim.js index 5fea7d3eb..e5169bc19 100644 --- a/packages/shadycss/src/scoping-shim.js +++ b/packages/shadycss/src/scoping-shim.js @@ -304,7 +304,7 @@ export default class ScopingShim { * * @param {!HTMLElement} element Element * @param {!string} name Attribute name. - * @param {!string} value New attribute value. + * @param {!string} newValue New attribute value. * @param {?string} oldValue Old attribute value or null if was unset. * @return {void} */ From aa40da3b0167ae9829542ec71da32c34f45797a0 Mon Sep 17 00:00:00 2001 From: Alexander Marks Date: Wed, 10 Jun 2020 14:48:50 -0700 Subject: [PATCH 10/10] Fix incorrect comment --- packages/shadycss/src/shadow-parts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shadycss/src/shadow-parts.js b/packages/shadycss/src/shadow-parts.js index 82bde82b9..004d89675 100644 --- a/packages/shadycss/src/shadow-parts.js +++ b/packages/shadycss/src/shadow-parts.js @@ -518,7 +518,7 @@ export function onInsertBefore(parentNode, newNode, referenceNode) { * [2] Walk upwards through the tree of shadow hosts following "exportparts" * attributes. For each exporting scope, for each given part node, add an * additional "shady-part" attribute value with format e.g. - * "parent-scope:grand-parent-scope:aliased-part-name". + * "grand-parent-scope:parent-scope:aliased-part-name". * * [3] If any of these ancestor scopes provide a ::part style rule to any of * the given parts, and if any of those rules consume a CSS custom property,