diff --git a/.agent/plans/eepp_css_selector_optimization_plan.md b/.agent/plans/eepp_css_selector_optimization_plan.md index 8f3bf3151..49269c460 100644 --- a/.agent/plans/eepp_css_selector_optimization_plan.md +++ b/.agent/plans/eepp_css_selector_optimization_plan.md @@ -307,6 +307,8 @@ Expected: # Phase 3: Fix Universal Selector Early Return +**Status: Implemented** + ## Motivation `StyleSheetSelectorRule::matches()` currently returns true early for `*` when `applyPseudo == false`. diff --git a/src/eepp/ui/css/stylesheetselectorrule.cpp b/src/eepp/ui/css/stylesheetselectorrule.cpp index 5626ce7da..d3298d3e1 100644 --- a/src/eepp/ui/css/stylesheetselectorrule.cpp +++ b/src/eepp/ui/css/stylesheetselectorrule.cpp @@ -318,10 +318,12 @@ bool StyleSheetSelectorRule::matches( UIWidget* element, const bool& applyPseudo flags |= TagName; } } else { - if ( !applyPseudo ) { + flags |= TagName; + + const Uint32 nonPseudoRequirements = + mRequirementFlags & ~( PseudoClass | StructuralPseudoClass ); + if ( !applyPseudo && nonPseudoRequirements == TagName ) { return true; - } else { - flags |= TagName; } } } diff --git a/src/tests/unit_tests/uihtml_tests.cpp b/src/tests/unit_tests/uihtml_tests.cpp index b10823795..001c28cd6 100644 --- a/src/tests/unit_tests/uihtml_tests.cpp +++ b/src/tests/unit_tests/uihtml_tests.cpp @@ -2803,6 +2803,63 @@ UTEST( UIHTML, StyleSheetSiblingCombinators ) { Engine::destroySingleton(); } +UTEST( UIHTML, UniversalSelectorRequirements ) { + Engine::instance()->createWindow( WindowSettings( 1024, 768, "Universal Selector Test", + WindowStyle::Default, WindowBackend::Default, + 32, {}, 1, false, true ), + ContextSettings( false, 0, 0, GLv_default, true, false ) ); + + UISceneNode* sceneNode = init_test_inline_block(); + const std::string html = R"html( + + + + + + +
class match
+
class miss
+ attribute match + attribute miss + + +)html"; + + sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) ); + sceneNode->update( Seconds( 1 ) ); + + auto* classMatch = sceneNode->getRoot()->find( "class-match" )->asType(); + auto* classMiss = sceneNode->getRoot()->find( "class-miss" )->asType(); + auto* attributeMatch = sceneNode->getRoot()->find( "attribute-match" )->asType(); + auto* attributeMiss = sceneNode->getRoot()->find( "attribute-miss" )->asType(); + ASSERT_TRUE( classMatch != nullptr ); + ASSERT_TRUE( classMiss != nullptr ); + ASSERT_TRUE( attributeMatch != nullptr ); + ASSERT_TRUE( attributeMiss != nullptr ); + + EXPECT_TRUE( classMatch->getFontColor() == Color::Red ); + EXPECT_TRUE( classMiss->getFontColor() != Color::Red ); + EXPECT_TRUE( attributeMatch->getFontColor() == Color::Green ); + EXPECT_TRUE( attributeMiss->getFontColor() != Color::Green ); + + StyleSheetSelector universalClass( "*.foo" ); + EXPECT_TRUE( universalClass.select( classMatch, false ) ); + EXPECT_FALSE( universalClass.select( classMiss, false ) ); + + StyleSheetSelector universalAttribute( "*[href]" ); + EXPECT_TRUE( universalAttribute.select( attributeMatch, false ) ); + EXPECT_FALSE( universalAttribute.select( attributeMiss, false ) ); + + StyleSheetSelector pureUniversal( "*" ); + EXPECT_TRUE( pureUniversal.select( classMiss, false ) ); + EXPECT_TRUE( pureUniversal.select( attributeMiss, false ) ); + + Engine::destroySingleton(); +} + UTEST( UIHTML, BlockList ) { Engine::instance()->createWindow( WindowSettings( 1024, 768, "Block List Test", WindowStyle::Default, WindowBackend::Default,