Bumping manifests a=b2g-bump
[gecko.git] / layout / generic / nsImageFrame.cpp
blob31d5183f5f06e86e50af81346dbb61bbaf6d3c6f
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /* rendering object for replaced elements with bitmap image data */
8 #include "nsImageFrame.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/EventStates.h"
12 #include "mozilla/MouseEvents.h"
14 #include "nsCOMPtr.h"
15 #include "nsIImageLoadingContent.h"
16 #include "nsString.h"
17 #include "nsPrintfCString.h"
18 #include "nsPresContext.h"
19 #include "nsRenderingContext.h"
20 #include "nsIPresShell.h"
21 #include "nsGkAtoms.h"
22 #include "nsIDocument.h"
23 #include "nsContentUtils.h"
24 #include "nsCSSAnonBoxes.h"
25 #include "nsStyleContext.h"
26 #include "nsStyleConsts.h"
27 #include "nsStyleCoord.h"
28 #include "nsTransform2D.h"
29 #include "nsImageMap.h"
30 #include "nsIIOService.h"
31 #include "nsILoadGroup.h"
32 #include "nsISupportsPriority.h"
33 #include "nsNetUtil.h"
34 #include "nsCSSRendering.h"
35 #include "nsIDOMHTMLAnchorElement.h"
36 #include "nsNameSpaceManager.h"
37 #include <algorithm>
38 #ifdef ACCESSIBILITY
39 #include "nsAccessibilityService.h"
40 #endif
41 #include "nsIDOMNode.h"
42 #include "nsLayoutUtils.h"
43 #include "nsDisplayList.h"
44 #include "nsIContent.h"
45 #include "nsIDocument.h"
46 #include "FrameLayerBuilder.h"
47 #include "nsISelectionController.h"
48 #include "nsISelection.h"
50 #include "imgIContainer.h"
51 #include "imgLoader.h"
52 #include "imgRequestProxy.h"
54 #include "nsCSSFrameConstructor.h"
55 #include "nsIDOMRange.h"
57 #include "nsError.h"
58 #include "nsBidiUtils.h"
59 #include "nsBidiPresUtils.h"
61 #include "gfxRect.h"
62 #include "ImageLayers.h"
63 #include "ImageContainer.h"
64 #include "nsStyleSet.h"
65 #include "nsBlockFrame.h"
66 #include "nsStyleStructInlines.h"
68 #include "mozilla/Preferences.h"
70 #include "mozilla/dom/Link.h"
72 using namespace mozilla;
74 // sizes (pixels) for image icon, padding and border frame
75 #define ICON_SIZE (16)
76 #define ICON_PADDING (3)
77 #define ALT_BORDER_WIDTH (1)
80 //we must add hooks soon
81 #define IMAGE_EDITOR_CHECK 1
83 // Default alignment value (so we can tell an unset value from a set value)
84 #define ALIGN_UNSET uint8_t(-1)
86 using namespace mozilla::layers;
87 using namespace mozilla::dom;
89 // static icon information
90 nsImageFrame::IconLoad* nsImageFrame::gIconLoad = nullptr;
92 // cached IO service for loading icons
93 nsIIOService* nsImageFrame::sIOService;
95 // test if the width and height are fixed, looking at the style data
96 static bool HaveFixedSize(const nsStylePosition* aStylePosition)
98 // check the width and height values in the reflow state's style struct
99 // - if width and height are specified as either coord or percentage, then
100 // the size of the image frame is constrained
101 return aStylePosition->mWidth.IsCoordPercentCalcUnit() &&
102 aStylePosition->mHeight.IsCoordPercentCalcUnit();
104 // use the data in the reflow state to decide if the image has a constrained size
105 // (i.e. width and height that are based on the containing block size and not the image size)
106 // so we can avoid animated GIF related reflows
107 inline bool HaveFixedSize(const nsHTMLReflowState& aReflowState)
109 NS_ASSERTION(aReflowState.mStylePosition, "crappy reflowState - null stylePosition");
110 // when an image has percent css style height or width, but ComputedHeight()
111 // or ComputedWidth() of reflow state is NS_UNCONSTRAINEDSIZE
112 // it needs to return false to cause an incremental reflow later
113 // if an image is inside table like bug 156731 simple testcase III,
114 // during pass 1 reflow, ComputedWidth() is NS_UNCONSTRAINEDSIZE
115 // in pass 2 reflow, ComputedWidth() is 0, it also needs to return false
116 // see bug 156731
117 const nsStyleCoord &height = aReflowState.mStylePosition->mHeight;
118 const nsStyleCoord &width = aReflowState.mStylePosition->mWidth;
119 return ((height.HasPercent() &&
120 NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight()) ||
121 (width.HasPercent() &&
122 (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedWidth() ||
123 0 == aReflowState.ComputedWidth())))
124 ? false
125 : HaveFixedSize(aReflowState.mStylePosition);
128 nsIFrame*
129 NS_NewImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
131 return new (aPresShell) nsImageFrame(aContext);
134 NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame)
137 nsImageFrame::nsImageFrame(nsStyleContext* aContext) :
138 ImageFrameSuper(aContext),
139 mComputedSize(0, 0),
140 mIntrinsicRatio(0, 0),
141 mDisplayingIcon(false),
142 mFirstFrameComplete(false),
143 mReflowCallbackPosted(false)
145 // We assume our size is not constrained and we haven't gotten an
146 // initial reflow yet, so don't touch those flags.
147 mIntrinsicSize.width.SetCoordValue(0);
148 mIntrinsicSize.height.SetCoordValue(0);
151 nsImageFrame::~nsImageFrame()
155 NS_QUERYFRAME_HEAD(nsImageFrame)
156 NS_QUERYFRAME_ENTRY(nsImageFrame)
157 NS_QUERYFRAME_TAIL_INHERITING(ImageFrameSuper)
159 #ifdef ACCESSIBILITY
160 a11y::AccType
161 nsImageFrame::AccessibleType()
163 // Don't use GetImageMap() to avoid reentrancy into accessibility.
164 if (HasImageMap()) {
165 return a11y::eHTMLImageMapType;
168 return a11y::eImageType;
170 #endif
172 void
173 nsImageFrame::DisconnectMap()
175 if (mImageMap) {
176 mImageMap->Destroy();
177 NS_RELEASE(mImageMap);
179 #ifdef ACCESSIBILITY
180 nsAccessibilityService* accService = GetAccService();
181 if (accService) {
182 accService->RecreateAccessible(PresContext()->PresShell(), mContent);
184 #endif
188 void
189 nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
191 if (mReflowCallbackPosted) {
192 PresContext()->PresShell()->CancelReflowCallback(this);
193 mReflowCallbackPosted = false;
196 // Tell our image map, if there is one, to clean up
197 // This causes the nsImageMap to unregister itself as
198 // a DOM listener.
199 DisconnectMap();
201 // set the frame to null so we don't send messages to a dead object.
202 if (mListener) {
203 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
204 if (imageLoader) {
205 // Notify our image loading content that we are going away so it can
206 // deregister with our refresh driver.
207 imageLoader->FrameDestroyed(this);
209 imageLoader->RemoveObserver(mListener);
212 reinterpret_cast<nsImageListener*>(mListener.get())->SetFrame(nullptr);
215 mListener = nullptr;
217 // If we were displaying an icon, take ourselves off the list
218 if (mDisplayingIcon)
219 gIconLoad->RemoveIconObserver(this);
221 nsSplittableFrame::DestroyFrom(aDestructRoot);
224 void
225 nsImageFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
227 ImageFrameSuper::DidSetStyleContext(aOldStyleContext);
229 if (!mImage) {
230 // We'll pick this change up whenever we do get an image.
231 return;
234 nsStyleImageOrientation newOrientation = StyleVisibility()->mImageOrientation;
236 // We need to update our orientation either if we had no style context before
237 // because this is the first time it's been set, or if the image-orientation
238 // property changed from its previous value.
239 bool shouldUpdateOrientation =
240 !aOldStyleContext ||
241 aOldStyleContext->StyleVisibility()->mImageOrientation != newOrientation;
243 if (shouldUpdateOrientation) {
244 nsCOMPtr<imgIContainer> image(mImage->Unwrap());
245 mImage = nsLayoutUtils::OrientImage(image, newOrientation);
247 UpdateIntrinsicSize(mImage);
248 UpdateIntrinsicRatio(mImage);
252 void
253 nsImageFrame::Init(nsIContent* aContent,
254 nsContainerFrame* aParent,
255 nsIFrame* aPrevInFlow)
257 nsSplittableFrame::Init(aContent, aParent, aPrevInFlow);
259 mListener = new nsImageListener(this);
261 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aContent);
262 if (!imageLoader) {
263 NS_RUNTIMEABORT("Why do we have an nsImageFrame here at all?");
266 imageLoader->AddObserver(mListener);
268 nsPresContext *aPresContext = PresContext();
270 if (!gIconLoad)
271 LoadIcons(aPresContext);
273 // We have a PresContext now, so we need to notify the image content node
274 // that it can register images.
275 imageLoader->FrameCreated(this);
277 // Give image loads associated with an image frame a small priority boost!
278 nsCOMPtr<imgIRequest> currentRequest;
279 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
280 getter_AddRefs(currentRequest));
281 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(currentRequest);
282 if (p)
283 p->AdjustPriority(-1);
285 // If we already have an image container, OnStartContainer won't be called
286 if (currentRequest) {
287 nsCOMPtr<imgIContainer> image;
288 currentRequest->GetImage(getter_AddRefs(image));
289 OnStartContainer(currentRequest, image);
293 bool
294 nsImageFrame::UpdateIntrinsicSize(imgIContainer* aImage)
296 NS_PRECONDITION(aImage, "null image");
297 if (!aImage)
298 return false;
300 IntrinsicSize oldIntrinsicSize = mIntrinsicSize;
301 mIntrinsicSize = IntrinsicSize();
303 // Set intrinsic size to match aImage's reported intrinsic width & height.
304 nsSize intrinsicSize;
305 if (NS_SUCCEEDED(aImage->GetIntrinsicSize(&intrinsicSize))) {
306 // If the image has no intrinsic width, intrinsicSize.width will be -1, and
307 // we can leave mIntrinsicSize.width at its default value of eStyleUnit_None.
308 // Otherwise we use intrinsicSize.width. Height works the same way.
309 if (intrinsicSize.width != -1)
310 mIntrinsicSize.width.SetCoordValue(intrinsicSize.width);
311 if (intrinsicSize.height != -1)
312 mIntrinsicSize.height.SetCoordValue(intrinsicSize.height);
313 } else {
314 // Failure means that the image hasn't loaded enough to report a result. We
315 // treat this case as if the image's intrinsic size was 0x0.
316 mIntrinsicSize.width.SetCoordValue(0);
317 mIntrinsicSize.height.SetCoordValue(0);
320 return mIntrinsicSize != oldIntrinsicSize;
323 bool
324 nsImageFrame::UpdateIntrinsicRatio(imgIContainer* aImage)
326 NS_PRECONDITION(aImage, "null image");
328 if (!aImage)
329 return false;
331 nsSize oldIntrinsicRatio = mIntrinsicRatio;
333 // Set intrinsic ratio to match aImage's reported intrinsic ratio.
334 if (NS_FAILED(aImage->GetIntrinsicRatio(&mIntrinsicRatio)))
335 mIntrinsicRatio.SizeTo(0, 0);
337 return mIntrinsicRatio != oldIntrinsicRatio;
340 bool
341 nsImageFrame::GetSourceToDestTransform(nsTransform2D& aTransform)
343 // Set the translation components.
344 // XXXbz does this introduce rounding errors because of the cast to
345 // float? Should we just manually add that stuff in every time
346 // instead?
347 nsRect innerArea = GetInnerArea();
348 aTransform.SetToTranslate(float(innerArea.x),
349 float(innerArea.y - GetContinuationOffset()));
351 // Set the scale factors.
352 if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
353 mIntrinsicSize.width.GetCoordValue() != 0 &&
354 mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
355 mIntrinsicSize.height.GetCoordValue() != 0 &&
356 mIntrinsicSize.width.GetCoordValue() != mComputedSize.width &&
357 mIntrinsicSize.height.GetCoordValue() != mComputedSize.height) {
359 aTransform.SetScale(float(mComputedSize.width) /
360 float(mIntrinsicSize.width.GetCoordValue()),
361 float(mComputedSize.height) /
362 float(mIntrinsicSize.height.GetCoordValue()));
363 return true;
366 return false;
370 * These two functions basically do the same check. The first one
371 * checks that the given request is the current request for our
372 * mContent. The second checks that the given image container the
373 * same as the image container on the current request for our
374 * mContent.
376 bool
377 nsImageFrame::IsPendingLoad(imgIRequest* aRequest) const
379 // Default to pending load in case of errors
380 nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
381 NS_ASSERTION(imageLoader, "No image loading content?");
383 int32_t requestType = nsIImageLoadingContent::UNKNOWN_REQUEST;
384 imageLoader->GetRequestType(aRequest, &requestType);
386 return requestType != nsIImageLoadingContent::CURRENT_REQUEST;
389 bool
390 nsImageFrame::IsPendingLoad(imgIContainer* aContainer) const
392 // default to pending load in case of errors
393 if (!aContainer) {
394 NS_ERROR("No image container!");
395 return true;
398 nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
399 NS_ASSERTION(imageLoader, "No image loading content?");
401 nsCOMPtr<imgIRequest> currentRequest;
402 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
403 getter_AddRefs(currentRequest));
404 if (!currentRequest) {
405 NS_ERROR("No current request");
406 return true;
409 nsCOMPtr<imgIContainer> currentContainer;
410 currentRequest->GetImage(getter_AddRefs(currentContainer));
412 return currentContainer != aContainer;
416 nsRect
417 nsImageFrame::SourceRectToDest(const nsIntRect& aRect)
419 // When scaling the image, row N of the source image may (depending on
420 // the scaling function) be used to draw any row in the destination image
421 // between floor(F * (N-1)) and ceil(F * (N+1)), where F is the
422 // floating-point scaling factor. The same holds true for columns.
423 // So, we start by computing that bound without the floor and ceiling.
425 nsRect r(nsPresContext::CSSPixelsToAppUnits(aRect.x - 1),
426 nsPresContext::CSSPixelsToAppUnits(aRect.y - 1),
427 nsPresContext::CSSPixelsToAppUnits(aRect.width + 2),
428 nsPresContext::CSSPixelsToAppUnits(aRect.height + 2));
430 nsTransform2D sourceToDest;
431 if (!GetSourceToDestTransform(sourceToDest)) {
432 // Failed to generate transform matrix. Return our whole inner area,
433 // to be on the safe side (since this method is used for generating
434 // invalidation rects).
435 return GetInnerArea();
438 sourceToDest.TransformCoord(&r.x, &r.y, &r.width, &r.height);
440 // Now, round the edges out to the pixel boundary.
441 nscoord scale = nsPresContext::CSSPixelsToAppUnits(1);
442 nscoord right = r.x + r.width;
443 nscoord bottom = r.y + r.height;
445 r.x -= (scale + (r.x % scale)) % scale;
446 r.y -= (scale + (r.y % scale)) % scale;
447 r.width = right + ((scale - (right % scale)) % scale) - r.x;
448 r.height = bottom + ((scale - (bottom % scale)) % scale) - r.y;
450 return r;
453 // Note that we treat NS_EVENT_STATE_SUPPRESSED images as "OK". This means
454 // that we'll construct image frames for them as needed if their display is
455 // toggled from "none" (though we won't paint them, unless their visibility
456 // is changed too).
457 #define BAD_STATES (NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED | \
458 NS_EVENT_STATE_LOADING)
460 // This is a macro so that we don't evaluate the boolean last arg
461 // unless we have to; it can be expensive
462 #define IMAGE_OK(_state, _loadingOK) \
463 (!(_state).HasAtLeastOneOfStates(BAD_STATES) || \
464 (!(_state).HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED) && \
465 (_state).HasState(NS_EVENT_STATE_LOADING) && (_loadingOK)))
467 /* static */
468 bool
469 nsImageFrame::ShouldCreateImageFrameFor(Element* aElement,
470 nsStyleContext* aStyleContext)
472 EventStates state = aElement->State();
473 if (IMAGE_OK(state,
474 HaveFixedSize(aStyleContext->StylePosition()))) {
475 // Image is fine; do the image frame thing
476 return true;
479 // Check if we want to use a placeholder box with an icon or just
480 // let the presShell make us into inline text. Decide as follows:
482 // - if our special "force icons" style is set, show an icon
483 // - else if our "do not show placeholders" pref is set, skip the icon
484 // - else:
485 // - if there is a src attribute, there is no alt attribute,
486 // and this is not an <object> (which could not possibly have
487 // such an attribute), show an icon.
488 // - if QuirksMode, and the IMG has a size show an icon.
489 // - otherwise, skip the icon
490 bool useSizedBox;
492 if (aStyleContext->StyleUIReset()->mForceBrokenImageIcon) {
493 useSizedBox = true;
495 else if (gIconLoad && gIconLoad->mPrefForceInlineAltText) {
496 useSizedBox = false;
498 else if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
499 !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::alt) &&
500 !aElement->IsHTML(nsGkAtoms::object) &&
501 !aElement->IsHTML(nsGkAtoms::input)) {
502 // Use a sized box if we have no alt text. This means no alt attribute
503 // and the node is not an object or an input (since those always have alt
504 // text).
505 useSizedBox = true;
507 else if (aStyleContext->PresContext()->CompatibilityMode() !=
508 eCompatibility_NavQuirks) {
509 useSizedBox = false;
511 else {
512 // check whether we have fixed size
513 useSizedBox = HaveFixedSize(aStyleContext->StylePosition());
516 return useSizedBox;
519 nsresult
520 nsImageFrame::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
522 if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
523 nsCOMPtr<imgIContainer> image;
524 aRequest->GetImage(getter_AddRefs(image));
525 return OnStartContainer(aRequest, image);
528 if (aType == imgINotificationObserver::FRAME_UPDATE) {
529 return OnDataAvailable(aRequest, aData);
532 if (aType == imgINotificationObserver::FRAME_COMPLETE) {
533 mFirstFrameComplete = true;
536 if (aType == imgINotificationObserver::LOAD_COMPLETE) {
537 uint32_t imgStatus;
538 aRequest->GetImageStatus(&imgStatus);
539 nsresult status =
540 imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
541 return OnStopRequest(aRequest, status);
544 return NS_OK;
547 static bool
548 SizeIsAvailable(imgIRequest* aRequest)
550 if (!aRequest)
551 return false;
553 uint32_t imageStatus = 0;
554 nsresult rv = aRequest->GetImageStatus(&imageStatus);
556 return NS_SUCCEEDED(rv) && (imageStatus & imgIRequest::STATUS_SIZE_AVAILABLE);
559 nsresult
560 nsImageFrame::OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage)
562 if (!aImage) return NS_ERROR_INVALID_ARG;
564 /* Get requested animation policy from the pres context:
565 * normal = 0
566 * one frame = 1
567 * one loop = 2
569 nsPresContext *presContext = PresContext();
570 aImage->SetAnimationMode(presContext->ImageAnimationMode());
572 if (IsPendingLoad(aRequest)) {
573 // We don't care
574 return NS_OK;
577 bool intrinsicSizeChanged = false;
578 if (SizeIsAvailable(aRequest)) {
579 // This is valid and for the current request, so update our stored image
580 // container, orienting according to our style.
581 mImage = nsLayoutUtils::OrientImage(aImage, StyleVisibility()->mImageOrientation);
583 intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
584 intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
585 } else {
586 // We no longer have a valid image, so release our stored image container.
587 mImage = nullptr;
589 // Have to size to 0,0 so that GetDesiredSize recalculates the size.
590 mIntrinsicSize.width.SetCoordValue(0);
591 mIntrinsicSize.height.SetCoordValue(0);
592 mIntrinsicRatio.SizeTo(0, 0);
593 intrinsicSizeChanged = true;
596 if (intrinsicSizeChanged && (mState & IMAGE_GOTINITIALREFLOW)) {
597 // Now we need to reflow if we have an unconstrained size and have
598 // already gotten the initial reflow
599 if (!(mState & IMAGE_SIZECONSTRAINED)) {
600 nsIPresShell *presShell = presContext->GetPresShell();
601 NS_ASSERTION(presShell, "No PresShell.");
602 if (presShell) {
603 presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
604 NS_FRAME_IS_DIRTY);
609 return NS_OK;
612 nsresult
613 nsImageFrame::OnDataAvailable(imgIRequest *aRequest,
614 const nsIntRect *aRect)
616 if (mFirstFrameComplete) {
617 nsCOMPtr<imgIContainer> container;
618 aRequest->GetImage(getter_AddRefs(container));
619 return FrameChanged(aRequest, container);
622 // XXX do we need to make sure that the reflow from the
623 // OnStartContainer has been processed before we start calling
624 // invalidate?
626 NS_ENSURE_ARG_POINTER(aRect);
628 if (!(mState & IMAGE_GOTINITIALREFLOW)) {
629 // Don't bother to do anything; we have a reflow coming up!
630 return NS_OK;
633 if (IsPendingLoad(aRequest)) {
634 // We don't care
635 return NS_OK;
638 nsIntRect rect = mImage ? mImage->GetImageSpaceInvalidationRect(*aRect)
639 : *aRect;
641 #ifdef DEBUG_decode
642 printf("Source rect (%d,%d,%d,%d)\n",
643 aRect->x, aRect->y, aRect->width, aRect->height);
644 #endif
646 if (rect.IsEqualInterior(nsIntRect::GetMaxSizedIntRect())) {
647 InvalidateFrame(nsDisplayItem::TYPE_IMAGE);
648 InvalidateFrame(nsDisplayItem::TYPE_ALT_FEEDBACK);
649 } else {
650 nsRect invalid = SourceRectToDest(rect);
651 InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_IMAGE);
652 InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_ALT_FEEDBACK);
655 return NS_OK;
658 nsresult
659 nsImageFrame::OnStopRequest(imgIRequest *aRequest,
660 nsresult aStatus)
662 // Check what request type we're dealing with
663 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
664 NS_ASSERTION(imageLoader, "Who's notifying us??");
665 int32_t loadType = nsIImageLoadingContent::UNKNOWN_REQUEST;
666 imageLoader->GetRequestType(aRequest, &loadType);
667 if (loadType != nsIImageLoadingContent::CURRENT_REQUEST &&
668 loadType != nsIImageLoadingContent::PENDING_REQUEST) {
669 return NS_ERROR_FAILURE;
672 NotifyNewCurrentRequest(aRequest, aStatus);
673 return NS_OK;
676 void
677 nsImageFrame::NotifyNewCurrentRequest(imgIRequest *aRequest,
678 nsresult aStatus)
680 nsCOMPtr<imgIContainer> image;
681 aRequest->GetImage(getter_AddRefs(image));
682 NS_ASSERTION(image || NS_FAILED(aStatus), "Successful load with no container?");
684 // May have to switch sizes here!
685 bool intrinsicSizeChanged = true;
686 if (NS_SUCCEEDED(aStatus) && image && SizeIsAvailable(aRequest)) {
687 // Update our stored image container, orienting according to our style.
688 mImage = nsLayoutUtils::OrientImage(image, StyleVisibility()->mImageOrientation);
690 intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
691 intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
692 } else {
693 // We no longer have a valid image, so release our stored image container.
694 mImage = nullptr;
696 // Have to size to 0,0 so that GetDesiredSize recalculates the size
697 mIntrinsicSize.width.SetCoordValue(0);
698 mIntrinsicSize.height.SetCoordValue(0);
699 mIntrinsicRatio.SizeTo(0, 0);
702 if (mState & IMAGE_GOTINITIALREFLOW) { // do nothing if we haven't gotten the initial reflow yet
703 if (!(mState & IMAGE_SIZECONSTRAINED) && intrinsicSizeChanged) {
704 nsIPresShell *presShell = PresContext()->GetPresShell();
705 if (presShell) {
706 presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
707 NS_FRAME_IS_DIRTY);
710 // Update border+content to account for image change
711 InvalidateFrame();
715 nsresult
716 nsImageFrame::FrameChanged(imgIRequest *aRequest,
717 imgIContainer *aContainer)
719 if (!StyleVisibility()->IsVisible()) {
720 return NS_OK;
723 if (IsPendingLoad(aContainer)) {
724 // We don't care about it
725 return NS_OK;
728 InvalidateLayer(nsDisplayItem::TYPE_IMAGE);
729 return NS_OK;
732 void
733 nsImageFrame::EnsureIntrinsicSizeAndRatio(nsPresContext* aPresContext)
735 // If mIntrinsicSize.width and height are 0, then we need to update from the
736 // image container.
737 if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
738 mIntrinsicSize.width.GetCoordValue() == 0 &&
739 mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
740 mIntrinsicSize.height.GetCoordValue() == 0) {
742 if (mImage) {
743 UpdateIntrinsicSize(mImage);
744 UpdateIntrinsicRatio(mImage);
745 } else {
746 // image request is null or image size not known, probably an
747 // invalid image specified
748 // - make the image big enough for the icon (it may not be
749 // used if inline alt expansion is used instead)
750 if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
751 nscoord edgeLengthToUse =
752 nsPresContext::CSSPixelsToAppUnits(
753 ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH)));
754 mIntrinsicSize.width.SetCoordValue(edgeLengthToUse);
755 mIntrinsicSize.height.SetCoordValue(edgeLengthToUse);
756 mIntrinsicRatio.SizeTo(1, 1);
762 /* virtual */
763 LogicalSize
764 nsImageFrame::ComputeSize(nsRenderingContext *aRenderingContext,
765 WritingMode aWM,
766 const LogicalSize& aCBSize,
767 nscoord aAvailableISize,
768 const LogicalSize& aMargin,
769 const LogicalSize& aBorder,
770 const LogicalSize& aPadding,
771 uint32_t aFlags)
773 nsPresContext *presContext = PresContext();
774 EnsureIntrinsicSizeAndRatio(presContext);
776 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
777 NS_ASSERTION(imageLoader, "No content node??");
778 mozilla::IntrinsicSize intrinsicSize(mIntrinsicSize);
780 // Content may override our default dimensions. This is termed as overriding
781 // the intrinsic size by the spec, but all other consumers of mIntrinsic*
782 // values are being used to refer to the real/true size of the image data.
783 if (imageLoader && mImage &&
784 intrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
785 intrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
786 uint32_t width;
787 uint32_t height;
788 if (NS_SUCCEEDED(imageLoader->GetNaturalWidth(&width)) &&
789 NS_SUCCEEDED(imageLoader->GetNaturalHeight(&height))) {
790 nscoord appWidth = nsPresContext::CSSPixelsToAppUnits((int32_t)width);
791 nscoord appHeight = nsPresContext::CSSPixelsToAppUnits((int32_t)height);
792 // If this image is rotated, we'll need to transpose the natural
793 // width/height.
794 bool coordFlip;
795 if (StyleVisibility()->mImageOrientation.IsFromImage()) {
796 coordFlip = mImage->GetOrientation().SwapsWidthAndHeight();
797 } else {
798 coordFlip = StyleVisibility()->mImageOrientation.SwapsWidthAndHeight();
800 intrinsicSize.width.SetCoordValue(coordFlip ? appHeight : appWidth);
801 intrinsicSize.height.SetCoordValue(coordFlip ? appWidth : appHeight);
805 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(aWM,
806 aRenderingContext, this,
807 intrinsicSize, mIntrinsicRatio,
808 aCBSize,
809 aMargin,
810 aBorder,
811 aPadding);
814 nsRect
815 nsImageFrame::GetInnerArea() const
817 return GetContentRect() - GetPosition();
820 Element*
821 nsImageFrame::GetMapElement() const
823 nsAutoString usemap;
824 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, usemap)) {
825 return mContent->OwnerDoc()->FindImageMap(usemap);
827 return nullptr;
830 // get the offset into the content area of the image where aImg starts if it is a continuation.
831 nscoord
832 nsImageFrame::GetContinuationOffset() const
834 nscoord offset = 0;
835 for (nsIFrame *f = GetPrevInFlow(); f; f = f->GetPrevInFlow()) {
836 offset += f->GetContentRect().height;
838 NS_ASSERTION(offset >= 0, "bogus GetContentRect");
839 return offset;
842 /* virtual */ nscoord
843 nsImageFrame::GetMinISize(nsRenderingContext *aRenderingContext)
845 // XXX The caller doesn't account for constraints of the height,
846 // min-height, and max-height properties.
847 DebugOnly<nscoord> result;
848 DISPLAY_MIN_WIDTH(this, result);
849 nsPresContext *presContext = PresContext();
850 EnsureIntrinsicSizeAndRatio(presContext);
851 return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
852 mIntrinsicSize.width.GetCoordValue() : 0;
855 /* virtual */ nscoord
856 nsImageFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
858 // XXX The caller doesn't account for constraints of the height,
859 // min-height, and max-height properties.
860 DebugOnly<nscoord> result;
861 DISPLAY_PREF_WIDTH(this, result);
862 nsPresContext *presContext = PresContext();
863 EnsureIntrinsicSizeAndRatio(presContext);
864 // convert from normal twips to scaled twips (printing...)
865 return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
866 mIntrinsicSize.width.GetCoordValue() : 0;
869 /* virtual */ IntrinsicSize
870 nsImageFrame::GetIntrinsicSize()
872 return mIntrinsicSize;
875 /* virtual */ nsSize
876 nsImageFrame::GetIntrinsicRatio()
878 return mIntrinsicRatio;
881 void
882 nsImageFrame::Reflow(nsPresContext* aPresContext,
883 nsHTMLReflowMetrics& aMetrics,
884 const nsHTMLReflowState& aReflowState,
885 nsReflowStatus& aStatus)
887 DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
888 DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
889 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
890 ("enter nsImageFrame::Reflow: availSize=%d,%d",
891 aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
893 NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
895 aStatus = NS_FRAME_COMPLETE;
897 // see if we have a frozen size (i.e. a fixed width and height)
898 if (HaveFixedSize(aReflowState)) {
899 mState |= IMAGE_SIZECONSTRAINED;
900 } else {
901 mState &= ~IMAGE_SIZECONSTRAINED;
904 // XXXldb These two bits are almost exact opposites (except in the
905 // middle of the initial reflow); remove IMAGE_GOTINITIALREFLOW.
906 if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
907 mState |= IMAGE_GOTINITIALREFLOW;
910 mComputedSize =
911 nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight());
913 aMetrics.Width() = mComputedSize.width;
914 aMetrics.Height() = mComputedSize.height;
916 // add borders and padding
917 aMetrics.Width() += aReflowState.ComputedPhysicalBorderPadding().LeftRight();
918 aMetrics.Height() += aReflowState.ComputedPhysicalBorderPadding().TopBottom();
920 if (GetPrevInFlow()) {
921 aMetrics.Width() = GetPrevInFlow()->GetSize().width;
922 nscoord y = GetContinuationOffset();
923 aMetrics.Height() -= y + aReflowState.ComputedPhysicalBorderPadding().top;
924 aMetrics.Height() = std::max(0, aMetrics.Height());
928 // we have to split images if we are:
929 // in Paginated mode, we need to have a constrained height, and have a height larger than our available height
930 uint32_t loadStatus = imgIRequest::STATUS_NONE;
931 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
932 NS_ASSERTION(imageLoader, "No content node??");
933 if (imageLoader) {
934 nsCOMPtr<imgIRequest> currentRequest;
935 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
936 getter_AddRefs(currentRequest));
937 if (currentRequest) {
938 currentRequest->GetImageStatus(&loadStatus);
941 if (aPresContext->IsPaginated() &&
942 ((loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) || (mState & IMAGE_SIZECONSTRAINED)) &&
943 NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight() &&
944 aMetrics.Height() > aReflowState.AvailableHeight()) {
945 // our desired height was greater than 0, so to avoid infinite
946 // splitting, use 1 pixel as the min
947 aMetrics.Height() = std::max(nsPresContext::CSSPixelsToAppUnits(1), aReflowState.AvailableHeight());
948 aStatus = NS_FRAME_NOT_COMPLETE;
951 aMetrics.SetOverflowAreasToDesiredBounds();
952 EventStates contentState = mContent->AsElement()->State();
953 bool imageOK = IMAGE_OK(contentState, true);
955 // Determine if the size is available
956 bool haveSize = false;
957 if (loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) {
958 haveSize = true;
961 if (!imageOK || !haveSize) {
962 nsRect altFeedbackSize(0, 0,
963 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)),
964 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)));
965 // We include the altFeedbackSize in our visual overflow, but not in our
966 // scrollable overflow, since it doesn't really need to be scrolled to
967 // outside the image.
968 static_assert(eOverflowType_LENGTH == 2, "Unknown overflow types?");
969 nsRect& visualOverflow = aMetrics.VisualOverflow();
970 visualOverflow.UnionRect(visualOverflow, altFeedbackSize);
972 FinishAndStoreOverflow(&aMetrics);
974 if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && !mReflowCallbackPosted) {
975 nsIPresShell* shell = PresContext()->PresShell();
976 mReflowCallbackPosted = true;
977 shell->PostReflowCallback(this);
980 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
981 ("exit nsImageFrame::Reflow: size=%d,%d",
982 aMetrics.Width(), aMetrics.Height()));
983 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
986 bool
987 nsImageFrame::ReflowFinished()
989 mReflowCallbackPosted = false;
991 nsLayoutUtils::UpdateImageVisibilityForFrame(this);
993 return false;
996 void
997 nsImageFrame::ReflowCallbackCanceled()
999 mReflowCallbackPosted = false;
1002 // Computes the width of the specified string. aMaxWidth specifies the maximum
1003 // width available. Once this limit is reached no more characters are measured.
1004 // The number of characters that fit within the maximum width are returned in
1005 // aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected
1006 // into the rendering context before this is called (for performance). MMP
1007 nscoord
1008 nsImageFrame::MeasureString(const char16_t* aString,
1009 int32_t aLength,
1010 nscoord aMaxWidth,
1011 uint32_t& aMaxFit,
1012 nsRenderingContext& aContext)
1014 nscoord totalWidth = 0;
1015 aContext.SetTextRunRTL(false);
1016 nscoord spaceWidth = aContext.GetWidth(' ');
1018 aMaxFit = 0;
1019 while (aLength > 0) {
1020 // Find the next place we can line break
1021 uint32_t len = aLength;
1022 bool trailingSpace = false;
1023 for (int32_t i = 0; i < aLength; i++) {
1024 if (dom::IsSpaceCharacter(aString[i]) && (i > 0)) {
1025 len = i; // don't include the space when measuring
1026 trailingSpace = true;
1027 break;
1031 // Measure this chunk of text, and see if it fits
1032 nscoord width =
1033 nsLayoutUtils::GetStringWidth(this, &aContext, aString, len);
1034 bool fits = (totalWidth + width) <= aMaxWidth;
1036 // If it fits on the line, or it's the first word we've processed then
1037 // include it
1038 if (fits || (0 == totalWidth)) {
1039 // New piece fits
1040 totalWidth += width;
1042 // If there's a trailing space then see if it fits as well
1043 if (trailingSpace) {
1044 if ((totalWidth + spaceWidth) <= aMaxWidth) {
1045 totalWidth += spaceWidth;
1046 } else {
1047 // Space won't fit. Leave it at the end but don't include it in
1048 // the width
1049 fits = false;
1052 len++;
1055 aMaxFit += len;
1056 aString += len;
1057 aLength -= len;
1060 if (!fits) {
1061 break;
1064 return totalWidth;
1067 // Formats the alt-text to fit within the specified rectangle. Breaks lines
1068 // between words if a word would extend past the edge of the rectangle
1069 void
1070 nsImageFrame::DisplayAltText(nsPresContext* aPresContext,
1071 nsRenderingContext& aRenderingContext,
1072 const nsString& aAltText,
1073 const nsRect& aRect)
1075 // Set font and color
1076 aRenderingContext.SetColor(StyleColor()->mColor);
1077 nsRefPtr<nsFontMetrics> fm;
1078 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
1079 nsLayoutUtils::FontSizeInflationFor(this));
1080 aRenderingContext.SetFont(fm);
1082 // Format the text to display within the formatting rect
1084 nscoord maxAscent = fm->MaxAscent();
1085 nscoord maxDescent = fm->MaxDescent();
1086 nscoord height = fm->MaxHeight();
1088 // XXX It would be nice if there was a way to have the font metrics tell
1089 // use where to break the text given a maximum width. At a minimum we need
1090 // to be able to get the break character...
1091 const char16_t* str = aAltText.get();
1092 int32_t strLen = aAltText.Length();
1093 nscoord y = aRect.y;
1095 if (!aPresContext->BidiEnabled() && HasRTLChars(aAltText)) {
1096 aPresContext->SetBidiEnabled();
1099 // Always show the first line, even if we have to clip it below
1100 bool firstLine = true;
1101 while ((strLen > 0) && (firstLine || (y + maxDescent) < aRect.YMost())) {
1102 // Determine how much of the text to display on this line
1103 uint32_t maxFit; // number of characters that fit
1104 nscoord strWidth = MeasureString(str, strLen, aRect.width, maxFit,
1105 aRenderingContext);
1107 // Display the text
1108 nsresult rv = NS_ERROR_FAILURE;
1110 if (aPresContext->BidiEnabled()) {
1111 const nsStyleVisibility* vis = StyleVisibility();
1112 if (vis->mDirection == NS_STYLE_DIRECTION_RTL)
1113 rv = nsBidiPresUtils::RenderText(str, maxFit, NSBIDI_RTL,
1114 aPresContext, aRenderingContext,
1115 aRenderingContext,
1116 aRect.XMost() - strWidth, y + maxAscent);
1117 else
1118 rv = nsBidiPresUtils::RenderText(str, maxFit, NSBIDI_LTR,
1119 aPresContext, aRenderingContext,
1120 aRenderingContext,
1121 aRect.x, y + maxAscent);
1123 if (NS_FAILED(rv))
1124 aRenderingContext.DrawString(str, maxFit, aRect.x, y + maxAscent);
1126 // Move to the next line
1127 str += maxFit;
1128 strLen -= maxFit;
1129 y += height;
1130 firstLine = false;
1134 struct nsRecessedBorder : public nsStyleBorder {
1135 nsRecessedBorder(nscoord aBorderWidth, nsPresContext* aPresContext)
1136 : nsStyleBorder(aPresContext)
1138 NS_FOR_CSS_SIDES(side) {
1139 // Note: use SetBorderColor here because we want to make sure
1140 // the "special" flags are unset.
1141 SetBorderColor(side, NS_RGB(0, 0, 0));
1142 mBorder.Side(side) = aBorderWidth;
1143 // Note: use SetBorderStyle here because we want to affect
1144 // mComputedBorder
1145 SetBorderStyle(side, NS_STYLE_BORDER_STYLE_INSET);
1150 class nsDisplayAltFeedback : public nsDisplayItem {
1151 public:
1152 nsDisplayAltFeedback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
1153 : nsDisplayItem(aBuilder, aFrame) {}
1155 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
1156 bool* aSnap) MOZ_OVERRIDE
1158 *aSnap = false;
1159 return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
1162 virtual void Paint(nsDisplayListBuilder* aBuilder,
1163 nsRenderingContext* aCtx) MOZ_OVERRIDE
1165 nsImageFrame* f = static_cast<nsImageFrame*>(mFrame);
1166 EventStates state = f->GetContent()->AsElement()->State();
1167 f->DisplayAltFeedback(*aCtx,
1168 mVisibleRect,
1169 IMAGE_OK(state, true)
1170 ? nsImageFrame::gIconLoad->mLoadingImage
1171 : nsImageFrame::gIconLoad->mBrokenImage,
1172 ToReferenceFrame());
1176 NS_DISPLAY_DECL_NAME("AltFeedback", TYPE_ALT_FEEDBACK)
1179 void
1180 nsImageFrame::DisplayAltFeedback(nsRenderingContext& aRenderingContext,
1181 const nsRect& aDirtyRect,
1182 imgIRequest* aRequest,
1183 nsPoint aPt)
1185 // We should definitely have a gIconLoad here.
1186 NS_ABORT_IF_FALSE(gIconLoad, "How did we succeed in Init then?");
1188 // Calculate the inner area
1189 nsRect inner = GetInnerArea() + aPt;
1191 // Display a recessed one pixel border
1192 nscoord borderEdgeWidth = nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH);
1194 // if inner area is empty, then make it big enough for at least the icon
1195 if (inner.IsEmpty()){
1196 inner.SizeTo(2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)),
1197 2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)));
1200 // Make sure we have enough room to actually render the border within
1201 // our frame bounds
1202 if ((inner.width < 2 * borderEdgeWidth) || (inner.height < 2 * borderEdgeWidth)) {
1203 return;
1206 // Paint the border
1207 nsRecessedBorder recessedBorder(borderEdgeWidth, PresContext());
1208 nsCSSRendering::PaintBorderWithStyleBorder(PresContext(), aRenderingContext,
1209 this, inner, inner,
1210 recessedBorder, mStyleContext);
1212 // Adjust the inner rect to account for the one pixel recessed border,
1213 // and a six pixel padding on each edge
1214 inner.Deflate(nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH),
1215 nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH));
1216 if (inner.IsEmpty()) {
1217 return;
1220 // Clip so we don't render outside the inner rect
1221 aRenderingContext.PushState();
1222 aRenderingContext.IntersectClip(inner);
1224 // Check if we should display image placeholders
1225 if (gIconLoad->mPrefShowPlaceholders) {
1226 const nsStyleVisibility* vis = StyleVisibility();
1227 nscoord size = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE);
1229 bool iconUsed = false;
1231 // If we weren't previously displaying an icon, register ourselves
1232 // as an observer for load and animation updates and flag that we're
1233 // doing so now.
1234 if (aRequest && !mDisplayingIcon) {
1235 gIconLoad->AddIconObserver(this);
1236 mDisplayingIcon = true;
1240 // If the icon in question is loaded and decoded, draw it
1241 uint32_t imageStatus = 0;
1242 if (aRequest)
1243 aRequest->GetImageStatus(&imageStatus);
1244 if (imageStatus & imgIRequest::STATUS_FRAME_COMPLETE) {
1245 nsCOMPtr<imgIContainer> imgCon;
1246 aRequest->GetImage(getter_AddRefs(imgCon));
1247 NS_ABORT_IF_FALSE(imgCon, "Frame Complete, but no image container?");
1248 nsRect dest((vis->mDirection == NS_STYLE_DIRECTION_RTL) ?
1249 inner.XMost() - size : inner.x,
1250 inner.y, size, size);
1251 nsLayoutUtils::DrawSingleImage(&aRenderingContext, PresContext(), imgCon,
1252 nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect,
1253 nullptr, imgIContainer::FLAG_NONE);
1254 iconUsed = true;
1257 // if we could not draw the icon, flag that we're waiting for it and
1258 // just draw some graffiti in the mean time
1259 if (!iconUsed) {
1260 nscoord iconXPos = (vis->mDirection == NS_STYLE_DIRECTION_RTL) ?
1261 inner.XMost() - size : inner.x;
1262 nscoord twoPX = nsPresContext::CSSPixelsToAppUnits(2);
1263 aRenderingContext.DrawRect(iconXPos, inner.y,size,size);
1264 aRenderingContext.PushState();
1265 aRenderingContext.SetColor(NS_RGB(0xFF,0,0));
1266 aRenderingContext.FillEllipse(size/2 + iconXPos, size/2 + inner.y,
1267 size/2 - twoPX, size/2 - twoPX);
1268 aRenderingContext.PopState();
1271 // Reduce the inner rect by the width of the icon, and leave an
1272 // additional ICON_PADDING pixels for padding
1273 int32_t iconWidth = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING);
1274 if (vis->mDirection != NS_STYLE_DIRECTION_RTL)
1275 inner.x += iconWidth;
1276 inner.width -= iconWidth;
1279 // If there's still room, display the alt-text
1280 if (!inner.IsEmpty()) {
1281 nsIContent* content = GetContent();
1282 if (content) {
1283 nsXPIDLString altText;
1284 nsCSSFrameConstructor::GetAlternateTextFor(content, content->Tag(),
1285 altText);
1286 DisplayAltText(PresContext(), aRenderingContext, altText, inner);
1290 aRenderingContext.PopState();
1293 #ifdef DEBUG
1294 static void PaintDebugImageMap(nsIFrame* aFrame, nsRenderingContext* aCtx,
1295 const nsRect& aDirtyRect, nsPoint aPt) {
1296 nsImageFrame* f = static_cast<nsImageFrame*>(aFrame);
1297 nsRect inner = f->GetInnerArea() + aPt;
1299 aCtx->SetColor(NS_RGB(0, 0, 0));
1300 aCtx->PushState();
1301 aCtx->Translate(inner.TopLeft());
1302 f->GetImageMap()->Draw(aFrame, *aCtx);
1303 aCtx->PopState();
1305 #endif
1307 void
1308 nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
1309 nsRenderingContext* aCtx) {
1310 uint32_t flags = imgIContainer::FLAG_NONE;
1311 if (aBuilder->ShouldSyncDecodeImages()) {
1312 flags |= imgIContainer::FLAG_SYNC_DECODE;
1314 if (aBuilder->IsPaintingToWindow()) {
1315 flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
1317 static_cast<nsImageFrame*>(mFrame)->
1318 PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mImage, flags);
1321 void
1322 nsDisplayImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
1323 const nsDisplayItemGeometry* aGeometry,
1324 nsRegion* aInvalidRegion)
1326 if (aBuilder->ShouldSyncDecodeImages() && mImage && !mImage->IsDecoded()) {
1327 bool snap;
1328 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
1331 nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
1334 already_AddRefed<ImageContainer>
1335 nsDisplayImage::GetContainer(LayerManager* aManager,
1336 nsDisplayListBuilder* aBuilder)
1338 nsRefPtr<ImageContainer> container;
1339 nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container));
1340 NS_ENSURE_SUCCESS(rv, nullptr);
1341 return container.forget();
1344 gfxRect
1345 nsDisplayImage::GetDestRect()
1347 int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
1348 nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
1350 nsRect dest = imageFrame->GetInnerArea() + ToReferenceFrame();
1351 gfxRect destRect(dest.x, dest.y, dest.width, dest.height);
1352 destRect.ScaleInverse(factor);
1354 return destRect;
1357 LayerState
1358 nsDisplayImage::GetLayerState(nsDisplayListBuilder* aBuilder,
1359 LayerManager* aManager,
1360 const ContainerLayerParameters& aParameters)
1362 bool animated = false;
1363 if (!nsLayoutUtils::AnimatedImageLayersEnabled() ||
1364 mImage->GetType() != imgIContainer::TYPE_RASTER ||
1365 NS_FAILED(mImage->GetAnimated(&animated)) ||
1366 !animated) {
1367 if (!aManager->IsCompositingCheap() ||
1368 !nsLayoutUtils::GPUImageScalingEnabled()) {
1369 return LAYER_NONE;
1373 if (!animated) {
1374 int32_t imageWidth;
1375 int32_t imageHeight;
1376 mImage->GetWidth(&imageWidth);
1377 mImage->GetHeight(&imageHeight);
1379 NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
1381 gfxRect destRect = GetDestRect();
1383 destRect.width *= aParameters.mXScale;
1384 destRect.height *= aParameters.mYScale;
1386 // Calculate the scaling factor for the frame.
1387 gfxSize scale = gfxSize(destRect.width / imageWidth,
1388 destRect.height / imageHeight);
1390 // If we are not scaling at all, no point in separating this into a layer.
1391 if (scale.width == 1.0f && scale.height == 1.0f) {
1392 return LAYER_NONE;
1395 // If the target size is pretty small, no point in using a layer.
1396 if (destRect.width * destRect.height < 64 * 64) {
1397 return LAYER_NONE;
1401 nsRefPtr<ImageContainer> container;
1402 mImage->GetImageContainer(aManager, getter_AddRefs(container));
1403 if (!container) {
1404 return LAYER_NONE;
1407 return LAYER_ACTIVE;
1410 already_AddRefed<Layer>
1411 nsDisplayImage::BuildLayer(nsDisplayListBuilder* aBuilder,
1412 LayerManager* aManager,
1413 const ContainerLayerParameters& aParameters)
1415 nsRefPtr<ImageContainer> container;
1416 nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container));
1417 NS_ENSURE_SUCCESS(rv, nullptr);
1419 nsRefPtr<ImageLayer> layer = static_cast<ImageLayer*>
1420 (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
1421 if (!layer) {
1422 layer = aManager->CreateImageLayer();
1423 if (!layer)
1424 return nullptr;
1426 layer->SetContainer(container);
1427 ConfigureLayer(layer, aParameters.mOffset);
1428 return layer.forget();
1431 void
1432 nsDisplayImage::ConfigureLayer(ImageLayer *aLayer, const nsIntPoint& aOffset)
1434 aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame));
1436 int32_t imageWidth;
1437 int32_t imageHeight;
1438 mImage->GetWidth(&imageWidth);
1439 mImage->GetHeight(&imageHeight);
1441 NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
1443 const gfxRect destRect = GetDestRect();
1445 gfx::Matrix transform;
1446 gfxPoint p = destRect.TopLeft() + aOffset;
1447 transform.Translate(p.x, p.y);
1448 transform.Scale(destRect.Width()/imageWidth,
1449 destRect.Height()/imageHeight);
1450 aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
1453 void
1454 nsImageFrame::PaintImage(nsRenderingContext& aRenderingContext, nsPoint aPt,
1455 const nsRect& aDirtyRect, imgIContainer* aImage,
1456 uint32_t aFlags)
1458 // Render the image into our content area (the area inside
1459 // the borders and padding)
1460 NS_ASSERTION(GetInnerArea().width == mComputedSize.width, "bad width");
1461 nsRect inner = GetInnerArea() + aPt;
1462 nsRect dest(inner.TopLeft(), mComputedSize);
1463 dest.y -= GetContinuationOffset();
1465 nsLayoutUtils::DrawSingleImage(&aRenderingContext, PresContext(), aImage,
1466 nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect,
1467 nullptr, aFlags);
1469 nsImageMap* map = GetImageMap();
1470 if (nullptr != map) {
1471 aRenderingContext.PushState();
1472 aRenderingContext.Translate(inner.TopLeft());
1473 aRenderingContext.SetColor(NS_RGB(255, 255, 255));
1474 aRenderingContext.SetLineStyle(nsLineStyle_kSolid);
1475 map->Draw(this, aRenderingContext);
1476 aRenderingContext.SetColor(NS_RGB(0, 0, 0));
1477 aRenderingContext.SetLineStyle(nsLineStyle_kDotted);
1478 map->Draw(this, aRenderingContext);
1479 aRenderingContext.PopState();
1483 void
1484 nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1485 const nsRect& aDirtyRect,
1486 const nsDisplayListSet& aLists)
1488 if (!IsVisibleForPainting(aBuilder))
1489 return;
1491 DisplayBorderBackgroundOutline(aBuilder, aLists);
1493 DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
1494 clip(aBuilder, this, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT);
1496 if (mComputedSize.width != 0 && mComputedSize.height != 0) {
1497 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
1498 NS_ASSERTION(imageLoader, "Not an image loading content?");
1500 nsCOMPtr<imgIRequest> currentRequest;
1501 if (imageLoader) {
1502 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
1503 getter_AddRefs(currentRequest));
1506 EventStates contentState = mContent->AsElement()->State();
1507 bool imageOK = IMAGE_OK(contentState, true);
1509 // XXX(seth): The SizeIsAvailable check here should not be necessary - the
1510 // intention is that a non-null mImage means we have a size, but there is
1511 // currently some code that violates this invariant.
1512 if (!imageOK || !mImage || !SizeIsAvailable(currentRequest)) {
1513 // No image yet, or image load failed. Draw the alt-text and an icon
1514 // indicating the status
1515 aLists.Content()->AppendNewToTop(new (aBuilder)
1516 nsDisplayAltFeedback(aBuilder, this));
1517 } else {
1518 aLists.Content()->AppendNewToTop(new (aBuilder)
1519 nsDisplayImage(aBuilder, this, mImage));
1521 // If we were previously displaying an icon, we're not anymore
1522 if (mDisplayingIcon) {
1523 gIconLoad->RemoveIconObserver(this);
1524 mDisplayingIcon = false;
1527 #ifdef DEBUG
1528 if (GetShowFrameBorders() && GetImageMap()) {
1529 aLists.Outlines()->AppendNewToTop(new (aBuilder)
1530 nsDisplayGeneric(aBuilder, this, PaintDebugImageMap, "DebugImageMap",
1531 nsDisplayItem::TYPE_DEBUG_IMAGE_MAP));
1533 #endif
1537 if (ShouldDisplaySelection()) {
1538 DisplaySelectionOverlay(aBuilder, aLists.Content(),
1539 nsISelectionDisplay::DISPLAY_IMAGES);
1543 bool
1544 nsImageFrame::ShouldDisplaySelection()
1546 // XXX what on EARTH is this code for?
1547 nsresult result;
1548 nsPresContext* presContext = PresContext();
1549 int16_t displaySelection = presContext->PresShell()->GetSelectionFlags();
1550 if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES))
1551 return false;//no need to check the blue border, we cannot be drawn selected
1552 //insert hook here for image selection drawing
1553 #if IMAGE_EDITOR_CHECK
1554 //check to see if this frame is in an editor context
1555 //isEditor check. this needs to be changed to have better way to check
1556 if (displaySelection == nsISelectionDisplay::DISPLAY_ALL)
1558 nsCOMPtr<nsISelectionController> selCon;
1559 result = GetSelectionController(presContext, getter_AddRefs(selCon));
1560 if (NS_SUCCEEDED(result) && selCon)
1562 nsCOMPtr<nsISelection> selection;
1563 result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
1564 if (NS_SUCCEEDED(result) && selection)
1566 int32_t rangeCount;
1567 selection->GetRangeCount(&rangeCount);
1568 if (rangeCount == 1) //if not one then let code drop to nsFrame::Paint
1570 nsCOMPtr<nsIContent> parentContent = mContent->GetParent();
1571 if (parentContent)
1573 int32_t thisOffset = parentContent->IndexOf(mContent);
1574 nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(parentContent);
1575 nsCOMPtr<nsIDOMNode> rangeNode;
1576 int32_t rangeOffset;
1577 nsCOMPtr<nsIDOMRange> range;
1578 selection->GetRangeAt(0,getter_AddRefs(range));
1579 if (range)
1581 range->GetStartContainer(getter_AddRefs(rangeNode));
1582 range->GetStartOffset(&rangeOffset);
1584 if (parentNode && rangeNode && (rangeNode == parentNode) && rangeOffset == thisOffset)
1586 range->GetEndContainer(getter_AddRefs(rangeNode));
1587 range->GetEndOffset(&rangeOffset);
1588 if ((rangeNode == parentNode) && (rangeOffset == (thisOffset +1))) //+1 since that would mean this whole content is selected only
1589 return false; //do not allow nsFrame do draw any further selection
1597 #endif
1598 return true;
1601 nsImageMap*
1602 nsImageFrame::GetImageMap()
1604 if (!mImageMap) {
1605 nsIContent* map = GetMapElement();
1606 if (map) {
1607 mImageMap = new nsImageMap();
1608 NS_ADDREF(mImageMap);
1609 mImageMap->Init(this, map);
1613 return mImageMap;
1616 bool
1617 nsImageFrame::IsServerImageMap()
1619 return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::ismap);
1622 // Translate an point that is relative to our frame
1623 // into a localized pixel coordinate that is relative to the
1624 // content area of this frame (inside the border+padding).
1625 void
1626 nsImageFrame::TranslateEventCoords(const nsPoint& aPoint,
1627 nsIntPoint& aResult)
1629 nscoord x = aPoint.x;
1630 nscoord y = aPoint.y;
1632 // Subtract out border and padding here so that the coordinates are
1633 // now relative to the content area of this frame.
1634 nsRect inner = GetInnerArea();
1635 x -= inner.x;
1636 y -= inner.y;
1638 aResult.x = nsPresContext::AppUnitsToIntCSSPixels(x);
1639 aResult.y = nsPresContext::AppUnitsToIntCSSPixels(y);
1642 bool
1643 nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget,
1644 nsIContent** aNode)
1646 bool status = false;
1647 aTarget.Truncate();
1648 *aHref = nullptr;
1649 *aNode = nullptr;
1651 // Walk up the content tree, looking for an nsIDOMAnchorElement
1652 for (nsIContent* content = mContent->GetParent();
1653 content; content = content->GetParent()) {
1654 nsCOMPtr<dom::Link> link(do_QueryInterface(content));
1655 if (link) {
1656 nsCOMPtr<nsIURI> href = content->GetHrefURI();
1657 if (href) {
1658 href->Clone(aHref);
1660 status = (*aHref != nullptr);
1662 nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(content));
1663 if (anchor) {
1664 anchor->GetTarget(aTarget);
1666 NS_ADDREF(*aNode = content);
1667 break;
1670 return status;
1673 nsresult
1674 nsImageFrame::GetContentForEvent(WidgetEvent* aEvent,
1675 nsIContent** aContent)
1677 NS_ENSURE_ARG_POINTER(aContent);
1679 nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
1680 if (f != this) {
1681 return f->GetContentForEvent(aEvent, aContent);
1684 // XXX We need to make this special check for area element's capturing the
1685 // mouse due to bug 135040. Remove it once that's fixed.
1686 nsIContent* capturingContent =
1687 aEvent->HasMouseEventMessage() ? nsIPresShell::GetCapturingContent() :
1688 nullptr;
1689 if (capturingContent && capturingContent->GetPrimaryFrame() == this) {
1690 *aContent = capturingContent;
1691 NS_IF_ADDREF(*aContent);
1692 return NS_OK;
1695 nsImageMap* map = GetImageMap();
1697 if (nullptr != map) {
1698 nsIntPoint p;
1699 TranslateEventCoords(
1700 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
1701 nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
1702 if (area) {
1703 area.forget(aContent);
1704 return NS_OK;
1708 *aContent = GetContent();
1709 NS_IF_ADDREF(*aContent);
1710 return NS_OK;
1713 // XXX what should clicks on transparent pixels do?
1714 nsresult
1715 nsImageFrame::HandleEvent(nsPresContext* aPresContext,
1716 WidgetGUIEvent* aEvent,
1717 nsEventStatus* aEventStatus)
1719 NS_ENSURE_ARG_POINTER(aEventStatus);
1721 if ((aEvent->message == NS_MOUSE_BUTTON_UP &&
1722 aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) ||
1723 aEvent->message == NS_MOUSE_MOVE) {
1724 nsImageMap* map = GetImageMap();
1725 bool isServerMap = IsServerImageMap();
1726 if ((nullptr != map) || isServerMap) {
1727 nsIntPoint p;
1728 TranslateEventCoords(
1729 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
1730 bool inside = false;
1731 // Even though client-side image map triggering happens
1732 // through content, we need to make sure we're not inside
1733 // (in case we deal with a case of both client-side and
1734 // sever-side on the same image - it happens!)
1735 if (nullptr != map) {
1736 inside = !!map->GetArea(p.x, p.y);
1739 if (!inside && isServerMap) {
1741 // Server side image maps use the href in a containing anchor
1742 // element to provide the basis for the destination url.
1743 nsCOMPtr<nsIURI> uri;
1744 nsAutoString target;
1745 nsCOMPtr<nsIContent> anchorNode;
1746 if (GetAnchorHREFTargetAndNode(getter_AddRefs(uri), target,
1747 getter_AddRefs(anchorNode))) {
1748 // XXX if the mouse is over/clicked in the border/padding area
1749 // we should probably just pretend nothing happened. Nav4
1750 // keeps the x,y coordinates positive as we do; IE doesn't
1751 // bother. Both of them send the click through even when the
1752 // mouse is over the border.
1753 if (p.x < 0) p.x = 0;
1754 if (p.y < 0) p.y = 0;
1755 nsAutoCString spec;
1756 uri->GetSpec(spec);
1757 spec += nsPrintfCString("?%d,%d", p.x, p.y);
1758 uri->SetSpec(spec);
1760 bool clicked = false;
1761 if (aEvent->message == NS_MOUSE_BUTTON_UP) {
1762 *aEventStatus = nsEventStatus_eConsumeDoDefault;
1763 clicked = true;
1765 nsContentUtils::TriggerLink(anchorNode, aPresContext, uri, target,
1766 clicked, true, true);
1772 return nsSplittableFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
1775 nsresult
1776 nsImageFrame::GetCursor(const nsPoint& aPoint,
1777 nsIFrame::Cursor& aCursor)
1779 nsImageMap* map = GetImageMap();
1780 if (nullptr != map) {
1781 nsIntPoint p;
1782 TranslateEventCoords(aPoint, p);
1783 nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
1784 if (area) {
1785 // Use the cursor from the style of the *area* element.
1786 // XXX Using the image as the parent style context isn't
1787 // technically correct, but it's probably the right thing to do
1788 // here, since it means that areas on which the cursor isn't
1789 // specified will inherit the style from the image.
1790 nsRefPtr<nsStyleContext> areaStyle =
1791 PresContext()->PresShell()->StyleSet()->
1792 ResolveStyleFor(area->AsElement(), StyleContext());
1793 FillCursorInformationFromStyle(areaStyle->StyleUserInterface(),
1794 aCursor);
1795 if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
1796 aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
1798 return NS_OK;
1801 return nsFrame::GetCursor(aPoint, aCursor);
1804 nsresult
1805 nsImageFrame::AttributeChanged(int32_t aNameSpaceID,
1806 nsIAtom* aAttribute,
1807 int32_t aModType)
1809 nsresult rv = nsSplittableFrame::AttributeChanged(aNameSpaceID,
1810 aAttribute, aModType);
1811 if (NS_FAILED(rv)) {
1812 return rv;
1814 if (nsGkAtoms::alt == aAttribute)
1816 PresContext()->PresShell()->FrameNeedsReflow(this,
1817 nsIPresShell::eStyleChange,
1818 NS_FRAME_IS_DIRTY);
1821 return NS_OK;
1824 nsIAtom*
1825 nsImageFrame::GetType() const
1827 return nsGkAtoms::imageFrame;
1830 #ifdef DEBUG_FRAME_DUMP
1831 nsresult
1832 nsImageFrame::GetFrameName(nsAString& aResult) const
1834 return MakeFrameName(NS_LITERAL_STRING("ImageFrame"), aResult);
1837 void
1838 nsImageFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
1840 nsCString str;
1841 ListGeneric(str, aPrefix, aFlags);
1843 // output the img src url
1844 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
1845 if (imageLoader) {
1846 nsCOMPtr<imgIRequest> currentRequest;
1847 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
1848 getter_AddRefs(currentRequest));
1849 if (currentRequest) {
1850 nsCOMPtr<nsIURI> uri;
1851 currentRequest->GetURI(getter_AddRefs(uri));
1852 nsAutoCString uristr;
1853 uri->GetAsciiSpec(uristr);
1854 str += nsPrintfCString(" [src=%s]", uristr.get());
1857 fprintf_stderr(out, "%s\n", str.get());
1859 #endif
1861 nsIFrame::LogicalSides
1862 nsImageFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
1864 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
1865 NS_STYLE_BOX_DECORATION_BREAK_CLONE)) {
1866 return LogicalSides();
1868 LogicalSides skip;
1869 if (nullptr != GetPrevInFlow()) {
1870 skip |= eLogicalSideBitsBStart;
1872 if (nullptr != GetNextInFlow()) {
1873 skip |= eLogicalSideBitsBEnd;
1875 return skip;
1878 nsresult
1879 nsImageFrame::GetIntrinsicImageSize(nsSize& aSize)
1881 if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
1882 mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
1883 aSize.SizeTo(mIntrinsicSize.width.GetCoordValue(),
1884 mIntrinsicSize.height.GetCoordValue());
1885 return NS_OK;
1888 return NS_ERROR_FAILURE;
1891 nsresult
1892 nsImageFrame::LoadIcon(const nsAString& aSpec,
1893 nsPresContext *aPresContext,
1894 imgRequestProxy** aRequest)
1896 nsresult rv = NS_OK;
1897 NS_PRECONDITION(!aSpec.IsEmpty(), "What happened??");
1899 if (!sIOService) {
1900 rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
1901 NS_ENSURE_SUCCESS(rv, rv);
1904 nsCOMPtr<nsIURI> realURI;
1905 SpecToURI(aSpec, sIOService, getter_AddRefs(realURI));
1907 nsRefPtr<imgLoader> il =
1908 nsContentUtils::GetImgLoaderForDocument(aPresContext->Document());
1910 nsCOMPtr<nsILoadGroup> loadGroup;
1911 GetLoadGroup(aPresContext, getter_AddRefs(loadGroup));
1913 // For icon loads, we don't need to merge with the loadgroup flags
1914 nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
1916 return il->LoadImage(realURI, /* icon URI */
1917 nullptr, /* initial document URI; this is only
1918 relevant for cookies, so does not
1919 apply to icons. */
1920 nullptr, /* referrer (not relevant for icons) */
1921 nullptr, /* principal (not relevant for icons) */
1922 loadGroup,
1923 gIconLoad,
1924 nullptr, /* Not associated with any particular document */
1925 loadFlags,
1926 nullptr,
1927 nullptr, /* channel policy not needed */
1928 EmptyString(),
1929 aRequest);
1932 void
1933 nsImageFrame::GetDocumentCharacterSet(nsACString& aCharset) const
1935 if (mContent) {
1936 NS_ASSERTION(mContent->GetComposedDoc(),
1937 "Frame still alive after content removed from document!");
1938 aCharset = mContent->GetComposedDoc()->GetDocumentCharacterSet();
1942 void
1943 nsImageFrame::SpecToURI(const nsAString& aSpec, nsIIOService *aIOService,
1944 nsIURI **aURI)
1946 nsCOMPtr<nsIURI> baseURI;
1947 if (mContent) {
1948 baseURI = mContent->GetBaseURI();
1950 nsAutoCString charset;
1951 GetDocumentCharacterSet(charset);
1952 NS_NewURI(aURI, aSpec,
1953 charset.IsEmpty() ? nullptr : charset.get(),
1954 baseURI, aIOService);
1957 void
1958 nsImageFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
1960 if (!aPresContext)
1961 return;
1963 NS_PRECONDITION(nullptr != aLoadGroup, "null OUT parameter pointer");
1965 nsIPresShell *shell = aPresContext->GetPresShell();
1967 if (!shell)
1968 return;
1970 nsIDocument *doc = shell->GetDocument();
1971 if (!doc)
1972 return;
1974 *aLoadGroup = doc->GetDocumentLoadGroup().take();
1977 nsresult nsImageFrame::LoadIcons(nsPresContext *aPresContext)
1979 NS_ASSERTION(!gIconLoad, "called LoadIcons twice");
1981 NS_NAMED_LITERAL_STRING(loadingSrc,"resource://gre-resources/loading-image.png");
1982 NS_NAMED_LITERAL_STRING(brokenSrc,"resource://gre-resources/broken-image.png");
1984 gIconLoad = new IconLoad();
1985 NS_ADDREF(gIconLoad);
1987 nsresult rv;
1988 // create a loader and load the images
1989 rv = LoadIcon(loadingSrc,
1990 aPresContext,
1991 getter_AddRefs(gIconLoad->mLoadingImage));
1992 if (NS_FAILED(rv)) {
1993 return rv;
1996 rv = LoadIcon(brokenSrc,
1997 aPresContext,
1998 getter_AddRefs(gIconLoad->mBrokenImage));
1999 return rv;
2002 NS_IMPL_ISUPPORTS(nsImageFrame::IconLoad, nsIObserver,
2003 imgINotificationObserver)
2005 static const char* kIconLoadPrefs[] = {
2006 "browser.display.force_inline_alttext",
2007 "browser.display.show_image_placeholders",
2008 nullptr
2011 nsImageFrame::IconLoad::IconLoad()
2013 // register observers
2014 Preferences::AddStrongObservers(this, kIconLoadPrefs);
2015 GetPrefs();
2018 void
2019 nsImageFrame::IconLoad::Shutdown()
2021 Preferences::RemoveObservers(this, kIconLoadPrefs);
2022 // in case the pref service releases us later
2023 if (mLoadingImage) {
2024 mLoadingImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
2025 mLoadingImage = nullptr;
2027 if (mBrokenImage) {
2028 mBrokenImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
2029 mBrokenImage = nullptr;
2033 NS_IMETHODIMP
2034 nsImageFrame::IconLoad::Observe(nsISupports *aSubject, const char* aTopic,
2035 const char16_t* aData)
2037 NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
2038 "wrong topic");
2039 #ifdef DEBUG
2040 // assert |aData| is one of our prefs.
2041 for (uint32_t i = 0; i < ArrayLength(kIconLoadPrefs) ||
2042 (NS_NOTREACHED("wrong pref"), false); ++i)
2043 if (NS_ConvertASCIItoUTF16(kIconLoadPrefs[i]) == nsDependentString(aData))
2044 break;
2045 #endif
2047 GetPrefs();
2048 return NS_OK;
2051 void nsImageFrame::IconLoad::GetPrefs()
2053 mPrefForceInlineAltText =
2054 Preferences::GetBool("browser.display.force_inline_alttext");
2056 mPrefShowPlaceholders =
2057 Preferences::GetBool("browser.display.show_image_placeholders", true);
2060 NS_IMETHODIMP
2061 nsImageFrame::IconLoad::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
2063 if (aType != imgINotificationObserver::LOAD_COMPLETE &&
2064 aType != imgINotificationObserver::FRAME_UPDATE) {
2065 return NS_OK;
2068 nsTObserverArray<nsImageFrame*>::ForwardIterator iter(mIconObservers);
2069 nsImageFrame *frame;
2070 while (iter.HasMore()) {
2071 frame = iter.GetNext();
2072 frame->InvalidateFrame();
2075 return NS_OK;
2078 NS_IMPL_ISUPPORTS(nsImageListener, imgINotificationObserver)
2080 nsImageListener::nsImageListener(nsImageFrame *aFrame) :
2081 mFrame(aFrame)
2085 nsImageListener::~nsImageListener()
2089 NS_IMETHODIMP
2090 nsImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
2092 if (!mFrame)
2093 return NS_ERROR_FAILURE;
2095 return mFrame->Notify(aRequest, aType, aData);
2098 static bool
2099 IsInAutoWidthTableCellForQuirk(nsIFrame *aFrame)
2101 if (eCompatibility_NavQuirks != aFrame->PresContext()->CompatibilityMode())
2102 return false;
2103 // Check if the parent of the closest nsBlockFrame has auto width.
2104 nsBlockFrame *ancestor = nsLayoutUtils::FindNearestBlockAncestor(aFrame);
2105 if (ancestor->StyleContext()->GetPseudo() == nsCSSAnonBoxes::cellContent) {
2106 // Assume direct parent is a table cell frame.
2107 nsFrame *grandAncestor = static_cast<nsFrame*>(ancestor->GetParent());
2108 return grandAncestor &&
2109 grandAncestor->StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto;
2111 return false;
2114 /* virtual */ void
2115 nsImageFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext,
2116 nsIFrame::InlineMinISizeData *aData)
2119 NS_ASSERTION(GetParent(), "Must have a parent if we get here!");
2121 nsIFrame* parent = GetParent();
2122 bool canBreak =
2123 !CanContinueTextRun() &&
2124 parent->StyleText()->WhiteSpaceCanWrap(parent) &&
2125 !IsInAutoWidthTableCellForQuirk(this);
2127 if (canBreak)
2128 aData->OptionallyBreak(aRenderingContext);
2130 aData->trailingWhitespace = 0;
2131 aData->skipWhitespace = false;
2132 aData->trailingTextFrame = nullptr;
2133 aData->currentLine += nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
2134 this, nsLayoutUtils::MIN_ISIZE);
2135 aData->atStartOfLine = false;
2137 if (canBreak)
2138 aData->OptionallyBreak(aRenderingContext);