Commit 283c1f06 authored by jan.koester's avatar jan.koester
Browse files

test

parent d5f1ec88
Loading
Loading
Loading
Loading
+26 −6
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "css.h"
#include "exception.h"
#include <iostream>

#include <algorithm>
#include <array>
@@ -326,10 +327,30 @@ namespace {
    // "[data-fit=fill]" in ".fade-box[data-fit=fill] img") is a real,
    // checkable requirement: it's matched against each candidate frame's own
    // attributes (see AncestorFrame::attributes) the same way
    // compoundPartsMatch checks tag/class/id -- a frame with no attributes
    // supplied (an empty map, meaning the caller didn't populate that field)
    // can't refute it, so it's treated as satisfied for that frame rather
    // than guessed wrong.
    // compoundPartsMatch checks tag/class/id. Always passed as a real (never
    // null) map -- a PREVIOUS version of this function treated an EMPTY
    // attributes map as "the caller didn't populate this field, so it can't
    // be checked" and skipped verification entirely (auto-passing), on the
    // theory that every real caller already builds a complete attribute map
    // for every ancestor. That theory was wrong: `elemAttrs` is empty for
    // any genuinely attribute-less ancestor too (a bare `<main>`, a `<div>`
    // with no attributes at all) -- both cases produce the exact same empty
    // std::map, so there was no way to tell "not populated" apart from
    // "genuinely has nothing to match" from the map alone. On a real page,
    // this let a totally unrelated attribute-selector requirement (e.g.
    // "[data-kit-frame=solid]" several ancestors up a real selector's chain)
    // match against the nearest attribute-less <main>/<div> in between
    // instead of correctly failing, letting page-builder rules meant for a
    // completely different component apply to ordinary content (confirmed
    // on a real page: an unrelated "[data-molecule][data-kit-frame=line]
    // [data-atom]:first-child:last-child[data-atom=image] .con-kit-
    // component-image{height:100%}" rule -- gated behind ancestors this
    // element never had -- matched anyway via a bare attribute-less <main>,
    // forcing height:100% with no definite ancestor height to resolve
    // against, collapsing several real images to 0 height). Passing the
    // real (possibly empty) map instead lets attributeConditionsSatisfied
    // correctly fail any non-trivial condition against a truly attribute-
    // less frame, exactly like a real browser would.
    bool ancestorChainSatisfies(const std::vector<CompoundParts> &ancestorCompounds,
                                 const std::vector<libhtmlpp::AncestorFrame> &ancestors,
                                 bool *usedUnverifiableAncestor = nullptr)
@@ -348,8 +369,7 @@ namespace {
                const libhtmlpp::AncestorFrame &frame = ancestors[idx];
                ++idx;
                if (compoundPartsMatch(compound, frame.tag, frame.classes, frame.id) &&
                    attributeConditionsSatisfied(compound.attrConditions,
                        frame.attributes.empty() ? nullptr : &frame.attributes)) {
                    attributeConditionsSatisfied(compound.attrConditions, &frame.attributes)) {
                    found = true;
                    break;
                }
+34 −0
Original line number Diff line number Diff line
@@ -143,6 +143,40 @@ int main(){
        };
        check(!CSSStyleSheet::approximateSelectorMatch(".a .b .frame", "div", classes, "", &onlyOuterAncestor),
              "a missing middle ancestor compound still fails the match");

        // Real bug, found on bautenschutz-wetzlar.de: an ancestor attribute-
        // selector condition (e.g. "[data-frame=solid]") was vacuously
        // satisfied against ANY ancestor with a genuinely empty attribute
        // map (a bare tag like <main> with no attributes at all) instead of
        // correctly failing -- ancestorChainSatisfies used to treat "empty
        // map" as "caller didn't populate this field, can't verify" and
        // auto-passed, but a real, attribute-less ancestor produces the
        // exact same empty map. This let an unrelated, specifically-gated
        // rule (meant for a totally different component several ancestors
        // over) match through the nearest bare <main>/<div> instead,
        // forcing height:100% with nothing definite to resolve against and
        // collapsing several real images on the page to 0 height.
        std::vector<AncestorFrame> attributelessAncestor = {
            {"main", {}, ""}, // attributes defaults to {} -- genuinely no attributes, not "unpopulated"
        };
        check(!CSSStyleSheet::approximateSelectorMatch("[data-frame=solid] .frame", "div", classes, "",
              &attributelessAncestor),
              "an attribute-selector ancestor condition does not vacuously pass against a genuinely "
              "attribute-less ancestor (the actual bug)");

        std::vector<AncestorFrame> wrongAttrAncestor = {
            {"div", {}, "", {{"data-frame", "line"}}},
        };
        check(!CSSStyleSheet::approximateSelectorMatch("[data-frame=solid] .frame", "div", classes, "",
              &wrongAttrAncestor),
              "an ancestor with a different attribute value still correctly fails");

        std::vector<AncestorFrame> rightAttrAncestor = {
            {"div", {}, "", {{"data-frame", "solid"}}},
        };
        check(CSSStyleSheet::approximateSelectorMatch("[data-frame=solid] .frame", "div", classes, "",
              &rightAttrAncestor),
              "an ancestor with the matching attribute value still correctly matches");
    }

    // --- collectApproximateMatches: cascade priority ---