DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (31ec81b5d7bb)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef imgStatusTracker_h__
#define imgStatusTracker_h__

class imgDecoderObserver;
class imgIContainer;
class imgStatusNotifyRunnable;
class imgRequestNotifyRunnable;
class imgStatusTrackerObserver;
class imgStatusTrackerNotifyingObserver;
class nsIRunnable;

#include "mozilla/RefPtr.h"
#include "nsCOMPtr.h"
#include "nsTObserverArray.h"
#include "nsRect.h"
#include "imgRequestProxy.h"

namespace mozilla {
namespace image {

class Image;

struct ImageStatusDiff
{
  ImageStatusDiff()
    : invalidRect()
    , diffState(0)
    , diffImageStatus(0)
    , unblockedOnload(false)
    , unsetDecodeStarted(false)
    , foundError(false)
    , foundIsMultipart(false)
    , foundLastPart(false)
    , gotDecoded(false)
  { }

  static ImageStatusDiff NoChange() { return ImageStatusDiff(); }
  bool IsNoChange() const { return *this == NoChange(); }

  bool operator!=(const ImageStatusDiff& aOther) const { return !(*this == aOther); }
  bool operator==(const ImageStatusDiff& aOther) const {
    return aOther.invalidRect == invalidRect
        && aOther.diffState == diffState
        && aOther.diffImageStatus == diffImageStatus
        && aOther.unblockedOnload == unblockedOnload
        && aOther.unsetDecodeStarted == unsetDecodeStarted
        && aOther.foundError == foundError
        && aOther.foundIsMultipart == foundIsMultipart
        && aOther.foundLastPart == foundLastPart
        && aOther.gotDecoded == gotDecoded;
  }

  void Combine(const ImageStatusDiff& aOther) {
    invalidRect = invalidRect.Union(aOther.invalidRect);
    diffState |= aOther.diffState;
    diffImageStatus |= aOther.diffImageStatus;
    unblockedOnload = unblockedOnload || aOther.unblockedOnload;
    unsetDecodeStarted = unsetDecodeStarted || aOther.unsetDecodeStarted;
    foundError = foundError || aOther.foundError;
    foundIsMultipart = foundIsMultipart || aOther.foundIsMultipart;
    foundLastPart = foundLastPart || aOther.foundLastPart;
    gotDecoded = gotDecoded || aOther.gotDecoded;
  }

  nsIntRect invalidRect;
  uint32_t  diffState;
  uint32_t  diffImageStatus;
  bool      unblockedOnload    : 1;
  bool      unsetDecodeStarted : 1;
  bool      foundError         : 1;
  bool      foundIsMultipart   : 1;
  bool      foundLastPart      : 1;
  bool      gotDecoded         : 1;
};

enum {
  stateRequestStarted    = 1u << 0,
  stateHasSize           = 1u << 1,
  stateDecodeStarted     = 1u << 2,
  stateDecodeStopped     = 1u << 3,
  stateFrameStopped      = 1u << 4,
  stateRequestStopped    = 1u << 5,
  stateBlockingOnload    = 1u << 6,
  stateImageIsAnimated   = 1u << 7
};

} // namespace image
} // namespace mozilla

/*
 * The image status tracker is a class that encapsulates all the loading and
 * decoding status about an Image, and makes it possible to send notifications
 * to imgRequestProxys, both synchronously (i.e., the status now) and
 * asynchronously (the status later).
 *
 * When a new proxy needs to be notified of the current state of an image, call
 * the Notify() method on this class with the relevant proxy as its argument,
 * and the notifications will be replayed to the proxy asynchronously.
 */

class imgStatusTracker
{
public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(imgStatusTracker)

  // aImage is the image that this status tracker will pass to the
  // imgRequestProxys in SyncNotify() and EmulateRequestFinished(), and must be
  // alive as long as this instance is, because we hold a weak reference to it.
  imgStatusTracker(mozilla::image::Image* aImage);
  ~imgStatusTracker();

  // Image-setter, for imgStatusTrackers created by imgRequest::Init, which
  // are created before their Image is created.  This method should only
  // be called once, and only on an imgStatusTracker that was initialized
  // without an image.
  void SetImage(mozilla::image::Image* aImage);

  // Inform this status tracker that it is associated with a multipart image.
  void SetIsMultipart() { mIsMultipart = true; }

  // Schedule an asynchronous "replaying" of all the notifications that would
  // have to happen to put us in the current state.
  // We will also take note of any notifications that happen between the time
  // Notify() is called and when we call SyncNotify on |proxy|, and replay them
  // as well.
  void Notify(imgRequestProxy* proxy);

  // Schedule an asynchronous "replaying" of all the notifications that would
  // have to happen to put us in the state we are in right now.
  // Unlike Notify(), does *not* take into account future notifications.
  // This is only useful if you do not have an imgRequest, e.g., if you are a
  // static request returned from imgIRequest::GetStaticRequest().
  void NotifyCurrentState(imgRequestProxy* proxy);

  // "Replay" all of the notifications that would have to happen to put us in
  // the state we're currently in.
  // Only use this if you're already servicing an asynchronous call (e.g.
  // OnStartRequest).
  void SyncNotify(imgRequestProxy* proxy);

  // Send some notifications that would be necessary to make |proxy| believe
  // the request is finished downloading and decoding.  We only send
  // OnStopRequest and UnblockOnload, and only if necessary.
  void EmulateRequestFinished(imgRequestProxy* proxy, nsresult aStatus);

  // We manage a set of consumers that are using an image and thus concerned
  // with its status. Weak pointers.
  void AddConsumer(imgRequestProxy* aConsumer);
  bool RemoveConsumer(imgRequestProxy* aConsumer, nsresult aStatus);
  size_t ConsumerCount() const { return mConsumers.Length(); }

  // This is intentionally non-general because its sole purpose is to support an
  // some obscure network priority logic in imgRequest. That stuff could probably
  // be improved, but it's too scary to mess with at the moment.
  bool FirstConsumerIs(imgRequestProxy* aConsumer);

  void AdoptConsumers(imgStatusTracker* aTracker) { mConsumers = aTracker->mConsumers; }

  // Returns whether we are in the process of loading; that is, whether we have
  // not received OnStopRequest.
  bool IsLoading() const;

  // Get the current image status (as in imgIRequest).
  uint32_t GetImageStatus() const;

  // Following are all the notification methods. You must call the Record
  // variant on this status tracker, then call the Send variant for each proxy
  // you want to notify.

  // Call when the request is being cancelled.
  void RecordCancel();

  // Shorthand for recording all the load notifications: StartRequest,
  // StartContainer, StopRequest.
  void RecordLoaded();

  // Shorthand for recording all the decode notifications: StartDecode,
  // StartFrame, DataAvailable, StopFrame, StopDecode.
  void RecordDecoded();

  /* non-virtual imgDecoderObserver methods */
  void RecordStartDecode();
  void SendStartDecode(imgRequestProxy* aProxy);
  void RecordStartContainer(imgIContainer* aContainer);
  void SendStartContainer(imgRequestProxy* aProxy);
  void RecordStartFrame();
  // No SendStartFrame since it's not observed below us.
  void RecordFrameChanged(const nsIntRect* aDirtyRect);
  void SendFrameChanged(imgRequestProxy* aProxy, const nsIntRect* aDirtyRect);
  void RecordStopFrame();
  void SendStopFrame(imgRequestProxy* aProxy);
  void RecordStopDecode(nsresult statusg);
  void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus);
  void RecordDiscard();
  void SendDiscard(imgRequestProxy* aProxy);
  void RecordUnlockedDraw();
  void SendUnlockedDraw(imgRequestProxy* aProxy);
  void RecordImageIsAnimated();
  void SendImageIsAnimated(imgRequestProxy *aProxy);

  /* non-virtual sort-of-nsIRequestObserver methods */
  void RecordStartRequest();
  void SendStartRequest(imgRequestProxy* aProxy);
  void RecordStopRequest(bool aLastPart, nsresult aStatus);
  void SendStopRequest(imgRequestProxy* aProxy, bool aLastPart, nsresult aStatus);

  void OnStartRequest();
  void OnDataAvailable();
  void OnStopRequest(bool aLastPart, nsresult aStatus);
  void OnDiscard();
  void FrameChanged(const nsIntRect* aDirtyRect);
  void OnUnlockedDraw();
  // This is called only by VectorImage, and only to ensure tests work
  // properly. Do not use it.
  void OnStopFrame();

  /* non-virtual imgIOnloadBlocker methods */
  // NB: If UnblockOnload is sent, and then we are asked to replay the
  // notifications, we will not send a BlockOnload/UnblockOnload pair.  This
  // is different from all the other notifications.
  void RecordBlockOnload();
  void SendBlockOnload(imgRequestProxy* aProxy);
  void RecordUnblockOnload();
  void SendUnblockOnload(imgRequestProxy* aProxy);

  void MaybeUnblockOnload();

  void RecordError();

  bool IsMultipart() const { return mIsMultipart; }

  // Weak pointer getters - no AddRefs.
  inline mozilla::image::Image* GetImage() const { return mImage; }

  inline imgDecoderObserver* GetDecoderObserver() { return mTrackerObserver.get(); }

  imgStatusTracker* CloneForRecording();

  // Compute the difference between this status tracker and aOther.
  mozilla::image::ImageStatusDiff Difference(imgStatusTracker* aOther) const;

  // Captures all of the decode notifications (i.e., not OnStartRequest /
  // OnStopRequest) so far as an ImageStatusDiff.
  mozilla::image::ImageStatusDiff DecodeStateAsDifference() const;

  // Update our state to incorporate the changes in aDiff.
  void ApplyDifference(const mozilla::image::ImageStatusDiff& aDiff);

  // Notify for the changes captured in an ImageStatusDiff. Because this may
  // result in recursive notifications, no decoding locks may be held.
  void SyncNotifyDifference(const mozilla::image::ImageStatusDiff& aDiff);

  nsIntRect GetInvalidRect() const { return mInvalidRect; }

  typedef nsTObserverArray<mozilla::WeakPtr<imgRequestProxy>> ProxyArray;
private:
  friend class imgStatusNotifyRunnable;
  friend class imgRequestNotifyRunnable;
  friend class imgStatusTrackerObserver;
  friend class imgStatusTrackerNotifyingObserver;
  imgStatusTracker(const imgStatusTracker& aOther);

  void FireFailureNotification();

  static void SyncNotifyState(ProxyArray& proxies,
                              bool hasImage, uint32_t state,
                              nsIntRect& dirtyRect, bool hadLastPart);

  nsCOMPtr<nsIRunnable> mRequestRunnable;

  // The invalid area of the most recent frame we know about. (All previous
  // frames are assumed to be fully valid.)
  nsIntRect mInvalidRect;

  // Weak pointer to the image. The image owns the status tracker.
  mozilla::image::Image* mImage;

  // List of proxies attached to the image. Each proxy represents a consumer
  // using the image.
  ProxyArray mConsumers;

  mozilla::RefPtr<imgDecoderObserver> mTrackerObserver;

  uint32_t mState;
  uint32_t mImageStatus;
  bool mIsMultipart    : 1;
  bool mHadLastPart    : 1;
  bool mHasBeenDecoded : 1;
};

#endif