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