Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  nsLayoutUtils.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "nsLayoutUtils.h"

#include <algorithm>
#include <limits>

#include "ActiveLayerTracker.h"
#include "DisplayItemClip.h"
#include "gfx2DGlue.h"
#include "gfxContext.h"
#include "gfxDrawable.h"
#include "gfxEnv.h"
#include "gfxMatrix.h"
#include "gfxPlatform.h"
#include "gfxRect.h"
#include "gfxTypes.h"
#include "gfxUtils.h"
#include "ImageContainer.h"
#include "ImageOps.h"
#include "ImageRegion.h"
#include "imgIContainer.h"
#include "imgIRequest.h"
#include "LayoutLogging.h"
#include "MobileViewportManager.h"
#include "mozilla/AccessibleCaretEventHub.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Baseline.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/DisplayPortUtils.h"
#include "mozilla/glean/GfxMetrics.h"
#include "mozilla/dom/AnonymousContent.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/CanvasUtils.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/DocumentInlines.h"
#include "mozilla/dom/DOMRect.h"
#include "mozilla/dom/DOMStringList.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLBodyElement.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/dom/HTMLImageElement.h"
#include "mozilla/dom/HTMLMediaElementBinding.h"
#include "mozilla/dom/HTMLVideoElement.h"
#include "mozilla/dom/InspectorFontFace.h"
#include "mozilla/dom/ImageBitmap.h"
#include "mozilla/dom/InteractiveWidget.h"
#include "mozilla/dom/KeyframeEffect.h"
#include "mozilla/dom/SVGViewportElement.h"
#include "mozilla/dom/UIEvent.h"
#include "mozilla/dom/VideoFrame.h"
#include "mozilla/dom/VideoFrameBinding.h"
#include "mozilla/intl/BidiEmbeddingLevel.h"
#include "mozilla/EffectCompositor.h"
#include "mozilla/EffectSet.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/PathHelpers.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/layers/APZCCallbackHelper.h"
#include "mozilla/layers/APZPublicUtils.h"  // for apz::CalculatePendingDisplayPort
#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/PAPZ.h"
#include "mozilla/layers/StackingContextHelper.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "mozilla/Likely.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/PerfStats.h"
#include "mozilla/Preferences.h"
#include "mozilla/PresShell.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/RestyleManager.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/ScrollContainerFrame.h"
#include "mozilla/ScrollOrigin.h"
#include "mozilla/ServoStyleSet.h"
#include "mozilla/ServoStyleSetInlines.h"
#include "mozilla/StaticPrefs_apz.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_font.h"
#include "mozilla/StaticPrefs_general.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_image.h"
#include "mozilla/StaticPrefs_layers.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/StyleAnimationValue.h"
#include "mozilla/SVGImageContext.h"
#include "mozilla/SVGIntegrationUtils.h"
#include "mozilla/SVGTextFrame.h"
#include "mozilla/SVGUtils.h"
#include "mozilla/Telemetry.h"
#include "mozilla/ToString.h"
#include "mozilla/Unused.h"
#include "mozilla/ViewportFrame.h"
#include "mozilla/ViewportUtils.h"
#include "mozilla/WheelHandlingHelper.h"  // for WheelHandlingUtils
#include "nsAnimationManager.h"
#include "nsAtom.h"
#include "nsBidiPresUtils.h"
#include "nsBlockFrame.h"
#include "nsCanvasFrame.h"
#include "nsCaret.h"
#include "nsCharTraits.h"
#include "nsCOMPtr.h"
#include "nsComputedDOMStyle.h"
#include "nsContentUtils.h"
#include "nsCSSAnonBoxes.h"
#include "nsCSSColorUtils.h"
#include "nsCSSFrameConstructor.h"
#include "nsCSSProps.h"
#include "nsCSSPseudoElements.h"
#include "nsCSSRendering.h"
#include "nsDisplayList.h"
#include "nsFieldSetFrame.h"
#include "nsFlexContainerFrame.h"
#include "nsFontInflationData.h"
#include "nsFontMetrics.h"
#include "nsFrameList.h"
#include "nsFrameSelection.h"
#include "nsGenericHTMLElement.h"
#include "nsGkAtoms.h"
#include "nsICanvasRenderingContextInternal.h"
#include "nsIContent.h"
#include "nsIContentInlines.h"
#include "nsIDocShell.h"
#include "nsIDocumentViewer.h"
#include "nsIFrameInlines.h"
#include "nsIImageLoadingContent.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIWidget.h"
#include "nsListControlFrame.h"
#include "nsMenuPopupFrame.h"
#include "nsPIDOMWindow.h"
#include "nsPlaceholderFrame.h"
#include "nsPresContext.h"
#include "nsPresContextInlines.h"
#include "nsRefreshDriver.h"
#include "nsRegion.h"
#include "nsStyleConsts.h"
#include "nsStyleStructInlines.h"
#include "nsStyleTransformMatrix.h"
#include "nsSubDocumentFrame.h"
#include "nsTableWrapperFrame.h"
#include "nsTArray.h"
#include "nsTextFragment.h"
#include "nsTextFrame.h"
#include "nsTHashMap.h"
#include "nsTransitionManager.h"
#include "nsView.h"
#include "nsViewManager.h"
#include "prenv.h"
#include "RegionBuilder.h"
#include "RetainedDisplayListBuilder.h"
#include "TextDrawTarget.h"
#include "UnitTransforms.h"
#include "ViewportFrame.h"

#include "nsXULPopupManager.h"

// Make sure getpid() works.
#ifdef XP_WIN
#  include <process.h>
#  define getpid _getpid
#else
#  include <unistd.h>
#endif

using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::image;
using namespace mozilla::layers;
using namespace mozilla::layout;
using namespace mozilla::gfx;
using mozilla::dom::HTMLMediaElement_Binding::HAVE_METADATA;
using mozilla::dom::HTMLMediaElement_Binding::HAVE_NOTHING;

typedef ScrollableLayerGuid::ViewID ViewID;
typedef nsStyleTransformMatrix::TransformReferenceBox TransformReferenceBox;

static ViewID sScrollIdCounter = ScrollableLayerGuid::START_SCROLL_ID;

typedef nsTHashMap<nsUint64HashKey, nsIContent*> ContentMap;
static StaticAutoPtr<ContentMap> sContentMap;

static ContentMap& GetContentMap() {
  if (!sContentMap) {
    sContentMap = new ContentMap();
  }
  return *sContentMap;
}

template <typename TestType>
static bool HasMatchingAnimations(EffectSet& aEffects, TestType&& aTest) {
  for (KeyframeEffect* effect : aEffects) {
    if (!effect->GetAnimation() || !effect->GetAnimation()->IsRelevant()) {
      continue;
    }

    if (aTest(*effect, aEffects)) {
      return true;
    }
  }

  return false;
}

template <typename TestType>
static bool HasMatchingAnimations(const nsIFrame* aFrame,
                                  const nsCSSPropertyIDSet& aPropertySet,
                                  TestType&& aTest) {
  MOZ_ASSERT(aFrame);

  if (!aFrame->MayHaveOpacityAnimation() &&
      aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::OpacityProperties())) {
    return false;
  }

  if (!aFrame->MayHaveTransformAnimation() &&
      aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::TransformLikeProperties())) {
    return false;
  }

  EffectSet* effectSet = EffectSet::GetForFrame(aFrame, aPropertySet);
  if (!effectSet) {
    return false;
  }

  return HasMatchingAnimations(*effectSet, aTest);
}

/* static */
bool nsLayoutUtils::HasAnimationOfPropertySet(
    const nsIFrame* aFrame, const nsCSSPropertyIDSet& aPropertySet) {
  return HasMatchingAnimations(
      aFrame, aPropertySet,
      [&aPropertySet](KeyframeEffect& aEffect, const EffectSet&) {
        return aEffect.HasAnimationOfPropertySet(aPropertySet);
      });
}

/* static */
bool nsLayoutUtils::HasAnimationOfPropertySet(
    const nsIFrame* aFrame, const nsCSSPropertyIDSet& aPropertySet,
    EffectSet* aEffectSet) {
  MOZ_ASSERT(
      !aEffectSet || EffectSet::GetForFrame(aFrame, aPropertySet) == aEffectSet,
      "The EffectSet, if supplied, should match what we would otherwise fetch");

  if (!aEffectSet) {
    return nsLayoutUtils::HasAnimationOfPropertySet(aFrame, aPropertySet);
  }

  if (!aEffectSet->MayHaveTransformAnimation() &&
      aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::TransformLikeProperties())) {
    return false;
  }

  if (!aEffectSet->MayHaveOpacityAnimation() &&
      aPropertySet.IsSubsetOf(nsCSSPropertyIDSet::OpacityProperties())) {
    return false;
  }

  return HasMatchingAnimations(
      *aEffectSet,
      [&aPropertySet](KeyframeEffect& aEffect, const EffectSet& aEffectSet) {
        return aEffect.HasAnimationOfPropertySet(aPropertySet);
      });
}

/* static */
bool nsLayoutUtils::HasAnimationOfTransformAndMotionPath(
    const nsIFrame* aFrame) {
  auto returnValue = [&]() -> bool {
    return nsLayoutUtils::HasAnimationOfPropertySet(
               aFrame,
               nsCSSPropertyIDSet{eCSSProperty_transform,
                                  eCSSProperty_translate, eCSSProperty_rotate,
                                  eCSSProperty_scale,
                                  eCSSProperty_offset_path}) ||
           (!aFrame->StyleDisplay()->mOffsetPath.IsNone() &&
            nsLayoutUtils::HasAnimationOfPropertySet(
                aFrame, nsCSSPropertyIDSet::MotionPathProperties()));
  };

  if (!aFrame->MayHaveTransformAnimation()) {
    MOZ_ASSERT(!returnValue());
    return false;
  }
  return returnValue();
}

/* static */
bool nsLayoutUtils::HasEffectiveAnimation(
    const nsIFrame* aFrame, const nsCSSPropertyIDSet& aPropertySet) {
  return HasMatchingAnimations(
      aFrame, aPropertySet,
      [&aPropertySet](KeyframeEffect& aEffect, const EffectSet& aEffectSet) {
        return aEffect.HasEffectiveAnimationOfPropertySet(aPropertySet,
                                                          aEffectSet);
      });
}

/* static */
nsCSSPropertyIDSet nsLayoutUtils::GetAnimationPropertiesForCompositor(
    const nsIFrame* aStyleFrame) {
  nsCSSPropertyIDSet properties;

  // We fetch the effects for the style frame here since this method is called
  // by RestyleManager::AddLayerChangesForAnimation which takes care to apply
  // the relevant hints to the primary frame as needed.
  EffectSet* effects = EffectSet::GetForStyleFrame(aStyleFrame);
  if (!effects) {
    return properties;
  }

  AnimationPerformanceWarning::Type warning;
  if (!EffectCompositor::AllowCompositorAnimationsOnFrame(aStyleFrame,
                                                          warning)) {
    return properties;
  }

  for (const KeyframeEffect* effect : *effects) {
    properties |= effect->GetPropertiesForCompositor(*effects, aStyleFrame);
  }

  // If properties only have motion-path properties, we have to make sure they
  // have effects. i.e. offset-path is not none or we have offset-path
  // animations.
  if (properties.IsSubsetOf(nsCSSPropertyIDSet::MotionPathProperties()) &&
      !properties.HasProperty(eCSSProperty_offset_path) &&
      aStyleFrame->StyleDisplay()->mOffsetPath.IsNone()) {
    properties.Empty();
  }

  return properties;
}

static float GetSuitableScale(float aMaxScale, float aMinScale,
                              nscoord aVisibleDimension,
                              nscoord aDisplayDimension) {
  float displayVisibleRatio =
      float(aDisplayDimension) / float(aVisibleDimension);
  // We want to rasterize based on the largest scale used during the
  // transform animation, unless that would make us rasterize something
  // larger than the screen.  But we never want to go smaller than the
  // minimum scale over the animation.
  if (FuzzyEqualsMultiplicative(displayVisibleRatio, aMaxScale, .01f)) {
    // Using aMaxScale may make us rasterize something a fraction larger than
    // the screen. However, if aMaxScale happens to be the final scale of a
    // transform animation it is better to use aMaxScale so that for the
    // fraction of a second before we delayerize the composited texture it has
    // a better chance of being pixel aligned and composited without resampling
    // (avoiding visually clunky delayerization).
    return aMaxScale;
  }
  return std::clamp(displayVisibleRatio, aMinScale, aMaxScale);
}

// The first value in this pair is the min scale, and the second one is the max
// scale.
using MinAndMaxScale = std::pair<MatrixScales, MatrixScales>;

static inline void UpdateMinMaxScale(const nsIFrame* aFrame,
                                     const AnimationValue& aValue,
                                     MinAndMaxScale& aMinAndMaxScale) {
  MatrixScales size = aValue.GetScaleValue(aFrame);
  MatrixScales& minScale = aMinAndMaxScale.first;
  MatrixScales& maxScale = aMinAndMaxScale.second;

  minScale = Min(minScale, size);
  maxScale = Max(maxScale, size);
}

// The final transform matrix is calculated by merging the final results of each
// transform-like properties, so do the scale factors. In other words, the
// potential min/max scales could be gotten by multiplying the max/min scales of
// each properties.
//
// For example, there is an animation:
//   from { "transform: scale(1, 1)", "scale: 3, 3" };
//   to   { "transform: scale(2, 2)", "scale: 1, 1" };
//
// the min scale is (1, 1) * (1, 1) = (1, 1), and
// The max scale is (2, 2) * (3, 3) = (6, 6).
// This means we multiply the min/max scale factor of transform property and the
// min/max scale factor of scale property to get the final max/min scale factor.
static Array<MinAndMaxScale, 2> GetMinAndMaxScaleForAnimationProperty(
    const nsIFrame* aFrame,
    const nsTArray<RefPtr<dom::Animation>>& aAnimations) {
  // We use a fixed array to store the min/max scales for each property.
  // The first element in the array is for eCSSProperty_transform, and the
  // second one is for eCSSProperty_scale.
  const MinAndMaxScale defaultValue =
      std::make_pair(MatrixScales(std::numeric_limits<float>::max(),
                                  std::numeric_limits<float>::max()),
                     MatrixScales(std::numeric_limits<float>::min(),
                                  std::numeric_limits<float>::min()));
  Array<MinAndMaxScale, 2> minAndMaxScales(defaultValue, defaultValue);

  for (dom::Animation* anim : aAnimations) {
    // This method is only expected to be passed animations that are running on
    // the compositor and we only pass playing animations to the compositor,
    // which are, by definition, "relevant" animations (animations that are
    // not yet finished or which are filling forwards).
    MOZ_ASSERT(anim->IsRelevant());

    const dom::KeyframeEffect* effect =
        anim->GetEffect() ? anim->GetEffect()->AsKeyframeEffect() : nullptr;
    MOZ_ASSERT(effect, "A playing animation should have a keyframe effect");
    for (const AnimationProperty& prop : effect->Properties()) {
      if (prop.mProperty.mID != eCSSProperty_transform &&
          prop.mProperty.mID != eCSSProperty_scale) {
        continue;
      }

      // 0: eCSSProperty_transform.
      // 1: eCSSProperty_scale.
      MinAndMaxScale& scales =
          minAndMaxScales[prop.mProperty.mID == eCSSProperty_transform ? 0 : 1];

      // We need to factor in the scale of the base style if the base style
      // will be used on the compositor.
      const AnimationValue& baseStyle = effect->BaseStyle(prop.mProperty);
      if (!baseStyle.IsNull()) {
        UpdateMinMaxScale(aFrame, baseStyle, scales);
      }

      for (const AnimationPropertySegment& segment : prop.mSegments) {
        // In case of add or accumulate composite, StyleAnimationValue does
        // not have a valid value.
        if (segment.HasReplaceableFromValue()) {
          UpdateMinMaxScale(aFrame, segment.mFromValue, scales);
        }

        if (segment.HasReplaceableToValue()) {
          UpdateMinMaxScale(aFrame, segment.mToValue, scales);
        }
      }
    }
  }

  return minAndMaxScales;
}

MatrixScales nsLayoutUtils::ComputeSuitableScaleForAnimation(
    const nsIFrame* aFrame, const nsSize& aVisibleSize,
    const nsSize& aDisplaySize) {
  const nsTArray<RefPtr<dom::Animation>> compositorAnimations =
      EffectCompositor::GetAnimationsForCompositor(
          aFrame,
          nsCSSPropertyIDSet{eCSSProperty_transform, eCSSProperty_scale});

  if (compositorAnimations.IsEmpty()) {
    return MatrixScales();
  }

  const Array<MinAndMaxScale, 2> minAndMaxScales =
      GetMinAndMaxScaleForAnimationProperty(aFrame, compositorAnimations);

  // This might cause an issue if users use std::numeric_limits<float>::min()
  // (or max()) as the scale value. However, in this case, we may render an
  // extreme small (or large) element, so this may not be a problem. If so,
  // please fix this.
  MatrixScales maxScale(std::numeric_limits<float>::min(),
                        std::numeric_limits<float>::min());
  MatrixScales minScale(std::numeric_limits<float>::max(),
                        std::numeric_limits<float>::max());

  auto isUnset = [](const MatrixScales& aMax, const MatrixScales& aMin) {
    return aMax.xScale == std::numeric_limits<float>::min() &&
           aMax.yScale == std::numeric_limits<float>::min() &&
           aMin.xScale == std::numeric_limits<float>::max() &&
           aMin.yScale == std::numeric_limits<float>::max();
  };

  // Iterate the slots to get the final scale value.
  for (const auto& pair : minAndMaxScales) {
    const MatrixScales& currMinScale = pair.first;
    const MatrixScales& currMaxScale = pair.second;

    if (isUnset(currMaxScale, currMinScale)) {
      // We don't have this animation property, so skip.
      continue;
    }

    if (isUnset(maxScale, minScale)) {
      // Initialize maxScale and minScale.
      maxScale = currMaxScale;
      minScale = currMinScale;
    } else {
      // The scale factors of each transform-like property should be multiplied
      // by others because we merge their sampled values as a final matrix by
      // matrix multiplication, so here we multiply the scale factors by the
      // previous one to get the possible max and min scale factors.
      maxScale = maxScale * currMaxScale;
      minScale = minScale * currMinScale;
    }
  }

  if (isUnset(maxScale, minScale)) {
    // We didn't encounter any transform-like property.
    return MatrixScales();
  }

  return MatrixScales(
      GetSuitableScale(maxScale.xScale, minScale.xScale, aVisibleSize.width,
                       aDisplaySize.width),
      GetSuitableScale(maxScale.yScale, minScale.yScale, aVisibleSize.height,
                       aDisplaySize.height));
}

bool nsLayoutUtils::AreAsyncAnimationsEnabled() {
  return StaticPrefs::layers_offmainthreadcomposition_async_animations() &&
         gfxPlatform::OffMainThreadCompositingEnabled();
}

bool nsLayoutUtils::AreRetainedDisplayListsEnabled() {
#ifdef MOZ_WIDGET_ANDROID
  return StaticPrefs::layout_display_list_retain();
#else
  if (XRE_IsContentProcess()) {
    return StaticPrefs::layout_display_list_retain();
  }

  if (XRE_IsE10sParentProcess()) {
    return StaticPrefs::layout_display_list_retain_chrome();
  }

  // Retained display lists require e10s.
  return false;
#endif
}

bool nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(nsIFrame* aFrame) {
  return GetRetainedDisplayListBuilder(aFrame) != nullptr;
}

RetainedDisplayListBuilder* nsLayoutUtils::GetRetainedDisplayListBuilder(
    nsIFrame* aFrame) {
  MOZ_ASSERT(aFrame);
  MOZ_ASSERT(aFrame->PresShell());

  // Use the pres shell root frame to get the display root frame. This skips
  // the early exit in |nsLayoutUtils::GetDisplayRootFrame()| for popup frames.
  const nsIFrame* rootFrame = aFrame->PresShell()->GetRootFrame();
  if (!rootFrame) {
    return nullptr;
  }

  const nsIFrame* displayRootFrame = GetDisplayRootFrame(rootFrame);
  MOZ_ASSERT(displayRootFrame);

  return displayRootFrame->GetProperty(RetainedDisplayListBuilder::Cached());
}

void nsLayoutUtils::UnionChildOverflow(nsIFrame* aFrame,
                                       OverflowAreas& aOverflowAreas,
                                       FrameChildListIDs aSkipChildLists) {
  for (const auto& [list, listID] : aFrame->ChildLists()) {
    if (aSkipChildLists.contains(listID)) {
      continue;
    }
    for (nsIFrame* child : list) {
      aOverflowAreas.UnionWith(
          child->GetActualAndNormalOverflowAreasRelativeToParent());
    }
  }
}

static void DestroyViewID(void* aObject, nsAtom* aPropertyName,
                          void* aPropertyValue, void* aData) {
  ViewID* id = static_cast<ViewID*>(aPropertyValue);
  GetContentMap().Remove(*id);
  delete id;
}

/**
 * A namespace class for static layout utilities.
 */


bool nsLayoutUtils::FindIDFor(const nsIContent* aContent, ViewID* aOutViewId) {
  void* scrollIdProperty = aContent->GetProperty(nsGkAtoms::RemoteId);
  if (scrollIdProperty) {
    *aOutViewId = *static_cast<ViewID*>(scrollIdProperty);
    return true;
  }
  return false;
}

ViewID nsLayoutUtils::FindOrCreateIDFor(nsIContent* aContent) {
  ViewID scrollId;

  if (!FindIDFor(aContent, &scrollId)) {
    scrollId = sScrollIdCounter++;
    aContent->SetProperty(nsGkAtoms::RemoteId, new ViewID(scrollId),
                          DestroyViewID);
    GetContentMap().InsertOrUpdate(scrollId, aContent);
  }

  return scrollId;
}

nsIContent* nsLayoutUtils::FindContentFor(ViewID aId) {
  MOZ_ASSERT(aId != ScrollableLayerGuid::NULL_SCROLL_ID,
             "Cannot find a content element in map for null IDs.");
  nsIContent* content;
  bool exists = GetContentMap().Get(aId, &content);

  if (exists) {
    return content;
  } else {
    return nullptr;
  }
}

nsIFrame* nsLayoutUtils::GetScrollContainerFrameFromContent(
    nsIContent* aContent) {
  nsIFrame* frame = aContent->GetPrimaryFrame();
  if (aContent->OwnerDoc()->GetRootElement() == aContent) {
    PresShell* presShell = frame ? frame->PresShell() : nullptr;
    if (!presShell) {
      presShell = aContent->OwnerDoc()->GetPresShell();
    }
    // We want the scroll container frame, the root scroll frame differs from
    // all others in that the primary frame is not the scroll frame.
    nsIFrame* rootScrollContainerFrame =
        presShell ? presShell->GetRootScrollContainerFrame() : nullptr;
    if (rootScrollContainerFrame) {
      frame = rootScrollContainerFrame;
    }
  }
  return frame;
}

ScrollContainerFrame* nsLayoutUtils::FindScrollContainerFrameFor(
    nsIContent* aContent) {
  nsIFrame* scrollContainerFrame = GetScrollContainerFrameFromContent(aContent);
  return scrollContainerFrame ? scrollContainerFrame->GetScrollTargetFrame()
                              : nullptr;
}

ScrollContainerFrame* nsLayoutUtils::FindScrollContainerFrameFor(ViewID aId) {
  nsIContent* content = FindContentFor(aId);
  if (!content) {
    return nullptr;
  }

  return FindScrollContainerFrameFor(content);
}

ViewID nsLayoutUtils::FindIDForScrollContainerFrame(
    ScrollContainerFrame* aScrollContainerFrame) {
  if (!aScrollContainerFrame) {
    return ScrollableLayerGuid::NULL_SCROLL_ID;
  }

  nsIContent* scrollContent = aScrollContainerFrame->GetContent();

  ScrollableLayerGuid::ViewID scrollId;
  if (scrollContent && nsLayoutUtils::FindIDFor(scrollContent, &scrollId)) {
    return scrollId;
  }

  return ScrollableLayerGuid::NULL_SCROLL_ID;
}

bool nsLayoutUtils::UsesAsyncScrolling(nsIFrame* aFrame) {
#ifdef MOZ_WIDGET_ANDROID
  // We always have async scrolling for android
  return true;
#endif

  return AsyncPanZoomEnabled(aFrame);
}

bool nsLayoutUtils::AsyncPanZoomEnabled(const nsIFrame* aFrame) {
  // We use this as a shortcut, since if the compositor will never use APZ,
  // no widget will either.
  if (!gfxPlatform::AsyncPanZoomEnabled()) {
    return false;
  }

  const nsIFrame* frame = nsLayoutUtils::GetDisplayRootFrame(aFrame);
  nsIWidget* widget = frame->GetNearestWidget();
  if (!widget) {
    return false;
  }
  return widget->AsyncPanZoomEnabled();
}

bool nsLayoutUtils::AllowZoomingForDocument(
    const mozilla::dom::Document* aDocument) {
  if (aDocument->GetPresShell() &&
      !aDocument->GetPresShell()->AsyncPanZoomEnabled()) {
    return false;
  }
  // True if we allow zooming for all documents on this platform, or if we are
  // in RDM.
  BrowsingContext* bc = aDocument->GetBrowsingContext();
  return StaticPrefs::apz_allow_zooming() || (bc && bc->InRDMPane());
}

static bool HasVisibleAnonymousContents(Document* aDoc) {
  for (RefPtr<AnonymousContent>& ac : aDoc->GetAnonymousContents()) {
    // We check to see if the anonymous content node has a frame. If it doesn't,
    // that means that's not visible to the user because e.g. it's display:none.
    // For now we assume that if it has a frame, it is visible. We might be able
    // to refine this further by adding complexity if it turns out this
    // condition results in a lot of false positives.
    if (ac->Host()->GetPrimaryFrame()) {
      return true;
    }
  }
  return false;
}

bool nsLayoutUtils::ShouldDisableApzForElement(nsIContent* aContent) {
  if (!aContent) {
    return false;
  }

  if (aContent->GetProperty(nsGkAtoms::apzDisabled)) {
    return true;
  }

  Document* doc = aContent->GetComposedDoc();
  if (PresShell* rootPresShell =
          APZCCallbackHelper::GetRootContentDocumentPresShellForContent(
              aContent)) {
    if (Document* rootDoc = rootPresShell->GetDocument()) {
      nsIFrame* rootScrollContainerFrame =
          rootPresShell->GetRootScrollContainerFrame();
      nsIContent* rootContent = rootScrollContainerFrame
                                    ? rootScrollContainerFrame->GetContent()
                                    : rootDoc->GetDocumentElement();
      // For the AccessibleCaret and other anonymous contents: disable APZ on
      // any scrollable subframes that are not the root scrollframe of a
      // document, if the document has any visible anonymous contents.
      //
      // If we find this is triggering in too many scenarios then we might
      // want to tighten this check further. The main use cases for which we
      // want to disable APZ as of this writing are listed in bug 1316318.
      if (aContent != rootContent && HasVisibleAnonymousContents(rootDoc)) {
        return true;
      }
    }
  }

  if (!doc) {
    return false;
  }

  if (PresShell* presShell = doc->GetPresShell()) {
    if (RefPtr<AccessibleCaretEventHub> eventHub =
            presShell->GetAccessibleCaretEventHub()) {
      // Disable APZ for all elements if AccessibleCaret tells us to do so.
      if (eventHub->ShouldDisableApz()) {
        return true;
      }
    }
  }

  return StaticPrefs::apz_disable_for_scroll_linked_effects() &&
         doc->HasScrollLinkedEffect();
}

void nsLayoutUtils::NotifyPaintSkipTransaction(ViewID aScrollId) {
  if (ScrollContainerFrame* sf =
          nsLayoutUtils::FindScrollContainerFrameFor(aScrollId)) {
    MOZ_ASSERT(sf && sf->PresShell() &&
               !sf->PresShell()->IsResolutionUpdated());
    sf->NotifyApzTransaction();
  }
}

nsContainerFrame* nsLayoutUtils::LastContinuationWithChild(
    nsContainerFrame* aFrame) {
  MOZ_ASSERT(aFrame, "NULL frame pointer");
  for (auto f = aFrame->LastContinuation(); f; f = f->GetPrevContinuation()) {
    for (const auto& childList : f->ChildLists()) {
      if (MOZ_LIKELY(!childList.mList.IsEmpty())) {
        return static_cast<nsContainerFrame*>(f);
      }
    }
  }
  return aFrame;
}

// static
FrameChildListID nsLayoutUtils::GetChildListNameFor(nsIFrame* aChildFrame) {
  FrameChildListID id = FrameChildListID::Principal;

  MOZ_DIAGNOSTIC_ASSERT(!aChildFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));

  if (aChildFrame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
    nsIFrame* pif = aChildFrame->GetPrevInFlow();
    if (pif->GetParent() == aChildFrame->GetParent()) {
      id = FrameChildListID::ExcessOverflowContainers;
    } else {
      id = FrameChildListID::OverflowContainers;
    }
  } else {
    LayoutFrameType childType = aChildFrame->Type();
    if (LayoutFrameType::TableColGroup == childType) {
      id = FrameChildListID::ColGroup;
    } else if (aChildFrame->IsTableCaption()) {
      id = FrameChildListID::Caption;
    } else {
      id = FrameChildListID::Principal;
    }
  }

#ifdef DEBUG
  // Verify that the frame is actually in that child list or in the
  // corresponding overflow list.
  nsContainerFrame* parent = aChildFrame->GetParent();
  bool found = parent->GetChildList(id).ContainsFrame(aChildFrame);
  if (!found) {
    found = parent->GetChildList(FrameChildListID::Overflow)
                .ContainsFrame(aChildFrame);
    MOZ_ASSERT(found, "not in child list");
  }
#endif

  return id;
}

static Element* GetPseudo(const nsIContent* aContent, nsAtom* aPseudoProperty) {
  MOZ_ASSERT(aPseudoProperty == nsGkAtoms::beforePseudoProperty ||
             aPseudoProperty == nsGkAtoms::afterPseudoProperty ||
             aPseudoProperty == nsGkAtoms::markerPseudoProperty);
  if (!aContent->MayHaveAnonymousChildren()) {
    return nullptr;
  }
  return static_cast<Element*>(aContent->GetProperty(aPseudoProperty));
}

/*static*/
Element* nsLayoutUtils::GetBeforePseudo(const nsIContent* aContent) {
  return GetPseudo(aContent, nsGkAtoms::beforePseudoProperty);
}

/*static*/
nsIFrame* nsLayoutUtils::GetBeforeFrame(const nsIContent* aContent) {
  Element* pseudo = GetBeforePseudo(aContent);
  return pseudo ? pseudo->GetPrimaryFrame() : nullptr;
}

/*static*/
Element* nsLayoutUtils::GetAfterPseudo(const nsIContent* aContent) {
  return GetPseudo(aContent, nsGkAtoms::afterPseudoProperty);
}

/*static*/
nsIFrame* nsLayoutUtils::GetAfterFrame(const nsIContent* aContent) {
  Element* pseudo = GetAfterPseudo(aContent);
  return pseudo ? pseudo->GetPrimaryFrame() : nullptr;
}

/*static*/
Element* nsLayoutUtils::GetMarkerPseudo(const nsIContent* aContent) {
  return GetPseudo(aContent, nsGkAtoms::markerPseudoProperty);
}

/*static*/
nsIFrame* nsLayoutUtils::GetMarkerFrame(const nsIContent* aContent) {
  Element* pseudo = GetMarkerPseudo(aContent);
  return pseudo ? pseudo->GetPrimaryFrame() : nullptr;
}

#ifdef ACCESSIBILITY
void nsLayoutUtils::GetMarkerSpokenText(const nsIContent* aContent,
                                        nsAString& aText) {
  MOZ_ASSERT(aContent && aContent->IsGeneratedContentContainerForMarker());

  aText.Truncate();

  nsIFrame* frame = aContent->GetPrimaryFrame();
  if (!frame) {
    return;
  }

  if (!frame->StyleContent()->NonAltContentItems().IsEmpty()) {
    for (nsIFrame* child : frame->PrincipalChildList()) {
      nsIFrame::RenderedText text = child->GetRenderedText();
      aText += text.mString;
    }
    return;
  }

  if (!frame->StyleList()->mListStyleImage.IsNone()) {
    // ::marker is an image, so use default bullet character.
    static const char16_t kDiscMarkerString[] = {0x2022, ' ', 0};
    aText.AssignLiteral(kDiscMarkerString);
    return;
  }

  frame->PresContext()
      ->FrameConstructor()
      ->GetContainStyleScopeManager()
      .GetSpokenCounterText(frame, aText);
}
#endif

// static
nsIFrame* nsLayoutUtils::GetClosestFrameOfType(nsIFrame* aFrame,
                                               LayoutFrameType aFrameType,
                                               nsIFrame* aStopAt) {
  for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
    if (frame->Type() == aFrameType) {
      return frame;
    }
    if (frame == aStopAt) {
      break;
    }
  }
  return nullptr;
}

/* static */
nsIFrame* nsLayoutUtils::GetPageFrame(nsIFrame* aFrame) {
  return GetClosestFrameOfType(aFrame, LayoutFrameType::Page);
}

/* static */
nsIFrame* nsLayoutUtils::GetStyleFrame(nsIFrame* aPrimaryFrame) {
  MOZ_ASSERT(aPrimaryFrame);
  if (aPrimaryFrame->IsTableWrapperFrame()) {
    nsIFrame* inner = aPrimaryFrame->PrincipalChildList().FirstChild();
    // inner may be null, if aPrimaryFrame is mid-destruction
    return inner;
  }

  return aPrimaryFrame;
}

const nsIFrame* nsLayoutUtils::GetStyleFrame(const nsIFrame* aPrimaryFrame) {
  return nsLayoutUtils::GetStyleFrame(const_cast<nsIFrame*>(aPrimaryFrame));
}

nsIFrame* nsLayoutUtils::GetStyleFrame(const nsIContent* aContent) {
  nsIFrame* frame = aContent->GetPrimaryFrame();
  if (!frame) {
    return nullptr;
  }

  return nsLayoutUtils::GetStyleFrame(frame);
}

CSSIntCoord nsLayoutUtils::UnthemedScrollbarSize(StyleScrollbarWidth aWidth) {
  switch (aWidth) {
    case StyleScrollbarWidth::Auto:
      return 12;
    case StyleScrollbarWidth::Thin:
      return 6;
    case StyleScrollbarWidth::None:
      return 0;
  }
  return 0;
}

/* static */
nsIFrame* nsLayoutUtils::GetPrimaryFrameFromStyleFrame(nsIFrame* aStyleFrame) {
  nsIFrame* parent = aStyleFrame->GetParent();
  return parent && parent->IsTableWrapperFrame() ? parent : aStyleFrame;
}

/* static */
const nsIFrame* nsLayoutUtils::GetPrimaryFrameFromStyleFrame(
    const nsIFrame* aStyleFrame) {
  return nsLayoutUtils::GetPrimaryFrameFromStyleFrame(
      const_cast<nsIFrame*>(aStyleFrame));
}

/*static*/
bool nsLayoutUtils::IsPrimaryStyleFrame(const nsIFrame* aFrame) {
  if (aFrame->IsTableWrapperFrame()) {
    return false;
  }

  const nsIFrame* parent = aFrame->GetParent();
  if (parent && parent->IsTableWrapperFrame()) {
    return parent->PrincipalChildList().FirstChild() == aFrame;
  }

  return aFrame->IsPrimaryFrame();
}

nsIFrame* nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) {
  NS_ASSERTION(aFrame->IsPlaceholderFrame(), "Must have a placeholder here");
  if (aFrame->HasAnyStateBits(PLACEHOLDER_FOR_FLOAT)) {
    nsIFrame* outOfFlowFrame =
        nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
    NS_ASSERTION(outOfFlowFrame && outOfFlowFrame->IsFloating(),
                 "How did that happen?");
    return outOfFlowFrame;
  }

  return nullptr;
}

// static
nsIFrame* nsLayoutUtils::GetCrossDocParentFrameInProcess(
    const nsIFrame* aFrame, nsPoint* aCrossDocOffset) {
  nsIFrame* p = aFrame->GetParent();
  if (p) {
    return p;
  }

  nsView* v = aFrame->GetView();
  if (!v) {
    return nullptr;
  }
  v = v->GetParent();  // anonymous inner view
  if (!v) {
    return nullptr;
  }
  v = v->GetParent();  // subdocumentframe's view
  if (!v) {
    return nullptr;
  }

  p = v->GetFrame();
  if (p && aCrossDocOffset) {
    nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(p);
    MOZ_ASSERT(subdocumentFrame);
    *aCrossDocOffset += subdocumentFrame->GetExtraOffset();
  }

  return p;
}

// static
nsIFrame* nsLayoutUtils::GetCrossDocParentFrame(const nsIFrame* aFrame,
                                                nsPoint* aCrossDocOffset) {
  return GetCrossDocParentFrameInProcess(aFrame, aCrossDocOffset);
}

// static
bool nsLayoutUtils::IsProperAncestorFrameCrossDoc(
    const nsIFrame* aAncestorFrame, const nsIFrame* aFrame,
    const nsIFrame* aCommonAncestor) {
  if (aFrame == aAncestorFrame) {
    return false;
  }
  return IsAncestorFrameCrossDoc(aAncestorFrame, aFrame, aCommonAncestor);
}

// static
bool nsLayoutUtils::IsProperAncestorFrameCrossDocInProcess(
    const nsIFrame* aAncestorFrame, const nsIFrame* aFrame,
    const nsIFrame* aCommonAncestor) {
  if (aFrame == aAncestorFrame) {
    return false;
  }
  return IsAncestorFrameCrossDocInProcess(aAncestorFrame, aFrame,
                                          aCommonAncestor);
}

// static
bool nsLayoutUtils::IsAncestorFrameCrossDoc(const nsIFrame* aAncestorFrame,
                                            const nsIFrame* aFrame,
                                            const nsIFrame* aCommonAncestor) {
  for (const nsIFrame* f = aFrame; f != aCommonAncestor;
       f = GetCrossDocParentFrameInProcess(f)) {
    if (f == aAncestorFrame) {
      return true;
    }
  }
  return aCommonAncestor == aAncestorFrame;
}

// static
bool nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
    const nsIFrame* aAncestorFrame, const nsIFrame* aFrame,
    const nsIFrame* aCommonAncestor) {
  for (const nsIFrame* f = aFrame; f != aCommonAncestor;
       f = GetCrossDocParentFrameInProcess(f)) {
    if (f == aAncestorFrame) {
      return true;
    }
  }
  return aCommonAncestor == aAncestorFrame;
}

// static
bool nsLayoutUtils::IsProperAncestorFrame(const nsIFrame* aAncestorFrame,
                                          const nsIFrame* aFrame,
                                          const nsIFrame* aCommonAncestor) {
  if (aFrame == aAncestorFrame) {
    return false;
  }
  for (const nsIFrame* f = aFrame; f != aCommonAncestor; f = f->GetParent()) {
    if (f == aAncestorFrame) {
      return true;
    }
  }
  return aCommonAncestor == aAncestorFrame;
}

// static
nsIFrame* nsLayoutUtils::FillAncestors(nsIFrame* aFrame,
                                       nsIFrame* aStopAtAncestor,
                                       nsTArray<nsIFrame*>* aAncestors) {
  while (aFrame && aFrame != aStopAtAncestor) {
    aAncestors->AppendElement(aFrame);
    aFrame = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);
  }
  return aFrame;
}

// Return true if aFrame1 is after aFrame2
static bool IsFrameAfter(nsIFrame* aFrame1, nsIFrame* aFrame2) {
  nsIFrame* f = aFrame2;
  do {
    f = f->GetNextSibling();
    if (f == aFrame1) {
      return true;
    }
  } while (f);
  return false;
}

// static
int32_t nsLayoutUtils::DoCompareTreePosition(nsIFrame* aFrame1,
                                             nsIFrame* aFrame2,
                                             nsIFrame* aCommonAncestor) {
  MOZ_ASSERT(aFrame1, "aFrame1 must not be null");
  MOZ_ASSERT(aFrame2, "aFrame2 must not be null");

  AutoTArray<nsIFrame*, 20> frame2Ancestors;
  nsIFrame* nonCommonAncestor =
      FillAncestors(aFrame2, aCommonAncestor, &frame2Ancestors);
  return DoCompareTreePosition(aFrame1, aFrame2, frame2Ancestors,
                               nonCommonAncestor ? aCommonAncestor : nullptr);
}

// static
int32_t nsLayoutUtils::DoCompareTreePosition(
    nsIFrame* aFrame1, nsIFrame* aFrame2, nsTArray<nsIFrame*>& aFrame2Ancestors,
    nsIFrame* aCommonAncestor) {
  MOZ_ASSERT(aFrame1, "aFrame1 must not be null");
  MOZ_ASSERT(aFrame2, "aFrame2 must not be null");

  nsPresContext* presContext = aFrame1->PresContext();
  if (presContext != aFrame2->PresContext()) {
    NS_ERROR("no common ancestor at all, different documents");
    return 0;
  }

  AutoTArray<nsIFrame*, 20> frame1Ancestors;
  if (aCommonAncestor &&
      !FillAncestors(aFrame1, aCommonAncestor, &frame1Ancestors)) {
    // We reached the root of the frame tree ... if aCommonAncestor was set,
    // it is wrong
    return DoCompareTreePosition(aFrame1, aFrame2, nullptr);
  }

  int32_t last1 = int32_t(frame1Ancestors.Length()) - 1;
  int32_t last2 = int32_t(aFrame2Ancestors.Length()) - 1;
  while (last1 >= 0 && last2 >= 0 &&
         frame1Ancestors[last1] == aFrame2Ancestors[last2]) {
    last1--;
    last2--;
  }

  if (last1 < 0) {
    if (last2 < 0) {
      NS_ASSERTION(aFrame1 == aFrame2, "internal error?");
      return 0;
    }
    // aFrame1 is an ancestor of aFrame2
    return -1;
  }

  if (last2 < 0) {
    // aFrame2 is an ancestor of aFrame1
    return 1;
  }

  nsIFrame* ancestor1 = frame1Ancestors[last1];
  nsIFrame* ancestor2 = aFrame2Ancestors[last2];
  // Now we should be able to walk sibling chains to find which one is first
  if (IsFrameAfter(ancestor2, ancestor1)) {
    return -1;
  }
  if (IsFrameAfter(ancestor1, ancestor2)) {
    return 1;
  }
  NS_WARNING("Frames were in different child lists???");
  return 0;
}

// static
nsIFrame* nsLayoutUtils::GetLastSibling(nsIFrame* aFrame) {
  if (!aFrame) {
    return nullptr;
  }

  nsIFrame* next;
  while ((next = aFrame->GetNextSibling()) != nullptr) {
    aFrame = next;
  }
  return aFrame;
}

// static
nsView* nsLayoutUtils::FindSiblingViewFor(nsView* aParentView,
                                          nsIFrame* aFrame) {
  nsIFrame* parentViewFrame = aParentView->GetFrame();
  nsIContent* parentViewContent =
      parentViewFrame ? parentViewFrame->GetContent() : nullptr;
  for (nsView* insertBefore = aParentView->GetFirstChild(); insertBefore;
       insertBefore = insertBefore->GetNextSibling()) {
    nsIFrame* f = insertBefore->GetFrame();
    if (!f) {
      // this view could be some anonymous view attached to a meaningful parent
      for (nsView* searchView = insertBefore->GetParent(); searchView;
           searchView = searchView->GetParent()) {
        f = searchView->GetFrame();
        if (f) {
          break;
        }
      }
      NS_ASSERTION(f, "Can't find a frame anywhere!");
    }
    if (!f || !aFrame->GetContent() || !f->GetContent() ||
        nsContentUtils::CompareTreePosition<TreeKind::Flat>(
            aFrame->GetContent(), f->GetContent(), parentViewContent) > 0) {
      // aFrame's content is after f's content (or we just don't know),
      // so put our view before f's view
      return insertBefore;
    }
  }
  return nullptr;
}

// static
ScrollContainerFrame* nsLayoutUtils::GetScrollContainerFrameFor(
    const nsIFrame* aScrolledFrame) {
  nsIFrame* frame = aScrolledFrame->GetParent();
  ScrollContainerFrame* sf = do_QueryFrame(frame);
  return (sf && sf->GetScrolledFrame() == aScrolledFrame) ? sf : nullptr;
}

/* static */
SideBits nsLayoutUtils::GetSideBitsForFixedPositionContent(
    const nsIFrame* aFixedPosFrame) {
  SideBits sides = SideBits::eNone;
  if (aFixedPosFrame) {
    const nsStylePosition* position = aFixedPosFrame->StylePosition();
    if (!position
             ->GetAnchorResolvedInset(eSideRight, StylePositionProperty::Fixed)
             .IsAuto()) {
      sides |= SideBits::eRight;
    }
    if (!position
             ->GetAnchorResolvedInset(eSideLeft, StylePositionProperty::Fixed)
             .IsAuto()) {
      sides |= SideBits::eLeft;
    }
    if (!position
             ->GetAnchorResolvedInset(eSideBottom, StylePositionProperty::Fixed)
             .IsAuto()) {
      sides |= SideBits::eBottom;
    }
    if (!position
             ->GetAnchorResolvedInset(eSideTop, StylePositionProperty::Fixed)
             .IsAuto()) {
      sides |= SideBits::eTop;
    }
  }
  return sides;
}

ScrollableLayerGuid::ViewID nsLayoutUtils::ScrollIdForRootScrollFrame(
    nsPresContext* aPresContext) {
  ViewID id = ScrollableLayerGuid::NULL_SCROLL_ID;
  if (nsIFrame* rootScrollFrame =
          aPresContext->PresShell()->GetRootScrollContainerFrame()) {
    if (nsIContent* content = rootScrollFrame->GetContent()) {
      id = FindOrCreateIDFor(content);
    }
  }
  return id;
}

// static
ScrollContainerFrame* nsLayoutUtils::GetNearestScrollableFrameForDirection(
    nsIFrame* aFrame, ScrollDirections aDirections) {
  NS_ASSERTION(
      aFrame, "GetNearestScrollableFrameForDirection expects a non-null frame");
  // FIXME Bug 1714720 : This nearest scroll target is not going to work over
  // process boundaries, in such cases we need to hand over in APZ side.
  for (nsIFrame* f = aFrame; f;
       f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
    ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(f);
    if (scrollContainerFrame) {
      ScrollDirections directions =
          scrollContainerFrame
              ->GetAvailableScrollingDirectionsForUserInputEvents();
      if (aDirections.contains(ScrollDirection::eVertical)) {
        if (directions.contains(ScrollDirection::eVertical)) {
          return scrollContainerFrame;
        }
      }
      if (aDirections.contains(ScrollDirection::eHorizontal)) {
        if (directions.contains(ScrollDirection::eHorizontal)) {
          return scrollContainerFrame;
        }
      }
    }
  }
  return nullptr;
}

static nsIFrame* GetNearestScrollableOrOverflowClipFrame(
    nsIFrame* aFrame, uint32_t aFlags,
    const std::function<bool(const nsIFrame* aCurrentFrame)>& aClipFrameCheck =
        nullptr) {
  MOZ_ASSERT(
      aFrame,
      "GetNearestScrollableOrOverflowClipFrame expects a non-null frame");

  auto GetNextFrame = [aFlags](const nsIFrame* aFrame) -> nsIFrame* {
    return (aFlags & nsLayoutUtils::SCROLLABLE_SAME_DOC)
               ? aFrame->GetParent()
               : nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
  };

  for (nsIFrame* f = aFrame; f; f = GetNextFrame(f)) {
    if (aClipFrameCheck && aClipFrameCheck(f)) {
      return f;
    }

    if ((aFlags & nsLayoutUtils::SCROLLABLE_STOP_AT_PAGE) && f->IsPageFrame()) {
      break;
    }

    // TODO: We should also stop at popup frames other than
    // SCROLLABLE_ONLY_ASYNC_SCROLLABLE cases.
    if ((aFlags & nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE) &&
        f->IsMenuPopupFrame()) {
      break;
    }

    if (ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(f)) {
      if (aFlags & nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE) {
        if (scrollContainerFrame->WantAsyncScroll()) {
          return f;
        }
      } else {
        ScrollStyles ss = scrollContainerFrame->GetScrollStyles();
        if ((aFlags & nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN) ||
            ss.mVertical != StyleOverflow::Hidden ||
            ss.mHorizontal != StyleOverflow::Hidden) {
          return f;
        }
      }
      if (aFlags & nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT) {
        PresShell* presShell = f->PresShell();
        if (presShell->GetRootScrollContainerFrame() == f &&
            presShell->GetDocument() &&
            presShell->GetDocument()->IsRootDisplayDocument()) {
          return f;
        }
      }
    }
    if ((aFlags & nsLayoutUtils::SCROLLABLE_FIXEDPOS_FINDS_ROOT) &&
        f->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
        nsLayoutUtils::IsReallyFixedPos(f)) {
      return f->PresShell()->GetRootScrollContainerFrame();
    }
  }
  return nullptr;
}

// static
ScrollContainerFrame* nsLayoutUtils::GetNearestScrollContainerFrame(
    nsIFrame* aFrame, uint32_t aFlags) {
  nsIFrame* found = GetNearestScrollableOrOverflowClipFrame(aFrame, aFlags);
  if (!found) {
    return nullptr;
  }

  return do_QueryFrame(found);
}

// static
nsIFrame* nsLayoutUtils::GetNearestOverflowClipFrame(nsIFrame* aFrame) {
  return GetNearestScrollableOrOverflowClipFrame(
      aFrame, SCROLLABLE_SAME_DOC | SCROLLABLE_INCLUDE_HIDDEN,
      [](const nsIFrame* currentFrame) -> bool {
        // In cases of SVG Inner/Outer frames it basically clips descendants
        // unless overflow: visible is explicitly specified.
        LayoutFrameType type = currentFrame->Type();
        return ((type == LayoutFrameType::SVGOuterSVG ||
                 type == LayoutFrameType::SVGInnerSVG) &&
                (currentFrame->StyleDisplay()->mOverflowX !=
                     StyleOverflow::Visible &&
                 currentFrame->StyleDisplay()->mOverflowY !=
                     StyleOverflow::Visible));
      });
}

// static
bool nsLayoutUtils::HasPseudoStyle(nsIContent* aContent,
                                   ComputedStyle* aComputedStyle,
                                   PseudoStyleType aPseudoElement,
                                   nsPresContext* aPresContext) {
  MOZ_ASSERT(aPresContext, "Must have a prescontext");

  RefPtr<ComputedStyle> pseudoContext;
  if (aContent) {
    pseudoContext = aPresContext->StyleSet()->ProbePseudoElementStyle(
        *aContent->AsElement(), aPseudoElement, nullptr, aComputedStyle);
  }
  return pseudoContext != nullptr;
}

nsPoint nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(Event* aDOMEvent,
                                                        nsIFrame* aFrame) {
  if (!aDOMEvent) {
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
  }
  WidgetEvent* event = aDOMEvent->WidgetEventPtr();
  if (!event) {
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
  }
  return GetEventCoordinatesRelativeTo(event, RelativeTo{aFrame});
}

static bool IsValidCoordinateTypeEvent(const WidgetEvent* aEvent) {
  if (!aEvent) {
    return false;
  }
  return aEvent->mClass == eMouseEventClass ||
         aEvent->mClass == eMouseScrollEventClass ||
         aEvent->mClass == eWheelEventClass ||
         aEvent->mClass == eDragEventClass ||
         aEvent->mClass == eSimpleGestureEventClass ||
         aEvent->mClass == ePointerEventClass ||
         aEvent->mClass == eGestureNotifyEventClass ||
         aEvent->mClass == eTouchEventClass ||
         aEvent->mClass == eQueryContentEventClass;
}

nsPoint nsLayoutUtils::GetEventCoordinatesRelativeTo(const WidgetEvent* aEvent,
                                                     RelativeTo aFrame) {
  if (!IsValidCoordinateTypeEvent(aEvent)) {
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
  }

  return GetEventCoordinatesRelativeTo(aEvent, aEvent->AsGUIEvent()->mRefPoint,
                                       aFrame);
}

nsPoint nsLayoutUtils::GetEventCoordinatesRelativeTo(
    const WidgetEvent* aEvent, const LayoutDeviceIntPoint& aPoint,
    RelativeTo aFrame) {
  if (!aFrame.mFrame) {
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
  }

  nsIWidget* widget = aEvent->AsGUIEvent()->mWidget;
  if (!widget) {
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
  }

  return GetEventCoordinatesRelativeTo(widget, aPoint, aFrame);
}

nsPoint GetEventCoordinatesRelativeTo(nsIWidget* aWidget,
                                      const LayoutDeviceIntPoint& aPoint,
                                      RelativeTo aFrame) {
  const nsIFrame* frame = aFrame.mFrame;
  if (!frame || !aWidget) {
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
  }

  nsView* view = frame->GetView();
  if (view) {
    nsIWidget* frameWidget = view->GetWidget();
    if (frameWidget && frameWidget == aWidget) {
      // Special case this cause it happens a lot.
      // This also fixes bug 664707, events in the extra-special case of select
      // dropdown popups that are transformed.
      nsPresContext* presContext = frame->PresContext();
      nsPoint pt(presContext->DevPixelsToAppUnits(aPoint.x),
                 presContext->DevPixelsToAppUnits(aPoint.y));
      return pt - view->ViewToWidgetOffset();
    }
  }

  /* If we walk up the frame tree and discover that any of the frames are
   * transformed, we need to do extra work to convert from the global
   * space to the local space.
   */

  const nsIFrame* rootFrame = frame;
  bool transformFound = false;
  for (const nsIFrame* f = frame; f;
       f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
    if (f->IsTransformed() || ViewportUtils::IsZoomedContentRoot(f)) {
      transformFound = true;
    }

    rootFrame = f;
  }

  nsView* rootView = rootFrame->GetView();
  if (!rootView) {
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
  }

  nsPoint widgetToView = nsLayoutUtils::TranslateWidgetToView(
      rootFrame->PresContext(), aWidget, aPoint, rootView);

  if (widgetToView == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) {
    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
  }

  // Convert from root document app units to app units of the document aFrame
  // is in.
  int32_t rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel();
  int32_t localAPD = frame->PresContext()->AppUnitsPerDevPixel();
  widgetToView = widgetToView.ScaleToOtherAppUnits(rootAPD, localAPD);

  /* If we encountered a transform, we can't do simple arithmetic to figure
   * out how to convert back to aFrame's coordinates and must use the CTM.
   */

  if (transformFound || frame->IsInSVGTextSubtree()) {
    return nsLayoutUtils::TransformRootPointToFrame(ViewportType::Visual,
                                                    aFrame, widgetToView);
  }

  /* Otherwise, all coordinate systems are translations of one another,
   * so we can just subtract out the difference.
   */

  return widgetToView - frame->GetOffsetToCrossDoc(rootFrame);
}

nsPoint nsLayoutUtils::GetEventCoordinatesRelativeTo(
    nsIWidget* aWidget, const LayoutDeviceIntPoint& aPoint, RelativeTo aFrame) {
  nsPoint result = ::GetEventCoordinatesRelativeTo(aWidget, aPoint, aFrame);
  if (aFrame.mViewportType == ViewportType::Layout && aFrame.mFrame &&
      aFrame.mFrame->Type() == LayoutFrameType::Viewport &&
      aFrame.mFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
    result = ViewportUtils::VisualToLayout(result, aFrame.mFrame->PresShell());
  }
  return result;
}

nsIFrame* nsLayoutUtils::GetPopupFrameForEventCoordinates(
    nsPresContext* aRootPresContext, const WidgetEvent* aEvent) {
  if (!IsValidCoordinateTypeEvent(aEvent)) {
    return nullptr;
  }

  const auto* guiEvent = aEvent->AsGUIEvent();
  return GetPopupFrameForPoint(aRootPresContext, guiEvent->mWidget,
                               guiEvent->mRefPoint);
}

nsIFrame* nsLayoutUtils::GetPopupFrameForPoint(
    nsPresContext* aRootPresContext, nsIWidget* aWidget,
    const mozilla::LayoutDeviceIntPoint& aPoint,
    GetPopupFrameForPointFlags aFlags /* = GetPopupFrameForPointFlags(0) */) {
  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  if (!pm) {
    return nullptr;
  }
  nsTArray<nsMenuPopupFrame*> popups;
  pm->GetVisiblePopups(popups);
  // Search from top to bottom
  for (nsMenuPopupFrame* popup : popups) {
    if (popup->PresContext()->GetRootPresContext() != aRootPresContext) {
      continue;
    }
    if (!popup->ScrollableOverflowRect().Contains(GetEventCoordinatesRelativeTo(
            aWidget, aPoint, RelativeTo{popup}))) {
      continue;
    }
    if (aFlags & GetPopupFrameForPointFlags::OnlyReturnFramesWithWidgets) {
      if (!popup->HasView() || !popup->GetView()->HasWidget()) {
        continue;
      }
    }
    return popup;
  }
  return nullptr;
}

void nsLayoutUtils::GetContainerAndOffsetAtEvent(PresShell* aPresShell,
                                                 const WidgetEvent* aEvent,
                                                 nsIContent** aContainer,
                                                 int32_t* aOffset) {
  MOZ_ASSERT(aContainer || aOffset);

  if (aContainer) {
    *aContainer = nullptr;
  }
  if (aOffset) {
    *aOffset = 0;
  }

  if (!aPresShell) {
    return;
  }

  aPresShell->FlushPendingNotifications(FlushType::Layout);

  RefPtr<nsPresContext> presContext = aPresShell->GetPresContext();
  if (!presContext) {
    return;
  }

  nsIFrame* targetFrame = presContext->EventStateManager()->GetEventTarget();
  if (!targetFrame) {
    return;
  }

  WidgetEvent* openingEvent = nullptr;
  // For popupshowing events, redirect via the original mouse event
  // that triggered the popup to open.
  if (aEvent->mMessage == eXULPopupShowing) {
    if (auto* pm = nsXULPopupManager::GetInstance()) {
      if (Event* openingPopupEvent = pm->GetOpeningPopupEvent()) {
        openingEvent = openingPopupEvent->WidgetEventPtr();
      }
    }
  }

  nsPoint point = nsLayoutUtils::GetEventCoordinatesRelativeTo(
      openingEvent ? openingEvent : aEvent, RelativeTo{targetFrame});

  if (aContainer) {
    // TODO: This result may be useful to change to Selection.  However, this
    //       may return improper node (e.g., native anonymous node) for the
    //       Selection.  Perhaps, this should take Selection optionally and
    //       if it's specified, needs to check if it's proper for the
    //       Selection.
    nsCOMPtr<nsIContent> container =
        targetFrame->GetContentOffsetsFromPoint(point).content;
    if (container && (!container->ChromeOnlyAccess() ||
                      nsContentUtils::CanAccessNativeAnon())) {
      container.forget(aContainer);
    }
  }
  if (aOffset) {
    *aOffset = targetFrame->GetContentOffsetsFromPoint(point).offset;
  }
}

void nsLayoutUtils::ConstrainToCoordValues(float& aStart, float& aSize) {
  MOZ_ASSERT(aSize >= 0);

  // Here we try to make sure that the resulting nsRect will continue to cover
  // as much of the area that was covered by the original gfx Rect as possible.

  // We clamp the bounds of the rect to {nscoord_MIN,nscoord_MAX} since
  // nsRect::X/Y() and nsRect::XMost/YMost() can't return values outwith this
  // range:
  float end = aStart + aSize;
  aStart = std::clamp(aStart, float(nscoord_MIN), float(nscoord_MAX));
  end = std::clamp(end, float(nscoord_MIN), float(nscoord_MAX));

  aSize = end - aStart;

  // We must also clamp aSize to {0,nscoord_MAX} since nsRect::Width/Height()
  // can't return a value greater than nscoord_MAX. If aSize is greater than
  // nscoord_MAX then we reduce it to nscoord_MAX while keeping the rect
  // centered:
  if (MOZ_UNLIKELY(std::isnan(aSize))) {
    // Can happen if aStart is -inf and aSize is +inf for example.
    aStart = 0.0f;
    aSize = float(nscoord_MAX);
  } else if (aSize > float(nscoord_MAX)) {
    float excess = aSize - float(nscoord_MAX);
    excess /= 2;
    aStart += excess;
    aSize = float(nscoord_MAX);
  }
}

/**
 * Given a gfxFloat, constrains its value to be between nscoord_MIN and
 * nscoord_MAX.
 *
 * @param aVal The value to constrain (in/out)
 */

static void ConstrainToCoordValues(gfxFloat& aVal) {
  if (aVal <= nscoord_MIN) {
    aVal = nscoord_MIN;
  } else if (aVal >= nscoord_MAX) {
    aVal = nscoord_MAX;
  }
}

void nsLayoutUtils::ConstrainToCoordValues(gfxFloat& aStart, gfxFloat& aSize) {
  gfxFloat max = aStart + aSize;

  // Clamp the end points to within nscoord range
  ::ConstrainToCoordValues(aStart);
  ::ConstrainToCoordValues(max);

  aSize = max - aStart;
  // If the width if still greater than the max nscoord, then bring both
  // endpoints in by the same amount until it fits.
  if (MOZ_UNLIKELY(std::isnan(aSize))) {
    // Can happen if aStart is -inf and aSize is +inf for example.
    aStart = 0.0f;
    aSize = nscoord_MAX;
  } else if (aSize > nscoord_MAX) {
    gfxFloat excess = aSize - nscoord_MAX;
    excess /= 2;

    aStart += excess;
    aSize = nscoord_MAX;
  } else if (aSize < nscoord_MIN) {
    gfxFloat excess = aSize - nscoord_MIN;
    excess /= 2;

    aStart -= excess;
    aSize = nscoord_MIN;
  }
}

nsRegion nsLayoutUtils::RoundedRectIntersectRect(const nsRect& aRoundedRect,
                                                 const nscoord aRadii[8],
                                                 const nsRect& aContainedRect) {
  // rectFullHeight and rectFullWidth together will approximately contain
  // the total area of the frame minus the rounded corners.
  nsRect rectFullHeight = aRoundedRect;
  nscoord xDiff = std::max(aRadii[eCornerTopLeftX], aRadii[eCornerBottomLeftX]);
  rectFullHeight.x += xDiff;
  rectFullHeight.width -=
      std::max(aRadii[eCornerTopRightX], aRadii[eCornerBottomRightX]) + xDiff;
  nsRect r1;
  r1.IntersectRect(rectFullHeight, aContainedRect);

  nsRect rectFullWidth = aRoundedRect;
  nscoord yDiff = std::max(aRadii[eCornerTopLeftY], aRadii[eCornerTopRightY]);
  rectFullWidth.y += yDiff;
  rectFullWidth.height -=
      std::max(aRadii[eCornerBottomLeftY], aRadii[eCornerBottomRightY]) + yDiff;
  nsRect r2;
  r2.IntersectRect(rectFullWidth, aContainedRect);

  nsRegion result;
  result.Or(r1, r2);
  return result;
}

nsIntRegion nsLayoutUtils::RoundedRectIntersectIntRect(
    const nsIntRect& aRoundedRect, const RectCornerRadii& aCornerRadii,
    const nsIntRect& aContainedRect) {
  // rectFullHeight and rectFullWidth together will approximately contain
  // the total area of the frame minus the rounded corners.
  nsIntRect rectFullHeight = aRoundedRect;
  uint32_t xDiff =
      std::max(aCornerRadii.TopLeft().width, aCornerRadii.BottomLeft().width);
  rectFullHeight.x += xDiff;
  rectFullHeight.width -= std::max(aCornerRadii.TopRight().width,
                                   aCornerRadii.BottomRight().width) +
                          xDiff;
  nsIntRect r1;
  r1.IntersectRect(rectFullHeight, aContainedRect);

  nsIntRect rectFullWidth = aRoundedRect;
  uint32_t yDiff =
      std::max(aCornerRadii.TopLeft().height, aCornerRadii.TopRight().height);
  rectFullWidth.y += yDiff;
  rectFullWidth.height -= std::max(aCornerRadii.BottomLeft().height,
                                   aCornerRadii.BottomRight().height) +
                          yDiff;
  nsIntRect r2;
  r2.IntersectRect(rectFullWidth, aContainedRect);

  nsIntRegion result;
  result.Or(r1, r2);
  return result;
}

// Helper for RoundedRectIntersectsRect.
static bool CheckCorner(nscoord aXOffset, nscoord aYOffset, nscoord aXRadius,
                        nscoord aYRadius) {
  MOZ_ASSERT(aXOffset > 0 && aYOffset > 0,
             "must not pass nonpositives to CheckCorner");
  MOZ_ASSERT(aXRadius >= 0 && aYRadius >= 0,
             "must not pass negatives to CheckCorner");

  // Avoid floating point math unless we're either (1) within the
  // quarter-ellipse area at the rounded corner or (2) outside the
  // rounding.
  if (aXOffset >= aXRadius || aYOffset >= aYRadius) {
    return true;
  }

  // Convert coordinates to a unit circle with (0,0) as the center of
  // curvature, and see if we're inside the circle or outside.
  float scaledX = float(aXRadius - aXOffset) / float(aXRadius);
  float scaledY = float(aYRadius - aYOffset) / float(aYRadius);
  return scaledX * scaledX + scaledY * scaledY < 1.0f;
}

bool nsLayoutUtils::RoundedRectIntersectsRect(const nsRect& aRoundedRect,
                                              const nscoord aRadii[8],
                                              const nsRect& aTestRect) {
  if (!aTestRect.Intersects(aRoundedRect)) {
    return false;
  }

  // distances from this edge of aRoundedRect to opposite edge of aTestRect,
  // which we know are positive due to the Intersects check above.
  nsMargin insets;
  insets.top = aTestRect.YMost() - aRoundedRect.y;
  insets.right = aRoundedRect.XMost() - aTestRect.x;
  insets.bottom = aRoundedRect.YMost() - aTestRect.y;
  insets.left = aTestRect.XMost() - aRoundedRect.x;

  // Check whether the bottom-right corner of aTestRect is inside the
  // top left corner of aBounds when rounded by aRadii, etc.  If any
  // corner is not, then fail; otherwise succeed.
  return CheckCorner(insets.left, insets.top, aRadii[eCornerTopLeftX],
                     aRadii[eCornerTopLeftY]) &&
         CheckCorner(insets.right, insets.top, aRadii[eCornerTopRightX],
                     aRadii[eCornerTopRightY]) &&
         CheckCorner(insets.right, insets.bottom, aRadii[eCornerBottomRightX],
                     aRadii[eCornerBottomRightY]) &&
         CheckCorner(insets.left, insets.bottom, aRadii[eCornerBottomLeftX],
                     aRadii[eCornerBottomLeftY]);
}

nsRect nsLayoutUtils::MatrixTransformRect(const nsRect& aBounds,
                                          const Matrix4x4& aMatrix,
                                          float aFactor) {
  RectDouble image =
      RectDouble(NSAppUnitsToDoublePixels(aBounds.x, aFactor),
                 NSAppUnitsToDoublePixels(aBounds.y, aFactor),
                 NSAppUnitsToDoublePixels(aBounds.width, aFactor),
                 NSAppUnitsToDoublePixels(aBounds.height, aFactor));

  RectDouble maxBounds = RectDouble(
      double(nscoord_MIN) / aFactor * 0.5, double(nscoord_MIN) / aFactor * 0.5,
      double(nscoord_MAX) / aFactor, double(nscoord_MAX) / aFactor);

  image = aMatrix.TransformAndClipBounds(image, maxBounds);

  return RoundGfxRectToAppRect(ThebesRect(image), aFactor);
}

nsRect nsLayoutUtils::MatrixTransformRect(const nsRect& aBounds,
                                          const Matrix4x4Flagged& aMatrix,
                                          float aFactor) {
  RectDouble image =
      RectDouble(NSAppUnitsToDoublePixels(aBounds.x, aFactor),
                 NSAppUnitsToDoublePixels(aBounds.y, aFactor),
                 NSAppUnitsToDoublePixels(aBounds.width, aFactor),
                 NSAppUnitsToDoublePixels(aBounds.height, aFactor));

  RectDouble maxBounds = RectDouble(
      double(nscoord_MIN) / aFactor * 0.5, double(nscoord_MIN) / aFactor * 0.5,
      double(nscoord_MAX) / aFactor, double(nscoord_MAX) / aFactor);

  image = aMatrix.TransformAndClipBounds(image, maxBounds);

  return RoundGfxRectToAppRect(ThebesRect(image), aFactor);
}

nsPoint nsLayoutUtils::MatrixTransformPoint(const nsPoint& aPoint,
                                            const Matrix4x4& aMatrix,
                                            float aFactor) {
  gfxPoint image = gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, aFactor),
                            NSAppUnitsToFloatPixels(aPoint.y, aFactor));
  image = aMatrix.TransformPoint(image);
  return nsPoint(NSFloatPixelsToAppUnits(float(image.x), aFactor),
                 NSFloatPixelsToAppUnits(float(image.y), aFactor));
}

void nsLayoutUtils::PostTranslate(Matrix4x4& aTransform, const nsPoint& aOrigin,
                                  float aAppUnitsPerPixel, bool aRounded) {
  Point3D gfxOrigin =
      Point3D(NSAppUnitsToFloatPixels(aOrigin.x, aAppUnitsPerPixel),
              NSAppUnitsToFloatPixels(aOrigin.y, aAppUnitsPerPixel), 0.0f);
  if (aRounded) {
    gfxOrigin.x = NS_round(gfxOrigin.x);
    gfxOrigin.y = NS_round(gfxOrigin.y);
  }
  aTransform.PostTranslate(gfxOrigin);
}

bool nsLayoutUtils::ShouldSnapToGrid(const nsIFrame* aFrame) {
  return !aFrame || !aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) ||
         aFrame->IsSVGOuterSVGAnonChildFrame();
}

--> --------------------

--> maximum size reached

--> --------------------

93%


¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge