Browse Source

refactoring of inline script blocking + improving AMO validation

pull/2/head
gorhill 10 years ago
parent
commit
6bc728f8b8
  1. 6
      platform/chromium/vapi-client.js
  2. 4
      platform/chromium/vapi-common.js
  3. 13
      platform/firefox/vapi-background.js
  4. 6
      platform/firefox/vapi-client.js
  5. 6
      platform/firefox/vapi-common.js
  6. 61
      src/css/info.css
  7. 119
      src/info.html
  8. 2
      src/js/assets.js
  9. 4
      src/js/async.js
  10. 4
      src/js/browsercache.js
  11. 46
      src/js/contentscript-end.js
  12. 417
      src/js/info.js
  13. 4
      src/js/logger-ui.js
  14. 4
      src/js/logger.js
  15. 108
      src/js/messaging.js
  16. 7
      src/js/pagestats.js
  17. 2
      src/js/popup.js
  18. 28
      src/js/tab.js
  19. 96
      src/js/traffic.js

6
platform/chromium/vapi-client.js

@ -180,6 +180,12 @@ if ( window !== window.top ) {
/******************************************************************************/ /******************************************************************************/
vAPI.setTimeout = vAPI.setTimeout || function(callback, delay) {
setTimeout(function() { callback(); }, delay);
};
/******************************************************************************/
})(this); })(this);
/******************************************************************************/ /******************************************************************************/

4
platform/chromium/vapi-common.js

@ -92,6 +92,10 @@ vAPI.localStorage = window.localStorage;
/******************************************************************************/ /******************************************************************************/
vAPI.setTimeout = vAPI.setTimeout || window.setTimeout.bind(window);
/******************************************************************************/
})(); })();
/******************************************************************************/ /******************************************************************************/

13
platform/firefox/vapi-background.js

@ -743,7 +743,7 @@ vAPI.tabs.injectScript = function(tabId, details, callback) {
); );
if ( typeof callback === 'function' ) { if ( typeof callback === 'function' ) {
setTimeout(callback, 13);
vAPI.setTimeout(callback, 13);
} }
}; };
@ -1434,7 +1434,7 @@ vAPI.toolbarButton.init = function() {
} }
// Anonymous elements need some time to be reachable // Anonymous elements need some time to be reachable
setTimeout(this.updateBadgeStyle, 250);
vAPI.setTimeout(this.updateBadgeStyle, 250);
}.bind(this.CUIEvents); }.bind(this.CUIEvents);
this.CUIEvents.onCustomizeEnd = updateBadge; this.CUIEvents.onCustomizeEnd = updateBadge;
this.CUIEvents.onWidgetUnderflow = updateBadge; this.CUIEvents.onWidgetUnderflow = updateBadge;
@ -1465,7 +1465,7 @@ vAPI.toolbarButton.init = function() {
this.onCreated = function(button) { this.onCreated = function(button) {
button.setAttribute('badge', ''); button.setAttribute('badge', '');
setTimeout(updateBadge, 250);
vAPI.setTimeout(updateBadge, 250);
}; };
CustomizableUI.addListener(this.CUIEvents); CustomizableUI.addListener(this.CUIEvents);
@ -1531,7 +1531,7 @@ vAPI.toolbarButton.onBeforeCreated = function(doc) {
if ( updateTimer ) { if ( updateTimer ) {
return; return;
} }
updateTimer = setTimeout(resizePopup, 10);
updateTimer = vAPI.setTimeout(resizePopup, 10);
}; };
var resizePopup = function() { var resizePopup = function() {
updateTimer = null; updateTimer = null;
@ -1870,8 +1870,7 @@ vAPI.browserData = {};
/******************************************************************************/ /******************************************************************************/
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsICacheService
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsICache
// https://developer.mozilla.org/en-US/docs/HTTP_Cache
vAPI.browserData.clearCache = function(callback) { vAPI.browserData.clearCache = function(callback) {
// PURGE_DISK_DATA_ONLY:1 // PURGE_DISK_DATA_ONLY:1
@ -1957,7 +1956,7 @@ vAPI.cookies.getAll = function(callback) {
} }
callback(out); callback(out);
}; };
setTimeout(onAsync.bind(this), 0);
vAPI.setTimeout(onAsync.bind(this), 0);
}; };
/******************************************************************************/ /******************************************************************************/

6
platform/firefox/vapi-client.js

@ -200,6 +200,12 @@ if ( window !== window.top ) {
/******************************************************************************/ /******************************************************************************/
vAPI.setTimeout = vAPI.setTimeout || function(callback, delay) {
setTimeout(function() { callback(); }, delay);
};
/******************************************************************************/
})(this); })(this);
/******************************************************************************/ /******************************************************************************/

6
platform/firefox/vapi-common.js

@ -153,6 +153,12 @@ vAPI.localStorage = {
/******************************************************************************/ /******************************************************************************/
vAPI.setTimeout = vAPI.setTimeout || function(callback, delay) {
setTimeout(function() { callback(); }, delay);
};
/******************************************************************************/
})(); })();
/******************************************************************************/ /******************************************************************************/

61
src/css/info.css

@ -1,61 +0,0 @@
select {
max-width: 20em;
}
#stats div div,#lists div div {
padding: 0 1em 0 0;
display: inline-block;
text-align: right;
}
#requestsFilters button {
font-size: 15px;
}
#requestsFilters label {
font-size: 13px;
padding-right: 0.5em;
white-space: nowrap;
}
#requests-log {
background-color: white;
border: 1px inset #eee;
direction: ltr;
font: 11px monospace;
margin: 0;
overflow: scroll;
padding: 0;
width: calc(100% - 1.5em);
}
#requestsTable {
border-collapse: collapse;
}
#requests-log tr {
margin: 0;
border: 0;
padding: 0;
color: #070;
}
#requests-log tr.ro {
color: gray;
}
#requests-log tr:hover {
background-color: #eee;
}
#requests-log tr.blocked-true {
color: #c00;
}
#requests-log tr:first-child {
font-weight: bold;
background-color: #eee;
}
#requests-log tr > td {
padding: 1px 0.75em 1px 0;
white-space: nowrap;
}
#requests-log tr > td:nth-of-type(2) {
text-align: right;
}
.type-main_frame {
font-weight: bold;
}
tr.unused {
display: none;
}

119
src/info.html

@ -1,119 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>µMatrix &mdash; Info</title>
<link rel="stylesheet" type="text/css" href="css/common.css">
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
<link rel="stylesheet" type="text/css" href="css/info.css">
</head>
<body>
<h2 data-i18n="statsPageGenericStats" id="generic-stats"></h2>
<div>
<ul>
<li id="statsPageCookieHeadersFoiled">
<li id="statsPageRefererHeadersFoiled">
<li id="statsPageHyperlinkAuditingFoiled">
<li id="statsPageCookiesRemoved">
<li id="statsPageLocalStoragesCleared">
<li id="statsPageBrowserCacheCleared">
</ul>
</div>
<h2 data-i18n="statsPageDetailedStats" id="detailed-stats"></h2>
<div>
<select id="selectPageUrlsTemplate" style="display: none;">
<option value="" data-i18n="statsPageDetailedAllPages"></option>
<option value="http://behind-the-scene/" data-i18n="statsPageDetailedBehindTheScenePage"></option>
</select>
<select id="selectPageUrls">
</select>
<h3 data-i18n="statsPageOverview"></h3>
<div id="stats">
<div style="white-space:nowrap;">
<div>
<span data-i18n="statsPageRequests"></span><br>
<span data-i18n="statsPageAll"></span><br>
<span data-i18n="statsPagePages"></span><br>
<span data-i18n="statsPageCookies"></span><br>
<span data-i18n="statsPageCSS"></span><br>
<span data-i18n="statsPageImages"></span><br>
<span data-i18n="statsPagePlugins"></span><br>
<span data-i18n="statsPageScripts"></span><br>
<span data-i18n="statsPageXHRs"></span><br>
<span data-i18n="statsPageFrames"></span><br>
<span data-i18n="statsPageOthers"></span><br>
</div>
<div style="color:#c00">
<span data-i18n="statsPageBlocked"></span><br>
<span id="blockedAllCount"></span><br>
<span id="blockedMainFrameCount"></span><br>
<span id="blockedCookieCount"></span><br>
<span id="blockedStylesheetCount"></span><br>
<span id="blockedImageCount"></span><br>
<span id="blockedObjectCount"></span><br>
<span id="blockedScriptCount"></span><br>
<span id="blockedXHRCount"></span><br>
<span id="blockedSubFrameCount"></span><br>
<span id="blockedOtherCount"></span><br>
</div>
<div style="color:#070">
<span data-i18n="statsPageAllowed"></span><br>
<span id="allowedAllCount"></span><br>
<span id="allowedMainFrameCount"></span><br>
<span id="allowedCookieCount"></span><br>
<span id="allowedStylesheetCount"></span><br>
<span id="allowedImageCount"></span><br>
<span id="allowedObjectCount"></span><br>
<span id="allowedScriptCount"></span><br>
<span id="allowedXHRCount"></span><br>
<span id="allowedSubFrameCount"></span><br>
<span id="allowedOtherCount"></span><br>
</div>
</div>
</div>
<h3 data-i18n="statsPageDetailed"></h3>
<div id="requests">
<div><span data-i18n="statsPageLogSizePrompt1"></span> <input id="max-logged-requests" type="text" value="50" size="3"> <span data-i18n="statsPageLogSizePrompt2"></span>
<button class="whatisthis"></button>
<p class="whatisthis-expandable para" data-i18n="statsPageLogSizeHelp"></p>
<p id="requestsFilters">
<button id="refreshRequests" class="fa" type="button">&#xf021;</button>
<button id="clearRequests" class="fa" type="button">&#xf12d;</button>
<label><input id="show-blocked" type="checkbox" checked value="1"><span style="color:#c00" data-i18n="statsPageBlocked"></span></label>
<label><input id="show-allowed" type="checkbox" checked value="1"><span style="color:#070" data-i18n="statsPageAllowed"></span></label>
<label><input id="show-doc" type="checkbox" checked value="1"><span data-i18n="statsPagePages"></span></label>
<label><input id="show-cookie" type="checkbox" checked value="1"><span data-i18n="statsPageCookies"></span></label>
<label><input id="show-css" type="checkbox" checked value="1"><span data-i18n="statsPageCSS"></span></label>
<label><input id="show-image" type="checkbox" checked value="1"><span data-i18n="statsPageImages"></span></label>
<label><input id="show-plugin" type="checkbox" checked value="1"><span data-i18n="statsPagePlugins"></span></label>
<label><input id="show-script" type="checkbox" checked value="1"><span data-i18n="statsPageScripts"></span></label>
<label><input id="show-xhr" type="checkbox" checked value="1"><span data-i18n="statsPageXHRs"></span></label>
<label><input id="show-frame" type="checkbox" checked value="1"><span data-i18n="statsPageFrames"></span></label>
<label><input id="show-other" type="checkbox" checked value="1"><span data-i18n="statsPageOthers"></span></label>
</p>
</div>
<div id="requests-log" style="overflow-y:hidden">
<table id="requestsTable">
<tr class="ro"><td>when<td>what<td><td>where</tr>
<tr class="ro" id="requestRowTemplate"><td><td><td><a href="" style="display:none">&lt;a&gt;</a><td></tr>
</table>
</div>
</div>
</div> <!-- end of detailed stats -->
<script src="js/vapi-common.js"></script>
<script src="js/vapi-client.js"></script>
<script src="js/udom.js"></script>
<script src="js/i18n.js"></script>
<script src="js/dashboard-common.js"></script>
<script src="js/info.js"></script>
</body>
</html>

2
src/js/assets.js

@ -1353,7 +1353,7 @@ var scheduleUpdateDaemon = function() {
if ( updateDaemonTimer !== null ) { if ( updateDaemonTimer !== null ) {
clearTimeout(updateDaemonTimer); clearTimeout(updateDaemonTimer);
} }
updateDaemonTimer = setTimeout(
updateDaemonTimer = vAPI.setTimeout(
updateDaemon, updateDaemon,
exports.manualUpdate ? manualUpdateDaemonTimerPeriod : autoUpdateDaemonTimerPeriod exports.manualUpdate ? manualUpdateDaemonTimerPeriod : autoUpdateDaemonTimerPeriod
); );

4
src/js/async.js

@ -71,7 +71,7 @@ AsyncJobManager.prototype.restartTimer = function() {
if ( when < this.timerWhen ) { if ( when < this.timerWhen ) {
clearTimeout(this.timerId); clearTimeout(this.timerId);
this.timerWhen = when; this.timerWhen = when;
this.timerId = setTimeout(processJobs, Math.max(when - Date.now(), 10));
this.timerId = vAPI.setTimeout(processJobs, Math.max(when - Date.now(), 10));
} }
}; };
@ -169,6 +169,6 @@ return asyncJobManager;
if ( vAPI.isBehindTheSceneTabId(tabId) ) { if ( vAPI.isBehindTheSceneTabId(tabId) ) {
return; return;
} }
tabIdToTimer[tabId] = setTimeout(updateBadge.bind(this, tabId), 500);
tabIdToTimer[tabId] = vAPI.setTimeout(updateBadge.bind(this, tabId), 500);
}; };
})(); })();

4
src/js/browsercache.js

@ -32,7 +32,7 @@
// Browser data jobs // Browser data jobs
var clearCache = function() { var clearCache = function() {
setTimeout(clearCache, 15 * 60 * 1000);
vAPI.setTimeout(clearCache, 15 * 60 * 1000);
var µm = µMatrix; var µm = µMatrix;
if ( !µm.userSettings.clearBrowserCache ) { if ( !µm.userSettings.clearBrowserCache ) {
@ -55,7 +55,7 @@ var clearCache = function() {
//console.debug('clearBrowserCacheCallback()> vAPI.browserData.clearCache() called'); //console.debug('clearBrowserCacheCallback()> vAPI.browserData.clearCache() called');
}; };
setTimeout(clearCache, 15 * 60 * 1000);
vAPI.setTimeout(clearCache, 15 * 60 * 1000);
/******************************************************************************/ /******************************************************************************/

46
src/js/contentscript-end.js

@ -242,7 +242,7 @@ var collapser = (function() {
clearTimeout(timer); clearTimeout(timer);
send(); send();
} else if ( timer === null ) { } else if ( timer === null ) {
timer = setTimeout(send, delay || 50);
timer = vAPI.setTimeout(send, delay || 50);
} }
}; };
@ -360,9 +360,9 @@ var collapser = (function() {
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
var nodesAddedHandler = function(nodeList, summary) {
var hasInlineScript = function(nodeList, summary) {
var i = 0; var i = 0;
var node, src, text;
var node, text;
while ( node = nodeList.item(i++) ) { while ( node = nodeList.item(i++) ) {
if ( node.nodeType !== 1 ) { if ( node.nodeType !== 1 ) {
continue; continue;
@ -371,35 +371,29 @@ var nodesAddedHandler = function(nodeList, summary) {
continue; continue;
} }
switch ( node.localName ) {
case 'script':
if ( node.localName === 'script' ) {
// https://github.com/gorhill/httpswitchboard/issues/252 // https://github.com/gorhill/httpswitchboard/issues/252
// Do not count uMatrix's own script tags, they are not required // Do not count uMatrix's own script tags, they are not required
// to "unbreak" a web page // to "unbreak" a web page
if ( typeof node.id === 'string' && node.id.lastIndexOf('uMatrix-', 0) === 0 ) { if ( typeof node.id === 'string' && node.id.lastIndexOf('uMatrix-', 0) === 0 ) {
break;
continue;
} }
text = node.textContent.trim(); text = node.textContent.trim();
if ( text !== '' ) {
summary.scriptSources['{inline_script}'] = true;
summary.mustReport = true;
}
src = (node.src || '').trim();
if ( src !== '' ) {
summary.scriptSources[src] = true;
summary.mustReport = true;
if ( text === '' ) {
continue;
} }
summary.inlineScript = true;
break; break;
}
case 'a':
if ( node.href.lastIndexOf('javascript', 0) === 0 ) {
summary.scriptSources['{inline_script}'] = true;
summary.mustReport = true;
}
if ( node.localName === 'a' && node.href.lastIndexOf('javascript', 0) === 0 ) {
summary.inlineScript = true;
break; break;
} }
} }
if ( summary.inlineScript ) {
summary.mustReport = true;
}
}; };
/******************************************************************************/ /******************************************************************************/
@ -412,11 +406,13 @@ var nodeListsAddedHandler = function(nodeLists) {
var summary = { var summary = {
what: 'contentScriptSummary', what: 'contentScriptSummary',
locationURL: window.location.href, locationURL: window.location.href,
scriptSources: {}, // to avoid duplicates
inlineScript: false,
mustReport: false mustReport: false
}; };
while ( i-- ) { while ( i-- ) {
nodesAddedHandler(nodeLists[i], summary);
if ( summary.inlineScript === false ) {
hasInlineScript(nodeLists[i], summary);
}
collapser.addBranches(nodeLists[i]); collapser.addBranches(nodeLists[i]);
} }
if ( summary.mustReport ) { if ( summary.mustReport ) {
@ -434,14 +430,14 @@ var nodeListsAddedHandler = function(nodeLists) {
var summary = { var summary = {
what: 'contentScriptSummary', what: 'contentScriptSummary',
locationURL: window.location.href, locationURL: window.location.href,
scriptSources: {}, // to avoid duplicates
inlineScript: false,
mustReport: true mustReport: true
}; };
// https://github.com/gorhill/httpswitchboard/issues/25 // https://github.com/gorhill/httpswitchboard/issues/25
// & // &
// 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('a[href^="javascript:"],script'), summary);
hasInlineScript(document.querySelectorAll('a[href^="javascript:"],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);
@ -484,7 +480,7 @@ var nodeListsAddedHandler = function(nodeLists) {
// nodes too often and the delay of many nodes less often. There is nothing // nodes too often and the delay of many nodes less often. There is nothing
// time critical here. // time critical here.
if ( addedNodeListsTimer === null ) { if ( addedNodeListsTimer === null ) {
addedNodeListsTimer = setTimeout(treeMutationObservedHandler, 250);
addedNodeListsTimer = vAPI.setTimeout(treeMutationObservedHandler, 250);
} }
}; };

417
src/js/info.js

@ -1,417 +0,0 @@
/*******************************************************************************
µMatrix - a Chromium browser extension to black/white list requests.
Copyright (C) 2014 Raymond Hill
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
*/
/* global vAPI, uDom */
/******************************************************************************/
(function() {
'use strict';
/******************************************************************************/
var messager = vAPI.messaging.channel('info.js');
var targetTabId = null;
var maxRequests = 500;
var cachedUserSettings = {};
/******************************************************************************/
// Get a list of latest net requests
function updateRequestData(callback) {
var onResponseReceived = function(r) {
var requests = [];
for ( var pageURL in r ) {
if ( r.hasOwnProperty(pageURL) === false ) {
continue;
}
requests = requests.concat(r[pageURL]);
}
requests = requests
.sort(function(a,b){return b.when-a.when;})
.slice(0, maxRequests);
callback(requests);
};
var request = {
what: 'getRequestLogs',
tabId: targetTabId
};
messager.send(request, onResponseReceived);
}
/******************************************************************************/
function clearRequestData() {
var request = {
what: 'clearRequestLogs',
tabId: targetTabId
};
messager.send(request);
}
/******************************************************************************/
function renderNumber(value) {
if ( isNaN(value) ) {
return '0';
}
return value.toLocaleString();
}
/******************************************************************************/
function renderNumbers(set) {
var keys = Object.keys(set);
var i = keys.length;
var key;
while ( i-- ) {
key = keys[i];
uDom(key).text(renderNumber(set[key]));
}
}
/******************************************************************************/
var renderLocalized = function(id, map) {
var uElem = uDom('#' + id);
var msg = vAPI.i18n(id);
for ( var k in map ) {
if ( map.hasOwnProperty(k) === false ) {
continue;
}
msg = msg.replace('{{' + k + '}}', map[k]);
}
uElem.html(msg);
};
/******************************************************************************/
function renderPageUrls() {
var onResponseReceived = function(result) {
var i, n;
var select = uDom('#selectPageUrls');
// Remove whatever was put there in a previous call
uDom('#selectPageUrls > option').remove();
var builtinOptions = uDom('#selectPageUrlsTemplate > option');
n = builtinOptions.length;
for ( i = 0; i < n; i++ ) {
option = builtinOptions.at(i).clone();
if ( option.val() === targetTabId ) {
option.attr('selected', true);
}
select.append(option);
}
var entries = result.pageURLs.sort();
var entry, pageURL, option;
n = entries.length;
for ( i = 0; i < n; i++ ) {
entry = entries[i];
// Behind-the-scene entry is always present, no need to recreate it
if ( entry.pageURL === result.behindTheSceneURL ) {
continue;
}
option = uDom('<option>');
option.val(entry.tabId);
option.text(entry.pageURL);
if ( entry.pageURL === targetTabId ) {
option.attr('selected', true);
}
select.append(option);
}
// Deselect whatever is currently selected
//uDom('#selectPageUrls > option:selected').prop('selected', false);
// Select whatever needs to be selected
//uDom('#selectPageUrls > option[value="'+targetUrl+'"]').prop('selected', true);
};
messager.send({ what: 'getPageURLs' }, onResponseReceived);
}
/******************************************************************************/
function renderStats() {
var onResponseReceived = function(r) {
if ( !r.pageNetStats ) {
targetTabId = null;
}
var requestStats = targetTabId ? r.pageNetStats : r.globalNetStats;
var blockedStats = requestStats.blocked;
var allowedStats = requestStats.allowed;
renderLocalized('statsPageCookieHeadersFoiled', { count: renderNumber(r.cookieHeaderFoiledCounter) });
renderLocalized('statsPageRefererHeadersFoiled', { count: renderNumber(r.refererHeaderFoiledCounter) });
renderLocalized('statsPageHyperlinkAuditingFoiled', { count: renderNumber(r.hyperlinkAuditingFoiledCounter) });
renderLocalized('statsPageCookiesRemoved', { count: renderNumber(r.cookieRemovedCounter) });
renderLocalized('statsPageLocalStoragesCleared', { count: renderNumber(r.localStorageRemovedCounter) });
renderLocalized('statsPageBrowserCacheCleared', { count: renderNumber(r.browserCacheClearedCounter) });
renderNumbers({
'#blockedAllCount': requestStats.blocked.all,
'#blockedMainFrameCount': blockedStats.doc,
'#blockedCookieCount': blockedStats.cookie,
'#blockedStylesheetCount': blockedStats.css,
'#blockedImageCount': blockedStats.image,
'#blockedObjectCount': blockedStats.plugin,
'#blockedScriptCount': blockedStats.script,
'#blockedXHRCount': blockedStats.xhr,
'#blockedSubFrameCount': blockedStats.frame,
'#blockedOtherCount': blockedStats.other,
'#allowedAllCount': allowedStats.all,
'#allowedMainFrameCount': allowedStats.doc,
'#allowedCookieCount': allowedStats.cookie,
'#allowedStylesheetCount': allowedStats.css,
'#allowedImageCount': allowedStats.image,
'#allowedObjectCount': allowedStats.plugin,
'#allowedScriptCount': allowedStats.script,
'#allowedXHRCount': allowedStats.xhr,
'#allowedSubFrameCount': allowedStats.frame,
'#allowedOtherCount': allowedStats.other
});
// because some i18n messages may contain links
uDom('a').attr('target', '_blank');
};
messager.send({
what: 'getStats',
tabId: targetTabId
},
onResponseReceived
);
}
/******************************************************************************/
function renderRequestRow(row, request) {
row.attr('id', '');
row.css('display', '');
row.removeClass();
if ( request.block !== false ) {
row.addClass('blocked-true');
} else {
row.addClass('blocked-false');
}
row.addClass('type-' + request.type);
var cells = row.descendants('td');
// when
var when = new Date(request.when);
cells.at(0).text(when.toLocaleTimeString());
// request type
cells.at(1).text(request.type);
// Well I got back full control since not using Tempo.js, I can now
// generate smarter hyperlinks, that is, not hyperlinking fake
// request URLs, which are recognizable with their curly braces inside.
var a = cells.at(2).descendants('a');
if ( request.url.search('{') < 0 ) {
a.attr('href', request.url);
a.css('display', '');
} else {
a.css('display', 'none');
}
// request URL
cells.at(3).text(request.url);
}
/******************************************************************************/
var renderRequests = function(requests) {
var table = uDom('#requestsTable');
var i, row;
var rowTemplate = table.descendants('#requestRowTemplate').first();
// Reuse whatever rows is already in there.
var rows = table.descendants('tr:not(.ro)');
var n = Math.min(requests.length, rows.length);
for ( i = 0; i < n; i++ ) {
renderRequestRow(rows.at(i), requests[i]);
}
// Unhide reused rows
rows.subset(0, n).removeClass('unused');
// Hide extra rows
rows.subset(n).addClass('unused');
// Create new rows to receive what is left
n = requests.length;
for ( ; i < n; i++ ) {
row = rowTemplate.clone();
renderRequestRow(row, requests[i]);
row.insertBefore(rowTemplate);
}
syncWithFilters();
};
/******************************************************************************/
var updateRequests = function() {
updateRequestData(renderRequests);
};
/******************************************************************************/
var clearRequests = function() {
clearRequestData();
renderRequests([]);
};
/******************************************************************************/
function changeUserSettings(name, value) {
cachedUserSettings[name] = value;
messager.send({
what: 'userSettings',
name: name,
value: value
});
}
/******************************************************************************/
function changeValueHandler(elem, setting, min, max) {
var oldVal = cachedUserSettings[setting];
var newVal = Math.round(parseFloat(elem.val()));
if ( typeof newVal !== 'number' ) {
newVal = oldVal;
} else {
newVal = Math.max(newVal, min);
newVal = Math.min(newVal, max);
}
elem.val(newVal);
if ( newVal !== oldVal ) {
changeUserSettings(setting, newVal);
}
}
/******************************************************************************/
function changeFilterHandler() {
// Save new state of filters in user settings
// Initialize request filters as per user settings:
// https://github.com/gorhill/httpswitchboard/issues/49
var statsFilters = cachedUserSettings.statsFilters;
uDom('input[id^="show-"][type="checkbox"]').forEach(function(input) {
statsFilters[input.attr('id')] = !!input.prop('checked');
});
changeUserSettings('statsFilters', statsFilters);
syncWithFilters();
}
/******************************************************************************/
// Synchronize list of net requests with filter states
function syncWithFilters() {
var blocked = ['blocked','allowed'];
var type = ['doc','cookie','css','image','plugin','script','xhr','frame','other'];
var i = blocked.length;
var j;
var display, selector;
while ( i-- ) {
j = type.length;
while ( j-- ) {
display = uDom('#show-' + blocked[i]).prop('checked') &&
uDom('#show-' + type[j]).prop('checked') ? '' : 'none';
selector = '.blocked-' + (blocked[i] === 'blocked') + '.type-' + type[j];
uDom(selector).css('display', display);
}
}
}
/******************************************************************************/
var renderTransientTimer;
function renderTransientData(internal) {
// This is in case this function is not called from timeout event
if ( internal && renderTransientTimer ) {
clearTimeout(renderTransientTimer);
}
renderPageUrls();
renderStats();
renderTransientTimer = setTimeout(renderTransientData, 10000); // every 10s
}
/******************************************************************************/
function targetTabIdChangeHandler() {
targetTabId = this[this.selectedIndex].value;
renderStats();
updateRequests();
}
/******************************************************************************/
function prepareToDie() {
changeValueHandler(uDom('#max-logged-requests'), 'maxLoggedRequests', 0, 999);
}
/******************************************************************************/
var installEventHandlers = function() {
uDom('#refreshRequests').on('click', updateRequests);
uDom('#clearRequests').on('click', clearRequests);
uDom('input[id^="show-"][type="checkbox"]').on('change', changeFilterHandler);
uDom('#selectPageUrls').on('change', targetTabIdChangeHandler);
uDom('#max-logged-requests').on('change', function(){ changeValueHandler(uDom(this), 'maxLoggedRequests', 0, 999); });
// https://github.com/gorhill/httpswitchboard/issues/197
window.addEventListener('beforeunload', prepareToDie);
};
/******************************************************************************/
uDom.onLoad(function(){
// Initialize request filters as per user settings:
// https://github.com/gorhill/httpswitchboard/issues/49
var onResponseReceived = function(userSettings) {
// cache a copy
cachedUserSettings = userSettings;
// init ui as per user settings
uDom('#max-logged-requests').val(userSettings.maxLoggedRequests);
var statsFilters = userSettings.statsFilters;
uDom('input[id^="show-"][type="checkbox"]').forEach(function(input) {
var filter = statsFilters[input.attr('id')];
input.prop('checked', filter === undefined || filter === true);
});
installEventHandlers();
};
messager.send({ what: 'getUserSettings' }, onResponseReceived);
renderTransientData(true);
updateRequests();
});
/******************************************************************************/
})();

4
src/js/logger-ui.js

@ -345,7 +345,7 @@ var onLogBufferRead = function(response) {
tbody.querySelector('tr') === null tbody.querySelector('tr') === null
); );
setTimeout(readLogBuffer, 1200);
vAPI.setTimeout(readLogBuffer, 1200);
}; };
/******************************************************************************/ /******************************************************************************/
@ -490,7 +490,7 @@ var rowFilterer = (function() {
if ( timer !== null ) { if ( timer !== null ) {
clearTimeout(timer); clearTimeout(timer);
} }
timer = setTimeout(commit, 750);
timer = vAPI.setTimeout(commit, 750);
}; };
})(); })();

4
src/js/logger.js

@ -164,7 +164,7 @@ var janitor = function() {
logBuffer = logBuffer.dispose(); logBuffer = logBuffer.dispose();
} }
if ( logBuffer !== null ) { if ( logBuffer !== null ) {
setTimeout(janitor, logBufferObsoleteAfter);
vAPI.setTimeout(janitor, logBufferObsoleteAfter);
} }
}; };
@ -181,7 +181,7 @@ var writeOne = function() {
var readAll = function() { var readAll = function() {
if ( logBuffer === null ) { if ( logBuffer === null ) {
logBuffer = new LogBuffer(); logBuffer = new LogBuffer();
setTimeout(janitor, logBufferObsoleteAfter);
vAPI.setTimeout(janitor, logBufferObsoleteAfter);
} }
return logBuffer.readAll(); return logBuffer.readAll();
}; };

108
src/js/messaging.js

@ -393,37 +393,28 @@ var contentScriptSummaryHandler = function(tabId, details) {
if ( !details || !details.locationURL ) { if ( !details || !details.locationURL ) {
return; return;
} }
if ( details.inlineScript !== true ) {
return;
}
// scripts
// https://github.com/gorhill/httpswitchboard/issues/25
var pageStore = µm.pageStoreFromTabId(tabId); var pageStore = µm.pageStoreFromTabId(tabId);
var pageURL = pageStore.pageUrl;
if ( pageStore === null ) {
return;
}
var pageHostname = pageStore.pageHostname; var pageHostname = pageStore.pageHostname;
var µmuri = µm.URI.set(details.locationURL); var µmuri = µm.URI.set(details.locationURL);
var frameURL = µmuri.normalizedURI(); var frameURL = µmuri.normalizedURI();
var frameHostname = µmuri.hostname; var frameHostname = µmuri.hostname;
var urls, url, r;
// https://github.com/gorhill/httpswitchboard/issues/333 // https://github.com/gorhill/httpswitchboard/issues/333
// Look-up here whether inline scripting is blocked for the frame. // Look-up here whether inline scripting is blocked for the frame.
var inlineScriptBlocked = µm.mustBlock(pageHostname, frameHostname, 'script'); var inlineScriptBlocked = µm.mustBlock(pageHostname, frameHostname, 'script');
// scripts
// https://github.com/gorhill/httpswitchboard/issues/25
if ( pageStore && inlineScriptBlocked ) {
urls = details.scriptSources;
for ( url in urls ) {
if ( !urls.hasOwnProperty(url) ) {
continue;
}
if ( url === '{inline_script}' ) {
url = frameURL + '{inline_script}';
}
r = µm.filterRequest(pageURL, 'script', url);
pageStore.recordRequest('script', url, r !== false);
µm.logger.writeOne(tabId, 'net', pageHostname, url, 'script', r);
}
}
// https://github.com/gorhill/httpswitchboard/issues/181
µm.onPageLoadCompleted(tabId);
var url = frameURL + '{inline_script}';
pageStore.recordRequest('script', url, inlineScriptBlocked);
µm.logger.writeOne(tabId, 'net', pageHostname, url, 'script', inlineScriptBlocked);
}; };
/******************************************************************************/ /******************************************************************************/
@ -794,79 +785,6 @@ vAPI.messaging.listen('hosts-files.js', onMessage);
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
// info.js
(function() {
var µm = µMatrix;
/******************************************************************************/
var getTabURLs = function() {
var pageURLs = [];
var pageStores = µm.pageStores;
for ( var tabId in pageStores ) {
if ( pageStores.hasOwnProperty(tabId) === false ) {
continue;
}
pageURLs.push({
tabId: tabId,
pageURL: pageStores[tabId].pageUrl
});
}
return {
pageURLs: pageURLs,
behindTheSceneURL: µm.behindTheSceneURL
};
};
/******************************************************************************/
var onMessage = function(request, sender, callback) {
// Async
switch ( request.what ) {
default:
break;
}
// Sync
var response;
switch ( request.what ) {
case 'getPageURLs':
response = getTabURLs();
break;
case 'getStats':
var pageStore = µm.pageStores[request.tabId];
response = {
globalNetStats: µm.requestStats,
pageNetStats: pageStore ? pageStore.requestStats : null,
cookieHeaderFoiledCounter: µm.cookieHeaderFoiledCounter,
refererHeaderFoiledCounter: µm.refererHeaderFoiledCounter,
hyperlinkAuditingFoiledCounter: µm.hyperlinkAuditingFoiledCounter,
cookieRemovedCounter: µm.cookieRemovedCounter,
localStorageRemovedCounter: µm.localStorageRemovedCounter,
browserCacheClearedCounter: µm.browserCacheClearedCounter
};
break;
default:
return vAPI.messaging.UNHANDLED;
}
callback(response);
};
vAPI.messaging.listen('info.js', onMessage);
})();
/******************************************************************************/
/******************************************************************************/
// about.js // about.js
(function() { (function() {

7
src/js/pagestats.js

@ -334,8 +334,6 @@ PageStore.prototype.init = function(tabContext) {
this.pageUrl = tabContext.normalURL; this.pageUrl = tabContext.normalURL;
this.pageHostname = tabContext.rootHostname; this.pageHostname = tabContext.rootHostname;
this.pageDomain = tabContext.rootDomain; this.pageDomain = tabContext.rootDomain;
this.pageScriptBlocked = false;
this.thirdpartyScript = false;
this.requests = µm.PageRequestStats.factory(); this.requests = µm.PageRequestStats.factory();
this.domains = {}; this.domains = {};
this.allHostnamesString = ' '; this.allHostnamesString = ' ';
@ -393,11 +391,6 @@ PageStore.prototype.recordRequest = function(type, url, block) {
var hostname = µm.URI.hostnameFromURI(url); var hostname = µm.URI.hostnameFromURI(url);
// https://github.com/gorhill/httpswitchboard/issues/181
if ( type === 'script' && hostname !== this.pageHostname ) {
this.thirdpartyScript = true;
}
this.distinctRequestCount++; this.distinctRequestCount++;
this.mtxCountModifiedTime = Date.now(); this.mtxCountModifiedTime = Date.now();

2
src/js/popup.js

@ -1250,7 +1250,7 @@ var matrixSnapshotPoller = (function() {
if ( timer !== null ) { if ( timer !== null ) {
return; return;
} }
timer = setTimeout(poll, 1414);
timer = vAPI.setTimeout(poll, 1414);
}; };
var unpollAsync = function() { var unpollAsync = function() {

28
src/js/tab.js

@ -181,7 +181,7 @@ housekeep itself.
TabContext.prototype.onTab = function(tab) { TabContext.prototype.onTab = function(tab) {
if ( tab ) { if ( tab ) {
this.timer = setTimeout(this.onTimerCallback, gcPeriod);
this.timer = vAPI.setTimeout(this.onTimerCallback, gcPeriod);
} else { } else {
this.destroy(); this.destroy();
} }
@ -204,7 +204,7 @@ housekeep itself.
} }
this.onTabCallback = this.onTab.bind(this); this.onTabCallback = this.onTab.bind(this);
this.onTimerCallback = this.onTimer.bind(this); this.onTimerCallback = this.onTimer.bind(this);
this.timer = setTimeout(this.onTimerCallback, gcPeriod);
this.timer = vAPI.setTimeout(this.onTimerCallback, gcPeriod);
}; };
// Update just force all properties to be updated to match the most current // Update just force all properties to be updated to match the most current
@ -531,7 +531,7 @@ vAPI.tabs.registerListeners();
var pageURL = pageStore.pageUrl; var pageURL = pageStore.pageUrl;
pageStoreCrypt[pageURL] = pageStore; pageStoreCrypt[pageURL] = pageStore;
pageStore.incinerationTimer = setTimeout(
pageStore.incinerationTimer = vAPI.setTimeout(
this.incineratePageStore.bind(this, tabId, pageURL), this.incineratePageStore.bind(this, tabId, pageURL),
4 * 60 * 1000 4 * 60 * 1000
); );
@ -616,24 +616,6 @@ vAPI.tabs.registerListeners();
/******************************************************************************/ /******************************************************************************/
µm.onPageLoadCompleted = function(tabId) {
var pageStore = this.pageStoreFromTabId(tabId);
if ( pageStore === null ) {
return;
}
// https://github.com/gorhill/httpswitchboard/issues/181
if ( pageStore.thirdpartyScript ) {
pageStore.recordRequest(
'script',
pageStore.pageUrl + '{3rd-party_scripts}',
pageStore.pageScriptBlocked
);
}
};
/******************************************************************************/
µm.forceReload = function(tabId) { µm.forceReload = function(tabId) {
vAPI.tabs.reload(tabId, { bypassCache: true }); vAPI.tabs.reload(tabId, { bypassCache: true });
}; };
@ -672,10 +654,10 @@ vAPI.tabs.registerListeners();
} }
cleanupSampleAt = n; cleanupSampleAt = n;
setTimeout(cleanup, cleanupPeriod);
vAPI.setTimeout(cleanup, cleanupPeriod);
}; };
setTimeout(cleanup, cleanupPeriod);
vAPI.setTimeout(cleanup, cleanupPeriod);
})(); })();
/******************************************************************************/ /******************************************************************************/

96
src/js/traffic.js

@ -256,113 +256,41 @@ var onHeadersReceived = function(details) {
// console.debug('onHeadersReceived()> "%s": %o', details.url, details); // console.debug('onHeadersReceived()> "%s": %o', details.url, details);
// Ignore schemes other than 'http...' // Ignore schemes other than 'http...'
if ( details.url.lastIndexOf('http', 0) !== 0 ) {
var requestURL = details.url;
if ( requestURL.lastIndexOf('http', 0) !== 0 ) {
return; return;
} }
var requestType = requestTypeNormalizer[details.type] || 'other';
if ( requestType === 'frame' ) {
return onSubDocHeadersReceived(details);
}
if ( requestType === 'doc' ) {
return onMainDocHeadersReceived(details);
}
};
/******************************************************************************/
var onMainDocHeadersReceived = function(details) {
var µm = µMatrix; var µm = µMatrix;
var tabId = details.tabId; var tabId = details.tabId;
var requestURL = details.url;
var requestType = requestTypeNormalizer[details.type] || 'other';
// https://github.com/gorhill/uMatrix/issues/145 // https://github.com/gorhill/uMatrix/issues/145
// Check if the main_frame is a download // Check if the main_frame is a download
if ( headerValue(details.responseHeaders, 'content-type').lastIndexOf('application/x-', 0) === 0 ) {
µm.tabContextManager.unpush(tabId, requestURL);
} else {
µm.tabContextManager.push(tabId, requestURL);
}
var tabContext = µm.tabContextManager.lookup(tabId);
if ( tabContext === null ) {
return;
}
// console.debug('onMainDocHeadersReceived()> "%s": %o', requestURL, details);
var rootHostname = tabContext.rootHostname;
var blockScript = µm.mustBlock(rootHostname, rootHostname, 'script');
// https://github.com/gorhill/httpswitchboard/issues/181
var pageStore = µm.pageStoreFromTabId(tabId);
if ( pageStore ) {
pageStore.pageScriptBlocked = blockScript;
}
if ( !blockScript ) {
return;
if ( requestType === 'doc' ) {
if ( headerValue(details.responseHeaders, 'content-type').lastIndexOf('application/x-', 0) === 0 ) {
µm.tabContextManager.unpush(tabId, requestURL);
} else {
µm.tabContextManager.push(tabId, requestURL);
}
} }
µm.logger.writeOne(tabId, 'net', rootHostname, requestURL + '{inline_script}', 'script', true);
// If javascript not allowed, say so through a `Content-Security-Policy` directive.
details.responseHeaders.push({
'name': 'Content-Security-Policy',
'value': "script-src 'none'"
});
return { responseHeaders: details.responseHeaders };
};
/******************************************************************************/
var onSubDocHeadersReceived = function(details) {
var µm = µMatrix;
var tabId = details.tabId;
// console.debug('onSubDocHeadersReceived()> "%s": %o', details.url, details);
// Do not ignore traffic outside tabs.
// https://github.com/gorhill/httpswitchboard/issues/91#issuecomment-37180275
var tabContext = µm.tabContextManager.lookup(tabId); var tabContext = µm.tabContextManager.lookup(tabId);
if ( tabContext === null ) { if ( tabContext === null ) {
return; return;
} }
// Evaluate
if ( µm.mustAllow(tabContext.rootHostname, details.hostname, 'script') ) { if ( µm.mustAllow(tabContext.rootHostname, details.hostname, 'script') ) {
return; return;
} }
// If javascript not allowed, say so through a `Content-Security-Policy` // If javascript not allowed, say so through a `Content-Security-Policy`
// directive.
// For inline javascript within iframes, we need to sandbox.
// https://github.com/gorhill/httpswitchboard/issues/73
// Now because sandbox cancels all permissions, this means
// not just javascript is disabled. To avoid negative side
// effects, I allow some other permissions, but...
// https://github.com/gorhill/uMatrix/issues/27
// Need to add `allow-popups` to prevent completely breaking links on
// some sites old style sites.
// TODO: Reuse CSP `sandbox` directive if it's already in the
// headers (strip out `allow-scripts` if present),
// and find out if the `sandbox` in the header interfere with a
// `sandbox` attribute which might be present on the iframe.
// console.debug('onSubDocHeadersReceived()> FRAME CSP "%s": %o, scope="%s"', details.url, details, pageURL);
µm.logger.writeOne(tabId, 'net', tabContext.rootHostname, details.url + '{inline_script}', 'script', true);
// If javascript not allowed, say so through a `Content-Security-Policy` directive.
// directive. We block only inline-script tags, all the external javascript
// will be blocked by our request handler.
details.responseHeaders.push({ details.responseHeaders.push({
'name': 'Content-Security-Policy', 'name': 'Content-Security-Policy',
'value': "script-src 'none'"
'value': "script-src 'unsafe-eval' *"
}); });
return { responseHeaders: details.responseHeaders }; return { responseHeaders: details.responseHeaders };
}; };

Loading…
Cancel
Save