Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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"
45 #include "imgIContainer.h"
46 #include "imgLoader.h"
47 #include "imgRequestProxy.h"
49 #include "nsCSSFrameConstructor.h"
50 #include "nsIDOMRange.h"
52 #include "nsError.h"
53 #include "nsBidiUtils.h"
54 #include "nsBidiPresUtils.h"
55 #include "mozIThirdPartyUtil.h"
57 #include "gfxRect.h"
58 #include "ImageLayers.h"
59 #include "ImageContainer.h"
60 #include "nsStyleSet.h"
61 #include "nsBlockFrame.h"
62 #include "nsStyleStructInlines.h"
64 #include "mozilla/Preferences.h"
66 #include "mozilla/dom/Link.h"
68 using namespace mozilla;
70 // sizes (pixels) for image icon, padding and border frame
71 #define ICON_SIZE (16)
72 #define ICON_PADDING (3)
73 #define ALT_BORDER_WIDTH (1)
76 //we must add hooks soon
77 #define IMAGE_EDITOR_CHECK 1
79 // Default alignment value (so we can tell an unset value from a set value)
80 #define ALIGN_UNSET uint8_t(-1)
82 using namespace mozilla::layers;
83 using namespace mozilla::dom;
85 // static icon information
86 nsImageFrame::IconLoad* nsImageFrame::gIconLoad = nullptr;
88 // cached IO service for loading icons
89 nsIIOService* nsImageFrame::sIOService;
91 // test if the width and height are fixed, looking at the style data
92 static bool HaveFixedSize(const nsStylePosition* aStylePosition)
93 {
94 // check the width and height values in the reflow state's style struct
95 // - if width and height are specified as either coord or percentage, then
96 // the size of the image frame is constrained
97 return aStylePosition->mWidth.IsCoordPercentCalcUnit() &&
98 aStylePosition->mHeight.IsCoordPercentCalcUnit();
99 }
100 // use the data in the reflow state to decide if the image has a constrained size
101 // (i.e. width and height that are based on the containing block size and not the image size)
102 // so we can avoid animated GIF related reflows
103 inline bool HaveFixedSize(const nsHTMLReflowState& aReflowState)
104 {
105 NS_ASSERTION(aReflowState.mStylePosition, "crappy reflowState - null stylePosition");
106 // when an image has percent css style height or width, but ComputedHeight()
107 // or ComputedWidth() of reflow state is NS_UNCONSTRAINEDSIZE
108 // it needs to return false to cause an incremental reflow later
109 // if an image is inside table like bug 156731 simple testcase III,
110 // during pass 1 reflow, ComputedWidth() is NS_UNCONSTRAINEDSIZE
111 // in pass 2 reflow, ComputedWidth() is 0, it also needs to return false
112 // see bug 156731
113 const nsStyleCoord &height = aReflowState.mStylePosition->mHeight;
114 const nsStyleCoord &width = aReflowState.mStylePosition->mWidth;
115 return ((height.HasPercent() &&
116 NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight()) ||
117 (width.HasPercent() &&
118 (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedWidth() ||
119 0 == aReflowState.ComputedWidth())))
120 ? false
121 : HaveFixedSize(aReflowState.mStylePosition);
122 }
124 nsIFrame*
125 NS_NewImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
126 {
127 return new (aPresShell) nsImageFrame(aContext);
128 }
130 NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame)
133 nsImageFrame::nsImageFrame(nsStyleContext* aContext) :
134 ImageFrameSuper(aContext),
135 mComputedSize(0, 0),
136 mIntrinsicRatio(0, 0),
137 mDisplayingIcon(false),
138 mFirstFrameComplete(false),
139 mReflowCallbackPosted(false)
140 {
141 // We assume our size is not constrained and we haven't gotten an
142 // initial reflow yet, so don't touch those flags.
143 mIntrinsicSize.width.SetCoordValue(0);
144 mIntrinsicSize.height.SetCoordValue(0);
145 }
147 nsImageFrame::~nsImageFrame()
148 {
149 }
151 NS_QUERYFRAME_HEAD(nsImageFrame)
152 NS_QUERYFRAME_ENTRY(nsImageFrame)
153 NS_QUERYFRAME_TAIL_INHERITING(ImageFrameSuper)
155 #ifdef ACCESSIBILITY
156 a11y::AccType
157 nsImageFrame::AccessibleType()
158 {
159 // Don't use GetImageMap() to avoid reentrancy into accessibility.
160 if (HasImageMap()) {
161 return a11y::eHTMLImageMapType;
162 }
164 return a11y::eImageType;
165 }
166 #endif
168 void
169 nsImageFrame::DisconnectMap()
170 {
171 if (mImageMap) {
172 mImageMap->Destroy();
173 NS_RELEASE(mImageMap);
175 #ifdef ACCESSIBILITY
176 nsAccessibilityService* accService = GetAccService();
177 if (accService) {
178 accService->RecreateAccessible(PresContext()->PresShell(), mContent);
179 }
180 #endif
181 }
182 }
184 void
185 nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
186 {
187 if (mReflowCallbackPosted) {
188 PresContext()->PresShell()->CancelReflowCallback(this);
189 mReflowCallbackPosted = false;
190 }
192 // Tell our image map, if there is one, to clean up
193 // This causes the nsImageMap to unregister itself as
194 // a DOM listener.
195 DisconnectMap();
197 // set the frame to null so we don't send messages to a dead object.
198 if (mListener) {
199 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
200 if (imageLoader) {
201 // Notify our image loading content that we are going away so it can
202 // deregister with our refresh driver.
203 imageLoader->FrameDestroyed(this);
205 imageLoader->RemoveObserver(mListener);
206 }
208 reinterpret_cast<nsImageListener*>(mListener.get())->SetFrame(nullptr);
209 }
211 mListener = nullptr;
213 // If we were displaying an icon, take ourselves off the list
214 if (mDisplayingIcon)
215 gIconLoad->RemoveIconObserver(this);
217 nsSplittableFrame::DestroyFrom(aDestructRoot);
218 }
222 void
223 nsImageFrame::Init(nsIContent* aContent,
224 nsIFrame* aParent,
225 nsIFrame* aPrevInFlow)
226 {
227 nsSplittableFrame::Init(aContent, aParent, aPrevInFlow);
229 mListener = new nsImageListener(this);
231 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aContent);
232 if (!imageLoader) {
233 NS_RUNTIMEABORT("Why do we have an nsImageFrame here at all?");
234 }
236 imageLoader->AddObserver(mListener);
238 nsPresContext *aPresContext = PresContext();
240 if (!gIconLoad)
241 LoadIcons(aPresContext);
243 // We have a PresContext now, so we need to notify the image content node
244 // that it can register images.
245 imageLoader->FrameCreated(this);
247 // Give image loads associated with an image frame a small priority boost!
248 nsCOMPtr<imgIRequest> currentRequest;
249 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
250 getter_AddRefs(currentRequest));
251 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(currentRequest);
252 if (p)
253 p->AdjustPriority(-1);
255 // If we already have an image container, OnStartContainer won't be called
256 if (currentRequest) {
257 nsCOMPtr<imgIContainer> image;
258 currentRequest->GetImage(getter_AddRefs(image));
259 OnStartContainer(currentRequest, image);
260 }
261 }
263 bool
264 nsImageFrame::UpdateIntrinsicSize(imgIContainer* aImage)
265 {
266 NS_PRECONDITION(aImage, "null image");
267 if (!aImage)
268 return false;
270 IntrinsicSize oldIntrinsicSize = mIntrinsicSize;
271 mIntrinsicSize = IntrinsicSize();
273 // Set intrinsic size to match aImage's reported intrinsic width & height.
274 nsSize intrinsicSize;
275 if (NS_SUCCEEDED(aImage->GetIntrinsicSize(&intrinsicSize))) {
276 // If the image has no intrinsic width, intrinsicSize.width will be -1, and
277 // we can leave mIntrinsicSize.width at its default value of eStyleUnit_None.
278 // Otherwise we use intrinsicSize.width. Height works the same way.
279 if (intrinsicSize.width != -1)
280 mIntrinsicSize.width.SetCoordValue(intrinsicSize.width);
281 if (intrinsicSize.height != -1)
282 mIntrinsicSize.height.SetCoordValue(intrinsicSize.height);
283 } else {
284 // Failure means that the image hasn't loaded enough to report a result. We
285 // treat this case as if the image's intrinsic size was 0x0.
286 mIntrinsicSize.width.SetCoordValue(0);
287 mIntrinsicSize.height.SetCoordValue(0);
288 }
290 return mIntrinsicSize != oldIntrinsicSize;
291 }
293 bool
294 nsImageFrame::UpdateIntrinsicRatio(imgIContainer* aImage)
295 {
296 NS_PRECONDITION(aImage, "null image");
298 if (!aImage)
299 return false;
301 nsSize oldIntrinsicRatio = mIntrinsicRatio;
303 // Set intrinsic ratio to match aImage's reported intrinsic ratio.
304 if (NS_FAILED(aImage->GetIntrinsicRatio(&mIntrinsicRatio)))
305 mIntrinsicRatio.SizeTo(0, 0);
307 return mIntrinsicRatio != oldIntrinsicRatio;
308 }
310 bool
311 nsImageFrame::GetSourceToDestTransform(nsTransform2D& aTransform)
312 {
313 // Set the translation components.
314 // XXXbz does this introduce rounding errors because of the cast to
315 // float? Should we just manually add that stuff in every time
316 // instead?
317 nsRect innerArea = GetInnerArea();
318 aTransform.SetToTranslate(float(innerArea.x),
319 float(innerArea.y - GetContinuationOffset()));
321 // Set the scale factors.
322 if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
323 mIntrinsicSize.width.GetCoordValue() != 0 &&
324 mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
325 mIntrinsicSize.height.GetCoordValue() != 0 &&
326 mIntrinsicSize.width.GetCoordValue() != mComputedSize.width &&
327 mIntrinsicSize.height.GetCoordValue() != mComputedSize.height) {
329 aTransform.SetScale(float(mComputedSize.width) /
330 float(mIntrinsicSize.width.GetCoordValue()),
331 float(mComputedSize.height) /
332 float(mIntrinsicSize.height.GetCoordValue()));
333 return true;
334 }
336 return false;
337 }
339 /*
340 * These two functions basically do the same check. The first one
341 * checks that the given request is the current request for our
342 * mContent. The second checks that the given image container the
343 * same as the image container on the current request for our
344 * mContent.
345 */
346 bool
347 nsImageFrame::IsPendingLoad(imgIRequest* aRequest) const
348 {
349 // Default to pending load in case of errors
350 nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
351 NS_ASSERTION(imageLoader, "No image loading content?");
353 int32_t requestType = nsIImageLoadingContent::UNKNOWN_REQUEST;
354 imageLoader->GetRequestType(aRequest, &requestType);
356 return requestType != nsIImageLoadingContent::CURRENT_REQUEST;
357 }
359 bool
360 nsImageFrame::IsPendingLoad(imgIContainer* aContainer) const
361 {
362 // default to pending load in case of errors
363 if (!aContainer) {
364 NS_ERROR("No image container!");
365 return true;
366 }
368 nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
369 NS_ASSERTION(imageLoader, "No image loading content?");
371 nsCOMPtr<imgIRequest> currentRequest;
372 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
373 getter_AddRefs(currentRequest));
374 if (!currentRequest) {
375 NS_ERROR("No current request");
376 return true;
377 }
379 nsCOMPtr<imgIContainer> currentContainer;
380 currentRequest->GetImage(getter_AddRefs(currentContainer));
382 return currentContainer != aContainer;
384 }
386 nsRect
387 nsImageFrame::SourceRectToDest(const nsIntRect& aRect)
388 {
389 // When scaling the image, row N of the source image may (depending on
390 // the scaling function) be used to draw any row in the destination image
391 // between floor(F * (N-1)) and ceil(F * (N+1)), where F is the
392 // floating-point scaling factor. The same holds true for columns.
393 // So, we start by computing that bound without the floor and ceiling.
395 nsRect r(nsPresContext::CSSPixelsToAppUnits(aRect.x - 1),
396 nsPresContext::CSSPixelsToAppUnits(aRect.y - 1),
397 nsPresContext::CSSPixelsToAppUnits(aRect.width + 2),
398 nsPresContext::CSSPixelsToAppUnits(aRect.height + 2));
400 nsTransform2D sourceToDest;
401 if (!GetSourceToDestTransform(sourceToDest)) {
402 // Failed to generate transform matrix. Return our whole inner area,
403 // to be on the safe side (since this method is used for generating
404 // invalidation rects).
405 return GetInnerArea();
406 }
408 sourceToDest.TransformCoord(&r.x, &r.y, &r.width, &r.height);
410 // Now, round the edges out to the pixel boundary.
411 nscoord scale = nsPresContext::CSSPixelsToAppUnits(1);
412 nscoord right = r.x + r.width;
413 nscoord bottom = r.y + r.height;
415 r.x -= (scale + (r.x % scale)) % scale;
416 r.y -= (scale + (r.y % scale)) % scale;
417 r.width = right + ((scale - (right % scale)) % scale) - r.x;
418 r.height = bottom + ((scale - (bottom % scale)) % scale) - r.y;
420 return r;
421 }
423 // Note that we treat NS_EVENT_STATE_SUPPRESSED images as "OK". This means
424 // that we'll construct image frames for them as needed if their display is
425 // toggled from "none" (though we won't paint them, unless their visibility
426 // is changed too).
427 #define BAD_STATES (NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED | \
428 NS_EVENT_STATE_LOADING)
430 // This is a macro so that we don't evaluate the boolean last arg
431 // unless we have to; it can be expensive
432 #define IMAGE_OK(_state, _loadingOK) \
433 (!(_state).HasAtLeastOneOfStates(BAD_STATES) || \
434 (!(_state).HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED) && \
435 (_state).HasState(NS_EVENT_STATE_LOADING) && (_loadingOK)))
437 /* static */
438 bool
439 nsImageFrame::ShouldCreateImageFrameFor(Element* aElement,
440 nsStyleContext* aStyleContext)
441 {
442 EventStates state = aElement->State();
443 if (IMAGE_OK(state,
444 HaveFixedSize(aStyleContext->StylePosition()))) {
445 // Image is fine; do the image frame thing
446 return true;
447 }
449 // Check if we want to use a placeholder box with an icon or just
450 // let the presShell make us into inline text. Decide as follows:
451 //
452 // - if our special "force icons" style is set, show an icon
453 // - else if our "do not show placeholders" pref is set, skip the icon
454 // - else:
455 // - if there is a src attribute, there is no alt attribute,
456 // and this is not an <object> (which could not possibly have
457 // such an attribute), show an icon.
458 // - if QuirksMode, and the IMG has a size show an icon.
459 // - otherwise, skip the icon
460 bool useSizedBox;
462 if (aStyleContext->StyleUIReset()->mForceBrokenImageIcon) {
463 useSizedBox = true;
464 }
465 else if (gIconLoad && gIconLoad->mPrefForceInlineAltText) {
466 useSizedBox = false;
467 }
468 else if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
469 !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::alt) &&
470 !aElement->IsHTML(nsGkAtoms::object) &&
471 !aElement->IsHTML(nsGkAtoms::input)) {
472 // Use a sized box if we have no alt text. This means no alt attribute
473 // and the node is not an object or an input (since those always have alt
474 // text).
475 useSizedBox = true;
476 }
477 else if (aStyleContext->PresContext()->CompatibilityMode() !=
478 eCompatibility_NavQuirks) {
479 useSizedBox = false;
480 }
481 else {
482 // check whether we have fixed size
483 useSizedBox = HaveFixedSize(aStyleContext->StylePosition());
484 }
486 return useSizedBox;
487 }
489 nsresult
490 nsImageFrame::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
491 {
492 if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
493 nsCOMPtr<imgIContainer> image;
494 aRequest->GetImage(getter_AddRefs(image));
495 return OnStartContainer(aRequest, image);
496 }
498 if (aType == imgINotificationObserver::FRAME_UPDATE) {
499 return OnDataAvailable(aRequest, aData);
500 }
502 if (aType == imgINotificationObserver::FRAME_COMPLETE) {
503 mFirstFrameComplete = true;
504 }
506 if (aType == imgINotificationObserver::LOAD_COMPLETE) {
507 uint32_t imgStatus;
508 aRequest->GetImageStatus(&imgStatus);
509 nsresult status =
510 imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
511 return OnStopRequest(aRequest, status);
512 }
514 return NS_OK;
515 }
517 static bool
518 SizeIsAvailable(imgIRequest* aRequest)
519 {
520 if (!aRequest)
521 return false;
523 uint32_t imageStatus = 0;
524 nsresult rv = aRequest->GetImageStatus(&imageStatus);
526 return NS_SUCCEEDED(rv) && (imageStatus & imgIRequest::STATUS_SIZE_AVAILABLE);
527 }
529 nsresult
530 nsImageFrame::OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage)
531 {
532 if (!aImage) return NS_ERROR_INVALID_ARG;
534 /* Get requested animation policy from the pres context:
535 * normal = 0
536 * one frame = 1
537 * one loop = 2
538 */
539 nsPresContext *presContext = PresContext();
540 aImage->SetAnimationMode(presContext->ImageAnimationMode());
542 if (IsPendingLoad(aRequest)) {
543 // We don't care
544 return NS_OK;
545 }
547 bool intrinsicSizeChanged = false;
548 if (SizeIsAvailable(aRequest)) {
549 // This is valid and for the current request, so update our stored image
550 // container, orienting according to our style.
551 mImage = nsLayoutUtils::OrientImage(aImage, StyleVisibility()->mImageOrientation);
553 intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
554 intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
555 } else {
556 // We no longer have a valid image, so release our stored image container.
557 mImage = nullptr;
559 // Have to size to 0,0 so that GetDesiredSize recalculates the size.
560 mIntrinsicSize.width.SetCoordValue(0);
561 mIntrinsicSize.height.SetCoordValue(0);
562 mIntrinsicRatio.SizeTo(0, 0);
563 intrinsicSizeChanged = true;
564 }
566 if (intrinsicSizeChanged && (mState & IMAGE_GOTINITIALREFLOW)) {
567 // Now we need to reflow if we have an unconstrained size and have
568 // already gotten the initial reflow
569 if (!(mState & IMAGE_SIZECONSTRAINED)) {
570 nsIPresShell *presShell = presContext->GetPresShell();
571 NS_ASSERTION(presShell, "No PresShell.");
572 if (presShell) {
573 presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
574 NS_FRAME_IS_DIRTY);
575 }
576 }
577 }
579 return NS_OK;
580 }
582 nsresult
583 nsImageFrame::OnDataAvailable(imgIRequest *aRequest,
584 const nsIntRect *aRect)
585 {
586 if (mFirstFrameComplete) {
587 nsCOMPtr<imgIContainer> container;
588 aRequest->GetImage(getter_AddRefs(container));
589 return FrameChanged(aRequest, container);
590 }
592 // XXX do we need to make sure that the reflow from the
593 // OnStartContainer has been processed before we start calling
594 // invalidate?
596 NS_ENSURE_ARG_POINTER(aRect);
598 if (!(mState & IMAGE_GOTINITIALREFLOW)) {
599 // Don't bother to do anything; we have a reflow coming up!
600 return NS_OK;
601 }
603 if (IsPendingLoad(aRequest)) {
604 // We don't care
605 return NS_OK;
606 }
608 #ifdef DEBUG_decode
609 printf("Source rect (%d,%d,%d,%d)\n",
610 aRect->x, aRect->y, aRect->width, aRect->height);
611 #endif
613 if (aRect->IsEqualInterior(nsIntRect::GetMaxSizedIntRect())) {
614 InvalidateFrame(nsDisplayItem::TYPE_IMAGE);
615 InvalidateFrame(nsDisplayItem::TYPE_ALT_FEEDBACK);
616 } else {
617 nsRect invalid = SourceRectToDest(*aRect);
618 InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_IMAGE);
619 InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_ALT_FEEDBACK);
620 }
622 return NS_OK;
623 }
625 nsresult
626 nsImageFrame::OnStopRequest(imgIRequest *aRequest,
627 nsresult aStatus)
628 {
629 // Check what request type we're dealing with
630 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
631 NS_ASSERTION(imageLoader, "Who's notifying us??");
632 int32_t loadType = nsIImageLoadingContent::UNKNOWN_REQUEST;
633 imageLoader->GetRequestType(aRequest, &loadType);
634 if (loadType != nsIImageLoadingContent::CURRENT_REQUEST &&
635 loadType != nsIImageLoadingContent::PENDING_REQUEST) {
636 return NS_ERROR_FAILURE;
637 }
639 NotifyNewCurrentRequest(aRequest, aStatus);
640 return NS_OK;
641 }
643 void
644 nsImageFrame::NotifyNewCurrentRequest(imgIRequest *aRequest,
645 nsresult aStatus)
646 {
647 nsCOMPtr<imgIContainer> image;
648 aRequest->GetImage(getter_AddRefs(image));
649 NS_ASSERTION(image || NS_FAILED(aStatus), "Successful load with no container?");
651 // May have to switch sizes here!
652 bool intrinsicSizeChanged = true;
653 if (NS_SUCCEEDED(aStatus) && image && SizeIsAvailable(aRequest)) {
654 // Update our stored image container, orienting according to our style.
655 mImage = nsLayoutUtils::OrientImage(image, StyleVisibility()->mImageOrientation);
657 intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
658 intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
659 } else {
660 // We no longer have a valid image, so release our stored image container.
661 mImage = nullptr;
663 // Have to size to 0,0 so that GetDesiredSize recalculates the size
664 mIntrinsicSize.width.SetCoordValue(0);
665 mIntrinsicSize.height.SetCoordValue(0);
666 mIntrinsicRatio.SizeTo(0, 0);
667 }
669 if (mState & IMAGE_GOTINITIALREFLOW) { // do nothing if we haven't gotten the initial reflow yet
670 if (!(mState & IMAGE_SIZECONSTRAINED) && intrinsicSizeChanged) {
671 nsIPresShell *presShell = PresContext()->GetPresShell();
672 if (presShell) {
673 presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
674 NS_FRAME_IS_DIRTY);
675 }
676 }
677 // Update border+content to account for image change
678 InvalidateFrame();
679 }
680 }
682 nsresult
683 nsImageFrame::FrameChanged(imgIRequest *aRequest,
684 imgIContainer *aContainer)
685 {
686 if (!StyleVisibility()->IsVisible()) {
687 return NS_OK;
688 }
690 if (IsPendingLoad(aContainer)) {
691 // We don't care about it
692 return NS_OK;
693 }
695 InvalidateLayer(nsDisplayItem::TYPE_IMAGE);
696 return NS_OK;
697 }
699 void
700 nsImageFrame::EnsureIntrinsicSizeAndRatio(nsPresContext* aPresContext)
701 {
702 // If mIntrinsicSize.width and height are 0, then we need to update from the
703 // image container.
704 if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
705 mIntrinsicSize.width.GetCoordValue() == 0 &&
706 mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
707 mIntrinsicSize.height.GetCoordValue() == 0) {
709 if (mImage) {
710 UpdateIntrinsicSize(mImage);
711 UpdateIntrinsicRatio(mImage);
712 } else {
713 // image request is null or image size not known, probably an
714 // invalid image specified
715 // - make the image big enough for the icon (it may not be
716 // used if inline alt expansion is used instead)
717 if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
718 nscoord edgeLengthToUse =
719 nsPresContext::CSSPixelsToAppUnits(
720 ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH)));
721 mIntrinsicSize.width.SetCoordValue(edgeLengthToUse);
722 mIntrinsicSize.height.SetCoordValue(edgeLengthToUse);
723 mIntrinsicRatio.SizeTo(1, 1);
724 }
725 }
726 }
727 }
729 /* virtual */ nsSize
730 nsImageFrame::ComputeSize(nsRenderingContext *aRenderingContext,
731 nsSize aCBSize, nscoord aAvailableWidth,
732 nsSize aMargin, nsSize aBorder, nsSize aPadding,
733 uint32_t aFlags)
734 {
735 nsPresContext *presContext = PresContext();
736 EnsureIntrinsicSizeAndRatio(presContext);
738 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
739 aRenderingContext, this,
740 mIntrinsicSize, mIntrinsicRatio, aCBSize,
741 aMargin, aBorder, aPadding);
742 }
744 nsRect
745 nsImageFrame::GetInnerArea() const
746 {
747 return GetContentRect() - GetPosition();
748 }
750 // get the offset into the content area of the image where aImg starts if it is a continuation.
751 nscoord
752 nsImageFrame::GetContinuationOffset() const
753 {
754 nscoord offset = 0;
755 for (nsIFrame *f = GetPrevInFlow(); f; f = f->GetPrevInFlow()) {
756 offset += f->GetContentRect().height;
757 }
758 NS_ASSERTION(offset >= 0, "bogus GetContentRect");
759 return offset;
760 }
762 /* virtual */ nscoord
763 nsImageFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
764 {
765 // XXX The caller doesn't account for constraints of the height,
766 // min-height, and max-height properties.
767 DebugOnly<nscoord> result;
768 DISPLAY_MIN_WIDTH(this, result);
769 nsPresContext *presContext = PresContext();
770 EnsureIntrinsicSizeAndRatio(presContext);
771 return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
772 mIntrinsicSize.width.GetCoordValue() : 0;
773 }
775 /* virtual */ nscoord
776 nsImageFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
777 {
778 // XXX The caller doesn't account for constraints of the height,
779 // min-height, and max-height properties.
780 DebugOnly<nscoord> result;
781 DISPLAY_PREF_WIDTH(this, result);
782 nsPresContext *presContext = PresContext();
783 EnsureIntrinsicSizeAndRatio(presContext);
784 // convert from normal twips to scaled twips (printing...)
785 return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
786 mIntrinsicSize.width.GetCoordValue() : 0;
787 }
789 /* virtual */ IntrinsicSize
790 nsImageFrame::GetIntrinsicSize()
791 {
792 return mIntrinsicSize;
793 }
795 /* virtual */ nsSize
796 nsImageFrame::GetIntrinsicRatio()
797 {
798 return mIntrinsicRatio;
799 }
801 nsresult
802 nsImageFrame::Reflow(nsPresContext* aPresContext,
803 nsHTMLReflowMetrics& aMetrics,
804 const nsHTMLReflowState& aReflowState,
805 nsReflowStatus& aStatus)
806 {
807 DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
808 DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
809 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
810 ("enter nsImageFrame::Reflow: availSize=%d,%d",
811 aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
813 NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
815 aStatus = NS_FRAME_COMPLETE;
817 // see if we have a frozen size (i.e. a fixed width and height)
818 if (HaveFixedSize(aReflowState)) {
819 mState |= IMAGE_SIZECONSTRAINED;
820 } else {
821 mState &= ~IMAGE_SIZECONSTRAINED;
822 }
824 // XXXldb These two bits are almost exact opposites (except in the
825 // middle of the initial reflow); remove IMAGE_GOTINITIALREFLOW.
826 if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
827 mState |= IMAGE_GOTINITIALREFLOW;
828 }
830 mComputedSize =
831 nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight());
833 aMetrics.Width() = mComputedSize.width;
834 aMetrics.Height() = mComputedSize.height;
836 // add borders and padding
837 aMetrics.Width() += aReflowState.ComputedPhysicalBorderPadding().LeftRight();
838 aMetrics.Height() += aReflowState.ComputedPhysicalBorderPadding().TopBottom();
840 if (GetPrevInFlow()) {
841 aMetrics.Width() = GetPrevInFlow()->GetSize().width;
842 nscoord y = GetContinuationOffset();
843 aMetrics.Height() -= y + aReflowState.ComputedPhysicalBorderPadding().top;
844 aMetrics.Height() = std::max(0, aMetrics.Height());
845 }
848 // we have to split images if we are:
849 // in Paginated mode, we need to have a constrained height, and have a height larger than our available height
850 uint32_t loadStatus = imgIRequest::STATUS_NONE;
851 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
852 NS_ASSERTION(imageLoader, "No content node??");
853 if (imageLoader) {
854 nsCOMPtr<imgIRequest> currentRequest;
855 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
856 getter_AddRefs(currentRequest));
857 if (currentRequest) {
858 currentRequest->GetImageStatus(&loadStatus);
859 }
860 }
861 if (aPresContext->IsPaginated() &&
862 ((loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) || (mState & IMAGE_SIZECONSTRAINED)) &&
863 NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight() &&
864 aMetrics.Height() > aReflowState.AvailableHeight()) {
865 // our desired height was greater than 0, so to avoid infinite
866 // splitting, use 1 pixel as the min
867 aMetrics.Height() = std::max(nsPresContext::CSSPixelsToAppUnits(1), aReflowState.AvailableHeight());
868 aStatus = NS_FRAME_NOT_COMPLETE;
869 }
871 aMetrics.SetOverflowAreasToDesiredBounds();
872 EventStates contentState = mContent->AsElement()->State();
873 bool imageOK = IMAGE_OK(contentState, true);
875 // Determine if the size is available
876 bool haveSize = false;
877 if (loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) {
878 haveSize = true;
879 }
881 if (!imageOK || !haveSize) {
882 nsRect altFeedbackSize(0, 0,
883 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)),
884 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)));
885 // We include the altFeedbackSize in our visual overflow, but not in our
886 // scrollable overflow, since it doesn't really need to be scrolled to
887 // outside the image.
888 static_assert(eOverflowType_LENGTH == 2, "Unknown overflow types?");
889 nsRect& visualOverflow = aMetrics.VisualOverflow();
890 visualOverflow.UnionRect(visualOverflow, altFeedbackSize);
891 }
892 FinishAndStoreOverflow(&aMetrics);
894 if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && !mReflowCallbackPosted) {
895 nsIPresShell* shell = PresContext()->PresShell();
896 mReflowCallbackPosted = true;
897 shell->PostReflowCallback(this);
898 }
900 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
901 ("exit nsImageFrame::Reflow: size=%d,%d",
902 aMetrics.Width(), aMetrics.Height()));
903 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
904 return NS_OK;
905 }
907 bool
908 nsImageFrame::ReflowFinished()
909 {
910 mReflowCallbackPosted = false;
912 nsLayoutUtils::UpdateImageVisibilityForFrame(this);
914 return false;
915 }
917 void
918 nsImageFrame::ReflowCallbackCanceled()
919 {
920 mReflowCallbackPosted = false;
921 }
923 // Computes the width of the specified string. aMaxWidth specifies the maximum
924 // width available. Once this limit is reached no more characters are measured.
925 // The number of characters that fit within the maximum width are returned in
926 // aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected
927 // into the rendering context before this is called (for performance). MMP
928 nscoord
929 nsImageFrame::MeasureString(const char16_t* aString,
930 int32_t aLength,
931 nscoord aMaxWidth,
932 uint32_t& aMaxFit,
933 nsRenderingContext& aContext)
934 {
935 nscoord totalWidth = 0;
936 aContext.SetTextRunRTL(false);
937 nscoord spaceWidth = aContext.GetWidth(' ');
939 aMaxFit = 0;
940 while (aLength > 0) {
941 // Find the next place we can line break
942 uint32_t len = aLength;
943 bool trailingSpace = false;
944 for (int32_t i = 0; i < aLength; i++) {
945 if (dom::IsSpaceCharacter(aString[i]) && (i > 0)) {
946 len = i; // don't include the space when measuring
947 trailingSpace = true;
948 break;
949 }
950 }
952 // Measure this chunk of text, and see if it fits
953 nscoord width =
954 nsLayoutUtils::GetStringWidth(this, &aContext, aString, len);
955 bool fits = (totalWidth + width) <= aMaxWidth;
957 // If it fits on the line, or it's the first word we've processed then
958 // include it
959 if (fits || (0 == totalWidth)) {
960 // New piece fits
961 totalWidth += width;
963 // If there's a trailing space then see if it fits as well
964 if (trailingSpace) {
965 if ((totalWidth + spaceWidth) <= aMaxWidth) {
966 totalWidth += spaceWidth;
967 } else {
968 // Space won't fit. Leave it at the end but don't include it in
969 // the width
970 fits = false;
971 }
973 len++;
974 }
976 aMaxFit += len;
977 aString += len;
978 aLength -= len;
979 }
981 if (!fits) {
982 break;
983 }
984 }
985 return totalWidth;
986 }
988 // Formats the alt-text to fit within the specified rectangle. Breaks lines
989 // between words if a word would extend past the edge of the rectangle
990 void
991 nsImageFrame::DisplayAltText(nsPresContext* aPresContext,
992 nsRenderingContext& aRenderingContext,
993 const nsString& aAltText,
994 const nsRect& aRect)
995 {
996 // Set font and color
997 aRenderingContext.SetColor(StyleColor()->mColor);
998 nsRefPtr<nsFontMetrics> fm;
999 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
1000 nsLayoutUtils::FontSizeInflationFor(this));
1001 aRenderingContext.SetFont(fm);
1003 // Format the text to display within the formatting rect
1005 nscoord maxAscent = fm->MaxAscent();
1006 nscoord maxDescent = fm->MaxDescent();
1007 nscoord height = fm->MaxHeight();
1009 // XXX It would be nice if there was a way to have the font metrics tell
1010 // use where to break the text given a maximum width. At a minimum we need
1011 // to be able to get the break character...
1012 const char16_t* str = aAltText.get();
1013 int32_t strLen = aAltText.Length();
1014 nscoord y = aRect.y;
1016 if (!aPresContext->BidiEnabled() && HasRTLChars(aAltText)) {
1017 aPresContext->SetBidiEnabled();
1018 }
1020 // Always show the first line, even if we have to clip it below
1021 bool firstLine = true;
1022 while ((strLen > 0) && (firstLine || (y + maxDescent) < aRect.YMost())) {
1023 // Determine how much of the text to display on this line
1024 uint32_t maxFit; // number of characters that fit
1025 nscoord strWidth = MeasureString(str, strLen, aRect.width, maxFit,
1026 aRenderingContext);
1028 // Display the text
1029 nsresult rv = NS_ERROR_FAILURE;
1031 if (aPresContext->BidiEnabled()) {
1032 const nsStyleVisibility* vis = StyleVisibility();
1033 if (vis->mDirection == NS_STYLE_DIRECTION_RTL)
1034 rv = nsBidiPresUtils::RenderText(str, maxFit, NSBIDI_RTL,
1035 aPresContext, aRenderingContext,
1036 aRenderingContext,
1037 aRect.XMost() - strWidth, y + maxAscent);
1038 else
1039 rv = nsBidiPresUtils::RenderText(str, maxFit, NSBIDI_LTR,
1040 aPresContext, aRenderingContext,
1041 aRenderingContext,
1042 aRect.x, y + maxAscent);
1043 }
1044 if (NS_FAILED(rv))
1045 aRenderingContext.DrawString(str, maxFit, aRect.x, y + maxAscent);
1047 // Move to the next line
1048 str += maxFit;
1049 strLen -= maxFit;
1050 y += height;
1051 firstLine = false;
1052 }
1053 }
1055 struct nsRecessedBorder : public nsStyleBorder {
1056 nsRecessedBorder(nscoord aBorderWidth, nsPresContext* aPresContext)
1057 : nsStyleBorder(aPresContext)
1058 {
1059 NS_FOR_CSS_SIDES(side) {
1060 // Note: use SetBorderColor here because we want to make sure
1061 // the "special" flags are unset.
1062 SetBorderColor(side, NS_RGB(0, 0, 0));
1063 mBorder.Side(side) = aBorderWidth;
1064 // Note: use SetBorderStyle here because we want to affect
1065 // mComputedBorder
1066 SetBorderStyle(side, NS_STYLE_BORDER_STYLE_INSET);
1067 }
1068 }
1069 };
1071 class nsDisplayAltFeedback : public nsDisplayItem {
1072 public:
1073 nsDisplayAltFeedback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
1074 : nsDisplayItem(aBuilder, aFrame) {}
1076 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
1077 bool* aSnap) MOZ_OVERRIDE
1078 {
1079 *aSnap = false;
1080 return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
1081 }
1083 virtual void Paint(nsDisplayListBuilder* aBuilder,
1084 nsRenderingContext* aCtx) MOZ_OVERRIDE
1085 {
1086 nsImageFrame* f = static_cast<nsImageFrame*>(mFrame);
1087 EventStates state = f->GetContent()->AsElement()->State();
1088 f->DisplayAltFeedback(*aCtx,
1089 mVisibleRect,
1090 IMAGE_OK(state, true)
1091 ? nsImageFrame::gIconLoad->mLoadingImage
1092 : nsImageFrame::gIconLoad->mBrokenImage,
1093 ToReferenceFrame());
1095 }
1097 NS_DISPLAY_DECL_NAME("AltFeedback", TYPE_ALT_FEEDBACK)
1098 };
1100 void
1101 nsImageFrame::DisplayAltFeedback(nsRenderingContext& aRenderingContext,
1102 const nsRect& aDirtyRect,
1103 imgIRequest* aRequest,
1104 nsPoint aPt)
1105 {
1106 // We should definitely have a gIconLoad here.
1107 NS_ABORT_IF_FALSE(gIconLoad, "How did we succeed in Init then?");
1109 // Calculate the inner area
1110 nsRect inner = GetInnerArea() + aPt;
1112 // Display a recessed one pixel border
1113 nscoord borderEdgeWidth = nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH);
1115 // if inner area is empty, then make it big enough for at least the icon
1116 if (inner.IsEmpty()){
1117 inner.SizeTo(2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)),
1118 2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)));
1119 }
1121 // Make sure we have enough room to actually render the border within
1122 // our frame bounds
1123 if ((inner.width < 2 * borderEdgeWidth) || (inner.height < 2 * borderEdgeWidth)) {
1124 return;
1125 }
1127 // Paint the border
1128 nsRecessedBorder recessedBorder(borderEdgeWidth, PresContext());
1129 nsCSSRendering::PaintBorderWithStyleBorder(PresContext(), aRenderingContext,
1130 this, inner, inner,
1131 recessedBorder, mStyleContext);
1133 // Adjust the inner rect to account for the one pixel recessed border,
1134 // and a six pixel padding on each edge
1135 inner.Deflate(nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH),
1136 nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH));
1137 if (inner.IsEmpty()) {
1138 return;
1139 }
1141 // Clip so we don't render outside the inner rect
1142 aRenderingContext.PushState();
1143 aRenderingContext.IntersectClip(inner);
1145 // Check if we should display image placeholders
1146 if (gIconLoad->mPrefShowPlaceholders) {
1147 const nsStyleVisibility* vis = StyleVisibility();
1148 nscoord size = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE);
1150 bool iconUsed = false;
1152 // If we weren't previously displaying an icon, register ourselves
1153 // as an observer for load and animation updates and flag that we're
1154 // doing so now.
1155 if (aRequest && !mDisplayingIcon) {
1156 gIconLoad->AddIconObserver(this);
1157 mDisplayingIcon = true;
1158 }
1161 // If the icon in question is loaded and decoded, draw it
1162 uint32_t imageStatus = 0;
1163 if (aRequest)
1164 aRequest->GetImageStatus(&imageStatus);
1165 if (imageStatus & imgIRequest::STATUS_FRAME_COMPLETE) {
1166 nsCOMPtr<imgIContainer> imgCon;
1167 aRequest->GetImage(getter_AddRefs(imgCon));
1168 NS_ABORT_IF_FALSE(imgCon, "Frame Complete, but no image container?");
1169 nsRect dest((vis->mDirection == NS_STYLE_DIRECTION_RTL) ?
1170 inner.XMost() - size : inner.x,
1171 inner.y, size, size);
1172 nsLayoutUtils::DrawSingleImage(&aRenderingContext, imgCon,
1173 nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect,
1174 nullptr, imgIContainer::FLAG_NONE);
1175 iconUsed = true;
1176 }
1178 // if we could not draw the icon, flag that we're waiting for it and
1179 // just draw some graffiti in the mean time
1180 if (!iconUsed) {
1181 nscoord iconXPos = (vis->mDirection == NS_STYLE_DIRECTION_RTL) ?
1182 inner.XMost() - size : inner.x;
1183 nscoord twoPX = nsPresContext::CSSPixelsToAppUnits(2);
1184 aRenderingContext.DrawRect(iconXPos, inner.y,size,size);
1185 aRenderingContext.PushState();
1186 aRenderingContext.SetColor(NS_RGB(0xFF,0,0));
1187 aRenderingContext.FillEllipse(size/2 + iconXPos, size/2 + inner.y,
1188 size/2 - twoPX, size/2 - twoPX);
1189 aRenderingContext.PopState();
1190 }
1192 // Reduce the inner rect by the width of the icon, and leave an
1193 // additional ICON_PADDING pixels for padding
1194 int32_t iconWidth = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING);
1195 if (vis->mDirection != NS_STYLE_DIRECTION_RTL)
1196 inner.x += iconWidth;
1197 inner.width -= iconWidth;
1198 }
1200 // If there's still room, display the alt-text
1201 if (!inner.IsEmpty()) {
1202 nsIContent* content = GetContent();
1203 if (content) {
1204 nsXPIDLString altText;
1205 nsCSSFrameConstructor::GetAlternateTextFor(content, content->Tag(),
1206 altText);
1207 DisplayAltText(PresContext(), aRenderingContext, altText, inner);
1208 }
1209 }
1211 aRenderingContext.PopState();
1212 }
1214 #ifdef DEBUG
1215 static void PaintDebugImageMap(nsIFrame* aFrame, nsRenderingContext* aCtx,
1216 const nsRect& aDirtyRect, nsPoint aPt) {
1217 nsImageFrame* f = static_cast<nsImageFrame*>(aFrame);
1218 nsRect inner = f->GetInnerArea() + aPt;
1220 aCtx->SetColor(NS_RGB(0, 0, 0));
1221 aCtx->PushState();
1222 aCtx->Translate(inner.TopLeft());
1223 f->GetImageMap()->Draw(aFrame, *aCtx);
1224 aCtx->PopState();
1225 }
1226 #endif
1228 void
1229 nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
1230 nsRenderingContext* aCtx) {
1231 uint32_t flags = imgIContainer::FLAG_NONE;
1232 if (aBuilder->ShouldSyncDecodeImages()) {
1233 flags |= imgIContainer::FLAG_SYNC_DECODE;
1234 }
1235 if (aBuilder->IsPaintingToWindow()) {
1236 flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
1237 }
1238 static_cast<nsImageFrame*>(mFrame)->
1239 PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mImage, flags);
1240 }
1242 void
1243 nsDisplayImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
1244 const nsDisplayItemGeometry* aGeometry,
1245 nsRegion* aInvalidRegion)
1246 {
1247 if (aBuilder->ShouldSyncDecodeImages() && mImage && !mImage->IsDecoded()) {
1248 bool snap;
1249 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
1250 }
1252 nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
1253 }
1255 already_AddRefed<ImageContainer>
1256 nsDisplayImage::GetContainer(LayerManager* aManager,
1257 nsDisplayListBuilder* aBuilder)
1258 {
1259 nsRefPtr<ImageContainer> container;
1260 nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container));
1261 NS_ENSURE_SUCCESS(rv, nullptr);
1262 return container.forget();
1263 }
1265 gfxRect
1266 nsDisplayImage::GetDestRect()
1267 {
1268 int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
1269 nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
1271 nsRect dest = imageFrame->GetInnerArea() + ToReferenceFrame();
1272 gfxRect destRect(dest.x, dest.y, dest.width, dest.height);
1273 destRect.ScaleInverse(factor);
1275 return destRect;
1276 }
1278 LayerState
1279 nsDisplayImage::GetLayerState(nsDisplayListBuilder* aBuilder,
1280 LayerManager* aManager,
1281 const ContainerLayerParameters& aParameters)
1282 {
1283 bool animated = false;
1284 if (!nsLayoutUtils::AnimatedImageLayersEnabled() ||
1285 mImage->GetType() != imgIContainer::TYPE_RASTER ||
1286 NS_FAILED(mImage->GetAnimated(&animated)) ||
1287 !animated) {
1288 if (!aManager->IsCompositingCheap() ||
1289 !nsLayoutUtils::GPUImageScalingEnabled()) {
1290 return LAYER_NONE;
1291 }
1292 }
1294 if (!animated) {
1295 int32_t imageWidth;
1296 int32_t imageHeight;
1297 mImage->GetWidth(&imageWidth);
1298 mImage->GetHeight(&imageHeight);
1300 NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
1302 gfxRect destRect = GetDestRect();
1304 destRect.width *= aParameters.mXScale;
1305 destRect.height *= aParameters.mYScale;
1307 // Calculate the scaling factor for the frame.
1308 gfxSize scale = gfxSize(destRect.width / imageWidth,
1309 destRect.height / imageHeight);
1311 // If we are not scaling at all, no point in separating this into a layer.
1312 if (scale.width == 1.0f && scale.height == 1.0f) {
1313 return LAYER_NONE;
1314 }
1316 // If the target size is pretty small, no point in using a layer.
1317 if (destRect.width * destRect.height < 64 * 64) {
1318 return LAYER_NONE;
1319 }
1320 }
1322 nsRefPtr<ImageContainer> container;
1323 mImage->GetImageContainer(aManager, getter_AddRefs(container));
1324 if (!container) {
1325 return LAYER_NONE;
1326 }
1328 return LAYER_ACTIVE;
1329 }
1331 already_AddRefed<Layer>
1332 nsDisplayImage::BuildLayer(nsDisplayListBuilder* aBuilder,
1333 LayerManager* aManager,
1334 const ContainerLayerParameters& aParameters)
1335 {
1336 nsRefPtr<ImageContainer> container;
1337 nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container));
1338 NS_ENSURE_SUCCESS(rv, nullptr);
1340 nsRefPtr<ImageLayer> layer = static_cast<ImageLayer*>
1341 (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
1342 if (!layer) {
1343 layer = aManager->CreateImageLayer();
1344 if (!layer)
1345 return nullptr;
1346 }
1347 layer->SetContainer(container);
1348 ConfigureLayer(layer, aParameters.mOffset);
1349 return layer.forget();
1350 }
1352 void
1353 nsDisplayImage::ConfigureLayer(ImageLayer *aLayer, const nsIntPoint& aOffset)
1354 {
1355 aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame));
1357 int32_t imageWidth;
1358 int32_t imageHeight;
1359 mImage->GetWidth(&imageWidth);
1360 mImage->GetHeight(&imageHeight);
1362 NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
1364 const gfxRect destRect = GetDestRect();
1366 gfx::Matrix transform;
1367 gfxPoint p = destRect.TopLeft() + aOffset;
1368 transform.Translate(p.x, p.y);
1369 transform.Scale(destRect.Width()/imageWidth,
1370 destRect.Height()/imageHeight);
1371 aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
1372 aLayer->SetVisibleRegion(nsIntRect(0, 0, imageWidth, imageHeight));
1373 }
1375 void
1376 nsImageFrame::PaintImage(nsRenderingContext& aRenderingContext, nsPoint aPt,
1377 const nsRect& aDirtyRect, imgIContainer* aImage,
1378 uint32_t aFlags)
1379 {
1380 // Render the image into our content area (the area inside
1381 // the borders and padding)
1382 NS_ASSERTION(GetInnerArea().width == mComputedSize.width, "bad width");
1383 nsRect inner = GetInnerArea() + aPt;
1384 nsRect dest(inner.TopLeft(), mComputedSize);
1385 dest.y -= GetContinuationOffset();
1387 nsLayoutUtils::DrawSingleImage(&aRenderingContext, aImage,
1388 nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect,
1389 nullptr, aFlags);
1391 nsImageMap* map = GetImageMap();
1392 if (nullptr != map) {
1393 aRenderingContext.PushState();
1394 aRenderingContext.Translate(inner.TopLeft());
1395 aRenderingContext.SetColor(NS_RGB(255, 255, 255));
1396 aRenderingContext.SetLineStyle(nsLineStyle_kSolid);
1397 map->Draw(this, aRenderingContext);
1398 aRenderingContext.SetColor(NS_RGB(0, 0, 0));
1399 aRenderingContext.SetLineStyle(nsLineStyle_kDotted);
1400 map->Draw(this, aRenderingContext);
1401 aRenderingContext.PopState();
1402 }
1403 }
1405 void
1406 nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1407 const nsRect& aDirtyRect,
1408 const nsDisplayListSet& aLists)
1409 {
1410 if (!IsVisibleForPainting(aBuilder))
1411 return;
1413 DisplayBorderBackgroundOutline(aBuilder, aLists);
1415 DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
1416 clip(aBuilder, this, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT);
1418 if (mComputedSize.width != 0 && mComputedSize.height != 0) {
1419 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
1420 NS_ASSERTION(imageLoader, "Not an image loading content?");
1422 nsCOMPtr<imgIRequest> currentRequest;
1423 if (imageLoader) {
1424 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
1425 getter_AddRefs(currentRequest));
1426 }
1428 EventStates contentState = mContent->AsElement()->State();
1429 bool imageOK = IMAGE_OK(contentState, true);
1431 // XXX(seth): The SizeIsAvailable check here should not be necessary - the
1432 // intention is that a non-null mImage means we have a size, but there is
1433 // currently some code that violates this invariant.
1434 if (!imageOK || !mImage || !SizeIsAvailable(currentRequest)) {
1435 // No image yet, or image load failed. Draw the alt-text and an icon
1436 // indicating the status
1437 aLists.Content()->AppendNewToTop(new (aBuilder)
1438 nsDisplayAltFeedback(aBuilder, this));
1439 } else {
1440 aLists.Content()->AppendNewToTop(new (aBuilder)
1441 nsDisplayImage(aBuilder, this, mImage));
1443 // If we were previously displaying an icon, we're not anymore
1444 if (mDisplayingIcon) {
1445 gIconLoad->RemoveIconObserver(this);
1446 mDisplayingIcon = false;
1447 }
1449 #ifdef DEBUG
1450 if (GetShowFrameBorders() && GetImageMap()) {
1451 aLists.Outlines()->AppendNewToTop(new (aBuilder)
1452 nsDisplayGeneric(aBuilder, this, PaintDebugImageMap, "DebugImageMap",
1453 nsDisplayItem::TYPE_DEBUG_IMAGE_MAP));
1454 }
1455 #endif
1456 }
1457 }
1459 if (ShouldDisplaySelection()) {
1460 DisplaySelectionOverlay(aBuilder, aLists.Content(),
1461 nsISelectionDisplay::DISPLAY_IMAGES);
1462 }
1463 }
1465 bool
1466 nsImageFrame::ShouldDisplaySelection()
1467 {
1468 // XXX what on EARTH is this code for?
1469 nsresult result;
1470 nsPresContext* presContext = PresContext();
1471 int16_t displaySelection = presContext->PresShell()->GetSelectionFlags();
1472 if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES))
1473 return false;//no need to check the blue border, we cannot be drawn selected
1474 //insert hook here for image selection drawing
1475 #if IMAGE_EDITOR_CHECK
1476 //check to see if this frame is in an editor context
1477 //isEditor check. this needs to be changed to have better way to check
1478 if (displaySelection == nsISelectionDisplay::DISPLAY_ALL)
1479 {
1480 nsCOMPtr<nsISelectionController> selCon;
1481 result = GetSelectionController(presContext, getter_AddRefs(selCon));
1482 if (NS_SUCCEEDED(result) && selCon)
1483 {
1484 nsCOMPtr<nsISelection> selection;
1485 result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
1486 if (NS_SUCCEEDED(result) && selection)
1487 {
1488 int32_t rangeCount;
1489 selection->GetRangeCount(&rangeCount);
1490 if (rangeCount == 1) //if not one then let code drop to nsFrame::Paint
1491 {
1492 nsCOMPtr<nsIContent> parentContent = mContent->GetParent();
1493 if (parentContent)
1494 {
1495 int32_t thisOffset = parentContent->IndexOf(mContent);
1496 nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(parentContent);
1497 nsCOMPtr<nsIDOMNode> rangeNode;
1498 int32_t rangeOffset;
1499 nsCOMPtr<nsIDOMRange> range;
1500 selection->GetRangeAt(0,getter_AddRefs(range));
1501 if (range)
1502 {
1503 range->GetStartContainer(getter_AddRefs(rangeNode));
1504 range->GetStartOffset(&rangeOffset);
1506 if (parentNode && rangeNode && (rangeNode == parentNode) && rangeOffset == thisOffset)
1507 {
1508 range->GetEndContainer(getter_AddRefs(rangeNode));
1509 range->GetEndOffset(&rangeOffset);
1510 if ((rangeNode == parentNode) && (rangeOffset == (thisOffset +1))) //+1 since that would mean this whole content is selected only
1511 return false; //do not allow nsFrame do draw any further selection
1512 }
1513 }
1514 }
1515 }
1516 }
1517 }
1518 }
1519 #endif
1520 return true;
1521 }
1523 nsImageMap*
1524 nsImageFrame::GetImageMap()
1525 {
1526 if (!mImageMap) {
1527 nsIContent* map = GetMapElement();
1528 if (map) {
1529 mImageMap = new nsImageMap();
1530 NS_ADDREF(mImageMap);
1531 mImageMap->Init(this, map);
1532 }
1533 }
1535 return mImageMap;
1536 }
1538 bool
1539 nsImageFrame::IsServerImageMap()
1540 {
1541 return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::ismap);
1542 }
1544 // Translate an point that is relative to our frame
1545 // into a localized pixel coordinate that is relative to the
1546 // content area of this frame (inside the border+padding).
1547 void
1548 nsImageFrame::TranslateEventCoords(const nsPoint& aPoint,
1549 nsIntPoint& aResult)
1550 {
1551 nscoord x = aPoint.x;
1552 nscoord y = aPoint.y;
1554 // Subtract out border and padding here so that the coordinates are
1555 // now relative to the content area of this frame.
1556 nsRect inner = GetInnerArea();
1557 x -= inner.x;
1558 y -= inner.y;
1560 aResult.x = nsPresContext::AppUnitsToIntCSSPixels(x);
1561 aResult.y = nsPresContext::AppUnitsToIntCSSPixels(y);
1562 }
1564 bool
1565 nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget,
1566 nsIContent** aNode)
1567 {
1568 bool status = false;
1569 aTarget.Truncate();
1570 *aHref = nullptr;
1571 *aNode = nullptr;
1573 // Walk up the content tree, looking for an nsIDOMAnchorElement
1574 for (nsIContent* content = mContent->GetParent();
1575 content; content = content->GetParent()) {
1576 nsCOMPtr<dom::Link> link(do_QueryInterface(content));
1577 if (link) {
1578 nsCOMPtr<nsIURI> href = content->GetHrefURI();
1579 if (href) {
1580 href->Clone(aHref);
1581 }
1582 status = (*aHref != nullptr);
1584 nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(content));
1585 if (anchor) {
1586 anchor->GetTarget(aTarget);
1587 }
1588 NS_ADDREF(*aNode = content);
1589 break;
1590 }
1591 }
1592 return status;
1593 }
1595 nsresult
1596 nsImageFrame::GetContentForEvent(WidgetEvent* aEvent,
1597 nsIContent** aContent)
1598 {
1599 NS_ENSURE_ARG_POINTER(aContent);
1601 nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
1602 if (f != this) {
1603 return f->GetContentForEvent(aEvent, aContent);
1604 }
1606 // XXX We need to make this special check for area element's capturing the
1607 // mouse due to bug 135040. Remove it once that's fixed.
1608 nsIContent* capturingContent =
1609 aEvent->HasMouseEventMessage() ? nsIPresShell::GetCapturingContent() :
1610 nullptr;
1611 if (capturingContent && capturingContent->GetPrimaryFrame() == this) {
1612 *aContent = capturingContent;
1613 NS_IF_ADDREF(*aContent);
1614 return NS_OK;
1615 }
1617 nsImageMap* map = GetImageMap();
1619 if (nullptr != map) {
1620 nsIntPoint p;
1621 TranslateEventCoords(
1622 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
1623 nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
1624 if (area) {
1625 area.forget(aContent);
1626 return NS_OK;
1627 }
1628 }
1630 *aContent = GetContent();
1631 NS_IF_ADDREF(*aContent);
1632 return NS_OK;
1633 }
1635 // XXX what should clicks on transparent pixels do?
1636 nsresult
1637 nsImageFrame::HandleEvent(nsPresContext* aPresContext,
1638 WidgetGUIEvent* aEvent,
1639 nsEventStatus* aEventStatus)
1640 {
1641 NS_ENSURE_ARG_POINTER(aEventStatus);
1643 if ((aEvent->message == NS_MOUSE_BUTTON_UP &&
1644 aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) ||
1645 aEvent->message == NS_MOUSE_MOVE) {
1646 nsImageMap* map = GetImageMap();
1647 bool isServerMap = IsServerImageMap();
1648 if ((nullptr != map) || isServerMap) {
1649 nsIntPoint p;
1650 TranslateEventCoords(
1651 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
1652 bool inside = false;
1653 // Even though client-side image map triggering happens
1654 // through content, we need to make sure we're not inside
1655 // (in case we deal with a case of both client-side and
1656 // sever-side on the same image - it happens!)
1657 if (nullptr != map) {
1658 inside = !!map->GetArea(p.x, p.y);
1659 }
1661 if (!inside && isServerMap) {
1663 // Server side image maps use the href in a containing anchor
1664 // element to provide the basis for the destination url.
1665 nsCOMPtr<nsIURI> uri;
1666 nsAutoString target;
1667 nsCOMPtr<nsIContent> anchorNode;
1668 if (GetAnchorHREFTargetAndNode(getter_AddRefs(uri), target,
1669 getter_AddRefs(anchorNode))) {
1670 // XXX if the mouse is over/clicked in the border/padding area
1671 // we should probably just pretend nothing happened. Nav4
1672 // keeps the x,y coordinates positive as we do; IE doesn't
1673 // bother. Both of them send the click through even when the
1674 // mouse is over the border.
1675 if (p.x < 0) p.x = 0;
1676 if (p.y < 0) p.y = 0;
1677 nsAutoCString spec;
1678 uri->GetSpec(spec);
1679 spec += nsPrintfCString("?%d,%d", p.x, p.y);
1680 uri->SetSpec(spec);
1682 bool clicked = false;
1683 if (aEvent->message == NS_MOUSE_BUTTON_UP) {
1684 *aEventStatus = nsEventStatus_eConsumeDoDefault;
1685 clicked = true;
1686 }
1687 nsContentUtils::TriggerLink(anchorNode, aPresContext, uri, target,
1688 clicked, true, true);
1689 }
1690 }
1691 }
1692 }
1694 return nsSplittableFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
1695 }
1697 nsresult
1698 nsImageFrame::GetCursor(const nsPoint& aPoint,
1699 nsIFrame::Cursor& aCursor)
1700 {
1701 nsImageMap* map = GetImageMap();
1702 if (nullptr != map) {
1703 nsIntPoint p;
1704 TranslateEventCoords(aPoint, p);
1705 nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
1706 if (area) {
1707 // Use the cursor from the style of the *area* element.
1708 // XXX Using the image as the parent style context isn't
1709 // technically correct, but it's probably the right thing to do
1710 // here, since it means that areas on which the cursor isn't
1711 // specified will inherit the style from the image.
1712 nsRefPtr<nsStyleContext> areaStyle =
1713 PresContext()->PresShell()->StyleSet()->
1714 ResolveStyleFor(area->AsElement(), StyleContext());
1715 FillCursorInformationFromStyle(areaStyle->StyleUserInterface(),
1716 aCursor);
1717 if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
1718 aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
1719 }
1720 return NS_OK;
1721 }
1722 }
1723 return nsFrame::GetCursor(aPoint, aCursor);
1724 }
1726 nsresult
1727 nsImageFrame::AttributeChanged(int32_t aNameSpaceID,
1728 nsIAtom* aAttribute,
1729 int32_t aModType)
1730 {
1731 nsresult rv = nsSplittableFrame::AttributeChanged(aNameSpaceID,
1732 aAttribute, aModType);
1733 if (NS_FAILED(rv)) {
1734 return rv;
1735 }
1736 if (nsGkAtoms::alt == aAttribute)
1737 {
1738 PresContext()->PresShell()->FrameNeedsReflow(this,
1739 nsIPresShell::eStyleChange,
1740 NS_FRAME_IS_DIRTY);
1741 }
1743 return NS_OK;
1744 }
1746 nsIAtom*
1747 nsImageFrame::GetType() const
1748 {
1749 return nsGkAtoms::imageFrame;
1750 }
1752 #ifdef DEBUG_FRAME_DUMP
1753 nsresult
1754 nsImageFrame::GetFrameName(nsAString& aResult) const
1755 {
1756 return MakeFrameName(NS_LITERAL_STRING("ImageFrame"), aResult);
1757 }
1759 void
1760 nsImageFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
1761 {
1762 nsCString str;
1763 ListGeneric(str, aPrefix, aFlags);
1765 // output the img src url
1766 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
1767 if (imageLoader) {
1768 nsCOMPtr<imgIRequest> currentRequest;
1769 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
1770 getter_AddRefs(currentRequest));
1771 if (currentRequest) {
1772 nsCOMPtr<nsIURI> uri;
1773 currentRequest->GetURI(getter_AddRefs(uri));
1774 nsAutoCString uristr;
1775 uri->GetAsciiSpec(uristr);
1776 str += nsPrintfCString(" [src=%s]", uristr.get());
1777 }
1778 }
1779 fprintf_stderr(out, "%s\n", str.get());
1780 }
1781 #endif
1783 int
1784 nsImageFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
1785 {
1786 int skip = 0;
1787 if (nullptr != GetPrevInFlow()) {
1788 skip |= LOGICAL_SIDE_B_START;
1789 }
1790 if (nullptr != GetNextInFlow()) {
1791 skip |= LOGICAL_SIDE_B_END;
1792 }
1793 return skip;
1794 }
1796 nsresult
1797 nsImageFrame::GetIntrinsicImageSize(nsSize& aSize)
1798 {
1799 if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
1800 mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
1801 aSize.SizeTo(mIntrinsicSize.width.GetCoordValue(),
1802 mIntrinsicSize.height.GetCoordValue());
1803 return NS_OK;
1804 }
1806 return NS_ERROR_FAILURE;
1807 }
1809 nsresult
1810 nsImageFrame::LoadIcon(const nsAString& aSpec,
1811 nsPresContext *aPresContext,
1812 imgRequestProxy** aRequest)
1813 {
1814 nsresult rv = NS_OK;
1815 NS_PRECONDITION(!aSpec.IsEmpty(), "What happened??");
1816 NS_PRECONDITION(aPresContext, "NULL PresContext");
1818 if (!sIOService) {
1819 rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
1820 NS_ENSURE_SUCCESS(rv, rv);
1821 }
1823 nsCOMPtr<nsIURI> realURI;
1824 SpecToURI(aSpec, sIOService, getter_AddRefs(realURI));
1826 nsRefPtr<imgLoader> il =
1827 nsContentUtils::GetImgLoaderForDocument(aPresContext->Document());
1829 nsCOMPtr<nsILoadGroup> loadGroup;
1830 GetLoadGroup(aPresContext, getter_AddRefs(loadGroup));
1832 // For icon loads, we don't need to merge with the loadgroup flags
1833 nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
1835 nsCOMPtr<nsIURI> firstPartyIsolationURI;
1836 nsCOMPtr<mozIThirdPartyUtil> thirdPartySvc
1837 = do_GetService(THIRDPARTYUTIL_CONTRACTID);
1838 // XXX: Should we pass the loadgroup, too? Is document ever likely
1839 // to be unset?
1840 thirdPartySvc->GetFirstPartyIsolationURI(nullptr, aPresContext->Document(),
1841 getter_AddRefs(firstPartyIsolationURI));
1843 return il->LoadImage(realURI, /* icon URI */
1844 firstPartyIsolationURI, /* initial document URI; this is only
1845 relevant for cookies, so does not
1846 apply to icons. */
1847 nullptr, /* referrer (not relevant for icons) */
1848 nullptr, /* principal (not relevant for icons) */
1849 loadGroup,
1850 gIconLoad,
1851 nullptr, /* Not associated with any particular document */
1852 loadFlags,
1853 nullptr,
1854 nullptr, /* channel policy not needed */
1855 EmptyString(),
1856 aRequest);
1857 }
1859 void
1860 nsImageFrame::GetDocumentCharacterSet(nsACString& aCharset) const
1861 {
1862 if (mContent) {
1863 NS_ASSERTION(mContent->GetDocument(),
1864 "Frame still alive after content removed from document!");
1865 aCharset = mContent->GetDocument()->GetDocumentCharacterSet();
1866 }
1867 }
1869 void
1870 nsImageFrame::SpecToURI(const nsAString& aSpec, nsIIOService *aIOService,
1871 nsIURI **aURI)
1872 {
1873 nsCOMPtr<nsIURI> baseURI;
1874 if (mContent) {
1875 baseURI = mContent->GetBaseURI();
1876 }
1877 nsAutoCString charset;
1878 GetDocumentCharacterSet(charset);
1879 NS_NewURI(aURI, aSpec,
1880 charset.IsEmpty() ? nullptr : charset.get(),
1881 baseURI, aIOService);
1882 }
1884 void
1885 nsImageFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
1886 {
1887 if (!aPresContext)
1888 return;
1890 NS_PRECONDITION(nullptr != aLoadGroup, "null OUT parameter pointer");
1892 nsIPresShell *shell = aPresContext->GetPresShell();
1894 if (!shell)
1895 return;
1897 nsIDocument *doc = shell->GetDocument();
1898 if (!doc)
1899 return;
1901 *aLoadGroup = doc->GetDocumentLoadGroup().take();
1902 }
1904 nsresult nsImageFrame::LoadIcons(nsPresContext *aPresContext)
1905 {
1906 NS_ASSERTION(!gIconLoad, "called LoadIcons twice");
1908 NS_NAMED_LITERAL_STRING(loadingSrc,"resource://gre-resources/loading-image.png");
1909 NS_NAMED_LITERAL_STRING(brokenSrc,"resource://gre-resources/broken-image.png");
1911 gIconLoad = new IconLoad();
1912 NS_ADDREF(gIconLoad);
1914 nsresult rv;
1915 // create a loader and load the images
1916 rv = LoadIcon(loadingSrc,
1917 aPresContext,
1918 getter_AddRefs(gIconLoad->mLoadingImage));
1919 if (NS_FAILED(rv)) {
1920 return rv;
1921 }
1923 rv = LoadIcon(brokenSrc,
1924 aPresContext,
1925 getter_AddRefs(gIconLoad->mBrokenImage));
1926 return rv;
1927 }
1929 NS_IMPL_ISUPPORTS(nsImageFrame::IconLoad, nsIObserver,
1930 imgINotificationObserver)
1932 static const char* kIconLoadPrefs[] = {
1933 "browser.display.force_inline_alttext",
1934 "browser.display.show_image_placeholders",
1935 nullptr
1936 };
1938 nsImageFrame::IconLoad::IconLoad()
1939 {
1940 // register observers
1941 Preferences::AddStrongObservers(this, kIconLoadPrefs);
1942 GetPrefs();
1943 }
1945 void
1946 nsImageFrame::IconLoad::Shutdown()
1947 {
1948 Preferences::RemoveObservers(this, kIconLoadPrefs);
1949 // in case the pref service releases us later
1950 if (mLoadingImage) {
1951 mLoadingImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
1952 mLoadingImage = nullptr;
1953 }
1954 if (mBrokenImage) {
1955 mBrokenImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
1956 mBrokenImage = nullptr;
1957 }
1958 }
1960 NS_IMETHODIMP
1961 nsImageFrame::IconLoad::Observe(nsISupports *aSubject, const char* aTopic,
1962 const char16_t* aData)
1963 {
1964 NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
1965 "wrong topic");
1966 #ifdef DEBUG
1967 // assert |aData| is one of our prefs.
1968 for (uint32_t i = 0; i < ArrayLength(kIconLoadPrefs) ||
1969 (NS_NOTREACHED("wrong pref"), false); ++i)
1970 if (NS_ConvertASCIItoUTF16(kIconLoadPrefs[i]) == nsDependentString(aData))
1971 break;
1972 #endif
1974 GetPrefs();
1975 return NS_OK;
1976 }
1978 void nsImageFrame::IconLoad::GetPrefs()
1979 {
1980 mPrefForceInlineAltText =
1981 Preferences::GetBool("browser.display.force_inline_alttext");
1983 mPrefShowPlaceholders =
1984 Preferences::GetBool("browser.display.show_image_placeholders", true);
1985 }
1987 NS_IMETHODIMP
1988 nsImageFrame::IconLoad::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
1989 {
1990 if (aType != imgINotificationObserver::LOAD_COMPLETE &&
1991 aType != imgINotificationObserver::FRAME_UPDATE) {
1992 return NS_OK;
1993 }
1995 nsTObserverArray<nsImageFrame*>::ForwardIterator iter(mIconObservers);
1996 nsImageFrame *frame;
1997 while (iter.HasMore()) {
1998 frame = iter.GetNext();
1999 frame->InvalidateFrame();
2000 }
2002 return NS_OK;
2003 }
2005 NS_IMPL_ISUPPORTS(nsImageListener, imgINotificationObserver)
2007 nsImageListener::nsImageListener(nsImageFrame *aFrame) :
2008 mFrame(aFrame)
2009 {
2010 }
2012 nsImageListener::~nsImageListener()
2013 {
2014 }
2016 NS_IMETHODIMP
2017 nsImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
2018 {
2019 if (!mFrame)
2020 return NS_ERROR_FAILURE;
2022 return mFrame->Notify(aRequest, aType, aData);
2023 }
2025 static bool
2026 IsInAutoWidthTableCellForQuirk(nsIFrame *aFrame)
2027 {
2028 if (eCompatibility_NavQuirks != aFrame->PresContext()->CompatibilityMode())
2029 return false;
2030 // Check if the parent of the closest nsBlockFrame has auto width.
2031 nsBlockFrame *ancestor = nsLayoutUtils::FindNearestBlockAncestor(aFrame);
2032 if (ancestor->StyleContext()->GetPseudo() == nsCSSAnonBoxes::cellContent) {
2033 // Assume direct parent is a table cell frame.
2034 nsFrame *grandAncestor = static_cast<nsFrame*>(ancestor->GetParent());
2035 return grandAncestor &&
2036 grandAncestor->StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto;
2037 }
2038 return false;
2039 }
2041 /* virtual */ void
2042 nsImageFrame::AddInlineMinWidth(nsRenderingContext *aRenderingContext,
2043 nsIFrame::InlineMinWidthData *aData)
2044 {
2046 NS_ASSERTION(GetParent(), "Must have a parent if we get here!");
2048 nsIFrame* parent = GetParent();
2049 bool canBreak =
2050 !CanContinueTextRun() &&
2051 parent->StyleText()->WhiteSpaceCanWrap(parent) &&
2052 !IsInAutoWidthTableCellForQuirk(this);
2054 if (canBreak)
2055 aData->OptionallyBreak(aRenderingContext);
2057 aData->trailingWhitespace = 0;
2058 aData->skipWhitespace = false;
2059 aData->trailingTextFrame = nullptr;
2060 aData->currentLine += nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
2061 this, nsLayoutUtils::MIN_WIDTH);
2062 aData->atStartOfLine = false;
2064 if (canBreak)
2065 aData->OptionallyBreak(aRenderingContext);
2067 }