diff --git a/src/background/control.js b/src/background/control.js index f3c8c0b..638704f 100644 --- a/src/background/control.js +++ b/src/background/control.js @@ -28,20 +28,12 @@ import { } from "../common/editDomainlist.js"; async function enable() { - if ("$BROWSER" == "firefox") { - var initProtection = initProtection_ff; - } else { - var initProtection = initProtection_cr; - } + var initProtection = initProtection_cr; initProtection(); } function disable() { - if ("$BROWSER" == "firefox") { - var haltProtection = haltProtection_ff; - } else if ("$BROWSER" == "chrome") { - var haltProtection = haltProtection_cr; - } + var haltProtection = haltProtection_cr; haltProtection(); } @@ -50,7 +42,6 @@ function disable() { // This is the very first thing the extension runs (async () => { - if ("$BROWSER" == "chrome") { chrome.scripting.registerContentScripts([ { id: "1", @@ -60,6 +51,14 @@ function disable() { runAt: "document_start", } ]); + + // Check if the browser is Firefox +if ("$BROWSER" == "firefox") { + chrome.runtime.onInstalled.addListener(function (details) { + if (details.reason === 'install') { + chrome.runtime.openOptionsPage((result) => {}); + } + }); } // Initializes the default settings let settingsDB = await storage.getStore(stores.settings); @@ -74,10 +73,8 @@ function disable() { if (isEnabled) { // Turns on the extension enable(); - if ("$BROWSER" == "chrome") { - updateRemovalScript(); - reloadDynamicRules(); - } + updateRemovalScript(); + reloadDynamicRules(); } })(); diff --git a/src/background/protection/listeners-firefox.js b/src/background/protection/listeners-firefox.js index 7725883..e4e037b 100644 --- a/src/background/protection/listeners-firefox.js +++ b/src/background/protection/listeners-firefox.js @@ -32,6 +32,7 @@ function enableListeners(callbacks) { onHeadersReceived, onBeforeNavigate, onCommitted, + onCompleted, } = callbacks; // (4) global Firefox listeners @@ -47,6 +48,7 @@ function enableListeners(callbacks) { ); chrome.webNavigation.onBeforeNavigate.addListener(onBeforeNavigate); chrome.webNavigation.onCommitted.addListener(onCommitted); + chrome.webNavigation.onCompleted.addListener(onCompleted); } /** @@ -58,12 +60,14 @@ function disableListeners(callbacks) { onHeadersReceived, onBeforeNavigate, onCommitted, + onCompleted, } = callbacks; chrome.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders); chrome.webRequest.onHeadersReceived.removeListener(onHeadersReceived); chrome.webNavigation.onBeforeNavigate.removeListener(onBeforeNavigate); chrome.webNavigation.onCommitted.removeListener(onCommitted); + chrome.webNavigation.onCompleted.removeListener(onCompleted); } export { enableListeners, disableListeners }; diff --git a/src/background/protection/protection-ff.js b/src/background/protection/protection-ff.js index 84e5a82..5ec2a32 100644 --- a/src/background/protection/protection-ff.js +++ b/src/background/protection/protection-ff.js @@ -32,7 +32,6 @@ var wellknown = {}; // Caches wellknown info to be sent to popup var signalPerTab = {}; // Caches if a signal is sent to render the popup icon var activeTabID = 0; // Caches current active tab id var sendSignal = true; // Caches if the signal can be sent to the curr domain -var isFirefox = ("$BROWSER" === "firefox"); var domPrev3rdParties = {}; //stores all the 3rd parties by domain (resets when you quit chrome) var globalParsedDomain; @@ -48,6 +47,7 @@ async function reloadVars() { reloadVars(); + /******************************************************************************/ /******************************************************************************/ /********** # Lisetener callbacks - Main functionality **********/ @@ -69,7 +69,7 @@ const listenerCallbacks = { onBeforeSendHeaders: async (details) => { await updateDomainlist(details); - if (sendSignal) { + if (true) { signalPerTab[details.tabId] = true; return addHeaders(details); } @@ -100,7 +100,7 @@ const listenerCallbacks = { */ onCommitted: async (details) => { initIAB(sendSignal); - if (sendSignal) { + if (true) { addDomSignal(details); updatePopupIcon(details); } @@ -119,6 +119,7 @@ const listenerCallbacks = { * @returns {array} details.requestHeaders */ function addHeaders(details) { + console.log("addHeaders called"); for (let signal in headers) { let s = headers[signal]; details.requestHeaders.push({ name: s.name, value: s.value }); @@ -131,7 +132,8 @@ function addHeaders(details) { * @param {object} details - retrieved info passed into callback */ function addDomSignal(details) { - chrome.tabs.executeScript(details.tabId, { + console.log("addDomSignal called"); + chrome.scripting.executeScript(details.tabId, { file: "../../content-scripts/injection/gpc-dom.js", frameId: details.frameId, // Supposed to solve multiple injections // as opposed to allFrames: true diff --git a/src/background/protection/protection.js b/src/background/protection/protection.js index c7117bc..6279534 100644 --- a/src/background/protection/protection.js +++ b/src/background/protection/protection.js @@ -38,10 +38,8 @@ var signalPerTab = {}; // Caches if a signal is sent to render the popup icon var activeTabID = 0; // Caches current active tab id var sendSignal = true; // Caches if the signal can be sent to the curr domain var domPrev3rdParties = {}; //stores all the 3rd parties by domain (resets when you quit chrome) -var isFirefox = ("$BROWSER" === "firefox"); var globalParsedDomain; - -var isFirefox = "$BROWSER" === "firefox"; +var setup = false; async function reloadVars() { let storedDomainlisted = await storage.get( @@ -73,15 +71,21 @@ const listenerCallbacks = { * @param {object} details - retrieved info passed into callback * @returns {array} */ - onBeforeSendHeaders: (details) => { - updateDomainlist(details); + onBeforeSendHeaders: async (details) => { + await updateDomainlist(details); }, /** * @param {object} details - retrieved info passed into callback */ - onHeadersReceived: (details) => { - logData(details); + onHeadersReceived: async (details) => { + //if (!setup){ + //initSetup(); + //} + await logData(details); + await sendData(); + + }, @@ -97,8 +101,13 @@ const listenerCallbacks = { * @param {object} details - retrieved info passed into callback */ onCommitted: async (details) => { - updateDomainlist(details); + await updateDomainlist(details); }, + + onCompleted: async (details) => { + await sendData(); + } + }; // closes listenerCallbacks object /******************************************************************************/ @@ -108,6 +117,29 @@ const listenerCallbacks = { /******************************************************************************/ +async function sendData(){ + // chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { + // if (tabs.id) { + // activeTabID = tabs.id; + // } + // }); + + let activeTab = await chrome.tabs.query({ active: true, currentWindow: true }); + let activeTabID = activeTab.length > 0 ? activeTab[0].id : null; + + let currentDomain = await getCurrentParsedDomain(); + let data = ["Please reload the site"]; + + + let info = await domPrev3rdParties[activeTabID][currentDomain]; + data = Object.keys(info); + + await storage.set(stores.thirdParties, data, currentDomain); + + +} + + function getCurrentParsedDomain() { return new Promise((resolve, reject) => { try { @@ -141,23 +173,26 @@ async function updateDomainlist(details) { let parsedUrl = psl.parse(url.hostname); let parsedDomain = parsedUrl.domain; let currDomainValue = await storage.get(stores.domainlist, parsedDomain); + let id = details.tabId; if (currDomainValue === undefined) { - storage.set(stores.domainlist, null, parsedDomain); // Sets to storage async + await storage.set(stores.domainlist, null, parsedDomain); // Sets to storage async } - //get the current parsed domain--this is used to store 3rd parties (using globalParsedDomain variable) - let currentDomain = await getCurrentParsedDomain(); - //initialize the objects - if (!(activeTabID in domPrev3rdParties)){ - domPrev3rdParties[activeTabID] = {}; + + + //get the current parsed domain--this is used to store 3rd parties (using globalParsedDomain variable) + if (!(id in domPrev3rdParties)){ + domPrev3rdParties[id] = {}; } if (!(currentDomain in domPrev3rdParties[activeTabID]) ){ - domPrev3rdParties[activeTabID][currentDomain] = {}; + domPrev3rdParties[id][currentDomain] = {}; } //as they come in, add the parsedDomain to the object with null value (just a placeholder) - domPrev3rdParties[activeTabID][currentDomain][parsedDomain] = null; + domPrev3rdParties[id][currentDomain][parsedDomain] = null; + + } function updatePopupIcon(tabId) { @@ -167,7 +202,7 @@ function updatePopupIcon(tabId) { }); } -function logData(details) { +async function logData(details) { let url = new URL(details.url); let parsed = psl.parse(url.hostname); @@ -285,21 +320,20 @@ function handleSendMessageError() { console.warn(error.message); } } -function dataToPopupHelper(){ +async function dataToPopupHelper(){ //data gets sent back every time the popup is clicked - let requestsData = {}; - - if (tabs[activeTabID] !== undefined) { - - requestsData = domPrev3rdParties[activeTabID][globalParsedDomain]; - } + let requestsData = {}; + let domain = await getCurrentParsedDomain(); + let parties = await storage.get(stores.thirdParties, "parties"); + parties = JSON.parse(parties); + + requestsData = parties[domain]; return requestsData } // Info back to popup -function dataToPopup(wellknownData) { - - let requestsData = dataToPopupHelper(); //get requests from the helper +async function dataToPopup(wellknownData) { + let requestsData = await dataToPopupHelper(); //get requests from the helper chrome.tabs.query({active: true, currentWindow: true}, function(tabs){ let popupData = { requests: requestsData, @@ -316,9 +350,9 @@ function dataToPopup(wellknownData) { }); } -function dataToPopupRequests() { - - let requestsData = dataToPopupHelper(); //get requests from the helper +async function dataToPopupRequests() { + let requestsData = await dataToPopupHelper(); //get requests from the helper + console.log("requests data in DTPR: ", requestsData) chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { chrome.runtime.sendMessage( @@ -392,10 +426,13 @@ async function onMessageHandlerAsync(message, sender, sendResponse) { storage.set(stores.domainlist, key, domain); // Sets to long term storage } if (message.msg === "POPUP_PROTECTION_REQUESTS") { - dataToPopupRequests(); + console.log("info queried"); + await dataToPopupRequests(); } if (message.msg === "CONTENT_SCRIPT_WELLKNOWN") { - let url = new URL(sender.origin); + // sender.origin not working for Firefox MV3, instead added a new message argument, message.origin_url + //let url = new URL(sender.origin); + let url = new URL(message.origin_url); let parsed = psl.parse(url.hostname); let domain = parsed.domain; @@ -405,7 +442,13 @@ async function onMessageHandlerAsync(message, sender, sendResponse) { wellknown[tabID] = message.data; let wellknownData = message.data; + + await storage.set(stores.wellknownInformation, wellknownData, domain); + + //await sendData(); + initIAB(!sendSignal); + if (wellknown[tabID] === null && sendSignal == null) { updatePopupIcon(tabID); } else if (wellknown[tabID]["gpc"] === true && sendSignal == null) { @@ -414,9 +457,9 @@ async function onMessageHandlerAsync(message, sender, sendResponse) { path: "assets/face-icons/optmeow-face-circle-green-128.png", }); } - chrome.runtime.onMessage.addListener(function (message, _, __) { + chrome.runtime.onMessage.addListener(async function (message, _, __) { if (message.msg === "POPUP_PROTECTION") { - dataToPopup(wellknownData); + await dataToPopup(wellknownData); } }); } @@ -454,7 +497,6 @@ async function onMessageHandlerAsync(message, sender, sendResponse) { initCookiesPerDomain(message.data); } if (message.msg === "DELETE_OPTOUT_COOKIES") { - console.log("Msg received. Info; ", message.data); deleteCookiesPerDomain(message.data); } @@ -484,6 +526,7 @@ function closeMessagePassing() { */ function onActivatedProtectionMode(info) { activeTabID = info.tabId; + console.log("onActivatedProtectionMode called"); } // Handles misc. setup & setup listeners @@ -498,6 +541,7 @@ function initSetup() { }); chrome.tabs.onActivated.addListener(onActivatedProtectionMode); + setup = true; } function closeSetup() { diff --git a/src/background/storage.js b/src/background/storage.js index 95a3734..823881a 100644 --- a/src/background/storage.js +++ b/src/background/storage.js @@ -26,6 +26,8 @@ const { saveAs } = pkg; const stores = Object.freeze({ settings: "SETTINGS", domainlist: "DOMAINLIST", + thirdParties: "THIRDPARTIES", + wellknownInformation: "WELLKNOWNDATA", }); /******************************************************************************/ @@ -36,6 +38,8 @@ const dbPromise = openDB("extensionDB", 1, { upgrade: function dbPromiseInternal(db) { db.createObjectStore(stores.domainlist); db.createObjectStore(stores.settings); + db.createObjectStore(stores.thirdParties); + db.createObjectStore(stores.wellknownInformation); }, }); @@ -97,9 +101,6 @@ const storage = { async function handleDownload() { const DOMAINLIST = await storage.getStore(stores.domainlist); let MANIFEST = chrome.runtime.getManifest(); - if ("$BROWSER" == "firefox") { - MANIFEST.version = "2.0"; - } let data = { VERSION: MANIFEST.version, DOMAINLIST: DOMAINLIST, diff --git a/src/common/editDomainlist.js b/src/common/editDomainlist.js index ac9f3d5..1fe07bd 100644 --- a/src/common/editDomainlist.js +++ b/src/common/editDomainlist.js @@ -29,7 +29,6 @@ import { /******************************************************************************/ async function updateRemovalScript() { - if ("$BROWSER" == "chrome") { let ex_matches = ["https://example.org/foo/bar.html"]; let domain; let domainValue; @@ -55,10 +54,8 @@ async function updateRemovalScript() { ]) .then(() => {}); } -} async function createCS(domain){ - if ("$BROWSER" == "chrome") { let script = await chrome.scripting.getRegisteredContentScripts({ }); @@ -78,10 +75,8 @@ async function createCS(domain){ ]) .then(() => {}); } -} async function deleteCS(domain){ - if ("$BROWSER" == "chrome") { let script = await chrome.scripting.getRegisteredContentScripts({ }); let ex_matches = script[0].excludeMatches; @@ -106,33 +101,26 @@ async function deleteCS(domain){ ]) .then(() => {}); } -} async function deleteDomainlistAndDynamicRules() { await storage.clear(stores.domainlist); - if ("$BROWSER" == "chrome") { deleteAllDynamicRules(); } -} async function addDomainToDomainlistAndRules(domain) { let id = 1; - if ("$BROWSER" == "chrome") { id = await getFreshId(); addDynamicRule(id, domain); // add the rule for the chosen domain createCS(domain); - } - await storage.set(stores.domainlist, id, domain); // record what rule the domain is associated to + await storage.set(stores.domainlist, id, domain); // record what rule the domain is associated to } async function removeDomainFromDomainlistAndRules(domain) { - if ("$BROWSER" == "chrome") { let id = await storage.get(stores.domainlist, domain); deleteDynamicRule(id); deleteCS(domain); + await storage.set(stores.domainlist, null, domain); } - await storage.set(stores.domainlist, null, domain); -} /******************************************************************************/ /******************************************************************************/ diff --git a/src/common/editRules.js b/src/common/editRules.js index 8d69a61..48e3080 100644 --- a/src/common/editRules.js +++ b/src/common/editRules.js @@ -101,8 +101,6 @@ export async function addDynamicRule(id, domain) { "csp_report", "media", "websocket", - "webtransport", - "webbundle", "other", ], }, @@ -121,7 +119,6 @@ export async function addDynamicRule(id, domain) { * receiving GPC or other opt-outs. */ export async function reloadDynamicRules() { - if ("$BROWSER" == "chrome") { deleteAllDynamicRules(); let domainlist = await storage.getStore(stores.domainlist); @@ -138,4 +135,3 @@ export async function reloadDynamicRules() { ); }); } -} diff --git a/src/content-scripts/contentScript.js b/src/content-scripts/contentScript.js index 398d7ef..e1a3856 100644 --- a/src/content-scripts/contentScript.js +++ b/src/content-scripts/contentScript.js @@ -20,6 +20,7 @@ https://developer.chrome.com/extensions/content_scripts /******************************************************************************/ /******************************************************************************/ + // To be injected to call the USPAPI function in analysis mode const uspapi = ` try { @@ -55,6 +56,7 @@ function injectScript(script) { async function getWellknown(url) { const response = await fetch(`${url.origin}/.well-known/gpc.json`); + new_url = url = JSON.parse(JSON.stringify(url)); let wellknownData; try { wellknownData = await response.json(); @@ -64,6 +66,7 @@ async function getWellknown(url) { chrome.runtime.sendMessage({ msg: "CONTENT_SCRIPT_WELLKNOWN", data: wellknownData, + origin_url: new_url }); } diff --git a/src/data/defaultSettings.js b/src/data/defaultSettings.js index 3cae0a4..5d70190 100644 --- a/src/data/defaultSettings.js +++ b/src/data/defaultSettings.js @@ -15,5 +15,6 @@ export const defaultSettings = { IS_DOMAINLISTED: false, IS_ENABLED: true, TUTORIAL_SHOWN: true, + REQUEST_PERMISSIONS_SHOWN: false, TUTORIAL_SHOWN_IN_POPUP: true, }; diff --git a/src/manifests/firefox/manifest-dev.json b/src/manifests/firefox/manifest-dev.json index 7e9fae7..2a49a84 100644 --- a/src/manifests/firefox/manifest-dev.json +++ b/src/manifests/firefox/manifest-dev.json @@ -4,19 +4,37 @@ "version": "4.3.1", "description": "OptMeowt allows Web users to make use of their rights to opt out from the sale and sharing of personal data", "permissions": [ - "", "webRequestBlocking", + "declarativeNetRequest", "webRequest", "webNavigation", "storage", "activeTab", "cookies", - "tabs" + "tabs", + "scripting" + ], + "declarative_net_request": { + "rule_resources": [ + { + "id": "universal_GPC", + "enabled": true, + "path": "rules/universal_gpc_rules.json" + }, + { + "id": "GPC_exceptions", + "enabled": true, + "path": "rules/gpc_exceptions_rules.json" + } + ] + }, + "host_permissions": [ + "" ], "icons": { "128": "assets/face-icons/icon128-face-circle.png" }, - "browser_action": { + "action": { "default_title": "OptMeowt", "default_popup": "popup.html" }, @@ -34,10 +52,18 @@ "background": { "scripts": ["background.bundle.js"] }, - "web_accessible_resources": [ - "json/*.json" - ], - "manifest_version": 2, + "web_accessible_resources": [{ + "resources": ["content-scripts/injection/gpc-dom.js"], + "matches": [""] + }], + "manifest_version": 3, "incognito": "spanning", - "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'" + "content_security_policy": { + "extension_pages": "script-src 'self'; object-src 'self'" + }, + "browser_specific_settings": { + "gecko": { + "id": "{7f22397f-fb61-47e2-9e4b-4ddd98faa275}" + } + } } diff --git a/src/options/views/domainlist-view/domainlist-view.js b/src/options/views/domainlist-view/domainlist-view.js index 3f462fc..0b24249 100644 --- a/src/options/views/domainlist-view/domainlist-view.js +++ b/src/options/views/domainlist-view/domainlist-view.js @@ -93,12 +93,6 @@ function deleteButtonListener(domain) { NOTE: It will be automatically added back to the list when the domain is requested again.`; if (confirm(deletePrompt)) { await storage.delete(stores.domainlist, domain); - if ("$BROWSER" == "firefox") { - chrome.runtime.sendMessage({ - msg: "REMOVE_FROM_DOMAINLIST", - data: domain, - }); - } reloadDynamicRules(); updateRemovalScript(); diff --git a/src/options/views/settings-view/settings-view.html b/src/options/views/settings-view/settings-view.html index 0c45805..16d2516 100644 --- a/src/options/views/settings-view/settings-view.html +++ b/src/options/views/settings-view/settings-view.html @@ -34,6 +34,22 @@

Welcome to OptMeowt!

+
+
+ +
+ +

Welcome to OptMeowt!

+
+
+ OptMeowt requires host permissions to be enabled to function correctly, please enable these permissions using the button below. Note that OptMeowt does not collect your data! +
+ +
+
+