Skip to content
Permalink
Browse files
[CSS Cascade Layers] Compute order correctly for late added sublayers
https://bugs.webkit.org/show_bug.cgi?id=229666

Reviewed by Simon Fraser.

LayoutTests/imported/w3c:

* web-platform-tests/css/css-cascade/layer-basic-expected.txt:

Source/WebCore:

In cases like

    @layer a.b { ... }
    @layer c { ... }
    @layer a.d { ... }

'c' should have higher priority than 'a.d'.

Replace the per-RuleData layer order vector with references (indexes) to layer entry vector.
These entries have order field that can be recomputed.

* style/RuleSet.cpp:
(WebCore::Style::RuleSet::addRule):
(WebCore::Style::RuleSet::Builder::addStyleRule):
(WebCore::Style::RuleSet::Builder::pushCascadeLayer):

Instead of computing order directly we just give each layer an identifier and add an entry for it to the layer vector.

(WebCore::Style::RuleSet::Builder::popCascadeLayer):
(WebCore::Style::RuleSet::Builder::~Builder):

Compute layer order after building for all layers.

(WebCore::Style::RuleSet::shrinkToFit):
* style/RuleSet.h:
(WebCore::Style::RuleSet::cascadeLayerForIdentifier):
(WebCore::Style::RuleSet::cascadeLayerForIdentifier const):
(WebCore::Style::RuleSet::cascadeLayerOrderFor const):


Canonical link: https://commits.webkit.org/241135@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@281798 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
anttijk committed Aug 31, 2021
1 parent f6f4945 commit 633f9c8cae851a00e25f6d79d5739c2f1907eaf3
Showing 5 changed files with 137 additions and 27 deletions.
@@ -1,3 +1,12 @@
2021-08-31 Antti Koivisto <antti@apple.com>

[CSS Cascade Layers] Compute order correctly for late added sublayers
https://bugs.webkit.org/show_bug.cgi?id=229666

Reviewed by Simon Fraser.

* web-platform-tests/css/css-cascade/layer-basic-expected.txt:

2021-08-31 Youenn Fablet <youenn@apple.com>

Add support for RTCIceTransport
@@ -17,20 +17,20 @@ PASS B6 Named layers
PASS B7 Named layers
PASS B8 Named layers
PASS B9 Named layers
FAIL B10 Named layers assert_equals: B10 Named layers, target 'first' expected "rgb(0, 128, 0)" but got "rgb(255, 0, 0)"
PASS B10 Named layers
PASS C1 Named layers shorthand
PASS C2 Named layers shorthand
FAIL C3 Named layers shorthand assert_equals: C3 Named layers shorthand, target 'first' expected "rgb(0, 128, 0)" but got "rgb(255, 0, 0)"
PASS C3 Named layers shorthand
PASS C4 Named layers shorthand
FAIL C5 Named layers shorthand assert_equals: C5 Named layers shorthand, target 'first' expected "rgb(0, 128, 0)" but got "rgb(255, 0, 0)"
PASS C5 Named layers shorthand
PASS D1 Mixed named and anonymous layers
PASS D2 Mixed named and anonymous layers
PASS D3 Mixed named and anonymous layers
FAIL D4 Mixed named and anonymous layers assert_equals: D4 Mixed named and anonymous layers, target 'first' expected "rgb(0, 128, 0)" but got "rgb(255, 0, 0)"
PASS D4 Mixed named and anonymous layers
PASS D5 Mixed named and anonymous layers
PASS E1 Statement syntax
PASS E2 Statement syntax
PASS E3 Statement syntax
PASS E4 Statement syntax
FAIL E5 Statement syntax assert_equals: E5 Statement syntax, target 'first' expected "rgb(0, 128, 0)" but got "rgb(255, 0, 0)"
PASS E5 Statement syntax

@@ -1,3 +1,39 @@
2021-08-31 Antti Koivisto <antti@apple.com>

[CSS Cascade Layers] Compute order correctly for late added sublayers
https://bugs.webkit.org/show_bug.cgi?id=229666

Reviewed by Simon Fraser.

In cases like

@layer a.b { ... }
@layer c { ... }
@layer a.d { ... }

'c' should have higher priority than 'a.d'.

Replace the per-RuleData layer order vector with references (indexes) to layer entry vector.
These entries have order field that can be recomputed.

* style/RuleSet.cpp:
(WebCore::Style::RuleSet::addRule):
(WebCore::Style::RuleSet::Builder::addStyleRule):
(WebCore::Style::RuleSet::Builder::pushCascadeLayer):

Instead of computing order directly we just give each layer an identifier and add an entry for it to the layer vector.

(WebCore::Style::RuleSet::Builder::popCascadeLayer):
(WebCore::Style::RuleSet::Builder::~Builder):

Compute layer order after building for all layers.

(WebCore::Style::RuleSet::shrinkToFit):
* style/RuleSet.h:
(WebCore::Style::RuleSet::cascadeLayerForIdentifier):
(WebCore::Style::RuleSet::cascadeLayerForIdentifier const):
(WebCore::Style::RuleSet::cascadeLayerOrderFor const):

2021-08-31 Philippe Normand <pnormand@igalia.com>

Unreviewed, OpenXR build warning fix.
@@ -83,15 +83,15 @@ static bool isHostSelectorMatchingInShadowTree(const CSSSelector& startSelector)
return leftmostSelector->match() == CSSSelector::PseudoClass && leftmostSelector->pseudoClassType() == CSSSelector::PseudoClassHost;
}

void RuleSet::addRule(const StyleRule& rule, unsigned selectorIndex, unsigned selectorListIndex, unsigned cascadeLayerOrder, MediaQueryCollector* mediaQueryCollector)
void RuleSet::addRule(const StyleRule& rule, unsigned selectorIndex, unsigned selectorListIndex, unsigned cascadeLayerIdentifier, MediaQueryCollector* mediaQueryCollector)
{
RuleData ruleData(rule, selectorIndex, selectorListIndex, m_ruleCount++);

if (cascadeLayerOrder) {
auto oldSize = m_cascadeLayerOrderForPosition.size();
m_cascadeLayerOrderForPosition.grow(m_ruleCount);
std::fill(m_cascadeLayerOrderForPosition.begin() + oldSize, m_cascadeLayerOrderForPosition.end(), 0);
m_cascadeLayerOrderForPosition.last() = cascadeLayerOrder;
if (cascadeLayerIdentifier) {
auto oldSize = m_cascadeLayerIdentifierForRulePosition.size();
m_cascadeLayerIdentifierForRulePosition.grow(m_ruleCount);
std::fill(m_cascadeLayerIdentifierForRulePosition.begin() + oldSize, m_cascadeLayerIdentifierForRulePosition.end(), 0);
m_cascadeLayerIdentifierForRulePosition.last() = cascadeLayerIdentifier;
}

m_features.collectFeatures(ruleData);
@@ -385,20 +385,34 @@ void RuleSet::Builder::addRulesFromSheet(const StyleSheetContents& sheet)
addChildRules(sheet.childRules());
}

RuleSet::Builder::~Builder()
{
if (mode == Mode::Normal && !cascadeLayerIdentifierMap.isEmpty())
updateCascadeLayerOrder();
}

void RuleSet::Builder::addStyleRule(const StyleRule& rule)
{
auto& selectorList = rule.selectorList();
if (selectorList.isEmpty())
return;
unsigned selectorListIndex = 0;
for (size_t selectorIndex = 0; selectorIndex != notFound; selectorIndex = selectorList.indexOfNextSelectorAfter(selectorIndex))
ruleSet->addRule(rule, selectorIndex, selectorListIndex++, cascadeLayerOrder, &mediaQueryCollector);
ruleSet->addRule(rule, selectorIndex, selectorListIndex++, currentCascadeLayerIdentifier, &mediaQueryCollector);
}

void RuleSet::Builder::pushCascadeLayer(const CascadeLayerName& name)
{
if (mode != Mode::Normal)
return;

if (cascadeLayerIdentifierMap.isEmpty() && !ruleSet->m_cascadeLayers.isEmpty()) {
// For incremental build, reconstruct the name->identifier map.
CascadeLayerIdentifier identifier = 0;
for (auto& layer : ruleSet->m_cascadeLayers)
cascadeLayerIdentifierMap.add(layer.resolvedName, ++identifier);
}

auto nameResolvingAnonymous = [&] {
if (name.isEmpty()) {
// Make unique name for an anonymous layer.
@@ -411,9 +425,10 @@ void RuleSet::Builder::pushCascadeLayer(const CascadeLayerName& name)
// For hierarchical names we register the containing layers individually first.
for (auto& nameSegment : nameResolvingAnonymous()) {
resolvedCascadeLayerName.append(nameSegment);
cascadeLayerOrder = ruleSet->m_cascadeLayerOrderMap.ensure(resolvedCascadeLayerName, [&] {
// FIXME: This is not correct when adding a sublayer to an already registered layer after it has gained siblings.
return ruleSet->m_cascadeLayerOrderMap.size() + 1;
currentCascadeLayerIdentifier = cascadeLayerIdentifierMap.ensure(resolvedCascadeLayerName, [&] {
// Previously unseen layer.
ruleSet->m_cascadeLayers.append({ resolvedCascadeLayerName, currentCascadeLayerIdentifier });
return ruleSet->m_cascadeLayers.size();
}).iterator->value;
}
}
@@ -422,9 +437,42 @@ void RuleSet::Builder::popCascadeLayer(const CascadeLayerName& name)
{
if (mode != Mode::Normal)
return;
auto size = name.isEmpty() ? 1 : name.size();
resolvedCascadeLayerName.shrink(resolvedCascadeLayerName.size() - size);
cascadeLayerOrder = resolvedCascadeLayerName.isEmpty() ? 0 : ruleSet->m_cascadeLayerOrderMap.get(resolvedCascadeLayerName);

for (auto size = name.isEmpty() ? 1 : name.size(); size--;) {
resolvedCascadeLayerName.removeLast();
currentCascadeLayerIdentifier = ruleSet->cascadeLayerForIdentifier(currentCascadeLayerIdentifier).parentIdentifier;
}
}

void RuleSet::Builder::updateCascadeLayerOrder()
{
auto compare = [&](CascadeLayerIdentifier a, CascadeLayerIdentifier b) {
while (a && b) {
// Identifiers are in parse order which almost corresponds to the layer priority order.
// The only exception is when a sublayer gets added to a layer after adding other non-sublayers.
// To resolve this we need look for a shared ancestor layer.
auto aParent = ruleSet->cascadeLayerForIdentifier(a).parentIdentifier;
auto bParent = ruleSet->cascadeLayerForIdentifier(b).parentIdentifier;
if (aParent == bParent || aParent == b || bParent == a)
break;
if (aParent > bParent)
a = aParent;
else
b = bParent;
}
return a < b;
};

Vector<CascadeLayerIdentifier> orderVector;
auto layerCount = ruleSet->m_cascadeLayers.size();
orderVector.reserveInitialCapacity(layerCount);
for (CascadeLayerIdentifier identifier = 1; identifier <= layerCount; ++identifier)
orderVector.uncheckedAppend(identifier);

std::sort(orderVector.begin(), orderVector.end(), compare);

for (unsigned i = 0; i < orderVector.size(); ++i)
ruleSet->cascadeLayerForIdentifier(orderVector[i]).order = i + 1;
}

template<typename Function>
@@ -561,7 +609,8 @@ void RuleSet::shrinkToFit()

shrinkDynamicRules(m_dynamicMediaQueryRules);

m_cascadeLayerOrderForPosition.shrinkToFit();
m_cascadeLayers.shrinkToFit();
m_cascadeLayerIdentifierForRulePosition.shrinkToFit();
}

RuleSet::MediaQueryCollector::~MediaQueryCollector() = default;
@@ -150,6 +150,8 @@ class RuleSet : public RefCounted<RuleSet> {
private:
RuleSet();

using CascadeLayerIdentifier = unsigned;

struct Builder {
enum class Mode { Normal, ResolverMutationScan };

@@ -158,16 +160,20 @@ class RuleSet : public RefCounted<RuleSet> {
Style::Resolver* resolver { nullptr };
Mode mode { Mode::Normal };
CascadeLayerName resolvedCascadeLayerName { };
unsigned cascadeLayerOrder { 0 };
HashMap<CascadeLayerName, CascadeLayerIdentifier> cascadeLayerIdentifierMap { };
CascadeLayerIdentifier currentCascadeLayerIdentifier { 0 };

void addRulesFromSheet(const StyleSheetContents&);

~Builder();

private:
void addChildRules(const Vector<RefPtr<StyleRuleBase>>&);
void addStyleRule(const StyleRule&);

void pushCascadeLayer(const CascadeLayerName&);
void popCascadeLayer(const CascadeLayerName&);
void updateCascadeLayerOrder();
};

struct CollectedMediaQueryChanges {
@@ -179,6 +185,14 @@ class RuleSet : public RefCounted<RuleSet> {

template<typename Function> void traverseRuleDatas(Function&&);

struct CascadeLayer {
CascadeLayerName resolvedName;
CascadeLayerIdentifier parentIdentifier;
unsigned order { 0 };
};
CascadeLayer& cascadeLayerForIdentifier(CascadeLayerIdentifier identifier) { return m_cascadeLayers[identifier - 1]; }
const CascadeLayer& cascadeLayerForIdentifier(CascadeLayerIdentifier identifier) const { return m_cascadeLayers[identifier - 1]; }

AtomRuleMap m_idRules;
AtomRuleMap m_classRules;
AtomRuleMap m_tagLocalNameRules;
@@ -199,9 +213,9 @@ class RuleSet : public RefCounted<RuleSet> {
HashMap<Vector<size_t>, Ref<const RuleSet>> m_mediaQueryInvalidationRuleSetCache;
unsigned m_ruleCount { 0 };

HashMap<CascadeLayerName, unsigned> m_cascadeLayerOrderMap;
// This is a side vector to hold layer order without bloating RuleData.
Vector<unsigned> m_cascadeLayerOrderForPosition;
Vector<CascadeLayer> m_cascadeLayers;
// This is a side vector to hold layer identifiers without bloating RuleData.
Vector<CascadeLayerIdentifier> m_cascadeLayerIdentifierForRulePosition;

bool m_hasHostPseudoClassRulesMatchingInShadowTree { false };
bool m_autoShrinkToFitEnabled { true };
@@ -220,11 +234,13 @@ inline const RuleSet::RuleDataVector* RuleSet::tagRules(const AtomString& key, b

inline unsigned RuleSet::cascadeLayerOrderFor(const RuleData& ruleData) const
{
if (m_cascadeLayerOrderForPosition.size() > ruleData.position())
return m_cascadeLayerOrderForPosition[ruleData.position()];
return 0;
if (m_cascadeLayerIdentifierForRulePosition.size() <= ruleData.position())
return 0;
auto identifier = m_cascadeLayerIdentifierForRulePosition[ruleData.position()];
if (!identifier)
return 0;
return cascadeLayerForIdentifier(identifier).order;
}


} // namespace Style
} // namespace WebCore

0 comments on commit 633f9c8

Please sign in to comment.