diff --git a/config-overrides.js b/config-overrides.js index 1f6431a..32654fa 100644 --- a/config-overrides.js +++ b/config-overrides.js @@ -89,7 +89,7 @@ const userscriptWebpack = (config, env) => { // @grant GM.getValue // @grant GM.deleteValue // @grant GM.info -// @grant unsafeWindow +// @inject-into content // @connect translate.googleapis.com // @connect api-edge.cognitive.microsofttranslator.com // @connect edge.microsoft.com diff --git a/package.json b/package.json index 0a3090e..eb843a2 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,7 @@ "react-app/jest" ], "globals": { - "GM": true, - "unsafeWindow": true + "GM": true } }, "browserslist": { diff --git a/src/libs/fetch.js b/src/libs/fetch.js index dc15e40..5a941aa 100644 --- a/src/libs/fetch.js +++ b/src/libs/fetch.js @@ -19,7 +19,7 @@ import { msAuth } from "./auth"; * @param {*} init * @returns */ -const fetchGM = async (input, { method = "GET", headers, body } = {}) => +export const fetchGM = async (input, { method = "GET", headers, body } = {}) => new Promise((resolve, reject) => { GM.xmlHttpRequest({ method, @@ -74,11 +74,21 @@ const fetchApi = async ({ input, init = {}, translator, token }) => { } if (isGm) { - const connects = GM?.info?.script?.connects || []; + let info; + if (window.KISS_GM) { + info = await window.KISS_GM.getInfo(); + } else { + info = GM.info; + } + const connects = info?.script?.connects || []; const url = new URL(input); const isSafe = connects.find((item) => url.hostname.endsWith(item)); if (isSafe) { - return fetchGM(input, init); + if (window.KISS_GM) { + return window.KISS_GM.fetch(input, init); + } else { + return fetchGM(input, init); + } } } return fetch(input, init); diff --git a/src/libs/gm.js b/src/libs/gm.js index c2a65ea..e84ab41 100644 --- a/src/libs/gm.js +++ b/src/libs/gm.js @@ -1,8 +1,22 @@ -export const injectScript = ({ eventName }) => { - window.callGM = ({ action, args }, timeout = 5000) => +import { fetchGM } from "./fetch"; + +/** + * 注入页面的脚本,请求并接受GM接口信息 + * @param {*} param0 + */ +export const injectScript = (ping) => { + const MSG_GM_xmlHttpRequest = "xmlHttpRequest"; + const MSG_GM_setValue = "setValue"; + const MSG_GM_getValue = "getValue"; + const MSG_GM_deleteValue = "deleteValue"; + const MSG_GM_info = "info"; + let GM_info; + + const promiseGM = (action, args, timeout = 5000) => new Promise((resolve, reject) => { + const pong = btoa(Math.random()).slice(3, 11); const handleEvent = (e) => { - window.removeEventListener(eventName + "_pong", handleEvent); + window.removeEventListener(pong, handleEvent); const { data, error } = e.detail; if (error) { reject(new Error(error)); @@ -11,13 +25,73 @@ export const injectScript = ({ eventName }) => { } }; - window.addEventListener(eventName + "_pong", handleEvent); + window.addEventListener(pong, handleEvent); window.dispatchEvent( - new CustomEvent(eventName + "_ping", { action, args }) + new CustomEvent(ping, { detail: { action, args, pong } }) ); setTimeout(() => { - handleEvent({ detail: { error: "timeout" } }); + window.removeEventListener(pong, handleEvent); + reject(new Error("timeout")); }, timeout); }); + + window.KISS_GM = { + fetch: (input, init) => promiseGM(MSG_GM_xmlHttpRequest, { input, init }), + setValue: (key, val) => promiseGM(MSG_GM_setValue, { key, val }), + getValue: (key) => promiseGM(MSG_GM_getValue, { key }), + deleteValue: (key) => promiseGM(MSG_GM_deleteValue, { key }), + getInfo: () => { + if (GM_info) { + return GM_info; + } + return promiseGM(MSG_GM_info); + }, + }; + window.APP_NAME = process.env.REACT_APP_NAME; +}; + +/** + * 监听并回应页面对GM接口的请求 + * @param {*} param0 + */ +export const handlePing = async (e) => { + const MSG_GM_xmlHttpRequest = "xmlHttpRequest"; + const MSG_GM_setValue = "setValue"; + const MSG_GM_getValue = "getValue"; + const MSG_GM_deleteValue = "deleteValue"; + const MSG_GM_info = "info"; + const { action, args, pong } = e.detail; + let res; + try { + switch (action) { + case MSG_GM_xmlHttpRequest: + const { input, init } = args; + res = await fetchGM(input, init); + break; + case MSG_GM_setValue: + const { key, val } = args; + await GM.setValue(key, val); + res = val; + break; + case MSG_GM_getValue: + res = await GM.getValue(args.key); + break; + case MSG_GM_deleteValue: + await GM.deleteValue(args.key); + res = "ok"; + break; + case MSG_GM_info: + res = GM.info; + break; + default: + throw new Error(`message action is unavailable: ${action}`); + } + + window.dispatchEvent(new CustomEvent(pong, { detail: { data: res } })); + } catch (err) { + window.dispatchEvent( + new CustomEvent(pong, { detail: { error: err.message } }) + ); + } }; diff --git a/src/libs/storage.js b/src/libs/storage.js index ecf9030..d4eaacb 100644 --- a/src/libs/storage.js +++ b/src/libs/storage.js @@ -4,8 +4,8 @@ async function set(key, val) { if (isExt) { await browser.storage.local.set({ [key]: val }); } else if (isGm) { - const oldValue = await GM.getValue(key); - await GM.setValue(key, val); + const oldValue = await (window.KISS_GM || GM).getValue(key); + await (window.KISS_GM || GM).setValue(key, val); window.dispatchEvent( new StorageEvent("storage", { key, @@ -31,7 +31,7 @@ async function get(key) { const val = await browser.storage.local.get([key]); return val[key]; } else if (isGm) { - const val = await GM.getValue(key); + const val = await (window.KISS_GM || GM).getValue(key); return val; } return window.localStorage.getItem(key); @@ -41,8 +41,8 @@ async function del(key) { if (isExt) { await browser.storage.local.remove([key]); } else if (isGm) { - const oldValue = await GM.getValue(key); - await GM.deleteValue(key); + const oldValue = await (window.KISS_GM || GM).getValue(key); + await (window.KISS_GM || GM).deleteValue(key); window.dispatchEvent( new StorageEvent("storage", { key, diff --git a/src/userscript.js b/src/userscript.js index cbf1217..1004231 100644 --- a/src/userscript.js +++ b/src/userscript.js @@ -9,6 +9,7 @@ import { trySyncAllSubRules } from "./libs/rules"; import { isGm } from "./libs/browser"; import { MSG_TRANS_TOGGLE, MSG_TRANS_PUTRULE } from "./config"; import { isIframe } from "./libs/iframe"; +import { genEventName, handlePing, injectScript } from "./libs/gm"; /** * 入口函数 @@ -20,8 +21,11 @@ const init = async () => { document.location.href.includes(process.env.REACT_APP_OPTIONSPAGE) || document.location.href.includes(process.env.REACT_APP_OPTIONSPAGE2) ) { - unsafeWindow.GM = GM; - unsafeWindow.APP_NAME = process.env.REACT_APP_NAME; + // unsafeWindow.GM = GM; + // unsafeWindow.APP_NAME = process.env.REACT_APP_NAME; + const ping = btoa(Math.random()).slice(3, 11); + window.addEventListener(ping, handlePing); + window.eval(`(${injectScript})("${ping}")`); return; }