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; }