summaryrefslogtreecommitdiffstats
path: root/layout/style/nsRuleProcessorData.h
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/nsRuleProcessorData.h')
-rw-r--r--layout/style/nsRuleProcessorData.h595
1 files changed, 595 insertions, 0 deletions
diff --git a/layout/style/nsRuleProcessorData.h b/layout/style/nsRuleProcessorData.h
new file mode 100644
index 000000000..6f8cba895
--- /dev/null
+++ b/layout/style/nsRuleProcessorData.h
@@ -0,0 +1,595 @@
+/* -*- 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 "nsAutoPtr.h"
+#include "nsChangeHint.h"
+#include "nsCompatibility.h"
+#include "nsCSSPseudoElements.h"
+#include "nsRuleWalker.h"
+#include "nsNthIndexCache.h"
+#include "nsILoadContext.h"
+#include "nsIDocument.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/BloomFilter.h"
+#include "mozilla/EventStates.h"
+#include "mozilla/GuardObjects.h"
+#include "mozilla/dom/Element.h"
+
+class nsIAtom;
+class nsIContent;
+class nsICSSPseudoComparator;
+struct TreeMatchContext;
+
+/**
+ * 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 MOZ_STACK_CLASS AncestorFilter {
+ friend struct TreeMatchContext;
+ public:
+ /* Maintenance of our ancestor state */
+ void PushAncestor(mozilla::dom::Element *aElement);
+ void PopAncestor();
+
+ /* 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 MOZ_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;
+ }
+
+ void AddScopeElement(mozilla::dom::Element* aElement) {
+ NS_PRECONDITION(mHaveSpecifiedScope,
+ "Should be set before calling AddScopeElement()");
+ mScopes.AppendElement(aElement);
+ }
+ bool IsScopeElement(mozilla::dom::Element* aElement) const {
+ return mScopes.Contains(aElement);
+ }
+ void SetHasSpecifiedScope() {
+ mHaveSpecifiedScope = true;
+ }
+ bool HasSpecifiedScope() const {
+ return mHaveSpecifiedScope;
+ }
+
+ /**
+ * Initialize the ancestor filter and list of style scopes. If aElement is
+ * not null, it and all its ancestors will be passed to
+ * mAncestorFilter.PushAncestor and PushStyleScope, starting from the root and
+ * going down the tree. Must only be called for elements in a document.
+ */
+ void InitAncestors(mozilla::dom::Element *aElement);
+
+ /**
+ * Like InitAncestors, but only initializes the style scope list, not the
+ * ancestor filter. May be called for elements outside a document.
+ */
+ void InitStyleScopes(mozilla::dom::Element* aElement);
+
+ void PushStyleScope(mozilla::dom::Element* aElement)
+ {
+ NS_PRECONDITION(aElement, "aElement must not be null");
+ if (aElement->IsScopedStyleRoot()) {
+ mStyleScopes.AppendElement(aElement);
+ }
+ }
+
+ void PopStyleScope(mozilla::dom::Element* aElement)
+ {
+ NS_PRECONDITION(aElement, "aElement must not be null");
+ if (mStyleScopes.SafeLastElement(nullptr) == aElement) {
+ mStyleScopes.TruncateLength(mStyleScopes.Length() - 1);
+ }
+ }
+
+ bool PopStyleScopeForSelectorMatching(mozilla::dom::Element* aElement)
+ {
+ NS_ASSERTION(mForScopedStyle, "only call PopStyleScopeForSelectorMatching "
+ "when mForScopedStyle is true");
+
+ if (!mCurrentStyleScope) {
+ return false;
+ }
+ if (mCurrentStyleScope == aElement) {
+ mCurrentStyleScope = nullptr;
+ }
+ return true;
+ }
+
+#ifdef DEBUG
+ void AssertHasAllStyleScopes(mozilla::dom::Element* aElement) const;
+#endif
+
+ bool SetStyleScopeForSelectorMatching(mozilla::dom::Element* aSubject,
+ mozilla::dom::Element* aScope)
+ {
+#ifdef DEBUG
+ AssertHasAllStyleScopes(aSubject);
+#endif
+
+ mForScopedStyle = !!aScope;
+ if (!aScope) {
+ // This is not for a scoped style sheet; return true, as we want
+ // selector matching to proceed.
+ mCurrentStyleScope = nullptr;
+ return true;
+ }
+ if (aScope == aSubject) {
+ // Although the subject is the same element as the scope, as soon
+ // as we continue with selector matching up the tree we don't want
+ // to match any more elements. So we return true to indicate that
+ // we want to do the initial selector matching, but set
+ // mCurrentStyleScope to null so that no ancestor elements will match.
+ mCurrentStyleScope = nullptr;
+ return true;
+ }
+ if (mStyleScopes.Contains(aScope)) {
+ // mStyleScopes contains all of the scope elements that are ancestors of
+ // aSubject, so if aScope is in mStyleScopes, then we do want selector
+ // matching to proceed.
+ mCurrentStyleScope = aScope;
+ return true;
+ }
+ // Otherwise, we're not in the scope, and we don't want to proceed
+ // with selector matching.
+ mCurrentStyleScope = nullptr;
+ return false;
+ }
+
+ bool IsWithinStyleScopeForSelectorMatching() const
+ {
+ NS_ASSERTION(mForScopedStyle, "only call IsWithinScopeForSelectorMatching "
+ "when mForScopedStyle is true");
+ return mCurrentStyleScope;
+ }
+
+ /* Helper class for maintaining the ancestor state */
+ class MOZ_RAII AutoAncestorPusher {
+ public:
+ explicit AutoAncestorPusher(TreeMatchContext& aTreeMatchContext
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mPushedAncestor(false)
+ , mPushedStyleScope(false)
+ , mTreeMatchContext(aTreeMatchContext)
+ , mElement(nullptr)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ }
+
+ void PushAncestorAndStyleScope(mozilla::dom::Element* aElement) {
+ MOZ_ASSERT(!mElement);
+ if (aElement) {
+ mElement = aElement;
+ mPushedAncestor = true;
+ mPushedStyleScope = true;
+ mTreeMatchContext.mAncestorFilter.PushAncestor(aElement);
+ mTreeMatchContext.PushStyleScope(aElement);
+ }
+ }
+
+ void PushAncestorAndStyleScope(nsIContent* aContent) {
+ if (aContent && aContent->IsElement()) {
+ PushAncestorAndStyleScope(aContent->AsElement());
+ }
+ }
+
+ void PushStyleScope(mozilla::dom::Element* aElement) {
+ MOZ_ASSERT(!mElement);
+ if (aElement) {
+ mElement = aElement;
+ mPushedStyleScope = true;
+ mTreeMatchContext.PushStyleScope(aElement);
+ }
+ }
+
+ void PushStyleScope(nsIContent* aContent) {
+ if (aContent && aContent->IsElement()) {
+ PushStyleScope(aContent->AsElement());
+ }
+ }
+
+ ~AutoAncestorPusher() {
+ if (mPushedAncestor) {
+ mTreeMatchContext.mAncestorFilter.PopAncestor();
+ }
+ if (mPushedStyleScope) {
+ mTreeMatchContext.PopStyleScope(mElement);
+ }
+ }
+
+ private:
+ bool mPushedAncestor;
+ bool mPushedStyleScope;
+ TreeMatchContext& mTreeMatchContext;
+ mozilla::dom::Element* mElement;
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+ };
+
+ /* Helper class for tracking whether we're skipping the ApplyStyleFixups
+ * code for special cases where child element style is modified based on
+ * parent display value.
+ *
+ * The optional second parameter aSkipParentDisplayBasedStyleFixup allows
+ * this class to be instantiated but only conditionally activated (e.g.
+ * in cases where we may or may not want to be skipping flex/grid-item
+ * style fixup for a particular chunk of code).
+ */
+ class MOZ_RAII AutoParentDisplayBasedStyleFixupSkipper {
+ public:
+ explicit AutoParentDisplayBasedStyleFixupSkipper(TreeMatchContext& aTreeMatchContext,
+ bool aSkipParentDisplayBasedStyleFixup = true
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mAutoRestorer(aTreeMatchContext.mSkippingParentDisplayBasedStyleFixup)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ if (aSkipParentDisplayBasedStyleFixup) {
+ aTreeMatchContext.mSkippingParentDisplayBasedStyleFixup = true;
+ }
+ }
+
+ private:
+ mozilla::AutoRestore<bool> mAutoRestorer;
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+ };
+
+ // 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;
+
+ // If true, then our contextual reference element set is specified,
+ // and is given by mScopes.
+ bool mHaveSpecifiedScope;
+
+ // How matching should be performed. See the documentation for
+ // nsRuleWalker::VisitedHandlingType.
+ nsRuleWalker::VisitedHandlingType mVisitedHandling;
+
+ // For matching :scope
+ AutoTArray<mozilla::dom::Element*, 1> mScopes;
+ 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;
+
+ // Whether we're currently skipping the part of ApplyStyleFixups that changes
+ // style of child elements based on their parent's display value
+ // (e.g. for children of elements that have a mandatory frame-type for which
+ // we ignore "display:flex/grid").
+ bool mSkippingParentDisplayBasedStyleFixup;
+
+ // Whether this TreeMatchContext is being used with an nsCSSRuleProcessor
+ // for an HTML5 scoped style sheet.
+ bool mForScopedStyle;
+
+ enum MatchVisited {
+ eNeverMatchVisited,
+ eMatchVisitedDefault
+ };
+
+ // List of ancestor elements that define a style scope (due to having a
+ // <style scoped> child).
+ AutoTArray<mozilla::dom::Element*, 1> mStyleScopes;
+
+ // The current style scope element for selector matching.
+ mozilla::dom::Element* mCurrentStyleScope;
+
+ // 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)
+ , mHaveSpecifiedScope(false)
+ , mVisitedHandling(aVisitedHandling)
+ , mDocument(aDocument)
+ , mScopedRoot(nullptr)
+ , mIsHTMLDocument(aDocument->IsHTMLDocument())
+ , mCompatMode(aDocument->GetCompatibilityMode())
+ , mUsingPrivateBrowsing(false)
+ , mSkippingParentDisplayBasedStyleFixup(false)
+ , mForScopedStyle(false)
+ , mCurrentStyleScope(nullptr)
+ {
+ if (aMatchVisited != eNeverMatchVisited) {
+ nsILoadContext* loadContext = mDocument->GetLoadContext();
+ if (loadContext) {
+ mUsingPrivateBrowsing = loadContext->UsePrivateBrowsing();
+ }
+ }
+ }
+};
+
+struct MOZ_STACK_CLASS RuleProcessorData {
+ RuleProcessorData(nsPresContext* aPresContext,
+ nsRuleWalker* aRuleWalker)
+ : mPresContext(aPresContext),
+ mRuleWalker(aRuleWalker),
+ mScope(nullptr)
+ {
+ NS_PRECONDITION(mPresContext, "Must have prescontext");
+ }
+
+ nsPresContext* const mPresContext;
+ nsRuleWalker* const mRuleWalker; // Used to add rules to our results.
+ mozilla::dom::Element* mScope;
+};
+
+struct MOZ_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 MOZ_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 MOZ_STACK_CLASS PseudoElementRuleProcessorData :
+ public ElementDependentRuleProcessorData {
+ PseudoElementRuleProcessorData(nsPresContext* aPresContext,
+ mozilla::dom::Element* aParentElement,
+ nsRuleWalker* aRuleWalker,
+ mozilla::CSSPseudoElementType aPseudoType,
+ TreeMatchContext& aTreeMatchContext,
+ mozilla::dom::Element* aPseudoElement)
+ : ElementDependentRuleProcessorData(aPresContext, aParentElement, aRuleWalker,
+ aTreeMatchContext),
+ mPseudoType(aPseudoType),
+ mPseudoElement(aPseudoElement)
+ {
+ NS_PRECONDITION(aPseudoType < mozilla::CSSPseudoElementType::Count,
+ "invalid aPseudoType value");
+ NS_PRECONDITION(aTreeMatchContext.mForStyling, "Styling here!");
+ NS_PRECONDITION(aRuleWalker, "Must have rule walker");
+ }
+
+ mozilla::CSSPseudoElementType mPseudoType;
+ mozilla::dom::Element* const mPseudoElement; // weak ref
+};
+
+struct MOZ_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 MOZ_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 MOZ_STACK_CLASS StateRuleProcessorData :
+ public ElementDependentRuleProcessorData {
+ StateRuleProcessorData(nsPresContext* aPresContext,
+ mozilla::dom::Element* aElement,
+ mozilla::EventStates aStateMask,
+ TreeMatchContext& aTreeMatchContext)
+ : ElementDependentRuleProcessorData(aPresContext, aElement, nullptr,
+ aTreeMatchContext),
+ mStateMask(aStateMask)
+ {
+ NS_PRECONDITION(!aTreeMatchContext.mForStyling, "Not styling here!");
+ }
+ // |HasStateDependentStyle| for which state(s)?
+ // Constants defined in mozilla/EventStates.h .
+ const mozilla::EventStates mStateMask;
+};
+
+struct MOZ_STACK_CLASS PseudoElementStateRuleProcessorData :
+ public StateRuleProcessorData {
+ PseudoElementStateRuleProcessorData(nsPresContext* aPresContext,
+ mozilla::dom::Element* aElement,
+ mozilla::EventStates aStateMask,
+ mozilla::CSSPseudoElementType aPseudoType,
+ TreeMatchContext& aTreeMatchContext,
+ mozilla::dom::Element* aPseudoElement)
+ : StateRuleProcessorData(aPresContext, aElement, aStateMask,
+ aTreeMatchContext),
+ mPseudoType(aPseudoType),
+ mPseudoElement(aPseudoElement)
+ {
+ NS_PRECONDITION(!aTreeMatchContext.mForStyling, "Not styling here!");
+ }
+
+ // We kind of want to inherit from both StateRuleProcessorData and
+ // PseudoElementRuleProcessorData. Instead we've just copied those
+ // members from PseudoElementRuleProcessorData to this struct.
+ mozilla::CSSPseudoElementType mPseudoType;
+ mozilla::dom::Element* const mPseudoElement; // weak ref
+};
+
+struct MOZ_STACK_CLASS AttributeRuleProcessorData :
+ public ElementDependentRuleProcessorData {
+ AttributeRuleProcessorData(nsPresContext* aPresContext,
+ mozilla::dom::Element* aElement,
+ int32_t aNameSpaceID,
+ nsIAtom* aAttribute,
+ int32_t aModType,
+ bool aAttrHasChanged,
+ const nsAttrValue* aOtherValue,
+ TreeMatchContext& aTreeMatchContext)
+ : ElementDependentRuleProcessorData(aPresContext, aElement, nullptr,
+ aTreeMatchContext),
+ mNameSpaceID(aNameSpaceID),
+ mAttribute(aAttribute),
+ mOtherValue(aOtherValue),
+ mModType(aModType),
+ mAttrHasChanged(aAttrHasChanged)
+ {
+ NS_PRECONDITION(!aTreeMatchContext.mForStyling, "Not styling here!");
+ }
+ int32_t mNameSpaceID; // Namespace of the attribute involved.
+ nsIAtom* mAttribute; // |HasAttributeDependentStyle| for which attribute?
+ // non-null if we have the value.
+ const nsAttrValue* mOtherValue;
+ int32_t mModType; // The type of modification (see nsIDOMMutationEvent).
+ bool mAttrHasChanged; // Whether the attribute has already changed.
+};
+
+#endif /* !defined(nsRuleProcessorData_h_) */