Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/style/ImageLoader.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 file,
5
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
/* A class that handles style system image loads (other image loads are handled
8
 * by the nodes in the content tree).
9
 */
10
11
#include "mozilla/css/ImageLoader.h"
12
#include "nsAutoPtr.h"
13
#include "nsContentUtils.h"
14
#include "nsLayoutUtils.h"
15
#include "nsError.h"
16
#include "nsDisplayList.h"
17
#include "nsIFrameInlines.h"
18
#include "FrameLayerBuilder.h"
19
#include "SVGObserverUtils.h"
20
#include "imgIContainer.h"
21
#include "Image.h"
22
#include "GeckoProfiler.h"
23
#include "mozilla/layers/WebRenderUserData.h"
24
25
namespace mozilla {
26
namespace css {
27
28
void
29
ImageLoader::DropDocumentReference()
30
0
{
31
0
  // It's okay if GetPresContext returns null here (due to the presshell pointer
32
0
  // on the document being null) as that means the presshell has already
33
0
  // been destroyed, and it also calls ClearFrames when it is destroyed.
34
0
  ClearFrames(GetPresContext());
35
0
36
0
  for (auto it = mImages.Iter(); !it.Done(); it.Next()) {
37
0
    ImageLoader::Image* image = it.Get()->GetKey();
38
0
    imgIRequest* request = image->mRequests.GetWeak(mDocument);
39
0
    if (request) {
40
0
      request->CancelAndForgetObserver(NS_BINDING_ABORTED);
41
0
    }
42
0
    image->mRequests.Remove(mDocument);
43
0
  }
44
0
  mImages.Clear();
45
0
46
0
  mDocument = nullptr;
47
0
}
48
49
// Normally, arrays of requests and frames are sorted by their pointer address,
50
// for faster lookup. When recording or replaying, we don't do this, so that
51
// the arrays retain their insertion order and are consistent between recording
52
// and replaying.
53
template <typename Elem, typename Item, typename Comparator = nsDefaultComparator<Elem, Item>>
54
static size_t
55
GetMaybeSortedIndex(const nsTArray<Elem>& aArray, const Item& aItem, bool* aFound,
56
                    Comparator aComparator = Comparator())
57
0
{
58
0
  if (recordreplay::IsRecordingOrReplaying()) {
59
0
    size_t index = aArray.IndexOf(aItem, 0, aComparator);
60
0
    *aFound = index != nsTArray<Elem>::NoIndex;
61
0
    return *aFound ? index + 1 : aArray.Length();
62
0
  }
63
0
  size_t index = aArray.IndexOfFirstElementGt(aItem, aComparator);
64
0
  *aFound = index > 0 && aComparator.Equals(aItem, aArray.ElementAt(index - 1));
65
0
  return index;
66
0
}
Unexecuted instantiation: Unified_cpp_layout_style1.cpp:unsigned long mozilla::css::GetMaybeSortedIndex<mozilla::css::ImageLoader::FrameWithFlags, mozilla::css::ImageLoader::FrameWithFlags, mozilla::css::ImageLoader::FrameOnlyComparator>(nsTArray<mozilla::css::ImageLoader::FrameWithFlags> const&, mozilla::css::ImageLoader::FrameWithFlags const&, bool*, mozilla::css::ImageLoader::FrameOnlyComparator)
Unexecuted instantiation: Unified_cpp_layout_style1.cpp:unsigned long mozilla::css::GetMaybeSortedIndex<nsCOMPtr<imgIRequest>, imgIRequest*, nsDefaultComparator<nsCOMPtr<imgIRequest>, imgIRequest*> >(nsTArray<nsCOMPtr<imgIRequest> > const&, imgIRequest* const&, bool*, nsDefaultComparator<nsCOMPtr<imgIRequest>, imgIRequest*>)
67
68
void
69
ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest,
70
                                     nsIFrame* aFrame,
71
                                     FrameFlags aFlags)
72
0
{
73
0
  nsCOMPtr<imgINotificationObserver> observer;
74
0
  aRequest->GetNotificationObserver(getter_AddRefs(observer));
75
0
  if (!observer) {
76
0
    // The request has already been canceled, so ignore it.  This is ok because
77
0
    // we're not going to get any more notifications from a canceled request.
78
0
    return;
79
0
  }
80
0
81
0
  MOZ_ASSERT(observer == this);
82
0
83
0
  FrameSet* frameSet =
84
0
    mRequestToFrameMap.LookupForAdd(aRequest).OrInsert([=]() {
85
0
      nsPresContext* presContext = GetPresContext();
86
0
      if (presContext) {
87
0
        nsLayoutUtils::RegisterImageRequestIfAnimated(presContext,
88
0
                                                      aRequest,
89
0
                                                      nullptr);
90
0
      }
91
0
      return new FrameSet();
92
0
    });
93
0
94
0
  RequestSet* requestSet =
95
0
    mFrameToRequestMap.LookupForAdd(aFrame).OrInsert([=]() {
96
0
      aFrame->SetHasImageRequest(true);
97
0
      return new RequestSet();
98
0
    });
99
0
100
0
  // Add frame to the frameSet, and handle any special processing the
101
0
  // frame might require.
102
0
  FrameWithFlags fwf(aFrame);
103
0
  FrameWithFlags* fwfToModify(&fwf);
104
0
105
0
  // See if the frameSet already has this frame.
106
0
  bool found;
107
0
  uint32_t i = GetMaybeSortedIndex(*frameSet, fwf, &found, FrameOnlyComparator());
108
0
  if (found) {
109
0
    // We're already tracking this frame, so prepare to modify the
110
0
    // existing FrameWithFlags object.
111
0
    fwfToModify = &frameSet->ElementAt(i-1);
112
0
  }
113
0
114
0
  // Check if the frame requires special processing.
115
0
  if (aFlags & REQUEST_REQUIRES_REFLOW) {
116
0
    fwfToModify->mFlags |= REQUEST_REQUIRES_REFLOW;
117
0
118
0
    // If we weren't already blocking onload, do that now.
119
0
    if ((fwfToModify->mFlags & REQUEST_HAS_BLOCKED_ONLOAD) == 0) {
120
0
      // Get request status to see if we should block onload, and if we can
121
0
      // request reflow immediately.
122
0
      uint32_t status = 0;
123
0
      if (NS_SUCCEEDED(aRequest->GetImageStatus(&status)) &&
124
0
          !(status & imgIRequest::STATUS_ERROR)) {
125
0
        // No error, so we can block onload.
126
0
        fwfToModify->mFlags |= REQUEST_HAS_BLOCKED_ONLOAD;
127
0
128
0
        // Block document onload until we either remove the frame in
129
0
        // RemoveRequestToFrameMapping or onLoadComplete, or complete a reflow.
130
0
        mDocument->BlockOnload();
131
0
132
0
        // We need to stay blocked until we get a reflow. If the first frame
133
0
        // is not yet decoded, we'll trigger that reflow from onFrameComplete.
134
0
        // But if the first frame is already decoded, we need to trigger that
135
0
        // reflow now, because we'll never get a call to onFrameComplete.
136
0
        if(status & imgIRequest::STATUS_FRAME_COMPLETE) {
137
0
          RequestReflowOnFrame(fwfToModify, aRequest);
138
0
        } else {
139
0
          // If we don't already have a complete frame, kickoff decode. This
140
0
          // will ensure that either onFrameComplete or onLoadComplete will
141
0
          // unblock document onload.
142
0
143
0
          // We want to request decode in such a way that avoids triggering
144
0
          // sync decode. First, we attempt to convert the aRequest into
145
0
          // a imgIContainer. If that succeeds, then aRequest has an image
146
0
          // and we can request decoding for size at zero size, and that will
147
0
          // trigger async decode. If the conversion to imgIContainer is
148
0
          // unsuccessful, then that means aRequest doesn't have an image yet,
149
0
          // which means we can safely call StartDecoding() on it without
150
0
          // triggering any synchronous work.
151
0
          nsCOMPtr<imgIContainer> imgContainer;
152
0
          aRequest->GetImage(getter_AddRefs(imgContainer));
153
0
          if (imgContainer) {
154
0
            imgContainer->RequestDecodeForSize(gfx::IntSize(0, 0),
155
0
              imgIContainer::DECODE_FLAGS_DEFAULT);
156
0
          } else {
157
0
            // It's safe to call StartDecoding directly, since it can't
158
0
            // trigger synchronous decode without an image. Flags are ignored.
159
0
            aRequest->StartDecoding(imgIContainer::FLAG_NONE);
160
0
          }
161
0
        }
162
0
      }
163
0
    }
164
0
  }
165
0
166
0
  // Do some sanity checking to ensure that we only add to one mapping
167
0
  // iff we also add to the other mapping.
168
0
  DebugOnly<bool> didAddToFrameSet(false);
169
0
  DebugOnly<bool> didAddToRequestSet(false);
170
0
171
0
  // If we weren't already tracking this frame, add it to the frameSet.
172
0
  if (!found) {
173
0
    frameSet->InsertElementAt(i, fwf);
174
0
    didAddToFrameSet = true;
175
0
  }
176
0
177
0
  // Add request to the request set if it wasn't already there.
178
0
  i = GetMaybeSortedIndex(*requestSet, aRequest, &found);
179
0
  if (!found) {
180
0
    requestSet->InsertElementAt(i, aRequest);
181
0
    didAddToRequestSet = true;
182
0
  }
183
0
184
0
  MOZ_ASSERT(didAddToFrameSet == didAddToRequestSet,
185
0
             "We should only add to one map iff we also add to the other map.");
186
0
}
187
188
void
189
ImageLoader::MaybeRegisterCSSImage(ImageLoader::Image* aImage)
190
0
{
191
0
  NS_ASSERTION(aImage, "This should never be null!");
192
0
193
0
  bool found = false;
194
0
  aImage->mRequests.GetWeak(mDocument, &found);
195
0
  if (found) {
196
0
    // This document already has a request.
197
0
    return;
198
0
  }
199
0
200
0
  imgRequestProxy* canonicalRequest = aImage->mRequests.GetWeak(nullptr);
201
0
  if (!canonicalRequest) {
202
0
    // The image was blocked or something.
203
0
    return;
204
0
  }
205
0
206
0
  RefPtr<imgRequestProxy> request;
207
0
208
0
  // Ignore errors here.  If cloning fails for some reason we'll put a null
209
0
  // entry in the hash and we won't keep trying to clone.
210
0
  mInClone = true;
211
0
  canonicalRequest->SyncClone(this, mDocument, getter_AddRefs(request));
212
0
  mInClone = false;
213
0
214
0
  aImage->mRequests.Put(mDocument, request);
215
0
216
0
  AddImage(aImage);
217
0
}
218
219
void
220
ImageLoader::DeregisterCSSImage(ImageLoader::Image* aImage)
221
0
{
222
0
  RemoveImage(aImage);
223
0
}
224
225
void
226
ImageLoader::RemoveRequestToFrameMapping(imgIRequest* aRequest,
227
                                         nsIFrame*    aFrame)
228
0
{
229
#ifdef DEBUG
230
  {
231
    nsCOMPtr<imgINotificationObserver> observer;
232
    aRequest->GetNotificationObserver(getter_AddRefs(observer));
233
    MOZ_ASSERT(!observer || observer == this);
234
  }
235
#endif
236
237
0
  if (auto entry = mRequestToFrameMap.Lookup(aRequest)) {
238
0
    FrameSet* frameSet = entry.Data();
239
0
    MOZ_ASSERT(frameSet, "This should never be null");
240
0
241
0
    // Before we remove aFrame from the frameSet, unblock onload if needed.
242
0
    bool found;
243
0
    uint32_t i = GetMaybeSortedIndex(*frameSet, FrameWithFlags(aFrame), &found,
244
0
                                     FrameOnlyComparator());
245
0
    if (found) {
246
0
      FrameWithFlags& fwf = frameSet->ElementAt(i-1);
247
0
      if (fwf.mFlags & REQUEST_HAS_BLOCKED_ONLOAD) {
248
0
        mDocument->UnblockOnload(false);
249
0
        // We're about to remove fwf from the frameSet, so we don't bother
250
0
        // updating the flag.
251
0
      }
252
0
      frameSet->RemoveElementAt(i-1);
253
0
    }
254
0
255
0
    if (frameSet->IsEmpty()) {
256
0
      nsPresContext* presContext = GetPresContext();
257
0
      if (presContext) {
258
0
        nsLayoutUtils::DeregisterImageRequest(presContext, aRequest, nullptr);
259
0
      }
260
0
      entry.Remove();
261
0
    }
262
0
  }
263
0
}
264
265
void
266
ImageLoader::RemoveFrameToRequestMapping(imgIRequest* aRequest,
267
                                         nsIFrame*    aFrame)
268
0
{
269
0
  if (auto entry = mFrameToRequestMap.Lookup(aFrame)) {
270
0
    RequestSet* requestSet = entry.Data();
271
0
    MOZ_ASSERT(requestSet, "This should never be null");
272
0
    if (recordreplay::IsRecordingOrReplaying()) {
273
0
      requestSet->RemoveElement(aRequest);
274
0
    } else {
275
0
      requestSet->RemoveElementSorted(aRequest);
276
0
    }
277
0
    if (requestSet->IsEmpty()) {
278
0
      aFrame->SetHasImageRequest(false);
279
0
      entry.Remove();
280
0
    }
281
0
  }
282
0
}
283
284
void
285
ImageLoader::DisassociateRequestFromFrame(imgIRequest* aRequest,
286
                                          nsIFrame*    aFrame)
287
0
{
288
0
  MOZ_ASSERT(aFrame->HasImageRequest(), "why call me?");
289
0
  RemoveRequestToFrameMapping(aRequest, aFrame);
290
0
  RemoveFrameToRequestMapping(aRequest, aFrame);
291
0
}
292
293
void
294
ImageLoader::DropRequestsForFrame(nsIFrame* aFrame)
295
0
{
296
0
  MOZ_ASSERT(aFrame->HasImageRequest(), "why call me?");
297
0
  nsAutoPtr<RequestSet> requestSet;
298
0
  mFrameToRequestMap.Remove(aFrame, &requestSet);
299
0
  aFrame->SetHasImageRequest(false);
300
0
  if (MOZ_UNLIKELY(!requestSet)) {
301
0
    MOZ_ASSERT_UNREACHABLE("HasImageRequest was lying");
302
0
    return;
303
0
  }
304
0
  for (imgIRequest* request : *requestSet) {
305
0
    RemoveRequestToFrameMapping(request, aFrame);
306
0
  }
307
0
}
308
309
void
310
ImageLoader::SetAnimationMode(uint16_t aMode)
311
0
{
312
0
  NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
313
0
               aMode == imgIContainer::kDontAnimMode ||
314
0
               aMode == imgIContainer::kLoopOnceAnimMode,
315
0
               "Wrong Animation Mode is being set!");
316
0
317
0
  for (auto iter = mRequestToFrameMap.ConstIter(); !iter.Done(); iter.Next()) {
318
0
    auto request = static_cast<imgIRequest*>(iter.Key());
319
0
320
#ifdef DEBUG
321
    {
322
      nsCOMPtr<imgIRequest> debugRequest = do_QueryInterface(request);
323
      NS_ASSERTION(debugRequest == request, "This is bad");
324
    }
325
#endif
326
327
0
    nsCOMPtr<imgIContainer> container;
328
0
    request->GetImage(getter_AddRefs(container));
329
0
    if (!container) {
330
0
      continue;
331
0
    }
332
0
333
0
    // This can fail if the image is in error, and we don't care.
334
0
    container->SetAnimationMode(aMode);
335
0
  }
336
0
}
337
338
void
339
ImageLoader::ClearFrames(nsPresContext* aPresContext)
340
0
{
341
0
  for (auto iter = mRequestToFrameMap.ConstIter(); !iter.Done(); iter.Next()) {
342
0
    auto request = static_cast<imgIRequest*>(iter.Key());
343
0
344
#ifdef DEBUG
345
    {
346
      nsCOMPtr<imgIRequest> debugRequest = do_QueryInterface(request);
347
      NS_ASSERTION(debugRequest == request, "This is bad");
348
    }
349
#endif
350
351
0
    if (aPresContext) {
352
0
      nsLayoutUtils::DeregisterImageRequest(aPresContext,
353
0
                                            request,
354
0
                                            nullptr);
355
0
    }
356
0
  }
357
0
358
0
  mRequestToFrameMap.Clear();
359
0
  mFrameToRequestMap.Clear();
360
0
}
361
362
void
363
ImageLoader::LoadImage(nsIURI* aURI, nsIPrincipal* aOriginPrincipal,
364
                       nsIURI* aReferrer,
365
                       mozilla::net::ReferrerPolicy aPolicy,
366
                       ImageLoader::Image* aImage,
367
                       CORSMode aCorsMode)
368
0
{
369
0
  NS_ASSERTION(aImage->mRequests.Count() == 0, "Huh?");
370
0
371
0
  aImage->mRequests.Put(nullptr, nullptr);
372
0
373
0
  if (!aURI) {
374
0
    return;
375
0
  }
376
0
377
0
  int32_t loadFlags = nsIRequest::LOAD_NORMAL |
378
0
                      nsContentUtils::CORSModeToLoadImageFlags(aCorsMode);
379
0
380
0
  RefPtr<imgRequestProxy> request;
381
0
  nsresult rv = nsContentUtils::LoadImage(aURI, mDocument, mDocument,
382
0
                                          aOriginPrincipal, 0, aReferrer,
383
0
                                          aPolicy,
384
0
                                          nullptr, loadFlags,
385
0
                                          NS_LITERAL_STRING("css"),
386
0
                                          getter_AddRefs(request));
387
0
388
0
  if (NS_FAILED(rv) || !request) {
389
0
    return;
390
0
  }
391
0
392
0
  RefPtr<imgRequestProxy> clonedRequest;
393
0
  mInClone = true;
394
0
  rv = request->SyncClone(this, mDocument, getter_AddRefs(clonedRequest));
395
0
  mInClone = false;
396
0
397
0
  if (NS_FAILED(rv)) {
398
0
    return;
399
0
  }
400
0
401
0
  aImage->mRequests.Put(nullptr, request);
402
0
  aImage->mRequests.Put(mDocument, clonedRequest);
403
0
404
0
  AddImage(aImage);
405
0
}
406
407
void
408
ImageLoader::AddImage(ImageLoader::Image* aImage)
409
0
{
410
0
  NS_ASSERTION(!mImages.Contains(aImage), "Huh?");
411
0
  mImages.PutEntry(aImage);
412
0
}
413
414
void
415
ImageLoader::RemoveImage(ImageLoader::Image* aImage)
416
0
{
417
0
  NS_ASSERTION(mImages.Contains(aImage), "Huh?");
418
0
  mImages.RemoveEntry(aImage);
419
0
}
420
421
nsPresContext*
422
ImageLoader::GetPresContext()
423
0
{
424
0
  if (!mDocument) {
425
0
    return nullptr;
426
0
  }
427
0
428
0
  return mDocument->GetPresContext();
429
0
}
430
431
static bool
432
IsRenderNoImages(uint32_t aDisplayItemKey)
433
0
{
434
0
  DisplayItemType type = GetDisplayItemTypeFromKey(aDisplayItemKey);
435
0
  uint8_t flags = GetDisplayItemFlagsForType(type);
436
0
  return flags & TYPE_RENDERS_NO_IMAGES;
437
0
}
438
439
static void
440
InvalidateImages(nsIFrame* aFrame)
441
0
{
442
0
  bool invalidateFrame = false;
443
0
  const SmallPointerArray<DisplayItemData>& array = aFrame->DisplayItemData();
444
0
  for (uint32_t i = 0; i < array.Length(); i++) {
445
0
    DisplayItemData* data = DisplayItemData::AssertDisplayItemData(array.ElementAt(i));
446
0
    uint32_t displayItemKey = data->GetDisplayItemKey();
447
0
    if (displayItemKey != 0 && !IsRenderNoImages(displayItemKey)) {
448
0
      if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
449
0
        DisplayItemType type = GetDisplayItemTypeFromKey(displayItemKey);
450
0
        printf_stderr("Invalidating display item(type=%d) based on frame %p \
451
0
                       because it might contain an invalidated image\n",
452
0
                       static_cast<uint32_t>(type), aFrame);
453
0
      }
454
0
455
0
      data->Invalidate();
456
0
      invalidateFrame = true;
457
0
    }
458
0
  }
459
0
  if (auto userDataTable =
460
0
       aFrame->GetProperty(layers::WebRenderUserDataProperty::Key())) {
461
0
    for (auto iter = userDataTable->Iter(); !iter.Done(); iter.Next()) {
462
0
      RefPtr<layers::WebRenderUserData> data = iter.UserData();
463
0
      if (data->GetType() == layers::WebRenderAnimationData::UserDataType::eFallback &&
464
0
          !IsRenderNoImages(data->GetDisplayItemKey())) {
465
0
        static_cast<layers::WebRenderFallbackData*>(data.get())->SetInvalid(true);
466
0
      }
467
0
      //XXX: handle Blob data
468
0
      invalidateFrame = true;
469
0
    }
470
0
  }
471
0
472
0
  if (invalidateFrame) {
473
0
    aFrame->SchedulePaint();
474
0
  }
475
0
}
476
477
void
478
ImageLoader::DoRedraw(FrameSet* aFrameSet, bool aForcePaint)
479
0
{
480
0
  NS_ASSERTION(aFrameSet, "Must have a frame set");
481
0
  NS_ASSERTION(mDocument, "Should have returned earlier!");
482
0
483
0
  for (FrameWithFlags& fwf : *aFrameSet) {
484
0
    nsIFrame* frame = fwf.mFrame;
485
0
    if (frame->StyleVisibility()->IsVisible()) {
486
0
      if (frame->IsFrameOfType(nsIFrame::eTablePart)) {
487
0
        // Tables don't necessarily build border/background display items
488
0
        // for the individual table part frames, so IterateRetainedDataFor
489
0
        // might not find the right display item.
490
0
        frame->InvalidateFrame();
491
0
      } else {
492
0
        InvalidateImages(frame);
493
0
494
0
        // Update ancestor rendering observers (-moz-element etc)
495
0
        nsIFrame *f = frame;
496
0
        while (f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
497
0
          SVGObserverUtils::InvalidateDirectRenderingObservers(f);
498
0
          f = nsLayoutUtils::GetCrossDocParentFrame(f);
499
0
        }
500
0
501
0
        if (aForcePaint) {
502
0
          frame->SchedulePaint();
503
0
        }
504
0
      }
505
0
    }
506
0
  }
507
0
}
508
509
void
510
ImageLoader::UnblockOnloadIfNeeded(nsIFrame* aFrame, imgIRequest* aRequest)
511
0
{
512
0
  MOZ_ASSERT(aFrame);
513
0
  MOZ_ASSERT(aRequest);
514
0
515
0
  FrameSet* frameSet = mRequestToFrameMap.Get(aRequest);
516
0
  if (!frameSet) {
517
0
    return;
518
0
  }
519
0
520
0
  size_t i = frameSet->BinaryIndexOf(FrameWithFlags(aFrame),
521
0
                                     FrameOnlyComparator());
522
0
  if (i != FrameSet::NoIndex) {
523
0
    FrameWithFlags& fwf = frameSet->ElementAt(i);
524
0
    if (fwf.mFlags & REQUEST_HAS_BLOCKED_ONLOAD) {
525
0
      mDocument->UnblockOnload(false);
526
0
      fwf.mFlags &= ~REQUEST_HAS_BLOCKED_ONLOAD;
527
0
    }
528
0
  }
529
0
}
530
531
void
532
ImageLoader::RequestReflowIfNeeded(FrameSet* aFrameSet, imgIRequest* aRequest)
533
0
{
534
0
  MOZ_ASSERT(aFrameSet);
535
0
536
0
  for (FrameWithFlags& fwf : *aFrameSet) {
537
0
    if (fwf.mFlags & REQUEST_REQUIRES_REFLOW) {
538
0
      // Tell the container of the frame to reflow because the
539
0
      // image request has finished decoding its first frame.
540
0
      RequestReflowOnFrame(&fwf, aRequest);
541
0
    }
542
0
  }
543
0
}
544
545
void
546
ImageLoader::RequestReflowOnFrame(FrameWithFlags* aFwf, imgIRequest* aRequest)
547
0
{
548
0
  nsIFrame* frame = aFwf->mFrame;
549
0
550
0
  // Actually request the reflow.
551
0
  nsIFrame* parent = frame->GetInFlowParent();
552
0
  parent->PresShell()->FrameNeedsReflow(parent, nsIPresShell::eStyleChange,
553
0
                                        NS_FRAME_IS_DIRTY);
554
0
555
0
  // We'll respond to the reflow events by unblocking onload, regardless
556
0
  // of whether the reflow was completed or cancelled. The callback will
557
0
  // also delete itself when it is called.
558
0
  ImageReflowCallback* unblocker = new ImageReflowCallback(this, frame,
559
0
                                                           aRequest);
560
0
  parent->PresShell()->PostReflowCallback(unblocker);
561
0
}
562
563
NS_IMPL_ADDREF(ImageLoader)
564
NS_IMPL_RELEASE(ImageLoader)
565
566
0
NS_INTERFACE_MAP_BEGIN(ImageLoader)
567
0
  NS_INTERFACE_MAP_ENTRY(imgINotificationObserver)
568
0
NS_INTERFACE_MAP_END
569
570
NS_IMETHODIMP
571
ImageLoader::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
572
0
{
573
0
#ifdef MOZ_GECKO_PROFILER
574
0
  nsCString uriString;
575
0
  if (profiler_is_active()) {
576
0
    nsCOMPtr<nsIURI> uri;
577
0
    aRequest->GetFinalURI(getter_AddRefs(uri));
578
0
    if (uri) {
579
0
      uri->GetSpec(uriString);
580
0
    }
581
0
  }
582
0
583
0
  AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("ImageLoader::Notify", OTHER, uriString);
584
0
#endif
585
0
586
0
  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
587
0
    nsCOMPtr<imgIContainer> image;
588
0
    aRequest->GetImage(getter_AddRefs(image));
589
0
    return OnSizeAvailable(aRequest, image);
590
0
  }
591
0
592
0
  if (aType == imgINotificationObserver::IS_ANIMATED) {
593
0
    return OnImageIsAnimated(aRequest);
594
0
  }
595
0
596
0
  if (aType == imgINotificationObserver::FRAME_COMPLETE) {
597
0
    return OnFrameComplete(aRequest);
598
0
  }
599
0
600
0
  if (aType == imgINotificationObserver::FRAME_UPDATE) {
601
0
    return OnFrameUpdate(aRequest);
602
0
  }
603
0
604
0
  if (aType == imgINotificationObserver::DECODE_COMPLETE) {
605
0
    nsCOMPtr<imgIContainer> image;
606
0
    aRequest->GetImage(getter_AddRefs(image));
607
0
    if (image && mDocument) {
608
0
      image->PropagateUseCounters(mDocument);
609
0
    }
610
0
  }
611
0
612
0
  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
613
0
    return OnLoadComplete(aRequest);
614
0
  }
615
0
616
0
  return NS_OK;
617
0
}
618
619
nsresult
620
ImageLoader::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
621
0
{
622
0
  nsPresContext* presContext = GetPresContext();
623
0
  if (!presContext) {
624
0
    return NS_OK;
625
0
  }
626
0
627
0
  aImage->SetAnimationMode(presContext->ImageAnimationMode());
628
0
629
0
  FrameSet* frameSet = mRequestToFrameMap.Get(aRequest);
630
0
  if (!frameSet) {
631
0
    return NS_OK;
632
0
  }
633
0
634
0
  for (FrameWithFlags& fwf : *frameSet) {
635
0
    nsIFrame* frame = fwf.mFrame;
636
0
    if (frame->StyleVisibility()->IsVisible()) {
637
0
      frame->MarkNeedsDisplayItemRebuild();
638
0
    }
639
0
  }
640
0
641
0
  return NS_OK;
642
0
}
643
644
nsresult
645
ImageLoader::OnImageIsAnimated(imgIRequest* aRequest)
646
0
{
647
0
  if (!mDocument) {
648
0
    return NS_OK;
649
0
  }
650
0
651
0
  FrameSet* frameSet = mRequestToFrameMap.Get(aRequest);
652
0
  if (!frameSet) {
653
0
    return NS_OK;
654
0
  }
655
0
656
0
  // Register with the refresh driver now that we are aware that
657
0
  // we are animated.
658
0
  nsPresContext* presContext = GetPresContext();
659
0
  if (presContext) {
660
0
    nsLayoutUtils::RegisterImageRequest(presContext,
661
0
                                        aRequest,
662
0
                                        nullptr);
663
0
  }
664
0
665
0
  return NS_OK;
666
0
}
667
668
nsresult
669
ImageLoader::OnFrameComplete(imgIRequest* aRequest)
670
0
{
671
0
  if (!mDocument || mInClone) {
672
0
    return NS_OK;
673
0
  }
674
0
675
0
  FrameSet* frameSet = mRequestToFrameMap.Get(aRequest);
676
0
  if (!frameSet) {
677
0
    return NS_OK;
678
0
  }
679
0
680
0
  // We may need reflow (for example if the image is from shape-outside).
681
0
  RequestReflowIfNeeded(frameSet, aRequest);
682
0
683
0
  // Since we just finished decoding a frame, we always want to paint, in case
684
0
  // we're now able to paint an image that we couldn't paint before (and hence
685
0
  // that we don't have retained data for).
686
0
  DoRedraw(frameSet, /* aForcePaint = */ true);
687
0
688
0
  return NS_OK;
689
0
}
690
691
nsresult
692
ImageLoader::OnFrameUpdate(imgIRequest* aRequest)
693
0
{
694
0
  if (!mDocument || mInClone) {
695
0
    return NS_OK;
696
0
  }
697
0
698
0
  FrameSet* frameSet = mRequestToFrameMap.Get(aRequest);
699
0
  if (!frameSet) {
700
0
    return NS_OK;
701
0
  }
702
0
703
0
  DoRedraw(frameSet, /* aForcePaint = */ false);
704
0
705
0
  return NS_OK;
706
0
}
707
708
nsresult
709
ImageLoader::OnLoadComplete(imgIRequest* aRequest)
710
0
{
711
0
  if (!mDocument || mInClone) {
712
0
    return NS_OK;
713
0
  }
714
0
715
0
  FrameSet* frameSet = mRequestToFrameMap.Get(aRequest);
716
0
  if (!frameSet) {
717
0
    return NS_OK;
718
0
  }
719
0
720
0
  // Check if aRequest has an error state. If it does, we need to unblock
721
0
  // Document onload for all the frames associated with this request that
722
0
  // have blocked onload. This is what happens in a CORS mode violation, and
723
0
  // may happen during other network events.
724
0
  uint32_t status = 0;
725
0
  if(NS_SUCCEEDED(aRequest->GetImageStatus(&status)) &&
726
0
     status & imgIRequest::STATUS_ERROR) {
727
0
    for (FrameWithFlags& fwf : *frameSet) {
728
0
      if (fwf.mFlags & REQUEST_HAS_BLOCKED_ONLOAD) {
729
0
        // We've blocked onload. Unblock onload and clear the flag.
730
0
        mDocument->UnblockOnload(false);
731
0
        fwf.mFlags &= ~REQUEST_HAS_BLOCKED_ONLOAD;
732
0
      }
733
0
    }
734
0
  }
735
0
736
0
  return NS_OK;
737
0
}
738
739
void
740
ImageLoader::FlushUseCounters()
741
0
{
742
0
  for (auto iter = mImages.Iter(); !iter.Done(); iter.Next()) {
743
0
    nsPtrHashKey<Image>* key = iter.Get();
744
0
    ImageLoader::Image* image = key->GetKey();
745
0
746
0
    imgIRequest* request = image->mRequests.GetWeak(mDocument);
747
0
748
0
    nsCOMPtr<imgIContainer> container;
749
0
    request->GetImage(getter_AddRefs(container));
750
0
    if (container) {
751
0
      static_cast<image::Image*>(container.get())->ReportUseCounters();
752
0
    }
753
0
  }
754
0
}
755
756
bool
757
ImageLoader::ImageReflowCallback::ReflowFinished()
758
0
{
759
0
  // Check that the frame is still valid. If it isn't, then onload was
760
0
  // unblocked when the frame was removed from the FrameSet in
761
0
  // RemoveRequestToFrameMapping.
762
0
  if (mFrame.IsAlive()) {
763
0
    mLoader->UnblockOnloadIfNeeded(mFrame, mRequest);
764
0
  }
765
0
766
0
  // Get rid of this callback object.
767
0
  delete this;
768
0
769
0
  // We don't need to trigger layout.
770
0
  return false;
771
0
}
772
773
void
774
ImageLoader::ImageReflowCallback::ReflowCallbackCanceled()
775
0
{
776
0
  // Check that the frame is still valid. If it isn't, then onload was
777
0
  // unblocked when the frame was removed from the FrameSet in
778
0
  // RemoveRequestToFrameMapping.
779
0
  if (mFrame.IsAlive()) {
780
0
    mLoader->UnblockOnloadIfNeeded(mFrame, mRequest);
781
0
  }
782
0
783
0
  // Get rid of this callback object.
784
0
  delete this;
785
0
}
786
787
} // namespace css
788
} // namespace mozilla