From a14b5b8d7f773848792b32dc85f806eb0576176a Mon Sep 17 00:00:00 2001 From: Edgar Chen Date: Wed, 13 Mar 2024 01:07:17 +0000 Subject: [PATCH] Bug 1858627 - Add nsIClipboard API to return nsIAsyncSetClipboardData synchrously; r=ipc-reviewers,nika This is a sync version of `nsIClipboard.asyncGetData`, which can be used for synchronous clipboard APIs, e.g. DataTransfer, to support the clipboard seqence number concept, see bug 1879401. Depends on D201657 Differential Revision: https://phabricator.services.mozilla.com/D201364 --- dom/ipc/ContentParent.cpp | 42 +++++ dom/ipc/ContentParent.h | 5 + dom/ipc/PContent.ipdl | 5 + ipc/ipdl/sync-messages.ini | 16 +- widget/nsBaseClipboard.cpp | 142 +++++++++++----- widget/nsBaseClipboard.h | 9 ++ widget/nsClipboardProxy.cpp | 29 ++++ widget/nsIClipboard.idl | 25 +++ widget/tests/chrome.toml | 13 +- widget/tests/clipboard_helper.js | 7 + ...file_test_clipboard_getDataSnapshotSync.js | 153 ++++++++++++++++++ widget/tests/mochitest.toml | 6 +- .../test_clipboard_getDataSnapshotSync.html | 19 +++ ..._clipboard_getDataSnapshotSync_chrome.html | 19 +++ 14 files changed, 441 insertions(+), 49 deletions(-) create mode 100644 widget/tests/file_test_clipboard_getDataSnapshotSync.js create mode 100644 widget/tests/test_clipboard_getDataSnapshotSync.html create mode 100644 widget/tests/test_clipboard_getDataSnapshotSync_chrome.html diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 240ba41b96113..980f9a38bfc66 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -3647,6 +3647,48 @@ mozilla::ipc::IPCResult ContentParent::RecvGetClipboardAsync( return IPC_OK(); } +mozilla::ipc::IPCResult ContentParent::RecvGetClipboardDataSnapshotSync( + nsTArray&& aTypes, const int32_t& aWhichClipboard, + const MaybeDiscarded& aRequestingWindowContext, + ClipboardReadRequestOrError* aRequestOrError) { + // If the requesting context has been discarded, cancel the paste. + if (aRequestingWindowContext.IsDiscarded()) { + *aRequestOrError = NS_ERROR_FAILURE; + return IPC_OK(); + } + + RefPtr requestingWindow = + aRequestingWindowContext.get_canonical(); + if (requestingWindow && requestingWindow->GetContentParent() != this) { + return IPC_FAIL( + this, "attempt to paste into WindowContext loaded in another process"); + } + + nsCOMPtr clipboard(do_GetService(kCClipboardCID)); + if (!clipboard) { + *aRequestOrError = NS_ERROR_FAILURE; + return IPC_OK(); + } + + nsCOMPtr asyncGetClipboardData; + nsresult rv = + clipboard->GetDataSnapshotSync(aTypes, aWhichClipboard, requestingWindow, + getter_AddRefs(asyncGetClipboardData)); + if (NS_FAILED(rv)) { + *aRequestOrError = rv; + return IPC_OK(); + } + + auto result = CreateClipboardReadRequest(*this, *asyncGetClipboardData); + if (result.isErr()) { + *aRequestOrError = result.unwrapErr(); + return IPC_OK(); + } + + *aRequestOrError = result.unwrap(); + return IPC_OK(); +} + already_AddRefed ContentParent::AllocPClipboardWriteRequestParent( const int32_t& aClipboardType) { diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index f17d112d093d9..a2762af245ccc 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -974,6 +974,11 @@ class ContentParent final : public PContentParent, mozilla::NotNull aRequestingPrincipal, GetClipboardAsyncResolver&& aResolver); + mozilla::ipc::IPCResult RecvGetClipboardDataSnapshotSync( + nsTArray&& aTypes, const int32_t& aWhichClipboard, + const MaybeDiscarded& aRequestingWindowContext, + ClipboardReadRequestOrError* aRequestOrError); + already_AddRefed AllocPClipboardWriteRequestParent(const int32_t& aClipboardType); diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 2ff049aecd0b2..f84ac4f48240e 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -1238,6 +1238,11 @@ parent: nsIPrincipal aRequestingPrincipal) returns (ClipboardReadRequestOrError aClipboardReadRequestOrError); + // Requests getting data from clipboard. + sync GetClipboardDataSnapshotSync(nsCString[] aTypes, int32_t aWhichClipboard, + MaybeDiscardedWindowContext aRequestingWindowContext) + returns (ClipboardReadRequestOrError aClipboardReadRequestOrError); + // Clears the clipboard. async EmptyClipboard(int32_t aWhichClipboard); diff --git a/ipc/ipdl/sync-messages.ini b/ipc/ipdl/sync-messages.ini index 63fac60c8dc0a..2d9c8d9ae8dd8 100644 --- a/ipc/ipdl/sync-messages.ini +++ b/ipc/ipdl/sync-messages.ini @@ -19,6 +19,16 @@ description = Only used by gtests [PQuotaTest::TryInspect_Success_CustomErr_IpcFail] description = Only used by gtests +# Clipboard +[PContent::GetClipboard] +description = Legacy synchronous clipboard API +[PContent::ClipboardHasType] +description = Legacy synchronous clipboard API +[PContent::GetExternalClipboardFormats] +description = Retrieve supported clipboard formats synchronously +[PContent::GetClipboardDataSnapshotSync] +description = Legacy synchronous clipboard API + # The rest [PHeapSnapshotTempFileHelper::OpenHeapSnapshotTempFile] description = legacy sync IPC - please add detailed description @@ -56,12 +66,6 @@ description = JS MessageManager implementation description = legacy sync IPC - please add detailed description [PContent::PURLClassifier] description = legacy sync IPC - please add detailed description -[PContent::GetClipboard] -description = Legacy synchronous clipboard API -[PContent::ClipboardHasType] -description = Legacy synchronous clipboard API -[PContent::GetExternalClipboardFormats] -description = Retrieve supported clipboard formats synchronously [PContent::GetIconForExtension] description = legacy sync IPC - please add detailed description [PContent::BeginDriverCrashGuard] diff --git a/widget/nsBaseClipboard.cpp b/widget/nsBaseClipboard.cpp index 40af1de388613..4fdc1a2cd857e 100644 --- a/widget/nsBaseClipboard.cpp +++ b/widget/nsBaseClipboard.cpp @@ -812,56 +812,124 @@ NS_IMETHODIMP nsBaseClipboard::AsyncGetData( return NS_OK; } -void nsBaseClipboard::AsyncGetDataInternal( +already_AddRefed +nsBaseClipboard::MaybeCreateGetRequestFromClipboardCache( const nsTArray& aFlavorList, int32_t aClipboardType, - mozilla::dom::WindowContext* aRequestingWindowContext, - nsIAsyncClipboardGetCallback* aCallback) { - MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType)); + mozilla::dom::WindowContext* aRequestingWindowContext) { + MOZ_DIAGNOSTIC_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType)); - if (mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) { - // 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(aClipboardType)) { - nsITransferable* cachedTransferable = clipboardCache->GetTransferable(); - MOZ_ASSERT(cachedTransferable); - - nsTArray transferableFlavors; - if (NS_SUCCEEDED(cachedTransferable->FlavorsTransferableCanExport( - transferableFlavors))) { - nsTArray 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); - } - } - } + if (!mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) { + return nullptr; + } + + // 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. + ClipboardCache* clipboardCache = GetClipboardCacheIfValid(aClipboardType); + if (!clipboardCache) { + return nullptr; + } + + nsITransferable* cachedTransferable = clipboardCache->GetTransferable(); + MOZ_ASSERT(cachedTransferable); + + nsTArray transferableFlavors; + if (NS_FAILED(cachedTransferable->FlavorsTransferableCanExport( + transferableFlavors))) { + return nullptr; + } - // XXX Do we need to check system clipboard for the flavors that cannot - // be found in cache? - auto asyncGetClipboardData = mozilla::MakeRefPtr( - aClipboardType, clipboardCache->GetSequenceNumber(), - std::move(results), true, this, aRequestingWindowContext); - aCallback->OnSuccess(asyncGetClipboardData); - return; + nsTArray 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); } } + } - // 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. + // XXX Do we need to check system clipboard for the flavors that cannot + // be found in cache? + return mozilla::MakeAndAddRef( + aClipboardType, clipboardCache->GetSequenceNumber(), std::move(results), + true /* aFromCache */, this, aRequestingWindowContext); +} + +void nsBaseClipboard::AsyncGetDataInternal( + const nsTArray& aFlavorList, int32_t aClipboardType, + mozilla::dom::WindowContext* aRequestingWindowContext, + nsIAsyncClipboardGetCallback* aCallback) { + MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType)); + + if (nsCOMPtr asyncGetClipboardData = + MaybeCreateGetRequestFromClipboardCache(aFlavorList, aClipboardType, + aRequestingWindowContext)) { + aCallback->OnSuccess(asyncGetClipboardData); + return; } + // 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, aClipboardType, aCallback, kGetAvailableFlavorsRetryCount, aRequestingWindowContext); } +NS_IMETHODIMP nsBaseClipboard::GetDataSnapshotSync( + const nsTArray& aFlavorList, int32_t aWhichClipboard, + mozilla::dom::WindowContext* aRequestingWindowContext, + nsIAsyncGetClipboardData** _retval) { + MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard); + + *_retval = nullptr; + + if (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 (nsCOMPtr asyncGetClipboardData = + MaybeCreateGetRequestFromClipboardCache(aFlavorList, aWhichClipboard, + aRequestingWindowContext)) { + asyncGetClipboardData.forget(_retval); + return NS_OK; + } + + auto sequenceNumberOrError = + GetNativeClipboardSequenceNumber(aWhichClipboard); + if (sequenceNumberOrError.isErr()) { + MOZ_CLIPBOARD_LOG("%s: unable to get sequence number for clipboard %d.", + __FUNCTION__, aWhichClipboard); + return sequenceNumberOrError.unwrapErr(); + } + + nsTArray results; + for (const auto& flavor : aFlavorList) { + auto resultOrError = HasNativeClipboardDataMatchingFlavors( + AutoTArray{flavor}, aWhichClipboard); + if (resultOrError.isOk() && resultOrError.unwrap()) { + results.AppendElement(flavor); + } + } + + *_retval = + mozilla::MakeAndAddRef( + aWhichClipboard, sequenceNumberOrError.unwrap(), std::move(results), + false /* aFromCache */, this, aRequestingWindowContext) + .take(); + return NS_OK; +} + NS_IMETHODIMP nsBaseClipboard::EmptyClipboard(int32_t aWhichClipboard) { MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard); diff --git a/widget/nsBaseClipboard.h b/widget/nsBaseClipboard.h index 8f90be725a902..ffa68d6240d05 100644 --- a/widget/nsBaseClipboard.h +++ b/widget/nsBaseClipboard.h @@ -55,6 +55,10 @@ class nsBaseClipboard : public nsIClipboard { mozilla::dom::WindowContext* aRequestingWindowContext, nsIPrincipal* aRequestingPrincipal, nsIAsyncClipboardGetCallback* aCallback) override final; + NS_IMETHOD GetDataSnapshotSync( + const nsTArray& aFlavorList, int32_t aWhichClipboard, + mozilla::dom::WindowContext* aRequestingWindowContext, + nsIAsyncGetClipboardData** _retval) override final; NS_IMETHOD EmptyClipboard(int32_t aWhichClipboard) override final; NS_IMETHOD HasDataMatchingFlavors(const nsTArray& aFlavorList, int32_t aWhichClipboard, @@ -206,6 +210,11 @@ class nsBaseClipboard : public nsIClipboard { nsIPrincipal* aRequestingPrincipal, nsIAsyncClipboardGetCallback* aCallback); + already_AddRefed + MaybeCreateGetRequestFromClipboardCache( + const nsTArray& aFlavorList, int32_t aClipboardType, + mozilla::dom::WindowContext* aRequestingWindowContext); + // Track the pending request for each clipboard type separately. And only need // to track the latest request for each clipboard type as the prior pending // request will be canceled when a new request is made. diff --git a/widget/nsClipboardProxy.cpp b/widget/nsClipboardProxy.cpp index a79dd8ec93688..3b27d5954d460 100644 --- a/widget/nsClipboardProxy.cpp +++ b/widget/nsClipboardProxy.cpp @@ -239,6 +239,35 @@ NS_IMETHODIMP nsClipboardProxy::AsyncGetData( return NS_OK; } +NS_IMETHODIMP nsClipboardProxy::GetDataSnapshotSync( + const nsTArray& aFlavorList, int32_t aWhichClipboard, + mozilla::dom::WindowContext* aRequestingWindowContext, + nsIAsyncGetClipboardData** _retval) { + *_retval = nullptr; + + if (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* contentChild = ContentChild::GetSingleton(); + ClipboardReadRequestOrError requestOrError; + contentChild->SendGetClipboardDataSnapshotSync( + aFlavorList, aWhichClipboard, aRequestingWindowContext, &requestOrError); + auto result = CreateAsyncGetClipboardDataProxy(std::move(requestOrError)); + if (result.isErr()) { + return result.unwrapErr(); + } + + result.unwrap().forget(_retval); + return NS_OK; +} + NS_IMETHODIMP nsClipboardProxy::EmptyClipboard(int32_t aWhichClipboard) { ContentChild::GetSingleton()->SendEmptyClipboard(aWhichClipboard); diff --git a/widget/nsIClipboard.idl b/widget/nsIClipboard.idl index 5ed2d22600448..a34f0f9298d08 100644 --- a/widget/nsIClipboard.idl +++ b/widget/nsIClipboard.idl @@ -178,6 +178,31 @@ interface nsIClipboard : nsISupports in nsIPrincipal aRequestingPrincipal, in nsIAsyncClipboardGetCallback aCallback); + /** + * Requests getting data 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 aRequestingWindowContext [optional] + * The window context window that is requesting the clipboard, which is + * used for content analysis. Passing null means that the content is + * exempt from content analysis. (for example, scripted clipboard read by + * system code) This parameter should not be null when calling this from a + * content process. + * @return nsIAsyncSetClipboardData if successful. + * @throws if the request can not be made. + */ + nsIAsyncGetClipboardData getDataSnapshotSync(in Array aFlavorList, + in long aWhichClipboard, + [optional] in WindowContext aRequestingWindowContext); + /** * This empties the clipboard and notifies the clipboard owner. * This empties the "logical" clipboard. It does not clear the native clipboard. diff --git a/widget/tests/chrome.toml b/widget/tests/chrome.toml index 67ec90c4d1313..8706a671853c1 100644 --- a/widget/tests/chrome.toml +++ b/widget/tests/chrome.toml @@ -9,9 +9,6 @@ support-files = [ # Privacy relevant -["test_bug1123480.xhtml"] -skip-if = ["win11_2009 && bits == 32"] - ["test_bug343416.xhtml"] skip-if = ["debug"] @@ -66,8 +63,8 @@ run-if = ["os == 'mac'"] # Cocoa widget test ["test_bug760802.xhtml"] -["test_clipboard_chrome.html"] -support-files = "file_test_clipboard.js" +["test_bug1123480.xhtml"] +skip-if = ["win11_2009 && bits == 32"] ["test_clipboard_asyncGetData_chrome.html"] support-files = "file_test_clipboard_asyncGetData.js" @@ -77,6 +74,12 @@ support-files = "file_test_clipboard_asyncSetData.js" ["test_clipboard_cache_chrome.html"] +["test_clipboard_chrome.html"] +support-files = "file_test_clipboard.js" + +["test_clipboard_getDataSnapshotSync_chrome.html"] +support-files = "file_test_clipboard_getDataSnapshotSync.js" + ["test_clipboard_owner_chrome.html"] ["test_composition_text_querycontent.xhtml"] diff --git a/widget/tests/clipboard_helper.js b/widget/tests/clipboard_helper.js index 96787468fbe77..0ba2f351825f5 100644 --- a/widget/tests/clipboard_helper.js +++ b/widget/tests/clipboard_helper.js @@ -157,6 +157,13 @@ function getClipboardData(aFlavor, aClipboardType) { } } +function getClipboardDataSnapshotSync(aClipboardType) { + return clipboard.getDataSnapshotSync( + ["text/plain", "text/html", "image/png"], + aClipboardType + ); +} + function asyncGetClipboardData(aClipboardType) { return new Promise((resolve, reject) => { try { diff --git a/widget/tests/file_test_clipboard_getDataSnapshotSync.js b/widget/tests/file_test_clipboard_getDataSnapshotSync.js new file mode 100644 index 0000000000000..92d7475e9cad1 --- /dev/null +++ b/widget/tests/file_test_clipboard_getDataSnapshotSync.js @@ -0,0 +1,153 @@ +/* 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_requestGetData_not_support() { + info(`Test getDataSnapshotSync request throwing on ${type}`); + SimpleTest.doesThrow( + () => clipboard.getDataSnapshotSync(["text/plain"], type), + "Passing unsupported clipboard type should throw" + ); + }); + return; + } + + add_task(async function test_clipboard_getDataSnapshotSync_throw() { + info(`Test getDataSnapshotSync request throwing on ${type}`); + SimpleTest.doesThrow( + () => clipboard.getDataSnapshotSync([], type), + "Passing empty flavor list should throw" + ); + }); + + add_task( + async function test_clipboard_getDataSnapshotSync_no_matched_flavor() { + info(`Test getDataSnapshotSync have no matched flavor on ${type}`); + cleanupAllClipboard(); + is( + getClipboardData("text/plain", type), + null, + "ensure clipboard is empty" + ); + + writeRandomStringToClipboard("text/plain", type); + let request = clipboard.getDataSnapshotSync(["text/html"], type); + isDeeply(request.flavorList, [], "Check flavorList"); + } + ); + + add_task(async function test_empty_data() { + info(`Test getDataSnapshotSync request with empty data on ${type}`); + cleanupAllClipboard(); + is(getClipboardData("text/plain", type), null, "ensure clipboard is empty"); + + let request = getClipboardDataSnapshotSync(type); + isDeeply(request.flavorList, [], "Check flavorList"); + await asyncClipboardRequestGetData(request, "text/plain", true).catch( + () => {} + ); + }); + + add_task(async function test_clipboard_getDataSnapshotSync_after_write() { + info(`Test getDataSnapshotSync request after write on ${type}`); + + let str = writeRandomStringToClipboard("text/plain", type); + let request = getClipboardDataSnapshotSync(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"); + }, + () => { + ok(true, "asyncClipboardRequestGetData should reject"); + } + ); + ok(!request.valid, "request should no longer be valid"); + + info(`check clipboard data again`); + request = getClipboardDataSnapshotSync(type); + isDeeply(request.flavorList, ["text/plain"], "Check flavorList"); + is( + await asyncClipboardRequestGetData(request, "text/plain"), + str, + "Check data" + ); + + cleanupAllClipboard(); + }); + + add_task(async function test_clipboard_getDataSnapshotSync_after_empty() { + info(`Test getDataSnapshotSync request after empty on ${type}`); + + let str = writeRandomStringToClipboard("text/plain", type); + let request = getClipboardDataSnapshotSync(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 + emptyClipboardData(type); + is(getClipboardData("text/plain", type), null, "ensure clipboard is empty"); + + await asyncClipboardRequestGetData(request, "text/plain").then( + () => { + ok(false, "asyncClipboardRequestGetData should not success"); + }, + () => { + ok(true, "asyncClipboardRequestGetData should reject"); + } + ); + ok(!request.valid, "request should no longer be valid"); + + info(`check clipboard data again`); + request = getClipboardDataSnapshotSync(type); + isDeeply(request.flavorList, [], "Check flavorList"); + + cleanupAllClipboard(); + }); +}); + +add_task(async function test_clipboard_getDataSnapshotSync_html_data() { + info(`Test getDataSnapshotSync request with html data`); + + const html_str = ``; + writeStringToClipboard(html_str, "text/html", clipboard.kGlobalClipboard); + + let request = getClipboardDataSnapshotSync(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") + ? `\n${html_str}\n\n` + : html_str, + "Check data" + ); + // Requesting a flavor that is not in the list should throw error. + await asyncClipboardRequestGetData(request, "text/plain", true).catch( + () => {} + ); +}); diff --git a/widget/tests/mochitest.toml b/widget/tests/mochitest.toml index 32dcf5c7955f5..4a4a9d2729549 100644 --- a/widget/tests/mochitest.toml +++ b/widget/tests/mochitest.toml @@ -25,12 +25,16 @@ skip-if = [ support-files = ["file_test_clipboard.js"] ["test_clipboard_asyncGetData.html"] -skip-if = ["display == 'wayland'"] # Bug 1864211 +skip-if = ["display == 'wayland'"] # Bug 1879835 support-files = ["file_test_clipboard_asyncGetData.js"] ["test_clipboard_asyncSetData.html"] support-files = ["file_test_clipboard_asyncSetData.js"] +["test_clipboard_getDataSnapshotSync.html"] +skip-if = ["display == 'wayland'"] # Bug 1879835 +support-files = "file_test_clipboard_getDataSnapshotSync.js" + ["test_contextmenu_by_mouse_on_unix.html"] run-if = ["os == 'linux'"] skip-if = ["headless"] # headless widget doesn't dispatch contextmenu event by mouse event. diff --git a/widget/tests/test_clipboard_getDataSnapshotSync.html b/widget/tests/test_clipboard_getDataSnapshotSync.html new file mode 100644 index 0000000000000..e800f6a1b09a9 --- /dev/null +++ b/widget/tests/test_clipboard_getDataSnapshotSync.html @@ -0,0 +1,19 @@ + + + + +Test for Bug 1858627 + + + + + +

+ +

+
+
+
+
diff --git a/widget/tests/test_clipboard_getDataSnapshotSync_chrome.html b/widget/tests/test_clipboard_getDataSnapshotSync_chrome.html
new file mode 100644
index 0000000000000..fbc283ccb3ac9
--- /dev/null
+++ b/widget/tests/test_clipboard_getDataSnapshotSync_chrome.html
@@ -0,0 +1,19 @@
+
+
+
+
+Test for Bug 1858627
+
+
+
+
+
+

+ +

+
+
+
+