Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
michael@0 | 2 | * |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #ifndef imgStatusTracker_h__ |
michael@0 | 8 | #define imgStatusTracker_h__ |
michael@0 | 9 | |
michael@0 | 10 | class imgDecoderObserver; |
michael@0 | 11 | class imgIContainer; |
michael@0 | 12 | class imgStatusNotifyRunnable; |
michael@0 | 13 | class imgRequestNotifyRunnable; |
michael@0 | 14 | class imgStatusTrackerObserver; |
michael@0 | 15 | class nsIRunnable; |
michael@0 | 16 | |
michael@0 | 17 | #include "mozilla/RefPtr.h" |
michael@0 | 18 | #include "mozilla/WeakPtr.h" |
michael@0 | 19 | #include "nsCOMPtr.h" |
michael@0 | 20 | #include "nsTObserverArray.h" |
michael@0 | 21 | #include "nsThreadUtils.h" |
michael@0 | 22 | #include "nsRect.h" |
michael@0 | 23 | #include "imgRequestProxy.h" |
michael@0 | 24 | |
michael@0 | 25 | namespace mozilla { |
michael@0 | 26 | namespace image { |
michael@0 | 27 | |
michael@0 | 28 | class Image; |
michael@0 | 29 | |
michael@0 | 30 | struct ImageStatusDiff |
michael@0 | 31 | { |
michael@0 | 32 | ImageStatusDiff() |
michael@0 | 33 | : invalidRect() |
michael@0 | 34 | , diffState(0) |
michael@0 | 35 | , diffImageStatus(0) |
michael@0 | 36 | , unblockedOnload(false) |
michael@0 | 37 | , unsetDecodeStarted(false) |
michael@0 | 38 | , foundError(false) |
michael@0 | 39 | , foundIsMultipart(false) |
michael@0 | 40 | , foundLastPart(false) |
michael@0 | 41 | , gotDecoded(false) |
michael@0 | 42 | { } |
michael@0 | 43 | |
michael@0 | 44 | static ImageStatusDiff NoChange() { return ImageStatusDiff(); } |
michael@0 | 45 | bool IsNoChange() const { return *this == NoChange(); } |
michael@0 | 46 | |
michael@0 | 47 | bool operator!=(const ImageStatusDiff& aOther) const { return !(*this == aOther); } |
michael@0 | 48 | bool operator==(const ImageStatusDiff& aOther) const { |
michael@0 | 49 | return aOther.invalidRect == invalidRect |
michael@0 | 50 | && aOther.diffState == diffState |
michael@0 | 51 | && aOther.diffImageStatus == diffImageStatus |
michael@0 | 52 | && aOther.unblockedOnload == unblockedOnload |
michael@0 | 53 | && aOther.unsetDecodeStarted == unsetDecodeStarted |
michael@0 | 54 | && aOther.foundError == foundError |
michael@0 | 55 | && aOther.foundIsMultipart == foundIsMultipart |
michael@0 | 56 | && aOther.foundLastPart == foundLastPart |
michael@0 | 57 | && aOther.gotDecoded == gotDecoded; |
michael@0 | 58 | } |
michael@0 | 59 | |
michael@0 | 60 | void Combine(const ImageStatusDiff& aOther) { |
michael@0 | 61 | invalidRect = invalidRect.Union(aOther.invalidRect); |
michael@0 | 62 | diffState |= aOther.diffState; |
michael@0 | 63 | diffImageStatus |= aOther.diffImageStatus; |
michael@0 | 64 | unblockedOnload = unblockedOnload || aOther.unblockedOnload; |
michael@0 | 65 | unsetDecodeStarted = unsetDecodeStarted || aOther.unsetDecodeStarted; |
michael@0 | 66 | foundError = foundError || aOther.foundError; |
michael@0 | 67 | foundIsMultipart = foundIsMultipart || aOther.foundIsMultipart; |
michael@0 | 68 | foundLastPart = foundLastPart || aOther.foundLastPart; |
michael@0 | 69 | gotDecoded = gotDecoded || aOther.gotDecoded; |
michael@0 | 70 | } |
michael@0 | 71 | |
michael@0 | 72 | nsIntRect invalidRect; |
michael@0 | 73 | uint32_t diffState; |
michael@0 | 74 | uint32_t diffImageStatus; |
michael@0 | 75 | bool unblockedOnload : 1; |
michael@0 | 76 | bool unsetDecodeStarted : 1; |
michael@0 | 77 | bool foundError : 1; |
michael@0 | 78 | bool foundIsMultipart : 1; |
michael@0 | 79 | bool foundLastPart : 1; |
michael@0 | 80 | bool gotDecoded : 1; |
michael@0 | 81 | }; |
michael@0 | 82 | |
michael@0 | 83 | enum { |
michael@0 | 84 | stateRequestStarted = 1u << 0, |
michael@0 | 85 | stateHasSize = 1u << 1, |
michael@0 | 86 | stateDecodeStarted = 1u << 2, |
michael@0 | 87 | stateDecodeStopped = 1u << 3, |
michael@0 | 88 | stateFrameStopped = 1u << 4, |
michael@0 | 89 | stateRequestStopped = 1u << 5, |
michael@0 | 90 | stateBlockingOnload = 1u << 6, |
michael@0 | 91 | stateImageIsAnimated = 1u << 7 |
michael@0 | 92 | }; |
michael@0 | 93 | |
michael@0 | 94 | } // namespace image |
michael@0 | 95 | } // namespace mozilla |
michael@0 | 96 | |
michael@0 | 97 | /* |
michael@0 | 98 | * The image status tracker is a class that encapsulates all the loading and |
michael@0 | 99 | * decoding status about an Image, and makes it possible to send notifications |
michael@0 | 100 | * to imgRequestProxys, both synchronously (i.e., the status now) and |
michael@0 | 101 | * asynchronously (the status later). |
michael@0 | 102 | * |
michael@0 | 103 | * When a new proxy needs to be notified of the current state of an image, call |
michael@0 | 104 | * the Notify() method on this class with the relevant proxy as its argument, |
michael@0 | 105 | * and the notifications will be replayed to the proxy asynchronously. |
michael@0 | 106 | */ |
michael@0 | 107 | |
michael@0 | 108 | |
michael@0 | 109 | class imgStatusTracker : public mozilla::SupportsWeakPtr<imgStatusTracker> |
michael@0 | 110 | { |
michael@0 | 111 | public: |
michael@0 | 112 | MOZ_DECLARE_REFCOUNTED_TYPENAME(imgStatusTracker) |
michael@0 | 113 | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(imgStatusTracker) |
michael@0 | 114 | |
michael@0 | 115 | // aImage is the image that this status tracker will pass to the |
michael@0 | 116 | // imgRequestProxys in SyncNotify() and EmulateRequestFinished(), and must be |
michael@0 | 117 | // alive as long as this instance is, because we hold a weak reference to it. |
michael@0 | 118 | imgStatusTracker(mozilla::image::Image* aImage); |
michael@0 | 119 | virtual ~imgStatusTracker(); |
michael@0 | 120 | |
michael@0 | 121 | // Image-setter, for imgStatusTrackers created by imgRequest::Init, which |
michael@0 | 122 | // are created before their Image is created. This method should only |
michael@0 | 123 | // be called once, and only on an imgStatusTracker that was initialized |
michael@0 | 124 | // without an image. |
michael@0 | 125 | void SetImage(mozilla::image::Image* aImage); |
michael@0 | 126 | |
michael@0 | 127 | // Image resetter, for when mImage is about to go out of scope. mImage is a |
michael@0 | 128 | // weak reference, and thus must be set to null when it's object is deleted. |
michael@0 | 129 | void ResetImage(); |
michael@0 | 130 | |
michael@0 | 131 | // Inform this status tracker that it is associated with a multipart image. |
michael@0 | 132 | void SetIsMultipart() { mIsMultipart = true; } |
michael@0 | 133 | |
michael@0 | 134 | // Schedule an asynchronous "replaying" of all the notifications that would |
michael@0 | 135 | // have to happen to put us in the current state. |
michael@0 | 136 | // We will also take note of any notifications that happen between the time |
michael@0 | 137 | // Notify() is called and when we call SyncNotify on |proxy|, and replay them |
michael@0 | 138 | // as well. |
michael@0 | 139 | // Should be called on the main thread only, since imgRequestProxy and GetURI |
michael@0 | 140 | // are not threadsafe. |
michael@0 | 141 | void Notify(imgRequestProxy* proxy); |
michael@0 | 142 | |
michael@0 | 143 | // Schedule an asynchronous "replaying" of all the notifications that would |
michael@0 | 144 | // have to happen to put us in the state we are in right now. |
michael@0 | 145 | // Unlike Notify(), does *not* take into account future notifications. |
michael@0 | 146 | // This is only useful if you do not have an imgRequest, e.g., if you are a |
michael@0 | 147 | // static request returned from imgIRequest::GetStaticRequest(). |
michael@0 | 148 | // Should be called on the main thread only, since imgRequestProxy and GetURI |
michael@0 | 149 | // are not threadsafe. |
michael@0 | 150 | void NotifyCurrentState(imgRequestProxy* proxy); |
michael@0 | 151 | |
michael@0 | 152 | // "Replay" all of the notifications that would have to happen to put us in |
michael@0 | 153 | // the state we're currently in. |
michael@0 | 154 | // Only use this if you're already servicing an asynchronous call (e.g. |
michael@0 | 155 | // OnStartRequest). |
michael@0 | 156 | // Should be called on the main thread only, since imgRequestProxy and GetURI |
michael@0 | 157 | // are not threadsafe. |
michael@0 | 158 | void SyncNotify(imgRequestProxy* proxy); |
michael@0 | 159 | |
michael@0 | 160 | // Send some notifications that would be necessary to make |proxy| believe |
michael@0 | 161 | // the request is finished downloading and decoding. We only send |
michael@0 | 162 | // OnStopRequest and UnblockOnload, and only if necessary. |
michael@0 | 163 | void EmulateRequestFinished(imgRequestProxy* proxy, nsresult aStatus); |
michael@0 | 164 | |
michael@0 | 165 | // We manage a set of consumers that are using an image and thus concerned |
michael@0 | 166 | // with its status. Weak pointers. |
michael@0 | 167 | void AddConsumer(imgRequestProxy* aConsumer); |
michael@0 | 168 | bool RemoveConsumer(imgRequestProxy* aConsumer, nsresult aStatus); |
michael@0 | 169 | size_t ConsumerCount() const { |
michael@0 | 170 | MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only"); |
michael@0 | 171 | return mConsumers.Length(); |
michael@0 | 172 | } |
michael@0 | 173 | |
michael@0 | 174 | // This is intentionally non-general because its sole purpose is to support an |
michael@0 | 175 | // some obscure network priority logic in imgRequest. That stuff could probably |
michael@0 | 176 | // be improved, but it's too scary to mess with at the moment. |
michael@0 | 177 | bool FirstConsumerIs(imgRequestProxy* aConsumer); |
michael@0 | 178 | |
michael@0 | 179 | void AdoptConsumers(imgStatusTracker* aTracker) { |
michael@0 | 180 | MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only"); |
michael@0 | 181 | MOZ_ASSERT(aTracker); |
michael@0 | 182 | mConsumers = aTracker->mConsumers; |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | // Returns whether we are in the process of loading; that is, whether we have |
michael@0 | 186 | // not received OnStopRequest. |
michael@0 | 187 | bool IsLoading() const; |
michael@0 | 188 | |
michael@0 | 189 | // Get the current image status (as in imgIRequest). |
michael@0 | 190 | uint32_t GetImageStatus() const; |
michael@0 | 191 | |
michael@0 | 192 | // Following are all the notification methods. You must call the Record |
michael@0 | 193 | // variant on this status tracker, then call the Send variant for each proxy |
michael@0 | 194 | // you want to notify. |
michael@0 | 195 | |
michael@0 | 196 | // Call when the request is being cancelled. |
michael@0 | 197 | void RecordCancel(); |
michael@0 | 198 | |
michael@0 | 199 | // Shorthand for recording all the load notifications: StartRequest, |
michael@0 | 200 | // StartContainer, StopRequest. |
michael@0 | 201 | void RecordLoaded(); |
michael@0 | 202 | |
michael@0 | 203 | // Shorthand for recording all the decode notifications: StartDecode, |
michael@0 | 204 | // StartFrame, DataAvailable, StopFrame, StopDecode. |
michael@0 | 205 | void RecordDecoded(); |
michael@0 | 206 | |
michael@0 | 207 | /* non-virtual imgDecoderObserver methods */ |
michael@0 | 208 | // Functions with prefix Send- are main thread only, since they contain calls |
michael@0 | 209 | // to imgRequestProxy functions, which are expected on the main thread. |
michael@0 | 210 | void RecordStartDecode(); |
michael@0 | 211 | void SendStartDecode(imgRequestProxy* aProxy); |
michael@0 | 212 | void RecordStartContainer(imgIContainer* aContainer); |
michael@0 | 213 | void SendStartContainer(imgRequestProxy* aProxy); |
michael@0 | 214 | void RecordStartFrame(); |
michael@0 | 215 | // No SendStartFrame since it's not observed below us. |
michael@0 | 216 | void RecordFrameChanged(const nsIntRect* aDirtyRect); |
michael@0 | 217 | void SendFrameChanged(imgRequestProxy* aProxy, const nsIntRect* aDirtyRect); |
michael@0 | 218 | void RecordStopFrame(); |
michael@0 | 219 | void SendStopFrame(imgRequestProxy* aProxy); |
michael@0 | 220 | void RecordStopDecode(nsresult statusg); |
michael@0 | 221 | void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus); |
michael@0 | 222 | void RecordDiscard(); |
michael@0 | 223 | void SendDiscard(imgRequestProxy* aProxy); |
michael@0 | 224 | void RecordUnlockedDraw(); |
michael@0 | 225 | void SendUnlockedDraw(imgRequestProxy* aProxy); |
michael@0 | 226 | void RecordImageIsAnimated(); |
michael@0 | 227 | void SendImageIsAnimated(imgRequestProxy *aProxy); |
michael@0 | 228 | |
michael@0 | 229 | /* non-virtual sort-of-nsIRequestObserver methods */ |
michael@0 | 230 | // Functions with prefix Send- are main thread only, since they contain calls |
michael@0 | 231 | // to imgRequestProxy functions, which are expected on the main thread. |
michael@0 | 232 | void RecordStartRequest(); |
michael@0 | 233 | void SendStartRequest(imgRequestProxy* aProxy); |
michael@0 | 234 | void RecordStopRequest(bool aLastPart, nsresult aStatus); |
michael@0 | 235 | void SendStopRequest(imgRequestProxy* aProxy, bool aLastPart, nsresult aStatus); |
michael@0 | 236 | |
michael@0 | 237 | // All main thread only because they call functions (like SendStartRequest) |
michael@0 | 238 | // which are expected to be called on the main thread. |
michael@0 | 239 | void OnStartRequest(); |
michael@0 | 240 | // OnDataAvailable will dispatch a call to itself onto the main thread if not |
michael@0 | 241 | // called there. |
michael@0 | 242 | void OnDataAvailable(); |
michael@0 | 243 | void OnStopRequest(bool aLastPart, nsresult aStatus); |
michael@0 | 244 | void OnDiscard(); |
michael@0 | 245 | void FrameChanged(const nsIntRect* aDirtyRect); |
michael@0 | 246 | void OnUnlockedDraw(); |
michael@0 | 247 | // This is called only by VectorImage, and only to ensure tests work |
michael@0 | 248 | // properly. Do not use it. |
michael@0 | 249 | void OnStopFrame(); |
michael@0 | 250 | |
michael@0 | 251 | /* non-virtual imgIOnloadBlocker methods */ |
michael@0 | 252 | // NB: If UnblockOnload is sent, and then we are asked to replay the |
michael@0 | 253 | // notifications, we will not send a BlockOnload/UnblockOnload pair. This |
michael@0 | 254 | // is different from all the other notifications. |
michael@0 | 255 | void RecordBlockOnload(); |
michael@0 | 256 | void SendBlockOnload(imgRequestProxy* aProxy); |
michael@0 | 257 | void RecordUnblockOnload(); |
michael@0 | 258 | void SendUnblockOnload(imgRequestProxy* aProxy); |
michael@0 | 259 | |
michael@0 | 260 | // Main thread only because mConsumers is not threadsafe. |
michael@0 | 261 | void MaybeUnblockOnload(); |
michael@0 | 262 | |
michael@0 | 263 | void RecordError(); |
michael@0 | 264 | |
michael@0 | 265 | bool IsMultipart() const { return mIsMultipart; } |
michael@0 | 266 | |
michael@0 | 267 | // Weak pointer getters - no AddRefs. |
michael@0 | 268 | inline already_AddRefed<mozilla::image::Image> GetImage() const { |
michael@0 | 269 | nsRefPtr<mozilla::image::Image> image = mImage; |
michael@0 | 270 | return image.forget(); |
michael@0 | 271 | } |
michael@0 | 272 | inline bool HasImage() { return mImage; } |
michael@0 | 273 | |
michael@0 | 274 | inline imgDecoderObserver* GetDecoderObserver() { return mTrackerObserver.get(); } |
michael@0 | 275 | |
michael@0 | 276 | already_AddRefed<imgStatusTracker> CloneForRecording(); |
michael@0 | 277 | |
michael@0 | 278 | // Compute the difference between this status tracker and aOther. |
michael@0 | 279 | mozilla::image::ImageStatusDiff Difference(imgStatusTracker* aOther) const; |
michael@0 | 280 | |
michael@0 | 281 | // Captures all of the decode notifications (i.e., not OnStartRequest / |
michael@0 | 282 | // OnStopRequest) so far as an ImageStatusDiff. |
michael@0 | 283 | mozilla::image::ImageStatusDiff DecodeStateAsDifference() const; |
michael@0 | 284 | |
michael@0 | 285 | // Update our state to incorporate the changes in aDiff. |
michael@0 | 286 | void ApplyDifference(const mozilla::image::ImageStatusDiff& aDiff); |
michael@0 | 287 | |
michael@0 | 288 | // Notify for the changes captured in an ImageStatusDiff. Because this may |
michael@0 | 289 | // result in recursive notifications, no decoding locks may be held. |
michael@0 | 290 | // Called on the main thread only. |
michael@0 | 291 | void SyncNotifyDifference(const mozilla::image::ImageStatusDiff& aDiff); |
michael@0 | 292 | |
michael@0 | 293 | nsIntRect GetInvalidRect() const { return mInvalidRect; } |
michael@0 | 294 | |
michael@0 | 295 | private: |
michael@0 | 296 | typedef nsTObserverArray<mozilla::WeakPtr<imgRequestProxy>> ProxyArray; |
michael@0 | 297 | friend class imgStatusNotifyRunnable; |
michael@0 | 298 | friend class imgRequestNotifyRunnable; |
michael@0 | 299 | friend class imgStatusTrackerObserver; |
michael@0 | 300 | friend class imgStatusTrackerInit; |
michael@0 | 301 | imgStatusTracker(const imgStatusTracker& aOther); |
michael@0 | 302 | |
michael@0 | 303 | // Main thread only because it deals with the observer service. |
michael@0 | 304 | void FireFailureNotification(); |
michael@0 | 305 | |
michael@0 | 306 | // Main thread only, since imgRequestProxy calls are expected on the main |
michael@0 | 307 | // thread, and mConsumers is not threadsafe. |
michael@0 | 308 | static void SyncNotifyState(ProxyArray& proxies, |
michael@0 | 309 | bool hasImage, uint32_t state, |
michael@0 | 310 | nsIntRect& dirtyRect, bool hadLastPart); |
michael@0 | 311 | |
michael@0 | 312 | nsCOMPtr<nsIRunnable> mRequestRunnable; |
michael@0 | 313 | |
michael@0 | 314 | // The invalid area of the most recent frame we know about. (All previous |
michael@0 | 315 | // frames are assumed to be fully valid.) |
michael@0 | 316 | nsIntRect mInvalidRect; |
michael@0 | 317 | |
michael@0 | 318 | // This weak ref should be set null when the image goes out of scope. |
michael@0 | 319 | mozilla::image::Image* mImage; |
michael@0 | 320 | |
michael@0 | 321 | // List of proxies attached to the image. Each proxy represents a consumer |
michael@0 | 322 | // using the image. Array and/or individual elements should only be accessed |
michael@0 | 323 | // on the main thread. |
michael@0 | 324 | ProxyArray mConsumers; |
michael@0 | 325 | |
michael@0 | 326 | mozilla::RefPtr<imgDecoderObserver> mTrackerObserver; |
michael@0 | 327 | |
michael@0 | 328 | uint32_t mState; |
michael@0 | 329 | uint32_t mImageStatus; |
michael@0 | 330 | bool mIsMultipart : 1; |
michael@0 | 331 | bool mHadLastPart : 1; |
michael@0 | 332 | bool mHasBeenDecoded : 1; |
michael@0 | 333 | }; |
michael@0 | 334 | |
michael@0 | 335 | class imgStatusTrackerInit |
michael@0 | 336 | { |
michael@0 | 337 | public: |
michael@0 | 338 | imgStatusTrackerInit(mozilla::image::Image* aImage, |
michael@0 | 339 | imgStatusTracker* aTracker); |
michael@0 | 340 | ~imgStatusTrackerInit(); |
michael@0 | 341 | private: |
michael@0 | 342 | imgStatusTracker* mTracker; |
michael@0 | 343 | }; |
michael@0 | 344 | |
michael@0 | 345 | #endif |