/* -*- 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 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 mFilter; // Stack of indices to pop to. These are indices into mHashes. nsTArray 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 mHashes; // A debug-only stack of Elements for use in assertions #ifdef DEBUG nsTArray 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 container = mDocument->GetContainer(); if (container) { nsCOMPtr loadContext = do_QueryInterface(container); NS_ASSERTION(loadContext, "Couldn't get loadContext from container; assuming no private browsing."); if (loadContext) { mUsingPrivateBrowsing = loadContext->UsePrivateBrowsing(); } } } } }; // The implementation of the constructor and destructor are currently in // nsCSSRuleProcessor.cpp. struct NS_STACK_CLASS RuleProcessorData { RuleProcessorData(nsPresContext* aPresContext, mozilla::dom::Element* aElement, nsRuleWalker* aRuleWalker, TreeMatchContext& aTreeMatchContext) : mPresContext(aPresContext) , mElement(aElement) , mRuleWalker(aRuleWalker) , 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"); } nsPresContext* const mPresContext; mozilla::dom::Element* const mElement; // weak ref, must not be null nsRuleWalker* const mRuleWalker; // Used to add rules to our results. TreeMatchContext& mTreeMatchContext; }; struct NS_STACK_CLASS ElementRuleProcessorData : public RuleProcessorData { ElementRuleProcessorData(nsPresContext* aPresContext, mozilla::dom::Element* aElement, nsRuleWalker* aRuleWalker, TreeMatchContext& aTreeMatchContext) : RuleProcessorData(aPresContext, aElement, aRuleWalker, aTreeMatchContext) { NS_PRECONDITION(aPresContext, "null pointer"); NS_PRECONDITION(aRuleWalker, "null pointer"); NS_PRECONDITION(aTreeMatchContext.mForStyling, "Styling here!"); } }; struct NS_STACK_CLASS PseudoElementRuleProcessorData : public RuleProcessorData { PseudoElementRuleProcessorData(nsPresContext* aPresContext, mozilla::dom::Element* aParentElement, nsRuleWalker* aRuleWalker, nsCSSPseudoElements::Type aPseudoType, TreeMatchContext& aTreeMatchContext) : RuleProcessorData(aPresContext, aParentElement, aRuleWalker, aTreeMatchContext), mPseudoType(aPseudoType) { NS_PRECONDITION(aPresContext, "null pointer"); NS_PRECONDITION(aPseudoType < nsCSSPseudoElements::ePseudo_PseudoElementCount, "null pointer"); NS_PRECONDITION(aRuleWalker, "null pointer"); NS_PRECONDITION(aTreeMatchContext.mForStyling, "Styling here!"); } nsCSSPseudoElements::Type mPseudoType; }; struct NS_STACK_CLASS AnonBoxRuleProcessorData { AnonBoxRuleProcessorData(nsPresContext* aPresContext, nsIAtom* aPseudoTag, nsRuleWalker* aRuleWalker) : mPresContext(aPresContext), mPseudoTag(aPseudoTag), mRuleWalker(aRuleWalker) { NS_PRECONDITION(mPresContext, "Must have prescontext"); NS_PRECONDITION(aPseudoTag, "Must have pseudo tag"); NS_PRECONDITION(aRuleWalker, "Must have rule walker"); } nsPresContext* mPresContext; nsIAtom* mPseudoTag; nsRuleWalker* mRuleWalker; }; #ifdef MOZ_XUL struct NS_STACK_CLASS XULTreeRuleProcessorData : public RuleProcessorData { XULTreeRuleProcessorData(nsPresContext* aPresContext, mozilla::dom::Element* aParentElement, nsRuleWalker* aRuleWalker, nsIAtom* aPseudoTag, nsICSSPseudoComparator* aComparator, TreeMatchContext& aTreeMatchContext) : RuleProcessorData(aPresContext, aParentElement, aRuleWalker, aTreeMatchContext), mPseudoTag(aPseudoTag), mComparator(aComparator) { NS_PRECONDITION(aPresContext, "null pointer"); NS_PRECONDITION(aPseudoTag, "null pointer"); NS_PRECONDITION(aRuleWalker, "null pointer"); NS_PRECONDITION(aComparator, "must have a comparator"); NS_PRECONDITION(aTreeMatchContext.mForStyling, "Styling here!"); } nsIAtom* mPseudoTag; nsICSSPseudoComparator* mComparator; }; #endif struct NS_STACK_CLASS StateRuleProcessorData : public RuleProcessorData { StateRuleProcessorData(nsPresContext* aPresContext, mozilla::dom::Element* aElement, nsEventStates aStateMask, TreeMatchContext& aTreeMatchContext) : RuleProcessorData(aPresContext, aElement, nullptr, aTreeMatchContext), mStateMask(aStateMask) { NS_PRECONDITION(aPresContext, "null pointer"); 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 RuleProcessorData { AttributeRuleProcessorData(nsPresContext* aPresContext, mozilla::dom::Element* aElement, nsIAtom* aAttribute, PRInt32 aModType, bool aAttrHasChanged, TreeMatchContext& aTreeMatchContext) : RuleProcessorData(aPresContext, aElement, nullptr, aTreeMatchContext), mAttribute(aAttribute), mModType(aModType), mAttrHasChanged(aAttrHasChanged) { NS_PRECONDITION(aPresContext, "null pointer"); NS_PRECONDITION(!aTreeMatchContext.mForStyling, "Not styling here!"); } nsIAtom* mAttribute; // |HasAttributeDependentStyle| for which attribute? PRInt32 mModType; // The type of modification (see nsIDOMMutationEvent). bool mAttrHasChanged; // Whether the attribute has already changed. }; #endif /* !defined(nsRuleProcessorData_h_) */