Coverage Report

Created: 2018-09-25 14:53

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