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/. */
6 /* rendering object for replaced elements with bitmap image data */
8 #include "nsImageFrame.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/EventStates.h"
12 #include "mozilla/MouseEvents.h"
15 #include "nsIImageLoadingContent.h"
17 #include "nsPrintfCString.h"
18 #include "nsPresContext.h"
19 #include "nsRenderingContext.h"
20 #include "nsIPresShell.h"
21 #include "nsGkAtoms.h"
22 #include "nsIDocument.h"
23 #include "nsContentUtils.h"
24 #include "nsCSSAnonBoxes.h"
25 #include "nsStyleContext.h"
26 #include "nsStyleConsts.h"
27 #include "nsStyleCoord.h"
28 #include "nsTransform2D.h"
29 #include "nsImageMap.h"
30 #include "nsIIOService.h"
31 #include "nsILoadGroup.h"
32 #include "nsISupportsPriority.h"
33 #include "nsNetUtil.h"
34 #include "nsCSSRendering.h"
35 #include "nsIDOMHTMLAnchorElement.h"
36 #include "nsNameSpaceManager.h"
39 #include "nsAccessibilityService.h"
41 #include "nsIDOMNode.h"
42 #include "nsLayoutUtils.h"
43 #include "nsDisplayList.h"
44 #include "nsIContent.h"
45 #include "nsIDocument.h"
46 #include "FrameLayerBuilder.h"
47 #include "nsISelectionController.h"
48 #include "nsISelection.h"
50 #include "imgIContainer.h"
51 #include "imgLoader.h"
52 #include "imgRequestProxy.h"
54 #include "nsCSSFrameConstructor.h"
55 #include "nsIDOMRange.h"
58 #include "nsBidiUtils.h"
59 #include "nsBidiPresUtils.h"
62 #include "ImageLayers.h"
63 #include "ImageContainer.h"
64 #include "nsStyleSet.h"
65 #include "nsBlockFrame.h"
66 #include "nsStyleStructInlines.h"
68 #include "mozilla/Preferences.h"
70 #include "mozilla/dom/Link.h"
72 using namespace mozilla
;
74 // sizes (pixels) for image icon, padding and border frame
75 #define ICON_SIZE (16)
76 #define ICON_PADDING (3)
77 #define ALT_BORDER_WIDTH (1)
80 //we must add hooks soon
81 #define IMAGE_EDITOR_CHECK 1
83 // Default alignment value (so we can tell an unset value from a set value)
84 #define ALIGN_UNSET uint8_t(-1)
86 using namespace mozilla::layers
;
87 using namespace mozilla::dom
;
89 // static icon information
90 nsImageFrame::IconLoad
* nsImageFrame::gIconLoad
= nullptr;
92 // cached IO service for loading icons
93 nsIIOService
* nsImageFrame::sIOService
;
95 // test if the width and height are fixed, looking at the style data
96 static bool HaveFixedSize(const nsStylePosition
* aStylePosition
)
98 // check the width and height values in the reflow state's style struct
99 // - if width and height are specified as either coord or percentage, then
100 // the size of the image frame is constrained
101 return aStylePosition
->mWidth
.IsCoordPercentCalcUnit() &&
102 aStylePosition
->mHeight
.IsCoordPercentCalcUnit();
104 // use the data in the reflow state to decide if the image has a constrained size
105 // (i.e. width and height that are based on the containing block size and not the image size)
106 // so we can avoid animated GIF related reflows
107 inline bool HaveFixedSize(const nsHTMLReflowState
& aReflowState
)
109 NS_ASSERTION(aReflowState
.mStylePosition
, "crappy reflowState - null stylePosition");
110 // when an image has percent css style height or width, but ComputedHeight()
111 // or ComputedWidth() of reflow state is NS_UNCONSTRAINEDSIZE
112 // it needs to return false to cause an incremental reflow later
113 // if an image is inside table like bug 156731 simple testcase III,
114 // during pass 1 reflow, ComputedWidth() is NS_UNCONSTRAINEDSIZE
115 // in pass 2 reflow, ComputedWidth() is 0, it also needs to return false
117 const nsStyleCoord
&height
= aReflowState
.mStylePosition
->mHeight
;
118 const nsStyleCoord
&width
= aReflowState
.mStylePosition
->mWidth
;
119 return ((height
.HasPercent() &&
120 NS_UNCONSTRAINEDSIZE
== aReflowState
.ComputedHeight()) ||
121 (width
.HasPercent() &&
122 (NS_UNCONSTRAINEDSIZE
== aReflowState
.ComputedWidth() ||
123 0 == aReflowState
.ComputedWidth())))
125 : HaveFixedSize(aReflowState
.mStylePosition
);
129 NS_NewImageFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
131 return new (aPresShell
) nsImageFrame(aContext
);
134 NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame
)
137 nsImageFrame::nsImageFrame(nsStyleContext
* aContext
) :
138 ImageFrameSuper(aContext
),
140 mIntrinsicRatio(0, 0),
141 mDisplayingIcon(false),
142 mFirstFrameComplete(false),
143 mReflowCallbackPosted(false)
145 // We assume our size is not constrained and we haven't gotten an
146 // initial reflow yet, so don't touch those flags.
147 mIntrinsicSize
.width
.SetCoordValue(0);
148 mIntrinsicSize
.height
.SetCoordValue(0);
151 nsImageFrame::~nsImageFrame()
155 NS_QUERYFRAME_HEAD(nsImageFrame
)
156 NS_QUERYFRAME_ENTRY(nsImageFrame
)
157 NS_QUERYFRAME_TAIL_INHERITING(ImageFrameSuper
)
161 nsImageFrame::AccessibleType()
163 // Don't use GetImageMap() to avoid reentrancy into accessibility.
165 return a11y::eHTMLImageMapType
;
168 return a11y::eImageType
;
173 nsImageFrame::DisconnectMap()
176 mImageMap
->Destroy();
177 NS_RELEASE(mImageMap
);
180 nsAccessibilityService
* accService
= GetAccService();
182 accService
->RecreateAccessible(PresContext()->PresShell(), mContent
);
189 nsImageFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
191 if (mReflowCallbackPosted
) {
192 PresContext()->PresShell()->CancelReflowCallback(this);
193 mReflowCallbackPosted
= false;
196 // Tell our image map, if there is one, to clean up
197 // This causes the nsImageMap to unregister itself as
201 // set the frame to null so we don't send messages to a dead object.
203 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
205 // Notify our image loading content that we are going away so it can
206 // deregister with our refresh driver.
207 imageLoader
->FrameDestroyed(this);
209 imageLoader
->RemoveObserver(mListener
);
212 reinterpret_cast<nsImageListener
*>(mListener
.get())->SetFrame(nullptr);
217 // If we were displaying an icon, take ourselves off the list
219 gIconLoad
->RemoveIconObserver(this);
221 nsSplittableFrame::DestroyFrom(aDestructRoot
);
225 nsImageFrame::DidSetStyleContext(nsStyleContext
* aOldStyleContext
)
227 ImageFrameSuper::DidSetStyleContext(aOldStyleContext
);
230 // We'll pick this change up whenever we do get an image.
234 nsStyleImageOrientation newOrientation
= StyleVisibility()->mImageOrientation
;
236 // We need to update our orientation either if we had no style context before
237 // because this is the first time it's been set, or if the image-orientation
238 // property changed from its previous value.
239 bool shouldUpdateOrientation
=
241 aOldStyleContext
->StyleVisibility()->mImageOrientation
!= newOrientation
;
243 if (shouldUpdateOrientation
) {
244 nsCOMPtr
<imgIContainer
> image(mImage
->Unwrap());
245 mImage
= nsLayoutUtils::OrientImage(image
, newOrientation
);
247 UpdateIntrinsicSize(mImage
);
248 UpdateIntrinsicRatio(mImage
);
253 nsImageFrame::Init(nsIContent
* aContent
,
254 nsContainerFrame
* aParent
,
255 nsIFrame
* aPrevInFlow
)
257 nsSplittableFrame::Init(aContent
, aParent
, aPrevInFlow
);
259 mListener
= new nsImageListener(this);
261 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(aContent
);
263 NS_RUNTIMEABORT("Why do we have an nsImageFrame here at all?");
266 imageLoader
->AddObserver(mListener
);
268 nsPresContext
*aPresContext
= PresContext();
271 LoadIcons(aPresContext
);
273 // We have a PresContext now, so we need to notify the image content node
274 // that it can register images.
275 imageLoader
->FrameCreated(this);
277 // Give image loads associated with an image frame a small priority boost!
278 nsCOMPtr
<imgIRequest
> currentRequest
;
279 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
280 getter_AddRefs(currentRequest
));
281 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(currentRequest
);
283 p
->AdjustPriority(-1);
285 // If we already have an image container, OnStartContainer won't be called
286 if (currentRequest
) {
287 nsCOMPtr
<imgIContainer
> image
;
288 currentRequest
->GetImage(getter_AddRefs(image
));
289 OnStartContainer(currentRequest
, image
);
294 nsImageFrame::UpdateIntrinsicSize(imgIContainer
* aImage
)
296 NS_PRECONDITION(aImage
, "null image");
300 IntrinsicSize oldIntrinsicSize
= mIntrinsicSize
;
301 mIntrinsicSize
= IntrinsicSize();
303 // Set intrinsic size to match aImage's reported intrinsic width & height.
304 nsSize intrinsicSize
;
305 if (NS_SUCCEEDED(aImage
->GetIntrinsicSize(&intrinsicSize
))) {
306 // If the image has no intrinsic width, intrinsicSize.width will be -1, and
307 // we can leave mIntrinsicSize.width at its default value of eStyleUnit_None.
308 // Otherwise we use intrinsicSize.width. Height works the same way.
309 if (intrinsicSize
.width
!= -1)
310 mIntrinsicSize
.width
.SetCoordValue(intrinsicSize
.width
);
311 if (intrinsicSize
.height
!= -1)
312 mIntrinsicSize
.height
.SetCoordValue(intrinsicSize
.height
);
314 // Failure means that the image hasn't loaded enough to report a result. We
315 // treat this case as if the image's intrinsic size was 0x0.
316 mIntrinsicSize
.width
.SetCoordValue(0);
317 mIntrinsicSize
.height
.SetCoordValue(0);
320 return mIntrinsicSize
!= oldIntrinsicSize
;
324 nsImageFrame::UpdateIntrinsicRatio(imgIContainer
* aImage
)
326 NS_PRECONDITION(aImage
, "null image");
331 nsSize oldIntrinsicRatio
= mIntrinsicRatio
;
333 // Set intrinsic ratio to match aImage's reported intrinsic ratio.
334 if (NS_FAILED(aImage
->GetIntrinsicRatio(&mIntrinsicRatio
)))
335 mIntrinsicRatio
.SizeTo(0, 0);
337 return mIntrinsicRatio
!= oldIntrinsicRatio
;
341 nsImageFrame::GetSourceToDestTransform(nsTransform2D
& aTransform
)
343 // Set the translation components.
344 // XXXbz does this introduce rounding errors because of the cast to
345 // float? Should we just manually add that stuff in every time
347 nsRect innerArea
= GetInnerArea();
348 aTransform
.SetToTranslate(float(innerArea
.x
),
349 float(innerArea
.y
- GetContinuationOffset()));
351 // Set the scale factors.
352 if (mIntrinsicSize
.width
.GetUnit() == eStyleUnit_Coord
&&
353 mIntrinsicSize
.width
.GetCoordValue() != 0 &&
354 mIntrinsicSize
.height
.GetUnit() == eStyleUnit_Coord
&&
355 mIntrinsicSize
.height
.GetCoordValue() != 0 &&
356 mIntrinsicSize
.width
.GetCoordValue() != mComputedSize
.width
&&
357 mIntrinsicSize
.height
.GetCoordValue() != mComputedSize
.height
) {
359 aTransform
.SetScale(float(mComputedSize
.width
) /
360 float(mIntrinsicSize
.width
.GetCoordValue()),
361 float(mComputedSize
.height
) /
362 float(mIntrinsicSize
.height
.GetCoordValue()));
370 * These two functions basically do the same check. The first one
371 * checks that the given request is the current request for our
372 * mContent. The second checks that the given image container the
373 * same as the image container on the current request for our
377 nsImageFrame::IsPendingLoad(imgIRequest
* aRequest
) const
379 // Default to pending load in case of errors
380 nsCOMPtr
<nsIImageLoadingContent
> imageLoader(do_QueryInterface(mContent
));
381 NS_ASSERTION(imageLoader
, "No image loading content?");
383 int32_t requestType
= nsIImageLoadingContent::UNKNOWN_REQUEST
;
384 imageLoader
->GetRequestType(aRequest
, &requestType
);
386 return requestType
!= nsIImageLoadingContent::CURRENT_REQUEST
;
390 nsImageFrame::IsPendingLoad(imgIContainer
* aContainer
) const
392 // default to pending load in case of errors
394 NS_ERROR("No image container!");
398 nsCOMPtr
<nsIImageLoadingContent
> imageLoader(do_QueryInterface(mContent
));
399 NS_ASSERTION(imageLoader
, "No image loading content?");
401 nsCOMPtr
<imgIRequest
> currentRequest
;
402 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
403 getter_AddRefs(currentRequest
));
404 if (!currentRequest
) {
405 NS_ERROR("No current request");
409 nsCOMPtr
<imgIContainer
> currentContainer
;
410 currentRequest
->GetImage(getter_AddRefs(currentContainer
));
412 return currentContainer
!= aContainer
;
417 nsImageFrame::SourceRectToDest(const nsIntRect
& aRect
)
419 // When scaling the image, row N of the source image may (depending on
420 // the scaling function) be used to draw any row in the destination image
421 // between floor(F * (N-1)) and ceil(F * (N+1)), where F is the
422 // floating-point scaling factor. The same holds true for columns.
423 // So, we start by computing that bound without the floor and ceiling.
425 nsRect
r(nsPresContext::CSSPixelsToAppUnits(aRect
.x
- 1),
426 nsPresContext::CSSPixelsToAppUnits(aRect
.y
- 1),
427 nsPresContext::CSSPixelsToAppUnits(aRect
.width
+ 2),
428 nsPresContext::CSSPixelsToAppUnits(aRect
.height
+ 2));
430 nsTransform2D sourceToDest
;
431 if (!GetSourceToDestTransform(sourceToDest
)) {
432 // Failed to generate transform matrix. Return our whole inner area,
433 // to be on the safe side (since this method is used for generating
434 // invalidation rects).
435 return GetInnerArea();
438 sourceToDest
.TransformCoord(&r
.x
, &r
.y
, &r
.width
, &r
.height
);
440 // Now, round the edges out to the pixel boundary.
441 nscoord scale
= nsPresContext::CSSPixelsToAppUnits(1);
442 nscoord right
= r
.x
+ r
.width
;
443 nscoord bottom
= r
.y
+ r
.height
;
445 r
.x
-= (scale
+ (r
.x
% scale
)) % scale
;
446 r
.y
-= (scale
+ (r
.y
% scale
)) % scale
;
447 r
.width
= right
+ ((scale
- (right
% scale
)) % scale
) - r
.x
;
448 r
.height
= bottom
+ ((scale
- (bottom
% scale
)) % scale
) - r
.y
;
453 // Note that we treat NS_EVENT_STATE_SUPPRESSED images as "OK". This means
454 // that we'll construct image frames for them as needed if their display is
455 // toggled from "none" (though we won't paint them, unless their visibility
457 #define BAD_STATES (NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED | \
458 NS_EVENT_STATE_LOADING)
460 // This is a macro so that we don't evaluate the boolean last arg
461 // unless we have to; it can be expensive
462 #define IMAGE_OK(_state, _loadingOK) \
463 (!(_state).HasAtLeastOneOfStates(BAD_STATES) || \
464 (!(_state).HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED) && \
465 (_state).HasState(NS_EVENT_STATE_LOADING) && (_loadingOK)))
469 nsImageFrame::ShouldCreateImageFrameFor(Element
* aElement
,
470 nsStyleContext
* aStyleContext
)
472 EventStates state
= aElement
->State();
474 HaveFixedSize(aStyleContext
->StylePosition()))) {
475 // Image is fine; do the image frame thing
479 // Check if we want to use a placeholder box with an icon or just
480 // let the presShell make us into inline text. Decide as follows:
482 // - if our special "force icons" style is set, show an icon
483 // - else if our "do not show placeholders" pref is set, skip the icon
485 // - if there is a src attribute, there is no alt attribute,
486 // and this is not an <object> (which could not possibly have
487 // such an attribute), show an icon.
488 // - if QuirksMode, and the IMG has a size show an icon.
489 // - otherwise, skip the icon
492 if (aStyleContext
->StyleUIReset()->mForceBrokenImageIcon
) {
495 else if (gIconLoad
&& gIconLoad
->mPrefForceInlineAltText
) {
498 else if (aElement
->HasAttr(kNameSpaceID_None
, nsGkAtoms::src
) &&
499 !aElement
->HasAttr(kNameSpaceID_None
, nsGkAtoms::alt
) &&
500 !aElement
->IsHTML(nsGkAtoms::object
) &&
501 !aElement
->IsHTML(nsGkAtoms::input
)) {
502 // Use a sized box if we have no alt text. This means no alt attribute
503 // and the node is not an object or an input (since those always have alt
507 else if (aStyleContext
->PresContext()->CompatibilityMode() !=
508 eCompatibility_NavQuirks
) {
512 // check whether we have fixed size
513 useSizedBox
= HaveFixedSize(aStyleContext
->StylePosition());
520 nsImageFrame::Notify(imgIRequest
* aRequest
, int32_t aType
, const nsIntRect
* aData
)
522 if (aType
== imgINotificationObserver::SIZE_AVAILABLE
) {
523 nsCOMPtr
<imgIContainer
> image
;
524 aRequest
->GetImage(getter_AddRefs(image
));
525 return OnStartContainer(aRequest
, image
);
528 if (aType
== imgINotificationObserver::FRAME_UPDATE
) {
529 return OnDataAvailable(aRequest
, aData
);
532 if (aType
== imgINotificationObserver::FRAME_COMPLETE
) {
533 mFirstFrameComplete
= true;
536 if (aType
== imgINotificationObserver::LOAD_COMPLETE
) {
538 aRequest
->GetImageStatus(&imgStatus
);
540 imgStatus
& imgIRequest::STATUS_ERROR
? NS_ERROR_FAILURE
: NS_OK
;
541 return OnStopRequest(aRequest
, status
);
548 SizeIsAvailable(imgIRequest
* aRequest
)
553 uint32_t imageStatus
= 0;
554 nsresult rv
= aRequest
->GetImageStatus(&imageStatus
);
556 return NS_SUCCEEDED(rv
) && (imageStatus
& imgIRequest::STATUS_SIZE_AVAILABLE
);
560 nsImageFrame::OnStartContainer(imgIRequest
*aRequest
, imgIContainer
*aImage
)
562 if (!aImage
) return NS_ERROR_INVALID_ARG
;
564 /* Get requested animation policy from the pres context:
569 nsPresContext
*presContext
= PresContext();
570 aImage
->SetAnimationMode(presContext
->ImageAnimationMode());
572 if (IsPendingLoad(aRequest
)) {
577 bool intrinsicSizeChanged
= false;
578 if (SizeIsAvailable(aRequest
)) {
579 // This is valid and for the current request, so update our stored image
580 // container, orienting according to our style.
581 mImage
= nsLayoutUtils::OrientImage(aImage
, StyleVisibility()->mImageOrientation
);
583 intrinsicSizeChanged
= UpdateIntrinsicSize(mImage
);
584 intrinsicSizeChanged
= UpdateIntrinsicRatio(mImage
) || intrinsicSizeChanged
;
586 // We no longer have a valid image, so release our stored image container.
589 // Have to size to 0,0 so that GetDesiredSize recalculates the size.
590 mIntrinsicSize
.width
.SetCoordValue(0);
591 mIntrinsicSize
.height
.SetCoordValue(0);
592 mIntrinsicRatio
.SizeTo(0, 0);
593 intrinsicSizeChanged
= true;
596 if (intrinsicSizeChanged
&& (mState
& IMAGE_GOTINITIALREFLOW
)) {
597 // Now we need to reflow if we have an unconstrained size and have
598 // already gotten the initial reflow
599 if (!(mState
& IMAGE_SIZECONSTRAINED
)) {
600 nsIPresShell
*presShell
= presContext
->GetPresShell();
601 NS_ASSERTION(presShell
, "No PresShell.");
603 presShell
->FrameNeedsReflow(this, nsIPresShell::eStyleChange
,
613 nsImageFrame::OnDataAvailable(imgIRequest
*aRequest
,
614 const nsIntRect
*aRect
)
616 if (mFirstFrameComplete
) {
617 nsCOMPtr
<imgIContainer
> container
;
618 aRequest
->GetImage(getter_AddRefs(container
));
619 return FrameChanged(aRequest
, container
);
622 // XXX do we need to make sure that the reflow from the
623 // OnStartContainer has been processed before we start calling
626 NS_ENSURE_ARG_POINTER(aRect
);
628 if (!(mState
& IMAGE_GOTINITIALREFLOW
)) {
629 // Don't bother to do anything; we have a reflow coming up!
633 if (IsPendingLoad(aRequest
)) {
638 nsIntRect rect
= mImage
? mImage
->GetImageSpaceInvalidationRect(*aRect
)
642 printf("Source rect (%d,%d,%d,%d)\n",
643 aRect
->x
, aRect
->y
, aRect
->width
, aRect
->height
);
646 if (rect
.IsEqualInterior(nsIntRect::GetMaxSizedIntRect())) {
647 InvalidateFrame(nsDisplayItem::TYPE_IMAGE
);
648 InvalidateFrame(nsDisplayItem::TYPE_ALT_FEEDBACK
);
650 nsRect invalid
= SourceRectToDest(rect
);
651 InvalidateFrameWithRect(invalid
, nsDisplayItem::TYPE_IMAGE
);
652 InvalidateFrameWithRect(invalid
, nsDisplayItem::TYPE_ALT_FEEDBACK
);
659 nsImageFrame::OnStopRequest(imgIRequest
*aRequest
,
662 // Check what request type we're dealing with
663 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
664 NS_ASSERTION(imageLoader
, "Who's notifying us??");
665 int32_t loadType
= nsIImageLoadingContent::UNKNOWN_REQUEST
;
666 imageLoader
->GetRequestType(aRequest
, &loadType
);
667 if (loadType
!= nsIImageLoadingContent::CURRENT_REQUEST
&&
668 loadType
!= nsIImageLoadingContent::PENDING_REQUEST
) {
669 return NS_ERROR_FAILURE
;
672 NotifyNewCurrentRequest(aRequest
, aStatus
);
677 nsImageFrame::NotifyNewCurrentRequest(imgIRequest
*aRequest
,
680 nsCOMPtr
<imgIContainer
> image
;
681 aRequest
->GetImage(getter_AddRefs(image
));
682 NS_ASSERTION(image
|| NS_FAILED(aStatus
), "Successful load with no container?");
684 // May have to switch sizes here!
685 bool intrinsicSizeChanged
= true;
686 if (NS_SUCCEEDED(aStatus
) && image
&& SizeIsAvailable(aRequest
)) {
687 // Update our stored image container, orienting according to our style.
688 mImage
= nsLayoutUtils::OrientImage(image
, StyleVisibility()->mImageOrientation
);
690 intrinsicSizeChanged
= UpdateIntrinsicSize(mImage
);
691 intrinsicSizeChanged
= UpdateIntrinsicRatio(mImage
) || intrinsicSizeChanged
;
693 // We no longer have a valid image, so release our stored image container.
696 // Have to size to 0,0 so that GetDesiredSize recalculates the size
697 mIntrinsicSize
.width
.SetCoordValue(0);
698 mIntrinsicSize
.height
.SetCoordValue(0);
699 mIntrinsicRatio
.SizeTo(0, 0);
702 if (mState
& IMAGE_GOTINITIALREFLOW
) { // do nothing if we haven't gotten the initial reflow yet
703 if (!(mState
& IMAGE_SIZECONSTRAINED
) && intrinsicSizeChanged
) {
704 nsIPresShell
*presShell
= PresContext()->GetPresShell();
706 presShell
->FrameNeedsReflow(this, nsIPresShell::eStyleChange
,
710 // Update border+content to account for image change
716 nsImageFrame::FrameChanged(imgIRequest
*aRequest
,
717 imgIContainer
*aContainer
)
719 if (!StyleVisibility()->IsVisible()) {
723 if (IsPendingLoad(aContainer
)) {
724 // We don't care about it
728 InvalidateLayer(nsDisplayItem::TYPE_IMAGE
);
733 nsImageFrame::EnsureIntrinsicSizeAndRatio(nsPresContext
* aPresContext
)
735 // If mIntrinsicSize.width and height are 0, then we need to update from the
737 if (mIntrinsicSize
.width
.GetUnit() == eStyleUnit_Coord
&&
738 mIntrinsicSize
.width
.GetCoordValue() == 0 &&
739 mIntrinsicSize
.height
.GetUnit() == eStyleUnit_Coord
&&
740 mIntrinsicSize
.height
.GetCoordValue() == 0) {
743 UpdateIntrinsicSize(mImage
);
744 UpdateIntrinsicRatio(mImage
);
746 // image request is null or image size not known, probably an
747 // invalid image specified
748 // - make the image big enough for the icon (it may not be
749 // used if inline alt expansion is used instead)
750 if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT
)) {
751 nscoord edgeLengthToUse
=
752 nsPresContext::CSSPixelsToAppUnits(
753 ICON_SIZE
+ (2 * (ICON_PADDING
+ ALT_BORDER_WIDTH
)));
754 mIntrinsicSize
.width
.SetCoordValue(edgeLengthToUse
);
755 mIntrinsicSize
.height
.SetCoordValue(edgeLengthToUse
);
756 mIntrinsicRatio
.SizeTo(1, 1);
764 nsImageFrame::ComputeSize(nsRenderingContext
*aRenderingContext
,
766 const LogicalSize
& aCBSize
,
767 nscoord aAvailableISize
,
768 const LogicalSize
& aMargin
,
769 const LogicalSize
& aBorder
,
770 const LogicalSize
& aPadding
,
773 nsPresContext
*presContext
= PresContext();
774 EnsureIntrinsicSizeAndRatio(presContext
);
776 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
777 NS_ASSERTION(imageLoader
, "No content node??");
778 mozilla::IntrinsicSize
intrinsicSize(mIntrinsicSize
);
780 // Content may override our default dimensions. This is termed as overriding
781 // the intrinsic size by the spec, but all other consumers of mIntrinsic*
782 // values are being used to refer to the real/true size of the image data.
783 if (imageLoader
&& mImage
&&
784 intrinsicSize
.width
.GetUnit() == eStyleUnit_Coord
&&
785 intrinsicSize
.height
.GetUnit() == eStyleUnit_Coord
) {
788 if (NS_SUCCEEDED(imageLoader
->GetNaturalWidth(&width
)) &&
789 NS_SUCCEEDED(imageLoader
->GetNaturalHeight(&height
))) {
790 nscoord appWidth
= nsPresContext::CSSPixelsToAppUnits((int32_t)width
);
791 nscoord appHeight
= nsPresContext::CSSPixelsToAppUnits((int32_t)height
);
792 // If this image is rotated, we'll need to transpose the natural
795 if (StyleVisibility()->mImageOrientation
.IsFromImage()) {
796 coordFlip
= mImage
->GetOrientation().SwapsWidthAndHeight();
798 coordFlip
= StyleVisibility()->mImageOrientation
.SwapsWidthAndHeight();
800 intrinsicSize
.width
.SetCoordValue(coordFlip
? appHeight
: appWidth
);
801 intrinsicSize
.height
.SetCoordValue(coordFlip
? appWidth
: appHeight
);
805 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(aWM
,
806 aRenderingContext
, this,
807 intrinsicSize
, mIntrinsicRatio
,
815 nsImageFrame::GetInnerArea() const
817 return GetContentRect() - GetPosition();
821 nsImageFrame::GetMapElement() const
824 if (mContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::usemap
, usemap
)) {
825 return mContent
->OwnerDoc()->FindImageMap(usemap
);
830 // get the offset into the content area of the image where aImg starts if it is a continuation.
832 nsImageFrame::GetContinuationOffset() const
835 for (nsIFrame
*f
= GetPrevInFlow(); f
; f
= f
->GetPrevInFlow()) {
836 offset
+= f
->GetContentRect().height
;
838 NS_ASSERTION(offset
>= 0, "bogus GetContentRect");
842 /* virtual */ nscoord
843 nsImageFrame::GetMinISize(nsRenderingContext
*aRenderingContext
)
845 // XXX The caller doesn't account for constraints of the height,
846 // min-height, and max-height properties.
847 DebugOnly
<nscoord
> result
;
848 DISPLAY_MIN_WIDTH(this, result
);
849 nsPresContext
*presContext
= PresContext();
850 EnsureIntrinsicSizeAndRatio(presContext
);
851 return mIntrinsicSize
.width
.GetUnit() == eStyleUnit_Coord
?
852 mIntrinsicSize
.width
.GetCoordValue() : 0;
855 /* virtual */ nscoord
856 nsImageFrame::GetPrefISize(nsRenderingContext
*aRenderingContext
)
858 // XXX The caller doesn't account for constraints of the height,
859 // min-height, and max-height properties.
860 DebugOnly
<nscoord
> result
;
861 DISPLAY_PREF_WIDTH(this, result
);
862 nsPresContext
*presContext
= PresContext();
863 EnsureIntrinsicSizeAndRatio(presContext
);
864 // convert from normal twips to scaled twips (printing...)
865 return mIntrinsicSize
.width
.GetUnit() == eStyleUnit_Coord
?
866 mIntrinsicSize
.width
.GetCoordValue() : 0;
869 /* virtual */ IntrinsicSize
870 nsImageFrame::GetIntrinsicSize()
872 return mIntrinsicSize
;
876 nsImageFrame::GetIntrinsicRatio()
878 return mIntrinsicRatio
;
882 nsImageFrame::Reflow(nsPresContext
* aPresContext
,
883 nsHTMLReflowMetrics
& aMetrics
,
884 const nsHTMLReflowState
& aReflowState
,
885 nsReflowStatus
& aStatus
)
887 DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
888 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aMetrics
, aStatus
);
889 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS
,
890 ("enter nsImageFrame::Reflow: availSize=%d,%d",
891 aReflowState
.AvailableWidth(), aReflowState
.AvailableHeight()));
893 NS_PRECONDITION(mState
& NS_FRAME_IN_REFLOW
, "frame is not in reflow");
895 aStatus
= NS_FRAME_COMPLETE
;
897 // see if we have a frozen size (i.e. a fixed width and height)
898 if (HaveFixedSize(aReflowState
)) {
899 mState
|= IMAGE_SIZECONSTRAINED
;
901 mState
&= ~IMAGE_SIZECONSTRAINED
;
904 // XXXldb These two bits are almost exact opposites (except in the
905 // middle of the initial reflow); remove IMAGE_GOTINITIALREFLOW.
906 if (GetStateBits() & NS_FRAME_FIRST_REFLOW
) {
907 mState
|= IMAGE_GOTINITIALREFLOW
;
911 nsSize(aReflowState
.ComputedWidth(), aReflowState
.ComputedHeight());
913 aMetrics
.Width() = mComputedSize
.width
;
914 aMetrics
.Height() = mComputedSize
.height
;
916 // add borders and padding
917 aMetrics
.Width() += aReflowState
.ComputedPhysicalBorderPadding().LeftRight();
918 aMetrics
.Height() += aReflowState
.ComputedPhysicalBorderPadding().TopBottom();
920 if (GetPrevInFlow()) {
921 aMetrics
.Width() = GetPrevInFlow()->GetSize().width
;
922 nscoord y
= GetContinuationOffset();
923 aMetrics
.Height() -= y
+ aReflowState
.ComputedPhysicalBorderPadding().top
;
924 aMetrics
.Height() = std::max(0, aMetrics
.Height());
928 // we have to split images if we are:
929 // in Paginated mode, we need to have a constrained height, and have a height larger than our available height
930 uint32_t loadStatus
= imgIRequest::STATUS_NONE
;
931 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
932 NS_ASSERTION(imageLoader
, "No content node??");
934 nsCOMPtr
<imgIRequest
> currentRequest
;
935 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
936 getter_AddRefs(currentRequest
));
937 if (currentRequest
) {
938 currentRequest
->GetImageStatus(&loadStatus
);
941 if (aPresContext
->IsPaginated() &&
942 ((loadStatus
& imgIRequest::STATUS_SIZE_AVAILABLE
) || (mState
& IMAGE_SIZECONSTRAINED
)) &&
943 NS_UNCONSTRAINEDSIZE
!= aReflowState
.AvailableHeight() &&
944 aMetrics
.Height() > aReflowState
.AvailableHeight()) {
945 // our desired height was greater than 0, so to avoid infinite
946 // splitting, use 1 pixel as the min
947 aMetrics
.Height() = std::max(nsPresContext::CSSPixelsToAppUnits(1), aReflowState
.AvailableHeight());
948 aStatus
= NS_FRAME_NOT_COMPLETE
;
951 aMetrics
.SetOverflowAreasToDesiredBounds();
952 EventStates contentState
= mContent
->AsElement()->State();
953 bool imageOK
= IMAGE_OK(contentState
, true);
955 // Determine if the size is available
956 bool haveSize
= false;
957 if (loadStatus
& imgIRequest::STATUS_SIZE_AVAILABLE
) {
961 if (!imageOK
|| !haveSize
) {
962 nsRect
altFeedbackSize(0, 0,
963 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
+2*(ICON_PADDING
+ALT_BORDER_WIDTH
)),
964 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
+2*(ICON_PADDING
+ALT_BORDER_WIDTH
)));
965 // We include the altFeedbackSize in our visual overflow, but not in our
966 // scrollable overflow, since it doesn't really need to be scrolled to
967 // outside the image.
968 static_assert(eOverflowType_LENGTH
== 2, "Unknown overflow types?");
969 nsRect
& visualOverflow
= aMetrics
.VisualOverflow();
970 visualOverflow
.UnionRect(visualOverflow
, altFeedbackSize
);
972 FinishAndStoreOverflow(&aMetrics
);
974 if ((GetStateBits() & NS_FRAME_FIRST_REFLOW
) && !mReflowCallbackPosted
) {
975 nsIPresShell
* shell
= PresContext()->PresShell();
976 mReflowCallbackPosted
= true;
977 shell
->PostReflowCallback(this);
980 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS
,
981 ("exit nsImageFrame::Reflow: size=%d,%d",
982 aMetrics
.Width(), aMetrics
.Height()));
983 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aMetrics
);
987 nsImageFrame::ReflowFinished()
989 mReflowCallbackPosted
= false;
991 nsLayoutUtils::UpdateImageVisibilityForFrame(this);
997 nsImageFrame::ReflowCallbackCanceled()
999 mReflowCallbackPosted
= false;
1002 // Computes the width of the specified string. aMaxWidth specifies the maximum
1003 // width available. Once this limit is reached no more characters are measured.
1004 // The number of characters that fit within the maximum width are returned in
1005 // aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected
1006 // into the rendering context before this is called (for performance). MMP
1008 nsImageFrame::MeasureString(const char16_t
* aString
,
1012 nsRenderingContext
& aContext
)
1014 nscoord totalWidth
= 0;
1015 aContext
.SetTextRunRTL(false);
1016 nscoord spaceWidth
= aContext
.GetWidth(' ');
1019 while (aLength
> 0) {
1020 // Find the next place we can line break
1021 uint32_t len
= aLength
;
1022 bool trailingSpace
= false;
1023 for (int32_t i
= 0; i
< aLength
; i
++) {
1024 if (dom::IsSpaceCharacter(aString
[i
]) && (i
> 0)) {
1025 len
= i
; // don't include the space when measuring
1026 trailingSpace
= true;
1031 // Measure this chunk of text, and see if it fits
1033 nsLayoutUtils::GetStringWidth(this, &aContext
, aString
, len
);
1034 bool fits
= (totalWidth
+ width
) <= aMaxWidth
;
1036 // If it fits on the line, or it's the first word we've processed then
1038 if (fits
|| (0 == totalWidth
)) {
1040 totalWidth
+= width
;
1042 // If there's a trailing space then see if it fits as well
1043 if (trailingSpace
) {
1044 if ((totalWidth
+ spaceWidth
) <= aMaxWidth
) {
1045 totalWidth
+= spaceWidth
;
1047 // Space won't fit. Leave it at the end but don't include it in
1067 // Formats the alt-text to fit within the specified rectangle. Breaks lines
1068 // between words if a word would extend past the edge of the rectangle
1070 nsImageFrame::DisplayAltText(nsPresContext
* aPresContext
,
1071 nsRenderingContext
& aRenderingContext
,
1072 const nsString
& aAltText
,
1073 const nsRect
& aRect
)
1075 // Set font and color
1076 aRenderingContext
.SetColor(StyleColor()->mColor
);
1077 nsRefPtr
<nsFontMetrics
> fm
;
1078 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm
),
1079 nsLayoutUtils::FontSizeInflationFor(this));
1080 aRenderingContext
.SetFont(fm
);
1082 // Format the text to display within the formatting rect
1084 nscoord maxAscent
= fm
->MaxAscent();
1085 nscoord maxDescent
= fm
->MaxDescent();
1086 nscoord height
= fm
->MaxHeight();
1088 // XXX It would be nice if there was a way to have the font metrics tell
1089 // use where to break the text given a maximum width. At a minimum we need
1090 // to be able to get the break character...
1091 const char16_t
* str
= aAltText
.get();
1092 int32_t strLen
= aAltText
.Length();
1093 nscoord y
= aRect
.y
;
1095 if (!aPresContext
->BidiEnabled() && HasRTLChars(aAltText
)) {
1096 aPresContext
->SetBidiEnabled();
1099 // Always show the first line, even if we have to clip it below
1100 bool firstLine
= true;
1101 while ((strLen
> 0) && (firstLine
|| (y
+ maxDescent
) < aRect
.YMost())) {
1102 // Determine how much of the text to display on this line
1103 uint32_t maxFit
; // number of characters that fit
1104 nscoord strWidth
= MeasureString(str
, strLen
, aRect
.width
, maxFit
,
1108 nsresult rv
= NS_ERROR_FAILURE
;
1110 if (aPresContext
->BidiEnabled()) {
1111 const nsStyleVisibility
* vis
= StyleVisibility();
1112 if (vis
->mDirection
== NS_STYLE_DIRECTION_RTL
)
1113 rv
= nsBidiPresUtils::RenderText(str
, maxFit
, NSBIDI_RTL
,
1114 aPresContext
, aRenderingContext
,
1116 aRect
.XMost() - strWidth
, y
+ maxAscent
);
1118 rv
= nsBidiPresUtils::RenderText(str
, maxFit
, NSBIDI_LTR
,
1119 aPresContext
, aRenderingContext
,
1121 aRect
.x
, y
+ maxAscent
);
1124 aRenderingContext
.DrawString(str
, maxFit
, aRect
.x
, y
+ maxAscent
);
1126 // Move to the next line
1134 struct nsRecessedBorder
: public nsStyleBorder
{
1135 nsRecessedBorder(nscoord aBorderWidth
, nsPresContext
* aPresContext
)
1136 : nsStyleBorder(aPresContext
)
1138 NS_FOR_CSS_SIDES(side
) {
1139 // Note: use SetBorderColor here because we want to make sure
1140 // the "special" flags are unset.
1141 SetBorderColor(side
, NS_RGB(0, 0, 0));
1142 mBorder
.Side(side
) = aBorderWidth
;
1143 // Note: use SetBorderStyle here because we want to affect
1145 SetBorderStyle(side
, NS_STYLE_BORDER_STYLE_INSET
);
1150 class nsDisplayAltFeedback
: public nsDisplayItem
{
1152 nsDisplayAltFeedback(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
)
1153 : nsDisplayItem(aBuilder
, aFrame
) {}
1155 virtual nsRect
GetBounds(nsDisplayListBuilder
* aBuilder
,
1156 bool* aSnap
) MOZ_OVERRIDE
1159 return mFrame
->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
1162 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
1163 nsRenderingContext
* aCtx
) MOZ_OVERRIDE
1165 nsImageFrame
* f
= static_cast<nsImageFrame
*>(mFrame
);
1166 EventStates state
= f
->GetContent()->AsElement()->State();
1167 f
->DisplayAltFeedback(*aCtx
,
1169 IMAGE_OK(state
, true)
1170 ? nsImageFrame::gIconLoad
->mLoadingImage
1171 : nsImageFrame::gIconLoad
->mBrokenImage
,
1172 ToReferenceFrame());
1176 NS_DISPLAY_DECL_NAME("AltFeedback", TYPE_ALT_FEEDBACK
)
1180 nsImageFrame::DisplayAltFeedback(nsRenderingContext
& aRenderingContext
,
1181 const nsRect
& aDirtyRect
,
1182 imgIRequest
* aRequest
,
1185 // We should definitely have a gIconLoad here.
1186 NS_ABORT_IF_FALSE(gIconLoad
, "How did we succeed in Init then?");
1188 // Calculate the inner area
1189 nsRect inner
= GetInnerArea() + aPt
;
1191 // Display a recessed one pixel border
1192 nscoord borderEdgeWidth
= nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH
);
1194 // if inner area is empty, then make it big enough for at least the icon
1195 if (inner
.IsEmpty()){
1196 inner
.SizeTo(2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
+ICON_PADDING
+ALT_BORDER_WIDTH
)),
1197 2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
+ICON_PADDING
+ALT_BORDER_WIDTH
)));
1200 // Make sure we have enough room to actually render the border within
1202 if ((inner
.width
< 2 * borderEdgeWidth
) || (inner
.height
< 2 * borderEdgeWidth
)) {
1207 nsRecessedBorder
recessedBorder(borderEdgeWidth
, PresContext());
1208 nsCSSRendering::PaintBorderWithStyleBorder(PresContext(), aRenderingContext
,
1210 recessedBorder
, mStyleContext
);
1212 // Adjust the inner rect to account for the one pixel recessed border,
1213 // and a six pixel padding on each edge
1214 inner
.Deflate(nsPresContext::CSSPixelsToAppUnits(ICON_PADDING
+ALT_BORDER_WIDTH
),
1215 nsPresContext::CSSPixelsToAppUnits(ICON_PADDING
+ALT_BORDER_WIDTH
));
1216 if (inner
.IsEmpty()) {
1220 // Clip so we don't render outside the inner rect
1221 aRenderingContext
.PushState();
1222 aRenderingContext
.IntersectClip(inner
);
1224 // Check if we should display image placeholders
1225 if (gIconLoad
->mPrefShowPlaceholders
) {
1226 const nsStyleVisibility
* vis
= StyleVisibility();
1227 nscoord size
= nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
);
1229 bool iconUsed
= false;
1231 // If we weren't previously displaying an icon, register ourselves
1232 // as an observer for load and animation updates and flag that we're
1234 if (aRequest
&& !mDisplayingIcon
) {
1235 gIconLoad
->AddIconObserver(this);
1236 mDisplayingIcon
= true;
1240 // If the icon in question is loaded and decoded, draw it
1241 uint32_t imageStatus
= 0;
1243 aRequest
->GetImageStatus(&imageStatus
);
1244 if (imageStatus
& imgIRequest::STATUS_FRAME_COMPLETE
) {
1245 nsCOMPtr
<imgIContainer
> imgCon
;
1246 aRequest
->GetImage(getter_AddRefs(imgCon
));
1247 NS_ABORT_IF_FALSE(imgCon
, "Frame Complete, but no image container?");
1248 nsRect
dest((vis
->mDirection
== NS_STYLE_DIRECTION_RTL
) ?
1249 inner
.XMost() - size
: inner
.x
,
1250 inner
.y
, size
, size
);
1251 nsLayoutUtils::DrawSingleImage(&aRenderingContext
, PresContext(), imgCon
,
1252 nsLayoutUtils::GetGraphicsFilterForFrame(this), dest
, aDirtyRect
,
1253 nullptr, imgIContainer::FLAG_NONE
);
1257 // if we could not draw the icon, flag that we're waiting for it and
1258 // just draw some graffiti in the mean time
1260 nscoord iconXPos
= (vis
->mDirection
== NS_STYLE_DIRECTION_RTL
) ?
1261 inner
.XMost() - size
: inner
.x
;
1262 nscoord twoPX
= nsPresContext::CSSPixelsToAppUnits(2);
1263 aRenderingContext
.DrawRect(iconXPos
, inner
.y
,size
,size
);
1264 aRenderingContext
.PushState();
1265 aRenderingContext
.SetColor(NS_RGB(0xFF,0,0));
1266 aRenderingContext
.FillEllipse(size
/2 + iconXPos
, size
/2 + inner
.y
,
1267 size
/2 - twoPX
, size
/2 - twoPX
);
1268 aRenderingContext
.PopState();
1271 // Reduce the inner rect by the width of the icon, and leave an
1272 // additional ICON_PADDING pixels for padding
1273 int32_t iconWidth
= nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
+ ICON_PADDING
);
1274 if (vis
->mDirection
!= NS_STYLE_DIRECTION_RTL
)
1275 inner
.x
+= iconWidth
;
1276 inner
.width
-= iconWidth
;
1279 // If there's still room, display the alt-text
1280 if (!inner
.IsEmpty()) {
1281 nsIContent
* content
= GetContent();
1283 nsXPIDLString altText
;
1284 nsCSSFrameConstructor::GetAlternateTextFor(content
, content
->Tag(),
1286 DisplayAltText(PresContext(), aRenderingContext
, altText
, inner
);
1290 aRenderingContext
.PopState();
1294 static void PaintDebugImageMap(nsIFrame
* aFrame
, nsRenderingContext
* aCtx
,
1295 const nsRect
& aDirtyRect
, nsPoint aPt
) {
1296 nsImageFrame
* f
= static_cast<nsImageFrame
*>(aFrame
);
1297 nsRect inner
= f
->GetInnerArea() + aPt
;
1299 aCtx
->SetColor(NS_RGB(0, 0, 0));
1301 aCtx
->Translate(inner
.TopLeft());
1302 f
->GetImageMap()->Draw(aFrame
, *aCtx
);
1308 nsDisplayImage::Paint(nsDisplayListBuilder
* aBuilder
,
1309 nsRenderingContext
* aCtx
) {
1310 uint32_t flags
= imgIContainer::FLAG_NONE
;
1311 if (aBuilder
->ShouldSyncDecodeImages()) {
1312 flags
|= imgIContainer::FLAG_SYNC_DECODE
;
1314 if (aBuilder
->IsPaintingToWindow()) {
1315 flags
|= imgIContainer::FLAG_HIGH_QUALITY_SCALING
;
1317 static_cast<nsImageFrame
*>(mFrame
)->
1318 PaintImage(*aCtx
, ToReferenceFrame(), mVisibleRect
, mImage
, flags
);
1322 nsDisplayImage::ComputeInvalidationRegion(nsDisplayListBuilder
* aBuilder
,
1323 const nsDisplayItemGeometry
* aGeometry
,
1324 nsRegion
* aInvalidRegion
)
1326 if (aBuilder
->ShouldSyncDecodeImages() && mImage
&& !mImage
->IsDecoded()) {
1328 aInvalidRegion
->Or(*aInvalidRegion
, GetBounds(aBuilder
, &snap
));
1331 nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder
, aGeometry
, aInvalidRegion
);
1334 already_AddRefed
<ImageContainer
>
1335 nsDisplayImage::GetContainer(LayerManager
* aManager
,
1336 nsDisplayListBuilder
* aBuilder
)
1338 nsRefPtr
<ImageContainer
> container
;
1339 nsresult rv
= mImage
->GetImageContainer(aManager
, getter_AddRefs(container
));
1340 NS_ENSURE_SUCCESS(rv
, nullptr);
1341 return container
.forget();
1345 nsDisplayImage::GetDestRect()
1347 int32_t factor
= mFrame
->PresContext()->AppUnitsPerDevPixel();
1348 nsImageFrame
* imageFrame
= static_cast<nsImageFrame
*>(mFrame
);
1350 nsRect dest
= imageFrame
->GetInnerArea() + ToReferenceFrame();
1351 gfxRect
destRect(dest
.x
, dest
.y
, dest
.width
, dest
.height
);
1352 destRect
.ScaleInverse(factor
);
1358 nsDisplayImage::GetLayerState(nsDisplayListBuilder
* aBuilder
,
1359 LayerManager
* aManager
,
1360 const ContainerLayerParameters
& aParameters
)
1362 bool animated
= false;
1363 if (!nsLayoutUtils::AnimatedImageLayersEnabled() ||
1364 mImage
->GetType() != imgIContainer::TYPE_RASTER
||
1365 NS_FAILED(mImage
->GetAnimated(&animated
)) ||
1367 if (!aManager
->IsCompositingCheap() ||
1368 !nsLayoutUtils::GPUImageScalingEnabled()) {
1375 int32_t imageHeight
;
1376 mImage
->GetWidth(&imageWidth
);
1377 mImage
->GetHeight(&imageHeight
);
1379 NS_ASSERTION(imageWidth
!= 0 && imageHeight
!= 0, "Invalid image size!");
1381 gfxRect destRect
= GetDestRect();
1383 destRect
.width
*= aParameters
.mXScale
;
1384 destRect
.height
*= aParameters
.mYScale
;
1386 // Calculate the scaling factor for the frame.
1387 gfxSize scale
= gfxSize(destRect
.width
/ imageWidth
,
1388 destRect
.height
/ imageHeight
);
1390 // If we are not scaling at all, no point in separating this into a layer.
1391 if (scale
.width
== 1.0f
&& scale
.height
== 1.0f
) {
1395 // If the target size is pretty small, no point in using a layer.
1396 if (destRect
.width
* destRect
.height
< 64 * 64) {
1401 nsRefPtr
<ImageContainer
> container
;
1402 mImage
->GetImageContainer(aManager
, getter_AddRefs(container
));
1407 return LAYER_ACTIVE
;
1410 already_AddRefed
<Layer
>
1411 nsDisplayImage::BuildLayer(nsDisplayListBuilder
* aBuilder
,
1412 LayerManager
* aManager
,
1413 const ContainerLayerParameters
& aParameters
)
1415 nsRefPtr
<ImageContainer
> container
;
1416 nsresult rv
= mImage
->GetImageContainer(aManager
, getter_AddRefs(container
));
1417 NS_ENSURE_SUCCESS(rv
, nullptr);
1419 nsRefPtr
<ImageLayer
> layer
= static_cast<ImageLayer
*>
1420 (aManager
->GetLayerBuilder()->GetLeafLayerFor(aBuilder
, this));
1422 layer
= aManager
->CreateImageLayer();
1426 layer
->SetContainer(container
);
1427 ConfigureLayer(layer
, aParameters
.mOffset
);
1428 return layer
.forget();
1432 nsDisplayImage::ConfigureLayer(ImageLayer
*aLayer
, const nsIntPoint
& aOffset
)
1434 aLayer
->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame
));
1437 int32_t imageHeight
;
1438 mImage
->GetWidth(&imageWidth
);
1439 mImage
->GetHeight(&imageHeight
);
1441 NS_ASSERTION(imageWidth
!= 0 && imageHeight
!= 0, "Invalid image size!");
1443 const gfxRect destRect
= GetDestRect();
1445 gfx::Matrix transform
;
1446 gfxPoint p
= destRect
.TopLeft() + aOffset
;
1447 transform
.Translate(p
.x
, p
.y
);
1448 transform
.Scale(destRect
.Width()/imageWidth
,
1449 destRect
.Height()/imageHeight
);
1450 aLayer
->SetBaseTransform(gfx::Matrix4x4::From2D(transform
));
1454 nsImageFrame::PaintImage(nsRenderingContext
& aRenderingContext
, nsPoint aPt
,
1455 const nsRect
& aDirtyRect
, imgIContainer
* aImage
,
1458 // Render the image into our content area (the area inside
1459 // the borders and padding)
1460 NS_ASSERTION(GetInnerArea().width
== mComputedSize
.width
, "bad width");
1461 nsRect inner
= GetInnerArea() + aPt
;
1462 nsRect
dest(inner
.TopLeft(), mComputedSize
);
1463 dest
.y
-= GetContinuationOffset();
1465 nsLayoutUtils::DrawSingleImage(&aRenderingContext
, PresContext(), aImage
,
1466 nsLayoutUtils::GetGraphicsFilterForFrame(this), dest
, aDirtyRect
,
1469 nsImageMap
* map
= GetImageMap();
1470 if (nullptr != map
) {
1471 aRenderingContext
.PushState();
1472 aRenderingContext
.Translate(inner
.TopLeft());
1473 aRenderingContext
.SetColor(NS_RGB(255, 255, 255));
1474 aRenderingContext
.SetLineStyle(nsLineStyle_kSolid
);
1475 map
->Draw(this, aRenderingContext
);
1476 aRenderingContext
.SetColor(NS_RGB(0, 0, 0));
1477 aRenderingContext
.SetLineStyle(nsLineStyle_kDotted
);
1478 map
->Draw(this, aRenderingContext
);
1479 aRenderingContext
.PopState();
1484 nsImageFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
1485 const nsRect
& aDirtyRect
,
1486 const nsDisplayListSet
& aLists
)
1488 if (!IsVisibleForPainting(aBuilder
))
1491 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
1493 DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
1494 clip(aBuilder
, this, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT
);
1496 if (mComputedSize
.width
!= 0 && mComputedSize
.height
!= 0) {
1497 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
1498 NS_ASSERTION(imageLoader
, "Not an image loading content?");
1500 nsCOMPtr
<imgIRequest
> currentRequest
;
1502 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
1503 getter_AddRefs(currentRequest
));
1506 EventStates contentState
= mContent
->AsElement()->State();
1507 bool imageOK
= IMAGE_OK(contentState
, true);
1509 // XXX(seth): The SizeIsAvailable check here should not be necessary - the
1510 // intention is that a non-null mImage means we have a size, but there is
1511 // currently some code that violates this invariant.
1512 if (!imageOK
|| !mImage
|| !SizeIsAvailable(currentRequest
)) {
1513 // No image yet, or image load failed. Draw the alt-text and an icon
1514 // indicating the status
1515 aLists
.Content()->AppendNewToTop(new (aBuilder
)
1516 nsDisplayAltFeedback(aBuilder
, this));
1518 aLists
.Content()->AppendNewToTop(new (aBuilder
)
1519 nsDisplayImage(aBuilder
, this, mImage
));
1521 // If we were previously displaying an icon, we're not anymore
1522 if (mDisplayingIcon
) {
1523 gIconLoad
->RemoveIconObserver(this);
1524 mDisplayingIcon
= false;
1528 if (GetShowFrameBorders() && GetImageMap()) {
1529 aLists
.Outlines()->AppendNewToTop(new (aBuilder
)
1530 nsDisplayGeneric(aBuilder
, this, PaintDebugImageMap
, "DebugImageMap",
1531 nsDisplayItem::TYPE_DEBUG_IMAGE_MAP
));
1537 if (ShouldDisplaySelection()) {
1538 DisplaySelectionOverlay(aBuilder
, aLists
.Content(),
1539 nsISelectionDisplay::DISPLAY_IMAGES
);
1544 nsImageFrame::ShouldDisplaySelection()
1546 // XXX what on EARTH is this code for?
1548 nsPresContext
* presContext
= PresContext();
1549 int16_t displaySelection
= presContext
->PresShell()->GetSelectionFlags();
1550 if (!(displaySelection
& nsISelectionDisplay::DISPLAY_IMAGES
))
1551 return false;//no need to check the blue border, we cannot be drawn selected
1552 //insert hook here for image selection drawing
1553 #if IMAGE_EDITOR_CHECK
1554 //check to see if this frame is in an editor context
1555 //isEditor check. this needs to be changed to have better way to check
1556 if (displaySelection
== nsISelectionDisplay::DISPLAY_ALL
)
1558 nsCOMPtr
<nsISelectionController
> selCon
;
1559 result
= GetSelectionController(presContext
, getter_AddRefs(selCon
));
1560 if (NS_SUCCEEDED(result
) && selCon
)
1562 nsCOMPtr
<nsISelection
> selection
;
1563 result
= selCon
->GetSelection(nsISelectionController::SELECTION_NORMAL
, getter_AddRefs(selection
));
1564 if (NS_SUCCEEDED(result
) && selection
)
1567 selection
->GetRangeCount(&rangeCount
);
1568 if (rangeCount
== 1) //if not one then let code drop to nsFrame::Paint
1570 nsCOMPtr
<nsIContent
> parentContent
= mContent
->GetParent();
1573 int32_t thisOffset
= parentContent
->IndexOf(mContent
);
1574 nsCOMPtr
<nsIDOMNode
> parentNode
= do_QueryInterface(parentContent
);
1575 nsCOMPtr
<nsIDOMNode
> rangeNode
;
1576 int32_t rangeOffset
;
1577 nsCOMPtr
<nsIDOMRange
> range
;
1578 selection
->GetRangeAt(0,getter_AddRefs(range
));
1581 range
->GetStartContainer(getter_AddRefs(rangeNode
));
1582 range
->GetStartOffset(&rangeOffset
);
1584 if (parentNode
&& rangeNode
&& (rangeNode
== parentNode
) && rangeOffset
== thisOffset
)
1586 range
->GetEndContainer(getter_AddRefs(rangeNode
));
1587 range
->GetEndOffset(&rangeOffset
);
1588 if ((rangeNode
== parentNode
) && (rangeOffset
== (thisOffset
+1))) //+1 since that would mean this whole content is selected only
1589 return false; //do not allow nsFrame do draw any further selection
1602 nsImageFrame::GetImageMap()
1605 nsIContent
* map
= GetMapElement();
1607 mImageMap
= new nsImageMap();
1608 NS_ADDREF(mImageMap
);
1609 mImageMap
->Init(this, map
);
1617 nsImageFrame::IsServerImageMap()
1619 return mContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::ismap
);
1622 // Translate an point that is relative to our frame
1623 // into a localized pixel coordinate that is relative to the
1624 // content area of this frame (inside the border+padding).
1626 nsImageFrame::TranslateEventCoords(const nsPoint
& aPoint
,
1627 nsIntPoint
& aResult
)
1629 nscoord x
= aPoint
.x
;
1630 nscoord y
= aPoint
.y
;
1632 // Subtract out border and padding here so that the coordinates are
1633 // now relative to the content area of this frame.
1634 nsRect inner
= GetInnerArea();
1638 aResult
.x
= nsPresContext::AppUnitsToIntCSSPixels(x
);
1639 aResult
.y
= nsPresContext::AppUnitsToIntCSSPixels(y
);
1643 nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI
** aHref
, nsString
& aTarget
,
1646 bool status
= false;
1651 // Walk up the content tree, looking for an nsIDOMAnchorElement
1652 for (nsIContent
* content
= mContent
->GetParent();
1653 content
; content
= content
->GetParent()) {
1654 nsCOMPtr
<dom::Link
> link(do_QueryInterface(content
));
1656 nsCOMPtr
<nsIURI
> href
= content
->GetHrefURI();
1660 status
= (*aHref
!= nullptr);
1662 nsCOMPtr
<nsIDOMHTMLAnchorElement
> anchor(do_QueryInterface(content
));
1664 anchor
->GetTarget(aTarget
);
1666 NS_ADDREF(*aNode
= content
);
1674 nsImageFrame::GetContentForEvent(WidgetEvent
* aEvent
,
1675 nsIContent
** aContent
)
1677 NS_ENSURE_ARG_POINTER(aContent
);
1679 nsIFrame
* f
= nsLayoutUtils::GetNonGeneratedAncestor(this);
1681 return f
->GetContentForEvent(aEvent
, aContent
);
1684 // XXX We need to make this special check for area element's capturing the
1685 // mouse due to bug 135040. Remove it once that's fixed.
1686 nsIContent
* capturingContent
=
1687 aEvent
->HasMouseEventMessage() ? nsIPresShell::GetCapturingContent() :
1689 if (capturingContent
&& capturingContent
->GetPrimaryFrame() == this) {
1690 *aContent
= capturingContent
;
1691 NS_IF_ADDREF(*aContent
);
1695 nsImageMap
* map
= GetImageMap();
1697 if (nullptr != map
) {
1699 TranslateEventCoords(
1700 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent
, this), p
);
1701 nsCOMPtr
<nsIContent
> area
= map
->GetArea(p
.x
, p
.y
);
1703 area
.forget(aContent
);
1708 *aContent
= GetContent();
1709 NS_IF_ADDREF(*aContent
);
1713 // XXX what should clicks on transparent pixels do?
1715 nsImageFrame::HandleEvent(nsPresContext
* aPresContext
,
1716 WidgetGUIEvent
* aEvent
,
1717 nsEventStatus
* aEventStatus
)
1719 NS_ENSURE_ARG_POINTER(aEventStatus
);
1721 if ((aEvent
->message
== NS_MOUSE_BUTTON_UP
&&
1722 aEvent
->AsMouseEvent()->button
== WidgetMouseEvent::eLeftButton
) ||
1723 aEvent
->message
== NS_MOUSE_MOVE
) {
1724 nsImageMap
* map
= GetImageMap();
1725 bool isServerMap
= IsServerImageMap();
1726 if ((nullptr != map
) || isServerMap
) {
1728 TranslateEventCoords(
1729 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent
, this), p
);
1730 bool inside
= false;
1731 // Even though client-side image map triggering happens
1732 // through content, we need to make sure we're not inside
1733 // (in case we deal with a case of both client-side and
1734 // sever-side on the same image - it happens!)
1735 if (nullptr != map
) {
1736 inside
= !!map
->GetArea(p
.x
, p
.y
);
1739 if (!inside
&& isServerMap
) {
1741 // Server side image maps use the href in a containing anchor
1742 // element to provide the basis for the destination url.
1743 nsCOMPtr
<nsIURI
> uri
;
1744 nsAutoString target
;
1745 nsCOMPtr
<nsIContent
> anchorNode
;
1746 if (GetAnchorHREFTargetAndNode(getter_AddRefs(uri
), target
,
1747 getter_AddRefs(anchorNode
))) {
1748 // XXX if the mouse is over/clicked in the border/padding area
1749 // we should probably just pretend nothing happened. Nav4
1750 // keeps the x,y coordinates positive as we do; IE doesn't
1751 // bother. Both of them send the click through even when the
1752 // mouse is over the border.
1753 if (p
.x
< 0) p
.x
= 0;
1754 if (p
.y
< 0) p
.y
= 0;
1757 spec
+= nsPrintfCString("?%d,%d", p
.x
, p
.y
);
1760 bool clicked
= false;
1761 if (aEvent
->message
== NS_MOUSE_BUTTON_UP
) {
1762 *aEventStatus
= nsEventStatus_eConsumeDoDefault
;
1765 nsContentUtils::TriggerLink(anchorNode
, aPresContext
, uri
, target
,
1766 clicked
, true, true);
1772 return nsSplittableFrame::HandleEvent(aPresContext
, aEvent
, aEventStatus
);
1776 nsImageFrame::GetCursor(const nsPoint
& aPoint
,
1777 nsIFrame::Cursor
& aCursor
)
1779 nsImageMap
* map
= GetImageMap();
1780 if (nullptr != map
) {
1782 TranslateEventCoords(aPoint
, p
);
1783 nsCOMPtr
<nsIContent
> area
= map
->GetArea(p
.x
, p
.y
);
1785 // Use the cursor from the style of the *area* element.
1786 // XXX Using the image as the parent style context isn't
1787 // technically correct, but it's probably the right thing to do
1788 // here, since it means that areas on which the cursor isn't
1789 // specified will inherit the style from the image.
1790 nsRefPtr
<nsStyleContext
> areaStyle
=
1791 PresContext()->PresShell()->StyleSet()->
1792 ResolveStyleFor(area
->AsElement(), StyleContext());
1793 FillCursorInformationFromStyle(areaStyle
->StyleUserInterface(),
1795 if (NS_STYLE_CURSOR_AUTO
== aCursor
.mCursor
) {
1796 aCursor
.mCursor
= NS_STYLE_CURSOR_DEFAULT
;
1801 return nsFrame::GetCursor(aPoint
, aCursor
);
1805 nsImageFrame::AttributeChanged(int32_t aNameSpaceID
,
1806 nsIAtom
* aAttribute
,
1809 nsresult rv
= nsSplittableFrame::AttributeChanged(aNameSpaceID
,
1810 aAttribute
, aModType
);
1811 if (NS_FAILED(rv
)) {
1814 if (nsGkAtoms::alt
== aAttribute
)
1816 PresContext()->PresShell()->FrameNeedsReflow(this,
1817 nsIPresShell::eStyleChange
,
1825 nsImageFrame::GetType() const
1827 return nsGkAtoms::imageFrame
;
1830 #ifdef DEBUG_FRAME_DUMP
1832 nsImageFrame::GetFrameName(nsAString
& aResult
) const
1834 return MakeFrameName(NS_LITERAL_STRING("ImageFrame"), aResult
);
1838 nsImageFrame::List(FILE* out
, const char* aPrefix
, uint32_t aFlags
) const
1841 ListGeneric(str
, aPrefix
, aFlags
);
1843 // output the img src url
1844 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
1846 nsCOMPtr
<imgIRequest
> currentRequest
;
1847 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
1848 getter_AddRefs(currentRequest
));
1849 if (currentRequest
) {
1850 nsCOMPtr
<nsIURI
> uri
;
1851 currentRequest
->GetURI(getter_AddRefs(uri
));
1852 nsAutoCString uristr
;
1853 uri
->GetAsciiSpec(uristr
);
1854 str
+= nsPrintfCString(" [src=%s]", uristr
.get());
1857 fprintf_stderr(out
, "%s\n", str
.get());
1861 nsIFrame::LogicalSides
1862 nsImageFrame::GetLogicalSkipSides(const nsHTMLReflowState
* aReflowState
) const
1864 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak
==
1865 NS_STYLE_BOX_DECORATION_BREAK_CLONE
)) {
1866 return LogicalSides();
1869 if (nullptr != GetPrevInFlow()) {
1870 skip
|= eLogicalSideBitsBStart
;
1872 if (nullptr != GetNextInFlow()) {
1873 skip
|= eLogicalSideBitsBEnd
;
1879 nsImageFrame::GetIntrinsicImageSize(nsSize
& aSize
)
1881 if (mIntrinsicSize
.width
.GetUnit() == eStyleUnit_Coord
&&
1882 mIntrinsicSize
.height
.GetUnit() == eStyleUnit_Coord
) {
1883 aSize
.SizeTo(mIntrinsicSize
.width
.GetCoordValue(),
1884 mIntrinsicSize
.height
.GetCoordValue());
1888 return NS_ERROR_FAILURE
;
1892 nsImageFrame::LoadIcon(const nsAString
& aSpec
,
1893 nsPresContext
*aPresContext
,
1894 imgRequestProxy
** aRequest
)
1896 nsresult rv
= NS_OK
;
1897 NS_PRECONDITION(!aSpec
.IsEmpty(), "What happened??");
1900 rv
= CallGetService(NS_IOSERVICE_CONTRACTID
, &sIOService
);
1901 NS_ENSURE_SUCCESS(rv
, rv
);
1904 nsCOMPtr
<nsIURI
> realURI
;
1905 SpecToURI(aSpec
, sIOService
, getter_AddRefs(realURI
));
1907 nsRefPtr
<imgLoader
> il
=
1908 nsContentUtils::GetImgLoaderForDocument(aPresContext
->Document());
1910 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1911 GetLoadGroup(aPresContext
, getter_AddRefs(loadGroup
));
1913 // For icon loads, we don't need to merge with the loadgroup flags
1914 nsLoadFlags loadFlags
= nsIRequest::LOAD_NORMAL
;
1916 return il
->LoadImage(realURI
, /* icon URI */
1917 nullptr, /* initial document URI; this is only
1918 relevant for cookies, so does not
1920 nullptr, /* referrer (not relevant for icons) */
1921 nullptr, /* principal (not relevant for icons) */
1924 nullptr, /* Not associated with any particular document */
1927 nullptr, /* channel policy not needed */
1933 nsImageFrame::GetDocumentCharacterSet(nsACString
& aCharset
) const
1936 NS_ASSERTION(mContent
->GetComposedDoc(),
1937 "Frame still alive after content removed from document!");
1938 aCharset
= mContent
->GetComposedDoc()->GetDocumentCharacterSet();
1943 nsImageFrame::SpecToURI(const nsAString
& aSpec
, nsIIOService
*aIOService
,
1946 nsCOMPtr
<nsIURI
> baseURI
;
1948 baseURI
= mContent
->GetBaseURI();
1950 nsAutoCString charset
;
1951 GetDocumentCharacterSet(charset
);
1952 NS_NewURI(aURI
, aSpec
,
1953 charset
.IsEmpty() ? nullptr : charset
.get(),
1954 baseURI
, aIOService
);
1958 nsImageFrame::GetLoadGroup(nsPresContext
*aPresContext
, nsILoadGroup
**aLoadGroup
)
1963 NS_PRECONDITION(nullptr != aLoadGroup
, "null OUT parameter pointer");
1965 nsIPresShell
*shell
= aPresContext
->GetPresShell();
1970 nsIDocument
*doc
= shell
->GetDocument();
1974 *aLoadGroup
= doc
->GetDocumentLoadGroup().take();
1977 nsresult
nsImageFrame::LoadIcons(nsPresContext
*aPresContext
)
1979 NS_ASSERTION(!gIconLoad
, "called LoadIcons twice");
1981 NS_NAMED_LITERAL_STRING(loadingSrc
,"resource://gre-resources/loading-image.png");
1982 NS_NAMED_LITERAL_STRING(brokenSrc
,"resource://gre-resources/broken-image.png");
1984 gIconLoad
= new IconLoad();
1985 NS_ADDREF(gIconLoad
);
1988 // create a loader and load the images
1989 rv
= LoadIcon(loadingSrc
,
1991 getter_AddRefs(gIconLoad
->mLoadingImage
));
1992 if (NS_FAILED(rv
)) {
1996 rv
= LoadIcon(brokenSrc
,
1998 getter_AddRefs(gIconLoad
->mBrokenImage
));
2002 NS_IMPL_ISUPPORTS(nsImageFrame::IconLoad
, nsIObserver
,
2003 imgINotificationObserver
)
2005 static const char* kIconLoadPrefs
[] = {
2006 "browser.display.force_inline_alttext",
2007 "browser.display.show_image_placeholders",
2011 nsImageFrame::IconLoad::IconLoad()
2013 // register observers
2014 Preferences::AddStrongObservers(this, kIconLoadPrefs
);
2019 nsImageFrame::IconLoad::Shutdown()
2021 Preferences::RemoveObservers(this, kIconLoadPrefs
);
2022 // in case the pref service releases us later
2023 if (mLoadingImage
) {
2024 mLoadingImage
->CancelAndForgetObserver(NS_ERROR_FAILURE
);
2025 mLoadingImage
= nullptr;
2028 mBrokenImage
->CancelAndForgetObserver(NS_ERROR_FAILURE
);
2029 mBrokenImage
= nullptr;
2034 nsImageFrame::IconLoad::Observe(nsISupports
*aSubject
, const char* aTopic
,
2035 const char16_t
* aData
)
2037 NS_ASSERTION(!nsCRT::strcmp(aTopic
, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
),
2040 // assert |aData| is one of our prefs.
2041 for (uint32_t i
= 0; i
< ArrayLength(kIconLoadPrefs
) ||
2042 (NS_NOTREACHED("wrong pref"), false); ++i
)
2043 if (NS_ConvertASCIItoUTF16(kIconLoadPrefs
[i
]) == nsDependentString(aData
))
2051 void nsImageFrame::IconLoad::GetPrefs()
2053 mPrefForceInlineAltText
=
2054 Preferences::GetBool("browser.display.force_inline_alttext");
2056 mPrefShowPlaceholders
=
2057 Preferences::GetBool("browser.display.show_image_placeholders", true);
2061 nsImageFrame::IconLoad::Notify(imgIRequest
*aRequest
, int32_t aType
, const nsIntRect
* aData
)
2063 if (aType
!= imgINotificationObserver::LOAD_COMPLETE
&&
2064 aType
!= imgINotificationObserver::FRAME_UPDATE
) {
2068 nsTObserverArray
<nsImageFrame
*>::ForwardIterator
iter(mIconObservers
);
2069 nsImageFrame
*frame
;
2070 while (iter
.HasMore()) {
2071 frame
= iter
.GetNext();
2072 frame
->InvalidateFrame();
2078 NS_IMPL_ISUPPORTS(nsImageListener
, imgINotificationObserver
)
2080 nsImageListener::nsImageListener(nsImageFrame
*aFrame
) :
2085 nsImageListener::~nsImageListener()
2090 nsImageListener::Notify(imgIRequest
*aRequest
, int32_t aType
, const nsIntRect
* aData
)
2093 return NS_ERROR_FAILURE
;
2095 return mFrame
->Notify(aRequest
, aType
, aData
);
2099 IsInAutoWidthTableCellForQuirk(nsIFrame
*aFrame
)
2101 if (eCompatibility_NavQuirks
!= aFrame
->PresContext()->CompatibilityMode())
2103 // Check if the parent of the closest nsBlockFrame has auto width.
2104 nsBlockFrame
*ancestor
= nsLayoutUtils::FindNearestBlockAncestor(aFrame
);
2105 if (ancestor
->StyleContext()->GetPseudo() == nsCSSAnonBoxes::cellContent
) {
2106 // Assume direct parent is a table cell frame.
2107 nsFrame
*grandAncestor
= static_cast<nsFrame
*>(ancestor
->GetParent());
2108 return grandAncestor
&&
2109 grandAncestor
->StylePosition()->mWidth
.GetUnit() == eStyleUnit_Auto
;
2115 nsImageFrame::AddInlineMinISize(nsRenderingContext
*aRenderingContext
,
2116 nsIFrame::InlineMinISizeData
*aData
)
2119 NS_ASSERTION(GetParent(), "Must have a parent if we get here!");
2121 nsIFrame
* parent
= GetParent();
2123 !CanContinueTextRun() &&
2124 parent
->StyleText()->WhiteSpaceCanWrap(parent
) &&
2125 !IsInAutoWidthTableCellForQuirk(this);
2128 aData
->OptionallyBreak(aRenderingContext
);
2130 aData
->trailingWhitespace
= 0;
2131 aData
->skipWhitespace
= false;
2132 aData
->trailingTextFrame
= nullptr;
2133 aData
->currentLine
+= nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
2134 this, nsLayoutUtils::MIN_ISIZE
);
2135 aData
->atStartOfLine
= false;
2138 aData
->OptionallyBreak(aRenderingContext
);