From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- layout/style/nsRuleProcessorData.h | 595 +++++++++++++++++++++++++++++++++++++ 1 file changed, 595 insertions(+) create mode 100644 layout/style/nsRuleProcessorData.h (limited to 'layout/style/nsRuleProcessorData.h') 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 + 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 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 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 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 + //