Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : //
7 : // Eric Vaughan
8 : // Netscape Communications
9 : //
10 : // See documentation in associated header file
11 : //
12 :
13 : #include "gfxContext.h"
14 : #include "nsImageBoxFrame.h"
15 : #include "nsGkAtoms.h"
16 : #include "nsStyleContext.h"
17 : #include "nsStyleConsts.h"
18 : #include "nsStyleUtil.h"
19 : #include "nsCOMPtr.h"
20 : #include "nsPresContext.h"
21 : #include "nsBoxLayoutState.h"
22 :
23 : #include "nsHTMLParts.h"
24 : #include "nsString.h"
25 : #include "nsLeafFrame.h"
26 : #include "nsIPresShell.h"
27 : #include "nsIDocument.h"
28 : #include "nsImageMap.h"
29 : #include "nsILinkHandler.h"
30 : #include "nsIURL.h"
31 : #include "nsILoadGroup.h"
32 : #include "nsContainerFrame.h"
33 : #include "nsCSSRendering.h"
34 : #include "nsIDOMHTMLImageElement.h"
35 : #include "nsNameSpaceManager.h"
36 : #include "nsTextFragment.h"
37 : #include "nsIDOMHTMLMapElement.h"
38 : #include "nsTransform2D.h"
39 : #include "nsITheme.h"
40 :
41 : #include "nsIServiceManager.h"
42 : #include "nsIURI.h"
43 : #include "nsThreadUtils.h"
44 : #include "nsDisplayList.h"
45 : #include "ImageLayers.h"
46 : #include "ImageContainer.h"
47 : #include "nsIContent.h"
48 :
49 : #include "nsContentUtils.h"
50 :
51 : #include "mozilla/BasicEvents.h"
52 : #include "mozilla/EventDispatcher.h"
53 : #include "mozilla/Maybe.h"
54 : #include "SVGImageContext.h"
55 : #include "Units.h"
56 :
57 : #define ONLOAD_CALLED_TOO_EARLY 1
58 :
59 : using namespace mozilla;
60 : using namespace mozilla::gfx;
61 : using namespace mozilla::image;
62 : using namespace mozilla::layers;
63 :
64 147 : class nsImageBoxFrameEvent : public Runnable
65 : {
66 : public:
67 49 : nsImageBoxFrameEvent(nsIContent* content, EventMessage message)
68 49 : : mozilla::Runnable("nsImageBoxFrameEvent")
69 : , mContent(content)
70 49 : , mMessage(message)
71 : {
72 49 : }
73 :
74 : NS_IMETHOD Run() override;
75 :
76 : private:
77 : nsCOMPtr<nsIContent> mContent;
78 : EventMessage mMessage;
79 : };
80 :
81 : NS_IMETHODIMP
82 49 : nsImageBoxFrameEvent::Run()
83 : {
84 49 : nsIPresShell *pres_shell = mContent->OwnerDoc()->GetShell();
85 49 : if (!pres_shell) {
86 0 : return NS_OK;
87 : }
88 :
89 98 : RefPtr<nsPresContext> pres_context = pres_shell->GetPresContext();
90 49 : if (!pres_context) {
91 0 : return NS_OK;
92 : }
93 :
94 49 : nsEventStatus status = nsEventStatus_eIgnore;
95 98 : WidgetEvent event(true, mMessage);
96 :
97 49 : event.mFlags.mBubbles = false;
98 49 : EventDispatcher::Dispatch(mContent, pres_context, &event, nullptr, &status);
99 49 : return NS_OK;
100 : }
101 :
102 : // Fire off an event that'll asynchronously call the image elements
103 : // onload handler once handled. This is needed since the image library
104 : // can't decide if it wants to call its observer methods
105 : // synchronously or asynchronously. If an image is loaded from the
106 : // cache the notifications come back synchronously, but if the image
107 : // is loaded from the network the notifications come back
108 : // asynchronously.
109 :
110 : void
111 49 : FireImageDOMEvent(nsIContent* aContent, EventMessage aMessage)
112 : {
113 49 : NS_ASSERTION(aMessage == eLoad || aMessage == eLoadError,
114 : "invalid message");
115 :
116 98 : nsCOMPtr<nsIRunnable> event = new nsImageBoxFrameEvent(aContent, aMessage);
117 49 : nsresult rv = aContent->OwnerDoc()->Dispatch("nsImageBoxFrameEvent",
118 : TaskCategory::Other,
119 49 : event.forget());
120 49 : if (NS_FAILED(rv)) {
121 0 : NS_WARNING("failed to dispatch image event");
122 : }
123 49 : }
124 :
125 : //
126 : // NS_NewImageBoxFrame
127 : //
128 : // Creates a new image frame and returns it
129 : //
130 : nsIFrame*
131 62 : NS_NewImageBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
132 : {
133 62 : return new (aPresShell) nsImageBoxFrame(aContext);
134 : }
135 :
136 62 : NS_IMPL_FRAMEARENA_HELPERS(nsImageBoxFrame)
137 :
138 : nsresult
139 8 : nsImageBoxFrame::AttributeChanged(int32_t aNameSpaceID,
140 : nsIAtom* aAttribute,
141 : int32_t aModType)
142 : {
143 8 : nsresult rv = nsLeafBoxFrame::AttributeChanged(aNameSpaceID, aAttribute,
144 8 : aModType);
145 :
146 8 : if (aAttribute == nsGkAtoms::src) {
147 1 : UpdateImage();
148 1 : PresContext()->PresShell()->
149 1 : FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
150 : }
151 7 : else if (aAttribute == nsGkAtoms::validate)
152 0 : UpdateLoadFlags();
153 :
154 8 : return rv;
155 : }
156 :
157 62 : nsImageBoxFrame::nsImageBoxFrame(nsStyleContext* aContext)
158 : : nsLeafBoxFrame(aContext, kClassID)
159 : , mIntrinsicSize(0, 0)
160 : , mLoadFlags(nsIRequest::LOAD_NORMAL)
161 : , mRequestRegistered(false)
162 : , mUseSrcAttr(false)
163 62 : , mSuppressStyleCheck(false)
164 : {
165 62 : MarkIntrinsicISizesDirty();
166 62 : }
167 :
168 30 : nsImageBoxFrame::~nsImageBoxFrame()
169 : {
170 30 : }
171 :
172 :
173 : /* virtual */ void
174 128 : nsImageBoxFrame::MarkIntrinsicISizesDirty()
175 : {
176 128 : SizeNeedsRecalc(mImageSize);
177 128 : nsLeafBoxFrame::MarkIntrinsicISizesDirty();
178 128 : }
179 :
180 : void
181 30 : nsImageBoxFrame::DestroyFrom(nsIFrame* aDestructRoot)
182 : {
183 30 : if (mImageRequest) {
184 44 : nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
185 44 : &mRequestRegistered);
186 :
187 : // Release image loader first so that it's refcnt can go to zero
188 22 : mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
189 : }
190 :
191 30 : if (mListener)
192 30 : reinterpret_cast<nsImageBoxListener*>(mListener.get())->SetFrame(nullptr); // set the frame to null so we don't send messages to a dead object.
193 :
194 30 : nsLeafBoxFrame::DestroyFrom(aDestructRoot);
195 30 : }
196 :
197 :
198 : void
199 62 : nsImageBoxFrame::Init(nsIContent* aContent,
200 : nsContainerFrame* aParent,
201 : nsIFrame* aPrevInFlow)
202 : {
203 62 : if (!mListener) {
204 124 : RefPtr<nsImageBoxListener> listener = new nsImageBoxListener();
205 62 : listener->SetFrame(this);
206 62 : mListener = listener.forget();
207 : }
208 :
209 62 : mSuppressStyleCheck = true;
210 62 : nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow);
211 62 : mSuppressStyleCheck = false;
212 :
213 62 : UpdateLoadFlags();
214 62 : UpdateImage();
215 62 : }
216 :
217 : void
218 69 : nsImageBoxFrame::UpdateImage()
219 : {
220 69 : nsPresContext* presContext = PresContext();
221 :
222 138 : RefPtr<imgRequestProxy> oldImageRequest = mImageRequest;
223 :
224 69 : if (mImageRequest) {
225 7 : nsLayoutUtils::DeregisterImageRequest(presContext, mImageRequest,
226 7 : &mRequestRegistered);
227 7 : mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
228 7 : mImageRequest = nullptr;
229 : }
230 :
231 : // get the new image src
232 138 : nsAutoString src;
233 69 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
234 69 : mUseSrcAttr = !src.IsEmpty();
235 69 : if (mUseSrcAttr) {
236 1 : nsIDocument* doc = mContent->GetComposedDoc();
237 1 : if (doc) {
238 : nsContentPolicyType contentPolicyType;
239 2 : nsCOMPtr<nsIPrincipal> loadingPrincipal;
240 1 : nsContentUtils::GetContentPolicyTypeForUIImageLoading(mContent,
241 2 : getter_AddRefs(loadingPrincipal),
242 1 : contentPolicyType);
243 :
244 2 : nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI();
245 2 : nsCOMPtr<nsIURI> uri;
246 2 : nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
247 : src,
248 : doc,
249 1 : baseURI);
250 1 : if (uri) {
251 3 : nsresult rv = nsContentUtils::LoadImage(uri, mContent, doc, loadingPrincipal,
252 : doc->GetDocumentURI(), doc->GetReferrerPolicy(),
253 : mListener, mLoadFlags,
254 3 : EmptyString(), getter_AddRefs(mImageRequest),
255 1 : contentPolicyType);
256 :
257 1 : if (NS_SUCCEEDED(rv) && mImageRequest) {
258 1 : nsLayoutUtils::RegisterImageRequestIfAnimated(presContext,
259 : mImageRequest,
260 1 : &mRequestRegistered);
261 : }
262 : }
263 : }
264 : } else {
265 : // Only get the list-style-image if we aren't being drawn
266 : // by a native theme.
267 68 : uint8_t appearance = StyleDisplay()->mAppearance;
268 68 : if (!(appearance && nsBox::gTheme &&
269 0 : nsBox::gTheme->ThemeSupportsWidget(nullptr, this, appearance))) {
270 : // get the list-style-image
271 68 : imgRequestProxy *styleRequest = StyleList()->GetListStyleImage();
272 68 : if (styleRequest) {
273 52 : styleRequest->Clone(mListener, getter_AddRefs(mImageRequest));
274 : }
275 : }
276 : }
277 :
278 69 : if (!mImageRequest) {
279 : // We have no image, so size to 0
280 16 : mIntrinsicSize.SizeTo(0, 0);
281 : } else {
282 : // We don't want discarding or decode-on-draw for xul images.
283 53 : mImageRequest->StartDecoding(imgIContainer::FLAG_NONE);
284 53 : mImageRequest->LockImage();
285 : }
286 :
287 : // Do this _after_ locking the new image in case they are the same image.
288 69 : if (oldImageRequest) {
289 7 : oldImageRequest->UnlockImage();
290 : }
291 69 : }
292 :
293 : void
294 62 : nsImageBoxFrame::UpdateLoadFlags()
295 : {
296 : static nsIContent::AttrValuesArray strings[] =
297 : {&nsGkAtoms::always, &nsGkAtoms::never, nullptr};
298 124 : switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::validate,
299 62 : strings, eCaseMatters)) {
300 : case 0:
301 0 : mLoadFlags = nsIRequest::VALIDATE_ALWAYS;
302 0 : break;
303 : case 1:
304 1 : mLoadFlags = nsIRequest::VALIDATE_NEVER|nsIRequest::LOAD_FROM_CACHE;
305 1 : break;
306 : default:
307 61 : mLoadFlags = nsIRequest::LOAD_NORMAL;
308 61 : break;
309 : }
310 62 : }
311 :
312 : void
313 365 : nsImageBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
314 : const nsRect& aDirtyRect,
315 : const nsDisplayListSet& aLists)
316 : {
317 365 : nsLeafBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
318 :
319 365 : if ((0 == mRect.width) || (0 == mRect.height)) {
320 : // Do not render when given a zero area. This avoids some useless
321 : // scaling work while we wait for our image dimensions to arrive
322 : // asynchronously.
323 24 : return;
324 : }
325 :
326 365 : if (!IsVisibleForPainting(aBuilder))
327 24 : return;
328 :
329 : uint32_t clipFlags =
330 341 : nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition()) ?
331 341 : 0 : DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT;
332 :
333 : DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
334 682 : clip(aBuilder, this, clipFlags);
335 :
336 682 : nsDisplayList list;
337 : list.AppendNewToTop(
338 341 : new (aBuilder) nsDisplayXULImage(aBuilder, this));
339 :
340 341 : CreateOwnLayerIfNeeded(aBuilder, &list);
341 :
342 341 : aLists.Content()->AppendToTop(&list);
343 : }
344 :
345 : DrawResult
346 82 : nsImageBoxFrame::PaintImage(gfxContext& aRenderingContext,
347 : const nsRect& aDirtyRect, nsPoint aPt,
348 : uint32_t aFlags)
349 : {
350 82 : if (!mImageRequest) {
351 : // This probably means we're drawn by a native theme.
352 0 : return DrawResult::SUCCESS;
353 : }
354 :
355 : // Don't draw if the image's size isn't available.
356 : uint32_t imgStatus;
357 164 : if (!NS_SUCCEEDED(mImageRequest->GetImageStatus(&imgStatus)) ||
358 82 : !(imgStatus & imgIRequest::STATUS_SIZE_AVAILABLE)) {
359 10 : return DrawResult::NOT_READY;
360 : }
361 :
362 144 : nsCOMPtr<imgIContainer> imgCon;
363 72 : mImageRequest->GetImage(getter_AddRefs(imgCon));
364 :
365 72 : if (!imgCon) {
366 0 : return DrawResult::NOT_READY;
367 : }
368 :
369 144 : Maybe<nsPoint> anchorPoint;
370 144 : nsRect dest = GetDestRect(aPt, anchorPoint);
371 :
372 : // don't draw if the image is not dirty
373 : // XXX(seth): Can this actually happen anymore?
374 144 : nsRect dirty;
375 72 : if (!dirty.IntersectRect(aDirtyRect, dest)) {
376 0 : return DrawResult::TEMPORARY_ERROR;
377 : }
378 :
379 72 : bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0);
380 :
381 144 : Maybe<SVGImageContext> svgContext;
382 72 : SVGImageContext::MaybeStoreContextPaint(svgContext, this, imgCon);
383 144 : return nsLayoutUtils::DrawSingleImage(
384 : aRenderingContext,
385 : PresContext(), imgCon,
386 : nsLayoutUtils::GetSamplingFilterForFrame(this),
387 : dest, dirty,
388 : svgContext, aFlags,
389 72 : anchorPoint.ptrOr(nullptr),
390 72 : hasSubRect ? &mSubRect : nullptr);
391 : }
392 :
393 : nsRect
394 83 : nsImageBoxFrame::GetDestRect(const nsPoint& aOffset, Maybe<nsPoint>& aAnchorPoint)
395 : {
396 166 : nsCOMPtr<imgIContainer> imgCon;
397 83 : mImageRequest->GetImage(getter_AddRefs(imgCon));
398 83 : MOZ_ASSERT(imgCon);
399 :
400 166 : nsRect clientRect;
401 83 : GetXULClientRect(clientRect);
402 83 : clientRect += aOffset;
403 83 : nsRect dest;
404 83 : if (!mUseSrcAttr) {
405 : // Our image (if we have one) is coming from the CSS property
406 : // 'list-style-image' (combined with '-moz-image-region'). For now, ignore
407 : // 'object-fit' & 'object-position' in this case, and just fill our rect.
408 : // XXXdholbert Should we even honor these properties in this case? They only
409 : // apply to replaced elements, and I'm not sure we count as a replaced
410 : // element when our image data is determined by CSS.
411 83 : dest = clientRect;
412 : } else {
413 : // Determine dest rect based on intrinsic size & ratio, along with
414 : // 'object-fit' & 'object-position' properties:
415 0 : IntrinsicSize intrinsicSize;
416 0 : nsSize intrinsicRatio;
417 0 : if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) {
418 : // Image has a valid size; use it as intrinsic size & ratio.
419 0 : intrinsicSize.width.SetCoordValue(mIntrinsicSize.width);
420 0 : intrinsicSize.height.SetCoordValue(mIntrinsicSize.height);
421 0 : intrinsicRatio = mIntrinsicSize;
422 : } else {
423 : // Image doesn't have a (valid) intrinsic size.
424 : // Try to look up intrinsic ratio and use that at least.
425 0 : imgCon->GetIntrinsicRatio(&intrinsicRatio);
426 : }
427 0 : aAnchorPoint.emplace();
428 0 : dest = nsLayoutUtils::ComputeObjectDestRect(clientRect,
429 : intrinsicSize,
430 : intrinsicRatio,
431 : StylePosition(),
432 : aAnchorPoint.ptr());
433 : }
434 :
435 166 : return dest;
436 : }
437 :
438 82 : void nsDisplayXULImage::Paint(nsDisplayListBuilder* aBuilder,
439 : gfxContext* aCtx)
440 : {
441 : // Even though we call StartDecoding when we get a new image we pass
442 : // FLAG_SYNC_DECODE_IF_FAST here for the case where the size we draw at is not
443 : // the intrinsic size of the image and we aren't likely to implement predictive
444 : // decoding at the correct size for this class like nsImageFrame has.
445 82 : uint32_t flags = imgIContainer::FLAG_SYNC_DECODE_IF_FAST;
446 82 : if (aBuilder->ShouldSyncDecodeImages())
447 0 : flags |= imgIContainer::FLAG_SYNC_DECODE;
448 82 : if (aBuilder->IsPaintingToWindow())
449 82 : flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
450 :
451 82 : DrawResult result = static_cast<nsImageBoxFrame*>(mFrame)->
452 164 : PaintImage(*aCtx, mVisibleRect, ToReferenceFrame(), flags);
453 :
454 82 : nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
455 82 : }
456 :
457 : nsDisplayItemGeometry*
458 71 : nsDisplayXULImage::AllocateGeometry(nsDisplayListBuilder* aBuilder)
459 : {
460 71 : return new nsDisplayItemGenericImageGeometry(this, aBuilder);
461 : }
462 :
463 : void
464 288 : nsDisplayXULImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
465 : const nsDisplayItemGeometry* aGeometry,
466 : nsRegion* aInvalidRegion)
467 : {
468 288 : auto boxFrame = static_cast<nsImageBoxFrame*>(mFrame);
469 : auto geometry =
470 288 : static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
471 :
472 576 : if (aBuilder->ShouldSyncDecodeImages() &&
473 288 : boxFrame->mImageRequest &&
474 0 : geometry->ShouldInvalidateToSyncDecodeImages()) {
475 : bool snap;
476 0 : aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
477 : }
478 :
479 288 : nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
480 288 : }
481 :
482 : bool
483 57 : nsDisplayXULImage::CanOptimizeToImageLayer(LayerManager* aManager,
484 : nsDisplayListBuilder* aBuilder)
485 : {
486 57 : nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame);
487 57 : if (!imageFrame->CanOptimizeToImageLayer()) {
488 0 : return false;
489 : }
490 :
491 57 : return nsDisplayImageContainer::CanOptimizeToImageLayer(aManager, aBuilder);
492 : }
493 :
494 : already_AddRefed<imgIContainer>
495 57 : nsDisplayXULImage::GetImage()
496 : {
497 57 : nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame);
498 57 : if (!imageFrame->mImageRequest) {
499 0 : return nullptr;
500 : }
501 :
502 114 : nsCOMPtr<imgIContainer> imgCon;
503 57 : imageFrame->mImageRequest->GetImage(getter_AddRefs(imgCon));
504 :
505 57 : return imgCon.forget();
506 : }
507 :
508 : nsRect
509 11 : nsDisplayXULImage::GetDestRect()
510 : {
511 22 : Maybe<nsPoint> anchorPoint;
512 22 : return static_cast<nsImageBoxFrame*>(mFrame)->GetDestRect(ToReferenceFrame(), anchorPoint);
513 : }
514 :
515 : bool
516 57 : nsImageBoxFrame::CanOptimizeToImageLayer()
517 : {
518 57 : bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0);
519 57 : if (hasSubRect) {
520 0 : return false;
521 : }
522 57 : return true;
523 : }
524 :
525 : //
526 : // DidSetStyleContext
527 : //
528 : // When the style context changes, make sure that all of our image is up to date.
529 : //
530 : /* virtual */ void
531 139 : nsImageBoxFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
532 : {
533 139 : nsLeafBoxFrame::DidSetStyleContext(aOldStyleContext);
534 :
535 : // Fetch our subrect.
536 139 : const nsStyleList* myList = StyleList();
537 139 : mSubRect = myList->mImageRegion; // before |mSuppressStyleCheck| test!
538 :
539 139 : if (mUseSrcAttr || mSuppressStyleCheck)
540 195 : return; // No more work required, since the image isn't specified by style.
541 :
542 : // If we're using a native theme implementation, we shouldn't draw anything.
543 77 : const nsStyleDisplay* disp = StyleDisplay();
544 77 : if (disp->mAppearance && nsBox::gTheme &&
545 0 : nsBox::gTheme->ThemeSupportsWidget(nullptr, this, disp->mAppearance))
546 0 : return;
547 :
548 : // If list-style-image changes, we have a new image.
549 83 : nsCOMPtr<nsIURI> oldURI, newURI;
550 77 : if (mImageRequest)
551 55 : mImageRequest->GetURI(getter_AddRefs(oldURI));
552 77 : if (myList->GetListStyleImage())
553 55 : myList->GetListStyleImage()->GetURI(getter_AddRefs(newURI));
554 : bool equal;
555 154 : if (newURI == oldURI || // handles null==null
556 18 : (newURI && oldURI &&
557 12 : NS_SUCCEEDED(newURI->Equals(oldURI, &equal)) && equal))
558 71 : return;
559 :
560 6 : UpdateImage();
561 : } // DidSetStyleContext
562 :
563 : void
564 79 : nsImageBoxFrame::GetImageSize()
565 : {
566 79 : if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) {
567 42 : mImageSize.width = mIntrinsicSize.width;
568 42 : mImageSize.height = mIntrinsicSize.height;
569 : } else {
570 37 : mImageSize.width = 0;
571 37 : mImageSize.height = 0;
572 : }
573 79 : }
574 :
575 : /**
576 : * Ok return our dimensions
577 : */
578 : nsSize
579 472 : nsImageBoxFrame::GetXULPrefSize(nsBoxLayoutState& aState)
580 : {
581 472 : nsSize size(0,0);
582 944 : DISPLAY_PREF_SIZE(this, size);
583 472 : if (DoesNeedRecalc(mImageSize))
584 79 : GetImageSize();
585 :
586 472 : if (!mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0))
587 42 : size = mSubRect.Size();
588 : else
589 430 : size = mImageSize;
590 :
591 472 : nsSize intrinsicSize = size;
592 :
593 472 : nsMargin borderPadding(0,0,0,0);
594 472 : GetXULBorderAndPadding(borderPadding);
595 472 : size.width += borderPadding.LeftRight();
596 472 : size.height += borderPadding.TopBottom();
597 :
598 : bool widthSet, heightSet;
599 472 : nsIFrame::AddXULPrefSize(this, size, widthSet, heightSet);
600 472 : NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE,
601 : "non-intrinsic size expected");
602 :
603 472 : nsSize minSize = GetXULMinSize(aState);
604 472 : nsSize maxSize = GetXULMaxSize(aState);
605 :
606 472 : if (!widthSet && !heightSet) {
607 255 : if (minSize.width != NS_INTRINSICSIZE)
608 255 : minSize.width -= borderPadding.LeftRight();
609 255 : if (minSize.height != NS_INTRINSICSIZE)
610 255 : minSize.height -= borderPadding.TopBottom();
611 255 : if (maxSize.width != NS_INTRINSICSIZE)
612 178 : maxSize.width -= borderPadding.LeftRight();
613 255 : if (maxSize.height != NS_INTRINSICSIZE)
614 0 : maxSize.height -= borderPadding.TopBottom();
615 :
616 : size = nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(minSize.width, minSize.height,
617 : maxSize.width, maxSize.height,
618 255 : intrinsicSize.width, intrinsicSize.height);
619 255 : NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE,
620 : "non-intrinsic size expected");
621 255 : size.width += borderPadding.LeftRight();
622 255 : size.height += borderPadding.TopBottom();
623 255 : return size;
624 : }
625 :
626 217 : if (!widthSet) {
627 26 : if (intrinsicSize.height > 0) {
628 : // Subtract off the border and padding from the height because the
629 : // content-box needs to be used to determine the ratio
630 0 : nscoord height = size.height - borderPadding.TopBottom();
631 0 : size.width = nscoord(int64_t(height) * int64_t(intrinsicSize.width) /
632 0 : int64_t(intrinsicSize.height));
633 : }
634 : else {
635 26 : size.width = intrinsicSize.width;
636 : }
637 :
638 26 : size.width += borderPadding.LeftRight();
639 : }
640 191 : else if (!heightSet) {
641 63 : if (intrinsicSize.width > 0) {
642 56 : nscoord width = size.width - borderPadding.LeftRight();
643 168 : size.height = nscoord(int64_t(width) * int64_t(intrinsicSize.height) /
644 112 : int64_t(intrinsicSize.width));
645 : }
646 : else {
647 7 : size.height = intrinsicSize.height;
648 : }
649 :
650 63 : size.height += borderPadding.TopBottom();
651 : }
652 :
653 217 : return BoundsCheck(minSize, size, maxSize);
654 : }
655 :
656 : nsSize
657 783 : nsImageBoxFrame::GetXULMinSize(nsBoxLayoutState& aState)
658 : {
659 : // An image can always scale down to (0,0).
660 783 : nsSize size(0,0);
661 1566 : DISPLAY_MIN_SIZE(this, size);
662 783 : AddBorderAndPadding(size);
663 : bool widthSet, heightSet;
664 783 : nsIFrame::AddXULMinSize(aState, this, size, widthSet, heightSet);
665 1566 : return size;
666 : }
667 :
668 : nscoord
669 218 : nsImageBoxFrame::GetXULBoxAscent(nsBoxLayoutState& aState)
670 : {
671 218 : return GetXULPrefSize(aState).height;
672 : }
673 :
674 : #ifdef DEBUG_FRAME_DUMP
675 : nsresult
676 0 : nsImageBoxFrame::GetFrameName(nsAString& aResult) const
677 : {
678 0 : return MakeFrameName(NS_LITERAL_STRING("ImageBox"), aResult);
679 : }
680 : #endif
681 :
682 : nsresult
683 316 : nsImageBoxFrame::Notify(imgIRequest* aRequest,
684 : int32_t aType,
685 : const nsIntRect* aData)
686 : {
687 316 : if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
688 96 : nsCOMPtr<imgIContainer> image;
689 48 : aRequest->GetImage(getter_AddRefs(image));
690 48 : return OnSizeAvailable(aRequest, image);
691 : }
692 :
693 268 : if (aType == imgINotificationObserver::DECODE_COMPLETE) {
694 48 : return OnDecodeComplete(aRequest);
695 : }
696 :
697 220 : if (aType == imgINotificationObserver::LOAD_COMPLETE) {
698 : uint32_t imgStatus;
699 49 : aRequest->GetImageStatus(&imgStatus);
700 : nsresult status =
701 49 : imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
702 49 : return OnLoadComplete(aRequest, status);
703 : }
704 :
705 171 : if (aType == imgINotificationObserver::IS_ANIMATED) {
706 2 : return OnImageIsAnimated(aRequest);
707 : }
708 :
709 169 : if (aType == imgINotificationObserver::FRAME_UPDATE) {
710 73 : return OnFrameUpdate(aRequest);
711 : }
712 :
713 96 : return NS_OK;
714 : }
715 :
716 : nsresult
717 48 : nsImageBoxFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
718 : {
719 48 : NS_ENSURE_ARG_POINTER(aImage);
720 :
721 : // Ensure the animation (if any) is started. Note: There is no
722 : // corresponding call to Decrement for this. This Increment will be
723 : // 'cleaned up' by the Request when it is destroyed, but only then.
724 48 : aRequest->IncrementAnimationConsumers();
725 :
726 : nscoord w, h;
727 48 : aImage->GetWidth(&w);
728 48 : aImage->GetHeight(&h);
729 :
730 48 : mIntrinsicSize.SizeTo(nsPresContext::CSSPixelsToAppUnits(w),
731 48 : nsPresContext::CSSPixelsToAppUnits(h));
732 :
733 48 : if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
734 32 : PresContext()->PresShell()->
735 32 : FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
736 : }
737 :
738 48 : return NS_OK;
739 : }
740 :
741 : nsresult
742 48 : nsImageBoxFrame::OnDecodeComplete(imgIRequest* aRequest)
743 : {
744 96 : nsBoxLayoutState state(PresContext());
745 48 : this->XULRedraw(state);
746 96 : return NS_OK;
747 : }
748 :
749 : nsresult
750 49 : nsImageBoxFrame::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus)
751 : {
752 49 : if (NS_SUCCEEDED(aStatus)) {
753 : // Fire an onload DOM event.
754 48 : FireImageDOMEvent(mContent, eLoad);
755 : } else {
756 : // Fire an onerror DOM event.
757 1 : mIntrinsicSize.SizeTo(0, 0);
758 1 : PresContext()->PresShell()->
759 1 : FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
760 1 : FireImageDOMEvent(mContent, eLoadError);
761 : }
762 :
763 49 : return NS_OK;
764 : }
765 :
766 : nsresult
767 2 : nsImageBoxFrame::OnImageIsAnimated(imgIRequest* aRequest)
768 : {
769 : // Register with our refresh driver, if we're animated.
770 2 : nsLayoutUtils::RegisterImageRequest(PresContext(), aRequest,
771 2 : &mRequestRegistered);
772 :
773 2 : return NS_OK;
774 : }
775 :
776 : nsresult
777 73 : nsImageBoxFrame::OnFrameUpdate(imgIRequest* aRequest)
778 : {
779 73 : if ((0 == mRect.width) || (0 == mRect.height)) {
780 36 : return NS_OK;
781 : }
782 :
783 37 : InvalidateLayer(nsDisplayItem::TYPE_XUL_IMAGE);
784 :
785 37 : return NS_OK;
786 : }
787 :
788 2330 : NS_IMPL_ISUPPORTS(nsImageBoxListener, imgINotificationObserver, imgIOnloadBlocker)
789 :
790 62 : nsImageBoxListener::nsImageBoxListener()
791 : {
792 62 : }
793 :
794 60 : nsImageBoxListener::~nsImageBoxListener()
795 : {
796 90 : }
797 :
798 : NS_IMETHODIMP
799 316 : nsImageBoxListener::Notify(imgIRequest *request, int32_t aType, const nsIntRect* aData)
800 : {
801 316 : if (!mFrame)
802 0 : return NS_OK;
803 :
804 316 : return mFrame->Notify(request, aType, aData);
805 : }
806 :
807 : NS_IMETHODIMP
808 61 : nsImageBoxListener::BlockOnload(imgIRequest *aRequest)
809 : {
810 61 : if (mFrame && mFrame->GetContent() && mFrame->GetContent()->GetUncomposedDoc()) {
811 61 : mFrame->GetContent()->GetUncomposedDoc()->BlockOnload();
812 : }
813 :
814 61 : return NS_OK;
815 : }
816 :
817 : NS_IMETHODIMP
818 61 : nsImageBoxListener::UnblockOnload(imgIRequest *aRequest)
819 : {
820 61 : if (mFrame && mFrame->GetContent() && mFrame->GetContent()->GetUncomposedDoc()) {
821 61 : mFrame->GetContent()->GetUncomposedDoc()->UnblockOnload(false);
822 : }
823 :
824 61 : return NS_OK;
825 : }
|