Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
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 "nsPresContext.h" // for nsCompatibility
15 #include "nsString.h"
16 #include "nsChangeHint.h"
17 #include "nsCSSPseudoElements.h"
18 #include "nsRuleWalker.h"
19 #include "nsNthIndexCache.h"
20 #include "nsILoadContext.h"
21 #include "nsIDocument.h"
22 #include "mozilla/AutoRestore.h"
23 #include "mozilla/BloomFilter.h"
24 #include "mozilla/EventStates.h"
25 #include "mozilla/GuardObjects.h"
27 class nsAttrValue;
28 class nsIAtom;
29 class nsIContent;
30 class nsICSSPseudoComparator;
31 class nsIStyleSheet;
32 struct TreeMatchContext;
34 /**
35 * An AncestorFilter is used to keep track of ancestors so that we can
36 * quickly tell that a particular selector is not relevant to a given
37 * element.
38 */
39 class MOZ_STACK_CLASS AncestorFilter {
40 friend struct TreeMatchContext;
41 public:
42 /* Maintenance of our ancestor state */
43 void PushAncestor(mozilla::dom::Element *aElement);
44 void PopAncestor();
46 /* Check whether we might have an ancestor matching one of the given
47 atom hashes. |hashes| must have length hashListLength */
48 template<size_t hashListLength>
49 bool MightHaveMatchingAncestor(const uint32_t* aHashes) const
50 {
51 MOZ_ASSERT(mFilter);
52 for (size_t i = 0; i < hashListLength && aHashes[i]; ++i) {
53 if (!mFilter->mightContain(aHashes[i])) {
54 return false;
55 }
56 }
58 return true;
59 }
61 bool HasFilter() const { return mFilter; }
63 #ifdef DEBUG
64 void AssertHasAllAncestors(mozilla::dom::Element *aElement) const;
65 #endif
67 private:
68 // Using 2^12 slots makes the Bloom filter a nice round page in
69 // size, so let's do that. We get a false positive rate of 1% or
70 // less even with several hundred things in the filter. Note that
71 // we allocate the filter lazily, because not all tree match
72 // contexts can use one effectively.
73 typedef mozilla::BloomFilter<12, nsIAtom> Filter;
74 nsAutoPtr<Filter> mFilter;
76 // Stack of indices to pop to. These are indices into mHashes.
77 nsTArray<uint32_t> mPopTargets;
79 // List of hashes; this is what we pop using mPopTargets. We store
80 // hashes of our ancestor element tag names, ids, and classes in
81 // here.
82 nsTArray<uint32_t> mHashes;
84 // A debug-only stack of Elements for use in assertions
85 #ifdef DEBUG
86 nsTArray<mozilla::dom::Element*> mElements;
87 #endif
88 };
90 /**
91 * A |TreeMatchContext| has data about a matching operation. The
92 * data are not node-specific but are invariants of the DOM tree the
93 * nodes being matched against are in.
94 *
95 * Most of the members are in parameters to selector matching. The
96 * one out parameter is mHaveRelevantLink. Consumers that use a
97 * TreeMatchContext for more than one matching operation and care
98 * about :visited and mHaveRelevantLink need to
99 * ResetForVisitedMatching() and ResetForUnvisitedMatching() as
100 * needed.
101 */
102 struct MOZ_STACK_CLASS TreeMatchContext {
103 // Reset this context for matching for the style-if-:visited.
104 void ResetForVisitedMatching() {
105 NS_PRECONDITION(mForStyling, "Why is this being called?");
106 mHaveRelevantLink = false;
107 mVisitedHandling = nsRuleWalker::eRelevantLinkVisited;
108 }
110 void ResetForUnvisitedMatching() {
111 NS_PRECONDITION(mForStyling, "Why is this being called?");
112 mHaveRelevantLink = false;
113 mVisitedHandling = nsRuleWalker::eRelevantLinkUnvisited;
114 }
116 void SetHaveRelevantLink() { mHaveRelevantLink = true; }
117 bool HaveRelevantLink() const { return mHaveRelevantLink; }
119 nsRuleWalker::VisitedHandlingType VisitedHandling() const
120 {
121 return mVisitedHandling;
122 }
124 void AddScopeElement(mozilla::dom::Element* aElement) {
125 NS_PRECONDITION(mHaveSpecifiedScope,
126 "Should be set before calling AddScopeElement()");
127 mScopes.AppendElement(aElement);
128 }
129 bool IsScopeElement(mozilla::dom::Element* aElement) const {
130 return mScopes.Contains(aElement);
131 }
132 void SetHasSpecifiedScope() {
133 mHaveSpecifiedScope = true;
134 }
135 bool HasSpecifiedScope() const {
136 return mHaveSpecifiedScope;
137 }
139 /**
140 * Initialize the ancestor filter and list of style scopes. If aElement is
141 * not null, it and all its ancestors will be passed to
142 * mAncestorFilter.PushAncestor and PushStyleScope, starting from the root and
143 * going down the tree. Must only be called for elements in a document.
144 */
145 void InitAncestors(mozilla::dom::Element *aElement);
147 /**
148 * Like InitAncestors, but only initializes the style scope list, not the
149 * ancestor filter. May be called for elements outside a document.
150 */
151 void InitStyleScopes(mozilla::dom::Element* aElement);
153 void PushStyleScope(mozilla::dom::Element* aElement)
154 {
155 NS_PRECONDITION(aElement, "aElement must not be null");
156 if (aElement->IsScopedStyleRoot()) {
157 mStyleScopes.AppendElement(aElement);
158 }
159 }
161 void PopStyleScope(mozilla::dom::Element* aElement)
162 {
163 NS_PRECONDITION(aElement, "aElement must not be null");
164 if (mStyleScopes.SafeLastElement(nullptr) == aElement) {
165 mStyleScopes.TruncateLength(mStyleScopes.Length() - 1);
166 }
167 }
169 bool PopStyleScopeForSelectorMatching(mozilla::dom::Element* aElement)
170 {
171 NS_ASSERTION(mForScopedStyle, "only call PopStyleScopeForSelectorMatching "
172 "when mForScopedStyle is true");
174 if (!mCurrentStyleScope) {
175 return false;
176 }
177 if (mCurrentStyleScope == aElement) {
178 mCurrentStyleScope = nullptr;
179 }
180 return true;
181 }
183 #ifdef DEBUG
184 void AssertHasAllStyleScopes(mozilla::dom::Element* aElement)
185 {
186 nsINode* cur = aElement->GetParentNode();
187 while (cur) {
188 if (cur->IsScopedStyleRoot()) {
189 MOZ_ASSERT(mStyleScopes.Contains(cur));
190 }
191 cur = cur->GetParentNode();
192 }
193 }
194 #endif
196 bool SetStyleScopeForSelectorMatching(mozilla::dom::Element* aSubject,
197 mozilla::dom::Element* aScope)
198 {
199 #ifdef DEBUG
200 AssertHasAllStyleScopes(aSubject);
201 #endif
203 mForScopedStyle = !!aScope;
204 if (!aScope) {
205 // This is not for a scoped style sheet; return true, as we want
206 // selector matching to proceed.
207 mCurrentStyleScope = nullptr;
208 return true;
209 }
210 if (aScope == aSubject) {
211 // Although the subject is the same element as the scope, as soon
212 // as we continue with selector matching up the tree we don't want
213 // to match any more elements. So we return true to indicate that
214 // we want to do the initial selector matching, but set
215 // mCurrentStyleScope to null so that no ancestor elements will match.
216 mCurrentStyleScope = nullptr;
217 return true;
218 }
219 if (mStyleScopes.Contains(aScope)) {
220 // mStyleScopes contains all of the scope elements that are ancestors of
221 // aSubject, so if aScope is in mStyleScopes, then we do want selector
222 // matching to proceed.
223 mCurrentStyleScope = aScope;
224 return true;
225 }
226 // Otherwise, we're not in the scope, and we don't want to proceed
227 // with selector matching.
228 mCurrentStyleScope = nullptr;
229 return false;
230 }
232 bool IsWithinStyleScopeForSelectorMatching() const
233 {
234 NS_ASSERTION(mForScopedStyle, "only call IsWithinScopeForSelectorMatching "
235 "when mForScopedStyle is true");
236 return mCurrentStyleScope;
237 }
239 /* Helper class for maintaining the ancestor state */
240 class MOZ_STACK_CLASS AutoAncestorPusher {
241 public:
242 AutoAncestorPusher(TreeMatchContext& aTreeMatchContext
243 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
244 : mPushedAncestor(false)
245 , mPushedStyleScope(false)
246 , mTreeMatchContext(aTreeMatchContext)
247 , mElement(nullptr)
248 {
249 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
250 }
252 void PushAncestorAndStyleScope(mozilla::dom::Element* aElement) {
253 MOZ_ASSERT(!mElement);
254 if (aElement) {
255 mElement = aElement;
256 mPushedAncestor = true;
257 mPushedStyleScope = true;
258 mTreeMatchContext.mAncestorFilter.PushAncestor(aElement);
259 mTreeMatchContext.PushStyleScope(aElement);
260 }
261 }
263 void PushAncestorAndStyleScope(nsIContent* aContent) {
264 if (aContent && aContent->IsElement()) {
265 PushAncestorAndStyleScope(aContent->AsElement());
266 }
267 }
269 void PushStyleScope(mozilla::dom::Element* aElement) {
270 MOZ_ASSERT(!mElement);
271 if (aElement) {
272 mElement = aElement;
273 mPushedStyleScope = true;
274 mTreeMatchContext.PushStyleScope(aElement);
275 }
276 }
278 void PushStyleScope(nsIContent* aContent) {
279 if (aContent && aContent->IsElement()) {
280 PushStyleScope(aContent->AsElement());
281 }
282 }
284 ~AutoAncestorPusher() {
285 if (mPushedAncestor) {
286 mTreeMatchContext.mAncestorFilter.PopAncestor();
287 }
288 if (mPushedStyleScope) {
289 mTreeMatchContext.PopStyleScope(mElement);
290 }
291 }
293 private:
294 bool mPushedAncestor;
295 bool mPushedStyleScope;
296 TreeMatchContext& mTreeMatchContext;
297 mozilla::dom::Element* mElement;
298 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
299 };
301 /* Helper class for tracking whether we're skipping the ApplyStyleFixups
302 * code for flex items.
303 *
304 * The optional second parameter aSkipFlexItemStyleFixup allows this
305 * class to be instantiated but only conditionally activated (e.g.
306 * in cases where we may or may not want to be skipping flex-item
307 * style fixup for a particular chunk of code).
308 */
309 class MOZ_STACK_CLASS AutoFlexItemStyleFixupSkipper {
310 public:
311 AutoFlexItemStyleFixupSkipper(TreeMatchContext& aTreeMatchContext,
312 bool aSkipFlexItemStyleFixup = true
313 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
314 : mAutoRestorer(aTreeMatchContext.mSkippingFlexItemStyleFixup)
315 {
316 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
317 if (aSkipFlexItemStyleFixup) {
318 aTreeMatchContext.mSkippingFlexItemStyleFixup = true;
319 }
320 }
322 private:
323 mozilla::AutoRestore<bool> mAutoRestorer;
324 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
325 };
327 // Is this matching operation for the creation of a style context?
328 // (If it is, we need to set slow selector bits on nodes indicating
329 // that certain restyling needs to happen.)
330 const bool mForStyling;
332 private:
333 // When mVisitedHandling is eRelevantLinkUnvisited, this is set to true if a
334 // relevant link (see explanation in definition of VisitedHandling enum) was
335 // encountered during the matching process, which means that matching needs
336 // to be rerun with eRelevantLinkVisited. Otherwise, its behavior is
337 // undefined (it might get set appropriately, or might not).
338 bool mHaveRelevantLink;
340 // If true, then our contextual reference element set is specified,
341 // and is given by mScopes.
342 bool mHaveSpecifiedScope;
344 // How matching should be performed. See the documentation for
345 // nsRuleWalker::VisitedHandlingType.
346 nsRuleWalker::VisitedHandlingType mVisitedHandling;
348 // For matching :scope
349 nsAutoTArray<mozilla::dom::Element*, 1> mScopes;
350 public:
351 // The document we're working with.
352 nsIDocument* const mDocument;
354 // Root of scoped stylesheet (set and unset by the supplier of the
355 // scoped stylesheet).
356 nsIContent* mScopedRoot;
358 // Whether our document is HTML (as opposed to XML of some sort,
359 // including XHTML).
360 // XXX XBL2 issue: Should we be caching this? What should it be for XBL2?
361 const bool mIsHTMLDocument;
363 // Possibly remove use of mCompatMode in SelectorMatches?
364 // XXX XBL2 issue: Should we be caching this? What should it be for XBL2?
365 const nsCompatibility mCompatMode;
367 // The nth-index cache we should use
368 nsNthIndexCache mNthIndexCache;
370 // An ancestor filter
371 AncestorFilter mAncestorFilter;
373 // Whether this document is using PB mode
374 bool mUsingPrivateBrowsing;
376 // Whether we're currently skipping the flex item chunk of ApplyStyleFixups
377 // when resolving style (e.g. for children of elements that have a mandatory
378 // frame-type and can't be flex containers despite having "display:flex").
379 bool mSkippingFlexItemStyleFixup;
381 // Whether this TreeMatchContext is being used with an nsCSSRuleProcessor
382 // for an HTML5 scoped style sheet.
383 bool mForScopedStyle;
385 enum MatchVisited {
386 eNeverMatchVisited,
387 eMatchVisitedDefault
388 };
390 // List of ancestor elements that define a style scope (due to having a
391 // <style scoped> child).
392 nsAutoTArray<mozilla::dom::Element*, 1> mStyleScopes;
394 // The current style scope element for selector matching.
395 mozilla::dom::Element* mCurrentStyleScope;
397 // Constructor to use when creating a tree match context for styling
398 TreeMatchContext(bool aForStyling,
399 nsRuleWalker::VisitedHandlingType aVisitedHandling,
400 nsIDocument* aDocument,
401 MatchVisited aMatchVisited = eMatchVisitedDefault)
402 : mForStyling(aForStyling)
403 , mHaveRelevantLink(false)
404 , mHaveSpecifiedScope(false)
405 , mVisitedHandling(aVisitedHandling)
406 , mDocument(aDocument)
407 , mScopedRoot(nullptr)
408 , mIsHTMLDocument(aDocument->IsHTML())
409 , mCompatMode(aDocument->GetCompatibilityMode())
410 , mUsingPrivateBrowsing(false)
411 , mSkippingFlexItemStyleFixup(false)
412 , mForScopedStyle(false)
413 , mCurrentStyleScope(nullptr)
414 {
415 if (aMatchVisited != eNeverMatchVisited) {
416 nsCOMPtr<nsISupports> container = mDocument->GetContainer();
417 if (container) {
418 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(container);
419 NS_ASSERTION(loadContext, "Couldn't get loadContext from container; assuming no private browsing.");
420 if (loadContext) {
421 mUsingPrivateBrowsing = loadContext->UsePrivateBrowsing();
422 }
423 }
424 }
425 }
426 };
428 struct MOZ_STACK_CLASS RuleProcessorData {
429 RuleProcessorData(nsPresContext* aPresContext,
430 nsRuleWalker* aRuleWalker)
431 : mPresContext(aPresContext),
432 mRuleWalker(aRuleWalker),
433 mScope(nullptr)
434 {
435 NS_PRECONDITION(mPresContext, "Must have prescontext");
436 }
438 nsPresContext* const mPresContext;
439 nsRuleWalker* const mRuleWalker; // Used to add rules to our results.
440 mozilla::dom::Element* mScope;
441 };
443 struct MOZ_STACK_CLASS ElementDependentRuleProcessorData :
444 public RuleProcessorData {
445 ElementDependentRuleProcessorData(nsPresContext* aPresContext,
446 mozilla::dom::Element* aElement,
447 nsRuleWalker* aRuleWalker,
448 TreeMatchContext& aTreeMatchContext)
449 : RuleProcessorData(aPresContext, aRuleWalker)
450 , mElement(aElement)
451 , mTreeMatchContext(aTreeMatchContext)
452 {
453 NS_ASSERTION(aElement, "null element leaked into SelectorMatches");
454 NS_ASSERTION(aElement->OwnerDoc(), "Document-less node here?");
455 NS_PRECONDITION(aTreeMatchContext.mForStyling == !!aRuleWalker,
456 "Should be styling if and only if we have a rule walker");
457 }
459 mozilla::dom::Element* const mElement; // weak ref, must not be null
460 TreeMatchContext& mTreeMatchContext;
461 };
463 struct MOZ_STACK_CLASS ElementRuleProcessorData :
464 public ElementDependentRuleProcessorData {
465 ElementRuleProcessorData(nsPresContext* aPresContext,
466 mozilla::dom::Element* aElement,
467 nsRuleWalker* aRuleWalker,
468 TreeMatchContext& aTreeMatchContext)
469 : ElementDependentRuleProcessorData(aPresContext, aElement, aRuleWalker,
470 aTreeMatchContext)
471 {
472 NS_PRECONDITION(aTreeMatchContext.mForStyling, "Styling here!");
473 NS_PRECONDITION(aRuleWalker, "Must have rule walker");
474 }
475 };
477 struct MOZ_STACK_CLASS PseudoElementRuleProcessorData :
478 public ElementDependentRuleProcessorData {
479 PseudoElementRuleProcessorData(nsPresContext* aPresContext,
480 mozilla::dom::Element* aParentElement,
481 nsRuleWalker* aRuleWalker,
482 nsCSSPseudoElements::Type aPseudoType,
483 TreeMatchContext& aTreeMatchContext,
484 mozilla::dom::Element* aPseudoElement)
485 : ElementDependentRuleProcessorData(aPresContext, aParentElement, aRuleWalker,
486 aTreeMatchContext),
487 mPseudoType(aPseudoType),
488 mPseudoElement(aPseudoElement)
489 {
490 NS_PRECONDITION(aPseudoType <
491 nsCSSPseudoElements::ePseudo_PseudoElementCount,
492 "invalid aPseudoType value");
493 NS_PRECONDITION(aTreeMatchContext.mForStyling, "Styling here!");
494 NS_PRECONDITION(aRuleWalker, "Must have rule walker");
495 }
497 nsCSSPseudoElements::Type mPseudoType;
498 mozilla::dom::Element* const mPseudoElement; // weak ref
499 };
501 struct MOZ_STACK_CLASS AnonBoxRuleProcessorData : public RuleProcessorData {
502 AnonBoxRuleProcessorData(nsPresContext* aPresContext,
503 nsIAtom* aPseudoTag,
504 nsRuleWalker* aRuleWalker)
505 : RuleProcessorData(aPresContext, aRuleWalker),
506 mPseudoTag(aPseudoTag)
507 {
508 NS_PRECONDITION(aPseudoTag, "Must have pseudo tag");
509 NS_PRECONDITION(aRuleWalker, "Must have rule walker");
510 }
512 nsIAtom* mPseudoTag;
513 };
515 #ifdef MOZ_XUL
516 struct MOZ_STACK_CLASS XULTreeRuleProcessorData :
517 public ElementDependentRuleProcessorData {
518 XULTreeRuleProcessorData(nsPresContext* aPresContext,
519 mozilla::dom::Element* aParentElement,
520 nsRuleWalker* aRuleWalker,
521 nsIAtom* aPseudoTag,
522 nsICSSPseudoComparator* aComparator,
523 TreeMatchContext& aTreeMatchContext)
524 : ElementDependentRuleProcessorData(aPresContext, aParentElement,
525 aRuleWalker, aTreeMatchContext),
526 mPseudoTag(aPseudoTag),
527 mComparator(aComparator)
528 {
529 NS_PRECONDITION(aPseudoTag, "null pointer");
530 NS_PRECONDITION(aRuleWalker, "Must have rule walker");
531 NS_PRECONDITION(aComparator, "must have a comparator");
532 NS_PRECONDITION(aTreeMatchContext.mForStyling, "Styling here!");
533 }
535 nsIAtom* mPseudoTag;
536 nsICSSPseudoComparator* mComparator;
537 };
538 #endif
540 struct MOZ_STACK_CLASS StateRuleProcessorData :
541 public ElementDependentRuleProcessorData {
542 StateRuleProcessorData(nsPresContext* aPresContext,
543 mozilla::dom::Element* aElement,
544 mozilla::EventStates aStateMask,
545 TreeMatchContext& aTreeMatchContext)
546 : ElementDependentRuleProcessorData(aPresContext, aElement, nullptr,
547 aTreeMatchContext),
548 mStateMask(aStateMask)
549 {
550 NS_PRECONDITION(!aTreeMatchContext.mForStyling, "Not styling here!");
551 }
552 // |HasStateDependentStyle| for which state(s)?
553 // Constants defined in mozilla/EventStates.h .
554 const mozilla::EventStates mStateMask;
555 };
557 struct MOZ_STACK_CLASS PseudoElementStateRuleProcessorData :
558 public StateRuleProcessorData {
559 PseudoElementStateRuleProcessorData(nsPresContext* aPresContext,
560 mozilla::dom::Element* aElement,
561 mozilla::EventStates aStateMask,
562 nsCSSPseudoElements::Type aPseudoType,
563 TreeMatchContext& aTreeMatchContext,
564 mozilla::dom::Element* aPseudoElement)
565 : StateRuleProcessorData(aPresContext, aElement, aStateMask,
566 aTreeMatchContext),
567 mPseudoType(aPseudoType),
568 mPseudoElement(aPseudoElement)
569 {
570 NS_PRECONDITION(!aTreeMatchContext.mForStyling, "Not styling here!");
571 }
573 // We kind of want to inherit from both StateRuleProcessorData and
574 // PseudoElementRuleProcessorData. Instead we've just copied those
575 // members from PseudoElementRuleProcessorData to this struct.
576 nsCSSPseudoElements::Type mPseudoType;
577 mozilla::dom::Element* const mPseudoElement; // weak ref
578 };
580 struct MOZ_STACK_CLASS AttributeRuleProcessorData :
581 public ElementDependentRuleProcessorData {
582 AttributeRuleProcessorData(nsPresContext* aPresContext,
583 mozilla::dom::Element* aElement,
584 nsIAtom* aAttribute,
585 int32_t aModType,
586 bool aAttrHasChanged,
587 TreeMatchContext& aTreeMatchContext)
588 : ElementDependentRuleProcessorData(aPresContext, aElement, nullptr,
589 aTreeMatchContext),
590 mAttribute(aAttribute),
591 mModType(aModType),
592 mAttrHasChanged(aAttrHasChanged)
593 {
594 NS_PRECONDITION(!aTreeMatchContext.mForStyling, "Not styling here!");
595 }
596 nsIAtom* mAttribute; // |HasAttributeDependentStyle| for which attribute?
597 int32_t mModType; // The type of modification (see nsIDOMMutationEvent).
598 bool mAttrHasChanged; // Whether the attribute has already changed.
599 };
601 #endif /* !defined(nsRuleProcessorData_h_) */