Bug 1942006 - Upstream a variety of Servo-specific code from Servo's downstream fork...
[gecko.git] / widget / nsClipboardProxy.cpp
blob7e701e6b035b4697bda4a92dadd37119f3a3b4ee
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"
9 #endif
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"
24 #include "nsCOMPtr.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) {}
38 NS_IMETHODIMP
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();
45 #endif
47 ContentChild* child = ContentChild::GetSingleton();
48 IPCTransferable ipcTransferable;
49 nsContentUtils::TransferableToIPCTransferable(aTransferable, &ipcTransferable,
50 false, nullptr);
51 child->SendSetClipboard(std::move(ipcTransferable), aWhichClipboard,
52 aWindowContext);
53 return NS_OK;
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);
66 return NS_OK;
69 NS_IMETHODIMP
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;
94 } else {
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 */);
111 namespace {
113 class ClipboardDataSnapshotProxy final : public nsIClipboardDataSnapshot {
114 public:
115 explicit ClipboardDataSnapshotProxy(ClipboardReadRequestChild* aActor)
116 : mActor(aActor) {
117 MOZ_ASSERT(mActor);
120 NS_DECL_ISUPPORTS
121 NS_DECL_NSICLIPBOARDDATASNAPSHOT
123 private:
124 virtual ~ClipboardDataSnapshotProxy() {
125 MOZ_ASSERT(mActor);
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) {
137 MOZ_ASSERT(mActor);
138 *aOutResult = mActor->CanSend();
139 return NS_OK;
142 NS_IMETHODIMP ClipboardDataSnapshotProxy::GetFlavorList(
143 nsTArray<nsCString>& aFlavorList) {
144 MOZ_ASSERT(mActor);
145 aFlavorList.AppendElements(mActor->FlavorList());
146 return NS_OK;
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);
159 if (NS_FAILED(rv)) {
160 return rv;
163 MOZ_ASSERT(mActor);
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__,
177 /* resolve */
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());
185 return;
188 nsresult rv = nsContentUtils::IPCTransferableDataToTransferable(
189 aIpcTransferableDataOrError.get_IPCTransferableData(),
190 false /* aAddDataFlavor */, transferable,
191 false /* aFilterUnknownFlavors */);
192 if (NS_FAILED(rv)) {
193 callback->OnComplete(rv);
194 return;
197 callback->OnComplete(NS_OK);
199 /* reject */
200 [callback = nsCOMPtr{aCallback}](ResponseRejectReason aReason) {
201 callback->OnComplete(ResponseRejectReason::ActorDestroyed == aReason
202 ? NS_ERROR_NOT_AVAILABLE
203 : NS_ERROR_FAILURE);
206 return NS_OK;
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);
218 if (NS_FAILED(rv)) {
219 return rv;
222 MOZ_ASSERT(mActor);
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);
236 if (!success) {
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 */);
248 if (NS_FAILED(rv)) {
249 return rv;
252 return NS_OK;
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()));
268 if (NS_WARN_IF(
269 !ContentChild::GetSingleton()->BindPClipboardReadRequestEndpoint(
270 std::move(request.childEndpoint()), requestChild))) {
271 return Err(NS_ERROR_FAILURE);
274 return MakeRefPtr<ClipboardDataSnapshotProxy>(requestChild);
277 } // namespace
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__,
291 aWhichClipboard);
292 return NS_ERROR_FAILURE;
295 ContentChild::GetSingleton()
296 ->SendGetClipboardDataSnapshot(aFlavorList, aWhichClipboard,
297 aRequestingWindowContext,
298 WrapNotNull(aRequestingPrincipal))
299 ->Then(
300 GetMainThreadSerialEventTarget(), __func__,
301 /* resolve */
302 [callback = nsCOMPtr{aCallback}](
303 ClipboardReadRequestOrError&& aClipboardReadRequestOrError) {
304 auto result = CreateClipboardDataSnapshotProxy(
305 std::move(aClipboardReadRequestOrError));
306 if (result.isErr()) {
307 callback->OnError(result.unwrapErr());
308 return;
311 callback->OnSuccess(result.inspect());
313 /* reject */
314 [callback = nsCOMPtr{aCallback}](ResponseRejectReason aReason) {
315 callback->OnError(NS_ERROR_FAILURE);
317 return NS_OK;
320 NS_IMETHODIMP nsClipboardProxy::GetDataSnapshotSync(
321 const nsTArray<nsCString>& aFlavorList,
322 nsIClipboard::ClipboardType aWhichClipboard,
323 mozilla::dom::WindowContext* aRequestingWindowContext,
324 nsIClipboardDataSnapshot** _retval) {
325 *_retval = nullptr;
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__,
333 aWhichClipboard);
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);
345 if (!result) {
346 return NS_ERROR_FAILURE;
348 if (ipcTransferableDataOrError.type() ==
349 IPCTransferableDataOrError::Tnsresult) {
350 return ipcTransferableDataOrError.get_nsresult();
352 nsresult rv;
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);
361 auto snapshot =
362 mozilla::MakeRefPtr<nsBaseClipboard::ClipboardPopulatedDataSnapshot>(
363 trans);
364 snapshot.forget(_retval);
365 return NS_OK;
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);
377 return NS_OK;
380 NS_IMETHODIMP
381 nsClipboardProxy::EmptyClipboard(nsIClipboard::ClipboardType aWhichClipboard) {
382 ContentChild::GetSingleton()->SendEmptyClipboard(aWhichClipboard);
383 return NS_OK;
386 NS_IMETHODIMP
387 nsClipboardProxy::HasDataMatchingFlavors(
388 const nsTArray<nsCString>& aFlavorList,
389 nsIClipboard::ClipboardType aWhichClipboard, bool* aHasType) {
390 *aHasType = false;
392 ContentChild::GetSingleton()->SendClipboardHasType(aFlavorList,
393 aWhichClipboard, aHasType);
395 return NS_OK;
398 NS_IMETHODIMP
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;
405 return NS_OK;
406 case kSelectionClipboard:
407 *aIsSupported = mClipboardCaps.supportsSelectionClipboard();
408 return NS_OK;
409 case kFindClipboard:
410 *aIsSupported = mClipboardCaps.supportsFindClipboard();
411 return NS_OK;
412 case kSelectionCache:
413 *aIsSupported = mClipboardCaps.supportsSelectionCache();
414 return NS_OK;
417 *aIsSupported = false;
418 return NS_OK;
421 void nsClipboardProxy::SetCapabilities(
422 const ClipboardCapabilities& aClipboardCaps) {
423 mClipboardCaps = aClipboardCaps;