|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 // vim: ft=cpp tw=78 sw=2 et ts=2 |
|
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 "imgIOnloadBlocker.h" |
|
18 #include "mozilla/CORSMode.h" |
|
19 #include "mozilla/EventStates.h" |
|
20 #include "nsCOMPtr.h" |
|
21 #include "nsIImageLoadingContent.h" |
|
22 #include "nsIRequest.h" |
|
23 #include "mozilla/ErrorResult.h" |
|
24 #include "nsAutoPtr.h" |
|
25 |
|
26 class nsIURI; |
|
27 class nsIDocument; |
|
28 class imgILoader; |
|
29 class nsIIOService; |
|
30 class nsPresContext; |
|
31 class nsIContent; |
|
32 class imgRequestProxy; |
|
33 |
|
34 #ifdef LoadImage |
|
35 // Undefine LoadImage to prevent naming conflict with Windows. |
|
36 #undef LoadImage |
|
37 #endif |
|
38 |
|
39 class nsImageLoadingContent : public nsIImageLoadingContent, |
|
40 public imgIOnloadBlocker |
|
41 { |
|
42 /* METHODS */ |
|
43 public: |
|
44 nsImageLoadingContent(); |
|
45 virtual ~nsImageLoadingContent(); |
|
46 |
|
47 NS_DECL_IMGINOTIFICATIONOBSERVER |
|
48 NS_DECL_NSIIMAGELOADINGCONTENT |
|
49 NS_DECL_IMGIONLOADBLOCKER |
|
50 |
|
51 // Web IDL binding methods. |
|
52 // Note that the XPCOM SetLoadingEnabled, AddObserver, RemoveObserver, |
|
53 // ForceImageState methods are OK for Web IDL bindings to use as well, |
|
54 // since none of them throw when called via the Web IDL bindings. |
|
55 |
|
56 bool LoadingEnabled() const { return mLoadingEnabled; } |
|
57 int16_t ImageBlockingStatus() const |
|
58 { |
|
59 return mImageBlockingStatus; |
|
60 } |
|
61 already_AddRefed<imgIRequest> |
|
62 GetRequest(int32_t aRequestType, mozilla::ErrorResult& aError); |
|
63 int32_t |
|
64 GetRequestType(imgIRequest* aRequest, mozilla::ErrorResult& aError); |
|
65 already_AddRefed<nsIURI> GetCurrentURI(mozilla::ErrorResult& aError); |
|
66 already_AddRefed<nsIStreamListener> |
|
67 LoadImageWithChannel(nsIChannel* aChannel, mozilla::ErrorResult& aError); |
|
68 void ForceReload(mozilla::ErrorResult& aError); |
|
69 |
|
70 |
|
71 |
|
72 protected: |
|
73 /** |
|
74 * LoadImage is called by subclasses when the appropriate |
|
75 * attributes (eg 'src' for <img> tags) change. The string passed |
|
76 * in is the new uri string; this consolidates the code for getting |
|
77 * the charset, constructing URI objects, and any other incidentals |
|
78 * into this superclass. |
|
79 * |
|
80 * @param aNewURI the URI spec to be loaded (may be a relative URI) |
|
81 * @param aForce If true, make sure to load the URI. If false, only |
|
82 * load if the URI is different from the currently loaded URI. |
|
83 * @param aNotify If true, nsIDocumentObserver state change notifications |
|
84 * will be sent as needed. |
|
85 */ |
|
86 nsresult LoadImage(const nsAString& aNewURI, bool aForce, |
|
87 bool aNotify); |
|
88 |
|
89 /** |
|
90 * ImageState is called by subclasses that are computing their content state. |
|
91 * The return value will have the NS_EVENT_STATE_BROKEN, |
|
92 * NS_EVENT_STATE_USERDISABLED, and NS_EVENT_STATE_SUPPRESSED bits set as |
|
93 * needed. Note that this state assumes that this node is "trying" to be an |
|
94 * image (so for example complete lack of attempt to load an image will lead |
|
95 * to NS_EVENT_STATE_BROKEN being set). Subclasses that are not "trying" to |
|
96 * be an image (eg an HTML <input> of type other than "image") should just |
|
97 * not call this method when computing their intrinsic state. |
|
98 */ |
|
99 mozilla::EventStates ImageState() const; |
|
100 |
|
101 /** |
|
102 * LoadImage is called by subclasses when the appropriate |
|
103 * attributes (eg 'src' for <img> tags) change. If callers have an |
|
104 * URI object already available, they should use this method. |
|
105 * |
|
106 * @param aNewURI the URI to be loaded |
|
107 * @param aForce If true, make sure to load the URI. If false, only |
|
108 * load if the URI is different from the currently loaded URI. |
|
109 * @param aNotify If true, nsIDocumentObserver state change notifications |
|
110 * will be sent as needed. |
|
111 * @param aDocument Optional parameter giving the document this node is in. |
|
112 * This is purely a performance optimization. |
|
113 * @param aLoadFlags Optional parameter specifying load flags to use for |
|
114 * the image load |
|
115 */ |
|
116 nsresult LoadImage(nsIURI* aNewURI, bool aForce, bool aNotify, |
|
117 nsIDocument* aDocument = nullptr, |
|
118 nsLoadFlags aLoadFlags = nsIRequest::LOAD_NORMAL); |
|
119 |
|
120 /** |
|
121 * helpers to get the document for this content (from the nodeinfo |
|
122 * and such). Not named GetOwnerDoc/GetCurrentDoc to prevent ambiguous |
|
123 * method names in subclasses |
|
124 * |
|
125 * @return the document we belong to |
|
126 */ |
|
127 nsIDocument* GetOurOwnerDoc(); |
|
128 nsIDocument* GetOurCurrentDoc(); |
|
129 |
|
130 /** |
|
131 * Helper function to get the frame associated with this content. Not named |
|
132 * GetPrimaryFrame to prevent ambiguous method names in subclasses. |
|
133 * |
|
134 * @return The frame which we belong to, or nullptr if it doesn't exist. |
|
135 */ |
|
136 nsIFrame* GetOurPrimaryFrame(); |
|
137 |
|
138 /** |
|
139 * Helper function to get the PresContext associated with this content's |
|
140 * frame. Not named GetPresContext to prevent ambiguous method names in |
|
141 * subclasses. |
|
142 * |
|
143 * @return The nsPresContext associated with our frame, or nullptr if either |
|
144 * the frame doesn't exist, or the frame's prescontext doesn't exist. |
|
145 */ |
|
146 nsPresContext* GetFramePresContext(); |
|
147 |
|
148 /** |
|
149 * CancelImageRequests is called by subclasses when they want to |
|
150 * cancel all image requests (for example when the subclass is |
|
151 * somehow not an image anymore). |
|
152 */ |
|
153 void CancelImageRequests(bool aNotify); |
|
154 |
|
155 /** |
|
156 * UseAsPrimaryRequest is called by subclasses when they have an existing |
|
157 * imgRequestProxy that they want this nsImageLoadingContent to use. This may |
|
158 * effectively be called instead of LoadImage or LoadImageWithChannel. |
|
159 * If aNotify is true, this method will notify on state changes. |
|
160 */ |
|
161 nsresult UseAsPrimaryRequest(imgRequestProxy* aRequest, bool aNotify); |
|
162 |
|
163 /** |
|
164 * Derived classes of nsImageLoadingContent MUST call |
|
165 * DestroyImageLoadingContent from their destructor, or earlier. It |
|
166 * does things that cannot be done in ~nsImageLoadingContent because |
|
167 * they rely on being able to QueryInterface to other derived classes, |
|
168 * which cannot happen once the derived class destructor has started |
|
169 * calling the base class destructors. |
|
170 */ |
|
171 void DestroyImageLoadingContent(); |
|
172 |
|
173 void ClearBrokenState() { mBroken = false; } |
|
174 |
|
175 // Sets blocking state only if the desired state is different from the |
|
176 // current one. See the comment for mBlockingOnload for more information. |
|
177 void SetBlockingOnload(bool aBlocking); |
|
178 |
|
179 /** |
|
180 * Returns the CORS mode that will be used for all future image loads. The |
|
181 * default implementation returns CORS_NONE unconditionally. |
|
182 */ |
|
183 virtual mozilla::CORSMode GetCORSMode(); |
|
184 |
|
185 // Subclasses are *required* to call BindToTree/UnbindFromTree. |
|
186 void BindToTree(nsIDocument* aDocument, nsIContent* aParent, |
|
187 nsIContent* aBindingParent, bool aCompileEventHandlers); |
|
188 void UnbindFromTree(bool aDeep, bool aNullParent); |
|
189 |
|
190 nsresult OnStopRequest(imgIRequest* aRequest, nsresult aStatus); |
|
191 void OnUnlockedDraw(); |
|
192 nsresult OnImageIsAnimated(imgIRequest *aRequest); |
|
193 |
|
194 private: |
|
195 /** |
|
196 * Struct used to manage the image observers. |
|
197 */ |
|
198 struct ImageObserver { |
|
199 ImageObserver(imgINotificationObserver* aObserver); |
|
200 ~ImageObserver(); |
|
201 |
|
202 nsCOMPtr<imgINotificationObserver> mObserver; |
|
203 ImageObserver* mNext; |
|
204 }; |
|
205 |
|
206 /** |
|
207 * Struct to report state changes |
|
208 */ |
|
209 struct AutoStateChanger { |
|
210 AutoStateChanger(nsImageLoadingContent* aImageContent, |
|
211 bool aNotify) : |
|
212 mImageContent(aImageContent), |
|
213 mNotify(aNotify) |
|
214 { |
|
215 mImageContent->mStateChangerDepth++; |
|
216 } |
|
217 ~AutoStateChanger() |
|
218 { |
|
219 mImageContent->mStateChangerDepth--; |
|
220 mImageContent->UpdateImageState(mNotify); |
|
221 } |
|
222 |
|
223 nsImageLoadingContent* mImageContent; |
|
224 bool mNotify; |
|
225 }; |
|
226 |
|
227 friend struct AutoStateChanger; |
|
228 |
|
229 /** |
|
230 * UpdateImageState recomputes the current state of this image loading |
|
231 * content and updates what ImageState() returns accordingly. It will also |
|
232 * fire a ContentStatesChanged() notification as needed if aNotify is true. |
|
233 */ |
|
234 void UpdateImageState(bool aNotify); |
|
235 |
|
236 /** |
|
237 * Method to fire an event once we know what's going on with the image load. |
|
238 * |
|
239 * @param aEventType "load" or "error" depending on how things went |
|
240 */ |
|
241 nsresult FireEvent(const nsAString& aEventType); |
|
242 |
|
243 protected: |
|
244 /** |
|
245 * Method to create an nsIURI object from the given string (will |
|
246 * handle getting the right charset, base, etc). You MUST pass in a |
|
247 * non-null document to this function. |
|
248 * |
|
249 * @param aSpec the string spec (from an HTML attribute, eg) |
|
250 * @param aDocument the document we belong to |
|
251 * @return the URI we want to be loading |
|
252 */ |
|
253 nsresult StringToURI(const nsAString& aSpec, nsIDocument* aDocument, |
|
254 nsIURI** aURI); |
|
255 |
|
256 void CreateStaticImageClone(nsImageLoadingContent* aDest) const; |
|
257 |
|
258 /** |
|
259 * Prepare and returns a reference to the "next request". If there's already |
|
260 * a _usable_ current request (one with SIZE_AVAILABLE), this request is |
|
261 * "pending" until it becomes usable. Otherwise, this becomes the current |
|
262 * request. |
|
263 */ |
|
264 nsRefPtr<imgRequestProxy>& PrepareNextRequest(); |
|
265 |
|
266 /** |
|
267 * Called when we would normally call PrepareNextRequest(), but the request was |
|
268 * blocked. |
|
269 */ |
|
270 void SetBlockedRequest(nsIURI* aURI, int16_t aContentDecision); |
|
271 |
|
272 /** |
|
273 * Returns a COMPtr reference to the current/pending image requests, cleaning |
|
274 * up and canceling anything that was there before. Note that if you just want |
|
275 * to get rid of one of the requests, you should call |
|
276 * Clear*Request(NS_BINDING_ABORTED) instead, since it passes a more appropriate |
|
277 * aReason than Prepare*Request() does (NS_ERROR_IMAGE_SRC_CHANGED). |
|
278 */ |
|
279 nsRefPtr<imgRequestProxy>& PrepareCurrentRequest(); |
|
280 nsRefPtr<imgRequestProxy>& PreparePendingRequest(); |
|
281 |
|
282 /** |
|
283 * Switch our pending request to be our current request. |
|
284 * mPendingRequest must be non-null! |
|
285 */ |
|
286 void MakePendingRequestCurrent(); |
|
287 |
|
288 /** |
|
289 * Cancels and nulls-out the "current" and "pending" requests if they exist. |
|
290 */ |
|
291 void ClearCurrentRequest(nsresult aReason, uint32_t aFlags); |
|
292 void ClearPendingRequest(nsresult aReason, uint32_t aFlags); |
|
293 |
|
294 /** |
|
295 * Retrieve a pointer to the 'registered with the refresh driver' flag for |
|
296 * which a particular image request corresponds. |
|
297 * |
|
298 * @returns A pointer to the boolean flag for a given image request, or |
|
299 * |nullptr| if the request is not either |mPendingRequest| or |
|
300 * |mCurrentRequest|. |
|
301 */ |
|
302 bool* GetRegisteredFlagForRequest(imgIRequest* aRequest); |
|
303 |
|
304 /** |
|
305 * Reset animation of the current request if |mNewRequestsWillNeedAnimationReset| |
|
306 * was true when the request was prepared. |
|
307 */ |
|
308 void ResetAnimationIfNeeded(); |
|
309 |
|
310 /** |
|
311 * Static helper method to tell us if we have the size of a request. The |
|
312 * image may be null. |
|
313 */ |
|
314 static bool HaveSize(imgIRequest *aImage); |
|
315 |
|
316 /** |
|
317 * Adds/Removes a given imgIRequest from our document's tracker. |
|
318 * |
|
319 * No-op if aImage is null. |
|
320 * |
|
321 * REQUEST_DISCARD passed to UntrackImage means we request the discard of the |
|
322 * decoded data of the image. |
|
323 */ |
|
324 void TrackImage(imgIRequest* aImage); |
|
325 enum { |
|
326 REQUEST_DISCARD = 0x1 |
|
327 }; |
|
328 void UntrackImage(imgIRequest* aImage, uint32_t aFlags = 0); |
|
329 |
|
330 /* MEMBERS */ |
|
331 nsRefPtr<imgRequestProxy> mCurrentRequest; |
|
332 nsRefPtr<imgRequestProxy> mPendingRequest; |
|
333 uint32_t mCurrentRequestFlags; |
|
334 uint32_t mPendingRequestFlags; |
|
335 |
|
336 enum { |
|
337 // Set if the request needs ResetAnimation called on it. |
|
338 REQUEST_NEEDS_ANIMATION_RESET = 0x00000001U, |
|
339 // Set if the request is blocking onload. |
|
340 REQUEST_BLOCKS_ONLOAD = 0x00000002U, |
|
341 // Set if the request is currently tracked with the document. |
|
342 REQUEST_IS_TRACKED = 0x00000004U |
|
343 }; |
|
344 |
|
345 // If the image was blocked or if there was an error loading, it's nice to |
|
346 // still keep track of what the URI was despite not having an imgIRequest. |
|
347 // We only maintain this in those situations (in the common case, this is |
|
348 // always null). |
|
349 nsCOMPtr<nsIURI> mCurrentURI; |
|
350 |
|
351 private: |
|
352 /** |
|
353 * Typically we will have only one observer (our frame in the screen |
|
354 * prescontext), so we want to only make space for one and to |
|
355 * heap-allocate anything past that (saves memory and malloc churn |
|
356 * in the common case). The storage is a linked list, we just |
|
357 * happen to actually hold the first observer instead of a pointer |
|
358 * to it. |
|
359 */ |
|
360 ImageObserver mObserverList; |
|
361 |
|
362 /** |
|
363 * When mIsImageStateForced is true, this holds the ImageState that we'll |
|
364 * return in ImageState(). |
|
365 */ |
|
366 mozilla::EventStates mForcedImageState; |
|
367 |
|
368 int16_t mImageBlockingStatus; |
|
369 bool mLoadingEnabled : 1; |
|
370 |
|
371 /** |
|
372 * When true, we return mForcedImageState from ImageState(). |
|
373 */ |
|
374 bool mIsImageStateForced : 1; |
|
375 |
|
376 /** |
|
377 * The state we had the last time we checked whether we needed to notify the |
|
378 * document of a state change. These are maintained by UpdateImageState. |
|
379 */ |
|
380 bool mLoading : 1; |
|
381 bool mBroken : 1; |
|
382 bool mUserDisabled : 1; |
|
383 bool mSuppressed : 1; |
|
384 bool mFireEventsOnDecode : 1; |
|
385 |
|
386 protected: |
|
387 /** |
|
388 * A hack to get animations to reset, see bug 594771. On requests |
|
389 * that originate from setting .src, we mark them for needing their animation |
|
390 * reset when they are ready. mNewRequestsWillNeedAnimationReset is set to |
|
391 * true while preparing such requests (as a hack around needing to change an |
|
392 * interface), and the other two booleans store which of the current |
|
393 * and pending requests are of the sort that need their animation restarted. |
|
394 */ |
|
395 bool mNewRequestsWillNeedAnimationReset : 1; |
|
396 |
|
397 private: |
|
398 /* The number of nested AutoStateChangers currently tracking our state. */ |
|
399 uint8_t mStateChangerDepth; |
|
400 |
|
401 // Flags to indicate whether each of the current and pending requests are |
|
402 // registered with the refresh driver. |
|
403 bool mCurrentRequestRegistered; |
|
404 bool mPendingRequestRegistered; |
|
405 |
|
406 // True when FrameCreate has been called but FrameDestroy has not. |
|
407 bool mFrameCreateCalled; |
|
408 |
|
409 uint32_t mVisibleCount; |
|
410 }; |
|
411 |
|
412 #endif // nsImageLoadingContent_h__ |