Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/image/ProgressTracker.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 "ImageLogging.h"
8
#include "ProgressTracker.h"
9
10
#include "imgIContainer.h"
11
#include "imgINotificationObserver.h"
12
#include "imgIRequest.h"
13
#include "Image.h"
14
#include "nsNetUtil.h"
15
#include "nsIObserverService.h"
16
17
#include "mozilla/Assertions.h"
18
#include "mozilla/Services.h"
19
#include "mozilla/SystemGroup.h"
20
21
using mozilla::WeakPtr;
22
23
namespace mozilla {
24
namespace image {
25
26
static void
27
CheckProgressConsistency(Progress aOldProgress, Progress aNewProgress, bool aIsMultipart)
28
0
{
29
0
  // Check preconditions for every progress bit.
30
0
31
0
  // Error's do not get propagated from the tracker for each image part to the
32
0
  // tracker for the multipart image because we don't want one bad part to
33
0
  // prevent the remaining parts from showing. So we need to consider whether
34
0
  // this is a tracker for a multipart image for these assertions to work.
35
0
36
0
  if (aNewProgress & FLAG_SIZE_AVAILABLE) {
37
0
    // No preconditions.
38
0
  }
39
0
  if (aNewProgress & FLAG_DECODE_COMPLETE) {
40
0
    MOZ_ASSERT(aNewProgress & FLAG_SIZE_AVAILABLE);
41
0
    MOZ_ASSERT(aIsMultipart || aNewProgress & (FLAG_FRAME_COMPLETE | FLAG_HAS_ERROR));
42
0
  }
43
0
  if (aNewProgress & FLAG_FRAME_COMPLETE) {
44
0
    MOZ_ASSERT(aNewProgress & FLAG_SIZE_AVAILABLE);
45
0
  }
46
0
  if (aNewProgress & FLAG_LOAD_COMPLETE) {
47
0
    MOZ_ASSERT(aIsMultipart || aNewProgress & (FLAG_SIZE_AVAILABLE | FLAG_HAS_ERROR));
48
0
  }
49
0
  if (aNewProgress & FLAG_IS_ANIMATED) {
50
0
    // No preconditions; like FLAG_HAS_TRANSPARENCY, we should normally never
51
0
    // discover this *after* FLAG_SIZE_AVAILABLE, but unfortunately some corrupt
52
0
    // GIFs may fool us.
53
0
  }
54
0
  if (aNewProgress & FLAG_HAS_TRANSPARENCY) {
55
0
    // XXX We'd like to assert that transparency is only set during metadata
56
0
    // decode but we don't have any way to assert that until bug 1254892 is fixed.
57
0
  }
58
0
  if (aNewProgress & FLAG_LAST_PART_COMPLETE) {
59
0
    MOZ_ASSERT(aNewProgress & FLAG_LOAD_COMPLETE);
60
0
  }
61
0
  if (aNewProgress & FLAG_HAS_ERROR) {
62
0
    // No preconditions.
63
0
  }
64
0
}
65
66
ProgressTracker::ProgressTracker()
67
  : mMutex("ProgressTracker::mMutex")
68
  , mImage(nullptr)
69
  , mEventTarget(WrapNotNull(nsCOMPtr<nsIEventTarget>(SystemGroup::EventTargetFor(TaskCategory::Other))))
70
  , mObserversWithTargets(0)
71
  , mObservers(new ObserverTable)
72
  , mProgress(NoProgress)
73
  , mIsMultipart(false)
74
0
{ }
75
76
void
77
ProgressTracker::SetImage(Image* aImage)
78
0
{
79
0
  MutexAutoLock lock(mMutex);
80
0
  MOZ_ASSERT(aImage, "Setting null image");
81
0
  MOZ_ASSERT(!mImage, "Setting image when we already have one");
82
0
  mImage = aImage;
83
0
}
84
85
void
86
ProgressTracker::ResetImage()
87
0
{
88
0
  MutexAutoLock lock(mMutex);
89
0
  MOZ_ASSERT(mImage, "Resetting image when it's already null!");
90
0
  mImage = nullptr;
91
0
}
92
93
uint32_t
94
ProgressTracker::GetImageStatus() const
95
0
{
96
0
  uint32_t status = imgIRequest::STATUS_NONE;
97
0
98
0
  // Translate our current state to a set of imgIRequest::STATE_* flags.
99
0
  if (mProgress & FLAG_SIZE_AVAILABLE) {
100
0
    status |= imgIRequest::STATUS_SIZE_AVAILABLE;
101
0
  }
102
0
  if (mProgress & FLAG_DECODE_COMPLETE) {
103
0
    status |= imgIRequest::STATUS_DECODE_COMPLETE;
104
0
  }
105
0
  if (mProgress & FLAG_FRAME_COMPLETE) {
106
0
    status |= imgIRequest::STATUS_FRAME_COMPLETE;
107
0
  }
108
0
  if (mProgress & FLAG_LOAD_COMPLETE) {
109
0
    status |= imgIRequest::STATUS_LOAD_COMPLETE;
110
0
  }
111
0
  if (mProgress & FLAG_IS_ANIMATED) {
112
0
    status |= imgIRequest::STATUS_IS_ANIMATED;
113
0
  }
114
0
  if (mProgress & FLAG_HAS_TRANSPARENCY) {
115
0
    status |= imgIRequest::STATUS_HAS_TRANSPARENCY;
116
0
  }
117
0
  if (mProgress & FLAG_HAS_ERROR) {
118
0
    status |= imgIRequest::STATUS_ERROR;
119
0
  }
120
0
121
0
  return status;
122
0
}
123
124
// A helper class to allow us to call SyncNotify asynchronously.
125
class AsyncNotifyRunnable : public Runnable
126
{
127
  public:
128
    AsyncNotifyRunnable(ProgressTracker* aTracker,
129
                        IProgressObserver* aObserver)
130
     : Runnable("ProgressTracker::AsyncNotifyRunnable")
131
     , mTracker(aTracker)
132
0
    {
133
0
      MOZ_ASSERT(NS_IsMainThread(), "Should be created on the main thread");
134
0
      MOZ_ASSERT(aTracker, "aTracker should not be null");
135
0
      MOZ_ASSERT(aObserver, "aObserver should not be null");
136
0
      mObservers.AppendElement(aObserver);
137
0
    }
138
139
    NS_IMETHOD Run() override
140
0
    {
141
0
      MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread");
142
0
      MOZ_ASSERT(mTracker, "mTracker should not be null");
143
0
      for (uint32_t i = 0; i < mObservers.Length(); ++i) {
144
0
        mObservers[i]->ClearPendingNotify();
145
0
        mTracker->SyncNotify(mObservers[i]);
146
0
      }
147
0
148
0
      mTracker->mRunnable = nullptr;
149
0
      return NS_OK;
150
0
    }
151
152
    void AddObserver(IProgressObserver* aObserver)
153
0
    {
154
0
      mObservers.AppendElement(aObserver);
155
0
    }
156
157
    void RemoveObserver(IProgressObserver* aObserver)
158
0
    {
159
0
      mObservers.RemoveElement(aObserver);
160
0
    }
161
162
  private:
163
    friend class ProgressTracker;
164
165
    RefPtr<ProgressTracker> mTracker;
166
    nsTArray<RefPtr<IProgressObserver>> mObservers;
167
};
168
169
void
170
ProgressTracker::Notify(IProgressObserver* aObserver)
171
0
{
172
0
  MOZ_ASSERT(NS_IsMainThread());
173
0
174
0
  if (aObserver->NotificationsDeferred()) {
175
0
    // There is a pending notification, or the observer isn't ready yet.
176
0
    return;
177
0
  }
178
0
179
0
  if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
180
0
    RefPtr<Image> image = GetImage();
181
0
    LOG_FUNC_WITH_PARAM(gImgLog,
182
0
                        "ProgressTracker::Notify async", "uri", image);
183
0
  }
184
0
185
0
  aObserver->MarkPendingNotify();
186
0
187
0
  // If we have an existing runnable that we can use, we just append this
188
0
  // observer to its list of observers to be notified. This ensures we don't
189
0
  // unnecessarily delay onload.
190
0
  AsyncNotifyRunnable* runnable =
191
0
    static_cast<AsyncNotifyRunnable*>(mRunnable.get());
192
0
193
0
  if (runnable) {
194
0
    runnable->AddObserver(aObserver);
195
0
  } else {
196
0
    mRunnable = new AsyncNotifyRunnable(this, aObserver);
197
0
    mEventTarget->Dispatch(mRunnable, NS_DISPATCH_NORMAL);
198
0
  }
199
0
}
200
201
// A helper class to allow us to call SyncNotify asynchronously for a given,
202
// fixed, state.
203
class AsyncNotifyCurrentStateRunnable : public Runnable
204
{
205
  public:
206
    AsyncNotifyCurrentStateRunnable(ProgressTracker* aProgressTracker,
207
                                    IProgressObserver* aObserver)
208
      : Runnable("image::AsyncNotifyCurrentStateRunnable")
209
      , mProgressTracker(aProgressTracker)
210
      , mObserver(aObserver)
211
0
    {
212
0
      MOZ_ASSERT(NS_IsMainThread(), "Should be created on the main thread");
213
0
      MOZ_ASSERT(mProgressTracker, "mProgressTracker should not be null");
214
0
      MOZ_ASSERT(mObserver, "mObserver should not be null");
215
0
      mImage = mProgressTracker->GetImage();
216
0
    }
217
218
    NS_IMETHOD Run() override
219
0
    {
220
0
      MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread");
221
0
      mObserver->ClearPendingNotify();
222
0
223
0
      mProgressTracker->SyncNotify(mObserver);
224
0
      return NS_OK;
225
0
    }
226
227
  private:
228
    RefPtr<ProgressTracker> mProgressTracker;
229
    RefPtr<IProgressObserver> mObserver;
230
231
    // We have to hold on to a reference to the tracker's image, just in case
232
    // it goes away while we're in the event queue.
233
    RefPtr<Image> mImage;
234
};
235
236
void
237
ProgressTracker::NotifyCurrentState(IProgressObserver* aObserver)
238
0
{
239
0
  MOZ_ASSERT(NS_IsMainThread());
240
0
241
0
  if (aObserver->NotificationsDeferred()) {
242
0
    // There is a pending notification, or the observer isn't ready yet.
243
0
    return;
244
0
  }
245
0
246
0
  if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
247
0
    RefPtr<Image> image = GetImage();
248
0
    LOG_FUNC_WITH_PARAM(gImgLog,
249
0
                        "ProgressTracker::NotifyCurrentState", "uri", image);
250
0
  }
251
0
252
0
  aObserver->MarkPendingNotify();
253
0
254
0
  nsCOMPtr<nsIRunnable> ev = new AsyncNotifyCurrentStateRunnable(this,
255
0
                                                                 aObserver);
256
0
  mEventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL);
257
0
}
258
259
/**
260
 * ImageObserverNotifier is a helper type that abstracts over the difference
261
 * between sending notifications to all of the observers in an ObserverTable,
262
 * and sending them to a single observer. This allows the same notification code
263
 * to be used for both cases.
264
 */
265
template <typename T> struct ImageObserverNotifier;
266
267
template <>
268
struct MOZ_STACK_CLASS ImageObserverNotifier<const ObserverTable*>
269
{
270
  explicit ImageObserverNotifier(const ObserverTable* aObservers,
271
                                 bool aIgnoreDeferral = false)
272
    : mObservers(aObservers)
273
    , mIgnoreDeferral(aIgnoreDeferral)
274
0
  { }
275
276
  template <typename Lambda>
277
  void operator()(Lambda aFunc)
278
0
  {
279
0
    for (auto iter = mObservers->ConstIter(); !iter.Done(); iter.Next()) {
280
0
      RefPtr<IProgressObserver> observer = iter.Data().get();
281
0
      if (observer &&
282
0
          (mIgnoreDeferral || !observer->NotificationsDeferred())) {
283
0
        aFunc(observer);
284
0
      }
285
0
    }
286
0
  }
Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#1}>(void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#1})
Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#2}>(void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#2})
Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#3}>(void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#3})
Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#4}>(void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#4})
Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#5}>(void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#5})
Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#6}>(void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#6})
Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#7}>(void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#7})
Unexecuted instantiation: Unified_cpp_image1.cpp:void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<mozilla::image::ProgressTracker::OnUnlockedDraw()::$_5::operator()(mozilla::image::ObserverTable const*) const::{lambda(mozilla::image::IProgressObserver*)#1}>(mozilla::image::ProgressTracker::OnUnlockedDraw()::$_5::operator()(mozilla::image::ObserverTable const*) const::{lambda(mozilla::image::IProgressObserver*)#1})
Unexecuted instantiation: Unified_cpp_image1.cpp:void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<mozilla::image::ProgressTracker::OnDiscard()::$_6::operator()(mozilla::image::ObserverTable const*) const::{lambda(mozilla::image::IProgressObserver*)#1}>(mozilla::image::ProgressTracker::OnDiscard()::$_6::operator()(mozilla::image::ObserverTable const*) const::{lambda(mozilla::image::IProgressObserver*)#1})
Unexecuted instantiation: Unified_cpp_image1.cpp:void mozilla::image::ImageObserverNotifier<mozilla::image::ObserverTable const*>::operator()<mozilla::image::ProgressTracker::OnImageAvailable()::$_7::operator()(mozilla::image::ObserverTable const*) const::{lambda(mozilla::image::IProgressObserver*)#1}>(mozilla::image::ProgressTracker::OnImageAvailable()::$_7::operator()(mozilla::image::ObserverTable const*) const::{lambda(mozilla::image::IProgressObserver*)#1})
287
288
private:
289
  const ObserverTable* mObservers;
290
  const bool mIgnoreDeferral;
291
};
292
293
template <>
294
struct MOZ_STACK_CLASS ImageObserverNotifier<IProgressObserver*>
295
{
296
  explicit ImageObserverNotifier(IProgressObserver* aObserver)
297
    : mObserver(aObserver)
298
0
  { }
299
300
  template <typename Lambda>
301
  void operator()(Lambda aFunc)
302
0
  {
303
0
    if (mObserver && !mObserver->NotificationsDeferred()) {
304
0
      aFunc(mObserver);
305
0
    }
306
0
  }
Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::IProgressObserver*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#1}>(void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#1})
Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::IProgressObserver*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#2}>(void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#2})
Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::IProgressObserver*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#3}>(void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#3})
Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::IProgressObserver*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#4}>(void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#4})
Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::IProgressObserver*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#5}>(void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#5})
Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::IProgressObserver*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#6}>(void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#6})
Unexecuted instantiation: void mozilla::image::ImageObserverNotifier<mozilla::image::IProgressObserver*>::operator()<void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#7}>(void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#7})
307
308
private:
309
  IProgressObserver* mObserver;
310
};
311
312
template <typename T> void
313
SyncNotifyInternal(const T& aObservers,
314
                   bool aHasImage,
315
                   Progress aProgress,
316
                   const nsIntRect& aDirtyRect)
317
0
{
318
0
  MOZ_ASSERT(NS_IsMainThread());
319
0
320
0
  typedef imgINotificationObserver I;
321
0
  ImageObserverNotifier<T> notify(aObservers);
322
0
323
0
  if (aProgress & FLAG_SIZE_AVAILABLE) {
324
0
    notify([](IProgressObserver* aObs) { aObs->Notify(I::SIZE_AVAILABLE); });
Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#1}::operator()(mozilla::image::IProgressObserver*) const
Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#1}::operator()(mozilla::image::IProgressObserver*) const
325
0
  }
326
0
327
0
  if (aHasImage) {
328
0
    // OnFrameUpdate
329
0
    // If there's any content in this frame at all (always true for
330
0
    // vector images, true for raster images that have decoded at
331
0
    // least one frame) then send OnFrameUpdate.
332
0
    if (!aDirtyRect.IsEmpty()) {
333
0
      notify([&](IProgressObserver* aObs) {
334
0
        aObs->Notify(I::FRAME_UPDATE, &aDirtyRect);
335
0
      });
Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#2}::operator()(mozilla::image::IProgressObserver*) const
Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#2}::operator()(mozilla::image::IProgressObserver*) const
336
0
    }
337
0
338
0
    if (aProgress & FLAG_FRAME_COMPLETE) {
339
0
      notify([](IProgressObserver* aObs) { aObs->Notify(I::FRAME_COMPLETE); });
Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#3}::operator()(mozilla::image::IProgressObserver*) const
Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#3}::operator()(mozilla::image::IProgressObserver*) const
340
0
    }
341
0
342
0
    if (aProgress & FLAG_HAS_TRANSPARENCY) {
343
0
      notify([](IProgressObserver* aObs) { aObs->Notify(I::HAS_TRANSPARENCY); });
Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#4}::operator()(mozilla::image::IProgressObserver*) const
Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#4}::operator()(mozilla::image::IProgressObserver*) const
344
0
    }
345
0
346
0
    if (aProgress & FLAG_IS_ANIMATED) {
347
0
      notify([](IProgressObserver* aObs) { aObs->Notify(I::IS_ANIMATED); });
Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#5}::operator()(mozilla::image::IProgressObserver*) const
Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#5}::operator()(mozilla::image::IProgressObserver*) const
348
0
    }
349
0
  }
350
0
351
0
  if (aProgress & FLAG_DECODE_COMPLETE) {
352
0
    MOZ_ASSERT(aHasImage, "Stopped decoding without ever having an image?");
353
0
    notify([](IProgressObserver* aObs) { aObs->Notify(I::DECODE_COMPLETE); });
Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#6}::operator()(mozilla::image::IProgressObserver*) const
Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#6}::operator()(mozilla::image::IProgressObserver*) const
354
0
  }
355
0
356
0
  if (aProgress & FLAG_LOAD_COMPLETE) {
357
0
    notify([=](IProgressObserver* aObs) {
358
0
      aObs->OnLoadComplete(aProgress & FLAG_LAST_PART_COMPLETE);
359
0
    });
Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#7}::operator()(mozilla::image::IProgressObserver*) const
Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)::{lambda(mozilla::image::IProgressObserver*)#7}::operator()(mozilla::image::IProgressObserver*) const
360
0
  }
361
0
}
Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::ObserverTable const*>(mozilla::image::ObserverTable const* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)
Unexecuted instantiation: void mozilla::image::SyncNotifyInternal<mozilla::image::IProgressObserver*>(mozilla::image::IProgressObserver* const&, bool, unsigned int, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&)
362
363
void
364
ProgressTracker::SyncNotifyProgress(Progress aProgress,
365
                                    const nsIntRect& aInvalidRect
366
                                                  /* = nsIntRect() */)
367
0
{
368
0
  MOZ_ASSERT(NS_IsMainThread(), "Use mObservers on main thread only");
369
0
370
0
  Progress progress = Difference(aProgress);
371
0
  CheckProgressConsistency(mProgress, mProgress | progress, mIsMultipart);
372
0
373
0
  // Apply the changes.
374
0
  mProgress |= progress;
375
0
376
0
  // Send notifications.
377
0
  mObservers.Read([&](const ObserverTable* aTable) {
378
0
    SyncNotifyInternal(aTable, HasImage(), progress, aInvalidRect);
379
0
  });
380
0
381
0
  if (progress & FLAG_HAS_ERROR) {
382
0
    FireFailureNotification();
383
0
  }
384
0
}
385
386
void
387
ProgressTracker::SyncNotify(IProgressObserver* aObserver)
388
0
{
389
0
  MOZ_ASSERT(NS_IsMainThread());
390
0
391
0
  RefPtr<Image> image = GetImage();
392
0
  LOG_SCOPE_WITH_PARAM(gImgLog,
393
0
                       "ProgressTracker::SyncNotify", "uri", image);
394
0
395
0
  nsIntRect rect;
396
0
  if (image) {
397
0
    int32_t width, height;
398
0
    if (NS_FAILED(image->GetWidth(&width)) ||
399
0
        NS_FAILED(image->GetHeight(&height))) {
400
0
      // Either the image has no intrinsic size, or it has an error.
401
0
      rect = GetMaxSizedIntRect();
402
0
    } else {
403
0
      rect.SizeTo(width, height);
404
0
    }
405
0
  }
406
0
407
0
  SyncNotifyInternal(aObserver, !!image, mProgress, rect);
408
0
}
409
410
void
411
ProgressTracker::EmulateRequestFinished(IProgressObserver* aObserver)
412
0
{
413
0
  MOZ_ASSERT(NS_IsMainThread(),
414
0
             "SyncNotifyState and mObservers are not threadsafe");
415
0
  RefPtr<IProgressObserver> kungFuDeathGrip(aObserver);
416
0
417
0
  if (!(mProgress & FLAG_LOAD_COMPLETE)) {
418
0
    aObserver->OnLoadComplete(true);
419
0
  }
420
0
}
421
422
already_AddRefed<nsIEventTarget>
423
ProgressTracker::GetEventTarget() const
424
0
{
425
0
  MutexAutoLock lock(mMutex);
426
0
  nsCOMPtr<nsIEventTarget> target = mEventTarget;
427
0
  return target.forget();
428
0
}
429
430
void
431
ProgressTracker::AddObserver(IProgressObserver* aObserver)
432
0
{
433
0
  MOZ_ASSERT(NS_IsMainThread());
434
0
  RefPtr<IProgressObserver> observer = aObserver;
435
0
436
0
  nsCOMPtr<nsIEventTarget> target = observer->GetEventTarget();
437
0
  if (target) {
438
0
    if (mObserversWithTargets == 0) {
439
0
      // On the first observer with a target (i.e. listener), always accept its
440
0
      // event target; this may be for a specific DocGroup, or it may be the
441
0
      // unlabelled main thread target.
442
0
      MutexAutoLock lock(mMutex);
443
0
      mEventTarget = WrapNotNull(target);
444
0
    } else if (mEventTarget.get() != target.get()) {
445
0
      // If a subsequent observer comes in with a different target, we need to
446
0
      // switch to use the unlabelled main thread target, if we haven't already.
447
0
      MutexAutoLock lock(mMutex);
448
0
      nsCOMPtr<nsIEventTarget> mainTarget(do_GetMainThread());
449
0
      mEventTarget = WrapNotNull(mainTarget);
450
0
    }
451
0
    ++mObserversWithTargets;
452
0
  }
453
0
454
0
  mObservers.Write([=](ObserverTable* aTable) {
455
0
    MOZ_ASSERT(!aTable->Get(observer, nullptr),
456
0
               "Adding duplicate entry for image observer");
457
0
458
0
    WeakPtr<IProgressObserver> weakPtr = observer.get();
459
0
    aTable->Put(observer, weakPtr);
460
0
  });
461
0
462
0
  MOZ_ASSERT(mObserversWithTargets <= ObserverCount());
463
0
}
464
465
bool
466
ProgressTracker::RemoveObserver(IProgressObserver* aObserver)
467
0
{
468
0
  MOZ_ASSERT(NS_IsMainThread());
469
0
  RefPtr<IProgressObserver> observer = aObserver;
470
0
471
0
  // Remove the observer from the list.
472
0
  bool removed = mObservers.Write([observer](ObserverTable* aTable) {
473
0
    return aTable->Remove(observer);
474
0
  });
475
0
476
0
  // Sometimes once an image is decoded, and all of its observers removed, a new
477
0
  // document may request the same image. Thus we need to clear our event target
478
0
  // state when the last observer is removed, so that we select the most
479
0
  // appropriate event target when a new observer is added. Since the event
480
0
  // target may have changed (e.g. due to the scheduler group going away before
481
0
  // we were removed), so we should be cautious comparing this target against
482
0
  // anything at this stage.
483
0
  if (removed) {
484
0
    nsCOMPtr<nsIEventTarget> target = observer->GetEventTarget();
485
0
    if (target) {
486
0
      MOZ_ASSERT(mObserversWithTargets > 0);
487
0
      --mObserversWithTargets;
488
0
489
0
      if (mObserversWithTargets == 0) {
490
0
        MutexAutoLock lock(mMutex);
491
0
        nsCOMPtr<nsIEventTarget> target(SystemGroup::EventTargetFor(TaskCategory::Other));
492
0
        mEventTarget = WrapNotNull(target);
493
0
      }
494
0
    }
495
0
496
0
    MOZ_ASSERT(mObserversWithTargets <= ObserverCount());
497
0
  }
498
0
499
0
  // Observers can get confused if they don't get all the proper teardown
500
0
  // notifications. Part ways on good terms.
501
0
  if (removed && !aObserver->NotificationsDeferred()) {
502
0
    EmulateRequestFinished(aObserver);
503
0
  }
504
0
505
0
  // Make sure we don't give callbacks to an observer that isn't interested in
506
0
  // them any more.
507
0
  AsyncNotifyRunnable* runnable =
508
0
    static_cast<AsyncNotifyRunnable*>(mRunnable.get());
509
0
510
0
  if (aObserver->NotificationsDeferred() && runnable) {
511
0
    runnable->RemoveObserver(aObserver);
512
0
    aObserver->ClearPendingNotify();
513
0
  }
514
0
515
0
  return removed;
516
0
}
517
518
uint32_t
519
ProgressTracker::ObserverCount() const
520
0
{
521
0
  MOZ_ASSERT(NS_IsMainThread());
522
0
  return mObservers.Read([](const ObserverTable* aTable) {
523
0
    return aTable->Count();
524
0
  });
525
0
}
526
527
void
528
ProgressTracker::OnUnlockedDraw()
529
0
{
530
0
  MOZ_ASSERT(NS_IsMainThread());
531
0
  mObservers.Read([](const ObserverTable* aTable) {
532
0
    ImageObserverNotifier<const ObserverTable*> notify(aTable);
533
0
    notify([](IProgressObserver* aObs) {
534
0
      aObs->Notify(imgINotificationObserver::UNLOCKED_DRAW);
535
0
    });
536
0
  });
537
0
}
538
539
void
540
ProgressTracker::ResetForNewRequest()
541
0
{
542
0
  MOZ_ASSERT(NS_IsMainThread());
543
0
  mProgress = NoProgress;
544
0
}
545
546
void
547
ProgressTracker::OnDiscard()
548
0
{
549
0
  MOZ_ASSERT(NS_IsMainThread());
550
0
  mObservers.Read([](const ObserverTable* aTable) {
551
0
    ImageObserverNotifier<const ObserverTable*> notify(aTable);
552
0
    notify([](IProgressObserver* aObs) {
553
0
      aObs->Notify(imgINotificationObserver::DISCARD);
554
0
    });
555
0
  });
556
0
}
557
558
void
559
ProgressTracker::OnImageAvailable()
560
0
{
561
0
  MOZ_ASSERT(NS_IsMainThread());
562
0
  // Notify any imgRequestProxys that are observing us that we have an Image.
563
0
  mObservers.Read([](const ObserverTable* aTable) {
564
0
    ImageObserverNotifier<const ObserverTable*>
565
0
      notify(aTable, /* aIgnoreDeferral = */ true);
566
0
    notify([](IProgressObserver* aObs) {
567
0
      aObs->SetHasImage();
568
0
    });
569
0
  });
570
0
}
571
572
void
573
ProgressTracker::FireFailureNotification()
574
0
{
575
0
  MOZ_ASSERT(NS_IsMainThread());
576
0
577
0
  // Some kind of problem has happened with image decoding.
578
0
  // Report the URI to net:failed-to-process-uri-conent observers.
579
0
  RefPtr<Image> image = GetImage();
580
0
  if (image) {
581
0
    // Should be on main thread, so ok to create a new nsIURI.
582
0
    nsCOMPtr<nsIURI> uri = image->GetURI();
583
0
    if (uri) {
584
0
      nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
585
0
      if (os) {
586
0
        os->NotifyObservers(uri, "net:failed-to-process-uri-content", nullptr);
587
0
      }
588
0
    }
589
0
  }
590
0
}
591
592
} // namespace image
593
} // namespace mozilla