Handle indirect sibling selector

Adds parsing and handling of the indirect sibling selector, this should
mean we can at least parse all CSS3 selectors even if we do not yet
support all of them.

Also adds tests for previously added CSS3 selectors.

Change-Id: I1ce9afb9466044a38bdec167affc21a87837e4a4
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
bb10
Allan Sandfeld Jensen 2016-07-27 18:04:21 +02:00 committed by Allan Sandfeld Jensen
parent f2922c80a4
commit 997fa05d90
5 changed files with 618 additions and 564 deletions

View File

@ -1867,12 +1867,15 @@ bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node)
do {
match = basicSelectorMatches(sel, node);
if (!match) {
if (sel.relationToNext == BasicSelector::MatchNextSelectorIfParent
|| i == selector.basicSelectors.count() - 1) // first element must always match!
if (i == selector.basicSelectors.count() - 1) // first element must always match!
break;
if (sel.relationToNext != BasicSelector::MatchNextSelectorIfAncestor &&
sel.relationToNext != BasicSelector::MatchNextSelectorIfIndirectAdjecent)
break;
}
if (match || sel.relationToNext != BasicSelector::MatchNextSelectorIfAncestor)
if (match || (sel.relationToNext != BasicSelector::MatchNextSelectorIfAncestor &&
sel.relationToNext != BasicSelector::MatchNextSelectorIfIndirectAdjecent))
--i;
if (i < 0)
@ -1885,16 +1888,18 @@ bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node)
NodePtr nextParent = parentNode(node);
freeNode(node);
node = nextParent;
} else if (sel.relationToNext == BasicSelector::MatchNextSelectorIfPreceeds) {
} else if (sel.relationToNext == BasicSelector::MatchNextSelectorIfDirectAdjecent
|| sel.relationToNext == BasicSelector::MatchNextSelectorIfIndirectAdjecent) {
NodePtr previousSibling = previousSiblingNode(node);
freeNode(node);
node = previousSibling;
}
}
if (isNullNode(node)) {
match = false;
break;
}
} while (i >= 0 && (match || sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor));
} while (i >= 0 && (match || sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor
|| sel.relationToNext == BasicSelector::MatchNextSelectorIfIndirectAdjecent));
freeNode(node);
@ -2356,9 +2361,11 @@ bool Parser::parseCombinator(BasicSelector::Relation *relation)
prev();
}
if (test(PLUS)) {
*relation = BasicSelector::MatchNextSelectorIfPreceeds;
*relation = BasicSelector::MatchNextSelectorIfDirectAdjecent;
} else if (test(GREATER)) {
*relation = BasicSelector::MatchNextSelectorIfParent;
} else if (test(TILDE)) {
*relation = BasicSelector::MatchNextSelectorIfIndirectAdjecent;
}
skipSpace();
return true;

View File

@ -561,7 +561,8 @@ struct BasicSelector
NoRelation,
MatchNextSelectorIfAncestor,
MatchNextSelectorIfParent,
MatchNextSelectorIfPreceeds
MatchNextSelectorIfDirectAdjecent,
MatchNextSelectorIfIndirectAdjecent,
};
QString elementName;
@ -690,6 +691,7 @@ enum TokenType {
PLUS,
GREATER,
COMMA,
TILDE,
STRING,
INVALID,
@ -789,7 +791,7 @@ public:
inline bool testImport() { return testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("import")); }
inline bool testMedia() { return testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("media")); }
inline bool testPage() { return testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("page")); }
inline bool testCombinator() { return test(PLUS) || test(GREATER) || test(S); }
inline bool testCombinator() { return test(PLUS) || test(GREATER) || test(TILDE) || test(S); }
inline bool testProperty() { return test(IDENT); }
bool testTerm();
inline bool testExpr() { return testTerm(); }

File diff suppressed because it is too large Load Diff

View File

@ -120,6 +120,7 @@ static const char *tokenName(QCss::TokenType t)
case QCss::PLUS: return "PLUS";
case QCss::GREATER: return "GREATER";
case QCss::COMMA: return "COMMA";
case QCss::TILDE: return "TILDE";
case QCss::STRING: return "STRING";
case QCss::INVALID: return "INVALID";
case QCss::IDENT: return "IDENT";
@ -480,7 +481,7 @@ void tst_QCssParser::selector_data()
QCss::BasicSelector basic;
basic.elementName = "p";
basic.relationToNext = QCss::BasicSelector::MatchNextSelectorIfPreceeds;
basic.relationToNext = QCss::BasicSelector::MatchNextSelectorIfDirectAdjecent;
sel.basicSelectors << basic;
basic = QCss::BasicSelector();
@ -572,14 +573,29 @@ void tst_QCssParser::selector_data()
QCss::BasicSelector basic;
basic.elementName = "e";
basic.relationToNext = QCss::BasicSelector::MatchNextSelectorIfPreceeds;
basic.relationToNext = QCss::BasicSelector::MatchNextSelectorIfDirectAdjecent;
sel.basicSelectors << basic;
basic.elementName = "f";
basic.relationToNext = QCss::BasicSelector::NoRelation;
sel.basicSelectors << basic;
QTest::newRow("precede") << QString("e + f") << sel;
QTest::newRow("lastsibling") << QString("e + f") << sel;
}
{
QCss::Selector sel;
QCss::BasicSelector basic;
basic.elementName = "e";
basic.relationToNext = QCss::BasicSelector::MatchNextSelectorIfIndirectAdjecent;
sel.basicSelectors << basic;
basic.elementName = "f";
basic.relationToNext = QCss::BasicSelector::NoRelation;
sel.basicSelectors << basic;
QTest::newRow("previoussibling") << QString("e ~ f") << sel;
}
{
@ -985,11 +1001,20 @@ void tst_QCssParser::styleSelector_data()
QTest::newRow("attrmatch") << true << QString("[foo=bar]") << QString("<p foo=\"bar\" />") << QString();
QTest::newRow("noattrmatch") << false << QString("[foo=bar]") << QString("<p foo=\"xyz\" />") << QString();
QTest::newRow("contains") << true << QString("[foo~=bar]") << QString("<p foo=\"baz bleh bar\" />") << QString();
QTest::newRow("notcontains") << false << QString("[foo~=bar]") << QString("<p foo=\"test\" />") << QString();
QTest::newRow("includes") << true << QString("[foo~=bar]") << QString("<p foo=\"baz bleh bar\" />") << QString();
QTest::newRow("notincludes") << false << QString("[foo~=bar]") << QString("<p foo=\"bazblehbar\" />") << QString();
QTest::newRow("beingswith") << true << QString("[foo|=bar]") << QString("<p foo=\"bar-bleh\" />") << QString();
QTest::newRow("notbeingswith") << false << QString("[foo|=bar]") << QString("<p foo=\"bleh-bar\" />") << QString();
QTest::newRow("dashmatch") << true << QString("[foo|=bar]") << QString("<p foo=\"bar-bleh\" />") << QString();
QTest::newRow("nodashmatch") << false << QString("[foo|=bar]") << QString("<p foo=\"barbleh\" />") << QString();
QTest::newRow("beginswith") << true << QString("[foo^=bar]") << QString("<p foo=\"barbleh\" />") << QString();
QTest::newRow("nobeginswith") << false << QString("[foo^=bar]") << QString("<p foo=\"blehbleh\" />") << QString();
QTest::newRow("endswith") << true << QString("[foo$=bar]") << QString("<p foo=\"barbar\" />") << QString();
QTest::newRow("noendswith") << false << QString("[foo$=bar]") << QString("<p foo=\"blehbleh\" />") << QString();
QTest::newRow("contains") << true << QString("[foo*=bar]") << QString("<p foo=\"blehbarbleh\" />") << QString();
QTest::newRow("nocontains") << false << QString("[foo*=bar]") << QString("<p foo=\"blehbleh\" />") << QString();
QTest::newRow("attr2") << true << QString("[bar=foo]") << QString("<p bleh=\"bar\" bar=\"foo\" />") << QString();
@ -1070,10 +1095,19 @@ void tst_QCssParser::styleSelector_data()
<< QString("<p1 /><p2 />")
<< QString("p2");
QTest::newRow("noprevioussibling") << false << QString("p2 + p1")
QTest::newRow("notprevioussibling") << false << QString("p2 + p1")
<< QString("<p1 /><p2 />")
<< QString("p2");
QTest::newRow("anyprevioussibling") << true << QString("p1 ~ p3")
<< QString("<p1 /><p2 /><p3 />")
<< QString("p3");
QTest::newRow("noprevioussibling") << false << QString("p3 ~ p2")
<< QString("<p1 /><p2 /><p3 />")
<< QString("p3");
QTest::newRow("ancestry_firstmismatch") << false << QString("parent child[foo=bar]")
<< QString("<parent><child /></parent>")
<< QString("parent/child");

View File

@ -44,6 +44,7 @@ LBRACE = {w}"{"
PLUS = {w}"+"
GREATER = {w}">"
COMMA = {w}","
TILDE= {w}"~"
STRING = {string}
INVALID = {invalid}