Bug 1272018 - Use shared memory to transfer drag image data. r?mccr8
MozReview-Commit-ID: K5r9LBQ1FO0
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -216,16 +216,17 @@
extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
const char** next, char16_t* result);
extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end,
int ns_aware, const char** colon);
class imgLoader;
using namespace mozilla::dom;
+using namespace mozilla::ipc;
using namespace mozilla::gfx;
using namespace mozilla::layers;
using namespace mozilla::widget;
using namespace mozilla;
const char kLoadAsData[] = "loadAsData";
nsIXPConnect *nsContentUtils::sXPConnect;
@@ -7522,45 +7523,137 @@ nsContentUtils::TransferableToIPCTransfe
}
}
}
}
}
}
}
-mozilla::UniquePtr<char[]>
-nsContentUtils::GetSurfaceData(mozilla::gfx::DataSourceSurface* aSurface,
- size_t* aLength, int32_t* aStride)
+namespace {
+// The default type used for calling GetSurfaceData(). Gets surface data as
+// raw buffer.
+struct GetSurfaceDataRawBuffer
+{
+ using ReturnType = mozilla::UniquePtr<char[]>;
+ using BufferType = char*;
+
+ ReturnType Allocate(size_t aSize)
+ {
+ return ReturnType(new char[aSize]);
+ }
+
+ static BufferType
+ GetBuffer(const ReturnType& aReturnValue)
+ {
+ return aReturnValue.get();
+ }
+
+ static ReturnType
+ NullValue()
+ {
+ return ReturnType();
+ }
+};
+
+// The type used for calling GetSurfaceData() that allocates and writes to
+// a shared memory buffer.
+struct GetSurfaceDataShmem
+{
+ using ReturnType = Shmem;
+ using BufferType = char*;
+
+ GetSurfaceDataShmem(IShmemAllocator* aAllocator)
+ : mAllocator(aAllocator)
+ { }
+
+ ReturnType Allocate(size_t aSize)
+ {
+ Shmem returnValue;
+ mAllocator->AllocShmem(aSize,
+ SharedMemory::TYPE_BASIC,
+ &returnValue);
+ return returnValue;
+ }
+
+ static BufferType
+ GetBuffer(ReturnType aReturnValue)
+ {
+ return aReturnValue.get<char>();
+ }
+
+ static ReturnType
+ NullValue()
+ {
+ return ReturnType();
+ }
+private:
+ IShmemAllocator* mAllocator;
+};
+
+/*
+ * Get the pixel data from the given source surface and return it as a buffer.
+ * The length and stride will be assigned from the surface.
+ */
+template <typename GetSurfaceDataContext = GetSurfaceDataRawBuffer>
+typename GetSurfaceDataContext::ReturnType
+GetSurfaceDataImpl(mozilla::gfx::DataSourceSurface* aSurface,
+ size_t* aLength, int32_t* aStride,
+ GetSurfaceDataContext aContext = GetSurfaceDataContext())
{
mozilla::gfx::DataSourceSurface::MappedSurface map;
- if (NS_WARN_IF(!aSurface->Map(mozilla::gfx::DataSourceSurface::MapType::READ, &map))) {
- return nullptr;
- }
+ if (!aSurface->Map(mozilla::gfx::DataSourceSurface::MapType::READ, &map)) {
+ return GetSurfaceDataContext::NullValue();
+ }
+
mozilla::gfx::IntSize size = aSurface->GetSize();
mozilla::CheckedInt32 requiredBytes =
mozilla::CheckedInt32(map.mStride) * mozilla::CheckedInt32(size.height);
size_t maxBufLen = requiredBytes.isValid() ? requiredBytes.value() : 0;
mozilla::gfx::SurfaceFormat format = aSurface->GetFormat();
// Surface data handling is totally nuts. This is the magic one needs to
// know to access the data.
size_t bufLen = maxBufLen - map.mStride + (size.width * BytesPerPixel(format));
// nsDependentCString wants null-terminated string.
- mozilla::UniquePtr<char[]> surfaceData(new char[maxBufLen + 1]);
- memcpy(surfaceData.get(), reinterpret_cast<char*>(map.mData), bufLen);
- memset(surfaceData.get() + bufLen, 0, maxBufLen - bufLen + 1);
+ typename GetSurfaceDataContext::ReturnType surfaceData = aContext.Allocate(maxBufLen + 1);
+ if (GetSurfaceDataContext::GetBuffer(surfaceData)) {
+ memcpy(GetSurfaceDataContext::GetBuffer(surfaceData),
+ reinterpret_cast<char*>(map.mData),
+ bufLen);
+ memset(GetSurfaceDataContext::GetBuffer(surfaceData) + bufLen,
+ 0,
+ maxBufLen - bufLen + 1);
+ }
*aLength = maxBufLen;
*aStride = map.mStride;
aSurface->Unmap();
return surfaceData;
}
+} // Anonymous namespace.
+
+mozilla::UniquePtr<char[]>
+nsContentUtils::GetSurfaceData(mozilla::gfx::DataSourceSurface* aSurface,
+ size_t* aLength, int32_t* aStride)
+{
+ return GetSurfaceDataImpl(aSurface, aLength, aStride);
+}
+
+void
+nsContentUtils::GetSurfaceData(mozilla::gfx::DataSourceSurface* aSurface,
+ size_t* aLength, int32_t* aStride,
+ IShmemAllocator* aAllocator,
+ Shmem *aOutShmem)
+{
+ *aOutShmem = GetSurfaceDataImpl(aSurface, aLength, aStride,
+ GetSurfaceDataShmem(aAllocator));
+}
mozilla::Modifiers
nsContentUtils::GetWidgetModifiers(int32_t aModifiers)
{
Modifiers result = 0;
if (aModifiers & nsIDOMWindowUtils::MODIFIER_SHIFT) {
result |= mozilla::MODIFIER_SHIFT;
}
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -124,16 +124,21 @@ class IPCDataTransfer;
class IPCDataTransferItem;
class NodeInfo;
class nsIContentChild;
class nsIContentParent;
class Selection;
class TabParent;
} // namespace dom
+namespace ipc {
+class Shmem;
+class IShmemAllocator;
+}
+
namespace gfx {
class DataSourceSurface;
} // namespace gfx
namespace layers {
class LayerManager;
} // namespace layers
@@ -2430,16 +2435,25 @@ public:
/*
* Get the pixel data from the given source surface and return it as a buffer.
* The length and stride will be assigned from the surface.
*/
static mozilla::UniquePtr<char[]> GetSurfaceData(mozilla::gfx::DataSourceSurface* aSurface,
size_t* aLength, int32_t* aStride);
+ /*
+ * Get the pixel data from the given source surface and fill it in Shmem.
+ * The length and stride will be assigned from the surface.
+ */
+ static void GetSurfaceData(mozilla::gfx::DataSourceSurface* aSurface,
+ size_t* aLength, int32_t* aStride,
+ mozilla::ipc::IShmemAllocator* aAlloc,
+ mozilla::ipc::Shmem *aOutShmem);
+
// Helpers shared by the implementations of nsContentUtils methods and
// nsIDOMWindowUtils methods.
static mozilla::Modifiers GetWidgetModifiers(int32_t aModifiers);
static nsIWidget* GetWidget(nsIPresShell* aPresShell, nsPoint* aOffset);
static int16_t GetButtonsFlagForButton(int32_t aButton);
static mozilla::LayoutDeviceIntPoint ToWidgetPoint(const mozilla::CSSPoint& aPoint,
const nsPoint& aOffset,
nsPresContext* aPresContext);
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -543,17 +543,17 @@ parent:
*/
async SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY, int32_t aCx, int32_t aCy);
prio(high) sync DispatchWheelEvent(WidgetWheelEvent event);
prio(high) sync DispatchMouseEvent(WidgetMouseEvent event);
prio(high) sync DispatchKeyboardEvent(WidgetKeyboardEvent event);
async InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action,
- nsCString visualData, uint32_t width, uint32_t height,
+ Shmem visualData, uint32_t width, uint32_t height,
uint32_t stride, uint8_t format,
int32_t dragAreaX, int32_t dragAreaY);
async AudioChannelActivityNotification(uint32_t aAudioChannel,
bool aActive);
child:
/**
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -223,17 +223,18 @@ class TabChild final : public TabChildBa
public nsIEmbeddingSiteWindow,
public nsIWebBrowserChromeFocus,
public nsIInterfaceRequestor,
public nsIWindowProvider,
public nsSupportsWeakReference,
public nsITabChild,
public nsIObserver,
public TabContext,
- public nsITooltipListener
+ public nsITooltipListener,
+ public mozilla::ipc::IShmemAllocator
{
typedef mozilla::dom::ClonedMessageData ClonedMessageData;
typedef mozilla::layout::RenderFrameChild RenderFrameChild;
typedef mozilla::layers::APZEventState APZEventState;
typedef mozilla::layers::SetAllowedTouchBehaviorCallback SetAllowedTouchBehaviorCallback;
public:
/**
@@ -280,16 +281,18 @@ public:
NS_DECL_NSIEMBEDDINGSITEWINDOW
NS_DECL_NSIWEBBROWSERCHROMEFOCUS
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSIWINDOWPROVIDER
NS_DECL_NSITABCHILD
NS_DECL_NSIOBSERVER
NS_DECL_NSITOOLTIPLISTENER
+ FORWARD_SHMEM_ALLOCATOR_TO(PBrowserChild)
+
/**
* MessageManagerCallback methods that we override.
*/
virtual bool DoSendBlockingMessage(JSContext* aCx,
const nsAString& aMessage,
StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
nsIPrincipal* aPrincipal,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -3222,17 +3222,17 @@ TabParent::RecvAsyncAuthPrompt(const nsC
level, holder, getter_AddRefs(dummy));
return rv == NS_OK;
}
bool
TabParent::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
const uint32_t& aAction,
- const nsCString& aVisualDnDData,
+ Shmem&& aVisualDnDData,
const uint32_t& aWidth, const uint32_t& aHeight,
const uint32_t& aStride, const uint8_t& aFormat,
const int32_t& aDragAreaX, const int32_t& aDragAreaY)
{
mInitialDataTransferItems.Clear();
nsIPresShell* shell = mFrameElement->OwnerDoc()->GetShell();
if (!shell) {
if (Manager()->IsContentParent()) {
@@ -3249,31 +3249,33 @@ TabParent::RecvInvokeDragSession(nsTArra
if (Manager()->IsContentParent()) {
nsCOMPtr<nsIDragService> dragService =
do_GetService("@mozilla.org/widget/dragservice;1");
if (dragService) {
dragService->MaybeAddChildProcess(Manager()->AsContentParent());
}
}
- if (aVisualDnDData.IsEmpty() ||
- (aVisualDnDData.Length() < aHeight * aStride)) {
+ if (!aVisualDnDData.IsReadable() ||
+ aVisualDnDData.Size<char>() < aHeight * aStride) {
mDnDVisualization = nullptr;
} else {
mDnDVisualization =
gfx::CreateDataSourceSurfaceFromData(gfx::IntSize(aWidth, aHeight),
static_cast<gfx::SurfaceFormat>(aFormat),
- reinterpret_cast<const uint8_t*>(aVisualDnDData.BeginReading()),
+ aVisualDnDData.get<uint8_t>(),
aStride);
}
mDragAreaX = aDragAreaX;
mDragAreaY = aDragAreaY;
esm->BeginTrackingRemoteDragGesture(mFrameElement);
+ Unused << DeallocShmem(aVisualDnDData);
+
return true;
}
void
TabParent::AddInitialDnDDataTo(DataTransfer* aDataTransfer)
{
for (uint32_t i = 0; i < mInitialDataTransferItems.Length(); ++i) {
nsTArray<IPCDataTransferItem>& itemArray = mInitialDataTransferItems[i];
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -589,17 +589,17 @@ public:
bool LayerTreeUpdate(bool aActive);
void SwapLayerTreeObservers(TabParent* aOther);
virtual bool
RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
const uint32_t& aAction,
- const nsCString& aVisualDnDData,
+ Shmem&& aVisualDnDData,
const uint32_t& aWidth, const uint32_t& aHeight,
const uint32_t& aStride, const uint8_t& aFormat,
const int32_t& aDragAreaX, const int32_t& aDragAreaY) override;
void AddInitialDnDDataTo(DataTransfer* aDataTransfer);
void TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface,
int32_t& aDragAreaX, int32_t& aDragAreaY);
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -270,16 +270,39 @@ private:
LinkedList<IToplevelProtocol> mOpenActors; // All protocol actors opened by this.
IToplevelProtocol* mOpener;
ProtocolId mProtocolId;
Transport* mTrans;
};
+class IShmemAllocator
+{
+public:
+ virtual bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) = 0;
+ virtual bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) = 0;
+ virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) = 0;
+};
+
+#define FORWARD_SHMEM_ALLOCATOR_TO(aImplClass) \
+ virtual bool AllocShmem(size_t aSize, \
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType, \
+ mozilla::ipc::Shmem* aShmem) override \
+ { return aImplClass::AllocShmem(aSize, aShmType, aShmem); } \
+ virtual bool AllocUnsafeShmem(size_t aSize, \
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType, \
+ mozilla::ipc::Shmem* aShmem) override \
+ { return aImplClass::AllocUnsafeShmem(aSize, aShmType, aShmem); } \
+ virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override \
+ { return aImplClass::DeallocShmem(aShmem); }
inline bool
LoggingEnabled()
{
#if defined(DEBUG)
return !!PR_GetEnv("MOZ_IPC_MESSAGE_LOG");
#else
return false;
--- a/widget/nsDragServiceProxy.cpp
+++ b/widget/nsDragServiceProxy.cpp
@@ -8,16 +8,19 @@
#include "nsIDocument.h"
#include "nsISupportsPrimitives.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/unused.h"
#include "nsContentUtils.h"
+using mozilla::ipc::Shmem;
+using mozilla::dom::TabChild;
+
NS_IMPL_ISUPPORTS_INHERITED0(nsDragServiceProxy, nsBaseDragService)
nsDragServiceProxy::nsDragServiceProxy()
{
}
nsDragServiceProxy::~nsDragServiceProxy()
{
@@ -25,18 +28,17 @@ nsDragServiceProxy::~nsDragServiceProxy(
nsresult
nsDragServiceProxy::InvokeDragSessionImpl(nsISupportsArray* aArrayTransferables,
nsIScriptableRegion* aRegion,
uint32_t aActionType)
{
nsCOMPtr<nsIDocument> doc = do_QueryInterface(mSourceDocument);
NS_ENSURE_STATE(doc->GetDocShell());
- mozilla::dom::TabChild* child =
- mozilla::dom::TabChild::GetFrom(doc->GetDocShell());
+ TabChild* child = TabChild::GetFrom(doc->GetDocShell());
NS_ENSURE_STATE(child);
nsTArray<mozilla::dom::IPCDataTransfer> dataTransfers;
nsContentUtils::TransferablesToIPCTransferables(aArrayTransferables,
dataTransfers,
false,
child->Manager(),
nullptr);
@@ -49,29 +51,34 @@ nsDragServiceProxy::InvokeDragSessionImp
if (surface) {
RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
surface->GetDataSurface();
mozilla::gfx::IntSize size = dataSurface->GetSize();
if (dataSurface) {
size_t length;
int32_t stride;
- mozilla::UniquePtr<char[]> surfaceData =
- nsContentUtils::GetSurfaceData(dataSurface, &length, &stride);
- nsDependentCString dragImage(surfaceData.get(), length);
-
+ Shmem surfaceData;
+ nsContentUtils::GetSurfaceData(dataSurface, &length, &stride, child,
+ &surfaceData);
+ // Save the surface data to shared memory.
+ if (!surfaceData.IsReadable() || !surfaceData.get<char>()) {
+ NS_WARNING("Failed to create shared memory for drag session.");
+ return NS_ERROR_FAILURE;
+ }
mozilla::Unused <<
- child->SendInvokeDragSession(dataTransfers, aActionType, dragImage,
+ child->SendInvokeDragSession(dataTransfers, aActionType, surfaceData,
size.width, size.height, stride,
static_cast<uint8_t>(dataSurface->GetFormat()),
dragRect.x, dragRect.y);
StartDragSession();
return NS_OK;
}
}
}
+ Shmem dragImage;
mozilla::Unused << child->SendInvokeDragSession(dataTransfers, aActionType,
- nsCString(),
+ dragImage,
0, 0, 0, 0, 0, 0);
StartDragSession();
return NS_OK;
}