forked from mirrors/gecko-dev
		
	 93a0510070
			
		
	
	
		93a0510070
		
	
	
	
	
		
			
			With the patches of bug 1815229, these errors appear: browser/base/content/test/performance/browser_startup_images.js | Loaded image resource://gre-resources/loading-image.png should have been shown It's a real issue, where we eagerly load the broken image icon and so on even though we don't use them. This fixes it by lazily-loading the icon once, only when needed. Differential Revision: https://phabricator.services.mozilla.com/D170159
		
			
				
	
	
		
			470 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			470 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| /* rendering object for replaced elements with image data */
 | |
| 
 | |
| #ifndef nsImageFrame_h___
 | |
| #define nsImageFrame_h___
 | |
| 
 | |
| #include "nsAtomicContainerFrame.h"
 | |
| #include "nsIObserver.h"
 | |
| 
 | |
| #include "imgINotificationObserver.h"
 | |
| 
 | |
| #include "nsDisplayList.h"
 | |
| #include "imgIContainer.h"
 | |
| #include "mozilla/Attributes.h"
 | |
| #include "mozilla/DebugOnly.h"
 | |
| #include "mozilla/StaticPtr.h"
 | |
| #include "nsIReflowCallback.h"
 | |
| #include "nsTObserverArray.h"
 | |
| 
 | |
| class nsFontMetrics;
 | |
| class nsImageMap;
 | |
| class nsIURI;
 | |
| class nsILoadGroup;
 | |
| class nsPresContext;
 | |
| class nsImageFrame;
 | |
| class nsTransform2D;
 | |
| class nsImageLoadingContent;
 | |
| 
 | |
| namespace mozilla {
 | |
| class nsDisplayImage;
 | |
| class PresShell;
 | |
| namespace layers {
 | |
| class ImageContainer;
 | |
| class LayerManager;
 | |
| }  // namespace layers
 | |
| }  // namespace mozilla
 | |
| 
 | |
| class nsImageListener final : public imgINotificationObserver {
 | |
|  protected:
 | |
|   virtual ~nsImageListener();
 | |
| 
 | |
|  public:
 | |
|   explicit nsImageListener(nsImageFrame* aFrame);
 | |
| 
 | |
|   NS_DECL_ISUPPORTS
 | |
|   NS_DECL_IMGINOTIFICATIONOBSERVER
 | |
| 
 | |
|   void SetFrame(nsImageFrame* frame) { mFrame = frame; }
 | |
| 
 | |
|  private:
 | |
|   nsImageFrame* mFrame;
 | |
| };
 | |
| 
 | |
| class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback {
 | |
|  public:
 | |
|   template <typename T>
 | |
|   using Maybe = mozilla::Maybe<T>;
 | |
|   using Nothing = mozilla::Nothing;
 | |
|   using Visibility = mozilla::Visibility;
 | |
| 
 | |
|   typedef mozilla::image::ImgDrawResult ImgDrawResult;
 | |
|   typedef mozilla::layers::ImageContainer ImageContainer;
 | |
|   typedef mozilla::layers::LayerManager LayerManager;
 | |
| 
 | |
|   NS_DECL_FRAMEARENA_HELPERS(nsImageFrame)
 | |
|   NS_DECL_QUERYFRAME
 | |
| 
 | |
|   void DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData&) override;
 | |
|   void DidSetComputedStyle(ComputedStyle* aOldStyle) final;
 | |
| 
 | |
|   void Init(nsIContent* aContent, nsContainerFrame* aParent,
 | |
|             nsIFrame* aPrevInFlow) override;
 | |
|   void BuildDisplayList(nsDisplayListBuilder*, const nsDisplayListSet&) final;
 | |
|   nscoord GetMinISize(gfxContext* aRenderingContext) final;
 | |
|   nscoord GetPrefISize(gfxContext* aRenderingContext) final;
 | |
|   mozilla::IntrinsicSize GetIntrinsicSize() final { return mIntrinsicSize; }
 | |
|   mozilla::AspectRatio GetIntrinsicRatio() const final {
 | |
|     return mIntrinsicRatio;
 | |
|   }
 | |
|   void Reflow(nsPresContext*, ReflowOutput&, const ReflowInput&,
 | |
|               nsReflowStatus&) override;
 | |
|   bool IsLeafDynamic() const override;
 | |
| 
 | |
|   nsresult GetContentForEvent(const mozilla::WidgetEvent*,
 | |
|                               nsIContent** aContent) final;
 | |
|   nsresult HandleEvent(nsPresContext*, mozilla::WidgetGUIEvent*,
 | |
|                        nsEventStatus*) override;
 | |
|   mozilla::Maybe<Cursor> GetCursor(const nsPoint&) override;
 | |
|   nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
 | |
|                             int32_t aModType) final;
 | |
| 
 | |
|   void OnVisibilityChange(
 | |
|       Visibility aNewVisibility,
 | |
|       const Maybe<OnNonvisible>& aNonvisibleAction = Nothing()) final;
 | |
| 
 | |
|   void ResponsiveContentDensityChanged();
 | |
|   void ElementStateChanged(mozilla::dom::ElementState) override;
 | |
|   void SetupOwnedRequest();
 | |
|   void DeinitOwnedRequest();
 | |
|   bool ShouldShowBrokenImageIcon() const;
 | |
| 
 | |
|   bool IsForImageLoadingContent() const {
 | |
|     return mKind == Kind::ImageLoadingContent;
 | |
|   }
 | |
| 
 | |
|   void UpdateXULImage();
 | |
|   const mozilla::StyleImage* GetImageFromStyle() const;
 | |
| 
 | |
| #ifdef ACCESSIBILITY
 | |
|   mozilla::a11y::AccType AccessibleType() override;
 | |
| #endif
 | |
| 
 | |
|   bool IsFrameOfType(uint32_t aFlags) const final {
 | |
|     return nsAtomicContainerFrame::IsFrameOfType(
 | |
|         aFlags & ~(nsIFrame::eReplaced | nsIFrame::eReplacedSizing));
 | |
|   }
 | |
| 
 | |
| #ifdef DEBUG_FRAME_DUMP
 | |
|   nsresult GetFrameName(nsAString& aResult) const override;
 | |
|   void List(FILE* out = stderr, const char* aPrefix = "",
 | |
|             ListFlags aFlags = ListFlags()) const final;
 | |
| #endif
 | |
| 
 | |
|   LogicalSides GetLogicalSkipSides() const final;
 | |
| 
 | |
|   static void ReleaseGlobals();
 | |
| 
 | |
|   already_AddRefed<imgIRequest> GetCurrentRequest() const;
 | |
|   void Notify(imgIRequest*, int32_t aType, const nsIntRect* aData);
 | |
| 
 | |
|   /**
 | |
|    * Returns whether we should replace an element with an image corresponding to
 | |
|    * its 'content' CSS property.
 | |
|    */
 | |
|   static bool ShouldCreateImageFrameForContentProperty(
 | |
|       const mozilla::dom::Element&, const ComputedStyle&);
 | |
| 
 | |
|   /**
 | |
|    * Function to test whether given an element and its style, that element
 | |
|    * should get an image frame, and if so, which kind of image frame (for
 | |
|    * `content`, or for the element itself).
 | |
|    */
 | |
|   enum class ImageFrameType {
 | |
|     ForContentProperty,
 | |
|     ForElementRequest,
 | |
|     None,
 | |
|   };
 | |
|   static ImageFrameType ImageFrameTypeFor(const mozilla::dom::Element&,
 | |
|                                           const ComputedStyle&);
 | |
| 
 | |
|   ImgDrawResult DisplayAltFeedback(gfxContext& aRenderingContext,
 | |
|                                    const nsRect& aDirtyRect, nsPoint aPt,
 | |
|                                    uint32_t aFlags);
 | |
| 
 | |
|   ImgDrawResult DisplayAltFeedbackWithoutLayer(
 | |
|       nsDisplayItem*, mozilla::wr::DisplayListBuilder&,
 | |
|       mozilla::wr::IpcResourceUpdateQueue&,
 | |
|       const mozilla::layers::StackingContextHelper&,
 | |
|       mozilla::layers::RenderRootStateManager*, nsDisplayListBuilder*,
 | |
|       nsPoint aPt, uint32_t aFlags);
 | |
| 
 | |
|   /**
 | |
|    * Return a map element associated with this image.
 | |
|    */
 | |
|   mozilla::dom::Element* GetMapElement() const;
 | |
| 
 | |
|   /**
 | |
|    * Return true if the image has associated image map.
 | |
|    */
 | |
|   bool HasImageMap() const { return mImageMap || GetMapElement(); }
 | |
| 
 | |
|   nsImageMap* GetImageMap();
 | |
|   nsImageMap* GetExistingImageMap() const { return mImageMap; }
 | |
| 
 | |
|   void AddInlineMinISize(gfxContext* aRenderingContext,
 | |
|                          InlineMinISizeData* aData) final;
 | |
| 
 | |
|   void DisconnectMap();
 | |
| 
 | |
|   // nsIReflowCallback
 | |
|   bool ReflowFinished() final;
 | |
|   void ReflowCallbackCanceled() final;
 | |
| 
 | |
|   // The kind of image frame we are.
 | |
|   enum class Kind : uint8_t {
 | |
|     // For an nsImageLoadingContent.
 | |
|     ImageLoadingContent,
 | |
|     // For a <xul:image> element.
 | |
|     XULImage,
 | |
|     // For css 'content: url(..)' on non-generated content.
 | |
|     ContentProperty,
 | |
|     // For a child of a ::before / ::after pseudo-element that had an url() item
 | |
|     // for the content property.
 | |
|     ContentPropertyAtIndex,
 | |
|     // For a list-style-image ::marker.
 | |
|     ListStyleImage,
 | |
|   };
 | |
| 
 | |
|   // Creates a suitable continuing frame for this frame.
 | |
|   nsImageFrame* CreateContinuingFrame(mozilla::PresShell*,
 | |
|                                       ComputedStyle*) const;
 | |
| 
 | |
|  private:
 | |
|   friend nsIFrame* NS_NewImageFrame(mozilla::PresShell*, ComputedStyle*);
 | |
|   friend nsIFrame* NS_NewXULImageFrame(mozilla::PresShell*, ComputedStyle*);
 | |
|   friend nsIFrame* NS_NewImageFrameForContentProperty(mozilla::PresShell*,
 | |
|                                                       ComputedStyle*);
 | |
|   friend nsIFrame* NS_NewImageFrameForGeneratedContentIndex(mozilla::PresShell*,
 | |
|                                                             ComputedStyle*);
 | |
|   friend nsIFrame* NS_NewImageFrameForListStyleImage(mozilla::PresShell*,
 | |
|                                                      ComputedStyle*);
 | |
| 
 | |
|   nsImageFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, Kind aKind)
 | |
|       : nsImageFrame(aStyle, aPresContext, kClassID, aKind) {}
 | |
| 
 | |
|   nsImageFrame(ComputedStyle*, nsPresContext* aPresContext, ClassID, Kind);
 | |
| 
 | |
|   void ReflowChildren(nsPresContext*, const ReflowInput&,
 | |
|                       const mozilla::LogicalSize& aImageSize);
 | |
| 
 | |
|   void UpdateIntrinsicSizeAndRatio();
 | |
| 
 | |
|  protected:
 | |
|   nsImageFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, ClassID aID)
 | |
|       : nsImageFrame(aStyle, aPresContext, aID, Kind::ImageLoadingContent) {}
 | |
| 
 | |
|   ~nsImageFrame() override;
 | |
| 
 | |
|   void EnsureIntrinsicSizeAndRatio();
 | |
| 
 | |
|   bool GotInitialReflow() const {
 | |
|     return !HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
 | |
|   }
 | |
| 
 | |
|   SizeComputationResult ComputeSize(
 | |
|       gfxContext* aRenderingContext, mozilla::WritingMode aWM,
 | |
|       const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
 | |
|       const mozilla::LogicalSize& aMargin,
 | |
|       const mozilla::LogicalSize& aBorderPadding,
 | |
|       const mozilla::StyleSizeOverrides& aSizeOverrides,
 | |
|       mozilla::ComputeSizeFlags aFlags) final;
 | |
| 
 | |
|   bool IsServerImageMap();
 | |
| 
 | |
|   // Translate a point that is relative to our frame into a localized CSS pixel
 | |
|   // coordinate that is relative to the content area of this frame (inside the
 | |
|   // border+padding).
 | |
|   mozilla::CSSIntPoint TranslateEventCoords(const nsPoint& aPoint);
 | |
| 
 | |
|   bool GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget,
 | |
|                                   nsIContent** aNode);
 | |
|   /**
 | |
|    * Computes the width of the string that fits into the available space
 | |
|    *
 | |
|    * @param in aLength total length of the string in PRUnichars
 | |
|    * @param in aMaxWidth width not to be exceeded
 | |
|    * @param out aMaxFit length of the string that fits within aMaxWidth
 | |
|    *            in PRUnichars
 | |
|    * @return width of the string that fits within aMaxWidth
 | |
|    */
 | |
|   nscoord MeasureString(const char16_t* aString, int32_t aLength,
 | |
|                         nscoord aMaxWidth, uint32_t& aMaxFit,
 | |
|                         gfxContext& aContext, nsFontMetrics& aFontMetrics);
 | |
| 
 | |
|   void DisplayAltText(nsPresContext* aPresContext,
 | |
|                       gfxContext& aRenderingContext, const nsString& aAltText,
 | |
|                       const nsRect& aRect);
 | |
| 
 | |
|   ImgDrawResult PaintImage(gfxContext& aRenderingContext, nsPoint aPt,
 | |
|                            const nsRect& aDirtyRect, imgIContainer* aImage,
 | |
|                            uint32_t aFlags);
 | |
| 
 | |
|   /**
 | |
|    * If we're ready to decode - that is, if our current request's image is
 | |
|    * available and our decoding heuristics are satisfied - then trigger a decode
 | |
|    * for our image at the size we predict it will be drawn next time it's
 | |
|    * painted.
 | |
|    */
 | |
|   void MaybeDecodeForPredictedSize();
 | |
| 
 | |
|   /**
 | |
|    * Is this frame part of a ::marker pseudo?
 | |
|    */
 | |
|   bool IsForMarkerPseudo() const;
 | |
| 
 | |
|  protected:
 | |
|   friend class nsImageListener;
 | |
|   friend class nsImageLoadingContent;
 | |
|   friend class mozilla::PresShell;
 | |
| 
 | |
|   void OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage);
 | |
|   void OnFrameUpdate(imgIRequest* aRequest, const nsIntRect* aRect);
 | |
|   void OnLoadComplete(imgIRequest* aRequest, nsresult aStatus);
 | |
| 
 | |
|   /**
 | |
|    * Notification that aRequest will now be the current request.
 | |
|    */
 | |
|   void NotifyNewCurrentRequest(imgIRequest* aRequest, nsresult aStatus);
 | |
| 
 | |
|   /// Always sync decode our image when painting if @aForce is true.
 | |
|   void SetForceSyncDecoding(bool aForce) { mForceSyncDecoding = aForce; }
 | |
| 
 | |
|   void AssertSyncDecodingHintIsInSync() const
 | |
| #ifndef DEBUG
 | |
|       {}
 | |
| #else
 | |
|       ;
 | |
| #endif
 | |
| 
 | |
|   /**
 | |
|    * Computes the predicted dest rect that we'll draw into, in app units, based
 | |
|    * upon the provided frame content box. (The content box is what
 | |
|    * nsDisplayImage::GetBounds() returns.)
 | |
|    * The result is not necessarily contained in the frame content box.
 | |
|    */
 | |
|   nsRect PredictedDestRect(const nsRect& aFrameContentBox);
 | |
| 
 | |
|  private:
 | |
|   nscoord GetContinuationOffset() const;
 | |
|   bool ShouldDisplaySelection();
 | |
| 
 | |
|   // Whether the image frame should use the mapped aspect ratio from width=""
 | |
|   // and height="".
 | |
|   bool ShouldUseMappedAspectRatio() const;
 | |
| 
 | |
|   // Recalculate mIntrinsicSize from the image.
 | |
|   bool UpdateIntrinsicSize();
 | |
| 
 | |
|   // Recalculate mIntrinsicRatio from the image.
 | |
|   bool UpdateIntrinsicRatio();
 | |
| 
 | |
|   /**
 | |
|    * This function calculates the transform for converting between
 | |
|    * source space & destination space. May fail if our image has a
 | |
|    * percent-valued or zero-valued height or width.
 | |
|    *
 | |
|    * @param aTransform The transform object to populate.
 | |
|    *
 | |
|    * @return whether we succeeded in creating the transform.
 | |
|    */
 | |
|   bool GetSourceToDestTransform(nsTransform2D& aTransform);
 | |
| 
 | |
|   /**
 | |
|    * Helper function to check whether the request corresponds to a load we don't
 | |
|    * care about.  Most of the decoder observer methods will bail early if this
 | |
|    * returns true.
 | |
|    */
 | |
|   bool IsPendingLoad(imgIRequest*) const;
 | |
| 
 | |
|   /**
 | |
|    * Updates mImage based on the current image request, and the image passed in
 | |
|    * (both can be null), and invalidate layout and paint as needed.
 | |
|    */
 | |
|   void UpdateImage(imgIRequest*, imgIContainer*);
 | |
| 
 | |
|   /**
 | |
|    * Function to convert a dirty rect in the source image to a dirty
 | |
|    * rect for the image frame.
 | |
|    */
 | |
|   nsRect SourceRectToDest(const nsIntRect& aRect);
 | |
| 
 | |
|   /**
 | |
|    * Triggers invalidation for both our image display item and, if appropriate,
 | |
|    * our alt-feedback display item.
 | |
|    *
 | |
|    * @param aLayerInvalidRect The area to invalidate in layer space. If null,
 | |
|    * the entire layer will be invalidated.
 | |
|    * @param aFrameInvalidRect The area to invalidate in frame space. If null,
 | |
|    * the entire frame will be invalidated.
 | |
|    */
 | |
|   void InvalidateSelf(const nsIntRect* aLayerInvalidRect,
 | |
|                       const nsRect* aFrameInvalidRect);
 | |
| 
 | |
|   void MaybeSendIntrinsicSizeAndRatioToEmbedder();
 | |
|   void MaybeSendIntrinsicSizeAndRatioToEmbedder(Maybe<mozilla::IntrinsicSize>,
 | |
|                                                 Maybe<mozilla::AspectRatio>);
 | |
| 
 | |
|   RefPtr<nsImageMap> mImageMap;
 | |
| 
 | |
|   RefPtr<nsImageListener> mListener;
 | |
| 
 | |
|   // An image request created for content: url(..), list-style-image, or
 | |
|   // <xul:image>.
 | |
|   RefPtr<imgRequestProxy> mOwnedRequest;
 | |
| 
 | |
|   nsCOMPtr<imgIContainer> mImage;
 | |
|   nsCOMPtr<imgIContainer> mPrevImage;
 | |
| 
 | |
|   // The content-box size as if we are not fragmented, cached in the most recent
 | |
|   // reflow.
 | |
|   nsSize mComputedSize;
 | |
| 
 | |
|   mozilla::IntrinsicSize mIntrinsicSize;
 | |
| 
 | |
|   // Stores mImage's intrinsic ratio, or a default AspectRatio if there's no
 | |
|   // intrinsic ratio.
 | |
|   mozilla::AspectRatio mIntrinsicRatio;
 | |
| 
 | |
|   const Kind mKind;
 | |
|   bool mOwnedRequestRegistered = false;
 | |
|   bool mDisplayingIcon = false;
 | |
|   bool mFirstFrameComplete = false;
 | |
|   bool mReflowCallbackPosted = false;
 | |
|   bool mForceSyncDecoding = false;
 | |
|   bool mIsInObjectOrEmbed = false;
 | |
| 
 | |
|  public:
 | |
|   friend class mozilla::nsDisplayImage;
 | |
|   friend class nsDisplayGradient;
 | |
| };
 | |
| 
 | |
| namespace mozilla {
 | |
| /**
 | |
|  * Note that nsDisplayImage does not receive events. However, an image element
 | |
|  * is replaced content so its background will be z-adjacent to the
 | |
|  * image itself, and hence receive events just as if the image itself
 | |
|  * received events.
 | |
|  */
 | |
| class nsDisplayImage final : public nsPaintedDisplayItem {
 | |
|  public:
 | |
|   typedef mozilla::layers::LayerManager LayerManager;
 | |
| 
 | |
|   nsDisplayImage(nsDisplayListBuilder* aBuilder, nsImageFrame* aFrame,
 | |
|                  imgIContainer* aImage, imgIContainer* aPrevImage)
 | |
|       : nsPaintedDisplayItem(aBuilder, aFrame),
 | |
|         mImage(aImage),
 | |
|         mPrevImage(aPrevImage) {
 | |
|     MOZ_COUNT_CTOR(nsDisplayImage);
 | |
|   }
 | |
|   ~nsDisplayImage() final { MOZ_COUNT_DTOR(nsDisplayImage); }
 | |
| 
 | |
|   void Paint(nsDisplayListBuilder*, gfxContext* aCtx) final;
 | |
| 
 | |
|   /**
 | |
|    * @return The dest rect we'll use when drawing this image, in app units.
 | |
|    *         Not necessarily contained in this item's bounds.
 | |
|    */
 | |
|   nsRect GetDestRect() const;
 | |
| 
 | |
|   nsRect GetBounds(bool* aSnap) const {
 | |
|     *aSnap = true;
 | |
|     return Frame()->GetContentRectRelativeToSelf() + ToReferenceFrame();
 | |
|   }
 | |
| 
 | |
|   nsRect GetBounds(nsDisplayListBuilder*, bool* aSnap) const final {
 | |
|     return GetBounds(aSnap);
 | |
|   }
 | |
| 
 | |
|   nsRegion GetOpaqueRegion(nsDisplayListBuilder*, bool* aSnap) const final;
 | |
| 
 | |
|   bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder&,
 | |
|                                mozilla::wr::IpcResourceUpdateQueue&,
 | |
|                                const StackingContextHelper&,
 | |
|                                mozilla::layers::RenderRootStateManager*,
 | |
|                                nsDisplayListBuilder*) final;
 | |
| 
 | |
|   NS_DISPLAY_DECL_NAME("Image", TYPE_IMAGE)
 | |
|  private:
 | |
|   nsCOMPtr<imgIContainer> mImage;
 | |
|   nsCOMPtr<imgIContainer> mPrevImage;
 | |
| };
 | |
| 
 | |
| }  // namespace mozilla
 | |
| 
 | |
| #endif /* nsImageFrame_h___ */
 |