1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 /* rendering object for replaced elements with image data */
7 
8 #include "nsImageFrame.h"
9 
10 #include "gfx2DGlue.h"
11 #include "gfxUtils.h"
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/EventStates.h"
14 #include "mozilla/gfx/2D.h"
15 #include "mozilla/gfx/Helpers.h"
16 #include "mozilla/gfx/PathHelpers.h"
17 #include "mozilla/MouseEvents.h"
18 #include "mozilla/Unused.h"
19 
20 #include "nsCOMPtr.h"
21 #include "nsFontMetrics.h"
22 #include "nsIImageLoadingContent.h"
23 #include "nsString.h"
24 #include "nsPrintfCString.h"
25 #include "nsPresContext.h"
26 #include "nsRenderingContext.h"
27 #include "nsIPresShell.h"
~nsILineIterator()28 #include "nsGkAtoms.h"
29 #include "nsIDocument.h"
30 #include "nsContentUtils.h"
31 #include "nsCSSAnonBoxes.h"
32 #include "nsStyleContext.h"
33 #include "nsStyleConsts.h"
34 #include "nsStyleCoord.h"
35 #include "nsStyleUtil.h"
36 #include "nsTransform2D.h"
37 #include "nsImageMap.h"
38 #include "nsIIOService.h"
39 #include "nsILoadGroup.h"
40 #include "nsISupportsPriority.h"
41 #include "nsNetUtil.h"
42 #include "nsNetCID.h"
43 #include "nsCSSRendering.h"
44 #include "nsIDOMHTMLAnchorElement.h"
45 #include "nsNameSpaceManager.h"
46 #include <algorithm>
47 #ifdef ACCESSIBILITY
48 #include "nsAccessibilityService.h"
49 #endif
50 #include "nsIDOMNode.h"
51 #include "nsLayoutUtils.h"
52 #include "nsDisplayList.h"
53 #include "nsIContent.h"
54 #include "nsIDocument.h"
55 #include "FrameLayerBuilder.h"
56 #include "nsISelectionController.h"
57 #include "nsISelection.h"
58 
59 #include "imgIContainer.h"
60 #include "imgLoader.h"
61 #include "imgRequestProxy.h"
62 
63 #include "nsCSSFrameConstructor.h"
64 #include "nsIDOMRange.h"
65 
66 #include "nsError.h"
67 #include "nsBidiUtils.h"
68 #include "nsBidiPresUtils.h"
69 
70 #include "gfxRect.h"
71 #include "ImageLayers.h"
72 #include "ImageContainer.h"
73 #include "mozilla/StyleSetHandle.h"
74 #include "mozilla/StyleSetHandleInlines.h"
75 #include "nsBlockFrame.h"
76 #include "nsStyleStructInlines.h"
77 
78 #include "mozilla/Preferences.h"
79 
80 #include "mozilla/dom/Link.h"
81 
82 using namespace mozilla;
83 using namespace mozilla::dom;
84 using namespace mozilla::gfx;
85 using namespace mozilla::image;
86 using namespace mozilla::layers;
87 
88 // sizes (pixels) for image icon, padding and border frame
89 #define ICON_SIZE        (16)
90 #define ICON_PADDING     (3)
91 #define ALT_BORDER_WIDTH (1)
92 
93 //we must add hooks soon
94 #define IMAGE_EDITOR_CHECK 1
95 
96 // Default alignment value (so we can tell an unset value from a set value)
97 #define ALIGN_UNSET uint8_t(-1)
nsAutoLineIterator()98 
99 // static icon information
100 nsImageFrame::IconLoad* nsImageFrame::gIconLoad = nullptr;
~nsAutoLineIterator()101 
102 // cached IO service for loading icons
103 nsIIOService* nsImageFrame::sIOService;
104 
105 // test if the width and height are fixed, looking at the style data
106 // This is used by nsImageFrame::ShouldCreateImageFrameFor and should
107 // not be used for layout decisions.
108 static bool HaveSpecifiedSize(const nsStylePosition* aStylePosition)
109 {
110   // check the width and height values in the reflow state's style struct
111   // - if width and height are specified as either coord or percentage, then
112   //   the size of the image frame is constrained
113   return aStylePosition->mWidth.IsCoordPercentCalcUnit() &&
114          aStylePosition->mHeight.IsCoordPercentCalcUnit();
115 }
116 
117 // Decide whether we can optimize away reflows that result from the
118 // image's intrinsic size changing.
119 inline bool HaveFixedSize(const ReflowInput& aReflowInput)
120 {
121   NS_ASSERTION(aReflowInput.mStylePosition, "crappy reflowInput - null stylePosition");
122   // Don't try to make this optimization when an image has percentages
123   // in its 'width' or 'height'.  The percentages might be treated like
124   // auto (especially for intrinsic width calculations and for heights).
125   return aReflowInput.mStylePosition->mHeight.ConvertsToLength() &&
126          aReflowInput.mStylePosition->mWidth.ConvertsToLength();
127 }
128 
129 nsIFrame*
130 NS_NewImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
131 {
132   return new (aPresShell) nsImageFrame(aContext);
133 }
134 
135 NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame)
136 
137 
138 nsImageFrame::nsImageFrame(nsStyleContext* aContext) :
139   nsAtomicContainerFrame(aContext),
140   mComputedSize(0, 0),
141   mIntrinsicRatio(0, 0),
142   mDisplayingIcon(false),
143   mFirstFrameComplete(false),
144   mReflowCallbackPosted(false),
145   mForceSyncDecoding(false)
146 {
147   EnableVisibilityTracking();
148 
149   // We assume our size is not constrained and we haven't gotten an
150   // initial reflow yet, so don't touch those flags.
151   mIntrinsicSize.width.SetCoordValue(0);
152   mIntrinsicSize.height.SetCoordValue(0);
153 }
154 
155 nsImageFrame::~nsImageFrame()
156 {
157 }
158 
159 NS_QUERYFRAME_HEAD(nsImageFrame)
160   NS_QUERYFRAME_ENTRY(nsImageFrame)
161 NS_QUERYFRAME_TAIL_INHERITING(nsAtomicContainerFrame)
162 
163 #ifdef ACCESSIBILITY
164 a11y::AccType
165 nsImageFrame::AccessibleType()
166 {
167   // Don't use GetImageMap() to avoid reentrancy into accessibility.
168   if (HasImageMap()) {
169     return a11y::eHTMLImageMapType;
170   }
171 
172   return a11y::eImageType;
173 }
174 #endif
175 
176 void
177 nsImageFrame::DisconnectMap()
178 {
179   if (mImageMap) {
180     mImageMap->Destroy();
181     mImageMap = nullptr;
182 
183 #ifdef ACCESSIBILITY
184   nsAccessibilityService* accService = GetAccService();
185   if (accService) {
186     accService->RecreateAccessible(PresContext()->PresShell(), mContent);
187   }
188 #endif
189   }
190 }
191 
192 void
193 nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
194 {
195   if (mReflowCallbackPosted) {
196     PresContext()->PresShell()->CancelReflowCallback(this);
197     mReflowCallbackPosted = false;
198   }
199 
200   // Tell our image map, if there is one, to clean up
201   // This causes the nsImageMap to unregister itself as
202   // a DOM listener.
203   DisconnectMap();
204 
205   // set the frame to null so we don't send messages to a dead object.
206   if (mListener) {
207     nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
208     if (imageLoader) {
209       // Notify our image loading content that we are going away so it can
210       // deregister with our refresh driver.
211       imageLoader->FrameDestroyed(this);
212 
213       imageLoader->RemoveObserver(mListener);
214     }
215 
216     reinterpret_cast<nsImageListener*>(mListener.get())->SetFrame(nullptr);
217   }
218 
219   mListener = nullptr;
220 
221   // If we were displaying an icon, take ourselves off the list
222   if (mDisplayingIcon)
223     gIconLoad->RemoveIconObserver(this);
224 
225   nsAtomicContainerFrame::DestroyFrom(aDestructRoot);
226 }
227 
228 void
229 nsImageFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
230 {
231   nsAtomicContainerFrame::DidSetStyleContext(aOldStyleContext);
232 
233   if (!mImage) {
234     // We'll pick this change up whenever we do get an image.
235     return;
236   }
237 
238   nsStyleImageOrientation newOrientation = StyleVisibility()->mImageOrientation;
239 
240   // We need to update our orientation either if we had no style context before
241   // because this is the first time it's been set, or if the image-orientation
242   // property changed from its previous value.
243   bool shouldUpdateOrientation =
244     !aOldStyleContext ||
245     aOldStyleContext->StyleVisibility()->mImageOrientation != newOrientation;
246 
247   if (shouldUpdateOrientation) {
248     nsCOMPtr<imgIContainer> image(mImage->Unwrap());
249     mImage = nsLayoutUtils::OrientImage(image, newOrientation);
250 
251     UpdateIntrinsicSize(mImage);
252     UpdateIntrinsicRatio(mImage);
253   }
254 }
255 
256 void
257 nsImageFrame::Init(nsIContent*       aContent,
258                    nsContainerFrame* aParent,
259                    nsIFrame*         aPrevInFlow)
260 {
261   nsAtomicContainerFrame::Init(aContent, aParent, aPrevInFlow);
262 
263   mListener = new nsImageListener(this);
264 
265   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aContent);
266   if (!imageLoader) {
267     NS_RUNTIMEABORT("Why do we have an nsImageFrame here at all?");
268   }
269 
270   imageLoader->AddObserver(mListener);
271 
272   nsPresContext *aPresContext = PresContext();
273 
274   if (!gIconLoad)
275     LoadIcons(aPresContext);
276 
277   // We have a PresContext now, so we need to notify the image content node
278   // that it can register images.
279   imageLoader->FrameCreated(this);
280 
281   // Give image loads associated with an image frame a small priority boost!
282   nsCOMPtr<imgIRequest> currentRequest;
283   imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
284                           getter_AddRefs(currentRequest));
285   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(currentRequest);
286   if (p)
287     p->AdjustPriority(-1);
288 }
289 
290 bool
291 nsImageFrame::UpdateIntrinsicSize(imgIContainer* aImage)
292 {
293   NS_PRECONDITION(aImage, "null image");
294   if (!aImage)
295     return false;
296 
297   IntrinsicSize oldIntrinsicSize = mIntrinsicSize;
298   mIntrinsicSize = IntrinsicSize();
299 
300   // Set intrinsic size to match aImage's reported intrinsic width & height.
301   nsSize intrinsicSize;
302   if (NS_SUCCEEDED(aImage->GetIntrinsicSize(&intrinsicSize))) {
303     // If the image has no intrinsic width, intrinsicSize.width will be -1, and
304     // we can leave mIntrinsicSize.width at its default value of eStyleUnit_None.
305     // Otherwise we use intrinsicSize.width. Height works the same way.
306     if (intrinsicSize.width != -1)
307       mIntrinsicSize.width.SetCoordValue(intrinsicSize.width);
308     if (intrinsicSize.height != -1)
309       mIntrinsicSize.height.SetCoordValue(intrinsicSize.height);
310   } else {
311     // Failure means that the image hasn't loaded enough to report a result. We
312     // treat this case as if the image's intrinsic size was 0x0.
313     mIntrinsicSize.width.SetCoordValue(0);
314     mIntrinsicSize.height.SetCoordValue(0);
315   }
316 
317   return mIntrinsicSize != oldIntrinsicSize;
318 }
319 
320 bool
321 nsImageFrame::UpdateIntrinsicRatio(imgIContainer* aImage)
322 {
323   NS_PRECONDITION(aImage, "null image");
324 
325   if (!aImage)
326     return false;
327 
328   nsSize oldIntrinsicRatio = mIntrinsicRatio;
329 
330   // Set intrinsic ratio to match aImage's reported intrinsic ratio.
331   if (NS_FAILED(aImage->GetIntrinsicRatio(&mIntrinsicRatio)))
332     mIntrinsicRatio.SizeTo(0, 0);
333 
334   return mIntrinsicRatio != oldIntrinsicRatio;
335 }
336 
337 bool
338 nsImageFrame::GetSourceToDestTransform(nsTransform2D& aTransform)
339 {
340   // First, figure out destRect (the rect we're rendering into).
341   // NOTE: We use mComputedSize instead of just GetInnerArea()'s own size here,
342   // because GetInnerArea() might be smaller if we're fragmented, whereas
343   // mComputedSize has our full content-box size (which we need for
344   // ComputeObjectDestRect to work correctly).
345   nsRect constraintRect(GetInnerArea().TopLeft(), mComputedSize);
346   constraintRect.y -= GetContinuationOffset();
347 
348   nsRect destRect = nsLayoutUtils::ComputeObjectDestRect(constraintRect,
349                                                          mIntrinsicSize,
350                                                          mIntrinsicRatio,
351                                                          StylePosition());
352   // Set the translation components, based on destRect
353   // XXXbz does this introduce rounding errors because of the cast to
354   // float?  Should we just manually add that stuff in every time
355   // instead?
356   aTransform.SetToTranslate(float(destRect.x),
357                             float(destRect.y));
358 
359   // Set the scale factors, based on destRect and intrinsic size.
360   if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
361       mIntrinsicSize.width.GetCoordValue() != 0 &&
362       mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
363       mIntrinsicSize.height.GetCoordValue() != 0 &&
364       mIntrinsicSize.width.GetCoordValue() != destRect.width &&
365       mIntrinsicSize.height.GetCoordValue() != destRect.height) {
366 
367     aTransform.SetScale(float(destRect.width)  /
368                         float(mIntrinsicSize.width.GetCoordValue()),
369                         float(destRect.height) /
370                         float(mIntrinsicSize.height.GetCoordValue()));
371     return true;
372   }
373 
374   return false;
375 }
376 
377 // This function checks whether the given request is the current request for our
378 // mContent.
379 bool
380 nsImageFrame::IsPendingLoad(imgIRequest* aRequest) const
381 {
382   // Default to pending load in case of errors
383   nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
384   NS_ASSERTION(imageLoader, "No image loading content?");
385 
386   int32_t requestType = nsIImageLoadingContent::UNKNOWN_REQUEST;
387   imageLoader->GetRequestType(aRequest, &requestType);
388 
389   return requestType != nsIImageLoadingContent::CURRENT_REQUEST;
390 }
391 
392 nsRect
393 nsImageFrame::SourceRectToDest(const nsIntRect& aRect)
394 {
395   // When scaling the image, row N of the source image may (depending on
396   // the scaling function) be used to draw any row in the destination image
397   // between floor(F * (N-1)) and ceil(F * (N+1)), where F is the
398   // floating-point scaling factor.  The same holds true for columns.
399   // So, we start by computing that bound without the floor and ceiling.
400 
401   nsRect r(nsPresContext::CSSPixelsToAppUnits(aRect.x - 1),
402            nsPresContext::CSSPixelsToAppUnits(aRect.y - 1),
403            nsPresContext::CSSPixelsToAppUnits(aRect.width + 2),
404            nsPresContext::CSSPixelsToAppUnits(aRect.height + 2));
405 
406   nsTransform2D sourceToDest;
407   if (!GetSourceToDestTransform(sourceToDest)) {
408     // Failed to generate transform matrix. Return our whole inner area,
409     // to be on the safe side (since this method is used for generating
410     // invalidation rects).
411     return GetInnerArea();
412   }
413 
414   sourceToDest.TransformCoord(&r.x, &r.y, &r.width, &r.height);
415 
416   // Now, round the edges out to the pixel boundary.
417   nscoord scale = nsPresContext::CSSPixelsToAppUnits(1);
418   nscoord right = r.x + r.width;
419   nscoord bottom = r.y + r.height;
420 
421   r.x -= (scale + (r.x % scale)) % scale;
422   r.y -= (scale + (r.y % scale)) % scale;
423   r.width = right + ((scale - (right % scale)) % scale) - r.x;
424   r.height = bottom + ((scale - (bottom % scale)) % scale) - r.y;
425 
426   return r;
427 }
428 
429 // Note that we treat NS_EVENT_STATE_SUPPRESSED images as "OK".  This means
430 // that we'll construct image frames for them as needed if their display is
431 // toggled from "none" (though we won't paint them, unless their visibility
432 // is changed too).
433 #define BAD_STATES (NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED | \
434                     NS_EVENT_STATE_LOADING)
435 
436 // This is a macro so that we don't evaluate the boolean last arg
437 // unless we have to; it can be expensive
438 #define IMAGE_OK(_state, _loadingOK)                                           \
439    (!(_state).HasAtLeastOneOfStates(BAD_STATES) ||                                    \
440     (!(_state).HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED) && \
441      (_state).HasState(NS_EVENT_STATE_LOADING) && (_loadingOK)))
442 
443 /* static */
444 bool
445 nsImageFrame::ShouldCreateImageFrameFor(Element* aElement,
446                                         nsStyleContext* aStyleContext)
447 {
448   EventStates state = aElement->State();
449   if (IMAGE_OK(state,
450                HaveSpecifiedSize(aStyleContext->StylePosition()))) {
451     // Image is fine; do the image frame thing
452     return true;
453   }
454 
455   // Check if we want to use a placeholder box with an icon or just
456   // let the presShell make us into inline text.  Decide as follows:
457   //
458   //  - if our special "force icons" style is set, show an icon
459   //  - else if our "do not show placeholders" pref is set, skip the icon
460   //  - else:
461   //  - if there is a src attribute, there is no alt attribute,
462   //    and this is not an <object> (which could not possibly have
463   //    such an attribute), show an icon.
464   //  - if QuirksMode, and the IMG has a size show an icon.
465   //  - otherwise, skip the icon
466   bool useSizedBox;
467 
468   if (aStyleContext->StyleUIReset()->mForceBrokenImageIcon) {
469     useSizedBox = true;
470   }
471   else if (gIconLoad && gIconLoad->mPrefForceInlineAltText) {
472     useSizedBox = false;
473   }
474   else if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
475            !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::alt) &&
476            !aElement->IsHTMLElement(nsGkAtoms::object) &&
477            !aElement->IsHTMLElement(nsGkAtoms::input)) {
478     // Use a sized box if we have no alt text.  This means no alt attribute
479     // and the node is not an object or an input (since those always have alt
480     // text).
481     useSizedBox = true;
482   }
483   else if (aStyleContext->PresContext()->CompatibilityMode() !=
484            eCompatibility_NavQuirks) {
485     useSizedBox = false;
486   }
487   else {
488     // check whether we have specified size
489     useSizedBox = HaveSpecifiedSize(aStyleContext->StylePosition());
490   }
491 
492   return useSizedBox;
493 }
494 
495 nsresult
496 nsImageFrame::Notify(imgIRequest* aRequest,
497                      int32_t aType,
498                      const nsIntRect* aRect)
499 {
500   if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
501     nsCOMPtr<imgIContainer> image;
502     aRequest->GetImage(getter_AddRefs(image));
503     return OnSizeAvailable(aRequest, image);
504   }
505 
506   if (aType == imgINotificationObserver::FRAME_UPDATE) {
507     return OnFrameUpdate(aRequest, aRect);
508   }
509 
510   if (aType == imgINotificationObserver::FRAME_COMPLETE) {
511     mFirstFrameComplete = true;
512   }
513 
514   if (aType == imgINotificationObserver::LOAD_COMPLETE) {
515     uint32_t imgStatus;
516     aRequest->GetImageStatus(&imgStatus);
517     nsresult status =
518         imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
519     return OnLoadComplete(aRequest, status);
520   }
521 
522   return NS_OK;
523 }
524 
525 static bool
526 SizeIsAvailable(imgIRequest* aRequest)
527 {
528   if (!aRequest)
529     return false;
530 
531   uint32_t imageStatus = 0;
532   nsresult rv = aRequest->GetImageStatus(&imageStatus);
533 
534   return NS_SUCCEEDED(rv) && (imageStatus & imgIRequest::STATUS_SIZE_AVAILABLE);
535 }
536 
537 nsresult
538 nsImageFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
539 {
540   if (!aImage) return NS_ERROR_INVALID_ARG;
541 
542   /* Get requested animation policy from the pres context:
543    *   normal = 0
544    *   one frame = 1
545    *   one loop = 2
546    */
547   nsPresContext *presContext = PresContext();
548   aImage->SetAnimationMode(presContext->ImageAnimationMode());
549 
550   if (IsPendingLoad(aRequest)) {
551     // We don't care
552     return NS_OK;
553   }
554 
555   bool intrinsicSizeChanged = false;
556   if (SizeIsAvailable(aRequest)) {
557     // This is valid and for the current request, so update our stored image
558     // container, orienting according to our style.
559     mImage = nsLayoutUtils::OrientImage(aImage, StyleVisibility()->mImageOrientation);
560 
561     intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
562     intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
563   } else {
564     // We no longer have a valid image, so release our stored image container.
565     mImage = mPrevImage = nullptr;
566 
567     // Have to size to 0,0 so that GetDesiredSize recalculates the size.
568     mIntrinsicSize.width.SetCoordValue(0);
569     mIntrinsicSize.height.SetCoordValue(0);
570     mIntrinsicRatio.SizeTo(0, 0);
571     intrinsicSizeChanged = true;
572   }
573 
574   if (intrinsicSizeChanged && (mState & IMAGE_GOTINITIALREFLOW)) {
575     // Now we need to reflow if we have an unconstrained size and have
576     // already gotten the initial reflow
577     if (!(mState & IMAGE_SIZECONSTRAINED)) {
578       nsIPresShell *presShell = presContext->GetPresShell();
579       NS_ASSERTION(presShell, "No PresShell.");
580       if (presShell) {
581         presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
582                                     NS_FRAME_IS_DIRTY);
583       }
584     } else {
585       // We've already gotten the initial reflow, and our size hasn't changed,
586       // so we're ready to request a decode.
587       MaybeDecodeForPredictedSize();
588     }
589 
590     mPrevImage = nullptr;
591   }
592 
593   return NS_OK;
594 }
595 
596 nsresult
597 nsImageFrame::OnFrameUpdate(imgIRequest* aRequest, const nsIntRect* aRect)
598 {
599   NS_ENSURE_ARG_POINTER(aRect);
600 
601   if (!(mState & IMAGE_GOTINITIALREFLOW)) {
602     // Don't bother to do anything; we have a reflow coming up!
603     return NS_OK;
604   }
605 
606   if (mFirstFrameComplete && !StyleVisibility()->IsVisible()) {
607     return NS_OK;
608   }
609 
610   if (IsPendingLoad(aRequest)) {
611     // We don't care
612     return NS_OK;
613   }
614 
615   nsIntRect layerInvalidRect = mImage
616                              ? mImage->GetImageSpaceInvalidationRect(*aRect)
617                              : *aRect;
618 
619   if (layerInvalidRect.IsEqualInterior(GetMaxSizedIntRect())) {
620     // Invalidate our entire area.
621     InvalidateSelf(nullptr, nullptr);
622     return NS_OK;
623   }
624 
625   nsRect frameInvalidRect = SourceRectToDest(layerInvalidRect);
626   InvalidateSelf(&layerInvalidRect, &frameInvalidRect);
627   return NS_OK;
628 }
629 
630 void
631 nsImageFrame::InvalidateSelf(const nsIntRect* aLayerInvalidRect,
632                              const nsRect* aFrameInvalidRect)
633 {
634   InvalidateLayer(nsDisplayItem::TYPE_IMAGE,
635                   aLayerInvalidRect,
636                   aFrameInvalidRect);
637 
638   if (!mFirstFrameComplete) {
639     InvalidateLayer(nsDisplayItem::TYPE_ALT_FEEDBACK,
640                     aLayerInvalidRect,
641                     aFrameInvalidRect);
642   }
643 }
644 
645 nsresult
646 nsImageFrame::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus)
647 {
648   // Check what request type we're dealing with
649   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
650   NS_ASSERTION(imageLoader, "Who's notifying us??");
651   int32_t loadType = nsIImageLoadingContent::UNKNOWN_REQUEST;
652   imageLoader->GetRequestType(aRequest, &loadType);
653   if (loadType != nsIImageLoadingContent::CURRENT_REQUEST &&
654       loadType != nsIImageLoadingContent::PENDING_REQUEST) {
655     return NS_ERROR_FAILURE;
656   }
657 
658   NotifyNewCurrentRequest(aRequest, aStatus);
659   return NS_OK;
660 }
661 
662 void
663 nsImageFrame::NotifyNewCurrentRequest(imgIRequest *aRequest,
664                                       nsresult aStatus)
665 {
666   nsCOMPtr<imgIContainer> image;
667   aRequest->GetImage(getter_AddRefs(image));
668   NS_ASSERTION(image || NS_FAILED(aStatus), "Successful load with no container?");
669 
670   // May have to switch sizes here!
671   bool intrinsicSizeChanged = true;
672   if (NS_SUCCEEDED(aStatus) && image && SizeIsAvailable(aRequest)) {
673     // Update our stored image container, orienting according to our style.
674     mImage = nsLayoutUtils::OrientImage(image, StyleVisibility()->mImageOrientation);
675 
676     intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
677     intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
678   } else {
679     // We no longer have a valid image, so release our stored image container.
680     mImage = mPrevImage = nullptr;
681 
682     // Have to size to 0,0 so that GetDesiredSize recalculates the size
683     mIntrinsicSize.width.SetCoordValue(0);
684     mIntrinsicSize.height.SetCoordValue(0);
685     mIntrinsicRatio.SizeTo(0, 0);
686   }
687 
688   if (mState & IMAGE_GOTINITIALREFLOW) { // do nothing if we haven't gotten the initial reflow yet
689     if (intrinsicSizeChanged) {
690       if (!(mState & IMAGE_SIZECONSTRAINED)) {
691         nsIPresShell *presShell = PresContext()->GetPresShell();
692         if (presShell) {
693           presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
694                                       NS_FRAME_IS_DIRTY);
695         }
696       } else {
697         // We've already gotten the initial reflow, and our size hasn't changed,
698         // so we're ready to request a decode.
699         MaybeDecodeForPredictedSize();
700       }
701 
702       mPrevImage = nullptr;
703     }
704     // Update border+content to account for image change
705     InvalidateFrame();
706   }
707 }
708 
709 void
710 nsImageFrame::MaybeDecodeForPredictedSize()
711 {
712   // Check that we're ready to decode.
713   if (!mImage) {
714     return;  // Nothing to do yet.
715   }
716 
717   if (mComputedSize.IsEmpty()) {
718     return;  // We won't draw anything, so no point in decoding.
719   }
720 
721   if (GetVisibility() != Visibility::APPROXIMATELY_VISIBLE) {
722     return;  // We're not visible, so don't decode.
723   }
724 
725   // OK, we're ready to decode. Compute the scale to the screen...
726   nsIPresShell* presShell = PresContext()->GetPresShell();
727   LayoutDeviceToScreenScale2D resolutionToScreen(
728       presShell->GetCumulativeResolution()
729     * nsLayoutUtils::GetTransformToAncestorScaleExcludingAnimated(this));
730 
731   // ...and this frame's content box...
732   const nsPoint offset =
733     GetOffsetToCrossDoc(nsLayoutUtils::GetReferenceFrame(this));
734   const nsRect frameContentBox = GetInnerArea() + offset;
735 
736   // ...and our predicted dest rect...
737   const int32_t factor = PresContext()->AppUnitsPerDevPixel();
738   const LayoutDeviceRect destRect =
739     LayoutDeviceRect::FromAppUnits(PredictedDestRect(frameContentBox), factor);
740 
741   // ...and use them to compute our predicted size in screen pixels.
742   const ScreenSize predictedScreenSize = destRect.Size() * resolutionToScreen;
743   const ScreenIntSize predictedScreenIntSize = RoundedToInt(predictedScreenSize);
744   if (predictedScreenIntSize.IsEmpty()) {
745     return;
746   }
747 
748   // Determine the optimal image size to use.
749   uint32_t flags = imgIContainer::FLAG_HIGH_QUALITY_SCALING
750                  | imgIContainer::FLAG_ASYNC_NOTIFY;
751   SamplingFilter samplingFilter =
752     nsLayoutUtils::GetSamplingFilterForFrame(this);
753   gfxSize gfxPredictedScreenSize = gfxSize(predictedScreenIntSize.width,
754                                            predictedScreenIntSize.height);
755   nsIntSize predictedImageSize =
756     mImage->OptimalImageSizeForDest(gfxPredictedScreenSize,
757                                     imgIContainer::FRAME_CURRENT,
758                                     samplingFilter, flags);
759 
760   // Request a decode.
761   mImage->RequestDecodeForSize(predictedImageSize, flags);
762 }
763 
764 nsRect
765 nsImageFrame::PredictedDestRect(const nsRect& aFrameContentBox)
766 {
767   // Note: To get the "dest rect", we have to provide the "constraint rect"
768   // (which is the content-box, with the effects of fragmentation undone).
769   nsRect constraintRect(aFrameContentBox.TopLeft(), mComputedSize);
770   constraintRect.y -= GetContinuationOffset();
771 
772   return nsLayoutUtils::ComputeObjectDestRect(constraintRect,
773                                               mIntrinsicSize,
774                                               mIntrinsicRatio,
775                                               StylePosition());
776 }
777 
778 void
779 nsImageFrame::EnsureIntrinsicSizeAndRatio()
780 {
781   // If mIntrinsicSize.width and height are 0, then we need to update from the
782   // image container.
783   if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
784       mIntrinsicSize.width.GetCoordValue() == 0 &&
785       mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
786       mIntrinsicSize.height.GetCoordValue() == 0) {
787 
788     if (mImage) {
789       UpdateIntrinsicSize(mImage);
790       UpdateIntrinsicRatio(mImage);
791     } else {
792       // image request is null or image size not known, probably an
793       // invalid image specified
794       if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
795         bool imageBroken = false;
796         // check for broken images. valid null images (eg. img src="") are
797         // not considered broken because they have no image requests
798         nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
799         if (imageLoader) {
800           nsCOMPtr<imgIRequest> currentRequest;
801           imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
802                                   getter_AddRefs(currentRequest));
803           uint32_t imageStatus;
804           imageBroken =
805             currentRequest &&
806             NS_SUCCEEDED(currentRequest->GetImageStatus(&imageStatus)) &&
807             (imageStatus & imgIRequest::STATUS_ERROR);
808         }
809         // invalid image specified. make the image big enough for the "broken" icon
810         if (imageBroken) {
811           nscoord edgeLengthToUse =
812             nsPresContext::CSSPixelsToAppUnits(
813               ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH)));
814           mIntrinsicSize.width.SetCoordValue(edgeLengthToUse);
815           mIntrinsicSize.height.SetCoordValue(edgeLengthToUse);
816           mIntrinsicRatio.SizeTo(1, 1);
817         }
818       }
819     }
820   }
821 }
822 
823 /* virtual */
824 LogicalSize
825 nsImageFrame::ComputeSize(nsRenderingContext *aRenderingContext,
826                           WritingMode aWM,
827                           const LogicalSize& aCBSize,
828                           nscoord aAvailableISize,
829                           const LogicalSize& aMargin,
830                           const LogicalSize& aBorder,
831                           const LogicalSize& aPadding,
832                           ComputeSizeFlags aFlags)
833 {
834   EnsureIntrinsicSizeAndRatio();
835 
836   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
837   NS_ASSERTION(imageLoader, "No content node??");
838   mozilla::IntrinsicSize intrinsicSize(mIntrinsicSize);
839 
840   // XXX(seth): We may sometimes find ourselves in the situation where we have
841   // mImage, but imageLoader's current request does not have a size yet.
842   // This can happen when we load an image speculatively from cache, it fails
843   // to validate, and the new image load hasn't fired SIZE_AVAILABLE yet. In
844   // this situation we should always use mIntrinsicSize, because
845   // GetNaturalWidth/Height will return 0, so we check CurrentRequestHasSize()
846   // below. See bug 1019840. We will fix this in bug 1141395.
847 
848   // Content may override our default dimensions. This is termed as overriding
849   // the intrinsic size by the spec, but all other consumers of mIntrinsic*
850   // values are being used to refer to the real/true size of the image data.
851   if (imageLoader && imageLoader->CurrentRequestHasSize() && mImage &&
852       intrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
853       intrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
854     uint32_t width;
855     uint32_t height;
856     if (NS_SUCCEEDED(imageLoader->GetNaturalWidth(&width)) &&
857         NS_SUCCEEDED(imageLoader->GetNaturalHeight(&height))) {
858       nscoord appWidth = nsPresContext::CSSPixelsToAppUnits((int32_t)width);
859       nscoord appHeight = nsPresContext::CSSPixelsToAppUnits((int32_t)height);
860       // If this image is rotated, we'll need to transpose the natural
861       // width/height.
862       bool coordFlip;
863       if (StyleVisibility()->mImageOrientation.IsFromImage()) {
864         coordFlip = mImage->GetOrientation().SwapsWidthAndHeight();
865       } else {
866         coordFlip = StyleVisibility()->mImageOrientation.SwapsWidthAndHeight();
867       }
868       intrinsicSize.width.SetCoordValue(coordFlip ? appHeight : appWidth);
869       intrinsicSize.height.SetCoordValue(coordFlip ? appWidth : appHeight);
870     }
871   }
872 
873   return ComputeSizeWithIntrinsicDimensions(aRenderingContext, aWM,
874                                             intrinsicSize, mIntrinsicRatio,
875                                             aCBSize, aMargin, aBorder, aPadding,
876                                             aFlags);
877 }
878 
879 // XXXdholbert This function's clients should probably just be calling
880 // GetContentRectRelativeToSelf() directly.
881 nsRect
882 nsImageFrame::GetInnerArea() const
883 {
884   return GetContentRectRelativeToSelf();
885 }
886 
887 Element*
888 nsImageFrame::GetMapElement() const
889 {
890   nsAutoString usemap;
891   if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, usemap)) {
892     return mContent->OwnerDoc()->FindImageMap(usemap);
893   }
894   return nullptr;
895 }
896 
897 // get the offset into the content area of the image where aImg starts if it is a continuation.
898 nscoord
899 nsImageFrame::GetContinuationOffset() const
900 {
901   nscoord offset = 0;
902   for (nsIFrame *f = GetPrevInFlow(); f; f = f->GetPrevInFlow()) {
903     offset += f->GetContentRect().height;
904   }
905   NS_ASSERTION(offset >= 0, "bogus GetContentRect");
906   return offset;
907 }
908 
909 /* virtual */ nscoord
910 nsImageFrame::GetMinISize(nsRenderingContext *aRenderingContext)
911 {
912   // XXX The caller doesn't account for constraints of the height,
913   // min-height, and max-height properties.
914   DebugOnly<nscoord> result;
915   DISPLAY_MIN_WIDTH(this, result);
916   EnsureIntrinsicSizeAndRatio();
917   return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
918     mIntrinsicSize.width.GetCoordValue() : 0;
919 }
920 
921 /* virtual */ nscoord
922 nsImageFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
923 {
924   // XXX The caller doesn't account for constraints of the height,
925   // min-height, and max-height properties.
926   DebugOnly<nscoord> result;
927   DISPLAY_PREF_WIDTH(this, result);
928   EnsureIntrinsicSizeAndRatio();
929   // convert from normal twips to scaled twips (printing...)
930   return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
931     mIntrinsicSize.width.GetCoordValue() : 0;
932 }
933 
934 /* virtual */ IntrinsicSize
935 nsImageFrame::GetIntrinsicSize()
936 {
937   return mIntrinsicSize;
938 }
939 
940 /* virtual */ nsSize
941 nsImageFrame::GetIntrinsicRatio()
942 {
943   return mIntrinsicRatio;
944 }
945 
946 void
947 nsImageFrame::Reflow(nsPresContext*          aPresContext,
948                      ReflowOutput&     aMetrics,
949                      const ReflowInput& aReflowInput,
950                      nsReflowStatus&          aStatus)
951 {
952   MarkInReflow();
953   DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
954   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
955   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
956                   ("enter nsImageFrame::Reflow: availSize=%d,%d",
957                   aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
958 
959   NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
960 
961   aStatus = NS_FRAME_COMPLETE;
962 
963   // see if we have a frozen size (i.e. a fixed width and height)
964   if (HaveFixedSize(aReflowInput)) {
965     mState |= IMAGE_SIZECONSTRAINED;
966   } else {
967     mState &= ~IMAGE_SIZECONSTRAINED;
968   }
969 
970   // XXXldb These two bits are almost exact opposites (except in the
971   // middle of the initial reflow); remove IMAGE_GOTINITIALREFLOW.
972   if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
973     mState |= IMAGE_GOTINITIALREFLOW;
974   }
975 
976   mComputedSize =
977     nsSize(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight());
978 
979   aMetrics.Width() = mComputedSize.width;
980   aMetrics.Height() = mComputedSize.height;
981 
982   // add borders and padding
983   aMetrics.Width()  += aReflowInput.ComputedPhysicalBorderPadding().LeftRight();
984   aMetrics.Height() += aReflowInput.ComputedPhysicalBorderPadding().TopBottom();
985 
986   if (GetPrevInFlow()) {
987     aMetrics.Width() = GetPrevInFlow()->GetSize().width;
988     nscoord y = GetContinuationOffset();
989     aMetrics.Height() -= y + aReflowInput.ComputedPhysicalBorderPadding().top;
990     aMetrics.Height() = std::max(0, aMetrics.Height());
991   }
992 
993 
994   // we have to split images if we are:
995   //  in Paginated mode, we need to have a constrained height, and have a height larger than our available height
996   uint32_t loadStatus = imgIRequest::STATUS_NONE;
997   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
998   NS_ASSERTION(imageLoader, "No content node??");
999   if (imageLoader) {
1000     nsCOMPtr<imgIRequest> currentRequest;
1001     imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
1002                             getter_AddRefs(currentRequest));
1003     if (currentRequest) {
1004       currentRequest->GetImageStatus(&loadStatus);
1005     }
1006   }
1007   if (aPresContext->IsPaginated() &&
1008       ((loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) || (mState & IMAGE_SIZECONSTRAINED)) &&
1009       NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableHeight() &&
1010       aMetrics.Height() > aReflowInput.AvailableHeight()) {
1011     // our desired height was greater than 0, so to avoid infinite
1012     // splitting, use 1 pixel as the min
1013     aMetrics.Height() = std::max(nsPresContext::CSSPixelsToAppUnits(1), aReflowInput.AvailableHeight());
1014     aStatus = NS_FRAME_NOT_COMPLETE;
1015   }
1016 
1017   aMetrics.SetOverflowAreasToDesiredBounds();
1018   EventStates contentState = mContent->AsElement()->State();
1019   bool imageOK = IMAGE_OK(contentState, true);
1020 
1021   // Determine if the size is available
1022   bool haveSize = false;
1023   if (loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) {
1024     haveSize = true;
1025   }
1026 
1027   if (!imageOK || !haveSize) {
1028     nsRect altFeedbackSize(0, 0,
1029                            nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)),
1030                            nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)));
1031     // We include the altFeedbackSize in our visual overflow, but not in our
1032     // scrollable overflow, since it doesn't really need to be scrolled to
1033     // outside the image.
1034     static_assert(eOverflowType_LENGTH == 2, "Unknown overflow types?");
1035     nsRect& visualOverflow = aMetrics.VisualOverflow();
1036     visualOverflow.UnionRect(visualOverflow, altFeedbackSize);
1037   } else {
1038     // We've just reflowed and we should have an accurate size, so we're ready
1039     // to request a decode.
1040     MaybeDecodeForPredictedSize();
1041   }
1042   FinishAndStoreOverflow(&aMetrics);
1043 
1044   if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && !mReflowCallbackPosted) {
1045     nsIPresShell* shell = PresContext()->PresShell();
1046     mReflowCallbackPosted = true;
1047     shell->PostReflowCallback(this);
1048   }
1049 
1050   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
1051                   ("exit nsImageFrame::Reflow: size=%d,%d",
1052                   aMetrics.Width(), aMetrics.Height()));
1053   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
1054 }
1055 
1056 bool
1057 nsImageFrame::ReflowFinished()
1058 {
1059   mReflowCallbackPosted = false;
1060 
1061   // XXX(seth): We don't need this. The purpose of updating visibility
1062   // synchronously is to ensure that animated images start animating
1063   // immediately. In the short term, however,
1064   // nsImageLoadingContent::OnUnlockedDraw() is enough to ensure that
1065   // animations start as soon as the image is painted for the first time, and in
1066   // the long term we want to update visibility information from the display
1067   // list whenever we paint, so we don't actually need to do this. However, to
1068   // avoid behavior changes during the transition from the old image visibility
1069   // code, we'll leave it in for now.
1070   UpdateVisibilitySynchronously();
1071 
1072   return false;
1073 }
1074 
1075 void
1076 nsImageFrame::ReflowCallbackCanceled()
1077 {
1078   mReflowCallbackPosted = false;
1079 }
1080 
1081 // Computes the width of the specified string. aMaxWidth specifies the maximum
1082 // width available. Once this limit is reached no more characters are measured.
1083 // The number of characters that fit within the maximum width are returned in
1084 // aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected
1085 // into the rendering context before this is called (for performance). MMP
1086 nscoord
1087 nsImageFrame::MeasureString(const char16_t*     aString,
1088                             int32_t              aLength,
1089                             nscoord              aMaxWidth,
1090                             uint32_t&            aMaxFit,
1091                             nsRenderingContext& aContext,
1092                             nsFontMetrics& aFontMetrics)
1093 {
1094   nscoord totalWidth = 0;
1095   aFontMetrics.SetTextRunRTL(false);
1096   nscoord spaceWidth = aFontMetrics.SpaceWidth();
1097 
1098   aMaxFit = 0;
1099   while (aLength > 0) {
1100     // Find the next place we can line break
1101     uint32_t  len = aLength;
1102     bool      trailingSpace = false;
1103     for (int32_t i = 0; i < aLength; i++) {
1104       if (dom::IsSpaceCharacter(aString[i]) && (i > 0)) {
1105         len = i;  // don't include the space when measuring
1106         trailingSpace = true;
1107         break;
1108       }
1109     }
1110 
1111     // Measure this chunk of text, and see if it fits
1112     nscoord width =
1113       nsLayoutUtils::AppUnitWidthOfStringBidi(aString, len, this, aFontMetrics,
1114                                               aContext);
1115     bool    fits = (totalWidth + width) <= aMaxWidth;
1116 
1117     // If it fits on the line, or it's the first word we've processed then
1118     // include it
1119     if (fits || (0 == totalWidth)) {
1120       // New piece fits
1121       totalWidth += width;
1122 
1123       // If there's a trailing space then see if it fits as well
1124       if (trailingSpace) {
1125         if ((totalWidth + spaceWidth) <= aMaxWidth) {
1126           totalWidth += spaceWidth;
1127         } else {
1128           // Space won't fit. Leave it at the end but don't include it in
1129           // the width
1130           fits = false;
1131         }
1132 
1133         len++;
1134       }
1135 
1136       aMaxFit += len;
1137       aString += len;
1138       aLength -= len;
1139     }
1140 
1141     if (!fits) {
1142       break;
1143     }
1144   }
1145   return totalWidth;
1146 }
1147 
1148 // Formats the alt-text to fit within the specified rectangle. Breaks lines
1149 // between words if a word would extend past the edge of the rectangle
1150 void
1151 nsImageFrame::DisplayAltText(nsPresContext*      aPresContext,
1152                              nsRenderingContext& aRenderingContext,
1153                              const nsString&      aAltText,
1154                              const nsRect&        aRect)
1155 {
1156   // Set font and color
1157   aRenderingContext.ThebesContext()->
1158     SetColor(Color::FromABGR(StyleColor()->mColor));
1159   RefPtr<nsFontMetrics> fm =
1160     nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
1161 
1162   // Format the text to display within the formatting rect
1163 
1164   nscoord maxAscent = fm->MaxAscent();
1165   nscoord maxDescent = fm->MaxDescent();
1166   nscoord lineHeight = fm->MaxHeight(); // line-relative, so an x-coordinate
1167                                         // length if writing mode is vertical
1168 
1169   WritingMode wm = GetWritingMode();
1170   bool isVertical = wm.IsVertical();
1171 
1172   fm->SetVertical(isVertical);
1173   fm->SetTextOrientation(StyleVisibility()->mTextOrientation);
1174 
1175   // XXX It would be nice if there was a way to have the font metrics tell
1176   // use where to break the text given a maximum width. At a minimum we need
1177   // to be able to get the break character...
1178   const char16_t* str = aAltText.get();
1179   int32_t strLen = aAltText.Length();
1180   nsPoint pt = wm.IsVerticalRL() ? aRect.TopRight() - nsPoint(lineHeight, 0)
1181                                  : aRect.TopLeft();
1182   nscoord iSize = isVertical ? aRect.height : aRect.width;
1183 
1184   if (!aPresContext->BidiEnabled() && HasRTLChars(aAltText)) {
1185     aPresContext->SetBidiEnabled();
1186   }
1187 
1188   // Always show the first line, even if we have to clip it below
1189   bool firstLine = true;
1190   while (strLen > 0) {
1191     if (!firstLine) {
1192       // If we've run out of space, break out of the loop
1193       if ((!isVertical && (pt.y + maxDescent) >= aRect.YMost()) ||
1194           (wm.IsVerticalRL() && (pt.x + maxDescent < aRect.x)) ||
1195           (wm.IsVerticalLR() && (pt.x + maxDescent >= aRect.XMost()))) {
1196         break;
1197       }
1198     }
1199 
1200     // Determine how much of the text to display on this line
1201     uint32_t  maxFit;  // number of characters that fit
1202     nscoord strWidth = MeasureString(str, strLen, iSize, maxFit,
1203                                      aRenderingContext, *fm);
1204 
1205     // Display the text
1206     nsresult rv = NS_ERROR_FAILURE;
1207 
1208     if (aPresContext->BidiEnabled()) {
1209       nsBidiDirection dir;
1210       nscoord x, y;
1211 
1212       if (isVertical) {
1213         x = pt.x + maxDescent;
1214         if (wm.IsBidiLTR()) {
1215           y = aRect.y;
1216           dir = NSBIDI_LTR;
1217         } else {
1218           y = aRect.YMost() - strWidth;
1219           dir = NSBIDI_RTL;
1220         }
1221       } else {
1222         y = pt.y + maxAscent;
1223         if (wm.IsBidiLTR()) {
1224           x = aRect.x;
1225           dir = NSBIDI_LTR;
1226         } else {
1227           x = aRect.XMost() - strWidth;
1228           dir = NSBIDI_RTL;
1229         }
1230       }
1231 
1232       rv = nsBidiPresUtils::RenderText(str, maxFit, dir,
1233                                        aPresContext, aRenderingContext,
1234                                        aRenderingContext.GetDrawTarget(),
1235                                        *fm, x, y);
1236     }
1237     if (NS_FAILED(rv)) {
1238       nsLayoutUtils::DrawUniDirString(str, maxFit,
1239                                       isVertical
1240                                         ? nsPoint(pt.x + maxDescent, pt.y)
1241                                         : nsPoint(pt.x, pt.y + maxAscent),
1242                                       *fm, aRenderingContext);
1243     }
1244 
1245     // Move to the next line
1246     str += maxFit;
1247     strLen -= maxFit;
1248     if (wm.IsVerticalRL()) {
1249       pt.x -= lineHeight;
1250     } else if (wm.IsVerticalLR()) {
1251       pt.x += lineHeight;
1252     } else {
1253       pt.y += lineHeight;
1254     }
1255 
1256     firstLine = false;
1257   }
1258 }
1259 
1260 struct nsRecessedBorder : public nsStyleBorder {
1261   nsRecessedBorder(nscoord aBorderWidth, nsPresContext* aPresContext)
1262     : nsStyleBorder(aPresContext)
1263   {
1264     NS_FOR_CSS_SIDES(side) {
1265       mBorderColor[side] = StyleComplexColor::FromColor(NS_RGB(0, 0, 0));
1266       mBorder.Side(side) = aBorderWidth;
1267       // Note: use SetBorderStyle here because we want to affect
1268       // mComputedBorder
1269       SetBorderStyle(side, NS_STYLE_BORDER_STYLE_INSET);
1270     }
1271   }
1272 };
1273 
1274 class nsDisplayAltFeedback : public nsDisplayItem {
1275 public:
1276   nsDisplayAltFeedback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
1277     : nsDisplayItem(aBuilder, aFrame) {}
1278 
1279   virtual nsDisplayItemGeometry*
1280   AllocateGeometry(nsDisplayListBuilder* aBuilder) override
1281   {
1282     return new nsDisplayItemGenericImageGeometry(this, aBuilder);
1283   }
1284 
1285   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
1286                                          const nsDisplayItemGeometry* aGeometry,
1287                                          nsRegion* aInvalidRegion) override
1288   {
1289     auto geometry =
1290       static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
1291 
1292     if (aBuilder->ShouldSyncDecodeImages() &&
1293         geometry->ShouldInvalidateToSyncDecodeImages()) {
1294       bool snap;
1295       aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
1296     }
1297 
1298     nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
1299   }
1300 
1301   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
1302                            bool* aSnap) override
1303   {
1304     *aSnap = false;
1305     return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
1306   }
1307 
1308   virtual void Paint(nsDisplayListBuilder* aBuilder,
1309                      nsRenderingContext* aCtx) override
1310   {
1311     // Always sync decode, because these icons are UI, and since they're not
1312     // discardable we'll pay the price of sync decoding at most once.
1313     uint32_t flags = imgIContainer::FLAG_SYNC_DECODE;
1314 
1315     nsImageFrame* f = static_cast<nsImageFrame*>(mFrame);
1316     DrawResult result =
1317       f->DisplayAltFeedback(*aCtx,
1318                             mVisibleRect,
1319                             ToReferenceFrame(),
1320                             flags);
1321 
1322     nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
1323   }
1324 
1325   NS_DISPLAY_DECL_NAME("AltFeedback", TYPE_ALT_FEEDBACK)
1326 };
1327 
1328 DrawResult
1329 nsImageFrame::DisplayAltFeedback(nsRenderingContext& aRenderingContext,
1330                                  const nsRect& aDirtyRect,
1331                                  nsPoint aPt,
1332                                  uint32_t aFlags)
1333 {
1334   // We should definitely have a gIconLoad here.
1335   MOZ_ASSERT(gIconLoad, "How did we succeed in Init then?");
1336 
1337   // Whether we draw the broken or loading icon.
1338   bool isLoading = IMAGE_OK(GetContent()->AsElement()->State(), true);
1339 
1340   // Calculate the inner area
1341   nsRect  inner = GetInnerArea() + aPt;
1342 
1343   // Display a recessed one pixel border
1344   nscoord borderEdgeWidth = nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH);
1345 
1346   // if inner area is empty, then make it big enough for at least the icon
1347   if (inner.IsEmpty()){
1348     inner.SizeTo(2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)),
1349                  2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)));
1350   }
1351 
1352   // Make sure we have enough room to actually render the border within
1353   // our frame bounds
1354   if ((inner.width < 2 * borderEdgeWidth) || (inner.height < 2 * borderEdgeWidth)) {
1355     return DrawResult::SUCCESS;
1356   }
1357 
1358   // Paint the border
1359   if (!isLoading || gIconLoad->mPrefShowLoadingPlaceholder) {
1360     nsRecessedBorder recessedBorder(borderEdgeWidth, PresContext());
1361 
1362     // Assert that we're not drawing a border-image here; if we were, we
1363     // couldn't ignore the DrawResult that PaintBorderWithStyleBorder returns.
1364     MOZ_ASSERT(recessedBorder.mBorderImageSource.GetType() == eStyleImageType_Null);
1365 
1366     Unused <<
1367       nsCSSRendering::PaintBorderWithStyleBorder(PresContext(), aRenderingContext,
1368                                                  this, inner, inner,
1369                                                  recessedBorder, mStyleContext,
1370                                                  PaintBorderFlags::SYNC_DECODE_IMAGES);
1371   }
1372 
1373   // Adjust the inner rect to account for the one pixel recessed border,
1374   // and a six pixel padding on each edge
1375   inner.Deflate(nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH),
1376                 nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH));
1377   if (inner.IsEmpty()) {
1378     return DrawResult::SUCCESS;
1379   }
1380 
1381   DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
1382   gfxContext* gfx = aRenderingContext.ThebesContext();
1383 
1384   // Clip so we don't render outside the inner rect
1385   gfx->Save();
1386   gfx->Clip(NSRectToSnappedRect(inner, PresContext()->AppUnitsPerDevPixel(),
1387                                 *drawTarget));
1388 
1389   DrawResult result = DrawResult::NOT_READY;
1390 
1391   // Check if we should display image placeholders
1392   if (!gIconLoad->mPrefShowPlaceholders ||
1393       (isLoading && !gIconLoad->mPrefShowLoadingPlaceholder)) {
1394     result = DrawResult::SUCCESS;
1395   } else {
1396     nscoord size = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE);
1397 
1398     imgIRequest* request = isLoading
1399                               ? nsImageFrame::gIconLoad->mLoadingImage
1400                               : nsImageFrame::gIconLoad->mBrokenImage;
1401 
1402     // If we weren't previously displaying an icon, register ourselves
1403     // as an observer for load and animation updates and flag that we're
1404     // doing so now.
1405     if (request && !mDisplayingIcon) {
1406       gIconLoad->AddIconObserver(this);
1407       mDisplayingIcon = true;
1408     }
1409 
1410     WritingMode wm = GetWritingMode();
1411     bool flushRight =
1412       (!wm.IsVertical() && !wm.IsBidiLTR()) || wm.IsVerticalRL();
1413 
1414     // If the icon in question is loaded, draw it.
1415     uint32_t imageStatus = 0;
1416     if (request)
1417       request->GetImageStatus(&imageStatus);
1418     if (imageStatus & imgIRequest::STATUS_LOAD_COMPLETE) {
1419       nsCOMPtr<imgIContainer> imgCon;
1420       request->GetImage(getter_AddRefs(imgCon));
1421       MOZ_ASSERT(imgCon, "Load complete, but no image container?");
1422       nsRect dest(flushRight ? inner.XMost() - size : inner.x,
1423                   inner.y, size, size);
1424       result = nsLayoutUtils::DrawSingleImage(*gfx, PresContext(), imgCon,
1425         nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
1426         nullptr, aFlags);
1427     }
1428 
1429     // If we could not draw the icon, just draw some graffiti in the mean time.
1430     if (result == DrawResult::NOT_READY) {
1431       ColorPattern color(ToDeviceColor(Color(1.f, 0.f, 0.f, 1.f)));
1432 
1433       nscoord iconXPos = flushRight ? inner.XMost() - size : inner.x;
1434 
1435       // stroked rect:
1436       nsRect rect(iconXPos, inner.y, size, size);
1437       Rect devPxRect =
1438         ToRect(nsLayoutUtils::RectToGfxRect(rect, PresContext()->AppUnitsPerDevPixel()));
1439       drawTarget->StrokeRect(devPxRect, color);
1440 
1441       // filled circle in bottom right quadrant of stroked rect:
1442       nscoord twoPX = nsPresContext::CSSPixelsToAppUnits(2);
1443       rect = nsRect(iconXPos + size/2, inner.y + size/2,
1444                     size/2 - twoPX, size/2 - twoPX);
1445       devPxRect =
1446         ToRect(nsLayoutUtils::RectToGfxRect(rect, PresContext()->AppUnitsPerDevPixel()));
1447       RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder();
1448       AppendEllipseToPath(builder, devPxRect.Center(), devPxRect.Size());
1449       RefPtr<Path> ellipse = builder->Finish();
1450       drawTarget->Fill(ellipse, color);
1451     }
1452 
1453     // Reduce the inner rect by the width of the icon, and leave an
1454     // additional ICON_PADDING pixels for padding
1455     int32_t paddedIconSize =
1456       nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING);
1457     if (wm.IsVertical()) {
1458       inner.y += paddedIconSize;
1459       inner.height -= paddedIconSize;
1460     } else {
1461       if (!flushRight) {
1462         inner.x += paddedIconSize;
1463       }
1464       inner.width -= paddedIconSize;
1465     }
1466   }
1467 
1468   // If there's still room, display the alt-text
1469   if (!inner.IsEmpty()) {
1470     nsIContent* content = GetContent();
1471     if (content) {
1472       nsXPIDLString altText;
1473       nsCSSFrameConstructor::GetAlternateTextFor(content,
1474                                                  content->NodeInfo()->NameAtom(),
1475                                                  altText);
1476       DisplayAltText(PresContext(), aRenderingContext, altText, inner);
1477     }
1478   }
1479 
1480   aRenderingContext.ThebesContext()->Restore();
1481 
1482   return result;
1483 }
1484 
1485 #ifdef DEBUG
1486 static void PaintDebugImageMap(nsIFrame* aFrame, DrawTarget* aDrawTarget,
1487                                const nsRect& aDirtyRect, nsPoint aPt)
1488 {
1489   nsImageFrame* f = static_cast<nsImageFrame*>(aFrame);
1490   nsRect inner = f->GetInnerArea() + aPt;
1491   gfxPoint devPixelOffset =
1492     nsLayoutUtils::PointToGfxPoint(inner.TopLeft(),
1493                                    aFrame->PresContext()->AppUnitsPerDevPixel());
1494   AutoRestoreTransform autoRestoreTransform(aDrawTarget);
1495   aDrawTarget->SetTransform(
1496     aDrawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset)));
1497   f->GetImageMap()->Draw(aFrame, *aDrawTarget,
1498                          ColorPattern(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f))));
1499 }
1500 #endif
1501 
1502 void
1503 nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
1504                       nsRenderingContext* aCtx)
1505 {
1506   uint32_t flags = imgIContainer::FLAG_NONE;
1507   if (aBuilder->ShouldSyncDecodeImages()) {
1508     flags |= imgIContainer::FLAG_SYNC_DECODE;
1509   }
1510   if (aBuilder->IsPaintingToWindow()) {
1511     flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
1512   }
1513 
1514   DrawResult result = static_cast<nsImageFrame*>(mFrame)->
1515     PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mImage, flags);
1516 
1517   if (result == DrawResult::NOT_READY ||
1518       result == DrawResult::INCOMPLETE ||
1519       result == DrawResult::TEMPORARY_ERROR) {
1520     // If the current image failed to paint because it's still loading or
1521     // decoding, try painting the previous image.
1522     if (mPrevImage) {
1523       result = static_cast<nsImageFrame*>(mFrame)->
1524         PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mPrevImage, flags);
1525     }
1526   }
1527 
1528   nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
1529 }
1530 
1531 nsDisplayItemGeometry*
1532 nsDisplayImage::AllocateGeometry(nsDisplayListBuilder* aBuilder)
1533 {
1534   return new nsDisplayItemGenericImageGeometry(this, aBuilder);
1535 }
1536 
1537 void
1538 nsDisplayImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
1539                                           const nsDisplayItemGeometry* aGeometry,
1540                                           nsRegion* aInvalidRegion)
1541 {
1542   auto geometry =
1543     static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
1544 
1545   if (aBuilder->ShouldSyncDecodeImages() &&
1546       geometry->ShouldInvalidateToSyncDecodeImages()) {
1547     bool snap;
1548     aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
1549   }
1550 
1551   nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
1552 }
1553 
1554 already_AddRefed<imgIContainer>
1555 nsDisplayImage::GetImage()
1556 {
1557   nsCOMPtr<imgIContainer> image = mImage;
1558   return image.forget();
1559 }
1560 
1561 nsRect
1562 nsDisplayImage::GetDestRect()
1563 {
1564   bool snap = true;
1565   const nsRect frameContentBox = GetBounds(&snap);
1566 
1567   nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
1568   return imageFrame->PredictedDestRect(frameContentBox);
1569 }
1570 
1571 LayerState
1572 nsDisplayImage::GetLayerState(nsDisplayListBuilder* aBuilder,
1573                               LayerManager* aManager,
1574                               const ContainerLayerParameters& aParameters)
1575 {
1576   bool animated = false;
1577   if (!nsLayoutUtils::AnimatedImageLayersEnabled() ||
1578       mImage->GetType() != imgIContainer::TYPE_RASTER ||
1579       NS_FAILED(mImage->GetAnimated(&animated)) ||
1580       !animated) {
1581     if (!aManager->IsCompositingCheap() ||
1582         !nsLayoutUtils::GPUImageScalingEnabled()) {
1583       return LAYER_NONE;
1584     }
1585   }
1586 
1587   if (!animated) {
1588     int32_t imageWidth;
1589     int32_t imageHeight;
1590     mImage->GetWidth(&imageWidth);
1591     mImage->GetHeight(&imageHeight);
1592 
1593     NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
1594 
1595     const int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
1596     const LayoutDeviceRect destRect =
1597       LayoutDeviceRect::FromAppUnits(GetDestRect(), factor);
1598     const LayerRect destLayerRect = destRect * aParameters.Scale();
1599 
1600     // Calculate the scaling factor for the frame.
1601     const gfxSize scale = gfxSize(destLayerRect.width / imageWidth,
1602                                   destLayerRect.height / imageHeight);
1603 
1604     // If we are not scaling at all, no point in separating this into a layer.
1605     if (scale.width == 1.0f && scale.height == 1.0f) {
1606       return LAYER_NONE;
1607     }
1608 
1609     // If the target size is pretty small, no point in using a layer.
1610     if (destLayerRect.width * destLayerRect.height < 64 * 64) {
1611       return LAYER_NONE;
1612     }
1613   }
1614 
1615   uint32_t flags = aBuilder->ShouldSyncDecodeImages()
1616                  ? imgIContainer::FLAG_SYNC_DECODE
1617                  : imgIContainer::FLAG_NONE;
1618 
1619   if (!mImage->IsImageContainerAvailable(aManager, flags)) {
1620     return LAYER_NONE;
1621   }
1622 
1623   return LAYER_ACTIVE;
1624 }
1625 
1626 
1627 /* virtual */ nsRegion
1628 nsDisplayImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
1629                                 bool* aSnap)
1630 {
1631   *aSnap = false;
1632   if (mImage && mImage->WillDrawOpaqueNow()) {
1633     const nsRect frameContentBox = GetBounds(aSnap);
1634     return GetDestRect().Intersect(frameContentBox);
1635   }
1636   return nsRegion();
1637 }
1638 
1639 already_AddRefed<Layer>
1640 nsDisplayImage::BuildLayer(nsDisplayListBuilder* aBuilder,
1641                            LayerManager* aManager,
1642                            const ContainerLayerParameters& aParameters)
1643 {
1644   uint32_t flags = aBuilder->ShouldSyncDecodeImages()
1645                  ? imgIContainer::FLAG_SYNC_DECODE
1646                  : imgIContainer::FLAG_NONE;
1647 
1648   RefPtr<ImageContainer> container =
1649     mImage->GetImageContainer(aManager, flags);
1650   if (!container) {
1651     return nullptr;
1652   }
1653 
1654   RefPtr<ImageLayer> layer = static_cast<ImageLayer*>
1655     (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
1656   if (!layer) {
1657     layer = aManager->CreateImageLayer();
1658     if (!layer)
1659       return nullptr;
1660   }
1661   layer->SetContainer(container);
1662   ConfigureLayer(layer, aParameters);
1663   return layer.forget();
1664 }
1665 
1666 DrawResult
1667 nsImageFrame::PaintImage(nsRenderingContext& aRenderingContext, nsPoint aPt,
1668                          const nsRect& aDirtyRect, imgIContainer* aImage,
1669                          uint32_t aFlags)
1670 {
1671   DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
1672 
1673   // Render the image into our content area (the area inside
1674   // the borders and padding)
1675   NS_ASSERTION(GetInnerArea().width == mComputedSize.width, "bad width");
1676 
1677   // NOTE: We use mComputedSize instead of just GetInnerArea()'s own size here,
1678   // because GetInnerArea() might be smaller if we're fragmented, whereas
1679   // mComputedSize has our full content-box size (which we need for
1680   // ComputeObjectDestRect to work correctly).
1681   nsRect constraintRect(aPt + GetInnerArea().TopLeft(), mComputedSize);
1682   constraintRect.y -= GetContinuationOffset();
1683 
1684   nsPoint anchorPoint;
1685   nsRect dest = nsLayoutUtils::ComputeObjectDestRect(constraintRect,
1686                                                      mIntrinsicSize,
1687                                                      mIntrinsicRatio,
1688                                                      StylePosition(),
1689                                                      &anchorPoint);
1690 
1691   uint32_t flags = aFlags;
1692   if (mForceSyncDecoding) {
1693     flags |= imgIContainer::FLAG_SYNC_DECODE;
1694   }
1695 
1696   DrawResult result =
1697     nsLayoutUtils::DrawSingleImage(*aRenderingContext.ThebesContext(),
1698       PresContext(), aImage,
1699       nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
1700       nullptr, flags, &anchorPoint);
1701 
1702   nsImageMap* map = GetImageMap();
1703   if (map) {
1704     gfxPoint devPixelOffset =
1705       nsLayoutUtils::PointToGfxPoint(dest.TopLeft(),
1706                                      PresContext()->AppUnitsPerDevPixel());
1707     AutoRestoreTransform autoRestoreTransform(drawTarget);
1708     drawTarget->SetTransform(
1709       drawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset)));
1710 
1711     // solid white stroke:
1712     ColorPattern white(ToDeviceColor(Color(1.f, 1.f, 1.f, 1.f)));
1713     map->Draw(this, *drawTarget, white);
1714 
1715     // then dashed black stroke over the top:
1716     ColorPattern black(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f)));
1717     StrokeOptions strokeOptions;
1718     nsLayoutUtils::InitDashPattern(strokeOptions, NS_STYLE_BORDER_STYLE_DOTTED);
1719     map->Draw(this, *drawTarget, black, strokeOptions);
1720   }
1721 
1722   if (result == DrawResult::SUCCESS) {
1723     mPrevImage = aImage;
1724   } else if (result == DrawResult::BAD_IMAGE) {
1725     mPrevImage = nullptr;
1726   }
1727 
1728   return result;
1729 }
1730 
1731 void
1732 nsImageFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
1733                                const nsRect&           aDirtyRect,
1734                                const nsDisplayListSet& aLists)
1735 {
1736   if (!IsVisibleForPainting(aBuilder))
1737     return;
1738 
1739   DisplayBorderBackgroundOutline(aBuilder, aLists);
1740 
1741   uint32_t clipFlags =
1742     nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition()) ?
1743     0 : DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT;
1744 
1745   DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
1746     clip(aBuilder, this, clipFlags);
1747 
1748   if (mComputedSize.width != 0 && mComputedSize.height != 0) {
1749     nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
1750     NS_ASSERTION(imageLoader, "Not an image loading content?");
1751 
1752     nsCOMPtr<imgIRequest> currentRequest;
1753     if (imageLoader) {
1754       imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
1755                               getter_AddRefs(currentRequest));
1756     }
1757 
1758     EventStates contentState = mContent->AsElement()->State();
1759     bool imageOK = IMAGE_OK(contentState, true);
1760 
1761     // XXX(seth): The SizeIsAvailable check here should not be necessary - the
1762     // intention is that a non-null mImage means we have a size, but there is
1763     // currently some code that violates this invariant.
1764     if (!imageOK || !mImage || !SizeIsAvailable(currentRequest)) {
1765       // No image yet, or image load failed. Draw the alt-text and an icon
1766       // indicating the status
1767       aLists.Content()->AppendNewToTop(new (aBuilder)
1768         nsDisplayAltFeedback(aBuilder, this));
1769 
1770       // This image is visible (we are being asked to paint it) but it's not
1771       // decoded yet. And we are not going to ask the image to draw, so this
1772       // may be the only chance to tell it that it should decode.
1773       if (currentRequest) {
1774         uint32_t status = 0;
1775         currentRequest->GetImageStatus(&status);
1776         if (!(status & imgIRequest::STATUS_DECODE_COMPLETE)) {
1777           MaybeDecodeForPredictedSize();
1778         }
1779       }
1780     } else {
1781       aLists.Content()->AppendNewToTop(new (aBuilder)
1782         nsDisplayImage(aBuilder, this, mImage, mPrevImage));
1783 
1784       // If we were previously displaying an icon, we're not anymore
1785       if (mDisplayingIcon) {
1786         gIconLoad->RemoveIconObserver(this);
1787         mDisplayingIcon = false;
1788       }
1789 
1790 #ifdef DEBUG
1791       if (GetShowFrameBorders() && GetImageMap()) {
1792         aLists.Outlines()->AppendNewToTop(new (aBuilder)
1793           nsDisplayGeneric(aBuilder, this, PaintDebugImageMap, "DebugImageMap",
1794                            nsDisplayItem::TYPE_DEBUG_IMAGE_MAP));
1795       }
1796 #endif
1797     }
1798   }
1799 
1800   if (ShouldDisplaySelection()) {
1801     DisplaySelectionOverlay(aBuilder, aLists.Content(),
1802                             nsISelectionDisplay::DISPLAY_IMAGES);
1803   }
1804 }
1805 
1806 bool
1807 nsImageFrame::ShouldDisplaySelection()
1808 {
1809   // XXX what on EARTH is this code for?
1810   nsresult result;
1811   nsPresContext* presContext = PresContext();
1812   int16_t displaySelection = presContext->PresShell()->GetSelectionFlags();
1813   if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES))
1814     return false;//no need to check the blue border, we cannot be drawn selected
1815 //insert hook here for image selection drawing
1816 #if IMAGE_EDITOR_CHECK
1817   //check to see if this frame is in an editor context
1818   //isEditor check. this needs to be changed to have better way to check
1819   if (displaySelection == nsISelectionDisplay::DISPLAY_ALL)
1820   {
1821     nsCOMPtr<nsISelectionController> selCon;
1822     result = GetSelectionController(presContext, getter_AddRefs(selCon));
1823     if (NS_SUCCEEDED(result) && selCon)
1824     {
1825       nsCOMPtr<nsISelection> selection;
1826       result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
1827       if (NS_SUCCEEDED(result) && selection)
1828       {
1829         int32_t rangeCount;
1830         selection->GetRangeCount(&rangeCount);
1831         if (rangeCount == 1) //if not one then let code drop to nsFrame::Paint
1832         {
1833           nsCOMPtr<nsIContent> parentContent = mContent->GetParent();
1834           if (parentContent)
1835           {
1836             int32_t thisOffset = parentContent->IndexOf(mContent);
1837             nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(parentContent);
1838             nsCOMPtr<nsIDOMNode> rangeNode;
1839             int32_t rangeOffset;
1840             nsCOMPtr<nsIDOMRange> range;
1841             selection->GetRangeAt(0,getter_AddRefs(range));
1842             if (range)
1843             {
1844               range->GetStartContainer(getter_AddRefs(rangeNode));
1845               range->GetStartOffset(&rangeOffset);
1846 
1847               if (parentNode && rangeNode && (rangeNode == parentNode) && rangeOffset == thisOffset)
1848               {
1849                 range->GetEndContainer(getter_AddRefs(rangeNode));
1850                 range->GetEndOffset(&rangeOffset);
1851                 if ((rangeNode == parentNode) && (rangeOffset == (thisOffset +1))) //+1 since that would mean this whole content is selected only
1852                   return false; //do not allow nsFrame do draw any further selection
1853               }
1854             }
1855           }
1856         }
1857       }
1858     }
1859   }
1860 #endif
1861   return true;
1862 }
1863 
1864 nsImageMap*
1865 nsImageFrame::GetImageMap()
1866 {
1867   if (!mImageMap) {
1868     nsIContent* map = GetMapElement();
1869     if (map) {
1870       mImageMap = new nsImageMap();
1871       mImageMap->Init(this, map);
1872     }
1873   }
1874 
1875   return mImageMap;
1876 }
1877 
1878 bool
1879 nsImageFrame::IsServerImageMap()
1880 {
1881   return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::ismap);
1882 }
1883 
1884 // Translate an point that is relative to our frame
1885 // into a localized pixel coordinate that is relative to the
1886 // content area of this frame (inside the border+padding).
1887 void
1888 nsImageFrame::TranslateEventCoords(const nsPoint& aPoint,
1889                                    nsIntPoint&     aResult)
1890 {
1891   nscoord x = aPoint.x;
1892   nscoord y = aPoint.y;
1893 
1894   // Subtract out border and padding here so that the coordinates are
1895   // now relative to the content area of this frame.
1896   nsRect inner = GetInnerArea();
1897   x -= inner.x;
1898   y -= inner.y;
1899 
1900   aResult.x = nsPresContext::AppUnitsToIntCSSPixels(x);
1901   aResult.y = nsPresContext::AppUnitsToIntCSSPixels(y);
1902 }
1903 
1904 bool
1905 nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget,
1906                                          nsIContent** aNode)
1907 {
1908   bool status = false;
1909   aTarget.Truncate();
1910   *aHref = nullptr;
1911   *aNode = nullptr;
1912 
1913   // Walk up the content tree, looking for an nsIDOMAnchorElement
1914   for (nsIContent* content = mContent->GetParent();
1915        content; content = content->GetParent()) {
1916     nsCOMPtr<dom::Link> link(do_QueryInterface(content));
1917     if (link) {
1918       nsCOMPtr<nsIURI> href = content->GetHrefURI();
1919       if (href) {
1920         href->Clone(aHref);
1921       }
1922       status = (*aHref != nullptr);
1923 
1924       nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(content));
1925       if (anchor) {
1926         anchor->GetTarget(aTarget);
1927       }
1928       NS_ADDREF(*aNode = content);
1929       break;
1930     }
1931   }
1932   return status;
1933 }
1934 
1935 nsresult
1936 nsImageFrame::GetContentForEvent(WidgetEvent* aEvent,
1937                                  nsIContent** aContent)
1938 {
1939   NS_ENSURE_ARG_POINTER(aContent);
1940 
1941   nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
1942   if (f != this) {
1943     return f->GetContentForEvent(aEvent, aContent);
1944   }
1945 
1946   // XXX We need to make this special check for area element's capturing the
1947   // mouse due to bug 135040. Remove it once that's fixed.
1948   nsIContent* capturingContent =
1949     aEvent->HasMouseEventMessage() ? nsIPresShell::GetCapturingContent() :
1950                                      nullptr;
1951   if (capturingContent && capturingContent->GetPrimaryFrame() == this) {
1952     *aContent = capturingContent;
1953     NS_IF_ADDREF(*aContent);
1954     return NS_OK;
1955   }
1956 
1957   nsImageMap* map = GetImageMap();
1958 
1959   if (nullptr != map) {
1960     nsIntPoint p;
1961     TranslateEventCoords(
1962       nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
1963     nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
1964     if (area) {
1965       area.forget(aContent);
1966       return NS_OK;
1967     }
1968   }
1969 
1970   *aContent = GetContent();
1971   NS_IF_ADDREF(*aContent);
1972   return NS_OK;
1973 }
1974 
1975 // XXX what should clicks on transparent pixels do?
1976 nsresult
1977 nsImageFrame::HandleEvent(nsPresContext* aPresContext,
1978                           WidgetGUIEvent* aEvent,
1979                           nsEventStatus* aEventStatus)
1980 {
1981   NS_ENSURE_ARG_POINTER(aEventStatus);
1982 
1983   if ((aEvent->mMessage == eMouseClick &&
1984        aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) ||
1985       aEvent->mMessage == eMouseMove) {
1986     nsImageMap* map = GetImageMap();
1987     bool isServerMap = IsServerImageMap();
1988     if ((nullptr != map) || isServerMap) {
1989       nsIntPoint p;
1990       TranslateEventCoords(
1991         nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
1992       bool inside = false;
1993       // Even though client-side image map triggering happens
1994       // through content, we need to make sure we're not inside
1995       // (in case we deal with a case of both client-side and
1996       // sever-side on the same image - it happens!)
1997       if (nullptr != map) {
1998         inside = !!map->GetArea(p.x, p.y);
1999       }
2000 
2001       if (!inside && isServerMap) {
2002 
2003         // Server side image maps use the href in a containing anchor
2004         // element to provide the basis for the destination url.
2005         nsCOMPtr<nsIURI> uri;
2006         nsAutoString target;
2007         nsCOMPtr<nsIContent> anchorNode;
2008         if (GetAnchorHREFTargetAndNode(getter_AddRefs(uri), target,
2009                                        getter_AddRefs(anchorNode))) {
2010           // XXX if the mouse is over/clicked in the border/padding area
2011           // we should probably just pretend nothing happened. Nav4
2012           // keeps the x,y coordinates positive as we do; IE doesn't
2013           // bother. Both of them send the click through even when the
2014           // mouse is over the border.
2015           if (p.x < 0) p.x = 0;
2016           if (p.y < 0) p.y = 0;
2017 
2018           nsAutoCString spec;
2019           nsresult rv = uri->GetSpec(spec);
2020           NS_ENSURE_SUCCESS(rv, rv);
2021 
2022           spec += nsPrintfCString("?%d,%d", p.x, p.y);
2023           rv = uri->SetSpec(spec);
2024           NS_ENSURE_SUCCESS(rv, rv);
2025 
2026           bool clicked = false;
2027           if (aEvent->mMessage == eMouseClick && !aEvent->DefaultPrevented()) {
2028             *aEventStatus = nsEventStatus_eConsumeDoDefault;
2029             clicked = true;
2030           }
2031           nsContentUtils::TriggerLink(anchorNode, aPresContext, uri, target,
2032                                       clicked, true, true);
2033         }
2034       }
2035     }
2036   }
2037 
2038   return nsAtomicContainerFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
2039 }
2040 
2041 nsresult
2042 nsImageFrame::GetCursor(const nsPoint& aPoint,
2043                         nsIFrame::Cursor& aCursor)
2044 {
2045   nsImageMap* map = GetImageMap();
2046   if (nullptr != map) {
2047     nsIntPoint p;
2048     TranslateEventCoords(aPoint, p);
2049     nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
2050     if (area) {
2051       // Use the cursor from the style of the *area* element.
2052       // XXX Using the image as the parent style context isn't
2053       // technically correct, but it's probably the right thing to do
2054       // here, since it means that areas on which the cursor isn't
2055       // specified will inherit the style from the image.
2056       RefPtr<nsStyleContext> areaStyle =
2057         PresContext()->PresShell()->StyleSet()->
2058           ResolveStyleFor(area->AsElement(), StyleContext());
2059       FillCursorInformationFromStyle(areaStyle->StyleUserInterface(),
2060                                      aCursor);
2061       if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
2062         aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
2063       }
2064       return NS_OK;
2065     }
2066   }
2067   return nsFrame::GetCursor(aPoint, aCursor);
2068 }
2069 
2070 nsresult
2071 nsImageFrame::AttributeChanged(int32_t aNameSpaceID,
2072                                nsIAtom* aAttribute,
2073                                int32_t aModType)
2074 {
2075   nsresult rv = nsAtomicContainerFrame::AttributeChanged(aNameSpaceID,
2076                                                          aAttribute, aModType);
2077   if (NS_FAILED(rv)) {
2078     return rv;
2079   }
2080   if (nsGkAtoms::alt == aAttribute)
2081   {
2082     PresContext()->PresShell()->FrameNeedsReflow(this,
2083                                                  nsIPresShell::eStyleChange,
2084                                                  NS_FRAME_IS_DIRTY);
2085   }
2086 
2087   return NS_OK;
2088 }
2089 
2090 void
2091 nsImageFrame::OnVisibilityChange(Visibility aNewVisibility,
2092                                  Maybe<OnNonvisible> aNonvisibleAction)
2093 {
2094   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
2095   if (!imageLoader) {
2096     MOZ_ASSERT_UNREACHABLE("Should have an nsIImageLoadingContent");
2097     nsAtomicContainerFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
2098     return;
2099   }
2100 
2101   imageLoader->OnVisibilityChange(aNewVisibility, aNonvisibleAction);
2102 
2103   if (aNewVisibility == Visibility::APPROXIMATELY_VISIBLE) {
2104     MaybeDecodeForPredictedSize();
2105   }
2106 
2107   nsAtomicContainerFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
2108 }
2109 
2110 nsIAtom*
2111 nsImageFrame::GetType() const
2112 {
2113   return nsGkAtoms::imageFrame;
2114 }
2115 
2116 #ifdef DEBUG_FRAME_DUMP
2117 nsresult
2118 nsImageFrame::GetFrameName(nsAString& aResult) const
2119 {
2120   return MakeFrameName(NS_LITERAL_STRING("ImageFrame"), aResult);
2121 }
2122 
2123 void
2124 nsImageFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
2125 {
2126   nsCString str;
2127   ListGeneric(str, aPrefix, aFlags);
2128 
2129   // output the img src url
2130   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
2131   if (imageLoader) {
2132     nsCOMPtr<imgIRequest> currentRequest;
2133     imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
2134                             getter_AddRefs(currentRequest));
2135     if (currentRequest) {
2136       nsCOMPtr<nsIURI> uri;
2137       currentRequest->GetURI(getter_AddRefs(uri));
2138       nsAutoCString uristr;
2139       uri->GetAsciiSpec(uristr);
2140       str += nsPrintfCString(" [src=%s]", uristr.get());
2141     }
2142   }
2143   fprintf_stderr(out, "%s\n", str.get());
2144 }
2145 #endif
2146 
2147 nsIFrame::LogicalSides
2148 nsImageFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const
2149 {
2150   if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
2151                      StyleBoxDecorationBreak::Clone)) {
2152     return LogicalSides();
2153   }
2154   LogicalSides skip;
2155   if (nullptr != GetPrevInFlow()) {
2156     skip |= eLogicalSideBitsBStart;
2157   }
2158   if (nullptr != GetNextInFlow()) {
2159     skip |= eLogicalSideBitsBEnd;
2160   }
2161   return skip;
2162 }
2163 
2164 nsresult
2165 nsImageFrame::GetIntrinsicImageSize(nsSize& aSize)
2166 {
2167   if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
2168       mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
2169     aSize.SizeTo(mIntrinsicSize.width.GetCoordValue(),
2170                  mIntrinsicSize.height.GetCoordValue());
2171     return NS_OK;
2172   }
2173 
2174   return NS_ERROR_FAILURE;
2175 }
2176 
2177 nsresult
2178 nsImageFrame::LoadIcon(const nsAString& aSpec,
2179                        nsPresContext *aPresContext,
2180                        imgRequestProxy** aRequest)
2181 {
2182   nsresult rv = NS_OK;
2183   NS_PRECONDITION(!aSpec.IsEmpty(), "What happened??");
2184 
2185   if (!sIOService) {
2186     rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
2187     NS_ENSURE_SUCCESS(rv, rv);
2188   }
2189 
2190   nsCOMPtr<nsIURI> realURI;
2191   SpecToURI(aSpec, sIOService, getter_AddRefs(realURI));
2192 
2193   RefPtr<imgLoader> il =
2194     nsContentUtils::GetImgLoaderForDocument(aPresContext->Document());
2195 
2196   nsCOMPtr<nsILoadGroup> loadGroup;
2197   GetLoadGroup(aPresContext, getter_AddRefs(loadGroup));
2198 
2199   // For icon loads, we don't need to merge with the loadgroup flags
2200   nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
2201   nsContentPolicyType contentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
2202 
2203   return il->LoadImage(realURI,     /* icon URI */
2204                        nullptr,      /* initial document URI; this is only
2205                                        relevant for cookies, so does not
2206                                        apply to icons. */
2207                        nullptr,      /* referrer (not relevant for icons) */
2208                        mozilla::net::RP_Default,
2209                        nullptr,      /* principal (not relevant for icons) */
2210                        loadGroup,
2211                        gIconLoad,
2212                        nullptr,      /* No context */
2213                        nullptr,      /* Not associated with any particular document */
2214                        loadFlags,
2215                        nullptr,
2216                        contentPolicyType,
2217                        EmptyString(),
2218                        aRequest);
2219 }
2220 
2221 void
2222 nsImageFrame::GetDocumentCharacterSet(nsACString& aCharset) const
2223 {
2224   if (mContent) {
2225     NS_ASSERTION(mContent->GetComposedDoc(),
2226                  "Frame still alive after content removed from document!");
2227     aCharset = mContent->GetComposedDoc()->GetDocumentCharacterSet();
2228   }
2229 }
2230 
2231 void
2232 nsImageFrame::SpecToURI(const nsAString& aSpec, nsIIOService *aIOService,
2233                          nsIURI **aURI)
2234 {
2235   nsCOMPtr<nsIURI> baseURI;
2236   if (mContent) {
2237     baseURI = mContent->GetBaseURI();
2238   }
2239   nsAutoCString charset;
2240   GetDocumentCharacterSet(charset);
2241   NS_NewURI(aURI, aSpec,
2242             charset.IsEmpty() ? nullptr : charset.get(),
2243             baseURI, aIOService);
2244 }
2245 
2246 void
2247 nsImageFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
2248 {
2249   if (!aPresContext)
2250     return;
2251 
2252   NS_PRECONDITION(nullptr != aLoadGroup, "null OUT parameter pointer");
2253 
2254   nsIPresShell *shell = aPresContext->GetPresShell();
2255 
2256   if (!shell)
2257     return;
2258 
2259   nsIDocument *doc = shell->GetDocument();
2260   if (!doc)
2261     return;
2262 
2263   *aLoadGroup = doc->GetDocumentLoadGroup().take();
2264 }
2265 
2266 nsresult nsImageFrame::LoadIcons(nsPresContext *aPresContext)
2267 {
2268   NS_ASSERTION(!gIconLoad, "called LoadIcons twice");
2269 
2270   NS_NAMED_LITERAL_STRING(loadingSrc,"resource://gre-resources/loading-image.png");
2271   NS_NAMED_LITERAL_STRING(brokenSrc,"resource://gre-resources/broken-image.png");
2272 
2273   gIconLoad = new IconLoad();
2274   NS_ADDREF(gIconLoad);
2275 
2276   nsresult rv;
2277   // create a loader and load the images
2278   rv = LoadIcon(loadingSrc,
2279                 aPresContext,
2280                 getter_AddRefs(gIconLoad->mLoadingImage));
2281   if (NS_FAILED(rv)) {
2282     return rv;
2283   }
2284 
2285   rv = LoadIcon(brokenSrc,
2286                 aPresContext,
2287                 getter_AddRefs(gIconLoad->mBrokenImage));
2288   if (NS_FAILED(rv)) {
2289     return rv;
2290   }
2291 
2292   return rv;
2293 }
2294 
2295 NS_IMPL_ISUPPORTS(nsImageFrame::IconLoad, nsIObserver,
2296                   imgINotificationObserver)
2297 
2298 static const char* kIconLoadPrefs[] = {
2299   "browser.display.force_inline_alttext",
2300   "browser.display.show_image_placeholders",
2301   "browser.display.show_loading_image_placeholder",
2302   nullptr
2303 };
2304 
2305 nsImageFrame::IconLoad::IconLoad()
2306 {
2307   // register observers
2308   Preferences::AddStrongObservers(this, kIconLoadPrefs);
2309   GetPrefs();
2310 }
2311 
2312 void
2313 nsImageFrame::IconLoad::Shutdown()
2314 {
2315   Preferences::RemoveObservers(this, kIconLoadPrefs);
2316   // in case the pref service releases us later
2317   if (mLoadingImage) {
2318     mLoadingImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
2319     mLoadingImage = nullptr;
2320   }
2321   if (mBrokenImage) {
2322     mBrokenImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
2323     mBrokenImage = nullptr;
2324   }
2325 }
2326 
2327 NS_IMETHODIMP
2328 nsImageFrame::IconLoad::Observe(nsISupports *aSubject, const char* aTopic,
2329                                 const char16_t* aData)
2330 {
2331   NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
2332                "wrong topic");
2333 #ifdef DEBUG
2334   // assert |aData| is one of our prefs.
2335   uint32_t i = 0;
2336   for (; i < ArrayLength(kIconLoadPrefs); ++i) {
2337     if (NS_ConvertASCIItoUTF16(kIconLoadPrefs[i]) == nsDependentString(aData))
2338       break;
2339   }
2340   MOZ_ASSERT(i < ArrayLength(kIconLoadPrefs));
2341 #endif
2342 
2343   GetPrefs();
2344   return NS_OK;
2345 }
2346 
2347 void nsImageFrame::IconLoad::GetPrefs()
2348 {
2349   mPrefForceInlineAltText =
2350     Preferences::GetBool("browser.display.force_inline_alttext");
2351 
2352   mPrefShowPlaceholders =
2353     Preferences::GetBool("browser.display.show_image_placeholders", true);
2354 
2355   mPrefShowLoadingPlaceholder =
2356     Preferences::GetBool("browser.display.show_loading_image_placeholder", true);
2357 }
2358 
2359 NS_IMETHODIMP
2360 nsImageFrame::IconLoad::Notify(imgIRequest* aRequest,
2361                                int32_t aType,
2362                                const nsIntRect* aData)
2363 {
2364   MOZ_ASSERT(aRequest);
2365 
2366   if (aType != imgINotificationObserver::LOAD_COMPLETE &&
2367       aType != imgINotificationObserver::FRAME_UPDATE) {
2368     return NS_OK;
2369   }
2370 
2371   if (aType == imgINotificationObserver::LOAD_COMPLETE) {
2372     nsCOMPtr<imgIContainer> image;
2373     aRequest->GetImage(getter_AddRefs(image));
2374     if (!image) {
2375       return NS_ERROR_FAILURE;
2376     }
2377 
2378     // Retrieve the image's intrinsic size.
2379     int32_t width = 0;
2380     int32_t height = 0;
2381     image->GetWidth(&width);
2382     image->GetHeight(&height);
2383 
2384     // Request a decode at that size.
2385     image->RequestDecodeForSize(IntSize(width, height),
2386                                 imgIContainer::DECODE_FLAGS_DEFAULT);
2387   }
2388 
2389   nsTObserverArray<nsImageFrame*>::ForwardIterator iter(mIconObservers);
2390   nsImageFrame *frame;
2391   while (iter.HasMore()) {
2392     frame = iter.GetNext();
2393     frame->InvalidateFrame();
2394   }
2395 
2396   return NS_OK;
2397 }
2398 
2399 NS_IMPL_ISUPPORTS(nsImageListener, imgINotificationObserver)
2400 
2401 nsImageListener::nsImageListener(nsImageFrame *aFrame) :
2402   mFrame(aFrame)
2403 {
2404 }
2405 
2406 nsImageListener::~nsImageListener()
2407 {
2408 }
2409 
2410 NS_IMETHODIMP
2411 nsImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
2412 {
2413   if (!mFrame)
2414     return NS_ERROR_FAILURE;
2415 
2416   return mFrame->Notify(aRequest, aType, aData);
2417 }
2418 
2419 static bool
2420 IsInAutoWidthTableCellForQuirk(nsIFrame *aFrame)
2421 {
2422   if (eCompatibility_NavQuirks != aFrame->PresContext()->CompatibilityMode())
2423     return false;
2424   // Check if the parent of the closest nsBlockFrame has auto width.
2425   nsBlockFrame *ancestor = nsLayoutUtils::FindNearestBlockAncestor(aFrame);
2426   if (ancestor->StyleContext()->GetPseudo() == nsCSSAnonBoxes::cellContent) {
2427     // Assume direct parent is a table cell frame.
2428     nsFrame *grandAncestor = static_cast<nsFrame*>(ancestor->GetParent());
2429     return grandAncestor &&
2430       grandAncestor->StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto;
2431   }
2432   return false;
2433 }
2434 
2435 /* virtual */ void
2436 nsImageFrame::AddInlineMinISize(nsRenderingContext* aRenderingContext,
2437                                 nsIFrame::InlineMinISizeData* aData)
2438 {
2439   nscoord isize = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
2440                     this, nsLayoutUtils::MIN_ISIZE);
2441   bool canBreak = !IsInAutoWidthTableCellForQuirk(this);
2442   aData->DefaultAddInlineMinISize(this, isize, canBreak);
2443 }
2444