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 /* rendering object for replaced elements with image data */
7
8 #include "nsImageFrame.h"
9
10 #include "gfx2DGlue.h"
11 #include "gfxUtils.h"
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/EventStates.h"
14 #include "mozilla/gfx/2D.h"
15 #include "mozilla/gfx/Helpers.h"
16 #include "mozilla/gfx/PathHelpers.h"
17 #include "mozilla/MouseEvents.h"
18 #include "mozilla/Unused.h"
19
20 #include "nsCOMPtr.h"
21 #include "nsFontMetrics.h"
22 #include "nsIImageLoadingContent.h"
23 #include "nsString.h"
24 #include "nsPrintfCString.h"
25 #include "nsPresContext.h"
26 #include "nsRenderingContext.h"
27 #include "nsIPresShell.h"
~nsILineIterator()28 #include "nsGkAtoms.h"
29 #include "nsIDocument.h"
30 #include "nsContentUtils.h"
31 #include "nsCSSAnonBoxes.h"
32 #include "nsStyleContext.h"
33 #include "nsStyleConsts.h"
34 #include "nsStyleCoord.h"
35 #include "nsStyleUtil.h"
36 #include "nsTransform2D.h"
37 #include "nsImageMap.h"
38 #include "nsIIOService.h"
39 #include "nsILoadGroup.h"
40 #include "nsISupportsPriority.h"
41 #include "nsNetUtil.h"
42 #include "nsNetCID.h"
43 #include "nsCSSRendering.h"
44 #include "nsIDOMHTMLAnchorElement.h"
45 #include "nsNameSpaceManager.h"
46 #include <algorithm>
47 #ifdef ACCESSIBILITY
48 #include "nsAccessibilityService.h"
49 #endif
50 #include "nsIDOMNode.h"
51 #include "nsLayoutUtils.h"
52 #include "nsDisplayList.h"
53 #include "nsIContent.h"
54 #include "nsIDocument.h"
55 #include "FrameLayerBuilder.h"
56 #include "nsISelectionController.h"
57 #include "nsISelection.h"
58
59 #include "imgIContainer.h"
60 #include "imgLoader.h"
61 #include "imgRequestProxy.h"
62
63 #include "nsCSSFrameConstructor.h"
64 #include "nsIDOMRange.h"
65
66 #include "nsError.h"
67 #include "nsBidiUtils.h"
68 #include "nsBidiPresUtils.h"
69
70 #include "gfxRect.h"
71 #include "ImageLayers.h"
72 #include "ImageContainer.h"
73 #include "mozilla/StyleSetHandle.h"
74 #include "mozilla/StyleSetHandleInlines.h"
75 #include "nsBlockFrame.h"
76 #include "nsStyleStructInlines.h"
77
78 #include "mozilla/Preferences.h"
79
80 #include "mozilla/dom/Link.h"
81
82 using namespace mozilla;
83 using namespace mozilla::dom;
84 using namespace mozilla::gfx;
85 using namespace mozilla::image;
86 using namespace mozilla::layers;
87
88 // sizes (pixels) for image icon, padding and border frame
89 #define ICON_SIZE (16)
90 #define ICON_PADDING (3)
91 #define ALT_BORDER_WIDTH (1)
92
93 //we must add hooks soon
94 #define IMAGE_EDITOR_CHECK 1
95
96 // Default alignment value (so we can tell an unset value from a set value)
97 #define ALIGN_UNSET uint8_t(-1)
nsAutoLineIterator()98
99 // static icon information
100 nsImageFrame::IconLoad* nsImageFrame::gIconLoad = nullptr;
~nsAutoLineIterator()101
102 // cached IO service for loading icons
103 nsIIOService* nsImageFrame::sIOService;
104
105 // test if the width and height are fixed, looking at the style data
106 // This is used by nsImageFrame::ShouldCreateImageFrameFor and should
107 // not be used for layout decisions.
108 static bool HaveSpecifiedSize(const nsStylePosition* aStylePosition)
109 {
110 // check the width and height values in the reflow state's style struct
111 // - if width and height are specified as either coord or percentage, then
112 // the size of the image frame is constrained
113 return aStylePosition->mWidth.IsCoordPercentCalcUnit() &&
114 aStylePosition->mHeight.IsCoordPercentCalcUnit();
115 }
116
117 // Decide whether we can optimize away reflows that result from the
118 // image's intrinsic size changing.
119 inline bool HaveFixedSize(const ReflowInput& aReflowInput)
120 {
121 NS_ASSERTION(aReflowInput.mStylePosition, "crappy reflowInput - null stylePosition");
122 // Don't try to make this optimization when an image has percentages
123 // in its 'width' or 'height'. The percentages might be treated like
124 // auto (especially for intrinsic width calculations and for heights).
125 return aReflowInput.mStylePosition->mHeight.ConvertsToLength() &&
126 aReflowInput.mStylePosition->mWidth.ConvertsToLength();
127 }
128
129 nsIFrame*
130 NS_NewImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
131 {
132 return new (aPresShell) nsImageFrame(aContext);
133 }
134
135 NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame)
136
137
138 nsImageFrame::nsImageFrame(nsStyleContext* aContext) :
139 nsAtomicContainerFrame(aContext),
140 mComputedSize(0, 0),
141 mIntrinsicRatio(0, 0),
142 mDisplayingIcon(false),
143 mFirstFrameComplete(false),
144 mReflowCallbackPosted(false),
145 mForceSyncDecoding(false)
146 {
147 EnableVisibilityTracking();
148
149 // We assume our size is not constrained and we haven't gotten an
150 // initial reflow yet, so don't touch those flags.
151 mIntrinsicSize.width.SetCoordValue(0);
152 mIntrinsicSize.height.SetCoordValue(0);
153 }
154
155 nsImageFrame::~nsImageFrame()
156 {
157 }
158
159 NS_QUERYFRAME_HEAD(nsImageFrame)
160 NS_QUERYFRAME_ENTRY(nsImageFrame)
161 NS_QUERYFRAME_TAIL_INHERITING(nsAtomicContainerFrame)
162
163 #ifdef ACCESSIBILITY
164 a11y::AccType
165 nsImageFrame::AccessibleType()
166 {
167 // Don't use GetImageMap() to avoid reentrancy into accessibility.
168 if (HasImageMap()) {
169 return a11y::eHTMLImageMapType;
170 }
171
172 return a11y::eImageType;
173 }
174 #endif
175
176 void
177 nsImageFrame::DisconnectMap()
178 {
179 if (mImageMap) {
180 mImageMap->Destroy();
181 mImageMap = nullptr;
182
183 #ifdef ACCESSIBILITY
184 nsAccessibilityService* accService = GetAccService();
185 if (accService) {
186 accService->RecreateAccessible(PresContext()->PresShell(), mContent);
187 }
188 #endif
189 }
190 }
191
192 void
193 nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
194 {
195 if (mReflowCallbackPosted) {
196 PresContext()->PresShell()->CancelReflowCallback(this);
197 mReflowCallbackPosted = false;
198 }
199
200 // Tell our image map, if there is one, to clean up
201 // This causes the nsImageMap to unregister itself as
202 // a DOM listener.
203 DisconnectMap();
204
205 // set the frame to null so we don't send messages to a dead object.
206 if (mListener) {
207 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
208 if (imageLoader) {
209 // Notify our image loading content that we are going away so it can
210 // deregister with our refresh driver.
211 imageLoader->FrameDestroyed(this);
212
213 imageLoader->RemoveObserver(mListener);
214 }
215
216 reinterpret_cast<nsImageListener*>(mListener.get())->SetFrame(nullptr);
217 }
218
219 mListener = nullptr;
220
221 // If we were displaying an icon, take ourselves off the list
222 if (mDisplayingIcon)
223 gIconLoad->RemoveIconObserver(this);
224
225 nsAtomicContainerFrame::DestroyFrom(aDestructRoot);
226 }
227
228 void
229 nsImageFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
230 {
231 nsAtomicContainerFrame::DidSetStyleContext(aOldStyleContext);
232
233 if (!mImage) {
234 // We'll pick this change up whenever we do get an image.
235 return;
236 }
237
238 nsStyleImageOrientation newOrientation = StyleVisibility()->mImageOrientation;
239
240 // We need to update our orientation either if we had no style context before
241 // because this is the first time it's been set, or if the image-orientation
242 // property changed from its previous value.
243 bool shouldUpdateOrientation =
244 !aOldStyleContext ||
245 aOldStyleContext->StyleVisibility()->mImageOrientation != newOrientation;
246
247 if (shouldUpdateOrientation) {
248 nsCOMPtr<imgIContainer> image(mImage->Unwrap());
249 mImage = nsLayoutUtils::OrientImage(image, newOrientation);
250
251 UpdateIntrinsicSize(mImage);
252 UpdateIntrinsicRatio(mImage);
253 }
254 }
255
256 void
257 nsImageFrame::Init(nsIContent* aContent,
258 nsContainerFrame* aParent,
259 nsIFrame* aPrevInFlow)
260 {
261 nsAtomicContainerFrame::Init(aContent, aParent, aPrevInFlow);
262
263 mListener = new nsImageListener(this);
264
265 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aContent);
266 if (!imageLoader) {
267 NS_RUNTIMEABORT("Why do we have an nsImageFrame here at all?");
268 }
269
270 imageLoader->AddObserver(mListener);
271
272 nsPresContext *aPresContext = PresContext();
273
274 if (!gIconLoad)
275 LoadIcons(aPresContext);
276
277 // We have a PresContext now, so we need to notify the image content node
278 // that it can register images.
279 imageLoader->FrameCreated(this);
280
281 // Give image loads associated with an image frame a small priority boost!
282 nsCOMPtr<imgIRequest> currentRequest;
283 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
284 getter_AddRefs(currentRequest));
285 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(currentRequest);
286 if (p)
287 p->AdjustPriority(-1);
288 }
289
290 bool
291 nsImageFrame::UpdateIntrinsicSize(imgIContainer* aImage)
292 {
293 NS_PRECONDITION(aImage, "null image");
294 if (!aImage)
295 return false;
296
297 IntrinsicSize oldIntrinsicSize = mIntrinsicSize;
298 mIntrinsicSize = IntrinsicSize();
299
300 // Set intrinsic size to match aImage's reported intrinsic width & height.
301 nsSize intrinsicSize;
302 if (NS_SUCCEEDED(aImage->GetIntrinsicSize(&intrinsicSize))) {
303 // If the image has no intrinsic width, intrinsicSize.width will be -1, and
304 // we can leave mIntrinsicSize.width at its default value of eStyleUnit_None.
305 // Otherwise we use intrinsicSize.width. Height works the same way.
306 if (intrinsicSize.width != -1)
307 mIntrinsicSize.width.SetCoordValue(intrinsicSize.width);
308 if (intrinsicSize.height != -1)
309 mIntrinsicSize.height.SetCoordValue(intrinsicSize.height);
310 } else {
311 // Failure means that the image hasn't loaded enough to report a result. We
312 // treat this case as if the image's intrinsic size was 0x0.
313 mIntrinsicSize.width.SetCoordValue(0);
314 mIntrinsicSize.height.SetCoordValue(0);
315 }
316
317 return mIntrinsicSize != oldIntrinsicSize;
318 }
319
320 bool
321 nsImageFrame::UpdateIntrinsicRatio(imgIContainer* aImage)
322 {
323 NS_PRECONDITION(aImage, "null image");
324
325 if (!aImage)
326 return false;
327
328 nsSize oldIntrinsicRatio = mIntrinsicRatio;
329
330 // Set intrinsic ratio to match aImage's reported intrinsic ratio.
331 if (NS_FAILED(aImage->GetIntrinsicRatio(&mIntrinsicRatio)))
332 mIntrinsicRatio.SizeTo(0, 0);
333
334 return mIntrinsicRatio != oldIntrinsicRatio;
335 }
336
337 bool
338 nsImageFrame::GetSourceToDestTransform(nsTransform2D& aTransform)
339 {
340 // First, figure out destRect (the rect we're rendering into).
341 // NOTE: We use mComputedSize instead of just GetInnerArea()'s own size here,
342 // because GetInnerArea() might be smaller if we're fragmented, whereas
343 // mComputedSize has our full content-box size (which we need for
344 // ComputeObjectDestRect to work correctly).
345 nsRect constraintRect(GetInnerArea().TopLeft(), mComputedSize);
346 constraintRect.y -= GetContinuationOffset();
347
348 nsRect destRect = nsLayoutUtils::ComputeObjectDestRect(constraintRect,
349 mIntrinsicSize,
350 mIntrinsicRatio,
351 StylePosition());
352 // Set the translation components, based on destRect
353 // XXXbz does this introduce rounding errors because of the cast to
354 // float? Should we just manually add that stuff in every time
355 // instead?
356 aTransform.SetToTranslate(float(destRect.x),
357 float(destRect.y));
358
359 // Set the scale factors, based on destRect and intrinsic size.
360 if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
361 mIntrinsicSize.width.GetCoordValue() != 0 &&
362 mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
363 mIntrinsicSize.height.GetCoordValue() != 0 &&
364 mIntrinsicSize.width.GetCoordValue() != destRect.width &&
365 mIntrinsicSize.height.GetCoordValue() != destRect.height) {
366
367 aTransform.SetScale(float(destRect.width) /
368 float(mIntrinsicSize.width.GetCoordValue()),
369 float(destRect.height) /
370 float(mIntrinsicSize.height.GetCoordValue()));
371 return true;
372 }
373
374 return false;
375 }
376
377 // This function checks whether the given request is the current request for our
378 // mContent.
379 bool
380 nsImageFrame::IsPendingLoad(imgIRequest* aRequest) const
381 {
382 // Default to pending load in case of errors
383 nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
384 NS_ASSERTION(imageLoader, "No image loading content?");
385
386 int32_t requestType = nsIImageLoadingContent::UNKNOWN_REQUEST;
387 imageLoader->GetRequestType(aRequest, &requestType);
388
389 return requestType != nsIImageLoadingContent::CURRENT_REQUEST;
390 }
391
392 nsRect
393 nsImageFrame::SourceRectToDest(const nsIntRect& aRect)
394 {
395 // When scaling the image, row N of the source image may (depending on
396 // the scaling function) be used to draw any row in the destination image
397 // between floor(F * (N-1)) and ceil(F * (N+1)), where F is the
398 // floating-point scaling factor. The same holds true for columns.
399 // So, we start by computing that bound without the floor and ceiling.
400
401 nsRect r(nsPresContext::CSSPixelsToAppUnits(aRect.x - 1),
402 nsPresContext::CSSPixelsToAppUnits(aRect.y - 1),
403 nsPresContext::CSSPixelsToAppUnits(aRect.width + 2),
404 nsPresContext::CSSPixelsToAppUnits(aRect.height + 2));
405
406 nsTransform2D sourceToDest;
407 if (!GetSourceToDestTransform(sourceToDest)) {
408 // Failed to generate transform matrix. Return our whole inner area,
409 // to be on the safe side (since this method is used for generating
410 // invalidation rects).
411 return GetInnerArea();
412 }
413
414 sourceToDest.TransformCoord(&r.x, &r.y, &r.width, &r.height);
415
416 // Now, round the edges out to the pixel boundary.
417 nscoord scale = nsPresContext::CSSPixelsToAppUnits(1);
418 nscoord right = r.x + r.width;
419 nscoord bottom = r.y + r.height;
420
421 r.x -= (scale + (r.x % scale)) % scale;
422 r.y -= (scale + (r.y % scale)) % scale;
423 r.width = right + ((scale - (right % scale)) % scale) - r.x;
424 r.height = bottom + ((scale - (bottom % scale)) % scale) - r.y;
425
426 return r;
427 }
428
429 // Note that we treat NS_EVENT_STATE_SUPPRESSED images as "OK". This means
430 // that we'll construct image frames for them as needed if their display is
431 // toggled from "none" (though we won't paint them, unless their visibility
432 // is changed too).
433 #define BAD_STATES (NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED | \
434 NS_EVENT_STATE_LOADING)
435
436 // This is a macro so that we don't evaluate the boolean last arg
437 // unless we have to; it can be expensive
438 #define IMAGE_OK(_state, _loadingOK) \
439 (!(_state).HasAtLeastOneOfStates(BAD_STATES) || \
440 (!(_state).HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED) && \
441 (_state).HasState(NS_EVENT_STATE_LOADING) && (_loadingOK)))
442
443 /* static */
444 bool
445 nsImageFrame::ShouldCreateImageFrameFor(Element* aElement,
446 nsStyleContext* aStyleContext)
447 {
448 EventStates state = aElement->State();
449 if (IMAGE_OK(state,
450 HaveSpecifiedSize(aStyleContext->StylePosition()))) {
451 // Image is fine; do the image frame thing
452 return true;
453 }
454
455 // Check if we want to use a placeholder box with an icon or just
456 // let the presShell make us into inline text. Decide as follows:
457 //
458 // - if our special "force icons" style is set, show an icon
459 // - else if our "do not show placeholders" pref is set, skip the icon
460 // - else:
461 // - if there is a src attribute, there is no alt attribute,
462 // and this is not an <object> (which could not possibly have
463 // such an attribute), show an icon.
464 // - if QuirksMode, and the IMG has a size show an icon.
465 // - otherwise, skip the icon
466 bool useSizedBox;
467
468 if (aStyleContext->StyleUIReset()->mForceBrokenImageIcon) {
469 useSizedBox = true;
470 }
471 else if (gIconLoad && gIconLoad->mPrefForceInlineAltText) {
472 useSizedBox = false;
473 }
474 else if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
475 !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::alt) &&
476 !aElement->IsHTMLElement(nsGkAtoms::object) &&
477 !aElement->IsHTMLElement(nsGkAtoms::input)) {
478 // Use a sized box if we have no alt text. This means no alt attribute
479 // and the node is not an object or an input (since those always have alt
480 // text).
481 useSizedBox = true;
482 }
483 else if (aStyleContext->PresContext()->CompatibilityMode() !=
484 eCompatibility_NavQuirks) {
485 useSizedBox = false;
486 }
487 else {
488 // check whether we have specified size
489 useSizedBox = HaveSpecifiedSize(aStyleContext->StylePosition());
490 }
491
492 return useSizedBox;
493 }
494
495 nsresult
496 nsImageFrame::Notify(imgIRequest* aRequest,
497 int32_t aType,
498 const nsIntRect* aRect)
499 {
500 if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
501 nsCOMPtr<imgIContainer> image;
502 aRequest->GetImage(getter_AddRefs(image));
503 return OnSizeAvailable(aRequest, image);
504 }
505
506 if (aType == imgINotificationObserver::FRAME_UPDATE) {
507 return OnFrameUpdate(aRequest, aRect);
508 }
509
510 if (aType == imgINotificationObserver::FRAME_COMPLETE) {
511 mFirstFrameComplete = true;
512 }
513
514 if (aType == imgINotificationObserver::LOAD_COMPLETE) {
515 uint32_t imgStatus;
516 aRequest->GetImageStatus(&imgStatus);
517 nsresult status =
518 imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
519 return OnLoadComplete(aRequest, status);
520 }
521
522 return NS_OK;
523 }
524
525 static bool
526 SizeIsAvailable(imgIRequest* aRequest)
527 {
528 if (!aRequest)
529 return false;
530
531 uint32_t imageStatus = 0;
532 nsresult rv = aRequest->GetImageStatus(&imageStatus);
533
534 return NS_SUCCEEDED(rv) && (imageStatus & imgIRequest::STATUS_SIZE_AVAILABLE);
535 }
536
537 nsresult
538 nsImageFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
539 {
540 if (!aImage) return NS_ERROR_INVALID_ARG;
541
542 /* Get requested animation policy from the pres context:
543 * normal = 0
544 * one frame = 1
545 * one loop = 2
546 */
547 nsPresContext *presContext = PresContext();
548 aImage->SetAnimationMode(presContext->ImageAnimationMode());
549
550 if (IsPendingLoad(aRequest)) {
551 // We don't care
552 return NS_OK;
553 }
554
555 bool intrinsicSizeChanged = false;
556 if (SizeIsAvailable(aRequest)) {
557 // This is valid and for the current request, so update our stored image
558 // container, orienting according to our style.
559 mImage = nsLayoutUtils::OrientImage(aImage, StyleVisibility()->mImageOrientation);
560
561 intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
562 intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
563 } else {
564 // We no longer have a valid image, so release our stored image container.
565 mImage = mPrevImage = nullptr;
566
567 // Have to size to 0,0 so that GetDesiredSize recalculates the size.
568 mIntrinsicSize.width.SetCoordValue(0);
569 mIntrinsicSize.height.SetCoordValue(0);
570 mIntrinsicRatio.SizeTo(0, 0);
571 intrinsicSizeChanged = true;
572 }
573
574 if (intrinsicSizeChanged && (mState & IMAGE_GOTINITIALREFLOW)) {
575 // Now we need to reflow if we have an unconstrained size and have
576 // already gotten the initial reflow
577 if (!(mState & IMAGE_SIZECONSTRAINED)) {
578 nsIPresShell *presShell = presContext->GetPresShell();
579 NS_ASSERTION(presShell, "No PresShell.");
580 if (presShell) {
581 presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
582 NS_FRAME_IS_DIRTY);
583 }
584 } else {
585 // We've already gotten the initial reflow, and our size hasn't changed,
586 // so we're ready to request a decode.
587 MaybeDecodeForPredictedSize();
588 }
589
590 mPrevImage = nullptr;
591 }
592
593 return NS_OK;
594 }
595
596 nsresult
597 nsImageFrame::OnFrameUpdate(imgIRequest* aRequest, const nsIntRect* aRect)
598 {
599 NS_ENSURE_ARG_POINTER(aRect);
600
601 if (!(mState & IMAGE_GOTINITIALREFLOW)) {
602 // Don't bother to do anything; we have a reflow coming up!
603 return NS_OK;
604 }
605
606 if (mFirstFrameComplete && !StyleVisibility()->IsVisible()) {
607 return NS_OK;
608 }
609
610 if (IsPendingLoad(aRequest)) {
611 // We don't care
612 return NS_OK;
613 }
614
615 nsIntRect layerInvalidRect = mImage
616 ? mImage->GetImageSpaceInvalidationRect(*aRect)
617 : *aRect;
618
619 if (layerInvalidRect.IsEqualInterior(GetMaxSizedIntRect())) {
620 // Invalidate our entire area.
621 InvalidateSelf(nullptr, nullptr);
622 return NS_OK;
623 }
624
625 nsRect frameInvalidRect = SourceRectToDest(layerInvalidRect);
626 InvalidateSelf(&layerInvalidRect, &frameInvalidRect);
627 return NS_OK;
628 }
629
630 void
631 nsImageFrame::InvalidateSelf(const nsIntRect* aLayerInvalidRect,
632 const nsRect* aFrameInvalidRect)
633 {
634 InvalidateLayer(nsDisplayItem::TYPE_IMAGE,
635 aLayerInvalidRect,
636 aFrameInvalidRect);
637
638 if (!mFirstFrameComplete) {
639 InvalidateLayer(nsDisplayItem::TYPE_ALT_FEEDBACK,
640 aLayerInvalidRect,
641 aFrameInvalidRect);
642 }
643 }
644
645 nsresult
646 nsImageFrame::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus)
647 {
648 // Check what request type we're dealing with
649 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
650 NS_ASSERTION(imageLoader, "Who's notifying us??");
651 int32_t loadType = nsIImageLoadingContent::UNKNOWN_REQUEST;
652 imageLoader->GetRequestType(aRequest, &loadType);
653 if (loadType != nsIImageLoadingContent::CURRENT_REQUEST &&
654 loadType != nsIImageLoadingContent::PENDING_REQUEST) {
655 return NS_ERROR_FAILURE;
656 }
657
658 NotifyNewCurrentRequest(aRequest, aStatus);
659 return NS_OK;
660 }
661
662 void
663 nsImageFrame::NotifyNewCurrentRequest(imgIRequest *aRequest,
664 nsresult aStatus)
665 {
666 nsCOMPtr<imgIContainer> image;
667 aRequest->GetImage(getter_AddRefs(image));
668 NS_ASSERTION(image || NS_FAILED(aStatus), "Successful load with no container?");
669
670 // May have to switch sizes here!
671 bool intrinsicSizeChanged = true;
672 if (NS_SUCCEEDED(aStatus) && image && SizeIsAvailable(aRequest)) {
673 // Update our stored image container, orienting according to our style.
674 mImage = nsLayoutUtils::OrientImage(image, StyleVisibility()->mImageOrientation);
675
676 intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
677 intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
678 } else {
679 // We no longer have a valid image, so release our stored image container.
680 mImage = mPrevImage = nullptr;
681
682 // Have to size to 0,0 so that GetDesiredSize recalculates the size
683 mIntrinsicSize.width.SetCoordValue(0);
684 mIntrinsicSize.height.SetCoordValue(0);
685 mIntrinsicRatio.SizeTo(0, 0);
686 }
687
688 if (mState & IMAGE_GOTINITIALREFLOW) { // do nothing if we haven't gotten the initial reflow yet
689 if (intrinsicSizeChanged) {
690 if (!(mState & IMAGE_SIZECONSTRAINED)) {
691 nsIPresShell *presShell = PresContext()->GetPresShell();
692 if (presShell) {
693 presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
694 NS_FRAME_IS_DIRTY);
695 }
696 } else {
697 // We've already gotten the initial reflow, and our size hasn't changed,
698 // so we're ready to request a decode.
699 MaybeDecodeForPredictedSize();
700 }
701
702 mPrevImage = nullptr;
703 }
704 // Update border+content to account for image change
705 InvalidateFrame();
706 }
707 }
708
709 void
710 nsImageFrame::MaybeDecodeForPredictedSize()
711 {
712 // Check that we're ready to decode.
713 if (!mImage) {
714 return; // Nothing to do yet.
715 }
716
717 if (mComputedSize.IsEmpty()) {
718 return; // We won't draw anything, so no point in decoding.
719 }
720
721 if (GetVisibility() != Visibility::APPROXIMATELY_VISIBLE) {
722 return; // We're not visible, so don't decode.
723 }
724
725 // OK, we're ready to decode. Compute the scale to the screen...
726 nsIPresShell* presShell = PresContext()->GetPresShell();
727 LayoutDeviceToScreenScale2D resolutionToScreen(
728 presShell->GetCumulativeResolution()
729 * nsLayoutUtils::GetTransformToAncestorScaleExcludingAnimated(this));
730
731 // ...and this frame's content box...
732 const nsPoint offset =
733 GetOffsetToCrossDoc(nsLayoutUtils::GetReferenceFrame(this));
734 const nsRect frameContentBox = GetInnerArea() + offset;
735
736 // ...and our predicted dest rect...
737 const int32_t factor = PresContext()->AppUnitsPerDevPixel();
738 const LayoutDeviceRect destRect =
739 LayoutDeviceRect::FromAppUnits(PredictedDestRect(frameContentBox), factor);
740
741 // ...and use them to compute our predicted size in screen pixels.
742 const ScreenSize predictedScreenSize = destRect.Size() * resolutionToScreen;
743 const ScreenIntSize predictedScreenIntSize = RoundedToInt(predictedScreenSize);
744 if (predictedScreenIntSize.IsEmpty()) {
745 return;
746 }
747
748 // Determine the optimal image size to use.
749 uint32_t flags = imgIContainer::FLAG_HIGH_QUALITY_SCALING
750 | imgIContainer::FLAG_ASYNC_NOTIFY;
751 SamplingFilter samplingFilter =
752 nsLayoutUtils::GetSamplingFilterForFrame(this);
753 gfxSize gfxPredictedScreenSize = gfxSize(predictedScreenIntSize.width,
754 predictedScreenIntSize.height);
755 nsIntSize predictedImageSize =
756 mImage->OptimalImageSizeForDest(gfxPredictedScreenSize,
757 imgIContainer::FRAME_CURRENT,
758 samplingFilter, flags);
759
760 // Request a decode.
761 mImage->RequestDecodeForSize(predictedImageSize, flags);
762 }
763
764 nsRect
765 nsImageFrame::PredictedDestRect(const nsRect& aFrameContentBox)
766 {
767 // Note: To get the "dest rect", we have to provide the "constraint rect"
768 // (which is the content-box, with the effects of fragmentation undone).
769 nsRect constraintRect(aFrameContentBox.TopLeft(), mComputedSize);
770 constraintRect.y -= GetContinuationOffset();
771
772 return nsLayoutUtils::ComputeObjectDestRect(constraintRect,
773 mIntrinsicSize,
774 mIntrinsicRatio,
775 StylePosition());
776 }
777
778 void
779 nsImageFrame::EnsureIntrinsicSizeAndRatio()
780 {
781 // If mIntrinsicSize.width and height are 0, then we need to update from the
782 // image container.
783 if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
784 mIntrinsicSize.width.GetCoordValue() == 0 &&
785 mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
786 mIntrinsicSize.height.GetCoordValue() == 0) {
787
788 if (mImage) {
789 UpdateIntrinsicSize(mImage);
790 UpdateIntrinsicRatio(mImage);
791 } else {
792 // image request is null or image size not known, probably an
793 // invalid image specified
794 if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
795 bool imageBroken = false;
796 // check for broken images. valid null images (eg. img src="") are
797 // not considered broken because they have no image requests
798 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
799 if (imageLoader) {
800 nsCOMPtr<imgIRequest> currentRequest;
801 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
802 getter_AddRefs(currentRequest));
803 uint32_t imageStatus;
804 imageBroken =
805 currentRequest &&
806 NS_SUCCEEDED(currentRequest->GetImageStatus(&imageStatus)) &&
807 (imageStatus & imgIRequest::STATUS_ERROR);
808 }
809 // invalid image specified. make the image big enough for the "broken" icon
810 if (imageBroken) {
811 nscoord edgeLengthToUse =
812 nsPresContext::CSSPixelsToAppUnits(
813 ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH)));
814 mIntrinsicSize.width.SetCoordValue(edgeLengthToUse);
815 mIntrinsicSize.height.SetCoordValue(edgeLengthToUse);
816 mIntrinsicRatio.SizeTo(1, 1);
817 }
818 }
819 }
820 }
821 }
822
823 /* virtual */
824 LogicalSize
825 nsImageFrame::ComputeSize(nsRenderingContext *aRenderingContext,
826 WritingMode aWM,
827 const LogicalSize& aCBSize,
828 nscoord aAvailableISize,
829 const LogicalSize& aMargin,
830 const LogicalSize& aBorder,
831 const LogicalSize& aPadding,
832 ComputeSizeFlags aFlags)
833 {
834 EnsureIntrinsicSizeAndRatio();
835
836 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
837 NS_ASSERTION(imageLoader, "No content node??");
838 mozilla::IntrinsicSize intrinsicSize(mIntrinsicSize);
839
840 // XXX(seth): We may sometimes find ourselves in the situation where we have
841 // mImage, but imageLoader's current request does not have a size yet.
842 // This can happen when we load an image speculatively from cache, it fails
843 // to validate, and the new image load hasn't fired SIZE_AVAILABLE yet. In
844 // this situation we should always use mIntrinsicSize, because
845 // GetNaturalWidth/Height will return 0, so we check CurrentRequestHasSize()
846 // below. See bug 1019840. We will fix this in bug 1141395.
847
848 // Content may override our default dimensions. This is termed as overriding
849 // the intrinsic size by the spec, but all other consumers of mIntrinsic*
850 // values are being used to refer to the real/true size of the image data.
851 if (imageLoader && imageLoader->CurrentRequestHasSize() && mImage &&
852 intrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
853 intrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
854 uint32_t width;
855 uint32_t height;
856 if (NS_SUCCEEDED(imageLoader->GetNaturalWidth(&width)) &&
857 NS_SUCCEEDED(imageLoader->GetNaturalHeight(&height))) {
858 nscoord appWidth = nsPresContext::CSSPixelsToAppUnits((int32_t)width);
859 nscoord appHeight = nsPresContext::CSSPixelsToAppUnits((int32_t)height);
860 // If this image is rotated, we'll need to transpose the natural
861 // width/height.
862 bool coordFlip;
863 if (StyleVisibility()->mImageOrientation.IsFromImage()) {
864 coordFlip = mImage->GetOrientation().SwapsWidthAndHeight();
865 } else {
866 coordFlip = StyleVisibility()->mImageOrientation.SwapsWidthAndHeight();
867 }
868 intrinsicSize.width.SetCoordValue(coordFlip ? appHeight : appWidth);
869 intrinsicSize.height.SetCoordValue(coordFlip ? appWidth : appHeight);
870 }
871 }
872
873 return ComputeSizeWithIntrinsicDimensions(aRenderingContext, aWM,
874 intrinsicSize, mIntrinsicRatio,
875 aCBSize, aMargin, aBorder, aPadding,
876 aFlags);
877 }
878
879 // XXXdholbert This function's clients should probably just be calling
880 // GetContentRectRelativeToSelf() directly.
881 nsRect
882 nsImageFrame::GetInnerArea() const
883 {
884 return GetContentRectRelativeToSelf();
885 }
886
887 Element*
888 nsImageFrame::GetMapElement() const
889 {
890 nsAutoString usemap;
891 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, usemap)) {
892 return mContent->OwnerDoc()->FindImageMap(usemap);
893 }
894 return nullptr;
895 }
896
897 // get the offset into the content area of the image where aImg starts if it is a continuation.
898 nscoord
899 nsImageFrame::GetContinuationOffset() const
900 {
901 nscoord offset = 0;
902 for (nsIFrame *f = GetPrevInFlow(); f; f = f->GetPrevInFlow()) {
903 offset += f->GetContentRect().height;
904 }
905 NS_ASSERTION(offset >= 0, "bogus GetContentRect");
906 return offset;
907 }
908
909 /* virtual */ nscoord
910 nsImageFrame::GetMinISize(nsRenderingContext *aRenderingContext)
911 {
912 // XXX The caller doesn't account for constraints of the height,
913 // min-height, and max-height properties.
914 DebugOnly<nscoord> result;
915 DISPLAY_MIN_WIDTH(this, result);
916 EnsureIntrinsicSizeAndRatio();
917 return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
918 mIntrinsicSize.width.GetCoordValue() : 0;
919 }
920
921 /* virtual */ nscoord
922 nsImageFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
923 {
924 // XXX The caller doesn't account for constraints of the height,
925 // min-height, and max-height properties.
926 DebugOnly<nscoord> result;
927 DISPLAY_PREF_WIDTH(this, result);
928 EnsureIntrinsicSizeAndRatio();
929 // convert from normal twips to scaled twips (printing...)
930 return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
931 mIntrinsicSize.width.GetCoordValue() : 0;
932 }
933
934 /* virtual */ IntrinsicSize
935 nsImageFrame::GetIntrinsicSize()
936 {
937 return mIntrinsicSize;
938 }
939
940 /* virtual */ nsSize
941 nsImageFrame::GetIntrinsicRatio()
942 {
943 return mIntrinsicRatio;
944 }
945
946 void
947 nsImageFrame::Reflow(nsPresContext* aPresContext,
948 ReflowOutput& aMetrics,
949 const ReflowInput& aReflowInput,
950 nsReflowStatus& aStatus)
951 {
952 MarkInReflow();
953 DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
954 DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
955 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
956 ("enter nsImageFrame::Reflow: availSize=%d,%d",
957 aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
958
959 NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
960
961 aStatus = NS_FRAME_COMPLETE;
962
963 // see if we have a frozen size (i.e. a fixed width and height)
964 if (HaveFixedSize(aReflowInput)) {
965 mState |= IMAGE_SIZECONSTRAINED;
966 } else {
967 mState &= ~IMAGE_SIZECONSTRAINED;
968 }
969
970 // XXXldb These two bits are almost exact opposites (except in the
971 // middle of the initial reflow); remove IMAGE_GOTINITIALREFLOW.
972 if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
973 mState |= IMAGE_GOTINITIALREFLOW;
974 }
975
976 mComputedSize =
977 nsSize(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight());
978
979 aMetrics.Width() = mComputedSize.width;
980 aMetrics.Height() = mComputedSize.height;
981
982 // add borders and padding
983 aMetrics.Width() += aReflowInput.ComputedPhysicalBorderPadding().LeftRight();
984 aMetrics.Height() += aReflowInput.ComputedPhysicalBorderPadding().TopBottom();
985
986 if (GetPrevInFlow()) {
987 aMetrics.Width() = GetPrevInFlow()->GetSize().width;
988 nscoord y = GetContinuationOffset();
989 aMetrics.Height() -= y + aReflowInput.ComputedPhysicalBorderPadding().top;
990 aMetrics.Height() = std::max(0, aMetrics.Height());
991 }
992
993
994 // we have to split images if we are:
995 // in Paginated mode, we need to have a constrained height, and have a height larger than our available height
996 uint32_t loadStatus = imgIRequest::STATUS_NONE;
997 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
998 NS_ASSERTION(imageLoader, "No content node??");
999 if (imageLoader) {
1000 nsCOMPtr<imgIRequest> currentRequest;
1001 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
1002 getter_AddRefs(currentRequest));
1003 if (currentRequest) {
1004 currentRequest->GetImageStatus(&loadStatus);
1005 }
1006 }
1007 if (aPresContext->IsPaginated() &&
1008 ((loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) || (mState & IMAGE_SIZECONSTRAINED)) &&
1009 NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableHeight() &&
1010 aMetrics.Height() > aReflowInput.AvailableHeight()) {
1011 // our desired height was greater than 0, so to avoid infinite
1012 // splitting, use 1 pixel as the min
1013 aMetrics.Height() = std::max(nsPresContext::CSSPixelsToAppUnits(1), aReflowInput.AvailableHeight());
1014 aStatus = NS_FRAME_NOT_COMPLETE;
1015 }
1016
1017 aMetrics.SetOverflowAreasToDesiredBounds();
1018 EventStates contentState = mContent->AsElement()->State();
1019 bool imageOK = IMAGE_OK(contentState, true);
1020
1021 // Determine if the size is available
1022 bool haveSize = false;
1023 if (loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) {
1024 haveSize = true;
1025 }
1026
1027 if (!imageOK || !haveSize) {
1028 nsRect altFeedbackSize(0, 0,
1029 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)),
1030 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)));
1031 // We include the altFeedbackSize in our visual overflow, but not in our
1032 // scrollable overflow, since it doesn't really need to be scrolled to
1033 // outside the image.
1034 static_assert(eOverflowType_LENGTH == 2, "Unknown overflow types?");
1035 nsRect& visualOverflow = aMetrics.VisualOverflow();
1036 visualOverflow.UnionRect(visualOverflow, altFeedbackSize);
1037 } else {
1038 // We've just reflowed and we should have an accurate size, so we're ready
1039 // to request a decode.
1040 MaybeDecodeForPredictedSize();
1041 }
1042 FinishAndStoreOverflow(&aMetrics);
1043
1044 if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && !mReflowCallbackPosted) {
1045 nsIPresShell* shell = PresContext()->PresShell();
1046 mReflowCallbackPosted = true;
1047 shell->PostReflowCallback(this);
1048 }
1049
1050 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
1051 ("exit nsImageFrame::Reflow: size=%d,%d",
1052 aMetrics.Width(), aMetrics.Height()));
1053 NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
1054 }
1055
1056 bool
1057 nsImageFrame::ReflowFinished()
1058 {
1059 mReflowCallbackPosted = false;
1060
1061 // XXX(seth): We don't need this. The purpose of updating visibility
1062 // synchronously is to ensure that animated images start animating
1063 // immediately. In the short term, however,
1064 // nsImageLoadingContent::OnUnlockedDraw() is enough to ensure that
1065 // animations start as soon as the image is painted for the first time, and in
1066 // the long term we want to update visibility information from the display
1067 // list whenever we paint, so we don't actually need to do this. However, to
1068 // avoid behavior changes during the transition from the old image visibility
1069 // code, we'll leave it in for now.
1070 UpdateVisibilitySynchronously();
1071
1072 return false;
1073 }
1074
1075 void
1076 nsImageFrame::ReflowCallbackCanceled()
1077 {
1078 mReflowCallbackPosted = false;
1079 }
1080
1081 // Computes the width of the specified string. aMaxWidth specifies the maximum
1082 // width available. Once this limit is reached no more characters are measured.
1083 // The number of characters that fit within the maximum width are returned in
1084 // aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected
1085 // into the rendering context before this is called (for performance). MMP
1086 nscoord
1087 nsImageFrame::MeasureString(const char16_t* aString,
1088 int32_t aLength,
1089 nscoord aMaxWidth,
1090 uint32_t& aMaxFit,
1091 nsRenderingContext& aContext,
1092 nsFontMetrics& aFontMetrics)
1093 {
1094 nscoord totalWidth = 0;
1095 aFontMetrics.SetTextRunRTL(false);
1096 nscoord spaceWidth = aFontMetrics.SpaceWidth();
1097
1098 aMaxFit = 0;
1099 while (aLength > 0) {
1100 // Find the next place we can line break
1101 uint32_t len = aLength;
1102 bool trailingSpace = false;
1103 for (int32_t i = 0; i < aLength; i++) {
1104 if (dom::IsSpaceCharacter(aString[i]) && (i > 0)) {
1105 len = i; // don't include the space when measuring
1106 trailingSpace = true;
1107 break;
1108 }
1109 }
1110
1111 // Measure this chunk of text, and see if it fits
1112 nscoord width =
1113 nsLayoutUtils::AppUnitWidthOfStringBidi(aString, len, this, aFontMetrics,
1114 aContext);
1115 bool fits = (totalWidth + width) <= aMaxWidth;
1116
1117 // If it fits on the line, or it's the first word we've processed then
1118 // include it
1119 if (fits || (0 == totalWidth)) {
1120 // New piece fits
1121 totalWidth += width;
1122
1123 // If there's a trailing space then see if it fits as well
1124 if (trailingSpace) {
1125 if ((totalWidth + spaceWidth) <= aMaxWidth) {
1126 totalWidth += spaceWidth;
1127 } else {
1128 // Space won't fit. Leave it at the end but don't include it in
1129 // the width
1130 fits = false;
1131 }
1132
1133 len++;
1134 }
1135
1136 aMaxFit += len;
1137 aString += len;
1138 aLength -= len;
1139 }
1140
1141 if (!fits) {
1142 break;
1143 }
1144 }
1145 return totalWidth;
1146 }
1147
1148 // Formats the alt-text to fit within the specified rectangle. Breaks lines
1149 // between words if a word would extend past the edge of the rectangle
1150 void
1151 nsImageFrame::DisplayAltText(nsPresContext* aPresContext,
1152 nsRenderingContext& aRenderingContext,
1153 const nsString& aAltText,
1154 const nsRect& aRect)
1155 {
1156 // Set font and color
1157 aRenderingContext.ThebesContext()->
1158 SetColor(Color::FromABGR(StyleColor()->mColor));
1159 RefPtr<nsFontMetrics> fm =
1160 nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
1161
1162 // Format the text to display within the formatting rect
1163
1164 nscoord maxAscent = fm->MaxAscent();
1165 nscoord maxDescent = fm->MaxDescent();
1166 nscoord lineHeight = fm->MaxHeight(); // line-relative, so an x-coordinate
1167 // length if writing mode is vertical
1168
1169 WritingMode wm = GetWritingMode();
1170 bool isVertical = wm.IsVertical();
1171
1172 fm->SetVertical(isVertical);
1173 fm->SetTextOrientation(StyleVisibility()->mTextOrientation);
1174
1175 // XXX It would be nice if there was a way to have the font metrics tell
1176 // use where to break the text given a maximum width. At a minimum we need
1177 // to be able to get the break character...
1178 const char16_t* str = aAltText.get();
1179 int32_t strLen = aAltText.Length();
1180 nsPoint pt = wm.IsVerticalRL() ? aRect.TopRight() - nsPoint(lineHeight, 0)
1181 : aRect.TopLeft();
1182 nscoord iSize = isVertical ? aRect.height : aRect.width;
1183
1184 if (!aPresContext->BidiEnabled() && HasRTLChars(aAltText)) {
1185 aPresContext->SetBidiEnabled();
1186 }
1187
1188 // Always show the first line, even if we have to clip it below
1189 bool firstLine = true;
1190 while (strLen > 0) {
1191 if (!firstLine) {
1192 // If we've run out of space, break out of the loop
1193 if ((!isVertical && (pt.y + maxDescent) >= aRect.YMost()) ||
1194 (wm.IsVerticalRL() && (pt.x + maxDescent < aRect.x)) ||
1195 (wm.IsVerticalLR() && (pt.x + maxDescent >= aRect.XMost()))) {
1196 break;
1197 }
1198 }
1199
1200 // Determine how much of the text to display on this line
1201 uint32_t maxFit; // number of characters that fit
1202 nscoord strWidth = MeasureString(str, strLen, iSize, maxFit,
1203 aRenderingContext, *fm);
1204
1205 // Display the text
1206 nsresult rv = NS_ERROR_FAILURE;
1207
1208 if (aPresContext->BidiEnabled()) {
1209 nsBidiDirection dir;
1210 nscoord x, y;
1211
1212 if (isVertical) {
1213 x = pt.x + maxDescent;
1214 if (wm.IsBidiLTR()) {
1215 y = aRect.y;
1216 dir = NSBIDI_LTR;
1217 } else {
1218 y = aRect.YMost() - strWidth;
1219 dir = NSBIDI_RTL;
1220 }
1221 } else {
1222 y = pt.y + maxAscent;
1223 if (wm.IsBidiLTR()) {
1224 x = aRect.x;
1225 dir = NSBIDI_LTR;
1226 } else {
1227 x = aRect.XMost() - strWidth;
1228 dir = NSBIDI_RTL;
1229 }
1230 }
1231
1232 rv = nsBidiPresUtils::RenderText(str, maxFit, dir,
1233 aPresContext, aRenderingContext,
1234 aRenderingContext.GetDrawTarget(),
1235 *fm, x, y);
1236 }
1237 if (NS_FAILED(rv)) {
1238 nsLayoutUtils::DrawUniDirString(str, maxFit,
1239 isVertical
1240 ? nsPoint(pt.x + maxDescent, pt.y)
1241 : nsPoint(pt.x, pt.y + maxAscent),
1242 *fm, aRenderingContext);
1243 }
1244
1245 // Move to the next line
1246 str += maxFit;
1247 strLen -= maxFit;
1248 if (wm.IsVerticalRL()) {
1249 pt.x -= lineHeight;
1250 } else if (wm.IsVerticalLR()) {
1251 pt.x += lineHeight;
1252 } else {
1253 pt.y += lineHeight;
1254 }
1255
1256 firstLine = false;
1257 }
1258 }
1259
1260 struct nsRecessedBorder : public nsStyleBorder {
1261 nsRecessedBorder(nscoord aBorderWidth, nsPresContext* aPresContext)
1262 : nsStyleBorder(aPresContext)
1263 {
1264 NS_FOR_CSS_SIDES(side) {
1265 mBorderColor[side] = StyleComplexColor::FromColor(NS_RGB(0, 0, 0));
1266 mBorder.Side(side) = aBorderWidth;
1267 // Note: use SetBorderStyle here because we want to affect
1268 // mComputedBorder
1269 SetBorderStyle(side, NS_STYLE_BORDER_STYLE_INSET);
1270 }
1271 }
1272 };
1273
1274 class nsDisplayAltFeedback : public nsDisplayItem {
1275 public:
1276 nsDisplayAltFeedback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
1277 : nsDisplayItem(aBuilder, aFrame) {}
1278
1279 virtual nsDisplayItemGeometry*
1280 AllocateGeometry(nsDisplayListBuilder* aBuilder) override
1281 {
1282 return new nsDisplayItemGenericImageGeometry(this, aBuilder);
1283 }
1284
1285 virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
1286 const nsDisplayItemGeometry* aGeometry,
1287 nsRegion* aInvalidRegion) override
1288 {
1289 auto geometry =
1290 static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
1291
1292 if (aBuilder->ShouldSyncDecodeImages() &&
1293 geometry->ShouldInvalidateToSyncDecodeImages()) {
1294 bool snap;
1295 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
1296 }
1297
1298 nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
1299 }
1300
1301 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
1302 bool* aSnap) override
1303 {
1304 *aSnap = false;
1305 return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
1306 }
1307
1308 virtual void Paint(nsDisplayListBuilder* aBuilder,
1309 nsRenderingContext* aCtx) override
1310 {
1311 // Always sync decode, because these icons are UI, and since they're not
1312 // discardable we'll pay the price of sync decoding at most once.
1313 uint32_t flags = imgIContainer::FLAG_SYNC_DECODE;
1314
1315 nsImageFrame* f = static_cast<nsImageFrame*>(mFrame);
1316 DrawResult result =
1317 f->DisplayAltFeedback(*aCtx,
1318 mVisibleRect,
1319 ToReferenceFrame(),
1320 flags);
1321
1322 nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
1323 }
1324
1325 NS_DISPLAY_DECL_NAME("AltFeedback", TYPE_ALT_FEEDBACK)
1326 };
1327
1328 DrawResult
1329 nsImageFrame::DisplayAltFeedback(nsRenderingContext& aRenderingContext,
1330 const nsRect& aDirtyRect,
1331 nsPoint aPt,
1332 uint32_t aFlags)
1333 {
1334 // We should definitely have a gIconLoad here.
1335 MOZ_ASSERT(gIconLoad, "How did we succeed in Init then?");
1336
1337 // Whether we draw the broken or loading icon.
1338 bool isLoading = IMAGE_OK(GetContent()->AsElement()->State(), true);
1339
1340 // Calculate the inner area
1341 nsRect inner = GetInnerArea() + aPt;
1342
1343 // Display a recessed one pixel border
1344 nscoord borderEdgeWidth = nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH);
1345
1346 // if inner area is empty, then make it big enough for at least the icon
1347 if (inner.IsEmpty()){
1348 inner.SizeTo(2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)),
1349 2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)));
1350 }
1351
1352 // Make sure we have enough room to actually render the border within
1353 // our frame bounds
1354 if ((inner.width < 2 * borderEdgeWidth) || (inner.height < 2 * borderEdgeWidth)) {
1355 return DrawResult::SUCCESS;
1356 }
1357
1358 // Paint the border
1359 if (!isLoading || gIconLoad->mPrefShowLoadingPlaceholder) {
1360 nsRecessedBorder recessedBorder(borderEdgeWidth, PresContext());
1361
1362 // Assert that we're not drawing a border-image here; if we were, we
1363 // couldn't ignore the DrawResult that PaintBorderWithStyleBorder returns.
1364 MOZ_ASSERT(recessedBorder.mBorderImageSource.GetType() == eStyleImageType_Null);
1365
1366 Unused <<
1367 nsCSSRendering::PaintBorderWithStyleBorder(PresContext(), aRenderingContext,
1368 this, inner, inner,
1369 recessedBorder, mStyleContext,
1370 PaintBorderFlags::SYNC_DECODE_IMAGES);
1371 }
1372
1373 // Adjust the inner rect to account for the one pixel recessed border,
1374 // and a six pixel padding on each edge
1375 inner.Deflate(nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH),
1376 nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH));
1377 if (inner.IsEmpty()) {
1378 return DrawResult::SUCCESS;
1379 }
1380
1381 DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
1382 gfxContext* gfx = aRenderingContext.ThebesContext();
1383
1384 // Clip so we don't render outside the inner rect
1385 gfx->Save();
1386 gfx->Clip(NSRectToSnappedRect(inner, PresContext()->AppUnitsPerDevPixel(),
1387 *drawTarget));
1388
1389 DrawResult result = DrawResult::NOT_READY;
1390
1391 // Check if we should display image placeholders
1392 if (!gIconLoad->mPrefShowPlaceholders ||
1393 (isLoading && !gIconLoad->mPrefShowLoadingPlaceholder)) {
1394 result = DrawResult::SUCCESS;
1395 } else {
1396 nscoord size = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE);
1397
1398 imgIRequest* request = isLoading
1399 ? nsImageFrame::gIconLoad->mLoadingImage
1400 : nsImageFrame::gIconLoad->mBrokenImage;
1401
1402 // If we weren't previously displaying an icon, register ourselves
1403 // as an observer for load and animation updates and flag that we're
1404 // doing so now.
1405 if (request && !mDisplayingIcon) {
1406 gIconLoad->AddIconObserver(this);
1407 mDisplayingIcon = true;
1408 }
1409
1410 WritingMode wm = GetWritingMode();
1411 bool flushRight =
1412 (!wm.IsVertical() && !wm.IsBidiLTR()) || wm.IsVerticalRL();
1413
1414 // If the icon in question is loaded, draw it.
1415 uint32_t imageStatus = 0;
1416 if (request)
1417 request->GetImageStatus(&imageStatus);
1418 if (imageStatus & imgIRequest::STATUS_LOAD_COMPLETE) {
1419 nsCOMPtr<imgIContainer> imgCon;
1420 request->GetImage(getter_AddRefs(imgCon));
1421 MOZ_ASSERT(imgCon, "Load complete, but no image container?");
1422 nsRect dest(flushRight ? inner.XMost() - size : inner.x,
1423 inner.y, size, size);
1424 result = nsLayoutUtils::DrawSingleImage(*gfx, PresContext(), imgCon,
1425 nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
1426 nullptr, aFlags);
1427 }
1428
1429 // If we could not draw the icon, just draw some graffiti in the mean time.
1430 if (result == DrawResult::NOT_READY) {
1431 ColorPattern color(ToDeviceColor(Color(1.f, 0.f, 0.f, 1.f)));
1432
1433 nscoord iconXPos = flushRight ? inner.XMost() - size : inner.x;
1434
1435 // stroked rect:
1436 nsRect rect(iconXPos, inner.y, size, size);
1437 Rect devPxRect =
1438 ToRect(nsLayoutUtils::RectToGfxRect(rect, PresContext()->AppUnitsPerDevPixel()));
1439 drawTarget->StrokeRect(devPxRect, color);
1440
1441 // filled circle in bottom right quadrant of stroked rect:
1442 nscoord twoPX = nsPresContext::CSSPixelsToAppUnits(2);
1443 rect = nsRect(iconXPos + size/2, inner.y + size/2,
1444 size/2 - twoPX, size/2 - twoPX);
1445 devPxRect =
1446 ToRect(nsLayoutUtils::RectToGfxRect(rect, PresContext()->AppUnitsPerDevPixel()));
1447 RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder();
1448 AppendEllipseToPath(builder, devPxRect.Center(), devPxRect.Size());
1449 RefPtr<Path> ellipse = builder->Finish();
1450 drawTarget->Fill(ellipse, color);
1451 }
1452
1453 // Reduce the inner rect by the width of the icon, and leave an
1454 // additional ICON_PADDING pixels for padding
1455 int32_t paddedIconSize =
1456 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING);
1457 if (wm.IsVertical()) {
1458 inner.y += paddedIconSize;
1459 inner.height -= paddedIconSize;
1460 } else {
1461 if (!flushRight) {
1462 inner.x += paddedIconSize;
1463 }
1464 inner.width -= paddedIconSize;
1465 }
1466 }
1467
1468 // If there's still room, display the alt-text
1469 if (!inner.IsEmpty()) {
1470 nsIContent* content = GetContent();
1471 if (content) {
1472 nsXPIDLString altText;
1473 nsCSSFrameConstructor::GetAlternateTextFor(content,
1474 content->NodeInfo()->NameAtom(),
1475 altText);
1476 DisplayAltText(PresContext(), aRenderingContext, altText, inner);
1477 }
1478 }
1479
1480 aRenderingContext.ThebesContext()->Restore();
1481
1482 return result;
1483 }
1484
1485 #ifdef DEBUG
1486 static void PaintDebugImageMap(nsIFrame* aFrame, DrawTarget* aDrawTarget,
1487 const nsRect& aDirtyRect, nsPoint aPt)
1488 {
1489 nsImageFrame* f = static_cast<nsImageFrame*>(aFrame);
1490 nsRect inner = f->GetInnerArea() + aPt;
1491 gfxPoint devPixelOffset =
1492 nsLayoutUtils::PointToGfxPoint(inner.TopLeft(),
1493 aFrame->PresContext()->AppUnitsPerDevPixel());
1494 AutoRestoreTransform autoRestoreTransform(aDrawTarget);
1495 aDrawTarget->SetTransform(
1496 aDrawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset)));
1497 f->GetImageMap()->Draw(aFrame, *aDrawTarget,
1498 ColorPattern(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f))));
1499 }
1500 #endif
1501
1502 void
1503 nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
1504 nsRenderingContext* aCtx)
1505 {
1506 uint32_t flags = imgIContainer::FLAG_NONE;
1507 if (aBuilder->ShouldSyncDecodeImages()) {
1508 flags |= imgIContainer::FLAG_SYNC_DECODE;
1509 }
1510 if (aBuilder->IsPaintingToWindow()) {
1511 flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
1512 }
1513
1514 DrawResult result = static_cast<nsImageFrame*>(mFrame)->
1515 PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mImage, flags);
1516
1517 if (result == DrawResult::NOT_READY ||
1518 result == DrawResult::INCOMPLETE ||
1519 result == DrawResult::TEMPORARY_ERROR) {
1520 // If the current image failed to paint because it's still loading or
1521 // decoding, try painting the previous image.
1522 if (mPrevImage) {
1523 result = static_cast<nsImageFrame*>(mFrame)->
1524 PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mPrevImage, flags);
1525 }
1526 }
1527
1528 nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
1529 }
1530
1531 nsDisplayItemGeometry*
1532 nsDisplayImage::AllocateGeometry(nsDisplayListBuilder* aBuilder)
1533 {
1534 return new nsDisplayItemGenericImageGeometry(this, aBuilder);
1535 }
1536
1537 void
1538 nsDisplayImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
1539 const nsDisplayItemGeometry* aGeometry,
1540 nsRegion* aInvalidRegion)
1541 {
1542 auto geometry =
1543 static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
1544
1545 if (aBuilder->ShouldSyncDecodeImages() &&
1546 geometry->ShouldInvalidateToSyncDecodeImages()) {
1547 bool snap;
1548 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
1549 }
1550
1551 nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
1552 }
1553
1554 already_AddRefed<imgIContainer>
1555 nsDisplayImage::GetImage()
1556 {
1557 nsCOMPtr<imgIContainer> image = mImage;
1558 return image.forget();
1559 }
1560
1561 nsRect
1562 nsDisplayImage::GetDestRect()
1563 {
1564 bool snap = true;
1565 const nsRect frameContentBox = GetBounds(&snap);
1566
1567 nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
1568 return imageFrame->PredictedDestRect(frameContentBox);
1569 }
1570
1571 LayerState
1572 nsDisplayImage::GetLayerState(nsDisplayListBuilder* aBuilder,
1573 LayerManager* aManager,
1574 const ContainerLayerParameters& aParameters)
1575 {
1576 bool animated = false;
1577 if (!nsLayoutUtils::AnimatedImageLayersEnabled() ||
1578 mImage->GetType() != imgIContainer::TYPE_RASTER ||
1579 NS_FAILED(mImage->GetAnimated(&animated)) ||
1580 !animated) {
1581 if (!aManager->IsCompositingCheap() ||
1582 !nsLayoutUtils::GPUImageScalingEnabled()) {
1583 return LAYER_NONE;
1584 }
1585 }
1586
1587 if (!animated) {
1588 int32_t imageWidth;
1589 int32_t imageHeight;
1590 mImage->GetWidth(&imageWidth);
1591 mImage->GetHeight(&imageHeight);
1592
1593 NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
1594
1595 const int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
1596 const LayoutDeviceRect destRect =
1597 LayoutDeviceRect::FromAppUnits(GetDestRect(), factor);
1598 const LayerRect destLayerRect = destRect * aParameters.Scale();
1599
1600 // Calculate the scaling factor for the frame.
1601 const gfxSize scale = gfxSize(destLayerRect.width / imageWidth,
1602 destLayerRect.height / imageHeight);
1603
1604 // If we are not scaling at all, no point in separating this into a layer.
1605 if (scale.width == 1.0f && scale.height == 1.0f) {
1606 return LAYER_NONE;
1607 }
1608
1609 // If the target size is pretty small, no point in using a layer.
1610 if (destLayerRect.width * destLayerRect.height < 64 * 64) {
1611 return LAYER_NONE;
1612 }
1613 }
1614
1615 uint32_t flags = aBuilder->ShouldSyncDecodeImages()
1616 ? imgIContainer::FLAG_SYNC_DECODE
1617 : imgIContainer::FLAG_NONE;
1618
1619 if (!mImage->IsImageContainerAvailable(aManager, flags)) {
1620 return LAYER_NONE;
1621 }
1622
1623 return LAYER_ACTIVE;
1624 }
1625
1626
1627 /* virtual */ nsRegion
1628 nsDisplayImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
1629 bool* aSnap)
1630 {
1631 *aSnap = false;
1632 if (mImage && mImage->WillDrawOpaqueNow()) {
1633 const nsRect frameContentBox = GetBounds(aSnap);
1634 return GetDestRect().Intersect(frameContentBox);
1635 }
1636 return nsRegion();
1637 }
1638
1639 already_AddRefed<Layer>
1640 nsDisplayImage::BuildLayer(nsDisplayListBuilder* aBuilder,
1641 LayerManager* aManager,
1642 const ContainerLayerParameters& aParameters)
1643 {
1644 uint32_t flags = aBuilder->ShouldSyncDecodeImages()
1645 ? imgIContainer::FLAG_SYNC_DECODE
1646 : imgIContainer::FLAG_NONE;
1647
1648 RefPtr<ImageContainer> container =
1649 mImage->GetImageContainer(aManager, flags);
1650 if (!container) {
1651 return nullptr;
1652 }
1653
1654 RefPtr<ImageLayer> layer = static_cast<ImageLayer*>
1655 (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
1656 if (!layer) {
1657 layer = aManager->CreateImageLayer();
1658 if (!layer)
1659 return nullptr;
1660 }
1661 layer->SetContainer(container);
1662 ConfigureLayer(layer, aParameters);
1663 return layer.forget();
1664 }
1665
1666 DrawResult
1667 nsImageFrame::PaintImage(nsRenderingContext& aRenderingContext, nsPoint aPt,
1668 const nsRect& aDirtyRect, imgIContainer* aImage,
1669 uint32_t aFlags)
1670 {
1671 DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
1672
1673 // Render the image into our content area (the area inside
1674 // the borders and padding)
1675 NS_ASSERTION(GetInnerArea().width == mComputedSize.width, "bad width");
1676
1677 // NOTE: We use mComputedSize instead of just GetInnerArea()'s own size here,
1678 // because GetInnerArea() might be smaller if we're fragmented, whereas
1679 // mComputedSize has our full content-box size (which we need for
1680 // ComputeObjectDestRect to work correctly).
1681 nsRect constraintRect(aPt + GetInnerArea().TopLeft(), mComputedSize);
1682 constraintRect.y -= GetContinuationOffset();
1683
1684 nsPoint anchorPoint;
1685 nsRect dest = nsLayoutUtils::ComputeObjectDestRect(constraintRect,
1686 mIntrinsicSize,
1687 mIntrinsicRatio,
1688 StylePosition(),
1689 &anchorPoint);
1690
1691 uint32_t flags = aFlags;
1692 if (mForceSyncDecoding) {
1693 flags |= imgIContainer::FLAG_SYNC_DECODE;
1694 }
1695
1696 DrawResult result =
1697 nsLayoutUtils::DrawSingleImage(*aRenderingContext.ThebesContext(),
1698 PresContext(), aImage,
1699 nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
1700 nullptr, flags, &anchorPoint);
1701
1702 nsImageMap* map = GetImageMap();
1703 if (map) {
1704 gfxPoint devPixelOffset =
1705 nsLayoutUtils::PointToGfxPoint(dest.TopLeft(),
1706 PresContext()->AppUnitsPerDevPixel());
1707 AutoRestoreTransform autoRestoreTransform(drawTarget);
1708 drawTarget->SetTransform(
1709 drawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset)));
1710
1711 // solid white stroke:
1712 ColorPattern white(ToDeviceColor(Color(1.f, 1.f, 1.f, 1.f)));
1713 map->Draw(this, *drawTarget, white);
1714
1715 // then dashed black stroke over the top:
1716 ColorPattern black(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f)));
1717 StrokeOptions strokeOptions;
1718 nsLayoutUtils::InitDashPattern(strokeOptions, NS_STYLE_BORDER_STYLE_DOTTED);
1719 map->Draw(this, *drawTarget, black, strokeOptions);
1720 }
1721
1722 if (result == DrawResult::SUCCESS) {
1723 mPrevImage = aImage;
1724 } else if (result == DrawResult::BAD_IMAGE) {
1725 mPrevImage = nullptr;
1726 }
1727
1728 return result;
1729 }
1730
1731 void
1732 nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1733 const nsRect& aDirtyRect,
1734 const nsDisplayListSet& aLists)
1735 {
1736 if (!IsVisibleForPainting(aBuilder))
1737 return;
1738
1739 DisplayBorderBackgroundOutline(aBuilder, aLists);
1740
1741 uint32_t clipFlags =
1742 nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition()) ?
1743 0 : DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT;
1744
1745 DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
1746 clip(aBuilder, this, clipFlags);
1747
1748 if (mComputedSize.width != 0 && mComputedSize.height != 0) {
1749 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
1750 NS_ASSERTION(imageLoader, "Not an image loading content?");
1751
1752 nsCOMPtr<imgIRequest> currentRequest;
1753 if (imageLoader) {
1754 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
1755 getter_AddRefs(currentRequest));
1756 }
1757
1758 EventStates contentState = mContent->AsElement()->State();
1759 bool imageOK = IMAGE_OK(contentState, true);
1760
1761 // XXX(seth): The SizeIsAvailable check here should not be necessary - the
1762 // intention is that a non-null mImage means we have a size, but there is
1763 // currently some code that violates this invariant.
1764 if (!imageOK || !mImage || !SizeIsAvailable(currentRequest)) {
1765 // No image yet, or image load failed. Draw the alt-text and an icon
1766 // indicating the status
1767 aLists.Content()->AppendNewToTop(new (aBuilder)
1768 nsDisplayAltFeedback(aBuilder, this));
1769
1770 // This image is visible (we are being asked to paint it) but it's not
1771 // decoded yet. And we are not going to ask the image to draw, so this
1772 // may be the only chance to tell it that it should decode.
1773 if (currentRequest) {
1774 uint32_t status = 0;
1775 currentRequest->GetImageStatus(&status);
1776 if (!(status & imgIRequest::STATUS_DECODE_COMPLETE)) {
1777 MaybeDecodeForPredictedSize();
1778 }
1779 }
1780 } else {
1781 aLists.Content()->AppendNewToTop(new (aBuilder)
1782 nsDisplayImage(aBuilder, this, mImage, mPrevImage));
1783
1784 // If we were previously displaying an icon, we're not anymore
1785 if (mDisplayingIcon) {
1786 gIconLoad->RemoveIconObserver(this);
1787 mDisplayingIcon = false;
1788 }
1789
1790 #ifdef DEBUG
1791 if (GetShowFrameBorders() && GetImageMap()) {
1792 aLists.Outlines()->AppendNewToTop(new (aBuilder)
1793 nsDisplayGeneric(aBuilder, this, PaintDebugImageMap, "DebugImageMap",
1794 nsDisplayItem::TYPE_DEBUG_IMAGE_MAP));
1795 }
1796 #endif
1797 }
1798 }
1799
1800 if (ShouldDisplaySelection()) {
1801 DisplaySelectionOverlay(aBuilder, aLists.Content(),
1802 nsISelectionDisplay::DISPLAY_IMAGES);
1803 }
1804 }
1805
1806 bool
1807 nsImageFrame::ShouldDisplaySelection()
1808 {
1809 // XXX what on EARTH is this code for?
1810 nsresult result;
1811 nsPresContext* presContext = PresContext();
1812 int16_t displaySelection = presContext->PresShell()->GetSelectionFlags();
1813 if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES))
1814 return false;//no need to check the blue border, we cannot be drawn selected
1815 //insert hook here for image selection drawing
1816 #if IMAGE_EDITOR_CHECK
1817 //check to see if this frame is in an editor context
1818 //isEditor check. this needs to be changed to have better way to check
1819 if (displaySelection == nsISelectionDisplay::DISPLAY_ALL)
1820 {
1821 nsCOMPtr<nsISelectionController> selCon;
1822 result = GetSelectionController(presContext, getter_AddRefs(selCon));
1823 if (NS_SUCCEEDED(result) && selCon)
1824 {
1825 nsCOMPtr<nsISelection> selection;
1826 result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
1827 if (NS_SUCCEEDED(result) && selection)
1828 {
1829 int32_t rangeCount;
1830 selection->GetRangeCount(&rangeCount);
1831 if (rangeCount == 1) //if not one then let code drop to nsFrame::Paint
1832 {
1833 nsCOMPtr<nsIContent> parentContent = mContent->GetParent();
1834 if (parentContent)
1835 {
1836 int32_t thisOffset = parentContent->IndexOf(mContent);
1837 nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(parentContent);
1838 nsCOMPtr<nsIDOMNode> rangeNode;
1839 int32_t rangeOffset;
1840 nsCOMPtr<nsIDOMRange> range;
1841 selection->GetRangeAt(0,getter_AddRefs(range));
1842 if (range)
1843 {
1844 range->GetStartContainer(getter_AddRefs(rangeNode));
1845 range->GetStartOffset(&rangeOffset);
1846
1847 if (parentNode && rangeNode && (rangeNode == parentNode) && rangeOffset == thisOffset)
1848 {
1849 range->GetEndContainer(getter_AddRefs(rangeNode));
1850 range->GetEndOffset(&rangeOffset);
1851 if ((rangeNode == parentNode) && (rangeOffset == (thisOffset +1))) //+1 since that would mean this whole content is selected only
1852 return false; //do not allow nsFrame do draw any further selection
1853 }
1854 }
1855 }
1856 }
1857 }
1858 }
1859 }
1860 #endif
1861 return true;
1862 }
1863
1864 nsImageMap*
1865 nsImageFrame::GetImageMap()
1866 {
1867 if (!mImageMap) {
1868 nsIContent* map = GetMapElement();
1869 if (map) {
1870 mImageMap = new nsImageMap();
1871 mImageMap->Init(this, map);
1872 }
1873 }
1874
1875 return mImageMap;
1876 }
1877
1878 bool
1879 nsImageFrame::IsServerImageMap()
1880 {
1881 return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::ismap);
1882 }
1883
1884 // Translate an point that is relative to our frame
1885 // into a localized pixel coordinate that is relative to the
1886 // content area of this frame (inside the border+padding).
1887 void
1888 nsImageFrame::TranslateEventCoords(const nsPoint& aPoint,
1889 nsIntPoint& aResult)
1890 {
1891 nscoord x = aPoint.x;
1892 nscoord y = aPoint.y;
1893
1894 // Subtract out border and padding here so that the coordinates are
1895 // now relative to the content area of this frame.
1896 nsRect inner = GetInnerArea();
1897 x -= inner.x;
1898 y -= inner.y;
1899
1900 aResult.x = nsPresContext::AppUnitsToIntCSSPixels(x);
1901 aResult.y = nsPresContext::AppUnitsToIntCSSPixels(y);
1902 }
1903
1904 bool
1905 nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget,
1906 nsIContent** aNode)
1907 {
1908 bool status = false;
1909 aTarget.Truncate();
1910 *aHref = nullptr;
1911 *aNode = nullptr;
1912
1913 // Walk up the content tree, looking for an nsIDOMAnchorElement
1914 for (nsIContent* content = mContent->GetParent();
1915 content; content = content->GetParent()) {
1916 nsCOMPtr<dom::Link> link(do_QueryInterface(content));
1917 if (link) {
1918 nsCOMPtr<nsIURI> href = content->GetHrefURI();
1919 if (href) {
1920 href->Clone(aHref);
1921 }
1922 status = (*aHref != nullptr);
1923
1924 nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(content));
1925 if (anchor) {
1926 anchor->GetTarget(aTarget);
1927 }
1928 NS_ADDREF(*aNode = content);
1929 break;
1930 }
1931 }
1932 return status;
1933 }
1934
1935 nsresult
1936 nsImageFrame::GetContentForEvent(WidgetEvent* aEvent,
1937 nsIContent** aContent)
1938 {
1939 NS_ENSURE_ARG_POINTER(aContent);
1940
1941 nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
1942 if (f != this) {
1943 return f->GetContentForEvent(aEvent, aContent);
1944 }
1945
1946 // XXX We need to make this special check for area element's capturing the
1947 // mouse due to bug 135040. Remove it once that's fixed.
1948 nsIContent* capturingContent =
1949 aEvent->HasMouseEventMessage() ? nsIPresShell::GetCapturingContent() :
1950 nullptr;
1951 if (capturingContent && capturingContent->GetPrimaryFrame() == this) {
1952 *aContent = capturingContent;
1953 NS_IF_ADDREF(*aContent);
1954 return NS_OK;
1955 }
1956
1957 nsImageMap* map = GetImageMap();
1958
1959 if (nullptr != map) {
1960 nsIntPoint p;
1961 TranslateEventCoords(
1962 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
1963 nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
1964 if (area) {
1965 area.forget(aContent);
1966 return NS_OK;
1967 }
1968 }
1969
1970 *aContent = GetContent();
1971 NS_IF_ADDREF(*aContent);
1972 return NS_OK;
1973 }
1974
1975 // XXX what should clicks on transparent pixels do?
1976 nsresult
1977 nsImageFrame::HandleEvent(nsPresContext* aPresContext,
1978 WidgetGUIEvent* aEvent,
1979 nsEventStatus* aEventStatus)
1980 {
1981 NS_ENSURE_ARG_POINTER(aEventStatus);
1982
1983 if ((aEvent->mMessage == eMouseClick &&
1984 aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) ||
1985 aEvent->mMessage == eMouseMove) {
1986 nsImageMap* map = GetImageMap();
1987 bool isServerMap = IsServerImageMap();
1988 if ((nullptr != map) || isServerMap) {
1989 nsIntPoint p;
1990 TranslateEventCoords(
1991 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
1992 bool inside = false;
1993 // Even though client-side image map triggering happens
1994 // through content, we need to make sure we're not inside
1995 // (in case we deal with a case of both client-side and
1996 // sever-side on the same image - it happens!)
1997 if (nullptr != map) {
1998 inside = !!map->GetArea(p.x, p.y);
1999 }
2000
2001 if (!inside && isServerMap) {
2002
2003 // Server side image maps use the href in a containing anchor
2004 // element to provide the basis for the destination url.
2005 nsCOMPtr<nsIURI> uri;
2006 nsAutoString target;
2007 nsCOMPtr<nsIContent> anchorNode;
2008 if (GetAnchorHREFTargetAndNode(getter_AddRefs(uri), target,
2009 getter_AddRefs(anchorNode))) {
2010 // XXX if the mouse is over/clicked in the border/padding area
2011 // we should probably just pretend nothing happened. Nav4
2012 // keeps the x,y coordinates positive as we do; IE doesn't
2013 // bother. Both of them send the click through even when the
2014 // mouse is over the border.
2015 if (p.x < 0) p.x = 0;
2016 if (p.y < 0) p.y = 0;
2017
2018 nsAutoCString spec;
2019 nsresult rv = uri->GetSpec(spec);
2020 NS_ENSURE_SUCCESS(rv, rv);
2021
2022 spec += nsPrintfCString("?%d,%d", p.x, p.y);
2023 rv = uri->SetSpec(spec);
2024 NS_ENSURE_SUCCESS(rv, rv);
2025
2026 bool clicked = false;
2027 if (aEvent->mMessage == eMouseClick && !aEvent->DefaultPrevented()) {
2028 *aEventStatus = nsEventStatus_eConsumeDoDefault;
2029 clicked = true;
2030 }
2031 nsContentUtils::TriggerLink(anchorNode, aPresContext, uri, target,
2032 clicked, true, true);
2033 }
2034 }
2035 }
2036 }
2037
2038 return nsAtomicContainerFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
2039 }
2040
2041 nsresult
2042 nsImageFrame::GetCursor(const nsPoint& aPoint,
2043 nsIFrame::Cursor& aCursor)
2044 {
2045 nsImageMap* map = GetImageMap();
2046 if (nullptr != map) {
2047 nsIntPoint p;
2048 TranslateEventCoords(aPoint, p);
2049 nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
2050 if (area) {
2051 // Use the cursor from the style of the *area* element.
2052 // XXX Using the image as the parent style context isn't
2053 // technically correct, but it's probably the right thing to do
2054 // here, since it means that areas on which the cursor isn't
2055 // specified will inherit the style from the image.
2056 RefPtr<nsStyleContext> areaStyle =
2057 PresContext()->PresShell()->StyleSet()->
2058 ResolveStyleFor(area->AsElement(), StyleContext());
2059 FillCursorInformationFromStyle(areaStyle->StyleUserInterface(),
2060 aCursor);
2061 if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
2062 aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
2063 }
2064 return NS_OK;
2065 }
2066 }
2067 return nsFrame::GetCursor(aPoint, aCursor);
2068 }
2069
2070 nsresult
2071 nsImageFrame::AttributeChanged(int32_t aNameSpaceID,
2072 nsIAtom* aAttribute,
2073 int32_t aModType)
2074 {
2075 nsresult rv = nsAtomicContainerFrame::AttributeChanged(aNameSpaceID,
2076 aAttribute, aModType);
2077 if (NS_FAILED(rv)) {
2078 return rv;
2079 }
2080 if (nsGkAtoms::alt == aAttribute)
2081 {
2082 PresContext()->PresShell()->FrameNeedsReflow(this,
2083 nsIPresShell::eStyleChange,
2084 NS_FRAME_IS_DIRTY);
2085 }
2086
2087 return NS_OK;
2088 }
2089
2090 void
2091 nsImageFrame::OnVisibilityChange(Visibility aNewVisibility,
2092 Maybe<OnNonvisible> aNonvisibleAction)
2093 {
2094 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
2095 if (!imageLoader) {
2096 MOZ_ASSERT_UNREACHABLE("Should have an nsIImageLoadingContent");
2097 nsAtomicContainerFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
2098 return;
2099 }
2100
2101 imageLoader->OnVisibilityChange(aNewVisibility, aNonvisibleAction);
2102
2103 if (aNewVisibility == Visibility::APPROXIMATELY_VISIBLE) {
2104 MaybeDecodeForPredictedSize();
2105 }
2106
2107 nsAtomicContainerFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction);
2108 }
2109
2110 nsIAtom*
2111 nsImageFrame::GetType() const
2112 {
2113 return nsGkAtoms::imageFrame;
2114 }
2115
2116 #ifdef DEBUG_FRAME_DUMP
2117 nsresult
2118 nsImageFrame::GetFrameName(nsAString& aResult) const
2119 {
2120 return MakeFrameName(NS_LITERAL_STRING("ImageFrame"), aResult);
2121 }
2122
2123 void
2124 nsImageFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
2125 {
2126 nsCString str;
2127 ListGeneric(str, aPrefix, aFlags);
2128
2129 // output the img src url
2130 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
2131 if (imageLoader) {
2132 nsCOMPtr<imgIRequest> currentRequest;
2133 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
2134 getter_AddRefs(currentRequest));
2135 if (currentRequest) {
2136 nsCOMPtr<nsIURI> uri;
2137 currentRequest->GetURI(getter_AddRefs(uri));
2138 nsAutoCString uristr;
2139 uri->GetAsciiSpec(uristr);
2140 str += nsPrintfCString(" [src=%s]", uristr.get());
2141 }
2142 }
2143 fprintf_stderr(out, "%s\n", str.get());
2144 }
2145 #endif
2146
2147 nsIFrame::LogicalSides
2148 nsImageFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const
2149 {
2150 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
2151 StyleBoxDecorationBreak::Clone)) {
2152 return LogicalSides();
2153 }
2154 LogicalSides skip;
2155 if (nullptr != GetPrevInFlow()) {
2156 skip |= eLogicalSideBitsBStart;
2157 }
2158 if (nullptr != GetNextInFlow()) {
2159 skip |= eLogicalSideBitsBEnd;
2160 }
2161 return skip;
2162 }
2163
2164 nsresult
2165 nsImageFrame::GetIntrinsicImageSize(nsSize& aSize)
2166 {
2167 if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
2168 mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
2169 aSize.SizeTo(mIntrinsicSize.width.GetCoordValue(),
2170 mIntrinsicSize.height.GetCoordValue());
2171 return NS_OK;
2172 }
2173
2174 return NS_ERROR_FAILURE;
2175 }
2176
2177 nsresult
2178 nsImageFrame::LoadIcon(const nsAString& aSpec,
2179 nsPresContext *aPresContext,
2180 imgRequestProxy** aRequest)
2181 {
2182 nsresult rv = NS_OK;
2183 NS_PRECONDITION(!aSpec.IsEmpty(), "What happened??");
2184
2185 if (!sIOService) {
2186 rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
2187 NS_ENSURE_SUCCESS(rv, rv);
2188 }
2189
2190 nsCOMPtr<nsIURI> realURI;
2191 SpecToURI(aSpec, sIOService, getter_AddRefs(realURI));
2192
2193 RefPtr<imgLoader> il =
2194 nsContentUtils::GetImgLoaderForDocument(aPresContext->Document());
2195
2196 nsCOMPtr<nsILoadGroup> loadGroup;
2197 GetLoadGroup(aPresContext, getter_AddRefs(loadGroup));
2198
2199 // For icon loads, we don't need to merge with the loadgroup flags
2200 nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
2201 nsContentPolicyType contentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
2202
2203 return il->LoadImage(realURI, /* icon URI */
2204 nullptr, /* initial document URI; this is only
2205 relevant for cookies, so does not
2206 apply to icons. */
2207 nullptr, /* referrer (not relevant for icons) */
2208 mozilla::net::RP_Default,
2209 nullptr, /* principal (not relevant for icons) */
2210 loadGroup,
2211 gIconLoad,
2212 nullptr, /* No context */
2213 nullptr, /* Not associated with any particular document */
2214 loadFlags,
2215 nullptr,
2216 contentPolicyType,
2217 EmptyString(),
2218 aRequest);
2219 }
2220
2221 void
2222 nsImageFrame::GetDocumentCharacterSet(nsACString& aCharset) const
2223 {
2224 if (mContent) {
2225 NS_ASSERTION(mContent->GetComposedDoc(),
2226 "Frame still alive after content removed from document!");
2227 aCharset = mContent->GetComposedDoc()->GetDocumentCharacterSet();
2228 }
2229 }
2230
2231 void
2232 nsImageFrame::SpecToURI(const nsAString& aSpec, nsIIOService *aIOService,
2233 nsIURI **aURI)
2234 {
2235 nsCOMPtr<nsIURI> baseURI;
2236 if (mContent) {
2237 baseURI = mContent->GetBaseURI();
2238 }
2239 nsAutoCString charset;
2240 GetDocumentCharacterSet(charset);
2241 NS_NewURI(aURI, aSpec,
2242 charset.IsEmpty() ? nullptr : charset.get(),
2243 baseURI, aIOService);
2244 }
2245
2246 void
2247 nsImageFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
2248 {
2249 if (!aPresContext)
2250 return;
2251
2252 NS_PRECONDITION(nullptr != aLoadGroup, "null OUT parameter pointer");
2253
2254 nsIPresShell *shell = aPresContext->GetPresShell();
2255
2256 if (!shell)
2257 return;
2258
2259 nsIDocument *doc = shell->GetDocument();
2260 if (!doc)
2261 return;
2262
2263 *aLoadGroup = doc->GetDocumentLoadGroup().take();
2264 }
2265
2266 nsresult nsImageFrame::LoadIcons(nsPresContext *aPresContext)
2267 {
2268 NS_ASSERTION(!gIconLoad, "called LoadIcons twice");
2269
2270 NS_NAMED_LITERAL_STRING(loadingSrc,"resource://gre-resources/loading-image.png");
2271 NS_NAMED_LITERAL_STRING(brokenSrc,"resource://gre-resources/broken-image.png");
2272
2273 gIconLoad = new IconLoad();
2274 NS_ADDREF(gIconLoad);
2275
2276 nsresult rv;
2277 // create a loader and load the images
2278 rv = LoadIcon(loadingSrc,
2279 aPresContext,
2280 getter_AddRefs(gIconLoad->mLoadingImage));
2281 if (NS_FAILED(rv)) {
2282 return rv;
2283 }
2284
2285 rv = LoadIcon(brokenSrc,
2286 aPresContext,
2287 getter_AddRefs(gIconLoad->mBrokenImage));
2288 if (NS_FAILED(rv)) {
2289 return rv;
2290 }
2291
2292 return rv;
2293 }
2294
2295 NS_IMPL_ISUPPORTS(nsImageFrame::IconLoad, nsIObserver,
2296 imgINotificationObserver)
2297
2298 static const char* kIconLoadPrefs[] = {
2299 "browser.display.force_inline_alttext",
2300 "browser.display.show_image_placeholders",
2301 "browser.display.show_loading_image_placeholder",
2302 nullptr
2303 };
2304
2305 nsImageFrame::IconLoad::IconLoad()
2306 {
2307 // register observers
2308 Preferences::AddStrongObservers(this, kIconLoadPrefs);
2309 GetPrefs();
2310 }
2311
2312 void
2313 nsImageFrame::IconLoad::Shutdown()
2314 {
2315 Preferences::RemoveObservers(this, kIconLoadPrefs);
2316 // in case the pref service releases us later
2317 if (mLoadingImage) {
2318 mLoadingImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
2319 mLoadingImage = nullptr;
2320 }
2321 if (mBrokenImage) {
2322 mBrokenImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
2323 mBrokenImage = nullptr;
2324 }
2325 }
2326
2327 NS_IMETHODIMP
2328 nsImageFrame::IconLoad::Observe(nsISupports *aSubject, const char* aTopic,
2329 const char16_t* aData)
2330 {
2331 NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
2332 "wrong topic");
2333 #ifdef DEBUG
2334 // assert |aData| is one of our prefs.
2335 uint32_t i = 0;
2336 for (; i < ArrayLength(kIconLoadPrefs); ++i) {
2337 if (NS_ConvertASCIItoUTF16(kIconLoadPrefs[i]) == nsDependentString(aData))
2338 break;
2339 }
2340 MOZ_ASSERT(i < ArrayLength(kIconLoadPrefs));
2341 #endif
2342
2343 GetPrefs();
2344 return NS_OK;
2345 }
2346
2347 void nsImageFrame::IconLoad::GetPrefs()
2348 {
2349 mPrefForceInlineAltText =
2350 Preferences::GetBool("browser.display.force_inline_alttext");
2351
2352 mPrefShowPlaceholders =
2353 Preferences::GetBool("browser.display.show_image_placeholders", true);
2354
2355 mPrefShowLoadingPlaceholder =
2356 Preferences::GetBool("browser.display.show_loading_image_placeholder", true);
2357 }
2358
2359 NS_IMETHODIMP
2360 nsImageFrame::IconLoad::Notify(imgIRequest* aRequest,
2361 int32_t aType,
2362 const nsIntRect* aData)
2363 {
2364 MOZ_ASSERT(aRequest);
2365
2366 if (aType != imgINotificationObserver::LOAD_COMPLETE &&
2367 aType != imgINotificationObserver::FRAME_UPDATE) {
2368 return NS_OK;
2369 }
2370
2371 if (aType == imgINotificationObserver::LOAD_COMPLETE) {
2372 nsCOMPtr<imgIContainer> image;
2373 aRequest->GetImage(getter_AddRefs(image));
2374 if (!image) {
2375 return NS_ERROR_FAILURE;
2376 }
2377
2378 // Retrieve the image's intrinsic size.
2379 int32_t width = 0;
2380 int32_t height = 0;
2381 image->GetWidth(&width);
2382 image->GetHeight(&height);
2383
2384 // Request a decode at that size.
2385 image->RequestDecodeForSize(IntSize(width, height),
2386 imgIContainer::DECODE_FLAGS_DEFAULT);
2387 }
2388
2389 nsTObserverArray<nsImageFrame*>::ForwardIterator iter(mIconObservers);
2390 nsImageFrame *frame;
2391 while (iter.HasMore()) {
2392 frame = iter.GetNext();
2393 frame->InvalidateFrame();
2394 }
2395
2396 return NS_OK;
2397 }
2398
2399 NS_IMPL_ISUPPORTS(nsImageListener, imgINotificationObserver)
2400
2401 nsImageListener::nsImageListener(nsImageFrame *aFrame) :
2402 mFrame(aFrame)
2403 {
2404 }
2405
2406 nsImageListener::~nsImageListener()
2407 {
2408 }
2409
2410 NS_IMETHODIMP
2411 nsImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
2412 {
2413 if (!mFrame)
2414 return NS_ERROR_FAILURE;
2415
2416 return mFrame->Notify(aRequest, aType, aData);
2417 }
2418
2419 static bool
2420 IsInAutoWidthTableCellForQuirk(nsIFrame *aFrame)
2421 {
2422 if (eCompatibility_NavQuirks != aFrame->PresContext()->CompatibilityMode())
2423 return false;
2424 // Check if the parent of the closest nsBlockFrame has auto width.
2425 nsBlockFrame *ancestor = nsLayoutUtils::FindNearestBlockAncestor(aFrame);
2426 if (ancestor->StyleContext()->GetPseudo() == nsCSSAnonBoxes::cellContent) {
2427 // Assume direct parent is a table cell frame.
2428 nsFrame *grandAncestor = static_cast<nsFrame*>(ancestor->GetParent());
2429 return grandAncestor &&
2430 grandAncestor->StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto;
2431 }
2432 return false;
2433 }
2434
2435 /* virtual */ void
2436 nsImageFrame::AddInlineMinISize(nsRenderingContext* aRenderingContext,
2437 nsIFrame::InlineMinISizeData* aData)
2438 {
2439 nscoord isize = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
2440 this, nsLayoutUtils::MIN_ISIZE);
2441 bool canBreak = !IsInAutoWidthTableCellForQuirk(this);
2442 aData->DefaultAddInlineMinISize(this, isize, canBreak);
2443 }
2444