Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/image/MultipartImage.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "MultipartImage.h"
7
8
#include "imgINotificationObserver.h"
9
10
namespace mozilla {
11
12
using gfx::IntSize;
13
using gfx::SourceSurface;
14
15
namespace image {
16
17
///////////////////////////////////////////////////////////////////////////////
18
// Helpers
19
///////////////////////////////////////////////////////////////////////////////
20
21
class NextPartObserver : public IProgressObserver
22
{
23
public:
24
  MOZ_DECLARE_REFCOUNTED_TYPENAME(NextPartObserver)
25
  NS_INLINE_DECL_REFCOUNTING(NextPartObserver, override)
26
27
  explicit NextPartObserver(MultipartImage* aOwner)
28
    : mOwner(aOwner)
29
0
  {
30
0
    MOZ_ASSERT(mOwner);
31
0
  }
32
33
  void BeginObserving(Image* aImage)
34
0
  {
35
0
    MOZ_ASSERT(aImage);
36
0
    mImage = aImage;
37
0
38
0
    RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
39
0
    tracker->AddObserver(this);
40
0
  }
41
42
  void BlockUntilDecodedAndFinishObserving()
43
0
  {
44
0
    // Use GetFrame() to block until our image finishes decoding.
45
0
    RefPtr<SourceSurface> surface =
46
0
      mImage->GetFrame(imgIContainer::FRAME_CURRENT,
47
0
                       imgIContainer::FLAG_SYNC_DECODE);
48
0
49
0
    // GetFrame() should've sent synchronous notifications that would have
50
0
    // caused us to call FinishObserving() (and null out mImage) already. If for
51
0
    // some reason it didn't, we should do so here.
52
0
    if (mImage) {
53
0
      FinishObserving();
54
0
    }
55
0
  }
56
57
  virtual void Notify(int32_t aType,
58
                      const nsIntRect* aRect = nullptr) override
59
0
  {
60
0
    if (!mImage) {
61
0
      // We've already finished observing the last image we were given.
62
0
      return;
63
0
    }
64
0
65
0
    if (aType == imgINotificationObserver::FRAME_COMPLETE) {
66
0
      FinishObserving();
67
0
    }
68
0
  }
69
70
  virtual void OnLoadComplete(bool aLastPart) override
71
0
  {
72
0
    if (!mImage) {
73
0
      // We've already finished observing the last image we were given.
74
0
      return;
75
0
    }
76
0
77
0
    // Retrieve the image's intrinsic size.
78
0
    int32_t width = 0;
79
0
    int32_t height = 0;
80
0
    mImage->GetWidth(&width);
81
0
    mImage->GetHeight(&height);
82
0
83
0
    // Request decoding at the intrinsic size.
84
0
    mImage->RequestDecodeForSize(IntSize(width, height),
85
0
                                 imgIContainer::DECODE_FLAGS_DEFAULT);
86
0
87
0
    // If there's already an error, we may never get a FRAME_COMPLETE
88
0
    // notification, so go ahead and notify our owner right away.
89
0
    RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
90
0
    if (tracker->GetProgress() & FLAG_HAS_ERROR) {
91
0
      FinishObserving();
92
0
    }
93
0
  }
94
95
  // Other notifications are ignored.
96
0
  virtual void SetHasImage() override { }
97
0
  virtual bool NotificationsDeferred() const override { return false; }
98
0
  virtual void MarkPendingNotify() override { }
99
0
  virtual void ClearPendingNotify() override { }
100
101
private:
102
0
  virtual ~NextPartObserver() { }
103
104
  void FinishObserving()
105
0
  {
106
0
    MOZ_ASSERT(mImage);
107
0
108
0
    RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
109
0
    tracker->RemoveObserver(this);
110
0
    mImage = nullptr;
111
0
112
0
    mOwner->FinishTransition();
113
0
  }
114
115
  MultipartImage* mOwner;
116
  RefPtr<Image> mImage;
117
};
118
119
120
///////////////////////////////////////////////////////////////////////////////
121
// Implementation
122
///////////////////////////////////////////////////////////////////////////////
123
124
MultipartImage::MultipartImage(Image* aFirstPart)
125
  : ImageWrapper(aFirstPart)
126
  , mPendingNotify(false)
127
0
{
128
0
  mNextPartObserver = new NextPartObserver(this);
129
0
}
130
131
void
132
MultipartImage::Init()
133
0
{
134
0
  MOZ_ASSERT(NS_IsMainThread());
135
0
  MOZ_ASSERT(mTracker, "Should've called SetProgressTracker() by now");
136
0
137
0
  // Start observing the first part.
138
0
  RefPtr<ProgressTracker> firstPartTracker =
139
0
    InnerImage()->GetProgressTracker();
140
0
  firstPartTracker->AddObserver(this);
141
0
  InnerImage()->IncrementAnimationConsumers();
142
0
}
143
144
MultipartImage::~MultipartImage()
145
0
{
146
0
  // Ask our ProgressTracker to drop its weak reference to us.
147
0
  mTracker->ResetImage();
148
0
}
149
150
NS_IMPL_ISUPPORTS_INHERITED0(MultipartImage, ImageWrapper)
151
152
void
153
MultipartImage::BeginTransitionToPart(Image* aNextPart)
154
0
{
155
0
  MOZ_ASSERT(NS_IsMainThread());
156
0
  MOZ_ASSERT(aNextPart);
157
0
158
0
  if (mNextPart) {
159
0
    // Let the decoder catch up so we don't drop frames.
160
0
    mNextPartObserver->BlockUntilDecodedAndFinishObserving();
161
0
    MOZ_ASSERT(!mNextPart);
162
0
  }
163
0
164
0
  mNextPart = aNextPart;
165
0
166
0
  // Start observing the next part; we'll complete the transition when
167
0
  // NextPartObserver calls FinishTransition.
168
0
  mNextPartObserver->BeginObserving(mNextPart);
169
0
  mNextPart->IncrementAnimationConsumers();
170
0
}
171
172
static Progress
173
FilterProgress(Progress aProgress)
174
0
{
175
0
  // Filter out onload blocking notifications, since we don't want to block
176
0
  // onload for multipart images.
177
0
  // Filter out errors, since we don't want errors in one part to error out
178
0
  // the whole stream.
179
0
  return aProgress & ~FLAG_HAS_ERROR;
180
0
}
181
182
void
183
MultipartImage::FinishTransition()
184
0
{
185
0
  MOZ_ASSERT(NS_IsMainThread());
186
0
  MOZ_ASSERT(mNextPart, "Should have a next part here");
187
0
188
0
  RefPtr<ProgressTracker> newCurrentPartTracker =
189
0
    mNextPart->GetProgressTracker();
190
0
  if (newCurrentPartTracker->GetProgress() & FLAG_HAS_ERROR) {
191
0
    // This frame has an error; drop it.
192
0
    mNextPart = nullptr;
193
0
194
0
    // We still need to notify, though.
195
0
    mTracker->ResetForNewRequest();
196
0
    RefPtr<ProgressTracker> currentPartTracker =
197
0
      InnerImage()->GetProgressTracker();
198
0
    mTracker
199
0
      ->SyncNotifyProgress(FilterProgress(currentPartTracker->GetProgress()));
200
0
201
0
    return;
202
0
  }
203
0
204
0
  // Stop observing the current part.
205
0
  {
206
0
    RefPtr<ProgressTracker> currentPartTracker =
207
0
      InnerImage()->GetProgressTracker();
208
0
    currentPartTracker->RemoveObserver(this);
209
0
  }
210
0
211
0
  // Make the next part become the current part.
212
0
  mTracker->ResetForNewRequest();
213
0
  SetInnerImage(mNextPart);
214
0
  mNextPart = nullptr;
215
0
  newCurrentPartTracker->AddObserver(this);
216
0
217
0
  // Finally, send all the notifications for the new current part and send a
218
0
  // FRAME_UPDATE notification so that observers know to redraw.
219
0
  mTracker
220
0
    ->SyncNotifyProgress(FilterProgress(newCurrentPartTracker->GetProgress()),
221
0
                         GetMaxSizedIntRect());
222
0
}
223
224
already_AddRefed<imgIContainer>
225
MultipartImage::Unwrap()
226
0
{
227
0
  // Although we wrap another image, we don't allow callers to unwrap as. As far
228
0
  // as external code is concerned, MultipartImage is atomic.
229
0
  nsCOMPtr<imgIContainer> image = this;
230
0
  return image.forget();
231
0
}
232
233
already_AddRefed<ProgressTracker>
234
MultipartImage::GetProgressTracker()
235
0
{
236
0
  MOZ_ASSERT(mTracker);
237
0
  RefPtr<ProgressTracker> tracker = mTracker;
238
0
  return tracker.forget();
239
0
}
240
241
void
242
MultipartImage::SetProgressTracker(ProgressTracker* aTracker)
243
0
{
244
0
  MOZ_ASSERT(aTracker);
245
0
  MOZ_ASSERT(!mTracker);
246
0
  mTracker = aTracker;
247
0
}
248
249
nsresult
250
MultipartImage::OnImageDataAvailable(nsIRequest* aRequest,
251
                                     nsISupports* aContext,
252
                                     nsIInputStream* aInStr,
253
                                     uint64_t aSourceOffset,
254
                                     uint32_t aCount)
255
0
{
256
0
  // Note that this method is special in that we forward it to the next part if
257
0
  // one exists, and *not* the current part.
258
0
259
0
  // We may trigger notifications that will free mNextPart, so keep it alive.
260
0
  RefPtr<Image> nextPart = mNextPart;
261
0
  if (nextPart) {
262
0
    nextPart->OnImageDataAvailable(aRequest, aContext, aInStr,
263
0
                                   aSourceOffset, aCount);
264
0
  } else {
265
0
    InnerImage()->OnImageDataAvailable(aRequest, aContext, aInStr,
266
0
                                       aSourceOffset, aCount);
267
0
  }
268
0
269
0
  return NS_OK;
270
0
}
271
272
nsresult
273
MultipartImage::OnImageDataComplete(nsIRequest* aRequest,
274
                                    nsISupports* aContext,
275
                                    nsresult aStatus,
276
                                    bool aLastPart)
277
0
{
278
0
  // Note that this method is special in that we forward it to the next part if
279
0
  // one exists, and *not* the current part.
280
0
281
0
  // We may trigger notifications that will free mNextPart, so keep it alive.
282
0
  RefPtr<Image> nextPart = mNextPart;
283
0
  if (nextPart) {
284
0
    nextPart->OnImageDataComplete(aRequest, aContext, aStatus, aLastPart);
285
0
  } else {
286
0
    InnerImage()->OnImageDataComplete(aRequest, aContext, aStatus, aLastPart);
287
0
  }
288
0
289
0
  return NS_OK;
290
0
}
291
292
void
293
MultipartImage::Notify(int32_t aType, const nsIntRect* aRect /* = nullptr*/)
294
0
{
295
0
  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
296
0
    mTracker->SyncNotifyProgress(FLAG_SIZE_AVAILABLE);
297
0
  } else if (aType == imgINotificationObserver::FRAME_UPDATE) {
298
0
    mTracker->SyncNotifyProgress(NoProgress, *aRect);
299
0
  } else if (aType == imgINotificationObserver::FRAME_COMPLETE) {
300
0
    mTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE);
301
0
  } else if (aType == imgINotificationObserver::LOAD_COMPLETE) {
302
0
    mTracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
303
0
  } else if (aType == imgINotificationObserver::DECODE_COMPLETE) {
304
0
    mTracker->SyncNotifyProgress(FLAG_DECODE_COMPLETE);
305
0
  } else if (aType == imgINotificationObserver::DISCARD) {
306
0
    mTracker->OnDiscard();
307
0
  } else if (aType == imgINotificationObserver::UNLOCKED_DRAW) {
308
0
    mTracker->OnUnlockedDraw();
309
0
  } else if (aType == imgINotificationObserver::IS_ANIMATED) {
310
0
    mTracker->SyncNotifyProgress(FLAG_IS_ANIMATED);
311
0
  } else if (aType == imgINotificationObserver::HAS_TRANSPARENCY) {
312
0
    mTracker->SyncNotifyProgress(FLAG_HAS_TRANSPARENCY);
313
0
  } else {
314
0
    MOZ_ASSERT_UNREACHABLE("Notification list should be exhaustive");
315
0
  }
316
0
}
317
318
void
319
MultipartImage::OnLoadComplete(bool aLastPart)
320
0
{
321
0
  Progress progress = FLAG_LOAD_COMPLETE;
322
0
  if (aLastPart) {
323
0
    progress |= FLAG_LAST_PART_COMPLETE;
324
0
  }
325
0
  mTracker->SyncNotifyProgress(progress);
326
0
}
327
328
void
329
MultipartImage::SetHasImage()
330
0
{
331
0
  mTracker->OnImageAvailable();
332
0
}
333
334
bool
335
MultipartImage::NotificationsDeferred() const
336
0
{
337
0
  return mPendingNotify;
338
0
}
339
340
void
341
MultipartImage::MarkPendingNotify()
342
0
{
343
0
  mPendingNotify = true;
344
0
}
345
346
void
347
MultipartImage::ClearPendingNotify()
348
0
{
349
0
  mPendingNotify = false;
350
0
}
351
352
} // namespace image
353
} // namespace mozilla