gecko/layout/style/nsRuleProcessorData.h

372 lines
13 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* data structures passed to nsIStyleRuleProcessor methods (to pull loop
* invariant computations out of the loop)
*/
#ifndef nsRuleProcessorData_h_
#define nsRuleProcessorData_h_
#include "nsPresContext.h" // for nsCompatibility
#include "nsString.h"
#include "nsChangeHint.h"
#include "nsIContent.h"
#include "nsCSSPseudoElements.h"
#include "nsRuleWalker.h"
#include "nsNthIndexCache.h"
#include "nsILoadContext.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<uint32_t> 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
* nodes being matched against are in.
*
* Most of the members are in parameters to selector matching. The
* one out parameter is mHaveRelevantLink. Consumers that use a
* TreeMatchContext for more than one matching operation and care
* about :visited and mHaveRelevantLink need to
* ResetForVisitedMatching() and ResetForUnvisitedMatching() as
* needed.
*/
struct NS_STACK_CLASS TreeMatchContext {
// Reset this context for matching for the style-if-:visited.
void ResetForVisitedMatching() {
NS_PRECONDITION(mForStyling, "Why is this being called?");
mHaveRelevantLink = false;
mVisitedHandling = nsRuleWalker::eRelevantLinkVisited;
}
void ResetForUnvisitedMatching() {
NS_PRECONDITION(mForStyling, "Why is this being called?");
mHaveRelevantLink = false;
mVisitedHandling = nsRuleWalker::eRelevantLinkUnvisited;
}
void SetHaveRelevantLink() { mHaveRelevantLink = true; }
bool HaveRelevantLink() const { return mHaveRelevantLink; }
nsRuleWalker::VisitedHandlingType VisitedHandling() const
{
return mVisitedHandling;
}
// Is this matching operation for the creation of a style context?
// (If it is, we need to set slow selector bits on nodes indicating
// that certain restyling needs to happen.)
const bool mForStyling;
private:
// When mVisitedHandling is eRelevantLinkUnvisited, this is set to true if a
// relevant link (see explanation in definition of VisitedHandling enum) was
// encountered during the matching process, which means that matching needs
// to be rerun with eRelevantLinkVisited. Otherwise, its behavior is
// undefined (it might get set appropriately, or might not).
bool mHaveRelevantLink;
// How matching should be performed. See the documentation for
// nsRuleWalker::VisitedHandlingType.
nsRuleWalker::VisitedHandlingType mVisitedHandling;
public:
// The document we're working with.
nsIDocument* const mDocument;
// Root of scoped stylesheet (set and unset by the supplier of the
// scoped stylesheet).
nsIContent* mScopedRoot;
// Whether our document is HTML (as opposed to XML of some sort,
// including XHTML).
// XXX XBL2 issue: Should we be caching this? What should it be for XBL2?
const bool mIsHTMLDocument;
// Possibly remove use of mCompatMode in SelectorMatches?
// XXX XBL2 issue: Should we be caching this? What should it be for XBL2?
const nsCompatibility mCompatMode;
// The nth-index cache we should use
nsNthIndexCache mNthIndexCache;
// An ancestor filter
AncestorFilter mAncestorFilter;
// Whether this document is using PB mode
bool mUsingPrivateBrowsing;
enum MatchVisited {
eNeverMatchVisited,
eMatchVisitedDefault
};
// Constructor to use when creating a tree match context for styling
TreeMatchContext(bool aForStyling,
nsRuleWalker::VisitedHandlingType aVisitedHandling,
nsIDocument* aDocument,
MatchVisited aMatchVisited = eMatchVisitedDefault)
: mForStyling(aForStyling)
, mHaveRelevantLink(false)
, mVisitedHandling(aVisitedHandling)
, mDocument(aDocument)
, mScopedRoot(nullptr)
, mIsHTMLDocument(aDocument->IsHTML())
, mCompatMode(aDocument->GetCompatibilityMode())
, mUsingPrivateBrowsing(false)
{
if (aMatchVisited != eNeverMatchVisited) {
nsCOMPtr<nsISupports> container = mDocument->GetContainer();
if (container) {
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(container);
NS_ASSERTION(loadContext, "Couldn't get loadContext from container; assuming no private browsing.");
if (loadContext) {
mUsingPrivateBrowsing = loadContext->UsePrivateBrowsing();
}
}
}
}
};
struct NS_STACK_CLASS RuleProcessorData {
RuleProcessorData(nsPresContext* aPresContext,
nsRuleWalker* aRuleWalker)
: mPresContext(aPresContext),
mRuleWalker(aRuleWalker)
{
NS_PRECONDITION(mPresContext, "Must have prescontext");
}
nsPresContext* const mPresContext;
nsRuleWalker* const mRuleWalker; // Used to add rules to our results.
};
struct NS_STACK_CLASS ElementDependentRuleProcessorData :
public RuleProcessorData {
ElementDependentRuleProcessorData(nsPresContext* aPresContext,
mozilla::dom::Element* aElement,
nsRuleWalker* aRuleWalker,
TreeMatchContext& aTreeMatchContext)
: RuleProcessorData(aPresContext, aRuleWalker)
, mElement(aElement)
, mTreeMatchContext(aTreeMatchContext)
{
NS_ASSERTION(aElement, "null element leaked into SelectorMatches");
NS_ASSERTION(aElement->OwnerDoc(), "Document-less node here?");
NS_PRECONDITION(aTreeMatchContext.mForStyling == !!aRuleWalker,
"Should be styling if and only if we have a rule walker");
}
mozilla::dom::Element* const mElement; // weak ref, must not be null
TreeMatchContext& mTreeMatchContext;
};
struct NS_STACK_CLASS ElementRuleProcessorData :
public ElementDependentRuleProcessorData {
ElementRuleProcessorData(nsPresContext* aPresContext,
mozilla::dom::Element* aElement,
nsRuleWalker* aRuleWalker,
TreeMatchContext& aTreeMatchContext)
: ElementDependentRuleProcessorData(aPresContext, aElement, aRuleWalker,
aTreeMatchContext)
{
NS_PRECONDITION(aTreeMatchContext.mForStyling, "Styling here!");
NS_PRECONDITION(aRuleWalker, "Must have rule walker");
}
};
struct NS_STACK_CLASS PseudoElementRuleProcessorData :
public ElementDependentRuleProcessorData {
PseudoElementRuleProcessorData(nsPresContext* aPresContext,
mozilla::dom::Element* aParentElement,
nsRuleWalker* aRuleWalker,
nsCSSPseudoElements::Type aPseudoType,
TreeMatchContext& aTreeMatchContext)
: ElementDependentRuleProcessorData(aPresContext, aParentElement, aRuleWalker,
aTreeMatchContext),
mPseudoType(aPseudoType)
{
NS_PRECONDITION(aPseudoType <
nsCSSPseudoElements::ePseudo_PseudoElementCount,
"invalid aPseudoType value");
NS_PRECONDITION(aTreeMatchContext.mForStyling, "Styling here!");
NS_PRECONDITION(aRuleWalker, "Must have rule walker");
}
nsCSSPseudoElements::Type mPseudoType;
};
struct NS_STACK_CLASS AnonBoxRuleProcessorData : public RuleProcessorData {
AnonBoxRuleProcessorData(nsPresContext* aPresContext,
nsIAtom* aPseudoTag,
nsRuleWalker* aRuleWalker)
: RuleProcessorData(aPresContext, aRuleWalker),
mPseudoTag(aPseudoTag)
{
NS_PRECONDITION(aPseudoTag, "Must have pseudo tag");
NS_PRECONDITION(aRuleWalker, "Must have rule walker");
}
nsIAtom* mPseudoTag;
};
#ifdef MOZ_XUL
struct NS_STACK_CLASS XULTreeRuleProcessorData :
public ElementDependentRuleProcessorData {
XULTreeRuleProcessorData(nsPresContext* aPresContext,
mozilla::dom::Element* aParentElement,
nsRuleWalker* aRuleWalker,
nsIAtom* aPseudoTag,
nsICSSPseudoComparator* aComparator,
TreeMatchContext& aTreeMatchContext)
: ElementDependentRuleProcessorData(aPresContext, aParentElement,
aRuleWalker, aTreeMatchContext),
mPseudoTag(aPseudoTag),
mComparator(aComparator)
{
NS_PRECONDITION(aPseudoTag, "null pointer");
NS_PRECONDITION(aRuleWalker, "Must have rule walker");
NS_PRECONDITION(aComparator, "must have a comparator");
NS_PRECONDITION(aTreeMatchContext.mForStyling, "Styling here!");
}
nsIAtom* mPseudoTag;
nsICSSPseudoComparator* mComparator;
};
#endif
struct NS_STACK_CLASS StateRuleProcessorData :
public ElementDependentRuleProcessorData {
StateRuleProcessorData(nsPresContext* aPresContext,
mozilla::dom::Element* aElement,
nsEventStates aStateMask,
TreeMatchContext& aTreeMatchContext)
: ElementDependentRuleProcessorData(aPresContext, aElement, nullptr,
aTreeMatchContext),
mStateMask(aStateMask)
{
NS_PRECONDITION(!aTreeMatchContext.mForStyling, "Not styling here!");
}
const nsEventStates mStateMask; // |HasStateDependentStyle| for which state(s)?
// Constants defined in nsEventStates.h .
};
struct NS_STACK_CLASS AttributeRuleProcessorData :
public ElementDependentRuleProcessorData {
AttributeRuleProcessorData(nsPresContext* aPresContext,
mozilla::dom::Element* aElement,
nsIAtom* aAttribute,
int32_t aModType,
bool aAttrHasChanged,
TreeMatchContext& aTreeMatchContext)
: ElementDependentRuleProcessorData(aPresContext, aElement, nullptr,
aTreeMatchContext),
mAttribute(aAttribute),
mModType(aModType),
mAttrHasChanged(aAttrHasChanged)
{
NS_PRECONDITION(!aTreeMatchContext.mForStyling, "Not styling here!");
}
nsIAtom* mAttribute; // |HasAttributeDependentStyle| for which attribute?
int32_t mModType; // The type of modification (see nsIDOMMutationEvent).
bool mAttrHasChanged; // Whether the attribute has already changed.
};
#endif /* !defined(nsRuleProcessorData_h_) */