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