Changeset 290205 in webkit


Ignore:
Timestamp:
Feb 19, 2022 6:05:53 AM (5 months ago)
Author:
Antti Koivisto
Message:

[CSS Container Queries] Match container queries correctly in non-rendered subtrees
https://bugs.webkit.org/show_bug.cgi?id=236840

Reviewed by Dean Jackson.

LayoutTests/imported/w3c:

  • web-platform-tests/css/css-contain/container-queries/backdrop-invalidation-expected.txt:
  • web-platform-tests/css/css-contain/container-queries/display-contents-expected.txt:
  • web-platform-tests/css/css-contain/container-queries/display-none-expected.txt:
  • web-platform-tests/css/css-contain/container-queries/pseudo-elements-001-expected.txt:
  • web-platform-tests/css/css-contain/container-queries/pseudo-elements-003.tentative-expected.txt:
  • web-platform-tests/css/css-contain/container-queries/size-container-no-principal-box-expected.txt:
  • web-platform-tests/css/css-contain/container-queries/top-layer-dialog-container-expected.txt:
  • web-platform-tests/css/css-contain/container-queries/top-layer-dialog-expected.txt:
  • web-platform-tests/css/css-contain/container-queries/top-layer-nested-dialog-expected.txt:

Source/WebCore:

"If the query container does not have a principal box ... then the result of evaluating the size feature is unknown."

https://drafts.csswg.org/css-contain-3/#size-container

  • rendering/updating/RenderTreeBuilderFirstLetter.cpp:

(WebCore::styleForFirstLetter):

Remove the assert, this issue is tested by marked-as-failure
imported/w3c/web-platform-tests/css/css-contain/container-queries/pseudo-elements-002.tentative.html

  • style/ContainerQueryEvaluator.cpp:

(WebCore::Style::ContainerQueryEvaluator::ContainerQueryEvaluator):
(WebCore::Style::ContainerQueryEvaluator::evaluate const):
(WebCore::Style::ContainerQueryEvaluator::resolveContainer const):

Factor into a function.
If we don't have selector matching state (like we do during style resolution) then look up containers from DOM.
An element without a renderer may still be a valid container (size queries againt it evaluate to unknown).

(WebCore::Style::ContainerQueryEvaluator::evaluateSizeFeature const):

  • style/ContainerQueryEvaluator.h:
  • style/ElementRuleCollector.cpp:

(WebCore::Style::ElementRuleCollector::containerQueryMatches):

Evaluate to unknown if we don't have a principal box.

Location:
trunk
Files:
15 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/imported/w3c/ChangeLog

    r290201 r290205  
     12022-02-19  Antti Koivisto  <antti@apple.com>
     2
     3        [CSS Container Queries] Match container queries correctly in non-rendered subtrees
     4        https://bugs.webkit.org/show_bug.cgi?id=236840
     5
     6        Reviewed by Dean Jackson.
     7
     8        * web-platform-tests/css/css-contain/container-queries/backdrop-invalidation-expected.txt:
     9        * web-platform-tests/css/css-contain/container-queries/display-contents-expected.txt:
     10        * web-platform-tests/css/css-contain/container-queries/display-none-expected.txt:
     11        * web-platform-tests/css/css-contain/container-queries/pseudo-elements-001-expected.txt:
     12        * web-platform-tests/css/css-contain/container-queries/pseudo-elements-003.tentative-expected.txt:
     13        * web-platform-tests/css/css-contain/container-queries/size-container-no-principal-box-expected.txt:
     14        * web-platform-tests/css/css-contain/container-queries/top-layer-dialog-container-expected.txt:
     15        * web-platform-tests/css/css-contain/container-queries/top-layer-dialog-expected.txt:
     16        * web-platform-tests/css/css-contain/container-queries/top-layer-nested-dialog-expected.txt:
     17
    1182022-02-19  Antoine Quint  <graouts@webkit.org>
    219
  • trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-contain/container-queries/backdrop-invalidation-expected.txt

    r288786 r290205  
    11
    2 FAIL Pseudo-element ::backdrop responds to container size changes assert_equals: expected "rgb(0, 0, 0)" but got "rgb(0, 128, 0)"
     2FAIL Pseudo-element ::backdrop responds to container size changes assert_equals: expected "rgb(0, 128, 0)" but got "rgb(0, 0, 0)"
    33
  • trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-contain/container-queries/display-contents-expected.txt

    r289222 r290205  
    11
    2 FAIL getComputedStyle when container is display:contents assert_equals: expected "" but got "50"
    3 FAIL getComputedStyle when container becomes display:contents assert_equals: expected "" but got "50"
    4 FAIL getComputedStyle when intermediate container becomes display:contents assert_equals: expected "" but got "50"
     2PASS getComputedStyle when container is display:contents
     3PASS getComputedStyle when container becomes display:contents
     4PASS getComputedStyle when intermediate container becomes display:contents
    55
  • trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-contain/container-queries/display-none-expected.txt

    r289222 r290205  
    11
    2 FAIL getComputedStyle when element is display:none assert_equals: expected "30" but got "50"
    3 FAIL getComputedStyle when parent is display:none assert_equals: expected "30" but got "50"
    4 FAIL getComputedStyle when ancestor is display:none assert_equals: expected "30" but got "50"
    5 FAIL getComputedStyle when container is display:none assert_equals: expected "" but got "50"
    6 FAIL getComputedStyle when element in nested container is display:none assert_equals: expected "30" but got "50"
    7 FAIL getComputedStyle when inner container is display:none assert_equals: expected "" but got "50"
    8 FAIL getComputedStyle when intermediate ancestor is display:none assert_equals: expected "" but got "50"
    9 FAIL getComputedStyle when outer container is display:none assert_equals: expected "" but got "50"
    10 FAIL getComputedStyle when element becomes display:none assert_equals: expected "30" but got "50"
    11 FAIL getComputedStyle when parent becomes display:none assert_equals: expected "30" but got "50"
    12 FAIL getComputedStyle when ancestor becomes display:none assert_equals: expected "30" but got "50"
    13 FAIL getComputedStyle when container becomes display:none assert_equals: expected "" but got "50"
    14 FAIL getComputedStyle when intermediate container becomes display:none assert_equals: expected "" but got "50"
     2PASS getComputedStyle when element is display:none
     3PASS getComputedStyle when parent is display:none
     4PASS getComputedStyle when ancestor is display:none
     5PASS getComputedStyle when container is display:none
     6PASS getComputedStyle when element in nested container is display:none
     7PASS getComputedStyle when inner container is display:none
     8PASS getComputedStyle when intermediate ancestor is display:none
     9PASS getComputedStyle when outer container is display:none
     10PASS getComputedStyle when element becomes display:none
     11PASS getComputedStyle when parent becomes display:none
     12PASS getComputedStyle when ancestor becomes display:none
     13PASS getComputedStyle when container becomes display:none
     14PASS getComputedStyle when intermediate container becomes display:none
    1515PASS getComputedStyle when ::before is display:none
    16 FAIL getComputedStyle when originating element is display:none assert_equals: expected "30" but got "50"
    17 FAIL getComputedStyle on ::before when ancestor element is display:none assert_equals: expected "30" but got "50"
    18 FAIL getComputedStyle on ::before when container is display:none assert_equals: expected "" but got "50"
     16PASS getComputedStyle when originating element is display:none
     17PASS getComputedStyle on ::before when ancestor element is display:none
     18PASS getComputedStyle on ::before when container is display:none
    1919
  • trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-contain/container-queries/pseudo-elements-001-expected.txt

    r288786 r290205  
    22One
    33
    4 FAIL Pseudo-elements ::before and ::after respond to container size changes assert_equals: expected "none" but got "\"before\""
    5 FAIL Pseudo-element ::marker responds to container size changes assert_equals: expected "rgb(0, 0, 0)" but got "rgb(0, 128, 0)"
     4FAIL Pseudo-elements ::before and ::after respond to container size changes assert_equals: expected "\"before\"" but got "none"
     5PASS Pseudo-element ::marker responds to container size changes
    66
  • trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-contain/container-queries/pseudo-elements-003.tentative-expected.txt

    r288786 r290205  
    99PASS Originating element container for outer ::first-line
    1010PASS Originating element container for outer ::first-letter
    11 FAIL Originating element container for ::backdrop assert_equals: ::backdrop not rendered expected "rgb(0, 255, 0)" but got "rgb(0, 128, 0)"
     11PASS Originating element container for ::backdrop
    1212
  • trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-contain/container-queries/size-container-no-principal-box-expected.txt

    r289222 r290205  
    11
    22PASS Check that container queries is supported
    3 FAIL (min-width: 0) does not match a container without a principal box (display:none) assert_equals: expected "rgb(0, 0, 0)" but got "rgb(255, 0, 0)"
     3PASS (min-width: 0) does not match a container without a principal box (display:none)
    44PASS (min-width: 0) does not match a container without a principal box (display:contents)
    55
  • trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-contain/container-queries/top-layer-dialog-container-expected.txt

    r288786 r290205  
    11
    2 FAIL #dialog initially sized by #containing-block assert_equals: expected "rgb(255, 0, 0)" but got "rgb(0, 128, 0)"
     2PASS #dialog initially sized by #containing-block
    33PASS #dialog sized by viewport
    44
  • trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-contain/container-queries/top-layer-dialog-expected.txt

    r289222 r290205  
    11
    2 FAIL #container initially wider than 200px assert_equals: expected "rgb(255, 0, 0)" but got "rgb(0, 255, 0)"
    3 FAIL #container changed to 200px assert_equals: expected "rgb(0, 128, 0)" but got "rgb(0, 255, 0)"
     2PASS #container initially wider than 200px
     3PASS #container changed to 200px
    44PASS Modal dialog still has parent as query container while in top layer
    55PASS Container changes width while dialog is in top layer
  • trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-contain/container-queries/top-layer-nested-dialog-expected.txt

    r289222 r290205  
    11
    2 FAIL Dialogs initially not matching for container queries assert_equals: expected "rgb(255, 0, 0)" but got "rgb(0, 255, 0)"
     2PASS Dialogs initially not matching for container queries
    33PASS Dialogs still not matching after showModal
    44PASS @container queries start matching
  • trunk/Source/WebCore/ChangeLog

    r290204 r290205  
     12022-02-19  Antti Koivisto  <antti@apple.com>
     2
     3        [CSS Container Queries] Match container queries correctly in non-rendered subtrees
     4        https://bugs.webkit.org/show_bug.cgi?id=236840
     5
     6        Reviewed by Dean Jackson.
     7
     8        "If the query container does not have a principal box ... then the result of evaluating the size feature is unknown."
     9
     10        https://drafts.csswg.org/css-contain-3/#size-container
     11
     12        * rendering/updating/RenderTreeBuilderFirstLetter.cpp:
     13        (WebCore::styleForFirstLetter):
     14
     15        Remove the assert, this issue is tested by marked-as-failure
     16        imported/w3c/web-platform-tests/css/css-contain/container-queries/pseudo-elements-002.tentative.html
     17
     18        * style/ContainerQueryEvaluator.cpp:
     19        (WebCore::Style::ContainerQueryEvaluator::ContainerQueryEvaluator):
     20        (WebCore::Style::ContainerQueryEvaluator::evaluate const):
     21        (WebCore::Style::ContainerQueryEvaluator::resolveContainer const):
     22
     23        Factor into a function.
     24        If we don't have selector matching state (like we do during style resolution) then look up containers from DOM.
     25        An element without a renderer may still be a valid container (size queries againt it evaluate to unknown).
     26
     27        (WebCore::Style::ContainerQueryEvaluator::evaluateSizeFeature const):
     28        * style/ContainerQueryEvaluator.h:
     29        * style/ElementRuleCollector.cpp:
     30        (WebCore::Style::ElementRuleCollector::containerQueryMatches):
     31
     32        Evaluate to unknown if we don't have a principal box.
     33
    1342022-02-19  Alan Bujtas  <zalan@apple.com>
    235
  • trunk/Source/WebCore/rendering/updating/RenderTreeBuilderFirstLetter.cpp

    r288942 r290205  
    4242{
    4343    auto* containerFirstLetterStyle = firstLetterBlock.getCachedPseudoStyle(PseudoId::FirstLetter, &firstLetterContainer.firstLineStyle());
    44     // FIXME: There appears to be some path where we have a first letter renderer without first letter style.
    45     ASSERT(containerFirstLetterStyle);
     44    // FIXME: first-letter style needs to be computed eagerly.
    4645    auto firstLetterStyle = RenderStyle::clone(containerFirstLetterStyle ? *containerFirstLetterStyle : firstLetterContainer.firstLineStyle());
    4746
  • trunk/Source/WebCore/style/ContainerQueryEvaluator.cpp

    r290037 r290205  
    2929#include "CSSToLengthConversionData.h"
    3030#include "CSSValueList.h"
     31#include "ComposedTreeAncestorIterator.h"
    3132#include "Document.h"
    3233#include "MediaFeatureNames.h"
     
    3940
    4041struct ContainerQueryEvaluator::ResolvedContainer {
    41     const RenderBox& renderer;
     42    const RenderBox* renderer { nullptr };
    4243    CSSToLengthConversionData conversionData;
    4344};
    4445
    45 ContainerQueryEvaluator::ContainerQueryEvaluator(const Vector<Ref<const Element>>& containers)
    46     : m_containers(containers)
     46ContainerQueryEvaluator::ContainerQueryEvaluator(const Element& element, PseudoId pseudoId, SelectorMatchingState* selectorMatchingState)
     47    : m_element(element)
     48    , m_pseudoId(pseudoId)
     49    , m_selectorMatchingState(selectorMatchingState)
    4750{
    4851}
     
    5053bool ContainerQueryEvaluator::evaluate(const FilteredContainerQuery& filteredContainerQuery) const
    5154{
    52     if (m_containers.isEmpty())
    53         return false;
    54 
    55     auto makeResolvedContainer = [&](const RenderBox& renderer) -> ResolvedContainer {
    56         auto& view = renderer.view();
    57         return {
    58             renderer,
    59             CSSToLengthConversionData { &renderer.style(), &view.style(), nullptr, &view, 1 }
    60         };
    61     };
    62 
    63     auto resolveContainer = [&]() -> std::optional<ResolvedContainer> {
    64         for (auto& container : makeReversedRange(m_containers)) {
    65             auto* renderer = dynamicDowncast<RenderBox>(container->renderer());
    66             if (!renderer)
    67                 return { };
    68             if (filteredContainerQuery.nameFilter.isEmpty())
    69                 return makeResolvedContainer(*renderer);
    70             // FIXME: Support type filter.
    71             if (renderer->style().containerNames().contains(filteredContainerQuery.nameFilter))
    72                 return makeResolvedContainer(*renderer);
    73         }
    74         return { };
    75     };
    76 
    77     auto container = resolveContainer();
     55    auto container = resolveContainer(filteredContainerQuery);
    7856    if (!container)
    7957        return false;
     
    8159    return evaluateQuery(filteredContainerQuery.query, *container) == EvaluationResult::True;
    8260}
     61
     62auto ContainerQueryEvaluator::resolveContainer(const FilteredContainerQuery& filteredContainerQuery) const -> std::optional<ResolvedContainer>
     63{
     64    auto makeResolvedContainer = [](const Element& element) -> ResolvedContainer {
     65        auto* renderer = dynamicDowncast<RenderBox>(element.renderer());
     66        if (!renderer)
     67            return { };
     68        auto& view = renderer->view();
     69        return ResolvedContainer {
     70            renderer,
     71            CSSToLengthConversionData { &renderer->style(), &view.style(), nullptr, &view, 1 }
     72        };
     73    };
     74
     75    auto isContainerForQuery = [&](const Element& element) {
     76        auto* style = element.existingComputedStyle();
     77        if (!style)
     78            return false;
     79        if (style->containerType() == ContainerType::None)
     80            return false;
     81        if (filteredContainerQuery.nameFilter.isEmpty())
     82            return true;
     83        return style->containerNames().contains(filteredContainerQuery.nameFilter);
     84    };
     85
     86    if (m_selectorMatchingState) {
     87        for (auto& container : makeReversedRange(m_selectorMatchingState->queryContainers)) {
     88            if (isContainerForQuery(container))
     89                return makeResolvedContainer(container);
     90        }
     91        return { };
     92    }
     93
     94    if (m_pseudoId != PseudoId::None) {
     95        if (isContainerForQuery(m_element))
     96            return makeResolvedContainer(m_element);
     97    }
     98
     99    for (auto& ancestor : composedTreeAncestors(const_cast<Element&>(m_element.get()))) {
     100        if (isContainerForQuery(ancestor))
     101            return makeResolvedContainer(ancestor);
     102    }
     103    return { };
     104}
     105
    83106
    84107auto ContainerQueryEvaluator::evaluateQuery(const CQ::ContainerQuery& containerQuery, const ResolvedContainer& container) const -> EvaluationResult
     
    155178auto ContainerQueryEvaluator::evaluateSizeFeature(const CQ::SizeFeature& sizeFeature, const ResolvedContainer& container) const -> EvaluationResult
    156179{
     180    // "If the query container does not have a principal box ... then the result of evaluating the size feature is unknown."
     181    // https://drafts.csswg.org/css-contain-3/#size-container
     182    if (!container.renderer)
     183        return EvaluationResult::Unknown;
     184
     185    auto& renderer = *container.renderer;
     186
    157187    auto compare = [](CQ::ComparisonOperator op, auto left, auto right) {
    158188        switch (op) {
     
    220250    enum class Axis : uint8_t { Both, Block, Inline, Width, Height };
    221251    auto containerSupportsRequiredAxis = [&](Axis axis) {
    222         switch (container.renderer.style().containerType()) {
     252        switch (renderer.style().containerType()) {
    223253        case ContainerType::Size:
    224254            return true;
    225255        case ContainerType::InlineSize:
    226256            if (axis == Axis::Width)
    227                 return container.renderer.isHorizontalWritingMode();
     257                return renderer.isHorizontalWritingMode();
    228258            if (axis == Axis::Height)
    229                 return !container.renderer.isHorizontalWritingMode();
     259                return !renderer.isHorizontalWritingMode();
    230260            return axis == Axis::Inline;
    231261        case ContainerType::None:
     
    239269            return EvaluationResult::Unknown;
    240270
    241         return evaluateSize(container.renderer.contentWidth());
     271        return evaluateSize(renderer.contentWidth());
    242272    }
    243273
     
    246276            return EvaluationResult::Unknown;
    247277
    248         return evaluateSize(container.renderer.contentHeight());
     278        return evaluateSize(renderer.contentHeight());
    249279    }
    250280
     
    253283            return EvaluationResult::Unknown;
    254284
    255         return evaluateSize(container.renderer.contentLogicalWidth());
     285        return evaluateSize(renderer.contentLogicalWidth());
    256286    }
    257287
     
    260290            return EvaluationResult::Unknown;
    261291
    262         return evaluateSize(container.renderer.contentLogicalHeight());
     292        return evaluateSize(renderer.contentLogicalHeight());
    263293    }
    264294
     
    267297            return EvaluationResult::Unknown;
    268298
    269         auto boxRatio = container.renderer.contentWidth().toDouble() / container.renderer.contentHeight().toDouble();
     299        auto boxRatio = renderer.contentWidth().toDouble() / renderer.contentHeight().toDouble();
    270300       
    271301        if (!sizeFeature.leftComparison && !sizeFeature.rightComparison)
     
    292322        auto& value = downcast<CSSPrimitiveValue>(*sizeFeature.rightComparison->value);
    293323
    294         bool isPortrait = container.renderer.contentHeight() >= container.renderer.contentWidth();
     324        bool isPortrait = renderer.contentHeight() >= renderer.contentWidth();
    295325        if (value.valueID() == CSSValuePortrait)
    296326            return toEvaluationResult(isPortrait);
  • trunk/Source/WebCore/style/ContainerQueryEvaluator.h

    r290037 r290205  
    2626
    2727#include "ContainerQuery.h"
     28#include "SelectorMatchingState.h"
    2829#include <wtf/Ref.h>
    2930
     
    3839class ContainerQueryEvaluator {
    3940public:
    40     ContainerQueryEvaluator(const Vector<Ref<const Element>>& containers);
     41    ContainerQueryEvaluator(const Element&, PseudoId, SelectorMatchingState*);
    4142
    4243    bool evaluate(const FilteredContainerQuery&) const;
     
    4445private:
    4546    struct ResolvedContainer;
     47    std::optional<ResolvedContainer> resolveContainer(const FilteredContainerQuery&) const;
    4648
    4749    EvaluationResult evaluateQuery(const CQ::ContainerQuery&, const ResolvedContainer&) const;
     
    5052    EvaluationResult evaluateSizeFeature(const CQ::SizeFeature&, const ResolvedContainer&) const;
    5153
    52     const Vector<Ref<const Element>>& m_containers;
     54    const Ref<const Element> m_element;
     55    const PseudoId m_pseudoId;
     56    SelectorMatchingState* m_selectorMatchingState;
    5357};
    5458
  • trunk/Source/WebCore/style/ElementRuleCollector.cpp

    r289706 r290205  
    508508bool ElementRuleCollector::containerQueryMatches(const FilteredContainerQuery& query)
    509509{
    510     if (!m_selectorMatchingState)
    511         return true;
    512 
    513     ContainerQueryEvaluator evaluator(m_selectorMatchingState->queryContainers);
     510    ContainerQueryEvaluator evaluator(element(), m_pseudoElementRequest.pseudoId , m_selectorMatchingState);
    514511    return evaluator.evaluate(query);
    515512}
Note: See TracChangeset for help on using the changeset viewer.