diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini
index 21fb21840e2..4059ddd4e74 100644
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -287,6 +287,7 @@ skip-if = os == 'win' || e10s # Bug 1056146 - zoom tests use FullZoomHelper and
[browser_bug1070778.js]
[browser_canonizeURL.js]
skip-if = e10s # Bug 1094510 - test hits the network in e10s mode only
+[browser_clipboard.js]
[browser_contentAreaClick.js]
[browser_contextSearchTabPosition.js]
skip-if = os == "mac" || e10s # bug 967013; e10s: bug 1094761 - test hits the network in e10s, causing next test to crash
diff --git a/browser/base/content/test/general/browser_clipboard.js b/browser/base/content/test/general/browser_clipboard.js
new file mode 100644
index 00000000000..c504e0b68a0
--- /dev/null
+++ b/browser/base/content/test/general/browser_clipboard.js
@@ -0,0 +1,123 @@
+// This test is used to check copy and paste in editable areas to ensure that non-text
+// types (html and images) are copied to and pasted from the clipboard properly.
+
+let testPage = "
Test Bold After Text
";
+
+add_task(function*() {
+ let tab = gBrowser.addTab();
+ let browser = gBrowser.getBrowserForTab(tab);
+
+ gBrowser.selectedTab = tab;
+
+ yield promiseTabLoadEvent(tab, "data:text/html," + escape(testPage));
+ yield SimpleTest.promiseFocus(browser.contentWindowAsCPOW);
+
+ let results = yield ContentTask.spawn(browser, {}, function* () {
+ var doc = content.document;
+ var main = doc.getElementById("main");
+ main.focus();
+
+ const utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIDOMWindowUtils);
+
+ const modifier = (content.navigator.platform.indexOf("Mac") >= 0) ?
+ Components.interfaces.nsIDOMWindowUtils.MODIFIER_META :
+ Components.interfaces.nsIDOMWindowUtils.MODIFIER_CONTROL;
+
+ function sendKey(key)
+ {
+ if (utils.sendKeyEvent("keydown", key, 0, modifier)) {
+ utils.sendKeyEvent("keypress", key, key.charCodeAt(0), modifier);
+ }
+ utils.sendKeyEvent("keyup", key, 0, modifier);
+ }
+
+ let results = [];
+ function is(l, r, v) {
+ results.push(((l === r) ? "PASSED" : "FAILED") + " got: " + l + " expected: " + r + " - " + v);
+ }
+
+ // Select an area of the text.
+ let selection = doc.getSelection();
+ selection.modify("move", "left", "line");
+ selection.modify("move", "right", "character");
+ selection.modify("move", "right", "character");
+ selection.modify("move", "right", "character");
+ selection.modify("extend", "right", "word");
+ selection.modify("extend", "right", "word");
+
+ yield new content.Promise((resolve, reject) => {
+ addEventListener("copy", function copyEvent(event) {
+ removeEventListener("copy", copyEvent, true);
+ // The data is empty as the selection is copied during the event default phase.
+ is(event.clipboardData.mozItemCount, 0, "Zero items on clipboard");
+ resolve();
+ }, true)
+
+ sendKey("c");
+ });
+
+ selection.modify("move", "right", "line");
+
+ yield new content.Promise((resolve, reject) => {
+ addEventListener("paste", function copyEvent(event) {
+ removeEventListener("paste", copyEvent, true);
+ let clipboardData = event.clipboardData;
+ is(clipboardData.mozItemCount, 1, "One item on clipboard");
+ is(clipboardData.types.length, 2, "Two types on clipboard");
+ is(clipboardData.types[0], "text/html", "text/html on clipboard");
+ is(clipboardData.types[1], "text/plain", "text/plain on clipboard");
+ is(clipboardData.getData("text/html"), "t Bold", "text/html value");
+ is(clipboardData.getData("text/plain"), "t Bold", "text/plain value");
+ resolve();
+ }, true)
+ sendKey("v");
+ });
+
+ is(main.innerHTML, "Test Bold After Textt Bold", "Copy and paste html");
+
+ selection.modify("extend", "left", "word");
+ selection.modify("extend", "left", "word");
+ selection.modify("extend", "left", "character");
+
+ yield new content.Promise((resolve, reject) => {
+ addEventListener("cut", function copyEvent(event) {
+ removeEventListener("cut", copyEvent, true);
+ event.clipboardData.setData("text/plain", "Some text");
+ event.clipboardData.setData("text/html", "Italic ");
+ selection.deleteFromDocument();
+ event.preventDefault();
+ resolve();
+ }, true)
+ sendKey("x");
+ });
+
+ selection.modify("move", "left", "line");
+
+ yield new content.Promise((resolve, reject) => {
+ addEventListener("paste", function copyEvent(event) {
+ removeEventListener("paste", copyEvent, true);
+ let clipboardData = event.clipboardData;
+ is(clipboardData.mozItemCount, 1, "One item on clipboard 2");
+ is(clipboardData.types.length, 2, "Two types on clipboard 2");
+ is(clipboardData.types[0], "text/html", "text/html on clipboard 2");
+ is(clipboardData.types[1], "text/plain", "text/plain on clipboard 2");
+ is(clipboardData.getData("text/html"), "Italic ", "text/html value 2");
+ is(clipboardData.getData("text/plain"), "Some text", "text/plain value 2");
+ resolve();
+ }, true)
+ sendKey("v");
+ });
+
+ is(main.innerHTML, "Italic Test Bold After", "Copy and paste html 2");
+ return results;
+ });
+
+ is(results.length, 15, "Correct number of results");
+ for (var t = 0; t < results.length; t++) {
+ ok(results[t].startsWith("PASSED"), results[t]);
+ }
+
+ gBrowser.removeCurrentTab();
+});
+
diff --git a/dom/base/moz.build b/dom/base/moz.build
index eb1bd3489bd..353a1290996 100644
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -417,6 +417,7 @@ LOCAL_INCLUDES += [
'/dom/xml',
'/dom/xslt/xpath',
'/dom/xul',
+ '/gfx/2d',
'/image/src',
'/js/xpconnect/src',
'/js/xpconnect/wrappers',
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index 80607405d0f..d8dd1905210 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -171,6 +171,7 @@
#include "nsReferencedElement.h"
#include "nsSandboxFlags.h"
#include "nsScriptSecurityManager.h"
+#include "nsStreamUtils.h"
#include "nsSVGFeatures.h"
#include "nsTextEditorState.h"
#include "nsTextFragment.h"
@@ -7219,7 +7220,6 @@ nsContentUtils::TransferablesToIPCTransferables(nsISupportsArray* aTransferables
mozilla::dom::nsIContentParent* aParent)
{
aIPC.Clear();
- MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
if (aTransferables) {
uint32_t transferableCount = 0;
aTransferables->Count(&transferableCount);
@@ -7227,75 +7227,135 @@ nsContentUtils::TransferablesToIPCTransferables(nsISupportsArray* aTransferables
IPCDataTransfer* dt = aIPC.AppendElement();
nsCOMPtr genericItem;
aTransferables->GetElementAt(i, getter_AddRefs(genericItem));
- nsCOMPtr item(do_QueryInterface(genericItem));
- if (item) {
- nsCOMPtr flavorList;
- item->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
- if (flavorList) {
- uint32_t flavorCount = 0;
- flavorList->Count(&flavorCount);
- for (uint32_t j = 0; j < flavorCount; ++j) {
- nsCOMPtr flavor = do_QueryElementAt(flavorList, j);
- if (!flavor) {
- continue;
+ nsCOMPtr transferable(do_QueryInterface(genericItem));
+ TransferableToIPCTransferable(transferable, dt, aChild, aParent);
+ }
+ }
+}
+
+void
+nsContentUtils::TransferableToIPCTransferable(nsITransferable* aTransferable,
+ IPCDataTransfer* aIPCDataTransfer,
+ mozilla::dom::nsIContentChild* aChild,
+ mozilla::dom::nsIContentParent* aParent)
+{
+ MOZ_ASSERT((aChild && !aParent) || (!aChild && aParent));
+
+ if (aTransferable) {
+ nsCOMPtr flavorList;
+ aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
+ if (flavorList) {
+ uint32_t flavorCount = 0;
+ flavorList->Count(&flavorCount);
+ for (uint32_t j = 0; j < flavorCount; ++j) {
+ nsCOMPtr flavor = do_QueryElementAt(flavorList, j);
+ if (!flavor) {
+ continue;
+ }
+
+ nsAutoCString flavorStr;
+ flavor->GetData(flavorStr);
+ if (!flavorStr.Length()) {
+ continue;
+ }
+
+ nsCOMPtr data;
+ uint32_t dataLen = 0;
+ aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(data), &dataLen);
+
+ nsCOMPtr text = do_QueryInterface(data);
+ nsCOMPtr ctext = do_QueryInterface(data);
+ if (text) {
+ nsAutoString dataAsString;
+ text->GetData(dataAsString);
+ IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+ item->flavor() = nsCString(flavorStr);
+ item->data() = nsString(dataAsString);
+ } else if (ctext) {
+ nsAutoCString dataAsString;
+ ctext->GetData(dataAsString);
+ IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+ item->flavor() = nsCString(flavorStr);
+ item->data() = nsCString(dataAsString);
+ } else {
+ nsCOMPtr sip =
+ do_QueryInterface(data);
+ if (sip) {
+ sip->GetData(getter_AddRefs(data));
+ }
+
+ // Images to be pasted on the clipboard are nsIInputStreams
+ nsCOMPtr stream(do_QueryInterface(data));
+ if (stream) {
+ IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+ item->flavor() = nsCString(flavorStr);
+
+ nsCString imageData;
+ NS_ConsumeStream(stream, UINT32_MAX, imageData);
+ item->data() = imageData;
+ continue;
+ }
+
+ // Images to be placed on the clipboard are imgIContainers.
+ nsCOMPtr image(do_QueryInterface(data));
+ if (image) {
+ RefPtr surface =
+ image->GetFrame(imgIContainer::FRAME_CURRENT,
+ imgIContainer::FLAG_SYNC_DECODE);
+
+ mozilla::RefPtr dataSurface =
+ surface->GetDataSurface();
+ size_t length;
+ int32_t stride;
+ mozilla::UniquePtr surfaceData =
+ nsContentUtils::GetSurfaceData(dataSurface, &length, &stride);
+
+ IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+ item->flavor() = nsCString(flavorStr);
+ item->data() = nsCString(surfaceData.get(), length);
+
+ IPCDataTransferImage& imageDetails = item->imageDetails();
+ mozilla::gfx::IntSize size = dataSurface->GetSize();
+ imageDetails.width() = size.width;
+ imageDetails.height() = size.height;
+ imageDetails.stride() = stride;
+ imageDetails.format() = static_cast(dataSurface->GetFormat());
+
+ continue;
+ }
+
+ // Otherwise, handle this as a file.
+ nsCOMPtr fileImpl;
+ nsCOMPtr file = do_QueryInterface(data);
+ if (file) {
+ fileImpl = new FileImplFile(file, false);
+ ErrorResult rv;
+ fileImpl->GetSize(rv);
+ fileImpl->GetLastModified(rv);
+ } else {
+ fileImpl = do_QueryInterface(data);
+ }
+ if (fileImpl) {
+ IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
+ item->flavor() = nsCString(flavorStr);
+ if (aChild) {
+ item->data() =
+ mozilla::dom::BlobChild::GetOrCreate(aChild,
+ static_cast(fileImpl.get()));
+ } else if (aParent) {
+ item->data() =
+ mozilla::dom::BlobParent::GetOrCreate(aParent,
+ static_cast(fileImpl.get()));
}
-
- nsAutoCString flavorStr;
- flavor->GetData(flavorStr);
- if (!flavorStr.Length()) {
- continue;
- }
-
- nsCOMPtr data;
- uint32_t dataLen = 0;
- item->GetTransferData(flavorStr.get(), getter_AddRefs(data), &dataLen);
-
- nsCOMPtr text = do_QueryInterface(data);
- if (text) {
- nsAutoString dataAsString;
- text->GetData(dataAsString);
- IPCDataTransferItem* item = dt->items().AppendElement();
+ } else {
+ // This is a hack to support kFilePromiseMime.
+ // On Windows there just needs to be an entry for it,
+ // and for OSX we need to create
+ // nsContentAreaDragDropDataProvider as nsIFlavorDataProvider.
+ if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
+ IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
item->flavor() = nsCString(flavorStr);
- item->data() = nsString(dataAsString);
- } else {
- nsCOMPtr sip =
- do_QueryInterface(data);
- if (sip) {
- sip->GetData(getter_AddRefs(data));
- }
- nsCOMPtr fileImpl;
- nsCOMPtr file = do_QueryInterface(data);
- if (file) {
- fileImpl = new FileImplFile(file, false);
- ErrorResult rv;
- fileImpl->GetSize(rv);
- fileImpl->GetLastModified(rv);
- } else {
- fileImpl = do_QueryInterface(data);
- }
- if (fileImpl) {
- IPCDataTransferItem* item = dt->items().AppendElement();
- item->flavor() = nsCString(flavorStr);
- if (aChild) {
- item->data() =
- mozilla::dom::BlobChild::GetOrCreate(aChild,
- static_cast(fileImpl.get()));
- } else if (aParent) {
- item->data() =
- mozilla::dom::BlobParent::GetOrCreate(aParent,
- static_cast(fileImpl.get()));
- }
- } else {
- // This is a hack to support kFilePromiseMime.
- // On Windows there just needs to be an entry for it,
- // and for OSX we need to create
- // nsContentAreaDragDropDataProvider as nsIFlavorDataProvider.
- if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
- IPCDataTransferItem* item = dt->items().AppendElement();
- item->flavor() = nsCString(flavorStr);
- item->data() = NS_ConvertUTF8toUTF16(flavorStr);
- }
- }
+ item->data() = NS_ConvertUTF8toUTF16(flavorStr);
}
}
}
@@ -7303,3 +7363,31 @@ nsContentUtils::TransferablesToIPCTransferables(nsISupportsArray* aTransferables
}
}
}
+
+mozilla::UniquePtr
+nsContentUtils::GetSurfaceData(mozilla::gfx::DataSourceSurface* aSurface,
+ size_t* aLength, int32_t* aStride)
+{
+ mozilla::gfx::DataSourceSurface::MappedSurface map;
+ aSurface->Map(mozilla::gfx::DataSourceSurface::MapType::READ, &map);
+ 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 surfaceData(new char[maxBufLen + 1]);
+ memcpy(surfaceData.get(), reinterpret_cast(map.mData), bufLen);
+ memset(surfaceData.get() + bufLen, 0, maxBufLen - bufLen + 1);
+
+ *aLength = maxBufLen;
+ *aStride = map.mStride;
+
+ aSurface->Unmap();
+ return surfaceData;
+}
diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
index 42d82796a3c..1b2459379c9 100644
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -103,6 +103,7 @@ class nsTextFragment;
class nsViewportInfo;
class nsWrapperCache;
class nsAttrValue;
+class nsITransferable;
struct JSPropertyDescriptor;
struct JSRuntime;
@@ -129,6 +130,10 @@ class Selection;
class TabParent;
} // namespace dom
+namespace gfx {
+class DataSourceSurface;
+} // namespace gfx
+
namespace layers {
class LayerManager;
} // namespace layers
@@ -2298,6 +2303,19 @@ public:
nsTArray& aIPC,
mozilla::dom::nsIContentChild* aChild,
mozilla::dom::nsIContentParent* aParent);
+
+ static void TransferableToIPCTransferable(nsITransferable* aTransferable,
+ mozilla::dom::IPCDataTransfer* aIPCDataTransfer,
+ mozilla::dom::nsIContentChild* aChild,
+ mozilla::dom::nsIContentParent* aParent);
+
+ /*
+ * 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 GetSurfaceData(mozilla::gfx::DataSourceSurface* aSurface,
+ size_t* aLength, int32_t* aStride);
+
private:
static bool InitializeEventTable();
diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
index be86711c940..a036b0d14d3 100755
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -130,6 +130,8 @@
#include "nsIURIFixup.h"
#include "nsIWindowWatcher.h"
#include "nsIXULRuntime.h"
+#include "gfxDrawable.h"
+#include "ImageOps.h"
#include "nsMemoryInfoDumper.h"
#include "nsMemoryReporterManager.h"
#include "nsServiceManagerUtils.h"
@@ -142,6 +144,7 @@
#include "ProcessPriorityManager.h"
#include "SandboxHal.h"
#include "ScreenManagerParent.h"
+#include "SourceSurfaceRawData.h"
#include "StructuredCloneUtils.h"
#include "TabParent.h"
#include "URIUtils.h"
@@ -221,7 +224,6 @@ using namespace mozilla::system;
#endif
static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
-static const char* sClipboardTextFlavors[] = { kUnicodeMime };
using base::ChildPrivileges;
using base::KillProcess;
@@ -2556,42 +2558,9 @@ ContentParent::RecvReadPermissions(InfallibleTArray* aPermissio
}
bool
-ContentParent::RecvSetClipboardText(const nsString& text,
- const bool& isPrivateData,
- const int32_t& whichClipboard)
-{
- nsresult rv;
- nsCOMPtr clipboard(do_GetService(kCClipboardCID, &rv));
- NS_ENSURE_SUCCESS(rv, true);
-
- nsCOMPtr dataWrapper =
- do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, true);
-
- rv = dataWrapper->SetData(text);
- NS_ENSURE_SUCCESS(rv, true);
-
- nsCOMPtr trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
- NS_ENSURE_SUCCESS(rv, true);
- trans->Init(nullptr);
-
- // If our data flavor has already been added, this will fail. But we don't care
- trans->AddDataFlavor(kUnicodeMime);
- trans->SetIsPrivateData(isPrivateData);
-
- nsCOMPtr nsisupportsDataWrapper =
- do_QueryInterface(dataWrapper);
-
- rv = trans->SetTransferData(kUnicodeMime, nsisupportsDataWrapper,
- text.Length() * sizeof(char16_t));
- NS_ENSURE_SUCCESS(rv, true);
-
- clipboard->SetData(trans, nullptr, whichClipboard);
- return true;
-}
-
-bool
-ContentParent::RecvGetClipboardText(const int32_t& whichClipboard, nsString* text)
+ContentParent::RecvSetClipboard(const IPCDataTransfer& aDataTransfer,
+ const bool& aIsPrivateData,
+ const int32_t& aWhichClipboard)
{
nsresult rv;
nsCOMPtr clipboard(do_GetService(kCClipboardCID, &rv));
@@ -2600,44 +2569,116 @@ ContentParent::RecvGetClipboardText(const int32_t& whichClipboard, nsString* tex
nsCOMPtr trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
NS_ENSURE_SUCCESS(rv, true);
trans->Init(nullptr);
- trans->AddDataFlavor(kUnicodeMime);
- clipboard->GetData(trans, whichClipboard);
- nsCOMPtr tmp;
- uint32_t len;
- rv = trans->GetTransferData(kUnicodeMime, getter_AddRefs(tmp), &len);
- if (NS_FAILED(rv))
- return true;
+ const nsTArray& items = aDataTransfer.items();
+ for (uint32_t j = 0; j < items.Length(); ++j) {
+ const IPCDataTransferItem& item = items[j];
- nsCOMPtr supportsString = do_QueryInterface(tmp);
- // No support for non-text data
- if (!supportsString)
- return true;
- supportsString->GetData(*text);
+ trans->AddDataFlavor(item.flavor().get());
+
+ if (item.data().type() == IPCDataTransferData::TnsString) {
+ nsCOMPtr dataWrapper =
+ do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, true);
+
+ nsString text = item.data().get_nsString();
+ rv = dataWrapper->SetData(text);
+ NS_ENSURE_SUCCESS(rv, true);
+
+ rv = trans->SetTransferData(item.flavor().get(), dataWrapper,
+ text.Length() * sizeof(char16_t));
+
+ NS_ENSURE_SUCCESS(rv, true);
+ } else if (item.data().type() == IPCDataTransferData::TnsCString) {
+ const IPCDataTransferImage& imageDetails = item.imageDetails();
+ const gfxIntSize size(imageDetails.width(), imageDetails.height());
+ if (!size.width || !size.height) {
+ return true;
+ }
+
+ nsCString text = item.data().get_nsCString();
+ mozilla::RefPtr image =
+ new mozilla::gfx::SourceSurfaceRawData();
+ mozilla::gfx::SourceSurfaceRawData* raw =
+ static_cast(image.get());
+ raw->InitWrappingData(
+ reinterpret_cast(const_cast(text).BeginWriting()),
+ size, imageDetails.stride(),
+ static_cast(imageDetails.format()), false);
+ raw->GuaranteePersistance();
+
+ nsRefPtr drawable = new gfxSurfaceDrawable(image, size);
+ nsCOMPtr imageContainer(image::ImageOps::CreateFromDrawable(drawable));
+
+ nsCOMPtr
+ imgPtr(do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv));
+
+ rv = imgPtr->SetData(imageContainer);
+ NS_ENSURE_SUCCESS(rv, true);
+
+ trans->SetTransferData(item.flavor().get(), imgPtr, sizeof(nsISupports*));
+ }
+ }
+
+ trans->SetIsPrivateData(aIsPrivateData);
+
+ clipboard->SetData(trans, nullptr, aWhichClipboard);
return true;
}
bool
-ContentParent::RecvEmptyClipboard(const int32_t& whichClipboard)
+ContentParent::RecvGetClipboard(nsTArray&& aTypes,
+ const int32_t& aWhichClipboard,
+ IPCDataTransfer* aDataTransfer)
{
nsresult rv;
nsCOMPtr clipboard(do_GetService(kCClipboardCID, &rv));
NS_ENSURE_SUCCESS(rv, true);
- clipboard->EmptyClipboard(whichClipboard);
+ nsCOMPtr trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
+ NS_ENSURE_SUCCESS(rv, true);
+ trans->Init(nullptr);
+ for (uint32_t t = 0; t < aTypes.Length(); t++) {
+ trans->AddDataFlavor(aTypes[t].get());
+ }
+
+ clipboard->GetData(trans, aWhichClipboard);
+ nsContentUtils::TransferableToIPCTransferable(trans, aDataTransfer,
+ nullptr, this);
return true;
}
bool
-ContentParent::RecvClipboardHasText(const int32_t& whichClipboard, bool* hasText)
+ContentParent::RecvEmptyClipboard(const int32_t& aWhichClipboard)
{
nsresult rv;
nsCOMPtr clipboard(do_GetService(kCClipboardCID, &rv));
NS_ENSURE_SUCCESS(rv, true);
- clipboard->HasDataMatchingFlavors(sClipboardTextFlavors, 1,
- whichClipboard, hasText);
+ clipboard->EmptyClipboard(aWhichClipboard);
+
+ return true;
+}
+
+bool
+ContentParent::RecvClipboardHasType(nsTArray&& aTypes,
+ const int32_t& aWhichClipboard,
+ bool* aHasType)
+{
+ nsresult rv;
+ nsCOMPtr clipboard(do_GetService(kCClipboardCID, &rv));
+ NS_ENSURE_SUCCESS(rv, true);
+
+ const char** typesChrs = new const char *[aTypes.Length()];
+ for (uint32_t t = 0; t < aTypes.Length(); t++) {
+ typesChrs[t] = aTypes[t].get();
+ }
+
+ clipboard->HasDataMatchingFlavors(typesChrs, aTypes.Length(),
+ aWhichClipboard, aHasType);
+
+ delete [] typesChrs;
return true;
}
diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h
index 506435d97a5..722a95002a6 100644
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -660,12 +660,16 @@ private:
virtual bool RecvReadPermissions(InfallibleTArray* aPermissions) override;
- virtual bool RecvSetClipboardText(const nsString& text,
- const bool& isPrivateData,
- const int32_t& whichClipboard) override;
- virtual bool RecvGetClipboardText(const int32_t& whichClipboard, nsString* text) override;
- virtual bool RecvEmptyClipboard(const int32_t& whichClipboard) override;
- virtual bool RecvClipboardHasText(const int32_t& whichClipboard, bool* hasText) override;
+ virtual bool RecvSetClipboard(const IPCDataTransfer& aDataTransfer,
+ const bool& aIsPrivateData,
+ const int32_t& aWhichClipboard) override;
+ virtual bool RecvGetClipboard(nsTArray&& aTypes,
+ const int32_t& aWhichClipboard,
+ IPCDataTransfer* aDataTransfer) override;
+ virtual bool RecvEmptyClipboard(const int32_t& aWhichClipboard) override;
+ virtual bool RecvClipboardHasType(nsTArray&& aTypes,
+ const int32_t& aWhichClipboard,
+ bool* aHasType) override;
virtual bool RecvGetSystemColors(const uint32_t& colorsCount,
InfallibleTArray* colors) override;
diff --git a/dom/ipc/DOMTypes.ipdlh b/dom/ipc/DOMTypes.ipdlh
index 58657e2ff68..38866d403bc 100644
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -137,13 +137,24 @@ union BlobConstructorParams
union IPCDataTransferData
{
- nsString;
- PBlob;
+ nsString; // text
+ nsCString; // images
+ PBlob; // files
+};
+
+struct IPCDataTransferImage
+{
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ uint8_t format;
};
struct IPCDataTransferItem
{
nsCString flavor;
+ // The image details are only used when transferring images.
+ IPCDataTransferImage imageDetails;
IPCDataTransferData data;
};
diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl
index 284c1277696..8c084feaf25 100644
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -776,12 +776,22 @@ parent:
// nsIPermissionManager messages
sync ReadPermissions() returns (Permission[] permissions);
- SetClipboardText(nsString text, bool isPrivateData, int32_t whichClipboard);
- sync GetClipboardText(int32_t whichClipboard)
- returns (nsString text);
- EmptyClipboard(int32_t whichClipboard);
- sync ClipboardHasText(int32_t whichClipboard)
- returns (bool hasText);
+ // Places the items within dataTransfer on the clipboard.
+ SetClipboard(IPCDataTransfer aDataTransfer,
+ bool aIsPrivateData,
+ int32_t aWhichClipboard);
+
+ // Given a list of supported types, returns the clipboard data for the
+ // first type that matches.
+ sync GetClipboard(nsCString[] aTypes, int32_t aWhichClipboard)
+ returns (IPCDataTransfer dataTransfer);
+
+ // Clears the clipboard.
+ EmptyClipboard(int32_t aWhichClipboard);
+
+ // Returns true if data of one of the specified types is on the clipboard.
+ sync ClipboardHasType(nsCString[] aTypes, int32_t aWhichClipboard)
+ returns (bool hasType);
sync GetSystemColors(uint32_t colorsCount)
returns (uint32_t[] colors);
diff --git a/widget/nsClipboardProxy.cpp b/widget/nsClipboardProxy.cpp
index 30c08d94c98..520efc76539 100644
--- a/widget/nsClipboardProxy.cpp
+++ b/widget/nsClipboardProxy.cpp
@@ -8,6 +8,8 @@
#include "nsCOMPtr.h"
#include "nsComponentManagerUtils.h"
#include "nsXULAppAPI.h"
+#include "nsContentUtils.h"
+#include "nsStringStream.h"
using namespace mozilla;
using namespace mozilla::dom;
@@ -23,21 +25,15 @@ NS_IMETHODIMP
nsClipboardProxy::SetData(nsITransferable *aTransferable,
nsIClipboardOwner *anOwner, int32_t aWhichClipboard)
{
- nsCOMPtr tmp;
- uint32_t len;
- nsresult rv = aTransferable->GetTransferData(kUnicodeMime, getter_AddRefs(tmp),
- &len);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr supportsString = do_QueryInterface(tmp);
- // No support for non-text data
- NS_ENSURE_TRUE(supportsString, NS_ERROR_NOT_IMPLEMENTED);
- nsAutoString buffer;
- supportsString->GetData(buffer);
+ ContentChild* child = ContentChild::GetSingleton();
+
+ IPCDataTransfer ipcDataTransfer;
+ nsContentUtils::TransferableToIPCTransferable(aTransferable, &ipcDataTransfer,
+ child, nullptr);
bool isPrivateData = false;
aTransferable->GetIsPrivateData(&isPrivateData);
- ContentChild::GetSingleton()->SendSetClipboardText(buffer, isPrivateData,
- aWhichClipboard);
+ child->SendSetClipboard(ipcDataTransfer, isPrivateData, aWhichClipboard);
return NS_OK;
}
@@ -45,25 +41,72 @@ nsClipboardProxy::SetData(nsITransferable *aTransferable,
NS_IMETHODIMP
nsClipboardProxy::GetData(nsITransferable *aTransferable, int32_t aWhichClipboard)
{
- nsAutoString buffer;
- ContentChild::GetSingleton()->SendGetClipboardText(aWhichClipboard, &buffer);
+ nsTArray types;
+
+ nsCOMPtr flavorList;
+ aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList));
+ if (flavorList) {
+ uint32_t flavorCount = 0;
+ flavorList->Count(&flavorCount);
+ for (uint32_t j = 0; j < flavorCount; ++j) {
+ nsCOMPtr flavor = do_QueryElementAt(flavorList, j);
+ if (flavor) {
+ nsAutoCString flavorStr;
+ flavor->GetData(flavorStr);
+ if (flavorStr.Length()) {
+ types.AppendElement(flavorStr);
+ }
+ }
+ }
+ }
nsresult rv;
- nsCOMPtr dataWrapper =
- do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
+ IPCDataTransfer dataTransfer;
+ ContentChild::GetSingleton()->SendGetClipboard(types, aWhichClipboard, &dataTransfer);
- rv = dataWrapper->SetData(buffer);
- NS_ENSURE_SUCCESS(rv, rv);
+ auto& items = dataTransfer.items();
+ for (uint32_t j = 0; j < items.Length(); ++j) {
+ const IPCDataTransferItem& item = items[j];
- // If our data flavor has already been added, this will fail. But we don't care
- aTransferable->AddDataFlavor(kUnicodeMime);
+ if (item.data().type() == IPCDataTransferData::TnsString) {
+ nsCOMPtr dataWrapper =
+ do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr nsisupportsDataWrapper =
- do_QueryInterface(dataWrapper);
- rv = aTransferable->SetTransferData(kUnicodeMime, nsisupportsDataWrapper,
- buffer.Length() * sizeof(char16_t));
- NS_ENSURE_SUCCESS(rv, rv);
+ nsString data = item.data().get_nsString();
+ rv = dataWrapper->SetData(data);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper,
+ data.Length() * sizeof(char16_t));
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (item.data().type() == IPCDataTransferData::TnsCString) {
+ // If this is an image, convert it into an nsIInputStream.
+ nsCString flavor = item.flavor();
+ if (flavor.EqualsLiteral(kJPEGImageMime) ||
+ flavor.EqualsLiteral(kJPGImageMime) ||
+ flavor.EqualsLiteral(kPNGImageMime) ||
+ flavor.EqualsLiteral(kGIFImageMime)) {
+ nsCOMPtr stream;
+ NS_NewCStringInputStream(getter_AddRefs(stream), item.data().get_nsCString());
+
+ rv = aTransferable->SetTransferData(flavor.get(), stream, sizeof(nsISupports*));
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (flavor.EqualsLiteral(kNativeHTMLMime)) {
+ nsCOMPtr dataWrapper =
+ do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString data = item.data().get_nsCString();
+ rv = dataWrapper->SetData(data);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aTransferable->SetTransferData(item.flavor().get(), dataWrapper,
+ data.Length());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ }
return NS_OK;
}
@@ -77,11 +120,18 @@ nsClipboardProxy::EmptyClipboard(int32_t aWhichClipboard)
NS_IMETHODIMP
nsClipboardProxy::HasDataMatchingFlavors(const char **aFlavorList,
- uint32_t aLength, int32_t aWhichClipboard,
- bool *aHasText)
+ uint32_t aLength, int32_t aWhichClipboard,
+ bool *aHasType)
{
- *aHasText = false;
- ContentChild::GetSingleton()->SendClipboardHasText(aWhichClipboard, aHasText);
+ *aHasType = false;
+
+ nsTArray types;
+ for (uint32_t j = 0; j < aLength; ++j) {
+ types.AppendElement(nsDependentCString(aFlavorList[j]));
+ }
+
+ ContentChild::GetSingleton()->SendClipboardHasType(types, aWhichClipboard, aHasType);
+
return NS_OK;
}
diff --git a/widget/nsDragServiceProxy.cpp b/widget/nsDragServiceProxy.cpp
index 8c94e96cf28..94726bc888c 100644
--- a/widget/nsDragServiceProxy.cpp
+++ b/widget/nsDragServiceProxy.cpp
@@ -58,30 +58,19 @@ nsDragServiceProxy::InvokeDragSession(nsIDOMNode* aDOMNode,
if (surface) {
mozilla::RefPtr dataSurface =
surface->GetDataSurface();
- mozilla::gfx::DataSourceSurface::MappedSurface map;
- dataSurface->Map(mozilla::gfx::DataSourceSurface::MapType::READ, &map);
mozilla::gfx::IntSize size = dataSurface->GetSize();
- mozilla::CheckedInt32 requiredBytes =
- mozilla::CheckedInt32(map.mStride) * mozilla::CheckedInt32(size.height);
- size_t maxBufLen = requiredBytes.isValid() ? requiredBytes.value() : 0;
- mozilla::gfx::SurfaceFormat format = dataSurface->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 dragImageData(new char[maxBufLen + 1]);
- memcpy(dragImageData.get(), reinterpret_cast(map.mData), bufLen);
- memset(dragImageData.get() + bufLen, 0, maxBufLen - bufLen + 1);
- nsDependentCString dragImage(dragImageData.get(), maxBufLen);
+ size_t length;
+ int32_t stride;
+ mozilla::UniquePtr surfaceData =
+ nsContentUtils::GetSurfaceData(dataSurface, &length, &stride);
+ nsDependentCString dragImage(surfaceData.get(), length);
mozilla::unused <<
child->SendInvokeDragSession(dataTransfers, aActionType, dragImage,
- size.width, size.height, map.mStride,
- static_cast(format),
+ size.width, size.height, stride,
+ static_cast(dataSurface->GetFormat()),
dragRect.x, dragRect.y);
- dataSurface->Unmap();
StartDragSession();
return NS_OK;
}