Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsImageLoadingContent.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
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
/*
8
 * A base class which implements nsIImageLoadingContent and can be
9
 * subclassed by various content nodes that want to provide image
10
 * loading functionality (eg <img>, <object>, etc).
11
 */
12
13
#include "nsImageLoadingContent.h"
14
#include "nsError.h"
15
#include "nsIContent.h"
16
#include "nsIDocument.h"
17
#include "nsIScriptGlobalObject.h"
18
#include "nsIDOMWindow.h"
19
#include "nsServiceManagerUtils.h"
20
#include "nsContentList.h"
21
#include "nsContentPolicyUtils.h"
22
#include "nsIURI.h"
23
#include "nsILoadGroup.h"
24
#include "imgIContainer.h"
25
#include "imgLoader.h"
26
#include "imgRequestProxy.h"
27
#include "nsThreadUtils.h"
28
#include "nsNetUtil.h"
29
#include "nsImageFrame.h"
30
#include "nsSVGImageFrame.h"
31
32
#include "nsIPresShell.h"
33
34
#include "nsIChannel.h"
35
#include "nsIStreamListener.h"
36
37
#include "nsIFrame.h"
38
39
#include "nsContentUtils.h"
40
#include "nsLayoutUtils.h"
41
#include "nsIContentPolicy.h"
42
#include "SVGObserverUtils.h"
43
44
#include "gfxPrefs.h"
45
46
#include "mozAutoDocUpdate.h"
47
#include "mozilla/AsyncEventDispatcher.h"
48
#include "mozilla/AutoRestore.h"
49
#include "mozilla/EventStateManager.h"
50
#include "mozilla/EventStates.h"
51
#include "mozilla/dom/Element.h"
52
#include "mozilla/dom/ImageTracker.h"
53
#include "mozilla/dom/ScriptSettings.h"
54
#include "mozilla/Preferences.h"
55
56
#ifdef LoadImage
57
// Undefine LoadImage to prevent naming conflict with Windows.
58
#undef LoadImage
59
#endif
60
61
using namespace mozilla;
62
using namespace mozilla::dom;
63
64
#ifdef DEBUG_chb
65
static void PrintReqURL(imgIRequest* req) {
66
  if (!req) {
67
    printf("(null req)\n");
68
    return;
69
  }
70
71
  nsCOMPtr<nsIURI> uri;
72
  req->GetURI(getter_AddRefs(uri));
73
  if (!uri) {
74
    printf("(null uri)\n");
75
    return;
76
  }
77
78
  nsAutoCString spec;
79
  uri->GetSpec(spec);
80
  printf("spec='%s'\n", spec.get());
81
}
82
#endif /* DEBUG_chb */
83
84
const nsAttrValue::EnumTable nsImageLoadingContent::kDecodingTable[] = {
85
  { "auto",   nsImageLoadingContent::ImageDecodingType::Auto },
86
  { "async",  nsImageLoadingContent::ImageDecodingType::Async },
87
  { "sync",   nsImageLoadingContent::ImageDecodingType::Sync },
88
  { nullptr,  0 }
89
};
90
91
const nsAttrValue::EnumTable* nsImageLoadingContent::kDecodingTableDefault =
92
  &nsImageLoadingContent::kDecodingTable[0];
93
94
nsImageLoadingContent::nsImageLoadingContent()
95
  : mCurrentRequestFlags(0),
96
    mPendingRequestFlags(0),
97
    mObserverList(nullptr),
98
    mImageBlockingStatus(nsIContentPolicy::ACCEPT),
99
    mLoadingEnabled(true),
100
    mIsImageStateForced(false),
101
    mLoading(false),
102
    // mBroken starts out true, since an image without a URI is broken....
103
    mBroken(true),
104
    mUserDisabled(false),
105
    mSuppressed(false),
106
    mNewRequestsWillNeedAnimationReset(false),
107
    mUseUrgentStartForChannel(false),
108
    mStateChangerDepth(0),
109
    mCurrentRequestRegistered(false),
110
    mPendingRequestRegistered(false),
111
    mIsStartingImageLoad(false),
112
    mSyncDecodingHint(false)
113
0
{
114
0
  if (!nsContentUtils::GetImgLoaderForChannel(nullptr, nullptr)) {
115
0
    mLoadingEnabled = false;
116
0
  }
117
0
118
0
  mMostRecentRequestChange = TimeStamp::ProcessCreation();
119
0
}
120
121
void
122
nsImageLoadingContent::DestroyImageLoadingContent()
123
0
{
124
0
  // Cancel our requests so they won't hold stale refs to us
125
0
  // NB: Don't ask to discard the images here.
126
0
  ClearCurrentRequest(NS_BINDING_ABORTED);
127
0
  ClearPendingRequest(NS_BINDING_ABORTED);
128
0
}
129
130
nsImageLoadingContent::~nsImageLoadingContent()
131
0
{
132
0
  NS_ASSERTION(!mCurrentRequest && !mPendingRequest,
133
0
               "DestroyImageLoadingContent not called");
134
0
  NS_ASSERTION(!mObserverList.mObserver && !mObserverList.mNext,
135
0
               "Observers still registered?");
136
0
  NS_ASSERTION(mScriptedObservers.IsEmpty(),
137
0
               "Scripted observers still registered?");
138
0
}
139
140
/*
141
 * imgINotificationObserver impl
142
 */
143
NS_IMETHODIMP
144
nsImageLoadingContent::Notify(imgIRequest* aRequest,
145
                              int32_t aType,
146
                              const nsIntRect* aData)
147
0
{
148
0
  MOZ_ASSERT(aRequest, "no request?");
149
0
  MOZ_ASSERT(aRequest == mCurrentRequest || aRequest == mPendingRequest,
150
0
             "Forgot to cancel a previous request?");
151
0
152
0
  if (aType == imgINotificationObserver::IS_ANIMATED) {
153
0
    return OnImageIsAnimated(aRequest);
154
0
  }
155
0
156
0
  if (aType == imgINotificationObserver::UNLOCKED_DRAW) {
157
0
    OnUnlockedDraw();
158
0
    return NS_OK;
159
0
  }
160
0
161
0
  {
162
0
    // Calling Notify on observers can modify the list of observers so make
163
0
    // a local copy.
164
0
    AutoTArray<nsCOMPtr<imgINotificationObserver>, 2> observers;
165
0
    for (ImageObserver* observer = &mObserverList, *next; observer;
166
0
         observer = next) {
167
0
      next = observer->mNext;
168
0
      if (observer->mObserver) {
169
0
        observers.AppendElement(observer->mObserver);
170
0
      }
171
0
    }
172
0
173
0
    nsAutoScriptBlocker scriptBlocker;
174
0
175
0
    for (auto& observer : observers) {
176
0
        observer->Notify(aRequest, aType, aData);
177
0
    }
178
0
  }
179
0
180
0
  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
181
0
    // Have to check for state changes here, since we might have been in
182
0
    // the LOADING state before.
183
0
    UpdateImageState(true);
184
0
  }
185
0
186
0
  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
187
0
    uint32_t reqStatus;
188
0
    aRequest->GetImageStatus(&reqStatus);
189
0
    /* triage STATUS_ERROR */
190
0
    if (reqStatus & imgIRequest::STATUS_ERROR) {
191
0
      nsresult errorCode = NS_OK;
192
0
      aRequest->GetImageErrorCode(&errorCode);
193
0
194
0
      /* Handle image not loading error because source was a tracking URL.
195
0
       * We make a note of this image node by including it in a dedicated
196
0
       * array of blocked tracking nodes under its parent document.
197
0
       */
198
0
      if (errorCode == NS_ERROR_TRACKING_URI) {
199
0
        nsCOMPtr<nsIContent> thisNode
200
0
          = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
201
0
202
0
        nsIDocument *doc = GetOurOwnerDoc();
203
0
        doc->AddBlockedTrackingNode(thisNode);
204
0
      }
205
0
    }
206
0
    nsresult status =
207
0
        reqStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
208
0
    return OnLoadComplete(aRequest, status);
209
0
  }
210
0
211
0
  if (aType == imgINotificationObserver::DECODE_COMPLETE) {
212
0
    nsCOMPtr<imgIContainer> container;
213
0
    aRequest->GetImage(getter_AddRefs(container));
214
0
    if (container) {
215
0
      container->PropagateUseCounters(GetOurOwnerDoc());
216
0
    }
217
0
218
0
    UpdateImageState(true);
219
0
  }
220
0
221
0
  return NS_OK;
222
0
}
223
224
nsresult
225
nsImageLoadingContent::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus)
226
0
{
227
0
  uint32_t oldStatus;
228
0
  aRequest->GetImageStatus(&oldStatus);
229
0
230
0
  //XXXjdm This occurs when we have a pending request created, then another
231
0
  //       pending request replaces it before the first one is finished.
232
0
  //       This begs the question of what the correct behaviour is; we used
233
0
  //       to not have to care because we ran this code in OnStopDecode which
234
0
  //       wasn't called when the first request was cancelled. For now, I choose
235
0
  //       to punt when the given request doesn't appear to have terminated in
236
0
  //       an expected state.
237
0
  if (!(oldStatus & (imgIRequest::STATUS_ERROR | imgIRequest::STATUS_LOAD_COMPLETE)))
238
0
    return NS_OK;
239
0
240
0
  // Our state may change. Watch it.
241
0
  AutoStateChanger changer(this, true);
242
0
243
0
  // If the pending request is loaded, switch to it.
244
0
  if (aRequest == mPendingRequest) {
245
0
    MakePendingRequestCurrent();
246
0
  }
247
0
  MOZ_ASSERT(aRequest == mCurrentRequest,
248
0
             "One way or another, we should be current by now");
249
0
250
0
  // Fire the appropriate DOM event.
251
0
  if (NS_SUCCEEDED(aStatus)) {
252
0
    FireEvent(NS_LITERAL_STRING("load"));
253
0
254
0
    // Do not fire loadend event for multipart/x-mixed-replace image streams.
255
0
    bool isMultipart;
256
0
    if (NS_FAILED(aRequest->GetMultipart(&isMultipart)) || !isMultipart) {
257
0
      FireEvent(NS_LITERAL_STRING("loadend"));
258
0
    }
259
0
  } else {
260
0
    FireEvent(NS_LITERAL_STRING("error"));
261
0
    FireEvent(NS_LITERAL_STRING("loadend"));
262
0
  }
263
0
264
0
  nsCOMPtr<nsINode> thisNode = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
265
0
  SVGObserverUtils::InvalidateDirectRenderingObservers(thisNode->AsElement());
266
0
267
0
  return NS_OK;
268
0
}
269
270
static bool
271
ImageIsAnimated(imgIRequest* aRequest)
272
0
{
273
0
  if (!aRequest) {
274
0
    return false;
275
0
  }
276
0
277
0
  nsCOMPtr<imgIContainer> image;
278
0
  if (NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))) {
279
0
    bool isAnimated = false;
280
0
    nsresult rv = image->GetAnimated(&isAnimated);
281
0
    if (NS_SUCCEEDED(rv) && isAnimated) {
282
0
      return true;
283
0
    }
284
0
  }
285
0
286
0
  return false;
287
0
}
288
289
void
290
nsImageLoadingContent::OnUnlockedDraw()
291
0
{
292
0
  // It's OK for non-animated images to wait until the next frame visibility
293
0
  // update to become locked. (And that's preferable, since in the case of
294
0
  // scrolling it keeps memory usage minimal.) For animated images, though, we
295
0
  // want to mark them visible right away so we can call
296
0
  // IncrementAnimationConsumers() on them and they'll start animating.
297
0
  if (!ImageIsAnimated(mCurrentRequest) && !ImageIsAnimated(mPendingRequest)) {
298
0
    return;
299
0
  }
300
0
301
0
  nsIFrame* frame = GetOurPrimaryFrame();
302
0
  if (!frame) {
303
0
    return;
304
0
  }
305
0
306
0
  if (frame->GetVisibility() == Visibility::APPROXIMATELY_VISIBLE) {
307
0
    // This frame is already marked visible; there's nothing to do.
308
0
    return;
309
0
  }
310
0
311
0
  nsPresContext* presContext = frame->PresContext();
312
0
  if (!presContext) {
313
0
    return;
314
0
  }
315
0
316
0
  nsIPresShell* presShell = presContext->PresShell();
317
0
  if (!presShell) {
318
0
    return;
319
0
  }
320
0
321
0
  presShell->EnsureFrameInApproximatelyVisibleList(frame);
322
0
}
323
324
nsresult
325
nsImageLoadingContent::OnImageIsAnimated(imgIRequest *aRequest)
326
0
{
327
0
  bool* requestFlag = GetRegisteredFlagForRequest(aRequest);
328
0
  if (requestFlag) {
329
0
    nsLayoutUtils::RegisterImageRequest(GetFramePresContext(),
330
0
                                        aRequest, requestFlag);
331
0
  }
332
0
333
0
  return NS_OK;
334
0
}
335
336
/*
337
 * nsIImageLoadingContent impl
338
 */
339
340
void
341
nsImageLoadingContent::SetLoadingEnabled(bool aLoadingEnabled)
342
0
{
343
0
  if (nsContentUtils::GetImgLoaderForChannel(nullptr, nullptr)) {
344
0
    mLoadingEnabled = aLoadingEnabled;
345
0
  }
346
0
}
347
348
void
349
nsImageLoadingContent::SetSyncDecodingHint(bool aHint)
350
0
{
351
0
  if (mSyncDecodingHint == aHint) {
352
0
    return;
353
0
  }
354
0
355
0
  mSyncDecodingHint = aHint;
356
0
  MaybeForceSyncDecoding(/* aPrepareNextRequest */ false);
357
0
}
358
359
void
360
nsImageLoadingContent::MaybeForceSyncDecoding(bool aPrepareNextRequest,
361
                                              nsIFrame* aFrame /* = nullptr */)
362
0
{
363
0
  nsIFrame* frame = aFrame ? aFrame : GetOurPrimaryFrame();
364
0
  nsImageFrame* imageFrame = do_QueryFrame(frame);
365
0
  nsSVGImageFrame* svgImageFrame = do_QueryFrame(frame);
366
0
  if (!imageFrame && !svgImageFrame) {
367
0
    return;
368
0
  }
369
0
370
0
  bool forceSync = mSyncDecodingHint;
371
0
  if (!forceSync && aPrepareNextRequest) {
372
0
    // Detect JavaScript-based animations created by changing the |src|
373
0
    // attribute on a timer.
374
0
    TimeStamp now = TimeStamp::Now();
375
0
    TimeDuration threshold =
376
0
      TimeDuration::FromMilliseconds(
377
0
        gfxPrefs::ImageInferSrcAnimationThresholdMS());
378
0
379
0
    // If the length of time between request changes is less than the threshold,
380
0
    // then force sync decoding to eliminate flicker from the animation.
381
0
    forceSync = (now - mMostRecentRequestChange < threshold);
382
0
    mMostRecentRequestChange = now;
383
0
  }
384
0
385
0
  if (imageFrame) {
386
0
    imageFrame->SetForceSyncDecoding(forceSync);
387
0
  } else {
388
0
    svgImageFrame->SetForceSyncDecoding(forceSync);
389
0
  }
390
0
}
391
392
NS_IMETHODIMP
393
nsImageLoadingContent::GetImageBlockingStatus(int16_t* aStatus)
394
0
{
395
0
  MOZ_ASSERT(aStatus, "Null out param");
396
0
  *aStatus = ImageBlockingStatus();
397
0
  return NS_OK;
398
0
}
399
400
static void
401
ReplayImageStatus(imgIRequest* aRequest, imgINotificationObserver* aObserver)
402
0
{
403
0
  if (!aRequest) {
404
0
    return;
405
0
  }
406
0
407
0
  uint32_t status = 0;
408
0
  nsresult rv = aRequest->GetImageStatus(&status);
409
0
  if (NS_FAILED(rv)) {
410
0
    return;
411
0
  }
412
0
413
0
  if (status & imgIRequest::STATUS_SIZE_AVAILABLE) {
414
0
    aObserver->Notify(aRequest, imgINotificationObserver::SIZE_AVAILABLE, nullptr);
415
0
  }
416
0
  if (status & imgIRequest::STATUS_FRAME_COMPLETE) {
417
0
    aObserver->Notify(aRequest, imgINotificationObserver::FRAME_COMPLETE, nullptr);
418
0
  }
419
0
  if (status & imgIRequest::STATUS_HAS_TRANSPARENCY) {
420
0
    aObserver->Notify(aRequest, imgINotificationObserver::HAS_TRANSPARENCY, nullptr);
421
0
  }
422
0
  if (status & imgIRequest::STATUS_IS_ANIMATED) {
423
0
    aObserver->Notify(aRequest, imgINotificationObserver::IS_ANIMATED, nullptr);
424
0
  }
425
0
  if (status & imgIRequest::STATUS_DECODE_COMPLETE) {
426
0
    aObserver->Notify(aRequest, imgINotificationObserver::DECODE_COMPLETE, nullptr);
427
0
  }
428
0
  if (status & imgIRequest::STATUS_LOAD_COMPLETE) {
429
0
    aObserver->Notify(aRequest, imgINotificationObserver::LOAD_COMPLETE, nullptr);
430
0
  }
431
0
}
432
433
void
434
nsImageLoadingContent::AddNativeObserver(imgINotificationObserver* aObserver)
435
0
{
436
0
  if (NS_WARN_IF(!aObserver)) {
437
0
    return;
438
0
  }
439
0
440
0
  if (!mObserverList.mObserver) {
441
0
    // Don't touch the linking of the list!
442
0
    mObserverList.mObserver = aObserver;
443
0
444
0
    ReplayImageStatus(mCurrentRequest, aObserver);
445
0
    ReplayImageStatus(mPendingRequest, aObserver);
446
0
447
0
    return;
448
0
  }
449
0
450
0
  // otherwise we have to create a new entry
451
0
452
0
  ImageObserver* observer = &mObserverList;
453
0
  while (observer->mNext) {
454
0
    observer = observer->mNext;
455
0
  }
456
0
457
0
  observer->mNext = new ImageObserver(aObserver);
458
0
  ReplayImageStatus(mCurrentRequest, aObserver);
459
0
  ReplayImageStatus(mPendingRequest, aObserver);
460
0
}
461
462
void
463
nsImageLoadingContent::RemoveNativeObserver(imgINotificationObserver* aObserver)
464
0
{
465
0
  if (NS_WARN_IF(!aObserver)) {
466
0
    return;
467
0
  }
468
0
469
0
  if (mObserverList.mObserver == aObserver) {
470
0
    mObserverList.mObserver = nullptr;
471
0
    // Don't touch the linking of the list!
472
0
    return;
473
0
  }
474
0
475
0
  // otherwise have to find it and splice it out
476
0
  ImageObserver* observer = &mObserverList;
477
0
  while (observer->mNext && observer->mNext->mObserver != aObserver) {
478
0
    observer = observer->mNext;
479
0
  }
480
0
481
0
  // At this point, we are pointing to the list element whose mNext is
482
0
  // the right observer (assuming of course that mNext is not null)
483
0
  if (observer->mNext) {
484
0
    // splice it out
485
0
    ImageObserver* oldObserver = observer->mNext;
486
0
    observer->mNext = oldObserver->mNext;
487
0
    oldObserver->mNext = nullptr;  // so we don't destroy them all
488
0
    delete oldObserver;
489
0
  }
490
#ifdef DEBUG
491
  else {
492
    NS_WARNING("Asked to remove nonexistent observer");
493
  }
494
#endif
495
}
496
497
void
498
nsImageLoadingContent::AddObserver(imgINotificationObserver* aObserver)
499
0
{
500
0
  if (NS_WARN_IF(!aObserver)) {
501
0
    return;
502
0
  }
503
0
504
0
  RefPtr<imgRequestProxy> currentReq;
505
0
  if (mCurrentRequest) {
506
0
    // Scripted observers may not belong to the same document as us, so when we
507
0
    // create the imgRequestProxy, we shouldn't use any. This allows the request
508
0
    // to dispatch notifications from the correct scheduler group.
509
0
    nsresult rv = mCurrentRequest->Clone(aObserver, nullptr, getter_AddRefs(currentReq));
510
0
    if (NS_FAILED(rv)) {
511
0
      return;
512
0
    }
513
0
  }
514
0
515
0
  RefPtr<imgRequestProxy> pendingReq;
516
0
  if (mPendingRequest) {
517
0
    // See above for why we don't use the loading document.
518
0
    nsresult rv = mPendingRequest->Clone(aObserver, nullptr, getter_AddRefs(pendingReq));
519
0
    if (NS_FAILED(rv)) {
520
0
      mCurrentRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
521
0
      return;
522
0
    }
523
0
  }
524
0
525
0
  mScriptedObservers.AppendElement(
526
0
    new ScriptedImageObserver(aObserver, std::move(currentReq), std::move(pendingReq)));
527
0
}
528
529
void
530
nsImageLoadingContent::RemoveObserver(imgINotificationObserver* aObserver)
531
0
{
532
0
  if (NS_WARN_IF(!aObserver)) {
533
0
    return;
534
0
  }
535
0
536
0
  if (NS_WARN_IF(mScriptedObservers.IsEmpty())) {
537
0
    return;
538
0
  }
539
0
540
0
  RefPtr<ScriptedImageObserver> observer;
541
0
  auto i = mScriptedObservers.Length();
542
0
  do {
543
0
    --i;
544
0
    if (mScriptedObservers[i]->mObserver == aObserver) {
545
0
      observer = std::move(mScriptedObservers[i]);
546
0
      mScriptedObservers.RemoveElementAt(i);
547
0
      break;
548
0
    }
549
0
  } while(i > 0);
550
0
551
0
  if (NS_WARN_IF(!observer)) {
552
0
    return;
553
0
  }
554
0
555
0
  // If the cancel causes a mutation, it will be harmless, because we have
556
0
  // already removed the observer from the list.
557
0
  observer->CancelRequests();
558
0
}
559
560
void
561
nsImageLoadingContent::ClearScriptedRequests(int32_t aRequestType, nsresult aReason)
562
0
{
563
0
  if (MOZ_LIKELY(mScriptedObservers.IsEmpty())) {
564
0
    return;
565
0
  }
566
0
567
0
  nsTArray<RefPtr<ScriptedImageObserver>> observers(mScriptedObservers);
568
0
  auto i = observers.Length();
569
0
  do {
570
0
    --i;
571
0
572
0
    RefPtr<imgRequestProxy> req;
573
0
    switch (aRequestType) {
574
0
    case CURRENT_REQUEST:
575
0
      req = std::move(observers[i]->mCurrentRequest);
576
0
      break;
577
0
    case PENDING_REQUEST:
578
0
      req = std::move(observers[i]->mPendingRequest);
579
0
      break;
580
0
    default:
581
0
      NS_ERROR("Unknown request type");
582
0
      return;
583
0
    }
584
0
585
0
    if (req) {
586
0
      req->CancelAndForgetObserver(aReason);
587
0
    }
588
0
  } while (i > 0);
589
0
}
590
591
void
592
nsImageLoadingContent::CloneScriptedRequests(imgRequestProxy* aRequest)
593
0
{
594
0
  MOZ_ASSERT(aRequest);
595
0
596
0
  if (MOZ_LIKELY(mScriptedObservers.IsEmpty())) {
597
0
    return;
598
0
  }
599
0
600
0
  bool current;
601
0
  if (aRequest == mCurrentRequest) {
602
0
    current = true;
603
0
  } else if (aRequest == mPendingRequest) {
604
0
    current = false;
605
0
  } else {
606
0
    MOZ_ASSERT_UNREACHABLE("Unknown request type");
607
0
    return;
608
0
  }
609
0
610
0
  nsTArray<RefPtr<ScriptedImageObserver>> observers(mScriptedObservers);
611
0
  auto i = observers.Length();
612
0
  do {
613
0
    --i;
614
0
615
0
    ScriptedImageObserver* observer = observers[i];
616
0
    RefPtr<imgRequestProxy>& req = current ? observer->mCurrentRequest
617
0
                                           : observer->mPendingRequest;
618
0
    if (NS_WARN_IF(req)) {
619
0
      MOZ_ASSERT_UNREACHABLE("Should have cancelled original request");
620
0
      req->CancelAndForgetObserver(NS_BINDING_ABORTED);
621
0
      req = nullptr;
622
0
    }
623
0
624
0
    nsresult rv = aRequest->Clone(observer->mObserver, nullptr, getter_AddRefs(req));
625
0
    Unused << NS_WARN_IF(NS_FAILED(rv));
626
0
  } while (i > 0);
627
0
}
628
629
void
630
nsImageLoadingContent::MakePendingScriptedRequestsCurrent()
631
0
{
632
0
  if (MOZ_LIKELY(mScriptedObservers.IsEmpty())) {
633
0
    return;
634
0
  }
635
0
636
0
  nsTArray<RefPtr<ScriptedImageObserver>> observers(mScriptedObservers);
637
0
  auto i = observers.Length();
638
0
  do {
639
0
    --i;
640
0
641
0
    ScriptedImageObserver* observer = observers[i];
642
0
    if (observer->mCurrentRequest) {
643
0
      observer->mCurrentRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
644
0
    }
645
0
    observer->mCurrentRequest = std::move(observer->mPendingRequest);
646
0
  } while (i > 0);
647
0
}
648
649
already_AddRefed<imgIRequest>
650
nsImageLoadingContent::GetRequest(int32_t aRequestType,
651
                                  ErrorResult& aError)
652
0
{
653
0
  nsCOMPtr<imgIRequest> request;
654
0
  switch(aRequestType) {
655
0
  case CURRENT_REQUEST:
656
0
    request = mCurrentRequest;
657
0
    break;
658
0
  case PENDING_REQUEST:
659
0
    request = mPendingRequest;
660
0
    break;
661
0
  default:
662
0
    NS_ERROR("Unknown request type");
663
0
    aError.Throw(NS_ERROR_UNEXPECTED);
664
0
  }
665
0
666
0
  return request.forget();
667
0
}
668
669
NS_IMETHODIMP
670
nsImageLoadingContent::GetRequest(int32_t aRequestType,
671
                                  imgIRequest** aRequest)
672
0
{
673
0
  NS_ENSURE_ARG_POINTER(aRequest);
674
0
675
0
  ErrorResult result;
676
0
  *aRequest = GetRequest(aRequestType, result).take();
677
0
678
0
  return result.StealNSResult();
679
0
}
680
681
NS_IMETHODIMP_(void)
682
nsImageLoadingContent::FrameCreated(nsIFrame* aFrame)
683
0
{
684
0
  NS_ASSERTION(aFrame, "aFrame is null");
685
0
686
0
  MaybeForceSyncDecoding(/* aPrepareNextRequest */ false, aFrame);
687
0
  TrackImage(mCurrentRequest, aFrame);
688
0
  TrackImage(mPendingRequest, aFrame);
689
0
690
0
  // We need to make sure that our image request is registered, if it should
691
0
  // be registered.
692
0
  nsPresContext* presContext = aFrame->PresContext();
693
0
  if (mCurrentRequest) {
694
0
    nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mCurrentRequest,
695
0
                                                  &mCurrentRequestRegistered);
696
0
  }
697
0
698
0
  if (mPendingRequest) {
699
0
    nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mPendingRequest,
700
0
                                                  &mPendingRequestRegistered);
701
0
  }
702
0
}
703
704
NS_IMETHODIMP_(void)
705
nsImageLoadingContent::FrameDestroyed(nsIFrame* aFrame)
706
0
{
707
0
  NS_ASSERTION(aFrame, "aFrame is null");
708
0
709
0
  // We need to make sure that our image request is deregistered.
710
0
  nsPresContext* presContext = GetFramePresContext();
711
0
  if (mCurrentRequest) {
712
0
    nsLayoutUtils::DeregisterImageRequest(presContext,
713
0
                                          mCurrentRequest,
714
0
                                          &mCurrentRequestRegistered);
715
0
  }
716
0
717
0
  if (mPendingRequest) {
718
0
    nsLayoutUtils::DeregisterImageRequest(presContext,
719
0
                                          mPendingRequest,
720
0
                                          &mPendingRequestRegistered);
721
0
  }
722
0
723
0
  UntrackImage(mCurrentRequest);
724
0
  UntrackImage(mPendingRequest);
725
0
726
0
  nsIPresShell* presShell = presContext ? presContext->GetPresShell() : nullptr;
727
0
  if (presShell) {
728
0
    presShell->RemoveFrameFromApproximatelyVisibleList(aFrame);
729
0
  }
730
0
}
731
732
/* static */
733
nsContentPolicyType
734
nsImageLoadingContent::PolicyTypeForLoad(ImageLoadType aImageLoadType)
735
0
{
736
0
  if (aImageLoadType == eImageLoadType_Imageset) {
737
0
    return nsIContentPolicy::TYPE_IMAGESET;
738
0
  }
739
0
740
0
  MOZ_ASSERT(aImageLoadType == eImageLoadType_Normal,
741
0
             "Unknown ImageLoadType type in PolicyTypeForLoad");
742
0
  return nsIContentPolicy::TYPE_INTERNAL_IMAGE;
743
0
}
744
745
int32_t
746
nsImageLoadingContent::GetRequestType(imgIRequest* aRequest,
747
                                      ErrorResult& aError)
748
0
{
749
0
  if (aRequest == mCurrentRequest) {
750
0
    return CURRENT_REQUEST;
751
0
  }
752
0
753
0
  if (aRequest == mPendingRequest) {
754
0
    return PENDING_REQUEST;
755
0
  }
756
0
757
0
  NS_ERROR("Unknown request");
758
0
  aError.Throw(NS_ERROR_UNEXPECTED);
759
0
  return UNKNOWN_REQUEST;
760
0
}
761
762
NS_IMETHODIMP
763
nsImageLoadingContent::GetRequestType(imgIRequest* aRequest,
764
                                      int32_t* aRequestType)
765
0
{
766
0
  MOZ_ASSERT(aRequestType, "Null out param");
767
0
768
0
  ErrorResult result;
769
0
  *aRequestType = GetRequestType(aRequest, result);
770
0
  return result.StealNSResult();
771
0
}
772
773
already_AddRefed<nsIURI>
774
nsImageLoadingContent::GetCurrentURI(ErrorResult& aError)
775
0
{
776
0
  nsCOMPtr<nsIURI> uri;
777
0
  if (mCurrentRequest) {
778
0
    mCurrentRequest->GetURI(getter_AddRefs(uri));
779
0
  } else {
780
0
    uri = mCurrentURI;
781
0
  }
782
0
783
0
  return uri.forget();
784
0
}
785
786
NS_IMETHODIMP
787
nsImageLoadingContent::GetCurrentURI(nsIURI** aURI)
788
0
{
789
0
  NS_ENSURE_ARG_POINTER(aURI);
790
0
791
0
  ErrorResult result;
792
0
  *aURI = GetCurrentURI(result).take();
793
0
  return result.StealNSResult();
794
0
}
795
796
already_AddRefed<nsIURI>
797
nsImageLoadingContent::GetCurrentRequestFinalURI()
798
0
{
799
0
  nsCOMPtr<nsIURI> uri;
800
0
  if (mCurrentRequest) {
801
0
    mCurrentRequest->GetFinalURI(getter_AddRefs(uri));
802
0
  }
803
0
804
0
  return uri.forget();
805
0
}
806
807
NS_IMETHODIMP
808
nsImageLoadingContent::LoadImageWithChannel(nsIChannel* aChannel,
809
                                            nsIStreamListener** aListener)
810
0
{
811
0
  imgLoader* loader =
812
0
    nsContentUtils::GetImgLoaderForChannel(aChannel, GetOurOwnerDoc());
813
0
  if (!loader) {
814
0
    return NS_ERROR_NULL_POINTER;
815
0
  }
816
0
817
0
  nsCOMPtr<nsIDocument> doc = GetOurOwnerDoc();
818
0
  if (!doc) {
819
0
    // Don't bother
820
0
    *aListener = nullptr;
821
0
    return NS_OK;
822
0
  }
823
0
824
0
  // XXX what should we do with content policies here, if anything?
825
0
  // Shouldn't that be done before the start of the load?
826
0
  // XXX what about shouldProcess?
827
0
828
0
  // Our state might change. Watch it.
829
0
  AutoStateChanger changer(this, true);
830
0
831
0
  // Do the load.
832
0
  RefPtr<imgRequestProxy>& req = PrepareNextRequest(eImageLoadType_Normal);
833
0
  nsresult rv = loader->
834
0
    LoadImageWithChannel(aChannel, this, doc, aListener, getter_AddRefs(req));
835
0
  if (NS_SUCCEEDED(rv)) {
836
0
    CloneScriptedRequests(req);
837
0
    TrackImage(req);
838
0
    ResetAnimationIfNeeded();
839
0
    return NS_OK;
840
0
  }
841
0
842
0
  MOZ_ASSERT(!req, "Shouldn't have non-null request here");
843
0
  // If we don't have a current URI, we might as well store this URI so people
844
0
  // know what we tried (and failed) to load.
845
0
  if (!mCurrentRequest)
846
0
    aChannel->GetURI(getter_AddRefs(mCurrentURI));
847
0
848
0
  FireEvent(NS_LITERAL_STRING("error"));
849
0
  FireEvent(NS_LITERAL_STRING("loadend"));
850
0
  return rv;
851
0
}
852
853
void
854
nsImageLoadingContent::ForceReload(bool aNotify, ErrorResult& aError)
855
0
{
856
0
  nsCOMPtr<nsIURI> currentURI;
857
0
  GetCurrentURI(getter_AddRefs(currentURI));
858
0
  if (!currentURI) {
859
0
    aError.Throw(NS_ERROR_NOT_AVAILABLE);
860
0
    return;
861
0
  }
862
0
863
0
  // We keep this flag around along with the old URI even for failed requests
864
0
  // without a live request object
865
0
  ImageLoadType loadType = \
866
0
    (mCurrentRequestFlags & REQUEST_IS_IMAGESET) ? eImageLoadType_Imageset
867
0
                                                 : eImageLoadType_Normal;
868
0
  nsresult rv = LoadImage(currentURI, true, aNotify, loadType, true, nullptr,
869
0
                          nsIRequest::VALIDATE_ALWAYS);
870
0
  if (NS_FAILED(rv)) {
871
0
    aError.Throw(rv);
872
0
  }
873
0
}
874
875
/*
876
 * Non-interface methods
877
 */
878
879
nsresult
880
nsImageLoadingContent::LoadImage(const nsAString& aNewURI,
881
                                 bool aForce,
882
                                 bool aNotify,
883
                                 ImageLoadType aImageLoadType,
884
                                 nsIPrincipal* aTriggeringPrincipal)
885
0
{
886
0
  // First, get a document (needed for security checks and the like)
887
0
  nsIDocument* doc = GetOurOwnerDoc();
888
0
  if (!doc) {
889
0
    // No reason to bother, I think...
890
0
    return NS_OK;
891
0
  }
892
0
893
0
  // Pending load/error events need to be canceled in some situations. This
894
0
  // is not documented in the spec, but can cause site compat problems if not
895
0
  // done. See bug 1309461 and https://github.com/whatwg/html/issues/1872.
896
0
  CancelPendingEvent();
897
0
898
0
  if (aNewURI.IsEmpty()) {
899
0
    // Cancel image requests and then fire only error event per spec.
900
0
    CancelImageRequests(aNotify);
901
0
    // Mark error event as cancelable only for src="" case, since only this
902
0
    // error causes site compat problem (bug 1308069) for now.
903
0
    FireEvent(NS_LITERAL_STRING("error"), true);
904
0
    return NS_OK;
905
0
  }
906
0
907
0
  // Fire loadstart event
908
0
  FireEvent(NS_LITERAL_STRING("loadstart"));
909
0
910
0
  // Parse the URI string to get image URI
911
0
  nsCOMPtr<nsIURI> imageURI;
912
0
  nsresult rv = StringToURI(aNewURI, doc, getter_AddRefs(imageURI));
913
0
  NS_ENSURE_SUCCESS(rv, rv);
914
0
  // XXXbiesi fire onerror if that failed?
915
0
916
0
  return LoadImage(imageURI, aForce, aNotify, aImageLoadType, false, doc,
917
0
                   nsIRequest::LOAD_NORMAL, aTriggeringPrincipal);
918
0
}
919
920
nsresult
921
nsImageLoadingContent::LoadImage(nsIURI* aNewURI,
922
                                 bool aForce,
923
                                 bool aNotify,
924
                                 ImageLoadType aImageLoadType,
925
                                 bool aLoadStart,
926
                                 nsIDocument* aDocument,
927
                                 nsLoadFlags aLoadFlags,
928
                                 nsIPrincipal* aTriggeringPrincipal)
929
0
{
930
0
  MOZ_ASSERT(!mIsStartingImageLoad, "some evil code is reentering LoadImage.");
931
0
  if (mIsStartingImageLoad) {
932
0
    return NS_OK;
933
0
  }
934
0
935
0
  // Pending load/error events need to be canceled in some situations. This
936
0
  // is not documented in the spec, but can cause site compat problems if not
937
0
  // done. See bug 1309461 and https://github.com/whatwg/html/issues/1872.
938
0
  CancelPendingEvent();
939
0
940
0
  // Fire loadstart event if required
941
0
  if (aLoadStart) {
942
0
    FireEvent(NS_LITERAL_STRING("loadstart"));
943
0
  }
944
0
945
0
  if (!mLoadingEnabled) {
946
0
    // XXX Why fire an error here? seems like the callers to SetLoadingEnabled
947
0
    // don't want/need it.
948
0
    FireEvent(NS_LITERAL_STRING("error"));
949
0
    FireEvent(NS_LITERAL_STRING("loadend"));
950
0
    return NS_OK;
951
0
  }
952
0
953
0
  NS_ASSERTION(!aDocument || aDocument == GetOurOwnerDoc(),
954
0
               "Bogus document passed in");
955
0
  // First, get a document (needed for security checks and the like)
956
0
  if (!aDocument) {
957
0
    aDocument = GetOurOwnerDoc();
958
0
    if (!aDocument) {
959
0
      // No reason to bother, I think...
960
0
      return NS_OK;
961
0
    }
962
0
  }
963
0
964
0
  AutoRestore<bool> guard(mIsStartingImageLoad);
965
0
  mIsStartingImageLoad = true;
966
0
967
0
  // Data documents, or documents from DOMParser shouldn't perform image loading.
968
0
  if (aDocument->IsLoadedAsData()) {
969
0
    // This is the only codepath on which we can reach SetBlockedRequest while
970
0
    // our pending request exists.  Just clear it out here if we do have one.
971
0
    ClearPendingRequest(NS_BINDING_ABORTED,
972
0
                        Some(OnNonvisible::DISCARD_IMAGES));
973
0
974
0
    SetBlockedRequest(nsIContentPolicy::REJECT_REQUEST);
975
0
976
0
    FireEvent(NS_LITERAL_STRING("error"));
977
0
    FireEvent(NS_LITERAL_STRING("loadend"));
978
0
    return NS_OK;
979
0
  }
980
0
981
0
  // URI equality check.
982
0
  //
983
0
  // We skip the equality check if our current image was blocked, since in that
984
0
  // case we really do want to try loading again.
985
0
  if (!aForce && NS_CP_ACCEPTED(mImageBlockingStatus)) {
986
0
    nsCOMPtr<nsIURI> currentURI;
987
0
    GetCurrentURI(getter_AddRefs(currentURI));
988
0
    bool equal;
989
0
    if (currentURI &&
990
0
        NS_SUCCEEDED(currentURI->Equals(aNewURI, &equal)) &&
991
0
        equal) {
992
0
      // Nothing to do here.
993
0
      return NS_OK;
994
0
    }
995
0
  }
996
0
997
0
  // From this point on, our image state could change. Watch it.
998
0
  AutoStateChanger changer(this, aNotify);
999
0
1000
0
  // Sanity check.
1001
0
  //
1002
0
  // We use the principal of aDocument to avoid having to QI |this| an extra
1003
0
  // time. It should always be the same as the principal of this node.
1004
#ifdef DEBUG
1005
  nsIContent* thisContent = AsContent();
1006
  MOZ_ASSERT(thisContent->NodePrincipal() == aDocument->NodePrincipal(),
1007
             "Principal mismatch?");
1008
#endif
1009
1010
0
  nsLoadFlags loadFlags = aLoadFlags |
1011
0
                          nsContentUtils::CORSModeToLoadImageFlags(
1012
0
                            GetCORSMode());
1013
0
1014
0
  // get document wide referrer policy
1015
0
  // if referrer attributes are enabled in preferences, load img referrer attribute
1016
0
  // if the image does not provide a referrer attribute, ignore this
1017
0
  net::ReferrerPolicy referrerPolicy = aDocument->GetReferrerPolicy();
1018
0
  net::ReferrerPolicy imgReferrerPolicy = GetImageReferrerPolicy();
1019
0
  if (imgReferrerPolicy != net::RP_Unset) {
1020
0
    referrerPolicy = imgReferrerPolicy;
1021
0
  }
1022
0
1023
0
  RefPtr<imgRequestProxy>& req = PrepareNextRequest(aImageLoadType);
1024
0
  nsCOMPtr<nsIContent> content =
1025
0
      do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1026
0
1027
0
  nsCOMPtr<nsIPrincipal> triggeringPrincipal;
1028
0
  bool result =
1029
0
    nsContentUtils::QueryTriggeringPrincipal(content, aTriggeringPrincipal,
1030
0
                                             getter_AddRefs(triggeringPrincipal));
1031
0
1032
0
  // If result is true, which means this node has specified 'triggeringprincipal'
1033
0
  // attribute on it, so we use favicon as the policy type.
1034
0
  nsContentPolicyType policyType = result ?
1035
0
                                     nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON:
1036
0
                                     PolicyTypeForLoad(aImageLoadType);
1037
0
1038
0
  nsCOMPtr<nsINode> thisNode =
1039
0
    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1040
0
  nsresult rv = nsContentUtils::LoadImage(aNewURI,
1041
0
                                          thisNode,
1042
0
                                          aDocument,
1043
0
                                          triggeringPrincipal,
1044
0
                                          0,
1045
0
                                          aDocument->GetDocumentURI(),
1046
0
                                          referrerPolicy,
1047
0
                                          this, loadFlags,
1048
0
                                          content->LocalName(),
1049
0
                                          getter_AddRefs(req),
1050
0
                                          policyType,
1051
0
                                          mUseUrgentStartForChannel);
1052
0
1053
0
  // Reset the flag to avoid loading from XPCOM or somewhere again else without
1054
0
  // initiated by user interaction.
1055
0
  mUseUrgentStartForChannel = false;
1056
0
1057
0
  // Tell the document to forget about the image preload, if any, for
1058
0
  // this URI, now that we might have another imgRequestProxy for it.
1059
0
  // That way if we get canceled later the image load won't continue.
1060
0
  aDocument->ForgetImagePreload(aNewURI);
1061
0
1062
0
  if (NS_SUCCEEDED(rv)) {
1063
0
    CloneScriptedRequests(req);
1064
0
    TrackImage(req);
1065
0
    ResetAnimationIfNeeded();
1066
0
1067
0
    // Handle cases when we just ended up with a pending request but it's
1068
0
    // already done.  In that situation we have to synchronously switch that
1069
0
    // request to being the current request, because websites depend on that
1070
0
    // behavior.
1071
0
    if (req == mPendingRequest) {
1072
0
      uint32_t pendingLoadStatus;
1073
0
      rv = req->GetImageStatus(&pendingLoadStatus);
1074
0
      if (NS_SUCCEEDED(rv) &&
1075
0
          (pendingLoadStatus & imgIRequest::STATUS_LOAD_COMPLETE)) {
1076
0
        MakePendingRequestCurrent();
1077
0
        MOZ_ASSERT(mCurrentRequest,
1078
0
                   "How could we not have a current request here?");
1079
0
1080
0
        nsImageFrame *f = do_QueryFrame(GetOurPrimaryFrame());
1081
0
        if (f) {
1082
0
          f->NotifyNewCurrentRequest(mCurrentRequest, NS_OK);
1083
0
        }
1084
0
      }
1085
0
    }
1086
0
  } else {
1087
0
    MOZ_ASSERT(!req, "Shouldn't have non-null request here");
1088
0
    // If we don't have a current URI, we might as well store this URI so people
1089
0
    // know what we tried (and failed) to load.
1090
0
    if (!mCurrentRequest)
1091
0
      mCurrentURI = aNewURI;
1092
0
1093
0
    FireEvent(NS_LITERAL_STRING("error"));
1094
0
    FireEvent(NS_LITERAL_STRING("loadend"));
1095
0
  }
1096
0
1097
0
  return NS_OK;
1098
0
}
1099
1100
void
1101
nsImageLoadingContent::ForceImageState(bool aForce,
1102
                                       EventStates::InternalType aState)
1103
0
{
1104
0
  mIsImageStateForced = aForce;
1105
0
  mForcedImageState = EventStates(aState);
1106
0
}
1107
1108
NS_IMETHODIMP
1109
nsImageLoadingContent::GetNaturalWidth(uint32_t* aNaturalWidth)
1110
0
{
1111
0
  NS_ENSURE_ARG_POINTER(aNaturalWidth);
1112
0
1113
0
  nsCOMPtr<imgIContainer> image;
1114
0
  if (mCurrentRequest) {
1115
0
    mCurrentRequest->GetImage(getter_AddRefs(image));
1116
0
  }
1117
0
1118
0
  int32_t width;
1119
0
  if (image && NS_SUCCEEDED(image->GetWidth(&width))) {
1120
0
    *aNaturalWidth = width;
1121
0
  } else {
1122
0
    *aNaturalWidth = 0;
1123
0
  }
1124
0
1125
0
  return NS_OK;
1126
0
}
1127
1128
NS_IMETHODIMP
1129
nsImageLoadingContent::GetNaturalHeight(uint32_t* aNaturalHeight)
1130
0
{
1131
0
  NS_ENSURE_ARG_POINTER(aNaturalHeight);
1132
0
1133
0
  nsCOMPtr<imgIContainer> image;
1134
0
  if (mCurrentRequest) {
1135
0
    mCurrentRequest->GetImage(getter_AddRefs(image));
1136
0
  }
1137
0
1138
0
  int32_t height;
1139
0
  if (image && NS_SUCCEEDED(image->GetHeight(&height))) {
1140
0
    *aNaturalHeight = height;
1141
0
  } else {
1142
0
    *aNaturalHeight = 0;
1143
0
  }
1144
0
1145
0
  return NS_OK;
1146
0
}
1147
1148
EventStates
1149
nsImageLoadingContent::ImageState() const
1150
0
{
1151
0
  if (mIsImageStateForced) {
1152
0
    return mForcedImageState;
1153
0
  }
1154
0
1155
0
  EventStates states;
1156
0
1157
0
  if (mBroken) {
1158
0
    states |= NS_EVENT_STATE_BROKEN;
1159
0
  }
1160
0
  if (mUserDisabled) {
1161
0
    states |= NS_EVENT_STATE_USERDISABLED;
1162
0
  }
1163
0
  if (mSuppressed) {
1164
0
    states |= NS_EVENT_STATE_SUPPRESSED;
1165
0
  }
1166
0
  if (mLoading) {
1167
0
    states |= NS_EVENT_STATE_LOADING;
1168
0
  }
1169
0
1170
0
  return states;
1171
0
}
1172
1173
void
1174
nsImageLoadingContent::UpdateImageState(bool aNotify)
1175
0
{
1176
0
  if (mStateChangerDepth > 0) {
1177
0
    // Ignore this call; we'll update our state when the outermost state changer
1178
0
    // is destroyed. Need this to work around the fact that some ImageLib
1179
0
    // stuff is actually sync and hence we can get OnStopDecode called while
1180
0
    // we're still under LoadImage, and OnStopDecode doesn't know anything about
1181
0
    // aNotify.
1182
0
    // XXX - This machinery should be removed after bug 521604.
1183
0
    return;
1184
0
  }
1185
0
1186
0
  nsIContent* thisContent = AsContent();
1187
0
1188
0
  mLoading = mBroken = mUserDisabled = mSuppressed = false;
1189
0
1190
0
  // If we were blocked by server-based content policy, we claim to be
1191
0
  // suppressed.  If we were blocked by type-based content policy, we claim to
1192
0
  // be user-disabled.  Otherwise, claim to be broken.
1193
0
  if (mImageBlockingStatus == nsIContentPolicy::REJECT_SERVER) {
1194
0
    mSuppressed = true;
1195
0
  } else if (mImageBlockingStatus == nsIContentPolicy::REJECT_TYPE) {
1196
0
    mUserDisabled = true;
1197
0
  } else if (!mCurrentRequest) {
1198
0
    // No current request means error, since we weren't disabled or suppressed
1199
0
    mBroken = true;
1200
0
  } else {
1201
0
    uint32_t currentLoadStatus;
1202
0
    nsresult rv = mCurrentRequest->GetImageStatus(&currentLoadStatus);
1203
0
    if (NS_FAILED(rv) || (currentLoadStatus & imgIRequest::STATUS_ERROR)) {
1204
0
      mBroken = true;
1205
0
    } else if (!(currentLoadStatus & imgIRequest::STATUS_SIZE_AVAILABLE)) {
1206
0
      mLoading = true;
1207
0
    }
1208
0
  }
1209
0
1210
0
  NS_ASSERTION(thisContent->IsElement(), "Not an element?");
1211
0
  thisContent->AsElement()->UpdateState(aNotify);
1212
0
}
1213
1214
void
1215
nsImageLoadingContent::CancelImageRequests(bool aNotify)
1216
0
{
1217
0
  AutoStateChanger changer(this, aNotify);
1218
0
  ClearPendingRequest(NS_BINDING_ABORTED, Some(OnNonvisible::DISCARD_IMAGES));
1219
0
  ClearCurrentRequest(NS_BINDING_ABORTED, Some(OnNonvisible::DISCARD_IMAGES));
1220
0
}
1221
1222
nsIDocument*
1223
nsImageLoadingContent::GetOurOwnerDoc()
1224
0
{
1225
0
  return AsContent()->OwnerDoc();
1226
0
}
1227
1228
nsIDocument*
1229
nsImageLoadingContent::GetOurCurrentDoc()
1230
0
{
1231
0
  return AsContent()->GetComposedDoc();
1232
0
}
1233
1234
nsIFrame*
1235
nsImageLoadingContent::GetOurPrimaryFrame()
1236
0
{
1237
0
  return AsContent()->GetPrimaryFrame();
1238
0
}
1239
1240
nsPresContext* nsImageLoadingContent::GetFramePresContext()
1241
0
{
1242
0
  nsIFrame* frame = GetOurPrimaryFrame();
1243
0
  if (!frame) {
1244
0
    return nullptr;
1245
0
  }
1246
0
1247
0
  return frame->PresContext();
1248
0
}
1249
1250
nsresult
1251
nsImageLoadingContent::StringToURI(const nsAString& aSpec,
1252
                                   nsIDocument* aDocument,
1253
                                   nsIURI** aURI)
1254
0
{
1255
0
  MOZ_ASSERT(aDocument, "Must have a document");
1256
0
  MOZ_ASSERT(aURI, "Null out param");
1257
0
1258
0
  // (1) Get the base URI
1259
0
  nsIContent* thisContent = AsContent();
1260
0
  nsCOMPtr<nsIURI> baseURL = thisContent->GetBaseURI();
1261
0
1262
0
  // (2) Get the charset
1263
0
  auto encoding = aDocument->GetDocumentCharacterSet();
1264
0
1265
0
  // (3) Construct the silly thing
1266
0
  return NS_NewURI(aURI,
1267
0
                   aSpec,
1268
0
                   encoding,
1269
0
                   baseURL,
1270
0
                   nsContentUtils::GetIOService());
1271
0
}
1272
1273
nsresult
1274
nsImageLoadingContent::FireEvent(const nsAString& aEventType, bool aIsCancelable)
1275
0
{
1276
0
  if (nsContentUtils::DocumentInactiveForImageLoads(GetOurOwnerDoc())) {
1277
0
    // Don't bother to fire any events, especially error events.
1278
0
    return NS_OK;
1279
0
  }
1280
0
1281
0
  // We have to fire the event asynchronously so that we won't go into infinite
1282
0
  // loops in cases when onLoad handlers reset the src and the new src is in
1283
0
  // cache.
1284
0
1285
0
  nsCOMPtr<nsINode> thisNode = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
1286
0
1287
0
  RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
1288
0
    new LoadBlockingAsyncEventDispatcher(thisNode,
1289
0
                                         aEventType,
1290
0
                                         CanBubble::eNo,
1291
0
                                         ChromeOnlyDispatch::eNo);
1292
0
  loadBlockingAsyncDispatcher->PostDOMEvent();
1293
0
1294
0
  if (aIsCancelable) {
1295
0
    mPendingEvent = loadBlockingAsyncDispatcher;
1296
0
  }
1297
0
1298
0
  return NS_OK;
1299
0
}
1300
1301
void
1302
nsImageLoadingContent::AsyncEventRunning(AsyncEventDispatcher* aEvent)
1303
0
{
1304
0
  if (mPendingEvent == aEvent) {
1305
0
    mPendingEvent = nullptr;
1306
0
  }
1307
0
}
1308
1309
void
1310
nsImageLoadingContent::CancelPendingEvent()
1311
0
{
1312
0
  if (mPendingEvent) {
1313
0
    mPendingEvent->Cancel();
1314
0
    mPendingEvent = nullptr;
1315
0
  }
1316
0
}
1317
1318
RefPtr<imgRequestProxy>&
1319
nsImageLoadingContent::PrepareNextRequest(ImageLoadType aImageLoadType)
1320
0
{
1321
0
  MaybeForceSyncDecoding(/* aPrepareNextRequest */ true);
1322
0
1323
0
  // We only want to cancel the existing current request if size is not
1324
0
  // available. bz says the web depends on this behavior.
1325
0
  // Otherwise, we get rid of any half-baked request that might be sitting there
1326
0
  // and make this one current.
1327
0
  return HaveSize(mCurrentRequest) ?
1328
0
           PreparePendingRequest(aImageLoadType) :
1329
0
           PrepareCurrentRequest(aImageLoadType);
1330
0
}
1331
1332
void
1333
nsImageLoadingContent::SetBlockedRequest(int16_t aContentDecision)
1334
0
{
1335
0
  // If this is not calling from LoadImage, for example, from ServiceWorker,
1336
0
  // bail out.
1337
0
  if (!mIsStartingImageLoad) {
1338
0
    return;
1339
0
  }
1340
0
1341
0
  // Sanity
1342
0
  MOZ_ASSERT(!NS_CP_ACCEPTED(aContentDecision), "Blocked but not?");
1343
0
1344
0
  // We should never have a pending request after we got blocked.
1345
0
  MOZ_ASSERT(!mPendingRequest, "mPendingRequest should be null.");
1346
0
1347
0
  if (HaveSize(mCurrentRequest)) {
1348
0
    // PreparePendingRequest set mPendingRequestFlags, now since we've decided
1349
0
    // to block it, we reset it back to 0.
1350
0
    mPendingRequestFlags = 0;
1351
0
  } else {
1352
0
    mImageBlockingStatus = aContentDecision;
1353
0
  }
1354
0
}
1355
1356
RefPtr<imgRequestProxy>&
1357
nsImageLoadingContent::PrepareCurrentRequest(ImageLoadType aImageLoadType)
1358
0
{
1359
0
  // Blocked images go through SetBlockedRequest, which is a separate path. For
1360
0
  // everything else, we're unblocked.
1361
0
  mImageBlockingStatus = nsIContentPolicy::ACCEPT;
1362
0
1363
0
  // Get rid of anything that was there previously.
1364
0
  ClearCurrentRequest(NS_BINDING_ABORTED,
1365
0
                      Some(OnNonvisible::DISCARD_IMAGES));
1366
0
1367
0
  if (mNewRequestsWillNeedAnimationReset) {
1368
0
    mCurrentRequestFlags |= REQUEST_NEEDS_ANIMATION_RESET;
1369
0
  }
1370
0
1371
0
  if (aImageLoadType == eImageLoadType_Imageset) {
1372
0
    mCurrentRequestFlags |= REQUEST_IS_IMAGESET;
1373
0
  }
1374
0
1375
0
  // Return a reference.
1376
0
  return mCurrentRequest;
1377
0
}
1378
1379
RefPtr<imgRequestProxy>&
1380
nsImageLoadingContent::PreparePendingRequest(ImageLoadType aImageLoadType)
1381
0
{
1382
0
  // Get rid of anything that was there previously.
1383
0
  ClearPendingRequest(NS_BINDING_ABORTED,
1384
0
                      Some(OnNonvisible::DISCARD_IMAGES));
1385
0
1386
0
  if (mNewRequestsWillNeedAnimationReset) {
1387
0
    mPendingRequestFlags |= REQUEST_NEEDS_ANIMATION_RESET;
1388
0
  }
1389
0
1390
0
  if (aImageLoadType == eImageLoadType_Imageset) {
1391
0
    mPendingRequestFlags |= REQUEST_IS_IMAGESET;
1392
0
  }
1393
0
1394
0
  // Return a reference.
1395
0
  return mPendingRequest;
1396
0
}
1397
1398
namespace {
1399
1400
class ImageRequestAutoLock
1401
{
1402
public:
1403
  explicit ImageRequestAutoLock(imgIRequest* aRequest)
1404
    : mRequest(aRequest)
1405
0
  {
1406
0
    if (mRequest) {
1407
0
      mRequest->LockImage();
1408
0
    }
1409
0
  }
1410
1411
  ~ImageRequestAutoLock()
1412
0
  {
1413
0
    if (mRequest) {
1414
0
      mRequest->UnlockImage();
1415
0
    }
1416
0
  }
1417
1418
private:
1419
  nsCOMPtr<imgIRequest> mRequest;
1420
};
1421
1422
} // namespace
1423
1424
void
1425
nsImageLoadingContent::MakePendingRequestCurrent()
1426
0
{
1427
0
  MOZ_ASSERT(mPendingRequest);
1428
0
1429
0
  // Lock mCurrentRequest for the duration of this method.  We do this because
1430
0
  // PrepareCurrentRequest() might unlock mCurrentRequest.  If mCurrentRequest
1431
0
  // and mPendingRequest are both requests for the same image, unlocking
1432
0
  // mCurrentRequest before we lock mPendingRequest can cause the lock count
1433
0
  // to go to 0 and the image to be discarded!
1434
0
  ImageRequestAutoLock autoLock(mCurrentRequest);
1435
0
1436
0
  ImageLoadType loadType = \
1437
0
    (mPendingRequestFlags & REQUEST_IS_IMAGESET) ? eImageLoadType_Imageset
1438
0
                                                 : eImageLoadType_Normal;
1439
0
1440
0
  PrepareCurrentRequest(loadType) = mPendingRequest;
1441
0
  MakePendingScriptedRequestsCurrent();
1442
0
  mPendingRequest = nullptr;
1443
0
  mCurrentRequestFlags = mPendingRequestFlags;
1444
0
  mPendingRequestFlags = 0;
1445
0
  mCurrentRequestRegistered = mPendingRequestRegistered;
1446
0
  mPendingRequestRegistered = false;
1447
0
  ResetAnimationIfNeeded();
1448
0
}
1449
1450
void
1451
nsImageLoadingContent::ClearCurrentRequest(nsresult aReason,
1452
                                           const Maybe<OnNonvisible>& aNonvisibleAction)
1453
0
{
1454
0
  if (!mCurrentRequest) {
1455
0
    // Even if we didn't have a current request, we might have been keeping
1456
0
    // a URI and flags as a placeholder for a failed load. Clear that now.
1457
0
    mCurrentURI = nullptr;
1458
0
    mCurrentRequestFlags = 0;
1459
0
    return;
1460
0
  }
1461
0
  MOZ_ASSERT(!mCurrentURI,
1462
0
             "Shouldn't have both mCurrentRequest and mCurrentURI!");
1463
0
1464
0
  // Deregister this image from the refresh driver so it no longer receives
1465
0
  // notifications.
1466
0
  nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mCurrentRequest,
1467
0
                                        &mCurrentRequestRegistered);
1468
0
1469
0
  // Clean up the request.
1470
0
  UntrackImage(mCurrentRequest, aNonvisibleAction);
1471
0
  ClearScriptedRequests(CURRENT_REQUEST, aReason);
1472
0
  mCurrentRequest->CancelAndForgetObserver(aReason);
1473
0
  mCurrentRequest = nullptr;
1474
0
  mCurrentRequestFlags = 0;
1475
0
}
1476
1477
void
1478
nsImageLoadingContent::ClearPendingRequest(nsresult aReason,
1479
                                           const Maybe<OnNonvisible>& aNonvisibleAction)
1480
0
{
1481
0
  if (!mPendingRequest)
1482
0
    return;
1483
0
1484
0
  // Deregister this image from the refresh driver so it no longer receives
1485
0
  // notifications.
1486
0
  nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mPendingRequest,
1487
0
                                        &mPendingRequestRegistered);
1488
0
1489
0
  UntrackImage(mPendingRequest, aNonvisibleAction);
1490
0
  ClearScriptedRequests(PENDING_REQUEST, aReason);
1491
0
  mPendingRequest->CancelAndForgetObserver(aReason);
1492
0
  mPendingRequest = nullptr;
1493
0
  mPendingRequestFlags = 0;
1494
0
}
1495
1496
bool*
1497
nsImageLoadingContent::GetRegisteredFlagForRequest(imgIRequest* aRequest)
1498
0
{
1499
0
  if (aRequest == mCurrentRequest) {
1500
0
    return &mCurrentRequestRegistered;
1501
0
  }
1502
0
  if (aRequest == mPendingRequest) {
1503
0
    return &mPendingRequestRegistered;
1504
0
  }
1505
0
  return nullptr;
1506
0
}
1507
1508
void
1509
nsImageLoadingContent::ResetAnimationIfNeeded()
1510
0
{
1511
0
  if (mCurrentRequest &&
1512
0
      (mCurrentRequestFlags & REQUEST_NEEDS_ANIMATION_RESET)) {
1513
0
    nsCOMPtr<imgIContainer> container;
1514
0
    mCurrentRequest->GetImage(getter_AddRefs(container));
1515
0
    if (container)
1516
0
      container->ResetAnimation();
1517
0
    mCurrentRequestFlags &= ~REQUEST_NEEDS_ANIMATION_RESET;
1518
0
  }
1519
0
}
1520
1521
bool
1522
nsImageLoadingContent::HaveSize(imgIRequest *aImage)
1523
0
{
1524
0
  // Handle the null case
1525
0
  if (!aImage)
1526
0
    return false;
1527
0
1528
0
  // Query the image
1529
0
  uint32_t status;
1530
0
  nsresult rv = aImage->GetImageStatus(&status);
1531
0
  return (NS_SUCCEEDED(rv) && (status & imgIRequest::STATUS_SIZE_AVAILABLE));
1532
0
}
1533
1534
void
1535
nsImageLoadingContent::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
1536
                                  nsIContent* aBindingParent)
1537
0
{
1538
0
  // We may be entering the document, so if our image should be tracked,
1539
0
  // track it.
1540
0
  if (!aDocument)
1541
0
    return;
1542
0
1543
0
  TrackImage(mCurrentRequest);
1544
0
  TrackImage(mPendingRequest);
1545
0
}
1546
1547
void
1548
nsImageLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
1549
0
{
1550
0
  // We may be leaving the document, so if our image is tracked, untrack it.
1551
0
  nsCOMPtr<nsIDocument> doc = GetOurCurrentDoc();
1552
0
  if (!doc)
1553
0
    return;
1554
0
1555
0
  UntrackImage(mCurrentRequest);
1556
0
  UntrackImage(mPendingRequest);
1557
0
}
1558
1559
void
1560
nsImageLoadingContent::OnVisibilityChange(Visibility aNewVisibility,
1561
                                          const Maybe<OnNonvisible>& aNonvisibleAction)
1562
0
{
1563
0
  switch (aNewVisibility) {
1564
0
    case Visibility::APPROXIMATELY_VISIBLE:
1565
0
      TrackImage(mCurrentRequest);
1566
0
      TrackImage(mPendingRequest);
1567
0
      break;
1568
0
1569
0
    case Visibility::APPROXIMATELY_NONVISIBLE:
1570
0
      UntrackImage(mCurrentRequest, aNonvisibleAction);
1571
0
      UntrackImage(mPendingRequest, aNonvisibleAction);
1572
0
      break;
1573
0
1574
0
    case Visibility::UNTRACKED:
1575
0
      MOZ_ASSERT_UNREACHABLE("Shouldn't notify for untracked visibility");
1576
0
      break;
1577
0
  }
1578
0
}
1579
1580
void
1581
nsImageLoadingContent::TrackImage(imgIRequest* aImage,
1582
                                  nsIFrame* aFrame /*= nullptr */)
1583
0
{
1584
0
  if (!aImage)
1585
0
    return;
1586
0
1587
0
  MOZ_ASSERT(aImage == mCurrentRequest || aImage == mPendingRequest,
1588
0
             "Why haven't we heard of this request?");
1589
0
1590
0
  nsIDocument* doc = GetOurCurrentDoc();
1591
0
  if (!doc) {
1592
0
    return;
1593
0
  }
1594
0
1595
0
  if (!aFrame) {
1596
0
    aFrame = GetOurPrimaryFrame();
1597
0
  }
1598
0
1599
0
  /* This line is deceptively simple. It hides a lot of subtlety. Before we
1600
0
   * create an nsImageFrame we call nsImageFrame::ShouldCreateImageFrameFor
1601
0
   * to determine if we should create an nsImageFrame or create a frame based
1602
0
   * on the display of the element (ie inline, block, etc). Inline, block, etc
1603
0
   * frames don't register for visibility tracking so they will return UNTRACKED
1604
0
   * from GetVisibility(). So this line is choosing to mark such images as
1605
0
   * visible. Once the image loads we will get an nsImageFrame and the proper
1606
0
   * visibility. This is a pitfall of tracking the visibility on the frames
1607
0
   * instead of the content node.
1608
0
   */
1609
0
  if (!aFrame || aFrame->GetVisibility() == Visibility::APPROXIMATELY_NONVISIBLE) {
1610
0
    return;
1611
0
  }
1612
0
1613
0
  if (aImage == mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
1614
0
    mCurrentRequestFlags |= REQUEST_IS_TRACKED;
1615
0
    doc->ImageTracker()->Add(mCurrentRequest);
1616
0
  }
1617
0
  if (aImage == mPendingRequest && !(mPendingRequestFlags & REQUEST_IS_TRACKED)) {
1618
0
    mPendingRequestFlags |= REQUEST_IS_TRACKED;
1619
0
    doc->ImageTracker()->Add(mPendingRequest);
1620
0
  }
1621
0
}
1622
1623
void
1624
nsImageLoadingContent::UntrackImage(imgIRequest* aImage,
1625
                                    const Maybe<OnNonvisible>& aNonvisibleAction
1626
                                      /* = Nothing() */)
1627
0
{
1628
0
  if (!aImage)
1629
0
    return;
1630
0
1631
0
  MOZ_ASSERT(aImage == mCurrentRequest || aImage == mPendingRequest,
1632
0
             "Why haven't we heard of this request?");
1633
0
1634
0
  // We may not be in the document.  If we outlived our document that's fine,
1635
0
  // because the document empties out the tracker and unlocks all locked images
1636
0
  // on destruction.  But if we were never in the document we may need to force
1637
0
  // discarding the image here, since this is the only chance we have.
1638
0
  nsIDocument* doc = GetOurCurrentDoc();
1639
0
  if (aImage == mCurrentRequest) {
1640
0
    if (doc && (mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
1641
0
      mCurrentRequestFlags &= ~REQUEST_IS_TRACKED;
1642
0
      doc->ImageTracker()->Remove(
1643
0
        mCurrentRequest,
1644
0
        aNonvisibleAction == Some(OnNonvisible::DISCARD_IMAGES)
1645
0
          ? ImageTracker::REQUEST_DISCARD
1646
0
          : 0);
1647
0
    } else if (aNonvisibleAction == Some(OnNonvisible::DISCARD_IMAGES)) {
1648
0
      // If we're not in the document we may still need to be discarded.
1649
0
      aImage->RequestDiscard();
1650
0
    }
1651
0
  }
1652
0
  if (aImage == mPendingRequest) {
1653
0
    if (doc && (mPendingRequestFlags & REQUEST_IS_TRACKED)) {
1654
0
      mPendingRequestFlags &= ~REQUEST_IS_TRACKED;
1655
0
      doc->ImageTracker()->Remove(
1656
0
        mPendingRequest,
1657
0
        aNonvisibleAction == Some(OnNonvisible::DISCARD_IMAGES)
1658
0
          ? ImageTracker::REQUEST_DISCARD
1659
0
          : 0);
1660
0
    } else if (aNonvisibleAction == Some(OnNonvisible::DISCARD_IMAGES)) {
1661
0
      // If we're not in the document we may still need to be discarded.
1662
0
      aImage->RequestDiscard();
1663
0
    }
1664
0
  }
1665
0
}
1666
1667
1668
void
1669
nsImageLoadingContent::CreateStaticImageClone(nsImageLoadingContent* aDest) const
1670
0
{
1671
0
  aDest->ClearScriptedRequests(CURRENT_REQUEST, NS_BINDING_ABORTED);
1672
0
  aDest->mCurrentRequest =
1673
0
    nsContentUtils::GetStaticRequest(aDest->GetOurOwnerDoc(), mCurrentRequest);
1674
0
  if (aDest->mCurrentRequest) {
1675
0
    aDest->CloneScriptedRequests(aDest->mCurrentRequest);
1676
0
  }
1677
0
  aDest->TrackImage(aDest->mCurrentRequest);
1678
0
  aDest->mForcedImageState = mForcedImageState;
1679
0
  aDest->mImageBlockingStatus = mImageBlockingStatus;
1680
0
  aDest->mLoadingEnabled = mLoadingEnabled;
1681
0
  aDest->mStateChangerDepth = mStateChangerDepth;
1682
0
  aDest->mIsImageStateForced = mIsImageStateForced;
1683
0
  aDest->mLoading = mLoading;
1684
0
  aDest->mBroken = mBroken;
1685
0
  aDest->mUserDisabled = mUserDisabled;
1686
0
  aDest->mSuppressed = mSuppressed;
1687
0
}
1688
1689
CORSMode
1690
nsImageLoadingContent::GetCORSMode()
1691
0
{
1692
0
  return CORS_NONE;
1693
0
}
1694
1695
nsImageLoadingContent::ImageObserver::ImageObserver(imgINotificationObserver* aObserver)
1696
  : mObserver(aObserver)
1697
  , mNext(nullptr)
1698
0
{
1699
0
  MOZ_COUNT_CTOR(ImageObserver);
1700
0
}
1701
1702
nsImageLoadingContent::ImageObserver::~ImageObserver()
1703
0
{
1704
0
  MOZ_COUNT_DTOR(ImageObserver);
1705
0
  NS_CONTENT_DELETE_LIST_MEMBER(ImageObserver, this, mNext);
1706
0
}
1707
1708
nsImageLoadingContent::ScriptedImageObserver::ScriptedImageObserver(imgINotificationObserver* aObserver,
1709
                                                                    RefPtr<imgRequestProxy>&& aCurrentRequest,
1710
                                                                    RefPtr<imgRequestProxy>&& aPendingRequest)
1711
  : mObserver(aObserver)
1712
  , mCurrentRequest(aCurrentRequest)
1713
  , mPendingRequest(aPendingRequest)
1714
0
{ }
1715
1716
nsImageLoadingContent::ScriptedImageObserver::~ScriptedImageObserver()
1717
0
{
1718
0
  // We should have cancelled any requests before getting released.
1719
0
  DebugOnly<bool> cancel = CancelRequests();
1720
0
  MOZ_ASSERT(!cancel, "Still have requests in ~ScriptedImageObserver!");
1721
0
}
1722
1723
bool
1724
nsImageLoadingContent::ScriptedImageObserver::CancelRequests()
1725
0
{
1726
0
  bool cancelled = false;
1727
0
  if (mCurrentRequest) {
1728
0
    mCurrentRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
1729
0
    mCurrentRequest = nullptr;
1730
0
    cancelled = true;
1731
0
  }
1732
0
  if (mPendingRequest) {
1733
0
    mPendingRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
1734
0
    mPendingRequest = nullptr;
1735
0
    cancelled = true;
1736
0
  }
1737
0
  return cancelled;
1738
0
}
1739
1740
// Only HTMLInputElement.h overrides this for <img> tags
1741
// all other subclasses use this one, i.e. ignore referrer attributes
1742
mozilla::net::ReferrerPolicy
1743
nsImageLoadingContent::GetImageReferrerPolicy()
1744
0
{
1745
0
  return mozilla::net::RP_Unset;
1746
0
}
1747
1748
Element*
1749
nsImageLoadingContent::FindImageMap()
1750
0
{
1751
0
  nsIContent* thisContent = AsContent();
1752
0
  Element* thisElement = thisContent->AsElement();
1753
0
1754
0
  nsAutoString useMap;
1755
0
  thisElement->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, useMap);
1756
0
  if (useMap.IsEmpty()) {
1757
0
    return nullptr;
1758
0
  }
1759
0
1760
0
  nsAString::const_iterator start, end;
1761
0
  useMap.BeginReading(start);
1762
0
  useMap.EndReading(end);
1763
0
1764
0
  int32_t hash = useMap.FindChar('#');
1765
0
  if (hash < 0) {
1766
0
    return nullptr;
1767
0
  }
1768
0
  // useMap contains a '#', set start to point right after the '#'
1769
0
  start.advance(hash + 1);
1770
0
1771
0
  if (start == end) {
1772
0
    return nullptr; // useMap == "#"
1773
0
  }
1774
0
1775
0
  RefPtr<nsContentList> imageMapList;
1776
0
  if (thisElement->IsInUncomposedDoc()) {
1777
0
    // Optimize the common case and use document level image map.
1778
0
    imageMapList = thisElement->OwnerDoc()->ImageMapList();
1779
0
  } else {
1780
0
    // Per HTML spec image map should be searched in the element's scope,
1781
0
    // so using SubtreeRoot() here.
1782
0
    // Because this is a temporary list, we don't need to make it live.
1783
0
    imageMapList = new nsContentList(thisElement->SubtreeRoot(),
1784
0
                                     kNameSpaceID_XHTML,
1785
0
                                     nsGkAtoms::map, nsGkAtoms::map,
1786
0
                                     true, /* deep */
1787
0
                                     false /* live */);
1788
0
  }
1789
0
1790
0
  nsAutoString mapName(Substring(start, end));
1791
0
1792
0
  uint32_t i, n = imageMapList->Length(true);
1793
0
  for (i = 0; i < n; ++i) {
1794
0
    nsIContent* map = imageMapList->Item(i);
1795
0
    if (map->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id,
1796
0
                                      mapName, eCaseMatters) ||
1797
0
        map->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
1798
0
                                      mapName, eCaseMatters)) {
1799
0
      return map->AsElement();
1800
0
    }
1801
0
  }
1802
0
1803
0
  return nullptr;
1804
0
}