Update configs. IGNORE BROKEN CHANGESETS CLOSED TREE NO BUG a=release ba=release
[gecko.git] / widget / windows / RemoteBackbuffer.cpp
blob56a8bae0fec502a2cbe7abaeb9114d6b4e5b84a6
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "RemoteBackbuffer.h"
7 #include "GeckoProfiler.h"
8 #include "nsThreadUtils.h"
9 #include "mozilla/Span.h"
10 #include "mozilla/gfx/Point.h"
11 #include "WinUtils.h"
12 #include <algorithm>
13 #include <type_traits>
15 namespace mozilla {
16 namespace widget {
17 namespace remote_backbuffer {
19 // This number can be adjusted as a time-memory tradeoff
20 constexpr uint8_t kMaxDirtyRects = 8;
22 struct IpcSafeRect {
23 explicit IpcSafeRect(const gfx::IntRect& aRect)
24 : x(aRect.x), y(aRect.y), width(aRect.width), height(aRect.height) {}
25 int32_t x;
26 int32_t y;
27 int32_t width;
28 int32_t height;
31 enum class ResponseResult {
32 Unknown,
33 Error,
34 BorrowSuccess,
35 BorrowSameBuffer,
36 PresentSuccess
39 enum class SharedDataType {
40 BorrowRequest,
41 BorrowRequestAllowSameBuffer,
42 BorrowResponse,
43 PresentRequest,
44 PresentResponse
47 struct BorrowResponseData {
48 ResponseResult result;
49 int32_t width;
50 int32_t height;
51 HANDLE fileMapping;
54 struct PresentRequestData {
55 uint8_t lenDirtyRects;
56 IpcSafeRect dirtyRects[kMaxDirtyRects];
59 struct PresentResponseData {
60 ResponseResult result;
63 struct SharedData {
64 SharedDataType dataType;
65 union {
66 BorrowResponseData borrowResponse;
67 PresentRequestData presentRequest;
68 PresentResponseData presentResponse;
69 } data;
72 static_assert(std::is_trivially_copyable<SharedData>::value &&
73 std::is_standard_layout<SharedData>::value,
74 "SharedData must be safe to pass over IPC boundaries");
76 class SharedImage {
77 public:
78 SharedImage()
79 : mWidth(0), mHeight(0), mFileMapping(nullptr), mPixelData(nullptr) {}
81 ~SharedImage() {
82 if (mPixelData) {
83 MOZ_ALWAYS_TRUE(::UnmapViewOfFile(mPixelData));
86 if (mFileMapping) {
87 MOZ_ALWAYS_TRUE(::CloseHandle(mFileMapping));
91 bool Initialize(int32_t aWidth, int32_t aHeight) {
92 MOZ_ASSERT(aWidth > 0);
93 MOZ_ASSERT(aHeight > 0);
95 mWidth = aWidth;
96 mHeight = aHeight;
98 DWORD bufferSize = static_cast<DWORD>(mHeight * GetStride());
100 mFileMapping = ::CreateFileMappingW(
101 INVALID_HANDLE_VALUE, nullptr /*secattr*/, PAGE_READWRITE,
102 0 /*sizeHigh*/, bufferSize, nullptr /*name*/);
103 if (!mFileMapping) {
104 return false;
107 void* mappedFilePtr =
108 ::MapViewOfFile(mFileMapping, FILE_MAP_ALL_ACCESS, 0 /*offsetHigh*/,
109 0 /*offsetLow*/, 0 /*bytesToMap*/);
110 if (!mappedFilePtr) {
111 return false;
114 mPixelData = reinterpret_cast<unsigned char*>(mappedFilePtr);
116 return true;
119 bool InitializeRemote(int32_t aWidth, int32_t aHeight, HANDLE aFileMapping) {
120 MOZ_ASSERT(aWidth > 0);
121 MOZ_ASSERT(aHeight > 0);
122 MOZ_ASSERT(aFileMapping);
124 mWidth = aWidth;
125 mHeight = aHeight;
126 mFileMapping = aFileMapping;
128 void* mappedFilePtr =
129 ::MapViewOfFile(mFileMapping, FILE_MAP_ALL_ACCESS, 0 /*offsetHigh*/,
130 0 /*offsetLow*/, 0 /*bytesToMap*/);
131 if (!mappedFilePtr) {
132 return false;
135 mPixelData = reinterpret_cast<unsigned char*>(mappedFilePtr);
137 return true;
140 HBITMAP CreateDIBSection() {
141 BITMAPINFO bitmapInfo = {};
142 bitmapInfo.bmiHeader.biSize = sizeof(bitmapInfo.bmiHeader);
143 bitmapInfo.bmiHeader.biWidth = mWidth;
144 bitmapInfo.bmiHeader.biHeight = -mHeight;
145 bitmapInfo.bmiHeader.biPlanes = 1;
146 bitmapInfo.bmiHeader.biBitCount = 32;
147 bitmapInfo.bmiHeader.biCompression = BI_RGB;
148 void* dummy = nullptr;
149 return ::CreateDIBSection(nullptr /*paletteDC*/, &bitmapInfo,
150 DIB_RGB_COLORS, &dummy, mFileMapping,
151 0 /*offset*/);
154 HANDLE CreateRemoteFileMapping(HANDLE aTargetProcess) {
155 MOZ_ASSERT(aTargetProcess);
157 HANDLE fileMapping = nullptr;
158 if (!::DuplicateHandle(GetCurrentProcess(), mFileMapping, aTargetProcess,
159 &fileMapping, 0 /*desiredAccess*/,
160 FALSE /*inheritHandle*/, DUPLICATE_SAME_ACCESS)) {
161 return nullptr;
163 return fileMapping;
166 already_AddRefed<gfx::DrawTarget> CreateDrawTarget() {
167 return gfx::Factory::CreateDrawTargetForData(
168 gfx::BackendType::CAIRO, mPixelData, gfx::IntSize(mWidth, mHeight),
169 GetStride(), gfx::SurfaceFormat::B8G8R8A8);
172 void CopyPixelsFrom(const SharedImage& other) {
173 const unsigned char* src = other.mPixelData;
174 unsigned char* dst = mPixelData;
176 int32_t width = std::min(mWidth, other.mWidth);
177 int32_t height = std::min(mHeight, other.mHeight);
179 for (int32_t row = 0; row < height; ++row) {
180 memcpy(dst, src, static_cast<uint32_t>(width * kBytesPerPixel));
181 src += other.GetStride();
182 dst += GetStride();
186 int32_t GetWidth() { return mWidth; }
188 int32_t GetHeight() { return mHeight; }
190 SharedImage(const SharedImage&) = delete;
191 SharedImage(SharedImage&&) = delete;
192 SharedImage& operator=(const SharedImage&) = delete;
193 SharedImage& operator=(SharedImage&&) = delete;
195 private:
196 static constexpr int32_t kBytesPerPixel = 4;
198 int32_t GetStride() const {
199 // DIB requires 32-bit row alignment
200 return (((mWidth * kBytesPerPixel) + 3) / 4) * 4;
203 int32_t mWidth;
204 int32_t mHeight;
205 HANDLE mFileMapping;
206 unsigned char* mPixelData;
209 class PresentableSharedImage {
210 public:
211 PresentableSharedImage()
212 : mSharedImage(),
213 mDeviceContext(nullptr),
214 mDIBSection(nullptr),
215 mSavedObject(nullptr) {}
217 ~PresentableSharedImage() {
218 if (mSavedObject) {
219 MOZ_ALWAYS_TRUE(::SelectObject(mDeviceContext, mSavedObject));
222 if (mDIBSection) {
223 MOZ_ALWAYS_TRUE(::DeleteObject(mDIBSection));
226 if (mDeviceContext) {
227 MOZ_ALWAYS_TRUE(::DeleteDC(mDeviceContext));
231 bool Initialize(int32_t aWidth, int32_t aHeight) {
232 if (!mSharedImage.Initialize(aWidth, aHeight)) {
233 return false;
236 mDeviceContext = ::CreateCompatibleDC(nullptr);
237 if (!mDeviceContext) {
238 return false;
241 mDIBSection = mSharedImage.CreateDIBSection();
242 if (!mDIBSection) {
243 return false;
246 mSavedObject = ::SelectObject(mDeviceContext, mDIBSection);
247 if (!mSavedObject) {
248 return false;
251 return true;
254 bool PresentToWindow(HWND aWindowHandle, TransparencyMode aTransparencyMode,
255 Span<const IpcSafeRect> aDirtyRects) {
256 if (aTransparencyMode == TransparencyMode::Transparent) {
257 // If our window is a child window or a child-of-a-child, the window
258 // that needs to be updated is the top level ancestor of the tree
259 HWND topLevelWindow = WinUtils::GetTopLevelHWND(aWindowHandle, true);
260 MOZ_ASSERT(::GetWindowLongPtr(topLevelWindow, GWL_EXSTYLE) &
261 WS_EX_LAYERED);
263 BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
264 POINT srcPos = {0, 0};
265 RECT clientRect = {};
266 if (!::GetClientRect(aWindowHandle, &clientRect)) {
267 return false;
269 MOZ_ASSERT(clientRect.left == 0);
270 MOZ_ASSERT(clientRect.top == 0);
271 int32_t width = clientRect.right;
272 int32_t height = clientRect.bottom;
273 SIZE winSize = {width, height};
274 // Window resize could cause the client area to be different than
275 // mSharedImage's size. If the client area doesn't match,
276 // PresentToWindow() returns false without calling UpdateLayeredWindow().
277 // Another call to UpdateLayeredWindow() will follow shortly, since the
278 // resize will eventually force the backbuffer to repaint itself again.
279 // When client area is larger than mSharedImage's size,
280 // UpdateLayeredWindow() draws the window completely invisible. But it
281 // does not return false.
282 if (width != mSharedImage.GetWidth() ||
283 height != mSharedImage.GetHeight()) {
284 return false;
287 return !!::UpdateLayeredWindow(
288 topLevelWindow, nullptr /*paletteDC*/, nullptr /*newPos*/, &winSize,
289 mDeviceContext, &srcPos, 0 /*colorKey*/, &bf, ULW_ALPHA);
292 gfx::IntRect sharedImageRect{0, 0, mSharedImage.GetWidth(),
293 mSharedImage.GetHeight()};
295 bool result = true;
297 HDC windowDC = ::GetDC(aWindowHandle);
298 if (!windowDC) {
299 return false;
302 for (auto& ipcDirtyRect : aDirtyRects) {
303 gfx::IntRect dirtyRect{ipcDirtyRect.x, ipcDirtyRect.y, ipcDirtyRect.width,
304 ipcDirtyRect.height};
305 gfx::IntRect bltRect = dirtyRect.Intersect(sharedImageRect);
307 if (!::BitBlt(windowDC, bltRect.x /*dstX*/, bltRect.y /*dstY*/,
308 bltRect.width, bltRect.height, mDeviceContext,
309 bltRect.x /*srcX*/, bltRect.y /*srcY*/, SRCCOPY)) {
310 result = false;
311 break;
315 MOZ_ALWAYS_TRUE(::ReleaseDC(aWindowHandle, windowDC));
317 return result;
320 HANDLE CreateRemoteFileMapping(HANDLE aTargetProcess) {
321 return mSharedImage.CreateRemoteFileMapping(aTargetProcess);
324 already_AddRefed<gfx::DrawTarget> CreateDrawTarget() {
325 return mSharedImage.CreateDrawTarget();
328 void CopyPixelsFrom(const PresentableSharedImage& other) {
329 mSharedImage.CopyPixelsFrom(other.mSharedImage);
332 int32_t GetWidth() { return mSharedImage.GetWidth(); }
334 int32_t GetHeight() { return mSharedImage.GetHeight(); }
336 PresentableSharedImage(const PresentableSharedImage&) = delete;
337 PresentableSharedImage(PresentableSharedImage&&) = delete;
338 PresentableSharedImage& operator=(const PresentableSharedImage&) = delete;
339 PresentableSharedImage& operator=(PresentableSharedImage&&) = delete;
341 private:
342 SharedImage mSharedImage;
343 HDC mDeviceContext;
344 HBITMAP mDIBSection;
345 HGDIOBJ mSavedObject;
348 Provider::Provider()
349 : mWindowHandle(nullptr),
350 mTargetProcess(nullptr),
351 mFileMapping(nullptr),
352 mRequestReadyEvent(nullptr),
353 mResponseReadyEvent(nullptr),
354 mSharedDataPtr(nullptr),
355 mStopServiceThread(false),
356 mServiceThread(nullptr),
357 mBackbuffer() {}
359 Provider::~Provider() {
360 mBackbuffer.reset();
362 if (mServiceThread) {
363 mStopServiceThread = true;
364 MOZ_ALWAYS_TRUE(::SetEvent(mRequestReadyEvent));
365 MOZ_ALWAYS_TRUE(PR_JoinThread(mServiceThread) == PR_SUCCESS);
368 if (mSharedDataPtr) {
369 MOZ_ALWAYS_TRUE(::UnmapViewOfFile(mSharedDataPtr));
372 if (mResponseReadyEvent) {
373 MOZ_ALWAYS_TRUE(::CloseHandle(mResponseReadyEvent));
376 if (mRequestReadyEvent) {
377 MOZ_ALWAYS_TRUE(::CloseHandle(mRequestReadyEvent));
380 if (mFileMapping) {
381 MOZ_ALWAYS_TRUE(::CloseHandle(mFileMapping));
384 if (mTargetProcess) {
385 MOZ_ALWAYS_TRUE(::CloseHandle(mTargetProcess));
389 bool Provider::Initialize(HWND aWindowHandle, DWORD aTargetProcessId,
390 TransparencyMode aTransparencyMode) {
391 MOZ_ASSERT(aWindowHandle);
392 MOZ_ASSERT(aTargetProcessId);
394 mWindowHandle = aWindowHandle;
396 mTargetProcess = ::OpenProcess(PROCESS_DUP_HANDLE, FALSE /*inheritHandle*/,
397 aTargetProcessId);
398 if (!mTargetProcess) {
399 return false;
402 mFileMapping = ::CreateFileMappingW(
403 INVALID_HANDLE_VALUE, nullptr /*secattr*/, PAGE_READWRITE, 0 /*sizeHigh*/,
404 static_cast<DWORD>(sizeof(SharedData)), nullptr /*name*/);
405 if (!mFileMapping) {
406 return false;
409 mRequestReadyEvent =
410 ::CreateEventW(nullptr /*secattr*/, FALSE /*manualReset*/,
411 FALSE /*initialState*/, nullptr /*name*/);
412 if (!mRequestReadyEvent) {
413 return false;
416 mResponseReadyEvent =
417 ::CreateEventW(nullptr /*secattr*/, FALSE /*manualReset*/,
418 FALSE /*initialState*/, nullptr /*name*/);
419 if (!mResponseReadyEvent) {
420 return false;
423 void* mappedFilePtr =
424 ::MapViewOfFile(mFileMapping, FILE_MAP_ALL_ACCESS, 0 /*offsetHigh*/,
425 0 /*offsetLow*/, 0 /*bytesToMap*/);
426 if (!mappedFilePtr) {
427 return false;
430 mSharedDataPtr = reinterpret_cast<SharedData*>(mappedFilePtr);
432 mStopServiceThread = false;
434 // Use a raw NSPR OS-level thread here instead of nsThread because we are
435 // performing low-level synchronization across processes using Win32 Events,
436 // and nsThread is designed around an incompatible "in-process task queue"
437 // model
438 mServiceThread = PR_CreateThread(
439 PR_USER_THREAD, [](void* p) { static_cast<Provider*>(p)->ThreadMain(); },
440 this, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,
441 0 /*default stack size*/);
442 if (!mServiceThread) {
443 return false;
446 mTransparencyMode = uint32_t(aTransparencyMode);
448 return true;
451 Maybe<RemoteBackbufferHandles> Provider::CreateRemoteHandles() {
452 return Some(
453 RemoteBackbufferHandles(ipc::FileDescriptor(mFileMapping),
454 ipc::FileDescriptor(mRequestReadyEvent),
455 ipc::FileDescriptor(mResponseReadyEvent)));
458 void Provider::UpdateTransparencyMode(TransparencyMode aTransparencyMode) {
459 mTransparencyMode = uint32_t(aTransparencyMode);
462 void Provider::ThreadMain() {
463 AUTO_PROFILER_REGISTER_THREAD("RemoteBackbuffer");
464 NS_SetCurrentThreadName("RemoteBackbuffer");
466 while (true) {
468 AUTO_PROFILER_THREAD_SLEEP;
469 MOZ_ALWAYS_TRUE(::WaitForSingleObject(mRequestReadyEvent, INFINITE) ==
470 WAIT_OBJECT_0);
473 if (mStopServiceThread) {
474 break;
477 switch (mSharedDataPtr->dataType) {
478 case SharedDataType::BorrowRequest:
479 case SharedDataType::BorrowRequestAllowSameBuffer: {
480 BorrowResponseData responseData = {};
482 HandleBorrowRequest(&responseData,
483 mSharedDataPtr->dataType ==
484 SharedDataType::BorrowRequestAllowSameBuffer);
486 mSharedDataPtr->dataType = SharedDataType::BorrowResponse;
487 mSharedDataPtr->data.borrowResponse = responseData;
489 MOZ_ALWAYS_TRUE(::SetEvent(mResponseReadyEvent));
491 break;
493 case SharedDataType::PresentRequest: {
494 PresentRequestData requestData = mSharedDataPtr->data.presentRequest;
495 PresentResponseData responseData = {};
497 HandlePresentRequest(requestData, &responseData);
499 mSharedDataPtr->dataType = SharedDataType::PresentResponse;
500 mSharedDataPtr->data.presentResponse = responseData;
502 MOZ_ALWAYS_TRUE(::SetEvent(mResponseReadyEvent));
504 break;
506 default:
507 break;
512 void Provider::HandleBorrowRequest(BorrowResponseData* aResponseData,
513 bool aAllowSameBuffer) {
514 MOZ_ASSERT(aResponseData);
516 aResponseData->result = ResponseResult::Error;
518 RECT clientRect = {};
519 if (!::GetClientRect(mWindowHandle, &clientRect)) {
520 return;
523 MOZ_ASSERT(clientRect.left == 0);
524 MOZ_ASSERT(clientRect.top == 0);
526 int32_t width = clientRect.right ? clientRect.right : 1;
527 int32_t height = clientRect.bottom ? clientRect.bottom : 1;
529 bool needNewBackbuffer = !aAllowSameBuffer || !mBackbuffer ||
530 (mBackbuffer->GetWidth() != width) ||
531 (mBackbuffer->GetHeight() != height);
533 if (!needNewBackbuffer) {
534 aResponseData->result = ResponseResult::BorrowSameBuffer;
535 return;
538 auto newBackbuffer = std::make_unique<PresentableSharedImage>();
539 if (!newBackbuffer->Initialize(width, height)) {
540 return;
543 // Preserve the contents of the old backbuffer (if it exists)
544 if (mBackbuffer) {
545 newBackbuffer->CopyPixelsFrom(*mBackbuffer);
546 mBackbuffer.reset();
549 HANDLE remoteFileMapping =
550 newBackbuffer->CreateRemoteFileMapping(mTargetProcess);
551 if (!remoteFileMapping) {
552 return;
555 aResponseData->result = ResponseResult::BorrowSuccess;
556 aResponseData->width = width;
557 aResponseData->height = height;
558 aResponseData->fileMapping = remoteFileMapping;
560 mBackbuffer = std::move(newBackbuffer);
563 void Provider::HandlePresentRequest(const PresentRequestData& aRequestData,
564 PresentResponseData* aResponseData) {
565 MOZ_ASSERT(aResponseData);
567 Span rectSpan(aRequestData.dirtyRects, kMaxDirtyRects);
569 aResponseData->result = ResponseResult::Error;
571 if (!mBackbuffer) {
572 return;
575 if (!mBackbuffer->PresentToWindow(
576 mWindowHandle, GetTransparencyMode(),
577 rectSpan.First(aRequestData.lenDirtyRects))) {
578 return;
581 aResponseData->result = ResponseResult::PresentSuccess;
584 Client::Client()
585 : mFileMapping(nullptr),
586 mRequestReadyEvent(nullptr),
587 mResponseReadyEvent(nullptr),
588 mSharedDataPtr(nullptr),
589 mBackbuffer() {}
591 Client::~Client() {
592 mBackbuffer.reset();
594 if (mSharedDataPtr) {
595 MOZ_ALWAYS_TRUE(::UnmapViewOfFile(mSharedDataPtr));
598 if (mResponseReadyEvent) {
599 MOZ_ALWAYS_TRUE(::CloseHandle(mResponseReadyEvent));
602 if (mRequestReadyEvent) {
603 MOZ_ALWAYS_TRUE(::CloseHandle(mRequestReadyEvent));
606 if (mFileMapping) {
607 MOZ_ALWAYS_TRUE(::CloseHandle(mFileMapping));
611 bool Client::Initialize(const RemoteBackbufferHandles& aRemoteHandles) {
612 MOZ_ASSERT(aRemoteHandles.fileMapping().IsValid());
613 MOZ_ASSERT(aRemoteHandles.requestReadyEvent().IsValid());
614 MOZ_ASSERT(aRemoteHandles.responseReadyEvent().IsValid());
616 // FIXME: Due to PCompositorWidget using virtual Recv methods,
617 // RemoteBackbufferHandles is passed by const reference, and cannot have its
618 // signature customized, meaning that we need to clone the handles here.
620 // Once PCompositorWidget is migrated to use direct call semantics, the
621 // signature can be changed to accept RemoteBackbufferHandles by rvalue
622 // reference or value, and the DuplicateHandle calls here can be avoided.
623 mFileMapping = aRemoteHandles.fileMapping().ClonePlatformHandle().release();
624 mRequestReadyEvent =
625 aRemoteHandles.requestReadyEvent().ClonePlatformHandle().release();
626 mResponseReadyEvent =
627 aRemoteHandles.responseReadyEvent().ClonePlatformHandle().release();
629 void* mappedFilePtr =
630 ::MapViewOfFile(mFileMapping, FILE_MAP_ALL_ACCESS, 0 /*offsetHigh*/,
631 0 /*offsetLow*/, 0 /*bytesToMap*/);
632 if (!mappedFilePtr) {
633 return false;
636 mSharedDataPtr = reinterpret_cast<SharedData*>(mappedFilePtr);
638 return true;
641 already_AddRefed<gfx::DrawTarget> Client::BorrowDrawTarget() {
642 mSharedDataPtr->dataType = mBackbuffer
643 ? SharedDataType::BorrowRequestAllowSameBuffer
644 : SharedDataType::BorrowRequest;
646 MOZ_ALWAYS_TRUE(::SetEvent(mRequestReadyEvent));
647 MOZ_ALWAYS_TRUE(::WaitForSingleObject(mResponseReadyEvent, INFINITE) ==
648 WAIT_OBJECT_0);
650 if (mSharedDataPtr->dataType != SharedDataType::BorrowResponse) {
651 return nullptr;
654 BorrowResponseData responseData = mSharedDataPtr->data.borrowResponse;
656 if ((responseData.result != ResponseResult::BorrowSameBuffer) &&
657 (responseData.result != ResponseResult::BorrowSuccess)) {
658 return nullptr;
661 if (responseData.result == ResponseResult::BorrowSuccess) {
662 mBackbuffer.reset();
664 auto newBackbuffer = std::make_unique<SharedImage>();
665 if (!newBackbuffer->InitializeRemote(responseData.width,
666 responseData.height,
667 responseData.fileMapping)) {
668 return nullptr;
671 mBackbuffer = std::move(newBackbuffer);
674 MOZ_ASSERT(mBackbuffer);
676 return mBackbuffer->CreateDrawTarget();
679 bool Client::PresentDrawTarget(gfx::IntRegion aDirtyRegion) {
680 mSharedDataPtr->dataType = SharedDataType::PresentRequest;
682 // Simplify the region until it has <= kMaxDirtyRects
683 aDirtyRegion.SimplifyOutward(kMaxDirtyRects);
685 Span rectSpan(mSharedDataPtr->data.presentRequest.dirtyRects, kMaxDirtyRects);
687 uint8_t rectIndex = 0;
688 for (auto iter = aDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
689 rectSpan[rectIndex] = IpcSafeRect(iter.Get());
690 ++rectIndex;
693 mSharedDataPtr->data.presentRequest.lenDirtyRects = rectIndex;
695 MOZ_ALWAYS_TRUE(::SetEvent(mRequestReadyEvent));
696 MOZ_ALWAYS_TRUE(::WaitForSingleObject(mResponseReadyEvent, INFINITE) ==
697 WAIT_OBJECT_0);
699 if (mSharedDataPtr->dataType != SharedDataType::PresentResponse) {
700 return false;
703 if (mSharedDataPtr->data.presentResponse.result !=
704 ResponseResult::PresentSuccess) {
705 return false;
708 return true;
711 } // namespace remote_backbuffer
712 } // namespace widget
713 } // namespace mozilla