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