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