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 #ifndef mozilla_css_ImageLoader_h___
11 #define mozilla_css_ImageLoader_h___
12 
13 #include "mozilla/CORSMode.h"
14 #include "nsClassHashtable.h"
15 #include "nsHashKeys.h"
16 #include "nsIFrame.h"
17 #include "nsIReflowCallback.h"
18 #include "nsTArray.h"
19 #include "imgIRequest.h"
20 #include "imgINotificationObserver.h"
21 #include "mozilla/Attributes.h"
22 
23 class imgIContainer;
24 class nsIFrame;
25 class nsPresContext;
26 class nsIURI;
27 class nsIPrincipal;
28 
29 namespace mozilla {
30 struct MediaFeatureChange;
31 namespace dom {
32 class Document;
33 }
34 
35 namespace css {
36 
37 /**
38  * NOTE: All methods must be called from the main thread unless otherwise
39  * specified.
40  */
41 class ImageLoader final {
42  public:
43   static void Init();
44   static void Shutdown();
45 
46   // We also associate flags alongside frames in the request-to-frames hashmap.
47   // These are used for special handling of events for requests.
48   typedef uint32_t FrameFlags;
49   enum {
50     REQUEST_REQUIRES_REFLOW = 1u << 0,
51     REQUEST_HAS_BLOCKED_ONLOAD = 1u << 1,
52   };
53 
ImageLoader(dom::Document * aDocument)54   explicit ImageLoader(dom::Document* aDocument) : mDocument(aDocument) {
55     MOZ_ASSERT(mDocument);
56   }
57 
58   NS_INLINE_DECL_REFCOUNTING(ImageLoader)
59 
60   void DropDocumentReference();
61 
62   void AssociateRequestToFrame(imgIRequest* aRequest, nsIFrame* aFrame,
63                                FrameFlags aFlags);
64 
65   void DisassociateRequestFromFrame(imgIRequest* aRequest, nsIFrame* aFrame);
66 
67   void DropRequestsForFrame(nsIFrame* aFrame);
68 
69   void SetAnimationMode(uint16_t aMode);
70 
71   // The prescontext for this ImageLoader's document. We need it to be passed
72   // in because this can be called during presentation destruction after the
73   // presshell pointer on the document has been cleared.
74   void ClearFrames(nsPresContext* aPresContext);
75 
76   // Triggers an image load.
77   static already_AddRefed<imgRequestProxy> LoadImage(
78       const StyleComputedImageUrl&, dom::Document&);
79 
80   // Usually, only one style value owns a given proxy. However, we have a hack
81   // to share image proxies in chrome documents under some circumstances. We
82   // need to keep track of this so that we don't stop tracking images too early.
83   //
84   // In practice it shouldn't matter as these chrome images are mostly static,
85   // but it is always good to keep sanity.
86   static void NoteSharedLoad(imgRequestProxy*);
87 
88   // Undoes what `LoadImage` does.
89   static void UnloadImage(imgRequestProxy*);
90 
91   // This is called whenever an image we care about notifies the
92   // GlobalImageObserver.
93   void Notify(imgIRequest*, int32_t aType, const nsIntRect* aData);
94 
95  private:
96   // Called when we stop caring about a given request.
97   void DeregisterImageRequest(imgIRequest*, nsPresContext*);
98 
99   // This callback is used to unblock document onload after a reflow
100   // triggered from an image load.
101   struct ImageReflowCallback final : public nsIReflowCallback {
102     RefPtr<ImageLoader> mLoader;
103     WeakFrame mFrame;
104     nsCOMPtr<imgIRequest> const mRequest;
105 
ImageReflowCallbackfinal106     ImageReflowCallback(ImageLoader* aLoader, nsIFrame* aFrame,
107                         imgIRequest* aRequest)
108         : mLoader(aLoader), mFrame(aFrame), mRequest(aRequest) {}
109 
110     bool ReflowFinished() override;
111     void ReflowCallbackCanceled() override;
112   };
113 
114   ~ImageLoader() = default;
115 
116   // We need to be able to look up the frames associated with a request (for
117   // delivering notifications) and the requests associated with a frame (when
118   // the frame goes away). Thus we maintain hashtables going both ways.  These
119   // should always be in sync.
120 
121   struct FrameWithFlags {
FrameWithFlagsFrameWithFlags122     explicit FrameWithFlags(nsIFrame* aFrame) : mFrame(aFrame), mFlags(0) {
123       MOZ_ASSERT(mFrame);
124     }
125     nsIFrame* const mFrame;
126     FrameFlags mFlags;
127   };
128 
129   // A helper class to compare FrameWithFlags by comparing mFrame and
130   // ignoring mFlags.
131   class FrameOnlyComparator {
132    public:
Equals(const FrameWithFlags & aElem1,const FrameWithFlags & aElem2)133     bool Equals(const FrameWithFlags& aElem1,
134                 const FrameWithFlags& aElem2) const {
135       return aElem1.mFrame == aElem2.mFrame;
136     }
137 
LessThan(const FrameWithFlags & aElem1,const FrameWithFlags & aElem2)138     bool LessThan(const FrameWithFlags& aElem1,
139                   const FrameWithFlags& aElem2) const {
140       return aElem1.mFrame < aElem2.mFrame;
141     }
142   };
143 
144   typedef nsTArray<FrameWithFlags> FrameSet;
145   typedef nsTArray<nsCOMPtr<imgIRequest>> RequestSet;
146   typedef nsClassHashtable<nsISupportsHashKey, FrameSet> RequestToFrameMap;
147   typedef nsClassHashtable<nsPtrHashKey<nsIFrame>, RequestSet>
148       FrameToRequestMap;
149 
150   nsPresContext* GetPresContext();
151 
152   void RequestPaintIfNeeded(FrameSet* aFrameSet, imgIRequest* aRequest,
153                             bool aForcePaint);
154   void UnblockOnloadIfNeeded(nsIFrame* aFrame, imgIRequest* aRequest);
155   void RequestReflowIfNeeded(FrameSet* aFrameSet, imgIRequest* aRequest);
156   void RequestReflowOnFrame(FrameWithFlags* aFwf, imgIRequest* aRequest);
157 
158   void OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage);
159   void OnFrameComplete(imgIRequest* aRequest);
160   void OnImageIsAnimated(imgIRequest* aRequest);
161   void OnFrameUpdate(imgIRequest* aRequest);
162   void OnLoadComplete(imgIRequest* aRequest);
163 
164   // Helpers for DropRequestsForFrame / DisassociateRequestFromFrame above.
165   void RemoveRequestToFrameMapping(imgIRequest* aRequest, nsIFrame* aFrame);
166   void RemoveFrameToRequestMapping(imgIRequest* aRequest, nsIFrame* aFrame);
167 
168   // A map of imgIRequests to the nsIFrames that are using them.
169   RequestToFrameMap mRequestToFrameMap;
170 
171   // A map of nsIFrames to the imgIRequests they use.
172   FrameToRequestMap mFrameToRequestMap;
173 
174   // A weak pointer to our document. Nulled out by DropDocumentReference.
175   dom::Document* mDocument;
176 };
177 
178 }  // namespace css
179 }  // namespace mozilla
180 
181 #endif /* mozilla_css_ImageLoader_h___ */
182