1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /* DOM object returned from element.getComputedStyle() */
8
9 #include "nsComputedDOMStyle.h"
10
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/FloatingPoint.h"
13 #include "mozilla/FontPropertyTypes.h"
14 #include "mozilla/Preferences.h"
15 #include "mozilla/PresShell.h"
16 #include "mozilla/PresShellInlines.h"
17 #include "mozilla/ScopeExit.h"
18 #include "mozilla/StaticPtr.h"
19 #include "mozilla/StaticPrefs_layout.h"
20
21 #include "nsError.h"
22 #include "nsIFrame.h"
23 #include "nsIFrameInlines.h"
24 #include "mozilla/ComputedStyle.h"
25 #include "nsIScrollableFrame.h"
26 #include "nsContentUtils.h"
27 #include "nsDocShell.h"
28 #include "nsIContent.h"
29 #include "nsStyleConsts.h"
30
31 #include "nsDOMCSSValueList.h"
32 #include "nsFlexContainerFrame.h"
33 #include "nsGridContainerFrame.h"
34 #include "nsGkAtoms.h"
35 #include "mozilla/ReflowInput.h"
36 #include "nsStyleUtil.h"
37 #include "nsStyleStructInlines.h"
38 #include "nsROCSSPrimitiveValue.h"
39
40 #include "nsPresContext.h"
41 #include "mozilla/dom/Document.h"
42
43 #include "nsCSSProps.h"
44 #include "nsCSSPseudoElements.h"
45 #include "mozilla/EffectSet.h"
46 #include "mozilla/IntegerRange.h"
47 #include "mozilla/ServoStyleSet.h"
48 #include "mozilla/RestyleManager.h"
49 #include "mozilla/ViewportFrame.h"
50 #include "nsLayoutUtils.h"
51 #include "nsDisplayList.h"
52 #include "nsDOMCSSDeclaration.h"
53 #include "nsStyleTransformMatrix.h"
54 #include "mozilla/dom/Element.h"
55 #include "mozilla/dom/ElementInlines.h"
56 #include "prtime.h"
57 #include "nsWrapperCacheInlines.h"
58 #include "mozilla/AppUnits.h"
59 #include <algorithm>
60 #include "mozilla/ComputedStyleInlines.h"
61 #include "nsPrintfCString.h"
62
63 using namespace mozilla;
64 using namespace mozilla::dom;
65
66 #if defined(DEBUG_bzbarsky) || defined(DEBUG_caillon)
67 # define DEBUG_ComputedDOMStyle
68 #endif
69
70 /*
71 * This is the implementation of the readonly CSSStyleDeclaration that is
72 * returned by the getComputedStyle() function.
73 */
74
NS_NewComputedDOMStyle(dom::Element * aElement,const nsAString & aPseudoElt,Document * aDocument,nsComputedDOMStyle::StyleType aStyleType)75 already_AddRefed<nsComputedDOMStyle> NS_NewComputedDOMStyle(
76 dom::Element* aElement, const nsAString& aPseudoElt, Document* aDocument,
77 nsComputedDOMStyle::StyleType aStyleType) {
78 RefPtr<nsComputedDOMStyle> computedStyle =
79 new nsComputedDOMStyle(aElement, aPseudoElt, aDocument, aStyleType);
80 return computedStyle.forget();
81 }
82
GetROCSSValueList(bool aCommaDelimited)83 static nsDOMCSSValueList* GetROCSSValueList(bool aCommaDelimited) {
84 return new nsDOMCSSValueList(aCommaDelimited);
85 }
86
GetRenderedElement(const Element * aElement,nsAtom * aPseudo)87 static const Element* GetRenderedElement(const Element* aElement,
88 nsAtom* aPseudo) {
89 if (aPseudo == nsCSSPseudoElements::before()) {
90 return nsLayoutUtils::GetBeforePseudo(aElement);
91 }
92 if (aPseudo == nsCSSPseudoElements::after()) {
93 return nsLayoutUtils::GetAfterPseudo(aElement);
94 }
95 if (aPseudo == nsCSSPseudoElements::marker()) {
96 return nsLayoutUtils::GetMarkerPseudo(aElement);
97 }
98 if (!aPseudo) {
99 return aElement;
100 }
101 return nullptr;
102 }
103
104 // Whether aDocument needs to restyle for aElement
ElementNeedsRestyle(Element * aElement,nsAtom * aPseudo,bool aMayNeedToFlushLayout)105 static bool ElementNeedsRestyle(Element* aElement, nsAtom* aPseudo,
106 bool aMayNeedToFlushLayout) {
107 const Document* doc = aElement->GetComposedDoc();
108 if (!doc) {
109 // If the element is out of the document we don't return styles for it, so
110 // nothing to do.
111 return false;
112 }
113
114 PresShell* presShell = doc->GetPresShell();
115 if (!presShell) {
116 // If there's no pres-shell we'll just either mint a new style from our
117 // caller document, or return no styles, so nothing to do (unless our owner
118 // element needs to get restyled, which could cause us to gain a pres shell,
119 // but the caller checks that).
120 return false;
121 }
122
123 // Unfortunately we don't know if the sheet change affects mElement or not, so
124 // just assume it will and that we need to flush normally.
125 ServoStyleSet* styleSet = presShell->StyleSet();
126 if (styleSet->StyleSheetsHaveChanged()) {
127 return true;
128 }
129
130 nsPresContext* presContext = presShell->GetPresContext();
131 MOZ_ASSERT(presContext);
132
133 // Pending media query updates can definitely change style on the element. For
134 // example, if you change the zoom factor and then call getComputedStyle, you
135 // should be able to observe the style with the new media queries.
136 //
137 // TODO(emilio): Does this need to also check the user font set? (it affects
138 // ch / ex units).
139 if (presContext->HasPendingMediaQueryUpdates()) {
140 // So gotta flush.
141 return true;
142 }
143
144 // If the pseudo-element is animating, make sure to flush.
145 if (aElement->MayHaveAnimations() && aPseudo) {
146 if (aPseudo == nsCSSPseudoElements::before()) {
147 if (EffectSet::GetEffectSet(aElement, PseudoStyleType::before)) {
148 return true;
149 }
150 } else if (aPseudo == nsCSSPseudoElements::after()) {
151 if (EffectSet::GetEffectSet(aElement, PseudoStyleType::after)) {
152 return true;
153 }
154 } else if (aPseudo == nsCSSPseudoElements::marker()) {
155 if (EffectSet::GetEffectSet(aElement, PseudoStyleType::marker)) {
156 return true;
157 }
158 }
159 }
160
161 // For Servo, we need to process the restyle-hint-invalidations first, to
162 // expand LaterSiblings hint, so that we can look whether ancestors need
163 // restyling.
164 RestyleManager* restyleManager = presContext->RestyleManager();
165 restyleManager->ProcessAllPendingAttributeAndStateInvalidations();
166
167 if (!presContext->EffectCompositor()->HasPendingStyleUpdates() &&
168 !doc->GetServoRestyleRoot()) {
169 return false;
170 }
171
172 // If there's a pseudo, we need to prefer that element, as the pseudo itself
173 // may have explicit restyles.
174 const Element* styledElement = GetRenderedElement(aElement, aPseudo);
175 // Try to skip the restyle otherwise.
176 return Servo_HasPendingRestyleAncestor(
177 styledElement ? styledElement : aElement, aMayNeedToFlushLayout);
178 }
179
180 /**
181 * An object that represents the ordered set of properties that are exposed on
182 * an nsComputedDOMStyle object and how their computed values can be obtained.
183 */
184 struct ComputedStyleMap {
185 friend class nsComputedDOMStyle;
186
187 struct Entry {
188 // Create a pointer-to-member-function type.
189 typedef already_AddRefed<CSSValue> (nsComputedDOMStyle::*ComputeMethod)();
190
191 nsCSSPropertyID mProperty;
192 ComputeMethod mGetter;
193
IsEnabledComputedStyleMap::Entry194 bool IsEnabled() const {
195 return nsCSSProps::IsEnabled(mProperty, CSSEnabledState::ForAllContent);
196 }
197 };
198
199 // This generated file includes definition of kEntries which is typed
200 // Entry[] and used below, so this #include has to be put here.
201 #include "nsComputedDOMStyleGenerated.inc"
202
203 /**
204 * Returns the number of properties that should be exposed on an
205 * nsComputedDOMStyle, ecxluding any disabled properties.
206 */
LengthComputedStyleMap207 uint32_t Length() {
208 Update();
209 return mExposedPropertyCount;
210 }
211
212 /**
213 * Returns the property at the given index in the list of properties
214 * that should be exposed on an nsComputedDOMStyle, excluding any
215 * disabled properties.
216 */
PropertyAtComputedStyleMap217 nsCSSPropertyID PropertyAt(uint32_t aIndex) {
218 Update();
219 return kEntries[EntryIndex(aIndex)].mProperty;
220 }
221
222 /**
223 * Searches for and returns the computed style map entry for the given
224 * property, or nullptr if the property is not exposed on nsComputedDOMStyle
225 * or is currently disabled.
226 */
FindEntryForPropertyComputedStyleMap227 const Entry* FindEntryForProperty(nsCSSPropertyID aPropID) {
228 Update();
229 for (uint32_t i = 0; i < mExposedPropertyCount; i++) {
230 const Entry* entry = &kEntries[EntryIndex(i)];
231 if (entry->mProperty == aPropID) {
232 return entry;
233 }
234 }
235 return nullptr;
236 }
237
238 /**
239 * Records that mIndexMap needs updating, due to prefs changing that could
240 * affect the set of properties exposed on an nsComputedDOMStyle.
241 */
MarkDirtyComputedStyleMap242 void MarkDirty() { mExposedPropertyCount = 0; }
243
244 // The member variables are public so that we can use an initializer in
245 // nsComputedDOMStyle::GetComputedStyleMap. Use the member functions
246 // above to get information from this object.
247
248 /**
249 * The number of properties that should be exposed on an nsComputedDOMStyle.
250 * This will be less than eComputedStyleProperty_COUNT if some property
251 * prefs are disabled. A value of 0 indicates that it and mIndexMap are out
252 * of date.
253 */
254 uint32_t mExposedPropertyCount;
255
256 /**
257 * A map of indexes on the nsComputedDOMStyle object to indexes into kEntries.
258 */
259 uint32_t mIndexMap[ArrayLength(kEntries)];
260
261 private:
262 /**
263 * Returns whether mExposedPropertyCount and mIndexMap are out of date.
264 */
IsDirtyComputedStyleMap265 bool IsDirty() { return mExposedPropertyCount == 0; }
266
267 /**
268 * Updates mExposedPropertyCount and mIndexMap to take into account properties
269 * whose prefs are currently disabled.
270 */
271 void Update();
272
273 /**
274 * Maps an nsComputedDOMStyle indexed getter index to an index into kEntries.
275 */
EntryIndexComputedStyleMap276 uint32_t EntryIndex(uint32_t aIndex) const {
277 MOZ_ASSERT(aIndex < mExposedPropertyCount);
278 return mIndexMap[aIndex];
279 }
280 };
281
282 constexpr ComputedStyleMap::Entry
283 ComputedStyleMap::kEntries[ArrayLength(kEntries)];
284
Update()285 void ComputedStyleMap::Update() {
286 if (!IsDirty()) {
287 return;
288 }
289
290 uint32_t index = 0;
291 for (uint32_t i = 0; i < ArrayLength(kEntries); i++) {
292 if (kEntries[i].IsEnabled()) {
293 mIndexMap[index++] = i;
294 }
295 }
296 mExposedPropertyCount = index;
297 }
298
nsComputedDOMStyle(dom::Element * aElement,const nsAString & aPseudoElt,Document * aDocument,StyleType aStyleType)299 nsComputedDOMStyle::nsComputedDOMStyle(dom::Element* aElement,
300 const nsAString& aPseudoElt,
301 Document* aDocument,
302 StyleType aStyleType)
303 : mDocumentWeak(nullptr),
304 mOuterFrame(nullptr),
305 mInnerFrame(nullptr),
306 mPresShell(nullptr),
307 mStyleType(aStyleType),
308 mExposeVisitedStyle(false),
309 mResolvedComputedStyle(false) {
310 MOZ_ASSERT(aElement);
311 MOZ_ASSERT(aDocument);
312 // TODO(emilio, bug 548397, https://github.com/w3c/csswg-drafts/issues/2403):
313 // Should use aElement->OwnerDoc() instead.
314 mDocumentWeak = do_GetWeakReference(aDocument);
315 mElement = aElement;
316 mPseudo = nsCSSPseudoElements::GetPseudoAtom(aPseudoElt);
317 }
318
~nsComputedDOMStyle()319 nsComputedDOMStyle::~nsComputedDOMStyle() {
320 MOZ_ASSERT(!mResolvedComputedStyle,
321 "Should have called ClearComputedStyle() during last release.");
322 }
323
324 NS_IMPL_CYCLE_COLLECTION_CLASS(nsComputedDOMStyle)
325
326 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsComputedDOMStyle)
327 tmp->ClearComputedStyle(); // remove observer before clearing mElement
328 NS_IMPL_CYCLE_COLLECTION_UNLINK(mElement)
329 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
330 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
331
332 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsComputedDOMStyle)
333 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement)
334 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
335
336 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsComputedDOMStyle)
337
338 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsComputedDOMStyle)
339 return tmp->HasKnownLiveWrapper();
340 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
341
342 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsComputedDOMStyle)
343 return tmp->HasKnownLiveWrapper();
344 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
345
346 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsComputedDOMStyle)
347 return tmp->HasKnownLiveWrapper();
348 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
349
350 // QueryInterface implementation for nsComputedDOMStyle
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsComputedDOMStyle)351 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsComputedDOMStyle)
352 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
353 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
354 NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration)
355
356 NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(nsComputedDOMStyle)
357 NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(
358 nsComputedDOMStyle, ClearComputedStyle())
359
360 nsresult nsComputedDOMStyle::GetPropertyValue(const nsCSSPropertyID aPropID,
361 nsACString& aValue) {
362 return GetPropertyValue(aPropID, EmptyCString(), aValue);
363 }
364
SetPropertyValue(const nsCSSPropertyID aPropID,const nsACString & aValue,nsIPrincipal * aSubjectPrincipal,ErrorResult & aRv)365 void nsComputedDOMStyle::SetPropertyValue(const nsCSSPropertyID aPropID,
366 const nsACString& aValue,
367 nsIPrincipal* aSubjectPrincipal,
368 ErrorResult& aRv) {
369 aRv.ThrowNoModificationAllowedError(nsPrintfCString(
370 "Can't set value for property '%s' in computed style",
371 PromiseFlatCString(nsCSSProps::GetStringValue(aPropID)).get()));
372 }
373
GetCssText(nsACString & aCssText)374 void nsComputedDOMStyle::GetCssText(nsACString& aCssText) {
375 aCssText.Truncate();
376 }
377
SetCssText(const nsACString & aCssText,nsIPrincipal * aSubjectPrincipal,ErrorResult & aRv)378 void nsComputedDOMStyle::SetCssText(const nsACString& aCssText,
379 nsIPrincipal* aSubjectPrincipal,
380 ErrorResult& aRv) {
381 aRv.ThrowNoModificationAllowedError("Can't set cssText on computed style");
382 }
383
Length()384 uint32_t nsComputedDOMStyle::Length() {
385 // Make sure we have up to date style so that we can include custom
386 // properties.
387 UpdateCurrentStyleSources(eCSSPropertyExtra_variable);
388 if (!mComputedStyle) {
389 return 0;
390 }
391
392 uint32_t length = GetComputedStyleMap()->Length() +
393 Servo_GetCustomPropertiesCount(mComputedStyle);
394
395 ClearCurrentStyleSources();
396
397 return length;
398 }
399
GetParentRule()400 css::Rule* nsComputedDOMStyle::GetParentRule() { return nullptr; }
401
402 NS_IMETHODIMP
GetPropertyValue(const nsACString & aPropertyName,nsACString & aReturn)403 nsComputedDOMStyle::GetPropertyValue(const nsACString& aPropertyName,
404 nsACString& aReturn) {
405 nsCSSPropertyID prop = nsCSSProps::LookupProperty(aPropertyName);
406 return GetPropertyValue(prop, aPropertyName, aReturn);
407 }
408
GetPropertyValue(nsCSSPropertyID aPropID,const nsACString & aMaybeCustomPropertyName,nsACString & aReturn)409 nsresult nsComputedDOMStyle::GetPropertyValue(
410 nsCSSPropertyID aPropID, const nsACString& aMaybeCustomPropertyName,
411 nsACString& aReturn) {
412 MOZ_ASSERT(aReturn.IsEmpty());
413
414 const ComputedStyleMap::Entry* entry = nullptr;
415 if (aPropID != eCSSPropertyExtra_variable) {
416 entry = GetComputedStyleMap()->FindEntryForProperty(aPropID);
417 if (!entry) {
418 return NS_OK;
419 }
420 }
421
422 UpdateCurrentStyleSources(aPropID);
423 if (!mComputedStyle) {
424 return NS_OK;
425 }
426
427 auto cleanup = mozilla::MakeScopeExit([&] { ClearCurrentStyleSources(); });
428
429 if (!entry) {
430 MOZ_ASSERT(nsCSSProps::IsCustomPropertyName(aMaybeCustomPropertyName));
431 const nsACString& name =
432 Substring(aMaybeCustomPropertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH);
433 Servo_GetCustomPropertyValue(mComputedStyle, &name, &aReturn);
434 return NS_OK;
435 }
436
437 if (nsCSSProps::PropHasFlags(aPropID, CSSPropFlags::IsLogical)) {
438 MOZ_ASSERT(entry);
439 MOZ_ASSERT(entry->mGetter == &nsComputedDOMStyle::DummyGetter);
440
441 DebugOnly<nsCSSPropertyID> logicalProp = aPropID;
442
443 aPropID = Servo_ResolveLogicalProperty(aPropID, mComputedStyle);
444 entry = GetComputedStyleMap()->FindEntryForProperty(aPropID);
445
446 MOZ_ASSERT(NeedsToFlushLayout(logicalProp) == NeedsToFlushLayout(aPropID),
447 "Logical and physical property don't agree on whether layout is "
448 "needed");
449 }
450
451 if (!nsCSSProps::PropHasFlags(aPropID, CSSPropFlags::SerializedByServo)) {
452 if (RefPtr<CSSValue> value = (this->*entry->mGetter)()) {
453 ErrorResult rv;
454 nsAutoString text;
455 value->GetCssText(text, rv);
456 CopyUTF16toUTF8(text, aReturn);
457 return rv.StealNSResult();
458 }
459 return NS_OK;
460 }
461
462 MOZ_ASSERT(entry->mGetter == &nsComputedDOMStyle::DummyGetter);
463 mComputedStyle->GetComputedPropertyValue(aPropID, aReturn);
464 return NS_OK;
465 }
466
467 /* static */
GetComputedStyle(Element * aElement,nsAtom * aPseudo,StyleType aStyleType)468 already_AddRefed<ComputedStyle> nsComputedDOMStyle::GetComputedStyle(
469 Element* aElement, nsAtom* aPseudo, StyleType aStyleType) {
470 if (Document* doc = aElement->GetComposedDoc()) {
471 doc->FlushPendingNotifications(FlushType::Style);
472 }
473 return GetComputedStyleNoFlush(aElement, aPseudo, aStyleType);
474 }
475
476 /**
477 * The following function checks whether we need to explicitly resolve the style
478 * again, even though we have a style coming from the frame.
479 *
480 * This basically checks whether the style is or may be under a ::first-line or
481 * ::first-letter frame, in which case we can't return the frame style, and we
482 * need to resolve it. See bug 505515.
483 */
MustReresolveStyle(const mozilla::ComputedStyle * aStyle)484 static bool MustReresolveStyle(const mozilla::ComputedStyle* aStyle) {
485 MOZ_ASSERT(aStyle);
486
487 // TODO(emilio): We may want to avoid re-resolving pseudo-element styles
488 // more often.
489 return aStyle->HasPseudoElementData() && !aStyle->IsPseudoElement();
490 }
491
GetPseudoType(nsAtom * aPseudo)492 static inline PseudoStyleType GetPseudoType(nsAtom* aPseudo) {
493 if (!aPseudo) {
494 return PseudoStyleType::NotPseudo;
495 }
496 return nsCSSPseudoElements::GetPseudoType(aPseudo,
497 CSSEnabledState::ForAllContent);
498 }
499
DoGetComputedStyleNoFlush(const Element * aElement,nsAtom * aPseudo,PresShell * aPresShell,StyleType aStyleType)500 already_AddRefed<ComputedStyle> nsComputedDOMStyle::DoGetComputedStyleNoFlush(
501 const Element* aElement, nsAtom* aPseudo, PresShell* aPresShell,
502 StyleType aStyleType) {
503 MOZ_ASSERT(aElement, "NULL element");
504
505 // If the content has a pres shell, we must use it. Otherwise we'd
506 // potentially mix rule trees by using the wrong pres shell's style
507 // set. Using the pres shell from the content also means that any
508 // content that's actually *in* a document will get the style from the
509 // correct document.
510 PresShell* presShell = nsContentUtils::GetPresShellForContent(aElement);
511 bool inDocWithShell = true;
512 if (!presShell) {
513 inDocWithShell = false;
514 presShell = aPresShell;
515 if (!presShell) {
516 return nullptr;
517 }
518 }
519
520 PseudoStyleType pseudoType = GetPseudoType(aPseudo);
521 if (aPseudo && !PseudoStyle::IsPseudoElement(pseudoType)) {
522 return nullptr;
523 }
524
525 if (!aElement->IsInComposedDoc()) {
526 // Don't return styles for disconnected elements, that makes no sense. This
527 // can only happen with a non-null presShell for cross-document calls.
528 //
529 // FIXME(emilio, bug 1483798): This should also not return styles for
530 // elements outside of the flat tree, not just outside of the document.
531 return nullptr;
532 }
533
534 // XXX the !aElement->IsHTMLElement(nsGkAtoms::area)
535 // check is needed due to bug 135040 (to avoid using
536 // mPrimaryFrame). Remove it once that's fixed.
537 if (inDocWithShell && aStyleType == eAll &&
538 !aElement->IsHTMLElement(nsGkAtoms::area)) {
539 if (const Element* element = GetRenderedElement(aElement, aPseudo)) {
540 if (nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(element)) {
541 ComputedStyle* result = styleFrame->Style();
542 // Don't use the style if it was influenced by pseudo-elements,
543 // since then it's not the primary style for this element / pseudo.
544 if (!MustReresolveStyle(result)) {
545 RefPtr<ComputedStyle> ret = result;
546 return ret.forget();
547 }
548 }
549 }
550 }
551
552 // No frame has been created, or we have a pseudo, or we're looking
553 // for the default style, so resolve the style ourselves.
554 ServoStyleSet* styleSet = presShell->StyleSet();
555
556 StyleRuleInclusion rules = aStyleType == eDefaultOnly
557 ? StyleRuleInclusion::DefaultOnly
558 : StyleRuleInclusion::All;
559 RefPtr<ComputedStyle> result =
560 styleSet->ResolveStyleLazily(*aElement, pseudoType, rules);
561 return result.forget();
562 }
563
564 already_AddRefed<ComputedStyle>
GetUnanimatedComputedStyleNoFlush(Element * aElement,nsAtom * aPseudo)565 nsComputedDOMStyle::GetUnanimatedComputedStyleNoFlush(Element* aElement,
566 nsAtom* aPseudo) {
567 RefPtr<ComputedStyle> style = GetComputedStyleNoFlush(aElement, aPseudo);
568 if (!style) {
569 return nullptr;
570 }
571
572 PseudoStyleType pseudoType = GetPseudoType(aPseudo);
573 PresShell* presShell = aElement->OwnerDoc()->GetPresShell();
574 MOZ_ASSERT(presShell,
575 "How in the world did we get a style a few lines above?");
576
577 Element* elementOrPseudoElement =
578 EffectCompositor::GetElementToRestyle(aElement, pseudoType);
579 if (!elementOrPseudoElement) {
580 return nullptr;
581 }
582
583 return presShell->StyleSet()->GetBaseContextForElement(elementOrPseudoElement,
584 style);
585 }
586
GetAdjustedValuesForBoxSizing()587 nsMargin nsComputedDOMStyle::GetAdjustedValuesForBoxSizing() {
588 // We want the width/height of whatever parts 'width' or 'height' controls,
589 // which can be different depending on the value of the 'box-sizing' property.
590 const nsStylePosition* stylePos = StylePosition();
591
592 nsMargin adjustment;
593 if (stylePos->mBoxSizing == StyleBoxSizing::Border) {
594 adjustment = mInnerFrame->GetUsedBorderAndPadding();
595 }
596
597 return adjustment;
598 }
599
AddImageURL(nsIURI & aURI,nsTArray<nsCString> & aURLs)600 static void AddImageURL(nsIURI& aURI, nsTArray<nsCString>& aURLs) {
601 nsCString spec;
602 nsresult rv = aURI.GetSpec(spec);
603 if (NS_FAILED(rv)) {
604 return;
605 }
606
607 aURLs.AppendElement(std::move(spec));
608 }
609
AddImageURL(const StyleComputedUrl & aURL,nsTArray<nsCString> & aURLs)610 static void AddImageURL(const StyleComputedUrl& aURL,
611 nsTArray<nsCString>& aURLs) {
612 if (aURL.IsLocalRef()) {
613 return;
614 }
615
616 if (nsIURI* uri = aURL.GetURI()) {
617 AddImageURL(*uri, aURLs);
618 }
619 }
620
AddImageURL(const StyleImage & aImage,nsTArray<nsCString> & aURLs)621 static void AddImageURL(const StyleImage& aImage, nsTArray<nsCString>& aURLs) {
622 if (auto* urlValue = aImage.GetImageRequestURLValue()) {
623 AddImageURL(*urlValue, aURLs);
624 }
625 }
626
AddImageURL(const StyleShapeOutside & aShapeOutside,nsTArray<nsCString> & aURLs)627 static void AddImageURL(const StyleShapeOutside& aShapeOutside,
628 nsTArray<nsCString>& aURLs) {
629 if (aShapeOutside.IsImage()) {
630 AddImageURL(aShapeOutside.AsImage(), aURLs);
631 }
632 }
633
AddImageURL(const StyleClipPath & aClipPath,nsTArray<nsCString> & aURLs)634 static void AddImageURL(const StyleClipPath& aClipPath,
635 nsTArray<nsCString>& aURLs) {
636 if (aClipPath.IsUrl()) {
637 AddImageURL(aClipPath.AsUrl(), aURLs);
638 }
639 }
640
AddImageURLs(const nsStyleImageLayers & aLayers,nsTArray<nsCString> & aURLs)641 static void AddImageURLs(const nsStyleImageLayers& aLayers,
642 nsTArray<nsCString>& aURLs) {
643 for (auto i : IntegerRange(aLayers.mLayers.Length())) {
644 AddImageURL(aLayers.mLayers[i].mImage, aURLs);
645 }
646 }
647
CollectImageURLsForProperty(nsCSSPropertyID aProp,const ComputedStyle & aStyle,nsTArray<nsCString> & aURLs)648 static void CollectImageURLsForProperty(nsCSSPropertyID aProp,
649 const ComputedStyle& aStyle,
650 nsTArray<nsCString>& aURLs) {
651 if (nsCSSProps::IsShorthand(aProp)) {
652 CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProp,
653 CSSEnabledState::ForAllContent) {
654 CollectImageURLsForProperty(*p, aStyle, aURLs);
655 }
656 return;
657 }
658
659 switch (aProp) {
660 case eCSSProperty_cursor:
661 for (auto& image : aStyle.StyleUI()->mCursor.images.AsSpan()) {
662 AddImageURL(image.image, aURLs);
663 }
664 break;
665 case eCSSProperty_background_image:
666 AddImageURLs(aStyle.StyleBackground()->mImage, aURLs);
667 break;
668 case eCSSProperty_mask_clip:
669 AddImageURLs(aStyle.StyleSVGReset()->mMask, aURLs);
670 break;
671 case eCSSProperty_list_style_image: {
672 const auto& image = aStyle.StyleList()->mListStyleImage;
673 if (image.IsUrl()) {
674 AddImageURL(image.AsUrl(), aURLs);
675 }
676 break;
677 }
678 case eCSSProperty_border_image_source:
679 AddImageURL(aStyle.StyleBorder()->mBorderImageSource, aURLs);
680 break;
681 case eCSSProperty_clip_path:
682 AddImageURL(aStyle.StyleSVGReset()->mClipPath, aURLs);
683 break;
684 case eCSSProperty_shape_outside:
685 AddImageURL(aStyle.StyleDisplay()->mShapeOutside, aURLs);
686 break;
687 default:
688 break;
689 }
690 }
691
GetCSSImageURLs(const nsACString & aPropertyName,nsTArray<nsCString> & aImageURLs,mozilla::ErrorResult & aRv)692 void nsComputedDOMStyle::GetCSSImageURLs(const nsACString& aPropertyName,
693 nsTArray<nsCString>& aImageURLs,
694 mozilla::ErrorResult& aRv) {
695 nsCSSPropertyID prop = nsCSSProps::LookupProperty(aPropertyName);
696 if (prop == eCSSProperty_UNKNOWN) {
697 // Note: not using nsPrintfCString here in case aPropertyName contains
698 // nulls.
699 aRv.ThrowSyntaxError("Invalid property name '"_ns + aPropertyName + "'"_ns);
700 return;
701 }
702
703 UpdateCurrentStyleSources(prop);
704
705 if (!mComputedStyle) {
706 return;
707 }
708
709 CollectImageURLsForProperty(prop, *mComputedStyle, aImageURLs);
710 ClearCurrentStyleSources();
711 }
712
713 // nsDOMCSSDeclaration abstract methods which should never be called
714 // on a nsComputedDOMStyle object, but must be defined to avoid
715 // compile errors.
GetOrCreateCSSDeclaration(Operation aOperation,DeclarationBlock ** aCreated)716 DeclarationBlock* nsComputedDOMStyle::GetOrCreateCSSDeclaration(
717 Operation aOperation, DeclarationBlock** aCreated) {
718 MOZ_CRASH("called nsComputedDOMStyle::GetCSSDeclaration");
719 }
720
SetCSSDeclaration(DeclarationBlock *,MutationClosureData *)721 nsresult nsComputedDOMStyle::SetCSSDeclaration(DeclarationBlock*,
722 MutationClosureData*) {
723 MOZ_CRASH("called nsComputedDOMStyle::SetCSSDeclaration");
724 }
725
DocToUpdate()726 Document* nsComputedDOMStyle::DocToUpdate() {
727 MOZ_CRASH("called nsComputedDOMStyle::DocToUpdate");
728 }
729
730 nsDOMCSSDeclaration::ParsingEnvironment
GetParsingEnvironment(nsIPrincipal * aSubjectPrincipal) const731 nsComputedDOMStyle::GetParsingEnvironment(
732 nsIPrincipal* aSubjectPrincipal) const {
733 MOZ_CRASH("called nsComputedDOMStyle::GetParsingEnvironment");
734 }
735
ClearComputedStyle()736 void nsComputedDOMStyle::ClearComputedStyle() {
737 if (mResolvedComputedStyle) {
738 mResolvedComputedStyle = false;
739 mElement->RemoveMutationObserver(this);
740 }
741 mComputedStyle = nullptr;
742 }
743
SetResolvedComputedStyle(RefPtr<ComputedStyle> && aContext,uint64_t aGeneration)744 void nsComputedDOMStyle::SetResolvedComputedStyle(
745 RefPtr<ComputedStyle>&& aContext, uint64_t aGeneration) {
746 if (!mResolvedComputedStyle) {
747 mResolvedComputedStyle = true;
748 mElement->AddMutationObserver(this);
749 }
750 mComputedStyle = aContext;
751 mComputedStyleGeneration = aGeneration;
752 mPresShellId = mPresShell->GetPresShellId();
753 }
754
SetFrameComputedStyle(mozilla::ComputedStyle * aStyle,uint64_t aGeneration)755 void nsComputedDOMStyle::SetFrameComputedStyle(mozilla::ComputedStyle* aStyle,
756 uint64_t aGeneration) {
757 ClearComputedStyle();
758 mComputedStyle = aStyle;
759 mComputedStyleGeneration = aGeneration;
760 mPresShellId = mPresShell->GetPresShellId();
761 }
762
MayNeedToFlushLayout(nsCSSPropertyID aPropID)763 static bool MayNeedToFlushLayout(nsCSSPropertyID aPropID) {
764 switch (aPropID) {
765 case eCSSProperty_width:
766 case eCSSProperty_height:
767 case eCSSProperty_block_size:
768 case eCSSProperty_inline_size:
769 case eCSSProperty_line_height:
770 case eCSSProperty_grid_template_rows:
771 case eCSSProperty_grid_template_columns:
772 case eCSSProperty_perspective_origin:
773 case eCSSProperty_transform_origin:
774 case eCSSProperty_transform:
775 case eCSSProperty_border_top_width:
776 case eCSSProperty_border_bottom_width:
777 case eCSSProperty_border_left_width:
778 case eCSSProperty_border_right_width:
779 case eCSSProperty_border_block_start_width:
780 case eCSSProperty_border_block_end_width:
781 case eCSSProperty_border_inline_start_width:
782 case eCSSProperty_border_inline_end_width:
783 case eCSSProperty_top:
784 case eCSSProperty_right:
785 case eCSSProperty_bottom:
786 case eCSSProperty_left:
787 case eCSSProperty_inset_block_start:
788 case eCSSProperty_inset_block_end:
789 case eCSSProperty_inset_inline_start:
790 case eCSSProperty_inset_inline_end:
791 case eCSSProperty_padding_top:
792 case eCSSProperty_padding_right:
793 case eCSSProperty_padding_bottom:
794 case eCSSProperty_padding_left:
795 case eCSSProperty_padding_block_start:
796 case eCSSProperty_padding_block_end:
797 case eCSSProperty_padding_inline_start:
798 case eCSSProperty_padding_inline_end:
799 case eCSSProperty_margin_top:
800 case eCSSProperty_margin_right:
801 case eCSSProperty_margin_bottom:
802 case eCSSProperty_margin_left:
803 case eCSSProperty_margin_block_start:
804 case eCSSProperty_margin_block_end:
805 case eCSSProperty_margin_inline_start:
806 case eCSSProperty_margin_inline_end:
807 return true;
808 default:
809 return false;
810 }
811 }
812
NeedsToFlushStyle(nsCSSPropertyID aPropID) const813 bool nsComputedDOMStyle::NeedsToFlushStyle(nsCSSPropertyID aPropID) const {
814 bool mayNeedToFlushLayout = MayNeedToFlushLayout(aPropID);
815
816 // We always compute styles from the element's owner document.
817 if (ElementNeedsRestyle(mElement, mPseudo, mayNeedToFlushLayout)) {
818 return true;
819 }
820
821 Document* doc = mElement->OwnerDoc();
822 // If parent document is there, also needs to check if there is some change
823 // that needs to flush this document (e.g. size change for iframe).
824 while (doc->StyleOrLayoutObservablyDependsOnParentDocumentLayout()) {
825 if (Element* element = doc->GetEmbedderElement()) {
826 if (ElementNeedsRestyle(element, nullptr, mayNeedToFlushLayout)) {
827 return true;
828 }
829 }
830
831 doc = doc->GetInProcessParentDocument();
832 }
833
834 return false;
835 }
836
IsNonReplacedInline(nsIFrame * aFrame)837 static bool IsNonReplacedInline(nsIFrame* aFrame) {
838 // FIXME: this should be IsInlineInsideStyle() since width/height
839 // doesn't apply to ruby boxes.
840 return aFrame->StyleDisplay()->IsInlineFlow() &&
841 !aFrame->IsFrameOfType(nsIFrame::eReplaced) &&
842 !aFrame->IsFieldSetFrame() && !aFrame->IsBlockFrame() &&
843 !aFrame->IsScrollFrame() && !aFrame->IsColumnSetWrapperFrame();
844 }
845
SideForPaddingOrMarginOrInsetProperty(nsCSSPropertyID aPropID)846 static Side SideForPaddingOrMarginOrInsetProperty(nsCSSPropertyID aPropID) {
847 switch (aPropID) {
848 case eCSSProperty_top:
849 case eCSSProperty_margin_top:
850 case eCSSProperty_padding_top:
851 return eSideTop;
852 case eCSSProperty_right:
853 case eCSSProperty_margin_right:
854 case eCSSProperty_padding_right:
855 return eSideRight;
856 case eCSSProperty_bottom:
857 case eCSSProperty_margin_bottom:
858 case eCSSProperty_padding_bottom:
859 return eSideBottom;
860 case eCSSProperty_left:
861 case eCSSProperty_margin_left:
862 case eCSSProperty_padding_left:
863 return eSideLeft;
864 default:
865 MOZ_ASSERT_UNREACHABLE("Unexpected property");
866 return eSideTop;
867 }
868 }
869
PaddingNeedsUsedValue(const LengthPercentage & aValue,const ComputedStyle & aStyle)870 static bool PaddingNeedsUsedValue(const LengthPercentage& aValue,
871 const ComputedStyle& aStyle) {
872 return !aValue.ConvertsToLength() || aStyle.StyleDisplay()->HasAppearance();
873 }
874
NeedsToFlushLayout(nsCSSPropertyID aPropID) const875 bool nsComputedDOMStyle::NeedsToFlushLayout(nsCSSPropertyID aPropID) const {
876 MOZ_ASSERT(aPropID != eCSSProperty_UNKNOWN);
877 if (aPropID == eCSSPropertyExtra_variable) {
878 return false;
879 }
880 nsIFrame* outerFrame = GetOuterFrame();
881 if (!outerFrame) {
882 return false;
883 }
884 nsIFrame* frame = nsLayoutUtils::GetStyleFrame(outerFrame);
885 auto* style = frame->Style();
886 if (nsCSSProps::PropHasFlags(aPropID, CSSPropFlags::IsLogical)) {
887 aPropID = Servo_ResolveLogicalProperty(aPropID, style);
888 }
889
890 switch (aPropID) {
891 case eCSSProperty_width:
892 case eCSSProperty_height:
893 return !IsNonReplacedInline(frame);
894 case eCSSProperty_line_height:
895 return frame->StyleText()->mLineHeight.IsMozBlockHeight();
896 case eCSSProperty_grid_template_rows:
897 case eCSSProperty_grid_template_columns:
898 return !!nsGridContainerFrame::GetGridContainerFrame(frame);
899 case eCSSProperty_perspective_origin:
900 return style->StyleDisplay()->mPerspectiveOrigin.HasPercent();
901 case eCSSProperty_transform_origin:
902 return style->StyleDisplay()->mTransformOrigin.HasPercent();
903 case eCSSProperty_transform:
904 return style->StyleDisplay()->mTransform.HasPercent();
905 case eCSSProperty_border_top_width:
906 case eCSSProperty_border_bottom_width:
907 case eCSSProperty_border_left_width:
908 case eCSSProperty_border_right_width:
909 // FIXME(emilio): This should return false per spec (bug 1551000), but
910 // themed borders don't make that easy, so for now flush for that case.
911 //
912 // TODO(emilio): If we make GetUsedBorder() stop returning 0 for an
913 // unreflowed frame, or something of that sort, then we can stop flushing
914 // layout for themed frames.
915 return style->StyleDisplay()->HasAppearance();
916 case eCSSProperty_top:
917 case eCSSProperty_right:
918 case eCSSProperty_bottom:
919 case eCSSProperty_left:
920 // Doing better than this is actually hard.
921 return style->StyleDisplay()->mPosition != StylePositionProperty::Static;
922 case eCSSProperty_padding_top:
923 case eCSSProperty_padding_right:
924 case eCSSProperty_padding_bottom:
925 case eCSSProperty_padding_left: {
926 Side side = SideForPaddingOrMarginOrInsetProperty(aPropID);
927 // Theming can override used padding, sigh.
928 //
929 // TODO(emilio): If we make GetUsedPadding() stop returning 0 for an
930 // unreflowed frame, or something of that sort, then we can stop flushing
931 // layout for themed frames.
932 return PaddingNeedsUsedValue(style->StylePadding()->mPadding.Get(side),
933 *style);
934 }
935 case eCSSProperty_margin_top:
936 case eCSSProperty_margin_right:
937 case eCSSProperty_margin_bottom:
938 case eCSSProperty_margin_left: {
939 // NOTE(emilio): This is dubious, but matches other browsers.
940 // See https://github.com/w3c/csswg-drafts/issues/2328
941 Side side = SideForPaddingOrMarginOrInsetProperty(aPropID);
942 return !style->StyleMargin()->mMargin.Get(side).ConvertsToLength();
943 }
944 default:
945 return false;
946 }
947 }
948
Flush(Document & aDocument,FlushType aFlushType)949 void nsComputedDOMStyle::Flush(Document& aDocument, FlushType aFlushType) {
950 MOZ_ASSERT(mElement->IsInComposedDoc());
951
952 #ifdef DEBUG
953 {
954 nsCOMPtr<Document> document = do_QueryReferent(mDocumentWeak);
955 MOZ_ASSERT(document == &aDocument);
956 }
957 #endif
958
959 aDocument.FlushPendingNotifications(aFlushType);
960 if (MOZ_UNLIKELY(&aDocument != mElement->OwnerDoc())) {
961 mElement->OwnerDoc()->FlushPendingNotifications(aFlushType);
962 }
963 }
964
GetOuterFrame() const965 nsIFrame* nsComputedDOMStyle::GetOuterFrame() const {
966 if (!mPseudo) {
967 return mElement->GetPrimaryFrame();
968 }
969 nsAtom* property = nullptr;
970 if (mPseudo == nsCSSPseudoElements::before()) {
971 property = nsGkAtoms::beforePseudoProperty;
972 } else if (mPseudo == nsCSSPseudoElements::after()) {
973 property = nsGkAtoms::afterPseudoProperty;
974 } else if (mPseudo == nsCSSPseudoElements::marker()) {
975 property = nsGkAtoms::markerPseudoProperty;
976 }
977 if (!property) {
978 return nullptr;
979 }
980 auto* pseudo = static_cast<Element*>(mElement->GetProperty(property));
981 return pseudo ? pseudo->GetPrimaryFrame() : nullptr;
982 }
983
UpdateCurrentStyleSources(nsCSSPropertyID aPropID)984 void nsComputedDOMStyle::UpdateCurrentStyleSources(nsCSSPropertyID aPropID) {
985 nsCOMPtr<Document> document = do_QueryReferent(mDocumentWeak);
986 if (!document) {
987 ClearComputedStyle();
988 return;
989 }
990
991 // We don't return styles for disconnected elements anymore, so don't go
992 // through the trouble of flushing or what not.
993 //
994 // TODO(emilio): We may want to return earlier for elements outside of the
995 // flat tree too: https://github.com/w3c/csswg-drafts/issues/1964
996 if (!mElement->IsInComposedDoc()) {
997 ClearComputedStyle();
998 return;
999 }
1000
1001 DebugOnly<bool> didFlush = false;
1002 if (NeedsToFlushStyle(aPropID)) {
1003 didFlush = true;
1004 // We look at the frame in NeedsToFlushLayout, so flush frames, not only
1005 // styles.
1006 Flush(*document, FlushType::Frames);
1007 }
1008
1009 if (NeedsToFlushLayout(aPropID)) {
1010 MOZ_ASSERT(MayNeedToFlushLayout(aPropID));
1011 didFlush = true;
1012 Flush(*document, FlushType::Layout);
1013 #ifdef DEBUG
1014 mFlushedPendingReflows = true;
1015 #endif
1016 } else {
1017 #ifdef DEBUG
1018 mFlushedPendingReflows = false;
1019 #endif
1020 }
1021
1022 mPresShell = document->GetPresShell();
1023 if (!mPresShell || !mPresShell->GetPresContext()) {
1024 ClearComputedStyle();
1025 return;
1026 }
1027
1028 // We need to use GetUndisplayedRestyleGeneration instead of
1029 // GetRestyleGeneration, because the caching of mComputedStyle is an
1030 // optimization that is useful only for displayed elements.
1031 // For undisplayed elements we need to take into account any DOM changes that
1032 // might cause a restyle, because Servo will not increase the generation for
1033 // undisplayed elements.
1034 // As for Gecko, GetUndisplayedRestyleGeneration is effectively equal to
1035 // GetRestyleGeneration, since the generation is incremented whenever we
1036 // process restyles.
1037 uint64_t currentGeneration =
1038 mPresShell->GetPresContext()->GetUndisplayedRestyleGeneration();
1039
1040 if (mComputedStyle && mComputedStyleGeneration == currentGeneration &&
1041 mPresShellId == mPresShell->GetPresShellId()) {
1042 // Our cached style is still valid.
1043 return;
1044 }
1045
1046 mComputedStyle = nullptr;
1047
1048 // XXX the !mElement->IsHTMLElement(nsGkAtoms::area)
1049 // check is needed due to bug 135040 (to avoid using
1050 // mPrimaryFrame). Remove it once that's fixed.
1051 if (mStyleType == eAll && !mElement->IsHTMLElement(nsGkAtoms::area)) {
1052 mOuterFrame = GetOuterFrame();
1053 mInnerFrame = mOuterFrame;
1054 if (mOuterFrame) {
1055 mInnerFrame = nsLayoutUtils::GetStyleFrame(mOuterFrame);
1056 SetFrameComputedStyle(mInnerFrame->Style(), currentGeneration);
1057 NS_ASSERTION(mComputedStyle, "Frame without style?");
1058 }
1059 }
1060
1061 if (!mComputedStyle || MustReresolveStyle(mComputedStyle)) {
1062 PresShell* presShellForContent = mElement->OwnerDoc()->GetPresShell();
1063 // Need to resolve a style.
1064 RefPtr<ComputedStyle> resolvedComputedStyle = DoGetComputedStyleNoFlush(
1065 mElement, mPseudo,
1066 presShellForContent ? presShellForContent : mPresShell, mStyleType);
1067 if (!resolvedComputedStyle) {
1068 ClearComputedStyle();
1069 return;
1070 }
1071
1072 // No need to re-get the generation, even though GetComputedStyle
1073 // will flush, since we flushed style at the top of this function.
1074 // We don't need to check this if we only flushed the parent.
1075 NS_ASSERTION(
1076 !didFlush ||
1077 currentGeneration ==
1078 mPresShell->GetPresContext()->GetUndisplayedRestyleGeneration(),
1079 "why should we have flushed style again?");
1080
1081 SetResolvedComputedStyle(std::move(resolvedComputedStyle),
1082 currentGeneration);
1083 NS_ASSERTION(mPseudo || !mComputedStyle->HasPseudoElementData(),
1084 "should not have pseudo-element data");
1085 }
1086
1087 // mExposeVisitedStyle is set to true only by testing APIs that
1088 // require chrome privilege.
1089 MOZ_ASSERT(!mExposeVisitedStyle || nsContentUtils::IsCallerChrome(),
1090 "mExposeVisitedStyle set incorrectly");
1091 if (mExposeVisitedStyle && mComputedStyle->RelevantLinkVisited()) {
1092 if (ComputedStyle* styleIfVisited = mComputedStyle->GetStyleIfVisited()) {
1093 mComputedStyle = styleIfVisited;
1094 }
1095 }
1096 }
1097
ClearCurrentStyleSources()1098 void nsComputedDOMStyle::ClearCurrentStyleSources() {
1099 // Release the current style if we got it off the frame.
1100 //
1101 // For a style we resolved, keep it around so that we can re-use it next time
1102 // this object is queried, but not if it-s a re-resolved style because we were
1103 // inside a pseudo-element.
1104 if (!mResolvedComputedStyle || mOuterFrame) {
1105 ClearComputedStyle();
1106 }
1107
1108 mOuterFrame = nullptr;
1109 mInnerFrame = nullptr;
1110 mPresShell = nullptr;
1111 }
1112
RemoveProperty(const nsACString & aPropertyName,nsACString & aReturn,ErrorResult & aRv)1113 void nsComputedDOMStyle::RemoveProperty(const nsACString& aPropertyName,
1114 nsACString& aReturn, ErrorResult& aRv) {
1115 // Note: not using nsPrintfCString here in case aPropertyName contains
1116 // nulls.
1117 aRv.ThrowNoModificationAllowedError("Can't remove property '"_ns +
1118 aPropertyName +
1119 "' from computed style"_ns);
1120 }
1121
GetPropertyPriority(const nsACString & aPropertyName,nsACString & aReturn)1122 void nsComputedDOMStyle::GetPropertyPriority(const nsACString& aPropertyName,
1123 nsACString& aReturn) {
1124 aReturn.Truncate();
1125 }
1126
SetProperty(const nsACString & aPropertyName,const nsACString & aValue,const nsACString & aPriority,nsIPrincipal * aSubjectPrincipal,ErrorResult & aRv)1127 void nsComputedDOMStyle::SetProperty(const nsACString& aPropertyName,
1128 const nsACString& aValue,
1129 const nsACString& aPriority,
1130 nsIPrincipal* aSubjectPrincipal,
1131 ErrorResult& aRv) {
1132 // Note: not using nsPrintfCString here in case aPropertyName contains
1133 // nulls.
1134 aRv.ThrowNoModificationAllowedError("Can't set value for property '"_ns +
1135 aPropertyName + "' in computed style"_ns);
1136 }
1137
IndexedGetter(uint32_t aIndex,bool & aFound,nsACString & aPropName)1138 void nsComputedDOMStyle::IndexedGetter(uint32_t aIndex, bool& aFound,
1139 nsACString& aPropName) {
1140 ComputedStyleMap* map = GetComputedStyleMap();
1141 uint32_t length = map->Length();
1142
1143 if (aIndex < length) {
1144 aFound = true;
1145 aPropName.Assign(nsCSSProps::GetStringValue(map->PropertyAt(aIndex)));
1146 return;
1147 }
1148
1149 // Custom properties are exposed with indexed properties just after all
1150 // of the built-in properties.
1151 UpdateCurrentStyleSources(eCSSPropertyExtra_variable);
1152 if (!mComputedStyle) {
1153 aFound = false;
1154 return;
1155 }
1156
1157 uint32_t count = Servo_GetCustomPropertiesCount(mComputedStyle);
1158
1159 const uint32_t index = aIndex - length;
1160 if (index < count) {
1161 aFound = true;
1162 aPropName.AssignLiteral("--");
1163 if (nsAtom* atom = Servo_GetCustomPropertyNameAt(mComputedStyle, index)) {
1164 aPropName.Append(nsAtomCString(atom));
1165 }
1166 } else {
1167 aFound = false;
1168 }
1169
1170 ClearCurrentStyleSources();
1171 }
1172
1173 // Property getters...
1174
DoGetBottom()1175 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBottom() {
1176 return GetOffsetWidthFor(eSideBottom);
1177 }
1178
1179 /* static */
SetToRGBAColor(nsROCSSPrimitiveValue * aValue,nscolor aColor)1180 void nsComputedDOMStyle::SetToRGBAColor(nsROCSSPrimitiveValue* aValue,
1181 nscolor aColor) {
1182 nsAutoString string;
1183 nsStyleUtil::GetSerializedColorValue(aColor, string);
1184 aValue->SetString(string);
1185 }
1186
SetValueFromComplexColor(nsROCSSPrimitiveValue * aValue,const mozilla::StyleColor & aColor)1187 void nsComputedDOMStyle::SetValueFromComplexColor(
1188 nsROCSSPrimitiveValue* aValue, const mozilla::StyleColor& aColor) {
1189 SetToRGBAColor(aValue, aColor.CalcColor(*mComputedStyle));
1190 }
1191
DoGetColumnRuleWidth()1192 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetColumnRuleWidth() {
1193 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1194 val->SetAppUnits(StyleColumn()->GetComputedColumnRuleWidth());
1195 return val.forget();
1196 }
1197
MaybeResolvePositionForTransform(const LengthPercentage & aX,const LengthPercentage & aY,nsIFrame * aInnerFrame)1198 static Position MaybeResolvePositionForTransform(const LengthPercentage& aX,
1199 const LengthPercentage& aY,
1200 nsIFrame* aInnerFrame) {
1201 if (!aInnerFrame) {
1202 return {aX, aY};
1203 }
1204 nsStyleTransformMatrix::TransformReferenceBox refBox(aInnerFrame);
1205 CSSPoint p = nsStyleTransformMatrix::Convert2DPosition(aX, aY, refBox);
1206 return {LengthPercentage::FromPixels(p.x), LengthPercentage::FromPixels(p.y)};
1207 }
1208
1209 /* Convert the stored representation into a list of two values and then hand
1210 * it back.
1211 */
DoGetTransformOrigin()1212 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTransformOrigin() {
1213 /* We need to build up a list of two values. We'll call them
1214 * width and height.
1215 */
1216
1217 /* Store things as a value list */
1218 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1219
1220 /* Now, get the values. */
1221 const auto& origin = StyleDisplay()->mTransformOrigin;
1222
1223 RefPtr<nsROCSSPrimitiveValue> width = new nsROCSSPrimitiveValue;
1224 auto position = MaybeResolvePositionForTransform(
1225 origin.horizontal, origin.vertical, mInnerFrame);
1226 SetValueToPosition(position, valueList);
1227 if (!origin.depth.IsZero()) {
1228 RefPtr<nsROCSSPrimitiveValue> depth = new nsROCSSPrimitiveValue;
1229 depth->SetPixels(origin.depth.ToCSSPixels());
1230 valueList->AppendCSSValue(depth.forget());
1231 }
1232 return valueList.forget();
1233 }
1234
1235 /* Convert the stored representation into a list of two values and then hand
1236 * it back.
1237 */
DoGetPerspectiveOrigin()1238 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPerspectiveOrigin() {
1239 /* We need to build up a list of two values. We'll call them
1240 * width and height.
1241 */
1242
1243 /* Store things as a value list */
1244 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1245
1246 /* Now, get the values. */
1247 const auto& origin = StyleDisplay()->mPerspectiveOrigin;
1248
1249 auto position = MaybeResolvePositionForTransform(
1250 origin.horizontal, origin.vertical, mInnerFrame);
1251 SetValueToPosition(position, valueList);
1252 return valueList.forget();
1253 }
1254
DoGetTransform()1255 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTransform() {
1256 const nsStyleDisplay* display = StyleDisplay();
1257 return GetTransformValue(display->mTransform);
1258 }
1259
1260 /* static */
MatrixToCSSValue(const mozilla::gfx::Matrix4x4 & matrix)1261 already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::MatrixToCSSValue(
1262 const mozilla::gfx::Matrix4x4& matrix) {
1263 bool is3D = !matrix.Is2D();
1264
1265 nsAutoString resultString(u"matrix"_ns);
1266 if (is3D) {
1267 resultString.AppendLiteral("3d");
1268 }
1269
1270 resultString.Append('(');
1271 resultString.AppendFloat(matrix._11);
1272 resultString.AppendLiteral(", ");
1273 resultString.AppendFloat(matrix._12);
1274 resultString.AppendLiteral(", ");
1275 if (is3D) {
1276 resultString.AppendFloat(matrix._13);
1277 resultString.AppendLiteral(", ");
1278 resultString.AppendFloat(matrix._14);
1279 resultString.AppendLiteral(", ");
1280 }
1281 resultString.AppendFloat(matrix._21);
1282 resultString.AppendLiteral(", ");
1283 resultString.AppendFloat(matrix._22);
1284 resultString.AppendLiteral(", ");
1285 if (is3D) {
1286 resultString.AppendFloat(matrix._23);
1287 resultString.AppendLiteral(", ");
1288 resultString.AppendFloat(matrix._24);
1289 resultString.AppendLiteral(", ");
1290 resultString.AppendFloat(matrix._31);
1291 resultString.AppendLiteral(", ");
1292 resultString.AppendFloat(matrix._32);
1293 resultString.AppendLiteral(", ");
1294 resultString.AppendFloat(matrix._33);
1295 resultString.AppendLiteral(", ");
1296 resultString.AppendFloat(matrix._34);
1297 resultString.AppendLiteral(", ");
1298 }
1299 resultString.AppendFloat(matrix._41);
1300 resultString.AppendLiteral(", ");
1301 resultString.AppendFloat(matrix._42);
1302 if (is3D) {
1303 resultString.AppendLiteral(", ");
1304 resultString.AppendFloat(matrix._43);
1305 resultString.AppendLiteral(", ");
1306 resultString.AppendFloat(matrix._44);
1307 }
1308 resultString.Append(')');
1309
1310 /* Create a value to hold our result. */
1311 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1312
1313 val->SetString(resultString);
1314 return val.forget();
1315 }
1316
DoGetOsxFontSmoothing()1317 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetOsxFontSmoothing() {
1318 if (nsContentUtils::ShouldResistFingerprinting(
1319 mPresShell->GetPresContext()->GetDocShell())) {
1320 return nullptr;
1321 }
1322
1323 nsAutoCString result;
1324 mComputedStyle->GetComputedPropertyValue(eCSSProperty__moz_osx_font_smoothing,
1325 result);
1326 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1327 val->SetString(result);
1328 return val.forget();
1329 }
1330
DoGetImageLayerPosition(const nsStyleImageLayers & aLayers)1331 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetImageLayerPosition(
1332 const nsStyleImageLayers& aLayers) {
1333 if (aLayers.mPositionXCount != aLayers.mPositionYCount) {
1334 // No value to return. We can't express this combination of
1335 // values as a shorthand.
1336 return nullptr;
1337 }
1338
1339 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
1340 for (uint32_t i = 0, i_end = aLayers.mPositionXCount; i < i_end; ++i) {
1341 RefPtr<nsDOMCSSValueList> itemList = GetROCSSValueList(false);
1342
1343 SetValueToPosition(aLayers.mLayers[i].mPosition, itemList);
1344 valueList->AppendCSSValue(itemList.forget());
1345 }
1346
1347 return valueList.forget();
1348 }
1349
SetValueToPosition(const Position & aPosition,nsDOMCSSValueList * aValueList)1350 void nsComputedDOMStyle::SetValueToPosition(const Position& aPosition,
1351 nsDOMCSSValueList* aValueList) {
1352 RefPtr<nsROCSSPrimitiveValue> valX = new nsROCSSPrimitiveValue;
1353 SetValueToLengthPercentage(valX, aPosition.horizontal, false);
1354 aValueList->AppendCSSValue(valX.forget());
1355
1356 RefPtr<nsROCSSPrimitiveValue> valY = new nsROCSSPrimitiveValue;
1357 SetValueToLengthPercentage(valY, aPosition.vertical, false);
1358 aValueList->AppendCSSValue(valY.forget());
1359 }
1360
SetValueToURLValue(const StyleComputedUrl * aURL,nsROCSSPrimitiveValue * aValue)1361 void nsComputedDOMStyle::SetValueToURLValue(const StyleComputedUrl* aURL,
1362 nsROCSSPrimitiveValue* aValue) {
1363 if (!aURL) {
1364 aValue->SetString("none");
1365 return;
1366 }
1367
1368 // If we have a usable nsIURI in the URLValue, and the url() wasn't
1369 // a fragment-only URL, serialize the nsIURI.
1370 if (!aURL->IsLocalRef()) {
1371 if (nsIURI* uri = aURL->GetURI()) {
1372 aValue->SetURI(uri);
1373 return;
1374 }
1375 }
1376
1377 // Otherwise, serialize the specified URL value.
1378 NS_ConvertUTF8toUTF16 source(aURL->SpecifiedSerialization());
1379 nsAutoString url;
1380 url.AppendLiteral(u"url(");
1381 nsStyleUtil::AppendEscapedCSSString(source, url, '"');
1382 url.Append(')');
1383 aValue->SetString(url);
1384 }
1385
1386 enum class Brackets { No, Yes };
1387
AppendGridLineNames(nsACString & aResult,Span<const StyleCustomIdent> aLineNames,Brackets aBrackets)1388 static void AppendGridLineNames(nsACString& aResult,
1389 Span<const StyleCustomIdent> aLineNames,
1390 Brackets aBrackets) {
1391 if (aLineNames.IsEmpty()) {
1392 if (aBrackets == Brackets::Yes) {
1393 aResult.AppendLiteral("[]");
1394 }
1395 return;
1396 }
1397 uint32_t numLines = aLineNames.Length();
1398 if (aBrackets == Brackets::Yes) {
1399 aResult.Append('[');
1400 }
1401 for (uint32_t i = 0;;) {
1402 // TODO: Maybe use servo to do this and avoid the silly utf16->utf8 dance?
1403 nsAutoString name;
1404 nsStyleUtil::AppendEscapedCSSIdent(
1405 nsDependentAtomString(aLineNames[i].AsAtom()), name);
1406 AppendUTF16toUTF8(name, aResult);
1407
1408 if (++i == numLines) {
1409 break;
1410 }
1411 aResult.Append(' ');
1412 }
1413 if (aBrackets == Brackets::Yes) {
1414 aResult.Append(']');
1415 }
1416 }
1417
AppendGridLineNames(nsDOMCSSValueList * aValueList,Span<const StyleCustomIdent> aLineNames,bool aSuppressEmptyList=true)1418 static void AppendGridLineNames(nsDOMCSSValueList* aValueList,
1419 Span<const StyleCustomIdent> aLineNames,
1420 bool aSuppressEmptyList = true) {
1421 if (aLineNames.IsEmpty() && aSuppressEmptyList) {
1422 return;
1423 }
1424 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1425 nsAutoCString lineNamesString;
1426 AppendGridLineNames(lineNamesString, aLineNames, Brackets::Yes);
1427 val->SetString(lineNamesString);
1428 aValueList->AppendCSSValue(val.forget());
1429 }
1430
AppendGridLineNames(nsDOMCSSValueList * aValueList,Span<const StyleCustomIdent> aLineNames1,Span<const StyleCustomIdent> aLineNames2)1431 static void AppendGridLineNames(nsDOMCSSValueList* aValueList,
1432 Span<const StyleCustomIdent> aLineNames1,
1433 Span<const StyleCustomIdent> aLineNames2) {
1434 if (aLineNames1.IsEmpty() && aLineNames2.IsEmpty()) {
1435 return;
1436 }
1437 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1438 nsAutoCString lineNamesString;
1439 lineNamesString.Assign('[');
1440 if (!aLineNames1.IsEmpty()) {
1441 AppendGridLineNames(lineNamesString, aLineNames1, Brackets::No);
1442 }
1443 if (!aLineNames2.IsEmpty()) {
1444 if (!aLineNames1.IsEmpty()) {
1445 lineNamesString.Append(' ');
1446 }
1447 AppendGridLineNames(lineNamesString, aLineNames2, Brackets::No);
1448 }
1449 lineNamesString.Append(']');
1450 val->SetString(lineNamesString);
1451 aValueList->AppendCSSValue(val.forget());
1452 }
1453
SetValueToTrackBreadth(nsROCSSPrimitiveValue * aValue,const StyleTrackBreadth & aBreadth)1454 void nsComputedDOMStyle::SetValueToTrackBreadth(
1455 nsROCSSPrimitiveValue* aValue, const StyleTrackBreadth& aBreadth) {
1456 using Tag = StyleTrackBreadth::Tag;
1457 switch (aBreadth.tag) {
1458 case Tag::MinContent:
1459 return aValue->SetString("min-content");
1460 case Tag::MaxContent:
1461 return aValue->SetString("max-content");
1462 case Tag::Auto:
1463 return aValue->SetString("auto");
1464 case Tag::Breadth:
1465 return SetValueToLengthPercentage(aValue, aBreadth.AsBreadth(), true);
1466 case Tag::Fr: {
1467 nsAutoString tmpStr;
1468 nsStyleUtil::AppendCSSNumber(aBreadth.AsFr(), tmpStr);
1469 tmpStr.AppendLiteral("fr");
1470 return aValue->SetString(tmpStr);
1471 }
1472 default:
1473 MOZ_ASSERT_UNREACHABLE("Unknown breadth value");
1474 return;
1475 }
1476 }
1477
GetGridTrackBreadth(const StyleTrackBreadth & aBreadth)1478 already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::GetGridTrackBreadth(
1479 const StyleTrackBreadth& aBreadth) {
1480 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1481 SetValueToTrackBreadth(val, aBreadth);
1482 return val.forget();
1483 }
1484
GetGridTrackSize(const StyleTrackSize & aTrackSize)1485 already_AddRefed<nsROCSSPrimitiveValue> nsComputedDOMStyle::GetGridTrackSize(
1486 const StyleTrackSize& aTrackSize) {
1487 if (aTrackSize.IsFitContent()) {
1488 // A fit-content() function.
1489 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1490 MOZ_ASSERT(aTrackSize.AsFitContent().IsBreadth(),
1491 "unexpected unit for fit-content() argument value");
1492 SetValueFromFitContentFunction(val, aTrackSize.AsFitContent().AsBreadth());
1493 return val.forget();
1494 }
1495
1496 if (aTrackSize.IsBreadth()) {
1497 return GetGridTrackBreadth(aTrackSize.AsBreadth());
1498 }
1499
1500 MOZ_ASSERT(aTrackSize.IsMinmax());
1501 auto& min = aTrackSize.AsMinmax()._0;
1502 auto& max = aTrackSize.AsMinmax()._1;
1503 if (min == max) {
1504 return GetGridTrackBreadth(min);
1505 }
1506
1507 // minmax(auto, <flex>) is equivalent to (and is our internal representation
1508 // of) <flex>, and both compute to <flex>
1509 if (min.IsAuto() && max.IsFr()) {
1510 return GetGridTrackBreadth(max);
1511 }
1512
1513 nsAutoString argumentStr, minmaxStr;
1514 minmaxStr.AppendLiteral("minmax(");
1515
1516 {
1517 RefPtr<nsROCSSPrimitiveValue> argValue = GetGridTrackBreadth(min);
1518 argValue->GetCssText(argumentStr, IgnoreErrors());
1519 minmaxStr.Append(argumentStr);
1520 argumentStr.Truncate();
1521 }
1522
1523 minmaxStr.AppendLiteral(", ");
1524
1525 {
1526 RefPtr<nsROCSSPrimitiveValue> argValue = GetGridTrackBreadth(max);
1527 argValue->GetCssText(argumentStr, IgnoreErrors());
1528 minmaxStr.Append(argumentStr);
1529 }
1530
1531 minmaxStr.Append(char16_t(')'));
1532 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1533 val->SetString(minmaxStr);
1534 return val.forget();
1535 }
1536
GetGridTemplateColumnsRows(const StyleGridTemplateComponent & aTrackList,const ComputedGridTrackInfo & aTrackInfo)1537 already_AddRefed<CSSValue> nsComputedDOMStyle::GetGridTemplateColumnsRows(
1538 const StyleGridTemplateComponent& aTrackList,
1539 const ComputedGridTrackInfo& aTrackInfo) {
1540 if (aTrackInfo.mIsMasonry) {
1541 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1542 val->SetString("masonry");
1543 return val.forget();
1544 }
1545
1546 if (aTrackInfo.mIsSubgrid) {
1547 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1548 RefPtr<nsROCSSPrimitiveValue> subgridKeyword = new nsROCSSPrimitiveValue;
1549 subgridKeyword->SetString("subgrid");
1550 valueList->AppendCSSValue(subgridKeyword.forget());
1551 for (const auto& lineNames : aTrackInfo.mResolvedLineNames) {
1552 AppendGridLineNames(valueList, lineNames, /*aSuppressEmptyList*/ false);
1553 }
1554 uint32_t line = aTrackInfo.mResolvedLineNames.Length();
1555 uint32_t lastLine = aTrackInfo.mNumExplicitTracks + 1;
1556 const Span<const StyleCustomIdent> empty;
1557 for (; line < lastLine; ++line) {
1558 AppendGridLineNames(valueList, empty, /*aSuppressEmptyList*/ false);
1559 }
1560 return valueList.forget();
1561 }
1562
1563 const bool serializeImplicit =
1564 StaticPrefs::layout_css_serialize_grid_implicit_tracks();
1565
1566 const nsTArray<nscoord>& trackSizes = aTrackInfo.mSizes;
1567 const uint32_t numExplicitTracks = aTrackInfo.mNumExplicitTracks;
1568 const uint32_t numLeadingImplicitTracks =
1569 aTrackInfo.mNumLeadingImplicitTracks;
1570 uint32_t numSizes = trackSizes.Length();
1571 MOZ_ASSERT(numSizes >= numLeadingImplicitTracks + numExplicitTracks);
1572
1573 const bool hasTracksToSerialize =
1574 serializeImplicit ? !!numSizes : !!numExplicitTracks;
1575 const bool hasRepeatAuto = aTrackList.HasRepeatAuto();
1576 if (!hasTracksToSerialize && !hasRepeatAuto) {
1577 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1578 val->SetString("none");
1579 return val.forget();
1580 }
1581
1582 // We've done layout on the grid and have resolved the sizes of its tracks,
1583 // so we'll return those sizes here. The grid spec says we MAY use
1584 // repeat(<positive-integer>, Npx) here for consecutive tracks with the same
1585 // size, but that doesn't seem worth doing since even for repeat(auto-*)
1586 // the resolved size might differ for the repeated tracks.
1587 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1588
1589 // Add any leading implicit tracks.
1590 if (serializeImplicit) {
1591 for (uint32_t i = 0; i < numLeadingImplicitTracks; ++i) {
1592 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1593 val->SetAppUnits(trackSizes[i]);
1594 valueList->AppendCSSValue(val.forget());
1595 }
1596 }
1597
1598 if (hasRepeatAuto) {
1599 const auto* const autoRepeatValue = aTrackList.GetRepeatAutoValue();
1600 const auto repeatLineNames = autoRepeatValue->line_names.AsSpan();
1601 MOZ_ASSERT(repeatLineNames.Length() >= 2);
1602 // Number of tracks inside the repeat, not including any repetitions.
1603 // Check that if we have truncated the number of tracks due to overflowing
1604 // the maximum track limit then we also truncate this repeat count.
1605 MOZ_ASSERT(repeatLineNames.Length() ==
1606 autoRepeatValue->track_sizes.len + 1);
1607 // If we have truncated the first repetition of repeat tracks, then we
1608 // can't index using autoRepeatValue->track_sizes.len, and
1609 // aTrackInfo.mRemovedRepeatTracks.Length() will account for all repeat
1610 // tracks that haven't been truncated.
1611 const uint32_t numRepeatTracks =
1612 std::min(aTrackInfo.mRemovedRepeatTracks.Length(),
1613 autoRepeatValue->track_sizes.len);
1614 MOZ_ASSERT(repeatLineNames.Length() >= numRepeatTracks + 1);
1615 // The total of all tracks in all repetitions of the repeat.
1616 const uint32_t totalNumRepeatTracks =
1617 aTrackInfo.mRemovedRepeatTracks.Length();
1618 const uint32_t repeatStart = aTrackInfo.mRepeatFirstTrack;
1619 // We need to skip over any track sizes which were resolved to 0 by
1620 // collapsed tracks. Keep track of the iteration separately.
1621 const auto explicitTrackSizeBegin =
1622 trackSizes.cbegin() + numLeadingImplicitTracks;
1623 const auto explicitTrackSizeEnd =
1624 explicitTrackSizeBegin + numExplicitTracks;
1625 auto trackSizeIter = explicitTrackSizeBegin;
1626 // Write any leading explicit tracks before the repeat.
1627 for (uint32_t i = 0; i < repeatStart; i++) {
1628 AppendGridLineNames(valueList, aTrackInfo.mResolvedLineNames[i]);
1629 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1630 val->SetAppUnits(*trackSizeIter++);
1631 valueList->AppendCSSValue(val.forget());
1632 }
1633 auto lineNameIter = aTrackInfo.mResolvedLineNames.cbegin() + repeatStart;
1634 // Write the track names at the start of the repeat, including the names
1635 // at the end of the last non-repeat track. Unlike all later repeat line
1636 // name lists, this one needs the resolved line name which includes both
1637 // the last non-repeat line names and the leading repeat line names.
1638 AppendGridLineNames(valueList, *lineNameIter++);
1639 {
1640 // Write out the first repeat value, checking for size zero (removed
1641 // track).
1642 const nscoord firstRepeatTrackSize =
1643 (!aTrackInfo.mRemovedRepeatTracks[0]) ? *trackSizeIter++ : 0;
1644 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1645 val->SetAppUnits(firstRepeatTrackSize);
1646 valueList->AppendCSSValue(val.forget());
1647 }
1648 // Write the line names and track sizes inside the repeat, checking for
1649 // removed tracks (size 0).
1650 for (uint32_t i = 1; i < totalNumRepeatTracks; i++) {
1651 const uint32_t repeatIndex = i % numRepeatTracks;
1652 // If we are rolling over from one repetition to the next, include track
1653 // names from both the end of the previous repeat and the start of the
1654 // next.
1655 if (repeatIndex == 0) {
1656 AppendGridLineNames(valueList,
1657 repeatLineNames[numRepeatTracks].AsSpan(),
1658 repeatLineNames[0].AsSpan());
1659 } else {
1660 AppendGridLineNames(valueList, repeatLineNames[repeatIndex].AsSpan());
1661 }
1662 MOZ_ASSERT(aTrackInfo.mRemovedRepeatTracks[i] ||
1663 trackSizeIter != explicitTrackSizeEnd);
1664 const nscoord repeatTrackSize =
1665 (!aTrackInfo.mRemovedRepeatTracks[i]) ? *trackSizeIter++ : 0;
1666 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1667 val->SetAppUnits(repeatTrackSize);
1668 valueList->AppendCSSValue(val.forget());
1669 }
1670 // The resolved line names include a single repetition of the auto-repeat
1671 // line names. Skip over those.
1672 lineNameIter += numRepeatTracks - 1;
1673 // Write out any more tracks after the repeat.
1674 while (trackSizeIter != explicitTrackSizeEnd) {
1675 AppendGridLineNames(valueList, *lineNameIter++);
1676 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1677 val->SetAppUnits(*trackSizeIter++);
1678 valueList->AppendCSSValue(val.forget());
1679 }
1680 // Write the final trailing line name.
1681 AppendGridLineNames(valueList, *lineNameIter++);
1682 } else if (numExplicitTracks > 0) {
1683 // If there are explicit tracks but no repeat tracks, just serialize those.
1684 for (uint32_t i = 0;; i++) {
1685 AppendGridLineNames(valueList, aTrackInfo.mResolvedLineNames[i]);
1686 if (i == numExplicitTracks) {
1687 break;
1688 }
1689 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1690 val->SetAppUnits(trackSizes[i + numLeadingImplicitTracks]);
1691 valueList->AppendCSSValue(val.forget());
1692 }
1693 }
1694 // Add any trailing implicit tracks.
1695 if (serializeImplicit) {
1696 for (uint32_t i = numLeadingImplicitTracks + numExplicitTracks;
1697 i < numSizes; ++i) {
1698 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1699 val->SetAppUnits(trackSizes[i]);
1700 valueList->AppendCSSValue(val.forget());
1701 }
1702 }
1703
1704 return valueList.forget();
1705 }
1706
DoGetGridTemplateColumns()1707 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetGridTemplateColumns() {
1708 nsGridContainerFrame* gridFrame =
1709 nsGridContainerFrame::GetGridFrameWithComputedInfo(mInnerFrame);
1710 if (!gridFrame) {
1711 // The element doesn't have a box - return the computed value.
1712 // https://drafts.csswg.org/css-grid/#resolved-track-list
1713 nsAutoCString string;
1714 mComputedStyle->GetComputedPropertyValue(eCSSProperty_grid_template_columns,
1715 string);
1716 RefPtr<nsROCSSPrimitiveValue> value = new nsROCSSPrimitiveValue;
1717 value->SetString(string);
1718 return value.forget();
1719 }
1720
1721 // GetGridFrameWithComputedInfo() above ensures that this returns non-null:
1722 const ComputedGridTrackInfo* info = gridFrame->GetComputedTemplateColumns();
1723 return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateColumns,
1724 *info);
1725 }
1726
DoGetGridTemplateRows()1727 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetGridTemplateRows() {
1728 nsGridContainerFrame* gridFrame =
1729 nsGridContainerFrame::GetGridFrameWithComputedInfo(mInnerFrame);
1730 if (!gridFrame) {
1731 // The element doesn't have a box - return the computed value.
1732 // https://drafts.csswg.org/css-grid/#resolved-track-list
1733 nsAutoCString string;
1734 mComputedStyle->GetComputedPropertyValue(eCSSProperty_grid_template_rows,
1735 string);
1736 RefPtr<nsROCSSPrimitiveValue> value = new nsROCSSPrimitiveValue;
1737 value->SetString(string);
1738 return value.forget();
1739 }
1740
1741 // GetGridFrameWithComputedInfo() above ensures that this returns non-null:
1742 const ComputedGridTrackInfo* info = gridFrame->GetComputedTemplateRows();
1743 return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateRows, *info);
1744 }
1745
DoGetPaddingTop()1746 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingTop() {
1747 return GetPaddingWidthFor(eSideTop);
1748 }
1749
DoGetPaddingBottom()1750 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingBottom() {
1751 return GetPaddingWidthFor(eSideBottom);
1752 }
1753
DoGetPaddingLeft()1754 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingLeft() {
1755 return GetPaddingWidthFor(eSideLeft);
1756 }
1757
DoGetPaddingRight()1758 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetPaddingRight() {
1759 return GetPaddingWidthFor(eSideRight);
1760 }
1761
DoGetBorderSpacing()1762 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderSpacing() {
1763 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1764
1765 RefPtr<nsROCSSPrimitiveValue> xSpacing = new nsROCSSPrimitiveValue;
1766 RefPtr<nsROCSSPrimitiveValue> ySpacing = new nsROCSSPrimitiveValue;
1767
1768 const nsStyleTableBorder* border = StyleTableBorder();
1769 xSpacing->SetAppUnits(border->mBorderSpacingCol);
1770 ySpacing->SetAppUnits(border->mBorderSpacingRow);
1771
1772 valueList->AppendCSSValue(xSpacing.forget());
1773 valueList->AppendCSSValue(ySpacing.forget());
1774
1775 return valueList.forget();
1776 }
1777
DoGetBorderTopWidth()1778 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderTopWidth() {
1779 return GetBorderWidthFor(eSideTop);
1780 }
1781
DoGetBorderBottomWidth()1782 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderBottomWidth() {
1783 return GetBorderWidthFor(eSideBottom);
1784 }
1785
DoGetBorderLeftWidth()1786 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderLeftWidth() {
1787 return GetBorderWidthFor(eSideLeft);
1788 }
1789
DoGetBorderRightWidth()1790 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetBorderRightWidth() {
1791 return GetBorderWidthFor(eSideRight);
1792 }
1793
DoGetMarginTopWidth()1794 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginTopWidth() {
1795 return GetMarginWidthFor(eSideTop);
1796 }
1797
DoGetMarginBottomWidth()1798 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginBottomWidth() {
1799 return GetMarginWidthFor(eSideBottom);
1800 }
1801
DoGetMarginLeftWidth()1802 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginLeftWidth() {
1803 return GetMarginWidthFor(eSideLeft);
1804 }
1805
DoGetMarginRightWidth()1806 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMarginRightWidth() {
1807 return GetMarginWidthFor(eSideRight);
1808 }
1809
DoGetLineHeight()1810 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetLineHeight() {
1811 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1812
1813 {
1814 nscoord lineHeight;
1815 if (GetLineHeightCoord(lineHeight)) {
1816 val->SetAppUnits(lineHeight);
1817 return val.forget();
1818 }
1819 }
1820
1821 auto& lh = StyleText()->mLineHeight;
1822 if (lh.IsLength()) {
1823 val->SetPixels(lh.AsLength().ToCSSPixels());
1824 } else if (lh.IsNumber()) {
1825 val->SetNumber(lh.AsNumber());
1826 } else if (lh.IsMozBlockHeight()) {
1827 val->SetString("-moz-block-height");
1828 } else {
1829 MOZ_ASSERT(lh.IsNormal());
1830 val->SetString("normal");
1831 }
1832 return val.forget();
1833 }
1834
DoGetTextDecoration()1835 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTextDecoration() {
1836 auto getPropertyValue = [&](nsCSSPropertyID aID) {
1837 RefPtr<nsROCSSPrimitiveValue> value = new nsROCSSPrimitiveValue;
1838 nsAutoCString string;
1839 mComputedStyle->GetComputedPropertyValue(aID, string);
1840 value->SetString(string);
1841 return value.forget();
1842 };
1843
1844 const nsStyleTextReset* textReset = StyleTextReset();
1845 RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
1846
1847 if (textReset->mTextDecorationLine != StyleTextDecorationLine::NONE) {
1848 valueList->AppendCSSValue(
1849 getPropertyValue(eCSSProperty_text_decoration_line));
1850 }
1851
1852 if (textReset->mTextDecorationStyle != NS_STYLE_TEXT_DECORATION_STYLE_SOLID) {
1853 valueList->AppendCSSValue(
1854 getPropertyValue(eCSSProperty_text_decoration_style));
1855 }
1856
1857 // The resolved color shouldn't be currentColor, so we always serialize it.
1858 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1859 SetValueFromComplexColor(val, StyleTextReset()->mTextDecorationColor);
1860 valueList->AppendCSSValue(val.forget());
1861
1862 if (!textReset->mTextDecorationThickness.IsAuto()) {
1863 valueList->AppendCSSValue(
1864 getPropertyValue(eCSSProperty_text_decoration_thickness));
1865 }
1866
1867 return valueList.forget();
1868 }
1869
DoGetHeight()1870 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetHeight() {
1871 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1872
1873 if (mInnerFrame && !IsNonReplacedInline(mInnerFrame)) {
1874 AssertFlushedPendingReflows();
1875 nsMargin adjustedValues = GetAdjustedValuesForBoxSizing();
1876 val->SetAppUnits(mInnerFrame->GetContentRect().height +
1877 adjustedValues.TopBottom());
1878 } else {
1879 SetValueToSize(val, StylePosition()->mHeight);
1880 }
1881
1882 return val.forget();
1883 }
1884
DoGetWidth()1885 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetWidth() {
1886 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1887
1888 if (mInnerFrame && !IsNonReplacedInline(mInnerFrame)) {
1889 AssertFlushedPendingReflows();
1890 nsMargin adjustedValues = GetAdjustedValuesForBoxSizing();
1891 val->SetAppUnits(mInnerFrame->GetContentRect().width +
1892 adjustedValues.LeftRight());
1893 } else {
1894 SetValueToSize(val, StylePosition()->mWidth);
1895 }
1896
1897 return val.forget();
1898 }
1899
DoGetMaxHeight()1900 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMaxHeight() {
1901 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1902 SetValueToMaxSize(val, StylePosition()->mMaxHeight);
1903 return val.forget();
1904 }
1905
DoGetMaxWidth()1906 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMaxWidth() {
1907 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1908 SetValueToMaxSize(val, StylePosition()->mMaxWidth);
1909 return val.forget();
1910 }
1911
1912 /**
1913 * This function indicates whether we should return "auto" as the
1914 * getComputedStyle() result for the (default) "min-width: auto" and
1915 * "min-height: auto" CSS values.
1916 *
1917 * As of this writing, the CSS Sizing draft spec says this "auto" value
1918 * *always* computes to itself. However, for now, we only make it compute to
1919 * itself for grid and flex items (the containers where "auto" has special
1920 * significance), because those are the only areas where the CSSWG has actually
1921 * resolved on this "computes-to-itself" behavior. For elements in other sorts
1922 * of containers, this function returns false, which will make us resolve
1923 * "auto" to 0.
1924 */
ShouldHonorMinSizeAutoInAxis(PhysicalAxis aAxis)1925 bool nsComputedDOMStyle::ShouldHonorMinSizeAutoInAxis(PhysicalAxis aAxis) {
1926 return mOuterFrame && mOuterFrame->IsFlexOrGridItem();
1927 }
1928
DoGetMinHeight()1929 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMinHeight() {
1930 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1931 StyleSize minHeight = StylePosition()->mMinHeight;
1932
1933 if (minHeight.IsAuto() && !ShouldHonorMinSizeAutoInAxis(eAxisVertical)) {
1934 minHeight = StyleSize::LengthPercentage(LengthPercentage::Zero());
1935 }
1936
1937 SetValueToSize(val, minHeight);
1938 return val.forget();
1939 }
1940
DoGetMinWidth()1941 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMinWidth() {
1942 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
1943
1944 StyleSize minWidth = StylePosition()->mMinWidth;
1945
1946 if (minWidth.IsAuto() && !ShouldHonorMinSizeAutoInAxis(eAxisHorizontal)) {
1947 minWidth = StyleSize::LengthPercentage(LengthPercentage::Zero());
1948 }
1949
1950 SetValueToSize(val, minWidth);
1951 return val.forget();
1952 }
1953
DoGetLeft()1954 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetLeft() {
1955 return GetOffsetWidthFor(eSideLeft);
1956 }
1957
DoGetRight()1958 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetRight() {
1959 return GetOffsetWidthFor(eSideRight);
1960 }
1961
DoGetTop()1962 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetTop() {
1963 return GetOffsetWidthFor(eSideTop);
1964 }
1965
GetOffsetWidthFor(mozilla::Side aSide)1966 already_AddRefed<CSSValue> nsComputedDOMStyle::GetOffsetWidthFor(
1967 mozilla::Side aSide) {
1968 const nsStyleDisplay* display = StyleDisplay();
1969
1970 mozilla::StylePositionProperty position = display->mPosition;
1971 if (!mOuterFrame) {
1972 // GetNonStaticPositionOffset or GetAbsoluteOffset don't handle elements
1973 // without frames in any sensible way. GetStaticOffset, however, is perfect
1974 // for that case.
1975 position = StylePositionProperty::Static;
1976 }
1977
1978 switch (position) {
1979 case StylePositionProperty::Static:
1980 return GetStaticOffset(aSide);
1981 case StylePositionProperty::Sticky:
1982 return GetNonStaticPositionOffset(
1983 aSide, false, &nsComputedDOMStyle::GetScrollFrameContentWidth,
1984 &nsComputedDOMStyle::GetScrollFrameContentHeight);
1985 case StylePositionProperty::Absolute:
1986 case StylePositionProperty::Fixed:
1987 return GetAbsoluteOffset(aSide);
1988 case StylePositionProperty::Relative:
1989 return GetNonStaticPositionOffset(
1990 aSide, true, &nsComputedDOMStyle::GetCBContentWidth,
1991 &nsComputedDOMStyle::GetCBContentHeight);
1992 default:
1993 MOZ_ASSERT_UNREACHABLE("Invalid position");
1994 return nullptr;
1995 }
1996 }
1997
1998 static_assert(eSideTop == 0 && eSideRight == 1 && eSideBottom == 2 &&
1999 eSideLeft == 3,
2000 "box side constants not as expected for NS_OPPOSITE_SIDE");
2001 #define NS_OPPOSITE_SIDE(s_) mozilla::Side(((s_) + 2) & 3)
2002
GetNonStaticPositionOffset(mozilla::Side aSide,bool aResolveAuto,PercentageBaseGetter aWidthGetter,PercentageBaseGetter aHeightGetter)2003 already_AddRefed<CSSValue> nsComputedDOMStyle::GetNonStaticPositionOffset(
2004 mozilla::Side aSide, bool aResolveAuto, PercentageBaseGetter aWidthGetter,
2005 PercentageBaseGetter aHeightGetter) {
2006 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2007
2008 const nsStylePosition* positionData = StylePosition();
2009 int32_t sign = 1;
2010 LengthPercentageOrAuto coord = positionData->mOffset.Get(aSide);
2011
2012 if (coord.IsAuto()) {
2013 if (!aResolveAuto) {
2014 val->SetString("auto");
2015 return val.forget();
2016 }
2017 coord = positionData->mOffset.Get(NS_OPPOSITE_SIDE(aSide));
2018 sign = -1;
2019 }
2020 if (!coord.IsLengthPercentage()) {
2021 val->SetPixels(0.0f);
2022 return val.forget();
2023 }
2024
2025 auto& lp = coord.AsLengthPercentage();
2026 if (lp.ConvertsToLength()) {
2027 val->SetPixels(sign * lp.ToLengthInCSSPixels());
2028 return val.forget();
2029 }
2030
2031 PercentageBaseGetter baseGetter = (aSide == eSideLeft || aSide == eSideRight)
2032 ? aWidthGetter
2033 : aHeightGetter;
2034 nscoord percentageBase;
2035 if (!(this->*baseGetter)(percentageBase)) {
2036 val->SetPixels(0.0f);
2037 return val.forget();
2038 }
2039 nscoord result = lp.Resolve(percentageBase);
2040 val->SetAppUnits(sign * result);
2041 return val.forget();
2042 }
2043
GetAbsoluteOffset(mozilla::Side aSide)2044 already_AddRefed<CSSValue> nsComputedDOMStyle::GetAbsoluteOffset(
2045 mozilla::Side aSide) {
2046 const auto& offset = StylePosition()->mOffset;
2047 const auto& coord = offset.Get(aSide);
2048 const auto& oppositeCoord = offset.Get(NS_OPPOSITE_SIDE(aSide));
2049
2050 if (coord.IsAuto() || oppositeCoord.IsAuto()) {
2051 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2052 val->SetAppUnits(GetUsedAbsoluteOffset(aSide));
2053 return val.forget();
2054 }
2055
2056 return GetNonStaticPositionOffset(
2057 aSide, false, &nsComputedDOMStyle::GetCBPaddingRectWidth,
2058 &nsComputedDOMStyle::GetCBPaddingRectHeight);
2059 }
2060
GetUsedAbsoluteOffset(mozilla::Side aSide)2061 nscoord nsComputedDOMStyle::GetUsedAbsoluteOffset(mozilla::Side aSide) {
2062 MOZ_ASSERT(mOuterFrame, "need a frame, so we can call GetContainingBlock()");
2063
2064 nsIFrame* container = mOuterFrame->GetContainingBlock();
2065 nsMargin margin = mOuterFrame->GetUsedMargin();
2066 nsMargin border = container->GetUsedBorder();
2067 nsMargin scrollbarSizes(0, 0, 0, 0);
2068 nsRect rect = mOuterFrame->GetRect();
2069 nsRect containerRect = container->GetRect();
2070
2071 if (container->IsViewportFrame()) {
2072 // For absolutely positioned frames scrollbars are taken into
2073 // account by virtue of getting a containing block that does
2074 // _not_ include the scrollbars. For fixed positioned frames,
2075 // the containing block is the viewport, which _does_ include
2076 // scrollbars. We have to do some extra work.
2077 // the first child in the default frame list is what we want
2078 nsIFrame* scrollingChild = container->PrincipalChildList().FirstChild();
2079 nsIScrollableFrame* scrollFrame = do_QueryFrame(scrollingChild);
2080 if (scrollFrame) {
2081 scrollbarSizes = scrollFrame->GetActualScrollbarSizes();
2082 }
2083
2084 // The viewport size might have been expanded by the visual viewport or
2085 // the minimum-scale size.
2086 const ViewportFrame* viewportFrame = do_QueryFrame(container);
2087 MOZ_ASSERT(viewportFrame);
2088 containerRect.SizeTo(
2089 viewportFrame->AdjustViewportSizeForFixedPosition(containerRect));
2090 } else if (container->IsGridContainerFrame() &&
2091 mOuterFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
2092 containerRect = nsGridContainerFrame::GridItemCB(mOuterFrame);
2093 rect.MoveBy(-containerRect.x, -containerRect.y);
2094 }
2095
2096 nscoord offset = 0;
2097 switch (aSide) {
2098 case eSideTop:
2099 offset = rect.y - margin.top - border.top - scrollbarSizes.top;
2100
2101 break;
2102 case eSideRight:
2103 offset = containerRect.width - rect.width - rect.x - margin.right -
2104 border.right - scrollbarSizes.right;
2105
2106 break;
2107 case eSideBottom:
2108 offset = containerRect.height - rect.height - rect.y - margin.bottom -
2109 border.bottom - scrollbarSizes.bottom;
2110
2111 break;
2112 case eSideLeft:
2113 offset = rect.x - margin.left - border.left - scrollbarSizes.left;
2114
2115 break;
2116 default:
2117 NS_ERROR("Invalid side");
2118 break;
2119 }
2120
2121 return offset;
2122 }
2123
GetStaticOffset(mozilla::Side aSide)2124 already_AddRefed<CSSValue> nsComputedDOMStyle::GetStaticOffset(
2125 mozilla::Side aSide) {
2126 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2127 SetValueToLengthPercentageOrAuto(val, StylePosition()->mOffset.Get(aSide),
2128 false);
2129 return val.forget();
2130 }
2131
GetPaddingWidthFor(mozilla::Side aSide)2132 already_AddRefed<CSSValue> nsComputedDOMStyle::GetPaddingWidthFor(
2133 mozilla::Side aSide) {
2134 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2135
2136 auto& padding = StylePadding()->mPadding.Get(aSide);
2137 if (!mInnerFrame || !PaddingNeedsUsedValue(padding, *mComputedStyle)) {
2138 SetValueToLengthPercentage(val, padding, true);
2139 } else {
2140 AssertFlushedPendingReflows();
2141 val->SetAppUnits(mInnerFrame->GetUsedPadding().Side(aSide));
2142 }
2143
2144 return val.forget();
2145 }
2146
GetLineHeightCoord(nscoord & aCoord)2147 bool nsComputedDOMStyle::GetLineHeightCoord(nscoord& aCoord) {
2148 nscoord blockHeight = NS_UNCONSTRAINEDSIZE;
2149 const auto& lh = StyleText()->mLineHeight;
2150
2151 if (lh.IsNormal() &&
2152 StaticPrefs::layout_css_line_height_normal_as_resolved_value_enabled()) {
2153 return false;
2154 }
2155
2156 if (lh.IsMozBlockHeight()) {
2157 if (!mInnerFrame) {
2158 return false;
2159 }
2160
2161 AssertFlushedPendingReflows();
2162
2163 if (nsLayoutUtils::IsNonWrapperBlock(mInnerFrame)) {
2164 blockHeight = mInnerFrame->GetContentRect().height;
2165 } else {
2166 GetCBContentHeight(blockHeight);
2167 }
2168 }
2169
2170 nsPresContext* presContext = mPresShell->GetPresContext();
2171
2172 // lie about font size inflation since we lie about font size (since
2173 // the inflation only applies to text)
2174 aCoord = ReflowInput::CalcLineHeight(mElement, mComputedStyle, presContext,
2175 blockHeight, 1.0f);
2176
2177 // CalcLineHeight uses font->mFont.size, but we want to use
2178 // font->mSize as the font size. Adjust for that. Also adjust for
2179 // the text zoom, if any.
2180 const nsStyleFont* font = StyleFont();
2181 float fCoord = float(aCoord);
2182 if (font->mAllowZoomAndMinSize) {
2183 fCoord /= presContext->EffectiveTextZoom();
2184 }
2185 if (font->mFont.size != font->mSize) {
2186 fCoord *= font->mSize.ToCSSPixels() / font->mFont.size.ToCSSPixels();
2187 }
2188 aCoord = NSToCoordRound(fCoord);
2189
2190 return true;
2191 }
2192
GetBorderWidthFor(mozilla::Side aSide)2193 already_AddRefed<CSSValue> nsComputedDOMStyle::GetBorderWidthFor(
2194 mozilla::Side aSide) {
2195 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2196
2197 nscoord width;
2198 if (mInnerFrame && mComputedStyle->StyleDisplay()->HasAppearance()) {
2199 AssertFlushedPendingReflows();
2200 width = mInnerFrame->GetUsedBorder().Side(aSide);
2201 } else {
2202 width = StyleBorder()->GetComputedBorderWidth(aSide);
2203 }
2204 val->SetAppUnits(width);
2205
2206 return val.forget();
2207 }
2208
GetMarginWidthFor(mozilla::Side aSide)2209 already_AddRefed<CSSValue> nsComputedDOMStyle::GetMarginWidthFor(
2210 mozilla::Side aSide) {
2211 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2212
2213 auto& margin = StyleMargin()->mMargin.Get(aSide);
2214 if (!mInnerFrame || margin.ConvertsToLength()) {
2215 SetValueToLengthPercentageOrAuto(val, margin, false);
2216 } else {
2217 AssertFlushedPendingReflows();
2218
2219 // For tables, GetUsedMargin always returns an empty margin, so we
2220 // should read the margin from the table wrapper frame instead.
2221 val->SetAppUnits(mOuterFrame->GetUsedMargin().Side(aSide));
2222 NS_ASSERTION(mOuterFrame == mInnerFrame ||
2223 mInnerFrame->GetUsedMargin() == nsMargin(0, 0, 0, 0),
2224 "Inner tables must have zero margins");
2225 }
2226
2227 return val.forget();
2228 }
2229
SetValueToExtremumLength(nsROCSSPrimitiveValue * aValue,nsIFrame::ExtremumLength aSize)2230 static void SetValueToExtremumLength(nsROCSSPrimitiveValue* aValue,
2231 nsIFrame::ExtremumLength aSize) {
2232 switch (aSize) {
2233 case nsIFrame::ExtremumLength::MaxContent:
2234 return aValue->SetString("max-content");
2235 case nsIFrame::ExtremumLength::MinContent:
2236 return aValue->SetString("min-content");
2237 case nsIFrame::ExtremumLength::MozAvailable:
2238 return aValue->SetString("-moz-available");
2239 case nsIFrame::ExtremumLength::MozFitContent:
2240 return aValue->SetString("-moz-fit-content");
2241 case nsIFrame::ExtremumLength::FitContentFunction:
2242 MOZ_ASSERT_UNREACHABLE("fit-content() should be handled separately");
2243 }
2244 MOZ_ASSERT_UNREACHABLE("Unknown extremum length?");
2245 }
2246
SetValueFromFitContentFunction(nsROCSSPrimitiveValue * aValue,const LengthPercentage & aLength)2247 void nsComputedDOMStyle::SetValueFromFitContentFunction(
2248 nsROCSSPrimitiveValue* aValue, const LengthPercentage& aLength) {
2249 nsAutoString argumentStr;
2250 SetValueToLengthPercentage(aValue, aLength, true);
2251 aValue->GetCssText(argumentStr, IgnoreErrors());
2252
2253 nsAutoString fitContentStr;
2254 fitContentStr.AppendLiteral("fit-content(");
2255 fitContentStr.Append(argumentStr);
2256 fitContentStr.Append(char16_t(')'));
2257 aValue->SetString(fitContentStr);
2258 }
2259
SetValueToSize(nsROCSSPrimitiveValue * aValue,const StyleSize & aSize)2260 void nsComputedDOMStyle::SetValueToSize(nsROCSSPrimitiveValue* aValue,
2261 const StyleSize& aSize) {
2262 if (aSize.IsAuto()) {
2263 return aValue->SetString("auto");
2264 }
2265 if (aSize.IsFitContentFunction()) {
2266 return SetValueFromFitContentFunction(aValue, aSize.AsFitContentFunction());
2267 }
2268 if (auto length = nsIFrame::ToExtremumLength(aSize)) {
2269 return SetValueToExtremumLength(aValue, *length);
2270 }
2271 MOZ_ASSERT(aSize.IsLengthPercentage());
2272 SetValueToLengthPercentage(aValue, aSize.AsLengthPercentage(), true);
2273 }
2274
SetValueToMaxSize(nsROCSSPrimitiveValue * aValue,const StyleMaxSize & aSize)2275 void nsComputedDOMStyle::SetValueToMaxSize(nsROCSSPrimitiveValue* aValue,
2276 const StyleMaxSize& aSize) {
2277 if (aSize.IsNone()) {
2278 return aValue->SetString("none");
2279 }
2280 if (aSize.IsFitContentFunction()) {
2281 return SetValueFromFitContentFunction(aValue, aSize.AsFitContentFunction());
2282 }
2283 if (auto length = nsIFrame::ToExtremumLength(aSize)) {
2284 return SetValueToExtremumLength(aValue, *length);
2285 }
2286 MOZ_ASSERT(aSize.IsLengthPercentage());
2287 SetValueToLengthPercentage(aValue, aSize.AsLengthPercentage(), true);
2288 }
2289
SetValueToLengthPercentageOrAuto(nsROCSSPrimitiveValue * aValue,const LengthPercentageOrAuto & aSize,bool aClampNegativeCalc)2290 void nsComputedDOMStyle::SetValueToLengthPercentageOrAuto(
2291 nsROCSSPrimitiveValue* aValue, const LengthPercentageOrAuto& aSize,
2292 bool aClampNegativeCalc) {
2293 if (aSize.IsAuto()) {
2294 return aValue->SetString("auto");
2295 }
2296 SetValueToLengthPercentage(aValue, aSize.AsLengthPercentage(),
2297 aClampNegativeCalc);
2298 }
2299
SetValueToLengthPercentage(nsROCSSPrimitiveValue * aValue,const mozilla::LengthPercentage & aLength,bool aClampNegativeCalc)2300 void nsComputedDOMStyle::SetValueToLengthPercentage(
2301 nsROCSSPrimitiveValue* aValue, const mozilla::LengthPercentage& aLength,
2302 bool aClampNegativeCalc) {
2303 if (aLength.ConvertsToLength()) {
2304 CSSCoord length = aLength.ToLengthInCSSPixels();
2305 if (aClampNegativeCalc) {
2306 length = std::max(float(length), 0.0f);
2307 }
2308 return aValue->SetPixels(length);
2309 }
2310 if (aLength.ConvertsToPercentage()) {
2311 float result = aLength.ToPercentage();
2312 if (aClampNegativeCalc) {
2313 result = std::max(result, 0.0f);
2314 }
2315 return aValue->SetPercent(result);
2316 }
2317
2318 nsAutoCString result;
2319 Servo_LengthPercentage_ToCss(&aLength, &result);
2320 aValue->SetString(result);
2321 }
2322
GetCBContentWidth(nscoord & aWidth)2323 bool nsComputedDOMStyle::GetCBContentWidth(nscoord& aWidth) {
2324 if (!mOuterFrame) {
2325 return false;
2326 }
2327
2328 AssertFlushedPendingReflows();
2329
2330 aWidth = mOuterFrame->GetContainingBlock()->GetContentRect().width;
2331 return true;
2332 }
2333
GetCBContentHeight(nscoord & aHeight)2334 bool nsComputedDOMStyle::GetCBContentHeight(nscoord& aHeight) {
2335 if (!mOuterFrame) {
2336 return false;
2337 }
2338
2339 AssertFlushedPendingReflows();
2340
2341 aHeight = mOuterFrame->GetContainingBlock()->GetContentRect().height;
2342 return true;
2343 }
2344
GetCBPaddingRectWidth(nscoord & aWidth)2345 bool nsComputedDOMStyle::GetCBPaddingRectWidth(nscoord& aWidth) {
2346 if (!mOuterFrame) {
2347 return false;
2348 }
2349
2350 AssertFlushedPendingReflows();
2351
2352 aWidth = mOuterFrame->GetContainingBlock()->GetPaddingRect().width;
2353 return true;
2354 }
2355
GetCBPaddingRectHeight(nscoord & aHeight)2356 bool nsComputedDOMStyle::GetCBPaddingRectHeight(nscoord& aHeight) {
2357 if (!mOuterFrame) {
2358 return false;
2359 }
2360
2361 AssertFlushedPendingReflows();
2362
2363 aHeight = mOuterFrame->GetContainingBlock()->GetPaddingRect().height;
2364 return true;
2365 }
2366
GetScrollFrameContentWidth(nscoord & aWidth)2367 bool nsComputedDOMStyle::GetScrollFrameContentWidth(nscoord& aWidth) {
2368 if (!mOuterFrame) {
2369 return false;
2370 }
2371
2372 AssertFlushedPendingReflows();
2373
2374 nsIScrollableFrame* scrollableFrame =
2375 nsLayoutUtils::GetNearestScrollableFrame(
2376 mOuterFrame->GetParent(),
2377 nsLayoutUtils::SCROLLABLE_SAME_DOC |
2378 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
2379
2380 if (!scrollableFrame) {
2381 return false;
2382 }
2383 aWidth =
2384 scrollableFrame->GetScrolledFrame()->GetContentRectRelativeToSelf().width;
2385 return true;
2386 }
2387
GetScrollFrameContentHeight(nscoord & aHeight)2388 bool nsComputedDOMStyle::GetScrollFrameContentHeight(nscoord& aHeight) {
2389 if (!mOuterFrame) {
2390 return false;
2391 }
2392
2393 AssertFlushedPendingReflows();
2394
2395 nsIScrollableFrame* scrollableFrame =
2396 nsLayoutUtils::GetNearestScrollableFrame(
2397 mOuterFrame->GetParent(),
2398 nsLayoutUtils::SCROLLABLE_SAME_DOC |
2399 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
2400
2401 if (!scrollableFrame) {
2402 return false;
2403 }
2404 aHeight = scrollableFrame->GetScrolledFrame()
2405 ->GetContentRectRelativeToSelf()
2406 .height;
2407 return true;
2408 }
2409
GetFrameBorderRectWidth(nscoord & aWidth)2410 bool nsComputedDOMStyle::GetFrameBorderRectWidth(nscoord& aWidth) {
2411 if (!mInnerFrame) {
2412 return false;
2413 }
2414
2415 AssertFlushedPendingReflows();
2416
2417 aWidth = mInnerFrame->GetSize().width;
2418 return true;
2419 }
2420
GetFrameBorderRectHeight(nscoord & aHeight)2421 bool nsComputedDOMStyle::GetFrameBorderRectHeight(nscoord& aHeight) {
2422 if (!mInnerFrame) {
2423 return false;
2424 }
2425
2426 AssertFlushedPendingReflows();
2427
2428 aHeight = mInnerFrame->GetSize().height;
2429 return true;
2430 }
2431
2432 /* If the property is "none", hand back "none" wrapped in a value.
2433 * Otherwise, compute the aggregate transform matrix and hands it back in a
2434 * "matrix" wrapper.
2435 */
GetTransformValue(const StyleTransform & aTransform)2436 already_AddRefed<CSSValue> nsComputedDOMStyle::GetTransformValue(
2437 const StyleTransform& aTransform) {
2438 /* If there are no transforms, then we should construct a single-element
2439 * entry and hand it back.
2440 */
2441 if (aTransform.IsNone()) {
2442 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2443 val->SetString("none");
2444 return val.forget();
2445 }
2446
2447 /* Otherwise, we need to compute the current value of the transform matrix,
2448 * store it in a string, and hand it back to the caller.
2449 */
2450
2451 /* Use the inner frame for the reference box. If we don't have an inner
2452 * frame we use empty dimensions to allow us to continue (and percentage
2453 * values in the transform will simply give broken results).
2454 * TODO: There is no good way for us to represent the case where there's no
2455 * frame, which is problematic. The reason is that when we have percentage
2456 * transforms, there are a total of four stored matrix entries that influence
2457 * the transform based on the size of the element. However, this poses a
2458 * problem, because only two of these values can be explicitly referenced
2459 * using the named transforms. Until a real solution is found, we'll just
2460 * use this approach.
2461 */
2462 nsStyleTransformMatrix::TransformReferenceBox refBox(mInnerFrame, nsRect());
2463 gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
2464 aTransform, refBox, float(mozilla::AppUnitsPerCSSPixel()));
2465
2466 return MatrixToCSSValue(matrix);
2467 }
2468
DoGetMask()2469 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetMask() {
2470 const nsStyleSVGReset* svg = StyleSVGReset();
2471 const nsStyleImageLayers::Layer& firstLayer = svg->mMask.mLayers[0];
2472
2473 // Mask is now a shorthand, but it used to be a longhand, so that we
2474 // need to support computed style for the cases where it used to be
2475 // a longhand.
2476 if (svg->mMask.mImageCount > 1 ||
2477 firstLayer.mClip != StyleGeometryBox::BorderBox ||
2478 firstLayer.mOrigin != StyleGeometryBox::BorderBox ||
2479 firstLayer.mComposite != StyleMaskComposite::Add ||
2480 firstLayer.mMaskMode != StyleMaskMode::MatchSource ||
2481 firstLayer.mPosition != Position::FromPercentage(0.0f) ||
2482 !firstLayer.mRepeat.IsInitialValue() ||
2483 !firstLayer.mSize.IsInitialValue() ||
2484 !(firstLayer.mImage.IsNone() || firstLayer.mImage.IsUrl())) {
2485 return nullptr;
2486 }
2487
2488 RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
2489
2490 SetValueToURLValue(firstLayer.mImage.GetImageRequestURLValue(), val);
2491
2492 return val.forget();
2493 }
2494
DummyGetter()2495 already_AddRefed<CSSValue> nsComputedDOMStyle::DummyGetter() {
2496 MOZ_CRASH("DummyGetter is not supposed to be invoked");
2497 }
2498
MarkComputedStyleMapDirty(const char * aPref,void * aMap)2499 static void MarkComputedStyleMapDirty(const char* aPref, void* aMap) {
2500 static_cast<ComputedStyleMap*>(aMap)->MarkDirty();
2501 }
2502
ParentChainChanged(nsIContent * aContent)2503 void nsComputedDOMStyle::ParentChainChanged(nsIContent* aContent) {
2504 NS_ASSERTION(mElement == aContent, "didn't we register mElement?");
2505 NS_ASSERTION(mResolvedComputedStyle,
2506 "should have only registered an observer when "
2507 "mResolvedComputedStyle is true");
2508
2509 ClearComputedStyle();
2510 }
2511
2512 /* static */
GetComputedStyleMap()2513 ComputedStyleMap* nsComputedDOMStyle::GetComputedStyleMap() {
2514 static ComputedStyleMap map{};
2515 return ↦
2516 }
2517
2518 static StaticAutoPtr<nsTArray<const char*>> gCallbackPrefs;
2519
2520 /* static */
RegisterPrefChangeCallbacks()2521 void nsComputedDOMStyle::RegisterPrefChangeCallbacks() {
2522 // Note that this will register callbacks for all properties with prefs, not
2523 // just those that are implemented on computed style objects, as it's not
2524 // easy to grab specific property data from ServoCSSPropList.h based on the
2525 // entries iterated in nsComputedDOMStylePropertyList.h.
2526
2527 AutoTArray<const char*, 64> prefs;
2528 for (const auto* p = nsCSSProps::kPropertyPrefTable;
2529 p->mPropID != eCSSProperty_UNKNOWN; p++) {
2530 // Many properties are controlled by the same preference, so de-duplicate
2531 // them before adding observers.
2532 //
2533 // Note: This is done by pointer comparison, which works because the mPref
2534 // members are string literals from the same same translation unit, and are
2535 // therefore de-duplicated by the compiler. On the off chance that we wind
2536 // up with some duplicates with different pointers, though, it's not a bit
2537 // deal.
2538 if (!prefs.ContainsSorted(p->mPref)) {
2539 prefs.InsertElementSorted(p->mPref);
2540 }
2541 }
2542 prefs.AppendElement(nullptr);
2543
2544 MOZ_ASSERT(!gCallbackPrefs);
2545 gCallbackPrefs = new nsTArray<const char*>(std::move(prefs));
2546
2547 Preferences::RegisterCallbacks(MarkComputedStyleMapDirty,
2548 gCallbackPrefs->Elements(),
2549 GetComputedStyleMap());
2550 }
2551
2552 /* static */
UnregisterPrefChangeCallbacks()2553 void nsComputedDOMStyle::UnregisterPrefChangeCallbacks() {
2554 if (!gCallbackPrefs) {
2555 return;
2556 }
2557
2558 Preferences::UnregisterCallbacks(MarkComputedStyleMapDirty,
2559 gCallbackPrefs->Elements(),
2560 GetComputedStyleMap());
2561 gCallbackPrefs = nullptr;
2562 }
2563