diff options
author | Johannes Hofmann <Johannes.Hofmann@gmx.de> | 2012-01-13 22:23:26 +0100 |
---|---|---|
committer | Johannes Hofmann <Johannes.Hofmann@gmx.de> | 2012-01-13 22:23:26 +0100 |
commit | a987a45a6d6bdf27aecf1ef0625801834ab27e44 (patch) | |
tree | 98723f8992c65ad81aae9f66f06d0f0bfda5ccd2 /src | |
parent | fe21a59c2c565d827147ac27f3398f2b3c2f22a4 (diff) |
fix descendant selector matching
When matching descendant selectors we need to test all
possibilities and not just the first one.
While at it refactor CssSelector::match ().
Testcase:
<html>
<head>
<style type=text/css> .a > .b .c { font-weight:bold }</style>
</head>
<body>
<div class=a>
<div class=b>
<div class=b>
<div class=c>should be bold</div>
</div>
</div>
</div>
</body>
</html>
Noticed-by: Sebastian Geerken <sgeerken@dillo.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/css.cc | 83 | ||||
-rw-r--r-- | src/css.hh | 13 | ||||
-rw-r--r-- | src/cssparser.cc | 6 |
3 files changed, 50 insertions, 52 deletions
@@ -102,7 +102,7 @@ CssSelector::CssSelector () { cs = selectorList->getRef (selectorList->size () - 1); cs->notMatchingBefore = -1; - cs->combinator = CHILD; + cs->combinator = COMB_NONE; cs->selector = new CssSimpleSelector (); }; @@ -115,55 +115,47 @@ CssSelector::~CssSelector () { /** * \brief Return whether selector matches at a given node in the document tree. */ -bool CssSelector::match (Doctree *docTree, const DoctreeNode *node) { - CssSimpleSelector *sel; - Combinator comb = CHILD; - int *notMatchingBefore; - const DoctreeNode *n; +bool CssSelector::match (Doctree *docTree, const DoctreeNode *node, + int i, Combinator comb) { + assert (node); - for (int i = selectorList->size () - 1; i >= 0; i--) { - struct CombinatorAndSelector *cs = selectorList->getRef (i); + if (i < 0) + return true; - sel = cs->selector; - notMatchingBefore = &cs->notMatchingBefore; + struct CombinatorAndSelector *cs = selectorList->getRef (i); + CssSimpleSelector *sel = cs->selector; - if (node == NULL) - return false; - - switch (comb) { - case CHILD: - case ADJACENT_SIBLING: - if (!sel->match (node)) - return false; - break; - case DESCENDANT: - n = node; - - while (true) { - if (node == NULL || node->num <= *notMatchingBefore) { - *notMatchingBefore = n->num; - return false; - } - - if (sel->match (node)) - break; + switch (comb) { + case COMB_NONE: + break; + case COMB_CHILD: + node = docTree->parent (node); + break; + case COMB_ADJACENT_SIBLING: + node = docTree->sibling (node); + break; + case COMB_DESCENDANT: + node = docTree->parent (node); - node = docTree->parent (node); - } - break; - default: - return false; // \todo implement other combinators - } + for (const DoctreeNode *n = node; + n && n->num > cs->notMatchingBefore; n = docTree->parent (n)) + if (sel->match (n) && match (docTree, n, i - 1, cs->combinator)) + return true; - comb = cs->combinator; + if (node) // remember that it didn't match to avoid future tests + cs->notMatchingBefore = node->num; - if (comb == ADJACENT_SIBLING) - node = docTree->sibling (node); - else - node = docTree->parent (node); + return false; + break; + default: + return false; // \todo implement other combinators } - return true; + if (!node || !sel->match (node)) + return false; + + // tail recursion should be optimized by the compiler + return match (docTree, node, i - 1, cs->combinator); } void CssSelector::addSimpleSelector (Combinator c) { @@ -198,13 +190,13 @@ void CssSelector::print () { if (i < selectorList->size () - 1) { switch (selectorList->getRef (i + 1)->combinator) { - case CHILD: + case COMB_CHILD: fprintf (stderr, "> "); break; - case DESCENDANT: + case COMB_DESCENDANT: fprintf (stderr, "\" \" "); break; - case ADJACENT_SIBLING: + case COMB_ADJACENT_SIBLING: fprintf (stderr, "+ "); break; default: @@ -260,6 +252,7 @@ void CssSimpleSelector::setSelect (SelectType t, const char *v) { * the document tree. */ bool CssSimpleSelector::match (const DoctreeNode *n) { + assert (n); if (element != ELEMENT_ANY && element != n->element) return false; if (pseudo != NULL && @@ -355,9 +355,10 @@ class CssSimpleSelector { class CssSelector { public: typedef enum { - DESCENDANT, - CHILD, - ADJACENT_SIBLING, + COMB_NONE, + COMB_DESCENDANT, + COMB_CHILD, + COMB_ADJACENT_SIBLING, } Combinator; private: @@ -370,6 +371,8 @@ class CssSelector { int refCount; lout::misc::SimpleVector <struct CombinatorAndSelector> *selectorList; + bool match (Doctree *dt, const DoctreeNode *node, int i, Combinator comb); + public: CssSelector (); ~CssSelector (); @@ -378,7 +381,9 @@ class CssSelector { return selectorList->getRef (selectorList->size () - 1)->selector; }; inline int size () { return selectorList->size (); }; - bool match (Doctree *dt, const DoctreeNode *node); + inline bool match (Doctree *dt, const DoctreeNode *node) { + return match (dt, node, selectorList->size () - 1, COMB_NONE); + }; int specificity (); void print (); inline void ref () { refCount++; } diff --git a/src/cssparser.cc b/src/cssparser.cc index 6d5b3768..306aa58c 100644 --- a/src/cssparser.cc +++ b/src/cssparser.cc @@ -1292,13 +1292,13 @@ CssSelector *CssParser::parseSelector() (tval[0] == ',' || tval[0] == '{')) { break; } else if (ttype == CSS_TK_CHAR && tval[0] == '>') { - selector->addSimpleSelector (CssSelector::CHILD); + selector->addSimpleSelector (CssSelector::COMB_CHILD); nextToken(); } else if (ttype == CSS_TK_CHAR && tval[0] == '+') { - selector->addSimpleSelector (CssSelector::ADJACENT_SIBLING); + selector->addSimpleSelector (CssSelector::COMB_ADJACENT_SIBLING); nextToken(); } else if (ttype != CSS_TK_END && spaceSeparated) { - selector->addSimpleSelector (CssSelector::DESCENDANT); + selector->addSimpleSelector (CssSelector::COMB_DESCENDANT); } else { delete selector; selector = NULL; |