diff --git a/README.md b/README.md index f0f51a5c..2d58d885 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 `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 - `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..7a3efee1 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,33 @@ browser.runtime.onMessage.addListener((request, sender, sendResponse) => { } break; } + case "API_GET_TAB": { + 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"); + sendResponse(null); + } + break; + } + case "API_SAVE_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 or bad arg"); + sendResponse(null); + } + 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..5a443735 100644 --- a/extension/Userscripts Extension/Resources/content.js +++ b/extension/Userscripts Extension/Resources/content.js @@ -245,6 +245,31 @@ 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 => { + respMessage.response = 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 => { + respMessage.response = 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([ 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([