1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
|
/* -*- 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;
};
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;
};
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_) */
|