diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /browser/extensions/webcompat/experiment-apis/trackingProtection.js | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | browser/extensions/webcompat/experiment-apis/trackingProtection.js | 168 | ||||
-rw-r--r-- | browser/extensions/webcompat/experiment-apis/trackingProtection.json | 75 |
2 files changed, 243 insertions, 0 deletions
diff --git a/browser/extensions/webcompat/experiment-apis/trackingProtection.js b/browser/extensions/webcompat/experiment-apis/trackingProtection.js new file mode 100644 index 0000000000..557090e974 --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/trackingProtection.js @@ -0,0 +1,168 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/* global ExtensionAPI, ExtensionCommon, ExtensionParent, Services, XPCOMUtils */ + +XPCOMUtils.defineLazyModuleGetters(this, { + Services: "resource://gre/modules/Services.jsm", +}); + +XPCOMUtils.defineLazyGlobalGetters(this, ["URL", "ChannelWrapper"]); + +class Manager { + constructor() { + this._allowLists = new Map(); + } + + _ensureStarted() { + if (this._classifierObserver) { + return; + } + + this._unblockedChannelIds = new Set(); + this._channelClassifier = Cc[ + "@mozilla.org/url-classifier/channel-classifier-service;1" + ].getService(Ci.nsIChannelClassifierService); + this._classifierObserver = {}; + this._classifierObserver.observe = (subject, topic, data) => { + switch (topic) { + case "http-on-stop-request": { + const { channelId } = subject.QueryInterface(Ci.nsIIdentChannel); + this._unblockedChannelIds.delete(channelId); + break; + } + case "urlclassifier-before-block-channel": { + const channel = subject.QueryInterface( + Ci.nsIUrlClassifierBlockedChannel + ); + const { channelId, url } = channel; + let topHost; + try { + topHost = new URL(channel.topLevelUrl).hostname; + } catch (_) { + return; + } + for (const allowList of this._allowLists.values()) { + for (const entry of allowList.values()) { + const { matcher, hosts, notHosts } = entry; + if (matcher.matches(url)) { + if ( + !notHosts?.has(topHost) && + (hosts === true || hosts.has(topHost)) + ) { + this._unblockedChannelIds.add(channelId); + channel.unblock(); + return; + } + } + } + } + break; + } + } + }; + Services.obs.addObserver(this._classifierObserver, "http-on-stop-request"); + this._channelClassifier.addListener(this._classifierObserver); + } + + stop() { + if (!this._classifierObserver) { + return; + } + + Services.obs.removeObserver( + this._classifierObserver, + "http-on-stop-request" + ); + this._channelClassifier.removeListener(this._classifierObserver); + delete this._channelClassifier; + delete this._classifierObserver; + } + + wasChannelIdUnblocked(channelId) { + return this._unblockedChannelIds.has(channelId); + } + + allow(allowListId, patterns, { hosts, notHosts }) { + this._ensureStarted(); + + if (!this._allowLists.has(allowListId)) { + this._allowLists.set(allowListId, new Map()); + } + const allowList = this._allowLists.get(allowListId); + for (const pattern of patterns) { + if (!allowList.has(pattern)) { + allowList.set(pattern, { + matcher: new MatchPattern(pattern), + }); + } + const allowListPattern = allowList.get(pattern); + if (!hosts) { + allowListPattern.hosts = true; + } else { + if (!allowListPattern.hosts) { + allowListPattern.hosts = new Set(); + } + for (const host of hosts) { + allowListPattern.hosts.add(host); + } + } + if (notHosts) { + if (!allowListPattern.notHosts) { + allowListPattern.notHosts = new Set(); + } + for (const notHost of notHosts) { + allowListPattern.notHosts.add(notHost); + } + } + } + } + + revoke(allowListId) { + this._allowLists.delete(allowListId); + } +} +var manager = new Manager(); + +function getChannelId(context, requestId) { + const wrapper = ChannelWrapper.getRegisteredChannel( + requestId, + context.extension.policy, + context.xulBrowser.frameLoader.remoteTab + ); + return wrapper?.channel?.QueryInterface(Ci.nsIIdentChannel)?.channelId; +} + +this.trackingProtection = class extends ExtensionAPI { + onShutdown(isAppShutdown) { + if (manager) { + manager.stop(); + } + } + + getAPI(context) { + return { + trackingProtection: { + async allow(allowListId, patterns, options) { + manager.allow(allowListId, patterns, options); + }, + async revoke(allowListId) { + manager.revoke(allowListId); + }, + async wasRequestUnblocked(requestId) { + if (!manager) { + return false; + } + const channelId = getChannelId(context, requestId); + if (!channelId) { + return false; + } + return manager.wasChannelIdUnblocked(channelId); + }, + }, + }; + } +}; diff --git a/browser/extensions/webcompat/experiment-apis/trackingProtection.json b/browser/extensions/webcompat/experiment-apis/trackingProtection.json new file mode 100644 index 0000000000..2627d1ea0f --- /dev/null +++ b/browser/extensions/webcompat/experiment-apis/trackingProtection.json @@ -0,0 +1,75 @@ +[ + { + "namespace": "trackingProtection", + "description": "experimental API allow requests through ETP", + "functions": [ + { + "name": "allow", + "type": "function", + "description": "Add specific requests to a given allow-list", + "parameters": [ + { + "name": "allowlistId", + "type": "string" + }, + { + "name": "patterns", + "description": "Array of match patterns", + "type": "array", + "items": { + "type": "string" + } + }, + { + "name": "options", + "type": "object", + "optional": true, + "properties": { + "hosts": { + "description": "Hosts to limit this bypass to (optional)", + "type": "array", + "items": { + "type": "string" + }, + "optional": true + }, + "notHosts": { + "description": "Hosts to not allow this bypass for (optional)", + "type": "array", + "items": { + "type": "string" + }, + "optional": true + } + } + } + ], + "async": true + }, + { + "name": "revoke", + "type": "function", + "description": "Revokes the given allow-list", + "parameters": [ + { + "name": "allowListId", + "type": "string" + } + ], + "async": true + }, + { + "name": "wasRequestUnblocked", + "type": "function", + "description": "Whether the given requestId was unblocked by any allowList", + "parameters": [ + { + "name": "requestId", + "type": "string" + } + ], + "async": true + } + ] + } +] |