tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RecordedEvent.h (19064B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      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 #ifndef MOZILLA_GFX_RECORDEDEVENT_H_
      8 #define MOZILLA_GFX_RECORDEDEVENT_H_
      9 
     10 #include <ostream>
     11 #include <sstream>
     12 #include <cstring>
     13 #include <functional>
     14 
     15 #include "RecordingTypes.h"
     16 #include "mozilla/PodOperations.h"
     17 #include "mozilla/gfx/Point.h"
     18 #include "mozilla/gfx/Types.h"
     19 #include "mozilla/ipc/ByteBuf.h"
     20 #include "nsRefPtrHashtable.h"
     21 
     22 namespace mozilla {
     23 namespace gfx {
     24 
     25 const uint32_t kMagicInt = 0xc001feed;
     26 
     27 // A change in major revision means a change in event binary format, causing
     28 // loss of backwards compatibility. Old streams will not work in a player
     29 // using a newer major revision. And new streams will not work in a player
     30 // using an older major revision.
     31 const uint16_t kMajorRevision = 10;
     32 // A change in minor revision means additions of new events. New streams will
     33 // not play in older players.
     34 const uint16_t kMinorRevision = 3;
     35 
     36 struct ReferencePtr {
     37  ReferencePtr() : mLongPtr(0) {}
     38 
     39  MOZ_IMPLICIT ReferencePtr(const void* aLongPtr)
     40      : mLongPtr(uint64_t(aLongPtr)) {}
     41 
     42  template <typename T>
     43  MOZ_IMPLICIT ReferencePtr(const RefPtr<T>& aPtr)
     44      : mLongPtr(uint64_t(aPtr.get())) {}
     45 
     46  explicit ReferencePtr(uintptr_t aLongPtr) : mLongPtr(uint64_t(aLongPtr)) {}
     47 
     48  ReferencePtr& operator=(const void* aLongPtr) {
     49    mLongPtr = uint64_t(aLongPtr);
     50    return *this;
     51  }
     52 
     53  template <typename T>
     54  ReferencePtr& operator=(const RefPtr<T>& aPtr) {
     55    mLongPtr = uint64_t(aPtr.get());
     56    return *this;
     57  }
     58 
     59  operator void*() const { return (void*)mLongPtr; }
     60 
     61  explicit operator uintptr_t() const { return uintptr_t(mLongPtr); }
     62 
     63  // Implement some operators so this class can be used as a key in
     64  // stdlib classes.
     65  bool operator<(const ReferencePtr& aOther) const {
     66    return mLongPtr < aOther.mLongPtr;
     67  }
     68 
     69  bool operator>(const ReferencePtr& aOther) const {
     70    return mLongPtr > aOther.mLongPtr;
     71  }
     72 
     73  bool operator==(const ReferencePtr& aOther) const {
     74    return mLongPtr == aOther.mLongPtr;
     75  }
     76 
     77  bool operator!=(const ReferencePtr& aOther) const {
     78    return !(*this == aOther);
     79  }
     80 
     81  bool operator>=(const ReferencePtr& aOther) const {
     82    return mLongPtr >= aOther.mLongPtr;
     83  }
     84 
     85  uint64_t mLongPtr;
     86 };
     87 
     88 struct RecordedFontDetails {
     89  uint64_t fontDataKey = 0;
     90  uint32_t index = 0;
     91 };
     92 
     93 struct RecordedDependentSurface {
     94  NS_INLINE_DECL_REFCOUNTING(RecordedDependentSurface);
     95 
     96  RecordedDependentSurface(const IntSize& aSize,
     97                           mozilla::ipc::ByteBuf&& aRecording)
     98      : mSize(aSize), mRecording(std::move(aRecording)) {}
     99 
    100  IntSize mSize;
    101  mozilla::ipc::ByteBuf mRecording;
    102 
    103 private:
    104  ~RecordedDependentSurface() = default;
    105 };
    106 
    107 // Used by the Azure drawing debugger (player2d)
    108 inline std::string StringFromPtr(ReferencePtr aPtr) {
    109  std::stringstream stream;
    110  stream << aPtr;
    111  return stream.str();
    112 }
    113 
    114 class Translator {
    115 public:
    116  virtual ~Translator() = default;
    117 
    118  virtual DrawTarget* LookupDrawTarget(ReferencePtr aRefPtr) = 0;
    119  virtual Path* LookupPath(ReferencePtr aRefPtr) = 0;
    120  virtual SourceSurface* LookupSourceSurface(ReferencePtr aRefPtr) = 0;
    121  virtual FilterNode* LookupFilterNode(ReferencePtr aRefPtr) = 0;
    122  virtual already_AddRefed<GradientStops> LookupGradientStops(
    123      ReferencePtr aRefPtr) = 0;
    124  virtual ScaledFont* LookupScaledFont(ReferencePtr aRefPtr) = 0;
    125  virtual UnscaledFont* LookupUnscaledFont(ReferencePtr aRefPtr) = 0;
    126  virtual NativeFontResource* LookupNativeFontResource(uint64_t aKey) = 0;
    127  virtual already_AddRefed<SourceSurface> LookupExternalSurface(uint64_t aKey) {
    128    return nullptr;
    129  }
    130  virtual already_AddRefed<SourceSurface>
    131  LookupSourceSurfaceFromSurfaceDescriptor(
    132      const layers::SurfaceDescriptor& aDesc) {
    133    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
    134    return nullptr;
    135  }
    136  void DrawDependentSurface(uint64_t aKey, const Rect& aRect);
    137  virtual void AddDrawTarget(ReferencePtr aRefPtr, DrawTarget* aDT) = 0;
    138  virtual void RemoveDrawTarget(ReferencePtr aRefPtr) = 0;
    139  virtual bool SetCurrentDrawTarget(ReferencePtr aRefPtr) = 0;
    140  virtual void AddPath(ReferencePtr aRefPtr, Path* aPath) = 0;
    141  virtual void RemovePath(ReferencePtr aRefPtr) = 0;
    142  virtual void AddSourceSurface(ReferencePtr aRefPtr, SourceSurface* aPath) = 0;
    143  virtual void RemoveSourceSurface(ReferencePtr aRefPtr) = 0;
    144  virtual void AddFilterNode(mozilla::gfx::ReferencePtr aRefPtr,
    145                             FilterNode* aSurface) = 0;
    146  virtual void RemoveFilterNode(mozilla::gfx::ReferencePtr aRefPtr) = 0;
    147 
    148  /**
    149   * Get GradientStops compatible with the translation DrawTarget type.
    150   * @param aRawStops array of raw gradient stops required
    151   * @param aNumStops length of aRawStops
    152   * @param aExtendMode extend mode required
    153   * @return an already addrefed GradientStops for our DrawTarget type
    154   */
    155  virtual already_AddRefed<GradientStops> GetOrCreateGradientStops(
    156      DrawTarget* aDrawTarget, GradientStop* aRawStops, uint32_t aNumStops,
    157      ExtendMode aExtendMode) {
    158    return aDrawTarget->CreateGradientStops(aRawStops, aNumStops, aExtendMode);
    159  }
    160  virtual void AddGradientStops(ReferencePtr aRefPtr, GradientStops* aPath) = 0;
    161  virtual void RemoveGradientStops(ReferencePtr aRefPtr) = 0;
    162  virtual void AddScaledFont(ReferencePtr aRefPtr, ScaledFont* aScaledFont) = 0;
    163  virtual void RemoveScaledFont(ReferencePtr aRefPtr) = 0;
    164  virtual void AddUnscaledFont(ReferencePtr aRefPtr,
    165                               UnscaledFont* aUnscaledFont) = 0;
    166  virtual void RemoveUnscaledFont(ReferencePtr aRefPtr) = 0;
    167  virtual void AddNativeFontResource(
    168      uint64_t aKey, NativeFontResource* aNativeFontResource) = 0;
    169 
    170  virtual already_AddRefed<DrawTarget> CreateDrawTarget(ReferencePtr aRefPtr,
    171                                                        const IntSize& aSize,
    172                                                        SurfaceFormat aFormat);
    173  virtual DrawTarget* GetReferenceDrawTarget() = 0;
    174  virtual Matrix GetReferenceDrawTargetTransform() { return Matrix(); }
    175  virtual void* GetFontContext() { return nullptr; }
    176 
    177  void SetDependentSurfaces(
    178      nsRefPtrHashtable<nsUint64HashKey, RecordedDependentSurface>*
    179          aDependentSurfaces) {
    180    mDependentSurfaces = aDependentSurfaces;
    181  }
    182 
    183  // NOTE that the returned DrawTarget may be in an error state!
    184  DrawTarget* GetCurrentDrawTarget() const { return mCurrentDT; }
    185 
    186  nsRefPtrHashtable<nsUint64HashKey, RecordedDependentSurface>*
    187      mDependentSurfaces = nullptr;
    188  DrawTarget* mCurrentDT = nullptr;
    189 };
    190 
    191 struct ColorPatternStorage {
    192  DeviceColor mColor;
    193 };
    194 
    195 struct LinearGradientPatternStorage {
    196  Point mBegin;
    197  Point mEnd;
    198  ReferencePtr mStops;
    199  Matrix mMatrix;
    200 };
    201 
    202 struct RadialGradientPatternStorage {
    203  Point mCenter1;
    204  Point mCenter2;
    205  Float mRadius1;
    206  Float mRadius2;
    207  ReferencePtr mStops;
    208  Matrix mMatrix;
    209 };
    210 
    211 struct ConicGradientPatternStorage {
    212  Point mCenter;
    213  Float mAngle;
    214  Float mStartOffset;
    215  Float mEndOffset;
    216  ReferencePtr mStops;
    217  Matrix mMatrix;
    218 };
    219 
    220 struct SurfacePatternStorage {
    221  ExtendMode mExtend;
    222  SamplingFilter mSamplingFilter;
    223  ReferencePtr mSurface;
    224  Matrix mMatrix;
    225  IntRect mSamplingRect;
    226 };
    227 
    228 struct PatternStorage {
    229  PatternType mType;
    230  union {
    231    char* mStorage;
    232    char mColor[sizeof(ColorPatternStorage)];
    233    char mLinear[sizeof(LinearGradientPatternStorage)];
    234    char mRadial[sizeof(RadialGradientPatternStorage)];
    235    char mConic[sizeof(ConicGradientPatternStorage)];
    236    char mSurface[sizeof(SurfacePatternStorage)];
    237  };
    238 };
    239 
    240 /* SizeCollector and MemWriter are used
    241 * in a pair to first collect the size of the
    242 * event that we're going to write and then
    243 * to write it without checking each individual
    244 * size. */
    245 struct SizeCollector {
    246  SizeCollector() : mTotalSize(0) {}
    247  void write(const char*, size_t s) { mTotalSize += s; }
    248  size_t mTotalSize;
    249 };
    250 
    251 struct MemWriter {
    252  constexpr explicit MemWriter(char* aPtr) : mPtr(aPtr) {}
    253  void write(const char* aData, size_t aSize) {
    254    memcpy(mPtr, aData, aSize);
    255    mPtr += aSize;
    256  }
    257  char* mPtr;
    258 };
    259 
    260 // An istream like class for reading from memory
    261 struct MemReader {
    262  constexpr MemReader(const char* aData, size_t aLen)
    263      : mData(aData), mEnd(aData + aLen) {}
    264  void read(char* s, std::streamsize n) {
    265    if (n <= (mEnd - mData)) {
    266      memcpy(s, mData, n);
    267      mData += n;
    268    } else {
    269      // We've requested more data than is available
    270      // set the Reader into an eof state
    271      SetIsBad();
    272    }
    273  }
    274  bool eof() { return mData > mEnd; }
    275  bool good() { return !eof(); }
    276  void SetIsBad() { mData = mEnd + 1; }
    277 
    278  const char* mData;
    279  const char* mEnd;
    280 };
    281 
    282 class ContiguousBuffer {
    283 public:
    284  ContiguousBuffer(char* aStart, size_t aSize)
    285      : mWriter(aStart), mEnd(aStart + aSize) {}
    286 
    287  constexpr MOZ_IMPLICIT ContiguousBuffer(std::nullptr_t) : mWriter(nullptr) {}
    288 
    289  MemWriter& Writer() { return mWriter; }
    290 
    291  size_t SizeRemaining() { return mWriter.mPtr ? mEnd - mWriter.mPtr : 0; }
    292 
    293  bool IsValid() { return !!mWriter.mPtr; }
    294 
    295 private:
    296  MemWriter mWriter;
    297  char* mEnd = nullptr;
    298 };
    299 
    300 // Allows a derived class to provide guaranteed contiguous buffer.
    301 class ContiguousBufferStream {
    302 public:
    303  /**
    304   * Templated RecordEvent function so that we can record into the buffer
    305   * quickly using MemWriter.
    306   *
    307   * @param aRecordedEvent the event to record
    308   */
    309  template <class RE>
    310  void RecordEvent(const RE* aRecordedEvent) {
    311    SizeCollector size;
    312    WriteElement(size, aRecordedEvent->GetType());
    313    aRecordedEvent->Record(size);
    314    auto& buffer = GetContiguousBuffer(size.mTotalSize);
    315    if (!buffer.IsValid()) {
    316      return;
    317    }
    318 
    319    MOZ_ASSERT(size.mTotalSize <= buffer.SizeRemaining());
    320 
    321    WriteElement(buffer.Writer(), aRecordedEvent->GetType());
    322    aRecordedEvent->Record(buffer.Writer());
    323    IncrementEventCount();
    324  }
    325 
    326 protected:
    327  /**
    328   * Provide a contiguous buffer with at least aSize remaining.
    329   */
    330  virtual ContiguousBuffer& GetContiguousBuffer(size_t aSize) = 0;
    331 
    332  virtual void IncrementEventCount() = 0;
    333 };
    334 
    335 struct MemStream {
    336  char* mData;
    337  size_t mLength;
    338  size_t mCapacity;
    339  bool mValid = true;
    340  bool Resize(size_t aSize) {
    341    if (!mValid) {
    342      return false;
    343    }
    344    mLength = aSize;
    345    if (mLength > mCapacity) {
    346      mCapacity = mCapacity * 2;
    347      // check if the doubled capacity is enough
    348      // otherwise use double mLength
    349      if (mLength > mCapacity) {
    350        mCapacity = mLength * 2;
    351      }
    352      char* data = (char*)realloc(mData, mCapacity);
    353      if (!data) {
    354        free(mData);
    355      }
    356      mData = data;
    357    }
    358    if (mData) {
    359      return true;
    360    }
    361    NS_ERROR("Failed to allocate MemStream!");
    362    mValid = false;
    363    mLength = 0;
    364    mCapacity = 0;
    365    return false;
    366  }
    367 
    368  void reset() {
    369    free(mData);
    370    mData = nullptr;
    371    mValid = true;
    372    mLength = 0;
    373    mCapacity = 0;
    374  }
    375 
    376  MemStream(const MemStream&) = delete;
    377  MemStream(MemStream&&) = delete;
    378  MemStream& operator=(const MemStream&) = delete;
    379  MemStream& operator=(MemStream&&) = delete;
    380 
    381  void write(const char* aData, size_t aSize) {
    382    if (Resize(mLength + aSize)) {
    383      memcpy(mData + mLength - aSize, aData, aSize);
    384    }
    385  }
    386 
    387  MemStream() : mData(nullptr), mLength(0), mCapacity(0) {}
    388  ~MemStream() { free(mData); }
    389 };
    390 
    391 class EventStream {
    392 public:
    393  virtual void write(const char* aData, size_t aSize) = 0;
    394  virtual void read(char* aOut, size_t aSize) = 0;
    395  virtual bool good() = 0;
    396  virtual void SetIsBad() = 0;
    397 };
    398 
    399 class RecordedEvent {
    400 public:
    401  enum EventType : uint8_t {
    402    INVALID = 0,
    403    DRAWTARGETCREATION,
    404    DRAWTARGETDESTRUCTION,
    405    SETCURRENTDRAWTARGET,
    406    FILLRECT,
    407    STROKERECT,
    408    STROKELINE,
    409    STROKECIRCLE,
    410    CLEARRECT,
    411    COPYSURFACE,
    412    SETPERMITSUBPIXELAA,
    413    SETTRANSFORM,
    414    PUSHCLIP,
    415    PUSHCLIPRECT,
    416    POPCLIP,
    417    REMOVEALLCLIPS,
    418    FILL,
    419    FILLCIRCLE,
    420    FILLGLYPHS,
    421    STROKEGLYPHS,
    422    MASK,
    423    STROKE,
    424    DRAWSURFACE,
    425    DRAWSURFACEDESCRIPTOR,
    426    DRAWDEPENDENTSURFACE,
    427    DRAWSURFACEWITHSHADOW,
    428    DRAWSHADOW,
    429    PATHCREATION,
    430    PATHDESTRUCTION,
    431    SOURCESURFACECREATION,
    432    SOURCESURFACEDESTRUCTION,
    433    GRADIENTSTOPSCREATION,
    434    GRADIENTSTOPSDESTRUCTION,
    435    SNAPSHOT,
    436    SCALEDFONTCREATION,
    437    SCALEDFONTDESTRUCTION,
    438    MASKSURFACE,
    439    FILTERNODECREATION,
    440    DEFERFILTERINPUT,
    441    FILTERNODEDESTRUCTION,
    442    DRAWFILTER,
    443    FILTERNODESETATTRIBUTE,
    444    FILTERNODESETINPUT,
    445    CREATESIMILARDRAWTARGET,
    446    CREATECLIPPEDDRAWTARGET,
    447    CREATEDRAWTARGETFORFILTER,
    448    FONTDATA,
    449    FONTDESC,
    450    PUSHLAYER,
    451    PUSHLAYERWITHBLEND,
    452    POPLAYER,
    453    UNSCALEDFONTCREATION,
    454    UNSCALEDFONTDESTRUCTION,
    455    INTOLUMINANCE,
    456    EXTRACTSUBRECT,
    457    EXTERNALSURFACECREATION,
    458    FLUSH,
    459    DETACHALLSNAPSHOTS,
    460    OPTIMIZESOURCESURFACE,
    461    LINK,
    462    DESTINATION,
    463    LAST,
    464  };
    465 
    466  virtual ~RecordedEvent() = default;
    467 
    468  static std::string GetEventName(EventType aType);
    469 
    470  /**
    471   * Play back this event using the translator. Note that derived classes
    472   * should
    473   * only return false when there is a fatal error, as it will probably mean
    474   * the
    475   * translation will abort.
    476   * @param aTranslator Translator to be used for retrieving other referenced
    477   *                    objects and making playback decisions.
    478   * @return true unless a fatal problem has occurred and playback should
    479   * abort.
    480   */
    481  virtual bool PlayEvent(Translator* aTranslator) const { return true; }
    482 
    483  virtual void RecordToStream(std::ostream& aStream) const = 0;
    484  virtual void RecordToStream(EventStream& aStream) const = 0;
    485  virtual void RecordToStream(ContiguousBufferStream& aStream) const = 0;
    486  virtual void RecordToStream(MemStream& aStream) const = 0;
    487 
    488  virtual void OutputSimpleEventInfo(std::stringstream& aStringStream) const {}
    489 
    490  template <class S>
    491  void RecordPatternData(S& aStream,
    492                         const PatternStorage& aPatternStorage) const;
    493  template <class S>
    494  void ReadPatternData(S& aStream, PatternStorage& aPatternStorage) const;
    495  void StorePattern(PatternStorage& aDestination, const Pattern& aSource) const;
    496 
    497  virtual std::string GetName() const = 0;
    498 
    499  virtual ReferencePtr GetDestinedDT() { return nullptr; }
    500 
    501  void OutputSimplePatternInfo(const PatternStorage& aStorage,
    502                               std::stringstream& aOutput) const;
    503 
    504  template <class S>
    505  static bool DoWithEvent(S& aStream, EventType aType,
    506                          const std::function<bool(RecordedEvent*)>& aAction);
    507  static bool DoWithEventFromStream(
    508      EventStream& aStream, EventType aType,
    509      const std::function<bool(RecordedEvent*)>& aAction);
    510  static bool DoWithEventFromReader(
    511      MemReader& aReader, EventType aType,
    512      const std::function<bool(RecordedEvent*)>& aAction);
    513 
    514  EventType GetType() const { return (EventType)mType; }
    515 
    516 protected:
    517  friend class DrawEventRecorderPrivate;
    518  friend class DrawEventRecorderMemory;
    519  static void RecordUnscaledFont(UnscaledFont* aUnscaledFont,
    520                                 std::ostream* aOutput);
    521  static void RecordUnscaledFont(UnscaledFont* aUnscaledFont,
    522                                 MemStream& aOutput);
    523  template <class S>
    524  static void RecordUnscaledFontImpl(UnscaledFont* aUnscaledFont, S& aOutput);
    525 
    526  MOZ_IMPLICIT RecordedEvent(EventType aType) : mType(aType) {}
    527 
    528  EventType mType;
    529 };
    530 
    531 class RecordedStrokeOptionsMixin {
    532 public:
    533  template <class S>
    534  void RecordStrokeOptions(S& aStream,
    535                           const StrokeOptions& aStrokeOptions) const;
    536  template <class S>
    537  void ReadStrokeOptions(S& aStream, StrokeOptions& aStrokeOptions);
    538 
    539 protected:
    540  UniquePtr<Float[]> mDashPatternStorage;
    541 };
    542 
    543 template <typename T, typename Z = size_t>
    544 class RecordedEventArray {
    545 public:
    546  T* data() { return mData.get(); }
    547  const T* data() const { return mData.get(); }
    548  Z size() const { return mSize; }
    549  bool empty() const { return !mSize; }
    550 
    551  void Assign(const T* aData, Z aSize) {
    552    mSize = aSize;
    553    mData = MakeUnique<T[]>(aSize);
    554    PodCopy(mData.get(), aData, aSize);
    555  }
    556 
    557  bool TryAlloc(Z aSize) {
    558    if (mSize > 0) {
    559      MOZ_ASSERT_UNREACHABLE();
    560      return false;
    561    }
    562    mData = MakeUniqueFallible<T[]>(aSize);
    563    if (!mData) {
    564      return false;
    565    }
    566    mSize = aSize;
    567    return true;
    568  }
    569 
    570  bool TryAssign(const T* aData, Z aSize) {
    571    if (!TryAlloc(aSize)) {
    572      return false;
    573    }
    574    PodCopy(mData.get(), aData, aSize);
    575    return true;
    576  }
    577 
    578  template <typename S>
    579  void Write(S& aStream) const {
    580    if (mSize) {
    581      aStream.write(reinterpret_cast<const char*>(mData.get()),
    582                    sizeof(T) * mSize);
    583    }
    584  }
    585 
    586  template <typename S>
    587  bool Read(S& aStream, Z aSize) {
    588    if (!aStream.good() || !TryAlloc(aSize)) {
    589      return false;
    590    }
    591    aStream.read(reinterpret_cast<char*>(mData.get()), sizeof(T) * mSize);
    592    if (!aStream.good()) {
    593      Clear();
    594      return false;
    595    }
    596    return true;
    597  }
    598 
    599  void Clear() {
    600    mSize = 0;
    601    mData.reset();
    602  }
    603 
    604 protected:
    605  Z mSize = 0;
    606  UniquePtr<T[]> mData;
    607 };
    608 
    609 class RecordedEventCString : public RecordedEventArray<char> {
    610 public:
    611  explicit RecordedEventCString(const char* aStr = nullptr) {
    612    if (aStr) {
    613      if (size_t len = strlen(aStr)) {
    614        Assign(aStr, len + 1);
    615      }
    616    }
    617  }
    618 
    619  template <typename S>
    620  bool Read(S& aStream, size_t aSize) {
    621    if (!RecordedEventArray<char>::Read(aStream, aSize) ||
    622        (size() > 0 && !memchr(data(), '\0', size()))) {
    623      Clear();
    624      return false;
    625    }
    626    return true;
    627  }
    628 };
    629 
    630 template <class Derived>
    631 class RecordedEventDerived : public RecordedEvent {
    632  using RecordedEvent::RecordedEvent;
    633 
    634 public:
    635  void RecordToStream(std::ostream& aStream) const override {
    636    WriteElement(aStream, this->mType);
    637    static_cast<const Derived*>(this)->Record(aStream);
    638  }
    639  void RecordToStream(EventStream& aStream) const override {
    640    WriteElement(aStream, this->mType);
    641    static_cast<const Derived*>(this)->Record(aStream);
    642  }
    643  void RecordToStream(ContiguousBufferStream& aStream) const final {
    644    aStream.RecordEvent(static_cast<const Derived*>(this));
    645  }
    646  void RecordToStream(MemStream& aStream) const override {
    647    SizeCollector size;
    648    WriteElement(size, this->mType);
    649    static_cast<const Derived*>(this)->Record(size);
    650 
    651    if (!aStream.Resize(aStream.mLength + size.mTotalSize)) {
    652      return;
    653    }
    654 
    655    MemWriter writer(aStream.mData + aStream.mLength - size.mTotalSize);
    656    WriteElement(writer, this->mType);
    657    static_cast<const Derived*>(this)->Record(writer);
    658  }
    659 };
    660 
    661 }  // namespace gfx
    662 }  // namespace mozilla
    663 
    664 // Helper struct that allows ReferencePtr to be used as a key in
    665 // std::unordered_map
    666 template <>
    667 struct std::hash<mozilla::gfx::ReferencePtr> {
    668  std::size_t operator()(const mozilla::gfx::ReferencePtr& aRef) const {
    669    return std::hash<uint64_t>{}(aRef.mLongPtr);
    670  }
    671 };
    672 
    673 #endif