tor-browser

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

nsLayoutUtils.cpp (378625B)


      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 #include "nsLayoutUtils.h"
      8 
      9 #include <algorithm>
     10 #include <limits>
     11 
     12 #include "ActiveLayerTracker.h"
     13 #include "AnchorPositioningUtils.h"
     14 #include "DisplayItemClip.h"
     15 #include "ImageContainer.h"
     16 #include "ImageOps.h"
     17 #include "ImageRegion.h"
     18 #include "LayoutLogging.h"
     19 #include "MobileViewportManager.h"
     20 #include "RegionBuilder.h"
     21 #include "RetainedDisplayListBuilder.h"
     22 #include "TextDrawTarget.h"
     23 #include "UnitTransforms.h"
     24 #include "ViewportFrame.h"
     25 #include "gfx2DGlue.h"
     26 #include "gfxContext.h"
     27 #include "gfxDrawable.h"
     28 #include "gfxEnv.h"
     29 #include "gfxMatrix.h"
     30 #include "gfxPlatform.h"
     31 #include "gfxRect.h"
     32 #include "gfxTypes.h"
     33 #include "gfxUtils.h"
     34 #include "imgIContainer.h"
     35 #include "imgIRequest.h"
     36 #include "mozilla/AccessibleCaretEventHub.h"
     37 #include "mozilla/Baseline.h"
     38 #include "mozilla/BasicEvents.h"
     39 #include "mozilla/ClearOnShutdown.h"
     40 #include "mozilla/DisplayPortUtils.h"
     41 #include "mozilla/EffectCompositor.h"
     42 #include "mozilla/EffectSet.h"
     43 #include "mozilla/EventDispatcher.h"
     44 #include "mozilla/EventStateManager.h"
     45 #include "mozilla/FloatingPoint.h"
     46 #include "mozilla/IntegerRange.h"
     47 #include "mozilla/Likely.h"
     48 #include "mozilla/LookAndFeel.h"
     49 #include "mozilla/Maybe.h"
     50 #include "mozilla/MemoryReporting.h"
     51 #include "mozilla/PerfStats.h"
     52 #include "mozilla/Preferences.h"
     53 #include "mozilla/PresShell.h"
     54 #include "mozilla/ProfilerLabels.h"
     55 #include "mozilla/ProfilerMarkers.h"
     56 #include "mozilla/RestyleManager.h"
     57 #include "mozilla/SVGImageContext.h"
     58 #include "mozilla/SVGIntegrationUtils.h"
     59 #include "mozilla/SVGTextFrame.h"
     60 #include "mozilla/SVGUtils.h"
     61 #include "mozilla/ScopeExit.h"
     62 #include "mozilla/ScrollContainerFrame.h"
     63 #include "mozilla/ScrollOrigin.h"
     64 #include "mozilla/ServoStyleSet.h"
     65 #include "mozilla/ServoStyleSetInlines.h"
     66 #include "mozilla/StaticPrefs_apz.h"
     67 #include "mozilla/StaticPrefs_browser.h"
     68 #include "mozilla/StaticPrefs_dom.h"
     69 #include "mozilla/StaticPrefs_font.h"
     70 #include "mozilla/StaticPrefs_general.h"
     71 #include "mozilla/StaticPrefs_gfx.h"
     72 #include "mozilla/StaticPrefs_image.h"
     73 #include "mozilla/StaticPrefs_layers.h"
     74 #include "mozilla/StaticPrefs_layout.h"
     75 #include "mozilla/StaticPtr.h"
     76 #include "mozilla/StyleAnimationValue.h"
     77 #include "mozilla/ViewportFrame.h"
     78 #include "mozilla/ViewportUtils.h"
     79 #include "mozilla/WheelHandlingHelper.h"  // for WheelHandlingUtils
     80 #include "mozilla/dom/AnonymousContent.h"
     81 #include "mozilla/dom/BrowserChild.h"
     82 #include "mozilla/dom/CanvasUtils.h"
     83 #include "mozilla/dom/CharacterDataBuffer.h"
     84 #include "mozilla/dom/DOMRect.h"
     85 #include "mozilla/dom/DOMStringList.h"
     86 #include "mozilla/dom/Document.h"
     87 #include "mozilla/dom/DocumentInlines.h"
     88 #include "mozilla/dom/Element.h"
     89 #include "mozilla/dom/HTMLBodyElement.h"
     90 #include "mozilla/dom/HTMLCanvasElement.h"
     91 #include "mozilla/dom/HTMLImageElement.h"
     92 #include "mozilla/dom/HTMLMediaElementBinding.h"
     93 #include "mozilla/dom/HTMLVideoElement.h"
     94 #include "mozilla/dom/ImageBitmap.h"
     95 #include "mozilla/dom/InspectorFontFace.h"
     96 #include "mozilla/dom/InteractiveWidget.h"
     97 #include "mozilla/dom/KeyframeEffect.h"
     98 #include "mozilla/dom/SVGViewportElement.h"
     99 #include "mozilla/dom/UIEvent.h"
    100 #include "mozilla/dom/VideoFrame.h"
    101 #include "mozilla/dom/VideoFrameBinding.h"
    102 #include "mozilla/gfx/2D.h"
    103 #include "mozilla/gfx/DataSurfaceHelpers.h"
    104 #include "mozilla/gfx/PathHelpers.h"
    105 #include "mozilla/gfx/gfxVars.h"
    106 #include "mozilla/glean/GfxMetrics.h"
    107 #include "mozilla/glean/LayoutMetrics.h"
    108 #include "mozilla/intl/BidiEmbeddingLevel.h"
    109 #include "mozilla/layers/APZCCallbackHelper.h"
    110 #include "mozilla/layers/APZPublicUtils.h"  // for apz::CalculatePendingDisplayPort
    111 #include "mozilla/layers/CompositorBridgeChild.h"
    112 #include "mozilla/layers/PAPZ.h"
    113 #include "mozilla/layers/StackingContextHelper.h"
    114 #include "mozilla/layers/WebRenderLayerManager.h"
    115 #include "nsAnimationManager.h"
    116 #include "nsAtom.h"
    117 #include "nsBidiPresUtils.h"
    118 #include "nsBlockFrame.h"
    119 #include "nsCOMPtr.h"
    120 #include "nsCSSAnonBoxes.h"
    121 #include "nsCSSColorUtils.h"
    122 #include "nsCSSFrameConstructor.h"
    123 #include "nsCSSProps.h"
    124 #include "nsCSSPseudoElements.h"
    125 #include "nsCSSRendering.h"
    126 #include "nsCanvasFrame.h"
    127 #include "nsCaret.h"
    128 #include "nsCharTraits.h"
    129 #include "nsComputedDOMStyle.h"
    130 #include "nsContentUtils.h"
    131 #include "nsDisplayList.h"
    132 #include "nsFieldSetFrame.h"
    133 #include "nsFlexContainerFrame.h"
    134 #include "nsFontInflationData.h"
    135 #include "nsFontMetrics.h"
    136 #include "nsFrameList.h"
    137 #include "nsFrameSelection.h"
    138 #include "nsGenericHTMLElement.h"
    139 #include "nsGkAtoms.h"
    140 #include "nsICanvasRenderingContextInternal.h"
    141 #include "nsIContent.h"
    142 #include "nsIContentInlines.h"
    143 #include "nsIDocShell.h"
    144 #include "nsIDocumentViewer.h"
    145 #include "nsIFrameInlines.h"
    146 #include "nsIImageLoadingContent.h"
    147 #include "nsIInterfaceRequestorUtils.h"
    148 #include "nsIWidget.h"
    149 #include "nsListControlFrame.h"
    150 #include "nsMenuPopupFrame.h"
    151 #include "nsPIDOMWindow.h"
    152 #include "nsPlaceholderFrame.h"
    153 #include "nsPresContext.h"
    154 #include "nsPresContextInlines.h"
    155 #include "nsRefreshDriver.h"
    156 #include "nsRegion.h"
    157 #include "nsStyleConsts.h"
    158 #include "nsStyleStructInlines.h"
    159 #include "nsStyleTransformMatrix.h"
    160 #include "nsSubDocumentFrame.h"
    161 #include "nsTArray.h"
    162 #include "nsTHashMap.h"
    163 #include "nsTableWrapperFrame.h"
    164 #include "nsTextFrame.h"
    165 #include "nsTransitionManager.h"
    166 #include "nsXULPopupManager.h"
    167 #include "prenv.h"
    168 
    169 // Make sure getpid() works.
    170 #ifdef XP_WIN
    171 #  include <process.h>
    172 #  define getpid _getpid
    173 #else
    174 #  include <unistd.h>
    175 #endif
    176 
    177 using namespace mozilla;
    178 using namespace mozilla::dom;
    179 using namespace mozilla::image;
    180 using namespace mozilla::layers;
    181 using namespace mozilla::layout;
    182 using namespace mozilla::gfx;
    183 using mozilla::dom::HTMLMediaElement_Binding::HAVE_METADATA;
    184 using mozilla::dom::HTMLMediaElement_Binding::HAVE_NOTHING;
    185 
    186 typedef ScrollableLayerGuid::ViewID ViewID;
    187 typedef nsStyleTransformMatrix::TransformReferenceBox TransformReferenceBox;
    188 
    189 static ViewID sScrollIdCounter = ScrollableLayerGuid::START_SCROLL_ID;
    190 
    191 typedef nsTHashMap<nsUint64HashKey, nsIContent*> ContentMap;
    192 static StaticAutoPtr<ContentMap> sContentMap;
    193 
    194 static ContentMap& GetContentMap() {
    195  if (!sContentMap) {
    196    sContentMap = new ContentMap();
    197  }
    198  return *sContentMap;
    199 }
    200 
    201 template <typename TestType>
    202 static bool HasMatchingAnimations(EffectSet& aEffects, TestType&& aTest) {
    203  for (KeyframeEffect* effect : aEffects) {
    204    if (!effect->GetAnimation() || !effect->GetAnimation()->IsRelevant()) {
    205      continue;
    206    }
    207 
    208    if (aTest(*effect, aEffects)) {
    209      return true;
    210    }
    211  }
    212 
    213  return false;
    214 }
    215 
    216 template <typename TestType>
    217 static bool HasMatchingAnimations(const nsIFrame* aFrame,
    218                                  const nsCSSPropertyIDSet& aPropertySet,
    219                                  TestType&& aTest) {
    220  MOZ_ASSERT(aFrame);
    221 
    222  if (!aFrame->MayHaveOpacityAnimation() &&
    223      aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::OpacityProperties())) {
    224    return false;
    225  }
    226 
    227  if (!aFrame->MayHaveTransformAnimation() &&
    228      aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::TransformLikeProperties())) {
    229    return false;
    230  }
    231 
    232  EffectSet* effectSet = EffectSet::GetForFrame(aFrame, aPropertySet);
    233  if (!effectSet) {
    234    return false;
    235  }
    236 
    237  return HasMatchingAnimations(*effectSet, aTest);
    238 }
    239 
    240 /* static */
    241 bool nsLayoutUtils::HasAnimationOfPropertySet(
    242    const nsIFrame* aFrame, const nsCSSPropertyIDSet& aPropertySet) {
    243  return HasMatchingAnimations(
    244      aFrame, aPropertySet,
    245      [&aPropertySet](KeyframeEffect& aEffect, const EffectSet&) {
    246        return aEffect.HasAnimationOfPropertySet(aPropertySet);
    247      });
    248 }
    249 
    250 /* static */
    251 bool nsLayoutUtils::HasAnimationOfPropertySet(
    252    const nsIFrame* aFrame, const nsCSSPropertyIDSet& aPropertySet,
    253    EffectSet* aEffectSet) {
    254  MOZ_ASSERT(
    255      !aEffectSet || EffectSet::GetForFrame(aFrame, aPropertySet) == aEffectSet,
    256      "The EffectSet, if supplied, should match what we would otherwise fetch");
    257 
    258  if (!aEffectSet) {
    259    return nsLayoutUtils::HasAnimationOfPropertySet(aFrame, aPropertySet);
    260  }
    261 
    262  if (!aEffectSet->MayHaveTransformAnimation() &&
    263      aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::TransformLikeProperties())) {
    264    return false;
    265  }
    266 
    267  if (!aEffectSet->MayHaveOpacityAnimation() &&
    268      aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::OpacityProperties())) {
    269    return false;
    270  }
    271 
    272  return HasMatchingAnimations(
    273      *aEffectSet,
    274      [&aPropertySet](KeyframeEffect& aEffect, const EffectSet& aEffectSet) {
    275        return aEffect.HasAnimationOfPropertySet(aPropertySet);
    276      });
    277 }
    278 
    279 /* static */
    280 bool nsLayoutUtils::HasAnimationOfTransformAndMotionPath(
    281    const nsIFrame* aFrame) {
    282  auto returnValue = [&]() -> bool {
    283    return nsLayoutUtils::HasAnimationOfPropertySet(
    284               aFrame,
    285               nsCSSPropertyIDSet{eCSSProperty_transform,
    286                                  eCSSProperty_translate, eCSSProperty_rotate,
    287                                  eCSSProperty_scale,
    288                                  eCSSProperty_offset_path}) ||
    289           (!aFrame->StyleDisplay()->mOffsetPath.IsNone() &&
    290            nsLayoutUtils::HasAnimationOfPropertySet(
    291                aFrame, nsCSSPropertyIDSet::MotionPathProperties()));
    292  };
    293 
    294  if (!aFrame->MayHaveTransformAnimation()) {
    295    MOZ_ASSERT(!returnValue());
    296    return false;
    297  }
    298  return returnValue();
    299 }
    300 
    301 /* static */
    302 bool nsLayoutUtils::HasEffectiveAnimation(
    303    const nsIFrame* aFrame, const nsCSSPropertyIDSet& aPropertySet) {
    304  return HasMatchingAnimations(
    305      aFrame, aPropertySet,
    306      [&aPropertySet](KeyframeEffect& aEffect, const EffectSet& aEffectSet) {
    307        return aEffect.HasEffectiveAnimationOfPropertySet(aPropertySet,
    308                                                          aEffectSet);
    309      });
    310 }
    311 
    312 /* static */
    313 nsCSSPropertyIDSet nsLayoutUtils::GetAnimationPropertiesForCompositor(
    314    const nsIFrame* aStyleFrame) {
    315  nsCSSPropertyIDSet properties;
    316 
    317  // We fetch the effects for the style frame here since this method is called
    318  // by RestyleManager::AddLayerChangesForAnimation which takes care to apply
    319  // the relevant hints to the primary frame as needed.
    320  EffectSet* effects = EffectSet::GetForStyleFrame(aStyleFrame);
    321  if (!effects) {
    322    return properties;
    323  }
    324 
    325  AnimationPerformanceWarning::Type warning;
    326  if (!EffectCompositor::AllowCompositorAnimationsOnFrame(aStyleFrame,
    327                                                          warning)) {
    328    return properties;
    329  }
    330 
    331  for (const KeyframeEffect* effect : *effects) {
    332    properties |= effect->GetPropertiesForCompositor(*effects, aStyleFrame);
    333  }
    334 
    335  // If properties only have motion-path properties, we have to make sure they
    336  // have effects. i.e. offset-path is not none or we have offset-path
    337  // animations.
    338  if (properties.IsSubsetOf(nsCSSPropertyIDSet::MotionPathProperties()) &&
    339      !properties.HasProperty(eCSSProperty_offset_path) &&
    340      aStyleFrame->StyleDisplay()->mOffsetPath.IsNone()) {
    341    properties.Empty();
    342  }
    343 
    344  return properties;
    345 }
    346 
    347 static float GetSuitableScale(float aMaxScale, float aMinScale,
    348                              nscoord aVisibleDimension,
    349                              nscoord aDisplayDimension) {
    350  float displayVisibleRatio =
    351      float(aDisplayDimension) / float(aVisibleDimension);
    352  // We want to rasterize based on the largest scale used during the
    353  // transform animation, unless that would make us rasterize something
    354  // larger than the screen.  But we never want to go smaller than the
    355  // minimum scale over the animation.
    356  if (FuzzyEqualsMultiplicative(displayVisibleRatio, aMaxScale, .01f)) {
    357    // Using aMaxScale may make us rasterize something a fraction larger than
    358    // the screen. However, if aMaxScale happens to be the final scale of a
    359    // transform animation it is better to use aMaxScale so that for the
    360    // fraction of a second before we delayerize the composited texture it has
    361    // a better chance of being pixel aligned and composited without resampling
    362    // (avoiding visually clunky delayerization).
    363    return aMaxScale;
    364  }
    365  return std::clamp(displayVisibleRatio, aMinScale, aMaxScale);
    366 }
    367 
    368 // The first value in this pair is the min scale, and the second one is the max
    369 // scale.
    370 using MinAndMaxScale = std::pair<MatrixScales, MatrixScales>;
    371 
    372 static inline void UpdateMinMaxScale(const nsIFrame* aFrame,
    373                                     const AnimationValue& aValue,
    374                                     MinAndMaxScale& aMinAndMaxScale) {
    375  MatrixScales size = aValue.GetScaleValue(aFrame);
    376  MatrixScales& minScale = aMinAndMaxScale.first;
    377  MatrixScales& maxScale = aMinAndMaxScale.second;
    378 
    379  minScale = Min(minScale, size);
    380  maxScale = Max(maxScale, size);
    381 }
    382 
    383 // The final transform matrix is calculated by merging the final results of each
    384 // transform-like properties, so do the scale factors. In other words, the
    385 // potential min/max scales could be gotten by multiplying the max/min scales of
    386 // each properties.
    387 //
    388 // For example, there is an animation:
    389 //   from { "transform: scale(1, 1)", "scale: 3, 3" };
    390 //   to   { "transform: scale(2, 2)", "scale: 1, 1" };
    391 //
    392 // the min scale is (1, 1) * (1, 1) = (1, 1), and
    393 // The max scale is (2, 2) * (3, 3) = (6, 6).
    394 // This means we multiply the min/max scale factor of transform property and the
    395 // min/max scale factor of scale property to get the final max/min scale factor.
    396 static Array<MinAndMaxScale, 2> GetMinAndMaxScaleForAnimationProperty(
    397    const nsIFrame* aFrame,
    398    const nsTArray<RefPtr<dom::Animation>>& aAnimations) {
    399  // We use a fixed array to store the min/max scales for each property.
    400  // The first element in the array is for eCSSProperty_transform, and the
    401  // second one is for eCSSProperty_scale.
    402  const MinAndMaxScale defaultValue =
    403      std::make_pair(MatrixScales(std::numeric_limits<float>::max(),
    404                                  std::numeric_limits<float>::max()),
    405                     MatrixScales(std::numeric_limits<float>::min(),
    406                                  std::numeric_limits<float>::min()));
    407  Array<MinAndMaxScale, 2> minAndMaxScales(defaultValue, defaultValue);
    408 
    409  for (dom::Animation* anim : aAnimations) {
    410    // This method is only expected to be passed animations that are running on
    411    // the compositor and we only pass playing animations to the compositor,
    412    // which are, by definition, "relevant" animations (animations that are
    413    // not yet finished or which are filling forwards).
    414    MOZ_ASSERT(anim->IsRelevant());
    415 
    416    const dom::KeyframeEffect* effect =
    417        anim->GetEffect() ? anim->GetEffect()->AsKeyframeEffect() : nullptr;
    418    MOZ_ASSERT(effect, "A playing animation should have a keyframe effect");
    419    for (const AnimationProperty& prop : effect->Properties()) {
    420      if (prop.mProperty.mId != eCSSProperty_transform &&
    421          prop.mProperty.mId != eCSSProperty_scale) {
    422        continue;
    423      }
    424 
    425      // 0: eCSSProperty_transform.
    426      // 1: eCSSProperty_scale.
    427      MinAndMaxScale& scales =
    428          minAndMaxScales[prop.mProperty.mId == eCSSProperty_transform ? 0 : 1];
    429 
    430      // We need to factor in the scale of the base style if the base style
    431      // will be used on the compositor.
    432      const AnimationValue& baseStyle = effect->BaseStyle(prop.mProperty);
    433      if (!baseStyle.IsNull()) {
    434        UpdateMinMaxScale(aFrame, baseStyle, scales);
    435      }
    436 
    437      for (const AnimationPropertySegment& segment : prop.mSegments) {
    438        // In case of add or accumulate composite, StyleAnimationValue does
    439        // not have a valid value.
    440        if (segment.HasReplaceableFromValue()) {
    441          UpdateMinMaxScale(aFrame, segment.mFromValue, scales);
    442        }
    443 
    444        if (segment.HasReplaceableToValue()) {
    445          UpdateMinMaxScale(aFrame, segment.mToValue, scales);
    446        }
    447      }
    448    }
    449  }
    450 
    451  return minAndMaxScales;
    452 }
    453 
    454 MatrixScales nsLayoutUtils::ComputeSuitableScaleForAnimation(
    455    const nsIFrame* aFrame, const nsSize& aVisibleSize,
    456    const nsSize& aDisplaySize) {
    457  const nsTArray<RefPtr<dom::Animation>> compositorAnimations =
    458      EffectCompositor::GetAnimationsForCompositor(
    459          aFrame,
    460          nsCSSPropertyIDSet{eCSSProperty_transform, eCSSProperty_scale});
    461 
    462  if (compositorAnimations.IsEmpty()) {
    463    return MatrixScales();
    464  }
    465 
    466  const Array<MinAndMaxScale, 2> minAndMaxScales =
    467      GetMinAndMaxScaleForAnimationProperty(aFrame, compositorAnimations);
    468 
    469  // This might cause an issue if users use std::numeric_limits<float>::min()
    470  // (or max()) as the scale value. However, in this case, we may render an
    471  // extreme small (or large) element, so this may not be a problem. If so,
    472  // please fix this.
    473  MatrixScales maxScale(std::numeric_limits<float>::min(),
    474                        std::numeric_limits<float>::min());
    475  MatrixScales minScale(std::numeric_limits<float>::max(),
    476                        std::numeric_limits<float>::max());
    477 
    478  auto isUnset = [](const MatrixScales& aMax, const MatrixScales& aMin) {
    479    return aMax.xScale == std::numeric_limits<float>::min() &&
    480           aMax.yScale == std::numeric_limits<float>::min() &&
    481           aMin.xScale == std::numeric_limits<float>::max() &&
    482           aMin.yScale == std::numeric_limits<float>::max();
    483  };
    484 
    485  // Iterate the slots to get the final scale value.
    486  for (const auto& pair : minAndMaxScales) {
    487    const MatrixScales& currMinScale = pair.first;
    488    const MatrixScales& currMaxScale = pair.second;
    489 
    490    if (isUnset(currMaxScale, currMinScale)) {
    491      // We don't have this animation property, so skip.
    492      continue;
    493    }
    494 
    495    if (isUnset(maxScale, minScale)) {
    496      // Initialize maxScale and minScale.
    497      maxScale = currMaxScale;
    498      minScale = currMinScale;
    499    } else {
    500      // The scale factors of each transform-like property should be multiplied
    501      // by others because we merge their sampled values as a final matrix by
    502      // matrix multiplication, so here we multiply the scale factors by the
    503      // previous one to get the possible max and min scale factors.
    504      maxScale = maxScale * currMaxScale;
    505      minScale = minScale * currMinScale;
    506    }
    507  }
    508 
    509  if (isUnset(maxScale, minScale)) {
    510    // We didn't encounter any transform-like property.
    511    return MatrixScales();
    512  }
    513 
    514  return MatrixScales(
    515      GetSuitableScale(maxScale.xScale, minScale.xScale, aVisibleSize.width,
    516                       aDisplaySize.width),
    517      GetSuitableScale(maxScale.yScale, minScale.yScale, aVisibleSize.height,
    518                       aDisplaySize.height));
    519 }
    520 
    521 bool nsLayoutUtils::AreAsyncAnimationsEnabled() {
    522  return StaticPrefs::layers_offmainthreadcomposition_async_animations() &&
    523         gfxPlatform::OffMainThreadCompositingEnabled();
    524 }
    525 
    526 bool nsLayoutUtils::AreRetainedDisplayListsEnabled() {
    527 #ifdef MOZ_WIDGET_ANDROID
    528  return StaticPrefs::layout_display_list_retain();
    529 #else
    530  if (XRE_IsContentProcess()) {
    531    return StaticPrefs::layout_display_list_retain();
    532  }
    533 
    534  if (XRE_IsE10sParentProcess()) {
    535    return StaticPrefs::layout_display_list_retain_chrome();
    536  }
    537 
    538  // Retained display lists require e10s.
    539  return false;
    540 #endif
    541 }
    542 
    543 bool nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(nsIFrame* aFrame) {
    544  return GetRetainedDisplayListBuilder(aFrame) != nullptr;
    545 }
    546 
    547 RetainedDisplayListBuilder* nsLayoutUtils::GetRetainedDisplayListBuilder(
    548    nsIFrame* aFrame) {
    549  MOZ_ASSERT(aFrame);
    550  MOZ_ASSERT(aFrame->PresShell());
    551 
    552  // Use the pres shell root frame to get the display root frame. This skips
    553  // the early exit in |nsLayoutUtils::GetDisplayRootFrame()| for popup frames.
    554  const nsIFrame* rootFrame = aFrame->PresShell()->GetRootFrame();
    555  if (!rootFrame) {
    556    return nullptr;
    557  }
    558 
    559  const nsIFrame* displayRootFrame = GetDisplayRootFrame(rootFrame);
    560  MOZ_ASSERT(displayRootFrame);
    561 
    562  return displayRootFrame->GetProperty(RetainedDisplayListBuilder::Cached());
    563 }
    564 
    565 void nsLayoutUtils::UnionChildOverflow(nsIFrame* aFrame,
    566                                       OverflowAreas& aOverflowAreas,
    567                                       FrameChildListIDs aSkipChildLists) {
    568  for (const auto& [list, listID] : aFrame->ChildLists()) {
    569    if (aSkipChildLists.contains(listID)) {
    570      continue;
    571    }
    572    for (nsIFrame* child : list) {
    573      aOverflowAreas.UnionWith(
    574          child->GetActualAndNormalOverflowAreasRelativeToParent());
    575    }
    576  }
    577 }
    578 
    579 static void DestroyViewID(void* aObject, nsAtom* aPropertyName,
    580                          void* aPropertyValue, void* aData) {
    581  ViewID* id = static_cast<ViewID*>(aPropertyValue);
    582  GetContentMap().Remove(*id);
    583  delete id;
    584 }
    585 
    586 /**
    587 * A namespace class for static layout utilities.
    588 */
    589 
    590 bool nsLayoutUtils::FindIDFor(const nsIContent* aContent, ViewID* aOutViewId) {
    591  void* scrollIdProperty = aContent->GetProperty(nsGkAtoms::RemoteId);
    592  if (scrollIdProperty) {
    593    *aOutViewId = *static_cast<ViewID*>(scrollIdProperty);
    594    return true;
    595  }
    596  return false;
    597 }
    598 
    599 ViewID nsLayoutUtils::FindOrCreateIDFor(nsIContent* aContent) {
    600  ViewID scrollId;
    601 
    602  if (!FindIDFor(aContent, &scrollId)) {
    603    scrollId = sScrollIdCounter++;
    604    aContent->SetProperty(nsGkAtoms::RemoteId, new ViewID(scrollId),
    605                          DestroyViewID);
    606    GetContentMap().InsertOrUpdate(scrollId, aContent);
    607  }
    608 
    609  return scrollId;
    610 }
    611 
    612 nsIContent* nsLayoutUtils::FindContentFor(ViewID aId) {
    613  MOZ_ASSERT(aId != ScrollableLayerGuid::NULL_SCROLL_ID,
    614             "Cannot find a content element in map for null IDs.");
    615  nsIContent* content;
    616  bool exists = GetContentMap().Get(aId, &content);
    617 
    618  if (exists) {
    619    return content;
    620  } else {
    621    return nullptr;
    622  }
    623 }
    624 
    625 nsIFrame* nsLayoutUtils::GetScrollContainerFrameFromContent(
    626    nsIContent* aContent) {
    627  nsIFrame* frame = aContent->GetPrimaryFrame();
    628  if (aContent->OwnerDoc()->GetRootElement() == aContent) {
    629    PresShell* presShell = frame ? frame->PresShell() : nullptr;
    630    if (!presShell) {
    631      presShell = aContent->OwnerDoc()->GetPresShell();
    632    }
    633    // We want the scroll container frame, the root scroll frame differs from
    634    // all others in that the primary frame is not the scroll frame.
    635    nsIFrame* rootScrollContainerFrame =
    636        presShell ? presShell->GetRootScrollContainerFrame() : nullptr;
    637    if (rootScrollContainerFrame) {
    638      frame = rootScrollContainerFrame;
    639    }
    640  }
    641  return frame;
    642 }
    643 
    644 ScrollContainerFrame* nsLayoutUtils::FindScrollContainerFrameFor(
    645    nsIContent* aContent) {
    646  nsIFrame* scrollContainerFrame = GetScrollContainerFrameFromContent(aContent);
    647  return scrollContainerFrame ? scrollContainerFrame->GetScrollTargetFrame()
    648                              : nullptr;
    649 }
    650 
    651 ScrollContainerFrame* nsLayoutUtils::FindScrollContainerFrameFor(ViewID aId) {
    652  nsIContent* content = FindContentFor(aId);
    653  if (!content) {
    654    return nullptr;
    655  }
    656 
    657  return FindScrollContainerFrameFor(content);
    658 }
    659 
    660 ViewID nsLayoutUtils::FindIDForScrollContainerFrame(
    661    ScrollContainerFrame* aScrollContainerFrame) {
    662  if (!aScrollContainerFrame) {
    663    return ScrollableLayerGuid::NULL_SCROLL_ID;
    664  }
    665 
    666  nsIContent* scrollContent = aScrollContainerFrame->GetContent();
    667 
    668  ScrollableLayerGuid::ViewID scrollId;
    669  if (scrollContent && nsLayoutUtils::FindIDFor(scrollContent, &scrollId)) {
    670    return scrollId;
    671  }
    672 
    673  return ScrollableLayerGuid::NULL_SCROLL_ID;
    674 }
    675 
    676 bool nsLayoutUtils::UsesAsyncScrolling(nsIFrame* aFrame) {
    677 #ifdef MOZ_WIDGET_ANDROID
    678  // We always have async scrolling for Android.
    679  return true;
    680 #else
    681  return AsyncPanZoomEnabled(aFrame);
    682 #endif
    683 }
    684 
    685 bool nsLayoutUtils::AsyncPanZoomEnabled(const nsIFrame* aFrame) {
    686  // We use this as a shortcut, since if the compositor will never use APZ,
    687  // no widget will either.
    688  if (!gfxPlatform::AsyncPanZoomEnabled()) {
    689    return false;
    690  }
    691 
    692  const nsIFrame* frame = nsLayoutUtils::GetDisplayRootFrame(aFrame);
    693  nsIWidget* widget = frame->GetNearestWidget();
    694  if (!widget) {
    695    return false;
    696  }
    697  return widget->AsyncPanZoomEnabled();
    698 }
    699 
    700 bool nsLayoutUtils::AllowZoomingForDocument(
    701    const mozilla::dom::Document* aDocument) {
    702  if (aDocument->GetPresShell() &&
    703      !aDocument->GetPresShell()->AsyncPanZoomEnabled()) {
    704    return false;
    705  }
    706  // True if we allow zooming for all documents on this platform, or if we are
    707  // in RDM.
    708  BrowsingContext* bc = aDocument->GetBrowsingContext();
    709  return StaticPrefs::apz_allow_zooming() || (bc && bc->InRDMPane());
    710 }
    711 
    712 static bool HasVisibleAnonymousContents(Document* aDoc) {
    713  for (RefPtr<AnonymousContent>& ac : aDoc->GetAnonymousContents()) {
    714    // We check to see if the anonymous content node has a frame. If it doesn't,
    715    // that means that's not visible to the user because e.g. it's display:none.
    716    // For now we assume that if it has a frame, it is visible. We might be able
    717    // to refine this further by adding complexity if it turns out this
    718    // condition results in a lot of false positives.
    719    if (ac->Host()->GetPrimaryFrame()) {
    720      return true;
    721    }
    722  }
    723  return false;
    724 }
    725 
    726 bool nsLayoutUtils::ShouldDisableApzForElement(nsIContent* aContent) {
    727  if (!aContent) {
    728    return false;
    729  }
    730 
    731  if (aContent->GetProperty(nsGkAtoms::apzDisabled)) {
    732    return true;
    733  }
    734 
    735  Document* doc = aContent->GetComposedDoc();
    736  if (PresShell* rootPresShell =
    737          APZCCallbackHelper::GetRootContentDocumentPresShellForContent(
    738              aContent)) {
    739    if (Document* rootDoc = rootPresShell->GetDocument()) {
    740      nsIFrame* rootScrollContainerFrame =
    741          rootPresShell->GetRootScrollContainerFrame();
    742      nsIContent* rootContent = rootScrollContainerFrame
    743                                    ? rootScrollContainerFrame->GetContent()
    744                                    : rootDoc->GetDocumentElement();
    745      // For the AccessibleCaret and other anonymous contents: disable APZ on
    746      // any scrollable subframes that are not the root scrollframe of a
    747      // document, if the document has any visible anonymous contents.
    748      //
    749      // If we find this is triggering in too many scenarios then we might
    750      // want to tighten this check further. The main use cases for which we
    751      // want to disable APZ as of this writing are listed in bug 1316318.
    752      if (aContent != rootContent && HasVisibleAnonymousContents(rootDoc)) {
    753        return true;
    754      }
    755    }
    756  }
    757 
    758  if (!doc) {
    759    return false;
    760  }
    761 
    762  if (PresShell* presShell = doc->GetPresShell()) {
    763    if (RefPtr<AccessibleCaretEventHub> eventHub =
    764            presShell->GetAccessibleCaretEventHub()) {
    765      // Disable APZ for all elements if AccessibleCaret tells us to do so.
    766      if (eventHub->ShouldDisableApz()) {
    767        return true;
    768      }
    769    }
    770  }
    771 
    772  return StaticPrefs::apz_disable_for_scroll_linked_effects() &&
    773         doc->HasScrollLinkedEffect();
    774 }
    775 
    776 void nsLayoutUtils::NotifyPaintSkipTransaction(ViewID aScrollId) {
    777  if (ScrollContainerFrame* sf =
    778          nsLayoutUtils::FindScrollContainerFrameFor(aScrollId)) {
    779    MOZ_ASSERT(sf && sf->PresShell() &&
    780               !sf->PresShell()->IsResolutionUpdated());
    781    sf->NotifyApzTransaction();
    782  }
    783 }
    784 
    785 void nsLayoutUtils::NotifyApzTransaction(ViewID aScrollId) {
    786  if (ScrollContainerFrame* sf =
    787          nsLayoutUtils::FindScrollContainerFrameFor(aScrollId)) {
    788    sf->NotifyApzTransaction();
    789  }
    790 }
    791 
    792 nsContainerFrame* nsLayoutUtils::LastContinuationWithChild(
    793    nsContainerFrame* aFrame) {
    794  MOZ_ASSERT(aFrame, "NULL frame pointer");
    795  for (auto f = aFrame->LastContinuation(); f; f = f->GetPrevContinuation()) {
    796    for (const auto& childList : f->ChildLists()) {
    797      if (MOZ_LIKELY(!childList.mList.IsEmpty())) {
    798        return static_cast<nsContainerFrame*>(f);
    799      }
    800    }
    801  }
    802  return aFrame;
    803 }
    804 
    805 // static
    806 FrameChildListID nsLayoutUtils::GetChildListNameFor(nsIFrame* aChildFrame) {
    807  FrameChildListID id = FrameChildListID::Principal;
    808 
    809  MOZ_DIAGNOSTIC_ASSERT(!aChildFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));
    810 
    811  if (aChildFrame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
    812    nsIFrame* pif = aChildFrame->GetPrevInFlow();
    813    if (pif->GetParent() == aChildFrame->GetParent()) {
    814      id = FrameChildListID::ExcessOverflowContainers;
    815    } else {
    816      id = FrameChildListID::OverflowContainers;
    817    }
    818  } else {
    819    LayoutFrameType childType = aChildFrame->Type();
    820    if (LayoutFrameType::TableColGroup == childType) {
    821      id = FrameChildListID::ColGroup;
    822    } else {
    823      id = FrameChildListID::Principal;
    824    }
    825  }
    826 
    827 #ifdef DEBUG
    828  // Verify that the frame is actually in that child list or in the
    829  // corresponding overflow list.
    830  nsContainerFrame* parent = aChildFrame->GetParent();
    831  bool found = parent->GetChildList(id).ContainsFrame(aChildFrame);
    832  if (!found) {
    833    found = parent->GetChildList(FrameChildListID::Overflow)
    834                .ContainsFrame(aChildFrame);
    835    MOZ_ASSERT(found, "not in child list");
    836  }
    837 #endif
    838 
    839  return id;
    840 }
    841 
    842 static Element* GetPseudo(const nsIContent* aContent, nsAtom* aPseudoProperty) {
    843  MOZ_ASSERT(aPseudoProperty == nsGkAtoms::beforePseudoProperty ||
    844             aPseudoProperty == nsGkAtoms::afterPseudoProperty ||
    845             aPseudoProperty == nsGkAtoms::markerPseudoProperty ||
    846             aPseudoProperty == nsGkAtoms::backdropPseudoProperty);
    847  if (!aContent->MayHaveAnonymousChildren()) {
    848    return nullptr;
    849  }
    850  return static_cast<Element*>(aContent->GetProperty(aPseudoProperty));
    851 }
    852 
    853 /*static*/
    854 Element* nsLayoutUtils::GetBeforePseudo(const nsIContent* aContent) {
    855  return GetPseudo(aContent, nsGkAtoms::beforePseudoProperty);
    856 }
    857 
    858 /*static*/
    859 nsIFrame* nsLayoutUtils::GetBeforeFrame(const nsIContent* aContent) {
    860  Element* pseudo = GetBeforePseudo(aContent);
    861  return pseudo ? pseudo->GetPrimaryFrame() : nullptr;
    862 }
    863 
    864 /*static*/
    865 Element* nsLayoutUtils::GetAfterPseudo(const nsIContent* aContent) {
    866  return GetPseudo(aContent, nsGkAtoms::afterPseudoProperty);
    867 }
    868 
    869 /*static*/
    870 nsIFrame* nsLayoutUtils::GetAfterFrame(const nsIContent* aContent) {
    871  Element* pseudo = GetAfterPseudo(aContent);
    872  return pseudo ? pseudo->GetPrimaryFrame() : nullptr;
    873 }
    874 
    875 /*static*/
    876 Element* nsLayoutUtils::GetMarkerPseudo(const nsIContent* aContent) {
    877  return GetPseudo(aContent, nsGkAtoms::markerPseudoProperty);
    878 }
    879 
    880 /*static*/
    881 nsIFrame* nsLayoutUtils::GetMarkerFrame(const nsIContent* aContent) {
    882  Element* pseudo = GetMarkerPseudo(aContent);
    883  return pseudo ? pseudo->GetPrimaryFrame() : nullptr;
    884 }
    885 
    886 Element* nsLayoutUtils::GetBackdropPseudo(const nsIContent* aContent) {
    887  return GetPseudo(aContent, nsGkAtoms::backdropPseudoProperty);
    888 }
    889 
    890 nsIFrame* nsLayoutUtils::GetBackdropFrame(const nsIContent* aContent) {
    891  Element* pseudo = GetBackdropPseudo(aContent);
    892  return pseudo ? pseudo->GetPrimaryFrame() : nullptr;
    893 }
    894 
    895 #ifdef ACCESSIBILITY
    896 void nsLayoutUtils::GetMarkerSpokenText(const nsIContent* aContent,
    897                                        nsAString& aText) {
    898  MOZ_ASSERT(aContent && aContent->IsGeneratedContentContainerForMarker());
    899 
    900  aText.Truncate();
    901 
    902  nsIFrame* frame = aContent->GetPrimaryFrame();
    903  if (!frame) {
    904    return;
    905  }
    906 
    907  if (!frame->StyleContent()->NonAltContentItems().IsEmpty()) {
    908    for (nsIFrame* child : frame->PrincipalChildList()) {
    909      nsIFrame::RenderedText text = child->GetRenderedText();
    910      aText += text.mString;
    911    }
    912    return;
    913  }
    914 
    915  if (!frame->StyleList()->mListStyleImage.IsNone()) {
    916    // ::marker is an image, so use default bullet character.
    917    static const char16_t kDiscMarkerString[] = {0x2022, ' ', 0};
    918    aText.AssignLiteral(kDiscMarkerString);
    919    return;
    920  }
    921 
    922  frame->PresContext()
    923      ->FrameConstructor()
    924      ->GetContainStyleScopeManager()
    925      .GetSpokenCounterText(frame, aText);
    926 }
    927 #endif
    928 
    929 const nsIFrame* nsLayoutUtils::GetClosestFrameOfType(const nsIFrame* aFrame,
    930                                                     LayoutFrameType aFrameType,
    931                                                     const nsIFrame* aStopAt) {
    932  for (const nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
    933    if (frame->Type() == aFrameType) {
    934      return frame;
    935    }
    936    if (frame == aStopAt) {
    937      break;
    938    }
    939  }
    940  return nullptr;
    941 }
    942 nsIFrame* nsLayoutUtils::GetClosestFrameOfType(nsIFrame* aFrame,
    943                                               LayoutFrameType aFrameType,
    944                                               const nsIFrame* aStopAt) {
    945  return const_cast<nsIFrame*>(GetClosestFrameOfType(
    946      const_cast<const nsIFrame*>(aFrame), aFrameType, aStopAt));
    947 }
    948 
    949 /* static */
    950 nsIFrame* nsLayoutUtils::GetPageFrame(nsIFrame* aFrame) {
    951  return GetClosestFrameOfType(aFrame, LayoutFrameType::Page);
    952 }
    953 
    954 /* static */
    955 const nsIFrame* nsLayoutUtils::GetPageFrame(const nsIFrame* aFrame) {
    956  return GetClosestFrameOfType(aFrame, LayoutFrameType::Page);
    957 }
    958 
    959 /* static */
    960 nsIFrame* nsLayoutUtils::GetStyleFrame(nsIFrame* aPrimaryFrame) {
    961  MOZ_ASSERT(aPrimaryFrame);
    962  if (const nsTableWrapperFrame* const table = do_QueryFrame(aPrimaryFrame)) {
    963    // The inner table may be null, if aPrimaryFrame is mid-destruction
    964    return table->InnerTableFrame();
    965  }
    966 
    967  return aPrimaryFrame;
    968 }
    969 
    970 const nsIFrame* nsLayoutUtils::GetStyleFrame(const nsIFrame* aPrimaryFrame) {
    971  return nsLayoutUtils::GetStyleFrame(const_cast<nsIFrame*>(aPrimaryFrame));
    972 }
    973 
    974 nsIFrame* nsLayoutUtils::GetStyleFrame(const nsIContent* aContent) {
    975  nsIFrame* frame = aContent->GetPrimaryFrame();
    976  if (!frame) {
    977    return nullptr;
    978  }
    979 
    980  return nsLayoutUtils::GetStyleFrame(frame);
    981 }
    982 
    983 CSSIntCoord nsLayoutUtils::UnthemedScrollbarSize(StyleScrollbarWidth aWidth) {
    984  switch (aWidth) {
    985    case StyleScrollbarWidth::Auto:
    986      return 12;
    987    case StyleScrollbarWidth::Thin:
    988      return 6;
    989    case StyleScrollbarWidth::None:
    990      return 0;
    991  }
    992  return 0;
    993 }
    994 
    995 /* static */
    996 nsIFrame* nsLayoutUtils::GetPrimaryFrameFromStyleFrame(nsIFrame* aStyleFrame) {
    997  nsIFrame* parent = aStyleFrame->GetParent();
    998  return parent && parent->IsTableWrapperFrame() ? parent : aStyleFrame;
    999 }
   1000 
   1001 /* static */
   1002 const nsIFrame* nsLayoutUtils::GetPrimaryFrameFromStyleFrame(
   1003    const nsIFrame* aStyleFrame) {
   1004  return nsLayoutUtils::GetPrimaryFrameFromStyleFrame(
   1005      const_cast<nsIFrame*>(aStyleFrame));
   1006 }
   1007 
   1008 /*static*/
   1009 bool nsLayoutUtils::IsPrimaryStyleFrame(const nsIFrame* aFrame) {
   1010  if (aFrame->IsTableWrapperFrame()) {
   1011    return false;
   1012  }
   1013 
   1014  const nsIFrame* parent = aFrame->GetParent();
   1015  if (const nsTableWrapperFrame* const tableWrapper = do_QueryFrame(parent)) {
   1016    return tableWrapper->InnerTableFrame() == aFrame;
   1017  }
   1018 
   1019  return aFrame->IsPrimaryFrame();
   1020 }
   1021 
   1022 nsIFrame* nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) {
   1023  NS_ASSERTION(aFrame->IsPlaceholderFrame(), "Must have a placeholder here");
   1024  if (aFrame->HasAnyStateBits(PLACEHOLDER_FOR_FLOAT)) {
   1025    nsIFrame* outOfFlowFrame =
   1026        nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
   1027    NS_ASSERTION(outOfFlowFrame && outOfFlowFrame->IsFloating(),
   1028                 "How did that happen?");
   1029    return outOfFlowFrame;
   1030  }
   1031 
   1032  return nullptr;
   1033 }
   1034 
   1035 // static
   1036 nsIFrame* nsLayoutUtils::GetCrossDocParentFrameInProcess(
   1037    const nsIFrame* aFrame, nsPoint* aCrossDocOffset) {
   1038  if (nsIFrame* p = aFrame->GetParent()) {
   1039    return p;
   1040  }
   1041  auto* embedder = aFrame->PresShell()->GetInProcessEmbedderFrame();
   1042  if (embedder && aCrossDocOffset) {
   1043    *aCrossDocOffset += embedder->GetExtraOffset();
   1044  }
   1045  return embedder;
   1046 }
   1047 
   1048 // static
   1049 nsIFrame* nsLayoutUtils::GetCrossDocParentFrame(const nsIFrame* aFrame,
   1050                                                nsPoint* aCrossDocOffset) {
   1051  return GetCrossDocParentFrameInProcess(aFrame, aCrossDocOffset);
   1052 }
   1053 
   1054 // static
   1055 bool nsLayoutUtils::IsProperAncestorFrameCrossDoc(
   1056    const nsIFrame* aAncestorFrame, const nsIFrame* aFrame,
   1057    const nsIFrame* aCommonAncestor) {
   1058  if (aFrame == aAncestorFrame) {
   1059    return false;
   1060  }
   1061  return IsAncestorFrameCrossDoc(aAncestorFrame, aFrame, aCommonAncestor);
   1062 }
   1063 
   1064 // static
   1065 bool nsLayoutUtils::IsProperAncestorFrameCrossDocInProcess(
   1066    const nsIFrame* aAncestorFrame, const nsIFrame* aFrame,
   1067    const nsIFrame* aCommonAncestor) {
   1068  if (aFrame == aAncestorFrame) {
   1069    return false;
   1070  }
   1071  return IsAncestorFrameCrossDocInProcess(aAncestorFrame, aFrame,
   1072                                          aCommonAncestor);
   1073 }
   1074 
   1075 // static
   1076 bool nsLayoutUtils::IsAncestorFrameCrossDoc(const nsIFrame* aAncestorFrame,
   1077                                            const nsIFrame* aFrame,
   1078                                            const nsIFrame* aCommonAncestor) {
   1079  for (const nsIFrame* f = aFrame; f != aCommonAncestor;
   1080       f = GetCrossDocParentFrameInProcess(f)) {
   1081    if (f == aAncestorFrame) {
   1082      return true;
   1083    }
   1084  }
   1085  return aCommonAncestor == aAncestorFrame;
   1086 }
   1087 
   1088 // static
   1089 bool nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
   1090    const nsIFrame* aAncestorFrame, const nsIFrame* aFrame,
   1091    const nsIFrame* aCommonAncestor) {
   1092  for (const nsIFrame* f = aFrame; f != aCommonAncestor;
   1093       f = GetCrossDocParentFrameInProcess(f)) {
   1094    if (f == aAncestorFrame) {
   1095      return true;
   1096    }
   1097  }
   1098  return aCommonAncestor == aAncestorFrame;
   1099 }
   1100 
   1101 // static
   1102 bool nsLayoutUtils::IsProperAncestorFrame(const nsIFrame* aAncestorFrame,
   1103                                          const nsIFrame* aFrame,
   1104                                          const nsIFrame* aCommonAncestor) {
   1105  if (aFrame == aAncestorFrame) {
   1106    return false;
   1107  }
   1108  for (const nsIFrame* f = aFrame; f != aCommonAncestor; f = f->GetParent()) {
   1109    if (f == aAncestorFrame) {
   1110      return true;
   1111    }
   1112  }
   1113  return aCommonAncestor == aAncestorFrame;
   1114 }
   1115 
   1116 // static
   1117 bool nsLayoutUtils::IsProperAncestorFrameConsideringContinuations(
   1118    const nsIFrame* aAncestorFrame, const nsIFrame* aFrame,
   1119    const nsIFrame* aCommonAncestor) {
   1120  MOZ_ASSERT(aAncestorFrame);
   1121  const nsIFrame* ancestorFirstContinuation =
   1122      aAncestorFrame->FirstContinuation();
   1123  if (!aFrame || aFrame->FirstContinuation() == ancestorFirstContinuation) {
   1124    return false;
   1125  }
   1126  const nsIFrame* commonFirstContinuation =
   1127      aCommonAncestor ? aCommonAncestor->FirstContinuation() : nullptr;
   1128  const nsIFrame* f = aFrame;
   1129  for (; f && f->FirstContinuation() != commonFirstContinuation;
   1130       f = f->GetParent()) {
   1131    if (f->FirstContinuation() == ancestorFirstContinuation) {
   1132      return true;
   1133    }
   1134  }
   1135  return f && commonFirstContinuation == ancestorFirstContinuation;
   1136 }
   1137 
   1138 // static
   1139 const nsIFrame* nsLayoutUtils::FillAncestors(
   1140    const nsIFrame* aFrame, const nsIFrame* aStopAtAncestor,
   1141    nsTArray<const nsIFrame*>* aAncestors) {
   1142  const nsIFrame* it = aFrame;
   1143  while (it && it != aStopAtAncestor) {
   1144    aAncestors->AppendElement(it);
   1145    it = nsLayoutUtils::GetParentOrPlaceholderFor(it);
   1146  }
   1147  return it;
   1148 }
   1149 
   1150 // Return true if aFrame1 is after aFrame2
   1151 static bool IsFrameAfter(const nsIFrame* aFrame1, const nsIFrame* aFrame2) {
   1152  const nsIFrame* f = aFrame2;
   1153  do {
   1154    f = f->GetNextSibling();
   1155    if (f == aFrame1) {
   1156      return true;
   1157    }
   1158  } while (f);
   1159  return false;
   1160 }
   1161 
   1162 // static
   1163 int32_t nsLayoutUtils::DoCompareTreePosition(const nsIFrame* aFrame1,
   1164                                             const nsIFrame* aFrame2,
   1165                                             const nsIFrame* aCommonAncestor) {
   1166  MOZ_ASSERT(aFrame1, "aFrame1 must not be null");
   1167  MOZ_ASSERT(aFrame2, "aFrame2 must not be null");
   1168 
   1169  AutoTArray<const nsIFrame*, 20> frame2Ancestors;
   1170  const nsIFrame* nonCommonAncestor =
   1171      FillAncestors(aFrame2, aCommonAncestor, &frame2Ancestors);
   1172  return DoCompareTreePosition(aFrame1, aFrame2, frame2Ancestors,
   1173                               nonCommonAncestor ? aCommonAncestor : nullptr);
   1174 }
   1175 
   1176 // static
   1177 int32_t nsLayoutUtils::DoCompareTreePosition(
   1178    const nsIFrame* aFrame1, const nsIFrame* aFrame2,
   1179    const nsTArray<const nsIFrame*>& aFrame2Ancestors,
   1180    const nsIFrame* aCommonAncestor) {
   1181  MOZ_ASSERT(aFrame1, "aFrame1 must not be null");
   1182  MOZ_ASSERT(aFrame2, "aFrame2 must not be null");
   1183 
   1184  nsPresContext* presContext = aFrame1->PresContext();
   1185  if (presContext != aFrame2->PresContext()) {
   1186    NS_ERROR("no common ancestor at all, different documents");
   1187    return 0;
   1188  }
   1189 
   1190  AutoTArray<const nsIFrame*, 20> frame1Ancestors;
   1191  const nsIFrame* frame1CommonAncestor =
   1192      FillAncestors(aFrame1, aCommonAncestor, &frame1Ancestors);
   1193  if (aCommonAncestor && !frame1CommonAncestor) {
   1194    // We reached the root of the frame tree ... if aCommonAncestor was set,
   1195    // it is wrong. We need to recompute without aCommonAncestor,
   1196    // but computing frame1Ancestors array again can be avoided by
   1197    // swapping the order of the arguments.
   1198    const int32_t oppositeResult =
   1199        DoCompareTreePosition(aFrame2, aFrame1, frame1Ancestors, nullptr);
   1200    return -oppositeResult;
   1201  }
   1202 
   1203  int32_t last1 = int32_t(frame1Ancestors.Length()) - 1;
   1204  int32_t last2 = int32_t(aFrame2Ancestors.Length()) - 1;
   1205  while (last1 >= 0 && last2 >= 0 &&
   1206         frame1Ancestors[last1] == aFrame2Ancestors[last2]) {
   1207    last1--;
   1208    last2--;
   1209  }
   1210 
   1211  if (last1 < 0) {
   1212    if (last2 < 0) {
   1213      NS_ASSERTION(aFrame1 == aFrame2, "internal error?");
   1214      return 0;
   1215    }
   1216    // aFrame1 is an ancestor of aFrame2
   1217    return -1;
   1218  }
   1219 
   1220  if (last2 < 0) {
   1221    // aFrame2 is an ancestor of aFrame1
   1222    return 1;
   1223  }
   1224 
   1225  const nsIFrame* ancestor1 = frame1Ancestors[last1];
   1226  const nsIFrame* ancestor2 = aFrame2Ancestors[last2];
   1227  // Now we should be able to walk sibling chains to find which one is first
   1228  if (IsFrameAfter(ancestor2, ancestor1)) {
   1229    return -1;
   1230  }
   1231  if (IsFrameAfter(ancestor1, ancestor2)) {
   1232    return 1;
   1233  }
   1234  NS_WARNING("Frames were in different child lists???");
   1235  return 0;
   1236 }
   1237 
   1238 // static
   1239 nsIFrame* nsLayoutUtils::GetLastSibling(nsIFrame* aFrame) {
   1240  if (!aFrame) {
   1241    return nullptr;
   1242  }
   1243 
   1244  nsIFrame* next;
   1245  while ((next = aFrame->GetNextSibling()) != nullptr) {
   1246    aFrame = next;
   1247  }
   1248  return aFrame;
   1249 }
   1250 
   1251 // static
   1252 ScrollContainerFrame* nsLayoutUtils::GetScrollContainerFrameFor(
   1253    const nsIFrame* aScrolledFrame) {
   1254  nsIFrame* frame = aScrolledFrame->GetParent();
   1255  ScrollContainerFrame* sf = do_QueryFrame(frame);
   1256  return (sf && sf->GetScrolledFrame() == aScrolledFrame) ? sf : nullptr;
   1257 }
   1258 
   1259 /* static */
   1260 SideBits nsLayoutUtils::GetSideBitsForFixedPositionContent(
   1261    const nsIFrame* aFixedPosFrame) {
   1262  SideBits sides = SideBits::eNone;
   1263  if (aFixedPosFrame) {
   1264    const nsStylePosition* position = aFixedPosFrame->StylePosition();
   1265    const auto params = AnchorPosOffsetResolutionParams::UseCBFrameSize(
   1266        {aFixedPosFrame, StylePositionProperty::Fixed});
   1267    if (!position->GetAnchorResolvedInset(eSideRight, params)->IsAuto()) {
   1268      sides |= SideBits::eRight;
   1269    }
   1270    if (!position->GetAnchorResolvedInset(eSideLeft, params)->IsAuto()) {
   1271      sides |= SideBits::eLeft;
   1272    }
   1273    if (!position->GetAnchorResolvedInset(eSideBottom, params)->IsAuto()) {
   1274      sides |= SideBits::eBottom;
   1275    }
   1276    if (!position->GetAnchorResolvedInset(eSideTop, params)->IsAuto()) {
   1277      sides |= SideBits::eTop;
   1278    }
   1279  }
   1280  return sides;
   1281 }
   1282 
   1283 ScrollableLayerGuid::ViewID nsLayoutUtils::ScrollIdForRootScrollFrame(
   1284    nsPresContext* aPresContext) {
   1285  ViewID id = ScrollableLayerGuid::NULL_SCROLL_ID;
   1286  if (nsIFrame* rootScrollFrame =
   1287          aPresContext->PresShell()->GetRootScrollContainerFrame()) {
   1288    if (nsIContent* content = rootScrollFrame->GetContent()) {
   1289      id = FindOrCreateIDFor(content);
   1290    }
   1291  }
   1292  return id;
   1293 }
   1294 
   1295 // static
   1296 ScrollContainerFrame* nsLayoutUtils::GetNearestScrollableFrameForDirection(
   1297    nsIFrame* aFrame, ScrollDirections aDirections) {
   1298  NS_ASSERTION(
   1299      aFrame, "GetNearestScrollableFrameForDirection expects a non-null frame");
   1300  // FIXME Bug 1714720 : This nearest scroll target is not going to work over
   1301  // process boundaries, in such cases we need to hand over in APZ side.
   1302  for (nsIFrame* f = aFrame; f;
   1303       f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
   1304    ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(f);
   1305    if (scrollContainerFrame) {
   1306      ScrollDirections directions =
   1307          scrollContainerFrame
   1308              ->GetAvailableScrollingDirectionsForUserInputEvents();
   1309      if (aDirections.contains(ScrollDirection::eVertical)) {
   1310        if (directions.contains(ScrollDirection::eVertical)) {
   1311          return scrollContainerFrame;
   1312        }
   1313      }
   1314      if (aDirections.contains(ScrollDirection::eHorizontal)) {
   1315        if (directions.contains(ScrollDirection::eHorizontal)) {
   1316          return scrollContainerFrame;
   1317        }
   1318      }
   1319    }
   1320  }
   1321  return nullptr;
   1322 }
   1323 
   1324 static nsIFrame* GetNearestScrollableOrOverflowClipFrame(
   1325    nsIFrame* aFrame, uint32_t aFlags,
   1326    const std::function<bool(const nsIFrame* aCurrentFrame)>& aClipFrameCheck =
   1327        nullptr) {
   1328  MOZ_ASSERT(
   1329      aFrame,
   1330      "GetNearestScrollableOrOverflowClipFrame expects a non-null frame");
   1331 
   1332  auto GetNextFrame = [aFlags](const nsIFrame* aFrame) -> nsIFrame* {
   1333    return (aFlags & nsLayoutUtils::SCROLLABLE_SAME_DOC)
   1334               ? aFrame->GetParent()
   1335               : nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
   1336  };
   1337 
   1338  // This should be kept in sync with
   1339  // DisplayPortUtils::OneStepInAsyncScrollableAncestorChain,
   1340  // DisplayPortUtils::OneStepInASRChain, DisplayPortUtils::GetASRAncestorFrame,
   1341  // and ShouldAsyncScrollWithAnchorNotCached.
   1342  for (nsIFrame* f = aFrame; f; f = GetNextFrame(f)) {
   1343    if (aClipFrameCheck && aClipFrameCheck(f)) {
   1344      return f;
   1345    }
   1346 
   1347    if ((aFlags & nsLayoutUtils::SCROLLABLE_STOP_AT_PAGE) && f->IsPageFrame()) {
   1348      break;
   1349    }
   1350 
   1351    // TODO: We should also stop at popup frames other than
   1352    // SCROLLABLE_ONLY_ASYNC_SCROLLABLE cases.
   1353    if ((aFlags & nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE) &&
   1354        f->IsMenuPopupFrame()) {
   1355      break;
   1356    }
   1357 
   1358    if (ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(f)) {
   1359      if (aFlags & nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE) {
   1360        if (scrollContainerFrame->WantAsyncScroll()) {
   1361          return f;
   1362        }
   1363      } else {
   1364        ScrollStyles ss = scrollContainerFrame->GetScrollStyles();
   1365        if ((aFlags & nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN) ||
   1366            ss.mVertical != StyleOverflow::Hidden ||
   1367            ss.mHorizontal != StyleOverflow::Hidden) {
   1368          return f;
   1369        }
   1370      }
   1371      if (aFlags & nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT) {
   1372        PresShell* presShell = f->PresShell();
   1373        if (presShell->GetRootScrollContainerFrame() == f &&
   1374            presShell->GetDocument() &&
   1375            presShell->GetDocument()->IsRootDisplayDocument()) {
   1376          return f;
   1377        }
   1378      }
   1379    }
   1380 
   1381    nsIFrame* anchor = nullptr;
   1382    // If the current frame also happens to be fixed then we want to check if it
   1383    // scrolls with its anchor before the special fixed behaviour below because
   1384    // scrolling with its anchor overrides that behaviour and is higher
   1385    // priority.
   1386 
   1387    // This needs to be a while loop because anchors can chain, and we don't
   1388    // want to consider each frame in this loop separately (as a potential
   1389    // scrollable ancestor) because they are all equivalent in the scrollable
   1390    // ancestor chain: they all scroll together. We are not walking up the async
   1391    // scrollable ancestor chain, but rather we are moving sideways. And when
   1392    // we exit this loop we want to move up one because we haven't yet ascended
   1393    // (because of that same reason), and that moving up one will happen either
   1394    // via the special fixed pos behaviour below or the next iteration of the
   1395    // outer for loop.
   1396    if (aFlags & nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE) {
   1397      while ((anchor = AnchorPositioningUtils::GetAnchorThatFrameScrollsWith(
   1398                  f, /* aBuilder */ nullptr))) {
   1399        f = anchor;
   1400      }
   1401    }
   1402 
   1403    if ((aFlags & nsLayoutUtils::SCROLLABLE_FIXEDPOS_FINDS_ROOT) &&
   1404        f->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
   1405        nsLayoutUtils::IsReallyFixedPos(f)) {
   1406      if (nsIFrame* root = f->PresShell()->GetRootScrollContainerFrame()) {
   1407        return root;
   1408      }
   1409    }
   1410  }
   1411  return nullptr;
   1412 }
   1413 
   1414 // static
   1415 ScrollContainerFrame* nsLayoutUtils::GetNearestScrollContainerFrame(
   1416    nsIFrame* aFrame, uint32_t aFlags) {
   1417  nsIFrame* found = GetNearestScrollableOrOverflowClipFrame(aFrame, aFlags);
   1418  if (!found) {
   1419    return nullptr;
   1420  }
   1421 
   1422  return do_QueryFrame(found);
   1423 }
   1424 
   1425 // static
   1426 nsIFrame* nsLayoutUtils::GetNearestOverflowClipFrame(nsIFrame* aFrame) {
   1427  return GetNearestScrollableOrOverflowClipFrame(
   1428      aFrame, SCROLLABLE_SAME_DOC | SCROLLABLE_INCLUDE_HIDDEN,
   1429      [](const nsIFrame* currentFrame) -> bool {
   1430        // In cases of SVG Inner/Outer frames it basically clips descendants
   1431        // unless overflow: visible is explicitly specified.
   1432        LayoutFrameType type = currentFrame->Type();
   1433        return ((type == LayoutFrameType::SVGOuterSVG ||
   1434                 type == LayoutFrameType::SVGInnerSVG) &&
   1435                (currentFrame->StyleDisplay()->mOverflowX !=
   1436                     StyleOverflow::Visible &&
   1437                 currentFrame->StyleDisplay()->mOverflowY !=
   1438                     StyleOverflow::Visible));
   1439      });
   1440 }
   1441 
   1442 // static
   1443 bool nsLayoutUtils::HasPseudoStyle(nsIContent* aContent,
   1444                                   ComputedStyle* aComputedStyle,
   1445                                   PseudoStyleType aPseudoElement,
   1446                                   nsPresContext* aPresContext) {
   1447  MOZ_ASSERT(aPresContext, "Must have a prescontext");
   1448 
   1449  RefPtr<ComputedStyle> pseudoContext;
   1450  if (aContent) {
   1451    pseudoContext = aPresContext->StyleSet()->ProbePseudoElementStyle(
   1452        *aContent->AsElement(), aPseudoElement, nullptr, aComputedStyle);
   1453  }
   1454  return pseudoContext != nullptr;
   1455 }
   1456 
   1457 nsPoint nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(Event* aDOMEvent,
   1458                                                        nsIFrame* aFrame) {
   1459  if (!aDOMEvent) {
   1460    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
   1461  }
   1462  WidgetEvent* event = aDOMEvent->WidgetEventPtr();
   1463  if (!event) {
   1464    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
   1465  }
   1466  return GetEventCoordinatesRelativeTo(event, RelativeTo{aFrame});
   1467 }
   1468 
   1469 static bool IsValidCoordinateTypeEvent(const WidgetEvent* aEvent) {
   1470  if (!aEvent) {
   1471    return false;
   1472  }
   1473  return aEvent->mClass == eMouseEventClass ||
   1474         aEvent->mClass == eMouseScrollEventClass ||
   1475         aEvent->mClass == eWheelEventClass ||
   1476         aEvent->mClass == eDragEventClass ||
   1477         aEvent->mClass == eSimpleGestureEventClass ||
   1478         aEvent->mClass == ePointerEventClass ||
   1479         aEvent->mClass == eGestureNotifyEventClass ||
   1480         aEvent->mClass == eTouchEventClass ||
   1481         aEvent->mClass == eQueryContentEventClass;
   1482 }
   1483 
   1484 nsPoint nsLayoutUtils::GetEventCoordinatesRelativeTo(const WidgetEvent* aEvent,
   1485                                                     RelativeTo aFrame) {
   1486  if (!IsValidCoordinateTypeEvent(aEvent)) {
   1487    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
   1488  }
   1489 
   1490  return GetEventCoordinatesRelativeTo(aEvent, aEvent->AsGUIEvent()->mRefPoint,
   1491                                       aFrame);
   1492 }
   1493 
   1494 nsPoint nsLayoutUtils::GetEventCoordinatesRelativeTo(
   1495    const WidgetEvent* aEvent, const LayoutDeviceIntPoint& aPoint,
   1496    RelativeTo aFrame) {
   1497  if (!aFrame.mFrame) {
   1498    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
   1499  }
   1500 
   1501  nsIWidget* widget = aEvent->AsGUIEvent()->mWidget;
   1502  if (!widget) {
   1503    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
   1504  }
   1505 
   1506  return GetEventCoordinatesRelativeTo(widget, aPoint, aFrame);
   1507 }
   1508 
   1509 nsPoint GetEventCoordinatesRelativeTo(nsIWidget* aWidget,
   1510                                      const LayoutDeviceIntPoint& aPoint,
   1511                                      RelativeTo aFrame) {
   1512  const nsIFrame* frame = aFrame.mFrame;
   1513  if (!frame || !aWidget) {
   1514    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
   1515  }
   1516 
   1517  if (frame->GetOwnWidget() == aWidget) {
   1518    // Special case this cause it happens a lot.
   1519    // This also fixes bug 664707, events in the extra-special case of select
   1520    // dropdown popups that are transformed.
   1521    nsPresContext* presContext = frame->PresContext();
   1522    return nsPoint(presContext->DevPixelsToAppUnits(aPoint.x),
   1523                   presContext->DevPixelsToAppUnits(aPoint.y));
   1524  }
   1525 
   1526  /* If we walk up the frame tree and discover that any of the frames are
   1527   * transformed, we need to do extra work to convert from the global
   1528   * space to the local space.
   1529   */
   1530  const nsIFrame* rootFrame = frame;
   1531  bool transformFound = false;
   1532  for (const nsIFrame* f = frame; f;
   1533       f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
   1534    if (f->IsTransformed() || ViewportUtils::IsZoomedContentRoot(f)) {
   1535      transformFound = true;
   1536    }
   1537 
   1538    rootFrame = f;
   1539  }
   1540 
   1541  auto rootToWidget = nsLayoutUtils::FrameToWidgetOffset(rootFrame, aWidget);
   1542  if (!rootToWidget) {
   1543    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
   1544  }
   1545 
   1546  const int32_t rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel();
   1547  nsPoint widgetToRoot =
   1548      LayoutDeviceIntPoint::ToAppUnits(aPoint, rootAPD) - *rootToWidget;
   1549 
   1550  // Convert from root document app units to app units of the document aFrame
   1551  // is in.
   1552  const int32_t localAPD = frame->PresContext()->AppUnitsPerDevPixel();
   1553  widgetToRoot = widgetToRoot.ScaleToOtherAppUnits(rootAPD, localAPD);
   1554 
   1555  /* If we encountered a transform, we can't do simple arithmetic to figure
   1556   * out how to convert back to aFrame's coordinates and must use the CTM.
   1557   */
   1558  if (transformFound || frame->IsInSVGTextSubtree()) {
   1559    return nsLayoutUtils::TransformRootPointToFrame(ViewportType::Visual,
   1560                                                    aFrame, widgetToRoot);
   1561  }
   1562 
   1563  /* Otherwise, all coordinate systems are translations of one another,
   1564   * so we can just subtract out the difference.
   1565   */
   1566  return widgetToRoot - frame->GetOffsetToCrossDoc(rootFrame);
   1567 }
   1568 
   1569 nsPoint nsLayoutUtils::GetEventCoordinatesRelativeTo(
   1570    nsIWidget* aWidget, const LayoutDeviceIntPoint& aPoint, RelativeTo aFrame) {
   1571  nsPoint result = ::GetEventCoordinatesRelativeTo(aWidget, aPoint, aFrame);
   1572  if (aFrame.mViewportType == ViewportType::Layout && aFrame.mFrame &&
   1573      aFrame.mFrame->Type() == LayoutFrameType::Viewport &&
   1574      aFrame.mFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
   1575    result = ViewportUtils::VisualToLayout(result, aFrame.mFrame->PresShell());
   1576  }
   1577  return result;
   1578 }
   1579 
   1580 nsIFrame* nsLayoutUtils::GetPopupFrameForEventCoordinates(
   1581    nsPresContext* aRootPresContext, const WidgetEvent* aEvent) {
   1582  if (!IsValidCoordinateTypeEvent(aEvent)) {
   1583    return nullptr;
   1584  }
   1585 
   1586  const auto* guiEvent = aEvent->AsGUIEvent();
   1587  return GetPopupFrameForPoint(aRootPresContext, guiEvent->mWidget,
   1588                               guiEvent->mRefPoint);
   1589 }
   1590 
   1591 nsMenuPopupFrame* nsLayoutUtils::GetPopupFrameForPoint(
   1592    nsPresContext* aRootPresContext, nsIWidget* aWidget,
   1593    const mozilla::LayoutDeviceIntPoint& aPoint,
   1594    GetPopupFrameForPointFlags aFlags /* = GetPopupFrameForPointFlags(0) */) {
   1595  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   1596  if (!pm) {
   1597    return nullptr;
   1598  }
   1599  nsTArray<nsMenuPopupFrame*> popups;
   1600  pm->GetVisiblePopups(popups);
   1601  // Search from top to bottom
   1602  for (nsMenuPopupFrame* popup : popups) {
   1603    if (popup->PresContext()->GetRootPresContext() != aRootPresContext) {
   1604      continue;
   1605    }
   1606    if (!popup->ScrollableOverflowRect().Contains(GetEventCoordinatesRelativeTo(
   1607            aWidget, aPoint, RelativeTo{popup}))) {
   1608      continue;
   1609    }
   1610    if (aFlags & GetPopupFrameForPointFlags::OnlyReturnFramesWithWidgets) {
   1611      if (!popup->GetWidget()) {
   1612        continue;
   1613      }
   1614    }
   1615    return popup;
   1616  }
   1617  return nullptr;
   1618 }
   1619 
   1620 void nsLayoutUtils::GetContainerAndOffsetAtEvent(PresShell* aPresShell,
   1621                                                 const WidgetEvent* aEvent,
   1622                                                 nsIContent** aContainer,
   1623                                                 int32_t* aOffset) {
   1624  MOZ_ASSERT(aContainer || aOffset);
   1625 
   1626  if (aContainer) {
   1627    *aContainer = nullptr;
   1628  }
   1629  if (aOffset) {
   1630    *aOffset = 0;
   1631  }
   1632 
   1633  if (!aPresShell) {
   1634    return;
   1635  }
   1636 
   1637  aPresShell->FlushPendingNotifications(FlushType::Layout);
   1638 
   1639  RefPtr<nsPresContext> presContext = aPresShell->GetPresContext();
   1640  if (!presContext) {
   1641    return;
   1642  }
   1643 
   1644  nsIFrame* targetFrame = presContext->EventStateManager()->GetEventTarget();
   1645  if (!targetFrame) {
   1646    return;
   1647  }
   1648 
   1649  WidgetEvent* openingEvent = nullptr;
   1650  // For popupshowing events, redirect via the original mouse event
   1651  // that triggered the popup to open.
   1652  if (aEvent->mMessage == eXULPopupShowing) {
   1653    if (auto* pm = nsXULPopupManager::GetInstance()) {
   1654      if (Event* openingPopupEvent = pm->GetOpeningPopupEvent()) {
   1655        openingEvent = openingPopupEvent->WidgetEventPtr();
   1656      }
   1657    }
   1658  }
   1659 
   1660  nsPoint point = nsLayoutUtils::GetEventCoordinatesRelativeTo(
   1661      openingEvent ? openingEvent : aEvent, RelativeTo{targetFrame});
   1662 
   1663  if (aContainer) {
   1664    // TODO: This result may be useful to change to Selection.  However, this
   1665    //       may return improper node (e.g., native anonymous node) for the
   1666    //       Selection.  Perhaps, this should take Selection optionally and
   1667    //       if it's specified, needs to check if it's proper for the
   1668    //       Selection.
   1669    nsCOMPtr<nsIContent> container =
   1670        targetFrame->GetContentOffsetsFromPoint(point).content;
   1671    if (container && (!container->ChromeOnlyAccess() ||
   1672                      nsContentUtils::CanAccessNativeAnon())) {
   1673      container.forget(aContainer);
   1674    }
   1675  }
   1676  if (aOffset) {
   1677    *aOffset = targetFrame->GetContentOffsetsFromPoint(point).offset;
   1678  }
   1679 }
   1680 
   1681 /**
   1682 * Given a floating point value, constrains its value to be between nscoord_MIN
   1683 * and nscoord_MAX.
   1684 *
   1685 * @param aVal The value to constrain (in/out)
   1686 */
   1687 static void ConstrainToCoordValues(double& aVal) {
   1688  if (aVal <= nscoord_MIN) {
   1689    aVal = nscoord_MIN;
   1690  } else if (aVal >= nscoord_MAX) {
   1691    aVal = nscoord_MAX;
   1692  }
   1693 }
   1694 
   1695 /* static */ void nsLayoutUtils::ConstrainToCoordValues(double& aStart,
   1696                                                        double& aSize) {
   1697  MOZ_ASSERT(std::isnan(aSize) || aSize >= 0);
   1698 
   1699  double max = aStart + aSize;
   1700 
   1701  // Clamp the end points to within nscoord range
   1702  ::ConstrainToCoordValues(aStart);
   1703  ::ConstrainToCoordValues(max);
   1704 
   1705  // Here we try to make sure that the resulting nsRect will continue to cover
   1706  // as much of the area that was covered by the original gfx Rect as possible.
   1707 
   1708  // We must also clamp aSize to {0,nscoord_MAX} since nsRect::Width/Height()
   1709  // can't return a value greater than nscoord_MAX. If aSize is greater than
   1710  // nscoord_MAX then we reduce it to nscoord_MAX while keeping the rect
   1711  // centered:
   1712 
   1713  aSize = max - aStart;
   1714  // If the width if still greater than the max nscoord, then bring both
   1715  // endpoints in by the same amount until it fits.
   1716  if (MOZ_UNLIKELY(std::isnan(aSize))) {
   1717    // Can happen if aStart is -inf and aSize is +inf for example.
   1718    // If either aStart or aSize is NaN on entry to this function then the
   1719    // calculations above this will make the other one NaN too, so this check
   1720    // ensures that we do not return NaN for either value.
   1721    aStart = 0.0f;
   1722    aSize = nscoord_MAX;
   1723  } else if (aSize > nscoord_MAX) {
   1724    double excess = aSize - nscoord_MAX;
   1725    excess /= 2;
   1726 
   1727    aStart += excess;
   1728    aSize = nscoord_MAX;
   1729  } else if (aSize < nscoord_MIN) {
   1730    double excess = aSize - nscoord_MIN;
   1731    excess /= 2;
   1732 
   1733    aStart -= excess;
   1734    aSize = nscoord_MIN;
   1735  }
   1736 }
   1737 
   1738 nsRegion nsLayoutUtils::RoundedRectIntersectRect(
   1739    const nsRect& aRoundedRect, const nsRectCornerRadii& aRadii,
   1740    const nsRect& aContainedRect) {
   1741  // rectFullHeight and rectFullWidth together will approximately contain
   1742  // the total area of the frame minus the rounded corners.
   1743  nsRect rectFullHeight = aRoundedRect;
   1744  nscoord xDiff = std::max(aRadii.TopLeft().width, aRadii.BottomLeft().width);
   1745  rectFullHeight.x += xDiff;
   1746  rectFullHeight.width -=
   1747      std::max(aRadii.TopRight().width, aRadii.BottomRight().width) + xDiff;
   1748  nsRect r1;
   1749  r1.IntersectRect(rectFullHeight, aContainedRect);
   1750 
   1751  nsRect rectFullWidth = aRoundedRect;
   1752  nscoord yDiff = std::max(aRadii.TopLeft().height, aRadii.TopRight().height);
   1753  rectFullWidth.y += yDiff;
   1754  rectFullWidth.height -=
   1755      std::max(aRadii.BottomLeft().height, aRadii.BottomRight().height) + yDiff;
   1756  nsRect r2;
   1757  r2.IntersectRect(rectFullWidth, aContainedRect);
   1758 
   1759  nsRegion result;
   1760  result.Or(r1, r2);
   1761  return result;
   1762 }
   1763 
   1764 nsIntRegion nsLayoutUtils::RoundedRectIntersectIntRect(
   1765    const nsIntRect& aRoundedRect, const RectCornerRadii& aCornerRadii,
   1766    const nsIntRect& aContainedRect) {
   1767  // rectFullHeight and rectFullWidth together will approximately contain
   1768  // the total area of the frame minus the rounded corners.
   1769  nsIntRect rectFullHeight = aRoundedRect;
   1770  uint32_t xDiff =
   1771      std::max(aCornerRadii.TopLeft().width, aCornerRadii.BottomLeft().width);
   1772  rectFullHeight.x += xDiff;
   1773  rectFullHeight.width -= std::max(aCornerRadii.TopRight().width,
   1774                                   aCornerRadii.BottomRight().width) +
   1775                          xDiff;
   1776  nsIntRect r1;
   1777  r1.IntersectRect(rectFullHeight, aContainedRect);
   1778 
   1779  nsIntRect rectFullWidth = aRoundedRect;
   1780  uint32_t yDiff =
   1781      std::max(aCornerRadii.TopLeft().height, aCornerRadii.TopRight().height);
   1782  rectFullWidth.y += yDiff;
   1783  rectFullWidth.height -= std::max(aCornerRadii.BottomLeft().height,
   1784                                   aCornerRadii.BottomRight().height) +
   1785                          yDiff;
   1786  nsIntRect r2;
   1787  r2.IntersectRect(rectFullWidth, aContainedRect);
   1788 
   1789  nsIntRegion result;
   1790  result.Or(r1, r2);
   1791  return result;
   1792 }
   1793 
   1794 // Helper for RoundedRectIntersectsRect.
   1795 static bool CheckCorner(nscoord aXOffset, nscoord aYOffset, nscoord aXRadius,
   1796                        nscoord aYRadius) {
   1797  MOZ_ASSERT(aXOffset > 0 && aYOffset > 0,
   1798             "must not pass nonpositives to CheckCorner");
   1799  MOZ_ASSERT(aXRadius >= 0 && aYRadius >= 0,
   1800             "must not pass negatives to CheckCorner");
   1801 
   1802  // Avoid floating point math unless we're either (1) within the
   1803  // quarter-ellipse area at the rounded corner or (2) outside the
   1804  // rounding.
   1805  if (aXOffset >= aXRadius || aYOffset >= aYRadius) {
   1806    return true;
   1807  }
   1808 
   1809  // Convert coordinates to a unit circle with (0,0) as the center of
   1810  // curvature, and see if we're inside the circle or outside.
   1811  float scaledX = float(aXRadius - aXOffset) / float(aXRadius);
   1812  float scaledY = float(aYRadius - aYOffset) / float(aYRadius);
   1813  return scaledX * scaledX + scaledY * scaledY < 1.0f;
   1814 }
   1815 
   1816 bool nsLayoutUtils::RoundedRectIntersectsRect(const nsRect& aRoundedRect,
   1817                                              const nsRectCornerRadii& aRadii,
   1818                                              const nsRect& aTestRect) {
   1819  if (!aTestRect.Intersects(aRoundedRect)) {
   1820    return false;
   1821  }
   1822 
   1823  // distances from this edge of aRoundedRect to opposite edge of aTestRect,
   1824  // which we know are positive due to the Intersects check above.
   1825  nsMargin insets;
   1826  insets.top = aTestRect.YMost() - aRoundedRect.y;
   1827  insets.right = aRoundedRect.XMost() - aTestRect.x;
   1828  insets.bottom = aRoundedRect.YMost() - aTestRect.y;
   1829  insets.left = aTestRect.XMost() - aRoundedRect.x;
   1830 
   1831  // Check whether the bottom-right corner of aTestRect is inside the
   1832  // top left corner of aBounds when rounded by aRadii, etc.  If any
   1833  // corner is not, then fail; otherwise succeed.
   1834  return CheckCorner(insets.left, insets.top, aRadii.TopLeft().width,
   1835                     aRadii.TopLeft().height) &&
   1836         CheckCorner(insets.right, insets.top, aRadii.TopRight().width,
   1837                     aRadii.TopRight().height) &&
   1838         CheckCorner(insets.right, insets.bottom, aRadii.BottomRight().width,
   1839                     aRadii.BottomRight().height) &&
   1840         CheckCorner(insets.left, insets.bottom, aRadii.BottomLeft().width,
   1841                     aRadii.BottomLeft().height);
   1842 }
   1843 
   1844 nsRect nsLayoutUtils::MatrixTransformRect(const nsRect& aBounds,
   1845                                          const Matrix4x4& aMatrix,
   1846                                          float aFactor) {
   1847  RectDouble image =
   1848      RectDouble(NSAppUnitsToDoublePixels(aBounds.x, aFactor),
   1849                 NSAppUnitsToDoublePixels(aBounds.y, aFactor),
   1850                 NSAppUnitsToDoublePixels(aBounds.width, aFactor),
   1851                 NSAppUnitsToDoublePixels(aBounds.height, aFactor));
   1852 
   1853  // We clip from nscoord_MIN to nscoord_MAX which allows the resulting rect to
   1854  // have a size that goes up to 2*nscoord_MAX. We then rely on
   1855  // RoundGfxRectToAppRect to handle this situation intelligently to give us a
   1856  // size representable as an nscoord by shifting the rect to its mid point and
   1857  // shrinking the size to nscoord_MAX.
   1858  RectDouble maxBounds = RectDouble(
   1859      double(nscoord_MIN) / aFactor, double(nscoord_MIN) / aFactor,
   1860      double(nscoord_MAX) / aFactor * 2.0, double(nscoord_MAX) / aFactor * 2.0);
   1861 
   1862  image = aMatrix.TransformAndClipBounds(image, maxBounds);
   1863 
   1864  return RoundGfxRectToAppRect(image, aFactor);
   1865 }
   1866 
   1867 nsRect nsLayoutUtils::MatrixTransformRect(const nsRect& aBounds,
   1868                                          const Matrix4x4Flagged& aMatrix,
   1869                                          float aFactor) {
   1870  RectDouble image =
   1871      RectDouble(NSAppUnitsToDoublePixels(aBounds.x, aFactor),
   1872                 NSAppUnitsToDoublePixels(aBounds.y, aFactor),
   1873                 NSAppUnitsToDoublePixels(aBounds.width, aFactor),
   1874                 NSAppUnitsToDoublePixels(aBounds.height, aFactor));
   1875 
   1876  // See comment above about these maxBounds.
   1877  RectDouble maxBounds = RectDouble(
   1878      double(nscoord_MIN) / aFactor, double(nscoord_MIN) / aFactor,
   1879      double(nscoord_MAX) / aFactor * 2.0, double(nscoord_MAX) / aFactor * 2.0);
   1880 
   1881  image = aMatrix.TransformAndClipBounds(image, maxBounds);
   1882 
   1883  return RoundGfxRectToAppRect(image, aFactor);
   1884 }
   1885 
   1886 nsPoint nsLayoutUtils::MatrixTransformPoint(const nsPoint& aPoint,
   1887                                            const Matrix4x4& aMatrix,
   1888                                            float aFactor) {
   1889  gfxPoint image = gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, aFactor),
   1890                            NSAppUnitsToFloatPixels(aPoint.y, aFactor));
   1891  image = aMatrix.TransformPoint(image);
   1892  return nsPoint(NSFloatPixelsToAppUnits(float(image.x), aFactor),
   1893                 NSFloatPixelsToAppUnits(float(image.y), aFactor));
   1894 }
   1895 
   1896 void nsLayoutUtils::PostTranslate(Matrix4x4& aTransform, const nsPoint& aOrigin,
   1897                                  float aAppUnitsPerPixel, bool aRounded) {
   1898  Point3D gfxOrigin =
   1899      Point3D(NSAppUnitsToFloatPixels(aOrigin.x, aAppUnitsPerPixel),
   1900              NSAppUnitsToFloatPixels(aOrigin.y, aAppUnitsPerPixel), 0.0f);
   1901  if (aRounded) {
   1902    gfxOrigin.x = NS_round(gfxOrigin.x);
   1903    gfxOrigin.y = NS_round(gfxOrigin.y);
   1904  }
   1905  aTransform.PostTranslate(gfxOrigin);
   1906 }
   1907 
   1908 bool nsLayoutUtils::ShouldSnapToGrid(const nsIFrame* aFrame) {
   1909  // TODO: Remove this function when this pref is being removed.
   1910  if (StaticPrefs::layout_disable_pixel_alignment()) {
   1911    return aFrame && aFrame->IsSVGOuterSVGAnonChildFrame();
   1912  }
   1913 
   1914  return !aFrame || !aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) ||
   1915         aFrame->IsSVGOuterSVGAnonChildFrame();
   1916 }
   1917 
   1918 Matrix4x4Flagged nsLayoutUtils::GetTransformToAncestor(
   1919    RelativeTo aFrame, RelativeTo aAncestor, uint32_t aFlags,
   1920    nsIFrame** aOutAncestor) {
   1921  nsIFrame* parent;
   1922  Matrix4x4Flagged ctm;
   1923  // Make sure we don't get an invalid combination of source and destination
   1924  // RelativeTo values.
   1925  MOZ_ASSERT(!(aFrame.mViewportType == ViewportType::Visual &&
   1926               aAncestor.mViewportType == ViewportType::Layout));
   1927  if (aFrame == aAncestor) {
   1928    return ctm;
   1929  }
   1930  ctm = aFrame.mFrame->GetTransformMatrix(aFrame.mViewportType, aAncestor,
   1931                                          &parent, aFlags);
   1932  if (!aFrame.mFrame->Combines3DTransformWithAncestors()) {
   1933    ctm.ProjectTo2D();
   1934  }
   1935  while (parent && parent != aAncestor.mFrame &&
   1936         (!(aFlags & nsIFrame::STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT) ||
   1937          (!parent->IsStackingContext() &&
   1938           !DisplayPortUtils::FrameHasDisplayPort(parent)))) {
   1939    nsIFrame* cur = parent;
   1940    ctm = ctm * cur->GetTransformMatrix(aFrame.mViewportType, aAncestor,
   1941                                        &parent, aFlags);
   1942    if (!cur->Combines3DTransformWithAncestors()) {
   1943      ctm.ProjectTo2D();
   1944    }
   1945  }
   1946  if (aOutAncestor) {
   1947    *aOutAncestor = parent;
   1948  }
   1949  return ctm;
   1950 }
   1951 
   1952 MatrixScales nsLayoutUtils::GetTransformToAncestorScale(
   1953    const nsIFrame* aFrame) {
   1954  Matrix4x4Flagged transform = GetTransformToAncestor(
   1955      RelativeTo{aFrame},
   1956      RelativeTo{nsLayoutUtils::GetDisplayRootFrame(aFrame)});
   1957  Matrix transform2D;
   1958  if (transform.CanDraw2D(&transform2D)) {
   1959    return ThebesMatrix(transform2D).ScaleFactors().ConvertTo<float>();
   1960  }
   1961  return MatrixScales();
   1962 }
   1963 
   1964 static Matrix4x4Flagged GetTransformToAncestorExcludingAnimated(
   1965    nsIFrame* aFrame, const nsIFrame* aAncestor) {
   1966  nsIFrame* parent;
   1967  Matrix4x4Flagged ctm;
   1968  if (aFrame == aAncestor) {
   1969    return ctm;
   1970  }
   1971  if (ActiveLayerTracker::IsScaleSubjectToAnimation(aFrame)) {
   1972    return ctm;
   1973  }
   1974  ctm = aFrame->GetTransformMatrix(ViewportType::Layout, RelativeTo{aAncestor},
   1975                                   &parent);
   1976  while (parent && parent != aAncestor) {
   1977    if (ActiveLayerTracker::IsScaleSubjectToAnimation(parent)) {
   1978      return Matrix4x4Flagged();
   1979    }
   1980    if (!parent->Extend3DContext()) {
   1981      ctm.ProjectTo2D();
   1982    }
   1983    ctm = ctm * parent->GetTransformMatrix(ViewportType::Layout,
   1984                                           RelativeTo{aAncestor}, &parent);
   1985  }
   1986  return ctm;
   1987 }
   1988 
   1989 MatrixScales nsLayoutUtils::GetTransformToAncestorScaleExcludingAnimated(
   1990    nsIFrame* aFrame) {
   1991  Matrix4x4Flagged transform = GetTransformToAncestorExcludingAnimated(
   1992      aFrame, nsLayoutUtils::GetDisplayRootFrame(aFrame));
   1993  Matrix transform2D;
   1994  if (transform.Is2D(&transform2D)) {
   1995    return ThebesMatrix(transform2D).ScaleFactors().ConvertTo<float>();
   1996  }
   1997  return MatrixScales();
   1998 }
   1999 
   2000 const nsIFrame* nsLayoutUtils::FindNearestCommonAncestorFrame(
   2001    const nsIFrame* aFrame1, const nsIFrame* aFrame2) {
   2002  AutoTArray<const nsIFrame*, 100> ancestors1;
   2003  AutoTArray<const nsIFrame*, 100> ancestors2;
   2004  const nsIFrame* commonAncestor = nullptr;
   2005  if (aFrame1->PresContext() == aFrame2->PresContext()) {
   2006    commonAncestor = aFrame1->PresShell()->GetRootFrame();
   2007  }
   2008  for (const nsIFrame* f = aFrame1; f != commonAncestor;
   2009       f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
   2010    ancestors1.AppendElement(f);
   2011  }
   2012  for (const nsIFrame* f = aFrame2; f != commonAncestor;
   2013       f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
   2014    ancestors2.AppendElement(f);
   2015  }
   2016  uint32_t minLengths = std::min(ancestors1.Length(), ancestors2.Length());
   2017  for (uint32_t i = 1; i <= minLengths; ++i) {
   2018    if (ancestors1[ancestors1.Length() - i] ==
   2019        ancestors2[ancestors2.Length() - i]) {
   2020      commonAncestor = ancestors1[ancestors1.Length() - i];
   2021    } else {
   2022      break;
   2023    }
   2024  }
   2025  return commonAncestor;
   2026 }
   2027 
   2028 const nsIFrame* nsLayoutUtils::FindNearestCommonAncestorFrameWithinBlock(
   2029    const nsTextFrame* aFrame1, const nsTextFrame* aFrame2) {
   2030  MOZ_ASSERT(aFrame1);
   2031  MOZ_ASSERT(aFrame2);
   2032 
   2033  const nsIFrame* f1 = aFrame1;
   2034  const nsIFrame* f2 = aFrame2;
   2035 
   2036  int n1 = 1;
   2037  int n2 = 1;
   2038 
   2039  for (auto f = f1->GetParent();;) {
   2040    NS_ASSERTION(f, "All text frames should have a block ancestor");
   2041    if (!f) {
   2042      return nullptr;
   2043    }
   2044    if (f->IsBlockFrameOrSubclass()) {
   2045      break;
   2046    }
   2047    ++n1;
   2048    f = f->GetParent();
   2049  }
   2050 
   2051  for (auto f = f2->GetParent();;) {
   2052    NS_ASSERTION(f, "All text frames should have a block ancestor");
   2053    if (!f) {
   2054      return nullptr;
   2055    }
   2056    if (f->IsBlockFrameOrSubclass()) {
   2057      break;
   2058    }
   2059    ++n2;
   2060    f = f->GetParent();
   2061  }
   2062 
   2063  if (n1 > n2) {
   2064    std::swap(n1, n2);
   2065    std::swap(f1, f2);
   2066  }
   2067 
   2068  while (n2 > n1) {
   2069    f2 = f2->GetParent();
   2070    --n2;
   2071  }
   2072 
   2073  while (n2 >= 0) {
   2074    if (f1 == f2) {
   2075      return f1;
   2076    }
   2077    f1 = f1->GetParent();
   2078    f2 = f2->GetParent();
   2079    --n2;
   2080  }
   2081 
   2082  return nullptr;
   2083 }
   2084 
   2085 bool nsLayoutUtils::AuthorSpecifiedBorderBackgroundDisablesTheming(
   2086    StyleAppearance aAppearance) {
   2087  return aAppearance == StyleAppearance::NumberInput ||
   2088         aAppearance == StyleAppearance::PasswordInput ||
   2089         aAppearance == StyleAppearance::Button ||
   2090         aAppearance == StyleAppearance::Textfield ||
   2091         aAppearance == StyleAppearance::Textarea ||
   2092         aAppearance == StyleAppearance::Listbox ||
   2093         aAppearance == StyleAppearance::Menulist;
   2094 }
   2095 
   2096 static SVGTextFrame* GetContainingSVGTextFrame(const nsIFrame* aFrame) {
   2097  if (!aFrame->IsInSVGTextSubtree()) {
   2098    return nullptr;
   2099  }
   2100 
   2101  return static_cast<SVGTextFrame*>(nsLayoutUtils::GetClosestFrameOfType(
   2102      aFrame->GetParent(), LayoutFrameType::SVGText));
   2103 }
   2104 
   2105 static bool TransformGfxPointFromAncestor(RelativeTo aFrame,
   2106                                          const Point& aPoint,
   2107                                          RelativeTo aAncestor,
   2108                                          Maybe<Matrix4x4Flagged>& aMatrixCache,
   2109                                          Point* aOut) {
   2110  SVGTextFrame* text = GetContainingSVGTextFrame(aFrame.mFrame);
   2111 
   2112  if (!aMatrixCache) {
   2113    auto matrix = nsLayoutUtils::GetTransformToAncestor(
   2114        RelativeTo{text ? text : aFrame.mFrame, aFrame.mViewportType},
   2115        aAncestor);
   2116    aMatrixCache = matrix.MaybeInverse();
   2117    if (aMatrixCache.isNothing()) {
   2118      return false;
   2119    }
   2120  }
   2121 
   2122  const Matrix4x4Flagged& ctm = *aMatrixCache;
   2123  Point4D point = ctm.ProjectPoint(aPoint);
   2124  if (!point.HasPositiveWCoord()) {
   2125    return false;
   2126  }
   2127 
   2128  *aOut = point.As2DPoint();
   2129 
   2130  if (text) {
   2131    *aOut = text->TransformFramePointToTextChild(*aOut, aFrame.mFrame);
   2132  }
   2133 
   2134  return true;
   2135 }
   2136 
   2137 static Point TransformGfxPointToAncestor(
   2138    RelativeTo aFrame, const Point& aPoint, RelativeTo aAncestor,
   2139    Maybe<Matrix4x4Flagged>& aMatrixCache) {
   2140  if (SVGTextFrame* text = GetContainingSVGTextFrame(aFrame.mFrame)) {
   2141    Point result =
   2142        text->TransformFramePointFromTextChild(aPoint, aFrame.mFrame);
   2143    return TransformGfxPointToAncestor(RelativeTo{text}, result, aAncestor,
   2144                                       aMatrixCache);
   2145  }
   2146  if (!aMatrixCache) {
   2147    aMatrixCache.emplace(
   2148        nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor));
   2149  }
   2150  return aMatrixCache->ProjectPoint(aPoint).As2DPoint();
   2151 }
   2152 
   2153 static Rect TransformGfxRectToAncestor(
   2154    RelativeTo aFrame, const Rect& aRect, RelativeTo aAncestor,
   2155    bool* aPreservesAxisAlignedRectangles = nullptr,
   2156    Maybe<Matrix4x4Flagged>* aMatrixCache = nullptr,
   2157    bool aStopAtStackingContextAndDisplayPortAndOOFFrame = false,
   2158    nsIFrame** aOutAncestor = nullptr) {
   2159  Rect result;
   2160  Matrix4x4Flagged ctm;
   2161  if (SVGTextFrame* text = GetContainingSVGTextFrame(aFrame.mFrame)) {
   2162    result = text->TransformFrameRectFromTextChild(aRect, aFrame.mFrame);
   2163 
   2164    result = TransformGfxRectToAncestor(
   2165        RelativeTo{text}, result, aAncestor, nullptr, aMatrixCache,
   2166        aStopAtStackingContextAndDisplayPortAndOOFFrame, aOutAncestor);
   2167    if (aPreservesAxisAlignedRectangles) {
   2168      // TransformFrameRectFromTextChild could involve any kind of transform, we
   2169      // could drill down into it to get an answer out of it but we don't yet.
   2170      *aPreservesAxisAlignedRectangles = false;
   2171    }
   2172    return result;
   2173  }
   2174  if (aMatrixCache && *aMatrixCache) {
   2175    // We are given a matrix to use, so use it
   2176    ctm = aMatrixCache->value();
   2177  } else {
   2178    // Else, compute it
   2179    uint32_t flags = 0;
   2180    if (aStopAtStackingContextAndDisplayPortAndOOFFrame) {
   2181      flags |= nsIFrame::STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT;
   2182    }
   2183    ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor, flags,
   2184                                                aOutAncestor);
   2185    if (aMatrixCache) {
   2186      // and put it in the cache, if provided
   2187      *aMatrixCache = Some(ctm);
   2188    }
   2189  }
   2190  // Fill out the axis-alignment flag
   2191  if (aPreservesAxisAlignedRectangles) {
   2192    // TransformFrameRectFromTextChild could involve any kind of transform, we
   2193    // could drill down into it to get an answer out of it but we don't yet.
   2194    Matrix matrix2d;
   2195    *aPreservesAxisAlignedRectangles =
   2196        ctm.Is2D(&matrix2d) && matrix2d.PreservesAxisAlignedRectangles();
   2197  }
   2198  const nsIFrame* ancestor = aOutAncestor ? *aOutAncestor : aAncestor.mFrame;
   2199  float factor = ancestor->PresContext()->AppUnitsPerDevPixel();
   2200  Rect maxBounds = Rect(
   2201      float(nscoord_MIN) / factor, float(nscoord_MIN) / factor,
   2202      float(nscoord_MAX) / factor * 2.0, float(nscoord_MAX) / factor * 2.0);
   2203  return ctm.TransformAndClipBounds(aRect, maxBounds);
   2204 }
   2205 
   2206 nsLayoutUtils::TransformResult nsLayoutUtils::TransformPoints(
   2207    RelativeTo aFromFrame, RelativeTo aToFrame, uint32_t aPointCount,
   2208    CSSPoint* aPoints) {
   2209  // Conceptually, {ViewportFrame, Visual} is an ancestor of
   2210  // {ViewportFrame, Layout}, so factor that into the nearest ancestor
   2211  // computation.
   2212  RelativeTo nearestCommonAncestor{
   2213      FindNearestCommonAncestorFrame(aFromFrame.mFrame, aToFrame.mFrame),
   2214      aFromFrame.mViewportType == ViewportType::Visual ||
   2215              aToFrame.mViewportType == ViewportType::Visual
   2216          ? ViewportType::Visual
   2217          : ViewportType::Layout};
   2218  if (!nearestCommonAncestor.mFrame) {
   2219    return NO_COMMON_ANCESTOR;
   2220  }
   2221  CSSToLayoutDeviceScale devPixelsPerCSSPixelFromFrame =
   2222      aFromFrame.mFrame->PresContext()->CSSToDevPixelScale();
   2223  CSSToLayoutDeviceScale devPixelsPerCSSPixelToFrame =
   2224      aToFrame.mFrame->PresContext()->CSSToDevPixelScale();
   2225  Maybe<Matrix4x4Flagged> cacheTo;
   2226  Maybe<Matrix4x4Flagged> cacheFrom;
   2227  for (uint32_t i = 0; i < aPointCount; ++i) {
   2228    LayoutDevicePoint devPixels = aPoints[i] * devPixelsPerCSSPixelFromFrame;
   2229    // What should the behaviour be if some of the points aren't invertible
   2230    // and others are? Just assume all points are for now.
   2231    Point toDevPixels =
   2232        TransformGfxPointToAncestor(aFromFrame, Point(devPixels.x, devPixels.y),
   2233                                    nearestCommonAncestor, cacheTo);
   2234    Point result;
   2235    if (!TransformGfxPointFromAncestor(
   2236            aToFrame, toDevPixels, nearestCommonAncestor, cacheFrom, &result)) {
   2237      return NONINVERTIBLE_TRANSFORM;
   2238    }
   2239    // Divide here so that when the devPixelsPerCSSPixels are the same, we get
   2240    // the correct answer instead of some inaccuracy multiplying a number by its
   2241    // reciprocal.
   2242    aPoints[i] =
   2243        LayoutDevicePoint(result.x, result.y) / devPixelsPerCSSPixelToFrame;
   2244  }
   2245  return TRANSFORM_SUCCEEDED;
   2246 }
   2247 
   2248 nsLayoutUtils::TransformResult nsLayoutUtils::TransformPoint(
   2249    RelativeTo aFromFrame, RelativeTo aToFrame, nsPoint& aPoint) {
   2250  CSSPoint point = CSSPoint::FromAppUnits(aPoint);
   2251  auto result = TransformPoints(aFromFrame, aToFrame, 1, &point);
   2252  if (result == TRANSFORM_SUCCEEDED) {
   2253    aPoint = CSSPoint::ToAppUnits(point);
   2254  }
   2255  return result;
   2256 }
   2257 
   2258 nsLayoutUtils::TransformResult nsLayoutUtils::TransformRect(
   2259    const nsIFrame* aFromFrame, const nsIFrame* aToFrame, nsRect& aRect) {
   2260  const nsIFrame* nearestCommonAncestor =
   2261      FindNearestCommonAncestorFrame(aFromFrame, aToFrame);
   2262  if (!nearestCommonAncestor) {
   2263    return NO_COMMON_ANCESTOR;
   2264  }
   2265  Matrix4x4Flagged downToDest = GetTransformToAncestor(
   2266      RelativeTo{aToFrame}, RelativeTo{nearestCommonAncestor});
   2267  // invert downToDest in place
   2268  if (!downToDest.Invert()) {
   2269    return NONINVERTIBLE_TRANSFORM;
   2270  }
   2271  aRect = TransformFrameRectToAncestor(aFromFrame, aRect,
   2272                                       RelativeTo{nearestCommonAncestor});
   2273 
   2274  float devPixelsPerAppUnitFromFrame =
   2275      1.0f / nearestCommonAncestor->PresContext()->AppUnitsPerDevPixel();
   2276  float devPixelsPerAppUnitToFrame =
   2277      1.0f / aToFrame->PresContext()->AppUnitsPerDevPixel();
   2278  gfx::Rect toDevPixels = downToDest.ProjectRectBounds(
   2279      gfx::Rect(aRect.x * devPixelsPerAppUnitFromFrame,
   2280                aRect.y * devPixelsPerAppUnitFromFrame,
   2281                aRect.width * devPixelsPerAppUnitFromFrame,
   2282                aRect.height * devPixelsPerAppUnitFromFrame),
   2283      Rect(-std::numeric_limits<Float>::max() * devPixelsPerAppUnitFromFrame *
   2284               0.5f,
   2285           -std::numeric_limits<Float>::max() * devPixelsPerAppUnitFromFrame *
   2286               0.5f,
   2287           std::numeric_limits<Float>::max() * devPixelsPerAppUnitFromFrame,
   2288           std::numeric_limits<Float>::max() * devPixelsPerAppUnitFromFrame));
   2289  aRect.x = NSToCoordRoundWithClamp(toDevPixels.x / devPixelsPerAppUnitToFrame);
   2290  aRect.y = NSToCoordRoundWithClamp(toDevPixels.y / devPixelsPerAppUnitToFrame);
   2291  aRect.width =
   2292      NSToCoordRoundWithClamp(toDevPixels.width / devPixelsPerAppUnitToFrame);
   2293  aRect.height =
   2294      NSToCoordRoundWithClamp(toDevPixels.height / devPixelsPerAppUnitToFrame);
   2295  return TRANSFORM_SUCCEEDED;
   2296 }
   2297 
   2298 nsRect nsLayoutUtils::GetRectRelativeToFrame(const Element* aElement,
   2299                                             const nsIFrame* aFrame) {
   2300  if (!aElement || !aFrame) {
   2301    return nsRect();
   2302  }
   2303 
   2304  nsIFrame* frame = aElement->GetPrimaryFrame();
   2305  if (!frame) {
   2306    return nsRect();
   2307  }
   2308 
   2309  nsRect rect = frame->GetRectRelativeToSelf();
   2310  nsLayoutUtils::TransformResult rv =
   2311      nsLayoutUtils::TransformRect(frame, aFrame, rect);
   2312  if (rv != nsLayoutUtils::TRANSFORM_SUCCEEDED) {
   2313    return nsRect();
   2314  }
   2315 
   2316  return rect;
   2317 }
   2318 
   2319 bool nsLayoutUtils::ContainsPoint(const nsRect& aRect, const nsPoint& aPoint,
   2320                                  nscoord aInflateSize) {
   2321  nsRect rect = aRect;
   2322  rect.Inflate(aInflateSize);
   2323  return rect.Contains(aPoint);
   2324 }
   2325 
   2326 nsRect nsLayoutUtils::ClampRectToScrollFrames(nsIFrame* aFrame,
   2327                                              const nsRect& aRect) {
   2328  nsIFrame* closestScrollFrame = nsLayoutUtils::GetClosestFrameOfType(
   2329      aFrame, LayoutFrameType::ScrollContainer);
   2330 
   2331  nsRect resultRect = aRect;
   2332 
   2333  while (closestScrollFrame) {
   2334    ScrollContainerFrame* sf = do_QueryFrame(closestScrollFrame);
   2335 
   2336    nsRect scrollPortRect = sf->GetScrollPortRect();
   2337    nsLayoutUtils::TransformRect(closestScrollFrame, aFrame, scrollPortRect);
   2338 
   2339    resultRect = resultRect.Intersect(scrollPortRect);
   2340 
   2341    // Check whether aRect is visible in the scroll frame or not.
   2342    if (resultRect.IsEmpty()) {
   2343      break;
   2344    }
   2345 
   2346    // Get next ancestor scroll frame.
   2347    closestScrollFrame = nsLayoutUtils::GetClosestFrameOfType(
   2348        closestScrollFrame->GetParent(), LayoutFrameType::ScrollContainer);
   2349  }
   2350 
   2351  return resultRect;
   2352 }
   2353 
   2354 nsPoint nsLayoutUtils::TransformAncestorPointToFrame(RelativeTo aFrame,
   2355                                                     const nsPoint& aPoint,
   2356                                                     RelativeTo aAncestor) {
   2357  float factor = aFrame.mFrame->PresContext()->AppUnitsPerDevPixel();
   2358  Point result(NSAppUnitsToFloatPixels(aPoint.x, factor),
   2359               NSAppUnitsToFloatPixels(aPoint.y, factor));
   2360 
   2361  Maybe<Matrix4x4Flagged> matrixCache;
   2362  if (!TransformGfxPointFromAncestor(aFrame, result, aAncestor, matrixCache,
   2363                                     &result)) {
   2364    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
   2365  }
   2366 
   2367  return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor),
   2368                 NSFloatPixelsToAppUnits(float(result.y), factor));
   2369 }
   2370 
   2371 nsPoint nsLayoutUtils::TransformFramePointToRoot(ViewportType aToType,
   2372                                                 RelativeTo aFromFrame,
   2373                                                 const nsPoint& aPoint) {
   2374  float factor = aFromFrame.mFrame->PresContext()->AppUnitsPerDevPixel();
   2375  Point result(NSAppUnitsToFloatPixels(aPoint.x, factor),
   2376               NSAppUnitsToFloatPixels(aPoint.y, factor));
   2377 
   2378  RelativeTo ancestor = RelativeTo{nullptr, aToType};
   2379 
   2380  Maybe<Matrix4x4Flagged> matrixCache;
   2381  Point res =
   2382      TransformGfxPointToAncestor(aFromFrame, result, ancestor, matrixCache);
   2383 
   2384  return nsPoint(NSFloatPixelsToAppUnits(float(res.x), factor),
   2385                 NSFloatPixelsToAppUnits(float(res.y), factor));
   2386 };
   2387 
   2388 nsRect nsLayoutUtils::TransformFrameRectToAncestor(
   2389    const nsIFrame* aFrame, const nsRect& aRect, RelativeTo aAncestor,
   2390    bool* aPreservesAxisAlignedRectangles /* = nullptr */,
   2391    Maybe<Matrix4x4Flagged>* aMatrixCache /* = nullptr */,
   2392    bool aStopAtStackingContextAndDisplayPortAndOOFFrame /* = false */,
   2393    nsIFrame** aOutAncestor /* = nullptr */) {
   2394  MOZ_ASSERT(IsAncestorFrameCrossDocInProcess(aAncestor.mFrame, aFrame),
   2395             "Fix the caller");
   2396  float srcAppUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
   2397  Rect result(NSAppUnitsToFloatPixels(aRect.x, srcAppUnitsPerDevPixel),
   2398              NSAppUnitsToFloatPixels(aRect.y, srcAppUnitsPerDevPixel),
   2399              NSAppUnitsToFloatPixels(aRect.width, srcAppUnitsPerDevPixel),
   2400              NSAppUnitsToFloatPixels(aRect.height, srcAppUnitsPerDevPixel));
   2401  result = TransformGfxRectToAncestor(
   2402      RelativeTo{aFrame}, result, aAncestor, aPreservesAxisAlignedRectangles,
   2403      aMatrixCache, aStopAtStackingContextAndDisplayPortAndOOFFrame,
   2404      aOutAncestor);
   2405 
   2406  return ScaleThenRoundGfxRectToAppRect(
   2407      result, aAncestor.mFrame->PresContext()->AppUnitsPerDevPixel());
   2408 }
   2409 
   2410 Maybe<nsPoint> nsLayoutUtils::FrameToWidgetOffset(const nsIFrame* aFrame,
   2411                                                  nsIWidget* aWidget) {
   2412  nsPoint toNearestOffset;
   2413  auto* nearest = aFrame->GetNearestWidget(toNearestOffset);
   2414  if (!nearest) {
   2415    return {};
   2416  }
   2417  return Some(toNearestOffset +
   2418              LayoutDeviceIntPoint::ToAppUnits(
   2419                  WidgetToWidgetOffset(nearest, aWidget),
   2420                  aFrame->PresContext()->AppUnitsPerDevPixel()));
   2421 }
   2422 
   2423 LayoutDeviceIntPoint nsLayoutUtils::WidgetToWidgetOffset(nsIWidget* aFrom,
   2424                                                         nsIWidget* aTo) {
   2425  if (aFrom == aTo) {
   2426    return {};
   2427  }
   2428  auto fromOffset = aFrom->WidgetToScreenOffset();
   2429  auto toOffset = aTo->WidgetToScreenOffset();
   2430  return fromOffset - toOffset;
   2431 }
   2432 
   2433 UsedClear nsLayoutUtils::CombineClearType(UsedClear aOrigClearType,
   2434                                          UsedClear aNewClearType) {
   2435  UsedClear clearType = aOrigClearType;
   2436  switch (clearType) {
   2437    case UsedClear::Left:
   2438      if (UsedClear::Right == aNewClearType ||
   2439          UsedClear::Both == aNewClearType) {
   2440        clearType = UsedClear::Both;
   2441      }
   2442      break;
   2443    case UsedClear::Right:
   2444      if (UsedClear::Left == aNewClearType ||
   2445          UsedClear::Both == aNewClearType) {
   2446        clearType = UsedClear::Both;
   2447      }
   2448      break;
   2449    case UsedClear::None:
   2450      if (UsedClear::Left == aNewClearType ||
   2451          UsedClear::Right == aNewClearType ||
   2452          UsedClear::Both == aNewClearType) {
   2453        clearType = aNewClearType;
   2454      }
   2455      break;
   2456    case UsedClear::Both:
   2457      // Do nothing.
   2458      break;
   2459  }
   2460  return clearType;
   2461 }
   2462 
   2463 #ifdef MOZ_DUMP_PAINTING
   2464 #  include <stdio.h>
   2465 
   2466 static bool gDumpEventList = false;
   2467 
   2468 // nsLayoutUtils::PaintFrame() can call itself recursively, so rather than
   2469 // maintaining a single paint count, we need a stack.
   2470 StaticAutoPtr<nsTArray<int>> gPaintCountStack;
   2471 
   2472 struct AutoNestedPaintCount {
   2473  AutoNestedPaintCount() { gPaintCountStack->AppendElement(0); }
   2474  ~AutoNestedPaintCount() { gPaintCountStack->RemoveLastElement(); }
   2475 };
   2476 
   2477 #endif
   2478 
   2479 nsIFrame* nsLayoutUtils::GetFrameForPoint(
   2480    RelativeTo aRelativeTo, nsPoint aPt, const FrameForPointOptions& aOptions) {
   2481  AUTO_PROFILER_LABEL("nsLayoutUtils::GetFrameForPoint", LAYOUT);
   2482 
   2483  nsresult rv;
   2484  AutoTArray<nsIFrame*, 8> outFrames;
   2485  rv = GetFramesForArea(aRelativeTo, nsRect(aPt, nsSize(1, 1)), outFrames,
   2486                        aOptions);
   2487  NS_ENSURE_SUCCESS(rv, nullptr);
   2488  return outFrames.SafeElementAt(0);
   2489 }
   2490 
   2491 nsresult nsLayoutUtils::GetFramesForArea(RelativeTo aRelativeTo,
   2492                                         const nsRect& aRect,
   2493                                         nsTArray<nsIFrame*>& aOutFrames,
   2494                                         const FrameForPointOptions& aOptions) {
   2495  AUTO_PROFILER_LABEL("nsLayoutUtils::GetFramesForArea", LAYOUT);
   2496 
   2497  nsIFrame* frame = const_cast<nsIFrame*>(aRelativeTo.mFrame);
   2498 
   2499  nsDisplayListBuilder builder(frame, nsDisplayListBuilderMode::EventDelivery,
   2500                               false);
   2501  builder.BeginFrame();
   2502  nsDisplayList list(&builder);
   2503 
   2504  if (aOptions.mBits.contains(FrameForPointOption::IgnorePaintSuppression)) {
   2505    builder.IgnorePaintSuppression();
   2506  }
   2507  if (aOptions.mBits.contains(FrameForPointOption::IgnoreRootScrollFrame)) {
   2508    nsIFrame* rootScrollContainerFrame =
   2509        frame->PresShell()->GetRootScrollContainerFrame();
   2510    if (rootScrollContainerFrame) {
   2511      builder.SetIgnoreScrollFrame(rootScrollContainerFrame);
   2512    }
   2513  }
   2514  if (aRelativeTo.mViewportType == ViewportType::Layout) {
   2515    builder.SetIsRelativeToLayoutViewport();
   2516  }
   2517  if (aOptions.mBits.contains(FrameForPointOption::IgnoreCrossDoc)) {
   2518    builder.SetDescendIntoSubdocuments(false);
   2519  }
   2520 
   2521  if (aOptions.mBits.contains(FrameForPointOption::OnlyVisible)) {
   2522    builder.SetHitTestIsForVisibility(aOptions.mVisibleThreshold);
   2523  }
   2524 
   2525  builder.EnterPresShell(frame);
   2526 
   2527  builder.SetVisibleRect(aRect);
   2528  builder.SetDirtyRect(aRect);
   2529 
   2530  frame->BuildDisplayListForStackingContext(&builder, &list);
   2531  builder.LeavePresShell(frame, nullptr);
   2532 
   2533 #ifdef MOZ_DUMP_PAINTING
   2534  if (gDumpEventList) {
   2535    fprintf_stderr(stderr, "Event handling --- (%d,%d):\n", aRect.x, aRect.y);
   2536 
   2537    std::stringstream ss;
   2538    nsIFrame::PrintDisplayList(&builder, list, ss);
   2539    print_stderr(ss);
   2540  }
   2541 #endif
   2542 
   2543  nsDisplayItem::HitTestState hitTestState;
   2544  list.HitTest(&builder, aRect, &hitTestState, &aOutFrames);
   2545 
   2546  builder.SetIsDestroying();
   2547  list.DeleteAll(&builder);
   2548  builder.EndFrame();
   2549  return NS_OK;
   2550 }
   2551 
   2552 mozilla::ParentLayerToScreenScale2D
   2553 nsLayoutUtils::GetTransformToAncestorScaleCrossProcessForFrameMetrics(
   2554    const nsIFrame* aFrame) {
   2555  ParentLayerToScreenScale2D transformToAncestorScale =
   2556      ViewAs<ParentLayerToScreenScale2D>(
   2557          nsLayoutUtils::GetTransformToAncestorScale(aFrame));
   2558 
   2559  if (BrowserChild* browserChild = BrowserChild::GetFrom(aFrame->PresShell())) {
   2560    transformToAncestorScale =
   2561        ViewTargetAs<ParentLayerPixel>(
   2562            transformToAncestorScale,
   2563            PixelCastJustification::PropagatingToChildProcess) *
   2564        browserChild->GetEffectsInfo().mTransformToAncestorScale;
   2565  }
   2566 
   2567  return transformToAncestorScale;
   2568 }
   2569 
   2570 FrameMetrics nsLayoutUtils::CalculateBasicFrameMetrics(
   2571    ScrollContainerFrame* aScrollContainerFrame) {
   2572  // Calculate the metrics necessary for calculating the displayport.
   2573  // This code has a lot in common with the code in ComputeFrameMetrics();
   2574  // we may want to refactor this at some point.
   2575  FrameMetrics metrics;
   2576  nsPresContext* presContext = aScrollContainerFrame->PresContext();
   2577  PresShell* presShell = presContext->PresShell();
   2578  CSSToLayoutDeviceScale deviceScale = presContext->CSSToDevPixelScale();
   2579  float resolution = 1.0f;
   2580  bool isRcdRsf = aScrollContainerFrame->IsRootScrollFrameOfDocument() &&
   2581                  presContext->IsRootContentDocumentCrossProcess();
   2582  metrics.SetIsRootContent(isRcdRsf);
   2583  if (isRcdRsf) {
   2584    // Only the root content document's root scroll container frame should pick
   2585    // up the presShell's resolution. All the other frames are 1.0.
   2586    resolution = presShell->GetResolution();
   2587  }
   2588  LayoutDeviceToLayerScale cumulativeResolution(
   2589      LayoutDeviceToLayerScale(presShell->GetCumulativeResolution()));
   2590 
   2591  LayerToParentLayerScale layerToParentLayerScale(1.0f);
   2592  metrics.SetDevPixelsPerCSSPixel(deviceScale);
   2593  metrics.SetPresShellResolution(resolution);
   2594 
   2595  metrics.SetTransformToAncestorScale(
   2596      GetTransformToAncestorScaleCrossProcessForFrameMetrics(
   2597          aScrollContainerFrame));
   2598  metrics.SetCumulativeResolution(cumulativeResolution);
   2599  metrics.SetZoom(deviceScale * cumulativeResolution * layerToParentLayerScale);
   2600 
   2601  // Only the size of the composition bounds is relevant to the
   2602  // displayport calculation, not its origin.
   2603  nsSize compositionSize =
   2604      nsLayoutUtils::CalculateCompositionSizeForFrame(aScrollContainerFrame);
   2605  LayoutDeviceToParentLayerScale compBoundsScale;
   2606  if (aScrollContainerFrame == presShell->GetRootScrollContainerFrame() &&
   2607      presContext->IsRootContentDocumentCrossProcess()) {
   2608    if (presContext->GetParentPresContext()) {
   2609      float res = presContext->GetParentPresContext()
   2610                      ->PresShell()
   2611                      ->GetCumulativeResolution();
   2612      compBoundsScale = LayoutDeviceToParentLayerScale(res);
   2613    }
   2614  } else {
   2615    compBoundsScale = cumulativeResolution * layerToParentLayerScale;
   2616  }
   2617  metrics.SetCompositionBounds(
   2618      LayoutDeviceRect::FromAppUnits(nsRect(nsPoint(0, 0), compositionSize),
   2619                                     presContext->AppUnitsPerDevPixel()) *
   2620      compBoundsScale);
   2621 
   2622  metrics.SetBoundingCompositionSize(
   2623      nsLayoutUtils::CalculateBoundingCompositionSize(aScrollContainerFrame,
   2624                                                      false, metrics));
   2625 
   2626  metrics.SetLayoutViewport(CSSRect::FromAppUnits(
   2627      nsRect(aScrollContainerFrame->GetScrollPosition(),
   2628             aScrollContainerFrame->GetScrollPortRect().Size())));
   2629  metrics.SetVisualScrollOffset(
   2630      isRcdRsf ? CSSPoint::FromAppUnits(presShell->GetVisualViewportOffset())
   2631               : metrics.GetLayoutViewport().TopLeft());
   2632 
   2633  metrics.SetScrollableRect(
   2634      CSSRect::FromAppUnits(nsLayoutUtils::CalculateScrollableRectForFrame(
   2635          aScrollContainerFrame, nullptr)));
   2636 
   2637  return metrics;
   2638 }
   2639 
   2640 ScrollContainerFrame* nsLayoutUtils::GetAsyncScrollableAncestorFrame(
   2641    nsIFrame* aTarget) {
   2642  // This should be kept in sync with
   2643  // DisplayPortUtils::OneStepInAsyncScrollableAncestorChain.
   2644  uint32_t flags = nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT |
   2645                   nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE |
   2646                   nsLayoutUtils::SCROLLABLE_FIXEDPOS_FINDS_ROOT;
   2647  return nsLayoutUtils::GetNearestScrollContainerFrame(aTarget, flags);
   2648 }
   2649 
   2650 void nsLayoutUtils::AddExtraBackgroundItems(nsDisplayListBuilder* aBuilder,
   2651                                            nsDisplayList* aList,
   2652                                            nsIFrame* aFrame,
   2653                                            const nsRect& aCanvasArea,
   2654                                            const nsRegion& aVisibleRegion,
   2655                                            nscolor aBackstop) {
   2656  if (aFrame->IsPageFrame()) {
   2657    // For printing, this function is first called on an nsPageFrame, which
   2658    // creates a display list with a PageContent item. The PageContent item's
   2659    // paint function calls this function on the nsPageFrame's child which is an
   2660    // nsPageContentFrame. We only want to add the canvas background color item
   2661    // once, for the nsPageContentFrame.
   2662    return;
   2663  }
   2664  // Add the canvas background color to the bottom of the list. This
   2665  // happens after we've built the list so that AddCanvasBackgroundColorItem
   2666  // can monkey with the contents if necessary.
   2667  nsRect canvasArea = aVisibleRegion.GetBounds();
   2668  canvasArea.IntersectRect(aCanvasArea, canvasArea);
   2669  nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
   2670      aBuilder, aFrame, canvasArea, canvasArea);
   2671  aFrame->PresShell()->AddCanvasBackgroundColorItem(aBuilder, aList, aFrame,
   2672                                                    canvasArea, aBackstop);
   2673 }
   2674 
   2675 // #define PRINT_HITTESTINFO_STATS
   2676 #ifdef PRINT_HITTESTINFO_STATS
   2677 void PrintHitTestInfoStatsInternal(nsDisplayList* aList, int& aTotal,
   2678                                   int& aHitTest, int& aVisible,
   2679                                   int& aSpecial) {
   2680  for (nsDisplayItem* i : *aList) {
   2681    aTotal++;
   2682 
   2683    if (i->GetChildren()) {
   2684      PrintHitTestInfoStatsInternal(i->GetChildren(), aTotal, aHitTest,
   2685                                    aVisible, aSpecial);
   2686    }
   2687 
   2688    if (i->GetType() == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
   2689      aHitTest++;
   2690 
   2691      const auto& hitTestInfo = static_cast<nsDisplayCompositorHitTestInfo*>(i)
   2692                                    ->GetHitTestInfo()
   2693                                    .Info();
   2694 
   2695      if (hitTestInfo.size() > 1) {
   2696        aSpecial++;
   2697        continue;
   2698      }
   2699 
   2700      if (hitTestInfo == CompositorHitTestFlags::eVisibleToHitTest) {
   2701        aVisible++;
   2702        continue;
   2703      }
   2704 
   2705      aSpecial++;
   2706    }
   2707  }
   2708 }
   2709 
   2710 void PrintHitTestInfoStats(nsDisplayList* aList) {
   2711  int total = 0;
   2712  int hitTest = 0;
   2713  int visible = 0;
   2714  int special = 0;
   2715 
   2716  PrintHitTestInfoStatsInternal(aList, total, hitTest, visible, special);
   2717 
   2718  double ratio = (double)hitTest / (double)total;
   2719 
   2720  printf(
   2721      "List %p: total items: %d, hit test items: %d, ratio: %f, visible: %d, "
   2722      "special: %d\n",
   2723      aList, total, hitTest, ratio, visible, special);
   2724 }
   2725 #endif
   2726 
   2727 static void DumpBeforePaintDisplayList(UniquePtr<std::stringstream>& aStream,
   2728                                       nsDisplayListBuilder* aBuilder,
   2729                                       nsDisplayList* aList,
   2730                                       const nsRect& aVisibleRect) {
   2731 #ifdef MOZ_DUMP_PAINTING
   2732  if (gfxEnv::MOZ_DUMP_PAINT_TO_FILE()) {
   2733    nsCString string("dump-");
   2734    // Include the process ID in the dump file name, to make sure that in an
   2735    // e10s setup different processes don't clobber each other's dump files.
   2736    string.AppendInt(getpid());
   2737    for (int paintCount : *gPaintCountStack) {
   2738      string.AppendLiteral("-");
   2739      string.AppendInt(paintCount);
   2740    }
   2741    string.AppendLiteral(".html");
   2742    gfxUtils::sDumpPaintFile = fopen(string.BeginReading(), "w");
   2743  } else {
   2744    gfxUtils::sDumpPaintFile = stderr;
   2745  }
   2746  if (gfxEnv::MOZ_DUMP_PAINT_TO_FILE()) {
   2747    *aStream << "<html><head><script>\n"
   2748                "var array = {};\n"
   2749                "function ViewImage(index) { \n"
   2750                "  var image = document.getElementById(index);\n"
   2751                "  if (image.src) {\n"
   2752                "    image.removeAttribute('src');\n"
   2753                "  } else {\n"
   2754                "    image.src = array[index];\n"
   2755                "  }\n"
   2756                "}</script></head><body>";
   2757  }
   2758 #endif
   2759  *aStream << nsPrintfCString(
   2760                  "Painting --- before optimization (dirty %d,%d,%d,%d):\n",
   2761                  aVisibleRect.x, aVisibleRect.y, aVisibleRect.width,
   2762                  aVisibleRect.height)
   2763                  .get();
   2764  nsIFrame::PrintDisplayList(aBuilder, *aList, *aStream,
   2765                             gfxEnv::MOZ_DUMP_PAINT_TO_FILE());
   2766 
   2767  if (gfxEnv::MOZ_DUMP_PAINT() || gfxEnv::MOZ_DUMP_PAINT_ITEMS()) {
   2768    // Flush stream now to avoid reordering dump output relative to
   2769    // messages dumped by PaintRoot below.
   2770    fprint_stderr(gfxUtils::sDumpPaintFile, *aStream);
   2771    aStream = MakeUnique<std::stringstream>();
   2772  }
   2773 }
   2774 
   2775 static void DumpAfterPaintDisplayList(UniquePtr<std::stringstream>& aStream,
   2776                                      nsDisplayListBuilder* aBuilder,
   2777                                      nsDisplayList* aList) {
   2778  *aStream << "Painting --- after optimization:\n";
   2779  nsIFrame::PrintDisplayList(aBuilder, *aList, *aStream,
   2780                             gfxEnv::MOZ_DUMP_PAINT_TO_FILE());
   2781 
   2782  fprint_stderr(gfxUtils::sDumpPaintFile, *aStream);
   2783 
   2784 #ifdef MOZ_DUMP_PAINTING
   2785  if (gfxEnv::MOZ_DUMP_PAINT_TO_FILE()) {
   2786    *aStream << "</body></html>";
   2787  }
   2788  if (gfxEnv::MOZ_DUMP_PAINT_TO_FILE()) {
   2789    fclose(gfxUtils::sDumpPaintFile);
   2790  }
   2791 #endif
   2792 
   2793  std::stringstream lsStream;
   2794  nsIFrame::PrintDisplayList(aBuilder, *aList, lsStream);
   2795 }
   2796 
   2797 struct TemporaryDisplayListBuilder {
   2798  TemporaryDisplayListBuilder(nsIFrame* aFrame,
   2799                              nsDisplayListBuilderMode aBuilderMode,
   2800                              const bool aBuildCaret)
   2801      : mBuilder(aFrame, aBuilderMode, aBuildCaret), mList(&mBuilder) {}
   2802 
   2803  ~TemporaryDisplayListBuilder() {
   2804    mBuilder.SetIsDestroying();
   2805    mList.DeleteAll(&mBuilder);
   2806  }
   2807 
   2808  nsDisplayListBuilder mBuilder;
   2809  nsDisplayList mList;
   2810  RetainedDisplayListMetrics mMetrics;
   2811 };
   2812 
   2813 void nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
   2814                               const nsRegion& aDirtyRegion, nscolor aBackstop,
   2815                               nsDisplayListBuilderMode aBuilderMode,
   2816                               PaintFrameFlags aFlags) {
   2817  AUTO_PROFILER_LABEL("nsLayoutUtils::PaintFrame", GRAPHICS);
   2818 
   2819  // Create a static storage counter that is incremented on eacy entry to
   2820  // PaintFrame and decremented on exit. We can use this later to determine if
   2821  // this is a top-level paint.
   2822  static uint32_t paintFrameDepth = 0;
   2823  ++paintFrameDepth;
   2824 
   2825 #ifdef MOZ_DUMP_PAINTING
   2826  if (!gPaintCountStack) {
   2827    gPaintCountStack = new nsTArray<int>();
   2828    ClearOnShutdown(&gPaintCountStack);
   2829 
   2830    gPaintCountStack->AppendElement(0);
   2831  }
   2832  ++gPaintCountStack->LastElement();
   2833  AutoNestedPaintCount nestedPaintCount;
   2834 #endif
   2835 
   2836  nsIFrame* displayRoot = GetDisplayRootFrame(aFrame);
   2837 
   2838  if ((aFlags & PaintFrameFlags::WidgetLayers) && displayRoot != aFrame) {
   2839    aFlags &= ~PaintFrameFlags::WidgetLayers;
   2840    NS_ASSERTION(aRenderingContext, "need a rendering context");
   2841  }
   2842 
   2843  nsPresContext* presContext = aFrame->PresContext();
   2844  PresShell* presShell = presContext->PresShell();
   2845 
   2846  TimeStamp startBuildDisplayList = TimeStamp::Now();
   2847  auto dlTimerId = mozilla::glean::paint::build_displaylist_time.Start();
   2848 
   2849  const bool buildCaret = !(aFlags & PaintFrameFlags::HideCaret);
   2850 
   2851  // Note that isForPainting here does not include the PaintForPrinting builder
   2852  // mode; that's OK because there is no point in using retained display lists
   2853  // for a print destination.
   2854  const bool isForPainting = (aFlags & PaintFrameFlags::WidgetLayers) &&
   2855                             aBuilderMode == nsDisplayListBuilderMode::Painting;
   2856 
   2857  // Only allow retaining for painting when preffed on, and for root frames
   2858  // (since the modified frame tracking is per-root-frame).
   2859  const bool retainDisplayList =
   2860      isForPainting && AreRetainedDisplayListsEnabled() && !aFrame->GetParent();
   2861 
   2862  RetainedDisplayListBuilder* retainedBuilder = nullptr;
   2863  Maybe<TemporaryDisplayListBuilder> temporaryBuilder;
   2864 
   2865  nsDisplayListBuilder* builder = nullptr;
   2866  nsDisplayList* list = nullptr;
   2867  RetainedDisplayListMetrics* metrics = nullptr;
   2868 
   2869  if (retainDisplayList) {
   2870    MOZ_ASSERT(aFrame == displayRoot);
   2871    retainedBuilder = aFrame->GetProperty(RetainedDisplayListBuilder::Cached());
   2872    if (!retainedBuilder) {
   2873      retainedBuilder =
   2874          new RetainedDisplayListBuilder(aFrame, aBuilderMode, buildCaret);
   2875      aFrame->SetProperty(RetainedDisplayListBuilder::Cached(),
   2876                          retainedBuilder);
   2877    }
   2878 
   2879    builder = retainedBuilder->Builder();
   2880    list = retainedBuilder->List();
   2881    metrics = retainedBuilder->Metrics();
   2882  } else {
   2883    temporaryBuilder.emplace(aFrame, aBuilderMode, buildCaret);
   2884    builder = &temporaryBuilder->mBuilder;
   2885    list = &temporaryBuilder->mList;
   2886    metrics = &temporaryBuilder->mMetrics;
   2887  }
   2888 
   2889  MOZ_ASSERT(builder && list && metrics);
   2890 
   2891  nsAutoString uri;
   2892  if (MOZ_LOG_TEST(GetLoggerByProcess(), LogLevel::Info) ||
   2893      MOZ_UNLIKELY(gfxUtils::DumpDisplayList()) ||
   2894      MOZ_UNLIKELY(gfxEnv::MOZ_DUMP_PAINT())) {
   2895    if (Document* doc = presContext->Document()) {
   2896      (void)doc->GetDocumentURI(uri);
   2897    }
   2898  }
   2899 
   2900  nsAutoString frameName, displayRootName;
   2901 #ifdef DEBUG_FRAME_DUMP
   2902  if (MOZ_LOG_TEST(GetLoggerByProcess(), LogLevel::Info)) {
   2903    aFrame->GetFrameName(frameName);
   2904    displayRoot->GetFrameName(displayRootName);
   2905  }
   2906 #endif
   2907 
   2908  DL_LOGI("PaintFrame: %p (%s), DisplayRoot: %p (%s), Builder: %p, URI: %s",
   2909          aFrame, NS_ConvertUTF16toUTF8(frameName).get(), displayRoot,
   2910          NS_ConvertUTF16toUTF8(displayRootName).get(), retainedBuilder,
   2911          NS_ConvertUTF16toUTF8(uri).get());
   2912 
   2913  metrics->Reset();
   2914  metrics->StartBuild();
   2915 
   2916  builder->BeginFrame();
   2917 
   2918  MOZ_ASSERT(paintFrameDepth >= 1);
   2919  // If this is a top-level paint, increment the paint sequence number.
   2920  if (paintFrameDepth == 1) {
   2921    // Increment the paint sequence number for the display list builder.
   2922    nsDisplayListBuilder::IncrementPaintSequenceNumber();
   2923  }
   2924 
   2925  if (aFlags & PaintFrameFlags::InTransform) {
   2926    builder->SetInTransform(true);
   2927  }
   2928  if (aFlags & PaintFrameFlags::SyncDecodeImages) {
   2929    builder->SetSyncDecodeImages(true);
   2930  }
   2931  if (aFlags & (PaintFrameFlags::WidgetLayers | PaintFrameFlags::ToWindow)) {
   2932    builder->SetPaintingToWindow(true);
   2933  }
   2934  if (aFlags & PaintFrameFlags::UseHighQualityScaling) {
   2935    builder->SetUseHighQualityScaling(true);
   2936  }
   2937  if (aFlags & PaintFrameFlags::ForWebRender) {
   2938    builder->SetPaintingForWebRender(true);
   2939  }
   2940  if (aFlags & PaintFrameFlags::IgnoreSuppression) {
   2941    builder->IgnorePaintSuppression();
   2942  }
   2943 
   2944  if (BrowsingContext* bc = presContext->Document()->GetBrowsingContext()) {
   2945    builder->SetInActiveDocShell(bc->IsActive());
   2946  }
   2947 
   2948  nsRect rootInkOverflow = aFrame->InkOverflowRectRelativeToSelf();
   2949 
   2950  // If the dynamic toolbar is completely collapsed, the visible rect should
   2951  // be expanded to include this area.
   2952  const bool hasDynamicToolbar =
   2953      presContext->IsRootContentDocumentCrossProcess() &&
   2954      presContext->HasDynamicToolbar();
   2955  if (hasDynamicToolbar) {
   2956    rootInkOverflow.SizeTo(nsLayoutUtils::ExpandHeightForDynamicToolbar(
   2957        presContext, rootInkOverflow.Size()));
   2958  }
   2959 
   2960  // If we are in a remote browser, then apply clipping from ancestor browsers
   2961  if (BrowserChild* browserChild = BrowserChild::GetFrom(presShell)) {
   2962    if (!browserChild->IsTopLevel()) {
   2963      const nsRect unscaledVisibleRect =
   2964          browserChild->GetVisibleRect().valueOr(nsRect());
   2965      rootInkOverflow.IntersectRect(rootInkOverflow, unscaledVisibleRect);
   2966    }
   2967  }
   2968 
   2969  builder->ClearHaveScrollableDisplayPort();
   2970  if (builder->IsPaintingToWindow() &&
   2971      nsLayoutUtils::AsyncPanZoomEnabled(aFrame)) {
   2972    DisplayPortUtils::MaybeCreateDisplayPortInFirstScrollFrameEncountered(
   2973        aFrame, builder);
   2974  }
   2975 
   2976  ScrollContainerFrame* rootScrollContainerFrame =
   2977      presShell->GetRootScrollContainerFrame();
   2978  if (rootScrollContainerFrame && !aFrame->GetParent()) {
   2979    nsRect displayPortBase = rootInkOverflow;
   2980    nsRect temp = displayPortBase;
   2981    (void)rootScrollContainerFrame->DecideScrollableLayer(
   2982        builder, &displayPortBase, &temp,
   2983        /* aSetBase = */ true);
   2984  }
   2985 
   2986  // In the case where we use APZ for the given popup frame, we need to set the
   2987  // displayport base.
   2988  if (aFrame->IsMenuPopupFrame() &&
   2989      nsLayoutUtils::AsyncPanZoomEnabled(aFrame) &&
   2990      !DisplayPortUtils::HasDisplayPort(aFrame->GetContent())) {
   2991    MOZ_ASSERT(XRE_IsParentProcess());
   2992    APZCCallbackHelper::InitializeRootDisplayport(aFrame);
   2993  }
   2994 
   2995  nsRegion visibleRegion;
   2996  if (aFlags & PaintFrameFlags::WidgetLayers) {
   2997    // This layer tree will be reused, so we'll need to calculate it
   2998    // for the whole "visible" area of the window
   2999    //
   3000    // |ignoreViewportScrolling| and |usingDisplayPort| are persistent
   3001    // document-rendering state.  We rely on PresShell to flush
   3002    // retained layers as needed when that persistent state changes.
   3003    visibleRegion = rootInkOverflow;
   3004  } else {
   3005    visibleRegion = aDirtyRegion;
   3006  }
   3007 
   3008  Maybe<nsPoint> originalScrollPosition;
   3009  auto maybeResetScrollPosition = MakeScopeExit([&]() {
   3010    if (originalScrollPosition && rootScrollContainerFrame) {
   3011      MOZ_ASSERT(rootScrollContainerFrame->GetScrolledFrame()->GetPosition() ==
   3012                 nsPoint());
   3013      rootScrollContainerFrame->GetScrolledFrame()->SetPosition(
   3014          *originalScrollPosition);
   3015    }
   3016  });
   3017 
   3018  nsRect canvasArea(nsPoint(0, 0),
   3019                    aFrame->InkOverflowRectRelativeToSelf().Size());
   3020  bool ignoreViewportScrolling =
   3021      !aFrame->GetParent() && presShell->IgnoringViewportScrolling();
   3022 
   3023  if (!aFrame->GetParent() && hasDynamicToolbar) {
   3024    canvasArea.SizeTo(nsLayoutUtils::ExpandHeightForDynamicToolbar(
   3025        presContext, canvasArea.Size()));
   3026  }
   3027 
   3028  if (ignoreViewportScrolling && rootScrollContainerFrame) {
   3029    if (aFlags & PaintFrameFlags::ResetViewportScrolling) {
   3030      // Temporarily scroll the root scroll frame to 0,0 so that position:fixed
   3031      // elements will appear fixed to the top-left of the document. We manually
   3032      // set the position of the scrolled frame instead of using ScrollTo, since
   3033      // the latter fires scroll listeners, which we don't want.
   3034      originalScrollPosition.emplace(
   3035          rootScrollContainerFrame->GetScrolledFrame()->GetPosition());
   3036      rootScrollContainerFrame->GetScrolledFrame()->SetPosition(nsPoint());
   3037    }
   3038    if (aFlags & PaintFrameFlags::DocumentRelative) {
   3039      // Make visibleRegion and aRenderingContext relative to the
   3040      // scrolled frame instead of the root frame.
   3041      nsPoint pos = rootScrollContainerFrame->GetScrollPosition();
   3042      visibleRegion.MoveBy(-pos);
   3043      if (aRenderingContext) {
   3044        gfxPoint devPixelOffset = nsLayoutUtils::PointToGfxPoint(
   3045            pos, presContext->AppUnitsPerDevPixel());
   3046        aRenderingContext->SetMatrixDouble(
   3047            aRenderingContext->CurrentMatrixDouble().PreTranslate(
   3048                devPixelOffset));
   3049      }
   3050    }
   3051    builder->SetIgnoreScrollFrame(rootScrollContainerFrame);
   3052 
   3053    nsCanvasFrame* canvasFrame =
   3054        do_QueryFrame(rootScrollContainerFrame->GetScrolledFrame());
   3055    if (canvasFrame) {
   3056      // Use UnionRect here to ensure that areas where the scrollbars
   3057      // were are still filled with the background color.
   3058      canvasArea.UnionRect(
   3059          canvasArea,
   3060          canvasFrame->CanvasArea() + builder->ToReferenceFrame(canvasFrame));
   3061    }
   3062  }
   3063 
   3064  nsRect visibleRect = visibleRegion.GetBounds();
   3065  PartialUpdateResult updateState = PartialUpdateResult::Failed;
   3066 
   3067  {
   3068    AUTO_PROFILER_LABEL_CATEGORY_PAIR(GRAPHICS_DisplayListBuilding);
   3069    AUTO_PROFILER_MARKER("DisplayList", GRAPHICS);
   3070    PerfStats::AutoMetricRecording<PerfStats::Metric::DisplayListBuilding>
   3071        autoRecording;
   3072 
   3073    ViewID id = ScrollableLayerGuid::NULL_SCROLL_ID;
   3074    nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(
   3075        builder);
   3076 
   3077    if (presShell->GetDocument() &&
   3078        presShell->GetDocument()->IsRootDisplayDocument() &&
   3079        !presShell->GetRootScrollContainerFrame()) {
   3080      // In cases where the root document is a XUL document, we want to take
   3081      // the ViewID from the root element, as that will be the ViewID of the
   3082      // root APZC in the tree. Skip doing this in cases where we know
   3083      // ScrollContainerFrame::BuilDisplayList will do it instead.
   3084      if (dom::Element* element =
   3085              presShell->GetDocument()->GetDocumentElement()) {
   3086        id = nsLayoutUtils::FindOrCreateIDFor(element);
   3087      }
   3088      // In some cases we get a root document here on an APZ-enabled window
   3089      // that doesn't have the root displayport initialized yet, even though
   3090      // the ChromeProcessController is supposed to do it when the widget is
   3091      // created. This can happen simply because the ChromeProcessController
   3092      // does it on the next spin of the event loop, and we can trigger a
   3093      // paint synchronously after window creation but before that runs. In
   3094      // that case we should initialize the root displayport here before we do
   3095      // the paint.
   3096    } else if (XRE_IsParentProcess() && presContext->IsRoot() &&
   3097               presShell->GetDocument() != nullptr &&
   3098               presShell->GetRootScrollContainerFrame() != nullptr &&
   3099               nsLayoutUtils::UsesAsyncScrolling(
   3100                   presShell->GetRootScrollContainerFrame())) {
   3101      if (dom::Element* element =
   3102              presShell->GetDocument()->GetDocumentElement()) {
   3103        if (!DisplayPortUtils::HasNonMinimalDisplayPort(element)) {
   3104          APZCCallbackHelper::InitializeRootDisplayport(presShell);
   3105        }
   3106      }
   3107    }
   3108 
   3109    asrSetter.SetCurrentScrollParentId(id);
   3110 
   3111    builder->SetVisibleRect(visibleRect);
   3112    builder->SetIsBuilding(true);
   3113    builder->SetAncestorHasApzAwareEventHandler(
   3114        builder->BuildCompositorHitTestInfo() &&
   3115        nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell));
   3116 
   3117    // If a pref is toggled that adds or removes display list items,
   3118    // we need to rebuild the display list. The pref may be toggled
   3119    // manually by the user, or during test setup.
   3120    if (retainDisplayList &&
   3121        !builder->ShouldRebuildDisplayListDueToPrefChange()) {
   3122      // Attempt to do a partial build and merge into the existing list.
   3123      // This calls BuildDisplayListForStacking context on a subset of the
   3124      // viewport.
   3125      updateState = retainedBuilder->AttemptPartialUpdate(aBackstop);
   3126      metrics->EndPartialBuild(updateState);
   3127    } else {
   3128      // Partial updates are disabled.
   3129      DL_LOGI("Partial updates are disabled");
   3130      metrics->mPartialUpdateResult = PartialUpdateResult::Failed;
   3131      metrics->mPartialUpdateFailReason = PartialUpdateFailReason::Disabled;
   3132 
   3133      // Now that we've decided to do a full rebuild make sure to clear the
   3134      // disable partial updates bool so that we can maybe attempt a partial
   3135      // update on the paint after this one (if it doesn't get disabled again
   3136      // during this paint).
   3137      builder->SetDisablePartialUpdates(false);
   3138    }
   3139 
   3140    // Rebuild the full display list if the partial display list build failed.
   3141    bool doFullRebuild = updateState == PartialUpdateResult::Failed;
   3142 
   3143    if (StaticPrefs::layout_display_list_build_twice()) {
   3144      // Build display list twice to compare partial and full display list
   3145      // build times.
   3146      metrics->StartBuild();
   3147      doFullRebuild = true;
   3148    }
   3149 
   3150    if (doFullRebuild) {
   3151      if (retainDisplayList) {
   3152        retainedBuilder->ClearRetainedData();
   3153 #ifdef DEBUG
   3154        mozilla::RDLUtils::AssertFrameSubtreeUnmodified(
   3155            builder->RootReferenceFrame());
   3156 #endif
   3157      }
   3158 
   3159      list->DeleteAll(builder);
   3160 
   3161      builder->ClearRetainedWindowRegions();
   3162      builder->ClearWillChangeBudgets();
   3163 
   3164      builder->EnterPresShell(aFrame);
   3165      builder->SetDirtyRect(visibleRect);
   3166 
   3167      DL_LOGI("Starting full display list build, root frame: %p",
   3168              builder->RootReferenceFrame());
   3169 
   3170      aFrame->BuildDisplayListForStackingContext(builder, list);
   3171      AddExtraBackgroundItems(builder, list, aFrame, canvasArea, visibleRegion,
   3172                              aBackstop);
   3173 
   3174      builder->LeavePresShell(aFrame, list);
   3175      metrics->EndFullBuild();
   3176 
   3177      DL_LOGI("Finished full display list build");
   3178      updateState = PartialUpdateResult::Updated;
   3179    }
   3180 
   3181    builder->SetIsBuilding(false);
   3182    builder->IncrementPresShellPaintCount(presShell);
   3183  }
   3184 
   3185  MOZ_ASSERT(updateState != PartialUpdateResult::Failed);
   3186  builder->Check();
   3187 
   3188  const double geckoDLBuildTime =
   3189      (TimeStamp::Now() - startBuildDisplayList).ToMilliseconds();
   3190  mozilla::glean::paint::build_displaylist_time.StopAndAccumulate(
   3191      std::move(dlTimerId));
   3192 
   3193  bool consoleNeedsDisplayList =
   3194      (gfxUtils::DumpDisplayList() || gfxEnv::MOZ_DUMP_PAINT()) &&
   3195      builder->IsInActiveDocShell();
   3196 #ifdef MOZ_DUMP_PAINTING
   3197  FILE* savedDumpFile = gfxUtils::sDumpPaintFile;
   3198 #endif
   3199 
   3200  UniquePtr<std::stringstream> ss;
   3201  if (consoleNeedsDisplayList) {
   3202    ss = MakeUnique<std::stringstream>();
   3203    *ss << "Display list for " << uri << "\n";
   3204    DumpBeforePaintDisplayList(ss, builder, list, visibleRect);
   3205  }
   3206 
   3207  uint32_t flags = nsDisplayList::PAINT_DEFAULT;
   3208  if (aFlags & PaintFrameFlags::WidgetLayers) {
   3209    flags |= nsDisplayList::PAINT_USE_WIDGET_LAYERS;
   3210    if (!(aFlags & PaintFrameFlags::DocumentRelative)) {
   3211      nsIWidget* widget = aFrame->GetNearestWidget();
   3212      if (widget) {
   3213        // If we're finished building display list items for painting of the
   3214        // outermost pres shell, notify the widget about any toolbars we've
   3215        // encountered.
   3216        widget->UpdateThemeGeometries(builder->GetThemeGeometries());
   3217      }
   3218    }
   3219  }
   3220  if (aFlags & PaintFrameFlags::ExistingTransaction) {
   3221    flags |= nsDisplayList::PAINT_EXISTING_TRANSACTION;
   3222  }
   3223  if (updateState == PartialUpdateResult::NoChange && !aRenderingContext) {
   3224    flags |= nsDisplayList::PAINT_IDENTICAL_DISPLAY_LIST;
   3225  }
   3226 
   3227 #ifdef PRINT_HITTESTINFO_STATS
   3228  if (XRE_IsContentProcess()) {
   3229    PrintHitTestInfoStats(list);
   3230  }
   3231 #endif
   3232  if (aFlags & PaintFrameFlags::CompositeOffscreen) {
   3233    flags |= nsDisplayList::PAINT_COMPOSITE_OFFSCREEN;
   3234  }
   3235 
   3236  TimeStamp paintStart = TimeStamp::Now();
   3237  list->PaintRoot(builder, aRenderingContext, flags, Some(geckoDLBuildTime));
   3238  glean::layout::paint_rasterize_time.AccumulateRawDuration(TimeStamp::Now() -
   3239                                                            paintStart);
   3240 
   3241  if (builder->IsPaintingToWindow()) {
   3242    presShell->EndPaint();
   3243  }
   3244  builder->Check();
   3245 
   3246  if (consoleNeedsDisplayList) {
   3247    DumpAfterPaintDisplayList(ss, builder, list);
   3248  }
   3249 
   3250 #ifdef MOZ_DUMP_PAINTING
   3251  gfxUtils::sDumpPaintFile = savedDumpFile;
   3252 #endif
   3253 
   3254  // Update the widget's opaque region information. This sets
   3255  // glass boundaries on Windows. Also set up the window dragging region.
   3256  if ((aFlags & PaintFrameFlags::WidgetLayers) &&
   3257      !(aFlags & PaintFrameFlags::DocumentRelative)) {
   3258    if (nsIWidget* widget = aFrame->GetNearestWidget()) {
   3259      const nsRegion& opaqueRegion = builder->GetWindowOpaqueRegion();
   3260      widget->UpdateOpaqueRegion(LayoutDeviceIntRegion::FromUnknownRegion(
   3261          opaqueRegion.ToNearestPixels(presContext->AppUnitsPerDevPixel())));
   3262      widget->UpdateWindowDraggingRegion(builder->GetWindowDraggingRegion());
   3263    }
   3264  }
   3265 
   3266  builder->Check();
   3267 
   3268  {
   3269    AUTO_PROFILER_MARKER("DisplayListResources", GRAPHICS);
   3270 
   3271    builder->EndFrame();
   3272 
   3273    if (temporaryBuilder) {
   3274      temporaryBuilder.reset();
   3275    }
   3276  }
   3277 
   3278  --paintFrameDepth;
   3279 #if 0
   3280  if (XRE_IsParentProcess()) {
   3281    if (metrics->mPartialUpdateResult == PartialUpdateResult::Failed) {
   3282      printf("DL partial update failed: %s, Frame: %p\n",
   3283             metrics->FailReasonString(), aFrame);
   3284    } else {
   3285      printf(
   3286          "DL partial build success!"
   3287          " new: %d, reused: %d, rebuilt: %d, removed: %d, total: %d\n",
   3288          metrics->mNewItems, metrics->mReusedItems, metrics->mRebuiltItems,
   3289          metrics->mRemovedItems, metrics->mTotalItems);
   3290    }
   3291  }
   3292 #endif
   3293 }
   3294 
   3295 /**
   3296 * Uses a binary search for find where the cursor falls in the line of text
   3297 * It also keeps track of the part of the string that has already been measured
   3298 * so it doesn't have to keep measuring the same text over and over
   3299 *
   3300 * @param "aBaseWidth" contains the width in twips of the portion
   3301 * of the text that has already been measured, and aBaseInx contains
   3302 * the index of the text that has already been measured.
   3303 *
   3304 * @param aTextWidth returns the (in twips) the length of the text that falls
   3305 * before the cursor aIndex contains the index of the text where the cursor
   3306 * falls
   3307 */
   3308 bool nsLayoutUtils::BinarySearchForPosition(
   3309    DrawTarget* aDrawTarget, nsFontMetrics& aFontMetrics, const char16_t* aText,
   3310    int32_t aBaseWidth, int32_t aBaseInx, int32_t aStartInx, int32_t aEndInx,
   3311    int32_t aCursorPos, int32_t& aIndex, int32_t& aTextWidth) {
   3312  int32_t range = aEndInx - aStartInx;
   3313  if ((range == 1) || (range == 2 && NS_IS_HIGH_SURROGATE(aText[aStartInx]))) {
   3314    aIndex = aStartInx + aBaseInx;
   3315    aTextWidth = nsLayoutUtils::AppUnitWidthOfString(aText, aIndex,
   3316                                                     aFontMetrics, aDrawTarget);
   3317    return true;
   3318  }
   3319 
   3320  int32_t inx = aStartInx + (range / 2);
   3321 
   3322  // Make sure we don't leave a dangling low surrogate
   3323  if (NS_IS_HIGH_SURROGATE(aText[inx - 1])) {
   3324    inx++;
   3325  }
   3326 
   3327  int32_t textWidth = nsLayoutUtils::AppUnitWidthOfString(
   3328      aText, inx, aFontMetrics, aDrawTarget);
   3329 
   3330  int32_t fullWidth = aBaseWidth + textWidth;
   3331  if (fullWidth == aCursorPos) {
   3332    aTextWidth = textWidth;
   3333    aIndex = inx;
   3334    return true;
   3335  } else if (aCursorPos < fullWidth) {
   3336    aTextWidth = aBaseWidth;
   3337    if (BinarySearchForPosition(aDrawTarget, aFontMetrics, aText, aBaseWidth,
   3338                                aBaseInx, aStartInx, inx, aCursorPos, aIndex,
   3339                                aTextWidth)) {
   3340      return true;
   3341    }
   3342  } else {
   3343    aTextWidth = fullWidth;
   3344    if (BinarySearchForPosition(aDrawTarget, aFontMetrics, aText, aBaseWidth,
   3345                                aBaseInx, inx, aEndInx, aCursorPos, aIndex,
   3346                                aTextWidth)) {
   3347      return true;
   3348    }
   3349  }
   3350  return false;
   3351 }
   3352 
   3353 void nsLayoutUtils::AddBoxesForFrame(nsIFrame* aFrame,
   3354                                     nsLayoutUtils::BoxCallback* aCallback) {
   3355  auto pseudoType = aFrame->Style()->GetPseudoType();
   3356 
   3357  if (pseudoType == PseudoStyleType::tableWrapper) {
   3358    for (nsIFrame* kid : aFrame->PrincipalChildList()) {
   3359      AddBoxesForFrame(kid, aCallback);
   3360      if (!aCallback->mIncludeCaptionBoxForTable) {
   3361        break;
   3362      }
   3363    }
   3364  } else if (pseudoType == PseudoStyleType::mozBlockInsideInlineWrapper ||
   3365             pseudoType == PseudoStyleType::mozMathMLAnonymousBlock) {
   3366    for (nsIFrame* kid : aFrame->PrincipalChildList()) {
   3367      AddBoxesForFrame(kid, aCallback);
   3368    }
   3369  } else {
   3370    aCallback->AddBox(aFrame);
   3371  }
   3372 }
   3373 
   3374 void nsLayoutUtils::GetAllInFlowBoxes(nsIFrame* aFrame,
   3375                                      BoxCallback* aCallback) {
   3376  aCallback->mInTargetContinuation = false;
   3377  while (aFrame) {
   3378    AddBoxesForFrame(aFrame, aCallback);
   3379    aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
   3380    aCallback->mInTargetContinuation = true;
   3381  }
   3382 }
   3383 
   3384 nsIFrame* nsLayoutUtils::GetFirstNonAnonymousFrame(nsIFrame* aFrame) {
   3385  while (aFrame) {
   3386    auto pseudoType = aFrame->Style()->GetPseudoType();
   3387    if (pseudoType == PseudoStyleType::tableWrapper ||
   3388        pseudoType == PseudoStyleType::mozBlockInsideInlineWrapper ||
   3389        pseudoType == PseudoStyleType::mozMathMLAnonymousBlock) {
   3390      for (nsIFrame* kid : aFrame->PrincipalChildList()) {
   3391        if (nsIFrame* f = GetFirstNonAnonymousFrame(kid)) {
   3392          return f;
   3393        }
   3394      }
   3395    } else {
   3396      return aFrame;
   3397    }
   3398 
   3399    aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
   3400  }
   3401  return nullptr;
   3402 }
   3403 
   3404 struct BoxToRect : public nsLayoutUtils::BoxCallback {
   3405  const nsIFrame* mRelativeTo;
   3406  RectCallback* mCallback;
   3407  nsLayoutUtils::GetAllInFlowRectsFlags mFlags;
   3408  // If the frame we're measuring relative to is the root, we know all frames
   3409  // are descendants of it, so we don't need to compute the common ancestor
   3410  // between a frame and mRelativeTo.
   3411  bool mRelativeToIsRoot;
   3412  // For the same reason, if the frame we're measuring relative to is the target
   3413  // (this is useful for IntersectionObserver), we know all frames are
   3414  // descendants of it except if we're in a continuation or ib-split-sibling of
   3415  // it.
   3416  bool mRelativeToIsTarget;
   3417 
   3418  BoxToRect(const nsIFrame* aTargetFrame, const nsIFrame* aRelativeTo,
   3419            RectCallback* aCallback,
   3420            nsLayoutUtils::GetAllInFlowRectsFlags aFlags)
   3421      : mRelativeTo(aRelativeTo),
   3422        mCallback(aCallback),
   3423        mFlags(aFlags),
   3424        mRelativeToIsRoot(!aRelativeTo->GetParent()),
   3425        mRelativeToIsTarget(aRelativeTo == aTargetFrame) {}
   3426 
   3427  void AddBox(nsIFrame* aFrame) override {
   3428    nsRect r;
   3429    nsIFrame* outer = SVGUtils::GetOuterSVGFrameAndCoveredRegion(aFrame, &r);
   3430    const bool usingSVGOuterFrame = !!outer;
   3431    if (!outer) {
   3432      outer = aFrame;
   3433      if (mFlags.contains(
   3434              nsLayoutUtils::GetAllInFlowRectsFlag::UseContentBox)) {
   3435        r = aFrame->GetContentRectRelativeToSelf();
   3436      } else if (mFlags.contains(
   3437                     nsLayoutUtils::GetAllInFlowRectsFlag::UsePaddingBox)) {
   3438        r = aFrame->GetPaddingRectRelativeToSelf();
   3439      } else if (mFlags.contains(
   3440                     nsLayoutUtils::GetAllInFlowRectsFlag::UseMarginBox)) {
   3441        r = aFrame->GetMarginRectRelativeToSelf();
   3442      } else if (mFlags.contains(nsLayoutUtils::GetAllInFlowRectsFlag::
   3443                                     UseMarginBoxWithAutoResolvedAsZero)) {
   3444        r = aFrame->GetRectRelativeToSelf();
   3445        nsMargin usedMargin =
   3446            aFrame->GetUsedMargin().ApplySkipSides(aFrame->GetSkipSides());
   3447        const auto* styleMargin = aFrame->StyleMargin();
   3448        const auto anchorResolutionParams =
   3449            AnchorPosResolutionParams::From(aFrame);
   3450        for (const Side side : AllPhysicalSides()) {
   3451          if (styleMargin->GetMargin(side, anchorResolutionParams)->IsAuto()) {
   3452            usedMargin.Side(side) = 0;
   3453          }
   3454        }
   3455        r.Inflate(usedMargin);
   3456      } else {
   3457        // Use the border-box.
   3458        r = aFrame->GetRectRelativeToSelf();
   3459      }
   3460    }
   3461    if (outer != mRelativeTo) {
   3462      if (mFlags.contains(
   3463              nsLayoutUtils::GetAllInFlowRectsFlag::AccountForTransforms)) {
   3464        const bool isAncestorKnown = [&] {
   3465          if (mRelativeToIsRoot) {
   3466            return true;
   3467          }
   3468          if (mRelativeToIsTarget && !mInTargetContinuation) {
   3469            return !usingSVGOuterFrame;
   3470          }
   3471          return false;
   3472        }();
   3473        if (isAncestorKnown) {
   3474          r = nsLayoutUtils::TransformFrameRectToAncestor(outer, r,
   3475                                                          mRelativeTo);
   3476        } else {
   3477          nsLayoutUtils::TransformRect(outer, mRelativeTo, r);
   3478        }
   3479      } else {
   3480        if (aFrame->PresContext() != mRelativeTo->PresContext()) {
   3481          r += outer->GetOffsetToCrossDoc(mRelativeTo);
   3482        } else {
   3483          r += outer->GetOffsetTo(mRelativeTo);
   3484        }
   3485      }
   3486    }
   3487    mCallback->AddRect(r);
   3488  }
   3489 };
   3490 
   3491 struct MOZ_RAII BoxToRectAndText : public BoxToRect {
   3492  Sequence<nsString>* mTextList;
   3493 
   3494  BoxToRectAndText(const nsIFrame* aTargetFrame, const nsIFrame* aRelativeTo,
   3495                   RectCallback* aCallback, Sequence<nsString>* aTextList,
   3496                   nsLayoutUtils::GetAllInFlowRectsFlags aFlags)
   3497      : BoxToRect(aTargetFrame, aRelativeTo, aCallback, aFlags),
   3498        mTextList(aTextList) {}
   3499 
   3500  static void AccumulateText(nsIFrame* aFrame, nsAString& aResult) {
   3501    MOZ_ASSERT(aFrame);
   3502 
   3503    // Get all the text in aFrame and child frames, while respecting
   3504    // the content offsets in each of the nsTextFrames.
   3505    if (aFrame->IsTextFrame()) {
   3506      nsTextFrame* textFrame = static_cast<nsTextFrame*>(aFrame);
   3507 
   3508      nsIFrame::RenderedText renderedText = textFrame->GetRenderedText(
   3509          textFrame->GetContentOffset(),
   3510          textFrame->GetContentOffset() + textFrame->GetContentLength(),
   3511          nsIFrame::TextOffsetType::OffsetsInContentText,
   3512          nsIFrame::TrailingWhitespace::DontTrim);
   3513 
   3514      aResult.Append(renderedText.mString);
   3515    }
   3516 
   3517    for (nsIFrame* child = aFrame->PrincipalChildList().FirstChild(); child;
   3518         child = child->GetNextSibling()) {
   3519      AccumulateText(child, aResult);
   3520    }
   3521  }
   3522 
   3523  void AddBox(nsIFrame* aFrame) override {
   3524    BoxToRect::AddBox(aFrame);
   3525    if (mTextList) {
   3526      nsString* textForFrame = mTextList->AppendElement(fallible);
   3527      if (textForFrame) {
   3528        AccumulateText(aFrame, *textForFrame);
   3529      }
   3530    }
   3531  }
   3532 };
   3533 
   3534 void nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame,
   3535                                      const nsIFrame* aRelativeTo,
   3536                                      RectCallback* aCallback,
   3537                                      GetAllInFlowRectsFlags aFlags) {
   3538  BoxToRect converter(aFrame, aRelativeTo, aCallback, aFlags);
   3539  GetAllInFlowBoxes(aFrame, &converter);
   3540 }
   3541 
   3542 void nsLayoutUtils::GetAllInFlowRectsAndTexts(nsIFrame* aFrame,
   3543                                              const nsIFrame* aRelativeTo,
   3544                                              RectCallback* aCallback,
   3545                                              Sequence<nsString>* aTextList,
   3546                                              GetAllInFlowRectsFlags aFlags) {
   3547  BoxToRectAndText converter(aFrame, aRelativeTo, aCallback, aTextList, aFlags);
   3548  GetAllInFlowBoxes(aFrame, &converter);
   3549 }
   3550 
   3551 nsLayoutUtils::RectAccumulator::RectAccumulator() : mSeenFirstRect(false) {}
   3552 
   3553 void nsLayoutUtils::RectAccumulator::AddRect(const nsRect& aRect) {
   3554  mResultRect.UnionRect(mResultRect, aRect);
   3555  if (!mSeenFirstRect) {
   3556    mSeenFirstRect = true;
   3557    mFirstRect = aRect;
   3558  }
   3559 }
   3560 
   3561 nsLayoutUtils::RectListBuilder::RectListBuilder(DOMRectList* aList)
   3562    : mRectList(aList) {}
   3563 
   3564 void nsLayoutUtils::RectListBuilder::AddRect(const nsRect& aRect) {
   3565  auto rect = MakeRefPtr<DOMRect>(mRectList);
   3566 
   3567  rect->SetLayoutRect(aRect);
   3568  mRectList->Append(std::move(rect));
   3569 }
   3570 
   3571 nsIFrame* nsLayoutUtils::GetContainingBlockForClientRect(nsIFrame* aFrame) {
   3572  return aFrame->PresShell()->GetRootFrame();
   3573 }
   3574 
   3575 nsRect nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame,
   3576                                             const nsIFrame* aRelativeTo,
   3577                                             GetAllInFlowRectsFlags aFlags) {
   3578  RectAccumulator accumulator;
   3579  GetAllInFlowRects(aFrame, aRelativeTo, &accumulator, aFlags);
   3580  return accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect
   3581                                           : accumulator.mResultRect;
   3582 }
   3583 
   3584 nsRect nsLayoutUtils::GetTextShadowRectsUnion(
   3585    const nsRect& aTextAndDecorationsRect, nsIFrame* aFrame, uint32_t aFlags) {
   3586  const nsStyleText* textStyle = aFrame->StyleText();
   3587  auto shadows = textStyle->mTextShadow.AsSpan();
   3588  if (shadows.IsEmpty()) {
   3589    return aTextAndDecorationsRect;
   3590  }
   3591 
   3592  nsRect resultRect = aTextAndDecorationsRect;
   3593  int32_t A2D = aFrame->PresContext()->AppUnitsPerDevPixel();
   3594  for (auto& shadow : shadows) {
   3595    nsMargin blur =
   3596        nsContextBoxBlur::GetBlurRadiusMargin(shadow.blur.ToAppUnits(), A2D);
   3597    if ((aFlags & EXCLUDE_BLUR_SHADOWS) && blur != nsMargin(0, 0, 0, 0)) {
   3598      continue;
   3599    }
   3600 
   3601    nsRect tmpRect(aTextAndDecorationsRect);
   3602 
   3603    tmpRect.MoveBy(
   3604        nsPoint(shadow.horizontal.ToAppUnits(), shadow.vertical.ToAppUnits()));
   3605    tmpRect.Inflate(blur);
   3606 
   3607    resultRect.UnionRect(resultRect, tmpRect);
   3608  }
   3609  return resultRect;
   3610 }
   3611 
   3612 enum ObjectDimensionType { eWidth, eHeight };
   3613 static nscoord ComputeMissingDimension(
   3614    const nsSize& aDefaultObjectSize, const AspectRatio& aIntrinsicRatio,
   3615    const Maybe<nscoord>& aSpecifiedWidth,
   3616    const Maybe<nscoord>& aSpecifiedHeight,
   3617    ObjectDimensionType aDimensionToCompute) {
   3618  // The "default sizing algorithm" computes the missing dimension as follows:
   3619  // (source: http://dev.w3.org/csswg/css-images-3/#default-sizing )
   3620 
   3621  // 1. "If the object has an intrinsic aspect ratio, the missing dimension of
   3622  //     the concrete object size is calculated using the intrinsic aspect
   3623  //     ratio and the present dimension."
   3624  if (aIntrinsicRatio) {
   3625    // Fill in the missing dimension using the intrinsic aspect ratio.
   3626    if (aDimensionToCompute == eWidth) {
   3627      return aIntrinsicRatio.ApplyTo(*aSpecifiedHeight);
   3628    }
   3629    return aIntrinsicRatio.Inverted().ApplyTo(*aSpecifiedWidth);
   3630  }
   3631 
   3632  // 2. "Otherwise, if the missing dimension is present in the object's
   3633  //     intrinsic dimensions, [...]"
   3634  // NOTE: *Skipping* this case, because we already know it's not true -- we're
   3635  // in this function because the missing dimension is *not* present in
   3636  // the object's intrinsic dimensions.
   3637 
   3638  // 3. "Otherwise, the missing dimension of the concrete object size is taken
   3639  //     from the default object size. "
   3640  return (aDimensionToCompute == eWidth) ? aDefaultObjectSize.width
   3641                                         : aDefaultObjectSize.height;
   3642 }
   3643 
   3644 /*
   3645 * This computes & returns the concrete object size of replaced content, if
   3646 * that content were to be rendered with "object-fit: none".  (Or, if the
   3647 * element has neither an intrinsic height nor width, this method returns an
   3648 * empty Maybe<> object.)
   3649 *
   3650 * As specced...
   3651 *   http://dev.w3.org/csswg/css-images-3/#valdef-object-fit-none
   3652 * ..we use "the default sizing algorithm with no specified size,
   3653 * and a default object size equal to the replaced element's used width and
   3654 * height."
   3655 *
   3656 * The default sizing algorithm is described here:
   3657 *   http://dev.w3.org/csswg/css-images-3/#default-sizing
   3658 * Quotes in the function-impl are taken from that ^ spec-text.
   3659 *
   3660 * Per its final bulleted section: since there's no specified size,
   3661 * we run the default sizing algorithm using the object's intrinsic size in
   3662 * place of the specified size. But if the object has neither an intrinsic
   3663 * height nor an intrinsic width, then we instead return without populating our
   3664 * outparam, and we let the caller figure out the size (using a contain
   3665 * constraint).
   3666 */
   3667 static Maybe<nsSize> MaybeComputeObjectFitNoneSize(
   3668    const nsSize& aDefaultObjectSize, const IntrinsicSize& aIntrinsicSize,
   3669    const AspectRatio& aIntrinsicRatio) {
   3670  // "If the object has an intrinsic height or width, its size is resolved as
   3671  // if its intrinsic dimensions were given as the specified size."
   3672  //
   3673  // So, first we check if we have an intrinsic height and/or width:
   3674  const Maybe<nscoord>& specifiedWidth = aIntrinsicSize.width;
   3675  const Maybe<nscoord>& specifiedHeight = aIntrinsicSize.height;
   3676 
   3677  Maybe<nsSize> noneSize;  // (the value we'll return)
   3678  if (specifiedWidth || specifiedHeight) {
   3679    // We have at least one specified dimension; use whichever dimension is
   3680    // specified, and compute the other one using our intrinsic ratio, or (if
   3681    // no valid ratio) using the default object size.
   3682    noneSize.emplace();
   3683 
   3684    noneSize->width =
   3685        specifiedWidth
   3686            ? *specifiedWidth
   3687            : ComputeMissingDimension(aDefaultObjectSize, aIntrinsicRatio,
   3688                                      specifiedWidth, specifiedHeight, eWidth);
   3689 
   3690    noneSize->height =
   3691        specifiedHeight
   3692            ? *specifiedHeight
   3693            : ComputeMissingDimension(aDefaultObjectSize, aIntrinsicRatio,
   3694                                      specifiedWidth, specifiedHeight, eHeight);
   3695  }
   3696  // [else:] "Otherwise [if there's neither an intrinsic height nor width], its
   3697  // size is resolved as a contain constraint against the default object size."
   3698  // We'll let our caller do that, to share code & avoid redundant
   3699  // computations; so, we return w/out populating noneSize.
   3700  return noneSize;
   3701 }
   3702 
   3703 // Computes the concrete object size to render into, as described at
   3704 // http://dev.w3.org/csswg/css-images-3/#concrete-size-resolution
   3705 static nsSize ComputeConcreteObjectSize(const nsSize& aConstraintSize,
   3706                                        const IntrinsicSize& aIntrinsicSize,
   3707                                        const AspectRatio& aIntrinsicRatio,
   3708                                        StyleObjectFit aObjectFit) {
   3709  // Handle default behavior (filling the container) w/ fast early return.
   3710  // (Also: if there's no valid intrinsic ratio, then we have the "fill"
   3711  // behavior & just use the constraint size.)
   3712  if (MOZ_LIKELY(aObjectFit == StyleObjectFit::Fill) || !aIntrinsicRatio) {
   3713    return aConstraintSize;
   3714  }
   3715 
   3716  // The type of constraint to compute (cover/contain), if needed:
   3717  Maybe<nsImageRenderer::FitType> fitType;
   3718 
   3719  Maybe<nsSize> noneSize;
   3720  if (aObjectFit == StyleObjectFit::None ||
   3721      aObjectFit == StyleObjectFit::ScaleDown) {
   3722    noneSize = MaybeComputeObjectFitNoneSize(aConstraintSize, aIntrinsicSize,
   3723                                             aIntrinsicRatio);
   3724    if (!noneSize || aObjectFit == StyleObjectFit::ScaleDown) {
   3725      // Need to compute a 'CONTAIN' constraint (either for the 'none' size
   3726      // itself, or for comparison w/ the 'none' size to resolve 'scale-down'.)
   3727      fitType.emplace(nsImageRenderer::CONTAIN);
   3728    }
   3729  } else if (aObjectFit == StyleObjectFit::Cover) {
   3730    fitType.emplace(nsImageRenderer::COVER);
   3731  } else if (aObjectFit == StyleObjectFit::Contain) {
   3732    fitType.emplace(nsImageRenderer::CONTAIN);
   3733  }
   3734 
   3735  Maybe<nsSize> constrainedSize;
   3736  if (fitType) {
   3737    constrainedSize.emplace(nsImageRenderer::ComputeConstrainedSize(
   3738        aConstraintSize, aIntrinsicRatio, *fitType));
   3739  }
   3740 
   3741  // Now, we should have all the sizing information that we need.
   3742  switch (aObjectFit) {
   3743    // skipping StyleObjectFit::Fill; we handled it w/ early-return.
   3744    case StyleObjectFit::Contain:
   3745    case StyleObjectFit::Cover:
   3746      MOZ_ASSERT(constrainedSize);
   3747      return *constrainedSize;
   3748 
   3749    case StyleObjectFit::None:
   3750      if (noneSize) {
   3751        return *noneSize;
   3752      }
   3753      MOZ_ASSERT(constrainedSize);
   3754      return *constrainedSize;
   3755 
   3756    case StyleObjectFit::ScaleDown:
   3757      MOZ_ASSERT(constrainedSize);
   3758      if (noneSize) {
   3759        constrainedSize->width =
   3760            std::min(constrainedSize->width, noneSize->width);
   3761        constrainedSize->height =
   3762            std::min(constrainedSize->height, noneSize->height);
   3763      }
   3764      return *constrainedSize;
   3765 
   3766    default:
   3767      MOZ_ASSERT_UNREACHABLE("Unexpected enum value for 'object-fit'");
   3768      return aConstraintSize;  // fall back to (default) 'fill' behavior
   3769  }
   3770 }
   3771 
   3772 // (Helper for HasInitialObjectFitAndPosition, to check
   3773 // each "object-position" coord.)
   3774 static bool IsCoord50Pct(const LengthPercentage& aCoord) {
   3775  return aCoord.ConvertsToPercentage() && aCoord.ToPercentage() == 0.5f;
   3776 }
   3777 
   3778 // Indicates whether the given nsStylePosition has the initial values
   3779 // for the "object-fit" and "object-position" properties.
   3780 static bool HasInitialObjectFitAndPosition(const nsStylePosition* aStylePos) {
   3781  const Position& objectPos = aStylePos->mObjectPosition;
   3782 
   3783  return aStylePos->mObjectFit == StyleObjectFit::Fill &&
   3784         IsCoord50Pct(objectPos.horizontal) && IsCoord50Pct(objectPos.vertical);
   3785 }
   3786 
   3787 /* static */
   3788 nsRect nsLayoutUtils::ComputeObjectDestRect(const nsRect& aConstraintRect,
   3789                                            const IntrinsicSize& aIntrinsicSize,
   3790                                            const AspectRatio& aIntrinsicRatio,
   3791                                            const nsStylePosition* aStylePos,
   3792                                            nsPoint* aAnchorPoint) {
   3793  // Step 1: Figure out our "concrete object size"
   3794  // (the size of the region we'll actually draw our image's pixels into).
   3795  nsSize concreteObjectSize =
   3796      ComputeConcreteObjectSize(aConstraintRect.Size(), aIntrinsicSize,
   3797                                aIntrinsicRatio, aStylePos->mObjectFit);
   3798 
   3799  // Step 2: Figure out how to align that region in the element's content-box.
   3800  nsPoint imageTopLeftPt, imageAnchorPt;
   3801  nsImageRenderer::ComputeObjectAnchorPoint(
   3802      aStylePos->mObjectPosition, aConstraintRect.Size(), concreteObjectSize,
   3803      &imageTopLeftPt, &imageAnchorPt);
   3804  // Right now, we're with respect to aConstraintRect's top-left point.  We add
   3805  // that point here, to convert to the same broader coordinate space that
   3806  // aConstraintRect is in.
   3807  imageTopLeftPt += aConstraintRect.TopLeft();
   3808  imageAnchorPt += aConstraintRect.TopLeft();
   3809 
   3810  if (aAnchorPoint) {
   3811    // Special-case: if our "object-fit" and "object-position" properties have
   3812    // their default values ("object-fit: fill; object-position:50% 50%"), then
   3813    // we'll override the calculated imageAnchorPt, and instead use the
   3814    // object's top-left corner.
   3815    //
   3816    // This special case is partly for backwards compatibility (since
   3817    // traditionally we've pixel-aligned the top-left corner of e.g. <img>
   3818    // elements), and partly because ComputeSnappedDrawingParameters produces
   3819    // less error if the anchor point is at the top-left corner. So, all other
   3820    // things being equal, we prefer that code path with less error.
   3821    if (HasInitialObjectFitAndPosition(aStylePos)) {
   3822      *aAnchorPoint = imageTopLeftPt;
   3823    } else {
   3824      *aAnchorPoint = imageAnchorPt;
   3825    }
   3826  }
   3827  return nsRect(imageTopLeftPt, concreteObjectSize);
   3828 }
   3829 
   3830 already_AddRefed<nsFontMetrics> nsLayoutUtils::GetFontMetricsForFrame(
   3831    const nsIFrame* aFrame, float aInflation) {
   3832  ComputedStyle* computedStyle = aFrame->Style();
   3833  uint8_t variantWidth = NS_FONT_VARIANT_WIDTH_NORMAL;
   3834  if (computedStyle->IsTextCombined()) {
   3835    MOZ_ASSERT(aFrame->IsTextFrame());
   3836    auto textFrame = static_cast<const nsTextFrame*>(aFrame);
   3837    auto clusters = textFrame->CountGraphemeClusters();
   3838    if (clusters == 2) {
   3839      variantWidth = NS_FONT_VARIANT_WIDTH_HALF;
   3840    } else if (clusters == 3) {
   3841      variantWidth = NS_FONT_VARIANT_WIDTH_THIRD;
   3842    } else if (clusters == 4) {
   3843      variantWidth = NS_FONT_VARIANT_WIDTH_QUARTER;
   3844    }
   3845  }
   3846  return GetFontMetricsForComputedStyle(computedStyle, aFrame->PresContext(),
   3847                                        aInflation, variantWidth);
   3848 }
   3849 
   3850 already_AddRefed<nsFontMetrics> nsLayoutUtils::GetFontMetricsForComputedStyle(
   3851    const ComputedStyle* aComputedStyle, nsPresContext* aPresContext,
   3852    float aInflation, uint8_t aVariantWidth) {
   3853  WritingMode wm(aComputedStyle);
   3854  const nsStyleFont* styleFont = aComputedStyle->StyleFont();
   3855  nsFontMetrics::Params params;
   3856  params.language = styleFont->mLanguage;
   3857  params.explicitLanguage = styleFont->mExplicitLanguage;
   3858  params.orientation = wm.IsVertical() && !wm.IsSideways()
   3859                           ? nsFontMetrics::eVertical
   3860                           : nsFontMetrics::eHorizontal;
   3861  // pass the user font set object into the device context to
   3862  // pass along to CreateFontGroup
   3863  params.userFontSet = aPresContext->GetUserFontSet();
   3864  params.textPerf = aPresContext->GetTextPerfMetrics();
   3865  params.featureValueLookup = aPresContext->GetFontFeatureValuesLookup();
   3866 
   3867  // When aInflation is 1.0 and we don't require width variant, avoid
   3868  // making a local copy of the nsFont.
   3869  // This also avoids running font.size through floats when it is large,
   3870  // which would be lossy.  Fortunately, in such cases, aInflation is
   3871  // guaranteed to be 1.0f.
   3872  if (aInflation == 1.0f && aVariantWidth == NS_FONT_VARIANT_WIDTH_NORMAL) {
   3873    return aPresContext->GetMetricsFor(styleFont->mFont, params);
   3874  }
   3875 
   3876  nsFont font = styleFont->mFont;
   3877  MOZ_ASSERT(!std::isnan(float(font.size.ToCSSPixels())),
   3878             "Style font should never be NaN");
   3879  font.size.ScaleBy(aInflation);
   3880  if (MOZ_UNLIKELY(std::isnan(float(font.size.ToCSSPixels())))) {
   3881    font.size = {0};
   3882  }
   3883  font.variantWidth = aVariantWidth;
   3884  return aPresContext->GetMetricsFor(font, params);
   3885 }
   3886 
   3887 nsIFrame* nsLayoutUtils::FindChildContainingDescendant(
   3888    nsIFrame* aParent, nsIFrame* aDescendantFrame) {
   3889  nsIFrame* result = aDescendantFrame;
   3890 
   3891  while (result) {
   3892    nsIFrame* parent = result->GetParent();
   3893    if (parent == aParent) {
   3894      break;
   3895    }
   3896 
   3897    // The frame is not an immediate child of aParent so walk up another level
   3898    result = parent;
   3899  }
   3900 
   3901  return result;
   3902 }
   3903 
   3904 nsBlockFrame* nsLayoutUtils::FindNearestBlockAncestor(nsIFrame* aFrame) {
   3905  nsIFrame* nextAncestor;
   3906  for (nextAncestor = aFrame->GetParent(); nextAncestor;
   3907       nextAncestor = nextAncestor->GetParent()) {
   3908    nsBlockFrame* block = do_QueryFrame(nextAncestor);
   3909    if (block) {
   3910      return block;
   3911    }
   3912  }
   3913  return nullptr;
   3914 }
   3915 
   3916 nsIFrame* nsLayoutUtils::GetParentOrPlaceholderFor(const nsIFrame* aFrame) {
   3917  // This condition must match the condition in FindContainingBlocks in
   3918  // RetainedDisplayListBuider.cpp, MarkFrameForDisplayIfVisible and
   3919  // UnmarkFrameForDisplayIfVisible in nsDisplayList.cpp
   3920  if (aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
   3921      !aFrame->GetPrevInFlow()) {
   3922    return aFrame->GetProperty(nsIFrame::PlaceholderFrameProperty());
   3923  }
   3924  return aFrame->GetParent();
   3925 }
   3926 
   3927 nsIFrame* nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(
   3928    const nsIFrame* aFrame) {
   3929  nsIFrame* f = GetParentOrPlaceholderFor(aFrame);
   3930  if (f) {
   3931    return f;
   3932  }
   3933  return GetCrossDocParentFrameInProcess(aFrame);
   3934 }
   3935 
   3936 nsIFrame* nsLayoutUtils::GetDisplayListParent(nsIFrame* aFrame) {
   3937  if (aFrame->HasAnyStateBits(NS_FRAME_IS_PUSHED_OUT_OF_FLOW)) {
   3938    return aFrame->GetParent();
   3939  }
   3940  return nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(aFrame);
   3941 }
   3942 
   3943 nsIFrame* nsLayoutUtils::GetPrevContinuationOrIBSplitSibling(
   3944    const nsIFrame* aFrame) {
   3945  if (nsIFrame* result = aFrame->GetPrevContinuation()) {
   3946    return result;
   3947  }
   3948 
   3949  if (aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
   3950    // We are the first frame in the continuation chain. Get the ib-split prev
   3951    // sibling property stored in us.
   3952    return aFrame->GetProperty(nsIFrame::IBSplitPrevSibling());
   3953  }
   3954 
   3955  return nullptr;
   3956 }
   3957 
   3958 nsIFrame* nsLayoutUtils::GetNextContinuationOrIBSplitSibling(
   3959    const nsIFrame* aFrame) {
   3960  if (nsIFrame* result = aFrame->GetNextContinuation()) {
   3961    return result;
   3962  }
   3963 
   3964  if (aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
   3965    // We only store the ib-split sibling annotation with the first frame in the
   3966    // continuation chain.
   3967    return aFrame->FirstContinuation()->GetProperty(nsIFrame::IBSplitSibling());
   3968  }
   3969 
   3970  return nullptr;
   3971 }
   3972 
   3973 nsIFrame* nsLayoutUtils::FirstContinuationOrIBSplitSibling(
   3974    const nsIFrame* aFrame) {
   3975  nsIFrame* result = aFrame->FirstContinuation();
   3976 
   3977  if (result->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
   3978    while (auto* f = result->GetProperty(nsIFrame::IBSplitPrevSibling())) {
   3979      result = f;
   3980    }
   3981  }
   3982 
   3983  return result;
   3984 }
   3985 
   3986 nsIFrame* nsLayoutUtils::LastContinuationOrIBSplitSibling(
   3987    const nsIFrame* aFrame) {
   3988  nsIFrame* result = aFrame->FirstContinuation();
   3989 
   3990  if (result->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
   3991    while (auto* f = result->GetProperty(nsIFrame::IBSplitSibling())) {
   3992      result = f;
   3993    }
   3994  }
   3995 
   3996  return result->LastContinuation();
   3997 }
   3998 
   3999 bool nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(
   4000    const nsIFrame* aFrame) {
   4001  if (aFrame->GetPrevContinuation()) {
   4002    return false;
   4003  }
   4004  if (aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT) &&
   4005      aFrame->GetProperty(nsIFrame::IBSplitPrevSibling())) {
   4006    return false;
   4007  }
   4008 
   4009  return true;
   4010 }
   4011 
   4012 bool nsLayoutUtils::IsViewportScrollbarFrame(nsIFrame* aFrame) {
   4013  if (!aFrame) {
   4014    return false;
   4015  }
   4016 
   4017  ScrollContainerFrame* rootScrollContainerFrame =
   4018      aFrame->PresShell()->GetRootScrollContainerFrame();
   4019  if (!rootScrollContainerFrame) {
   4020    return false;
   4021  }
   4022 
   4023  if (!IsProperAncestorFrame(rootScrollContainerFrame, aFrame)) {
   4024    return false;
   4025  }
   4026 
   4027  nsIFrame* rootScrolledFrame = rootScrollContainerFrame->GetScrolledFrame();
   4028  return !(rootScrolledFrame == aFrame ||
   4029           IsProperAncestorFrame(rootScrolledFrame, aFrame));
   4030 }
   4031 
   4032 static nscoord GetBSizePercentBasisAdjustment(StyleBoxSizing aBoxSizing,
   4033                                              nsIFrame* aFrame,
   4034                                              bool aHorizontalAxis,
   4035                                              bool aResolvesAgainstPaddingBox);
   4036 
   4037 static Maybe<nscoord> GetPercentBSize(const LengthPercentage& aSize,
   4038                                      nsIFrame* aFrame, bool aHorizontalAxis);
   4039 
   4040 // Only call on aSize for which GetAbsoluteSize returned Nothing().
   4041 //
   4042 // Bug 1363918: We can remove GetPercentBSize() after we've updated all of
   4043 // IntrinsicForAxis()'s callsites to pass it a percentage basis.
   4044 template <typename SizeOrMaxSize>
   4045 static Maybe<nscoord> GetPercentBSize(const SizeOrMaxSize& aSize,
   4046                                      nsIFrame* aFrame, bool aHorizontalAxis) {
   4047  if (!aSize->IsLengthPercentage()) {
   4048    return Nothing();
   4049  }
   4050  return GetPercentBSize(aSize->AsLengthPercentage(), aFrame, aHorizontalAxis);
   4051 }
   4052 
   4053 // Only call on aSize for which GetAbsoluteSize returned Nothing().
   4054 static Maybe<nscoord> GetPercentBSize(const LengthPercentage& aSize,
   4055                                      nsIFrame* aFrame, bool aHorizontalAxis) {
   4056  if (!aSize.HasPercent()) {
   4057    return Nothing();
   4058  }
   4059 
   4060  MOZ_ASSERT(!aSize.ConvertsToLength(),
   4061             "GetAbsoluteSize should have handled this");
   4062 
   4063  // During reflow, ScrollContainerFrame::ReflowScrolledFrame uses
   4064  // SetComputedHeight on the reflow input for its child to propagate its
   4065  // computed height to the scrolled content. So here we skip to the scroll
   4066  // frame that contains this scrolled content in order to get the same
   4067  // behavior as layout when computing percentage heights.
   4068  nsIFrame* f = aFrame->GetContainingBlock(nsIFrame::SKIP_SCROLLED_FRAME);
   4069  if (!f) {
   4070    MOZ_ASSERT_UNREACHABLE("top of frame tree not a containing block");
   4071    return Nothing();
   4072  }
   4073 
   4074  // Helper to compute the block-size, max-block-size, and min-block-size later
   4075  // in this function.
   4076  auto GetBSize = [&](const auto& aSize) {
   4077    return nsLayoutUtils::GetAbsoluteSize(*aSize).orElse(
   4078        [&]() { return GetPercentBSize(aSize, f, aHorizontalAxis); });
   4079  };
   4080 
   4081  WritingMode wm = f->GetWritingMode();
   4082  const nsStylePosition* pos = f->StylePosition();
   4083  const auto anchorResolutionParams = AnchorPosResolutionParams::From(f);
   4084  Maybe<nscoord> bSize = GetBSize(pos->BSize(wm, anchorResolutionParams));
   4085  if (!bSize) {
   4086    LayoutFrameType fType = f->Type();
   4087    if (fType != LayoutFrameType::Viewport &&
   4088        fType != LayoutFrameType::Canvas &&
   4089        fType != LayoutFrameType::PageContent) {
   4090      // There's no basis for the percentage height, so it acts like auto.
   4091      // Should we consider a max-height < min-height pair a basis for
   4092      // percentage heights?  The spec is somewhat unclear, and not doing
   4093      // so is simpler and avoids troubling discontinuities in behavior,
   4094      // so I'll choose not to. -LDB
   4095      return Nothing();
   4096    }
   4097    // For the viewport, canvas, and page-content kids, the percentage
   4098    // basis is just the parent block-size.
   4099    bSize.emplace(f->BSize(wm));
   4100    if (*bSize == NS_UNCONSTRAINEDSIZE) {
   4101      // We don't have a percentage basis after all
   4102      return Nothing();
   4103    }
   4104  }
   4105 
   4106  if (Maybe<nscoord> maxBSize =
   4107          GetBSize(pos->MaxBSize(wm, anchorResolutionParams))) {
   4108    if (*maxBSize < *bSize) {
   4109      *bSize = *maxBSize;
   4110    }
   4111  }
   4112 
   4113  if (Maybe<nscoord> minBSize =
   4114          GetBSize(pos->MinBSize(wm, anchorResolutionParams))) {
   4115    if (*minBSize > *bSize) {
   4116      *bSize = *minBSize;
   4117    }
   4118  }
   4119 
   4120  // If we're an abspos box, percentages in that case resolve against the
   4121  // padding box.
   4122  //
   4123  // TODO: This could conceivably cause some problems with fieldsets (which are
   4124  // the other place that wants to ignore padding), but solving that here
   4125  // without hardcoding a check for f being a fieldset-content frame is a bit of
   4126  // a pain.
   4127  const bool resolvesAgainstPaddingBox = aFrame->IsAbsolutelyPositioned();
   4128  *bSize += GetBSizePercentBasisAdjustment(pos->mBoxSizing, f, aHorizontalAxis,
   4129                                           resolvesAgainstPaddingBox);
   4130 
   4131  *bSize = std::max(*bSize, 0);
   4132  return Some(std::max(aSize.Resolve(*bSize), 0));
   4133 }
   4134 
   4135 // If aSize can be resolved to a definite value, returns it; otherwise returns
   4136 // Nothing().
   4137 static Maybe<nscoord> GetDefiniteSize(
   4138    const LengthPercentage& aSize, nsIFrame* aFrame, bool aIsInlineAxis,
   4139    const Maybe<LogicalSize>& aPercentageBasis) {
   4140  if (aSize.ConvertsToLength()) {
   4141    return Some(aSize.ToLength());
   4142  }
   4143 
   4144  if (!aPercentageBasis) {
   4145    return Nothing();
   4146  }
   4147 
   4148  auto wm = aFrame->GetWritingMode();
   4149  nscoord pb = aIsInlineAxis ? aPercentageBasis.value().ISize(wm)
   4150                             : aPercentageBasis.value().BSize(wm);
   4151  if (pb == NS_UNCONSTRAINEDSIZE) {
   4152    return Nothing();
   4153  }
   4154  return Some(std::max(0, aSize.Resolve(pb)));
   4155 }
   4156 
   4157 // If aSize can be resolved to a definite value, returns it; otherwise returns
   4158 // Nothing().
   4159 template <typename SizeOrMaxSize>
   4160 static Maybe<nscoord> GetDefiniteSize(
   4161    const SizeOrMaxSize& aSize, nsIFrame* aFrame, bool aIsInlineAxis,
   4162    const Maybe<LogicalSize>& aPercentageBasis) {
   4163  if (!aSize->IsLengthPercentage()) {
   4164    return Nothing();
   4165  }
   4166  return GetDefiniteSize(aSize->AsLengthPercentage(), aFrame, aIsInlineAxis,
   4167                         aPercentageBasis);
   4168 }
   4169 
   4170 // NOTE: this function will be replaced by GetDefiniteSizeTakenByBoxSizing (bug
   4171 // 1363918). Please do not add new uses of this function.
   4172 //
   4173 // Get the amount of space to add or subtract out of aFrame's 'block-size' or
   4174 // property value due its borders and paddings, given the box-sizing value in
   4175 // aBoxSizing.
   4176 //
   4177 // aHorizontalAxis is true if our inline direction is horizontal and our block
   4178 // direction is vertical. aResolvesAgainstPaddingBox is true if padding should
   4179 // be added or not removed.
   4180 static nscoord GetBSizePercentBasisAdjustment(StyleBoxSizing aBoxSizing,
   4181                                              nsIFrame* aFrame,
   4182                                              bool aHorizontalAxis,
   4183                                              bool aResolvesAgainstPaddingBox) {
   4184  nscoord adjustment = 0;
   4185  if (aBoxSizing == StyleBoxSizing::Border) {
   4186    const auto& border = aFrame->StyleBorder()->GetComputedBorder();
   4187    adjustment -= aHorizontalAxis ? border.TopBottom() : border.LeftRight();
   4188  }
   4189  if ((aBoxSizing == StyleBoxSizing::Border) == !aResolvesAgainstPaddingBox) {
   4190    const auto& stylePadding = aFrame->StylePadding()->mPadding;
   4191    const LengthPercentage& paddingStart =
   4192        stylePadding.Get(aHorizontalAxis ? eSideTop : eSideLeft);
   4193    const LengthPercentage& paddingEnd =
   4194        stylePadding.Get(aHorizontalAxis ? eSideBottom : eSideRight);
   4195 
   4196    // XXXbz Calling GetPercentBSize on padding values looks bogus, since
   4197    // percent padding is always a percentage of the inline-size of the
   4198    // containing block.  We should perhaps just treat non-absolute paddings
   4199    // here as 0 instead, except that in some cases the width may in fact be
   4200    // known.  See bug 1231059.
   4201    auto GetPadding = [&](const LengthPercentage& aPadding) {
   4202      return nsLayoutUtils::GetAbsoluteSize(aPadding).orElse(
   4203          [&]() { return GetPercentBSize(aPadding, aFrame, aHorizontalAxis); });
   4204    };
   4205    if (Maybe<nscoord> pad = GetPadding(paddingStart)) {
   4206      adjustment += aResolvesAgainstPaddingBox ? *pad : -*pad;
   4207    }
   4208    if (Maybe<nscoord> pad = GetPadding(paddingEnd)) {
   4209      adjustment += aResolvesAgainstPaddingBox ? *pad : -*pad;
   4210    }
   4211  }
   4212  return adjustment;
   4213 }
   4214 
   4215 // Get the amount of space taken out of aFrame's content area due to its
   4216 // borders and paddings given the box-sizing value in aBoxSizing.  We don't
   4217 // get aBoxSizing from the frame because some callers want to compute this for
   4218 // specific box-sizing values.
   4219 // aIsInlineAxis is true if we're computing for aFrame's inline axis.
   4220 // aIgnorePadding is true if padding should be ignored.
   4221 static nscoord GetDefiniteSizeTakenByBoxSizing(
   4222    StyleBoxSizing aBoxSizing, nsIFrame* aFrame, bool aIsInlineAxis,
   4223    bool aIgnorePadding, const Maybe<LogicalSize>& aPercentageBasis) {
   4224  nscoord sizeTakenByBoxSizing = 0;
   4225  if (MOZ_UNLIKELY(aBoxSizing == StyleBoxSizing::Border)) {
   4226    const bool isHorizontalAxis =
   4227        aIsInlineAxis == !aFrame->GetWritingMode().IsVertical();
   4228    const nsStyleBorder* styleBorder = aFrame->StyleBorder();
   4229    sizeTakenByBoxSizing = isHorizontalAxis
   4230                               ? styleBorder->GetComputedBorder().LeftRight()
   4231                               : styleBorder->GetComputedBorder().TopBottom();
   4232    if (!aIgnorePadding) {
   4233      const auto& stylePadding = aFrame->StylePadding()->mPadding;
   4234      const LengthPercentage& pStart =
   4235          stylePadding.Get(isHorizontalAxis ? eSideLeft : eSideTop);
   4236      const LengthPercentage& pEnd =
   4237          stylePadding.Get(isHorizontalAxis ? eSideRight : eSideBottom);
   4238 
   4239      // XXXbz Calling GetPercentBSize on padding values looks bogus, since
   4240      // percent padding is always a percentage of the inline-size of the
   4241      // containing block.  We should perhaps just treat non-absolute paddings
   4242      // here as 0 instead, except that in some cases the width may in fact be
   4243      // known.  See bug 1231059.
   4244      auto GetPadding =
   4245          [&](const LengthPercentage& aPadding) -> Maybe<nscoord> {
   4246        if (Maybe<nscoord> padding = GetDefiniteSize(
   4247                aPadding, aFrame, aIsInlineAxis, aPercentageBasis)) {
   4248          return padding;
   4249        }
   4250        if (aPercentageBasis) {
   4251          return Nothing();
   4252        }
   4253        return GetPercentBSize(aPadding, aFrame, isHorizontalAxis);
   4254      };
   4255      if (Maybe<nscoord> pad = GetPadding(pStart)) {
   4256        sizeTakenByBoxSizing += *pad;
   4257      }
   4258      if (Maybe<nscoord> pad = GetPadding(pEnd)) {
   4259        sizeTakenByBoxSizing += *pad;
   4260      }
   4261    }
   4262  }
   4263  return sizeTakenByBoxSizing;
   4264 }
   4265 
   4266 /**
   4267 * Handles only max-content and min-content, and
   4268 * -moz-fit-content for min-width and max-width, since the others
   4269 * (-moz-fit-content for width, and -moz-available) have no effect on
   4270 * intrinsic widths.
   4271 */
   4272 static Maybe<nscoord> GetIntrinsicSize(nsIFrame::ExtremumLength aLength,
   4273                                       gfxContext* aRenderingContext,
   4274                                       nsIFrame* aFrame,
   4275                                       Maybe<nscoord> aISizeFromAspectRatio,
   4276                                       nsIFrame::SizeProperty aProperty,
   4277                                       nscoord aContentBoxToBoxSizingDiff) {
   4278  if (aLength == nsIFrame::ExtremumLength::MozAvailable ||
   4279      aLength == nsIFrame::ExtremumLength::Stretch) {
   4280    return Nothing();
   4281  }
   4282 
   4283  if (aLength == nsIFrame::ExtremumLength::FitContentFunction) {
   4284    // fit-content() should be handled by the caller.
   4285    return Nothing();
   4286  }
   4287 
   4288  if (aLength == nsIFrame::ExtremumLength::FitContent) {
   4289    switch (aProperty) {
   4290      case nsIFrame::SizeProperty::Size:
   4291        // handle like 'width: auto'
   4292        return Nothing();
   4293      case nsIFrame::SizeProperty::MaxSize:
   4294        // constrain large 'width' values down to max-content
   4295        aLength = nsIFrame::ExtremumLength::MaxContent;
   4296        break;
   4297      case nsIFrame::SizeProperty::MinSize:
   4298        // constrain small 'width' or 'max-width' values up to min-content
   4299        aLength = nsIFrame::ExtremumLength::MinContent;
   4300        break;
   4301    }
   4302  }
   4303 
   4304  NS_ASSERTION(aLength == nsIFrame::ExtremumLength::MinContent ||
   4305                   aLength == nsIFrame::ExtremumLength::MaxContent,
   4306               "should have reduced everything remaining to one of these");
   4307 
   4308  // If aFrame is a container for font size inflation, then shrink
   4309  // wrapping inside of it should not apply font size inflation.
   4310  AutoMaybeDisableFontInflation an(aFrame);
   4311 
   4312  nscoord result;
   4313  if (aISizeFromAspectRatio) {
   4314    result = *aISizeFromAspectRatio;
   4315  } else {
   4316    // Bug 1363918: We need to refactor this function to compute a percentage
   4317    // basis when computing intrinsic sizes.
   4318    const IntrinsicSizeInput input(aRenderingContext, Nothing(), Nothing());
   4319    auto type = aLength == nsIFrame::ExtremumLength::MaxContent
   4320                    ? IntrinsicISizeType::PrefISize
   4321                    : IntrinsicISizeType::MinISize;
   4322    result = aFrame->IntrinsicISize(input, type);
   4323  }
   4324 
   4325  result += aContentBoxToBoxSizingDiff;
   4326  return Some(result);
   4327 }
   4328 
   4329 template <typename SizeOrMaxSize>
   4330 static Maybe<nscoord> GetIntrinsicSize(const SizeOrMaxSize& aSize,
   4331                                       gfxContext* aRenderingContext,
   4332                                       nsIFrame* aFrame,
   4333                                       Maybe<nscoord> aISizeFromAspectRatio,
   4334                                       nsIFrame::SizeProperty aProperty,
   4335                                       nscoord aContentBoxToBoxSizingDiff) {
   4336  auto length = nsIFrame::ToExtremumLength(aSize);
   4337  if (!length) {
   4338    return Nothing();
   4339  }
   4340  return GetIntrinsicSize(*length, aRenderingContext, aFrame,
   4341                          aISizeFromAspectRatio, aProperty,
   4342                          aContentBoxToBoxSizingDiff);
   4343 }
   4344 
   4345 static nscoord GetFitContentSizeForMaxOrPreferredSize(
   4346    const IntrinsicISizeType aType, const nsIFrame::SizeProperty aProperty,
   4347    const nsIFrame* aFrame, const LengthPercentage& aStyleSize,
   4348    const nscoord aInitialValue, const nscoord aMinContentSize,
   4349    const nscoord aMaxContentSize) {
   4350  MOZ_ASSERT(aProperty != nsIFrame::SizeProperty::MinSize);
   4351 
   4352  nscoord size;
   4353  // 1. Treat fit-content()'s arg as a plain LengthPercentage
   4354  // However, we have to handle the cyclic percentage contribution first.
   4355  //
   4356  // https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution
   4357  if (aType == IntrinsicISizeType::MinISize &&
   4358      aFrame->IsPercentageResolvedAgainstZero(aStyleSize, aProperty)) {
   4359    // Case (c) in the spec.
   4360    // FIXME: This doesn't follow the spec for calc(). We should fix this in
   4361    // Bug 1463700.
   4362    size = 0;
   4363  } else if (Maybe<nscoord> length =
   4364                 nsLayoutUtils::GetAbsoluteSize(aStyleSize)) {
   4365    size = *length;
   4366  } else {
   4367    // As initial value. Case (a) and (b) in the spec.
   4368    size = aInitialValue;
   4369  }
   4370 
   4371  // 2. Clamp size by min-content and max-content.
   4372  return std::clamp(size, aMinContentSize, aMaxContentSize);
   4373 }
   4374 
   4375 /**
   4376 * Add aOffsets which describes what to add on outside of the content box
   4377 * aContentSize (controlled by 'box-sizing') and apply min/max properties.
   4378 * We have to account for these properties after getting all the offsets
   4379 * (margin, border, padding) because percentages do not operate linearly.
   4380 * Doing this is ok because although percentages aren't handled linearly,
   4381 * they are handled monotonically.
   4382 *
   4383 * @param aContentSize the content size calculated so far
   4384                       (@see IntrinsicForAxis)
   4385 * @param aStyleSize a 'width' or 'height' property value
   4386 * @param aFixedMinSize if aStyleMinSize is a definite size then this contains
   4387 *                      the value, otherwise Nothing()
   4388 * @param aStyleMinSize a 'min-width' or 'min-height' property value
   4389 * @param aFixedMaxSize if aStyleMaxSize is a definite size then this contains
   4390 *                      the value, otherwise Nothing()
   4391 * @param aStyleMaxSize a 'max-width' or 'max-height' property value
   4392 * @param aISizeFromAspectRatio the content-box inline size computed from
   4393 *                              aspect-ratio and the definite block size.
   4394 *                              We use this value to resolve sizes in inline
   4395 *                              axis with intrinsic keyword.
   4396 * @param aFlags same as for IntrinsicForAxis
   4397 */
   4398 static nscoord AddIntrinsicSizeOffset(
   4399    gfxContext* aRenderingContext, nsIFrame* aFrame,
   4400    const nsIFrame::IntrinsicSizeOffsetData& aOffsets, IntrinsicISizeType aType,
   4401    StyleBoxSizing aBoxSizing, nscoord aContentSize,
   4402    const StyleSize& aStyleSize, const Maybe<nscoord> aFixedMinSize,
   4403    const StyleSize& aStyleMinSize, const Maybe<nscoord> aFixedMaxSize,
   4404    const StyleMaxSize& aStyleMaxSize, Maybe<nscoord> aISizeFromAspectRatio,
   4405    uint32_t aFlags, PhysicalAxis aAxis) {
   4406  const nscoord padding =
   4407      aFlags & nsLayoutUtils::IGNORE_PADDING ? 0 : aOffsets.padding;
   4408  nscoord contentBoxToBoxSizingDiff;
   4409  nscoord boxSizingToMarginDiff;
   4410 
   4411  // Note: |result| can be either the border-box size or the content-box size,
   4412  // depending on the value of aBoxSizing.
   4413  nscoord result;
   4414  if (aBoxSizing == StyleBoxSizing::Border) {
   4415    contentBoxToBoxSizingDiff = padding + aOffsets.border;
   4416    boxSizingToMarginDiff = aOffsets.margin;
   4417    result = NSCoordSaturatingAdd(aContentSize, contentBoxToBoxSizingDiff);
   4418  } else {
   4419    MOZ_ASSERT(aBoxSizing == StyleBoxSizing::Content);
   4420    contentBoxToBoxSizingDiff = 0;
   4421    boxSizingToMarginDiff = padding + aOffsets.border + aOffsets.margin;
   4422    result = aContentSize;
   4423  }
   4424 
   4425  // Compute min-content/max-content for fit-content().
   4426  nscoord minContent = 0;
   4427  nscoord maxContent = NS_UNCONSTRAINEDSIZE;
   4428  if (aStyleSize.IsFitContentFunction() ||
   4429      aStyleMaxSize.IsFitContentFunction() ||
   4430      aStyleMinSize.IsFitContentFunction()) {
   4431    if (aISizeFromAspectRatio) {
   4432      minContent = maxContent = *aISizeFromAspectRatio;
   4433    } else {
   4434      // Bug 1363918: We need to refactor this function to compute a percentage
   4435      // basis when computing intrinsic sizes.
   4436      const IntrinsicSizeInput input(aRenderingContext, Nothing(), Nothing());
   4437      minContent = aFrame->GetMinISize(input);
   4438      maxContent = aFrame->GetPrefISize(input);
   4439    }
   4440    minContent += contentBoxToBoxSizingDiff;
   4441    maxContent += contentBoxToBoxSizingDiff;
   4442  }
   4443 
   4444  // Compute size.
   4445  const bool isInlineAxis =
   4446      aAxis == aFrame->GetWritingMode().PhysicalAxis(LogicalAxis::Inline);
   4447  if (aType == IntrinsicISizeType::MinISize && isInlineAxis &&
   4448      aFrame->IsPercentageResolvedAgainstZero(aStyleSize, aStyleMaxSize)) {
   4449    // Apply the compressible min-content contribution rule only in aFrame's
   4450    // *inline* axis, to maintain web-compatibility with Chrome and Safari.
   4451    // https://drafts.csswg.org/css-sizing-3/#min-content-zero
   4452    // XXX bug 1463700: this doesn't handle calc() according to spec
   4453    result = 0;
   4454  } else if (Maybe<nscoord> size =
   4455                 nsLayoutUtils::GetAbsoluteSize(aStyleSize).orElse([&]() {
   4456                   return GetIntrinsicSize(aStyleSize, aRenderingContext,
   4457                                           aFrame, aISizeFromAspectRatio,
   4458                                           nsIFrame::SizeProperty::Size,
   4459                                           contentBoxToBoxSizingDiff);
   4460                 })) {
   4461    result = *size + boxSizingToMarginDiff;
   4462  } else if (aStyleSize.IsFitContentFunction()) {
   4463    // |result| here is the content size or border size, depends on
   4464    // StyleBoxSizing. We use it as the initial value when handling the cyclic
   4465    // percentage.
   4466    nscoord initial = result;
   4467    nscoord fitContentFuncSize = GetFitContentSizeForMaxOrPreferredSize(
   4468        aType, nsIFrame::SizeProperty::Size, aFrame,
   4469        aStyleSize.AsFitContentFunction(), initial, minContent, maxContent);
   4470    // Add border and padding.
   4471    result = NSCoordSaturatingAdd(fitContentFuncSize, boxSizingToMarginDiff);
   4472  } else {
   4473    result = NSCoordSaturatingAdd(result, boxSizingToMarginDiff);
   4474  }
   4475 
   4476  // Compute max-size.
   4477  Maybe<nscoord> maxSize = aFixedMaxSize.orElse([&]() {
   4478    return GetIntrinsicSize(
   4479        aStyleMaxSize, aRenderingContext, aFrame, aISizeFromAspectRatio,
   4480        nsIFrame::SizeProperty::MaxSize, contentBoxToBoxSizingDiff);
   4481  });
   4482  if (maxSize) {
   4483    *maxSize += boxSizingToMarginDiff;
   4484    if (result > *maxSize) {
   4485      result = *maxSize;
   4486    }
   4487  } else if (aStyleMaxSize.IsFitContentFunction()) {
   4488    nscoord fitContentFuncSize = GetFitContentSizeForMaxOrPreferredSize(
   4489        aType, nsIFrame::SizeProperty::MaxSize, aFrame,
   4490        aStyleMaxSize.AsFitContentFunction(), NS_UNCONSTRAINEDSIZE, minContent,
   4491        maxContent);
   4492    maxSize.emplace(
   4493        NSCoordSaturatingAdd(fitContentFuncSize, boxSizingToMarginDiff));
   4494    if (result > *maxSize) {
   4495      result = *maxSize;
   4496    }
   4497  }
   4498 
   4499  // Compute min-size.
   4500  Maybe<nscoord> minSize = aFixedMinSize.orElse([&]() {
   4501    return GetIntrinsicSize(
   4502        aStyleMinSize, aRenderingContext, aFrame, aISizeFromAspectRatio,
   4503        nsIFrame::SizeProperty::MinSize, contentBoxToBoxSizingDiff);
   4504  });
   4505  if (minSize) {
   4506    *minSize += boxSizingToMarginDiff;
   4507    if (result < *minSize) {
   4508      result = *minSize;
   4509    }
   4510  } else if (aStyleMinSize.IsFitContentFunction()) {
   4511    minSize =
   4512        nsLayoutUtils::GetAbsoluteSize(aStyleMinSize.AsFitContentFunction());
   4513    if (!minSize) {
   4514      // FIXME: Bug 1463700, we should resolve only the percentage part to 0
   4515      // such as min-width: fit-content(calc(50% + 50px)).
   4516      minSize.emplace(0);
   4517    }
   4518    nscoord fitContentFuncSize = CSSMinMax(*minSize, minContent, maxContent);
   4519    *minSize = NSCoordSaturatingAdd(fitContentFuncSize, boxSizingToMarginDiff);
   4520    if (result < *minSize) {
   4521      result = *minSize;
   4522    }
   4523  }
   4524 
   4525  const nscoord borderPaddingMargin =
   4526      contentBoxToBoxSizingDiff + boxSizingToMarginDiff;
   4527  result = std::max(result, borderPaddingMargin);
   4528 
   4529  const nsStyleDisplay* disp = aFrame->StyleDisplay();
   4530  if (aFrame->IsThemed(disp)) {
   4531    nsPresContext* pc = aFrame->PresContext();
   4532    LayoutDeviceIntSize devSize = pc->Theme()->GetMinimumWidgetSize(
   4533        pc, aFrame, disp->EffectiveAppearance());
   4534    nscoord themeSize = pc->DevPixelsToAppUnits(
   4535        aAxis == PhysicalAxis::Vertical ? devSize.height : devSize.width);
   4536    // GetMinimumWidgetSize() returns a border-box width.
   4537    themeSize += aOffsets.margin;
   4538    if (themeSize > result) {
   4539      result = themeSize;
   4540    }
   4541  }
   4542  return result;
   4543 }
   4544 
   4545 static void AddStateBitToAncestors(nsIFrame* aFrame, nsFrameState aBit) {
   4546  for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
   4547    if (f->HasAnyStateBits(aBit)) {
   4548      break;
   4549    }
   4550    f->AddStateBits(aBit);
   4551  }
   4552 }
   4553 
   4554 static nsSize MeasureIntrinsicContentSize(
   4555    gfxContext* aContext, nsIFrame* aFrame,
   4556    const Maybe<LogicalSize>& aPercentageBasis) {
   4557  nsPresContext* pc = aFrame->PresContext();
   4558  nsIFrame* parent = aFrame->GetParent();
   4559  const WritingMode parentWM = parent->GetWritingMode();
   4560  const WritingMode childWM = aFrame->GetWritingMode();
   4561 
   4562  // We only expect to hit this codepath when the frame creates an
   4563  // orthogonal flow.
   4564  MOZ_ASSERT(childWM.IsOrthogonalTo(parentWM));
   4565 
   4566  const ReflowInput dummyParentRI(
   4567      pc, parent, aContext,
   4568      LogicalSize(parentWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE),
   4569      ReflowInput::InitFlag::DummyParentReflowInput);
   4570  const ReflowInput reflowInput(
   4571      pc, dummyParentRI, aFrame,
   4572      LogicalSize(childWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE),
   4573      aPercentageBasis);
   4574 
   4575  // We call Reflow and FinishReflowChild just to measure the (desired) size of
   4576  // the child frame. It can not actually be positioned properly at this stage,
   4577  // since we don't have a known containing block; that will be done by the
   4578  // main reflow that's in progress.
   4579  ReflowOutput reflowOutput(reflowInput);
   4580  nsReflowStatus status;
   4581  aFrame->Reflow(pc, reflowOutput, reflowInput, status);
   4582  MOZ_ASSERT(status.IsFullyComplete());
   4583 
   4584  const nsIFrame::ReflowChildFlags flags =
   4585      nsIFrame::ReflowChildFlags::NoMoveFrame |
   4586      nsIFrame::ReflowChildFlags::NoDeleteNextInFlowChild;
   4587  nsContainerFrame::FinishReflowChild(aFrame, pc, reflowOutput, &reflowInput,
   4588                                      childWM, LogicalPoint(parentWM), nsSize(),
   4589                                      flags);
   4590 
   4591  // We're just returning the child's desired content size; IntrinsicForAxis
   4592  // accounts for border and padding later through AddIntrinsicSizeOffset.
   4593  return aFrame->ContentSize(childWM).GetPhysicalSize(childWM);
   4594 }
   4595 
   4596 /* static */
   4597 nscoord nsLayoutUtils::IntrinsicForAxis(
   4598    PhysicalAxis aAxis, gfxContext* aRenderingContext, nsIFrame* aFrame,
   4599    IntrinsicISizeType aType, const Maybe<LogicalSize>& aPercentageBasis,
   4600    uint32_t aFlags, nscoord aMarginBoxMinSizeClamp,
   4601    const StyleSizeOverrides& aSizeOverrides) {
   4602  MOZ_ASSERT(aFrame, "null frame");
   4603  MOZ_ASSERT(aFrame->GetParent(),
   4604             "IntrinsicForAxis called on frame not in tree");
   4605  MOZ_ASSERT(aFrame->GetParent()->Type() != LayoutFrameType::GridContainer ||
   4606                 aPercentageBasis.isSome(),
   4607             "grid layout should always pass a percentage basis");
   4608 
   4609  const bool horizontalAxis = MOZ_LIKELY(aAxis == PhysicalAxis::Horizontal);
   4610 
   4611  // If aFrame is a container for font size inflation, then shrink
   4612  // wrapping inside of it should not apply font size inflation.
   4613  AutoMaybeDisableFontInflation an(aFrame);
   4614 
   4615  // We want the size this frame will contribute to the parent's inline-size,
   4616  // so we work in the parent's writing mode; but if aFrame is orthogonal to
   4617  // its parent, we'll need to look at its BSize instead of min/pref-ISize.
   4618  const nsStylePosition* stylePos = aFrame->StylePosition();
   4619  StyleBoxSizing boxSizing = stylePos->mBoxSizing;
   4620  PhysicalAxis ourInlineAxis =
   4621      aFrame->GetWritingMode().PhysicalAxis(LogicalAxis::Inline);
   4622  const bool isInlineAxis = aAxis == ourInlineAxis;
   4623 
   4624  const auto anchorResolutionParams = AnchorPosResolutionParams::From(aFrame);
   4625  auto styleMinISize = horizontalAxis
   4626                           ? stylePos->GetMinWidth(anchorResolutionParams)
   4627                           : stylePos->GetMinHeight(anchorResolutionParams);
   4628  const Maybe<StyleSize>& styleISizeOverride =
   4629      isInlineAxis ? aSizeOverrides.mStyleISize : aSizeOverrides.mStyleBSize;
   4630  auto styleISize =
   4631      styleISizeOverride
   4632          ? AnchorResolvedSizeHelper::Overridden(*styleISizeOverride)
   4633          : (horizontalAxis ? stylePos->GetWidth(anchorResolutionParams)
   4634                            : stylePos->GetHeight(anchorResolutionParams));
   4635  auto styleMaxISize = horizontalAxis
   4636                           ? stylePos->GetMaxWidth(anchorResolutionParams)
   4637                           : stylePos->GetMaxHeight(anchorResolutionParams);
   4638 
   4639  auto ResetIfKeywords = [](AnchorResolvedSize& aSize,
   4640                            AnchorResolvedSize& aMinSize,
   4641                            AnchorResolvedMaxSize& aMaxSize) {
   4642    if (!aSize->IsLengthPercentage()) {
   4643      aSize = AnchorResolvedSizeHelper::Auto();
   4644    }
   4645    if (!aMinSize->IsLengthPercentage()) {
   4646      aMinSize = AnchorResolvedSizeHelper::Auto();
   4647    }
   4648    if (!aMaxSize->IsLengthPercentage()) {
   4649      aMaxSize = AnchorResolvedMaxSizeHelper::None();
   4650    }
   4651  };
   4652  // According to the spec, max-content and min-content should behave as the
   4653  // property's initial values in block axis.
   4654  // It also make senses to use the initial values for -moz-fit-content and
   4655  // -moz-available for intrinsic size in block axis. Therefore, we reset them
   4656  // if needed.
   4657  if (!isInlineAxis) {
   4658    ResetIfKeywords(styleISize, styleMinISize, styleMaxISize);
   4659  }
   4660 
   4661  // We build up the content box size, storing in |result|, and then
   4662  // adding padding, border and margin in AddIntrinsicSizeOffset().
   4663  nscoord result = 0;
   4664 
   4665  Maybe<nscoord> fixedMaxISize = GetAbsoluteSize(*styleMaxISize);
   4666  Maybe<nscoord> fixedMinISize;
   4667 
   4668  // Treat "min-width: auto" as 0.
   4669  if (styleMinISize->IsAuto()) {
   4670    // NOTE: Technically, "auto" is supposed to behave like "min-content" on
   4671    // flex items. However, we don't need to worry about that here, because
   4672    // flex items' min-sizes are intentionally ignored until the flex
   4673    // container explicitly considers them during space distribution.
   4674    fixedMinISize.emplace(0);
   4675  } else {
   4676    fixedMinISize = GetAbsoluteSize(*styleMinISize);
   4677  }
   4678 
   4679  // Handle elements with an intrinsic ratio (or size) and a specified
   4680  // height, min-height, or max-height.
   4681  // NOTE:
   4682  // 1. We treat "min-height:auto" as "0" for the purpose of this code,
   4683  // since that's what it means in all cases except for on flex items -- and
   4684  // even there, we're supposed to ignore it (i.e. treat it as 0) until the
   4685  // flex container explicitly considers it.
   4686  // 2. The 'B' in |styleBSize|, |styleMinBSize|, and |styleMaxBSize|
   4687  // represents the ratio-determining axis of |aFrame|. It could be the inline
   4688  // axis or the block axis of |aFrame|. (So we are calculating the size
   4689  // along the ratio-dependent axis in this if-branch.)
   4690  const Maybe<StyleSize>& styleBSizeOverride =
   4691      isInlineAxis ? aSizeOverrides.mStyleBSize : aSizeOverrides.mStyleISize;
   4692  auto styleBSize =
   4693      styleBSizeOverride
   4694          ? AnchorResolvedSizeHelper::Overridden(*styleBSizeOverride)
   4695          : (horizontalAxis ? stylePos->GetHeight(anchorResolutionParams)
   4696                            : stylePos->GetWidth(anchorResolutionParams));
   4697  auto styleMinBSize = horizontalAxis
   4698                           ? stylePos->GetMinHeight(anchorResolutionParams)
   4699                           : stylePos->GetMinWidth(anchorResolutionParams);
   4700  auto styleMaxBSize = horizontalAxis
   4701                           ? stylePos->GetMaxHeight(anchorResolutionParams)
   4702                           : stylePos->GetMaxWidth(anchorResolutionParams);
   4703 
   4704  // According to the spec, max-content and min-content should behave as the
   4705  // property's initial values in block axis.
   4706  // It also make senses to use the initial values for -moz-fit-content and
   4707  // -moz-available for intrinsic size in block axis. Therefore, we reset them
   4708  // if needed.
   4709  if (isInlineAxis) {
   4710    ResetIfKeywords(styleBSize, styleMinBSize, styleMaxBSize);
   4711  }
   4712 
   4713  auto childWM = aFrame->GetWritingMode();
   4714  nscoord pmPercentageBasis = NS_UNCONSTRAINEDSIZE;
   4715  if (aPercentageBasis.isSome()) {
   4716    // The padding/margin percentage basis is the inline-size in the parent's
   4717    // writing-mode.
   4718    pmPercentageBasis =
   4719        aFrame->GetParent()->GetWritingMode().IsOrthogonalTo(childWM)
   4720            ? aPercentageBasis->BSize(childWM)
   4721            : aPercentageBasis->ISize(childWM);
   4722  }
   4723  nsIFrame::IntrinsicSizeOffsetData offsetInRequestedAxis =
   4724      MOZ_LIKELY(isInlineAxis)
   4725          ? aFrame->IntrinsicISizeOffsets(pmPercentageBasis)
   4726          : aFrame->IntrinsicBSizeOffsets(pmPercentageBasis);
   4727 
   4728  auto GetContentEdgeToBoxSizing = [&](const StyleBoxSizing aBoxSizing) {
   4729    if (aBoxSizing == StyleBoxSizing::Content) {
   4730      return LogicalSize(childWM);
   4731    }
   4732    nsIFrame::IntrinsicSizeOffsetData offsetInOtherAxis =
   4733        MOZ_LIKELY(isInlineAxis)
   4734            ? aFrame->IntrinsicBSizeOffsets(pmPercentageBasis)
   4735            : aFrame->IntrinsicISizeOffsets(pmPercentageBasis);
   4736    const auto& inlineOffset =
   4737        isInlineAxis ? offsetInRequestedAxis : offsetInOtherAxis;
   4738    const auto& blockOffset =
   4739        isInlineAxis ? offsetInOtherAxis : offsetInRequestedAxis;
   4740    return LogicalSize(childWM, inlineOffset.BorderPadding(),
   4741                       blockOffset.BorderPadding());
   4742  };
   4743 
   4744  // Helper to compute the block-size, max-block-size, and min-block-size later
   4745  // in this function.
   4746  auto GetBSize = [&](const auto& aSize) -> Maybe<nscoord> {
   4747    if (Maybe<nscoord> bSize =
   4748            GetDefiniteSize(aSize, aFrame, !isInlineAxis, aPercentageBasis)) {
   4749      return bSize;
   4750    }
   4751    if (aPercentageBasis) {
   4752      return Nothing();
   4753    }
   4754    // Find percentage basis, then compute.
   4755    return GetPercentBSize(aSize, aFrame, horizontalAxis);
   4756  };
   4757 
   4758  Maybe<nscoord> iSizeFromAspectRatio;
   4759  Maybe<LogicalSize> contentEdgeToBoxSizing;
   4760 
   4761  const bool ignorePadding =
   4762      (aFlags & IGNORE_PADDING) || aFrame->IsAbsolutelyPositioned();
   4763 
   4764  // If we have a specified width (or a specified 'min-width' greater
   4765  // than the specified 'max-width', which works out to the same thing),
   4766  // don't even bother getting the frame's intrinsic width, because in
   4767  // this case GetAbsoluteSize(styleISize) will always succeed, so
   4768  // we'll never need the intrinsic dimensions.
   4769  if (styleISize->IsMaxContent() || styleISize->IsMinContent()) {
   4770    MOZ_ASSERT(isInlineAxis);
   4771    // -moz-fit-content and -moz-available enumerated widths compute intrinsic
   4772    // widths just like auto.
   4773    // For max-content and min-content, we handle them like
   4774    // specified widths, but ignore box-sizing.
   4775    boxSizing = StyleBoxSizing::Content;
   4776  } else if (!styleISize->ConvertsToLength() &&
   4777             !(styleISize->IsFitContentFunction() &&
   4778               styleISize->AsFitContentFunction().ConvertsToLength()) &&
   4779             !(fixedMaxISize && fixedMinISize &&
   4780               *fixedMaxISize <= *fixedMinISize)) {
   4781    if (MOZ_UNLIKELY(!isInlineAxis)) {
   4782      IntrinsicSize intrinsicSize = aFrame->GetIntrinsicSize();
   4783      const auto& intrinsicBSize =
   4784          horizontalAxis ? intrinsicSize.width : intrinsicSize.height;
   4785      if (intrinsicBSize) {
   4786        result = *intrinsicBSize;
   4787      } else {
   4788        // We don't have an intrinsic bsize and we need aFrame's block-dir size.
   4789        if (aFlags & BAIL_IF_REFLOW_NEEDED) {
   4790          return NS_INTRINSIC_ISIZE_UNKNOWN;
   4791        }
   4792        nsSize size = MeasureIntrinsicContentSize(aRenderingContext, aFrame,
   4793                                                  aPercentageBasis);
   4794        result = horizontalAxis ? size.Width() : size.Height();
   4795      }
   4796    } else {
   4797      // To resolve aFrame's intrinsic inline size, we first check if we can
   4798      // resolve a block-axis percentage basis for aFrame's children. This can
   4799      // influence their inline size contributions, e.g. if they have an
   4800      // aspect-ratio and a percentage-based block size.
   4801      const nscoord percentageBasisBSizeForFrame =
   4802          aPercentageBasis ? aPercentageBasis->BSize(childWM)
   4803                           : NS_UNCONSTRAINEDSIZE;
   4804      nscoord percentageBasisBSizeForChildren;
   4805      if (aFrame->IsBlockContainer()) {
   4806        // Compute and cache the box-sizing adjustment in contentEdgeToBoxSizing
   4807        // for later use within this function.
   4808        contentEdgeToBoxSizing.emplace(GetContentEdgeToBoxSizing(boxSizing));
   4809 
   4810        // aFrame is a containing block, so its block size (with min and max
   4811        // block size constraints applied) serves as the percentage basis for
   4812        // its children.
   4813        percentageBasisBSizeForChildren =
   4814            nsIFrame::ComputeBSizeValueAsPercentageBasis(
   4815                *styleBSize, *styleMinBSize, *styleMaxBSize,
   4816                percentageBasisBSizeForFrame,
   4817                contentEdgeToBoxSizing->BSize(childWM));
   4818      } else {
   4819        // aFrame is not a containing block, so its children share the same
   4820        // containing block as aFrame. Therefore, the percentage basis for
   4821        // aFrame's children is the same as that for aFrame.
   4822        percentageBasisBSizeForChildren = percentageBasisBSizeForFrame;
   4823      }
   4824      const IntrinsicSizeInput input(
   4825          aRenderingContext, aPercentageBasis,
   4826          Some(LogicalSize(childWM, NS_UNCONSTRAINEDSIZE,
   4827                           percentageBasisBSizeForChildren)));
   4828      result = aFrame->IntrinsicISize(input, aType);
   4829    }
   4830 
   4831    // If our BSize or min/max-BSize properties are set to values that we can
   4832    // resolve and that will impose a constraint when transferred through our
   4833    // aspect ratio (if we have one), then compute and apply that constraint.
   4834    //
   4835    // (Note: This helper-bool & lambda just let us optimize away the actual
   4836    // transferring-and-clamping arithmetic, for the common case where we can
   4837    // tell that none of the block-axis size properties establish a meaningful
   4838    // transferred constraint.)
   4839    const bool mightHaveBlockAxisConstraintToTransfer = [&] {
   4840      if (!styleBSize->BehavesLikeInitialValueOnBlockAxis()) {
   4841        return true;  // BSize property might have a constraint to transfer.
   4842      }
   4843      // Check for min-BSize values that would obviously produce zero in the
   4844      // transferring logic that follows; zero is trivially-ignorable as a
   4845      // transferred lower-bound. (These include the the property's initial
   4846      // value, explicit 0, and values that are equivalent to these.)
   4847      bool minBSizeHasNoConstraintToTransfer =
   4848          styleMinBSize->BehavesLikeInitialValueOnBlockAxis() ||
   4849          (styleMinBSize->IsLengthPercentage() &&
   4850           styleMinBSize->AsLengthPercentage().IsDefinitelyZero());
   4851      if (!minBSizeHasNoConstraintToTransfer) {
   4852        return true;  // min-BSize property might have a constraint to transfer.
   4853      }
   4854      if (!styleMaxBSize->BehavesLikeInitialValueOnBlockAxis()) {
   4855        return true;  // max-BSize property might have a constraint to transfer.
   4856      }
   4857      return false;
   4858    }();
   4859    if (mightHaveBlockAxisConstraintToTransfer) {
   4860      if (AspectRatio ratio = aFrame->GetAspectRatio()) {
   4861        AddStateBitToAncestors(
   4862            aFrame, NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
   4863 
   4864        nscoord bSizeTakenByBoxSizing = GetDefiniteSizeTakenByBoxSizing(
   4865            boxSizing, aFrame, !isInlineAxis, ignorePadding, aPercentageBasis);
   4866        if (!contentEdgeToBoxSizing) {
   4867          contentEdgeToBoxSizing.emplace(GetContentEdgeToBoxSizing(boxSizing));
   4868        }
   4869 
   4870        if (Maybe<nscoord> bSize = GetBSize(styleBSize)) {
   4871          *bSize = std::max(0, *bSize - bSizeTakenByBoxSizing);
   4872          // We are computing the size of |aFrame|, so we use the inline & block
   4873          // dimensions of |aFrame|.
   4874          result = ratio.ComputeRatioDependentSize(
   4875              isInlineAxis ? LogicalAxis::Inline : LogicalAxis::Block, childWM,
   4876              *bSize, *contentEdgeToBoxSizing);
   4877          // We have got the iSizeForAspectRatio value, so we don't need to
   4878          // compute this again below.
   4879          iSizeFromAspectRatio.emplace(result);
   4880        }
   4881 
   4882        if (Maybe<nscoord> maxBSize = GetBSize(styleMaxBSize)) {
   4883          *maxBSize = std::max(0, *maxBSize - bSizeTakenByBoxSizing);
   4884          nscoord maxISize = ratio.ComputeRatioDependentSize(
   4885              isInlineAxis ? LogicalAxis::Inline : LogicalAxis::Block, childWM,
   4886              *maxBSize, *contentEdgeToBoxSizing);
   4887          result = std::min(result, maxISize);
   4888        }
   4889 
   4890        if (Maybe<nscoord> minBSize = GetBSize(styleMinBSize)) {
   4891          *minBSize = std::max(0, *minBSize - bSizeTakenByBoxSizing);
   4892          nscoord minISize = ratio.ComputeRatioDependentSize(
   4893              isInlineAxis ? LogicalAxis::Inline : LogicalAxis::Block, childWM,
   4894              *minBSize, *contentEdgeToBoxSizing);
   4895          result = std::max(result, minISize);
   4896        }
   4897      }
   4898    }
   4899  }
   4900 
   4901  // If we have an aspect-ratio and a definite block size of |aFrame|, we should
   4902  // use them to resolve the sizes with intrinsic keywords in the inline axis.
   4903  // If |aAxis| is the block axis of |aFrame|, intrinsic keywords should behaves
   4904  // as auto, so we don't need this.
   4905  // https://github.com/w3c/csswg-drafts/issues/5032
   4906  const AspectRatio ar = aFrame->GetAspectRatio();
   4907  if (isInlineAxis && ar && !iSizeFromAspectRatio &&
   4908      (nsIFrame::IsIntrinsicKeyword(*styleISize) ||
   4909       nsIFrame::IsIntrinsicKeyword(*styleMinISize) ||
   4910       nsIFrame::IsIntrinsicKeyword(*styleMaxISize))) {
   4911    if (Maybe<nscoord> bSize = GetBSize(styleBSize)) {
   4912      // We cannot reuse |boxSizing| because it may be updated to content-box
   4913      // in the above if-branch.
   4914      const StyleBoxSizing boxSizingForAR = stylePos->mBoxSizing;
   4915      if (!contentEdgeToBoxSizing) {
   4916        contentEdgeToBoxSizing.emplace(
   4917            GetContentEdgeToBoxSizing(boxSizingForAR));
   4918      }
   4919      nscoord bSizeTakenByBoxSizing =
   4920          GetDefiniteSizeTakenByBoxSizing(boxSizingForAR, aFrame, !isInlineAxis,
   4921                                          ignorePadding, aPercentageBasis);
   4922 
   4923      *bSize -= bSizeTakenByBoxSizing;
   4924      iSizeFromAspectRatio.emplace(ar.ComputeRatioDependentSize(
   4925          LogicalAxis::Inline, childWM, *bSize, *contentEdgeToBoxSizing));
   4926    }
   4927  }
   4928 
   4929  nscoord contentBoxSize = result;
   4930  result = AddIntrinsicSizeOffset(
   4931      aRenderingContext, aFrame, offsetInRequestedAxis, aType, boxSizing,
   4932      result, *styleISize, fixedMinISize, *styleMinISize, fixedMaxISize,
   4933      *styleMaxISize, iSizeFromAspectRatio, aFlags, aAxis);
   4934  nscoord overflow = result - aMarginBoxMinSizeClamp;
   4935  if (MOZ_UNLIKELY(overflow > 0)) {
   4936    nscoord newContentBoxSize = std::max(nscoord(0), contentBoxSize - overflow);
   4937    result -= contentBoxSize - newContentBoxSize;
   4938  }
   4939 
   4940  return result;
   4941 }
   4942 
   4943 /* static */
   4944 nscoord nsLayoutUtils::IntrinsicForContainer(
   4945    gfxContext* aRenderingContext, nsIFrame* aFrame, IntrinsicISizeType aType,
   4946    const Maybe<LogicalSize>& aPercentageBasis, uint32_t aFlags,
   4947    const StyleSizeOverrides& aSizeOverrides) {
   4948  MOZ_ASSERT(aFrame && aFrame->GetParent());
   4949  // We want the size aFrame will contribute to its parent's inline-size.
   4950  PhysicalAxis axis =
   4951      aFrame->GetParent()->GetWritingMode().PhysicalAxis(LogicalAxis::Inline);
   4952  return IntrinsicForAxis(axis, aRenderingContext, aFrame, aType,
   4953                          aPercentageBasis, aFlags, NS_MAXSIZE, aSizeOverrides);
   4954 }
   4955 
   4956 /* static */
   4957 nscoord nsLayoutUtils::MinSizeContributionForAxis(
   4958    PhysicalAxis aAxis, gfxContext* aRC, nsIFrame* aFrame,
   4959    IntrinsicISizeType aType, const LogicalSize& aPercentageBasis,
   4960    uint32_t aFlags) {
   4961  MOZ_ASSERT(aFrame);
   4962  MOZ_ASSERT(aFrame->IsFlexOrGridItem(),
   4963             "only grid/flex items have this behavior currently");
   4964 
   4965  // Note: this method is only meant for grid/flex items.
   4966  const nsStylePosition* const stylePos = aFrame->StylePosition();
   4967  const auto anchorResolutionParams = AnchorPosResolutionParams::From(aFrame);
   4968  auto size = aAxis == PhysicalAxis::Horizontal
   4969                  ? stylePos->GetMinWidth(anchorResolutionParams)
   4970                  : stylePos->GetMinHeight(anchorResolutionParams);
   4971  auto maxSize = aAxis == PhysicalAxis::Horizontal
   4972                     ? stylePos->GetMaxWidth(anchorResolutionParams)
   4973                     : stylePos->GetMaxHeight(anchorResolutionParams);
   4974  auto childWM = aFrame->GetWritingMode();
   4975  PhysicalAxis ourInlineAxis = childWM.PhysicalAxis(LogicalAxis::Inline);
   4976  // According to the spec, max-content and min-content should behave as the
   4977  // property's initial values in block axis.
   4978  // It also make senses to use the initial values for -moz-fit-content and
   4979  // -moz-available for intrinsic size in block axis. Therefore, we reset them
   4980  // if needed.
   4981  if (aAxis != ourInlineAxis) {
   4982    if (size->BehavesLikeInitialValueOnBlockAxis()) {
   4983      size = AnchorResolvedSizeHelper::Auto();
   4984    }
   4985    if (maxSize->BehavesLikeInitialValueOnBlockAxis()) {
   4986      maxSize = AnchorResolvedMaxSizeHelper::None();
   4987    }
   4988  }
   4989 
   4990  Maybe<nscoord> fixedMinSize;
   4991  if (size->IsAuto()) {
   4992    if (aFrame->StyleDisplay()->IsScrollableOverflow()) {
   4993      // min-[width|height]:auto with scrollable overflow computes to
   4994      // zero.
   4995      fixedMinSize.emplace(0);
   4996    } else {
   4997      size = aAxis == PhysicalAxis::Horizontal
   4998                 ? stylePos->GetWidth(anchorResolutionParams)
   4999                 : stylePos->GetHeight(anchorResolutionParams);
   5000      // This is same as above: keywords should behaves as property's initial
   5001      // values in block axis.
   5002      if (aAxis != ourInlineAxis &&
   5003          size->BehavesLikeInitialValueOnBlockAxis()) {
   5004        size = AnchorResolvedSizeHelper::Auto();
   5005      }
   5006 
   5007      fixedMinSize = GetAbsoluteSize(*size);
   5008      if (fixedMinSize) {
   5009        // We have a definite width/height.  This is the "specified size" in:
   5010        // https://drafts.csswg.org/css-grid/#min-size-auto
   5011      } else if (aFrame->IsPercentageResolvedAgainstZero(*size, *maxSize)) {
   5012        // XXX bug 1463700: this doesn't handle calc() according to spec
   5013        fixedMinSize.emplace(0);
   5014      }
   5015      // fall through - the caller will have to deal with "transferred size"
   5016    }
   5017  } else {
   5018    fixedMinSize = GetAbsoluteSize(*size);
   5019    if (!fixedMinSize && size->IsLengthPercentage()) {
   5020      MOZ_ASSERT(size->HasPercent());
   5021      fixedMinSize.emplace(0);
   5022    }
   5023  }
   5024 
   5025  if (!fixedMinSize) {
   5026    // Let the caller deal with the "content size" cases.
   5027    return NS_UNCONSTRAINEDSIZE;
   5028  }
   5029 
   5030  // If aFrame is a container for font size inflation, then shrink
   5031  // wrapping inside of it should not apply font size inflation.
   5032  AutoMaybeDisableFontInflation an(aFrame);
   5033 
   5034  // The padding/margin percentage basis is the inline-size in the parent's
   5035  // writing-mode.
   5036  nscoord pmPercentageBasis =
   5037      aFrame->GetParent()->GetWritingMode().IsOrthogonalTo(childWM)
   5038          ? aPercentageBasis.BSize(childWM)
   5039          : aPercentageBasis.ISize(childWM);
   5040  nsIFrame::IntrinsicSizeOffsetData offsets =
   5041      ourInlineAxis == aAxis ? aFrame->IntrinsicISizeOffsets(pmPercentageBasis)
   5042                             : aFrame->IntrinsicBSizeOffsets(pmPercentageBasis);
   5043  nscoord result = 0;
   5044  // Note: aISizeFromAspectRatio is Nothing() here because we don't handle
   5045  // "content size" cases here (i.e. we've returned earlier when |fixedMinSize|
   5046  // is Nothing()).
   5047  result = AddIntrinsicSizeOffset(
   5048      aRC, aFrame, offsets, aType, stylePos->mBoxSizing, result, *size,
   5049      fixedMinSize, *size, Nothing(), *maxSize, Nothing(), aFlags, aAxis);
   5050 
   5051  return result;
   5052 }
   5053 
   5054 /* static */
   5055 void nsLayoutUtils::MarkDescendantsDirty(nsIFrame* aSubtreeRoot) {
   5056  AutoTArray<nsIFrame*, 4> subtrees;
   5057  subtrees.AppendElement(aSubtreeRoot);
   5058 
   5059  // dirty descendants, iterating over subtrees that may include
   5060  // additional subtrees associated with placeholders
   5061  do {
   5062    nsIFrame* subtreeRoot = subtrees.PopLastElement();
   5063 
   5064    // Mark all descendants dirty (using an nsTArray stack rather than
   5065    // recursion).
   5066    // Note that ReflowInput::InitResizeFlags has some similar
   5067    // code; see comments there for how and why it differs.
   5068    AutoTArray<nsIFrame*, 32> stack;
   5069    stack.AppendElement(subtreeRoot);
   5070 
   5071    do {
   5072      nsIFrame* f = stack.PopLastElement();
   5073 
   5074      f->MarkIntrinsicISizesDirty();
   5075 
   5076      if (f->IsPlaceholderFrame()) {
   5077        nsIFrame* oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
   5078        if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) {
   5079          // We have another distinct subtree we need to mark.
   5080          subtrees.AppendElement(oof);
   5081        }
   5082      }
   5083 
   5084      for (const auto& childList : f->ChildLists()) {
   5085        for (nsIFrame* kid : childList.mList) {
   5086          stack.AppendElement(kid);
   5087        }
   5088      }
   5089    } while (stack.Length() != 0);
   5090  } while (subtrees.Length() != 0);
   5091 }
   5092 
   5093 /* static */
   5094 void nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(
   5095    nsIFrame* aFrame) {
   5096  AutoTArray<nsIFrame*, 32> stack;
   5097  stack.AppendElement(aFrame);
   5098 
   5099  do {
   5100    nsIFrame* f = stack.PopLastElement();
   5101 
   5102    if (!f->HasAnyStateBits(
   5103            NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
   5104      continue;
   5105    }
   5106    f->MarkIntrinsicISizesDirty();
   5107 
   5108    for (const auto& childList : f->ChildLists()) {
   5109      for (nsIFrame* kid : childList.mList) {
   5110        stack.AppendElement(kid);
   5111      }
   5112    }
   5113  } while (stack.Length() != 0);
   5114 }
   5115 
   5116 nsSize nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(
   5117    nscoord minWidth, nscoord minHeight, nscoord maxWidth, nscoord maxHeight,
   5118    nscoord tentWidth, nscoord tentHeight) {
   5119  // Now apply min/max-width/height - CSS 2.1 sections 10.4 and 10.7:
   5120 
   5121  if (minWidth > maxWidth) {
   5122    maxWidth = minWidth;
   5123  }
   5124  if (minHeight > maxHeight) {
   5125    maxHeight = minHeight;
   5126  }
   5127 
   5128  nscoord heightAtMaxWidth, heightAtMinWidth, widthAtMaxHeight,
   5129      widthAtMinHeight;
   5130 
   5131  if (tentWidth > 0) {
   5132    heightAtMaxWidth = NSCoordMulDiv(maxWidth, tentHeight, tentWidth);
   5133    if (heightAtMaxWidth < minHeight) {
   5134      heightAtMaxWidth = minHeight;
   5135    }
   5136    heightAtMinWidth = NSCoordMulDiv(minWidth, tentHeight, tentWidth);
   5137    if (heightAtMinWidth > maxHeight) {
   5138      heightAtMinWidth = maxHeight;
   5139    }
   5140  } else {
   5141    heightAtMaxWidth = heightAtMinWidth =
   5142        CSSMinMax(tentHeight, minHeight, maxHeight);
   5143  }
   5144 
   5145  if (tentHeight > 0) {
   5146    widthAtMaxHeight = NSCoordMulDiv(maxHeight, tentWidth, tentHeight);
   5147    if (widthAtMaxHeight < minWidth) {
   5148      widthAtMaxHeight = minWidth;
   5149    }
   5150    widthAtMinHeight = NSCoordMulDiv(minHeight, tentWidth, tentHeight);
   5151    if (widthAtMinHeight > maxWidth) {
   5152      widthAtMinHeight = maxWidth;
   5153    }
   5154  } else {
   5155    widthAtMaxHeight = widthAtMinHeight =
   5156        CSSMinMax(tentWidth, minWidth, maxWidth);
   5157  }
   5158 
   5159  // The table at http://www.w3.org/TR/CSS21/visudet.html#min-max-widths :
   5160 
   5161  nscoord width, height;
   5162 
   5163  if (tentWidth > maxWidth) {
   5164    if (tentHeight > maxHeight) {
   5165      if (int64_t(maxWidth) * int64_t(tentHeight) <=
   5166          int64_t(maxHeight) * int64_t(tentWidth)) {
   5167        width = maxWidth;
   5168        height = heightAtMaxWidth;
   5169      } else {
   5170        width = widthAtMaxHeight;
   5171        height = maxHeight;
   5172      }
   5173    } else {
   5174      // This also covers "(w > max-width) and (h < min-height)" since in
   5175      // that case (max-width/w < 1), and with (h < min-height):
   5176      //   max(max-width * h/w, min-height) == min-height
   5177      width = maxWidth;
   5178      height = heightAtMaxWidth;
   5179    }
   5180  } else if (tentWidth < minWidth) {
   5181    if (tentHeight < minHeight) {
   5182      if (int64_t(minWidth) * int64_t(tentHeight) <=
   5183          int64_t(minHeight) * int64_t(tentWidth)) {
   5184        width = widthAtMinHeight;
   5185        height = minHeight;
   5186      } else {
   5187        width = minWidth;
   5188        height = heightAtMinWidth;
   5189      }
   5190    } else {
   5191      // This also covers "(w < min-width) and (h > max-height)" since in
   5192      // that case (min-width/w > 1), and with (h > max-height):
   5193      //   min(min-width * h/w, max-height) == max-height
   5194      width = minWidth;
   5195      height = heightAtMinWidth;
   5196    }
   5197  } else {
   5198    if (tentHeight > maxHeight) {
   5199      width = widthAtMaxHeight;
   5200      height = maxHeight;
   5201    } else if (tentHeight < minHeight) {
   5202      width = widthAtMinHeight;
   5203      height = minHeight;
   5204    } else {
   5205      width = tentWidth;
   5206      height = tentHeight;
   5207    }
   5208  }
   5209 
   5210  return nsSize(width, height);
   5211 }
   5212 
   5213 static nscolor DarkenColor(nscolor aColor) {
   5214  uint16_t hue, sat, value;
   5215  uint8_t alpha;
   5216 
   5217  // convert the RBG to HSV so we can get the lightness (which is the v)
   5218  NS_RGB2HSV(aColor, hue, sat, value, alpha);
   5219 
   5220  // The goal here is to send white to black while letting colored
   5221  // stuff stay colored... So we adopt the following approach.
   5222  // Something with sat = 0 should end up with value = 0.  Something
   5223  // with a high sat can end up with a high value and it's ok.... At
   5224  // the same time, we don't want to make things lighter.  Do
   5225  // something simple, since it seems to work.
   5226  if (value > sat) {
   5227    value = sat;
   5228    // convert this color back into the RGB color space.
   5229    NS_HSV2RGB(aColor, hue, sat, value, alpha);
   5230  }
   5231  return aColor;
   5232 }
   5233 
   5234 // Check whether we should darken text/decoration colors. We need to do this if
   5235 // background images and colors are being suppressed, because that means
   5236 // light text will not be visible against the (presumed light-colored)
   5237 // background.
   5238 static bool ShouldDarkenColors(nsIFrame* aFrame) {
   5239  nsPresContext* pc = aFrame->PresContext();
   5240  if (pc->GetBackgroundColorDraw() || pc->GetBackgroundImageDraw()) {
   5241    return false;
   5242  }
   5243  return aFrame->StyleVisibility()->mPrintColorAdjust !=
   5244         StylePrintColorAdjust::Exact;
   5245 }
   5246 
   5247 nscolor nsLayoutUtils::DarkenColorIfNeeded(nsIFrame* aFrame, nscolor aColor) {
   5248  return ShouldDarkenColors(aFrame) ? DarkenColor(aColor) : aColor;
   5249 }
   5250 
   5251 gfxFloat nsLayoutUtils::GetMaybeSnappedBaselineY(nsIFrame* aFrame,
   5252                                                 gfxContext* aContext,
   5253                                                 nscoord aY, nscoord aAscent) {
   5254  gfxFloat baseline = gfxFloat(aY) + aAscent;
   5255  // TODO: Remove this function when this pref is being removed.
   5256  if (StaticPrefs::layout_disable_pixel_alignment()) {
   5257    return baseline;
   5258  }
   5259 
   5260  if (aContext->CurrentMatrix().IsSingular()) {
   5261    return baseline;
   5262  }
   5263 
   5264  gfxFloat appUnitsPerDevUnit = aFrame->PresContext()->AppUnitsPerDevPixel();
   5265  gfxRect putativeRect(0, baseline / appUnitsPerDevUnit, 1, 1);
   5266  if (!aContext->UserToDevicePixelSnapped(
   5267          putativeRect, gfxContext::SnapOption::IgnoreScale)) {
   5268    return baseline;
   5269  }
   5270  return aContext->DeviceToUser(putativeRect.TopLeft()).y * appUnitsPerDevUnit;
   5271 }
   5272 
   5273 gfxFloat nsLayoutUtils::GetMaybeSnappedBaselineX(nsIFrame* aFrame,
   5274                                                 gfxContext* aContext,
   5275                                                 nscoord aX, nscoord aAscent) {
   5276  gfxFloat baseline = gfxFloat(aX) + aAscent;
   5277  // TODO: Remove this function when this pref is being removed.
   5278  if (StaticPrefs::layout_disable_pixel_alignment()) {
   5279    return baseline;
   5280  }
   5281 
   5282  if (aContext->CurrentMatrix().IsSingular()) {
   5283    return baseline;
   5284  }
   5285 
   5286  gfxFloat appUnitsPerDevUnit = aFrame->PresContext()->AppUnitsPerDevPixel();
   5287  gfxRect putativeRect(baseline / appUnitsPerDevUnit, 0, 1, 1);
   5288  if (!aContext->UserToDevicePixelSnapped(
   5289          putativeRect, gfxContext::SnapOption::IgnoreScale)) {
   5290    return baseline;
   5291  }
   5292  return aContext->DeviceToUser(putativeRect.TopLeft()).x * appUnitsPerDevUnit;
   5293 }
   5294 
   5295 // Hard limit substring lengths to 8000 characters ... this lets us statically
   5296 // size the cluster buffer array in FindSafeLength
   5297 #define MAX_GFX_TEXT_BUF_SIZE 8000
   5298 
   5299 static int32_t FindSafeLength(const char16_t* aString, uint32_t aLength,
   5300                              uint32_t aMaxChunkLength) {
   5301  if (aLength <= aMaxChunkLength) {
   5302    return aLength;
   5303  }
   5304 
   5305  int32_t len = aMaxChunkLength;
   5306 
   5307  // Ensure that we don't break inside a surrogate pair
   5308  while (len > 0 && NS_IS_LOW_SURROGATE(aString[len])) {
   5309    len--;
   5310  }
   5311  if (len == 0) {
   5312    // We don't want our caller to go into an infinite loop, so don't
   5313    // return zero. It's hard to imagine how we could actually get here
   5314    // unless there are languages that allow clusters of arbitrary size.
   5315    // If there are and someone feeds us a 500+ character cluster, too
   5316    // bad.
   5317    return aMaxChunkLength;
   5318  }
   5319  return len;
   5320 }
   5321 
   5322 static int32_t GetMaxChunkLength(nsFontMetrics& aFontMetrics) {
   5323  return std::min(aFontMetrics.GetMaxStringLength(), MAX_GFX_TEXT_BUF_SIZE);
   5324 }
   5325 
   5326 nscoord nsLayoutUtils::AppUnitWidthOfString(const char16_t* aString,
   5327                                            uint32_t aLength,
   5328                                            nsFontMetrics& aFontMetrics,
   5329                                            DrawTarget* aDrawTarget) {
   5330  uint32_t maxChunkLength = GetMaxChunkLength(aFontMetrics);
   5331  nscoord width = 0;
   5332  while (aLength > 0) {
   5333    int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
   5334    width += aFontMetrics.GetWidth(aString, len, aDrawTarget);
   5335    aLength -= len;
   5336    aString += len;
   5337  }
   5338  return width;
   5339 }
   5340 
   5341 nscoord nsLayoutUtils::AppUnitWidthOfStringBidi(const char16_t* aString,
   5342                                                uint32_t aLength,
   5343                                                const nsIFrame* aFrame,
   5344                                                nsFontMetrics& aFontMetrics,
   5345                                                gfxContext& aContext) {
   5346  nsPresContext* presContext = aFrame->PresContext();
   5347  if (presContext->BidiEnabled()) {
   5348    mozilla::intl::BidiEmbeddingLevel level =
   5349        nsBidiPresUtils::BidiLevelFromStyle(aFrame->Style());
   5350    return nsBidiPresUtils::MeasureTextWidth(
   5351        aString, aLength, level, presContext, aContext, aFontMetrics);
   5352  }
   5353  aFontMetrics.SetTextRunRTL(false);
   5354  aFontMetrics.SetVertical(aFrame->GetWritingMode().IsVertical());
   5355  aFontMetrics.SetTextOrientation(aFrame->StyleVisibility()->mTextOrientation);
   5356  return nsLayoutUtils::AppUnitWidthOfString(aString, aLength, aFontMetrics,
   5357                                             aContext.GetDrawTarget());
   5358 }
   5359 
   5360 bool nsLayoutUtils::StringWidthIsGreaterThan(const nsString& aString,
   5361                                             nsFontMetrics& aFontMetrics,
   5362                                             DrawTarget* aDrawTarget,
   5363                                             nscoord aWidth) {
   5364  const char16_t* string = aString.get();
   5365  uint32_t length = aString.Length();
   5366  uint32_t maxChunkLength = GetMaxChunkLength(aFontMetrics);
   5367  nscoord width = 0;
   5368  while (length > 0) {
   5369    int32_t len = FindSafeLength(string, length, maxChunkLength);
   5370    width += aFontMetrics.GetWidth(string, len, aDrawTarget);
   5371    if (width > aWidth) {
   5372      return true;
   5373    }
   5374    length -= len;
   5375    string += len;
   5376  }
   5377  return false;
   5378 }
   5379 
   5380 nsBoundingMetrics nsLayoutUtils::AppUnitBoundsOfString(
   5381    const char16_t* aString, uint32_t aLength, nsFontMetrics& aFontMetrics,
   5382    DrawTarget* aDrawTarget) {
   5383  uint32_t maxChunkLength = GetMaxChunkLength(aFontMetrics);
   5384  int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
   5385  // Assign directly in the first iteration. This ensures that
   5386  // negative ascent/descent can be returned and the left bearing
   5387  // is properly initialized.
   5388  nsBoundingMetrics totalMetrics =
   5389      aFontMetrics.GetBoundingMetrics(aString, len, aDrawTarget);
   5390  aLength -= len;
   5391  aString += len;
   5392 
   5393  while (aLength > 0) {
   5394    len = FindSafeLength(aString, aLength, maxChunkLength);
   5395    nsBoundingMetrics metrics =
   5396        aFontMetrics.GetBoundingMetrics(aString, len, aDrawTarget);
   5397    totalMetrics += metrics;
   5398    aLength -= len;
   5399    aString += len;
   5400  }
   5401  return totalMetrics;
   5402 }
   5403 
   5404 void nsLayoutUtils::DrawString(const nsIFrame* aFrame,
   5405                               nsFontMetrics& aFontMetrics,
   5406                               gfxContext* aContext, const char16_t* aString,
   5407                               int32_t aLength, nsPoint aPoint,
   5408                               ComputedStyle* aComputedStyle,
   5409                               DrawStringFlags aFlags) {
   5410  nsresult rv = NS_ERROR_FAILURE;
   5411 
   5412  // If caller didn't pass a style, use the frame's.
   5413  if (!aComputedStyle) {
   5414    aComputedStyle = aFrame->Style();
   5415  }
   5416 
   5417  if (aFlags & DrawStringFlags::ForceHorizontal) {
   5418    aFontMetrics.SetVertical(false);
   5419  } else {
   5420    aFontMetrics.SetVertical(WritingMode(aComputedStyle).IsVertical());
   5421  }
   5422 
   5423  aFontMetrics.SetTextOrientation(
   5424      aComputedStyle->StyleVisibility()->mTextOrientation);
   5425 
   5426  nsPresContext* presContext = aFrame->PresContext();
   5427  if (presContext->BidiEnabled()) {
   5428    mozilla::intl::BidiEmbeddingLevel level =
   5429        nsBidiPresUtils::BidiLevelFromStyle(aComputedStyle);
   5430    rv = nsBidiPresUtils::RenderText(aString, aLength, level, presContext,
   5431                                     *aContext, aContext->GetDrawTarget(),
   5432                                     aFontMetrics, aPoint.x, aPoint.y);
   5433  }
   5434  if (NS_FAILED(rv)) {
   5435    aFontMetrics.SetTextRunRTL(false);
   5436    DrawUniDirString(aString, aLength, aPoint, aFontMetrics, *aContext);
   5437  }
   5438 }
   5439 
   5440 void nsLayoutUtils::DrawUniDirString(const char16_t* aString, uint32_t aLength,
   5441                                     const nsPoint& aPoint,
   5442                                     nsFontMetrics& aFontMetrics,
   5443                                     gfxContext& aContext) {
   5444  nscoord x = aPoint.x;
   5445  nscoord y = aPoint.y;
   5446 
   5447  uint32_t maxChunkLength = GetMaxChunkLength(aFontMetrics);
   5448  if (aLength <= maxChunkLength) {
   5449    aFontMetrics.DrawString(aString, aLength, x, y, &aContext,
   5450                            aContext.GetDrawTarget());
   5451    return;
   5452  }
   5453 
   5454  bool isRTL = aFontMetrics.GetTextRunRTL();
   5455 
   5456  // If we're drawing right to left, we must start at the end.
   5457  if (isRTL) {
   5458    x += nsLayoutUtils::AppUnitWidthOfString(aString, aLength, aFontMetrics,
   5459                                             aContext.GetDrawTarget());
   5460  }
   5461 
   5462  while (aLength > 0) {
   5463    int32_t len = FindSafeLength(aString, aLength, maxChunkLength);
   5464    nscoord width =
   5465        aFontMetrics.GetWidth(aString, len, aContext.GetDrawTarget());
   5466    if (isRTL) {
   5467      x -= width;
   5468    }
   5469    aFontMetrics.DrawString(aString, len, x, y, &aContext,
   5470                            aContext.GetDrawTarget());
   5471    if (!isRTL) {
   5472      x += width;
   5473    }
   5474    aLength -= len;
   5475    aString += len;
   5476  }
   5477 }
   5478 
   5479 /* static */
   5480 void nsLayoutUtils::PaintTextShadow(
   5481    const nsIFrame* aFrame, gfxContext* aContext, const nsRect& aTextRect,
   5482    const nsRect& aDirtyRect, const nscolor& aForegroundColor,
   5483    TextShadowCallback aCallback, void* aCallbackData) {
   5484  const nsStyleText* textStyle = aFrame->StyleText();
   5485  auto shadows = textStyle->mTextShadow.AsSpan();
   5486  if (shadows.IsEmpty()) {
   5487    return;
   5488  }
   5489 
   5490  // Text shadow happens with the last value being painted at the back,
   5491  // ie. it is painted first.
   5492  gfxContext* aDestCtx = aContext;
   5493  for (auto& shadow : Reversed(shadows)) {
   5494    nsPoint shadowOffset(shadow.horizontal.ToAppUnits(),
   5495                         shadow.vertical.ToAppUnits());
   5496    nscoord blurRadius = std::max(shadow.blur.ToAppUnits(), 0);
   5497 
   5498    nsRect shadowRect(aTextRect);
   5499    shadowRect.MoveBy(shadowOffset);
   5500 
   5501    nsPresContext* presCtx = aFrame->PresContext();
   5502    nsContextBoxBlur contextBoxBlur;
   5503 
   5504    nscolor shadowColor = shadow.color.CalcColor(aForegroundColor);
   5505 
   5506    // Webrender just needs the shadow details
   5507    if (auto* textDrawer = aContext->GetTextDrawer()) {
   5508      wr::Shadow wrShadow;
   5509 
   5510      wrShadow.offset = {
   5511          presCtx->AppUnitsToFloatDevPixels(shadow.horizontal.ToAppUnits()),
   5512          presCtx->AppUnitsToFloatDevPixels(shadow.vertical.ToAppUnits())};
   5513 
   5514      wrShadow.blur_radius = presCtx->AppUnitsToFloatDevPixels(blurRadius);
   5515      wrShadow.color = wr::ToColorF(ToDeviceColor(shadowColor));
   5516 
   5517      // Gecko already inflates the bounding rect of text shadows,
   5518      // so tell WR not to inflate again.
   5519      bool inflate = false;
   5520      textDrawer->AppendShadow(wrShadow, inflate);
   5521      continue;
   5522    }
   5523 
   5524    gfxContext* shadowContext = contextBoxBlur.Init(
   5525        shadowRect, 0, blurRadius, presCtx->AppUnitsPerDevPixel(), aDestCtx,
   5526        aDirtyRect, nullptr);
   5527    if (!shadowContext) {
   5528      continue;
   5529    }
   5530 
   5531    aDestCtx->Save();
   5532    aDestCtx->NewPath();
   5533    aDestCtx->SetColor(sRGBColor::FromABGR(shadowColor));
   5534 
   5535    // The callback will draw whatever we want to blur as a shadow.
   5536    aCallback(shadowContext, shadowOffset, shadowColor, aCallbackData);
   5537 
   5538    contextBoxBlur.DoPaint();
   5539    aDestCtx->Restore();
   5540  }
   5541 }
   5542 
   5543 /* static */
   5544 nscoord nsLayoutUtils::GetCenteredFontBaseline(nsFontMetrics* aFontMetrics,
   5545                                               nscoord aLineHeight,
   5546                                               bool aIsInverted) {
   5547  nscoord fontAscent =
   5548      aIsInverted ? aFontMetrics->MaxDescent() : aFontMetrics->MaxAscent();
   5549  nscoord fontHeight = aFontMetrics->MaxHeight();
   5550 
   5551  nscoord leading = aLineHeight - fontHeight;
   5552  return fontAscent + leading / 2;
   5553 }
   5554 
   5555 /* static */
   5556 bool nsLayoutUtils::GetFirstLineBaseline(WritingMode aWritingMode,
   5557                                         const nsIFrame* aFrame,
   5558                                         nscoord* aResult) {
   5559  LinePosition position;
   5560  if (!GetFirstLinePosition(aWritingMode, aFrame, &position)) {
   5561    return false;
   5562  }
   5563  *aResult = position.mBaseline;
   5564  return true;
   5565 }
   5566 
   5567 /* static */
   5568 bool nsLayoutUtils::GetFirstLinePosition(WritingMode aWM,
   5569                                         const nsIFrame* aFrame,
   5570                                         LinePosition* aResult) {
   5571  if (aFrame->StyleDisplay()->IsContainLayout()) {
   5572    return false;
   5573  }
   5574  const nsBlockFrame* block = do_QueryFrame(aFrame);
   5575  if (!block) {
   5576    // For the first-line baseline we also have to check for a table, and if
   5577    // so, use the baseline of its first row.
   5578    LayoutFrameType fType = aFrame->Type();
   5579    if (fType == LayoutFrameType::TableWrapper ||
   5580        fType == LayoutFrameType::FlexContainer ||
   5581        fType == LayoutFrameType::GridContainer) {
   5582      if ((fType == LayoutFrameType::GridContainer &&
   5583           aFrame->HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) ||
   5584          (fType == LayoutFrameType::FlexContainer &&
   5585           aFrame->HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) ||
   5586          (fType == LayoutFrameType::TableWrapper &&
   5587           static_cast<const nsTableWrapperFrame*>(aFrame)->GetRowCount() ==
   5588               0)) {
   5589        // empty grid/flex/table container
   5590        aResult->mBStart = 0;
   5591        aResult->mBaseline = Baseline::SynthesizeBOffsetFromBorderBox(
   5592            aFrame, aWM, BaselineSharingGroup::First);
   5593        aResult->mBEnd = aFrame->BSize(aWM);
   5594        return true;
   5595      }
   5596      if (fType == LayoutFrameType::TableWrapper &&
   5597          aFrame->GetWritingMode().IsOrthogonalTo(aWM)) {
   5598        // For tables, the upcoming GetLogicalBaseline call would determine the
   5599        // table's baseline from its first row that has a baseline. However:
   5600        // this doesn't make sense for an orthogonal writing mode, so in that
   5601        // case we report no baseline instead. The table wrapper and its rows
   5602        // should flow the same way, so we can bail out early, but this logic
   5603        // wouldn't be correct to transplant into other places in the codebase
   5604        // (Depending on how bug 1786633 is resolved).
   5605        return false;
   5606      }
   5607      aResult->mBStart = 0;
   5608      aResult->mBaseline = aFrame->GetLogicalBaseline(aWM);
   5609      // This is what we want for the list bullet caller; not sure if
   5610      // other future callers will want the same.
   5611      aResult->mBEnd = aFrame->BSize(aWM);
   5612      return true;
   5613    }
   5614 
   5615    // For first-line baselines, we have to consider scroll frames.
   5616    if (const ScrollContainerFrame* sFrame = do_QueryFrame(aFrame)) {
   5617      LinePosition kidPosition;
   5618      if (GetFirstLinePosition(aWM, sFrame->GetScrolledFrame(), &kidPosition)) {
   5619        // Consider only the border (Padding is ignored, since
   5620        // `-moz-scrolled-content` inherits and handles the padding) that
   5621        // contributes to the kid's position, not the scrolling, so we get the
   5622        // initial position.
   5623        *aResult = kidPosition + aFrame->GetLogicalUsedBorder(aWM).BStart(aWM);
   5624        // Don't want to move the line's block positioning, but the baseline
   5625        // needs to be clamped (See bug 1791069).
   5626        aResult->mBaseline = CSSMinMax(aResult->mBaseline, 0,
   5627                                       aFrame->GetLogicalSize(aWM).BSize(aWM));
   5628        return true;
   5629      }
   5630      return false;
   5631    }
   5632 
   5633    if (fType == LayoutFrameType::FieldSet) {
   5634      LinePosition kidPosition;
   5635      // Get the first baseline from the fieldset content, not from the legend.
   5636      nsIFrame* kid = static_cast<const nsFieldSetFrame*>(aFrame)->GetInner();
   5637      if (kid && GetFirstLinePosition(aWM, kid, &kidPosition)) {
   5638        *aResult = kidPosition +
   5639                   kid->GetLogicalNormalPosition(aWM, aFrame->GetSize()).B(aWM);
   5640        return true;
   5641      }
   5642      return false;
   5643    }
   5644 
   5645    if (fType == LayoutFrameType::ColumnSet) {
   5646      // Note(dshin): This is basically the same as
   5647      // `nsColumnSetFrame::GetNaturalBaselineBOffset`, but with line start and
   5648      // end, all stored in `LinePosition`. Field value apart from baseline is
   5649      // used in one other place
   5650      // (`nsBlockFrame`) - if that goes away, this becomes a duplication that
   5651      // should be removed.
   5652      LinePosition kidPosition;
   5653      for (const auto* kid : aFrame->PrincipalChildList()) {
   5654        LinePosition position;
   5655        if (!GetFirstLinePosition(aWM, kid, &position)) {
   5656          continue;
   5657        }
   5658        if (position.mBaseline < kidPosition.mBaseline) {
   5659          kidPosition = position;
   5660        }
   5661      }
   5662      if (kidPosition.mBaseline != nscoord_MAX) {
   5663        *aResult = kidPosition;
   5664        return true;
   5665      }
   5666    }
   5667 
   5668    // No baseline.
   5669    return false;
   5670  }
   5671 
   5672  for (const auto& line : block->Lines()) {
   5673    if (line.IsBlock()) {
   5674      const nsIFrame* kid = line.mFirstChild;
   5675      LinePosition kidPosition;
   5676      if (GetFirstLinePosition(aWM, kid, &kidPosition)) {
   5677        // XXX Not sure if this is the correct value to use for container
   5678        //    width here. It will only be used in vertical-rl layout,
   5679        //    which we don't have full support and testing for yet.
   5680        const auto& containerSize = line.mContainerSize;
   5681        *aResult = kidPosition +
   5682                   kid->GetLogicalNormalPosition(aWM, containerSize).B(aWM);
   5683        return true;
   5684      }
   5685    } else {
   5686      // XXX Is this the right test?  We have some bogus empty lines
   5687      // floating around, but IsEmpty is perhaps too weak.
   5688      if (0 != line.BSize() || !line.IsEmpty()) {
   5689        nscoord bStart = line.BStart();
   5690        aResult->mBStart = bStart;
   5691        aResult->mBaseline = bStart + line.GetLogicalAscent();
   5692        aResult->mBEnd = bStart + line.BSize();
   5693        return true;
   5694      }
   5695    }
   5696  }
   5697  return false;
   5698 }
   5699 
   5700 /* static */
   5701 bool nsLayoutUtils::GetLastLineBaseline(WritingMode aWM, const nsIFrame* aFrame,
   5702                                        nscoord* aResult) {
   5703  if (aFrame->StyleDisplay()->IsContainLayout()) {
   5704    return false;
   5705  }
   5706 
   5707  const nsBlockFrame* block = do_QueryFrame(aFrame);
   5708  if (!block) {
   5709    if (const ScrollContainerFrame* sFrame = do_QueryFrame(aFrame)) {
   5710      // Use the baseline position only if the last line's baseline is within
   5711      // the scrolling frame's box in the initial position.
   5712      const auto* scrolledFrame = sFrame->GetScrolledFrame();
   5713      if (!GetLastLineBaseline(aWM, scrolledFrame, aResult)) {
   5714        return false;
   5715      }
   5716      // Go from scrolled frame to scrollable frame position.
   5717      *aResult += aFrame->GetLogicalUsedBorder(aWM).BStart(aWM);
   5718      const auto maxBaseline = aFrame->GetLogicalSize(aWM).BSize(aWM);
   5719      // Clamp the last baseline to border (See bug 1791069).
   5720      *aResult = std::clamp(*aResult, 0, maxBaseline);
   5721      return true;
   5722    }
   5723 
   5724    // No need to duplicate the baseline logic (Unlike `GetFirstLinePosition`,
   5725    // we don't need to return any other value apart from baseline), just defer
   5726    // to `GetNaturalBaselineBOffset`. Technically, we could do this at
   5727    // `ColumnSetWrapperFrame` level, but this keeps it symmetric to
   5728    // `GetFirstLinePosition`.
   5729    if (aFrame->IsColumnSetFrame()) {
   5730      const auto baseline = aFrame->GetNaturalBaselineBOffset(
   5731          aWM, BaselineSharingGroup::Last, BaselineExportContext::Other);
   5732      if (!baseline) {
   5733        return false;
   5734      }
   5735      *aResult = aFrame->BSize(aWM) - *baseline;
   5736      return true;
   5737    }
   5738    // No baseline.
   5739    return false;
   5740  }
   5741 
   5742  for (nsBlockFrame::ConstReverseLineIterator line = block->LinesRBegin(),
   5743                                              line_end = block->LinesREnd();
   5744       line != line_end; ++line) {
   5745    if (line->IsBlock()) {
   5746      nsIFrame* kid = line->mFirstChild;
   5747      nscoord kidBaseline;
   5748      const nsSize& containerSize = line->mContainerSize;
   5749      if (GetLastLineBaseline(aWM, kid, &kidBaseline)) {
   5750        // Ignore relative positioning for baseline calculations
   5751        *aResult = kidBaseline +
   5752                   kid->GetLogicalNormalPosition(aWM, containerSize).B(aWM);
   5753        return true;
   5754      }
   5755      if (kid->IsScrollContainerFrame()) {
   5756        // Defer to nsIFrame::GetLogicalBaseline (which synthesizes a baseline
   5757        // from the margin-box).
   5758        kidBaseline = kid->GetLogicalBaseline(aWM);
   5759        *aResult = kidBaseline +
   5760                   kid->GetLogicalNormalPosition(aWM, containerSize).B(aWM);
   5761        return true;
   5762      }
   5763    } else {
   5764      // XXX Is this the right test?  We have some bogus empty lines
   5765      // floating around, but IsEmpty is perhaps too weak.
   5766      if (line->BSize() != 0 || !line->IsEmpty()) {
   5767        *aResult = line->BStart() + line->GetLogicalAscent();
   5768        return true;
   5769      }
   5770    }
   5771  }
   5772  return false;
   5773 }
   5774 
   5775 static nscoord CalculateBlockContentBEnd(WritingMode aWM,
   5776                                         nsBlockFrame* aFrame) {
   5777  MOZ_ASSERT(aFrame, "null ptr");
   5778 
   5779  nscoord contentBEnd = 0;
   5780 
   5781  for (const auto& line : aFrame->Lines()) {
   5782    if (line.IsBlock()) {
   5783      nsIFrame* child = line.mFirstChild;
   5784      const auto& containerSize = line.mContainerSize;
   5785      nscoord offset =
   5786          child->GetLogicalNormalPosition(aWM, containerSize).B(aWM);
   5787      contentBEnd =
   5788          std::max(contentBEnd,
   5789                   nsLayoutUtils::CalculateContentBEnd(aWM, child) + offset);
   5790    } else {
   5791      contentBEnd = std::max(contentBEnd, line.BEnd());
   5792    }
   5793  }
   5794  return contentBEnd;
   5795 }
   5796 
   5797 /* static */
   5798 nscoord nsLayoutUtils::CalculateContentBEnd(WritingMode aWM, nsIFrame* aFrame) {
   5799  MOZ_ASSERT(aFrame, "null ptr");
   5800 
   5801  nscoord contentBEnd = aFrame->BSize(aWM);
   5802 
   5803  // We want scrollable overflow rather than visual because this
   5804  // calculation is intended to affect layout.
   5805  LogicalSize overflowSize(aWM, aFrame->ScrollableOverflowRect().Size());
   5806  if (overflowSize.BSize(aWM) > contentBEnd) {
   5807    FrameChildListIDs skip = {FrameChildListID::PushedAbsolute,
   5808                              FrameChildListID::Overflow,
   5809                              FrameChildListID::ExcessOverflowContainers,
   5810                              FrameChildListID::OverflowOutOfFlow};
   5811    nsBlockFrame* blockFrame = do_QueryFrame(aFrame);
   5812    if (blockFrame) {
   5813      contentBEnd =
   5814          std::max(contentBEnd, CalculateBlockContentBEnd(aWM, blockFrame));
   5815      skip += FrameChildListID::Principal;
   5816    }
   5817    for (const auto& [list, listID] : aFrame->ChildLists()) {
   5818      if (!skip.contains(listID)) {
   5819        for (nsIFrame* child : list) {
   5820          nscoord offset =
   5821              child->GetLogicalNormalPosition(aWM, aFrame->GetSize()).B(aWM);
   5822          contentBEnd =
   5823              std::max(contentBEnd, CalculateContentBEnd(aWM, child) + offset);
   5824        }
   5825      }
   5826    }
   5827  }
   5828  return contentBEnd;
   5829 }
   5830 
   5831 /* static */
   5832 nsIFrame* nsLayoutUtils::GetClosestLayer(nsIFrame* aFrame) {
   5833  nsIFrame* layer;
   5834  for (layer = aFrame; layer; layer = layer->GetParent()) {
   5835    if (layer->IsAbsPosContainingBlock() ||
   5836        (layer->GetParent() && layer->GetParent()->IsScrollContainerFrame())) {
   5837      break;
   5838    }
   5839  }
   5840  if (layer) {
   5841    return layer;
   5842  }
   5843  return aFrame->PresShell()->GetRootFrame();
   5844 }
   5845 
   5846 SamplingFilter nsLayoutUtils::GetSamplingFilterForFrame(nsIFrame* aForFrame) {
   5847  switch (aForFrame->UsedImageRendering()) {
   5848    case StyleImageRendering::Smooth:
   5849    case StyleImageRendering::Optimizequality:
   5850      return SamplingFilter::LINEAR;
   5851    case StyleImageRendering::CrispEdges:
   5852    case StyleImageRendering::Optimizespeed:
   5853    case StyleImageRendering::Pixelated:
   5854      return SamplingFilter::POINT;
   5855    case StyleImageRendering::Auto:
   5856      return SamplingFilter::GOOD;
   5857  }
   5858  MOZ_ASSERT_UNREACHABLE("Unknown image-rendering value");
   5859  return SamplingFilter::GOOD;
   5860 }
   5861 
   5862 /**
   5863 * Given an image being drawn into an appunit coordinate system, and
   5864 * a point in that coordinate system, map the point back into image
   5865 * pixel space.
   5866 * @param aSize the size of the image, in pixels
   5867 * @param aDest the rectangle that the image is being mapped into
   5868 * @param aPt a point in the same coordinate system as the rectangle
   5869 */
   5870 static gfxPoint MapToFloatImagePixels(const gfxSize& aSize,
   5871                                      const gfxRect& aDest,
   5872                                      const gfxPoint& aPt) {
   5873  return gfxPoint(((aPt.x - aDest.X()) * aSize.width) / aDest.Width(),
   5874                  ((aPt.y - aDest.Y()) * aSize.height) / aDest.Height());
   5875 }
   5876 
   5877 /**
   5878 * Given an image being drawn into an pixel-based coordinate system, and
   5879 * a point in image space, map the point into the pixel-based coordinate
   5880 * system.
   5881 * @param aSize the size of the image, in pixels
   5882 * @param aDest the rectangle that the image is being mapped into
   5883 * @param aPt a point in image space
   5884 */
   5885 static gfxPoint MapToFloatUserPixels(const gfxSize& aSize, const gfxRect& aDest,
   5886                                     const gfxPoint& aPt) {
   5887  return gfxPoint(aPt.x * aDest.Width() / aSize.width + aDest.X(),
   5888                  aPt.y * aDest.Height() / aSize.height + aDest.Y());
   5889 }
   5890 
   5891 /* static */
   5892 gfxRect nsLayoutUtils::RectToGfxRect(const nsRect& aRect,
   5893                                     int32_t aAppUnitsPerDevPixel) {
   5894  return gfxRect(gfxFloat(aRect.x) / aAppUnitsPerDevPixel,
   5895                 gfxFloat(aRect.y) / aAppUnitsPerDevPixel,
   5896                 gfxFloat(aRect.width) / aAppUnitsPerDevPixel,
   5897                 gfxFloat(aRect.height) / aAppUnitsPerDevPixel);
   5898 }
   5899 
   5900 struct SnappedImageDrawingParameters {
   5901  // A transform from image space to device space.
   5902  gfxMatrix imageSpaceToDeviceSpace;
   5903  // The size at which the image should be drawn (which may not be its
   5904  // intrinsic size due to, for example, HQ scaling).
   5905  nsIntSize size;
   5906  // The region in tiled image space which will be drawn, with an associated
   5907  // region to which sampling should be restricted.
   5908  ImageRegion region;
   5909  // The default viewport size for SVG images, which we use unless a different
   5910  // one has been explicitly specified. This is the same as |size| except that
   5911  // it does not take into account any transformation on the gfxContext we're
   5912  // drawing to - for example, CSS transforms are not taken into account.
   5913  CSSIntSize svgViewportSize;
   5914  // Whether there's anything to draw at all.
   5915  bool shouldDraw;
   5916 
   5917  SnappedImageDrawingParameters()
   5918      : region(ImageRegion::Empty()), shouldDraw(false) {}
   5919 
   5920  SnappedImageDrawingParameters(const gfxMatrix& aImageSpaceToDeviceSpace,
   5921                                const nsIntSize& aSize,
   5922                                const ImageRegion& aRegion,
   5923                                const CSSIntSize& aSVGViewportSize)
   5924      : imageSpaceToDeviceSpace(aImageSpaceToDeviceSpace),
   5925        size(aSize),
   5926        region(aRegion),
   5927        svgViewportSize(aSVGViewportSize),
   5928        shouldDraw(true) {}
   5929 };
   5930 
   5931 /**
   5932 * Given two axis-aligned rectangles, returns the transformation that maps the
   5933 * first onto the second.
   5934 *
   5935 * @param aFrom The rect to be transformed.
   5936 * @param aTo The rect that aFrom should be mapped onto by the transformation.
   5937 */
   5938 static gfxMatrix TransformBetweenRects(const gfxRect& aFrom,
   5939                                       const gfxRect& aTo) {
   5940  MatrixScalesDouble scale(aTo.width / aFrom.width, aTo.height / aFrom.height);
   5941  gfxPoint translation(aTo.x - aFrom.x * scale.xScale,
   5942                       aTo.y - aFrom.y * scale.yScale);
   5943  return gfxMatrix(scale.xScale, 0, 0, scale.yScale, translation.x,
   5944                   translation.y);
   5945 }
   5946 
   5947 static nsRect TileNearRect(const nsRect& aAnyTile, const nsRect& aTargetRect) {
   5948  nsPoint distance = aTargetRect.TopLeft() - aAnyTile.TopLeft();
   5949  return aAnyTile + nsPoint(distance.x / aAnyTile.width * aAnyTile.width,
   5950                            distance.y / aAnyTile.height * aAnyTile.height);
   5951 }
   5952 
   5953 static gfxFloat StableRound(gfxFloat aValue) {
   5954  // Values slightly less than 0.5 should round up like 0.5 would; we're
   5955  // assuming they were meant to be 0.5.
   5956  return floor(aValue + 0.5001);
   5957 }
   5958 
   5959 static gfxPoint StableRound(const gfxPoint& aPoint) {
   5960  return gfxPoint(StableRound(aPoint.x), StableRound(aPoint.y));
   5961 }
   5962 
   5963 /**
   5964 * Given a set of input parameters, compute certain output parameters
   5965 * for drawing an image with the image snapping algorithm.
   5966 * See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering
   5967 *
   5968 *  @see nsLayoutUtils::DrawImage() for the descriptions of input parameters
   5969 */
   5970 static SnappedImageDrawingParameters ComputeSnappedImageDrawingParameters(
   5971    gfxContext* aCtx, int32_t aAppUnitsPerDevPixel, const nsRect aDest,
   5972    const nsRect aFill, const nsPoint aAnchor, const nsRect aDirty,
   5973    imgIContainer* aImage, const SamplingFilter aSamplingFilter,
   5974    uint32_t aImageFlags, ExtendMode aExtendMode) {
   5975  if (aDest.IsEmpty() || aFill.IsEmpty()) {
   5976    return SnappedImageDrawingParameters();
   5977  }
   5978 
   5979  // Avoid unnecessarily large offsets.
   5980  bool doTile = !aDest.Contains(aFill);
   5981  nsRect appUnitDest =
   5982      doTile ? TileNearRect(aDest, aFill.Intersect(aDirty)) : aDest;
   5983  nsPoint anchor = aAnchor + (appUnitDest.TopLeft() - aDest.TopLeft());
   5984 
   5985  gfxRect devPixelDest =
   5986      nsLayoutUtils::RectToGfxRect(appUnitDest, aAppUnitsPerDevPixel);
   5987  gfxRect devPixelFill =
   5988      nsLayoutUtils::RectToGfxRect(aFill, aAppUnitsPerDevPixel);
   5989  gfxRect devPixelDirty =
   5990      nsLayoutUtils::RectToGfxRect(aDirty, aAppUnitsPerDevPixel);
   5991 
   5992  gfxMatrix currentMatrix = aCtx->CurrentMatrixDouble();
   5993  gfxRect fill = devPixelFill;
   5994  gfxRect dest = devPixelDest;
   5995  bool didSnap;
   5996  // Snap even if we have a scale in the context. But don't snap if
   5997  // we have something that's not translation+scale, or if the scale flips in
   5998  // the X or Y direction, because snapped image drawing can't handle that yet.
   5999  if (!currentMatrix.HasNonAxisAlignedTransform() && currentMatrix._11 > 0.0 &&
   6000      currentMatrix._22 > 0.0 &&
   6001      aCtx->UserToDevicePixelSnapped(fill,
   6002                                     gfxContext::SnapOption::IgnoreScale) &&
   6003      aCtx->UserToDevicePixelSnapped(dest,
   6004                                     gfxContext::SnapOption::IgnoreScale)) {
   6005    // We snapped. On this code path, |fill| and |dest| take into account
   6006    // currentMatrix's transform.
   6007    didSnap = true;
   6008  } else {
   6009    // We didn't snap. On this code path, |fill| and |dest| do not take into
   6010    // account currentMatrix's transform.
   6011    didSnap = false;
   6012    fill = devPixelFill;
   6013    dest = devPixelDest;
   6014  }
   6015 
   6016  // If we snapped above, |dest| already takes into account |currentMatrix|'s
   6017  // scale and has integer coordinates. If not, we need these properties to
   6018  // compute the optimal drawn image size, so compute |snappedDestSize| here.
   6019  gfxSize snappedDestSize = dest.Size();
   6020  auto scaleFactors = currentMatrix.ScaleFactors();
   6021  if (!didSnap) {
   6022    snappedDestSize.Scale(scaleFactors.xScale, scaleFactors.yScale);
   6023    snappedDestSize.width = NS_round(snappedDestSize.width);
   6024    snappedDestSize.height = NS_round(snappedDestSize.height);
   6025  }
   6026 
   6027  // We need to be sure that this is at least one pixel in width and height,
   6028  // or we'll end up drawing nothing even if we have a nonempty fill.
   6029  snappedDestSize.width = std::max(snappedDestSize.width, 1.0);
   6030  snappedDestSize.height = std::max(snappedDestSize.height, 1.0);
   6031 
   6032  // Bail if we're not going to end up drawing anything.
   6033  if (fill.IsEmpty()) {
   6034    return SnappedImageDrawingParameters();
   6035  }
   6036 
   6037  nsIntSize intImageSize = aImage->OptimalImageSizeForDest(
   6038      snappedDestSize, imgIContainer::FRAME_CURRENT, aSamplingFilter,
   6039      aImageFlags);
   6040 
   6041  nsIntSize svgViewportSize;
   6042  if (scaleFactors.xScale == 1.0 && scaleFactors.yScale == 1.0) {
   6043    // intImageSize is scaled by currentMatrix. But since there are no scale
   6044    // factors in currentMatrix, it is safe to assign intImageSize to
   6045    // svgViewportSize directly.
   6046    svgViewportSize = intImageSize;
   6047  } else {
   6048    // We should not take into account any transformation of currentMatrix
   6049    // when computing svg viewport size. Since currentMatrix contains scale
   6050    // factors, we need to recompute SVG viewport by unscaled devPixelDest.
   6051    svgViewportSize = aImage->OptimalImageSizeForDest(
   6052        devPixelDest.Size(), imgIContainer::FRAME_CURRENT, aSamplingFilter,
   6053        aImageFlags);
   6054  }
   6055 
   6056  gfxSize imageSize(intImageSize.width, intImageSize.height);
   6057 
   6058  // Compute the set of pixels that would be sampled by an ideal rendering
   6059  gfxPoint subimageTopLeft =
   6060      MapToFloatImagePixels(imageSize, devPixelDest, devPixelFill.TopLeft());
   6061  gfxPoint subimageBottomRight = MapToFloatImagePixels(
   6062      imageSize, devPixelDest, devPixelFill.BottomRight());
   6063  gfxRect subimage;
   6064  subimage.MoveTo(NSToIntFloor(subimageTopLeft.x),
   6065                  NSToIntFloor(subimageTopLeft.y));
   6066  subimage.SizeTo(NSToIntCeil(subimageBottomRight.x) - subimage.x,
   6067                  NSToIntCeil(subimageBottomRight.y) - subimage.y);
   6068 
   6069  if (subimage.IsEmpty()) {
   6070    // Bail if the subimage is empty (we're not going to be drawing anything).
   6071    return SnappedImageDrawingParameters();
   6072  }
   6073 
   6074  gfxMatrix transform;
   6075  gfxMatrix invTransform;
   6076 
   6077  bool anchorAtUpperLeft =
   6078      anchor.x == appUnitDest.x && anchor.y == appUnitDest.y;
   6079  bool exactlyOneImageCopy = aFill.IsEqualEdges(appUnitDest);
   6080  if (anchorAtUpperLeft && exactlyOneImageCopy) {
   6081    // The simple case: we can ignore the anchor point and compute the
   6082    // transformation from the sampled region (the subimage) to the fill rect.
   6083    // This approach is preferable when it works since it tends to produce
   6084    // less numerical error.
   6085    transform = TransformBetweenRects(subimage, fill);
   6086    invTransform = TransformBetweenRects(fill, subimage);
   6087  } else {
   6088    // The more complicated case: we compute the transformation from the
   6089    // image rect positioned at the image space anchor point to the dest rect
   6090    // positioned at the device space anchor point.
   6091 
   6092    // Compute the anchor point in both device space and image space.  This
   6093    // code assumes that pixel-based devices have one pixel per device unit!
   6094    gfxPoint anchorPoint(gfxFloat(anchor.x) / aAppUnitsPerDevPixel,
   6095                         gfxFloat(anchor.y) / aAppUnitsPerDevPixel);
   6096    gfxPoint imageSpaceAnchorPoint =
   6097        MapToFloatImagePixels(imageSize, devPixelDest, anchorPoint);
   6098 
   6099    if (didSnap) {
   6100      imageSpaceAnchorPoint = StableRound(imageSpaceAnchorPoint);
   6101      anchorPoint = imageSpaceAnchorPoint;
   6102      anchorPoint = MapToFloatUserPixels(imageSize, devPixelDest, anchorPoint);
   6103      anchorPoint = currentMatrix.TransformPoint(anchorPoint);
   6104      anchorPoint = StableRound(anchorPoint);
   6105    }
   6106 
   6107    // Compute an unsnapped version of the dest rect's size. We continue to
   6108    // follow the pattern that we take |currentMatrix| into account only if
   6109    // |didSnap| is true.
   6110    gfxSize unsnappedDestSize =
   6111        didSnap ? devPixelDest.Size() * currentMatrix.ScaleFactors()
   6112                : devPixelDest.Size();
   6113 
   6114    gfxRect anchoredDestRect(anchorPoint, unsnappedDestSize);
   6115    gfxRect anchoredImageRect(imageSpaceAnchorPoint, imageSize);
   6116 
   6117    // Calculate anchoredDestRect with snapped fill rect when the devPixelFill
   6118    // rect corresponds to just a single tile in that direction
   6119    if (fill.Width() != devPixelFill.Width() &&
   6120        devPixelDest.x == devPixelFill.x &&
   6121        devPixelDest.XMost() == devPixelFill.XMost()) {
   6122      anchoredDestRect.width = fill.width;
   6123    }
   6124    if (fill.Height() != devPixelFill.Height() &&
   6125        devPixelDest.y == devPixelFill.y &&
   6126        devPixelDest.YMost() == devPixelFill.YMost()) {
   6127      anchoredDestRect.height = fill.height;
   6128    }
   6129 
   6130    transform = TransformBetweenRects(anchoredImageRect, anchoredDestRect);
   6131    invTransform = TransformBetweenRects(anchoredDestRect, anchoredImageRect);
   6132  }
   6133 
   6134  // If the transform is not a straight translation by integers, then
   6135  // filtering will occur, and restricting the fill rect to the dirty rect
   6136  // would change the values computed for edge pixels, which we can't allow.
   6137  // Also, if 'didSnap' is false then rounding out 'devPixelDirty' might not
   6138  // produce pixel-aligned coordinates, which would also break the values
   6139  // computed for edge pixels.
   6140  if (didSnap && !invTransform.HasNonIntegerTranslation()) {
   6141    // This form of Transform is safe to call since non-axis-aligned
   6142    // transforms wouldn't be snapped.
   6143    devPixelDirty = currentMatrix.TransformRect(devPixelDirty);
   6144    devPixelDirty.RoundOut();
   6145    fill = fill.Intersect(devPixelDirty);
   6146  }
   6147  if (fill.IsEmpty()) {
   6148    return SnappedImageDrawingParameters();
   6149  }
   6150 
   6151  gfxRect imageSpaceFill(didSnap ? invTransform.TransformRect(fill)
   6152                                 : invTransform.TransformBounds(fill));
   6153 
   6154  // If we didn't snap, we need to post-multiply the matrix on the context to
   6155  // get the final matrix we'll draw with, because we didn't take it into
   6156  // account when computing the matrices above.
   6157  if (!didSnap) {
   6158    transform = transform * currentMatrix;
   6159  }
   6160 
   6161  ExtendMode extendMode = (aImageFlags & imgIContainer::FLAG_CLAMP)
   6162                              ? ExtendMode::CLAMP
   6163                              : aExtendMode;
   6164  // We were passed in the default extend mode but need to tile.
   6165  if (extendMode == ExtendMode::CLAMP && doTile) {
   6166    MOZ_ASSERT(!(aImageFlags & imgIContainer::FLAG_CLAMP));
   6167    extendMode = ExtendMode::REPEAT;
   6168  }
   6169 
   6170  ImageRegion region = ImageRegion::CreateWithSamplingRestriction(
   6171      imageSpaceFill, subimage, extendMode);
   6172 
   6173  return SnappedImageDrawingParameters(
   6174      transform, intImageSize, region,
   6175      CSSIntSize(svgViewportSize.width, svgViewportSize.height));
   6176 }
   6177 
   6178 static ImgDrawResult DrawImageInternal(
   6179    gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage,
   6180    const SamplingFilter aSamplingFilter, const nsRect& aDest,
   6181    const nsRect& aFill, const nsPoint& aAnchor, const nsRect& aDirty,
   6182    const SVGImageContext& aSVGContext, uint32_t aImageFlags,
   6183    ExtendMode aExtendMode = ExtendMode::CLAMP, float aOpacity = 1.0) {
   6184  ImgDrawResult result = ImgDrawResult::SUCCESS;
   6185 
   6186  aImageFlags |= imgIContainer::FLAG_ASYNC_NOTIFY;
   6187 
   6188  if (aPresContext->Type() == nsPresContext::eContext_Print) {
   6189    // We want vector images to be passed on as vector commands, not a raster
   6190    // image.
   6191    aImageFlags |= imgIContainer::FLAG_BYPASS_SURFACE_CACHE;
   6192  }
   6193  if (aDest.Contains(aFill)) {
   6194    aImageFlags |= imgIContainer::FLAG_CLAMP;
   6195  }
   6196  int32_t appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel();
   6197 
   6198  SnappedImageDrawingParameters params = ComputeSnappedImageDrawingParameters(
   6199      &aContext, appUnitsPerDevPixel, aDest, aFill, aAnchor, aDirty, aImage,
   6200      aSamplingFilter, aImageFlags, aExtendMode);
   6201 
   6202  if (!params.shouldDraw) {
   6203    return result;
   6204  }
   6205 
   6206  {
   6207    gfxContextMatrixAutoSaveRestore contextMatrixRestorer(&aContext);
   6208 
   6209    aContext.SetMatrixDouble(params.imageSpaceToDeviceSpace);
   6210 
   6211    SVGImageContext newContext = aSVGContext;
   6212    if (!aSVGContext.GetViewportSize()) {
   6213      newContext.SetViewportSize(Some(params.svgViewportSize));
   6214    }
   6215 
   6216    result = aImage->Draw(&aContext, params.size, params.region,
   6217                          imgIContainer::FRAME_CURRENT, aSamplingFilter,
   6218                          newContext, aImageFlags, aOpacity);
   6219  }
   6220 
   6221  return result;
   6222 }
   6223 
   6224 /* static */
   6225 ImgDrawResult nsLayoutUtils::DrawSingleUnscaledImage(
   6226    gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage,
   6227    const SamplingFilter aSamplingFilter, const nsPoint& aDest,
   6228    const nsRect* aDirty, const SVGImageContext& aSVGContext,
   6229    uint32_t aImageFlags, const nsRect* aSourceArea) {
   6230  CSSIntSize imageSize;
   6231  aImage->GetWidth(&imageSize.width);
   6232  aImage->GetHeight(&imageSize.height);
   6233  aImage->GetResolution().ApplyTo(imageSize.width, imageSize.height);
   6234 
   6235  if (imageSize.width < 1 || imageSize.height < 1) {
   6236    NS_WARNING("Image width or height is non-positive");
   6237    return ImgDrawResult::TEMPORARY_ERROR;
   6238  }
   6239 
   6240  nsSize size(CSSPixel::ToAppUnits(imageSize));
   6241  nsRect source;
   6242  if (aSourceArea) {
   6243    source = *aSourceArea;
   6244  } else {
   6245    source.SizeTo(size);
   6246  }
   6247 
   6248  nsRect dest(aDest - source.TopLeft(), size);
   6249  nsRect fill(aDest, source.Size());
   6250  // Ensure that only a single image tile is drawn. If aSourceArea extends
   6251  // outside the image bounds, we want to honor the aSourceArea-to-aDest
   6252  // translation but we don't want to actually tile the image.
   6253  fill.IntersectRect(fill, dest);
   6254  return DrawImageInternal(aContext, aPresContext, aImage, aSamplingFilter,
   6255                           dest, fill, aDest, aDirty ? *aDirty : dest,
   6256                           aSVGContext, aImageFlags);
   6257 }
   6258 
   6259 /* static */
   6260 ImgDrawResult nsLayoutUtils::DrawSingleImage(
   6261    gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage,
   6262    SamplingFilter aSamplingFilter, const nsRect& aDest, const nsRect& aDirty,
   6263    const SVGImageContext& aSVGContext, uint32_t aImageFlags,
   6264    const nsPoint* aAnchorPoint) {
   6265  // NOTE(emilio): We can hardcode resolution to 1 here, since we're interested
   6266  // in the actual image pixels, for snapping purposes, not on the adjusted
   6267  // size.
   6268  CSSIntSize pixelImageSize(ComputeSizeForDrawingWithFallback(
   6269      aImage, ImageResolution(), aDest.Size()));
   6270  if (pixelImageSize.width < 1 || pixelImageSize.height < 1) {
   6271    NS_ASSERTION(pixelImageSize.width >= 0 && pixelImageSize.height >= 0,
   6272                 "Image width or height is negative");
   6273    return ImgDrawResult::SUCCESS;  // no point in drawing a zero size image
   6274  }
   6275 
   6276  const nsSize imageSize(CSSPixel::ToAppUnits(pixelImageSize));
   6277  const nsRect source(nsPoint(), imageSize);
   6278  const nsRect dest = GetWholeImageDestination(imageSize, source, aDest);
   6279 
   6280  // Ensure that only a single image tile is drawn. If aSourceArea extends
   6281  // outside the image bounds, we want to honor the aSourceArea-to-aDest
   6282  // transform but we don't want to actually tile the image.
   6283  nsRect fill;
   6284  fill.IntersectRect(aDest, dest);
   6285  return DrawImageInternal(aContext, aPresContext, aImage, aSamplingFilter,
   6286                           dest, fill,
   6287                           aAnchorPoint ? *aAnchorPoint : fill.TopLeft(),
   6288                           aDirty, aSVGContext, aImageFlags);
   6289 }
   6290 
   6291 /* static */
   6292 void nsLayoutUtils::ComputeSizeForDrawing(
   6293    imgIContainer* aImage, const ImageResolution& aResolution,
   6294    /* outparam */ CSSIntSize& aImageSize,
   6295    /* outparam */ AspectRatio& aIntrinsicRatio,
   6296    /* outparam */ bool& aGotWidth,
   6297    /* outparam */ bool& aGotHeight) {
   6298  aGotWidth = NS_SUCCEEDED(aImage->GetWidth(&aImageSize.width));
   6299  aGotHeight = NS_SUCCEEDED(aImage->GetHeight(&aImageSize.height));
   6300  aIntrinsicRatio = aImage->GetIntrinsicRatio();
   6301 
   6302  if (aGotWidth) {
   6303    aResolution.ApplyXTo(aImageSize.width);
   6304  }
   6305  if (aGotHeight) {
   6306    aResolution.ApplyYTo(aImageSize.height);
   6307  }
   6308 }
   6309 
   6310 /* static */
   6311 CSSIntSize nsLayoutUtils::ComputeSizeForDrawingWithFallback(
   6312    imgIContainer* aImage, const ImageResolution& aResolution,
   6313    const nsSize& aFallbackSize) {
   6314  CSSIntSize imageSize;
   6315  AspectRatio imageRatio;
   6316  bool gotHeight, gotWidth;
   6317  ComputeSizeForDrawing(aImage, aResolution, imageSize, imageRatio, gotWidth,
   6318                        gotHeight);
   6319 
   6320  // If we didn't get both width and height, try to compute them using the
   6321  // intrinsic ratio of the image.
   6322  if (gotWidth != gotHeight) {
   6323    if (!gotWidth) {
   6324      if (imageRatio) {
   6325        imageSize.width = imageRatio.ApplyTo(imageSize.height);
   6326        gotWidth = true;
   6327      }
   6328    } else {
   6329      if (imageRatio) {
   6330        imageSize.height = imageRatio.Inverted().ApplyTo(imageSize.width);
   6331        gotHeight = true;
   6332      }
   6333    }
   6334  }
   6335 
   6336  // If we still don't have a width or height, just use the fallback size the
   6337  // caller provided.
   6338  if (!gotWidth) {
   6339    imageSize.width =
   6340        nsPresContext::AppUnitsToIntCSSPixels(aFallbackSize.width);
   6341  }
   6342  if (!gotHeight) {
   6343    imageSize.height =
   6344        nsPresContext::AppUnitsToIntCSSPixels(aFallbackSize.height);
   6345  }
   6346 
   6347  return imageSize;
   6348 }
   6349 
   6350 /* static */ LayerIntRect SnapRectForImage(
   6351    const gfx::Matrix& aTransform, const gfx::MatrixScales& aScaleFactors,
   6352    const LayoutDeviceRect& aRect) {
   6353  // Attempt to snap pixels, the same as ComputeSnappedImageDrawingParameters.
   6354  // Any changes to the algorithm here will need to be reflected there.
   6355  bool snapped = false;
   6356  LayerIntRect snapRect;
   6357  if (!aTransform.HasNonAxisAlignedTransform() && aTransform._11 > 0.0 &&
   6358      aTransform._22 > 0.0) {
   6359    gfxRect rect(gfxPoint(aRect.X(), aRect.Y()),
   6360                 gfxSize(aRect.Width(), aRect.Height()));
   6361 
   6362    gfxPoint p1 =
   6363        ThebesPoint(aTransform.TransformPoint(ToPoint(rect.TopLeft())));
   6364    gfxPoint p2 =
   6365        ThebesPoint(aTransform.TransformPoint(ToPoint(rect.TopRight())));
   6366    gfxPoint p3 =
   6367        ThebesPoint(aTransform.TransformPoint(ToPoint(rect.BottomRight())));
   6368 
   6369    if (p2 == gfxPoint(p1.x, p3.y) || p2 == gfxPoint(p3.x, p1.y)) {
   6370      p1.Round();
   6371      p3.Round();
   6372 
   6373      IntPoint p1i(int32_t(p1.x), int32_t(p1.y));
   6374      IntPoint p3i(int32_t(p3.x), int32_t(p3.y));
   6375 
   6376      snapRect.MoveTo(std::min(p1i.x, p3i.x), std::min(p1i.y, p3i.y));
   6377      snapRect.SizeTo(std::max(p1i.x, p3i.x) - snapRect.X(),
   6378                      std::max(p1i.y, p3i.y) - snapRect.Y());
   6379      snapped = true;
   6380    }
   6381  }
   6382 
   6383  if (!snapped) {
   6384    // If we couldn't snap directly with the transform, we need to go best
   6385    // effort in layer pixels.
   6386    snapRect = RoundedToInt(
   6387        aRect * LayoutDeviceToLayerScale2D::FromUnknownScale(aScaleFactors));
   6388  }
   6389 
   6390  // An empty size is unacceptable so we ensure our suggested size is at least
   6391  // 1 pixel wide/tall.
   6392  if (snapRect.Width() < 1) {
   6393    snapRect.SetWidth(1);
   6394  }
   6395  if (snapRect.Height() < 1) {
   6396    snapRect.SetHeight(1);
   6397  }
   6398  return snapRect;
   6399 }
   6400 
   6401 /* static */
   6402 IntSize nsLayoutUtils::ComputeImageContainerDrawingParameters(
   6403    imgIContainer* aImage, nsIFrame* aForFrame,
   6404    const LayoutDeviceRect& aDestRect, const LayoutDeviceRect& aFillRect,
   6405    const StackingContextHelper& aSc, uint32_t aFlags,
   6406    SVGImageContext& aSVGContext, Maybe<ImageIntRegion>& aRegion) {
   6407  MOZ_ASSERT(aImage);
   6408  MOZ_ASSERT(aForFrame);
   6409 
   6410  MatrixScales scaleFactors = aSc.GetInheritedScale();
   6411  SamplingFilter samplingFilter =
   6412      nsLayoutUtils::GetSamplingFilterForFrame(aForFrame);
   6413 
   6414  // Compute our SVG context parameters, if any. Don't replace the viewport
   6415  // size if it was already set, prefer what the caller gave.
   6416  SVGImageContext::MaybeStoreContextPaint(aSVGContext, aForFrame, aImage);
   6417  if ((scaleFactors.xScale != 1.0 || scaleFactors.yScale != 1.0) &&
   6418      aImage->GetType() == imgIContainer::TYPE_VECTOR &&
   6419      (!aSVGContext.GetViewportSize())) {
   6420    gfxSize gfxDestSize(aDestRect.Width(), aDestRect.Height());
   6421    IntSize viewportSize = aImage->OptimalImageSizeForDest(
   6422        gfxDestSize, imgIContainer::FRAME_CURRENT, samplingFilter, aFlags);
   6423 
   6424    CSSIntSize cssViewportSize(viewportSize.width, viewportSize.height);
   6425    aSVGContext.SetViewportSize(Some(cssViewportSize));
   6426  }
   6427 
   6428  const gfx::Matrix& itm = aSc.GetInheritedTransform();
   6429  LayerIntRect destRect = SnapRectForImage(itm, scaleFactors, aDestRect);
   6430 
   6431  // Since we always decode entire raster images, we only care about the
   6432  // ImageIntRegion for vector images when we are recording blobs, for which we
   6433  // may only draw part of in some cases.
   6434  if ((aImage->GetType() != imgIContainer::TYPE_VECTOR) ||
   6435      !(aFlags & imgIContainer::FLAG_RECORD_BLOB)) {
   6436    // If the transform scale of our stacking context helper is being animated
   6437    // on the compositor then the transform will have the current value of the
   6438    // scale, but the scale factors will have max value of the scale animation.
   6439    // So we want to ask for a decoded image that can fulfill that larger size.
   6440    int32_t scaleWidth = int32_t(ceil(aDestRect.Width() * scaleFactors.xScale));
   6441    if (scaleWidth > destRect.width + 2) {
   6442      destRect.width = scaleWidth;
   6443    }
   6444    int32_t scaleHeight =
   6445        int32_t(ceil(aDestRect.Height() * scaleFactors.yScale));
   6446    if (scaleHeight > destRect.height + 2) {
   6447      destRect.height = scaleHeight;
   6448    }
   6449 
   6450    return aImage->OptimalImageSizeForDest(
   6451        gfxSize(destRect.Width(), destRect.Height()),
   6452        imgIContainer::FRAME_CURRENT, samplingFilter, aFlags);
   6453  }
   6454 
   6455  // We only use the region rect with blob recordings. This is because when we
   6456  // rasterize an SVG image in process, we always create a complete
   6457  // rasterization of the whole image which can be given to any caller, while
   6458  // we support partial rasterization with the blob recordings.
   6459  if (aFlags & imgIContainer::FLAG_RECORD_BLOB) {
   6460    // If the dest rect contains the fill rect, then we are only displaying part
   6461    // of the vector image. We need to calculate the restriction region to avoid
   6462    // drawing more than we need, and sampling outside the desired bounds.
   6463    LayerIntRect clipRect = SnapRectForImage(itm, scaleFactors, aFillRect);
   6464    if (destRect.Contains(clipRect)) {
   6465      LayerIntRect restrictRect = destRect.Intersect(clipRect);
   6466      restrictRect.MoveBy(-destRect.TopLeft());
   6467 
   6468      if (restrictRect.Width() < 1) {
   6469        restrictRect.SetWidth(1);
   6470      }
   6471      if (restrictRect.Height() < 1) {
   6472        restrictRect.SetHeight(1);
   6473      }
   6474 
   6475      if (restrictRect.X() != 0 || restrictRect.Y() != 0 ||
   6476          restrictRect.Size() != destRect.Size()) {
   6477        IntRect sampleRect = restrictRect.ToUnknownRect();
   6478        aRegion = Some(ImageIntRegion::CreateWithSamplingRestriction(
   6479            sampleRect, sampleRect, ExtendMode::CLAMP));
   6480      }
   6481    }
   6482  }
   6483 
   6484  // VectorImage::OptimalImageSizeForDest will just round up, but we already
   6485  // have an integer size.
   6486  return destRect.Size().ToUnknownSize();
   6487 }
   6488 
   6489 /* static */
   6490 nsPoint nsLayoutUtils::GetBackgroundFirstTilePos(const nsPoint& aDest,
   6491                                                 const nsPoint& aFill,
   6492                                                 const nsSize& aRepeatSize) {
   6493  return nsPoint(NSToIntFloor(float(aFill.x - aDest.x) / aRepeatSize.width) *
   6494                     aRepeatSize.width,
   6495                 NSToIntFloor(float(aFill.y - aDest.y) / aRepeatSize.height) *
   6496                     aRepeatSize.height) +
   6497         aDest;
   6498 }
   6499 
   6500 /* static */
   6501 ImgDrawResult nsLayoutUtils::DrawBackgroundImage(
   6502    gfxContext& aContext, nsIFrame* aForFrame, nsPresContext* aPresContext,
   6503    imgIContainer* aImage, SamplingFilter aSamplingFilter, const nsRect& aDest,
   6504    const nsRect& aFill, const nsSize& aRepeatSize, const nsPoint& aAnchor,
   6505    const nsRect& aDirty, uint32_t aImageFlags, ExtendMode aExtendMode,
   6506    float aOpacity) {
   6507  AUTO_PROFILER_LABEL("nsLayoutUtils::DrawBackgroundImage",
   6508                      GRAPHICS_Rasterization);
   6509 
   6510  CSSIntSize destCSSSize{nsPresContext::AppUnitsToIntCSSPixels(aDest.width),
   6511                         nsPresContext::AppUnitsToIntCSSPixels(aDest.height)};
   6512 
   6513  SVGImageContext svgContext(Some(destCSSSize));
   6514  SVGImageContext::MaybeStoreContextPaint(svgContext, aForFrame, aImage);
   6515 
   6516  /* Fast path when there is no need for image spacing */
   6517  if (aRepeatSize.width == aDest.width && aRepeatSize.height == aDest.height) {
   6518    return DrawImageInternal(aContext, aPresContext, aImage, aSamplingFilter,
   6519                             aDest, aFill, aAnchor, aDirty, svgContext,
   6520                             aImageFlags, aExtendMode, aOpacity);
   6521  }
   6522 
   6523  const nsPoint firstTilePos =
   6524      GetBackgroundFirstTilePos(aDest.TopLeft(), aFill.TopLeft(), aRepeatSize);
   6525  const nscoord xMost = aFill.XMost();
   6526  const nscoord repeatWidth = aRepeatSize.width;
   6527  const nscoord yMost = aFill.YMost();
   6528  const nscoord repeatHeight = aRepeatSize.height;
   6529  nsRect dest(0, 0, aDest.width, aDest.height);
   6530  nsPoint anchor = aAnchor;
   6531  for (nscoord x = firstTilePos.x; x < xMost; x += repeatWidth) {
   6532    for (nscoord y = firstTilePos.y; y < yMost; y += repeatHeight) {
   6533      dest.x = x;
   6534      dest.y = y;
   6535      ImgDrawResult result = DrawImageInternal(
   6536          aContext, aPresContext, aImage, aSamplingFilter, dest, dest, anchor,
   6537          aDirty, svgContext, aImageFlags, ExtendMode::CLAMP, aOpacity);
   6538      anchor.y += repeatHeight;
   6539      if (result != ImgDrawResult::SUCCESS) {
   6540        return result;
   6541      }
   6542    }
   6543    anchor.x += repeatWidth;
   6544    anchor.y = aAnchor.y;
   6545  }
   6546 
   6547  return ImgDrawResult::SUCCESS;
   6548 }
   6549 
   6550 /* static */
   6551 ImgDrawResult nsLayoutUtils::DrawImage(
   6552    gfxContext& aContext, ComputedStyle* aComputedStyle,
   6553    nsPresContext* aPresContext, imgIContainer* aImage,
   6554    const SamplingFilter aSamplingFilter, const nsRect& aDest,
   6555    const nsRect& aFill, const nsPoint& aAnchor, const nsRect& aDirty,
   6556    uint32_t aImageFlags, float aOpacity) {
   6557  SVGImageContext svgContext;
   6558  SVGImageContext::MaybeStoreContextPaint(svgContext, *aPresContext,
   6559                                          *aComputedStyle, aImage);
   6560 
   6561  return DrawImageInternal(aContext, aPresContext, aImage, aSamplingFilter,
   6562                           aDest, aFill, aAnchor, aDirty, svgContext,
   6563                           aImageFlags, ExtendMode::CLAMP, aOpacity);
   6564 }
   6565 
   6566 /* static */
   6567 nsRect nsLayoutUtils::GetWholeImageDestination(const nsSize& aWholeImageSize,
   6568                                               const nsRect& aImageSourceArea,
   6569                                               const nsRect& aDestArea) {
   6570  double scaleX = double(aDestArea.width) / aImageSourceArea.width;
   6571  double scaleY = double(aDestArea.height) / aImageSourceArea.height;
   6572  nscoord destOffsetX = NSToCoordRound(aImageSourceArea.x * scaleX);
   6573  nscoord destOffsetY = NSToCoordRound(aImageSourceArea.y * scaleY);
   6574  nscoord wholeSizeX = NSToCoordRound(aWholeImageSize.width * scaleX);
   6575  nscoord wholeSizeY = NSToCoordRound(aWholeImageSize.height * scaleY);
   6576  return nsRect(aDestArea.TopLeft() - nsPoint(destOffsetX, destOffsetY),
   6577                nsSize(wholeSizeX, wholeSizeY));
   6578 }
   6579 
   6580 /* static */
   6581 already_AddRefed<imgIContainer> nsLayoutUtils::OrientImage(
   6582    imgIContainer* aContainer, const StyleImageOrientation& aOrientation) {
   6583  MOZ_ASSERT(aContainer, "Should have an image container");
   6584  nsCOMPtr<imgIContainer> img(aContainer);
   6585 
   6586  switch (aOrientation) {
   6587    case StyleImageOrientation::FromImage:
   6588      break;
   6589    case StyleImageOrientation::None:
   6590      img = ImageOps::Unorient(img);
   6591      break;
   6592  }
   6593 
   6594  return img.forget();
   6595 }
   6596 
   6597 /* static */
   6598 bool nsLayoutUtils::ImageRequestUsesCORS(imgIRequest* aRequest) {
   6599  int32_t corsMode = mozilla::CORS_NONE;
   6600  return NS_SUCCEEDED(aRequest->GetCORSMode(&corsMode)) &&
   6601         corsMode != mozilla::CORS_NONE;
   6602 }
   6603 
   6604 static bool NonZeroCorner(const LengthPercentage& aLength) {
   6605  // Since negative results are clamped to 0, check > 0.
   6606  return aLength.Resolve(nscoord_MAX) > 0 || aLength.Resolve(0) > 0;
   6607 }
   6608 
   6609 /* static */
   6610 bool nsLayoutUtils::HasNonZeroCorner(const BorderRadius& aCorners) {
   6611  for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
   6612    if (NonZeroCorner(aCorners.Get(corner))) {
   6613      return true;
   6614    }
   6615  }
   6616  return false;
   6617 }
   6618 
   6619 // aCorner is a "full corner" value, i.e. eCornerTopLeft etc.
   6620 static bool IsCornerAdjacentToSide(uint8_t aCorner, Side aSide) {
   6621  static_assert((int)eSideTop == eCornerTopLeft, "Check for Full Corner");
   6622  static_assert((int)eSideRight == eCornerTopRight, "Check for Full Corner");
   6623  static_assert((int)eSideBottom == eCornerBottomRight,
   6624                "Check for Full Corner");
   6625  static_assert((int)eSideLeft == eCornerBottomLeft, "Check for Full Corner");
   6626  static_assert((int)eSideTop == ((eCornerTopRight - 1) & 3),
   6627                "Check for Full Corner");
   6628  static_assert((int)eSideRight == ((eCornerBottomRight - 1) & 3),
   6629                "Check for Full Corner");
   6630  static_assert((int)eSideBottom == ((eCornerBottomLeft - 1) & 3),
   6631                "Check for Full Corner");
   6632  static_assert((int)eSideLeft == ((eCornerTopLeft - 1) & 3),
   6633                "Check for Full Corner");
   6634 
   6635  return aSide == aCorner || aSide == ((aCorner - 1) & 3);
   6636 }
   6637 
   6638 /* static */
   6639 bool nsLayoutUtils::HasNonZeroCornerOnSide(const BorderRadius& aCorners,
   6640                                           Side aSide) {
   6641  static_assert(eCornerTopLeftX / 2 == eCornerTopLeft,
   6642                "Check for Non Zero on side");
   6643  static_assert(eCornerTopLeftY / 2 == eCornerTopLeft,
   6644                "Check for Non Zero on side");
   6645  static_assert(eCornerTopRightX / 2 == eCornerTopRight,
   6646                "Check for Non Zero on side");
   6647  static_assert(eCornerTopRightY / 2 == eCornerTopRight,
   6648                "Check for Non Zero on side");
   6649  static_assert(eCornerBottomRightX / 2 == eCornerBottomRight,
   6650                "Check for Non Zero on side");
   6651  static_assert(eCornerBottomRightY / 2 == eCornerBottomRight,
   6652                "Check for Non Zero on side");
   6653  static_assert(eCornerBottomLeftX / 2 == eCornerBottomLeft,
   6654                "Check for Non Zero on side");
   6655  static_assert(eCornerBottomLeftY / 2 == eCornerBottomLeft,
   6656                "Check for Non Zero on side");
   6657 
   6658  for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
   6659    // corner is a "half corner" value, so dividing by two gives us a
   6660    // "full corner" value.
   6661    if (NonZeroCorner(aCorners.Get(corner)) &&
   6662        IsCornerAdjacentToSide(corner / 2, aSide)) {
   6663      return true;
   6664    }
   6665  }
   6666  return false;
   6667 }
   6668 
   6669 /* static */
   6670 widget::TransparencyMode nsLayoutUtils::GetFrameTransparency(
   6671    const nsIFrame* aBackgroundFrame, const nsIFrame* aCSSRootFrame) {
   6672  if (!aCSSRootFrame->StyleEffects()->IsOpaque()) {
   6673    return TransparencyMode::Transparent;
   6674  }
   6675 
   6676  if (HasNonZeroCorner(aCSSRootFrame->StyleBorder()->mBorderRadius)) {
   6677    return TransparencyMode::Transparent;
   6678  }
   6679 
   6680  nsITheme::Transparency transparency;
   6681  if (aCSSRootFrame->IsThemed(&transparency)) {
   6682    return transparency == nsITheme::eTransparent
   6683               ? TransparencyMode::Transparent
   6684               : TransparencyMode::Opaque;
   6685  }
   6686 
   6687  // We need an uninitialized window to be treated as opaque because doing
   6688  // otherwise breaks window display effects on some platforms, specifically
   6689  // Vista. (bug 450322)
   6690  if (aBackgroundFrame->IsViewportFrame() &&
   6691      !aBackgroundFrame->PrincipalChildList().FirstChild()) {
   6692    return TransparencyMode::Opaque;
   6693  }
   6694 
   6695  const ComputedStyle* bgSC = nsCSSRendering::FindBackground(aBackgroundFrame);
   6696  if (!bgSC) {
   6697    return TransparencyMode::Transparent;
   6698  }
   6699  const nsStyleBackground* bg = bgSC->StyleBackground();
   6700  if (NS_GET_A(bg->BackgroundColor(bgSC)) < 255 ||
   6701      // bottom layer's clip is used for the color
   6702      bg->BottomLayer().mClip != StyleGeometryBox::BorderBox) {
   6703    return TransparencyMode::Transparent;
   6704  }
   6705  return TransparencyMode::Opaque;
   6706 }
   6707 
   6708 /* static */
   6709 bool nsLayoutUtils::IsPopup(const nsIFrame* aFrame) {
   6710  return aFrame->IsMenuPopupFrame();
   6711 }
   6712 
   6713 /* static */
   6714 nsIFrame* nsLayoutUtils::GetDisplayRootFrame(nsIFrame* aFrame) {
   6715  return const_cast<nsIFrame*>(
   6716      nsLayoutUtils::GetDisplayRootFrame(const_cast<const nsIFrame*>(aFrame)));
   6717 }
   6718 
   6719 /* static */
   6720 const nsIFrame* nsLayoutUtils::GetDisplayRootFrame(const nsIFrame* aFrame) {
   6721  // We could use GetRootPresContext() here if the
   6722  // NS_FRAME_IN_POPUP frame bit is set.
   6723  const nsIFrame* f = aFrame;
   6724  for (;;) {
   6725    if (!f->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
   6726      f = f->PresShell()->GetRootFrame();
   6727      if (!f) {
   6728        return aFrame;
   6729      }
   6730    } else if (IsPopup(f)) {
   6731      return f;
   6732    }
   6733    nsIFrame* parent = GetCrossDocParentFrameInProcess(f);
   6734    if (!parent) {
   6735      return f;
   6736    }
   6737    f = parent;
   6738  }
   6739 }
   6740 
   6741 /* static */
   6742 nsIFrame* nsLayoutUtils::GetReferenceFrame(nsIFrame* aFrame) {
   6743  nsIFrame* f = aFrame;
   6744  for (;;) {
   6745    if (f->IsTransformed() || IsPopup(f)) {
   6746      return f;
   6747    }
   6748    nsIFrame* parent = GetCrossDocParentFrameInProcess(f);
   6749    if (!parent) {
   6750      return f;
   6751    }
   6752    f = parent;
   6753  }
   6754 }
   6755 
   6756 /* static */ gfx::ShapedTextFlags nsLayoutUtils::GetTextRunFlagsForStyle(
   6757    const ComputedStyle* aComputedStyle, nsPresContext* aPresContext,
   6758    const nsStyleFont* aStyleFont, const nsStyleText* aStyleText,
   6759    nscoord aLetterSpacing) {
   6760  gfx::ShapedTextFlags result = gfx::ShapedTextFlags();
   6761  if (aLetterSpacing != 0 ||
   6762      aStyleText->mTextJustify == StyleTextJustify::InterCharacter) {
   6763    result |= gfx::ShapedTextFlags::TEXT_DISABLE_OPTIONAL_LIGATURES;
   6764  }
   6765  if (aStyleText->mMozControlCharacterVisibility ==
   6766      StyleMozControlCharacterVisibility::Hidden) {
   6767    result |= gfx::ShapedTextFlags::TEXT_HIDE_CONTROL_CHARACTERS;
   6768  }
   6769  switch (aComputedStyle->StyleText()->mTextRendering) {
   6770    case StyleTextRendering::Optimizespeed:
   6771      result |= gfx::ShapedTextFlags::TEXT_OPTIMIZE_SPEED;
   6772      break;
   6773    case StyleTextRendering::Auto:
   6774      if (aPresContext &&
   6775          aStyleFont->mFont.size.ToCSSPixels() <
   6776              aPresContext->DevPixelsToFloatCSSPixels(
   6777                  StaticPrefs::browser_display_auto_quality_min_font_size())) {
   6778        result |= gfx::ShapedTextFlags::TEXT_OPTIMIZE_SPEED;
   6779      }
   6780      break;
   6781    default:
   6782      break;
   6783  }
   6784  return result | GetTextRunOrientFlagsForStyle(aComputedStyle);
   6785 }
   6786 
   6787 /* static */ gfx::ShapedTextFlags nsLayoutUtils::GetTextRunOrientFlagsForStyle(
   6788    const ComputedStyle* aComputedStyle) {
   6789  auto writingMode = aComputedStyle->StyleVisibility()->mWritingMode;
   6790  switch (writingMode) {
   6791    case StyleWritingModeProperty::HorizontalTb:
   6792      return gfx::ShapedTextFlags::TEXT_ORIENT_HORIZONTAL;
   6793 
   6794    case StyleWritingModeProperty::VerticalLr:
   6795    case StyleWritingModeProperty::VerticalRl:
   6796      switch (aComputedStyle->StyleVisibility()->mTextOrientation) {
   6797        case StyleTextOrientation::Mixed:
   6798          return gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED;
   6799        case StyleTextOrientation::Upright:
   6800          return gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
   6801        case StyleTextOrientation::Sideways:
   6802          return gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
   6803        default:
   6804          MOZ_ASSERT_UNREACHABLE("unknown text-orientation");
   6805          return gfx::ShapedTextFlags();
   6806      }
   6807 
   6808    case StyleWritingModeProperty::SidewaysLr:
   6809      return gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT;
   6810 
   6811    case StyleWritingModeProperty::SidewaysRl:
   6812      return gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
   6813 
   6814    default:
   6815      MOZ_ASSERT_UNREACHABLE("unknown writing-mode");
   6816      return gfx::ShapedTextFlags();
   6817  }
   6818 }
   6819 
   6820 /* static */
   6821 void nsLayoutUtils::GetRectDifferenceStrips(const nsRect& aR1,
   6822                                            const nsRect& aR2, nsRect* aHStrip,
   6823                                            nsRect* aVStrip) {
   6824  NS_ASSERTION(aR1.TopLeft() == aR2.TopLeft(),
   6825               "expected rects at the same position");
   6826  nsRect unionRect(aR1.x, aR1.y, std::max(aR1.width, aR2.width),
   6827                   std::max(aR1.height, aR2.height));
   6828  nscoord VStripStart = std::min(aR1.width, aR2.width);
   6829  nscoord HStripStart = std::min(aR1.height, aR2.height);
   6830  *aVStrip = unionRect;
   6831  aVStrip->x += VStripStart;
   6832  aVStrip->width -= VStripStart;
   6833  *aHStrip = unionRect;
   6834  aHStrip->y += HStripStart;
   6835  aHStrip->height -= HStripStart;
   6836 }
   6837 
   6838 nsDeviceContext* nsLayoutUtils::GetDeviceContextForScreenInfo(
   6839    nsPIDOMWindowOuter* aWindow) {
   6840  if (!aWindow) {
   6841    return nullptr;
   6842  }
   6843 
   6844  nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
   6845  while (docShell) {
   6846    // Now make sure our size is up to date.  That will mean that the device
   6847    // context does the right thing on multi-monitor systems when we return it
   6848    // to the caller.  It will also make sure that our prescontext has been
   6849    // created, if we're supposed to have one.
   6850    nsCOMPtr<nsPIDOMWindowOuter> win = docShell->GetWindow();
   6851    if (!win) {
   6852      // No reason to go on
   6853      return nullptr;
   6854    }
   6855 
   6856    win->EnsureSizeAndPositionUpToDate();
   6857 
   6858    RefPtr<nsPresContext> presContext = docShell->GetPresContext();
   6859    if (presContext) {
   6860      nsDeviceContext* context = presContext->DeviceContext();
   6861      if (context) {
   6862        return context;
   6863      }
   6864    }
   6865 
   6866    nsCOMPtr<nsIDocShellTreeItem> parentItem;
   6867    docShell->GetInProcessParent(getter_AddRefs(parentItem));
   6868    docShell = do_QueryInterface(parentItem);
   6869  }
   6870 
   6871  return nullptr;
   6872 }
   6873 
   6874 /* static */
   6875 bool nsLayoutUtils::IsReallyFixedPos(const nsIFrame* aFrame) {
   6876  MOZ_ASSERT(aFrame->StyleDisplay()->mPosition == StylePositionProperty::Fixed,
   6877             "IsReallyFixedPos called on non-'position:fixed' frame");
   6878  return MayBeReallyFixedPos(aFrame);
   6879 }
   6880 
   6881 /* static */
   6882 bool nsLayoutUtils::MayBeReallyFixedPos(const nsIFrame* aFrame) {
   6883  MOZ_ASSERT(aFrame->GetParent(),
   6884             "MayBeReallyFixedPos called on frame not in tree");
   6885  LayoutFrameType parentType = aFrame->GetParent()->Type();
   6886  return parentType == LayoutFrameType::Viewport ||
   6887         parentType == LayoutFrameType::PageContent;
   6888 }
   6889 
   6890 /* static */
   6891 bool nsLayoutUtils::IsInPositionFixedSubtree(const nsIFrame* aFrame) {
   6892  for (const nsIFrame* f = aFrame; f; f = f->GetParent()) {
   6893    if (f->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
   6894        nsLayoutUtils::IsReallyFixedPos(f)) {
   6895      return true;
   6896    }
   6897  }
   6898  return false;
   6899 }
   6900 
   6901 SurfaceFromElementResult nsLayoutUtils::SurfaceFromOffscreenCanvas(
   6902    OffscreenCanvas* aOffscreenCanvas, uint32_t aSurfaceFlags,
   6903    RefPtr<DrawTarget>& aTarget) {
   6904  SurfaceFromElementResult result;
   6905 
   6906  IntSize size = aOffscreenCanvas->GetWidthHeight().ToUnknownSize();
   6907  if (size.IsEmpty()) {
   6908    return result;
   6909  }
   6910 
   6911  result.mSourceSurface =
   6912      aOffscreenCanvas->GetSurfaceSnapshot(&result.mAlphaType);
   6913  if (!result.mSourceSurface) {
   6914    // If the element doesn't have a context then we won't get a snapshot. The
   6915    // canvas spec wants us to not error and just draw nothing, so return an
   6916    // empty surface.
   6917    result.mSize = size;
   6918    result.mAlphaType = gfxAlphaType::Opaque;
   6919    RefPtr<DrawTarget> ref =
   6920        aTarget ? aTarget : gfxPlatform::ThreadLocalScreenReferenceDrawTarget();
   6921    if (ref->CanCreateSimilarDrawTarget(size, SurfaceFormat::B8G8R8A8)) {
   6922      RefPtr<DrawTarget> dt =
   6923          ref->CreateSimilarDrawTarget(size, SurfaceFormat::B8G8R8A8);
   6924      if (dt) {
   6925        result.mSourceSurface = dt->Snapshot();
   6926      }
   6927    }
   6928  } else {
   6929    result.mSize = result.mSourceSurface->GetSize();
   6930 
   6931    // If we want an exact sized surface, then we need to scale if we don't
   6932    // match the intrinsic size.
   6933    const bool exactSize = aSurfaceFlags & SFE_EXACT_SIZE_SURFACE;
   6934    if (exactSize && size != result.mSize) {
   6935      result.mSize = size;
   6936      result.mSourceSurface =
   6937          gfxUtils::ScaleSourceSurface(*result.mSourceSurface, size);
   6938    }
   6939 
   6940    if (aTarget && result.mSourceSurface) {
   6941      RefPtr<SourceSurface> opt =
   6942          aTarget->OptimizeSourceSurface(result.mSourceSurface);
   6943      if (opt) {
   6944        result.mSourceSurface = opt;
   6945      }
   6946    }
   6947  }
   6948 
   6949  result.mHasSize = true;
   6950  result.mIntrinsicSize = size;
   6951  result.mIsWriteOnly = aOffscreenCanvas->IsWriteOnly();
   6952 
   6953  nsIGlobalObject* global = aOffscreenCanvas->GetParentObject();
   6954  if (global) {
   6955    result.mPrincipal = global->PrincipalOrNull();
   6956  }
   6957 
   6958  return result;
   6959 }
   6960 
   6961 SurfaceFromElementResult nsLayoutUtils::SurfaceFromVideoFrame(
   6962    VideoFrame* aVideoFrame, uint32_t aSurfaceFlags,
   6963    RefPtr<DrawTarget>& aTarget) {
   6964  SurfaceFromElementResult result;
   6965 
   6966  RefPtr<layers::Image> layersImage = aVideoFrame->GetImage();
   6967  if (!layersImage) {
   6968    return result;
   6969  }
   6970 
   6971  IntSize codedSize = aVideoFrame->NativeCodedSize();
   6972  IntRect visibleRect = aVideoFrame->NativeVisibleRect();
   6973  IntSize displaySize = aVideoFrame->NativeDisplaySize();
   6974 
   6975  MOZ_ASSERT(layersImage->GetSize() == codedSize);
   6976  IntRect codedRect(IntPoint(0, 0), codedSize);
   6977 
   6978  if (visibleRect.IsEqualEdges(codedRect) && displaySize == codedSize) {
   6979    // The display and coded rects are identical, which means we can just use
   6980    // the image as is.
   6981    result.mLayersImage = std::move(layersImage);
   6982    result.mSize = codedSize;
   6983    result.mIntrinsicSize = codedSize;
   6984  } else if (aSurfaceFlags & SFE_ALLOW_UNCROPPED_UNSCALED) {
   6985    // The caller supports cropping/scaling.
   6986    result.mLayersImage = std::move(layersImage);
   6987    result.mCropRect = Some(visibleRect);
   6988    result.mSize = codedSize;
   6989    result.mIntrinsicSize = displaySize;
   6990  } else {
   6991    // The caller does not support cropping/scaling. We need to on its behalf.
   6992    RefPtr<SourceSurface> surface = layersImage->GetAsSourceSurface();
   6993    if (!surface) {
   6994      return result;
   6995    }
   6996 
   6997    RefPtr<DrawTarget> ref = aTarget
   6998                                 ? aTarget
   6999                                 : gfxPlatform::GetPlatform()
   7000                                       ->ThreadLocalScreenReferenceDrawTarget();
   7001    if (!ref->CanCreateSimilarDrawTarget(displaySize,
   7002                                         SurfaceFormat::B8G8R8A8)) {
   7003      return result;
   7004    }
   7005 
   7006    RefPtr<DrawTarget> dt =
   7007        ref->CreateSimilarDrawTarget(displaySize, SurfaceFormat::B8G8R8A8);
   7008    if (!dt) {
   7009      return result;
   7010    }
   7011 
   7012    gfx::Rect dstRect(0, 0, displaySize.Width(), displaySize.Height());
   7013    gfx::Rect srcRect(visibleRect.X(), visibleRect.Y(), visibleRect.Width(),
   7014                      visibleRect.Height());
   7015    dt->DrawSurface(surface, dstRect, srcRect);
   7016    result.mSourceSurface = dt->Snapshot();
   7017    if (NS_WARN_IF(!result.mSourceSurface)) {
   7018      return result;
   7019    }
   7020 
   7021    result.mSize = displaySize;
   7022    result.mIntrinsicSize = displaySize;
   7023  }
   7024 
   7025  result.mAlphaType = gfxAlphaType::Premult;
   7026  Nullable<VideoPixelFormat> format = aVideoFrame->GetFormat();
   7027  if (!format.IsNull()) {
   7028    switch (format.Value()) {
   7029      case VideoPixelFormat::I420:
   7030      case VideoPixelFormat::I422:
   7031      case VideoPixelFormat::I444:
   7032      case VideoPixelFormat::NV12:
   7033      case VideoPixelFormat::RGBX:
   7034      case VideoPixelFormat::BGRX:
   7035        result.mAlphaType = gfxAlphaType::Opaque;
   7036        break;
   7037      default:
   7038        break;
   7039    }
   7040  }
   7041 
   7042  result.mHasSize = true;
   7043 
   7044  // We shouldn't have a VideoFrame if either of these is true.
   7045  result.mHadCrossOriginRedirects = false;
   7046  result.mIsWriteOnly = false;
   7047 
   7048  nsIGlobalObject* global = aVideoFrame->GetParentObject();
   7049  if (global) {
   7050    result.mPrincipal = global->PrincipalOrNull();
   7051  }
   7052 
   7053  if (aTarget) {
   7054    // They gave us a DrawTarget to optimize for, so even though we may have a
   7055    // layers::Image, we should unconditionally try to grab a SourceSurface and
   7056    // try to optimize it.
   7057    if (result.mLayersImage) {
   7058      MOZ_ASSERT(!result.mSourceSurface);
   7059      result.mSourceSurface = result.mLayersImage->GetAsSourceSurface();
   7060    }
   7061 
   7062    if (result.mSourceSurface) {
   7063      RefPtr<SourceSurface> opt =
   7064          aTarget->OptimizeSourceSurface(result.mSourceSurface);
   7065      if (opt) {
   7066        result.mSourceSurface = std::move(opt);
   7067      }
   7068    }
   7069  }
   7070 
   7071  return result;
   7072 }
   7073 
   7074 SurfaceFromElementResult nsLayoutUtils::SurfaceFromImageBitmap(
   7075    mozilla::dom::ImageBitmap* aImageBitmap, uint32_t aSurfaceFlags) {
   7076  return aImageBitmap->SurfaceFrom(aSurfaceFlags);
   7077 }
   7078 
   7079 SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(
   7080    nsIImageLoadingContent* aElement, const Maybe<int32_t>& aResizeWidth,
   7081    const Maybe<int32_t>& aResizeHeight, uint32_t aSurfaceFlags,
   7082    RefPtr<DrawTarget>& aTarget) {
   7083  SurfaceFromElementResult result;
   7084  nsresult rv;
   7085 
   7086  nsCOMPtr<imgIRequest> imgRequest;
   7087  rv = aElement->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
   7088                            getter_AddRefs(imgRequest));
   7089  if (NS_FAILED(rv)) {
   7090    return result;
   7091  }
   7092 
   7093  if (!imgRequest) {
   7094    // There's no image request. This is either because a request for
   7095    // a non-empty URI failed, or the URI is the empty string.
   7096    nsCOMPtr<nsIURI> currentURI;
   7097    aElement->GetCurrentURI(getter_AddRefs(currentURI));
   7098    if (!currentURI) {
   7099      // Treat the empty URI as available instead of broken state.
   7100      result.mHasSize = true;
   7101    }
   7102    return result;
   7103  }
   7104 
   7105  uint32_t status;
   7106  imgRequest->GetImageStatus(&status);
   7107  result.mHasSize = status & imgIRequest::STATUS_SIZE_AVAILABLE;
   7108  if ((status & imgIRequest::STATUS_LOAD_COMPLETE) == 0) {
   7109    // Spec says to use GetComplete, but that only works on
   7110    // HTMLImageElement, and we support all sorts of other stuff
   7111    // here.  Do this for now pending spec clarification.
   7112    result.mIsStillLoading = (status & imgIRequest::STATUS_ERROR) == 0;
   7113    return result;
   7114  }
   7115 
   7116  nsCOMPtr<nsIPrincipal> principal;
   7117  rv = imgRequest->GetImagePrincipal(getter_AddRefs(principal));
   7118  if (NS_FAILED(rv)) {
   7119    return result;
   7120  }
   7121 
   7122  nsCOMPtr<imgIContainer> imgContainer;
   7123  rv = imgRequest->GetImage(getter_AddRefs(imgContainer));
   7124  if (NS_FAILED(rv)) {
   7125    return result;
   7126  }
   7127 
   7128  nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
   7129 
   7130  // Ensure that the image is oriented the same way as it's displayed
   7131  // if the image request is of the same origin.
   7132  auto orientation =
   7133      content->GetPrimaryFrame() &&
   7134              !(aSurfaceFlags & SFE_ORIENTATION_FROM_IMAGE)
   7135          ? content->GetPrimaryFrame()->StyleVisibility()->UsedImageOrientation(
   7136                imgRequest)
   7137          : nsStyleVisibility::UsedImageOrientation(
   7138                imgRequest, StyleImageOrientation::FromImage);
   7139  imgContainer = OrientImage(imgContainer, orientation);
   7140 
   7141  const bool noRasterize = aSurfaceFlags & SFE_NO_RASTERIZING_VECTORS;
   7142 
   7143  uint32_t whichFrame = aSurfaceFlags & SFE_WANT_FIRST_FRAME_IF_IMAGE
   7144                            ? (uint32_t)imgIContainer::FRAME_FIRST
   7145                            : (uint32_t)imgIContainer::FRAME_CURRENT;
   7146  const bool exactSize = aSurfaceFlags & SFE_EXACT_SIZE_SURFACE;
   7147 
   7148  uint32_t frameFlags =
   7149      imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY;
   7150  if (aSurfaceFlags & SFE_NO_COLORSPACE_CONVERSION) {
   7151    frameFlags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION;
   7152  }
   7153  if (aSurfaceFlags & SFE_ALLOW_NON_PREMULT) {
   7154    frameFlags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
   7155  }
   7156 
   7157  int32_t imgWidth, imgHeight;
   7158  HTMLImageElement* element = HTMLImageElement::FromNodeOrNull(content);
   7159  if (aSurfaceFlags & SFE_USE_ELEMENT_SIZE_IF_VECTOR && element &&
   7160      imgContainer->GetType() == imgIContainer::TYPE_VECTOR) {
   7161    // We're holding a strong ref to "element" via "content".
   7162    imgWidth = MOZ_KnownLive(element)->Width();
   7163    imgHeight = MOZ_KnownLive(element)->Height();
   7164  } else {
   7165    auto res = imgContainer->GetResolution();
   7166    rv = imgContainer->GetWidth(&imgWidth);
   7167    if (NS_SUCCEEDED(rv)) {
   7168      res.ApplyXTo(imgWidth);
   7169    } else if (aResizeWidth.isSome()) {
   7170      imgWidth = *aResizeWidth;
   7171    } else {
   7172      // As stated in css-sizing-3 Intrinsic Sizes, the fallback size of
   7173      // 300 x 150 for the width and height as needed.
   7174      //
   7175      // See https://drafts.csswg.org/css-sizing-3/#intrinsic-sizes
   7176      imgWidth = kFallbackIntrinsicWidthInPixels;
   7177    }
   7178    rv = imgContainer->GetHeight(&imgHeight);
   7179    if (NS_SUCCEEDED(rv)) {
   7180      res.ApplyYTo(imgHeight);
   7181    } else if (aResizeHeight.isSome()) {
   7182      imgHeight = *aResizeHeight;
   7183    } else {
   7184      // As stated in css-sizing-3 Intrinsic Sizes, the fallback size of
   7185      // 300 x 150 for the width and height as needed.
   7186      //
   7187      // See https://drafts.csswg.org/css-sizing-3/#intrinsic-sizes
   7188      imgHeight = kFallbackIntrinsicHeightInPixels;
   7189    }
   7190  }
   7191  result.mSize = result.mIntrinsicSize = IntSize(imgWidth, imgHeight);
   7192 
   7193  if (!noRasterize || imgContainer->GetType() == imgIContainer::TYPE_RASTER) {
   7194    result.mSourceSurface =
   7195        imgContainer->GetFrameAtSize(result.mSize, whichFrame, frameFlags);
   7196    if (!result.mSourceSurface) {
   7197      return result;
   7198    }
   7199    IntSize surfSize = result.mSourceSurface->GetSize();
   7200    if (exactSize && surfSize != result.mSize) {
   7201      result.mSourceSurface =
   7202          gfxUtils::ScaleSourceSurface(*result.mSourceSurface, result.mSize);
   7203      if (!result.mSourceSurface) {
   7204        return result;
   7205      }
   7206    } else {
   7207      result.mSize = surfSize;
   7208    }
   7209    // The surface we return is likely to be cached. We don't want to have to
   7210    // convert to a surface that's compatible with aTarget each time it's used
   7211    // (that would result in terrible performance), so we convert once here
   7212    // upfront if aTarget is specified.
   7213    if (aTarget) {
   7214      RefPtr<SourceSurface> optSurface =
   7215          aTarget->OptimizeSourceSurface(result.mSourceSurface);
   7216      if (optSurface) {
   7217        result.mSourceSurface = optSurface;
   7218      }
   7219    }
   7220 
   7221    const auto& format = result.mSourceSurface->GetFormat();
   7222    if (IsOpaque(format)) {
   7223      result.mAlphaType = gfxAlphaType::Opaque;
   7224    } else if (frameFlags & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA) {
   7225      result.mAlphaType = gfxAlphaType::NonPremult;
   7226    } else {
   7227      result.mAlphaType = gfxAlphaType::Premult;
   7228    }
   7229  } else {
   7230    result.mDrawInfo.mImgContainer = imgContainer;
   7231    result.mDrawInfo.mWhichFrame = whichFrame;
   7232    result.mDrawInfo.mDrawingFlags = frameFlags;
   7233  }
   7234 
   7235  result.mCORSUsed = nsLayoutUtils::ImageRequestUsesCORS(imgRequest);
   7236 
   7237  bool hadCrossOriginRedirects = true;
   7238  imgRequest->GetHadCrossOriginRedirects(&hadCrossOriginRedirects);
   7239 
   7240  result.mPrincipal = std::move(principal);
   7241  result.mHadCrossOriginRedirects = hadCrossOriginRedirects;
   7242  result.mImageRequest = std::move(imgRequest);
   7243  result.mIsWriteOnly = CanvasUtils::CheckWriteOnlySecurity(
   7244      result.mCORSUsed, result.mPrincipal, result.mHadCrossOriginRedirects);
   7245 
   7246  return result;
   7247 }
   7248 
   7249 SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(
   7250    HTMLImageElement* aElement, uint32_t aSurfaceFlags,
   7251    RefPtr<DrawTarget>& aTarget) {
   7252  return SurfaceFromElement(static_cast<nsIImageLoadingContent*>(aElement),
   7253                            Nothing(), Nothing(), aSurfaceFlags, aTarget);
   7254 }
   7255 
   7256 SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(
   7257    HTMLCanvasElement* aElement, uint32_t aSurfaceFlags,
   7258    RefPtr<DrawTarget>& aTarget) {
   7259  SurfaceFromElementResult result;
   7260 
   7261  IntSize size = aElement->GetSize().ToUnknownSize();
   7262  if (size.IsEmpty()) {
   7263    return result;
   7264  }
   7265 
   7266  auto pAlphaType = &result.mAlphaType;
   7267  if (!(aSurfaceFlags & SFE_ALLOW_NON_PREMULT)) {
   7268    pAlphaType =
   7269        nullptr;  // Coersce GetSurfaceSnapshot to give us Opaque/Premult only.
   7270  }
   7271  result.mSourceSurface = aElement->GetSurfaceSnapshot(pAlphaType, aTarget);
   7272  if (!result.mSourceSurface) {
   7273    // If the element doesn't have a context then we won't get a snapshot. The
   7274    // canvas spec wants us to not error and just draw nothing, so return an
   7275    // empty surface.
   7276    result.mSize = size;
   7277    result.mAlphaType = gfxAlphaType::Opaque;
   7278    RefPtr<DrawTarget> ref =
   7279        aTarget ? aTarget
   7280                : gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
   7281    if (ref->CanCreateSimilarDrawTarget(size, SurfaceFormat::B8G8R8A8)) {
   7282      RefPtr<DrawTarget> dt =
   7283          ref->CreateSimilarDrawTarget(size, SurfaceFormat::B8G8R8A8);
   7284      if (dt) {
   7285        result.mSourceSurface = dt->Snapshot();
   7286      }
   7287    }
   7288  } else {
   7289    result.mSize = result.mSourceSurface->GetSize();
   7290 
   7291    // If we want an exact sized surface, then we need to scale if we don't
   7292    // match the intrinsic size.
   7293    const bool exactSize = aSurfaceFlags & SFE_EXACT_SIZE_SURFACE;
   7294    if (exactSize && size != result.mSize) {
   7295      result.mSize = size;
   7296      result.mSourceSurface =
   7297          gfxUtils::ScaleSourceSurface(*result.mSourceSurface, size);
   7298    }
   7299 
   7300    if (aTarget && result.mSourceSurface) {
   7301      RefPtr<SourceSurface> opt =
   7302          aTarget->OptimizeSourceSurface(result.mSourceSurface);
   7303      if (opt) {
   7304        result.mSourceSurface = opt;
   7305      }
   7306    }
   7307  }
   7308 
   7309  // Ensure that any future changes to the canvas trigger proper invalidation,
   7310  // in case this is being used by -moz-element()
   7311  aElement->MarkContextClean();
   7312 
   7313  result.mHasSize = true;
   7314  result.mIntrinsicSize = size;
   7315  result.mPrincipal = aElement->NodePrincipal();
   7316  result.mHadCrossOriginRedirects = false;
   7317  result.mIsWriteOnly = aElement->IsWriteOnly();
   7318 
   7319  return result;
   7320 }
   7321 
   7322 SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(
   7323    HTMLVideoElement* aElement, uint32_t aSurfaceFlags,
   7324    RefPtr<DrawTarget>& aTarget, bool aOptimizeSourceSurface) {
   7325  SurfaceFromElementResult result;
   7326  result.mAlphaType = gfxAlphaType::Opaque;  // Assume opaque.
   7327 
   7328  if (aElement->ContainsRestrictedContent()) {
   7329    return result;
   7330  }
   7331 
   7332  uint16_t readyState = aElement->ReadyState();
   7333  if (readyState == HAVE_NOTHING || readyState == HAVE_METADATA) {
   7334    result.mIsStillLoading = true;
   7335    return result;
   7336  }
   7337 
   7338  // If it doesn't have a principal, just bail
   7339  nsCOMPtr<nsIPrincipal> principal = aElement->GetCurrentVideoPrincipal();
   7340  if (!principal) {
   7341    return result;
   7342  }
   7343 
   7344  result.mLayersImage = aElement->GetCurrentImage();
   7345  if (!result.mLayersImage) {
   7346    return result;
   7347  }
   7348 
   7349  result.mCORSUsed = aElement->GetCORSMode() != CORS_NONE;
   7350  result.mHasSize = true;
   7351  result.mSize = result.mLayersImage->GetSize();
   7352  result.mIntrinsicSize =
   7353      gfx::IntSize(aElement->VideoWidth(), aElement->VideoHeight());
   7354  result.mPrincipal = std::move(principal);
   7355  result.mHadCrossOriginRedirects = aElement->HadCrossOriginRedirects();
   7356  result.mIsWriteOnly = CanvasUtils::CheckWriteOnlySecurity(
   7357      result.mCORSUsed, result.mPrincipal, result.mHadCrossOriginRedirects);
   7358 
   7359  if (aTarget && aOptimizeSourceSurface) {
   7360    // They gave us a DrawTarget to optimize for, so even though we have a
   7361    // layers::Image, we should unconditionally try to grab a SourceSurface and
   7362    // try to optimize it.
   7363    if ((result.mSourceSurface = result.mLayersImage->GetAsSourceSurface())) {
   7364      RefPtr<SourceSurface> opt =
   7365          aTarget->OptimizeSourceSurface(result.mSourceSurface);
   7366      if (opt) {
   7367        result.mSourceSurface = opt;
   7368      }
   7369    }
   7370  }
   7371 
   7372  return result;
   7373 }
   7374 
   7375 SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(
   7376    dom::Element* aElement, const Maybe<int32_t>& aResizeWidth,
   7377    const Maybe<int32_t>& aResizeHeight, uint32_t aSurfaceFlags,
   7378    RefPtr<DrawTarget>& aTarget) {
   7379  // If it's a <canvas>, we may be able to just grab its internal surface
   7380  if (HTMLCanvasElement* canvas = HTMLCanvasElement::FromNodeOrNull(aElement)) {
   7381    return SurfaceFromElement(canvas, aSurfaceFlags, aTarget);
   7382  }
   7383 
   7384  // Maybe it's <video>?
   7385  if (HTMLVideoElement* video = HTMLVideoElement::FromNodeOrNull(aElement)) {
   7386    return SurfaceFromElement(video, aSurfaceFlags, aTarget);
   7387  }
   7388 
   7389  // Finally, check if it's a normal image
   7390  nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aElement);
   7391 
   7392  if (!imageLoader) {
   7393    return SurfaceFromElementResult();
   7394  }
   7395 
   7396  return SurfaceFromElement(imageLoader, aResizeWidth, aResizeHeight,
   7397                            aSurfaceFlags, aTarget);
   7398 }
   7399 
   7400 /* static */
   7401 Element* nsLayoutUtils::GetEditableRootContentByContentEditable(
   7402    Document* aDocument) {
   7403  // If the document is in designMode we should return nullptr.
   7404  if (!aDocument || aDocument->IsInDesignMode()) {
   7405    return nullptr;
   7406  }
   7407 
   7408  // contenteditable only works with HTML document.
   7409  // XXXbz should this test IsHTMLOrXHTML(), or just IsHTML()?
   7410  if (!aDocument->IsHTMLOrXHTML()) {
   7411    return nullptr;
   7412  }
   7413 
   7414  Element* rootElement = aDocument->GetRootElement();
   7415  if (rootElement && rootElement->IsEditable()) {
   7416    return rootElement;
   7417  }
   7418 
   7419  // If there is no editable root element, check its <body> element.
   7420  // Note that the body element could be <frameset> element.
   7421  Element* bodyElement = aDocument->GetBody();
   7422  if (bodyElement && bodyElement->IsEditable()) {
   7423    return bodyElement;
   7424  }
   7425  return nullptr;
   7426 }
   7427 
   7428 #ifdef DEBUG
   7429 /* static */
   7430 void nsLayoutUtils::AssertNoDuplicateContinuations(
   7431    nsIFrame* aContainer, const nsFrameList& aFrameList) {
   7432  for (nsIFrame* f : aFrameList) {
   7433    // Check only later continuations of f; we deal with checking the
   7434    // earlier continuations when we hit those earlier continuations in
   7435    // the frame list.
   7436    for (nsIFrame* c = f; (c = c->GetNextInFlow());) {
   7437      NS_ASSERTION(c->GetParent() != aContainer || !aFrameList.ContainsFrame(c),
   7438                   "Two continuations of the same frame in the same "
   7439                   "frame list");
   7440    }
   7441  }
   7442 }
   7443 
   7444 // Is one of aFrame's ancestors a letter frame?
   7445 static bool IsInLetterFrame(nsIFrame* aFrame) {
   7446  for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
   7447    if (f->IsLetterFrame()) {
   7448      return true;
   7449    }
   7450  }
   7451  return false;
   7452 }
   7453 
   7454 /* static */
   7455 void nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(nsIFrame* aSubtreeRoot) {
   7456  NS_ASSERTION(aSubtreeRoot->GetPrevInFlow(),
   7457               "frame tree not empty, but caller reported complete status");
   7458 
   7459  // Also assert that text frames map no text.
   7460  auto [start, end] = aSubtreeRoot->GetOffsets();
   7461  // In some cases involving :first-letter, we'll partially unlink a
   7462  // continuation in the middle of a continuation chain from its
   7463  // previous and next continuations before destroying it, presumably so
   7464  // that we don't also destroy the later continuations.  Once we've
   7465  // done this, GetOffsets returns incorrect values.
   7466  // For examples, see list of tests in
   7467  // https://bugzilla.mozilla.org/show_bug.cgi?id=619021#c29
   7468  NS_ASSERTION(start == end || IsInLetterFrame(aSubtreeRoot),
   7469               "frame tree not empty, but caller reported complete status");
   7470 
   7471  for (const auto& childList : aSubtreeRoot->ChildLists()) {
   7472    for (nsIFrame* child : childList.mList) {
   7473      nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(child);
   7474    }
   7475  }
   7476 }
   7477 #endif
   7478 
   7479 static void GetFontFacesForFramesInner(
   7480    nsIFrame* aFrame, nsLayoutUtils::UsedFontFaceList& aResult,
   7481    nsLayoutUtils::UsedFontFaceTable& aFontFaces, uint32_t aMaxRanges,
   7482    bool aSkipCollapsedWhitespace) {
   7483  MOZ_ASSERT(aFrame, "NULL frame pointer");
   7484 
   7485  if (aFrame->IsTextFrame()) {
   7486    if (!aFrame->GetPrevContinuation()) {
   7487      nsLayoutUtils::GetFontFacesForText(aFrame, 0, INT32_MAX, true, aResult,
   7488                                         aFontFaces, aMaxRanges,
   7489                                         aSkipCollapsedWhitespace);
   7490    }
   7491    return;
   7492  }
   7493 
   7494  for (nsIFrame* child : aFrame->PrincipalChildList()) {
   7495    child = nsPlaceholderFrame::GetRealFrameFor(child);
   7496    GetFontFacesForFramesInner(child, aResult, aFontFaces, aMaxRanges,
   7497                               aSkipCollapsedWhitespace);
   7498  }
   7499 }
   7500 
   7501 /* static */
   7502 nsresult nsLayoutUtils::GetFontFacesForFrames(nsIFrame* aFrame,
   7503                                              UsedFontFaceList& aResult,
   7504                                              UsedFontFaceTable& aFontFaces,
   7505                                              uint32_t aMaxRanges,
   7506                                              bool aSkipCollapsedWhitespace) {
   7507  MOZ_ASSERT(aFrame, "NULL frame pointer");
   7508 
   7509  while (aFrame) {
   7510    GetFontFacesForFramesInner(aFrame, aResult, aFontFaces, aMaxRanges,
   7511                               aSkipCollapsedWhitespace);
   7512    aFrame = GetNextContinuationOrIBSplitSibling(aFrame);
   7513  }
   7514 
   7515  return NS_OK;
   7516 }
   7517 
   7518 static void AddFontsFromTextRun(gfxTextRun* aTextRun, nsTextFrame* aFrame,
   7519                                gfxSkipCharsIterator& aSkipIter,
   7520                                const gfxTextRun::Range& aRange,
   7521                                nsLayoutUtils::UsedFontFaceList& aResult,
   7522                                nsLayoutUtils::UsedFontFaceTable& aFontFaces,
   7523                                uint32_t aMaxRanges) {
   7524  nsIContent* content = aFrame->GetContent();
   7525  int32_t contentLimit =
   7526      aFrame->GetContentOffset() + aFrame->GetInFlowContentLength();
   7527  for (gfxTextRun::GlyphRunIterator glyphRuns(aTextRun, aRange);
   7528       !glyphRuns.AtEnd(); glyphRuns.NextRun()) {
   7529    gfxFontEntry* fe = glyphRuns.GlyphRun()->mFont->GetFontEntry();
   7530    // if we have already listed this face, just make sure the match type is
   7531    // recorded
   7532    InspectorFontFace* fontFace = aFontFaces.Get(fe);
   7533    if (fontFace) {
   7534      fontFace->AddMatchType(glyphRuns.GlyphRun()->mMatchType);
   7535    } else {
   7536      // A new font entry we haven't seen before
   7537      fontFace = new InspectorFontFace(fe, aTextRun->GetFontGroup(),
   7538                                       glyphRuns.GlyphRun()->mMatchType);
   7539      aFontFaces.InsertOrUpdate(fe, fontFace);
   7540      aResult.AppendElement(fontFace);
   7541    }
   7542 
   7543    // Add this glyph run to the fontFace's list of ranges, unless we have
   7544    // already collected as many as wanted.
   7545    if (fontFace->RangeCount() < aMaxRanges) {
   7546      int32_t start =
   7547          aSkipIter.ConvertSkippedToOriginal(glyphRuns.StringStart());
   7548      int32_t end = aSkipIter.ConvertSkippedToOriginal(glyphRuns.StringEnd());
   7549 
   7550      // Mapping back from textrun offsets ("skipped" offsets that reflect the
   7551      // text after whitespace collapsing, etc) to DOM content offsets in the
   7552      // original text is ambiguous, because many original characters can
   7553      // map to a single skipped offset. aSkipIter.ConvertSkippedToOriginal()
   7554      // will return an "original" offset that corresponds to the *end* of
   7555      // a collapsed run of characters in this case; but that might extend
   7556      // beyond the current content node if the textrun mapped multiple nodes.
   7557      // So we clamp the end offset to keep it valid for the content node
   7558      // that corresponds to the current textframe.
   7559      end = std::min(end, contentLimit);
   7560 
   7561      if (end > start) {
   7562        RefPtr<nsRange> range =
   7563            nsRange::Create(content, start, content, end, IgnoreErrors());
   7564        NS_WARNING_ASSERTION(range,
   7565                             "nsRange::Create() failed to create valid range");
   7566        if (range) {
   7567          fontFace->AddRange(range);
   7568        }
   7569      }
   7570    }
   7571  }
   7572 }
   7573 
   7574 /* static */
   7575 void nsLayoutUtils::GetFontFacesForText(nsIFrame* aFrame, int32_t aStartOffset,
   7576                                        int32_t aEndOffset,
   7577                                        bool aFollowContinuations,
   7578                                        UsedFontFaceList& aResult,
   7579                                        UsedFontFaceTable& aFontFaces,
   7580                                        uint32_t aMaxRanges,
   7581                                        bool aSkipCollapsedWhitespace) {
   7582  MOZ_ASSERT(aFrame, "NULL frame pointer");
   7583 
   7584  if (!aFrame->IsTextFrame()) {
   7585    return;
   7586  }
   7587 
   7588  if (!aFrame->StyleVisibility()->IsVisible()) {
   7589    return;
   7590  }
   7591 
   7592  nsTextFrame* curr = static_cast<nsTextFrame*>(aFrame);
   7593  do {
   7594    int32_t fstart = std::max(curr->GetContentOffset(), aStartOffset);
   7595    int32_t fend = std::min(curr->GetContentEnd(), aEndOffset);
   7596    if (fstart >= fend) {
   7597      curr = static_cast<nsTextFrame*>(curr->GetNextContinuation());
   7598      continue;
   7599    }
   7600 
   7601    // curr is overlapping with the offset we want
   7602    gfxSkipCharsIterator iter = curr->EnsureTextRun(nsTextFrame::eInflated);
   7603    gfxTextRun* textRun = curr->GetTextRun(nsTextFrame::eInflated);
   7604    if (!textRun) {
   7605      NS_WARNING("failed to get textRun, low memory?");
   7606      return;
   7607    }
   7608 
   7609    // include continuations in the range that share the same textrun
   7610    nsTextFrame* next = nullptr;
   7611    if (aFollowContinuations && fend < aEndOffset) {
   7612      next = static_cast<nsTextFrame*>(curr->GetNextContinuation());
   7613      while (next && next->GetTextRun(nsTextFrame::eInflated) == textRun) {
   7614        fend = std::min(next->GetContentEnd(), aEndOffset);
   7615        next = fend < aEndOffset
   7616                   ? static_cast<nsTextFrame*>(next->GetNextContinuation())
   7617                   : nullptr;
   7618      }
   7619    }
   7620 
   7621    if (!aSkipCollapsedWhitespace || (curr->HasAnyNoncollapsedCharacters() &&
   7622                                      curr->HasNonSuppressedText())) {
   7623      gfxTextRun::Range range(iter.ConvertOriginalToSkipped(fstart),
   7624                              iter.ConvertOriginalToSkipped(fend));
   7625      AddFontsFromTextRun(textRun, curr, iter, range, aResult, aFontFaces,
   7626                          aMaxRanges);
   7627    }
   7628 
   7629    curr = next;
   7630  } while (aFollowContinuations && curr);
   7631 }
   7632 
   7633 /* static */
   7634 size_t nsLayoutUtils::SizeOfTextRunsForFrames(nsIFrame* aFrame,
   7635                                              MallocSizeOf aMallocSizeOf,
   7636                                              bool clear) {
   7637  MOZ_ASSERT(aFrame, "NULL frame pointer");
   7638 
   7639  size_t total = 0;
   7640 
   7641  if (aFrame->IsTextFrame()) {
   7642    nsTextFrame* textFrame = static_cast<nsTextFrame*>(aFrame);
   7643    for (uint32_t i = 0; i < 2; ++i) {
   7644      gfxTextRun* run = textFrame->GetTextRun(
   7645          (i != 0) ? nsTextFrame::eInflated : nsTextFrame::eNotInflated);
   7646      if (run) {
   7647        if (clear) {
   7648          run->ResetSizeOfAccountingFlags();
   7649        } else {
   7650          total += run->MaybeSizeOfIncludingThis(aMallocSizeOf);
   7651        }
   7652      }
   7653    }
   7654    return total;
   7655  }
   7656 
   7657  for (const auto& childList : aFrame->ChildLists()) {
   7658    for (nsIFrame* f : childList.mList) {
   7659      total += SizeOfTextRunsForFrames(f, aMallocSizeOf, clear);
   7660    }
   7661  }
   7662  return total;
   7663 }
   7664 
   7665 /* static */
   7666 void nsLayoutUtils::RecomputeSmoothScrollDefault() {
   7667  // We want prefers-reduced-motion to determine the default
   7668  // value of the general.smoothScroll pref. If the user
   7669  // changed the pref we want to respect the change.
   7670  Preferences::SetBool(
   7671      StaticPrefs::GetPrefName_general_smoothScroll(),
   7672      !LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedMotion, 0),
   7673      PrefValueKind::Default);
   7674 }
   7675 
   7676 /* static */
   7677 bool nsLayoutUtils::IsSmoothScrollingEnabled() {
   7678  if (nsContentUtils::ShouldResistFingerprinting(
   7679          "We use the global RFP pref to maintain consistent scroll behavior "
   7680          "in the browser.",
   7681          RFPTarget::CSSPrefersReducedMotion)) {
   7682    return true;
   7683  }
   7684  return StaticPrefs::general_smoothScroll();
   7685 }
   7686 
   7687 /* static */
   7688 void nsLayoutUtils::Initialize() {
   7689  nsComputedDOMStyle::RegisterPrefChangeCallbacks();
   7690 }
   7691 
   7692 /* static */
   7693 void nsLayoutUtils::Shutdown() {
   7694  if (sContentMap) {
   7695    sContentMap = nullptr;
   7696  }
   7697 
   7698  nsComputedDOMStyle::UnregisterPrefChangeCallbacks();
   7699 }
   7700 
   7701 /* static */
   7702 void nsLayoutUtils::RegisterImageRequest(nsPresContext* aPresContext,
   7703                                         imgIRequest* aRequest,
   7704                                         bool* aRequestRegistered) {
   7705  if (!aPresContext) {
   7706    return;
   7707  }
   7708 
   7709  if (aRequestRegistered && *aRequestRegistered) {
   7710    // Our request is already registered with the refresh driver, so
   7711    // no need to register it again.
   7712    return;
   7713  }
   7714 
   7715  if (aRequest) {
   7716    aPresContext->RefreshDriver()->AddImageRequest(aRequest);
   7717    if (aRequestRegistered) {
   7718      *aRequestRegistered = true;
   7719    }
   7720  }
   7721 }
   7722 
   7723 /* static */
   7724 void nsLayoutUtils::RegisterImageRequestIfAnimated(nsPresContext* aPresContext,
   7725                                                   imgIRequest* aRequest,
   7726                                                   bool* aRequestRegistered) {
   7727  if (!aPresContext) {
   7728    return;
   7729  }
   7730 
   7731  if (aRequestRegistered && *aRequestRegistered) {
   7732    // Our request is already registered with the refresh driver, so
   7733    // no need to register it again.
   7734    return;
   7735  }
   7736 
   7737  if (aRequest) {
   7738    nsCOMPtr<imgIContainer> image;
   7739    if (NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))) {
   7740      // Check to verify that the image is animated. If so, then add it to the
   7741      // list of images tracked by the refresh driver.
   7742      bool isAnimated = false;
   7743      nsresult rv = image->GetAnimated(&isAnimated);
   7744      if (NS_SUCCEEDED(rv) && isAnimated) {
   7745        aPresContext->RefreshDriver()->AddImageRequest(aRequest);
   7746        if (aRequestRegistered) {
   7747          *aRequestRegistered = true;
   7748        }
   7749      }
   7750    }
   7751  }
   7752 }
   7753 
   7754 /* static */
   7755 void nsLayoutUtils::DeregisterImageRequest(nsPresContext* aPresContext,
   7756                                           imgIRequest* aRequest,
   7757                                           bool* aRequestRegistered) {
   7758  if (!aPresContext) {
   7759    return;
   7760  }
   7761 
   7762  // Deregister our imgIRequest with the refresh driver to
   7763  // complete tear-down, but only if it has been registered
   7764  if (aRequestRegistered && !*aRequestRegistered) {
   7765    return;
   7766  }
   7767 
   7768  if (aRequest) {
   7769    nsCOMPtr<imgIContainer> image;
   7770    if (NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))) {
   7771      aPresContext->RefreshDriver()->RemoveImageRequest(aRequest);
   7772 
   7773      if (aRequestRegistered) {
   7774        *aRequestRegistered = false;
   7775      }
   7776    }
   7777  }
   7778 }
   7779 
   7780 /* static */
   7781 void nsLayoutUtils::PostRestyleEvent(Element* aElement,
   7782                                     RestyleHint aRestyleHint,
   7783                                     nsChangeHint aMinChangeHint) {
   7784  if (Document* doc = aElement->GetComposedDoc()) {
   7785    if (nsPresContext* presContext = doc->GetPresContext()) {
   7786      presContext->RestyleManager()->PostRestyleEvent(aElement, aRestyleHint,
   7787                                                      aMinChangeHint);
   7788    }
   7789  }
   7790 }
   7791 
   7792 nsSetAttrRunnable::nsSetAttrRunnable(Element* aElement, nsAtom* aAttrName,
   7793                                     const nsAString& aValue)
   7794    : mozilla::Runnable("nsSetAttrRunnable"),
   7795      mElement(aElement),
   7796      mAttrName(aAttrName),
   7797      mValue(aValue) {
   7798  NS_ASSERTION(aElement && aAttrName, "Missing stuff, prepare to crash");
   7799 }
   7800 
   7801 nsSetAttrRunnable::nsSetAttrRunnable(Element* aElement, nsAtom* aAttrName,
   7802                                     int32_t aValue)
   7803    : mozilla::Runnable("nsSetAttrRunnable"),
   7804      mElement(aElement),
   7805      mAttrName(aAttrName) {
   7806  NS_ASSERTION(aElement && aAttrName, "Missing stuff, prepare to crash");
   7807  mValue.AppendInt(aValue);
   7808 }
   7809 
   7810 NS_IMETHODIMP
   7811 nsSetAttrRunnable::Run() {
   7812  return mElement->SetAttr(kNameSpaceID_None, mAttrName, mValue, true);
   7813 }
   7814 
   7815 nsUnsetAttrRunnable::nsUnsetAttrRunnable(Element* aElement, nsAtom* aAttrName)
   7816    : mozilla::Runnable("nsUnsetAttrRunnable"),
   7817      mElement(aElement),
   7818      mAttrName(aAttrName) {
   7819  NS_ASSERTION(aElement && aAttrName, "Missing stuff, prepare to crash");
   7820 }
   7821 
   7822 NS_IMETHODIMP
   7823 nsUnsetAttrRunnable::Run() {
   7824  return mElement->UnsetAttr(kNameSpaceID_None, mAttrName, true);
   7825 }
   7826 
   7827 /**
   7828 * Compute the minimum font size inside of a container with the given
   7829 * width, such that **when the user zooms the container to fill the full
   7830 * width of the device**, the fonts satisfy our minima.
   7831 */
   7832 static nscoord MinimumFontSizeFor(nsPresContext* aPresContext,
   7833                                  WritingMode aWritingMode,
   7834                                  nscoord aContainerISize) {
   7835  PresShell* presShell = aPresContext->PresShell();
   7836 
   7837  uint32_t emPerLine = presShell->FontSizeInflationEmPerLine();
   7838  uint32_t minTwips = presShell->FontSizeInflationMinTwips();
   7839  if (emPerLine == 0 && minTwips == 0) {
   7840    return 0;
   7841  }
   7842 
   7843  nscoord byLine = 0, byInch = 0;
   7844  if (emPerLine != 0) {
   7845    byLine = aContainerISize / emPerLine;
   7846  }
   7847  if (minTwips != 0) {
   7848    // REVIEW: Is this giving us app units and sizes *not* counting
   7849    // viewport scaling?
   7850    gfxSize screenSize = aPresContext->ScreenSizeInchesForFontInflation();
   7851    float deviceISizeInches =
   7852        aWritingMode.IsVertical() ? screenSize.height : screenSize.width;
   7853    byInch =
   7854        NSToCoordRound(aContainerISize / (deviceISizeInches * 1440 / minTwips));
   7855  }
   7856  return std::max(byLine, byInch);
   7857 }
   7858 
   7859 /* static */
   7860 float nsLayoutUtils::FontSizeInflationInner(const nsIFrame* aFrame,
   7861                                            nscoord aMinFontSize) {
   7862  // Note that line heights should be inflated by the same ratio as the
   7863  // font size of the same text; thus we operate only on the font size
   7864  // even when we're scaling a line height.
   7865  nscoord styleFontSize = aFrame->StyleFont()->mFont.size.ToAppUnits();
   7866  if (styleFontSize <= 0) {
   7867    // Never scale zero font size.
   7868    return 1.0;
   7869  }
   7870 
   7871  if (aMinFontSize <= 0) {
   7872    // No need to scale.
   7873    return 1.0;
   7874  }
   7875 
   7876  // If between this current frame and its font inflation container there is a
   7877  // non-inline element with fixed width or height, then we should not inflate
   7878  // fonts for this frame.
   7879  for (const nsIFrame* f = aFrame; f && !f->IsContainerForFontSizeInflation();
   7880       f = f->GetParent()) {
   7881    nsIContent* content = f->GetContent();
   7882    LayoutFrameType fType = f->Type();
   7883    nsIFrame* parent = f->GetParent();
   7884    // Also, if there is more than one frame corresponding to a single
   7885    // content node, we want the outermost one.
   7886    if (!(parent && parent->GetContent() == content) &&
   7887        // ignore width/height on inlines since they don't apply
   7888        fType != LayoutFrameType::Inline &&
   7889        // ignore width on radios and checkboxes since we enlarge them and
   7890        // they have width/height in ua.css
   7891        fType != LayoutFrameType::CheckboxRadio) {
   7892      // ruby annotations should have the same inflation as its
   7893      // grandparent, which is the ruby frame contains the annotation.
   7894      if (fType == LayoutFrameType::RubyText) {
   7895        MOZ_ASSERT(parent && parent->IsRubyTextContainerFrame());
   7896        nsIFrame* grandparent = parent->GetParent();
   7897        MOZ_ASSERT(grandparent && grandparent->IsRubyFrame());
   7898        return FontSizeInflationFor(grandparent);
   7899      }
   7900      WritingMode wm = f->GetWritingMode();
   7901      const auto anchorResolutionParams = AnchorPosResolutionParams::From(f);
   7902      const auto stylePosISize =
   7903          f->StylePosition()->ISize(wm, anchorResolutionParams);
   7904      const auto stylePosBSize =
   7905          f->StylePosition()->BSize(wm, anchorResolutionParams);
   7906      if (!stylePosISize->IsAuto() ||
   7907          !stylePosBSize->BehavesLikeInitialValueOnBlockAxis()) {
   7908        return 1.0;
   7909      }
   7910    }
   7911  }
   7912 
   7913  int32_t interceptParam = StaticPrefs::font_size_inflation_mappingIntercept();
   7914  float maxRatio = (float)StaticPrefs::font_size_inflation_maxRatio() / 100.0f;
   7915 
   7916  float ratio = float(styleFontSize) / float(aMinFontSize);
   7917  float inflationRatio;
   7918 
   7919  // Given a minimum inflated font size m, a specified font size s, we want to
   7920  // find the inflated font size i and then return the ratio of i to s (i/s).
   7921  if (interceptParam >= 0) {
   7922    // Since the mapping intercept parameter P is greater than zero, we use it
   7923    // to determine the point where our mapping function intersects the i=s
   7924    // line. This means that we have an equation of the form:
   7925    //
   7926    // i = m + s*(P/2)/(1 + P/2), if s <= (1 + P/2)*m
   7927    // i = s, if s >= (1 + P/2)*m
   7928 
   7929    float intercept = 1 + float(interceptParam) / 2.0f;
   7930    if (ratio >= intercept) {
   7931      // If we're already at 1+P/2 or more times the minimum, don't scale.
   7932      return 1.0;
   7933    }
   7934 
   7935    // The point (intercept, intercept) is where the part of the i vs. s graph
   7936    // that's not slope 1 meets the i=s line.  (This part of the
   7937    // graph is a line from (0, m), to that point). We calculate the
   7938    // intersection point to be ((1+P/2)m, (1+P/2)m), where P is the
   7939    // intercept parameter above. We then need to return i/s.
   7940    inflationRatio = (1.0f + (ratio * (intercept - 1) / intercept)) / ratio;
   7941  } else {
   7942    // This is the case where P is negative. We essentially want to implement
   7943    // the case for P=infinity here, so we make i = s + m, which means that
   7944    // i/s = s/s + m/s = 1 + 1/ratio
   7945    inflationRatio = 1 + 1.0f / ratio;
   7946  }
   7947 
   7948  if (maxRatio > 1.0 && inflationRatio > maxRatio) {
   7949    return maxRatio;
   7950  } else {
   7951    return inflationRatio;
   7952  }
   7953 }
   7954 
   7955 static bool ShouldInflateFontsForContainer(const nsIFrame* aFrame) {
   7956  // We only want to inflate fonts for text that is in a place
   7957  // with room to expand.  The question is what the best heuristic for
   7958  // that is...
   7959  // For now, we're going to use NS_FRAME_IN_CONSTRAINED_BSIZE, which
   7960  // indicates whether the frame is inside something with a constrained
   7961  // block-size (propagating down the tree), but the propagation stops when
   7962  // we hit overflow-y [or -x, for vertical mode]: scroll or auto.
   7963  const nsStyleText* styleText = aFrame->StyleText();
   7964 
   7965  return styleText->mTextSizeAdjust != StyleTextSizeAdjust::None &&
   7966         !aFrame->HasAnyStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE) &&
   7967         // We also want to disable font inflation for containers that have
   7968         // preformatted text.
   7969         // MathML cells need special treatment. See bug 1002526 comment 56.
   7970         (styleText->WhiteSpaceCanWrap(aFrame) || aFrame->IsMathMLFrame());
   7971 }
   7972 
   7973 nscoord nsLayoutUtils::InflationMinFontSizeFor(const nsIFrame* aFrame) {
   7974  nsPresContext* presContext = aFrame->PresContext();
   7975  if (!FontSizeInflationEnabled(presContext) ||
   7976      presContext->mInflationDisabledForShrinkWrap) {
   7977    return 0;
   7978  }
   7979 
   7980  for (const nsIFrame* f = aFrame; f; f = f->GetParent()) {
   7981    if (f->IsContainerForFontSizeInflation()) {
   7982      if (!ShouldInflateFontsForContainer(f)) {
   7983        return 0;
   7984      }
   7985 
   7986      nsFontInflationData* data =
   7987          nsFontInflationData::FindFontInflationDataFor(aFrame);
   7988      // FIXME: The need to null-check here is sort of a bug, and might
   7989      // lead to incorrect results.
   7990      if (!data || !data->InflationEnabled()) {
   7991        return 0;
   7992      }
   7993 
   7994      return MinimumFontSizeFor(aFrame->PresContext(), aFrame->GetWritingMode(),
   7995                                data->UsableISize());
   7996    }
   7997  }
   7998 
   7999  MOZ_ASSERT(false, "root should always be container");
   8000 
   8001  return 0;
   8002 }
   8003 
   8004 float nsLayoutUtils::FontSizeInflationFor(const nsIFrame* aFrame) {
   8005  if (aFrame->IsInSVGTextSubtree()) {
   8006    const nsIFrame* container = aFrame;
   8007    while (!container->IsSVGTextFrame()) {
   8008      container = container->GetParent();
   8009    }
   8010    NS_ASSERTION(container, "expected to find an ancestor SVGTextFrame");
   8011    return static_cast<const SVGTextFrame*>(container)
   8012        ->GetFontSizeScaleFactor();
   8013  }
   8014 
   8015  if (!FontSizeInflationEnabled(aFrame->PresContext())) {
   8016    return 1.0f;
   8017  }
   8018 
   8019  return FontSizeInflationInner(aFrame, InflationMinFontSizeFor(aFrame));
   8020 }
   8021 
   8022 /* static */
   8023 bool nsLayoutUtils::FontSizeInflationEnabled(nsPresContext* aPresContext) {
   8024  PresShell* presShell = aPresContext->GetPresShell();
   8025  if (!presShell) {
   8026    return false;
   8027  }
   8028  return presShell->FontSizeInflationEnabled();
   8029 }
   8030 
   8031 /* static */
   8032 nsRect nsLayoutUtils::GetBoxShadowRectForFrame(nsIFrame* aFrame,
   8033                                               const nsSize& aFrameSize) {
   8034  auto boxShadows = aFrame->StyleEffects()->mBoxShadow.AsSpan();
   8035  if (boxShadows.IsEmpty()) {
   8036    return nsRect();
   8037  }
   8038 
   8039  nsRect inputRect(nsPoint(0, 0), aFrameSize);
   8040 
   8041  // According to the CSS spec, box-shadow should be based on the border box.
   8042  // However, that looks broken when the background extends outside the border
   8043  // box, as can be the case with native theming.  To fix that we expand the
   8044  // area that we shadow to include the bounds of any native theme drawing.
   8045  const nsStyleDisplay* styleDisplay = aFrame->StyleDisplay();
   8046  nsITheme::Transparency transparency;
   8047  if (aFrame->IsThemed(styleDisplay, &transparency)) {
   8048    // For opaque (rectangular) theme widgets we can take the generic
   8049    // border-box path with border-radius disabled.
   8050    if (transparency != nsITheme::eOpaque) {
   8051      nsPresContext* presContext = aFrame->PresContext();
   8052      presContext->Theme()->GetWidgetOverflow(
   8053          presContext->DeviceContext(), aFrame,
   8054          styleDisplay->EffectiveAppearance(), &inputRect);
   8055    }
   8056  }
   8057 
   8058  nsRect shadows;
   8059  int32_t A2D = aFrame->PresContext()->AppUnitsPerDevPixel();
   8060  for (auto& shadow : boxShadows) {
   8061    nsRect tmpRect = inputRect;
   8062 
   8063    // inset shadows are never painted outside the frame
   8064    if (shadow.inset) {
   8065      continue;
   8066    }
   8067 
   8068    tmpRect.MoveBy(nsPoint(shadow.base.horizontal.ToAppUnits(),
   8069                           shadow.base.vertical.ToAppUnits()));
   8070    tmpRect.Inflate(shadow.spread.ToAppUnits());
   8071    tmpRect.Inflate(nsContextBoxBlur::GetBlurRadiusMargin(
   8072        shadow.base.blur.ToAppUnits(), A2D));
   8073    shadows.UnionRect(shadows, tmpRect);
   8074  }
   8075  return shadows;
   8076 }
   8077 
   8078 /* static */
   8079 bool nsLayoutUtils::GetDocumentViewerSize(
   8080    const nsPresContext* aPresContext, LayoutDeviceIntSize& aOutSize,
   8081    SubtractDynamicToolbar aSubtractDynamicToolbar) {
   8082  nsCOMPtr<nsIDocShell> docShell = aPresContext->GetDocShell();
   8083  if (!docShell) {
   8084    return false;
   8085  }
   8086 
   8087  nsCOMPtr<nsIDocumentViewer> viewer;
   8088  docShell->GetDocViewer(getter_AddRefs(viewer));
   8089  if (!viewer) {
   8090    return false;
   8091  }
   8092 
   8093  LayoutDeviceIntRect bounds;
   8094  viewer->GetBounds(bounds);
   8095 
   8096  if (aPresContext->IsRootContentDocumentCrossProcess() &&
   8097      aSubtractDynamicToolbar == SubtractDynamicToolbar::Yes &&
   8098      aPresContext->HasDynamicToolbar() && !bounds.IsEmpty()) {
   8099    MOZ_ASSERT(aPresContext->IsRootContentDocumentCrossProcess());
   8100    bounds.height -= aPresContext->GetDynamicToolbarMaxHeight();
   8101    // Collapse the size in the case the dynamic toolbar max height is greater
   8102    // than the content bound height so that hopefully embedders of GeckoView
   8103    // may notice they set wrong dynamic toolbar max height.
   8104    if (bounds.height < 0) {
   8105      bounds.height = 0;
   8106    }
   8107  }
   8108 
   8109  aOutSize = bounds.Size();
   8110  return true;
   8111 }
   8112 
   8113 bool nsLayoutUtils::UpdateCompositionBoundsForRCDRSF(
   8114    ParentLayerRect& aCompBounds, const nsPresContext* aPresContext,
   8115    IncludeDynamicToolbar aIncludeDynamicToolbar) {
   8116  SubtractDynamicToolbar shouldSubtractDynamicToolbar =
   8117      aIncludeDynamicToolbar == IncludeDynamicToolbar::Force
   8118          ? SubtractDynamicToolbar::No
   8119      : aPresContext->IsRootContentDocumentCrossProcess() &&
   8120              aPresContext->HasDynamicToolbar()
   8121          ? SubtractDynamicToolbar::Yes
   8122          : SubtractDynamicToolbar::No;
   8123 
   8124  const bool isKeyboardVisibleOnOverlaysContent =
   8125      aPresContext->GetKeyboardHeight() &&
   8126      aPresContext->Document()->InteractiveWidget() ==
   8127          InteractiveWidget::OverlaysContent;
   8128  if (shouldSubtractDynamicToolbar == SubtractDynamicToolbar::Yes &&
   8129      // In `overlays-content` mode with the software keyboard visible, avoid
   8130      // flipping `shouldSubtractDynamicToolbar` below. We want to exclude
   8131      // the dynamic toolbar height from the visual viewport (composition
   8132      // bounds) height in this case to be consistent with the handling of the
   8133      // layout viewport height in ExpandHeightForDynamicToolbar(). Otherwise,
   8134      // the visual viewport will be taller than the layout viewport which can
   8135      // lead to rendering problems.
   8136      !isKeyboardVisibleOnOverlaysContent) {
   8137    if (RefPtr<MobileViewportManager> MVM =
   8138            aPresContext->PresShell()->GetMobileViewportManager()) {
   8139      // Convert the intrinsic composition size to app units here since
   8140      // the returned size of below CalculateScrollableRectForFrame call has
   8141      // been already converted/rounded to app units.
   8142      nsSize intrinsicCompositionSize =
   8143          CSSSize::ToAppUnits(MVM->GetIntrinsicCompositionSize());
   8144 
   8145      if (ScrollContainerFrame* rootScrollContainerFrame =
   8146              aPresContext->PresShell()->GetRootScrollContainerFrame()) {
   8147        // Expand the composition size to include the area initially covered by
   8148        // the dynamic toolbar only if the content is taller than the intrinsic
   8149        // composition size (i.e. the dynamic toolbar should be able to move
   8150        // only if the content is vertically scrollable).
   8151        if (intrinsicCompositionSize.height <
   8152            CalculateScrollableRectForFrame(rootScrollContainerFrame, nullptr)
   8153                .Height()) {
   8154          shouldSubtractDynamicToolbar = SubtractDynamicToolbar::No;
   8155        }
   8156      }
   8157    }
   8158  }
   8159 
   8160  LayoutDeviceIntSize contentSize;
   8161  if (!GetDocumentViewerSize(aPresContext, contentSize,
   8162                             shouldSubtractDynamicToolbar)) {
   8163    return false;
   8164  }
   8165 
   8166  // Add the keyboard height in the case of
   8167  // `interactive-widget=overlays-content` so that contents being overlaid by
   8168  // the keyboard can NOT be reachable by scrolling.
   8169  if (isKeyboardVisibleOnOverlaysContent) {
   8170    contentSize.height += ViewAs<LayoutDevicePixel>(
   8171        aPresContext->GetKeyboardHeight(),
   8172        PixelCastJustification::LayoutDeviceIsScreenForBounds);
   8173  }
   8174  aCompBounds.SizeTo(ViewAs<ParentLayerPixel>(
   8175      LayoutDeviceSize(contentSize),
   8176      PixelCastJustification::LayoutDeviceIsParentLayerForRCDRSF));
   8177  return true;
   8178 }
   8179 
   8180 /* static */
   8181 nsMargin nsLayoutUtils::ScrollbarAreaToExcludeFromCompositionBoundsFor(
   8182    const nsIFrame* aScrollFrame) {
   8183  if (!aScrollFrame || !aScrollFrame->GetScrollTargetFrame()) {
   8184    return nsMargin();
   8185  }
   8186  nsPresContext* presContext = aScrollFrame->PresContext();
   8187  PresShell* presShell = presContext->GetPresShell();
   8188  if (!presShell) {
   8189    return nsMargin();
   8190  }
   8191  bool isRootScrollContainerFrame =
   8192      aScrollFrame == presShell->GetRootScrollContainerFrame();
   8193  bool isRootContentDocRootScrollFrame =
   8194      isRootScrollContainerFrame &&
   8195      presContext->IsRootContentDocumentCrossProcess();
   8196  if (!isRootContentDocRootScrollFrame) {
   8197    return nsMargin();
   8198  }
   8199  if (presContext->UseOverlayScrollbars()) {
   8200    return nsMargin();
   8201  }
   8202  ScrollContainerFrame* scrollContainerFrame =
   8203      aScrollFrame->GetScrollTargetFrame();
   8204  if (!scrollContainerFrame) {
   8205    return nsMargin();
   8206  }
   8207  return scrollContainerFrame->GetActualScrollbarSizes(
   8208      ScrollContainerFrame::ScrollbarSizesOptions::
   8209          INCLUDE_VISUAL_VIEWPORT_SCROLLBARS);
   8210 }
   8211 
   8212 /* static */
   8213 nsSize nsLayoutUtils::CalculateCompositionSizeForFrame(
   8214    nsIFrame* aFrame, bool aSubtractScrollbars,
   8215    const nsSize* aOverrideScrollPortSize,
   8216    IncludeDynamicToolbar aIncludeDynamicToolbar) {
   8217  // If we have a scroll container frame, restrict the composition bounds to its
   8218  // scroll port. The scroll port excludes the frame borders and the scroll
   8219  // bars, which we don't want to be part of the composition bounds.
   8220  ScrollContainerFrame* scrollContainerFrame = aFrame->GetScrollTargetFrame();
   8221  nsRect rect = scrollContainerFrame ? scrollContainerFrame->GetScrollPortRect()
   8222                                     : aFrame->GetRect();
   8223  nsSize size =
   8224      aOverrideScrollPortSize ? *aOverrideScrollPortSize : rect.Size();
   8225 
   8226  nsPresContext* presContext = aFrame->PresContext();
   8227  PresShell* presShell = presContext->PresShell();
   8228 
   8229  bool isRootContentDocRootScrollFrame =
   8230      presContext->IsRootContentDocumentCrossProcess() &&
   8231      aFrame == presShell->GetRootScrollContainerFrame();
   8232  if (isRootContentDocRootScrollFrame) {
   8233    ParentLayerRect compBounds;
   8234    if (UpdateCompositionBoundsForRCDRSF(compBounds, presContext,
   8235                                         aIncludeDynamicToolbar)) {
   8236      int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
   8237      size = nsSize(compBounds.width * auPerDevPixel,
   8238                    compBounds.height * auPerDevPixel);
   8239    }
   8240  }
   8241 
   8242  if (aSubtractScrollbars) {
   8243    nsMargin margins = ScrollbarAreaToExcludeFromCompositionBoundsFor(aFrame);
   8244    size.width -= margins.LeftRight();
   8245    size.height -= margins.TopBottom();
   8246  }
   8247 
   8248  return size;
   8249 }
   8250 
   8251 /* static */
   8252 CSSSize nsLayoutUtils::CalculateBoundingCompositionSize(
   8253    const nsIFrame* aFrame, bool aIsRootContentDocRootScrollFrame,
   8254    const FrameMetrics& aMetrics) {
   8255  if (aIsRootContentDocRootScrollFrame) {
   8256    return ViewAs<LayerPixel>(
   8257               aMetrics.GetCompositionBounds().Size(),
   8258               PixelCastJustification::ParentLayerToLayerForRootComposition) *
   8259           LayerToScreenScale(1.0f) / aMetrics.DisplayportPixelsPerCSSPixel();
   8260  }
   8261  nsPresContext* presContext = aFrame->PresContext();
   8262  ScreenSize rootCompositionSize;
   8263  nsPresContext* rootPresContext =
   8264      presContext->GetInProcessRootContentDocumentPresContext();
   8265  if (!rootPresContext) {
   8266    rootPresContext = presContext->GetRootPresContext();
   8267  }
   8268 
   8269  const bool isPopupRoot = aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP);
   8270  PresShell* rootPresShell = nullptr;
   8271  if (rootPresContext && !isPopupRoot) {
   8272    rootPresShell = rootPresContext->PresShell();
   8273    if (nsIFrame* rootFrame = rootPresShell->GetRootFrame()) {
   8274      ParentLayerRect compBounds;
   8275      if (UpdateCompositionBoundsForRCDRSF(compBounds, rootPresContext)) {
   8276        rootCompositionSize = ViewAs<ScreenPixel>(
   8277            compBounds.Size(),
   8278            PixelCastJustification::ScreenIsParentLayerForRoot);
   8279      } else {
   8280        // LayoutDeviceToScreenScale2D =
   8281        //   LayoutDeviceToParentLayerScale *
   8282        //   ParentLayerToScreenScale2D
   8283        LayoutDeviceToScreenScale2D cumulativeResolution =
   8284            LayoutDeviceToParentLayerScale(
   8285                rootPresShell->GetCumulativeResolution()) *
   8286            GetTransformToAncestorScaleCrossProcessForFrameMetrics(rootFrame);
   8287 
   8288        int32_t rootAUPerDevPixel = rootPresContext->AppUnitsPerDevPixel();
   8289        rootCompositionSize = (LayoutDeviceRect::FromAppUnits(
   8290                                   rootFrame->GetRect(), rootAUPerDevPixel) *
   8291                               cumulativeResolution)
   8292                                  .Size();
   8293      }
   8294    }
   8295  } else {
   8296    nsIWidget* widget = aFrame->GetNearestWidget();
   8297    LayoutDeviceIntRect widgetBounds = widget->GetBounds();
   8298    rootCompositionSize = ScreenSize(ViewAs<ScreenPixel>(
   8299        widgetBounds.Size(),
   8300        PixelCastJustification::LayoutDeviceIsScreenForBounds));
   8301  }
   8302 
   8303  // Adjust composition size for the size of scroll bars.
   8304  nsIFrame* rootRootScrollContainerFrame =
   8305      rootPresShell && !isPopupRoot
   8306          ? rootPresShell->GetRootScrollContainerFrame()
   8307          : nullptr;
   8308  nsMargin scrollbarMargins = ScrollbarAreaToExcludeFromCompositionBoundsFor(
   8309      rootRootScrollContainerFrame);
   8310  LayoutDeviceMargin margins = LayoutDeviceMargin::FromAppUnits(
   8311      scrollbarMargins, rootPresContext->AppUnitsPerDevPixel());
   8312  // Scrollbars are not subject to resolution scaling, so LD pixels = layer
   8313  // pixels for them.
   8314  rootCompositionSize.width -= margins.LeftRight();
   8315  rootCompositionSize.height -= margins.TopBottom();
   8316 
   8317  CSSSize result =
   8318      rootCompositionSize / aMetrics.DisplayportPixelsPerCSSPixel();
   8319 
   8320  // If this is a nested content process, the in-process root content document's
   8321  // composition size may still be arbitrarily large, so bound it further by
   8322  // how much of the in-process RCD is visible in the top-level (cross-process
   8323  // RCD) viewport.
   8324  if (rootPresShell) {
   8325    if (BrowserChild* bc = BrowserChild::GetFrom(rootPresShell)) {
   8326      if (const auto& visibleRect =
   8327              bc->GetTopLevelViewportVisibleRectInSelfCoords()) {
   8328        CSSSize cssVisibleRect =
   8329            visibleRect->Size() / rootPresContext->CSSToDevPixelScale();
   8330        result = Min(result, cssVisibleRect);
   8331      }
   8332    }
   8333  }
   8334 
   8335  return result;
   8336 }
   8337 
   8338 /* static */
   8339 nsRect nsLayoutUtils::CalculateScrollableRectForFrame(
   8340    const ScrollContainerFrame* aScrollContainerFrame,
   8341    const nsIFrame* aRootFrame) {
   8342  nsRect contentBounds;
   8343  if (aScrollContainerFrame) {
   8344    contentBounds = aScrollContainerFrame->GetScrollRange();
   8345 
   8346    nsPoint scrollPosition = aScrollContainerFrame->GetScrollPosition();
   8347    if (aScrollContainerFrame->GetScrollStyles().mVertical ==
   8348        StyleOverflow::Hidden) {
   8349      contentBounds.y = scrollPosition.y;
   8350      contentBounds.height = 0;
   8351    }
   8352    if (aScrollContainerFrame->GetScrollStyles().mHorizontal ==
   8353        StyleOverflow::Hidden) {
   8354      contentBounds.x = scrollPosition.x;
   8355      contentBounds.width = 0;
   8356    }
   8357 
   8358    contentBounds.width += aScrollContainerFrame->GetScrollPortRect().width;
   8359    contentBounds.height += aScrollContainerFrame->GetScrollPortRect().height;
   8360  } else {
   8361    contentBounds = aRootFrame->GetRect();
   8362    // Clamp to (0, 0) if there is no corresponding scrollable frame for the
   8363    // given |aRootFrame|.
   8364    contentBounds.MoveTo(0, 0);
   8365  }
   8366  return contentBounds;
   8367 }
   8368 
   8369 /* static */
   8370 nsRect nsLayoutUtils::CalculateExpandedScrollableRect(nsIFrame* aFrame) {
   8371  nsRect scrollableRect = CalculateScrollableRectForFrame(
   8372      aFrame->GetScrollTargetFrame(), aFrame->PresShell()->GetRootFrame());
   8373  nsSize compSize = CalculateCompositionSizeForFrame(aFrame);
   8374 
   8375  if (aFrame == aFrame->PresShell()->GetRootScrollContainerFrame()) {
   8376    // the composition size for the root scroll frame does not include the
   8377    // local resolution, so we adjust.
   8378    float res = aFrame->PresShell()->GetResolution();
   8379    compSize.width = NSToCoordRound(compSize.width / res);
   8380    compSize.height = NSToCoordRound(compSize.height / res);
   8381  }
   8382 
   8383  if (scrollableRect.width < compSize.width) {
   8384    scrollableRect.x =
   8385        std::max(0, scrollableRect.x - (compSize.width - scrollableRect.width));
   8386    scrollableRect.width = compSize.width;
   8387  }
   8388 
   8389  if (scrollableRect.height < compSize.height) {
   8390    scrollableRect.y = std::max(
   8391        0, scrollableRect.y - (compSize.height - scrollableRect.height));
   8392    scrollableRect.height = compSize.height;
   8393  }
   8394  return scrollableRect;
   8395 }
   8396 
   8397 /* static */
   8398 void nsLayoutUtils::DoLogTestDataForPaint(WebRenderLayerManager* aManager,
   8399                                          ViewID aScrollId,
   8400                                          const std::string& aKey,
   8401                                          const std::string& aValue) {
   8402  MOZ_ASSERT(nsLayoutUtils::IsAPZTestLoggingEnabled(), "don't call me");
   8403  aManager->LogTestDataForCurrentPaint(aScrollId, aKey, aValue);
   8404 }
   8405 
   8406 void nsLayoutUtils::LogAdditionalTestData(nsDisplayListBuilder* aBuilder,
   8407                                          const std::string& aKey,
   8408                                          const std::string& aValue) {
   8409  WebRenderLayerManager* manager = aBuilder->GetWidgetLayerManager();
   8410  if (!manager) {
   8411    return;
   8412  }
   8413  manager->LogAdditionalTestData(aKey, aValue);
   8414 }
   8415 
   8416 /* static */
   8417 bool nsLayoutUtils::IsAPZTestLoggingEnabled() {
   8418  return StaticPrefs::apz_test_logging_enabled();
   8419 }
   8420 
   8421 ////////////////////////////////////////
   8422 // SurfaceFromElementResult
   8423 
   8424 SurfaceFromElementResult::SurfaceFromElementResult()
   8425    // Use safe default values here
   8426    : mHadCrossOriginRedirects(false),
   8427      mIsWriteOnly(true),
   8428      mIsStillLoading(false),
   8429      mHasSize(false),
   8430      mCORSUsed(false),
   8431      mAlphaType(gfxAlphaType::Opaque) {}
   8432 
   8433 const RefPtr<mozilla::gfx::SourceSurface>&
   8434 SurfaceFromElementResult::GetSourceSurface() {
   8435  if (!mSourceSurface && mLayersImage) {
   8436    mSourceSurface = mLayersImage->GetAsSourceSurface();
   8437  }
   8438 
   8439  return mSourceSurface;
   8440 }
   8441 
   8442 ////////////////////////////////////////
   8443 
   8444 bool nsLayoutUtils::IsNonWrapperBlock(nsIFrame* aFrame) {
   8445  MOZ_ASSERT(aFrame);
   8446  return aFrame->IsBlockFrameOrSubclass() && !aFrame->IsBlockWrapper();
   8447 }
   8448 
   8449 AutoMaybeDisableFontInflation::AutoMaybeDisableFontInflation(nsIFrame* aFrame) {
   8450  // FIXME: Now that inflation calculations are based on the flow
   8451  // root's NCA's (nearest common ancestor of its inflatable
   8452  // descendants) width, we could probably disable inflation in
   8453  // fewer cases than we currently do.
   8454  // MathML cells need special treatment. See bug 1002526 comment 56.
   8455  if (aFrame->IsContainerForFontSizeInflation() && !aFrame->IsMathMLFrame()) {
   8456    mPresContext = aFrame->PresContext();
   8457    mOldValue = mPresContext->mInflationDisabledForShrinkWrap;
   8458    mPresContext->mInflationDisabledForShrinkWrap = true;
   8459  } else {
   8460    // indicate we have nothing to restore
   8461    mPresContext = nullptr;
   8462    mOldValue = false;
   8463  }
   8464 }
   8465 
   8466 AutoMaybeDisableFontInflation::~AutoMaybeDisableFontInflation() {
   8467  if (mPresContext) {
   8468    mPresContext->mInflationDisabledForShrinkWrap = mOldValue;
   8469  }
   8470 }
   8471 
   8472 namespace mozilla {
   8473 
   8474 Rect NSRectToRect(const nsRect& aRect, double aAppUnitsPerPixel) {
   8475  // Note that by making aAppUnitsPerPixel a double we're doing floating-point
   8476  // division using a larger type and avoiding rounding error.
   8477  return Rect(Float(aRect.x / aAppUnitsPerPixel),
   8478              Float(aRect.y / aAppUnitsPerPixel),
   8479              Float(aRect.width / aAppUnitsPerPixel),
   8480              Float(aRect.height / aAppUnitsPerPixel));
   8481 }
   8482 
   8483 Rect NSRectToSnappedRect(const nsRect& aRect, double aAppUnitsPerPixel,
   8484                         const gfx::DrawTarget& aSnapDT) {
   8485  // Note that by making aAppUnitsPerPixel a double we're doing floating-point
   8486  // division using a larger type and avoiding rounding error.
   8487  Rect rect(Float(aRect.x / aAppUnitsPerPixel),
   8488            Float(aRect.y / aAppUnitsPerPixel),
   8489            Float(aRect.width / aAppUnitsPerPixel),
   8490            Float(aRect.height / aAppUnitsPerPixel));
   8491  MaybeSnapToDevicePixels(rect, aSnapDT, true);
   8492  return rect;
   8493 }
   8494 // Similar to a snapped rect, except an axis is left unsnapped if the snapping
   8495 // process results in a length of 0.
   8496 Rect NSRectToNonEmptySnappedRect(const nsRect& aRect, double aAppUnitsPerPixel,
   8497                                 const gfx::DrawTarget& aSnapDT) {
   8498  // Note that by making aAppUnitsPerPixel a double we're doing floating-point
   8499  // division using a larger type and avoiding rounding error.
   8500  Rect rect(Float(aRect.x / aAppUnitsPerPixel),
   8501            Float(aRect.y / aAppUnitsPerPixel),
   8502            Float(aRect.width / aAppUnitsPerPixel),
   8503            Float(aRect.height / aAppUnitsPerPixel));
   8504  MaybeSnapToDevicePixels(rect, aSnapDT, true, false);
   8505  return rect;
   8506 }
   8507 
   8508 void StrokeLineWithSnapping(const nsPoint& aP1, const nsPoint& aP2,
   8509                            int32_t aAppUnitsPerDevPixel,
   8510                            DrawTarget& aDrawTarget, const Pattern& aPattern,
   8511                            const StrokeOptions& aStrokeOptions,
   8512                            const DrawOptions& aDrawOptions) {
   8513  Point p1 = NSPointToPoint(aP1, aAppUnitsPerDevPixel);
   8514  Point p2 = NSPointToPoint(aP2, aAppUnitsPerDevPixel);
   8515  SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
   8516                                    aStrokeOptions.mLineWidth);
   8517  aDrawTarget.StrokeLine(p1, p2, aPattern, aStrokeOptions, aDrawOptions);
   8518 }
   8519 
   8520 }  // namespace mozilla
   8521 
   8522 /* static */
   8523 void nsLayoutUtils::SetBSizeFromFontMetrics(const nsIFrame* aFrame,
   8524                                            ReflowOutput& aMetrics,
   8525                                            const LogicalMargin& aFramePadding,
   8526                                            WritingMode aLineWM,
   8527                                            WritingMode aFrameWM) {
   8528  RefPtr<nsFontMetrics> fm =
   8529      nsLayoutUtils::GetInflatedFontMetricsForFrame(aFrame);
   8530 
   8531  if (fm) {
   8532    // Compute final height of the frame.
   8533    //
   8534    // Do things the standard css2 way -- though it's hard to find it
   8535    // in the css2 spec! It's actually found in the css1 spec section
   8536    // 4.4 (you will have to read between the lines to really see
   8537    // it).
   8538    //
   8539    // The height of our box is the sum of our font size plus the top
   8540    // and bottom border and padding. The height of children do not
   8541    // affect our height.
   8542    aMetrics.SetBlockStartAscent(
   8543        aLineWM.IsAlphabeticalBaseline()
   8544            ? aLineWM.IsLineInverted() ? fm->MaxDescent() : fm->MaxAscent()
   8545            : fm->MaxHeight() / 2);
   8546    aMetrics.BSize(aLineWM) = fm->MaxHeight();
   8547  } else {
   8548    NS_WARNING("Cannot get font metrics - defaulting sizes to 0");
   8549    aMetrics.SetBlockStartAscent(aMetrics.BSize(aLineWM) = 0);
   8550  }
   8551  aMetrics.SetBlockStartAscent(aMetrics.BlockStartAscent() +
   8552                               aFramePadding.BStart(aFrameWM));
   8553  aMetrics.BSize(aLineWM) += aFramePadding.BStartEnd(aFrameWM);
   8554 }
   8555 
   8556 /* static */
   8557 // _BOUNDARY because Dispatch() with `targets` must not handle the event.
   8558 MOZ_CAN_RUN_SCRIPT_BOUNDARY bool
   8559 nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(
   8560    PresShell* aPresShell) {
   8561  if (RefPtr<Document> doc = aPresShell->GetDocument()) {
   8562    WidgetEvent event(true, eVoidEvent);
   8563    nsTArray<EventTarget*> targets;
   8564    nsresult rv = EventDispatcher::Dispatch(doc, nullptr, &event, nullptr,
   8565                                            nullptr, nullptr, &targets);
   8566    NS_ENSURE_SUCCESS(rv, false);
   8567    for (size_t i = 0; i < targets.Length(); i++) {
   8568      if (targets[i]->IsApzAware()) {
   8569        return true;
   8570      }
   8571    }
   8572  }
   8573  return false;
   8574 }
   8575 
   8576 /* static */
   8577 bool nsLayoutUtils::CanScrollOriginClobberApz(ScrollOrigin aScrollOrigin) {
   8578  switch (aScrollOrigin) {
   8579    case ScrollOrigin::None:
   8580    case ScrollOrigin::NotSpecified:
   8581    case ScrollOrigin::Apz:
   8582    case ScrollOrigin::Restore:
   8583      return false;
   8584    default:
   8585      return true;
   8586  }
   8587 }
   8588 
   8589 /* static */
   8590 ScrollMetadata nsLayoutUtils::ComputeScrollMetadata(
   8591    const nsIFrame* aForFrame, const nsIFrame* aScrollFrame,
   8592    nsIContent* aContent, const nsIFrame* aItemFrame,
   8593    const nsPoint& aOffsetToReferenceFrame,
   8594    WebRenderLayerManager* aLayerManager, ViewID aScrollParentId,
   8595    const nsSize& aScrollPortSize, bool aIsRootContent) {
   8596  const nsPresContext* presContext = aForFrame->PresContext();
   8597  int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
   8598 
   8599  PresShell* presShell = presContext->GetPresShell();
   8600  ScrollMetadata metadata;
   8601  FrameMetrics& metrics = metadata.GetMetrics();
   8602  metrics.SetLayoutViewport(
   8603      CSSRect(CSSPoint(), CSSSize::FromAppUnits(aScrollPortSize)));
   8604 
   8605  nsIDocShell* docShell = presContext->GetDocShell();
   8606  const BrowsingContext* bc =
   8607      docShell ? docShell->GetBrowsingContext() : nullptr;
   8608  bool isTouchEventsEnabled =
   8609      bc &&
   8610      bc->TouchEventsOverride() == mozilla::dom::TouchEventsOverride::Enabled;
   8611 
   8612  if (bc && bc->InRDMPane() && isTouchEventsEnabled) {
   8613    metadata.SetIsRDMTouchSimulationActive(true);
   8614  }
   8615 
   8616  ViewID scrollId = ScrollableLayerGuid::NULL_SCROLL_ID;
   8617  if (aContent) {
   8618    if (void* paintRequestTime =
   8619            aContent->GetProperty(nsGkAtoms::paintRequestTime)) {
   8620      metrics.SetPaintRequestTime(*static_cast<TimeStamp*>(paintRequestTime));
   8621      aContent->RemoveProperty(nsGkAtoms::paintRequestTime);
   8622    }
   8623    scrollId = nsLayoutUtils::FindOrCreateIDFor(aContent);
   8624    nsRect dp;
   8625    if (DisplayPortUtils::GetDisplayPort(aContent, &dp)) {
   8626      metrics.SetDisplayPort(CSSRect::FromAppUnits(dp));
   8627      DisplayPortUtils::MarkDisplayPortAsPainted(aContent);
   8628    }
   8629 
   8630    metrics.SetHasNonZeroDisplayPortMargins(false);
   8631    if (DisplayPortMarginsPropertyData* currentData =
   8632            static_cast<DisplayPortMarginsPropertyData*>(
   8633                aContent->GetProperty(nsGkAtoms::DisplayPortMargins))) {
   8634      if (currentData->mMargins.mMargins != ScreenMargin()) {
   8635        metrics.SetHasNonZeroDisplayPortMargins(true);
   8636      }
   8637    }
   8638 
   8639    // Note: GetProperty() will return nullptr both in the case where
   8640    // the property hasn't been set, and in the case where the property
   8641    // has been set to false (in which case the property value is
   8642    // `reinterpret_cast<void*>(false)` which is nullptr.
   8643    if (aContent->GetProperty(nsGkAtoms::forceMousewheelAutodir)) {
   8644      metadata.SetForceMousewheelAutodir(true);
   8645    }
   8646 
   8647    if (aContent->GetProperty(nsGkAtoms::forceMousewheelAutodirHonourRoot)) {
   8648      metadata.SetForceMousewheelAutodirHonourRoot(true);
   8649    }
   8650 
   8651    if (IsAPZTestLoggingEnabled()) {
   8652      LogTestDataForPaint(aLayerManager, scrollId, "displayport",
   8653                          metrics.GetDisplayPort());
   8654    }
   8655 
   8656    metrics.SetMinimalDisplayPort(
   8657        aContent->GetProperty(nsGkAtoms::MinimalDisplayPort));
   8658  }
   8659 
   8660  ScrollContainerFrame* scrollContainerFrame =
   8661      aScrollFrame ? aScrollFrame->GetScrollTargetFrame() : nullptr;
   8662 
   8663  metrics.SetScrollableRect(
   8664      CSSRect::FromAppUnits(nsLayoutUtils::CalculateScrollableRectForFrame(
   8665          scrollContainerFrame, aForFrame)));
   8666 
   8667  if (scrollContainerFrame) {
   8668    CSSPoint layoutScrollOffset =
   8669        CSSPoint::FromAppUnits(scrollContainerFrame->GetScrollPosition());
   8670    CSSPoint visualScrollOffset =
   8671        aIsRootContent
   8672            ? CSSPoint::FromAppUnits(presShell->GetVisualViewportOffset())
   8673            : layoutScrollOffset;
   8674    metrics.SetVisualScrollOffset(visualScrollOffset);
   8675    // APZ sometimes reads this even if we haven't set a visual scroll
   8676    // update type (specifically, in the isFirstPaint case), so always
   8677    // set it.
   8678    metrics.SetVisualDestination(visualScrollOffset);
   8679 
   8680    if (aIsRootContent) {
   8681      if (aLayerManager->GetIsFirstPaint() &&
   8682          presShell->IsVisualViewportOffsetSet()) {
   8683        // Restore the visual viewport offset to the copy stored on the
   8684        // main thread.
   8685        presShell->ScrollToVisual(presShell->GetVisualViewportOffset(),
   8686                                  FrameMetrics::eRestore, ScrollMode::Instant);
   8687      }
   8688    }
   8689 
   8690    if (scrollContainerFrame->IsRootScrollFrameOfDocument()) {
   8691      if (const Maybe<PresShell::VisualScrollUpdate>& visualUpdate =
   8692              presShell->GetPendingVisualScrollUpdate()) {
   8693        metrics.SetVisualDestination(
   8694            CSSPoint::FromAppUnits(visualUpdate->mVisualScrollOffset));
   8695        metrics.SetVisualScrollUpdateType(visualUpdate->mUpdateType);
   8696        presShell->AcknowledgePendingVisualScrollUpdate();
   8697      }
   8698    }
   8699 
   8700    if (aIsRootContent) {
   8701      // Expand the layout viewport to the size including the area covered by
   8702      // the dynamic toolbar in the case where the dynamic toolbar is being
   8703      // used, otherwise when the dynamic toolbar transitions on the compositor,
   8704      // the layout viewport will be smaller than the visual viewport on the
   8705      // compositor, thus the layout viewport offset will be forced to be moved
   8706      // in FrameMetrics::KeepLayoutViewportEnclosingVisualViewport.
   8707      if (presContext->HasDynamicToolbar()) {
   8708        CSSRect viewport = metrics.GetLayoutViewport();
   8709        viewport.SizeTo(nsLayoutUtils::ExpandHeightForDynamicToolbar(
   8710            presContext, viewport.Size()));
   8711        metrics.SetLayoutViewport(viewport);
   8712 
   8713        // We need to set 'fixed margins' to adjust 'fixed margins' value on the
   8714        // composiutor in the case where the dynamic toolbar is completely
   8715        // hidden because the margin value on the compositor is offset from the
   8716        // position where the dynamic toolbar is completely VISIBLE but now the
   8717        // toolbar is completely HIDDEN we need to adjust the difference on the
   8718        // compositor.
   8719        if (presContext->GetDynamicToolbarState() ==
   8720            DynamicToolbarState::Collapsed) {
   8721          metrics.SetFixedLayerMargins(ScreenMargin(
   8722              0, 0,
   8723              ScreenCoord(presContext->GetDynamicToolbarHeight() -
   8724                          presContext->GetDynamicToolbarMaxHeight()),
   8725              0));
   8726        }
   8727      }
   8728 
   8729      metrics.SetIsSoftwareKeyboardVisible(presContext->GetKeyboardHeight() >
   8730                                           0);
   8731      metrics.SetInteractiveWidget(
   8732          presContext->Document()->InteractiveWidget());
   8733    }
   8734 
   8735    metrics.SetScrollGeneration(
   8736        scrollContainerFrame->CurrentScrollGeneration());
   8737 
   8738    CSSRect viewport = metrics.GetLayoutViewport();
   8739    viewport.MoveTo(layoutScrollOffset);
   8740    metrics.SetLayoutViewport(viewport);
   8741 
   8742    nsSize lineScrollAmount = scrollContainerFrame->GetLineScrollAmount();
   8743    LayoutDeviceIntSize lineScrollAmountInDevPixels =
   8744        LayoutDeviceIntSize::FromAppUnitsRounded(
   8745            lineScrollAmount, presContext->AppUnitsPerDevPixel());
   8746    metadata.SetLineScrollAmount(lineScrollAmountInDevPixels);
   8747 
   8748    nsSize pageScrollAmount = scrollContainerFrame->GetPageScrollAmount();
   8749    LayoutDeviceIntSize pageScrollAmountInDevPixels =
   8750        LayoutDeviceIntSize::FromAppUnitsRounded(
   8751            pageScrollAmount, presContext->AppUnitsPerDevPixel());
   8752    metadata.SetPageScrollAmount(pageScrollAmountInDevPixels);
   8753 
   8754    if (aScrollFrame->GetParent()) {
   8755      metadata.SetDisregardedDirection(
   8756          WheelHandlingUtils::GetDisregardedWheelScrollDirection(
   8757              aScrollFrame->GetParent()));
   8758    }
   8759 
   8760    metadata.SetSnapInfo(scrollContainerFrame->GetScrollSnapInfo());
   8761    metadata.SetOverscrollBehavior(
   8762        scrollContainerFrame->GetOverscrollBehaviorInfo());
   8763    auto scrollStyles = scrollContainerFrame->GetScrollStyles();
   8764    metadata.SetOverflow({scrollStyles.mHorizontal, scrollStyles.mVertical});
   8765    metadata.SetScrollUpdates(scrollContainerFrame->GetScrollUpdates());
   8766  }
   8767 
   8768  // If we have the scrollparent being the same as the scroll id, the
   8769  // compositor-side code could get into an infinite loop while building the
   8770  // overscroll handoff chain.
   8771  MOZ_ASSERT(aScrollParentId == ScrollableLayerGuid::NULL_SCROLL_ID ||
   8772             scrollId != aScrollParentId);
   8773  metrics.SetScrollId(scrollId);
   8774  metrics.SetIsRootContent(aIsRootContent);
   8775  metadata.SetScrollParentId(aScrollParentId);
   8776 
   8777  const nsIFrame* rootScrollContainerFrame =
   8778      presShell->GetRootScrollContainerFrame();
   8779  bool isRootScrollContainerFrame = aScrollFrame == rootScrollContainerFrame;
   8780  Document* document = presShell->GetDocument();
   8781 
   8782  if (scrollId != ScrollableLayerGuid::NULL_SCROLL_ID) {
   8783    if (aForFrame->IsMenuPopupFrame()) {
   8784      // In the case of popup windows, the menu popup frame becomes the root.
   8785      MOZ_ASSERT(XRE_IsParentProcess());
   8786      metadata.SetIsLayersIdRoot(true);
   8787    } else if (!presContext->GetParentPresContext()) {
   8788      if ((aScrollFrame && isRootScrollContainerFrame)) {
   8789        metadata.SetIsLayersIdRoot(true);
   8790      } else {
   8791        MOZ_ASSERT(document, "A non-root-scroll frame must be in a document");
   8792        if (aContent == document->GetDocumentElement()) {
   8793          metadata.SetIsLayersIdRoot(true);
   8794        }
   8795      }
   8796    }
   8797  }
   8798 
   8799  // Get whether the root content is RTL(E.g. it's true either if
   8800  // "writing-mode: vertical-rl", or if
   8801  // "writing-mode: horizontal-tb; direction: rtl;" in CSS).
   8802  // For the concept of this and the reason why we need to get this kind of
   8803  // information, see the definition of |mIsAutoDirRootContentRTL| in struct
   8804  // |ScrollMetadata|.
   8805  const Element* bodyElement = document ? document->GetBodyElement() : nullptr;
   8806  const nsIFrame* primaryFrame =
   8807      bodyElement ? bodyElement->GetPrimaryFrame() : rootScrollContainerFrame;
   8808  if (!primaryFrame) {
   8809    primaryFrame = rootScrollContainerFrame;
   8810  }
   8811  if (primaryFrame) {
   8812    WritingMode writingModeOfRootScrollFrame = primaryFrame->GetWritingMode();
   8813    if (writingModeOfRootScrollFrame.IsPhysicalRTL()) {
   8814      metadata.SetIsAutoDirRootContentRTL(true);
   8815    }
   8816  }
   8817 
   8818  // Only the root scrollable frame for a given presShell should pick up
   8819  // the presShell's resolution. All the other frames are 1.0.
   8820  if (isRootScrollContainerFrame) {
   8821    metrics.SetPresShellResolution(presShell->GetResolution());
   8822  } else {
   8823    metrics.SetPresShellResolution(1.0f);
   8824  }
   8825 
   8826  if (presShell->IsResolutionUpdated()) {
   8827    metadata.SetResolutionUpdated(true);
   8828  }
   8829 
   8830  // The cumulative resolution is the resolution at which the scroll frame's
   8831  // content is actually rendered. It includes the pres shell resolutions of
   8832  // all the pres shells from here up to the root, as well as any css-driven
   8833  // resolution. We don't need to compute it as it's already stored in the
   8834  // container parameters... except if we're in WebRender in which case we
   8835  // don't have a aContainerParameters. In that case we're also not rasterizing
   8836  // in Gecko anyway, so the only resolution we care about here is the presShell
   8837  // resolution which we need to propagate to WebRender.
   8838  metrics.SetCumulativeResolution(
   8839      LayoutDeviceToLayerScale(presShell->GetCumulativeResolution()));
   8840 
   8841  metrics.SetTransformToAncestorScale(
   8842      GetTransformToAncestorScaleCrossProcessForFrameMetrics(
   8843          aScrollFrame ? aScrollFrame : aForFrame));
   8844  metrics.SetDevPixelsPerCSSPixel(presContext->CSSToDevPixelScale());
   8845 
   8846  // Initially, AsyncPanZoomController should render the content to the screen
   8847  // at the painted resolution.
   8848  const LayerToParentLayerScale layerToParentLayerScale(1.0f);
   8849  metrics.SetZoom(metrics.GetCumulativeResolution() *
   8850                  metrics.GetDevPixelsPerCSSPixel() * layerToParentLayerScale);
   8851 
   8852  // Calculate the composition bounds as the size of the scroll frame and
   8853  // its origin relative to the reference frame.
   8854  // If aScrollFrame is null, we are in a document without a root scroll frame,
   8855  // so it's a xul document. In this case, use the size of the viewport frame.
   8856  const nsIFrame* frameForCompositionBoundsCalculation =
   8857      aScrollFrame ? aScrollFrame : aForFrame;
   8858  nsRect compositionBounds(
   8859      frameForCompositionBoundsCalculation->GetOffsetToCrossDoc(aItemFrame) +
   8860          aOffsetToReferenceFrame,
   8861      frameForCompositionBoundsCalculation->GetSize());
   8862  if (scrollContainerFrame) {
   8863    // If we have a scrollable frame, restrict the composition bounds to its
   8864    // scroll port. The scroll port excludes the frame borders and the scroll
   8865    // bars, which we don't want to be part of the composition bounds.
   8866    nsRect scrollPort = scrollContainerFrame->GetScrollPortRect();
   8867    compositionBounds = nsRect(
   8868        compositionBounds.TopLeft() + scrollPort.TopLeft(), scrollPort.Size());
   8869  }
   8870  ParentLayerRect frameBounds =
   8871      LayoutDeviceRect::FromAppUnits(compositionBounds, auPerDevPixel) *
   8872      metrics.GetCumulativeResolution() * layerToParentLayerScale;
   8873 
   8874  // For the root scroll frame of the root content document (RCD-RSF), the above
   8875  // calculation will yield the size of the viewport frame as the composition
   8876  // bounds, which doesn't actually correspond to what is visible when
   8877  // nsIDOMWindowUtils::setCSSViewport has been called to modify the visible
   8878  // area of the prescontext that the viewport frame is reflowed into. In that
   8879  // case if our document has a widget then the widget's bounds will correspond
   8880  // to what is visible. If we don't have a widget the root view's bounds
   8881  // correspond to what would be visible because they don't get modified by
   8882  // setCSSViewport.
   8883  bool isRootContentDocRootScrollFrame =
   8884      isRootScrollContainerFrame &&
   8885      presContext->IsRootContentDocumentCrossProcess();
   8886  if (isRootContentDocRootScrollFrame) {
   8887    UpdateCompositionBoundsForRCDRSF(frameBounds, presContext);
   8888    if (RefPtr<MobileViewportManager> MVM =
   8889            presContext->PresShell()->GetMobileViewportManager()) {
   8890      metrics.SetCompositionSizeWithoutDynamicToolbar(
   8891          MVM->GetCompositionSizeWithoutDynamicToolbar());
   8892    }
   8893  }
   8894 
   8895  metrics.SetCompositionBoundsWidthIgnoringScrollbars(frameBounds.width);
   8896 
   8897  nsMargin sizes = ScrollbarAreaToExcludeFromCompositionBoundsFor(aScrollFrame);
   8898  // Scrollbars are not subject to resolution scaling, so LD pixels = layer
   8899  // pixels for them.
   8900  ParentLayerMargin boundMargins =
   8901      LayoutDeviceMargin::FromAppUnits(sizes, auPerDevPixel) *
   8902      LayoutDeviceToParentLayerScale(1.0f);
   8903  frameBounds.Deflate(boundMargins);
   8904 
   8905  metrics.SetCompositionBounds(frameBounds);
   8906 
   8907  metrics.SetBoundingCompositionSize(
   8908      nsLayoutUtils::CalculateBoundingCompositionSize(
   8909          aScrollFrame ? aScrollFrame : aForFrame,
   8910          isRootContentDocRootScrollFrame, metrics));
   8911 
   8912  if (StaticPrefs::apz_printtree() || StaticPrefs::apz_test_logging_enabled()) {
   8913    if (const nsIContent* content =
   8914            frameForCompositionBoundsCalculation->GetContent()) {
   8915      nsAutoString contentDescription;
   8916      if (content->IsElement()) {
   8917        content->AsElement()->Describe(contentDescription);
   8918      } else {
   8919        contentDescription.AssignLiteral("(not an element)");
   8920      }
   8921      metadata.SetContentDescription(
   8922          NS_LossyConvertUTF16toASCII(contentDescription));
   8923      if (IsAPZTestLoggingEnabled()) {
   8924        LogTestDataForPaint(aLayerManager, scrollId, "contentDescription",
   8925                            metadata.GetContentDescription().get());
   8926      }
   8927    }
   8928  }
   8929 
   8930  metrics.SetPresShellId(presShell->GetPresShellId());
   8931 
   8932  if (ShouldDisableApzForElement(aContent)) {
   8933    metadata.SetForceDisableApz(true);
   8934  }
   8935 
   8936  metadata.SetIsPaginatedPresentation(presContext->Type() !=
   8937                                      nsPresContext::eContext_Galley);
   8938 
   8939  return metadata;
   8940 }
   8941 
   8942 /*static*/
   8943 Maybe<ScrollMetadata> nsLayoutUtils::GetRootMetadata(
   8944    nsDisplayListBuilder* aBuilder, WebRenderLayerManager* aLayerManager,
   8945    const std::function<bool(ViewID& aScrollId)>& aCallback) {
   8946  nsIFrame* frame = aBuilder->RootReferenceFrame();
   8947  nsPresContext* presContext = frame->PresContext();
   8948  PresShell* presShell = presContext->PresShell();
   8949  Document* document = presShell->GetDocument();
   8950 
   8951  // There is one case where we want the root container layer to have metrics.
   8952  // If the parent process is using XUL windows, there is no root scrollframe,
   8953  // and without explicitly creating metrics there will be no guaranteed
   8954  // top-level APZC.
   8955  bool addMetrics =
   8956      XRE_IsParentProcess() && !presShell->GetRootScrollContainerFrame();
   8957 
   8958  // Add metrics if there are none in the layer tree with the id (create an id
   8959  // if there isn't one already) of the root scroll frame/root content.
   8960  bool ensureMetricsForRootId =
   8961      nsLayoutUtils::AsyncPanZoomEnabled(frame) &&
   8962      aBuilder->IsPaintingToWindow() &&
   8963      (!presContext->GetParentPresContext() || frame->IsMenuPopupFrame());
   8964  MOZ_ASSERT(!presContext->GetParentPresContext() || frame->IsMenuPopupFrame());
   8965 
   8966  nsIContent* content = nullptr;
   8967  ScrollContainerFrame* rootScrollContainerFrame =
   8968      presShell->GetRootScrollContainerFrame();
   8969  if (frame->IsMenuPopupFrame()) {
   8970    content = frame->GetContent();
   8971  } else if (rootScrollContainerFrame) {
   8972    content = rootScrollContainerFrame->GetContent();
   8973  } else {
   8974    // If there is no root scroll frame, pick the document element instead.
   8975    // The only case we don't want to do this is in non-APZ fennec, where
   8976    // we want the root xul document to get a null scroll id so that the root
   8977    // content document gets the first non-null scroll id.
   8978    content = document->GetDocumentElement();
   8979  }
   8980 
   8981  if (ensureMetricsForRootId && content) {
   8982    ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor(content);
   8983    if (aCallback(scrollId)) {
   8984      ensureMetricsForRootId = false;
   8985    }
   8986  }
   8987 
   8988  if (addMetrics || ensureMetricsForRootId) {
   8989    bool isRootContent = presContext->IsRootContentDocumentCrossProcess();
   8990 
   8991    nsSize scrollPortSize = frame->GetSize();
   8992    if (isRootContent && rootScrollContainerFrame) {
   8993      scrollPortSize = rootScrollContainerFrame->GetScrollPortRect().Size();
   8994    }
   8995    return Some(nsLayoutUtils::ComputeScrollMetadata(
   8996        frame, rootScrollContainerFrame, content, frame,
   8997        aBuilder->ToReferenceFrame(frame), aLayerManager,
   8998        ScrollableLayerGuid::NULL_SCROLL_ID, scrollPortSize, isRootContent));
   8999  }
   9000 
   9001  return Nothing();
   9002 }
   9003 
   9004 /* static */
   9005 void nsLayoutUtils::TransformToAncestorAndCombineRegions(
   9006    const nsRegion& aRegion, nsIFrame* aFrame, const nsIFrame* aAncestorFrame,
   9007    nsRegion* aPreciseTargetDest, nsRegion* aImpreciseTargetDest,
   9008    Maybe<Matrix4x4Flagged>* aMatrixCache, const DisplayItemClip* aClip) {
   9009  if (aRegion.IsEmpty()) {
   9010    return;
   9011  }
   9012  bool isPrecise;
   9013  RegionBuilder<nsRegion> transformedRegion;
   9014  for (nsRegion::RectIterator it = aRegion.RectIter(); !it.Done(); it.Next()) {
   9015    nsRect transformed = TransformFrameRectToAncestor(
   9016        aFrame, it.Get(), aAncestorFrame, &isPrecise, aMatrixCache);
   9017    if (aClip) {
   9018      transformed = aClip->ApplyNonRoundedIntersection(transformed);
   9019      if (aClip->GetRoundedRectCount() > 0) {
   9020        isPrecise = false;
   9021      }
   9022    }
   9023    transformedRegion.OrWith(transformed);
   9024  }
   9025  nsRegion* dest = isPrecise ? aPreciseTargetDest : aImpreciseTargetDest;
   9026  dest->OrWith(transformedRegion.ToRegion());
   9027  // If the region becomes too complex this has a large performance impact.
   9028  // We limit its complexity here.
   9029  if (dest->GetNumRects() > 12) {
   9030    dest->SimplifyOutward(6);
   9031    if (isPrecise) {
   9032      aPreciseTargetDest->OrWith(*aImpreciseTargetDest);
   9033      *aImpreciseTargetDest = std::move(*aPreciseTargetDest);
   9034      aImpreciseTargetDest->SimplifyOutward(6);
   9035      *aPreciseTargetDest = nsRegion();
   9036    }
   9037  }
   9038 }
   9039 
   9040 /* static */
   9041 bool nsLayoutUtils::ShouldUseNoFramesSheet(Document* aDocument) {
   9042  bool allowSubframes = true;
   9043  nsIDocShell* docShell = aDocument->GetDocShell();
   9044  if (docShell) {
   9045    docShell->GetAllowSubframes(&allowSubframes);
   9046  }
   9047  return !allowSubframes;
   9048 }
   9049 
   9050 /* static */
   9051 void nsLayoutUtils::GetFrameTextContent(nsIFrame* aFrame, nsAString& aResult) {
   9052  aResult.Truncate();
   9053  AppendFrameTextContent(aFrame, aResult);
   9054 }
   9055 
   9056 /* static */
   9057 void nsLayoutUtils::AppendFrameTextContent(nsIFrame* aFrame,
   9058                                           nsAString& aResult) {
   9059  if (aFrame->IsTextFrame()) {
   9060    auto* const textFrame = static_cast<nsTextFrame*>(aFrame);
   9061    const auto offset = AssertedCast<uint32_t>(textFrame->GetContentOffset());
   9062    const auto length = AssertedCast<uint32_t>(textFrame->GetContentLength());
   9063    textFrame->CharacterDataBuffer().AppendTo(aResult, offset, length);
   9064  } else {
   9065    for (nsIFrame* child : aFrame->PrincipalChildList()) {
   9066      AppendFrameTextContent(child, aResult);
   9067    }
   9068  }
   9069 }
   9070 
   9071 /* static */
   9072 nsRect nsLayoutUtils::GetSelectionBoundingRect(const Selection* aSel) {
   9073  nsRect res;
   9074  // Bounding client rect may be empty after calling GetBoundingClientRect
   9075  // when range is collapsed. So we get caret's rect when range is
   9076  // collapsed.
   9077  if (aSel->IsCollapsed()) {
   9078    nsIFrame* frame = nsCaret::GetGeometry(aSel, &res);
   9079    if (frame) {
   9080      nsIFrame* relativeTo = GetContainingBlockForClientRect(frame);
   9081      res = TransformFrameRectToAncestor(frame, res, relativeTo);
   9082    }
   9083  } else {
   9084    RectAccumulator accumulator;
   9085    const uint32_t rangeCount = aSel->RangeCount();
   9086    for (const uint32_t idx : IntegerRange(rangeCount)) {
   9087      MOZ_ASSERT(aSel->RangeCount() == rangeCount);
   9088      nsRange* range = aSel->GetRangeAt(idx);
   9089      nsRange::CollectClientRectsAndText(
   9090          &accumulator, nullptr, range, range->GetStartContainer(),
   9091          range->StartOffset(), range->GetEndContainer(), range->EndOffset(),
   9092          true, false);
   9093    }
   9094    res = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect
   9095                                            : accumulator.mResultRect;
   9096  }
   9097 
   9098  return res;
   9099 }
   9100 
   9101 /* static */
   9102 nsBlockFrame* nsLayoutUtils::GetFloatContainingBlock(nsIFrame* aFrame) {
   9103  nsIFrame* ancestor = aFrame->GetParent();
   9104  while (ancestor && !ancestor->IsFloatContainingBlock()) {
   9105    ancestor = ancestor->GetParent();
   9106  }
   9107  MOZ_ASSERT(!ancestor || ancestor->IsBlockFrameOrSubclass(),
   9108             "Float containing block can only be block frame");
   9109  return static_cast<nsBlockFrame*>(ancestor);
   9110 }
   9111 
   9112 // The implementations of this calculation are adapted from
   9113 // Element::GetBoundingClientRect().
   9114 /* static */
   9115 CSSRect nsLayoutUtils::GetBoundingContentRect(
   9116    const nsIContent* aContent,
   9117    const ScrollContainerFrame* aRootScrollContainerFrame,
   9118    Maybe<CSSRect>* aOutNearestScrollClip) {
   9119  if (nsIFrame* frame = aContent->GetPrimaryFrame()) {
   9120    return GetBoundingFrameRect(frame, aRootScrollContainerFrame,
   9121                                aOutNearestScrollClip);
   9122  }
   9123  return CSSRect();
   9124 }
   9125 
   9126 /* static */
   9127 CSSRect nsLayoutUtils::GetBoundingFrameRect(
   9128    nsIFrame* aFrame, const ScrollContainerFrame* aRootScrollContainerFrame,
   9129    Maybe<CSSRect>* aOutNearestScrollClip) {
   9130  CSSRect result;
   9131  nsIFrame* relativeTo = aRootScrollContainerFrame->GetScrolledFrame();
   9132  result = CSSRect::FromAppUnits(nsLayoutUtils::GetAllInFlowRectsUnion(
   9133      aFrame, relativeTo,
   9134      nsLayoutUtils::GetAllInFlowRectsFlag::AccountForTransforms));
   9135 
   9136  // If the element is contained in a scroll container frame that is not the
   9137  // root scroll container frame, make sure to clip the result so that it is not
   9138  // larger than the containing scroll container frame's bounds.
   9139  ScrollContainerFrame* scrollContainerFrame =
   9140      nsLayoutUtils::GetNearestScrollContainerFrame(
   9141          aFrame, SCROLLABLE_INCLUDE_HIDDEN | SCROLLABLE_FIXEDPOS_FINDS_ROOT);
   9142  if (scrollContainerFrame &&
   9143      scrollContainerFrame != aRootScrollContainerFrame) {
   9144    // Get the bounds of the scroll frame in the same coordinate space
   9145    // as |result|.
   9146    nsRect subFrameRect = scrollContainerFrame->GetRectRelativeToSelf();
   9147    TransformResult res = nsLayoutUtils::TransformRect(
   9148        scrollContainerFrame, relativeTo, subFrameRect);
   9149    MOZ_ASSERT(res == TRANSFORM_SUCCEEDED || res == NONINVERTIBLE_TRANSFORM);
   9150    if (res == TRANSFORM_SUCCEEDED) {
   9151      CSSRect subFrameRectCSS = CSSRect::FromAppUnits(subFrameRect);
   9152      if (aOutNearestScrollClip) {
   9153        *aOutNearestScrollClip = Some(subFrameRectCSS);
   9154      }
   9155 
   9156      result = subFrameRectCSS.Intersect(result);
   9157    }
   9158  }
   9159  return result;
   9160 }
   9161 
   9162 /* static */
   9163 bool nsLayoutUtils::IsTransformed(nsIFrame* aForFrame, nsIFrame* aTopFrame) {
   9164  for (nsIFrame* f = aForFrame; f != aTopFrame; f = f->GetParent()) {
   9165    if (f->IsTransformed()) {
   9166      return true;
   9167    }
   9168  }
   9169  return false;
   9170 }
   9171 
   9172 /*static*/
   9173 CSSPoint nsLayoutUtils::GetCumulativeApzCallbackTransform(nsIFrame* aFrame) {
   9174  CSSPoint delta;
   9175  if (!aFrame) {
   9176    return delta;
   9177  }
   9178  nsIFrame* frame = aFrame;
   9179  nsCOMPtr<nsIContent> lastContent;
   9180  bool seenRcdRsf = false;
   9181 
   9182  // Helper lambda to apply the callback transform for a single frame.
   9183  auto applyCallbackTransformForFrame = [&](nsIFrame* frame) {
   9184    if (frame) {
   9185      nsCOMPtr<nsIContent> content = frame->GetContent();
   9186      if (content && (content != lastContent)) {
   9187        void* property = content->GetProperty(nsGkAtoms::apzCallbackTransform);
   9188        if (property) {
   9189          delta += *static_cast<CSSPoint*>(property);
   9190        }
   9191      }
   9192      lastContent = content;
   9193    }
   9194  };
   9195 
   9196  while (frame) {
   9197    // Apply the callback transform for the current frame.
   9198    applyCallbackTransformForFrame(frame);
   9199 
   9200    // Keep track of whether we've encountered the RCD-RSF's content element.
   9201    nsPresContext* pc = frame->PresContext();
   9202    if (pc->IsRootContentDocumentCrossProcess()) {
   9203      if (PresShell* shell = pc->GetPresShell()) {
   9204        if (nsIFrame* rsf = shell->GetRootScrollContainerFrame()) {
   9205          if (frame->GetContent() == rsf->GetContent()) {
   9206            seenRcdRsf = true;
   9207          }
   9208        }
   9209      }
   9210    }
   9211 
   9212    // If we reach the RCD's viewport frame, but have not encountered
   9213    // the RCD-RSF, we were inside fixed content in the RCD.
   9214    // We still want to apply the RCD-RSF's callback transform because
   9215    // it contains the offset between the visual and layout viewports
   9216    // which applies to fixed content as well.
   9217    ViewportFrame* viewportFrame = do_QueryFrame(frame);
   9218    if (viewportFrame) {
   9219      if (pc->IsRootContentDocumentCrossProcess() && !seenRcdRsf) {
   9220        applyCallbackTransformForFrame(
   9221            pc->PresShell()->GetRootScrollContainerFrame());
   9222      }
   9223    }
   9224 
   9225    // Proceed to the parent frame.
   9226    frame = GetCrossDocParentFrameInProcess(frame);
   9227  }
   9228  return delta;
   9229 }
   9230 
   9231 static nsSize ComputeMaxSizeForPartialPrerender(nsIFrame* aFrame,
   9232                                                nsSize aMaxSize) {
   9233  Matrix4x4Flagged transform = nsLayoutUtils::GetTransformToAncestor(
   9234      RelativeTo{aFrame},
   9235      RelativeTo{nsLayoutUtils::GetDisplayRootFrame(aFrame)});
   9236 
   9237  Matrix transform2D;
   9238  if (!transform.Is2D(&transform2D)) {
   9239    return aMaxSize;
   9240  }
   9241 
   9242  gfx::Rect result(0, 0, aMaxSize.width, aMaxSize.height);
   9243  auto scale = transform2D.ScaleFactors();
   9244  if (scale.xScale != 0 && scale.yScale != 0) {
   9245    result.width /= scale.xScale;
   9246    result.height /= scale.yScale;
   9247  }
   9248 
   9249  // Don't apply translate.
   9250  transform2D._31 = 0.0f;
   9251  transform2D._32 = 0.0f;
   9252 
   9253  // Don't apply scale.
   9254  if (scale.xScale != 0 && scale.yScale != 0) {
   9255    transform2D._11 /= scale.xScale;
   9256    transform2D._12 /= scale.xScale;
   9257    transform2D._21 /= scale.yScale;
   9258    transform2D._22 /= scale.yScale;
   9259  }
   9260 
   9261  // Theoretically we should use transform2D.Inverse() here but in this case
   9262  // |transform2D| is a pure rotation matrix, no scaling, no translate at all,
   9263  // so that the result bound's width and height would be pretty much same
   9264  // as the one rotated by the inverse matrix.
   9265  result = transform2D.TransformBounds(result);
   9266  return nsSize(
   9267      result.width < (float)nscoord_MAX ? result.width : nscoord_MAX,
   9268      result.height < (float)nscoord_MAX ? result.height : nscoord_MAX);
   9269 }
   9270 
   9271 /* static */
   9272 nsRect nsLayoutUtils::ComputePartialPrerenderArea(
   9273    nsIFrame* aFrame, const nsRect& aDirtyRect, const nsRect& aOverflow,
   9274    const nsSize& aPrerenderSize) {
   9275  nsSize maxSizeForPartialPrerender =
   9276      ComputeMaxSizeForPartialPrerender(aFrame, aPrerenderSize);
   9277  // Simple calculation for now: center the pre-render area on the dirty rect,
   9278  // and clamp to the overflow area. Later we can do more advanced things like
   9279  // redistributing from one axis to another, or from one side to another.
   9280  nscoord xExcess =
   9281      std::max(maxSizeForPartialPrerender.width - aDirtyRect.width, 0);
   9282  nscoord yExcess =
   9283      std::max(maxSizeForPartialPrerender.height - aDirtyRect.height, 0);
   9284  nsRect result = aDirtyRect;
   9285  result.Inflate(xExcess / 2, yExcess / 2);
   9286  return result.MoveInsideAndClamp(aOverflow);
   9287 }
   9288 
   9289 static bool LineHasNonEmptyContentWorker(nsIFrame* aFrame) {
   9290  // Look for non-empty frames, but ignore inline and br frames.
   9291  // For inline frames, descend into the children, if any.
   9292  if (aFrame->IsInlineFrame()) {
   9293    for (nsIFrame* child : aFrame->PrincipalChildList()) {
   9294      if (LineHasNonEmptyContentWorker(child)) {
   9295        return true;
   9296      }
   9297    }
   9298  } else {
   9299    if (!aFrame->IsBrFrame() && !aFrame->IsEmpty()) {
   9300      return true;
   9301    }
   9302  }
   9303  return false;
   9304 }
   9305 
   9306 static bool LineHasNonEmptyContent(nsLineBox* aLine) {
   9307  int32_t count = aLine->GetChildCount();
   9308  for (nsIFrame* frame = aLine->mFirstChild; count > 0;
   9309       --count, frame = frame->GetNextSibling()) {
   9310    if (LineHasNonEmptyContentWorker(frame)) {
   9311      return true;
   9312    }
   9313  }
   9314  return false;
   9315 }
   9316 
   9317 /* static */
   9318 bool nsLayoutUtils::IsInvisibleBreak(const nsINode* aNode,
   9319                                     nsIFrame** aNextLineFrame) {
   9320  if (aNextLineFrame) {
   9321    *aNextLineFrame = nullptr;
   9322  }
   9323 
   9324  if (!aNode->IsElement() || !aNode->IsEditable()) {
   9325    return false;
   9326  }
   9327  nsIFrame* frame = aNode->AsElement()->GetPrimaryFrame();
   9328  if (!frame || !frame->IsBrFrame()) {
   9329    return false;
   9330  }
   9331 
   9332  nsContainerFrame* f = frame->GetParent();
   9333  while (f && f->IsLineParticipant()) {
   9334    f = f->GetParent();
   9335  }
   9336  nsBlockFrame* blockAncestor = do_QueryFrame(f);
   9337  if (!blockAncestor) {
   9338    // The container frame doesn't support line breaking.
   9339    return false;
   9340  }
   9341 
   9342  bool valid = false;
   9343  nsBlockInFlowLineIterator iter(blockAncestor, frame, &valid);
   9344  if (!valid) {
   9345    return false;
   9346  }
   9347 
   9348  bool lineNonEmpty = LineHasNonEmptyContent(iter.GetLine());
   9349  if (!lineNonEmpty) {
   9350    return false;
   9351  }
   9352 
   9353  while (iter.Next()) {
   9354    auto currentLine = iter.GetLine();
   9355    // Completely skip empty lines.
   9356    if (!currentLine->IsEmpty()) {
   9357      // If we come across an inline line, the BR has caused a visible line
   9358      // break.
   9359      if (currentLine->IsInline()) {
   9360        if (aNextLineFrame) {
   9361          *aNextLineFrame = currentLine->mFirstChild;
   9362        }
   9363        return false;
   9364      }
   9365      break;
   9366    }
   9367  }
   9368 
   9369  return lineNonEmpty;
   9370 }
   9371 
   9372 /* static */
   9373 nsRect nsLayoutUtils::ComputeSVGOriginBox(SVGViewportElement* aElement) {
   9374  if (!aElement) {
   9375    return {};
   9376  }
   9377 
   9378  if (aElement->HasViewBox()) {
   9379    // Return the "origin box", which is defined as a rect positioned at the
   9380    // origin, but with the width and height given by the viewBox attribute
   9381    //
   9382    // https://drafts.csswg.org/css-box-3/#valdef-box-view-box
   9383    //
   9384    // For more discussion see
   9385    // https://github.com/web-platform-tests/interop/issues/509
   9386    const SVGViewBox& value = aElement->GetAnimatedViewBox()->GetAnimValue();
   9387    return nsRect(0, 0, nsPresContext::CSSPixelsToAppUnits(value.width),
   9388                  nsPresContext::CSSPixelsToAppUnits(value.height));
   9389  }
   9390 
   9391  // No viewBox is specified, uses the nearest SVG viewport as reference
   9392  // box.
   9393  auto viewportSize = aElement->GetViewportSize();
   9394  return nsRect(0, 0, nsPresContext::CSSPixelsToAppUnits(viewportSize.width),
   9395                nsPresContext::CSSPixelsToAppUnits(viewportSize.height));
   9396 }
   9397 
   9398 /* static */
   9399 nsRect nsLayoutUtils::ComputeSVGReferenceRect(
   9400    nsIFrame* aFrame, StyleGeometryBox aGeometryBox,
   9401    MayHaveNonScalingStrokeCyclicDependency aMayHaveCyclicDependency) {
   9402  MOZ_ASSERT(aFrame->GetContent()->IsSVGElement());
   9403  nsRect r;
   9404 
   9405  switch (aGeometryBox) {
   9406    case StyleGeometryBox::StrokeBox: {
   9407      // XXX Bug 1299876
   9408      // The size of stroke-box is not correct if this graphic element has
   9409      // specific stroke-linejoin or stroke-linecap.
   9410      const uint32_t flags = SVGUtils::eBBoxIncludeFillGeometry |
   9411                             SVGUtils::eBBoxIncludeStroke |
   9412                             (bool(aMayHaveCyclicDependency)
   9413                                  ? SVGUtils::eAvoidCycleIfNonScalingStroke
   9414                                  : 0);
   9415      gfxRect bbox = SVGUtils::GetBBox(aFrame, flags);
   9416      r = nsLayoutUtils::RoundGfxRectToAppRect(bbox, AppUnitsPerCSSPixel());
   9417      break;
   9418    }
   9419    case StyleGeometryBox::ViewBox: {
   9420      SVGViewportElement* viewportElement =
   9421          SVGElement::FromNode(aFrame->GetContent())->GetCtx();
   9422      if (!viewportElement) {
   9423        // We should not render without a viewport so return an empty rect.
   9424        break;
   9425      }
   9426      r = nsLayoutUtils::ComputeSVGOriginBox(viewportElement);
   9427      break;
   9428    }
   9429    case StyleGeometryBox::FillBox: {
   9430      gfxRect bbox =
   9431          SVGUtils::GetBBox(aFrame, SVGUtils::eBBoxIncludeFillGeometry);
   9432      r = nsLayoutUtils::RoundGfxRectToAppRect(bbox, AppUnitsPerCSSPixel());
   9433      break;
   9434    }
   9435    default: {
   9436      MOZ_ASSERT_UNREACHABLE("unsupported SVG box");
   9437      break;
   9438    }
   9439  }
   9440 
   9441  return r;
   9442 }
   9443 
   9444 /* static */
   9445 nsRect nsLayoutUtils::ComputeHTMLReferenceRect(const nsIFrame* aFrame,
   9446                                               StyleGeometryBox aGeometryBox) {
   9447  nsRect r;
   9448 
   9449  switch (aGeometryBox) {
   9450    case StyleGeometryBox::ContentBox:
   9451      r = aFrame->GetContentRectRelativeToSelf();
   9452      break;
   9453    case StyleGeometryBox::PaddingBox:
   9454      r = aFrame->GetPaddingRectRelativeToSelf();
   9455      break;
   9456    case StyleGeometryBox::MarginBox:
   9457      r = aFrame->GetMarginRectRelativeToSelf();
   9458      break;
   9459    case StyleGeometryBox::BorderBox:
   9460      r = aFrame->GetRectRelativeToSelf();
   9461      break;
   9462    default:
   9463      MOZ_ASSERT_UNREACHABLE("unsupported CSS box");
   9464      break;
   9465  }
   9466 
   9467  return r;
   9468 }
   9469 
   9470 static StyleGeometryBox ShapeBoxToGeometryBox(const StyleShapeBox& aBox) {
   9471  switch (aBox) {
   9472    case StyleShapeBox::BorderBox:
   9473      return StyleGeometryBox::BorderBox;
   9474    case StyleShapeBox::ContentBox:
   9475      return StyleGeometryBox::ContentBox;
   9476    case StyleShapeBox::MarginBox:
   9477      return StyleGeometryBox::MarginBox;
   9478    case StyleShapeBox::PaddingBox:
   9479      return StyleGeometryBox::PaddingBox;
   9480  }
   9481  MOZ_ASSERT_UNREACHABLE("Unknown shape box type");
   9482  return StyleGeometryBox::MarginBox;
   9483 }
   9484 
   9485 static StyleGeometryBox ClipPathBoxToGeometryBox(
   9486    const StyleShapeGeometryBox& aBox) {
   9487  using Tag = StyleShapeGeometryBox::Tag;
   9488  switch (aBox.tag) {
   9489    case Tag::ShapeBox:
   9490      return ShapeBoxToGeometryBox(aBox.AsShapeBox());
   9491    case Tag::ElementDependent:
   9492      return StyleGeometryBox::NoBox;
   9493    case Tag::FillBox:
   9494      return StyleGeometryBox::FillBox;
   9495    case Tag::StrokeBox:
   9496      return StyleGeometryBox::StrokeBox;
   9497    case Tag::ViewBox:
   9498      return StyleGeometryBox::ViewBox;
   9499  }
   9500  MOZ_ASSERT_UNREACHABLE("Unknown shape box type");
   9501  return StyleGeometryBox::NoBox;
   9502 }
   9503 
   9504 // The mapping is from
   9505 // https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box
   9506 /* static */
   9507 nsRect nsLayoutUtils::ComputeClipPathGeometryBox(
   9508    nsIFrame* aFrame, const StyleShapeGeometryBox& aBox) {
   9509  StyleGeometryBox box = ClipPathBoxToGeometryBox(aBox);
   9510 
   9511  if (aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
   9512    // For SVG elements without associated CSS layout box, the used value for
   9513    // content-box and padding-box is fill-box and for border-box and margin-box
   9514    // is stroke-box.
   9515    switch (box) {
   9516      case StyleGeometryBox::ContentBox:
   9517      case StyleGeometryBox::PaddingBox:
   9518      case StyleGeometryBox::FillBox:
   9519        return ComputeSVGReferenceRect(aFrame, StyleGeometryBox::FillBox);
   9520      case StyleGeometryBox::NoBox:
   9521      case StyleGeometryBox::BorderBox:
   9522      case StyleGeometryBox::MarginBox:
   9523      case StyleGeometryBox::StrokeBox:
   9524        return ComputeSVGReferenceRect(aFrame, StyleGeometryBox::StrokeBox);
   9525      case StyleGeometryBox::ViewBox:
   9526        return ComputeSVGReferenceRect(aFrame, StyleGeometryBox::ViewBox);
   9527      default:
   9528        MOZ_ASSERT_UNREACHABLE("Unknown clip-path geometry box");
   9529        // Use default, border-box (as stroke-box in SVG layout).
   9530        return ComputeSVGReferenceRect(aFrame, StyleGeometryBox::StrokeBox);
   9531    }
   9532  }
   9533 
   9534  // For elements with associated CSS layout box, the used value for fill-box is
   9535  // content-box and for stroke-box and view-box is border-box.
   9536  switch (box) {
   9537    case StyleGeometryBox::FillBox:
   9538    case StyleGeometryBox::ContentBox:
   9539      return ComputeHTMLReferenceRect(aFrame, StyleGeometryBox::ContentBox);
   9540    case StyleGeometryBox::NoBox:
   9541    case StyleGeometryBox::StrokeBox:
   9542    case StyleGeometryBox::ViewBox:
   9543    case StyleGeometryBox::BorderBox:
   9544      return ComputeHTMLReferenceRect(aFrame, StyleGeometryBox::BorderBox);
   9545    case StyleGeometryBox::PaddingBox:
   9546      return ComputeHTMLReferenceRect(aFrame, StyleGeometryBox::PaddingBox);
   9547    case StyleGeometryBox::MarginBox:
   9548      return ComputeHTMLReferenceRect(aFrame, StyleGeometryBox::MarginBox);
   9549    default:
   9550      MOZ_ASSERT_UNREACHABLE("Unknown clip-path geometry box");
   9551      // Use default, border-box.
   9552      return ComputeHTMLReferenceRect(aFrame, StyleGeometryBox::BorderBox);
   9553  }
   9554 }
   9555 
   9556 /* static */
   9557 nsPoint nsLayoutUtils::ComputeOffsetToUserSpace(nsDisplayListBuilder* aBuilder,
   9558                                                nsIFrame* aFrame) {
   9559  nsPoint offsetToBoundingBox =
   9560      aBuilder->ToReferenceFrame(aFrame) -
   9561      SVGIntegrationUtils::GetOffsetToBoundingBox(aFrame);
   9562  if (!aFrame->IsSVGFrame()) {
   9563    // Snap the offset if the reference frame is not a SVG frame, since other
   9564    // frames will be snapped to pixel when rendering.
   9565    offsetToBoundingBox =
   9566        nsPoint(aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(
   9567                    offsetToBoundingBox.x),
   9568                aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(
   9569                    offsetToBoundingBox.y));
   9570  }
   9571 
   9572  // During SVG painting, the offset computed here is applied to the gfxContext
   9573  // "ctx" used to paint the mask. After applying only "offsetToBoundingBox",
   9574  // "ctx" would have its origin at the top left corner of frame's bounding box
   9575  // (over all continuations).
   9576  // However, SVG painting needs the origin to be located at the origin of the
   9577  // SVG frame's "user space", i.e. the space in which, for example, the
   9578  // frame's BBox lives.
   9579  // SVG geometry frames and foreignObject frames apply their own offsets, so
   9580  // their position is relative to their user space. So for these frame types,
   9581  // if we want "ctx" to be in user space, we first need to subtract the
   9582  // frame's position so that SVG painting can later add it again and the
   9583  // frame is painted in the right place.
   9584  gfxPoint toUserSpaceGfx =
   9585      SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame);
   9586  nsPoint toUserSpace =
   9587      nsPoint(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),
   9588              nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
   9589 
   9590  return (offsetToBoundingBox - toUserSpace);
   9591 }
   9592 
   9593 /* static */
   9594 already_AddRefed<nsFontMetrics> nsLayoutUtils::GetMetricsFor(
   9595    nsPresContext* aPresContext, bool aIsVertical,
   9596    const nsStyleFont* aStyleFont, Length aFontSize, bool aUseUserFontSet) {
   9597  nsFont font = aStyleFont->mFont;
   9598  font.size = aFontSize;
   9599  gfxFont::Orientation orientation =
   9600      aIsVertical ? nsFontMetrics::eVertical : nsFontMetrics::eHorizontal;
   9601  nsFontMetrics::Params params;
   9602  params.language = aStyleFont->mLanguage;
   9603  params.explicitLanguage = aStyleFont->mExplicitLanguage;
   9604  params.orientation = orientation;
   9605  params.userFontSet =
   9606      aUseUserFontSet ? aPresContext->GetUserFontSet() : nullptr;
   9607  params.textPerf = aPresContext->GetTextPerfMetrics();
   9608  params.featureValueLookup = aPresContext->GetFontFeatureValuesLookup();
   9609  return aPresContext->GetMetricsFor(font, params);
   9610 }
   9611 
   9612 static void GetSpoofedSystemFontForRFP(LookAndFeel::FontID aFontID,
   9613                                       gfxFontStyle& aStyle, nsAString& aName) {
   9614 #if defined(XP_MACOSX) || defined(MOZ_WIDGET_UIKIT)
   9615  aName = u"-apple-system"_ns;
   9616  // Values taken from a macOS 10.15 system.
   9617  switch (aFontID) {
   9618    case LookAndFeel::FontID::Caption:
   9619    case LookAndFeel::FontID::Menu:
   9620      aStyle.size = 13;
   9621      break;
   9622    case LookAndFeel::FontID::SmallCaption:
   9623      aStyle.weight = gfxFontStyle::FontWeight::BOLD;
   9624      // fall-through for font-size
   9625      [[fallthrough]];
   9626    case LookAndFeel::FontID::MessageBox:
   9627    case LookAndFeel::FontID::StatusBar:
   9628      aStyle.size = 11;
   9629      break;
   9630    default:
   9631      aStyle.size = 12;
   9632      break;
   9633  }
   9634 #elif defined(XP_WIN)
   9635  // Windows uses Segoe UI for Latin alphabets, but other fonts for some RTL
   9636  // languages, so we fallback to sans-serif to fall back to the user's
   9637  // default sans-serif. Size is 12px for all system fonts (tried in an en-US
   9638  // system).
   9639  aName = u"sans-serif"_ns;
   9640  aStyle.size = 12;
   9641 #elif defined(MOZ_WIDGET_ANDROID)
   9642  // Keep consistency with nsLookAndFeel::NativeGetFont.
   9643  aName = u"Roboto"_ns;
   9644  aStyle.size = 12;
   9645 #elif defined(MOZ_WIDGET_GTK)
   9646  // On Linux, there is not a default. For example, GNOME on Debian uses
   9647  // Cantarell, 14.667px. Ubuntu Mate uses the Ubuntu font, but also 14.667px.
   9648  // Fedora with KDE uses Noto Sans, 13.3333px, but it uses Noto Sans on
   9649  // GNOME, too.
   9650  // In general, Linux uses some sans-serif, but its size can vary between
   9651  // 12px and 16px. We chose 15px because it is what Firefox is doing for the
   9652  // UI font-size.
   9653  // tor-browser#43141: Hardcode Arimo in case our custom fontconfig is
   9654  // missing.
   9655  aName = u"Arimo"_ns;
   9656  aStyle.size = 15;
   9657 #else
   9658 #  error "Unknown platform"
   9659 #endif
   9660 }
   9661 
   9662 /* static */
   9663 void nsLayoutUtils::ComputeSystemFont(nsFont* aSystemFont,
   9664                                      LookAndFeel::FontID aFontID,
   9665                                      const nsFont& aDefaultVariableFont,
   9666                                      const Document* aDocument) {
   9667  gfxFontStyle fontStyle;
   9668  nsAutoString systemFontName;
   9669  if (aDocument->ShouldResistFingerprinting(
   9670          RFPTarget::FontVisibilityRestrictGenerics)) {
   9671    GetSpoofedSystemFontForRFP(aFontID, fontStyle, systemFontName);
   9672  } else if (!LookAndFeel::GetFont(aFontID, systemFontName, fontStyle)) {
   9673    return;
   9674  }
   9675  systemFontName.Trim("\"'");
   9676  NS_ConvertUTF16toUTF8 nameu8(systemFontName);
   9677  Servo_FontFamily_ForSystemFont(&nameu8, &aSystemFont->family);
   9678  aSystemFont->style = fontStyle.style;
   9679  aSystemFont->family.is_system_font = fontStyle.systemFont;
   9680  aSystemFont->weight = fontStyle.weight;
   9681  aSystemFont->stretch = fontStyle.stretch;
   9682  aSystemFont->size = Length::FromPixels(fontStyle.size);
   9683 
   9684  // aSystemFont->langGroup = fontStyle.langGroup;
   9685 
   9686  switch (StyleFontSizeAdjust::Tag(fontStyle.sizeAdjustBasis)) {
   9687    case StyleFontSizeAdjust::Tag::None:
   9688      aSystemFont->sizeAdjust = StyleFontSizeAdjust::None();
   9689      break;
   9690    case StyleFontSizeAdjust::Tag::ExHeight:
   9691      aSystemFont->sizeAdjust =
   9692          StyleFontSizeAdjust::ExHeight(fontStyle.sizeAdjust);
   9693      break;
   9694    case StyleFontSizeAdjust::Tag::CapHeight:
   9695      aSystemFont->sizeAdjust =
   9696          StyleFontSizeAdjust::CapHeight(fontStyle.sizeAdjust);
   9697      break;
   9698    case StyleFontSizeAdjust::Tag::ChWidth:
   9699      aSystemFont->sizeAdjust =
   9700          StyleFontSizeAdjust::ChWidth(fontStyle.sizeAdjust);
   9701      break;
   9702    case StyleFontSizeAdjust::Tag::IcWidth:
   9703      aSystemFont->sizeAdjust =
   9704          StyleFontSizeAdjust::IcWidth(fontStyle.sizeAdjust);
   9705      break;
   9706    case StyleFontSizeAdjust::Tag::IcHeight:
   9707      aSystemFont->sizeAdjust =
   9708          StyleFontSizeAdjust::IcHeight(fontStyle.sizeAdjust);
   9709      break;
   9710  }
   9711 
   9712  if (aFontID == LookAndFeel::FontID::MozField ||
   9713      aFontID == LookAndFeel::FontID::MozButton ||
   9714      aFontID == LookAndFeel::FontID::MozList) {
   9715    // For textfields, buttons and selects, we use whatever font is defined by
   9716    // the system. Which it appears (and the assumption is) it is always a
   9717    // proportional font. Then we always use 2 points smaller than what the
   9718    // browser has defined as the default proportional font.
   9719    //
   9720    // This matches historical Windows behavior and other browsers.
   9721    auto newSize =
   9722        aDefaultVariableFont.size.ToCSSPixels() - CSSPixel::FromPoints(2.0f);
   9723    aSystemFont->size = Length::FromPixels(std::max(float(newSize), 0.0f));
   9724  }
   9725 }
   9726 
   9727 /* static */
   9728 bool nsLayoutUtils::ShouldHandleMetaViewport(const Document* aDocument) {
   9729  BrowsingContext* bc = aDocument->GetBrowsingContext();
   9730  return StaticPrefs::dom_meta_viewport_enabled() || (bc && bc->InRDMPane());
   9731 }
   9732 
   9733 /* static */
   9734 ComputedStyle* nsLayoutUtils::StyleForScrollbar(
   9735    const nsIFrame* aScrollbarPart) {
   9736  // Get the closest content node which is not an anonymous scrollbar
   9737  // part. It should be the originating element of the scrollbar part.
   9738  nsIContent* content = aScrollbarPart->GetContent();
   9739  // Note that the content may be a normal element with scrollbar part
   9740  // value specified for its -moz-appearance, so don't rely on it being
   9741  // a native anonymous. Also note that we have to check the node name
   9742  // because anonymous element like generated content may originate a
   9743  // scrollbar.
   9744  MOZ_ASSERT(content, "No content for the scrollbar part?");
   9745  while (content && content->IsInNativeAnonymousSubtree() &&
   9746         content->IsAnyOfXULElements(
   9747             nsGkAtoms::scrollbar, nsGkAtoms::scrollbarbutton,
   9748             nsGkAtoms::scrollcorner, nsGkAtoms::slider, nsGkAtoms::thumb)) {
   9749    content = content->GetParent();
   9750  }
   9751  MOZ_ASSERT(content, "Native anonymous element with no originating node?");
   9752  // Use the style from the primary frame of the content.
   9753  // Note: it is important to use the primary frame rather than an
   9754  // ancestor frame of the scrollbar part for the correct handling of
   9755  // viewport scrollbar. The content of the scroll frame of the viewport
   9756  // is the root element, but its style inherits from the viewport.
   9757  // Since we need to use the style of root element for the viewport
   9758  // scrollbar, we have to get the style from the primary frame.
   9759  if (nsIFrame* primaryFrame = content->GetPrimaryFrame()) {
   9760    return primaryFrame->Style();
   9761  }
   9762  // If the element doesn't have primary frame, get the computed style
   9763  // from the element directly. This can happen on viewport, because
   9764  // the scrollbar of viewport may be shown when the root element has
   9765  // > display: none; overflow: scroll;
   9766  MOZ_ASSERT(
   9767      content == aScrollbarPart->PresContext()->Document()->GetRootElement(),
   9768      "Root element is the only case for this fallback "
   9769      "path to be triggered");
   9770  RefPtr<ComputedStyle> style =
   9771      ServoStyleSet::ResolveServoStyle(*content->AsElement());
   9772  // Dropping the strong reference is fine because the style should be
   9773  // held strongly by the element.
   9774  return style.get();
   9775 }
   9776 
   9777 enum class FramePosition : uint8_t {
   9778  Unknown,
   9779  InView,
   9780  OutOfView,
   9781 };
   9782 
   9783 // NOTE: Returns a pair of Nothing() and `FramePosition::Unknown` if |aFrame|
   9784 // is not in out-of-process or if we haven't received enough information from
   9785 // APZ.
   9786 static std::pair<Maybe<ScreenRect>, FramePosition>
   9787 GetFrameRectVisibleRectOnScreen(const nsIFrame* aFrame,
   9788                                const nsRect& aFrameRect) {
   9789  // We actually want the in-process top prescontext here.
   9790  nsPresContext* topContextInProcess =
   9791      aFrame->PresContext()->GetInProcessRootContentDocumentPresContext();
   9792  if (!topContextInProcess) {
   9793    // We are in chrome process.
   9794    return std::make_pair(Nothing(), FramePosition::Unknown);
   9795  }
   9796 
   9797  if (topContextInProcess->Document()->IsTopLevelContentDocument()) {
   9798    // We are in the top of content document.
   9799    return std::make_pair(Nothing(), FramePosition::Unknown);
   9800  }
   9801 
   9802  nsIDocShell* docShell = topContextInProcess->GetDocShell();
   9803  BrowserChild* browserChild = BrowserChild::GetFrom(docShell);
   9804  if (!browserChild) {
   9805    // We are not in out-of-process iframe.
   9806    return std::make_pair(Nothing(), FramePosition::Unknown);
   9807  }
   9808 
   9809  if (!browserChild->GetEffectsInfo().IsVisible()) {
   9810    // There is no visible rect on this iframe at all.
   9811    return std::make_pair(Some(ScreenRect()), FramePosition::Unknown);
   9812  }
   9813 
   9814  Maybe<ScreenRect> visibleRect =
   9815      browserChild->GetTopLevelViewportVisibleRectInBrowserCoords();
   9816  if (!visibleRect) {
   9817    // We are unsure if we haven't received the transformed rectangle of the
   9818    // iframe's visible area.
   9819    return std::make_pair(Nothing(), FramePosition::Unknown);
   9820  }
   9821 
   9822  nsIFrame* rootFrame = topContextInProcess->PresShell()->GetRootFrame();
   9823  nsRect transformedToIFrame = nsLayoutUtils::TransformFrameRectToAncestor(
   9824      aFrame, aFrameRect, rootFrame);
   9825 
   9826  LayoutDeviceRect rectInLayoutDevicePixel = LayoutDeviceRect::FromAppUnits(
   9827      transformedToIFrame, topContextInProcess->AppUnitsPerDevPixel());
   9828 
   9829  ScreenRect transformedToRoot = ViewAs<ScreenPixel>(
   9830      browserChild->GetChildToParentConversionMatrix().TransformBounds(
   9831          rectInLayoutDevicePixel),
   9832      PixelCastJustification::ContentProcessIsLayerInUiProcess);
   9833 
   9834  FramePosition position = FramePosition::Unknown;
   9835  // we need to check whether the transformed rect is outside the iframe
   9836  // visible rect or not because in some cases the rect size is (0x0), thus
   9837  // the intersection between the transformed rect and the iframe visible rect
   9838  // would also be (0x0), then we can't tell whether the given nsIFrame is
   9839  // inside the iframe visible rect or not by calling BaseRect::IsEmpty for the
   9840  // intersection.
   9841  if (transformedToRoot.x > visibleRect->XMost() ||
   9842      transformedToRoot.y > visibleRect->YMost() ||
   9843      visibleRect->x > transformedToRoot.XMost() ||
   9844      visibleRect->y > transformedToRoot.YMost()) {
   9845    position = FramePosition::OutOfView;
   9846  } else {
   9847    position = FramePosition::InView;
   9848  }
   9849 
   9850  return std::make_pair(Some(visibleRect->Intersect(transformedToRoot)),
   9851                        position);
   9852 }
   9853 
   9854 // static
   9855 bool nsLayoutUtils::FrameRectIsScrolledOutOfViewInCrossProcess(
   9856    const nsIFrame* aFrame, const nsRect& aFrameRect) {
   9857  auto [visibleRect, framePosition] =
   9858      GetFrameRectVisibleRectOnScreen(aFrame, aFrameRect);
   9859  if (visibleRect.isNothing()) {
   9860    return false;
   9861  }
   9862 
   9863  return visibleRect->IsEmpty() && framePosition != FramePosition::InView;
   9864 }
   9865 
   9866 // static
   9867 bool nsLayoutUtils::FrameIsMostlyScrolledOutOfViewInCrossProcess(
   9868    const nsIFrame* aFrame, nscoord aMargin) {
   9869  auto [visibleRect, framePosition] = GetFrameRectVisibleRectOnScreen(
   9870      aFrame, aFrame->InkOverflowRectRelativeToSelf());
   9871  (void)framePosition;
   9872  if (visibleRect.isNothing()) {
   9873    return false;
   9874  }
   9875 
   9876  nsPresContext* topContextInProcess =
   9877      aFrame->PresContext()->GetInProcessRootContentDocumentPresContext();
   9878  MOZ_ASSERT(topContextInProcess);
   9879 
   9880  nsIDocShell* docShell = topContextInProcess->GetDocShell();
   9881  BrowserChild* browserChild = BrowserChild::GetFrom(docShell);
   9882  MOZ_ASSERT(browserChild);
   9883 
   9884  auto scale =
   9885      browserChild->GetChildToParentConversionMatrix().As2D().ScaleFactors();
   9886  const CSSCoord cssMargin = CSSPixel::FromAppUnits(aMargin);
   9887  ScreenSize margin =
   9888      CSSSize(cssMargin, cssMargin) * ViewAs<CSSToScreenScale2D>(scale);
   9889 
   9890  return visibleRect->width < margin.width ||
   9891         visibleRect->height < margin.height;
   9892 }
   9893 
   9894 // static
   9895 nsSize nsLayoutUtils::ExpandHeightForViewportUnits(nsPresContext* aPresContext,
   9896                                                   const nsSize& aSize) {
   9897  nsSize sizeForViewportUnits = aPresContext->GetSizeForViewportUnits();
   9898 
   9899  // |aSize| might be the size expanded to the minimum-scale size whereas the
   9900  // size for viewport units is not scaled so that we need to expand the |aSize|
   9901  // height by multiplying by the ratio of the viewport units height to the
   9902  // visible area height.
   9903  float vhExpansionRatio = (float)sizeForViewportUnits.height /
   9904                           aPresContext->GetVisibleArea().height;
   9905 
   9906  MOZ_ASSERT(aSize.height <= NSCoordSaturatingNonnegativeMultiply(
   9907                                 aSize.height, vhExpansionRatio));
   9908  return nsSize(aSize.width, NSCoordSaturatingNonnegativeMultiply(
   9909                                 aSize.height, vhExpansionRatio));
   9910 }
   9911 
   9912 template <typename SizeType>
   9913 /* static */ SizeType ExpandHeightForDynamicToolbarImpl(
   9914    const nsPresContext* aPresContext, const SizeType& aSize) {
   9915  MOZ_ASSERT(aPresContext);
   9916 
   9917  // This expansion is applicable only for cases where the software keyboard is
   9918  // hidden or the document is `interactive-widget=resizes-content` mode
   9919  // because in other cases the visual viewport size is always smaller than
   9920  // the layout viewport so that there should be room to scroll.
   9921  if (!aPresContext->IsKeyboardHiddenOrResizesContentMode()) {
   9922    return aSize;
   9923  }
   9924 
   9925  LayoutDeviceIntSize displaySize;
   9926  if (RefPtr<MobileViewportManager> MVM =
   9927          aPresContext->PresShell()->GetMobileViewportManager()) {
   9928    displaySize = MVM->DisplaySize();
   9929  } else if (!nsLayoutUtils::GetDocumentViewerSize(aPresContext, displaySize)) {
   9930    return aSize;
   9931  }
   9932 
   9933  float toolbarHeightRatio =
   9934      mozilla::ScreenCoord(aPresContext->GetDynamicToolbarMaxHeight()) /
   9935      mozilla::ViewAs<mozilla::ScreenPixel>(
   9936          displaySize,
   9937          mozilla::PixelCastJustification::LayoutDeviceIsScreenForBounds)
   9938          .height;
   9939 
   9940  SizeType expandedSize = aSize;
   9941  static_assert(std::is_same_v<nsSize, SizeType> ||
   9942                std::is_same_v<CSSSize, SizeType>);
   9943  if constexpr (std::is_same_v<nsSize, SizeType>) {
   9944    expandedSize.height =
   9945        NSCoordSaturatingAdd(aSize.height, aSize.height * toolbarHeightRatio);
   9946  } else if (std::is_same_v<CSSSize, SizeType>) {
   9947    expandedSize.height = aSize.height + aSize.height * toolbarHeightRatio;
   9948  }
   9949  return expandedSize;
   9950 }
   9951 
   9952 CSSSize nsLayoutUtils::ExpandHeightForDynamicToolbar(
   9953    const nsPresContext* aPresContext, const CSSSize& aSize) {
   9954  return ExpandHeightForDynamicToolbarImpl(aPresContext, aSize);
   9955 }
   9956 nsSize nsLayoutUtils::ExpandHeightForDynamicToolbar(
   9957    const nsPresContext* aPresContext, const nsSize& aSize) {
   9958  return ExpandHeightForDynamicToolbarImpl(aPresContext, aSize);
   9959 }
   9960 
   9961 nsRect nsLayoutUtils::GetCombinedFragmentRects(const nsIFrame* aFrame,
   9962                                               bool aRelativeToSelf) {
   9963  bool isPaginated = aFrame->PresContext()->IsPaginated();
   9964 
   9965  // Lazy getter for aFrame's page-frame ancestor, if any.
   9966  Maybe<const nsIFrame*> maybePageFrame;
   9967  auto currPageFrame = [=, &maybePageFrame]() -> const nsIFrame* {
   9968    MOZ_ASSERT(isPaginated);
   9969    if (!maybePageFrame) {
   9970      maybePageFrame.emplace(nsLayoutUtils::GetPageFrame(aFrame));
   9971    }
   9972    return maybePageFrame.ref();
   9973  };
   9974 
   9975  // A continuation is considered "on the same page" if the context is not
   9976  // paginated, or if it has the same page-frame ancestor.
   9977  auto onSamePage = [=](const nsIFrame* aContinuation) -> bool {
   9978    return !isPaginated ||
   9979           nsLayoutUtils::GetPageFrame(aContinuation) == currPageFrame();
   9980  };
   9981 
   9982  // Collect rects from our continuations (limited to those that are on the
   9983  // same page if the context is paginated).
   9984  nsRect rect = aFrame->GetRectRelativeToSelf();
   9985  for (const nsIFrame* f = aFrame->GetNextContinuation(); f && onSamePage(f);
   9986       f = f->GetNextContinuation()) {
   9987    rect = rect.Union(f->GetRectRelativeToSelf() + f->GetOffsetTo(aFrame));
   9988  }
   9989  for (const nsIFrame* f = aFrame->GetPrevContinuation(); f && onSamePage(f);
   9990       f = f->GetPrevContinuation()) {
   9991    rect = rect.Union(f->GetRectRelativeToSelf() + f->GetOffsetTo(aFrame));
   9992  }
   9993 
   9994  return aRelativeToSelf ? rect : rect + aFrame->GetPosition();
   9995 }