diff --git a/src/css/common.css b/src/css/common.css index c029bdd..ce51448 100644 --- a/src/css/common.css +++ b/src/css/common.css @@ -31,6 +31,9 @@ display: inline-block; } +body { + font-size: 14px; + } body[dir="ltr"] { direction: ltr; } diff --git a/src/css/logger-ui.css b/src/css/logger-ui.css index 120fc73..ca28a20 100644 --- a/src/css/logger-ui.css +++ b/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; } diff --git a/src/css/popup.css b/src/css/popup.css index ae7244c..207f167 100644 --- a/src/css/popup.css +++ b/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; } diff --git a/src/css/scope-selector.css b/src/css/scope-selector.css new file mode 100644 index 0000000..720381e --- /dev/null +++ b/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; + } diff --git a/src/js/assets.js b/src/js/assets.js index d3cfd3e..195b014 100644 --- a/src/js/assets.js +++ b/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: '' }); }; diff --git a/src/js/browsercache.js b/src/js/browsercache.js index 33500e3..6f9cb8c 100644 --- a/src/js/browsercache.js +++ b/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'); }; diff --git a/src/js/cookies.js b/src/js/cookies.js index ffd3ecb..890bfb4 100644 --- a/src/js/cookies.js +++ b/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' + }); } } }; diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index f72dccc..6016f77 100644 --- a/src/js/logger-ui.js +++ b/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; } - switch ( entry.cat ) { - case 'error': - case 'info': + let tr; + let fvdc = firstVarDataCol; + + if ( details.error !== undefined ) { + tr = createRow('1'); + tr.classList.add('cat_error'); + tr.cells[fvdc].textContent = details.error; + } else if ( details.info !== undefined ) { tr = createRow('1'); - if ( entry.d0 === 'cookie' ) { - tr.cells[fvdc].appendChild(emphasizeCookie(entry.d1)); + 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; - - 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; } + let height = tbody.offsetHeight; + + 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,6 +684,285 @@ var rowFilterer = (function() { }; })(); +/******************************************************************************/ +/******************************************************************************/ + +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 + ); + + // 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); + }; + + 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') + ); + } + 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); + } + ); + }; + + let quitHandler = function(ev) { + let target = ev.target; + if ( target.classList.contains('modalDialog') ) { + stop(); + } + }; + + let scopeChangeHandler = function() { + colorize(); + }; + + let attachRulePicker = function(ev) { + let target = ev.target; + if ( + target instanceof HTMLElement === false || + target.classList.contains('ruleCell') === false + ) { + return; + } + target.appendChild(ruleActionPicker); + }; + + let removeRulePicker = function(ev) { + let target = ev.target; + if ( + target instanceof HTMLElement === false || + ruleActionPicker.closest('.ruleCell') === target.closest('.ruleCell') + ) { + return; + } + removeSelf(ruleActionPicker); + }; + + 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'; + } + let request = { + what, + srcHostname: uMatrixScopeWidget.getScope(), + desHostname: desHn, + type + }; + vAPI.messaging.send('default', request, colorize); + }; + + + 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) + }); + }; + + 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; + } + let row = cell.closest('.ruleRow'); + entries.push({ + srcHn, + desHn: row.getAttribute('data-deshn'), + type: cell.getAttribute('data-type') + }); + } + return entries; + }; + + let persist = function() { + let entries = diff(); + if ( entries.length === 0 ) { return; } + vAPI.messaging.send( + 'default', + { what: 'rulesetPersist', entries }, + colorize + ); + }; + + let revert = function() { + let entries = diff(); + if ( entries.length === 0 ) { return; } + vAPI.messaging.send( + 'default', + { what: 'rulesetRevert', entries }, + colorize + ); + }; + + 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); + + vAPI.messaging.send( + 'logger-ui.js', + { what: 'getRuleEditorOptions' }, + options => { setup({ tabId, srcHn, desHn, type, options }); } + ); + }; + + 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); + }; + + return { start, stop }; +})(); + /******************************************************************************/ var toJunkyard = function(trs) { @@ -725,140 +1010,6 @@ var toggleCompactRow = function(ev) { /******************************************************************************/ -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 ) { - 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'); - } - }; - - var toggleSize = function() { - container.classList.toggle('hide'); - }; - - var onResizeRequested = function() { - var popupBody = popup.contentWindow.document.body; - if ( popupBody.hasAttribute('data-resize-popup') === false ) { - return; - } - popupBody.removeAttribute('data-resize-popup'); - resizePopup(); - }; - - var onLoad = function() { - resizePopup(); - var popupBody = popup.contentDocument.body; - popupBody.removeAttribute('data-resize-popup'); - popupObserver.observe(popupBody, { - attributes: true, - attributesFilter: [ 'data-resize-popup' ] - }); - }; - - var toggleOn = function(td) { - var tr = td.parentNode; - var matches = tr.className.match(/(?:^| )tab_([^ ]+)/); - if ( matches === null ) { - return; - } - realTabId = localTabId = matches[1]; - if ( localTabId === 'bts' ) { - realTabId = noTabId; - } - - container = document.getElementById('popupContainer'); - - container.querySelector('div > span:nth-of-type(1)').addEventListener('click', toggleSize); - container.querySelector('div > span:nth-of-type(2)').addEventListener('click', toggleOff); - - popup = document.createElement('iframe'); - popup.addEventListener('load', onLoad); - popup.setAttribute('src', 'popup.html?tabId=' + realTabId); - popupObserver = new MutationObserver(onResizeRequested); - container.appendChild(popup); - - style = document.getElementById('popupFilterer'); - style.textContent = styleTemplate.replace('{{tabId}}', localTabId); - - document.body.classList.add('popupOn'); - }; - - var toggleOff = function() { - document.body.classList.remove('popupOn'); - - 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; - - style.textContent = ''; - style = null; - - container = null; - realTabId = null; - }; - - var exports = { - toggleOn: function(ev) { - if ( realTabId === null ) { - toggleOn(ev.target); - } - }, - toggleOff: function() { - if ( realTabId !== null ) { - toggleOff(); - } - } - }; - - Object.defineProperty(exports, 'tabId', { - get: function() { return realTabId || 0; } - }); - - return exports; -})(); - -/******************************************************************************/ - var grabView = function() { if ( ownerId === undefined ) { ownerId = Date.now(); @@ -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); /******************************************************************************/ diff --git a/src/js/logger.js b/src/js/logger.js index ded4f72..3e86f44 100644 --- a/src/js/logger.js +++ b/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; diff --git a/src/js/matrix.js b/src/js/matrix.js index aaa223b..26311fd 100644 --- a/src/js/matrix.js +++ b/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; diff --git a/src/js/messaging.js b/src/js/messaging.js index 9688d71..ce8136d 100644 --- a/src/js/messaging.js +++ b/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); + // Look-up here whether inline scripting is blocked for the frame. + 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 && diff --git a/src/js/popup.js b/src/js/popup.js index 886277d..7899303 100644 --- a/src/js/popup.js +++ b/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); }); diff --git a/src/js/scope-selector.js b/src/js/scope-selector.js new file mode 100644 index 0000000..35189ff --- /dev/null +++ b/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 + +})(); + +/******************************************************************************/ +/******************************************************************************/ + diff --git a/src/js/traffic.js b/src/js/traffic.js index 461f575..05dc026 100644 --- a/src/js/traffic.js +++ b/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 + }); } } diff --git a/src/logger-ui.html b/src/logger-ui.html index ea0f57a..638ca65 100644 --- a/src/logger-ui.html +++ b/src/logger-ui.html @@ -4,6 +4,7 @@ + @@ -36,19 +37,45 @@ -
-
-
+
+
+
+
+   +
+ +
+
+
+
+
+ + +
+ +
+
+ +
+
+ + + diff --git a/src/popup.html b/src/popup.html index ef1cab0..b33086b 100644 --- a/src/popup.html +++ b/src/popup.html @@ -5,6 +5,7 @@ + uMatrix panel @@ -119,6 +120,7 @@ +