Bug 449447. Reuse the caching GetNthIndex for first/last/onlyChild selectors

This commit is contained in:
Boris Zbarsky 2008-08-18 12:47:12 -04:00
parent 86fc9bf82f
commit fbb19aff3d
2 changed files with 91 additions and 45 deletions

View File

@ -1005,24 +1005,26 @@ RuleProcessorData::GetNthIndex(PRBool aIsOfType, PRBool aIsFromEnd,
PRInt32 result = 1;
nsIContent* parent = mParentContent;
PRUint32 cur;
PRUint32 childCount = parent->GetChildCount();
nsIContent * const * curChildPtr = parent->GetChildArray();
PRInt32 increment;
nsIContent * const * stopPtr;
if (aIsFromEnd) {
cur = parent->GetChildCount() - 1;
stopPtr = curChildPtr - 1;
curChildPtr += childCount - 1;
increment = -1;
} else {
cur = 0;
increment = 1;
stopPtr = curChildPtr + childCount;
}
for (;;) {
nsIContent* child = parent->GetChildAt(cur);
if (!child) {
for ( ; ; curChildPtr += increment) {
if (curChildPtr == stopPtr) {
// mContent is the root of an anonymous content subtree.
result = 0; // special value to indicate that it is not at any index
break;
}
cur += increment;
nsIContent* child = *curChildPtr;
if (child == mContent)
break;
if (child->IsNodeOfType(nsINode::eELEMENT) &&
@ -1190,68 +1192,57 @@ static PRBool SelectorMatches(RuleProcessorData &data,
for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
pseudoClass && result; pseudoClass = pseudoClass->mNext) {
PRInt32 stateToCheck = 0;
if ((nsCSSPseudoClasses::firstChild == pseudoClass->mAtom) ||
(nsCSSPseudoClasses::firstNode == pseudoClass->mAtom) ) {
nsIContent *firstChild = nsnull;
if (nsCSSPseudoClasses::firstNode == pseudoClass->mAtom) {
nsIContent *firstNode = nsnull;
nsIContent *parent = data.mParentContent;
if (parent) {
if (setNodeFlags)
parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
PRBool acceptNonWhitespace =
nsCSSPseudoClasses::firstNode == pseudoClass->mAtom;
PRInt32 index = -1;
do {
firstChild = parent->GetChildAt(++index);
// stop at first non-comment and non-whitespace node (and
// non-text node for firstChild)
} while (firstChild &&
!IsSignificantChild(firstChild, acceptNonWhitespace, PR_FALSE));
firstNode = parent->GetChildAt(++index);
// stop at first non-comment and non-whitespace node
} while (firstNode &&
!IsSignificantChild(firstNode, PR_TRUE, PR_FALSE));
}
result = (data.mContent == firstChild);
result = (data.mContent == firstNode);
}
else if ((nsCSSPseudoClasses::lastChild == pseudoClass->mAtom) ||
(nsCSSPseudoClasses::lastNode == pseudoClass->mAtom)) {
nsIContent *lastChild = nsnull;
else if (nsCSSPseudoClasses::lastNode == pseudoClass->mAtom) {
nsIContent *lastNode = nsnull;
nsIContent *parent = data.mParentContent;
if (parent) {
if (setNodeFlags)
parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
PRBool acceptNonWhitespace =
nsCSSPseudoClasses::lastNode == pseudoClass->mAtom;
PRUint32 index = parent->GetChildCount();
do {
lastChild = parent->GetChildAt(--index);
// stop at first non-comment and non-whitespace node (and
// non-text node for lastChild)
} while (lastChild &&
!IsSignificantChild(lastChild, acceptNonWhitespace, PR_FALSE));
lastNode = parent->GetChildAt(--index);
// stop at first non-comment and non-whitespace node
} while (lastNode &&
!IsSignificantChild(lastNode, PR_TRUE, PR_FALSE));
}
result = (data.mContent == lastChild);
result = (data.mContent == lastNode);
}
else if (nsCSSPseudoClasses::onlyChild == pseudoClass->mAtom) {
nsIContent *onlyChild = nsnull;
nsIContent *moreChild = nsnull;
else if (nsCSSPseudoClasses::firstChild == pseudoClass->mAtom ||
nsCSSPseudoClasses::lastChild == pseudoClass->mAtom ||
nsCSSPseudoClasses::onlyChild == pseudoClass->mAtom) {
nsIContent *parent = data.mParentContent;
if (parent) {
const PRBool checkFirst =
pseudoClass->mAtom != nsCSSPseudoClasses::lastChild;
const PRBool checkLast =
pseudoClass->mAtom != nsCSSPseudoClasses::firstChild;
if (setNodeFlags)
parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
PRInt32 index = -1;
do {
onlyChild = parent->GetChildAt(++index);
// stop at first non-comment, non-whitespace and non-text node
} while (onlyChild &&
!IsSignificantChild(onlyChild, PR_FALSE, PR_FALSE));
if (data.mContent == onlyChild) {
// see if there's any more
do {
moreChild = parent->GetChildAt(++index);
} while (moreChild && !IsSignificantChild(moreChild, PR_FALSE, PR_FALSE));
}
result = (!checkFirst ||
data.GetNthIndex(PR_FALSE, PR_FALSE, PR_TRUE) == 1) &&
(!checkLast ||
data.GetNthIndex(PR_FALSE, PR_TRUE, PR_TRUE) == 1);
} else {
result = PR_FALSE;
}
result = (data.mContent == onlyChild && moreChild == nsnull);
}
else if (nsCSSPseudoClasses::nthChild == pseudoClass->mAtom ||
nsCSSPseudoClasses::nthLastChild == pseudoClass->mAtom ||

View File

@ -377,6 +377,61 @@ function run() {
test_selector_in_html(":nth-last-of-type(6)", seven_ps,
pset([2]), pset([1, 3, 4, 5, 6, 7]));
// Test [first|last|only]-[child|node|of-type]
var interesting_doc = "<!----> <div id='p1'> <!---->x<p id='s1'></p> <!----><p id='s2'></p> <!----></div> <!----><p id='p2'> <!----><span id='s3'></span> <!----><span id='s4'></span> <!---->x</p> <!----><div id='p3'> <!----><p id='s5'></p> <!----></div> <!---->";
function idset(ids) { // takes an array of ids
return function idset_filter(doc) {
var result = [];
for each (var id in ids)
result.push(doc.getElementById(id));
return result;
}
}
test_parseable(":first-child");
test_parseable(":last-child");
test_parseable(":only-child");
test_parseable(":-moz-first-node");
test_parseable(":-moz-last-node");
test_parseable(":first-of-type");
test_parseable(":last-of-type");
test_parseable(":only-of-type");
test_selector_in_html(":first-child", seven_ps,
pset([1]), pset([2, 3, 4, 5, 6, 7]));
test_selector_in_html(":first-child", interesting_doc,
idset(["p1", "s1", "s3", "s5"]),
idset(["s2", "p2", "s4", "p3"]));
test_selector_in_html(":-moz-first-node", interesting_doc,
idset(["p1", "s3", "s5"]),
idset(["s1", "s2", "p2", "s4", "p3"]));
test_selector_in_html(":last-child", seven_ps,
pset([7]), pset([1, 2, 3, 4, 5, 6]));
test_selector_in_html(":last-child", interesting_doc,
idset(["s2", "s4", "p3", "s5"]),
idset(["p1", "s1", "p2", "s3"]));
test_selector_in_html(":-moz-last-node", interesting_doc,
idset(["s2", "p3", "s5"]),
idset(["p1", "s1", "p2", "s3", "s4"]));
test_selector_in_html(":only-child", seven_ps,
pset([]), pset([1, 2, 3, 4, 5, 6, 7]));
test_selector_in_html(":only-child", interesting_doc,
idset(["s5"]),
idset(["p1", "s1", "s2", "p2", "s3", "s4", "p3"]));
test_selector_in_html(":first-of-type", seven_ps,
pset([1]), pset([2, 3, 4, 5, 6, 7]));
test_selector_in_html(":first-of-type", interesting_doc,
idset(["p1", "s1", "p2", "s3", "s5"]),
idset(["s2", "s4", "p3"]));
test_selector_in_html(":last-of-type", seven_ps,
pset([7]), pset([1, 2, 3, 4, 5, 6]));
test_selector_in_html(":last-of-type", interesting_doc,
idset(["s2", "p2", "s4", "p3", "s5"]),
idset(["p1", "s1", "s3"]));
test_selector_in_html(":only-of-type", seven_ps,
pset([]), pset([1, 2, 3, 4, 5, 6, 7]));
test_selector_in_html(":only-of-type", interesting_doc,
idset(["p2", "s5"]),
idset(["p1", "s1", "s2", "s3", "s4", "p3"]));
// And a bunch of tests for the of-type aspect of :nth-of-type() and
// :nth-last-of-type(). Note that the last div here contains two
// children.