Bug 1071562, e10s, support non-text types in clipboard (html, images, etc), r=smaug

This commit is contained in:
Neil Deakin 2015-04-16 15:38:12 -04:00
parent 1174410076
commit 1ce970af4d
11 changed files with 520 additions and 184 deletions

View File

@ -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

View File

@ -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 = "<div id='main' contenteditable='true'>Test <b>Bold</b> After Text</div>";
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 <b>Bold</b>", "text/html value");
is(clipboardData.getData("text/plain"), "t Bold", "text/plain value");
resolve();
}, true)
sendKey("v");
});
is(main.innerHTML, "Test <b>Bold</b> After Textt <b>Bold</b>", "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", "<i>Italic</i> ");
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"), "<i>Italic</i> ", "text/html value 2");
is(clipboardData.getData("text/plain"), "Some text", "text/plain value 2");
resolve();
}, true)
sendKey("v");
});
is(main.innerHTML, "<i>Italic</i> Test <b>Bold</b> After<b></b>", "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();
});

View File

@ -417,6 +417,7 @@ LOCAL_INCLUDES += [
'/dom/xml',
'/dom/xslt/xpath',
'/dom/xul',
'/gfx/2d',
'/image/src',
'/js/xpconnect/src',
'/js/xpconnect/wrappers',

View File

@ -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<nsISupports> genericItem;
aTransferables->GetElementAt(i, getter_AddRefs(genericItem));
nsCOMPtr<nsITransferable> item(do_QueryInterface(genericItem));
if (item) {
nsCOMPtr<nsISupportsArray> flavorList;
item->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
if (flavorList) {
uint32_t flavorCount = 0;
flavorList->Count(&flavorCount);
for (uint32_t j = 0; j < flavorCount; ++j) {
nsCOMPtr<nsISupportsCString> flavor = do_QueryElementAt(flavorList, j);
if (!flavor) {
continue;
nsCOMPtr<nsITransferable> 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<nsISupportsArray> flavorList;
aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
if (flavorList) {
uint32_t flavorCount = 0;
flavorList->Count(&flavorCount);
for (uint32_t j = 0; j < flavorCount; ++j) {
nsCOMPtr<nsISupportsCString> flavor = do_QueryElementAt(flavorList, j);
if (!flavor) {
continue;
}
nsAutoCString flavorStr;
flavor->GetData(flavorStr);
if (!flavorStr.Length()) {
continue;
}
nsCOMPtr<nsISupports> data;
uint32_t dataLen = 0;
aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(data), &dataLen);
nsCOMPtr<nsISupportsString> text = do_QueryInterface(data);
nsCOMPtr<nsISupportsCString> 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<nsISupportsInterfacePointer> sip =
do_QueryInterface(data);
if (sip) {
sip->GetData(getter_AddRefs(data));
}
// Images to be pasted on the clipboard are nsIInputStreams
nsCOMPtr<nsIInputStream> 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<imgIContainer> image(do_QueryInterface(data));
if (image) {
RefPtr<mozilla::gfx::SourceSurface> surface =
image->GetFrame(imgIContainer::FRAME_CURRENT,
imgIContainer::FLAG_SYNC_DECODE);
mozilla::RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
surface->GetDataSurface();
size_t length;
int32_t stride;
mozilla::UniquePtr<char[]> 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<uint8_t>(dataSurface->GetFormat());
continue;
}
// Otherwise, handle this as a file.
nsCOMPtr<FileImpl> fileImpl;
nsCOMPtr<nsIFile> 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*>(fileImpl.get()));
} else if (aParent) {
item->data() =
mozilla::dom::BlobParent::GetOrCreate(aParent,
static_cast<FileImpl*>(fileImpl.get()));
}
nsAutoCString flavorStr;
flavor->GetData(flavorStr);
if (!flavorStr.Length()) {
continue;
}
nsCOMPtr<nsISupports> data;
uint32_t dataLen = 0;
item->GetTransferData(flavorStr.get(), getter_AddRefs(data), &dataLen);
nsCOMPtr<nsISupportsString> 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<nsISupportsInterfacePointer> sip =
do_QueryInterface(data);
if (sip) {
sip->GetData(getter_AddRefs(data));
}
nsCOMPtr<FileImpl> fileImpl;
nsCOMPtr<nsIFile> 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*>(fileImpl.get()));
} else if (aParent) {
item->data() =
mozilla::dom::BlobParent::GetOrCreate(aParent,
static_cast<FileImpl*>(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<char[]>
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<char[]> surfaceData(new char[maxBufLen + 1]);
memcpy(surfaceData.get(), reinterpret_cast<char*>(map.mData), bufLen);
memset(surfaceData.get() + bufLen, 0, maxBufLen - bufLen + 1);
*aLength = maxBufLen;
*aStride = map.mStride;
aSurface->Unmap();
return surfaceData;
}

View File

@ -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<mozilla::dom::IPCDataTransfer>& 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<char[]> GetSurfaceData(mozilla::gfx::DataSourceSurface* aSurface,
size_t* aLength, int32_t* aStride);
private:
static bool InitializeEventTable();

View File

@ -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<IPC::Permission>* aPermissio
}
bool
ContentParent::RecvSetClipboardText(const nsString& text,
const bool& isPrivateData,
const int32_t& whichClipboard)
{
nsresult rv;
nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
NS_ENSURE_SUCCESS(rv, true);
nsCOMPtr<nsISupportsString> dataWrapper =
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, true);
rv = dataWrapper->SetData(text);
NS_ENSURE_SUCCESS(rv, true);
nsCOMPtr<nsITransferable> 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<nsISupports> 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<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
@ -2600,44 +2569,116 @@ ContentParent::RecvGetClipboardText(const int32_t& whichClipboard, nsString* tex
nsCOMPtr<nsITransferable> 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<nsISupports> tmp;
uint32_t len;
rv = trans->GetTransferData(kUnicodeMime, getter_AddRefs(tmp), &len);
if (NS_FAILED(rv))
return true;
const nsTArray<IPCDataTransferItem>& items = aDataTransfer.items();
for (uint32_t j = 0; j < items.Length(); ++j) {
const IPCDataTransferItem& item = items[j];
nsCOMPtr<nsISupportsString> 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<nsISupportsString> 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<gfx::DataSourceSurface> image =
new mozilla::gfx::SourceSurfaceRawData();
mozilla::gfx::SourceSurfaceRawData* raw =
static_cast<mozilla::gfx::SourceSurfaceRawData*>(image.get());
raw->InitWrappingData(
reinterpret_cast<uint8_t*>(const_cast<nsCString&>(text).BeginWriting()),
size, imageDetails.stride(),
static_cast<mozilla::gfx::SurfaceFormat>(imageDetails.format()), false);
raw->GuaranteePersistance();
nsRefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(image, size);
nsCOMPtr<imgIContainer> imageContainer(image::ImageOps::CreateFromDrawable(drawable));
nsCOMPtr<nsISupportsInterfacePointer>
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<nsCString>&& aTypes,
const int32_t& aWhichClipboard,
IPCDataTransfer* aDataTransfer)
{
nsresult rv;
nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
NS_ENSURE_SUCCESS(rv, true);
clipboard->EmptyClipboard(whichClipboard);
nsCOMPtr<nsITransferable> 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<nsIClipboard> 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<nsCString>&& aTypes,
const int32_t& aWhichClipboard,
bool* aHasType)
{
nsresult rv;
nsCOMPtr<nsIClipboard> 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;
}

View File

@ -660,12 +660,16 @@ private:
virtual bool RecvReadPermissions(InfallibleTArray<IPC::Permission>* 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<nsCString>&& aTypes,
const int32_t& aWhichClipboard,
IPCDataTransfer* aDataTransfer) override;
virtual bool RecvEmptyClipboard(const int32_t& aWhichClipboard) override;
virtual bool RecvClipboardHasType(nsTArray<nsCString>&& aTypes,
const int32_t& aWhichClipboard,
bool* aHasType) override;
virtual bool RecvGetSystemColors(const uint32_t& colorsCount,
InfallibleTArray<uint32_t>* colors) override;

View File

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

View File

@ -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);

View File

@ -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<nsISupports> tmp;
uint32_t len;
nsresult rv = aTransferable->GetTransferData(kUnicodeMime, getter_AddRefs(tmp),
&len);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISupportsString> 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<nsCString> types;
nsCOMPtr<nsISupportsArray> flavorList;
aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList));
if (flavorList) {
uint32_t flavorCount = 0;
flavorList->Count(&flavorCount);
for (uint32_t j = 0; j < flavorCount; ++j) {
nsCOMPtr<nsISupportsCString> flavor = do_QueryElementAt(flavorList, j);
if (flavor) {
nsAutoCString flavorStr;
flavor->GetData(flavorStr);
if (flavorStr.Length()) {
types.AppendElement(flavorStr);
}
}
}
}
nsresult rv;
nsCOMPtr<nsISupportsString> 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<nsISupportsString> dataWrapper =
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISupports> 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<nsIInputStream> 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<nsISupportsCString> 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<nsCString> types;
for (uint32_t j = 0; j < aLength; ++j) {
types.AppendElement(nsDependentCString(aFlavorList[j]));
}
ContentChild::GetSingleton()->SendClipboardHasType(types, aWhichClipboard, aHasType);
return NS_OK;
}

View File

@ -58,30 +58,19 @@ nsDragServiceProxy::InvokeDragSession(nsIDOMNode* aDOMNode,
if (surface) {
mozilla::RefPtr<mozilla::gfx::DataSourceSurface> 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<char[]> dragImageData(new char[maxBufLen + 1]);
memcpy(dragImageData.get(), reinterpret_cast<char*>(map.mData), bufLen);
memset(dragImageData.get() + bufLen, 0, maxBufLen - bufLen + 1);
nsDependentCString dragImage(dragImageData.get(), maxBufLen);
size_t length;
int32_t stride;
mozilla::UniquePtr<char[]> 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<uint8_t>(format),
size.width, size.height, stride,
static_cast<uint8_t>(dataSurface->GetFormat()),
dragRect.x, dragRect.y);
dataSurface->Unmap();
StartDragSession();
return NS_OK;
}