FIX: Fall back to clipboard.writeText if ClipboardItem not supported (#16419)
Firefox does not support window.ClipboardItem yet (it is behind a flag (dom.events.asyncClipboard.clipboardItem) as at version 87.) so we need to fall back to the normal non-async clipboard copy, that works in every browser except Safari. This commit also tests the clipboardCopyAsync function by stubbing out the clipboard on the window.navigator. This fixes an issue in the discourse-chat plugin, where the "Quote in Topic" button errored in Firefox.
This commit is contained in:
parent
f26d07c1ad
commit
cecdef83a8
|
@ -313,7 +313,7 @@ export function isAppleDevice() {
|
|||
// IE has no DOMNodeInserted so can not get this hack despite saying it is like iPhone
|
||||
// This will apply hack on all iDevices
|
||||
let caps = helperContext().capabilities;
|
||||
return caps.isIOS && !navigator.userAgent.match(/Trident/g);
|
||||
return caps.isIOS && !window.navigator.userAgent.match(/Trident/g);
|
||||
}
|
||||
|
||||
let iPadDetected = undefined;
|
||||
|
@ -321,8 +321,8 @@ let iPadDetected = undefined;
|
|||
export function isiPad() {
|
||||
if (iPadDetected === undefined) {
|
||||
iPadDetected =
|
||||
navigator.userAgent.match(/iPad/g) &&
|
||||
!navigator.userAgent.match(/Trident/g);
|
||||
window.navigator.userAgent.match(/iPad/g) &&
|
||||
!window.navigator.userAgent.match(/Trident/g);
|
||||
}
|
||||
return iPadDetected;
|
||||
}
|
||||
|
@ -512,8 +512,8 @@ export function translateModKey(string) {
|
|||
export function clipboardCopy(text) {
|
||||
// Use the Async Clipboard API when available.
|
||||
// Requires a secure browsing context (i.e. HTTPS)
|
||||
if (navigator.clipboard) {
|
||||
return navigator.clipboard.writeText(text).catch(function (err) {
|
||||
if (window.navigator.clipboard) {
|
||||
return window.navigator.clipboard.writeText(text).catch(function (err) {
|
||||
throw err !== undefined
|
||||
? err
|
||||
: new DOMException("The request is not allowed", "NotAllowedError");
|
||||
|
@ -532,12 +532,29 @@ export function clipboardCopy(text) {
|
|||
//
|
||||
// Note that the promise passed in should return a Blob with type of
|
||||
// text/plain.
|
||||
export function clipboardCopyAsync(promise) {
|
||||
export function clipboardCopyAsync(functionReturningPromise) {
|
||||
// Use the Async Clipboard API when available.
|
||||
// Requires a secure browsing context (i.e. HTTPS)
|
||||
if (navigator.clipboard) {
|
||||
return navigator.clipboard
|
||||
.write([new window.ClipboardItem({ "text/plain": promise() })])
|
||||
if (window.navigator.clipboard) {
|
||||
// Firefox does not support window.ClipboardItem yet (it is behind
|
||||
// a flag (dom.events.asyncClipboard.clipboardItem) as at version 87.)
|
||||
// so we need to fall back to the normal non-async clipboard copy, that
|
||||
// works in every browser except Safari.
|
||||
//
|
||||
// TODO: (martin) Look at this on 2022-07-01 to see if support has
|
||||
// changed.
|
||||
if (!window.ClipboardItem) {
|
||||
return functionReturningPromise().then((textBlob) => {
|
||||
return textBlob.text().then((text) => {
|
||||
return clipboardCopy(text);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return window.navigator.clipboard
|
||||
.write([
|
||||
new window.ClipboardItem({ "text/plain": functionReturningPromise() }),
|
||||
])
|
||||
.catch(function (err) {
|
||||
throw err !== undefined
|
||||
? err
|
||||
|
@ -546,7 +563,7 @@ export function clipboardCopyAsync(promise) {
|
|||
}
|
||||
|
||||
// ...Otherwise, use document.execCommand() fallback
|
||||
return promise().then((textBlob) => {
|
||||
return functionReturningPromise().then((textBlob) => {
|
||||
textBlob.text().then((text) => {
|
||||
return clipboardCopyFallback(text);
|
||||
});
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { Promise } from "rsvp";
|
||||
import {
|
||||
avatarImg,
|
||||
avatarUrl,
|
||||
caretRowCol,
|
||||
clipboardCopyAsync,
|
||||
defaultHomepage,
|
||||
emailValid,
|
||||
escapeExpression,
|
||||
|
@ -15,9 +17,13 @@ import {
|
|||
slugify,
|
||||
toAsciiPrintable,
|
||||
} from "discourse/lib/utilities";
|
||||
import sinon from "sinon";
|
||||
import { test } from "qunit";
|
||||
import Handlebars from "handlebars";
|
||||
import { discourseModule } from "discourse/tests/helpers/qunit-helpers";
|
||||
import {
|
||||
chromeTest,
|
||||
discourseModule,
|
||||
} from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
discourseModule("Unit | Utilities", function () {
|
||||
test("escapeExpression", function (assert) {
|
||||
|
@ -283,3 +289,51 @@ discourseModule("Unit | Utilities", function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
discourseModule("Unit | Utilities | clipboard", function (hooks) {
|
||||
let mockClipboard;
|
||||
hooks.beforeEach(function () {
|
||||
mockClipboard = {
|
||||
writeText: sinon.stub().resolves(true),
|
||||
write: sinon.stub().resolves(true),
|
||||
};
|
||||
sinon.stub(window.navigator, "clipboard").get(() => mockClipboard);
|
||||
});
|
||||
|
||||
function getPromiseFunction() {
|
||||
return () =>
|
||||
new Promise((resolve) => {
|
||||
resolve(
|
||||
new Blob(["some text to copy"], {
|
||||
type: "text/plain",
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
test("clipboardCopyAsync - browser does not support window.ClipboardItem", async function (assert) {
|
||||
// without this check the stubbing will fail on Firefox
|
||||
if (window.ClipboardItem) {
|
||||
sinon.stub(window, "ClipboardItem").value(null);
|
||||
}
|
||||
|
||||
await clipboardCopyAsync(getPromiseFunction());
|
||||
assert.strictEqual(
|
||||
mockClipboard.writeText.calledWith("some text to copy"),
|
||||
true,
|
||||
"it writes to the clipboard using writeText instead of write"
|
||||
);
|
||||
});
|
||||
|
||||
chromeTest(
|
||||
"clipboardCopyAsync - browser does support window.ClipboardItem",
|
||||
async function (assert) {
|
||||
await clipboardCopyAsync(getPromiseFunction());
|
||||
assert.strictEqual(
|
||||
mockClipboard.write.called,
|
||||
true,
|
||||
"it writes to the clipboard using write"
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue