tor-browser

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

SVGImageFrame.cpp (32897B)


      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 "SVGImageFrame.h"
      8 
      9 // Keep in (case-insensitive) order:
     10 #include "ImageRegion.h"
     11 #include "SVGGeometryProperty.h"
     12 #include "gfxContext.h"
     13 #include "gfxPlatform.h"
     14 #include "imgIContainer.h"
     15 #include "imgINotificationObserver.h"
     16 #include "mozilla/ComputedStyleInlines.h"
     17 #include "mozilla/PresShell.h"
     18 #include "mozilla/SVGContentUtils.h"
     19 #include "mozilla/SVGImageContext.h"
     20 #include "mozilla/SVGObserverUtils.h"
     21 #include "mozilla/SVGUtils.h"
     22 #include "mozilla/StaticPrefs_image.h"
     23 #include "mozilla/dom/LargestContentfulPaint.h"
     24 #include "mozilla/dom/SVGImageElement.h"
     25 #include "mozilla/image/WebRenderImageProvider.h"
     26 #include "mozilla/layers/RenderRootStateManager.h"
     27 #include "mozilla/layers/WebRenderLayerManager.h"
     28 #include "nsContainerFrame.h"
     29 #include "nsIImageLoadingContent.h"
     30 #include "nsIMutationObserver.h"
     31 #include "nsIReflowCallback.h"
     32 #include "nsLayoutUtils.h"
     33 
     34 using namespace mozilla::dom;
     35 using namespace mozilla::gfx;
     36 using namespace mozilla::image;
     37 using namespace mozilla::dom::SVGPreserveAspectRatio_Binding;
     38 namespace SVGT = SVGGeometryProperty::Tags;
     39 
     40 namespace mozilla {
     41 
     42 class SVGImageListener final : public imgINotificationObserver {
     43 public:
     44  explicit SVGImageListener(SVGImageFrame* aFrame);
     45 
     46  NS_DECL_ISUPPORTS
     47  NS_DECL_IMGINOTIFICATIONOBSERVER
     48 
     49  void SetFrame(SVGImageFrame* frame) { mFrame = frame; }
     50 
     51 private:
     52  ~SVGImageListener() = default;
     53 
     54  SVGImageFrame* mFrame;
     55 };
     56 
     57 // ---------------------------------------------------------------------
     58 // nsQueryFrame methods
     59 
     60 NS_QUERYFRAME_HEAD(SVGImageFrame)
     61  NS_QUERYFRAME_ENTRY(ISVGDisplayableFrame)
     62  NS_QUERYFRAME_ENTRY(SVGImageFrame)
     63 NS_QUERYFRAME_TAIL_INHERITING(nsIFrame)
     64 
     65 }  // namespace mozilla
     66 
     67 nsIFrame* NS_NewSVGImageFrame(mozilla::PresShell* aPresShell,
     68                              mozilla::ComputedStyle* aStyle) {
     69  return new (aPresShell)
     70      mozilla::SVGImageFrame(aStyle, aPresShell->GetPresContext());
     71 }
     72 
     73 namespace mozilla {
     74 
     75 NS_IMPL_FRAMEARENA_HELPERS(SVGImageFrame)
     76 
     77 SVGImageFrame::~SVGImageFrame() {
     78  // set the frame to null so we don't send messages to a dead object.
     79  if (mListener) {
     80    nsCOMPtr<nsIImageLoadingContent> imageLoader =
     81        do_QueryInterface(GetContent());
     82    if (imageLoader) {
     83      imageLoader->RemoveNativeObserver(mListener);
     84    }
     85    reinterpret_cast<SVGImageListener*>(mListener.get())->SetFrame(nullptr);
     86  }
     87  mListener = nullptr;
     88 }
     89 
     90 void SVGImageFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
     91                         nsIFrame* aPrevInFlow) {
     92  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::image),
     93               "Content is not an SVG image!");
     94 
     95  AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD);
     96  nsIFrame::Init(aContent, aParent, aPrevInFlow);
     97 
     98  if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
     99    // Non-display frames are likely to be patterns, masks or the like.
    100    // Treat them as always visible.
    101    // This call must happen before the FrameCreated. This is because the
    102    // primary frame pointer on our content node isn't set until after this
    103    // function ends, so there is no way for the resulting OnVisibilityChange
    104    // notification to get a frame. FrameCreated has a workaround for this in
    105    // that it passes our frame around so it can be accessed. OnVisibilityChange
    106    // doesn't have that workaround.
    107    IncApproximateVisibleCount();
    108  }
    109 
    110  mListener = new SVGImageListener(this);
    111  nsCOMPtr<nsIImageLoadingContent> imageLoader =
    112      do_QueryInterface(GetContent());
    113  if (!imageLoader) {
    114    MOZ_CRASH("Why is this not an image loading content?");
    115  }
    116 
    117  // We should have a PresContext now, so let's notify our image loader that
    118  // we need to register any image animations with the refresh driver.
    119  imageLoader->FrameCreated(this);
    120 
    121  imageLoader->AddNativeObserver(mListener);
    122 }
    123 
    124 /* virtual */
    125 void SVGImageFrame::Destroy(DestroyContext& aContext) {
    126  if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
    127    DecApproximateVisibleCount();
    128  }
    129 
    130  if (mReflowCallbackPosted) {
    131    PresShell()->CancelReflowCallback(this);
    132    mReflowCallbackPosted = false;
    133  }
    134 
    135  nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
    136 
    137  if (imageLoader) {
    138    imageLoader->FrameDestroyed(this);
    139  }
    140 
    141  nsIFrame::Destroy(aContext);
    142 }
    143 
    144 /* virtual */
    145 void SVGImageFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
    146  nsIFrame::DidSetComputedStyle(aOldStyle);
    147 
    148  if (!mImageContainer || !aOldStyle) {
    149    return;
    150  }
    151 
    152  nsCOMPtr<imgIRequest> currentRequest;
    153  nsCOMPtr<nsIImageLoadingContent> imageLoader =
    154      do_QueryInterface(GetContent());
    155  if (imageLoader) {
    156    imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
    157                            getter_AddRefs(currentRequest));
    158  }
    159 
    160  StyleImageOrientation newOrientation =
    161      StyleVisibility()->UsedImageOrientation(currentRequest);
    162  StyleImageOrientation oldOrientation =
    163      aOldStyle->StyleVisibility()->UsedImageOrientation(currentRequest);
    164 
    165  if (oldOrientation != newOrientation) {
    166    nsCOMPtr<imgIContainer> image(mImageContainer->Unwrap());
    167    mImageContainer = nsLayoutUtils::OrientImage(image, newOrientation);
    168  }
    169 
    170  // TODO(heycam): We should handle aspect-ratio, like nsImageFrame does.
    171 }
    172 
    173 bool SVGImageFrame::DoGetParentSVGTransforms(
    174    gfx::Matrix* aFromParentTransform) const {
    175  return SVGUtils::GetParentSVGTransforms(this, aFromParentTransform);
    176 }
    177 
    178 //----------------------------------------------------------------------
    179 // nsIFrame methods:
    180 
    181 nsresult SVGImageFrame::AttributeChanged(int32_t aNameSpaceID,
    182                                         nsAtom* aAttribute,
    183                                         AttrModType aModType) {
    184  if (aNameSpaceID == kNameSpaceID_None) {
    185    if (aAttribute == nsGkAtoms::preserveAspectRatio) {
    186      // We don't paint the content of the image using display lists, therefore
    187      // we have to invalidate for this children-only transform changes since
    188      // there is no layer tree to notice that the transform changed and
    189      // recomposite.
    190      InvalidateFrame();
    191      return NS_OK;
    192    }
    193  }
    194  if (aModType == AttrModType::Removal &&
    195      (aNameSpaceID == kNameSpaceID_None ||
    196       aNameSpaceID == kNameSpaceID_XLink) &&
    197      aAttribute == nsGkAtoms::href) {
    198    auto* element = static_cast<SVGImageElement*>(GetContent());
    199    if (aNameSpaceID == kNameSpaceID_None ||
    200        !element->mStringAttributes[SVGImageElement::HREF].IsExplicitlySet()) {
    201      mImageContainer = nullptr;
    202      InvalidateFrame();
    203    }
    204  }
    205 
    206  return NS_OK;
    207 }
    208 
    209 void SVGImageFrame::OnVisibilityChange(
    210    Visibility aNewVisibility, const Maybe<OnNonvisible>& aNonvisibleAction) {
    211  nsCOMPtr<nsIImageLoadingContent> imageLoader =
    212      do_QueryInterface(GetContent());
    213  if (imageLoader) {
    214    imageLoader->OnVisibilityChange(aNewVisibility, aNonvisibleAction);
    215  }
    216 
    217  nsIFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
    218 }
    219 
    220 gfx::Matrix SVGImageFrame::GetRasterImageTransform(int32_t aNativeWidth,
    221                                                   int32_t aNativeHeight) {
    222  float x, y, width, height;
    223  SVGImageElement* element = static_cast<SVGImageElement*>(GetContent());
    224  SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width, SVGT::Height>(
    225      element, &x, &y, &width, &height);
    226 
    227  Matrix viewBoxTM = SVGContentUtils::GetViewBoxTransform(
    228      width, height, 0, 0, aNativeWidth, aNativeHeight,
    229      element->mPreserveAspectRatio);
    230 
    231  return viewBoxTM * gfx::Matrix::Translation(x, y);
    232 }
    233 
    234 gfx::Matrix SVGImageFrame::GetVectorImageTransform() {
    235  float x, y;
    236  SVGImageElement* element = static_cast<SVGImageElement*>(GetContent());
    237  SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y>(element, &x, &y);
    238 
    239  // No viewBoxTM needed here -- our height/width overrides any concept of
    240  // "native size" that the SVG image has, and it will handle viewBox and
    241  // preserveAspectRatio on its own once we give it a region to draw into.
    242 
    243  return gfx::Matrix::Translation(x, y);
    244 }
    245 
    246 bool SVGImageFrame::GetIntrinsicImageDimensions(
    247    mozilla::gfx::Size& aSize, mozilla::AspectRatio& aAspectRatio) const {
    248  if (!mImageContainer) {
    249    return false;
    250  }
    251 
    252  ImageResolution resolution = mImageContainer->GetResolution();
    253  int32_t width, height;
    254  if (NS_FAILED(mImageContainer->GetWidth(&width))) {
    255    aSize.width = -1;
    256  } else {
    257    aSize.width = width;
    258    resolution.ApplyXTo(aSize.width);
    259  }
    260 
    261  if (NS_FAILED(mImageContainer->GetHeight(&height))) {
    262    aSize.height = -1;
    263  } else {
    264    aSize.height = height;
    265    resolution.ApplyYTo(aSize.height);
    266  }
    267 
    268  aAspectRatio = mImageContainer->GetIntrinsicRatio();
    269  return true;
    270 }
    271 
    272 bool SVGImageFrame::TransformContextForPainting(gfxContext* aGfxContext,
    273                                                const gfxMatrix& aTransform) {
    274  gfx::Matrix imageTransform;
    275  if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
    276    imageTransform = GetVectorImageTransform() * ToMatrix(aTransform);
    277  } else {
    278    int32_t nativeWidth, nativeHeight;
    279    if (NS_FAILED(mImageContainer->GetWidth(&nativeWidth)) ||
    280        NS_FAILED(mImageContainer->GetHeight(&nativeHeight)) ||
    281        nativeWidth == 0 || nativeHeight == 0) {
    282      return false;
    283    }
    284    mImageContainer->GetResolution().ApplyTo(nativeWidth, nativeHeight);
    285    imageTransform = GetRasterImageTransform(nativeWidth, nativeHeight) *
    286                     ToMatrix(aTransform);
    287 
    288    // NOTE: We need to cancel out the effects of Full-Page-Zoom, or else
    289    // it'll get applied an extra time by DrawSingleUnscaledImage.
    290    nscoord appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel();
    291    gfxFloat pageZoomFactor =
    292        nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPx);
    293    imageTransform.PreScale(pageZoomFactor, pageZoomFactor);
    294  }
    295 
    296  if (imageTransform.IsSingular()) {
    297    return false;
    298  }
    299 
    300  aGfxContext->Multiply(ThebesMatrix(imageTransform));
    301  return true;
    302 }
    303 
    304 //----------------------------------------------------------------------
    305 // ISVGDisplayableFrame methods
    306 
    307 void SVGImageFrame::PaintSVG(gfxContext& aContext, const gfxMatrix& aTransform,
    308                             imgDrawingParams& aImgParams) {
    309  if (!StyleVisibility()->IsVisible()) {
    310    return;
    311  }
    312 
    313  float x, y, width, height;
    314  SVGImageElement* imgElem = static_cast<SVGImageElement*>(GetContent());
    315  SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width, SVGT::Height>(
    316      imgElem, &x, &y, &width, &height);
    317  NS_ASSERTION(width > 0 && height > 0,
    318               "Should only be painting things with valid width/height");
    319 
    320  if (!mImageContainer) {
    321    nsCOMPtr<imgIRequest> currentRequest;
    322    nsCOMPtr<nsIImageLoadingContent> imageLoader =
    323        do_QueryInterface(GetContent());
    324    if (imageLoader) {
    325      imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
    326                              getter_AddRefs(currentRequest));
    327    }
    328 
    329    if (currentRequest) {
    330      currentRequest->GetImage(getter_AddRefs(mImageContainer));
    331    }
    332  }
    333 
    334  if (mImageContainer) {
    335    gfxClipAutoSaveRestore autoSaveClip(&aContext);
    336 
    337    if (StyleDisplay()->IsScrollableOverflow()) {
    338      gfxRect clipRect =
    339          SVGUtils::GetClipRectForFrame(this, x, y, width, height);
    340      autoSaveClip.TransformedClip(aTransform, clipRect);
    341    }
    342 
    343    gfxContextMatrixAutoSaveRestore autoSaveMatrix(&aContext);
    344 
    345    if (!TransformContextForPainting(&aContext, aTransform)) {
    346      return;
    347    }
    348 
    349    // fill-opacity doesn't affect <image>, so if we're allowed to
    350    // optimize group opacity, the opacity used for compositing the
    351    // image into the current canvas is just the group opacity.
    352    float opacity = 1.0f;
    353    if (SVGUtils::CanOptimizeOpacity(this)) {
    354      opacity = StyleEffects()->mOpacity;
    355    }
    356 
    357    gfxGroupForBlendAutoSaveRestore autoGroupForBlend(&aContext);
    358    if (opacity != 1.0f || StyleEffects()->HasMixBlendMode()) {
    359      autoGroupForBlend.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
    360                                              opacity);
    361    }
    362 
    363    nscoord appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel();
    364    uint32_t flags = aImgParams.imageFlags;
    365    if (mForceSyncDecoding) {
    366      flags |= imgIContainer::FLAG_SYNC_DECODE;
    367    }
    368 
    369    if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
    370      // Package up the attributes of this image element which can override the
    371      // attributes of mImageContainer's internal SVG document.  The 'width' &
    372      // 'height' values we're passing in here are in CSS units (though they
    373      // come from width/height *attributes* in SVG). They influence the region
    374      // of the SVG image's internal document that is visible, in combination
    375      // with preserveAspectRatio and viewBox.
    376      const SVGImageContext context(
    377          Some(CSSIntSize::Ceil(width, height)),
    378          Some(imgElem->mPreserveAspectRatio.GetAnimValue()));
    379 
    380      // For the actual draw operation to draw crisply (and at the right size),
    381      // our destination rect needs to be |width|x|height|, *in dev pixels*.
    382      LayoutDeviceSize devPxSize(width, height);
    383      nsRect destRect(nsPoint(), LayoutDevicePixel::ToAppUnits(
    384                                     devPxSize, appUnitsPerDevPx));
    385      nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest();
    386      if (currentRequest) {
    387        LCPHelpers::FinalizeLCPEntryForImage(
    388            GetContent()->AsElement(),
    389            static_cast<imgRequestProxy*>(currentRequest.get()), destRect);
    390      }
    391 
    392      // Note: Can't use DrawSingleUnscaledImage for the TYPE_VECTOR case.
    393      // That method needs our image to have a fixed native width & height,
    394      // and that's not always true for TYPE_VECTOR images.
    395      aImgParams.result &= nsLayoutUtils::DrawSingleImage(
    396          aContext, PresContext(), mImageContainer,
    397          nsLayoutUtils::GetSamplingFilterForFrame(this), destRect, destRect,
    398          context, flags);
    399    } else {  // mImageContainer->GetType() == TYPE_RASTER
    400      aImgParams.result &= nsLayoutUtils::DrawSingleUnscaledImage(
    401          aContext, PresContext(), mImageContainer,
    402          nsLayoutUtils::GetSamplingFilterForFrame(this), nsPoint(0, 0),
    403          nullptr, SVGImageContext(), flags);
    404    }
    405  }
    406 }
    407 
    408 void SVGImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
    409                                     const nsDisplayListSet& aLists) {
    410  if (!static_cast<const SVGElement*>(GetContent())->HasValidDimensions()) {
    411    return;
    412  }
    413 
    414  if (aBuilder->IsForPainting()) {
    415    if (!IsVisibleForPainting()) {
    416      return;
    417    }
    418    if (StyleEffects()->IsTransparent() && SVGUtils::CanOptimizeOpacity(this)) {
    419      return;
    420    }
    421    aBuilder->BuildCompositorHitTestInfoIfNeeded(this,
    422                                                 aLists.BorderBackground());
    423  }
    424 
    425  DisplayOutline(aBuilder, aLists);
    426  aLists.Content()->AppendNewToTop<DisplaySVGImage>(aBuilder, this);
    427 }
    428 
    429 bool SVGImageFrame::IsInvisible() const {
    430  if (!StyleVisibility()->IsVisible()) {
    431    return true;
    432  }
    433 
    434  // Anything below will round to zero later down the pipeline.
    435  constexpr float opacity_threshold = 1.0 / 128.0;
    436 
    437  return StyleEffects()->mOpacity <= opacity_threshold &&
    438         SVGUtils::CanOptimizeOpacity(this);
    439 }
    440 
    441 bool SVGImageFrame::CreateWebRenderCommands(
    442    mozilla::wr::DisplayListBuilder& aBuilder,
    443    mozilla::wr::IpcResourceUpdateQueue& aResources,
    444    const mozilla::layers::StackingContextHelper& aSc,
    445    mozilla::layers::RenderRootStateManager* aManager,
    446    nsDisplayListBuilder* aDisplayListBuilder, DisplaySVGImage* aItem,
    447    bool aDryRun) {
    448  if (!StyleVisibility()->IsVisible()) {
    449    return true;
    450  }
    451 
    452  float opacity = 1.0f;
    453  if (SVGUtils::CanOptimizeOpacity(this)) {
    454    opacity = StyleEffects()->mOpacity;
    455  }
    456 
    457  if (opacity != 1.0f) {
    458    // FIXME: not implemented, might be trivial
    459    return false;
    460  }
    461  if (StyleEffects()->HasMixBlendMode()) {
    462    // FIXME: not implemented
    463    return false;
    464  }
    465 
    466  // try to setup the image
    467  if (!mImageContainer) {
    468    nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest();
    469    if (currentRequest) {
    470      currentRequest->GetImage(getter_AddRefs(mImageContainer));
    471    }
    472  }
    473 
    474  if (!mImageContainer) {
    475    // nothing to draw (yet)
    476    return true;
    477  }
    478 
    479  uint32_t flags = aDisplayListBuilder->GetImageDecodeFlags();
    480  if (mForceSyncDecoding) {
    481    flags |= imgIContainer::FLAG_SYNC_DECODE;
    482  }
    483 
    484  // Compute bounds of the image
    485  nscoord appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel();
    486  int32_t appUnitsPerCSSPixel = AppUnitsPerCSSPixel();
    487 
    488  float x, y, width, height;
    489  SVGImageElement* imgElem = static_cast<SVGImageElement*>(GetContent());
    490  SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width, SVGT::Height>(
    491      imgElem, &x, &y, &width, &height);
    492  NS_ASSERTION(width > 0 && height > 0,
    493               "Should only be painting things with valid width/height");
    494 
    495  auto toReferenceFrame = aItem->ToReferenceFrame();
    496  auto appRect = nsLayoutUtils::RoundGfxRectToAppRect(Rect(0, 0, width, height),
    497                                                      appUnitsPerCSSPixel);
    498  appRect += toReferenceFrame;
    499  auto destRect = LayoutDeviceRect::FromAppUnits(appRect, appUnitsPerDevPx);
    500  auto clipRect = destRect;
    501 
    502  if (StyleDisplay()->IsScrollableOverflow()) {
    503    // Apply potential non-trivial clip
    504    auto cssClip = SVGUtils::GetClipRectForFrame(this, 0, 0, width, height);
    505    auto appClip =
    506        nsLayoutUtils::RoundGfxRectToAppRect(cssClip, appUnitsPerCSSPixel);
    507    appClip += toReferenceFrame;
    508    clipRect = LayoutDeviceRect::FromAppUnits(appClip, appUnitsPerDevPx);
    509 
    510    // Apply preserveAspectRatio
    511    if (mImageContainer->GetType() == imgIContainer::TYPE_RASTER) {
    512      int32_t nativeWidth, nativeHeight;
    513      if (NS_FAILED(mImageContainer->GetWidth(&nativeWidth)) ||
    514          NS_FAILED(mImageContainer->GetHeight(&nativeHeight)) ||
    515          nativeWidth == 0 || nativeHeight == 0) {
    516        // Image has no size; nothing to draw
    517        return true;
    518      }
    519 
    520      mImageContainer->GetResolution().ApplyTo(nativeWidth, nativeHeight);
    521 
    522      auto preserveAspectRatio = imgElem->mPreserveAspectRatio.GetAnimValue();
    523      uint16_t align = preserveAspectRatio.GetAlign();
    524      uint16_t meetOrSlice = preserveAspectRatio.GetMeetOrSlice();
    525 
    526      // default to the defaults
    527      if (align == SVG_PRESERVEASPECTRATIO_UNKNOWN) {
    528        align = SVG_PRESERVEASPECTRATIO_XMIDYMID;
    529      }
    530      if (meetOrSlice == SVG_MEETORSLICE_UNKNOWN) {
    531        meetOrSlice = SVG_MEETORSLICE_MEET;
    532      }
    533 
    534      // aspect > 1 is horizontal
    535      // aspect < 1 is vertical
    536      float nativeAspect = ((float)nativeWidth) / ((float)nativeHeight);
    537      float viewAspect = width / height;
    538 
    539      // "Meet" is "fit image to view"; "Slice" is "cover view with image".
    540      //
    541      // Whether we meet or slice, one side of the destRect will always be
    542      // perfectly spanned by our image. The only questions to answer are
    543      // "which side won't span perfectly" and "should that side be grown
    544      // or shrunk".
    545      //
    546      // Because we fit our image to the destRect, this all just reduces to:
    547      // "if meet, shrink to fit. if slice, grow to fit."
    548      if (align != SVG_PRESERVEASPECTRATIO_NONE && nativeAspect != viewAspect) {
    549        // Slightly redundant bools, but they make the conditions clearer
    550        bool tooTall = nativeAspect > viewAspect;
    551        bool tooWide = nativeAspect < viewAspect;
    552        if ((meetOrSlice == SVG_MEETORSLICE_MEET && tooTall) ||
    553            (meetOrSlice == SVG_MEETORSLICE_SLICE && tooWide)) {
    554          // Adjust height and realign y
    555          auto oldHeight = destRect.height;
    556          destRect.height = destRect.width / nativeAspect;
    557          auto heightChange = oldHeight - destRect.height;
    558          switch (align) {
    559            case SVG_PRESERVEASPECTRATIO_XMINYMIN:
    560            case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
    561            case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
    562              // align to top (no-op)
    563              break;
    564            case SVG_PRESERVEASPECTRATIO_XMINYMID:
    565            case SVG_PRESERVEASPECTRATIO_XMIDYMID:
    566            case SVG_PRESERVEASPECTRATIO_XMAXYMID:
    567              // align to center
    568              destRect.y += heightChange / 2.0f;
    569              break;
    570            case SVG_PRESERVEASPECTRATIO_XMINYMAX:
    571            case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
    572            case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
    573              // align to bottom
    574              destRect.y += heightChange;
    575              break;
    576            default:
    577              MOZ_ASSERT_UNREACHABLE("Unknown value for align");
    578          }
    579        } else if ((meetOrSlice == SVG_MEETORSLICE_MEET && tooWide) ||
    580                   (meetOrSlice == SVG_MEETORSLICE_SLICE && tooTall)) {
    581          // Adjust width and realign x
    582          auto oldWidth = destRect.width;
    583          destRect.width = destRect.height * nativeAspect;
    584          auto widthChange = oldWidth - destRect.width;
    585          switch (align) {
    586            case SVG_PRESERVEASPECTRATIO_XMINYMIN:
    587            case SVG_PRESERVEASPECTRATIO_XMINYMID:
    588            case SVG_PRESERVEASPECTRATIO_XMINYMAX:
    589              // align to left (no-op)
    590              break;
    591            case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
    592            case SVG_PRESERVEASPECTRATIO_XMIDYMID:
    593            case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
    594              // align to center
    595              destRect.x += widthChange / 2.0f;
    596              break;
    597            case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
    598            case SVG_PRESERVEASPECTRATIO_XMAXYMID:
    599            case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
    600              // align to right
    601              destRect.x += widthChange;
    602              break;
    603            default:
    604              MOZ_ASSERT_UNREACHABLE("Unknown value for align");
    605          }
    606        }
    607      }
    608    }
    609  }
    610 
    611  SVGImageContext svgContext;
    612  if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) {
    613    if (StaticPrefs::image_svg_blob_image()) {
    614      flags |= imgIContainer::FLAG_RECORD_BLOB;
    615    }
    616    // Forward preserveAspectRatio to inner SVGs
    617    svgContext.SetViewportSize(Some(CSSIntSize::Ceil(width, height)));
    618    svgContext.SetPreserveAspectRatio(
    619        Some(imgElem->mPreserveAspectRatio.GetAnimValue()));
    620  }
    621 
    622  Maybe<ImageIntRegion> region;
    623  IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters(
    624      mImageContainer, this, destRect, clipRect, aSc, flags, svgContext,
    625      region);
    626 
    627  if (nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest()) {
    628    LCPHelpers::FinalizeLCPEntryForImage(
    629        GetContent()->AsElement(),
    630        static_cast<imgRequestProxy*>(currentRequest.get()),
    631        LayoutDeviceRect::ToAppUnits(destRect, appUnitsPerDevPx) -
    632            toReferenceFrame);
    633  }
    634 
    635  RefPtr<image::WebRenderImageProvider> provider;
    636  ImgDrawResult drawResult = mImageContainer->GetImageProvider(
    637      aManager->LayerManager(), decodeSize, svgContext, region, flags,
    638      getter_AddRefs(provider));
    639 
    640  // While we got a container, it may not contain a fully decoded surface. If
    641  // that is the case, and we have an image we were previously displaying which
    642  // has a fully decoded surface, then we should prefer the previous image.
    643  switch (drawResult) {
    644    case ImgDrawResult::NOT_READY:
    645    case ImgDrawResult::TEMPORARY_ERROR:
    646      // nothing to draw (yet)
    647      return true;
    648    case ImgDrawResult::NOT_SUPPORTED:
    649      // things we haven't implemented for WR yet
    650      return false;
    651    default:
    652      // image is ready to draw
    653      break;
    654  }
    655 
    656  // Don't do any actual mutations to state if we're doing a dry run
    657  // (used to decide if we're making this into an active layer)
    658  if (!aDryRun) {
    659    // If the image container is empty, we don't want to fallback. Any other
    660    // failure will be due to resource constraints and fallback is unlikely to
    661    // help us. Hence we can ignore the return value from PushImage.
    662    if (provider) {
    663      aManager->CommandBuilder().PushImageProvider(aItem, provider, drawResult,
    664                                                   aBuilder, aResources,
    665                                                   destRect, clipRect);
    666    }
    667  }
    668 
    669  return true;
    670 }
    671 
    672 nsIFrame* SVGImageFrame::GetFrameForPoint(const gfxPoint& aPoint) {
    673  if (!HasAnyStateBits(NS_STATE_SVG_CLIPPATH_CHILD) && IgnoreHitTest()) {
    674    return nullptr;
    675  }
    676 
    677  Rect rect;
    678  SVGImageElement* element = static_cast<SVGImageElement*>(GetContent());
    679  SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width, SVGT::Height>(
    680      element, &rect.x, &rect.y, &rect.width, &rect.height);
    681 
    682  if (!rect.Contains(ToPoint(aPoint))) {
    683    return nullptr;
    684  }
    685 
    686  // Special case for raster images -- we only want to accept points that fall
    687  // in the underlying image's (scaled to fit) native bounds.  That region
    688  // doesn't necessarily map to our <image> element's [x,y,width,height] if the
    689  // raster image's aspect ratio is being preserved.  We have to look up the
    690  // native image size & our viewBox transform in order to filter out points
    691  // that fall outside that area.  (This special case doesn't apply to vector
    692  // images because they don't limit their drawing to explicit "native
    693  // bounds" -- they have an infinite canvas on which to place content.)
    694  if (StyleDisplay()->IsScrollableOverflow() && mImageContainer) {
    695    if (mImageContainer->GetType() == imgIContainer::TYPE_RASTER) {
    696      int32_t nativeWidth, nativeHeight;
    697      if (NS_FAILED(mImageContainer->GetWidth(&nativeWidth)) ||
    698          NS_FAILED(mImageContainer->GetHeight(&nativeHeight)) ||
    699          nativeWidth == 0 || nativeHeight == 0) {
    700        return nullptr;
    701      }
    702      mImageContainer->GetResolution().ApplyTo(nativeWidth, nativeHeight);
    703      Matrix viewBoxTM = SVGContentUtils::GetViewBoxTransform(
    704          rect.width, rect.height, 0, 0, nativeWidth, nativeHeight,
    705          element->mPreserveAspectRatio);
    706      if (!SVGUtils::HitTestRect(viewBoxTM, 0, 0, nativeWidth, nativeHeight,
    707                                 aPoint.x - rect.x, aPoint.y - rect.y)) {
    708        return nullptr;
    709      }
    710    }
    711  }
    712 
    713  return this;
    714 }
    715 
    716 void SVGImageFrame::ReflowSVG() {
    717  NS_ASSERTION(SVGUtils::OuterSVGIsCallingReflowSVG(this),
    718               "This call is probably a wasteful mistake");
    719 
    720  MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY),
    721             "ReflowSVG mechanism not designed for this");
    722 
    723  if (!SVGUtils::NeedsReflowSVG(this)) {
    724    return;
    725  }
    726 
    727  float x, y, width, height;
    728  SVGImageElement* element = static_cast<SVGImageElement*>(GetContent());
    729  SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width, SVGT::Height>(
    730      element, &x, &y, &width, &height);
    731 
    732  Rect extent(x, y, width, height);
    733 
    734  if (!extent.IsEmpty()) {
    735    mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent, AppUnitsPerCSSPixel());
    736  } else {
    737    mRect.SetEmpty();
    738  }
    739 
    740  if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
    741    // Make sure we have our filter property (if any) before calling
    742    // FinishAndStoreOverflow (subsequent filter changes are handled off
    743    // nsChangeHint_UpdateEffects):
    744    SVGObserverUtils::UpdateEffects(this);
    745 
    746    if (!mReflowCallbackPosted) {
    747      mReflowCallbackPosted = true;
    748      PresShell()->PostReflowCallback(this);
    749    }
    750  }
    751 
    752  nsRect overflow = nsRect(nsPoint(0, 0), mRect.Size());
    753  OverflowAreas overflowAreas(overflow, overflow);
    754  FinishAndStoreOverflow(overflowAreas, mRect.Size());
    755 
    756  RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
    757                  NS_FRAME_HAS_DIRTY_CHILDREN);
    758 
    759  // Invalidate, but only if this is not our first reflow (since if it is our
    760  // first reflow then we haven't had our first paint yet).
    761  if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
    762    InvalidateFrame();
    763  }
    764 }
    765 
    766 bool SVGImageFrame::ReflowFinished() {
    767  mReflowCallbackPosted = false;
    768 
    769  // XXX(seth): We don't need this. The purpose of updating visibility
    770  // synchronously is to ensure that animated images start animating
    771  // immediately. In the short term, however,
    772  // nsImageLoadingContent::OnUnlockedDraw() is enough to ensure that
    773  // animations start as soon as the image is painted for the first time, and in
    774  // the long term we want to update visibility information from the display
    775  // list whenever we paint, so we don't actually need to do this. However, to
    776  // avoid behavior changes during the transition from the old image visibility
    777  // code, we'll leave it in for now.
    778  UpdateVisibilitySynchronously();
    779 
    780  return false;
    781 }
    782 
    783 void SVGImageFrame::ReflowCallbackCanceled() { mReflowCallbackPosted = false; }
    784 
    785 already_AddRefed<imgIRequest> SVGImageFrame::GetCurrentRequest() const {
    786  nsCOMPtr<imgIRequest> request;
    787  nsCOMPtr<nsIImageLoadingContent> imageLoader =
    788      do_QueryInterface(GetContent());
    789  if (imageLoader) {
    790    imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
    791                            getter_AddRefs(request));
    792  }
    793  return request.forget();
    794 }
    795 
    796 bool SVGImageFrame::IgnoreHitTest() const {
    797  switch (Style()->PointerEvents()) {
    798    case StylePointerEvents::None:
    799      break;
    800    case StylePointerEvents::Visiblepainted:
    801    case StylePointerEvents::Auto:
    802      if (StyleVisibility()->IsVisible()) {
    803        /* XXX: should check pixel transparency */
    804        return false;
    805      }
    806      break;
    807    case StylePointerEvents::Visiblefill:
    808    case StylePointerEvents::Visiblestroke:
    809    case StylePointerEvents::Visible:
    810      if (StyleVisibility()->IsVisible()) {
    811        return false;
    812      }
    813      break;
    814    case StylePointerEvents::Painted:
    815      /* XXX: should check pixel transparency */
    816      return false;
    817    case StylePointerEvents::Fill:
    818    case StylePointerEvents::Stroke:
    819    case StylePointerEvents::All:
    820      return false;
    821    default:
    822      NS_ERROR("not reached");
    823      break;
    824  }
    825 
    826  return true;
    827 }
    828 
    829 void SVGImageFrame::NotifySVGChanged(ChangeFlags aFlags) {
    830  MOZ_ASSERT(aFlags.contains(ChangeFlag::TransformChanged) ||
    831                 aFlags.contains(ChangeFlag::CoordContextChanged),
    832             "Invalidation logic may need adjusting");
    833 }
    834 
    835 SVGBBox SVGImageFrame::GetBBoxContribution(const Matrix& aToBBoxUserspace,
    836                                           uint32_t aFlags) {
    837  if (aToBBoxUserspace.IsSingular()) {
    838    // XXX ReportToConsole
    839    return {};
    840  }
    841 
    842  if ((aFlags & SVGUtils::eForGetClientRects) &&
    843      aToBBoxUserspace.PreservesAxisAlignedRectangles()) {
    844    if (!mRect.IsEmpty()) {
    845      Rect rect = NSRectToRect(mRect, AppUnitsPerCSSPixel());
    846      return aToBBoxUserspace.TransformBounds(rect);
    847    }
    848    return {};
    849  }
    850 
    851  auto* element = static_cast<SVGImageElement*>(GetContent());
    852 
    853  return element->GeometryBounds(aToBBoxUserspace);
    854 }
    855 
    856 //----------------------------------------------------------------------
    857 // SVGImageListener implementation
    858 
    859 NS_IMPL_ISUPPORTS(SVGImageListener, imgINotificationObserver)
    860 
    861 SVGImageListener::SVGImageListener(SVGImageFrame* aFrame) : mFrame(aFrame) {}
    862 
    863 void SVGImageListener::Notify(imgIRequest* aRequest, int32_t aType,
    864                              const nsIntRect* aData) {
    865  if (!mFrame) {
    866    return;
    867  }
    868 
    869  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
    870    mFrame->InvalidateFrame();
    871    nsLayoutUtils::PostRestyleEvent(mFrame->GetContent()->AsElement(),
    872                                    RestyleHint{0},
    873                                    nsChangeHint_InvalidateRenderingObservers);
    874    SVGUtils::ScheduleReflowSVG(mFrame);
    875  }
    876 
    877  if (aType == imgINotificationObserver::FRAME_UPDATE) {
    878    // No new dimensions, so we don't need to call
    879    // SVGUtils::InvalidateAndScheduleBoundsUpdate.
    880    nsLayoutUtils::PostRestyleEvent(mFrame->GetContent()->AsElement(),
    881                                    RestyleHint{0},
    882                                    nsChangeHint_InvalidateRenderingObservers);
    883    mFrame->InvalidateFrame();
    884  }
    885 
    886  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
    887    // Called once the resource's dimensions have been obtained.
    888    nsCOMPtr<imgIContainer> image;
    889    aRequest->GetImage(getter_AddRefs(image));
    890    if (image) {
    891      StyleImageOrientation orientation =
    892          mFrame->StyleVisibility()->UsedImageOrientation(aRequest);
    893      image = nsLayoutUtils::OrientImage(image, orientation);
    894      image->SetAnimationMode(mFrame->PresContext()->ImageAnimationMode());
    895      mFrame->mImageContainer = std::move(image);
    896    }
    897    mFrame->InvalidateFrame();
    898    nsLayoutUtils::PostRestyleEvent(mFrame->GetContent()->AsElement(),
    899                                    RestyleHint{0},
    900                                    nsChangeHint_InvalidateRenderingObservers);
    901    SVGUtils::ScheduleReflowSVG(mFrame);
    902  }
    903 }
    904 
    905 }  // namespace mozilla