/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 mozilla_layout_printing_DrawEventRecorder_h #define mozilla_layout_printing_DrawEventRecorder_h #include #include "mozilla/gfx/DrawEventRecorder.h" #include "mozilla/gfx/RecordingTypes.h" #include "prio.h" namespace mozilla { namespace layout { class PRFileDescStream final : public mozilla::gfx::EventStream { // Most writes, as seen in the print IPC use case, are very small (<32 bytes), // with a small number of very large (>40KB) writes. Writes larger than this // value are not buffered. static const size_t kBufferSize = 1024; public: PRFileDescStream() : mFd(nullptr), mBuffer(nullptr), mBufferPos(0), mGood(true) {} PRFileDescStream(const PRFileDescStream& other) = delete; ~PRFileDescStream() { Close(); } void OpenFD(PRFileDesc* aFd) { MOZ_DIAGNOSTIC_ASSERT(!IsOpen()); mFd = aFd; mGood = !!mFd; mBuffer.reset(new uint8_t[kBufferSize]); mBufferPos = 0; } void Close() { // We need to be API compatible with std::ostream, and so we silently handle // closes on a closed FD. if (IsOpen()) { Flush(); PR_Close(mFd); mFd = nullptr; mBuffer.reset(); mBufferPos = 0; } } bool IsOpen() { return mFd != nullptr; } void Flush() { // See comment in Close(). if (IsOpen() && mBufferPos > 0) { PRInt32 length = PR_Write(mFd, static_cast(mBuffer.get()), mBufferPos); mGood = length >= 0 && static_cast(length) == mBufferPos; mBufferPos = 0; } } void Seek(PRInt64 aOffset, PRSeekWhence aWhence) { Flush(); PRInt64 pos = PR_Seek64(mFd, aOffset, aWhence); mGood = pos != -1; } void write(const char* aData, size_t aSize) override { if (!good()) { return; } // See comment in Close(). if (IsOpen()) { // If we're writing more data than could ever fit in our buffer, flush the // buffer and write directly. if (aSize > kBufferSize) { Flush(); PRInt32 length = PR_Write(mFd, static_cast(aData), aSize); mGood = length >= 0 && static_cast(length) == aSize; // If our write could fit in our buffer, but doesn't because the buffer // is partially full, write to the buffer, flush the buffer, and then // write the rest of the data to the buffer. } else if (aSize > AvailableBufferSpace()) { size_t length = AvailableBufferSpace(); WriteToBuffer(aData, length); Flush(); WriteToBuffer(aData + length, aSize - length); // Write fits in the buffer. } else { WriteToBuffer(aData, aSize); } } } void read(char* aOut, size_t aSize) override { if (!good()) { return; } Flush(); PRInt32 res = PR_Read(mFd, static_cast(aOut), aSize); mGood = res >= 0 && (static_cast(res) == aSize); } bool good() final { return mGood; } void SetIsBad() final { mGood = false; } private: size_t AvailableBufferSpace() { return kBufferSize - mBufferPos; } void WriteToBuffer(const char* aData, size_t aSize) { MOZ_ASSERT(aSize <= AvailableBufferSpace()); memcpy(mBuffer.get() + mBufferPos, aData, aSize); mBufferPos += aSize; } PRFileDesc* mFd; std::unique_ptr mBuffer; size_t mBufferPos; bool mGood; }; class DrawEventRecorderPRFileDesc final : public gfx::DrawEventRecorderPrivate { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderPRFileDesc, override) explicit DrawEventRecorderPRFileDesc(){}; ~DrawEventRecorderPRFileDesc(); void RecordEvent(const gfx::RecordedEvent& aEvent) override; /** * Returns whether a recording file is currently open. */ bool IsOpen(); /** * Opens the recorder with the provided PRFileDesc *. */ void OpenFD(PRFileDesc* aFd); /** * Closes the file so that it can be processed. The recorder does NOT forget * which objects it has recorded. This can be used with OpenNew, so that a * recording can be processed in chunks. The file must be open. */ void Close(); private: void Flush() override; PRFileDescStream mOutputStream; }; } // namespace layout } // namespace mozilla #endif /* mozilla_layout_printing_DrawEventRecorder_h */