mirror of
https://github.com/gorhill/uMatrix.git
synced 2024-06-17 01:35:00 +12:00
Fix #335
Additionally, performance improvements: - Reduce overhead of collapsing elements (see https://github.com/gorhill/uBlock/issues/2839) - Cache decomposition of source hostname when matrix-filtering Also, various code review.
This commit is contained in:
parent
8615f3b804
commit
73c8da05b7
|
@ -342,6 +342,10 @@
|
|||
"message": "Collapse placeholder of blocked elements",
|
||||
"description": "English: Collapse placeholder of blocked elements"
|
||||
},
|
||||
"settingsCollapseBlacklisted" : {
|
||||
"message": "Collapse placeholder of blacklisted elements",
|
||||
"description": "A setting in the dashboard's Settings pane: 'blacklisted' means 'for which there is a specific block rule', 'specific' means 'a rule for which the destination hostname is not `*`'"
|
||||
},
|
||||
"settingsNoscriptTagsSpoofed" : {
|
||||
"message": "Spoof <code><noscript></code> tags when 1st-party scripts are blocked",
|
||||
"description": "This appears in the Settings pane in the dashboard"
|
||||
|
|
|
@ -36,7 +36,9 @@ a {
|
|||
button {
|
||||
padding: 0.3em 0.5em;
|
||||
}
|
||||
|
||||
input[disabled] + label {
|
||||
color: gray;
|
||||
}
|
||||
.para {
|
||||
width: 40em;
|
||||
}
|
||||
|
|
|
@ -91,6 +91,7 @@ return {
|
|||
clearBrowserCache: true,
|
||||
clearBrowserCacheAfter: 60,
|
||||
cloudStorageEnabled: false,
|
||||
collapseBlacklisted: true,
|
||||
collapseBlocked: false,
|
||||
colorBlindFriendly: false,
|
||||
deleteCookies: false,
|
||||
|
|
|
@ -109,71 +109,86 @@ vAPI.contentscriptEndInjected = true;
|
|||
// https://github.com/gorhill/uMatrix/issues/45
|
||||
|
||||
var collapser = (function() {
|
||||
var timer = null;
|
||||
var requestId = 1;
|
||||
var newRequests = [];
|
||||
var pendingRequests = {};
|
||||
var pendingRequestCount = 0;
|
||||
var srcProps = {
|
||||
'img': 'src'
|
||||
var resquestIdGenerator = 1,
|
||||
processTimer,
|
||||
toProcess = [],
|
||||
toFilter = [],
|
||||
toCollapse = new Map(),
|
||||
cachedBlockedMap,
|
||||
cachedBlockedMapHash,
|
||||
cachedBlockedMapTimer,
|
||||
reURLPlaceholder = /\{\{url\}\}/g;
|
||||
var src1stProps = {
|
||||
'embed': 'src',
|
||||
'iframe': 'src',
|
||||
'img': 'src',
|
||||
'object': 'data'
|
||||
};
|
||||
var reURLplaceholder = /\{\{url\}\}/g;
|
||||
|
||||
var PendingRequest = function(target) {
|
||||
this.id = requestId++;
|
||||
this.target = target;
|
||||
pendingRequests[this.id] = this;
|
||||
pendingRequestCount += 1;
|
||||
};
|
||||
|
||||
// Because a while ago I have observed constructors are faster than
|
||||
// literal object instanciations.
|
||||
var BouncingRequest = function(id, tagName, url) {
|
||||
this.id = id;
|
||||
this.tagName = tagName;
|
||||
this.url = url;
|
||||
this.blocked = false;
|
||||
var src2ndProps = {
|
||||
'img': 'srcset'
|
||||
};
|
||||
var tagToTypeMap = {
|
||||
embed: 'media',
|
||||
iframe: 'frame',
|
||||
img: 'image',
|
||||
object: 'media'
|
||||
};
|
||||
var cachedBlockedSetClear = function() {
|
||||
cachedBlockedMap =
|
||||
cachedBlockedMapHash =
|
||||
cachedBlockedMapTimer = undefined;
|
||||
};
|
||||
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/174
|
||||
// Do not remove fragment from src URL
|
||||
var onProcessed = function(response) {
|
||||
if ( !response ) {
|
||||
if ( !response ) { // This happens if uBO is disabled or restarted.
|
||||
toCollapse.clear();
|
||||
return;
|
||||
}
|
||||
var requests = response.requests;
|
||||
if ( requests === null || Array.isArray(requests) === false ) {
|
||||
|
||||
var targets = toCollapse.get(response.id);
|
||||
if ( targets === undefined ) { return; }
|
||||
toCollapse.delete(response.id);
|
||||
if ( cachedBlockedMapHash !== response.hash ) {
|
||||
cachedBlockedMap = new Map(response.blockedResources);
|
||||
cachedBlockedMapHash = response.hash;
|
||||
if ( cachedBlockedMapTimer !== undefined ) {
|
||||
clearTimeout(cachedBlockedMapTimer);
|
||||
}
|
||||
cachedBlockedMapTimer = vAPI.setTimeout(cachedBlockedSetClear, 30000);
|
||||
}
|
||||
if ( cachedBlockedMap === undefined || cachedBlockedMap.size === 0 ) {
|
||||
return;
|
||||
}
|
||||
var collapse = response.collapse;
|
||||
var placeholders = response.placeholders;
|
||||
var i = requests.length;
|
||||
var request, entry, target, tagName, docurl, replaced;
|
||||
while ( i-- ) {
|
||||
request = requests[i];
|
||||
if ( pendingRequests.hasOwnProperty(request.id) === false ) {
|
||||
continue;
|
||||
|
||||
var placeholders = response.placeholders,
|
||||
tag, prop, src, collapsed, docurl, replaced;
|
||||
|
||||
for ( var target of targets ) {
|
||||
tag = target.localName;
|
||||
prop = src1stProps[tag];
|
||||
if ( prop === undefined ) { continue; }
|
||||
src = target[prop];
|
||||
if ( typeof src !== 'string' || src.length === 0 ) {
|
||||
prop = src2ndProps[tag];
|
||||
if ( prop === undefined ) { continue; }
|
||||
src = target[prop];
|
||||
if ( typeof src !== 'string' || src.length === 0 ) { continue; }
|
||||
}
|
||||
entry = pendingRequests[request.id];
|
||||
delete pendingRequests[request.id];
|
||||
pendingRequestCount -= 1;
|
||||
|
||||
// Not blocked
|
||||
if ( !request.blocked ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
target = entry.target;
|
||||
|
||||
// No placeholders
|
||||
if ( collapse ) {
|
||||
collapsed = cachedBlockedMap.get(tagToTypeMap[tag] + ' ' + src);
|
||||
if ( collapsed === undefined ) { continue; }
|
||||
if ( collapsed ) {
|
||||
target.style.setProperty('display', 'none', 'important');
|
||||
target.hidden = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
tagName = target.localName;
|
||||
|
||||
// Special case: iframe
|
||||
if ( tagName === 'iframe' ) {
|
||||
docurl = 'data:text/html,' + encodeURIComponent(placeholders.iframe.replace(reURLplaceholder, request.url));
|
||||
if ( tag === 'iframe' ) {
|
||||
docurl =
|
||||
'data:text/html,' +
|
||||
encodeURIComponent(
|
||||
placeholders.iframe.replace(reURLPlaceholder, src)
|
||||
);
|
||||
replaced = false;
|
||||
// Using contentWindow.location prevent tainting browser
|
||||
// history -- i.e. breaking back button (seen on Chromium).
|
||||
|
@ -189,148 +204,125 @@ var collapser = (function() {
|
|||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Everything else
|
||||
target.setAttribute(srcProps[tagName], placeholders[tagName]);
|
||||
target.setAttribute(src1stProps[tag], placeholders[tag]);
|
||||
target.style.setProperty('border', placeholders.border, 'important');
|
||||
target.style.setProperty('background', placeholders.background, 'important');
|
||||
}
|
||||
|
||||
// Renew map: I believe that even if all properties are deleted, an
|
||||
// object will still use more memory than a brand new one.
|
||||
if ( pendingRequestCount === 0 ) {
|
||||
pendingRequests = {};
|
||||
}
|
||||
};
|
||||
|
||||
var send = function() {
|
||||
timer = null;
|
||||
vAPI.messaging.send('contentscript.js', {
|
||||
what: 'evaluateURLs',
|
||||
requests: newRequests
|
||||
}, onProcessed);
|
||||
newRequests = [];
|
||||
processTimer = undefined;
|
||||
toCollapse.set(resquestIdGenerator, toProcess);
|
||||
var msg = {
|
||||
what: 'lookupBlockedCollapsibles',
|
||||
id: resquestIdGenerator,
|
||||
toFilter: toFilter,
|
||||
hash: cachedBlockedMapHash
|
||||
};
|
||||
vAPI.messaging.send('contentscript.js', msg, onProcessed);
|
||||
toProcess = [];
|
||||
toFilter = [];
|
||||
resquestIdGenerator += 1;
|
||||
};
|
||||
|
||||
var process = function(delay) {
|
||||
if ( newRequests.length === 0 ) {
|
||||
return;
|
||||
}
|
||||
if ( toProcess.length === 0 ) { return; }
|
||||
if ( delay === 0 ) {
|
||||
clearTimeout(timer);
|
||||
if ( processTimer !== undefined ) {
|
||||
clearTimeout(processTimer);
|
||||
}
|
||||
send();
|
||||
} else if ( timer === null ) {
|
||||
timer = vAPI.setTimeout(send, delay || 50);
|
||||
} else if ( processTimer === undefined ) {
|
||||
processTimer = vAPI.setTimeout(send, delay || 47);
|
||||
}
|
||||
};
|
||||
|
||||
var add = function(target) {
|
||||
toProcess.push(target);
|
||||
};
|
||||
|
||||
var addMany = function(targets) {
|
||||
var i = targets.length;
|
||||
while ( i-- ) {
|
||||
toProcess.push(targets[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var iframeSourceModified = function(mutations) {
|
||||
var i = mutations.length;
|
||||
while ( i-- ) {
|
||||
addFrameNode(mutations[i].target, true);
|
||||
addIFrame(mutations[i].target, true);
|
||||
}
|
||||
process();
|
||||
};
|
||||
var iframeSourceObserver = null;
|
||||
var iframeSourceObserver;
|
||||
var iframeSourceObserverOptions = {
|
||||
attributes: true,
|
||||
attributeFilter: [ 'src' ]
|
||||
};
|
||||
|
||||
var addFrameNode = function(iframe, dontObserve) {
|
||||
var addIFrame = function(iframe, dontObserve) {
|
||||
// https://github.com/gorhill/uBlock/issues/162
|
||||
// Be prepared to deal with possible change of src attribute.
|
||||
if ( dontObserve !== true ) {
|
||||
if ( iframeSourceObserver === null ) {
|
||||
if ( iframeSourceObserver === undefined ) {
|
||||
iframeSourceObserver = new MutationObserver(iframeSourceModified);
|
||||
}
|
||||
iframeSourceObserver.observe(iframe, iframeSourceObserverOptions);
|
||||
}
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/174
|
||||
// Do not remove fragment from src URL
|
||||
var src = iframe.src;
|
||||
if ( src.lastIndexOf('http', 0) !== 0 ) {
|
||||
return;
|
||||
}
|
||||
var req = new PendingRequest(iframe);
|
||||
newRequests.push(new BouncingRequest(req.id, 'iframe', src));
|
||||
if ( src === '' || typeof src !== 'string' ) { return; }
|
||||
if ( src.startsWith('http') === false ) { return; }
|
||||
toFilter.push({ type: 'frame', url: iframe.src });
|
||||
add(iframe);
|
||||
};
|
||||
|
||||
var addNode = function(target) {
|
||||
var tagName = target.localName;
|
||||
if ( tagName === 'iframe' ) {
|
||||
addFrameNode(target);
|
||||
return;
|
||||
}
|
||||
var prop = srcProps[tagName];
|
||||
if ( prop === undefined ) {
|
||||
return;
|
||||
}
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/174
|
||||
// Do not remove fragment from src URL
|
||||
var src = target[prop];
|
||||
if ( typeof src !== 'string' || src === '' ) {
|
||||
return;
|
||||
}
|
||||
if ( src.lastIndexOf('http', 0) !== 0 ) {
|
||||
return;
|
||||
}
|
||||
var req = new PendingRequest(target);
|
||||
newRequests.push(new BouncingRequest(req.id, tagName, src));
|
||||
};
|
||||
|
||||
var addNodes = function(nodes) {
|
||||
var node;
|
||||
var i = nodes.length;
|
||||
var addIFrames = function(iframes) {
|
||||
var i = iframes.length;
|
||||
while ( i-- ) {
|
||||
node = nodes[i];
|
||||
if ( node.nodeType === 1 ) {
|
||||
addNode(node);
|
||||
addIFrame(iframes[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var addNodeList = function(nodeList) {
|
||||
var node,
|
||||
i = nodeList.length;
|
||||
while ( i-- ) {
|
||||
node = nodeList[i];
|
||||
if ( node.nodeType !== 1 ) { continue; }
|
||||
if ( node.localName === 'iframe' ) {
|
||||
addIFrame(node);
|
||||
}
|
||||
if ( node.childElementCount !== 0 ) {
|
||||
addIFrames(node.querySelectorAll('iframe'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var addBranches = function(branches) {
|
||||
var root;
|
||||
var i = branches.length;
|
||||
while ( i-- ) {
|
||||
root = branches[i];
|
||||
if ( root.nodeType === 1 ) {
|
||||
addNode(root);
|
||||
// blocked images will be reported by onResourceFailed
|
||||
addNodes(root.querySelectorAll('iframe'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Listener to collapse blocked resources.
|
||||
// - Future requests not blocked yet
|
||||
// - Elements dynamically added to the page
|
||||
// - Elements which resource URL changes
|
||||
var onResourceFailed = function(ev) {
|
||||
addNode(ev.target);
|
||||
process();
|
||||
if ( tagToTypeMap[ev.target.localName] !== undefined ) {
|
||||
add(ev.target);
|
||||
process();
|
||||
}
|
||||
};
|
||||
document.addEventListener('error', onResourceFailed, true);
|
||||
|
||||
vAPI.shutdown.add(function() {
|
||||
if ( timer !== null ) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
if ( iframeSourceObserver !== null ) {
|
||||
iframeSourceObserver.disconnect();
|
||||
iframeSourceObserver = null;
|
||||
}
|
||||
document.removeEventListener('error', onResourceFailed, true);
|
||||
newRequests = [];
|
||||
pendingRequests = {};
|
||||
pendingRequestCount = 0;
|
||||
if ( iframeSourceObserver !== undefined ) {
|
||||
iframeSourceObserver.disconnect();
|
||||
iframeSourceObserver = undefined;
|
||||
}
|
||||
if ( processTimer !== undefined ) {
|
||||
clearTimeout(processTimer);
|
||||
processTimer = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
addNodes: addNodes,
|
||||
addBranches: addBranches,
|
||||
addMany: addMany,
|
||||
addIFrames: addIFrames,
|
||||
addNodeList: addNodeList,
|
||||
process: process
|
||||
};
|
||||
})();
|
||||
|
@ -345,10 +337,6 @@ var hasInlineScript = function(nodeList, summary) {
|
|||
if ( node.nodeType !== 1 ) {
|
||||
continue;
|
||||
}
|
||||
if ( typeof node.localName !== 'string' ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( node.localName === 'script' ) {
|
||||
text = node.textContent.trim();
|
||||
if ( text === '' ) {
|
||||
|
@ -357,7 +345,6 @@ var hasInlineScript = function(nodeList, summary) {
|
|||
summary.inlineScript = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( node.localName === 'a' && node.href.lastIndexOf('javascript', 0) === 0 ) {
|
||||
summary.inlineScript = true;
|
||||
break;
|
||||
|
@ -368,8 +355,6 @@ var hasInlineScript = function(nodeList, summary) {
|
|||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var nodeListsAddedHandler = function(nodeLists) {
|
||||
var i = nodeLists.length;
|
||||
if ( i === 0 ) {
|
||||
|
@ -385,7 +370,7 @@ var nodeListsAddedHandler = function(nodeLists) {
|
|||
if ( summary.inlineScript === false ) {
|
||||
hasInlineScript(nodeLists[i], summary);
|
||||
}
|
||||
collapser.addBranches(nodeLists[i]);
|
||||
collapser.addNodeList(nodeLists[i]);
|
||||
}
|
||||
if ( summary.mustReport ) {
|
||||
vAPI.messaging.send('contentscript.js', summary);
|
||||
|
@ -415,7 +400,8 @@ var nodeListsAddedHandler = function(nodeLists) {
|
|||
|
||||
vAPI.messaging.send('contentscript.js', summary);
|
||||
|
||||
collapser.addNodes(document.querySelectorAll('iframe,img'));
|
||||
collapser.addMany(document.querySelectorAll('img'));
|
||||
collapser.addIFrames(document.querySelectorAll('iframe'));
|
||||
collapser.process();
|
||||
})();
|
||||
|
||||
|
@ -427,6 +413,9 @@ var nodeListsAddedHandler = function(nodeLists) {
|
|||
// Added node lists will be cumulated here before being processed
|
||||
|
||||
(function() {
|
||||
// This fixes http://acid3.acidtests.org/
|
||||
if ( !document.body ) { return; }
|
||||
|
||||
var addedNodeLists = [];
|
||||
var addedNodeListsTimer = null;
|
||||
|
||||
|
@ -439,28 +428,19 @@ var nodeListsAddedHandler = function(nodeLists) {
|
|||
// https://github.com/gorhill/uBlock/issues/205
|
||||
// Do not handle added node directly from within mutation observer.
|
||||
var treeMutationObservedHandlerAsync = function(mutations) {
|
||||
var iMutation = mutations.length;
|
||||
var nodeList;
|
||||
var iMutation = mutations.length,
|
||||
nodeList;
|
||||
while ( iMutation-- ) {
|
||||
nodeList = mutations[iMutation].addedNodes;
|
||||
if ( nodeList.length !== 0 ) {
|
||||
addedNodeLists.push(nodeList);
|
||||
}
|
||||
}
|
||||
// I arbitrarily chose 250 ms for now:
|
||||
// I have to compromise between the overhead of processing too few
|
||||
// nodes too often and the delay of many nodes less often. There is nothing
|
||||
// time critical here.
|
||||
if ( addedNodeListsTimer === null ) {
|
||||
addedNodeListsTimer = vAPI.setTimeout(treeMutationObservedHandler, 250);
|
||||
addedNodeListsTimer = vAPI.setTimeout(treeMutationObservedHandler, 47);
|
||||
}
|
||||
};
|
||||
|
||||
// This fixes http://acid3.acidtests.org/
|
||||
if ( !document.body ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// https://github.com/gorhill/httpswitchboard/issues/176
|
||||
var treeObserver = new MutationObserver(treeMutationObservedHandlerAsync);
|
||||
treeObserver.observe(document.body, {
|
||||
|
|
|
@ -39,6 +39,9 @@ var uniqueIdGenerator = 1;
|
|||
var Matrix = function() {
|
||||
this.id = uniqueIdGenerator++;
|
||||
this.reset();
|
||||
this.sourceRegister = '';
|
||||
this.decomposedSourceRegister = [''];
|
||||
this.specificityRegister = 0;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -141,9 +144,7 @@ var isIPAddress = function(hostname) {
|
|||
/******************************************************************************/
|
||||
|
||||
var toBroaderHostname = function(hostname) {
|
||||
if ( hostname === '*' ) {
|
||||
return '';
|
||||
}
|
||||
if ( hostname === '*' ) { return ''; }
|
||||
if ( isIPAddress(hostname) ) {
|
||||
return toBroaderIPAddress(hostname);
|
||||
}
|
||||
|
@ -192,6 +193,20 @@ Matrix.prototype.reset = function() {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
Matrix.prototype.decomposeSource = function(srcHostname) {
|
||||
if ( srcHostname === this.sourceRegister ) { return; }
|
||||
var hn = srcHostname;
|
||||
this.decomposedSourceRegister[0] = this.sourceRegister = hn;
|
||||
var i = 1;
|
||||
for (;;) {
|
||||
hn = toBroaderHostname(hn);
|
||||
this.decomposedSourceRegister[i++] = hn;
|
||||
if ( hn === '' ) { break; }
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Copy another matrix to self. Do this incrementally to minimize impact on
|
||||
// a live matrix.
|
||||
|
||||
|
@ -331,10 +346,13 @@ Matrix.prototype.evaluateCell = function(srcHostname, desHostname, type) {
|
|||
/******************************************************************************/
|
||||
|
||||
Matrix.prototype.evaluateCellZ = function(srcHostname, desHostname, type) {
|
||||
var bitOffset = typeBitOffsets.get(type);
|
||||
var s = srcHostname;
|
||||
var v;
|
||||
this.decomposeSource(srcHostname);
|
||||
|
||||
var bitOffset = typeBitOffsets.get(type),
|
||||
s, v, i = 0;
|
||||
for (;;) {
|
||||
s = this.decomposedSourceRegister[i++];
|
||||
if ( s === '' ) { break; }
|
||||
v = this.rules.get(s + ' ' + desHostname);
|
||||
if ( v !== undefined ) {
|
||||
v = v >> bitOffset & 3;
|
||||
|
@ -342,9 +360,6 @@ Matrix.prototype.evaluateCellZ = function(srcHostname, desHostname, type) {
|
|||
return v;
|
||||
}
|
||||
}
|
||||
// TODO: external rules? (for presets)
|
||||
s = toBroaderHostname(s);
|
||||
if ( s === '' ) { break; }
|
||||
}
|
||||
// srcHostname is '*' at this point
|
||||
|
||||
|
@ -366,6 +381,7 @@ Matrix.prototype.evaluateCellZ = function(srcHostname, desHostname, type) {
|
|||
|
||||
Matrix.prototype.evaluateCellZXY = function(srcHostname, desHostname, type) {
|
||||
// Matrix filtering switch
|
||||
this.specificityRegister = 0;
|
||||
if ( this.evaluateSwitchZ('matrix-off', srcHostname) ) {
|
||||
return Matrix.GreenIndirect;
|
||||
}
|
||||
|
@ -377,11 +393,13 @@ Matrix.prototype.evaluateCellZXY = function(srcHostname, desHostname, type) {
|
|||
// evaluating net requests.
|
||||
|
||||
// Specific-hostname specific-type cell
|
||||
this.specificityRegister = 1;
|
||||
var r = this.evaluateCellZ(srcHostname, desHostname, type);
|
||||
if ( r === 1 ) { return Matrix.RedDirect; }
|
||||
if ( r === 2 ) { return Matrix.GreenDirect; }
|
||||
|
||||
// Specific-hostname any-type cell
|
||||
this.specificityRegister = 2;
|
||||
var rl = this.evaluateCellZ(srcHostname, desHostname, '*');
|
||||
if ( rl === 1 ) { return Matrix.RedIndirect; }
|
||||
|
||||
|
@ -390,10 +408,9 @@ Matrix.prototype.evaluateCellZXY = function(srcHostname, desHostname, type) {
|
|||
|
||||
// Ancestor cells, up to 1st-party destination domain
|
||||
if ( firstPartyDesDomain !== '' ) {
|
||||
this.specificityRegister = 3;
|
||||
for (;;) {
|
||||
if ( d === firstPartyDesDomain ) {
|
||||
break;
|
||||
}
|
||||
if ( d === firstPartyDesDomain ) { break; }
|
||||
d = d.slice(d.indexOf('.') + 1);
|
||||
|
||||
// specific-hostname specific-type cell
|
||||
|
@ -420,11 +437,10 @@ Matrix.prototype.evaluateCellZXY = function(srcHostname, desHostname, type) {
|
|||
}
|
||||
|
||||
// Keep going, up to root
|
||||
this.specificityRegister = 4;
|
||||
for (;;) {
|
||||
d = toBroaderHostname(d);
|
||||
if ( d === '*' ) {
|
||||
break;
|
||||
}
|
||||
if ( d === '*' ) { break; }
|
||||
|
||||
// specific-hostname specific-type cell
|
||||
r = this.evaluateCellZ(srcHostname, d, type);
|
||||
|
@ -438,6 +454,7 @@ Matrix.prototype.evaluateCellZXY = function(srcHostname, desHostname, type) {
|
|||
}
|
||||
|
||||
// Any-hostname specific-type cells
|
||||
this.specificityRegister = 5;
|
||||
r = this.evaluateCellZ(srcHostname, '*', type);
|
||||
// Line below is strict-blocking
|
||||
if ( r === 1 ) { return Matrix.RedIndirect; }
|
||||
|
@ -446,6 +463,7 @@ Matrix.prototype.evaluateCellZXY = function(srcHostname, desHostname, type) {
|
|||
if ( r === 2 ) { return Matrix.GreenIndirect; }
|
||||
|
||||
// Any-hostname any-type cell
|
||||
this.specificityRegister = 6;
|
||||
r = this.evaluateCellZ(srcHostname, '*', '*');
|
||||
if ( r === 1 ) { return Matrix.RedIndirect; }
|
||||
if ( r === 2 ) { return Matrix.GreenIndirect; }
|
||||
|
@ -534,12 +552,14 @@ Matrix.prototype.evaluateSwitch = function(switchName, srcHostname) {
|
|||
|
||||
Matrix.prototype.evaluateSwitchZ = function(switchName, srcHostname) {
|
||||
var bitOffset = switchBitOffsets.get(switchName);
|
||||
if ( bitOffset === undefined ) {
|
||||
return false;
|
||||
}
|
||||
var bits;
|
||||
var s = srcHostname;
|
||||
if ( bitOffset === undefined ) { return false; }
|
||||
|
||||
this.decomposeSource(srcHostname);
|
||||
|
||||
var s, bits, i = 0;
|
||||
for (;;) {
|
||||
s = this.decomposedSourceRegister[i++];
|
||||
if ( s === '' ) { break; }
|
||||
bits = this.switches.get(s) || 0;
|
||||
if ( bits !== 0 ) {
|
||||
bits = bits >> bitOffset & 3;
|
||||
|
@ -547,10 +567,6 @@ Matrix.prototype.evaluateSwitchZ = function(switchName, srcHostname) {
|
|||
return bits === 1;
|
||||
}
|
||||
}
|
||||
s = toBroaderHostname(s);
|
||||
if ( s === '' ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
|
|
@ -427,68 +427,58 @@ var contentScriptSummaryHandler = function(tabId, details) {
|
|||
/******************************************************************************/
|
||||
|
||||
var contentScriptLocalStorageHandler = function(tabId, pageURL) {
|
||||
var µmuri = µm.URI.set(pageURL);
|
||||
var response = µm.mustBlock(µm.scopeFromURL(pageURL), µmuri.hostname, 'cookie');
|
||||
µm.recordFromTabId(
|
||||
tabId,
|
||||
'cookie',
|
||||
µmuri.rootURL() + '/{localStorage}',
|
||||
response
|
||||
var tabContext = µm.tabContextManager.lookup(tabId);
|
||||
if ( tabContext === null ) { return; }
|
||||
|
||||
var blocked = µm.mustBlock(
|
||||
tabContext.rootHostname,
|
||||
µm.URI.hostnameFromURI(pageURL),
|
||||
'cookie'
|
||||
);
|
||||
response = response && µm.userSettings.deleteLocalStorage;
|
||||
if ( response ) {
|
||||
|
||||
var pageStore = µm.pageStoreFromTabId(tabId);
|
||||
if ( pageStore !== null ) {
|
||||
var requestURL = µm.URI.originFromURI(pageURL) + '/{localStorage}';
|
||||
pageStore.recordRequest('cookie', requestURL, blocked);
|
||||
µm.logger.writeOne(tabId, 'net', tabContext.rootHostname, requestURL, 'cookie', blocked);
|
||||
}
|
||||
|
||||
var removeStorage = blocked && µm.userSettings.deleteLocalStorage;
|
||||
if ( removeStorage ) {
|
||||
µm.localStorageRemovedCounter++;
|
||||
}
|
||||
return response;
|
||||
|
||||
return removeStorage;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Evaluate many URLs against the matrix.
|
||||
|
||||
var evaluateURLs = function(tabId, requests) {
|
||||
var collapse = µm.userSettings.collapseBlocked;
|
||||
var lookupBlockedCollapsibles = function(tabId, requests) {
|
||||
var response = {
|
||||
collapse: collapse,
|
||||
requests: requests
|
||||
blockedResources: [],
|
||||
hash: requests.hash,
|
||||
id: requests.id,
|
||||
placeholders: placeholders
|
||||
};
|
||||
|
||||
// Create evaluation context
|
||||
var tabContext = µm.tabContextManager.lookup(tabId);
|
||||
if ( tabContext === null ) {
|
||||
return response;
|
||||
}
|
||||
var rootHostname = tabContext.rootHostname;
|
||||
|
||||
//console.debug('messaging.js/contentscript.js: processing %d requests', requests.length);
|
||||
|
||||
var pageStore = µm.pageStoreFromTabId(tabId);
|
||||
var µmuri = µm.URI;
|
||||
var typeMap = tagNameToRequestTypeMap;
|
||||
var request, type;
|
||||
var i = requests.length;
|
||||
while ( i-- ) {
|
||||
request = requests[i];
|
||||
type = typeMap[request.tagName];
|
||||
request.blocked = µm.mustBlock(
|
||||
rootHostname,
|
||||
µmuri.hostnameFromURI(request.url),
|
||||
type
|
||||
);
|
||||
// https://github.com/gorhill/uMatrix/issues/205
|
||||
// If blocked, the URL must be recorded by the page store, so as to ensure
|
||||
// they are properly reflected in the matrix.
|
||||
if ( request.blocked && pageStore ) {
|
||||
pageStore.recordRequest(type, request.url, true);
|
||||
}
|
||||
if ( pageStore !== null ) {
|
||||
pageStore.lookupBlockedCollapsibles(requests, response);
|
||||
}
|
||||
|
||||
if ( collapse ) {
|
||||
placeholders = null;
|
||||
return response;
|
||||
}
|
||||
// TODO: evaluate whether the issue reported below still exists.
|
||||
// https://github.com/gorhill/uMatrix/issues/205
|
||||
// If blocked, the URL must be recorded by the page store, so as to
|
||||
// ensure they are properly reflected in the matrix.
|
||||
|
||||
if ( placeholders === null ) {
|
||||
if ( response.placeholders === null ) {
|
||||
placeholders = {
|
||||
background:
|
||||
vAPI.localStorage.getItem('placeholderBackground') ||
|
||||
|
@ -505,19 +495,12 @@ var evaluateURLs = function(tabId, requests) {
|
|||
};
|
||||
placeholders.iframe =
|
||||
placeholders.iframe.replace('{{bg}}', placeholders.background);
|
||||
response.placeholders = placeholders;
|
||||
}
|
||||
response.placeholders = placeholders;
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var tagNameToRequestTypeMap = {
|
||||
'iframe': 'frame',
|
||||
'img': 'image'
|
||||
};
|
||||
|
||||
var placeholders = null;
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -544,8 +527,8 @@ var onMessage = function(request, sender, callback) {
|
|||
contentScriptSummaryHandler(tabId, request);
|
||||
break;
|
||||
|
||||
case 'evaluateURLs':
|
||||
response = evaluateURLs(tabId, request.requests);
|
||||
case 'lookupBlockedCollapsibles':
|
||||
response = lookupBlockedCollapsibles(tabId, request);
|
||||
break;
|
||||
|
||||
case 'mustRenderNoscriptTags?':
|
||||
|
|
|
@ -25,117 +25,245 @@
|
|||
|
||||
µMatrix.pageStoreFactory = (function() {
|
||||
|
||||
var µm = µMatrix;
|
||||
var pageStoreJunkyard = [];
|
||||
/******************************************************************************/
|
||||
|
||||
// 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 µMatrix.
|
||||
var uidFromURL = function(uri) {
|
||||
var hint = 0x811c9dc5;
|
||||
var i = uri.length;
|
||||
var µm = µMatrix;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var 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(); }
|
||||
var 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;
|
||||
var obsolete = Date.now() - this.shelfLife;
|
||||
for ( var 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.
|
||||
|
||||
var 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.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) {
|
||||
var tabContext = µm.tabContextManager.lookup(this.tabId);
|
||||
if ( tabContext === null ) { return; }
|
||||
|
||||
var collapseBlacklisted = µm.userSettings.collapseBlacklisted,
|
||||
collapseBlocked = µm.userSettings.collapseBlocked,
|
||||
entry;
|
||||
|
||||
var blockedResources = response.blockedResources;
|
||||
|
||||
if (
|
||||
Array.isArray(request.toFilter) &&
|
||||
request.toFilter.length !== 0
|
||||
) {
|
||||
var roothn = tabContext.rootHostname,
|
||||
hnFromURI = µm.URI.hostnameFromURI,
|
||||
tMatrix = µm.tMatrix;
|
||||
for ( 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 ( entry of this.blockedCollapsibles.blocked ) {
|
||||
blockedResources.push([
|
||||
entry[0],
|
||||
collapseBlocked || collapseBlacklisted && (entry[1] & 1) !== 0
|
||||
]);
|
||||
}
|
||||
},
|
||||
|
||||
recordRequest: function(type, url, block) {
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
if ( block !== false ) {
|
||||
this.perLoadBlockedRequestCount++;
|
||||
} else {
|
||||
this.perLoadAllowedRequestCount++;
|
||||
}
|
||||
|
||||
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,
|
||||
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;
|
||||
};
|
||||
|
||||
function PageStore(tabContext) {
|
||||
this.requestStats = µm.requestStatsFactory();
|
||||
this.off = false;
|
||||
this.init(tabContext);
|
||||
}
|
||||
};
|
||||
|
||||
PageStore.prototype = {
|
||||
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 = new Map();
|
||||
this.domains = new Set();
|
||||
this.allHostnamesString = ' ';
|
||||
this.requestStats.reset();
|
||||
this.distinctRequestCount = 0;
|
||||
this.perLoadAllowedRequestCount = 0;
|
||||
this.perLoadBlockedRequestCount = 0;
|
||||
this.incinerationTimer = null;
|
||||
this.mtxContentModifiedTime = 0;
|
||||
this.mtxCountModifiedTime = 0;
|
||||
return this;
|
||||
},
|
||||
dispose: function() {
|
||||
this.hostnameTypeCells.clear();
|
||||
this.rawUrl = '';
|
||||
this.pageUrl = '';
|
||||
this.pageHostname = '';
|
||||
this.pageDomain = '';
|
||||
this.title = '';
|
||||
this.domains.clear();
|
||||
this.allHostnamesString = ' ';
|
||||
if ( this.incinerationTimer !== null ) {
|
||||
clearTimeout(this.incinerationTimer);
|
||||
this.incinerationTimer = null;
|
||||
}
|
||||
if ( pageStoreJunkyard.length < 8 ) {
|
||||
pageStoreJunkyard.push(this);
|
||||
}
|
||||
},
|
||||
recordRequest: function(type, url, block) {
|
||||
var hostname = µm.URI.hostnameFromURI(url);
|
||||
/******************************************************************************/
|
||||
|
||||
// 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 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 = uidFromURL(url);
|
||||
if ( uids.has(uid) ) { return; }
|
||||
uids.add(uid);
|
||||
return function pageStoreFactory(tabContext) {
|
||||
var entry = PageStore.prototype.pageStoreJunkyard.pop();
|
||||
if ( entry ) {
|
||||
return entry.init(tabContext);
|
||||
}
|
||||
return new PageStore(tabContext);
|
||||
};
|
||||
|
||||
// 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);
|
||||
|
||||
if ( block !== false ) {
|
||||
this.perLoadBlockedRequestCount++;
|
||||
} else {
|
||||
this.perLoadAllowedRequestCount++;
|
||||
}
|
||||
|
||||
this.distinctRequestCount++;
|
||||
this.mtxCountModifiedTime = Date.now();
|
||||
|
||||
if ( this.domains.has(hostname) === false ) {
|
||||
this.domains.add(hostname);
|
||||
this.allHostnamesString += hostname + ' ';
|
||||
this.mtxContentModifiedTime = Date.now();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return function pageStoreFactory(tabContext) {
|
||||
var entry = pageStoreJunkyard.pop();
|
||||
if ( entry ) {
|
||||
return entry.init(tabContext);
|
||||
}
|
||||
return new PageStore(tabContext);
|
||||
};
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
@ -53,16 +53,16 @@ function changeMatrixSwitch(name, state) {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
function onChangeValueHandler(uelem, setting, min, max) {
|
||||
function onChangeValueHandler(elem, setting, min, max) {
|
||||
var oldVal = cachedSettings.userSettings[setting];
|
||||
var newVal = Math.round(parseFloat(uelem.val()));
|
||||
var newVal = Math.round(parseFloat(elem.value));
|
||||
if ( typeof newVal !== 'number' ) {
|
||||
newVal = oldVal;
|
||||
} else {
|
||||
newVal = Math.max(newVal, min);
|
||||
newVal = Math.min(newVal, max);
|
||||
}
|
||||
uelem.val(newVal);
|
||||
elem.value = newVal;
|
||||
if ( newVal !== oldVal ) {
|
||||
changeUserSettings(setting, newVal);
|
||||
}
|
||||
|
@ -71,50 +71,89 @@ function onChangeValueHandler(uelem, setting, min, max) {
|
|||
/******************************************************************************/
|
||||
|
||||
function prepareToDie() {
|
||||
onChangeValueHandler(uDom('#delete-unused-session-cookies-after'), 'deleteUnusedSessionCookiesAfter', 15, 1440);
|
||||
onChangeValueHandler(uDom('#clear-browser-cache-after'), 'clearBrowserCacheAfter', 15, 1440);
|
||||
onChangeValueHandler(
|
||||
uDom.nodeFromId('deleteUnusedSessionCookiesAfter'),
|
||||
'deleteUnusedSessionCookiesAfter',
|
||||
15, 1440
|
||||
);
|
||||
onChangeValueHandler(
|
||||
uDom.nodeFromId('clearBrowserCacheAfter'),
|
||||
'clearBrowserCacheAfter',
|
||||
15, 1440
|
||||
);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var installEventHandlers = function() {
|
||||
uDom('input[name="displayTextSize"]').on('change', function(){
|
||||
changeUserSettings('displayTextSize', this.value);
|
||||
});
|
||||
function onInputChanged(ev) {
|
||||
var target = ev.target;
|
||||
|
||||
uDom('#popupScopeLevel').on('change', function(){
|
||||
changeUserSettings('popupScopeLevel', this.value);
|
||||
});
|
||||
switch ( target.id ) {
|
||||
case 'displayTextSizeNormal':
|
||||
case 'displayTextSizeLarge':
|
||||
changeUserSettings('displayTextSize', target.value);
|
||||
break;
|
||||
case 'clearBrowserCache':
|
||||
case 'cloudStorageEnabled':
|
||||
case 'collapseBlacklisted':
|
||||
case 'collapseBlocked':
|
||||
case 'colorBlindFriendly':
|
||||
case 'deleteCookies':
|
||||
case 'deleteLocalStorage':
|
||||
case 'deleteUnusedSessionCookies':
|
||||
case 'iconBadgeEnabled':
|
||||
case 'processHyperlinkAuditing':
|
||||
changeUserSettings(target.id, target.checked);
|
||||
break;
|
||||
case 'noMixedContent':
|
||||
case 'processReferer':
|
||||
changeMatrixSwitch(
|
||||
target.getAttribute('data-matrix-switch'),
|
||||
target.checked
|
||||
);
|
||||
break;
|
||||
case 'deleteUnusedSessionCookiesAfter':
|
||||
onChangeValueHandler(target, 'deleteUnusedSessionCookiesAfter', 15, 1440);
|
||||
break;
|
||||
case 'clearBrowserCacheAfter':
|
||||
onChangeValueHandler(target, 'clearBrowserCacheAfter', 15, 1440);
|
||||
break;
|
||||
case 'popupScopeLevel':
|
||||
changeUserSettings('popupScopeLevel', target.value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
uDom('[data-setting-bool]').on('change', function(){
|
||||
var settingName = this.getAttribute('data-setting-bool');
|
||||
if ( typeof settingName === 'string' && settingName !== '' ) {
|
||||
changeUserSettings(settingName, this.checked);
|
||||
}
|
||||
});
|
||||
|
||||
uDom('[data-matrix-switch]').on('change', function(){
|
||||
var switchName = this.getAttribute('data-matrix-switch');
|
||||
if ( typeof switchName === 'string' && switchName !== '' ) {
|
||||
changeMatrixSwitch(switchName, this.checked);
|
||||
}
|
||||
});
|
||||
|
||||
uDom('#delete-unused-session-cookies-after').on('change', function(){
|
||||
onChangeValueHandler(uDom(this), 'deleteUnusedSessionCookiesAfter', 15, 1440);
|
||||
});
|
||||
uDom('#clear-browser-cache-after').on('change', function(){
|
||||
onChangeValueHandler(uDom(this), 'clearBrowserCacheAfter', 15, 1440);
|
||||
});
|
||||
|
||||
// https://github.com/gorhill/httpswitchboard/issues/197
|
||||
uDom(window).on('beforeunload', prepareToDie);
|
||||
};
|
||||
switch ( target.id ) {
|
||||
case 'collapseBlocked':
|
||||
synchronizeWidgets();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
uDom.onLoad(function() {
|
||||
var onSettingsReceived = function(settings) {
|
||||
function synchronizeWidgets() {
|
||||
var e1, e2;
|
||||
|
||||
e1 = uDom.nodeFromId('collapseBlocked');
|
||||
e2 = uDom.nodeFromId('collapseBlacklisted');
|
||||
if ( e1.checked ) {
|
||||
e2.setAttribute('disabled', '');
|
||||
} else {
|
||||
e2.removeAttribute('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.messaging.send(
|
||||
'settings.js',
|
||||
{ what: 'getUserSettings' },
|
||||
function onSettingsReceived(settings) {
|
||||
// Cache copy
|
||||
cachedSettings = settings;
|
||||
|
||||
|
@ -122,10 +161,7 @@ uDom.onLoad(function() {
|
|||
var matrixSwitches = settings.matrixSwitches;
|
||||
|
||||
uDom('[data-setting-bool]').forEach(function(elem){
|
||||
var settingName = elem.attr('data-setting-bool');
|
||||
if ( typeof settingName === 'string' && settingName !== '' ) {
|
||||
elem.prop('checked', userSettings[settingName] === true);
|
||||
}
|
||||
elem.prop('checked', userSettings[elem.prop('id')] === true);
|
||||
});
|
||||
|
||||
uDom('[data-matrix-switch]').forEach(function(elem){
|
||||
|
@ -140,18 +176,19 @@ uDom.onLoad(function() {
|
|||
});
|
||||
|
||||
uDom.nodeFromId('popupScopeLevel').value = userSettings.popupScopeLevel;
|
||||
uDom.nodeFromId('deleteUnusedSessionCookiesAfter').value =
|
||||
userSettings.deleteUnusedSessionCookiesAfter;
|
||||
uDom.nodeFromId('clearBrowserCacheAfter').value =
|
||||
userSettings.clearBrowserCacheAfter;
|
||||
|
||||
uDom('#delete-unused-session-cookies-after').val(userSettings.deleteUnusedSessionCookiesAfter);
|
||||
uDom('#clear-browser-cache-after').val(userSettings.clearBrowserCacheAfter);
|
||||
synchronizeWidgets();
|
||||
|
||||
installEventHandlers();
|
||||
};
|
||||
vAPI.messaging.send(
|
||||
'settings.js',
|
||||
{ what: 'getUserSettings' },
|
||||
onSettingsReceived
|
||||
);
|
||||
});
|
||||
document.addEventListener('change', onInputChanged);
|
||||
|
||||
// https://github.com/gorhill/httpswitchboard/issues/197
|
||||
uDom(window).on('beforeunload', prepareToDie);
|
||||
}
|
||||
);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
@ -552,19 +552,6 @@ vAPI.tabs.registerListeners();
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
// Log a request
|
||||
|
||||
µm.recordFromTabId = function(tabId, type, url, blocked) {
|
||||
var pageStore = this.pageStoreFromTabId(tabId);
|
||||
if ( pageStore === null ) {
|
||||
return;
|
||||
}
|
||||
pageStore.recordRequest(type, url, blocked);
|
||||
this.logger.writeOne(tabId, 'net', pageStore.pageHostname, url, type, blocked);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
µm.forceReload = function(tabId, bypassCache) {
|
||||
vAPI.tabs.reload(tabId, bypassCache);
|
||||
};
|
||||
|
|
|
@ -75,26 +75,19 @@ var onBeforeRootFrameRequestHandler = function(details) {
|
|||
// Intercept and filter web requests according to white and black lists.
|
||||
|
||||
var onBeforeRequestHandler = function(details) {
|
||||
var µm = µMatrix,
|
||||
µmuri = µm.URI;
|
||||
|
||||
// rhill 2014-02-17: Ignore 'filesystem:': this can happen when listening
|
||||
// to 'chrome-extension://'.
|
||||
var requestScheme = µmuri.schemeFromURI(details.url);
|
||||
if ( requestScheme === 'filesystem' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var requestType = requestTypeNormalizer[details.type] || 'other';
|
||||
|
||||
// https://github.com/gorhill/httpswitchboard/issues/303
|
||||
// Wherever the main doc comes from, create a receiver page URL: synthetize
|
||||
// one if needed.
|
||||
if ( requestType === 'doc' && details.parentFrameId < 0 ) {
|
||||
if ( requestType === 'doc' && details.parentFrameId === -1 ) {
|
||||
return onBeforeRootFrameRequestHandler(details);
|
||||
}
|
||||
|
||||
var requestURL = details.url;
|
||||
var µm = µMatrix,
|
||||
µmuri = µm.URI,
|
||||
requestURL = details.url,
|
||||
requestScheme = µmuri.schemeFromURI(requestURL);
|
||||
|
||||
// Ignore non-network schemes
|
||||
if ( µmuri.isNetworkScheme(requestScheme) === false ) {
|
||||
|
@ -109,13 +102,24 @@ var onBeforeRequestHandler = function(details) {
|
|||
// to scope on unknown scheme? Etc.
|
||||
// https://github.com/gorhill/httpswitchboard/issues/191
|
||||
// https://github.com/gorhill/httpswitchboard/issues/91#issuecomment-37180275
|
||||
var tabContext = µm.tabContextManager.mustLookup(details.tabId);
|
||||
var tabId = tabContext.tabId;
|
||||
var rootHostname = tabContext.rootHostname;
|
||||
var tabContext = µm.tabContextManager.mustLookup(details.tabId),
|
||||
tabId = tabContext.tabId,
|
||||
rootHostname = tabContext.rootHostname,
|
||||
specificity = 0;
|
||||
|
||||
// Filter through matrix
|
||||
var block = µm.tMatrix.mustBlock(
|
||||
rootHostname,
|
||||
µmuri.hostnameFromURI(requestURL),
|
||||
requestType
|
||||
);
|
||||
if ( block ) {
|
||||
specificity = µm.tMatrix.specificityRegister;
|
||||
}
|
||||
|
||||
// Enforce strict secure connection?
|
||||
var block = false;
|
||||
if (
|
||||
block === false &&
|
||||
tabContext.secure &&
|
||||
µmuri.isSecureScheme(requestScheme) === false &&
|
||||
µm.tMatrix.evaluateSwitchZ('https-strict', rootHostname)
|
||||
|
@ -123,11 +127,6 @@ var onBeforeRequestHandler = function(details) {
|
|||
block = true;
|
||||
}
|
||||
|
||||
// Disallow request as per temporary matrix?
|
||||
if ( block === false ) {
|
||||
block = µm.mustBlock(rootHostname, µmuri.hostnameFromURI(requestURL), requestType);
|
||||
}
|
||||
|
||||
// Record request.
|
||||
// https://github.com/gorhill/httpswitchboard/issues/342
|
||||
// The way requests are handled now, it may happen at this point some
|
||||
|
@ -138,16 +137,10 @@ var onBeforeRequestHandler = function(details) {
|
|||
pageStore.recordRequest(requestType, requestURL, block);
|
||||
µm.logger.writeOne(tabId, 'net', rootHostname, requestURL, details.type, block);
|
||||
|
||||
// Allowed?
|
||||
if ( !block ) {
|
||||
// console.debug('onBeforeRequestHandler()> ALLOW "%s": %o', details.url, details);
|
||||
return;
|
||||
if ( block ) {
|
||||
pageStore.cacheBlockedCollapsible(requestType, requestURL, specificity);
|
||||
return { 'cancel': true };
|
||||
}
|
||||
|
||||
// Blocked
|
||||
// console.debug('onBeforeRequestHandler()> BLOCK "%s": %o', details.url, details);
|
||||
|
||||
return { 'cancel': true };
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
@ -33,17 +33,17 @@ ul > li.separator {
|
|||
|
||||
<h2 data-i18n="settingsMatrixConvenienceHeader"></h2>
|
||||
<ul>
|
||||
<li>
|
||||
<input id="iconBadgeEnabled" type="checkbox" data-setting-bool="iconBadgeEnabled">
|
||||
<li><input id="iconBadgeEnabled" type="checkbox" data-setting-bool>
|
||||
<label data-i18n="settingsIconBadgeEnabled" for="iconBadgeEnabled"></label>
|
||||
<li>
|
||||
<input id="collapseBlocked" type="checkbox" data-setting-bool="collapseBlocked">
|
||||
<li><input id="collapseBlocked" type="checkbox" data-setting-bool>
|
||||
<label data-i18n="settingsCollapseBlocked" for="collapseBlocked"></label>
|
||||
<li>
|
||||
<input id="noscriptTagsSpoofed" type="checkbox" data-matrix-switch="noscript-spoof">
|
||||
<ul>
|
||||
<li><input id="collapseBlacklisted" type="checkbox" data-setting-bool>
|
||||
<label data-i18n="settingsCollapseBlacklisted" for="collapseBlacklisted"></label>
|
||||
</ul>
|
||||
<li><input id="noscriptTagsSpoofed" type="checkbox" data-matrix-switch="noscript-spoof">
|
||||
<label data-i18n="settingsNoscriptTagsSpoofed" for="noscriptTagsSpoofed"></label>
|
||||
<li>
|
||||
<input id="cloudStorageEnabled" type="checkbox" data-setting-bool="cloudStorageEnabled">
|
||||
<li><input id="cloudStorageEnabled" type="checkbox" data-setting-bool>
|
||||
<label data-i18n="settingsCloudStorageEnabled" for="cloudStorageEnabled"></label>
|
||||
</ul>
|
||||
<h2 data-i18n="settingsMatrixDisplayHeader"></h2>
|
||||
|
@ -56,18 +56,18 @@ ul > li.separator {
|
|||
<label data-i18n="settingsDefaultScopeLevel"></label> <select id="popupScopeLevel"><option data-i18n="settingsDefaultScopeLevel2" value="site"><option data-i18n="settingsDefaultScopeLevel1" value="domain"><option data-i18n="settingsDefaultScopeLevel0" value="*"></select>
|
||||
<li class="separator">
|
||||
<li>
|
||||
<input id="colorBlindFriendly" type="checkbox" data-setting-bool="colorBlindFriendly">
|
||||
<input id="colorBlindFriendly" type="checkbox" data-setting-bool>
|
||||
<label data-i18n="settingsMatrixDisplayColorBlind" for="colorBlindFriendly"></label>
|
||||
</ul>
|
||||
<h2 data-i18n="privacyPageName"></h2>
|
||||
<ul>
|
||||
<li>
|
||||
<input id="delete-blacklisted-cookies" type="checkbox" data-setting-bool="deleteCookies"><label data-i18n="privacyDeleteBlockedCookiesPrompt" for="delete-blacklisted-cookies"></label>
|
||||
<input id="deleteCookies" type="checkbox" data-setting-bool><label data-i18n="privacyDeleteBlockedCookiesPrompt" for="deleteCookies"></label>
|
||||
<span class="whatisthis"></span>
|
||||
<div class="whatisthis-expandable para" data-i18n="privacyDeleteBlockedCookiesHelp"></div>
|
||||
<li>
|
||||
<input id="delete-unused-session-cookies" type="checkbox" data-setting-bool="deleteUnusedSessionCookies"><label data-i18n="privacyDeleteNonBlockedSessionCookiesPrompt1" for="delete-unused-session-cookies"></label>
|
||||
<input id="delete-unused-session-cookies-after" type="text" value="60" size="3"><span data-i18n="privacyDeleteNonBlockedSessionCookiesPrompt2"></span>
|
||||
<input id="deleteUnusedSessionCookies" type="checkbox" data-setting-bool><label data-i18n="privacyDeleteNonBlockedSessionCookiesPrompt1" for="deleteUnusedSessionCookies"></label>
|
||||
<input id="deleteUnusedSessionCookiesAfter" type="text" value="60" size="3"><span data-i18n="privacyDeleteNonBlockedSessionCookiesPrompt2"></span>
|
||||
<span class="whatisthis"></span>
|
||||
<div class="whatisthis-expandable para" data-i18n="privacyDeleteNonBlockedSessionCookiesHelp"></div>
|
||||
<!--
|
||||
|
@ -89,22 +89,22 @@ ul > li.separator {
|
|||
of these cookies so that they cannot be used to track you.
|
||||
-->
|
||||
<li>
|
||||
<input id="delete-blacklisted-localstorage" type="checkbox" data-setting-bool="deleteLocalStorage"><label data-i18n="privacyDeleteBlockedLocalStoragePrompt" for="delete-blacklisted-localstorage"></label>
|
||||
<input id="deleteLocalStorage" type="checkbox" data-setting-bool><label data-i18n="privacyDeleteBlockedLocalStoragePrompt" for="deleteLocalStorage"></label>
|
||||
<li>
|
||||
<input id="clear-browser-cache" type="checkbox" data-setting-bool="clearBrowserCache"><label data-i18n="privacyClearCachePrompt1" for="clear-browser-cache"></label>
|
||||
<input id="clear-browser-cache-after" type="text" value="60" size="3"> <label data-i18n="privacyClearCachePrompt2" for="clear-browser-cache-after"></label>
|
||||
<input id="clearBrowserCache" type="checkbox" data-setting-bool><label data-i18n="privacyClearCachePrompt1" for="clearBrowserCache"></label>
|
||||
<input id="clearBrowserCacheAfter" type="text" value="60" size="3"> <label data-i18n="privacyClearCachePrompt2" for="clearBrowserCacheAfter"></label>
|
||||
<span class="whatisthis"></span>
|
||||
<div class="whatisthis-expandable para" data-i18n="privacyClearCacheHelp"></div>
|
||||
<li>
|
||||
<input id="process-referer" type="checkbox" data-matrix-switch="referrer-spoof"><label data-i18n="privacyProcessRefererPrompt" for="process-referer"></label>
|
||||
<input id="processReferer" type="checkbox" data-matrix-switch="referrer-spoof"><label data-i18n="privacyProcessRefererPrompt" for="processReferer"></label>
|
||||
<span class="whatisthis"></span>
|
||||
<div class="whatisthis-expandable para" data-i18n="privacyProcessRefererHelp"></div>
|
||||
<li>
|
||||
<input id="no-mixed-content" type="checkbox" data-matrix-switch="https-strict"><label data-i18n="privacyNoMixedContentPrompt" for="no-mixed-content"></label>
|
||||
<input id="noMixedContent" type="checkbox" data-matrix-switch="https-strict"><label data-i18n="privacyNoMixedContentPrompt" for="noMixedContent"></label>
|
||||
<span class="whatisthis"></span>
|
||||
<div class="whatisthis-expandable para" data-i18n="privacyNoMixedContentHelp"></div>
|
||||
<li>
|
||||
<input id="process-hyperlink-auditing" type="checkbox" data-setting-bool="processHyperlinkAuditing"><label data-i18n="privacyProcessHyperlinkAuditingPrompt" for="process-hyperlink-auditing"></label>
|
||||
<input id="processHyperlinkAuditing" type="checkbox" data-setting-bool><label data-i18n="privacyProcessHyperlinkAuditingPrompt" for="processHyperlinkAuditing"></label>
|
||||
<span class="whatisthis"></span>
|
||||
<div class="whatisthis-expandable para" data-i18n="privacyProcessHyperlinkAuditingHelp"></div>
|
||||
</ul>
|
||||
|
|
Loading…
Reference in a new issue