summaryrefslogtreecommitdiffstats
path: root/layout/printing/DrawEventRecorder.h
blob: 26adf8862ab2bf4c0534b003ba5b86afbced5057 (plain)
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
/* -*- 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 <memory>

#include "mozilla/gfx/DrawEventRecorder.h"
#include "mozilla/gfx/RecordingTypes.h"
#include "prio.h"
#include "nsTArray.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<const void*>(mBuffer.get()), mBufferPos);
      mGood = length >= 0 && static_cast<size_t>(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<const void*>(aData), aSize);
        mGood = length >= 0 && static_cast<size_t>(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<void*>(aOut), aSize);
    mGood = res >= 0 && (static_cast<size_t>(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<uint8_t[]> mBuffer;
  size_t mBufferPos;
  bool mGood;
};

class DrawEventRecorderPRFileDesc final : public gfx::DrawEventRecorderPrivate {
 public:
  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderPRFileDesc, override)
  explicit DrawEventRecorderPRFileDesc() = default;
  ~DrawEventRecorderPRFileDesc();

  gfx::RecorderType GetRecorderType() const final {
    return gfx::RecorderType::PRFILEDESC;
  }

  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();

  void AddDependentSurface(uint64_t aDependencyId) override;
  nsTArray<uint64_t>&& TakeDependentSurfaces();

 private:
  void Flush() override;

  PRFileDescStream mOutputStream;
  nsTArray<uint64_t> mDependentSurfaces;
};

}  // namespace layout

}  // namespace mozilla

#endif /* mozilla_layout_printing_DrawEventRecorder_h */