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