Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
32b1dea
add GM.getTab and GM.saveTab
maggch97 Aug 4, 2022
d99a16b
update getTab and saveTab to use sessionStorage
quoid Aug 6, 2022
b32b6dd
update readme to note that getTab can return `Any`
quoid Aug 6, 2022
0c98e91
formatting
quoid Aug 6, 2022
87d692a
add new methods to utils
quoid Aug 6, 2022
ec6c52b
add GM.getTab and GM.saveTab
maggch97 Aug 4, 2022
30e679e
Merge branch 'master' of github.com:maggch97/userscripts into maggch9…
quoid Aug 6, 2022
ea6c6a2
remove duplicate case
quoid Aug 6, 2022
5218b85
update api responses
quoid Aug 6, 2022
5ea4788
bump build and version
quoid Aug 6, 2022
0b33242
Merge pull request #296 from maggch97/master
quoid Aug 6, 2022
5f7b969
userscriptInstall should only check top frame #295
quoid Aug 10, 2022
a2c401e
get pre tag contents rather than all innerText #295
quoid Aug 10, 2022
2e18fc3
further changes to `@grant` and `@inject-into` interactions #265
quoid Aug 12, 2022
dd2df9e
rebuild extension page #283
quoid Aug 12, 2022
369b1bf
update match func to allow for hosts without .tld & fix protocol matc…
quoid Aug 18, 2022
125f16c
update matching test
quoid Aug 18, 2022
7bd1a58
bump build
quoid Aug 18, 2022
2237956
support for GM_info.script.icon #302
quoid Aug 18, 2022
690875b
bump build
quoid Aug 18, 2022
ae29d17
grammar fix #302
quoid Aug 18, 2022
125ef46
Fixing type on 'onloadstart' in line 210
spd2last Aug 24, 2022
29a028e
Merge pull request #304 from spd2last/spd2last_userscripts_bugfix-onl…
quoid Aug 24, 2022
dffdc11
return empty tab obj instead of null
maggch97 Aug 25, 2022
3878e6c
Merge pull request #306 from maggch97/v4.2.3
quoid Aug 26, 2022
4c2d8fe
bump build
quoid Aug 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ Userscripts Safari currently supports the following userscript metadata:

- `@name` - This will be the name that displays in the sidebar and be used as the filename - you can *not* use the same name for multiple files of the same type
- `@description`- Use this to describe what your userscript does - this will be displayed in the sidebar - there is a setting to hide descriptions
- `@icon` - This doesn't have a function with this userscript manager, but the **first value** provided in the metadata will be accessible in the `GM_/GM.info` object
- `@match` - Domain match patterns - you can use several instances of this field if you'd like multiple domain matches - view [this article for more information on constructing patterns](https://developer.chrome.com/extensions/match_patterns)
- **Note:** this extension only supports `http/s`
- `@exclude-match` - Domain patterns where you do *not* want the script to run
Expand Down Expand Up @@ -191,6 +192,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
Expand Down
9 changes: 8 additions & 1 deletion extension/Userscripts Extension/Functions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1094,13 +1094,18 @@ func match(_ ptcl: String,_ host: String,_ path: String,_ matchPattern: String)
if (ptcl != "http:" && ptcl != "https:") {
return false
}
let partsPattern = #"^(http:|https:|\*:)\/\/((?:\*\.)?(?:[a-z0-9-]+\.)+(?:[a-z0-9]+)|\*\.[a-z]+|\*)(\/[^\s]*)$"#
let partsPattern = #"^(http:|https:|\*:)\/\/((?:\*\.)?(?:[a-z0-9-]+\.)+(?:[a-z0-9]+)|\*\.[a-z]+|\*|[a-z0-9]+)(\/[^\s]*)$"#
let partsPatternReg = try! NSRegularExpression(pattern: partsPattern, options: .caseInsensitive)
let range = NSMakeRange(0, matchPattern.utf16.count)
guard let parts = partsPatternReg.firstMatch(in: matchPattern, options: [], range: range) else {
err("malformed regex match pattern")
return false
}
// ensure url protocol matches pattern protocol
let protocolPattern = matchPattern[Range(parts.range(at: 1), in: matchPattern)!]
if (protocolPattern != "*:" && ptcl != protocolPattern) {
return false
}
// construct host regex from matchPattern
let matchPatternHost = matchPattern[Range(parts.range(at: 2), in: matchPattern)!]
var hostPattern = "^\(matchPatternHost.replacingOccurrences(of: ".", with: "\\."))$"
Expand Down Expand Up @@ -1294,6 +1299,7 @@ func getCode(_ filenames: [String], _ isTop: Bool)-> [String: Any]? {
let description = metadata["description"]?[0] ?? ""
let excludes = metadata["exclude"] ?? []
let excludeMatches = metadata["exclude-match"] ?? []
let icon = metadata["icon"]?[0] ?? ""
let includes = metadata["include"] ?? []
let matches = metadata["match"] ?? []
let requires = metadata["require"] ?? []
Expand All @@ -1305,6 +1311,7 @@ func getCode(_ filenames: [String], _ isTop: Bool)-> [String: Any]? {
"exclude-match": excludeMatches,
"filename": filename,
"grants": grants,
"icon": icon,
"includes": includes,
"inject-into": injectInto,
"matches": matches,
Expand Down
103 changes: 101 additions & 2 deletions extension/Userscripts Extension/Resources/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -207,7 +254,7 @@ const apis = {
} else if (name.includes("READYSTATECHANGE") && details.onreadystatechange) {
details.onreadystatechange(response);
} else if (name.includes("LOADSTART") && details.onloadstart) {
details.onloadtstart(response);
details.onloadstart(response);
} else if (name.includes("ABORT") && details.onabort) {
details.onabort(response);
} else if (name.includes("ERROR") && details.onerror) {
Expand Down Expand Up @@ -348,6 +395,7 @@ function addApis({userscripts, uid, scriptHandler, scriptHandlerVersion}) {
const userscript = userscripts[i];
const filename = userscript.scriptObject.filename;
const grants = userscript.scriptObject.grants;
const injectInto = userscript.scriptObject["inject-into"];
// prepare the api string
let api = `const US_uid = "${uid}";\nconst US_filename = "${filename}";`;
// all scripts get access to US_info / GM./GM_info, prepare that object
Expand All @@ -361,7 +409,23 @@ function addApis({userscripts, uid, scriptHandler, scriptHandlerVersion}) {
api += "\nconst GM_info = US_info;";
gmMethods.push("info: US_info");
// if @grant explicitly set to none, empty grants array
if (grants.includes("none")) grants.length = 0;
if (grants.includes("none")) {
grants.length = 0;
}
// @grant exist for page scoped userscript
if (grants.length && injectInto === "page") {
// remove grants
grants.length = 0;
// provide warning for content script
userscript.warning = `${filename} @grant values changed due to @inject-into value`;
}
// @grant exist for auto scoped userscript
if (grants.length && injectInto === "auto") {
// change scope
userscript.scriptObject["inject-into"] = "content";
// provide warning for content script
userscript.warning = `${filename} @inject-into value changed due to @grant values`;
}
// loop through each @grant for the userscript, add methods as needed
for (let j = 0; j < grants.length; j++) {
const grant = grants[j];
Expand Down Expand Up @@ -405,6 +469,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")) {
Expand Down Expand Up @@ -713,6 +785,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 == null ? {} : 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": {
Expand Down
37 changes: 35 additions & 2 deletions extension/Userscripts Extension/Resources/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ browser.runtime.sendMessage({name: "REQ_USERSCRIPTS", uid: uid}, response => {
userscript.scriptObject["inject-into"] = "content";
console.warn(`${userscript.scriptObject.filename} had it's @inject-value automatically set to "content" because it has @grant values - see: https://github.com/quoid/userscripts/issues/252#issuecomment-1136637700`);
}
// log warning if provided
if (userscript.warning) console.warn(userscript.warning);
processJS(
userscript.scriptObject.name,
userscript.scriptObject.filename,
Expand Down Expand Up @@ -245,6 +247,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);
Expand Down Expand Up @@ -300,17 +327,23 @@ browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
|| name === "USERSCRIPT_INSTALL_01"
|| name === "USERSCRIPT_INSTALL_02"
) {
// only response to top frame messages
if (window !== window.top) return;
const types = [
"text/plain",
"application/ecmascript",
"application/javascript",
"text/ecmascript",
"text/javascript"
];
if (!document.contentType || types.indexOf(document.contentType) === -1) {
if (
!document.contentType
|| types.indexOf(document.contentType) === -1
|| !document.querySelector("pre")
) {
sendResponse({invalid: true});
} else {
const message = {name: name, content: document.body.innerText};
const message = {name: name, content: document.querySelector("pre").innerText};
browser.runtime.sendMessage(message, response => {
sendResponse(response);
});
Expand Down
2 changes: 1 addition & 1 deletion extension/Userscripts Extension/Resources/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"default_locale": "en",
"name": "__MSG_extension_name__",
"description": "__MSG_extension_description__",
"version": "4.2.2",
"version": "4.2.3",
"icons": {
"48": "images/icon-48.png",
"96": "images/icon-96.png",
Expand Down
7 changes: 5 additions & 2 deletions extension/Userscripts Extension/Resources/page.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading