image/src/imgRequestProxy.cpp

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

mercurial