nsImageFrame.h (17037B)
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 #ifndef nsImageFrame_h___ 10 #define nsImageFrame_h___ 11 12 #include "imgIContainer.h" 13 #include "imgINotificationObserver.h" 14 #include "mozilla/Attributes.h" 15 #include "mozilla/StaticPtr.h" 16 #include "nsAtomicContainerFrame.h" 17 #include "nsDisplayList.h" 18 #include "nsIObserver.h" 19 #include "nsIReflowCallback.h" 20 #include "nsTObserverArray.h" 21 22 class nsFontMetrics; 23 class nsImageMap; 24 class nsIURI; 25 class nsILoadGroup; 26 class nsPresContext; 27 class nsImageFrame; 28 class nsTransform2D; 29 class nsImageLoadingContent; 30 31 namespace mozilla { 32 class nsDisplayImage; 33 class PresShell; 34 namespace layers { 35 class ImageContainer; 36 class LayerManager; 37 } // namespace layers 38 } // namespace mozilla 39 40 class nsImageListener final : public imgINotificationObserver { 41 protected: 42 virtual ~nsImageListener(); 43 44 public: 45 explicit nsImageListener(nsImageFrame* aFrame); 46 47 NS_DECL_ISUPPORTS 48 NS_DECL_IMGINOTIFICATIONOBSERVER 49 50 void SetFrame(nsImageFrame* frame) { mFrame = frame; } 51 52 private: 53 nsImageFrame* mFrame; 54 }; 55 56 class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback { 57 public: 58 template <typename T> 59 using Maybe = mozilla::Maybe<T>; 60 using Nothing = mozilla::Nothing; 61 using Visibility = mozilla::Visibility; 62 63 typedef mozilla::image::ImgDrawResult ImgDrawResult; 64 typedef mozilla::layers::ImageContainer ImageContainer; 65 typedef mozilla::layers::LayerManager LayerManager; 66 67 NS_DECL_FRAMEARENA_HELPERS(nsImageFrame) 68 NS_DECL_QUERYFRAME 69 70 void Destroy(DestroyContext&) override; 71 void DidSetComputedStyle(ComputedStyle* aOldStyle) final; 72 73 void Init(nsIContent* aContent, nsContainerFrame* aParent, 74 nsIFrame* aPrevInFlow) override; 75 void BuildDisplayList(nsDisplayListBuilder*, const nsDisplayListSet&) final; 76 77 nscoord IntrinsicISize(const mozilla::IntrinsicSizeInput& aInput, 78 mozilla::IntrinsicISizeType aType) final; 79 80 mozilla::IntrinsicSize GetIntrinsicSize() final { return mIntrinsicSize; } 81 mozilla::AspectRatio GetIntrinsicRatio() const final { 82 return mIntrinsicRatio; 83 } 84 void Reflow(nsPresContext*, ReflowOutput&, const ReflowInput&, 85 nsReflowStatus&) override; 86 bool IsLeafDynamic() const override; 87 88 nsIContent* GetContentForEvent(const mozilla::WidgetEvent*) const final; 89 MOZ_CAN_RUN_SCRIPT_BOUNDARY 90 nsresult HandleEvent(nsPresContext*, mozilla::WidgetGUIEvent*, 91 nsEventStatus*) override; 92 Cursor GetCursor(const nsPoint&) override; 93 nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute, 94 AttrModType aModType) final; 95 96 void OnVisibilityChange( 97 Visibility aNewVisibility, 98 const Maybe<OnNonvisible>& aNonvisibleAction = Nothing()) final; 99 100 void MarkIntrinsicISizesDirty() override; 101 102 void ResponsiveContentDensityChanged(); 103 void ElementStateChanged(mozilla::dom::ElementState) override; 104 void SetupOwnedRequest(); 105 void DeinitOwnedRequest(); 106 bool ShouldShowBrokenImageIcon() const; 107 108 bool IsForImageLoadingContent() const { 109 return mKind == Kind::ImageLoadingContent; 110 } 111 112 void UpdateXULImage(); 113 const mozilla::StyleImage* GetImageFromStyle() const; 114 115 #ifdef ACCESSIBILITY 116 mozilla::a11y::AccType AccessibleType() override; 117 #endif 118 119 #ifdef DEBUG_FRAME_DUMP 120 nsresult GetFrameName(nsAString& aResult) const override; 121 void List(FILE* out = stderr, const char* aPrefix = "", 122 ListFlags aFlags = ListFlags()) const final; 123 #endif 124 125 LogicalSides GetLogicalSkipSides() const final; 126 127 static void ReleaseGlobals(); 128 129 already_AddRefed<imgIRequest> GetCurrentRequest() const; 130 void Notify(imgIRequest*, int32_t aType, const nsIntRect* aData); 131 132 /** 133 * Returns whether we should replace an element with an image corresponding to 134 * its 'content' CSS property. 135 */ 136 static bool ShouldCreateImageFrameForContentProperty( 137 const mozilla::dom::Element&, const ComputedStyle&); 138 139 /** 140 * Function to test whether given an element and its style, that element 141 * should get an image frame, and if so, which kind of image frame (for 142 * `content`, or for the element itself). 143 */ 144 enum class ImageFrameType { 145 ForContentProperty, 146 ForElementRequest, 147 None, 148 }; 149 static ImageFrameType ImageFrameTypeFor(const mozilla::dom::Element&, 150 const ComputedStyle&); 151 152 ImgDrawResult DisplayAltFeedback(gfxContext& aRenderingContext, 153 const nsRect& aDirtyRect, nsPoint aPt, 154 uint32_t aFlags); 155 156 ImgDrawResult DisplayAltFeedbackWithoutLayer( 157 nsDisplayItem*, mozilla::wr::DisplayListBuilder&, 158 mozilla::wr::IpcResourceUpdateQueue&, 159 const mozilla::layers::StackingContextHelper&, 160 mozilla::layers::RenderRootStateManager*, nsDisplayListBuilder*, 161 nsPoint aPt, uint32_t aFlags); 162 163 /** 164 * Return a map element associated with this image. 165 */ 166 mozilla::dom::Element* GetMapElement() const; 167 168 /** 169 * Return true if the image has associated image map. 170 */ 171 bool HasImageMap() const { return mImageMap || GetMapElement(); } 172 173 nsImageMap* GetImageMap(); 174 nsImageMap* GetExistingImageMap() const { return mImageMap; } 175 176 void AddInlineMinISize(const mozilla::IntrinsicSizeInput& aInput, 177 InlineMinISizeData* aData) final; 178 179 void DisconnectMap(); 180 181 // nsIReflowCallback 182 bool ReflowFinished() final; 183 void ReflowCallbackCanceled() final; 184 185 // The kind of image frame we are. 186 enum class Kind : uint8_t { 187 // For an nsImageLoadingContent. 188 ImageLoadingContent, 189 // For a <xul:image> element. 190 XULImage, 191 // For css 'content: url(..)' on non-generated content. 192 ContentProperty, 193 // For a child of a ::before / ::after pseudo-element that had an url() item 194 // for the content property. 195 ContentPropertyAtIndex, 196 // For a list-style-image ::marker. 197 ListStyleImage, 198 // For a ::view-transition-old or ::view-transition-new pseudo-element. 199 // Which one of the two is determined by the PseudoStyleType applying to us. 200 ViewTransition, 201 }; 202 203 // Creates a suitable continuing frame for this frame. 204 nsImageFrame* CreateContinuingFrame(mozilla::PresShell*, 205 ComputedStyle*) const; 206 207 mozilla::AspectRatio ComputeIntrinsicRatioForImage( 208 imgIContainer*, bool aIgnoreContainment = false) const; 209 210 private: 211 friend nsIFrame* NS_NewImageFrame(mozilla::PresShell*, ComputedStyle*); 212 friend nsIFrame* NS_NewXULImageFrame(mozilla::PresShell*, ComputedStyle*); 213 friend nsIFrame* NS_NewImageFrameForContentProperty(mozilla::PresShell*, 214 ComputedStyle*); 215 friend nsIFrame* NS_NewImageFrameForGeneratedContentIndex(mozilla::PresShell*, 216 ComputedStyle*); 217 friend nsIFrame* NS_NewImageFrameForListStyleImage(mozilla::PresShell*, 218 ComputedStyle*); 219 friend nsIFrame* NS_NewImageFrameForViewTransition(mozilla::PresShell*, 220 ComputedStyle*); 221 222 nsImageFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, Kind aKind) 223 : nsImageFrame(aStyle, aPresContext, kClassID, aKind) {} 224 225 nsImageFrame(ComputedStyle*, nsPresContext* aPresContext, ClassID, Kind); 226 227 void ReflowChildren(nsPresContext*, const ReflowInput&, 228 const mozilla::LogicalSize& aImageSize); 229 230 void UpdateIntrinsicSizeAndRatio(); 231 232 protected: 233 nsImageFrame(ComputedStyle* aStyle, nsPresContext* aPresContext, ClassID aID) 234 : nsImageFrame(aStyle, aPresContext, aID, Kind::ImageLoadingContent) {} 235 236 ~nsImageFrame() override; 237 238 /** 239 * Populate/update mIntrinsicSize and mIntrinsicSize if necessary. 240 * 241 * @param aConsiderIntrinsicsDirty if true, then this function will update 242 * mIntrinsicSize and mIntrinsicRatio *regardless* of what their current 243 * value is. (We'll still reason about whether the value changed or not 244 * when deciding whether additional notifications are needed.) This param 245 * defaults to false, but it's used in MarkIntrinsicISizesDirty. 246 */ 247 void EnsureIntrinsicSizeAndRatio(bool aConsiderIntrinsicsDirty = false); 248 249 bool GotInitialReflow() const { 250 return !HasAnyStateBits(NS_FRAME_FIRST_REFLOW); 251 } 252 253 SizeComputationResult ComputeSize( 254 const SizeComputationInput& aSizingInput, mozilla::WritingMode aWM, 255 const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize, 256 const mozilla::LogicalSize& aMargin, 257 const mozilla::LogicalSize& aBorderPadding, 258 const mozilla::StyleSizeOverrides& aSizeOverrides, 259 mozilla::ComputeSizeFlags aFlags) final; 260 261 bool IsServerImageMap(); 262 263 // Translate a point that is relative to our frame into a localized CSS pixel 264 // coordinate that is relative to the content area of this frame (inside the 265 // border+padding). 266 mozilla::CSSIntPoint TranslateEventCoords(const nsPoint&) const; 267 268 bool GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget, 269 nsIContent** aNode); 270 /** 271 * Computes the width of the string that fits into the available space 272 * 273 * @param in aLength total length of the string in PRUnichars 274 * @param in aMaxWidth width not to be exceeded 275 * @param out aMaxFit length of the string that fits within aMaxWidth 276 * in PRUnichars 277 * @return width of the string that fits within aMaxWidth 278 */ 279 nscoord MeasureString(const char16_t* aString, int32_t aLength, 280 nscoord aMaxWidth, uint32_t& aMaxFit, 281 gfxContext& aContext, nsFontMetrics& aFontMetrics); 282 283 void DisplayAltText(nsPresContext* aPresContext, 284 gfxContext& aRenderingContext, const nsString& aAltText, 285 const nsRect& aRect); 286 287 ImgDrawResult PaintImage(gfxContext& aRenderingContext, nsPoint aPt, 288 const nsRect& aDirtyRect, imgIContainer* aImage, 289 uint32_t aFlags); 290 291 /** 292 * If we're ready to decode - that is, if our current request's image is 293 * available and our decoding heuristics are satisfied - then trigger a decode 294 * for our image at the size we predict it will be drawn next time it's 295 * painted. 296 */ 297 void MaybeDecodeForPredictedSize(); 298 299 protected: 300 friend class nsImageListener; 301 friend class nsImageLoadingContent; 302 friend class mozilla::PresShell; 303 304 void OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage); 305 void OnFrameUpdate(imgIRequest* aRequest, const nsIntRect* aRect); 306 void OnLoadComplete(imgIRequest* aRequest); 307 mozilla::IntrinsicSize ComputeIntrinsicSize( 308 bool aIgnoreContainment = false) const; 309 // Whether the image frame should use the mapped aspect ratio from width="" 310 // and height="". 311 bool ShouldUseMappedAspectRatio() const; 312 313 nsAtom* GetViewTransitionName() const; 314 Maybe<nsSize> GetViewTransitionBorderBoxSize() const; 315 mozilla::wr::ImageKey GetViewTransitionImageKey( 316 mozilla::layers::RenderRootStateManager*, 317 mozilla::wr::IpcResourceUpdateQueue&) const; 318 319 /** 320 * Notification that aRequest will now be the current request. 321 */ 322 void NotifyNewCurrentRequest(imgIRequest* aRequest); 323 324 /// Always sync decode our image when painting if @aForce is true. 325 void SetForceSyncDecoding(bool aForce) { mForceSyncDecoding = aForce; } 326 327 void AssertSyncDecodingHintIsInSync() const 328 #ifndef DEBUG 329 {} 330 #else 331 ; 332 #endif 333 334 /** 335 * Computes the dest rect that we'll draw into, in app units, based upon the 336 * provided frame content box. The result is not necessarily contained in the 337 * frame content box. 338 */ 339 nsRect GetDestRect(const nsRect& aFrameContentBox, 340 nsPoint* aAnchorPoint = nullptr); 341 342 private: 343 nscoord GetContinuationOffset() const; 344 bool ShouldDisplaySelection(); 345 346 // Recalculate mIntrinsicSize from the image. 347 bool UpdateIntrinsicSize(); 348 349 // Recalculate mIntrinsicRatio from the image. 350 bool UpdateIntrinsicRatio(); 351 352 /** 353 * This function calculates the transform for converting between 354 * source space & destination space. May fail if our image has a 355 * percent-valued or zero-valued height or width. 356 * 357 * @param aTransform The transform object to populate. 358 * 359 * @return whether we succeeded in creating the transform. 360 */ 361 bool GetSourceToDestTransform(nsTransform2D& aTransform); 362 363 /** 364 * Helper function to check whether the request corresponds to a load we don't 365 * care about. Most of the decoder observer methods will bail early if this 366 * returns true. 367 */ 368 bool IsPendingLoad(imgIRequest*) const; 369 370 /** 371 * Updates mImage based on the current image request, and the image passed in 372 * (both can be null), and invalidate layout and paint as needed. 373 */ 374 void UpdateImage(imgIRequest*, imgIContainer*); 375 376 /** 377 * Function to convert a dirty rect in the source image to a dirty 378 * rect for the image frame. 379 */ 380 nsRect SourceRectToDest(const nsIntRect& aRect); 381 382 /** 383 * Triggers invalidation for both our image display item and, if appropriate, 384 * our alt-feedback display item. 385 * 386 * @param aLayerInvalidRect The area to invalidate in layer space. If null, 387 * the entire layer will be invalidated. 388 * @param aFrameInvalidRect The area to invalidate in frame space. If null, 389 * the entire frame will be invalidated. 390 */ 391 void InvalidateSelf(const nsIntRect* aLayerInvalidRect, 392 const nsRect* aFrameInvalidRect); 393 394 void MaybeSendIntrinsicSizeAndRatioToEmbedder(); 395 void MaybeSendIntrinsicSizeAndRatioToEmbedder(Maybe<mozilla::IntrinsicSize>, 396 Maybe<mozilla::AspectRatio>); 397 398 RefPtr<nsImageMap> mImageMap; 399 400 RefPtr<nsImageListener> mListener; 401 402 // An image request created for content: url(..), list-style-image, or 403 // <xul:image>. 404 RefPtr<imgRequestProxy> mOwnedRequest; 405 406 nsCOMPtr<imgIContainer> mImage; 407 nsCOMPtr<imgIContainer> mPrevImage; 408 409 // The content-box size as if we are not fragmented, cached in the most recent 410 // reflow. 411 nsSize mComputedSize; 412 413 mozilla::IntrinsicSize mIntrinsicSize; 414 415 // Stores mImage's intrinsic ratio, or a default AspectRatio if there's no 416 // intrinsic ratio. 417 mozilla::AspectRatio mIntrinsicRatio; 418 419 const Kind mKind; 420 bool mOwnedRequestRegistered = false; 421 bool mDisplayingIcon = false; 422 bool mFirstFrameComplete = false; 423 bool mReflowCallbackPosted = false; 424 bool mForceSyncDecoding = false; 425 bool mIsInObjectOrEmbed = false; 426 427 public: 428 friend class mozilla::nsDisplayImage; 429 friend class nsDisplayGradient; 430 }; 431 432 namespace mozilla { 433 /** 434 * Note that nsDisplayImage does not receive events. However, an image element 435 * is replaced content so its background will be z-adjacent to the 436 * image itself, and hence receive events just as if the image itself 437 * received events. 438 */ 439 class nsDisplayImage final : public nsPaintedDisplayItem { 440 public: 441 typedef mozilla::layers::LayerManager LayerManager; 442 443 nsDisplayImage(nsDisplayListBuilder* aBuilder, nsImageFrame* aFrame) 444 : nsPaintedDisplayItem(aBuilder, aFrame) { 445 MOZ_COUNT_CTOR(nsDisplayImage); 446 } 447 448 MOZ_COUNTED_DTOR_FINAL(nsDisplayImage) 449 450 void Paint(nsDisplayListBuilder*, gfxContext* aCtx) final; 451 452 /** 453 * @return The dest rect we'll use when drawing this image, in app units. 454 * Not necessarily contained in this item's bounds. 455 */ 456 nsRect GetDestRect() const; 457 nsRect GetDestRectViewTransition() const; 458 459 nsRect GetBounds(bool* aSnap) const { 460 *aSnap = true; 461 return Frame()->InkOverflowRectRelativeToSelf() + ToReferenceFrame(); 462 } 463 464 nsRect GetBounds(nsDisplayListBuilder*, bool* aSnap) const final { 465 return GetBounds(aSnap); 466 } 467 468 nsRegion GetOpaqueRegion(nsDisplayListBuilder*, bool* aSnap) const final; 469 470 bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder&, 471 mozilla::wr::IpcResourceUpdateQueue&, 472 const StackingContextHelper&, 473 mozilla::layers::RenderRootStateManager*, 474 nsDisplayListBuilder*) final; 475 476 void MaybeCreateWebRenderCommandsForViewTransition( 477 mozilla::wr::DisplayListBuilder&, mozilla::wr::IpcResourceUpdateQueue&, 478 const StackingContextHelper&, mozilla::layers::RenderRootStateManager*, 479 nsDisplayListBuilder*); 480 481 nsImageFrame* Frame() const { 482 MOZ_ASSERT(mFrame->IsImageFrame() || mFrame->IsImageControlFrame()); 483 return static_cast<nsImageFrame*>(mFrame); 484 } 485 486 NS_DISPLAY_DECL_NAME("Image", TYPE_IMAGE) 487 }; 488 489 } // namespace mozilla 490 491 #endif /* nsImageFrame_h___ */