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
9 #include <utility>
10
11 #include "Image.h"
12 #include "ImageLogging.h"
13 #include "ImageOps.h"
14 #include "ImageTypes.h"
15 #include "imgINotificationObserver.h"
16 #include "imgLoader.h"
17 #include "mozilla/dom/Document.h"
18 #include "mozilla/Telemetry.h" // for Telemetry
19 #include "mozilla/dom/DocGroup.h" // for DocGroup
20 #include "nsCRTGlue.h"
21 #include "nsError.h"
22
23 using namespace mozilla;
24 using namespace mozilla::image;
25 using mozilla::dom::Document;
26
27 // The split of imgRequestProxy and imgRequestProxyStatic means that
28 // certain overridden functions need to be usable in the destructor.
29 // Since virtual functions can't be used in that way, this class
30 // provides a behavioural trait for each class to use instead.
31 class ProxyBehaviour {
32 public:
33 virtual ~ProxyBehaviour() = default;
34
35 virtual already_AddRefed<mozilla::image::Image> GetImage() const = 0;
36 virtual bool HasImage() const = 0;
37 virtual already_AddRefed<ProgressTracker> GetProgressTracker() const = 0;
38 virtual imgRequest* GetOwner() const = 0;
39 virtual void SetOwner(imgRequest* aOwner) = 0;
40 };
41
42 class RequestBehaviour : public ProxyBehaviour {
43 public:
RequestBehaviour()44 RequestBehaviour() : mOwner(nullptr), mOwnerHasImage(false) {}
45
46 already_AddRefed<mozilla::image::Image> GetImage() const override;
47 bool HasImage() const override;
48 already_AddRefed<ProgressTracker> GetProgressTracker() const override;
49
GetOwner() const50 imgRequest* GetOwner() const override { return mOwner; }
51
SetOwner(imgRequest * aOwner)52 void SetOwner(imgRequest* aOwner) override {
53 mOwner = aOwner;
54
55 if (mOwner) {
56 RefPtr<ProgressTracker> ownerProgressTracker = GetProgressTracker();
57 mOwnerHasImage = ownerProgressTracker && ownerProgressTracker->HasImage();
58 } else {
59 mOwnerHasImage = false;
60 }
61 }
62
63 private:
64 // We maintain the following invariant:
65 // The proxy is registered at most with a single imgRequest as an observer,
66 // and whenever it is, mOwner points to that object. This helps ensure that
67 // imgRequestProxy::~imgRequestProxy unregisters the proxy as an observer
68 // from whatever request it was registered with (if any). This, in turn,
69 // means that imgRequest::mObservers will not have any stale pointers in it.
70 RefPtr<imgRequest> mOwner;
71
72 bool mOwnerHasImage;
73 };
74
GetImage() const75 already_AddRefed<mozilla::image::Image> RequestBehaviour::GetImage() const {
76 if (!mOwnerHasImage) {
77 return nullptr;
78 }
79 RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
80 return progressTracker->GetImage();
81 }
82
GetProgressTracker() const83 already_AddRefed<ProgressTracker> RequestBehaviour::GetProgressTracker() const {
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 return mOwner->GetProgressTracker();
91 }
92
93 NS_IMPL_ADDREF(imgRequestProxy)
NS_IMPL_RELEASE(imgRequestProxy)94 NS_IMPL_RELEASE(imgRequestProxy)
95
96 NS_INTERFACE_MAP_BEGIN(imgRequestProxy)
97 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, PreloaderBase)
98 NS_INTERFACE_MAP_ENTRY(imgIRequest)
99 NS_INTERFACE_MAP_ENTRY(nsIRequest)
100 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
101 NS_INTERFACE_MAP_ENTRY_CONCRETE(imgRequestProxy)
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 mForceDispatchLoadGroup(false),
115 mListenerIsStrongRef(false),
116 mDecodeRequested(false),
117 mPendingNotify(false),
118 mValidating(false),
119 mHadListener(false),
120 mHadDispatch(false) {
121 /* member initializers and constructor code */
122 LOG_FUNC(gImgLog, "imgRequestProxy::imgRequestProxy");
123 }
124
~imgRequestProxy()125 imgRequestProxy::~imgRequestProxy() {
126 /* destructor code */
127 MOZ_ASSERT(!mListener, "Someone forgot to properly cancel this request!");
128
129 // If we had a listener, that means we would have issued notifications. With
130 // bug 1359833, we added support for main thread scheduler groups. Each
131 // imgRequestProxy may have its own associated listener, document and/or
132 // scheduler group. Typically most imgRequestProxy belong to the same
133 // document, or have no listener, which means we will want to execute all main
134 // thread code in that shared scheduler group. Less frequently, there may be
135 // multiple imgRequests and they have separate documents, which means that
136 // when we issue state notifications, some or all need to be dispatched to the
137 // appropriate scheduler group for each request. This should be rare, so we
138 // want to monitor the frequency of dispatching in the wild.
139 if (mHadListener) {
140 mozilla::Telemetry::Accumulate(mozilla::Telemetry::IMAGE_REQUEST_DISPATCHED,
141 mHadDispatch);
142 }
143
144 MOZ_RELEASE_ASSERT(!mLockCount, "Someone forgot to unlock on time?");
145
146 ClearAnimationConsumers();
147
148 // Explicitly set mListener to null to ensure that the RemoveProxy
149 // call below can't send |this| to an arbitrary listener while |this|
150 // is being destroyed. This is all belt-and-suspenders in view of the
151 // above assert.
152 NullOutListener();
153
154 /* Call RemoveProxy with a successful status. This will keep the
155 channel, if still downloading data, from being canceled if 'this' is
156 the last observer. This allows the image to continue to download and
157 be cached even if no one is using it currently.
158 */
159 mCanceled = true;
160 RemoveFromOwner(NS_OK);
161
162 RemoveFromLoadGroup();
163 LOG_FUNC(gImgLog, "imgRequestProxy::~imgRequestProxy");
164 }
165
Init(imgRequest * aOwner,nsILoadGroup * aLoadGroup,Document * aLoadingDocument,nsIURI * aURI,imgINotificationObserver * aObserver)166 nsresult imgRequestProxy::Init(imgRequest* aOwner, nsILoadGroup* aLoadGroup,
167 Document* aLoadingDocument, nsIURI* aURI,
168 imgINotificationObserver* aObserver) {
169 MOZ_ASSERT(!GetOwner() && !mListener,
170 "imgRequestProxy is already initialized");
171
172 LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequestProxy::Init", "request", aOwner);
173
174 MOZ_ASSERT(mAnimationConsumers == 0, "Cannot have animation before Init");
175
176 mBehaviour->SetOwner(aOwner);
177 mListener = aObserver;
178 // Make sure to addref mListener before the AddToOwner call below, since
179 // that call might well want to release it if the imgRequest has
180 // already seen OnStopRequest.
181 if (mListener) {
182 mHadListener = true;
183 mListenerIsStrongRef = true;
184 NS_ADDREF(mListener);
185 }
186 mLoadGroup = aLoadGroup;
187 mURI = aURI;
188
189 // Note: AddToOwner won't send all the On* notifications immediately
190 AddToOwner(aLoadingDocument);
191
192 return NS_OK;
193 }
194
ChangeOwner(imgRequest * aNewOwner)195 nsresult imgRequestProxy::ChangeOwner(imgRequest* aNewOwner) {
196 MOZ_ASSERT(GetOwner(), "Cannot ChangeOwner on a proxy without an owner!");
197
198 if (mCanceled) {
199 // Ensure that this proxy has received all notifications to date
200 // before we clean it up when removing it from the old owner below.
201 SyncNotifyListener();
202 }
203
204 // If we're holding locks, unlock the old image.
205 // Note that UnlockImage decrements mLockCount each time it's called.
206 uint32_t oldLockCount = mLockCount;
207 while (mLockCount) {
208 UnlockImage();
209 }
210
211 // If we're holding animation requests, undo them.
212 uint32_t oldAnimationConsumers = mAnimationConsumers;
213 ClearAnimationConsumers();
214
215 GetOwner()->RemoveProxy(this, NS_OK);
216
217 mBehaviour->SetOwner(aNewOwner);
218 MOZ_ASSERT(!GetValidator(), "New owner cannot be validating!");
219
220 // If we were locked, apply the locks here
221 for (uint32_t i = 0; i < oldLockCount; i++) {
222 LockImage();
223 }
224
225 // If we had animation requests, restore them here. Note that we
226 // do this *after* RemoveProxy, which clears out animation consumers
227 // (see bug 601723).
228 for (uint32_t i = 0; i < oldAnimationConsumers; i++) {
229 IncrementAnimationConsumers();
230 }
231
232 AddToOwner(nullptr);
233 return NS_OK;
234 }
235
MarkValidating()236 void imgRequestProxy::MarkValidating() {
237 MOZ_ASSERT(GetValidator());
238 mValidating = true;
239 }
240
ClearValidating()241 void imgRequestProxy::ClearValidating() {
242 MOZ_ASSERT(mValidating);
243 MOZ_ASSERT(!GetValidator());
244 mValidating = false;
245
246 // If we'd previously requested a synchronous decode, request a decode on the
247 // new image.
248 if (mDecodeRequested) {
249 mDecodeRequested = false;
250 StartDecoding(imgIContainer::FLAG_NONE);
251 }
252 }
253
GetEventTarget() const254 already_AddRefed<nsIEventTarget> imgRequestProxy::GetEventTarget() const {
255 nsCOMPtr<nsIEventTarget> target(mEventTarget);
256 return target.forget();
257 }
258
DispatchWithTargetIfAvailable(already_AddRefed<nsIRunnable> aEvent)259 nsresult imgRequestProxy::DispatchWithTargetIfAvailable(
260 already_AddRefed<nsIRunnable> aEvent) {
261 LOG_FUNC(gImgLog, "imgRequestProxy::DispatchWithTargetIfAvailable");
262
263 // This method should only be used when it is *expected* that we are
264 // dispatching an event (e.g. we want to handle an event asynchronously)
265 // rather we need to (e.g. we are in the wrong scheduler group context).
266 // As such, we do not set mHadDispatch for telemetry purposes.
267 if (mEventTarget) {
268 mEventTarget->Dispatch(CreateRenderBlockingRunnable(std::move(aEvent)),
269 NS_DISPATCH_NORMAL);
270 return NS_OK;
271 }
272
273 return NS_DispatchToMainThread(
274 CreateRenderBlockingRunnable(std::move(aEvent)));
275 }
276
AddToOwner(Document * aLoadingDocument)277 void imgRequestProxy::AddToOwner(Document* aLoadingDocument) {
278 // An imgRequestProxy can be initialized with neither a listener nor a
279 // document. The caller could follow up later by cloning the canonical
280 // imgRequestProxy with the actual listener. This is possible because
281 // imgLoader::LoadImage does not require a valid listener to be provided.
282 //
283 // Without a listener, we don't need to set our scheduler group, because
284 // we have nothing to signal. However if we were told what document this
285 // is for, it is likely that future listeners will belong to the same
286 // scheduler group.
287 //
288 // With a listener, we always need to update our scheduler group. A null
289 // scheduler group is valid with or without a document, but that means
290 // we will use the most generic event target possible on dispatch.
291 if (aLoadingDocument) {
292 RefPtr<mozilla::dom::DocGroup> docGroup = aLoadingDocument->GetDocGroup();
293 if (docGroup) {
294 mEventTarget = docGroup->EventTargetFor(mozilla::TaskCategory::Other);
295 MOZ_ASSERT(mEventTarget);
296 }
297 }
298
299 if (mListener && !mEventTarget) {
300 mEventTarget = do_GetMainThread();
301 }
302
303 imgRequest* owner = GetOwner();
304 if (!owner) {
305 return;
306 }
307
308 owner->AddProxy(this);
309 }
310
RemoveFromOwner(nsresult aStatus)311 void imgRequestProxy::RemoveFromOwner(nsresult aStatus) {
312 imgRequest* owner = GetOwner();
313 if (owner) {
314 if (mValidating) {
315 imgCacheValidator* validator = owner->GetValidator();
316 MOZ_ASSERT(validator);
317 validator->RemoveProxy(this);
318 mValidating = false;
319 }
320
321 owner->RemoveProxy(this, aStatus);
322 }
323 }
324
AddToLoadGroup()325 void imgRequestProxy::AddToLoadGroup() {
326 NS_ASSERTION(!mIsInLoadGroup, "Whaa, we're already in the loadgroup!");
327 MOZ_ASSERT(!mForceDispatchLoadGroup);
328
329 /* While in theory there could be a dispatch outstanding to remove this
330 request from the load group, in practice we only add to the load group
331 (when previously not in a load group) at initialization. */
332 if (!mIsInLoadGroup && mLoadGroup) {
333 LOG_FUNC(gImgLog, "imgRequestProxy::AddToLoadGroup");
334 mLoadGroup->AddRequest(this, nullptr);
335 mIsInLoadGroup = true;
336 }
337 }
338
RemoveFromLoadGroup()339 void imgRequestProxy::RemoveFromLoadGroup() {
340 if (!mIsInLoadGroup || !mLoadGroup) {
341 return;
342 }
343
344 /* Sometimes we may not be able to remove ourselves from the load group in
345 the current context. This is because our listeners are not re-entrant (e.g.
346 we are in the middle of CancelAndForgetObserver or SyncClone). */
347 if (mForceDispatchLoadGroup) {
348 LOG_FUNC(gImgLog, "imgRequestProxy::RemoveFromLoadGroup -- dispatch");
349
350 /* We take away the load group from the request temporarily; this prevents
351 additional dispatches via RemoveFromLoadGroup occurring, as well as
352 MoveToBackgroundInLoadGroup from removing and readding. This is safe
353 because we know that once we get here, blocking the load group at all is
354 unnecessary. */
355 mIsInLoadGroup = false;
356 nsCOMPtr<nsILoadGroup> loadGroup = std::move(mLoadGroup);
357 RefPtr<imgRequestProxy> self(this);
358 DispatchWithTargetIfAvailable(NS_NewRunnableFunction(
359 "imgRequestProxy::RemoveFromLoadGroup", [self, loadGroup]() -> void {
360 loadGroup->RemoveRequest(self, nullptr, NS_OK);
361 }));
362 return;
363 }
364
365 LOG_FUNC(gImgLog, "imgRequestProxy::RemoveFromLoadGroup");
366
367 /* calling RemoveFromLoadGroup may cause the document to finish
368 loading, which could result in our death. We need to make sure
369 that we stay alive long enough to fight another battle... at
370 least until we exit this function. */
371 nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
372 mLoadGroup->RemoveRequest(this, nullptr, NS_OK);
373 mLoadGroup = nullptr;
374 mIsInLoadGroup = false;
375 }
376
MoveToBackgroundInLoadGroup()377 void imgRequestProxy::MoveToBackgroundInLoadGroup() {
378 /* Even if we are still in the load group, we may have taken away the load
379 group reference itself because we are in the process of leaving the group.
380 In that case, there is no need to background the request. */
381 if (!mLoadGroup) {
382 return;
383 }
384
385 /* There is no need to dispatch if we only need to add ourselves to the load
386 group without removal. It is the removal which causes the problematic
387 callbacks (see RemoveFromLoadGroup). */
388 if (mIsInLoadGroup && mForceDispatchLoadGroup) {
389 LOG_FUNC(gImgLog,
390 "imgRequestProxy::MoveToBackgroundInLoadGroup -- dispatch");
391
392 RefPtr<imgRequestProxy> self(this);
393 DispatchWithTargetIfAvailable(NS_NewRunnableFunction(
394 "imgRequestProxy::MoveToBackgroundInLoadGroup",
395 [self]() -> void { self->MoveToBackgroundInLoadGroup(); }));
396 return;
397 }
398
399 LOG_FUNC(gImgLog, "imgRequestProxy::MoveToBackgroundInLoadGroup");
400 nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
401 if (mIsInLoadGroup) {
402 mLoadGroup->RemoveRequest(this, nullptr, NS_OK);
403 }
404
405 mLoadFlags |= nsIRequest::LOAD_BACKGROUND;
406 mLoadGroup->AddRequest(this, nullptr);
407 }
408
409 /** nsIRequest / imgIRequest methods **/
410
411 NS_IMETHODIMP
GetName(nsACString & aName)412 imgRequestProxy::GetName(nsACString& aName) {
413 aName.Truncate();
414
415 if (mURI) {
416 mURI->GetSpec(aName);
417 }
418
419 return NS_OK;
420 }
421
422 NS_IMETHODIMP
IsPending(bool * _retval)423 imgRequestProxy::IsPending(bool* _retval) { return NS_ERROR_NOT_IMPLEMENTED; }
424
425 NS_IMETHODIMP
GetStatus(nsresult * aStatus)426 imgRequestProxy::GetStatus(nsresult* aStatus) {
427 return NS_ERROR_NOT_IMPLEMENTED;
428 }
429
430 NS_IMETHODIMP
Cancel(nsresult status)431 imgRequestProxy::Cancel(nsresult status) {
432 if (mCanceled) {
433 return NS_ERROR_FAILURE;
434 }
435
436 LOG_SCOPE(gImgLog, "imgRequestProxy::Cancel");
437
438 mCanceled = true;
439
440 nsCOMPtr<nsIRunnable> ev = new imgCancelRunnable(this, status);
441 return DispatchWithTargetIfAvailable(ev.forget());
442 }
443
DoCancel(nsresult status)444 void imgRequestProxy::DoCancel(nsresult status) {
445 RemoveFromOwner(status);
446 RemoveFromLoadGroup();
447 NullOutListener();
448 }
449
450 NS_IMETHODIMP
CancelAndForgetObserver(nsresult aStatus)451 imgRequestProxy::CancelAndForgetObserver(nsresult aStatus) {
452 // If mCanceled is true but mListener is non-null, that means
453 // someone called Cancel() on us but the imgCancelRunnable is still
454 // pending. We still need to null out mListener before returning
455 // from this function in this case. That means we want to do the
456 // RemoveProxy call right now, because we need to deliver the
457 // onStopRequest.
458 if (mCanceled && !mListener) {
459 return NS_ERROR_FAILURE;
460 }
461
462 LOG_SCOPE(gImgLog, "imgRequestProxy::CancelAndForgetObserver");
463
464 mCanceled = true;
465 mForceDispatchLoadGroup = true;
466 RemoveFromOwner(aStatus);
467 RemoveFromLoadGroup();
468 mForceDispatchLoadGroup = false;
469
470 NullOutListener();
471
472 return NS_OK;
473 }
474
475 NS_IMETHODIMP
StartDecoding(uint32_t aFlags)476 imgRequestProxy::StartDecoding(uint32_t aFlags) {
477 // Flag this, so we know to request after validation if pending.
478 if (IsValidating()) {
479 mDecodeRequested = true;
480 return NS_OK;
481 }
482
483 RefPtr<Image> image = GetImage();
484 if (image) {
485 return image->StartDecoding(aFlags);
486 }
487
488 if (GetOwner()) {
489 GetOwner()->StartDecoding();
490 }
491
492 return NS_OK;
493 }
494
StartDecodingWithResult(uint32_t aFlags)495 bool imgRequestProxy::StartDecodingWithResult(uint32_t aFlags) {
496 // Flag this, so we know to request after validation if pending.
497 if (IsValidating()) {
498 mDecodeRequested = true;
499 return false;
500 }
501
502 RefPtr<Image> image = GetImage();
503 if (image) {
504 return image->StartDecodingWithResult(aFlags);
505 }
506
507 if (GetOwner()) {
508 GetOwner()->StartDecoding();
509 }
510
511 return false;
512 }
513
RequestDecodeWithResult(uint32_t aFlags)514 imgIContainer::DecodeResult imgRequestProxy::RequestDecodeWithResult(
515 uint32_t aFlags) {
516 if (IsValidating()) {
517 mDecodeRequested = true;
518 return imgIContainer::DECODE_REQUESTED;
519 }
520
521 RefPtr<Image> image = GetImage();
522 if (image) {
523 return image->RequestDecodeWithResult(aFlags);
524 }
525
526 if (GetOwner()) {
527 GetOwner()->StartDecoding();
528 }
529
530 return imgIContainer::DECODE_REQUESTED;
531 }
532
533 NS_IMETHODIMP
LockImage()534 imgRequestProxy::LockImage() {
535 mLockCount++;
536 RefPtr<Image> image =
537 GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
538 if (image) {
539 return image->LockImage();
540 }
541 return NS_OK;
542 }
543
544 NS_IMETHODIMP
UnlockImage()545 imgRequestProxy::UnlockImage() {
546 MOZ_ASSERT(mLockCount > 0, "calling unlock but no locks!");
547
548 mLockCount--;
549 RefPtr<Image> image =
550 GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
551 if (image) {
552 return image->UnlockImage();
553 }
554 return NS_OK;
555 }
556
557 NS_IMETHODIMP
RequestDiscard()558 imgRequestProxy::RequestDiscard() {
559 RefPtr<Image> image = GetImage();
560 if (image) {
561 return image->RequestDiscard();
562 }
563 return NS_OK;
564 }
565
566 NS_IMETHODIMP
IncrementAnimationConsumers()567 imgRequestProxy::IncrementAnimationConsumers() {
568 mAnimationConsumers++;
569 RefPtr<Image> image =
570 GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
571 if (image) {
572 image->IncrementAnimationConsumers();
573 }
574 return NS_OK;
575 }
576
577 NS_IMETHODIMP
DecrementAnimationConsumers()578 imgRequestProxy::DecrementAnimationConsumers() {
579 // We may get here if some responsible code called Increment,
580 // then called us, but we have meanwhile called ClearAnimationConsumers
581 // because we needed to get rid of them earlier (see
582 // imgRequest::RemoveProxy), and hence have nothing left to
583 // decrement. (In such a case we got rid of the animation consumers
584 // early, but not the observer.)
585 if (mAnimationConsumers > 0) {
586 mAnimationConsumers--;
587 RefPtr<Image> image =
588 GetOwner() && GetOwner()->ImageAvailable() ? GetImage() : nullptr;
589 if (image) {
590 image->DecrementAnimationConsumers();
591 }
592 }
593 return NS_OK;
594 }
595
ClearAnimationConsumers()596 void imgRequestProxy::ClearAnimationConsumers() {
597 while (mAnimationConsumers > 0) {
598 DecrementAnimationConsumers();
599 }
600 }
601
602 NS_IMETHODIMP
Suspend()603 imgRequestProxy::Suspend() { return NS_ERROR_NOT_IMPLEMENTED; }
604
605 NS_IMETHODIMP
Resume()606 imgRequestProxy::Resume() { return NS_ERROR_NOT_IMPLEMENTED; }
607
608 NS_IMETHODIMP
GetLoadGroup(nsILoadGroup ** loadGroup)609 imgRequestProxy::GetLoadGroup(nsILoadGroup** loadGroup) {
610 NS_IF_ADDREF(*loadGroup = mLoadGroup.get());
611 return NS_OK;
612 }
613 NS_IMETHODIMP
SetLoadGroup(nsILoadGroup * loadGroup)614 imgRequestProxy::SetLoadGroup(nsILoadGroup* loadGroup) {
615 if (loadGroup != mLoadGroup) {
616 MOZ_ASSERT_UNREACHABLE("Switching load groups is unsupported!");
617 return NS_ERROR_NOT_IMPLEMENTED;
618 }
619 return NS_OK;
620 }
621
622 NS_IMETHODIMP
GetLoadFlags(nsLoadFlags * flags)623 imgRequestProxy::GetLoadFlags(nsLoadFlags* flags) {
624 *flags = mLoadFlags;
625 return NS_OK;
626 }
627 NS_IMETHODIMP
SetLoadFlags(nsLoadFlags flags)628 imgRequestProxy::SetLoadFlags(nsLoadFlags flags) {
629 mLoadFlags = flags;
630 return NS_OK;
631 }
632
633 NS_IMETHODIMP
GetTRRMode(nsIRequest::TRRMode * aTRRMode)634 imgRequestProxy::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
635 return GetTRRModeImpl(aTRRMode);
636 }
637
638 NS_IMETHODIMP
SetTRRMode(nsIRequest::TRRMode aTRRMode)639 imgRequestProxy::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
640 return SetTRRModeImpl(aTRRMode);
641 }
642
643 /** imgIRequest methods **/
644
645 NS_IMETHODIMP
GetImage(imgIContainer ** aImage)646 imgRequestProxy::GetImage(imgIContainer** aImage) {
647 NS_ENSURE_TRUE(aImage, NS_ERROR_NULL_POINTER);
648 // It's possible that our owner has an image but hasn't notified us of it -
649 // that'll happen if we get Canceled before the owner instantiates its image
650 // (because Canceling unregisters us as a listener on mOwner). If we're
651 // in that situation, just grab the image off of mOwner.
652 RefPtr<Image> image = GetImage();
653 nsCOMPtr<imgIContainer> imageToReturn;
654 if (image) {
655 imageToReturn = image;
656 }
657 if (!imageToReturn && GetOwner()) {
658 imageToReturn = GetOwner()->GetImage();
659 }
660 if (!imageToReturn) {
661 return NS_ERROR_FAILURE;
662 }
663
664 imageToReturn.swap(*aImage);
665
666 return NS_OK;
667 }
668
669 NS_IMETHODIMP
GetProviderId(uint32_t * aId)670 imgRequestProxy::GetProviderId(uint32_t* aId) {
671 NS_ENSURE_TRUE(aId, NS_ERROR_NULL_POINTER);
672
673 nsCOMPtr<imgIContainer> image;
674 nsresult rv = GetImage(getter_AddRefs(image));
675 if (NS_SUCCEEDED(rv)) {
676 *aId = image->GetProviderId();
677 } else {
678 *aId = 0;
679 }
680
681 return NS_OK;
682 }
683
684 NS_IMETHODIMP
GetImageStatus(uint32_t * aStatus)685 imgRequestProxy::GetImageStatus(uint32_t* aStatus) {
686 if (IsValidating()) {
687 // We are currently validating the image, and so our status could revert if
688 // we discard the cache. We should also be deferring notifications, such
689 // that the caller will be notified when validation completes. Rather than
690 // risk misleading the caller, return nothing.
691 *aStatus = imgIRequest::STATUS_NONE;
692 } else {
693 RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
694 *aStatus = progressTracker->GetImageStatus();
695 }
696
697 return NS_OK;
698 }
699
700 NS_IMETHODIMP
GetImageErrorCode(nsresult * aStatus)701 imgRequestProxy::GetImageErrorCode(nsresult* aStatus) {
702 if (!GetOwner()) {
703 return NS_ERROR_FAILURE;
704 }
705
706 *aStatus = GetOwner()->GetImageErrorCode();
707
708 return NS_OK;
709 }
710
711 NS_IMETHODIMP
GetURI(nsIURI ** aURI)712 imgRequestProxy::GetURI(nsIURI** aURI) {
713 MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread to convert URI");
714 nsCOMPtr<nsIURI> uri = mURI;
715 uri.forget(aURI);
716 return NS_OK;
717 }
718
GetFinalURI(nsIURI ** aURI)719 nsresult imgRequestProxy::GetFinalURI(nsIURI** aURI) {
720 if (!GetOwner()) {
721 return NS_ERROR_FAILURE;
722 }
723
724 return GetOwner()->GetFinalURI(aURI);
725 }
726
727 NS_IMETHODIMP
GetNotificationObserver(imgINotificationObserver ** aObserver)728 imgRequestProxy::GetNotificationObserver(imgINotificationObserver** aObserver) {
729 *aObserver = mListener;
730 NS_IF_ADDREF(*aObserver);
731 return NS_OK;
732 }
733
734 NS_IMETHODIMP
GetMimeType(char ** aMimeType)735 imgRequestProxy::GetMimeType(char** aMimeType) {
736 if (!GetOwner()) {
737 return NS_ERROR_FAILURE;
738 }
739
740 const char* type = GetOwner()->GetMimeType();
741 if (!type) {
742 return NS_ERROR_FAILURE;
743 }
744
745 *aMimeType = NS_xstrdup(type);
746
747 return NS_OK;
748 }
749
NewClonedProxy()750 imgRequestProxy* imgRequestProxy::NewClonedProxy() {
751 return new imgRequestProxy();
752 }
753
754 NS_IMETHODIMP
Clone(imgINotificationObserver * aObserver,imgIRequest ** aClone)755 imgRequestProxy::Clone(imgINotificationObserver* aObserver,
756 imgIRequest** aClone) {
757 nsresult result;
758 imgRequestProxy* proxy;
759 result = PerformClone(aObserver, nullptr, /* aSyncNotify */ true, &proxy);
760 *aClone = proxy;
761 return result;
762 }
763
SyncClone(imgINotificationObserver * aObserver,Document * aLoadingDocument,imgRequestProxy ** aClone)764 nsresult imgRequestProxy::SyncClone(imgINotificationObserver* aObserver,
765 Document* aLoadingDocument,
766 imgRequestProxy** aClone) {
767 return PerformClone(aObserver, aLoadingDocument,
768 /* aSyncNotify */ true, aClone);
769 }
770
Clone(imgINotificationObserver * aObserver,Document * aLoadingDocument,imgRequestProxy ** aClone)771 nsresult imgRequestProxy::Clone(imgINotificationObserver* aObserver,
772 Document* aLoadingDocument,
773 imgRequestProxy** aClone) {
774 return PerformClone(aObserver, aLoadingDocument,
775 /* aSyncNotify */ false, aClone);
776 }
777
PerformClone(imgINotificationObserver * aObserver,Document * aLoadingDocument,bool aSyncNotify,imgRequestProxy ** aClone)778 nsresult imgRequestProxy::PerformClone(imgINotificationObserver* aObserver,
779 Document* aLoadingDocument,
780 bool aSyncNotify,
781 imgRequestProxy** aClone) {
782 MOZ_ASSERT(aClone, "Null out param");
783
784 LOG_SCOPE(gImgLog, "imgRequestProxy::Clone");
785
786 *aClone = nullptr;
787 RefPtr<imgRequestProxy> clone = NewClonedProxy();
788
789 nsCOMPtr<nsILoadGroup> loadGroup;
790 if (aLoadingDocument) {
791 loadGroup = aLoadingDocument->GetDocumentLoadGroup();
792 }
793
794 // It is important to call |SetLoadFlags()| before calling |Init()| because
795 // |Init()| adds the request to the loadgroup.
796 // When a request is added to a loadgroup, its load flags are merged
797 // with the load flags of the loadgroup.
798 // XXXldb That's not true anymore. Stuff from imgLoader adds the
799 // request to the loadgroup.
800 clone->SetLoadFlags(mLoadFlags);
801 nsresult rv = clone->Init(mBehaviour->GetOwner(), loadGroup, aLoadingDocument,
802 mURI, aObserver);
803 if (NS_FAILED(rv)) {
804 return rv;
805 }
806
807 // Assign to *aClone before calling Notify so that if the caller expects to
808 // only be notified for requests it's already holding pointers to it won't be
809 // surprised.
810 NS_ADDREF(*aClone = clone);
811
812 imgCacheValidator* validator = GetValidator();
813 if (validator) {
814 // Note that if we have a validator, we don't want to issue notifications at
815 // here because we want to defer until that completes. AddProxy will add us
816 // to the load group; we cannot avoid that in this case, because we don't
817 // know when the validation will complete, and if it will cause us to
818 // discard our cached state anyways. We are probably already blocked by the
819 // original LoadImage(WithChannel) request in any event.
820 clone->MarkValidating();
821 validator->AddProxy(clone);
822 } else {
823 // We only want to add the request to the load group of the owning document
824 // if it is still in progress. Some callers cannot handle a supurious load
825 // group removal (e.g. print preview) so we must be careful. On the other
826 // hand, if after cloning, the original request proxy is cancelled /
827 // destroyed, we need to ensure that any clones still block the load group
828 // if it is incomplete.
829 bool addToLoadGroup = mIsInLoadGroup;
830 if (!addToLoadGroup) {
831 RefPtr<ProgressTracker> tracker = clone->GetProgressTracker();
832 addToLoadGroup =
833 tracker && !(tracker->GetProgress() & FLAG_LOAD_COMPLETE);
834 }
835
836 if (addToLoadGroup) {
837 clone->AddToLoadGroup();
838 }
839
840 if (aSyncNotify) {
841 // This is wrong!!! We need to notify asynchronously, but there's code
842 // that assumes that we don't. This will be fixed in bug 580466. Note that
843 // if we have a validator, we won't issue notifications anyways because
844 // they are deferred, so there is no point in requesting.
845 clone->mForceDispatchLoadGroup = true;
846 clone->SyncNotifyListener();
847 clone->mForceDispatchLoadGroup = false;
848 } else {
849 // Without a validator, we can request asynchronous notifications
850 // immediately. If there was a validator, this would override the deferral
851 // and that would be incorrect.
852 clone->NotifyListener();
853 }
854 }
855
856 return NS_OK;
857 }
858
859 NS_IMETHODIMP
GetImagePrincipal(nsIPrincipal ** aPrincipal)860 imgRequestProxy::GetImagePrincipal(nsIPrincipal** aPrincipal) {
861 if (!GetOwner()) {
862 return NS_ERROR_FAILURE;
863 }
864
865 nsCOMPtr<nsIPrincipal> principal = GetOwner()->GetPrincipal();
866 principal.forget(aPrincipal);
867 return NS_OK;
868 }
869
870 NS_IMETHODIMP
GetHadCrossOriginRedirects(bool * aHadCrossOriginRedirects)871 imgRequestProxy::GetHadCrossOriginRedirects(bool* aHadCrossOriginRedirects) {
872 *aHadCrossOriginRedirects = false;
873
874 nsCOMPtr<nsITimedChannel> timedChannel = TimedChannel();
875 if (timedChannel) {
876 bool allRedirectsSameOrigin = false;
877 *aHadCrossOriginRedirects =
878 NS_SUCCEEDED(
879 timedChannel->GetAllRedirectsSameOrigin(&allRedirectsSameOrigin)) &&
880 !allRedirectsSameOrigin;
881 }
882
883 return NS_OK;
884 }
885
886 NS_IMETHODIMP
GetMultipart(bool * aMultipart)887 imgRequestProxy::GetMultipart(bool* aMultipart) {
888 if (!GetOwner()) {
889 return NS_ERROR_FAILURE;
890 }
891
892 *aMultipart = GetOwner()->GetMultipart();
893
894 return NS_OK;
895 }
896
897 NS_IMETHODIMP
GetCORSMode(int32_t * aCorsMode)898 imgRequestProxy::GetCORSMode(int32_t* aCorsMode) {
899 if (!GetOwner()) {
900 return NS_ERROR_FAILURE;
901 }
902
903 *aCorsMode = GetOwner()->GetCORSMode();
904
905 return NS_OK;
906 }
907
908 NS_IMETHODIMP
BoostPriority(uint32_t aCategory)909 imgRequestProxy::BoostPriority(uint32_t aCategory) {
910 NS_ENSURE_STATE(GetOwner() && !mCanceled);
911 GetOwner()->BoostPriority(aCategory);
912 return NS_OK;
913 }
914
915 /** nsISupportsPriority methods **/
916
917 NS_IMETHODIMP
GetPriority(int32_t * priority)918 imgRequestProxy::GetPriority(int32_t* priority) {
919 NS_ENSURE_STATE(GetOwner());
920 *priority = GetOwner()->Priority();
921 return NS_OK;
922 }
923
924 NS_IMETHODIMP
SetPriority(int32_t priority)925 imgRequestProxy::SetPriority(int32_t priority) {
926 NS_ENSURE_STATE(GetOwner() && !mCanceled);
927 GetOwner()->AdjustPriority(this, priority - GetOwner()->Priority());
928 return NS_OK;
929 }
930
931 NS_IMETHODIMP
AdjustPriority(int32_t priority)932 imgRequestProxy::AdjustPriority(int32_t priority) {
933 // We don't require |!mCanceled| here. This may be called even if we're
934 // cancelled, because it's invoked as part of the process of removing an image
935 // from the load group.
936 NS_ENSURE_STATE(GetOwner());
937 GetOwner()->AdjustPriority(this, priority);
938 return NS_OK;
939 }
940
NotificationTypeToString(int32_t aType)941 static const char* NotificationTypeToString(int32_t aType) {
942 switch (aType) {
943 case imgINotificationObserver::SIZE_AVAILABLE:
944 return "SIZE_AVAILABLE";
945 case imgINotificationObserver::FRAME_UPDATE:
946 return "FRAME_UPDATE";
947 case imgINotificationObserver::FRAME_COMPLETE:
948 return "FRAME_COMPLETE";
949 case imgINotificationObserver::LOAD_COMPLETE:
950 return "LOAD_COMPLETE";
951 case imgINotificationObserver::DECODE_COMPLETE:
952 return "DECODE_COMPLETE";
953 case imgINotificationObserver::DISCARD:
954 return "DISCARD";
955 case imgINotificationObserver::UNLOCKED_DRAW:
956 return "UNLOCKED_DRAW";
957 case imgINotificationObserver::IS_ANIMATED:
958 return "IS_ANIMATED";
959 case imgINotificationObserver::HAS_TRANSPARENCY:
960 return "HAS_TRANSPARENCY";
961 default:
962 MOZ_ASSERT_UNREACHABLE("Notification list should be exhaustive");
963 return "(unknown notification)";
964 }
965 }
966
Notify(int32_t aType,const mozilla::gfx::IntRect * aRect)967 void imgRequestProxy::Notify(int32_t aType,
968 const mozilla::gfx::IntRect* aRect) {
969 MOZ_ASSERT(aType != imgINotificationObserver::LOAD_COMPLETE,
970 "Should call OnLoadComplete");
971
972 LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::Notify", "type",
973 NotificationTypeToString(aType));
974
975 if (!mListener || mCanceled) {
976 return;
977 }
978
979 // Make sure the listener stays alive while we notify.
980 nsCOMPtr<imgINotificationObserver> listener(mListener);
981
982 listener->Notify(this, aType, aRect);
983 }
984
OnLoadComplete(bool aLastPart)985 void imgRequestProxy::OnLoadComplete(bool aLastPart) {
986 LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnLoadComplete", "uri", mURI);
987
988 // There's all sorts of stuff here that could kill us (the OnStopRequest call
989 // on the listener, the removal from the loadgroup, the release of the
990 // listener, etc). Don't let them do it.
991 RefPtr<imgRequestProxy> self(this);
992
993 if (mListener && !mCanceled) {
994 // Hold a ref to the listener while we call it, just in case.
995 nsCOMPtr<imgINotificationObserver> listener(mListener);
996 listener->Notify(this, imgINotificationObserver::LOAD_COMPLETE, nullptr);
997 }
998
999 // If we're expecting more data from a multipart channel, re-add ourself
1000 // to the loadgroup so that the document doesn't lose track of the load.
1001 // If the request is already a background request and there's more data
1002 // coming, we can just leave the request in the loadgroup as-is.
1003 if (aLastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) {
1004 if (aLastPart) {
1005 RemoveFromLoadGroup();
1006
1007 nsresult errorCode = NS_OK;
1008 // if the load is cross origin without CORS, or the CORS access is
1009 // rejected, always fire load event to avoid leaking site information for
1010 // <link rel=preload>.
1011 // XXXedgar, currently we don't do the same thing for <img>.
1012 imgRequest* request = GetOwner();
1013 if (!request || !(request->IsDeniedCrossSiteCORSRequest() ||
1014 request->IsCrossSiteNoCORSRequest())) {
1015 uint32_t status = imgIRequest::STATUS_NONE;
1016 GetImageStatus(&status);
1017 if (status & imgIRequest::STATUS_ERROR) {
1018 errorCode = NS_ERROR_FAILURE;
1019 }
1020 }
1021 NotifyStop(errorCode);
1022 } else {
1023 // More data is coming, so change the request to be a background request
1024 // and put it back in the loadgroup.
1025 MoveToBackgroundInLoadGroup();
1026 }
1027 }
1028
1029 if (mListenerIsStrongRef && aLastPart) {
1030 MOZ_ASSERT(mListener, "How did that happen?");
1031 // Drop our strong ref to the listener now that we're done with
1032 // everything. Note that this can cancel us and other fun things
1033 // like that. Don't add anything in this method after this point.
1034 imgINotificationObserver* obs = mListener;
1035 mListenerIsStrongRef = false;
1036 NS_RELEASE(obs);
1037 }
1038 }
1039
NullOutListener()1040 void imgRequestProxy::NullOutListener() {
1041 // If we have animation consumers, then they don't matter anymore
1042 if (mListener) {
1043 ClearAnimationConsumers();
1044 }
1045
1046 if (mListenerIsStrongRef) {
1047 // Releasing could do weird reentery stuff, so just play it super-safe
1048 nsCOMPtr<imgINotificationObserver> obs;
1049 obs.swap(mListener);
1050 mListenerIsStrongRef = false;
1051 } else {
1052 mListener = nullptr;
1053 }
1054 }
1055
1056 NS_IMETHODIMP
GetStaticRequest(imgIRequest ** aReturn)1057 imgRequestProxy::GetStaticRequest(imgIRequest** aReturn) {
1058 RefPtr<imgRequestProxy> proxy =
1059 GetStaticRequest(static_cast<Document*>(nullptr));
1060 if (proxy != this) {
1061 RefPtr<Image> image = GetImage();
1062 if (image && image->HasError()) {
1063 // image/test/unit/test_async_notification_404.js needs this, but ideally
1064 // this special case can be removed from the scripted codepath.
1065 return NS_ERROR_FAILURE;
1066 }
1067 }
1068 proxy.forget(aReturn);
1069 return NS_OK;
1070 }
1071
GetStaticRequest(Document * aLoadingDocument)1072 already_AddRefed<imgRequestProxy> imgRequestProxy::GetStaticRequest(
1073 Document* aLoadingDocument) {
1074 MOZ_DIAGNOSTIC_ASSERT(!aLoadingDocument ||
1075 aLoadingDocument->IsStaticDocument());
1076 RefPtr<Image> image = GetImage();
1077
1078 bool animated;
1079 if (!image || (NS_SUCCEEDED(image->GetAnimated(&animated)) && !animated)) {
1080 // Early exit - we're not animated, so we don't have to do anything.
1081 return do_AddRef(this);
1082 }
1083
1084 // We are animated. We need to create a frozen version of this image.
1085 RefPtr<Image> frozenImage = ImageOps::Freeze(image);
1086
1087 // Create a static imgRequestProxy with our new extracted frame.
1088 nsCOMPtr<nsIPrincipal> currentPrincipal;
1089 GetImagePrincipal(getter_AddRefs(currentPrincipal));
1090 bool hadCrossOriginRedirects = true;
1091 GetHadCrossOriginRedirects(&hadCrossOriginRedirects);
1092 RefPtr<imgRequestProxy> req = new imgRequestProxyStatic(
1093 frozenImage, currentPrincipal, hadCrossOriginRedirects);
1094 req->Init(nullptr, nullptr, aLoadingDocument, mURI, nullptr);
1095
1096 return req.forget();
1097 }
1098
NotifyListener()1099 void imgRequestProxy::NotifyListener() {
1100 // It would be nice to notify the observer directly in the status tracker
1101 // instead of through the proxy, but there are several places we do extra
1102 // processing when we receive notifications (like OnStopRequest()), and we
1103 // need to check mCanceled everywhere too.
1104
1105 RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
1106 if (GetOwner()) {
1107 // Send the notifications to our listener asynchronously.
1108 progressTracker->Notify(this);
1109 } else {
1110 // We don't have an imgRequest, so we can only notify the clone of our
1111 // current state, but we still have to do that asynchronously.
1112 MOZ_ASSERT(HasImage(), "if we have no imgRequest, we should have an Image");
1113 progressTracker->NotifyCurrentState(this);
1114 }
1115 }
1116
SyncNotifyListener()1117 void imgRequestProxy::SyncNotifyListener() {
1118 // It would be nice to notify the observer directly in the status tracker
1119 // instead of through the proxy, but there are several places we do extra
1120 // processing when we receive notifications (like OnStopRequest()), and we
1121 // need to check mCanceled everywhere too.
1122
1123 RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
1124 progressTracker->SyncNotify(this);
1125 }
1126
SetHasImage()1127 void imgRequestProxy::SetHasImage() {
1128 RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
1129 MOZ_ASSERT(progressTracker);
1130 RefPtr<Image> image = progressTracker->GetImage();
1131 MOZ_ASSERT(image);
1132
1133 // Force any private status related to the owner to reflect
1134 // the presence of an image;
1135 mBehaviour->SetOwner(mBehaviour->GetOwner());
1136
1137 // Apply any locks we have
1138 for (uint32_t i = 0; i < mLockCount; ++i) {
1139 image->LockImage();
1140 }
1141
1142 // Apply any animation consumers we have
1143 for (uint32_t i = 0; i < mAnimationConsumers; i++) {
1144 image->IncrementAnimationConsumers();
1145 }
1146 }
1147
GetProgressTracker() const1148 already_AddRefed<ProgressTracker> imgRequestProxy::GetProgressTracker() const {
1149 return mBehaviour->GetProgressTracker();
1150 }
1151
GetImage() const1152 already_AddRefed<mozilla::image::Image> imgRequestProxy::GetImage() const {
1153 return mBehaviour->GetImage();
1154 }
1155
HasImage() const1156 bool RequestBehaviour::HasImage() const {
1157 if (!mOwnerHasImage) {
1158 return false;
1159 }
1160 RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
1161 return progressTracker ? progressTracker->HasImage() : false;
1162 }
1163
HasImage() const1164 bool imgRequestProxy::HasImage() const { return mBehaviour->HasImage(); }
1165
GetOwner() const1166 imgRequest* imgRequestProxy::GetOwner() const { return mBehaviour->GetOwner(); }
1167
GetValidator() const1168 imgCacheValidator* imgRequestProxy::GetValidator() const {
1169 imgRequest* owner = GetOwner();
1170 if (!owner) {
1171 return nullptr;
1172 }
1173 return owner->GetValidator();
1174 }
1175
TimedChannel()1176 nsITimedChannel* imgRequestProxy::TimedChannel() {
1177 if (!GetOwner()) {
1178 return nullptr;
1179 }
1180 return GetOwner()->GetTimedChannel();
1181 }
1182
1183 ////////////////// imgRequestProxyStatic methods
1184
1185 class StaticBehaviour : public ProxyBehaviour {
1186 public:
StaticBehaviour(mozilla::image::Image * aImage)1187 explicit StaticBehaviour(mozilla::image::Image* aImage) : mImage(aImage) {}
1188
GetImage() const1189 already_AddRefed<mozilla::image::Image> GetImage() const override {
1190 RefPtr<mozilla::image::Image> image = mImage;
1191 return image.forget();
1192 }
1193
HasImage() const1194 bool HasImage() const override { return mImage; }
1195
GetProgressTracker() const1196 already_AddRefed<ProgressTracker> GetProgressTracker() const override {
1197 return mImage->GetProgressTracker();
1198 }
1199
GetOwner() const1200 imgRequest* GetOwner() const override { return nullptr; }
1201
SetOwner(imgRequest * aOwner)1202 void SetOwner(imgRequest* aOwner) override {
1203 MOZ_ASSERT(!aOwner,
1204 "We shouldn't be giving static requests a non-null owner.");
1205 }
1206
1207 private:
1208 // Our image. We have to hold a strong reference here, because that's normally
1209 // the job of the underlying request.
1210 RefPtr<mozilla::image::Image> mImage;
1211 };
1212
imgRequestProxyStatic(mozilla::image::Image * aImage,nsIPrincipal * aPrincipal,bool aHadCrossOriginRedirects)1213 imgRequestProxyStatic::imgRequestProxyStatic(mozilla::image::Image* aImage,
1214 nsIPrincipal* aPrincipal,
1215 bool aHadCrossOriginRedirects)
1216 : mPrincipal(aPrincipal),
1217 mHadCrossOriginRedirects(aHadCrossOriginRedirects) {
1218 mBehaviour = mozilla::MakeUnique<StaticBehaviour>(aImage);
1219 }
1220
1221 NS_IMETHODIMP
GetImagePrincipal(nsIPrincipal ** aPrincipal)1222 imgRequestProxyStatic::GetImagePrincipal(nsIPrincipal** aPrincipal) {
1223 if (!mPrincipal) {
1224 return NS_ERROR_FAILURE;
1225 }
1226
1227 NS_ADDREF(*aPrincipal = mPrincipal);
1228
1229 return NS_OK;
1230 }
1231
1232 NS_IMETHODIMP
GetHadCrossOriginRedirects(bool * aHadCrossOriginRedirects)1233 imgRequestProxyStatic::GetHadCrossOriginRedirects(
1234 bool* aHadCrossOriginRedirects) {
1235 *aHadCrossOriginRedirects = mHadCrossOriginRedirects;
1236 return NS_OK;
1237 }
1238
NewClonedProxy()1239 imgRequestProxy* imgRequestProxyStatic::NewClonedProxy() {
1240 nsCOMPtr<nsIPrincipal> currentPrincipal;
1241 GetImagePrincipal(getter_AddRefs(currentPrincipal));
1242 bool hadCrossOriginRedirects = true;
1243 GetHadCrossOriginRedirects(&hadCrossOriginRedirects);
1244 RefPtr<mozilla::image::Image> image = GetImage();
1245 return new imgRequestProxyStatic(image, currentPrincipal,
1246 hadCrossOriginRedirects);
1247 }
1248