1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * David Hyatt <hyatt@netscape.com>
24 : * Pierre Phaneuf <pp@ludusdesign.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : /* the interface (to internal code) for retrieving computed style data */
41 :
42 : #include "nsStyleConsts.h"
43 : #include "nsString.h"
44 : #include "nsPresContext.h"
45 : #include "nsIStyleRule.h"
46 :
47 : #include "nsCOMPtr.h"
48 : #include "nsStyleSet.h"
49 : #include "nsIPresShell.h"
50 :
51 : #include "nsRuleNode.h"
52 : #include "nsStyleContext.h"
53 : #include "prlog.h"
54 : #include "nsStyleAnimation.h"
55 :
56 : #ifdef DEBUG
57 : // #define NOISY_DEBUG
58 : #endif
59 :
60 : //----------------------------------------------------------------------
61 :
62 :
63 0 : nsStyleContext::nsStyleContext(nsStyleContext* aParent,
64 : nsIAtom* aPseudoTag,
65 : nsCSSPseudoElements::Type aPseudoType,
66 : nsRuleNode* aRuleNode,
67 : nsPresContext* aPresContext)
68 : : mParent(aParent),
69 : mChild(nsnull),
70 : mEmptyChild(nsnull),
71 : mPseudoTag(aPseudoTag),
72 : mRuleNode(aRuleNode),
73 : mAllocations(nsnull),
74 : mCachedResetData(nsnull),
75 : mBits(((PRUint32)aPseudoType) << NS_STYLE_CONTEXT_TYPE_SHIFT),
76 0 : mRefCnt(0)
77 : {
78 : // This check has to be done "backward", because if it were written the
79 : // more natural way it wouldn't fail even when it needed to.
80 : MOZ_STATIC_ASSERT((PR_UINT32_MAX >> NS_STYLE_CONTEXT_TYPE_SHIFT) >=
81 : nsCSSPseudoElements::ePseudo_MAX,
82 : "pseudo element bits no longer fit in a PRUint32");
83 :
84 0 : mNextSibling = this;
85 0 : mPrevSibling = this;
86 0 : if (mParent) {
87 0 : mParent->AddRef();
88 0 : mParent->AddChild(this);
89 : #ifdef DEBUG
90 0 : nsRuleNode *r1 = mParent->GetRuleNode(), *r2 = aRuleNode;
91 0 : while (r1->GetParent())
92 0 : r1 = r1->GetParent();
93 0 : while (r2->GetParent())
94 0 : r2 = r2->GetParent();
95 0 : NS_ASSERTION(r1 == r2, "must be in the same rule tree as parent");
96 : #endif
97 : }
98 :
99 0 : ApplyStyleFixups(aPresContext);
100 :
101 : #define eStyleStruct_LastItem (nsStyleStructID_Length - 1)
102 : NS_ASSERTION(NS_STYLE_INHERIT_MASK & NS_STYLE_INHERIT_BIT(LastItem),
103 : "NS_STYLE_INHERIT_MASK must be bigger, and other bits shifted");
104 : #undef eStyleStruct_LastItem
105 :
106 0 : mRuleNode->AddRef();
107 0 : }
108 :
109 0 : nsStyleContext::~nsStyleContext()
110 : {
111 0 : NS_ASSERTION((nsnull == mChild) && (nsnull == mEmptyChild), "destructing context with children");
112 :
113 0 : nsPresContext *presContext = mRuleNode->GetPresContext();
114 :
115 0 : mRuleNode->Release();
116 :
117 : presContext->PresShell()->StyleSet()->
118 0 : NotifyStyleContextDestroyed(presContext, this);
119 :
120 0 : if (mParent) {
121 0 : mParent->RemoveChild(this);
122 0 : mParent->Release();
123 : }
124 :
125 : // Free up our data structs.
126 0 : mCachedInheritedData.DestroyStructs(mBits, presContext);
127 0 : if (mCachedResetData) {
128 0 : mCachedResetData->Destroy(mBits, presContext);
129 : }
130 :
131 0 : FreeAllocations(presContext);
132 0 : }
133 :
134 0 : void nsStyleContext::AddChild(nsStyleContext* aChild)
135 : {
136 0 : NS_ASSERTION(aChild->mPrevSibling == aChild &&
137 : aChild->mNextSibling == aChild,
138 : "child already in a child list");
139 :
140 0 : nsStyleContext **list = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild;
141 :
142 : // Insert at the beginning of the list. See also FindChildWithRules.
143 0 : if (*list) {
144 : // Link into existing elements, if there are any.
145 0 : aChild->mNextSibling = (*list);
146 0 : aChild->mPrevSibling = (*list)->mPrevSibling;
147 0 : (*list)->mPrevSibling->mNextSibling = aChild;
148 0 : (*list)->mPrevSibling = aChild;
149 : }
150 0 : (*list) = aChild;
151 0 : }
152 :
153 0 : void nsStyleContext::RemoveChild(nsStyleContext* aChild)
154 : {
155 0 : NS_PRECONDITION(nsnull != aChild && this == aChild->mParent, "bad argument");
156 :
157 0 : nsStyleContext **list = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild;
158 :
159 0 : if (aChild->mPrevSibling != aChild) { // has siblings
160 0 : if ((*list) == aChild) {
161 0 : (*list) = (*list)->mNextSibling;
162 : }
163 : }
164 : else {
165 0 : NS_ASSERTION((*list) == aChild, "bad sibling pointers");
166 0 : (*list) = nsnull;
167 : }
168 :
169 0 : aChild->mPrevSibling->mNextSibling = aChild->mNextSibling;
170 0 : aChild->mNextSibling->mPrevSibling = aChild->mPrevSibling;
171 0 : aChild->mNextSibling = aChild;
172 0 : aChild->mPrevSibling = aChild;
173 0 : }
174 :
175 : already_AddRefed<nsStyleContext>
176 0 : nsStyleContext::FindChildWithRules(const nsIAtom* aPseudoTag,
177 : nsRuleNode* aRuleNode,
178 : nsRuleNode* aRulesIfVisited,
179 : bool aRelevantLinkVisited)
180 : {
181 0 : NS_ABORT_IF_FALSE(aRulesIfVisited || !aRelevantLinkVisited,
182 : "aRelevantLinkVisited should only be set when we have a separate style");
183 0 : PRUint32 threshold = 10; // The # of siblings we're willing to examine
184 : // before just giving this whole thing up.
185 :
186 0 : nsStyleContext* result = nsnull;
187 0 : nsStyleContext *list = aRuleNode->IsRoot() ? mEmptyChild : mChild;
188 :
189 0 : if (list) {
190 0 : nsStyleContext *child = list;
191 0 : do {
192 0 : if (child->mRuleNode == aRuleNode &&
193 0 : child->mPseudoTag == aPseudoTag &&
194 0 : !child->IsStyleIfVisited() &&
195 0 : child->RelevantLinkVisited() == aRelevantLinkVisited) {
196 0 : bool match = false;
197 0 : if (aRulesIfVisited) {
198 0 : match = child->GetStyleIfVisited() &&
199 0 : child->GetStyleIfVisited()->mRuleNode == aRulesIfVisited;
200 : } else {
201 0 : match = !child->GetStyleIfVisited();
202 : }
203 0 : if (match) {
204 0 : result = child;
205 0 : break;
206 : }
207 : }
208 0 : child = child->mNextSibling;
209 0 : threshold--;
210 0 : if (threshold == 0)
211 0 : break;
212 : } while (child != list);
213 : }
214 :
215 0 : if (result) {
216 0 : if (result != list) {
217 : // Move result to the front of the list.
218 0 : RemoveChild(result);
219 0 : AddChild(result);
220 : }
221 :
222 : // Add reference for the caller.
223 0 : result->AddRef();
224 : }
225 :
226 0 : return result;
227 : }
228 :
229 0 : const void* nsStyleContext::GetCachedStyleData(nsStyleStructID aSID)
230 : {
231 : const void* cachedData;
232 0 : if (nsCachedStyleData::IsReset(aSID)) {
233 0 : if (mCachedResetData) {
234 0 : cachedData = mCachedResetData->mStyleStructs[aSID];
235 : } else {
236 0 : cachedData = nsnull;
237 : }
238 : } else {
239 0 : cachedData = mCachedInheritedData.mStyleStructs[aSID];
240 : }
241 0 : return cachedData;
242 : }
243 :
244 0 : const void* nsStyleContext::GetStyleData(nsStyleStructID aSID)
245 : {
246 0 : const void* cachedData = GetCachedStyleData(aSID);
247 0 : if (cachedData)
248 0 : return cachedData; // We have computed data stored on this node in the context tree.
249 0 : return mRuleNode->GetStyleData(aSID, this, true); // Our rule node will take care of it for us.
250 : }
251 :
252 : // This is an evil evil function, since it forces you to alloc your own separate copy of
253 : // style data! Do not use this function unless you absolutely have to! You should avoid
254 : // this at all costs! -dwh
255 : void*
256 0 : nsStyleContext::GetUniqueStyleData(const nsStyleStructID& aSID)
257 : {
258 : // If we already own the struct and no kids could depend on it, then
259 : // just return it. (We leak in this case if there are kids -- and this
260 : // function really shouldn't be called for style contexts that could
261 : // have kids depending on the data. ClearStyleData would be OK, but
262 : // this test for no mChild or mEmptyChild doesn't catch that case.)
263 0 : const void *current = GetStyleData(aSID);
264 0 : if (!mChild && !mEmptyChild &&
265 0 : !(mBits & nsCachedStyleData::GetBitForSID(aSID)) &&
266 0 : GetCachedStyleData(aSID))
267 0 : return const_cast<void*>(current);
268 :
269 : void* result;
270 0 : nsPresContext *presContext = PresContext();
271 0 : switch (aSID) {
272 :
273 : #define UNIQUE_CASE(c_) \
274 : case eStyleStruct_##c_: \
275 : result = new (presContext) nsStyle##c_( \
276 : * static_cast<const nsStyle##c_ *>(current)); \
277 : break;
278 :
279 0 : UNIQUE_CASE(Display)
280 0 : UNIQUE_CASE(Background)
281 0 : UNIQUE_CASE(Text)
282 0 : UNIQUE_CASE(TextReset)
283 :
284 : #undef UNIQUE_CASE
285 :
286 : default:
287 0 : NS_ERROR("Struct type not supported. Please find another way to do this if you can!");
288 0 : return nsnull;
289 : }
290 :
291 0 : if (!result) {
292 : NS_WARNING("Ran out of memory while trying to allocate memory for a unique style struct! "
293 0 : "Returning the non-unique data.");
294 0 : return const_cast<void*>(current);
295 : }
296 :
297 0 : SetStyle(aSID, result);
298 0 : mBits &= ~nsCachedStyleData::GetBitForSID(aSID);
299 :
300 0 : return result;
301 : }
302 :
303 : void
304 0 : nsStyleContext::SetStyle(nsStyleStructID aSID, void* aStruct)
305 : {
306 : // This method should only be called from nsRuleNode! It is not a public
307 : // method!
308 :
309 0 : NS_ASSERTION(aSID >= 0 && aSID < nsStyleStructID_Length, "out of bounds");
310 :
311 : // NOTE: nsCachedStyleData::GetStyleData works roughly the same way.
312 : // See the comments there (in nsRuleNode.h) for more details about
313 : // what this is doing and why.
314 :
315 : void** dataSlot;
316 0 : if (nsCachedStyleData::IsReset(aSID)) {
317 0 : if (!mCachedResetData) {
318 0 : mCachedResetData = new (mRuleNode->GetPresContext()) nsResetStyleData;
319 : // XXXbz And if that fails?
320 : }
321 0 : dataSlot = &mCachedResetData->mStyleStructs[aSID];
322 : } else {
323 0 : dataSlot = &mCachedInheritedData.mStyleStructs[aSID];
324 : }
325 0 : NS_ASSERTION(!*dataSlot || (mBits & nsCachedStyleData::GetBitForSID(aSID)),
326 : "Going to leak style data");
327 0 : *dataSlot = aStruct;
328 0 : }
329 :
330 : void
331 0 : nsStyleContext::ApplyStyleFixups(nsPresContext* aPresContext)
332 : {
333 : // See if we have any text decorations.
334 : // First see if our parent has text decorations. If our parent does, then we inherit the bit.
335 0 : if (mParent && mParent->HasTextDecorationLines()) {
336 0 : mBits |= NS_STYLE_HAS_TEXT_DECORATION_LINES;
337 : } else {
338 : // We might have defined a decoration.
339 0 : const nsStyleTextReset* text = GetStyleTextReset();
340 0 : PRUint8 decorationLine = text->mTextDecorationLine;
341 0 : if (decorationLine != NS_STYLE_TEXT_DECORATION_LINE_NONE &&
342 : decorationLine != NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL) {
343 0 : mBits |= NS_STYLE_HAS_TEXT_DECORATION_LINES;
344 : }
345 : }
346 :
347 0 : if ((mParent && mParent->HasPseudoElementData()) || mPseudoTag) {
348 0 : mBits |= NS_STYLE_HAS_PSEUDO_ELEMENT_DATA;
349 : }
350 :
351 : // Correct tables.
352 0 : const nsStyleDisplay* disp = GetStyleDisplay();
353 0 : if (disp->mDisplay == NS_STYLE_DISPLAY_TABLE) {
354 : // -moz-center and -moz-right are used for HTML's alignment
355 : // This is covering the <div align="right"><table>...</table></div> case.
356 : // In this case, we don't want to inherit the text alignment into the table.
357 0 : const nsStyleText* text = GetStyleText();
358 :
359 0 : if (text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER ||
360 : text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)
361 : {
362 0 : nsStyleText* uniqueText = (nsStyleText*)GetUniqueStyleData(eStyleStruct_Text);
363 0 : uniqueText->mTextAlign = NS_STYLE_TEXT_ALIGN_DEFAULT;
364 : }
365 : }
366 :
367 : // CSS2.1 section 9.2.4 specifies fixups for the 'display' property of
368 : // the root element. We can't implement them in nsRuleNode because we
369 : // don't want to store all display structs that aren't 'block',
370 : // 'inline', or 'table' in the style context tree on the off chance
371 : // that the root element has its style reresolved later. So do them
372 : // here if needed, by changing the style data, so that other code
373 : // doesn't get confused by looking at the style data.
374 0 : if (!mParent) {
375 0 : if (disp->mDisplay != NS_STYLE_DISPLAY_NONE &&
376 : disp->mDisplay != NS_STYLE_DISPLAY_BLOCK &&
377 : disp->mDisplay != NS_STYLE_DISPLAY_TABLE) {
378 : nsStyleDisplay *mutable_display = static_cast<nsStyleDisplay*>
379 0 : (GetUniqueStyleData(eStyleStruct_Display));
380 : // If we're in this code, then mOriginalDisplay doesn't matter
381 : // for purposes of the cascade (because this nsStyleDisplay
382 : // isn't living in the ruletree anyway), and for determining
383 : // hypothetical boxes it's better to have mOriginalDisplay
384 : // matching mDisplay here.
385 0 : if (mutable_display->mDisplay == NS_STYLE_DISPLAY_INLINE_TABLE)
386 : mutable_display->mOriginalDisplay = mutable_display->mDisplay =
387 0 : NS_STYLE_DISPLAY_TABLE;
388 : else
389 : mutable_display->mOriginalDisplay = mutable_display->mDisplay =
390 0 : NS_STYLE_DISPLAY_BLOCK;
391 : }
392 : }
393 :
394 : // Computer User Interface style, to trigger loads of cursors
395 0 : GetStyleUserInterface();
396 0 : }
397 :
398 : nsChangeHint
399 0 : nsStyleContext::CalcStyleDifference(nsStyleContext* aOther)
400 : {
401 0 : nsChangeHint hint = NS_STYLE_HINT_NONE;
402 0 : NS_ENSURE_TRUE(aOther, hint);
403 : // We must always ensure that we populate the structs on the new style
404 : // context that are filled in on the old context, so that if we get
405 : // two style changes in succession, the second of which causes a real
406 : // style change, the PeekStyleData doesn't return null (implying that
407 : // nobody ever looked at that struct's data). In other words, we
408 : // can't skip later structs if we get a big change up front, because
409 : // we could later get a small change in one of those structs that we
410 : // don't want to miss.
411 :
412 : // If our rule nodes are the same, then we are looking at the same
413 : // style data. We know this because CalcStyleDifference is always
414 : // called on two style contexts that point to the same element, so we
415 : // know that our position in the style context tree is the same and
416 : // our position in the rule node tree is also the same.
417 0 : bool compare = mRuleNode != aOther->mRuleNode;
418 :
419 : #define DO_STRUCT_DIFFERENCE(struct_) \
420 : PR_BEGIN_MACRO \
421 : NS_ASSERTION(NS_IsHintSubset(nsStyle##struct_::MaxDifference(), maxHint), \
422 : "Struct placed in the wrong maxHint section"); \
423 : const nsStyle##struct_* this##struct_ = PeekStyle##struct_(); \
424 : if (this##struct_) { \
425 : const nsStyle##struct_* other##struct_ = aOther->GetStyle##struct_(); \
426 : if ((compare || nsStyle##struct_::ForceCompare()) && \
427 : !NS_IsHintSubset(maxHint, hint) && \
428 : this##struct_ != other##struct_) { \
429 : NS_ASSERTION(NS_IsHintSubset( \
430 : this##struct_->CalcDifference(*other##struct_), \
431 : nsStyle##struct_::MaxDifference()), \
432 : "CalcDifference() returned bigger hint than MaxDifference()"); \
433 : NS_UpdateHint(hint, this##struct_->CalcDifference(*other##struct_)); \
434 : } \
435 : } \
436 : PR_END_MACRO
437 :
438 : // We begin by examining those style structs that are capable of
439 : // causing the maximal difference, a FRAMECHANGE.
440 : // FRAMECHANGE Structs: Display, XUL, Content, UserInterface,
441 : // Visibility, Outline, TableBorder, Table, Text, UIReset, Quotes
442 : nsChangeHint maxHint = nsChangeHint(NS_STYLE_HINT_FRAMECHANGE |
443 : nsChangeHint_UpdateTransformLayer | nsChangeHint_UpdateOpacityLayer |
444 0 : nsChangeHint_UpdateOverflow);
445 0 : DO_STRUCT_DIFFERENCE(Display);
446 :
447 : maxHint = nsChangeHint(NS_STYLE_HINT_FRAMECHANGE |
448 0 : nsChangeHint_UpdateCursor);
449 0 : DO_STRUCT_DIFFERENCE(XUL);
450 0 : DO_STRUCT_DIFFERENCE(Column);
451 0 : DO_STRUCT_DIFFERENCE(Content);
452 0 : DO_STRUCT_DIFFERENCE(UserInterface);
453 0 : DO_STRUCT_DIFFERENCE(Visibility);
454 0 : DO_STRUCT_DIFFERENCE(Outline);
455 0 : DO_STRUCT_DIFFERENCE(TableBorder);
456 0 : DO_STRUCT_DIFFERENCE(Table);
457 0 : DO_STRUCT_DIFFERENCE(UIReset);
458 0 : DO_STRUCT_DIFFERENCE(Text);
459 0 : DO_STRUCT_DIFFERENCE(List);
460 : // If the quotes implementation is ever going to change we might not need
461 : // a framechange here and a reflow should be sufficient. See bug 35768.
462 0 : DO_STRUCT_DIFFERENCE(Quotes);
463 :
464 0 : maxHint = nsChangeHint(NS_STYLE_HINT_REFLOW | nsChangeHint_UpdateEffects);
465 0 : DO_STRUCT_DIFFERENCE(SVGReset);
466 0 : DO_STRUCT_DIFFERENCE(SVG);
467 :
468 : // At this point, we know that the worst kind of damage we could do is
469 : // a reflow.
470 0 : maxHint = NS_STYLE_HINT_REFLOW;
471 :
472 : // The following structs cause (as their maximal difference) a reflow
473 : // to occur. REFLOW Structs: Font, Margin, Padding, Border, List,
474 : // Position, Text, TextReset
475 0 : DO_STRUCT_DIFFERENCE(Font);
476 0 : DO_STRUCT_DIFFERENCE(Margin);
477 0 : DO_STRUCT_DIFFERENCE(Padding);
478 0 : DO_STRUCT_DIFFERENCE(Border);
479 0 : DO_STRUCT_DIFFERENCE(Position);
480 0 : DO_STRUCT_DIFFERENCE(TextReset);
481 :
482 : // Most backgrounds only require a re-render (i.e., a VISUAL change), but
483 : // backgrounds using -moz-element need to reset SVG effects, too.
484 0 : maxHint = nsChangeHint(NS_STYLE_HINT_VISUAL | nsChangeHint_UpdateEffects);
485 0 : DO_STRUCT_DIFFERENCE(Background);
486 :
487 : // Color only needs a repaint.
488 0 : maxHint = NS_STYLE_HINT_VISUAL;
489 0 : DO_STRUCT_DIFFERENCE(Color);
490 :
491 : #undef DO_STRUCT_DIFFERENCE
492 :
493 : // Note that we do not check whether this->RelevantLinkVisited() !=
494 : // aOther->RelevantLinkVisited(); we don't need to since
495 : // nsCSSFrameConstructor::DoContentStateChanged always adds
496 : // nsChangeHint_RepaintFrame for NS_EVENT_STATE_VISITED changes (and
497 : // needs to, since HasStateDependentStyle probably doesn't work right
498 : // for NS_EVENT_STATE_VISITED). Hopefully this doesn't actually
499 : // expose whether links are visited to performance tests since all
500 : // link coloring happens asynchronously at a time when it's hard for
501 : // the page to measure.
502 : // However, we do need to compute the larger of the changes that can
503 : // happen depending on whether the link is visited or unvisited, since
504 : // doing only the one that's currently appropriate would expose which
505 : // links are in history to easy performance measurement. Therefore,
506 : // here, we add nsChangeHint_RepaintFrame hints (the maximum for
507 : // things that can depend on :visited) for the properties on which we
508 : // call GetVisitedDependentColor.
509 0 : nsStyleContext *thisVis = GetStyleIfVisited(),
510 0 : *otherVis = aOther->GetStyleIfVisited();
511 0 : if (!thisVis != !otherVis) {
512 : // One style context has a style-if-visited and the other doesn't.
513 : // Presume a difference.
514 0 : NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
515 0 : } else if (thisVis && !NS_IsHintSubset(nsChangeHint_RepaintFrame, hint)) {
516 : // Both style contexts have a style-if-visited.
517 0 : bool change = false;
518 :
519 : // NB: Calling Peek on |this|, not |thisVis|, since callers may look
520 : // at a struct on |this| without looking at the same struct on
521 : // |thisVis| (including this function if we skip one of these checks
522 : // due to change being true already or due to the old style context
523 : // not having a style-if-visited), but not the other way around.
524 0 : if (PeekStyleColor()) {
525 0 : if (thisVis->GetStyleColor()->mColor !=
526 0 : otherVis->GetStyleColor()->mColor) {
527 0 : change = true;
528 : }
529 : }
530 :
531 : // NB: Calling Peek on |this|, not |thisVis| (see above).
532 0 : if (!change && PeekStyleBackground()) {
533 0 : if (thisVis->GetStyleBackground()->mBackgroundColor !=
534 0 : otherVis->GetStyleBackground()->mBackgroundColor) {
535 0 : change = true;
536 : }
537 : }
538 :
539 : // NB: Calling Peek on |this|, not |thisVis| (see above).
540 0 : if (!change && PeekStyleBorder()) {
541 0 : const nsStyleBorder *thisVisBorder = thisVis->GetStyleBorder();
542 0 : const nsStyleBorder *otherVisBorder = otherVis->GetStyleBorder();
543 0 : NS_FOR_CSS_SIDES(side) {
544 : bool thisFG, otherFG;
545 : nscolor thisColor, otherColor;
546 0 : thisVisBorder->GetBorderColor(side, thisColor, thisFG);
547 0 : otherVisBorder->GetBorderColor(side, otherColor, otherFG);
548 0 : if (thisFG != otherFG || (!thisFG && thisColor != otherColor)) {
549 0 : change = true;
550 0 : break;
551 : }
552 : }
553 : }
554 :
555 : // NB: Calling Peek on |this|, not |thisVis| (see above).
556 0 : if (!change && PeekStyleOutline()) {
557 0 : const nsStyleOutline *thisVisOutline = thisVis->GetStyleOutline();
558 0 : const nsStyleOutline *otherVisOutline = otherVis->GetStyleOutline();
559 : bool haveColor;
560 : nscolor thisColor, otherColor;
561 0 : if (thisVisOutline->GetOutlineInitialColor() !=
562 0 : otherVisOutline->GetOutlineInitialColor() ||
563 : (haveColor = thisVisOutline->GetOutlineColor(thisColor)) !=
564 0 : otherVisOutline->GetOutlineColor(otherColor) ||
565 : (haveColor && thisColor != otherColor)) {
566 0 : change = true;
567 : }
568 : }
569 :
570 : // NB: Calling Peek on |this|, not |thisVis| (see above).
571 0 : if (!change && PeekStyleColumn()) {
572 0 : const nsStyleColumn *thisVisColumn = thisVis->GetStyleColumn();
573 0 : const nsStyleColumn *otherVisColumn = otherVis->GetStyleColumn();
574 0 : if (thisVisColumn->mColumnRuleColor != otherVisColumn->mColumnRuleColor ||
575 : thisVisColumn->mColumnRuleColorIsForeground !=
576 : otherVisColumn->mColumnRuleColorIsForeground) {
577 0 : change = true;
578 : }
579 : }
580 :
581 : // NB: Calling Peek on |this|, not |thisVis| (see above).
582 0 : if (!change && PeekStyleTextReset()) {
583 0 : const nsStyleTextReset *thisVisTextReset = thisVis->GetStyleTextReset();
584 0 : const nsStyleTextReset *otherVisTextReset = otherVis->GetStyleTextReset();
585 : nscolor thisVisDecColor, otherVisDecColor;
586 : bool thisVisDecColorIsFG, otherVisDecColorIsFG;
587 : thisVisTextReset->GetDecorationColor(thisVisDecColor,
588 0 : thisVisDecColorIsFG);
589 : otherVisTextReset->GetDecorationColor(otherVisDecColor,
590 0 : otherVisDecColorIsFG);
591 0 : if (thisVisDecColorIsFG != otherVisDecColorIsFG ||
592 0 : (!thisVisDecColorIsFG && thisVisDecColor != otherVisDecColor)) {
593 0 : change = true;
594 : }
595 : }
596 :
597 : // NB: Calling Peek on |this|, not |thisVis| (see above).
598 0 : if (!change && PeekStyleSVG()) {
599 0 : const nsStyleSVG *thisVisSVG = thisVis->GetStyleSVG();
600 0 : const nsStyleSVG *otherVisSVG = otherVis->GetStyleSVG();
601 0 : if (thisVisSVG->mFill != otherVisSVG->mFill ||
602 0 : thisVisSVG->mStroke != otherVisSVG->mStroke) {
603 0 : change = true;
604 : }
605 : }
606 :
607 0 : if (change) {
608 0 : NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
609 : }
610 : }
611 :
612 0 : return hint;
613 : }
614 :
615 : void
616 0 : nsStyleContext::Mark()
617 : {
618 : // Mark our rule node.
619 0 : mRuleNode->Mark();
620 :
621 : // Mark our children (i.e., tell them to mark their rule nodes, etc.).
622 0 : if (mChild) {
623 0 : nsStyleContext* child = mChild;
624 0 : do {
625 0 : child->Mark();
626 0 : child = child->mNextSibling;
627 : } while (mChild != child);
628 : }
629 :
630 0 : if (mEmptyChild) {
631 0 : nsStyleContext* child = mEmptyChild;
632 0 : do {
633 0 : child->Mark();
634 0 : child = child->mNextSibling;
635 : } while (mEmptyChild != child);
636 : }
637 0 : }
638 :
639 : #ifdef DEBUG
640 0 : void nsStyleContext::List(FILE* out, PRInt32 aIndent)
641 : {
642 : // Indent
643 : PRInt32 ix;
644 0 : for (ix = aIndent; --ix >= 0; ) fputs(" ", out);
645 : fprintf(out, "%p(%d) parent=%p ",
646 0 : (void*)this, mRefCnt, (void *)mParent);
647 0 : if (mPseudoTag) {
648 0 : nsAutoString buffer;
649 0 : mPseudoTag->ToString(buffer);
650 0 : fputs(NS_LossyConvertUTF16toASCII(buffer).get(), out);
651 0 : fputs(" ", out);
652 : }
653 :
654 0 : if (mRuleNode) {
655 0 : fputs("{\n", out);
656 0 : nsRuleNode* ruleNode = mRuleNode;
657 0 : while (ruleNode) {
658 0 : nsIStyleRule *styleRule = ruleNode->GetRule();
659 0 : if (styleRule) {
660 0 : styleRule->List(out, aIndent + 1);
661 : }
662 0 : ruleNode = ruleNode->GetParent();
663 : }
664 0 : for (ix = aIndent; --ix >= 0; ) fputs(" ", out);
665 0 : fputs("}\n", out);
666 : }
667 : else {
668 0 : fputs("{}\n", out);
669 : }
670 :
671 0 : if (nsnull != mChild) {
672 0 : nsStyleContext* child = mChild;
673 0 : do {
674 0 : child->List(out, aIndent + 1);
675 0 : child = child->mNextSibling;
676 : } while (mChild != child);
677 : }
678 0 : if (nsnull != mEmptyChild) {
679 0 : nsStyleContext* child = mEmptyChild;
680 0 : do {
681 0 : child->List(out, aIndent + 1);
682 0 : child = child->mNextSibling;
683 : } while (mEmptyChild != child);
684 : }
685 0 : }
686 : #endif
687 :
688 : // Overloaded new operator. Initializes the memory to 0 and relies on an arena
689 : // (which comes from the presShell) to perform the allocation.
690 : void*
691 0 : nsStyleContext::operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW
692 : {
693 : // Check the recycle list first.
694 0 : return aPresContext->AllocateFromShell(sz);
695 : }
696 :
697 : // Overridden to prevent the global delete from being called, since the memory
698 : // came out of an nsIArena instead of the global delete operator's heap.
699 : void
700 0 : nsStyleContext::Destroy()
701 : {
702 : // Get the pres context from our rule node.
703 0 : nsRefPtr<nsPresContext> presContext = mRuleNode->GetPresContext();
704 :
705 : // Call our destructor.
706 0 : this->~nsStyleContext();
707 :
708 : // Don't let the memory be freed, since it will be recycled
709 : // instead. Don't call the global operator delete.
710 0 : presContext->FreeToShell(sizeof(nsStyleContext), this);
711 0 : }
712 :
713 : already_AddRefed<nsStyleContext>
714 0 : NS_NewStyleContext(nsStyleContext* aParentContext,
715 : nsIAtom* aPseudoTag,
716 : nsCSSPseudoElements::Type aPseudoType,
717 : nsRuleNode* aRuleNode,
718 : nsPresContext* aPresContext)
719 : {
720 : nsStyleContext* context =
721 : new (aPresContext) nsStyleContext(aParentContext, aPseudoTag, aPseudoType,
722 0 : aRuleNode, aPresContext);
723 0 : if (context)
724 0 : context->AddRef();
725 0 : return context;
726 : }
727 :
728 0 : static nscolor ExtractColor(nsCSSProperty aProperty,
729 : nsStyleContext *aStyleContext)
730 : {
731 0 : nsStyleAnimation::Value val;
732 : #ifdef DEBUG
733 : bool success =
734 : #endif
735 0 : nsStyleAnimation::ExtractComputedValue(aProperty, aStyleContext, val);
736 0 : NS_ABORT_IF_FALSE(success,
737 : "aProperty must be extractable by nsStyleAnimation");
738 0 : return val.GetColorValue();
739 : }
740 :
741 : struct ColorIndexSet {
742 : PRUint8 colorIndex, alphaIndex;
743 : };
744 :
745 : static const ColorIndexSet gVisitedIndices[2] = { { 0, 0 }, { 1, 0 } };
746 :
747 : nscolor
748 0 : nsStyleContext::GetVisitedDependentColor(nsCSSProperty aProperty)
749 : {
750 0 : NS_ASSERTION(aProperty == eCSSProperty_color ||
751 : aProperty == eCSSProperty_background_color ||
752 : aProperty == eCSSProperty_border_top_color ||
753 : aProperty == eCSSProperty_border_right_color_value ||
754 : aProperty == eCSSProperty_border_bottom_color ||
755 : aProperty == eCSSProperty_border_left_color_value ||
756 : aProperty == eCSSProperty_outline_color ||
757 : aProperty == eCSSProperty__moz_column_rule_color ||
758 : aProperty == eCSSProperty_text_decoration_color ||
759 : aProperty == eCSSProperty_fill ||
760 : aProperty == eCSSProperty_stroke,
761 : "we need to add to nsStyleContext::CalcStyleDifference");
762 :
763 : nscolor colors[2];
764 0 : colors[0] = ExtractColor(aProperty, this);
765 :
766 0 : nsStyleContext *visitedStyle = this->GetStyleIfVisited();
767 0 : if (!visitedStyle) {
768 0 : return colors[0];
769 : }
770 :
771 0 : colors[1] = ExtractColor(aProperty, visitedStyle);
772 :
773 : return nsStyleContext::CombineVisitedColors(colors,
774 0 : this->RelevantLinkVisited());
775 : }
776 :
777 : /* static */ nscolor
778 0 : nsStyleContext::CombineVisitedColors(nscolor *aColors, bool aLinkIsVisited)
779 : {
780 0 : if (NS_GET_A(aColors[1]) == 0) {
781 : // If the style-if-visited is transparent, then just use the
782 : // unvisited style rather than using the (meaningless) color
783 : // components of the visited style along with a potentially
784 : // non-transparent alpha value.
785 0 : aLinkIsVisited = false;
786 : }
787 :
788 : // NOTE: We want this code to have as little timing dependence as
789 : // possible on whether this->RelevantLinkVisited() is true.
790 : const ColorIndexSet &set =
791 0 : gVisitedIndices[aLinkIsVisited ? 1 : 0];
792 :
793 0 : nscolor colorColor = aColors[set.colorIndex];
794 0 : nscolor alphaColor = aColors[set.alphaIndex];
795 0 : return NS_RGBA(NS_GET_R(colorColor), NS_GET_G(colorColor),
796 : NS_GET_B(colorColor), NS_GET_A(alphaColor));
797 : }
798 :
799 : void*
800 0 : nsStyleContext::Alloc(size_t aSize)
801 : {
802 0 : nsIPresShell *shell = PresContext()->PresShell();
803 :
804 0 : aSize += offsetof(AllocationHeader, mStorageStart);
805 : AllocationHeader *alloc =
806 0 : static_cast<AllocationHeader*>(shell->AllocateMisc(aSize));
807 :
808 0 : alloc->mSize = aSize; // NOTE: inflated by header
809 :
810 0 : alloc->mNext = mAllocations;
811 0 : mAllocations = alloc;
812 :
813 0 : return static_cast<void*>(&alloc->mStorageStart);
814 : }
815 :
816 : void
817 0 : nsStyleContext::FreeAllocations(nsPresContext *aPresContext)
818 : {
819 0 : nsIPresShell *shell = aPresContext->PresShell();
820 :
821 0 : for (AllocationHeader *alloc = mAllocations, *next; alloc; alloc = next) {
822 0 : next = alloc->mNext;
823 0 : shell->FreeMisc(alloc->mSize, alloc);
824 : }
825 0 : }
|