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 image data */
8 #include "nsImageFrame.h"
10 #include "gfx2DGlue.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"
20 #include "nsFontMetrics.h"
21 #include "nsIImageLoadingContent.h"
23 #include "nsPrintfCString.h"
24 #include "nsPresContext.h"
25 #include "nsRenderingContext.h"
26 #include "nsIPresShell.h"
27 #include "nsGkAtoms.h"
28 #include "nsIDocument.h"
29 #include "nsContentUtils.h"
30 #include "nsCSSAnonBoxes.h"
31 #include "nsStyleContext.h"
32 #include "nsStyleConsts.h"
33 #include "nsStyleCoord.h"
34 #include "nsStyleUtil.h"
35 #include "nsTransform2D.h"
36 #include "nsImageMap.h"
37 #include "nsIIOService.h"
38 #include "nsILoadGroup.h"
39 #include "nsISupportsPriority.h"
40 #include "nsNetUtil.h"
41 #include "nsCSSRendering.h"
42 #include "nsIDOMHTMLAnchorElement.h"
43 #include "nsNameSpaceManager.h"
46 #include "nsAccessibilityService.h"
48 #include "nsIDOMNode.h"
49 #include "nsLayoutUtils.h"
50 #include "nsDisplayList.h"
51 #include "nsIContent.h"
52 #include "nsIDocument.h"
53 #include "FrameLayerBuilder.h"
54 #include "nsISelectionController.h"
55 #include "nsISelection.h"
57 #include "imgIContainer.h"
58 #include "imgLoader.h"
59 #include "imgRequestProxy.h"
61 #include "nsCSSFrameConstructor.h"
62 #include "nsIDOMRange.h"
65 #include "nsBidiUtils.h"
66 #include "nsBidiPresUtils.h"
69 #include "ImageLayers.h"
70 #include "ImageContainer.h"
71 #include "nsStyleSet.h"
72 #include "nsBlockFrame.h"
73 #include "nsStyleStructInlines.h"
75 #include "mozilla/Preferences.h"
77 #include "mozilla/dom/Link.h"
79 using namespace mozilla
;
80 using namespace mozilla::dom
;
81 using namespace mozilla::gfx
;
82 using namespace mozilla::image
;
83 using namespace mozilla::layers
;
85 // sizes (pixels) for image icon, padding and border frame
86 #define ICON_SIZE (16)
87 #define ICON_PADDING (3)
88 #define ALT_BORDER_WIDTH (1)
90 //we must add hooks soon
91 #define IMAGE_EDITOR_CHECK 1
93 // Default alignment value (so we can tell an unset value from a set value)
94 #define ALIGN_UNSET uint8_t(-1)
96 // static icon information
97 nsImageFrame::IconLoad
* nsImageFrame::gIconLoad
= nullptr;
99 // cached IO service for loading icons
100 nsIIOService
* nsImageFrame::sIOService
;
102 // test if the width and height are fixed, looking at the style data
103 static bool HaveFixedSize(const nsStylePosition
* aStylePosition
)
105 // check the width and height values in the reflow state's style struct
106 // - if width and height are specified as either coord or percentage, then
107 // the size of the image frame is constrained
108 return aStylePosition
->mWidth
.IsCoordPercentCalcUnit() &&
109 aStylePosition
->mHeight
.IsCoordPercentCalcUnit();
112 // use the data in the reflow state to decide if the image has a constrained size
113 // (i.e. width and height that are based on the containing block size and not the image size)
114 // so we can avoid animated GIF related reflows
115 inline bool HaveFixedSize(const nsHTMLReflowState
& aReflowState
)
117 NS_ASSERTION(aReflowState
.mStylePosition
, "crappy reflowState - null stylePosition");
118 // when an image has percent css style height or width, but ComputedHeight()
119 // or ComputedWidth() of reflow state is NS_UNCONSTRAINEDSIZE
120 // it needs to return false to cause an incremental reflow later
121 // if an image is inside table like bug 156731 simple testcase III,
122 // during pass 1 reflow, ComputedWidth() is NS_UNCONSTRAINEDSIZE
123 // in pass 2 reflow, ComputedWidth() is 0, it also needs to return false
125 const nsStyleCoord
&height
= aReflowState
.mStylePosition
->mHeight
;
126 const nsStyleCoord
&width
= aReflowState
.mStylePosition
->mWidth
;
127 return ((height
.HasPercent() &&
128 NS_UNCONSTRAINEDSIZE
== aReflowState
.ComputedHeight()) ||
129 (width
.HasPercent() &&
130 (NS_UNCONSTRAINEDSIZE
== aReflowState
.ComputedWidth() ||
131 0 == aReflowState
.ComputedWidth())))
133 : HaveFixedSize(aReflowState
.mStylePosition
);
137 NS_NewImageFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
139 return new (aPresShell
) nsImageFrame(aContext
);
142 NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame
)
145 nsImageFrame::nsImageFrame(nsStyleContext
* aContext
) :
146 ImageFrameSuper(aContext
),
148 mIntrinsicRatio(0, 0),
149 mDisplayingIcon(false),
150 mFirstFrameComplete(false),
151 mReflowCallbackPosted(false)
153 // We assume our size is not constrained and we haven't gotten an
154 // initial reflow yet, so don't touch those flags.
155 mIntrinsicSize
.width
.SetCoordValue(0);
156 mIntrinsicSize
.height
.SetCoordValue(0);
159 nsImageFrame::~nsImageFrame()
163 NS_QUERYFRAME_HEAD(nsImageFrame
)
164 NS_QUERYFRAME_ENTRY(nsImageFrame
)
165 NS_QUERYFRAME_TAIL_INHERITING(ImageFrameSuper
)
169 nsImageFrame::AccessibleType()
171 // Don't use GetImageMap() to avoid reentrancy into accessibility.
173 return a11y::eHTMLImageMapType
;
176 return a11y::eImageType
;
181 nsImageFrame::DisconnectMap()
184 mImageMap
->Destroy();
185 NS_RELEASE(mImageMap
);
188 nsAccessibilityService
* accService
= GetAccService();
190 accService
->RecreateAccessible(PresContext()->PresShell(), mContent
);
197 nsImageFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
199 if (mReflowCallbackPosted
) {
200 PresContext()->PresShell()->CancelReflowCallback(this);
201 mReflowCallbackPosted
= false;
204 // Tell our image map, if there is one, to clean up
205 // This causes the nsImageMap to unregister itself as
209 // set the frame to null so we don't send messages to a dead object.
211 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
213 // Notify our image loading content that we are going away so it can
214 // deregister with our refresh driver.
215 imageLoader
->FrameDestroyed(this);
217 imageLoader
->RemoveObserver(mListener
);
220 reinterpret_cast<nsImageListener
*>(mListener
.get())->SetFrame(nullptr);
225 // If we were displaying an icon, take ourselves off the list
227 gIconLoad
->RemoveIconObserver(this);
229 nsSplittableFrame::DestroyFrom(aDestructRoot
);
233 nsImageFrame::DidSetStyleContext(nsStyleContext
* aOldStyleContext
)
235 ImageFrameSuper::DidSetStyleContext(aOldStyleContext
);
238 // We'll pick this change up whenever we do get an image.
242 nsStyleImageOrientation newOrientation
= StyleVisibility()->mImageOrientation
;
244 // We need to update our orientation either if we had no style context before
245 // because this is the first time it's been set, or if the image-orientation
246 // property changed from its previous value.
247 bool shouldUpdateOrientation
=
249 aOldStyleContext
->StyleVisibility()->mImageOrientation
!= newOrientation
;
251 if (shouldUpdateOrientation
) {
252 nsCOMPtr
<imgIContainer
> image(mImage
->Unwrap());
253 mImage
= nsLayoutUtils::OrientImage(image
, newOrientation
);
255 UpdateIntrinsicSize(mImage
);
256 UpdateIntrinsicRatio(mImage
);
261 nsImageFrame::Init(nsIContent
* aContent
,
262 nsContainerFrame
* aParent
,
263 nsIFrame
* aPrevInFlow
)
265 nsSplittableFrame::Init(aContent
, aParent
, aPrevInFlow
);
267 mListener
= new nsImageListener(this);
269 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(aContent
);
271 NS_RUNTIMEABORT("Why do we have an nsImageFrame here at all?");
274 imageLoader
->AddObserver(mListener
);
276 nsPresContext
*aPresContext
= PresContext();
279 LoadIcons(aPresContext
);
281 // We have a PresContext now, so we need to notify the image content node
282 // that it can register images.
283 imageLoader
->FrameCreated(this);
285 // Give image loads associated with an image frame a small priority boost!
286 nsCOMPtr
<imgIRequest
> currentRequest
;
287 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
288 getter_AddRefs(currentRequest
));
289 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(currentRequest
);
291 p
->AdjustPriority(-1);
295 nsImageFrame::UpdateIntrinsicSize(imgIContainer
* aImage
)
297 NS_PRECONDITION(aImage
, "null image");
301 IntrinsicSize oldIntrinsicSize
= mIntrinsicSize
;
302 mIntrinsicSize
= IntrinsicSize();
304 // Set intrinsic size to match aImage's reported intrinsic width & height.
305 nsSize intrinsicSize
;
306 if (NS_SUCCEEDED(aImage
->GetIntrinsicSize(&intrinsicSize
))) {
307 // If the image has no intrinsic width, intrinsicSize.width will be -1, and
308 // we can leave mIntrinsicSize.width at its default value of eStyleUnit_None.
309 // Otherwise we use intrinsicSize.width. Height works the same way.
310 if (intrinsicSize
.width
!= -1)
311 mIntrinsicSize
.width
.SetCoordValue(intrinsicSize
.width
);
312 if (intrinsicSize
.height
!= -1)
313 mIntrinsicSize
.height
.SetCoordValue(intrinsicSize
.height
);
315 // Failure means that the image hasn't loaded enough to report a result. We
316 // treat this case as if the image's intrinsic size was 0x0.
317 mIntrinsicSize
.width
.SetCoordValue(0);
318 mIntrinsicSize
.height
.SetCoordValue(0);
321 return mIntrinsicSize
!= oldIntrinsicSize
;
325 nsImageFrame::UpdateIntrinsicRatio(imgIContainer
* aImage
)
327 NS_PRECONDITION(aImage
, "null image");
332 nsSize oldIntrinsicRatio
= mIntrinsicRatio
;
334 // Set intrinsic ratio to match aImage's reported intrinsic ratio.
335 if (NS_FAILED(aImage
->GetIntrinsicRatio(&mIntrinsicRatio
)))
336 mIntrinsicRatio
.SizeTo(0, 0);
338 return mIntrinsicRatio
!= oldIntrinsicRatio
;
342 nsImageFrame::GetSourceToDestTransform(nsTransform2D
& aTransform
)
344 // First, figure out destRect (the rect we're rendering into).
345 // NOTE: We use mComputedSize instead of just GetInnerArea()'s own size here,
346 // because GetInnerArea() might be smaller if we're fragmented, whereas
347 // mComputedSize has our full content-box size (which we need for
348 // ComputeObjectDestRect to work correctly).
349 nsRect
constraintRect(GetInnerArea().TopLeft(), mComputedSize
);
350 constraintRect
.y
-= GetContinuationOffset();
352 nsRect destRect
= nsLayoutUtils::ComputeObjectDestRect(constraintRect
,
356 // Set the translation components, based on destRect
357 // XXXbz does this introduce rounding errors because of the cast to
358 // float? Should we just manually add that stuff in every time
360 aTransform
.SetToTranslate(float(destRect
.x
),
363 // Set the scale factors, based on destRect and intrinsic size.
364 if (mIntrinsicSize
.width
.GetUnit() == eStyleUnit_Coord
&&
365 mIntrinsicSize
.width
.GetCoordValue() != 0 &&
366 mIntrinsicSize
.height
.GetUnit() == eStyleUnit_Coord
&&
367 mIntrinsicSize
.height
.GetCoordValue() != 0 &&
368 mIntrinsicSize
.width
.GetCoordValue() != destRect
.width
&&
369 mIntrinsicSize
.height
.GetCoordValue() != destRect
.height
) {
371 aTransform
.SetScale(float(destRect
.width
) /
372 float(mIntrinsicSize
.width
.GetCoordValue()),
373 float(destRect
.height
) /
374 float(mIntrinsicSize
.height
.GetCoordValue()));
381 // This function checks whether the given request is the current request for our
384 nsImageFrame::IsPendingLoad(imgIRequest
* aRequest
) const
386 // Default to pending load in case of errors
387 nsCOMPtr
<nsIImageLoadingContent
> imageLoader(do_QueryInterface(mContent
));
388 NS_ASSERTION(imageLoader
, "No image loading content?");
390 int32_t requestType
= nsIImageLoadingContent::UNKNOWN_REQUEST
;
391 imageLoader
->GetRequestType(aRequest
, &requestType
);
393 return requestType
!= nsIImageLoadingContent::CURRENT_REQUEST
;
397 nsImageFrame::SourceRectToDest(const nsIntRect
& aRect
)
399 // When scaling the image, row N of the source image may (depending on
400 // the scaling function) be used to draw any row in the destination image
401 // between floor(F * (N-1)) and ceil(F * (N+1)), where F is the
402 // floating-point scaling factor. The same holds true for columns.
403 // So, we start by computing that bound without the floor and ceiling.
405 nsRect
r(nsPresContext::CSSPixelsToAppUnits(aRect
.x
- 1),
406 nsPresContext::CSSPixelsToAppUnits(aRect
.y
- 1),
407 nsPresContext::CSSPixelsToAppUnits(aRect
.width
+ 2),
408 nsPresContext::CSSPixelsToAppUnits(aRect
.height
+ 2));
410 nsTransform2D sourceToDest
;
411 if (!GetSourceToDestTransform(sourceToDest
)) {
412 // Failed to generate transform matrix. Return our whole inner area,
413 // to be on the safe side (since this method is used for generating
414 // invalidation rects).
415 return GetInnerArea();
418 sourceToDest
.TransformCoord(&r
.x
, &r
.y
, &r
.width
, &r
.height
);
420 // Now, round the edges out to the pixel boundary.
421 nscoord scale
= nsPresContext::CSSPixelsToAppUnits(1);
422 nscoord right
= r
.x
+ r
.width
;
423 nscoord bottom
= r
.y
+ r
.height
;
425 r
.x
-= (scale
+ (r
.x
% scale
)) % scale
;
426 r
.y
-= (scale
+ (r
.y
% scale
)) % scale
;
427 r
.width
= right
+ ((scale
- (right
% scale
)) % scale
) - r
.x
;
428 r
.height
= bottom
+ ((scale
- (bottom
% scale
)) % scale
) - r
.y
;
433 // Note that we treat NS_EVENT_STATE_SUPPRESSED images as "OK". This means
434 // that we'll construct image frames for them as needed if their display is
435 // toggled from "none" (though we won't paint them, unless their visibility
437 #define BAD_STATES (NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED | \
438 NS_EVENT_STATE_LOADING)
440 // This is a macro so that we don't evaluate the boolean last arg
441 // unless we have to; it can be expensive
442 #define IMAGE_OK(_state, _loadingOK) \
443 (!(_state).HasAtLeastOneOfStates(BAD_STATES) || \
444 (!(_state).HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED) && \
445 (_state).HasState(NS_EVENT_STATE_LOADING) && (_loadingOK)))
449 nsImageFrame::ShouldCreateImageFrameFor(Element
* aElement
,
450 nsStyleContext
* aStyleContext
)
452 EventStates state
= aElement
->State();
454 HaveFixedSize(aStyleContext
->StylePosition()))) {
455 // Image is fine; do the image frame thing
459 // Check if we want to use a placeholder box with an icon or just
460 // let the presShell make us into inline text. Decide as follows:
462 // - if our special "force icons" style is set, show an icon
463 // - else if our "do not show placeholders" pref is set, skip the icon
465 // - if there is a src attribute, there is no alt attribute,
466 // and this is not an <object> (which could not possibly have
467 // such an attribute), show an icon.
468 // - if QuirksMode, and the IMG has a size show an icon.
469 // - otherwise, skip the icon
472 if (aStyleContext
->StyleUIReset()->mForceBrokenImageIcon
) {
475 else if (gIconLoad
&& gIconLoad
->mPrefForceInlineAltText
) {
478 else if (aElement
->HasAttr(kNameSpaceID_None
, nsGkAtoms::src
) &&
479 !aElement
->HasAttr(kNameSpaceID_None
, nsGkAtoms::alt
) &&
480 !aElement
->IsHTML(nsGkAtoms::object
) &&
481 !aElement
->IsHTML(nsGkAtoms::input
)) {
482 // Use a sized box if we have no alt text. This means no alt attribute
483 // and the node is not an object or an input (since those always have alt
487 else if (aStyleContext
->PresContext()->CompatibilityMode() !=
488 eCompatibility_NavQuirks
) {
492 // check whether we have fixed size
493 useSizedBox
= HaveFixedSize(aStyleContext
->StylePosition());
500 nsImageFrame::Notify(imgIRequest
* aRequest
,
502 const nsIntRect
* aRect
)
504 if (aType
== imgINotificationObserver::SIZE_AVAILABLE
) {
505 nsCOMPtr
<imgIContainer
> image
;
506 aRequest
->GetImage(getter_AddRefs(image
));
507 return OnSizeAvailable(aRequest
, image
);
510 if (aType
== imgINotificationObserver::FRAME_UPDATE
) {
511 return OnFrameUpdate(aRequest
, aRect
);
514 if (aType
== imgINotificationObserver::FRAME_COMPLETE
) {
515 mFirstFrameComplete
= true;
518 if (aType
== imgINotificationObserver::LOAD_COMPLETE
) {
520 aRequest
->GetImageStatus(&imgStatus
);
522 imgStatus
& imgIRequest::STATUS_ERROR
? NS_ERROR_FAILURE
: NS_OK
;
523 return OnLoadComplete(aRequest
, status
);
530 SizeIsAvailable(imgIRequest
* aRequest
)
535 uint32_t imageStatus
= 0;
536 nsresult rv
= aRequest
->GetImageStatus(&imageStatus
);
538 return NS_SUCCEEDED(rv
) && (imageStatus
& imgIRequest::STATUS_SIZE_AVAILABLE
);
542 nsImageFrame::OnSizeAvailable(imgIRequest
* aRequest
, imgIContainer
* aImage
)
544 if (!aImage
) return NS_ERROR_INVALID_ARG
;
546 /* Get requested animation policy from the pres context:
551 nsPresContext
*presContext
= PresContext();
552 aImage
->SetAnimationMode(presContext
->ImageAnimationMode());
554 if (IsPendingLoad(aRequest
)) {
559 bool intrinsicSizeChanged
= false;
560 if (SizeIsAvailable(aRequest
)) {
561 // This is valid and for the current request, so update our stored image
562 // container, orienting according to our style.
563 mImage
= nsLayoutUtils::OrientImage(aImage
, StyleVisibility()->mImageOrientation
);
565 intrinsicSizeChanged
= UpdateIntrinsicSize(mImage
);
566 intrinsicSizeChanged
= UpdateIntrinsicRatio(mImage
) || intrinsicSizeChanged
;
568 // We no longer have a valid image, so release our stored image container.
571 // Have to size to 0,0 so that GetDesiredSize recalculates the size.
572 mIntrinsicSize
.width
.SetCoordValue(0);
573 mIntrinsicSize
.height
.SetCoordValue(0);
574 mIntrinsicRatio
.SizeTo(0, 0);
575 intrinsicSizeChanged
= true;
578 if (intrinsicSizeChanged
&& (mState
& IMAGE_GOTINITIALREFLOW
)) {
579 // Now we need to reflow if we have an unconstrained size and have
580 // already gotten the initial reflow
581 if (!(mState
& IMAGE_SIZECONSTRAINED
)) {
582 nsIPresShell
*presShell
= presContext
->GetPresShell();
583 NS_ASSERTION(presShell
, "No PresShell.");
585 presShell
->FrameNeedsReflow(this, nsIPresShell::eStyleChange
,
595 nsImageFrame::OnFrameUpdate(imgIRequest
* aRequest
, const nsIntRect
* aRect
)
597 NS_ENSURE_ARG_POINTER(aRect
);
599 if (!(mState
& IMAGE_GOTINITIALREFLOW
)) {
600 // Don't bother to do anything; we have a reflow coming up!
604 if (mFirstFrameComplete
&& !StyleVisibility()->IsVisible()) {
608 if (IsPendingLoad(aRequest
)) {
613 nsIntRect layerInvalidRect
= mImage
614 ? mImage
->GetImageSpaceInvalidationRect(*aRect
)
617 if (layerInvalidRect
.IsEqualInterior(nsIntRect::GetMaxSizedIntRect())) {
618 // Invalidate our entire area.
619 InvalidateSelf(nullptr, nullptr);
623 nsRect frameInvalidRect
= SourceRectToDest(layerInvalidRect
);
624 InvalidateSelf(&layerInvalidRect
, &frameInvalidRect
);
629 nsImageFrame::InvalidateSelf(const nsIntRect
* aLayerInvalidRect
,
630 const nsRect
* aFrameInvalidRect
)
632 InvalidateLayer(nsDisplayItem::TYPE_IMAGE
,
636 if (!mFirstFrameComplete
) {
637 InvalidateLayer(nsDisplayItem::TYPE_ALT_FEEDBACK
,
644 nsImageFrame::OnLoadComplete(imgIRequest
* aRequest
, nsresult aStatus
)
646 // Check what request type we're dealing with
647 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
648 NS_ASSERTION(imageLoader
, "Who's notifying us??");
649 int32_t loadType
= nsIImageLoadingContent::UNKNOWN_REQUEST
;
650 imageLoader
->GetRequestType(aRequest
, &loadType
);
651 if (loadType
!= nsIImageLoadingContent::CURRENT_REQUEST
&&
652 loadType
!= nsIImageLoadingContent::PENDING_REQUEST
) {
653 return NS_ERROR_FAILURE
;
656 NotifyNewCurrentRequest(aRequest
, aStatus
);
661 nsImageFrame::NotifyNewCurrentRequest(imgIRequest
*aRequest
,
664 nsCOMPtr
<imgIContainer
> image
;
665 aRequest
->GetImage(getter_AddRefs(image
));
666 NS_ASSERTION(image
|| NS_FAILED(aStatus
), "Successful load with no container?");
668 // May have to switch sizes here!
669 bool intrinsicSizeChanged
= true;
670 if (NS_SUCCEEDED(aStatus
) && image
&& SizeIsAvailable(aRequest
)) {
671 // Update our stored image container, orienting according to our style.
672 mImage
= nsLayoutUtils::OrientImage(image
, StyleVisibility()->mImageOrientation
);
674 intrinsicSizeChanged
= UpdateIntrinsicSize(mImage
);
675 intrinsicSizeChanged
= UpdateIntrinsicRatio(mImage
) || intrinsicSizeChanged
;
677 // We no longer have a valid image, so release our stored image container.
680 // Have to size to 0,0 so that GetDesiredSize recalculates the size
681 mIntrinsicSize
.width
.SetCoordValue(0);
682 mIntrinsicSize
.height
.SetCoordValue(0);
683 mIntrinsicRatio
.SizeTo(0, 0);
686 if (mState
& IMAGE_GOTINITIALREFLOW
) { // do nothing if we haven't gotten the initial reflow yet
687 if (!(mState
& IMAGE_SIZECONSTRAINED
) && intrinsicSizeChanged
) {
688 nsIPresShell
*presShell
= PresContext()->GetPresShell();
690 presShell
->FrameNeedsReflow(this, nsIPresShell::eStyleChange
,
694 // Update border+content to account for image change
700 nsImageFrame::EnsureIntrinsicSizeAndRatio()
702 // If mIntrinsicSize.width and height are 0, then we need to update from the
704 if (mIntrinsicSize
.width
.GetUnit() == eStyleUnit_Coord
&&
705 mIntrinsicSize
.width
.GetCoordValue() == 0 &&
706 mIntrinsicSize
.height
.GetUnit() == eStyleUnit_Coord
&&
707 mIntrinsicSize
.height
.GetCoordValue() == 0) {
710 UpdateIntrinsicSize(mImage
);
711 UpdateIntrinsicRatio(mImage
);
713 // image request is null or image size not known, probably an
714 // invalid image specified
715 // - make the image big enough for the icon (it may not be
716 // used if inline alt expansion is used instead)
717 if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT
)) {
718 nscoord edgeLengthToUse
=
719 nsPresContext::CSSPixelsToAppUnits(
720 ICON_SIZE
+ (2 * (ICON_PADDING
+ ALT_BORDER_WIDTH
)));
721 mIntrinsicSize
.width
.SetCoordValue(edgeLengthToUse
);
722 mIntrinsicSize
.height
.SetCoordValue(edgeLengthToUse
);
723 mIntrinsicRatio
.SizeTo(1, 1);
731 nsImageFrame::ComputeSize(nsRenderingContext
*aRenderingContext
,
733 const LogicalSize
& aCBSize
,
734 nscoord aAvailableISize
,
735 const LogicalSize
& aMargin
,
736 const LogicalSize
& aBorder
,
737 const LogicalSize
& aPadding
,
738 ComputeSizeFlags aFlags
)
740 EnsureIntrinsicSizeAndRatio();
742 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
743 NS_ASSERTION(imageLoader
, "No content node??");
744 mozilla::IntrinsicSize
intrinsicSize(mIntrinsicSize
);
746 // XXX(seth): We may sometimes find ourselves in the situation where we have
747 // mImage, but imageLoader's current request does not have a size yet.
748 // This can happen when we load an image speculatively from cache, it fails
749 // to validate, and the new image load hasn't fired SIZE_AVAILABLE yet. In
750 // this situation we should always use mIntrinsicSize, because
751 // GetNaturalWidth/Height will return 0, so we check CurrentRequestHasSize()
752 // below. See bug 1019840. We will fix this in bug 1141395.
754 // Content may override our default dimensions. This is termed as overriding
755 // the intrinsic size by the spec, but all other consumers of mIntrinsic*
756 // values are being used to refer to the real/true size of the image data.
757 if (imageLoader
&& imageLoader
->CurrentRequestHasSize() && mImage
&&
758 intrinsicSize
.width
.GetUnit() == eStyleUnit_Coord
&&
759 intrinsicSize
.height
.GetUnit() == eStyleUnit_Coord
) {
762 if (NS_SUCCEEDED(imageLoader
->GetNaturalWidth(&width
)) &&
763 NS_SUCCEEDED(imageLoader
->GetNaturalHeight(&height
))) {
764 nscoord appWidth
= nsPresContext::CSSPixelsToAppUnits((int32_t)width
);
765 nscoord appHeight
= nsPresContext::CSSPixelsToAppUnits((int32_t)height
);
766 // If this image is rotated, we'll need to transpose the natural
769 if (StyleVisibility()->mImageOrientation
.IsFromImage()) {
770 coordFlip
= mImage
->GetOrientation().SwapsWidthAndHeight();
772 coordFlip
= StyleVisibility()->mImageOrientation
.SwapsWidthAndHeight();
774 intrinsicSize
.width
.SetCoordValue(coordFlip
? appHeight
: appWidth
);
775 intrinsicSize
.height
.SetCoordValue(coordFlip
? appWidth
: appHeight
);
779 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(aWM
,
780 aRenderingContext
, this,
781 intrinsicSize
, mIntrinsicRatio
,
788 // XXXdholbert This function's clients should probably just be calling
789 // GetContentRectRelativeToSelf() directly.
791 nsImageFrame::GetInnerArea() const
793 return GetContentRectRelativeToSelf();
797 nsImageFrame::GetMapElement() const
800 if (mContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::usemap
, usemap
)) {
801 return mContent
->OwnerDoc()->FindImageMap(usemap
);
806 // get the offset into the content area of the image where aImg starts if it is a continuation.
808 nsImageFrame::GetContinuationOffset() const
811 for (nsIFrame
*f
= GetPrevInFlow(); f
; f
= f
->GetPrevInFlow()) {
812 offset
+= f
->GetContentRect().height
;
814 NS_ASSERTION(offset
>= 0, "bogus GetContentRect");
818 /* virtual */ nscoord
819 nsImageFrame::GetMinISize(nsRenderingContext
*aRenderingContext
)
821 // XXX The caller doesn't account for constraints of the height,
822 // min-height, and max-height properties.
823 DebugOnly
<nscoord
> result
;
824 DISPLAY_MIN_WIDTH(this, result
);
825 EnsureIntrinsicSizeAndRatio();
826 return mIntrinsicSize
.width
.GetUnit() == eStyleUnit_Coord
?
827 mIntrinsicSize
.width
.GetCoordValue() : 0;
830 /* virtual */ nscoord
831 nsImageFrame::GetPrefISize(nsRenderingContext
*aRenderingContext
)
833 // XXX The caller doesn't account for constraints of the height,
834 // min-height, and max-height properties.
835 DebugOnly
<nscoord
> result
;
836 DISPLAY_PREF_WIDTH(this, result
);
837 EnsureIntrinsicSizeAndRatio();
838 // convert from normal twips to scaled twips (printing...)
839 return mIntrinsicSize
.width
.GetUnit() == eStyleUnit_Coord
?
840 mIntrinsicSize
.width
.GetCoordValue() : 0;
843 /* virtual */ IntrinsicSize
844 nsImageFrame::GetIntrinsicSize()
846 return mIntrinsicSize
;
850 nsImageFrame::GetIntrinsicRatio()
852 return mIntrinsicRatio
;
856 nsImageFrame::Reflow(nsPresContext
* aPresContext
,
857 nsHTMLReflowMetrics
& aMetrics
,
858 const nsHTMLReflowState
& aReflowState
,
859 nsReflowStatus
& aStatus
)
861 DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
862 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aMetrics
, aStatus
);
863 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS
,
864 ("enter nsImageFrame::Reflow: availSize=%d,%d",
865 aReflowState
.AvailableWidth(), aReflowState
.AvailableHeight()));
867 NS_PRECONDITION(mState
& NS_FRAME_IN_REFLOW
, "frame is not in reflow");
869 aStatus
= NS_FRAME_COMPLETE
;
871 // see if we have a frozen size (i.e. a fixed width and height)
872 if (HaveFixedSize(aReflowState
)) {
873 mState
|= IMAGE_SIZECONSTRAINED
;
875 mState
&= ~IMAGE_SIZECONSTRAINED
;
878 // XXXldb These two bits are almost exact opposites (except in the
879 // middle of the initial reflow); remove IMAGE_GOTINITIALREFLOW.
880 if (GetStateBits() & NS_FRAME_FIRST_REFLOW
) {
881 mState
|= IMAGE_GOTINITIALREFLOW
;
885 nsSize(aReflowState
.ComputedWidth(), aReflowState
.ComputedHeight());
887 aMetrics
.Width() = mComputedSize
.width
;
888 aMetrics
.Height() = mComputedSize
.height
;
890 // add borders and padding
891 aMetrics
.Width() += aReflowState
.ComputedPhysicalBorderPadding().LeftRight();
892 aMetrics
.Height() += aReflowState
.ComputedPhysicalBorderPadding().TopBottom();
894 if (GetPrevInFlow()) {
895 aMetrics
.Width() = GetPrevInFlow()->GetSize().width
;
896 nscoord y
= GetContinuationOffset();
897 aMetrics
.Height() -= y
+ aReflowState
.ComputedPhysicalBorderPadding().top
;
898 aMetrics
.Height() = std::max(0, aMetrics
.Height());
902 // we have to split images if we are:
903 // in Paginated mode, we need to have a constrained height, and have a height larger than our available height
904 uint32_t loadStatus
= imgIRequest::STATUS_NONE
;
905 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
906 NS_ASSERTION(imageLoader
, "No content node??");
908 nsCOMPtr
<imgIRequest
> currentRequest
;
909 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
910 getter_AddRefs(currentRequest
));
911 if (currentRequest
) {
912 currentRequest
->GetImageStatus(&loadStatus
);
915 if (aPresContext
->IsPaginated() &&
916 ((loadStatus
& imgIRequest::STATUS_SIZE_AVAILABLE
) || (mState
& IMAGE_SIZECONSTRAINED
)) &&
917 NS_UNCONSTRAINEDSIZE
!= aReflowState
.AvailableHeight() &&
918 aMetrics
.Height() > aReflowState
.AvailableHeight()) {
919 // our desired height was greater than 0, so to avoid infinite
920 // splitting, use 1 pixel as the min
921 aMetrics
.Height() = std::max(nsPresContext::CSSPixelsToAppUnits(1), aReflowState
.AvailableHeight());
922 aStatus
= NS_FRAME_NOT_COMPLETE
;
925 aMetrics
.SetOverflowAreasToDesiredBounds();
926 EventStates contentState
= mContent
->AsElement()->State();
927 bool imageOK
= IMAGE_OK(contentState
, true);
929 // Determine if the size is available
930 bool haveSize
= false;
931 if (loadStatus
& imgIRequest::STATUS_SIZE_AVAILABLE
) {
935 if (!imageOK
|| !haveSize
) {
936 nsRect
altFeedbackSize(0, 0,
937 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
+2*(ICON_PADDING
+ALT_BORDER_WIDTH
)),
938 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
+2*(ICON_PADDING
+ALT_BORDER_WIDTH
)));
939 // We include the altFeedbackSize in our visual overflow, but not in our
940 // scrollable overflow, since it doesn't really need to be scrolled to
941 // outside the image.
942 static_assert(eOverflowType_LENGTH
== 2, "Unknown overflow types?");
943 nsRect
& visualOverflow
= aMetrics
.VisualOverflow();
944 visualOverflow
.UnionRect(visualOverflow
, altFeedbackSize
);
946 FinishAndStoreOverflow(&aMetrics
);
948 if ((GetStateBits() & NS_FRAME_FIRST_REFLOW
) && !mReflowCallbackPosted
) {
949 nsIPresShell
* shell
= PresContext()->PresShell();
950 mReflowCallbackPosted
= true;
951 shell
->PostReflowCallback(this);
954 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS
,
955 ("exit nsImageFrame::Reflow: size=%d,%d",
956 aMetrics
.Width(), aMetrics
.Height()));
957 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aMetrics
);
961 nsImageFrame::ReflowFinished()
963 mReflowCallbackPosted
= false;
965 nsLayoutUtils::UpdateImageVisibilityForFrame(this);
971 nsImageFrame::ReflowCallbackCanceled()
973 mReflowCallbackPosted
= false;
976 // Computes the width of the specified string. aMaxWidth specifies the maximum
977 // width available. Once this limit is reached no more characters are measured.
978 // The number of characters that fit within the maximum width are returned in
979 // aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected
980 // into the rendering context before this is called (for performance). MMP
982 nsImageFrame::MeasureString(const char16_t
* aString
,
986 nsRenderingContext
& aContext
,
987 nsFontMetrics
& aFontMetrics
)
989 nscoord totalWidth
= 0;
990 aFontMetrics
.SetTextRunRTL(false);
991 nscoord spaceWidth
= aFontMetrics
.SpaceWidth();
994 while (aLength
> 0) {
995 // Find the next place we can line break
996 uint32_t len
= aLength
;
997 bool trailingSpace
= false;
998 for (int32_t i
= 0; i
< aLength
; i
++) {
999 if (dom::IsSpaceCharacter(aString
[i
]) && (i
> 0)) {
1000 len
= i
; // don't include the space when measuring
1001 trailingSpace
= true;
1006 // Measure this chunk of text, and see if it fits
1008 nsLayoutUtils::AppUnitWidthOfStringBidi(aString
, len
, this, aFontMetrics
,
1010 bool fits
= (totalWidth
+ width
) <= aMaxWidth
;
1012 // If it fits on the line, or it's the first word we've processed then
1014 if (fits
|| (0 == totalWidth
)) {
1016 totalWidth
+= width
;
1018 // If there's a trailing space then see if it fits as well
1019 if (trailingSpace
) {
1020 if ((totalWidth
+ spaceWidth
) <= aMaxWidth
) {
1021 totalWidth
+= spaceWidth
;
1023 // Space won't fit. Leave it at the end but don't include it in
1043 // Formats the alt-text to fit within the specified rectangle. Breaks lines
1044 // between words if a word would extend past the edge of the rectangle
1046 nsImageFrame::DisplayAltText(nsPresContext
* aPresContext
,
1047 nsRenderingContext
& aRenderingContext
,
1048 const nsString
& aAltText
,
1049 const nsRect
& aRect
)
1051 // Set font and color
1052 aRenderingContext
.ThebesContext()->SetColor(StyleColor()->mColor
);
1053 nsRefPtr
<nsFontMetrics
> fm
;
1054 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm
),
1055 nsLayoutUtils::FontSizeInflationFor(this));
1057 // Format the text to display within the formatting rect
1059 nscoord maxAscent
= fm
->MaxAscent();
1060 nscoord maxDescent
= fm
->MaxDescent();
1061 nscoord lineHeight
= fm
->MaxHeight(); // line-relative, so an x-coordinate
1062 // length if writing mode is vertical
1064 WritingMode wm
= GetWritingMode();
1065 bool isVertical
= wm
.IsVertical();
1067 fm
->SetVertical(isVertical
);
1068 fm
->SetTextOrientation(StyleVisibility()->mTextOrientation
);
1070 // XXX It would be nice if there was a way to have the font metrics tell
1071 // use where to break the text given a maximum width. At a minimum we need
1072 // to be able to get the break character...
1073 const char16_t
* str
= aAltText
.get();
1074 int32_t strLen
= aAltText
.Length();
1075 nsPoint pt
= wm
.IsVerticalRL() ? aRect
.TopRight() - nsPoint(lineHeight
, 0)
1077 nscoord iSize
= isVertical
? aRect
.height
: aRect
.width
;
1079 if (!aPresContext
->BidiEnabled() && HasRTLChars(aAltText
)) {
1080 aPresContext
->SetBidiEnabled();
1083 // Always show the first line, even if we have to clip it below
1084 bool firstLine
= true;
1085 while (strLen
> 0) {
1087 // If we've run out of space, break out of the loop
1088 if ((!isVertical
&& (pt
.y
+ maxDescent
) >= aRect
.YMost()) ||
1089 (wm
.IsVerticalRL() && (pt
.x
+ maxDescent
< aRect
.x
)) ||
1090 (wm
.IsVerticalLR() && (pt
.x
+ maxDescent
>= aRect
.XMost()))) {
1095 // Determine how much of the text to display on this line
1096 uint32_t maxFit
; // number of characters that fit
1097 nscoord strWidth
= MeasureString(str
, strLen
, iSize
, maxFit
,
1098 aRenderingContext
, *fm
);
1101 nsresult rv
= NS_ERROR_FAILURE
;
1103 if (aPresContext
->BidiEnabled()) {
1104 nsBidiDirection dir
;
1108 x
= pt
.x
+ maxDescent
; // XXX will need update for sideways-left
1109 if (wm
.IsBidiLTR()) {
1113 y
= aRect
.YMost() - strWidth
;
1117 y
= pt
.y
+ maxAscent
;
1118 if (wm
.IsBidiLTR()) {
1122 x
= aRect
.XMost() - strWidth
;
1127 rv
= nsBidiPresUtils::RenderText(str
, maxFit
, dir
,
1128 aPresContext
, aRenderingContext
,
1129 aRenderingContext
, *fm
, x
, y
);
1131 if (NS_FAILED(rv
)) {
1132 nsLayoutUtils::DrawUniDirString(str
, maxFit
,
1134 ? nsPoint(pt
.x
+ maxDescent
, pt
.y
)
1135 : nsPoint(pt
.x
, pt
.y
+ maxAscent
),
1136 *fm
, aRenderingContext
);
1139 // Move to the next line
1142 if (wm
.IsVerticalRL()) {
1144 } else if (wm
.IsVerticalLR()) {
1154 struct nsRecessedBorder
: public nsStyleBorder
{
1155 nsRecessedBorder(nscoord aBorderWidth
, nsPresContext
* aPresContext
)
1156 : nsStyleBorder(aPresContext
)
1158 NS_FOR_CSS_SIDES(side
) {
1159 // Note: use SetBorderColor here because we want to make sure
1160 // the "special" flags are unset.
1161 SetBorderColor(side
, NS_RGB(0, 0, 0));
1162 mBorder
.Side(side
) = aBorderWidth
;
1163 // Note: use SetBorderStyle here because we want to affect
1165 SetBorderStyle(side
, NS_STYLE_BORDER_STYLE_INSET
);
1170 class nsDisplayAltFeedback
: public nsDisplayItem
{
1172 nsDisplayAltFeedback(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
)
1173 : nsDisplayItem(aBuilder
, aFrame
) {}
1175 virtual nsRect
GetBounds(nsDisplayListBuilder
* aBuilder
,
1176 bool* aSnap
) MOZ_OVERRIDE
1179 return mFrame
->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
1182 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
1183 nsRenderingContext
* aCtx
) MOZ_OVERRIDE
1185 nsImageFrame
* f
= static_cast<nsImageFrame
*>(mFrame
);
1186 EventStates state
= f
->GetContent()->AsElement()->State();
1187 f
->DisplayAltFeedback(*aCtx
,
1189 IMAGE_OK(state
, true)
1190 ? nsImageFrame::gIconLoad
->mLoadingImage
1191 : nsImageFrame::gIconLoad
->mBrokenImage
,
1192 ToReferenceFrame());
1196 NS_DISPLAY_DECL_NAME("AltFeedback", TYPE_ALT_FEEDBACK
)
1200 nsImageFrame::DisplayAltFeedback(nsRenderingContext
& aRenderingContext
,
1201 const nsRect
& aDirtyRect
,
1202 imgIRequest
* aRequest
,
1205 // We should definitely have a gIconLoad here.
1206 NS_ABORT_IF_FALSE(gIconLoad
, "How did we succeed in Init then?");
1208 // Calculate the inner area
1209 nsRect inner
= GetInnerArea() + aPt
;
1211 // Display a recessed one pixel border
1212 nscoord borderEdgeWidth
= nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH
);
1214 // if inner area is empty, then make it big enough for at least the icon
1215 if (inner
.IsEmpty()){
1216 inner
.SizeTo(2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
+ICON_PADDING
+ALT_BORDER_WIDTH
)),
1217 2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
+ICON_PADDING
+ALT_BORDER_WIDTH
)));
1220 // Make sure we have enough room to actually render the border within
1222 if ((inner
.width
< 2 * borderEdgeWidth
) || (inner
.height
< 2 * borderEdgeWidth
)) {
1227 nsRecessedBorder
recessedBorder(borderEdgeWidth
, PresContext());
1228 nsCSSRendering::PaintBorderWithStyleBorder(PresContext(), aRenderingContext
,
1230 recessedBorder
, mStyleContext
);
1232 // Adjust the inner rect to account for the one pixel recessed border,
1233 // and a six pixel padding on each edge
1234 inner
.Deflate(nsPresContext::CSSPixelsToAppUnits(ICON_PADDING
+ALT_BORDER_WIDTH
),
1235 nsPresContext::CSSPixelsToAppUnits(ICON_PADDING
+ALT_BORDER_WIDTH
));
1236 if (inner
.IsEmpty()) {
1240 DrawTarget
* drawTarget
= aRenderingContext
.GetDrawTarget();
1241 gfxContext
* gfx
= aRenderingContext
.ThebesContext();
1243 // Clip so we don't render outside the inner rect
1245 gfx
->Clip(NSRectToSnappedRect(inner
, PresContext()->AppUnitsPerDevPixel(),
1248 // Check if we should display image placeholders
1249 if (gIconLoad
->mPrefShowPlaceholders
) {
1250 nscoord size
= nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
);
1252 bool iconUsed
= false;
1254 // If we weren't previously displaying an icon, register ourselves
1255 // as an observer for load and animation updates and flag that we're
1257 if (aRequest
&& !mDisplayingIcon
) {
1258 gIconLoad
->AddIconObserver(this);
1259 mDisplayingIcon
= true;
1262 WritingMode wm
= GetWritingMode();
1264 (!wm
.IsVertical() && !wm
.IsBidiLTR()) || wm
.IsVerticalRL();
1266 // If the icon in question is loaded and decoded, draw it
1267 uint32_t imageStatus
= 0;
1269 aRequest
->GetImageStatus(&imageStatus
);
1270 if (imageStatus
& imgIRequest::STATUS_LOAD_COMPLETE
) {
1271 nsCOMPtr
<imgIContainer
> imgCon
;
1272 aRequest
->GetImage(getter_AddRefs(imgCon
));
1273 MOZ_ASSERT(imgCon
, "Load complete, but no image container?");
1274 nsRect
dest(flushRight
? inner
.XMost() - size
: inner
.x
,
1275 inner
.y
, size
, size
);
1276 nsLayoutUtils::DrawSingleImage(*gfx
, PresContext(), imgCon
,
1277 nsLayoutUtils::GetGraphicsFilterForFrame(this), dest
, aDirtyRect
,
1278 nullptr, imgIContainer::FLAG_SYNC_DECODE
);
1282 // if we could not draw the icon, flag that we're waiting for it and
1283 // just draw some graffiti in the mean time
1285 ColorPattern
color(ToDeviceColor(Color(1.f
, 0.f
, 0.f
, 1.f
)));
1287 nscoord iconXPos
= flushRight
? inner
.XMost() - size
: inner
.x
;
1290 nsRect
rect(iconXPos
, inner
.y
, size
, size
);
1292 ToRect(nsLayoutUtils::RectToGfxRect(rect
, PresContext()->AppUnitsPerDevPixel()));
1293 drawTarget
->StrokeRect(devPxRect
, color
);
1295 // filled circle in bottom right quadrant of stroked rect:
1296 nscoord twoPX
= nsPresContext::CSSPixelsToAppUnits(2);
1297 rect
= nsRect(iconXPos
+ size
/2, inner
.y
+ size
/2,
1298 size
/2 - twoPX
, size
/2 - twoPX
);
1300 ToRect(nsLayoutUtils::RectToGfxRect(rect
, PresContext()->AppUnitsPerDevPixel()));
1301 RefPtr
<PathBuilder
> builder
= drawTarget
->CreatePathBuilder();
1302 AppendEllipseToPath(builder
, devPxRect
.Center(), devPxRect
.Size());
1303 RefPtr
<Path
> ellipse
= builder
->Finish();
1304 drawTarget
->Fill(ellipse
, color
);
1307 // Reduce the inner rect by the width of the icon, and leave an
1308 // additional ICON_PADDING pixels for padding
1309 int32_t paddedIconSize
=
1310 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
+ ICON_PADDING
);
1311 if (wm
.IsVertical()) {
1312 inner
.y
+= paddedIconSize
;
1313 inner
.height
-= paddedIconSize
;
1316 inner
.x
+= paddedIconSize
;
1318 inner
.width
-= paddedIconSize
;
1322 // If there's still room, display the alt-text
1323 if (!inner
.IsEmpty()) {
1324 nsIContent
* content
= GetContent();
1326 nsXPIDLString altText
;
1327 nsCSSFrameConstructor::GetAlternateTextFor(content
, content
->Tag(),
1329 DisplayAltText(PresContext(), aRenderingContext
, altText
, inner
);
1333 aRenderingContext
.ThebesContext()->Restore();
1337 static void PaintDebugImageMap(nsIFrame
* aFrame
, nsRenderingContext
* aCtx
,
1338 const nsRect
& aDirtyRect
, nsPoint aPt
)
1340 nsImageFrame
* f
= static_cast<nsImageFrame
*>(aFrame
);
1341 nsRect inner
= f
->GetInnerArea() + aPt
;
1342 gfxPoint devPixelOffset
=
1343 nsLayoutUtils::PointToGfxPoint(inner
.TopLeft(),
1344 aFrame
->PresContext()->AppUnitsPerDevPixel());
1345 DrawTarget
* drawTarget
= aCtx
->GetDrawTarget();
1346 AutoRestoreTransform
autoRestoreTransform(drawTarget
);
1347 drawTarget
->SetTransform(
1348 drawTarget
->GetTransform().PreTranslate(ToPoint(devPixelOffset
)));
1349 f
->GetImageMap()->Draw(aFrame
, *drawTarget
,
1350 ColorPattern(ToDeviceColor(Color(0.f
, 0.f
, 0.f
, 1.f
))));
1355 nsDisplayImage::Paint(nsDisplayListBuilder
* aBuilder
,
1356 nsRenderingContext
* aCtx
) {
1357 uint32_t flags
= imgIContainer::FLAG_NONE
;
1358 if (aBuilder
->ShouldSyncDecodeImages()) {
1359 flags
|= imgIContainer::FLAG_SYNC_DECODE
;
1361 if (aBuilder
->IsPaintingToWindow()) {
1362 flags
|= imgIContainer::FLAG_HIGH_QUALITY_SCALING
;
1365 DrawResult result
= static_cast<nsImageFrame
*>(mFrame
)->
1366 PaintImage(*aCtx
, ToReferenceFrame(), mVisibleRect
, mImage
, flags
);
1368 nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result
);
1371 nsDisplayItemGeometry
*
1372 nsDisplayImage::AllocateGeometry(nsDisplayListBuilder
* aBuilder
)
1374 return new nsDisplayItemGenericImageGeometry(this, aBuilder
);
1378 nsDisplayImage::ComputeInvalidationRegion(nsDisplayListBuilder
* aBuilder
,
1379 const nsDisplayItemGeometry
* aGeometry
,
1380 nsRegion
* aInvalidRegion
)
1383 static_cast<const nsDisplayItemGenericImageGeometry
*>(aGeometry
);
1385 if (aBuilder
->ShouldSyncDecodeImages() &&
1386 geometry
->ShouldInvalidateToSyncDecodeImages()) {
1388 aInvalidRegion
->Or(*aInvalidRegion
, GetBounds(aBuilder
, &snap
));
1391 nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder
, aGeometry
, aInvalidRegion
);
1394 already_AddRefed
<ImageContainer
>
1395 nsDisplayImage::GetContainer(LayerManager
* aManager
,
1396 nsDisplayListBuilder
* aBuilder
)
1398 nsRefPtr
<ImageContainer
> container
;
1399 mImage
->GetImageContainer(aManager
, getter_AddRefs(container
));
1400 return container
.forget();
1404 nsDisplayImage::GetDestRect()
1406 int32_t factor
= mFrame
->PresContext()->AppUnitsPerDevPixel();
1409 nsRect dest
= GetBounds(&snap
);
1410 gfxRect
destRect(dest
.x
, dest
.y
, dest
.width
, dest
.height
);
1411 destRect
.ScaleInverse(factor
);
1417 nsDisplayImage::GetLayerState(nsDisplayListBuilder
* aBuilder
,
1418 LayerManager
* aManager
,
1419 const ContainerLayerParameters
& aParameters
)
1421 bool animated
= false;
1422 if (!nsLayoutUtils::AnimatedImageLayersEnabled() ||
1423 mImage
->GetType() != imgIContainer::TYPE_RASTER
||
1424 NS_FAILED(mImage
->GetAnimated(&animated
)) ||
1426 if (!aManager
->IsCompositingCheap() ||
1427 !nsLayoutUtils::GPUImageScalingEnabled()) {
1434 int32_t imageHeight
;
1435 mImage
->GetWidth(&imageWidth
);
1436 mImage
->GetHeight(&imageHeight
);
1438 NS_ASSERTION(imageWidth
!= 0 && imageHeight
!= 0, "Invalid image size!");
1440 gfxRect destRect
= GetDestRect();
1442 destRect
.width
*= aParameters
.mXScale
;
1443 destRect
.height
*= aParameters
.mYScale
;
1445 // Calculate the scaling factor for the frame.
1446 gfxSize scale
= gfxSize(destRect
.width
/ imageWidth
,
1447 destRect
.height
/ imageHeight
);
1449 // If we are not scaling at all, no point in separating this into a layer.
1450 if (scale
.width
== 1.0f
&& scale
.height
== 1.0f
) {
1454 // If the target size is pretty small, no point in using a layer.
1455 if (destRect
.width
* destRect
.height
< 64 * 64) {
1460 nsRefPtr
<ImageContainer
> container
;
1461 mImage
->GetImageContainer(aManager
, getter_AddRefs(container
));
1466 return LAYER_ACTIVE
;
1470 /* virtual */ nsRegion
1471 nsDisplayImage::GetOpaqueRegion(nsDisplayListBuilder
* aBuilder
,
1475 if (mImage
&& mImage
->IsOpaque()) {
1476 // OK, the entire region painted by the image is opaque. But what is that
1477 // region? It's the image's "dest rect" (the rect where a full copy of
1478 // the image is mapped), clipped to the container's content box (which is
1479 // what GetBounds() returns). So, we grab those rects and intersect them.
1480 const nsRect frameContentBox
= GetBounds(aSnap
);
1482 // Note: To get the "dest rect", we have to provide the "constraint rect"
1483 // (which is the content-box, with the effects of fragmentation undone).
1484 nsImageFrame
* imageFrame
= static_cast<nsImageFrame
*>(mFrame
);
1485 nsRect
constraintRect(frameContentBox
.TopLeft(),
1486 imageFrame
->mComputedSize
);
1487 constraintRect
.y
-= imageFrame
->GetContinuationOffset();
1489 const nsRect destRect
=
1490 nsLayoutUtils::ComputeObjectDestRect(constraintRect
,
1491 imageFrame
->mIntrinsicSize
,
1492 imageFrame
->mIntrinsicRatio
,
1493 imageFrame
->StylePosition());
1495 return nsRegion(destRect
.Intersect(frameContentBox
));
1500 already_AddRefed
<Layer
>
1501 nsDisplayImage::BuildLayer(nsDisplayListBuilder
* aBuilder
,
1502 LayerManager
* aManager
,
1503 const ContainerLayerParameters
& aParameters
)
1505 nsRefPtr
<ImageContainer
> container
;
1506 nsresult rv
= mImage
->GetImageContainer(aManager
, getter_AddRefs(container
));
1507 NS_ENSURE_SUCCESS(rv
, nullptr);
1509 nsRefPtr
<ImageLayer
> layer
= static_cast<ImageLayer
*>
1510 (aManager
->GetLayerBuilder()->GetLeafLayerFor(aBuilder
, this));
1512 layer
= aManager
->CreateImageLayer();
1516 layer
->SetContainer(container
);
1517 ConfigureLayer(layer
, aParameters
.mOffset
);
1518 return layer
.forget();
1522 nsDisplayImage::ConfigureLayer(ImageLayer
*aLayer
, const nsIntPoint
& aOffset
)
1524 aLayer
->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame
));
1527 int32_t imageHeight
;
1528 mImage
->GetWidth(&imageWidth
);
1529 mImage
->GetHeight(&imageHeight
);
1531 NS_ASSERTION(imageWidth
!= 0 && imageHeight
!= 0, "Invalid image size!");
1532 if (imageWidth
> 0 && imageHeight
> 0) {
1533 // We're actually using the ImageContainer. Let our frame know that it
1534 // should consider itself to have painted successfully.
1535 nsDisplayItemGenericImageGeometry::UpdateDrawResult(this,
1536 DrawResult::SUCCESS
);
1539 const gfxRect destRect
= GetDestRect();
1541 gfxPoint p
= destRect
.TopLeft() + aOffset
;
1542 Matrix transform
= Matrix::Translation(p
.x
, p
.y
);
1543 transform
.PreScale(destRect
.Width() / imageWidth
,
1544 destRect
.Height() / imageHeight
);
1545 aLayer
->SetBaseTransform(gfx::Matrix4x4::From2D(transform
));
1549 nsImageFrame::PaintImage(nsRenderingContext
& aRenderingContext
, nsPoint aPt
,
1550 const nsRect
& aDirtyRect
, imgIContainer
* aImage
,
1553 DrawTarget
* drawTarget
= aRenderingContext
.GetDrawTarget();
1555 // Render the image into our content area (the area inside
1556 // the borders and padding)
1557 NS_ASSERTION(GetInnerArea().width
== mComputedSize
.width
, "bad width");
1559 // NOTE: We use mComputedSize instead of just GetInnerArea()'s own size here,
1560 // because GetInnerArea() might be smaller if we're fragmented, whereas
1561 // mComputedSize has our full content-box size (which we need for
1562 // ComputeObjectDestRect to work correctly).
1563 nsRect
constraintRect(aPt
+ GetInnerArea().TopLeft(), mComputedSize
);
1564 constraintRect
.y
-= GetContinuationOffset();
1566 nsPoint anchorPoint
;
1567 nsRect dest
= nsLayoutUtils::ComputeObjectDestRect(constraintRect
,
1574 nsLayoutUtils::DrawSingleImage(*aRenderingContext
.ThebesContext(),
1575 PresContext(), aImage
,
1576 nsLayoutUtils::GetGraphicsFilterForFrame(this), dest
, aDirtyRect
,
1577 nullptr, aFlags
, &anchorPoint
);
1579 nsImageMap
* map
= GetImageMap();
1581 gfxPoint devPixelOffset
=
1582 nsLayoutUtils::PointToGfxPoint(dest
.TopLeft(),
1583 PresContext()->AppUnitsPerDevPixel());
1584 AutoRestoreTransform
autoRestoreTransform(drawTarget
);
1585 drawTarget
->SetTransform(
1586 drawTarget
->GetTransform().PreTranslate(ToPoint(devPixelOffset
)));
1588 // solid white stroke:
1589 ColorPattern
white(ToDeviceColor(Color(1.f
, 1.f
, 1.f
, 1.f
)));
1590 map
->Draw(this, *drawTarget
, white
);
1592 // then dashed black stroke over the top:
1593 ColorPattern
black(ToDeviceColor(Color(0.f
, 0.f
, 0.f
, 1.f
)));
1594 StrokeOptions strokeOptions
;
1595 nsLayoutUtils::InitDashPattern(strokeOptions
, NS_STYLE_BORDER_STYLE_DOTTED
);
1596 map
->Draw(this, *drawTarget
, black
, strokeOptions
);
1603 nsImageFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
1604 const nsRect
& aDirtyRect
,
1605 const nsDisplayListSet
& aLists
)
1607 if (!IsVisibleForPainting(aBuilder
))
1610 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
1612 uint32_t clipFlags
=
1613 nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition()) ?
1614 0 : DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT
;
1616 DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
1617 clip(aBuilder
, this, clipFlags
);
1619 if (mComputedSize
.width
!= 0 && mComputedSize
.height
!= 0) {
1620 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
1621 NS_ASSERTION(imageLoader
, "Not an image loading content?");
1623 nsCOMPtr
<imgIRequest
> currentRequest
;
1625 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
1626 getter_AddRefs(currentRequest
));
1629 EventStates contentState
= mContent
->AsElement()->State();
1630 bool imageOK
= IMAGE_OK(contentState
, true);
1632 // XXX(seth): The SizeIsAvailable check here should not be necessary - the
1633 // intention is that a non-null mImage means we have a size, but there is
1634 // currently some code that violates this invariant.
1635 if (!imageOK
|| !mImage
|| !SizeIsAvailable(currentRequest
)) {
1636 // No image yet, or image load failed. Draw the alt-text and an icon
1637 // indicating the status
1638 aLists
.Content()->AppendNewToTop(new (aBuilder
)
1639 nsDisplayAltFeedback(aBuilder
, this));
1641 aLists
.Content()->AppendNewToTop(new (aBuilder
)
1642 nsDisplayImage(aBuilder
, this, mImage
));
1644 // If we were previously displaying an icon, we're not anymore
1645 if (mDisplayingIcon
) {
1646 gIconLoad
->RemoveIconObserver(this);
1647 mDisplayingIcon
= false;
1651 if (GetShowFrameBorders() && GetImageMap()) {
1652 aLists
.Outlines()->AppendNewToTop(new (aBuilder
)
1653 nsDisplayGeneric(aBuilder
, this, PaintDebugImageMap
, "DebugImageMap",
1654 nsDisplayItem::TYPE_DEBUG_IMAGE_MAP
));
1660 if (ShouldDisplaySelection()) {
1661 DisplaySelectionOverlay(aBuilder
, aLists
.Content(),
1662 nsISelectionDisplay::DISPLAY_IMAGES
);
1667 nsImageFrame::ShouldDisplaySelection()
1669 // XXX what on EARTH is this code for?
1671 nsPresContext
* presContext
= PresContext();
1672 int16_t displaySelection
= presContext
->PresShell()->GetSelectionFlags();
1673 if (!(displaySelection
& nsISelectionDisplay::DISPLAY_IMAGES
))
1674 return false;//no need to check the blue border, we cannot be drawn selected
1675 //insert hook here for image selection drawing
1676 #if IMAGE_EDITOR_CHECK
1677 //check to see if this frame is in an editor context
1678 //isEditor check. this needs to be changed to have better way to check
1679 if (displaySelection
== nsISelectionDisplay::DISPLAY_ALL
)
1681 nsCOMPtr
<nsISelectionController
> selCon
;
1682 result
= GetSelectionController(presContext
, getter_AddRefs(selCon
));
1683 if (NS_SUCCEEDED(result
) && selCon
)
1685 nsCOMPtr
<nsISelection
> selection
;
1686 result
= selCon
->GetSelection(nsISelectionController::SELECTION_NORMAL
, getter_AddRefs(selection
));
1687 if (NS_SUCCEEDED(result
) && selection
)
1690 selection
->GetRangeCount(&rangeCount
);
1691 if (rangeCount
== 1) //if not one then let code drop to nsFrame::Paint
1693 nsCOMPtr
<nsIContent
> parentContent
= mContent
->GetParent();
1696 int32_t thisOffset
= parentContent
->IndexOf(mContent
);
1697 nsCOMPtr
<nsIDOMNode
> parentNode
= do_QueryInterface(parentContent
);
1698 nsCOMPtr
<nsIDOMNode
> rangeNode
;
1699 int32_t rangeOffset
;
1700 nsCOMPtr
<nsIDOMRange
> range
;
1701 selection
->GetRangeAt(0,getter_AddRefs(range
));
1704 range
->GetStartContainer(getter_AddRefs(rangeNode
));
1705 range
->GetStartOffset(&rangeOffset
);
1707 if (parentNode
&& rangeNode
&& (rangeNode
== parentNode
) && rangeOffset
== thisOffset
)
1709 range
->GetEndContainer(getter_AddRefs(rangeNode
));
1710 range
->GetEndOffset(&rangeOffset
);
1711 if ((rangeNode
== parentNode
) && (rangeOffset
== (thisOffset
+1))) //+1 since that would mean this whole content is selected only
1712 return false; //do not allow nsFrame do draw any further selection
1725 nsImageFrame::GetImageMap()
1728 nsIContent
* map
= GetMapElement();
1730 mImageMap
= new nsImageMap();
1731 NS_ADDREF(mImageMap
);
1732 mImageMap
->Init(this, map
);
1740 nsImageFrame::IsServerImageMap()
1742 return mContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::ismap
);
1745 // Translate an point that is relative to our frame
1746 // into a localized pixel coordinate that is relative to the
1747 // content area of this frame (inside the border+padding).
1749 nsImageFrame::TranslateEventCoords(const nsPoint
& aPoint
,
1750 nsIntPoint
& aResult
)
1752 nscoord x
= aPoint
.x
;
1753 nscoord y
= aPoint
.y
;
1755 // Subtract out border and padding here so that the coordinates are
1756 // now relative to the content area of this frame.
1757 nsRect inner
= GetInnerArea();
1761 aResult
.x
= nsPresContext::AppUnitsToIntCSSPixels(x
);
1762 aResult
.y
= nsPresContext::AppUnitsToIntCSSPixels(y
);
1766 nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI
** aHref
, nsString
& aTarget
,
1769 bool status
= false;
1774 // Walk up the content tree, looking for an nsIDOMAnchorElement
1775 for (nsIContent
* content
= mContent
->GetParent();
1776 content
; content
= content
->GetParent()) {
1777 nsCOMPtr
<dom::Link
> link(do_QueryInterface(content
));
1779 nsCOMPtr
<nsIURI
> href
= content
->GetHrefURI();
1783 status
= (*aHref
!= nullptr);
1785 nsCOMPtr
<nsIDOMHTMLAnchorElement
> anchor(do_QueryInterface(content
));
1787 anchor
->GetTarget(aTarget
);
1789 NS_ADDREF(*aNode
= content
);
1797 nsImageFrame::GetContentForEvent(WidgetEvent
* aEvent
,
1798 nsIContent
** aContent
)
1800 NS_ENSURE_ARG_POINTER(aContent
);
1802 nsIFrame
* f
= nsLayoutUtils::GetNonGeneratedAncestor(this);
1804 return f
->GetContentForEvent(aEvent
, aContent
);
1807 // XXX We need to make this special check for area element's capturing the
1808 // mouse due to bug 135040. Remove it once that's fixed.
1809 nsIContent
* capturingContent
=
1810 aEvent
->HasMouseEventMessage() ? nsIPresShell::GetCapturingContent() :
1812 if (capturingContent
&& capturingContent
->GetPrimaryFrame() == this) {
1813 *aContent
= capturingContent
;
1814 NS_IF_ADDREF(*aContent
);
1818 nsImageMap
* map
= GetImageMap();
1820 if (nullptr != map
) {
1822 TranslateEventCoords(
1823 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent
, this), p
);
1824 nsCOMPtr
<nsIContent
> area
= map
->GetArea(p
.x
, p
.y
);
1826 area
.forget(aContent
);
1831 *aContent
= GetContent();
1832 NS_IF_ADDREF(*aContent
);
1836 // XXX what should clicks on transparent pixels do?
1838 nsImageFrame::HandleEvent(nsPresContext
* aPresContext
,
1839 WidgetGUIEvent
* aEvent
,
1840 nsEventStatus
* aEventStatus
)
1842 NS_ENSURE_ARG_POINTER(aEventStatus
);
1844 if ((aEvent
->message
== NS_MOUSE_BUTTON_UP
&&
1845 aEvent
->AsMouseEvent()->button
== WidgetMouseEvent::eLeftButton
) ||
1846 aEvent
->message
== NS_MOUSE_MOVE
) {
1847 nsImageMap
* map
= GetImageMap();
1848 bool isServerMap
= IsServerImageMap();
1849 if ((nullptr != map
) || isServerMap
) {
1851 TranslateEventCoords(
1852 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent
, this), p
);
1853 bool inside
= false;
1854 // Even though client-side image map triggering happens
1855 // through content, we need to make sure we're not inside
1856 // (in case we deal with a case of both client-side and
1857 // sever-side on the same image - it happens!)
1858 if (nullptr != map
) {
1859 inside
= !!map
->GetArea(p
.x
, p
.y
);
1862 if (!inside
&& isServerMap
) {
1864 // Server side image maps use the href in a containing anchor
1865 // element to provide the basis for the destination url.
1866 nsCOMPtr
<nsIURI
> uri
;
1867 nsAutoString target
;
1868 nsCOMPtr
<nsIContent
> anchorNode
;
1869 if (GetAnchorHREFTargetAndNode(getter_AddRefs(uri
), target
,
1870 getter_AddRefs(anchorNode
))) {
1871 // XXX if the mouse is over/clicked in the border/padding area
1872 // we should probably just pretend nothing happened. Nav4
1873 // keeps the x,y coordinates positive as we do; IE doesn't
1874 // bother. Both of them send the click through even when the
1875 // mouse is over the border.
1876 if (p
.x
< 0) p
.x
= 0;
1877 if (p
.y
< 0) p
.y
= 0;
1880 spec
+= nsPrintfCString("?%d,%d", p
.x
, p
.y
);
1883 bool clicked
= false;
1884 if (aEvent
->message
== NS_MOUSE_BUTTON_UP
) {
1885 *aEventStatus
= nsEventStatus_eConsumeDoDefault
;
1888 nsContentUtils::TriggerLink(anchorNode
, aPresContext
, uri
, target
,
1889 clicked
, true, true);
1895 return nsSplittableFrame::HandleEvent(aPresContext
, aEvent
, aEventStatus
);
1899 nsImageFrame::GetCursor(const nsPoint
& aPoint
,
1900 nsIFrame::Cursor
& aCursor
)
1902 nsImageMap
* map
= GetImageMap();
1903 if (nullptr != map
) {
1905 TranslateEventCoords(aPoint
, p
);
1906 nsCOMPtr
<nsIContent
> area
= map
->GetArea(p
.x
, p
.y
);
1908 // Use the cursor from the style of the *area* element.
1909 // XXX Using the image as the parent style context isn't
1910 // technically correct, but it's probably the right thing to do
1911 // here, since it means that areas on which the cursor isn't
1912 // specified will inherit the style from the image.
1913 nsRefPtr
<nsStyleContext
> areaStyle
=
1914 PresContext()->PresShell()->StyleSet()->
1915 ResolveStyleFor(area
->AsElement(), StyleContext());
1916 FillCursorInformationFromStyle(areaStyle
->StyleUserInterface(),
1918 if (NS_STYLE_CURSOR_AUTO
== aCursor
.mCursor
) {
1919 aCursor
.mCursor
= NS_STYLE_CURSOR_DEFAULT
;
1924 return nsFrame::GetCursor(aPoint
, aCursor
);
1928 nsImageFrame::AttributeChanged(int32_t aNameSpaceID
,
1929 nsIAtom
* aAttribute
,
1932 nsresult rv
= nsSplittableFrame::AttributeChanged(aNameSpaceID
,
1933 aAttribute
, aModType
);
1934 if (NS_FAILED(rv
)) {
1937 if (nsGkAtoms::alt
== aAttribute
)
1939 PresContext()->PresShell()->FrameNeedsReflow(this,
1940 nsIPresShell::eStyleChange
,
1948 nsImageFrame::GetType() const
1950 return nsGkAtoms::imageFrame
;
1953 #ifdef DEBUG_FRAME_DUMP
1955 nsImageFrame::GetFrameName(nsAString
& aResult
) const
1957 return MakeFrameName(NS_LITERAL_STRING("ImageFrame"), aResult
);
1961 nsImageFrame::List(FILE* out
, const char* aPrefix
, uint32_t aFlags
) const
1964 ListGeneric(str
, aPrefix
, aFlags
);
1966 // output the img src url
1967 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
1969 nsCOMPtr
<imgIRequest
> currentRequest
;
1970 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
1971 getter_AddRefs(currentRequest
));
1972 if (currentRequest
) {
1973 nsCOMPtr
<nsIURI
> uri
;
1974 currentRequest
->GetURI(getter_AddRefs(uri
));
1975 nsAutoCString uristr
;
1976 uri
->GetAsciiSpec(uristr
);
1977 str
+= nsPrintfCString(" [src=%s]", uristr
.get());
1980 fprintf_stderr(out
, "%s\n", str
.get());
1984 nsIFrame::LogicalSides
1985 nsImageFrame::GetLogicalSkipSides(const nsHTMLReflowState
* aReflowState
) const
1987 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak
==
1988 NS_STYLE_BOX_DECORATION_BREAK_CLONE
)) {
1989 return LogicalSides();
1992 if (nullptr != GetPrevInFlow()) {
1993 skip
|= eLogicalSideBitsBStart
;
1995 if (nullptr != GetNextInFlow()) {
1996 skip
|= eLogicalSideBitsBEnd
;
2002 nsImageFrame::GetIntrinsicImageSize(nsSize
& aSize
)
2004 if (mIntrinsicSize
.width
.GetUnit() == eStyleUnit_Coord
&&
2005 mIntrinsicSize
.height
.GetUnit() == eStyleUnit_Coord
) {
2006 aSize
.SizeTo(mIntrinsicSize
.width
.GetCoordValue(),
2007 mIntrinsicSize
.height
.GetCoordValue());
2011 return NS_ERROR_FAILURE
;
2015 nsImageFrame::LoadIcon(const nsAString
& aSpec
,
2016 nsPresContext
*aPresContext
,
2017 imgRequestProxy
** aRequest
)
2019 nsresult rv
= NS_OK
;
2020 NS_PRECONDITION(!aSpec
.IsEmpty(), "What happened??");
2023 rv
= CallGetService(NS_IOSERVICE_CONTRACTID
, &sIOService
);
2024 NS_ENSURE_SUCCESS(rv
, rv
);
2027 nsCOMPtr
<nsIURI
> realURI
;
2028 SpecToURI(aSpec
, sIOService
, getter_AddRefs(realURI
));
2030 nsRefPtr
<imgLoader
> il
=
2031 nsContentUtils::GetImgLoaderForDocument(aPresContext
->Document());
2033 nsCOMPtr
<nsILoadGroup
> loadGroup
;
2034 GetLoadGroup(aPresContext
, getter_AddRefs(loadGroup
));
2036 // For icon loads, we don't need to merge with the loadgroup flags
2037 nsLoadFlags loadFlags
= nsIRequest::LOAD_NORMAL
;
2038 nsContentPolicyType contentPolicyType
= nsIContentPolicy::TYPE_IMAGE
;
2040 return il
->LoadImage(realURI
, /* icon URI */
2041 nullptr, /* initial document URI; this is only
2042 relevant for cookies, so does not
2044 nullptr, /* referrer (not relevant for icons) */
2045 mozilla::net::RP_Default
,
2046 nullptr, /* principal (not relevant for icons) */
2049 nullptr, /* Not associated with any particular document */
2058 nsImageFrame::GetDocumentCharacterSet(nsACString
& aCharset
) const
2061 NS_ASSERTION(mContent
->GetComposedDoc(),
2062 "Frame still alive after content removed from document!");
2063 aCharset
= mContent
->GetComposedDoc()->GetDocumentCharacterSet();
2068 nsImageFrame::SpecToURI(const nsAString
& aSpec
, nsIIOService
*aIOService
,
2071 nsCOMPtr
<nsIURI
> baseURI
;
2073 baseURI
= mContent
->GetBaseURI();
2075 nsAutoCString charset
;
2076 GetDocumentCharacterSet(charset
);
2077 NS_NewURI(aURI
, aSpec
,
2078 charset
.IsEmpty() ? nullptr : charset
.get(),
2079 baseURI
, aIOService
);
2083 nsImageFrame::GetLoadGroup(nsPresContext
*aPresContext
, nsILoadGroup
**aLoadGroup
)
2088 NS_PRECONDITION(nullptr != aLoadGroup
, "null OUT parameter pointer");
2090 nsIPresShell
*shell
= aPresContext
->GetPresShell();
2095 nsIDocument
*doc
= shell
->GetDocument();
2099 *aLoadGroup
= doc
->GetDocumentLoadGroup().take();
2102 nsresult
nsImageFrame::LoadIcons(nsPresContext
*aPresContext
)
2104 NS_ASSERTION(!gIconLoad
, "called LoadIcons twice");
2106 NS_NAMED_LITERAL_STRING(loadingSrc
,"resource://gre-resources/loading-image.png");
2107 NS_NAMED_LITERAL_STRING(brokenSrc
,"resource://gre-resources/broken-image.png");
2109 gIconLoad
= new IconLoad();
2110 NS_ADDREF(gIconLoad
);
2113 // create a loader and load the images
2114 rv
= LoadIcon(loadingSrc
,
2116 getter_AddRefs(gIconLoad
->mLoadingImage
));
2117 if (NS_FAILED(rv
)) {
2120 gIconLoad
->mLoadingImage
->RequestDecode();
2122 rv
= LoadIcon(brokenSrc
,
2124 getter_AddRefs(gIconLoad
->mBrokenImage
));
2125 if (NS_FAILED(rv
)) {
2128 gIconLoad
->mBrokenImage
->RequestDecode();
2133 NS_IMPL_ISUPPORTS(nsImageFrame::IconLoad
, nsIObserver
,
2134 imgINotificationObserver
)
2136 static const char* kIconLoadPrefs
[] = {
2137 "browser.display.force_inline_alttext",
2138 "browser.display.show_image_placeholders",
2142 nsImageFrame::IconLoad::IconLoad()
2144 // register observers
2145 Preferences::AddStrongObservers(this, kIconLoadPrefs
);
2150 nsImageFrame::IconLoad::Shutdown()
2152 Preferences::RemoveObservers(this, kIconLoadPrefs
);
2153 // in case the pref service releases us later
2154 if (mLoadingImage
) {
2155 mLoadingImage
->CancelAndForgetObserver(NS_ERROR_FAILURE
);
2156 mLoadingImage
= nullptr;
2159 mBrokenImage
->CancelAndForgetObserver(NS_ERROR_FAILURE
);
2160 mBrokenImage
= nullptr;
2165 nsImageFrame::IconLoad::Observe(nsISupports
*aSubject
, const char* aTopic
,
2166 const char16_t
* aData
)
2168 NS_ASSERTION(!nsCRT::strcmp(aTopic
, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
),
2171 // assert |aData| is one of our prefs.
2172 for (uint32_t i
= 0; i
< ArrayLength(kIconLoadPrefs
) ||
2173 (NS_NOTREACHED("wrong pref"), false); ++i
)
2174 if (NS_ConvertASCIItoUTF16(kIconLoadPrefs
[i
]) == nsDependentString(aData
))
2182 void nsImageFrame::IconLoad::GetPrefs()
2184 mPrefForceInlineAltText
=
2185 Preferences::GetBool("browser.display.force_inline_alttext");
2187 mPrefShowPlaceholders
=
2188 Preferences::GetBool("browser.display.show_image_placeholders", true);
2192 nsImageFrame::IconLoad::Notify(imgIRequest
*aRequest
, int32_t aType
, const nsIntRect
* aData
)
2194 if (aType
!= imgINotificationObserver::LOAD_COMPLETE
&&
2195 aType
!= imgINotificationObserver::FRAME_UPDATE
) {
2199 nsTObserverArray
<nsImageFrame
*>::ForwardIterator
iter(mIconObservers
);
2200 nsImageFrame
*frame
;
2201 while (iter
.HasMore()) {
2202 frame
= iter
.GetNext();
2203 frame
->InvalidateFrame();
2209 NS_IMPL_ISUPPORTS(nsImageListener
, imgINotificationObserver
)
2211 nsImageListener::nsImageListener(nsImageFrame
*aFrame
) :
2216 nsImageListener::~nsImageListener()
2221 nsImageListener::Notify(imgIRequest
*aRequest
, int32_t aType
, const nsIntRect
* aData
)
2224 return NS_ERROR_FAILURE
;
2226 return mFrame
->Notify(aRequest
, aType
, aData
);
2230 IsInAutoWidthTableCellForQuirk(nsIFrame
*aFrame
)
2232 if (eCompatibility_NavQuirks
!= aFrame
->PresContext()->CompatibilityMode())
2234 // Check if the parent of the closest nsBlockFrame has auto width.
2235 nsBlockFrame
*ancestor
= nsLayoutUtils::FindNearestBlockAncestor(aFrame
);
2236 if (ancestor
->StyleContext()->GetPseudo() == nsCSSAnonBoxes::cellContent
) {
2237 // Assume direct parent is a table cell frame.
2238 nsFrame
*grandAncestor
= static_cast<nsFrame
*>(ancestor
->GetParent());
2239 return grandAncestor
&&
2240 grandAncestor
->StylePosition()->mWidth
.GetUnit() == eStyleUnit_Auto
;
2246 nsImageFrame::AddInlineMinISize(nsRenderingContext
*aRenderingContext
,
2247 nsIFrame::InlineMinISizeData
*aData
)
2250 NS_ASSERTION(GetParent(), "Must have a parent if we get here!");
2252 nsIFrame
* parent
= GetParent();
2254 !CanContinueTextRun() &&
2255 parent
->StyleText()->WhiteSpaceCanWrap(parent
) &&
2256 !IsInAutoWidthTableCellForQuirk(this);
2259 aData
->OptionallyBreak(aRenderingContext
);
2261 aData
->trailingWhitespace
= 0;
2262 aData
->skipWhitespace
= false;
2263 aData
->trailingTextFrame
= nullptr;
2264 aData
->currentLine
+= nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
2265 this, nsLayoutUtils::MIN_ISIZE
);
2266 aData
->atStartOfLine
= false;
2269 aData
->OptionallyBreak(aRenderingContext
);