gecko-dev/widget/nsClipboardProxy.cpp
Greg Stoll 6b9e711a99 Bug 1871135 - Add content analysis support to clipboard operations r=edgar,nika,masayuki,devtools-reviewers
When content analysis is on, pastes will be checked by the CA
agent while tab input is blocked. The synchronous nsIClipboard.getData()
method must block until the analysis result is received, so this
requires doing a SpinEventLoopUntil.

Differential Revision: https://phabricator.services.mozilla.com/D196997
2024-02-13 11:26:48 +00:00

272 lines
8.9 KiB
C++

/* 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 "nsClipboardProxy.h"
#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"
#include "mozilla/Maybe.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/Unused.h"
#include "nsArrayUtils.h"
#include "nsBaseClipboard.h"
#include "nsISupportsPrimitives.h"
#include "nsCOMPtr.h"
#include "nsComponentManagerUtils.h"
#include "nsXULAppAPI.h"
#include "nsContentUtils.h"
#include "PermissionMessageUtils.h"
using namespace mozilla;
using namespace mozilla::dom;
NS_IMPL_ISUPPORTS(nsClipboardProxy, nsIClipboard, nsIClipboardProxy)
nsClipboardProxy::nsClipboardProxy() : mClipboardCaps(false, false, false) {}
NS_IMETHODIMP
nsClipboardProxy::SetData(nsITransferable* aTransferable,
nsIClipboardOwner* anOwner, int32_t aWhichClipboard) {
#if defined(ACCESSIBILITY) && defined(XP_WIN)
a11y::Compatibility::SuppressA11yForClipboardCopy();
#endif
ContentChild* child = ContentChild::GetSingleton();
IPCTransferable ipcTransferable;
nsContentUtils::TransferableToIPCTransferable(aTransferable, &ipcTransferable,
false, nullptr);
child->SendSetClipboard(std::move(ipcTransferable), aWhichClipboard);
return NS_OK;
}
NS_IMETHODIMP nsClipboardProxy::AsyncSetData(
int32_t aWhichClipboard, nsIAsyncClipboardRequestCallback* aCallback,
nsIAsyncSetClipboardData** _retval) {
RefPtr<ClipboardWriteRequestChild> request =
MakeRefPtr<ClipboardWriteRequestChild>(aCallback);
ContentChild::GetSingleton()->SendPClipboardWriteRequestConstructor(
request, aWhichClipboard);
request.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP
nsClipboardProxy::GetData(nsITransferable* aTransferable,
int32_t aWhichClipboard,
mozilla::dom::WindowContext* aWindowContext) {
MOZ_DIAGNOSTIC_ASSERT(aWindowContext && aWindowContext->IsInProcess(),
"content clipboard reads must be associated with an "
"in-process WindowContext");
if (aWindowContext->IsDiscarded()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsTArray<nsCString> types;
aTransferable->FlavorsTransferableCanImport(types);
IPCTransferableData transferable;
ContentChild::GetSingleton()->SendGetClipboard(types, aWhichClipboard,
aWindowContext, &transferable);
return nsContentUtils::IPCTransferableDataToTransferable(
transferable, false /* aAddDataFlavor */, 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,
mozilla::dom::WindowContext* aRequestingWindowContext,
nsIPrincipal* aRequestingPrincipal,
nsIAsyncClipboardGetCallback* aCallback) {
if (!aCallback || !aRequestingPrincipal || 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,
aRequestingWindowContext,
WrapNotNull(aRequestingPrincipal))
->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);
return NS_OK;
}
NS_IMETHODIMP
nsClipboardProxy::HasDataMatchingFlavors(const nsTArray<nsCString>& aFlavorList,
int32_t aWhichClipboard,
bool* aHasType) {
*aHasType = false;
ContentChild::GetSingleton()->SendClipboardHasType(aFlavorList,
aWhichClipboard, aHasType);
return NS_OK;
}
NS_IMETHODIMP
nsClipboardProxy::IsClipboardTypeSupported(int32_t aWhichClipboard,
bool* aIsSupported) {
switch (aWhichClipboard) {
case kGlobalClipboard:
// We always support the global clipboard.
*aIsSupported = true;
return NS_OK;
case kSelectionClipboard:
*aIsSupported = mClipboardCaps.supportsSelectionClipboard();
return NS_OK;
case kFindClipboard:
*aIsSupported = mClipboardCaps.supportsFindClipboard();
return NS_OK;
case kSelectionCache:
*aIsSupported = mClipboardCaps.supportsSelectionCache();
return NS_OK;
}
*aIsSupported = false;
return NS_OK;
}
void nsClipboardProxy::SetCapabilities(
const ClipboardCapabilities& aClipboardCaps) {
mClipboardCaps = aClipboardCaps;
}