1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/src/imgStatusTracker.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,345 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * 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 +#ifndef imgStatusTracker_h__ 1.11 +#define imgStatusTracker_h__ 1.12 + 1.13 +class imgDecoderObserver; 1.14 +class imgIContainer; 1.15 +class imgStatusNotifyRunnable; 1.16 +class imgRequestNotifyRunnable; 1.17 +class imgStatusTrackerObserver; 1.18 +class nsIRunnable; 1.19 + 1.20 +#include "mozilla/RefPtr.h" 1.21 +#include "mozilla/WeakPtr.h" 1.22 +#include "nsCOMPtr.h" 1.23 +#include "nsTObserverArray.h" 1.24 +#include "nsThreadUtils.h" 1.25 +#include "nsRect.h" 1.26 +#include "imgRequestProxy.h" 1.27 + 1.28 +namespace mozilla { 1.29 +namespace image { 1.30 + 1.31 +class Image; 1.32 + 1.33 +struct ImageStatusDiff 1.34 +{ 1.35 + ImageStatusDiff() 1.36 + : invalidRect() 1.37 + , diffState(0) 1.38 + , diffImageStatus(0) 1.39 + , unblockedOnload(false) 1.40 + , unsetDecodeStarted(false) 1.41 + , foundError(false) 1.42 + , foundIsMultipart(false) 1.43 + , foundLastPart(false) 1.44 + , gotDecoded(false) 1.45 + { } 1.46 + 1.47 + static ImageStatusDiff NoChange() { return ImageStatusDiff(); } 1.48 + bool IsNoChange() const { return *this == NoChange(); } 1.49 + 1.50 + bool operator!=(const ImageStatusDiff& aOther) const { return !(*this == aOther); } 1.51 + bool operator==(const ImageStatusDiff& aOther) const { 1.52 + return aOther.invalidRect == invalidRect 1.53 + && aOther.diffState == diffState 1.54 + && aOther.diffImageStatus == diffImageStatus 1.55 + && aOther.unblockedOnload == unblockedOnload 1.56 + && aOther.unsetDecodeStarted == unsetDecodeStarted 1.57 + && aOther.foundError == foundError 1.58 + && aOther.foundIsMultipart == foundIsMultipart 1.59 + && aOther.foundLastPart == foundLastPart 1.60 + && aOther.gotDecoded == gotDecoded; 1.61 + } 1.62 + 1.63 + void Combine(const ImageStatusDiff& aOther) { 1.64 + invalidRect = invalidRect.Union(aOther.invalidRect); 1.65 + diffState |= aOther.diffState; 1.66 + diffImageStatus |= aOther.diffImageStatus; 1.67 + unblockedOnload = unblockedOnload || aOther.unblockedOnload; 1.68 + unsetDecodeStarted = unsetDecodeStarted || aOther.unsetDecodeStarted; 1.69 + foundError = foundError || aOther.foundError; 1.70 + foundIsMultipart = foundIsMultipart || aOther.foundIsMultipart; 1.71 + foundLastPart = foundLastPart || aOther.foundLastPart; 1.72 + gotDecoded = gotDecoded || aOther.gotDecoded; 1.73 + } 1.74 + 1.75 + nsIntRect invalidRect; 1.76 + uint32_t diffState; 1.77 + uint32_t diffImageStatus; 1.78 + bool unblockedOnload : 1; 1.79 + bool unsetDecodeStarted : 1; 1.80 + bool foundError : 1; 1.81 + bool foundIsMultipart : 1; 1.82 + bool foundLastPart : 1; 1.83 + bool gotDecoded : 1; 1.84 +}; 1.85 + 1.86 +enum { 1.87 + stateRequestStarted = 1u << 0, 1.88 + stateHasSize = 1u << 1, 1.89 + stateDecodeStarted = 1u << 2, 1.90 + stateDecodeStopped = 1u << 3, 1.91 + stateFrameStopped = 1u << 4, 1.92 + stateRequestStopped = 1u << 5, 1.93 + stateBlockingOnload = 1u << 6, 1.94 + stateImageIsAnimated = 1u << 7 1.95 +}; 1.96 + 1.97 +} // namespace image 1.98 +} // namespace mozilla 1.99 + 1.100 +/* 1.101 + * The image status tracker is a class that encapsulates all the loading and 1.102 + * decoding status about an Image, and makes it possible to send notifications 1.103 + * to imgRequestProxys, both synchronously (i.e., the status now) and 1.104 + * asynchronously (the status later). 1.105 + * 1.106 + * When a new proxy needs to be notified of the current state of an image, call 1.107 + * the Notify() method on this class with the relevant proxy as its argument, 1.108 + * and the notifications will be replayed to the proxy asynchronously. 1.109 + */ 1.110 + 1.111 + 1.112 +class imgStatusTracker : public mozilla::SupportsWeakPtr<imgStatusTracker> 1.113 +{ 1.114 +public: 1.115 + MOZ_DECLARE_REFCOUNTED_TYPENAME(imgStatusTracker) 1.116 + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(imgStatusTracker) 1.117 + 1.118 + // aImage is the image that this status tracker will pass to the 1.119 + // imgRequestProxys in SyncNotify() and EmulateRequestFinished(), and must be 1.120 + // alive as long as this instance is, because we hold a weak reference to it. 1.121 + imgStatusTracker(mozilla::image::Image* aImage); 1.122 + virtual ~imgStatusTracker(); 1.123 + 1.124 + // Image-setter, for imgStatusTrackers created by imgRequest::Init, which 1.125 + // are created before their Image is created. This method should only 1.126 + // be called once, and only on an imgStatusTracker that was initialized 1.127 + // without an image. 1.128 + void SetImage(mozilla::image::Image* aImage); 1.129 + 1.130 + // Image resetter, for when mImage is about to go out of scope. mImage is a 1.131 + // weak reference, and thus must be set to null when it's object is deleted. 1.132 + void ResetImage(); 1.133 + 1.134 + // Inform this status tracker that it is associated with a multipart image. 1.135 + void SetIsMultipart() { mIsMultipart = true; } 1.136 + 1.137 + // Schedule an asynchronous "replaying" of all the notifications that would 1.138 + // have to happen to put us in the current state. 1.139 + // We will also take note of any notifications that happen between the time 1.140 + // Notify() is called and when we call SyncNotify on |proxy|, and replay them 1.141 + // as well. 1.142 + // Should be called on the main thread only, since imgRequestProxy and GetURI 1.143 + // are not threadsafe. 1.144 + void Notify(imgRequestProxy* proxy); 1.145 + 1.146 + // Schedule an asynchronous "replaying" of all the notifications that would 1.147 + // have to happen to put us in the state we are in right now. 1.148 + // Unlike Notify(), does *not* take into account future notifications. 1.149 + // This is only useful if you do not have an imgRequest, e.g., if you are a 1.150 + // static request returned from imgIRequest::GetStaticRequest(). 1.151 + // Should be called on the main thread only, since imgRequestProxy and GetURI 1.152 + // are not threadsafe. 1.153 + void NotifyCurrentState(imgRequestProxy* proxy); 1.154 + 1.155 + // "Replay" all of the notifications that would have to happen to put us in 1.156 + // the state we're currently in. 1.157 + // Only use this if you're already servicing an asynchronous call (e.g. 1.158 + // OnStartRequest). 1.159 + // Should be called on the main thread only, since imgRequestProxy and GetURI 1.160 + // are not threadsafe. 1.161 + void SyncNotify(imgRequestProxy* proxy); 1.162 + 1.163 + // Send some notifications that would be necessary to make |proxy| believe 1.164 + // the request is finished downloading and decoding. We only send 1.165 + // OnStopRequest and UnblockOnload, and only if necessary. 1.166 + void EmulateRequestFinished(imgRequestProxy* proxy, nsresult aStatus); 1.167 + 1.168 + // We manage a set of consumers that are using an image and thus concerned 1.169 + // with its status. Weak pointers. 1.170 + void AddConsumer(imgRequestProxy* aConsumer); 1.171 + bool RemoveConsumer(imgRequestProxy* aConsumer, nsresult aStatus); 1.172 + size_t ConsumerCount() const { 1.173 + MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only"); 1.174 + return mConsumers.Length(); 1.175 + } 1.176 + 1.177 + // This is intentionally non-general because its sole purpose is to support an 1.178 + // some obscure network priority logic in imgRequest. That stuff could probably 1.179 + // be improved, but it's too scary to mess with at the moment. 1.180 + bool FirstConsumerIs(imgRequestProxy* aConsumer); 1.181 + 1.182 + void AdoptConsumers(imgStatusTracker* aTracker) { 1.183 + MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only"); 1.184 + MOZ_ASSERT(aTracker); 1.185 + mConsumers = aTracker->mConsumers; 1.186 + } 1.187 + 1.188 + // Returns whether we are in the process of loading; that is, whether we have 1.189 + // not received OnStopRequest. 1.190 + bool IsLoading() const; 1.191 + 1.192 + // Get the current image status (as in imgIRequest). 1.193 + uint32_t GetImageStatus() const; 1.194 + 1.195 + // Following are all the notification methods. You must call the Record 1.196 + // variant on this status tracker, then call the Send variant for each proxy 1.197 + // you want to notify. 1.198 + 1.199 + // Call when the request is being cancelled. 1.200 + void RecordCancel(); 1.201 + 1.202 + // Shorthand for recording all the load notifications: StartRequest, 1.203 + // StartContainer, StopRequest. 1.204 + void RecordLoaded(); 1.205 + 1.206 + // Shorthand for recording all the decode notifications: StartDecode, 1.207 + // StartFrame, DataAvailable, StopFrame, StopDecode. 1.208 + void RecordDecoded(); 1.209 + 1.210 + /* non-virtual imgDecoderObserver methods */ 1.211 + // Functions with prefix Send- are main thread only, since they contain calls 1.212 + // to imgRequestProxy functions, which are expected on the main thread. 1.213 + void RecordStartDecode(); 1.214 + void SendStartDecode(imgRequestProxy* aProxy); 1.215 + void RecordStartContainer(imgIContainer* aContainer); 1.216 + void SendStartContainer(imgRequestProxy* aProxy); 1.217 + void RecordStartFrame(); 1.218 + // No SendStartFrame since it's not observed below us. 1.219 + void RecordFrameChanged(const nsIntRect* aDirtyRect); 1.220 + void SendFrameChanged(imgRequestProxy* aProxy, const nsIntRect* aDirtyRect); 1.221 + void RecordStopFrame(); 1.222 + void SendStopFrame(imgRequestProxy* aProxy); 1.223 + void RecordStopDecode(nsresult statusg); 1.224 + void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus); 1.225 + void RecordDiscard(); 1.226 + void SendDiscard(imgRequestProxy* aProxy); 1.227 + void RecordUnlockedDraw(); 1.228 + void SendUnlockedDraw(imgRequestProxy* aProxy); 1.229 + void RecordImageIsAnimated(); 1.230 + void SendImageIsAnimated(imgRequestProxy *aProxy); 1.231 + 1.232 + /* non-virtual sort-of-nsIRequestObserver methods */ 1.233 + // Functions with prefix Send- are main thread only, since they contain calls 1.234 + // to imgRequestProxy functions, which are expected on the main thread. 1.235 + void RecordStartRequest(); 1.236 + void SendStartRequest(imgRequestProxy* aProxy); 1.237 + void RecordStopRequest(bool aLastPart, nsresult aStatus); 1.238 + void SendStopRequest(imgRequestProxy* aProxy, bool aLastPart, nsresult aStatus); 1.239 + 1.240 + // All main thread only because they call functions (like SendStartRequest) 1.241 + // which are expected to be called on the main thread. 1.242 + void OnStartRequest(); 1.243 + // OnDataAvailable will dispatch a call to itself onto the main thread if not 1.244 + // called there. 1.245 + void OnDataAvailable(); 1.246 + void OnStopRequest(bool aLastPart, nsresult aStatus); 1.247 + void OnDiscard(); 1.248 + void FrameChanged(const nsIntRect* aDirtyRect); 1.249 + void OnUnlockedDraw(); 1.250 + // This is called only by VectorImage, and only to ensure tests work 1.251 + // properly. Do not use it. 1.252 + void OnStopFrame(); 1.253 + 1.254 + /* non-virtual imgIOnloadBlocker methods */ 1.255 + // NB: If UnblockOnload is sent, and then we are asked to replay the 1.256 + // notifications, we will not send a BlockOnload/UnblockOnload pair. This 1.257 + // is different from all the other notifications. 1.258 + void RecordBlockOnload(); 1.259 + void SendBlockOnload(imgRequestProxy* aProxy); 1.260 + void RecordUnblockOnload(); 1.261 + void SendUnblockOnload(imgRequestProxy* aProxy); 1.262 + 1.263 + // Main thread only because mConsumers is not threadsafe. 1.264 + void MaybeUnblockOnload(); 1.265 + 1.266 + void RecordError(); 1.267 + 1.268 + bool IsMultipart() const { return mIsMultipart; } 1.269 + 1.270 + // Weak pointer getters - no AddRefs. 1.271 + inline already_AddRefed<mozilla::image::Image> GetImage() const { 1.272 + nsRefPtr<mozilla::image::Image> image = mImage; 1.273 + return image.forget(); 1.274 + } 1.275 + inline bool HasImage() { return mImage; } 1.276 + 1.277 + inline imgDecoderObserver* GetDecoderObserver() { return mTrackerObserver.get(); } 1.278 + 1.279 + already_AddRefed<imgStatusTracker> CloneForRecording(); 1.280 + 1.281 + // Compute the difference between this status tracker and aOther. 1.282 + mozilla::image::ImageStatusDiff Difference(imgStatusTracker* aOther) const; 1.283 + 1.284 + // Captures all of the decode notifications (i.e., not OnStartRequest / 1.285 + // OnStopRequest) so far as an ImageStatusDiff. 1.286 + mozilla::image::ImageStatusDiff DecodeStateAsDifference() const; 1.287 + 1.288 + // Update our state to incorporate the changes in aDiff. 1.289 + void ApplyDifference(const mozilla::image::ImageStatusDiff& aDiff); 1.290 + 1.291 + // Notify for the changes captured in an ImageStatusDiff. Because this may 1.292 + // result in recursive notifications, no decoding locks may be held. 1.293 + // Called on the main thread only. 1.294 + void SyncNotifyDifference(const mozilla::image::ImageStatusDiff& aDiff); 1.295 + 1.296 + nsIntRect GetInvalidRect() const { return mInvalidRect; } 1.297 + 1.298 +private: 1.299 + typedef nsTObserverArray<mozilla::WeakPtr<imgRequestProxy>> ProxyArray; 1.300 + friend class imgStatusNotifyRunnable; 1.301 + friend class imgRequestNotifyRunnable; 1.302 + friend class imgStatusTrackerObserver; 1.303 + friend class imgStatusTrackerInit; 1.304 + imgStatusTracker(const imgStatusTracker& aOther); 1.305 + 1.306 + // Main thread only because it deals with the observer service. 1.307 + void FireFailureNotification(); 1.308 + 1.309 + // Main thread only, since imgRequestProxy calls are expected on the main 1.310 + // thread, and mConsumers is not threadsafe. 1.311 + static void SyncNotifyState(ProxyArray& proxies, 1.312 + bool hasImage, uint32_t state, 1.313 + nsIntRect& dirtyRect, bool hadLastPart); 1.314 + 1.315 + nsCOMPtr<nsIRunnable> mRequestRunnable; 1.316 + 1.317 + // The invalid area of the most recent frame we know about. (All previous 1.318 + // frames are assumed to be fully valid.) 1.319 + nsIntRect mInvalidRect; 1.320 + 1.321 + // This weak ref should be set null when the image goes out of scope. 1.322 + mozilla::image::Image* mImage; 1.323 + 1.324 + // List of proxies attached to the image. Each proxy represents a consumer 1.325 + // using the image. Array and/or individual elements should only be accessed 1.326 + // on the main thread. 1.327 + ProxyArray mConsumers; 1.328 + 1.329 + mozilla::RefPtr<imgDecoderObserver> mTrackerObserver; 1.330 + 1.331 + uint32_t mState; 1.332 + uint32_t mImageStatus; 1.333 + bool mIsMultipart : 1; 1.334 + bool mHadLastPart : 1; 1.335 + bool mHasBeenDecoded : 1; 1.336 +}; 1.337 + 1.338 +class imgStatusTrackerInit 1.339 +{ 1.340 +public: 1.341 + imgStatusTrackerInit(mozilla::image::Image* aImage, 1.342 + imgStatusTracker* aTracker); 1.343 + ~imgStatusTrackerInit(); 1.344 +private: 1.345 + imgStatusTracker* mTracker; 1.346 +}; 1.347 + 1.348 +#endif