2014-10-18 08:01:09 +13:00
|
|
|
/*******************************************************************************
|
|
|
|
|
2018-04-14 08:32:48 +12:00
|
|
|
uMatrix - a browser extension to black/white list requests.
|
2018-02-02 02:25:23 +13:00
|
|
|
Copyright (C) 2013-2018 Raymond Hill
|
2014-10-18 08:01:09 +13:00
|
|
|
|
|
|
|
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://github.com/gorhill/uMatrix
|
|
|
|
*/
|
|
|
|
|
2017-04-28 00:10:54 +12:00
|
|
|
'use strict';
|
|
|
|
|
2017-12-04 08:56:08 +13:00
|
|
|
/******************************************************************************/
|
2014-10-18 08:01:09 +13:00
|
|
|
|
2017-12-04 08:56:08 +13:00
|
|
|
µMatrix.pageStoreFactory = (function() {
|
2014-10-18 08:01:09 +13:00
|
|
|
|
2017-12-09 01:54:49 +13:00
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
var µm = µMatrix;
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
var BlockedCollapsibles = function() {
|
|
|
|
this.boundPruneAsyncCallback = this.pruneAsyncCallback.bind(this);
|
|
|
|
this.blocked = new Map();
|
|
|
|
this.hash = 0;
|
|
|
|
this.timer = null;
|
2018-07-07 02:39:38 +12:00
|
|
|
this.tOrigin = Date.now();
|
2017-12-09 01:54:49 +13:00
|
|
|
};
|
|
|
|
|
|
|
|
BlockedCollapsibles.prototype = {
|
|
|
|
|
2018-07-07 02:39:38 +12:00
|
|
|
shelfLife: 10 * 1000,
|
2017-12-09 01:54:49 +13:00
|
|
|
|
|
|
|
add: function(type, url, isSpecific) {
|
|
|
|
if ( this.blocked.size === 0 ) { this.pruneAsync(); }
|
2018-07-07 02:39:38 +12:00
|
|
|
let tStamp = Date.now() - this.tOrigin;
|
2017-12-09 01:54:49 +13:00
|
|
|
// 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 ) {
|
2018-07-07 02:39:38 +12:00
|
|
|
tStamp |= 0x00000001;
|
2017-12-09 01:54:49 +13:00
|
|
|
} else {
|
2018-07-07 02:39:38 +12:00
|
|
|
tStamp &= 0xFFFFFFFE;
|
2014-10-18 08:01:09 +13:00
|
|
|
}
|
2018-07-07 02:39:38 +12:00
|
|
|
this.blocked.set(type + ' ' + url, tStamp);
|
2018-07-06 06:38:05 +12:00
|
|
|
this.hash += 1;
|
2017-12-09 01:54:49 +13:00
|
|
|
},
|
|
|
|
|
|
|
|
reset: function() {
|
|
|
|
this.blocked.clear();
|
|
|
|
this.hash = 0;
|
|
|
|
if ( this.timer !== null ) {
|
|
|
|
clearTimeout(this.timer);
|
|
|
|
this.timer = null;
|
|
|
|
}
|
2018-07-07 02:39:38 +12:00
|
|
|
this.tOrigin = Date.now();
|
2017-12-09 01:54:49 +13:00
|
|
|
},
|
2017-12-04 05:40:27 +13:00
|
|
|
|
2017-12-09 01:54:49 +13:00
|
|
|
pruneAsync: function() {
|
|
|
|
if ( this.timer === null ) {
|
|
|
|
this.timer = vAPI.setTimeout(
|
|
|
|
this.boundPruneAsyncCallback,
|
2018-07-07 02:39:38 +12:00
|
|
|
this.shelfLife * 2
|
2017-12-09 01:54:49 +13:00
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
pruneAsyncCallback: function() {
|
|
|
|
this.timer = null;
|
2018-07-07 02:39:38 +12:00
|
|
|
let tObsolete = Date.now() - this.tOrigin - this.shelfLife;
|
2018-07-06 06:38:05 +12:00
|
|
|
for ( let entry of this.blocked ) {
|
2018-07-07 02:39:38 +12:00
|
|
|
if ( entry[1] <= tObsolete ) {
|
2017-12-09 01:54:49 +13:00
|
|
|
this.blocked.delete(entry[0]);
|
|
|
|
}
|
|
|
|
}
|
2018-07-07 02:39:38 +12:00
|
|
|
if ( this.blocked.size !== 0 ) {
|
|
|
|
this.pruneAsync();
|
|
|
|
} else {
|
|
|
|
this.tOrigin = Date.now();
|
|
|
|
}
|
2014-10-18 08:01:09 +13:00
|
|
|
}
|
2017-12-09 01:54:49 +13:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
var PageStore = function(tabContext) {
|
|
|
|
this.hostnameTypeCells = new Map();
|
|
|
|
this.domains = new Set();
|
|
|
|
this.blockedCollapsibles = new BlockedCollapsibles();
|
|
|
|
this.init(tabContext);
|
|
|
|
};
|
|
|
|
|
|
|
|
PageStore.prototype = {
|
2014-10-18 08:01:09 +13:00
|
|
|
|
2017-12-09 01:54:49 +13:00
|
|
|
collapsibleTypes: new Set([ 'image' ]),
|
|
|
|
pageStoreJunkyard: [],
|
|
|
|
|
|
|
|
init: function(tabContext) {
|
|
|
|
this.tabId = tabContext.tabId;
|
2018-04-14 03:19:36 +12:00
|
|
|
this.rawURL = tabContext.rawURL;
|
2017-12-09 01:54:49 +13:00
|
|
|
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.perLoadAllowedRequestCount = 0;
|
|
|
|
this.perLoadBlockedRequestCount = 0;
|
2018-06-18 00:22:47 +12:00
|
|
|
this.perLoadBlockedReferrerCount = 0;
|
2017-12-22 02:30:05 +13:00
|
|
|
this.has3pReferrer = false;
|
|
|
|
this.hasMixedContent = false;
|
|
|
|
this.hasNoscriptTags = false;
|
2018-01-01 12:18:10 +13:00
|
|
|
this.hasWebWorkers = false;
|
2017-12-09 01:54:49 +13:00
|
|
|
this.incinerationTimer = null;
|
|
|
|
this.mtxContentModifiedTime = 0;
|
|
|
|
this.mtxCountModifiedTime = 0;
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
dispose: function() {
|
2018-03-27 09:19:36 +13:00
|
|
|
this.tabId = '';
|
2018-04-14 03:19:36 +12:00
|
|
|
this.rawURL = '';
|
2017-12-09 01:54:49 +13:00
|
|
|
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);
|
2017-12-04 05:40:27 +13:00
|
|
|
this.incinerationTimer = null;
|
2017-12-09 01:54:49 +13:00
|
|
|
}
|
|
|
|
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) {
|
|
|
|
var tabContext = µm.tabContextManager.lookup(this.tabId);
|
|
|
|
if ( tabContext === null ) { return; }
|
2014-10-18 08:01:09 +13:00
|
|
|
|
2017-12-09 01:54:49 +13:00
|
|
|
if (
|
|
|
|
Array.isArray(request.toFilter) &&
|
|
|
|
request.toFilter.length !== 0
|
|
|
|
) {
|
2018-07-06 06:38:05 +12:00
|
|
|
let roothn = tabContext.rootHostname,
|
2017-12-09 01:54:49 +13:00
|
|
|
hnFromURI = µm.URI.hostnameFromURI,
|
|
|
|
tMatrix = µm.tMatrix;
|
2018-07-06 06:38:05 +12:00
|
|
|
for ( let entry of request.toFilter ) {
|
|
|
|
if ( tMatrix.mustBlock(roothn, hnFromURI(entry.url), entry.type) ) {
|
|
|
|
this.blockedCollapsibles.add(
|
|
|
|
entry.type,
|
|
|
|
entry.url,
|
|
|
|
tMatrix.specificityRegister < 5
|
|
|
|
);
|
2017-12-09 01:54:49 +13:00
|
|
|
}
|
2017-12-04 05:40:27 +13:00
|
|
|
}
|
|
|
|
}
|
2015-04-12 09:15:57 +12:00
|
|
|
|
2017-12-09 01:54:49 +13:00
|
|
|
if ( this.blockedCollapsibles.hash === response.hash ) { return; }
|
|
|
|
response.hash = this.blockedCollapsibles.hash;
|
|
|
|
|
2018-07-06 06:38:05 +12:00
|
|
|
let collapseBlacklisted = µm.userSettings.collapseBlacklisted,
|
|
|
|
collapseBlocked = µm.userSettings.collapseBlocked,
|
|
|
|
blockedResources = response.blockedResources;
|
|
|
|
|
|
|
|
for ( let entry of this.blockedCollapsibles.blocked ) {
|
2017-12-09 01:54:49 +13:00
|
|
|
blockedResources.push([
|
|
|
|
entry[0],
|
|
|
|
collapseBlocked || collapseBlacklisted && (entry[1] & 1) !== 0
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
recordRequest: function(type, url, block) {
|
2018-10-17 02:58:04 +13:00
|
|
|
if ( this.tabId <= 0 ) { return; }
|
|
|
|
|
2018-02-02 02:25:23 +13:00
|
|
|
if ( block ) {
|
|
|
|
this.perLoadBlockedRequestCount++;
|
|
|
|
} else {
|
|
|
|
this.perLoadAllowedRequestCount++;
|
|
|
|
}
|
|
|
|
|
2017-12-09 01:54:49 +13:00
|
|
|
// 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
|
|
|
|
var hostname = µm.URI.hostnameFromURI(url),
|
|
|
|
key = hostname + ' ' + type,
|
|
|
|
uids = this.hostnameTypeCells.get(key);
|
|
|
|
if ( uids === undefined ) {
|
|
|
|
this.hostnameTypeCells.set(key, (uids = new Set()));
|
|
|
|
} else if ( uids.size > 99 ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var uid = this.uidFromURL(url);
|
|
|
|
if ( uids.has(uid) ) { return; }
|
|
|
|
uids.add(uid);
|
|
|
|
|
|
|
|
µm.updateBadgeAsync(this.tabId);
|
|
|
|
|
|
|
|
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,
|
|
|
|
i = uri.length;
|
|
|
|
while ( i-- ) {
|
|
|
|
hint ^= uri.charCodeAt(i) | 0;
|
|
|
|
hint += (hint<<1) + (hint<<4) + (hint<<7) + (hint<<8) + (hint<<24) | 0;
|
|
|
|
hint >>>= 0;
|
2017-12-04 05:40:27 +13:00
|
|
|
}
|
2017-12-09 01:54:49 +13:00
|
|
|
return hint;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
return function pageStoreFactory(tabContext) {
|
|
|
|
var entry = PageStore.prototype.pageStoreJunkyard.pop();
|
|
|
|
if ( entry ) {
|
|
|
|
return entry.init(tabContext);
|
|
|
|
}
|
|
|
|
return new PageStore(tabContext);
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2014-10-18 08:01:09 +13:00
|
|
|
})();
|
|
|
|
|
|
|
|
/******************************************************************************/
|