Fixed textfield rendering in nsNativeThemeQt
[mozilla-central.git] / layout / generic / nsImageFrame.cpp
blobaa8806973948a96759cda95fb99f70b9cca623c2
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
13 * License.
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.
22 * Contributor(s):
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"
42 #include "nsCOMPtr.h"
43 #include "nsImageFrame.h"
44 #include "nsIImageLoadingContent.h"
45 #include "nsString.h"
46 #include "nsPrintfCString.h"
47 #include "nsPresContext.h"
48 #include "nsIRenderingContext.h"
49 #include "nsIPresShell.h"
50 #include "nsIImage.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"
61 #include "nsIURL.h"
62 #include "nsIIOService.h"
63 #include "nsIURL.h"
64 #include "nsILoadGroup.h"
65 #include "nsISupportsPriority.h"
66 #include "nsIServiceManager.h"
67 #include "nsNetUtil.h"
68 #include "nsHTMLContainerFrame.h"
69 #include "prprf.h"
70 #include "nsIFontMetrics.h"
71 #include "nsCSSRendering.h"
72 #include "nsILink.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"
81 #ifdef ACCESSIBILITY
82 #include "nsIAccessibilityService.h"
83 #endif
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"
107 #ifdef DEBUG
108 #undef NOISY_IMAGE_LOADING
109 #undef NOISY_ICON_LOADING
110 #else
111 #undef NOISY_IMAGE_LOADING
112 #undef NOISY_ICON_LOADING
113 #endif
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
159 // see bug 156731
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())))
165 ? PR_FALSE
166 : HaveFixedSize(aReflowState.mStylePosition);
169 nsIFrame*
170 NS_NewImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
172 return new (aPresShell) nsImageFrame(aContext);
176 nsImageFrame::nsImageFrame(nsStyleContext* aContext) :
177 ImageFrameSuper(aContext),
178 mComputedSize(0, 0),
179 mIntrinsicSize(0, 0)
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()
189 NS_IMETHODIMP
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);
196 return NS_OK;
199 return ImageFrameSuper::QueryInterface(aIID, aInstancePtr);
202 #ifdef ACCESSIBILITY
203 NS_IMETHODIMP nsImageFrame::GetAccessible(nsIAccessible** aAccessible)
205 nsCOMPtr<nsIAccessibilityService> accService = do_GetService("@mozilla.org/accessibilityService;1");
207 if (accService) {
208 return accService->CreateHTMLImageAccessible(static_cast<nsIFrame*>(this), aAccessible);
211 return NS_ERROR_FAILURE;
213 #endif
215 NS_IMETHODIMP_(nsrefcnt) nsImageFrame::AddRef(void)
217 NS_WARNING("not supported for frames");
218 return 1;
221 NS_IMETHODIMP_(nsrefcnt) nsImageFrame::Release(void)
223 NS_WARNING("not supported for frames");
224 return 1;
227 void
228 nsImageFrame::Destroy()
230 // Tell our image map, if there is one, to clean up
231 // This causes the nsImageMap to unregister itself as
232 // a DOM listener.
233 if (mImageMap) {
234 mImageMap->Destroy();
235 NS_RELEASE(mImageMap);
238 // set the frame to null so we don't send messages to a dead object.
239 if (mListener) {
240 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
241 if (imageLoader) {
242 imageLoader->RemoveObserver(mListener);
245 reinterpret_cast<nsImageListener*>(mListener.get())->SetFrame(nsnull);
248 mListener = nsnull;
250 nsSplittableFrame::Destroy();
255 NS_IMETHODIMP
256 nsImageFrame::Init(nsIContent* aContent,
257 nsIFrame* aParent,
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();
272 if (!gIconLoad)
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);
280 if (p)
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));
288 if (image) {
289 image->SetAnimationMode(aPresContext->ImageAnimationMode());
290 // Ensure the animation (if any) is started.
291 image->StartAnimation();
295 return rv;
298 PRBool
299 nsImageFrame::UpdateIntrinsicSize(imgIContainer* aImage)
301 NS_PRECONDITION(aImage, "null image");
303 PRBool intrinsicSizeChanged = PR_FALSE;
305 if (aImage) {
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;
320 void
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
325 // scaled!
327 // XXXbz does this introduce rounding errors because of the cast to
328 // float? Should we just manually add that stuff in every time
329 // instead?
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
347 * mContent.
349 PRBool
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;
362 PRBool
363 nsImageFrame::IsPendingLoad(imgIContainer* aContainer) const
365 // default to pending load in case of errors
366 if (!aContainer) {
367 NS_ERROR("No image container!");
368 return PR_TRUE;
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");
379 return PR_TRUE;
382 nsCOMPtr<imgIContainer> currentContainer;
383 currentRequest->GetImage(getter_AddRefs(currentContainer));
385 return currentContainer != aContainer;
389 nsRect
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;
415 return r;
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
421 // is changed too).
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 && \
430 (_loadingOK)))
432 /* static */
433 PRBool
434 nsImageFrame::ShouldCreateImageFrameFor(nsIContent* aContent,
435 nsStyleContext* aStyleContext)
437 PRInt32 state = aContent->IntrinsicState();
438 if (IMAGE_OK(state,
439 HaveFixedSize(aStyleContext->GetStylePosition()))) {
440 // Image is fine; do the image frame thing
441 return PR_TRUE;
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
449 // - else:
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
452 // icon.
453 // - if QuirksMode, and the IMG has a size show an icon.
454 // - otherwise, skip the icon
455 PRBool useSizedBox;
457 if (aStyleContext->GetStyleUIReset()->mForceBrokenImageIcon) {
458 useSizedBox = PR_TRUE;
460 else if (gIconLoad && gIconLoad->mPrefForceInlineAltText) {
461 useSizedBox = PR_FALSE;
463 else {
464 if (aStyleContext->PresContext()->CompatibilityMode() !=
465 eCompatibility_NavQuirks) {
466 useSizedBox = PR_FALSE;
468 else {
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
475 // text).
476 if (!aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::alt) &&
477 localName != nsGkAtoms::object &&
478 localName != nsGkAtoms::input) {
479 useSizedBox = PR_TRUE;
481 else {
482 // check whether we have fixed size
483 useSizedBox = HaveFixedSize(aStyleContext->GetStylePosition());
488 return useSizedBox;
491 nsresult
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)) {
498 return NS_OK;
501 /* Get requested animation policy from the pres context:
502 * normal = 0
503 * one frame = 1
504 * one loop = 2
506 nsPresContext *presContext = PresContext();
507 aImage->SetAnimationMode(presContext->ImageAnimationMode());
508 // Ensure the animation (if any) is started.
509 aImage->StartAnimation();
511 if (IsPendingLoad(aRequest)) {
512 // We don't care
513 return NS_OK;
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.");
523 if (presShell) {
524 presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
525 NS_FRAME_IS_DIRTY);
529 return NS_OK;
532 nsresult
533 nsImageFrame::OnDataAvailable(imgIRequest *aRequest,
534 gfxIImageFrame *aFrame,
535 const nsRect *aRect)
537 // XXX do we need to make sure that the reflow from the
538 // OnStartContainer has been processed before we start calling
539 // invalidate?
541 NS_ENSURE_ARG_POINTER(aRect);
543 if (!(mState & IMAGE_GOTINITIALREFLOW)) {
544 // Don't bother to do anything; we have a reflow coming up!
545 return NS_OK;
548 // XXX We really need to round this out, now that we're doing better
549 // image scaling!
550 nsRect r = SourceRectToDest(*aRect);
552 // handle iconLoads first...
553 if (HandleIconLoads(aRequest, PR_FALSE)) {
554 // Image changed, invalidate
555 Invalidate(r, PR_FALSE);
556 return NS_OK;
559 if (IsPendingLoad(aRequest)) {
560 // We don't care
561 return NS_OK;
564 // Don't invalidate if the current visible frame isn't the one the data is
565 // from
566 nsCOMPtr<imgIContainer> container;
567 aRequest->GetImage(getter_AddRefs(container));
568 if (container) {
569 nsCOMPtr<gfxIImageFrame> currentFrame;
570 container->GetCurrentFrame(getter_AddRefs(currentFrame));
571 if (aFrame != currentFrame) {
572 // just bail
573 return NS_OK;
577 #ifdef DEBUG_decode
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);
581 #endif
583 Invalidate(r, PR_FALSE);
585 return NS_OK;
588 nsresult
589 nsImageFrame::OnStopDecode(imgIRequest *aRequest,
590 nsresult aStatus,
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))) {
599 return NS_OK;
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);
621 else {
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) {
628 if (presShell) {
629 presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
630 NS_FRAME_IS_DIRTY);
632 } else {
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);
641 return NS_OK;
644 nsresult
645 nsImageFrame::FrameChanged(imgIContainer *aContainer,
646 gfxIImageFrame *aNewFrame,
647 nsRect *aDirtyRect)
649 if (!GetStyleVisibility()->IsVisible()) {
650 return NS_OK;
653 if (IsPendingLoad(aContainer)) {
654 // We don't care about it
655 return NS_OK;
658 nsRect r = SourceRectToDest(*aDirtyRect);
660 // Update border+content to account for image change
661 Invalidate(r, PR_FALSE);
662 return NS_OK;
665 void
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);
673 if (imageLoader) {
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);
684 } else {
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))));
699 /* virtual */ nsSize
700 nsImageFrame::ComputeSize(nsIRenderingContext *aRenderingContext,
701 nsSize aCBSize, nscoord aAvailableWidth,
702 nsSize aMargin, nsSize aBorder, nsSize aPadding,
703 PRBool aShrinkWrap)
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);
720 nsRect
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.
727 nscoord
728 nsImageFrame::GetContinuationOffset() const
730 nscoord offset = 0;
731 for (nsIFrame *f = GetPrevInFlow(); f; f = f->GetPrevInFlow()) {
732 offset += f->GetContentRect().height;
734 NS_ASSERTION(offset >= 0, "bogus GetContentRect");
735 return offset;
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.
743 nscoord result;
744 DISPLAY_MIN_WIDTH(this, result);
745 nsPresContext *presContext = PresContext();
746 EnsureIntrinsicSize(presContext);
747 result = mIntrinsicSize.width;
748 return result;
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.
756 nscoord result;
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;
762 return result;
765 /* virtual */ nsSize
766 nsImageFrame::GetIntrinsicRatio()
768 EnsureIntrinsicSize(PresContext());
769 return mIntrinsicSize;
772 NS_IMETHODIMP
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;
791 } else {
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;
801 mComputedSize =
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??");
825 if (imageLoader) {
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);
859 return NS_OK;
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
867 nscoord
868 nsImageFrame::MeasureString(const PRUnichar* aString,
869 PRInt32 aLength,
870 nscoord aMaxWidth,
871 PRUint32& aMaxFit,
872 nsIRenderingContext& aContext)
874 nscoord totalWidth = 0;
875 nscoord spaceWidth;
876 aContext.SetTextRunRTL(PR_FALSE);
877 aContext.GetWidth(' ', spaceWidth);
879 aMaxFit = 0;
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;
888 break;
892 // Measure this chunk of text, and see if it fits
893 nscoord width =
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
898 // include it
899 if (fits || (0 == totalWidth)) {
900 // New piece fits
901 totalWidth += width;
903 // If there's a trailing space then see if it fits as well
904 if (trailingSpace) {
905 if ((totalWidth + spaceWidth) <= aMaxWidth) {
906 totalWidth += spaceWidth;
907 } else {
908 // Space won't fit. Leave it at the end but don't include it in
909 // the width
910 fits = PR_FALSE;
913 len++;
916 aMaxFit += len;
917 aString += len;
918 aLength -= len;
921 if (!fits) {
922 break;
925 return totalWidth;
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
930 void
931 nsImageFrame::DisplayAltText(nsPresContext* aPresContext,
932 nsIRenderingContext& aRenderingContext,
933 const nsString& aAltText,
934 const nsRect& aRect)
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
941 nsIFontMetrics* fm;
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();
954 nscoord y = aRect.y;
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,
961 aRenderingContext);
963 // Display the text
964 nsresult rv = NS_ERROR_FAILURE;
966 if (aPresContext->BidiEnabled()) {
967 nsBidiPresUtils* bidiUtils = aPresContext->GetBidiUtils();
969 if (bidiUtils) {
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);
975 else
976 rv = bidiUtils->RenderText(str, maxFit, NSBIDI_LTR,
977 aPresContext, aRenderingContext,
978 aRect.x, y + maxAscent);
981 if (NS_FAILED(rv))
982 aRenderingContext.DrawString(str, maxFit, aRect.x, y + maxAscent);
984 // Move to the next line
985 str += maxFit;
986 strLen -= maxFit;
987 y += height;
988 firstLine = PR_FALSE;
991 NS_RELEASE(fm);
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
1004 // mComputedBorder
1005 SetBorderStyle(side, NS_STYLE_BORDER_STYLE_INSET);
1010 void
1011 nsImageFrame::DisplayAltFeedback(nsIRenderingContext& aRenderingContext,
1012 const nsRect& aDirtyRect,
1013 imgIRequest* aRequest,
1014 nsPoint aPt)
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
1029 // our frame bounds
1030 if ((inner.width < 2 * borderEdgeWidth) || (inner.height < 2 * borderEdgeWidth)) {
1031 return;
1034 // Paint the border
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()) {
1044 return;
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
1054 if (dispIcon) {
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;
1064 if (aRequest) {
1065 aRequest->GetImage(getter_AddRefs(imgCon));
1067 if (imgCon) {
1068 // draw it
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);
1073 iconUsed = PR_TRUE;
1077 // if we could not draw the image, then just draw some graffiti
1078 if (!iconUsed) {
1079 nscolor oldColor;
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();
1102 if (content) {
1103 nsXPIDLString altText;
1104 nsCSSFrameConstructor::GetAlternateTextFor(content, content->Tag(),
1105 altText);
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,
1118 aDirtyRect,
1119 IMAGE_OK(f->GetContent()->IntrinsicState(), PR_TRUE)
1120 ? nsImageFrame::gIconLoad->mLoadingImage
1121 : nsImageFrame::gIconLoad->mBrokenImage,
1122 aPt);
1125 #ifdef NS_DEBUG
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));
1133 aCtx->PushState();
1134 aCtx->Translate(inner.x, inner.y);
1135 f->GetImageMap(pc)->Draw(pc, *aCtx);
1136 aCtx->PopState();
1138 #endif
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
1144 * received events.
1146 class nsDisplayImage : public nsDisplayItem {
1147 public:
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")
1158 private:
1159 nsCOMPtr<imgIContainer> mImage;
1162 void
1163 nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
1164 nsIRenderingContext* aCtx, const nsRect& aDirtyRect) {
1165 static_cast<nsImageFrame*>(mFrame)->
1166 PaintImage(*aCtx, aBuilder->ToReferenceFrame(mFrame), aDirtyRect, mImage);
1169 void
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;
1177 nsRect clip;
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();
1197 NS_IMETHODIMP
1198 nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1199 const nsRect& aDirtyRect,
1200 const nsDisplayListSet& aLists)
1202 if (!IsVisibleForPainting(aBuilder))
1203 return NS_OK;
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
1208 // place.
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;
1220 if (imageLoader) {
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);
1240 else {
1241 rv = aLists.Content()->AppendNewToTop(new (aBuilder)
1242 nsDisplayImage(this, imgCon));
1243 NS_ENSURE_SUCCESS(rv, rv);
1245 #ifdef DEBUG
1246 if (GetShowFrameBorders() && GetImageMap(PresContext())) {
1247 rv = aLists.Outlines()->AppendNewToTop(new (aBuilder)
1248 nsDisplayGeneric(this, PaintDebugImageMap, "DebugImageMap"));
1249 NS_ENSURE_SUCCESS(rv, rv);
1251 #endif
1255 // XXX what on EARTH is this code for?
1256 PRInt16 displaySelection = 0;
1257 nsresult result;
1258 nsPresContext* presContext = PresContext();
1259 result = presContext->PresShell()->GetSelectionFlags(&displaySelection);
1260 if (NS_FAILED(result))
1261 return 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)
1278 PRInt32 rangeCount;
1279 selection->GetRangeCount(&rangeCount);
1280 if (rangeCount == 1) //if not one then let code drop to nsFrame::Paint
1282 nsCOMPtr<nsIContent> parentContent = mContent->GetParent();
1283 if (parentContent)
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));
1291 if (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
1309 #endif
1311 return DisplaySelectionOverlay(aBuilder, aLists,
1312 nsISelectionDisplay::DISPLAY_IMAGES);
1315 NS_IMETHODIMP
1316 nsImageFrame::GetImageMap(nsPresContext *aPresContext, nsIImageMap **aImageMap)
1318 nsImageMap *map = GetImageMap(aPresContext);
1319 return CallQueryInterface(map, aImageMap);
1322 nsImageMap*
1323 nsImageFrame::GetImageMap(nsPresContext* aPresContext)
1325 if (!mImageMap) {
1326 nsIDocument* doc = mContent->GetDocument();
1327 if (!doc) {
1328 return nsnull;
1331 nsAutoString usemap;
1332 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, usemap);
1334 nsCOMPtr<nsIDOMHTMLMapElement> map = nsImageMapUtils::FindImageMap(doc,usemap);
1335 if (map) {
1336 mImageMap = new nsImageMap();
1337 if (mImageMap) {
1338 NS_ADDREF(mImageMap);
1339 mImageMap->Init(aPresContext->PresShell(), this, map);
1344 return mImageMap;
1347 PRBool
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).
1356 void
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();
1366 x -= inner.x;
1367 y -= inner.y;
1369 aResult.x = nsPresContext::AppUnitsToIntCSSPixels(x);
1370 aResult.y = nsPresContext::AppUnitsToIntCSSPixels(y);
1373 PRBool
1374 nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget,
1375 nsIContent** aNode)
1377 PRBool status = PR_FALSE;
1378 aTarget.Truncate();
1379 *aHref = nsnull;
1380 *aNode = nsnull;
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));
1386 if (link) {
1387 link->GetHrefURI(aHref);
1388 status = (*aHref != nsnull);
1390 nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(content));
1391 if (anchor) {
1392 anchor->GetTarget(aTarget);
1394 NS_ADDREF(*aNode = content);
1395 break;
1398 return status;
1401 NS_IMETHODIMP
1402 nsImageFrame::GetContentForEvent(nsPresContext* aPresContext,
1403 nsEvent* aEvent,
1404 nsIContent** aContent)
1406 NS_ENSURE_ARG_POINTER(aContent);
1407 nsImageMap* map;
1408 map = GetImageMap(aPresContext);
1410 if (nsnull != map) {
1411 nsIntPoint p;
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) {
1418 *aContent = area;
1419 NS_ADDREF(*aContent);
1420 return NS_OK;
1424 *aContent = GetContent();
1425 NS_IF_ADDREF(*aContent);
1426 return NS_OK;
1429 // XXX what should clicks on transparent pixels do?
1430 NS_IMETHODIMP
1431 nsImageFrame::HandleEvent(nsPresContext* aPresContext,
1432 nsGUIEvent* aEvent,
1433 nsEventStatus* aEventStatus)
1435 NS_ENSURE_ARG_POINTER(aEventStatus);
1436 nsImageMap* map;
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) {
1445 nsIntPoint p;
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;
1474 nsCAutoString spec;
1475 uri->GetSpec(spec);
1476 spec += nsPrintfCString("?%d,%d", p.x, p.y);
1477 uri->SetSpec(spec);
1479 PRBool clicked = PR_FALSE;
1480 if (aEvent->message == NS_MOUSE_BUTTON_UP) {
1481 *aEventStatus = nsEventStatus_eConsumeDoDefault;
1482 clicked = PR_TRUE;
1484 nsContentUtils::TriggerLink(anchorNode, aPresContext, uri, target,
1485 clicked, PR_TRUE);
1491 return nsSplittableFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
1494 NS_IMETHODIMP
1495 nsImageFrame::GetCursor(const nsPoint& aPoint,
1496 nsIFrame::Cursor& aCursor)
1498 nsPresContext* context = PresContext();
1499 nsImageMap* map = GetImageMap(context);
1500 if (nsnull != map) {
1501 nsIntPoint p;
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());
1513 if (areaStyle) {
1514 FillCursorInformationFromStyle(areaStyle->GetStyleUserInterface(),
1515 aCursor);
1516 if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
1517 aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
1519 return NS_OK;
1523 return nsFrame::GetCursor(aPoint, aCursor);
1526 NS_IMETHODIMP
1527 nsImageFrame::AttributeChanged(PRInt32 aNameSpaceID,
1528 nsIAtom* aAttribute,
1529 PRInt32 aModType)
1531 nsresult rv = nsSplittableFrame::AttributeChanged(aNameSpaceID,
1532 aAttribute, aModType);
1533 if (NS_FAILED(rv)) {
1534 return rv;
1536 if (nsGkAtoms::alt == aAttribute)
1538 PresContext()->PresShell()->FrameNeedsReflow(this,
1539 nsIPresShell::eStyleChange,
1540 NS_FRAME_IS_DIRTY);
1543 return NS_OK;
1546 nsIAtom*
1547 nsImageFrame::GetType() const
1549 return nsGkAtoms::imageFrame;
1552 #ifdef DEBUG
1553 NS_IMETHODIMP
1554 nsImageFrame::GetFrameName(nsAString& aResult) const
1556 return MakeFrameName(NS_LITERAL_STRING("ImageFrame"), aResult);
1559 NS_IMETHODIMP
1560 nsImageFrame::List(FILE* out, PRInt32 aIndent) const
1562 IndentBy(out, aIndent);
1563 ListTag(out);
1564 #ifdef DEBUG_waterson
1565 fprintf(out, " [parent=%p]", mParent);
1566 #endif
1567 if (HasView()) {
1568 fprintf(out, " [view=%p]", (void*)GetView());
1570 fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width,
1571 mRect.height);
1572 if (0 != mState) {
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);
1579 if (imageLoader) {
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());
1591 fputs("\n", out);
1592 return NS_OK;
1594 #endif
1596 PRIntn
1597 nsImageFrame::GetSkipSides() const
1599 PRIntn skip = 0;
1600 if (nsnull != GetPrevInFlow()) {
1601 skip |= 1 << NS_SIDE_TOP;
1603 if (nsnull != GetNextInFlow()) {
1604 skip |= 1 << NS_SIDE_BOTTOM;
1606 return skip;
1609 NS_IMETHODIMP
1610 nsImageFrame::GetIntrinsicImageSize(nsSize& aSize)
1612 aSize = mIntrinsicSize;
1613 return NS_OK;
1616 nsresult
1617 nsImageFrame::LoadIcon(const nsAString& aSpec,
1618 nsPresContext *aPresContext,
1619 imgIRequest** aRequest)
1621 nsresult rv = NS_OK;
1622 NS_PRECONDITION(!aSpec.IsEmpty(), "What happened??");
1624 if (!sIOService) {
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
1644 apply to icons. */
1645 nsnull, /* referrer (not relevant for icons) */
1646 loadGroup,
1647 mListener,
1648 nsnull, /* Not associated with any particular document */
1649 loadFlags,
1650 nsnull,
1651 nsnull,
1652 aRequest);
1655 void
1656 nsImageFrame::GetDocumentCharacterSet(nsACString& aCharset) const
1658 if (mContent) {
1659 NS_ASSERTION(mContent->GetDocument(),
1660 "Frame still alive after content removed from document!");
1661 aCharset = mContent->GetDocument()->GetDocumentCharacterSet();
1665 void
1666 nsImageFrame::SpecToURI(const nsAString& aSpec, nsIIOService *aIOService,
1667 nsIURI **aURI)
1669 nsCOMPtr<nsIURI> baseURI;
1670 if (mContent) {
1671 baseURI = mContent->GetBaseURI();
1673 nsCAutoString charset;
1674 GetDocumentCharacterSet(charset);
1675 NS_NewURI(aURI, aSpec,
1676 charset.IsEmpty() ? nsnull : charset.get(),
1677 baseURI, aIOService);
1680 void
1681 nsImageFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
1683 if (!aPresContext)
1684 return;
1686 NS_PRECONDITION(nsnull != aLoadGroup, "null OUT parameter pointer");
1688 nsIPresShell *shell = aPresContext->GetPresShell();
1690 if (!shell)
1691 return;
1693 nsIDocument *doc = shell->GetDocument();
1694 if (!doc)
1695 return;
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);
1708 if (!gIconLoad)
1709 return NS_ERROR_OUT_OF_MEMORY;
1710 NS_ADDREF(gIconLoad);
1712 nsresult rv;
1713 // create a loader and load the images
1714 rv = LoadIcon(loadingSrc,
1715 aPresContext,
1716 getter_AddRefs(gIconLoad->mLoadingImage));
1717 #ifdef NOISY_ICON_LOADING
1718 printf("Loading request %p, rv=%u\n",
1719 gIconLoad->mLoadingImage.get(), rv);
1720 #endif
1722 if (NS_FAILED(rv)) {
1723 return rv;
1726 rv = LoadIcon(brokenSrc,
1727 aPresContext,
1728 getter_AddRefs(gIconLoad->mBrokenImage));
1729 #ifdef NOISY_ICON_LOADING
1730 printf("Loading request %p, rv=%u\n",
1731 gIconLoad->mBrokenImage.get(), rv);
1732 #endif
1734 // ImageLoader will callback into OnStartContainer, which will
1735 // handle the mIconsLoaded flag
1737 return rv;
1740 PRBool nsImageFrame::HandleIconLoads(imgIRequest* aRequest, PRBool aLoaded)
1742 PRBool result = PR_FALSE;
1744 if (gIconLoad) {
1745 // check which image it is
1746 if (aRequest == gIconLoad->mLoadingImage ||
1747 aRequest == gIconLoad->mBrokenImage) {
1748 result = PR_TRUE;
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" );
1759 #endif
1762 #ifdef NOISY_ICON_LOADING
1763 printf( "HandleIconLoads returned %s (%p)\n", result ? "TRUE" : "FALSE", this);
1764 #endif
1766 return result;
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),
1778 mIconsLoaded(0)
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);
1787 GetPrefs();
1791 NS_IMETHODIMP
1792 nsImageFrame::IconLoad::Observe(nsISupports *aSubject, const char* aTopic,
1793 const PRUnichar* aData)
1795 NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
1796 "wrong topic");
1797 #ifdef DEBUG
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))
1802 break;
1803 #endif
1805 GetPrefs();
1806 return NS_OK;
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",
1816 PR_TRUE);
1819 NS_IMPL_ISUPPORTS2(nsImageListener, imgIDecoderObserver, imgIContainerObserver)
1821 nsImageListener::nsImageListener(nsImageFrame *aFrame) :
1822 mFrame(aFrame)
1826 nsImageListener::~nsImageListener()
1830 NS_IMETHODIMP nsImageListener::OnStartContainer(imgIRequest *aRequest,
1831 imgIContainer *aImage)
1833 if (!mFrame)
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)
1843 if (!mFrame)
1844 return NS_ERROR_FAILURE;
1846 return mFrame->OnDataAvailable(aRequest, aFrame, aRect);
1849 NS_IMETHODIMP nsImageListener::OnStopDecode(imgIRequest *aRequest,
1850 nsresult status,
1851 const PRUnichar *statusArg)
1853 if (!mFrame)
1854 return NS_ERROR_FAILURE;
1856 return mFrame->OnStopDecode(aRequest, status, statusArg);
1859 NS_IMETHODIMP nsImageListener::FrameChanged(imgIContainer *aContainer,
1860 gfxIImageFrame *newframe,
1861 nsRect * dirtyRect)
1863 if (!mFrame)
1864 return NS_ERROR_FAILURE;
1866 return mFrame->FrameChanged(aContainer, newframe, dirtyRect);
1869 static PRBool
1870 IsInAutoWidthTableCellForQuirk(nsIFrame *aFrame)
1872 if (eCompatibility_NavQuirks != aFrame->PresContext()->CompatibilityMode())
1873 return PR_FALSE;
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;
1882 return PR_FALSE;
1885 /* virtual */ void
1886 nsImageFrame::AddInlineMinWidth(nsIRenderingContext *aRenderingContext,
1887 nsIFrame::InlineMinWidthData *aData)
1890 NS_ASSERTION(GetParent(), "Must have a parent if we get here!");
1892 PRBool canBreak =
1893 !CanContinueTextRun() &&
1894 GetParent()->GetStyleText()->WhiteSpaceCanWrap() &&
1895 !IsInAutoWidthTableCellForQuirk(this);
1897 if (canBreak)
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;
1907 if (canBreak)
1908 aData->OptionallyBreak(aRenderingContext);