123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- /*******************************************************************************
- ηMatrix - a browser extension to black/white list requests.
- Copyright (C) 2013-2019 Raymond Hill
- Copyright (C) 2019-2022 Alessio Vanni
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see {http://www.gnu.org/licenses/}.
- Home: https://gitlab.com/vannilla/ematrix
- uMatrix Home: https://github.com/gorhill/uMatrix
- */
- 'use strict';
- ηMatrix.pageStoreFactory = (function() {
- Cu.import('chrome://ematrix/content/lib/UriTools.jsm');
- let ηm = ηMatrix;
- let BlockedCollapsibles = function () {
- this.boundPruneAsyncCallback = this.pruneAsyncCallback.bind(this);
- this.blocked = new Map();
- this.hash = 0;
- this.timer = null;
- };
- BlockedCollapsibles.prototype = {
- shelfLife: 10 * 1000,
- add: function (type, url, isSpecific) {
- if (this.blocked.size === 0) {
- this.pruneAsync();
- }
- let now = Date.now() / 1000 | 0;
- // The following "trick" is to encode the specifity into
- // the lsb of the time stamp so as to avoid to have to
- // allocate a memory structure to store both time stamp
- // and specificity.
- if (isSpecific) {
- now |= 0x00000001;
- } else {
- now &= 0xFFFFFFFE;
- }
- this.blocked.set(type + ' ' + url, now);
- this.hash = now;
- },
- reset: function () {
- this.blocked.clear();
- this.hash = 0;
- if (this.timer !== null) {
- clearTimeout(this.timer);
- this.timer = null;
- }
- },
- pruneAsync: function () {
- if (this.timer === null) {
- this.timer = vAPI.setTimeout(this.boundPruneAsyncCallback,
- this.shelfLife * 2);
- }
- },
- pruneAsyncCallback: function () {
- this.timer = null;
- let obsolete = Date.now() - this.shelfLife;
- for (let entry of this.blocked) {
- if (entry[1] <= obsolete) {
- this.blocked.delete(entry[0]);
- }
- }
- if (this.blocked.size !== 0) {
- this.pruneAsync();
- }
- }
- };
- // Ref: Given a URL, returns a (somewhat) unique 32-bit value
- // Based on: FNV32a
- // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-reference-source
- // The rest is custom, suited for uMatrix.
- let PageStore = function (tabContext) {
- this.hostnameTypeCells = new Map();
- this.domains = new Set();
- this.blockedCollapsibles = new BlockedCollapsibles();
- this.requestStats = ηm.requestStatsFactory();
- this.off = false;
- this.init(tabContext);
- };
- PageStore.prototype = {
- collapsibleTypes: new Set([ 'image' ]),
- pageStoreJunkyard: [],
- init: function (tabContext) {
- this.tabId = tabContext.tabId;
- this.rawUrl = tabContext.rawURL;
- this.pageUrl = tabContext.normalURL;
- this.pageHostname = tabContext.rootHostname;
- this.pageDomain = tabContext.rootDomain;
- this.title = '';
- this.hostnameTypeCells.clear();
- this.domains.clear();
- this.allHostnamesString = ' ';
- this.blockedCollapsibles.reset();
- this.requestStats.reset();
- this.distinctRequestCount = 0;
- this.perLoadAllowedRequestCount = 0;
- this.perLoadBlockedRequestCount = 0;
- this.has3pReferrer = false;
- this.hasMixedContent = false;
- this.hasNoscriptTags = false;
- this.hasWebWorkers = false;
- this.incinerationTimer = null;
- this.mtxContentModifiedTime = 0;
- this.mtxCountModifiedTime = 0;
- return this;
- },
- dispose: function () {
- this.rawUrl = '';
- this.pageUrl = '';
- this.pageHostname = '';
- this.pageDomain = '';
- this.title = '';
- this.hostnameTypeCells.clear();
- this.domains.clear();
- this.allHostnamesString = ' ';
- this.blockedCollapsibles.reset();
- if (this.incinerationTimer !== null) {
- clearTimeout(this.incinerationTimer);
- this.incinerationTimer = null;
- }
- if (this.pageStoreJunkyard.length < 8) {
- this.pageStoreJunkyard.push(this);
- }
- },
- cacheBlockedCollapsible: function (type, url, specificity) {
- if (this.collapsibleTypes.has(type)) {
- this.blockedCollapsibles.add(type,
- url,
- specificity !== 0
- && specificity < 5);
- }
- },
- lookupBlockedCollapsibles: function (request, response) {
- let tabContext = ηm.tabContextManager.lookup(this.tabId);
- if (tabContext === null) {
- return;
- }
- let collapseBlacklisted = ηm.userSettings.collapseBlacklisted;
- let collapseBlocked = ηm.userSettings.collapseBlocked;
- let blockedResources = response.blockedResources;
- if (Array.isArray(request.toFilter) && request.toFilter.length !== 0) {
- let roothn = tabContext.rootHostname;
- let hnFromURI = UriTools.hostnameFromURI;
- let tMatrix = ηm.tMatrix;
- for (let entry of request.toFilter) {
- if (tMatrix.mustBlock(roothn,
- hnFromURI(entry.url),
- entry.type) === false) {
- continue;
- }
- blockedResources.push([
- entry.type + ' ' + entry.url,
- collapseBlocked
- || collapseBlacklisted
- && tMatrix.specificityRegister !== 0
- && tMatrix.specificityRegister < 5
- ]);
- }
- }
- if (this.blockedCollapsibles.hash === response.hash) {
- return;
- }
- response.hash = this.blockedCollapsibles.hash;
- for (let entry of this.blockedCollapsibles.blocked) {
- blockedResources.push([
- entry[0],
- collapseBlocked
- || collapseBlacklisted
- && (entry[1] & 1) !== 0
- ]);
- }
- },
- recordRequest: function (type, url, block) {
- if (block !== false) {
- this.perLoadBlockedRequestCount++;
- } else {
- this.perLoadAllowedRequestCount++;
- }
- // Store distinct network requests. This is used to:
- // - remember which hostname/type were seen
- // - count the number of distinct URLs for any given
- // hostname-type pair
- let hostname = UriTools.hostnameFromURI(url);
- let key = hostname + ' ' + type;
- let uids = this.hostnameTypeCells.get(key);
- if (uids === undefined) {
- this.hostnameTypeCells.set(key, (uids = new Set()));
- } else if (uids.size > 99) {
- return;
- }
- let uid = this.uidFromURL(url);
- if (uids.has(uid)) {
- return;
- }
- uids.add(uid);
- // Count blocked/allowed requests
- this.requestStats.record(type, block);
- // https://github.com/gorhill/httpswitchboard/issues/306
- // If it is recorded locally, record globally
- ηm.requestStats.record(type, block);
- ηm.updateBadgeAsync(this.tabId);
- // this.distinctRequestCount++;
- this.mtxCountModifiedTime = Date.now();
- if (this.domains.has(hostname) === false) {
- this.domains.add(hostname);
- this.allHostnamesString += hostname + ' ';
- this.mtxContentModifiedTime = Date.now();
- }
- },
- uidFromURL: function (uri) {
- var hint = 0x811c9dc5;
- let i = uri.length;
- while (i--) {
- hint ^= uri.charCodeAt(i) | 0;
- hint += (hint<<1) + (hint<<4) + (hint<<7) + (hint<<8) + (hint<<24) | 0;
- hint >>>= 0;
- }
- return hint;
- }
- };
- return function (tabContext) {
- let entry = PageStore.prototype.pageStoreJunkyard.pop();
- if (entry) {
- return entry.init(tabContext);
- }
- return new PageStore(tabContext);
- };
- })();
|