tor-browser

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

nsImageFrame.cpp (111338B)


      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 /* rendering object for replaced elements with image data */
      8 
      9 #include "nsImageFrame.h"
     10 
     11 #include <algorithm>
     12 
     13 #include "TextDrawTarget.h"
     14 #include "gfx2DGlue.h"
     15 #include "gfxContext.h"
     16 #include "gfxUtils.h"
     17 #include "mozilla/ComputedStyle.h"
     18 #include "mozilla/DebugOnly.h"
     19 #include "mozilla/Encoding.h"
     20 #include "mozilla/HTMLEditor.h"
     21 #include "mozilla/MouseEvents.h"
     22 #include "mozilla/PresShell.h"
     23 #include "mozilla/PresShellInlines.h"
     24 #include "mozilla/SVGImageContext.h"
     25 #include "mozilla/StaticPrefs_browser.h"
     26 #include "mozilla/StaticPrefs_image.h"
     27 #include "mozilla/StaticPrefs_layout.h"
     28 #include "mozilla/dom/Document.h"
     29 #include "mozilla/dom/FetchPriority.h"
     30 #include "mozilla/dom/GeneratedImageContent.h"
     31 #include "mozilla/dom/HTMLAreaElement.h"
     32 #include "mozilla/dom/HTMLImageElement.h"
     33 #include "mozilla/dom/LargestContentfulPaint.h"
     34 #include "mozilla/dom/NameSpaceConstants.h"
     35 #include "mozilla/dom/ReferrerInfo.h"
     36 #include "mozilla/dom/ResponsiveImageSelector.h"
     37 #include "mozilla/dom/ViewTransition.h"
     38 #include "mozilla/gfx/2D.h"
     39 #include "mozilla/gfx/Helpers.h"
     40 #include "mozilla/gfx/PathHelpers.h"
     41 #include "mozilla/image/WebRenderImageProvider.h"
     42 #include "mozilla/intl/BidiEmbeddingLevel.h"
     43 #include "mozilla/layers/RenderRootStateManager.h"
     44 #include "mozilla/layers/WebRenderLayerManager.h"
     45 #include "nsCOMPtr.h"
     46 #include "nsCSSAnonBoxes.h"
     47 #include "nsCSSRendering.h"
     48 #include "nsContentUtils.h"
     49 #include "nsFontMetrics.h"
     50 #include "nsGkAtoms.h"
     51 #include "nsIFrameInlines.h"
     52 #include "nsIImageLoadingContent.h"
     53 #include "nsILoadGroup.h"
     54 #include "nsImageLoadingContent.h"
     55 #include "nsImageMap.h"
     56 #include "nsImageRenderer.h"
     57 #include "nsNameSpaceManager.h"
     58 #include "nsNetCID.h"
     59 #include "nsNetUtil.h"
     60 #include "nsObjectLoadingContent.h"
     61 #include "nsPresContext.h"
     62 #include "nsPrintfCString.h"
     63 #include "nsString.h"
     64 #include "nsStyleConsts.h"
     65 #include "nsStyleUtil.h"
     66 #include "nsTransform2D.h"
     67 #ifdef ACCESSIBILITY
     68 #  include "nsAccessibilityService.h"
     69 #endif
     70 #include "DisplayListClipState.h"
     71 #include "ImageContainer.h"
     72 #include "ImageRegion.h"
     73 #include "gfxRect.h"
     74 #include "imgIContainer.h"
     75 #include "imgLoader.h"
     76 #include "imgRequestProxy.h"
     77 #include "mozilla/ServoStyleSet.h"
     78 #include "mozilla/dom/BrowserChild.h"
     79 #include "mozilla/dom/HTMLAnchorElement.h"
     80 #include "mozilla/dom/Link.h"
     81 #include "mozilla/dom/Selection.h"
     82 #include "nsBidiPresUtils.h"
     83 #include "nsBidiUtils.h"
     84 #include "nsBlockFrame.h"
     85 #include "nsCSSFrameConstructor.h"
     86 #include "nsDisplayList.h"
     87 #include "nsError.h"
     88 #include "nsIContent.h"
     89 #include "nsIURIMutator.h"
     90 #include "nsLayoutUtils.h"
     91 #include "nsRange.h"
     92 #include "nsStyleStructInlines.h"
     93 
     94 using namespace mozilla;
     95 using namespace mozilla::dom;
     96 using namespace mozilla::gfx;
     97 using namespace mozilla::image;
     98 using namespace mozilla::layers;
     99 
    100 using mozilla::layout::TextDrawTarget;
    101 
    102 static constexpr wr::ImageKey kNoKey{{0}, 0};
    103 
    104 class nsDisplayGradient final : public nsPaintedDisplayItem {
    105 public:
    106  nsDisplayGradient(nsDisplayListBuilder* aBuilder, nsImageFrame* aFrame)
    107      : nsPaintedDisplayItem(aBuilder, aFrame) {
    108    MOZ_COUNT_CTOR(nsDisplayGradient);
    109  }
    110 
    111  MOZ_COUNTED_DTOR_FINAL(nsDisplayGradient)
    112 
    113  nsRect GetBounds(bool* aSnap) const {
    114    *aSnap = true;
    115    return Frame()->GetContentRectRelativeToSelf() + ToReferenceFrame();
    116  }
    117 
    118  nsRect GetBounds(nsDisplayListBuilder*, bool* aSnap) const final {
    119    return GetBounds(aSnap);
    120  }
    121 
    122  void Paint(nsDisplayListBuilder*, gfxContext* aCtx) final;
    123 
    124  bool CreateWebRenderCommands(wr::DisplayListBuilder&,
    125                               wr::IpcResourceUpdateQueue&,
    126                               const StackingContextHelper&,
    127                               layers::RenderRootStateManager*,
    128                               nsDisplayListBuilder*) final;
    129 
    130  NS_DISPLAY_DECL_NAME("Gradient", TYPE_GRADIENT)
    131 };
    132 
    133 void nsDisplayGradient::Paint(nsDisplayListBuilder* aBuilder,
    134                              gfxContext* aCtx) {
    135  auto* frame = static_cast<nsImageFrame*>(Frame());
    136  nsImageRenderer imageRenderer(frame, frame->GetImageFromStyle(),
    137                                aBuilder->GetImageRendererFlags());
    138  nsSize size = frame->GetSize();
    139  imageRenderer.SetPreferredSize({}, size);
    140 
    141  ImgDrawResult result;
    142  if (!imageRenderer.PrepareImage()) {
    143    result = imageRenderer.PrepareResult();
    144  } else {
    145    nsRect dest(ToReferenceFrame(), size);
    146    result = imageRenderer.DrawLayer(
    147        frame->PresContext(), *aCtx, dest, dest, dest.TopLeft(),
    148        GetPaintRect(aBuilder, aCtx), dest.Size(), /* aOpacity = */ 1.0f);
    149  }
    150  (void)result;
    151 }
    152 
    153 bool nsDisplayGradient::CreateWebRenderCommands(
    154    wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
    155    const StackingContextHelper& aSc, layers::RenderRootStateManager* aManager,
    156    nsDisplayListBuilder* aDisplayListBuilder) {
    157  auto* frame = static_cast<nsImageFrame*>(Frame());
    158  nsImageRenderer imageRenderer(frame, frame->GetImageFromStyle(),
    159                                aDisplayListBuilder->GetImageRendererFlags());
    160  nsSize size = frame->GetSize();
    161  imageRenderer.SetPreferredSize({}, size);
    162 
    163  ImgDrawResult result;
    164  if (!imageRenderer.PrepareImage()) {
    165    result = imageRenderer.PrepareResult();
    166  } else {
    167    nsRect dest(ToReferenceFrame(), size);
    168    result = imageRenderer.BuildWebRenderDisplayItemsForLayer(
    169        frame->PresContext(), aBuilder, aResources, aSc, aManager, this, dest,
    170        dest, dest.TopLeft(), dest, dest.Size(),
    171        /* aOpacity = */ 1.0f);
    172    if (result == ImgDrawResult::NOT_SUPPORTED) {
    173      return false;
    174    }
    175  }
    176  return true;
    177 }
    178 
    179 // sizes (pixels) for image icon, padding and border frame
    180 #define ICON_SIZE (16)
    181 #define ICON_PADDING (3)
    182 #define ALT_BORDER_WIDTH (1)
    183 
    184 // Default alignment value (so we can tell an unset value from a set value)
    185 #define ALIGN_UNSET uint8_t(-1)
    186 
    187 class BrokenImageIcon final : public imgINotificationObserver {
    188  // private class that wraps the data and logic needed for
    189  // broken image and loading image icons
    190 public:
    191  explicit BrokenImageIcon(const nsImageFrame& aFrame);
    192  static void Shutdown();
    193 
    194  NS_DECL_ISUPPORTS
    195  NS_DECL_IMGINOTIFICATIONOBSERVER
    196 
    197  static imgRequestProxy* GetImage(nsImageFrame* aFrame) {
    198    return Get(*aFrame).mImage.get();
    199  }
    200 
    201  static void AddObserver(nsImageFrame* aFrame) {
    202    auto& instance = Get(*aFrame);
    203    MOZ_ASSERT(!instance.mObservers.Contains(aFrame),
    204               "Observer shouldn't aleady be in array");
    205    instance.mObservers.AppendElement(aFrame);
    206  }
    207 
    208  static void RemoveObserver(nsImageFrame* aFrame) {
    209    auto& instance = Get(*aFrame);
    210    DebugOnly<bool> didRemove = instance.mObservers.RemoveElement(aFrame);
    211    MOZ_ASSERT(didRemove, "Observer not in array");
    212  }
    213 
    214 private:
    215  static BrokenImageIcon& Get(const nsImageFrame& aFrame) {
    216    if (!gSingleton) {
    217      gSingleton = new BrokenImageIcon(aFrame);
    218    }
    219    return *gSingleton;
    220  }
    221 
    222  ~BrokenImageIcon() = default;
    223 
    224  nsTObserverArray<nsImageFrame*> mObservers;
    225  RefPtr<imgRequestProxy> mImage;
    226 
    227  static StaticRefPtr<BrokenImageIcon> gSingleton;
    228 };
    229 
    230 StaticRefPtr<BrokenImageIcon> BrokenImageIcon::gSingleton;
    231 
    232 NS_IMPL_ISUPPORTS(BrokenImageIcon, imgINotificationObserver)
    233 
    234 BrokenImageIcon::BrokenImageIcon(const nsImageFrame& aFrame) {
    235  constexpr auto brokenSrc = u"resource://gre-resources/broken-image.png"_ns;
    236  nsCOMPtr<nsIURI> realURI;
    237  NS_NewURI(getter_AddRefs(realURI), brokenSrc);
    238 
    239  MOZ_ASSERT(realURI, "how?");
    240  if (NS_WARN_IF(!realURI)) {
    241    return;
    242  }
    243 
    244  nsPresContext* pc = aFrame.PresContext();
    245  Document* doc = pc->Document();
    246  RefPtr<imgLoader> il = nsContentUtils::GetImgLoaderForDocument(doc);
    247 
    248  nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
    249 
    250  // For icon loads, we don't need to merge with the loadgroup flags
    251  const nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
    252  const nsContentPolicyType contentPolicyType =
    253      nsIContentPolicy::TYPE_INTERNAL_IMAGE;
    254 
    255  nsresult rv =
    256      il->LoadImage(realURI, /* icon URI */
    257                    nullptr, /* initial document URI; this is only
    258                               relevant for cookies, so does not
    259                               apply to icons. */
    260                    nullptr, /* referrer (not relevant for icons) */
    261                    nullptr, /* principal (not relevant for icons) */
    262                    0, loadGroup, this, nullptr, /* No context */
    263                    nullptr, /* Not associated with any particular document */
    264                    loadFlags, nullptr, contentPolicyType, u""_ns,
    265                    false, /* aUseUrgentStartForChannel */
    266                    false, /* aLinkPreload */
    267                    0, FetchPriority::Auto, getter_AddRefs(mImage));
    268  (void)NS_WARN_IF(NS_FAILED(rv));
    269 }
    270 
    271 void BrokenImageIcon::Shutdown() {
    272  if (!gSingleton) {
    273    return;
    274  }
    275  if (gSingleton->mImage) {
    276    gSingleton->mImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
    277    gSingleton->mImage = nullptr;
    278  }
    279  gSingleton = nullptr;
    280 }
    281 
    282 void BrokenImageIcon::Notify(imgIRequest* aRequest, int32_t aType,
    283                             const nsIntRect* aData) {
    284  MOZ_ASSERT(aRequest);
    285 
    286  if (aType != imgINotificationObserver::LOAD_COMPLETE &&
    287      aType != imgINotificationObserver::FRAME_UPDATE) {
    288    return;
    289  }
    290 
    291  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
    292    nsCOMPtr<imgIContainer> image;
    293    aRequest->GetImage(getter_AddRefs(image));
    294    if (!image) {
    295      return;
    296    }
    297 
    298    // Retrieve the image's intrinsic size.
    299    int32_t width = 0;
    300    int32_t height = 0;
    301    image->GetWidth(&width);
    302    image->GetHeight(&height);
    303 
    304    // Request a decode at that size.
    305    image->RequestDecodeForSize(IntSize(width, height),
    306                                imgIContainer::DECODE_FLAGS_DEFAULT |
    307                                    imgIContainer::FLAG_HIGH_QUALITY_SCALING);
    308  }
    309 
    310  for (nsImageFrame* frame : mObservers.ForwardRange()) {
    311    frame->InvalidateFrame();
    312  }
    313 }
    314 
    315 // test if the width and height are fixed, looking at the style data
    316 // This is used by nsImageFrame::ImageFrameTypeFor and should not be used for
    317 // layout decisions.
    318 static bool HaveSpecifiedSize(const nsStylePosition* aStylePosition,
    319                              const AnchorPosResolutionParams& aParams) {
    320  // check the width and height values in the reflow input's style struct
    321  // - if width and height are specified as either coord or percentage, then
    322  //   the size of the image frame is constrained
    323  return aStylePosition->GetWidth(aParams)->IsLengthPercentage() &&
    324         aStylePosition->GetHeight(aParams)->IsLengthPercentage();
    325 }
    326 
    327 template <typename SizeOrMaxSize>
    328 static bool DependsOnIntrinsicSize(const SizeOrMaxSize& aMinOrMaxSize) {
    329  auto length = nsIFrame::ToExtremumLength(aMinOrMaxSize);
    330  if (!length) {
    331    return false;
    332  }
    333  switch (*length) {
    334    case nsIFrame::ExtremumLength::MinContent:
    335    case nsIFrame::ExtremumLength::MaxContent:
    336    case nsIFrame::ExtremumLength::FitContent:
    337    case nsIFrame::ExtremumLength::FitContentFunction:
    338      return true;
    339    case nsIFrame::ExtremumLength::MozAvailable:
    340    case nsIFrame::ExtremumLength::Stretch:
    341      return false;
    342  }
    343  MOZ_ASSERT_UNREACHABLE("Unknown sizing keyword?");
    344  return false;
    345 }
    346 
    347 // Decide whether we can optimize away reflows that result from the
    348 // image's intrinsic size changing.
    349 static bool SizeDependsOnIntrinsicSize(const ReflowInput& aReflowInput) {
    350  const auto& position = *aReflowInput.mStylePosition;
    351  const auto anchorResolutionParams =
    352      AnchorPosResolutionParams::From(&aReflowInput);
    353  WritingMode wm = aReflowInput.GetWritingMode();
    354  // Don't try to make this optimization when an image has percentages
    355  // in its 'width' or 'height'.  The percentages might be treated like
    356  // auto (especially for intrinsic width calculations and for heights).
    357  //
    358  // min-width: min-content and such can also affect our intrinsic size.
    359  // but note that those keywords on the block axis behave like auto, so we
    360  // don't need to check them.
    361  //
    362  // Flex item's min-[width|height]:auto resolution depends on intrinsic size.
    363  return !position.GetHeight(anchorResolutionParams)->ConvertsToLength() ||
    364         !position.GetWidth(anchorResolutionParams)->ConvertsToLength() ||
    365         DependsOnIntrinsicSize(
    366             *position.MinISize(wm, anchorResolutionParams)) ||
    367         DependsOnIntrinsicSize(
    368             *position.MaxISize(wm, anchorResolutionParams)) ||
    369         aReflowInput.mFrame->IsFlexItem();
    370 }
    371 
    372 nsIFrame* NS_NewImageFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
    373  return new (aPresShell) nsImageFrame(aStyle, aPresShell->GetPresContext(),
    374                                       nsImageFrame::Kind::ImageLoadingContent);
    375 }
    376 
    377 static bool ShouldCreateImageFrameForContentProperty(
    378    const ComputedStyle& aStyle) {
    379  Span<const StyleContentItem> items =
    380      aStyle.StyleContent()->NonAltContentItems();
    381  return items.Length() == 1 && items[0].IsImage();
    382 }
    383 
    384 nsIFrame* NS_NewXULImageFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
    385  auto kind = ShouldCreateImageFrameForContentProperty(*aStyle)
    386                  ? nsImageFrame::Kind::ContentProperty
    387                  : nsImageFrame::Kind::XULImage;
    388  return new (aPresShell)
    389      nsImageFrame(aStyle, aPresShell->GetPresContext(), kind);
    390 }
    391 
    392 nsIFrame* NS_NewImageFrameForContentProperty(PresShell* aPresShell,
    393                                             ComputedStyle* aStyle) {
    394  return new (aPresShell) nsImageFrame(aStyle, aPresShell->GetPresContext(),
    395                                       nsImageFrame::Kind::ContentProperty);
    396 }
    397 
    398 nsIFrame* NS_NewImageFrameForGeneratedContentIndex(PresShell* aPresShell,
    399                                                   ComputedStyle* aStyle) {
    400  return new (aPresShell)
    401      nsImageFrame(aStyle, aPresShell->GetPresContext(),
    402                   nsImageFrame::Kind::ContentPropertyAtIndex);
    403 }
    404 
    405 nsIFrame* NS_NewImageFrameForListStyleImage(PresShell* aPresShell,
    406                                            ComputedStyle* aStyle) {
    407  return new (aPresShell) nsImageFrame(aStyle, aPresShell->GetPresContext(),
    408                                       nsImageFrame::Kind::ListStyleImage);
    409 }
    410 
    411 nsIFrame* NS_NewImageFrameForViewTransition(PresShell* aPresShell,
    412                                            ComputedStyle* aStyle) {
    413  return new (aPresShell) nsImageFrame(aStyle, aPresShell->GetPresContext(),
    414                                       nsImageFrame::Kind::ViewTransition);
    415 }
    416 
    417 bool nsImageFrame::ShouldShowBrokenImageIcon() const {
    418  // NOTE(emilio, https://github.com/w3c/csswg-drafts/issues/2832): WebKit and
    419  // Blink behave differently here for content: url(..), for now adapt to
    420  // Blink's behavior.
    421  if (mKind != Kind::ImageLoadingContent) {
    422    return false;
    423  }
    424 
    425  if (!StaticPrefs::browser_display_show_image_placeholders()) {
    426    return false;
    427  }
    428 
    429  // <img alt=""> is special, and it shouldn't draw the broken image icon,
    430  // unlike the no-alt attribute or non-empty-alt-attribute case.
    431  if (auto* image = HTMLImageElement::FromNode(mContent)) {
    432    const nsAttrValue* alt = image->GetParsedAttr(nsGkAtoms::alt);
    433    if (alt && alt->IsEmptyString()) {
    434      return false;
    435    }
    436  }
    437 
    438  // check for broken images. valid null images (eg. img src="") are
    439  // not considered broken because they have no image requests
    440  if (nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest()) {
    441    uint32_t imageStatus;
    442    return NS_SUCCEEDED(currentRequest->GetImageStatus(&imageStatus)) &&
    443           (imageStatus & imgIRequest::STATUS_ERROR);
    444  }
    445 
    446  nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
    447  MOZ_ASSERT(imageLoader);
    448  // Show the broken image icon only if we've tried to perform a load at all
    449  // (that is, if we have a current uri).
    450  nsCOMPtr<nsIURI> currentURI = imageLoader->GetCurrentURI();
    451  return !!currentURI;
    452 }
    453 
    454 nsImageFrame* nsImageFrame::CreateContinuingFrame(
    455    mozilla::PresShell* aPresShell, ComputedStyle* aStyle) const {
    456  return new (aPresShell)
    457      nsImageFrame(aStyle, aPresShell->GetPresContext(), mKind);
    458 }
    459 
    460 NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame)
    461 
    462 nsImageFrame::nsImageFrame(ComputedStyle* aStyle, nsPresContext* aPresContext,
    463                           ClassID aID, Kind aKind)
    464    : nsAtomicContainerFrame(aStyle, aPresContext, aID),
    465      mIntrinsicSize(0, 0),
    466      mKind(aKind) {
    467  EnableVisibilityTracking();
    468 }
    469 
    470 nsImageFrame::~nsImageFrame() = default;
    471 
    472 NS_QUERYFRAME_HEAD(nsImageFrame)
    473  NS_QUERYFRAME_ENTRY(nsImageFrame)
    474 NS_QUERYFRAME_TAIL_INHERITING(nsAtomicContainerFrame)
    475 
    476 #ifdef ACCESSIBILITY
    477 a11y::AccType nsImageFrame::AccessibleType() {
    478  if (mKind == Kind::ListStyleImage) {
    479    // This is an HTMLListBulletAccessible.
    480    return a11y::eNoType;
    481  }
    482 
    483  if (mKind == Kind::ViewTransition) {
    484    // View transitions don't show up in the a11y tree.
    485    return a11y::eNoType;
    486  }
    487 
    488  // Don't use GetImageMap() to avoid reentrancy into accessibility.
    489  if (HasImageMap()) {
    490    return a11y::eHTMLImageMapType;
    491  }
    492 
    493  return a11y::eImageType;
    494 }
    495 #endif
    496 
    497 void nsImageFrame::DisconnectMap() {
    498  if (!mImageMap) {
    499    return;
    500  }
    501 
    502  mImageMap->Destroy();
    503  mImageMap = nullptr;
    504 
    505 #ifdef ACCESSIBILITY
    506  if (nsAccessibilityService* accService = GetAccService()) {
    507    accService->RecreateAccessible(PresShell(), mContent);
    508  }
    509 #endif
    510 }
    511 
    512 void nsImageFrame::Destroy(DestroyContext& aContext) {
    513  MaybeSendIntrinsicSizeAndRatioToEmbedder(Nothing(), Nothing());
    514 
    515  if (mReflowCallbackPosted) {
    516    PresShell()->CancelReflowCallback(this);
    517    mReflowCallbackPosted = false;
    518  }
    519 
    520  // Tell our image map, if there is one, to clean up
    521  // This causes the nsImageMap to unregister itself as
    522  // a DOM listener.
    523  DisconnectMap();
    524 
    525  MOZ_ASSERT(mListener);
    526 
    527  if (mKind == Kind::ImageLoadingContent) {
    528    MOZ_ASSERT(!mOwnedRequest);
    529    MOZ_ASSERT(!mOwnedRequestRegistered);
    530    nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
    531    MOZ_ASSERT(imageLoader);
    532 
    533    // Notify our image loading content that we are going away so it can
    534    // deregister with our refresh driver.
    535    imageLoader->FrameDestroyed(this);
    536    imageLoader->RemoveNativeObserver(mListener);
    537  } else {
    538    DeinitOwnedRequest();
    539  }
    540 
    541  // set the frame to null so we don't send messages to a dead object.
    542  mListener->SetFrame(nullptr);
    543  mListener = nullptr;
    544 
    545  // If we were displaying an icon, take ourselves off the list
    546  if (mDisplayingIcon) {
    547    BrokenImageIcon::RemoveObserver(this);
    548  }
    549 
    550  nsAtomicContainerFrame::Destroy(aContext);
    551 }
    552 
    553 void nsImageFrame::DeinitOwnedRequest() {
    554  MOZ_ASSERT(mKind != Kind::ImageLoadingContent);
    555  if (!mOwnedRequest) {
    556    return;
    557  }
    558  PresContext()->Document()->UntrackImage(mOwnedRequest);
    559  nsLayoutUtils::DeregisterImageRequest(PresContext(), mOwnedRequest,
    560                                        &mOwnedRequestRegistered);
    561  mOwnedRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
    562  mOwnedRequest = nullptr;
    563 }
    564 
    565 void nsImageFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
    566  nsAtomicContainerFrame::DidSetComputedStyle(aOldStyle);
    567 
    568  // A list-style-image ::marker default size is calculated from the font's
    569  // em-size, which might have changed here.
    570  if (mKind == Kind::ListStyleImage) {
    571    UpdateIntrinsicSize();
    572  }
    573 
    574  // Normal "owned" images reframe when `content` or `list-style-image` change,
    575  // but XUL images don't (and we don't really need to). So deal with the
    576  // dynamic list-style-image change in that case.
    577  //
    578  // TODO(emilio): We might want to do the same for regular list-style-image or
    579  // even simple content: url() changes.
    580  if (mKind == Kind::XULImage && aOldStyle) {
    581    if (!mContent->AsElement()->HasNonEmptyAttr(nsGkAtoms::src) &&
    582        aOldStyle->StyleList()->mListStyleImage !=
    583            StyleList()->mListStyleImage) {
    584      UpdateXULImage();
    585    }
    586    // If we have no image our intrinsic size might be themed. We need to
    587    // update the size even if the effective appearance hasn't changed to
    588    // deal correctly with theme changes.
    589    if (!mOwnedRequest) {
    590      UpdateIntrinsicSize();
    591    }
    592  }
    593 
    594  // We need to update our orientation either if we had no ComputedStyle before
    595  // because this is the first time it's been set, or if the image-orientation
    596  // property changed from its previous value.
    597  bool shouldUpdateOrientation = false;
    598  nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest();
    599  const auto newOrientation =
    600      StyleVisibility()->UsedImageOrientation(currentRequest);
    601  if (mImage) {
    602    if (aOldStyle) {
    603      auto oldOrientation =
    604          aOldStyle->StyleVisibility()->UsedImageOrientation(currentRequest);
    605      shouldUpdateOrientation = oldOrientation != newOrientation;
    606    } else {
    607      shouldUpdateOrientation = true;
    608    }
    609  }
    610 
    611  if (shouldUpdateOrientation) {
    612    nsCOMPtr<imgIContainer> image(mImage->Unwrap());
    613    mImage = nsLayoutUtils::OrientImage(image, newOrientation);
    614 
    615    UpdateIntrinsicSize();
    616    UpdateIntrinsicRatio();
    617  } else if (!aOldStyle || aOldStyle->StylePosition()->mAspectRatio !=
    618                               StylePosition()->mAspectRatio) {
    619    UpdateIntrinsicRatio();
    620  }
    621 }
    622 
    623 static bool SizeIsAvailable(imgIRequest* aRequest) {
    624  if (!aRequest) {
    625    return false;
    626  }
    627 
    628  uint32_t imageStatus = 0;
    629  nsresult rv = aRequest->GetImageStatus(&imageStatus);
    630  return NS_SUCCEEDED(rv) && (imageStatus & imgIRequest::STATUS_SIZE_AVAILABLE);
    631 }
    632 
    633 const StyleImage* nsImageFrame::GetImageFromStyle() const {
    634  switch (mKind) {
    635    case Kind::ViewTransition:
    636      break;
    637    case Kind::ImageLoadingContent:
    638      break;
    639    case Kind::ListStyleImage:
    640      MOZ_ASSERT(
    641          GetParent()->GetContent()->IsGeneratedContentContainerForMarker());
    642      MOZ_ASSERT(mContent->IsHTMLElement(nsGkAtoms::mozgeneratedcontentimage));
    643      return &StyleList()->mListStyleImage;
    644    case Kind::XULImage:
    645      MOZ_ASSERT(!mContent->AsElement()->HasNonEmptyAttr(nsGkAtoms::src));
    646      return &StyleList()->mListStyleImage;
    647    case Kind::ContentProperty:
    648    case Kind::ContentPropertyAtIndex: {
    649      uint32_t contentIndex = 0;
    650      const nsStyleContent* styleContent = StyleContent();
    651      if (mKind == Kind::ContentPropertyAtIndex) {
    652        MOZ_RELEASE_ASSERT(
    653            mContent->IsHTMLElement(nsGkAtoms::mozgeneratedcontentimage));
    654        contentIndex =
    655            static_cast<GeneratedImageContent*>(mContent.get())->Index();
    656 
    657        // TODO(emilio): Consider inheriting the `content` property instead of
    658        // doing this parent traversal?
    659        nsIFrame* parent = GetParent();
    660        MOZ_DIAGNOSTIC_ASSERT(
    661            parent->GetContent()->IsGeneratedContentContainerForMarker() ||
    662            parent->GetContent()->IsGeneratedContentContainerForAfter() ||
    663            parent->GetContent()->IsGeneratedContentContainerForBefore());
    664        nsIFrame* nonAnonymousParent = parent;
    665        while (nonAnonymousParent->Style()->IsAnonBox()) {
    666          nonAnonymousParent = nonAnonymousParent->GetParent();
    667        }
    668        MOZ_DIAGNOSTIC_ASSERT(parent->GetContent() ==
    669                              nonAnonymousParent->GetContent());
    670        styleContent = nonAnonymousParent->StyleContent();
    671      }
    672      auto items = styleContent->NonAltContentItems();
    673      MOZ_RELEASE_ASSERT(contentIndex < items.Length());
    674      const auto& contentItem = items[contentIndex];
    675      MOZ_RELEASE_ASSERT(contentItem.IsImage());
    676      return &contentItem.AsImage();
    677    }
    678  }
    679  MOZ_ASSERT_UNREACHABLE("Don't call me");
    680  return nullptr;
    681 }
    682 
    683 void nsImageFrame::UpdateXULImage() {
    684  MOZ_ASSERT(mKind == Kind::XULImage);
    685  DeinitOwnedRequest();
    686 
    687  nsAutoString src;
    688  nsPresContext* pc = PresContext();
    689  if (mContent->AsElement()->GetAttr(nsGkAtoms::src, src) && !src.IsEmpty()) {
    690    nsContentPolicyType contentPolicyType;
    691    nsCOMPtr<nsIPrincipal> triggeringPrincipal;
    692    uint64_t requestContextID = 0;
    693    nsContentUtils::GetContentPolicyTypeForUIImageLoading(
    694        mContent, getter_AddRefs(triggeringPrincipal), contentPolicyType,
    695        &requestContextID);
    696    nsCOMPtr<nsIURI> uri;
    697    nsContentUtils::NewURIWithDocumentCharset(
    698        getter_AddRefs(uri), src, pc->Document(), mContent->GetBaseURI());
    699    if (uri) {
    700      auto referrerInfo = MakeRefPtr<ReferrerInfo>(*mContent->AsElement());
    701      nsContentUtils::LoadImage(
    702          uri, mContent, pc->Document(), triggeringPrincipal, requestContextID,
    703          referrerInfo, mListener, nsIRequest::LOAD_NORMAL, u""_ns,
    704          getter_AddRefs(mOwnedRequest), contentPolicyType);
    705      SetupOwnedRequest();
    706    }
    707  } else {
    708    const auto* image = GetImageFromStyle();
    709    if (image->IsImageRequestType()) {
    710      if (imgRequestProxy* proxy = image->GetImageRequest()) {
    711        proxy->Clone(mListener, pc->Document(), getter_AddRefs(mOwnedRequest));
    712        SetupOwnedRequest();
    713      }
    714    }
    715  }
    716 
    717  if (!mOwnedRequest) {
    718    UpdateImage(nullptr, nullptr);
    719  }
    720 }
    721 
    722 void nsImageFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
    723                        nsIFrame* aPrevInFlow) {
    724  MOZ_ASSERT_IF(aPrevInFlow,
    725                aPrevInFlow->Type() == Type() &&
    726                    static_cast<nsImageFrame*>(aPrevInFlow)->mKind == mKind);
    727 
    728  nsAtomicContainerFrame::Init(aContent, aParent, aPrevInFlow);
    729 
    730  mListener = new nsImageListener(this);
    731 
    732  GetImageMap();  // Ensure to init the image map asap. This is important to
    733                  // make <area> elements focusable.
    734 
    735  if (StaticPrefs::layout_image_eager_broken_image_icon()) {
    736    (void)BrokenImageIcon::GetImage(this);
    737  }
    738 
    739  nsPresContext* pc = PresContext();
    740  if (mKind == Kind::ImageLoadingContent) {
    741    nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aContent);
    742    MOZ_ASSERT(imageLoader);
    743    imageLoader->AddNativeObserver(mListener);
    744    // We have a PresContext now, so we need to notify the image content node
    745    // that it can register images.
    746    imageLoader->FrameCreated(this);
    747    AssertSyncDecodingHintIsInSync();
    748    if (nsIDocShell* docShell = pc->GetDocShell()) {
    749      RefPtr<BrowsingContext> bc = docShell->GetBrowsingContext();
    750      mIsInObjectOrEmbed = bc->IsEmbedderTypeObjectOrEmbed() &&
    751                           pc->Document()->IsImageDocument();
    752    }
    753  } else if (mKind == Kind::XULImage) {
    754    UpdateXULImage();
    755  } else if (mKind == Kind::ViewTransition) {
    756    // View transitions have a surface directly.
    757  } else {
    758    const StyleImage* image = GetImageFromStyle();
    759    if (image->IsImageRequestType()) {
    760      if (imgRequestProxy* proxy = image->GetImageRequest()) {
    761        proxy->Clone(mListener, pc->Document(), getter_AddRefs(mOwnedRequest));
    762        SetupOwnedRequest();
    763      }
    764    }
    765  }
    766 
    767  // Give image loads associated with an image frame a small priority boost.
    768  if (nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest()) {
    769    uint32_t categoryToBoostPriority = imgIRequest::CATEGORY_FRAME_INIT;
    770 
    771    // Increase load priority further if intrinsic size might be important for
    772    // layout.
    773    if (!HaveSpecifiedSize(StylePosition(),
    774                           AnchorPosResolutionParams::From(this))) {
    775      categoryToBoostPriority |= imgIRequest::CATEGORY_SIZE_QUERY;
    776    }
    777 
    778    currentRequest->BoostPriority(categoryToBoostPriority);
    779  }
    780 
    781  MaybeSendIntrinsicSizeAndRatioToEmbedder();
    782 }
    783 
    784 void nsImageFrame::SetupOwnedRequest() {
    785  MOZ_ASSERT(mKind != Kind::ImageLoadingContent);
    786  if (!mOwnedRequest) {
    787    return;
    788  }
    789 
    790  // We're not using AssociateRequestToFrame for the content property, so we
    791  // need to add it to the image tracker manually.
    792  PresContext()->Document()->TrackImage(mOwnedRequest);
    793 
    794  uint32_t status = 0;
    795  nsresult rv = mOwnedRequest->GetImageStatus(&status);
    796  if (NS_FAILED(rv)) {
    797    return;
    798  }
    799 
    800  if (status & imgIRequest::STATUS_SIZE_AVAILABLE) {
    801    nsCOMPtr<imgIContainer> image;
    802    mOwnedRequest->GetImage(getter_AddRefs(image));
    803    OnSizeAvailable(mOwnedRequest, image);
    804  }
    805 
    806  if (status & imgIRequest::STATUS_FRAME_COMPLETE) {
    807    mFirstFrameComplete = true;
    808  }
    809 
    810  if (status & imgIRequest::STATUS_IS_ANIMATED) {
    811    nsLayoutUtils::RegisterImageRequest(PresContext(), mOwnedRequest,
    812                                        &mOwnedRequestRegistered);
    813  }
    814 }
    815 
    816 static void ScaleIntrinsicSizeForDensity(IntrinsicSize& aSize,
    817                                         const ImageResolution& aResolution) {
    818  if (aSize.width) {
    819    aResolution.ApplyXTo(aSize.width.ref());
    820  }
    821  if (aSize.height) {
    822    aResolution.ApplyYTo(aSize.height.ref());
    823  }
    824 }
    825 
    826 static void ScaleIntrinsicSizeForDensity(imgIContainer* aImage,
    827                                         nsIContent& aContent,
    828                                         IntrinsicSize& aSize) {
    829  ImageResolution resolution = aImage->GetResolution();
    830  if (auto* image = HTMLImageElement::FromNode(aContent)) {
    831    if (auto* selector = image->GetResponsiveImageSelector()) {
    832      resolution.ScaleBy(selector->GetSelectedImageDensity());
    833    }
    834  }
    835  ScaleIntrinsicSizeForDensity(aSize, resolution);
    836 }
    837 
    838 static nscoord ListImageDefaultLength(const nsImageFrame& aFrame) {
    839  // https://drafts.csswg.org/css-lists-3/#image-markers
    840  // The spec says we should use 1em x 1em, but that seems too large.
    841  // See disussion in https://github.com/w3c/csswg-drafts/issues/4207
    842  auto* pc = aFrame.PresContext();
    843  RefPtr<nsFontMetrics> fm =
    844      nsLayoutUtils::GetFontMetricsForComputedStyle(aFrame.Style(), pc);
    845  RefPtr<gfxFont> font = fm->GetThebesFontGroup()->GetFirstValidFont();
    846  auto emAU =
    847      font->GetMetrics(fm->Orientation()).emHeight * pc->AppUnitsPerDevPixel();
    848  return std::max(NSToCoordRound(0.4f * emAU),
    849                  nsPresContext::CSSPixelsToAppUnits(1));
    850 }
    851 
    852 IntrinsicSize nsImageFrame::ComputeIntrinsicSize(
    853    bool aIgnoreContainment) const {
    854  const auto containAxes =
    855      aIgnoreContainment ? ContainSizeAxes(false, false) : GetContainSizeAxes();
    856  if (containAxes.IsBoth()) {
    857    return FinishIntrinsicSize(containAxes, IntrinsicSize(0, 0));
    858  }
    859 
    860  nsSize size;
    861  if (mImage && NS_SUCCEEDED(mImage->GetIntrinsicSizeInAppUnits(&size))) {
    862    IntrinsicSize intrinsicSize;
    863    intrinsicSize.width = size.width == -1 ? Nothing() : Some(size.width);
    864    intrinsicSize.height = size.height == -1 ? Nothing() : Some(size.height);
    865    if (mKind == nsImageFrame::Kind::ListStyleImage) {
    866      if (intrinsicSize.width.isNothing() || intrinsicSize.height.isNothing()) {
    867        nscoord defaultLength = ListImageDefaultLength(*this);
    868        if (intrinsicSize.width.isNothing()) {
    869          intrinsicSize.width = Some(defaultLength);
    870        }
    871        if (intrinsicSize.height.isNothing()) {
    872          intrinsicSize.height = Some(defaultLength);
    873        }
    874      }
    875    }
    876    if (mKind == nsImageFrame::Kind::ImageLoadingContent ||
    877        (mKind == nsImageFrame::Kind::XULImage &&
    878         GetContent()->AsElement()->HasNonEmptyAttr(nsGkAtoms::src))) {
    879      ScaleIntrinsicSizeForDensity(mImage, *GetContent(), intrinsicSize);
    880    } else {
    881      // We don't include zoom here because FinishIntrinsicSize already does it
    882      // for us.
    883      ScaleIntrinsicSizeForDensity(
    884          intrinsicSize,
    885          GetImageFromStyle()->GetResolution(/* aStyleForZoom = */ nullptr));
    886    }
    887    return FinishIntrinsicSize(containAxes, intrinsicSize);
    888  }
    889 
    890  if (auto size = GetViewTransitionBorderBoxSize()) {
    891    IntrinsicSize intrinsicSize;
    892    intrinsicSize.width.emplace(size->width);
    893    intrinsicSize.height.emplace(size->height);
    894    return FinishIntrinsicSize(containAxes, intrinsicSize);
    895  }
    896 
    897  if (mKind == nsImageFrame::Kind::ListStyleImage) {
    898    // Note: images are handled above, this handles gradients etc.
    899    const nscoord defaultLength = ListImageDefaultLength(*this);
    900    return FinishIntrinsicSize(containAxes,
    901                               IntrinsicSize(defaultLength, defaultLength));
    902  }
    903 
    904  if (mKind == nsImageFrame::Kind::XULImage && IsThemed()) {
    905    nsPresContext* pc = PresContext();
    906    // FIXME: const_cast here is a bit evil but IsThemed and so does the same.
    907    const auto widgetSize = pc->Theme()->GetMinimumWidgetSize(
    908        pc, const_cast<nsImageFrame*>(this),
    909        StyleDisplay()->EffectiveAppearance());
    910    const IntrinsicSize intrinsicSize(
    911        LayoutDeviceIntSize::ToAppUnits(widgetSize, pc->AppUnitsPerDevPixel()));
    912    return FinishIntrinsicSize(containAxes, intrinsicSize);
    913  }
    914 
    915  if (ShouldShowBrokenImageIcon()) {
    916    nscoord edgeLengthToUse = nsPresContext::CSSPixelsToAppUnits(
    917        ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH)));
    918    return FinishIntrinsicSize(containAxes,
    919                               IntrinsicSize(edgeLengthToUse, edgeLengthToUse));
    920  }
    921 
    922  if (ShouldUseMappedAspectRatio() &&
    923      StylePosition()->mAspectRatio.HasRatio()) {
    924    return IntrinsicSize();
    925  }
    926 
    927  // XXX: No FinishIntrinsicSize?
    928  return IntrinsicSize(0, 0);
    929 }
    930 
    931 // For compat reasons, see bug 1602047, we don't use the intrinsic ratio from
    932 // width="" and height="" for images with no src attribute (no request).
    933 //
    934 // But we shouldn't get fooled by <img loading=lazy>. We do want to apply the
    935 // ratio then...
    936 bool nsImageFrame::ShouldUseMappedAspectRatio() const {
    937  if (mKind != Kind::ImageLoadingContent) {
    938    return true;
    939  }
    940  nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest();
    941  if (currentRequest) {
    942    return true;
    943  }
    944  // TODO(emilio): Investigate the compat situation of the above check, maybe we
    945  // can just check for empty src attribute or something...
    946  auto* image = HTMLImageElement::FromNode(mContent);
    947  return image && image->IsAwaitingLoadOrLazyLoading();
    948 }
    949 
    950 bool nsImageFrame::UpdateIntrinsicSize() {
    951  IntrinsicSize oldIntrinsicSize = mIntrinsicSize;
    952  mIntrinsicSize = ComputeIntrinsicSize();
    953  return mIntrinsicSize != oldIntrinsicSize;
    954 }
    955 
    956 nsAtom* nsImageFrame::GetViewTransitionName() const {
    957  if (mKind != Kind::ViewTransition) {
    958    return nullptr;
    959  }
    960  MOZ_ASSERT(GetContent()->AsElement()->HasName());
    961  return GetContent()
    962      ->AsElement()
    963      ->GetParsedAttr(nsGkAtoms::name)
    964      ->GetAtomValue();
    965 }
    966 
    967 Maybe<nsSize> nsImageFrame::GetViewTransitionBorderBoxSize() const {
    968  auto* name = GetViewTransitionName();
    969  if (!name) {
    970    return {};
    971  }
    972  auto* vt = PresContext()->Document()->GetActiveViewTransition();
    973  if (NS_WARN_IF(!vt)) {
    974    return {};
    975  }
    976  return Style()->GetPseudoType() == PseudoStyleType::viewTransitionOld
    977             ? vt->GetOldBorderBoxSize(name)
    978             : vt->GetNewBorderBoxSize(name);
    979 }
    980 
    981 wr::ImageKey nsImageFrame::GetViewTransitionImageKey(
    982    layers::RenderRootStateManager* aManager,
    983    wr::IpcResourceUpdateQueue& aResources) const {
    984  auto* name = GetViewTransitionName();
    985  if (!name) {
    986    return kNoKey;
    987  }
    988  auto* vt = PresContext()->Document()->GetActiveViewTransition();
    989  if (NS_WARN_IF(!vt)) {
    990    return kNoKey;
    991  }
    992  const auto* key =
    993      Style()->GetPseudoType() == PseudoStyleType::viewTransitionOld
    994          ? vt->ReadOldImageKey(name, aManager, aResources)
    995          : vt->GetNewImageKey(name);
    996  return key ? *key : kNoKey;
    997 }
    998 
    999 AspectRatio nsImageFrame::ComputeIntrinsicRatioForImage(
   1000    imgIContainer* aImage, bool aIgnoreContainment) const {
   1001  if (!aIgnoreContainment && GetContainSizeAxes().IsAny()) {
   1002    return AspectRatio();
   1003  }
   1004 
   1005  if (aImage) {
   1006    if (AspectRatio fromImage = aImage->GetIntrinsicRatio()) {
   1007      return fromImage;
   1008    }
   1009  }
   1010 
   1011  if (auto size = GetViewTransitionBorderBoxSize()) {
   1012    return AspectRatio::FromSize(*size);
   1013  }
   1014 
   1015  if (ShouldUseMappedAspectRatio()) {
   1016    const StyleAspectRatio& ratio = StylePosition()->mAspectRatio;
   1017    if (ratio.auto_ && ratio.HasRatio()) {
   1018      // Return the mapped intrinsic aspect ratio stored in
   1019      // nsStylePosition::mAspectRatio.
   1020      return ratio.ratio.AsRatio().ToLayoutRatio(UseBoxSizing::Yes);
   1021    }
   1022  }
   1023  if (ShouldShowBrokenImageIcon()) {
   1024    return AspectRatio(1.0f);
   1025  }
   1026  return AspectRatio();
   1027 }
   1028 
   1029 bool nsImageFrame::UpdateIntrinsicRatio() {
   1030  AspectRatio oldIntrinsicRatio = mIntrinsicRatio;
   1031  mIntrinsicRatio = ComputeIntrinsicRatioForImage(mImage);
   1032  return mIntrinsicRatio != oldIntrinsicRatio;
   1033 }
   1034 
   1035 bool nsImageFrame::GetSourceToDestTransform(nsTransform2D& aTransform) {
   1036  nsRect destRect = GetDestRect(GetContentRectRelativeToSelf());
   1037  // Set the translation components, based on destRect
   1038  // XXXbz does this introduce rounding errors because of the cast to
   1039  // float?  Should we just manually add that stuff in every time
   1040  // instead?
   1041  aTransform.SetToTranslate(float(destRect.x), float(destRect.y));
   1042 
   1043  // NOTE(emilio): This intrinsicSize is not the same as the layout intrinsic
   1044  // size (mIntrinsicSize), which can be scaled due to ResponsiveImageSelector,
   1045  // see ScaleIntrinsicSizeForDensity.
   1046  nsSize intrinsicSize;
   1047  if (!mImage ||
   1048      !NS_SUCCEEDED(mImage->GetIntrinsicSizeInAppUnits(&intrinsicSize)) ||
   1049      intrinsicSize.IsEmpty()) {
   1050    return false;
   1051  }
   1052 
   1053  aTransform.SetScale(float(destRect.width) / float(intrinsicSize.width),
   1054                      float(destRect.height) / float(intrinsicSize.height));
   1055  return true;
   1056 }
   1057 
   1058 // This function checks whether the given request is the current request for our
   1059 // mContent.
   1060 bool nsImageFrame::IsPendingLoad(imgIRequest* aRequest) const {
   1061  // Default to pending load in case of errors
   1062  if (mKind != Kind::ImageLoadingContent) {
   1063    MOZ_ASSERT(aRequest == mOwnedRequest);
   1064    return false;
   1065  }
   1066 
   1067  nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
   1068  MOZ_ASSERT(imageLoader);
   1069 
   1070  int32_t requestType = nsIImageLoadingContent::UNKNOWN_REQUEST;
   1071  imageLoader->GetRequestType(aRequest, &requestType);
   1072 
   1073  return requestType != nsIImageLoadingContent::CURRENT_REQUEST;
   1074 }
   1075 
   1076 nsRect nsImageFrame::SourceRectToDest(const nsIntRect& aRect) {
   1077  // When scaling the image, row N of the source image may (depending on
   1078  // the scaling function) be used to draw any row in the destination image
   1079  // between floor(F * (N-1)) and ceil(F * (N+1)), where F is the
   1080  // floating-point scaling factor.  The same holds true for columns.
   1081  // So, we start by computing that bound without the floor and ceiling.
   1082 
   1083  nsRect r(nsPresContext::CSSPixelsToAppUnits(aRect.x - 1),
   1084           nsPresContext::CSSPixelsToAppUnits(aRect.y - 1),
   1085           nsPresContext::CSSPixelsToAppUnits(aRect.width + 2),
   1086           nsPresContext::CSSPixelsToAppUnits(aRect.height + 2));
   1087 
   1088  nsTransform2D sourceToDest;
   1089  if (!GetSourceToDestTransform(sourceToDest)) {
   1090    // Failed to generate transform matrix. Return our whole content area,
   1091    // to be on the safe side (since this method is used for generating
   1092    // invalidation rects).
   1093    return GetContentRectRelativeToSelf();
   1094  }
   1095 
   1096  sourceToDest.TransformCoord(&r.x, &r.y, &r.width, &r.height);
   1097 
   1098  // Now, round the edges out to the pixel boundary.
   1099  nscoord scale = nsPresContext::CSSPixelsToAppUnits(1);
   1100  nscoord right = r.x + r.width;
   1101  nscoord bottom = r.y + r.height;
   1102 
   1103  r.x -= (scale + (r.x % scale)) % scale;
   1104  r.y -= (scale + (r.y % scale)) % scale;
   1105  r.width = right + ((scale - (right % scale)) % scale) - r.x;
   1106  r.height = bottom + ((scale - (bottom % scale)) % scale) - r.y;
   1107 
   1108  return r;
   1109 }
   1110 
   1111 static bool ImageOk(ElementState aState) {
   1112  return !aState.HasState(ElementState::BROKEN);
   1113 }
   1114 
   1115 static bool HasAltText(const Element& aElement) {
   1116  // We always return some alternate text for <input>, see
   1117  // nsCSSFrameConstructor::GetAlternateTextFor.
   1118  if (aElement.IsHTMLElement(nsGkAtoms::input)) {
   1119    return true;
   1120  }
   1121 
   1122  MOZ_ASSERT(aElement.IsHTMLElement(nsGkAtoms::img));
   1123  return aElement.HasNonEmptyAttr(nsGkAtoms::alt);
   1124 }
   1125 
   1126 bool nsImageFrame::ShouldCreateImageFrameForContentProperty(
   1127    const Element& aElement, const ComputedStyle& aStyle) {
   1128  if (aElement.IsRootOfNativeAnonymousSubtree()) {
   1129    return false;
   1130  }
   1131  return ::ShouldCreateImageFrameForContentProperty(aStyle);
   1132 }
   1133 
   1134 // Check if we want to use an image frame or just let the frame constructor make
   1135 // us into an inline, and if so, which kind of image frame should we create.
   1136 /* static */
   1137 auto nsImageFrame::ImageFrameTypeFor(const Element& aElement,
   1138                                     const ComputedStyle& aStyle)
   1139    -> ImageFrameType {
   1140  if (ShouldCreateImageFrameForContentProperty(aElement, aStyle)) {
   1141    // Prefer the content property, for compat reasons, see bug 1484928.
   1142    return ImageFrameType::ForContentProperty;
   1143  }
   1144 
   1145  if (ImageOk(aElement.State())) {
   1146    // Image is fine or loading; do the image frame thing
   1147    return ImageFrameType::ForElementRequest;
   1148  }
   1149 
   1150  if (aStyle.StyleUIReset()->mMozForceBrokenImageIcon) {
   1151    return ImageFrameType::ForElementRequest;
   1152  }
   1153 
   1154  if (!HasAltText(aElement)) {
   1155    return ImageFrameType::ForElementRequest;
   1156  }
   1157 
   1158  // FIXME(emilio, bug 1788767): We definitely don't reframe when
   1159  // HaveSpecifiedSize changes...
   1160  if (aElement.OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks &&
   1161      HaveSpecifiedSize(aStyle.StylePosition(),
   1162                        {nullptr, aStyle.StyleDisplay()->mPosition})) {
   1163    return ImageFrameType::ForElementRequest;
   1164  }
   1165 
   1166  return ImageFrameType::None;
   1167 }
   1168 
   1169 void nsImageFrame::Notify(imgIRequest* aRequest, int32_t aType,
   1170                          const nsIntRect* aRect) {
   1171  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
   1172    nsCOMPtr<imgIContainer> image;
   1173    aRequest->GetImage(getter_AddRefs(image));
   1174    return OnSizeAvailable(aRequest, image);
   1175  }
   1176 
   1177  if (aType == imgINotificationObserver::FRAME_UPDATE) {
   1178    return OnFrameUpdate(aRequest, aRect);
   1179  }
   1180 
   1181  if (aType == imgINotificationObserver::FRAME_COMPLETE) {
   1182    mFirstFrameComplete = true;
   1183  }
   1184 
   1185  if (aType == imgINotificationObserver::IS_ANIMATED &&
   1186      mKind != Kind::ImageLoadingContent) {
   1187    nsLayoutUtils::RegisterImageRequest(PresContext(), mOwnedRequest,
   1188                                        &mOwnedRequestRegistered);
   1189  }
   1190 
   1191  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
   1192    LargestContentfulPaint::MaybeProcessImageForElementTiming(
   1193        static_cast<imgRequestProxy*>(aRequest), GetContent()->AsElement());
   1194    return OnLoadComplete(aRequest);
   1195  }
   1196 }
   1197 
   1198 void nsImageFrame::OnSizeAvailable(imgIRequest* aRequest,
   1199                                   imgIContainer* aImage) {
   1200  if (!aImage) {
   1201    return;
   1202  }
   1203 
   1204  /* Get requested animation policy from the pres context:
   1205   *   normal = 0
   1206   *   one frame = 1
   1207   *   one loop = 2
   1208   */
   1209  aImage->SetAnimationMode(PresContext()->ImageAnimationMode());
   1210 
   1211  if (IsPendingLoad(aRequest)) {
   1212    // We don't care
   1213    return;
   1214  }
   1215 
   1216  UpdateImage(aRequest, aImage);
   1217 }
   1218 
   1219 void nsImageFrame::UpdateImage(imgIRequest* aRequest, imgIContainer* aImage) {
   1220  if (SizeIsAvailable(aRequest)) {
   1221    StyleImageOrientation orientation =
   1222        StyleVisibility()->UsedImageOrientation(aRequest);
   1223    // This is valid and for the current request, so update our stored image
   1224    // container, orienting according to our style.
   1225    mImage = nsLayoutUtils::OrientImage(aImage, orientation);
   1226    MOZ_ASSERT(mImage);
   1227  } else {
   1228    // We no longer have a valid image, so release our stored image container.
   1229    mImage = mPrevImage = nullptr;
   1230    if (mKind == Kind::ListStyleImage) {
   1231      auto* genContent = static_cast<GeneratedImageContent*>(GetContent());
   1232      genContent->NotifyLoadFailed();
   1233      // No need to continue below since the above state change will destroy
   1234      // this frame.
   1235      return;
   1236    }
   1237  }
   1238 
   1239  UpdateIntrinsicSizeAndRatio();
   1240 
   1241  if (!GotInitialReflow()) {
   1242    return;
   1243  }
   1244 
   1245  // We're going to need to repaint now either way.
   1246  InvalidateFrame();
   1247 }
   1248 
   1249 void nsImageFrame::OnFrameUpdate(imgIRequest* aRequest,
   1250                                 const nsIntRect* aRect) {
   1251  if (NS_WARN_IF(!aRect)) {
   1252    return;
   1253  }
   1254 
   1255  if (!GotInitialReflow()) {
   1256    // Don't bother to do anything; we have a reflow coming up!
   1257    return;
   1258  }
   1259 
   1260  if (mFirstFrameComplete && !StyleVisibility()->IsVisible()) {
   1261    return;
   1262  }
   1263 
   1264  if (IsPendingLoad(aRequest)) {
   1265    // We don't care
   1266    return;
   1267  }
   1268 
   1269  nsIntRect layerInvalidRect =
   1270      mImage ? mImage->GetImageSpaceInvalidationRect(*aRect) : *aRect;
   1271 
   1272  if (layerInvalidRect.IsEqualInterior(GetMaxSizedIntRect())) {
   1273    // Invalidate our entire area.
   1274    InvalidateSelf(nullptr, nullptr);
   1275    return;
   1276  }
   1277 
   1278  nsRect frameInvalidRect = SourceRectToDest(layerInvalidRect);
   1279  InvalidateSelf(&layerInvalidRect, &frameInvalidRect);
   1280 }
   1281 
   1282 void nsImageFrame::InvalidateSelf(const nsIntRect* aLayerInvalidRect,
   1283                                  const nsRect* aFrameInvalidRect) {
   1284  // Check if WebRender has interacted with this frame. If it has
   1285  // we need to let it know that things have changed.
   1286  const auto type = DisplayItemType::TYPE_IMAGE;
   1287  const auto providerId = mImage ? mImage->GetProviderId() : 0;
   1288  if (WebRenderUserData::ProcessInvalidateForImage(this, type, providerId)) {
   1289    return;
   1290  }
   1291 
   1292  InvalidateLayer(type, aLayerInvalidRect, aFrameInvalidRect);
   1293 
   1294  if (!mFirstFrameComplete) {
   1295    InvalidateLayer(DisplayItemType::TYPE_ALT_FEEDBACK, aLayerInvalidRect,
   1296                    aFrameInvalidRect);
   1297  }
   1298 }
   1299 
   1300 void nsImageFrame::MaybeSendIntrinsicSizeAndRatioToEmbedder() {
   1301  MaybeSendIntrinsicSizeAndRatioToEmbedder(Some(GetIntrinsicSize()),
   1302                                           Some(GetAspectRatio()));
   1303 }
   1304 
   1305 void nsImageFrame::MaybeSendIntrinsicSizeAndRatioToEmbedder(
   1306    Maybe<IntrinsicSize> aIntrinsicSize, Maybe<AspectRatio> aIntrinsicRatio) {
   1307  if (!mIsInObjectOrEmbed || !mImage) {
   1308    return;
   1309  }
   1310 
   1311  nsCOMPtr<nsIDocShell> docShell = PresContext()->GetDocShell();
   1312  if (!docShell) {
   1313    return;
   1314  }
   1315 
   1316  BrowsingContext* bc = docShell->GetBrowsingContext();
   1317  if (!bc) {
   1318    return;
   1319  }
   1320  MOZ_ASSERT(bc->IsContentSubframe());
   1321 
   1322  if (bc->GetParent()->IsInProcess()) {
   1323    if (Element* embedder = bc->GetEmbedderElement()) {
   1324      if (nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(embedder)) {
   1325        static_cast<nsObjectLoadingContent*>(olc.get())
   1326            ->SubdocumentIntrinsicSizeOrRatioChanged(aIntrinsicSize,
   1327                                                     aIntrinsicRatio);
   1328      } else {
   1329        MOZ_ASSERT_UNREACHABLE("Got out of sync?");
   1330      }
   1331      return;
   1332    }
   1333  }
   1334 
   1335  if (BrowserChild* browserChild = BrowserChild::GetFrom(docShell)) {
   1336    (void)browserChild->SendIntrinsicSizeOrRatioChanged(aIntrinsicSize,
   1337                                                        aIntrinsicRatio);
   1338  }
   1339 }
   1340 
   1341 void nsImageFrame::OnLoadComplete(imgIRequest* aRequest) {
   1342  NotifyNewCurrentRequest(aRequest);
   1343 }
   1344 
   1345 void nsImageFrame::ElementStateChanged(ElementState aStates) {
   1346  if (!(aStates & ElementState::BROKEN)) {
   1347    return;
   1348  }
   1349  if (mKind != Kind::ImageLoadingContent) {
   1350    return;
   1351  }
   1352  if (!ImageOk(mContent->AsElement()->State())) {
   1353    UpdateImage(nullptr, nullptr);
   1354  }
   1355 }
   1356 
   1357 void nsImageFrame::ResponsiveContentDensityChanged() {
   1358  UpdateIntrinsicSizeAndRatio();
   1359 }
   1360 
   1361 void nsImageFrame::UpdateIntrinsicSizeAndRatio() {
   1362  bool intrinsicSizeOrRatioChanged = [&] {
   1363    // NOTE(emilio): We intentionally want to call both functions and avoid
   1364    // short-circuiting.
   1365    bool intrinsicSizeChanged = UpdateIntrinsicSize();
   1366    bool intrinsicRatioChanged = UpdateIntrinsicRatio();
   1367    return intrinsicSizeChanged || intrinsicRatioChanged;
   1368  }();
   1369 
   1370  if (!intrinsicSizeOrRatioChanged) {
   1371    return;
   1372  }
   1373 
   1374  // Our aspect-ratio property value changed, and an embedding <object> or
   1375  // <embed> might care about that.
   1376  MaybeSendIntrinsicSizeAndRatioToEmbedder();
   1377 
   1378  if (!GotInitialReflow()) {
   1379    return;
   1380  }
   1381 
   1382  // Now we need to reflow if we have an unconstrained size and have
   1383  // already gotten the initial reflow.
   1384  if (!HasAnyStateBits(IMAGE_SIZECONSTRAINED)) {
   1385    PresShell()->FrameNeedsReflow(
   1386        this, IntrinsicDirty::FrameAncestorsAndDescendants, NS_FRAME_IS_DIRTY);
   1387  } else if (PresShell()->IsActive()) {
   1388    // We've already gotten the initial reflow, and our size hasn't changed,
   1389    // so we're ready to request a decode.
   1390    MaybeDecodeForPredictedSize();
   1391  }
   1392 }
   1393 
   1394 void nsImageFrame::NotifyNewCurrentRequest(imgIRequest* aRequest) {
   1395  nsCOMPtr<imgIContainer> image;
   1396  aRequest->GetImage(getter_AddRefs(image));
   1397 #ifdef DEBUG
   1398  uint32_t imgStatus;
   1399  aRequest->GetImageStatus(&imgStatus);
   1400  NS_ASSERTION(image || (imgStatus & imgIRequest::STATUS_ERROR),
   1401               "Successful load with no container?");
   1402 #endif
   1403  UpdateImage(aRequest, image);
   1404 }
   1405 
   1406 void nsImageFrame::MaybeDecodeForPredictedSize() {
   1407  // Check that we're ready to decode.
   1408  if (!mImage) {
   1409    return;  // Nothing to do yet.
   1410  }
   1411 
   1412  if (mComputedSize.IsEmpty()) {
   1413    return;  // We won't draw anything, so no point in decoding.
   1414  }
   1415 
   1416  if (GetVisibility() != Visibility::ApproximatelyVisible) {
   1417    return;  // We're not visible, so don't decode.
   1418  }
   1419 
   1420  // OK, we're ready to decode. Compute the scale to the screen...
   1421  mozilla::PresShell* presShell = PresShell();
   1422  MatrixScales scale =
   1423      ScaleFactor<UnknownUnits, UnknownUnits>(
   1424          presShell->GetCumulativeResolution()) *
   1425      nsLayoutUtils::GetTransformToAncestorScaleExcludingAnimated(this);
   1426  auto resolutionToScreen = ViewAs<LayoutDeviceToScreenScale2D>(scale);
   1427 
   1428  // If we are in a remote browser, then apply scaling from ancestor browsers
   1429  if (BrowserChild* browserChild = BrowserChild::GetFrom(presShell)) {
   1430    resolutionToScreen =
   1431        resolutionToScreen * ViewAs<ScreenToScreenScale2D>(
   1432                                 browserChild->GetEffectsInfo().mRasterScale);
   1433  }
   1434 
   1435  // ...and this frame's content box...
   1436  const nsPoint offset =
   1437      GetOffsetToCrossDoc(nsLayoutUtils::GetReferenceFrame(this));
   1438  const nsRect frameContentBox = GetContentRectRelativeToSelf() + offset;
   1439 
   1440  // ...and our predicted dest rect...
   1441  const int32_t factor = PresContext()->AppUnitsPerDevPixel();
   1442  const LayoutDeviceRect destRect =
   1443      LayoutDeviceRect::FromAppUnits(GetDestRect(frameContentBox), factor);
   1444 
   1445  // ...and use them to compute our predicted size in screen pixels.
   1446  const ScreenSize predictedScreenSize = destRect.Size() * resolutionToScreen;
   1447  const ScreenIntSize predictedScreenIntSize =
   1448      RoundedToInt(predictedScreenSize);
   1449  if (predictedScreenIntSize.IsEmpty()) {
   1450    return;
   1451  }
   1452 
   1453  // Determine the optimal image size to use.
   1454  uint32_t flags = imgIContainer::FLAG_HIGH_QUALITY_SCALING |
   1455                   imgIContainer::FLAG_ASYNC_NOTIFY;
   1456  SamplingFilter samplingFilter =
   1457      nsLayoutUtils::GetSamplingFilterForFrame(this);
   1458  gfxSize gfxPredictedScreenSize =
   1459      gfxSize(predictedScreenIntSize.width, predictedScreenIntSize.height);
   1460  nsIntSize predictedImageSize = mImage->OptimalImageSizeForDest(
   1461      gfxPredictedScreenSize, imgIContainer::FRAME_CURRENT, samplingFilter,
   1462      flags);
   1463 
   1464  // Request a decode.
   1465  mImage->RequestDecodeForSize(predictedImageSize, flags);
   1466 }
   1467 
   1468 nsRect nsImageFrame::GetDestRect(const nsRect& aFrameContentBox,
   1469                                 nsPoint* aAnchorPoint) {
   1470  // Note: To get the "dest rect", we have to provide the "constraint rect"
   1471  // (which is the content-box, with the effects of fragmentation undone).
   1472  nsRect constraintRect(aFrameContentBox.TopLeft(), mComputedSize);
   1473  constraintRect.y -= GetContinuationOffset();
   1474 
   1475  auto intrinsicSize = mIntrinsicSize;
   1476  auto intrinsicRatio = mIntrinsicRatio;
   1477  if (GetContainSizeAxes().IsAny()) {
   1478    // Ignore containment for object-fit computations.
   1479    const bool ignoreContainment = true;
   1480    intrinsicSize = ComputeIntrinsicSize(ignoreContainment);
   1481    intrinsicRatio = ComputeIntrinsicRatioForImage(mImage, ignoreContainment);
   1482  }
   1483  return nsLayoutUtils::ComputeObjectDestRect(constraintRect, intrinsicSize,
   1484                                              intrinsicRatio, StylePosition(),
   1485                                              aAnchorPoint);
   1486 }
   1487 
   1488 void nsImageFrame::EnsureIntrinsicSizeAndRatio(bool aConsiderIntrinsicsDirty) {
   1489  const auto containAxes = GetContainSizeAxes();
   1490  if (containAxes.IsBoth()) {
   1491    // If we have 'contain:size', then we have no intrinsic aspect ratio,
   1492    // and the intrinsic size is determined by contain-intrinsic-size,
   1493    // regardless of what our underlying image may think.
   1494    mIntrinsicSize = FinishIntrinsicSize(containAxes, IntrinsicSize(0, 0));
   1495    mIntrinsicRatio = AspectRatio();
   1496    return;
   1497  }
   1498 
   1499  // If mIntrinsicSize is set (i.e. anything besides (0,0)), then we assume that
   1500  // our intrinsic size/ratio have been already computed and don't need
   1501  // recomputing, *unless* the aConsiderIntrinsicsDirty param is set to true
   1502  // (in which case our intrinsic size/ratio might be invalid).
   1503  //
   1504  // The fallback list-style-image marker size might have been set in
   1505  // DidSetComputedStyle, and it might have changed since then.
   1506  // TODO(emilio): We should remove that special case and add missing
   1507  // invalidation if/where needed.
   1508  if (!aConsiderIntrinsicsDirty && mIntrinsicSize != IntrinsicSize(0, 0) &&
   1509      mKind != Kind::ListStyleImage) {
   1510    return;
   1511  }
   1512 
   1513  bool intrinsicSizeOrRatioChanged = UpdateIntrinsicSize();
   1514  intrinsicSizeOrRatioChanged =
   1515      UpdateIntrinsicRatio() || intrinsicSizeOrRatioChanged;
   1516 
   1517  if (intrinsicSizeOrRatioChanged) {
   1518    // Our aspect-ratio property value changed, and an embedding <object> or
   1519    // <embed> might care about that.
   1520    MaybeSendIntrinsicSizeAndRatioToEmbedder();
   1521  }
   1522 }
   1523 
   1524 nsIFrame::SizeComputationResult nsImageFrame::ComputeSize(
   1525    const SizeComputationInput& aSizingInput, WritingMode aWM,
   1526    const LogicalSize& aCBSize, nscoord aAvailableISize,
   1527    const LogicalSize& aMargin, const LogicalSize& aBorderPadding,
   1528    const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) {
   1529  EnsureIntrinsicSizeAndRatio();
   1530  return {
   1531      ComputeSizeWithIntrinsicDimensions(
   1532          aSizingInput.mRenderingContext, aWM, mIntrinsicSize, GetAspectRatio(),
   1533          aCBSize, aMargin, aBorderPadding, aSizeOverrides, aFlags),
   1534      AspectRatioUsage::None};
   1535 }
   1536 
   1537 Element* nsImageFrame::GetMapElement() const {
   1538  return IsForImageLoadingContent()
   1539             ? nsImageLoadingContent::FindImageMap(mContent->AsElement())
   1540             : nullptr;
   1541 }
   1542 
   1543 // get the offset into the content area of the image where aImg starts if it is
   1544 // a continuation.
   1545 nscoord nsImageFrame::GetContinuationOffset() const {
   1546  nscoord offset = 0;
   1547  for (nsIFrame* f = GetPrevInFlow(); f; f = f->GetPrevInFlow()) {
   1548    offset += f->GetContentRect().height;
   1549  }
   1550  NS_ASSERTION(offset >= 0, "bogus GetContentRect");
   1551  return offset;
   1552 }
   1553 
   1554 nscoord nsImageFrame::IntrinsicISize(const IntrinsicSizeInput& aInput,
   1555                                     IntrinsicISizeType aType) {
   1556  EnsureIntrinsicSizeAndRatio();
   1557  return mIntrinsicSize.ISize(GetWritingMode()).valueOr(0);
   1558 }
   1559 
   1560 void nsImageFrame::ReflowChildren(nsPresContext* aPresContext,
   1561                                  const ReflowInput& aReflowInput,
   1562                                  const LogicalSize& aImageSize) {
   1563  for (nsIFrame* child : mFrames) {
   1564    ReflowOutput childDesiredSize(aReflowInput);
   1565    WritingMode wm = GetWritingMode();
   1566    // Shouldn't be hard to support if we want, but why bother.
   1567    MOZ_ASSERT(
   1568        wm == child->GetWritingMode(),
   1569        "We don't expect mismatched writing-modes in content we control");
   1570    nsReflowStatus childStatus;
   1571 
   1572    LogicalPoint childOffset(wm);
   1573    ReflowInput childReflowInput(aPresContext, aReflowInput, child, aImageSize);
   1574    const nsSize containerSize = aImageSize.GetPhysicalSize(wm);
   1575    ReflowChild(child, aPresContext, childDesiredSize, childReflowInput, wm,
   1576                childOffset, containerSize, ReflowChildFlags::Default,
   1577                childStatus);
   1578 
   1579    FinishReflowChild(child, aPresContext, childDesiredSize, &childReflowInput,
   1580                      wm, childOffset, containerSize,
   1581                      ReflowChildFlags::Default);
   1582  }
   1583 }
   1584 
   1585 void nsImageFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
   1586                          const ReflowInput& aReflowInput,
   1587                          nsReflowStatus& aStatus) {
   1588  MarkInReflow();
   1589  DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
   1590  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
   1591  NS_FRAME_TRACE(
   1592      NS_FRAME_TRACE_CALLS,
   1593      ("enter nsImageFrame::Reflow: availSize=%d,%d",
   1594       aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
   1595 
   1596  MOZ_ASSERT(HasAnyStateBits(NS_FRAME_IN_REFLOW), "frame is not in reflow");
   1597 
   1598  // see if we have a frozen size (i.e. a fixed width and height)
   1599  if (!SizeDependsOnIntrinsicSize(aReflowInput)) {
   1600    AddStateBits(IMAGE_SIZECONSTRAINED);
   1601  } else {
   1602    RemoveStateBits(IMAGE_SIZECONSTRAINED);
   1603  }
   1604 
   1605  mComputedSize = aReflowInput.ComputedPhysicalSize();
   1606 
   1607  const auto wm = GetWritingMode();
   1608  aMetrics.SetSize(wm, aReflowInput.ComputedSizeWithBorderPadding(wm));
   1609 
   1610  if (GetPrevInFlow()) {
   1611    aMetrics.Width() = GetPrevInFlow()->GetSize().width;
   1612    nscoord y = GetContinuationOffset();
   1613    aMetrics.Height() -= y + aReflowInput.ComputedPhysicalBorderPadding().top;
   1614    aMetrics.Height() = std::max(0, aMetrics.Height());
   1615  }
   1616 
   1617  // we have to split images if we are:
   1618  //  in Paginated mode, we need to have a constrained height, and have a height
   1619  //  larger than our available height
   1620  uint32_t loadStatus = imgIRequest::STATUS_NONE;
   1621  if (nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest()) {
   1622    currentRequest->GetImageStatus(&loadStatus);
   1623  }
   1624 
   1625  if (aPresContext->IsPaginated() &&
   1626      ((loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) ||
   1627       HasAnyStateBits(IMAGE_SIZECONSTRAINED)) &&
   1628      NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableHeight() &&
   1629      aMetrics.Height() > aReflowInput.AvailableHeight()) {
   1630    // our desired height was greater than 0, so to avoid infinite
   1631    // splitting, use 1 pixel as the min
   1632    aMetrics.Height() = std::max(nsPresContext::CSSPixelsToAppUnits(1),
   1633                                 aReflowInput.AvailableHeight());
   1634    aStatus.SetIncomplete();
   1635  }
   1636 
   1637  aMetrics.SetOverflowAreasToDesiredBounds();
   1638  const bool imageOK = mKind != Kind::ImageLoadingContent ||
   1639                       ImageOk(mContent->AsElement()->State());
   1640 
   1641  // Determine if the size is available
   1642  const bool haveSize = loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE;
   1643  if (!imageOK || !haveSize) {
   1644    nsRect altFeedbackSize(
   1645        0, 0,
   1646        nsPresContext::CSSPixelsToAppUnits(
   1647            ICON_SIZE + 2 * (ICON_PADDING + ALT_BORDER_WIDTH)),
   1648        nsPresContext::CSSPixelsToAppUnits(
   1649            ICON_SIZE + 2 * (ICON_PADDING + ALT_BORDER_WIDTH)));
   1650    // We include the altFeedbackSize in our ink overflow, but not in our
   1651    // scrollable overflow, since it doesn't really need to be scrolled to
   1652    // outside the image.
   1653    nsRect& inkOverflow = aMetrics.InkOverflow();
   1654    inkOverflow.UnionRect(inkOverflow, altFeedbackSize);
   1655  } else {
   1656    // Union with our dest rect (note that it will most likely get clipped in
   1657    // FinishAndStoreOverflow). Only do this if we're not fragmented, since in
   1658    // that case overflow goes into our continuation.
   1659    if (aStatus.IsComplete()) {
   1660      aMetrics.mOverflowAreas.UnionAllWith(
   1661          GetDestRect(aReflowInput.ComputedPhysicalContentBoxRelativeToSelf()));
   1662    }
   1663    if (PresShell()->IsActive()) {
   1664      // We've just reflowed and we should have an accurate size, so we're ready
   1665      // to request a decode.
   1666      MaybeDecodeForPredictedSize();
   1667    }
   1668  }
   1669  FinishAndStoreOverflow(&aMetrics, aReflowInput.mStyleDisplay);
   1670 
   1671  // Reflow the child frames. Our children can't affect our size in any way.
   1672  ReflowChildren(aPresContext, aReflowInput, aMetrics.Size(GetWritingMode()));
   1673 
   1674  if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW) && !mReflowCallbackPosted) {
   1675    mReflowCallbackPosted = true;
   1676    PresShell()->PostReflowCallback(this);
   1677  }
   1678 
   1679  NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsImageFrame::Reflow: size=%d,%d",
   1680                                        aMetrics.Width(), aMetrics.Height()));
   1681 }
   1682 
   1683 bool nsImageFrame::ReflowFinished() {
   1684  mReflowCallbackPosted = false;
   1685 
   1686  // XXX(seth): We don't need this. The purpose of updating visibility
   1687  // synchronously is to ensure that animated images start animating
   1688  // immediately. In the short term, however,
   1689  // nsImageLoadingContent::OnUnlockedDraw() is enough to ensure that
   1690  // animations start as soon as the image is painted for the first time, and in
   1691  // the long term we want to update visibility information from the display
   1692  // list whenever we paint, so we don't actually need to do this. However, to
   1693  // avoid behavior changes during the transition from the old image visibility
   1694  // code, we'll leave it in for now.
   1695  UpdateVisibilitySynchronously();
   1696 
   1697  return false;
   1698 }
   1699 
   1700 void nsImageFrame::ReflowCallbackCanceled() { mReflowCallbackPosted = false; }
   1701 
   1702 // Computes the width of the specified string. aMaxWidth specifies the maximum
   1703 // width available. Once this limit is reached no more characters are measured.
   1704 // The number of characters that fit within the maximum width are returned in
   1705 // aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected
   1706 // into the rendering context before this is called (for performance). MMP
   1707 nscoord nsImageFrame::MeasureString(const char16_t* aString, int32_t aLength,
   1708                                    nscoord aMaxWidth, uint32_t& aMaxFit,
   1709                                    gfxContext& aContext,
   1710                                    nsFontMetrics& aFontMetrics) {
   1711  nscoord totalWidth = 0;
   1712  aFontMetrics.SetTextRunRTL(false);
   1713  nscoord spaceWidth = aFontMetrics.SpaceWidth();
   1714 
   1715  aMaxFit = 0;
   1716  while (aLength > 0) {
   1717    // Find the next place we can line break
   1718    uint32_t len = aLength;
   1719    bool trailingSpace = false;
   1720    for (int32_t i = 0; i < aLength; i++) {
   1721      if (dom::IsSpaceCharacter(aString[i]) && (i > 0)) {
   1722        len = i;  // don't include the space when measuring
   1723        trailingSpace = true;
   1724        break;
   1725      }
   1726    }
   1727 
   1728    // Measure this chunk of text, and see if it fits
   1729    nscoord width = nsLayoutUtils::AppUnitWidthOfStringBidi(
   1730        aString, len, this, aFontMetrics, aContext);
   1731    bool fits = (totalWidth + width) <= aMaxWidth;
   1732 
   1733    // If it fits on the line, or it's the first word we've processed then
   1734    // include it
   1735    if (fits || (0 == totalWidth)) {
   1736      // New piece fits
   1737      totalWidth += width;
   1738 
   1739      // If there's a trailing space then see if it fits as well
   1740      if (trailingSpace) {
   1741        if ((totalWidth + spaceWidth) <= aMaxWidth) {
   1742          totalWidth += spaceWidth;
   1743        } else {
   1744          // Space won't fit. Leave it at the end but don't include it in
   1745          // the width
   1746          fits = false;
   1747        }
   1748 
   1749        len++;
   1750      }
   1751 
   1752      aMaxFit += len;
   1753      aString += len;
   1754      aLength -= len;
   1755    }
   1756 
   1757    if (!fits) {
   1758      break;
   1759    }
   1760  }
   1761  return totalWidth;
   1762 }
   1763 
   1764 // Formats the alt-text to fit within the specified rectangle. Breaks lines
   1765 // between words if a word would extend past the edge of the rectangle
   1766 void nsImageFrame::DisplayAltText(nsPresContext* aPresContext,
   1767                                  gfxContext& aRenderingContext,
   1768                                  const nsString& aAltText,
   1769                                  const nsRect& aRect) {
   1770  // Set font and color
   1771  aRenderingContext.SetColor(
   1772      sRGBColor::FromABGR(StyleText()->mColor.ToColor()));
   1773  RefPtr<nsFontMetrics> fm =
   1774      nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
   1775 
   1776  // Format the text to display within the formatting rect
   1777 
   1778  nscoord maxAscent = fm->MaxAscent();
   1779  nscoord maxDescent = fm->MaxDescent();
   1780  nscoord lineHeight = fm->MaxHeight();  // line-relative, so an x-coordinate
   1781                                         // length if writing mode is vertical
   1782 
   1783  WritingMode wm = GetWritingMode();
   1784  bool isVertical = wm.IsVertical();
   1785 
   1786  fm->SetVertical(isVertical);
   1787  fm->SetTextOrientation(StyleVisibility()->mTextOrientation);
   1788 
   1789  // XXX It would be nice if there was a way to have the font metrics tell
   1790  // use where to break the text given a maximum width. At a minimum we need
   1791  // to be able to get the break character...
   1792  const char16_t* str = aAltText.get();
   1793  int32_t strLen = aAltText.Length();
   1794  nsPoint pt = wm.IsVerticalRL() ? aRect.TopRight() - nsPoint(lineHeight, 0)
   1795                                 : aRect.TopLeft();
   1796  nscoord iSize = isVertical ? aRect.height : aRect.width;
   1797 
   1798  if (!aPresContext->BidiEnabled() && HasRTLChars(aAltText)) {
   1799    aPresContext->SetBidiEnabled();
   1800  }
   1801 
   1802  // Always show the first line, even if we have to clip it below
   1803  bool firstLine = true;
   1804  while (strLen > 0) {
   1805    if (!firstLine) {
   1806      // If we've run out of space, break out of the loop
   1807      if ((!isVertical && (pt.y + maxDescent) >= aRect.YMost()) ||
   1808          (wm.IsVerticalRL() && (pt.x + maxDescent < aRect.x)) ||
   1809          (wm.IsVerticalLR() && (pt.x + maxDescent >= aRect.XMost()))) {
   1810        break;
   1811      }
   1812    }
   1813 
   1814    // Determine how much of the text to display on this line
   1815    uint32_t maxFit;  // number of characters that fit
   1816    nscoord strWidth =
   1817        MeasureString(str, strLen, iSize, maxFit, aRenderingContext, *fm);
   1818 
   1819    // Display the text
   1820    nsresult rv = NS_ERROR_FAILURE;
   1821 
   1822    if (aPresContext->BidiEnabled()) {
   1823      mozilla::intl::BidiEmbeddingLevel level;
   1824      nscoord x, y;
   1825 
   1826      if (isVertical) {
   1827        x = pt.x + maxDescent;
   1828        if (wm.IsBidiLTR()) {
   1829          y = aRect.y;
   1830          level = mozilla::intl::BidiEmbeddingLevel::LTR();
   1831        } else {
   1832          y = aRect.YMost() - strWidth;
   1833          level = mozilla::intl::BidiEmbeddingLevel::RTL();
   1834        }
   1835      } else {
   1836        y = pt.y + maxAscent;
   1837        if (wm.IsBidiLTR()) {
   1838          x = aRect.x;
   1839          level = mozilla::intl::BidiEmbeddingLevel::LTR();
   1840        } else {
   1841          x = aRect.XMost() - strWidth;
   1842          level = mozilla::intl::BidiEmbeddingLevel::RTL();
   1843        }
   1844      }
   1845 
   1846      rv = nsBidiPresUtils::RenderText(
   1847          str, maxFit, level, aPresContext, aRenderingContext,
   1848          aRenderingContext.GetDrawTarget(), *fm, x, y);
   1849    }
   1850    if (NS_FAILED(rv)) {
   1851      nsLayoutUtils::DrawUniDirString(str, maxFit,
   1852                                      isVertical
   1853                                          ? nsPoint(pt.x + maxDescent, pt.y)
   1854                                          : nsPoint(pt.x, pt.y + maxAscent),
   1855                                      *fm, aRenderingContext);
   1856    }
   1857 
   1858    // Move to the next line
   1859    str += maxFit;
   1860    strLen -= maxFit;
   1861    if (wm.IsVerticalRL()) {
   1862      pt.x -= lineHeight;
   1863    } else if (wm.IsVerticalLR()) {
   1864      pt.x += lineHeight;
   1865    } else {
   1866      pt.y += lineHeight;
   1867    }
   1868 
   1869    firstLine = false;
   1870  }
   1871 }
   1872 
   1873 struct nsRecessedBorder : public nsStyleBorder {
   1874  explicit nsRecessedBorder(nscoord aBorderWidth) {
   1875    for (const auto side : AllPhysicalSides()) {
   1876      BorderColorFor(side) = StyleColor::Black();
   1877      mBorder.Get(side) = aBorderWidth;
   1878      mBorderStyle.Get(side) = StyleBorderStyle::Inset;
   1879    }
   1880  }
   1881 };
   1882 
   1883 class nsDisplayAltFeedback final : public nsPaintedDisplayItem {
   1884 public:
   1885  nsDisplayAltFeedback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
   1886      : nsPaintedDisplayItem(aBuilder, aFrame) {}
   1887 
   1888  nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) const final {
   1889    *aSnap = false;
   1890    return mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
   1891  }
   1892 
   1893  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) final {
   1894    // Always sync decode, because these icons are UI, and since they're not
   1895    // discardable we'll pay the price of sync decoding at most once.
   1896    uint32_t flags = imgIContainer::FLAG_SYNC_DECODE;
   1897 
   1898    nsImageFrame* f = static_cast<nsImageFrame*>(mFrame);
   1899    (void)f->DisplayAltFeedback(*aCtx, GetPaintRect(aBuilder, aCtx),
   1900                                ToReferenceFrame(), flags);
   1901  }
   1902 
   1903  bool CreateWebRenderCommands(
   1904      wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
   1905      const StackingContextHelper& aSc,
   1906      layers::RenderRootStateManager* aManager,
   1907      nsDisplayListBuilder* aDisplayListBuilder) final {
   1908    // Always sync decode, because these icons are UI, and since they're not
   1909    // discardable we'll pay the price of sync decoding at most once.
   1910    uint32_t flags =
   1911        imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY;
   1912    nsImageFrame* f = static_cast<nsImageFrame*>(mFrame);
   1913    ImgDrawResult result = f->DisplayAltFeedbackWithoutLayer(
   1914        this, aBuilder, aResources, aSc, aManager, aDisplayListBuilder,
   1915        ToReferenceFrame(), flags);
   1916 
   1917    return result == ImgDrawResult::SUCCESS;
   1918  }
   1919 
   1920  NS_DISPLAY_DECL_NAME("AltFeedback", TYPE_ALT_FEEDBACK)
   1921 };
   1922 
   1923 ImgDrawResult nsImageFrame::DisplayAltFeedback(gfxContext& aRenderingContext,
   1924                                               const nsRect& aDirtyRect,
   1925                                               nsPoint aPt, uint32_t aFlags) {
   1926  // Whether we draw the broken or loading icon.
   1927  bool isLoading = mKind != Kind::ImageLoadingContent ||
   1928                   ImageOk(mContent->AsElement()->State());
   1929 
   1930  // Calculate the content area.
   1931  nsRect inner = GetContentRectRelativeToSelf() + aPt;
   1932 
   1933  // Display a recessed one pixel border
   1934  nscoord borderEdgeWidth =
   1935      nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH);
   1936 
   1937  // if inner area is empty, then make it big enough for at least the icon
   1938  if (inner.IsEmpty()) {
   1939    inner.SizeTo(2 * (nsPresContext::CSSPixelsToAppUnits(
   1940                         ICON_SIZE + ICON_PADDING + ALT_BORDER_WIDTH)),
   1941                 2 * (nsPresContext::CSSPixelsToAppUnits(
   1942                         ICON_SIZE + ICON_PADDING + ALT_BORDER_WIDTH)));
   1943  }
   1944 
   1945  // Make sure we have enough room to actually render the border within
   1946  // our frame bounds
   1947  if ((inner.width < 2 * borderEdgeWidth) ||
   1948      (inner.height < 2 * borderEdgeWidth)) {
   1949    return ImgDrawResult::SUCCESS;
   1950  }
   1951 
   1952  // Paint the border
   1953  if (!isLoading) {
   1954    nsRecessedBorder recessedBorder(borderEdgeWidth);
   1955 
   1956    // Assert that we're not drawing a border-image here; if we were, we
   1957    // couldn't ignore the ImgDrawResult that PaintBorderWithStyleBorder
   1958    // returns.
   1959    MOZ_ASSERT(recessedBorder.mBorderImageSource.IsNone());
   1960 
   1961    (void)nsCSSRendering::PaintBorderWithStyleBorder(
   1962        PresContext(), aRenderingContext, this, inner, inner, recessedBorder,
   1963        mComputedStyle, PaintBorderFlags::SyncDecodeImages);
   1964  }
   1965 
   1966  // Adjust the inner rect to account for the one pixel recessed border,
   1967  // and a six pixel padding on each edge
   1968  inner.Deflate(
   1969      nsPresContext::CSSPixelsToAppUnits(ICON_PADDING + ALT_BORDER_WIDTH),
   1970      nsPresContext::CSSPixelsToAppUnits(ICON_PADDING + ALT_BORDER_WIDTH));
   1971  if (inner.IsEmpty()) {
   1972    return ImgDrawResult::SUCCESS;
   1973  }
   1974 
   1975  DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
   1976 
   1977  // Clip so we don't render outside the inner rect
   1978  aRenderingContext.Save();
   1979  aRenderingContext.Clip(NSRectToSnappedRect(
   1980      inner, PresContext()->AppUnitsPerDevPixel(), *drawTarget));
   1981 
   1982  ImgDrawResult result = ImgDrawResult::SUCCESS;
   1983 
   1984  // Check if we should display image placeholders
   1985  if (ShouldShowBrokenImageIcon()) {
   1986    result = ImgDrawResult::NOT_READY;
   1987    nscoord size = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE);
   1988    imgIRequest* request = BrokenImageIcon::GetImage(this);
   1989 
   1990    // If we weren't previously displaying an icon, register ourselves
   1991    // as an observer for load and animation updates and flag that we're
   1992    // doing so now.
   1993    if (request && !mDisplayingIcon) {
   1994      BrokenImageIcon::AddObserver(this);
   1995      mDisplayingIcon = true;
   1996    }
   1997 
   1998    WritingMode wm = GetWritingMode();
   1999    bool flushRight = wm.IsPhysicalRTL();
   2000 
   2001    // If the icon in question is loaded, draw it.
   2002    uint32_t imageStatus = 0;
   2003    if (request) {
   2004      request->GetImageStatus(&imageStatus);
   2005    }
   2006    if (imageStatus & imgIRequest::STATUS_LOAD_COMPLETE &&
   2007        !(imageStatus & imgIRequest::STATUS_ERROR)) {
   2008      nsCOMPtr<imgIContainer> imgCon;
   2009      request->GetImage(getter_AddRefs(imgCon));
   2010      MOZ_ASSERT(imgCon, "Load complete, but no image container?");
   2011      nsRect dest(flushRight ? inner.XMost() - size : inner.x, inner.y, size,
   2012                  size);
   2013      result = nsLayoutUtils::DrawSingleImage(
   2014          aRenderingContext, PresContext(), imgCon,
   2015          nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
   2016          SVGImageContext(), aFlags);
   2017    }
   2018 
   2019    // If we could not draw the icon, just draw some graffiti in the mean time.
   2020    if (result == ImgDrawResult::NOT_READY) {
   2021      ColorPattern color(ToDeviceColor(sRGBColor(1.f, 0.f, 0.f, 1.f)));
   2022 
   2023      nscoord iconXPos = flushRight ? inner.XMost() - size : inner.x;
   2024 
   2025      // stroked rect:
   2026      nsRect rect(iconXPos, inner.y, size, size);
   2027      Rect devPxRect = ToRect(nsLayoutUtils::RectToGfxRect(
   2028          rect, PresContext()->AppUnitsPerDevPixel()));
   2029      drawTarget->StrokeRect(devPxRect, color);
   2030 
   2031      // filled circle in bottom right quadrant of stroked rect:
   2032      nscoord twoPX = nsPresContext::CSSPixelsToAppUnits(2);
   2033      rect = nsRect(iconXPos + size / 2, inner.y + size / 2, size / 2 - twoPX,
   2034                    size / 2 - twoPX);
   2035      devPxRect = ToRect(nsLayoutUtils::RectToGfxRect(
   2036          rect, PresContext()->AppUnitsPerDevPixel()));
   2037      RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder();
   2038      AppendEllipseToPath(builder, devPxRect.Center(), devPxRect.Size());
   2039      RefPtr<Path> ellipse = builder->Finish();
   2040      drawTarget->Fill(ellipse, color);
   2041    }
   2042 
   2043    // Reduce the inner rect by the width of the icon, and leave an
   2044    // additional ICON_PADDING pixels for padding
   2045    int32_t paddedIconSize =
   2046        nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING);
   2047    if (wm.IsVertical()) {
   2048      inner.y += paddedIconSize;
   2049      inner.height -= paddedIconSize;
   2050    } else {
   2051      if (!flushRight) {
   2052        inner.x += paddedIconSize;
   2053      }
   2054      inner.width -= paddedIconSize;
   2055    }
   2056  }
   2057 
   2058  // If there's still room, display the alt-text
   2059  if (!inner.IsEmpty()) {
   2060    nsAutoString altText;
   2061    nsCSSFrameConstructor::GetAlternateTextFor(*mContent->AsElement(), altText);
   2062    DisplayAltText(PresContext(), aRenderingContext, altText, inner);
   2063  }
   2064 
   2065  aRenderingContext.Restore();
   2066 
   2067  return result;
   2068 }
   2069 
   2070 ImgDrawResult nsImageFrame::DisplayAltFeedbackWithoutLayer(
   2071    nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
   2072    wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc,
   2073    layers::RenderRootStateManager* aManager,
   2074    nsDisplayListBuilder* aDisplayListBuilder, nsPoint aPt, uint32_t aFlags) {
   2075  // Whether we draw the broken or loading icon.
   2076  bool isLoading = mKind != Kind::ImageLoadingContent ||
   2077                   ImageOk(mContent->AsElement()->State());
   2078 
   2079  // Calculate the content area.
   2080  nsRect inner = GetContentRectRelativeToSelf() + aPt;
   2081 
   2082  // Display a recessed one pixel border
   2083  nscoord borderEdgeWidth =
   2084      nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH);
   2085 
   2086  // if inner area is empty, then make it big enough for at least the icon
   2087  if (inner.IsEmpty()) {
   2088    inner.SizeTo(2 * (nsPresContext::CSSPixelsToAppUnits(
   2089                         ICON_SIZE + ICON_PADDING + ALT_BORDER_WIDTH)),
   2090                 2 * (nsPresContext::CSSPixelsToAppUnits(
   2091                         ICON_SIZE + ICON_PADDING + ALT_BORDER_WIDTH)));
   2092  }
   2093 
   2094  // Make sure we have enough room to actually render the border within
   2095  // our frame bounds
   2096  if ((inner.width < 2 * borderEdgeWidth) ||
   2097      (inner.height < 2 * borderEdgeWidth)) {
   2098    return ImgDrawResult::SUCCESS;
   2099  }
   2100 
   2101  // If the TextDrawTarget requires fallback we need to rollback everything we
   2102  // may have added to the display list, but we don't find that out until the
   2103  // end.
   2104  bool textDrawResult = true;
   2105  class AutoSaveRestore {
   2106   public:
   2107    explicit AutoSaveRestore(wr::DisplayListBuilder& aBuilder,
   2108                             bool& aTextDrawResult)
   2109        : mBuilder(aBuilder), mTextDrawResult(aTextDrawResult) {
   2110      mBuilder.Save();
   2111    }
   2112    ~AutoSaveRestore() {
   2113      // If we have to use fallback for the text restore the builder and remove
   2114      // anything else we added to the display list, we need to use fallback.
   2115      if (mTextDrawResult) {
   2116        mBuilder.ClearSave();
   2117      } else {
   2118        mBuilder.Restore();
   2119      }
   2120    }
   2121 
   2122   private:
   2123    wr::DisplayListBuilder& mBuilder;
   2124    bool& mTextDrawResult;
   2125  };
   2126 
   2127  AutoSaveRestore autoSaveRestore(aBuilder, textDrawResult);
   2128 
   2129  // Paint the border
   2130  if (!isLoading) {
   2131    nsRecessedBorder recessedBorder(borderEdgeWidth);
   2132    // Assert that we're not drawing a border-image here; if we were, we
   2133    // couldn't ignore the ImgDrawResult that PaintBorderWithStyleBorder
   2134    // returns.
   2135    MOZ_ASSERT(recessedBorder.mBorderImageSource.IsNone());
   2136 
   2137    nsRect rect = nsRect(aPt, GetSize());
   2138    (void)nsCSSRendering::CreateWebRenderCommandsForBorderWithStyleBorder(
   2139        aItem, this, rect, aBuilder, aResources, aSc, aManager,
   2140        aDisplayListBuilder, recessedBorder);
   2141  }
   2142 
   2143  // Adjust the inner rect to account for the one pixel recessed border,
   2144  // and a six pixel padding on each edge
   2145  inner.Deflate(
   2146      nsPresContext::CSSPixelsToAppUnits(ICON_PADDING + ALT_BORDER_WIDTH),
   2147      nsPresContext::CSSPixelsToAppUnits(ICON_PADDING + ALT_BORDER_WIDTH));
   2148  if (inner.IsEmpty()) {
   2149    return ImgDrawResult::SUCCESS;
   2150  }
   2151 
   2152  // Clip to this rect so we don't render outside the inner rect
   2153  const auto bounds = LayoutDeviceRect::FromAppUnits(
   2154      inner, PresContext()->AppUnitsPerDevPixel());
   2155  auto wrBounds = wr::ToLayoutRect(bounds);
   2156 
   2157  // Check if we should display image placeholders
   2158  if (ShouldShowBrokenImageIcon()) {
   2159    ImgDrawResult result = ImgDrawResult::NOT_READY;
   2160    nscoord size = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE);
   2161    imgIRequest* request = BrokenImageIcon::GetImage(this);
   2162 
   2163    // If we weren't previously displaying an icon, register ourselves
   2164    // as an observer for load and animation updates and flag that we're
   2165    // doing so now.
   2166    if (request && !mDisplayingIcon) {
   2167      BrokenImageIcon::AddObserver(this);
   2168      mDisplayingIcon = true;
   2169    }
   2170 
   2171    WritingMode wm = GetWritingMode();
   2172    const bool flushRight = wm.IsPhysicalRTL();
   2173 
   2174    // If the icon in question is loaded, draw it.
   2175    uint32_t imageStatus = 0;
   2176    if (request) {
   2177      request->GetImageStatus(&imageStatus);
   2178    }
   2179    if (imageStatus & imgIRequest::STATUS_LOAD_COMPLETE &&
   2180        !(imageStatus & imgIRequest::STATUS_ERROR)) {
   2181      nsCOMPtr<imgIContainer> imgCon;
   2182      request->GetImage(getter_AddRefs(imgCon));
   2183      MOZ_ASSERT(imgCon, "Load complete, but no image container?");
   2184 
   2185      nsRect dest(flushRight ? inner.XMost() - size : inner.x, inner.y, size,
   2186                  size);
   2187 
   2188      const int32_t factor = PresContext()->AppUnitsPerDevPixel();
   2189      const auto destRect = LayoutDeviceRect::FromAppUnits(dest, factor);
   2190 
   2191      SVGImageContext svgContext;
   2192      Maybe<ImageIntRegion> region;
   2193      IntSize decodeSize =
   2194          nsLayoutUtils::ComputeImageContainerDrawingParameters(
   2195              imgCon, this, destRect, destRect, aSc, aFlags, svgContext,
   2196              region);
   2197      RefPtr<image::WebRenderImageProvider> provider;
   2198      result = imgCon->GetImageProvider(aManager->LayerManager(), decodeSize,
   2199                                        svgContext, region, aFlags,
   2200                                        getter_AddRefs(provider));
   2201      if (provider) {
   2202        bool wrResult = aManager->CommandBuilder().PushImageProvider(
   2203            aItem, provider, result, aBuilder, aResources, destRect, bounds);
   2204        result &= wrResult ? ImgDrawResult::SUCCESS : ImgDrawResult::NOT_READY;
   2205      } else {
   2206        // We don't use &= here because we want the result to be NOT_READY so
   2207        // the next block executes.
   2208        result = ImgDrawResult::NOT_READY;
   2209      }
   2210    }
   2211 
   2212    // If we could not draw the icon, just draw some graffiti in the mean time.
   2213    if (result == ImgDrawResult::NOT_READY) {
   2214      auto color = wr::ColorF{1.0f, 0.0f, 0.0f, 1.0f};
   2215      bool isBackfaceVisible = !aItem->BackfaceIsHidden();
   2216 
   2217      nscoord iconXPos = flushRight ? inner.XMost() - size : inner.x;
   2218 
   2219      // stroked rect:
   2220      nsRect rect(iconXPos, inner.y, size, size);
   2221      auto devPxRect = LayoutDeviceRect::FromAppUnits(
   2222          rect, PresContext()->AppUnitsPerDevPixel());
   2223      auto dest = wr::ToLayoutRect(devPxRect);
   2224 
   2225      auto borderWidths = wr::ToBorderWidths(1.0, 1.0, 1.0, 1.0);
   2226      wr::BorderSide side = {color, wr::BorderStyle::Solid};
   2227      wr::BorderSide sides[4] = {side, side, side, side};
   2228      Range<const wr::BorderSide> sidesRange(sides, 4);
   2229      aBuilder.PushBorder(dest, wrBounds, isBackfaceVisible, borderWidths,
   2230                          sidesRange, wr::EmptyBorderRadius());
   2231 
   2232      // filled circle in bottom right quadrant of stroked rect:
   2233      nscoord twoPX = nsPresContext::CSSPixelsToAppUnits(2);
   2234      rect = nsRect(iconXPos + size / 2, inner.y + size / 2, size / 2 - twoPX,
   2235                    size / 2 - twoPX);
   2236      devPxRect = LayoutDeviceRect::FromAppUnits(
   2237          rect, PresContext()->AppUnitsPerDevPixel());
   2238      dest = wr::ToLayoutRect(devPxRect);
   2239 
   2240      aBuilder.PushRoundedRect(dest, wrBounds, isBackfaceVisible, color);
   2241    }
   2242 
   2243    // Reduce the inner rect by the width of the icon, and leave an
   2244    // additional ICON_PADDING pixels for padding
   2245    int32_t paddedIconSize =
   2246        nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING);
   2247    if (wm.IsVertical()) {
   2248      inner.y += paddedIconSize;
   2249      inner.height -= paddedIconSize;
   2250    } else {
   2251      if (!flushRight) {
   2252        inner.x += paddedIconSize;
   2253      }
   2254      inner.width -= paddedIconSize;
   2255    }
   2256  }
   2257 
   2258  // Draw text
   2259  if (!inner.IsEmpty()) {
   2260    RefPtr<TextDrawTarget> textDrawer =
   2261        new TextDrawTarget(aBuilder, aResources, aSc, aManager, aItem, inner,
   2262                           /* aCallerDoesSaveRestore = */ true);
   2263    MOZ_ASSERT(textDrawer->IsValid());
   2264    if (textDrawer->IsValid()) {
   2265      gfxContext captureCtx(textDrawer);
   2266 
   2267      nsAutoString altText;
   2268      nsCSSFrameConstructor::GetAlternateTextFor(*mContent->AsElement(),
   2269                                                 altText);
   2270      DisplayAltText(PresContext(), captureCtx, altText, inner);
   2271 
   2272      textDrawer->TerminateShadows();
   2273      textDrawResult = !textDrawer->CheckHasUnsupportedFeatures();
   2274    }
   2275  }
   2276 
   2277  // Purposely ignore local DrawResult because we handled it not being success
   2278  // already.
   2279  return textDrawResult ? ImgDrawResult::SUCCESS : ImgDrawResult::NOT_READY;
   2280 }
   2281 
   2282 // We want to sync-decode in this case, as otherwise we either need to flash
   2283 // white while waiting to decode the new image, or paint the old image with a
   2284 // different aspect-ratio, which would be bad as it'd be stretched.
   2285 //
   2286 // See bug 1589955.
   2287 static bool OldImageHasDifferentRatio(const nsImageFrame& aFrame,
   2288                                      imgIContainer& aImage,
   2289                                      imgIContainer* aPrevImage) {
   2290  if (!aPrevImage || aPrevImage == &aImage) {
   2291    return false;
   2292  }
   2293 
   2294  // If we don't depend on our intrinsic image size / ratio, we're good.
   2295  //
   2296  // FIXME(emilio): There's the case of the old image being painted
   2297  // intrinsically, and src and styles changing at the same time... Maybe we
   2298  // should keep track of the old GetPaintRect()'s ratio and the image's ratio,
   2299  // instead of checking this bit?
   2300  if (aFrame.HasAnyStateBits(IMAGE_SIZECONSTRAINED)) {
   2301    return false;
   2302  }
   2303 
   2304  auto currentRatio = aFrame.GetIntrinsicRatio();
   2305  auto oldRatio = aFrame.ComputeIntrinsicRatioForImage(aPrevImage);
   2306  return oldRatio != currentRatio;
   2307 }
   2308 
   2309 #ifdef DEBUG
   2310 void nsImageFrame::AssertSyncDecodingHintIsInSync() const {
   2311  if (!IsForImageLoadingContent()) {
   2312    return;
   2313  }
   2314  nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
   2315  MOZ_ASSERT(imageLoader);
   2316 
   2317  // The opposite is not true, we might have some other heuristics which force
   2318  // sync-decoding of images.
   2319  MOZ_ASSERT_IF(imageLoader->GetSyncDecodingHint(), mForceSyncDecoding);
   2320 }
   2321 #endif
   2322 
   2323 void nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) {
   2324  auto* frame = Frame();
   2325  frame->AssertSyncDecodingHintIsInSync();
   2326 
   2327  auto* image = frame->mImage.get();
   2328  auto* prevImage = frame->mPrevImage.get();
   2329  if (!image) {
   2330    return;
   2331  }
   2332 
   2333  const bool oldImageIsDifferent =
   2334      OldImageHasDifferentRatio(*frame, *image, prevImage);
   2335 
   2336  uint32_t flags = aBuilder->GetImageDecodeFlags();
   2337  if (aBuilder->ShouldSyncDecodeImages() || oldImageIsDifferent ||
   2338      frame->mForceSyncDecoding) {
   2339    flags |= imgIContainer::FLAG_SYNC_DECODE;
   2340  }
   2341 
   2342  ImgDrawResult result = frame->PaintImage(
   2343      *aCtx, ToReferenceFrame(), GetPaintRect(aBuilder, aCtx), image, flags);
   2344 
   2345  if (result == ImgDrawResult::NOT_READY ||
   2346      result == ImgDrawResult::INCOMPLETE ||
   2347      result == ImgDrawResult::TEMPORARY_ERROR) {
   2348    // If the current image failed to paint because it's still loading or
   2349    // decoding, try painting the previous image.
   2350    if (prevImage) {
   2351      result =
   2352          frame->PaintImage(*aCtx, ToReferenceFrame(),
   2353                            GetPaintRect(aBuilder, aCtx), prevImage, flags);
   2354    }
   2355  }
   2356 }
   2357 
   2358 nsRect nsDisplayImage::GetDestRect() const {
   2359  auto* f = static_cast<nsImageFrame*>(mFrame);
   2360  return f->GetDestRect(f->GetContentRectRelativeToSelf() + ToReferenceFrame());
   2361 }
   2362 
   2363 nsRect nsDisplayImage::GetDestRectViewTransition() const {
   2364  nsRect destRect = GetDestRect();
   2365  auto* image = static_cast<nsImageFrame*>(mFrame);
   2366 
   2367  auto* name = image->GetViewTransitionName();
   2368  auto* vt = image->PresContext()->Document()->GetActiveViewTransition();
   2369 
   2370  if (!name || !vt) {
   2371    return destRect;
   2372  }
   2373 
   2374  // In view transitions, the snapshot images natural dimension is the captured
   2375  // elements principal border box. In order to render the captured overflow to
   2376  // its appropiate position and scale, we must internally map and scale the
   2377  // destRect with respect to the captured element's inkOverflowRect.
   2378  nsRect inkOverflowRect;
   2379  nsSize borderBoxSize;
   2380  Maybe<nsRect> activeRect;
   2381 
   2382  if (image->Style()->GetPseudoType() == PseudoStyleType::viewTransitionOld) {
   2383    inkOverflowRect = vt->GetOldInkOverflowRect(name).value();
   2384    borderBoxSize = vt->GetOldBorderBoxSize(name).value();
   2385    activeRect = vt->GetOldActiveRect(name);
   2386  } else {
   2387    inkOverflowRect = vt->GetNewInkOverflowRect(name).value();
   2388    borderBoxSize = vt->GetNewBorderBoxSize(name).value();
   2389    activeRect = vt->GetNewActiveRect(name);
   2390  }
   2391 
   2392  if (borderBoxSize.IsEmpty()) {
   2393    return destRect;
   2394  }
   2395 
   2396  // Scale the ink overflow offset to maintain its position relative to
   2397  // the destination border box, as if the offset scaled with the element.
   2398  auto xRatio = static_cast<float>(inkOverflowRect.X()) / borderBoxSize.Width();
   2399  auto yRatio =
   2400      static_cast<float>(inkOverflowRect.Y()) / borderBoxSize.Height();
   2401  auto scaledX = std::round(xRatio * destRect.Width());
   2402  auto scaledY = std::round(yRatio * destRect.Height());
   2403 
   2404  const nsPoint scaledInkOverflowOffset(scaledX, scaledY);
   2405 
   2406  // Scale destRect’s size to match the captured element’s relative ink overflow
   2407  // size.
   2408  auto widthRatio =
   2409      static_cast<float>(inkOverflowRect.Width()) / borderBoxSize.Width();
   2410  auto heightRatio =
   2411      static_cast<float>(inkOverflowRect.Height()) / borderBoxSize.Height();
   2412  const nsSize scaledInkOverflowSize(
   2413      std::round(widthRatio * destRect.Width()),
   2414      std::round(heightRatio * destRect.Height()));
   2415 
   2416  destRect = nsRect(destRect.TopLeft() + scaledInkOverflowOffset,
   2417                    scaledInkOverflowSize);
   2418 
   2419  if (activeRect) {
   2420    destRect = destRect.Intersect(activeRect.value());
   2421  }
   2422 
   2423  return destRect;
   2424 }
   2425 
   2426 nsRegion nsDisplayImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
   2427                                         bool* aSnap) const {
   2428  *aSnap = false;
   2429  auto* image = Frame()->mImage.get();
   2430  if (image && image->WillDrawOpaqueNow()) {
   2431    const nsRect frameContentBox = GetBounds(aSnap);
   2432    return GetDestRect().Intersect(frameContentBox);
   2433  }
   2434  return nsRegion();
   2435 }
   2436 
   2437 void nsDisplayImage::MaybeCreateWebRenderCommandsForViewTransition(
   2438    wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
   2439    const StackingContextHelper& aSc, RenderRootStateManager* aManager,
   2440    nsDisplayListBuilder* aDisplayListBuilder) {
   2441  auto* frame = Frame();
   2442  MOZ_ASSERT(!frame->mImage);
   2443  auto key = frame->GetViewTransitionImageKey(aManager, aResources);
   2444  if (NS_WARN_IF(key == kNoKey)) {
   2445    return;
   2446  }
   2447  VT_LOG_DEBUG("GetViewTransitionImageKey(%s) = %s", frame->ListTag().get(),
   2448               ToString(key).c_str());
   2449  nsRect destAppUnits = GetDestRectViewTransition();
   2450  const int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
   2451  const auto destRect =
   2452      wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(destAppUnits, factor));
   2453  auto rendering = wr::ToImageRendering(frame->UsedImageRendering());
   2454  aBuilder.PushDebug(1);
   2455  aBuilder.PushImage(destRect, destRect, !BackfaceIsHidden(),
   2456                     /* aForceAntiAliasing = */ false, rendering, key);
   2457 }
   2458 
   2459 bool nsDisplayImage::CreateWebRenderCommands(
   2460    wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
   2461    const StackingContextHelper& aSc, RenderRootStateManager* aManager,
   2462    nsDisplayListBuilder* aDisplayListBuilder) {
   2463  auto* frame = Frame();
   2464  auto* image = frame->mImage.get();
   2465  if (!image) {
   2466    MaybeCreateWebRenderCommandsForViewTransition(
   2467        aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
   2468    return true;
   2469  }
   2470 
   2471  if (nsImageMap* map = frame->GetImageMap(); map && map->HasFocus()) {
   2472    // We can't draw some of the focus areas (in particular, PolyArea would be
   2473    // somewhat hard to do).
   2474    return false;
   2475  }
   2476 
   2477  auto* prevImage = frame->mPrevImage.get();
   2478 
   2479  frame->AssertSyncDecodingHintIsInSync();
   2480  const bool oldImageIsDifferent =
   2481      OldImageHasDifferentRatio(*frame, *image, prevImage);
   2482 
   2483  uint32_t flags = aDisplayListBuilder->GetImageDecodeFlags();
   2484  if (aDisplayListBuilder->ShouldSyncDecodeImages() || oldImageIsDifferent ||
   2485      frame->mForceSyncDecoding) {
   2486    flags |= imgIContainer::FLAG_SYNC_DECODE;
   2487  }
   2488  if (StaticPrefs::image_svg_blob_image() &&
   2489      image->GetType() == imgIContainer::TYPE_VECTOR) {
   2490    flags |= imgIContainer::FLAG_RECORD_BLOB;
   2491  }
   2492 
   2493  const nsRect destAppUnits = GetDestRect();
   2494  const int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
   2495  const auto destRect = LayoutDeviceRect::FromAppUnits(destAppUnits, factor);
   2496 
   2497  SVGImageContext svgContext;
   2498  Maybe<ImageIntRegion> region;
   2499  IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters(
   2500      image, mFrame, destRect, destRect, aSc, flags, svgContext, region);
   2501 
   2502  RefPtr<image::WebRenderImageProvider> provider;
   2503  ImgDrawResult drawResult =
   2504      image->GetImageProvider(aManager->LayerManager(), decodeSize, svgContext,
   2505                              region, flags, getter_AddRefs(provider));
   2506 
   2507  if (nsCOMPtr<imgIRequest> currentRequest = frame->GetCurrentRequest()) {
   2508    LCPHelpers::FinalizeLCPEntryForImage(
   2509        frame->GetContent()->AsElement(),
   2510        static_cast<imgRequestProxy*>(currentRequest.get()),
   2511        destAppUnits - ToReferenceFrame());
   2512  }
   2513 
   2514  // While we got a container, it may not contain a fully decoded surface. If
   2515  // that is the case, and we have an image we were previously displaying which
   2516  // has a fully decoded surface, then we should prefer the previous image.
   2517  bool updatePrevImage = false;
   2518  switch (drawResult) {
   2519    case ImgDrawResult::NOT_READY:
   2520    case ImgDrawResult::INCOMPLETE:
   2521    case ImgDrawResult::TEMPORARY_ERROR:
   2522      if (prevImage && prevImage != image) {
   2523        // The current image and the previous image might be switching between
   2524        // rasterized surfaces and blob recordings, so we need to update the
   2525        // flags appropriately.
   2526        uint32_t prevFlags = flags;
   2527        if (StaticPrefs::image_svg_blob_image() &&
   2528            prevImage->GetType() == imgIContainer::TYPE_VECTOR) {
   2529          prevFlags |= imgIContainer::FLAG_RECORD_BLOB;
   2530        } else {
   2531          prevFlags &= ~imgIContainer::FLAG_RECORD_BLOB;
   2532        }
   2533 
   2534        RefPtr<image::WebRenderImageProvider> prevProvider;
   2535        ImgDrawResult prevDrawResult = prevImage->GetImageProvider(
   2536            aManager->LayerManager(), decodeSize, svgContext, region, prevFlags,
   2537            getter_AddRefs(prevProvider));
   2538        if (prevProvider && (prevDrawResult == ImgDrawResult::SUCCESS ||
   2539                             prevDrawResult == ImgDrawResult::WRONG_SIZE)) {
   2540          // We use WRONG_SIZE here to ensure that when the frame next tries to
   2541          // invalidate due to a frame update from the current image, we don't
   2542          // consider the result from the previous image to be a valid result to
   2543          // avoid redrawing.
   2544          drawResult = ImgDrawResult::WRONG_SIZE;
   2545          provider = std::move(prevProvider);
   2546          flags = prevFlags;
   2547          break;
   2548        }
   2549 
   2550        // Previous image was unusable; we can forget about it.
   2551        updatePrevImage = true;
   2552      }
   2553      break;
   2554    case ImgDrawResult::NOT_SUPPORTED:
   2555      return false;
   2556    default:
   2557      updatePrevImage = prevImage != image;
   2558      break;
   2559  }
   2560 
   2561  // The previous image was not used, and is different from the current image.
   2562  // We should forget about it. We need to update the frame as well because the
   2563  // display item may get recreated.
   2564  if (updatePrevImage) {
   2565    frame->mPrevImage = frame->mImage;
   2566  }
   2567 
   2568  // If the image provider is null, we don't want to fallback. Any other
   2569  // failure will be due to resource constraints and fallback is unlikely to
   2570  // help us. Hence we can ignore the return value from PushImage.
   2571  aManager->CommandBuilder().PushImageProvider(
   2572      this, provider, drawResult, aBuilder, aResources, destRect, destRect);
   2573  return true;
   2574 }
   2575 
   2576 ImgDrawResult nsImageFrame::PaintImage(gfxContext& aRenderingContext,
   2577                                       nsPoint aPt, const nsRect& aDirtyRect,
   2578                                       imgIContainer* aImage, uint32_t aFlags) {
   2579  DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
   2580 
   2581  // Render the image into our content area (the area inside
   2582  // the borders and padding)
   2583  NS_ASSERTION(GetContentRectRelativeToSelf().width == mComputedSize.width,
   2584               "bad width");
   2585 
   2586  nsPoint anchorPoint;
   2587  const nsRect dest =
   2588      GetDestRect(GetContentRectRelativeToSelf() + aPt, &anchorPoint);
   2589 
   2590  SVGImageContext svgContext;
   2591  SVGImageContext::MaybeStoreContextPaint(svgContext, this, aImage);
   2592 
   2593  ImgDrawResult result = nsLayoutUtils::DrawSingleImage(
   2594      aRenderingContext, PresContext(), aImage,
   2595      nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
   2596      svgContext, aFlags, &anchorPoint);
   2597 
   2598  if (nsImageMap* map = GetImageMap(); map && map->HasFocus()) {
   2599    gfxPoint devPixelOffset = nsLayoutUtils::PointToGfxPoint(
   2600        dest.TopLeft(), PresContext()->AppUnitsPerDevPixel());
   2601    AutoRestoreTransform autoRestoreTransform(drawTarget);
   2602    drawTarget->SetTransform(
   2603        drawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset)));
   2604 
   2605    // solid white stroke:
   2606    ColorPattern white(ToDeviceColor(sRGBColor::OpaqueWhite()));
   2607    map->DrawFocus(this, *drawTarget, white);
   2608 
   2609    // then dashed black stroke over the top:
   2610    ColorPattern black(ToDeviceColor(sRGBColor::OpaqueBlack()));
   2611    StrokeOptions strokeOptions;
   2612    nsLayoutUtils::InitDashPattern(strokeOptions, StyleBorderStyle::Dotted);
   2613    map->DrawFocus(this, *drawTarget, black, strokeOptions);
   2614  }
   2615 
   2616  if (result == ImgDrawResult::SUCCESS) {
   2617    mPrevImage = aImage;
   2618  } else if (result == ImgDrawResult::BAD_IMAGE) {
   2619    mPrevImage = nullptr;
   2620  }
   2621 
   2622  return result;
   2623 }
   2624 
   2625 already_AddRefed<imgIRequest> nsImageFrame::GetCurrentRequest() const {
   2626  if (mKind != Kind::ImageLoadingContent) {
   2627    return do_AddRef(mOwnedRequest);
   2628  }
   2629 
   2630  MOZ_ASSERT(!mOwnedRequest);
   2631 
   2632  nsCOMPtr<imgIRequest> request;
   2633  nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
   2634  MOZ_ASSERT(imageLoader);
   2635  imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
   2636                          getter_AddRefs(request));
   2637  return request.forget();
   2638 }
   2639 
   2640 void nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
   2641                                    const nsDisplayListSet& aLists) {
   2642  if (!IsVisibleForPainting()) {
   2643    return;
   2644  }
   2645 
   2646  DisplayBorderBackgroundOutline(aBuilder, aLists);
   2647 
   2648  if (HidesContent()) {
   2649    DisplaySelectionOverlay(aBuilder, aLists.Content(),
   2650                            nsISelectionDisplay::DISPLAY_IMAGES);
   2651    return;
   2652  }
   2653 
   2654  DisplayListClipState::AutoSaveRestore clipState(aBuilder);
   2655  auto clipAxes = ShouldApplyOverflowClipping(StyleDisplay());
   2656  if (!clipAxes.isEmpty()) {
   2657    nsRect clipRect;
   2658    nsRectCornerRadii radii;
   2659    bool haveRadii =
   2660        ComputeOverflowClipRectRelativeToSelf(clipAxes, clipRect, radii);
   2661    clipState.ClipContainingBlockDescendants(
   2662        clipRect + aBuilder->ToReferenceFrame(this),
   2663        haveRadii ? &radii : nullptr);
   2664  }
   2665 
   2666  if (!mComputedSize.IsEmpty()) {
   2667    const bool imageOK = mKind != Kind::ImageLoadingContent ||
   2668                         ImageOk(mContent->AsElement()->State());
   2669 
   2670    nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest();
   2671 
   2672    const bool isViewTransition = mKind == Kind::ViewTransition;
   2673    const bool isImageFromStyle = mKind != Kind::ImageLoadingContent &&
   2674                                  mKind != Kind::XULImage && !isViewTransition;
   2675    const bool drawAltFeedback = [&] {
   2676      if (!imageOK) {
   2677        return true;
   2678      }
   2679      if (isImageFromStyle && !GetImageFromStyle()->IsImageRequestType()) {
   2680        // If we're a gradient, we don't need to draw alt feedback.
   2681        return false;
   2682      }
   2683      if (isViewTransition) {
   2684        // Same for view transitions.
   2685        return false;
   2686      }
   2687      // XXX(seth): The SizeIsAvailable check here should not be necessary - the
   2688      // intention is that a non-null mImage means we have a size, but there is
   2689      // currently some code that violates this invariant.
   2690      return !mImage || !SizeIsAvailable(currentRequest);
   2691    }();
   2692 
   2693    if (drawAltFeedback) {
   2694      // No image yet, or image load failed. Draw the alt-text and an icon
   2695      // indicating the status
   2696      aLists.Content()->AppendNewToTop<nsDisplayAltFeedback>(aBuilder, this);
   2697 
   2698      // This image is visible (we are being asked to paint it) but it's not
   2699      // decoded yet. And we are not going to ask the image to draw, so this
   2700      // may be the only chance to tell it that it should decode.
   2701      if (currentRequest) {
   2702        uint32_t status = 0;
   2703        currentRequest->GetImageStatus(&status);
   2704        if (!(status & imgIRequest::STATUS_DECODE_COMPLETE)) {
   2705          MaybeDecodeForPredictedSize();
   2706        }
   2707        // Increase loading priority if the image is ready to be displayed.
   2708        if (!(status & imgIRequest::STATUS_LOAD_COMPLETE)) {
   2709          currentRequest->BoostPriority(imgIRequest::CATEGORY_DISPLAY);
   2710        }
   2711      }
   2712    } else {
   2713      if (mImage || isViewTransition) {
   2714        aLists.Content()->AppendNewToTop<nsDisplayImage>(aBuilder, this);
   2715      } else if (isImageFromStyle) {
   2716        aLists.Content()->AppendNewToTop<nsDisplayGradient>(aBuilder, this);
   2717      }
   2718 
   2719      // If we were previously displaying an icon, we're not anymore
   2720      if (mDisplayingIcon) {
   2721        BrokenImageIcon::RemoveObserver(this);
   2722        mDisplayingIcon = false;
   2723      }
   2724    }
   2725  }
   2726 
   2727  if (ShouldDisplaySelection()) {
   2728    DisplaySelectionOverlay(aBuilder, aLists.Content(),
   2729                            nsISelectionDisplay::DISPLAY_IMAGES);
   2730  }
   2731 
   2732  BuildDisplayListForNonBlockChildren(aBuilder, aLists);
   2733 }
   2734 
   2735 bool nsImageFrame::ShouldDisplaySelection() {
   2736  int16_t displaySelection = PresShell()->GetSelectionFlags();
   2737  if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES)) {
   2738    // no need to check the blue border, we cannot be drawn selected.
   2739    return false;
   2740  }
   2741 
   2742  if (displaySelection != nsISelectionDisplay::DISPLAY_ALL) {
   2743    return true;
   2744  }
   2745 
   2746  // If the image is currently resize target of the editor, don't draw the
   2747  // selection overlay.
   2748  HTMLEditor* htmlEditor = nsContentUtils::GetHTMLEditor(PresContext());
   2749  if (!htmlEditor) {
   2750    return true;
   2751  }
   2752 
   2753  return htmlEditor->GetResizerTarget() != mContent;
   2754 }
   2755 
   2756 nsImageMap* nsImageFrame::GetImageMap() {
   2757  if (!mImageMap) {
   2758    if (nsIContent* map = GetMapElement()) {
   2759      mImageMap = new nsImageMap();
   2760      mImageMap->Init(this, map);
   2761    }
   2762  }
   2763 
   2764  return mImageMap;
   2765 }
   2766 
   2767 bool nsImageFrame::IsServerImageMap() {
   2768  return mContent->AsElement()->HasAttr(nsGkAtoms::ismap);
   2769 }
   2770 
   2771 CSSIntPoint nsImageFrame::TranslateEventCoords(const nsPoint& aPoint) const {
   2772  const nsRect contentRect = GetContentRectRelativeToSelf();
   2773  // Subtract out border and padding here so that the coordinates are
   2774  // now relative to the content area of this frame.
   2775  return CSSPixel::FromAppUnitsRounded(aPoint - contentRect.TopLeft());
   2776 }
   2777 
   2778 bool nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget,
   2779                                              nsIContent** aNode) {
   2780  aTarget.Truncate();
   2781  *aHref = nullptr;
   2782  *aNode = nullptr;
   2783 
   2784  // Walk up the content tree, looking for an nsIDOMAnchorElement
   2785  for (nsIContent* content = mContent->GetParent(); content;
   2786       content = content->GetParent()) {
   2787    nsCOMPtr<dom::Link> link = do_QueryInterface(content);
   2788    if (!link) {
   2789      continue;
   2790    }
   2791    if (nsCOMPtr<nsIURI> href = link->GetURI()) {
   2792      href.forget(aHref);
   2793    }
   2794 
   2795    if (auto* anchor = HTMLAnchorElement::FromNode(content)) {
   2796      anchor->GetLinkTarget(aTarget);
   2797    }
   2798    NS_ADDREF(*aNode = content);
   2799    return *aHref != nullptr;
   2800  }
   2801  return false;
   2802 }
   2803 
   2804 bool nsImageFrame::IsLeafDynamic() const {
   2805  if (mKind != Kind::ImageLoadingContent) {
   2806    // Image frames created for "content: url()" could have an author-controlled
   2807    // shadow root, we want to be a regular leaf for those.
   2808    return true;
   2809  }
   2810  // For elements that create image frames, calling attachShadow() will throw,
   2811  // so the only ShadowRoot we could ever have is a UA widget.
   2812  const auto* shadow = mContent->AsElement()->GetShadowRoot();
   2813  MOZ_ASSERT_IF(shadow, shadow->IsUAWidget());
   2814  return !shadow;
   2815 }
   2816 
   2817 nsIContent* nsImageFrame::GetContentForEvent(const WidgetEvent* aEvent) const {
   2818  if (mImageMap) {
   2819    // XXX We need to make this special check for area element's capturing the
   2820    // mouse due to bug 135040. Remove it once that's fixed.
   2821    nsIContent* capturingContent = aEvent->HasMouseEventMessage()
   2822                                       ? PresShell::GetCapturingContent()
   2823                                       : nullptr;
   2824    if (capturingContent && capturingContent->GetPrimaryFrame() == this) {
   2825      return capturingContent;
   2826    }
   2827    const CSSIntPoint p = TranslateEventCoords(
   2828        nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, RelativeTo{this}));
   2829    if (auto* area = mImageMap->GetArea(p)) {
   2830      return area;
   2831    }
   2832  }
   2833  return nsIFrame::GetContentForEvent(aEvent);
   2834 }
   2835 
   2836 // XXX what should clicks on transparent pixels do?
   2837 nsresult nsImageFrame::HandleEvent(nsPresContext* aPresContext,
   2838                                   WidgetGUIEvent* aEvent,
   2839                                   nsEventStatus* aEventStatus) {
   2840  NS_ENSURE_ARG_POINTER(aEventStatus);
   2841 
   2842  if ((aEvent->mMessage == ePointerClick &&
   2843       aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary) ||
   2844      aEvent->mMessage == eMouseMove) {
   2845    nsImageMap* map = GetImageMap();
   2846    bool isServerMap = IsServerImageMap();
   2847    if (map || isServerMap) {
   2848      CSSIntPoint p =
   2849          TranslateEventCoords(nsLayoutUtils::GetEventCoordinatesRelativeTo(
   2850              aEvent, RelativeTo{this}));
   2851 
   2852      // Even though client-side image map triggering happens
   2853      // through content, we need to make sure we're not inside
   2854      // (in case we deal with a case of both client-side and
   2855      // sever-side on the same image - it happens!)
   2856      const bool inside = map && map->GetArea(p);
   2857 
   2858      if (!inside && isServerMap) {
   2859        // Server side image maps use the href in a containing anchor
   2860        // element to provide the basis for the destination url.
   2861        nsCOMPtr<nsIURI> uri;
   2862        nsAutoString target;
   2863        nsCOMPtr<nsIContent> anchorNode;
   2864        if (GetAnchorHREFTargetAndNode(getter_AddRefs(uri), target,
   2865                                       getter_AddRefs(anchorNode))) {
   2866          // XXX if the mouse is over/clicked in the border/padding area
   2867          // we should probably just pretend nothing happened. Nav4
   2868          // keeps the x,y coordinates positive as we do; IE doesn't
   2869          // bother. Both of them send the click through even when the
   2870          // mouse is over the border.
   2871          if (p.x < 0) {
   2872            p.x = 0;
   2873          }
   2874          if (p.y < 0) {
   2875            p.y = 0;
   2876          }
   2877 
   2878          nsAutoCString spec;
   2879          nsresult rv = uri->GetSpec(spec);
   2880          NS_ENSURE_SUCCESS(rv, rv);
   2881 
   2882          spec += nsPrintfCString("?%d,%d", p.x.value, p.y.value);
   2883          rv = NS_MutateURI(uri).SetSpec(spec).Finalize(uri);
   2884          NS_ENSURE_SUCCESS(rv, rv);
   2885 
   2886          if (aEvent->mMessage == ePointerClick &&
   2887              !aEvent->DefaultPrevented()) {
   2888            *aEventStatus = nsEventStatus_eConsumeDoDefault;
   2889            nsContentUtils::TriggerLinkClick(
   2890                anchorNode, uri, target,
   2891                aEvent->IsTrusted() ? UserNavigationInvolvement::Activation
   2892                                    : UserNavigationInvolvement::None);
   2893          } else {
   2894            nsContentUtils::TriggerLinkMouseOver(anchorNode, uri, target);
   2895          }
   2896        }
   2897      }
   2898    }
   2899  }
   2900 
   2901  return nsAtomicContainerFrame::HandleEvent(aPresContext, aEvent,
   2902                                             aEventStatus);
   2903 }
   2904 
   2905 nsIFrame::Cursor nsImageFrame::GetCursor(const nsPoint& aPoint) {
   2906  nsImageMap* map = GetImageMap();
   2907  if (!map) {
   2908    return nsIFrame::GetCursor(aPoint);
   2909  }
   2910  const CSSIntPoint p = TranslateEventCoords(aPoint);
   2911  HTMLAreaElement* area = map->GetArea(p);
   2912  if (!area) {
   2913    return nsIFrame::GetCursor(aPoint);
   2914  }
   2915 
   2916  // Use the cursor from the style of the *area* element.
   2917  RefPtr<ComputedStyle> areaStyle =
   2918      PresShell()->StyleSet()->ResolveStyleLazily(*area);
   2919 
   2920  // This is one of the cases, like the <xul:tree> pseudo-style stuff, where we
   2921  // get styles out of the blue and expect to trigger image loads for those.
   2922  areaStyle->StartImageLoads(*PresContext()->Document());
   2923 
   2924  StyleCursorKind kind = areaStyle->StyleUI()->Cursor().keyword;
   2925  if (kind == StyleCursorKind::Auto) {
   2926    kind = StyleCursorKind::Default;
   2927  }
   2928  return Cursor{kind, AllowCustomCursorImage::Yes, std::move(areaStyle)};
   2929 }
   2930 
   2931 nsresult nsImageFrame::AttributeChanged(int32_t aNameSpaceID,
   2932                                        nsAtom* aAttribute,
   2933                                        AttrModType aModType) {
   2934  nsresult rv = nsAtomicContainerFrame::AttributeChanged(aNameSpaceID,
   2935                                                         aAttribute, aModType);
   2936  if (NS_FAILED(rv)) {
   2937    return rv;
   2938  }
   2939  if (nsGkAtoms::alt == aAttribute) {
   2940    PresShell()->FrameNeedsReflow(
   2941        this, IntrinsicDirty::FrameAncestorsAndDescendants, NS_FRAME_IS_DIRTY);
   2942  }
   2943  if (mKind == Kind::XULImage && aAttribute == nsGkAtoms::src &&
   2944      aNameSpaceID == kNameSpaceID_None) {
   2945    UpdateXULImage();
   2946  }
   2947  return NS_OK;
   2948 }
   2949 
   2950 void nsImageFrame::OnVisibilityChange(
   2951    Visibility aNewVisibility, const Maybe<OnNonvisible>& aNonvisibleAction) {
   2952  if (mKind == Kind::ImageLoadingContent) {
   2953    nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
   2954    imageLoader->OnVisibilityChange(aNewVisibility, aNonvisibleAction);
   2955  }
   2956 
   2957  if (aNewVisibility == Visibility::ApproximatelyVisible &&
   2958      PresShell()->IsActive()) {
   2959    MaybeDecodeForPredictedSize();
   2960  }
   2961 
   2962  nsAtomicContainerFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
   2963 }
   2964 
   2965 void nsImageFrame::MarkIntrinsicISizesDirty() {
   2966  // Recompute our intrinsic size and ratio. It's important that we pass 'true'
   2967  // here -- that makes us recompute the intrinsic size *and* ratio, regardless
   2968  // of their current value. Without that, EnsureIntrinsicSizeAndRatio assumes
   2969  // the intrinsic ratio can't have changed in some cases; but if we're a
   2970  // "content-visibility:auto" image that's being scrolled back into view, our
   2971  // ratio may have changed from not-existing to existing, per spec text[1]
   2972  // about suppressing the natural aspect ratio when size-contained.
   2973  // [1] https://drafts.csswg.org/css-contain-2/#containment-size
   2974  EnsureIntrinsicSizeAndRatio(true);
   2975 
   2976  // Call superclass method:
   2977  nsAtomicContainerFrame::MarkIntrinsicISizesDirty();
   2978 }
   2979 
   2980 #ifdef DEBUG_FRAME_DUMP
   2981 nsresult nsImageFrame::GetFrameName(nsAString& aResult) const {
   2982  return MakeFrameName(u"ImageFrame"_ns, aResult);
   2983 }
   2984 
   2985 void nsImageFrame::List(FILE* out, const char* aPrefix,
   2986                        ListFlags aFlags) const {
   2987  nsCString str;
   2988  ListGeneric(str, aPrefix, aFlags);
   2989 
   2990  // output the img src url
   2991  if (nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest()) {
   2992    nsCOMPtr<nsIURI> uri = currentRequest->GetURI();
   2993    nsAutoCString uristr;
   2994    uri->GetAsciiSpec(uristr);
   2995    str += nsPrintfCString(" [src=%s]", uristr.get());
   2996  }
   2997  fprintf_stderr(out, "%s\n", str.get());
   2998 }
   2999 #endif
   3000 
   3001 LogicalSides nsImageFrame::GetLogicalSkipSides() const {
   3002  LogicalSides skip(mWritingMode);
   3003  if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
   3004                   StyleBoxDecorationBreak::Clone)) {
   3005    return skip;
   3006  }
   3007  if (GetPrevInFlow()) {
   3008    skip += LogicalSide::BStart;
   3009  }
   3010  if (GetNextInFlow()) {
   3011    skip += LogicalSide::BEnd;
   3012  }
   3013  return skip;
   3014 }
   3015 NS_IMPL_ISUPPORTS(nsImageListener, imgINotificationObserver)
   3016 
   3017 nsImageListener::nsImageListener(nsImageFrame* aFrame) : mFrame(aFrame) {}
   3018 
   3019 nsImageListener::~nsImageListener() = default;
   3020 
   3021 void nsImageListener::Notify(imgIRequest* aRequest, int32_t aType,
   3022                             const nsIntRect* aData) {
   3023  if (!mFrame) {
   3024    return;
   3025  }
   3026 
   3027  return mFrame->Notify(aRequest, aType, aData);
   3028 }
   3029 
   3030 static bool IsInAutoWidthTableCellForQuirk(nsIFrame* aFrame) {
   3031  if (eCompatibility_NavQuirks != aFrame->PresContext()->CompatibilityMode()) {
   3032    return false;
   3033  }
   3034  // Check if the parent of the closest nsBlockFrame has auto width.
   3035  nsBlockFrame* ancestor = nsLayoutUtils::FindNearestBlockAncestor(aFrame);
   3036  if (ancestor->Style()->GetPseudoType() == PseudoStyleType::cellContent) {
   3037    // Assume direct parent is a table cell frame.
   3038    nsIFrame* grandAncestor = static_cast<nsIFrame*>(ancestor->GetParent());
   3039    return grandAncestor &&
   3040           grandAncestor->StylePosition()
   3041               ->GetWidth(AnchorPosResolutionParams::From(grandAncestor))
   3042               ->IsAuto();
   3043  }
   3044  return false;
   3045 }
   3046 
   3047 void nsImageFrame::AddInlineMinISize(const IntrinsicSizeInput& aInput,
   3048                                     InlineMinISizeData* aData) {
   3049  // Note: we are one of the children that mPercentageBasisForChildren was
   3050  // prepared for (i.e. our parent frame prepares the percentage basis for us,
   3051  // not for our own children). Hence it's fine that we're resolving our
   3052  // percentages sizes against this basis in IntrinsicForContainer().
   3053  nscoord isize = nsLayoutUtils::IntrinsicForContainer(
   3054      aInput.mContext, this, IntrinsicISizeType::MinISize,
   3055      aInput.mPercentageBasisForChildren);
   3056  bool canBreak = !IsInAutoWidthTableCellForQuirk(this);
   3057  aData->DefaultAddInlineMinISize(this, isize, canBreak);
   3058 }
   3059 
   3060 void nsImageFrame::ReleaseGlobals() { BrokenImageIcon::Shutdown(); }