1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 * data structures passed to nsIStyleRuleProcessor methods (to pull loop
8 * invariant computations out of the loop)
11 #ifndef nsRuleProcessorData_h_
12 #define nsRuleProcessorData_h_
14 #include "nsChangeHint.h"
15 #include "nsCompatibility.h"
16 #include "nsCSSPseudoElements.h"
17 #include "nsRuleWalker.h"
18 #include "nsNthIndexCache.h"
19 #include "nsILoadContext.h"
20 #include "nsIDocument.h"
21 #include "mozilla/AutoRestore.h"
22 #include "mozilla/BloomFilter.h"
23 #include "mozilla/EventStates.h"
24 #include "mozilla/GuardObjects.h"
29 class nsICSSPseudoComparator
;
31 struct TreeMatchContext
;
34 * An AncestorFilter is used to keep track of ancestors so that we can
35 * quickly tell that a particular selector is not relevant to a given
38 class MOZ_STACK_CLASS AncestorFilter
{
39 friend struct TreeMatchContext
;
41 /* Maintenance of our ancestor state */
42 void PushAncestor(mozilla::dom::Element
*aElement
);
45 /* Check whether we might have an ancestor matching one of the given
46 atom hashes. |hashes| must have length hashListLength */
47 template<size_t hashListLength
>
48 bool MightHaveMatchingAncestor(const uint32_t* aHashes
) const
51 for (size_t i
= 0; i
< hashListLength
&& aHashes
[i
]; ++i
) {
52 if (!mFilter
->mightContain(aHashes
[i
])) {
60 bool HasFilter() const { return mFilter
; }
63 void AssertHasAllAncestors(mozilla::dom::Element
*aElement
) const;
67 // Using 2^12 slots makes the Bloom filter a nice round page in
68 // size, so let's do that. We get a false positive rate of 1% or
69 // less even with several hundred things in the filter. Note that
70 // we allocate the filter lazily, because not all tree match
71 // contexts can use one effectively.
72 typedef mozilla::BloomFilter
<12, nsIAtom
> Filter
;
73 nsAutoPtr
<Filter
> mFilter
;
75 // Stack of indices to pop to. These are indices into mHashes.
76 nsTArray
<uint32_t> mPopTargets
;
78 // List of hashes; this is what we pop using mPopTargets. We store
79 // hashes of our ancestor element tag names, ids, and classes in
81 nsTArray
<uint32_t> mHashes
;
83 // A debug-only stack of Elements for use in assertions
85 nsTArray
<mozilla::dom::Element
*> mElements
;
90 * A |TreeMatchContext| has data about a matching operation. The
91 * data are not node-specific but are invariants of the DOM tree the
92 * nodes being matched against are in.
94 * Most of the members are in parameters to selector matching. The
95 * one out parameter is mHaveRelevantLink. Consumers that use a
96 * TreeMatchContext for more than one matching operation and care
97 * about :visited and mHaveRelevantLink need to
98 * ResetForVisitedMatching() and ResetForUnvisitedMatching() as
101 struct MOZ_STACK_CLASS TreeMatchContext
{
102 // Reset this context for matching for the style-if-:visited.
103 void ResetForVisitedMatching() {
104 NS_PRECONDITION(mForStyling
, "Why is this being called?");
105 mHaveRelevantLink
= false;
106 mVisitedHandling
= nsRuleWalker::eRelevantLinkVisited
;
109 void ResetForUnvisitedMatching() {
110 NS_PRECONDITION(mForStyling
, "Why is this being called?");
111 mHaveRelevantLink
= false;
112 mVisitedHandling
= nsRuleWalker::eRelevantLinkUnvisited
;
115 void SetHaveRelevantLink() { mHaveRelevantLink
= true; }
116 bool HaveRelevantLink() const { return mHaveRelevantLink
; }
118 nsRuleWalker::VisitedHandlingType
VisitedHandling() const
120 return mVisitedHandling
;
123 void AddScopeElement(mozilla::dom::Element
* aElement
) {
124 NS_PRECONDITION(mHaveSpecifiedScope
,
125 "Should be set before calling AddScopeElement()");
126 mScopes
.AppendElement(aElement
);
128 bool IsScopeElement(mozilla::dom::Element
* aElement
) const {
129 return mScopes
.Contains(aElement
);
131 void SetHasSpecifiedScope() {
132 mHaveSpecifiedScope
= true;
134 bool HasSpecifiedScope() const {
135 return mHaveSpecifiedScope
;
139 * Initialize the ancestor filter and list of style scopes. If aElement is
140 * not null, it and all its ancestors will be passed to
141 * mAncestorFilter.PushAncestor and PushStyleScope, starting from the root and
142 * going down the tree. Must only be called for elements in a document.
144 void InitAncestors(mozilla::dom::Element
*aElement
);
147 * Like InitAncestors, but only initializes the style scope list, not the
148 * ancestor filter. May be called for elements outside a document.
150 void InitStyleScopes(mozilla::dom::Element
* aElement
);
152 void PushStyleScope(mozilla::dom::Element
* aElement
)
154 NS_PRECONDITION(aElement
, "aElement must not be null");
155 if (aElement
->IsScopedStyleRoot()) {
156 mStyleScopes
.AppendElement(aElement
);
160 void PopStyleScope(mozilla::dom::Element
* aElement
)
162 NS_PRECONDITION(aElement
, "aElement must not be null");
163 if (mStyleScopes
.SafeLastElement(nullptr) == aElement
) {
164 mStyleScopes
.TruncateLength(mStyleScopes
.Length() - 1);
168 bool PopStyleScopeForSelectorMatching(mozilla::dom::Element
* aElement
)
170 NS_ASSERTION(mForScopedStyle
, "only call PopStyleScopeForSelectorMatching "
171 "when mForScopedStyle is true");
173 if (!mCurrentStyleScope
) {
176 if (mCurrentStyleScope
== aElement
) {
177 mCurrentStyleScope
= nullptr;
183 void AssertHasAllStyleScopes(mozilla::dom::Element
* aElement
) const;
186 bool SetStyleScopeForSelectorMatching(mozilla::dom::Element
* aSubject
,
187 mozilla::dom::Element
* aScope
)
190 AssertHasAllStyleScopes(aSubject
);
193 mForScopedStyle
= !!aScope
;
195 // This is not for a scoped style sheet; return true, as we want
196 // selector matching to proceed.
197 mCurrentStyleScope
= nullptr;
200 if (aScope
== aSubject
) {
201 // Although the subject is the same element as the scope, as soon
202 // as we continue with selector matching up the tree we don't want
203 // to match any more elements. So we return true to indicate that
204 // we want to do the initial selector matching, but set
205 // mCurrentStyleScope to null so that no ancestor elements will match.
206 mCurrentStyleScope
= nullptr;
209 if (mStyleScopes
.Contains(aScope
)) {
210 // mStyleScopes contains all of the scope elements that are ancestors of
211 // aSubject, so if aScope is in mStyleScopes, then we do want selector
212 // matching to proceed.
213 mCurrentStyleScope
= aScope
;
216 // Otherwise, we're not in the scope, and we don't want to proceed
217 // with selector matching.
218 mCurrentStyleScope
= nullptr;
222 bool IsWithinStyleScopeForSelectorMatching() const
224 NS_ASSERTION(mForScopedStyle
, "only call IsWithinScopeForSelectorMatching "
225 "when mForScopedStyle is true");
226 return mCurrentStyleScope
;
229 /* Helper class for maintaining the ancestor state */
230 class MOZ_STACK_CLASS AutoAncestorPusher
{
232 explicit AutoAncestorPusher(TreeMatchContext
& aTreeMatchContext
233 MOZ_GUARD_OBJECT_NOTIFIER_PARAM
)
234 : mPushedAncestor(false)
235 , mPushedStyleScope(false)
236 , mTreeMatchContext(aTreeMatchContext
)
239 MOZ_GUARD_OBJECT_NOTIFIER_INIT
;
242 void PushAncestorAndStyleScope(mozilla::dom::Element
* aElement
) {
243 MOZ_ASSERT(!mElement
);
246 mPushedAncestor
= true;
247 mPushedStyleScope
= true;
248 mTreeMatchContext
.mAncestorFilter
.PushAncestor(aElement
);
249 mTreeMatchContext
.PushStyleScope(aElement
);
253 void PushAncestorAndStyleScope(nsIContent
* aContent
) {
254 if (aContent
&& aContent
->IsElement()) {
255 PushAncestorAndStyleScope(aContent
->AsElement());
259 void PushStyleScope(mozilla::dom::Element
* aElement
) {
260 MOZ_ASSERT(!mElement
);
263 mPushedStyleScope
= true;
264 mTreeMatchContext
.PushStyleScope(aElement
);
268 void PushStyleScope(nsIContent
* aContent
) {
269 if (aContent
&& aContent
->IsElement()) {
270 PushStyleScope(aContent
->AsElement());
274 ~AutoAncestorPusher() {
275 if (mPushedAncestor
) {
276 mTreeMatchContext
.mAncestorFilter
.PopAncestor();
278 if (mPushedStyleScope
) {
279 mTreeMatchContext
.PopStyleScope(mElement
);
284 bool mPushedAncestor
;
285 bool mPushedStyleScope
;
286 TreeMatchContext
& mTreeMatchContext
;
287 mozilla::dom::Element
* mElement
;
288 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
291 /* Helper class for tracking whether we're skipping the ApplyStyleFixups
292 * code for special cases where child element style is modified based on
293 * parent display value.
295 * The optional second parameter aSkipParentDisplayBasedStyleFixup allows
296 * this class to be instantiated but only conditionally activated (e.g.
297 * in cases where we may or may not want to be skipping flex/grid-item
298 * style fixup for a particular chunk of code).
300 class MOZ_STACK_CLASS AutoParentDisplayBasedStyleFixupSkipper
{
302 explicit AutoParentDisplayBasedStyleFixupSkipper(TreeMatchContext
& aTreeMatchContext
,
303 bool aSkipParentDisplayBasedStyleFixup
= true
304 MOZ_GUARD_OBJECT_NOTIFIER_PARAM
)
305 : mAutoRestorer(aTreeMatchContext
.mSkippingParentDisplayBasedStyleFixup
)
307 MOZ_GUARD_OBJECT_NOTIFIER_INIT
;
308 if (aSkipParentDisplayBasedStyleFixup
) {
309 aTreeMatchContext
.mSkippingParentDisplayBasedStyleFixup
= true;
314 mozilla::AutoRestore
<bool> mAutoRestorer
;
315 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
318 // Is this matching operation for the creation of a style context?
319 // (If it is, we need to set slow selector bits on nodes indicating
320 // that certain restyling needs to happen.)
321 const bool mForStyling
;
324 // When mVisitedHandling is eRelevantLinkUnvisited, this is set to true if a
325 // relevant link (see explanation in definition of VisitedHandling enum) was
326 // encountered during the matching process, which means that matching needs
327 // to be rerun with eRelevantLinkVisited. Otherwise, its behavior is
328 // undefined (it might get set appropriately, or might not).
329 bool mHaveRelevantLink
;
331 // If true, then our contextual reference element set is specified,
332 // and is given by mScopes.
333 bool mHaveSpecifiedScope
;
335 // How matching should be performed. See the documentation for
336 // nsRuleWalker::VisitedHandlingType.
337 nsRuleWalker::VisitedHandlingType mVisitedHandling
;
339 // For matching :scope
340 nsAutoTArray
<mozilla::dom::Element
*, 1> mScopes
;
342 // The document we're working with.
343 nsIDocument
* const mDocument
;
345 // Root of scoped stylesheet (set and unset by the supplier of the
346 // scoped stylesheet).
347 nsIContent
* mScopedRoot
;
349 // Whether our document is HTML (as opposed to XML of some sort,
351 // XXX XBL2 issue: Should we be caching this? What should it be for XBL2?
352 const bool mIsHTMLDocument
;
354 // Possibly remove use of mCompatMode in SelectorMatches?
355 // XXX XBL2 issue: Should we be caching this? What should it be for XBL2?
356 const nsCompatibility mCompatMode
;
358 // The nth-index cache we should use
359 nsNthIndexCache mNthIndexCache
;
361 // An ancestor filter
362 AncestorFilter mAncestorFilter
;
364 // Whether this document is using PB mode
365 bool mUsingPrivateBrowsing
;
367 // Whether we're currently skipping the part of ApplyStyleFixups that changes
368 // style of child elements based on their parent's display value
369 // (e.g. for children of elements that have a mandatory frame-type for which
370 // we ignore "display:flex/grid").
371 bool mSkippingParentDisplayBasedStyleFixup
;
373 // Whether this TreeMatchContext is being used with an nsCSSRuleProcessor
374 // for an HTML5 scoped style sheet.
375 bool mForScopedStyle
;
382 // List of ancestor elements that define a style scope (due to having a
383 // <style scoped> child).
384 nsAutoTArray
<mozilla::dom::Element
*, 1> mStyleScopes
;
386 // The current style scope element for selector matching.
387 mozilla::dom::Element
* mCurrentStyleScope
;
389 // Constructor to use when creating a tree match context for styling
390 TreeMatchContext(bool aForStyling
,
391 nsRuleWalker::VisitedHandlingType aVisitedHandling
,
392 nsIDocument
* aDocument
,
393 MatchVisited aMatchVisited
= eMatchVisitedDefault
)
394 : mForStyling(aForStyling
)
395 , mHaveRelevantLink(false)
396 , mHaveSpecifiedScope(false)
397 , mVisitedHandling(aVisitedHandling
)
398 , mDocument(aDocument
)
399 , mScopedRoot(nullptr)
400 , mIsHTMLDocument(aDocument
->IsHTML())
401 , mCompatMode(aDocument
->GetCompatibilityMode())
402 , mUsingPrivateBrowsing(false)
403 , mSkippingParentDisplayBasedStyleFixup(false)
404 , mForScopedStyle(false)
405 , mCurrentStyleScope(nullptr)
407 if (aMatchVisited
!= eNeverMatchVisited
) {
408 nsCOMPtr
<nsISupports
> container
= mDocument
->GetContainer();
410 nsCOMPtr
<nsILoadContext
> loadContext
= do_QueryInterface(container
);
411 NS_ASSERTION(loadContext
, "Couldn't get loadContext from container; assuming no private browsing.");
413 mUsingPrivateBrowsing
= loadContext
->UsePrivateBrowsing();
420 struct MOZ_STACK_CLASS RuleProcessorData
{
421 RuleProcessorData(nsPresContext
* aPresContext
,
422 nsRuleWalker
* aRuleWalker
)
423 : mPresContext(aPresContext
),
424 mRuleWalker(aRuleWalker
),
427 NS_PRECONDITION(mPresContext
, "Must have prescontext");
430 nsPresContext
* const mPresContext
;
431 nsRuleWalker
* const mRuleWalker
; // Used to add rules to our results.
432 mozilla::dom::Element
* mScope
;
435 struct MOZ_STACK_CLASS ElementDependentRuleProcessorData
:
436 public RuleProcessorData
{
437 ElementDependentRuleProcessorData(nsPresContext
* aPresContext
,
438 mozilla::dom::Element
* aElement
,
439 nsRuleWalker
* aRuleWalker
,
440 TreeMatchContext
& aTreeMatchContext
)
441 : RuleProcessorData(aPresContext
, aRuleWalker
)
443 , mTreeMatchContext(aTreeMatchContext
)
445 NS_ASSERTION(aElement
, "null element leaked into SelectorMatches");
446 NS_ASSERTION(aElement
->OwnerDoc(), "Document-less node here?");
447 NS_PRECONDITION(aTreeMatchContext
.mForStyling
== !!aRuleWalker
,
448 "Should be styling if and only if we have a rule walker");
451 mozilla::dom::Element
* const mElement
; // weak ref, must not be null
452 TreeMatchContext
& mTreeMatchContext
;
455 struct MOZ_STACK_CLASS ElementRuleProcessorData
:
456 public ElementDependentRuleProcessorData
{
457 ElementRuleProcessorData(nsPresContext
* aPresContext
,
458 mozilla::dom::Element
* aElement
,
459 nsRuleWalker
* aRuleWalker
,
460 TreeMatchContext
& aTreeMatchContext
)
461 : ElementDependentRuleProcessorData(aPresContext
, aElement
, aRuleWalker
,
464 NS_PRECONDITION(aTreeMatchContext
.mForStyling
, "Styling here!");
465 NS_PRECONDITION(aRuleWalker
, "Must have rule walker");
469 struct MOZ_STACK_CLASS PseudoElementRuleProcessorData
:
470 public ElementDependentRuleProcessorData
{
471 PseudoElementRuleProcessorData(nsPresContext
* aPresContext
,
472 mozilla::dom::Element
* aParentElement
,
473 nsRuleWalker
* aRuleWalker
,
474 nsCSSPseudoElements::Type aPseudoType
,
475 TreeMatchContext
& aTreeMatchContext
,
476 mozilla::dom::Element
* aPseudoElement
)
477 : ElementDependentRuleProcessorData(aPresContext
, aParentElement
, aRuleWalker
,
479 mPseudoType(aPseudoType
),
480 mPseudoElement(aPseudoElement
)
482 NS_PRECONDITION(aPseudoType
<
483 nsCSSPseudoElements::ePseudo_PseudoElementCount
,
484 "invalid aPseudoType value");
485 NS_PRECONDITION(aTreeMatchContext
.mForStyling
, "Styling here!");
486 NS_PRECONDITION(aRuleWalker
, "Must have rule walker");
489 nsCSSPseudoElements::Type mPseudoType
;
490 mozilla::dom::Element
* const mPseudoElement
; // weak ref
493 struct MOZ_STACK_CLASS AnonBoxRuleProcessorData
: public RuleProcessorData
{
494 AnonBoxRuleProcessorData(nsPresContext
* aPresContext
,
496 nsRuleWalker
* aRuleWalker
)
497 : RuleProcessorData(aPresContext
, aRuleWalker
),
498 mPseudoTag(aPseudoTag
)
500 NS_PRECONDITION(aPseudoTag
, "Must have pseudo tag");
501 NS_PRECONDITION(aRuleWalker
, "Must have rule walker");
508 struct MOZ_STACK_CLASS XULTreeRuleProcessorData
:
509 public ElementDependentRuleProcessorData
{
510 XULTreeRuleProcessorData(nsPresContext
* aPresContext
,
511 mozilla::dom::Element
* aParentElement
,
512 nsRuleWalker
* aRuleWalker
,
514 nsICSSPseudoComparator
* aComparator
,
515 TreeMatchContext
& aTreeMatchContext
)
516 : ElementDependentRuleProcessorData(aPresContext
, aParentElement
,
517 aRuleWalker
, aTreeMatchContext
),
518 mPseudoTag(aPseudoTag
),
519 mComparator(aComparator
)
521 NS_PRECONDITION(aPseudoTag
, "null pointer");
522 NS_PRECONDITION(aRuleWalker
, "Must have rule walker");
523 NS_PRECONDITION(aComparator
, "must have a comparator");
524 NS_PRECONDITION(aTreeMatchContext
.mForStyling
, "Styling here!");
528 nsICSSPseudoComparator
* mComparator
;
532 struct MOZ_STACK_CLASS StateRuleProcessorData
:
533 public ElementDependentRuleProcessorData
{
534 StateRuleProcessorData(nsPresContext
* aPresContext
,
535 mozilla::dom::Element
* aElement
,
536 mozilla::EventStates aStateMask
,
537 TreeMatchContext
& aTreeMatchContext
)
538 : ElementDependentRuleProcessorData(aPresContext
, aElement
, nullptr,
540 mStateMask(aStateMask
)
542 NS_PRECONDITION(!aTreeMatchContext
.mForStyling
, "Not styling here!");
544 // |HasStateDependentStyle| for which state(s)?
545 // Constants defined in mozilla/EventStates.h .
546 const mozilla::EventStates mStateMask
;
549 struct MOZ_STACK_CLASS PseudoElementStateRuleProcessorData
:
550 public StateRuleProcessorData
{
551 PseudoElementStateRuleProcessorData(nsPresContext
* aPresContext
,
552 mozilla::dom::Element
* aElement
,
553 mozilla::EventStates aStateMask
,
554 nsCSSPseudoElements::Type aPseudoType
,
555 TreeMatchContext
& aTreeMatchContext
,
556 mozilla::dom::Element
* aPseudoElement
)
557 : StateRuleProcessorData(aPresContext
, aElement
, aStateMask
,
559 mPseudoType(aPseudoType
),
560 mPseudoElement(aPseudoElement
)
562 NS_PRECONDITION(!aTreeMatchContext
.mForStyling
, "Not styling here!");
565 // We kind of want to inherit from both StateRuleProcessorData and
566 // PseudoElementRuleProcessorData. Instead we've just copied those
567 // members from PseudoElementRuleProcessorData to this struct.
568 nsCSSPseudoElements::Type mPseudoType
;
569 mozilla::dom::Element
* const mPseudoElement
; // weak ref
572 struct MOZ_STACK_CLASS AttributeRuleProcessorData
:
573 public ElementDependentRuleProcessorData
{
574 AttributeRuleProcessorData(nsPresContext
* aPresContext
,
575 mozilla::dom::Element
* aElement
,
578 bool aAttrHasChanged
,
579 TreeMatchContext
& aTreeMatchContext
)
580 : ElementDependentRuleProcessorData(aPresContext
, aElement
, nullptr,
582 mAttribute(aAttribute
),
584 mAttrHasChanged(aAttrHasChanged
)
586 NS_PRECONDITION(!aTreeMatchContext
.mForStyling
, "Not styling here!");
588 nsIAtom
* mAttribute
; // |HasAttributeDependentStyle| for which attribute?
589 int32_t mModType
; // The type of modification (see nsIDOMMutationEvent).
590 bool mAttrHasChanged
; // Whether the attribute has already changed.
593 #endif /* !defined(nsRuleProcessorData_h_) */