layout/style/nsStyleContext.cpp

changeset 2
7e26c7da4463
equal deleted inserted replaced
-1:000000000000 0:c98811782092
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

mercurial