Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
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 * the container for the style sheets that apply to a presentation, and
8 * the internal API that the style system exposes for creating (and
9 * potentially re-creating) style contexts
10 */
12 #include "mozilla/ArrayUtils.h"
13 #include "mozilla/EventStates.h"
14 #include "mozilla/MemoryReporting.h"
16 #include "nsStyleSet.h"
17 #include "nsCSSStyleSheet.h"
18 #include "nsIDocumentInlines.h"
19 #include "nsRuleWalker.h"
20 #include "nsStyleContext.h"
21 #include "mozilla/css/StyleRule.h"
22 #include "nsCSSAnonBoxes.h"
23 #include "nsCSSPseudoElements.h"
24 #include "nsCSSRuleProcessor.h"
25 #include "nsDataHashtable.h"
26 #include "nsIContent.h"
27 #include "nsRuleData.h"
28 #include "nsRuleProcessorData.h"
29 #include "nsTransitionManager.h"
30 #include "nsAnimationManager.h"
31 #include "nsStyleSheetService.h"
32 #include "mozilla/dom/Element.h"
33 #include "GeckoProfiler.h"
34 #include "nsHTMLCSSStyleSheet.h"
35 #include "nsHTMLStyleSheet.h"
36 #include "nsCSSRules.h"
38 using namespace mozilla;
39 using namespace mozilla::dom;
41 NS_IMPL_ISUPPORTS(nsEmptyStyleRule, nsIStyleRule)
43 /* virtual */ void
44 nsEmptyStyleRule::MapRuleInfoInto(nsRuleData* aRuleData)
45 {
46 }
48 #ifdef DEBUG
49 /* virtual */ void
50 nsEmptyStyleRule::List(FILE* out, int32_t aIndent) const
51 {
52 for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out);
53 fputs("[empty style rule] {}\n", out);
54 }
55 #endif
57 NS_IMPL_ISUPPORTS(nsInitialStyleRule, nsIStyleRule)
59 /* virtual */ void
60 nsInitialStyleRule::MapRuleInfoInto(nsRuleData* aRuleData)
61 {
62 // Iterate over the property groups
63 for (nsStyleStructID sid = nsStyleStructID(0);
64 sid < nsStyleStructID_Length; sid = nsStyleStructID(sid + 1)) {
65 if (aRuleData->mSIDs & (1 << sid)) {
66 // Iterate over nsCSSValues within the property group
67 nsCSSValue * const value_start =
68 aRuleData->mValueStorage + aRuleData->mValueOffsets[sid];
69 for (nsCSSValue *value = value_start,
70 *value_end = value + nsCSSProps::PropertyCountInStruct(sid);
71 value != value_end; ++value) {
72 // If MathML is disabled take care not to set MathML properties (or we
73 // will trigger assertions in nsRuleNode)
74 if (sid == eStyleStruct_Font &&
75 !aRuleData->mPresContext->Document()->GetMathMLEnabled()) {
76 size_t index = value - value_start;
77 if (index == nsCSSProps::PropertyIndexInStruct(
78 eCSSProperty_script_level) ||
79 index == nsCSSProps::PropertyIndexInStruct(
80 eCSSProperty_script_size_multiplier) ||
81 index == nsCSSProps::PropertyIndexInStruct(
82 eCSSProperty_script_min_size) ||
83 index == nsCSSProps::PropertyIndexInStruct(
84 eCSSProperty_math_variant) ||
85 index == nsCSSProps::PropertyIndexInStruct(
86 eCSSProperty_math_display)) {
87 continue;
88 }
89 }
90 if (value->GetUnit() == eCSSUnit_Null) {
91 value->SetInitialValue();
92 }
93 }
94 }
95 }
96 }
98 #ifdef DEBUG
99 /* virtual */ void
100 nsInitialStyleRule::List(FILE* out, int32_t aIndent) const
101 {
102 for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out);
103 fputs("[initial style rule] {}\n", out);
104 }
105 #endif
107 NS_IMPL_ISUPPORTS(nsDisableTextZoomStyleRule, nsIStyleRule)
109 /* virtual */ void
110 nsDisableTextZoomStyleRule::MapRuleInfoInto(nsRuleData* aRuleData)
111 {
112 if (!(aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Font)))
113 return;
115 nsCSSValue* value = aRuleData->ValueForTextZoom();
116 if (value->GetUnit() == eCSSUnit_Null)
117 value->SetNoneValue();
118 }
120 #ifdef DEBUG
121 /* virtual */ void
122 nsDisableTextZoomStyleRule::List(FILE* out, int32_t aIndent) const
123 {
124 for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out);
125 fputs("[disable text zoom style rule] {}\n", out);
126 }
127 #endif
129 static const nsStyleSet::sheetType gCSSSheetTypes[] = {
130 // From lowest to highest in cascading order.
131 nsStyleSet::eAgentSheet,
132 nsStyleSet::eUserSheet,
133 nsStyleSet::eDocSheet,
134 nsStyleSet::eScopedDocSheet,
135 nsStyleSet::eOverrideSheet
136 };
138 nsStyleSet::nsStyleSet()
139 : mRuleTree(nullptr),
140 mBatching(0),
141 mInShutdown(false),
142 mAuthorStyleDisabled(false),
143 mInReconstruct(false),
144 mInitFontFeatureValuesLookup(true),
145 mDirty(0),
146 mUnusedRuleNodeCount(0)
147 {
148 }
150 size_t
151 nsStyleSet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
152 {
153 size_t n = aMallocSizeOf(this);
155 for (int i = 0; i < eSheetTypeCount; i++) {
156 if (mRuleProcessors[i]) {
157 n += mRuleProcessors[i]->SizeOfIncludingThis(aMallocSizeOf);
158 }
159 n += mSheets[i].SizeOfExcludingThis(nullptr, aMallocSizeOf);
160 }
162 for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) {
163 n += mScopedDocSheetRuleProcessors[i]->SizeOfIncludingThis(aMallocSizeOf);
164 }
165 n += mScopedDocSheetRuleProcessors.SizeOfExcludingThis(aMallocSizeOf);
167 n += mRoots.SizeOfExcludingThis(aMallocSizeOf);
168 n += mOldRuleTrees.SizeOfExcludingThis(aMallocSizeOf);
170 return n;
171 }
173 void
174 nsStyleSet::Init(nsPresContext *aPresContext)
175 {
176 mFirstLineRule = new nsEmptyStyleRule;
177 mFirstLetterRule = new nsEmptyStyleRule;
178 mPlaceholderRule = new nsEmptyStyleRule;
179 mDisableTextZoomStyleRule = new nsDisableTextZoomStyleRule;
181 mRuleTree = nsRuleNode::CreateRootNode(aPresContext);
183 GatherRuleProcessors(eAnimationSheet);
184 GatherRuleProcessors(eTransitionSheet);
185 }
187 nsresult
188 nsStyleSet::BeginReconstruct()
189 {
190 NS_ASSERTION(!mInReconstruct, "Unmatched begin/end?");
191 NS_ASSERTION(mRuleTree, "Reconstructing before first construction?");
193 // Create a new rule tree root
194 nsRuleNode* newTree =
195 nsRuleNode::CreateRootNode(mRuleTree->PresContext());
196 if (!newTree)
197 return NS_ERROR_OUT_OF_MEMORY;
199 // Save the old rule tree so we can destroy it later
200 if (!mOldRuleTrees.AppendElement(mRuleTree)) {
201 newTree->Destroy();
202 return NS_ERROR_OUT_OF_MEMORY;
203 }
205 // We need to keep mRoots so that the rule tree GC will only free the
206 // rule trees that really aren't referenced anymore (which should be
207 // all of them, if there are no bugs in reresolution code).
209 mInReconstruct = true;
210 mRuleTree = newTree;
212 return NS_OK;
213 }
215 void
216 nsStyleSet::EndReconstruct()
217 {
218 NS_ASSERTION(mInReconstruct, "Unmatched begin/end?");
219 mInReconstruct = false;
220 #ifdef DEBUG
221 for (int32_t i = mRoots.Length() - 1; i >= 0; --i) {
222 nsRuleNode *n = mRoots[i]->RuleNode();
223 while (n->GetParent()) {
224 n = n->GetParent();
225 }
226 // Since nsStyleContext's mParent and mRuleNode are immutable, and
227 // style contexts own their parents, and nsStyleContext asserts in
228 // its constructor that the style context and its parent are in the
229 // same rule tree, we don't need to check any of the children of
230 // mRoots; we only need to check the rule nodes of mRoots
231 // themselves.
233 NS_ASSERTION(n == mRuleTree, "style context has old rule node");
234 }
235 #endif
236 // This *should* destroy the only element of mOldRuleTrees, but in
237 // case of some bugs (which would trigger the above assertions), it
238 // won't.
239 GCRuleTrees();
240 }
242 void
243 nsStyleSet::SetQuirkStyleSheet(nsIStyleSheet* aQuirkStyleSheet)
244 {
245 NS_ASSERTION(aQuirkStyleSheet, "Must have quirk sheet if this is called");
246 NS_ASSERTION(!mQuirkStyleSheet, "Multiple calls to SetQuirkStyleSheet?");
247 NS_ASSERTION(mSheets[eAgentSheet].IndexOf(aQuirkStyleSheet) != -1,
248 "Quirk style sheet not one of our agent sheets?");
249 mQuirkStyleSheet = aQuirkStyleSheet;
250 }
252 typedef nsDataHashtable<nsPtrHashKey<nsINode>, uint32_t> ScopeDepthCache;
254 // Returns the depth of a style scope element, with 1 being the depth of
255 // a style scope element that has no ancestor style scope elements. The
256 // depth does not count intervening non-scope elements.
257 static uint32_t
258 GetScopeDepth(nsINode* aScopeElement, ScopeDepthCache& aCache)
259 {
260 nsINode* parent = aScopeElement->GetParent();
261 if (!parent || !parent->IsElementInStyleScope()) {
262 return 1;
263 }
265 uint32_t depth = aCache.Get(aScopeElement);
266 if (!depth) {
267 for (nsINode* n = parent; n; n = n->GetParent()) {
268 if (n->IsScopedStyleRoot()) {
269 depth = GetScopeDepth(n, aCache) + 1;
270 aCache.Put(aScopeElement, depth);
271 break;
272 }
273 }
274 }
275 return depth;
276 }
278 struct ScopedSheetOrder
279 {
280 nsCSSStyleSheet* mSheet;
281 uint32_t mDepth;
282 uint32_t mOrder;
284 bool operator==(const ScopedSheetOrder& aRHS) const
285 {
286 return mDepth == aRHS.mDepth &&
287 mOrder == aRHS.mOrder;
288 }
290 bool operator<(const ScopedSheetOrder& aRHS) const
291 {
292 if (mDepth != aRHS.mDepth) {
293 return mDepth < aRHS.mDepth;
294 }
295 return mOrder < aRHS.mOrder;
296 }
297 };
299 // Sorts aSheets such that style sheets for ancestor scopes come
300 // before those for descendant scopes, and with sheets for a single
301 // scope in document order.
302 static void
303 SortStyleSheetsByScope(nsTArray<nsCSSStyleSheet*>& aSheets)
304 {
305 uint32_t n = aSheets.Length();
306 if (n == 1) {
307 return;
308 }
310 ScopeDepthCache cache;
312 nsTArray<ScopedSheetOrder> sheets;
313 sheets.SetLength(n);
315 // For each sheet, record the depth of its scope element and its original
316 // document order.
317 for (uint32_t i = 0; i < n; i++) {
318 sheets[i].mSheet = aSheets[i];
319 sheets[i].mDepth = GetScopeDepth(aSheets[i]->GetScopeElement(), cache);
320 sheets[i].mOrder = i;
321 }
323 // Sort by depth first, then document order.
324 sheets.Sort();
326 for (uint32_t i = 0; i < n; i++) {
327 aSheets[i] = sheets[i].mSheet;
328 }
329 }
331 nsresult
332 nsStyleSet::GatherRuleProcessors(sheetType aType)
333 {
334 mRuleProcessors[aType] = nullptr;
335 if (aType == eScopedDocSheet) {
336 for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) {
337 nsIStyleRuleProcessor* processor = mScopedDocSheetRuleProcessors[i].get();
338 Element* scope =
339 static_cast<nsCSSRuleProcessor*>(processor)->GetScopeElement();
340 scope->ClearIsScopedStyleRoot();
341 }
342 mScopedDocSheetRuleProcessors.Clear();
343 }
344 if (mAuthorStyleDisabled && (aType == eDocSheet ||
345 aType == eScopedDocSheet ||
346 aType == eStyleAttrSheet)) {
347 // Don't regather if this level is disabled. Note that we gather
348 // preshint sheets no matter what, but then skip them for some
349 // elements later if mAuthorStyleDisabled.
350 return NS_OK;
351 }
352 switch (aType) {
353 // handle the types for which have a rule processor that does not
354 // implement the style sheet interface.
355 case eAnimationSheet:
356 MOZ_ASSERT(mSheets[aType].Count() == 0);
357 mRuleProcessors[aType] = PresContext()->AnimationManager();
358 return NS_OK;
359 case eTransitionSheet:
360 MOZ_ASSERT(mSheets[aType].Count() == 0);
361 mRuleProcessors[aType] = PresContext()->TransitionManager();
362 return NS_OK;
363 case eStyleAttrSheet:
364 MOZ_ASSERT(mSheets[aType].Count() == 0);
365 mRuleProcessors[aType] = PresContext()->Document()->GetInlineStyleSheet();
366 return NS_OK;
367 case ePresHintSheet:
368 MOZ_ASSERT(mSheets[aType].Count() == 0);
369 mRuleProcessors[aType] = PresContext()->Document()->GetAttributeStyleSheet();
370 return NS_OK;
371 default:
372 // keep going
373 break;
374 }
375 if (aType == eScopedDocSheet) {
376 // Create a rule processor for each scope.
377 uint32_t count = mSheets[eScopedDocSheet].Count();
378 if (count) {
379 // Gather the scoped style sheets into an array as
380 // nsCSSStyleSheets, and mark all of their scope elements
381 // as scoped style roots.
382 nsTArray<nsCSSStyleSheet*> sheets(count);
383 for (uint32_t i = 0; i < count; i++) {
384 nsRefPtr<nsCSSStyleSheet> sheet =
385 do_QueryObject(mSheets[eScopedDocSheet].ObjectAt(i));
386 sheets.AppendElement(sheet);
388 Element* scope = sheet->GetScopeElement();
389 scope->SetIsScopedStyleRoot();
390 }
392 // Sort the scoped style sheets so that those for the same scope are
393 // adjacent and that ancestor scopes come before descendent scopes.
394 SortStyleSheetsByScope(sheets);
396 uint32_t start = 0, end;
397 do {
398 // Find the range of style sheets with the same scope.
399 Element* scope = sheets[start]->GetScopeElement();
400 end = start + 1;
401 while (end < count && sheets[end]->GetScopeElement() == scope) {
402 end++;
403 }
405 scope->SetIsScopedStyleRoot();
407 // Create a rule processor for the scope.
408 nsTArray< nsRefPtr<nsCSSStyleSheet> > sheetsForScope;
409 sheetsForScope.AppendElements(sheets.Elements() + start, end - start);
410 mScopedDocSheetRuleProcessors.AppendElement
411 (new nsCSSRuleProcessor(sheetsForScope, uint8_t(aType), scope));
413 start = end;
414 } while (start < count);
415 }
416 return NS_OK;
417 }
418 if (mSheets[aType].Count()) {
419 switch (aType) {
420 case eAgentSheet:
421 case eUserSheet:
422 case eDocSheet:
423 case eOverrideSheet: {
424 // levels containing CSS stylesheets (apart from eScopedDocSheet)
425 nsCOMArray<nsIStyleSheet>& sheets = mSheets[aType];
426 nsTArray<nsRefPtr<nsCSSStyleSheet> > cssSheets(sheets.Count());
427 for (int32_t i = 0, i_end = sheets.Count(); i < i_end; ++i) {
428 nsRefPtr<nsCSSStyleSheet> cssSheet = do_QueryObject(sheets[i]);
429 NS_ASSERTION(cssSheet, "not a CSS sheet");
430 cssSheets.AppendElement(cssSheet);
431 }
432 mRuleProcessors[aType] =
433 new nsCSSRuleProcessor(cssSheets, uint8_t(aType), nullptr);
434 } break;
436 default:
437 // levels containing non-CSS stylesheets
438 NS_ASSERTION(mSheets[aType].Count() == 1, "only one sheet per level");
439 mRuleProcessors[aType] = do_QueryInterface(mSheets[aType][0]);
440 break;
441 }
442 }
444 return NS_OK;
445 }
447 static bool
448 IsScopedStyleSheet(nsIStyleSheet* aSheet)
449 {
450 nsRefPtr<nsCSSStyleSheet> cssSheet = do_QueryObject(aSheet);
451 NS_ASSERTION(cssSheet, "expected aSheet to be an nsCSSStyleSheet");
453 return cssSheet->GetScopeElement();
454 }
456 nsresult
457 nsStyleSet::AppendStyleSheet(sheetType aType, nsIStyleSheet *aSheet)
458 {
459 NS_PRECONDITION(aSheet, "null arg");
460 NS_ASSERTION(aSheet->IsApplicable(),
461 "Inapplicable sheet being placed in style set");
462 mSheets[aType].RemoveObject(aSheet);
463 if (!mSheets[aType].AppendObject(aSheet))
464 return NS_ERROR_OUT_OF_MEMORY;
466 return DirtyRuleProcessors(aType);
467 }
469 nsresult
470 nsStyleSet::PrependStyleSheet(sheetType aType, nsIStyleSheet *aSheet)
471 {
472 NS_PRECONDITION(aSheet, "null arg");
473 NS_ASSERTION(aSheet->IsApplicable(),
474 "Inapplicable sheet being placed in style set");
475 mSheets[aType].RemoveObject(aSheet);
476 if (!mSheets[aType].InsertObjectAt(aSheet, 0))
477 return NS_ERROR_OUT_OF_MEMORY;
479 return DirtyRuleProcessors(aType);
480 }
482 nsresult
483 nsStyleSet::RemoveStyleSheet(sheetType aType, nsIStyleSheet *aSheet)
484 {
485 NS_PRECONDITION(aSheet, "null arg");
486 NS_ASSERTION(aSheet->IsComplete(),
487 "Incomplete sheet being removed from style set");
488 mSheets[aType].RemoveObject(aSheet);
490 return DirtyRuleProcessors(aType);
491 }
493 nsresult
494 nsStyleSet::ReplaceSheets(sheetType aType,
495 const nsCOMArray<nsIStyleSheet> &aNewSheets)
496 {
497 mSheets[aType].Clear();
498 if (!mSheets[aType].AppendObjects(aNewSheets))
499 return NS_ERROR_OUT_OF_MEMORY;
501 return DirtyRuleProcessors(aType);
502 }
504 nsresult
505 nsStyleSet::InsertStyleSheetBefore(sheetType aType, nsIStyleSheet *aNewSheet,
506 nsIStyleSheet *aReferenceSheet)
507 {
508 NS_PRECONDITION(aNewSheet && aReferenceSheet, "null arg");
509 NS_ASSERTION(aNewSheet->IsApplicable(),
510 "Inapplicable sheet being placed in style set");
512 mSheets[aType].RemoveObject(aNewSheet);
513 int32_t idx = mSheets[aType].IndexOf(aReferenceSheet);
514 if (idx < 0)
515 return NS_ERROR_INVALID_ARG;
517 if (!mSheets[aType].InsertObjectAt(aNewSheet, idx))
518 return NS_ERROR_OUT_OF_MEMORY;
520 return DirtyRuleProcessors(aType);
521 }
523 nsresult
524 nsStyleSet::DirtyRuleProcessors(sheetType aType)
525 {
526 if (!mBatching)
527 return GatherRuleProcessors(aType);
529 mDirty |= 1 << aType;
530 return NS_OK;
531 }
533 bool
534 nsStyleSet::GetAuthorStyleDisabled()
535 {
536 return mAuthorStyleDisabled;
537 }
539 nsresult
540 nsStyleSet::SetAuthorStyleDisabled(bool aStyleDisabled)
541 {
542 if (aStyleDisabled == !mAuthorStyleDisabled) {
543 mAuthorStyleDisabled = aStyleDisabled;
544 BeginUpdate();
545 mDirty |= 1 << eDocSheet |
546 1 << eScopedDocSheet |
547 1 << eStyleAttrSheet;
548 return EndUpdate();
549 }
550 return NS_OK;
551 }
553 // -------- Doc Sheets
555 nsresult
556 nsStyleSet::AddDocStyleSheet(nsIStyleSheet* aSheet, nsIDocument* aDocument)
557 {
558 NS_PRECONDITION(aSheet && aDocument, "null arg");
559 NS_ASSERTION(aSheet->IsApplicable(),
560 "Inapplicable sheet being placed in style set");
562 sheetType type = IsScopedStyleSheet(aSheet) ?
563 eScopedDocSheet :
564 eDocSheet;
565 nsCOMArray<nsIStyleSheet>& sheets = mSheets[type];
567 sheets.RemoveObject(aSheet);
568 nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
570 // lowest index first
571 int32_t newDocIndex = aDocument->GetIndexOfStyleSheet(aSheet);
573 int32_t count = sheets.Count();
574 int32_t index;
575 for (index = 0; index < count; index++) {
576 nsIStyleSheet* sheet = sheets.ObjectAt(index);
577 int32_t sheetDocIndex = aDocument->GetIndexOfStyleSheet(sheet);
578 if (sheetDocIndex > newDocIndex)
579 break;
581 // If the sheet is not owned by the document it can be an author
582 // sheet registered at nsStyleSheetService or an additional author
583 // sheet on the document, which means the new
584 // doc sheet should end up before it.
585 if (sheetDocIndex < 0 &&
586 ((sheetService &&
587 sheetService->AuthorStyleSheets()->IndexOf(sheet) >= 0) ||
588 sheet == aDocument->FirstAdditionalAuthorSheet()))
589 break;
590 }
591 if (!sheets.InsertObjectAt(aSheet, index))
592 return NS_ERROR_OUT_OF_MEMORY;
594 return DirtyRuleProcessors(type);
595 }
597 nsresult
598 nsStyleSet::RemoveDocStyleSheet(nsIStyleSheet *aSheet)
599 {
600 nsRefPtr<nsCSSStyleSheet> cssSheet = do_QueryObject(aSheet);
601 bool isScoped = cssSheet && cssSheet->GetScopeElement();
602 return RemoveStyleSheet(isScoped ? eScopedDocSheet : eDocSheet, aSheet);
603 }
605 // Batching
606 void
607 nsStyleSet::BeginUpdate()
608 {
609 ++mBatching;
610 }
612 nsresult
613 nsStyleSet::EndUpdate()
614 {
615 NS_ASSERTION(mBatching > 0, "Unbalanced EndUpdate");
616 if (--mBatching) {
617 // We're not completely done yet.
618 return NS_OK;
619 }
621 for (int i = 0; i < eSheetTypeCount; ++i) {
622 if (mDirty & (1 << i)) {
623 nsresult rv = GatherRuleProcessors(sheetType(i));
624 NS_ENSURE_SUCCESS(rv, rv);
625 }
626 }
628 mDirty = 0;
629 return NS_OK;
630 }
632 void
633 nsStyleSet::EnableQuirkStyleSheet(bool aEnable)
634 {
635 #ifdef DEBUG
636 bool oldEnabled;
637 {
638 nsCOMPtr<nsIDOMCSSStyleSheet> domSheet =
639 do_QueryInterface(mQuirkStyleSheet);
640 domSheet->GetDisabled(&oldEnabled);
641 oldEnabled = !oldEnabled;
642 }
643 #endif
644 mQuirkStyleSheet->SetEnabled(aEnable);
645 #ifdef DEBUG
646 // This should always be OK, since SetEnabled should call
647 // ClearRuleCascades.
648 // Note that we can hit this codepath multiple times when document.open()
649 // (potentially implied) happens multiple times.
650 if (mRuleProcessors[eAgentSheet] && aEnable != oldEnabled) {
651 static_cast<nsCSSRuleProcessor*>(static_cast<nsIStyleRuleProcessor*>(
652 mRuleProcessors[eAgentSheet]))->AssertQuirksChangeOK();
653 }
654 #endif
655 }
657 template<class T>
658 static bool
659 EnumRulesMatching(nsIStyleRuleProcessor* aProcessor, void* aData)
660 {
661 T* data = static_cast<T*>(aData);
662 aProcessor->RulesMatching(data);
663 return true;
664 }
666 static inline bool
667 IsMoreSpecificThanAnimation(nsRuleNode *aRuleNode)
668 {
669 return !aRuleNode->IsRoot() &&
670 (aRuleNode->GetLevel() == nsStyleSet::eTransitionSheet ||
671 aRuleNode->IsImportantRule());
672 }
674 static nsIStyleRule*
675 GetAnimationRule(nsRuleNode *aRuleNode)
676 {
677 nsRuleNode *n = aRuleNode;
678 while (IsMoreSpecificThanAnimation(n)) {
679 n = n->GetParent();
680 }
682 if (n->IsRoot() || n->GetLevel() != nsStyleSet::eAnimationSheet) {
683 return nullptr;
684 }
686 return n->GetRule();
687 }
689 static nsRuleNode*
690 ReplaceAnimationRule(nsRuleNode *aOldRuleNode,
691 nsIStyleRule *aOldAnimRule,
692 nsIStyleRule *aNewAnimRule)
693 {
694 nsTArray<nsRuleNode*> moreSpecificNodes;
696 nsRuleNode *n = aOldRuleNode;
697 while (IsMoreSpecificThanAnimation(n)) {
698 moreSpecificNodes.AppendElement(n);
699 n = n->GetParent();
700 }
702 if (aOldAnimRule) {
703 NS_ABORT_IF_FALSE(n->GetRule() == aOldAnimRule, "wrong rule");
704 NS_ABORT_IF_FALSE(n->GetLevel() == nsStyleSet::eAnimationSheet,
705 "wrong level");
706 n = n->GetParent();
707 }
709 NS_ABORT_IF_FALSE(!IsMoreSpecificThanAnimation(n) &&
710 (n->IsRoot() ||
711 n->GetLevel() != nsStyleSet::eAnimationSheet),
712 "wrong level");
714 if (aNewAnimRule) {
715 n = n->Transition(aNewAnimRule, nsStyleSet::eAnimationSheet, false);
716 }
718 for (uint32_t i = moreSpecificNodes.Length(); i-- != 0; ) {
719 nsRuleNode *oldNode = moreSpecificNodes[i];
720 n = n->Transition(oldNode->GetRule(), oldNode->GetLevel(),
721 oldNode->IsImportantRule());
722 }
724 return n;
725 }
727 /**
728 * |GetContext| implements sharing of style contexts (not just the data
729 * on the rule nodes) between siblings and cousins of the same
730 * generation. (It works for cousins of the same generation since
731 * |aParentContext| could itself be a shared context.)
732 */
733 already_AddRefed<nsStyleContext>
734 nsStyleSet::GetContext(nsStyleContext* aParentContext,
735 nsRuleNode* aRuleNode,
736 // aVisitedRuleNode may be null; if it is null
737 // it means that we don't need to force creation
738 // of a StyleIfVisited. (But if we make one
739 // because aParentContext has one, then aRuleNode
740 // should be used.)
741 nsRuleNode* aVisitedRuleNode,
742 nsIAtom* aPseudoTag,
743 nsCSSPseudoElements::Type aPseudoType,
744 Element* aElementForAnimation,
745 uint32_t aFlags)
746 {
747 NS_PRECONDITION((!aPseudoTag &&
748 aPseudoType ==
749 nsCSSPseudoElements::ePseudo_NotPseudoElement) ||
750 (aPseudoTag &&
751 nsCSSPseudoElements::GetPseudoType(aPseudoTag) ==
752 aPseudoType),
753 "Pseudo mismatch");
755 if (aVisitedRuleNode == aRuleNode) {
756 // No need to force creation of a visited style in this case.
757 aVisitedRuleNode = nullptr;
758 }
760 // Ensure |aVisitedRuleNode != nullptr| corresponds to the need to
761 // create an if-visited style context, and that in that case, we have
762 // parentIfVisited set correctly.
763 nsStyleContext *parentIfVisited =
764 aParentContext ? aParentContext->GetStyleIfVisited() : nullptr;
765 if (parentIfVisited) {
766 if (!aVisitedRuleNode) {
767 aVisitedRuleNode = aRuleNode;
768 }
769 } else {
770 if (aVisitedRuleNode) {
771 parentIfVisited = aParentContext;
772 }
773 }
775 if (aFlags & eIsLink) {
776 // If this node is a link, we want its visited's style context's
777 // parent to be the regular style context of its parent, because
778 // only the visitedness of the relevant link should influence style.
779 parentIfVisited = aParentContext;
780 }
782 nsRefPtr<nsStyleContext> result;
783 if (aParentContext)
784 result = aParentContext->FindChildWithRules(aPseudoTag, aRuleNode,
785 aVisitedRuleNode,
786 aFlags & eIsVisitedLink);
788 #ifdef NOISY_DEBUG
789 if (result)
790 fprintf(stdout, "--- SharedSC %d ---\n", ++gSharedCount);
791 else
792 fprintf(stdout, "+++ NewSC %d +++\n", ++gNewCount);
793 #endif
795 if (!result) {
796 result = NS_NewStyleContext(aParentContext, aPseudoTag, aPseudoType,
797 aRuleNode, aFlags & eSkipFlexItemStyleFixup);
798 if (aVisitedRuleNode) {
799 nsRefPtr<nsStyleContext> resultIfVisited =
800 NS_NewStyleContext(parentIfVisited, aPseudoTag, aPseudoType,
801 aVisitedRuleNode,
802 aFlags & eSkipFlexItemStyleFixup);
803 if (!parentIfVisited) {
804 mRoots.AppendElement(resultIfVisited);
805 }
806 resultIfVisited->SetIsStyleIfVisited();
807 result->SetStyleIfVisited(resultIfVisited.forget());
809 bool relevantLinkVisited = (aFlags & eIsLink) ?
810 (aFlags & eIsVisitedLink) :
811 (aParentContext && aParentContext->RelevantLinkVisited());
813 if (relevantLinkVisited) {
814 result->AddStyleBit(NS_STYLE_RELEVANT_LINK_VISITED);
815 }
816 }
817 if (!aParentContext) {
818 mRoots.AppendElement(result);
819 }
820 }
821 else {
822 NS_ASSERTION(result->GetPseudoType() == aPseudoType, "Unexpected type");
823 NS_ASSERTION(result->GetPseudo() == aPseudoTag, "Unexpected pseudo");
824 }
826 if (aFlags & eDoAnimation) {
827 // Normally the animation manager has already added the correct
828 // style rule. However, if the animation-name just changed, it
829 // might have been wrong. So ask it to double-check based on the
830 // resulting style context.
831 nsIStyleRule *oldAnimRule = GetAnimationRule(aRuleNode);
832 nsIStyleRule *animRule = PresContext()->AnimationManager()->
833 CheckAnimationRule(result, aElementForAnimation);
834 NS_ABORT_IF_FALSE(result->RuleNode() == aRuleNode,
835 "unexpected rule node");
836 NS_ABORT_IF_FALSE(!result->GetStyleIfVisited() == !aVisitedRuleNode,
837 "unexpected visited rule node");
838 NS_ABORT_IF_FALSE(!aVisitedRuleNode ||
839 result->GetStyleIfVisited()->RuleNode() ==
840 aVisitedRuleNode,
841 "unexpected visited rule node");
842 NS_ABORT_IF_FALSE(!aVisitedRuleNode ||
843 oldAnimRule == GetAnimationRule(aVisitedRuleNode),
844 "animation rule mismatch between rule nodes");
845 if (oldAnimRule != animRule) {
846 nsRuleNode *ruleNode =
847 ReplaceAnimationRule(aRuleNode, oldAnimRule, animRule);
848 nsRuleNode *visitedRuleNode = aVisitedRuleNode
849 ? ReplaceAnimationRule(aVisitedRuleNode, oldAnimRule, animRule)
850 : nullptr;
851 NS_ABORT_IF_FALSE(!visitedRuleNode ||
852 GetAnimationRule(ruleNode) ==
853 GetAnimationRule(visitedRuleNode),
854 "animation rule mismatch between rule nodes");
855 result = GetContext(aParentContext, ruleNode, visitedRuleNode,
856 aPseudoTag, aPseudoType, nullptr,
857 aFlags & ~eDoAnimation);
858 }
859 }
861 if (aElementForAnimation && aElementForAnimation->IsHTML(nsGkAtoms::body) &&
862 aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement &&
863 PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) {
864 nsIDocument* doc = aElementForAnimation->GetCurrentDoc();
865 if (doc && doc->GetBodyElement() == aElementForAnimation) {
866 // Update the prescontext's body color
867 PresContext()->SetBodyTextColor(result->StyleColor()->mColor);
868 }
869 }
871 return result.forget();
872 }
874 void
875 nsStyleSet::AddImportantRules(nsRuleNode* aCurrLevelNode,
876 nsRuleNode* aLastPrevLevelNode,
877 nsRuleWalker* aRuleWalker)
878 {
879 NS_ASSERTION(aCurrLevelNode &&
880 aCurrLevelNode != aLastPrevLevelNode, "How did we get here?");
882 nsAutoTArray<nsIStyleRule*, 16> importantRules;
883 for (nsRuleNode *node = aCurrLevelNode; node != aLastPrevLevelNode;
884 node = node->GetParent()) {
885 // We guarantee that we never walk the root node here, so no need
886 // to null-check GetRule(). Furthermore, it must be a CSS rule.
887 NS_ASSERTION(nsRefPtr<css::StyleRule>(do_QueryObject(node->GetRule())),
888 "Unexpected non-CSS rule");
890 nsIStyleRule* impRule =
891 static_cast<css::StyleRule*>(node->GetRule())->GetImportantRule();
892 if (impRule)
893 importantRules.AppendElement(impRule);
894 }
896 NS_ASSERTION(importantRules.Length() != 0,
897 "Why did we think there were important rules?");
899 for (uint32_t i = importantRules.Length(); i-- != 0; ) {
900 aRuleWalker->Forward(importantRules[i]);
901 }
902 }
904 #ifdef DEBUG
905 void
906 nsStyleSet::AssertNoImportantRules(nsRuleNode* aCurrLevelNode,
907 nsRuleNode* aLastPrevLevelNode)
908 {
909 if (!aCurrLevelNode)
910 return;
912 for (nsRuleNode *node = aCurrLevelNode; node != aLastPrevLevelNode;
913 node = node->GetParent()) {
914 nsRefPtr<css::StyleRule> rule(do_QueryObject(node->GetRule()));
915 NS_ASSERTION(rule, "Unexpected non-CSS rule");
917 NS_ASSERTION(!rule->GetImportantRule(), "Unexpected important rule");
918 }
919 }
921 void
922 nsStyleSet::AssertNoCSSRules(nsRuleNode* aCurrLevelNode,
923 nsRuleNode* aLastPrevLevelNode)
924 {
925 if (!aCurrLevelNode)
926 return;
928 for (nsRuleNode *node = aCurrLevelNode; node != aLastPrevLevelNode;
929 node = node->GetParent()) {
930 nsIStyleRule *rule = node->GetRule();
931 nsRefPtr<css::StyleRule> cssRule(do_QueryObject(rule));
932 NS_ASSERTION(!cssRule || !cssRule->Selector(), "Unexpected CSS rule");
933 }
934 }
935 #endif
937 // Enumerate the rules in a way that cares about the order of the rules.
938 void
939 nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc,
940 RuleProcessorData* aData, Element* aElement,
941 nsRuleWalker* aRuleWalker)
942 {
943 PROFILER_LABEL("nsStyleSet", "FileRules");
945 // Cascading order:
946 // [least important]
947 // - UA normal rules = Agent normal
948 // - User normal rules = User normal
949 // - Presentation hints = PresHint normal
950 // - Author normal rules = Document normal
951 // - Override normal rules = Override normal
952 // - animation rules = Animation normal
953 // - Author !important rules = Document !important
954 // - Override !important rules = Override !important
955 // - User !important rules = User !important
956 // - UA !important rules = Agent !important
957 // - transition rules = Transition normal
958 // [most important]
960 // Save off the last rule before we start walking our agent sheets;
961 // this will be either the root or one of the restriction rules.
962 nsRuleNode* lastRestrictionRN = aRuleWalker->CurrentNode();
964 aRuleWalker->SetLevel(eAgentSheet, false, true);
965 if (mRuleProcessors[eAgentSheet])
966 (*aCollectorFunc)(mRuleProcessors[eAgentSheet], aData);
967 nsRuleNode* lastAgentRN = aRuleWalker->CurrentNode();
968 bool haveImportantUARules = !aRuleWalker->GetCheckForImportantRules();
970 aRuleWalker->SetLevel(eUserSheet, false, true);
971 bool skipUserStyles =
972 aElement && aElement->IsInNativeAnonymousSubtree();
973 if (!skipUserStyles && mRuleProcessors[eUserSheet]) // NOTE: different
974 (*aCollectorFunc)(mRuleProcessors[eUserSheet], aData);
975 nsRuleNode* lastUserRN = aRuleWalker->CurrentNode();
976 bool haveImportantUserRules = !aRuleWalker->GetCheckForImportantRules();
978 aRuleWalker->SetLevel(ePresHintSheet, false, false);
979 if (mRuleProcessors[ePresHintSheet])
980 (*aCollectorFunc)(mRuleProcessors[ePresHintSheet], aData);
981 nsRuleNode* lastPresHintRN = aRuleWalker->CurrentNode();
983 aRuleWalker->SetLevel(eDocSheet, false, true);
984 bool cutOffInheritance = false;
985 if (mBindingManager && aElement) {
986 // We can supply additional document-level sheets that should be walked.
987 mBindingManager->WalkRules(aCollectorFunc,
988 static_cast<ElementDependentRuleProcessorData*>(aData),
989 &cutOffInheritance);
990 }
991 if (!skipUserStyles && !cutOffInheritance && // NOTE: different
992 mRuleProcessors[eDocSheet])
993 (*aCollectorFunc)(mRuleProcessors[eDocSheet], aData);
994 nsRuleNode* lastDocRN = aRuleWalker->CurrentNode();
995 bool haveImportantDocRules = !aRuleWalker->GetCheckForImportantRules();
996 nsTArray<nsRuleNode*> lastScopedRNs;
997 nsTArray<bool> haveImportantScopedRules;
998 bool haveAnyImportantScopedRules = false;
999 if (!skipUserStyles && !cutOffInheritance &&
1000 aElement && aElement->IsElementInStyleScope()) {
1001 lastScopedRNs.SetLength(mScopedDocSheetRuleProcessors.Length());
1002 haveImportantScopedRules.SetLength(mScopedDocSheetRuleProcessors.Length());
1003 for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) {
1004 aRuleWalker->SetLevel(eScopedDocSheet, false, true);
1005 nsCSSRuleProcessor* processor =
1006 static_cast<nsCSSRuleProcessor*>(mScopedDocSheetRuleProcessors[i].get());
1007 aData->mScope = processor->GetScopeElement();
1008 (*aCollectorFunc)(mScopedDocSheetRuleProcessors[i], aData);
1009 lastScopedRNs[i] = aRuleWalker->CurrentNode();
1010 haveImportantScopedRules[i] = !aRuleWalker->GetCheckForImportantRules();
1011 haveAnyImportantScopedRules = haveAnyImportantScopedRules || haveImportantScopedRules[i];
1012 }
1013 aData->mScope = nullptr;
1014 }
1015 nsRuleNode* lastScopedRN = aRuleWalker->CurrentNode();
1016 aRuleWalker->SetLevel(eStyleAttrSheet, false, true);
1017 if (mRuleProcessors[eStyleAttrSheet])
1018 (*aCollectorFunc)(mRuleProcessors[eStyleAttrSheet], aData);
1019 nsRuleNode* lastStyleAttrRN = aRuleWalker->CurrentNode();
1020 bool haveImportantStyleAttrRules = !aRuleWalker->GetCheckForImportantRules();
1022 aRuleWalker->SetLevel(eOverrideSheet, false, true);
1023 if (mRuleProcessors[eOverrideSheet])
1024 (*aCollectorFunc)(mRuleProcessors[eOverrideSheet], aData);
1025 nsRuleNode* lastOvrRN = aRuleWalker->CurrentNode();
1026 bool haveImportantOverrideRules = !aRuleWalker->GetCheckForImportantRules();
1028 // This needs to match IsMoreSpecificThanAnimation() above.
1029 aRuleWalker->SetLevel(eAnimationSheet, false, false);
1030 (*aCollectorFunc)(mRuleProcessors[eAnimationSheet], aData);
1032 if (haveAnyImportantScopedRules) {
1033 for (uint32_t i = lastScopedRNs.Length(); i-- != 0; ) {
1034 aRuleWalker->SetLevel(eScopedDocSheet, true, false);
1035 nsRuleNode* startRN = lastScopedRNs[i];
1036 nsRuleNode* endRN = i == 0 ? lastDocRN : lastScopedRNs[i - 1];
1037 if (haveImportantScopedRules[i]) {
1038 AddImportantRules(startRN, endRN, aRuleWalker); // scoped
1039 }
1040 #ifdef DEBUG
1041 else {
1042 AssertNoImportantRules(startRN, endRN);
1043 }
1044 #endif
1045 }
1046 }
1047 #ifdef DEBUG
1048 else {
1049 AssertNoImportantRules(lastScopedRN, lastDocRN);
1050 }
1051 #endif
1053 if (haveImportantDocRules) {
1054 aRuleWalker->SetLevel(eDocSheet, true, false);
1055 AddImportantRules(lastDocRN, lastPresHintRN, aRuleWalker); // doc
1056 }
1057 #ifdef DEBUG
1058 else {
1059 AssertNoImportantRules(lastDocRN, lastPresHintRN);
1060 }
1061 #endif
1063 if (haveImportantStyleAttrRules) {
1064 aRuleWalker->SetLevel(eStyleAttrSheet, true, false);
1065 AddImportantRules(lastStyleAttrRN, lastScopedRN, aRuleWalker); // style attr
1066 }
1067 #ifdef DEBUG
1068 else {
1069 AssertNoImportantRules(lastStyleAttrRN, lastScopedRN);
1070 }
1071 #endif
1073 if (haveImportantOverrideRules) {
1074 aRuleWalker->SetLevel(eOverrideSheet, true, false);
1075 AddImportantRules(lastOvrRN, lastStyleAttrRN, aRuleWalker); // override
1076 }
1077 #ifdef DEBUG
1078 else {
1079 AssertNoImportantRules(lastOvrRN, lastStyleAttrRN);
1080 }
1081 #endif
1083 #ifdef DEBUG
1084 AssertNoCSSRules(lastPresHintRN, lastUserRN);
1085 #endif
1087 if (haveImportantUserRules) {
1088 aRuleWalker->SetLevel(eUserSheet, true, false);
1089 AddImportantRules(lastUserRN, lastAgentRN, aRuleWalker); //user
1090 }
1091 #ifdef DEBUG
1092 else {
1093 AssertNoImportantRules(lastUserRN, lastAgentRN);
1094 }
1095 #endif
1097 if (haveImportantUARules) {
1098 aRuleWalker->SetLevel(eAgentSheet, true, false);
1099 AddImportantRules(lastAgentRN, lastRestrictionRN, aRuleWalker); //agent
1100 }
1101 #ifdef DEBUG
1102 else {
1103 AssertNoImportantRules(lastAgentRN, lastRestrictionRN);
1104 }
1105 #endif
1107 #ifdef DEBUG
1108 AssertNoCSSRules(lastRestrictionRN, mRuleTree);
1109 #endif
1111 #ifdef DEBUG
1112 nsRuleNode *lastImportantRN = aRuleWalker->CurrentNode();
1113 #endif
1114 aRuleWalker->SetLevel(eTransitionSheet, false, false);
1115 (*aCollectorFunc)(mRuleProcessors[eTransitionSheet], aData);
1116 #ifdef DEBUG
1117 AssertNoCSSRules(aRuleWalker->CurrentNode(), lastImportantRN);
1118 #endif
1120 }
1122 // Enumerate all the rules in a way that doesn't care about the order
1123 // of the rules and doesn't walk !important-rules.
1124 void
1125 nsStyleSet::WalkRuleProcessors(nsIStyleRuleProcessor::EnumFunc aFunc,
1126 ElementDependentRuleProcessorData* aData,
1127 bool aWalkAllXBLStylesheets)
1128 {
1129 if (mRuleProcessors[eAgentSheet])
1130 (*aFunc)(mRuleProcessors[eAgentSheet], aData);
1132 bool skipUserStyles = aData->mElement->IsInNativeAnonymousSubtree();
1133 if (!skipUserStyles && mRuleProcessors[eUserSheet]) // NOTE: different
1134 (*aFunc)(mRuleProcessors[eUserSheet], aData);
1136 if (mRuleProcessors[ePresHintSheet])
1137 (*aFunc)(mRuleProcessors[ePresHintSheet], aData);
1139 bool cutOffInheritance = false;
1140 if (mBindingManager) {
1141 // We can supply additional document-level sheets that should be walked.
1142 if (aWalkAllXBLStylesheets) {
1143 mBindingManager->WalkAllRules(aFunc, aData);
1144 } else {
1145 mBindingManager->WalkRules(aFunc, aData, &cutOffInheritance);
1146 }
1147 }
1148 if (!skipUserStyles && !cutOffInheritance) {
1149 if (mRuleProcessors[eDocSheet]) // NOTE: different
1150 (*aFunc)(mRuleProcessors[eDocSheet], aData);
1151 if (aData->mElement->IsElementInStyleScope()) {
1152 for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++)
1153 (*aFunc)(mScopedDocSheetRuleProcessors[i], aData);
1154 }
1155 }
1156 if (mRuleProcessors[eStyleAttrSheet])
1157 (*aFunc)(mRuleProcessors[eStyleAttrSheet], aData);
1158 if (mRuleProcessors[eOverrideSheet])
1159 (*aFunc)(mRuleProcessors[eOverrideSheet], aData);
1160 (*aFunc)(mRuleProcessors[eAnimationSheet], aData);
1161 (*aFunc)(mRuleProcessors[eTransitionSheet], aData);
1162 }
1164 static void
1165 InitStyleScopes(TreeMatchContext& aTreeContext, Element* aElement)
1166 {
1167 if (aElement->IsElementInStyleScope()) {
1168 aTreeContext.InitStyleScopes(aElement->GetParentElement());
1169 }
1170 }
1172 already_AddRefed<nsStyleContext>
1173 nsStyleSet::ResolveStyleFor(Element* aElement,
1174 nsStyleContext* aParentContext)
1175 {
1176 TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited,
1177 aElement->OwnerDoc());
1178 InitStyleScopes(treeContext, aElement);
1179 return ResolveStyleFor(aElement, aParentContext, treeContext);
1180 }
1182 already_AddRefed<nsStyleContext>
1183 nsStyleSet::ResolveStyleFor(Element* aElement,
1184 nsStyleContext* aParentContext,
1185 TreeMatchContext& aTreeMatchContext)
1186 {
1187 NS_ENSURE_FALSE(mInShutdown, nullptr);
1188 NS_ASSERTION(aElement, "aElement must not be null");
1190 nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
1191 aTreeMatchContext.ResetForUnvisitedMatching();
1192 ElementRuleProcessorData data(PresContext(), aElement, &ruleWalker,
1193 aTreeMatchContext);
1194 WalkDisableTextZoomRule(aElement, &ruleWalker);
1195 FileRules(EnumRulesMatching<ElementRuleProcessorData>, &data, aElement,
1196 &ruleWalker);
1198 nsRuleNode *ruleNode = ruleWalker.CurrentNode();
1199 nsRuleNode *visitedRuleNode = nullptr;
1201 if (aTreeMatchContext.HaveRelevantLink()) {
1202 aTreeMatchContext.ResetForVisitedMatching();
1203 ruleWalker.Reset();
1204 FileRules(EnumRulesMatching<ElementRuleProcessorData>, &data, aElement,
1205 &ruleWalker);
1206 visitedRuleNode = ruleWalker.CurrentNode();
1207 }
1209 uint32_t flags = eDoAnimation;
1210 if (nsCSSRuleProcessor::IsLink(aElement)) {
1211 flags |= eIsLink;
1212 }
1213 if (nsCSSRuleProcessor::GetContentState(aElement, aTreeMatchContext).
1214 HasState(NS_EVENT_STATE_VISITED)) {
1215 flags |= eIsVisitedLink;
1216 }
1217 if (aTreeMatchContext.mSkippingFlexItemStyleFixup) {
1218 flags |= eSkipFlexItemStyleFixup;
1219 }
1221 return GetContext(aParentContext, ruleNode, visitedRuleNode,
1222 nullptr, nsCSSPseudoElements::ePseudo_NotPseudoElement,
1223 aElement, flags);
1224 }
1226 already_AddRefed<nsStyleContext>
1227 nsStyleSet::ResolveStyleForRules(nsStyleContext* aParentContext,
1228 const nsTArray< nsCOMPtr<nsIStyleRule> > &aRules)
1229 {
1230 NS_ENSURE_FALSE(mInShutdown, nullptr);
1232 nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
1233 // FIXME: Perhaps this should be passed in, but it probably doesn't
1234 // matter.
1235 ruleWalker.SetLevel(eDocSheet, false, false);
1236 for (uint32_t i = 0; i < aRules.Length(); i++) {
1237 ruleWalker.ForwardOnPossiblyCSSRule(aRules.ElementAt(i));
1238 }
1240 return GetContext(aParentContext, ruleWalker.CurrentNode(), nullptr,
1241 nullptr, nsCSSPseudoElements::ePseudo_NotPseudoElement,
1242 nullptr, eNoFlags);
1243 }
1245 already_AddRefed<nsStyleContext>
1246 nsStyleSet::ResolveStyleForRules(nsStyleContext* aParentContext,
1247 nsStyleContext* aOldStyle,
1248 const nsTArray<RuleAndLevel>& aRules)
1249 {
1250 nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
1251 for (int32_t i = aRules.Length() - 1; i >= 0; --i) {
1252 ruleWalker.SetLevel(aRules[i].mLevel, false, false);
1253 ruleWalker.ForwardOnPossiblyCSSRule(aRules[i].mRule);
1254 }
1256 uint32_t flags = eNoFlags;
1257 if (aOldStyle->IsLinkContext()) {
1258 flags |= eIsLink;
1259 }
1260 if (aOldStyle->RelevantLinkVisited()) {
1261 flags |= eIsVisitedLink;
1262 }
1264 return GetContext(aParentContext, ruleWalker.CurrentNode(), nullptr,
1265 nullptr, nsCSSPseudoElements::ePseudo_NotPseudoElement,
1266 nullptr, flags);
1267 }
1269 already_AddRefed<nsStyleContext>
1270 nsStyleSet::ResolveStyleByAddingRules(nsStyleContext* aBaseContext,
1271 const nsCOMArray<nsIStyleRule> &aRules)
1272 {
1273 NS_ENSURE_FALSE(mInShutdown, nullptr);
1275 nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
1276 ruleWalker.SetCurrentNode(aBaseContext->RuleNode());
1277 // FIXME: Perhaps this should be passed in, but it probably doesn't
1278 // matter.
1279 ruleWalker.SetLevel(eDocSheet, false, false);
1280 for (int32_t i = 0; i < aRules.Count(); i++) {
1281 ruleWalker.ForwardOnPossiblyCSSRule(aRules.ObjectAt(i));
1282 }
1284 nsRuleNode *ruleNode = ruleWalker.CurrentNode();
1285 nsRuleNode *visitedRuleNode = nullptr;
1287 if (aBaseContext->GetStyleIfVisited()) {
1288 ruleWalker.SetCurrentNode(aBaseContext->GetStyleIfVisited()->RuleNode());
1289 for (int32_t i = 0; i < aRules.Count(); i++) {
1290 ruleWalker.ForwardOnPossiblyCSSRule(aRules.ObjectAt(i));
1291 }
1292 visitedRuleNode = ruleWalker.CurrentNode();
1293 }
1295 uint32_t flags = eNoFlags;
1296 if (aBaseContext->IsLinkContext()) {
1297 flags |= eIsLink;
1298 }
1299 if (aBaseContext->RelevantLinkVisited()) {
1300 flags |= eIsVisitedLink;
1301 }
1302 return GetContext(aBaseContext->GetParent(), ruleNode, visitedRuleNode,
1303 aBaseContext->GetPseudo(),
1304 aBaseContext->GetPseudoType(),
1305 nullptr, flags);
1306 }
1308 already_AddRefed<nsStyleContext>
1309 nsStyleSet::ResolveStyleForNonElement(nsStyleContext* aParentContext)
1310 {
1311 return GetContext(aParentContext, mRuleTree, nullptr,
1312 nsCSSAnonBoxes::mozNonElement,
1313 nsCSSPseudoElements::ePseudo_AnonBox, nullptr,
1314 eNoFlags);
1315 }
1317 void
1318 nsStyleSet::WalkRestrictionRule(nsCSSPseudoElements::Type aPseudoType,
1319 nsRuleWalker* aRuleWalker)
1320 {
1321 // This needs to match GetPseudoRestriction in nsRuleNode.cpp.
1322 aRuleWalker->SetLevel(eAgentSheet, false, false);
1323 if (aPseudoType == nsCSSPseudoElements::ePseudo_firstLetter)
1324 aRuleWalker->Forward(mFirstLetterRule);
1325 else if (aPseudoType == nsCSSPseudoElements::ePseudo_firstLine)
1326 aRuleWalker->Forward(mFirstLineRule);
1327 else if (aPseudoType == nsCSSPseudoElements::ePseudo_mozPlaceholder)
1328 aRuleWalker->Forward(mPlaceholderRule);
1329 }
1331 void
1332 nsStyleSet::WalkDisableTextZoomRule(Element* aElement, nsRuleWalker* aRuleWalker)
1333 {
1334 aRuleWalker->SetLevel(eAgentSheet, false, false);
1335 if (aElement->IsSVG(nsGkAtoms::text))
1336 aRuleWalker->Forward(mDisableTextZoomStyleRule);
1337 }
1339 already_AddRefed<nsStyleContext>
1340 nsStyleSet::ResolvePseudoElementStyle(Element* aParentElement,
1341 nsCSSPseudoElements::Type aType,
1342 nsStyleContext* aParentContext,
1343 Element* aPseudoElement)
1344 {
1345 NS_ENSURE_FALSE(mInShutdown, nullptr);
1347 NS_ASSERTION(aType < nsCSSPseudoElements::ePseudo_PseudoElementCount,
1348 "must have pseudo element type");
1349 NS_ASSERTION(aParentElement, "Must have parent element");
1351 nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
1352 TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited,
1353 aParentElement->OwnerDoc());
1354 InitStyleScopes(treeContext, aParentElement);
1355 PseudoElementRuleProcessorData data(PresContext(), aParentElement,
1356 &ruleWalker, aType, treeContext,
1357 aPseudoElement);
1358 WalkRestrictionRule(aType, &ruleWalker);
1359 FileRules(EnumRulesMatching<PseudoElementRuleProcessorData>, &data,
1360 aParentElement, &ruleWalker);
1362 nsRuleNode *ruleNode = ruleWalker.CurrentNode();
1363 nsRuleNode *visitedRuleNode = nullptr;
1365 if (treeContext.HaveRelevantLink()) {
1366 treeContext.ResetForVisitedMatching();
1367 ruleWalker.Reset();
1368 WalkRestrictionRule(aType, &ruleWalker);
1369 FileRules(EnumRulesMatching<PseudoElementRuleProcessorData>, &data,
1370 aParentElement, &ruleWalker);
1371 visitedRuleNode = ruleWalker.CurrentNode();
1372 }
1374 // For pseudos, |data.IsLink()| being true means that
1375 // our parent node is a link.
1376 uint32_t flags = eNoFlags;
1377 if (aType == nsCSSPseudoElements::ePseudo_before ||
1378 aType == nsCSSPseudoElements::ePseudo_after) {
1379 flags |= eDoAnimation;
1380 } else {
1381 // Flex containers don't expect to have any pseudo-element children aside
1382 // from ::before and ::after. So if we have such a child, we're not
1383 // actually in a flex container, and we should skip flex-item style fixup.
1384 flags |= eSkipFlexItemStyleFixup;
1385 }
1387 return GetContext(aParentContext, ruleNode, visitedRuleNode,
1388 nsCSSPseudoElements::GetPseudoAtom(aType), aType,
1389 aParentElement, flags);
1390 }
1392 already_AddRefed<nsStyleContext>
1393 nsStyleSet::ProbePseudoElementStyle(Element* aParentElement,
1394 nsCSSPseudoElements::Type aType,
1395 nsStyleContext* aParentContext)
1396 {
1397 TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited,
1398 aParentElement->OwnerDoc());
1399 InitStyleScopes(treeContext, aParentElement);
1400 return ProbePseudoElementStyle(aParentElement, aType, aParentContext,
1401 treeContext);
1402 }
1404 already_AddRefed<nsStyleContext>
1405 nsStyleSet::ProbePseudoElementStyle(Element* aParentElement,
1406 nsCSSPseudoElements::Type aType,
1407 nsStyleContext* aParentContext,
1408 TreeMatchContext& aTreeMatchContext,
1409 Element* aPseudoElement)
1410 {
1411 NS_ENSURE_FALSE(mInShutdown, nullptr);
1413 NS_ASSERTION(aType < nsCSSPseudoElements::ePseudo_PseudoElementCount,
1414 "must have pseudo element type");
1415 NS_ASSERTION(aParentElement, "aParentElement must not be null");
1417 nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(aType);
1418 nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
1419 aTreeMatchContext.ResetForUnvisitedMatching();
1420 PseudoElementRuleProcessorData data(PresContext(), aParentElement,
1421 &ruleWalker, aType, aTreeMatchContext,
1422 aPseudoElement);
1423 WalkRestrictionRule(aType, &ruleWalker);
1424 // not the root if there was a restriction rule
1425 nsRuleNode *adjustedRoot = ruleWalker.CurrentNode();
1426 FileRules(EnumRulesMatching<PseudoElementRuleProcessorData>, &data,
1427 aParentElement, &ruleWalker);
1429 nsRuleNode *ruleNode = ruleWalker.CurrentNode();
1430 if (ruleNode == adjustedRoot) {
1431 return nullptr;
1432 }
1434 nsRuleNode *visitedRuleNode = nullptr;
1436 if (aTreeMatchContext.HaveRelevantLink()) {
1437 aTreeMatchContext.ResetForVisitedMatching();
1438 ruleWalker.Reset();
1439 WalkRestrictionRule(aType, &ruleWalker);
1440 FileRules(EnumRulesMatching<PseudoElementRuleProcessorData>, &data,
1441 aParentElement, &ruleWalker);
1442 visitedRuleNode = ruleWalker.CurrentNode();
1443 }
1445 // For pseudos, |data.IsLink()| being true means that
1446 // our parent node is a link.
1447 uint32_t flags = eNoFlags;
1448 if (aType == nsCSSPseudoElements::ePseudo_before ||
1449 aType == nsCSSPseudoElements::ePseudo_after) {
1450 flags |= eDoAnimation;
1451 } else {
1452 // Flex containers don't expect to have any pseudo-element children aside
1453 // from ::before and ::after. So if we have such a child, we're not
1454 // actually in a flex container, and we should skip flex-item style fixup.
1455 flags |= eSkipFlexItemStyleFixup;
1456 }
1458 nsRefPtr<nsStyleContext> result =
1459 GetContext(aParentContext, ruleNode, visitedRuleNode,
1460 pseudoTag, aType,
1461 aParentElement, flags);
1463 // For :before and :after pseudo-elements, having display: none or no
1464 // 'content' property is equivalent to not having the pseudo-element
1465 // at all.
1466 if (result &&
1467 (pseudoTag == nsCSSPseudoElements::before ||
1468 pseudoTag == nsCSSPseudoElements::after)) {
1469 const nsStyleDisplay *display = result->StyleDisplay();
1470 const nsStyleContent *content = result->StyleContent();
1471 // XXXldb What is contentCount for |content: ""|?
1472 if (display->mDisplay == NS_STYLE_DISPLAY_NONE ||
1473 content->ContentCount() == 0) {
1474 result = nullptr;
1475 }
1476 }
1478 return result.forget();
1479 }
1481 already_AddRefed<nsStyleContext>
1482 nsStyleSet::ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag,
1483 nsStyleContext* aParentContext)
1484 {
1485 NS_ENSURE_FALSE(mInShutdown, nullptr);
1487 #ifdef DEBUG
1488 bool isAnonBox = nsCSSAnonBoxes::IsAnonBox(aPseudoTag)
1489 #ifdef MOZ_XUL
1490 && !nsCSSAnonBoxes::IsTreePseudoElement(aPseudoTag)
1491 #endif
1492 ;
1493 NS_PRECONDITION(isAnonBox, "Unexpected pseudo");
1494 #endif
1496 nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
1497 AnonBoxRuleProcessorData data(PresContext(), aPseudoTag, &ruleWalker);
1498 FileRules(EnumRulesMatching<AnonBoxRuleProcessorData>, &data, nullptr,
1499 &ruleWalker);
1501 if (aPseudoTag == nsCSSAnonBoxes::pageContent) {
1502 // Add any @page rules that are specified.
1503 nsTArray<nsCSSPageRule*> rules;
1504 nsTArray<css::ImportantRule*> importantRules;
1505 nsPresContext* presContext = PresContext();
1506 presContext->StyleSet()->AppendPageRules(presContext, rules);
1507 for (uint32_t i = 0, i_end = rules.Length(); i != i_end; ++i) {
1508 ruleWalker.Forward(rules[i]);
1509 css::ImportantRule* importantRule = rules[i]->GetImportantRule();
1510 if (importantRule) {
1511 importantRules.AppendElement(importantRule);
1512 }
1513 }
1514 for (uint32_t i = 0, i_end = importantRules.Length(); i != i_end; ++i) {
1515 ruleWalker.Forward(importantRules[i]);
1516 }
1517 }
1519 return GetContext(aParentContext, ruleWalker.CurrentNode(), nullptr,
1520 aPseudoTag, nsCSSPseudoElements::ePseudo_AnonBox,
1521 nullptr, eNoFlags);
1522 }
1524 #ifdef MOZ_XUL
1525 already_AddRefed<nsStyleContext>
1526 nsStyleSet::ResolveXULTreePseudoStyle(Element* aParentElement,
1527 nsIAtom* aPseudoTag,
1528 nsStyleContext* aParentContext,
1529 nsICSSPseudoComparator* aComparator)
1530 {
1531 NS_ENSURE_FALSE(mInShutdown, nullptr);
1533 NS_ASSERTION(aPseudoTag, "must have pseudo tag");
1534 NS_ASSERTION(nsCSSAnonBoxes::IsTreePseudoElement(aPseudoTag),
1535 "Unexpected pseudo");
1537 nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
1538 TreeMatchContext treeContext(true, nsRuleWalker::eRelevantLinkUnvisited,
1539 aParentElement->OwnerDoc());
1540 InitStyleScopes(treeContext, aParentElement);
1541 XULTreeRuleProcessorData data(PresContext(), aParentElement, &ruleWalker,
1542 aPseudoTag, aComparator, treeContext);
1543 FileRules(EnumRulesMatching<XULTreeRuleProcessorData>, &data, aParentElement,
1544 &ruleWalker);
1546 nsRuleNode *ruleNode = ruleWalker.CurrentNode();
1547 nsRuleNode *visitedRuleNode = nullptr;
1549 if (treeContext.HaveRelevantLink()) {
1550 treeContext.ResetForVisitedMatching();
1551 ruleWalker.Reset();
1552 FileRules(EnumRulesMatching<XULTreeRuleProcessorData>, &data,
1553 aParentElement, &ruleWalker);
1554 visitedRuleNode = ruleWalker.CurrentNode();
1555 }
1557 return GetContext(aParentContext, ruleNode, visitedRuleNode,
1558 // For pseudos, |data.IsLink()| being true means that
1559 // our parent node is a link.
1560 aPseudoTag, nsCSSPseudoElements::ePseudo_XULTree,
1561 nullptr, eNoFlags);
1562 }
1563 #endif
1565 bool
1566 nsStyleSet::AppendFontFaceRules(nsPresContext* aPresContext,
1567 nsTArray<nsFontFaceRuleContainer>& aArray)
1568 {
1569 NS_ENSURE_FALSE(mInShutdown, false);
1571 for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) {
1572 if (gCSSSheetTypes[i] == eScopedDocSheet)
1573 continue;
1574 nsCSSRuleProcessor *ruleProc = static_cast<nsCSSRuleProcessor*>
1575 (mRuleProcessors[gCSSSheetTypes[i]].get());
1576 if (ruleProc && !ruleProc->AppendFontFaceRules(aPresContext, aArray))
1577 return false;
1578 }
1579 return true;
1580 }
1582 nsCSSKeyframesRule*
1583 nsStyleSet::KeyframesRuleForName(nsPresContext* aPresContext,
1584 const nsString& aName)
1585 {
1586 NS_ENSURE_FALSE(mInShutdown, nullptr);
1588 for (uint32_t i = ArrayLength(gCSSSheetTypes); i-- != 0; ) {
1589 if (gCSSSheetTypes[i] == eScopedDocSheet)
1590 continue;
1591 nsCSSRuleProcessor *ruleProc = static_cast<nsCSSRuleProcessor*>
1592 (mRuleProcessors[gCSSSheetTypes[i]].get());
1593 if (!ruleProc)
1594 continue;
1595 nsCSSKeyframesRule* result =
1596 ruleProc->KeyframesRuleForName(aPresContext, aName);
1597 if (result)
1598 return result;
1599 }
1600 return nullptr;
1601 }
1603 bool
1604 nsStyleSet::AppendFontFeatureValuesRules(nsPresContext* aPresContext,
1605 nsTArray<nsCSSFontFeatureValuesRule*>& aArray)
1606 {
1607 NS_ENSURE_FALSE(mInShutdown, false);
1609 for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) {
1610 nsCSSRuleProcessor *ruleProc = static_cast<nsCSSRuleProcessor*>
1611 (mRuleProcessors[gCSSSheetTypes[i]].get());
1612 if (ruleProc &&
1613 !ruleProc->AppendFontFeatureValuesRules(aPresContext, aArray))
1614 {
1615 return false;
1616 }
1617 }
1618 return true;
1619 }
1621 already_AddRefed<gfxFontFeatureValueSet>
1622 nsStyleSet::GetFontFeatureValuesLookup()
1623 {
1624 if (mInitFontFeatureValuesLookup) {
1625 mInitFontFeatureValuesLookup = false;
1627 nsTArray<nsCSSFontFeatureValuesRule*> rules;
1628 AppendFontFeatureValuesRules(PresContext(), rules);
1630 mFontFeatureValuesLookup = new gfxFontFeatureValueSet();
1632 uint32_t i, numRules = rules.Length();
1633 for (i = 0; i < numRules; i++) {
1634 nsCSSFontFeatureValuesRule *rule = rules[i];
1636 const nsTArray<nsString>& familyList = rule->GetFamilyList();
1637 const nsTArray<gfxFontFeatureValueSet::FeatureValues>&
1638 featureValues = rule->GetFeatureValues();
1640 // for each family
1641 uint32_t f, numFam;
1643 numFam = familyList.Length();
1644 for (f = 0; f < numFam; f++) {
1645 const nsString& family = familyList.ElementAt(f);
1646 nsAutoString silly(family);
1647 mFontFeatureValuesLookup->AddFontFeatureValues(silly, featureValues);
1648 }
1649 }
1650 }
1652 nsRefPtr<gfxFontFeatureValueSet> lookup = mFontFeatureValuesLookup;
1653 return lookup.forget();
1654 }
1656 bool
1657 nsStyleSet::AppendPageRules(nsPresContext* aPresContext,
1658 nsTArray<nsCSSPageRule*>& aArray)
1659 {
1660 NS_ENSURE_FALSE(mInShutdown, false);
1662 for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) {
1663 if (gCSSSheetTypes[i] == eScopedDocSheet)
1664 continue;
1665 nsCSSRuleProcessor* ruleProc = static_cast<nsCSSRuleProcessor*>
1666 (mRuleProcessors[gCSSSheetTypes[i]].get());
1667 if (ruleProc && !ruleProc->AppendPageRules(aPresContext, aArray))
1668 return false;
1669 }
1670 return true;
1671 }
1673 void
1674 nsStyleSet::BeginShutdown(nsPresContext* aPresContext)
1675 {
1676 mInShutdown = 1;
1677 mRoots.Clear(); // no longer valid, since we won't keep it up to date
1678 }
1680 void
1681 nsStyleSet::Shutdown(nsPresContext* aPresContext)
1682 {
1683 mRuleTree->Destroy();
1684 mRuleTree = nullptr;
1686 // We can have old rule trees either because:
1687 // (1) we failed the assertions in EndReconstruct, or
1688 // (2) we're shutting down within a reconstruct (see bug 462392)
1689 for (uint32_t i = mOldRuleTrees.Length(); i > 0; ) {
1690 --i;
1691 mOldRuleTrees[i]->Destroy();
1692 }
1693 mOldRuleTrees.Clear();
1694 }
1696 static const uint32_t kGCInterval = 300;
1698 void
1699 nsStyleSet::NotifyStyleContextDestroyed(nsPresContext* aPresContext,
1700 nsStyleContext* aStyleContext)
1701 {
1702 if (mInShutdown)
1703 return;
1705 // Remove style contexts from mRoots even if mOldRuleTree is non-null. This
1706 // could be a style context from the new ruletree!
1707 if (!aStyleContext->GetParent()) {
1708 mRoots.RemoveElement(aStyleContext);
1709 }
1711 if (mInReconstruct)
1712 return;
1714 if (mUnusedRuleNodeCount >= kGCInterval) {
1715 GCRuleTrees();
1716 }
1717 }
1719 void
1720 nsStyleSet::GCRuleTrees()
1721 {
1722 mUnusedRuleNodeCount = 0;
1724 // Mark the style context tree by marking all style contexts which
1725 // have no parent, which will mark all descendants. This will reach
1726 // style contexts in the undisplayed map and "additional style
1727 // contexts" since they are descendants of the roots.
1728 for (int32_t i = mRoots.Length() - 1; i >= 0; --i) {
1729 mRoots[i]->Mark();
1730 }
1732 // Sweep the rule tree.
1733 #ifdef DEBUG
1734 bool deleted =
1735 #endif
1736 mRuleTree->Sweep();
1737 NS_ASSERTION(!deleted, "Root node must not be gc'd");
1739 // Sweep the old rule trees.
1740 for (uint32_t i = mOldRuleTrees.Length(); i > 0; ) {
1741 --i;
1742 if (mOldRuleTrees[i]->Sweep()) {
1743 // It was deleted, as it should be.
1744 mOldRuleTrees.RemoveElementAt(i);
1745 } else {
1746 NS_NOTREACHED("old rule tree still referenced");
1747 }
1748 }
1749 }
1751 /**
1752 * Return an equivalent to aRuleNode with both animation and transition
1753 * rules removed, and post a restyle if needed.
1754 */
1755 static inline nsRuleNode*
1756 SkipAnimationRules(nsRuleNode* aRuleNode, Element* aElement, bool isPseudo)
1757 {
1758 nsRuleNode* ruleNode = aRuleNode;
1759 // The transition rule must be at the top of the cascade.
1760 if (!ruleNode->IsRoot() &&
1761 ruleNode->GetLevel() == nsStyleSet::eTransitionSheet) {
1762 ruleNode = ruleNode->GetParent();
1763 }
1764 NS_ABORT_IF_FALSE(ruleNode->IsRoot() ||
1765 ruleNode->GetLevel() != nsStyleSet::eTransitionSheet,
1766 "can't have more than one transition rule");
1768 // Use our existing ReplaceAnimationRule function to replace the
1769 // animation rule, if present.
1770 nsIStyleRule* animationRule = GetAnimationRule(ruleNode);
1771 if (animationRule) {
1772 ruleNode = ReplaceAnimationRule(ruleNode, animationRule, nullptr);
1773 }
1775 if (ruleNode != aRuleNode) {
1776 NS_ASSERTION(aElement, "How can we have transition rules but no element?");
1777 // Need to do an animation restyle, just like
1778 // nsTransitionManager::WalkTransitionRule and
1779 // nsAnimationManager::GetAnimationRule would.
1780 nsRestyleHint hint = isPseudo ? eRestyle_Subtree : eRestyle_Self;
1781 aRuleNode->PresContext()->PresShell()->RestyleForAnimation(aElement, hint);
1782 }
1783 return ruleNode;
1784 }
1786 already_AddRefed<nsStyleContext>
1787 nsStyleSet::ReparentStyleContext(nsStyleContext* aStyleContext,
1788 nsStyleContext* aNewParentContext,
1789 Element* aElement)
1790 {
1791 MOZ_ASSERT(aStyleContext, "aStyleContext must not be null");
1793 // This short-circuit is OK because we don't call TryStartingTransition
1794 // during style reresolution if the style context pointer hasn't changed.
1795 if (aStyleContext->GetParent() == aNewParentContext) {
1796 nsRefPtr<nsStyleContext> ret = aStyleContext;
1797 return ret.forget();
1798 }
1800 nsIAtom* pseudoTag = aStyleContext->GetPseudo();
1801 nsCSSPseudoElements::Type pseudoType = aStyleContext->GetPseudoType();
1802 nsRuleNode* ruleNode = aStyleContext->RuleNode();
1804 // Skip transition rules as needed just like
1805 // nsTransitionManager::WalkTransitionRule would.
1806 bool skipAnimationRules = PresContext()->IsProcessingRestyles() &&
1807 !PresContext()->IsProcessingAnimationStyleChange();
1808 if (skipAnimationRules) {
1809 // Make sure that we're not using transition rules or animation rules for
1810 // our new style context. If we need them, an animation restyle will
1811 // provide.
1812 ruleNode =
1813 SkipAnimationRules(ruleNode, aElement,
1814 pseudoType !=
1815 nsCSSPseudoElements::ePseudo_NotPseudoElement);
1816 }
1818 nsRuleNode* visitedRuleNode = nullptr;
1819 nsStyleContext* visitedContext = aStyleContext->GetStyleIfVisited();
1820 // Reparenting a style context just changes where we inherit from,
1821 // not what rules we match or what our DOM looks like. In
1822 // particular, it doesn't change whether this is a style context for
1823 // a link.
1824 if (visitedContext) {
1825 visitedRuleNode = visitedContext->RuleNode();
1826 // Again, skip transition rules as needed
1827 if (skipAnimationRules) {
1828 // FIXME do something here for animations?
1829 visitedRuleNode =
1830 SkipAnimationRules(visitedRuleNode, aElement,
1831 pseudoType !=
1832 nsCSSPseudoElements::ePseudo_NotPseudoElement);
1833 }
1834 }
1836 uint32_t flags = eNoFlags;
1837 if (aStyleContext->IsLinkContext()) {
1838 flags |= eIsLink;
1839 }
1841 // If we're a style context for a link, then we already know whether
1842 // our relevant link is visited, since that does not depend on our
1843 // parent. Otherwise, we need to match aNewParentContext.
1844 bool relevantLinkVisited = aStyleContext->IsLinkContext() ?
1845 aStyleContext->RelevantLinkVisited() :
1846 aNewParentContext->RelevantLinkVisited();
1848 if (relevantLinkVisited) {
1849 flags |= eIsVisitedLink;
1850 }
1852 if (pseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
1853 pseudoType == nsCSSPseudoElements::ePseudo_before ||
1854 pseudoType == nsCSSPseudoElements::ePseudo_after) {
1855 flags |= eDoAnimation;
1856 }
1858 if (aElement && aElement->IsRootOfAnonymousSubtree()) {
1859 // For anonymous subtree roots, don't tweak "display" value based on
1860 // whether or not the parent is styled as a flex container. (If the parent
1861 // has anonymous-subtree kids, then we know it's not actually going to get
1862 // a flex container frame, anyway.)
1863 flags |= eSkipFlexItemStyleFixup;
1864 }
1866 return GetContext(aNewParentContext, ruleNode, visitedRuleNode,
1867 pseudoTag, pseudoType,
1868 aElement, flags);
1869 }
1871 struct MOZ_STACK_CLASS StatefulData : public StateRuleProcessorData {
1872 StatefulData(nsPresContext* aPresContext, Element* aElement,
1873 EventStates aStateMask, TreeMatchContext& aTreeMatchContext)
1874 : StateRuleProcessorData(aPresContext, aElement, aStateMask,
1875 aTreeMatchContext),
1876 mHint(nsRestyleHint(0))
1877 {}
1878 nsRestyleHint mHint;
1879 };
1881 struct MOZ_STACK_CLASS StatefulPseudoElementData : public PseudoElementStateRuleProcessorData {
1882 StatefulPseudoElementData(nsPresContext* aPresContext, Element* aElement,
1883 EventStates aStateMask, nsCSSPseudoElements::Type aPseudoType,
1884 TreeMatchContext& aTreeMatchContext, Element* aPseudoElement)
1885 : PseudoElementStateRuleProcessorData(aPresContext, aElement, aStateMask,
1886 aPseudoType, aTreeMatchContext,
1887 aPseudoElement),
1888 mHint(nsRestyleHint(0))
1889 {}
1890 nsRestyleHint mHint;
1891 };
1893 static bool SheetHasDocumentStateStyle(nsIStyleRuleProcessor* aProcessor,
1894 void *aData)
1895 {
1896 StatefulData* data = (StatefulData*)aData;
1897 if (aProcessor->HasDocumentStateDependentStyle(data)) {
1898 data->mHint = eRestyle_Self;
1899 return false; // don't continue
1900 }
1901 return true; // continue
1902 }
1904 // Test if style is dependent on a document state.
1905 bool
1906 nsStyleSet::HasDocumentStateDependentStyle(nsPresContext* aPresContext,
1907 nsIContent* aContent,
1908 EventStates aStateMask)
1909 {
1910 if (!aContent || !aContent->IsElement())
1911 return false;
1913 TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited,
1914 aContent->OwnerDoc());
1915 InitStyleScopes(treeContext, aContent->AsElement());
1916 StatefulData data(aPresContext, aContent->AsElement(), aStateMask,
1917 treeContext);
1918 WalkRuleProcessors(SheetHasDocumentStateStyle, &data, true);
1919 return data.mHint != 0;
1920 }
1922 static bool SheetHasStatefulStyle(nsIStyleRuleProcessor* aProcessor,
1923 void *aData)
1924 {
1925 StatefulData* data = (StatefulData*)aData;
1926 nsRestyleHint hint = aProcessor->HasStateDependentStyle(data);
1927 data->mHint = nsRestyleHint(data->mHint | hint);
1928 return true; // continue
1929 }
1931 static bool SheetHasStatefulPseudoElementStyle(nsIStyleRuleProcessor* aProcessor,
1932 void *aData)
1933 {
1934 StatefulPseudoElementData* data = (StatefulPseudoElementData*)aData;
1935 nsRestyleHint hint = aProcessor->HasStateDependentStyle(data);
1936 data->mHint = nsRestyleHint(data->mHint | hint);
1937 return true; // continue
1938 }
1940 // Test if style is dependent on content state
1941 nsRestyleHint
1942 nsStyleSet::HasStateDependentStyle(nsPresContext* aPresContext,
1943 Element* aElement,
1944 EventStates aStateMask)
1945 {
1946 TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited,
1947 aElement->OwnerDoc());
1948 InitStyleScopes(treeContext, aElement);
1949 StatefulData data(aPresContext, aElement, aStateMask, treeContext);
1950 WalkRuleProcessors(SheetHasStatefulStyle, &data, false);
1951 return data.mHint;
1952 }
1954 nsRestyleHint
1955 nsStyleSet::HasStateDependentStyle(nsPresContext* aPresContext,
1956 Element* aElement,
1957 nsCSSPseudoElements::Type aPseudoType,
1958 Element* aPseudoElement,
1959 EventStates aStateMask)
1960 {
1961 TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited,
1962 aElement->OwnerDoc());
1963 InitStyleScopes(treeContext, aElement);
1964 StatefulPseudoElementData data(aPresContext, aElement, aStateMask,
1965 aPseudoType, treeContext, aPseudoElement);
1966 WalkRuleProcessors(SheetHasStatefulPseudoElementStyle, &data, false);
1967 return data.mHint;
1968 }
1970 struct MOZ_STACK_CLASS AttributeData : public AttributeRuleProcessorData {
1971 AttributeData(nsPresContext* aPresContext,
1972 Element* aElement, nsIAtom* aAttribute, int32_t aModType,
1973 bool aAttrHasChanged, TreeMatchContext& aTreeMatchContext)
1974 : AttributeRuleProcessorData(aPresContext, aElement, aAttribute, aModType,
1975 aAttrHasChanged, aTreeMatchContext),
1976 mHint(nsRestyleHint(0))
1977 {}
1978 nsRestyleHint mHint;
1979 };
1981 static bool
1982 SheetHasAttributeStyle(nsIStyleRuleProcessor* aProcessor, void *aData)
1983 {
1984 AttributeData* data = (AttributeData*)aData;
1985 nsRestyleHint hint = aProcessor->HasAttributeDependentStyle(data);
1986 data->mHint = nsRestyleHint(data->mHint | hint);
1987 return true; // continue
1988 }
1990 // Test if style is dependent on content state
1991 nsRestyleHint
1992 nsStyleSet::HasAttributeDependentStyle(nsPresContext* aPresContext,
1993 Element* aElement,
1994 nsIAtom* aAttribute,
1995 int32_t aModType,
1996 bool aAttrHasChanged)
1997 {
1998 TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited,
1999 aElement->OwnerDoc());
2000 InitStyleScopes(treeContext, aElement);
2001 AttributeData data(aPresContext, aElement, aAttribute,
2002 aModType, aAttrHasChanged, treeContext);
2003 WalkRuleProcessors(SheetHasAttributeStyle, &data, false);
2004 return data.mHint;
2005 }
2007 bool
2008 nsStyleSet::MediumFeaturesChanged(nsPresContext* aPresContext)
2009 {
2010 // We can't use WalkRuleProcessors without a content node.
2011 bool stylesChanged = false;
2012 for (uint32_t i = 0; i < ArrayLength(mRuleProcessors); ++i) {
2013 nsIStyleRuleProcessor *processor = mRuleProcessors[i];
2014 if (!processor) {
2015 continue;
2016 }
2017 bool thisChanged = processor->MediumFeaturesChanged(aPresContext);
2018 stylesChanged = stylesChanged || thisChanged;
2019 }
2020 for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); ++i) {
2021 nsIStyleRuleProcessor *processor = mScopedDocSheetRuleProcessors[i];
2022 bool thisChanged = processor->MediumFeaturesChanged(aPresContext);
2023 stylesChanged = stylesChanged || thisChanged;
2024 }
2026 if (mBindingManager) {
2027 bool thisChanged = false;
2028 mBindingManager->MediumFeaturesChanged(aPresContext, &thisChanged);
2029 stylesChanged = stylesChanged || thisChanged;
2030 }
2032 return stylesChanged;
2033 }
2035 nsCSSStyleSheet::EnsureUniqueInnerResult
2036 nsStyleSet::EnsureUniqueInnerOnCSSSheets()
2037 {
2038 nsAutoTArray<nsCSSStyleSheet*, 32> queue;
2039 for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) {
2040 nsCOMArray<nsIStyleSheet> &sheets = mSheets[gCSSSheetTypes[i]];
2041 for (uint32_t j = 0, j_end = sheets.Count(); j < j_end; ++j) {
2042 nsCSSStyleSheet *sheet = static_cast<nsCSSStyleSheet*>(sheets[j]);
2043 queue.AppendElement(sheet);
2044 }
2045 }
2047 if (mBindingManager) {
2048 mBindingManager->AppendAllSheets(queue);
2049 }
2051 nsCSSStyleSheet::EnsureUniqueInnerResult res =
2052 nsCSSStyleSheet::eUniqueInner_AlreadyUnique;
2053 while (!queue.IsEmpty()) {
2054 uint32_t idx = queue.Length() - 1;
2055 nsCSSStyleSheet *sheet = queue[idx];
2056 queue.RemoveElementAt(idx);
2058 nsCSSStyleSheet::EnsureUniqueInnerResult sheetRes =
2059 sheet->EnsureUniqueInner();
2060 if (sheetRes == nsCSSStyleSheet::eUniqueInner_ClonedInner) {
2061 res = sheetRes;
2062 }
2064 // Enqueue all the sheet's children.
2065 sheet->AppendAllChildSheets(queue);
2066 }
2067 return res;
2068 }
2070 nsIStyleRule*
2071 nsStyleSet::InitialStyleRule()
2072 {
2073 if (!mInitialStyleRule) {
2074 mInitialStyleRule = new nsInitialStyleRule;
2075 }
2076 return mInitialStyleRule;
2077 }