Bug 696205 part 2. Add a querySelector fast-path for selectors whose rightmost sequence of simple selectors contains an id. r=sicking

This commit is contained in:
Boris Zbarsky 2011-10-31 22:50:50 -04:00
Родитель 440978fc40
Коммит 5a98b45de1
4 изменённых файлов: 79 добавлений и 6 удалений

Просмотреть файл

@ -126,8 +126,8 @@ class Element;
} // namespace mozilla } // namespace mozilla
#define NS_IDOCUMENT_IID \ #define NS_IDOCUMENT_IID \
{ 0x3d24831e, 0x2a2b, 0x42f4, \ { 0xb52356d4, 0xe191, 0x4cf8, \
{ 0x9d, 0x98, 0x17, 0x60, 0x18, 0xab, 0x6e, 0xfb } } { 0xb8, 0x58, 0xc0, 0xf1, 0xe1, 0x98, 0x09, 0xdf } }
// Flag for AddStyleSheet(). // Flag for AddStyleSheet().
#define NS_STYLESHEET_FROM_CATALOG (1 << 0) #define NS_STYLESHEET_FROM_CATALOG (1 << 0)
@ -1510,6 +1510,13 @@ public:
*/ */
virtual Element* GetElementById(const nsAString& aElementId) = 0; virtual Element* GetElementById(const nsAString& aElementId) = 0;
/**
* This method returns _all_ the elements in this document which
* have id aElementId, if there are any. Otherwise it returns null.
* The entries of the nsSmallVoidArray are Element*
*/
virtual const nsSmallVoidArray* GetAllElementsForId(const nsAString& aElementId) const = 0;
/** /**
* Lookup an image element using its associated ID, which is usually provided * Lookup an image element using its associated ID, which is usually provided
* by |-moz-element()|. Similar to GetElementById, with the difference that * by |-moz-element()|. Similar to GetElementById, with the difference that

Просмотреть файл

@ -4036,6 +4036,17 @@ nsDocument::GetElementById(const nsAString& aElementId)
return entry ? entry->GetIdElement() : nsnull; return entry ? entry->GetIdElement() : nsnull;
} }
const nsSmallVoidArray*
nsDocument::GetAllElementsForId(const nsAString& aElementId) const
{
if (aElementId.IsEmpty()) {
return nsnull;
}
nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId);
return entry ? entry->GetIdElements() : nsnull;
}
NS_IMETHODIMP NS_IMETHODIMP
nsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn) nsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn)
{ {

Просмотреть файл

@ -171,6 +171,12 @@ public:
* id. Otherwise returns null. * id. Otherwise returns null.
*/ */
Element* GetIdElement(); Element* GetIdElement();
/**
* Returns the list of all elements associated with this id.
*/
const nsSmallVoidArray* GetIdElements() const {
return &mIdContentList;
}
/** /**
* If this entry has a non-null image element set (using SetImageElement), * If this entry has a non-null image element set (using SetImageElement),
* the image element will be returned, otherwise the same as GetIdElement(). * the image element will be returned, otherwise the same as GetIdElement().
@ -244,7 +250,7 @@ private:
void FireChangeCallbacks(Element* aOldElement, Element* aNewElement, void FireChangeCallbacks(Element* aOldElement, Element* aNewElement,
bool aImageOnly = false); bool aImageOnly = false);
// empty if there are no elementswith this ID. // empty if there are no elements with this ID.
// The elements are stored as weak pointers. // The elements are stored as weak pointers.
nsSmallVoidArray mIdContentList; nsSmallVoidArray mIdContentList;
nsRefPtr<nsBaseContentList> mNameContentList; nsRefPtr<nsBaseContentList> mNameContentList;
@ -926,6 +932,7 @@ public:
const nsAString& aLocalName); const nsAString& aLocalName);
virtual Element *GetElementById(const nsAString& aElementId); virtual Element *GetElementById(const nsAString& aElementId);
virtual const nsSmallVoidArray* GetAllElementsForId(const nsAString& aElementId) const;
virtual Element *LookupImageElement(const nsAString& aElementId); virtual Element *LookupImageElement(const nsAString& aElementId);

Просмотреть файл

@ -5352,6 +5352,9 @@ ParseSelectorList(nsINode* aNode,
return NS_OK; return NS_OK;
} }
// 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.
template<bool onlyFirstMatch, class T> template<bool onlyFirstMatch, class T>
inline static nsresult FindMatchingElements(nsINode* aRoot, inline static nsresult FindMatchingElements(nsINode* aRoot,
const nsAString& aSelector, const nsAString& aSelector,
@ -5361,10 +5364,55 @@ inline static nsresult FindMatchingElements(nsINode* aRoot,
nsresult rv = ParseSelectorList(aRoot, aSelector, nsresult rv = ParseSelectorList(aRoot, aSelector,
getter_Transfers(selectorList)); getter_Transfers(selectorList));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(selectorList, NS_OK);
NS_ASSERTION(selectorList->mSelectors,
"How can we not have any selectors?");
nsIDocument* doc = aRoot->OwnerDoc();
TreeMatchContext matchingContext(false, nsRuleWalker::eRelevantLinkUnvisited,
doc);
// Fast-path selectors involving IDs. We can only do this if aRoot
// is in the document and the document is not in quirks mode, since
// ID selectors are case-insensitive in quirks mode. Also, only do
// this if selectorList only has one selector, because otherwise
// ordering the elements correctly is a pain.
NS_ASSERTION(aRoot->IsElement() || aRoot->IsNodeOfType(nsINode::eDOCUMENT),
"Unexpected root node");
if (aRoot->IsInDoc() &&
doc->GetCompatibilityMode() != eCompatibility_NavQuirks &&
!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 (PRUint32 i = 0; i < elements->Count(); ++i) {
Element *element = static_cast<Element*>(elements->ElementAt(i));
if (!aRoot->IsElement() ||
nsContentUtils::ContentIsDescendantOf(element, aRoot)) {
// We have an element with the right id and it's a 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.
return NS_OK;
}
TreeMatchContext matchingContext(false,
nsRuleWalker::eRelevantLinkUnvisited,
aRoot->OwnerDoc());
for (nsIContent* cur = aRoot->GetFirstChild(); for (nsIContent* cur = aRoot->GetFirstChild();
cur; cur;
cur = cur->GetNextNode(aRoot)) { cur = cur->GetNextNode(aRoot)) {