Bug 1272018 - Use shared memory to transfer drag image data. r=nical
authorCervantes Yu <cyu@mozilla.com>
Wed, 15 Jun 2016 13:49:13 +0100
changeset 341944 d3571746fb0bed108c2e6d7ce5a78fe8374755fd
parent 341943 a509094fc1f8f470570dff6911333b7bf3591b36
child 341945 4526172c1bda6e9985078bf1462a4f5d1d53dbe6
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs1272018
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1272018 - Use shared memory to transfer drag image data. r=nical MozReview-Commit-ID: K5r9LBQ1FO0
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
ipc/glue/ProtocolUtils.h
widget/nsDragServiceProxy.cpp
--- 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;
@@ -7557,46 +7558,138 @@ nsContentUtils::TransferableToIPCTransfe
             }
           }
         }
       }
     }
   }
 }
 
-mozilla::UniquePtr<char[]>
-nsContentUtils::GetSurfaceData(
-  NotNull<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*;
+
+  explicit 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(
+  NotNull<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
@@ -126,16 +126,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
 
@@ -2447,16 +2452,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::NotNull<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
@@ -95,16 +95,22 @@ struct ShowInfo
   bool fullscreenAllowed;
   bool isPrivate;
   bool fakeShowInfo;
   bool isTransparent;
   float dpi;
   double defaultScale;
 };
 
+union OptionalShmem
+{
+  void_t;
+  Shmem;
+};
+
 prio(normal upto urgent) sync protocol PBrowser
 {
     manager PContent or PContentBridge;
 
     manages PColorPicker;
     manages PDocAccessible;
     manages PDocumentRenderer;
     manages PFilePicker;
@@ -543,17 +549,18 @@ 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,
+                            OptionalShmem 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
@@ -3247,17 +3247,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,
+                                 const OptionalShmem& 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()) {
@@ -3274,31 +3274,36 @@ 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.type() == OptionalShmem::Tvoid_t ||
+      !aVisualDnDData.get_Shmem().IsReadable() ||
+      aVisualDnDData.get_Shmem().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_Shmem().get<uint8_t>(),
                                              aStride);
   }
   mDragAreaX = aDragAreaX;
   mDragAreaY = aDragAreaY;
 
   esm->BeginTrackingRemoteDragGesture(mFrameElement);
 
+  if (aVisualDnDData.type() == OptionalShmem::TShmem) {
+    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
@@ -590,17 +590,17 @@ public:
 
   bool LayerTreeUpdate(bool aActive);
 
   void SwapLayerTreeObservers(TabParent* aOther);
 
   virtual bool
   RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
                         const uint32_t& aAction,
-                        const nsCString& aVisualDnDData,
+                        const OptionalShmem& 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,20 @@
 #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;
+using mozilla::dom::OptionalShmem;
+
 NS_IMPL_ISUPPORTS_INHERITED0(nsDragServiceProxy, nsBaseDragService)
 
 nsDragServiceProxy::nsDragServiceProxy()
 {
 }
 
 nsDragServiceProxy::~nsDragServiceProxy()
 {
@@ -25,18 +29,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);
 
@@ -48,31 +51,35 @@ nsDragServiceProxy::InvokeDragSessionImp
              &dragRect, &surface, &pc);
 
     if (surface) {
       RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
         surface->GetDataSurface();
       if (dataSurface) {
         size_t length;
         int32_t stride;
-        mozilla::UniquePtr<char[]> surfaceData =
-          nsContentUtils::GetSurfaceData(WrapNotNull(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::gfx::IntSize size = dataSurface->GetSize();
         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;
       }
     }
   }
 
   mozilla::Unused << child->SendInvokeDragSession(dataTransfers, aActionType,
-                                                  nsCString(),
+                                                  mozilla::void_t(),
                                                   0, 0, 0, 0, 0, 0);
   StartDragSession();
   return NS_OK;
 }