Raymond Hill 6 years ago
parent
commit
2256552899
No known key found for this signature in database GPG Key ID: 25E1490B761470C2
  1. 3
      src/css/common.css
  2. 255
      src/css/logger-ui.css
  3. 88
      src/css/popup.css
  4. 84
      src/css/scope-selector.css
  5. 10
      src/js/assets.js
  6. 10
      src/js/browsercache.js
  7. 47
      src/js/cookies.js
  8. 631
      src/js/logger-ui.js
  9. 40
      src/js/logger.js
  10. 31
      src/js/matrix.js
  11. 156
      src/js/messaging.js
  12. 107
      src/js/popup.js
  13. 152
      src/js/scope-selector.js
  14. 150
      src/js/traffic.js
  15. 33
      src/logger-ui.html
  16. 2
      src/popup.html

3
src/css/common.css

@ -31,6 +31,9 @@
display: inline-block;
}
body {
font-size: 14px;
}
body[dir="ltr"] {
direction: ltr;
}

255
src/css/logger-ui.css

@ -92,7 +92,7 @@ input:focus {
width: 4.6em;
}
#content table > colgroup > col:nth-of-type(2) {
width: 2.2em;
width: 30%;
}
#content table > colgroup > col:nth-of-type(3) {
width: 2.2em;
@ -101,7 +101,7 @@ input:focus {
width: 5.4em;
}
#content table > colgroup > col:nth-of-type(5) {
width: calc(100% - 14.4em);
width: calc(100% - 4.6em - 30% - 2.2em - 5.4em);
}
#content table tr {
background-color: #fafafa;
@ -151,13 +151,8 @@ body.compactView #content tr:not(.vExpanded) td {
text-align: right;
white-space: nowrap;
}
#content table tr td:nth-of-type(2) {
text-align: center;
white-space: nowrap;
}
#content table tr.tab_bts > td:nth-of-type(2):before {
content: '\f070';
font: 1em FontAwesome;
#content table tr td:nth-of-type(2):not([colspan]) {
text-align: right;
}
#content table tr.tab:not(.canMtx) {
opacity: 0.3;
@ -165,17 +160,8 @@ body.compactView #content tr:not(.vExpanded) td {
#content table tr.tab:not(.canMtx):hover {
opacity: 0.7;
}
#content table tr.tab:not(.canMtx) > td:nth-of-type(2):before {
content: '\f00d';
font: 1em FontAwesome;
}
body:not(.popupOn) #content table tr.canMtx td:nth-of-type(2) {
cursor: zoom-in;
}
body:not(.popupOn) #content table tr.canMtx td:nth-of-type(2):hover {
background: #ccc;
}
#content table tr.cat_net td:nth-of-type(3) {
cursor: pointer;
font: 12px monospace;
text-align: center;
white-space: nowrap;
@ -189,45 +175,232 @@ body:not(.popupOn) #content table tr.canMtx td:nth-of-type(2):hover {
opacity: 1;
}
#popupContainer {
background: white;
border: 1px solid gray;
display: none;
overflow: hidden;
.modalDialog {
align-items: center;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
height: 100vh;
justify-content: center;
left: 0;
position: fixed;
right: 1em;
top: 0;
z-index: 200;
width: 100vw;
z-index: 1000;
}
body.popupOn #popupContainer {
display: block;
.modalDialog > .dialog {
background-color: white;
font: 15px httpsb,sans-serif;
min-width: fit-content;
padding: 0.5em;
width: 90%;
}
#ruleEditor section {
display: flex;
}
.scopeWidget {
line-height: 2.5em;
margin-bottom: 0.5em;
}
#specificScope, .ruleCell:nth-of-type(1) {
flex-grow: 1;
}
#globalScope, .ruleCell:nth-of-type(2) {
width: 4em;
}
#popupContainer > div {
background: #888;
.ruleEditorToolbar {
display: flex;
flex-direction: column;
justify-content: space-around;
margin-left: 0.5em;
}
.ruleEditorToolbar button {
background-color: white;
border: 0;
color: black;
cursor: pointer;
margin: 0;
padding: 0.2em;
position: relative;
}
#popupContainer > div {
text-align: right;
.ruleEditorToolbar button:hover {
background-color: #eee;
}
#popupContainer > div > span {
.ruleEditorToolbar button.disabled {
color: #ccc;
}
.ruleEditorToolbar button.fa {
font: 1.7em FontAwesome;
min-width: 1.5em;
}
.ruleEditorToolbar button > span.badge {
background-color: rgba(240,240,240,0.75);
bottom: 1px;
color: #000;
display: inline-block;
font-family: sans-serif;
font-size: 40%;
padding: 1px 1px;
pointer-events: none;
position: absolute;
right: 1px;
}
.ruleEditorToolbar button.disabled > span.badge {
display: none;
}
button.scopeRel {
color: #24c;
}
body[data-scope="*"] button.scopeRel {
color: #000;
}
.ruleWidgets {
display: flex;
flex-direction: column;
flex-grow: 1;
}
.ruleRow {
display: flex;
line-height: 2em;
margin-top: 1px;
}
.ruleCell {
background-color: #eee;
border: 1px dotted rgba(0,0,0,0.2);
cursor: pointer;
display: inline-block;
font: 14px FontAwesome;
padding: 3px;
margin-left: 1px;
padding: 1px;
position: relative;
}
.ruleCell:hover {
border-style: solid;
}
.ruleCell:nth-of-type(1) {
margin-left: 0;
text-align: right;
}
.ruleCell:nth-of-type(2) {
text-align: center;
}
#popupContainer > div > span:hover {
.ruleCell[data-tcolor="1"] {
border-color: #debaba;
color: black;
background-color: #f8d0d0;
}
#ruleEditor.colorblind .ruleCell[data-tcolor="1"] {
border-color: rgba(0, 19, 110, 0.3);
color: black;
background-color: rgba(0, 19, 110, 0.2);
}
.ruleCell[data-tcolor="2"] {
border-color: #bad6ba;
color: black;
background-color: #d0f0d0;
}
#ruleEditor.colorblind .ruleCell[data-tcolor="2"] {
border-color: rgba(255, 194, 57, 0.3);
color: black;
background-color: rgba(255, 194, 57, 0.2);
}
.ruleCell[data-tcolor="129"] {
color: white;
background-color: #c00;
}
#ruleEditor.colorblind .ruleCell[data-tcolor="129"] {
color: white;
background-color: rgb(0, 19, 110);
}
.ruleCell[data-tcolor="130"] {
color: white;
background-color: #080;
}
#popupContainer > iframe {
#ruleEditor.colorblind .ruleCell[data-tcolor="130"] {
border-color: rgb(255, 194, 57);
color: black;
background-color: rgb(255, 194, 57);
}
.ruleCell[data-pcolor="129"] {
background-image: url('../img/permanent-black-small.png');
background-repeat: no-repeat;
background-position: -1px -1px;
}
#ruleEditor.colorblind .ruleCell[data-pcolor="129"] {
background-image: url('../img/permanent-black-small-cb.png');
}
.ruleCell[data-pcolor="130"] {
background-image: url('../img/permanent-white-small.png');
background-repeat: no-repeat;
background-position: -1px -1px;
}
#ruleEditor.colorblind .ruleCell[data-pcolor="130"] {
background-image: url('../img/permanent-white-small-cb.png');
}
#ruleActionPicker {
border: 0;
height: 100%;
left: 0;
margin: 0;
padding: 0;
position: absolute;
top: 0;
width: 100%;
z-index: 10;
}
.allowRule, .blockRule {
margin: 0;
border: 0;
padding: 0;
position: absolute;
left: 0;
width: 100%;
height: 50%;
background: transparent;
}
#popupContainer.hide {
width: 6em !important;
.allowRule {
top: 0;
}
#popupContainer.hide > iframe {
display: none;
.blockRule {
top: 50%;
}
.ruleCell[data-tcolor="1"] .allowRule:hover {
background-color: #080;
opacity: 0.25;
}
.ruleCell[data-tcolor="1"] .blockRule:hover {
background-color: #c00;
opacity: 0.25;
}
.ruleCell[data-tcolor="2"] .allowRule:hover {
background-color: #080;
opacity: 0.25;
}
.ruleCell[data-tcolor="2"] .blockRule:hover {
background-color: #c00;
opacity: 0.25;
}
.ruleCell[data-tcolor="129"] .allowRule:hover {
background-color: transparent;
}
.ruleCell[data-tcolor="129"] .blockRule:hover {
background-color: transparent;
}
.ruleCell[data-pcolor="130"] .allowRule:hover {
background-color: transparent;
}
.ruleCell[data-pcolor="130"] .blockRule:hover {
background-color: transparent;
}
#ruleEditor.colorblind .ruleCell[data-tcolor="1"] .allowRule:hover,
#ruleEditor.colorblind .ruleCell[data-tcolor="2"] .allowRule:hover {
background-color: rgb(255, 194, 57);
opacity: 0.6;
}
#ruleEditor.colorblind .ruleCell[data-tcolor="1"] .blockRule:hover,
#ruleEditor.colorblind .ruleCell[data-tcolor="2"] .blockRule:hover {
background-color: rgb(0, 19, 110);
opacity: 0.4;
}

88
src/css/popup.css

@ -1,3 +1,24 @@
/*******************************************************************************
uMatrix - a browser extension to black/white list requests.
Copyright (C) 2014-present 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
*/
body {
background-color: white;
border: 0;
@ -301,76 +322,13 @@ button.disabled > span.badge {
display: block;
}
body .toolbar .scope {
background-color: #ccc;
border: 1px solid #ccc;
box-sizing: content-box;
-moz-box-sizing: content-box;
display: inline-flex;
color: white;
margin: 0;
padding: 1px;
cursor: pointer;
}
body .toolbar .scope > span {
align-items: center;
display: inline-flex;
}
body .toolbar .scope > span > span {
pointer-events: none;
white-space: nowrap;
}
body .toolbar #specificScope {
direction: ltr;
justify-content: flex-end;
width: 16em;
}
body .toolbar #specificScope.on {
background-color: #24c;
border-color: #24c;
}
body .toolbar #specificScope > span {
background-color: #ccc;
justify-content: flex-end;
}
body .toolbar #specificScope > span.on {
background-color: #24c;
}
body .toolbar #specificScope > span:first-of-type:not(.on):hover,
body .toolbar #specificScope > span:first-of-type:not(.on):hover ~ span:not(.on),
body .toolbar #specificScope > span:not(.on) + span:not(.on):hover,
body .toolbar #specificScope > span:not(.on) + span:not(.on):hover ~ span:not(.on) {
background-color: #999;
}
body .toolbar #specificScope > span:first-of-type:not(.on):hover ~ span,
body .toolbar #specificScope > span:not(.on) + span:not(.on):hover ~ span,
body .toolbar #specificScope > span.on + span:hover,
body .toolbar #specificScope > span.on + span:hover ~ span {
background-color: #139;
}
body .toolbar #specificScope > span:first-of-type {
flex: 1;
}
body .toolbar #globalScope {
justify-content: center;
margin-left: 1px;
width: 1.6em;
}
body .toolbar #globalScope.on {
background-color: #000;
border-color: #000;
}
body .toolbar #globalScope:not(.on):hover {
background-color: #999;
border-color: #999;
}
body .toolbar .scopeRel {
color: #24c;
}
body.globalScope .toolbar .scopeRel {
body[data-scope="*"] .toolbar .scopeRel {
color: #000;
}
body.globalScope .toolbar .scopeRel.disabled {
body[data-scope="*"] .toolbar .scopeRel.disabled {
color: #ccc;
}

84
src/css/scope-selector.css

@ -0,0 +1,84 @@
/*******************************************************************************
uMatrix - a browser extension to black/white list requests.
Copyright (C) 2018-present 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
*/
.scope {
background-color: #ccc;
border: 1px solid #ccc;
box-sizing: content-box;
-moz-box-sizing: content-box;
display: inline-flex;
color: white;
margin: 0;
padding: 1px;
cursor: pointer;
}
.scope > span {
align-items: center;
display: inline-flex;
}
.scope > span > span {
pointer-events: none;
white-space: nowrap;
}
#specificScope {
direction: ltr;
justify-content: flex-end;
width: 16em;
}
#specificScope.on {
background-color: #24c;
border-color: #24c;
}
#specificScope > span {
background-color: #ccc;
justify-content: flex-end;
}
#specificScope > span.on {
background-color: #24c;
}
#specificScope > span:first-of-type:not(.on):hover,
#specificScope > span:first-of-type:not(.on):hover ~ span:not(.on),
#specificScope > span:not(.on) + span:not(.on):hover,
#specificScope > span:not(.on) + span:not(.on):hover ~ span:not(.on) {
background-color: #999;
}
#specificScope > span:first-of-type:not(.on):hover ~ span,
#specificScope > span:not(.on) + span:not(.on):hover ~ span,
#specificScope > span.on + span:hover,
#specificScope > span.on + span:hover ~ span {
background-color: #139;
}
#specificScope > span:first-of-type {
flex: 1;
}
#globalScope {
justify-content: center;
margin-left: 1px;
width: 1.6em;
}
#globalScope.on {
background-color: #000;
border-color: #000;
}
#globalScope:not(.on):hover {
background-color: #999;
border-color: #999;
}

10
src/js/assets.js

@ -1,7 +1,7 @@
/*******************************************************************************
uMatrix - a browser extension to black/white list requests.
Copyright (C) 2013-2018 Raymond Hill
Copyright (C) 2013-present 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
@ -101,11 +101,9 @@ api.fetchText = function(url, onLoad, onError) {
var onErrorReceived = function() {
this.onload = this.onerror = this.ontimeout = null;
µMatrix.logger.writeOne(
'',
'error',
errorCantConnectTo.replace('{{url}}', actualUrl)
);
µMatrix.logger.writeOne({
error: errorCantConnectTo.replace('{{url}}', actualUrl)
});
onError.call(null, { url: url, content: '' });
};

10
src/js/browsercache.js

@ -1,7 +1,7 @@
/*******************************************************************************
uMatrix - a Chromium browser extension to black/white list requests.
Copyright (C) 2015 Raymond Hill
uMatrix - a browser extension to black/white list requests.
Copyright (C) 2015-present 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
@ -19,14 +19,12 @@
Home: https://github.com/gorhill/uMatrix
*/
/* global µMatrix */
'use strict';
/******************************************************************************/
(function() {
'use strict';
/******************************************************************************/
// Browser data jobs
@ -50,7 +48,7 @@ var clearCache = function() {
µm.browserCacheClearedCounter++;
// TODO: i18n
µm.logger.writeOne('', 'info', vAPI.i18n('loggerEntryBrowserCacheCleared'));
µm.logger.writeOne({ info: vAPI.i18n('loggerEntryBrowserCacheCleared') });
//console.debug('clearBrowserCacheCallback()> vAPI.browserData.clearCache() called');
};

47
src/js/cookies.js

@ -1,7 +1,7 @@
/*******************************************************************************
uMatrix - a browser extension to black/white list requests.
Copyright (C) 2013-2018 Raymond Hill
Copyright (C) 2013-present 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
@ -210,7 +210,7 @@ var recordPageCookie = (function() {
for ( let cookieKey of qentry[1] ) {
let cookieEntry = cookieDict.get(cookieKey);
if ( cookieEntry === undefined ) { continue; }
let block = µm.mustBlock(
let blocked = µm.mustBlock(
pageStore.pageHostname,
cookieEntry.hostname,
'cookie'
@ -224,17 +224,17 @@ var recordPageCookie = (function() {
cookieLogEntryBuilder[4] =
encodeURIComponent(cookieEntry.name);
let cookieURL = cookieLogEntryBuilder.join('');
pageStore.recordRequest('cookie', cookieURL, block);
µm.logger.writeOne(
pageStore.tabId,
'net',
pageStore.pageHostname,
cookieURL,
'cookie',
block
);
pageStore.recordRequest('cookie', cookieURL, blocked);
µm.logger.writeOne({
tabId: pageStore.tabId,
srcHn: pageStore.pageHostname,
desHn: cookieEntry.hostname,
desURL: cookieURL,
type: 'cookie',
blocked
});
cookieEntry.usedOn.add(pageStore.pageHostname);
if ( !block ) { continue; }
if ( !blocked ) { continue; }
if ( µm.userSettings.deleteCookies ) {
removeCookieAsync(cookieKey);
}
@ -288,9 +288,8 @@ var removeCookieAsync = function(cookieKey) {
var chromeCookieRemove = function(cookieEntry, name) {
var url = cookieURLFromCookieEntry(cookieEntry);
if ( url === '' ) {
return;
}
if ( url === '' ) { return; }
var sessionCookieKey = cookieKeyFromCookieURL(url, 'session', name);
var persistCookieKey = cookieKeyFromCookieURL(url, 'persistent', name);
var callback = function(details) {
@ -300,13 +299,17 @@ var chromeCookieRemove = function(cookieEntry, name) {
if ( success ) {
µm.cookieRemovedCounter += 1;
}
µm.logger.writeOne('', 'info', 'cookie', template.replace('{{value}}', sessionCookieKey));
µm.logger.writeOne({
info: template.replace('{{value}}', sessionCookieKey)
});
}
if ( removeCookieFromDict(persistCookieKey) ) {
if ( success ) {
µm.cookieRemovedCounter += 1;
}
µm.logger.writeOne('', 'info', 'cookie', template.replace('{{value}}', persistCookieKey));
µm.logger.writeOne({
info: template.replace('{{value}}', persistCookieKey)
});
}
};
@ -549,7 +552,10 @@ vAPI.cookies.onChanged = (function() {
vAPI.cookies.onRemoved = function(cookie) {
var cookieKey = cookieKeyFromCookie(cookie);
if ( removeCookieFromDict(cookieKey) ) {
µm.logger.writeOne('', 'info', 'cookie', i18nCookieDeleteSuccess.replace('{{value}}', cookieKey));
µm.logger.writeOne({
info: i18nCookieDeleteSuccess.replace('{{value}}', cookieKey),
prettify: 'cookie'
});
}
};
@ -560,7 +566,10 @@ vAPI.cookies.onRemoved = function(cookie) {
vAPI.cookies.onAllRemoved = function() {
for ( var cookieKey of cookieDict.keys() ) {
if ( removeCookieFromDict(cookieKey) ) {
µm.logger.writeOne('', 'info', 'cookie', i18nCookieDeleteSuccess.replace('{{value}}', cookieKey));
µm.logger.writeOne({
info: i18nCookieDeleteSuccess.replace('{{value}}', cookieKey),
prettify: 'cookie'
});
}
}
};

631
src/js/logger-ui.js

@ -1,7 +1,7 @@
/*******************************************************************************
uMatrix - a browser extension to benchmark browser session.
Copyright (C) 2015-2018 Raymond Hill
Copyright (C) 2015-present 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
@ -19,7 +19,7 @@
Home: https://github.com/gorhill/sessbench
*/
/* global uDom */
/* global publicSuffixList, uDom, uMatrixScopeWidget */
'use strict';
@ -32,8 +32,8 @@
var tbody = document.querySelector('#content tbody');
var trJunkyard = [];
var tdJunkyard = [];
var firstVarDataCol = 2; // currently, column 2 (0-based index)
var lastVarDataIndex = 3; // currently, d0-d3
var firstVarDataCol = 1; // currently, column 2 (0-based index)
var lastVarDataIndex = 4; // currently, 5 columns at most
var maxEntries = 0;
var noTabId = '';
var pageStores = new Map();
@ -41,7 +41,6 @@ var pageStoresToken;
var ownerId = Date.now();
var emphasizeTemplate = document.querySelector('#emphasizeTemplate > span');
var hiddenTemplate = document.querySelector('#hiddenTemplate > span');
var prettyRequestTypes = {
'main_frame': 'doc',
@ -67,14 +66,21 @@ document.getElementById('content').style.setProperty(
/******************************************************************************/
var classNameFromTabId = function(tabId) {
if ( tabId === noTabId ) {
return 'tab_bts';
let removeChildren = function(node) {
while ( node.firstChild ) {
node.removeChild(node.firstChild);
}
if ( tabId > 0 ) {
return 'tab_' + tabId;
};
let removeSelf = function(node) {
let parent = node && node.parentNode;
if ( parent ) {
parent.removeChild(node);
}
return '';
};
let prependChild = function(parent, child) {
parent.insertBefore(child, parent.firstElementChild);
};
/******************************************************************************/
@ -158,21 +164,21 @@ var createCellAt = function(tr, index) {
/******************************************************************************/
var createRow = function(layout) {
var tr = trJunkyard.pop();
let tr = trJunkyard.pop();
if ( tr ) {
tr.className = '';
} else {
tr = document.createElement('tr');
}
for ( var index = 0; index < firstVarDataCol; index++ ) {
let index;
for ( index = 0; index < firstVarDataCol; index++ ) {
createCellAt(tr, index);
}
var i = 1, span = 1, td;
let i = 1, span = 1;
let td;
for (;;) {
td = createCellAt(tr, index);
if ( i === lastVarDataIndex ) {
break;
}
if ( i === lastVarDataIndex ) { break; }
if ( layout.charAt(i) !== '1' ) {
span += 1;
} else {
@ -188,22 +194,18 @@ var createRow = function(layout) {
td.setAttribute('colspan', span);
}
index += 1;
while ( (td = tr.cells[index]) ) {
for (;;) {
td = tr.cells[index];
if ( !td ) { break; }
tdJunkyard.push(tr.removeChild(td));
}
tr.removeAttribute('data-srchn');
tr.removeAttribute('data-deshn');
return tr;
};
/******************************************************************************/
var createHiddenTextNode = function(text) {
var node = hiddenTemplate.cloneNode(true);
node.textContent = text;
return node;
};
/******************************************************************************/
var padTo2 = function(v) {
return v < 10 ? '0' + v : v;
};
@ -223,66 +225,84 @@ var createGap = function(tabId, url) {
/******************************************************************************/
var renderLogEntry = function(entry) {
var tr;
var fvdc = firstVarDataCol;
let details;
try {
details = JSON.parse(entry.details);
} catch(ex) {
console.error(ex);
}
if ( details instanceof Object === false ) { return; }
let tr;
let fvdc = firstVarDataCol;
switch ( entry.cat ) {
case 'error':
case 'info':
if ( details.error !== undefined ) {
tr = createRow('1');
if ( entry.d0 === 'cookie' ) {
tr.cells[fvdc].appendChild(emphasizeCookie(entry.d1));
tr.classList.add('cat_error');
tr.cells[fvdc].textContent = details.error;
} else if ( details.info !== undefined ) {
tr = createRow('1');
tr.classList.add('cat_info');
if ( details.prettify === 'cookie' ) {
tr.cells[fvdc].appendChild(emphasizeCookie(details.info));
} else {
tr.cells[fvdc].textContent = entry.d0;
tr.cells[fvdc].textContent = details.info;
}
break;
case 'net':
tr = createRow('111');
} else if ( details.srcHn !== undefined && details.desHn !== undefined ) {
tr = createRow('1111');
tr.classList.add('canMtx');
tr.classList.add('cat_net');
tr.setAttribute('data-srchn', details.srcHn);
tr.setAttribute('data-deshn', details.desHn);
tr.setAttribute('data-type', details.type);
// If the request is that of a root frame, insert a gap in the table
// in order to visually separate entries for different documents.
if ( entry.d2 === 'doc' && entry.tab !== noTabId ) {
createGap(entry.tab, entry.d1);
if ( details.type === 'doc' && details.tabId !== noTabId ) {
createGap(details.tabId, details.desURL);
}
if ( entry.d3 ) {
tr.cells[fvdc+0].textContent = details.srcHn;
if ( details.blocked ) {
tr.classList.add('blocked');
tr.cells[fvdc].textContent = '--';
tr.cells[fvdc+1].textContent = '--';
} else {
tr.cells[fvdc].textContent = '';
tr.cells[fvdc+1].textContent = '';
}
tr.cells[fvdc+1].textContent = (prettyRequestTypes[entry.d2] || entry.d2);
if ( dontEmphasizeSet.has(entry.d2) ) {
tr.cells[fvdc+2].textContent = entry.d1;
} else if ( entry.d2 === 'cookie' ) {
tr.cells[fvdc+2].appendChild(emphasizeCookie(entry.d1));
tr.cells[fvdc+2].textContent = (prettyRequestTypes[details.type] || details.type);
if ( dontEmphasizeSet.has(details.type) ) {
tr.cells[fvdc+3].textContent = details.desURL;
} else {
tr.cells[fvdc+2].appendChild(emphasizeHostname(entry.d1));
tr.cells[fvdc+3].appendChild(emphasizeHostname(details.desURL));
}
break;
default:
} else if ( details.header ) {
tr = createRow('1111');
tr.classList.add('canMtx');
tr.classList.add('cat_net');
tr.cells[fvdc+0].textContent = details.srcHn || '';
if ( details.change === -1 ) {
tr.classList.add('blocked');
tr.cells[fvdc+1].textContent = '--';
} else {
tr.cells[fvdc+1].textContent = '';
}
tr.cells[fvdc+2].textContent = details.header.name;
tr.cells[fvdc+3].textContent = details.header.value;
} else {
tr = createRow('1');
tr.cells[fvdc].textContent = entry.d0;
break;
tr.cells[fvdc].textContent = 'huh?';
}
// Fields common to all rows.
var time = logDate;
let time = logDate;
time.setTime(entry.tstamp - logDateTimezoneOffset);
tr.cells[0].textContent = padTo2(time.getUTCHours()) + ':' +
padTo2(time.getUTCMinutes()) + ':' +
padTo2(time.getSeconds());
if ( entry.tab ) {
if ( details.tabId ) {
tr.classList.add('tab');
tr.classList.add(classNameFromTabId(entry.tab));
if ( entry.tab === noTabId ) {
tr.cells[1].appendChild(createHiddenTextNode('bts'));
}
}
if ( entry.cat !== '' ) {
tr.classList.add('cat_' + entry.cat);
tr.setAttribute('data-tabid', details.tabId);
} else {
tr.removeAttribute('data-tabid');
}
rowFilterer.filterOne(tr, true);
@ -297,18 +317,13 @@ var logDate = new Date(),
/******************************************************************************/
var renderLogEntries = function(response) {
var entries = response.entries;
let entries = response.entries;
if ( entries.length === 0 ) { return; }
// Preserve scroll position
var height = tbody.offsetHeight;
let height = tbody.offsetHeight;
var n = entries.length;
var entry;
for ( var i = 0; i < n; i++ ) {
entry = entries[i];
// Unlikely, but it may happen
if ( entry.tab && pageStores.has(entry.tab) === false ) { continue; }
for ( let i = 0, n = entries.length; i < n; i++ ) {
renderLogEntry(entries[i]);
}
@ -317,10 +332,8 @@ var renderLogEntries = function(response) {
// dynamically refreshed pages.
truncateLog(maxEntries);
var yDelta = tbody.offsetHeight - height;
if ( yDelta === 0 ) {
return;
}
let yDelta = tbody.offsetHeight - height;
if ( yDelta === 0 ) { return; }
// Chromium:
// body.scrollTop = good value
@ -333,7 +346,7 @@ var renderLogEntries = function(response) {
// Firefox:
// body.scrollTop = 0
// body.parentNode.scrollTop = good value
var parentNode = document.body.parentNode;
let parentNode = document.body.parentNode;
if ( parentNode && parentNode.scrollTop !== 0 ) {
parentNode.scrollTop += yDelta;
}
@ -357,10 +370,6 @@ var synchronizeTabIds = function(newPageStores) {
trs.removeClass('canMtx');
rowVoided = true;
}
// Remove popup if it is currently bound to a removed tab.
if ( tabId === popupManager.tabId ) {
popupManager.toggleOff();
}
}
var select = document.getElementById('pageSelector');
@ -379,7 +388,7 @@ var synchronizeTabIds = function(newPageStores) {
select.appendChild(option);
}
option.textContent = newPageStores.get(tabId);
option.value = classNameFromTabId(tabId);
option.value = tabId;
if ( option.value === selectValue ) {
option.setAttribute('selected', '');
} else {
@ -487,22 +496,19 @@ var readLogBufferAsync = function() {
/******************************************************************************/
var pageSelectorChanged = function() {
var style = document.getElementById('tabFilterer');
var tabClass = document.getElementById('pageSelector').value;
var sheet = style.sheet;
let style = document.getElementById('tabFilterer');
let tabId = document.getElementById('pageSelector').value;
let sheet = style.sheet;
while ( sheet.cssRules.length !== 0 ) {
sheet.deleteRule(0);
}
if ( tabClass !== '' ) {
if ( tabId.length !== 0 ) {
sheet.insertRule(
'#content table tr:not(.' + tabClass + ') { display: none; }',
'#content table tr:not([data-tabid="' + tabId + '"]) { display: none; }',
0
);
}
uDom('#refresh').toggleClass(
'disabled',
tabClass === '' || tabClass === 'tab_bts'
);
uDom('#refresh').toggleClass('disabled', /^\d+$/.test(tabId) === false);
};
/******************************************************************************/
@ -678,184 +684,329 @@ var rowFilterer = (function() {
};
})();
/******************************************************************************/
/******************************************************************************/
var toJunkyard = function(trs) {
trs.remove();
var i = trs.length;
while ( i-- ) {
trJunkyard.push(trs.nodeAt(i));
var ruleEditor = (function() {
let ruleEditorNode = document.getElementById('ruleEditor');
let ruleActionPicker = document.getElementById('ruleActionPicker');
let listeners = [];
let addListener = function(node, type, handler, bits) {
let options;
if ( typeof bits === 'number' && (bits & 0b11) !== 0 ) {
options = {};
if ( bits & 0b01 ) {
options.capture = true;
}
if ( bits & 0b10 ) {
options.passive = true;
}
}
listeners.push({ node, type, handler, options });
return node.addEventListener(type, handler, options);
};
/******************************************************************************/
let setup = function(details) {
ruleEditorNode.setAttribute('data-tabid', details.tabId);
ruleEditorNode.classList.toggle(
'colorblind',
details.options.colorBlindFriendly === true
);
var clearBuffer = function() {
var tbody = document.querySelector('#content tbody');
var tr;
while ( tbody.firstChild !== null ) {
tr = tbody.lastElementChild;
trJunkyard.push(tbody.removeChild(tr));
}
uDom('#clear').addClass('disabled');
uDom('#clean').addClass('disabled');
// Initialize scope selector
let srcDn = domainFromSrcHostname(details.srcHn);
let scope = details.options.popupScopeLevel === '*' ?
'*' :
details.options.popupScopeLevel === 'domain' ?
srcDn :
details.srcHn;
uMatrixScopeWidget.init(srcDn, details.srcHn, scope, ruleEditorNode);
// Create rule rows
let ruleWidgets = ruleEditorNode.querySelector('.ruleWidgets');
removeChildren(ruleWidgets);
let ruleWidgetTemplate =
document.querySelector('#ruleRowTemplate .ruleRow');
// Rules: specific to desHn, from broadest to narrowest
let desHn = details.desHn;
let desDn = domainFromDesHostname(desHn);
for (;;) {
let ruleRow = ruleWidgetTemplate.cloneNode(true);
ruleRow.setAttribute('data-deshn', desHn);
ruleRow.children[0].textContent = desHn;
ruleRow.children[1].setAttribute('data-type', details.type);
if ( desHn === details.desHn ) {
ruleRow.children[1].textContent = '1';
}
prependChild(ruleWidgets, ruleRow);
if ( desHn === desDn ) { break; }
let pos = desHn.indexOf('.');
if ( pos === -1 ) { break; }
desHn = desHn.slice(pos + 1);
}
// Rules: 1st-party, if needed
if ( desDn === srcDn ) {
let ruleRow = ruleWidgetTemplate.cloneNode(true);
ruleRow.setAttribute('data-deshn', '1st-party');
ruleRow.children[0].textContent = '1st-party';
ruleRow.children[1].setAttribute('data-type', details.type);
prependChild(ruleWidgets, ruleRow);
}
// Rules: unspecific
{
let ruleRow = ruleWidgetTemplate.cloneNode(true);
ruleRow.setAttribute('data-deshn', '*');
ruleRow.children[0].textContent = 'all';
ruleRow.children[1].setAttribute('data-type', details.type);
ruleRow.children[1].textContent = details.type;
prependChild(ruleWidgets, ruleRow);
}
colorize();
addListener(ruleEditorNode, 'click', quitHandler, 0b01);
addListener(window, 'uMatrixScopeWidgetChange', scopeChangeHandler);
addListener(ruleWidgets, 'mouseenter', attachRulePicker, 0b11);
addListener(ruleWidgets, 'mouseleave', removeRulePicker, 0b11);
addListener(ruleActionPicker, 'click', rulePickerHandler, 0b11);
addListener(ruleEditorNode.querySelector('.buttonReload'), 'click', reload);
addListener(ruleEditorNode.querySelector('.buttonRevertScope'), 'click', revert);
addListener(ruleEditorNode.querySelector('.buttonPersist'), 'click', persist);
document.body.appendChild(ruleEditorNode);
};
/******************************************************************************/
var cleanBuffer = function() {
var rows = uDom('#content tr.tab:not(.canMtx)').remove();
var i = rows.length;
while ( i-- ) {
trJunkyard.push(rows.nodeAt(i));
let colorize = function() {
let srcHn = uMatrixScopeWidget.getScope();
let ruleCells = ruleEditorNode.querySelectorAll('.ruleCell');
let ruleParts = [];
for ( let ruleCell of ruleCells ) {
ruleParts.push(
srcHn,
ruleCell.closest('.ruleRow').getAttribute('data-deshn'),
ruleCell.getAttribute('data-type')
);
}
uDom('#clean').addClass('disabled');
vAPI.messaging.send(
'default',
{ what: 'getCellColors', ruleParts },
response => {
let tColors = response.tColors,
pColors = response.pColors,
diffCount = 0;
for ( let i = 0; i < ruleCells.length; i++ ) {
let ruleCell = ruleCells[i];
let tColor = tColors[i];
let pColor = pColors[i];
ruleCell.setAttribute('data-tcolor', tColor);
ruleCell.setAttribute('data-pcolor', pColor);
if ( tColor === pColor ) { continue; }
if ( tColor < 128 && pColor < 128 ) { continue; }
diffCount += 1;
}
let dirty = diffCount !== 0;
ruleEditorNode
.querySelector('.buttonPersist .badge')
.textContent = dirty ? diffCount : '';
ruleEditorNode
.querySelector('.buttonRevertScope')
.classList
.toggle('disabled', !dirty);
ruleEditorNode
.querySelector('.buttonPersist')
.classList
.toggle('disabled', !dirty);
}
);
};
/******************************************************************************/
var toggleCompactView = function() {
document.body.classList.toggle('compactView');
uDom('#content table .vExpanded').removeClass('vExpanded');
let quitHandler = function(ev) {
let target = ev.target;
if ( target.classList.contains('modalDialog') ) {
stop();
}
};
var toggleCompactRow = function(ev) {
ev.target.parentElement.classList.toggle('vExpanded');
let scopeChangeHandler = function() {
colorize();
};
/******************************************************************************/
var popupManager = (function() {
var realTabId = null;
var localTabId = null;
var container = null;
var popup = null;
var popupObserver = null;
var style = null;
var styleTemplate = [
'tr:not(.tab_{{tabId}}) {',
'cursor: not-allowed;',
'opacity: 0.2;',
'}'
].join('\n');
var resizePopup = function() {
if ( popup === null ) {
let attachRulePicker = function(ev) {
let target = ev.target;
if (
target instanceof HTMLElement === false ||
target.classList.contains('ruleCell') === false
) {
return;
}
var popupBody = popup.contentWindow.document.body;
if ( popupBody.clientWidth !== 0 && container.clientWidth !== popupBody.clientWidth ) {
container.style.setProperty('width', popupBody.clientWidth + 'px');
}
popup.style.removeProperty('height');
if ( popupBody.clientHeight !== 0 && popup.clientHeight !== popupBody.clientHeight ) {
popup.style.setProperty('height', popupBody.clientHeight + 'px');
}
var ph = document.documentElement.clientHeight;
var crect = container.getBoundingClientRect();
if ( crect.height > ph ) {
popup.style.setProperty('height', 'calc(' + ph + 'px - 1.8em)');
}
// Adjust width for presence/absence of vertical scroll bar which may
// have appeared as a result of last operation.
var cw = container.clientWidth;
var dw = popup.contentWindow.document.documentElement.clientWidth;
if ( cw !== dw ) {
container.style.setProperty('width', (2 * cw - dw) + 'px');
}
target.appendChild(ruleActionPicker);
};
var toggleSize = function() {
container.classList.toggle('hide');
let removeRulePicker = function(ev) {
let target = ev.target;
if (
target instanceof HTMLElement === false ||
ruleActionPicker.closest('.ruleCell') === target.closest('.ruleCell')
) {
return;
}
removeSelf(ruleActionPicker);
};
var onResizeRequested = function() {
var popupBody = popup.contentWindow.document.body;
if ( popupBody.hasAttribute('data-resize-popup') === false ) {
return;
let rulePickerHandler = function(ev) {
let action = ev.target.className;
if ( action !== 'allowRule' && action !== 'blockRule' ) { return; }
let cell = ev.target.closest('.ruleCell');
if ( cell === null ) { return; }
let row = cell.closest('.ruleRow');
let desHn = row.getAttribute('data-deshn');
let type = cell.getAttribute('data-type');
let color = parseInt(cell.getAttribute('data-tcolor'), 10);
let what;
if ( color === 1 || color === 2 ) {
what = action === 'blockRule' ?
'blacklistMatrixCell' :
'whitelistMatrixCell';
} else if ( desHn === '*' && type === '*' ) {
what = color === 130 ?
'blacklistMatrixCell' :
'whitelistMatrixCell';
} else {
what = 'graylistMatrixCell';
}
popupBody.removeAttribute('data-resize-popup');
resizePopup();
let request = {
what,
srcHostname: uMatrixScopeWidget.getScope(),
desHostname: desHn,
type
};
vAPI.messaging.send('default', request, colorize);
};
var onLoad = function() {
resizePopup();
var popupBody = popup.contentDocument.body;
popupBody.removeAttribute('data-resize-popup');
popupObserver.observe(popupBody, {
attributes: true,
attributesFilter: [ 'data-resize-popup' ]
let reload = function(ev) {
vAPI.messaging.send('default', {
what: 'forceReloadTab',
tabId: parseInt(ruleEditorNode.getAttribute('data-tabid'), 10),
bypassCache: ev && (ev.ctrlKey || ev.metaKey || ev.shiftKey)
});
};
var toggleOn = function(td) {
var tr = td.parentNode;
var matches = tr.className.match(/(?:^| )tab_([^ ]+)/);
if ( matches === null ) {
return;
let diff = function() {
let entries = [];
let cells = ruleEditorNode.querySelectorAll('.ruleCell');
let srcHn = uMatrixScopeWidget.getScope();
for ( let cell of cells ) {
let tColor = cell.getAttribute('data-tcolor');
let pColor = cell.getAttribute('data-pcolor');
if ( tColor === pColor || tColor < 128 && pColor < 128 ) {
continue;
}
realTabId = localTabId = matches[1];
if ( localTabId === 'bts' ) {
realTabId = noTabId;
let row = cell.closest('.ruleRow');
entries.push({
srcHn,
desHn: row.getAttribute('data-deshn'),
type: cell.getAttribute('data-type')
});
}
return entries;
};
container = document.getElementById('popupContainer');
let persist = function() {
let entries = diff();
if ( entries.length === 0 ) { return; }
vAPI.messaging.send(
'default',
{ what: 'rulesetPersist', entries },
colorize
);
};
container.querySelector('div > span:nth-of-type(1)').addEventListener('click', toggleSize);
container.querySelector('div > span:nth-of-type(2)').addEventListener('click', toggleOff);
let revert = function() {
let entries = diff();
if ( entries.length === 0 ) { return; }
vAPI.messaging.send(
'default',
{ what: 'rulesetRevert', entries },
colorize
);
};
popup = document.createElement('iframe');
popup.addEventListener('load', onLoad);
popup.setAttribute('src', 'popup.html?tabId=' + realTabId);
popupObserver = new MutationObserver(onResizeRequested);
container.appendChild(popup);
let start = function(ev) {
let targetRow = ev.target.parentElement;
let srcHn = targetRow.getAttribute('data-srchn') || '';
let desHn = targetRow.getAttribute('data-deshn') || '';
let type = targetRow.getAttribute('data-type') || '';
if ( srcHn === '' || desHn === '' || type === '' ) { return; }
let tabId = parseInt(targetRow.getAttribute('data-tabid'), 10);
style = document.getElementById('popupFilterer');
style.textContent = styleTemplate.replace('{{tabId}}', localTabId);
vAPI.messaging.send(
'logger-ui.js',
{ what: 'getRuleEditorOptions' },
options => { setup({ tabId, srcHn, desHn, type, options }); }
);
};
document.body.classList.add('popupOn');
let stop = function() {
for ( let { node, type, handler, options } of listeners ) {
node.removeEventListener(type, handler, options);
}
listeners = [];
ruleEditorNode.querySelector('.buttonReload').removeEventListener('click', reload);
removeSelf(ruleEditorNode);
};
var toggleOff = function() {
document.body.classList.remove('popupOn');
return { start, stop };
})();
container.querySelector('div > span:nth-of-type(1)').removeEventListener('click', toggleSize);
container.querySelector('div > span:nth-of-type(2)').removeEventListener('click', toggleOff);
container.classList.remove('hide');
/******************************************************************************/
popup.removeEventListener('load', onLoad);
popupObserver.disconnect();
popupObserver = null;
popup.setAttribute('src', '');
container.removeChild(popup);
popup = null;
var toJunkyard = function(trs) {
trs.remove();
var i = trs.length;
while ( i-- ) {
trJunkyard.push(trs.nodeAt(i));
}
};
style.textContent = '';
style = null;
/******************************************************************************/
container = null;
realTabId = null;
var clearBuffer = function() {
var tbody = document.querySelector('#content tbody');
var tr;
while ( tbody.firstChild !== null ) {
tr = tbody.lastElementChild;
trJunkyard.push(tbody.removeChild(tr));
}
uDom('#clear').addClass('disabled');
uDom('#clean').addClass('disabled');
};
var exports = {
toggleOn: function(ev) {
if ( realTabId === null ) {
toggleOn(ev.target);
}
},
toggleOff: function() {
if ( realTabId !== null ) {
toggleOff();
}
/******************************************************************************/
var cleanBuffer = function() {
var rows = uDom('#content tr.tab:not(.canMtx)').remove();
var i = rows.length;
while ( i-- ) {
trJunkyard.push(rows.nodeAt(i));
}
uDom('#clean').addClass('disabled');
};
Object.defineProperty(exports, 'tabId', {
get: function() { return realTabId || 0; }
});
/******************************************************************************/
return exports;
})();
var toggleCompactView = function() {
document.body.classList.toggle('compactView');
uDom('#content table .vExpanded').removeClass('vExpanded');
};
var toggleCompactRow = function(ev) {
ev.target.parentElement.classList.toggle('vExpanded');
};
/******************************************************************************/
@ -882,6 +1033,40 @@ window.addEventListener('beforeunload', releaseView);
/******************************************************************************/
// We will lookup domains locally.
let domainFromSrcHostname = (function() {
let srcHn = '', srcDn = '';
return function(hn) {
if ( hn !== srcHn ) {
srcHn = hn;
srcDn = publicSuffixList.getDomain(hn);
}
return srcDn;
};
})();
let domainFromDesHostname = (function() {
let desHn = '', desDn = '';
return function(hn) {
if ( hn !== desHn ) {
desHn = hn;
desDn = publicSuffixList.getDomain(hn);
}
return desDn;
};
})();
vAPI.messaging.send(
'logger-ui.js',
{ what: 'getPublicSuffixListData' },
response => {
publicSuffixList.fromSelfie(response);
}
);
/******************************************************************************/
readLogBuffer();
uDom('#pageSelector').on('change', pageSelectorChanged);
@ -891,7 +1076,7 @@ uDom('#clean').on('click', cleanBuffer);
uDom('#clear').on('click', clearBuffer);
uDom('#maxEntries').on('change', onMaxEntriesChanged);
uDom('#content table').on('click', 'tr > td:nth-of-type(1)', toggleCompactRow);
uDom('#content table').on('click', 'tr.canMtx > td:nth-of-type(2)', popupManager.toggleOn);
uDom('#content table').on('click', 'tr.canMtx > td:nth-of-type(3)', ruleEditor.start);
/******************************************************************************/

40
src/js/logger.js

@ -1,7 +1,7 @@
/*******************************************************************************
uMatrix - a browser extension to block requests.
Copyright (C) 2015-2017 Raymond Hill
Copyright (C) 2015-present 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
@ -26,29 +26,24 @@
µMatrix.logger = (function() {
var LogEntry = function(args) {
this.init(args);
let LogEntry = function(details) {
this.init(details);
};
LogEntry.prototype.init = function(args) {
LogEntry.prototype.init = function(details) {
this.tstamp = Date.now();
this.tab = args[0] || '';
this.cat = args[1] || '';
this.d0 = args[2];
this.d1 = args[3];
this.d2 = args[4];
this.d3 = args[5];
this.details = JSON.stringify(details);
};
var buffer = null;
var lastReadTime = 0;
var writePtr = 0;
let buffer = null;
let lastReadTime = 0;
let writePtr = 0;
// After 60 seconds without being read, a buffer will be considered
// unused, and thus removed from memory.
var logBufferObsoleteAfter = 30 * 1000;
let logBufferObsoleteAfter = 30 * 1000;
var janitor = function() {
let janitor = function() {
if (
buffer !== null &&
lastReadTime < (Date.now() - logBufferObsoleteAfter)
@ -56,34 +51,37 @@
buffer = null;
writePtr = 0;
api.ownerId = undefined;
api.enabled = false;
}
if ( buffer !== null ) {
vAPI.setTimeout(janitor, logBufferObsoleteAfter);
}
};
var api = {
let api = {
enabled: false,
ownerId: undefined,
writeOne: function() {
writeOne: function(details) {
if ( buffer === null ) { return; }
if ( writePtr === buffer.length ) {
buffer.push(new LogEntry(arguments));
buffer.push(new LogEntry(details));
} else {
buffer[writePtr].init(arguments);
buffer[writePtr].init(details);
}
writePtr += 1;
},
readAll: function(ownerId) {
this.ownerId = ownerId;
this.enabled = true;
if ( buffer === null ) {
buffer = [];
vAPI.setTimeout(janitor, logBufferObsoleteAfter);
}
var out = buffer.slice(0, writePtr);
let out = buffer.slice(0, writePtr);
writePtr = 0;
lastReadTime = Date.now();
return out;
}
},
};
return api;

31
src/js/matrix.js

@ -509,8 +509,8 @@ Matrix.prototype.evaluateCellZXY = function(srcHostname, desHostname, type) {
/******************************************************************************/
Matrix.prototype.evaluateRowZXY = function(srcHostname, desHostname) {
var out = [];
for ( var type of typeBitOffsets.keys() ) {
let out = [];
for ( let type of typeBitOffsets.keys() ) {
out.push(this.evaluateCellZXY(srcHostname, desHostname, type));
}
return out;
@ -846,6 +846,33 @@ Matrix.prototype.applyDiff = function(diff, from) {
return changed;
};
Matrix.prototype.copyRuleset = function(entries, from, deep) {
let changed = false;
for ( let entry of entries ) {
let srcHn = entry.srcHn;
for (;;) {
if (
entry.switchName !== undefined &&
switchBitOffsets.has(entry.switchName)
) {
let val = from.evaluateSwitch(entry.switchName, srcHn);
if ( this.setSwitch(entry.switchName, srcHn, val) ) {
changed = true;
}
} else if ( entry.desHn && entry.type ) {
let val = from.evaluateCell(srcHn, entry.desHn, entry.type);
if ( this.setCell(srcHn, entry.desHn, entry.type, val) ) {
changed = true;
}
}
if ( !deep ) { break; }
srcHn = toBroaderHostname(srcHn);
if ( srcHn === '' ) { break; }
}
}
return changed;
};
/******************************************************************************/
return Matrix;

156
src/js/messaging.js

@ -19,6 +19,8 @@
Home: https://github.com/gorhill/uMatrix
*/
/* globals publicSuffixList */
'use strict';
/******************************************************************************/
@ -53,6 +55,14 @@ function onMessage(request, sender, callback) {
var response;
switch ( request.what ) {
case 'blacklistMatrixCell':
µm.tMatrix.blacklistCell(
request.srcHostname,
request.desHostname,
request.type
);
break;
case 'forceReloadTab':
µm.forceReload(request.tabId, request.bypassCache);
break;
@ -62,6 +72,25 @@ function onMessage(request, sender, callback) {
µm.assets.updateStart({ delay: 2000 });
break;
case 'getCellColors':
let ruleParts = request.ruleParts;
let tColors = [];
let pColors = [];
for ( let i = 0, n = ruleParts.length; i < n; i += 3 ) {
tColors.push(µm.tMatrix.evaluateCellZXY(
ruleParts[i+0],
ruleParts[i+1],
ruleParts[i+2]
));
pColors.push(µm.pMatrix.evaluateCellZXY(
ruleParts[i+0],
ruleParts[i+1],
ruleParts[i+2]
));
}
response = { tColors, pColors };
break;
case 'getUserSettings':
response = {
userSettings: µm.userSettings,
@ -81,6 +110,14 @@ function onMessage(request, sender, callback) {
µm.gotoURL(request);
break;
case 'graylistMatrixCell':
µm.tMatrix.graylistCell(
request.srcHostname,
request.desHostname,
request.type
);
break;
case 'mustBlock':
response = µm.mustBlock(
request.scope,
@ -89,6 +126,16 @@ function onMessage(request, sender, callback) {
);
break;
case 'rulesetRevert':
µm.tMatrix.copyRuleset(request.entries, µm.pMatrix, true);
break;
case 'rulesetPersist':
if ( µm.pMatrix.copyRuleset(request.entries, µm.tMatrix, true) ) {
µm.saveMatrix();
}
break;
case 'readRawSettings':
response = µm.stringFromRawSettings();
break;
@ -115,6 +162,14 @@ function onMessage(request, sender, callback) {
response = µm.changeUserSettings(request.name, request.value);
break;
case 'whitelistMatrixCell':
µm.tMatrix.whitelistCell(
request.srcHostname,
request.desHostname,
request.type
);
break;
case 'writeRawSettings':
µm.rawSettingsFromString(request.content);
break;
@ -366,30 +421,6 @@ var onMessage = function(request, sender, callback) {
);
break;
case 'blacklistMatrixCell':
µm.tMatrix.blacklistCell(
request.srcHostname,
request.desHostname,
request.type
);
break;
case 'whitelistMatrixCell':
µm.tMatrix.whitelistCell(
request.srcHostname,
request.desHostname,
request.type
);
break;
case 'graylistMatrixCell':
µm.tMatrix.graylistCell(
request.srcHostname,
request.desHostname,
request.type
);
break;
case 'applyDiffToPermanentMatrix': // aka "persist"
if ( µm.pMatrix.applyDiff(request.diff, µm.tMatrix) ) {
µm.saveMatrix();
@ -429,44 +460,44 @@ var µm = µMatrix;
var foundInlineCode = function(tabId, pageStore, details, type) {
if ( pageStore === null ) { return; }
var pageHostname = pageStore.pageHostname,
let srcHn = pageStore.pageHostname,
µmuri = µm.URI.set(details.documentURI),
desHn = µmuri.hostname,
frameURL = µmuri.normalizedURI();
var blocked = details.blocked;
let blocked = details.blocked;
if ( blocked === undefined ) {
blocked = µm.mustBlock(pageHostname, µmuri.hostname, type);
blocked = µm.mustBlock(srcHn, desHn, type);
}
var mapTo = {
let mapTo = {
css: 'style',
script: 'script'
};
// https://github.com/gorhill/httpswitchboard/issues/333
// Look-up here whether inline scripting is blocked for the frame.
var url = frameURL + '{inline_' + mapTo[type] + '}';
pageStore.recordRequest(type, url, blocked);
µm.logger.writeOne(tabId, 'net', pageHostname, url, type, blocked);
let desURL = frameURL + '{inline_' + mapTo[type] + '}';
pageStore.recordRequest(type, desURL, blocked);
µm.logger.writeOne({ tabId, srcHn, desHn, desURL, type, blocked });
};
/******************************************************************************/
var contentScriptLocalStorageHandler = function(tabId, originURL) {
var tabContext = µm.tabContextManager.lookup(tabId);
let tabContext = µm.tabContextManager.lookup(tabId);
if ( tabContext === null ) { return; }
var blocked = µm.mustBlock(
tabContext.rootHostname,
µm.URI.hostnameFromURI(originURL),
'cookie'
);
let srcHn = tabContext.rootHostname,
desHn = µm.URI.hostnameFromURI(originURL),
type = 'cookie',
blocked = µm.mustBlock(srcHn, desHn, type);
var pageStore = µm.pageStoreFromTabId(tabId);
let pageStore = µm.pageStoreFromTabId(tabId);
if ( pageStore !== null ) {
var requestURL = originURL + '/{localStorage}';
pageStore.recordRequest('cookie', requestURL, blocked);
µm.logger.writeOne(tabId, 'net', tabContext.rootHostname, requestURL, 'cookie', blocked);
let desURL = originURL + '/{localStorage}';
pageStore.recordRequest(type, desURL, blocked);
µm.logger.writeOne({ tabId, srcHn, desHn, desURL, type, blocked });
}
var removeStorage = blocked && µm.userSettings.deleteLocalStorage;
@ -545,13 +576,13 @@ var onMessage = function(request, sender, callback) {
break;
}
var tabId = sender && sender.tab ? sender.tab.id || 0 : 0,
let tabId = sender && sender.tab ? sender.tab.id || 0 : 0,
tabContext = µm.tabContextManager.lookup(tabId),
rootHostname = tabContext && tabContext.rootHostname,
srcHn = tabContext && tabContext.rootHostname,
pageStore = µm.pageStoreFromTabId(tabId);
// Sync
var response;
let response;
switch ( request.what ) {
case 'contentScriptHasLocalStorage':
@ -565,8 +596,8 @@ var onMessage = function(request, sender, callback) {
case 'mustRenderNoscriptTags?':
if ( tabContext === null ) { break; }
response =
µm.tMatrix.mustBlock(rootHostname, rootHostname, 'script') &&
µm.tMatrix.evaluateSwitchZ('noscript-spoof', rootHostname);
µm.tMatrix.mustBlock(srcHn, srcHn, 'script') &&
µm.tMatrix.evaluateSwitchZ('noscript-spoof', srcHn);
if ( pageStore !== null ) {
pageStore.hasNoscriptTags = true;
}
@ -578,15 +609,25 @@ var onMessage = function(request, sender, callback) {
case 'securityPolicyViolation':
if ( request.directive === 'worker-src' ) {
var url = µm.URI.hostnameFromURI(request.blockedURI) !== '' ?
request.blockedURI :
request.documentURI;
let desURL = request.blockedURI;
let desHn = µm.URI.hostnameFromURI(desURL);
if ( desHn === '' ) {
desURL = request.documentURI;
desHn = µm.URI.hostnameFromURI(desURL);
}
if ( pageStore !== null ) {
pageStore.hasWebWorkers = true;
pageStore.recordRequest('script', url, request.blocked);
pageStore.recordRequest('script', desURL, request.blocked);
}
if ( tabContext !== null ) {
µm.logger.writeOne(tabId, 'net', rootHostname, url, 'worker', request.blocked);
µm.logger.writeOne({
tabId,
srcHn,
desHn,
desURL,
type: 'worker',
blocked: request.blocked
});
}
} else if ( request.directive === 'script-src' ) {
foundInlineCode(tabId, pageStore, request, 'script');
@ -597,7 +638,7 @@ var onMessage = function(request, sender, callback) {
case 'shutdown?':
if ( tabContext !== null ) {
response = µm.tMatrix.evaluateSwitchZ('matrix-off', rootHostname);
response = µm.tMatrix.evaluateSwitchZ('matrix-off', srcHn);
}
break;
@ -936,6 +977,17 @@ var onMessage = function(request, sender, callback) {
var response;
switch ( request.what ) {
case 'getPublicSuffixListData':
response = publicSuffixList.toSelfie();
break;
case 'getRuleEditorOptions':
response = {
colorBlindFriendly: µm.userSettings.colorBlindFriendly,
popupScopeLevel: µm.userSettings.popupScopeLevel
};
break;
case 'readMany':
if (
µm.logger.ownerId !== undefined &&

107
src/js/popup.js

@ -1,7 +1,7 @@
/*******************************************************************************
uMatrix - a browser extension to black/white list requests.
Copyright (C) 2014-2018 Raymond Hill
Copyright (C) 2014-present 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
@ -19,7 +19,7 @@
Home: https://github.com/gorhill/uMatrix
*/
/* global punycode, uDom */
/* global punycode, uDom, uMatrixScopeWidget */
/* jshint esnext: true, bitwise: false */
'use strict';
@ -501,22 +501,20 @@ function getCellAction(hostname, type, leaning) {
function handleFilter(button, leaning) {
// our parent cell knows who we are
var cell = button.ancestors('div.matCell'),
let cell = button.ancestors('div.matCell'),
expandos = expandosFromNode(cell),
type = expandos.reqType,
desHostname = expandos.hostname;
// https://github.com/gorhill/uMatrix/issues/24
// No hostname can happen -- like with blacklist meta row
if ( desHostname === '' ) {
return;
}
var request = {
if ( desHostname === '' ) { return; }
let request = {
what: getCellAction(desHostname, type, leaning),
srcHostname: matrixSnapshot.scope,
desHostname: desHostname,
type: type
};
vAPI.messaging.send('popup.js', request, updateMatrixSnapshot);
vAPI.messaging.send('default', request, updateMatrixSnapshot);
}
function handleWhitelistFilter(button) {
@ -1055,7 +1053,11 @@ var makeMenu = function() {
makeMatrixGroup4(groupStats[4]);
endMatrixUpdate();
initScopeCell();
uMatrixScopeWidget.init(
matrixSnapshot.domain,
matrixSnapshot.hostname,
matrixSnapshot.scope
);
updateMatrixButtons();
resizePopup();
recipeManager.fetch();
@ -1098,91 +1100,15 @@ function initMenuEnvironment() {
/******************************************************************************/
// Create page scopes for the web page
function selectGlobalScope() {
if ( matrixSnapshot.scope === '*' ) { return; }
matrixSnapshot.scope = '*';
document.body.classList.add('globalScope');
matrixSnapshot.tMatrixModifiedTime = undefined;
updateMatrixSnapshot();
dropDownMenuHide();
}
function selectSpecificScope(ev) {
var newScope = ev.target.getAttribute('data-scope');
function scopeChangeHandler(ev) {
let newScope = ev.detail.scope;
if ( !newScope || matrixSnapshot.scope === newScope ) { return; }
document.body.classList.remove('globalScope');
matrixSnapshot.scope = newScope;
matrixSnapshot.tMatrixModifiedTime = undefined;
updateMatrixSnapshot();
dropDownMenuHide();
}
function initScopeCell() {
// It's possible there is no page URL at this point: some pages cannot
// be filtered by uMatrix.
if ( matrixSnapshot.url === '' ) { return; }
var specificScope = uDom.nodeFromId('specificScope');
while ( specificScope.firstChild !== null ) {
specificScope.removeChild(specificScope.firstChild);
}
// Fill in the scope menu entries
var pos = matrixSnapshot.domain.indexOf('.');
var tld, labels;
if ( pos === -1 ) {
tld = '';
labels = matrixSnapshot.hostname;
} else {
tld = matrixSnapshot.domain.slice(pos + 1);
labels = matrixSnapshot.hostname.slice(0, -tld.length);
}
var beg = 0, span, label;
while ( beg < labels.length ) {
pos = labels.indexOf('.', beg);
if ( pos === -1 ) {
pos = labels.length;
} else {
pos += 1;
}
label = document.createElement('span');
label.appendChild(
document.createTextNode(punycode.toUnicode(labels.slice(beg, pos)))
);
span = document.createElement('span');
span.setAttribute('data-scope', labels.slice(beg) + tld);
span.appendChild(label);
specificScope.appendChild(span);
beg = pos;
}
if ( tld !== '' ) {
label = document.createElement('span');
label.appendChild(document.createTextNode(punycode.toUnicode(tld)));
span = document.createElement('span');
span.setAttribute('data-scope', tld);
span.appendChild(label);
specificScope.appendChild(span);
}
updateScopeCell();
}
function updateScopeCell() {
var specificScope = uDom.nodeFromId('specificScope'),
isGlobal = matrixSnapshot.scope === '*';
document.body.classList.toggle('globalScope', isGlobal);
specificScope.classList.toggle('on', !isGlobal);
uDom.nodeFromId('globalScope').classList.toggle('on', isGlobal);
for ( var node of specificScope.children ) {
node.classList.toggle(
'on',
!isGlobal &&
matrixSnapshot.scope.endsWith(node.getAttribute('data-scope'))
);
}
}
/******************************************************************************/
function updateMatrixSwitches() {
@ -1277,7 +1203,7 @@ function revertMatrix() {
// Buttons which are affected by any changes in the matrix
function updateMatrixButtons() {
updateScopeCell();
uMatrixScopeWidget.update(matrixSnapshot.scope);
updateMatrixSwitches();
updatePersistButton();
}
@ -1285,7 +1211,7 @@ function updateMatrixButtons() {
/******************************************************************************/
function buttonReloadHandler(ev) {
vAPI.messaging.send('popup.js', {
vAPI.messaging.send('default', {
what: 'forceReloadTab',
tabId: matrixSnapshot.tabId,
bypassCache: ev.ctrlKey || ev.metaKey || ev.shiftKey
@ -1637,8 +1563,7 @@ matrixCellHotspots = uDom('#cellHotspots').detach();
uDom('body')
.on('mouseenter', '.matCell', mouseenterMatrixCellHandler)
.on('mouseleave', '.matCell', mouseleaveMatrixCellHandler);
uDom('#specificScope').on('click', selectSpecificScope);
uDom('#globalScope').on('click', selectGlobalScope);
window.addEventListener('uMatrixScopeWidgetChange', scopeChangeHandler);
uDom('#buttonMtxSwitches').on('click', function(ev) {
dropDownMenuShow(ev.target);
});

152
src/js/scope-selector.js

@ -0,0 +1,152 @@
/*******************************************************************************
uMatrix - a browser extension to black/white list requests.
Copyright (C) 2018-present 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
*/
/* exported uMatrixScopeWidget */
/* global punycode */
'use strict';
/******************************************************************************/
/******************************************************************************/
let uMatrixScopeWidget = (function() {
// Start of private namespace
// >>>>>>>>
/******************************************************************************/
let currentScope = '';
let listening = false;
let fireChangeEvent = function() {
document.body.setAttribute('data-scope', currentScope);
let ev = new CustomEvent(
'uMatrixScopeWidgetChange',
{
detail: { scope: currentScope }
}
);
window.dispatchEvent(ev);
};
let init = function(domain, hostname, scope, container) {
if ( typeof domain !== 'string' || domain === '' ) { return; }
currentScope = '';
// Reset widget
if ( !container ) {
container = document;
}
let specificScope = container.querySelector('#specificScope');
while ( specificScope.firstChild !== null ) {
specificScope.removeChild(specificScope.firstChild);
}
// Fill in the scope menu entries
let pos = domain.indexOf('.');
let tld, labels;
if ( pos === -1 ) {
tld = '';
labels = hostname;
} else {
tld = domain.slice(pos + 1);
labels = hostname.slice(0, -tld.length);
}
let beg = 0;
while ( beg < labels.length ) {
pos = labels.indexOf('.', beg);
if ( pos === -1 ) {
pos = labels.length;
} else {
pos += 1;
}
let label = document.createElement('span');
label.appendChild(
document.createTextNode(punycode.toUnicode(labels.slice(beg, pos)))
);
let span = document.createElement('span');
span.setAttribute('data-scope', labels.slice(beg) + tld);
span.appendChild(label);
specificScope.appendChild(span);
beg = pos;
}
if ( tld !== '' ) {
let label = document.createElement('span');
label.appendChild(document.createTextNode(punycode.toUnicode(tld)));
let span = document.createElement('span');
span.setAttribute('data-scope', tld);
span.appendChild(label);
specificScope.appendChild(span);
}
if ( listening === false ) {
container.querySelector('#specificScope').addEventListener(
'click',
ev => { update(ev.target.getAttribute('data-scope')); }
);
container.querySelector('#globalScope').addEventListener(
'click',
( ) => { update('*'); }
);
listening = true;
}
update(scope, container);
};
let getScope = function() {
return currentScope;
};
let update = function(scope, container) {
if ( scope === currentScope ) { return; }
currentScope = scope;
if ( !container ) {
container = document;
}
let specificScope = container.querySelector('#specificScope'),
isGlobal = scope === '*';
specificScope.classList.toggle('on', !isGlobal);
container.querySelector('#globalScope').classList.toggle('on', isGlobal);
for ( let node of specificScope.children ) {
node.classList.toggle(
'on',
!isGlobal &&
scope.endsWith(node.getAttribute('data-scope'))
);
}
fireChangeEvent();
};
return { init, getScope, update };
/******************************************************************************/
// <<<<<<<<
// End of private namespace
})();
/******************************************************************************/
/******************************************************************************/

150
src/js/traffic.js

@ -32,29 +32,29 @@
// Intercept and filter web requests according to white and black lists.
var onBeforeRootFrameRequestHandler = function(details) {
var µm = µMatrix;
var requestURL = details.url;
var requestHostname = µm.URI.hostnameFromURI(requestURL);
var tabId = details.tabId;
let µm = µMatrix;
let desURL = details.url;
let desHn = µm.URI.hostnameFromURI(desURL);
let tabId = details.tabId;
µm.tabContextManager.push(tabId, requestURL);
µm.tabContextManager.push(tabId, desURL);
var tabContext = µm.tabContextManager.mustLookup(tabId);
var rootHostname = tabContext.rootHostname;
let tabContext = µm.tabContextManager.mustLookup(tabId);
let srcHn = tabContext.rootHostname;
// Disallow request as per matrix?
var block = µm.mustBlock(rootHostname, requestHostname, 'doc');
let blocked = µm.mustBlock(srcHn, desHn, 'doc');
var pageStore = µm.pageStoreFromTabId(tabId);
pageStore.recordRequest('doc', requestURL, block);
let pageStore = µm.pageStoreFromTabId(tabId);
pageStore.recordRequest('doc', desURL, blocked);
pageStore.perLoadAllowedRequestCount = 0;
pageStore.perLoadBlockedRequestCount = 0;
µm.logger.writeOne(tabId, 'net', rootHostname, requestURL, 'doc', block);
µm.logger.writeOne({ tabId, srcHn, desHn, desURL, type: 'doc', blocked });
// Not blocked
if ( !block ) {
let redirectURL = maybeRedirectRootFrame(requestHostname, requestURL);
if ( redirectURL !== requestURL ) {
if ( !blocked ) {
let redirectURL = maybeRedirectRootFrame(desHn, desURL);
if ( redirectURL !== desURL ) {
return { redirectUrl: redirectURL };
}
µm.cookieHunter.recordPageCookies(pageStore);
@ -62,11 +62,7 @@ var onBeforeRootFrameRequestHandler = function(details) {
}
// Blocked
var query = btoa(JSON.stringify({
url: requestURL,
hn: requestHostname,
why: '?'
}));
let query = btoa(JSON.stringify({ url: desURL, hn: desHn, why: '?' }));
vAPI.tabs.replace(tabId, vAPI.getURL('main-blocked.html?details=') + query);
@ -99,19 +95,19 @@ var maybeRedirectRootFrame = function(hostname, url) {
// Intercept and filter web requests according to white and black lists.
var onBeforeRequestHandler = function(details) {
var µm = µMatrix,
let µm = µMatrix,
µmuri = µm.URI,
requestURL = details.url,
requestScheme = µmuri.schemeFromURI(requestURL);
desURL = details.url,
desScheme = µmuri.schemeFromURI(desURL);
if ( µmuri.isNetworkScheme(requestScheme) === false ) { return; }
if ( µmuri.isNetworkScheme(desScheme) === false ) { return; }
var requestType = requestTypeNormalizer[details.type] || 'other';
let type = 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 === -1 ) {
if ( type === 'doc' && details.parentFrameId === -1 ) {
return onBeforeRootFrameRequestHandler(details);
}
@ -122,9 +118,9 @@ 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),
let tabContext = µm.tabContextManager.mustLookup(details.tabId),
tabId = tabContext.tabId,
rootHostname = tabContext.rootHostname,
srcHn = tabContext.rootHostname,
specificity = 0;
// https://github.com/gorhill/uMatrix/issues/995
@ -137,18 +133,16 @@ var onBeforeRequestHandler = function(details) {
µmuri.isNetworkURI(details.documentUrl)
) {
tabId = µm.tabContextManager.tabIdFromURL(details.documentUrl);
rootHostname = µmuri.hostnameFromURI(
srcHn = µmuri.hostnameFromURI(
µm.normalizePageURL(0, details.documentUrl)
);
}
// Filter through matrix
var block = µm.tMatrix.mustBlock(
rootHostname,
µmuri.hostnameFromURI(requestURL),
requestType
);
if ( block ) {
let desHn = µmuri.hostnameFromURI(desURL);
let blocked = µm.tMatrix.mustBlock(srcHn, desHn, type);
if ( blocked ) {
specificity = µm.tMatrix.specificityRegister;
}
@ -158,21 +152,23 @@ var onBeforeRequestHandler = function(details) {
// processing has already been performed, and that a synthetic URL has
// been constructed for logging purpose. Use this synthetic URL if
// it is available.
var pageStore = µm.mustPageStoreFromTabId(tabId);
let pageStore = µm.mustPageStoreFromTabId(tabId);
// Enforce strict secure connection?
if ( tabContext.secure && µmuri.isSecureScheme(requestScheme) === false ) {
if ( tabContext.secure && µmuri.isSecureScheme(desScheme) === false ) {
pageStore.hasMixedContent = true;
if ( block === false ) {
block = µm.tMatrix.evaluateSwitchZ('https-strict', rootHostname);
if ( blocked === false ) {
blocked = µm.tMatrix.evaluateSwitchZ('https-strict', srcHn);
}
}
pageStore.recordRequest(requestType, requestURL, block);
µm.logger.writeOne(tabId, 'net', rootHostname, requestURL, details.type, block);
pageStore.recordRequest(type, desURL, blocked);
if ( µm.logger.enabled ) {
µm.logger.writeOne({ tabId, srcHn, desHn, desURL, type, blocked });
}
if ( block ) {
pageStore.cacheBlockedCollapsible(requestType, requestURL, specificity);
if ( blocked ) {
pageStore.cacheBlockedCollapsible(type, desURL, specificity);
return { 'cancel': true };
}
};
@ -184,11 +180,11 @@ var onBeforeRequestHandler = function(details) {
var onBeforeSendHeadersHandler = function(details) {
let µm = µMatrix,
µmuri = µm.URI,
requestURL = details.url,
requestScheme = µmuri.schemeFromURI(requestURL);
desURL = details.url,
desScheme = µmuri.schemeFromURI(desURL);
// Ignore non-network schemes
if ( µmuri.isNetworkScheme(requestScheme) === false ) { return; }
if ( µmuri.isNetworkScheme(desScheme) === false ) { return; }
// Re-classify orphan HTTP requests as behind-the-scene requests. There is
// not much else which can be done, because there are URLs
@ -227,10 +223,10 @@ var onBeforeSendHeadersHandler = function(details) {
if ( headerIndex !== -1 ) {
let headerValue = requestHeaders[headerIndex].value;
if ( headerValue !== '' ) {
var block = µm.userSettings.processHyperlinkAuditing;
pageStore.recordRequest('other', requestURL + '{Ping-To:' + headerValue + '}', block);
µm.logger.writeOne(tabId, 'net', '', requestURL, 'ping', block);
if ( block ) {
let blocked = µm.userSettings.processHyperlinkAuditing;
pageStore.recordRequest('other', desURL + '{Ping-To:' + headerValue + '}', blocked);
µm.logger.writeOne({ tabId, desURL, type: 'ping', blocked });
if ( blocked ) {
µm.hyperlinkAuditingFoiledCounter += 1;
return { 'cancel': true };
}
@ -240,8 +236,8 @@ var onBeforeSendHeadersHandler = function(details) {
// If we reach this point, request is not blocked, so what is left to do
// is to sanitize headers.
let rootHostname = pageStore.pageHostname,
requestHostname = µmuri.hostnameFromURI(requestURL),
let srcHn = pageStore.pageHostname,
desHn = µmuri.hostnameFromURI(desURL),
modified = false;
// Process `Cookie` header.
@ -249,7 +245,7 @@ var onBeforeSendHeadersHandler = function(details) {
headerIndex = headerIndexFromName('cookie', requestHeaders);
if (
headerIndex !== -1 &&
µm.mustBlock(rootHostname, requestHostname, 'cookie')
µm.mustBlock(srcHn, desHn, 'cookie')
) {
modified = true;
let headerValue = requestHeaders[headerIndex].value;
@ -257,7 +253,12 @@ var onBeforeSendHeadersHandler = function(details) {
µm.cookieHeaderFoiledCounter++;
if ( requestType === 'doc' ) {
pageStore.perLoadBlockedRequestCount++;
µm.logger.writeOne(tabId, 'net', '', headerValue, 'COOKIE', true);
µm.logger.writeOne({
tabId,
srcHn,
header: { name: 'COOKIE', value: headerValue },
change: -1
});
}
}
@ -285,23 +286,33 @@ var onBeforeSendHeadersHandler = function(details) {
if ( headerIndex !== -1 ) {
let headerValue = requestHeaders[headerIndex].value;
if ( headerValue !== '' ) {
let toDomain = µmuri.domainFromHostname(requestHostname);
let toDomain = µmuri.domainFromHostname(desHn);
if ( toDomain !== '' && toDomain !== µmuri.domainFromURI(headerValue) ) {
pageStore.has3pReferrer = true;
if ( µm.tMatrix.evaluateSwitchZ('referrer-spoof', rootHostname) ) {
if ( µm.tMatrix.evaluateSwitchZ('referrer-spoof', srcHn) ) {
modified = true;
let newValue;
if ( details.method === 'GET' ) {
newValue = requestHeaders[headerIndex].value =
requestScheme + '://' + requestHostname + '/';
desScheme + '://' + desHn + '/';
} else {
requestHeaders.splice(headerIndex, 1);
}
if ( pageStore.perLoadBlockedReferrerCount === 0 ) {
pageStore.perLoadBlockedRequestCount += 1;
µm.logger.writeOne(tabId, 'net', '', headerValue, 'REFERER', true);
µm.logger.writeOne({
tabId,
srcHn,
header: { name: 'REFERER', value: headerValue },
change: -1
});
if ( newValue !== undefined ) {
µm.logger.writeOne(tabId, 'net', '', newValue, 'REFERER', false);
µm.logger.writeOne({
tabId,
srcHn,
header: { name: 'REFERER', value: newValue },
change: +1
});
}
}
pageStore.perLoadBlockedReferrerCount += 1;
@ -329,7 +340,7 @@ var onBeforeSendHeadersHandler = function(details) {
var onHeadersReceived = function(details) {
// Ignore schemes other than 'http...'
var µm = µMatrix,
let µm = µMatrix,
tabId = details.tabId,
requestURL = details.url,
requestType = requestTypeNormalizer[details.type] || 'other';
@ -340,25 +351,25 @@ var onHeadersReceived = function(details) {
µm.tabContextManager.push(tabId, requestURL);
}
var tabContext = µm.tabContextManager.lookup(tabId);
let tabContext = µm.tabContextManager.lookup(tabId);
if ( tabContext === null ) { return; }
var csp = [],
let csp = [],
cspReport = [],
rootHostname = tabContext.rootHostname,
requestHostname = µm.URI.hostnameFromURI(requestURL);
srcHn = tabContext.rootHostname,
desHn = µm.URI.hostnameFromURI(requestURL);
// Inline script tags.
if ( µm.mustAllow(rootHostname, requestHostname, 'script' ) !== true ) {
if ( µm.mustBlock(srcHn, desHn, 'script' ) ) {
csp.push(µm.cspNoInlineScript);
}
// Inline style tags.
if ( µm.mustAllow(rootHostname, requestHostname, 'css' ) !== true ) {
if ( µm.mustBlock(srcHn, desHn, 'css' ) ) {
csp.push(µm.cspNoInlineStyle);
}
if ( µm.tMatrix.evaluateSwitchZ('no-workers', rootHostname) ) {
if ( µm.tMatrix.evaluateSwitchZ('no-workers', srcHn) ) {
csp.push(µm.cspNoWorker);
} else if ( µm.rawSettings.disableCSPReportInjection === false ) {
cspReport.push(µm.cspNoWorker);
@ -391,7 +402,12 @@ var onHeadersReceived = function(details) {
value: cspTotal
});
if ( requestType === 'doc' ) {
µm.logger.writeOne(tabId, 'net', '', cspRight, 'CSP', false);
µm.logger.writeOne({
tabId,
srcHn,
header: { name: 'CSP', value: cspRight },
change: +1
});
}
}

33
src/logger-ui.html

@ -4,6 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="css/common.css">
<link rel="stylesheet" type="text/css" href="css/scope-selector.css">
<link rel="stylesheet" type="text/css" href="css/logger-ui.css">
<link rel="shortcut icon" type="image/png" href="img/icon_16.png">
<title data-i18n="loggerPageName"></title>
@ -36,19 +37,45 @@
</table>
</div>
<div id="popupContainer">
<div><span>&#xf068;</span>&ensp;<span>&#xf00d;</span></div>
</div>
<div style="display: none;">
<div id="emphasizeTemplate"><span><span></span><b></b><span></span></span></div>
<div id="hiddenTemplate"><span style="display:none;"></span></div>
<div id="ruleEditor" class="modalDialog">
<div class="dialog">
<section class="scopeWidget">
<span class="scope" id="specificScope"><span>&nbsp;</span></span><!--
--><span class="scope" id="globalScope" data-scope="*" data-i18n-tip="matrixGlobalScopeTip"><span><span>&#x2217;</span></span></span>
<div class="ruleEditorToolbar">
<button type="button" class="buttonReload fa tip-anchor-right" data-i18n-tip="matrixReloadButton">&#xf021;</button>
</div>
</section>
<section>
<div class="ruleWidgets"></div>
<div class="ruleEditorToolbar">
<button type="button" class="buttonRevertScope fa scopeRel tip-anchor-right" tabindex="-1" data-i18n-tip="matrixRevertButtonTip">&#xf12d;</button>
<button type="button" class="buttonPersist fa scopeRel tip-anchor-right" data-i18n-tip="matrixPersistButtonTip">&#xf023;<span class="badge"></span></button>
</section>
</section>
</div>
</div>
<div id="ruleRowTemplate" style="display: none;">
<div class="ruleRow"><!--
--><span class="ruleCell" data-type="*">&nbsp;</span><!--
--><span class="ruleCell" data-type>&nbsp;</span><!--
--></div>
</div>
<div id="ruleActionPicker"><div class="allowRule"></div><div class="blockRule"></div></div>
</div>
<script src="lib/punycode.js"></script>
<script src="lib/publicsuffixlist.js"></script>
<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/scope-selector.js"></script>
<script src="js/logger-ui.js"></script>
</body>

2
src/popup.html

@ -5,6 +5,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/common.css" type="text/css">
<link rel="stylesheet" href="css/scope-selector.css" type="text/css">
<link rel="stylesheet" href="css/popup.css" type="text/css">
<title>uMatrix panel</title>
</head>
@ -119,6 +120,7 @@
<script src="js/vapi-client.js"></script>
<script src="js/udom.js"></script>
<script src="js/i18n.js"></script>
<script src="js/scope-selector.js"></script>
<script src="js/popup.js"></script>
</body>

Loading…
Cancel
Save