/src/mozilla-central/layout/style/ImageLoader.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 file, |
5 | | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | /* A class that handles style system image loads (other image loads are handled |
8 | | * by the nodes in the content tree). |
9 | | */ |
10 | | |
11 | | #include "mozilla/css/ImageLoader.h" |
12 | | #include "nsAutoPtr.h" |
13 | | #include "nsContentUtils.h" |
14 | | #include "nsLayoutUtils.h" |
15 | | #include "nsError.h" |
16 | | #include "nsDisplayList.h" |
17 | | #include "nsIFrameInlines.h" |
18 | | #include "FrameLayerBuilder.h" |
19 | | #include "SVGObserverUtils.h" |
20 | | #include "imgIContainer.h" |
21 | | #include "Image.h" |
22 | | #include "GeckoProfiler.h" |
23 | | #include "mozilla/layers/WebRenderUserData.h" |
24 | | |
25 | | namespace mozilla { |
26 | | namespace css { |
27 | | |
28 | | void |
29 | | ImageLoader::DropDocumentReference() |
30 | 0 | { |
31 | 0 | // It's okay if GetPresContext returns null here (due to the presshell pointer |
32 | 0 | // on the document being null) as that means the presshell has already |
33 | 0 | // been destroyed, and it also calls ClearFrames when it is destroyed. |
34 | 0 | ClearFrames(GetPresContext()); |
35 | 0 |
|
36 | 0 | for (auto it = mImages.Iter(); !it.Done(); it.Next()) { |
37 | 0 | ImageLoader::Image* image = it.Get()->GetKey(); |
38 | 0 | imgIRequest* request = image->mRequests.GetWeak(mDocument); |
39 | 0 | if (request) { |
40 | 0 | request->CancelAndForgetObserver(NS_BINDING_ABORTED); |
41 | 0 | } |
42 | 0 | image->mRequests.Remove(mDocument); |
43 | 0 | } |
44 | 0 | mImages.Clear(); |
45 | 0 |
|
46 | 0 | mDocument = nullptr; |
47 | 0 | } |
48 | | |
49 | | // Normally, arrays of requests and frames are sorted by their pointer address, |
50 | | // for faster lookup. When recording or replaying, we don't do this, so that |
51 | | // the arrays retain their insertion order and are consistent between recording |
52 | | // and replaying. |
53 | | template <typename Elem, typename Item, typename Comparator = nsDefaultComparator<Elem, Item>> |
54 | | static size_t |
55 | | GetMaybeSortedIndex(const nsTArray<Elem>& aArray, const Item& aItem, bool* aFound, |
56 | | Comparator aComparator = Comparator()) |
57 | 0 | { |
58 | 0 | if (recordreplay::IsRecordingOrReplaying()) { |
59 | 0 | size_t index = aArray.IndexOf(aItem, 0, aComparator); |
60 | 0 | *aFound = index != nsTArray<Elem>::NoIndex; |
61 | 0 | return *aFound ? index + 1 : aArray.Length(); |
62 | 0 | } |
63 | 0 | size_t index = aArray.IndexOfFirstElementGt(aItem, aComparator); |
64 | 0 | *aFound = index > 0 && aComparator.Equals(aItem, aArray.ElementAt(index - 1)); |
65 | 0 | return index; |
66 | 0 | } Unexecuted instantiation: Unified_cpp_layout_style1.cpp:unsigned long mozilla::css::GetMaybeSortedIndex<mozilla::css::ImageLoader::FrameWithFlags, mozilla::css::ImageLoader::FrameWithFlags, mozilla::css::ImageLoader::FrameOnlyComparator>(nsTArray<mozilla::css::ImageLoader::FrameWithFlags> const&, mozilla::css::ImageLoader::FrameWithFlags const&, bool*, mozilla::css::ImageLoader::FrameOnlyComparator) Unexecuted instantiation: Unified_cpp_layout_style1.cpp:unsigned long mozilla::css::GetMaybeSortedIndex<nsCOMPtr<imgIRequest>, imgIRequest*, nsDefaultComparator<nsCOMPtr<imgIRequest>, imgIRequest*> >(nsTArray<nsCOMPtr<imgIRequest> > const&, imgIRequest* const&, bool*, nsDefaultComparator<nsCOMPtr<imgIRequest>, imgIRequest*>) |
67 | | |
68 | | void |
69 | | ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest, |
70 | | nsIFrame* aFrame, |
71 | | FrameFlags aFlags) |
72 | 0 | { |
73 | 0 | nsCOMPtr<imgINotificationObserver> observer; |
74 | 0 | aRequest->GetNotificationObserver(getter_AddRefs(observer)); |
75 | 0 | if (!observer) { |
76 | 0 | // The request has already been canceled, so ignore it. This is ok because |
77 | 0 | // we're not going to get any more notifications from a canceled request. |
78 | 0 | return; |
79 | 0 | } |
80 | 0 | |
81 | 0 | MOZ_ASSERT(observer == this); |
82 | 0 |
|
83 | 0 | FrameSet* frameSet = |
84 | 0 | mRequestToFrameMap.LookupForAdd(aRequest).OrInsert([=]() { |
85 | 0 | nsPresContext* presContext = GetPresContext(); |
86 | 0 | if (presContext) { |
87 | 0 | nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, |
88 | 0 | aRequest, |
89 | 0 | nullptr); |
90 | 0 | } |
91 | 0 | return new FrameSet(); |
92 | 0 | }); |
93 | 0 |
|
94 | 0 | RequestSet* requestSet = |
95 | 0 | mFrameToRequestMap.LookupForAdd(aFrame).OrInsert([=]() { |
96 | 0 | aFrame->SetHasImageRequest(true); |
97 | 0 | return new RequestSet(); |
98 | 0 | }); |
99 | 0 |
|
100 | 0 | // Add frame to the frameSet, and handle any special processing the |
101 | 0 | // frame might require. |
102 | 0 | FrameWithFlags fwf(aFrame); |
103 | 0 | FrameWithFlags* fwfToModify(&fwf); |
104 | 0 |
|
105 | 0 | // See if the frameSet already has this frame. |
106 | 0 | bool found; |
107 | 0 | uint32_t i = GetMaybeSortedIndex(*frameSet, fwf, &found, FrameOnlyComparator()); |
108 | 0 | if (found) { |
109 | 0 | // We're already tracking this frame, so prepare to modify the |
110 | 0 | // existing FrameWithFlags object. |
111 | 0 | fwfToModify = &frameSet->ElementAt(i-1); |
112 | 0 | } |
113 | 0 |
|
114 | 0 | // Check if the frame requires special processing. |
115 | 0 | if (aFlags & REQUEST_REQUIRES_REFLOW) { |
116 | 0 | fwfToModify->mFlags |= REQUEST_REQUIRES_REFLOW; |
117 | 0 |
|
118 | 0 | // If we weren't already blocking onload, do that now. |
119 | 0 | if ((fwfToModify->mFlags & REQUEST_HAS_BLOCKED_ONLOAD) == 0) { |
120 | 0 | // Get request status to see if we should block onload, and if we can |
121 | 0 | // request reflow immediately. |
122 | 0 | uint32_t status = 0; |
123 | 0 | if (NS_SUCCEEDED(aRequest->GetImageStatus(&status)) && |
124 | 0 | !(status & imgIRequest::STATUS_ERROR)) { |
125 | 0 | // No error, so we can block onload. |
126 | 0 | fwfToModify->mFlags |= REQUEST_HAS_BLOCKED_ONLOAD; |
127 | 0 |
|
128 | 0 | // Block document onload until we either remove the frame in |
129 | 0 | // RemoveRequestToFrameMapping or onLoadComplete, or complete a reflow. |
130 | 0 | mDocument->BlockOnload(); |
131 | 0 |
|
132 | 0 | // We need to stay blocked until we get a reflow. If the first frame |
133 | 0 | // is not yet decoded, we'll trigger that reflow from onFrameComplete. |
134 | 0 | // But if the first frame is already decoded, we need to trigger that |
135 | 0 | // reflow now, because we'll never get a call to onFrameComplete. |
136 | 0 | if(status & imgIRequest::STATUS_FRAME_COMPLETE) { |
137 | 0 | RequestReflowOnFrame(fwfToModify, aRequest); |
138 | 0 | } else { |
139 | 0 | // If we don't already have a complete frame, kickoff decode. This |
140 | 0 | // will ensure that either onFrameComplete or onLoadComplete will |
141 | 0 | // unblock document onload. |
142 | 0 |
|
143 | 0 | // We want to request decode in such a way that avoids triggering |
144 | 0 | // sync decode. First, we attempt to convert the aRequest into |
145 | 0 | // a imgIContainer. If that succeeds, then aRequest has an image |
146 | 0 | // and we can request decoding for size at zero size, and that will |
147 | 0 | // trigger async decode. If the conversion to imgIContainer is |
148 | 0 | // unsuccessful, then that means aRequest doesn't have an image yet, |
149 | 0 | // which means we can safely call StartDecoding() on it without |
150 | 0 | // triggering any synchronous work. |
151 | 0 | nsCOMPtr<imgIContainer> imgContainer; |
152 | 0 | aRequest->GetImage(getter_AddRefs(imgContainer)); |
153 | 0 | if (imgContainer) { |
154 | 0 | imgContainer->RequestDecodeForSize(gfx::IntSize(0, 0), |
155 | 0 | imgIContainer::DECODE_FLAGS_DEFAULT); |
156 | 0 | } else { |
157 | 0 | // It's safe to call StartDecoding directly, since it can't |
158 | 0 | // trigger synchronous decode without an image. Flags are ignored. |
159 | 0 | aRequest->StartDecoding(imgIContainer::FLAG_NONE); |
160 | 0 | } |
161 | 0 | } |
162 | 0 | } |
163 | 0 | } |
164 | 0 | } |
165 | 0 |
|
166 | 0 | // Do some sanity checking to ensure that we only add to one mapping |
167 | 0 | // iff we also add to the other mapping. |
168 | 0 | DebugOnly<bool> didAddToFrameSet(false); |
169 | 0 | DebugOnly<bool> didAddToRequestSet(false); |
170 | 0 |
|
171 | 0 | // If we weren't already tracking this frame, add it to the frameSet. |
172 | 0 | if (!found) { |
173 | 0 | frameSet->InsertElementAt(i, fwf); |
174 | 0 | didAddToFrameSet = true; |
175 | 0 | } |
176 | 0 |
|
177 | 0 | // Add request to the request set if it wasn't already there. |
178 | 0 | i = GetMaybeSortedIndex(*requestSet, aRequest, &found); |
179 | 0 | if (!found) { |
180 | 0 | requestSet->InsertElementAt(i, aRequest); |
181 | 0 | didAddToRequestSet = true; |
182 | 0 | } |
183 | 0 |
|
184 | 0 | MOZ_ASSERT(didAddToFrameSet == didAddToRequestSet, |
185 | 0 | "We should only add to one map iff we also add to the other map."); |
186 | 0 | } |
187 | | |
188 | | void |
189 | | ImageLoader::MaybeRegisterCSSImage(ImageLoader::Image* aImage) |
190 | 0 | { |
191 | 0 | NS_ASSERTION(aImage, "This should never be null!"); |
192 | 0 |
|
193 | 0 | bool found = false; |
194 | 0 | aImage->mRequests.GetWeak(mDocument, &found); |
195 | 0 | if (found) { |
196 | 0 | // This document already has a request. |
197 | 0 | return; |
198 | 0 | } |
199 | 0 | |
200 | 0 | imgRequestProxy* canonicalRequest = aImage->mRequests.GetWeak(nullptr); |
201 | 0 | if (!canonicalRequest) { |
202 | 0 | // The image was blocked or something. |
203 | 0 | return; |
204 | 0 | } |
205 | 0 | |
206 | 0 | RefPtr<imgRequestProxy> request; |
207 | 0 |
|
208 | 0 | // Ignore errors here. If cloning fails for some reason we'll put a null |
209 | 0 | // entry in the hash and we won't keep trying to clone. |
210 | 0 | mInClone = true; |
211 | 0 | canonicalRequest->SyncClone(this, mDocument, getter_AddRefs(request)); |
212 | 0 | mInClone = false; |
213 | 0 |
|
214 | 0 | aImage->mRequests.Put(mDocument, request); |
215 | 0 |
|
216 | 0 | AddImage(aImage); |
217 | 0 | } |
218 | | |
219 | | void |
220 | | ImageLoader::DeregisterCSSImage(ImageLoader::Image* aImage) |
221 | 0 | { |
222 | 0 | RemoveImage(aImage); |
223 | 0 | } |
224 | | |
225 | | void |
226 | | ImageLoader::RemoveRequestToFrameMapping(imgIRequest* aRequest, |
227 | | nsIFrame* aFrame) |
228 | 0 | { |
229 | | #ifdef DEBUG |
230 | | { |
231 | | nsCOMPtr<imgINotificationObserver> observer; |
232 | | aRequest->GetNotificationObserver(getter_AddRefs(observer)); |
233 | | MOZ_ASSERT(!observer || observer == this); |
234 | | } |
235 | | #endif |
236 | |
|
237 | 0 | if (auto entry = mRequestToFrameMap.Lookup(aRequest)) { |
238 | 0 | FrameSet* frameSet = entry.Data(); |
239 | 0 | MOZ_ASSERT(frameSet, "This should never be null"); |
240 | 0 |
|
241 | 0 | // Before we remove aFrame from the frameSet, unblock onload if needed. |
242 | 0 | bool found; |
243 | 0 | uint32_t i = GetMaybeSortedIndex(*frameSet, FrameWithFlags(aFrame), &found, |
244 | 0 | FrameOnlyComparator()); |
245 | 0 | if (found) { |
246 | 0 | FrameWithFlags& fwf = frameSet->ElementAt(i-1); |
247 | 0 | if (fwf.mFlags & REQUEST_HAS_BLOCKED_ONLOAD) { |
248 | 0 | mDocument->UnblockOnload(false); |
249 | 0 | // We're about to remove fwf from the frameSet, so we don't bother |
250 | 0 | // updating the flag. |
251 | 0 | } |
252 | 0 | frameSet->RemoveElementAt(i-1); |
253 | 0 | } |
254 | 0 |
|
255 | 0 | if (frameSet->IsEmpty()) { |
256 | 0 | nsPresContext* presContext = GetPresContext(); |
257 | 0 | if (presContext) { |
258 | 0 | nsLayoutUtils::DeregisterImageRequest(presContext, aRequest, nullptr); |
259 | 0 | } |
260 | 0 | entry.Remove(); |
261 | 0 | } |
262 | 0 | } |
263 | 0 | } |
264 | | |
265 | | void |
266 | | ImageLoader::RemoveFrameToRequestMapping(imgIRequest* aRequest, |
267 | | nsIFrame* aFrame) |
268 | 0 | { |
269 | 0 | if (auto entry = mFrameToRequestMap.Lookup(aFrame)) { |
270 | 0 | RequestSet* requestSet = entry.Data(); |
271 | 0 | MOZ_ASSERT(requestSet, "This should never be null"); |
272 | 0 | if (recordreplay::IsRecordingOrReplaying()) { |
273 | 0 | requestSet->RemoveElement(aRequest); |
274 | 0 | } else { |
275 | 0 | requestSet->RemoveElementSorted(aRequest); |
276 | 0 | } |
277 | 0 | if (requestSet->IsEmpty()) { |
278 | 0 | aFrame->SetHasImageRequest(false); |
279 | 0 | entry.Remove(); |
280 | 0 | } |
281 | 0 | } |
282 | 0 | } |
283 | | |
284 | | void |
285 | | ImageLoader::DisassociateRequestFromFrame(imgIRequest* aRequest, |
286 | | nsIFrame* aFrame) |
287 | 0 | { |
288 | 0 | MOZ_ASSERT(aFrame->HasImageRequest(), "why call me?"); |
289 | 0 | RemoveRequestToFrameMapping(aRequest, aFrame); |
290 | 0 | RemoveFrameToRequestMapping(aRequest, aFrame); |
291 | 0 | } |
292 | | |
293 | | void |
294 | | ImageLoader::DropRequestsForFrame(nsIFrame* aFrame) |
295 | 0 | { |
296 | 0 | MOZ_ASSERT(aFrame->HasImageRequest(), "why call me?"); |
297 | 0 | nsAutoPtr<RequestSet> requestSet; |
298 | 0 | mFrameToRequestMap.Remove(aFrame, &requestSet); |
299 | 0 | aFrame->SetHasImageRequest(false); |
300 | 0 | if (MOZ_UNLIKELY(!requestSet)) { |
301 | 0 | MOZ_ASSERT_UNREACHABLE("HasImageRequest was lying"); |
302 | 0 | return; |
303 | 0 | } |
304 | 0 | for (imgIRequest* request : *requestSet) { |
305 | 0 | RemoveRequestToFrameMapping(request, aFrame); |
306 | 0 | } |
307 | 0 | } |
308 | | |
309 | | void |
310 | | ImageLoader::SetAnimationMode(uint16_t aMode) |
311 | 0 | { |
312 | 0 | NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode || |
313 | 0 | aMode == imgIContainer::kDontAnimMode || |
314 | 0 | aMode == imgIContainer::kLoopOnceAnimMode, |
315 | 0 | "Wrong Animation Mode is being set!"); |
316 | 0 |
|
317 | 0 | for (auto iter = mRequestToFrameMap.ConstIter(); !iter.Done(); iter.Next()) { |
318 | 0 | auto request = static_cast<imgIRequest*>(iter.Key()); |
319 | 0 |
|
320 | | #ifdef DEBUG |
321 | | { |
322 | | nsCOMPtr<imgIRequest> debugRequest = do_QueryInterface(request); |
323 | | NS_ASSERTION(debugRequest == request, "This is bad"); |
324 | | } |
325 | | #endif |
326 | |
|
327 | 0 | nsCOMPtr<imgIContainer> container; |
328 | 0 | request->GetImage(getter_AddRefs(container)); |
329 | 0 | if (!container) { |
330 | 0 | continue; |
331 | 0 | } |
332 | 0 | |
333 | 0 | // This can fail if the image is in error, and we don't care. |
334 | 0 | container->SetAnimationMode(aMode); |
335 | 0 | } |
336 | 0 | } |
337 | | |
338 | | void |
339 | | ImageLoader::ClearFrames(nsPresContext* aPresContext) |
340 | 0 | { |
341 | 0 | for (auto iter = mRequestToFrameMap.ConstIter(); !iter.Done(); iter.Next()) { |
342 | 0 | auto request = static_cast<imgIRequest*>(iter.Key()); |
343 | 0 |
|
344 | | #ifdef DEBUG |
345 | | { |
346 | | nsCOMPtr<imgIRequest> debugRequest = do_QueryInterface(request); |
347 | | NS_ASSERTION(debugRequest == request, "This is bad"); |
348 | | } |
349 | | #endif |
350 | |
|
351 | 0 | if (aPresContext) { |
352 | 0 | nsLayoutUtils::DeregisterImageRequest(aPresContext, |
353 | 0 | request, |
354 | 0 | nullptr); |
355 | 0 | } |
356 | 0 | } |
357 | 0 |
|
358 | 0 | mRequestToFrameMap.Clear(); |
359 | 0 | mFrameToRequestMap.Clear(); |
360 | 0 | } |
361 | | |
362 | | void |
363 | | ImageLoader::LoadImage(nsIURI* aURI, nsIPrincipal* aOriginPrincipal, |
364 | | nsIURI* aReferrer, |
365 | | mozilla::net::ReferrerPolicy aPolicy, |
366 | | ImageLoader::Image* aImage, |
367 | | CORSMode aCorsMode) |
368 | 0 | { |
369 | 0 | NS_ASSERTION(aImage->mRequests.Count() == 0, "Huh?"); |
370 | 0 |
|
371 | 0 | aImage->mRequests.Put(nullptr, nullptr); |
372 | 0 |
|
373 | 0 | if (!aURI) { |
374 | 0 | return; |
375 | 0 | } |
376 | 0 | |
377 | 0 | int32_t loadFlags = nsIRequest::LOAD_NORMAL | |
378 | 0 | nsContentUtils::CORSModeToLoadImageFlags(aCorsMode); |
379 | 0 |
|
380 | 0 | RefPtr<imgRequestProxy> request; |
381 | 0 | nsresult rv = nsContentUtils::LoadImage(aURI, mDocument, mDocument, |
382 | 0 | aOriginPrincipal, 0, aReferrer, |
383 | 0 | aPolicy, |
384 | 0 | nullptr, loadFlags, |
385 | 0 | NS_LITERAL_STRING("css"), |
386 | 0 | getter_AddRefs(request)); |
387 | 0 |
|
388 | 0 | if (NS_FAILED(rv) || !request) { |
389 | 0 | return; |
390 | 0 | } |
391 | 0 | |
392 | 0 | RefPtr<imgRequestProxy> clonedRequest; |
393 | 0 | mInClone = true; |
394 | 0 | rv = request->SyncClone(this, mDocument, getter_AddRefs(clonedRequest)); |
395 | 0 | mInClone = false; |
396 | 0 |
|
397 | 0 | if (NS_FAILED(rv)) { |
398 | 0 | return; |
399 | 0 | } |
400 | 0 | |
401 | 0 | aImage->mRequests.Put(nullptr, request); |
402 | 0 | aImage->mRequests.Put(mDocument, clonedRequest); |
403 | 0 |
|
404 | 0 | AddImage(aImage); |
405 | 0 | } |
406 | | |
407 | | void |
408 | | ImageLoader::AddImage(ImageLoader::Image* aImage) |
409 | 0 | { |
410 | 0 | NS_ASSERTION(!mImages.Contains(aImage), "Huh?"); |
411 | 0 | mImages.PutEntry(aImage); |
412 | 0 | } |
413 | | |
414 | | void |
415 | | ImageLoader::RemoveImage(ImageLoader::Image* aImage) |
416 | 0 | { |
417 | 0 | NS_ASSERTION(mImages.Contains(aImage), "Huh?"); |
418 | 0 | mImages.RemoveEntry(aImage); |
419 | 0 | } |
420 | | |
421 | | nsPresContext* |
422 | | ImageLoader::GetPresContext() |
423 | 0 | { |
424 | 0 | if (!mDocument) { |
425 | 0 | return nullptr; |
426 | 0 | } |
427 | 0 | |
428 | 0 | return mDocument->GetPresContext(); |
429 | 0 | } |
430 | | |
431 | | static bool |
432 | | IsRenderNoImages(uint32_t aDisplayItemKey) |
433 | 0 | { |
434 | 0 | DisplayItemType type = GetDisplayItemTypeFromKey(aDisplayItemKey); |
435 | 0 | uint8_t flags = GetDisplayItemFlagsForType(type); |
436 | 0 | return flags & TYPE_RENDERS_NO_IMAGES; |
437 | 0 | } |
438 | | |
439 | | static void |
440 | | InvalidateImages(nsIFrame* aFrame) |
441 | 0 | { |
442 | 0 | bool invalidateFrame = false; |
443 | 0 | const SmallPointerArray<DisplayItemData>& array = aFrame->DisplayItemData(); |
444 | 0 | for (uint32_t i = 0; i < array.Length(); i++) { |
445 | 0 | DisplayItemData* data = DisplayItemData::AssertDisplayItemData(array.ElementAt(i)); |
446 | 0 | uint32_t displayItemKey = data->GetDisplayItemKey(); |
447 | 0 | if (displayItemKey != 0 && !IsRenderNoImages(displayItemKey)) { |
448 | 0 | if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { |
449 | 0 | DisplayItemType type = GetDisplayItemTypeFromKey(displayItemKey); |
450 | 0 | printf_stderr("Invalidating display item(type=%d) based on frame %p \ |
451 | 0 | because it might contain an invalidated image\n", |
452 | 0 | static_cast<uint32_t>(type), aFrame); |
453 | 0 | } |
454 | 0 |
|
455 | 0 | data->Invalidate(); |
456 | 0 | invalidateFrame = true; |
457 | 0 | } |
458 | 0 | } |
459 | 0 | if (auto userDataTable = |
460 | 0 | aFrame->GetProperty(layers::WebRenderUserDataProperty::Key())) { |
461 | 0 | for (auto iter = userDataTable->Iter(); !iter.Done(); iter.Next()) { |
462 | 0 | RefPtr<layers::WebRenderUserData> data = iter.UserData(); |
463 | 0 | if (data->GetType() == layers::WebRenderAnimationData::UserDataType::eFallback && |
464 | 0 | !IsRenderNoImages(data->GetDisplayItemKey())) { |
465 | 0 | static_cast<layers::WebRenderFallbackData*>(data.get())->SetInvalid(true); |
466 | 0 | } |
467 | 0 | //XXX: handle Blob data |
468 | 0 | invalidateFrame = true; |
469 | 0 | } |
470 | 0 | } |
471 | 0 |
|
472 | 0 | if (invalidateFrame) { |
473 | 0 | aFrame->SchedulePaint(); |
474 | 0 | } |
475 | 0 | } |
476 | | |
477 | | void |
478 | | ImageLoader::DoRedraw(FrameSet* aFrameSet, bool aForcePaint) |
479 | 0 | { |
480 | 0 | NS_ASSERTION(aFrameSet, "Must have a frame set"); |
481 | 0 | NS_ASSERTION(mDocument, "Should have returned earlier!"); |
482 | 0 |
|
483 | 0 | for (FrameWithFlags& fwf : *aFrameSet) { |
484 | 0 | nsIFrame* frame = fwf.mFrame; |
485 | 0 | if (frame->StyleVisibility()->IsVisible()) { |
486 | 0 | if (frame->IsFrameOfType(nsIFrame::eTablePart)) { |
487 | 0 | // Tables don't necessarily build border/background display items |
488 | 0 | // for the individual table part frames, so IterateRetainedDataFor |
489 | 0 | // might not find the right display item. |
490 | 0 | frame->InvalidateFrame(); |
491 | 0 | } else { |
492 | 0 | InvalidateImages(frame); |
493 | 0 |
|
494 | 0 | // Update ancestor rendering observers (-moz-element etc) |
495 | 0 | nsIFrame *f = frame; |
496 | 0 | while (f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) { |
497 | 0 | SVGObserverUtils::InvalidateDirectRenderingObservers(f); |
498 | 0 | f = nsLayoutUtils::GetCrossDocParentFrame(f); |
499 | 0 | } |
500 | 0 |
|
501 | 0 | if (aForcePaint) { |
502 | 0 | frame->SchedulePaint(); |
503 | 0 | } |
504 | 0 | } |
505 | 0 | } |
506 | 0 | } |
507 | 0 | } |
508 | | |
509 | | void |
510 | | ImageLoader::UnblockOnloadIfNeeded(nsIFrame* aFrame, imgIRequest* aRequest) |
511 | 0 | { |
512 | 0 | MOZ_ASSERT(aFrame); |
513 | 0 | MOZ_ASSERT(aRequest); |
514 | 0 |
|
515 | 0 | FrameSet* frameSet = mRequestToFrameMap.Get(aRequest); |
516 | 0 | if (!frameSet) { |
517 | 0 | return; |
518 | 0 | } |
519 | 0 | |
520 | 0 | size_t i = frameSet->BinaryIndexOf(FrameWithFlags(aFrame), |
521 | 0 | FrameOnlyComparator()); |
522 | 0 | if (i != FrameSet::NoIndex) { |
523 | 0 | FrameWithFlags& fwf = frameSet->ElementAt(i); |
524 | 0 | if (fwf.mFlags & REQUEST_HAS_BLOCKED_ONLOAD) { |
525 | 0 | mDocument->UnblockOnload(false); |
526 | 0 | fwf.mFlags &= ~REQUEST_HAS_BLOCKED_ONLOAD; |
527 | 0 | } |
528 | 0 | } |
529 | 0 | } |
530 | | |
531 | | void |
532 | | ImageLoader::RequestReflowIfNeeded(FrameSet* aFrameSet, imgIRequest* aRequest) |
533 | 0 | { |
534 | 0 | MOZ_ASSERT(aFrameSet); |
535 | 0 |
|
536 | 0 | for (FrameWithFlags& fwf : *aFrameSet) { |
537 | 0 | if (fwf.mFlags & REQUEST_REQUIRES_REFLOW) { |
538 | 0 | // Tell the container of the frame to reflow because the |
539 | 0 | // image request has finished decoding its first frame. |
540 | 0 | RequestReflowOnFrame(&fwf, aRequest); |
541 | 0 | } |
542 | 0 | } |
543 | 0 | } |
544 | | |
545 | | void |
546 | | ImageLoader::RequestReflowOnFrame(FrameWithFlags* aFwf, imgIRequest* aRequest) |
547 | 0 | { |
548 | 0 | nsIFrame* frame = aFwf->mFrame; |
549 | 0 |
|
550 | 0 | // Actually request the reflow. |
551 | 0 | nsIFrame* parent = frame->GetInFlowParent(); |
552 | 0 | parent->PresShell()->FrameNeedsReflow(parent, nsIPresShell::eStyleChange, |
553 | 0 | NS_FRAME_IS_DIRTY); |
554 | 0 |
|
555 | 0 | // We'll respond to the reflow events by unblocking onload, regardless |
556 | 0 | // of whether the reflow was completed or cancelled. The callback will |
557 | 0 | // also delete itself when it is called. |
558 | 0 | ImageReflowCallback* unblocker = new ImageReflowCallback(this, frame, |
559 | 0 | aRequest); |
560 | 0 | parent->PresShell()->PostReflowCallback(unblocker); |
561 | 0 | } |
562 | | |
563 | | NS_IMPL_ADDREF(ImageLoader) |
564 | | NS_IMPL_RELEASE(ImageLoader) |
565 | | |
566 | 0 | NS_INTERFACE_MAP_BEGIN(ImageLoader) |
567 | 0 | NS_INTERFACE_MAP_ENTRY(imgINotificationObserver) |
568 | 0 | NS_INTERFACE_MAP_END |
569 | | |
570 | | NS_IMETHODIMP |
571 | | ImageLoader::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData) |
572 | 0 | { |
573 | 0 | #ifdef MOZ_GECKO_PROFILER |
574 | 0 | nsCString uriString; |
575 | 0 | if (profiler_is_active()) { |
576 | 0 | nsCOMPtr<nsIURI> uri; |
577 | 0 | aRequest->GetFinalURI(getter_AddRefs(uri)); |
578 | 0 | if (uri) { |
579 | 0 | uri->GetSpec(uriString); |
580 | 0 | } |
581 | 0 | } |
582 | 0 |
|
583 | 0 | AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("ImageLoader::Notify", OTHER, uriString); |
584 | 0 | #endif |
585 | 0 |
|
586 | 0 | if (aType == imgINotificationObserver::SIZE_AVAILABLE) { |
587 | 0 | nsCOMPtr<imgIContainer> image; |
588 | 0 | aRequest->GetImage(getter_AddRefs(image)); |
589 | 0 | return OnSizeAvailable(aRequest, image); |
590 | 0 | } |
591 | 0 | |
592 | 0 | if (aType == imgINotificationObserver::IS_ANIMATED) { |
593 | 0 | return OnImageIsAnimated(aRequest); |
594 | 0 | } |
595 | 0 | |
596 | 0 | if (aType == imgINotificationObserver::FRAME_COMPLETE) { |
597 | 0 | return OnFrameComplete(aRequest); |
598 | 0 | } |
599 | 0 | |
600 | 0 | if (aType == imgINotificationObserver::FRAME_UPDATE) { |
601 | 0 | return OnFrameUpdate(aRequest); |
602 | 0 | } |
603 | 0 | |
604 | 0 | if (aType == imgINotificationObserver::DECODE_COMPLETE) { |
605 | 0 | nsCOMPtr<imgIContainer> image; |
606 | 0 | aRequest->GetImage(getter_AddRefs(image)); |
607 | 0 | if (image && mDocument) { |
608 | 0 | image->PropagateUseCounters(mDocument); |
609 | 0 | } |
610 | 0 | } |
611 | 0 |
|
612 | 0 | if (aType == imgINotificationObserver::LOAD_COMPLETE) { |
613 | 0 | return OnLoadComplete(aRequest); |
614 | 0 | } |
615 | 0 | |
616 | 0 | return NS_OK; |
617 | 0 | } |
618 | | |
619 | | nsresult |
620 | | ImageLoader::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage) |
621 | 0 | { |
622 | 0 | nsPresContext* presContext = GetPresContext(); |
623 | 0 | if (!presContext) { |
624 | 0 | return NS_OK; |
625 | 0 | } |
626 | 0 | |
627 | 0 | aImage->SetAnimationMode(presContext->ImageAnimationMode()); |
628 | 0 |
|
629 | 0 | FrameSet* frameSet = mRequestToFrameMap.Get(aRequest); |
630 | 0 | if (!frameSet) { |
631 | 0 | return NS_OK; |
632 | 0 | } |
633 | 0 | |
634 | 0 | for (FrameWithFlags& fwf : *frameSet) { |
635 | 0 | nsIFrame* frame = fwf.mFrame; |
636 | 0 | if (frame->StyleVisibility()->IsVisible()) { |
637 | 0 | frame->MarkNeedsDisplayItemRebuild(); |
638 | 0 | } |
639 | 0 | } |
640 | 0 |
|
641 | 0 | return NS_OK; |
642 | 0 | } |
643 | | |
644 | | nsresult |
645 | | ImageLoader::OnImageIsAnimated(imgIRequest* aRequest) |
646 | 0 | { |
647 | 0 | if (!mDocument) { |
648 | 0 | return NS_OK; |
649 | 0 | } |
650 | 0 | |
651 | 0 | FrameSet* frameSet = mRequestToFrameMap.Get(aRequest); |
652 | 0 | if (!frameSet) { |
653 | 0 | return NS_OK; |
654 | 0 | } |
655 | 0 | |
656 | 0 | // Register with the refresh driver now that we are aware that |
657 | 0 | // we are animated. |
658 | 0 | nsPresContext* presContext = GetPresContext(); |
659 | 0 | if (presContext) { |
660 | 0 | nsLayoutUtils::RegisterImageRequest(presContext, |
661 | 0 | aRequest, |
662 | 0 | nullptr); |
663 | 0 | } |
664 | 0 |
|
665 | 0 | return NS_OK; |
666 | 0 | } |
667 | | |
668 | | nsresult |
669 | | ImageLoader::OnFrameComplete(imgIRequest* aRequest) |
670 | 0 | { |
671 | 0 | if (!mDocument || mInClone) { |
672 | 0 | return NS_OK; |
673 | 0 | } |
674 | 0 | |
675 | 0 | FrameSet* frameSet = mRequestToFrameMap.Get(aRequest); |
676 | 0 | if (!frameSet) { |
677 | 0 | return NS_OK; |
678 | 0 | } |
679 | 0 | |
680 | 0 | // We may need reflow (for example if the image is from shape-outside). |
681 | 0 | RequestReflowIfNeeded(frameSet, aRequest); |
682 | 0 |
|
683 | 0 | // Since we just finished decoding a frame, we always want to paint, in case |
684 | 0 | // we're now able to paint an image that we couldn't paint before (and hence |
685 | 0 | // that we don't have retained data for). |
686 | 0 | DoRedraw(frameSet, /* aForcePaint = */ true); |
687 | 0 |
|
688 | 0 | return NS_OK; |
689 | 0 | } |
690 | | |
691 | | nsresult |
692 | | ImageLoader::OnFrameUpdate(imgIRequest* aRequest) |
693 | 0 | { |
694 | 0 | if (!mDocument || mInClone) { |
695 | 0 | return NS_OK; |
696 | 0 | } |
697 | 0 | |
698 | 0 | FrameSet* frameSet = mRequestToFrameMap.Get(aRequest); |
699 | 0 | if (!frameSet) { |
700 | 0 | return NS_OK; |
701 | 0 | } |
702 | 0 | |
703 | 0 | DoRedraw(frameSet, /* aForcePaint = */ false); |
704 | 0 |
|
705 | 0 | return NS_OK; |
706 | 0 | } |
707 | | |
708 | | nsresult |
709 | | ImageLoader::OnLoadComplete(imgIRequest* aRequest) |
710 | 0 | { |
711 | 0 | if (!mDocument || mInClone) { |
712 | 0 | return NS_OK; |
713 | 0 | } |
714 | 0 | |
715 | 0 | FrameSet* frameSet = mRequestToFrameMap.Get(aRequest); |
716 | 0 | if (!frameSet) { |
717 | 0 | return NS_OK; |
718 | 0 | } |
719 | 0 | |
720 | 0 | // Check if aRequest has an error state. If it does, we need to unblock |
721 | 0 | // Document onload for all the frames associated with this request that |
722 | 0 | // have blocked onload. This is what happens in a CORS mode violation, and |
723 | 0 | // may happen during other network events. |
724 | 0 | uint32_t status = 0; |
725 | 0 | if(NS_SUCCEEDED(aRequest->GetImageStatus(&status)) && |
726 | 0 | status & imgIRequest::STATUS_ERROR) { |
727 | 0 | for (FrameWithFlags& fwf : *frameSet) { |
728 | 0 | if (fwf.mFlags & REQUEST_HAS_BLOCKED_ONLOAD) { |
729 | 0 | // We've blocked onload. Unblock onload and clear the flag. |
730 | 0 | mDocument->UnblockOnload(false); |
731 | 0 | fwf.mFlags &= ~REQUEST_HAS_BLOCKED_ONLOAD; |
732 | 0 | } |
733 | 0 | } |
734 | 0 | } |
735 | 0 |
|
736 | 0 | return NS_OK; |
737 | 0 | } |
738 | | |
739 | | void |
740 | | ImageLoader::FlushUseCounters() |
741 | 0 | { |
742 | 0 | for (auto iter = mImages.Iter(); !iter.Done(); iter.Next()) { |
743 | 0 | nsPtrHashKey<Image>* key = iter.Get(); |
744 | 0 | ImageLoader::Image* image = key->GetKey(); |
745 | 0 |
|
746 | 0 | imgIRequest* request = image->mRequests.GetWeak(mDocument); |
747 | 0 |
|
748 | 0 | nsCOMPtr<imgIContainer> container; |
749 | 0 | request->GetImage(getter_AddRefs(container)); |
750 | 0 | if (container) { |
751 | 0 | static_cast<image::Image*>(container.get())->ReportUseCounters(); |
752 | 0 | } |
753 | 0 | } |
754 | 0 | } |
755 | | |
756 | | bool |
757 | | ImageLoader::ImageReflowCallback::ReflowFinished() |
758 | 0 | { |
759 | 0 | // Check that the frame is still valid. If it isn't, then onload was |
760 | 0 | // unblocked when the frame was removed from the FrameSet in |
761 | 0 | // RemoveRequestToFrameMapping. |
762 | 0 | if (mFrame.IsAlive()) { |
763 | 0 | mLoader->UnblockOnloadIfNeeded(mFrame, mRequest); |
764 | 0 | } |
765 | 0 |
|
766 | 0 | // Get rid of this callback object. |
767 | 0 | delete this; |
768 | 0 |
|
769 | 0 | // We don't need to trigger layout. |
770 | 0 | return false; |
771 | 0 | } |
772 | | |
773 | | void |
774 | | ImageLoader::ImageReflowCallback::ReflowCallbackCanceled() |
775 | 0 | { |
776 | 0 | // Check that the frame is still valid. If it isn't, then onload was |
777 | 0 | // unblocked when the frame was removed from the FrameSet in |
778 | 0 | // RemoveRequestToFrameMapping. |
779 | 0 | if (mFrame.IsAlive()) { |
780 | 0 | mLoader->UnblockOnloadIfNeeded(mFrame, mRequest); |
781 | 0 | } |
782 | 0 |
|
783 | 0 | // Get rid of this callback object. |
784 | 0 | delete this; |
785 | 0 | } |
786 | | |
787 | | } // namespace css |
788 | | } // namespace mozilla |