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 Communicator client 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.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
40 // Netscape Communications
42 // See documentation in associated header file
45 #include "nsImageBoxFrame.h"
46 #include "nsIDeviceContext.h"
47 #include "nsIFontMetrics.h"
48 #include "nsGkAtoms.h"
49 #include "nsStyleContext.h"
50 #include "nsStyleConsts.h"
52 #include "nsPresContext.h"
53 #include "nsBoxLayoutState.h"
55 #include "nsHTMLParts.h"
57 #include "nsLeafFrame.h"
58 #include "nsPresContext.h"
59 #include "nsIRenderingContext.h"
60 #include "nsIPresShell.h"
61 #include "nsIDocument.h"
62 #include "nsIHTMLDocument.h"
63 #include "nsStyleConsts.h"
64 #include "nsImageMap.h"
65 #include "nsILinkHandler.h"
67 #include "nsILoadGroup.h"
68 #include "nsHTMLContainerFrame.h"
70 #include "nsIFontMetrics.h"
71 #include "nsCSSRendering.h"
72 #include "nsIDOMHTMLImageElement.h"
73 #include "nsIDeviceContext.h"
74 #include "nsINameSpaceManager.h"
75 #include "nsTextFragment.h"
76 #include "nsIDOMHTMLMapElement.h"
77 #include "nsBoxLayoutState.h"
78 #include "nsIDOMDocument.h"
79 #include "nsTransform2D.h"
82 #include "nsIServiceManager.h"
84 #include "nsNetUtil.h"
85 #include "nsThreadUtils.h"
86 #include "nsGUIEvent.h"
87 #include "nsEventDispatcher.h"
88 #include "nsDisplayList.h"
90 #include "nsContentUtils.h"
92 #define ONLOAD_CALLED_TOO_EARLY 1
94 class nsImageBoxFrameEvent
: public nsRunnable
97 nsImageBoxFrameEvent(nsIContent
*content
, PRUint32 message
)
98 : mContent(content
), mMessage(message
) {}
103 nsCOMPtr
<nsIContent
> mContent
;
108 nsImageBoxFrameEvent::Run()
110 nsIDocument
* doc
= mContent
->GetOwnerDoc();
115 nsIPresShell
*pres_shell
= doc
->GetPrimaryShell();
120 nsCOMPtr
<nsPresContext
> pres_context
= pres_shell
->GetPresContext();
125 nsEventStatus status
= nsEventStatus_eIgnore
;
126 nsEvent
event(PR_TRUE
, mMessage
);
128 event
.flags
|= NS_EVENT_FLAG_CANT_BUBBLE
;
129 nsEventDispatcher::Dispatch(mContent
, pres_context
, &event
, nsnull
, &status
);
133 // Fire off an event that'll asynchronously call the image elements
134 // onload handler once handled. This is needed since the image library
135 // can't decide if it wants to call it's observer methods
136 // synchronously or asynchronously. If an image is loaded from the
137 // cache the notifications come back synchronously, but if the image
138 // is loaded from the netswork the notifications come back
142 FireImageDOMEvent(nsIContent
* aContent
, PRUint32 aMessage
)
144 NS_ASSERTION(aMessage
== NS_LOAD
|| aMessage
== NS_LOAD_ERROR
,
147 nsCOMPtr
<nsIRunnable
> event
= new nsImageBoxFrameEvent(aContent
, aMessage
);
148 if (NS_FAILED(NS_DispatchToCurrentThread(event
)))
149 NS_WARNING("failed to dispatch image event");
153 // NS_NewImageBoxFrame
155 // Creates a new image frame and returns it
158 NS_NewImageBoxFrame (nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
160 return new (aPresShell
) nsImageBoxFrame (aPresShell
, aContext
);
163 NS_IMPL_FRAMEARENA_HELPERS(nsImageBoxFrame
)
166 nsImageBoxFrame::AttributeChanged(PRInt32 aNameSpaceID
,
170 nsresult rv
= nsLeafBoxFrame::AttributeChanged(aNameSpaceID
, aAttribute
,
173 if (aAttribute
== nsGkAtoms::src
) {
175 PresContext()->PresShell()->
176 FrameNeedsReflow(this, nsIPresShell::eStyleChange
, NS_FRAME_IS_DIRTY
);
178 else if (aAttribute
== nsGkAtoms::validate
)
184 nsImageBoxFrame::nsImageBoxFrame(nsIPresShell
* aShell
, nsStyleContext
* aContext
):
185 nsLeafBoxFrame(aShell
, aContext
),
187 mLoadFlags(nsIRequest::LOAD_NORMAL
),
188 mUseSrcAttr(PR_FALSE
),
189 mSuppressStyleCheck(PR_FALSE
)
191 MarkIntrinsicWidthsDirty();
194 nsImageBoxFrame::~nsImageBoxFrame()
200 nsImageBoxFrame::MarkIntrinsicWidthsDirty()
202 SizeNeedsRecalc(mImageSize
);
203 nsLeafBoxFrame::MarkIntrinsicWidthsDirty();
207 nsImageBoxFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
209 // Release image loader first so that it's refcnt can go to zero
211 mImageRequest
->CancelAndForgetObserver(NS_ERROR_FAILURE
);
214 reinterpret_cast<nsImageBoxListener
*>(mListener
.get())->SetFrame(nsnull
); // set the frame to null so we don't send messages to a dead object.
216 nsLeafBoxFrame::DestroyFrom(aDestructRoot
);
221 nsImageBoxFrame::Init(nsIContent
* aContent
,
223 nsIFrame
* aPrevInFlow
)
226 nsImageBoxListener
*listener
;
227 NS_NEWXPCOM(listener
, nsImageBoxListener
);
229 listener
->SetFrame(this);
230 listener
->QueryInterface(NS_GET_IID(imgIDecoderObserver
), getter_AddRefs(mListener
));
231 NS_RELEASE(listener
);
234 mSuppressStyleCheck
= PR_TRUE
;
235 nsresult rv
= nsLeafBoxFrame::Init(aContent
, aParent
, aPrevInFlow
);
236 mSuppressStyleCheck
= PR_FALSE
;
245 nsImageBoxFrame::UpdateImage()
248 mImageRequest
->CancelAndForgetObserver(NS_ERROR_FAILURE
);
249 mImageRequest
= nsnull
;
252 // get the new image src
254 mContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::src
, src
);
255 mUseSrcAttr
= !src
.IsEmpty();
257 nsIDocument
* doc
= mContent
->GetDocument();
259 // No need to do anything here...
262 nsCOMPtr
<nsIURI
> baseURI
= mContent
->GetBaseURI();
263 nsCOMPtr
<nsIURI
> uri
;
264 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri
),
269 if (uri
&& nsContentUtils::CanLoadImage(uri
, mContent
, doc
,
270 mContent
->NodePrincipal())) {
271 nsContentUtils::LoadImage(uri
, doc
, mContent
->NodePrincipal(),
272 doc
->GetDocumentURI(), mListener
, mLoadFlags
,
273 getter_AddRefs(mImageRequest
));
276 // Only get the list-style-image if we aren't being drawn
277 // by a native theme.
278 PRUint8 appearance
= GetStyleDisplay()->mAppearance
;
279 if (!(appearance
&& nsBox::gTheme
&&
280 nsBox::gTheme
->ThemeSupportsWidget(nsnull
, this, appearance
))) {
281 // get the list-style-image
282 imgIRequest
*styleRequest
= GetStyleList()->GetListStyleImage();
284 styleRequest
->Clone(mListener
, getter_AddRefs(mImageRequest
));
289 if (!mImageRequest
) {
290 // We have no image, so size to 0
291 mIntrinsicSize
.SizeTo(0, 0);
296 nsImageBoxFrame::UpdateLoadFlags()
298 static nsIContent::AttrValuesArray strings
[] =
299 {&nsGkAtoms::always
, &nsGkAtoms::never
, nsnull
};
300 switch (mContent
->FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::validate
,
301 strings
, eCaseMatters
)) {
303 mLoadFlags
= nsIRequest::VALIDATE_ALWAYS
;
306 mLoadFlags
= nsIRequest::VALIDATE_NEVER
|nsIRequest::LOAD_FROM_CACHE
;
309 mLoadFlags
= nsIRequest::LOAD_NORMAL
;
314 class nsDisplayXULImage
: public nsDisplayItem
{
316 nsDisplayXULImage(nsImageBoxFrame
* aFrame
) : nsDisplayItem(aFrame
) {
317 MOZ_COUNT_CTOR(nsDisplayXULImage
);
319 #ifdef NS_BUILD_REFCNT_LOGGING
320 virtual ~nsDisplayXULImage() {
321 MOZ_COUNT_DTOR(nsDisplayXULImage
);
325 // Doesn't handle HitTest because nsLeafBoxFrame already creates an
326 // event receiver for us
327 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
328 nsIRenderingContext
* aCtx
);
329 NS_DISPLAY_DECL_NAME("XULImage")
332 void nsDisplayXULImage::Paint(nsDisplayListBuilder
* aBuilder
,
333 nsIRenderingContext
* aCtx
)
335 static_cast<nsImageBoxFrame
*>(mFrame
)->
336 PaintImage(*aCtx
, mVisibleRect
, aBuilder
->ToReferenceFrame(mFrame
),
337 aBuilder
->ShouldSyncDecodeImages()
338 ? (PRUint32
) imgIContainer::FLAG_SYNC_DECODE
339 : (PRUint32
) imgIContainer::FLAG_NONE
);
343 nsImageBoxFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
344 const nsRect
& aDirtyRect
,
345 const nsDisplayListSet
& aLists
)
347 nsresult rv
= nsLeafBoxFrame::BuildDisplayList(aBuilder
, aDirtyRect
, aLists
);
348 NS_ENSURE_SUCCESS(rv
, rv
);
350 if ((0 == mRect
.width
) || (0 == mRect
.height
)) {
351 // Do not render when given a zero area. This avoids some useless
352 // scaling work while we wait for our image dimensions to arrive
357 if (!IsVisibleForPainting(aBuilder
))
360 return aLists
.Content()->AppendNewToTop(new (aBuilder
) nsDisplayXULImage(this));
364 nsImageBoxFrame::PaintImage(nsIRenderingContext
& aRenderingContext
,
365 const nsRect
& aDirtyRect
, nsPoint aPt
,
376 // don't draw if the image is not dirty
378 if (!dirty
.IntersectRect(aDirtyRect
, rect
))
381 nsCOMPtr
<imgIContainer
> imgCon
;
382 mImageRequest
->GetImage(getter_AddRefs(imgCon
));
385 PRBool hasSubRect
= !mUseSrcAttr
&& (mSubRect
.width
> 0 || mSubRect
.height
> 0);
386 nsLayoutUtils::DrawSingleImage(&aRenderingContext
, imgCon
,
387 nsLayoutUtils::GetGraphicsFilterForFrame(this),
388 rect
, dirty
, aFlags
, hasSubRect
? &mSubRect
: nsnull
);
394 // DidSetStyleContext
396 // When the style context changes, make sure that all of our image is up to date.
399 nsImageBoxFrame::DidSetStyleContext(nsStyleContext
* aOldStyleContext
)
401 nsLeafBoxFrame::DidSetStyleContext(aOldStyleContext
);
403 // Fetch our subrect.
404 const nsStyleList
* myList
= GetStyleList();
405 mSubRect
= myList
->mImageRegion
; // before |mSuppressStyleCheck| test!
407 if (mUseSrcAttr
|| mSuppressStyleCheck
)
408 return; // No more work required, since the image isn't specified by style.
410 // If we're using a native theme implementation, we shouldn't draw anything.
411 const nsStyleDisplay
* disp
= GetStyleDisplay();
412 if (disp
->mAppearance
&& nsBox::gTheme
&&
413 nsBox::gTheme
->ThemeSupportsWidget(nsnull
, this, disp
->mAppearance
))
416 // If list-style-image changes, we have a new image.
417 nsCOMPtr
<nsIURI
> oldURI
, newURI
;
419 mImageRequest
->GetURI(getter_AddRefs(oldURI
));
420 if (myList
->GetListStyleImage())
421 myList
->GetListStyleImage()->GetURI(getter_AddRefs(newURI
));
423 if (newURI
== oldURI
|| // handles null==null
425 NS_SUCCEEDED(newURI
->Equals(oldURI
, &equal
)) && equal
))
429 } // DidSetStyleContext
432 nsImageBoxFrame::GetImageSize()
434 if (mIntrinsicSize
.width
> 0 && mIntrinsicSize
.height
> 0) {
435 mImageSize
.width
= mIntrinsicSize
.width
;
436 mImageSize
.height
= mIntrinsicSize
.height
;
438 mImageSize
.width
= 0;
439 mImageSize
.height
= 0;
445 * Ok return our dimensions
448 nsImageBoxFrame::GetPrefSize(nsBoxLayoutState
& aState
)
451 DISPLAY_PREF_SIZE(this, size
);
452 if (DoesNeedRecalc(mImageSize
))
455 if (!mUseSrcAttr
&& (mSubRect
.width
> 0 || mSubRect
.height
> 0))
456 size
= nsSize(mSubRect
.width
, mSubRect
.height
);
459 AddBorderAndPadding(size
);
460 nsIBox::AddCSSPrefSize(aState
, this, size
);
462 nsSize minSize
= GetMinSize(aState
);
463 nsSize maxSize
= GetMaxSize(aState
);
465 return BoundsCheck(minSize
, size
, maxSize
);
469 nsImageBoxFrame::GetMinSize(nsBoxLayoutState
& aState
)
471 // An image can always scale down to (0,0).
473 DISPLAY_MIN_SIZE(this, size
);
474 AddBorderAndPadding(size
);
475 nsIBox::AddCSSMinSize(aState
, this, size
);
480 nsImageBoxFrame::GetBoxAscent(nsBoxLayoutState
& aState
)
482 return GetPrefSize(aState
).height
;
486 nsImageBoxFrame::GetType() const
488 return nsGkAtoms::imageBoxFrame
;
493 nsImageBoxFrame::GetFrameName(nsAString
& aResult
) const
495 return MakeFrameName(NS_LITERAL_STRING("ImageBox"), aResult
);
500 NS_IMETHODIMP
nsImageBoxFrame::OnStartContainer(imgIRequest
*request
,
501 imgIContainer
*image
)
503 NS_ENSURE_ARG_POINTER(image
);
505 // Ensure the animation (if any) is started
506 image
->StartAnimation();
510 image
->GetHeight(&h
);
512 mIntrinsicSize
.SizeTo(nsPresContext::CSSPixelsToAppUnits(w
),
513 nsPresContext::CSSPixelsToAppUnits(h
));
515 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW
)) {
516 PresContext()->PresShell()->
517 FrameNeedsReflow(this, nsIPresShell::eStyleChange
, NS_FRAME_IS_DIRTY
);
523 NS_IMETHODIMP
nsImageBoxFrame::OnStopContainer(imgIRequest
*request
,
524 imgIContainer
*image
)
526 nsBoxLayoutState
state(PresContext());
532 NS_IMETHODIMP
nsImageBoxFrame::OnStopDecode(imgIRequest
*request
,
534 const PRUnichar
*statusArg
)
536 if (NS_SUCCEEDED(aStatus
))
537 // Fire an onload DOM event.
538 FireImageDOMEvent(mContent
, NS_LOAD
);
540 // Fire an onerror DOM event.
541 mIntrinsicSize
.SizeTo(0, 0);
542 PresContext()->PresShell()->
543 FrameNeedsReflow(this, nsIPresShell::eStyleChange
, NS_FRAME_IS_DIRTY
);
544 FireImageDOMEvent(mContent
, NS_LOAD_ERROR
);
550 NS_IMETHODIMP
nsImageBoxFrame::FrameChanged(imgIContainer
*container
,
551 nsIntRect
*dirtyRect
)
553 nsBoxLayoutState
state(PresContext());
559 NS_IMPL_ISUPPORTS2(nsImageBoxListener
, imgIDecoderObserver
, imgIContainerObserver
)
561 nsImageBoxListener::nsImageBoxListener()
565 nsImageBoxListener::~nsImageBoxListener()
569 NS_IMETHODIMP
nsImageBoxListener::OnStartContainer(imgIRequest
*request
,
570 imgIContainer
*image
)
573 return NS_ERROR_FAILURE
;
575 return mFrame
->OnStartContainer(request
, image
);
578 NS_IMETHODIMP
nsImageBoxListener::OnStopContainer(imgIRequest
*request
,
579 imgIContainer
*image
)
582 return NS_ERROR_FAILURE
;
584 return mFrame
->OnStopContainer(request
, image
);
587 NS_IMETHODIMP
nsImageBoxListener::OnStopDecode(imgIRequest
*request
,
589 const PRUnichar
*statusArg
)
592 return NS_ERROR_FAILURE
;
594 return mFrame
->OnStopDecode(request
, status
, statusArg
);
597 NS_IMETHODIMP
nsImageBoxListener::FrameChanged(imgIContainer
*container
,
598 nsIntRect
*dirtyRect
)
601 return NS_ERROR_FAILURE
;
603 return mFrame
->FrameChanged(container
, dirtyRect
);