Bug 933193 part 1. Factor out the id selector fast-path from querySelector(All) so we can reuse it a bit more broadly. r=smaug

This commit is contained in:
Boris Zbarsky 2013-11-01 15:39:24 -04:00
parent fdcb4e1cac
commit 2ff0889188

View File

@ -2319,6 +2319,59 @@ AddScopeElements(TreeMatchContext& aMatchContext,
}
}
namespace {
struct SelectorMatchInfo {
nsCSSSelectorList* const mSelectorList;
TreeMatchContext& mMatchContext;
};
}
// Given an id, find elements with that id under aRoot that match aMatchInfo if
// any is provided. If no SelectorMatchInfo is provided, just find the ones
// with the given id. aRoot must be in the document.
template<bool onlyFirstMatch, class T>
inline static void
FindMatchingElementsWithId(const nsAString& aId, nsINode* aRoot,
SelectorMatchInfo* aMatchInfo,
T& aList)
{
MOZ_ASSERT(aRoot->IsInDoc(),
"Don't call me if the root is not in the document");
MOZ_ASSERT(aRoot->IsElement() || aRoot->IsNodeOfType(nsINode::eDOCUMENT),
"The optimization below to check ContentIsDescendantOf only for "
"elements depends on aRoot being either an element or a "
"document if it's in the document. Note that document fragments "
"can't be IsInDoc(), so should never show up here.");
const nsSmallVoidArray* elements = aRoot->OwnerDoc()->GetAllElementsForId(aId);
if (!elements) {
// Nothing to do; we're done
return;
}
// XXXbz: Should we fall back to the tree walk if aRoot is not the
// document and |elements| is long, for some value of "long"?
for (int32_t i = 0; i < elements->Count(); ++i) {
Element *element = static_cast<Element*>(elements->ElementAt(i));
if (!aRoot->IsElement() ||
(element != aRoot &&
nsContentUtils::ContentIsDescendantOf(element, aRoot))) {
// We have an element with the right id and it's a strict descendant
// of aRoot. Make sure it really matches the selector.
if (!aMatchInfo ||
nsCSSRuleProcessor::SelectorListMatches(element,
aMatchInfo->mMatchContext,
aMatchInfo->mSelectorList)) {
aList.AppendElement(element);
if (onlyFirstMatch) {
return;
}
}
}
}
}
// Actually find elements matching aSelectorList (which must not be
// null) and which are descendants of aRoot and put them in aList. If
// onlyFirstMatch, then stop once the first one is found.
@ -2326,7 +2379,6 @@ template<bool onlyFirstMatch, class Collector, class T>
MOZ_ALWAYS_INLINE static nsresult
FindMatchingElements(nsINode* aRoot, const nsAString& aSelector, T &aList)
{
nsIDocument* doc = aRoot->OwnerDoc();
nsIDocument::SelectorCache& cache = doc->GetSelectorCache();
nsCSSSelectorList* selectorList = nullptr;
@ -2380,32 +2432,9 @@ FindMatchingElements(nsINode* aRoot, const nsAString& aSelector, T &aList)
!selectorList->mNext &&
selectorList->mSelectors->mIDList) {
nsIAtom* id = selectorList->mSelectors->mIDList->mAtom;
const nsSmallVoidArray* elements =
doc->GetAllElementsForId(nsDependentAtomString(id));
// XXXbz: Should we fall back to the tree walk if aRoot is not the
// document and |elements| is long, for some value of "long"?
if (elements) {
for (int32_t i = 0; i < elements->Count(); ++i) {
Element *element = static_cast<Element*>(elements->ElementAt(i));
if (!aRoot->IsElement() ||
(element != aRoot &&
nsContentUtils::ContentIsDescendantOf(element, aRoot))) {
// We have an element with the right id and it's a strict descendant
// of aRoot. Make sure it really matches the selector.
if (nsCSSRuleProcessor::SelectorListMatches(element, matchingContext,
selectorList)) {
aList.AppendElement(element);
if (onlyFirstMatch) {
return NS_OK;
}
}
}
}
}
// No elements with this id, or none of them are our descendants,
// or none of them match. We're done here.
SelectorMatchInfo info = { selectorList, matchingContext };
FindMatchingElementsWithId<onlyFirstMatch, T>(nsDependentAtomString(id),
aRoot, &info, aList);
return NS_OK;
}