зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 71e63781b38c (bug 1604412) for failures on /browser_startup_syncIPC.js -PCompositorWidget. CLOSED TREE
This commit is contained in:
Родитель
ff0444bf80
Коммит
9c0442f988
|
@ -1111,21 +1111,18 @@ description = legacy sync IPC - please add detailed description
|
|||
description = legacy sync IPC - please add detailed description
|
||||
[PCompositorBridge::CheckContentOnlyTDR]
|
||||
description = legacy sync IPC - please add detailed description
|
||||
[PCompositorWidget::Initialize]
|
||||
description = Fallable initialization for the widget. Must be called right after construction before any other messages are sent
|
||||
platform = win
|
||||
[PCompositorWidget::EnterPresentLock]
|
||||
description = Obtain exclusive access to the widget surface. Used to ensure events don't change the surface while it's being used as a render target
|
||||
description = legacy sync IPC - please add detailed description
|
||||
platform = win
|
||||
[PCompositorWidget::LeavePresentLock]
|
||||
description = Release the exclusive lock that EnterPresentLock obtained
|
||||
description = legacy sync IPC - please add detailed description
|
||||
platform = win
|
||||
[PCompositorBridge::SupportsAsyncDXGISurface]
|
||||
description = legacy sync IPC - please add detailed description
|
||||
[PCompositorBridge::PreferredDXGIAdapter]
|
||||
description = legacy sync IPC - please add detailed description
|
||||
[PCompositorWidget::ClearTransparentWindow]
|
||||
description = Synchronously clear the widget surface. Does not rely on (Enter|Leave)PresentLock. When call returns, window surface has been fully updated with cleared pixel values.
|
||||
description = legacy sync IPC - please add detailed description
|
||||
platform = win
|
||||
[PImageBridge::WillClose]
|
||||
description = legacy sync IPC - please add detailed description
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include "nsBaseWidget.h"
|
||||
#include "VsyncDispatcher.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "RemoteBackbuffer.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace widget {
|
||||
|
@ -25,8 +24,7 @@ CompositorWidgetChild::CompositorWidgetChild(
|
|||
mWnd(reinterpret_cast<HWND>(
|
||||
aInitData.get_WinCompositorWidgetInitData().hWnd())),
|
||||
mTransparencyMode(
|
||||
aInitData.get_WinCompositorWidgetInitData().transparencyMode()),
|
||||
mRemoteBackbufferProvider() {
|
||||
aInitData.get_WinCompositorWidgetInitData().transparencyMode()) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(!gfxPlatform::IsHeadless());
|
||||
MOZ_ASSERT(mWnd && ::IsWindow(mWnd));
|
||||
|
@ -34,21 +32,7 @@ CompositorWidgetChild::CompositorWidgetChild(
|
|||
|
||||
CompositorWidgetChild::~CompositorWidgetChild() {}
|
||||
|
||||
bool CompositorWidgetChild::Initialize() {
|
||||
mRemoteBackbufferProvider = std::make_unique<remote_backbuffer::Provider>();
|
||||
if (!mRemoteBackbufferProvider->Initialize(mWnd, OtherPid())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto maybeRemoteHandles = mRemoteBackbufferProvider->CreateRemoteHandles();
|
||||
if (!maybeRemoteHandles) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Unused << SendInitialize(*maybeRemoteHandles);
|
||||
|
||||
return true;
|
||||
}
|
||||
bool CompositorWidgetChild::Initialize() { return true; }
|
||||
|
||||
void CompositorWidgetChild::EnterPresentLock() {
|
||||
Unused << SendEnterPresentLock();
|
||||
|
|
|
@ -15,10 +15,6 @@ class CompositorVsyncDispatcher;
|
|||
|
||||
namespace widget {
|
||||
|
||||
namespace remote_backbuffer {
|
||||
class Provider;
|
||||
}
|
||||
|
||||
class CompositorWidgetChild final : public PCompositorWidgetChild,
|
||||
public PlatformCompositorWidgetDelegate {
|
||||
public:
|
||||
|
@ -50,8 +46,6 @@ class CompositorWidgetChild final : public PCompositorWidgetChild,
|
|||
|
||||
HWND mWnd;
|
||||
nsTransparencyMode mTransparencyMode;
|
||||
|
||||
std::unique_ptr<remote_backbuffer::Provider> mRemoteBackbufferProvider;
|
||||
};
|
||||
|
||||
} // namespace widget
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include "VsyncDispatcher.h"
|
||||
#include "WinCompositorWindowThread.h"
|
||||
#include "VRShMem.h"
|
||||
#include "RemoteBackbuffer.h"
|
||||
|
||||
#include <ddraw.h>
|
||||
|
||||
|
@ -35,26 +34,24 @@ CompositorWidgetParent::CompositorWidgetParent(
|
|||
aOptions),
|
||||
mWnd(reinterpret_cast<HWND>(
|
||||
aInitData.get_WinCompositorWidgetInitData().hWnd())),
|
||||
mTransparentSurfaceLock("mTransparentSurfaceLock"),
|
||||
mTransparencyMode(
|
||||
aInitData.get_WinCompositorWidgetInitData().transparencyMode()),
|
||||
mLockedBackBufferData(nullptr),
|
||||
mRemoteBackbufferClient() {
|
||||
mMemoryDC(nullptr),
|
||||
mCompositeDC(nullptr),
|
||||
mLockedBackBufferData(nullptr) {
|
||||
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_GPU);
|
||||
MOZ_ASSERT(mWnd && ::IsWindow(mWnd));
|
||||
|
||||
// mNotDeferEndRemoteDrawing is set on the main thread during init,
|
||||
// but is only accessed after on the compositor thread.
|
||||
mNotDeferEndRemoteDrawing =
|
||||
StaticPrefs::layers_offmainthreadcomposition_frame_rate() == 0 ||
|
||||
gfxPlatform::IsInLayoutAsapMode() || gfxPlatform::ForceSoftwareVsync();
|
||||
}
|
||||
|
||||
CompositorWidgetParent::~CompositorWidgetParent() {}
|
||||
|
||||
bool CompositorWidgetParent::Initialize(
|
||||
const RemoteBackbufferHandles& aRemoteHandles) {
|
||||
mRemoteBackbufferClient = std::make_unique<remote_backbuffer::Client>();
|
||||
if (!mRemoteBackbufferClient->Initialize(aRemoteHandles)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompositorWidgetParent::PreRender(WidgetRenderingContext* aContext) {
|
||||
// This can block waiting for WM_SETTEXT to finish
|
||||
// Using PreRender is unnecessarily pessimistic because
|
||||
|
@ -77,18 +74,88 @@ LayoutDeviceIntSize CompositorWidgetParent::GetClientSize() {
|
|||
}
|
||||
|
||||
already_AddRefed<gfx::DrawTarget> CompositorWidgetParent::StartRemoteDrawing() {
|
||||
MOZ_ASSERT(mRemoteBackbufferClient);
|
||||
MutexAutoLock lock(mTransparentSurfaceLock);
|
||||
|
||||
return mRemoteBackbufferClient->BorrowDrawTarget();
|
||||
MOZ_ASSERT(!mCompositeDC);
|
||||
|
||||
RefPtr<gfxASurface> surf;
|
||||
if (mTransparencyMode == eTransparencyTransparent) {
|
||||
surf = EnsureTransparentSurface();
|
||||
}
|
||||
|
||||
// Must call this after EnsureTransparentSurface(), since it could update
|
||||
// the DC.
|
||||
HDC dc = GetWindowSurface();
|
||||
if (!surf) {
|
||||
if (!dc) {
|
||||
return nullptr;
|
||||
}
|
||||
uint32_t flags = (mTransparencyMode == eTransparencyOpaque)
|
||||
? 0
|
||||
: gfxWindowsSurface::FLAG_IS_TRANSPARENT;
|
||||
surf = new gfxWindowsSurface(dc, flags);
|
||||
}
|
||||
|
||||
IntSize size = surf->GetSize();
|
||||
if (size.width <= 0 || size.height <= 0) {
|
||||
if (dc) {
|
||||
FreeWindowSurface(dc);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<DrawTarget> dt =
|
||||
mozilla::gfx::Factory::CreateDrawTargetForCairoSurface(
|
||||
surf->CairoSurface(), size);
|
||||
if (dt) {
|
||||
mCompositeDC = dc;
|
||||
} else {
|
||||
FreeWindowSurface(dc);
|
||||
}
|
||||
|
||||
return dt.forget();
|
||||
}
|
||||
|
||||
void CompositorWidgetParent::EndRemoteDrawing() {
|
||||
MOZ_ASSERT(!mLockedBackBufferData);
|
||||
|
||||
Unused << mRemoteBackbufferClient->PresentDrawTarget();
|
||||
if (mTransparencyMode == eTransparencyTransparent) {
|
||||
MOZ_ASSERT(mTransparentSurface);
|
||||
RedrawTransparentWindow();
|
||||
}
|
||||
if (mCompositeDC) {
|
||||
FreeWindowSurface(mCompositeDC);
|
||||
}
|
||||
mCompositeDC = nullptr;
|
||||
}
|
||||
|
||||
bool CompositorWidgetParent::NeedsToDeferEndRemoteDrawing() { return false; }
|
||||
bool CompositorWidgetParent::NeedsToDeferEndRemoteDrawing() {
|
||||
if (mNotDeferEndRemoteDrawing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IDirectDraw7* ddraw = DeviceManagerDx::Get()->GetDirectDraw();
|
||||
if (!ddraw) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD scanLine = 0;
|
||||
int height = ::GetSystemMetrics(SM_CYSCREEN);
|
||||
HRESULT ret = ddraw->GetScanLine(&scanLine);
|
||||
if (ret == DDERR_VERTICALBLANKINPROGRESS) {
|
||||
scanLine = 0;
|
||||
} else if (ret != DD_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if there is a risk of tearing with GDI.
|
||||
if (static_cast<int>(scanLine) > height / 2) {
|
||||
// No need to defer.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::DrawTarget>
|
||||
CompositorWidgetParent::GetBackBufferDrawTarget(gfx::DrawTarget* aScreenTarget,
|
||||
|
@ -137,6 +204,31 @@ bool CompositorWidgetParent::InitCompositor(layers::Compositor* aCompositor) {
|
|||
return true;
|
||||
}
|
||||
|
||||
RefPtr<gfxASurface> CompositorWidgetParent::EnsureTransparentSurface() {
|
||||
mTransparentSurfaceLock.AssertCurrentThreadOwns();
|
||||
MOZ_ASSERT(mTransparencyMode == eTransparencyTransparent);
|
||||
|
||||
IntSize size = GetClientSize().ToUnknownSize();
|
||||
if (!mTransparentSurface || mTransparentSurface->GetSize() != size) {
|
||||
mTransparentSurface = nullptr;
|
||||
mMemoryDC = nullptr;
|
||||
CreateTransparentSurface(size);
|
||||
}
|
||||
|
||||
RefPtr<gfxASurface> surface = mTransparentSurface;
|
||||
return surface.forget();
|
||||
}
|
||||
|
||||
void CompositorWidgetParent::CreateTransparentSurface(
|
||||
const gfx::IntSize& aSize) {
|
||||
mTransparentSurfaceLock.AssertCurrentThreadOwns();
|
||||
MOZ_ASSERT(!mTransparentSurface && !mMemoryDC);
|
||||
RefPtr<gfxWindowsSurface> surface =
|
||||
new gfxWindowsSurface(aSize, SurfaceFormat::A8R8G8B8_UINT32);
|
||||
mTransparentSurface = surface;
|
||||
mMemoryDC = surface->GetDC();
|
||||
}
|
||||
|
||||
bool CompositorWidgetParent::HasGlass() const {
|
||||
MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread() ||
|
||||
wr::RenderThread::IsInRenderThread());
|
||||
|
@ -146,14 +238,36 @@ bool CompositorWidgetParent::HasGlass() const {
|
|||
transparencyMode == eTransparencyBorderlessGlass;
|
||||
}
|
||||
|
||||
bool CompositorWidgetParent::IsHidden() const { return ::IsIconic(mWnd); }
|
||||
bool CompositorWidgetParent::RedrawTransparentWindow() {
|
||||
MOZ_ASSERT(mTransparencyMode == eTransparencyTransparent);
|
||||
|
||||
mozilla::ipc::IPCResult CompositorWidgetParent::RecvInitialize(
|
||||
const RemoteBackbufferHandles& aRemoteHandles) {
|
||||
Unused << Initialize(aRemoteHandles);
|
||||
return IPC_OK();
|
||||
LayoutDeviceIntSize size = GetClientSize();
|
||||
|
||||
::GdiFlush();
|
||||
|
||||
BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
|
||||
SIZE winSize = {size.width, size.height};
|
||||
POINT srcPos = {0, 0};
|
||||
HWND hWnd = WinUtils::GetTopLevelHWND(mWnd, true);
|
||||
RECT winRect;
|
||||
::GetWindowRect(hWnd, &winRect);
|
||||
|
||||
// perform the alpha blend
|
||||
return !!::UpdateLayeredWindow(hWnd, nullptr, (POINT*)&winRect, &winSize,
|
||||
mMemoryDC, &srcPos, 0, &bf, ULW_ALPHA);
|
||||
}
|
||||
|
||||
HDC CompositorWidgetParent::GetWindowSurface() {
|
||||
return eTransparencyTransparent == mTransparencyMode ? mMemoryDC
|
||||
: ::GetDC(mWnd);
|
||||
}
|
||||
|
||||
void CompositorWidgetParent::FreeWindowSurface(HDC dc) {
|
||||
if (eTransparencyTransparent != mTransparencyMode) ::ReleaseDC(mWnd, dc);
|
||||
}
|
||||
|
||||
bool CompositorWidgetParent::IsHidden() const { return ::IsIconic(mWnd); }
|
||||
|
||||
mozilla::ipc::IPCResult CompositorWidgetParent::RecvEnterPresentLock() {
|
||||
mPresentLock.Enter();
|
||||
return IPC_OK();
|
||||
|
@ -166,28 +280,39 @@ mozilla::ipc::IPCResult CompositorWidgetParent::RecvLeavePresentLock() {
|
|||
|
||||
mozilla::ipc::IPCResult CompositorWidgetParent::RecvUpdateTransparency(
|
||||
const nsTransparencyMode& aMode) {
|
||||
mTransparencyMode = aMode;
|
||||
MutexAutoLock lock(mTransparentSurfaceLock);
|
||||
if (mTransparencyMode == aMode) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mTransparencyMode = aMode;
|
||||
mTransparentSurface = nullptr;
|
||||
mMemoryDC = nullptr;
|
||||
|
||||
if (mTransparencyMode == eTransparencyTransparent) {
|
||||
EnsureTransparentSurface();
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult CompositorWidgetParent::RecvClearTransparentWindow() {
|
||||
gfx::CriticalSectionAutoEnter lock(&mPresentLock);
|
||||
|
||||
RefPtr<DrawTarget> drawTarget = mRemoteBackbufferClient->BorrowDrawTarget();
|
||||
if (!drawTarget) {
|
||||
MutexAutoLock lock(mTransparentSurfaceLock);
|
||||
if (!mTransparentSurface) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
IntSize size = drawTarget->GetSize();
|
||||
if (size.IsEmpty()) {
|
||||
return IPC_OK();
|
||||
EnsureTransparentSurface();
|
||||
|
||||
IntSize size = mTransparentSurface->GetSize();
|
||||
if (!size.IsEmpty()) {
|
||||
RefPtr<DrawTarget> drawTarget =
|
||||
gfxPlatform::CreateDrawTargetForSurface(mTransparentSurface, size);
|
||||
if (!drawTarget) {
|
||||
return IPC_OK();
|
||||
}
|
||||
drawTarget->ClearRect(Rect(0, 0, size.width, size.height));
|
||||
RedrawTransparentWindow();
|
||||
}
|
||||
|
||||
drawTarget->ClearRect(Rect(0, 0, size.width, size.height));
|
||||
|
||||
Unused << mRemoteBackbufferClient->PresentDrawTarget();
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
|
|
@ -13,10 +13,6 @@
|
|||
namespace mozilla {
|
||||
namespace widget {
|
||||
|
||||
namespace remote_backbuffer {
|
||||
class Client;
|
||||
}
|
||||
|
||||
class CompositorWidgetParent final : public PCompositorWidgetParent,
|
||||
public WinCompositorWidget {
|
||||
public:
|
||||
|
@ -24,8 +20,6 @@ class CompositorWidgetParent final : public PCompositorWidgetParent,
|
|||
const layers::CompositorOptions& aOptions);
|
||||
~CompositorWidgetParent() override;
|
||||
|
||||
bool Initialize(const RemoteBackbufferHandles& aRemoteHandles);
|
||||
|
||||
bool PreRender(WidgetRenderingContext*) override;
|
||||
void PostRender(WidgetRenderingContext*) override;
|
||||
already_AddRefed<gfx::DrawTarget> StartRemoteDrawing() override;
|
||||
|
@ -41,8 +35,6 @@ class CompositorWidgetParent final : public PCompositorWidgetParent,
|
|||
|
||||
bool HasGlass() const override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvInitialize(
|
||||
const RemoteBackbufferHandles& aRemoteHandles) override;
|
||||
mozilla::ipc::IPCResult RecvEnterPresentLock() override;
|
||||
mozilla::ipc::IPCResult RecvLeavePresentLock() override;
|
||||
mozilla::ipc::IPCResult RecvUpdateTransparency(
|
||||
|
@ -60,6 +52,16 @@ class CompositorWidgetParent final : public PCompositorWidgetParent,
|
|||
void SetRootLayerTreeID(const layers::LayersId& aRootLayerTreeId) override;
|
||||
|
||||
private:
|
||||
bool RedrawTransparentWindow();
|
||||
|
||||
// Ensure that a transparent surface exists, then return it.
|
||||
RefPtr<gfxASurface> EnsureTransparentSurface();
|
||||
|
||||
HDC GetWindowSurface();
|
||||
void FreeWindowSurface(HDC dc);
|
||||
|
||||
void CreateTransparentSurface(const gfx::IntSize& aSize);
|
||||
|
||||
RefPtr<VsyncObserver> mVsyncObserver;
|
||||
Maybe<layers::LayersId> mRootLayerTreeID;
|
||||
|
||||
|
@ -68,13 +70,17 @@ class CompositorWidgetParent final : public PCompositorWidgetParent,
|
|||
gfx::CriticalSection mPresentLock;
|
||||
|
||||
// Transparency handling.
|
||||
mozilla::Mutex mTransparentSurfaceLock;
|
||||
mozilla::Atomic<nsTransparencyMode, MemoryOrdering::Relaxed>
|
||||
mTransparencyMode;
|
||||
RefPtr<gfxASurface> mTransparentSurface;
|
||||
HDC mMemoryDC;
|
||||
HDC mCompositeDC;
|
||||
|
||||
// Locked back buffer of BasicCompositor
|
||||
uint8_t* mLockedBackBufferData;
|
||||
|
||||
std::unique_ptr<remote_backbuffer::Client> mRemoteBackbufferClient;
|
||||
bool mNotDeferEndRemoteDrawing;
|
||||
};
|
||||
|
||||
} // namespace widget
|
||||
|
|
|
@ -13,19 +13,11 @@ using nsTransparencyMode from "mozilla/widget/WidgetMessageUtils.h";
|
|||
namespace mozilla {
|
||||
namespace widget {
|
||||
|
||||
struct RemoteBackbufferHandles {
|
||||
WindowsHandle fileMapping;
|
||||
WindowsHandle requestReadyEvent;
|
||||
WindowsHandle responseReadyEvent;
|
||||
};
|
||||
|
||||
sync protocol PCompositorWidget
|
||||
{
|
||||
manager PCompositorBridge;
|
||||
|
||||
parent:
|
||||
sync Initialize(RemoteBackbufferHandles aRemoteHandles);
|
||||
|
||||
sync EnterPresentLock();
|
||||
sync LeavePresentLock();
|
||||
async UpdateTransparency(nsTransparencyMode aMode);
|
||||
|
|
|
@ -1,567 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "RemoteBackbuffer.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace widget {
|
||||
namespace remote_backbuffer {
|
||||
|
||||
enum class ResponseResult {
|
||||
Unknown,
|
||||
Error,
|
||||
BorrowSuccess,
|
||||
BorrowSameBuffer,
|
||||
PresentSuccess
|
||||
};
|
||||
|
||||
enum class SharedDataType {
|
||||
BorrowRequest,
|
||||
BorrowResponse,
|
||||
PresentRequest,
|
||||
PresentResponse
|
||||
};
|
||||
|
||||
struct BorrowResponseData {
|
||||
ResponseResult result;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
HANDLE fileMapping;
|
||||
};
|
||||
|
||||
struct PresentResponseData {
|
||||
ResponseResult result;
|
||||
};
|
||||
|
||||
struct SharedData {
|
||||
SharedDataType dataType;
|
||||
union {
|
||||
BorrowResponseData borrowResponse;
|
||||
PresentResponseData presentResponse;
|
||||
} data;
|
||||
};
|
||||
|
||||
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);
|
||||
MOZ_ASSERT(aHeight);
|
||||
|
||||
mWidth = aWidth;
|
||||
mHeight = aHeight;
|
||||
|
||||
DWORD bufferSize = static_cast<DWORD>(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<unsigned char*>(mappedFilePtr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InitializeRemote(int32_t aWidth, int32_t aHeight, HANDLE aFileMapping) {
|
||||
MOZ_ASSERT(aWidth);
|
||||
MOZ_ASSERT(aHeight);
|
||||
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<unsigned char*>(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<gfx::DrawTarget> CreateDrawTarget() {
|
||||
return gfx::Factory::CreateDrawTargetForData(
|
||||
gfx::BackendType::CAIRO, mPixelData, IntSize(mWidth, mHeight),
|
||||
GetStride(), gfx::SurfaceFormat::B8G8R8A8);
|
||||
}
|
||||
|
||||
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:
|
||||
int32_t GetStride() {
|
||||
constexpr int32_t kBytesPerPixel = 4;
|
||||
|
||||
// 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) {
|
||||
bool isLayered =
|
||||
::GetWindowLongPtrW(aWindowHandle, GWL_EXSTYLE) & WS_EX_LAYERED;
|
||||
|
||||
if (isLayered) {
|
||||
BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
|
||||
SIZE winSize = {mSharedImage.GetWidth(), mSharedImage.GetHeight()};
|
||||
POINT srcPos = {0, 0};
|
||||
return !!::UpdateLayeredWindow(
|
||||
aWindowHandle, nullptr /*paletteDC*/, nullptr /*newPos*/, &winSize,
|
||||
mDeviceContext, &srcPos, 0 /*colorKey*/, &bf, ULW_ALPHA);
|
||||
}
|
||||
|
||||
HDC windowDC = ::GetDC(aWindowHandle);
|
||||
if (!windowDC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = ::BitBlt(windowDC, 0 /*dstX*/, 0 /*dstY*/,
|
||||
mSharedImage.GetWidth(), mSharedImage.GetHeight(),
|
||||
mDeviceContext, 0 /*srcX*/, 0 /*srcY*/, SRCCOPY);
|
||||
|
||||
MOZ_ALWAYS_TRUE(::ReleaseDC(aWindowHandle, windowDC));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
HANDLE CreateRemoteFileMapping(DWORD aTargetProcessId) {
|
||||
return mSharedImage.CreateRemoteFileMapping(aTargetProcessId);
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::DrawTarget> CreateDrawTarget() {
|
||||
return mSharedImage.CreateDrawTarget();
|
||||
}
|
||||
|
||||
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) {
|
||||
MOZ_ASSERT(aWindowHandle);
|
||||
MOZ_ASSERT(aTargetProcessId);
|
||||
|
||||
mWindowHandle = aWindowHandle;
|
||||
mTargetProcessId = aTargetProcessId;
|
||||
|
||||
mFileMapping = ::CreateFileMappingW(
|
||||
INVALID_HANDLE_VALUE, nullptr /*secattr*/, PAGE_READWRITE, 0 /*sizeHigh*/,
|
||||
static_cast<DWORD>(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<SharedData*>(mappedFilePtr);
|
||||
|
||||
mStopServiceThread = false;
|
||||
|
||||
mServiceThread = std::thread([this] { this->ThreadMain(); });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Maybe<RemoteBackbufferHandles> 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<WindowsHandle>(fileMapping),
|
||||
reinterpret_cast<WindowsHandle>(requestReadyEvent),
|
||||
reinterpret_cast<WindowsHandle>(responseReadyEvent)));
|
||||
}
|
||||
|
||||
void Provider::ThreadMain() {
|
||||
while (true) {
|
||||
MOZ_ALWAYS_TRUE(::WaitForSingleObject(mRequestReadyEvent, INFINITE) ==
|
||||
WAIT_OBJECT_0);
|
||||
|
||||
if (mStopServiceThread) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (mSharedDataPtr->dataType) {
|
||||
case SharedDataType::BorrowRequest: {
|
||||
BorrowResponseData responseData = {};
|
||||
|
||||
HandleBorrowRequest(&responseData);
|
||||
|
||||
mSharedDataPtr->dataType = SharedDataType::BorrowResponse;
|
||||
mSharedDataPtr->data.borrowResponse = responseData;
|
||||
|
||||
MOZ_ALWAYS_TRUE(::SetEvent(mResponseReadyEvent));
|
||||
|
||||
break;
|
||||
}
|
||||
case SharedDataType::PresentRequest: {
|
||||
PresentResponseData responseData = {};
|
||||
|
||||
HandlePresentRequest(&responseData);
|
||||
|
||||
mSharedDataPtr->dataType = SharedDataType::PresentResponse;
|
||||
mSharedDataPtr->data.presentResponse = responseData;
|
||||
|
||||
MOZ_ALWAYS_TRUE(::SetEvent(mResponseReadyEvent));
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void Provider::HandleBorrowRequest(BorrowResponseData* aResponseData) {
|
||||
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 = !mBackbuffer || (mBackbuffer->GetWidth() != width) ||
|
||||
(mBackbuffer->GetHeight() != height);
|
||||
|
||||
if (!needNewBackbuffer) {
|
||||
aResponseData->result = ResponseResult::BorrowSameBuffer;
|
||||
return;
|
||||
}
|
||||
|
||||
mBackbuffer.reset();
|
||||
|
||||
mBackbuffer = std::make_unique<PresentableSharedImage>();
|
||||
if (!mBackbuffer->Initialize(width, height)) {
|
||||
return;
|
||||
}
|
||||
|
||||
HANDLE remoteFileMapping =
|
||||
mBackbuffer->CreateRemoteFileMapping(mTargetProcessId);
|
||||
if (!remoteFileMapping) {
|
||||
return;
|
||||
}
|
||||
|
||||
aResponseData->result = ResponseResult::BorrowSuccess;
|
||||
aResponseData->width = width;
|
||||
aResponseData->height = height;
|
||||
aResponseData->fileMapping = remoteFileMapping;
|
||||
}
|
||||
|
||||
void Provider::HandlePresentRequest(PresentResponseData* aResponseData) {
|
||||
MOZ_ASSERT(aResponseData);
|
||||
|
||||
aResponseData->result = ResponseResult::Error;
|
||||
|
||||
if (!mBackbuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mBackbuffer->PresentToWindow(mWindowHandle)) {
|
||||
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<HANDLE>(aRemoteHandles.fileMapping());
|
||||
mRequestReadyEvent =
|
||||
reinterpret_cast<HANDLE>(aRemoteHandles.requestReadyEvent());
|
||||
mResponseReadyEvent =
|
||||
reinterpret_cast<HANDLE>(aRemoteHandles.responseReadyEvent());
|
||||
|
||||
void* mappedFilePtr =
|
||||
::MapViewOfFile(mFileMapping, FILE_MAP_ALL_ACCESS, 0 /*offsetHigh*/,
|
||||
0 /*offsetLow*/, 0 /*bytesToMap*/);
|
||||
if (!mappedFilePtr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mSharedDataPtr = reinterpret_cast<SharedData*>(mappedFilePtr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::DrawTarget> Client::BorrowDrawTarget() {
|
||||
mSharedDataPtr->dataType = 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();
|
||||
|
||||
mBackbuffer = std::make_unique<SharedImage>();
|
||||
if (!mBackbuffer->InitializeRemote(responseData.width, responseData.height,
|
||||
responseData.fileMapping)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return mBackbuffer->CreateDrawTarget();
|
||||
}
|
||||
|
||||
bool Client::PresentDrawTarget() {
|
||||
mSharedDataPtr->dataType = SharedDataType::PresentRequest;
|
||||
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
|
|
@ -1,81 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 widget_windows_RemoteBackbuffer_h
|
||||
#define widget_windows_RemoteBackbuffer_h
|
||||
|
||||
#include <thread>
|
||||
#include <windows.h>
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace widget {
|
||||
namespace remote_backbuffer {
|
||||
|
||||
struct SharedData;
|
||||
struct BorrowResponseData;
|
||||
struct PresentResponseData;
|
||||
class SharedImage;
|
||||
class PresentableSharedImage;
|
||||
|
||||
class Provider {
|
||||
public:
|
||||
Provider();
|
||||
~Provider();
|
||||
|
||||
bool Initialize(HWND aWindowHandle, DWORD aTargetProcessId);
|
||||
|
||||
Maybe<RemoteBackbufferHandles> CreateRemoteHandles();
|
||||
|
||||
Provider(const Provider&) = delete;
|
||||
Provider(Provider&&) = delete;
|
||||
Provider& operator=(const Provider&) = delete;
|
||||
Provider& operator=(Provider&&) = delete;
|
||||
|
||||
private:
|
||||
void ThreadMain();
|
||||
|
||||
void HandleBorrowRequest(BorrowResponseData* aResponseData);
|
||||
void HandlePresentRequest(PresentResponseData* aResponseData);
|
||||
|
||||
HWND mWindowHandle;
|
||||
DWORD mTargetProcessId;
|
||||
HANDLE mFileMapping;
|
||||
HANDLE mRequestReadyEvent;
|
||||
HANDLE mResponseReadyEvent;
|
||||
SharedData* mSharedDataPtr;
|
||||
bool mStopServiceThread;
|
||||
std::thread mServiceThread;
|
||||
std::unique_ptr<PresentableSharedImage> mBackbuffer;
|
||||
};
|
||||
|
||||
class Client {
|
||||
public:
|
||||
Client();
|
||||
~Client();
|
||||
|
||||
bool Initialize(const RemoteBackbufferHandles& aRemoteHandles);
|
||||
|
||||
already_AddRefed<gfx::DrawTarget> BorrowDrawTarget();
|
||||
bool PresentDrawTarget();
|
||||
|
||||
Client(const Client&) = delete;
|
||||
Client(Client&&) = delete;
|
||||
Client& operator=(const Client&) = delete;
|
||||
Client& operator=(Client&&) = delete;
|
||||
|
||||
private:
|
||||
HANDLE mFileMapping;
|
||||
HANDLE mRequestReadyEvent;
|
||||
HANDLE mResponseReadyEvent;
|
||||
SharedData* mSharedDataPtr;
|
||||
std::unique_ptr<SharedImage> mBackbuffer;
|
||||
};
|
||||
|
||||
} // namespace remote_backbuffer
|
||||
} // namespace widget
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // widget_windows_RemoteBackbuffer_h
|
|
@ -68,7 +68,6 @@ UNIFIED_SOURCES += [
|
|||
'nsWindowGfx.cpp',
|
||||
'nsWinGesture.cpp',
|
||||
'ProcInfo.cpp',
|
||||
'RemoteBackbuffer.cpp',
|
||||
'ScreenHelperWin.cpp',
|
||||
'TaskbarPreview.cpp',
|
||||
'TaskbarPreviewButton.cpp',
|
||||
|
|
Загрузка…
Ссылка в новой задаче