/* -*- 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 using Maybe = mozilla::Maybe; 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 Destroy(DestroyContext&) override; void DidSetComputedStyle(ComputedStyle* aOldStyle) final; void Init(nsIContent* aContent, nsContainerFrame* aParent, nsIFrame* aPrevInFlow) override; void BuildDisplayList(nsDisplayListBuilder*, const nsDisplayListSet&) final; nscoord IntrinsicISize(const mozilla::IntrinsicSizeInput& aInput, mozilla::IntrinsicISizeType aType) 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; nsIContent* GetContentForEvent(const mozilla::WidgetEvent*) const final; MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult HandleEvent(nsPresContext*, mozilla::WidgetGUIEvent*, nsEventStatus*) override; Cursor GetCursor(const nsPoint&) override; nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute, int32_t aModType) final; void OnVisibilityChange( Visibility aNewVisibility, const Maybe& aNonvisibleAction = Nothing()) final; void MarkIntrinsicISizesDirty() override; 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 #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 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(const mozilla::IntrinsicSizeInput& aInput, 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 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, // For a ::view-transition-old or ::view-transition-new pseudo-element. // Which one of the two is determined by the PseudoStyleType applying to us. ViewTransition, }; // Creates a suitable continuing frame for this frame. nsImageFrame* CreateContinuingFrame(mozilla::PresShell*, ComputedStyle*) const; mozilla::AspectRatio ComputeIntrinsicRatioForImage( imgIContainer*, bool aIgnoreContainment = false) 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*); friend nsIFrame* NS_NewImageFrameForViewTransition(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; /** * Populate/update mIntrinsicSize and mIntrinsicSize if necessary. * * @param aConsiderIntrinsicsDirty if true, then this function will update * mIntrinsicSize and mIntrinsicRatio *regardless* of what their current * value is. (We'll still reason about whether the value changed or not * when deciding whether additional notifications are needed.) This param * defaults to false, but it's used in MarkIntrinsicISizesDirty. */ void EnsureIntrinsicSizeAndRatio(bool aConsiderIntrinsicsDirty = false); 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&) const; 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(); 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); mozilla::IntrinsicSize ComputeIntrinsicSize( bool aIgnoreContainment = false) const; // Whether the image frame should use the mapped aspect ratio from width="" // and height="". bool ShouldUseMappedAspectRatio() const; nsAtom* GetViewTransitionName() const; Maybe GetViewTransitionSnapshotSize() const; mozilla::wr::ImageKey GetViewTransitionImageKey( mozilla::layers::RenderRootStateManager*, mozilla::wr::IpcResourceUpdateQueue&) const; /** * Notification that aRequest will now be the current request. */ void NotifyNewCurrentRequest(imgIRequest* aRequest); /// 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 dest rect that we'll draw into, in app units, based upon the * provided frame content box. The result is not necessarily contained in the * frame content box. */ nsRect GetDestRect(const nsRect& aFrameContentBox, nsPoint* aAnchorPoint = nullptr); private: nscoord GetContinuationOffset() const; bool ShouldDisplaySelection(); // 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, Maybe); RefPtr mImageMap; RefPtr mListener; // An image request created for content: url(..), list-style-image, or // . RefPtr mOwnedRequest; nsCOMPtr mImage; nsCOMPtr 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) : nsPaintedDisplayItem(aBuilder, aFrame) { MOZ_COUNT_CTOR(nsDisplayImage); } MOZ_COUNTED_DTOR_FINAL(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; void MaybeCreateWebRenderCommandsForViewTransition( mozilla::wr::DisplayListBuilder&, mozilla::wr::IpcResourceUpdateQueue&, const StackingContextHelper&, mozilla::layers::RenderRootStateManager*, nsDisplayListBuilder*); nsImageFrame* Frame() const { MOZ_ASSERT(mFrame->IsImageFrame() || mFrame->IsImageControlFrame()); return static_cast(mFrame); } NS_DISPLAY_DECL_NAME("Image", TYPE_IMAGE) }; } // namespace mozilla #endif /* nsImageFrame_h___ */