|
|
@ -1,7 +1,7 @@
|
|
|
|
/*******************************************************************************
|
|
|
|
/*******************************************************************************
|
|
|
|
|
|
|
|
|
|
|
|
µMatrix - a Chromium browser extension to black/white list requests.
|
|
|
|
µMatrix - a Chromium browser extension to black/white list requests.
|
|
|
|
Copyright (C) 2014 Raymond Hill
|
|
|
|
Copyright (C) 2014-2105 Raymond Hill
|
|
|
|
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
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
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
@ -63,18 +63,18 @@ vAPI.contentscriptEndInjected = true;
|
|
|
|
|
|
|
|
|
|
|
|
var localMessager = vAPI.messaging.channel('contentscript-end.js');
|
|
|
|
var localMessager = vAPI.messaging.channel('contentscript-end.js');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vAPI.shutdown.add(function() {
|
|
|
|
|
|
|
|
localMessager.close();
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
// This is to be executed only once: putting this code in its own closure
|
|
|
|
// Unrendered noscript (because CSP) workaround
|
|
|
|
// means the code will be flushed from memory once executed.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*------------[ Unrendered Noscript (because CSP) Workaround ]----------------*/
|
|
|
|
// Executed once.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
var checkScriptBlacklistedHandler = function(response) {
|
|
|
|
var checkScriptBlacklistedHandler = function(response) {
|
|
|
|
if ( !response.scriptBlacklisted ) {
|
|
|
|
if ( !response.scriptBlacklisted ) {
|
|
|
|
return;
|
|
|
|
return;
|
|
|
@ -94,9 +94,14 @@ localMessager.send({
|
|
|
|
what: 'checkScriptBlacklisted',
|
|
|
|
what: 'checkScriptBlacklisted',
|
|
|
|
url: window.location.href
|
|
|
|
url: window.location.href
|
|
|
|
}, checkScriptBlacklistedHandler);
|
|
|
|
}, checkScriptBlacklistedHandler);
|
|
|
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Executed only once.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
var localStorageHandler = function(mustRemove) {
|
|
|
|
var localStorageHandler = function(mustRemove) {
|
|
|
|
if ( mustRemove ) {
|
|
|
|
if ( mustRemove ) {
|
|
|
|
window.localStorage.clear();
|
|
|
|
window.localStorage.clear();
|
|
|
@ -135,16 +140,166 @@ try {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
catch (e) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
})();
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
// 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 = {
|
|
|
|
|
|
|
|
'iframe': 'src',
|
|
|
|
|
|
|
|
'img': 'src'
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var PendingRequest = function(target, tagName, attr) {
|
|
|
|
|
|
|
|
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.collapse = false;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var onProcessed = function(requests) {
|
|
|
|
|
|
|
|
if ( requests === null || Array.isArray(requests) === false ) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var i = requests.length;
|
|
|
|
|
|
|
|
var request, entry;
|
|
|
|
|
|
|
|
while ( i-- ) {
|
|
|
|
|
|
|
|
request = requests[i];
|
|
|
|
|
|
|
|
if ( pendingRequests.hasOwnProperty(request.id) === false ) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = pendingRequests[request.id];
|
|
|
|
|
|
|
|
delete pendingRequests[request.id];
|
|
|
|
|
|
|
|
pendingRequestCount -= 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// https://github.com/chrisaljoudi/uBlock/issues/869
|
|
|
|
|
|
|
|
if ( !request.collapse ) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// https://github.com/chrisaljoudi/uBlock/issues/399
|
|
|
|
|
|
|
|
// Never remove elements from the DOM, just hide them
|
|
|
|
|
|
|
|
entry.target.style.setProperty('display', 'none', '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;
|
|
|
|
|
|
|
|
localMessager.send({
|
|
|
|
|
|
|
|
what: 'evaluateURLs',
|
|
|
|
|
|
|
|
requests: newRequests
|
|
|
|
|
|
|
|
}, onProcessed);
|
|
|
|
|
|
|
|
newRequests = [];
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var process = function(delay) {
|
|
|
|
|
|
|
|
if ( newRequests.length === 0 ) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( delay === 0 ) {
|
|
|
|
|
|
|
|
clearTimeout(timer);
|
|
|
|
|
|
|
|
send();
|
|
|
|
|
|
|
|
} else if ( timer === null ) {
|
|
|
|
|
|
|
|
timer = setTimeout(send, delay || 50);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var addNode = function(target) {
|
|
|
|
|
|
|
|
var tagName = target.localName;
|
|
|
|
|
|
|
|
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, tagName, prop);
|
|
|
|
|
|
|
|
newRequests.push(new BouncingRequest(req.id, tagName, src));
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var addNodes = function(nodes) {
|
|
|
|
|
|
|
|
var node;
|
|
|
|
|
|
|
|
var i = nodes.length;
|
|
|
|
|
|
|
|
while ( i-- ) {
|
|
|
|
|
|
|
|
node = nodes[i];
|
|
|
|
|
|
|
|
if ( node.nodeType === 1 ) {
|
|
|
|
|
|
|
|
addNode(node);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
document.addEventListener('error', onResourceFailed, true);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vAPI.shutdown.add(function() {
|
|
|
|
|
|
|
|
if ( timer !== null ) {
|
|
|
|
|
|
|
|
clearTimeout(timer);
|
|
|
|
|
|
|
|
timer = null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
document.removeEventListener('error', onResourceFailed, true);
|
|
|
|
|
|
|
|
newRequests = [];
|
|
|
|
|
|
|
|
pendingRequests = {};
|
|
|
|
|
|
|
|
pendingRequestCount = 0;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
addNodes: addNodes,
|
|
|
|
|
|
|
|
addBranches: addBranches,
|
|
|
|
|
|
|
|
process: process
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
var nodesAddedHandler = function(nodeList, summary) {
|
|
|
|
var nodesAddedHandler = function(nodeList, summary) {
|
|
|
@ -154,17 +309,17 @@ var nodesAddedHandler = function(nodeList, summary) {
|
|
|
|
if ( node.nodeType !== 1 ) {
|
|
|
|
if ( node.nodeType !== 1 ) {
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( typeof node.tagName !== 'string' ) {
|
|
|
|
if ( typeof node.localName !== 'string' ) {
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch ( node.tagName.toUpperCase() ) {
|
|
|
|
switch ( node.localName ) {
|
|
|
|
|
|
|
|
|
|
|
|
case 'SCRIPT':
|
|
|
|
case 'script':
|
|
|
|
// https://github.com/gorhill/httpswitchboard/issues/252
|
|
|
|
// https://github.com/gorhill/httpswitchboard/issues/252
|
|
|
|
// Do not count µMatrix's own script tags, they are not required
|
|
|
|
// Do not count µMatrix's own script tags, they are not required
|
|
|
|
// to "unbreak" a web page
|
|
|
|
// to "unbreak" a web page
|
|
|
|
if ( node.id && node.id.indexOf('uMatrix-') === 0 ) {
|
|
|
|
if ( typeof node.id === 'string' && node.id.lastIndexOf('uMatrix-', 0) === 0 ) {
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
text = node.textContent.trim();
|
|
|
|
text = node.textContent.trim();
|
|
|
@ -179,14 +334,14 @@ var nodesAddedHandler = function(nodeList, summary) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'A':
|
|
|
|
case 'a':
|
|
|
|
if ( node.href.indexOf('javascript:') === 0 ) {
|
|
|
|
if ( node.href.lastIndexOf('javascript', 0) === 0 ) {
|
|
|
|
summary.scriptSources['{inline_script}'] = true;
|
|
|
|
summary.scriptSources['{inline_script}'] = true;
|
|
|
|
summary.mustReport = true;
|
|
|
|
summary.mustReport = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'OBJECT':
|
|
|
|
case 'object':
|
|
|
|
src = (node.data || '').trim();
|
|
|
|
src = (node.data || '').trim();
|
|
|
|
if ( src !== '' ) {
|
|
|
|
if ( src !== '' ) {
|
|
|
|
summary.pluginSources[src] = true;
|
|
|
|
summary.pluginSources[src] = true;
|
|
|
@ -194,7 +349,7 @@ var nodesAddedHandler = function(nodeList, summary) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'EMBED':
|
|
|
|
case 'embed':
|
|
|
|
src = (node.src || '').trim();
|
|
|
|
src = (node.src || '').trim();
|
|
|
|
if ( src !== '' ) {
|
|
|
|
if ( src !== '' ) {
|
|
|
|
summary.pluginSources[src] = true;
|
|
|
|
summary.pluginSources[src] = true;
|
|
|
@ -221,13 +376,18 @@ var nodeListsAddedHandler = function(nodeLists) {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
while ( i-- ) {
|
|
|
|
while ( i-- ) {
|
|
|
|
nodesAddedHandler(nodeLists[i], summary);
|
|
|
|
nodesAddedHandler(nodeLists[i], summary);
|
|
|
|
|
|
|
|
collapser.addBranches(nodeLists[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( summary.mustReport ) {
|
|
|
|
if ( summary.mustReport ) {
|
|
|
|
localMessager.send(summary);
|
|
|
|
localMessager.send(summary);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
collapser.process();
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Executed only once.
|
|
|
|
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
(function() {
|
|
|
|
var summary = {
|
|
|
|
var summary = {
|
|
|
@ -241,18 +401,24 @@ var nodeListsAddedHandler = function(nodeLists) {
|
|
|
|
// &
|
|
|
|
// &
|
|
|
|
// Looks for inline javascript also in at least one a[href] element.
|
|
|
|
// Looks for inline javascript also in at least one a[href] element.
|
|
|
|
// https://github.com/gorhill/httpswitchboard/issues/131
|
|
|
|
// https://github.com/gorhill/httpswitchboard/issues/131
|
|
|
|
nodesAddedHandler(document.querySelectorAll('script, a[href^="javascript:"], object, embed'), summary);
|
|
|
|
nodesAddedHandler(document.querySelectorAll('a[href^="javascript:"],embed,object,script'), summary);
|
|
|
|
|
|
|
|
|
|
|
|
//console.debug('contentscript-end.js > firstObservationHandler(): found %d script tags in "%s"', Object.keys(summary.scriptSources).length, window.location.href);
|
|
|
|
//console.debug('contentscript-end.js > firstObservationHandler(): found %d script tags in "%s"', Object.keys(summary.scriptSources).length, window.location.href);
|
|
|
|
|
|
|
|
|
|
|
|
localMessager.send(summary);
|
|
|
|
localMessager.send(summary);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
collapser.addNodes(document.querySelectorAll('iframe,img'));
|
|
|
|
|
|
|
|
collapser.process();
|
|
|
|
})();
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
// Observe changes in the DOM
|
|
|
|
// Observe changes in the DOM
|
|
|
|
|
|
|
|
|
|
|
|
// Added node lists will be cumulated here before being processed
|
|
|
|
// Added node lists will be cumulated here before being processed
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
var addedNodeLists = [];
|
|
|
|
var addedNodeLists = [];
|
|
|
|
var addedNodeListsTimer = null;
|
|
|
|
var addedNodeListsTimer = null;
|
|
|
|
|
|
|
|
|
|
|
@ -284,17 +450,37 @@ var treeMutationObservedHandlerAsync = function(mutations) {
|
|
|
|
|
|
|
|
|
|
|
|
// This fixes http://acid3.acidtests.org/
|
|
|
|
// This fixes http://acid3.acidtests.org/
|
|
|
|
if ( document.body ) {
|
|
|
|
if ( document.body ) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// https://github.com/gorhill/httpswitchboard/issues/176
|
|
|
|
// https://github.com/gorhill/httpswitchboard/issues/176
|
|
|
|
var treeObserver = new MutationObserver(treeMutationObservedHandlerAsync);
|
|
|
|
var treeObserver = new MutationObserver(treeMutationObservedHandlerAsync);
|
|
|
|
treeObserver.observe(document.body, {
|
|
|
|
treeObserver.observe(document.body, {
|
|
|
|
childList: true,
|
|
|
|
childList: true,
|
|
|
|
subtree: true
|
|
|
|
subtree: true
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vAPI.shutdown.add(function() {
|
|
|
|
|
|
|
|
if ( addedNodeListsTimer !== null ) {
|
|
|
|
|
|
|
|
clearTimeout(addedNodeListsTimer);
|
|
|
|
|
|
|
|
addedNodeListsTimer = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( treeObserver !== null ) {
|
|
|
|
|
|
|
|
treeObserver.disconnect();
|
|
|
|
|
|
|
|
treeObserver = null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
addedNodeLists = [];
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
})();
|
|
|
|
localMessager.send({ what: 'shutdown?' }, function(response) {
|
|
|
|
|
|
|
|
if ( response === true ) {
|
|
|
|
|
|
|
|
vAPI.shutdown.exec();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|