Diff
Modified: trunk/LayoutTests/imported/w3c/ChangeLog (250711 => 250712)
--- trunk/LayoutTests/imported/w3c/ChangeLog 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/LayoutTests/imported/w3c/ChangeLog 2019-10-04 08:33:05 UTC (rev 250712)
@@ -1,3 +1,18 @@
+2019-10-04 Antti Koivisto <an...@apple.com>
+
+ [CSS Shadow Parts] Support 'exportparts' attribute
+ https://bugs.webkit.org/show_bug.cgi?id=202520
+
+ Reviewed by Ryosuke Niwa.
+
+ * web-platform-tests/css/css-shadow-parts/double-forward-expected.txt:
+ * web-platform-tests/css/css-shadow-parts/invalidation-change-exportparts-forward-expected.txt:
+ * web-platform-tests/css/css-shadow-parts/invalidation-change-part-name-forward-expected.txt:
+ * web-platform-tests/css/css-shadow-parts/invalidation-complex-selector-forward-expected.txt:
+ * web-platform-tests/css/css-shadow-parts/precedence-part-vs-part-expected.txt:
+ * web-platform-tests/css/css-shadow-parts/simple-forward-expected.txt:
+ * web-platform-tests/css/css-shadow-parts/simple-forward-shorthand-expected.txt:
+
2019-10-04 Ryosuke Niwa <rn...@webkit.org>
Radio button groups are not scoped by shadow boundaries
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/double-forward-expected.txt (250711 => 250712)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/double-forward-expected.txt 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/double-forward-expected.txt 2019-10-04 08:33:05 UTC (rev 250712)
@@ -1,4 +1,4 @@
The following text should be green:
-FAIL Part in inner host is forwarded through the middle host for styling by document style sheet assert_equals: expected "rgb(0, 128, 0)" but got "rgb(255, 0, 0)"
+PASS Part in inner host is forwarded through the middle host for styling by document style sheet
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/invalidation-change-exportparts-forward-expected.txt (250711 => 250712)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/invalidation-change-exportparts-forward-expected.txt 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/invalidation-change-exportparts-forward-expected.txt 2019-10-04 08:33:05 UTC (rev 250712)
@@ -1,4 +1,4 @@
The following text should be green:
-FAIL Part in selected host changed color assert_not_equals: got disallowed value "rgb(0, 128, 0)"
+FAIL Part in selected host changed color assert_not_equals: got disallowed value "rgb(255, 0, 0)"
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/invalidation-change-part-name-forward-expected.txt (250711 => 250712)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/invalidation-change-part-name-forward-expected.txt 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/invalidation-change-part-name-forward-expected.txt 2019-10-04 08:33:05 UTC (rev 250712)
@@ -1,4 +1,4 @@
The following text should be green:
-FAIL Part in selected host changed color assert_not_equals: got disallowed value "rgb(0, 128, 0)"
+FAIL Part in selected host changed color assert_not_equals: got disallowed value "rgb(255, 0, 0)"
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/invalidation-complex-selector-forward-expected.txt (250711 => 250712)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/invalidation-complex-selector-forward-expected.txt 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/invalidation-complex-selector-forward-expected.txt 2019-10-04 08:33:05 UTC (rev 250712)
@@ -1,4 +1,4 @@
The following text should be green:
-FAIL Part in selected host changed color assert_not_equals: got disallowed value "rgb(0, 128, 0)"
+PASS Part in selected host changed color
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/precedence-part-vs-part-expected.txt (250711 => 250712)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/precedence-part-vs-part-expected.txt 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/precedence-part-vs-part-expected.txt 2019-10-04 08:33:05 UTC (rev 250712)
@@ -1,4 +1,4 @@
The following text should be green:
-FAIL Style from document overrides style from outer CE assert_equals: expected "rgb(0, 128, 0)" but got "rgb(255, 0, 0)"
+PASS Style from document overrides style from outer CE
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/simple-forward-expected.txt (250711 => 250712)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/simple-forward-expected.txt 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/simple-forward-expected.txt 2019-10-04 08:33:05 UTC (rev 250712)
@@ -1,4 +1,4 @@
The following text should be green:
-FAIL Part in inner host is forwarded for styling by document style sheet assert_equals: expected "rgb(0, 128, 0)" but got "rgb(255, 0, 0)"
+PASS Part in inner host is forwarded for styling by document style sheet
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/simple-forward-shorthand-expected.txt (250711 => 250712)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/simple-forward-shorthand-expected.txt 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-shadow-parts/simple-forward-shorthand-expected.txt 2019-10-04 08:33:05 UTC (rev 250712)
@@ -1,4 +1,4 @@
The following text should be green:
-FAIL Part in inner host is forwarded, under the same name, for styling by document style sheet assert_equals: expected "rgb(0, 128, 0)" but got "rgb(255, 0, 0)"
+PASS Part in inner host is forwarded, under the same name, for styling by document style sheet
Modified: trunk/Source/WebCore/ChangeLog (250711 => 250712)
--- trunk/Source/WebCore/ChangeLog 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/Source/WebCore/ChangeLog 2019-10-04 08:33:05 UTC (rev 250712)
@@ -1,3 +1,45 @@
+2019-10-04 Antti Koivisto <an...@apple.com>
+
+ [CSS Shadow Parts] Support 'exportparts' attribute
+ https://bugs.webkit.org/show_bug.cgi?id=202520
+
+ Reviewed by Ryosuke Niwa.
+
+ Support 'exportparts' attribute for exporting part mappings from subcomponents.
+
+ * css/ElementRuleCollector.cpp:
+ (WebCore::ElementRuleCollector::matchAuthorRules):
+ (WebCore::ElementRuleCollector::matchPartPseudoElementRules):
+
+ Recurse to containing scopes to collect part rules if there are exported mappings.
+
+ (WebCore::ElementRuleCollector::ruleMatches):
+ * css/ElementRuleCollector.h:
+ * css/SelectorChecker.cpp:
+ (WebCore::SelectorChecker::matchRecursively const):
+
+ Make ShadowDescendant fake combinator skip directly to the scope where the part rules are coming from.
+
+ (WebCore::SelectorChecker::checkOne const):
+
+ Resolve names via mappings if needed.
+
+ * css/SelectorChecker.h:
+ * dom/Element.cpp:
+ (WebCore::Element::attributeChanged):
+
+ Invalidate mappings as needed.
+
+ * dom/ShadowRoot.cpp:
+ (WebCore::parsePartMappings):
+
+ Parse the mappings microsyntax.
+
+ (WebCore::ShadowRoot::partMappings const):
+ (WebCore::ShadowRoot::invalidatePartMappings):
+ * dom/ShadowRoot.h:
+ * html/HTMLAttributeNames.in:
+
2019-10-04 Ryosuke Niwa <rn...@webkit.org>
A newly inserted element doesn't get assigned to a named slot if slot assignments had already happened
Modified: trunk/Source/WebCore/css/ElementRuleCollector.cpp (250711 => 250712)
--- trunk/Source/WebCore/css/ElementRuleCollector.cpp 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/Source/WebCore/css/ElementRuleCollector.cpp 2019-10-04 08:33:05 UTC (rev 250712)
@@ -205,7 +205,7 @@
matchAuthorShadowPseudoElementRules(includeEmptyRules, ruleRange);
if (!m_element.partNames().isEmpty())
- matchPartPseudoElementRules(includeEmptyRules, ruleRange);
+ matchPartPseudoElementRules(*m_element.containingShadowRoot(), includeEmptyRules, ruleRange);
}
sortAndTransferMatchedRules();
@@ -263,14 +263,26 @@
}
}
-void ElementRuleCollector::matchPartPseudoElementRules(bool includeEmptyRules, StyleResolver::RuleRange& ruleRange)
+void ElementRuleCollector::matchPartPseudoElementRules(const ShadowRoot& containingShadowRoot, bool includeEmptyRules, StyleResolver::RuleRange& ruleRange)
{
ASSERT(m_element.isInShadowTree());
- auto& shadowRoot = *m_element.containingShadowRoot();
- auto& hostAuthorRules = Style::Scope::forNode(*shadowRoot.host()).resolver().ruleSets().authorStyle();
+ ASSERT(!m_element.partNames().isEmpty());
- MatchRequest hostAuthorRequest { &hostAuthorRules, includeEmptyRules, Style::ScopeOrdinal::ContainingHost };
- collectMatchingRulesForList(&hostAuthorRules.partPseudoElementRules(), hostAuthorRequest, ruleRange);
+ auto& shadowHost = *containingShadowRoot.host();
+ {
+ SetForScope<const Element*> partMatchingScope(m_shadowHostInPartRuleScope, &shadowHost);
+
+ auto& hostAuthorRules = Style::Scope::forNode(shadowHost).resolver().ruleSets().authorStyle();
+ MatchRequest hostAuthorRequest { &hostAuthorRules, includeEmptyRules, Style::ScopeOrdinal::ContainingHost };
+ collectMatchingRulesForList(&hostAuthorRules.partPseudoElementRules(), hostAuthorRequest, ruleRange);
+ }
+
+ // Element may be exposed to styling from enclosing scopes via exportparts attributes.
+ if (containingShadowRoot.partMappings().isEmpty())
+ return;
+
+ if (auto* parentShadowRoot = shadowHost.containingShadowRoot())
+ matchPartPseudoElementRules(*parentShadowRoot, includeEmptyRules, ruleRange);
}
void ElementRuleCollector::collectMatchingShadowPseudoElementRules(const MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange)
@@ -428,6 +440,7 @@
context.scrollbar = m_pseudoStyleRequest.scrollbar;
context.scrollbarPart = m_pseudoStyleRequest.scrollbarPart;
context.isMatchingHostPseudoClass = m_isMatchingHostPseudoClass;
+ context.shadowHostInPartRuleScope = m_shadowHostInPartRuleScope;
bool selectorMatches;
#if ENABLE(CSS_SELECTOR_JIT)
Modified: trunk/Source/WebCore/css/ElementRuleCollector.h (250711 => 250712)
--- trunk/Source/WebCore/css/ElementRuleCollector.h 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/Source/WebCore/css/ElementRuleCollector.h 2019-10-04 08:33:05 UTC (rev 250712)
@@ -75,7 +75,7 @@
void matchAuthorShadowPseudoElementRules(bool includeEmptyRules, StyleResolver::RuleRange&);
void matchHostPseudoClassRules(bool includeEmptyRules, StyleResolver::RuleRange&);
void matchSlottedPseudoElementRules(bool includeEmptyRules, StyleResolver::RuleRange&);
- void matchPartPseudoElementRules(bool includeEmptyRules, StyleResolver::RuleRange&);
+ void matchPartPseudoElementRules(const ShadowRoot& containingShadowRoot, bool includeEmptyRules, StyleResolver::RuleRange&);
void collectMatchingShadowPseudoElementRules(const MatchRequest&, StyleResolver::RuleRange&);
std::unique_ptr<RuleSet::RuleDataVector> collectSlottedPseudoElementRulesForSlot(bool includeEmptyRules);
@@ -100,6 +100,7 @@
SelectorChecker::Mode m_mode { SelectorChecker::Mode::ResolvingStyle };
bool m_isMatchingSlottedPseudoElements { false };
bool m_isMatchingHostPseudoClass { false };
+ const Element* m_shadowHostInPartRuleScope { nullptr };
Vector<std::unique_ptr<RuleSet::RuleDataVector>> m_keepAliveSlottedPseudoElementRules;
Vector<MatchedRule, 64> m_matchedRules;
Modified: trunk/Source/WebCore/css/SelectorChecker.cpp (250711 => 250712)
--- trunk/Source/WebCore/css/SelectorChecker.cpp 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/Source/WebCore/css/SelectorChecker.cpp 2019-10-04 08:33:05 UTC (rev 250712)
@@ -429,10 +429,11 @@
}
case CSSSelector::ShadowDescendant:
{
- Element* shadowHostNode = context.element->shadowHost();
- if (!shadowHostNode)
+ // When matching foo::part(bar) we skip directly to the tree of element 'foo'.
+ auto* shadowHost = checkingContext.shadowHostInPartRuleScope ? checkingContext.shadowHostInPartRuleScope : context.element->shadowHost();
+ if (!shadowHost)
return MatchResult::fails(Match::SelectorFailsCompletely);
- nextContext.element = shadowHostNode;
+ nextContext.element = shadowHost;
nextContext.firstSelectorOfTheFragment = nextContext.selector;
nextContext.isSubjectOrAdjacentElement = false;
PseudoIdSet ignoreDynamicPseudo;
@@ -1149,13 +1150,32 @@
ASSERT(checkingContext.resolvingMode == Mode::CollectingRules);
return is<HTMLSlotElement>(element);
- case CSSSelector::PseudoElementPart:
+ case CSSSelector::PseudoElementPart: {
+ auto translatePartNameToRuleScope = [&](AtomString partName) {
+ for (auto* shadowRoot = element.containingShadowRoot(); shadowRoot; shadowRoot = shadowRoot->host()->containingShadowRoot()) {
+ // Apply mappings up to the scope the rules are coming from.
+ if (shadowRoot->host() == checkingContext.shadowHostInPartRuleScope)
+ break;
+ partName = shadowRoot->partMappings().get(partName);
+ if (partName.isEmpty())
+ return AtomString();
+ }
+ return partName;
+ };
+
+ Vector<AtomString, 4> translatedPartNames;
+ for (unsigned i = 0; i < element.partNames().size(); ++i) {
+ auto translatedPartName = translatePartNameToRuleScope(element.partNames()[i]);
+ if (!translatedPartName.isEmpty())
+ translatedPartNames.append(translatedPartName);
+ }
+
for (auto& part : *selector.argumentList()) {
- if (!element.partNames().contains(part))
+ if (!translatedPartNames.contains(part))
return false;
}
return true;
-
+ }
default:
return true;
}
Modified: trunk/Source/WebCore/css/SelectorChecker.h (250711 => 250712)
--- trunk/Source/WebCore/css/SelectorChecker.h 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/Source/WebCore/css/SelectorChecker.h 2019-10-04 08:33:05 UTC (rev 250712)
@@ -84,6 +84,7 @@
ScrollbarPart scrollbarPart { NoPart };
const ContainerNode* scope { nullptr };
bool isMatchingHostPseudoClass { false };
+ const Element* shadowHostInPartRuleScope { nullptr };
// FIXME: It would be nicer to have a separate object for return values. This requires some more work in the selector compiler.
Style::Relations styleRelations;
Modified: trunk/Source/WebCore/dom/Element.cpp (250711 => 250712)
--- trunk/Source/WebCore/dom/Element.cpp 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/Source/WebCore/dom/Element.cpp 2019-10-04 08:33:05 UTC (rev 250712)
@@ -1719,8 +1719,6 @@
document().invalidateAccessKeyCache();
else if (name == HTMLNames::classAttr)
classAttributeChanged(newValue);
- else if (name == HTMLNames::partAttr)
- partAttributeChanged(newValue);
else if (name == HTMLNames::idAttr) {
AtomString oldId = elementData()->idForStyleResolution();
AtomString newId = makeIdForStyleResolution(newValue, document().inQuirksMode());
@@ -1743,6 +1741,11 @@
if (auto* shadowRoot = parent->shadowRoot())
shadowRoot->hostChildElementDidChangeSlotAttribute(*this, oldValue, newValue);
}
+ } else if (name == HTMLNames::partAttr)
+ partAttributeChanged(newValue);
+ else if (name == HTMLNames::exportpartsAttr) {
+ if (auto* shadowRoot = this->shadowRoot())
+ shadowRoot->invalidatePartMappings();
}
}
Modified: trunk/Source/WebCore/dom/ShadowRoot.cpp (250711 => 250712)
--- trunk/Source/WebCore/dom/ShadowRoot.cpp 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/Source/WebCore/dom/ShadowRoot.cpp 2019-10-04 08:33:05 UTC (rev 250712)
@@ -30,6 +30,7 @@
#include "CSSStyleSheet.h"
#include "ElementTraversal.h"
+#include "HTMLParserIdioms.h"
#include "HTMLSlotElement.h"
#include "RenderElement.h"
#include "RuntimeEnabledFeatures.h"
@@ -50,6 +51,7 @@
void* styleSheetList;
void* host;
void* slotAssignment;
+ Optional<HashMap<AtomString, AtomString>> partMappings;
};
COMPILE_ASSERT(sizeof(ShadowRoot) == sizeof(SameSizeAsShadowRoot), shadowroot_should_stay_small);
@@ -252,6 +254,92 @@
return m_slotAssignment->assignedNodesForSlot(slot, *this);
}
+static Optional<std::pair<AtomString, AtomString>> parsePartMapping(StringView mappingString)
+{
+ const auto end = mappingString.length();
+
+ auto skipWhitespace = [&] (auto position) {
+ while (position < end && isHTMLSpace(mappingString[position]))
+ ++position;
+ return position;
+ };
+
+ auto collectValue = [&] (auto position) {
+ while (position < end && (!isHTMLSpace(mappingString[position]) && mappingString[position] != ':'))
+ ++position;
+ return position;
+ };
+
+ size_t begin = 0;
+ begin = skipWhitespace(begin);
+
+ auto firstPartEnd = collectValue(begin);
+ if (firstPartEnd == begin)
+ return { };
+
+ auto firstPart = mappingString.substring(begin, firstPartEnd - begin).toAtomString();
+
+ begin = skipWhitespace(firstPartEnd);
+ if (begin == end)
+ return std::make_pair(firstPart, firstPart);
+
+ if (mappingString[begin] != ':')
+ return { };
+
+ begin = skipWhitespace(begin + 1);
+
+ auto secondPartEnd = collectValue(begin);
+ if (secondPartEnd == begin)
+ return { };
+
+ auto secondPart = mappingString.substring(begin, secondPartEnd - begin).toAtomString();
+
+ begin = skipWhitespace(secondPartEnd);
+ if (begin != end)
+ return { };
+
+ return std::make_pair(firstPart, secondPart);
+}
+
+static void parsePartMappingsList(HashMap<AtomString, AtomString>& mappings, StringView mappingsListString)
+{
+ const auto end = mappingsListString.length();
+
+ size_t begin = 0;
+ while (begin < end) {
+ size_t mappingEnd = begin;
+ while (mappingEnd < end && mappingsListString[mappingEnd] != ',')
+ ++mappingEnd;
+
+ auto result = parsePartMapping(mappingsListString.substring(begin, mappingEnd - begin));
+ if (result)
+ mappings.add(result->first, result->second);
+
+ if (mappingEnd == end)
+ break;
+
+ begin = mappingEnd + 1;
+ }
+}
+
+const HashMap<AtomString, AtomString>& ShadowRoot::partMappings() const
+{
+ if (!m_partMappings) {
+ m_partMappings = HashMap<AtomString, AtomString>();
+
+ auto exportpartsValue = host()->attributeWithoutSynchronization(HTMLNames::exportpartsAttr);
+ if (!exportpartsValue.isEmpty() && RuntimeEnabledFeatures::sharedFeatures().cssShadowPartsEnabled())
+ parsePartMappingsList(*m_partMappings, exportpartsValue);
+ }
+
+ return *m_partMappings;
+}
+
+void ShadowRoot::invalidatePartMappings()
+{
+ m_partMappings = { };
+}
+
Vector<ShadowRoot*> assignedShadowRootsIfSlotted(const Node& node)
{
Vector<ShadowRoot*> result;
Modified: trunk/Source/WebCore/dom/ShadowRoot.h (250711 => 250712)
--- trunk/Source/WebCore/dom/ShadowRoot.h 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/Source/WebCore/dom/ShadowRoot.h 2019-10-04 08:33:05 UTC (rev 250712)
@@ -30,6 +30,7 @@
#include "DocumentFragment.h"
#include "Element.h"
#include "ShadowRootMode.h"
+#include <wtf/HashMap.h>
namespace WebCore {
@@ -92,6 +93,9 @@
void moveShadowRootToNewParentScope(TreeScope&, Document&);
void moveShadowRootToNewDocument(Document&);
+ const HashMap<AtomString, AtomString>& partMappings() const;
+ void invalidatePartMappings();
+
protected:
ShadowRoot(Document&, ShadowRootMode);
@@ -116,6 +120,7 @@
std::unique_ptr<Style::Scope> m_styleScope;
std::unique_ptr<SlotAssignment> m_slotAssignment;
+ mutable Optional<HashMap<AtomString, AtomString>> m_partMappings;
};
inline Element* ShadowRoot::activeElement() const
Modified: trunk/Source/WebCore/html/HTMLAttributeNames.in (250711 => 250712)
--- trunk/Source/WebCore/html/HTMLAttributeNames.in 2019-10-04 08:14:18 UTC (rev 250711)
+++ trunk/Source/WebCore/html/HTMLAttributeNames.in 2019-10-04 08:33:05 UTC (rev 250712)
@@ -123,6 +123,7 @@
end
event
expanded
+exportparts
face
filename
focused