tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsComputedDOMStyle.cpp (85217B)


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