1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 /* A class that handles style system image loads (other image loads are handled
6 * by the nodes in the content tree).
9 #include "mozilla/css/ImageLoader.h"
10 #include "nsContentUtils.h"
11 #include "nsLayoutUtils.h"
13 #include "nsDisplayList.h"
14 #include "FrameLayerBuilder.h"
15 #include "nsSVGEffects.h"
16 #include "imgIContainer.h"
21 /* static */ PLDHashOperator
22 ImageLoader::SetAnimationModeEnumerator(nsISupports
* aKey
, FrameSet
* aValue
,
25 imgIRequest
* request
= static_cast<imgIRequest
*>(aKey
);
27 uint16_t* mode
= static_cast<uint16_t*>(aClosure
);
31 nsCOMPtr
<imgIRequest
> debugRequest
= do_QueryInterface(aKey
);
32 NS_ASSERTION(debugRequest
== request
, "This is bad");
36 nsCOMPtr
<imgIContainer
> container
;
37 request
->GetImage(getter_AddRefs(container
));
42 // This can fail if the image is in error, and we don't care.
43 container
->SetAnimationMode(*mode
);
48 static PLDHashOperator
49 ClearImageHashSet(nsPtrHashKey
<ImageLoader::Image
>* aKey
, void* aClosure
)
51 nsIDocument
* doc
= static_cast<nsIDocument
*>(aClosure
);
52 ImageLoader::Image
* image
= aKey
->GetKey();
54 imgIRequest
* request
= image
->mRequests
.GetWeak(doc
);
56 request
->CancelAndForgetObserver(NS_BINDING_ABORTED
);
59 image
->mRequests
.Remove(doc
);
61 return PL_DHASH_REMOVE
;
65 ImageLoader::DropDocumentReference()
67 // It's okay if GetPresContext returns null here (due to the presshell pointer
68 // on the document being null) as that means the presshell has already
69 // been destroyed, and it also calls ClearFrames when it is destroyed.
70 ClearFrames(GetPresContext());
71 mImages
.EnumerateEntries(&ClearImageHashSet
, mDocument
);
76 ImageLoader::AssociateRequestToFrame(imgIRequest
* aRequest
,
79 nsCOMPtr
<imgINotificationObserver
> observer
;
80 aRequest
->GetNotificationObserver(getter_AddRefs(observer
));
82 // The request has already been canceled, so ignore it. This is ok because
83 // we're not going to get any more notifications from a canceled request.
87 MOZ_ASSERT(observer
== this);
89 FrameSet
* frameSet
= nullptr;
90 if (mRequestToFrameMap
.Get(aRequest
, &frameSet
)) {
91 NS_ASSERTION(frameSet
, "This should never be null!");
95 nsAutoPtr
<FrameSet
> newFrameSet(new FrameSet());
97 mRequestToFrameMap
.Put(aRequest
, newFrameSet
);
98 frameSet
= newFrameSet
.forget();
100 nsPresContext
* presContext
= GetPresContext();
102 nsLayoutUtils::RegisterImageRequestIfAnimated(presContext
,
108 RequestSet
* requestSet
= nullptr;
109 if (mFrameToRequestMap
.Get(aFrame
, &requestSet
)) {
110 NS_ASSERTION(requestSet
, "This should never be null");
114 nsAutoPtr
<RequestSet
> newRequestSet(new RequestSet());
116 mFrameToRequestMap
.Put(aFrame
, newRequestSet
);
117 requestSet
= newRequestSet
.forget();
120 // Add these to the sets, but only if they're not already there.
121 uint32_t i
= frameSet
->IndexOfFirstElementGt(aFrame
);
122 if (i
== 0 || aFrame
!= frameSet
->ElementAt(i
-1)) {
123 frameSet
->InsertElementAt(i
, aFrame
);
125 i
= requestSet
->IndexOfFirstElementGt(aRequest
);
126 if (i
== 0 || aRequest
!= requestSet
->ElementAt(i
-1)) {
127 requestSet
->InsertElementAt(i
, aRequest
);
132 ImageLoader::MaybeRegisterCSSImage(ImageLoader::Image
* aImage
)
134 NS_ASSERTION(aImage
, "This should never be null!");
137 aImage
->mRequests
.GetWeak(mDocument
, &found
);
139 // This document already has a request.
143 imgRequestProxy
* canonicalRequest
= aImage
->mRequests
.GetWeak(nullptr);
144 if (!canonicalRequest
) {
145 // The image was blocked or something.
149 nsRefPtr
<imgRequestProxy
> request
;
151 // Ignore errors here. If cloning fails for some reason we'll put a null
152 // entry in the hash and we won't keep trying to clone.
154 canonicalRequest
->Clone(this, getter_AddRefs(request
));
157 aImage
->mRequests
.Put(mDocument
, request
);
163 ImageLoader::DeregisterCSSImage(ImageLoader::Image
* aImage
)
169 ImageLoader::DisassociateRequestFromFrame(imgIRequest
* aRequest
,
172 FrameSet
* frameSet
= nullptr;
173 RequestSet
* requestSet
= nullptr;
177 nsCOMPtr
<imgINotificationObserver
> observer
;
178 aRequest
->GetNotificationObserver(getter_AddRefs(observer
));
179 MOZ_ASSERT(!observer
|| observer
== this);
183 mRequestToFrameMap
.Get(aRequest
, &frameSet
);
184 mFrameToRequestMap
.Get(aFrame
, &requestSet
);
187 frameSet
->RemoveElementSorted(aFrame
);
190 requestSet
->RemoveElementSorted(aRequest
);
193 if (frameSet
&& !frameSet
->Length()) {
194 mRequestToFrameMap
.Remove(aRequest
);
196 nsPresContext
* presContext
= GetPresContext();
198 nsLayoutUtils::DeregisterImageRequest(presContext
,
204 if (requestSet
&& !requestSet
->Length()) {
205 mFrameToRequestMap
.Remove(aFrame
);
210 ImageLoader::DropRequestsForFrame(nsIFrame
* aFrame
)
212 RequestSet
* requestSet
= nullptr;
213 if (!mFrameToRequestMap
.Get(aFrame
, &requestSet
)) {
217 NS_ASSERTION(requestSet
, "This should never be null");
219 RequestSet
frozenRequestSet(*requestSet
);
220 for (RequestSet::size_type i
= frozenRequestSet
.Length(); i
!= 0; --i
) {
221 imgIRequest
* request
= frozenRequestSet
.ElementAt(i
- 1);
223 DisassociateRequestFromFrame(request
, aFrame
);
228 ImageLoader::SetAnimationMode(uint16_t aMode
)
230 NS_ASSERTION(aMode
== imgIContainer::kNormalAnimMode
||
231 aMode
== imgIContainer::kDontAnimMode
||
232 aMode
== imgIContainer::kLoopOnceAnimMode
,
233 "Wrong Animation Mode is being set!");
235 mRequestToFrameMap
.EnumerateRead(SetAnimationModeEnumerator
, &aMode
);
238 /* static */ PLDHashOperator
239 ImageLoader::DeregisterRequestEnumerator(nsISupports
* aKey
, FrameSet
* aValue
,
242 imgIRequest
* request
= static_cast<imgIRequest
*>(aKey
);
246 nsCOMPtr
<imgIRequest
> debugRequest
= do_QueryInterface(aKey
);
247 NS_ASSERTION(debugRequest
== request
, "This is bad");
251 nsPresContext
* presContext
= static_cast<nsPresContext
*>(aClosure
);
253 nsLayoutUtils::DeregisterImageRequest(presContext
,
258 return PL_DHASH_NEXT
;
262 ImageLoader::ClearFrames(nsPresContext
* aPresContext
)
264 mRequestToFrameMap
.EnumerateRead(DeregisterRequestEnumerator
, aPresContext
);
265 mRequestToFrameMap
.Clear();
266 mFrameToRequestMap
.Clear();
270 ImageLoader::LoadImage(nsIURI
* aURI
, nsIPrincipal
* aOriginPrincipal
,
271 nsIURI
* aReferrer
, ImageLoader::Image
* aImage
)
273 NS_ASSERTION(aImage
->mRequests
.Count() == 0, "Huh?");
275 aImage
->mRequests
.Put(nullptr, nullptr);
281 if (!nsContentUtils::CanLoadImage(aURI
, mDocument
, mDocument
,
286 nsRefPtr
<imgRequestProxy
> request
;
287 nsContentUtils::LoadImage(aURI
, mDocument
, aOriginPrincipal
, aReferrer
,
288 mDocument
->GetReferrerPolicy(),
289 nullptr, nsIRequest::LOAD_NORMAL
,
290 NS_LITERAL_STRING("css"),
291 getter_AddRefs(request
));
297 nsRefPtr
<imgRequestProxy
> clonedRequest
;
299 nsresult rv
= request
->Clone(this, getter_AddRefs(clonedRequest
));
306 aImage
->mRequests
.Put(nullptr, request
);
307 aImage
->mRequests
.Put(mDocument
, clonedRequest
);
313 ImageLoader::AddImage(ImageLoader::Image
* aImage
)
315 NS_ASSERTION(!mImages
.Contains(aImage
), "Huh?");
316 if (!mImages
.PutEntry(aImage
)) {
317 NS_RUNTIMEABORT("OOM");
322 ImageLoader::RemoveImage(ImageLoader::Image
* aImage
)
324 NS_ASSERTION(mImages
.Contains(aImage
), "Huh?");
325 mImages
.RemoveEntry(aImage
);
329 ImageLoader::GetPresContext()
335 nsIPresShell
* shell
= mDocument
->GetShell();
340 return shell
->GetPresContext();
343 void InvalidateImagesCallback(nsIFrame
* aFrame
,
344 FrameLayerBuilder::DisplayItemData
* aItem
)
346 nsDisplayItem::Type type
= nsDisplayItem::GetDisplayItemTypeFromKey(aItem
->GetDisplayItemKey());
347 uint8_t flags
= nsDisplayItem::GetDisplayItemFlagsForType(type
);
349 if (flags
& nsDisplayItem::TYPE_RENDERS_NO_IMAGES
) {
354 aFrame
->SchedulePaint();
356 // Update ancestor rendering observers (-moz-element etc)
357 nsIFrame
*f
= aFrame
;
358 while (f
&& !f
->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT
)) {
359 nsSVGEffects::InvalidateDirectRenderingObservers(f
);
360 f
= nsLayoutUtils::GetCrossDocParentFrame(f
);
365 ImageLoader::DoRedraw(FrameSet
* aFrameSet
, bool aForcePaint
)
367 NS_ASSERTION(aFrameSet
, "Must have a frame set");
368 NS_ASSERTION(mDocument
, "Should have returned earlier!");
370 FrameSet::size_type length
= aFrameSet
->Length();
371 for (FrameSet::size_type i
= 0; i
< length
; i
++) {
372 nsIFrame
* frame
= aFrameSet
->ElementAt(i
);
374 if (frame
->StyleVisibility()->IsVisible()) {
375 if (frame
->IsFrameOfType(nsIFrame::eTablePart
)) {
376 // Tables don't necessarily build border/background display items
377 // for the individual table part frames, so IterateRetainedDataFor
378 // might not find the right display item.
379 frame
->InvalidateFrame();
381 FrameLayerBuilder::IterateRetainedDataFor(frame
, InvalidateImagesCallback
);
383 frame
->SchedulePaint();
390 NS_IMPL_ADDREF(ImageLoader
)
391 NS_IMPL_RELEASE(ImageLoader
)
393 NS_INTERFACE_MAP_BEGIN(ImageLoader
)
394 NS_INTERFACE_MAP_ENTRY(imgINotificationObserver
)
395 NS_INTERFACE_MAP_ENTRY(imgIOnloadBlocker
)
399 ImageLoader::Notify(imgIRequest
* aRequest
, int32_t aType
, const nsIntRect
* aData
)
401 if (aType
== imgINotificationObserver::SIZE_AVAILABLE
) {
402 nsCOMPtr
<imgIContainer
> image
;
403 aRequest
->GetImage(getter_AddRefs(image
));
404 return OnSizeAvailable(aRequest
, image
);
407 if (aType
== imgINotificationObserver::IS_ANIMATED
) {
408 return OnImageIsAnimated(aRequest
);
411 if (aType
== imgINotificationObserver::FRAME_COMPLETE
) {
412 return OnFrameComplete(aRequest
);
415 if (aType
== imgINotificationObserver::FRAME_UPDATE
) {
416 return OnFrameUpdate(aRequest
);
423 ImageLoader::OnSizeAvailable(imgIRequest
* aRequest
, imgIContainer
* aImage
)
425 nsPresContext
* presContext
= GetPresContext();
430 aImage
->SetAnimationMode(presContext
->ImageAnimationMode());
436 ImageLoader::OnImageIsAnimated(imgIRequest
* aRequest
)
442 FrameSet
* frameSet
= nullptr;
443 if (!mRequestToFrameMap
.Get(aRequest
, &frameSet
)) {
447 // Register with the refresh driver now that we are aware that
449 nsPresContext
* presContext
= GetPresContext();
451 nsLayoutUtils::RegisterImageRequest(presContext
,
460 ImageLoader::OnFrameComplete(imgIRequest
* aRequest
)
462 if (!mDocument
|| mInClone
) {
466 FrameSet
* frameSet
= nullptr;
467 if (!mRequestToFrameMap
.Get(aRequest
, &frameSet
)) {
471 NS_ASSERTION(frameSet
, "This should never be null!");
473 // Since we just finished decoding a frame, we always want to paint, in case
474 // we're now able to paint an image that we couldn't paint before (and hence
475 // that we don't have retained data for).
476 DoRedraw(frameSet
, /* aForcePaint = */ true);
482 ImageLoader::OnFrameUpdate(imgIRequest
* aRequest
)
484 if (!mDocument
|| mInClone
) {
488 FrameSet
* frameSet
= nullptr;
489 if (!mRequestToFrameMap
.Get(aRequest
, &frameSet
)) {
493 NS_ASSERTION(frameSet
, "This should never be null!");
495 DoRedraw(frameSet
, /* aForcePaint = */ false);
501 ImageLoader::BlockOnload(imgIRequest
* aRequest
)
507 mDocument
->BlockOnload();
513 ImageLoader::UnblockOnload(imgIRequest
* aRequest
)
519 mDocument
->UnblockOnload(false);
525 } // namespace mozilla