|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
2 * |
|
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 #include "imgStatusTracker.h" |
|
8 |
|
9 #include "imgIContainer.h" |
|
10 #include "imgRequestProxy.h" |
|
11 #include "imgDecoderObserver.h" |
|
12 #include "Image.h" |
|
13 #include "ImageLogging.h" |
|
14 #include "nsNetUtil.h" |
|
15 #include "nsIObserverService.h" |
|
16 |
|
17 #include "mozilla/Assertions.h" |
|
18 #include "mozilla/Services.h" |
|
19 |
|
20 using namespace mozilla::image; |
|
21 using mozilla::WeakPtr; |
|
22 |
|
23 class imgStatusTrackerObserver : public imgDecoderObserver |
|
24 { |
|
25 public: |
|
26 imgStatusTrackerObserver(imgStatusTracker* aTracker) |
|
27 : mTracker(aTracker->asWeakPtr()) |
|
28 { |
|
29 MOZ_ASSERT(aTracker); |
|
30 } |
|
31 |
|
32 void SetTracker(imgStatusTracker* aTracker) |
|
33 { |
|
34 MOZ_ASSERT(aTracker); |
|
35 mTracker = aTracker->asWeakPtr(); |
|
36 } |
|
37 |
|
38 /** imgDecoderObserver methods **/ |
|
39 |
|
40 virtual void OnStartDecode() MOZ_OVERRIDE |
|
41 { |
|
42 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartDecode"); |
|
43 nsRefPtr<imgStatusTracker> tracker = mTracker.get(); |
|
44 if (!tracker) { return; } |
|
45 tracker->RecordStartDecode(); |
|
46 if (!tracker->IsMultipart()) { |
|
47 tracker->RecordBlockOnload(); |
|
48 } |
|
49 } |
|
50 |
|
51 virtual void OnStartRequest() MOZ_OVERRIDE |
|
52 { |
|
53 NS_NOTREACHED("imgStatusTrackerObserver(imgDecoderObserver)::OnStartRequest"); |
|
54 } |
|
55 |
|
56 virtual void OnStartContainer() MOZ_OVERRIDE |
|
57 { |
|
58 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartContainer"); |
|
59 nsRefPtr<imgStatusTracker> tracker = mTracker.get(); |
|
60 if (!tracker) { return; } |
|
61 nsRefPtr<Image> image = tracker->GetImage();; |
|
62 tracker->RecordStartContainer(image); |
|
63 } |
|
64 |
|
65 virtual void OnStartFrame() MOZ_OVERRIDE |
|
66 { |
|
67 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartFrame"); |
|
68 nsRefPtr<imgStatusTracker> tracker = mTracker.get(); |
|
69 if (!tracker) { return; } |
|
70 tracker->RecordStartFrame(); |
|
71 } |
|
72 |
|
73 virtual void FrameChanged(const nsIntRect* dirtyRect) MOZ_OVERRIDE |
|
74 { |
|
75 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::FrameChanged"); |
|
76 nsRefPtr<imgStatusTracker> tracker = mTracker.get(); |
|
77 if (!tracker) { return; } |
|
78 tracker->RecordFrameChanged(dirtyRect); |
|
79 } |
|
80 |
|
81 virtual void OnStopFrame() MOZ_OVERRIDE |
|
82 { |
|
83 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopFrame"); |
|
84 nsRefPtr<imgStatusTracker> tracker = mTracker.get(); |
|
85 if (!tracker) { return; } |
|
86 tracker->RecordStopFrame(); |
|
87 tracker->RecordUnblockOnload(); |
|
88 } |
|
89 |
|
90 virtual void OnStopDecode(nsresult aStatus) MOZ_OVERRIDE |
|
91 { |
|
92 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopDecode"); |
|
93 nsRefPtr<imgStatusTracker> tracker = mTracker.get(); |
|
94 if (!tracker) { return; } |
|
95 tracker->RecordStopDecode(aStatus); |
|
96 |
|
97 // This is really hacky. We need to handle the case where we start decoding, |
|
98 // block onload, but then hit an error before we get to our first frame. |
|
99 tracker->RecordUnblockOnload(); |
|
100 } |
|
101 |
|
102 virtual void OnStopRequest(bool aLastPart, nsresult aStatus) MOZ_OVERRIDE |
|
103 { |
|
104 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopRequest"); |
|
105 nsRefPtr<imgStatusTracker> tracker = mTracker.get(); |
|
106 if (!tracker) { return; } |
|
107 tracker->RecordStopRequest(aLastPart, aStatus); |
|
108 } |
|
109 |
|
110 virtual void OnDiscard() MOZ_OVERRIDE |
|
111 { |
|
112 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnDiscard"); |
|
113 nsRefPtr<imgStatusTracker> tracker = mTracker.get(); |
|
114 if (!tracker) { return; } |
|
115 tracker->RecordDiscard(); |
|
116 } |
|
117 |
|
118 virtual void OnUnlockedDraw() MOZ_OVERRIDE |
|
119 { |
|
120 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnUnlockedDraw"); |
|
121 nsRefPtr<imgStatusTracker> tracker = mTracker.get(); |
|
122 if (!tracker) { return; } |
|
123 NS_ABORT_IF_FALSE(tracker->HasImage(), |
|
124 "OnUnlockedDraw callback before we've created our image"); |
|
125 tracker->RecordUnlockedDraw(); |
|
126 } |
|
127 |
|
128 virtual void OnImageIsAnimated() MOZ_OVERRIDE |
|
129 { |
|
130 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnImageIsAnimated"); |
|
131 nsRefPtr<imgStatusTracker> tracker = mTracker.get(); |
|
132 if (!tracker) { return; } |
|
133 tracker->RecordImageIsAnimated(); |
|
134 } |
|
135 |
|
136 virtual void OnError() MOZ_OVERRIDE |
|
137 { |
|
138 LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnError"); |
|
139 nsRefPtr<imgStatusTracker> tracker = mTracker.get(); |
|
140 if (!tracker) { return; } |
|
141 tracker->RecordError(); |
|
142 } |
|
143 |
|
144 protected: |
|
145 virtual ~imgStatusTrackerObserver() {} |
|
146 |
|
147 private: |
|
148 WeakPtr<imgStatusTracker> mTracker; |
|
149 }; |
|
150 |
|
151 // imgStatusTracker methods |
|
152 |
|
153 imgStatusTracker::imgStatusTracker(Image* aImage) |
|
154 : mImage(aImage), |
|
155 mState(0), |
|
156 mImageStatus(imgIRequest::STATUS_NONE), |
|
157 mIsMultipart(false), |
|
158 mHadLastPart(false), |
|
159 mHasBeenDecoded(false) |
|
160 { |
|
161 mTrackerObserver = new imgStatusTrackerObserver(this); |
|
162 } |
|
163 |
|
164 // Private, used only by CloneForRecording. |
|
165 imgStatusTracker::imgStatusTracker(const imgStatusTracker& aOther) |
|
166 : mImage(aOther.mImage), |
|
167 mState(aOther.mState), |
|
168 mImageStatus(aOther.mImageStatus), |
|
169 mIsMultipart(aOther.mIsMultipart), |
|
170 mHadLastPart(aOther.mHadLastPart), |
|
171 mHasBeenDecoded(aOther.mHasBeenDecoded) |
|
172 // Note: we explicitly don't copy several fields: |
|
173 // - mRequestRunnable, because it won't be nulled out when the |
|
174 // mRequestRunnable's Run function eventually gets called. |
|
175 // - mProperties, because we don't need it and it'd just point at the same |
|
176 // object |
|
177 // - mConsumers, because we don't need to talk to consumers |
|
178 // - mInvalidRect, because the point of it is to be fired off and reset |
|
179 { |
|
180 mTrackerObserver = new imgStatusTrackerObserver(this); |
|
181 } |
|
182 |
|
183 imgStatusTracker::~imgStatusTracker() |
|
184 {} |
|
185 |
|
186 imgStatusTrackerInit::imgStatusTrackerInit(mozilla::image::Image* aImage, |
|
187 imgStatusTracker* aTracker) |
|
188 { |
|
189 MOZ_ASSERT(aImage); |
|
190 |
|
191 if (aTracker) { |
|
192 mTracker = aTracker; |
|
193 mTracker->SetImage(aImage); |
|
194 } else { |
|
195 mTracker = new imgStatusTracker(aImage); |
|
196 } |
|
197 aImage->SetStatusTracker(mTracker); |
|
198 MOZ_ASSERT(mTracker); |
|
199 } |
|
200 |
|
201 imgStatusTrackerInit::~imgStatusTrackerInit() |
|
202 { |
|
203 mTracker->ResetImage(); |
|
204 } |
|
205 |
|
206 void |
|
207 imgStatusTracker::SetImage(Image* aImage) |
|
208 { |
|
209 NS_ABORT_IF_FALSE(aImage, "Setting null image"); |
|
210 NS_ABORT_IF_FALSE(!mImage, "Setting image when we already have one"); |
|
211 mImage = aImage; |
|
212 } |
|
213 |
|
214 void |
|
215 imgStatusTracker::ResetImage() |
|
216 { |
|
217 NS_ABORT_IF_FALSE(mImage, "Resetting image when it's already null!"); |
|
218 mImage = nullptr; |
|
219 } |
|
220 |
|
221 bool |
|
222 imgStatusTracker::IsLoading() const |
|
223 { |
|
224 // Checking for whether OnStopRequest has fired allows us to say we're |
|
225 // loading before OnStartRequest gets called, letting the request properly |
|
226 // get removed from the cache in certain cases. |
|
227 return !(mState & stateRequestStopped); |
|
228 } |
|
229 |
|
230 uint32_t |
|
231 imgStatusTracker::GetImageStatus() const |
|
232 { |
|
233 return mImageStatus; |
|
234 } |
|
235 |
|
236 // A helper class to allow us to call SyncNotify asynchronously. |
|
237 class imgRequestNotifyRunnable : public nsRunnable |
|
238 { |
|
239 public: |
|
240 imgRequestNotifyRunnable(imgStatusTracker* aTracker, |
|
241 imgRequestProxy* aRequestProxy) |
|
242 : mTracker(aTracker) |
|
243 { |
|
244 MOZ_ASSERT(NS_IsMainThread(), "Should be created on the main thread"); |
|
245 MOZ_ASSERT(aRequestProxy, "aRequestProxy should not be null"); |
|
246 MOZ_ASSERT(aTracker, "aTracker should not be null"); |
|
247 mProxies.AppendElement(aRequestProxy); |
|
248 } |
|
249 |
|
250 NS_IMETHOD Run() |
|
251 { |
|
252 MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread"); |
|
253 MOZ_ASSERT(mTracker, "mTracker should not be null"); |
|
254 for (uint32_t i = 0; i < mProxies.Length(); ++i) { |
|
255 mProxies[i]->SetNotificationsDeferred(false); |
|
256 mTracker->SyncNotify(mProxies[i]); |
|
257 } |
|
258 |
|
259 mTracker->mRequestRunnable = nullptr; |
|
260 return NS_OK; |
|
261 } |
|
262 |
|
263 void AddProxy(imgRequestProxy* aRequestProxy) |
|
264 { |
|
265 mProxies.AppendElement(aRequestProxy); |
|
266 } |
|
267 |
|
268 void RemoveProxy(imgRequestProxy* aRequestProxy) |
|
269 { |
|
270 mProxies.RemoveElement(aRequestProxy); |
|
271 } |
|
272 |
|
273 private: |
|
274 friend class imgStatusTracker; |
|
275 |
|
276 nsRefPtr<imgStatusTracker> mTracker; |
|
277 nsTArray< nsRefPtr<imgRequestProxy> > mProxies; |
|
278 }; |
|
279 |
|
280 void |
|
281 imgStatusTracker::Notify(imgRequestProxy* proxy) |
|
282 { |
|
283 MOZ_ASSERT(NS_IsMainThread(), "imgRequestProxy is not threadsafe"); |
|
284 #ifdef PR_LOGGING |
|
285 if (mImage && mImage->GetURI()) { |
|
286 nsRefPtr<ImageURL> uri(mImage->GetURI()); |
|
287 nsAutoCString spec; |
|
288 uri->GetSpec(spec); |
|
289 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::Notify async", "uri", spec.get()); |
|
290 } else { |
|
291 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::Notify async", "uri", "<unknown>"); |
|
292 } |
|
293 #endif |
|
294 |
|
295 proxy->SetNotificationsDeferred(true); |
|
296 |
|
297 // If we have an existing runnable that we can use, we just append this proxy |
|
298 // to its list of proxies to be notified. This ensures we don't unnecessarily |
|
299 // delay onload. |
|
300 imgRequestNotifyRunnable* runnable = static_cast<imgRequestNotifyRunnable*>(mRequestRunnable.get()); |
|
301 if (runnable) { |
|
302 runnable->AddProxy(proxy); |
|
303 } else { |
|
304 mRequestRunnable = new imgRequestNotifyRunnable(this, proxy); |
|
305 NS_DispatchToCurrentThread(mRequestRunnable); |
|
306 } |
|
307 } |
|
308 |
|
309 // A helper class to allow us to call SyncNotify asynchronously for a given, |
|
310 // fixed, state. |
|
311 class imgStatusNotifyRunnable : public nsRunnable |
|
312 { |
|
313 public: |
|
314 imgStatusNotifyRunnable(imgStatusTracker* statusTracker, |
|
315 imgRequestProxy* requestproxy) |
|
316 : mStatusTracker(statusTracker), mProxy(requestproxy) |
|
317 { |
|
318 MOZ_ASSERT(NS_IsMainThread(), "Should be created on the main thread"); |
|
319 MOZ_ASSERT(requestproxy, "requestproxy cannot be null"); |
|
320 MOZ_ASSERT(statusTracker, "status should not be null"); |
|
321 mImage = statusTracker->GetImage(); |
|
322 } |
|
323 |
|
324 NS_IMETHOD Run() |
|
325 { |
|
326 MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread"); |
|
327 mProxy->SetNotificationsDeferred(false); |
|
328 |
|
329 mStatusTracker->SyncNotify(mProxy); |
|
330 return NS_OK; |
|
331 } |
|
332 |
|
333 private: |
|
334 nsRefPtr<imgStatusTracker> mStatusTracker; |
|
335 // We have to hold on to a reference to the tracker's image, just in case |
|
336 // it goes away while we're in the event queue. |
|
337 nsRefPtr<Image> mImage; |
|
338 nsRefPtr<imgRequestProxy> mProxy; |
|
339 }; |
|
340 |
|
341 void |
|
342 imgStatusTracker::NotifyCurrentState(imgRequestProxy* proxy) |
|
343 { |
|
344 MOZ_ASSERT(NS_IsMainThread(), "imgRequestProxy is not threadsafe"); |
|
345 #ifdef PR_LOGGING |
|
346 nsRefPtr<ImageURL> uri; |
|
347 proxy->GetURI(getter_AddRefs(uri)); |
|
348 nsAutoCString spec; |
|
349 uri->GetSpec(spec); |
|
350 LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::NotifyCurrentState", "uri", spec.get()); |
|
351 #endif |
|
352 |
|
353 proxy->SetNotificationsDeferred(true); |
|
354 |
|
355 // We don't keep track of |
|
356 nsCOMPtr<nsIRunnable> ev = new imgStatusNotifyRunnable(this, proxy); |
|
357 NS_DispatchToCurrentThread(ev); |
|
358 } |
|
359 |
|
360 #define NOTIFY_IMAGE_OBSERVERS(func) \ |
|
361 do { \ |
|
362 ProxyArray::ForwardIterator iter(proxies); \ |
|
363 while (iter.HasMore()) { \ |
|
364 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); \ |
|
365 if (proxy && !proxy->NotificationsDeferred()) { \ |
|
366 proxy->func; \ |
|
367 } \ |
|
368 } \ |
|
369 } while (false); |
|
370 |
|
371 /* static */ void |
|
372 imgStatusTracker::SyncNotifyState(ProxyArray& proxies, |
|
373 bool hasImage, uint32_t state, |
|
374 nsIntRect& dirtyRect, bool hadLastPart) |
|
375 { |
|
376 MOZ_ASSERT(NS_IsMainThread()); |
|
377 // OnStartRequest |
|
378 if (state & stateRequestStarted) |
|
379 NOTIFY_IMAGE_OBSERVERS(OnStartRequest()); |
|
380 |
|
381 // OnStartContainer |
|
382 if (state & stateHasSize) |
|
383 NOTIFY_IMAGE_OBSERVERS(OnStartContainer()); |
|
384 |
|
385 // OnStartDecode |
|
386 if (state & stateDecodeStarted) |
|
387 NOTIFY_IMAGE_OBSERVERS(OnStartDecode()); |
|
388 |
|
389 // BlockOnload |
|
390 if (state & stateBlockingOnload) |
|
391 NOTIFY_IMAGE_OBSERVERS(BlockOnload()); |
|
392 |
|
393 if (hasImage) { |
|
394 // OnFrameUpdate |
|
395 // If there's any content in this frame at all (always true for |
|
396 // vector images, true for raster images that have decoded at |
|
397 // least one frame) then send OnFrameUpdate. |
|
398 if (!dirtyRect.IsEmpty()) |
|
399 NOTIFY_IMAGE_OBSERVERS(OnFrameUpdate(&dirtyRect)); |
|
400 |
|
401 if (state & stateFrameStopped) |
|
402 NOTIFY_IMAGE_OBSERVERS(OnStopFrame()); |
|
403 |
|
404 // OnImageIsAnimated |
|
405 if (state & stateImageIsAnimated) |
|
406 NOTIFY_IMAGE_OBSERVERS(OnImageIsAnimated()); |
|
407 } |
|
408 |
|
409 if (state & stateDecodeStopped) { |
|
410 NS_ABORT_IF_FALSE(hasImage, "stopped decoding without ever having an image?"); |
|
411 NOTIFY_IMAGE_OBSERVERS(OnStopDecode()); |
|
412 } |
|
413 |
|
414 if (state & stateRequestStopped) { |
|
415 NOTIFY_IMAGE_OBSERVERS(OnStopRequest(hadLastPart)); |
|
416 } |
|
417 } |
|
418 |
|
419 ImageStatusDiff |
|
420 imgStatusTracker::Difference(imgStatusTracker* aOther) const |
|
421 { |
|
422 MOZ_ASSERT(aOther, "aOther cannot be null"); |
|
423 ImageStatusDiff diff; |
|
424 diff.diffState = ~mState & aOther->mState & ~stateRequestStarted; |
|
425 diff.diffImageStatus = ~mImageStatus & aOther->mImageStatus; |
|
426 diff.unblockedOnload = mState & stateBlockingOnload && !(aOther->mState & stateBlockingOnload); |
|
427 diff.unsetDecodeStarted = mImageStatus & imgIRequest::STATUS_DECODE_STARTED |
|
428 && !(aOther->mImageStatus & imgIRequest::STATUS_DECODE_STARTED); |
|
429 diff.foundError = (mImageStatus != imgIRequest::STATUS_ERROR) |
|
430 && (aOther->mImageStatus == imgIRequest::STATUS_ERROR); |
|
431 |
|
432 MOZ_ASSERT(!mIsMultipart || aOther->mIsMultipart, "mIsMultipart should be monotonic"); |
|
433 diff.foundIsMultipart = !mIsMultipart && aOther->mIsMultipart; |
|
434 diff.foundLastPart = !mHadLastPart && aOther->mHadLastPart; |
|
435 |
|
436 diff.gotDecoded = !mHasBeenDecoded && aOther->mHasBeenDecoded; |
|
437 |
|
438 // Only record partial invalidations if we haven't been decoded before. |
|
439 // When images are re-decoded after discarding, we don't want to display |
|
440 // partially decoded versions to the user. |
|
441 const uint32_t combinedStatus = mImageStatus | aOther->mImageStatus; |
|
442 const bool doInvalidations = !(mHasBeenDecoded || aOther->mHasBeenDecoded) |
|
443 || combinedStatus & imgIRequest::STATUS_ERROR |
|
444 || combinedStatus & imgIRequest::STATUS_DECODE_COMPLETE; |
|
445 |
|
446 // Record and reset the invalid rectangle. |
|
447 // XXX(seth): We shouldn't be resetting anything here; see bug 910441. |
|
448 if (doInvalidations) { |
|
449 diff.invalidRect = aOther->mInvalidRect; |
|
450 aOther->mInvalidRect.SetEmpty(); |
|
451 } |
|
452 |
|
453 return diff; |
|
454 } |
|
455 |
|
456 ImageStatusDiff |
|
457 imgStatusTracker::DecodeStateAsDifference() const |
|
458 { |
|
459 ImageStatusDiff diff; |
|
460 diff.diffState = mState & ~stateRequestStarted; |
|
461 |
|
462 // All other ImageStatusDiff fields are intentionally left at their default |
|
463 // values; we only want to notify decode state changes. |
|
464 |
|
465 return diff; |
|
466 } |
|
467 |
|
468 void |
|
469 imgStatusTracker::ApplyDifference(const ImageStatusDiff& aDiff) |
|
470 { |
|
471 LOG_SCOPE(GetImgLog(), "imgStatusTracker::ApplyDifference"); |
|
472 |
|
473 // We must not modify or notify for the start-load state, which happens from Necko callbacks. |
|
474 uint32_t loadState = mState & stateRequestStarted; |
|
475 |
|
476 // Synchronize our state. |
|
477 mState |= aDiff.diffState | loadState; |
|
478 if (aDiff.unblockedOnload) |
|
479 mState &= ~stateBlockingOnload; |
|
480 |
|
481 mIsMultipart = mIsMultipart || aDiff.foundIsMultipart; |
|
482 mHadLastPart = mHadLastPart || aDiff.foundLastPart; |
|
483 mHasBeenDecoded = mHasBeenDecoded || aDiff.gotDecoded; |
|
484 |
|
485 // Update the image status. There are some subtle points which are handled below. |
|
486 mImageStatus |= aDiff.diffImageStatus; |
|
487 |
|
488 // Unset bits which can get unset as part of the decoding process. |
|
489 if (aDiff.unsetDecodeStarted) |
|
490 mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED; |
|
491 |
|
492 // The error state is sticky and overrides all other bits. |
|
493 if (mImageStatus & imgIRequest::STATUS_ERROR) |
|
494 mImageStatus = imgIRequest::STATUS_ERROR; |
|
495 } |
|
496 |
|
497 void |
|
498 imgStatusTracker::SyncNotifyDifference(const ImageStatusDiff& diff) |
|
499 { |
|
500 MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only"); |
|
501 LOG_SCOPE(GetImgLog(), "imgStatusTracker::SyncNotifyDifference"); |
|
502 |
|
503 nsIntRect invalidRect = mInvalidRect.Union(diff.invalidRect); |
|
504 |
|
505 SyncNotifyState(mConsumers, !!mImage, diff.diffState, invalidRect, mHadLastPart); |
|
506 |
|
507 mInvalidRect.SetEmpty(); |
|
508 |
|
509 if (diff.unblockedOnload) { |
|
510 ProxyArray::ForwardIterator iter(mConsumers); |
|
511 while (iter.HasMore()) { |
|
512 // Hold on to a reference to this proxy, since notifying the state can |
|
513 // cause it to disappear. |
|
514 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); |
|
515 |
|
516 if (proxy && !proxy->NotificationsDeferred()) { |
|
517 SendUnblockOnload(proxy); |
|
518 } |
|
519 } |
|
520 } |
|
521 |
|
522 if (diff.foundError) { |
|
523 FireFailureNotification(); |
|
524 } |
|
525 } |
|
526 |
|
527 already_AddRefed<imgStatusTracker> |
|
528 imgStatusTracker::CloneForRecording() |
|
529 { |
|
530 // Grab a ref to this to ensure it isn't deleted. |
|
531 nsRefPtr<imgStatusTracker> thisStatusTracker = this; |
|
532 nsRefPtr<imgStatusTracker> clone = new imgStatusTracker(*thisStatusTracker); |
|
533 return clone.forget(); |
|
534 } |
|
535 |
|
536 void |
|
537 imgStatusTracker::SyncNotify(imgRequestProxy* proxy) |
|
538 { |
|
539 MOZ_ASSERT(NS_IsMainThread(), "imgRequestProxy is not threadsafe"); |
|
540 #ifdef PR_LOGGING |
|
541 nsRefPtr<ImageURL> uri; |
|
542 proxy->GetURI(getter_AddRefs(uri)); |
|
543 nsAutoCString spec; |
|
544 uri->GetSpec(spec); |
|
545 LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgStatusTracker::SyncNotify", "uri", spec.get()); |
|
546 #endif |
|
547 |
|
548 nsIntRect r; |
|
549 if (mImage) { |
|
550 // XXX - Should only send partial rects here, but that needs to |
|
551 // wait until we fix up the observer interface |
|
552 r = mImage->FrameRect(imgIContainer::FRAME_CURRENT); |
|
553 } |
|
554 |
|
555 ProxyArray array; |
|
556 array.AppendElement(proxy->asWeakPtr()); |
|
557 SyncNotifyState(array, !!mImage, mState, r, mHadLastPart); |
|
558 } |
|
559 |
|
560 void |
|
561 imgStatusTracker::EmulateRequestFinished(imgRequestProxy* aProxy, |
|
562 nsresult aStatus) |
|
563 { |
|
564 MOZ_ASSERT(NS_IsMainThread(), |
|
565 "SyncNotifyState and mConsumers are not threadsafe"); |
|
566 nsCOMPtr<imgIRequest> kungFuDeathGrip(aProxy); |
|
567 |
|
568 // In certain cases the request might not have started yet. |
|
569 // We still need to fulfill the contract. |
|
570 if (!(mState & stateRequestStarted)) { |
|
571 aProxy->OnStartRequest(); |
|
572 } |
|
573 |
|
574 if (mState & stateBlockingOnload) { |
|
575 aProxy->UnblockOnload(); |
|
576 } |
|
577 |
|
578 if (!(mState & stateRequestStopped)) { |
|
579 aProxy->OnStopRequest(true); |
|
580 } |
|
581 } |
|
582 |
|
583 void |
|
584 imgStatusTracker::AddConsumer(imgRequestProxy* aConsumer) |
|
585 { |
|
586 MOZ_ASSERT(NS_IsMainThread()); |
|
587 mConsumers.AppendElementUnlessExists(aConsumer->asWeakPtr()); |
|
588 } |
|
589 |
|
590 // XXX - The last argument should go away. |
|
591 bool |
|
592 imgStatusTracker::RemoveConsumer(imgRequestProxy* aConsumer, nsresult aStatus) |
|
593 { |
|
594 MOZ_ASSERT(NS_IsMainThread()); |
|
595 // Remove the proxy from the list. |
|
596 bool removed = mConsumers.RemoveElement(aConsumer); |
|
597 |
|
598 // Consumers can get confused if they don't get all the proper teardown |
|
599 // notifications. Part ways on good terms. |
|
600 if (removed && !aConsumer->NotificationsDeferred()) { |
|
601 EmulateRequestFinished(aConsumer, aStatus); |
|
602 } |
|
603 |
|
604 // Make sure we don't give callbacks to a consumer that isn't interested in |
|
605 // them any more. |
|
606 imgRequestNotifyRunnable* runnable = static_cast<imgRequestNotifyRunnable*>(mRequestRunnable.get()); |
|
607 if (aConsumer->NotificationsDeferred() && runnable) { |
|
608 runnable->RemoveProxy(aConsumer); |
|
609 aConsumer->SetNotificationsDeferred(false); |
|
610 } |
|
611 |
|
612 return removed; |
|
613 } |
|
614 |
|
615 bool |
|
616 imgStatusTracker::FirstConsumerIs(imgRequestProxy* aConsumer) |
|
617 { |
|
618 MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only"); |
|
619 ProxyArray::ForwardIterator iter(mConsumers); |
|
620 while (iter.HasMore()) { |
|
621 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); |
|
622 if (proxy) { |
|
623 return proxy.get() == aConsumer; |
|
624 } |
|
625 } |
|
626 return false; |
|
627 } |
|
628 |
|
629 void |
|
630 imgStatusTracker::RecordCancel() |
|
631 { |
|
632 if (!(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL)) |
|
633 mImageStatus = imgIRequest::STATUS_ERROR; |
|
634 } |
|
635 |
|
636 void |
|
637 imgStatusTracker::RecordLoaded() |
|
638 { |
|
639 NS_ABORT_IF_FALSE(mImage, "RecordLoaded called before we have an Image"); |
|
640 mState |= stateRequestStarted | stateHasSize | stateRequestStopped; |
|
641 mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE | imgIRequest::STATUS_LOAD_COMPLETE; |
|
642 mHadLastPart = true; |
|
643 } |
|
644 |
|
645 void |
|
646 imgStatusTracker::RecordDecoded() |
|
647 { |
|
648 NS_ABORT_IF_FALSE(mImage, "RecordDecoded called before we have an Image"); |
|
649 mState |= stateDecodeStarted | stateDecodeStopped | stateFrameStopped; |
|
650 mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE | imgIRequest::STATUS_DECODE_COMPLETE; |
|
651 mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED; |
|
652 } |
|
653 |
|
654 void |
|
655 imgStatusTracker::RecordStartDecode() |
|
656 { |
|
657 NS_ABORT_IF_FALSE(mImage, "RecordStartDecode without an Image"); |
|
658 mState |= stateDecodeStarted; |
|
659 mImageStatus |= imgIRequest::STATUS_DECODE_STARTED; |
|
660 } |
|
661 |
|
662 void |
|
663 imgStatusTracker::SendStartDecode(imgRequestProxy* aProxy) |
|
664 { |
|
665 MOZ_ASSERT(NS_IsMainThread()); |
|
666 if (!aProxy->NotificationsDeferred()) |
|
667 aProxy->OnStartDecode(); |
|
668 } |
|
669 |
|
670 void |
|
671 imgStatusTracker::RecordStartContainer(imgIContainer* aContainer) |
|
672 { |
|
673 NS_ABORT_IF_FALSE(mImage, |
|
674 "RecordStartContainer called before we have an Image"); |
|
675 NS_ABORT_IF_FALSE(mImage == aContainer, |
|
676 "RecordStartContainer called with wrong Image"); |
|
677 mState |= stateHasSize; |
|
678 mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE; |
|
679 } |
|
680 |
|
681 void |
|
682 imgStatusTracker::SendStartContainer(imgRequestProxy* aProxy) |
|
683 { |
|
684 MOZ_ASSERT(NS_IsMainThread()); |
|
685 if (!aProxy->NotificationsDeferred()) |
|
686 aProxy->OnStartContainer(); |
|
687 } |
|
688 |
|
689 void |
|
690 imgStatusTracker::RecordStartFrame() |
|
691 { |
|
692 mInvalidRect.SetEmpty(); |
|
693 } |
|
694 |
|
695 // No SendStartFrame since it's not observed below us. |
|
696 |
|
697 void |
|
698 imgStatusTracker::RecordStopFrame() |
|
699 { |
|
700 NS_ABORT_IF_FALSE(mImage, "RecordStopFrame called before we have an Image"); |
|
701 mState |= stateFrameStopped; |
|
702 mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE; |
|
703 } |
|
704 |
|
705 void |
|
706 imgStatusTracker::SendStopFrame(imgRequestProxy* aProxy) |
|
707 { |
|
708 MOZ_ASSERT(NS_IsMainThread()); |
|
709 if (!aProxy->NotificationsDeferred()) |
|
710 aProxy->OnStopFrame(); |
|
711 } |
|
712 |
|
713 void |
|
714 imgStatusTracker::RecordStopDecode(nsresult aStatus) |
|
715 { |
|
716 NS_ABORT_IF_FALSE(mImage, |
|
717 "RecordStopDecode called before we have an Image"); |
|
718 mState |= stateDecodeStopped; |
|
719 |
|
720 if (NS_SUCCEEDED(aStatus) && mImageStatus != imgIRequest::STATUS_ERROR) { |
|
721 mImageStatus |= imgIRequest::STATUS_DECODE_COMPLETE; |
|
722 mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED; |
|
723 mHasBeenDecoded = true; |
|
724 // If we weren't successful, clear all success status bits and set error. |
|
725 } else { |
|
726 mImageStatus = imgIRequest::STATUS_ERROR; |
|
727 } |
|
728 } |
|
729 |
|
730 void |
|
731 imgStatusTracker::SendStopDecode(imgRequestProxy* aProxy, |
|
732 nsresult aStatus) |
|
733 { |
|
734 MOZ_ASSERT(NS_IsMainThread()); |
|
735 if (!aProxy->NotificationsDeferred()) |
|
736 aProxy->OnStopDecode(); |
|
737 } |
|
738 |
|
739 void |
|
740 imgStatusTracker::RecordDiscard() |
|
741 { |
|
742 NS_ABORT_IF_FALSE(mImage, |
|
743 "RecordDiscard called before we have an Image"); |
|
744 // Clear the state bits we no longer deserve. |
|
745 uint32_t stateBitsToClear = stateDecodeStopped; |
|
746 mState &= ~stateBitsToClear; |
|
747 |
|
748 // Clear the status bits we no longer deserve. |
|
749 uint32_t statusBitsToClear = imgIRequest::STATUS_DECODE_STARTED | |
|
750 imgIRequest::STATUS_FRAME_COMPLETE | |
|
751 imgIRequest::STATUS_DECODE_COMPLETE; |
|
752 mImageStatus &= ~statusBitsToClear; |
|
753 } |
|
754 |
|
755 void |
|
756 imgStatusTracker::SendDiscard(imgRequestProxy* aProxy) |
|
757 { |
|
758 MOZ_ASSERT(NS_IsMainThread()); |
|
759 if (!aProxy->NotificationsDeferred()) |
|
760 aProxy->OnDiscard(); |
|
761 } |
|
762 |
|
763 |
|
764 void |
|
765 imgStatusTracker::RecordUnlockedDraw() |
|
766 { |
|
767 NS_ABORT_IF_FALSE(mImage, |
|
768 "RecordUnlockedDraw called before we have an Image"); |
|
769 } |
|
770 |
|
771 void |
|
772 imgStatusTracker::RecordImageIsAnimated() |
|
773 { |
|
774 NS_ABORT_IF_FALSE(mImage, |
|
775 "RecordImageIsAnimated called before we have an Image"); |
|
776 mState |= stateImageIsAnimated; |
|
777 } |
|
778 |
|
779 void |
|
780 imgStatusTracker::SendImageIsAnimated(imgRequestProxy* aProxy) |
|
781 { |
|
782 MOZ_ASSERT(NS_IsMainThread()); |
|
783 if (!aProxy->NotificationsDeferred()) |
|
784 aProxy->OnImageIsAnimated(); |
|
785 } |
|
786 |
|
787 void |
|
788 imgStatusTracker::SendUnlockedDraw(imgRequestProxy* aProxy) |
|
789 { |
|
790 MOZ_ASSERT(NS_IsMainThread()); |
|
791 if (!aProxy->NotificationsDeferred()) |
|
792 aProxy->OnUnlockedDraw(); |
|
793 } |
|
794 |
|
795 void |
|
796 imgStatusTracker::OnUnlockedDraw() |
|
797 { |
|
798 MOZ_ASSERT(NS_IsMainThread()); |
|
799 RecordUnlockedDraw(); |
|
800 ProxyArray::ForwardIterator iter(mConsumers); |
|
801 while (iter.HasMore()) { |
|
802 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); |
|
803 if (proxy) { |
|
804 SendUnlockedDraw(proxy); |
|
805 } |
|
806 } |
|
807 } |
|
808 |
|
809 void |
|
810 imgStatusTracker::RecordFrameChanged(const nsIntRect* aDirtyRect) |
|
811 { |
|
812 NS_ABORT_IF_FALSE(mImage, |
|
813 "RecordFrameChanged called before we have an Image"); |
|
814 mInvalidRect = mInvalidRect.Union(*aDirtyRect); |
|
815 } |
|
816 |
|
817 void |
|
818 imgStatusTracker::SendFrameChanged(imgRequestProxy* aProxy, |
|
819 const nsIntRect* aDirtyRect) |
|
820 { |
|
821 MOZ_ASSERT(NS_IsMainThread()); |
|
822 if (!aProxy->NotificationsDeferred()) |
|
823 aProxy->OnFrameUpdate(aDirtyRect); |
|
824 } |
|
825 |
|
826 /* non-virtual sort-of-nsIRequestObserver methods */ |
|
827 void |
|
828 imgStatusTracker::RecordStartRequest() |
|
829 { |
|
830 // We're starting a new load, so clear any status and state bits indicating |
|
831 // load/decode |
|
832 mImageStatus &= ~imgIRequest::STATUS_LOAD_PARTIAL; |
|
833 mImageStatus &= ~imgIRequest::STATUS_LOAD_COMPLETE; |
|
834 mImageStatus &= ~imgIRequest::STATUS_FRAME_COMPLETE; |
|
835 mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED; |
|
836 mImageStatus &= ~imgIRequest::STATUS_DECODE_COMPLETE; |
|
837 mState &= ~stateRequestStarted; |
|
838 mState &= ~stateDecodeStarted; |
|
839 mState &= ~stateDecodeStopped; |
|
840 mState &= ~stateRequestStopped; |
|
841 mState &= ~stateBlockingOnload; |
|
842 mState &= ~stateImageIsAnimated; |
|
843 |
|
844 mState |= stateRequestStarted; |
|
845 } |
|
846 |
|
847 void |
|
848 imgStatusTracker::SendStartRequest(imgRequestProxy* aProxy) |
|
849 { |
|
850 MOZ_ASSERT(NS_IsMainThread()); |
|
851 if (!aProxy->NotificationsDeferred()) |
|
852 aProxy->OnStartRequest(); |
|
853 } |
|
854 |
|
855 void |
|
856 imgStatusTracker::OnStartRequest() |
|
857 { |
|
858 MOZ_ASSERT(NS_IsMainThread()); |
|
859 RecordStartRequest(); |
|
860 ProxyArray::ForwardIterator iter(mConsumers); |
|
861 while (iter.HasMore()) { |
|
862 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); |
|
863 if (proxy) { |
|
864 SendStartRequest(proxy); |
|
865 } |
|
866 } |
|
867 } |
|
868 |
|
869 void |
|
870 imgStatusTracker::RecordStopRequest(bool aLastPart, |
|
871 nsresult aStatus) |
|
872 { |
|
873 mHadLastPart = aLastPart; |
|
874 mState |= stateRequestStopped; |
|
875 |
|
876 // If we were successful in loading, note that the image is complete. |
|
877 if (NS_SUCCEEDED(aStatus) && mImageStatus != imgIRequest::STATUS_ERROR) |
|
878 mImageStatus |= imgIRequest::STATUS_LOAD_COMPLETE; |
|
879 else |
|
880 mImageStatus = imgIRequest::STATUS_ERROR; |
|
881 } |
|
882 |
|
883 void |
|
884 imgStatusTracker::SendStopRequest(imgRequestProxy* aProxy, |
|
885 bool aLastPart, |
|
886 nsresult aStatus) |
|
887 { |
|
888 MOZ_ASSERT(NS_IsMainThread()); |
|
889 if (!aProxy->NotificationsDeferred()) { |
|
890 aProxy->OnStopRequest(aLastPart); |
|
891 } |
|
892 } |
|
893 |
|
894 class OnStopRequestEvent : public nsRunnable |
|
895 { |
|
896 public: |
|
897 OnStopRequestEvent(imgStatusTracker* aTracker, |
|
898 bool aLastPart, |
|
899 nsresult aStatus) |
|
900 : mTracker(aTracker) |
|
901 , mLastPart(aLastPart) |
|
902 , mStatus(aStatus) |
|
903 { |
|
904 MOZ_ASSERT(!NS_IsMainThread(), "Should be created off the main thread"); |
|
905 MOZ_ASSERT(aTracker, "aTracker should not be null"); |
|
906 } |
|
907 |
|
908 NS_IMETHOD Run() |
|
909 { |
|
910 MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread"); |
|
911 MOZ_ASSERT(mTracker, "mTracker should not be null"); |
|
912 mTracker->OnStopRequest(mLastPart, mStatus); |
|
913 return NS_OK; |
|
914 } |
|
915 private: |
|
916 nsRefPtr<imgStatusTracker> mTracker; |
|
917 bool mLastPart; |
|
918 nsresult mStatus; |
|
919 }; |
|
920 |
|
921 void |
|
922 imgStatusTracker::OnStopRequest(bool aLastPart, |
|
923 nsresult aStatus) |
|
924 { |
|
925 if (!NS_IsMainThread()) { |
|
926 NS_DispatchToMainThread( |
|
927 new OnStopRequestEvent(this, aLastPart, aStatus)); |
|
928 return; |
|
929 } |
|
930 bool preexistingError = mImageStatus == imgIRequest::STATUS_ERROR; |
|
931 |
|
932 RecordStopRequest(aLastPart, aStatus); |
|
933 /* notify the kids */ |
|
934 ProxyArray::ForwardIterator srIter(mConsumers); |
|
935 while (srIter.HasMore()) { |
|
936 nsRefPtr<imgRequestProxy> proxy = srIter.GetNext().get(); |
|
937 if (proxy) { |
|
938 SendStopRequest(proxy, aLastPart, aStatus); |
|
939 } |
|
940 } |
|
941 |
|
942 if (NS_FAILED(aStatus) && !preexistingError) { |
|
943 FireFailureNotification(); |
|
944 } |
|
945 } |
|
946 |
|
947 void |
|
948 imgStatusTracker::OnDiscard() |
|
949 { |
|
950 MOZ_ASSERT(NS_IsMainThread()); |
|
951 RecordDiscard(); |
|
952 |
|
953 /* notify the kids */ |
|
954 ProxyArray::ForwardIterator iter(mConsumers); |
|
955 while (iter.HasMore()) { |
|
956 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); |
|
957 if (proxy) { |
|
958 SendDiscard(proxy); |
|
959 } |
|
960 } |
|
961 } |
|
962 |
|
963 void |
|
964 imgStatusTracker::FrameChanged(const nsIntRect* aDirtyRect) |
|
965 { |
|
966 MOZ_ASSERT(NS_IsMainThread()); |
|
967 RecordFrameChanged(aDirtyRect); |
|
968 |
|
969 /* notify the kids */ |
|
970 ProxyArray::ForwardIterator iter(mConsumers); |
|
971 while (iter.HasMore()) { |
|
972 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); |
|
973 if (proxy) { |
|
974 SendFrameChanged(proxy, aDirtyRect); |
|
975 } |
|
976 } |
|
977 } |
|
978 |
|
979 void |
|
980 imgStatusTracker::OnStopFrame() |
|
981 { |
|
982 MOZ_ASSERT(NS_IsMainThread()); |
|
983 RecordStopFrame(); |
|
984 |
|
985 /* notify the kids */ |
|
986 ProxyArray::ForwardIterator iter(mConsumers); |
|
987 while (iter.HasMore()) { |
|
988 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); |
|
989 if (proxy) { |
|
990 SendStopFrame(proxy); |
|
991 } |
|
992 } |
|
993 } |
|
994 |
|
995 void |
|
996 imgStatusTracker::OnDataAvailable() |
|
997 { |
|
998 if (!NS_IsMainThread()) { |
|
999 // Note: SetHasImage calls Image::Lock and Image::IncrementAnimationCounter |
|
1000 // so subsequent calls or dispatches which Unlock or Decrement~ should |
|
1001 // be issued after this to avoid race conditions. |
|
1002 NS_DispatchToMainThread( |
|
1003 NS_NewRunnableMethod(this, &imgStatusTracker::OnDataAvailable)); |
|
1004 return; |
|
1005 } |
|
1006 // Notify any imgRequestProxys that are observing us that we have an Image. |
|
1007 ProxyArray::ForwardIterator iter(mConsumers); |
|
1008 while (iter.HasMore()) { |
|
1009 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); |
|
1010 if (proxy) { |
|
1011 proxy->SetHasImage(); |
|
1012 } |
|
1013 } |
|
1014 } |
|
1015 |
|
1016 void |
|
1017 imgStatusTracker::RecordBlockOnload() |
|
1018 { |
|
1019 MOZ_ASSERT(!(mState & stateBlockingOnload)); |
|
1020 mState |= stateBlockingOnload; |
|
1021 } |
|
1022 |
|
1023 void |
|
1024 imgStatusTracker::SendBlockOnload(imgRequestProxy* aProxy) |
|
1025 { |
|
1026 MOZ_ASSERT(NS_IsMainThread()); |
|
1027 if (!aProxy->NotificationsDeferred()) { |
|
1028 aProxy->BlockOnload(); |
|
1029 } |
|
1030 } |
|
1031 |
|
1032 void |
|
1033 imgStatusTracker::RecordUnblockOnload() |
|
1034 { |
|
1035 mState &= ~stateBlockingOnload; |
|
1036 } |
|
1037 |
|
1038 void |
|
1039 imgStatusTracker::SendUnblockOnload(imgRequestProxy* aProxy) |
|
1040 { |
|
1041 MOZ_ASSERT(NS_IsMainThread()); |
|
1042 if (!aProxy->NotificationsDeferred()) { |
|
1043 aProxy->UnblockOnload(); |
|
1044 } |
|
1045 } |
|
1046 |
|
1047 void |
|
1048 imgStatusTracker::MaybeUnblockOnload() |
|
1049 { |
|
1050 if (!NS_IsMainThread()) { |
|
1051 NS_DispatchToMainThread( |
|
1052 NS_NewRunnableMethod(this, &imgStatusTracker::MaybeUnblockOnload)); |
|
1053 return; |
|
1054 } |
|
1055 if (!(mState & stateBlockingOnload)) { |
|
1056 return; |
|
1057 } |
|
1058 |
|
1059 RecordUnblockOnload(); |
|
1060 |
|
1061 ProxyArray::ForwardIterator iter(mConsumers); |
|
1062 while (iter.HasMore()) { |
|
1063 nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); |
|
1064 if (proxy) { |
|
1065 SendUnblockOnload(proxy); |
|
1066 } |
|
1067 } |
|
1068 } |
|
1069 |
|
1070 void |
|
1071 imgStatusTracker::RecordError() |
|
1072 { |
|
1073 mImageStatus = imgIRequest::STATUS_ERROR; |
|
1074 } |
|
1075 |
|
1076 void |
|
1077 imgStatusTracker::FireFailureNotification() |
|
1078 { |
|
1079 MOZ_ASSERT(NS_IsMainThread()); |
|
1080 |
|
1081 // Some kind of problem has happened with image decoding. |
|
1082 // Report the URI to net:failed-to-process-uri-conent observers. |
|
1083 if (mImage) { |
|
1084 // Should be on main thread, so ok to create a new nsIURI. |
|
1085 nsCOMPtr<nsIURI> uri; |
|
1086 { |
|
1087 nsRefPtr<ImageURL> threadsafeUriData = mImage->GetURI(); |
|
1088 uri = threadsafeUriData ? threadsafeUriData->ToIURI() : nullptr; |
|
1089 } |
|
1090 if (uri) { |
|
1091 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
1092 if (os) { |
|
1093 os->NotifyObservers(uri, "net:failed-to-process-uri-content", nullptr); |
|
1094 } |
|
1095 } |
|
1096 } |
|
1097 } |