From 32b1dea1fdb44531b98b1880761ec3fe1b570fe2 Mon Sep 17 00:00:00 2001 From: Weijie Zhao Date: Thu, 4 Aug 2022 18:47:03 +0800 Subject: [PATCH 1/8] add GM.getTab and GM.saveTab --- README.md | 5 ++ .../Resources/background.js | 80 +++++++++++++++++++ .../Resources/content.js | 27 +++++++ .../Userscripts Extension/Resources/page.js | 4 +- 4 files changed, 115 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f0f51a5c..64bece08 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,11 @@ Userscripts currently supports the following api methods. All methods are asynch - on success returns a promise resolved with an object indicating success - `GM.listValues()` - on success returns a promise resolved with an array of the key names of **presently set** values +- `GM.getTab()` + - on success returns a promise resolved with a object that is persistent as long as this tab is open +- `GM.saveTab(tabObj)` + - `tabObj: Any` + - on success returns a promise resolved with an object indicating success - `GM.openInTab(url, openInBackground)` - `url: String`, `openInBackground: Bool` - on success returns a promise resolved with the [tab data](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/Tab) for the tab just opened diff --git a/extension/Userscripts Extension/Resources/background.js b/extension/Userscripts Extension/Resources/background.js index f6290b83..22362075 100644 --- a/extension/Userscripts Extension/Resources/background.js +++ b/extension/Userscripts Extension/Resources/background.js @@ -5,6 +5,7 @@ // xhrs are only kept in this array when active, otherwise array is empty // that means it's ok that this var gets reset when the bg page unloads let xhrs = []; +let US_tabs = {}; /* global US_filename, US_uid */ // filename and uid will be available to functions at runtime @@ -158,6 +159,53 @@ const apis = { window.postMessage({id: US_uid, name: "API_SET_CLIPBOARD", data: data, type: type}); return undefined; }, + US_getTab() { + const pid = Math.random().toString(36).substring(1, 9); + return new Promise(resolve => { + const callback = e => { + if ( + e.data.pid !== pid + || e.data.id !== US_uid + || e.data.name !== "RESP_GET_TAB" + || e.data.filename !== US_filename + ) return; + const response = e.data.response; + resolve(response); + window.removeEventListener("message", callback); + }; + window.addEventListener("message", callback); + window.postMessage({ + id: US_uid, + pid: pid, + name: "API_GET_TAB", + filename: US_filename, + }); + }); + }, + US_saveTab(tab) { + const pid = Math.random().toString(36).substring(1, 9); + return new Promise(resolve => { + const callback = e => { + if ( + e.data.pid !== pid + || e.data.id !== US_uid + || e.data.name !== "RESP_SAVE_TAB" + || e.data.filename !== US_filename + ) return; + const response = e.data.response; + resolve(response); + window.removeEventListener("message", callback); + }; + window.addEventListener("message", callback); + window.postMessage({ + id: US_uid, + pid: pid, + name: "API_SAVE_TAB", + filename: US_filename, + tab: tab + }); + }); + }, // when xhr is called it sends a message to the content script // and adds it's own event listener to get responses from content script // each xhr has a unique id so it won't respond to different xhr @@ -405,6 +453,14 @@ function addApis({userscripts, uid, scriptHandler, scriptHandlerVersion}) { api += `\n${apis.US_setClipboardSync}`; api += "\nconst GM_setClipboard = US_setClipboardSync;"; break; + case "GM.getTab": + api += `\n${apis.US_getTab}`; + gmMethods.push("getTab: US_getTab"); + break; + case "GM.saveTab": + api += `\n${apis.US_saveTab}`; + gmMethods.push("saveTab: US_saveTab"); + break; case "GM_xmlhttpRequest": case "GM.xmlHttpRequest": if (!includedMethods.includes("xhr")) { @@ -713,6 +769,30 @@ browser.runtime.onMessage.addListener((request, sender, sendResponse) => { } break; } + case "API_GET_TAB": { + if (typeof sender.tab != 'undefined') { + if (typeof US_tabs[sender.tab.id] == 'undefined') US_tabs[sender.tab.id] = { storage: {} }; + let tab = US_tabs[sender.tab.id]; + sendResponse(tab); + } else { + console.error("unable to deliver tab due to empty tab id"); + sendResponse(null); + } + break; + } + case "API_SAVE_TAB": { + if (typeof sender.tab != 'undefined') { + let tab = {}; + for (let k in request.tab) { + tab[k] = request.tab[k]; + }; + US_tabs[sender.tab.id] = tab; + } else { + console.error("unable to save tab due to empty tab id"); + } + sendResponse({}); + break; + } case "USERSCRIPT_INSTALL_00": case "USERSCRIPT_INSTALL_01": case "USERSCRIPT_INSTALL_02": { diff --git a/extension/Userscripts Extension/Resources/content.js b/extension/Userscripts Extension/Resources/content.js index ffc483e7..b08207a6 100644 --- a/extension/Userscripts Extension/Resources/content.js +++ b/extension/Userscripts Extension/Resources/content.js @@ -245,6 +245,33 @@ function handleApiMessages(e) { window.postMessage(respMessage); }); break; + case "API_GET_TAB": + message = { + name: name, + filename: e.data.filename, + pid: pid, + }; + browser.runtime.sendMessage(message, response => { + const undef = response === `undefined--${pid}`; + respMessage.response = undef ? undefined : response; + respMessage.filename = e.data.filename; + window.postMessage(respMessage); + }); + break; + case "API_SAVE_TAB": + message = { + name: name, + filename: e.data.filename, + pid: pid, + tab: e.data.tab + }; + browser.runtime.sendMessage(message, response => { + const undef = response === `undefined--${pid}`; + respMessage.response = undef ? undefined : response; + respMessage.filename = e.data.filename; + window.postMessage(respMessage); + }); + break; case "API_XHR_ABORT_INJ": message = {name: "API_XHR_ABORT_CS", xhrId: e.data.xhrId}; browser.runtime.sendMessage(message); diff --git a/extension/Userscripts Extension/Resources/page.js b/extension/Userscripts Extension/Resources/page.js index ada155d3..9c3ff103 100644 --- a/extension/Userscripts Extension/Resources/page.js +++ b/extension/Userscripts Extension/Resources/page.js @@ -1067,7 +1067,9 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o "GM_addStyle", "GM_info", "GM_setClipboard", - "GM_xmlhttpRequest" + "GM_xmlhttpRequest", + "GM.getTab", + "GM.saveTab" ]); const validKeys = new Set([ From d99a16b054b7145238bf1ee39df089c14c8da1d2 Mon Sep 17 00:00:00 2001 From: quoid Date: Sat, 6 Aug 2022 11:59:36 -0400 Subject: [PATCH 2/8] update getTab and saveTab to use sessionStorage --- .../Resources/background.js | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/extension/Userscripts Extension/Resources/background.js b/extension/Userscripts Extension/Resources/background.js index 22362075..7a3efee1 100644 --- a/extension/Userscripts Extension/Resources/background.js +++ b/extension/Userscripts Extension/Resources/background.js @@ -5,7 +5,6 @@ // xhrs are only kept in this array when active, otherwise array is empty // that means it's ok that this var gets reset when the bg page unloads let xhrs = []; -let US_tabs = {}; /* global US_filename, US_uid */ // filename and uid will be available to functions at runtime @@ -178,7 +177,7 @@ const apis = { id: US_uid, pid: pid, name: "API_GET_TAB", - filename: US_filename, + filename: US_filename }); }); }, @@ -770,9 +769,15 @@ browser.runtime.onMessage.addListener((request, sender, sendResponse) => { break; } case "API_GET_TAB": { - if (typeof sender.tab != 'undefined') { - if (typeof US_tabs[sender.tab.id] == 'undefined') US_tabs[sender.tab.id] = { storage: {} }; - let tab = US_tabs[sender.tab.id]; + if (typeof sender.tab !== "undefined") { + let tab = null; + const tabData = sessionStorage.getItem(`tab-${sender.tab.id}`); + try { + // if tabData is null, can still parse it and return that + tab = JSON.parse(tabData); + } catch (error) { + console.error("failed to parse tab data for getTab"); + } sendResponse(tab); } else { console.error("unable to deliver tab due to empty tab id"); @@ -781,16 +786,13 @@ browser.runtime.onMessage.addListener((request, sender, sendResponse) => { break; } case "API_SAVE_TAB": { - if (typeof sender.tab != 'undefined') { - let tab = {}; - for (let k in request.tab) { - tab[k] = request.tab[k]; - }; - US_tabs[sender.tab.id] = tab; + if (typeof sender.tab !== "undefined" && request.tab) { + sessionStorage.setItem(`tab-${sender.tab.id}`, JSON.stringify(request.tab)); + sendResponse({success: true}); } else { - console.error("unable to save tab due to empty tab id"); + console.error("unable to save tab due to empty tab id or bad arg"); + sendResponse(null); } - sendResponse({}); break; } case "USERSCRIPT_INSTALL_00": From b32b6dd5b3d12c1dd51faee1d8af2a05332cb7d5 Mon Sep 17 00:00:00 2001 From: quoid Date: Sat, 6 Aug 2022 12:00:00 -0400 Subject: [PATCH 3/8] update readme to note that getTab can return `Any` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 64bece08..2d58d885 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ Userscripts currently supports the following api methods. All methods are asynch - `GM.listValues()` - on success returns a promise resolved with an array of the key names of **presently set** values - `GM.getTab()` - - on success returns a promise resolved with a object that is persistent as long as this tab is open + - on success returns a promise resolved with `Any` data that is persistent as long as this tab is open - `GM.saveTab(tabObj)` - `tabObj: Any` - on success returns a promise resolved with an object indicating success From 0c98e91276a451d76b8cfa68a9ce3aa9eba3493d Mon Sep 17 00:00:00 2001 From: quoid Date: Sat, 6 Aug 2022 12:00:08 -0400 Subject: [PATCH 4/8] formatting --- extension/Userscripts Extension/Resources/content.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/Userscripts Extension/Resources/content.js b/extension/Userscripts Extension/Resources/content.js index b08207a6..4046397c 100644 --- a/extension/Userscripts Extension/Resources/content.js +++ b/extension/Userscripts Extension/Resources/content.js @@ -249,7 +249,7 @@ function handleApiMessages(e) { message = { name: name, filename: e.data.filename, - pid: pid, + pid: pid }; browser.runtime.sendMessage(message, response => { const undef = response === `undefined--${pid}`; From 87d692a83e0f5394262d6097afea28a55e025b75 Mon Sep 17 00:00:00 2001 From: quoid Date: Sat, 6 Aug 2022 12:00:50 -0400 Subject: [PATCH 5/8] add new methods to utils --- src/page/utils.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/page/utils.js b/src/page/utils.js index c8ff9225..34b886c6 100644 --- a/src/page/utils.js +++ b/src/page/utils.js @@ -152,7 +152,9 @@ export const validGrants = new Set([ "GM_addStyle", "GM_info", "GM_setClipboard", - "GM_xmlhttpRequest" + "GM_xmlhttpRequest", + "GM.getTab", + "GM.saveTab" ]); export const validKeys = new Set([ From ec6c52b5abf07cdbb4515030ca22b538bc13c390 Mon Sep 17 00:00:00 2001 From: Weijie Zhao Date: Thu, 4 Aug 2022 18:47:03 +0800 Subject: [PATCH 6/8] add GM.getTab and GM.saveTab --- README.md | 5 ++ .../Resources/background.js | 83 +++++++++++++++++++ .../Resources/content.js | 27 ++++++ .../Userscripts Extension/Resources/page.js | 4 +- 4 files changed, 118 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f0f51a5c..64bece08 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,11 @@ Userscripts currently supports the following api methods. All methods are asynch - on success returns a promise resolved with an object indicating success - `GM.listValues()` - on success returns a promise resolved with an array of the key names of **presently set** values +- `GM.getTab()` + - on success returns a promise resolved with a object that is persistent as long as this tab is open +- `GM.saveTab(tabObj)` + - `tabObj: Any` + - on success returns a promise resolved with an object indicating success - `GM.openInTab(url, openInBackground)` - `url: String`, `openInBackground: Bool` - on success returns a promise resolved with the [tab data](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/Tab) for the tab just opened diff --git a/extension/Userscripts Extension/Resources/background.js b/extension/Userscripts Extension/Resources/background.js index f6290b83..34dff048 100644 --- a/extension/Userscripts Extension/Resources/background.js +++ b/extension/Userscripts Extension/Resources/background.js @@ -158,6 +158,53 @@ const apis = { window.postMessage({id: US_uid, name: "API_SET_CLIPBOARD", data: data, type: type}); return undefined; }, + US_getTab() { + const pid = Math.random().toString(36).substring(1, 9); + return new Promise(resolve => { + const callback = e => { + if ( + e.data.pid !== pid + || e.data.id !== US_uid + || e.data.name !== "RESP_GET_TAB" + || e.data.filename !== US_filename + ) return; + const response = e.data.response; + resolve(response); + window.removeEventListener("message", callback); + }; + window.addEventListener("message", callback); + window.postMessage({ + id: US_uid, + pid: pid, + name: "API_GET_TAB", + filename: US_filename, + }); + }); + }, + US_saveTab(tab) { + const pid = Math.random().toString(36).substring(1, 9); + return new Promise(resolve => { + const callback = e => { + if ( + e.data.pid !== pid + || e.data.id !== US_uid + || e.data.name !== "RESP_SAVE_TAB" + || e.data.filename !== US_filename + ) return; + const response = e.data.response; + resolve(response); + window.removeEventListener("message", callback); + }; + window.addEventListener("message", callback); + window.postMessage({ + id: US_uid, + pid: pid, + name: "API_SAVE_TAB", + filename: US_filename, + tab: tab + }); + }); + }, // when xhr is called it sends a message to the content script // and adds it's own event listener to get responses from content script // each xhr has a unique id so it won't respond to different xhr @@ -405,6 +452,14 @@ function addApis({userscripts, uid, scriptHandler, scriptHandlerVersion}) { api += `\n${apis.US_setClipboardSync}`; api += "\nconst GM_setClipboard = US_setClipboardSync;"; break; + case "GM.getTab": + api += `\n${apis.US_getTab}`; + gmMethods.push("getTab: US_getTab"); + break; + case "GM.saveTab": + api += `\n${apis.US_saveTab}`; + gmMethods.push("saveTab: US_saveTab"); + break; case "GM_xmlhttpRequest": case "GM.xmlHttpRequest": if (!includedMethods.includes("xhr")) { @@ -713,6 +768,34 @@ browser.runtime.onMessage.addListener((request, sender, sendResponse) => { } break; } + case "API_GET_TAB": { + if (typeof sender.tab != 'undefined') { + let tabSessionKey = "tab_" + sender.tab.id; + if (sessionStorage[tabSessionKey] == null || typeof sessionStorage[tabSessionKey] == 'undefined') { + sessionStorage[tabSessionKey] = JSON.stringify({ storage: {} }); + }; + let tab = JSON.parse(sessionStorage[tabSessionKey]); + sendResponse(tab); + } else { + console.error("unable to deliver tab due to empty tab id"); + sendResponse(null); + } + break; + } + case "API_SAVE_TAB": { + if (typeof sender.tab != 'undefined') { + let tabSessionKey = "tab_" + sender.tab.id; + let tab = {}; + for (let k in request.tab) { + tab[k] = request.tab[k]; + }; + sessionStorage[tabSessionKey] = JSON.stringify(tab); + } else { + console.error("unable to save tab due to empty tab id"); + } + sendResponse({}); + break; + } case "USERSCRIPT_INSTALL_00": case "USERSCRIPT_INSTALL_01": case "USERSCRIPT_INSTALL_02": { diff --git a/extension/Userscripts Extension/Resources/content.js b/extension/Userscripts Extension/Resources/content.js index ffc483e7..b08207a6 100644 --- a/extension/Userscripts Extension/Resources/content.js +++ b/extension/Userscripts Extension/Resources/content.js @@ -245,6 +245,33 @@ function handleApiMessages(e) { window.postMessage(respMessage); }); break; + case "API_GET_TAB": + message = { + name: name, + filename: e.data.filename, + pid: pid, + }; + browser.runtime.sendMessage(message, response => { + const undef = response === `undefined--${pid}`; + respMessage.response = undef ? undefined : response; + respMessage.filename = e.data.filename; + window.postMessage(respMessage); + }); + break; + case "API_SAVE_TAB": + message = { + name: name, + filename: e.data.filename, + pid: pid, + tab: e.data.tab + }; + browser.runtime.sendMessage(message, response => { + const undef = response === `undefined--${pid}`; + respMessage.response = undef ? undefined : response; + respMessage.filename = e.data.filename; + window.postMessage(respMessage); + }); + break; case "API_XHR_ABORT_INJ": message = {name: "API_XHR_ABORT_CS", xhrId: e.data.xhrId}; browser.runtime.sendMessage(message); diff --git a/extension/Userscripts Extension/Resources/page.js b/extension/Userscripts Extension/Resources/page.js index ada155d3..9c3ff103 100644 --- a/extension/Userscripts Extension/Resources/page.js +++ b/extension/Userscripts Extension/Resources/page.js @@ -1067,7 +1067,9 @@ var JSHINT;"undefined"==typeof window&&(window={}),function(){var f=function u(o "GM_addStyle", "GM_info", "GM_setClipboard", - "GM_xmlhttpRequest" + "GM_xmlhttpRequest", + "GM.getTab", + "GM.saveTab" ]); const validKeys = new Set([ From ea6c6a24775b4d83eb1f79be8c78b166ccd9e743 Mon Sep 17 00:00:00 2001 From: quoid Date: Sat, 6 Aug 2022 13:08:35 -0400 Subject: [PATCH 7/8] remove duplicate case --- extension/Userscripts Extension/Resources/background.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/extension/Userscripts Extension/Resources/background.js b/extension/Userscripts Extension/Resources/background.js index 0755a464..7a3efee1 100644 --- a/extension/Userscripts Extension/Resources/background.js +++ b/extension/Userscripts Extension/Resources/background.js @@ -460,14 +460,6 @@ function addApis({userscripts, uid, scriptHandler, scriptHandlerVersion}) { api += `\n${apis.US_saveTab}`; gmMethods.push("saveTab: US_saveTab"); break; - case "GM.getTab": - api += `\n${apis.US_getTab}`; - gmMethods.push("getTab: US_getTab"); - break; - case "GM.saveTab": - api += `\n${apis.US_saveTab}`; - gmMethods.push("saveTab: US_saveTab"); - break; case "GM_xmlhttpRequest": case "GM.xmlHttpRequest": if (!includedMethods.includes("xhr")) { From 5218b85bda15e484c25f058dec19a3e04cc47d15 Mon Sep 17 00:00:00 2001 From: quoid Date: Sat, 6 Aug 2022 13:09:03 -0400 Subject: [PATCH 8/8] update api responses --- extension/Userscripts Extension/Resources/content.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/extension/Userscripts Extension/Resources/content.js b/extension/Userscripts Extension/Resources/content.js index 4046397c..5a443735 100644 --- a/extension/Userscripts Extension/Resources/content.js +++ b/extension/Userscripts Extension/Resources/content.js @@ -252,8 +252,7 @@ function handleApiMessages(e) { pid: pid }; browser.runtime.sendMessage(message, response => { - const undef = response === `undefined--${pid}`; - respMessage.response = undef ? undefined : response; + respMessage.response = response; respMessage.filename = e.data.filename; window.postMessage(respMessage); }); @@ -266,8 +265,7 @@ function handleApiMessages(e) { tab: e.data.tab }; browser.runtime.sendMessage(message, response => { - const undef = response === `undefined--${pid}`; - respMessage.response = undef ? undefined : response; + respMessage.response = response; respMessage.filename = e.data.filename; window.postMessage(respMessage); });