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