Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsImageLoadingContent.h
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
#ifndef nsImageLoadingContent_h__
14
#define nsImageLoadingContent_h__
15
16
#include "imgINotificationObserver.h"
17
#include "mozilla/CORSMode.h"
18
#include "mozilla/EventStates.h"
19
#include "mozilla/TimeStamp.h"
20
#include "nsCOMPtr.h"
21
#include "nsIImageLoadingContent.h"
22
#include "nsIRequest.h"
23
#include "mozilla/ErrorResult.h"
24
#include "nsIContentPolicy.h"
25
#include "mozilla/dom/BindingDeclarations.h"
26
#include "mozilla/net/ReferrerPolicy.h"
27
#include "nsAttrValue.h"
28
29
class nsIURI;
30
class nsIDocument;
31
class nsPresContext;
32
class nsIContent;
33
class imgRequestProxy;
34
35
namespace mozilla {
36
class AsyncEventDispatcher;
37
namespace dom {
38
class Element;
39
} // namespace Element;
40
} // namespace mozilla
41
42
#ifdef LoadImage
43
// Undefine LoadImage to prevent naming conflict with Windows.
44
#undef LoadImage
45
#endif
46
47
class nsImageLoadingContent : public nsIImageLoadingContent
48
{
49
  template <typename T> using Maybe = mozilla::Maybe<T>;
50
  using Nothing = mozilla::Nothing;
51
  using OnNonvisible = mozilla::OnNonvisible;
52
  using Visibility = mozilla::Visibility;
53
54
  /* METHODS */
55
public:
56
  nsImageLoadingContent();
57
  virtual ~nsImageLoadingContent();
58
59
  NS_DECL_IMGINOTIFICATIONOBSERVER
60
  NS_DECL_NSIIMAGELOADINGCONTENT
61
62
  // Web IDL binding methods.
63
  // Note that the XPCOM SetLoadingEnabled, ForceImageState methods are OK for
64
  // Web IDL bindings to use as well, since none of them throw when called via
65
  // the Web IDL bindings.
66
67
0
  bool LoadingEnabled() const { return mLoadingEnabled; }
68
  int16_t ImageBlockingStatus() const
69
0
  {
70
0
    return mImageBlockingStatus;
71
0
  }
72
  void AddObserver(imgINotificationObserver* aObserver);
73
  void RemoveObserver(imgINotificationObserver* aObserver);
74
  already_AddRefed<imgIRequest>
75
    GetRequest(int32_t aRequestType, mozilla::ErrorResult& aError);
76
  int32_t
77
    GetRequestType(imgIRequest* aRequest, mozilla::ErrorResult& aError);
78
  already_AddRefed<nsIURI> GetCurrentURI(mozilla::ErrorResult& aError);
79
  already_AddRefed<nsIURI> GetCurrentRequestFinalURI();
80
  void ForceReload(bool aNotify, mozilla::ErrorResult& aError);
81
82
  mozilla::dom::Element* FindImageMap();
83
84
  /**
85
   * Toggle whether or not to synchronously decode an image on draw.
86
   */
87
  void SetSyncDecodingHint(bool aHint);
88
89
protected:
90
  enum ImageLoadType {
91
    // Most normal image loads
92
    eImageLoadType_Normal,
93
    // From a <img srcset> or <picture> context. Affects type given to content
94
    // policy.
95
    eImageLoadType_Imageset
96
  };
97
98
  /**
99
   * LoadImage is called by subclasses when the appropriate
100
   * attributes (eg 'src' for <img> tags) change.  The string passed
101
   * in is the new uri string; this consolidates the code for getting
102
   * the charset, constructing URI objects, and any other incidentals
103
   * into this superclass.
104
   *
105
   * @param aNewURI the URI spec to be loaded (may be a relative URI)
106
   * @param aForce If true, make sure to load the URI.  If false, only
107
   *        load if the URI is different from the currently loaded URI.
108
   * @param aNotify If true, nsIDocumentObserver state change notifications
109
   *                will be sent as needed.
110
   * @param aImageLoadType The ImageLoadType for this request
111
   * @param aTriggeringPrincipal Optional parameter specifying the triggering
112
   *        principal to use for the image load
113
   */
114
  nsresult LoadImage(const nsAString& aNewURI, bool aForce,
115
                     bool aNotify, ImageLoadType aImageLoadType,
116
                     nsIPrincipal* aTriggeringPrincipal = nullptr);
117
118
  /**
119
   * ImageState is called by subclasses that are computing their content state.
120
   * The return value will have the NS_EVENT_STATE_BROKEN,
121
   * NS_EVENT_STATE_USERDISABLED, and NS_EVENT_STATE_SUPPRESSED bits set as
122
   * needed.  Note that this state assumes that this node is "trying" to be an
123
   * image (so for example complete lack of attempt to load an image will lead
124
   * to NS_EVENT_STATE_BROKEN being set).  Subclasses that are not "trying" to
125
   * be an image (eg an HTML <input> of type other than "image") should just
126
   * not call this method when computing their intrinsic state.
127
   */
128
  mozilla::EventStates ImageState() const;
129
130
  /**
131
   * LoadImage is called by subclasses when the appropriate
132
   * attributes (eg 'src' for <img> tags) change. If callers have an
133
   * URI object already available, they should use this method.
134
   *
135
   * @param aNewURI the URI to be loaded
136
   * @param aForce If true, make sure to load the URI.  If false, only
137
   *        load if the URI is different from the currently loaded URI.
138
   * @param aNotify If true, nsIDocumentObserver state change notifications
139
   *                will be sent as needed.
140
   * @param aImageLoadType The ImageLoadType for this request
141
   * @param aLoadStart If true, dispatch "loadstart" event.
142
   * @param aDocument Optional parameter giving the document this node is in.
143
   *        This is purely a performance optimization.
144
   * @param aLoadFlags Optional parameter specifying load flags to use for
145
   *        the image load
146
   * @param aTriggeringPrincipal Optional parameter specifying the triggering
147
   *        principal to use for the image load
148
   */
149
  nsresult LoadImage(nsIURI* aNewURI, bool aForce, bool aNotify,
150
                     ImageLoadType aImageLoadType, bool aLoadStart = true,
151
                     nsIDocument* aDocument = nullptr,
152
                     nsLoadFlags aLoadFlags = nsIRequest::LOAD_NORMAL,
153
                     nsIPrincipal* aTriggeringPrincipal = nullptr);
154
155
  nsresult LoadImage(nsIURI* aNewURI, bool aForce, bool aNotify,
156
                     ImageLoadType aImageLoadType,
157
                     nsIPrincipal* aTriggeringPrincipal)
158
0
  {
159
0
    return LoadImage(aNewURI, aForce, aNotify, aImageLoadType,
160
0
                     true, nullptr, nsIRequest::LOAD_NORMAL,
161
0
                     aTriggeringPrincipal);
162
0
  }
163
164
  /**
165
   * helpers to get the document for this content (from the nodeinfo
166
   * and such).  Not named GetOwnerDoc/GetCurrentDoc to prevent ambiguous
167
   * method names in subclasses
168
   *
169
   * @return the document we belong to
170
   */
171
  nsIDocument* GetOurOwnerDoc();
172
  nsIDocument* GetOurCurrentDoc();
173
174
  /**
175
   * Helper function to get the frame associated with this content. Not named
176
   * GetPrimaryFrame to prevent ambiguous method names in subclasses.
177
   *
178
   * @return The frame which we belong to, or nullptr if it doesn't exist.
179
   */
180
  nsIFrame* GetOurPrimaryFrame();
181
182
  /**
183
   * Helper function to get the PresContext associated with this content's
184
   * frame. Not named GetPresContext to prevent ambiguous method names in
185
   * subclasses.
186
   *
187
   * @return The nsPresContext associated with our frame, or nullptr if either
188
   *         the frame doesn't exist, or the frame's prescontext doesn't exist.
189
   */
190
  nsPresContext* GetFramePresContext();
191
192
  /**
193
   * CancelImageRequests is called by subclasses when they want to
194
   * cancel all image requests (for example when the subclass is
195
   * somehow not an image anymore).
196
   */
197
  void CancelImageRequests(bool aNotify);
198
199
  /**
200
   * Derived classes of nsImageLoadingContent MUST call
201
   * DestroyImageLoadingContent from their destructor, or earlier.  It
202
   * does things that cannot be done in ~nsImageLoadingContent because
203
   * they rely on being able to QueryInterface to other derived classes,
204
   * which cannot happen once the derived class destructor has started
205
   * calling the base class destructors.
206
   */
207
  void DestroyImageLoadingContent();
208
209
0
  void ClearBrokenState() { mBroken = false; }
210
211
  /**
212
   * Returns the CORS mode that will be used for all future image loads. The
213
   * default implementation returns CORS_NONE unconditionally.
214
   */
215
  virtual mozilla::CORSMode GetCORSMode();
216
217
  virtual mozilla::net::ReferrerPolicy GetImageReferrerPolicy();
218
219
  // Subclasses are *required* to call BindToTree/UnbindFromTree.
220
  void BindToTree(nsIDocument* aDocument, nsIContent* aParent,
221
                  nsIContent* aBindingParent);
222
  void UnbindFromTree(bool aDeep, bool aNullParent);
223
224
  nsresult OnLoadComplete(imgIRequest* aRequest, nsresult aStatus);
225
  void OnUnlockedDraw();
226
  nsresult OnImageIsAnimated(imgIRequest *aRequest);
227
228
  // The nsContentPolicyType we would use for this ImageLoadType
229
  static nsContentPolicyType PolicyTypeForLoad(ImageLoadType aImageLoadType);
230
231
  void AsyncEventRunning(mozilla::AsyncEventDispatcher* aEvent);
232
233
  // Get ourselves as an nsIContent*.  Not const because some of the callers
234
  // want a non-const nsIContent.
235
  virtual nsIContent* AsContent() = 0;
236
237
  enum class ImageDecodingType : uint8_t {
238
    Auto,
239
    Async,
240
    Sync,
241
  };
242
243
  static const nsAttrValue::EnumTable kDecodingTable[];
244
  static const nsAttrValue::EnumTable* kDecodingTableDefault;
245
246
private:
247
  /**
248
   * Struct used to manage the native image observers.
249
   */
250
  struct ImageObserver {
251
    explicit ImageObserver(imgINotificationObserver* aObserver);
252
    ~ImageObserver();
253
254
    nsCOMPtr<imgINotificationObserver> mObserver;
255
    ImageObserver* mNext;
256
  };
257
258
  /**
259
   * Struct used to manage the scripted/XPCOM image observers.
260
   */
261
  class ScriptedImageObserver final {
262
  public:
263
    NS_INLINE_DECL_REFCOUNTING(ScriptedImageObserver)
264
265
    ScriptedImageObserver(imgINotificationObserver* aObserver,
266
                          RefPtr<imgRequestProxy>&& aCurrentRequest,
267
                          RefPtr<imgRequestProxy>&& aPendingRequest);
268
    bool CancelRequests();
269
270
    nsCOMPtr<imgINotificationObserver> mObserver;
271
    RefPtr<imgRequestProxy> mCurrentRequest;
272
    RefPtr<imgRequestProxy> mPendingRequest;
273
274
  private:
275
    ~ScriptedImageObserver();
276
  };
277
278
  /**
279
   * Struct to report state changes
280
   */
281
  struct AutoStateChanger {
282
    AutoStateChanger(nsImageLoadingContent* aImageContent,
283
                     bool aNotify) :
284
      mImageContent(aImageContent),
285
      mNotify(aNotify)
286
0
    {
287
0
      mImageContent->mStateChangerDepth++;
288
0
    }
289
    ~AutoStateChanger()
290
0
    {
291
0
      mImageContent->mStateChangerDepth--;
292
0
      mImageContent->UpdateImageState(mNotify);
293
0
    }
294
295
    nsImageLoadingContent* mImageContent;
296
    bool mNotify;
297
  };
298
299
  friend struct AutoStateChanger;
300
301
  /**
302
   * UpdateImageState recomputes the current state of this image loading
303
   * content and updates what ImageState() returns accordingly.  It will also
304
   * fire a ContentStatesChanged() notification as needed if aNotify is true.
305
   */
306
  void UpdateImageState(bool aNotify);
307
308
  /**
309
   * Method to fire an event once we know what's going on with the image load.
310
   *
311
   * @param aEventType "loadstart", "loadend", "load", or "error" depending on
312
   *                   how things went
313
   * @param aIsCancelable true if event is cancelable.
314
   */
315
  nsresult FireEvent(const nsAString& aEventType, bool aIsCancelable = false);
316
317
  /**
318
   * Method to cancel and null-out pending event if they exist.
319
   */
320
  void CancelPendingEvent();
321
322
  RefPtr<mozilla::AsyncEventDispatcher> mPendingEvent;
323
324
protected:
325
  /**
326
   * Method to create an nsIURI object from the given string (will
327
   * handle getting the right charset, base, etc).  You MUST pass in a
328
   * non-null document to this function.
329
   *
330
   * @param aSpec the string spec (from an HTML attribute, eg)
331
   * @param aDocument the document we belong to
332
   * @return the URI we want to be loading
333
   */
334
  nsresult StringToURI(const nsAString& aSpec, nsIDocument* aDocument,
335
                       nsIURI** aURI);
336
337
  void CreateStaticImageClone(nsImageLoadingContent* aDest) const;
338
339
  /**
340
   * Prepare and returns a reference to the "next request". If there's already
341
   * a _usable_ current request (one with SIZE_AVAILABLE), this request is
342
   * "pending" until it becomes usable. Otherwise, this becomes the current
343
   * request.
344
   *
345
   * @param aImageLoadType The ImageLoadType for this request
346
   */
347
   RefPtr<imgRequestProxy>& PrepareNextRequest(ImageLoadType aImageLoadType);
348
349
  /**
350
   * Returns a COMPtr reference to the current/pending image requests, cleaning
351
   * up and canceling anything that was there before. Note that if you just want
352
   * to get rid of one of the requests, you should call
353
   * Clear*Request(NS_BINDING_ABORTED) instead.
354
   *
355
   * @param aImageLoadType The ImageLoadType for this request
356
   */
357
  RefPtr<imgRequestProxy>& PrepareCurrentRequest(ImageLoadType aImageLoadType);
358
  RefPtr<imgRequestProxy>& PreparePendingRequest(ImageLoadType aImageLoadType);
359
360
  /**
361
   * Switch our pending request to be our current request.
362
   * mPendingRequest must be non-null!
363
   */
364
  void MakePendingRequestCurrent();
365
366
  /**
367
   * Cancels and nulls-out the "current" and "pending" requests if they exist.
368
   *
369
   * @param aNonvisibleAction An action to take if the image is no longer
370
   *                          visible as a result; see |UntrackImage|.
371
   */
372
  void ClearCurrentRequest(nsresult aReason,
373
                           const Maybe<OnNonvisible>& aNonvisibleAction = Nothing());
374
  void ClearPendingRequest(nsresult aReason,
375
                           const Maybe<OnNonvisible>& aNonvisibleAction = Nothing());
376
377
  /**
378
   * Retrieve a pointer to the 'registered with the refresh driver' flag for
379
   * which a particular image request corresponds.
380
   *
381
   * @returns A pointer to the boolean flag for a given image request, or
382
   *          |nullptr| if the request is not either |mPendingRequest| or
383
   *          |mCurrentRequest|.
384
   */
385
  bool* GetRegisteredFlagForRequest(imgIRequest* aRequest);
386
387
  /**
388
   * Reset animation of the current request if |mNewRequestsWillNeedAnimationReset|
389
   * was true when the request was prepared.
390
   */
391
  void ResetAnimationIfNeeded();
392
393
  /**
394
   * Static helper method to tell us if we have the size of a request. The
395
   * image may be null.
396
   */
397
  static bool HaveSize(imgIRequest *aImage);
398
399
  /**
400
   * Adds/Removes a given imgIRequest from our document's tracker.
401
   *
402
   * No-op if aImage is null.
403
   *
404
   * @param aFrame If called from FrameCreated the frame passed to FrameCreated.
405
   *               This is our frame, but at the time of the FrameCreated call
406
   *               our primary frame pointer hasn't been set yet, so this is
407
   *               only way to get our frame.
408
   *
409
   * @param aNonvisibleAction A requested action if the frame has become
410
   *                          nonvisible. If Nothing(), no action is
411
   *                          requested. If DISCARD_IMAGES is specified, the
412
   *                          frame is requested to ask any images it's
413
   *                          associated with to discard their surfaces if
414
   *                          possible.
415
   */
416
  void TrackImage(imgIRequest* aImage, nsIFrame* aFrame = nullptr);
417
  void UntrackImage(imgIRequest* aImage,
418
                    const Maybe<OnNonvisible>& aNonvisibleAction = Nothing());
419
420
  /* MEMBERS */
421
  RefPtr<imgRequestProxy> mCurrentRequest;
422
  RefPtr<imgRequestProxy> mPendingRequest;
423
  uint32_t mCurrentRequestFlags;
424
  uint32_t mPendingRequestFlags;
425
426
  enum {
427
    // Set if the request needs ResetAnimation called on it.
428
    REQUEST_NEEDS_ANIMATION_RESET = 0x00000001U,
429
    // Set if the request is currently tracked with the document.
430
    REQUEST_IS_TRACKED = 0x00000004U,
431
    // Set if this is an imageset request, such as from <img srcset> or
432
    // <picture>
433
    REQUEST_IS_IMAGESET = 0x00000008U
434
  };
435
436
  // If the image was blocked or if there was an error loading, it's nice to
437
  // still keep track of what the URI was despite not having an imgIRequest.
438
  // We only maintain this in those situations (in the common case, this is
439
  // always null).
440
  nsCOMPtr<nsIURI>      mCurrentURI;
441
442
private:
443
  /**
444
   * Clones the given "current" or "pending" request for each scripted observer.
445
   */
446
  void CloneScriptedRequests(imgRequestProxy* aRequest);
447
448
  /**
449
   * Cancels and nulls-out the "current" or "pending" requests if they exist
450
   * for each scripted observer.
451
   */
452
  void ClearScriptedRequests(int32_t aRequestType, nsresult aReason);
453
454
  /**
455
   * Moves the "pending" request into the "current" request for each scripted
456
   * observer. If there is an existing "current" request, it will cancel it
457
   * first.
458
   */
459
  void MakePendingScriptedRequestsCurrent();
460
461
  /**
462
   * Depending on the configured decoding hint, and/or how recently we updated
463
   * the image request, force or stop the frame from decoding the image
464
   * synchronously when it is drawn.
465
   * @param aPrepareNextRequest True if this is when updating the image request.
466
   * @param aFrame If called from FrameCreated the frame passed to FrameCreated.
467
   *               This is our frame, but at the time of the FrameCreated call
468
   *               our primary frame pointer hasn't been set yet, so this is
469
   *               only way to get our frame.
470
   */
471
  void MaybeForceSyncDecoding(bool aPrepareNextRequest,
472
                              nsIFrame* aFrame = nullptr);
473
474
  /**
475
   * Typically we will have only one observer (our frame in the screen
476
   * prescontext), so we want to only make space for one and to
477
   * heap-allocate anything past that (saves memory and malloc churn
478
   * in the common case).  The storage is a linked list, we just
479
   * happen to actually hold the first observer instead of a pointer
480
   * to it.
481
   */
482
  ImageObserver mObserverList;
483
484
  /**
485
   * Typically we will have no scripted observers, as this is only used by
486
   * chrome, legacy extensions, and some mochitests. An empty array reserves
487
   * minimal memory.
488
   */
489
  nsTArray<RefPtr<ScriptedImageObserver>> mScriptedObservers;
490
491
  /**
492
   * When mIsImageStateForced is true, this holds the ImageState that we'll
493
   * return in ImageState().
494
   */
495
  mozilla::EventStates mForcedImageState;
496
497
  mozilla::TimeStamp mMostRecentRequestChange;
498
499
  int16_t mImageBlockingStatus;
500
  bool mLoadingEnabled : 1;
501
502
  /**
503
   * When true, we return mForcedImageState from ImageState().
504
   */
505
  bool mIsImageStateForced : 1;
506
507
  /**
508
   * The state we had the last time we checked whether we needed to notify the
509
   * document of a state change.  These are maintained by UpdateImageState.
510
   */
511
  bool mLoading : 1;
512
  bool mBroken : 1;
513
  bool mUserDisabled : 1;
514
  bool mSuppressed : 1;
515
516
protected:
517
  /**
518
   * A hack to get animations to reset, see bug 594771. On requests
519
   * that originate from setting .src, we mark them for needing their animation
520
   * reset when they are ready. mNewRequestsWillNeedAnimationReset is set to
521
   * true while preparing such requests (as a hack around needing to change an
522
   * interface), and the other two booleans store which of the current
523
   * and pending requests are of the sort that need their animation restarted.
524
   */
525
  bool mNewRequestsWillNeedAnimationReset : 1;
526
527
  /**
528
   * Flag to indicate whether the channel should be mark as urgent-start.
529
   * It should be set in *Element and passed to nsContentUtils::LoadImage.
530
   * True if we want to set nsIClassOfService::UrgentStart to the channel to
531
   * get the response ASAP for better user responsiveness.
532
   */
533
  bool mUseUrgentStartForChannel;
534
private:
535
  /* The number of nested AutoStateChangers currently tracking our state. */
536
  uint8_t mStateChangerDepth;
537
538
  // Flags to indicate whether each of the current and pending requests are
539
  // registered with the refresh driver.
540
  bool mCurrentRequestRegistered;
541
  bool mPendingRequestRegistered;
542
543
  // TODO:
544
  // Bug 1353685: Should ServiceWorker call SetBlockedRequest?
545
  //
546
  // This member is used in SetBlockedRequest, if it's true, then this call is
547
  // triggered from LoadImage.
548
  // If this is false, it means this call is from other places like
549
  // ServiceWorker, then we will ignore call to SetBlockedRequest for now.
550
  //
551
  // Also we use this variable to check if some evil code is reentering LoadImage.
552
  bool mIsStartingImageLoad;
553
554
  // If true, force frames to synchronously decode images on draw.
555
  bool mSyncDecodingHint;
556
};
557
558
#endif // nsImageLoadingContent_h__