Skip to content

Commit

Permalink
Merge pull request #308 from quoid/v4.2.3
Browse files Browse the repository at this point in the history
Version 4.2.3/1.2.3
  • Loading branch information
quoid authored Aug 28, 2022
2 parents 7109430 + 4c2d8fe commit be3ad85
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 42 deletions.
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

0 comments on commit be3ad85

Please sign in to comment.