mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 22:32:46 +00:00
Bug 1915351
part 3 - add entry point to get all clipboard to actor r=dlp-reviewers,ipc-reviewers,nika,edgar,handyman
Add an entry point to ClipboardContentAnalysisParent to get all the data on the clipboard, which also analyzes all the clipboard contents. Differential Revision: https://phabricator.services.mozilla.com/D223412
This commit is contained in:
parent
d836de2847
commit
c12829c03e
@ -98,6 +98,8 @@ description = Only used by gtests
|
||||
# Clipboard
|
||||
[PClipboardContentAnalysis::GetClipboard]
|
||||
description = Called from PContent::GetClipboard which is a legacy synchronous clipboard API
|
||||
[PClipboardContentAnalysis::GetAllClipboardDataSync]
|
||||
description = Called from synchronous DataTransfer constructor
|
||||
[PClipboardReadRequest::GetDataSync]
|
||||
description = Called from synchronous DataTransfer constructor
|
||||
[PContent::GetClipboard]
|
||||
|
@ -5,7 +5,9 @@
|
||||
|
||||
#include "ContentAnalysis.h"
|
||||
#include "mozilla/ClipboardContentAnalysisParent.h"
|
||||
#include "mozilla/ClipboardReadRequestParent.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/WindowContext.h"
|
||||
#include "mozilla/dom/WindowGlobalParent.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
@ -23,7 +25,7 @@ using ClipboardResultPromise =
|
||||
static RefPtr<ClipboardResultPromise> GetClipboardImpl(
|
||||
const nsTArray<nsCString>& aTypes,
|
||||
nsIClipboard::ClipboardType aWhichClipboard,
|
||||
uint64_t aRequestingWindowContextId,
|
||||
uint64_t aRequestingWindowContextId, bool aCheckAllContent,
|
||||
dom::ThreadsafeContentParentHandle* aRequestingContentParent) {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
@ -65,20 +67,55 @@ static RefPtr<ClipboardResultPromise> GetClipboardImpl(
|
||||
return ClipboardResultPromise::CreateAndReject(
|
||||
transferableToCheck.unwrapErr(), __func__);
|
||||
}
|
||||
nsCOMPtr<nsITransferable> transferable = transferableToCheck.unwrap();
|
||||
if (aCheckAllContent) {
|
||||
for (const auto& type : aTypes) {
|
||||
AutoTArray<nsCString, 1> singleTypeArray{type};
|
||||
auto singleTransferableToCheck =
|
||||
dom::ContentParent::CreateClipboardTransferable(singleTypeArray);
|
||||
if (singleTransferableToCheck.isErr()) {
|
||||
return ClipboardResultPromise::CreateAndReject(
|
||||
singleTransferableToCheck.unwrapErr(), __func__);
|
||||
}
|
||||
|
||||
// Pass nullptr for the window here because we will be doing
|
||||
// content analysis ourselves asynchronously (so it doesn't block
|
||||
// main thread we're running on now)
|
||||
nsCOMPtr transferable = transferableToCheck.unwrap();
|
||||
// Ideally we would be calling GetDataSnapshot() here to avoid blocking the
|
||||
// main thread (and this would mean we could also pass in the window here so
|
||||
// we wouldn't have to duplicate the Content Analysis code below). See
|
||||
// bug 1908280.
|
||||
nsresult rv = clipboard->GetData(transferable, aWhichClipboard, nullptr);
|
||||
if (NS_FAILED(rv)) {
|
||||
return ClipboardResultPromise::CreateAndReject(rv, __func__);
|
||||
// Pass nullptr for the window here because we will be doing
|
||||
// content analysis ourselves asynchronously (so it doesn't block
|
||||
// main thread we're running on now)
|
||||
nsCOMPtr singleTransferable = singleTransferableToCheck.unwrap();
|
||||
// Ideally we would be calling GetDataSnapshot() here to avoid blocking
|
||||
// the main thread (and this would mean we could also pass in the window
|
||||
// here so we wouldn't have to duplicate the Content Analysis code below).
|
||||
// See bug 1908280.
|
||||
nsresult rv =
|
||||
clipboard->GetData(singleTransferable, aWhichClipboard, nullptr);
|
||||
if (NS_FAILED(rv)) {
|
||||
return ClipboardResultPromise::CreateAndReject(rv, __func__);
|
||||
}
|
||||
nsCOMPtr<nsISupports> data;
|
||||
rv =
|
||||
singleTransferable->GetTransferData(type.get(), getter_AddRefs(data));
|
||||
// This call will fail if the data is null
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = transferable->SetTransferData(type.get(), data);
|
||||
if (NS_FAILED(rv)) {
|
||||
return ClipboardResultPromise::CreateAndReject(rv, __func__);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Pass nullptr for the window here because we will be doing
|
||||
// content analysis ourselves asynchronously (so it doesn't block
|
||||
// main thread we're running on now)
|
||||
//
|
||||
// Ideally we would be calling GetDataSnapshot() here to avoid blocking the
|
||||
// main thread (and this would mean we could also pass in the window here so
|
||||
// we wouldn't have to duplicate the Content Analysis code below). See
|
||||
// bug 1908280.
|
||||
nsresult rv = clipboard->GetData(transferable, aWhichClipboard, nullptr);
|
||||
if (NS_FAILED(rv)) {
|
||||
return ClipboardResultPromise::CreateAndReject(rv, __func__);
|
||||
}
|
||||
}
|
||||
|
||||
auto resultPromise = MakeRefPtr<ClipboardResultPromise::Private>(__func__);
|
||||
|
||||
auto contentAnalysisCallback =
|
||||
@ -106,41 +143,51 @@ static RefPtr<ClipboardResultPromise> GetClipboardImpl(
|
||||
|
||||
contentanalysis::ContentAnalysis::CheckClipboardContentAnalysis(
|
||||
static_cast<nsBaseClipboard*>(clipboard.get()), window, transferable,
|
||||
aWhichClipboard, contentAnalysisCallback);
|
||||
aWhichClipboard, contentAnalysisCallback, aCheckAllContent);
|
||||
return resultPromise;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ipc::IPCResult ClipboardContentAnalysisParent::RecvGetClipboard(
|
||||
ipc::IPCResult ClipboardContentAnalysisParent::GetSomeClipboardData(
|
||||
nsTArray<nsCString>&& aTypes,
|
||||
const nsIClipboard::ClipboardType& aWhichClipboard,
|
||||
const uint64_t& aRequestingWindowContextId,
|
||||
const uint64_t& aRequestingWindowContextId, bool aCheckAllContent,
|
||||
IPCTransferableDataOrError* aTransferableDataOrError) {
|
||||
// The whole point of having this actor is that it runs on a background thread
|
||||
// and so waiting for the content analysis result won't cause the main thread
|
||||
// to use SpinEventLoopUntil() which can cause a shutdownhang per bug 1901197.
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
Monitor mon("ClipboardContentAnalysisParent::RecvGetClipboard");
|
||||
Monitor mon("ClipboardContentAnalysisParent::GetSomeClipboardData");
|
||||
InvokeAsync(GetMainThreadSerialEventTarget(), __func__,
|
||||
[&]() {
|
||||
return GetClipboardImpl(aTypes, aWhichClipboard,
|
||||
aRequestingWindowContextId,
|
||||
mThreadsafeContentParentHandle);
|
||||
return GetClipboardImpl(
|
||||
aTypes, aWhichClipboard, aRequestingWindowContextId,
|
||||
aCheckAllContent, mThreadsafeContentParentHandle);
|
||||
})
|
||||
->Then(GetMainThreadSerialEventTarget(), __func__,
|
||||
[&](ClipboardResultPromise::ResolveOrRejectValue&& aResult) {
|
||||
AssertIsOnMainThread();
|
||||
// Acquire the lock, pass the data back to the background
|
||||
// thread, and notify the background thread that work is
|
||||
// complete.
|
||||
MonitorAutoLock lock(mon);
|
||||
if (aResult.IsResolve()) {
|
||||
*aTransferableDataOrError = std::move(aResult.ResolveValue());
|
||||
} else {
|
||||
auto monitor = MakeScopeExit([&]() { mon.Notify(); });
|
||||
if (aResult.IsReject()) {
|
||||
*aTransferableDataOrError = aResult.RejectValue();
|
||||
return;
|
||||
}
|
||||
|
||||
if (aCheckAllContent) {
|
||||
// Content Analysis succeeded on everything
|
||||
// Just return the flavors that were asked for
|
||||
IPCTransferableData analyzedData =
|
||||
std::move(aResult.ResolveValue());
|
||||
nsTArray<IPCTransferableDataItem> dataItems;
|
||||
for (auto& item : analyzedData.items()) {
|
||||
if (aTypes.Contains(item.flavor())) {
|
||||
dataItems.AppendElement(std::move(item));
|
||||
}
|
||||
}
|
||||
IPCTransferableData data(std::move(dataItems));
|
||||
*aTransferableDataOrError = std::move(data);
|
||||
} else {
|
||||
*aTransferableDataOrError = std::move(aResult.ResolveValue());
|
||||
}
|
||||
mon.Notify();
|
||||
});
|
||||
|
||||
{
|
||||
@ -155,11 +202,38 @@ ipc::IPCResult ClipboardContentAnalysisParent::RecvGetClipboard(
|
||||
IPCTransferableDataOrError::Tnsresult) {
|
||||
NS_WARNING(nsPrintfCString(
|
||||
"ClipboardContentAnalysisParent::"
|
||||
"RecvGetClipboard got error %x",
|
||||
"GetSomeClipboardData got error %x",
|
||||
static_cast<int>(aTransferableDataOrError->get_nsresult()))
|
||||
.get());
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
ipc::IPCResult ClipboardContentAnalysisParent::RecvGetClipboard(
|
||||
nsTArray<nsCString>&& aTypes,
|
||||
const nsIClipboard::ClipboardType& aWhichClipboard,
|
||||
const uint64_t& aRequestingWindowContextId,
|
||||
IPCTransferableDataOrError* aTransferableDataOrError) {
|
||||
// The whole point of having this actor is that it runs on a background thread
|
||||
// and so waiting for the content analysis result won't cause the main thread
|
||||
// to use SpinEventLoopUntil() which can cause a shutdownhang per bug 1901197.
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
return GetSomeClipboardData(
|
||||
std::move(aTypes), aWhichClipboard, aRequestingWindowContextId,
|
||||
/* aCheckAllContent */ false, aTransferableDataOrError);
|
||||
}
|
||||
|
||||
ipc::IPCResult ClipboardContentAnalysisParent::RecvGetAllClipboardDataSync(
|
||||
nsTArray<nsCString>&& aTypes,
|
||||
const nsIClipboard::ClipboardType& aWhichClipboard,
|
||||
const uint64_t& aRequestingWindowContextId,
|
||||
IPCTransferableDataOrError* aTransferableDataOrError) {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
return GetSomeClipboardData(
|
||||
std::move(aTypes), aWhichClipboard, aRequestingWindowContextId,
|
||||
/* aCheckAllContent */ true, aTransferableDataOrError);
|
||||
}
|
||||
} // namespace mozilla
|
||||
|
@ -25,6 +25,11 @@ class ClipboardContentAnalysisParent final
|
||||
private:
|
||||
~ClipboardContentAnalysisParent() = default;
|
||||
RefPtr<dom::ThreadsafeContentParentHandle> mThreadsafeContentParentHandle;
|
||||
ipc::IPCResult GetSomeClipboardData(
|
||||
nsTArray<nsCString>&& aTypes,
|
||||
const nsIClipboard::ClipboardType& aWhichClipboard,
|
||||
const uint64_t& aRequestingWindowContextId, bool aCheckAllContent,
|
||||
IPCTransferableDataOrError* aTransferableDataOrError);
|
||||
|
||||
public:
|
||||
ipc::IPCResult RecvGetClipboard(
|
||||
@ -32,6 +37,11 @@ class ClipboardContentAnalysisParent final
|
||||
const nsIClipboard::ClipboardType& aWhichClipboard,
|
||||
const uint64_t& aRequestingWindowContextId,
|
||||
IPCTransferableDataOrError* aTransferableDataOrError);
|
||||
ipc::IPCResult RecvGetAllClipboardDataSync(
|
||||
nsTArray<nsCString>&& aTypes,
|
||||
const nsIClipboard::ClipboardType& aWhichClipboard,
|
||||
const uint64_t& aRequestingWindowContextId,
|
||||
IPCTransferableDataOrError* aTransferableDataOrError);
|
||||
};
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
include IPCTransferable;
|
||||
include protocol PContent;
|
||||
|
||||
include "mozilla/widget/WidgetMessageUtils.h";
|
||||
|
||||
@ -18,11 +19,18 @@ sync protocol PClipboardContentAnalysis
|
||||
parent:
|
||||
// Given a list of supported types, returns the clipboard data for the
|
||||
// first type that matches.
|
||||
// aRequestingWindowContext is the window that is requesting the clipboard,
|
||||
// which is used for content analysis.
|
||||
// aRequestingWindowContextId is the ID of the window that is requesting
|
||||
// the clipboard, which is used for content analysis.
|
||||
sync GetClipboard(nsCString[] aTypes, ClipboardType aWhichClipboard,
|
||||
uint64_t aRequestingWindowContextId)
|
||||
returns (IPCTransferableDataOrError transferableDataOrError);
|
||||
|
||||
// Requests getting data from clipboard.
|
||||
// aRequestingWindowContextId is the ID of the window that is requesting
|
||||
// the clipboard, which is used for content analysis.
|
||||
sync GetAllClipboardDataSync(nsCString[] aTypes, ClipboardType aWhichClipboard,
|
||||
uint64_t aRequestingWindowContextId)
|
||||
returns (IPCTransferableDataOrError transferableDataOrError);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1184,6 +1184,87 @@ bool nsBaseClipboard::ClipboardDataSnapshot::IsValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsBaseClipboard::ClipboardPopulatedDataSnapshot,
|
||||
nsIClipboardDataSnapshot)
|
||||
|
||||
nsBaseClipboard::ClipboardPopulatedDataSnapshot::ClipboardPopulatedDataSnapshot(
|
||||
nsITransferable* aTransferable)
|
||||
: mTransferable(aTransferable) {
|
||||
MOZ_ASSERT(mTransferable);
|
||||
aTransferable->FlavorsTransferableCanExport(mFlavors);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsBaseClipboard::ClipboardPopulatedDataSnapshot::GetValid(
|
||||
bool* aOutResult) {
|
||||
// Since this is a snapshot of what the clipboard data was, this is always
|
||||
// valid
|
||||
*aOutResult = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsBaseClipboard::ClipboardPopulatedDataSnapshot::GetFlavorList(
|
||||
nsTArray<nsCString>& aFlavors) {
|
||||
aFlavors.AppendElements(mFlavors);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsBaseClipboard::ClipboardPopulatedDataSnapshot::GetData(
|
||||
nsITransferable* aTransferable,
|
||||
nsIAsyncClipboardRequestCallback* aCallback) {
|
||||
if (!aTransferable || !aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"ClipboardPopulatedDataSnapshot::GetData",
|
||||
[self = RefPtr{this}, transferable = RefPtr{aTransferable},
|
||||
callback = RefPtr{aCallback}]() {
|
||||
nsresult rv = self->GetDataSync(transferable);
|
||||
callback->OnComplete(rv);
|
||||
}));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsBaseClipboard::ClipboardPopulatedDataSnapshot::GetDataSync(
|
||||
nsITransferable* aTransferable) {
|
||||
MOZ_CLIPBOARD_LOG("ClipboardPopulatedDataSnapshot::GetDataSync: %p", this);
|
||||
|
||||
if (!aTransferable) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// This method only fills in the data for the first flavor passed in. This
|
||||
// seems weird but matches the IDL documentation and behavior.
|
||||
if (!flavors.IsEmpty()) {
|
||||
nsCOMPtr<nsISupports> data;
|
||||
rv = mTransferable->GetTransferData(flavors[0].get(), getter_AddRefs(data));
|
||||
if (NS_FAILED(rv)) {
|
||||
aTransferable->ClearAllData();
|
||||
return rv;
|
||||
}
|
||||
rv = aTransferable->SetTransferData(flavors[0].get(), data);
|
||||
if (NS_FAILED(rv)) {
|
||||
aTransferable->ClearAllData();
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mozilla::Maybe<uint64_t> nsBaseClipboard::GetClipboardCacheInnerWindowId(
|
||||
ClipboardType aClipboardType) {
|
||||
auto* clipboardCache = GetClipboardCacheIfValid(aClipboardType);
|
||||
|
@ -85,6 +85,19 @@ class nsBaseClipboard : public nsIClipboard {
|
||||
virtual mozilla::Result<int32_t, nsresult> GetNativeClipboardSequenceNumber(
|
||||
ClipboardType aWhichClipboard) = 0;
|
||||
|
||||
class ClipboardPopulatedDataSnapshot final : public nsIClipboardDataSnapshot {
|
||||
public:
|
||||
explicit ClipboardPopulatedDataSnapshot(nsITransferable* aTransferable);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICLIPBOARDDATASNAPSHOT
|
||||
private:
|
||||
virtual ~ClipboardPopulatedDataSnapshot() = default;
|
||||
nsCOMPtr<nsITransferable> mTransferable;
|
||||
// List of available data types for clipboard content.
|
||||
nsTArray<nsCString> mFlavors;
|
||||
};
|
||||
|
||||
protected:
|
||||
virtual ~nsBaseClipboard();
|
||||
|
||||
|
@ -332,9 +332,39 @@ NS_IMETHODIMP nsClipboardProxy::GetDataSnapshotSync(
|
||||
aWhichClipboard);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
ContentChild* contentChild = ContentChild::GetSingleton();
|
||||
if (MOZ_UNLIKELY(nsIContentAnalysis::MightBeActive())) {
|
||||
// If Content Analysis is active we want to fetch all the clipboard data
|
||||
// up front since we need to analyze it anyway.
|
||||
RefPtr<ClipboardContentAnalysisChild> contentAnalysis =
|
||||
ClipboardContentAnalysisChild::GetOrCreate();
|
||||
IPCTransferableDataOrError ipcTransferableDataOrError;
|
||||
bool result = contentAnalysis->SendGetAllClipboardDataSync(
|
||||
aFlavorList, aWhichClipboard, aRequestingWindowContext->InnerWindowId(),
|
||||
&ipcTransferableDataOrError);
|
||||
if (!result) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (ipcTransferableDataOrError.type() ==
|
||||
IPCTransferableDataOrError::Tnsresult) {
|
||||
return ipcTransferableDataOrError.get_nsresult();
|
||||
}
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsITransferable> trans =
|
||||
do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
trans->Init(nullptr);
|
||||
rv = nsContentUtils::IPCTransferableDataToTransferable(
|
||||
ipcTransferableDataOrError.get_IPCTransferableData(),
|
||||
true /* aAddDataFlavor */, trans, false /* aFilterUnknownFlavors */);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
auto snapshot =
|
||||
mozilla::MakeRefPtr<nsBaseClipboard::ClipboardPopulatedDataSnapshot>(
|
||||
trans);
|
||||
snapshot.forget(_retval);
|
||||
return NS_OK;
|
||||
}
|
||||
ClipboardReadRequestOrError requestOrError;
|
||||
ContentChild* contentChild = ContentChild::GetSingleton();
|
||||
contentChild->SendGetClipboardDataSnapshotSync(
|
||||
aFlavorList, aWhichClipboard, aRequestingWindowContext, &requestOrError);
|
||||
auto result = CreateClipboardDataSnapshotProxy(std::move(requestOrError));
|
||||
|
Loading…
Reference in New Issue
Block a user