Skip to content

Commit

Permalink
Continued work on Manifest v3 support. Compatibility with GM API is p…
Browse files Browse the repository at this point in the history
…rovided, script injection mechanism is improved
  • Loading branch information
modos189 committed Feb 17, 2024
1 parent 01350c4 commit 08ba239
Show file tree
Hide file tree
Showing 14 changed files with 415 additions and 171 deletions.
126 changes: 97 additions & 29 deletions src/background/background.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
//@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3
import { Manager } from "lib-iitc-manager";
import browser from "webextension-polyfill";
import { IS_SCRIPTING_API, IS_USERSCRIPTS_API } from "@/userscripts/env";
import { _ } from "@/i18n";
import { inject_plugin } from "./injector";
import {
inject_gm_api,
inject_plugin,
is_userscripts_api_available,
} from "@/userscripts/wrapper";
import {
onUpdatedListener,
onRemovedListener,
Expand All @@ -11,6 +16,7 @@ import {
} from "./intel";
import "./requests";
import { strToBase64 } from "@/strToBase64";
import { isIITCEnabled } from "@/userscripts/utils";

const manager = new Manager({
storage: browser.storage.local,
Expand All @@ -37,24 +43,40 @@ const manager = new Manager({
} catch {
// If popup is closed, message goes nowhere and an error occurs. Ignore.
}
if (IS_USERSCRIPTS_API) {
isIITCEnabled().then((status) => {
if (status) {
init_userscripts_api();
manager.inject().then();
}
});
}
},
inject_plugin: (plugin) => inject_plugin(plugin).then(),
inject_plugin: async (plugin) => {
await inject_plugin(plugin);
},
is_daemon: IS_USERSCRIPTS_API,
});

manager.run().then();

const { onUpdated, onRemoved } = browser.tabs;
onUpdated.addListener((tabId, status, tab) =>
onUpdatedListener(tabId, status, tab, manager)
);
onRemoved.addListener(onRemovedListener);
if (IS_SCRIPTING_API) {
const { onUpdated, onRemoved } = browser.tabs;
onUpdated.addListener((tabId, status, tab) =>
onUpdatedListener(tabId, status, tab, manager)
);
onRemoved.addListener(onRemovedListener);
}

browser.runtime.onMessage.addListener(async (request) => {
switch (request.type) {
case "requestOpenIntel":
await onRequestOpenIntel();
break;
case "toggleIITC":
if (IS_USERSCRIPTS_API) {
await manage_user_scripts_status(request.value);
}
await onToggleIITC(request.value);
break;
case "xmlHttpRequestHandler":
Expand Down Expand Up @@ -148,38 +170,84 @@ async function xmlHttpRequestHandler(data) {
response: JSON.stringify(response),
});

const injectedCode = `
const bridge_data = strToBase64(String(detail_stringify));
if (IS_USERSCRIPTS_API) {
let allTabs = await browser.tabs.query({ status: "complete" });

allTabs = allTabs.filter(function (tab) {
return tab.status === "complete" && tab.url;
});

for (const tab of allTabs) {
await browser.tabs.sendMessage(tab.id, {
type: "xmlHttpRequestToCS",
value: bridge_data,
});
}
} else {
const injectedCode = `
document.dispatchEvent(new CustomEvent('bridgeResponse', {
detail: "${strToBase64(String(detail_stringify))}"
detail: "${bridge_data}"
}));
`;

try {
await browser.tabs.executeScript(data.tab_id, {
code: injectedCode,
});
} catch (error) {
console.error(`An error occurred while execute script: ${error.message}`);
try {
await browser.tabs.executeScript(data.tab_id, {
code: injectedCode,
});
} catch (error) {
console.error(

Check warning on line 199 in src/background/background.js

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
`An error occurred while execute script: ${error.message}`
);
}
}
}

const req = new XMLHttpRequest();
req.onload = function () {
const response = {
readyState: this.readyState,
responseHeaders: this.responseHeaders,
responseText: this.responseText,
status: this.status,
statusText: this.statusText,
try {
const response = await fetch(data.url, {
method: data.method,
headers: data.headers,
body: data.method !== "GET" ? data.data : undefined,
credentials: data.user && data.password ? "include" : "same-origin",
});

const text = await response.text();

// Create a response object similar to the one in XMLHttpRequest
const responseObject = {
readyState: 4,
responseHeaders: "Not directly accessible with fetch",
responseText: text,
status: response.status,
statusText: response.statusText,
};
xmlResponse(data.tab_id, data.onload, response);
};
req.open(data.method, data.url, true, data.user, data.password);
for (let [header_name, header_value] of Object.entries(data.headers)) {
req.setRequestHeader(header_name, header_value);

await xmlResponse(data.tab_id, data.onload, responseObject);
} catch (error) {
console.error("Fetch error:", error);

Check warning on line 227 in src/background/background.js

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
}
}

const init_userscripts_api = () => {
if (!is_userscripts_api_available) return;
chrome.userScripts.configureWorld({
csp: "script-src 'self' 'unsafe-inline'",
messaging: true,
});
inject_gm_api();
};

req.send(data.data);
async function manage_user_scripts_status(status) {
if (status === true) {
init_userscripts_api();
manager.inject().then();
} else {
try {
await chrome.userScripts.unregister();
} catch (e) {
console.log(e);

Check warning on line 248 in src/background/background.js

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
}
}
}

self.addEventListener("install", () => {
Expand Down
78 changes: 43 additions & 35 deletions src/background/injector.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,20 @@
//@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3

import browser from "webextension-polyfill";
import { check_matching } from "lib-iitc-manager";
import {
gm_api_for_plugin,
is_userscripts_api_available,
} from "@/userscripts/wrapper";
import { getNiaTabsToInject, getPluginMatches } from "@/background/utils";

// TODO
// https://developer.chrome.com/docs/extensions/reference/api/userScripts#developer_mode_for_extension_users

export async function inject_plugin(plugin) {
const tabs = await getTabsToInject();

const is_ingress_tab = (url) => {
return /https:\/\/(intel|missions).ingress.com\/*/.test(url);
};

chrome.userScripts.configureWorld({
csp: "script-src 'self' 'unsafe-inline'",
});

try {
chrome.userScripts.unregister(["iitc"]);
} catch (error) {
console.log(error);
}
chrome.userScripts.register([
{
id: "iitc",
matches: ["https://intel.ingress.com/*"],
js: [{ code: plugin.code }],
},
]);
export async function inject_plugin_via_content_scripts(plugin, use_gm_api) {
const tabs = await getNiaTabsToInject(plugin);
for (let tab of Object.values(tabs)) {
if (
(!is_ingress_tab(tab.url) || !check_matching(plugin, "<all_ingress>")) &&
!check_matching(plugin, tab.url)
) {
continue;
if (use_gm_api) {
plugin.code = await gm_api_for_plugin(plugin, tab.id);
}

try {
Expand All @@ -58,11 +38,39 @@ export async function inject_plugin(plugin) {
}
}

// Fetch all completly loaded Ingress Intel tabs
export async function getTabsToInject() {
let allTabs = await browser.tabs.query({ status: "complete" });
export async function inject_plugin_via_userscripts_api(plugin, use_gm_api) {
if (!is_userscripts_api_available) return;

if (use_gm_api) {
plugin.code = await gm_api_for_plugin(plugin, 0);
}

return allTabs.filter(function (tab) {
return tab.status === "complete" && tab.url;
});
let scripts = [];
try {
scripts = await chrome.userScripts.getScripts();
} catch (e) {
console.log(e);

Check warning on line 52 in src/background/injector.js

View workflow job for this annotation

GitHub Actions / build

Unexpected console statement
return;
}
const plugin_obj = [
{
id: plugin.uid,
matches:
plugin.uid === "gm_api" ? ["https://*/*"] : getPluginMatches(plugin),
js: [{ code: plugin.code }],
runAt: plugin.uid === "gm_api" ? "document_start" : "document_end",
world: "MAIN",
},
];

const is_exist = scripts.some((script) => script.id === plugin.uid);
if (!is_exist) {
await chrome.userScripts.register(plugin_obj);
return;
}

const exist_script = scripts.find((script) => script.id === plugin.uid);
if (exist_script.js[0].code !== plugin_obj[0].js[0].code) {
await chrome.userScripts.update(plugin_obj);
}
}
9 changes: 4 additions & 5 deletions src/background/intel.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3
import browser from "webextension-polyfill";
import { getTabsToInject } from "./injector";
import { getTabsToInject } from "@/background/utils";
import { isIITCEnabled } from "@/userscripts/utils";

let lastIITCTab = null;

Expand Down Expand Up @@ -53,10 +54,8 @@ export function onRemovedListener(tabId) {
}

async function initialize(manager) {
const storage = await browser.storage.local.get(["IITC_is_enabled"]);
const status = storage["IITC_is_enabled"];

if (status !== false) {
const status = await isIITCEnabled();
if (status) {
await manager.inject();
}
}
Expand Down
7 changes: 3 additions & 4 deletions src/background/requests.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import browser from "webextension-polyfill";
import { parseMeta, ajaxGet, getUniqId } from "lib-iitc-manager";
import { isIITCEnabled } from "@/userscripts/utils";

const IS_CHROME = !!global.chrome.app;
const whitelist = [
Expand Down Expand Up @@ -65,10 +66,8 @@ async function bypass(tabId, url) {
* @return {Promise<void>}
*/
async function maybeInstallUserJs(tabId, url) {
const IITC_is_enabled = await browser.storage.local
.get(["IITC_is_enabled"])
.then((data) => data.IITC_is_enabled);
if (IITC_is_enabled === false) {
const status = await isIITCEnabled();
if (status === false) {
await bypass(tabId, url);
return;
}
Expand Down
38 changes: 38 additions & 0 deletions src/background/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//@license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3

import browser from "webextension-polyfill";
import { check_matching } from "lib-iitc-manager";

const is_ingress_tab = (url) => {
return /https:\/\/(intel|missions).ingress.com\/*/.test(url);
};

// Fetch all completly loaded tabs
export async function getTabsToInject() {
let allTabs = await browser.tabs.query({ status: "complete" });
return allTabs.filter(function (tab) {
return tab.status === "complete" && tab.url;
});
}

// Filter all completly loaded Ingress Intel tabs
export async function getNiaTabsToInject(plugin) {
const tabs = await getTabsToInject();
return Object.values(tabs).filter(
(tab) =>
(is_ingress_tab(tab.url) && check_matching(plugin, "<all_ingress>")) ||
check_matching(plugin, tab.url)
);
}

export function getPluginMatches(plugin) {
let matches = [];
if (check_matching(plugin, "<all_ingress>")) {
matches.push("https://intel.ingress.com/*");
matches.push("https://missions.ingress.com/*");
}
if (plugin.match) {
matches = matches.concat(plugin.match);
}
return matches;
}
Loading

0 comments on commit 08ba239

Please sign in to comment.