|
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/. */ |
|
5 |
|
6 /* the interface (to internal code) for retrieving computed style data */ |
|
7 |
|
8 #include "mozilla/DebugOnly.h" |
|
9 |
|
10 #include "nsCSSAnonBoxes.h" |
|
11 #include "nsStyleConsts.h" |
|
12 #include "nsString.h" |
|
13 #include "nsPresContext.h" |
|
14 #include "nsIStyleRule.h" |
|
15 |
|
16 #include "nsCOMPtr.h" |
|
17 #include "nsStyleSet.h" |
|
18 #include "nsIPresShell.h" |
|
19 |
|
20 #include "nsRuleNode.h" |
|
21 #include "nsStyleContext.h" |
|
22 #include "nsStyleAnimation.h" |
|
23 #include "GeckoProfiler.h" |
|
24 |
|
25 #ifdef DEBUG |
|
26 // #define NOISY_DEBUG |
|
27 #endif |
|
28 |
|
29 using namespace mozilla; |
|
30 |
|
31 //---------------------------------------------------------------------- |
|
32 |
|
33 |
|
34 nsStyleContext::nsStyleContext(nsStyleContext* aParent, |
|
35 nsIAtom* aPseudoTag, |
|
36 nsCSSPseudoElements::Type aPseudoType, |
|
37 nsRuleNode* aRuleNode, |
|
38 bool aSkipFlexItemStyleFixup) |
|
39 : mParent(aParent), |
|
40 mChild(nullptr), |
|
41 mEmptyChild(nullptr), |
|
42 mPseudoTag(aPseudoTag), |
|
43 mRuleNode(aRuleNode), |
|
44 mAllocations(nullptr), |
|
45 mCachedResetData(nullptr), |
|
46 mBits(((uint64_t)aPseudoType) << NS_STYLE_CONTEXT_TYPE_SHIFT), |
|
47 mRefCnt(0) |
|
48 { |
|
49 // This check has to be done "backward", because if it were written the |
|
50 // more natural way it wouldn't fail even when it needed to. |
|
51 static_assert((UINT64_MAX >> NS_STYLE_CONTEXT_TYPE_SHIFT) >= |
|
52 nsCSSPseudoElements::ePseudo_MAX, |
|
53 "pseudo element bits no longer fit in a uint64_t"); |
|
54 MOZ_ASSERT(aRuleNode); |
|
55 |
|
56 mNextSibling = this; |
|
57 mPrevSibling = this; |
|
58 if (mParent) { |
|
59 mParent->AddRef(); |
|
60 mParent->AddChild(this); |
|
61 #ifdef DEBUG |
|
62 nsRuleNode *r1 = mParent->RuleNode(), *r2 = aRuleNode; |
|
63 while (r1->GetParent()) |
|
64 r1 = r1->GetParent(); |
|
65 while (r2->GetParent()) |
|
66 r2 = r2->GetParent(); |
|
67 NS_ASSERTION(r1 == r2, "must be in the same rule tree as parent"); |
|
68 #endif |
|
69 } |
|
70 |
|
71 mRuleNode->AddRef(); |
|
72 mRuleNode->SetUsedDirectly(); // before ApplyStyleFixups()! |
|
73 |
|
74 ApplyStyleFixups(aSkipFlexItemStyleFixup); |
|
75 |
|
76 #define eStyleStruct_LastItem (nsStyleStructID_Length - 1) |
|
77 NS_ASSERTION(NS_STYLE_INHERIT_MASK & NS_STYLE_INHERIT_BIT(LastItem), |
|
78 "NS_STYLE_INHERIT_MASK must be bigger, and other bits shifted"); |
|
79 #undef eStyleStruct_LastItem |
|
80 } |
|
81 |
|
82 nsStyleContext::~nsStyleContext() |
|
83 { |
|
84 NS_ASSERTION((nullptr == mChild) && (nullptr == mEmptyChild), "destructing context with children"); |
|
85 |
|
86 nsPresContext *presContext = mRuleNode->PresContext(); |
|
87 |
|
88 mRuleNode->Release(); |
|
89 |
|
90 presContext->PresShell()->StyleSet()-> |
|
91 NotifyStyleContextDestroyed(presContext, this); |
|
92 |
|
93 if (mParent) { |
|
94 mParent->RemoveChild(this); |
|
95 mParent->Release(); |
|
96 } |
|
97 |
|
98 // Free up our data structs. |
|
99 mCachedInheritedData.DestroyStructs(mBits, presContext); |
|
100 if (mCachedResetData) { |
|
101 mCachedResetData->Destroy(mBits, presContext); |
|
102 } |
|
103 |
|
104 FreeAllocations(presContext); |
|
105 } |
|
106 |
|
107 void nsStyleContext::AddChild(nsStyleContext* aChild) |
|
108 { |
|
109 NS_ASSERTION(aChild->mPrevSibling == aChild && |
|
110 aChild->mNextSibling == aChild, |
|
111 "child already in a child list"); |
|
112 |
|
113 nsStyleContext **listPtr = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild; |
|
114 // Explicitly dereference listPtr so that compiler doesn't have to know that mNextSibling |
|
115 // etc. don't alias with what ever listPtr points at. |
|
116 nsStyleContext *list = *listPtr; |
|
117 |
|
118 // Insert at the beginning of the list. See also FindChildWithRules. |
|
119 if (list) { |
|
120 // Link into existing elements, if there are any. |
|
121 aChild->mNextSibling = list; |
|
122 aChild->mPrevSibling = list->mPrevSibling; |
|
123 list->mPrevSibling->mNextSibling = aChild; |
|
124 list->mPrevSibling = aChild; |
|
125 } |
|
126 (*listPtr) = aChild; |
|
127 } |
|
128 |
|
129 void nsStyleContext::RemoveChild(nsStyleContext* aChild) |
|
130 { |
|
131 NS_PRECONDITION(nullptr != aChild && this == aChild->mParent, "bad argument"); |
|
132 |
|
133 nsStyleContext **list = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild; |
|
134 |
|
135 if (aChild->mPrevSibling != aChild) { // has siblings |
|
136 if ((*list) == aChild) { |
|
137 (*list) = (*list)->mNextSibling; |
|
138 } |
|
139 } |
|
140 else { |
|
141 NS_ASSERTION((*list) == aChild, "bad sibling pointers"); |
|
142 (*list) = nullptr; |
|
143 } |
|
144 |
|
145 aChild->mPrevSibling->mNextSibling = aChild->mNextSibling; |
|
146 aChild->mNextSibling->mPrevSibling = aChild->mPrevSibling; |
|
147 aChild->mNextSibling = aChild; |
|
148 aChild->mPrevSibling = aChild; |
|
149 } |
|
150 |
|
151 already_AddRefed<nsStyleContext> |
|
152 nsStyleContext::FindChildWithRules(const nsIAtom* aPseudoTag, |
|
153 nsRuleNode* aRuleNode, |
|
154 nsRuleNode* aRulesIfVisited, |
|
155 bool aRelevantLinkVisited) |
|
156 { |
|
157 NS_ABORT_IF_FALSE(aRulesIfVisited || !aRelevantLinkVisited, |
|
158 "aRelevantLinkVisited should only be set when we have a separate style"); |
|
159 uint32_t threshold = 10; // The # of siblings we're willing to examine |
|
160 // before just giving this whole thing up. |
|
161 |
|
162 nsRefPtr<nsStyleContext> result; |
|
163 nsStyleContext *list = aRuleNode->IsRoot() ? mEmptyChild : mChild; |
|
164 |
|
165 if (list) { |
|
166 nsStyleContext *child = list; |
|
167 do { |
|
168 if (child->mRuleNode == aRuleNode && |
|
169 child->mPseudoTag == aPseudoTag && |
|
170 !child->IsStyleIfVisited() && |
|
171 child->RelevantLinkVisited() == aRelevantLinkVisited) { |
|
172 bool match = false; |
|
173 if (aRulesIfVisited) { |
|
174 match = child->GetStyleIfVisited() && |
|
175 child->GetStyleIfVisited()->mRuleNode == aRulesIfVisited; |
|
176 } else { |
|
177 match = !child->GetStyleIfVisited(); |
|
178 } |
|
179 if (match) { |
|
180 result = child; |
|
181 break; |
|
182 } |
|
183 } |
|
184 child = child->mNextSibling; |
|
185 threshold--; |
|
186 if (threshold == 0) |
|
187 break; |
|
188 } while (child != list); |
|
189 } |
|
190 |
|
191 if (result) { |
|
192 if (result != list) { |
|
193 // Move result to the front of the list. |
|
194 RemoveChild(result); |
|
195 AddChild(result); |
|
196 } |
|
197 } |
|
198 |
|
199 return result.forget(); |
|
200 } |
|
201 |
|
202 const void* nsStyleContext::GetCachedStyleData(nsStyleStructID aSID) |
|
203 { |
|
204 const void* cachedData; |
|
205 if (nsCachedStyleData::IsReset(aSID)) { |
|
206 if (mCachedResetData) { |
|
207 cachedData = mCachedResetData->mStyleStructs[aSID]; |
|
208 } else { |
|
209 cachedData = nullptr; |
|
210 } |
|
211 } else { |
|
212 cachedData = mCachedInheritedData.mStyleStructs[aSID]; |
|
213 } |
|
214 return cachedData; |
|
215 } |
|
216 |
|
217 const void* nsStyleContext::StyleData(nsStyleStructID aSID) |
|
218 { |
|
219 const void* cachedData = GetCachedStyleData(aSID); |
|
220 if (cachedData) |
|
221 return cachedData; // We have computed data stored on this node in the context tree. |
|
222 return mRuleNode->GetStyleData(aSID, this, true); // Our rule node will take care of it for us. |
|
223 } |
|
224 |
|
225 // This is an evil evil function, since it forces you to alloc your own separate copy of |
|
226 // style data! Do not use this function unless you absolutely have to! You should avoid |
|
227 // this at all costs! -dwh |
|
228 void* |
|
229 nsStyleContext::GetUniqueStyleData(const nsStyleStructID& aSID) |
|
230 { |
|
231 // If we already own the struct and no kids could depend on it, then |
|
232 // just return it. (We leak in this case if there are kids -- and this |
|
233 // function really shouldn't be called for style contexts that could |
|
234 // have kids depending on the data. ClearStyleData would be OK, but |
|
235 // this test for no mChild or mEmptyChild doesn't catch that case.) |
|
236 const void *current = StyleData(aSID); |
|
237 if (!mChild && !mEmptyChild && |
|
238 !(mBits & nsCachedStyleData::GetBitForSID(aSID)) && |
|
239 GetCachedStyleData(aSID)) |
|
240 return const_cast<void*>(current); |
|
241 |
|
242 void* result; |
|
243 nsPresContext *presContext = PresContext(); |
|
244 switch (aSID) { |
|
245 |
|
246 #define UNIQUE_CASE(c_) \ |
|
247 case eStyleStruct_##c_: \ |
|
248 result = new (presContext) nsStyle##c_( \ |
|
249 * static_cast<const nsStyle##c_ *>(current)); \ |
|
250 break; |
|
251 |
|
252 UNIQUE_CASE(Display) |
|
253 UNIQUE_CASE(Background) |
|
254 UNIQUE_CASE(Text) |
|
255 UNIQUE_CASE(TextReset) |
|
256 |
|
257 #undef UNIQUE_CASE |
|
258 |
|
259 default: |
|
260 NS_ERROR("Struct type not supported. Please find another way to do this if you can!"); |
|
261 return nullptr; |
|
262 } |
|
263 |
|
264 SetStyle(aSID, result); |
|
265 mBits &= ~static_cast<uint64_t>(nsCachedStyleData::GetBitForSID(aSID)); |
|
266 |
|
267 return result; |
|
268 } |
|
269 |
|
270 void |
|
271 nsStyleContext::SetStyle(nsStyleStructID aSID, void* aStruct) |
|
272 { |
|
273 // This method should only be called from nsRuleNode! It is not a public |
|
274 // method! |
|
275 |
|
276 NS_ASSERTION(aSID >= 0 && aSID < nsStyleStructID_Length, "out of bounds"); |
|
277 |
|
278 // NOTE: nsCachedStyleData::GetStyleData works roughly the same way. |
|
279 // See the comments there (in nsRuleNode.h) for more details about |
|
280 // what this is doing and why. |
|
281 |
|
282 void** dataSlot; |
|
283 if (nsCachedStyleData::IsReset(aSID)) { |
|
284 if (!mCachedResetData) { |
|
285 mCachedResetData = new (mRuleNode->PresContext()) nsResetStyleData; |
|
286 } |
|
287 dataSlot = &mCachedResetData->mStyleStructs[aSID]; |
|
288 } else { |
|
289 dataSlot = &mCachedInheritedData.mStyleStructs[aSID]; |
|
290 } |
|
291 NS_ASSERTION(!*dataSlot || (mBits & nsCachedStyleData::GetBitForSID(aSID)), |
|
292 "Going to leak style data"); |
|
293 *dataSlot = aStruct; |
|
294 } |
|
295 |
|
296 void |
|
297 nsStyleContext::ApplyStyleFixups(bool aSkipFlexItemStyleFixup) |
|
298 { |
|
299 // See if we have any text decorations. |
|
300 // First see if our parent has text decorations. If our parent does, then we inherit the bit. |
|
301 if (mParent && mParent->HasTextDecorationLines()) { |
|
302 mBits |= NS_STYLE_HAS_TEXT_DECORATION_LINES; |
|
303 } else { |
|
304 // We might have defined a decoration. |
|
305 const nsStyleTextReset* text = StyleTextReset(); |
|
306 uint8_t decorationLine = text->mTextDecorationLine; |
|
307 if (decorationLine != NS_STYLE_TEXT_DECORATION_LINE_NONE && |
|
308 decorationLine != NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL) { |
|
309 mBits |= NS_STYLE_HAS_TEXT_DECORATION_LINES; |
|
310 } |
|
311 } |
|
312 |
|
313 if ((mParent && mParent->HasPseudoElementData()) || mPseudoTag) { |
|
314 mBits |= NS_STYLE_HAS_PSEUDO_ELEMENT_DATA; |
|
315 } |
|
316 |
|
317 // Correct tables. |
|
318 const nsStyleDisplay* disp = StyleDisplay(); |
|
319 if (disp->mDisplay == NS_STYLE_DISPLAY_TABLE) { |
|
320 // -moz-center and -moz-right are used for HTML's alignment |
|
321 // This is covering the <div align="right"><table>...</table></div> case. |
|
322 // In this case, we don't want to inherit the text alignment into the table. |
|
323 const nsStyleText* text = StyleText(); |
|
324 |
|
325 if (text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER || |
|
326 text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT) |
|
327 { |
|
328 nsStyleText* uniqueText = (nsStyleText*)GetUniqueStyleData(eStyleStruct_Text); |
|
329 uniqueText->mTextAlign = NS_STYLE_TEXT_ALIGN_DEFAULT; |
|
330 } |
|
331 } |
|
332 |
|
333 // CSS2.1 section 9.2.4 specifies fixups for the 'display' property of |
|
334 // the root element. We can't implement them in nsRuleNode because we |
|
335 // don't want to store all display structs that aren't 'block', |
|
336 // 'inline', or 'table' in the style context tree on the off chance |
|
337 // that the root element has its style reresolved later. So do them |
|
338 // here if needed, by changing the style data, so that other code |
|
339 // doesn't get confused by looking at the style data. |
|
340 if (!mParent) { |
|
341 uint8_t displayVal = disp->mDisplay; |
|
342 nsRuleNode::EnsureBlockDisplay(displayVal, true); |
|
343 if (displayVal != disp->mDisplay) { |
|
344 nsStyleDisplay *mutable_display = |
|
345 static_cast<nsStyleDisplay*>(GetUniqueStyleData(eStyleStruct_Display)); |
|
346 |
|
347 // If we're in this code, then mOriginalDisplay doesn't matter |
|
348 // for purposes of the cascade (because this nsStyleDisplay |
|
349 // isn't living in the ruletree anyway), and for determining |
|
350 // hypothetical boxes it's better to have mOriginalDisplay |
|
351 // matching mDisplay here. |
|
352 mutable_display->mOriginalDisplay = mutable_display->mDisplay = |
|
353 displayVal; |
|
354 } |
|
355 } |
|
356 |
|
357 // Adjust the "display" values of flex and grid items (but not for raw text, |
|
358 // placeholders, or table-parts). CSS3 Flexbox section 4 says: |
|
359 // # The computed 'display' of a flex item is determined |
|
360 // # by applying the table in CSS 2.1 Chapter 9.7. |
|
361 // ...which converts inline-level elements to their block-level equivalents. |
|
362 if (!aSkipFlexItemStyleFixup && mParent) { |
|
363 const nsStyleDisplay* parentDisp = mParent->StyleDisplay(); |
|
364 if ((parentDisp->mDisplay == NS_STYLE_DISPLAY_FLEX || |
|
365 parentDisp->mDisplay == NS_STYLE_DISPLAY_INLINE_FLEX || |
|
366 parentDisp->mDisplay == NS_STYLE_DISPLAY_GRID || |
|
367 parentDisp->mDisplay == NS_STYLE_DISPLAY_INLINE_GRID) && |
|
368 GetPseudo() != nsCSSAnonBoxes::mozNonElement) { |
|
369 uint8_t displayVal = disp->mDisplay; |
|
370 // Skip table parts. |
|
371 // NOTE: This list needs to be kept in sync with |
|
372 // nsCSSFrameConstructor.cpp's "sDisplayData" array -- specifically, |
|
373 // this should be the list of display-values that have |
|
374 // FCDATA_DESIRED_PARENT_TYPE_TO_BITS specified in that array. |
|
375 if (NS_STYLE_DISPLAY_TABLE_CAPTION != displayVal && |
|
376 NS_STYLE_DISPLAY_TABLE_ROW_GROUP != displayVal && |
|
377 NS_STYLE_DISPLAY_TABLE_HEADER_GROUP != displayVal && |
|
378 NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP != displayVal && |
|
379 NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP != displayVal && |
|
380 NS_STYLE_DISPLAY_TABLE_COLUMN != displayVal && |
|
381 NS_STYLE_DISPLAY_TABLE_ROW != displayVal && |
|
382 NS_STYLE_DISPLAY_TABLE_CELL != displayVal) { |
|
383 |
|
384 // NOTE: Technically, we shouldn't modify the 'display' value of |
|
385 // positioned elements, since they aren't flex items. However, we don't |
|
386 // need to worry about checking for that, because if we're positioned, |
|
387 // we'll have already been through a call to EnsureBlockDisplay() in |
|
388 // nsRuleNode, so this call here won't change anything. So we're OK. |
|
389 nsRuleNode::EnsureBlockDisplay(displayVal); |
|
390 if (displayVal != disp->mDisplay) { |
|
391 NS_ASSERTION(!disp->IsAbsolutelyPositionedStyle(), |
|
392 "We shouldn't be changing the display value of " |
|
393 "positioned content (and we should have already " |
|
394 "converted its display value to be block-level...)"); |
|
395 nsStyleDisplay *mutable_display = |
|
396 static_cast<nsStyleDisplay*>(GetUniqueStyleData(eStyleStruct_Display)); |
|
397 mutable_display->mDisplay = displayVal; |
|
398 } |
|
399 } |
|
400 } |
|
401 } |
|
402 |
|
403 // Compute User Interface style, to trigger loads of cursors |
|
404 StyleUserInterface(); |
|
405 } |
|
406 |
|
407 nsChangeHint |
|
408 nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, |
|
409 nsChangeHint aParentHintsNotHandledForDescendants) |
|
410 { |
|
411 PROFILER_LABEL("nsStyleContext", "CalcStyleDifference"); |
|
412 |
|
413 NS_ABORT_IF_FALSE(NS_IsHintSubset(aParentHintsNotHandledForDescendants, |
|
414 nsChangeHint_Hints_NotHandledForDescendants), |
|
415 "caller is passing inherited hints, but shouldn't be"); |
|
416 |
|
417 nsChangeHint hint = NS_STYLE_HINT_NONE; |
|
418 NS_ENSURE_TRUE(aOther, hint); |
|
419 // We must always ensure that we populate the structs on the new style |
|
420 // context that are filled in on the old context, so that if we get |
|
421 // two style changes in succession, the second of which causes a real |
|
422 // style change, the PeekStyleData doesn't return null (implying that |
|
423 // nobody ever looked at that struct's data). In other words, we |
|
424 // can't skip later structs if we get a big change up front, because |
|
425 // we could later get a small change in one of those structs that we |
|
426 // don't want to miss. |
|
427 |
|
428 // If our rule nodes are the same, then any differences in style data |
|
429 // are already accounted for by differences on ancestors. We know |
|
430 // this because CalcStyleDifference is always called on two style |
|
431 // contexts that point to the same element, so we know that our |
|
432 // position in the style context tree is the same and our position in |
|
433 // the rule node tree is also the same. |
|
434 // However, if there were noninherited style change hints on the |
|
435 // parent, we might produce these same noninherited hints on this |
|
436 // style context's frame due to 'inherit' values, so we do need to |
|
437 // compare. |
|
438 // (Things like 'em' units are handled by the change hint produced |
|
439 // by font-size changing, so we don't need to worry about them like |
|
440 // we worry about 'inherit' values.) |
|
441 bool compare = mRuleNode != aOther->mRuleNode; |
|
442 |
|
443 // If we had any change in variable values, then we'll need to examine |
|
444 // all of the other style structs too, even if the new style context has |
|
445 // the same rule node as the old one. |
|
446 const nsStyleVariables* thisVariables = PeekStyleVariables(); |
|
447 if (thisVariables) { |
|
448 const nsStyleVariables* otherVariables = aOther->StyleVariables(); |
|
449 if (thisVariables->mVariables != otherVariables->mVariables) { |
|
450 compare = true; |
|
451 } |
|
452 } |
|
453 |
|
454 DebugOnly<int> styleStructCount = 1; // count Variables already |
|
455 |
|
456 #define DO_STRUCT_DIFFERENCE(struct_) \ |
|
457 PR_BEGIN_MACRO \ |
|
458 const nsStyle##struct_* this##struct_ = PeekStyle##struct_(); \ |
|
459 if (this##struct_) { \ |
|
460 const nsStyle##struct_* other##struct_ = aOther->Style##struct_(); \ |
|
461 nsChangeHint maxDifference = nsStyle##struct_::MaxDifference(); \ |
|
462 nsChangeHint maxDifferenceNeverInherited = \ |
|
463 nsStyle##struct_::MaxDifferenceNeverInherited(); \ |
|
464 if ((compare || \ |
|
465 (NS_SubtractHint(maxDifference, maxDifferenceNeverInherited) & \ |
|
466 aParentHintsNotHandledForDescendants)) && \ |
|
467 !NS_IsHintSubset(maxDifference, hint) && \ |
|
468 this##struct_ != other##struct_) { \ |
|
469 NS_ASSERTION(NS_IsHintSubset( \ |
|
470 this##struct_->CalcDifference(*other##struct_), \ |
|
471 nsStyle##struct_::MaxDifference()), \ |
|
472 "CalcDifference() returned bigger hint than MaxDifference()"); \ |
|
473 NS_UpdateHint(hint, this##struct_->CalcDifference(*other##struct_)); \ |
|
474 } \ |
|
475 } \ |
|
476 styleStructCount++; \ |
|
477 PR_END_MACRO |
|
478 |
|
479 // In general, we want to examine structs starting with those that can |
|
480 // cause the largest style change, down to those that can cause the |
|
481 // smallest. This lets us skip later ones if we already have a hint |
|
482 // that subsumes their MaxDifference. (As the hints get |
|
483 // finer-grained, this optimization is becoming less useful, though.) |
|
484 DO_STRUCT_DIFFERENCE(Display); |
|
485 DO_STRUCT_DIFFERENCE(XUL); |
|
486 DO_STRUCT_DIFFERENCE(Column); |
|
487 DO_STRUCT_DIFFERENCE(Content); |
|
488 DO_STRUCT_DIFFERENCE(UserInterface); |
|
489 DO_STRUCT_DIFFERENCE(Visibility); |
|
490 DO_STRUCT_DIFFERENCE(Outline); |
|
491 DO_STRUCT_DIFFERENCE(TableBorder); |
|
492 DO_STRUCT_DIFFERENCE(Table); |
|
493 DO_STRUCT_DIFFERENCE(UIReset); |
|
494 DO_STRUCT_DIFFERENCE(Text); |
|
495 DO_STRUCT_DIFFERENCE(List); |
|
496 DO_STRUCT_DIFFERENCE(Quotes); |
|
497 DO_STRUCT_DIFFERENCE(SVGReset); |
|
498 DO_STRUCT_DIFFERENCE(SVG); |
|
499 DO_STRUCT_DIFFERENCE(Position); |
|
500 DO_STRUCT_DIFFERENCE(Font); |
|
501 DO_STRUCT_DIFFERENCE(Margin); |
|
502 DO_STRUCT_DIFFERENCE(Padding); |
|
503 DO_STRUCT_DIFFERENCE(Border); |
|
504 DO_STRUCT_DIFFERENCE(TextReset); |
|
505 DO_STRUCT_DIFFERENCE(Background); |
|
506 DO_STRUCT_DIFFERENCE(Color); |
|
507 |
|
508 #undef DO_STRUCT_DIFFERENCE |
|
509 |
|
510 MOZ_ASSERT(styleStructCount == nsStyleStructID_Length, |
|
511 "missing a call to DO_STRUCT_DIFFERENCE"); |
|
512 |
|
513 // Note that we do not check whether this->RelevantLinkVisited() != |
|
514 // aOther->RelevantLinkVisited(); we don't need to since |
|
515 // nsCSSFrameConstructor::DoContentStateChanged always adds |
|
516 // nsChangeHint_RepaintFrame for NS_EVENT_STATE_VISITED changes (and |
|
517 // needs to, since HasStateDependentStyle probably doesn't work right |
|
518 // for NS_EVENT_STATE_VISITED). Hopefully this doesn't actually |
|
519 // expose whether links are visited to performance tests since all |
|
520 // link coloring happens asynchronously at a time when it's hard for |
|
521 // the page to measure. |
|
522 // However, we do need to compute the larger of the changes that can |
|
523 // happen depending on whether the link is visited or unvisited, since |
|
524 // doing only the one that's currently appropriate would expose which |
|
525 // links are in history to easy performance measurement. Therefore, |
|
526 // here, we add nsChangeHint_RepaintFrame hints (the maximum for |
|
527 // things that can depend on :visited) for the properties on which we |
|
528 // call GetVisitedDependentColor. |
|
529 nsStyleContext *thisVis = GetStyleIfVisited(), |
|
530 *otherVis = aOther->GetStyleIfVisited(); |
|
531 if (!thisVis != !otherVis) { |
|
532 // One style context has a style-if-visited and the other doesn't. |
|
533 // Presume a difference. |
|
534 NS_UpdateHint(hint, nsChangeHint_RepaintFrame); |
|
535 } else if (thisVis && !NS_IsHintSubset(nsChangeHint_RepaintFrame, hint)) { |
|
536 // Both style contexts have a style-if-visited. |
|
537 bool change = false; |
|
538 |
|
539 // NB: Calling Peek on |this|, not |thisVis|, since callers may look |
|
540 // at a struct on |this| without looking at the same struct on |
|
541 // |thisVis| (including this function if we skip one of these checks |
|
542 // due to change being true already or due to the old style context |
|
543 // not having a style-if-visited), but not the other way around. |
|
544 if (PeekStyleColor()) { |
|
545 if (thisVis->StyleColor()->mColor != |
|
546 otherVis->StyleColor()->mColor) { |
|
547 change = true; |
|
548 } |
|
549 } |
|
550 |
|
551 // NB: Calling Peek on |this|, not |thisVis| (see above). |
|
552 if (!change && PeekStyleBackground()) { |
|
553 if (thisVis->StyleBackground()->mBackgroundColor != |
|
554 otherVis->StyleBackground()->mBackgroundColor) { |
|
555 change = true; |
|
556 } |
|
557 } |
|
558 |
|
559 // NB: Calling Peek on |this|, not |thisVis| (see above). |
|
560 if (!change && PeekStyleBorder()) { |
|
561 const nsStyleBorder *thisVisBorder = thisVis->StyleBorder(); |
|
562 const nsStyleBorder *otherVisBorder = otherVis->StyleBorder(); |
|
563 NS_FOR_CSS_SIDES(side) { |
|
564 bool thisFG, otherFG; |
|
565 nscolor thisColor, otherColor; |
|
566 thisVisBorder->GetBorderColor(side, thisColor, thisFG); |
|
567 otherVisBorder->GetBorderColor(side, otherColor, otherFG); |
|
568 if (thisFG != otherFG || (!thisFG && thisColor != otherColor)) { |
|
569 change = true; |
|
570 break; |
|
571 } |
|
572 } |
|
573 } |
|
574 |
|
575 // NB: Calling Peek on |this|, not |thisVis| (see above). |
|
576 if (!change && PeekStyleOutline()) { |
|
577 const nsStyleOutline *thisVisOutline = thisVis->StyleOutline(); |
|
578 const nsStyleOutline *otherVisOutline = otherVis->StyleOutline(); |
|
579 bool haveColor; |
|
580 nscolor thisColor, otherColor; |
|
581 if (thisVisOutline->GetOutlineInitialColor() != |
|
582 otherVisOutline->GetOutlineInitialColor() || |
|
583 (haveColor = thisVisOutline->GetOutlineColor(thisColor)) != |
|
584 otherVisOutline->GetOutlineColor(otherColor) || |
|
585 (haveColor && thisColor != otherColor)) { |
|
586 change = true; |
|
587 } |
|
588 } |
|
589 |
|
590 // NB: Calling Peek on |this|, not |thisVis| (see above). |
|
591 if (!change && PeekStyleColumn()) { |
|
592 const nsStyleColumn *thisVisColumn = thisVis->StyleColumn(); |
|
593 const nsStyleColumn *otherVisColumn = otherVis->StyleColumn(); |
|
594 if (thisVisColumn->mColumnRuleColor != otherVisColumn->mColumnRuleColor || |
|
595 thisVisColumn->mColumnRuleColorIsForeground != |
|
596 otherVisColumn->mColumnRuleColorIsForeground) { |
|
597 change = true; |
|
598 } |
|
599 } |
|
600 |
|
601 // NB: Calling Peek on |this|, not |thisVis| (see above). |
|
602 if (!change && PeekStyleTextReset()) { |
|
603 const nsStyleTextReset *thisVisTextReset = thisVis->StyleTextReset(); |
|
604 const nsStyleTextReset *otherVisTextReset = otherVis->StyleTextReset(); |
|
605 nscolor thisVisDecColor, otherVisDecColor; |
|
606 bool thisVisDecColorIsFG, otherVisDecColorIsFG; |
|
607 thisVisTextReset->GetDecorationColor(thisVisDecColor, |
|
608 thisVisDecColorIsFG); |
|
609 otherVisTextReset->GetDecorationColor(otherVisDecColor, |
|
610 otherVisDecColorIsFG); |
|
611 if (thisVisDecColorIsFG != otherVisDecColorIsFG || |
|
612 (!thisVisDecColorIsFG && thisVisDecColor != otherVisDecColor)) { |
|
613 change = true; |
|
614 } |
|
615 } |
|
616 |
|
617 // NB: Calling Peek on |this|, not |thisVis| (see above). |
|
618 if (!change && PeekStyleSVG()) { |
|
619 const nsStyleSVG *thisVisSVG = thisVis->StyleSVG(); |
|
620 const nsStyleSVG *otherVisSVG = otherVis->StyleSVG(); |
|
621 if (thisVisSVG->mFill != otherVisSVG->mFill || |
|
622 thisVisSVG->mStroke != otherVisSVG->mStroke) { |
|
623 change = true; |
|
624 } |
|
625 } |
|
626 |
|
627 if (change) { |
|
628 NS_UpdateHint(hint, nsChangeHint_RepaintFrame); |
|
629 } |
|
630 } |
|
631 |
|
632 return hint; |
|
633 } |
|
634 |
|
635 void |
|
636 nsStyleContext::Mark() |
|
637 { |
|
638 // Mark our rule node. |
|
639 mRuleNode->Mark(); |
|
640 |
|
641 // Mark our children (i.e., tell them to mark their rule nodes, etc.). |
|
642 if (mChild) { |
|
643 nsStyleContext* child = mChild; |
|
644 do { |
|
645 child->Mark(); |
|
646 child = child->mNextSibling; |
|
647 } while (mChild != child); |
|
648 } |
|
649 |
|
650 if (mEmptyChild) { |
|
651 nsStyleContext* child = mEmptyChild; |
|
652 do { |
|
653 child->Mark(); |
|
654 child = child->mNextSibling; |
|
655 } while (mEmptyChild != child); |
|
656 } |
|
657 } |
|
658 |
|
659 #ifdef DEBUG |
|
660 void nsStyleContext::List(FILE* out, int32_t aIndent) |
|
661 { |
|
662 // Indent |
|
663 int32_t ix; |
|
664 for (ix = aIndent; --ix >= 0; ) fputs(" ", out); |
|
665 fprintf(out, "%p(%d) parent=%p ", |
|
666 (void*)this, mRefCnt, (void *)mParent); |
|
667 if (mPseudoTag) { |
|
668 nsAutoString buffer; |
|
669 mPseudoTag->ToString(buffer); |
|
670 fputs(NS_LossyConvertUTF16toASCII(buffer).get(), out); |
|
671 fputs(" ", out); |
|
672 } |
|
673 |
|
674 if (mRuleNode) { |
|
675 fputs("{\n", out); |
|
676 nsRuleNode* ruleNode = mRuleNode; |
|
677 while (ruleNode) { |
|
678 nsIStyleRule *styleRule = ruleNode->GetRule(); |
|
679 if (styleRule) { |
|
680 styleRule->List(out, aIndent + 1); |
|
681 } |
|
682 ruleNode = ruleNode->GetParent(); |
|
683 } |
|
684 for (ix = aIndent; --ix >= 0; ) fputs(" ", out); |
|
685 fputs("}\n", out); |
|
686 } |
|
687 else { |
|
688 fputs("{}\n", out); |
|
689 } |
|
690 |
|
691 if (nullptr != mChild) { |
|
692 nsStyleContext* child = mChild; |
|
693 do { |
|
694 child->List(out, aIndent + 1); |
|
695 child = child->mNextSibling; |
|
696 } while (mChild != child); |
|
697 } |
|
698 if (nullptr != mEmptyChild) { |
|
699 nsStyleContext* child = mEmptyChild; |
|
700 do { |
|
701 child->List(out, aIndent + 1); |
|
702 child = child->mNextSibling; |
|
703 } while (mEmptyChild != child); |
|
704 } |
|
705 } |
|
706 #endif |
|
707 |
|
708 // Overloaded new operator. Initializes the memory to 0 and relies on an arena |
|
709 // (which comes from the presShell) to perform the allocation. |
|
710 void* |
|
711 nsStyleContext::operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW |
|
712 { |
|
713 // Check the recycle list first. |
|
714 return aPresContext->PresShell()->AllocateByObjectID(nsPresArena::nsStyleContext_id, sz); |
|
715 } |
|
716 |
|
717 // Overridden to prevent the global delete from being called, since the memory |
|
718 // came out of an nsIArena instead of the global delete operator's heap. |
|
719 void |
|
720 nsStyleContext::Destroy() |
|
721 { |
|
722 // Get the pres context from our rule node. |
|
723 nsRefPtr<nsPresContext> presContext = mRuleNode->PresContext(); |
|
724 |
|
725 // Call our destructor. |
|
726 this->~nsStyleContext(); |
|
727 |
|
728 // Don't let the memory be freed, since it will be recycled |
|
729 // instead. Don't call the global operator delete. |
|
730 presContext->PresShell()->FreeByObjectID(nsPresArena::nsStyleContext_id, this); |
|
731 } |
|
732 |
|
733 already_AddRefed<nsStyleContext> |
|
734 NS_NewStyleContext(nsStyleContext* aParentContext, |
|
735 nsIAtom* aPseudoTag, |
|
736 nsCSSPseudoElements::Type aPseudoType, |
|
737 nsRuleNode* aRuleNode, |
|
738 bool aSkipFlexItemStyleFixup) |
|
739 { |
|
740 nsRefPtr<nsStyleContext> context = |
|
741 new (aRuleNode->PresContext()) |
|
742 nsStyleContext(aParentContext, aPseudoTag, aPseudoType, aRuleNode, |
|
743 aSkipFlexItemStyleFixup); |
|
744 return context.forget(); |
|
745 } |
|
746 |
|
747 static inline void |
|
748 ExtractAnimationValue(nsCSSProperty aProperty, |
|
749 nsStyleContext* aStyleContext, |
|
750 nsStyleAnimation::Value& aResult) |
|
751 { |
|
752 DebugOnly<bool> success = |
|
753 nsStyleAnimation::ExtractComputedValue(aProperty, aStyleContext, aResult); |
|
754 NS_ABORT_IF_FALSE(success, |
|
755 "aProperty must be extractable by nsStyleAnimation"); |
|
756 } |
|
757 |
|
758 static nscolor |
|
759 ExtractColor(nsCSSProperty aProperty, |
|
760 nsStyleContext *aStyleContext) |
|
761 { |
|
762 nsStyleAnimation::Value val; |
|
763 ExtractAnimationValue(aProperty, aStyleContext, val); |
|
764 return val.GetColorValue(); |
|
765 } |
|
766 |
|
767 static nscolor |
|
768 ExtractColorLenient(nsCSSProperty aProperty, |
|
769 nsStyleContext *aStyleContext) |
|
770 { |
|
771 nsStyleAnimation::Value val; |
|
772 ExtractAnimationValue(aProperty, aStyleContext, val); |
|
773 if (val.GetUnit() == nsStyleAnimation::eUnit_Color) { |
|
774 return val.GetColorValue(); |
|
775 } |
|
776 return NS_RGBA(0, 0, 0, 0); |
|
777 } |
|
778 |
|
779 struct ColorIndexSet { |
|
780 uint8_t colorIndex, alphaIndex; |
|
781 }; |
|
782 |
|
783 static const ColorIndexSet gVisitedIndices[2] = { { 0, 0 }, { 1, 0 } }; |
|
784 |
|
785 nscolor |
|
786 nsStyleContext::GetVisitedDependentColor(nsCSSProperty aProperty) |
|
787 { |
|
788 NS_ASSERTION(aProperty == eCSSProperty_color || |
|
789 aProperty == eCSSProperty_background_color || |
|
790 aProperty == eCSSProperty_border_top_color || |
|
791 aProperty == eCSSProperty_border_right_color_value || |
|
792 aProperty == eCSSProperty_border_bottom_color || |
|
793 aProperty == eCSSProperty_border_left_color_value || |
|
794 aProperty == eCSSProperty_outline_color || |
|
795 aProperty == eCSSProperty__moz_column_rule_color || |
|
796 aProperty == eCSSProperty_text_decoration_color || |
|
797 aProperty == eCSSProperty_fill || |
|
798 aProperty == eCSSProperty_stroke, |
|
799 "we need to add to nsStyleContext::CalcStyleDifference"); |
|
800 |
|
801 bool isPaintProperty = aProperty == eCSSProperty_fill || |
|
802 aProperty == eCSSProperty_stroke; |
|
803 |
|
804 nscolor colors[2]; |
|
805 colors[0] = isPaintProperty ? ExtractColorLenient(aProperty, this) |
|
806 : ExtractColor(aProperty, this); |
|
807 |
|
808 nsStyleContext *visitedStyle = this->GetStyleIfVisited(); |
|
809 if (!visitedStyle) { |
|
810 return colors[0]; |
|
811 } |
|
812 |
|
813 colors[1] = isPaintProperty ? ExtractColorLenient(aProperty, visitedStyle) |
|
814 : ExtractColor(aProperty, visitedStyle); |
|
815 |
|
816 return nsStyleContext::CombineVisitedColors(colors, |
|
817 this->RelevantLinkVisited()); |
|
818 } |
|
819 |
|
820 /* static */ nscolor |
|
821 nsStyleContext::CombineVisitedColors(nscolor *aColors, bool aLinkIsVisited) |
|
822 { |
|
823 if (NS_GET_A(aColors[1]) == 0) { |
|
824 // If the style-if-visited is transparent, then just use the |
|
825 // unvisited style rather than using the (meaningless) color |
|
826 // components of the visited style along with a potentially |
|
827 // non-transparent alpha value. |
|
828 aLinkIsVisited = false; |
|
829 } |
|
830 |
|
831 // NOTE: We want this code to have as little timing dependence as |
|
832 // possible on whether this->RelevantLinkVisited() is true. |
|
833 const ColorIndexSet &set = |
|
834 gVisitedIndices[aLinkIsVisited ? 1 : 0]; |
|
835 |
|
836 nscolor colorColor = aColors[set.colorIndex]; |
|
837 nscolor alphaColor = aColors[set.alphaIndex]; |
|
838 return NS_RGBA(NS_GET_R(colorColor), NS_GET_G(colorColor), |
|
839 NS_GET_B(colorColor), NS_GET_A(alphaColor)); |
|
840 } |
|
841 |
|
842 void* |
|
843 nsStyleContext::Alloc(size_t aSize) |
|
844 { |
|
845 nsIPresShell *shell = PresContext()->PresShell(); |
|
846 |
|
847 aSize += offsetof(AllocationHeader, mStorageStart); |
|
848 AllocationHeader *alloc = |
|
849 static_cast<AllocationHeader*>(shell->AllocateMisc(aSize)); |
|
850 |
|
851 alloc->mSize = aSize; // NOTE: inflated by header |
|
852 |
|
853 alloc->mNext = mAllocations; |
|
854 mAllocations = alloc; |
|
855 |
|
856 return static_cast<void*>(&alloc->mStorageStart); |
|
857 } |
|
858 |
|
859 void |
|
860 nsStyleContext::FreeAllocations(nsPresContext *aPresContext) |
|
861 { |
|
862 nsIPresShell *shell = aPresContext->PresShell(); |
|
863 |
|
864 for (AllocationHeader *alloc = mAllocations, *next; alloc; alloc = next) { |
|
865 next = alloc->mNext; |
|
866 shell->FreeMisc(alloc->mSize, alloc); |
|
867 } |
|
868 } |
|
869 |
|
870 #ifdef DEBUG |
|
871 /* static */ void |
|
872 nsStyleContext::AssertStyleStructMaxDifferenceValid() |
|
873 { |
|
874 #define STYLE_STRUCT(name, checkdata_cb) \ |
|
875 MOZ_ASSERT(NS_IsHintSubset(nsStyle##name::MaxDifferenceNeverInherited(), \ |
|
876 nsStyle##name::MaxDifference())); |
|
877 #include "nsStyleStructList.h" |
|
878 #undef STYLE_STRUCT |
|
879 } |
|
880 #endif |