Additionally, there has been refactoring work done regarding
filtering context used throughout uBO, motivated by the fix
here.
This commit is contained in:
Raymond Hill 2018-12-13 12:30:54 -05:00
parent a629dbdd08
commit 9b27a98f90
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
26 changed files with 1261 additions and 1203 deletions

View File

@ -282,21 +282,12 @@ vAPI.tabs = {};
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/3546
// Added a new flavor of behind-the-scene tab id: vAPI.anyTabId.
// vAPI.anyTabId will be used for network requests which can be filtered,
// because they comes with enough contextual information. It's just not
// possible to pinpoint exactly from which tab it comes from. For example,
// with Firefox/webext, the `documentUrl` property is available for every
// network requests.
vAPI.isBehindTheSceneTabId = function(tabId) {
return tabId < 0;
};
vAPI.unsetTabId = 0;
vAPI.noTabId = -1; // definitely not any existing tab
vAPI.anyTabId = -2; // one of the existing tab
/******************************************************************************/

View File

@ -74,11 +74,9 @@
// Chromium 63+ supports the `initiator` property, which contains
// the URL of the origin from which the network request was made.
if (
details.tabId === vAPI.noTabId &&
typeof details.initiator === 'string' &&
details.initiator !== 'null'
) {
details.tabId = vAPI.anyTabId;
details.documentUrl = details.initiator;
}

View File

@ -1,7 +1,7 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2017-2018 Raymond Hill
Copyright (C) 2017-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
@ -66,13 +66,6 @@
let parsedURL = new URL('about:blank');
vAPI.net.normalizeDetails = function(details) {
if (
details.tabId === vAPI.noTabId &&
typeof details.documentUrl === 'string'
) {
details.tabId = vAPI.anyTabId;
}
if ( mustPunycode && !reAsciiHostname.test(details.url) ) {
parsedURL.href = details.url;
details.url = details.url.replace(

View File

@ -85,13 +85,11 @@ vAPI.net.registerListeners = function() {
// Chromium uses `initiator` property.
if (
details.documentUrl === undefined &&
typeof details.initiator === 'string'
typeof details.initiator === 'string' &&
details.initiator !== 'null'
) {
details.documentUrl = details.initiator;
}
if ( typeof details.documentUrl === 'string' ) {
details.tabId = vAPI.anyTabId;
}
}
// https://github.com/gorhill/uBlock/issues/1493

View File

@ -536,7 +536,7 @@
"description":"Appears in the logger's tab selector"
},
"logBehindTheScene":{
"message":"Behind the scene",
"message":"Tabless",
"description":"Pretty name for behind-the-scene network requests"
},
"loggerCurrentTab":{

View File

@ -20,6 +20,7 @@
<script src="js/lz4.js"></script>
<script src="js/cachestorage.js"></script>
<script src="js/assets.js"></script>
<script src="js/filtering-context.js"></script>
<script src="js/redirect-engine.js"></script>
<script src="js/dynamic-net-filtering.js"></script>
<script src="js/static-net-filtering.js"></script>

View File

@ -16,9 +16,11 @@ textarea {
width: 100%;
}
.permatoolbar {
align-items: center;
background-color: white;
border: 0;
box-sizing: border-box;
display: flex;
font-size: 120%;
left: 0;
margin: 0;
@ -28,14 +30,15 @@ textarea {
z-index: 10;
}
.permatoolbar .button {
align-items: center;
background-color: white;
border: none;
box-sizing: border-box;
cursor: pointer;
display: inline-block;
display: inline-flex;
font-size: 150%;
margin: 0;
padding: 8px;
padding: 0.25em;
}
.permatoolbar .button.disabled {
opacity: 0.2;
@ -54,8 +57,14 @@ textarea {
vertical-align: middle;
}
#pageSelector {
margin-right: 1em;
padding: 0.25em 0;
width: 28em;
padding: 0.2em 0;
}
#showpopup img {
filter: grayscale(100%);
height: auto;
width: 1em;
}
/*
@ -128,19 +137,22 @@ textarea {
width: 4.6em;
}
#netInspector table > colgroup > col:nth-of-type(2) {
width: 2.1em;
width: 16%;
}
#netInspector table > colgroup > col:nth-of-type(3) {
width: 20%;
}
#netInspector table > colgroup > col:nth-of-type(4) {
width: 2.1em;
}
#netInspector table > colgroup > col:nth-of-type(4) {
width: 20%;
}
#netInspector table > colgroup > col:nth-of-type(5) {
width: 5.8em;
width: 2.4em;
}
#netInspector table > colgroup > col:nth-of-type(6) {
width: calc(100% - 14.6em - 20%);
width: 6em;
}
#netInspector table > colgroup > col:nth-of-type(7) {
width: calc(100% - 4.6em - 16% - 2.1em - 20% - 2.4em - 6em);
}
#netInspector.f table tr.f {
display: none;
@ -205,6 +217,12 @@ body #netInspector td {
text-overflow: ellipsis;
white-space: nowrap;
}
#netInspector tr[data-tabid].void {
opacity: 0.3;
}
#netInspector tr[data-tabid].void:hover {
opacity: 0.7;
}
#netInspector tr td:nth-of-type(1) {
cursor: default;
@ -212,68 +230,71 @@ body #netInspector td {
white-space: nowrap;
}
#netInspector tr td:nth-of-type(2) {
text-align: center;
white-space: nowrap;
}
#netInspector tr.tab_bts > td:nth-of-type(2):before {
content: '\f070';
font: 1em FontAwesome;
}
#netInspector tr.tab:not(.canMtx) {
opacity: 0.3;
}
#netInspector tr.tab:not(.canMtx):hover {
opacity: 0.7;
}
#netInspector tr.tab:not(.canMtx) > td:nth-of-type(2):before {
content: '\f00d';
font: 1em FontAwesome;
}
body:not(.popupOn) #netInspector tr.canMtx td:nth-of-type(2) {
#netInspector tr.canLookup td:nth-of-type(2) {
cursor: zoom-in;
}
body:not(.popupOn) #netInspector tr.canMtx td:nth-of-type(2):hover {
background: #ccc;
}
#netInspector tr.canLookup td:nth-of-type(3) {
cursor: zoom-in;
}
#netInspector tr.cat_net td:nth-of-type(4),
#netInspector tr.cat_cosmetic td:nth-of-type(4),
#netInspector tr.cat_redirect td:nth-of-type(4) {
#netInspector tr.cat_net td:nth-of-type(3),
#netInspector tr.cat_cosmetic td:nth-of-type(3),
#netInspector tr.cat_redirect td:nth-of-type(3) {
font: 12px monospace;
text-align: center;
white-space: nowrap;
}
#netInspector tr.cat_net td:nth-of-type(4) {
#netInspector tr.cat_net td:nth-of-type(3) {
cursor: pointer;
position: relative;
}
#netInspector tr.cat_net td:nth-of-type(4):hover {
#netInspector tr.cat_net td:nth-of-type(3):hover {
background: #ccc;
}
#netInspector tr.cat_net td:nth-of-type(6) > span > b {
#netInspector tr td:nth-of-type(4) {
}
#netInspector tr[data-dochn] td:nth-of-type(4) {
direction: rtl;
}
#netInspector tr td:nth-of-type(5) {
cursor: default;
position: relative;
text-align: center;
}
#netInspector tr.tab_bts td:nth-of-type(5)::after {
border: 5px solid #bbb;
border-bottom: 0;
border-top: 0;
bottom: 0;
content: '\00a0';
left: 0;
position: absolute;
right: 0;
top: 0;
width: calc(100% - 10px);
z-index: -1;
}
#netInspector tr td:nth-of-type(6) {
text-align: right;
}
#netInspector tr.cat_net td:nth-of-type(7) > span > b {
font-weight: bold;
}
#netInspector tr td:nth-of-type(6) b {
#netInspector tr td:nth-of-type(7) b {
font-weight: normal;
}
#netInspector tr.blocked td:nth-of-type(6) b {
#netInspector tr.blocked td:nth-of-type(7) b {
background-color: rgba(192, 0, 0, 0.2);
}
body.colorBlind #netInspector tr.blocked td:nth-of-type(6) b {
body.colorBlind #netInspector tr.blocked td:nth-of-type(7) b {
background-color: rgba(0, 19, 110, 0.2);
}
#netInspector tr.nooped td:nth-of-type(6) b {
#netInspector tr.nooped td:nth-of-type(7) b {
background-color: rgba(108, 108, 108, 0.2);
}
body.colorBlind #netInspector tr.nooped td:nth-of-type(6) b {
body.colorBlind #netInspector tr.nooped td:nth-of-type(7) b {
background-color: rgba(96, 96, 96, 0.2);
}
#netInspector tr.allowed td:nth-of-type(6) b {
#netInspector tr.allowed td:nth-of-type(7) b {
background-color: rgba(0, 160, 0, 0.2);
}
body.colorBlind #netInspector tr.allowed td:nth-of-type(6) b {
body.colorBlind #netInspector tr.allowed td:nth-of-type(7) b {
background-color: rgba(255, 194, 57, 0.2);
}
@ -281,49 +302,17 @@ body.colorBlind #netInspector tr.allowed td:nth-of-type(6) b {
background: white;
border: 1px solid gray;
border-radius: 3px;
bottom: 0;
box-sizing: border-box;
display: none;
overflow: hidden;
position: fixed;
right: 0;
top: 0;
z-index: 200;
}
#netInspector.popupOn #popupContainer {
#inspectors.popupOn #popupContainer {
display: block;
}
#popupContainer > div {
background: #aaa;
border: 0;
}
body[dir="ltr"] #popupContainer > div {
text-align: right;
}
body[dir="rtl"] #popupContainer > div {
text-align: left;
}
#popupContainer > div > span {
color: #eee;
cursor: pointer;
display: inline-block;
font: 14px FontAwesome;
padding: 0 3px;
}
#popupContainer > div > span:hover {
color: white;
}
#popupContainer > iframe {
border: 0;
padding: 0;
margin: 0;
width: 100%;
}
#popupContainer.hide {
width: 6em !important;
}
#popupContainer.hide > iframe {
display: none;
}
.modalDialog {
align-items: center;

View File

@ -27,13 +27,12 @@
/******************************************************************************/
var reIsExternalPath = /^(?:[a-z-]+):\/\//,
const reIsExternalPath = /^(?:[a-z-]+):\/\//,
reIsUserAsset = /^user-/,
errorCantConnectTo = vAPI.i18n('errorCantConnectTo'),
noopfunc = function(){};
var api = {
};
const api = {};
/******************************************************************************/
@ -64,14 +63,14 @@ var fireNotification = function(topic, details) {
/******************************************************************************/
api.fetchText = function(url, onLoad, onError) {
var isExternal = reIsExternalPath.test(url),
actualUrl = isExternal ? url : vAPI.getURL(url);
const isExternal = reIsExternalPath.test(url);
let actualUrl = isExternal ? url : vAPI.getURL(url);
// https://github.com/gorhill/uBlock/issues/2592
// Force browser cache to be bypassed, but only for resources which have
// been fetched more than one hour ago.
if ( isExternal ) {
var queryValue = '_=' + Math.floor(Date.now() / 7200000);
const queryValue = '_=' + Math.floor(Date.now() / 7200000);
if ( actualUrl.indexOf('?') === -1 ) {
actualUrl += '?';
} else {
@ -84,12 +83,12 @@ api.fetchText = function(url, onLoad, onError) {
onError = onLoad;
}
var contentLoaded = 0,
timeoutAfter = µBlock.hiddenSettings.assetFetchTimeout * 1000 || 30000,
timeoutTimer,
xhr = new XMLHttpRequest();
const timeoutAfter = µBlock.hiddenSettings.assetFetchTimeout * 1000 || 30000;
const xhr = new XMLHttpRequest();
let contentLoaded = 0;
let timeoutTimer;
var cleanup = function() {
const cleanup = function() {
xhr.removeEventListener('load', onLoadEvent);
xhr.removeEventListener('error', onErrorEvent);
xhr.removeEventListener('abort', onErrorEvent);
@ -101,11 +100,11 @@ api.fetchText = function(url, onLoad, onError) {
};
// https://github.com/gorhill/uMatrix/issues/15
var onLoadEvent = function() {
const onLoadEvent = function() {
cleanup();
// xhr for local files gives status 0, but actually succeeds
var details = {
url: url,
const details = {
url,
content: '',
statusCode: this.status || 200,
statusText: this.statusText || ''
@ -120,7 +119,7 @@ api.fetchText = function(url, onLoad, onError) {
// we never download anything else than plain text: discard if response
// appears to be a HTML document: could happen when server serves
// some kind of error page I suppose
var text = this.responseText.trim();
const text = this.responseText.trim();
if ( text.startsWith('<') && text.endsWith('>') ) {
return onError.call(null, details);
}
@ -128,19 +127,21 @@ api.fetchText = function(url, onLoad, onError) {
onLoad(details);
};
var onErrorEvent = function() {
const onErrorEvent = function() {
cleanup();
µBlock.logger.writeOne('', 'error', errorCantConnectTo.replace('{{msg}}', actualUrl));
onError({ url: url, content: '' });
µBlock.logger.writeOne({
error: errorCantConnectTo.replace('{{msg}}', actualUrl)
});
onError({ url, content: '' });
};
var onTimeout = function() {
const onTimeout = function() {
xhr.abort();
};
// https://github.com/gorhill/uBlock/issues/2526
// - Timeout only when there is no progress.
var onProgressEvent = function(ev) {
const onProgressEvent = function(ev) {
if ( ev.loaded === contentLoaded ) { return; }
contentLoaded = ev.loaded;
if ( timeoutTimer !== undefined ) {

View File

@ -1,7 +1,7 @@
/*******************************************************************************
uBlock Origin - a browser extension to block 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
@ -579,8 +579,11 @@ FilterContainer.prototype.compileGenericSelector = function(parsed, writer) {
/******************************************************************************/
FilterContainer.prototype.compileGenericHideSelector = function(parsed, writer) {
let selector = parsed.suffix;
FilterContainer.prototype.compileGenericHideSelector = function(
parsed,
writer
) {
const selector = parsed.suffix;
// For some selectors, it is mandatory to have a hostname or entity:
// ##.foo:-abp-contains(...)
@ -596,18 +599,16 @@ FilterContainer.prototype.compileGenericHideSelector = function(parsed, writer)
// ##:xpath(...)
// ##.foo:style(...)
if ( this.reNeedHostname.test(selector) ) {
µb.logger.writeOne(
'',
'error',
'Cosmetic filtering invalid generic filter: ##' + selector
);
µb.logger.writeOne({
error: 'Cosmetic filtering invalid generic filter: ##' + selector
});
return;
}
let type = selector.charCodeAt(0);
if ( type === 0x23 /* '#' */ ) {
let key = this.keyFromSelector(selector);
const key = this.keyFromSelector(selector);
if ( key === undefined ) { return; }
// Simple selector-based CSS rule: no need to test for whether the
// selector is valid, the regex took care of this. Most generic
@ -624,7 +625,7 @@ FilterContainer.prototype.compileGenericHideSelector = function(parsed, writer)
}
if ( type === 0x2E /* '.' */ ) {
let key = this.keyFromSelector(selector);
const key = this.keyFromSelector(selector);
if ( key === undefined ) { return; }
// Simple selector-based CSS rule: no need to test for whether the
// selector is valid, the regex took care of this. Most generic
@ -640,16 +641,16 @@ FilterContainer.prototype.compileGenericHideSelector = function(parsed, writer)
return;
}
let compiled = µb.staticExtFilteringEngine.compileSelector(selector);
const compiled = µb.staticExtFilteringEngine.compileSelector(selector);
if ( compiled === undefined ) { return; }
// TODO: Detect and error on procedural cosmetic filters.
// https://github.com/gorhill/uBlock/issues/909
// Anything which contains a plain id/class selector can be classified
// as a low generic cosmetic filter.
let matches = this.rePlainSelectorEx.exec(selector);
const matches = this.rePlainSelectorEx.exec(selector);
if ( matches !== null ) {
let key = matches[1] || matches[2];
const key = matches[1] || matches[2];
type = key.charCodeAt(0);
writer.push([
type === 0x23 ? 1 : 3 /* lg+ */,

282
src/js/filtering-context.js Normal file
View File

@ -0,0 +1,282 @@
/*******************************************************************************
uBlock Origin - a browser extension to block 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/uBlock
*/
'use strict';
/******************************************************************************/
µBlock.FilteringContext = function(other) {
if ( other instanceof µBlock.FilteringContext ) {
return this.fromFilteringContext(other);
}
this.tstamp = 0;
this.realm = '';
this.type = undefined;
this.url = undefined;
this.hostname = undefined;
this.domain = undefined;
this.docId = undefined;
this.docOrigin = undefined;
this.docHostname = undefined;
this.docDomain = undefined;
this.tabId = undefined;
this.tabOrigin = undefined;
this.tabHostname = undefined;
this.tabDomain = undefined;
this.filter = undefined;
};
µBlock.FilteringContext.prototype = {
fromTabId: function(tabId) {
if ( tabId !== -1 || tabId !== this.tabId ) {
const tabContext = µBlock.tabContextManager.mustLookup(tabId);
this.tabOrigin = tabContext.origin;
this.tabHostname = tabContext.rootHostname;
this.tabDomain = tabContext.rootDomain;
this.tabId = tabId;
}
return this;
},
fromWebrequestDetails: function(details) {
const tabId = details.tabId;
if ( tabId > 0 && details.type === 'main_frame' ) {
µBlock.tabContextManager.push(tabId, details.url);
}
this.fromTabId(tabId);
this.realm = '';
this.type = details.type;
this.setURL(details.url);
this.docId = details.type !== 'sub_frame'
? details.frameId
: details.parentFrameId;
if ( this.tabId > 0 ) {
if ( this.docId === 0 ) {
this.docOrigin = this.tabOrigin;
this.docHostname = this.tabHostname;
this.docDomain = this.tabDomain;
} else if ( details.documentUrl !== undefined ) {
this.setDocOriginFromURL(details.documentUrl);
} else {
const pageStore = µBlock.pageStoreFromTabId(this.docId);
const docStore = pageStore && pageStore.frames.get(this.docId);
if ( docStore ) {
this.docOrigin = undefined;
this.docHostname = docStore.pageHostname;
this.docDomain = docStore.pageDomain;
}
}
} else if ( details.documentUrl !== undefined ) {
this.setTabOriginFromURL(details.documentUrl);
this.setDocOriginFromURL(details.documentUrl);
} else {
this.setDocOrigin(this.tabOrigin);
}
this.filter = undefined;
return this;
},
fromFilteringContext: function(other) {
this.realm = other.realm;
this.type = other.type;
this.url = other.url;
this.hostname = other.hostname;
this.domain = other.domain;
this.docId = other.docId;
this.docOrigin = other.docOrigin;
this.docHostname = other.docHostname;
this.docDomain = other.docDomain;
this.tabId = other.tabId;
this.tabOrigin = other.tabOrigin;
this.tabHostname = other.tabHostname;
this.tabDomain = other.tabDomain;
this.filter = undefined;
return this;
},
duplicate: function() {
return (new µBlock.FilteringContext(this));
},
setRealm: function(a) {
this.realm = a;
return this;
},
setType: function(a) {
this.type = a;
return this;
},
setURL: function(a) {
if ( a !== this.url ) {
this.hostname = this.domain = undefined;
this.url = a;
}
return this;
},
getHostname: function() {
if ( this.hostname === undefined ) {
this.hostname = this.hostnameFromURI(this.url);
}
return this.hostname;
},
setHostname: function(a) {
if ( a !== this.hostname ) {
this.domain = undefined;
this.hostname = a;
}
return this;
},
getDomain: function() {
if ( this.domain === undefined ) {
this.domain = this.domainFromHostname(this.getHostname());
}
return this.domain;
},
setDomain: function(a) {
this.domain = a;
return this;
},
getDocOrigin: function() {
if ( this.docOrigin === undefined ) {
this.docOrigin = this.tabOrigin;
}
return this.docOrigin;
},
setDocOrigin: function(a) {
if ( a !== this.docOrigin ) {
this.docHostname = this.docDomain = undefined;
this.docOrigin = a;
}
return this;
},
setDocOriginFromURL: function(a) {
return this.setDocOrigin(this.originFromURI(a));
},
getDocHostname: function() {
if ( this.docHostname === undefined ) {
this.docHostname = this.hostnameFromURI(this.getDocOrigin());
}
return this.docHostname;
},
setDocHostname: function(a) {
if ( a !== this.docHostname ) {
this.docDomain = undefined;
this.docHostname = a;
}
return this;
},
getDocDomain: function() {
if ( this.docDomain === undefined ) {
this.docDomain = this.domainFromHostname(this.getDocHostname());
}
return this.docDomain;
},
setDocDomain: function(a) {
this.docDomain = a;
return this;
},
// The idea is to minimize the amout of work done to figure out whether
// the resource is 3rd-party to the document.
is3rdPartyToDoc: function() {
const docDomain = this.getDocDomain();
if ( this.domain !== undefined ) { return this.domain !== docDomain; }
const hostname = this.getHostname();
if ( hostname.endsWith(docDomain) === false ) { return true; }
const i = hostname.length - docDomain.length;
if ( i === 0 ) { return false; }
return hostname.charCodeAt(i - 1) !== 0x2E /* '.' */;
},
setTabId: function(a) {
this.tabId = a;
return this;
},
getTabOrigin: function() {
if ( this.tabOrigin === undefined ) {
const tabContext = µBlock.tabContextManager.mustLookup(this.tabId);
this.tabOrigin = tabContext.origin;
this.tabHostname = tabContext.rootHostname;
this.tabDomain = tabContext.rootDomain;
}
return this.tabOrigin;
},
setTabOrigin: function(a) {
if ( a !== this.tabOrigin ) {
this.tabHostname = this.tabDomain = undefined;
this.tabOrigin = a;
}
return this;
},
setTabOriginFromURL: function(a) {
return this.setTabOrigin(this.originFromURI(a));
},
getTabHostname: function() {
if ( this.tabHostname === undefined ) {
this.tabHostname = this.hostnameFromURI(this.getTabOrigin());
}
return this.tabHostname;
},
setTabHostname: function(a) {
if ( a !== this.tabHostname ) {
this.tabDomain = undefined;
this.tabHostname = a;
}
return this;
},
getTabDomain: function() {
if ( this.tabDomain === undefined ) {
this.tabDomain = this.domainFromHostname(this.getTabHostname());
}
return this.tabDomain;
},
setTabDomain: function(a) {
this.docDomain = a;
return this;
},
// The idea is to minimize the amout of work done to figure out whether
// the resource is 3rd-party to the top document.
is3rdPartyToTab: function() {
const tabDomain = this.getTabDomain();
if ( this.domain !== undefined ) { return this.domain !== tabDomain; }
const hostname = this.getHostname();
if ( hostname.endsWith(tabDomain) === false ) { return true; }
const i = hostname.length - tabDomain.length;
if ( i === 0 ) { return false; }
return hostname.charCodeAt(i - 1) !== 0x2E /* '.' */;
},
setFilter: function(a) {
this.filter = a;
return this;
},
toLogger: function() {
this.tstamp = Date.now();
if ( this.domain === undefined ) {
void this.getDomain();
}
if ( this.docDomain === undefined ) {
void this.getDocDomain();
}
if ( this.tabDomain === undefined ) {
void this.getTabDomain();
}
µBlock.logger.writeOne(this);
},
originFromURI: µBlock.URI.originFromURI,
hostnameFromURI: µBlock.URI.hostnameFromURI,
domainFromHostname: µBlock.URI.domainFromHostname,
};
µBlock.filteringContext = new µBlock.FilteringContext();

View File

@ -162,18 +162,18 @@
};
const logOne = function(details, exception, selector) {
µb.logger.writeOne(
details.tabId,
'cosmetic',
{
µBlock.filteringContext
.duplicate()
.fromTabId(details.tabId)
.setRealm('cosmetic')
.setType('dom')
.setURL(details.url)
.setDocOriginFromURL(details.url)
.setFilter({
source: 'cosmetic',
raw: (exception === 0 ? '##' : '#@#') + '^' + selector
},
'dom',
details.url,
null,
details.hostname
);
})
.toLogger();
};
const applyProceduralSelector = function(details, selector) {
@ -192,7 +192,7 @@
modified = true;
}
}
if ( modified && µb.logger.isEnabled() ) {
if ( modified && µb.logger.enabled ) {
logOne(details, 0, pselector.raw);
}
return modified;
@ -209,7 +209,7 @@
modified = true;
}
}
if ( modified && µb.logger.isEnabled() ) {
if ( modified && µb.logger.enabled ) {
logOne(details, 0, selector);
}
return modified;
@ -277,7 +277,7 @@
};
api.retrieve = function(details) {
let hostname = details.hostname;
const hostname = details.hostname;
// https://github.com/gorhill/uBlock/issues/2835
// Do not filter if the site is under an `allow` rule.
@ -288,13 +288,13 @@
return;
}
let toRemoveArray = [];
let domainHash = µb.staticExtFilteringEngine.makeHash(details.domain);
const toRemoveArray = [];
const domainHash = µb.staticExtFilteringEngine.makeHash(details.domain);
if ( domainHash !== 0 ) {
filterDB.retrieve(domainHash, hostname, toRemoveArray);
}
let entity = details.entity;
let entityHash = µb.staticExtFilteringEngine.makeHash(entity);
const entity = details.entity;
const entityHash = µb.staticExtFilteringEngine.makeHash(entity);
if ( entityHash !== 0 ) {
filterDB.retrieve(entityHash, entity, toRemoveArray);
}
@ -313,14 +313,14 @@
return toRemoveArray;
}
let toRemoveMap = new Map();
for ( let entry of toRemoveArray ) {
const toRemoveMap = new Map();
for ( const entry of toRemoveArray ) {
toRemoveMap.set(entry.selector, entry);
}
for ( let entry of notToRemoveArray ) {
for ( const entry of notToRemoveArray ) {
if ( toRemoveMap.has(entry.selector) === false ) { continue; }
toRemoveMap.delete(entry.selector);
if ( µb.logger.isEnabled() === false ) { continue; }
if ( µb.logger.enabled === false ) { continue; }
let selector = entry.selector;
if ( entry.type === 65 ) {
selector = JSON.parse(selector).raw;

View File

@ -559,16 +559,16 @@ var onMouseOver = (function() {
/******************************************************************************/
var currentTabId = function() {
const currentTabId = function() {
if ( showdomButton.classList.contains('active') === false ) { return 0; }
return logger.tabIdFromPageSelector();
};
/******************************************************************************/
var injectInspector = function() {
var tabId = currentTabId();
if ( tabId === 0 ) { return; }
const injectInspector = function() {
const tabId = currentTabId();
if ( tabId <= 0 ) { return; }
inspectedTabId = tabId;
messaging.send('loggerUI', {
what: 'scriptlet',
@ -592,7 +592,11 @@ var shutdownInspector = function() {
/******************************************************************************/
var onTabIdChanged = function() {
if ( inspectedTabId !== currentTabId() ) {
const tabId = currentTabId();
if ( tabId <= 0 ) {
return toggleOff();
}
if ( inspectedTabId !== tabId ) {
shutdownInspector();
injectInspector();
}
@ -632,7 +636,8 @@ var revert = function() {
/******************************************************************************/
var toggleOn = function() {
const toggleOn = function() {
uDom.nodeFromId('inspectors').classList.add('dom');
window.addEventListener('beforeunload', toggleOff);
document.addEventListener('tabIdChanged', onTabIdChanged);
domTree.addEventListener('click', onClicked, true);
@ -647,7 +652,9 @@ var toggleOn = function() {
/******************************************************************************/
var toggleOff = function() {
const toggleOff = function() {
showdomButton.classList.remove('active');
uDom.nodeFromId('inspectors').classList.remove('dom');
shutdownInspector();
window.removeEventListener('beforeunload', toggleOff);
document.removeEventListener('tabIdChanged', onTabIdChanged);
@ -663,7 +670,7 @@ var toggleOff = function() {
/******************************************************************************/
var toggle = function() {
const toggle = function() {
if ( showdomButton.classList.toggle('active') ) {
toggleOn();
} else {

View File

@ -29,16 +29,17 @@
/******************************************************************************/
var logger = self.logger = {
const messaging = vAPI.messaging;
const logger = self.logger = {
ownerId: Date.now()
};
var messaging = vAPI.messaging;
var activeTabId;
let activeTabId;
/******************************************************************************/
var removeAllChildren = logger.removeAllChildren = function(node) {
const removeAllChildren = logger.removeAllChildren = function(node) {
while ( node.firstChild ) {
node.removeChild(node.firstChild);
}
@ -46,18 +47,19 @@ var removeAllChildren = logger.removeAllChildren = function(node) {
/******************************************************************************/
var tabIdFromClassName = function(className) {
var matches = className.match(/\btab_([^ ]+)\b/);
const tabIdFromClassName = function(className) {
const matches = className.match(/\btab_([^ ]+)\b/);
if ( matches === null ) { return 0; }
if ( matches[1] === 'bts' ) { return -1; }
return parseInt(matches[1], 10);
};
var tabIdFromPageSelector = logger.tabIdFromPageSelector = function() {
var tabClass = uDom.nodeFromId('pageSelector').value;
const tabIdFromPageSelector = logger.tabIdFromPageSelector = function() {
const tabClass = uDom.nodeFromId('pageSelector').value;
if ( tabClass === 'tab_active' && activeTabId !== undefined ) {
return activeTabId;
}
if ( tabClass === 'tab_bts' ) { return -1; }
return /^tab_\d+$/.test(tabClass) ? parseInt(tabClass.slice(4), 10) : 0;
};
@ -78,12 +80,11 @@ var tabIdFromPageSelector = logger.tabIdFromPageSelector = function() {
var tbody = document.querySelector('#netInspector tbody');
var trJunkyard = [];
var tdJunkyard = [];
var firstVarDataCol = 2; // currently, column 2 (0-based index)
var lastVarDataIndex = 4; // currently, d0-d3
var firstVarDataCol = 1;
var lastVarDataIndex = 6;
var maxEntries = 5000;
var allTabIds = new Map();
var allTabIdsToken;
var hiddenTemplate = document.querySelector('#hiddenTemplate > span');
var reRFC3986 = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#]*)(\?[^#]*)?(#.*)?/;
var netFilteringDialog = uDom.nodeFromId('netFilteringDialog');
@ -159,14 +160,15 @@ var renderedURLTemplate = document.querySelector('#renderedURLTemplate > span');
/******************************************************************************/
var createCellAt = function(tr, index) {
var td = tr.cells[index];
var mustAppend = !td;
const createCellAt = function(tr, index) {
let td = tr.cells[index];
const mustAppend = !td;
if ( mustAppend ) {
td = tdJunkyard.pop();
}
if ( td ) {
td.removeAttribute('colspan');
td.removeAttribute('title');
td.textContent = '';
} else {
td = document.createElement('td');
@ -180,24 +182,25 @@ var createCellAt = function(tr, index) {
/******************************************************************************/
var createRow = function(layout) {
var tr = trJunkyard.pop();
let tr = trJunkyard.pop();
if ( tr ) {
tr.className = '';
tr.removeAttribute('data-hn-page');
tr.removeAttribute('data-hn-frame');
tr.removeAttribute('data-tabhn');
tr.removeAttribute('data-dochn');
tr.removeAttribute('data-filter');
tr.removeAttribute('data-tabid');
tr.removeAttribute('title');
} else {
tr = document.createElement('tr');
}
for ( var index = 0; index < firstVarDataCol; index++ ) {
let index = 0;
for ( ; index < firstVarDataCol; index++ ) {
createCellAt(tr, index);
}
var i = 1, span = 1, td;
let i = 1, span = 1, td;
for (;;) {
td = createCellAt(tr, index);
if ( i === lastVarDataIndex ) {
break;
}
if ( i === lastVarDataIndex ) { break; }
if ( layout.charAt(i) !== '1' ) {
span += 1;
} else {
@ -221,24 +224,15 @@ var createRow = function(layout) {
/******************************************************************************/
var createHiddenTextNode = function(text) {
var node = hiddenTemplate.cloneNode(true);
node.textContent = text;
return node;
};
/******************************************************************************/
var padTo2 = function(v) {
return v < 10 ? '0' + v : v;
};
/******************************************************************************/
var createGap = function(tabId, url) {
var tr = createRow('1');
tr.classList.add('tab');
tr.classList.add('canMtx');
const createGap = function(tabId, url) {
const tr = createRow('1');
tr.setAttribute('data-tabid', tabId);
tr.classList.add('tab_' + tabId);
tr.classList.add('maindoc');
tr.cells[firstVarDataCol].textContent = url;
@ -247,36 +241,30 @@ var createGap = function(tabId, url) {
/******************************************************************************/
var renderNetLogEntry = function(tr, entry) {
var trcl = tr.classList;
var filter = entry.d0 || undefined;
var type = entry.d1;
var url = entry.d2;
var td;
trcl.add('canMtx');
var renderNetLogEntry = function(tr, details) {
const trcl = tr.classList;
const type = details.type;
const url = details.url;
let td;
// 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 ( type === 'main_frame' ) {
createGap(entry.tab, url);
createGap(details.tabId, url);
}
// Contexts
if ( entry.d3 ) {
tr.setAttribute('data-hn-page', entry.d3);
}
if ( entry.d4 ) {
tr.setAttribute('data-hn-frame', entry.d4);
tr.classList.add('cat_' + details.realm);
let filter = details.filter || undefined;
let filteringType;
if ( filter !== undefined ) {
if ( typeof filter.source === 'string' ) {
filteringType = filter.source;
trcl.add(filteringType);
}
}
var filteringType;
if ( filter !== undefined && typeof filter.source === 'string' ) {
filteringType = filter.source;
trcl.add(filteringType);
}
td = tr.cells[2];
td = tr.cells[1];
if ( filter !== undefined ) {
if ( filteringType === 'static' ) {
td.textContent = filter.raw;
@ -290,7 +278,7 @@ var renderNetLogEntry = function(tr, entry) {
}
}
td = tr.cells[3];
td = tr.cells[2];
if ( filter !== undefined ) {
if ( filter.result === 1 ) {
trcl.add('blocked');
@ -301,65 +289,86 @@ var renderNetLogEntry = function(tr, entry) {
} else if ( filter.result === 3 ) {
trcl.add('nooped');
td.textContent = '**';
} else if ( filter.source === 'redirect' ) {
} else if ( filteringType === 'redirect' ) {
trcl.add('redirect');
td.textContent = '<<';
}
}
tr.cells[4].textContent = (prettyRequestTypes[type] || type);
if ( details.tabHostname ) {
tr.setAttribute('data-tabhn', details.tabHostname);
}
if ( details.docHostname ) {
tr.setAttribute('data-dochn', details.docHostname);
tr.cells[3].textContent = details.docHostname;
}
var re = null;
// Partyness
if ( details.realm === 'net' && details.domain !== undefined ) {
td = tr.cells[4];
let text = '';
if ( details.tabDomain !== undefined ) {
text += details.domain === details.tabDomain ? '1' : '3';
} else {
text += '?';
}
if ( details.docDomain !== details.tabDomain ) {
text += ',';
if ( details.docDomain !== undefined ) {
text += details.domain === details.docDomain ? '1' : '3';
} else {
text += '?';
}
}
td.textContent = text;
text = details.domain;
if ( details.docDomain ) {
text = details.docDomain + ' \u21d2 ' + text;
}
if ( details.tabDomain && details.tabDomain !== details.docDomain ) {
text = details.tabDomain + ' \u21d2 ' + text;
}
td.setAttribute('title', text);
}
tr.cells[5].textContent = (prettyRequestTypes[type] || type);
let re = null;
if ( filteringType === 'static' ) {
re = new RegExp(filter.regex, 'gi');
} else if ( filteringType === 'dynamicUrl' ) {
re = regexFromURLFilteringResult(filter.rule.join(' '));
}
tr.cells[5].appendChild(nodeFromURL(url, re));
tr.cells[6].appendChild(nodeFromURL(url, re));
};
/******************************************************************************/
var renderLogEntry = function(entry) {
var tr;
var fvdc = firstVarDataCol;
var renderLogEntry = function(details) {
const fvdc = firstVarDataCol;
let tr;
switch ( entry.cat ) {
case 'error':
case 'info':
if ( details.error !== undefined ) {
tr = createRow('1');
tr.cells[fvdc].textContent = entry.d0;
break;
case 'cosmetic':
case 'net':
case 'redirect':
tr = createRow('1111');
renderNetLogEntry(tr, entry);
break;
default:
tr.cells[fvdc].textContent = details.error;
} else if ( details.url !== undefined ) {
tr = createRow('111111');
renderNetLogEntry(tr, details);
} else {
tr = createRow('1');
tr.cells[fvdc].textContent = entry.d0;
break;
tr.cells[fvdc].textContent = '???';
}
// Fields common to all rows.
var time = logDate;
time.setTime(entry.tstamp - logDateTimezoneOffset);
const time = logDate;
time.setTime(details.tstamp - logDateTimezoneOffset);
tr.cells[0].textContent = padTo2(time.getUTCHours()) + ':' +
padTo2(time.getUTCMinutes()) + ':' +
padTo2(time.getSeconds());
if ( entry.tab ) {
tr.classList.add('tab');
tr.classList.add(classNameFromTabId(entry.tab));
if ( entry.tab < 0 ) {
tr.cells[1].appendChild(createHiddenTextNode('bts'));
}
}
if ( entry.cat !== '' ) {
tr.classList.add('cat_' + entry.cat);
if ( details.tabId ) {
tr.setAttribute('data-tabid', details.tabId);
tr.classList.add(classNameFromTabId(details.tabId));
}
rowFilterer.filterOne(tr, true);
@ -368,29 +377,29 @@ var renderLogEntry = function(entry) {
};
// Reuse date objects.
var logDate = new Date(),
logDateTimezoneOffset = logDate.getTimezoneOffset() * 60000;
const logDate = new Date();
const logDateTimezoneOffset = logDate.getTimezoneOffset() * 60000;
/******************************************************************************/
var renderLogEntries = function(response) {
const renderLogEntries = function(response) {
document.body.classList.toggle('colorBlind', response.colorBlind);
let entries = response.entries;
const entries = response.entries;
if ( entries.length === 0 ) { return; }
// Preserve scroll position
let height = tbody.offsetHeight;
const height = tbody.offsetHeight;
let tabIds = allTabIds;
for ( let i = 0, n = entries.length; i < n; i++ ) {
let entry = entries[i];
let tr = renderLogEntry(entries[i]);
const tabIds = allTabIds;
for ( const entry of entries ) {
const details = JSON.parse(entry.details);
const tr = renderLogEntry(details);
// https://github.com/gorhill/uBlock/issues/1613#issuecomment-217637122
// Unlikely, but it may happen: mark as void if associated tab no
// longer exist.
if ( entry.tab && tabIds.has(entry.tab) === false ) {
tr.classList.remove('canMtx');
if ( details.tabId && tabIds.has(details.tabId) === false ) {
tr.classList.add('void');
}
}
@ -400,9 +409,9 @@ var renderLogEntries = function(response) {
truncateLog(maxEntries);
// Follow waterfall if not observing top of waterfall.
let yDelta = tbody.offsetHeight - height;
const yDelta = tbody.offsetHeight - height;
if ( yDelta === 0 ) { return; }
let container = uDom.nodeFromSelector('#netInspector .vscrollable');
const container = uDom.nodeFromSelector('#netInspector .vscrollable');
if ( container.scrollTop !== 0 ) {
container.scrollTop += yDelta;
}
@ -428,21 +437,20 @@ let updateCurrentTabTitle = (function() {
/******************************************************************************/
var synchronizeTabIds = function(newTabIds) {
var select = uDom.nodeFromId('pageSelector');
var selectValue = select.value;
var oldTabIds = allTabIds;
var autoDeleteVoidRows = selectValue === 'tab_active';
var rowVoided = false;
for ( var tabId of oldTabIds.keys() ) {
const synchronizeTabIds = function(newTabIds) {
const select = uDom.nodeFromId('pageSelector');
const selectValue = select.value;
const oldTabIds = allTabIds;
const autoDeleteVoidRows = selectValue === 'tab_active';
let rowVoided = false;
for ( const tabId of oldTabIds.keys() ) {
if ( newTabIds.has(tabId) ) { continue; }
// Mark or remove voided rows
var trs = uDom('.tab_' + tabId);
const trs = uDom('.tab_' + tabId);
if ( autoDeleteVoidRows ) {
toJunkyard(trs);
} else {
trs.removeClass('canMtx');
trs.addClass('void');
rowVoided = true;
}
// Remove popup if it is currently bound to a removed tab.
@ -451,14 +459,14 @@ var synchronizeTabIds = function(newTabIds) {
}
}
var tabIds = Array.from(newTabIds.keys()).sort(function(a, b) {
const tabIds = Array.from(newTabIds.keys()).sort(function(a, b) {
return newTabIds.get(a).localeCompare(newTabIds.get(b));
});
var option;
for ( var i = 0, j = 3; i < tabIds.length; i++ ) {
tabId = tabIds[i];
let j = 3;
for ( let i = 0; i < tabIds.length; i++ ) {
const tabId = tabIds[i];
if ( tabId < 0 ) { continue; }
option = select.options[j];
let option = select.options[j];
if ( !option ) {
option = document.createElement('option');
select.appendChild(option);
@ -508,7 +516,7 @@ var truncateLog = function(size) {
/******************************************************************************/
var onLogBufferRead = function(response) {
const onLogBufferRead = function(response) {
if ( !response || response.unavailable ) {
readLogBufferAsync();
return;
@ -547,7 +555,7 @@ var onLogBufferRead = function(response) {
if ( rowVoided ) {
uDom('#clean').toggleClass(
'disabled',
tbody.querySelector('#netInspector tr.tab:not(.canMtx)') === null
tbody.querySelector('#netInspector tr[data-tabid].void') === null
);
}
@ -566,20 +574,20 @@ var onLogBufferRead = function(response) {
// automatically. If called after init time, this will be messy, and this would
// require a bit more code to ensure no multi time out events.
var readLogBuffer = function() {
const readLogBuffer = function() {
if ( logger.ownerId === undefined ) { return; }
vAPI.messaging.send(
'loggerUI',
{
what: 'readAll',
ownerId: logger.ownerId,
tabIdsToken: allTabIdsToken
tabIdsToken: allTabIdsToken,
},
onLogBufferRead
);
};
var readLogBufferAsync = function() {
const readLogBufferAsync = function() {
if ( logger.ownerId === undefined ) { return; }
vAPI.setTimeout(readLogBuffer, 1200);
};
@ -646,10 +654,14 @@ let pageSelectorFromURLHash = (function() {
select.selectedIndex = option.index;
select.value = option.value;
uDom('.needtab').toggleClass(
uDom('.needdom').toggleClass(
'disabled',
tabClass === '' || tabClass === 'tab_bts'
);
uDom('.needscope').toggleClass(
'disabled',
tabClass === ''
);
};
})();
@ -657,7 +669,7 @@ let pageSelectorFromURLHash = (function() {
var reloadTab = function(ev) {
var tabId = tabIdFromPageSelector();
if ( tabId === 0 ) { return; }
if ( tabId <= 0 ) { return; }
messaging.send('loggerUI', {
what: 'reloadTab',
tabId: tabId,
@ -1227,10 +1239,10 @@ var netFilteringManager = (function() {
dialog = netFilteringDialog.querySelector('.dialog');
targetRow = ev.target.parentElement;
targetTabId = tabIdFromClassName(targetRow.className);
targetType = targetRow.cells[4].textContent.trim() || '';
targetURLs = createTargetURLs(targetRow.cells[5].textContent);
targetPageHostname = targetRow.getAttribute('data-hn-page') || '';
targetFrameHostname = targetRow.getAttribute('data-hn-frame') || '';
targetType = targetRow.cells[5].textContent.trim() || '';
targetURLs = createTargetURLs(targetRow.cells[6].textContent);
targetPageHostname = targetRow.getAttribute('data-tabhn') || '';
targetFrameHostname = targetRow.getAttribute('data-dochn') || '';
// We need the root domain names for best user experience.
messaging.send(
@ -1345,7 +1357,7 @@ var reverseLookupManager = (function() {
let toggleOn = function(ev) {
let row = ev.target.parentElement;
rawFilter = row.cells[2].textContent;
rawFilter = row.cells[1].textContent;
if ( rawFilter === '' ) { return; }
if ( row.classList.contains('cat_net') ) {
@ -1363,7 +1375,7 @@ var reverseLookupManager = (function() {
'loggerUI',
{
what: 'listsFromCosmeticFilter',
url: row.cells[5].textContent,
url: row.cells[6].textContent,
rawFilter: rawFilter,
},
reverseLookupDone
@ -1385,10 +1397,10 @@ var reverseLookupManager = (function() {
/******************************************************************************/
/******************************************************************************/
var rowFilterer = (function() {
var filters = [];
const rowFilterer = (function() {
let filters = [];
var parseInput = function() {
const parseInput = function() {
filters = [];
var rawPart, hardBeg, hardEnd;
@ -1438,33 +1450,30 @@ var rowFilterer = (function() {
}
};
var filterOne = function(tr, clean) {
var ff = filters;
var fcount = ff.length;
const filterOne = function(tr, clean) {
const ff = filters;
const fcount = ff.length;
if ( fcount === 0 && clean === true ) {
return;
}
// do not filter out doc boundaries, they help separate important
// section of log.
var cl = tr.classList;
if ( cl.contains('maindoc') ) {
return;
}
const cl = tr.classList;
if ( cl.contains('maindoc') ) { return; }
if ( fcount === 0 ) {
cl.remove('f');
return;
}
var cc = tr.cells;
var ccount = cc.length;
var hit, j, f;
const cc = tr.cells;
const ccount = cc.length;
// each filter expression must hit (implicit and-op)
// if...
// positive filter expression = there must one hit on any field
// negative filter expression = there must be no hit on all fields
for ( var i = 0; i < fcount; i++ ) {
f = ff[i];
hit = !f.r;
for ( j = 0; j < ccount; j++ ) {
for ( let i = 0; i < fcount; i++ ) {
let f = ff[i];
let hit = !f.r;
for ( let j = 1; j < ccount; j++ ) {
if ( f.re.test(cc[j].textContent) ) {
hit = f.r;
break;
@ -1478,7 +1487,7 @@ var rowFilterer = (function() {
cl.remove('f');
};
var filterAll = function() {
const filterAll = function() {
// Special case: no filter
if ( filters.length === 0 ) {
uDom('#netInspector tr').removeClass('f');
@ -1492,7 +1501,7 @@ var rowFilterer = (function() {
}
};
var onFilterChangedAsync = (function() {
const onFilterChangedAsync = (function() {
var timer = null;
var commit = function() {
timer = null;
@ -1507,7 +1516,7 @@ var rowFilterer = (function() {
};
})();
var onFilterButton = function() {
const onFilterButton = function() {
uDom.nodeFromId('netInspector').classList.toggle('f');
};
@ -1559,14 +1568,14 @@ var clearBuffer = function() {
);
uDom.nodeFromId('clean').classList.toggle(
'disabled',
tbody.querySelector('#netInspector tr.tab:not(.canMtx)') === null
tbody.querySelector('#netInspector tr[data-tabid].void') === null
);
};
/******************************************************************************/
var cleanBuffer = function() {
var rows = uDom('#netInspector tr.tab:not(.canMtx)').remove();
var rows = uDom('#netInspector tr[data-tabid].void').remove();
var i = rows.length;
while ( i-- ) {
trJunkyard.push(rows.nodeAt(i));
@ -1587,44 +1596,23 @@ var toggleVCompactRow = function(ev) {
/******************************************************************************/
var toggleInspectors = function() {
uDom.nodeFromId('inspectors').classList.toggle('dom');
};
/******************************************************************************/
var popupManager = (function() {
var realTabId = null;
var localTabId = null;
var container = null;
var popup = null;
var popupObserver = null;
var style = null;
var styleTemplate = [
'#netInspector tr:not(.tab_{{tabId}}) {',
'cursor: not-allowed;',
'opacity: 0.2;',
'}'
].join('\n');
let realTabId = 0;
let popup = null;
let popupObserver = null;
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');
const resizePopup = function() {
if ( popup === null ) { return; }
const popupBody = popup.contentWindow.document.body;
if ( popupBody.clientWidth !== 0 && popup.clientWidth !== popupBody.clientWidth ) {
popup.style.setProperty('width', popupBody.clientWidth + 'px');
}
if ( popupBody.clientHeight !== 0 && popup.clientHeight !== popupBody.clientHeight ) {
popup.style.setProperty('height', popupBody.clientHeight + 'px');
}
};
var toggleSize = function() {
container.classList.toggle('hide');
};
var onLoad = function() {
const onLoad = function() {
resizePopup();
popupObserver.observe(popup.contentDocument.body, {
subtree: true,
@ -1632,61 +1620,55 @@ var popupManager = (function() {
});
};
var toggleOn = function(td) {
var tr = td.parentNode;
realTabId = localTabId = tabIdFromClassName(tr.className);
if ( realTabId === 0 ) { return; }
container = uDom.nodeFromId('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(resizePopup);
container.appendChild(popup);
style = uDom.nodeFromId('popupFilterer');
style.textContent = styleTemplate.replace('{{tabId}}', localTabId);
var parent = uDom.nodeFromId('netInspector');
var rect = parent.getBoundingClientRect();
container.style.setProperty('top', rect.top + 'px');
container.style.setProperty('right', (rect.right - parent.clientWidth) + 'px');
parent.classList.add('popupOn');
const setTabId = function(tabId) {
if ( popup === null ) { return; }
popup.setAttribute('src', 'popup.html?tabId=' + tabId);
};
var toggleOff = function() {
uDom.nodeFromId('netInspector').classList.remove('popupOn');
const onTabIdChanged = function() {
const tabId = tabIdFromPageSelector();
if ( tabId === 0 ) { return toggleOff(); }
realTabId = tabId;
setTabId(realTabId);
};
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');
const toggleOn = function() {
const tabId = tabIdFromPageSelector();
if ( tabId === 0 ) { return; }
realTabId = tabId;
popup = uDom.nodeFromId('popupContainer');
popup.addEventListener('load', onLoad);
popupObserver = new MutationObserver(resizePopup);
const parent = uDom.nodeFromId('inspectors');
const rect = parent.getBoundingClientRect();
popup.style.setProperty('right', (rect.right - parent.clientWidth) + 'px');
parent.classList.add('popupOn');
document.addEventListener('tabIdChanged', onTabIdChanged);
setTabId(realTabId);
};
const toggleOff = function() {
document.removeEventListener('tabIdChanged', onTabIdChanged);
uDom.nodeFromId('inspectors').classList.remove('popupOn');
popup.removeEventListener('load', onLoad);
popupObserver.disconnect();
popupObserver = null;
popup.setAttribute('src', '');
container.removeChild(popup);
popup = null;
style.textContent = '';
style = null;
container = null;
realTabId = null;
realTabId = 0;
};
var exports = {
toggleOn: function(ev) {
if ( realTabId === null ) {
toggleOn(ev.target);
}
const exports = {
toggleOn: function() {
void (realTabId === 0 ? toggleOn() : toggleOff());
},
toggleOff: function() {
if ( realTabId !== null ) {
if ( realTabId !== 0 ) {
toggleOff();
}
}
@ -1701,14 +1683,14 @@ var popupManager = (function() {
/******************************************************************************/
var grabView = function() {
const grabView = function() {
if ( logger.ownerId === undefined ) {
logger.ownerId = Date.now();
}
readLogBufferAsync();
};
var releaseView = function() {
const releaseView = function() {
if ( logger.ownerId === undefined ) { return; }
vAPI.messaging.send(
'loggerUI',
@ -1728,16 +1710,15 @@ readLogBuffer();
uDom('#pageSelector').on('change', pageSelectorChanged);
uDom('#refresh').on('click', reloadTab);
uDom('#showdom').on('click', toggleInspectors);
uDom('#showpopup').on('click', popupManager.toggleOn);
uDom('#netInspector .vCompactToggler').on('click', toggleVCompactView);
uDom('#clean').on('click', cleanBuffer);
uDom('#clear').on('click', clearBuffer);
uDom('#maxEntries').on('change', onMaxEntriesChanged);
uDom('#netInspector table').on('click', 'tr > td:nth-of-type(1)', toggleVCompactRow);
uDom('#netInspector table').on('click', 'tr.canMtx > td:nth-of-type(2)', popupManager.toggleOn);
uDom('#netInspector').on('click', 'tr.canLookup > td:nth-of-type(3)', reverseLookupManager.toggleOn);
uDom('#netInspector').on('click', 'tr.cat_net > td:nth-of-type(4)', netFilteringManager.toggleOn);
uDom('#netInspector').on('click', 'tr.canLookup > td:nth-of-type(2)', reverseLookupManager.toggleOn);
uDom('#netInspector').on('click', 'tr.cat_net > td:nth-of-type(3)', netFilteringManager.toggleOn);
// https://github.com/gorhill/uBlock/issues/507
// Ensure tab selector is in sync with URL hash

View File

@ -1,7 +1,7 @@
/*******************************************************************************
uBlock - a browser extension to block requests.
Copyright (C) 2015-2017 Raymond Hill
uBlock Origin - a browser extension to block 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
@ -26,34 +26,28 @@
µBlock.logger = (function() {
var LogEntry = function(args) {
this.init(args);
const LogEntry = function(details) {
this.init(details);
};
LogEntry.prototype.init = function(args) {
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.d4 = args[6];
LogEntry.prototype.init = function(details) {
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;
const logBufferObsoleteAfter = 30 * 1000;
var janitor = function() {
const janitor = ( ) => {
if (
buffer !== null &&
lastReadTime < (Date.now() - logBufferObsoleteAfter)
) {
api.enabled = false;
buffer = null;
writePtr = 0;
api.ownerId = undefined;
@ -64,31 +58,30 @@
}
};
var api = {
const 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;
if ( buffer === null ) {
this.enabled = true;
buffer = [];
vAPI.setTimeout(janitor, logBufferObsoleteAfter);
}
var out = buffer.slice(0, writePtr);
const out = buffer.slice(0, writePtr);
writePtr = 0;
lastReadTime = Date.now();
return out;
},
isEnabled: function() {
return buffer !== null;
}
};
return api;

View File

@ -527,9 +527,8 @@ var onMessage = function(request, sender, callback) {
case 'shouldRenderNoscriptTags':
if ( pageStore === null ) { break; }
let tabContext = µb.tabContextManager.lookup(tabId);
if ( tabContext === null ) { break; }
if ( pageStore.filterScripting(tabContext.rootHostname, undefined) ) {
const fctxt = µBlock.filteringContext.fromTabId(tabId);
if ( pageStore.filterScripting(fctxt, undefined) ) {
vAPI.tabs.injectScript(
tabId,
{
@ -561,7 +560,10 @@ var onMessage = function(request, sender, callback) {
request.domain = µb.URI.domainFromHostname(request.hostname);
request.entity = µb.URI.entityFromDomain(request.domain);
response.specificCosmeticFilters =
µb.cosmeticFilteringEngine.retrieveSpecificSelectors(request, response);
µb.cosmeticFilteringEngine.retrieveSpecificSelectors(
request,
response
);
if ( µb.canInjectScriptletsNow === false ) {
response.scriptlets = µb.scriptletFilteringEngine.retrieve(request);
}
@ -1319,25 +1321,20 @@ var domSurveyFinalReport = function(tabId) {
/******************************************************************************/
var logCosmeticFilters = function(tabId, details) {
if ( µb.logger.isEnabled() === false ) {
return;
}
const logCosmeticFilters = function(tabId, details) {
if ( µb.logger.enabled === false ) { return; }
var selectors = details.matchedSelectors;
selectors.sort();
for ( var i = 0; i < selectors.length; i++ ) {
µb.logger.writeOne(
tabId,
'cosmetic',
{ source: 'cosmetic', raw: '##' + selectors[i] },
'dom',
details.frameURL,
null,
details.frameHostname
);
const filter = { source: 'cosmetic', raw: '' };
const fctxt = µb.filteringContext.duplicate();
fctxt.fromTabId(tabId)
.setRealm('cosmetic')
.setType('dom')
.setURL(details.frameURL)
.setDocOriginFromURL(details.frameURL)
.setFilter(filter);
for ( const selector of details.matchedSelectors.sort() ) {
filter.raw = '##' + selector;
fctxt.toLogger();
}
};

View File

@ -37,18 +37,18 @@ To create a log of net requests
/******************************************************************************/
var µb = µBlock;
const µb = µBlock;
/******************************************************************************/
/******************************************************************************/
// To mitigate memory churning
var netFilteringCacheJunkyard = [],
netFilteringCacheJunkyardMax = 10;
const netFilteringCacheJunkyard = [];
const netFilteringCacheJunkyardMax = 10;
/******************************************************************************/
var NetFilteringResultCache = function() {
const NetFilteringResultCache = function() {
this.boundPruneAsyncCallback = this.pruneAsyncCallback.bind(this);
this.init();
};
@ -60,7 +60,7 @@ NetFilteringResultCache.prototype.shelfLife = 15 * 1000;
/******************************************************************************/
NetFilteringResultCache.factory = function() {
var entry = netFilteringCacheJunkyard.pop();
let entry = netFilteringCacheJunkyard.pop();
if ( entry === undefined ) {
entry = new NetFilteringResultCache();
} else {
@ -90,31 +90,35 @@ NetFilteringResultCache.prototype.dispose = function() {
/******************************************************************************/
NetFilteringResultCache.prototype.rememberResult = function(context, result, logData) {
NetFilteringResultCache.prototype.rememberResult = function(
fctxt,
result,
logData
) {
if ( this.results.size === 0 ) {
this.pruneAsync();
}
var key = context.pageHostname + ' ' + context.requestType + ' ' + context.requestURL;
const key = fctxt.getDocHostname() + ' ' + fctxt.type + ' ' + fctxt.url;
this.results.set(key, {
result: result,
logData: logData,
tstamp: Date.now()
});
if ( result !== 1 ) { return; }
var now = Date.now();
const now = Date.now();
this.blocked.set(key, now);
this.hash = now;
};
/******************************************************************************/
NetFilteringResultCache.prototype.rememberBlock = function(details) {
NetFilteringResultCache.prototype.rememberBlock = function(fctxt) {
if ( this.blocked.size === 0 ) {
this.pruneAsync();
}
var now = Date.now();
const now = Date.now();
this.blocked.set(
details.pageHostname + ' ' + details.requestType + ' ' + details.requestURL,
fctxt.getDocHostname() + ' ' + fctxt.type + ' ' + fctxt.url,
now
);
this.hash = now;
@ -162,21 +166,20 @@ NetFilteringResultCache.prototype.pruneAsyncCallback = function() {
/******************************************************************************/
NetFilteringResultCache.prototype.lookupResult = function(context) {
NetFilteringResultCache.prototype.lookupResult = function(fctxt) {
return this.results.get(
context.pageHostname + ' ' +
context.requestType + ' ' +
context.requestURL
fctxt.getDocHostname() + ' ' +
fctxt.type + ' ' +
fctxt.url
);
};
/******************************************************************************/
NetFilteringResultCache.prototype.lookupAllBlocked = function(hostname) {
var result = [],
pos;
for ( var entry of this.blocked ) {
pos = entry[0].indexOf(' ');
const result = [];
for ( const entry of this.blocked ) {
const pos = entry[0].indexOf(' ');
if ( entry[0].slice(0, pos) === hostname ) {
result[result.length] = entry[0].slice(pos + 1);
}
@ -193,19 +196,19 @@ NetFilteringResultCache.prototype.lookupAllBlocked = function(hostname) {
// refactoring.
// To mitigate memory churning
var frameStoreJunkyard = [];
var frameStoreJunkyardMax = 50;
const frameStoreJunkyard = [];
const frameStoreJunkyardMax = 50;
/******************************************************************************/
var FrameStore = function(frameURL) {
const FrameStore = function(frameURL) {
this.init(frameURL);
};
/******************************************************************************/
FrameStore.factory = function(frameURL) {
var entry = frameStoreJunkyard.pop();
const entry = frameStoreJunkyard.pop();
if ( entry === undefined ) {
return new FrameStore(frameURL);
}
@ -215,7 +218,7 @@ FrameStore.factory = function(frameURL) {
/******************************************************************************/
FrameStore.prototype.init = function(frameURL) {
var µburi = µb.URI;
const µburi = µb.URI;
this.pageHostname = µburi.hostnameFromURI(frameURL);
this.pageDomain = µburi.domainFromHostname(this.pageHostname) || this.pageHostname;
return this;
@ -235,12 +238,12 @@ FrameStore.prototype.dispose = function() {
/******************************************************************************/
// To mitigate memory churning
var pageStoreJunkyard = [];
var pageStoreJunkyardMax = 10;
const pageStoreJunkyard = [];
const pageStoreJunkyardMax = 10;
/******************************************************************************/
var PageStore = function(tabId, context) {
const PageStore = function(tabId, context) {
this.init(tabId, context);
this.journal = [];
this.journalTimer = null;
@ -251,7 +254,7 @@ var PageStore = function(tabId, context) {
/******************************************************************************/
PageStore.factory = function(tabId, context) {
var entry = pageStoreJunkyard.pop();
let entry = pageStoreJunkyard.pop();
if ( entry === undefined ) {
entry = new PageStore(tabId, context);
} else {
@ -297,6 +300,18 @@ PageStore.prototype.init = function(tabId, context) {
this.netFilteringCache = NetFilteringResultCache.factory();
this.internalRedirectionCount = 0;
// The current filtering context is cloned because:
// - We may be called with or without the current context having been
// initialized.
// - If it has been initialized, we do not want to change the state
// of the current context.
const fctxt = µb.logger.enabled
? µBlock.filteringContext
.duplicate()
.fromTabId(tabId)
.setURL(tabContext.rawURL)
: undefined;
// https://github.com/uBlockOrigin/uBlock-issues/issues/314
const masterSwitch = tabContext.getNetFilteringSwitch();
@ -307,18 +322,13 @@ PageStore.prototype.init = function(tabId, context) {
if (
masterSwitch &&
this.noCosmeticFiltering &&
µb.logger.isEnabled() &&
µb.logger.enabled &&
context === 'tabCommitted'
) {
µb.logger.writeOne(
tabId,
'cosmetic',
µb.sessionSwitches.toLogData(),
'dom',
tabContext.rawURL,
this.tabHostname,
this.tabHostname
);
fctxt.setRealm('cosmetic')
.setType('dom')
.setFilter(µb.sessionSwitches.toLogData())
.toLogger();
}
// Support `generichide` filter option.
@ -332,18 +342,13 @@ PageStore.prototype.init = function(tabId, context) {
this.noGenericCosmeticFiltering = result === 2;
if (
result !== 0 &&
µb.logger.isEnabled() &&
µb.logger.enabled &&
context === 'tabCommitted'
) {
µb.logger.writeOne(
tabId,
'net',
µb.staticNetFilteringEngine.toLogData(),
'generichide',
tabContext.rawURL,
this.tabHostname,
this.tabHostname
);
fctxt.setRealm('net')
.setType('generichide')
.setFilter(µb.staticNetFilteringEngine.toLogData())
.toLogger();
}
}
}
@ -357,7 +362,7 @@ PageStore.prototype.reuse = function(context) {
// When force refreshing a page, the page store data needs to be reset.
// If the hostname changes, we can't merely just update the context.
var tabContext = µb.tabContextManager.mustLookup(this.tabId);
const tabContext = µb.tabContextManager.mustLookup(this.tabId);
if ( tabContext.rootHostname !== this.tabHostname ) {
context = '';
}
@ -416,22 +421,18 @@ PageStore.prototype.dispose = function() {
/******************************************************************************/
PageStore.prototype.disposeFrameStores = function() {
for ( var frameStore of this.frames.values() ) {
for ( const frameStore of this.frames.values() ) {
frameStore.dispose();
}
this.frames.clear();
};
/******************************************************************************/
PageStore.prototype.getFrame = function(frameId) {
return this.frames.get(frameId) || null;
};
/******************************************************************************/
PageStore.prototype.setFrame = function(frameId, frameURL) {
var frameStore = this.frames.get(frameId);
const frameStore = this.frames.get(frameId);
if ( frameStore !== undefined ) {
frameStore.init(frameURL);
} else {
@ -441,54 +442,19 @@ PageStore.prototype.setFrame = function(frameId, frameURL) {
/******************************************************************************/
PageStore.prototype.createContextFromPage = function() {
var context = µb.tabContextManager.createContext(this.tabId);
context.pageHostname = context.rootHostname;
context.pageDomain = context.rootDomain;
return context;
};
PageStore.prototype.createContextFromFrameId = function(frameId) {
var context = µb.tabContextManager.createContext(this.tabId);
var frameStore = this.frames.get(frameId);
if ( frameStore !== undefined ) {
context.pageHostname = frameStore.pageHostname;
context.pageDomain = frameStore.pageDomain;
} else {
context.pageHostname = context.rootHostname;
context.pageDomain = context.rootDomain;
}
return context;
};
PageStore.prototype.createContextFromFrameHostname = function(frameHostname) {
var context = µb.tabContextManager.createContext(this.tabId);
context.pageHostname = frameHostname;
context.pageDomain = µb.URI.domainFromHostname(frameHostname) || frameHostname;
return context;
};
/******************************************************************************/
PageStore.prototype.getNetFilteringSwitch = function() {
return µb.tabContextManager.mustLookup(this.tabId).getNetFilteringSwitch();
};
/******************************************************************************/
PageStore.prototype.getSpecificCosmeticFilteringSwitch = function() {
return this.noCosmeticFiltering !== true;
};
/******************************************************************************/
PageStore.prototype.getGenericCosmeticFilteringSwitch = function() {
return this.noGenericCosmeticFiltering !== true &&
this.noCosmeticFiltering !== true;
};
/******************************************************************************/
PageStore.prototype.toggleNetFilteringSwitch = function(url, scope, state) {
µb.toggleNetFilteringSwitch(url, scope, state);
this.netFilteringCache.empty();
@ -562,15 +528,14 @@ PageStore.prototype.journalProcess = function(fromTimer) {
}
this.journalTimer = null;
var journal = this.journal,
n = journal.length,
aggregateCounts = 0,
now = Date.now(),
pivot = this.journalLastCommitted || 0;
const journal = this.journal;
const now = Date.now();
let aggregateCounts = 0;
let pivot = this.journalLastCommitted || 0;
// Everything after pivot originates from current page.
for ( let i = pivot; i < n; i += 2 ) {
let hostname = journal[i];
for ( let i = pivot; i < journal.length; i += 2 ) {
const hostname = journal[i];
let hostnameCounts = this.hostnameToCountMap.get(hostname);
if ( hostnameCounts === undefined ) {
hostnameCounts = 0;
@ -605,88 +570,101 @@ PageStore.prototype.journalProcess = function(fromTimer) {
/******************************************************************************/
PageStore.prototype.filterRequest = function(context) {
this.logData = undefined;
PageStore.prototype.filterRequest = function(fctxt) {
fctxt.filter = undefined;
if ( this.getNetFilteringSwitch() === false ) {
return 0;
}
var requestType = context.requestType;
const requestType = fctxt.type;
if ( requestType === 'csp_report' && this.filterCSPReport(context) === 1 ) {
if ( requestType === 'csp_report' && this.filterCSPReport(fctxt) === 1 ) {
return 1;
}
if ( requestType.endsWith('font') && this.filterFont(context) === 1 ) {
if ( requestType.endsWith('font') && this.filterFont(fctxt) === 1 ) {
return 1;
}
if (
requestType === 'script' &&
this.filterScripting(context.rootHostname, true) === 1
this.filterScripting(fctxt, true) === 1
) {
return 1;
}
var cacheableResult = this.cacheableResults[requestType] === true;
const cacheableResult = this.cacheableResults.has(requestType);
if ( cacheableResult ) {
var entry = this.netFilteringCache.lookupResult(context);
const entry = this.netFilteringCache.lookupResult(fctxt);
if ( entry !== undefined ) {
this.logData = entry.logData;
fctxt.filter = entry.logData;
return entry.result;
}
}
// Dynamic URL filtering.
var result = µb.sessionURLFiltering.evaluateZ(context.rootHostname, context.requestURL, requestType);
if ( result !== 0 && µb.logger.isEnabled() ) {
this.logData = µb.sessionURLFiltering.toLogData();
let result = µb.sessionURLFiltering.evaluateZ(
fctxt.getTabHostname(),
fctxt.url,
requestType
);
if ( result !== 0 && µb.logger.enabled ) {
fctxt.filter = µb.sessionURLFiltering.toLogData();
}
// Dynamic hostname/type filtering.
if ( result === 0 && µb.userSettings.advancedUserEnabled ) {
result = µb.sessionFirewall.evaluateCellZY(context.rootHostname, context.requestHostname, requestType);
if ( result !== 0 && result !== 3 && µb.logger.isEnabled() ) {
this.logData = µb.sessionFirewall.toLogData();
result = µb.sessionFirewall.evaluateCellZY(
fctxt.getTabHostname(),
fctxt.getHostname(),
requestType
);
if ( result !== 0 && result !== 3 && µb.logger.enabled ) {
fctxt.filter = µb.sessionFirewall.toLogData();
}
}
// Static filtering has lowest precedence.
if ( result === 0 || result === 3 ) {
result = µb.staticNetFilteringEngine.matchString(context);
if ( result !== 0 && µb.logger.isEnabled() ) {
this.logData = µb.staticNetFilteringEngine.toLogData();
result = µb.staticNetFilteringEngine.matchString(fctxt);
if ( result !== 0 && µb.logger.enabled ) {
fctxt.filter = µb.staticNetFilteringEngine.toLogData();
}
}
if ( cacheableResult ) {
this.netFilteringCache.rememberResult(context, result, this.logData);
} else if ( result === 1 && this.collapsibleResources[requestType] === true ) {
this.netFilteringCache.rememberBlock(context, true);
this.netFilteringCache.rememberResult(fctxt, result, this.logData);
} else if ( result === 1 && this.collapsibleResources.has(requestType) ) {
this.netFilteringCache.rememberBlock(fctxt, true);
}
return result;
};
PageStore.prototype.cacheableResults = {
sub_frame: true
};
PageStore.prototype.cacheableResults = new Set([
'sub_frame',
]);
PageStore.prototype.collapsibleResources = {
image: true,
media: true,
object: true,
sub_frame: true
};
PageStore.prototype.collapsibleResources = new Set([
'image',
'media',
'object',
'sub_frame',
]);
/******************************************************************************/
PageStore.prototype.filterCSPReport = function(context) {
if ( µb.sessionSwitches.evaluateZ('no-csp-reports', context.requestHostname) ) {
if ( µb.logger.isEnabled() ) {
this.logData = µb.sessionSwitches.toLogData();
PageStore.prototype.filterCSPReport = function(fctxt) {
if (
µb.sessionSwitches.evaluateZ(
'no-csp-reports',
fctxt.getHostname()
)
) {
if ( µb.logger.enabled ) {
fctxt.filter = µb.sessionSwitches.toLogData();
}
return 1;
}
@ -695,13 +673,18 @@ PageStore.prototype.filterCSPReport = function(context) {
/******************************************************************************/
PageStore.prototype.filterFont = function(context) {
if ( context.requestType === 'font' ) {
PageStore.prototype.filterFont = function(fctxt) {
if ( fctxt.type === 'font' ) {
this.remoteFontCount += 1;
}
if ( µb.sessionSwitches.evaluateZ('no-remote-fonts', context.rootHostname) !== false ) {
if ( µb.logger.isEnabled() ) {
this.logData = µb.sessionSwitches.toLogData();
if (
µb.sessionSwitches.evaluateZ(
'no-remote-fonts',
fctxt.getTabHostname()
) !== false
) {
if ( µb.logger.enabled ) {
fctxt.filter = µb.sessionSwitches.toLogData();
}
return 1;
}
@ -710,18 +693,22 @@ PageStore.prototype.filterFont = function(context) {
/******************************************************************************/
PageStore.prototype.filterScripting = function(rootHostname, netFiltering) {
PageStore.prototype.filterScripting = function(fctxt, netFiltering) {
fctxt.filter = undefined;
if ( netFiltering === undefined ) {
netFiltering = this.getNetFilteringSwitch();
}
if (
netFiltering === false ||
µb.sessionSwitches.evaluateZ('no-scripting', rootHostname) === false
µb.sessionSwitches.evaluateZ(
'no-scripting',
fctxt.getTabHostname()
) === false
) {
return 0;
}
if ( µb.logger.isEnabled() ) {
this.logData = µb.sessionSwitches.toLogData();
if ( µb.logger.enabled ) {
fctxt.filter = µb.sessionSwitches.toLogData();
}
return 1;
};
@ -730,13 +717,18 @@ PageStore.prototype.filterScripting = function(rootHostname, netFiltering) {
// The caller is responsible to check whether filtering is enabled or not.
PageStore.prototype.filterLargeMediaElement = function(size) {
this.logData = undefined;
PageStore.prototype.filterLargeMediaElement = function(fctxt, size) {
fctxt.filter = undefined;
if ( Date.now() < this.allowLargeMediaElementsUntil ) {
return 0;
}
if ( µb.sessionSwitches.evaluateZ('no-large-media', this.tabHostname) !== true ) {
if (
µb.sessionSwitches.evaluateZ(
'no-large-media',
fctxt.getTabHostname()
) !== true
) {
return 0;
}
if ( (size >>> 10) < µb.userSettings.largeMediaSize ) {
@ -751,8 +743,8 @@ PageStore.prototype.filterLargeMediaElement = function(size) {
);
}
if ( µb.logger.isEnabled() ) {
this.logData = µb.sessionSwitches.toLogData();
if ( µb.logger.enabled ) {
fctxt.filter = µb.sessionSwitches.toLogData();
}
return 1;
@ -763,26 +755,27 @@ PageStore.prototype.filterLargeMediaElement = function(size) {
/******************************************************************************/
PageStore.prototype.getBlockedResources = function(request, response) {
var µburi = µb.URI,
normalURL = µb.normalizePageURL(this.tabId, request.frameURL),
frameHostname = µburi.hostnameFromURI(normalURL),
resources = request.resources;
const normalURL = µb.normalizePageURL(this.tabId, request.frameURL);
const resources = request.resources;
const fctxt = µBlock.filteringContext;
fctxt.fromTabId(this.tabId)
.setDocOriginFromURL(normalURL);
// Force some resources to go through the filtering engine in order to
// populate the blocked-resources cache. This is required because for
// some resources it's not possible to detect whether they were blocked
// content script-side (i.e. `iframes` -- unlike `img`).
if ( Array.isArray(resources) && resources.length !== 0 ) {
var context = this.createContextFromFrameHostname(frameHostname);
for ( var resource of resources ) {
context.requestType = resource.type;
context.requestHostname = µburi.hostnameFromURI(resource.url);
context.requestURL = resource.url;
this.filterRequest(context);
for ( const resource of resources ) {
this.filterRequest(
fctxt.setType(resource.type)
.setURL(resource.url)
);
}
}
if ( this.netFilteringCache.hash === response.hash ) { return; }
response.hash = this.netFilteringCache.hash;
response.blockedResources = this.netFilteringCache.lookupAllBlocked(frameHostname);
response.blockedResources =
this.netFilteringCache.lookupAllBlocked(fctxt.getDocHostname());
};
/******************************************************************************/

View File

@ -95,14 +95,14 @@ const RedirectEntry = function() {
// - https://stackoverflow.com/a/8056313
// - https://bugzilla.mozilla.org/show_bug.cgi?id=998076
RedirectEntry.prototype.toURL = function(details) {
RedirectEntry.prototype.toURL = function(fctxt) {
if (
this.warURL !== undefined &&
details instanceof Object &&
details.requestType !== 'xmlhttprequest' &&
fctxt instanceof Object &&
fctxt.type !== 'xmlhttprequest' &&
(
suffersSpuriousRedirectConflicts === false ||
details.requestURL.startsWith('https:')
fctxt.url.startsWith('https:')
)
) {
return this.warURL + '?secret=' + vAPI.warSecret;
@ -187,14 +187,14 @@ RedirectEngine.prototype.toBroaderHostname = function(hostname) {
/******************************************************************************/
RedirectEngine.prototype.lookup = function(context) {
var type = context.requestType;
RedirectEngine.prototype.lookup = function(fctxt) {
const type = fctxt.type;
if ( this.ruleTypes.has(type) === false ) { return; }
var src = context.pageHostname,
des = context.requestHostname,
desAll = this._desAll,
reqURL = context.requestURL;
var n = 0;
const desAll = this._desAll;
const reqURL = fctxt.url;
let src = fctxt.getDocHostname();
let des = fctxt.getHostname();
let n = 0;
for (;;) {
if ( this.ruleDestinations.has(des) ) {
desAll[n] = des; n += 1;
@ -203,11 +203,10 @@ RedirectEngine.prototype.lookup = function(context) {
if ( des === '' ) { break; }
}
if ( n === 0 ) { return; }
var entries;
for (;;) {
if ( this.ruleSources.has(src) ) {
for ( var i = 0; i < n; i++ ) {
entries = this.rules.get(src + ' ' + desAll[i] + ' ' + type);
for ( let i = 0; i < n; i++ ) {
const entries = this.rules.get(src + ' ' + desAll[i] + ' ' + type);
if ( entries && this.lookupToken(entries, reqURL) ) {
return this.resourceNameRegister;
}
@ -234,12 +233,12 @@ RedirectEngine.prototype.lookupToken = function(entries, reqURL) {
/******************************************************************************/
RedirectEngine.prototype.toURL = function(context) {
let token = this.lookup(context);
RedirectEngine.prototype.toURL = function(fctxt) {
let token = this.lookup(fctxt);
if ( token === undefined ) { return; }
let entry = this.resources.get(token);
if ( entry !== undefined ) {
return entry.toURL(context);
return entry.toURL(fctxt);
}
};

View File

@ -24,18 +24,26 @@
/******************************************************************************/
µBlock.scriptletFilteringEngine = (function() {
let api = {};
let µb = µBlock,
scriptletDB = new µb.staticExtFilteringEngine.HostnameBasedDB(),
const µb = µBlock,
duplicates = new Set(),
acceptedCount = 0,
discardedCount = 0,
scriptletCache = new µb.MRUCache(32),
exceptionsRegister = new Set(),
scriptletsRegister = new Map(),
exceptionsRegister = new Set(),
reEscapeScriptArg = /[\\'"]/g;
let acceptedCount = 0,
discardedCount = 0,
scriptletDB = new µb.staticExtFilteringEngine.HostnameBasedDB();
const api = {
get acceptedCount() {
return acceptedCount;
},
get discardedCount() {
return discardedCount;
}
};
// Purpose of `contentscriptCode` below is too programmatically inject
// content script code which only purpose is to inject scriptlets. This
// essentially does the same as what uBO's declarative content script does,
@ -58,8 +66,8 @@
// Consequently, the programmatic-injection code path is taken only with
// Chromium-based browsers.
let contentscriptCode = (function() {
let parts = [
const contentscriptCode = (function() {
const parts = [
'(',
function(hostname, scriptlets) {
if (
@ -163,15 +171,15 @@
};
})();
let lookupScriptlet = function(raw, reng, toInject) {
const lookupScriptlet = function(raw, reng, toInject) {
if ( toInject.has(raw) ) { return; }
if ( scriptletCache.resetTime < reng.modifyTime ) {
scriptletCache.reset();
}
var content = scriptletCache.lookup(raw);
let content = scriptletCache.lookup(raw);
if ( content === undefined ) {
var token, args,
pos = raw.indexOf(',');
const pos = raw.indexOf(',');
let token, args;
if ( pos === -1 ) {
token = raw;
} else {
@ -196,13 +204,12 @@
// Fill template placeholders. Return falsy if:
// - At least one argument contains anything else than /\w/ and `.`
let patchScriptlet = function(content, args) {
var i = 1,
pos, arg;
const patchScriptlet = function(content, args) {
let i = 1;
while ( args !== '' ) {
pos = args.indexOf(',');
let pos = args.indexOf(',');
if ( pos === -1 ) { pos = args.length; }
arg = args.slice(0, pos).trim().replace(reEscapeScriptArg, '\\$&');
const arg = args.slice(0, pos).trim().replace(reEscapeScriptArg, '\\$&');
content = content.replace('{{' + i + '}}', arg);
args = args.slice(pos + 1).trim();
i++;
@ -210,19 +217,19 @@
return content;
};
let logOne = function(isException, token, details) {
µb.logger.writeOne(
details.tabId,
'cosmetic',
{
const logOne = function(isException, token, details) {
µBlock.filteringContext
.duplicate()
.fromTabId(details.tabId)
.setRealm('cosmetic')
.setType('dom')
.setURL(details.url)
.setDocOriginFromURL(details.url)
.setFilter({
source: 'cosmetic',
raw: (isException ? '#@#' : '##') + '+js(' + token + ')'
},
'dom',
details.url,
null,
details.hostname
);
})
.toLogger();
};
api.reset = function() {
@ -254,7 +261,7 @@
// because there is no way to create an exception to an exception.
for ( let hn of parsed.hostnames ) {
let negated = hn.charCodeAt(0) === 0x7E /* '~' */;
const negated = hn.charCodeAt(0) === 0x7E /* '~' */;
if ( negated ) {
hn = hn.slice(1);
}
@ -280,13 +287,13 @@
while ( reader.next() ) {
acceptedCount += 1;
let fingerprint = reader.fingerprint();
const fingerprint = reader.fingerprint();
if ( duplicates.has(fingerprint) ) {
discardedCount += 1;
continue;
}
duplicates.add(fingerprint);
let args = reader.args();
const args = reader.args();
if ( args.length < 4 ) { continue; }
scriptletDB.add(
args[1],
@ -299,10 +306,10 @@
if ( scriptletDB.size === 0 ) { return; }
if ( µb.hiddenSettings.ignoreScriptInjectFilters ) { return; }
let reng = µb.redirectEngine;
const reng = µb.redirectEngine;
if ( !reng ) { return; }
let hostname = request.hostname;
const hostname = request.hostname;
// https://github.com/gorhill/uBlock/issues/2835
// Do not inject scriptlets if the site is under an `allow` rule.
@ -313,8 +320,8 @@
return;
}
let domain = request.domain,
entity = request.entity;
const domain = request.domain;
const entity = request.entity;
// https://github.com/gorhill/uBlock/issues/1954
// Implicit
@ -322,7 +329,7 @@
for (;;) {
lookupScriptlet(hn + '.js', reng, scriptletsRegister);
if ( hn === domain ) { break; }
var pos = hn.indexOf('.');
const pos = hn.indexOf('.');
if ( pos === -1 ) { break; }
hn = hn.slice(pos + 1);
}
@ -332,16 +339,16 @@
// Explicit
let entries = [];
let domainHash = µb.staticExtFilteringEngine.makeHash(domain);
const domainHash = µb.staticExtFilteringEngine.makeHash(domain);
if ( domainHash !== 0 ) {
scriptletDB.retrieve(domainHash, hostname, entries);
}
let entityHash = µb.staticExtFilteringEngine.makeHash(entity);
const entityHash = µb.staticExtFilteringEngine.makeHash(entity);
if ( entityHash !== 0 ) {
scriptletDB.retrieve(entityHash, entity, entries);
}
scriptletDB.retrieve(0, hostname, entries);
for ( let entry of entries ) {
for ( const entry of entries ) {
lookupScriptlet(entry.token, reng, scriptletsRegister);
}
@ -356,19 +363,19 @@
scriptletDB.retrieve(entityHash | 0b0001, entity, entries);
}
scriptletDB.retrieve(0 | 0b0001, hostname, entries);
for ( let entry of entries ) {
for ( const entry of entries ) {
exceptionsRegister.add(entry.token);
}
// Return an array of scriptlets, and log results if needed.
let out = [],
logger = µb.logger.isEnabled() ? µb.logger : null,
isException;
for ( let entry of scriptletsRegister ) {
if ( (isException = exceptionsRegister.has(entry[0])) === false ) {
const out = [];
const loggerEnabled = µb.logger.enabled;
for ( const entry of scriptletsRegister ) {
const isException = exceptionsRegister.has(entry[0]);
if ( isException === false ) {
out.push(entry[1]);
}
if ( logger !== null ) {
if ( loggerEnabled ) {
logOne(isException, entry[0], request);
}
}
@ -384,7 +391,7 @@
api.injectNow = function(details) {
if ( typeof details.frameId !== 'number' ) { return; }
if ( µb.URI.isNetworkURI(details.url) === false ) { return; }
let request = {
const request = {
tabId: details.tabId,
frameId: details.frameId,
url: details.url,
@ -394,7 +401,7 @@
};
request.domain = µb.URI.domainFromHostname(request.hostname);
request.entity = µb.URI.entityFromDomain(request.domain);
let scriptlets = µb.scriptletFilteringEngine.retrieve(request);
const scriptlets = µb.scriptletFilteringEngine.retrieve(request);
if ( scriptlets === undefined ) { return; }
let code = contentscriptCode.assemble(request.hostname, scriptlets);
if ( µb.hiddenSettings.debugScriptlets ) {
@ -419,19 +426,6 @@
scriptletDB = new µb.staticExtFilteringEngine.HostnameBasedDB(selfie);
};
Object.defineProperties(api, {
acceptedCount: {
get: function() {
return acceptedCount;
}
},
discardedCount: {
get: function() {
return discardedCount;
}
}
});
return api;
})();

View File

@ -663,11 +663,9 @@
return compiled;
}
µb.logger.writeOne(
'',
'error',
'Cosmetic filtering invalid filter: ' + raw
);
µb.logger.writeOne({
error: 'Cosmetic filtering invalid filter: ' + raw
});
};
return entryPoint;

View File

@ -126,13 +126,6 @@ let pageHostnameRegister = '',
// Local helpers
// Be sure to not confuse 'example.com' with 'anotherexample.com'
const isFirstParty = function(domain, hostname) {
return hostname.endsWith(domain) &&
(hostname.length === domain.length ||
hostname.charCodeAt(hostname.length - domain.length - 1) === 0x2E /* '.' */);
};
const normalizeRegexSource = function(s) {
try {
const re = new RegExp(s);
@ -2184,12 +2177,10 @@ FilterContainer.prototype.compile = function(raw, writer) {
// ORDER OF TESTS IS IMPORTANT!
// Ignore empty lines
var s = raw.trim();
if ( s.length === 0 ) {
return false;
}
const s = raw.trim();
if ( s.length === 0 ) { return false; }
var parsed = this.filterParser.parse(s);
const parsed = this.filterParser.parse(s);
// Ignore element-hiding filters
if ( parsed.elemHiding ) {
@ -2198,7 +2189,9 @@ FilterContainer.prototype.compile = function(raw, writer) {
// Ignore filters with unsupported options
if ( parsed.unsupported ) {
µb.logger.writeOne('', 'error', 'Network filtering invalid filter: ' + raw);
µb.logger.writeOne({
error: 'Network filtering invalid filter: ' + raw
});
return false;
}
@ -2217,7 +2210,7 @@ FilterContainer.prototype.compile = function(raw, writer) {
parsed.makeToken();
var fdata;
let fdata;
if ( parsed.isRegex ) {
fdata = FilterRegex.compile(parsed);
} else if ( parsed.hostnamePure ) {
@ -2256,7 +2249,7 @@ FilterContainer.prototype.compile = function(raw, writer) {
fdata = FilterPlain.compile(parsed);
}
var fwrapped;
let fwrapped;
if ( parsed.domainOpt !== '' ) {
fwrapped = fdata;
fdata = FilterOrigin.compile(parsed);
@ -2520,24 +2513,22 @@ FilterContainer.prototype.matchStringGenericHide = function(requestURL) {
// Some type of requests are exceptional, they need custom handling,
// not the generic handling.
FilterContainer.prototype.matchStringExactType = function(context, requestURL, requestType) {
FilterContainer.prototype.matchStringExactType = function(fctxt, requestType) {
// Special cases.
if ( requestType === 'generichide' ) {
return this.matchStringGenericHide(requestURL);
return this.matchStringGenericHide(fctxt.url);
}
let type = typeNameToTypeValue[requestType];
if ( type === undefined ) { return 0; }
// Prime tokenizer: we get a normalized URL in return.
let url = this.urlTokenizer.setURL(requestURL);
let url = this.urlTokenizer.setURL(fctxt.url);
// These registers will be used by various filters
pageHostnameRegister = context.pageHostname || '';
requestHostnameRegister = µb.URI.hostnameFromURI(url);
pageHostnameRegister = fctxt.getDocHostname();
requestHostnameRegister = fctxt.getHostname();
let party = isFirstParty(context.pageDomain, requestHostnameRegister)
? FirstParty
: ThirdParty;
let party = fctxt.is3rdPartyToDoc() ? ThirdParty : FirstParty;
let categories = this.categories,
catBits, bucket;
@ -2602,15 +2593,15 @@ FilterContainer.prototype.matchStringExactType = function(context, requestURL, r
/******************************************************************************/
FilterContainer.prototype.matchString = function(context) {
FilterContainer.prototype.matchString = function(fctxt) {
// https://github.com/chrisaljoudi/uBlock/issues/519
// Use exact type match for anything beyond `other`
// Also, be prepared to support unknown types
let type = typeNameToTypeValue[context.requestType];
let type = typeNameToTypeValue[fctxt.type];
if ( type === undefined ) {
type = otherTypeBitValue;
} else if ( type === 0 || type > otherTypeBitValue ) {
return this.matchStringExactType(context, context.requestURL, context.requestType);
return this.matchStringExactType(fctxt, fctxt.type);
}
// The logic here is simple:
@ -2635,17 +2626,17 @@ FilterContainer.prototype.matchString = function(context) {
// filter.
// Prime tokenizer: we get a normalized URL in return.
let url = this.urlTokenizer.setURL(context.requestURL);
let url = this.urlTokenizer.setURL(fctxt.url);
// These registers will be used by various filters
pageHostnameRegister = context.pageHostname || '';
requestHostnameRegister = context.requestHostname;
pageHostnameRegister = fctxt.getDocHostname();
requestHostnameRegister = fctxt.getHostname();
this.fRegister = null;
let party = isFirstParty(context.pageDomain, context.requestHostname)
? FirstParty
: ThirdParty;
let party = fctxt.is3rdPartyToDoc()
? ThirdParty
: FirstParty;
let categories = this.categories,
catBits, bucket;

View File

@ -215,6 +215,7 @@ housekeep itself.
this.stack = [];
this.rawURL =
this.normalURL =
this.origin =
this.rootHostname =
this.rootDomain = '';
this.commitTimer = null;
@ -305,14 +306,21 @@ housekeep itself.
TabContext.prototype.update = function() {
this.netFilteringReadTime = 0;
if ( this.stack.length === 0 ) {
this.rawURL = this.normalURL = this.rootHostname = this.rootDomain = '';
this.rawURL =
this.normalURL =
this.origin =
this.rootHostname =
this.rootDomain = '';
return;
}
var stackEntry = this.stack[this.stack.length - 1];
this.rawURL = stackEntry.url;
this.normalURL = µb.normalizePageURL(this.tabId, this.rawURL);
this.rootHostname = µb.URI.hostnameFromURI(this.normalURL);
this.rootDomain = µb.URI.domainFromHostname(this.rootHostname) || this.rootHostname;
this.origin = µb.URI.originFromURI(this.normalURL);
this.rootHostname = µb.URI.hostnameFromURI(this.origin);
this.rootDomain =
µb.URI.domainFromHostname(this.rootHostname) ||
this.rootHostname;
};
// Called whenever a candidate root URL is spotted for the tab.
@ -431,15 +439,13 @@ housekeep itself.
// Behind-the-scene tab context
(function() {
var entry = new TabContext(vAPI.noTabId);
const entry = new TabContext(vAPI.noTabId);
entry.stack.push(new StackEntry('', true));
entry.rawURL = '';
entry.normalURL = µb.normalizePageURL(entry.tabId);
entry.rootHostname = µb.URI.hostnameFromURI(entry.normalURL);
entry.origin = µb.URI.originFromURI(entry.normalURL);
entry.rootHostname = µb.URI.hostnameFromURI(entry.origin);
entry.rootDomain = µb.URI.domainFromHostname(entry.rootHostname);
entry = new TabContext(vAPI.anyTabId);
entry.stack.push(new StackEntry('', true));
})();
// Context object, typically to be used to feed filtering engines.
@ -454,6 +460,7 @@ housekeep itself.
this.pageHostname =
this.pageDomain =
this.requestURL =
this.origin =
this.requestHostname =
this.requestDomain = '';
return this;
@ -550,8 +557,7 @@ vAPI.tabs.onClosed = function(tabId) {
vAPI.tabs.onPopupUpdated = (function() {
// The same context object will be reused everytime. This also allows to
// remember whether a popup or popunder was matched.
var context = {},
logData;
const fctxt = µBlock.filteringContext.setFilter(undefined);
// https://github.com/gorhill/uBlock/commit/1d448b85b2931412508aa01bf899e0b6f0033626#commitcomment-14944764
// See if two URLs are different, disregarding scheme -- because the
@ -560,10 +566,10 @@ vAPI.tabs.onPopupUpdated = (function() {
// Maybe no link element was clicked.
// https://github.com/gorhill/uBlock/issues/3287
// Do not bail out if the target URL has no hostname.
var areDifferentURLs = function(a, b) {
const areDifferentURLs = function(a, b) {
if ( b === '' ) { return true; }
if ( b.startsWith('about:') ) { return false; }
var pos = a.indexOf('://');
let pos = a.indexOf('://');
if ( pos === -1 ) { return false; }
a = a.slice(pos);
pos = b.indexOf('://');
@ -573,19 +579,12 @@ vAPI.tabs.onPopupUpdated = (function() {
return b !== a;
};
var popupMatch = function(openerURL, targetURL, popupType) {
var openerHostname = µb.URI.hostnameFromURI(openerURL),
openerDomain = µb.URI.domainFromHostname(openerHostname),
result;
context.pageHostname = openerHostname;
context.pageDomain = openerDomain;
context.rootURL = openerURL;
context.rootHostname = openerHostname;
context.rootDomain = openerDomain;
context.requestURL = targetURL;
context.requestHostname = µb.URI.hostnameFromURI(targetURL);
context.requestType = 'popup';
const popupMatch = function(openerURL, targetURL, popupType) {
fctxt.setTabOriginFromURL(openerURL)
.setDocOriginFromURL(openerURL)
.setURL(targetURL)
.setType('popup');
let result;
// https://github.com/gorhill/uBlock/issues/1735
// Do not bail out on `data:` URI, they are commonly used for popups.
@ -603,16 +602,19 @@ vAPI.tabs.onPopupUpdated = (function() {
// Ignore bad target URL. On Firefox, an `about:blank` tab may be
// opened for a new tab before it is filled in with the real target
// URL.
if ( openerHostname !== '' && targetURL !== 'about:blank' ) {
if ( fctxt.getTabHostname() !== '' && targetURL !== 'about:blank' ) {
// Check per-site switch first
// https://github.com/gorhill/uBlock/issues/3060
// - The no-popups switch must apply only to popups, not to
// popunders.
if (
popupType === 'popup' &&
µb.sessionSwitches.evaluateZ('no-popups', openerHostname)
µb.sessionSwitches.evaluateZ(
'no-popups',
fctxt.getTabHostname()
)
) {
logData = {
fctxt.filter = {
raw: 'no-popups: ' + µb.sessionSwitches.z + ' true',
result: 1,
source: 'switch'
@ -624,7 +626,7 @@ vAPI.tabs.onPopupUpdated = (function() {
// Take into account popup-specific rules in dynamic URL
// filtering, OR generic allow rules.
result = µb.sessionURLFiltering.evaluateZ(
openerHostname,
fctxt.getTabHostname(),
targetURL,
popupType
);
@ -632,7 +634,7 @@ vAPI.tabs.onPopupUpdated = (function() {
result === 1 && µb.sessionURLFiltering.type === popupType ||
result === 2
) {
logData = µb.sessionURLFiltering.toLogData();
fctxt.filter = µb.sessionURLFiltering.toLogData();
return result;
}
@ -641,12 +643,12 @@ vAPI.tabs.onPopupUpdated = (function() {
// rules are ignored, as block rules are not meant to block
// specific types like `popup` (just like with static filters).
result = µb.sessionFirewall.evaluateCellZY(
openerHostname,
context.requestHostname,
fctxt.getTabHostname(),
fctxt.getHostname(),
popupType
);
if ( result === 2 ) {
logData = µb.sessionFirewall.toLogData();
fctxt.filter = µb.sessionFirewall.toLogData();
return 2;
}
}
@ -656,12 +658,11 @@ vAPI.tabs.onPopupUpdated = (function() {
// Don't block if uBlock is turned off in popup's context
if ( µb.getNetFilteringSwitch(targetURL) ) {
result = µb.staticNetFilteringEngine.matchStringExactType(
context,
targetURL,
fctxt,
popupType
);
if ( result !== 0 ) {
logData = µb.staticNetFilteringEngine.toLogData();
fctxt.filter = µb.staticNetFilteringEngine.toLogData();
return result;
}
}
@ -669,23 +670,23 @@ vAPI.tabs.onPopupUpdated = (function() {
return 0;
};
var mapPopunderResult = function(popunderURL, popunderHostname, result) {
const mapPopunderResult = function(popunderURL, popunderHostname, result) {
if (
logData === undefined ||
logData.source !== 'static' ||
logData.token === µb.staticNetFilteringEngine.noTokenHash
fctxt.filter === undefined ||
fctxt.filter !== 'static' ||
fctxt.filter.token === µb.staticNetFilteringEngine.noTokenHash
) {
return 0;
}
if ( logData.token === µb.staticNetFilteringEngine.dotTokenHash ) {
if ( fctxt.filter.token === µb.staticNetFilteringEngine.dotTokenHash ) {
return result;
}
var re = new RegExp(logData.regex, 'i'),
matches = re.exec(popunderURL);
const re = new RegExp(fctxt.filter.regex, 'i');
const matches = re.exec(popunderURL);
if ( matches === null ) { return 0; }
var beg = matches.index,
end = beg + matches[0].length,
pos = popunderURL.indexOf(popunderHostname);
const beg = matches.index;
const end = beg + matches[0].length;
const pos = popunderURL.indexOf(popunderHostname);
if ( pos === -1 ) { return 0; }
// https://github.com/gorhill/uBlock/issues/1471
// We test whether the opener hostname as at least one character
@ -698,36 +699,32 @@ vAPI.tabs.onPopupUpdated = (function() {
: 0;
};
var popunderMatch = function(openerURL, targetURL) {
var result = popupMatch(targetURL, openerURL, 'popunder');
if ( result === 1 ) {
return result;
}
const popunderMatch = function(openerURL, targetURL) {
let result = popupMatch(targetURL, openerURL, 'popunder');
if ( result === 1 ) { return result; }
// https://github.com/gorhill/uBlock/issues/1010#issuecomment-186824878
// Check the opener tab as if it were the newly opened tab: if there
// is a hit against a popup filter, and if the matching filter is not
// a broad one, we will consider the opener tab to be a popunder tab.
// For now, a "broad" filter is one which does not touch any part of
// the hostname part of the opener URL.
var popunderURL = openerURL,
let popunderURL = openerURL,
popunderHostname = µb.URI.hostnameFromURI(popunderURL);
if ( popunderHostname === '' ) {
return 0;
}
if ( popunderHostname === '' ) { return 0; }
result = mapPopunderResult(
popunderURL,
popunderHostname,
popupMatch(targetURL, popunderURL, 'popup')
);
if ( result !== 0 ) {
return result;
}
if ( result !== 0 ) { return result; }
// https://github.com/gorhill/uBlock/issues/1598
// Try to find a match against origin part of the opener URL.
// Try to find a match against origin part of the opener URL.
popunderURL = µb.URI.originFromURI(popunderURL);
if ( popunderURL === '' ) {
return 0;
}
if ( popunderURL === '' ) { return 0; }
return mapPopunderResult(
popunderURL,
popunderHostname,
@ -737,40 +734,43 @@ vAPI.tabs.onPopupUpdated = (function() {
return function(targetTabId, openerDetails) {
// Opener details.
var openerTabId = openerDetails.tabId;
var tabContext = µb.tabContextManager.lookup(openerTabId);
const openerTabId = openerDetails.tabId;
let tabContext = µb.tabContextManager.lookup(openerTabId);
if ( tabContext === null ) { return; }
var openerURL = tabContext.rawURL;
const openerURL = tabContext.rawURL;
if ( openerURL === '' ) { return; }
// Popup details.
tabContext = µb.tabContextManager.lookup(targetTabId);
if ( tabContext === null ) { return; }
var targetURL = tabContext.rawURL;
let targetURL = tabContext.rawURL;
if ( targetURL === '' ) { return; }
// https://github.com/gorhill/uBlock/issues/341
// Allow popups if uBlock is turned off in opener's context.
if ( µb.getNetFilteringSwitch(openerURL) === false ) {
return;
}
if ( µb.getNetFilteringSwitch(openerURL) === false ) { return; }
// https://github.com/gorhill/uBlock/issues/1538
if ( µb.getNetFilteringSwitch(µb.normalizePageURL(openerTabId, openerURL)) === false ) {
if (
µb.getNetFilteringSwitch(µb.normalizePageURL(
openerTabId,
openerURL)
) === false
) {
return;
}
// If the page URL is that of our "blocked page" URL, extract the URL of
// the page which was blocked.
if ( targetURL.startsWith(vAPI.getURL('document-blocked.html')) ) {
var matches = /details=([^&]+)/.exec(targetURL);
const matches = /details=([^&]+)/.exec(targetURL);
if ( matches !== null ) {
targetURL = JSON.parse(atob(matches[1])).url;
}
}
// Popup test.
var popupType = 'popup',
let popupType = 'popup',
result = 0;
// https://github.com/gorhill/uBlock/issues/2919
// - If the target tab matches a clicked link, assume it's legit.
@ -788,18 +788,21 @@ vAPI.tabs.onPopupUpdated = (function() {
// Log only for when there was a hit against an actual filter (allow or block).
// https://github.com/gorhill/uBlock/issues/2776
if ( µb.logger.isEnabled() ) {
µb.logger.writeOne(
popupType === 'popup' ? openerTabId : targetTabId,
'net',
result !== 0 ? logData : undefined,
popupType,
popupType === 'popup' ? targetURL : openerURL,
µb.URI.hostnameFromURI(context.rootURL),
µb.URI.hostnameFromURI(context.rootURL)
);
if ( µb.logger.enabled ) {
fctxt.setRealm('net').setType(popupType);
if ( popupType === 'popup' ) {
fctxt.setURL(targetURL)
.setTabId(openerTabId)
.setTabOriginFromURL(openerURL)
.setDocOriginFromURL(openerURL);
} else {
fctxt.setURL(openerURL)
.setTabId(targetTabId)
.setTabOriginFromURL(targetURL)
.setDocOriginFromURL(targetURL);
}
fctxt.toLogger();
}
logData = undefined;
// Not blocked
if ( result !== 1 ) {
@ -808,9 +811,9 @@ vAPI.tabs.onPopupUpdated = (function() {
// Only if a popup was blocked do we report it in the dynamic
// filtering pane.
var pageStore = µb.pageStoreFromTabId(openerTabId);
const pageStore = µb.pageStoreFromTabId(openerTabId);
if ( pageStore ) {
pageStore.journalAddRequest(context.requestHostname, result);
pageStore.journalAddRequest(fctxt.getHostname(), result);
pageStore.popupBlockedCount += 1;
}
@ -910,13 +913,9 @@ vAPI.tabs.registerListeners();
// Permanent page store for behind-the-scene requests. Must never be removed.
(function() {
var pageStore = µb.PageStore.factory(vAPI.noTabId);
const pageStore = µb.PageStore.factory(vAPI.noTabId);
µb.pageStores.set(pageStore.tabId, pageStore);
pageStore.title = vAPI.i18n('logBehindTheScene');
pageStore = µb.PageStore.factory(vAPI.anyTabId);
µb.pageStores.set(pageStore.tabId, pageStore);
pageStore.title = '[Any one of the known tabs]';
})();
/******************************************************************************/

File diff suppressed because it is too large Load Diff

View File

@ -538,7 +538,7 @@ var matchBucket = function(url, hostname, bucket, start) {
// cosmetic filters.
µBlock.logCosmeticFilters = function(tabId, frameId) {
if ( this.logger.isEnabled() ) {
if ( this.logger.enabled ) {
vAPI.tabs.injectScript(tabId, {
file: '/js/scriptlets/cosmetic-logger.js',
frameId: frameId,

View File

@ -1,7 +1,7 @@
/*******************************************************************************
uBlock Origin - a browser extension to block 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
@ -51,7 +51,7 @@ var reRFC3986 = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#]*)(\?[^#]*)?(#.*)?/;
// Derived
var reSchemeFromURI = /^[^:\/?#]+:/;
var reAuthorityFromURI = /^(?:[^:\/?#]+:)?(\/\/[^\/?#]+)/;
var reOriginFromURI = /^(?:[^:\/?#]+:)\/\/(?:[^\/?#]+)?/;
var reOriginFromURI = /^(?:[^:\/?#]+:)\/\/[^\/?#]+/;
var reCommonHostnameFromURL = /^https?:\/\/([0-9a-z_][0-9a-z._-]*[0-9a-z])\//;
var rePathFromURI = /^(?:[^:\/?#]+:)?(?:\/\/[^\/?#]*)?([^?#]*)/;
var reMustNormalizeHostname = /[^0-9a-z._-]/;
@ -225,7 +225,7 @@ URI.assemble = function(bits) {
/******************************************************************************/
URI.originFromURI = function(uri) {
var matches = reOriginFromURI.exec(uri);
const matches = reOriginFromURI.exec(uri);
return matches !== null ? matches[0].toLowerCase() : '';
};

View File

@ -37,14 +37,14 @@
/******************************************************************************/
var exceptions = new Map();
var rules = new Map();
let exceptions = new Map();
let rules = new Map();
// This value dictate how the search will be performed:
// < this.cutoffLength = indexOf()
// >= this.cutoffLength = binary search
var cutoffLength = 256;
var mustPunycode = /[^a-z0-9.-]/;
const cutoffLength = 256;
const mustPunycode = /[^a-z0-9.-]/;
/******************************************************************************/
@ -284,7 +284,7 @@ function crystallize(store) {
/******************************************************************************/
var selfieMagic = 1;
const selfieMagic = 1;
function toSelfie() {
return {
@ -311,12 +311,15 @@ function fromSelfie(selfie) {
root = root || window;
root.publicSuffixList = {
'version': '1.0',
'parse': parse,
'getDomain': getDomain,
'getPublicSuffix': getPublicSuffix,
'toSelfie': toSelfie,
'fromSelfie': fromSelfie,
version: '1.0',
parse: parse,
getDomain: getDomain,
getPublicSuffix: getPublicSuffix,
toSelfie: toSelfie,
fromSelfie: fromSelfie,
get empty() {
return rules.size === 0;
}
};
/******************************************************************************/

View File

@ -17,8 +17,9 @@
<option value="tab_bts" data-i18n="logBehindTheScene">
<option value="tab_active" data-i18n="loggerCurrentTab">
</select>
<span id="refresh" class="button fa disabled needtab">&#xf021;</span>
<span id="showdom" class="button fa">&#xf121;</span>
<span id="refresh" class="button fa disabled needdom">&#xf021;</span>
<span id="showdom" class="button fa disabled needdom">&#xf121;</span>
<span id="showpopup" class="button disabled needscope"><img src="/img/icon_64.png"></span>
</div>
</div>
@ -37,7 +38,6 @@
<ul id="domTree"></ul>
</div>
</div>
<div id="netInspector" class="inspector vCompact f">
<div class="permatoolbar">
<div>
@ -50,21 +50,17 @@
</div>
<div class="vscrollable">
<style id="tabFilterer"></style>
<style id="popupFilterer"></style>
<table>
<colgroup><col><col><col><col><col></colgroup>
<colgroup><col><col><col><col><col><col></colgroup>
<tbody></tbody>
</table>
<div id="popupContainer">
<div><span>&#xf068;</span>&ensp;<span>&#xf00d;</span></div>
</div>
</div>
</div>
<iframe id="popupContainer" class="hide"></iframe>
</div>
<div id="templates" style="display: none;">
<div id="renderedURLTemplate"><span><span></span><b></b><span></span></span></div>
<div id="hiddenTemplate"><span style="display:none;"></span></div>
<div id="netFilteringDialog" class="modalDialog">
<div class="dialog">
<div class="hide preview"></div>