1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 /* rendering object for replaced elements with bitmap image data */
41 #include "nsHTMLParts.h"
43 #include "nsImageFrame.h"
44 #include "nsIImageLoadingContent.h"
46 #include "nsPrintfCString.h"
47 #include "nsPresContext.h"
48 #include "nsIRenderingContext.h"
49 #include "nsIPresShell.h"
51 #include "nsIWidget.h"
52 #include "nsGkAtoms.h"
53 #include "nsIDocument.h"
54 #include "nsINodeInfo.h"
55 #include "nsContentUtils.h"
56 #include "nsCSSAnonBoxes.h"
57 #include "nsStyleContext.h"
58 #include "nsStyleConsts.h"
59 #include "nsImageMap.h"
60 #include "nsILinkHandler.h"
62 #include "nsIIOService.h"
64 #include "nsILoadGroup.h"
65 #include "nsISupportsPriority.h"
66 #include "nsIServiceManager.h"
67 #include "nsNetUtil.h"
68 #include "nsHTMLContainerFrame.h"
70 #include "nsIFontMetrics.h"
71 #include "nsCSSRendering.h"
73 #include "nsIDOMHTMLAnchorElement.h"
74 #include "nsIDOMHTMLImageElement.h"
75 #include "nsIDeviceContext.h"
76 #include "nsINameSpaceManager.h"
77 #include "nsTextFragment.h"
78 #include "nsIDOMHTMLMapElement.h"
79 #include "nsImageMapUtils.h"
80 #include "nsIScriptSecurityManager.h"
82 #include "nsIAccessibilityService.h"
84 #include "nsIServiceManager.h"
85 #include "nsIDOMNode.h"
86 #include "nsGUIEvent.h"
87 #include "nsLayoutUtils.h"
88 #include "nsDisplayList.h"
90 #include "imgIContainer.h"
91 #include "imgILoader.h"
93 #include "nsContentPolicyUtils.h"
94 #include "nsCSSFrameConstructor.h"
95 #include "nsIPrefBranch2.h"
96 #include "nsIPrefService.h"
97 #include "gfxIImageFrame.h"
98 #include "nsIDOMRange.h"
100 #include "nsIContentPolicy.h"
101 #include "nsContentPolicyUtils.h"
102 #include "nsIEventStateManager.h"
103 #include "nsLayoutErrors.h"
104 #include "nsBidiUtils.h"
105 #include "nsBidiPresUtils.h"
108 #undef NOISY_IMAGE_LOADING
109 #undef NOISY_ICON_LOADING
111 #undef NOISY_IMAGE_LOADING
112 #undef NOISY_ICON_LOADING
115 // sizes (pixels) for image icon, padding and border frame
116 #define ICON_SIZE (16)
117 #define ICON_PADDING (3)
118 #define ALT_BORDER_WIDTH (1)
121 //we must add hooks soon
122 #define IMAGE_EDITOR_CHECK 1
124 // Default alignment value (so we can tell an unset value from a set value)
125 #define ALIGN_UNSET PRUint8(-1)
127 // static icon information
128 nsImageFrame::IconLoad
* nsImageFrame::gIconLoad
= nsnull
;
130 // cached IO service for loading icons
131 nsIIOService
* nsImageFrame::sIOService
;
133 // test if the width and height are fixed, looking at the style data
134 static PRBool
HaveFixedSize(const nsStylePosition
* aStylePosition
)
136 // check the width and height values in the reflow state's style struct
137 // - if width and height are specified as either coord or percentage, then
138 // the size of the image frame is constrained
139 nsStyleUnit widthUnit
= aStylePosition
->mWidth
.GetUnit();
140 nsStyleUnit heightUnit
= aStylePosition
->mHeight
.GetUnit();
142 return ((widthUnit
== eStyleUnit_Coord
||
143 widthUnit
== eStyleUnit_Percent
) &&
144 (heightUnit
== eStyleUnit_Coord
||
145 heightUnit
== eStyleUnit_Percent
));
147 // use the data in the reflow state to decide if the image has a constrained size
148 // (i.e. width and height that are based on the containing block size and not the image size)
149 // so we can avoid animated GIF related reflows
150 inline PRBool
HaveFixedSize(const nsHTMLReflowState
& aReflowState
)
152 NS_ASSERTION(aReflowState
.mStylePosition
, "crappy reflowState - null stylePosition");
153 // when an image has percent css style height or width, but ComputedHeight()
154 // or ComputedWidth() of reflow state is NS_UNCONSTRAINEDSIZE
155 // it needs to return PR_FALSE to cause an incremental reflow later
156 // if an image is inside table like bug 156731 simple testcase III,
157 // during pass 1 reflow, ComputedWidth() is NS_UNCONSTRAINEDSIZE
158 // in pass 2 reflow, ComputedWidth() is 0, it also needs to return PR_FALSE
160 nsStyleUnit heightUnit
= (*(aReflowState
.mStylePosition
)).mHeight
.GetUnit();
161 nsStyleUnit widthUnit
= (*(aReflowState
.mStylePosition
)).mWidth
.GetUnit();
162 return ((eStyleUnit_Percent
== heightUnit
&& NS_UNCONSTRAINEDSIZE
== aReflowState
.ComputedHeight()) ||
163 (eStyleUnit_Percent
== widthUnit
&& (NS_UNCONSTRAINEDSIZE
== aReflowState
.ComputedWidth() ||
164 0 == aReflowState
.ComputedWidth())))
166 : HaveFixedSize(aReflowState
.mStylePosition
);
170 NS_NewImageFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
172 return new (aPresShell
) nsImageFrame(aContext
);
176 nsImageFrame::nsImageFrame(nsStyleContext
* aContext
) :
177 ImageFrameSuper(aContext
),
181 // We assume our size is not constrained and we haven't gotten an
182 // initial reflow yet, so don't touch those flags.
185 nsImageFrame::~nsImageFrame()
190 nsImageFrame::QueryInterface(const nsIID
& aIID
, void** aInstancePtr
)
192 NS_PRECONDITION(aInstancePtr
, "null out param");
194 if (aIID
.Equals(NS_GET_IID(nsIImageFrame
))) {
195 *aInstancePtr
= static_cast<nsIImageFrame
*>(this);
199 return ImageFrameSuper::QueryInterface(aIID
, aInstancePtr
);
203 NS_IMETHODIMP
nsImageFrame::GetAccessible(nsIAccessible
** aAccessible
)
205 nsCOMPtr
<nsIAccessibilityService
> accService
= do_GetService("@mozilla.org/accessibilityService;1");
208 return accService
->CreateHTMLImageAccessible(static_cast<nsIFrame
*>(this), aAccessible
);
211 return NS_ERROR_FAILURE
;
215 NS_IMETHODIMP_(nsrefcnt
) nsImageFrame::AddRef(void)
217 NS_WARNING("not supported for frames");
221 NS_IMETHODIMP_(nsrefcnt
) nsImageFrame::Release(void)
223 NS_WARNING("not supported for frames");
228 nsImageFrame::Destroy()
230 // Tell our image map, if there is one, to clean up
231 // This causes the nsImageMap to unregister itself as
234 mImageMap
->Destroy();
235 NS_RELEASE(mImageMap
);
238 // set the frame to null so we don't send messages to a dead object.
240 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
242 imageLoader
->RemoveObserver(mListener
);
245 reinterpret_cast<nsImageListener
*>(mListener
.get())->SetFrame(nsnull
);
250 nsSplittableFrame::Destroy();
256 nsImageFrame::Init(nsIContent
* aContent
,
258 nsIFrame
* aPrevInFlow
)
260 nsresult rv
= nsSplittableFrame::Init(aContent
, aParent
, aPrevInFlow
);
261 NS_ENSURE_SUCCESS(rv
, rv
);
263 mListener
= new nsImageListener(this);
264 if (!mListener
) return NS_ERROR_OUT_OF_MEMORY
;
266 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(aContent
);
267 NS_ENSURE_TRUE(imageLoader
, NS_ERROR_UNEXPECTED
);
268 imageLoader
->AddObserver(mListener
);
270 nsPresContext
*aPresContext
= PresContext();
273 LoadIcons(aPresContext
);
275 // Give image loads associated with an image frame a small priority boost!
276 nsCOMPtr
<imgIRequest
> currentRequest
;
277 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
278 getter_AddRefs(currentRequest
));
279 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(currentRequest
);
281 p
->AdjustPriority(-1);
283 // If we already have an image container, OnStartContainer won't be called
284 // Set the animation mode here
285 if (currentRequest
) {
286 nsCOMPtr
<imgIContainer
> image
;
287 currentRequest
->GetImage(getter_AddRefs(image
));
289 image
->SetAnimationMode(aPresContext
->ImageAnimationMode());
290 // Ensure the animation (if any) is started.
291 image
->StartAnimation();
299 nsImageFrame::UpdateIntrinsicSize(imgIContainer
* aImage
)
301 NS_PRECONDITION(aImage
, "null image");
303 PRBool intrinsicSizeChanged
= PR_FALSE
;
306 nsSize imageSizeInPx
;
307 aImage
->GetWidth(&imageSizeInPx
.width
);
308 aImage
->GetHeight(&imageSizeInPx
.height
);
309 nsSize
newSize(nsPresContext::CSSPixelsToAppUnits(imageSizeInPx
.width
),
310 nsPresContext::CSSPixelsToAppUnits(imageSizeInPx
.height
));
311 if (mIntrinsicSize
!= newSize
) {
312 intrinsicSizeChanged
= PR_TRUE
;
313 mIntrinsicSize
= newSize
;
317 return intrinsicSizeChanged
;
321 nsImageFrame::RecalculateTransform()
323 // In any case, we need to translate this over appropriately. Set
324 // translation _before_ setting scaling so that it does not get
327 // XXXbz does this introduce rounding errors because of the cast to
328 // float? Should we just manually add that stuff in every time
330 nsRect innerArea
= GetInnerArea();
331 mTransform
.SetToTranslate(float(innerArea
.x
),
332 float(innerArea
.y
- GetContinuationOffset()));
334 // Set the scale factors
335 if (mIntrinsicSize
.width
!= 0 && mIntrinsicSize
.height
!= 0 &&
336 mIntrinsicSize
!= mComputedSize
) {
337 mTransform
.AddScale(float(mComputedSize
.width
) / float(mIntrinsicSize
.width
),
338 float(mComputedSize
.height
) / float(mIntrinsicSize
.height
));
343 * These two functions basically do the same check. The first one
344 * checks that the given request is the current request for our
345 * mContent. The second checks that the given image container the
346 * same as the image container on the current request for our
350 nsImageFrame::IsPendingLoad(imgIRequest
* aRequest
) const
352 // Default to pending load in case of errors
353 nsCOMPtr
<nsIImageLoadingContent
> imageLoader(do_QueryInterface(mContent
));
354 NS_ASSERTION(imageLoader
, "No image loading content?");
356 PRInt32 requestType
= nsIImageLoadingContent::UNKNOWN_REQUEST
;
357 imageLoader
->GetRequestType(aRequest
, &requestType
);
359 return requestType
!= nsIImageLoadingContent::CURRENT_REQUEST
;
363 nsImageFrame::IsPendingLoad(imgIContainer
* aContainer
) const
365 // default to pending load in case of errors
367 NS_ERROR("No image container!");
371 nsCOMPtr
<nsIImageLoadingContent
> imageLoader(do_QueryInterface(mContent
));
372 NS_ASSERTION(imageLoader
, "No image loading content?");
374 nsCOMPtr
<imgIRequest
> currentRequest
;
375 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
376 getter_AddRefs(currentRequest
));
377 if (!currentRequest
) {
378 NS_ERROR("No current request");
382 nsCOMPtr
<imgIContainer
> currentContainer
;
383 currentRequest
->GetImage(getter_AddRefs(currentContainer
));
385 return currentContainer
!= aContainer
;
390 nsImageFrame::SourceRectToDest(const nsRect
& aRect
)
392 // When scaling the image, row N of the source image may (depending on
393 // the scaling function) be used to draw any row in the destination image
394 // between floor(F * (N-1)) and ceil(F * (N+1)), where F is the
395 // floating-point scaling factor. The same holds true for columns.
396 // So, we start by computing that bound without the floor and ceiling.
398 nsRect
r(nsPresContext::CSSPixelsToAppUnits(aRect
.x
- 1),
399 nsPresContext::CSSPixelsToAppUnits(aRect
.y
- 1),
400 nsPresContext::CSSPixelsToAppUnits(aRect
.width
+ 2),
401 nsPresContext::CSSPixelsToAppUnits(aRect
.height
+ 2));
403 mTransform
.TransformCoord(&r
.x
, &r
.y
, &r
.width
, &r
.height
);
405 // Now, round the edges out to the pixel boundary.
406 int scale
= nsPresContext::CSSPixelsToAppUnits(1);
407 nscoord right
= r
.x
+ r
.width
;
408 nscoord bottom
= r
.y
+ r
.height
;
410 r
.x
-= (scale
+ (r
.x
% scale
)) % scale
;
411 r
.y
-= (scale
+ (r
.y
% scale
)) % scale
;
412 r
.width
= right
+ ((scale
- (right
% scale
)) % scale
) - r
.x
;
413 r
.height
= bottom
+ ((scale
- (bottom
% scale
)) % scale
) - r
.y
;
418 // Note that we treat NS_EVENT_STATE_SUPPRESSED images as "OK". This means
419 // that we'll construct image frames for them as needed if their display is
420 // toggled from "none" (though we won't paint them, unless their visibility
422 #define BAD_STATES (NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED | \
423 NS_EVENT_STATE_LOADING)
425 // This is a macro so that we don't evaluate the boolean last arg
426 // unless we have to; it can be expensive
427 #define IMAGE_OK(_state, _loadingOK) \
428 (((_state) & BAD_STATES) == 0 || \
429 (((_state) & BAD_STATES) == NS_EVENT_STATE_LOADING && \
434 nsImageFrame::ShouldCreateImageFrameFor(nsIContent
* aContent
,
435 nsStyleContext
* aStyleContext
)
437 PRInt32 state
= aContent
->IntrinsicState();
439 HaveFixedSize(aStyleContext
->GetStylePosition()))) {
440 // Image is fine; do the image frame thing
444 // Check if we want to use a placeholder box with an icon or just
445 // let the presShell make us into inline text. Decide as follows:
447 // - if our special "force icons" style is set, show an icon
448 // - else if our "do not show placeholders" pref is set, skip the icon
450 // - if QuirksMode, and there is no alt attribute, and this is not an
451 // <object> (which could not possibly have such an attribute), show an
453 // - if QuirksMode, and the IMG has a size show an icon.
454 // - otherwise, skip the icon
457 if (aStyleContext
->GetStyleUIReset()->mForceBrokenImageIcon
) {
458 useSizedBox
= PR_TRUE
;
460 else if (gIconLoad
&& gIconLoad
->mPrefForceInlineAltText
) {
461 useSizedBox
= PR_FALSE
;
464 if (aStyleContext
->PresContext()->CompatibilityMode() !=
465 eCompatibility_NavQuirks
) {
466 useSizedBox
= PR_FALSE
;
469 // We are in quirks mode, so we can just check the tag name; no need to
470 // check the namespace.
471 nsIAtom
*localName
= aContent
->NodeInfo()->NameAtom();
473 // Use a sized box if we have no alt text. This means no alt attribute
474 // and the node is not an object or an input (since those always have alt
476 if (!aContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::alt
) &&
477 localName
!= nsGkAtoms::object
&&
478 localName
!= nsGkAtoms::input
) {
479 useSizedBox
= PR_TRUE
;
482 // check whether we have fixed size
483 useSizedBox
= HaveFixedSize(aStyleContext
->GetStylePosition());
492 nsImageFrame::OnStartContainer(imgIRequest
*aRequest
, imgIContainer
*aImage
)
494 if (!aImage
) return NS_ERROR_INVALID_ARG
;
496 // handle iconLoads first...
497 if (HandleIconLoads(aRequest
, PR_FALSE
)) {
501 /* Get requested animation policy from the pres context:
506 nsPresContext
*presContext
= PresContext();
507 aImage
->SetAnimationMode(presContext
->ImageAnimationMode());
508 // Ensure the animation (if any) is started.
509 aImage
->StartAnimation();
511 if (IsPendingLoad(aRequest
)) {
516 UpdateIntrinsicSize(aImage
);
518 // Now we need to reflow if we have an unconstrained size and have
519 // already gotten the initial reflow
520 if (!(mState
& IMAGE_SIZECONSTRAINED
) && (mState
& IMAGE_GOTINITIALREFLOW
)) {
521 nsIPresShell
*presShell
= presContext
->GetPresShell();
522 NS_ASSERTION(presShell
, "No PresShell.");
524 presShell
->FrameNeedsReflow(this, nsIPresShell::eStyleChange
,
533 nsImageFrame::OnDataAvailable(imgIRequest
*aRequest
,
534 gfxIImageFrame
*aFrame
,
537 // XXX do we need to make sure that the reflow from the
538 // OnStartContainer has been processed before we start calling
541 NS_ENSURE_ARG_POINTER(aRect
);
543 if (!(mState
& IMAGE_GOTINITIALREFLOW
)) {
544 // Don't bother to do anything; we have a reflow coming up!
548 // XXX We really need to round this out, now that we're doing better
550 nsRect r
= SourceRectToDest(*aRect
);
552 // handle iconLoads first...
553 if (HandleIconLoads(aRequest
, PR_FALSE
)) {
554 // Image changed, invalidate
555 Invalidate(r
, PR_FALSE
);
559 if (IsPendingLoad(aRequest
)) {
564 // Don't invalidate if the current visible frame isn't the one the data is
566 nsCOMPtr
<imgIContainer
> container
;
567 aRequest
->GetImage(getter_AddRefs(container
));
569 nsCOMPtr
<gfxIImageFrame
> currentFrame
;
570 container
->GetCurrentFrame(getter_AddRefs(currentFrame
));
571 if (aFrame
!= currentFrame
) {
578 printf("Source rect (%d,%d,%d,%d) -> invalidate dest rect (%d,%d,%d,%d)\n",
579 aRect
->x
, aRect
->y
, aRect
->width
, aRect
->height
,
580 r
.x
, r
.y
, r
.width
, r
.height
);
583 Invalidate(r
, PR_FALSE
);
589 nsImageFrame::OnStopDecode(imgIRequest
*aRequest
,
591 const PRUnichar
*aStatusArg
)
593 nsPresContext
*presContext
= PresContext();
594 nsIPresShell
*presShell
= presContext
->GetPresShell();
595 NS_ASSERTION(presShell
, "No PresShell.");
597 // handle iconLoads first...
598 if (HandleIconLoads(aRequest
, NS_SUCCEEDED(aStatus
))) {
602 // Check what request type we're dealing with
603 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
604 NS_ASSERTION(imageLoader
, "Who's notifying us??");
605 PRInt32 loadType
= nsIImageLoadingContent::UNKNOWN_REQUEST
;
606 imageLoader
->GetRequestType(aRequest
, &loadType
);
607 if (loadType
!= nsIImageLoadingContent::CURRENT_REQUEST
&&
608 loadType
!= nsIImageLoadingContent::PENDING_REQUEST
) {
609 return NS_ERROR_FAILURE
;
612 if (loadType
== nsIImageLoadingContent::PENDING_REQUEST
) {
613 // May have to switch sizes here!
614 PRBool intrinsicSizeChanged
= PR_TRUE
;
615 if (NS_SUCCEEDED(aStatus
)) {
616 nsCOMPtr
<imgIContainer
> imageContainer
;
617 aRequest
->GetImage(getter_AddRefs(imageContainer
));
618 NS_ASSERTION(imageContainer
, "Successful load with no container?");
619 intrinsicSizeChanged
= UpdateIntrinsicSize(imageContainer
);
622 // Have to size to 0,0 so that GetDesiredSize recalculates the size
623 mIntrinsicSize
.SizeTo(0, 0);
626 if (mState
& IMAGE_GOTINITIALREFLOW
) { // do nothing if we haven't gotten the initial reflow yet
627 if (!(mState
& IMAGE_SIZECONSTRAINED
) && intrinsicSizeChanged
) {
629 presShell
->FrameNeedsReflow(this, nsIPresShell::eStyleChange
,
633 nsSize s
= GetSize();
634 nsRect
r(0, 0, s
.width
, s
.height
);
635 // Update border+content to account for image change
636 Invalidate(r
, PR_FALSE
);
645 nsImageFrame::FrameChanged(imgIContainer
*aContainer
,
646 gfxIImageFrame
*aNewFrame
,
649 if (!GetStyleVisibility()->IsVisible()) {
653 if (IsPendingLoad(aContainer
)) {
654 // We don't care about it
658 nsRect r
= SourceRectToDest(*aDirtyRect
);
660 // Update border+content to account for image change
661 Invalidate(r
, PR_FALSE
);
666 nsImageFrame::EnsureIntrinsicSize(nsPresContext
* aPresContext
)
668 // if mIntrinsicSize.width and height are 0, then we should
669 // check to see if the size is already known by the image container.
670 if (mIntrinsicSize
.width
== 0 && mIntrinsicSize
.height
== 0) {
671 nsCOMPtr
<imgIRequest
> currentRequest
;
672 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
674 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
675 getter_AddRefs(currentRequest
));
677 nsCOMPtr
<imgIContainer
> currentContainer
;
678 if (currentRequest
) {
679 currentRequest
->GetImage(getter_AddRefs(currentContainer
));
682 if (currentContainer
) {
683 UpdateIntrinsicSize(currentContainer
);
685 // image request is null or image size not known, probably an
686 // invalid image specified
687 // - make the image big enough for the icon (it may not be
688 // used if inline alt expansion is used instead)
689 // XXX: we need this in composer, but it is also good for
690 // XXX: general quirks mode to always have room for the icon
691 if (aPresContext
->CompatibilityMode() == eCompatibility_NavQuirks
) {
692 mIntrinsicSize
.SizeTo(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
+(2*(ICON_PADDING
+ALT_BORDER_WIDTH
))),
693 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
+(2*(ICON_PADDING
+ALT_BORDER_WIDTH
))));
700 nsImageFrame::ComputeSize(nsIRenderingContext
*aRenderingContext
,
701 nsSize aCBSize
, nscoord aAvailableWidth
,
702 nsSize aMargin
, nsSize aBorder
, nsSize aPadding
,
705 nsPresContext
*presContext
= PresContext();
706 EnsureIntrinsicSize(presContext
);
708 IntrinsicSize intrinsicSize
;
709 intrinsicSize
.width
.SetCoordValue(mIntrinsicSize
.width
);
710 intrinsicSize
.height
.SetCoordValue(mIntrinsicSize
.height
);
712 nsSize
& intrinsicRatio
= mIntrinsicSize
; // won't actually be used
714 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
715 aRenderingContext
, this,
716 intrinsicSize
, intrinsicRatio
, aCBSize
,
717 aMargin
, aBorder
, aPadding
);
721 nsImageFrame::GetInnerArea() const
723 return GetContentRect() - GetPosition();
726 // get the offset into the content area of the image where aImg starts if it is a continuation.
728 nsImageFrame::GetContinuationOffset() const
731 for (nsIFrame
*f
= GetPrevInFlow(); f
; f
= f
->GetPrevInFlow()) {
732 offset
+= f
->GetContentRect().height
;
734 NS_ASSERTION(offset
>= 0, "bogus GetContentRect");
738 /* virtual */ nscoord
739 nsImageFrame::GetMinWidth(nsIRenderingContext
*aRenderingContext
)
741 // XXX The caller doesn't account for constraints of the height,
742 // min-height, and max-height properties.
744 DISPLAY_MIN_WIDTH(this, result
);
745 nsPresContext
*presContext
= PresContext();
746 EnsureIntrinsicSize(presContext
);
747 result
= mIntrinsicSize
.width
;
751 /* virtual */ nscoord
752 nsImageFrame::GetPrefWidth(nsIRenderingContext
*aRenderingContext
)
754 // XXX The caller doesn't account for constraints of the height,
755 // min-height, and max-height properties.
757 DISPLAY_PREF_WIDTH(this, result
);
758 nsPresContext
*presContext
= PresContext();
759 EnsureIntrinsicSize(presContext
);
760 // convert from normal twips to scaled twips (printing...)
761 result
= mIntrinsicSize
.width
;
766 nsImageFrame::GetIntrinsicRatio()
768 EnsureIntrinsicSize(PresContext());
769 return mIntrinsicSize
;
773 nsImageFrame::Reflow(nsPresContext
* aPresContext
,
774 nsHTMLReflowMetrics
& aMetrics
,
775 const nsHTMLReflowState
& aReflowState
,
776 nsReflowStatus
& aStatus
)
778 DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
779 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aMetrics
, aStatus
);
780 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS
,
781 ("enter nsImageFrame::Reflow: availSize=%d,%d",
782 aReflowState
.availableWidth
, aReflowState
.availableHeight
));
784 NS_PRECONDITION(mState
& NS_FRAME_IN_REFLOW
, "frame is not in reflow");
786 aStatus
= NS_FRAME_COMPLETE
;
788 // see if we have a frozen size (i.e. a fixed width and height)
789 if (HaveFixedSize(aReflowState
)) {
790 mState
|= IMAGE_SIZECONSTRAINED
;
792 mState
&= ~IMAGE_SIZECONSTRAINED
;
795 // XXXldb These two bits are almost exact opposites (except in the
796 // middle of the initial reflow); remove IMAGE_GOTINITIALREFLOW.
797 if (GetStateBits() & NS_FRAME_FIRST_REFLOW
) {
798 mState
|= IMAGE_GOTINITIALREFLOW
;
802 nsSize(aReflowState
.ComputedWidth(), aReflowState
.ComputedHeight());
803 RecalculateTransform();
805 aMetrics
.width
= mComputedSize
.width
;
806 aMetrics
.height
= mComputedSize
.height
;
808 // add borders and padding
809 aMetrics
.width
+= aReflowState
.mComputedBorderPadding
.LeftRight();
810 aMetrics
.height
+= aReflowState
.mComputedBorderPadding
.TopBottom();
812 if (GetPrevInFlow()) {
813 aMetrics
.width
= GetPrevInFlow()->GetSize().width
;
814 nscoord y
= GetContinuationOffset();
815 aMetrics
.height
-= y
+ aReflowState
.mComputedBorderPadding
.top
;
816 aMetrics
.height
= PR_MAX(0, aMetrics
.height
);
820 // we have to split images if we are:
821 // in Paginated mode, we need to have a constrained height, and have a height larger than our available height
822 PRUint32 loadStatus
= imgIRequest::STATUS_NONE
;
823 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
824 NS_ASSERTION(imageLoader
, "No content node??");
826 nsCOMPtr
<imgIRequest
> currentRequest
;
827 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
828 getter_AddRefs(currentRequest
));
829 if (currentRequest
) {
830 currentRequest
->GetImageStatus(&loadStatus
);
833 if (aPresContext
->IsPaginated() &&
834 ((loadStatus
& imgIRequest::STATUS_SIZE_AVAILABLE
) || (mState
& IMAGE_SIZECONSTRAINED
)) &&
835 NS_UNCONSTRAINEDSIZE
!= aReflowState
.availableHeight
&&
836 aMetrics
.height
> aReflowState
.availableHeight
) {
837 // our desired height was greater than 0, so to avoid infinite
838 // splitting, use 1 pixel as the min
839 aMetrics
.height
= PR_MAX(nsPresContext::CSSPixelsToAppUnits(1), aReflowState
.availableHeight
);
840 aStatus
= NS_FRAME_NOT_COMPLETE
;
843 aMetrics
.mOverflowArea
.SetRect(0, 0, aMetrics
.width
, aMetrics
.height
);
844 FinishAndStoreOverflow(&aMetrics
);
846 // Now that that's all done, check whether we're resizing... if we are,
847 // invalidate our rect.
848 // XXXbz we really only want to do this when reflow is completely done, but
849 // we have no way to detect when mRect changes (since SetRect is non-virtual,
850 // so this is the best we can do).
851 if (mRect
.width
!= aMetrics
.width
|| mRect
.height
!= aMetrics
.height
) {
852 Invalidate(nsRect(0, 0, mRect
.width
, mRect
.height
), PR_FALSE
);
855 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS
,
856 ("exit nsImageFrame::Reflow: size=%d,%d",
857 aMetrics
.width
, aMetrics
.height
));
858 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aMetrics
);
862 // Computes the width of the specified string. aMaxWidth specifies the maximum
863 // width available. Once this limit is reached no more characters are measured.
864 // The number of characters that fit within the maximum width are returned in
865 // aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected
866 // into the rendering context before this is called (for performance). MMP
868 nsImageFrame::MeasureString(const PRUnichar
* aString
,
872 nsIRenderingContext
& aContext
)
874 nscoord totalWidth
= 0;
876 aContext
.SetTextRunRTL(PR_FALSE
);
877 aContext
.GetWidth(' ', spaceWidth
);
880 while (aLength
> 0) {
881 // Find the next place we can line break
882 PRUint32 len
= aLength
;
883 PRBool trailingSpace
= PR_FALSE
;
884 for (PRInt32 i
= 0; i
< aLength
; i
++) {
885 if (XP_IS_SPACE(aString
[i
]) && (i
> 0)) {
886 len
= i
; // don't include the space when measuring
887 trailingSpace
= PR_TRUE
;
892 // Measure this chunk of text, and see if it fits
894 nsLayoutUtils::GetStringWidth(this, &aContext
, aString
, len
);
895 PRBool fits
= (totalWidth
+ width
) <= aMaxWidth
;
897 // If it fits on the line, or it's the first word we've processed then
899 if (fits
|| (0 == totalWidth
)) {
903 // If there's a trailing space then see if it fits as well
905 if ((totalWidth
+ spaceWidth
) <= aMaxWidth
) {
906 totalWidth
+= spaceWidth
;
908 // Space won't fit. Leave it at the end but don't include it in
928 // Formats the alt-text to fit within the specified rectangle. Breaks lines
929 // between words if a word would extend past the edge of the rectangle
931 nsImageFrame::DisplayAltText(nsPresContext
* aPresContext
,
932 nsIRenderingContext
& aRenderingContext
,
933 const nsString
& aAltText
,
936 // Set font and color
937 aRenderingContext
.SetColor(GetStyleColor()->mColor
);
938 nsLayoutUtils::SetFontFromStyle(&aRenderingContext
, mStyleContext
);
940 // Format the text to display within the formatting rect
942 aRenderingContext
.GetFontMetrics(fm
);
944 nscoord maxAscent
, maxDescent
, height
;
945 fm
->GetMaxAscent(maxAscent
);
946 fm
->GetMaxDescent(maxDescent
);
947 fm
->GetHeight(height
);
949 // XXX It would be nice if there was a way to have the font metrics tell
950 // use where to break the text given a maximum width. At a minimum we need
951 // to be able to get the break character...
952 const PRUnichar
* str
= aAltText
.get();
953 PRInt32 strLen
= aAltText
.Length();
955 // Always show the first line, even if we have to clip it below
956 PRBool firstLine
= PR_TRUE
;
957 while ((strLen
> 0) && (firstLine
|| (y
+ maxDescent
) < aRect
.YMost())) {
958 // Determine how much of the text to display on this line
959 PRUint32 maxFit
; // number of characters that fit
960 nscoord strWidth
= MeasureString(str
, strLen
, aRect
.width
, maxFit
,
964 nsresult rv
= NS_ERROR_FAILURE
;
966 if (aPresContext
->BidiEnabled()) {
967 nsBidiPresUtils
* bidiUtils
= aPresContext
->GetBidiUtils();
970 const nsStyleVisibility
* vis
= GetStyleVisibility();
971 if (vis
->mDirection
== NS_STYLE_DIRECTION_RTL
)
972 rv
= bidiUtils
->RenderText(str
, maxFit
, NSBIDI_RTL
,
973 aPresContext
, aRenderingContext
,
974 aRect
.XMost() - strWidth
, y
+ maxAscent
);
976 rv
= bidiUtils
->RenderText(str
, maxFit
, NSBIDI_LTR
,
977 aPresContext
, aRenderingContext
,
978 aRect
.x
, y
+ maxAscent
);
982 aRenderingContext
.DrawString(str
, maxFit
, aRect
.x
, y
+ maxAscent
);
984 // Move to the next line
988 firstLine
= PR_FALSE
;
994 struct nsRecessedBorder
: public nsStyleBorder
{
995 nsRecessedBorder(nscoord aBorderWidth
, nsPresContext
* aPresContext
)
996 : nsStyleBorder(aPresContext
)
998 NS_FOR_CSS_SIDES(side
) {
999 // Note: use SetBorderColor here because we want to make sure
1000 // the "special" flags are unset.
1001 SetBorderColor(side
, NS_RGB(0, 0, 0));
1002 mBorder
.side(side
) = aBorderWidth
;
1003 // Note: use SetBorderStyle here because we want to affect
1005 SetBorderStyle(side
, NS_STYLE_BORDER_STYLE_INSET
);
1011 nsImageFrame::DisplayAltFeedback(nsIRenderingContext
& aRenderingContext
,
1012 const nsRect
& aDirtyRect
,
1013 imgIRequest
* aRequest
,
1016 // Calculate the inner area
1017 nsRect inner
= GetInnerArea() + aPt
;
1019 // Display a recessed one pixel border
1020 nscoord borderEdgeWidth
= nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH
);
1022 // if inner area is empty, then make it big enough for at least the icon
1023 if (inner
.IsEmpty()){
1024 inner
.SizeTo(2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
+ICON_PADDING
+ALT_BORDER_WIDTH
)),
1025 2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
+ICON_PADDING
+ALT_BORDER_WIDTH
)));
1028 // Make sure we have enough room to actually render the border within
1030 if ((inner
.width
< 2 * borderEdgeWidth
) || (inner
.height
< 2 * borderEdgeWidth
)) {
1035 nsRecessedBorder
recessedBorder(borderEdgeWidth
, PresContext());
1036 nsCSSRendering::PaintBorder(PresContext(), aRenderingContext
, this, inner
,
1037 inner
, recessedBorder
, mStyleContext
, 0);
1039 // Adjust the inner rect to account for the one pixel recessed border,
1040 // and a six pixel padding on each edge
1041 inner
.Deflate(nsPresContext::CSSPixelsToAppUnits(ICON_PADDING
+ALT_BORDER_WIDTH
),
1042 nsPresContext::CSSPixelsToAppUnits(ICON_PADDING
+ALT_BORDER_WIDTH
));
1043 if (inner
.IsEmpty()) {
1047 // Clip so we don't render outside the inner rect
1048 aRenderingContext
.PushState();
1049 aRenderingContext
.SetClipRect(inner
, nsClipCombine_kIntersect
);
1051 PRBool dispIcon
= gIconLoad
? gIconLoad
->mPrefShowPlaceholders
: PR_TRUE
;
1053 // Check if we should display image placeholders
1055 const nsStyleVisibility
* vis
= GetStyleVisibility();
1056 nscoord size
= nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
);
1058 PRBool iconUsed
= PR_FALSE
;
1060 // see if the icon images are present...
1061 if (gIconLoad
&& gIconLoad
->mIconsLoaded
) {
1062 // pick the correct image
1063 nsCOMPtr
<imgIContainer
> imgCon
;
1065 aRequest
->GetImage(getter_AddRefs(imgCon
));
1069 nsRect
dest((vis
->mDirection
== NS_STYLE_DIRECTION_RTL
) ?
1070 inner
.XMost() - size
: inner
.x
,
1071 inner
.y
, size
, size
);
1072 nsLayoutUtils::DrawImage(&aRenderingContext
, imgCon
, dest
, aDirtyRect
);
1077 // if we could not draw the image, then just draw some graffiti
1080 nscoord iconXPos
= (vis
->mDirection
== NS_STYLE_DIRECTION_RTL
) ?
1081 inner
.XMost() - size
: inner
.x
;
1082 nscoord twoPX
= nsPresContext::CSSPixelsToAppUnits(2);
1083 aRenderingContext
.DrawRect(iconXPos
, inner
.y
,size
,size
);
1084 aRenderingContext
.GetColor(oldColor
);
1085 aRenderingContext
.SetColor(NS_RGB(0xFF,0,0));
1086 aRenderingContext
.FillEllipse(size
/2 + iconXPos
, size
/2 + inner
.y
,
1087 size
/2 - twoPX
, size
/2 - twoPX
);
1088 aRenderingContext
.SetColor(oldColor
);
1091 // Reduce the inner rect by the width of the icon, and leave an
1092 // additional ICON_PADDING pixels for padding
1093 PRInt32 iconWidth
= nsPresContext::CSSPixelsToAppUnits(ICON_SIZE
+ ICON_PADDING
);
1094 if (vis
->mDirection
!= NS_STYLE_DIRECTION_RTL
)
1095 inner
.x
+= iconWidth
;
1096 inner
.width
-= iconWidth
;
1099 // If there's still room, display the alt-text
1100 if (!inner
.IsEmpty()) {
1101 nsIContent
* content
= GetContent();
1103 nsXPIDLString altText
;
1104 nsCSSFrameConstructor::GetAlternateTextFor(content
, content
->Tag(),
1106 DisplayAltText(PresContext(), aRenderingContext
, altText
, inner
);
1110 aRenderingContext
.PopState();
1113 static void PaintAltFeedback(nsIFrame
* aFrame
, nsIRenderingContext
* aCtx
,
1114 const nsRect
& aDirtyRect
, nsPoint aPt
)
1116 nsImageFrame
* f
= static_cast<nsImageFrame
*>(aFrame
);
1117 f
->DisplayAltFeedback(*aCtx
,
1119 IMAGE_OK(f
->GetContent()->IntrinsicState(), PR_TRUE
)
1120 ? nsImageFrame::gIconLoad
->mLoadingImage
1121 : nsImageFrame::gIconLoad
->mBrokenImage
,
1126 static void PaintDebugImageMap(nsIFrame
* aFrame
, nsIRenderingContext
* aCtx
,
1127 const nsRect
& aDirtyRect
, nsPoint aPt
) {
1128 nsImageFrame
* f
= static_cast<nsImageFrame
*>(aFrame
);
1129 nsRect inner
= f
->GetInnerArea() + aPt
;
1130 nsPresContext
* pc
= f
->PresContext();
1132 aCtx
->SetColor(NS_RGB(0, 0, 0));
1134 aCtx
->Translate(inner
.x
, inner
.y
);
1135 f
->GetImageMap(pc
)->Draw(pc
, *aCtx
);
1141 * Note that nsDisplayImage does not receive events. However, an image element
1142 * is replaced content so its background will be z-adjacent to the
1143 * image itself, and hence receive events just as if the image itself
1146 class nsDisplayImage
: public nsDisplayItem
{
1148 nsDisplayImage(nsImageFrame
* aFrame
, imgIContainer
* aImage
)
1149 : nsDisplayItem(aFrame
), mImage(aImage
) {
1150 MOZ_COUNT_CTOR(nsDisplayImage
);
1152 virtual ~nsDisplayImage() {
1153 MOZ_COUNT_DTOR(nsDisplayImage
);
1155 virtual void Paint(nsDisplayListBuilder
* aBuilder
, nsIRenderingContext
* aCtx
,
1156 const nsRect
& aDirtyRect
);
1157 NS_DISPLAY_DECL_NAME("Image")
1159 nsCOMPtr
<imgIContainer
> mImage
;
1163 nsDisplayImage::Paint(nsDisplayListBuilder
* aBuilder
,
1164 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
) {
1165 static_cast<nsImageFrame
*>(mFrame
)->
1166 PaintImage(*aCtx
, aBuilder
->ToReferenceFrame(mFrame
), aDirtyRect
, mImage
);
1170 nsImageFrame::PaintImage(nsIRenderingContext
& aRenderingContext
, nsPoint aPt
,
1171 const nsRect
& aDirtyRect
, imgIContainer
* aImage
)
1173 // Render the image into our content area (the area inside
1174 // the borders and padding)
1175 NS_ASSERTION(GetInnerArea().width
== mComputedSize
.width
, "bad width");
1176 nsRect inner
= GetInnerArea() + aPt
;
1178 clip
.IntersectRect(inner
, aDirtyRect
);
1180 nsRect
dest(inner
.TopLeft(), mComputedSize
);
1181 dest
.y
-= GetContinuationOffset();
1183 nsLayoutUtils::DrawImage(&aRenderingContext
, aImage
, dest
, clip
);
1185 nsPresContext
* presContext
= PresContext();
1186 nsImageMap
* map
= GetImageMap(presContext
);
1187 if (nsnull
!= map
) {
1188 aRenderingContext
.PushState();
1189 aRenderingContext
.SetColor(NS_RGB(0, 0, 0));
1190 aRenderingContext
.SetLineStyle(nsLineStyle_kDotted
);
1191 aRenderingContext
.Translate(inner
.x
, inner
.y
);
1192 map
->Draw(presContext
, aRenderingContext
);
1193 aRenderingContext
.PopState();
1198 nsImageFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
1199 const nsRect
& aDirtyRect
,
1200 const nsDisplayListSet
& aLists
)
1202 if (!IsVisibleForPainting(aBuilder
))
1205 // REVIEW: We don't need any special logic here for deciding which layer
1206 // to put the background in ... it goes in aLists.BorderBackground() and
1207 // then if we have a block parent, it will put our background in the right
1209 nsresult rv
= DisplayBorderBackgroundOutline(aBuilder
, aLists
);
1210 NS_ENSURE_SUCCESS(rv
, rv
);
1211 // REVIEW: Checking mRect.IsEmpty() makes no sense to me, so I removed it.
1212 // It can't have been protecting us against bad situations with zero-size
1213 // images since adding a border would make the rect non-empty.
1215 if (mComputedSize
.width
!= 0 && mComputedSize
.height
!= 0) {
1216 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
1217 NS_ASSERTION(imageLoader
, "Not an image loading content?");
1219 nsCOMPtr
<imgIRequest
> currentRequest
;
1221 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
1222 getter_AddRefs(currentRequest
));
1225 PRInt32 contentState
= mContent
->IntrinsicState();
1226 PRBool imageOK
= IMAGE_OK(contentState
, PR_TRUE
);
1228 nsCOMPtr
<imgIContainer
> imgCon
;
1229 if (currentRequest
) {
1230 currentRequest
->GetImage(getter_AddRefs(imgCon
));
1233 if (!imageOK
|| !imgCon
) {
1234 // No image yet, or image load failed. Draw the alt-text and an icon
1235 // indicating the status
1236 rv
= aLists
.Content()->AppendNewToTop(new (aBuilder
)
1237 nsDisplayGeneric(this, PaintAltFeedback
, "AltFeedback"));
1238 NS_ENSURE_SUCCESS(rv
, rv
);
1241 rv
= aLists
.Content()->AppendNewToTop(new (aBuilder
)
1242 nsDisplayImage(this, imgCon
));
1243 NS_ENSURE_SUCCESS(rv
, rv
);
1246 if (GetShowFrameBorders() && GetImageMap(PresContext())) {
1247 rv
= aLists
.Outlines()->AppendNewToTop(new (aBuilder
)
1248 nsDisplayGeneric(this, PaintDebugImageMap
, "DebugImageMap"));
1249 NS_ENSURE_SUCCESS(rv
, rv
);
1255 // XXX what on EARTH is this code for?
1256 PRInt16 displaySelection
= 0;
1258 nsPresContext
* presContext
= PresContext();
1259 result
= presContext
->PresShell()->GetSelectionFlags(&displaySelection
);
1260 if (NS_FAILED(result
))
1262 if (!(displaySelection
& nsISelectionDisplay::DISPLAY_IMAGES
))
1263 return NS_OK
;//no need to check the blue border, we cannot be drawn selected
1264 //insert hook here for image selection drawing
1265 #if IMAGE_EDITOR_CHECK
1266 //check to see if this frame is in an editor context
1267 //isEditor check. this needs to be changed to have better way to check
1268 if (displaySelection
== nsISelectionDisplay::DISPLAY_ALL
)
1270 nsCOMPtr
<nsISelectionController
> selCon
;
1271 result
= GetSelectionController(presContext
, getter_AddRefs(selCon
));
1272 if (NS_SUCCEEDED(result
) && selCon
)
1274 nsCOMPtr
<nsISelection
> selection
;
1275 result
= selCon
->GetSelection(nsISelectionController::SELECTION_NORMAL
, getter_AddRefs(selection
));
1276 if (NS_SUCCEEDED(result
) && selection
)
1279 selection
->GetRangeCount(&rangeCount
);
1280 if (rangeCount
== 1) //if not one then let code drop to nsFrame::Paint
1282 nsCOMPtr
<nsIContent
> parentContent
= mContent
->GetParent();
1285 PRInt32 thisOffset
= parentContent
->IndexOf(mContent
);
1286 nsCOMPtr
<nsIDOMNode
> parentNode
= do_QueryInterface(parentContent
);
1287 nsCOMPtr
<nsIDOMNode
> rangeNode
;
1288 PRInt32 rangeOffset
;
1289 nsCOMPtr
<nsIDOMRange
> range
;
1290 selection
->GetRangeAt(0,getter_AddRefs(range
));
1293 range
->GetStartContainer(getter_AddRefs(rangeNode
));
1294 range
->GetStartOffset(&rangeOffset
);
1296 if (parentNode
&& rangeNode
&& (rangeNode
== parentNode
) && rangeOffset
== thisOffset
)
1298 range
->GetEndContainer(getter_AddRefs(rangeNode
));
1299 range
->GetEndOffset(&rangeOffset
);
1300 if ((rangeNode
== parentNode
) && (rangeOffset
== (thisOffset
+1))) //+1 since that would mean this whole content is selected only
1301 return NS_OK
; //do not allow nsFrame do draw any further selection
1311 return DisplaySelectionOverlay(aBuilder
, aLists
,
1312 nsISelectionDisplay::DISPLAY_IMAGES
);
1316 nsImageFrame::GetImageMap(nsPresContext
*aPresContext
, nsIImageMap
**aImageMap
)
1318 nsImageMap
*map
= GetImageMap(aPresContext
);
1319 return CallQueryInterface(map
, aImageMap
);
1323 nsImageFrame::GetImageMap(nsPresContext
* aPresContext
)
1326 nsIDocument
* doc
= mContent
->GetDocument();
1331 nsAutoString usemap
;
1332 mContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::usemap
, usemap
);
1334 nsCOMPtr
<nsIDOMHTMLMapElement
> map
= nsImageMapUtils::FindImageMap(doc
,usemap
);
1336 mImageMap
= new nsImageMap();
1338 NS_ADDREF(mImageMap
);
1339 mImageMap
->Init(aPresContext
->PresShell(), this, map
);
1348 nsImageFrame::IsServerImageMap()
1350 return mContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::ismap
);
1353 // Translate an point that is relative to our frame
1354 // into a localized pixel coordinate that is relative to the
1355 // content area of this frame (inside the border+padding).
1357 nsImageFrame::TranslateEventCoords(const nsPoint
& aPoint
,
1358 nsIntPoint
& aResult
)
1360 nscoord x
= aPoint
.x
;
1361 nscoord y
= aPoint
.y
;
1363 // Subtract out border and padding here so that the coordinates are
1364 // now relative to the content area of this frame.
1365 nsRect inner
= GetInnerArea();
1369 aResult
.x
= nsPresContext::AppUnitsToIntCSSPixels(x
);
1370 aResult
.y
= nsPresContext::AppUnitsToIntCSSPixels(y
);
1374 nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI
** aHref
, nsString
& aTarget
,
1377 PRBool status
= PR_FALSE
;
1382 // Walk up the content tree, looking for an nsIDOMAnchorElement
1383 for (nsIContent
* content
= mContent
->GetParent();
1384 content
; content
= content
->GetParent()) {
1385 nsCOMPtr
<nsILink
> link(do_QueryInterface(content
));
1387 link
->GetHrefURI(aHref
);
1388 status
= (*aHref
!= nsnull
);
1390 nsCOMPtr
<nsIDOMHTMLAnchorElement
> anchor(do_QueryInterface(content
));
1392 anchor
->GetTarget(aTarget
);
1394 NS_ADDREF(*aNode
= content
);
1402 nsImageFrame::GetContentForEvent(nsPresContext
* aPresContext
,
1404 nsIContent
** aContent
)
1406 NS_ENSURE_ARG_POINTER(aContent
);
1408 map
= GetImageMap(aPresContext
);
1410 if (nsnull
!= map
) {
1412 TranslateEventCoords(
1413 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent
, this), p
);
1414 PRBool inside
= PR_FALSE
;
1415 nsCOMPtr
<nsIContent
> area
;
1416 inside
= map
->IsInside(p
.x
, p
.y
, getter_AddRefs(area
));
1417 if (inside
&& area
) {
1419 NS_ADDREF(*aContent
);
1424 *aContent
= GetContent();
1425 NS_IF_ADDREF(*aContent
);
1429 // XXX what should clicks on transparent pixels do?
1431 nsImageFrame::HandleEvent(nsPresContext
* aPresContext
,
1433 nsEventStatus
* aEventStatus
)
1435 NS_ENSURE_ARG_POINTER(aEventStatus
);
1438 if (aEvent
->eventStructType
== NS_MOUSE_EVENT
&&
1439 (aEvent
->message
== NS_MOUSE_BUTTON_UP
&&
1440 static_cast<nsMouseEvent
*>(aEvent
)->button
== nsMouseEvent::eLeftButton
) ||
1441 aEvent
->message
== NS_MOUSE_MOVE
) {
1442 map
= GetImageMap(aPresContext
);
1443 PRBool isServerMap
= IsServerImageMap();
1444 if ((nsnull
!= map
) || isServerMap
) {
1446 TranslateEventCoords(
1447 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent
, this), p
);
1448 PRBool inside
= PR_FALSE
;
1449 // Even though client-side image map triggering happens
1450 // through content, we need to make sure we're not inside
1451 // (in case we deal with a case of both client-side and
1452 // sever-side on the same image - it happens!)
1453 if (nsnull
!= map
) {
1454 nsCOMPtr
<nsIContent
> area
;
1455 inside
= map
->IsInside(p
.x
, p
.y
, getter_AddRefs(area
));
1458 if (!inside
&& isServerMap
) {
1460 // Server side image maps use the href in a containing anchor
1461 // element to provide the basis for the destination url.
1462 nsCOMPtr
<nsIURI
> uri
;
1463 nsAutoString target
;
1464 nsCOMPtr
<nsIContent
> anchorNode
;
1465 if (GetAnchorHREFTargetAndNode(getter_AddRefs(uri
), target
,
1466 getter_AddRefs(anchorNode
))) {
1467 // XXX if the mouse is over/clicked in the border/padding area
1468 // we should probably just pretend nothing happened. Nav4
1469 // keeps the x,y coordinates positive as we do; IE doesn't
1470 // bother. Both of them send the click through even when the
1471 // mouse is over the border.
1472 if (p
.x
< 0) p
.x
= 0;
1473 if (p
.y
< 0) p
.y
= 0;
1476 spec
+= nsPrintfCString("?%d,%d", p
.x
, p
.y
);
1479 PRBool clicked
= PR_FALSE
;
1480 if (aEvent
->message
== NS_MOUSE_BUTTON_UP
) {
1481 *aEventStatus
= nsEventStatus_eConsumeDoDefault
;
1484 nsContentUtils::TriggerLink(anchorNode
, aPresContext
, uri
, target
,
1491 return nsSplittableFrame::HandleEvent(aPresContext
, aEvent
, aEventStatus
);
1495 nsImageFrame::GetCursor(const nsPoint
& aPoint
,
1496 nsIFrame::Cursor
& aCursor
)
1498 nsPresContext
* context
= PresContext();
1499 nsImageMap
* map
= GetImageMap(context
);
1500 if (nsnull
!= map
) {
1502 TranslateEventCoords(aPoint
, p
);
1503 nsCOMPtr
<nsIContent
> area
;
1504 if (map
->IsInside(p
.x
, p
.y
, getter_AddRefs(area
))) {
1505 // Use the cursor from the style of the *area* element.
1506 // XXX Using the image as the parent style context isn't
1507 // technically correct, but it's probably the right thing to do
1508 // here, since it means that areas on which the cursor isn't
1509 // specified will inherit the style from the image.
1510 nsRefPtr
<nsStyleContext
> areaStyle
=
1511 PresContext()->PresShell()->StyleSet()->
1512 ResolveStyleFor(area
, GetStyleContext());
1514 FillCursorInformationFromStyle(areaStyle
->GetStyleUserInterface(),
1516 if (NS_STYLE_CURSOR_AUTO
== aCursor
.mCursor
) {
1517 aCursor
.mCursor
= NS_STYLE_CURSOR_DEFAULT
;
1523 return nsFrame::GetCursor(aPoint
, aCursor
);
1527 nsImageFrame::AttributeChanged(PRInt32 aNameSpaceID
,
1528 nsIAtom
* aAttribute
,
1531 nsresult rv
= nsSplittableFrame::AttributeChanged(aNameSpaceID
,
1532 aAttribute
, aModType
);
1533 if (NS_FAILED(rv
)) {
1536 if (nsGkAtoms::alt
== aAttribute
)
1538 PresContext()->PresShell()->FrameNeedsReflow(this,
1539 nsIPresShell::eStyleChange
,
1547 nsImageFrame::GetType() const
1549 return nsGkAtoms::imageFrame
;
1554 nsImageFrame::GetFrameName(nsAString
& aResult
) const
1556 return MakeFrameName(NS_LITERAL_STRING("ImageFrame"), aResult
);
1560 nsImageFrame::List(FILE* out
, PRInt32 aIndent
) const
1562 IndentBy(out
, aIndent
);
1564 #ifdef DEBUG_waterson
1565 fprintf(out
, " [parent=%p]", mParent
);
1568 fprintf(out
, " [view=%p]", (void*)GetView());
1570 fprintf(out
, " {%d,%d,%d,%d}", mRect
.x
, mRect
.y
, mRect
.width
,
1573 fprintf(out
, " [state=%08x]", mState
);
1575 fprintf(out
, " [content=%p]", (void*)mContent
);
1577 // output the img src url
1578 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(mContent
);
1580 nsCOMPtr
<imgIRequest
> currentRequest
;
1581 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
1582 getter_AddRefs(currentRequest
));
1583 if (currentRequest
) {
1584 nsCOMPtr
<nsIURI
> uri
;
1585 currentRequest
->GetURI(getter_AddRefs(uri
));
1586 nsCAutoString uristr
;
1587 uri
->GetAsciiSpec(uristr
);
1588 fprintf(out
, " [src=%s]", uristr
.get());
1597 nsImageFrame::GetSkipSides() const
1600 if (nsnull
!= GetPrevInFlow()) {
1601 skip
|= 1 << NS_SIDE_TOP
;
1603 if (nsnull
!= GetNextInFlow()) {
1604 skip
|= 1 << NS_SIDE_BOTTOM
;
1610 nsImageFrame::GetIntrinsicImageSize(nsSize
& aSize
)
1612 aSize
= mIntrinsicSize
;
1617 nsImageFrame::LoadIcon(const nsAString
& aSpec
,
1618 nsPresContext
*aPresContext
,
1619 imgIRequest
** aRequest
)
1621 nsresult rv
= NS_OK
;
1622 NS_PRECONDITION(!aSpec
.IsEmpty(), "What happened??");
1625 rv
= CallGetService(NS_IOSERVICE_CONTRACTID
, &sIOService
);
1626 NS_ENSURE_SUCCESS(rv
, rv
);
1629 nsCOMPtr
<nsIURI
> realURI
;
1630 SpecToURI(aSpec
, sIOService
, getter_AddRefs(realURI
));
1632 nsCOMPtr
<imgILoader
> il(do_GetService("@mozilla.org/image/loader;1", &rv
));
1633 if (NS_FAILED(rv
)) return rv
;
1635 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1636 GetLoadGroup(aPresContext
, getter_AddRefs(loadGroup
));
1638 // For icon loads, we don't need to merge with the loadgroup flags
1639 nsLoadFlags loadFlags
= nsIRequest::LOAD_NORMAL
;
1641 return il
->LoadImage(realURI
, /* icon URI */
1642 nsnull
, /* initial document URI; this is only
1643 relevant for cookies, so does not
1645 nsnull
, /* referrer (not relevant for icons) */
1648 nsnull
, /* Not associated with any particular document */
1656 nsImageFrame::GetDocumentCharacterSet(nsACString
& aCharset
) const
1659 NS_ASSERTION(mContent
->GetDocument(),
1660 "Frame still alive after content removed from document!");
1661 aCharset
= mContent
->GetDocument()->GetDocumentCharacterSet();
1666 nsImageFrame::SpecToURI(const nsAString
& aSpec
, nsIIOService
*aIOService
,
1669 nsCOMPtr
<nsIURI
> baseURI
;
1671 baseURI
= mContent
->GetBaseURI();
1673 nsCAutoString charset
;
1674 GetDocumentCharacterSet(charset
);
1675 NS_NewURI(aURI
, aSpec
,
1676 charset
.IsEmpty() ? nsnull
: charset
.get(),
1677 baseURI
, aIOService
);
1681 nsImageFrame::GetLoadGroup(nsPresContext
*aPresContext
, nsILoadGroup
**aLoadGroup
)
1686 NS_PRECONDITION(nsnull
!= aLoadGroup
, "null OUT parameter pointer");
1688 nsIPresShell
*shell
= aPresContext
->GetPresShell();
1693 nsIDocument
*doc
= shell
->GetDocument();
1697 *aLoadGroup
= doc
->GetDocumentLoadGroup().get(); // already_AddRefed
1700 nsresult
nsImageFrame::LoadIcons(nsPresContext
*aPresContext
)
1702 NS_ASSERTION(!gIconLoad
, "called LoadIcons twice");
1704 NS_NAMED_LITERAL_STRING(loadingSrc
,"resource://gre/res/loading-image.gif");
1705 NS_NAMED_LITERAL_STRING(brokenSrc
,"resource://gre/res/broken-image.gif");
1707 gIconLoad
= new IconLoad(mListener
);
1709 return NS_ERROR_OUT_OF_MEMORY
;
1710 NS_ADDREF(gIconLoad
);
1713 // create a loader and load the images
1714 rv
= LoadIcon(loadingSrc
,
1716 getter_AddRefs(gIconLoad
->mLoadingImage
));
1717 #ifdef NOISY_ICON_LOADING
1718 printf("Loading request %p, rv=%u\n",
1719 gIconLoad
->mLoadingImage
.get(), rv
);
1722 if (NS_FAILED(rv
)) {
1726 rv
= LoadIcon(brokenSrc
,
1728 getter_AddRefs(gIconLoad
->mBrokenImage
));
1729 #ifdef NOISY_ICON_LOADING
1730 printf("Loading request %p, rv=%u\n",
1731 gIconLoad
->mBrokenImage
.get(), rv
);
1734 // ImageLoader will callback into OnStartContainer, which will
1735 // handle the mIconsLoaded flag
1740 PRBool
nsImageFrame::HandleIconLoads(imgIRequest
* aRequest
, PRBool aLoaded
)
1742 PRBool result
= PR_FALSE
;
1745 // check which image it is
1746 if (aRequest
== gIconLoad
->mLoadingImage
||
1747 aRequest
== gIconLoad
->mBrokenImage
) {
1749 if (aLoaded
&& (++gIconLoad
->mIconsLoaded
== 2))
1750 gIconLoad
->mLoadObserver
= nsnull
;
1753 #ifdef NOISY_ICON_LOADING
1754 if (gIconLoad
->mIconsLoaded
&& result
) {
1755 printf( "Icons Loaded: request for %s\n",
1756 aRequest
== gIconLoad
->mLoadingImage
1757 ? "mLoadingImage" : "mBrokenImage" );
1762 #ifdef NOISY_ICON_LOADING
1763 printf( "HandleIconLoads returned %s (%p)\n", result
? "TRUE" : "FALSE", this);
1769 NS_IMPL_ISUPPORTS1(nsImageFrame::IconLoad
, nsIObserver
)
1771 static const char kIconLoadPrefs
[][40] = {
1772 "browser.display.force_inline_alttext",
1773 "browser.display.show_image_placeholders"
1776 nsImageFrame::IconLoad::IconLoad(imgIDecoderObserver
*aObserver
)
1777 : mLoadObserver(aObserver
),
1780 nsCOMPtr
<nsIPrefBranch2
> prefBranch
=
1781 do_QueryInterface(nsContentUtils::GetPrefBranch());
1783 // register observers
1784 for (PRUint32 i
= 0; i
< NS_ARRAY_LENGTH(kIconLoadPrefs
); ++i
)
1785 prefBranch
->AddObserver(kIconLoadPrefs
[i
], this, PR_FALSE
);
1792 nsImageFrame::IconLoad::Observe(nsISupports
*aSubject
, const char* aTopic
,
1793 const PRUnichar
* aData
)
1795 NS_ASSERTION(!nsCRT::strcmp(aTopic
, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
),
1798 // assert |aData| is one of our prefs.
1799 for (PRUint32 i
= 0; i
< NS_ARRAY_LENGTH(kIconLoadPrefs
) ||
1800 (NS_NOTREACHED("wrong pref"), PR_FALSE
); ++i
)
1801 if (NS_ConvertASCIItoUTF16(kIconLoadPrefs
[i
]) == nsDependentString(aData
))
1809 void nsImageFrame::IconLoad::GetPrefs()
1811 mPrefForceInlineAltText
=
1812 nsContentUtils::GetBoolPref("browser.display.force_inline_alttext");
1814 mPrefShowPlaceholders
=
1815 nsContentUtils::GetBoolPref("browser.display.show_image_placeholders",
1819 NS_IMPL_ISUPPORTS2(nsImageListener
, imgIDecoderObserver
, imgIContainerObserver
)
1821 nsImageListener::nsImageListener(nsImageFrame
*aFrame
) :
1826 nsImageListener::~nsImageListener()
1830 NS_IMETHODIMP
nsImageListener::OnStartContainer(imgIRequest
*aRequest
,
1831 imgIContainer
*aImage
)
1834 return NS_ERROR_FAILURE
;
1836 return mFrame
->OnStartContainer(aRequest
, aImage
);
1839 NS_IMETHODIMP
nsImageListener::OnDataAvailable(imgIRequest
*aRequest
,
1840 gfxIImageFrame
*aFrame
,
1841 const nsRect
*aRect
)
1844 return NS_ERROR_FAILURE
;
1846 return mFrame
->OnDataAvailable(aRequest
, aFrame
, aRect
);
1849 NS_IMETHODIMP
nsImageListener::OnStopDecode(imgIRequest
*aRequest
,
1851 const PRUnichar
*statusArg
)
1854 return NS_ERROR_FAILURE
;
1856 return mFrame
->OnStopDecode(aRequest
, status
, statusArg
);
1859 NS_IMETHODIMP
nsImageListener::FrameChanged(imgIContainer
*aContainer
,
1860 gfxIImageFrame
*newframe
,
1864 return NS_ERROR_FAILURE
;
1866 return mFrame
->FrameChanged(aContainer
, newframe
, dirtyRect
);
1870 IsInAutoWidthTableCellForQuirk(nsIFrame
*aFrame
)
1872 if (eCompatibility_NavQuirks
!= aFrame
->PresContext()->CompatibilityMode())
1874 // Check if the parent of the closest nsBlockFrame has auto width.
1875 nsBlockFrame
*ancestor
= nsLayoutUtils::FindNearestBlockAncestor(aFrame
);
1876 if (ancestor
->GetStyleContext()->GetPseudoType() == nsCSSAnonBoxes::cellContent
) {
1877 // Assume direct parent is a table cell frame.
1878 nsFrame
*grandAncestor
= static_cast<nsFrame
*>(ancestor
->GetParent());
1879 return grandAncestor
&&
1880 grandAncestor
->GetStylePosition()->mWidth
.GetUnit() == eStyleUnit_Auto
;
1886 nsImageFrame::AddInlineMinWidth(nsIRenderingContext
*aRenderingContext
,
1887 nsIFrame::InlineMinWidthData
*aData
)
1890 NS_ASSERTION(GetParent(), "Must have a parent if we get here!");
1893 !CanContinueTextRun() &&
1894 GetParent()->GetStyleText()->WhiteSpaceCanWrap() &&
1895 !IsInAutoWidthTableCellForQuirk(this);
1898 aData
->OptionallyBreak(aRenderingContext
);
1900 aData
->trailingWhitespace
= 0;
1901 aData
->skipWhitespace
= PR_FALSE
;
1902 aData
->trailingTextFrame
= nsnull
;
1903 aData
->currentLine
+= nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
1904 this, nsLayoutUtils::MIN_WIDTH
);
1905 aData
->atStartOfLine
= PR_FALSE
;
1908 aData
->OptionallyBreak(aRenderingContext
);