1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsImageLoadingContent.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,412 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +// vim: ft=cpp tw=78 sw=2 et ts=2 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * A base class which implements nsIImageLoadingContent and can be 1.12 + * subclassed by various content nodes that want to provide image 1.13 + * loading functionality (eg <img>, <object>, etc). 1.14 + */ 1.15 + 1.16 +#ifndef nsImageLoadingContent_h__ 1.17 +#define nsImageLoadingContent_h__ 1.18 + 1.19 +#include "imgINotificationObserver.h" 1.20 +#include "imgIOnloadBlocker.h" 1.21 +#include "mozilla/CORSMode.h" 1.22 +#include "mozilla/EventStates.h" 1.23 +#include "nsCOMPtr.h" 1.24 +#include "nsIImageLoadingContent.h" 1.25 +#include "nsIRequest.h" 1.26 +#include "mozilla/ErrorResult.h" 1.27 +#include "nsAutoPtr.h" 1.28 + 1.29 +class nsIURI; 1.30 +class nsIDocument; 1.31 +class imgILoader; 1.32 +class nsIIOService; 1.33 +class nsPresContext; 1.34 +class nsIContent; 1.35 +class imgRequestProxy; 1.36 + 1.37 +#ifdef LoadImage 1.38 +// Undefine LoadImage to prevent naming conflict with Windows. 1.39 +#undef LoadImage 1.40 +#endif 1.41 + 1.42 +class nsImageLoadingContent : public nsIImageLoadingContent, 1.43 + public imgIOnloadBlocker 1.44 +{ 1.45 + /* METHODS */ 1.46 +public: 1.47 + nsImageLoadingContent(); 1.48 + virtual ~nsImageLoadingContent(); 1.49 + 1.50 + NS_DECL_IMGINOTIFICATIONOBSERVER 1.51 + NS_DECL_NSIIMAGELOADINGCONTENT 1.52 + NS_DECL_IMGIONLOADBLOCKER 1.53 + 1.54 + // Web IDL binding methods. 1.55 + // Note that the XPCOM SetLoadingEnabled, AddObserver, RemoveObserver, 1.56 + // ForceImageState methods are OK for Web IDL bindings to use as well, 1.57 + // since none of them throw when called via the Web IDL bindings. 1.58 + 1.59 + bool LoadingEnabled() const { return mLoadingEnabled; } 1.60 + int16_t ImageBlockingStatus() const 1.61 + { 1.62 + return mImageBlockingStatus; 1.63 + } 1.64 + already_AddRefed<imgIRequest> 1.65 + GetRequest(int32_t aRequestType, mozilla::ErrorResult& aError); 1.66 + int32_t 1.67 + GetRequestType(imgIRequest* aRequest, mozilla::ErrorResult& aError); 1.68 + already_AddRefed<nsIURI> GetCurrentURI(mozilla::ErrorResult& aError); 1.69 + already_AddRefed<nsIStreamListener> 1.70 + LoadImageWithChannel(nsIChannel* aChannel, mozilla::ErrorResult& aError); 1.71 + void ForceReload(mozilla::ErrorResult& aError); 1.72 + 1.73 + 1.74 + 1.75 +protected: 1.76 + /** 1.77 + * LoadImage is called by subclasses when the appropriate 1.78 + * attributes (eg 'src' for <img> tags) change. The string passed 1.79 + * in is the new uri string; this consolidates the code for getting 1.80 + * the charset, constructing URI objects, and any other incidentals 1.81 + * into this superclass. 1.82 + * 1.83 + * @param aNewURI the URI spec to be loaded (may be a relative URI) 1.84 + * @param aForce If true, make sure to load the URI. If false, only 1.85 + * load if the URI is different from the currently loaded URI. 1.86 + * @param aNotify If true, nsIDocumentObserver state change notifications 1.87 + * will be sent as needed. 1.88 + */ 1.89 + nsresult LoadImage(const nsAString& aNewURI, bool aForce, 1.90 + bool aNotify); 1.91 + 1.92 + /** 1.93 + * ImageState is called by subclasses that are computing their content state. 1.94 + * The return value will have the NS_EVENT_STATE_BROKEN, 1.95 + * NS_EVENT_STATE_USERDISABLED, and NS_EVENT_STATE_SUPPRESSED bits set as 1.96 + * needed. Note that this state assumes that this node is "trying" to be an 1.97 + * image (so for example complete lack of attempt to load an image will lead 1.98 + * to NS_EVENT_STATE_BROKEN being set). Subclasses that are not "trying" to 1.99 + * be an image (eg an HTML <input> of type other than "image") should just 1.100 + * not call this method when computing their intrinsic state. 1.101 + */ 1.102 + mozilla::EventStates ImageState() const; 1.103 + 1.104 + /** 1.105 + * LoadImage is called by subclasses when the appropriate 1.106 + * attributes (eg 'src' for <img> tags) change. If callers have an 1.107 + * URI object already available, they should use this method. 1.108 + * 1.109 + * @param aNewURI the URI to be loaded 1.110 + * @param aForce If true, make sure to load the URI. If false, only 1.111 + * load if the URI is different from the currently loaded URI. 1.112 + * @param aNotify If true, nsIDocumentObserver state change notifications 1.113 + * will be sent as needed. 1.114 + * @param aDocument Optional parameter giving the document this node is in. 1.115 + * This is purely a performance optimization. 1.116 + * @param aLoadFlags Optional parameter specifying load flags to use for 1.117 + * the image load 1.118 + */ 1.119 + nsresult LoadImage(nsIURI* aNewURI, bool aForce, bool aNotify, 1.120 + nsIDocument* aDocument = nullptr, 1.121 + nsLoadFlags aLoadFlags = nsIRequest::LOAD_NORMAL); 1.122 + 1.123 + /** 1.124 + * helpers to get the document for this content (from the nodeinfo 1.125 + * and such). Not named GetOwnerDoc/GetCurrentDoc to prevent ambiguous 1.126 + * method names in subclasses 1.127 + * 1.128 + * @return the document we belong to 1.129 + */ 1.130 + nsIDocument* GetOurOwnerDoc(); 1.131 + nsIDocument* GetOurCurrentDoc(); 1.132 + 1.133 + /** 1.134 + * Helper function to get the frame associated with this content. Not named 1.135 + * GetPrimaryFrame to prevent ambiguous method names in subclasses. 1.136 + * 1.137 + * @return The frame which we belong to, or nullptr if it doesn't exist. 1.138 + */ 1.139 + nsIFrame* GetOurPrimaryFrame(); 1.140 + 1.141 + /** 1.142 + * Helper function to get the PresContext associated with this content's 1.143 + * frame. Not named GetPresContext to prevent ambiguous method names in 1.144 + * subclasses. 1.145 + * 1.146 + * @return The nsPresContext associated with our frame, or nullptr if either 1.147 + * the frame doesn't exist, or the frame's prescontext doesn't exist. 1.148 + */ 1.149 + nsPresContext* GetFramePresContext(); 1.150 + 1.151 + /** 1.152 + * CancelImageRequests is called by subclasses when they want to 1.153 + * cancel all image requests (for example when the subclass is 1.154 + * somehow not an image anymore). 1.155 + */ 1.156 + void CancelImageRequests(bool aNotify); 1.157 + 1.158 + /** 1.159 + * UseAsPrimaryRequest is called by subclasses when they have an existing 1.160 + * imgRequestProxy that they want this nsImageLoadingContent to use. This may 1.161 + * effectively be called instead of LoadImage or LoadImageWithChannel. 1.162 + * If aNotify is true, this method will notify on state changes. 1.163 + */ 1.164 + nsresult UseAsPrimaryRequest(imgRequestProxy* aRequest, bool aNotify); 1.165 + 1.166 + /** 1.167 + * Derived classes of nsImageLoadingContent MUST call 1.168 + * DestroyImageLoadingContent from their destructor, or earlier. It 1.169 + * does things that cannot be done in ~nsImageLoadingContent because 1.170 + * they rely on being able to QueryInterface to other derived classes, 1.171 + * which cannot happen once the derived class destructor has started 1.172 + * calling the base class destructors. 1.173 + */ 1.174 + void DestroyImageLoadingContent(); 1.175 + 1.176 + void ClearBrokenState() { mBroken = false; } 1.177 + 1.178 + // Sets blocking state only if the desired state is different from the 1.179 + // current one. See the comment for mBlockingOnload for more information. 1.180 + void SetBlockingOnload(bool aBlocking); 1.181 + 1.182 + /** 1.183 + * Returns the CORS mode that will be used for all future image loads. The 1.184 + * default implementation returns CORS_NONE unconditionally. 1.185 + */ 1.186 + virtual mozilla::CORSMode GetCORSMode(); 1.187 + 1.188 + // Subclasses are *required* to call BindToTree/UnbindFromTree. 1.189 + void BindToTree(nsIDocument* aDocument, nsIContent* aParent, 1.190 + nsIContent* aBindingParent, bool aCompileEventHandlers); 1.191 + void UnbindFromTree(bool aDeep, bool aNullParent); 1.192 + 1.193 + nsresult OnStopRequest(imgIRequest* aRequest, nsresult aStatus); 1.194 + void OnUnlockedDraw(); 1.195 + nsresult OnImageIsAnimated(imgIRequest *aRequest); 1.196 + 1.197 +private: 1.198 + /** 1.199 + * Struct used to manage the image observers. 1.200 + */ 1.201 + struct ImageObserver { 1.202 + ImageObserver(imgINotificationObserver* aObserver); 1.203 + ~ImageObserver(); 1.204 + 1.205 + nsCOMPtr<imgINotificationObserver> mObserver; 1.206 + ImageObserver* mNext; 1.207 + }; 1.208 + 1.209 + /** 1.210 + * Struct to report state changes 1.211 + */ 1.212 + struct AutoStateChanger { 1.213 + AutoStateChanger(nsImageLoadingContent* aImageContent, 1.214 + bool aNotify) : 1.215 + mImageContent(aImageContent), 1.216 + mNotify(aNotify) 1.217 + { 1.218 + mImageContent->mStateChangerDepth++; 1.219 + } 1.220 + ~AutoStateChanger() 1.221 + { 1.222 + mImageContent->mStateChangerDepth--; 1.223 + mImageContent->UpdateImageState(mNotify); 1.224 + } 1.225 + 1.226 + nsImageLoadingContent* mImageContent; 1.227 + bool mNotify; 1.228 + }; 1.229 + 1.230 + friend struct AutoStateChanger; 1.231 + 1.232 + /** 1.233 + * UpdateImageState recomputes the current state of this image loading 1.234 + * content and updates what ImageState() returns accordingly. It will also 1.235 + * fire a ContentStatesChanged() notification as needed if aNotify is true. 1.236 + */ 1.237 + void UpdateImageState(bool aNotify); 1.238 + 1.239 + /** 1.240 + * Method to fire an event once we know what's going on with the image load. 1.241 + * 1.242 + * @param aEventType "load" or "error" depending on how things went 1.243 + */ 1.244 + nsresult FireEvent(const nsAString& aEventType); 1.245 + 1.246 +protected: 1.247 + /** 1.248 + * Method to create an nsIURI object from the given string (will 1.249 + * handle getting the right charset, base, etc). You MUST pass in a 1.250 + * non-null document to this function. 1.251 + * 1.252 + * @param aSpec the string spec (from an HTML attribute, eg) 1.253 + * @param aDocument the document we belong to 1.254 + * @return the URI we want to be loading 1.255 + */ 1.256 + nsresult StringToURI(const nsAString& aSpec, nsIDocument* aDocument, 1.257 + nsIURI** aURI); 1.258 + 1.259 + void CreateStaticImageClone(nsImageLoadingContent* aDest) const; 1.260 + 1.261 + /** 1.262 + * Prepare and returns a reference to the "next request". If there's already 1.263 + * a _usable_ current request (one with SIZE_AVAILABLE), this request is 1.264 + * "pending" until it becomes usable. Otherwise, this becomes the current 1.265 + * request. 1.266 + */ 1.267 + nsRefPtr<imgRequestProxy>& PrepareNextRequest(); 1.268 + 1.269 + /** 1.270 + * Called when we would normally call PrepareNextRequest(), but the request was 1.271 + * blocked. 1.272 + */ 1.273 + void SetBlockedRequest(nsIURI* aURI, int16_t aContentDecision); 1.274 + 1.275 + /** 1.276 + * Returns a COMPtr reference to the current/pending image requests, cleaning 1.277 + * up and canceling anything that was there before. Note that if you just want 1.278 + * to get rid of one of the requests, you should call 1.279 + * Clear*Request(NS_BINDING_ABORTED) instead, since it passes a more appropriate 1.280 + * aReason than Prepare*Request() does (NS_ERROR_IMAGE_SRC_CHANGED). 1.281 + */ 1.282 + nsRefPtr<imgRequestProxy>& PrepareCurrentRequest(); 1.283 + nsRefPtr<imgRequestProxy>& PreparePendingRequest(); 1.284 + 1.285 + /** 1.286 + * Switch our pending request to be our current request. 1.287 + * mPendingRequest must be non-null! 1.288 + */ 1.289 + void MakePendingRequestCurrent(); 1.290 + 1.291 + /** 1.292 + * Cancels and nulls-out the "current" and "pending" requests if they exist. 1.293 + */ 1.294 + void ClearCurrentRequest(nsresult aReason, uint32_t aFlags); 1.295 + void ClearPendingRequest(nsresult aReason, uint32_t aFlags); 1.296 + 1.297 + /** 1.298 + * Retrieve a pointer to the 'registered with the refresh driver' flag for 1.299 + * which a particular image request corresponds. 1.300 + * 1.301 + * @returns A pointer to the boolean flag for a given image request, or 1.302 + * |nullptr| if the request is not either |mPendingRequest| or 1.303 + * |mCurrentRequest|. 1.304 + */ 1.305 + bool* GetRegisteredFlagForRequest(imgIRequest* aRequest); 1.306 + 1.307 + /** 1.308 + * Reset animation of the current request if |mNewRequestsWillNeedAnimationReset| 1.309 + * was true when the request was prepared. 1.310 + */ 1.311 + void ResetAnimationIfNeeded(); 1.312 + 1.313 + /** 1.314 + * Static helper method to tell us if we have the size of a request. The 1.315 + * image may be null. 1.316 + */ 1.317 + static bool HaveSize(imgIRequest *aImage); 1.318 + 1.319 + /** 1.320 + * Adds/Removes a given imgIRequest from our document's tracker. 1.321 + * 1.322 + * No-op if aImage is null. 1.323 + * 1.324 + * REQUEST_DISCARD passed to UntrackImage means we request the discard of the 1.325 + * decoded data of the image. 1.326 + */ 1.327 + void TrackImage(imgIRequest* aImage); 1.328 + enum { 1.329 + REQUEST_DISCARD = 0x1 1.330 + }; 1.331 + void UntrackImage(imgIRequest* aImage, uint32_t aFlags = 0); 1.332 + 1.333 + /* MEMBERS */ 1.334 + nsRefPtr<imgRequestProxy> mCurrentRequest; 1.335 + nsRefPtr<imgRequestProxy> mPendingRequest; 1.336 + uint32_t mCurrentRequestFlags; 1.337 + uint32_t mPendingRequestFlags; 1.338 + 1.339 + enum { 1.340 + // Set if the request needs ResetAnimation called on it. 1.341 + REQUEST_NEEDS_ANIMATION_RESET = 0x00000001U, 1.342 + // Set if the request is blocking onload. 1.343 + REQUEST_BLOCKS_ONLOAD = 0x00000002U, 1.344 + // Set if the request is currently tracked with the document. 1.345 + REQUEST_IS_TRACKED = 0x00000004U 1.346 + }; 1.347 + 1.348 + // If the image was blocked or if there was an error loading, it's nice to 1.349 + // still keep track of what the URI was despite not having an imgIRequest. 1.350 + // We only maintain this in those situations (in the common case, this is 1.351 + // always null). 1.352 + nsCOMPtr<nsIURI> mCurrentURI; 1.353 + 1.354 +private: 1.355 + /** 1.356 + * Typically we will have only one observer (our frame in the screen 1.357 + * prescontext), so we want to only make space for one and to 1.358 + * heap-allocate anything past that (saves memory and malloc churn 1.359 + * in the common case). The storage is a linked list, we just 1.360 + * happen to actually hold the first observer instead of a pointer 1.361 + * to it. 1.362 + */ 1.363 + ImageObserver mObserverList; 1.364 + 1.365 + /** 1.366 + * When mIsImageStateForced is true, this holds the ImageState that we'll 1.367 + * return in ImageState(). 1.368 + */ 1.369 + mozilla::EventStates mForcedImageState; 1.370 + 1.371 + int16_t mImageBlockingStatus; 1.372 + bool mLoadingEnabled : 1; 1.373 + 1.374 + /** 1.375 + * When true, we return mForcedImageState from ImageState(). 1.376 + */ 1.377 + bool mIsImageStateForced : 1; 1.378 + 1.379 + /** 1.380 + * The state we had the last time we checked whether we needed to notify the 1.381 + * document of a state change. These are maintained by UpdateImageState. 1.382 + */ 1.383 + bool mLoading : 1; 1.384 + bool mBroken : 1; 1.385 + bool mUserDisabled : 1; 1.386 + bool mSuppressed : 1; 1.387 + bool mFireEventsOnDecode : 1; 1.388 + 1.389 +protected: 1.390 + /** 1.391 + * A hack to get animations to reset, see bug 594771. On requests 1.392 + * that originate from setting .src, we mark them for needing their animation 1.393 + * reset when they are ready. mNewRequestsWillNeedAnimationReset is set to 1.394 + * true while preparing such requests (as a hack around needing to change an 1.395 + * interface), and the other two booleans store which of the current 1.396 + * and pending requests are of the sort that need their animation restarted. 1.397 + */ 1.398 + bool mNewRequestsWillNeedAnimationReset : 1; 1.399 + 1.400 +private: 1.401 + /* The number of nested AutoStateChangers currently tracking our state. */ 1.402 + uint8_t mStateChangerDepth; 1.403 + 1.404 + // Flags to indicate whether each of the current and pending requests are 1.405 + // registered with the refresh driver. 1.406 + bool mCurrentRequestRegistered; 1.407 + bool mPendingRequestRegistered; 1.408 + 1.409 + // True when FrameCreate has been called but FrameDestroy has not. 1.410 + bool mFrameCreateCalled; 1.411 + 1.412 + uint32_t mVisibleCount; 1.413 +}; 1.414 + 1.415 +#endif // nsImageLoadingContent_h__