1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/GeckoStyleContext.h"
8 
9 #include "CSSVariableImageTable.h"
10 #include "nsFontMetrics.h"
11 #include "nsStyleConsts.h"
12 #include "nsStyleStruct.h"
13 #include "nsPresContext.h"
14 #include "nsRuleNode.h"
15 #include "nsStyleContextInlines.h"
16 #include "nsIFrame.h"
17 #include "nsLayoutUtils.h"
18 #include "mozilla/ReflowInput.h"
19 #include "mozilla/StyleSetHandleInlines.h"
20 #include "RubyUtils.h"
21 
22 using namespace mozilla;
23 
24 #ifdef DEBUG
25 // Whether to perform expensive assertions in the nsStyleContext destructor.
26 static bool sExpensiveStyleStructAssertionsEnabled;
27 
Initialize()28 /* static */ void GeckoStyleContext::Initialize() {
29   Preferences::AddBoolVarCache(
30       &sExpensiveStyleStructAssertionsEnabled,
31       "layout.css.expensive-style-struct-assertions.enabled");
32 }
33 #endif
34 
GeckoStyleContext(GeckoStyleContext * aParent,nsAtom * aPseudoTag,CSSPseudoElementType aPseudoType,already_AddRefed<nsRuleNode> aRuleNode,bool aSkipParentDisplayBasedStyleFixup)35 GeckoStyleContext::GeckoStyleContext(GeckoStyleContext* aParent,
36                                      nsAtom* aPseudoTag,
37                                      CSSPseudoElementType aPseudoType,
38                                      already_AddRefed<nsRuleNode> aRuleNode,
39                                      bool aSkipParentDisplayBasedStyleFixup)
40     : nsStyleContext(aPseudoTag, aPseudoType),
41       mCachedResetData(nullptr),
42       mRefCnt(0),
43       mChild(nullptr),
44       mEmptyChild(nullptr),
45       mRuleNode(Move(aRuleNode)),
46       mParent(aParent)
47 #ifdef DEBUG
48       ,
49       mComputingStruct(nsStyleStructID_None),
50       mFrameRefCnt(0)
51 #endif
52 {
53   mBits |= NS_STYLE_CONTEXT_IS_GECKO;
54 
55   if (aParent) {
56 #ifdef DEBUG
57     nsRuleNode *r1 = mParent->RuleNode(), *r2 = mRuleNode;
58     while (r1->GetParent()) r1 = r1->GetParent();
59     while (r2->GetParent()) r2 = r2->GetParent();
60     NS_ASSERTION(r1 == r2, "must be in the same rule tree as parent");
61 #endif
62   } else {
63     PresContext()->PresShell()->StyleSet()->RootStyleContextAdded();
64   }
65 
66   mRuleNode->SetUsedDirectly();  // before ApplyStyleFixups()!
67   // FinishConstruction() calls AddChild which needs these
68   // to be initialized!
69   mNextSibling = this;
70   mPrevSibling = this;
71 
72   FinishConstruction();
73   ApplyStyleFixups(aSkipParentDisplayBasedStyleFixup);
74 }
75 
76 // Overloaded new operator. Initializes the memory to 0 and relies on an arena
77 // (which comes from the presShell) to perform the allocation.
operator new(size_t sz,nsPresContext * aPresContext)78 void* GeckoStyleContext::operator new(size_t sz, nsPresContext* aPresContext) {
79   MOZ_ASSERT(sz == sizeof(GeckoStyleContext));
80   // Check the recycle list first.
81   return aPresContext->PresShell()->AllocateByObjectID(
82       eArenaObjectID_GeckoStyleContext, sz);
83 }
84 
85 // Overridden to prevent the global delete from being called, since the memory
86 // came out of an nsIArena instead of the global delete operator's heap.
Destroy()87 void GeckoStyleContext::Destroy() {
88   // Get the pres context.
89   RefPtr<nsPresContext> presContext = PresContext();
90   // Call our destructor.
91   this->~GeckoStyleContext();
92   // Don't let the memory be freed, since it will be recycled
93   // instead. Don't call the global operator delete.
94   presContext->PresShell()->FreeByObjectID(eArenaObjectID_GeckoStyleContext,
95                                            this);
96 }
97 
~GeckoStyleContext()98 GeckoStyleContext::~GeckoStyleContext() {
99   nsPresContext* presContext = PresContext();
100 #ifdef DEBUG
101   NS_ASSERTION(HasNoChildren(), "destructing context with children");
102   if (sExpensiveStyleStructAssertionsEnabled) {
103     // Assert that the style structs we are about to destroy are not referenced
104     // anywhere else in the style context tree.  These checks are expensive,
105     // which is why they are not enabled by default.
106     GeckoStyleContext* root = this;
107     while (root->GetParent()) {
108       root = root->GetParent();
109     }
110     root->AssertStructsNotUsedElsewhere(this,
111                                         std::numeric_limits<int32_t>::max());
112   } else {
113     // In DEBUG builds when the pref is not enabled, we perform a more limited
114     // check just of the children of this style context.
115     this->AssertStructsNotUsedElsewhere(this, 2);
116   }
117 
118   nsStyleSet* geckoStyleSet =
119       presContext->PresShell()->StyleSet()->GetAsGecko();
120   NS_ASSERTION(
121       !geckoStyleSet ||
122           geckoStyleSet->GetRuleTree() == AsGecko()->RuleNode()->RuleTree() ||
123           geckoStyleSet->IsInRuleTreeReconstruct(),
124       "destroying style context from old rule tree too late");
125 #endif
126 
127   if (mParent) {
128     mParent->AsGecko()->RemoveChild(this);
129   } else {
130     presContext->StyleSet()->RootStyleContextRemoved();
131   }
132 
133   // Free up our data structs.
134   DestroyCachedStructs(presContext);
135   CSSVariableImageTable::RemoveAll(this);
136 }
137 
AddChild(GeckoStyleContext * aChild)138 void GeckoStyleContext::AddChild(GeckoStyleContext* aChild) {
139   NS_ASSERTION(aChild->mPrevSibling == aChild && aChild->mNextSibling == aChild,
140                "child already in a child list");
141 
142   GeckoStyleContext** listPtr =
143       aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild;
144   if (const nsRuleNode* source = aChild->mRuleNode) {
145     if (source->IsRoot()) {
146       listPtr = &mEmptyChild;
147     }
148   }
149 
150   // Explicitly dereference listPtr so that compiler doesn't have to know that
151   // mNextSibling etc. don't alias with what ever listPtr points at.
152   GeckoStyleContext* list = *listPtr;
153 
154   // Insert at the beginning of the list.  See also FindChildWithRules.
155   if (list) {
156     // Link into existing elements, if there are any.
157     aChild->mNextSibling = list;
158     aChild->mPrevSibling = list->mPrevSibling;
159     list->mPrevSibling->mNextSibling = aChild;
160     list->mPrevSibling = aChild;
161   }
162   (*listPtr) = aChild;
163 }
164 
MoveTo(GeckoStyleContext * aNewParent)165 void GeckoStyleContext::MoveTo(GeckoStyleContext* aNewParent) {
166   MOZ_ASSERT(aNewParent != mParent);
167 
168   // This function shouldn't be getting called if the parents have different
169   // values for some flags in mBits (unless the flag is also set on this style
170   // context) because if that were the case we would need to recompute those
171   // bits for |this|.
172 
173 #define CHECK_FLAG(bit_)                                                      \
174   MOZ_ASSERT(                                                                 \
175       (mParent->AsGecko()->mBits & (bit_)) == (aNewParent->mBits & (bit_)) || \
176           (mBits & (bit_)),                                                   \
177       "MoveTo cannot be called if " #bit_                                     \
178       " value on old and new "                                                \
179       "style context parents do not match, unless the flag is set "           \
180       "on this style context");
181 
182   CHECK_FLAG(NS_STYLE_HAS_PSEUDO_ELEMENT_DATA)
183   CHECK_FLAG(NS_STYLE_IN_DISPLAY_NONE_SUBTREE)
184   CHECK_FLAG(NS_STYLE_HAS_TEXT_DECORATION_LINES)
185   CHECK_FLAG(NS_STYLE_RELEVANT_LINK_VISITED)
186 
187 #undef CHECK_FLAG
188 
189   // Assertions checking for visited style are just to avoid some tricky
190   // cases we can't be bothered handling at the moment.
191   MOZ_ASSERT(!IsStyleIfVisited());
192   MOZ_ASSERT(!mParent->IsStyleIfVisited());
193   MOZ_ASSERT(!aNewParent->IsStyleIfVisited());
194   auto* styleIfVisited = GetStyleIfVisited();
195   MOZ_ASSERT(!styleIfVisited || styleIfVisited->mParent == mParent);
196 
197   if (mParent->HasChildThatUsesResetStyle()) {
198     aNewParent->AddStyleBit(NS_STYLE_HAS_CHILD_THAT_USES_RESET_STYLE);
199   }
200 
201   mParent->RemoveChild(this);
202   mParent = aNewParent;
203   mParent->AddChild(this);
204 
205   if (styleIfVisited) {
206     styleIfVisited->mParent->RemoveChild(styleIfVisited);
207     styleIfVisited->mParent = aNewParent;
208     styleIfVisited->mParent->AddChild(styleIfVisited);
209   }
210 }
211 
RemoveChild(GeckoStyleContext * aChild)212 void GeckoStyleContext::RemoveChild(GeckoStyleContext* aChild) {
213   NS_PRECONDITION(nullptr != aChild && this == aChild->mParent, "bad argument");
214 
215   MOZ_ASSERT(aChild->mRuleNode, "child context should have rule node");
216   GeckoStyleContext** list =
217       aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild;
218 
219   if (aChild->mPrevSibling != aChild) {  // has siblings
220     if ((*list) == aChild) {
221       (*list) = (*list)->mNextSibling;
222     }
223   } else {
224     NS_ASSERTION((*list) == aChild, "bad sibling pointers");
225     (*list) = nullptr;
226   }
227 
228   aChild->mPrevSibling->mNextSibling = aChild->mNextSibling;
229   aChild->mNextSibling->mPrevSibling = aChild->mPrevSibling;
230   aChild->mNextSibling = aChild;
231   aChild->mPrevSibling = aChild;
232 }
233 
234 #ifdef DEBUG
ListDescendants(FILE * out,int32_t aIndent)235 void GeckoStyleContext::ListDescendants(FILE* out, int32_t aIndent) {
236   if (nullptr != mChild) {
237     GeckoStyleContext* child = mChild;
238     do {
239       child->List(out, aIndent + 1, true);
240       child = child->mNextSibling;
241     } while (mChild != child);
242   }
243   if (nullptr != mEmptyChild) {
244     GeckoStyleContext* child = mEmptyChild;
245     do {
246       child->List(out, aIndent + 1, true);
247       child = child->mNextSibling;
248     } while (mEmptyChild != child);
249   }
250 }
251 #endif
252 
ClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs)253 void GeckoStyleContext::ClearCachedInheritedStyleDataOnDescendants(
254     uint32_t aStructs) {
255   if (mChild) {
256     GeckoStyleContext* child = mChild;
257     do {
258       child->DoClearCachedInheritedStyleDataOnDescendants(aStructs);
259       child = child->mNextSibling;
260     } while (mChild != child);
261   }
262   if (mEmptyChild) {
263     GeckoStyleContext* child = mEmptyChild;
264     do {
265       child->DoClearCachedInheritedStyleDataOnDescendants(aStructs);
266       child = child->mNextSibling;
267     } while (mEmptyChild != child);
268   }
269 }
270 
DoClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs)271 void GeckoStyleContext::DoClearCachedInheritedStyleDataOnDescendants(
272     uint32_t aStructs) {
273   NS_ASSERTION(mFrameRefCnt == 0, "frame still referencing style context");
274   for (nsStyleStructID i = nsStyleStructID_Inherited_Start;
275        i < nsStyleStructID_Inherited_Start + nsStyleStructID_Inherited_Count;
276        i = nsStyleStructID(i + 1)) {
277     uint32_t bit = nsCachedStyleData::GetBitForSID(i);
278     if (aStructs & bit) {
279       if (!(mBits & bit) && mCachedInheritedData.mStyleStructs[i]) {
280         aStructs &= ~bit;
281       } else {
282         mCachedInheritedData.mStyleStructs[i] = nullptr;
283       }
284     }
285   }
286 
287   if (mCachedResetData) {
288     for (nsStyleStructID i = nsStyleStructID_Reset_Start;
289          i < nsStyleStructID_Reset_Start + nsStyleStructID_Reset_Count;
290          i = nsStyleStructID(i + 1)) {
291       uint32_t bit = nsCachedStyleData::GetBitForSID(i);
292       if (aStructs & bit) {
293         if (!(mBits & bit) && mCachedResetData->mStyleStructs[i]) {
294           aStructs &= ~bit;
295         } else {
296           mCachedResetData->mStyleStructs[i] = nullptr;
297         }
298       }
299     }
300   }
301 
302   if (aStructs == 0) {
303     return;
304   }
305 
306   ClearCachedInheritedStyleDataOnDescendants(aStructs);
307 }
308 
FindChildWithRules(const nsAtom * aPseudoTag,nsRuleNode * aSource,nsRuleNode * aSourceIfVisited,bool aRelevantLinkVisited)309 already_AddRefed<GeckoStyleContext> GeckoStyleContext::FindChildWithRules(
310     const nsAtom* aPseudoTag, nsRuleNode* aSource, nsRuleNode* aSourceIfVisited,
311     bool aRelevantLinkVisited) {
312   uint32_t threshold = 10;  // The # of siblings we're willing to examine
313                             // before just giving this whole thing up.
314 
315   RefPtr<GeckoStyleContext> result;
316   MOZ_ASSERT(aSource);
317   GeckoStyleContext* list = aSource->IsRoot() ? mEmptyChild : mChild;
318 
319   if (list) {
320     GeckoStyleContext* child = list;
321     do {
322       if (child->RuleNode() == aSource && child->mPseudoTag == aPseudoTag &&
323           !child->IsStyleIfVisited() &&
324           child->RelevantLinkVisited() == aRelevantLinkVisited) {
325         bool match = false;
326         if (aSourceIfVisited) {
327           match = child->GetStyleIfVisited() &&
328                   child->GetStyleIfVisited()->RuleNode() == aSourceIfVisited;
329         } else {
330           match = !child->GetStyleIfVisited();
331         }
332         if (match && !(child->mBits & NS_STYLE_INELIGIBLE_FOR_SHARING)) {
333           result = child;
334           break;
335         }
336       }
337       child = child->mNextSibling;
338       threshold--;
339       if (threshold == 0) break;
340     } while (child != list);
341   }
342 
343   if (result) {
344     if (result != list) {
345       // Move result to the front of the list.
346       RemoveChild(result);
347       AddChild(result);
348     }
349     result->mBits |= NS_STYLE_IS_SHARED;
350   }
351 
352   return result.forget();
353 }
354 
355 // This is an evil evil function, since it forces you to alloc your own separate
356 // copy of style data!  Do not use this function unless you absolutely have to!
357 // You should avoid this at all costs! -dwh
GetUniqueStyleData(const nsStyleStructID & aSID)358 void* GeckoStyleContext::GetUniqueStyleData(const nsStyleStructID& aSID) {
359   // If we already own the struct and no kids could depend on it, then
360   // just return it.  (We leak in this case if there are kids -- and this
361   // function really shouldn't be called for style contexts that could
362   // have kids depending on the data.  ClearStyleData would be OK, but
363   // this test for no mChild or mEmptyChild doesn't catch that case.)
364   const void* current = StyleData(aSID);
365   if (!mChild && !mEmptyChild &&
366       !(mBits & nsCachedStyleData::GetBitForSID(aSID)) &&
367       GetCachedStyleData(aSID))
368     return const_cast<void*>(current);
369 
370   void* result;
371   nsPresContext* presContext = PresContext();
372   switch (aSID) {
373 #define UNIQUE_CASE(c_)                                         \
374   case eStyleStruct_##c_:                                       \
375     result = new (presContext)                                  \
376         nsStyle##c_(*static_cast<const nsStyle##c_*>(current)); \
377     break;
378 
379     UNIQUE_CASE(Font)
380     UNIQUE_CASE(Display)
381     UNIQUE_CASE(Position)
382     UNIQUE_CASE(Text)
383     UNIQUE_CASE(TextReset)
384     UNIQUE_CASE(Visibility)
385 
386 #undef UNIQUE_CASE
387 
388     default:
389       NS_ERROR(
390           "Struct type not supported.  Please find another way to do this if "
391           "you can!");
392       return nullptr;
393   }
394 
395   SetStyle(aSID, result);
396   mBits &= ~static_cast<uint64_t>(nsCachedStyleData::GetBitForSID(aSID));
397 
398   return result;
399 }
400 
401 // This is an evil function, but less evil than GetUniqueStyleData. It
402 // creates an empty style struct for this nsStyleContext.
CreateEmptyStyleData(const nsStyleStructID & aSID)403 void* GeckoStyleContext::CreateEmptyStyleData(const nsStyleStructID& aSID) {
404   MOZ_ASSERT(!mChild && !mEmptyChild &&
405                  !(mBits & nsCachedStyleData::GetBitForSID(aSID)) &&
406                  !GetCachedStyleData(aSID),
407              "This style should not have been computed");
408 
409   void* result;
410   nsPresContext* presContext = PresContext();
411   switch (aSID) {
412 #define UNIQUE_CASE(c_)                                  \
413   case eStyleStruct_##c_:                                \
414     result = new (presContext) nsStyle##c_(presContext); \
415     break;
416 
417     UNIQUE_CASE(Border)
418     UNIQUE_CASE(Padding)
419 
420 #undef UNIQUE_CASE
421 
422     default:
423       NS_ERROR("Struct type not supported.");
424       return nullptr;
425   }
426 
427   // The new struct is owned by this style context, but that we don't
428   // need to clear the bit in mBits because we've asserted that at the
429   // top of this function.
430   SetStyle(aSID, result);
431   return result;
432 }
433 
SetIneligibleForSharing()434 void GeckoStyleContext::SetIneligibleForSharing() {
435   if (mBits & NS_STYLE_INELIGIBLE_FOR_SHARING) {
436     return;
437   }
438   mBits |= NS_STYLE_INELIGIBLE_FOR_SHARING;
439   if (mChild) {
440     GeckoStyleContext* child = mChild;
441     do {
442       child->SetIneligibleForSharing();
443       child = child->mNextSibling;
444     } while (mChild != child);
445   }
446   if (mEmptyChild) {
447     GeckoStyleContext* child = mEmptyChild;
448     do {
449       child->SetIneligibleForSharing();
450       child = child->mNextSibling;
451     } while (mEmptyChild != child);
452   }
453 }
454 
455 #ifdef RESTYLE_LOGGING
GetCachedStyleDataAsString(uint32_t aStructs)456 nsCString GeckoStyleContext::GetCachedStyleDataAsString(uint32_t aStructs) {
457   nsCString structs;
458   for (nsStyleStructID i = nsStyleStructID(0); i < nsStyleStructID_Length;
459        i = nsStyleStructID(i + 1)) {
460     if (aStructs & nsCachedStyleData::GetBitForSID(i)) {
461       const void* data = GetCachedStyleData(i);
462       if (!structs.IsEmpty()) {
463         structs.Append(' ');
464       }
465       structs.AppendPrintf("%s=%p", StructName(i), data);
466       if (HasCachedDependentStyleData(i)) {
467         structs.AppendLiteral("(dependent)");
468       } else {
469         structs.AppendLiteral("(owned)");
470       }
471     }
472   }
473   return structs;
474 }
475 
LoggingDepth()476 int32_t& GeckoStyleContext::LoggingDepth() {
477   static int32_t depth = 0;
478   return depth;
479 }
480 
LogStyleContextTree(int32_t aLoggingDepth,uint32_t aStructs)481 void GeckoStyleContext::LogStyleContextTree(int32_t aLoggingDepth,
482                                             uint32_t aStructs) {
483   LoggingDepth() = aLoggingDepth;
484   LogStyleContextTree(true, aStructs);
485 }
486 
LogStyleContextTree(bool aFirst,uint32_t aStructs)487 void GeckoStyleContext::LogStyleContextTree(bool aFirst, uint32_t aStructs) {
488   nsCString structs = GetCachedStyleDataAsString(aStructs);
489   if (!structs.IsEmpty()) {
490     structs.Append(' ');
491   }
492 
493   nsCString pseudo;
494   if (mPseudoTag) {
495     nsAutoString pseudoTag;
496     mPseudoTag->ToString(pseudoTag);
497     AppendUTF16toUTF8(pseudoTag, pseudo);
498     pseudo.Append(' ');
499   }
500 
501   nsCString flags;
502   if (IsStyleIfVisited()) {
503     flags.AppendLiteral("IS_STYLE_IF_VISITED ");
504   }
505   if (HasChildThatUsesGrandancestorStyle()) {
506     flags.AppendLiteral("CHILD_USES_GRANDANCESTOR_STYLE ");
507   }
508   if (IsShared()) {
509     flags.AppendLiteral("IS_SHARED ");
510   }
511 
512   nsCString parent;
513   if (aFirst) {
514     parent.AppendPrintf("parent=%p ", mParent.get());
515   }
516 
517   LOG_RESTYLE("%p(%d) %s%s%s%s", this, mRefCnt, structs.get(), pseudo.get(),
518               flags.get(), parent.get());
519 
520   LOG_RESTYLE_INDENT();
521 
522   if (nullptr != mChild) {
523     GeckoStyleContext* child = mChild;
524     do {
525       child->LogStyleContextTree(false, aStructs);
526       child = child->mNextSibling;
527     } while (mChild != child);
528   }
529   if (nullptr != mEmptyChild) {
530     GeckoStyleContext* child = mEmptyChild;
531     do {
532       child->LogStyleContextTree(false, aStructs);
533       child = child->mNextSibling;
534     } while (mEmptyChild != child);
535   }
536 }
537 #endif
538 
ShouldSuppressLineBreak(const nsStyleContext * aContext,const nsStyleDisplay * aDisplay,const nsStyleContext * aParentContext,const nsStyleDisplay * aParentDisplay)539 static bool ShouldSuppressLineBreak(const nsStyleContext* aContext,
540                                     const nsStyleDisplay* aDisplay,
541                                     const nsStyleContext* aParentContext,
542                                     const nsStyleDisplay* aParentDisplay) {
543   // The display change should only occur for "in-flow" children
544   if (aDisplay->IsOutOfFlowStyle()) {
545     return false;
546   }
547   // Display value of any anonymous box should not be touched. In most
548   // cases, anonymous boxes are actually not in ruby frame, but instead,
549   // some other frame with a ruby display value. Non-element pseudos
550   // which represents text frames, as well as ruby pseudos are excluded
551   // because we still want to set the flag for them.
552   if ((aContext->GetPseudoType() == CSSPseudoElementType::InheritingAnonBox ||
553        aContext->GetPseudoType() ==
554            CSSPseudoElementType::NonInheritingAnonBox) &&
555       !nsCSSAnonBoxes::IsNonElement(aContext->GetPseudo()) &&
556       !RubyUtils::IsRubyPseudo(aContext->GetPseudo())) {
557     return false;
558   }
559   if (aParentContext->ShouldSuppressLineBreak()) {
560     // Line break suppressing bit is propagated to any children of
561     // line participants, which include inline, contents, and inline
562     // ruby boxes.
563     if (aParentDisplay->mDisplay == mozilla::StyleDisplay::Inline ||
564         aParentDisplay->mDisplay == mozilla::StyleDisplay::Contents ||
565         aParentDisplay->mDisplay == mozilla::StyleDisplay::Ruby ||
566         aParentDisplay->mDisplay == mozilla::StyleDisplay::RubyBaseContainer) {
567       return true;
568     }
569   }
570   // Any descendant of ruby level containers is non-breakable, but
571   // the level containers themselves are breakable. We have to check
572   // the container display type against all ruby display type here
573   // because any of the ruby boxes could be anonymous.
574   // Note that, when certain HTML tags, e.g. form controls, have ruby
575   // level container display type, they could also escape from this flag
576   // while they shouldn't. However, it is generally fine since they
577   // won't usually break the assertion that there is no line break
578   // inside ruby, because:
579   // 1. their display types, the ruby level container types, are inline-
580   //    outside, which means they won't cause any forced line break; and
581   // 2. they never start an inline span, which means their children, if
582   //    any, won't be able to break the line its ruby ancestor lays; and
583   // 3. their parent frame is always a ruby content frame (due to
584   //    anonymous ruby box generation), which makes line layout suppress
585   //    any optional line break around this frame.
586   // However, there is one special case which is BR tag, because it
587   // directly affects the line layout. This case is handled by the BR
588   // frame which checks the flag of its parent frame instead of itself.
589   if ((aParentDisplay->IsRubyDisplayType() &&
590        aDisplay->mDisplay != mozilla::StyleDisplay::RubyBaseContainer &&
591        aDisplay->mDisplay != mozilla::StyleDisplay::RubyTextContainer) ||
592       // Since ruby base and ruby text may exist themselves without any
593       // non-anonymous frame outside, we should also check them.
594       aDisplay->mDisplay == mozilla::StyleDisplay::RubyBase ||
595       aDisplay->mDisplay == mozilla::StyleDisplay::RubyText) {
596     return true;
597   }
598   return false;
599 }
600 
FinishConstruction()601 void GeckoStyleContext::FinishConstruction() {
602   MOZ_ASSERT(RuleNode());
603 
604   if (mParent) {
605     mParent->AddChild(this);
606   }
607 
608   SetStyleBits();
609 }
610 
SetStyleBits()611 void GeckoStyleContext::SetStyleBits() {
612   if ((mParent && mParent->HasPseudoElementData()) || IsPseudoElement()) {
613     AddStyleBit(NS_STYLE_HAS_PSEUDO_ELEMENT_DATA);
614   }
615 
616   // Set the NS_STYLE_IN_DISPLAY_NONE_SUBTREE bit
617   const nsStyleDisplay* disp = StyleDisplay();
618   if ((mParent && mParent->IsInDisplayNoneSubtree()) ||
619       disp->mDisplay == mozilla::StyleDisplay::None) {
620     AddStyleBit(NS_STYLE_IN_DISPLAY_NONE_SUBTREE);
621   }
622 
623   // Mark text combined for text-combine-upright, as needed.
624   if (mPseudoTag == nsCSSAnonBoxes::mozText && mParent &&
625       mParent->StyleVisibility()->mWritingMode !=
626           NS_STYLE_WRITING_MODE_HORIZONTAL_TB &&
627       mParent->StyleText()->mTextCombineUpright ==
628           NS_STYLE_TEXT_COMBINE_UPRIGHT_ALL) {
629     AddStyleBit(NS_STYLE_IS_TEXT_COMBINED);
630   }
631 }
632 
633 // Flex & grid containers blockify their children.
634 //  "The display value of a flex item is blockified"
635 //    https://drafts.csswg.org/css-flexbox-1/#flex-items
636 //  "The display value of a grid item is blockified"
637 //    https://drafts.csswg.org/css-grid/#grid-items
ShouldBlockifyChildren(const nsStyleDisplay * aStyleDisp)638 static bool ShouldBlockifyChildren(const nsStyleDisplay* aStyleDisp) {
639   auto displayVal = aStyleDisp->mDisplay;
640   return mozilla::StyleDisplay::Flex == displayVal ||
641          mozilla::StyleDisplay::InlineFlex == displayVal ||
642          mozilla::StyleDisplay::Grid == displayVal ||
643          mozilla::StyleDisplay::InlineGrid == displayVal;
644 }
645 
646 #ifdef DEBUG
AssertStructsNotUsedElsewhere(GeckoStyleContext * aDestroyingContext,int32_t aLevels) const647 void GeckoStyleContext::AssertStructsNotUsedElsewhere(
648     GeckoStyleContext* aDestroyingContext, int32_t aLevels) const {
649   if (aLevels == 0) {
650     return;
651   }
652 
653   void* data;
654 
655   if (mBits & NS_STYLE_IS_GOING_AWAY) {
656     return;
657   }
658 
659   if (this != aDestroyingContext) {
660     nsInheritedStyleData& destroyingInheritedData =
661         aDestroyingContext->mCachedInheritedData;
662 #define STYLE_STRUCT_INHERITED(name_, checkdata_cb)                           \
663   data = destroyingInheritedData.mStyleStructs[eStyleStruct_##name_];         \
664   if (data && !(aDestroyingContext->mBits & NS_STYLE_INHERIT_BIT(name_)) &&   \
665       (mCachedInheritedData.mStyleStructs[eStyleStruct_##name_] == data)) {   \
666     printf_stderr("style struct %p found on style context %p\n", data, this); \
667     nsString url;                                                             \
668     nsresult rv = PresContext()->Document()->GetURL(url);                     \
669     if (NS_SUCCEEDED(rv)) {                                                   \
670       printf_stderr("  in %s\n", NS_ConvertUTF16toUTF8(url).get());           \
671     }                                                                         \
672     MOZ_ASSERT(false, "destroying " #name_                                    \
673                       " style struct still present "                          \
674                       "in style context tree");                               \
675   }
676 #define STYLE_STRUCT_RESET(name_, checkdata_cb)
677 
678 #include "nsStyleStructList.h"
679 
680 #undef STYLE_STRUCT_INHERITED
681 #undef STYLE_STRUCT_RESET
682 
683     if (mCachedResetData) {
684       nsResetStyleData* destroyingResetData =
685           aDestroyingContext->mCachedResetData;
686       if (destroyingResetData) {
687 #define STYLE_STRUCT_INHERITED(name_, checkdata_cb_)
688 #define STYLE_STRUCT_RESET(name_, checkdata_cb)                               \
689   data = destroyingResetData->mStyleStructs[eStyleStruct_##name_];            \
690   if (data && !(aDestroyingContext->mBits & NS_STYLE_INHERIT_BIT(name_)) &&   \
691       (mCachedResetData->mStyleStructs[eStyleStruct_##name_] == data)) {      \
692     printf_stderr("style struct %p found on style context %p\n", data, this); \
693     nsString url;                                                             \
694     nsresult rv = PresContext()->Document()->GetURL(url);                     \
695     if (NS_SUCCEEDED(rv)) {                                                   \
696       printf_stderr("  in %s\n", NS_ConvertUTF16toUTF8(url).get());           \
697     }                                                                         \
698     MOZ_ASSERT(false, "destroying " #name_                                    \
699                       " style struct still present "                          \
700                       "in style context tree");                               \
701   }
702 
703 #include "nsStyleStructList.h"
704 
705 #undef STYLE_STRUCT_INHERITED
706 #undef STYLE_STRUCT_RESET
707       }
708     }
709   }
710 
711   if (mChild) {
712     const GeckoStyleContext* child = mChild;
713     do {
714       child->AssertStructsNotUsedElsewhere(aDestroyingContext, aLevels - 1);
715       child = child->mNextSibling;
716     } while (child != mChild);
717   }
718 
719   if (mEmptyChild) {
720     const GeckoStyleContext* child = mEmptyChild;
721     do {
722       child->AssertStructsNotUsedElsewhere(aDestroyingContext, aLevels - 1);
723       child = child->mNextSibling;
724     } while (child != mEmptyChild);
725   }
726 }
727 #endif
728 
ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup)729 void GeckoStyleContext::ApplyStyleFixups(
730     bool aSkipParentDisplayBasedStyleFixup) {
731 #define GET_UNIQUE_STYLE_DATA(name_) \
732   static_cast<nsStyle##name_*>(GetUniqueStyleData(eStyleStruct_##name_))
733 
734   // CSS Inline Layout Level 3 - 3.5 Sizing Initial Letters:
735   // For an N-line drop initial in a Western script, the cap-height of the
736   // letter needs to be (N – 1) times the line-height, plus the cap-height
737   // of the surrounding text.
738   if (mPseudoTag == nsCSSPseudoElements::firstLetter) {
739     const nsStyleTextReset* textReset = StyleTextReset();
740     if (textReset->mInitialLetterSize != 0.0f) {
741       GeckoStyleContext* containerSC = GetParent();
742       const nsStyleDisplay* containerDisp = containerSC->StyleDisplay();
743       while (containerDisp->mDisplay == mozilla::StyleDisplay::Contents) {
744         if (!containerSC->GetParent()) {
745           break;
746         }
747         containerSC = containerSC->GetParent();
748         containerDisp = containerSC->StyleDisplay();
749       }
750       nscoord containerLH = ReflowInput::CalcLineHeight(nullptr, containerSC,
751                                                         NS_AUTOHEIGHT, 1.0f);
752       RefPtr<nsFontMetrics> containerFM =
753           nsLayoutUtils::GetFontMetricsForStyleContext(containerSC);
754       MOZ_ASSERT(containerFM, "Should have fontMetrics!!");
755       nscoord containerCH = containerFM->CapHeight();
756       RefPtr<nsFontMetrics> firstLetterFM =
757           nsLayoutUtils::GetFontMetricsForStyleContext(this);
758       MOZ_ASSERT(firstLetterFM, "Should have fontMetrics!!");
759       nscoord firstLetterCH = firstLetterFM->CapHeight();
760       nsStyleFont* mutableStyleFont = GET_UNIQUE_STYLE_DATA(Font);
761       float invCapHeightRatio =
762           mutableStyleFont->mFont.size / NSCoordToFloat(firstLetterCH);
763       mutableStyleFont->mFont.size = NSToCoordRound(
764           ((textReset->mInitialLetterSize - 1) * containerLH + containerCH) *
765           invCapHeightRatio);
766     }
767   }
768 
769   // Change writing mode of text frame for text-combine-upright. We use
770   // style structs of the parent to avoid triggering computation before
771   // we change the writing mode.
772   // It is safe to look at the parent's style because we are looking at
773   // inherited properties, and ::-moz-text never matches any rules.
774   if (mPseudoTag == nsCSSAnonBoxes::mozText && mParent &&
775       mParent->StyleVisibility()->mWritingMode !=
776           NS_STYLE_WRITING_MODE_HORIZONTAL_TB &&
777       mParent->StyleText()->mTextCombineUpright ==
778           NS_STYLE_TEXT_COMBINE_UPRIGHT_ALL) {
779     MOZ_ASSERT(!PeekStyleVisibility(),
780                "If StyleVisibility was already "
781                "computed, some properties may have been computed "
782                "incorrectly based on the old writing mode value");
783     nsStyleVisibility* mutableVis = GET_UNIQUE_STYLE_DATA(Visibility);
784     mutableVis->mWritingMode = NS_STYLE_WRITING_MODE_HORIZONTAL_TB;
785   }
786 
787   // See if we have any text decorations.
788   // First see if our parent has text decorations.  If our parent does, then we
789   // inherit the bit.
790   if (mParent && mParent->HasTextDecorationLines()) {
791     AddStyleBit(NS_STYLE_HAS_TEXT_DECORATION_LINES);
792   } else {
793     // We might have defined a decoration.
794     if (StyleTextReset()->HasTextDecorationLines()) {
795       AddStyleBit(NS_STYLE_HAS_TEXT_DECORATION_LINES);
796     }
797   }
798 
799   // CSS 2.1 10.1: Propagate the root element's 'direction' to the ICB.
800   // (PageContentFrame/CanvasFrame etc will inherit 'direction')
801   if (mPseudoTag == nsCSSAnonBoxes::viewport) {
802     nsPresContext* presContext = PresContext();
803     mozilla::dom::Element* docElement =
804         presContext->Document()->GetRootElement();
805     if (docElement) {
806       RefPtr<nsStyleContext> rootStyle =
807           presContext->StyleSet()->AsGecko()->ResolveStyleFor(docElement,
808                                                               nullptr);
809       auto dir = rootStyle->StyleVisibility()->mDirection;
810       if (dir != StyleVisibility()->mDirection) {
811         nsStyleVisibility* uniqueVisibility = GET_UNIQUE_STYLE_DATA(Visibility);
812         uniqueVisibility->mDirection = dir;
813       }
814     }
815   }
816 
817   // Correct tables.
818   const nsStyleDisplay* disp = StyleDisplay();
819   if (disp->mDisplay == mozilla::StyleDisplay::Table) {
820     // -moz-center and -moz-right are used for HTML's alignment
821     // This is covering the <div align="right"><table>...</table></div> case.
822     // In this case, we don't want to inherit the text alignment into the table.
823     const nsStyleText* text = StyleText();
824 
825     if (text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT ||
826         text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER ||
827         text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT) {
828       nsStyleText* uniqueText = GET_UNIQUE_STYLE_DATA(Text);
829       uniqueText->mTextAlign = NS_STYLE_TEXT_ALIGN_START;
830     }
831   }
832 
833   // Fixup the "justify-items: auto" value based on our parent style here if
834   // needed.
835   //
836   // Note that this only pulls a unique struct in the case the parent has the
837   // "legacy" modifier (which is not the default), and the computed value would
838   // change as a result.
839   //
840   // We check the parent first just to avoid unconditionally pulling the
841   // nsStylePosition struct on every style context.
842   if (mParent &&
843       mParent->StylePosition()->mJustifyItems & NS_STYLE_JUSTIFY_LEGACY &&
844       StylePosition()->mSpecifiedJustifyItems == NS_STYLE_JUSTIFY_AUTO &&
845       StylePosition()->mJustifyItems !=
846           mParent->StylePosition()->mJustifyItems) {
847     nsStylePosition* uniquePosition = GET_UNIQUE_STYLE_DATA(Position);
848     uniquePosition->mJustifyItems = mParent->StylePosition()->mJustifyItems;
849   }
850 
851   // CSS2.1 section 9.2.4 specifies fixups for the 'display' property of
852   // the root element.  We can't implement them in nsRuleNode because we
853   // don't want to store all display structs that aren't 'block',
854   // 'inline', or 'table' in the style context tree on the off chance
855   // that the root element has its style reresolved later.  So do them
856   // here if needed, by changing the style data, so that other code
857   // doesn't get confused by looking at the style data.
858   if (!mParent &&
859       // We don't want to blockify various anon boxes that just happen to not
860       // inherit from anything.  So restrict blockification only to actual
861       // elements, the viewport (which should be block anyway, but in SVG
862       // document's isn't because we lazy-load ua.css there), and the ::backdrop
863       // pseudo-element.  This last is explicitly allowed to have any specified
864       // display type in the spec, but computes to a blockified display type per
865       // various provisions of
866       // https://fullscreen.spec.whatwg.org/#new-stacking-layer
867       (!mPseudoTag || mPseudoTag == nsCSSAnonBoxes::viewport ||
868        mPseudoTag == nsCSSPseudoElements::backdrop)) {
869     auto displayVal = disp->mDisplay;
870     if (displayVal != mozilla::StyleDisplay::Contents) {
871       nsRuleNode::EnsureBlockDisplay(displayVal, true);
872     } else {
873       // http://dev.w3.org/csswg/css-display/#transformations
874       // "... a display-outside of 'contents' computes to block-level
875       //  on the root element."
876       displayVal = mozilla::StyleDisplay::Block;
877     }
878     if (displayVal != disp->mDisplay) {
879       nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
880       disp = mutable_display;
881 
882       // If we're in this code, then mOriginalDisplay doesn't matter
883       // for purposes of the cascade (because this nsStyleDisplay
884       // isn't living in the ruletree anyway), and for determining
885       // hypothetical boxes it's better to have mOriginalDisplay
886       // matching mDisplay here.
887       mutable_display->mOriginalDisplay = mutable_display->mDisplay =
888           displayVal;
889     }
890   }
891 
892   // Adjust the "display" values of flex and grid items (but not for raw text
893   // or placeholders). CSS3 Flexbox section 4 says:
894   //   # The computed 'display' of a flex item is determined
895   //   # by applying the table in CSS 2.1 Chapter 9.7.
896   // ...which converts inline-level elements to their block-level equivalents.
897   // Any block-level element directly contained by elements with ruby display
898   // values are converted to their inline-level equivalents.
899   if (!aSkipParentDisplayBasedStyleFixup && mParent) {
900     // Skip display:contents ancestors to reach the potential container.
901     // (If there are only display:contents ancestors between this node and
902     // a flex/grid container ancestor, then this node is a flex/grid item, since
903     // its parent *in the frame tree* will be the flex/grid container. So we
904     // treat it like a flex/grid item here.)
905     GeckoStyleContext* containerContext = GetParent();
906     const nsStyleDisplay* containerDisp = containerContext->StyleDisplay();
907     while (containerDisp->mDisplay == mozilla::StyleDisplay::Contents) {
908       if (!containerContext->GetParent()) {
909         break;
910       }
911       containerContext = containerContext->GetParent();
912       containerDisp = containerContext->StyleDisplay();
913     }
914     if (ShouldBlockifyChildren(containerDisp) &&
915         !nsCSSAnonBoxes::IsNonElement(GetPseudo())) {
916       // NOTE: Technically, we shouldn't modify the 'display' value of
917       // positioned elements, since they aren't flex/grid items. However,
918       // we don't need to worry about checking for that, because if we're
919       // positioned, we'll have already been through a call to
920       // EnsureBlockDisplay() in nsRuleNode, so this call here won't change
921       // anything. So we're OK.
922       auto displayVal = disp->mDisplay;
923       nsRuleNode::EnsureBlockDisplay(displayVal);
924       if (displayVal != disp->mDisplay) {
925         NS_ASSERTION(!disp->IsAbsolutelyPositionedStyle(),
926                      "We shouldn't be changing the display value of "
927                      "positioned content (and we should have already "
928                      "converted its display value to be block-level...)");
929         nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
930         disp = mutable_display;
931         mutable_display->mDisplay = displayVal;
932       }
933     }
934   }
935 
936   // Note: This must come after the blockification above, otherwise we fail
937   // the grid-item-blockifying-001.html reftest.
938   if (mParent &&
939       ::ShouldSuppressLineBreak(this, disp, mParent, mParent->StyleDisplay())) {
940     mBits |= NS_STYLE_SUPPRESS_LINEBREAK;
941     auto displayVal = disp->mDisplay;
942     nsRuleNode::EnsureInlineDisplay(displayVal);
943     if (displayVal != disp->mDisplay) {
944       nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
945       disp = mutable_display;
946       mutable_display->mDisplay = displayVal;
947     }
948   }
949   // Suppress border/padding of ruby level containers
950   if (disp->mDisplay == mozilla::StyleDisplay::RubyBaseContainer ||
951       disp->mDisplay == mozilla::StyleDisplay::RubyTextContainer) {
952     CreateEmptyStyleData(eStyleStruct_Border);
953     CreateEmptyStyleData(eStyleStruct_Padding);
954   }
955   if (disp->IsRubyDisplayType()) {
956     // Per CSS Ruby spec section Bidi Reordering, for all ruby boxes,
957     // the 'normal' and 'embed' values of 'unicode-bidi' should compute to
958     // 'isolate', and 'bidi-override' should compute to 'isolate-override'.
959     const nsStyleTextReset* textReset = StyleTextReset();
960     uint8_t unicodeBidi = textReset->mUnicodeBidi;
961     if (unicodeBidi == NS_STYLE_UNICODE_BIDI_NORMAL ||
962         unicodeBidi == NS_STYLE_UNICODE_BIDI_EMBED) {
963       unicodeBidi = NS_STYLE_UNICODE_BIDI_ISOLATE;
964     } else if (unicodeBidi == NS_STYLE_UNICODE_BIDI_BIDI_OVERRIDE) {
965       unicodeBidi = NS_STYLE_UNICODE_BIDI_ISOLATE_OVERRIDE;
966     }
967     if (unicodeBidi != textReset->mUnicodeBidi) {
968       nsStyleTextReset* mutableTextReset = GET_UNIQUE_STYLE_DATA(TextReset);
969       mutableTextReset->mUnicodeBidi = unicodeBidi;
970     }
971   }
972 
973   /*
974    * According to https://drafts.csswg.org/css-writing-modes-3/#block-flow:
975    *
976    * If a box has a different block flow direction than its containing block:
977    *   * If the box has a specified display of inline, its display computes
978    *     to inline-block. [CSS21]
979    *   ...etc.
980    */
981   if (disp->mDisplay == mozilla::StyleDisplay::Inline &&
982       !nsCSSAnonBoxes::IsNonElement(mPseudoTag) && mParent) {
983     auto cbContext = GetParent();
984     while (cbContext->StyleDisplay()->mDisplay ==
985            mozilla::StyleDisplay::Contents) {
986       cbContext = cbContext->GetParent();
987     }
988     MOZ_ASSERT(cbContext, "the root context can't have display:contents");
989     // We don't need the full mozilla::WritingMode value (incorporating dir
990     // and text-orientation) here; just the writing-mode property is enough.
991     if (StyleVisibility()->mWritingMode !=
992         cbContext->StyleVisibility()->mWritingMode) {
993       nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
994       disp = mutable_display;
995       mutable_display->mOriginalDisplay = mutable_display->mDisplay =
996           mozilla::StyleDisplay::InlineBlock;
997     }
998   }
999 
1000   // Compute User Interface style, to trigger loads of cursors
1001   StyleUserInterface();
1002 #undef GET_UNIQUE_STYLE_DATA
1003 }
1004 
HasNoChildren() const1005 bool GeckoStyleContext::HasNoChildren() const {
1006   return (nullptr == mChild) && (nullptr == mEmptyChild);
1007 }
1008 
SetStyle(nsStyleStructID aSID,void * aStruct)1009 void GeckoStyleContext::SetStyle(nsStyleStructID aSID, void* aStruct) {
1010   // This method should only be called from nsRuleNode!  It is not a public
1011   // method!
1012 
1013   NS_ASSERTION(aSID >= 0 && aSID < nsStyleStructID_Length, "out of bounds");
1014 
1015   // NOTE:  nsCachedStyleData::GetStyleData works roughly the same way.
1016   // See the comments there (in nsRuleNode.h) for more details about
1017   // what this is doing and why.
1018 
1019   void** dataSlot;
1020   if (nsCachedStyleData::IsReset(aSID)) {
1021     if (!mCachedResetData) {
1022       mCachedResetData = new (PresContext()) nsResetStyleData;
1023     }
1024     dataSlot = &mCachedResetData->mStyleStructs[aSID];
1025   } else {
1026     dataSlot = &mCachedInheritedData.mStyleStructs[aSID];
1027   }
1028   NS_ASSERTION(!*dataSlot || (mBits & nsCachedStyleData::GetBitForSID(aSID)),
1029                "Going to leak style data");
1030   *dataSlot = aStruct;
1031 }
1032 
StyleData(nsStyleStructID aSID)1033 const void* GeckoStyleContext::StyleData(nsStyleStructID aSID) {
1034   const void* cachedData = GetCachedStyleData(aSID);
1035   if (cachedData)
1036     return cachedData;  // We have computed data stored on this node in the
1037                         // context tree.
1038   // Our style source will take care of it for us.
1039   const void* newData =
1040       AsGecko()->RuleNode()->GetStyleData(aSID, this->AsGecko(), true);
1041   if (!nsCachedStyleData::IsReset(aSID)) {
1042     // always cache inherited data on the style context; the rule
1043     // node set the bit in mBits for us if needed.
1044     mCachedInheritedData.mStyleStructs[aSID] = const_cast<void*>(newData);
1045   }
1046 
1047   return newData;
1048 }
1049 
DestroyCachedStructs(nsPresContext * aPresContext)1050 void GeckoStyleContext::DestroyCachedStructs(nsPresContext* aPresContext) {
1051   mCachedInheritedData.DestroyStructs(mBits, aPresContext);
1052   if (mCachedResetData) {
1053     mCachedResetData->Destroy(mBits, aPresContext);
1054   }
1055 }
1056 
SwapStyleData(GeckoStyleContext * aNewContext,uint32_t aStructs)1057 void GeckoStyleContext::SwapStyleData(GeckoStyleContext* aNewContext,
1058                                       uint32_t aStructs) {
1059   static_assert(nsStyleStructID_Length <= 32, "aStructs is not big enough");
1060 
1061   for (nsStyleStructID i = nsStyleStructID_Inherited_Start;
1062        i < nsStyleStructID_Inherited_Start + nsStyleStructID_Inherited_Count;
1063        i = nsStyleStructID(i + 1)) {
1064     uint32_t bit = nsCachedStyleData::GetBitForSID(i);
1065     if (!(aStructs & bit)) {
1066       continue;
1067     }
1068     void*& thisData = mCachedInheritedData.mStyleStructs[i];
1069     void*& otherData = aNewContext->mCachedInheritedData.mStyleStructs[i];
1070     if (mBits & bit) {
1071       if (thisData == otherData) {
1072         thisData = nullptr;
1073       }
1074     } else if (!(aNewContext->mBits & bit) && thisData && otherData) {
1075       std::swap(thisData, otherData);
1076     }
1077   }
1078 
1079   for (nsStyleStructID i = nsStyleStructID_Reset_Start;
1080        i < nsStyleStructID_Reset_Start + nsStyleStructID_Reset_Count;
1081        i = nsStyleStructID(i + 1)) {
1082     uint32_t bit = nsCachedStyleData::GetBitForSID(i);
1083     if (!(aStructs & bit)) {
1084       continue;
1085     }
1086     if (!mCachedResetData) {
1087       mCachedResetData = new (PresContext()) nsResetStyleData;
1088     }
1089     if (!aNewContext->mCachedResetData) {
1090       aNewContext->mCachedResetData = new (PresContext()) nsResetStyleData;
1091     }
1092     void*& thisData = mCachedResetData->mStyleStructs[i];
1093     void*& otherData = aNewContext->mCachedResetData->mStyleStructs[i];
1094     if (mBits & bit) {
1095       if (thisData == otherData) {
1096         thisData = nullptr;
1097       }
1098     } else if (!(aNewContext->mBits & bit) && thisData && otherData) {
1099       std::swap(thisData, otherData);
1100     }
1101   }
1102 }
1103 
SetStyleIfVisited(already_AddRefed<GeckoStyleContext> aStyleIfVisited)1104 void GeckoStyleContext::SetStyleIfVisited(
1105     already_AddRefed<GeckoStyleContext> aStyleIfVisited) {
1106   MOZ_ASSERT(!IsStyleIfVisited(), "this context is not visited data");
1107   NS_ASSERTION(!mStyleIfVisited, "should only be set once");
1108 
1109   mStyleIfVisited = aStyleIfVisited;
1110 
1111   MOZ_ASSERT(mStyleIfVisited->IsStyleIfVisited(),
1112              "other context is visited data");
1113   MOZ_ASSERT(!mStyleIfVisited->GetStyleIfVisited(),
1114              "other context does not have visited data");
1115   NS_ASSERTION(GetStyleIfVisited()->GetPseudo() == GetPseudo(),
1116                "pseudo tag mismatch");
1117   if (GetParent() && GetParent()->GetStyleIfVisited()) {
1118     MOZ_ASSERT(
1119         GetStyleIfVisited()->GetParent() == GetParent()->GetStyleIfVisited() ||
1120             GetStyleIfVisited()->GetParent() == GetParent(),
1121         "parent mismatch");
1122   } else {
1123     MOZ_ASSERT(GetStyleIfVisited()->GetParent() == GetParent(),
1124                "parent mismatch");
1125   }
1126 }
1127