forked from mirrors/gecko-dev
Backed out changeset c8561b4726a2 (bug 1852947) for causing bustage on nsClipboardProxy.cpp CLOSED TREE
This commit is contained in:
parent
9d4a3c9bdb
commit
ced0683cec
25 changed files with 418 additions and 1417 deletions
|
|
@ -78,169 +78,6 @@ static bool MaybeCreateAndDispatchMozClipboardReadPasteEvent(
|
|||
Cancelable::eNo)));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* This is a base class for ClipboardGetCallbackForRead and
|
||||
* ClipboardGetCallbackForReadText.
|
||||
*/
|
||||
class ClipboardGetCallback : public nsIAsyncClipboardGetCallback {
|
||||
public:
|
||||
explicit ClipboardGetCallback(RefPtr<Promise>&& aPromise)
|
||||
: mPromise(std::move(aPromise)) {}
|
||||
|
||||
// This object will never be held by a cycle-collected object, so it doesn't
|
||||
// need to be cycle-collected despite holding alive cycle-collected objects.
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIAsyncClipboardGetCallback
|
||||
NS_IMETHOD OnError(nsresult aResult) override final {
|
||||
MOZ_ASSERT(mPromise);
|
||||
RefPtr<Promise> p(std::move(mPromise));
|
||||
p->MaybeRejectWithNotAllowedError(
|
||||
"Clipboard read operation is not allowed.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~ClipboardGetCallback() { MOZ_ASSERT(!mPromise); };
|
||||
|
||||
// Not cycle-collected, because it should be nulled when the request is
|
||||
// answered, rejected or aborted.
|
||||
RefPtr<Promise> mPromise;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(ClipboardGetCallback, nsIAsyncClipboardGetCallback)
|
||||
|
||||
class ClipboardGetCallbackForRead final : public ClipboardGetCallback {
|
||||
public:
|
||||
explicit ClipboardGetCallbackForRead(nsIGlobalObject* aGlobal,
|
||||
RefPtr<Promise>&& aPromise)
|
||||
: ClipboardGetCallback(std::move(aPromise)), mGlobal(aGlobal) {}
|
||||
|
||||
// This object will never be held by a cycle-collected object, so it doesn't
|
||||
// need to be cycle-collected despite holding alive cycle-collected objects.
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// nsIAsyncClipboardGetCallback
|
||||
NS_IMETHOD OnSuccess(
|
||||
nsIAsyncGetClipboardData* aAsyncGetClipboardData) override {
|
||||
MOZ_ASSERT(mPromise);
|
||||
MOZ_ASSERT(aAsyncGetClipboardData);
|
||||
|
||||
nsTArray<nsCString> flavorList;
|
||||
nsresult rv = aAsyncGetClipboardData->GetFlavorList(flavorList);
|
||||
if (NS_FAILED(rv)) {
|
||||
return OnError(rv);
|
||||
}
|
||||
|
||||
AutoTArray<RefPtr<ClipboardItem::ItemEntry>, 3> entries;
|
||||
for (const auto& format : flavorList) {
|
||||
auto entry = MakeRefPtr<ClipboardItem::ItemEntry>(
|
||||
mGlobal, NS_ConvertUTF8toUTF16(format));
|
||||
entry->LoadDataFromSystemClipboard(aAsyncGetClipboardData);
|
||||
entries.AppendElement(std::move(entry));
|
||||
}
|
||||
|
||||
RefPtr<Promise> p(std::move(mPromise));
|
||||
// We currently only support one clipboard item.
|
||||
p->MaybeResolve(
|
||||
AutoTArray<RefPtr<ClipboardItem>, 1>{MakeRefPtr<ClipboardItem>(
|
||||
mGlobal, PresentationStyle::Unspecified, std::move(entries))});
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
~ClipboardGetCallbackForRead() = default;
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(ClipboardGetCallbackForRead, ClipboardGetCallback)
|
||||
|
||||
class ClipboardGetCallbackForReadText final
|
||||
: public ClipboardGetCallback,
|
||||
public nsIAsyncClipboardRequestCallback {
|
||||
public:
|
||||
explicit ClipboardGetCallbackForReadText(RefPtr<Promise>&& aPromise)
|
||||
: ClipboardGetCallback(std::move(aPromise)) {}
|
||||
|
||||
// This object will never be held by a cycle-collected object, so it doesn't
|
||||
// need to be cycle-collected despite holding alive cycle-collected objects.
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// nsIAsyncClipboardGetCallback
|
||||
NS_IMETHOD OnSuccess(
|
||||
nsIAsyncGetClipboardData* aAsyncGetClipboardData) override {
|
||||
MOZ_ASSERT(mPromise);
|
||||
MOZ_ASSERT(!mTransferable);
|
||||
MOZ_ASSERT(aAsyncGetClipboardData);
|
||||
|
||||
AutoTArray<nsCString, 3> flavors;
|
||||
nsresult rv = aAsyncGetClipboardData->GetFlavorList(flavors);
|
||||
if (NS_FAILED(rv)) {
|
||||
return OnError(rv);
|
||||
}
|
||||
|
||||
mTransferable = do_CreateInstance("@mozilla.org/widget/transferable;1");
|
||||
if (NS_WARN_IF(!mTransferable)) {
|
||||
return OnError(NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
|
||||
mTransferable->Init(nullptr);
|
||||
mTransferable->AddDataFlavor(kTextMime);
|
||||
if (!flavors.Contains(kTextMime)) {
|
||||
return OnComplete(NS_OK);
|
||||
}
|
||||
|
||||
rv = aAsyncGetClipboardData->GetData(mTransferable, this);
|
||||
if (NS_FAILED(rv)) {
|
||||
return OnError(rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIAsyncClipboardRequestCallback
|
||||
NS_IMETHOD OnComplete(nsresult aResult) override {
|
||||
MOZ_ASSERT(mPromise);
|
||||
MOZ_ASSERT(mTransferable);
|
||||
|
||||
if (NS_FAILED(aResult)) {
|
||||
return OnError(aResult);
|
||||
}
|
||||
|
||||
nsAutoString str;
|
||||
nsCOMPtr<nsISupports> data;
|
||||
nsresult rv =
|
||||
mTransferable->GetTransferData(kTextMime, getter_AddRefs(data));
|
||||
if (!NS_WARN_IF(NS_FAILED(rv))) {
|
||||
nsCOMPtr<nsISupportsString> supportsstr = do_QueryInterface(data);
|
||||
MOZ_ASSERT(supportsstr);
|
||||
if (supportsstr) {
|
||||
supportsstr->GetData(str);
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<Promise> p(std::move(mPromise));
|
||||
p->MaybeResolve(str);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
~ClipboardGetCallbackForReadText() = default;
|
||||
|
||||
nsCOMPtr<nsITransferable> mTransferable;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(ClipboardGetCallbackForReadText,
|
||||
ClipboardGetCallback,
|
||||
nsIAsyncClipboardRequestCallback)
|
||||
|
||||
} // namespace
|
||||
|
||||
void Clipboard::ReadRequest::Answer() {
|
||||
RefPtr<Promise> p(std::move(mPromise));
|
||||
RefPtr<nsPIDOMWindowInner> owner(std::move(mOwner));
|
||||
|
|
@ -253,30 +90,89 @@ void Clipboard::ReadRequest::Answer() {
|
|||
return;
|
||||
}
|
||||
|
||||
RefPtr<ClipboardGetCallback> callback;
|
||||
switch (mType) {
|
||||
case ReadRequestType::eRead: {
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(owner);
|
||||
if (NS_WARN_IF(!global)) {
|
||||
clipboardService
|
||||
->AsyncHasDataMatchingFlavors(
|
||||
// Mandatory data types defined in
|
||||
// https://w3c.github.io/clipboard-apis/#mandatory-data-types-x
|
||||
AutoTArray<nsCString, 3>{nsDependentCString(kHTMLMime),
|
||||
nsDependentCString(kTextMime),
|
||||
nsDependentCString(kPNGImageMime)},
|
||||
nsIClipboard::kGlobalClipboard)
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
/* resolve */
|
||||
[owner, p](nsTArray<nsCString> formats) {
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(owner);
|
||||
if (NS_WARN_IF(!global)) {
|
||||
p->MaybeReject(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
AutoTArray<RefPtr<ClipboardItem::ItemEntry>, 3> entries;
|
||||
for (const auto& format : formats) {
|
||||
nsCOMPtr<nsITransferable> trans =
|
||||
do_CreateInstance("@mozilla.org/widget/transferable;1");
|
||||
if (NS_WARN_IF(!trans)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
trans->Init(nullptr);
|
||||
trans->AddDataFlavor(format.get());
|
||||
|
||||
RefPtr<ClipboardItem::ItemEntry> entry =
|
||||
MakeRefPtr<ClipboardItem::ItemEntry>(
|
||||
global, NS_ConvertUTF8toUTF16(format));
|
||||
entry->LoadDataFromSystemClipboard(*trans);
|
||||
entries.AppendElement(std::move(entry));
|
||||
}
|
||||
|
||||
// We currently only support one clipboard item.
|
||||
AutoTArray<RefPtr<ClipboardItem>, 1> items;
|
||||
items.AppendElement(MakeRefPtr<ClipboardItem>(
|
||||
global, PresentationStyle::Unspecified,
|
||||
std::move(entries)));
|
||||
|
||||
p->MaybeResolve(std::move(items));
|
||||
},
|
||||
/* reject */
|
||||
[p](nsresult rv) { p->MaybeReject(rv); });
|
||||
break;
|
||||
}
|
||||
case ReadRequestType::eReadText: {
|
||||
nsCOMPtr<nsITransferable> trans =
|
||||
do_CreateInstance("@mozilla.org/widget/transferable;1");
|
||||
if (NS_WARN_IF(!trans)) {
|
||||
p->MaybeReject(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
callback = MakeRefPtr<ClipboardGetCallbackForRead>(global, std::move(p));
|
||||
rv = clipboardService->AsyncGetData(
|
||||
// Mandatory data types defined in
|
||||
// https://w3c.github.io/clipboard-apis/#mandatory-data-types-x
|
||||
AutoTArray<nsCString, 3>{nsDependentCString(kHTMLMime),
|
||||
nsDependentCString(kTextMime),
|
||||
nsDependentCString(kPNGImageMime)},
|
||||
nsIClipboard::kGlobalClipboard, callback);
|
||||
break;
|
||||
}
|
||||
case ReadRequestType::eReadText: {
|
||||
callback = MakeRefPtr<ClipboardGetCallbackForReadText>(std::move(p));
|
||||
rv = clipboardService->AsyncGetData(
|
||||
AutoTArray<nsCString, 1>{nsDependentCString(kTextMime)},
|
||||
nsIClipboard::kGlobalClipboard, callback);
|
||||
trans->Init(nullptr);
|
||||
trans->AddDataFlavor(kTextMime);
|
||||
clipboardService->AsyncGetData(trans, nsIClipboard::kGlobalClipboard)
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
/* resolve */
|
||||
[trans, p]() {
|
||||
nsCOMPtr<nsISupports> data;
|
||||
nsresult rv =
|
||||
trans->GetTransferData(kTextMime, getter_AddRefs(data));
|
||||
|
||||
nsAutoString str;
|
||||
if (!NS_WARN_IF(NS_FAILED(rv))) {
|
||||
nsCOMPtr<nsISupportsString> supportsstr =
|
||||
do_QueryInterface(data);
|
||||
MOZ_ASSERT(supportsstr);
|
||||
if (supportsstr) {
|
||||
supportsstr->GetData(str);
|
||||
}
|
||||
}
|
||||
|
||||
p->MaybeResolve(str);
|
||||
},
|
||||
/* reject */
|
||||
[p](nsresult rv) { p->MaybeReject(rv); });
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
|
@ -284,12 +180,6 @@ void Clipboard::ReadRequest::Answer() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_ASSERT(callback);
|
||||
callback->OnError(rv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsReadTextExposedToContent() {
|
||||
|
|
@ -865,8 +755,7 @@ already_AddRefed<Promise> Clipboard::WriteText(const nsAString& aData,
|
|||
|
||||
void Clipboard::ReadRequest::MaybeRejectWithNotAllowedError(
|
||||
const nsACString& aMessage) {
|
||||
RefPtr<Promise> p(std::move(mPromise));
|
||||
p->MaybeRejectWithNotAllowedError(aMessage);
|
||||
mPromise->MaybeRejectWithNotAllowedError(aMessage);
|
||||
}
|
||||
|
||||
void Clipboard::OnUserReactedToPasteMenuPopup(const bool aAllowed) {
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@ NS_IMPL_CYCLE_COLLECTION(ClipboardItem::ItemEntry, mGlobal, mData,
|
|||
mPendingGetTypeRequests)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ClipboardItem::ItemEntry)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIAsyncClipboardRequestCallback)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, PromiseNativeHandler)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(ClipboardItem::ItemEntry)
|
||||
|
|
@ -31,7 +30,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(ClipboardItem::ItemEntry)
|
|||
void ClipboardItem::ItemEntry::ResolvedCallback(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
ErrorResult& aRv) {
|
||||
MOZ_ASSERT(!mTransferable);
|
||||
MOZ_ASSERT(!mLoadingPromise.Exists());
|
||||
mIsLoadingData = false;
|
||||
OwningStringOrBlob clipboardData;
|
||||
if (!clipboardData.Init(aCx, aValue)) {
|
||||
|
|
@ -46,7 +45,7 @@ void ClipboardItem::ItemEntry::ResolvedCallback(JSContext* aCx,
|
|||
void ClipboardItem::ItemEntry::RejectedCallback(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
ErrorResult& aRv) {
|
||||
MOZ_ASSERT(!mTransferable);
|
||||
MOZ_ASSERT(!mLoadingPromise.Exists());
|
||||
mIsLoadingData = false;
|
||||
RejectPendingPromises(NS_ERROR_DOM_DATA_ERR);
|
||||
}
|
||||
|
|
@ -78,95 +77,94 @@ ClipboardItem::ItemEntry::GetData() {
|
|||
return GetDataPromise::CreateAndResolve(std::move(data), __func__);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP ClipboardItem::ItemEntry::OnComplete(nsresult aResult) {
|
||||
MOZ_ASSERT(mIsLoadingData);
|
||||
|
||||
mIsLoadingData = false;
|
||||
nsCOMPtr<nsITransferable> trans = std::move(mTransferable);
|
||||
|
||||
if (NS_FAILED(aResult)) {
|
||||
RejectPendingPromises(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(trans);
|
||||
nsCOMPtr<nsISupports> data;
|
||||
nsresult rv = trans->GetTransferData(NS_ConvertUTF16toUTF8(mType).get(),
|
||||
getter_AddRefs(data));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
RejectPendingPromises(rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<Blob> blob;
|
||||
if (nsCOMPtr<nsISupportsString> supportsstr = do_QueryInterface(data)) {
|
||||
nsAutoString str;
|
||||
supportsstr->GetData(str);
|
||||
|
||||
blob = Blob::CreateStringBlob(mGlobal, NS_ConvertUTF16toUTF8(str), mType);
|
||||
} else if (nsCOMPtr<nsIInputStream> istream = do_QueryInterface(data)) {
|
||||
uint64_t available;
|
||||
void* data = nullptr;
|
||||
rv = NS_ReadInputStreamToBuffer(istream, &data, -1, &available);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
RejectPendingPromises(rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
blob = Blob::CreateMemoryBlob(mGlobal, data, available, mType);
|
||||
} else if (nsCOMPtr<nsISupportsCString> supportscstr =
|
||||
do_QueryInterface(data)) {
|
||||
nsAutoCString str;
|
||||
supportscstr->GetData(str);
|
||||
|
||||
blob = Blob::CreateStringBlob(mGlobal, str, mType);
|
||||
}
|
||||
|
||||
if (!blob) {
|
||||
RejectPendingPromises(NS_ERROR_DOM_DATA_ERR);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
OwningStringOrBlob clipboardData;
|
||||
clipboardData.SetAsBlob() = std::move(blob);
|
||||
MaybeResolvePendingPromises(std::move(clipboardData));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void ClipboardItem::ItemEntry::LoadDataFromSystemClipboard(
|
||||
nsIAsyncGetClipboardData* aDataGetter) {
|
||||
MOZ_ASSERT(aDataGetter);
|
||||
nsITransferable& aTransferable) {
|
||||
// XXX maybe we could consider adding a method to check whether the union
|
||||
// object is uninitialized or initialized.
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mData.IsString() && !mData.IsBlob(),
|
||||
"Data should be uninitialized.");
|
||||
MOZ_DIAGNOSTIC_ASSERT(mLoadResult.isNothing(), "Should have no load result");
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mIsLoadingData && !mTransferable,
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mIsLoadingData && !mLoadingPromise.Exists(),
|
||||
"Should not be in the process of loading data");
|
||||
|
||||
mIsLoadingData = true;
|
||||
|
||||
mTransferable = do_CreateInstance("@mozilla.org/widget/transferable;1");
|
||||
if (NS_WARN_IF(!mTransferable)) {
|
||||
OnComplete(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
mTransferable->Init(nullptr);
|
||||
mTransferable->AddDataFlavor(NS_ConvertUTF16toUTF8(mType).get());
|
||||
|
||||
nsresult rv = aDataGetter->GetData(mTransferable, this);
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIClipboard> clipboard(
|
||||
do_GetService("@mozilla.org/widget/clipboard;1", &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
OnComplete(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
mIsLoadingData = true;
|
||||
nsCOMPtr<nsITransferable> trans(&aTransferable);
|
||||
clipboard->AsyncGetData(trans, nsIClipboard::kGlobalClipboard)
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
/* resolved */
|
||||
[self = RefPtr{this}, trans]() {
|
||||
self->mIsLoadingData = false;
|
||||
self->mLoadingPromise.Complete();
|
||||
|
||||
nsCOMPtr<nsISupports> data;
|
||||
nsresult rv = trans->GetTransferData(
|
||||
NS_ConvertUTF16toUTF8(self->Type()).get(),
|
||||
getter_AddRefs(data));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
self->RejectPendingPromises(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Blob> blob;
|
||||
if (nsCOMPtr<nsISupportsString> supportsstr =
|
||||
do_QueryInterface(data)) {
|
||||
nsAutoString str;
|
||||
supportsstr->GetData(str);
|
||||
|
||||
blob = Blob::CreateStringBlob(
|
||||
self->mGlobal, NS_ConvertUTF16toUTF8(str), self->Type());
|
||||
} else if (nsCOMPtr<nsIInputStream> istream =
|
||||
do_QueryInterface(data)) {
|
||||
uint64_t available;
|
||||
void* data = nullptr;
|
||||
nsresult rv =
|
||||
NS_ReadInputStreamToBuffer(istream, &data, -1, &available);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
self->RejectPendingPromises(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
blob = Blob::CreateMemoryBlob(self->mGlobal, data, available,
|
||||
self->Type());
|
||||
} else if (nsCOMPtr<nsISupportsCString> supportscstr =
|
||||
do_QueryInterface(data)) {
|
||||
nsAutoCString str;
|
||||
supportscstr->GetData(str);
|
||||
|
||||
blob = Blob::CreateStringBlob(self->mGlobal, str, self->Type());
|
||||
}
|
||||
|
||||
if (!blob) {
|
||||
self->RejectPendingPromises(NS_ERROR_DOM_DATA_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
OwningStringOrBlob clipboardData;
|
||||
clipboardData.SetAsBlob() = std::move(blob);
|
||||
self->MaybeResolvePendingPromises(std::move(clipboardData));
|
||||
},
|
||||
/* rejected */
|
||||
[self = RefPtr{this}](nsresult rv) {
|
||||
self->mIsLoadingData = false;
|
||||
self->mLoadingPromise.Complete();
|
||||
self->RejectPendingPromises(rv);
|
||||
})
|
||||
->Track(mLoadingPromise);
|
||||
}
|
||||
|
||||
void ClipboardItem::ItemEntry::LoadDataFromDataPromise(Promise& aDataPromise) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mData.IsString() && !mData.IsBlob(),
|
||||
"Data should be uninitialized");
|
||||
MOZ_DIAGNOSTIC_ASSERT(mLoadResult.isNothing(), "Should have no load result");
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mIsLoadingData && !mTransferable,
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mIsLoadingData && !mLoadingPromise.Exists(),
|
||||
"Should not be in the process of loading data");
|
||||
|
||||
mIsLoadingData = true;
|
||||
|
|
@ -223,7 +221,7 @@ void ClipboardItem::ItemEntry::RejectPendingPromises(nsresult aRv) {
|
|||
MOZ_DIAGNOSTIC_ASSERT(!mData.IsString() && !mData.IsBlob(),
|
||||
"Data should be uninitialized");
|
||||
MOZ_DIAGNOSTIC_ASSERT(mLoadResult.isNothing(), "Should not have load result");
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mIsLoadingData && !mTransferable,
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mIsLoadingData && !mLoadingPromise.Exists(),
|
||||
"Should not be in the process of loading data");
|
||||
mLoadResult.emplace(aRv);
|
||||
auto promiseHolders = std::move(mPendingGetDataRequests);
|
||||
|
|
@ -241,7 +239,7 @@ void ClipboardItem::ItemEntry::MaybeResolvePendingPromises(
|
|||
MOZ_DIAGNOSTIC_ASSERT(!mData.IsString() && !mData.IsBlob(),
|
||||
"Data should be uninitialized");
|
||||
MOZ_DIAGNOSTIC_ASSERT(mLoadResult.isNothing(), "Should not have load result");
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mIsLoadingData && !mTransferable,
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mIsLoadingData && !mLoadingPromise.Exists(),
|
||||
"Should not be in the process of loading data");
|
||||
mLoadResult.emplace(NS_OK);
|
||||
mData = std::move(aData);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@
|
|||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
|
||||
#include "nsIClipboard.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
class nsITransferable;
|
||||
|
|
@ -26,15 +25,13 @@ class Promise;
|
|||
|
||||
class ClipboardItem final : public nsWrapperCache {
|
||||
public:
|
||||
class ItemEntry final : public PromiseNativeHandler,
|
||||
public nsIAsyncClipboardRequestCallback {
|
||||
class ItemEntry final : public PromiseNativeHandler {
|
||||
public:
|
||||
using GetDataPromise =
|
||||
MozPromise<OwningStringOrBlob, nsresult, /* IsExclusive = */ true>;
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_NSIASYNCCLIPBOARDREQUESTCALLBACK
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ItemEntry, PromiseNativeHandler)
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(ItemEntry)
|
||||
|
||||
explicit ItemEntry(nsIGlobalObject* aGlobal, const nsAString& aType)
|
||||
: mGlobal(aGlobal), mType(aType) {
|
||||
|
|
@ -57,7 +54,7 @@ class ClipboardItem final : public nsWrapperCache {
|
|||
RefPtr<GetDataPromise> GetData();
|
||||
|
||||
// Load data from system clipboard.
|
||||
void LoadDataFromSystemClipboard(nsIAsyncGetClipboardData* aDataGetter);
|
||||
void LoadDataFromSystemClipboard(nsITransferable& aTransferable);
|
||||
void LoadDataFromDataPromise(Promise& aDataPromise);
|
||||
|
||||
// If clipboard data is in the process of loading from either system
|
||||
|
|
@ -68,6 +65,7 @@ class ClipboardItem final : public nsWrapperCache {
|
|||
|
||||
private:
|
||||
~ItemEntry() {
|
||||
mLoadingPromise.DisconnectIfExists();
|
||||
if (!mPendingGetDataRequests.IsEmpty()) {
|
||||
RejectPendingPromises(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
|
@ -89,7 +87,7 @@ class ClipboardItem final : public nsWrapperCache {
|
|||
|
||||
// Indicates if the data is still being loaded.
|
||||
bool mIsLoadingData = false;
|
||||
nsCOMPtr<nsITransferable> mTransferable;
|
||||
MozPromiseRequestHolder<GenericPromise> mLoadingPromise;
|
||||
|
||||
// Pending promises for data retrieval requests.
|
||||
nsTArray<MozPromiseHolder<GetDataPromise>> mPendingGetDataRequests;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
#include "imgLoader.h"
|
||||
#include "ScrollingMetrics.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/ClipboardReadRequestChild.h"
|
||||
#include "mozilla/Components.h"
|
||||
#include "mozilla/HangDetails.h"
|
||||
#include "mozilla/LoadInfo.h"
|
||||
|
|
@ -1988,12 +1987,6 @@ PRemotePrintJobChild* ContentChild::AllocPRemotePrintJobChild() {
|
|||
#endif
|
||||
}
|
||||
|
||||
already_AddRefed<PClipboardReadRequestChild>
|
||||
ContentChild::AllocPClipboardReadRequestChild(
|
||||
const nsTArray<nsCString>& aTypes) {
|
||||
return MakeAndAddRef<ClipboardReadRequestChild>(aTypes);
|
||||
}
|
||||
|
||||
media::PMediaChild* ContentChild::AllocPMediaChild() {
|
||||
return media::AllocPMediaChild();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -229,9 +229,6 @@ class ContentChild final : public PContentChild,
|
|||
|
||||
PRemotePrintJobChild* AllocPRemotePrintJobChild();
|
||||
|
||||
already_AddRefed<PClipboardReadRequestChild> AllocPClipboardReadRequestChild(
|
||||
const nsTArray<nsCString>& aTypes);
|
||||
|
||||
PMediaChild* AllocPMediaChild();
|
||||
|
||||
bool DeallocPMediaChild(PMediaChild* aActor);
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@
|
|||
#include "mozilla/BenchmarkStorageParent.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/ClipboardReadRequestParent.h"
|
||||
#include "mozilla/ClipboardWriteRequestParent.h"
|
||||
#include "mozilla/ContentBlockingUserInteraction.h"
|
||||
#include "mozilla/FOGIPC.h"
|
||||
|
|
@ -3564,6 +3563,26 @@ mozilla::ipc::IPCResult ContentParent::RecvClipboardHasType(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentParent::RecvClipboardHasTypesAsync(
|
||||
nsTArray<nsCString>&& aTypes, const int32_t& aWhichClipboard,
|
||||
ClipboardHasTypesAsyncResolver&& aResolver) {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
return IPC_FAIL(this, "RecvGetClipboardTypes failed.");
|
||||
}
|
||||
|
||||
clipboard->AsyncHasDataMatchingFlavors(aTypes, aWhichClipboard)
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
/* resolve */
|
||||
[aResolver](nsTArray<nsCString> types) { aResolver(types); },
|
||||
/* reject */
|
||||
[aResolver](nsresult rv) { aResolver(nsTArray<nsCString>{}); });
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentParent::RecvGetExternalClipboardFormats(
|
||||
const int32_t& aWhichClipboard, const bool& aPlainTextOnly,
|
||||
nsTArray<nsCString>* aTypes) {
|
||||
|
|
@ -3573,54 +3592,6 @@ mozilla::ipc::IPCResult ContentParent::RecvGetExternalClipboardFormats(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class ClipboardGetCallback final : public nsIAsyncClipboardGetCallback {
|
||||
public:
|
||||
ClipboardGetCallback(ContentParent* aContentParent,
|
||||
ContentParent::GetClipboardAsyncResolver&& aResolver)
|
||||
: mContentParent(aContentParent), mResolver(std::move(aResolver)) {}
|
||||
|
||||
// This object will never be held by a cycle-collected object, so it doesn't
|
||||
// need to be cycle-collected despite holding alive cycle-collected objects.
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIAsyncClipboardGetCallback
|
||||
NS_IMETHOD OnSuccess(
|
||||
nsIAsyncGetClipboardData* aAsyncGetClipboardData) override {
|
||||
nsTArray<nsCString> flavors;
|
||||
nsresult rv = aAsyncGetClipboardData->GetFlavorList(flavors);
|
||||
if (NS_FAILED(rv)) {
|
||||
return OnError(rv);
|
||||
}
|
||||
|
||||
auto requestParent = MakeNotNull<RefPtr<ClipboardReadRequestParent>>(
|
||||
mContentParent, aAsyncGetClipboardData);
|
||||
if (!mContentParent->SendPClipboardReadRequestConstructor(
|
||||
requestParent, std::move(flavors))) {
|
||||
return OnError(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
mResolver(PClipboardReadRequestOrError(requestParent));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD OnError(nsresult aResult) override {
|
||||
mResolver(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
~ClipboardGetCallback() = default;
|
||||
|
||||
RefPtr<ContentParent> mContentParent;
|
||||
ContentParent::GetClipboardAsyncResolver mResolver;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(ClipboardGetCallback, nsIAsyncClipboardGetCallback)
|
||||
|
||||
} // namespace
|
||||
|
||||
mozilla::ipc::IPCResult ContentParent::RecvGetClipboardAsync(
|
||||
nsTArray<nsCString>&& aTypes, const int32_t& aWhichClipboard,
|
||||
GetClipboardAsyncResolver&& aResolver) {
|
||||
|
|
@ -3632,13 +3603,25 @@ mozilla::ipc::IPCResult ContentParent::RecvGetClipboardAsync(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
auto callback = MakeRefPtr<ClipboardGetCallback>(this, std::move(aResolver));
|
||||
rv = clipboard->AsyncGetData(aTypes, aWhichClipboard, callback);
|
||||
if (NS_FAILED(rv)) {
|
||||
callback->OnError(rv);
|
||||
// Create transferable
|
||||
auto result = CreateTransferable(aTypes);
|
||||
if (result.isErr()) {
|
||||
aResolver(result.unwrapErr());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Get data from clipboard
|
||||
nsCOMPtr<nsITransferable> trans = result.unwrap();
|
||||
clipboard->AsyncGetData(trans, nsIClipboard::kGlobalClipboard)
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
[trans, aResolver,
|
||||
self = RefPtr{this}](GenericPromise::ResolveOrRejectValue&& aValue) {
|
||||
IPCTransferableData ipcTransferableData;
|
||||
nsContentUtils::TransferableToIPCTransferableData(
|
||||
trans, &ipcTransferableData, false /* aInSyncMessage */, self);
|
||||
aResolver(std::move(ipcTransferableData));
|
||||
});
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -987,6 +987,10 @@ class ContentParent final : public PContentParent,
|
|||
const int32_t& aWhichClipboard,
|
||||
bool* aHasType);
|
||||
|
||||
mozilla::ipc::IPCResult RecvClipboardHasTypesAsync(
|
||||
nsTArray<nsCString>&& aTypes, const int32_t& aWhichClipboard,
|
||||
ClipboardHasTypesAsyncResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvGetExternalClipboardFormats(
|
||||
const int32_t& aWhichClipboard, const bool& aPlainTextOnly,
|
||||
nsTArray<nsCString>* aTypes);
|
||||
|
|
|
|||
|
|
@ -74,11 +74,6 @@ struct IPCTransferableData
|
|||
IPCTransferableDataItem[] items;
|
||||
};
|
||||
|
||||
union IPCTransferableDataOrError {
|
||||
IPCTransferableData;
|
||||
nsresult;
|
||||
};
|
||||
|
||||
struct IPCTransferable
|
||||
{
|
||||
IPCTransferableData data;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
include protocol PBackgroundStarter;
|
||||
include protocol PBrowser;
|
||||
include protocol PClipboardReadRequest;
|
||||
include protocol PClipboardWriteRequest;
|
||||
include protocol PCompositorManager;
|
||||
include protocol PContentPermissionRequest;
|
||||
|
|
@ -462,8 +461,8 @@ struct IPCImage {
|
|||
ImageIntSize size;
|
||||
};
|
||||
|
||||
union PClipboardReadRequestOrError {
|
||||
PClipboardReadRequest;
|
||||
union IPCTransferableDataOrError {
|
||||
IPCTransferableData;
|
||||
nsresult;
|
||||
};
|
||||
|
||||
|
|
@ -476,7 +475,6 @@ union PClipboardReadRequestOrError {
|
|||
sync protocol PContent
|
||||
{
|
||||
manages PBrowser;
|
||||
manages PClipboardReadRequest;
|
||||
manages PClipboardWriteRequest;
|
||||
manages PContentPermissionRequest;
|
||||
manages PCycleCollectWithLogs;
|
||||
|
|
@ -1045,11 +1043,10 @@ child:
|
|||
// details.
|
||||
async InitNextGenLocalStorageEnabled(bool enabled);
|
||||
|
||||
async PRemotePrintJob();
|
||||
|
||||
async PClipboardReadRequest(nsCString[] aTypes);
|
||||
async PRemotePrintJob();
|
||||
|
||||
parent:
|
||||
|
||||
async SynchronizeLayoutHistoryState(MaybeDiscardedBrowsingContext aContext,
|
||||
nullable nsILayoutHistoryState aState);
|
||||
|
||||
|
|
@ -1219,9 +1216,9 @@ parent:
|
|||
// Returns a list of formats supported by the clipboard
|
||||
sync GetExternalClipboardFormats(int32_t aWhichClipboard, bool aPlainTextOnly) returns (nsCString[] aTypes);
|
||||
|
||||
// Requests getting data from clipboard.
|
||||
async GetClipboardAsync(nsCString[] aTypes, int32_t aWhichClipboard)
|
||||
returns (PClipboardReadRequestOrError aClipboardReadRequest);
|
||||
// Given a list of supported types, returns the clipboard data for the
|
||||
// first type that matches.
|
||||
async GetClipboardAsync(nsCString[] aTypes, int32_t aWhichClipboard) returns (IPCTransferableDataOrError transferableData);
|
||||
|
||||
// Clears the clipboard.
|
||||
async EmptyClipboard(int32_t aWhichClipboard);
|
||||
|
|
@ -1230,6 +1227,11 @@ parent:
|
|||
sync ClipboardHasType(nsCString[] aTypes, int32_t aWhichClipboard)
|
||||
returns (bool hasType);
|
||||
|
||||
// Given a list of supported types, returns a list of types that clipboard
|
||||
// constains the data for the specified type.
|
||||
async ClipboardHasTypesAsync(nsCString [] aTypes, int32_t aWhichClipboard)
|
||||
returns (nsCString [] types);
|
||||
|
||||
/**
|
||||
* Notify the parent that the child has started a clipboard write request,
|
||||
* and that the data will be sent over another IPC message once it is ready.
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_ClipboardReadRequestChild_h
|
||||
#define mozilla_ClipboardReadRequestChild_h
|
||||
|
||||
#include "mozilla/PClipboardReadRequestChild.h"
|
||||
|
||||
class nsITransferable;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ClipboardReadRequestChild final : public PClipboardReadRequestChild {
|
||||
public:
|
||||
explicit ClipboardReadRequestChild(const nsTArray<nsCString>& aFlavorList) {
|
||||
mFlavorList.AppendElements(aFlavorList);
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(ClipboardReadRequestChild)
|
||||
|
||||
const nsTArray<nsCString>& FlavorList() const { return mFlavorList; }
|
||||
|
||||
protected:
|
||||
virtual ~ClipboardReadRequestChild() = default;
|
||||
|
||||
private:
|
||||
nsTArray<nsCString> mFlavorList;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_ClipboardReadRequestChild_h
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/ClipboardReadRequestParent.h"
|
||||
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/net/CookieJarSettings.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsIClipboard.h"
|
||||
#include "nsITransferable.h"
|
||||
#include "nsWidgetsCID.h"
|
||||
|
||||
using mozilla::dom::ContentParent;
|
||||
using mozilla::ipc::IPCResult;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace {
|
||||
|
||||
class ClipboardGetDataCallback final : public nsIAsyncClipboardRequestCallback {
|
||||
public:
|
||||
explicit ClipboardGetDataCallback(std::function<void(nsresult)>&& aCallback)
|
||||
: mCallback(std::move(aCallback)) {}
|
||||
|
||||
// This object will never be held by a cycle-collected object, so it doesn't
|
||||
// need to be cycle-collected despite holding alive cycle-collected objects.
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIAsyncClipboardRequestCallback
|
||||
NS_IMETHOD OnComplete(nsresult aResult) override {
|
||||
mCallback(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
~ClipboardGetDataCallback() = default;
|
||||
|
||||
std::function<void(nsresult)> mCallback;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(ClipboardGetDataCallback, nsIAsyncClipboardRequestCallback)
|
||||
|
||||
static Result<nsCOMPtr<nsITransferable>, nsresult> CreateTransferable(
|
||||
const nsTArray<nsCString>& aTypes) {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsITransferable> trans =
|
||||
do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return Err(rv);
|
||||
}
|
||||
|
||||
MOZ_TRY(trans->Init(nullptr));
|
||||
// The private flag is only used to prevent the data from being cached to the
|
||||
// disk. The flag is not exported to the IPCDataTransfer object.
|
||||
// The flag is set because we are not sure whether the clipboard data is used
|
||||
// in a private browsing context. The transferable is only used in this scope,
|
||||
// so the cache would not reduce memory consumption anyway.
|
||||
trans->SetIsPrivateData(true);
|
||||
// Fill out flavors for transferable
|
||||
for (uint32_t t = 0; t < aTypes.Length(); t++) {
|
||||
MOZ_TRY(trans->AddDataFlavor(aTypes[t].get()));
|
||||
}
|
||||
|
||||
return std::move(trans);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
IPCResult ClipboardReadRequestParent::RecvGetData(
|
||||
const nsTArray<nsCString>& aFlavors, GetDataResolver&& aResolver) {
|
||||
bool valid = false;
|
||||
if (NS_FAILED(mAsyncGetClipboardData->GetValid(&valid)) || !valid) {
|
||||
Unused << PClipboardReadRequestParent::Send__delete__(this);
|
||||
aResolver(NS_ERROR_FAILURE);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Create transferable
|
||||
auto result = CreateTransferable(aFlavors);
|
||||
if (result.isErr()) {
|
||||
aResolver(result.unwrapErr());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITransferable> trans = result.unwrap();
|
||||
RefPtr<ClipboardGetDataCallback> callback =
|
||||
MakeRefPtr<ClipboardGetDataCallback>([self = RefPtr{this},
|
||||
resolver = std::move(aResolver),
|
||||
trans,
|
||||
manager = mManager](nsresult aRv) {
|
||||
if (NS_FAILED(aRv)) {
|
||||
bool valid = false;
|
||||
if (NS_FAILED(self->mAsyncGetClipboardData->GetValid(&valid)) ||
|
||||
!valid) {
|
||||
Unused << PClipboardReadRequestParent::Send__delete__(self);
|
||||
}
|
||||
resolver(aRv);
|
||||
return;
|
||||
}
|
||||
|
||||
dom::IPCTransferableData ipcTransferableData;
|
||||
nsContentUtils::TransferableToIPCTransferableData(
|
||||
trans, &ipcTransferableData, false /* aInSyncMessage */, manager);
|
||||
resolver(std::move(ipcTransferableData));
|
||||
});
|
||||
nsresult rv = mAsyncGetClipboardData->GetData(trans, callback);
|
||||
if (NS_FAILED(rv)) {
|
||||
callback->OnComplete(rv);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_ClipboardReadRequestParent_h
|
||||
#define mozilla_ClipboardReadRequestParent_h
|
||||
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/PClipboardReadRequestParent.h"
|
||||
#include "nsIClipboard.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ClipboardReadRequestParent final : public PClipboardReadRequestParent {
|
||||
using IPCResult = mozilla::ipc::IPCResult;
|
||||
using ContentParent = mozilla::dom::ContentParent;
|
||||
|
||||
public:
|
||||
ClipboardReadRequestParent(ContentParent* aManager,
|
||||
nsIAsyncGetClipboardData* aAsyncGetClipboardData)
|
||||
: mManager(aManager), mAsyncGetClipboardData(aAsyncGetClipboardData) {}
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(ClipboardReadRequestParent, override)
|
||||
|
||||
// PClipboardReadRequestParent
|
||||
IPCResult RecvGetData(const nsTArray<nsCString>& aFlavors,
|
||||
GetDataResolver&& aResolver);
|
||||
|
||||
private:
|
||||
~ClipboardReadRequestParent() = default;
|
||||
|
||||
RefPtr<ContentParent> mManager;
|
||||
nsCOMPtr<nsIAsyncGetClipboardData> mAsyncGetClipboardData;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_ClipboardReadRequestParent_h
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
include protocol PContent;
|
||||
|
||||
include DOMTypes;
|
||||
include IPCTransferable;
|
||||
include NeckoChannelParams;
|
||||
|
||||
using nsContentPolicyType from "nsIContentPolicy.h";
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
protocol PClipboardReadRequest {
|
||||
manager PContent;
|
||||
|
||||
parent:
|
||||
async GetData(nsCString[] aFlavors) returns (IPCTransferableDataOrError aTransferableData);
|
||||
|
||||
both:
|
||||
async __delete__();
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
@ -166,8 +166,6 @@ EXPORTS += [
|
|||
|
||||
EXPORTS.mozilla += [
|
||||
"BasicEvents.h",
|
||||
"ClipboardReadRequestChild.h",
|
||||
"ClipboardReadRequestParent.h",
|
||||
"ClipboardWriteRequestChild.h",
|
||||
"ClipboardWriteRequestParent.h",
|
||||
"ColorScheme.h",
|
||||
|
|
@ -217,7 +215,6 @@ EXPORTS.mozilla.widget += [
|
|||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
"ClipboardReadRequestParent.cpp",
|
||||
"ClipboardWriteRequestChild.cpp",
|
||||
"ClipboardWriteRequestParent.cpp",
|
||||
"CompositorWidget.cpp",
|
||||
|
|
@ -377,7 +374,6 @@ else:
|
|||
|
||||
IPDL_SOURCES += [
|
||||
"LookAndFeelTypes.ipdlh",
|
||||
"PClipboardReadRequest.ipdl",
|
||||
"PClipboardWriteRequest.ipdl",
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@ using mozilla::LogLevel;
|
|||
using mozilla::UniquePtr;
|
||||
using mozilla::dom::ClipboardCapabilities;
|
||||
|
||||
static const int32_t kGetAvailableFlavorsRetryCount = 5;
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsBaseClipboard::AsyncSetClipboardData,
|
||||
nsIAsyncSetClipboardData)
|
||||
|
||||
|
|
@ -225,7 +223,29 @@ nsresult nsBaseClipboard::GetDataFromClipboardCache(
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return clipboardCache->GetData(aTransferable);
|
||||
nsITransferable* cachedTransferable = clipboardCache->GetTransferable();
|
||||
MOZ_ASSERT(cachedTransferable);
|
||||
|
||||
// get flavor list that includes all acceptable flavors (including ones
|
||||
// obtained through conversion)
|
||||
nsTArray<nsCString> flavors;
|
||||
if (NS_FAILED(aTransferable->FlavorsTransferableCanImport(flavors))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
for (const auto& flavor : flavors) {
|
||||
nsCOMPtr<nsISupports> dataSupports;
|
||||
if (NS_SUCCEEDED(cachedTransferable->GetTransferData(
|
||||
flavor.get(), getter_AddRefs(dataSupports)))) {
|
||||
MOZ_CLIPBOARD_LOG("%s: getting %s from cache.", __FUNCTION__,
|
||||
flavor.get());
|
||||
aTransferable->SetTransferData(flavor.get(), dataSupports);
|
||||
// maybe try to fill in more types? Is there a point?
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -257,124 +277,41 @@ NS_IMETHODIMP nsBaseClipboard::GetData(nsITransferable* aTransferable,
|
|||
return GetNativeClipboardData(aTransferable, aWhichClipboard);
|
||||
}
|
||||
|
||||
void nsBaseClipboard::MaybeRetryGetAvailableFlavors(
|
||||
const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard,
|
||||
nsIAsyncClipboardGetCallback* aCallback, int32_t aRetryCount) {
|
||||
// Note we have to get the clipboard sequence number first before the actual
|
||||
// read. This is to use it to verify the clipboard data is still the one we
|
||||
// try to read, instead of the later state.
|
||||
auto sequenceNumberOrError =
|
||||
GetNativeClipboardSequenceNumber(aWhichClipboard);
|
||||
if (sequenceNumberOrError.isErr()) {
|
||||
MOZ_CLIPBOARD_LOG("%s: unable to get sequence number for clipboard %d.",
|
||||
__FUNCTION__, aWhichClipboard);
|
||||
aCallback->OnError(sequenceNumberOrError.unwrapErr());
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t sequenceNumber = sequenceNumberOrError.unwrap();
|
||||
AsyncHasNativeClipboardDataMatchingFlavors(
|
||||
aFlavorList, aWhichClipboard,
|
||||
[self = RefPtr{this}, callback = nsCOMPtr{aCallback}, aWhichClipboard,
|
||||
aRetryCount, flavorList = aFlavorList.Clone(),
|
||||
sequenceNumber](auto aFlavorsOrError) {
|
||||
if (aFlavorsOrError.isErr()) {
|
||||
MOZ_CLIPBOARD_LOG(
|
||||
"%s: unable to get available flavors for clipboard %d.",
|
||||
__FUNCTION__, aWhichClipboard);
|
||||
callback->OnError(aFlavorsOrError.unwrapErr());
|
||||
return;
|
||||
}
|
||||
|
||||
auto sequenceNumberOrError =
|
||||
self->GetNativeClipboardSequenceNumber(aWhichClipboard);
|
||||
if (sequenceNumberOrError.isErr()) {
|
||||
MOZ_CLIPBOARD_LOG(
|
||||
"%s: unable to get sequence number for clipboard %d.",
|
||||
__FUNCTION__, aWhichClipboard);
|
||||
callback->OnError(sequenceNumberOrError.unwrapErr());
|
||||
return;
|
||||
}
|
||||
|
||||
if (sequenceNumber == sequenceNumberOrError.unwrap()) {
|
||||
auto asyncGetClipboardData =
|
||||
mozilla::MakeRefPtr<AsyncGetClipboardData>(
|
||||
aWhichClipboard, sequenceNumber,
|
||||
std::move(aFlavorsOrError.unwrap()), false, self);
|
||||
callback->OnSuccess(asyncGetClipboardData);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aRetryCount > 0) {
|
||||
MOZ_CLIPBOARD_LOG(
|
||||
"%s: clipboard=%d, ignore the data due to the sequence number "
|
||||
"doesn't match, retry (%d) ..",
|
||||
__FUNCTION__, aWhichClipboard, aRetryCount);
|
||||
self->MaybeRetryGetAvailableFlavors(flavorList, aWhichClipboard,
|
||||
callback, aRetryCount - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(false, "How can this happen?!?");
|
||||
callback->OnError(NS_ERROR_FAILURE);
|
||||
});
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsBaseClipboard::AsyncGetData(
|
||||
const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard,
|
||||
nsIAsyncClipboardGetCallback* aCallback) {
|
||||
RefPtr<GenericPromise> nsBaseClipboard::AsyncGetData(
|
||||
nsITransferable* aTransferable, int32_t aWhichClipboard) {
|
||||
MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard);
|
||||
|
||||
if (!aCallback || aFlavorList.IsEmpty()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)) {
|
||||
MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__,
|
||||
aWhichClipboard);
|
||||
return NS_ERROR_FAILURE;
|
||||
if (!aTransferable) {
|
||||
NS_ASSERTION(false, "clipboard given a null transferable");
|
||||
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||
}
|
||||
|
||||
if (mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) {
|
||||
// If we were the last ones to put something on the navtive clipboard, then
|
||||
// If we were the last ones to put something on the native clipboard, then
|
||||
// just use the cached transferable. Otherwise clear it because it isn't
|
||||
// relevant any more.
|
||||
if (auto* clipboardCache = GetClipboardCacheIfValid(aWhichClipboard)) {
|
||||
nsITransferable* cachedTransferable = clipboardCache->GetTransferable();
|
||||
MOZ_ASSERT(cachedTransferable);
|
||||
|
||||
nsTArray<nsCString> transferableFlavors;
|
||||
if (NS_SUCCEEDED(cachedTransferable->FlavorsTransferableCanExport(
|
||||
transferableFlavors))) {
|
||||
nsTArray<nsCString> results;
|
||||
for (const auto& transferableFlavor : transferableFlavors) {
|
||||
for (const auto& flavor : aFlavorList) {
|
||||
// XXX We need special check for image as we always put the image as
|
||||
// "native" on the clipboard.
|
||||
if (transferableFlavor.Equals(flavor) ||
|
||||
(transferableFlavor.Equals(kNativeImageMime) &&
|
||||
nsContentUtils::IsFlavorImage(flavor))) {
|
||||
MOZ_CLIPBOARD_LOG(" has %s", flavor.get());
|
||||
results.AppendElement(flavor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto asyncGetClipboardData = mozilla::MakeRefPtr<AsyncGetClipboardData>(
|
||||
aWhichClipboard, clipboardCache->GetSequenceNumber(),
|
||||
std::move(results), true, this);
|
||||
aCallback->OnSuccess(asyncGetClipboardData);
|
||||
return NS_OK;
|
||||
}
|
||||
if (NS_SUCCEEDED(
|
||||
GetDataFromClipboardCache(aTransferable, aWhichClipboard))) {
|
||||
// maybe try to fill in more types? Is there a point?
|
||||
return GenericPromise::CreateAndResolve(true, __func__);
|
||||
}
|
||||
|
||||
// At this point we can't satisfy the request from cache data so let's look
|
||||
// for things other people put on the system clipboard.
|
||||
// at this point we can't satisfy the request from cache data so let's look
|
||||
// for things other people put on the system clipboard
|
||||
}
|
||||
|
||||
MaybeRetryGetAvailableFlavors(aFlavorList, aWhichClipboard, aCallback,
|
||||
kGetAvailableFlavorsRetryCount);
|
||||
return NS_OK;
|
||||
RefPtr<GenericPromise::Private> dataPromise =
|
||||
mozilla::MakeRefPtr<GenericPromise::Private>(__func__);
|
||||
AsyncGetNativeClipboardData(aTransferable, aWhichClipboard,
|
||||
[dataPromise](nsresult aResult) {
|
||||
if (NS_FAILED(aResult)) {
|
||||
dataPromise->Reject(aResult, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
dataPromise->Resolve(true, __func__);
|
||||
});
|
||||
return dataPromise.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsBaseClipboard::EmptyClipboard(int32_t aWhichClipboard) {
|
||||
|
|
@ -478,6 +415,52 @@ nsBaseClipboard::HasDataMatchingFlavors(const nsTArray<nsCString>& aFlavorList,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<DataFlavorsPromise> nsBaseClipboard::AsyncHasDataMatchingFlavors(
|
||||
const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard) {
|
||||
MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard);
|
||||
if (MOZ_CLIPBOARD_LOG_ENABLED()) {
|
||||
MOZ_CLIPBOARD_LOG(" Asking for content clipboard=%i:\n",
|
||||
aWhichClipboard);
|
||||
for (const auto& flavor : aFlavorList) {
|
||||
MOZ_CLIPBOARD_LOG(" MIME %s", flavor.get());
|
||||
}
|
||||
}
|
||||
|
||||
if (mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) {
|
||||
// First, check if we have valid data in our cached transferable.
|
||||
auto flavorsOrError = GetFlavorsFromClipboardCache(aWhichClipboard);
|
||||
if (flavorsOrError.isOk()) {
|
||||
nsTArray<nsCString> results;
|
||||
for (const auto& transferableFlavor : flavorsOrError.unwrap()) {
|
||||
for (const auto& flavor : aFlavorList) {
|
||||
// XXX We need special check for image as we always put the image as
|
||||
// "native" on the clipboard.
|
||||
if (transferableFlavor.Equals(flavor) ||
|
||||
(transferableFlavor.Equals(kNativeImageMime) &&
|
||||
nsContentUtils::IsFlavorImage(flavor))) {
|
||||
MOZ_CLIPBOARD_LOG(" has %s", flavor.get());
|
||||
results.AppendElement(flavor);
|
||||
}
|
||||
}
|
||||
}
|
||||
return DataFlavorsPromise::CreateAndResolve(std::move(results), __func__);
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<DataFlavorsPromise::Private> flavorPromise =
|
||||
mozilla::MakeRefPtr<DataFlavorsPromise::Private>(__func__);
|
||||
AsyncHasNativeClipboardDataMatchingFlavors(
|
||||
aFlavorList, aWhichClipboard, [flavorPromise](auto aResultOrError) {
|
||||
if (aResultOrError.isErr()) {
|
||||
flavorPromise->Reject(aResultOrError.unwrapErr(), __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
flavorPromise->Resolve(std::move(aResultOrError.unwrap()), __func__);
|
||||
});
|
||||
return flavorPromise.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsBaseClipboard::IsClipboardTypeSupported(int32_t aWhichClipboard,
|
||||
bool* aRetval) {
|
||||
|
|
@ -537,121 +520,6 @@ void nsBaseClipboard::ClearClipboardCache(int32_t aClipboardType) {
|
|||
cache->Clear();
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsBaseClipboard::AsyncGetClipboardData,
|
||||
nsIAsyncGetClipboardData)
|
||||
|
||||
nsBaseClipboard::AsyncGetClipboardData::AsyncGetClipboardData(
|
||||
int32_t aClipboardType, int32_t aSequenceNumber,
|
||||
nsTArray<nsCString>&& aFlavors, bool aFromCache,
|
||||
nsBaseClipboard* aClipboard)
|
||||
: mClipboardType(aClipboardType),
|
||||
mSequenceNumber(aSequenceNumber),
|
||||
mFlavors(std::move(aFlavors)),
|
||||
mFromCache(aFromCache),
|
||||
mClipboard(aClipboard) {
|
||||
MOZ_ASSERT(mClipboard);
|
||||
MOZ_ASSERT(
|
||||
mClipboard->nsIClipboard::IsClipboardTypeSupported(mClipboardType));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsBaseClipboard::AsyncGetClipboardData::GetValid(
|
||||
bool* aOutResult) {
|
||||
*aOutResult = IsValid();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsBaseClipboard::AsyncGetClipboardData::GetFlavorList(
|
||||
nsTArray<nsCString>& aFlavors) {
|
||||
aFlavors.AppendElements(mFlavors);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsBaseClipboard::AsyncGetClipboardData::GetData(
|
||||
nsITransferable* aTransferable,
|
||||
nsIAsyncClipboardRequestCallback* aCallback) {
|
||||
MOZ_CLIPBOARD_LOG("AsyncGetClipboardData::GetData: %p", this);
|
||||
|
||||
if (!aTransferable || !aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsTArray<nsCString> flavors;
|
||||
nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// If the requested flavor is not in the list, throw an error.
|
||||
for (const auto& flavor : flavors) {
|
||||
if (!mFlavors.Contains(flavor)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsValid()) {
|
||||
aCallback->OnComplete(NS_ERROR_FAILURE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mClipboard);
|
||||
|
||||
if (mFromCache) {
|
||||
const auto* clipboardCache =
|
||||
mClipboard->GetClipboardCacheIfValid(mClipboardType);
|
||||
// `IsValid()` above ensures we should get a valid cache and matched
|
||||
// sequence number here.
|
||||
MOZ_DIAGNOSTIC_ASSERT(clipboardCache);
|
||||
MOZ_DIAGNOSTIC_ASSERT(clipboardCache->GetSequenceNumber() ==
|
||||
mSequenceNumber);
|
||||
aCallback->OnComplete(clipboardCache->GetData(aTransferable));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Since this is an async operation, we need to check if the data is still
|
||||
// valid after we get the result.
|
||||
mClipboard->AsyncGetNativeClipboardData(
|
||||
aTransferable, mClipboardType,
|
||||
[callback = nsCOMPtr{aCallback}, self = RefPtr{this}](nsresult aResult) {
|
||||
// `IsValid()` checks the clipboard sequence number to ensure the data
|
||||
// we are requesting is still valid.
|
||||
callback->OnComplete(self->IsValid() ? aResult : NS_ERROR_FAILURE);
|
||||
});
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool nsBaseClipboard::AsyncGetClipboardData::IsValid() {
|
||||
if (!mClipboard) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the data should from cache, check if cache is still valid or the
|
||||
// sequence numbers are matched.
|
||||
if (mFromCache) {
|
||||
const auto* clipboardCache =
|
||||
mClipboard->GetClipboardCacheIfValid(mClipboardType);
|
||||
if (!clipboardCache) {
|
||||
mClipboard = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return mSequenceNumber == clipboardCache->GetSequenceNumber();
|
||||
}
|
||||
|
||||
auto resultOrError =
|
||||
mClipboard->GetNativeClipboardSequenceNumber(mClipboardType);
|
||||
if (resultOrError.isErr()) {
|
||||
mClipboard = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mSequenceNumber != resultOrError.unwrap()) {
|
||||
mClipboard = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsBaseClipboard::ClipboardCache* nsBaseClipboard::GetClipboardCacheIfValid(
|
||||
int32_t aClipboardType) {
|
||||
MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType));
|
||||
|
|
@ -686,32 +554,3 @@ void nsBaseClipboard::ClipboardCache::Clear() {
|
|||
mTransferable = nullptr;
|
||||
mSequenceNumber = -1;
|
||||
}
|
||||
|
||||
nsresult nsBaseClipboard::ClipboardCache::GetData(
|
||||
nsITransferable* aTransferable) const {
|
||||
MOZ_ASSERT(aTransferable);
|
||||
MOZ_ASSERT(mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled());
|
||||
|
||||
// get flavor list that includes all acceptable flavors (including ones
|
||||
// obtained through conversion)
|
||||
nsTArray<nsCString> flavors;
|
||||
if (NS_FAILED(aTransferable->FlavorsTransferableCanImport(flavors))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mTransferable);
|
||||
for (const auto& flavor : flavors) {
|
||||
nsCOMPtr<nsISupports> dataSupports;
|
||||
if (NS_SUCCEEDED(mTransferable->GetTransferData(
|
||||
flavor.get(), getter_AddRefs(dataSupports)))) {
|
||||
MOZ_CLIPBOARD_LOG("%s: getting %s from cache.", __FUNCTION__,
|
||||
flavor.get());
|
||||
aTransferable->SetTransferData(flavor.get(), dataSupports);
|
||||
// XXX we only read the first available type from native clipboard, so
|
||||
// make cache behave the same.
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,15 +44,17 @@ class nsBaseClipboard : public nsIClipboard {
|
|||
nsIAsyncSetClipboardData** _retval) override final;
|
||||
NS_IMETHOD GetData(nsITransferable* aTransferable,
|
||||
int32_t aWhichClipboard) override final;
|
||||
NS_IMETHOD AsyncGetData(
|
||||
const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard,
|
||||
nsIAsyncClipboardGetCallback* aCallback) override final;
|
||||
NS_IMETHOD EmptyClipboard(int32_t aWhichClipboard) override final;
|
||||
NS_IMETHOD HasDataMatchingFlavors(const nsTArray<nsCString>& aFlavorList,
|
||||
int32_t aWhichClipboard,
|
||||
bool* aOutResult) override final;
|
||||
NS_IMETHOD IsClipboardTypeSupported(int32_t aWhichClipboard,
|
||||
bool* aRetval) override final;
|
||||
RefPtr<mozilla::GenericPromise> AsyncGetData(
|
||||
nsITransferable* aTransferable, int32_t aWhichClipboard) override final;
|
||||
RefPtr<DataFlavorsPromise> AsyncHasDataMatchingFlavors(
|
||||
const nsTArray<nsCString>& aFlavorList,
|
||||
int32_t aWhichClipboard) override final;
|
||||
|
||||
using GetDataCallback = mozilla::MoveOnlyFunction<void(nsresult)>;
|
||||
using HasMatchingFlavorsCallback = mozilla::MoveOnlyFunction<void(
|
||||
|
|
@ -111,33 +113,6 @@ class nsBaseClipboard : public nsIClipboard {
|
|||
nsCOMPtr<nsIAsyncClipboardRequestCallback> mCallback;
|
||||
};
|
||||
|
||||
class AsyncGetClipboardData final : public nsIAsyncGetClipboardData {
|
||||
public:
|
||||
AsyncGetClipboardData(int32_t aClipboardType, int32_t aSequenceNumber,
|
||||
nsTArray<nsCString>&& aFlavors, bool aFromCache,
|
||||
nsBaseClipboard* aClipboard);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIASYNCGETCLIPBOARDDATA
|
||||
|
||||
private:
|
||||
virtual ~AsyncGetClipboardData() = default;
|
||||
bool IsValid();
|
||||
|
||||
// The clipboard type defined in nsIClipboard.
|
||||
const int32_t mClipboardType;
|
||||
// The sequence number associated with the clipboard content for this
|
||||
// request. If it doesn't match with the current sequence number in system
|
||||
// clipboard, this request targets stale data and is deemed invalid.
|
||||
const int32_t mSequenceNumber;
|
||||
// List of available data types for clipboard content.
|
||||
const nsTArray<nsCString> mFlavors;
|
||||
// Data should be read from cache.
|
||||
const bool mFromCache;
|
||||
// This is also used to indicate whether this request is still valid.
|
||||
RefPtr<nsBaseClipboard> mClipboard;
|
||||
};
|
||||
|
||||
class ClipboardCache final {
|
||||
public:
|
||||
~ClipboardCache() {
|
||||
|
|
@ -161,7 +136,6 @@ class nsBaseClipboard : public nsIClipboard {
|
|||
nsITransferable* GetTransferable() const { return mTransferable; }
|
||||
nsIClipboardOwner* GetClipboardOwner() const { return mClipboardOwner; }
|
||||
int32_t GetSequenceNumber() const { return mSequenceNumber; }
|
||||
nsresult GetData(nsITransferable* aTransferable) const;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsITransferable> mTransferable;
|
||||
|
|
@ -169,11 +143,6 @@ class nsBaseClipboard : public nsIClipboard {
|
|||
int32_t mSequenceNumber = -1;
|
||||
};
|
||||
|
||||
void MaybeRetryGetAvailableFlavors(const nsTArray<nsCString>& aFlavorList,
|
||||
int32_t aWhichClipboard,
|
||||
nsIAsyncClipboardGetCallback* aCallback,
|
||||
int32_t aRetryCount);
|
||||
|
||||
// Return clipboard cache if the cached data is valid, otherwise clear the
|
||||
// cached data and returns null.
|
||||
ClipboardCache* GetClipboardCacheIfValid(int32_t aClipboardType);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#if defined(ACCESSIBILITY) && defined(XP_WIN)
|
||||
# include "mozilla/a11y/Compatibility.h"
|
||||
#endif
|
||||
#include "mozilla/ClipboardReadRequestChild.h"
|
||||
#include "mozilla/ClipboardWriteRequestChild.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/net/CookieJarSettings.h"
|
||||
|
|
@ -68,149 +67,6 @@ nsClipboardProxy::GetData(nsITransferable* aTransferable,
|
|||
false /* aFilterUnknownFlavors */);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class AsyncGetClipboardDataProxy final : public nsIAsyncGetClipboardData {
|
||||
public:
|
||||
explicit AsyncGetClipboardDataProxy(ClipboardReadRequestChild* aActor)
|
||||
: mActor(aActor) {
|
||||
MOZ_ASSERT(mActor);
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIASYNCGETCLIPBOARDDATA
|
||||
|
||||
private:
|
||||
virtual ~AsyncGetClipboardDataProxy() {
|
||||
MOZ_ASSERT(mActor);
|
||||
if (mActor->CanSend()) {
|
||||
PClipboardReadRequestChild::Send__delete__(mActor);
|
||||
}
|
||||
};
|
||||
|
||||
RefPtr<ClipboardReadRequestChild> mActor;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(AsyncGetClipboardDataProxy, nsIAsyncGetClipboardData)
|
||||
|
||||
NS_IMETHODIMP AsyncGetClipboardDataProxy::GetValid(bool* aOutResult) {
|
||||
MOZ_ASSERT(mActor);
|
||||
*aOutResult = mActor->CanSend();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP AsyncGetClipboardDataProxy::GetFlavorList(
|
||||
nsTArray<nsCString>& aFlavorList) {
|
||||
MOZ_ASSERT(mActor);
|
||||
aFlavorList.AppendElements(mActor->FlavorList());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP AsyncGetClipboardDataProxy::GetData(
|
||||
nsITransferable* aTransferable,
|
||||
nsIAsyncClipboardRequestCallback* aCallback) {
|
||||
if (!aTransferable || !aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// Get a list of flavors this transferable can import
|
||||
nsTArray<nsCString> flavors;
|
||||
nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mActor);
|
||||
// If the requested flavor is not in the list, throw an error.
|
||||
for (const auto& flavor : flavors) {
|
||||
if (!mActor->FlavorList().Contains(flavor)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mActor->CanSend()) {
|
||||
return aCallback->OnComplete(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
mActor->SendGetData(flavors)->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
/* resolve */
|
||||
[self = RefPtr{this}, callback = nsCOMPtr{aCallback},
|
||||
transferable = nsCOMPtr{aTransferable}](
|
||||
const IPCTransferableDataOrError& aIpcTransferableDataOrError) {
|
||||
if (aIpcTransferableDataOrError.type() ==
|
||||
IPCTransferableDataOrError::Tnsresult) {
|
||||
MOZ_ASSERT(NS_FAILED(aIpcTransferableDataOrError.get_nsresult()));
|
||||
callback->OnComplete(aIpcTransferableDataOrError.get_nsresult());
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = nsContentUtils::IPCTransferableDataToTransferable(
|
||||
aIpcTransferableDataOrError.get_IPCTransferableData(),
|
||||
false /* aAddDataFlavor */, transferable,
|
||||
false /* aFilterUnknownFlavors */);
|
||||
if (NS_FAILED(rv)) {
|
||||
callback->OnComplete(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
callback->OnComplete(NS_OK);
|
||||
},
|
||||
/* reject */
|
||||
[callback =
|
||||
nsCOMPtr{aCallback}](mozilla::ipc::ResponseRejectReason aReason) {
|
||||
callback->OnComplete(NS_ERROR_FAILURE);
|
||||
});
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NS_IMETHODIMP nsClipboardProxy::AsyncGetData(
|
||||
const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard,
|
||||
nsIAsyncClipboardGetCallback* aCallback) {
|
||||
if (!aCallback || aFlavorList.IsEmpty()) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)) {
|
||||
MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__,
|
||||
aWhichClipboard);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
ContentChild::GetSingleton()
|
||||
->SendGetClipboardAsync(aFlavorList, aWhichClipboard)
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
/* resolve */
|
||||
[callback = nsCOMPtr{aCallback}](const PClipboardReadRequestOrError&
|
||||
aClipboardReadRequestOrError) {
|
||||
if (aClipboardReadRequestOrError.type() ==
|
||||
PClipboardReadRequestOrError::Tnsresult) {
|
||||
MOZ_ASSERT(
|
||||
NS_FAILED(aClipboardReadRequestOrError.get_nsresult()));
|
||||
callback->OnError(aClipboardReadRequestOrError.get_nsresult());
|
||||
return;
|
||||
}
|
||||
|
||||
auto asyncGetClipboardData = MakeRefPtr<AsyncGetClipboardDataProxy>(
|
||||
static_cast<ClipboardReadRequestChild*>(
|
||||
aClipboardReadRequestOrError.get_PClipboardReadRequest()
|
||||
.AsChild()
|
||||
.get()));
|
||||
|
||||
callback->OnSuccess(asyncGetClipboardData);
|
||||
},
|
||||
/* reject */
|
||||
[callback = nsCOMPtr{aCallback}](
|
||||
mozilla::ipc::ResponseRejectReason aReason) {
|
||||
callback->OnError(NS_ERROR_FAILURE);
|
||||
});
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsClipboardProxy::EmptyClipboard(int32_t aWhichClipboard) {
|
||||
ContentChild::GetSingleton()->SendEmptyClipboard(aWhichClipboard);
|
||||
|
|
@ -256,3 +112,70 @@ void nsClipboardProxy::SetCapabilities(
|
|||
const ClipboardCapabilities& aClipboardCaps) {
|
||||
mClipboardCaps = aClipboardCaps;
|
||||
}
|
||||
|
||||
RefPtr<DataFlavorsPromise> nsClipboardProxy::AsyncHasDataMatchingFlavors(
|
||||
const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard) {
|
||||
auto promise = MakeRefPtr<DataFlavorsPromise::Private>(__func__);
|
||||
ContentChild::GetSingleton()
|
||||
->SendClipboardHasTypesAsync(aFlavorList, aWhichClipboard)
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
/* resolve */
|
||||
[promise](nsTArray<nsCString> types) {
|
||||
promise->Resolve(std::move(types), __func__);
|
||||
},
|
||||
/* reject */
|
||||
[promise](mozilla::ipc::ResponseRejectReason aReason) {
|
||||
promise->Reject(NS_ERROR_FAILURE, __func__);
|
||||
});
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
RefPtr<GenericPromise> nsClipboardProxy::AsyncGetData(
|
||||
nsITransferable* aTransferable, int32_t aWhichClipboard) {
|
||||
if (!aTransferable) {
|
||||
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||
}
|
||||
|
||||
// Get a list of flavors this transferable can import
|
||||
nsTArray<nsCString> flavors;
|
||||
nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
|
||||
if (NS_FAILED(rv)) {
|
||||
return GenericPromise::CreateAndReject(rv, __func__);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITransferable> transferable(aTransferable);
|
||||
auto promise = MakeRefPtr<GenericPromise::Private>(__func__);
|
||||
ContentChild::GetSingleton()
|
||||
->SendGetClipboardAsync(flavors, aWhichClipboard)
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
/* resolve */
|
||||
[promise, transferable](
|
||||
const IPCTransferableDataOrError& ipcTransferableDataOrError) {
|
||||
if (ipcTransferableDataOrError.type() ==
|
||||
IPCTransferableDataOrError::Tnsresult) {
|
||||
promise->Reject(ipcTransferableDataOrError.get_nsresult(),
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = nsContentUtils::IPCTransferableDataToTransferable(
|
||||
ipcTransferableDataOrError.get_IPCTransferableData(),
|
||||
false /* aAddDataFlavor */, transferable,
|
||||
false /* aFilterUnknownFlavors */);
|
||||
if (NS_FAILED(rv)) {
|
||||
promise->Reject(rv, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
promise->Resolve(true, __func__);
|
||||
},
|
||||
/* reject */
|
||||
[promise](mozilla::ipc::ResponseRejectReason aReason) {
|
||||
promise->Reject(NS_ERROR_FAILURE, __func__);
|
||||
});
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,17 @@
|
|||
#include "nsITransferable.idl"
|
||||
#include "nsIClipboardOwner.idl"
|
||||
|
||||
%{C++
|
||||
#include "mozilla/MozPromise.h"
|
||||
|
||||
using DataFlavorsPromise = mozilla::MozPromise<nsTArray<nsCString>, nsresult, true>;
|
||||
%}
|
||||
|
||||
interface nsIArray;
|
||||
|
||||
native AsyncGetDataPromise(RefPtr<mozilla::GenericPromise>);
|
||||
native AsyncDataFlavorsPromise(RefPtr<DataFlavorsPromise>);
|
||||
|
||||
[scriptable, builtinclass, uuid(801e2318-c8fa-11ed-afa1-0242ac120002)]
|
||||
interface nsIAsyncSetClipboardData : nsISupports {
|
||||
/**
|
||||
|
|
@ -46,54 +55,6 @@ interface nsIAsyncClipboardRequestCallback : nsISupports
|
|||
void onComplete(in nsresult aResult);
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(c18ea2f7-6b6f-4a38-9ab3-a8781fdfcc39)]
|
||||
interface nsIAsyncGetClipboardData : nsISupports {
|
||||
/**
|
||||
* Determines whether this request is still valid (e.g., the clipboard content
|
||||
* associated with this request is not stale).
|
||||
*/
|
||||
readonly attribute boolean valid;
|
||||
|
||||
/**
|
||||
* The available flavors in the clipboard.
|
||||
*/
|
||||
readonly attribute Array<ACString> flavorList;
|
||||
|
||||
/**
|
||||
* Filters the flavors that `aTransferable` can import (see
|
||||
* `nsITransferable::flavorsTransferableCanImport`). Every specified flavors
|
||||
* must exist in `flavorList`, or the request will be rejected. If the request
|
||||
* remains valid, it retrieves the data for the first flavor. The data is then
|
||||
* set for `aTransferable`.
|
||||
*
|
||||
* @param aTransferable
|
||||
* The transferable which contains the flavors to be read.
|
||||
* @param aCallback
|
||||
* The nsIAsyncClipboardRequestCallback to be invoked once the get
|
||||
* request is either successfully completed or rejected.
|
||||
* @result NS_OK if no errors
|
||||
*/
|
||||
void getData(in nsITransferable aTransferable,
|
||||
in nsIAsyncClipboardRequestCallback aCallback);
|
||||
};
|
||||
|
||||
[scriptable, uuid(ce23c1c4-58fd-4c33-8579-fa0796d9652c)]
|
||||
interface nsIAsyncClipboardGetCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* Indicates that the clipboard get request has succeeded.
|
||||
*/
|
||||
void onSuccess(in nsIAsyncGetClipboardData aAsyncGetClipboardData);
|
||||
|
||||
/**
|
||||
* Indicates that the clipboard get request has rejected.
|
||||
*
|
||||
* @param aResult
|
||||
* The reason for the rejection, can not be NS_OK.
|
||||
*/
|
||||
void onError(in nsresult aResult);
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(ceaa0047-647f-4b8e-ad1c-aff9fa62aa51)]
|
||||
interface nsIClipboard : nsISupports
|
||||
{
|
||||
|
|
@ -148,26 +109,6 @@ interface nsIClipboard : nsISupports
|
|||
|
||||
void getData ( in nsITransferable aTransferable, in long aWhichClipboard ) ;
|
||||
|
||||
/**
|
||||
* Requests getting data asynchronously from the native clipboard. This does
|
||||
* not actually retreive the data, but returns a nsIAsyncGetClipboardData
|
||||
* contains current avaiable data formats. If the native clipboard is
|
||||
* updated, either by us or other application, the existing
|
||||
* nsIAsyncGetClipboardData becomes invalid.
|
||||
*
|
||||
* @param aFlavorList
|
||||
* Specific data formats ('flavors') that can be retrieved from the
|
||||
* clipboard.
|
||||
* @param aWhichClipboard
|
||||
* Specifies the clipboard to which this operation applies.
|
||||
* @param aCallback
|
||||
* The callback object that will be notified upon completion.
|
||||
* @result NS_OK if no errors
|
||||
*/
|
||||
void asyncGetData(in Array<ACString> aFlavorList,
|
||||
in long aWhichClipboard,
|
||||
in nsIAsyncClipboardGetCallback aCallback);
|
||||
|
||||
/**
|
||||
* This empties the clipboard and notifies the clipboard owner.
|
||||
* This empties the "logical" clipboard. It does not clear the native clipboard.
|
||||
|
|
@ -202,4 +143,29 @@ interface nsIClipboard : nsISupports
|
|||
*/
|
||||
[infallible]
|
||||
boolean isClipboardTypeSupported(in long aWhichClipboard);
|
||||
|
||||
/**
|
||||
* Filters the flavors aTransferable can import (see
|
||||
* `nsITransferable::flavorsTransferableCanImport`) and gets the data for the
|
||||
* first flavor. That data is set for aTransferable.
|
||||
*
|
||||
* @param aTransferable The transferable
|
||||
* @param aWhichClipboard Specifies the clipboard to which this operation applies.
|
||||
* @return MozPromise The returned promise will resolve when the data is ready or reject
|
||||
* if any error occurs.
|
||||
*/
|
||||
[noscript, notxpcom, nostdcall]
|
||||
AsyncGetDataPromise asyncGetData(in nsITransferable aTransferable, in long aWhichClipboard);
|
||||
|
||||
/**
|
||||
* Check if there is data on the clipboard matching each of the flavors in the
|
||||
* given list.
|
||||
*
|
||||
* @param aFlavorList An array of ASCII strings.
|
||||
* @param aWhichClipboard Specifies the clipboard to which this operation applies.
|
||||
* @return MozPromise The returned promise will resolve with the list of matched flavors
|
||||
* when the check is completed or reject if any error occurs.
|
||||
*/
|
||||
[noscript, notxpcom, nostdcall]
|
||||
AsyncDataFlavorsPromise asyncHasDataMatchingFlavors(in Array<ACString> aFlavorList, in long aWhichClipboard);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -74,9 +74,6 @@ skip-if = ["toolkit != 'cocoa'"] # Cocoa widget test
|
|||
["test_clipboard_chrome.html"]
|
||||
support-files = "file_test_clipboard.js"
|
||||
|
||||
["test_clipboard_asyncGetData_chrome.html"]
|
||||
support-files = "file_test_clipboard_asyncGetData.js"
|
||||
|
||||
["test_clipboard_asyncSetData_chrome.html"]
|
||||
support-files = "file_test_clipboard_asyncSetData.js"
|
||||
|
||||
|
|
|
|||
|
|
@ -14,25 +14,13 @@ const clipboardTypes = [
|
|||
clipboard.kSelectionCache,
|
||||
];
|
||||
|
||||
function emptyClipboardData(aType) {
|
||||
// XXX gtk doesn't support emptying clipboard data which is stored from
|
||||
// other application (bug 1853884). As a workaround, we set dummy data
|
||||
// to the clipboard first to ensure the subsequent emptyClipboard call
|
||||
// works.
|
||||
if (navigator.platform.includes("Linux")) {
|
||||
writeStringToClipboard("foo", "text/plain", aType);
|
||||
}
|
||||
|
||||
clipboard.emptyClipboard(aType);
|
||||
}
|
||||
|
||||
function cleanupAllClipboard() {
|
||||
for (const type of clipboardTypes) {
|
||||
clipboardTypes.forEach(function (type) {
|
||||
if (clipboard.isClipboardTypeSupported(type)) {
|
||||
info(`cleanup clipboard ${type}`);
|
||||
emptyClipboardData(type);
|
||||
clipboard.emptyClipboard(type);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function generateRandomString() {
|
||||
|
|
@ -65,14 +53,6 @@ function addStringToTransferable(aFlavor, aStr, aTrans) {
|
|||
aTrans.setTransferData(aFlavor, supportsStr);
|
||||
}
|
||||
|
||||
function updateStringToTransferable(aFlavor, aStr, aTrans) {
|
||||
let supportsStr = Cc["@mozilla.org/supports-string;1"].createInstance(
|
||||
Ci.nsISupportsString
|
||||
);
|
||||
supportsStr.data = aStr;
|
||||
aTrans.setTransferData(aFlavor, supportsStr);
|
||||
}
|
||||
|
||||
function writeStringToClipboard(
|
||||
aStr,
|
||||
aFlavor,
|
||||
|
|
@ -135,73 +115,3 @@ function getClipboardData(aFlavor, aClipboardType) {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function asyncGetClipboardData(aClipboardType) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
clipboard.asyncGetData(
|
||||
["text/plain", "text/html", "image/png"],
|
||||
aClipboardType,
|
||||
{
|
||||
QueryInterface: SpecialPowers.ChromeUtils.generateQI([
|
||||
"nsIAsyncClipboardGetCallback",
|
||||
]),
|
||||
// nsIAsyncClipboardGetCallback
|
||||
onSuccess: SpecialPowers.wrapCallback(function (
|
||||
aAsyncGetClipboardData
|
||||
) {
|
||||
resolve(aAsyncGetClipboardData);
|
||||
}),
|
||||
onError: SpecialPowers.wrapCallback(function (aResult) {
|
||||
reject(aResult);
|
||||
}),
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
ok(false, `asyncGetData should not throw`);
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function asyncClipboardRequestGetData(aRequest, aFlavor, aThrows = false) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(
|
||||
Ci.nsITransferable
|
||||
);
|
||||
trans.init(null);
|
||||
trans.addDataFlavor(aFlavor);
|
||||
try {
|
||||
aRequest.getData(trans, aResult => {
|
||||
if (aResult != Cr.NS_OK) {
|
||||
reject(aResult);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var data = SpecialPowers.createBlankObject();
|
||||
trans.getTransferData(aFlavor, data);
|
||||
resolve(data.value.QueryInterface(Ci.nsISupportsString).data);
|
||||
} catch (ex) {
|
||||
// XXX: should widget set empty string to transferable when there no
|
||||
// data in system clipboard?
|
||||
resolve("");
|
||||
}
|
||||
});
|
||||
ok(
|
||||
!aThrows,
|
||||
`nsIAsyncGetClipboardData.getData should ${
|
||||
aThrows ? "throw" : "success"
|
||||
}`
|
||||
);
|
||||
} catch (e) {
|
||||
ok(
|
||||
aThrows,
|
||||
`nsIAsyncGetClipboardData.getData should ${
|
||||
aThrows ? "throw" : "success"
|
||||
}`
|
||||
);
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,160 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* import-globals-from clipboard_helper.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
clipboardTypes.forEach(function (type) {
|
||||
if (!clipboard.isClipboardTypeSupported(type)) {
|
||||
add_task(async function test_clipboard_asyncGetData_not_support() {
|
||||
info(`Test asyncGetData request throwing on ${type}`);
|
||||
SimpleTest.doesThrow(
|
||||
() => clipboard.asyncGetData(["text/plain"], type, {}),
|
||||
"Passing unsupported clipboard type should throw"
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
add_task(async function test_clipboard_asyncGetData_throw() {
|
||||
info(`Test asyncGetData request throwing on ${type}`);
|
||||
SimpleTest.doesThrow(
|
||||
() => clipboard.asyncGetData([], type, {}),
|
||||
"Passing empty flavor list should throw"
|
||||
);
|
||||
|
||||
SimpleTest.doesThrow(
|
||||
() => clipboard.asyncGetData(["text/plain"], type, null),
|
||||
"Passing no callback should throw"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_clipboard_asyncGetData_no_matched_flavor() {
|
||||
info(`Test asyncGetData have no matched flavor on ${type}`);
|
||||
cleanupAllClipboard();
|
||||
|
||||
writeRandomStringToClipboard("text/plain", type);
|
||||
let request = await new Promise(resolve => {
|
||||
clipboard.asyncGetData(["text/html"], type, {
|
||||
QueryInterface: SpecialPowers.ChromeUtils.generateQI([
|
||||
"nsIAsyncClipboardGetCallback",
|
||||
]),
|
||||
// nsIAsyncClipboardGetCallback
|
||||
onSuccess: SpecialPowers.wrapCallback(function (
|
||||
aAsyncGetClipboardData
|
||||
) {
|
||||
resolve(aAsyncGetClipboardData);
|
||||
}),
|
||||
});
|
||||
});
|
||||
isDeeply(request.flavorList, [], "Check flavorList");
|
||||
});
|
||||
|
||||
add_task(async function test_empty_data() {
|
||||
info(`Test asyncGetData request with empty data on ${type}`);
|
||||
cleanupAllClipboard();
|
||||
|
||||
let request = await asyncGetClipboardData(type);
|
||||
isDeeply(request.flavorList, [], "Check flavorList");
|
||||
await asyncClipboardRequestGetData(request, "text/plain", true).catch(
|
||||
() => {}
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_clipboard_asyncGetData_after_write() {
|
||||
info(`Test asyncGetData request after write on ${type}`);
|
||||
|
||||
let str = writeRandomStringToClipboard("text/plain", type);
|
||||
let request = await asyncGetClipboardData(type);
|
||||
isDeeply(request.flavorList, ["text/plain"], "Check flavorList");
|
||||
is(
|
||||
await asyncClipboardRequestGetData(request, "text/plain"),
|
||||
str,
|
||||
"Check data"
|
||||
);
|
||||
ok(request.valid, "request should still be valid");
|
||||
// Requesting a flavor that is not in the list should throw error.
|
||||
await asyncClipboardRequestGetData(request, "text/html", true).catch(
|
||||
() => {}
|
||||
);
|
||||
ok(request.valid, "request should still be valid");
|
||||
|
||||
// Writing a new data should invalid existing get request.
|
||||
str = writeRandomStringToClipboard("text/plain", type);
|
||||
await asyncClipboardRequestGetData(request, "text/plain").then(
|
||||
() => {
|
||||
ok(false, "asyncClipboardRequestGetData should not success");
|
||||
},
|
||||
e => {
|
||||
ok(true, "asyncClipboardRequestGetData should reject");
|
||||
}
|
||||
);
|
||||
ok(!request.valid, "request should no longer be valid");
|
||||
|
||||
info(`check clipboard data again`);
|
||||
request = await asyncGetClipboardData(type);
|
||||
isDeeply(request.flavorList, ["text/plain"], "Check flavorList");
|
||||
is(
|
||||
await asyncClipboardRequestGetData(request, "text/plain"),
|
||||
str,
|
||||
"Check data"
|
||||
);
|
||||
|
||||
cleanupAllClipboard();
|
||||
});
|
||||
|
||||
add_task(async function test_clipboard_asyncGetData_after_empty() {
|
||||
info(`Test asyncGetData request after empty on ${type}`);
|
||||
|
||||
let str = writeRandomStringToClipboard("text/plain", type);
|
||||
let request = await asyncGetClipboardData(type);
|
||||
isDeeply(request.flavorList, ["text/plain"], "Check flavorList");
|
||||
is(
|
||||
await asyncClipboardRequestGetData(request, "text/plain"),
|
||||
str,
|
||||
"Check data"
|
||||
);
|
||||
ok(request.valid, "request should still be valid");
|
||||
|
||||
// Empty clipboard data
|
||||
await emptyClipboardData(type);
|
||||
await asyncClipboardRequestGetData(request, "text/plain").then(
|
||||
() => {
|
||||
ok(false, "asyncClipboardRequestGetData should not success");
|
||||
},
|
||||
e => {
|
||||
ok(true, "asyncClipboardRequestGetData should reject");
|
||||
}
|
||||
);
|
||||
ok(!request.valid, "request should no longer be valid");
|
||||
|
||||
info(`check clipboard data again`);
|
||||
request = await asyncGetClipboardData(type);
|
||||
isDeeply(request.flavorList, [], "Check flavorList");
|
||||
|
||||
cleanupAllClipboard();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_html_data() {
|
||||
info(`Test asyncGetData request with html data`);
|
||||
|
||||
const html_str = `<img src="https://example.com/oops">`;
|
||||
writeStringToClipboard(html_str, "text/html", clipboard.kGlobalClipboard);
|
||||
|
||||
let request = await asyncGetClipboardData(clipboard.kGlobalClipboard);
|
||||
isDeeply(request.flavorList, ["text/html"], "Check flavorList");
|
||||
is(
|
||||
await asyncClipboardRequestGetData(request, "text/html"),
|
||||
// On Windows, widget adds extra data into HTML clipboard.
|
||||
navigator.platform.includes("Win")
|
||||
? `<html><body>\n<!--StartFragment-->${html_str}<!--EndFragment-->\n</body>\n</html>`
|
||||
: html_str,
|
||||
"Check data"
|
||||
);
|
||||
// Requesting a flavor that is not in the list should throw error.
|
||||
await asyncClipboardRequestGetData(request, "text/plain", true).catch(
|
||||
() => {}
|
||||
);
|
||||
});
|
||||
|
|
@ -24,9 +24,6 @@ skip-if = [
|
|||
]
|
||||
support-files = ["file_test_clipboard.js"]
|
||||
|
||||
["test_clipboard_asyncGetData.html"]
|
||||
support-files = ["file_test_clipboard_asyncGetData.js"]
|
||||
|
||||
["test_clipboard_asyncSetData.html"]
|
||||
support-files = ["file_test_clipboard_asyncSetData.js"]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1852947
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 1852947</title>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="clipboard_helper.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<!-- Tests are in file_clipboard_asyncGetData.js -->
|
||||
<script src="file_test_clipboard_asyncGetData.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1852947
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 1852947</title>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="clipboard_helper.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<!-- Tests are in file_clipboard_asyncGetData.js -->
|
||||
<script src="file_test_clipboard_asyncGetData.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -16,7 +16,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1812543
|
|||
<script class="testbody" type="application/javascript">
|
||||
|
||||
function testClipboardCache(aClipboardType, aAsync, aIsSupportGetFromCachedTransferable) {
|
||||
add_task(async function test_clipboard_get() {
|
||||
add_task(function test_clipboard_get() {
|
||||
info(`test_clipboard_get ${aAsync ? "async " : ""}` +
|
||||
`with pref ${aIsSupportGetFromCachedTransferable ? "enabled" : "disabled"}`);
|
||||
|
||||
|
|
@ -77,13 +77,13 @@ function testClipboardCache(aClipboardType, aAsync, aIsSupportGetFromCachedTrans
|
|||
}
|
||||
|
||||
info(`Clean all clipboard data`);
|
||||
await cleanupAllClipboard();
|
||||
cleanupAllClipboard();
|
||||
});
|
||||
}
|
||||
|
||||
function runClipboardCacheTests(aIsSupportGetFromCachedTransferable) {
|
||||
add_task(async function setup() {
|
||||
await cleanupAllClipboard();
|
||||
cleanupAllClipboard();
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[
|
||||
|
|
@ -99,7 +99,7 @@ function runClipboardCacheTests(aIsSupportGetFromCachedTransferable) {
|
|||
return;
|
||||
}
|
||||
|
||||
add_task(async function test_clipboard_hasDataMatchingFlavors() {
|
||||
add_task(function test_clipboard_hasDataMatchingFlavors() {
|
||||
info(`test_clipboard_hasDataMatchingFlavors with pref ` +
|
||||
`${aIsSupportGetFromCachedTransferable ? "enabled" : "disabled"}`);
|
||||
|
||||
|
|
@ -165,51 +165,7 @@ function runClipboardCacheTests(aIsSupportGetFromCachedTransferable) {
|
|||
`Check if there is text/foo flavor on clipboard ${type}`);
|
||||
|
||||
// Clean clipboard data.
|
||||
await cleanupAllClipboard();
|
||||
});
|
||||
|
||||
add_task(async function test_clipboard_asyncGetData() {
|
||||
const testClipboardData = async function(aRequest, aExpectedData) {
|
||||
is(aRequest.flavorList.length, Object.keys(aExpectedData).length, "Check flavorList length");
|
||||
for (const [key, value] of Object.entries(aExpectedData)) {
|
||||
ok(aRequest.flavorList.includes(key), `${key} should be available`);
|
||||
is(await asyncClipboardRequestGetData(aRequest, key), value,
|
||||
`Check ${key} data`);
|
||||
}
|
||||
};
|
||||
|
||||
info(`test_clipboard_hasDataMatchingFlavors with pref ` +
|
||||
`${aIsSupportGetFromCachedTransferable ? "enabled" : "disabled"}`);
|
||||
|
||||
const clipboardData = { "text/plain": generateRandomString() };
|
||||
const trans = generateNewTransferable("text/plain", clipboardData["text/plain"]);
|
||||
|
||||
info(`Write text/plain data to clipboard ${type}`);
|
||||
clipboard.setData(trans, null, type);
|
||||
await testClipboardData(await asyncGetClipboardData(type), clipboardData);
|
||||
|
||||
info(`Add text/html data to transferable`);
|
||||
const htmlString = `<div>${generateRandomString()}</div>`;
|
||||
addStringToTransferable("text/html", htmlString, trans);
|
||||
// XXX macOS uses cached transferable to implement kSelectionCache type, too,
|
||||
// so it behaves differently than other types.
|
||||
if (aIsSupportGetFromCachedTransferable ||
|
||||
(type == clipboard.kSelectionCache && !SpecialPowers.isHeadless)) {
|
||||
clipboardData["text/html"] = htmlString;
|
||||
}
|
||||
await testClipboardData(await asyncGetClipboardData(type), clipboardData);
|
||||
|
||||
info(`Should not get the data from other clipboard type`);
|
||||
clipboardTypes.forEach(async function(otherType) {
|
||||
if (otherType != type &&
|
||||
clipboard.isClipboardTypeSupported(otherType)) {
|
||||
info(`Check clipboard type ${otherType}`);
|
||||
await testClipboardData(await asyncGetClipboardData(otherType), {});
|
||||
}
|
||||
});
|
||||
|
||||
info(`Check data on clipboard ${type} again`);
|
||||
await testClipboardData(await asyncGetClipboardData(type), clipboardData);
|
||||
cleanupAllClipboard();
|
||||
});
|
||||
|
||||
// Test sync set clipboard data.
|
||||
|
|
|
|||
Loading…
Reference in a new issue