Global Metrics

path: .metrics.nexits.average
old: 0.6666666666666666
new: 1.1162790697674418

path: .metrics.nexits.sum
old: 2.0
new: 48.0

path: .metrics.cyclomatic.average
old: 3.0
new: 2.2452830188679247

path: .metrics.cyclomatic.sum
old: 12.0
new: 119.0

path: .metrics.nargs.average
old: 1.6666666666666667
new: 0.6744186046511628

path: .metrics.nargs.sum
old: 5.0
new: 29.0

path: .metrics.loc.lloc
old: 5.0
new: 214.0

path: .metrics.loc.blank
old: 50.0
new: 105.0

path: .metrics.loc.cloc
old: 43.0
new: 52.0

path: .metrics.loc.sloc
old: 288.0
new: 675.0

path: .metrics.loc.ploc
old: 195.0
new: 518.0

path: .metrics.halstead.N1
old: 247.0
new: 1491.0

path: .metrics.halstead.bugs
old: 0.7130407462111065
new: 4.544279849278452

path: .metrics.halstead.n1
old: 20.0
new: 37.0

path: .metrics.halstead.estimated_program_length
old: 1231.6940194737326
new: 1858.611237190005

path: .metrics.halstead.length
old: 596.0
new: 2441.0

path: .metrics.halstead.purity_ratio
old: 2.066600703814988
new: 0.7614138620196662

path: .metrics.halstead.time
old: 5496.432501201217
new: 88431.4975850394

path: .metrics.halstead.volume
old: 4450.692907849467
new: 19472.540293263297

path: .metrics.halstead.effort
old: 98935.78502162192
new: 1591766.956530709

path: .metrics.halstead.level
old: 0.04498567335243553
new: 0.012233285917496443

path: .metrics.halstead.N2
old: 349.0
new: 950.0

path: .metrics.halstead.difficulty
old: 22.22929936305733
new: 81.74418604651163

path: .metrics.halstead.n2
old: 157.0
new: 215.0

path: .metrics.halstead.vocabulary
old: 177.0
new: 252.0

path: .metrics.mi.mi_visual_studio
old: 19.19052739410797
new: 0.0

path: .metrics.mi.mi_sei
old: 1.0388958348046202
new: -61.88229299670572

path: .metrics.mi.mi_original
old: 32.81580184392463
new: -13.26750051469783

path: .metrics.cognitive.average
old: 2.0
new: 1.372093023255814

path: .metrics.cognitive.sum
old: 6.0
new: 59.0

path: .metrics.nom.closures
old: 0.0
new: 1.0

path: .metrics.nom.functions
old: 3.0
new: 42.0

path: .metrics.nom.total
old: 3.0
new: 43.0

Spaces Data

Minimal test - lines (11, 675)

path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 2.269230769230769

path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 118.0

path: .spaces[0].metrics.nom.functions
old: 1.0
new: 42.0

path: .spaces[0].metrics.nom.total
old: 1.0
new: 43.0

path: .spaces[0].metrics.nom.closures
old: 0.0
new: 1.0

path: .spaces[0].metrics.nargs.average
old: 3.0
new: 0.6744186046511628

path: .spaces[0].metrics.nargs.sum
old: 3.0
new: 29.0

path: .spaces[0].metrics.cognitive.average
old: 0.0
new: 1.372093023255814

path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 59.0

path: .spaces[0].metrics.halstead.time
old: 27.782333124327245
new: 88872.68454594695

path: .spaces[0].metrics.halstead.estimated_program_length
old: 44.039100017307746
new: 1840.2429236363748

path: .spaces[0].metrics.halstead.bugs
old: 0.02100097985297437
new: 4.559381660232157

path: .spaces[0].metrics.halstead.difficulty
old: 4.0
new: 82.33802816901408

path: .spaces[0].metrics.halstead.vocabulary
old: 15.0
new: 250.0

path: .spaces[0].metrics.halstead.volume
old: 125.0204990594726
new: 19428.54787029083

path: .spaces[0].metrics.halstead.n2
old: 9.0
new: 213.0

path: .spaces[0].metrics.halstead.effort
old: 500.0819962378904
new: 1599708.321827045

path: .spaces[0].metrics.halstead.level
old: 0.25
new: 0.012145056448853915

path: .spaces[0].metrics.halstead.n1
old: 6.0
new: 37.0

path: .spaces[0].metrics.halstead.purity_ratio
old: 1.376221875540867
new: 0.7545071437623513

path: .spaces[0].metrics.halstead.N1
old: 20.0
new: 1491.0

path: .spaces[0].metrics.halstead.N2
old: 12.0
new: 948.0

path: .spaces[0].metrics.halstead.length
old: 32.0
new: 2439.0

path: .spaces[0].metrics.loc.blank
old: 0.0
new: 104.0

path: .spaces[0].metrics.loc.lloc
old: 2.0
new: 214.0

path: .spaces[0].metrics.loc.sloc
old: 7.0
new: 665.0

path: .spaces[0].metrics.loc.ploc
old: 7.0
new: 514.0

path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 47.0

path: .spaces[0].metrics.mi.mi_original
old: 114.13817146037346
new: -12.783943834763464

path: .spaces[0].metrics.mi.mi_visual_studio
old: 66.74746869027688
new: 0.0

path: .spaces[0].metrics.mi.mi_sei
old: 89.06754180940669
new: -62.11397268830072

path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 48.0

path: .spaces[0].metrics.nexits.average
old: 0.0
new: 1.1162790697674418

Code

namespace mozilla {
namespace widget {
namespace remote_backbuffer {

// This number can be adjusted as a time-memory tradeoff
constexpr uint8_t kMaxDirtyRects = 8;

struct IpcSafeRect {
  explicit IpcSafeRect(const gfx::IntRect& aRect)
      : x(aRect.x), y(aRect.y), width(aRect.width), height(aRect.height) {}
  int32_t x;
  int32_t y;
  int32_t width;
  int32_t height;
};

enum class ResponseResult {
  Unknown,
  Error,
  BorrowSuccess,
  BorrowSameBuffer,
  PresentSuccess
};

enum class SharedDataType {
  BorrowRequest,
  BorrowRequestAllowSameBuffer,
  BorrowResponse,
  PresentRequest,
  PresentResponse
};

struct BorrowResponseData {
  ResponseResult result;
  int32_t width;
  int32_t height;
  HANDLE fileMapping;
};

struct PresentRequestData {
  uint8_t lenDirtyRects;
  IpcSafeRect dirtyRects[kMaxDirtyRects];
};

struct PresentResponseData {
  ResponseResult result;
};

struct SharedData {
  SharedDataType dataType;
  union {
    BorrowResponseData borrowResponse;
    PresentRequestData presentRequest;
    PresentResponseData presentResponse;
  } data;
};

static_assert(std::is_trivially_copyable::value &&
                  std::is_standard_layout::value,
              "SharedData must be safe to pass over IPC boundaries");

class SharedImage {
 public:
  SharedImage()
      : mWidth(0), mHeight(0), mFileMapping(nullptr), mPixelData(nullptr) {}

  ~SharedImage() {
    if (mPixelData) {
      MOZ_ALWAYS_TRUE(::UnmapViewOfFile(mPixelData));
    }

    if (mFileMapping) {
      MOZ_ALWAYS_TRUE(::CloseHandle(mFileMapping));
    }
  }

  bool Initialize(int32_t aWidth, int32_t aHeight) {
    MOZ_ASSERT(aWidth > 0);
    MOZ_ASSERT(aHeight > 0);

    mWidth = aWidth;
    mHeight = aHeight;

    DWORD bufferSize = static_cast(mHeight * GetStride());

    mFileMapping = ::CreateFileMappingW(
        INVALID_HANDLE_VALUE, nullptr /*secattr*/, PAGE_READWRITE,
        0 /*sizeHigh*/, bufferSize, nullptr /*name*/);
    if (!mFileMapping) {
      return false;
    }

    void* mappedFilePtr =
        ::MapViewOfFile(mFileMapping, FILE_MAP_ALL_ACCESS, 0 /*offsetHigh*/,
                        0 /*offsetLow*/, 0 /*bytesToMap*/);
    if (!mappedFilePtr) {
      return false;
    }

    mPixelData = reinterpret_cast(mappedFilePtr);

    return true;
  }

  bool InitializeRemote(int32_t aWidth, int32_t aHeight, HANDLE aFileMapping) {
    MOZ_ASSERT(aWidth > 0);
    MOZ_ASSERT(aHeight > 0);
    MOZ_ASSERT(aFileMapping);

    mWidth = aWidth;
    mHeight = aHeight;
    mFileMapping = aFileMapping;

    void* mappedFilePtr =
        ::MapViewOfFile(mFileMapping, FILE_MAP_ALL_ACCESS, 0 /*offsetHigh*/,
                        0 /*offsetLow*/, 0 /*bytesToMap*/);
    if (!mappedFilePtr) {
      return false;
    }

    mPixelData = reinterpret_cast(mappedFilePtr);

    return true;
  }

  HBITMAP CreateDIBSection() {
    BITMAPINFO bitmapInfo = {};
    bitmapInfo.bmiHeader.biSize = sizeof(bitmapInfo.bmiHeader);
    bitmapInfo.bmiHeader.biWidth = mWidth;
    bitmapInfo.bmiHeader.biHeight = -mHeight;
    bitmapInfo.bmiHeader.biPlanes = 1;
    bitmapInfo.bmiHeader.biBitCount = 32;
    bitmapInfo.bmiHeader.biCompression = BI_RGB;
    void* dummy = nullptr;
    return ::CreateDIBSection(nullptr /*paletteDC*/, &bitmapInfo,
                              DIB_RGB_COLORS, &dummy, mFileMapping,
                              0 /*offset*/);
  }

  HANDLE CreateRemoteFileMapping(DWORD aTargetProcessId) {
    MOZ_ASSERT(aTargetProcessId);

    HANDLE fileMapping = nullptr;
    if (!ipc::DuplicateHandle(mFileMapping, aTargetProcessId, &fileMapping,
                              0 /*desiredAccess*/, DUPLICATE_SAME_ACCESS)) {
      return nullptr;
    }
    return fileMapping;
  }

  already_AddRefed CreateDrawTarget() {
    return gfx::Factory::CreateDrawTargetForData(
        gfx::BackendType::CAIRO, mPixelData, IntSize(mWidth, mHeight),
        GetStride(), gfx::SurfaceFormat::B8G8R8A8);
  }

  void CopyPixelsFrom(const SharedImage& other) {
    const unsigned char* src = other.mPixelData;
    unsigned char* dst = mPixelData;

    int32_t width = std::min(mWidth, other.mWidth);
    int32_t height = std::min(mHeight, other.mHeight);

    for (int32_t row = 0; row < height; ++row) {
      memcpy(dst, src, static_cast(width * kBytesPerPixel));
      src += other.GetStride();
      dst += GetStride();
    }
  }

  int32_t GetWidth() { return mWidth; }

  int32_t GetHeight() { return mHeight; }

  SharedImage(const SharedImage&) = delete;
  SharedImage(SharedImage&&) = delete;
  SharedImage& operator=(const SharedImage&) = delete;
  SharedImage& operator=(SharedImage&&) = delete;

 private:
  static constexpr int32_t kBytesPerPixel = 4;

  int32_t GetStride() const {
    // DIB requires 32-bit row alignment
    return (((mWidth * kBytesPerPixel) + 3) / 4) * 4;
  }

  int32_t mWidth;
  int32_t mHeight;
  HANDLE mFileMapping;
  unsigned char* mPixelData;
};

class PresentableSharedImage {
 public:
  PresentableSharedImage()
      : mSharedImage(),
        mDeviceContext(nullptr),
        mDIBSection(nullptr),
        mSavedObject(nullptr) {}

  ~PresentableSharedImage() {
    if (mSavedObject) {
      MOZ_ALWAYS_TRUE(::SelectObject(mDeviceContext, mSavedObject));
    }

    if (mDIBSection) {
      MOZ_ALWAYS_TRUE(::DeleteObject(mDIBSection));
    }

    if (mDeviceContext) {
      MOZ_ALWAYS_TRUE(::DeleteDC(mDeviceContext));
    }
  }

  bool Initialize(int32_t aWidth, int32_t aHeight) {
    if (!mSharedImage.Initialize(aWidth, aHeight)) {
      return false;
    }

    mDeviceContext = ::CreateCompatibleDC(nullptr);
    if (!mDeviceContext) {
      return false;
    }

    mDIBSection = mSharedImage.CreateDIBSection();
    if (!mDIBSection) {
      return false;
    }

    mSavedObject = ::SelectObject(mDeviceContext, mDIBSection);
    if (!mSavedObject) {
      return false;
    }

    return true;
  }

  bool PresentToWindow(HWND aWindowHandle, nsTransparencyMode aTransparencyMode,
                       Span aDirtyRects) {
    if (aTransparencyMode == eTransparencyTransparent) {
      // If our window is a child window or a child-of-a-child, the window
      // that needs to be updated is the top level ancestor of the tree
      HWND topLevelWindow = WinUtils::GetTopLevelHWND(aWindowHandle, true);
      MOZ_ASSERT(::GetWindowLongPtr(topLevelWindow, GWL_EXSTYLE) &
                 WS_EX_LAYERED);

      BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
      SIZE winSize = {mSharedImage.GetWidth(), mSharedImage.GetHeight()};
      POINT srcPos = {0, 0};
      return !!::UpdateLayeredWindow(
          topLevelWindow, nullptr /*paletteDC*/, nullptr /*newPos*/, &winSize,
          mDeviceContext, &srcPos, 0 /*colorKey*/, &bf, ULW_ALPHA);
    }

    IntRect sharedImageRect{0, 0, mSharedImage.GetWidth(),
                            mSharedImage.GetHeight()};

    bool result = true;

    HDC windowDC = ::GetDC(aWindowHandle);
    if (!windowDC) {
      return false;
    }

    for (auto& ipcDirtyRect : aDirtyRects) {
      IntRect dirtyRect{ipcDirtyRect.x, ipcDirtyRect.y, ipcDirtyRect.width,
                        ipcDirtyRect.height};
      IntRect bltRect = dirtyRect.Intersect(sharedImageRect);

      if (!::BitBlt(windowDC, bltRect.x /*dstX*/, bltRect.y /*dstY*/,
                    bltRect.width, bltRect.height, mDeviceContext,
                    bltRect.x /*srcX*/, bltRect.y /*srcY*/, SRCCOPY)) {
        result = false;
        break;
      }
    }

    MOZ_ALWAYS_TRUE(::ReleaseDC(aWindowHandle, windowDC));

    return result;
  }

  HANDLE CreateRemoteFileMapping(DWORD aTargetProcessId) {
    return mSharedImage.CreateRemoteFileMapping(aTargetProcessId);
  }

  already_AddRefed CreateDrawTarget() {
    return mSharedImage.CreateDrawTarget();
  }

  void CopyPixelsFrom(const PresentableSharedImage& other) {
    mSharedImage.CopyPixelsFrom(other.mSharedImage);
  }

  int32_t GetWidth() { return mSharedImage.GetWidth(); }

  int32_t GetHeight() { return mSharedImage.GetHeight(); }

  PresentableSharedImage(const PresentableSharedImage&) = delete;
  PresentableSharedImage(PresentableSharedImage&&) = delete;
  PresentableSharedImage& operator=(const PresentableSharedImage&) = delete;
  PresentableSharedImage& operator=(PresentableSharedImage&&) = delete;

 private:
  SharedImage mSharedImage;
  HDC mDeviceContext;
  HBITMAP mDIBSection;
  HGDIOBJ mSavedObject;
};

Provider::Provider()
    : mWindowHandle(nullptr),
      mTargetProcessId(0),
      mFileMapping(nullptr),
      mRequestReadyEvent(nullptr),
      mResponseReadyEvent(nullptr),
      mSharedDataPtr(nullptr),
      mStopServiceThread(false),
      mServiceThread(),
      mBackbuffer() {}

Provider::~Provider() {
  mBackbuffer.reset();

  if (mServiceThread.joinable()) {
    mStopServiceThread = true;
    MOZ_ALWAYS_TRUE(::SetEvent(mRequestReadyEvent));
    mServiceThread.join();
  }

  if (mSharedDataPtr) {
    MOZ_ALWAYS_TRUE(::UnmapViewOfFile(mSharedDataPtr));
  }

  if (mResponseReadyEvent) {
    MOZ_ALWAYS_TRUE(::CloseHandle(mResponseReadyEvent));
  }

  if (mRequestReadyEvent) {
    MOZ_ALWAYS_TRUE(::CloseHandle(mRequestReadyEvent));
  }

  if (mFileMapping) {
    MOZ_ALWAYS_TRUE(::CloseHandle(mFileMapping));
  }
}

bool Provider::Initialize(HWND aWindowHandle, DWORD aTargetProcessId,
                          nsTransparencyMode aTransparencyMode) {
  MOZ_ASSERT(aWindowHandle);
  MOZ_ASSERT(aTargetProcessId);

  mWindowHandle = aWindowHandle;
  mTargetProcessId = aTargetProcessId;

  mFileMapping = ::CreateFileMappingW(
      INVALID_HANDLE_VALUE, nullptr /*secattr*/, PAGE_READWRITE, 0 /*sizeHigh*/,
      static_cast(sizeof(SharedData)), nullptr /*name*/);
  if (!mFileMapping) {
    return false;
  }

  mRequestReadyEvent =
      ::CreateEventW(nullptr /*secattr*/, FALSE /*manualReset*/,
                     FALSE /*initialState*/, nullptr /*name*/);
  if (!mRequestReadyEvent) {
    return false;
  }

  mResponseReadyEvent =
      ::CreateEventW(nullptr /*secattr*/, FALSE /*manualReset*/,
                     FALSE /*initialState*/, nullptr /*name*/);
  if (!mResponseReadyEvent) {
    return false;
  }

  void* mappedFilePtr =
      ::MapViewOfFile(mFileMapping, FILE_MAP_ALL_ACCESS, 0 /*offsetHigh*/,
                      0 /*offsetLow*/, 0 /*bytesToMap*/);
  if (!mappedFilePtr) {
    return false;
  }

  mSharedDataPtr = reinterpret_cast(mappedFilePtr);

  mStopServiceThread = false;

  mServiceThread = std::thread([this] { this->ThreadMain(); });

  mTransparencyMode = aTransparencyMode;

  return true;
}

Maybe Provider::CreateRemoteHandles() {
  HANDLE fileMapping = nullptr;
  if (!ipc::DuplicateHandle(mFileMapping, mTargetProcessId, &fileMapping,
                            0 /*desiredAccess*/, DUPLICATE_SAME_ACCESS)) {
    return Nothing();
  }

  HANDLE requestReadyEvent = nullptr;
  if (!ipc::DuplicateHandle(mRequestReadyEvent, mTargetProcessId,
                            &requestReadyEvent, 0 /*desiredAccess*/,
                            DUPLICATE_SAME_ACCESS)) {
    return Nothing();
  }

  HANDLE responseReadyEvent = nullptr;
  if (!ipc::DuplicateHandle(mResponseReadyEvent, mTargetProcessId,
                            &responseReadyEvent, 0 /*desiredAccess*/,
                            DUPLICATE_SAME_ACCESS)) {
    return Nothing();
  }

  return Some(RemoteBackbufferHandles(
      reinterpret_cast(fileMapping),
      reinterpret_cast(requestReadyEvent),
      reinterpret_cast(responseReadyEvent)));
}

void Provider::UpdateTransparencyMode(nsTransparencyMode aTransparencyMode) {
  mTransparencyMode = aTransparencyMode;
}

void Provider::ThreadMain() {
  while (true) {
    MOZ_ALWAYS_TRUE(::WaitForSingleObject(mRequestReadyEvent, INFINITE) ==
                    WAIT_OBJECT_0);

    if (mStopServiceThread) {
      break;
    }

    switch (mSharedDataPtr->dataType) {
      case SharedDataType::BorrowRequest:
      case SharedDataType::BorrowRequestAllowSameBuffer: {
        BorrowResponseData responseData = {};

        HandleBorrowRequest(&responseData,
                            mSharedDataPtr->dataType ==
                                SharedDataType::BorrowRequestAllowSameBuffer);

        mSharedDataPtr->dataType = SharedDataType::BorrowResponse;
        mSharedDataPtr->data.borrowResponse = responseData;

        MOZ_ALWAYS_TRUE(::SetEvent(mResponseReadyEvent));

        break;
      }
      case SharedDataType::PresentRequest: {
        PresentRequestData requestData = mSharedDataPtr->data.presentRequest;
        PresentResponseData responseData = {};

        HandlePresentRequest(requestData, &responseData);

        mSharedDataPtr->dataType = SharedDataType::PresentResponse;
        mSharedDataPtr->data.presentResponse = responseData;

        MOZ_ALWAYS_TRUE(::SetEvent(mResponseReadyEvent));

        break;
      }
      default:
        break;
    };
  }
}

void Provider::HandleBorrowRequest(BorrowResponseData* aResponseData,
                                   bool aAllowSameBuffer) {
  MOZ_ASSERT(aResponseData);

  aResponseData->result = ResponseResult::Error;

  RECT clientRect = {};
  if (!::GetClientRect(mWindowHandle, &clientRect)) {
    return;
  }

  MOZ_ASSERT(clientRect.left == 0);
  MOZ_ASSERT(clientRect.top == 0);

  int32_t width = clientRect.right ? clientRect.right : 1;
  int32_t height = clientRect.bottom ? clientRect.bottom : 1;

  bool needNewBackbuffer = !aAllowSameBuffer || !mBackbuffer ||
                           (mBackbuffer->GetWidth() != width) ||
                           (mBackbuffer->GetHeight() != height);

  if (!needNewBackbuffer) {
    aResponseData->result = ResponseResult::BorrowSameBuffer;
    return;
  }

  auto newBackbuffer = std::make_unique();
  if (!newBackbuffer->Initialize(width, height)) {
    return;
  }

  // Preserve the contents of the old backbuffer (if it exists)
  if (mBackbuffer) {
    newBackbuffer->CopyPixelsFrom(*mBackbuffer);
    mBackbuffer.reset();
  }

  HANDLE remoteFileMapping =
      newBackbuffer->CreateRemoteFileMapping(mTargetProcessId);
  if (!remoteFileMapping) {
    return;
  }

  aResponseData->result = ResponseResult::BorrowSuccess;
  aResponseData->width = width;
  aResponseData->height = height;
  aResponseData->fileMapping = remoteFileMapping;

  mBackbuffer = std::move(newBackbuffer);
}

void Provider::HandlePresentRequest(const PresentRequestData& aRequestData,
                                    PresentResponseData* aResponseData) {
  MOZ_ASSERT(aResponseData);

  Span rectSpan(aRequestData.dirtyRects, kMaxDirtyRects);

  aResponseData->result = ResponseResult::Error;

  if (!mBackbuffer) {
    return;
  }

  if (!mBackbuffer->PresentToWindow(
          mWindowHandle, mTransparencyMode,
          rectSpan.First(aRequestData.lenDirtyRects))) {
    return;
  }

  aResponseData->result = ResponseResult::PresentSuccess;
}

Client::Client()
    : mFileMapping(nullptr),
      mRequestReadyEvent(nullptr),
      mResponseReadyEvent(nullptr),
      mSharedDataPtr(nullptr),
      mBackbuffer() {}

Client::~Client() {
  mBackbuffer.reset();

  if (mSharedDataPtr) {
    MOZ_ALWAYS_TRUE(::UnmapViewOfFile(mSharedDataPtr));
  }

  if (mResponseReadyEvent) {
    MOZ_ALWAYS_TRUE(::CloseHandle(mResponseReadyEvent));
  }

  if (mRequestReadyEvent) {
    MOZ_ALWAYS_TRUE(::CloseHandle(mRequestReadyEvent));
  }

  if (mFileMapping) {
    MOZ_ALWAYS_TRUE(::CloseHandle(mFileMapping));
  }
}

bool Client::Initialize(const RemoteBackbufferHandles& aRemoteHandles) {
  MOZ_ASSERT(aRemoteHandles.fileMapping());
  MOZ_ASSERT(aRemoteHandles.requestReadyEvent());
  MOZ_ASSERT(aRemoteHandles.responseReadyEvent());

  mFileMapping = reinterpret_cast(aRemoteHandles.fileMapping());
  mRequestReadyEvent =
      reinterpret_cast(aRemoteHandles.requestReadyEvent());
  mResponseReadyEvent =
      reinterpret_cast(aRemoteHandles.responseReadyEvent());

  void* mappedFilePtr =
      ::MapViewOfFile(mFileMapping, FILE_MAP_ALL_ACCESS, 0 /*offsetHigh*/,
                      0 /*offsetLow*/, 0 /*bytesToMap*/);
  if (!mappedFilePtr) {
    return false;
  }

  mSharedDataPtr = reinterpret_cast(mappedFilePtr);

  return true;
}

already_AddRefed Client::BorrowDrawTarget() {
  mSharedDataPtr->dataType = mBackbuffer
                                 ? SharedDataType::BorrowRequestAllowSameBuffer
                                 : SharedDataType::BorrowRequest;

  MOZ_ALWAYS_TRUE(::SetEvent(mRequestReadyEvent));
  MOZ_ALWAYS_TRUE(::WaitForSingleObject(mResponseReadyEvent, INFINITE) ==
                  WAIT_OBJECT_0);

  if (mSharedDataPtr->dataType != SharedDataType::BorrowResponse) {
    return nullptr;
  }

  BorrowResponseData responseData = mSharedDataPtr->data.borrowResponse;

  if ((responseData.result != ResponseResult::BorrowSameBuffer) &&
      (responseData.result != ResponseResult::BorrowSuccess)) {
    return nullptr;
  }

  if (responseData.result == ResponseResult::BorrowSuccess) {
    mBackbuffer.reset();

    auto newBackbuffer = std::make_unique();
    if (!newBackbuffer->InitializeRemote(responseData.width,
                                         responseData.height,
                                         responseData.fileMapping)) {
      return nullptr;
    }

    mBackbuffer = std::move(newBackbuffer);
  }

  MOZ_ASSERT(mBackbuffer);

  return mBackbuffer->CreateDrawTarget();
}

bool Client::PresentDrawTarget(gfx::IntRegion aDirtyRegion) {
  mSharedDataPtr->dataType = SharedDataType::PresentRequest;

  // Simplify the region until it has <= kMaxDirtyRects
  aDirtyRegion.SimplifyOutward(kMaxDirtyRects);

  Span rectSpan(mSharedDataPtr->data.presentRequest.dirtyRects, kMaxDirtyRects);

  uint8_t rectIndex = 0;
  for (auto iter = aDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
    rectSpan[rectIndex] = IpcSafeRect(iter.Get());
    ++rectIndex;
  }

  mSharedDataPtr->data.presentRequest.lenDirtyRects = rectIndex;

  MOZ_ALWAYS_TRUE(::SetEvent(mRequestReadyEvent));
  MOZ_ALWAYS_TRUE(::WaitForSingleObject(mResponseReadyEvent, INFINITE) ==
                  WAIT_OBJECT_0);

  if (mSharedDataPtr->dataType != SharedDataType::PresentResponse) {
    return false;
  }

  if (mSharedDataPtr->data.presentResponse.result !=
      ResponseResult::PresentSuccess) {
    return false;
  }

  return true;
}

}  // namespace remote_backbuffer
}  // namespace widget
}  // namespace mozilla