mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 01:48:05 +00:00
Bug 705877 part 3. Hang an optional Bloom filter and some methods for managing it off the TreeMatchContext. r=dbaron
This commit is contained in:
parent
e9f9272eeb
commit
f593cd5e83
@ -3242,3 +3242,99 @@ nsCSSRuleProcessor::SelectorListMatches(Element* aElement,
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// AncestorFilter out of line methods
|
||||
void
|
||||
AncestorFilter::Init(Element *aElement)
|
||||
{
|
||||
MOZ_ASSERT(!mFilter);
|
||||
MOZ_ASSERT(mHashes.IsEmpty());
|
||||
|
||||
mFilter = new Filter();
|
||||
|
||||
if (NS_LIKELY(aElement)) {
|
||||
MOZ_ASSERT(aElement->IsInDoc(),
|
||||
"aElement must be in the document for the assumption that "
|
||||
"GetNodeParent() is non-null on all element ancestors of "
|
||||
"aElement to be true");
|
||||
// Collect up the ancestors
|
||||
nsAutoTArray<Element*, 50> ancestors;
|
||||
Element* cur = aElement;
|
||||
do {
|
||||
ancestors.AppendElement(cur);
|
||||
nsINode* parent = cur->GetNodeParent();
|
||||
if (!parent->IsElement()) {
|
||||
break;
|
||||
}
|
||||
cur = parent->AsElement();
|
||||
} while (true);
|
||||
|
||||
// Now push them in reverse order.
|
||||
for (PRUint32 i = ancestors.Length(); i-- != 0; ) {
|
||||
PushAncestor(ancestors[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AncestorFilter::PushAncestor(Element *aElement)
|
||||
{
|
||||
MOZ_ASSERT(mFilter);
|
||||
|
||||
PRUint32 oldLength = mHashes.Length();
|
||||
|
||||
mPopTargets.AppendElement(oldLength);
|
||||
#ifdef DEBUG
|
||||
mElements.AppendElement(aElement);
|
||||
#endif
|
||||
mHashes.AppendElement(aElement->Tag()->hash());
|
||||
nsIAtom *id = aElement->GetID();
|
||||
if (id) {
|
||||
mHashes.AppendElement(id->hash());
|
||||
}
|
||||
const nsAttrValue *classes = aElement->GetClasses();
|
||||
if (classes) {
|
||||
PRUint32 classCount = classes->GetAtomCount();
|
||||
for (PRUint32 i = 0; i < classCount; ++i) {
|
||||
mHashes.AppendElement(classes->AtomAt(i)->hash());
|
||||
}
|
||||
}
|
||||
|
||||
PRUint32 newLength = mHashes.Length();
|
||||
for (PRUint32 i = oldLength; i < newLength; ++i) {
|
||||
mFilter->add(mHashes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AncestorFilter::PopAncestor()
|
||||
{
|
||||
MOZ_ASSERT(!mPopTargets.IsEmpty());
|
||||
MOZ_ASSERT(mPopTargets.Length() == mElements.Length());
|
||||
|
||||
PRUint32 popTargetLength = mPopTargets.Length();
|
||||
PRUint32 newLength = mPopTargets[popTargetLength-1];
|
||||
|
||||
mPopTargets.TruncateLength(popTargetLength-1);
|
||||
#ifdef DEBUG
|
||||
mElements.TruncateLength(popTargetLength-1);
|
||||
#endif
|
||||
|
||||
PRUint32 oldLength = mHashes.Length();
|
||||
for (PRUint32 i = newLength; i < oldLength; ++i) {
|
||||
mFilter->remove(mHashes[i]);
|
||||
}
|
||||
mHashes.TruncateLength(newLength);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
AncestorFilter::AssertHasAllAncestors(Element *aElement) const
|
||||
{
|
||||
nsINode* cur = aElement->GetNodeParent();
|
||||
while (cur && cur->IsElement()) {
|
||||
MOZ_ASSERT(mElements.Contains(cur));
|
||||
cur = cur->GetNodeParent();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -51,12 +51,102 @@
|
||||
#include "nsCSSPseudoElements.h"
|
||||
#include "nsRuleWalker.h"
|
||||
#include "nsNthIndexCache.h"
|
||||
#include "mozilla/BloomFilter.h"
|
||||
#include "mozilla/GuardObjects.h"
|
||||
|
||||
class nsIStyleSheet;
|
||||
class nsIAtom;
|
||||
class nsICSSPseudoComparator;
|
||||
class nsAttrValue;
|
||||
|
||||
/**
|
||||
* An AncestorFilter is used to keep track of ancestors so that we can
|
||||
* quickly tell that a particular selector is not relevant to a given
|
||||
* element.
|
||||
*/
|
||||
class NS_STACK_CLASS AncestorFilter {
|
||||
public:
|
||||
/**
|
||||
* Initialize the filter. If aElement is not null, it and all its
|
||||
* ancestors will be passed to PushAncestor, starting from the root
|
||||
* and going down the tree.
|
||||
*/
|
||||
void Init(mozilla::dom::Element *aElement);
|
||||
|
||||
/* Maintenance of our ancestor state */
|
||||
void PushAncestor(mozilla::dom::Element *aElement);
|
||||
void PopAncestor();
|
||||
|
||||
/* Helper class for maintaining the ancestor state */
|
||||
class NS_STACK_CLASS AutoAncestorPusher {
|
||||
public:
|
||||
AutoAncestorPusher(bool aDoPush,
|
||||
AncestorFilter &aFilter,
|
||||
mozilla::dom::Element *aElement
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: mPushed(aDoPush && aElement), mFilter(aFilter)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
if (mPushed) {
|
||||
mFilter.PushAncestor(aElement);
|
||||
}
|
||||
}
|
||||
~AutoAncestorPusher() {
|
||||
if (mPushed) {
|
||||
mFilter.PopAncestor();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool mPushed;
|
||||
AncestorFilter &mFilter;
|
||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
|
||||
/* Check whether we might have an ancestor matching one of the given
|
||||
atom hashes. |hashes| must have length hashListLength */
|
||||
template<size_t hashListLength>
|
||||
bool MightHaveMatchingAncestor(const uint32_t* aHashes) const
|
||||
{
|
||||
MOZ_ASSERT(mFilter);
|
||||
for (size_t i = 0; i < hashListLength && aHashes[i]; ++i) {
|
||||
if (!mFilter->mightContain(aHashes[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HasFilter() const { return mFilter; }
|
||||
|
||||
#ifdef DEBUG
|
||||
void AssertHasAllAncestors(mozilla::dom::Element *aElement) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Using 2^12 slots makes the Bloom filter a nice round page in
|
||||
// size, so let's do that. We get a false positive rate of 1% or
|
||||
// less even with several hundred things in the filter. Note that
|
||||
// we allocate the filter lazily, because not all tree match
|
||||
// contexts can use one effectively.
|
||||
typedef mozilla::BloomFilter<12, nsIAtom> Filter;
|
||||
nsAutoPtr<Filter> mFilter;
|
||||
|
||||
// Stack of indices to pop to. These are indices into mHashes.
|
||||
nsTArray<PRUint32> mPopTargets;
|
||||
|
||||
// List of hashes; this is what we pop using mPopTargets. We store
|
||||
// hashes of our ancestor element tag names, ids, and classes in
|
||||
// here.
|
||||
nsTArray<uint32_t> mHashes;
|
||||
|
||||
// A debug-only stack of Elements for use in assertions
|
||||
#ifdef DEBUG
|
||||
nsTArray<mozilla::dom::Element*> mElements;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* A |TreeMatchContext| has data about a matching operation. The
|
||||
* data are not node-specific but are invariants of the DOM tree the
|
||||
@ -128,6 +218,9 @@ struct NS_STACK_CLASS TreeMatchContext {
|
||||
// The nth-index cache we should use
|
||||
nsNthIndexCache mNthIndexCache;
|
||||
|
||||
// An ancestor filter
|
||||
AncestorFilter mAncestorFilter;
|
||||
|
||||
// Constructor to use when creating a tree match context for styling
|
||||
TreeMatchContext(bool aForStyling,
|
||||
nsRuleWalker::VisitedHandlingType aVisitedHandling,
|
||||
|
Loading…
Reference in New Issue
Block a user