1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsClipboardProxy.h"
7 #if defined(ACCESSIBILITY) && defined(XP_WIN)
8 # include "mozilla/a11y/Compatibility.h"
10 #include "mozilla/ClipboardContentAnalysisChild.h"
11 #include "mozilla/ClipboardReadRequestChild.h"
12 #include "mozilla/ClipboardWriteRequestChild.h"
13 #include "mozilla/Components.h"
14 #include "mozilla/dom/ContentChild.h"
15 #include "mozilla/net/CookieJarSettings.h"
16 #include "mozilla/Maybe.h"
17 #include "mozilla/dom/WindowGlobalChild.h"
18 #include "mozilla/Unused.h"
19 #include "mozilla/SpinEventLoopUntil.h"
20 #include "nsArrayUtils.h"
21 #include "nsBaseClipboard.h"
22 #include "nsIContentAnalysis.h"
23 #include "nsISupportsPrimitives.h"
25 #include "nsComponentManagerUtils.h"
26 #include "nsXULAppAPI.h"
27 #include "nsContentUtils.h"
28 #include "PermissionMessageUtils.h"
30 using mozilla::ipc::ResponseRejectReason
;
31 using namespace mozilla
;
32 using namespace mozilla::dom
;
34 NS_IMPL_ISUPPORTS(nsClipboardProxy
, nsIClipboard
, nsIClipboardProxy
)
36 nsClipboardProxy::nsClipboardProxy() : mClipboardCaps(false, false, false) {}
39 nsClipboardProxy::SetData(nsITransferable
* aTransferable
,
40 nsIClipboardOwner
* anOwner
,
41 nsIClipboard::ClipboardType aWhichClipboard
,
42 mozilla::dom::WindowContext
* aWindowContext
) {
43 #if defined(ACCESSIBILITY) && defined(XP_WIN)
44 a11y::Compatibility::SuppressA11yForClipboardCopy();
47 ContentChild
* child
= ContentChild::GetSingleton();
48 IPCTransferable ipcTransferable
;
49 nsContentUtils::TransferableToIPCTransferable(aTransferable
, &ipcTransferable
,
51 child
->SendSetClipboard(std::move(ipcTransferable
), aWhichClipboard
,
56 NS_IMETHODIMP
nsClipboardProxy::AsyncSetData(
57 nsIClipboard::ClipboardType aWhichClipboard
,
58 mozilla::dom::WindowContext
* aSettingWindowContext
,
59 nsIAsyncClipboardRequestCallback
* aCallback
,
60 nsIAsyncSetClipboardData
** _retval
) {
61 RefPtr
<ClipboardWriteRequestChild
> request
=
62 MakeRefPtr
<ClipboardWriteRequestChild
>(aCallback
);
63 ContentChild::GetSingleton()->SendPClipboardWriteRequestConstructor(
64 request
, aWhichClipboard
, aSettingWindowContext
);
65 request
.forget(_retval
);
70 nsClipboardProxy::GetData(nsITransferable
* aTransferable
,
71 nsIClipboard::ClipboardType aWhichClipboard
,
72 mozilla::dom::WindowContext
* aWindowContext
) {
73 MOZ_DIAGNOSTIC_ASSERT(aWindowContext
&& aWindowContext
->IsInProcess(),
74 "content clipboard reads must be associated with an "
75 "in-process WindowContext");
76 if (aWindowContext
->IsDiscarded()) {
77 return NS_ERROR_NOT_AVAILABLE
;
79 nsTArray
<nsCString
> types
;
80 aTransferable
->FlavorsTransferableCanImport(types
);
82 IPCTransferableDataOrError transferableOrError
;
83 if (MOZ_UNLIKELY(nsIContentAnalysis::MightBeActive())) {
84 RefPtr
<ClipboardContentAnalysisChild
> contentAnalysis
=
85 ClipboardContentAnalysisChild::GetOrCreate();
86 if (!contentAnalysis
) {
87 return NS_ERROR_FAILURE
;
89 if (!contentAnalysis
->SendGetClipboard(types
, aWhichClipboard
,
90 aWindowContext
->InnerWindowId(),
91 &transferableOrError
)) {
92 return NS_ERROR_FAILURE
;
95 if (!ContentChild::GetSingleton()->SendGetClipboard(
96 types
, aWhichClipboard
, aWindowContext
, &transferableOrError
)) {
97 return NS_ERROR_FAILURE
;
101 if (transferableOrError
.type() == IPCTransferableDataOrError::Tnsresult
) {
102 MOZ_ASSERT(NS_FAILED(transferableOrError
.get_nsresult()));
103 return transferableOrError
.get_nsresult();
106 return nsContentUtils::IPCTransferableDataToTransferable(
107 transferableOrError
.get_IPCTransferableData(), false /* aAddDataFlavor */,
108 aTransferable
, false /* aFilterUnknownFlavors */);
113 class ClipboardDataSnapshotProxy final
: public nsIClipboardDataSnapshot
{
115 explicit ClipboardDataSnapshotProxy(ClipboardReadRequestChild
* aActor
)
121 NS_DECL_NSICLIPBOARDDATASNAPSHOT
124 virtual ~ClipboardDataSnapshotProxy() {
126 if (mActor
->CanSend()) {
127 PClipboardReadRequestChild::Send__delete__(mActor
);
131 RefPtr
<ClipboardReadRequestChild
> mActor
;
134 NS_IMPL_ISUPPORTS(ClipboardDataSnapshotProxy
, nsIClipboardDataSnapshot
)
136 NS_IMETHODIMP
ClipboardDataSnapshotProxy::GetValid(bool* aOutResult
) {
138 *aOutResult
= mActor
->CanSend();
142 NS_IMETHODIMP
ClipboardDataSnapshotProxy::GetFlavorList(
143 nsTArray
<nsCString
>& aFlavorList
) {
145 aFlavorList
.AppendElements(mActor
->FlavorList());
149 NS_IMETHODIMP
ClipboardDataSnapshotProxy::GetData(
150 nsITransferable
* aTransferable
,
151 nsIAsyncClipboardRequestCallback
* aCallback
) {
152 if (!aTransferable
|| !aCallback
) {
153 return NS_ERROR_INVALID_ARG
;
156 // Get a list of flavors this transferable can import
157 nsTArray
<nsCString
> flavors
;
158 nsresult rv
= aTransferable
->FlavorsTransferableCanImport(flavors
);
164 // If the requested flavor is not in the list, throw an error.
165 for (const auto& flavor
: flavors
) {
166 if (!mActor
->FlavorList().Contains(flavor
)) {
167 return NS_ERROR_FAILURE
;
171 if (!mActor
->CanSend()) {
172 return aCallback
->OnComplete(NS_ERROR_NOT_AVAILABLE
);
175 mActor
->SendGetData(flavors
)->Then(
176 GetMainThreadSerialEventTarget(), __func__
,
178 [self
= RefPtr
{this}, callback
= nsCOMPtr
{aCallback
},
179 transferable
= nsCOMPtr
{aTransferable
}](
180 const IPCTransferableDataOrError
& aIpcTransferableDataOrError
) {
181 if (aIpcTransferableDataOrError
.type() ==
182 IPCTransferableDataOrError::Tnsresult
) {
183 MOZ_ASSERT(NS_FAILED(aIpcTransferableDataOrError
.get_nsresult()));
184 callback
->OnComplete(aIpcTransferableDataOrError
.get_nsresult());
188 nsresult rv
= nsContentUtils::IPCTransferableDataToTransferable(
189 aIpcTransferableDataOrError
.get_IPCTransferableData(),
190 false /* aAddDataFlavor */, transferable
,
191 false /* aFilterUnknownFlavors */);
193 callback
->OnComplete(rv
);
197 callback
->OnComplete(NS_OK
);
200 [callback
= nsCOMPtr
{aCallback
}](ResponseRejectReason aReason
) {
201 callback
->OnComplete(ResponseRejectReason::ActorDestroyed
== aReason
202 ? NS_ERROR_NOT_AVAILABLE
209 NS_IMETHODIMP
ClipboardDataSnapshotProxy::GetDataSync(
210 nsITransferable
* aTransferable
) {
211 if (!aTransferable
) {
212 return NS_ERROR_INVALID_ARG
;
215 // Get a list of flavors this transferable can import
216 nsTArray
<nsCString
> flavors
;
217 nsresult rv
= aTransferable
->FlavorsTransferableCanImport(flavors
);
223 // If the requested flavor is not in the list, throw an error.
224 for (const auto& flavor
: flavors
) {
225 if (!mActor
->FlavorList().Contains(flavor
)) {
226 return NS_ERROR_FAILURE
;
230 if (!mActor
->CanSend()) {
231 return NS_ERROR_NOT_AVAILABLE
;
234 IPCTransferableDataOrError ipcTransferableDataOrError
;
235 bool success
= mActor
->SendGetDataSync(flavors
, &ipcTransferableDataOrError
);
237 return NS_ERROR_NOT_AVAILABLE
;
239 if (ipcTransferableDataOrError
.type() ==
240 IPCTransferableDataOrError::Tnsresult
) {
241 MOZ_ASSERT(NS_FAILED(ipcTransferableDataOrError
.get_nsresult()));
242 return ipcTransferableDataOrError
.get_nsresult();
244 rv
= nsContentUtils::IPCTransferableDataToTransferable(
245 ipcTransferableDataOrError
.get_IPCTransferableData(),
246 false /* aAddDataFlavor */, aTransferable
,
247 false /* aFilterUnknownFlavors */);
255 static Result
<RefPtr
<ClipboardDataSnapshotProxy
>, nsresult
>
256 CreateClipboardDataSnapshotProxy(
257 ClipboardReadRequestOrError
&& aClipboardReadRequestOrError
) {
258 if (aClipboardReadRequestOrError
.type() ==
259 ClipboardReadRequestOrError::Tnsresult
) {
260 MOZ_ASSERT(NS_FAILED(aClipboardReadRequestOrError
.get_nsresult()));
261 return Err(aClipboardReadRequestOrError
.get_nsresult());
264 ClipboardReadRequest
& request
=
265 aClipboardReadRequestOrError
.get_ClipboardReadRequest();
266 auto requestChild
= MakeRefPtr
<ClipboardReadRequestChild
>(
267 std::move(request
.availableTypes()));
269 !ContentChild::GetSingleton()->BindPClipboardReadRequestEndpoint(
270 std::move(request
.childEndpoint()), requestChild
))) {
271 return Err(NS_ERROR_FAILURE
);
274 return MakeRefPtr
<ClipboardDataSnapshotProxy
>(requestChild
);
279 NS_IMETHODIMP
nsClipboardProxy::GetDataSnapshot(
280 const nsTArray
<nsCString
>& aFlavorList
,
281 nsIClipboard::ClipboardType aWhichClipboard
,
282 mozilla::dom::WindowContext
* aRequestingWindowContext
,
283 nsIPrincipal
* aRequestingPrincipal
,
284 nsIClipboardGetDataSnapshotCallback
* aCallback
) {
285 if (!aCallback
|| !aRequestingPrincipal
|| aFlavorList
.IsEmpty()) {
286 return NS_ERROR_INVALID_ARG
;
289 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
290 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__
,
292 return NS_ERROR_FAILURE
;
295 ContentChild::GetSingleton()
296 ->SendGetClipboardDataSnapshot(aFlavorList
, aWhichClipboard
,
297 aRequestingWindowContext
,
298 WrapNotNull(aRequestingPrincipal
))
300 GetMainThreadSerialEventTarget(), __func__
,
302 [callback
= nsCOMPtr
{aCallback
}](
303 ClipboardReadRequestOrError
&& aClipboardReadRequestOrError
) {
304 auto result
= CreateClipboardDataSnapshotProxy(
305 std::move(aClipboardReadRequestOrError
));
306 if (result
.isErr()) {
307 callback
->OnError(result
.unwrapErr());
311 callback
->OnSuccess(result
.inspect());
314 [callback
= nsCOMPtr
{aCallback
}](ResponseRejectReason aReason
) {
315 callback
->OnError(NS_ERROR_FAILURE
);
320 NS_IMETHODIMP
nsClipboardProxy::GetDataSnapshotSync(
321 const nsTArray
<nsCString
>& aFlavorList
,
322 nsIClipboard::ClipboardType aWhichClipboard
,
323 mozilla::dom::WindowContext
* aRequestingWindowContext
,
324 nsIClipboardDataSnapshot
** _retval
) {
327 if (aFlavorList
.IsEmpty()) {
328 return NS_ERROR_INVALID_ARG
;
331 if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard
)) {
332 MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__
,
334 return NS_ERROR_FAILURE
;
336 if (MOZ_UNLIKELY(nsIContentAnalysis::MightBeActive())) {
337 // If Content Analysis is active we want to fetch all the clipboard data
338 // up front since we need to analyze it anyway.
339 RefPtr
<ClipboardContentAnalysisChild
> contentAnalysis
=
340 ClipboardContentAnalysisChild::GetOrCreate();
341 IPCTransferableDataOrError ipcTransferableDataOrError
;
342 bool result
= contentAnalysis
->SendGetAllClipboardDataSync(
343 aFlavorList
, aWhichClipboard
, aRequestingWindowContext
->InnerWindowId(),
344 &ipcTransferableDataOrError
);
346 return NS_ERROR_FAILURE
;
348 if (ipcTransferableDataOrError
.type() ==
349 IPCTransferableDataOrError::Tnsresult
) {
350 return ipcTransferableDataOrError
.get_nsresult();
353 nsCOMPtr
<nsITransferable
> trans
=
354 do_CreateInstance("@mozilla.org/widget/transferable;1", &rv
);
355 NS_ENSURE_SUCCESS(rv
, rv
);
356 trans
->Init(nullptr);
357 rv
= nsContentUtils::IPCTransferableDataToTransferable(
358 ipcTransferableDataOrError
.get_IPCTransferableData(),
359 true /* aAddDataFlavor */, trans
, false /* aFilterUnknownFlavors */);
360 NS_ENSURE_SUCCESS(rv
, rv
);
362 mozilla::MakeRefPtr
<nsBaseClipboard::ClipboardPopulatedDataSnapshot
>(
364 snapshot
.forget(_retval
);
367 ClipboardReadRequestOrError requestOrError
;
368 ContentChild
* contentChild
= ContentChild::GetSingleton();
369 contentChild
->SendGetClipboardDataSnapshotSync(
370 aFlavorList
, aWhichClipboard
, aRequestingWindowContext
, &requestOrError
);
371 auto result
= CreateClipboardDataSnapshotProxy(std::move(requestOrError
));
372 if (result
.isErr()) {
373 return result
.unwrapErr();
376 result
.unwrap().forget(_retval
);
381 nsClipboardProxy::EmptyClipboard(nsIClipboard::ClipboardType aWhichClipboard
) {
382 ContentChild::GetSingleton()->SendEmptyClipboard(aWhichClipboard
);
387 nsClipboardProxy::HasDataMatchingFlavors(
388 const nsTArray
<nsCString
>& aFlavorList
,
389 nsIClipboard::ClipboardType aWhichClipboard
, bool* aHasType
) {
392 ContentChild::GetSingleton()->SendClipboardHasType(aFlavorList
,
393 aWhichClipboard
, aHasType
);
399 nsClipboardProxy::IsClipboardTypeSupported(
400 nsIClipboard::ClipboardType aWhichClipboard
, bool* aIsSupported
) {
401 switch (aWhichClipboard
) {
402 case kGlobalClipboard
:
403 // We always support the global clipboard.
404 *aIsSupported
= true;
406 case kSelectionClipboard
:
407 *aIsSupported
= mClipboardCaps
.supportsSelectionClipboard();
410 *aIsSupported
= mClipboardCaps
.supportsFindClipboard();
412 case kSelectionCache
:
413 *aIsSupported
= mClipboardCaps
.supportsSelectionCache();
417 *aIsSupported
= false;
421 void nsClipboardProxy::SetCapabilities(
422 const ClipboardCapabilities
& aClipboardCaps
) {
423 mClipboardCaps
= aClipboardCaps
;