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