Skip to content

Commit d1a5cb1

Browse files
cyfung1031CodFrm
andauthored
♻️ 兼容 FF: GM_setClipboard (#928)
Co-authored-by: 王一之 <yz@ggnb.top>
1 parent 43bc40f commit d1a5cb1

File tree

4 files changed

+71
-29
lines changed

4 files changed

+71
-29
lines changed

src/app/service/content/gm_api/gm_api.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,7 +1278,19 @@ export default class GMApi extends GM_Base {
12781278
@GMContext.API({})
12791279
GM_setClipboard(data: string, info?: GMTypes.GMClipboardInfo, cb?: () => void) {
12801280
if (this.isInvalidContext()) return;
1281-
this.sendMessage("GM_setClipboard", [data, info])
1281+
// 物件参数意义不明。日后再检视特殊处理
1282+
// 未支持 TM4.19+ application/octet-stream
1283+
// 参考: https://github.com/Tampermonkey/tampermonkey/issues/1250
1284+
let mimetype: string | undefined;
1285+
if (typeof info === "object" && info?.mimetype) {
1286+
mimetype = info.mimetype;
1287+
} else {
1288+
mimetype = (typeof info === "string" ? info : info?.type) || "text/plain";
1289+
if (mimetype === "text") mimetype = "text/plain";
1290+
else if (mimetype === "html") mimetype = "text/html";
1291+
}
1292+
data = `${data}`; // 强制 string type
1293+
this.sendMessage("GM_setClipboard", [data, mimetype])
12821294
.then(() => {
12831295
if (typeof cb === "function") {
12841296
cb();
@@ -1294,7 +1306,11 @@ export default class GMApi extends GM_Base {
12941306
@GMContext.API({ depend: ["GM_setClipboard"] })
12951307
["GM.setClipboard"](data: string, info?: string | { type?: string; mimetype?: string }): Promise<void> {
12961308
if (this.isInvalidContext()) return new Promise<void>(() => {});
1297-
return this.sendMessage("GM_setClipboard", [data, info]);
1309+
return new Promise<void>((resolve) => {
1310+
this.GM_setClipboard(data, info, () => {
1311+
resolve();
1312+
});
1313+
});
12981314
}
12991315

13001316
@GMContext.API()

src/app/service/offscreen/gm_api.ts

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { BgGMXhr } from "@App/pkg/utils/xhr/bg_gm_xhr";
22
import type { IGetSender, Group } from "@Packages/message/server";
3+
import { mightPrepareSetClipboard, setClipboard } from "../service_worker/clipboard";
34

45
export default class GMApi {
56
constructor(private group: Group) {}
@@ -11,32 +12,12 @@ export default class GMApi {
1112
bgGmXhr.do();
1213
}
1314

14-
textarea: HTMLTextAreaElement = document.createElement("textarea");
15-
16-
clipboardData: { type?: string; data: string } | undefined;
17-
18-
async setClipboard({ data, type }: { data: string; type: string }) {
19-
this.clipboardData = {
20-
type,
21-
data,
22-
};
23-
this.textarea.focus();
24-
document.execCommand("copy", false, <any>null);
15+
async setClipboard({ data, mimetype }: { data: string; mimetype: string }) {
16+
setClipboard(data, mimetype);
2517
}
2618

2719
init() {
28-
this.textarea.style.display = "none";
29-
document.documentElement.appendChild(this.textarea);
30-
document.addEventListener("copy", (e: ClipboardEvent) => {
31-
if (!this.clipboardData || !e.clipboardData) {
32-
return;
33-
}
34-
e.preventDefault();
35-
const { type, data } = this.clipboardData;
36-
e.clipboardData.setData(type || "text/plain", data);
37-
this.clipboardData = undefined;
38-
});
39-
20+
mightPrepareSetClipboard();
4021
this.group.on("xmlHttpRequest", this.xmlHttpRequest.bind(this));
4122
this.group.on("setClipboard", this.setClipboard.bind(this));
4223
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
let textareaDOM: HTMLTextAreaElement | undefined;
2+
let customClipboardData: { mimetype: string; data: string } | undefined;
3+
4+
// 抽出成独立处理。日后有需要可以改成 chrome API
5+
export const setClipboard = (data: string, mimetype: string) => {
6+
if (!textareaDOM) {
7+
throw new Error("mightPrepareSetClipboard shall be called first.");
8+
}
9+
customClipboardData = {
10+
mimetype,
11+
data,
12+
};
13+
textareaDOM!.focus();
14+
document.execCommand("copy", false, <any>null);
15+
};
16+
17+
// 设置 setClipboard 相关DOM
18+
export const mightPrepareSetClipboard = () => {
19+
if (textareaDOM) {
20+
return;
21+
}
22+
if (typeof document !== "object") {
23+
throw new Error(
24+
"mightPrepareSetClipboard shall be only called in either Chrome offscreen or FF background script."
25+
);
26+
}
27+
textareaDOM = document.createElement("textarea") as HTMLTextAreaElement;
28+
textareaDOM.style.display = "none";
29+
document.documentElement.appendChild(textareaDOM);
30+
document.addEventListener("copy", (e: ClipboardEvent) => {
31+
if (!customClipboardData || !e?.clipboardData?.setData) {
32+
return;
33+
}
34+
e.preventDefault();
35+
const { mimetype, data } = customClipboardData;
36+
customClipboardData = undefined;
37+
e.clipboardData.setData(mimetype || "text/plain", data);
38+
});
39+
};

src/app/service/service_worker/gm_api/gm_api.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
} from "./gm_xhr";
4343
import { headerModifierMap, headersReceivedMap } from "./gm_xhr";
4444
import { BgGMXhr } from "@App/pkg/utils/xhr/bg_gm_xhr";
45+
import { mightPrepareSetClipboard, setClipboard } from "../clipboard";
4546

4647
let generatedUniqueMarkerIDs = "";
4748
let generatedUniqueMarkerIDWhen = "";
@@ -1246,10 +1247,15 @@ export default class GMApi {
12461247
}
12471248

12481249
@PermissionVerify.API()
1249-
async GM_setClipboard(request: GMApiRequest<[string, GMTypes.GMClipboardInfo?]>, _sender: IGetSender) {
1250-
const [data, type] = request.params;
1251-
const clipboardType = type || "text/plain";
1252-
await sendMessage(this.msgSender, "offscreen/gmApi/setClipboard", { data, type: clipboardType });
1250+
async GM_setClipboard(request: GMApiRequest<[string, string]>, _sender: IGetSender) {
1251+
const [data, mimetype] = request.params;
1252+
if (typeof document === "object" && document?.documentElement) {
1253+
// FF background script
1254+
mightPrepareSetClipboard();
1255+
setClipboard(data, mimetype);
1256+
} else {
1257+
await sendMessage(this.msgSender, "offscreen/gmApi/setClipboard", { data, mimetype });
1258+
}
12531259
}
12541260

12551261
@PermissionVerify.API()

0 commit comments

Comments
 (0)