Line data Source code
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 "ImageLogging.h"
8 : #include "imgRequestProxy.h"
9 : #include "imgIOnloadBlocker.h"
10 : #include "imgLoader.h"
11 : #include "Image.h"
12 : #include "ImageOps.h"
13 : #include "nsError.h"
14 : #include "nsCRTGlue.h"
15 : #include "imgINotificationObserver.h"
16 :
17 : using namespace mozilla::image;
18 :
19 : // The split of imgRequestProxy and imgRequestProxyStatic means that
20 : // certain overridden functions need to be usable in the destructor.
21 : // Since virtual functions can't be used in that way, this class
22 : // provides a behavioural trait for each class to use instead.
23 137 : class ProxyBehaviour
24 : {
25 : public:
26 29 : virtual ~ProxyBehaviour() = default;
27 :
28 : virtual already_AddRefed<mozilla::image::Image> GetImage() const = 0;
29 : virtual bool HasImage() const = 0;
30 : virtual already_AddRefed<ProgressTracker> GetProgressTracker() const = 0;
31 : virtual imgRequest* GetOwner() const = 0;
32 : virtual void SetOwner(imgRequest* aOwner) = 0;
33 : };
34 :
35 87 : class RequestBehaviour : public ProxyBehaviour
36 : {
37 : public:
38 137 : RequestBehaviour() : mOwner(nullptr), mOwnerHasImage(false) {}
39 :
40 : already_AddRefed<mozilla::image::Image>GetImage() const override;
41 : bool HasImage() const override;
42 : already_AddRefed<ProgressTracker> GetProgressTracker() const override;
43 :
44 1137 : imgRequest* GetOwner() const override {
45 1137 : return mOwner;
46 : }
47 :
48 253 : void SetOwner(imgRequest* aOwner) override {
49 253 : mOwner = aOwner;
50 :
51 253 : if (mOwner) {
52 506 : RefPtr<ProgressTracker> ownerProgressTracker = GetProgressTracker();
53 253 : mOwnerHasImage = ownerProgressTracker && ownerProgressTracker->HasImage();
54 : } else {
55 0 : mOwnerHasImage = false;
56 : }
57 253 : }
58 :
59 : private:
60 : // We maintain the following invariant:
61 : // The proxy is registered at most with a single imgRequest as an observer,
62 : // and whenever it is, mOwner points to that object. This helps ensure that
63 : // imgRequestProxy::~imgRequestProxy unregisters the proxy as an observer
64 : // from whatever request it was registered with (if any). This, in turn,
65 : // means that imgRequest::mObservers will not have any stale pointers in it.
66 : RefPtr<imgRequest> mOwner;
67 :
68 : bool mOwnerHasImage;
69 : };
70 :
71 : already_AddRefed<mozilla::image::Image>
72 1374 : RequestBehaviour::GetImage() const
73 : {
74 1374 : if (!mOwnerHasImage) {
75 167 : return nullptr;
76 : }
77 2414 : RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
78 1207 : return progressTracker->GetImage();
79 : }
80 :
81 : already_AddRefed<ProgressTracker>
82 2331 : RequestBehaviour::GetProgressTracker() const
83 : {
84 : // NOTE: It's possible that our mOwner has an Image that it didn't notify
85 : // us about, if we were Canceled before its Image was constructed.
86 : // (Canceling removes us as an observer, so mOwner has no way to notify us).
87 : // That's why this method uses mOwner->GetProgressTracker() instead of just
88 : // mOwner->mProgressTracker -- we might have a null mImage and yet have an
89 : // mOwner with a non-null mImage (and a null mProgressTracker pointer).
90 2331 : return mOwner->GetProgressTracker();
91 : }
92 :
93 2861 : NS_IMPL_ADDREF(imgRequestProxy)
94 2698 : NS_IMPL_RELEASE(imgRequestProxy)
95 :
96 374 : NS_INTERFACE_MAP_BEGIN(imgRequestProxy)
97 374 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, imgIRequest)
98 374 : NS_INTERFACE_MAP_ENTRY(imgIRequest)
99 180 : NS_INTERFACE_MAP_ENTRY(nsIRequest)
100 49 : NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
101 49 : NS_INTERFACE_MAP_ENTRY(nsISecurityInfoProvider)
102 49 : NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITimedChannel,
103 : TimedChannel() != nullptr)
104 48 : NS_INTERFACE_MAP_END
105 :
106 137 : imgRequestProxy::imgRequestProxy() :
107 137 : mBehaviour(new RequestBehaviour),
108 : mURI(nullptr),
109 : mListener(nullptr),
110 : mLoadFlags(nsIRequest::LOAD_NORMAL),
111 : mLockCount(0),
112 : mAnimationConsumers(0),
113 : mCanceled(false),
114 : mIsInLoadGroup(false),
115 : mListenerIsStrongRef(false),
116 : mDecodeRequested(false),
117 274 : mDeferNotifications(false)
118 : {
119 : /* member initializers and constructor code */
120 :
121 137 : }
122 :
123 87 : imgRequestProxy::~imgRequestProxy()
124 : {
125 : /* destructor code */
126 29 : NS_PRECONDITION(!mListener,
127 : "Someone forgot to properly cancel this request!");
128 :
129 : // Unlock the image the proper number of times if we're holding locks on
130 : // it. Note that UnlockImage() decrements mLockCount each time it's called.
131 73 : while (mLockCount) {
132 22 : UnlockImage();
133 : }
134 :
135 29 : ClearAnimationConsumers();
136 :
137 : // Explicitly set mListener to null to ensure that the RemoveProxy
138 : // call below can't send |this| to an arbitrary listener while |this|
139 : // is being destroyed. This is all belt-and-suspenders in view of the
140 : // above assert.
141 29 : NullOutListener();
142 :
143 29 : if (GetOwner()) {
144 : /* Call RemoveProxy with a successful status. This will keep the
145 : channel, if still downloading data, from being canceled if 'this' is
146 : the last observer. This allows the image to continue to download and
147 : be cached even if no one is using it currently.
148 : */
149 29 : mCanceled = true;
150 29 : GetOwner()->RemoveProxy(this, NS_OK);
151 : }
152 87 : }
153 :
154 : nsresult
155 137 : imgRequestProxy::Init(imgRequest* aOwner,
156 : nsILoadGroup* aLoadGroup,
157 : ImageURL* aURI,
158 : imgINotificationObserver* aObserver)
159 : {
160 137 : NS_PRECONDITION(!GetOwner() && !mListener,
161 : "imgRequestProxy is already initialized");
162 :
163 274 : LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequestProxy::Init", "request",
164 : aOwner);
165 :
166 137 : MOZ_ASSERT(mAnimationConsumers == 0, "Cannot have animation before Init");
167 :
168 137 : mBehaviour->SetOwner(aOwner);
169 137 : mListener = aObserver;
170 : // Make sure to addref mListener before the AddProxy call below, since
171 : // that call might well want to release it if the imgRequest has
172 : // already seen OnStopRequest.
173 137 : if (mListener) {
174 97 : mListenerIsStrongRef = true;
175 97 : NS_ADDREF(mListener);
176 : }
177 137 : mLoadGroup = aLoadGroup;
178 137 : mURI = aURI;
179 :
180 : // Note: AddProxy won't send all the On* notifications immediately
181 137 : if (GetOwner()) {
182 137 : GetOwner()->AddProxy(this);
183 : }
184 :
185 274 : return NS_OK;
186 : }
187 :
188 : nsresult
189 0 : imgRequestProxy::ChangeOwner(imgRequest* aNewOwner)
190 : {
191 0 : NS_PRECONDITION(GetOwner(),
192 : "Cannot ChangeOwner on a proxy without an owner!");
193 :
194 0 : if (mCanceled) {
195 : // Ensure that this proxy has received all notifications to date
196 : // before we clean it up when removing it from the old owner below.
197 0 : SyncNotifyListener();
198 : }
199 :
200 : // If we're holding locks, unlock the old image.
201 : // Note that UnlockImage decrements mLockCount each time it's called.
202 0 : uint32_t oldLockCount = mLockCount;
203 0 : while (mLockCount) {
204 0 : UnlockImage();
205 : }
206 :
207 : // If we're holding animation requests, undo them.
208 0 : uint32_t oldAnimationConsumers = mAnimationConsumers;
209 0 : ClearAnimationConsumers();
210 :
211 0 : GetOwner()->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER);
212 :
213 0 : mBehaviour->SetOwner(aNewOwner);
214 :
215 : // If we were locked, apply the locks here
216 0 : for (uint32_t i = 0; i < oldLockCount; i++) {
217 0 : LockImage();
218 : }
219 :
220 : // If we had animation requests, restore them here. Note that we
221 : // do this *after* RemoveProxy, which clears out animation consumers
222 : // (see bug 601723).
223 0 : for (uint32_t i = 0; i < oldAnimationConsumers; i++) {
224 0 : IncrementAnimationConsumers();
225 : }
226 :
227 0 : GetOwner()->AddProxy(this);
228 :
229 : // If we'd previously requested a synchronous decode, request a decode on the
230 : // new image.
231 0 : if (mDecodeRequested) {
232 0 : StartDecoding(imgIContainer::FLAG_NONE);
233 : }
234 :
235 0 : return NS_OK;
236 : }
237 :
238 : void
239 45 : imgRequestProxy::AddToLoadGroup()
240 : {
241 45 : NS_ASSERTION(!mIsInLoadGroup, "Whaa, we're already in the loadgroup!");
242 :
243 45 : if (!mIsInLoadGroup && mLoadGroup) {
244 45 : mLoadGroup->AddRequest(this, nullptr);
245 45 : mIsInLoadGroup = true;
246 : }
247 45 : }
248 :
249 : void
250 166 : imgRequestProxy::RemoveFromLoadGroup(bool releaseLoadGroup)
251 : {
252 166 : if (!mIsInLoadGroup) {
253 121 : return;
254 : }
255 :
256 : /* calling RemoveFromLoadGroup may cause the document to finish
257 : loading, which could result in our death. We need to make sure
258 : that we stay alive long enough to fight another battle... at
259 : least until we exit this function.
260 : */
261 90 : nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
262 :
263 45 : mLoadGroup->RemoveRequest(this, nullptr, NS_OK);
264 45 : mIsInLoadGroup = false;
265 :
266 45 : if (releaseLoadGroup) {
267 : // We're done with the loadgroup, release it.
268 45 : mLoadGroup = nullptr;
269 : }
270 : }
271 :
272 :
273 : /** nsIRequest / imgIRequest methods **/
274 :
275 : NS_IMETHODIMP
276 434 : imgRequestProxy::GetName(nsACString& aName)
277 : {
278 434 : aName.Truncate();
279 :
280 434 : if (mURI) {
281 434 : mURI->GetSpec(aName);
282 : }
283 :
284 434 : return NS_OK;
285 : }
286 :
287 : NS_IMETHODIMP
288 0 : imgRequestProxy::IsPending(bool* _retval)
289 : {
290 0 : return NS_ERROR_NOT_IMPLEMENTED;
291 : }
292 :
293 : NS_IMETHODIMP
294 0 : imgRequestProxy::GetStatus(nsresult* aStatus)
295 : {
296 0 : return NS_ERROR_NOT_IMPLEMENTED;
297 : }
298 :
299 : NS_IMETHODIMP
300 0 : imgRequestProxy::Cancel(nsresult status)
301 : {
302 0 : if (mCanceled) {
303 0 : return NS_ERROR_FAILURE;
304 : }
305 :
306 0 : LOG_SCOPE(gImgLog, "imgRequestProxy::Cancel");
307 :
308 0 : mCanceled = true;
309 :
310 0 : nsCOMPtr<nsIRunnable> ev = new imgCancelRunnable(this, status);
311 0 : return NS_DispatchToCurrentThread(ev);
312 : }
313 :
314 : void
315 0 : imgRequestProxy::DoCancel(nsresult status)
316 : {
317 0 : if (GetOwner()) {
318 0 : GetOwner()->RemoveProxy(this, status);
319 : }
320 :
321 0 : NullOutListener();
322 0 : }
323 :
324 : NS_IMETHODIMP
325 29 : imgRequestProxy::CancelAndForgetObserver(nsresult aStatus)
326 : {
327 : // If mCanceled is true but mListener is non-null, that means
328 : // someone called Cancel() on us but the imgCancelRunnable is still
329 : // pending. We still need to null out mListener before returning
330 : // from this function in this case. That means we want to do the
331 : // RemoveProxy call right now, because we need to deliver the
332 : // onStopRequest.
333 29 : if (mCanceled && !mListener) {
334 0 : return NS_ERROR_FAILURE;
335 : }
336 :
337 58 : LOG_SCOPE(gImgLog, "imgRequestProxy::CancelAndForgetObserver");
338 :
339 29 : mCanceled = true;
340 :
341 : // Now cheat and make sure our removal from loadgroup happens async
342 29 : bool oldIsInLoadGroup = mIsInLoadGroup;
343 29 : mIsInLoadGroup = false;
344 :
345 29 : if (GetOwner()) {
346 29 : GetOwner()->RemoveProxy(this, aStatus);
347 : }
348 :
349 29 : mIsInLoadGroup = oldIsInLoadGroup;
350 :
351 29 : if (mIsInLoadGroup) {
352 0 : NS_DispatchToCurrentThread(NewRunnableMethod("imgRequestProxy::DoRemoveFromLoadGroup",
353 0 : this, &imgRequestProxy::DoRemoveFromLoadGroup));
354 : }
355 :
356 29 : NullOutListener();
357 :
358 29 : return NS_OK;
359 : }
360 :
361 : NS_IMETHODIMP
362 53 : imgRequestProxy::StartDecoding(uint32_t aFlags)
363 : {
364 : // Flag this, so we know to transfer the request if our owner changes
365 53 : mDecodeRequested = true;
366 :
367 106 : RefPtr<Image> image = GetImage();
368 53 : if (image) {
369 17 : return image->StartDecoding(aFlags);
370 : }
371 :
372 36 : if (GetOwner()) {
373 36 : GetOwner()->StartDecoding();
374 : }
375 :
376 36 : return NS_OK;
377 : }
378 :
379 : bool
380 9 : imgRequestProxy::StartDecodingWithResult(uint32_t aFlags)
381 : {
382 : // Flag this, so we know to transfer the request if our owner changes
383 9 : mDecodeRequested = true;
384 :
385 18 : RefPtr<Image> image = GetImage();
386 9 : if (image) {
387 9 : return image->StartDecodingWithResult(aFlags);
388 : }
389 :
390 0 : if (GetOwner()) {
391 0 : GetOwner()->StartDecoding();
392 : }
393 :
394 0 : return false;
395 : }
396 :
397 : NS_IMETHODIMP
398 181 : imgRequestProxy::LockImage()
399 : {
400 181 : mLockCount++;
401 362 : RefPtr<Image> image = GetImage();
402 181 : if (image) {
403 91 : return image->LockImage();
404 : }
405 90 : return NS_OK;
406 : }
407 :
408 : NS_IMETHODIMP
409 122 : imgRequestProxy::UnlockImage()
410 : {
411 122 : MOZ_ASSERT(mLockCount > 0, "calling unlock but no locks!");
412 :
413 122 : mLockCount--;
414 244 : RefPtr<Image> image = GetImage();
415 122 : if (image) {
416 108 : return image->UnlockImage();
417 : }
418 14 : return NS_OK;
419 : }
420 :
421 : NS_IMETHODIMP
422 0 : imgRequestProxy::RequestDiscard()
423 : {
424 0 : RefPtr<Image> image = GetImage();
425 0 : if (image) {
426 0 : return image->RequestDiscard();
427 : }
428 0 : return NS_OK;
429 : }
430 :
431 : NS_IMETHODIMP
432 59 : imgRequestProxy::IncrementAnimationConsumers()
433 : {
434 59 : mAnimationConsumers++;
435 118 : RefPtr<Image> image = GetImage();
436 59 : if (image) {
437 48 : image->IncrementAnimationConsumers();
438 : }
439 118 : return NS_OK;
440 : }
441 :
442 : NS_IMETHODIMP
443 25 : imgRequestProxy::DecrementAnimationConsumers()
444 : {
445 : // We may get here if some responsible code called Increment,
446 : // then called us, but we have meanwhile called ClearAnimationConsumers
447 : // because we needed to get rid of them earlier (see
448 : // imgRequest::RemoveProxy), and hence have nothing left to
449 : // decrement. (In such a case we got rid of the animation consumers
450 : // early, but not the observer.)
451 25 : if (mAnimationConsumers > 0) {
452 25 : mAnimationConsumers--;
453 50 : RefPtr<Image> image = GetImage();
454 25 : if (image) {
455 25 : image->DecrementAnimationConsumers();
456 : }
457 : }
458 25 : return NS_OK;
459 : }
460 :
461 : void
462 140 : imgRequestProxy::ClearAnimationConsumers()
463 : {
464 164 : while (mAnimationConsumers > 0) {
465 24 : DecrementAnimationConsumers();
466 : }
467 116 : }
468 :
469 : NS_IMETHODIMP
470 0 : imgRequestProxy::Suspend()
471 : {
472 0 : return NS_ERROR_NOT_IMPLEMENTED;
473 : }
474 :
475 : NS_IMETHODIMP
476 0 : imgRequestProxy::Resume()
477 : {
478 0 : return NS_ERROR_NOT_IMPLEMENTED;
479 : }
480 :
481 : NS_IMETHODIMP
482 0 : imgRequestProxy::GetLoadGroup(nsILoadGroup** loadGroup)
483 : {
484 0 : NS_IF_ADDREF(*loadGroup = mLoadGroup.get());
485 0 : return NS_OK;
486 : }
487 : NS_IMETHODIMP
488 0 : imgRequestProxy::SetLoadGroup(nsILoadGroup* loadGroup)
489 : {
490 0 : mLoadGroup = loadGroup;
491 0 : return NS_OK;
492 : }
493 :
494 : NS_IMETHODIMP
495 176 : imgRequestProxy::GetLoadFlags(nsLoadFlags* flags)
496 : {
497 176 : *flags = mLoadFlags;
498 176 : return NS_OK;
499 : }
500 : NS_IMETHODIMP
501 137 : imgRequestProxy::SetLoadFlags(nsLoadFlags flags)
502 : {
503 137 : mLoadFlags = flags;
504 137 : return NS_OK;
505 : }
506 :
507 : /** imgIRequest methods **/
508 :
509 : NS_IMETHODIMP
510 925 : imgRequestProxy::GetImage(imgIContainer** aImage)
511 : {
512 925 : NS_ENSURE_TRUE(aImage, NS_ERROR_NULL_POINTER);
513 : // It's possible that our owner has an image but hasn't notified us of it -
514 : // that'll happen if we get Canceled before the owner instantiates its image
515 : // (because Canceling unregisters us as a listener on mOwner). If we're
516 : // in that situation, just grab the image off of mOwner.
517 1850 : RefPtr<Image> image = GetImage();
518 1850 : nsCOMPtr<imgIContainer> imageToReturn;
519 925 : if (image) {
520 909 : imageToReturn = do_QueryInterface(image);
521 : }
522 925 : if (!imageToReturn && GetOwner()) {
523 16 : imageToReturn = GetOwner()->GetImage();
524 : }
525 925 : if (!imageToReturn) {
526 16 : return NS_ERROR_FAILURE;
527 : }
528 :
529 909 : imageToReturn.swap(*aImage);
530 :
531 909 : return NS_OK;
532 : }
533 :
534 : NS_IMETHODIMP
535 659 : imgRequestProxy::GetImageStatus(uint32_t* aStatus)
536 : {
537 1318 : RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
538 659 : *aStatus = progressTracker->GetImageStatus();
539 :
540 1318 : return NS_OK;
541 : }
542 :
543 : NS_IMETHODIMP
544 0 : imgRequestProxy::GetImageErrorCode(nsresult* aStatus)
545 : {
546 0 : if (!GetOwner()) {
547 0 : return NS_ERROR_FAILURE;
548 : }
549 :
550 0 : *aStatus = GetOwner()->GetImageErrorCode();
551 :
552 0 : return NS_OK;
553 : }
554 :
555 : NS_IMETHODIMP
556 110 : imgRequestProxy::GetURI(nsIURI** aURI)
557 : {
558 110 : MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread to convert URI");
559 220 : nsCOMPtr<nsIURI> uri = mURI->ToIURI();
560 110 : uri.forget(aURI);
561 220 : return NS_OK;
562 : }
563 :
564 : nsresult
565 0 : imgRequestProxy::GetCurrentURI(nsIURI** aURI)
566 : {
567 0 : if (!GetOwner()) {
568 0 : return NS_ERROR_FAILURE;
569 : }
570 :
571 0 : return GetOwner()->GetCurrentURI(aURI);
572 : }
573 :
574 : nsresult
575 0 : imgRequestProxy::GetURI(ImageURL** aURI)
576 : {
577 0 : if (!mURI) {
578 0 : return NS_ERROR_FAILURE;
579 : }
580 :
581 0 : NS_ADDREF(*aURI = mURI);
582 :
583 0 : return NS_OK;
584 : }
585 :
586 : NS_IMETHODIMP
587 14 : imgRequestProxy::GetNotificationObserver(imgINotificationObserver** aObserver)
588 : {
589 14 : *aObserver = mListener;
590 14 : NS_IF_ADDREF(*aObserver);
591 14 : return NS_OK;
592 : }
593 :
594 : NS_IMETHODIMP
595 0 : imgRequestProxy::GetMimeType(char** aMimeType)
596 : {
597 0 : if (!GetOwner()) {
598 0 : return NS_ERROR_FAILURE;
599 : }
600 :
601 0 : const char* type = GetOwner()->GetMimeType();
602 0 : if (!type) {
603 0 : return NS_ERROR_FAILURE;
604 : }
605 :
606 0 : *aMimeType = NS_strdup(type);
607 :
608 0 : return NS_OK;
609 : }
610 :
611 92 : static imgRequestProxy* NewProxy(imgRequestProxy* /*aThis*/)
612 : {
613 92 : return new imgRequestProxy();
614 : }
615 :
616 0 : imgRequestProxy* NewStaticProxy(imgRequestProxy* aThis)
617 : {
618 0 : nsCOMPtr<nsIPrincipal> currentPrincipal;
619 0 : aThis->GetImagePrincipal(getter_AddRefs(currentPrincipal));
620 0 : RefPtr<mozilla::image::Image> image = aThis->GetImage();
621 0 : return new imgRequestProxyStatic(image, currentPrincipal);
622 : }
623 :
624 : NS_IMETHODIMP
625 0 : imgRequestProxy::Clone(imgINotificationObserver* aObserver,
626 : imgIRequest** aClone)
627 : {
628 : nsresult result;
629 : imgRequestProxy* proxy;
630 0 : result = Clone(aObserver, &proxy);
631 0 : *aClone = proxy;
632 0 : return result;
633 : }
634 :
635 92 : nsresult imgRequestProxy::Clone(imgINotificationObserver* aObserver,
636 : imgRequestProxy** aClone)
637 : {
638 92 : return PerformClone(aObserver, NewProxy, aClone);
639 : }
640 :
641 : nsresult
642 92 : imgRequestProxy::PerformClone(imgINotificationObserver* aObserver,
643 : imgRequestProxy* (aAllocFn)(imgRequestProxy*),
644 : imgRequestProxy** aClone)
645 : {
646 92 : NS_PRECONDITION(aClone, "Null out param");
647 :
648 184 : LOG_SCOPE(gImgLog, "imgRequestProxy::Clone");
649 :
650 92 : *aClone = nullptr;
651 184 : RefPtr<imgRequestProxy> clone = aAllocFn(this);
652 :
653 : // It is important to call |SetLoadFlags()| before calling |Init()| because
654 : // |Init()| adds the request to the loadgroup.
655 : // When a request is added to a loadgroup, its load flags are merged
656 : // with the load flags of the loadgroup.
657 : // XXXldb That's not true anymore. Stuff from imgLoader adds the
658 : // request to the loadgroup.
659 92 : clone->SetLoadFlags(mLoadFlags);
660 92 : nsresult rv = clone->Init(mBehaviour->GetOwner(), mLoadGroup,
661 92 : mURI, aObserver);
662 92 : if (NS_FAILED(rv)) {
663 0 : return rv;
664 : }
665 :
666 92 : if (GetOwner() && GetOwner()->GetValidator()) {
667 0 : clone->SetNotificationsDeferred(true);
668 0 : GetOwner()->GetValidator()->AddProxy(clone);
669 : }
670 :
671 : // Assign to *aClone before calling Notify so that if the caller expects to
672 : // only be notified for requests it's already holding pointers to it won't be
673 : // surprised.
674 92 : NS_ADDREF(*aClone = clone);
675 :
676 : // This is wrong!!! We need to notify asynchronously, but there's code that
677 : // assumes that we don't. This will be fixed in bug 580466.
678 92 : clone->SyncNotifyListener();
679 :
680 92 : return NS_OK;
681 : }
682 :
683 : NS_IMETHODIMP
684 0 : imgRequestProxy::GetImagePrincipal(nsIPrincipal** aPrincipal)
685 : {
686 0 : if (!GetOwner()) {
687 0 : return NS_ERROR_FAILURE;
688 : }
689 :
690 0 : nsCOMPtr<nsIPrincipal> principal = GetOwner()->GetPrincipal();
691 0 : principal.forget(aPrincipal);
692 0 : return NS_OK;
693 : }
694 :
695 : NS_IMETHODIMP
696 4 : imgRequestProxy::GetMultipart(bool* aMultipart)
697 : {
698 4 : if (!GetOwner()) {
699 0 : return NS_ERROR_FAILURE;
700 : }
701 :
702 4 : *aMultipart = GetOwner()->GetMultipart();
703 :
704 4 : return NS_OK;
705 : }
706 :
707 : NS_IMETHODIMP
708 0 : imgRequestProxy::GetCORSMode(int32_t* aCorsMode)
709 : {
710 0 : if (!GetOwner()) {
711 0 : return NS_ERROR_FAILURE;
712 : }
713 :
714 0 : *aCorsMode = GetOwner()->GetCORSMode();
715 :
716 0 : return NS_OK;
717 : }
718 :
719 : NS_IMETHODIMP
720 0 : imgRequestProxy::BoostPriority(uint32_t aCategory)
721 : {
722 0 : NS_ENSURE_STATE(GetOwner() && !mCanceled);
723 0 : GetOwner()->BoostPriority(aCategory);
724 0 : return NS_OK;
725 : }
726 :
727 : /** nsISupportsPriority methods **/
728 :
729 : NS_IMETHODIMP
730 0 : imgRequestProxy::GetPriority(int32_t* priority)
731 : {
732 0 : NS_ENSURE_STATE(GetOwner());
733 0 : *priority = GetOwner()->Priority();
734 0 : return NS_OK;
735 : }
736 :
737 : NS_IMETHODIMP
738 0 : imgRequestProxy::SetPriority(int32_t priority)
739 : {
740 0 : NS_ENSURE_STATE(GetOwner() && !mCanceled);
741 0 : GetOwner()->AdjustPriority(this, priority - GetOwner()->Priority());
742 0 : return NS_OK;
743 : }
744 :
745 : NS_IMETHODIMP
746 0 : imgRequestProxy::AdjustPriority(int32_t priority)
747 : {
748 : // We don't require |!mCanceled| here. This may be called even if we're
749 : // cancelled, because it's invoked as part of the process of removing an image
750 : // from the load group.
751 0 : NS_ENSURE_STATE(GetOwner());
752 0 : GetOwner()->AdjustPriority(this, priority);
753 0 : return NS_OK;
754 : }
755 :
756 : /** nsISecurityInfoProvider methods **/
757 :
758 : NS_IMETHODIMP
759 0 : imgRequestProxy::GetSecurityInfo(nsISupports** _retval)
760 : {
761 0 : if (GetOwner()) {
762 0 : return GetOwner()->GetSecurityInfo(_retval);
763 : }
764 :
765 0 : *_retval = nullptr;
766 0 : return NS_OK;
767 : }
768 :
769 : NS_IMETHODIMP
770 0 : imgRequestProxy::GetHasTransferredData(bool* hasData)
771 : {
772 0 : if (GetOwner()) {
773 0 : *hasData = GetOwner()->HasTransferredData();
774 : } else {
775 : // The safe thing to do is to claim we have data
776 0 : *hasData = true;
777 : }
778 0 : return NS_OK;
779 : }
780 :
781 : static const char*
782 719 : NotificationTypeToString(int32_t aType)
783 : {
784 719 : switch(aType)
785 : {
786 132 : case imgINotificationObserver::SIZE_AVAILABLE: return "SIZE_AVAILABLE";
787 205 : case imgINotificationObserver::FRAME_UPDATE: return "FRAME_UPDATE";
788 122 : case imgINotificationObserver::FRAME_COMPLETE: return "FRAME_COMPLETE";
789 0 : case imgINotificationObserver::LOAD_COMPLETE: return "LOAD_COMPLETE";
790 122 : case imgINotificationObserver::DECODE_COMPLETE: return "DECODE_COMPLETE";
791 0 : case imgINotificationObserver::DISCARD: return "DISCARD";
792 0 : case imgINotificationObserver::UNLOCKED_DRAW: return "UNLOCKED_DRAW";
793 6 : case imgINotificationObserver::IS_ANIMATED: return "IS_ANIMATED";
794 132 : case imgINotificationObserver::HAS_TRANSPARENCY: return "HAS_TRANSPARENCY";
795 : default:
796 0 : NS_NOTREACHED("Notification list should be exhaustive");
797 0 : return "(unknown notification)";
798 : }
799 : }
800 :
801 : void
802 719 : imgRequestProxy::Notify(int32_t aType, const mozilla::gfx::IntRect* aRect)
803 : {
804 719 : MOZ_ASSERT(aType != imgINotificationObserver::LOAD_COMPLETE,
805 : "Should call OnLoadComplete");
806 :
807 : LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::Notify", "type",
808 719 : NotificationTypeToString(aType));
809 :
810 719 : if (!mListener || mCanceled) {
811 215 : return;
812 : }
813 :
814 : // Make sure the listener stays alive while we notify.
815 1008 : nsCOMPtr<imgINotificationObserver> listener(mListener);
816 :
817 504 : listener->Notify(this, aType, aRect);
818 : }
819 :
820 : void
821 137 : imgRequestProxy::OnLoadComplete(bool aLastPart)
822 : {
823 137 : if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
824 0 : nsAutoCString name;
825 0 : GetName(name);
826 : LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnLoadComplete",
827 0 : "name", name.get());
828 : }
829 :
830 : // There's all sorts of stuff here that could kill us (the OnStopRequest call
831 : // on the listener, the removal from the loadgroup, the release of the
832 : // listener, etc). Don't let them do it.
833 274 : nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
834 :
835 137 : if (mListener && !mCanceled) {
836 : // Hold a ref to the listener while we call it, just in case.
837 186 : nsCOMPtr<imgINotificationObserver> listener(mListener);
838 93 : listener->Notify(this, imgINotificationObserver::LOAD_COMPLETE, nullptr);
839 : }
840 :
841 : // If we're expecting more data from a multipart channel, re-add ourself
842 : // to the loadgroup so that the document doesn't lose track of the load.
843 : // If the request is already a background request and there's more data
844 : // coming, we can just leave the request in the loadgroup as-is.
845 137 : if (aLastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) {
846 137 : RemoveFromLoadGroup(aLastPart);
847 : // More data is coming, so change the request to be a background request
848 : // and put it back in the loadgroup.
849 137 : if (!aLastPart) {
850 0 : mLoadFlags |= nsIRequest::LOAD_BACKGROUND;
851 0 : AddToLoadGroup();
852 : }
853 : }
854 :
855 137 : if (mListenerIsStrongRef && aLastPart) {
856 97 : NS_PRECONDITION(mListener, "How did that happen?");
857 : // Drop our strong ref to the listener now that we're done with
858 : // everything. Note that this can cancel us and other fun things
859 : // like that. Don't add anything in this method after this point.
860 97 : imgINotificationObserver* obs = mListener;
861 97 : mListenerIsStrongRef = false;
862 97 : NS_RELEASE(obs);
863 : }
864 137 : }
865 :
866 : void
867 173 : imgRequestProxy::BlockOnload()
868 : {
869 173 : if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
870 0 : nsAutoCString name;
871 0 : GetName(name);
872 : LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::BlockOnload",
873 0 : "name", name.get());
874 : }
875 :
876 346 : nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
877 173 : if (blocker) {
878 119 : blocker->BlockOnload(this);
879 : }
880 173 : }
881 :
882 : void
883 173 : imgRequestProxy::UnblockOnload()
884 : {
885 173 : if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
886 0 : nsAutoCString name;
887 0 : GetName(name);
888 : LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::UnblockOnload",
889 0 : "name", name.get());
890 : }
891 :
892 346 : nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
893 173 : if (blocker) {
894 119 : blocker->UnblockOnload(this);
895 : }
896 173 : }
897 :
898 : void
899 58 : imgRequestProxy::NullOutListener()
900 : {
901 : // If we have animation consumers, then they don't matter anymore
902 58 : if (mListener) {
903 29 : ClearAnimationConsumers();
904 : }
905 :
906 58 : if (mListenerIsStrongRef) {
907 : // Releasing could do weird reentery stuff, so just play it super-safe
908 0 : nsCOMPtr<imgINotificationObserver> obs;
909 0 : obs.swap(mListener);
910 0 : mListenerIsStrongRef = false;
911 : } else {
912 58 : mListener = nullptr;
913 : }
914 58 : }
915 :
916 : NS_IMETHODIMP
917 0 : imgRequestProxy::GetStaticRequest(imgIRequest** aReturn)
918 : {
919 : imgRequestProxy* proxy;
920 0 : nsresult result = GetStaticRequest(&proxy);
921 0 : *aReturn = proxy;
922 0 : return result;
923 : }
924 :
925 : nsresult
926 0 : imgRequestProxy::GetStaticRequest(imgRequestProxy** aReturn)
927 : {
928 0 : *aReturn = nullptr;
929 0 : RefPtr<Image> image = GetImage();
930 :
931 : bool animated;
932 0 : if (!image || (NS_SUCCEEDED(image->GetAnimated(&animated)) && !animated)) {
933 : // Early exit - we're not animated, so we don't have to do anything.
934 0 : NS_ADDREF(*aReturn = this);
935 0 : return NS_OK;
936 : }
937 :
938 : // Check for errors in the image. Callers code rely on GetStaticRequest
939 : // failing in this case, though with FrozenImage there's no technical reason
940 : // for it anymore.
941 0 : if (image->HasError()) {
942 0 : return NS_ERROR_FAILURE;
943 : }
944 :
945 : // We are animated. We need to create a frozen version of this image.
946 0 : RefPtr<Image> frozenImage = ImageOps::Freeze(image);
947 :
948 : // Create a static imgRequestProxy with our new extracted frame.
949 0 : nsCOMPtr<nsIPrincipal> currentPrincipal;
950 0 : GetImagePrincipal(getter_AddRefs(currentPrincipal));
951 : RefPtr<imgRequestProxy> req = new imgRequestProxyStatic(frozenImage,
952 0 : currentPrincipal);
953 0 : req->Init(nullptr, nullptr, mURI, nullptr);
954 :
955 0 : NS_ADDREF(*aReturn = req);
956 :
957 0 : return NS_OK;
958 : }
959 :
960 : void
961 4 : imgRequestProxy::NotifyListener()
962 : {
963 : // It would be nice to notify the observer directly in the status tracker
964 : // instead of through the proxy, but there are several places we do extra
965 : // processing when we receive notifications (like OnStopRequest()), and we
966 : // need to check mCanceled everywhere too.
967 :
968 8 : RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
969 4 : if (GetOwner()) {
970 : // Send the notifications to our listener asynchronously.
971 4 : progressTracker->Notify(this);
972 : } else {
973 : // We don't have an imgRequest, so we can only notify the clone of our
974 : // current state, but we still have to do that asynchronously.
975 0 : MOZ_ASSERT(HasImage(),
976 : "if we have no imgRequest, we should have an Image");
977 0 : progressTracker->NotifyCurrentState(this);
978 : }
979 4 : }
980 :
981 : void
982 92 : imgRequestProxy::SyncNotifyListener()
983 : {
984 : // It would be nice to notify the observer directly in the status tracker
985 : // instead of through the proxy, but there are several places we do extra
986 : // processing when we receive notifications (like OnStopRequest()), and we
987 : // need to check mCanceled everywhere too.
988 :
989 184 : RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
990 92 : progressTracker->SyncNotify(this);
991 92 : }
992 :
993 : void
994 116 : imgRequestProxy::SetHasImage()
995 : {
996 232 : RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
997 116 : MOZ_ASSERT(progressTracker);
998 232 : RefPtr<Image> image = progressTracker->GetImage();
999 116 : MOZ_ASSERT(image);
1000 :
1001 : // Force any private status related to the owner to reflect
1002 : // the presence of an image;
1003 116 : mBehaviour->SetOwner(mBehaviour->GetOwner());
1004 :
1005 : // Apply any locks we have
1006 192 : for (uint32_t i = 0; i < mLockCount; ++i) {
1007 76 : image->LockImage();
1008 : }
1009 :
1010 : // Apply any animation consumers we have
1011 127 : for (uint32_t i = 0; i < mAnimationConsumers; i++) {
1012 11 : image->IncrementAnimationConsumers();
1013 : }
1014 116 : }
1015 :
1016 : already_AddRefed<ProgressTracker>
1017 871 : imgRequestProxy::GetProgressTracker() const
1018 : {
1019 871 : return mBehaviour->GetProgressTracker();
1020 : }
1021 :
1022 : already_AddRefed<mozilla::image::Image>
1023 1374 : imgRequestProxy::GetImage() const
1024 : {
1025 1374 : return mBehaviour->GetImage();
1026 : }
1027 :
1028 : bool
1029 0 : RequestBehaviour::HasImage() const
1030 : {
1031 0 : if (!mOwnerHasImage) {
1032 0 : return false;
1033 : }
1034 0 : RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
1035 0 : return progressTracker ? progressTracker->HasImage() : false;
1036 : }
1037 :
1038 : bool
1039 0 : imgRequestProxy::HasImage() const
1040 : {
1041 0 : return mBehaviour->HasImage();
1042 : }
1043 :
1044 : imgRequest*
1045 929 : imgRequestProxy::GetOwner() const
1046 : {
1047 929 : return mBehaviour->GetOwner();
1048 : }
1049 :
1050 : ////////////////// imgRequestProxyStatic methods
1051 :
1052 0 : class StaticBehaviour : public ProxyBehaviour
1053 : {
1054 : public:
1055 0 : explicit StaticBehaviour(mozilla::image::Image* aImage) : mImage(aImage) {}
1056 :
1057 : already_AddRefed<mozilla::image::Image>
1058 0 : GetImage() const override {
1059 0 : RefPtr<mozilla::image::Image> image = mImage;
1060 0 : return image.forget();
1061 : }
1062 :
1063 0 : bool HasImage() const override {
1064 0 : return mImage;
1065 : }
1066 :
1067 0 : already_AddRefed<ProgressTracker> GetProgressTracker()
1068 : const override {
1069 0 : return mImage->GetProgressTracker();
1070 : }
1071 :
1072 0 : imgRequest* GetOwner() const override {
1073 0 : return nullptr;
1074 : }
1075 :
1076 0 : void SetOwner(imgRequest* aOwner) override {
1077 0 : MOZ_ASSERT(!aOwner,
1078 : "We shouldn't be giving static requests a non-null owner.");
1079 0 : }
1080 :
1081 : private:
1082 : // Our image. We have to hold a strong reference here, because that's normally
1083 : // the job of the underlying request.
1084 : RefPtr<mozilla::image::Image> mImage;
1085 : };
1086 :
1087 0 : imgRequestProxyStatic::imgRequestProxyStatic(mozilla::image::Image* aImage,
1088 0 : nsIPrincipal* aPrincipal)
1089 0 : : mPrincipal(aPrincipal)
1090 : {
1091 0 : mBehaviour = mozilla::MakeUnique<StaticBehaviour>(aImage);
1092 0 : }
1093 :
1094 : NS_IMETHODIMP
1095 0 : imgRequestProxyStatic::GetImagePrincipal(nsIPrincipal** aPrincipal)
1096 : {
1097 0 : if (!mPrincipal) {
1098 0 : return NS_ERROR_FAILURE;
1099 : }
1100 :
1101 0 : NS_ADDREF(*aPrincipal = mPrincipal);
1102 :
1103 0 : return NS_OK;
1104 : }
1105 :
1106 : nsresult
1107 0 : imgRequestProxyStatic::Clone(imgINotificationObserver* aObserver,
1108 : imgRequestProxy** aClone)
1109 : {
1110 0 : return PerformClone(aObserver, NewStaticProxy, aClone);
1111 9 : }
|