Bumping manifests a=b2g-bump
[gecko.git] / layout / style / nsRuleProcessorData.h
blob1e68c0f28faac767ef259cd4d1b92275e5403c37
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/. */
6 /*
7 * data structures passed to nsIStyleRuleProcessor methods (to pull loop
8 * invariant computations out of the loop)
9 */
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"
26 class nsAttrValue;
27 class nsIAtom;
28 class nsIContent;
29 class nsICSSPseudoComparator;
30 class nsIStyleSheet;
31 struct TreeMatchContext;
33 /**
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
36 * element.
38 class MOZ_STACK_CLASS AncestorFilter {
39 friend struct TreeMatchContext;
40 public:
41 /* Maintenance of our ancestor state */
42 void PushAncestor(mozilla::dom::Element *aElement);
43 void PopAncestor();
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
50 MOZ_ASSERT(mFilter);
51 for (size_t i = 0; i < hashListLength && aHashes[i]; ++i) {
52 if (!mFilter->mightContain(aHashes[i])) {
53 return false;
57 return true;
60 bool HasFilter() const { return mFilter; }
62 #ifdef DEBUG
63 void AssertHasAllAncestors(mozilla::dom::Element *aElement) const;
64 #endif
66 private:
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
80 // here.
81 nsTArray<uint32_t> mHashes;
83 // A debug-only stack of Elements for use in assertions
84 #ifdef DEBUG
85 nsTArray<mozilla::dom::Element*> mElements;
86 #endif
89 /**
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
99 * needed.
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) {
174 return false;
176 if (mCurrentStyleScope == aElement) {
177 mCurrentStyleScope = nullptr;
179 return true;
182 #ifdef DEBUG
183 void AssertHasAllStyleScopes(mozilla::dom::Element* aElement) const;
184 #endif
186 bool SetStyleScopeForSelectorMatching(mozilla::dom::Element* aSubject,
187 mozilla::dom::Element* aScope)
189 #ifdef DEBUG
190 AssertHasAllStyleScopes(aSubject);
191 #endif
193 mForScopedStyle = !!aScope;
194 if (!aScope) {
195 // This is not for a scoped style sheet; return true, as we want
196 // selector matching to proceed.
197 mCurrentStyleScope = nullptr;
198 return true;
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;
207 return true;
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;
214 return true;
216 // Otherwise, we're not in the scope, and we don't want to proceed
217 // with selector matching.
218 mCurrentStyleScope = nullptr;
219 return false;
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 {
231 public:
232 explicit AutoAncestorPusher(TreeMatchContext& aTreeMatchContext
233 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
234 : mPushedAncestor(false)
235 , mPushedStyleScope(false)
236 , mTreeMatchContext(aTreeMatchContext)
237 , mElement(nullptr)
239 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
242 void PushAncestorAndStyleScope(mozilla::dom::Element* aElement) {
243 MOZ_ASSERT(!mElement);
244 if (aElement) {
245 mElement = aElement;
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);
261 if (aElement) {
262 mElement = aElement;
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);
283 private:
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 {
301 public:
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;
313 private:
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;
323 private:
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;
341 public:
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,
350 // including XHTML).
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;
377 enum MatchVisited {
378 eNeverMatchVisited,
379 eMatchVisitedDefault
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();
409 if (container) {
410 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(container);
411 NS_ASSERTION(loadContext, "Couldn't get loadContext from container; assuming no private browsing.");
412 if (loadContext) {
413 mUsingPrivateBrowsing = loadContext->UsePrivateBrowsing();
420 struct MOZ_STACK_CLASS RuleProcessorData {
421 RuleProcessorData(nsPresContext* aPresContext,
422 nsRuleWalker* aRuleWalker)
423 : mPresContext(aPresContext),
424 mRuleWalker(aRuleWalker),
425 mScope(nullptr)
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)
442 , mElement(aElement)
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,
462 aTreeMatchContext)
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,
478 aTreeMatchContext),
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,
495 nsIAtom* aPseudoTag,
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");
504 nsIAtom* mPseudoTag;
507 #ifdef MOZ_XUL
508 struct MOZ_STACK_CLASS XULTreeRuleProcessorData :
509 public ElementDependentRuleProcessorData {
510 XULTreeRuleProcessorData(nsPresContext* aPresContext,
511 mozilla::dom::Element* aParentElement,
512 nsRuleWalker* aRuleWalker,
513 nsIAtom* aPseudoTag,
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!");
527 nsIAtom* mPseudoTag;
528 nsICSSPseudoComparator* mComparator;
530 #endif
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,
539 aTreeMatchContext),
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,
558 aTreeMatchContext),
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,
576 nsIAtom* aAttribute,
577 int32_t aModType,
578 bool aAttrHasChanged,
579 TreeMatchContext& aTreeMatchContext)
580 : ElementDependentRuleProcessorData(aPresContext, aElement, nullptr,
581 aTreeMatchContext),
582 mAttribute(aAttribute),
583 mModType(aModType),
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_) */