DrawEventRecorder.h (4892B)
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_layout_printing_DrawEventRecorder_h 8 #define mozilla_layout_printing_DrawEventRecorder_h 9 10 #include <memory> 11 12 #include "mozilla/gfx/DrawEventRecorder.h" 13 #include "mozilla/gfx/RecordingTypes.h" 14 #include "nsTArray.h" 15 #include "prio.h" 16 17 namespace mozilla { 18 namespace layout { 19 20 class PRFileDescStream final : public mozilla::gfx::EventStream { 21 // Most writes, as seen in the print IPC use case, are very small (<32 bytes), 22 // with a small number of very large (>40KB) writes. Writes larger than this 23 // value are not buffered. 24 static const size_t kBufferSize = 1024; 25 26 public: 27 PRFileDescStream() 28 : mFd(nullptr), mBuffer(nullptr), mBufferPos(0), mGood(true) {} 29 PRFileDescStream(const PRFileDescStream& other) = delete; 30 ~PRFileDescStream() { Close(); } 31 32 void OpenFD(PRFileDesc* aFd) { 33 MOZ_DIAGNOSTIC_ASSERT(!IsOpen()); 34 mFd = aFd; 35 mGood = !!mFd; 36 mBuffer.reset(new uint8_t[kBufferSize]); 37 mBufferPos = 0; 38 } 39 40 void Close() { 41 // We need to be API compatible with std::ostream, and so we silently handle 42 // closes on a closed FD. 43 if (IsOpen()) { 44 Flush(); 45 PR_Close(mFd); 46 mFd = nullptr; 47 mBuffer.reset(); 48 mBufferPos = 0; 49 } 50 } 51 52 bool IsOpen() { return mFd != nullptr; } 53 54 void Flush() { 55 // See comment in Close(). 56 if (IsOpen() && mBufferPos > 0) { 57 PRInt32 length = 58 PR_Write(mFd, static_cast<const void*>(mBuffer.get()), mBufferPos); 59 mGood = length >= 0 && static_cast<size_t>(length) == mBufferPos; 60 mBufferPos = 0; 61 } 62 } 63 64 void Seek(PRInt64 aOffset, PRSeekWhence aWhence) { 65 Flush(); 66 PRInt64 pos = PR_Seek64(mFd, aOffset, aWhence); 67 mGood = pos != -1; 68 } 69 70 void write(const char* aData, size_t aSize) override { 71 if (!good()) { 72 return; 73 } 74 75 // See comment in Close(). 76 if (IsOpen()) { 77 // If we're writing more data than could ever fit in our buffer, flush the 78 // buffer and write directly. 79 if (aSize > kBufferSize) { 80 Flush(); 81 PRInt32 length = PR_Write(mFd, static_cast<const void*>(aData), aSize); 82 mGood = length >= 0 && static_cast<size_t>(length) == aSize; 83 // If our write could fit in our buffer, but doesn't because the buffer 84 // is partially full, write to the buffer, flush the buffer, and then 85 // write the rest of the data to the buffer. 86 } else if (aSize > AvailableBufferSpace()) { 87 size_t length = AvailableBufferSpace(); 88 WriteToBuffer(aData, length); 89 Flush(); 90 91 WriteToBuffer(aData + length, aSize - length); 92 // Write fits in the buffer. 93 } else { 94 WriteToBuffer(aData, aSize); 95 } 96 } 97 } 98 99 void read(char* aOut, size_t aSize) override { 100 if (!good()) { 101 return; 102 } 103 104 Flush(); 105 PRInt32 res = PR_Read(mFd, static_cast<void*>(aOut), aSize); 106 mGood = res >= 0 && (static_cast<size_t>(res) == aSize); 107 } 108 109 bool good() final { return mGood; } 110 111 void SetIsBad() final { mGood = false; } 112 113 private: 114 size_t AvailableBufferSpace() { return kBufferSize - mBufferPos; } 115 116 void WriteToBuffer(const char* aData, size_t aSize) { 117 MOZ_ASSERT(aSize <= AvailableBufferSpace()); 118 memcpy(mBuffer.get() + mBufferPos, aData, aSize); 119 mBufferPos += aSize; 120 } 121 122 PRFileDesc* mFd; 123 std::unique_ptr<uint8_t[]> mBuffer; 124 size_t mBufferPos; 125 bool mGood; 126 }; 127 128 class DrawEventRecorderPRFileDesc final : public gfx::DrawEventRecorderPrivate { 129 public: 130 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderPRFileDesc, override) 131 explicit DrawEventRecorderPRFileDesc() = default; 132 ~DrawEventRecorderPRFileDesc(); 133 134 gfx::RecorderType GetRecorderType() const final { 135 return gfx::RecorderType::PRFILEDESC; 136 } 137 138 void RecordEvent(const gfx::RecordedEvent& aEvent) override; 139 140 /** 141 * Returns whether a recording file is currently open. 142 */ 143 bool IsOpen(); 144 145 /** 146 * Opens the recorder with the provided PRFileDesc *. 147 */ 148 void OpenFD(PRFileDesc* aFd); 149 150 /** 151 * Closes the file so that it can be processed. The recorder does NOT forget 152 * which objects it has recorded. This can be used with OpenNew, so that a 153 * recording can be processed in chunks. The file must be open. 154 */ 155 void Close(); 156 157 void AddDependentSurface(uint64_t aDependencyId) override; 158 nsTArray<uint64_t>&& TakeDependentSurfaces(); 159 160 private: 161 void Flush() override; 162 163 PRFileDescStream mOutputStream; 164 nsTArray<uint64_t> mDependentSurfaces; 165 }; 166 167 } // namespace layout 168 169 } // namespace mozilla 170 171 #endif /* mozilla_layout_printing_DrawEventRecorder_h */