/src/mozilla-central/layout/xul/nsImageBoxFrame.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | // |
8 | | // Eric Vaughan |
9 | | // Netscape Communications |
10 | | // |
11 | | // See documentation in associated header file |
12 | | // |
13 | | |
14 | | #include "gfxContext.h" |
15 | | #include "nsImageBoxFrame.h" |
16 | | #include "nsGkAtoms.h" |
17 | | #include "mozilla/ComputedStyle.h" |
18 | | #include "nsStyleConsts.h" |
19 | | #include "nsStyleUtil.h" |
20 | | #include "nsCOMPtr.h" |
21 | | #include "nsPresContext.h" |
22 | | #include "nsBoxLayoutState.h" |
23 | | |
24 | | #include "nsHTMLParts.h" |
25 | | #include "nsString.h" |
26 | | #include "nsLeafFrame.h" |
27 | | #include "nsIPresShell.h" |
28 | | #include "nsIDocument.h" |
29 | | #include "nsImageMap.h" |
30 | | #include "nsILinkHandler.h" |
31 | | #include "nsIURL.h" |
32 | | #include "nsILoadGroup.h" |
33 | | #include "nsContainerFrame.h" |
34 | | #include "nsCSSRendering.h" |
35 | | #include "nsNameSpaceManager.h" |
36 | | #include "nsTextFragment.h" |
37 | | #include "nsTransform2D.h" |
38 | | #include "nsITheme.h" |
39 | | |
40 | | #include "nsIServiceManager.h" |
41 | | #include "nsIURI.h" |
42 | | #include "nsThreadUtils.h" |
43 | | #include "nsDisplayList.h" |
44 | | #include "ImageLayers.h" |
45 | | #include "ImageContainer.h" |
46 | | #include "nsIContent.h" |
47 | | |
48 | | #include "nsContentUtils.h" |
49 | | |
50 | | #include "mozilla/BasicEvents.h" |
51 | | #include "mozilla/EventDispatcher.h" |
52 | | #include "mozilla/Maybe.h" |
53 | | #include "SVGImageContext.h" |
54 | | #include "Units.h" |
55 | | #include "mozilla/layers/WebRenderLayerManager.h" |
56 | | |
57 | | #if defined(XP_WIN) |
58 | | // Undefine LoadImage to prevent naming conflict with Windows. |
59 | | #undef LoadImage |
60 | | #endif |
61 | | |
62 | | #define ONLOAD_CALLED_TOO_EARLY 1 |
63 | | |
64 | | using namespace mozilla; |
65 | | using namespace mozilla::gfx; |
66 | | using namespace mozilla::image; |
67 | | using namespace mozilla::layers; |
68 | | |
69 | | class nsImageBoxFrameEvent : public Runnable |
70 | | { |
71 | | public: |
72 | | nsImageBoxFrameEvent(nsIContent* content, EventMessage message) |
73 | | : mozilla::Runnable("nsImageBoxFrameEvent") |
74 | | , mContent(content) |
75 | | , mMessage(message) |
76 | 0 | { |
77 | 0 | } |
78 | | |
79 | | NS_IMETHOD Run() override; |
80 | | |
81 | | private: |
82 | | nsCOMPtr<nsIContent> mContent; |
83 | | EventMessage mMessage; |
84 | | }; |
85 | | |
86 | | NS_IMETHODIMP |
87 | | nsImageBoxFrameEvent::Run() |
88 | 0 | { |
89 | 0 | RefPtr<nsPresContext> pres_context = mContent->OwnerDoc()->GetPresContext(); |
90 | 0 | if (!pres_context) { |
91 | 0 | return NS_OK; |
92 | 0 | } |
93 | 0 | |
94 | 0 | nsEventStatus status = nsEventStatus_eIgnore; |
95 | 0 | WidgetEvent event(true, mMessage); |
96 | 0 |
|
97 | 0 | event.mFlags.mBubbles = false; |
98 | 0 | EventDispatcher::Dispatch(mContent, pres_context, &event, nullptr, &status); |
99 | 0 | return NS_OK; |
100 | 0 | } |
101 | | |
102 | | // Fire off an event that'll asynchronously call the image elements |
103 | | // onload handler once handled. This is needed since the image library |
104 | | // can't decide if it wants to call its observer methods |
105 | | // synchronously or asynchronously. If an image is loaded from the |
106 | | // cache the notifications come back synchronously, but if the image |
107 | | // is loaded from the network the notifications come back |
108 | | // asynchronously. |
109 | | static void |
110 | | FireImageDOMEvent(nsIContent* aContent, EventMessage aMessage) |
111 | 0 | { |
112 | 0 | NS_ASSERTION(aMessage == eLoad || aMessage == eLoadError, |
113 | 0 | "invalid message"); |
114 | 0 |
|
115 | 0 | nsCOMPtr<nsIRunnable> event = new nsImageBoxFrameEvent(aContent, aMessage); |
116 | 0 | nsresult rv = aContent->OwnerDoc()->Dispatch(TaskCategory::Other, |
117 | 0 | event.forget()); |
118 | 0 | if (NS_FAILED(rv)) { |
119 | 0 | NS_WARNING("failed to dispatch image event"); |
120 | 0 | } |
121 | 0 | } |
122 | | |
123 | | // |
124 | | // NS_NewImageBoxFrame |
125 | | // |
126 | | // Creates a new image frame and returns it |
127 | | // |
128 | | nsIFrame* |
129 | | NS_NewImageBoxFrame (nsIPresShell* aPresShell, ComputedStyle* aStyle) |
130 | 0 | { |
131 | 0 | return new (aPresShell) nsImageBoxFrame(aStyle); |
132 | 0 | } |
133 | | |
134 | | NS_IMPL_FRAMEARENA_HELPERS(nsImageBoxFrame) |
135 | | |
136 | | nsresult |
137 | | nsImageBoxFrame::AttributeChanged(int32_t aNameSpaceID, |
138 | | nsAtom* aAttribute, |
139 | | int32_t aModType) |
140 | 0 | { |
141 | 0 | nsresult rv = nsLeafBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, |
142 | 0 | aModType); |
143 | 0 |
|
144 | 0 | if (aAttribute == nsGkAtoms::src) { |
145 | 0 | UpdateImage(); |
146 | 0 | PresShell()-> |
147 | 0 | FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); |
148 | 0 | } |
149 | 0 | else if (aAttribute == nsGkAtoms::validate) |
150 | 0 | UpdateLoadFlags(); |
151 | 0 |
|
152 | 0 | return rv; |
153 | 0 | } |
154 | | |
155 | | nsImageBoxFrame::nsImageBoxFrame(ComputedStyle* aStyle) |
156 | | : nsLeafBoxFrame(aStyle, kClassID) |
157 | | , mIntrinsicSize(0, 0) |
158 | | , mLoadFlags(nsIRequest::LOAD_NORMAL) |
159 | | , mRequestRegistered(false) |
160 | | , mUseSrcAttr(false) |
161 | | , mSuppressStyleCheck(false) |
162 | 0 | { |
163 | 0 | MarkIntrinsicISizesDirty(); |
164 | 0 | } |
165 | | |
166 | | nsImageBoxFrame::~nsImageBoxFrame() |
167 | 0 | { |
168 | 0 | } |
169 | | |
170 | | |
171 | | /* virtual */ void |
172 | | nsImageBoxFrame::MarkIntrinsicISizesDirty() |
173 | 0 | { |
174 | 0 | SizeNeedsRecalc(mImageSize); |
175 | 0 | nsLeafBoxFrame::MarkIntrinsicISizesDirty(); |
176 | 0 | } |
177 | | |
178 | | void |
179 | | nsImageBoxFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) |
180 | 0 | { |
181 | 0 | if (mImageRequest) { |
182 | 0 | nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest, |
183 | 0 | &mRequestRegistered); |
184 | 0 |
|
185 | 0 | mImageRequest->UnlockImage(); |
186 | 0 |
|
187 | 0 | // Release image loader first so that it's refcnt can go to zero |
188 | 0 | mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE); |
189 | 0 | } |
190 | 0 |
|
191 | 0 | if (mListener) |
192 | 0 | reinterpret_cast<nsImageBoxListener*>(mListener.get())->ClearFrame(); // set the frame to null so we don't send messages to a dead object. |
193 | 0 |
|
194 | 0 | nsLeafBoxFrame::DestroyFrom(aDestructRoot, aPostDestroyData); |
195 | 0 | } |
196 | | |
197 | | |
198 | | void |
199 | | nsImageBoxFrame::Init(nsIContent* aContent, |
200 | | nsContainerFrame* aParent, |
201 | | nsIFrame* aPrevInFlow) |
202 | 0 | { |
203 | 0 | if (!mListener) { |
204 | 0 | RefPtr<nsImageBoxListener> listener = new nsImageBoxListener(this); |
205 | 0 | mListener = listener.forget(); |
206 | 0 | } |
207 | 0 |
|
208 | 0 | mSuppressStyleCheck = true; |
209 | 0 | nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow); |
210 | 0 | mSuppressStyleCheck = false; |
211 | 0 |
|
212 | 0 | UpdateLoadFlags(); |
213 | 0 | UpdateImage(); |
214 | 0 | } |
215 | | |
216 | | void |
217 | | nsImageBoxFrame::UpdateImage() |
218 | 0 | { |
219 | 0 | nsPresContext* presContext = PresContext(); |
220 | 0 |
|
221 | 0 | RefPtr<imgRequestProxy> oldImageRequest = mImageRequest; |
222 | 0 |
|
223 | 0 | if (mImageRequest) { |
224 | 0 | nsLayoutUtils::DeregisterImageRequest(presContext, mImageRequest, |
225 | 0 | &mRequestRegistered); |
226 | 0 | mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE); |
227 | 0 | mImageRequest = nullptr; |
228 | 0 | } |
229 | 0 |
|
230 | 0 | // get the new image src |
231 | 0 | nsAutoString src; |
232 | 0 | mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src); |
233 | 0 | mUseSrcAttr = !src.IsEmpty(); |
234 | 0 | if (mUseSrcAttr) { |
235 | 0 | nsIDocument* doc = mContent->GetComposedDoc(); |
236 | 0 | if (doc) { |
237 | 0 | nsContentPolicyType contentPolicyType; |
238 | 0 | nsCOMPtr<nsIPrincipal> triggeringPrincipal; |
239 | 0 | uint64_t requestContextID = 0; |
240 | 0 | nsContentUtils::GetContentPolicyTypeForUIImageLoading(mContent, |
241 | 0 | getter_AddRefs(triggeringPrincipal), |
242 | 0 | contentPolicyType, |
243 | 0 | &requestContextID); |
244 | 0 |
|
245 | 0 | nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI(); |
246 | 0 | nsCOMPtr<nsIURI> uri; |
247 | 0 | nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), |
248 | 0 | src, |
249 | 0 | doc, |
250 | 0 | baseURI); |
251 | 0 | if (uri) { |
252 | 0 | nsresult rv = nsContentUtils::LoadImage(uri, mContent, doc, |
253 | 0 | triggeringPrincipal, |
254 | 0 | requestContextID, |
255 | 0 | doc->GetDocumentURI(), |
256 | 0 | doc->GetReferrerPolicy(), |
257 | 0 | mListener, mLoadFlags, |
258 | 0 | EmptyString(), |
259 | 0 | getter_AddRefs(mImageRequest), |
260 | 0 | contentPolicyType); |
261 | 0 |
|
262 | 0 | if (NS_SUCCEEDED(rv) && mImageRequest) { |
263 | 0 | nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, |
264 | 0 | mImageRequest, |
265 | 0 | &mRequestRegistered); |
266 | 0 | } |
267 | 0 | } |
268 | 0 | } |
269 | 0 | } else { |
270 | 0 | // Only get the list-style-image if we aren't being drawn |
271 | 0 | // by a native theme. |
272 | 0 | auto* display = StyleDisplay(); |
273 | 0 | if (!(display->HasAppearance() && nsBox::gTheme && |
274 | 0 | nsBox::gTheme->ThemeSupportsWidget(nullptr, this, display->mAppearance))) { |
275 | 0 | // get the list-style-image |
276 | 0 | imgRequestProxy *styleRequest = StyleList()->GetListStyleImage(); |
277 | 0 | if (styleRequest) { |
278 | 0 | styleRequest->SyncClone(mListener, |
279 | 0 | mContent->GetComposedDoc(), |
280 | 0 | getter_AddRefs(mImageRequest)); |
281 | 0 | } |
282 | 0 | } |
283 | 0 | } |
284 | 0 |
|
285 | 0 | if (!mImageRequest) { |
286 | 0 | // We have no image, so size to 0 |
287 | 0 | mIntrinsicSize.SizeTo(0, 0); |
288 | 0 | } else { |
289 | 0 | // We don't want discarding or decode-on-draw for xul images. |
290 | 0 | mImageRequest->StartDecoding(imgIContainer::FLAG_NONE); |
291 | 0 | mImageRequest->LockImage(); |
292 | 0 | } |
293 | 0 |
|
294 | 0 | // Do this _after_ locking the new image in case they are the same image. |
295 | 0 | if (oldImageRequest) { |
296 | 0 | oldImageRequest->UnlockImage(); |
297 | 0 | } |
298 | 0 | } |
299 | | |
300 | | void |
301 | | nsImageBoxFrame::UpdateLoadFlags() |
302 | | { |
303 | | static Element::AttrValuesArray strings[] = |
304 | | {&nsGkAtoms::always, &nsGkAtoms::never, nullptr}; |
305 | | switch (mContent->AsElement()->FindAttrValueIn(kNameSpaceID_None, |
306 | | nsGkAtoms::validate, strings, |
307 | | eCaseMatters)) { |
308 | | case 0: |
309 | | mLoadFlags = nsIRequest::VALIDATE_ALWAYS; |
310 | | break; |
311 | | case 1: |
312 | | mLoadFlags = nsIRequest::VALIDATE_NEVER|nsIRequest::LOAD_FROM_CACHE; |
313 | | break; |
314 | | default: |
315 | | mLoadFlags = nsIRequest::LOAD_NORMAL; |
316 | | break; |
317 | | } |
318 | | } |
319 | | |
320 | | void |
321 | | nsImageBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
322 | | const nsDisplayListSet& aLists) |
323 | 0 | { |
324 | 0 | nsLeafBoxFrame::BuildDisplayList(aBuilder, aLists); |
325 | 0 |
|
326 | 0 | if ((0 == mRect.width) || (0 == mRect.height)) { |
327 | 0 | // Do not render when given a zero area. This avoids some useless |
328 | 0 | // scaling work while we wait for our image dimensions to arrive |
329 | 0 | // asynchronously. |
330 | 0 | return; |
331 | 0 | } |
332 | 0 | |
333 | 0 | if (!IsVisibleForPainting(aBuilder)) |
334 | 0 | return; |
335 | 0 | |
336 | 0 | uint32_t clipFlags = |
337 | 0 | nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition()) ? |
338 | 0 | 0 : DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT; |
339 | 0 |
|
340 | 0 | DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox |
341 | 0 | clip(aBuilder, this, clipFlags); |
342 | 0 |
|
343 | 0 | nsDisplayList list; |
344 | 0 | list.AppendToTop( |
345 | 0 | MakeDisplayItem<nsDisplayXULImage>(aBuilder, this)); |
346 | 0 |
|
347 | 0 | CreateOwnLayerIfNeeded(aBuilder, &list); |
348 | 0 |
|
349 | 0 | aLists.Content()->AppendToTop(&list); |
350 | 0 | } |
351 | | |
352 | | already_AddRefed<imgIContainer> |
353 | | nsImageBoxFrame::GetImageContainerForPainting(const nsPoint& aPt, |
354 | | ImgDrawResult& aDrawResult, |
355 | | Maybe<nsPoint>& aAnchorPoint, |
356 | | nsRect& aDest) |
357 | 0 | { |
358 | 0 | if (!mImageRequest) { |
359 | 0 | // This probably means we're drawn by a native theme. |
360 | 0 | aDrawResult = ImgDrawResult::SUCCESS; |
361 | 0 | return nullptr; |
362 | 0 | } |
363 | 0 | |
364 | 0 | // Don't draw if the image's size isn't available. |
365 | 0 | uint32_t imgStatus; |
366 | 0 | if (!NS_SUCCEEDED(mImageRequest->GetImageStatus(&imgStatus)) || |
367 | 0 | !(imgStatus & imgIRequest::STATUS_SIZE_AVAILABLE)) { |
368 | 0 | aDrawResult = ImgDrawResult::NOT_READY; |
369 | 0 | return nullptr; |
370 | 0 | } |
371 | 0 | |
372 | 0 | nsCOMPtr<imgIContainer> imgCon; |
373 | 0 | mImageRequest->GetImage(getter_AddRefs(imgCon)); |
374 | 0 |
|
375 | 0 | if (!imgCon) { |
376 | 0 | aDrawResult = ImgDrawResult::NOT_READY; |
377 | 0 | return nullptr; |
378 | 0 | } |
379 | 0 | |
380 | 0 | aDest = GetDestRect(aPt, aAnchorPoint); |
381 | 0 | aDrawResult = ImgDrawResult::SUCCESS; |
382 | 0 | return imgCon.forget(); |
383 | 0 | } |
384 | | |
385 | | ImgDrawResult |
386 | | nsImageBoxFrame::PaintImage(gfxContext& aRenderingContext, |
387 | | const nsRect& aDirtyRect, nsPoint aPt, |
388 | | uint32_t aFlags) |
389 | 0 | { |
390 | 0 | ImgDrawResult result; |
391 | 0 | Maybe<nsPoint> anchorPoint; |
392 | 0 | nsRect dest; |
393 | 0 | nsCOMPtr<imgIContainer> imgCon = GetImageContainerForPainting(aPt, result, |
394 | 0 | anchorPoint, |
395 | 0 | dest); |
396 | 0 | if (!imgCon) { |
397 | 0 | return result; |
398 | 0 | } |
399 | 0 | |
400 | 0 | // don't draw if the image is not dirty |
401 | 0 | // XXX(seth): Can this actually happen anymore? |
402 | 0 | nsRect dirty; |
403 | 0 | if (!dirty.IntersectRect(aDirtyRect, dest)) { |
404 | 0 | return ImgDrawResult::TEMPORARY_ERROR; |
405 | 0 | } |
406 | 0 | |
407 | 0 | bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0); |
408 | 0 |
|
409 | 0 | Maybe<SVGImageContext> svgContext; |
410 | 0 | SVGImageContext::MaybeStoreContextPaint(svgContext, this, imgCon); |
411 | 0 | return nsLayoutUtils::DrawSingleImage( |
412 | 0 | aRenderingContext, |
413 | 0 | PresContext(), imgCon, |
414 | 0 | nsLayoutUtils::GetSamplingFilterForFrame(this), |
415 | 0 | dest, dirty, |
416 | 0 | svgContext, aFlags, |
417 | 0 | anchorPoint.ptrOr(nullptr), |
418 | 0 | hasSubRect ? &mSubRect : nullptr); |
419 | 0 | } |
420 | | |
421 | | ImgDrawResult |
422 | | nsImageBoxFrame::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder, |
423 | | mozilla::wr::IpcResourceUpdateQueue& aResources, |
424 | | const StackingContextHelper& aSc, |
425 | | mozilla::layers::WebRenderLayerManager* aManager, |
426 | | nsDisplayItem* aItem, |
427 | | nsPoint aPt, |
428 | | uint32_t aFlags) |
429 | 0 | { |
430 | 0 | ImgDrawResult result; |
431 | 0 | Maybe<nsPoint> anchorPoint; |
432 | 0 | nsRect dest; |
433 | 0 | nsCOMPtr<imgIContainer> imgCon = GetImageContainerForPainting(aPt, result, |
434 | 0 | anchorPoint, |
435 | 0 | dest); |
436 | 0 | if (!imgCon) { |
437 | 0 | return result; |
438 | 0 | } |
439 | 0 | |
440 | 0 | uint32_t containerFlags = imgIContainer::FLAG_ASYNC_NOTIFY; |
441 | 0 | if (aFlags & nsImageRenderer::FLAG_PAINTING_TO_WINDOW) { |
442 | 0 | containerFlags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING; |
443 | 0 | } |
444 | 0 | if (aFlags & nsImageRenderer::FLAG_SYNC_DECODE_IMAGES) { |
445 | 0 | containerFlags |= imgIContainer::FLAG_SYNC_DECODE; |
446 | 0 | } |
447 | 0 |
|
448 | 0 | const int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel(); |
449 | 0 | LayoutDeviceRect fillRect = LayoutDeviceRect::FromAppUnits(dest, |
450 | 0 | appUnitsPerDevPixel); |
451 | 0 | Maybe<SVGImageContext> svgContext; |
452 | 0 | gfx::IntSize decodeSize = |
453 | 0 | nsLayoutUtils::ComputeImageContainerDrawingParameters(imgCon, aItem->Frame(), fillRect, |
454 | 0 | aSc, containerFlags, svgContext); |
455 | 0 |
|
456 | 0 | RefPtr<layers::ImageContainer> container; |
457 | 0 | result = imgCon->GetImageContainerAtSize(aManager, decodeSize, svgContext, |
458 | 0 | containerFlags, getter_AddRefs(container)); |
459 | 0 | if (!container) { |
460 | 0 | NS_WARNING("Failed to get image container"); |
461 | 0 | return result; |
462 | 0 | } |
463 | 0 |
|
464 | 0 | mozilla::wr::ImageRendering rendering = wr::ToImageRendering( |
465 | 0 | nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame())); |
466 | 0 | gfx::IntSize size; |
467 | 0 | Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey( |
468 | 0 | aItem, container, aBuilder, aResources, rendering, aSc, size, Nothing()); |
469 | 0 | if (key.isNothing()) { |
470 | 0 | return result; |
471 | 0 | } |
472 | 0 | wr::LayoutRect fill = wr::ToRoundedLayoutRect(fillRect); |
473 | 0 |
|
474 | 0 | LayoutDeviceSize gapSize(0, 0); |
475 | 0 | aBuilder.PushImage(fill, |
476 | 0 | fill, |
477 | 0 | !BackfaceIsHidden(), |
478 | 0 | wr::ToLayoutSize(fillRect.Size()), |
479 | 0 | wr::ToLayoutSize(gapSize), |
480 | 0 | rendering, |
481 | 0 | key.value()); |
482 | 0 |
|
483 | 0 | return result; |
484 | 0 | } |
485 | | |
486 | | nsRect |
487 | | nsImageBoxFrame::GetDestRect(const nsPoint& aOffset, Maybe<nsPoint>& aAnchorPoint) |
488 | 0 | { |
489 | 0 | nsCOMPtr<imgIContainer> imgCon; |
490 | 0 | mImageRequest->GetImage(getter_AddRefs(imgCon)); |
491 | 0 | MOZ_ASSERT(imgCon); |
492 | 0 |
|
493 | 0 | nsRect clientRect; |
494 | 0 | GetXULClientRect(clientRect); |
495 | 0 | clientRect += aOffset; |
496 | 0 | nsRect dest; |
497 | 0 | if (!mUseSrcAttr) { |
498 | 0 | // Our image (if we have one) is coming from the CSS property |
499 | 0 | // 'list-style-image' (combined with '-moz-image-region'). For now, ignore |
500 | 0 | // 'object-fit' & 'object-position' in this case, and just fill our rect. |
501 | 0 | // XXXdholbert Should we even honor these properties in this case? They only |
502 | 0 | // apply to replaced elements, and I'm not sure we count as a replaced |
503 | 0 | // element when our image data is determined by CSS. |
504 | 0 | dest = clientRect; |
505 | 0 | } else { |
506 | 0 | // Determine dest rect based on intrinsic size & ratio, along with |
507 | 0 | // 'object-fit' & 'object-position' properties: |
508 | 0 | IntrinsicSize intrinsicSize; |
509 | 0 | nsSize intrinsicRatio; |
510 | 0 | if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) { |
511 | 0 | // Image has a valid size; use it as intrinsic size & ratio. |
512 | 0 | intrinsicSize.width.SetCoordValue(mIntrinsicSize.width); |
513 | 0 | intrinsicSize.height.SetCoordValue(mIntrinsicSize.height); |
514 | 0 | intrinsicRatio = mIntrinsicSize; |
515 | 0 | } else { |
516 | 0 | // Image doesn't have a (valid) intrinsic size. |
517 | 0 | // Try to look up intrinsic ratio and use that at least. |
518 | 0 | imgCon->GetIntrinsicRatio(&intrinsicRatio); |
519 | 0 | } |
520 | 0 | aAnchorPoint.emplace(); |
521 | 0 | dest = nsLayoutUtils::ComputeObjectDestRect(clientRect, |
522 | 0 | intrinsicSize, |
523 | 0 | intrinsicRatio, |
524 | 0 | StylePosition(), |
525 | 0 | aAnchorPoint.ptr()); |
526 | 0 | } |
527 | 0 |
|
528 | 0 | return dest; |
529 | 0 | } |
530 | | |
531 | | void nsDisplayXULImage::Paint(nsDisplayListBuilder* aBuilder, |
532 | | gfxContext* aCtx) |
533 | 0 | { |
534 | 0 | // Even though we call StartDecoding when we get a new image we pass |
535 | 0 | // FLAG_SYNC_DECODE_IF_FAST here for the case where the size we draw at is not |
536 | 0 | // the intrinsic size of the image and we aren't likely to implement predictive |
537 | 0 | // decoding at the correct size for this class like nsImageFrame has. |
538 | 0 | uint32_t flags = imgIContainer::FLAG_SYNC_DECODE_IF_FAST; |
539 | 0 | if (aBuilder->ShouldSyncDecodeImages()) |
540 | 0 | flags |= imgIContainer::FLAG_SYNC_DECODE; |
541 | 0 | if (aBuilder->IsPaintingToWindow()) |
542 | 0 | flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING; |
543 | 0 |
|
544 | 0 | ImgDrawResult result = static_cast<nsImageBoxFrame*>(mFrame)-> |
545 | 0 | PaintImage(*aCtx, GetPaintRect(), ToReferenceFrame(), flags); |
546 | 0 |
|
547 | 0 | nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result); |
548 | 0 | } |
549 | | |
550 | | bool |
551 | | nsDisplayXULImage::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder, |
552 | | mozilla::wr::IpcResourceUpdateQueue& aResources, |
553 | | const StackingContextHelper& aSc, |
554 | | mozilla::layers::WebRenderLayerManager* aManager, |
555 | | nsDisplayListBuilder* aDisplayListBuilder) |
556 | 0 | { |
557 | 0 | nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame); |
558 | 0 | if (!imageFrame->CanOptimizeToImageLayer()) { |
559 | 0 | return false; |
560 | 0 | } |
561 | 0 | |
562 | 0 | if (!imageFrame->mImageRequest) { |
563 | 0 | return true; |
564 | 0 | } |
565 | 0 | |
566 | 0 | uint32_t flags = imgIContainer::FLAG_SYNC_DECODE_IF_FAST; |
567 | 0 | if (aDisplayListBuilder->ShouldSyncDecodeImages()) { |
568 | 0 | flags |= imgIContainer::FLAG_SYNC_DECODE; |
569 | 0 | } |
570 | 0 | if (aDisplayListBuilder->IsPaintingToWindow()) { |
571 | 0 | flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING; |
572 | 0 | } |
573 | 0 |
|
574 | 0 | ImgDrawResult result = imageFrame-> |
575 | 0 | CreateWebRenderCommands(aBuilder, aResources, aSc, aManager, this, ToReferenceFrame(), flags); |
576 | 0 | if (result == ImgDrawResult::NOT_SUPPORTED) { |
577 | 0 | return false; |
578 | 0 | } |
579 | 0 | |
580 | 0 | nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result); |
581 | 0 | return true; |
582 | 0 | } |
583 | | |
584 | | nsDisplayItemGeometry* |
585 | | nsDisplayXULImage::AllocateGeometry(nsDisplayListBuilder* aBuilder) |
586 | 0 | { |
587 | 0 | return new nsDisplayItemGenericImageGeometry(this, aBuilder); |
588 | 0 | } |
589 | | |
590 | | void |
591 | | nsDisplayXULImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
592 | | const nsDisplayItemGeometry* aGeometry, |
593 | | nsRegion* aInvalidRegion) const |
594 | 0 | { |
595 | 0 | auto boxFrame = static_cast<nsImageBoxFrame*>(mFrame); |
596 | 0 | auto geometry = |
597 | 0 | static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry); |
598 | 0 |
|
599 | 0 | if (aBuilder->ShouldSyncDecodeImages() && |
600 | 0 | boxFrame->mImageRequest && |
601 | 0 | geometry->ShouldInvalidateToSyncDecodeImages()) { |
602 | 0 | bool snap; |
603 | 0 | aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); |
604 | 0 | } |
605 | 0 |
|
606 | 0 | nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); |
607 | 0 | } |
608 | | |
609 | | bool |
610 | | nsDisplayXULImage::CanOptimizeToImageLayer(LayerManager* aManager, |
611 | | nsDisplayListBuilder* aBuilder) |
612 | 0 | { |
613 | 0 | nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame); |
614 | 0 | if (!imageFrame->CanOptimizeToImageLayer()) { |
615 | 0 | return false; |
616 | 0 | } |
617 | 0 | |
618 | 0 | return nsDisplayImageContainer::CanOptimizeToImageLayer(aManager, aBuilder); |
619 | 0 | } |
620 | | |
621 | | already_AddRefed<imgIContainer> |
622 | | nsDisplayXULImage::GetImage() |
623 | 0 | { |
624 | 0 | nsImageBoxFrame* imageFrame = static_cast<nsImageBoxFrame*>(mFrame); |
625 | 0 | if (!imageFrame->mImageRequest) { |
626 | 0 | return nullptr; |
627 | 0 | } |
628 | 0 | |
629 | 0 | nsCOMPtr<imgIContainer> imgCon; |
630 | 0 | imageFrame->mImageRequest->GetImage(getter_AddRefs(imgCon)); |
631 | 0 |
|
632 | 0 | return imgCon.forget(); |
633 | 0 | } |
634 | | |
635 | | nsRect |
636 | | nsDisplayXULImage::GetDestRect() const |
637 | 0 | { |
638 | 0 | Maybe<nsPoint> anchorPoint; |
639 | 0 | return static_cast<nsImageBoxFrame*>(mFrame)->GetDestRect(ToReferenceFrame(), anchorPoint); |
640 | 0 | } |
641 | | |
642 | | bool |
643 | | nsImageBoxFrame::CanOptimizeToImageLayer() |
644 | 0 | { |
645 | 0 | bool hasSubRect = !mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0); |
646 | 0 | if (hasSubRect) { |
647 | 0 | return false; |
648 | 0 | } |
649 | 0 | return true; |
650 | 0 | } |
651 | | |
652 | | // |
653 | | // DidSetComputedStyle |
654 | | // |
655 | | // When the ComputedStyle changes, make sure that all of our image is up to |
656 | | // date. |
657 | | // |
658 | | /* virtual */ void |
659 | | nsImageBoxFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) |
660 | 0 | { |
661 | 0 | nsLeafBoxFrame::DidSetComputedStyle(aOldComputedStyle); |
662 | 0 |
|
663 | 0 | // Fetch our subrect. |
664 | 0 | const nsStyleList* myList = StyleList(); |
665 | 0 | mSubRect = myList->mImageRegion; // before |mSuppressStyleCheck| test! |
666 | 0 |
|
667 | 0 | if (mUseSrcAttr || mSuppressStyleCheck) |
668 | 0 | return; // No more work required, since the image isn't specified by style. |
669 | 0 | |
670 | 0 | // If we're using a native theme implementation, we shouldn't draw anything. |
671 | 0 | const nsStyleDisplay* disp = StyleDisplay(); |
672 | 0 | if (disp->HasAppearance() && nsBox::gTheme && |
673 | 0 | nsBox::gTheme->ThemeSupportsWidget(nullptr, this, disp->mAppearance)) |
674 | 0 | return; |
675 | 0 | |
676 | 0 | // If list-style-image changes, we have a new image. |
677 | 0 | nsCOMPtr<nsIURI> oldURI, newURI; |
678 | 0 | if (mImageRequest) |
679 | 0 | mImageRequest->GetURI(getter_AddRefs(oldURI)); |
680 | 0 | if (myList->GetListStyleImage()) |
681 | 0 | myList->GetListStyleImage()->GetURI(getter_AddRefs(newURI)); |
682 | 0 | bool equal; |
683 | 0 | if (newURI == oldURI || // handles null==null |
684 | 0 | (newURI && oldURI && |
685 | 0 | NS_SUCCEEDED(newURI->Equals(oldURI, &equal)) && equal)) |
686 | 0 | return; |
687 | 0 | |
688 | 0 | UpdateImage(); |
689 | 0 | } // DidSetComputedStyle |
690 | | |
691 | | void |
692 | | nsImageBoxFrame::GetImageSize() |
693 | 0 | { |
694 | 0 | if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) { |
695 | 0 | mImageSize.width = mIntrinsicSize.width; |
696 | 0 | mImageSize.height = mIntrinsicSize.height; |
697 | 0 | } else { |
698 | 0 | mImageSize.width = 0; |
699 | 0 | mImageSize.height = 0; |
700 | 0 | } |
701 | 0 | } |
702 | | |
703 | | /** |
704 | | * Ok return our dimensions |
705 | | */ |
706 | | nsSize |
707 | | nsImageBoxFrame::GetXULPrefSize(nsBoxLayoutState& aState) |
708 | 0 | { |
709 | 0 | nsSize size(0,0); |
710 | 0 | DISPLAY_PREF_SIZE(this, size); |
711 | 0 | if (DoesNeedRecalc(mImageSize)) |
712 | 0 | GetImageSize(); |
713 | 0 |
|
714 | 0 | if (!mUseSrcAttr && (mSubRect.width > 0 || mSubRect.height > 0)) |
715 | 0 | size = mSubRect.Size(); |
716 | 0 | else |
717 | 0 | size = mImageSize; |
718 | 0 |
|
719 | 0 | nsSize intrinsicSize = size; |
720 | 0 |
|
721 | 0 | nsMargin borderPadding(0,0,0,0); |
722 | 0 | GetXULBorderAndPadding(borderPadding); |
723 | 0 | size.width += borderPadding.LeftRight(); |
724 | 0 | size.height += borderPadding.TopBottom(); |
725 | 0 |
|
726 | 0 | bool widthSet, heightSet; |
727 | 0 | nsIFrame::AddXULPrefSize(this, size, widthSet, heightSet); |
728 | 0 | NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE, |
729 | 0 | "non-intrinsic size expected"); |
730 | 0 |
|
731 | 0 | nsSize minSize = GetXULMinSize(aState); |
732 | 0 | nsSize maxSize = GetXULMaxSize(aState); |
733 | 0 |
|
734 | 0 | if (!widthSet && !heightSet) { |
735 | 0 | if (minSize.width != NS_INTRINSICSIZE) |
736 | 0 | minSize.width -= borderPadding.LeftRight(); |
737 | 0 | if (minSize.height != NS_INTRINSICSIZE) |
738 | 0 | minSize.height -= borderPadding.TopBottom(); |
739 | 0 | if (maxSize.width != NS_INTRINSICSIZE) |
740 | 0 | maxSize.width -= borderPadding.LeftRight(); |
741 | 0 | if (maxSize.height != NS_INTRINSICSIZE) |
742 | 0 | maxSize.height -= borderPadding.TopBottom(); |
743 | 0 |
|
744 | 0 | size = nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(minSize.width, minSize.height, |
745 | 0 | maxSize.width, maxSize.height, |
746 | 0 | intrinsicSize.width, intrinsicSize.height); |
747 | 0 | NS_ASSERTION(size.width != NS_INTRINSICSIZE && size.height != NS_INTRINSICSIZE, |
748 | 0 | "non-intrinsic size expected"); |
749 | 0 | size.width += borderPadding.LeftRight(); |
750 | 0 | size.height += borderPadding.TopBottom(); |
751 | 0 | return size; |
752 | 0 | } |
753 | 0 |
|
754 | 0 | if (!widthSet) { |
755 | 0 | if (intrinsicSize.height > 0) { |
756 | 0 | // Subtract off the border and padding from the height because the |
757 | 0 | // content-box needs to be used to determine the ratio |
758 | 0 | nscoord height = size.height - borderPadding.TopBottom(); |
759 | 0 | size.width = nscoord(int64_t(height) * int64_t(intrinsicSize.width) / |
760 | 0 | int64_t(intrinsicSize.height)); |
761 | 0 | } |
762 | 0 | else { |
763 | 0 | size.width = intrinsicSize.width; |
764 | 0 | } |
765 | 0 |
|
766 | 0 | size.width += borderPadding.LeftRight(); |
767 | 0 | } |
768 | 0 | else if (!heightSet) { |
769 | 0 | if (intrinsicSize.width > 0) { |
770 | 0 | nscoord width = size.width - borderPadding.LeftRight(); |
771 | 0 | size.height = nscoord(int64_t(width) * int64_t(intrinsicSize.height) / |
772 | 0 | int64_t(intrinsicSize.width)); |
773 | 0 | } |
774 | 0 | else { |
775 | 0 | size.height = intrinsicSize.height; |
776 | 0 | } |
777 | 0 |
|
778 | 0 | size.height += borderPadding.TopBottom(); |
779 | 0 | } |
780 | 0 |
|
781 | 0 | return BoundsCheck(minSize, size, maxSize); |
782 | 0 | } |
783 | | |
784 | | nsSize |
785 | | nsImageBoxFrame::GetXULMinSize(nsBoxLayoutState& aState) |
786 | 0 | { |
787 | 0 | // An image can always scale down to (0,0). |
788 | 0 | nsSize size(0,0); |
789 | 0 | DISPLAY_MIN_SIZE(this, size); |
790 | 0 | AddBorderAndPadding(size); |
791 | 0 | bool widthSet, heightSet; |
792 | 0 | nsIFrame::AddXULMinSize(aState, this, size, widthSet, heightSet); |
793 | 0 | return size; |
794 | 0 | } |
795 | | |
796 | | nscoord |
797 | | nsImageBoxFrame::GetXULBoxAscent(nsBoxLayoutState& aState) |
798 | 0 | { |
799 | 0 | return GetXULPrefSize(aState).height; |
800 | 0 | } |
801 | | |
802 | | #ifdef DEBUG_FRAME_DUMP |
803 | | nsresult |
804 | | nsImageBoxFrame::GetFrameName(nsAString& aResult) const |
805 | | { |
806 | | return MakeFrameName(NS_LITERAL_STRING("ImageBox"), aResult); |
807 | | } |
808 | | #endif |
809 | | |
810 | | nsresult |
811 | | nsImageBoxFrame::Notify(imgIRequest* aRequest, |
812 | | int32_t aType, |
813 | | const nsIntRect* aData) |
814 | 0 | { |
815 | 0 | if (aType == imgINotificationObserver::SIZE_AVAILABLE) { |
816 | 0 | nsCOMPtr<imgIContainer> image; |
817 | 0 | aRequest->GetImage(getter_AddRefs(image)); |
818 | 0 | return OnSizeAvailable(aRequest, image); |
819 | 0 | } |
820 | 0 | |
821 | 0 | if (aType == imgINotificationObserver::DECODE_COMPLETE) { |
822 | 0 | return OnDecodeComplete(aRequest); |
823 | 0 | } |
824 | 0 | |
825 | 0 | if (aType == imgINotificationObserver::LOAD_COMPLETE) { |
826 | 0 | uint32_t imgStatus; |
827 | 0 | aRequest->GetImageStatus(&imgStatus); |
828 | 0 | nsresult status = |
829 | 0 | imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK; |
830 | 0 | return OnLoadComplete(aRequest, status); |
831 | 0 | } |
832 | 0 |
|
833 | 0 | if (aType == imgINotificationObserver::IS_ANIMATED) { |
834 | 0 | return OnImageIsAnimated(aRequest); |
835 | 0 | } |
836 | 0 | |
837 | 0 | if (aType == imgINotificationObserver::FRAME_UPDATE) { |
838 | 0 | return OnFrameUpdate(aRequest); |
839 | 0 | } |
840 | 0 | |
841 | 0 | return NS_OK; |
842 | 0 | } |
843 | | |
844 | | nsresult |
845 | | nsImageBoxFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage) |
846 | 0 | { |
847 | 0 | NS_ENSURE_ARG_POINTER(aImage); |
848 | 0 |
|
849 | 0 | // Ensure the animation (if any) is started. Note: There is no |
850 | 0 | // corresponding call to Decrement for this. This Increment will be |
851 | 0 | // 'cleaned up' by the Request when it is destroyed, but only then. |
852 | 0 | aRequest->IncrementAnimationConsumers(); |
853 | 0 |
|
854 | 0 | nscoord w, h; |
855 | 0 | aImage->GetWidth(&w); |
856 | 0 | aImage->GetHeight(&h); |
857 | 0 |
|
858 | 0 | mIntrinsicSize.SizeTo(nsPresContext::CSSPixelsToAppUnits(w), |
859 | 0 | nsPresContext::CSSPixelsToAppUnits(h)); |
860 | 0 |
|
861 | 0 | if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { |
862 | 0 | PresShell()-> |
863 | 0 | FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); |
864 | 0 | } |
865 | 0 |
|
866 | 0 | return NS_OK; |
867 | 0 | } |
868 | | |
869 | | nsresult |
870 | | nsImageBoxFrame::OnDecodeComplete(imgIRequest* aRequest) |
871 | 0 | { |
872 | 0 | nsBoxLayoutState state(PresContext()); |
873 | 0 | this->XULRedraw(state); |
874 | 0 | return NS_OK; |
875 | 0 | } |
876 | | |
877 | | nsresult |
878 | | nsImageBoxFrame::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus) |
879 | 0 | { |
880 | 0 | if (NS_SUCCEEDED(aStatus)) { |
881 | 0 | // Fire an onload DOM event. |
882 | 0 | FireImageDOMEvent(mContent, eLoad); |
883 | 0 | } else { |
884 | 0 | // Fire an onerror DOM event. |
885 | 0 | mIntrinsicSize.SizeTo(0, 0); |
886 | 0 | PresShell()-> |
887 | 0 | FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); |
888 | 0 | FireImageDOMEvent(mContent, eLoadError); |
889 | 0 | } |
890 | 0 |
|
891 | 0 | return NS_OK; |
892 | 0 | } |
893 | | |
894 | | nsresult |
895 | | nsImageBoxFrame::OnImageIsAnimated(imgIRequest* aRequest) |
896 | 0 | { |
897 | 0 | // Register with our refresh driver, if we're animated. |
898 | 0 | nsLayoutUtils::RegisterImageRequest(PresContext(), aRequest, |
899 | 0 | &mRequestRegistered); |
900 | 0 |
|
901 | 0 | return NS_OK; |
902 | 0 | } |
903 | | |
904 | | nsresult |
905 | | nsImageBoxFrame::OnFrameUpdate(imgIRequest* aRequest) |
906 | 0 | { |
907 | 0 | if ((0 == mRect.width) || (0 == mRect.height)) { |
908 | 0 | return NS_OK; |
909 | 0 | } |
910 | 0 | |
911 | 0 | // Check if WebRender has interacted with this frame. If it has |
912 | 0 | // we need to let it know that things have changed. |
913 | 0 | if (HasProperty(WebRenderUserDataProperty::Key())) { |
914 | 0 | uint32_t key = static_cast<uint32_t>(DisplayItemType::TYPE_XUL_IMAGE); |
915 | 0 | RefPtr<WebRenderFallbackData> data = |
916 | 0 | GetWebRenderUserData<WebRenderFallbackData>(this, key); |
917 | 0 | if (data) { |
918 | 0 | data->SetInvalid(true); |
919 | 0 | } |
920 | 0 | SchedulePaint(); |
921 | 0 | return NS_OK; |
922 | 0 | } |
923 | 0 |
|
924 | 0 | InvalidateLayer(DisplayItemType::TYPE_XUL_IMAGE); |
925 | 0 |
|
926 | 0 | return NS_OK; |
927 | 0 | } |
928 | | |
929 | | NS_IMPL_ISUPPORTS(nsImageBoxListener, imgINotificationObserver) |
930 | | |
931 | | nsImageBoxListener::nsImageBoxListener(nsImageBoxFrame *frame) |
932 | | : mFrame(frame) |
933 | 0 | { |
934 | 0 | } |
935 | | |
936 | | nsImageBoxListener::~nsImageBoxListener() |
937 | 0 | { |
938 | 0 | } |
939 | | |
940 | | NS_IMETHODIMP |
941 | | nsImageBoxListener::Notify(imgIRequest *request, int32_t aType, const nsIntRect* aData) |
942 | 0 | { |
943 | 0 | if (!mFrame) |
944 | 0 | return NS_OK; |
945 | 0 | |
946 | 0 | return mFrame->Notify(request, aType, aData); |
947 | 0 | } |