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 &map;
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