Line data Source code
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/. */
4 :
5 : /* A class that handles style system image loads (other image loads are handled
6 : * by the nodes in the content tree).
7 : */
8 :
9 : #include "mozilla/css/ImageLoader.h"
10 : #include "nsAutoPtr.h"
11 : #include "nsContentUtils.h"
12 : #include "nsLayoutUtils.h"
13 : #include "nsError.h"
14 : #include "nsDisplayList.h"
15 : #include "FrameLayerBuilder.h"
16 : #include "nsSVGEffects.h"
17 : #include "imgIContainer.h"
18 : #include "Image.h"
19 :
20 : namespace mozilla {
21 : namespace css {
22 :
23 : void
24 0 : ImageLoader::DropDocumentReference()
25 : {
26 : // It's okay if GetPresContext returns null here (due to the presshell pointer
27 : // on the document being null) as that means the presshell has already
28 : // been destroyed, and it also calls ClearFrames when it is destroyed.
29 0 : ClearFrames(GetPresContext());
30 :
31 0 : for (auto it = mImages.Iter(); !it.Done(); it.Next()) {
32 0 : ImageLoader::Image* image = it.Get()->GetKey();
33 0 : imgIRequest* request = image->mRequests.GetWeak(mDocument);
34 0 : if (request) {
35 0 : request->CancelAndForgetObserver(NS_BINDING_ABORTED);
36 : }
37 0 : image->mRequests.Remove(mDocument);
38 : }
39 0 : mImages.Clear();
40 :
41 0 : mDocument = nullptr;
42 0 : }
43 :
44 : void
45 12 : ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest,
46 : nsIFrame* aFrame)
47 : {
48 24 : nsCOMPtr<imgINotificationObserver> observer;
49 12 : aRequest->GetNotificationObserver(getter_AddRefs(observer));
50 12 : if (!observer) {
51 : // The request has already been canceled, so ignore it. This is ok because
52 : // we're not going to get any more notifications from a canceled request.
53 0 : return;
54 : }
55 :
56 12 : MOZ_ASSERT(observer == this);
57 :
58 12 : FrameSet* frameSet = nullptr;
59 12 : if (mRequestToFrameMap.Get(aRequest, &frameSet)) {
60 1 : NS_ASSERTION(frameSet, "This should never be null!");
61 : }
62 :
63 12 : if (!frameSet) {
64 22 : nsAutoPtr<FrameSet> newFrameSet(new FrameSet());
65 :
66 11 : mRequestToFrameMap.Put(aRequest, newFrameSet);
67 11 : frameSet = newFrameSet.forget();
68 :
69 11 : nsPresContext* presContext = GetPresContext();
70 11 : if (presContext) {
71 : nsLayoutUtils::RegisterImageRequestIfAnimated(presContext,
72 : aRequest,
73 11 : nullptr);
74 : }
75 : }
76 :
77 12 : RequestSet* requestSet = nullptr;
78 12 : if (mFrameToRequestMap.Get(aFrame, &requestSet)) {
79 2 : NS_ASSERTION(requestSet, "This should never be null");
80 : }
81 :
82 12 : if (!requestSet) {
83 20 : nsAutoPtr<RequestSet> newRequestSet(new RequestSet());
84 :
85 10 : mFrameToRequestMap.Put(aFrame, newRequestSet);
86 10 : requestSet = newRequestSet.forget();
87 10 : aFrame->SetHasImageRequest(true);
88 : }
89 :
90 : // Add these to the sets, but only if they're not already there.
91 12 : uint32_t i = frameSet->IndexOfFirstElementGt(aFrame);
92 12 : if (i == 0 || aFrame != frameSet->ElementAt(i-1)) {
93 12 : frameSet->InsertElementAt(i, aFrame);
94 : }
95 12 : i = requestSet->IndexOfFirstElementGt(aRequest);
96 12 : if (i == 0 || aRequest != requestSet->ElementAt(i-1)) {
97 12 : requestSet->InsertElementAt(i, aRequest);
98 : }
99 : }
100 :
101 : void
102 141 : ImageLoader::MaybeRegisterCSSImage(ImageLoader::Image* aImage)
103 : {
104 141 : NS_ASSERTION(aImage, "This should never be null!");
105 :
106 141 : bool found = false;
107 141 : aImage->mRequests.GetWeak(mDocument, &found);
108 141 : if (found) {
109 : // This document already has a request.
110 282 : return;
111 : }
112 :
113 0 : imgRequestProxy* canonicalRequest = aImage->mRequests.GetWeak(nullptr);
114 0 : if (!canonicalRequest) {
115 : // The image was blocked or something.
116 0 : return;
117 : }
118 :
119 0 : RefPtr<imgRequestProxy> request;
120 :
121 : // Ignore errors here. If cloning fails for some reason we'll put a null
122 : // entry in the hash and we won't keep trying to clone.
123 0 : mInClone = true;
124 0 : canonicalRequest->Clone(this, getter_AddRefs(request));
125 0 : mInClone = false;
126 :
127 0 : aImage->mRequests.Put(mDocument, request);
128 :
129 0 : AddImage(aImage);
130 : }
131 :
132 : void
133 0 : ImageLoader::DeregisterCSSImage(ImageLoader::Image* aImage)
134 : {
135 0 : RemoveImage(aImage);
136 0 : }
137 :
138 : void
139 2 : ImageLoader::RemoveRequestToFrameMapping(imgIRequest* aRequest,
140 : nsIFrame* aFrame)
141 : {
142 : #ifdef DEBUG
143 : {
144 4 : nsCOMPtr<imgINotificationObserver> observer;
145 2 : aRequest->GetNotificationObserver(getter_AddRefs(observer));
146 2 : MOZ_ASSERT(!observer || observer == this);
147 : }
148 : #endif
149 :
150 2 : if (auto entry = mRequestToFrameMap.Lookup(aRequest)) {
151 2 : FrameSet* frameSet = entry.Data();
152 2 : MOZ_ASSERT(frameSet, "This should never be null");
153 2 : frameSet->RemoveElementSorted(aFrame);
154 2 : if (frameSet->IsEmpty()) {
155 1 : nsPresContext* presContext = GetPresContext();
156 1 : if (presContext) {
157 1 : nsLayoutUtils::DeregisterImageRequest(presContext, aRequest, nullptr);
158 : }
159 1 : entry.Remove();
160 : }
161 : }
162 2 : }
163 :
164 : void
165 0 : ImageLoader::RemoveFrameToRequestMapping(imgIRequest* aRequest,
166 : nsIFrame* aFrame)
167 : {
168 0 : if (auto entry = mFrameToRequestMap.Lookup(aFrame)) {
169 0 : RequestSet* requestSet = entry.Data();
170 0 : MOZ_ASSERT(requestSet, "This should never be null");
171 0 : requestSet->RemoveElementSorted(aRequest);
172 0 : if (requestSet->IsEmpty()) {
173 0 : aFrame->SetHasImageRequest(false);
174 0 : entry.Remove();
175 : }
176 : }
177 0 : }
178 :
179 : void
180 0 : ImageLoader::DisassociateRequestFromFrame(imgIRequest* aRequest,
181 : nsIFrame* aFrame)
182 : {
183 0 : MOZ_ASSERT(aFrame->HasImageRequest(), "why call me?");
184 0 : RemoveRequestToFrameMapping(aRequest, aFrame);
185 0 : RemoveFrameToRequestMapping(aRequest, aFrame);
186 0 : }
187 :
188 : void
189 2 : ImageLoader::DropRequestsForFrame(nsIFrame* aFrame)
190 : {
191 2 : MOZ_ASSERT(aFrame->HasImageRequest(), "why call me?");
192 4 : nsAutoPtr<RequestSet> requestSet;
193 2 : mFrameToRequestMap.Remove(aFrame, &requestSet);
194 2 : aFrame->SetHasImageRequest(false);
195 2 : if (MOZ_UNLIKELY(!requestSet)) {
196 0 : MOZ_ASSERT_UNREACHABLE("HasImageRequest was lying");
197 : return;
198 : }
199 4 : for (imgIRequest* request : *requestSet) {
200 2 : RemoveRequestToFrameMapping(request, aFrame);
201 : }
202 2 : }
203 :
204 : void
205 0 : ImageLoader::SetAnimationMode(uint16_t aMode)
206 : {
207 0 : NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
208 : aMode == imgIContainer::kDontAnimMode ||
209 : aMode == imgIContainer::kLoopOnceAnimMode,
210 : "Wrong Animation Mode is being set!");
211 :
212 0 : for (auto iter = mRequestToFrameMap.ConstIter(); !iter.Done(); iter.Next()) {
213 0 : auto request = static_cast<imgIRequest*>(iter.Key());
214 :
215 : #ifdef DEBUG
216 : {
217 0 : nsCOMPtr<imgIRequest> debugRequest = do_QueryInterface(request);
218 0 : NS_ASSERTION(debugRequest == request, "This is bad");
219 : }
220 : #endif
221 :
222 0 : nsCOMPtr<imgIContainer> container;
223 0 : request->GetImage(getter_AddRefs(container));
224 0 : if (!container) {
225 0 : continue;
226 : }
227 :
228 : // This can fail if the image is in error, and we don't care.
229 0 : container->SetAnimationMode(aMode);
230 : }
231 0 : }
232 :
233 : void
234 4 : ImageLoader::ClearFrames(nsPresContext* aPresContext)
235 : {
236 4 : for (auto iter = mRequestToFrameMap.ConstIter(); !iter.Done(); iter.Next()) {
237 0 : auto request = static_cast<imgIRequest*>(iter.Key());
238 :
239 : #ifdef DEBUG
240 : {
241 0 : nsCOMPtr<imgIRequest> debugRequest = do_QueryInterface(request);
242 0 : NS_ASSERTION(debugRequest == request, "This is bad");
243 : }
244 : #endif
245 :
246 0 : if (aPresContext) {
247 : nsLayoutUtils::DeregisterImageRequest(aPresContext,
248 : request,
249 0 : nullptr);
250 : }
251 : }
252 :
253 4 : mRequestToFrameMap.Clear();
254 4 : mFrameToRequestMap.Clear();
255 4 : }
256 :
257 : void
258 40 : ImageLoader::LoadImage(nsIURI* aURI, nsIPrincipal* aOriginPrincipal,
259 : nsIURI* aReferrer, ImageLoader::Image* aImage)
260 : {
261 40 : NS_ASSERTION(aImage->mRequests.Count() == 0, "Huh?");
262 :
263 40 : aImage->mRequests.Put(nullptr, nullptr);
264 :
265 40 : if (!aURI) {
266 0 : return;
267 : }
268 :
269 80 : RefPtr<imgRequestProxy> request;
270 120 : nsresult rv = nsContentUtils::LoadImage(aURI, mDocument, mDocument,
271 : aOriginPrincipal, aReferrer,
272 40 : mDocument->GetReferrerPolicy(),
273 : nullptr, nsIRequest::LOAD_NORMAL,
274 80 : NS_LITERAL_STRING("css"),
275 160 : getter_AddRefs(request));
276 :
277 40 : if (NS_FAILED(rv) || !request) {
278 0 : return;
279 : }
280 :
281 80 : RefPtr<imgRequestProxy> clonedRequest;
282 40 : mInClone = true;
283 40 : rv = request->Clone(this, getter_AddRefs(clonedRequest));
284 40 : mInClone = false;
285 :
286 40 : if (NS_FAILED(rv)) {
287 0 : return;
288 : }
289 :
290 40 : aImage->mRequests.Put(nullptr, request);
291 40 : aImage->mRequests.Put(mDocument, clonedRequest);
292 :
293 40 : AddImage(aImage);
294 : }
295 :
296 : void
297 40 : ImageLoader::AddImage(ImageLoader::Image* aImage)
298 : {
299 40 : NS_ASSERTION(!mImages.Contains(aImage), "Huh?");
300 40 : mImages.PutEntry(aImage);
301 40 : }
302 :
303 : void
304 0 : ImageLoader::RemoveImage(ImageLoader::Image* aImage)
305 : {
306 0 : NS_ASSERTION(mImages.Contains(aImage), "Huh?");
307 0 : mImages.RemoveEntry(aImage);
308 0 : }
309 :
310 : nsPresContext*
311 52 : ImageLoader::GetPresContext()
312 : {
313 52 : if (!mDocument) {
314 0 : return nullptr;
315 : }
316 :
317 52 : nsIPresShell* shell = mDocument->GetShell();
318 52 : if (!shell) {
319 0 : return nullptr;
320 : }
321 :
322 52 : return shell->GetPresContext();
323 : }
324 :
325 15 : void InvalidateImagesCallback(nsIFrame* aFrame,
326 : DisplayItemData* aItem)
327 : {
328 15 : nsDisplayItem::Type type = nsDisplayItem::GetDisplayItemTypeFromKey(aItem->GetDisplayItemKey());
329 15 : uint8_t flags = nsDisplayItem::GetDisplayItemFlagsForType(type);
330 :
331 15 : if (flags & nsDisplayItem::TYPE_RENDERS_NO_IMAGES) {
332 2 : return;
333 : }
334 :
335 13 : if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
336 0 : printf_stderr("Invalidating display item(type=%d) based on frame %p \
337 0 : because it might contain an invalidated image\n", type, aFrame);
338 : }
339 13 : aItem->Invalidate();
340 13 : aFrame->SchedulePaint();
341 : }
342 :
343 : void
344 15 : ImageLoader::DoRedraw(FrameSet* aFrameSet, bool aForcePaint)
345 : {
346 15 : NS_ASSERTION(aFrameSet, "Must have a frame set");
347 15 : NS_ASSERTION(mDocument, "Should have returned earlier!");
348 :
349 15 : FrameSet::size_type length = aFrameSet->Length();
350 30 : for (FrameSet::size_type i = 0; i < length; i++) {
351 15 : nsIFrame* frame = aFrameSet->ElementAt(i);
352 :
353 15 : if (frame->StyleVisibility()->IsVisible()) {
354 15 : if (frame->IsFrameOfType(nsIFrame::eTablePart)) {
355 : // Tables don't necessarily build border/background display items
356 : // for the individual table part frames, so IterateRetainedDataFor
357 : // might not find the right display item.
358 0 : frame->InvalidateFrame();
359 : } else {
360 15 : FrameLayerBuilder::IterateRetainedDataFor(frame, InvalidateImagesCallback);
361 :
362 : // Update ancestor rendering observers (-moz-element etc)
363 15 : nsIFrame *f = frame;
364 267 : while (f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
365 126 : nsSVGEffects::InvalidateDirectRenderingObservers(f);
366 126 : f = nsLayoutUtils::GetCrossDocParentFrame(f);
367 : }
368 :
369 15 : if (aForcePaint) {
370 6 : frame->SchedulePaint();
371 : }
372 : }
373 : }
374 : }
375 15 : }
376 :
377 739 : NS_IMPL_ADDREF(ImageLoader)
378 684 : NS_IMPL_RELEASE(ImageLoader)
379 :
380 376 : NS_INTERFACE_MAP_BEGIN(ImageLoader)
381 376 : NS_INTERFACE_MAP_ENTRY(imgINotificationObserver)
382 108 : NS_INTERFACE_MAP_ENTRY(imgIOnloadBlocker)
383 0 : NS_INTERFACE_MAP_END
384 :
385 : NS_IMETHODIMP
386 254 : ImageLoader::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
387 : {
388 254 : if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
389 80 : nsCOMPtr<imgIContainer> image;
390 40 : aRequest->GetImage(getter_AddRefs(image));
391 40 : return OnSizeAvailable(aRequest, image);
392 : }
393 :
394 214 : if (aType == imgINotificationObserver::IS_ANIMATED) {
395 2 : return OnImageIsAnimated(aRequest);
396 : }
397 :
398 212 : if (aType == imgINotificationObserver::FRAME_COMPLETE) {
399 35 : return OnFrameComplete(aRequest);
400 : }
401 :
402 177 : if (aType == imgINotificationObserver::FRAME_UPDATE) {
403 62 : return OnFrameUpdate(aRequest);
404 : }
405 :
406 115 : if (aType == imgINotificationObserver::DECODE_COMPLETE) {
407 70 : nsCOMPtr<imgIContainer> image;
408 35 : aRequest->GetImage(getter_AddRefs(image));
409 35 : if (image && mDocument) {
410 35 : image->PropagateUseCounters(mDocument);
411 : }
412 : }
413 :
414 115 : return NS_OK;
415 : }
416 :
417 : nsresult
418 40 : ImageLoader::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
419 : {
420 40 : nsPresContext* presContext = GetPresContext();
421 40 : if (!presContext) {
422 0 : return NS_OK;
423 : }
424 :
425 40 : aImage->SetAnimationMode(presContext->ImageAnimationMode());
426 :
427 40 : return NS_OK;
428 : }
429 :
430 : nsresult
431 2 : ImageLoader::OnImageIsAnimated(imgIRequest* aRequest)
432 : {
433 2 : if (!mDocument) {
434 0 : return NS_OK;
435 : }
436 :
437 2 : FrameSet* frameSet = nullptr;
438 2 : if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
439 2 : return NS_OK;
440 : }
441 :
442 : // Register with the refresh driver now that we are aware that
443 : // we are animated.
444 0 : nsPresContext* presContext = GetPresContext();
445 0 : if (presContext) {
446 : nsLayoutUtils::RegisterImageRequest(presContext,
447 : aRequest,
448 0 : nullptr);
449 : }
450 :
451 0 : return NS_OK;
452 : }
453 :
454 : nsresult
455 35 : ImageLoader::OnFrameComplete(imgIRequest* aRequest)
456 : {
457 35 : if (!mDocument || mInClone) {
458 0 : return NS_OK;
459 : }
460 :
461 35 : FrameSet* frameSet = nullptr;
462 35 : if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
463 29 : return NS_OK;
464 : }
465 :
466 6 : NS_ASSERTION(frameSet, "This should never be null!");
467 :
468 : // Since we just finished decoding a frame, we always want to paint, in case
469 : // we're now able to paint an image that we couldn't paint before (and hence
470 : // that we don't have retained data for).
471 6 : DoRedraw(frameSet, /* aForcePaint = */ true);
472 :
473 6 : return NS_OK;
474 : }
475 :
476 : nsresult
477 62 : ImageLoader::OnFrameUpdate(imgIRequest* aRequest)
478 : {
479 62 : if (!mDocument || mInClone) {
480 0 : return NS_OK;
481 : }
482 :
483 62 : FrameSet* frameSet = nullptr;
484 62 : if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
485 53 : return NS_OK;
486 : }
487 :
488 9 : NS_ASSERTION(frameSet, "This should never be null!");
489 :
490 9 : DoRedraw(frameSet, /* aForcePaint = */ false);
491 :
492 9 : return NS_OK;
493 : }
494 :
495 : NS_IMETHODIMP
496 54 : ImageLoader::BlockOnload(imgIRequest* aRequest)
497 : {
498 54 : if (!mDocument) {
499 0 : return NS_OK;
500 : }
501 :
502 54 : mDocument->BlockOnload();
503 :
504 54 : return NS_OK;
505 : }
506 :
507 : NS_IMETHODIMP
508 54 : ImageLoader::UnblockOnload(imgIRequest* aRequest)
509 : {
510 54 : if (!mDocument) {
511 0 : return NS_OK;
512 : }
513 :
514 54 : mDocument->UnblockOnload(false);
515 :
516 54 : return NS_OK;
517 : }
518 :
519 : void
520 0 : ImageLoader::FlushUseCounters()
521 : {
522 0 : for (auto iter = mImages.Iter(); !iter.Done(); iter.Next()) {
523 0 : nsPtrHashKey<Image>* key = iter.Get();
524 0 : ImageLoader::Image* image = key->GetKey();
525 :
526 0 : imgIRequest* request = image->mRequests.GetWeak(mDocument);
527 :
528 0 : nsCOMPtr<imgIContainer> container;
529 0 : request->GetImage(getter_AddRefs(container));
530 0 : if (container) {
531 0 : static_cast<image::Image*>(container.get())->ReportUseCounters();
532 : }
533 : }
534 0 : }
535 :
536 : } // namespace css
537 : } // namespace mozilla
|