mirror of https://github.com/gorhill/uBlock.git
Add support for removal of response headers
The syntax to remove response header is a special case of HTML filtering, whereas the response headers are targeted, rather than the response body: example.com##^responseheader(header-name) Where `header-name` is the name of the header to remove, and must always be lowercase. The removal of response headers can only be applied to document resources, i.e. main- or sub-frames. Only a limited set of headers can be targeted for removal: location refresh report-to set-cookie This limitation is to ensure that uBO never lowers the security profile of web pages, i.e. we wouldn't want to remove `content-security-policy`. Given that the header removal occurs at onHeaderReceived time, this new ability works for all browsers. The motivation for this new filtering ability is instance of website using a `refresh` header to redirect a visitor to an undesirable destination after a few seconds.
This commit is contained in:
parent
af980c5c06
commit
f876b68171
|
@ -34,6 +34,7 @@
|
||||||
<script src="js/cosmetic-filtering.js"></script>
|
<script src="js/cosmetic-filtering.js"></script>
|
||||||
<script src="js/scriptlet-filtering.js"></script>
|
<script src="js/scriptlet-filtering.js"></script>
|
||||||
<script src="js/html-filtering.js"></script>
|
<script src="js/html-filtering.js"></script>
|
||||||
|
<script src="js/httpheader-filtering.js"></script>
|
||||||
<script src="js/hnswitches.js"></script>
|
<script src="js/hnswitches.js"></script>
|
||||||
<script src="js/ublock.js"></script>
|
<script src="js/ublock.js"></script>
|
||||||
<script src="js/storage.js"></script>
|
<script src="js/storage.js"></script>
|
||||||
|
|
|
@ -260,11 +260,11 @@ body.colorBlind #netFilteringDialog > .panes > .details > div[data-status="2"] {
|
||||||
#vwRenderer .logEntry > div[data-tabid="-1"] {
|
#vwRenderer .logEntry > div[data-tabid="-1"] {
|
||||||
text-shadow: 0 0.2em 0.4em #aaa;
|
text-shadow: 0 0.2em 0.4em #aaa;
|
||||||
}
|
}
|
||||||
#vwRenderer .logEntry > div.cosmeticRealm,
|
#vwRenderer .logEntry > div.extendedRealm,
|
||||||
#vwRenderer .logEntry > div.redirect {
|
#vwRenderer .logEntry > div.redirect {
|
||||||
background-color: rgba(255, 255, 0, 0.1);
|
background-color: rgba(255, 255, 0, 0.1);
|
||||||
}
|
}
|
||||||
body.colorBlind #vwRenderer .logEntry > div.cosmeticRealm,
|
body.colorBlind #vwRenderer .logEntry > div.extendedRealm,
|
||||||
body.colorBlind #vwRenderer .logEntry > div.redirect {
|
body.colorBlind #vwRenderer .logEntry > div.redirect {
|
||||||
background-color: rgba(0, 19, 110, 0.1);
|
background-color: rgba(0, 19, 110, 0.1);
|
||||||
}
|
}
|
||||||
|
@ -324,13 +324,13 @@ body[dir="rtl"] #vwRenderer .logEntry > div > span:first-child {
|
||||||
#vwRenderer .logEntry > div.messageRealm[data-type="tabLoad"] > span:nth-of-type(2) {
|
#vwRenderer .logEntry > div.messageRealm[data-type="tabLoad"] > span:nth-of-type(2) {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
#vwRenderer .logEntry > div.cosmeticRealm > span:nth-of-type(2) > span:first-of-type {
|
#vwRenderer .logEntry > div.extendedRealm > span:nth-of-type(2) > span:first-of-type {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#vwRenderer .logEntry > div.cosmeticRealm > span:nth-of-type(2) > span:last-of-type {
|
#vwRenderer .logEntry > div.extendedRealm > span:nth-of-type(2) > span:last-of-type {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
#vwRenderer .logEntry > div.cosmeticRealm.isException > span:nth-of-type(2) > span:last-of-type {
|
#vwRenderer .logEntry > div.extendedRealm.isException > span:nth-of-type(2) > span:last-of-type {
|
||||||
text-decoration: line-through rgba(0,0,255,0.7);
|
text-decoration: line-through rgba(0,0,255,0.7);
|
||||||
}
|
}
|
||||||
#vwRenderer .logEntry > div > span:nth-of-type(3) {
|
#vwRenderer .logEntry > div > span:nth-of-type(3) {
|
||||||
|
@ -615,12 +615,12 @@ body[dir="rtl"] #netFilteringDialog > .headers > .tools {
|
||||||
#netFilteringDialog > .headers > .tools > span:hover {
|
#netFilteringDialog > .headers > .tools > span:hover {
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
#netFilteringDialog.cosmeticRealm > .headers > .dynamic,
|
#netFilteringDialog.extendedRealm > .headers > .dynamic,
|
||||||
#netFilteringDialog.cosmeticRealm > .panes > .dynamic {
|
#netFilteringDialog.extendedRealm > .panes > .dynamic {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#netFilteringDialog.cosmeticRealm > .headers > .static,
|
#netFilteringDialog.extendedRealm > .headers > .static,
|
||||||
#netFilteringDialog.cosmeticRealm > .panes > .static {
|
#netFilteringDialog.extendedRealm > .panes > .static {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#netFilteringDialog > div.panes {
|
#netFilteringDialog > div.panes {
|
||||||
|
|
|
@ -166,6 +166,7 @@ const µBlock = (( ) => { // jshint ignore:line
|
||||||
compiledCosmeticSection: 200,
|
compiledCosmeticSection: 200,
|
||||||
compiledScriptletSection: 300,
|
compiledScriptletSection: 300,
|
||||||
compiledHTMLSection: 400,
|
compiledHTMLSection: 400,
|
||||||
|
compiledHTTPHeaderSection: 500,
|
||||||
compiledSentinelSection: 1000,
|
compiledSentinelSection: 1000,
|
||||||
compiledBadSubsection: 1,
|
compiledBadSubsection: 1,
|
||||||
|
|
||||||
|
|
|
@ -549,6 +549,23 @@ const initHints = function() {
|
||||||
|
|
||||||
const getExtSelectorHints = function(cursor, line) {
|
const getExtSelectorHints = function(cursor, line) {
|
||||||
const beg = cursor.ch;
|
const beg = cursor.ch;
|
||||||
|
// Special selector case: `^responseheader`
|
||||||
|
{
|
||||||
|
const match = /#\^([a-z]+)$/.exec(line.slice(0, beg));
|
||||||
|
if (
|
||||||
|
match !== null &&
|
||||||
|
'responseheader'.startsWith(match[1]) &&
|
||||||
|
line.slice(beg) === ''
|
||||||
|
) {
|
||||||
|
return pickBestHints(
|
||||||
|
cursor,
|
||||||
|
match[1],
|
||||||
|
'',
|
||||||
|
[ 'responseheader()' ]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Procedural operators
|
||||||
const matchLeft = /#\^?.*:([^:]*)$/.exec(line.slice(0, beg));
|
const matchLeft = /#\^?.*:([^:]*)$/.exec(line.slice(0, beg));
|
||||||
const matchRight = /^([a-z-]*)\(?/.exec(line.slice(beg));
|
const matchRight = /^([a-z-]*)\(?/.exec(line.slice(beg));
|
||||||
if ( matchLeft === null || matchRight === null ) { return; }
|
if ( matchLeft === null || matchRight === null ) { return; }
|
||||||
|
@ -561,6 +578,18 @@ const initHints = function() {
|
||||||
return pickBestHints(cursor, matchLeft[1], matchRight[1], hints);
|
return pickBestHints(cursor, matchLeft[1], matchRight[1], hints);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getExtHeaderHints = function(cursor, line) {
|
||||||
|
const beg = cursor.ch;
|
||||||
|
const matchLeft = /#\^responseheader\((.*)$/.exec(line.slice(0, beg));
|
||||||
|
const matchRight = /^([^)]*)/.exec(line.slice(beg));
|
||||||
|
if ( matchLeft === null || matchRight === null ) { return; }
|
||||||
|
const hints = [];
|
||||||
|
for ( const hint of parser.removableHTTPHeaders ) {
|
||||||
|
hints.push(hint);
|
||||||
|
}
|
||||||
|
return pickBestHints(cursor, matchLeft[1], matchRight[1], hints);
|
||||||
|
};
|
||||||
|
|
||||||
const getExtScriptletHints = function(cursor, line) {
|
const getExtScriptletHints = function(cursor, line) {
|
||||||
const beg = cursor.ch;
|
const beg = cursor.ch;
|
||||||
const matchLeft = /#\+\js\(([^,]*)$/.exec(line.slice(0, beg));
|
const matchLeft = /#\+\js\(([^,]*)$/.exec(line.slice(0, beg));
|
||||||
|
@ -607,10 +636,12 @@ const initHints = function() {
|
||||||
let hints;
|
let hints;
|
||||||
if ( cursor.ch <= parser.slices[parser.optionsAnchorSpan.i+1] ) {
|
if ( cursor.ch <= parser.slices[parser.optionsAnchorSpan.i+1] ) {
|
||||||
hints = getOriginHints(cursor, line);
|
hints = getOriginHints(cursor, line);
|
||||||
|
} else if ( parser.hasFlavor(parser.BITFlavorExtScriptlet) ) {
|
||||||
|
hints = getExtScriptletHints(cursor, line);
|
||||||
|
} else if ( parser.hasFlavor(parser.BITFlavorExtResponseHeader) ) {
|
||||||
|
hints = getExtHeaderHints(cursor, line);
|
||||||
} else {
|
} else {
|
||||||
hints = parser.hasFlavor(parser.BITFlavorExtScriptlet)
|
hints = getExtSelectorHints(cursor, line);
|
||||||
? getExtScriptletHints(cursor, line)
|
|
||||||
: getExtSelectorHints(cursor, line);
|
|
||||||
}
|
}
|
||||||
return hints;
|
return hints;
|
||||||
}
|
}
|
||||||
|
|
|
@ -540,6 +540,15 @@ FilterContainer.prototype.compileSpecificSelector = function(
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
FilterContainer.prototype.compileTemporary = function(parser) {
|
||||||
|
return {
|
||||||
|
session: this.sessionFilterDB,
|
||||||
|
selector: parser.result.compiled,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
FilterContainer.prototype.fromCompiledContent = function(reader, options) {
|
FilterContainer.prototype.fromCompiledContent = function(reader, options) {
|
||||||
if ( options.skipCosmetic ) {
|
if ( options.skipCosmetic ) {
|
||||||
this.skipCompiledContent(reader);
|
this.skipCompiledContent(reader);
|
||||||
|
|
|
@ -237,12 +237,12 @@
|
||||||
µBlock.filteringContext
|
µBlock.filteringContext
|
||||||
.duplicate()
|
.duplicate()
|
||||||
.fromTabId(details.tabId)
|
.fromTabId(details.tabId)
|
||||||
.setRealm('cosmetic')
|
.setRealm('extended')
|
||||||
.setType('dom')
|
.setType('dom')
|
||||||
.setURL(details.url)
|
.setURL(details.url)
|
||||||
.setDocOriginFromURL(details.url)
|
.setDocOriginFromURL(details.url)
|
||||||
.setFilter({
|
.setFilter({
|
||||||
source: 'cosmetic',
|
source: 'extended',
|
||||||
raw: `${exception === 0 ? '##' : '#@#'}^${selector}`
|
raw: `${exception === 0 ? '##' : '#@#'}^${selector}`
|
||||||
})
|
})
|
||||||
.toLogger();
|
.toLogger();
|
||||||
|
@ -322,6 +322,13 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
api.compileTemporary = function(parser) {
|
||||||
|
return {
|
||||||
|
session: sessionFilterDB,
|
||||||
|
selector: parser.result.compiled,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
api.fromCompiledContent = function(reader) {
|
api.fromCompiledContent = function(reader) {
|
||||||
// Don't bother loading filters if stream filtering is not supported.
|
// Don't bother loading filters if stream filtering is not supported.
|
||||||
if ( µb.canFilterResponseData === false ) { return; }
|
if ( µb.canFilterResponseData === false ) { return; }
|
||||||
|
@ -348,15 +355,6 @@
|
||||||
api.retrieve = function(details) {
|
api.retrieve = function(details) {
|
||||||
const 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.
|
|
||||||
if (
|
|
||||||
µb.userSettings.advancedUserEnabled &&
|
|
||||||
µb.sessionFirewall.evaluateCellZY(hostname, hostname, '*') === 2
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const plains = new Set();
|
const plains = new Set();
|
||||||
const procedurals = new Set();
|
const procedurals = new Set();
|
||||||
const exceptions = new Set();
|
const exceptions = new Set();
|
||||||
|
@ -379,6 +377,15 @@
|
||||||
|
|
||||||
if ( plains.size === 0 && procedurals.size === 0 ) { return; }
|
if ( plains.size === 0 && procedurals.size === 0 ) { return; }
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/2835
|
||||||
|
// Do not filter if the site is under an `allow` rule.
|
||||||
|
if (
|
||||||
|
µb.userSettings.advancedUserEnabled &&
|
||||||
|
µb.sessionFirewall.evaluateCellZY(hostname, hostname, '*') === 2
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const out = { plains, procedurals };
|
const out = { plains, procedurals };
|
||||||
|
|
||||||
if ( exceptions.size === 0 ) {
|
if ( exceptions.size === 0 ) {
|
||||||
|
|
|
@ -29,6 +29,9 @@
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// TODO: fix the inconsistencies re. realm vs. filter source which have
|
||||||
|
// accumulated over time.
|
||||||
|
|
||||||
const messaging = vAPI.messaging;
|
const messaging = vAPI.messaging;
|
||||||
const logger = self.logger = { ownerId: Date.now() };
|
const logger = self.logger = { ownerId: Date.now() };
|
||||||
const logDate = new Date();
|
const logDate = new Date();
|
||||||
|
@ -341,6 +344,11 @@ const processLoggerEntries = function(response) {
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const parseLogEntry = function(details) {
|
const parseLogEntry = function(details) {
|
||||||
|
// Patch realm until changed all over codebase to make this unecessary
|
||||||
|
if ( details.realm === 'cosmetic' ) {
|
||||||
|
details.realm = 'extended';
|
||||||
|
}
|
||||||
|
|
||||||
const entry = new LogEntry(details);
|
const entry = new LogEntry(details);
|
||||||
|
|
||||||
// Assemble the text content, i.e. the pre-built string which will be
|
// Assemble the text content, i.e. the pre-built string which will be
|
||||||
|
@ -441,6 +449,8 @@ const viewPort = (( ) => {
|
||||||
const vwLogEntryTemplate = document.querySelector('#logEntryTemplate > div');
|
const vwLogEntryTemplate = document.querySelector('#logEntryTemplate > div');
|
||||||
const vwEntries = [];
|
const vwEntries = [];
|
||||||
|
|
||||||
|
const detailableRealms = new Set([ 'network', 'extended' ]);
|
||||||
|
|
||||||
let vwHeight = 0;
|
let vwHeight = 0;
|
||||||
let lineHeight = 0;
|
let lineHeight = 0;
|
||||||
let wholeHeight = 0;
|
let wholeHeight = 0;
|
||||||
|
@ -672,7 +682,7 @@ const viewPort = (( ) => {
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( details.realm === 'network' || details.realm === 'cosmetic' ) {
|
if ( detailableRealms.has(details.realm) ) {
|
||||||
divcl.add('canDetails');
|
divcl.add('canDetails');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -685,13 +695,13 @@ const viewPort = (( ) => {
|
||||||
}
|
}
|
||||||
if ( filteringType === 'static' ) {
|
if ( filteringType === 'static' ) {
|
||||||
divcl.add('canLookup');
|
divcl.add('canLookup');
|
||||||
if ( filter.modifier === true ) {
|
} else if ( details.realm === 'extended' ) {
|
||||||
div.setAttribute('data-modifier', '');
|
|
||||||
}
|
|
||||||
} else if ( filteringType === 'cosmetic' ) {
|
|
||||||
divcl.add('canLookup');
|
divcl.add('canLookup');
|
||||||
divcl.toggle('isException', filter.raw.startsWith('#@#'));
|
divcl.toggle('isException', filter.raw.startsWith('#@#'));
|
||||||
}
|
}
|
||||||
|
if ( filter.modifier === true ) {
|
||||||
|
div.setAttribute('data-modifier', '');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
span = div.children[1];
|
span = div.children[1];
|
||||||
if ( renderFilterToSpan(span, cells[1]) === false ) {
|
if ( renderFilterToSpan(span, cells[1]) === false ) {
|
||||||
|
@ -1575,7 +1585,7 @@ const reloadTab = function(ev) {
|
||||||
rawFilter: rawFilter,
|
rawFilter: rawFilter,
|
||||||
});
|
});
|
||||||
handleResponse(response);
|
handleResponse(response);
|
||||||
} else if ( targetRow.classList.contains('cosmeticRealm') ) {
|
} else if ( targetRow.classList.contains('extendedRealm') ) {
|
||||||
const response = await messaging.send('loggerUI', {
|
const response = await messaging.send('loggerUI', {
|
||||||
what: 'listsFromCosmeticFilter',
|
what: 'listsFromCosmeticFilter',
|
||||||
url: targetRow.children[6].textContent,
|
url: targetRow.children[6].textContent,
|
||||||
|
@ -1583,7 +1593,7 @@ const reloadTab = function(ev) {
|
||||||
});
|
});
|
||||||
handleResponse(response);
|
handleResponse(response);
|
||||||
}
|
}
|
||||||
} ;
|
};
|
||||||
|
|
||||||
const fillSummaryPane = function() {
|
const fillSummaryPane = function() {
|
||||||
const rows = dialog.querySelectorAll('.pane.details > div');
|
const rows = dialog.querySelectorAll('.pane.details > div');
|
||||||
|
@ -1595,7 +1605,7 @@ const reloadTab = function(ev) {
|
||||||
text = filterFromTargetRow();
|
text = filterFromTargetRow();
|
||||||
if (
|
if (
|
||||||
(text !== '') &&
|
(text !== '') &&
|
||||||
(trcl.contains('cosmeticRealm') || trcl.contains('networkRealm'))
|
(trcl.contains('extendedRealm') || trcl.contains('networkRealm'))
|
||||||
) {
|
) {
|
||||||
toSummaryPaneFilterNode(rows[0], text);
|
toSummaryPaneFilterNode(rows[0], text);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1607,7 +1617,7 @@ const reloadTab = function(ev) {
|
||||||
(
|
(
|
||||||
trcl.contains('dynamicHost') ||
|
trcl.contains('dynamicHost') ||
|
||||||
trcl.contains('dynamicUrl') ||
|
trcl.contains('dynamicUrl') ||
|
||||||
trcl.contains('switch')
|
trcl.contains('switchRealm')
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
rows[2].children[1].textContent = text;
|
rows[2].children[1].textContent = text;
|
||||||
|
@ -1677,7 +1687,9 @@ const reloadTab = function(ev) {
|
||||||
|
|
||||||
// Fill dynamic URL filtering pane
|
// Fill dynamic URL filtering pane
|
||||||
const fillDynamicPane = function() {
|
const fillDynamicPane = function() {
|
||||||
if ( targetRow.classList.contains('cosmeticRealm') ) { return; }
|
if ( targetRow.classList.contains('extendedRealm') ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/662#issuecomment-509220702
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/662#issuecomment-509220702
|
||||||
if ( targetType === 'doc' ) { return; }
|
if ( targetType === 'doc' ) { return; }
|
||||||
|
@ -1712,8 +1724,6 @@ const reloadTab = function(ev) {
|
||||||
}
|
}
|
||||||
|
|
||||||
colorize();
|
colorize();
|
||||||
|
|
||||||
uDom('#modalOverlayContainer [data-pane="dynamic"]').removeClass('hide');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const fillOriginSelect = function(select, hostname, domain) {
|
const fillOriginSelect = function(select, hostname, domain) {
|
||||||
|
@ -1733,7 +1743,9 @@ const reloadTab = function(ev) {
|
||||||
|
|
||||||
// Fill static filtering pane
|
// Fill static filtering pane
|
||||||
const fillStaticPane = function() {
|
const fillStaticPane = function() {
|
||||||
if ( targetRow.classList.contains('cosmeticRealm') ) { return; }
|
if ( targetRow.classList.contains('extendedRealm') ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const template = vAPI.i18n('loggerStaticFilteringSentence');
|
const template = vAPI.i18n('loggerStaticFilteringSentence');
|
||||||
const rePlaceholder = /\{\{[^}]+?\}\}/g;
|
const rePlaceholder = /\{\{[^}]+?\}\}/g;
|
||||||
|
@ -1842,8 +1854,8 @@ const reloadTab = function(ev) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
dialog.classList.toggle(
|
dialog.classList.toggle(
|
||||||
'cosmeticRealm',
|
'extendedRealm',
|
||||||
targetRow.classList.contains('cosmeticRealm')
|
targetRow.classList.contains('extendedRealm')
|
||||||
);
|
);
|
||||||
targetDomain = domains[0];
|
targetDomain = domains[0];
|
||||||
targetPageDomain = domains[1];
|
targetPageDomain = domains[1];
|
||||||
|
@ -2403,10 +2415,10 @@ const popupManager = (( ) => {
|
||||||
// Filter hit stats' MVP ("minimum viable product")
|
// Filter hit stats' MVP ("minimum viable product")
|
||||||
//
|
//
|
||||||
const loggerStats = (( ) => {
|
const loggerStats = (( ) => {
|
||||||
|
const enabled = false;
|
||||||
const filterHits = new Map();
|
const filterHits = new Map();
|
||||||
let dialog;
|
let dialog;
|
||||||
let timer;
|
let timer;
|
||||||
|
|
||||||
const makeRow = function() {
|
const makeRow = function() {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.appendChild(document.createElement('span'));
|
div.appendChild(document.createElement('span'));
|
||||||
|
@ -2470,6 +2482,7 @@ const loggerStats = (( ) => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
processFilter: function(filter) {
|
processFilter: function(filter) {
|
||||||
|
if ( enabled !== true ) { return; }
|
||||||
if ( filter.source !== 'static' && filter.source !== 'cosmetic' ) {
|
if ( filter.source !== 'static' && filter.source !== 'cosmetic' ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1440,21 +1440,14 @@ const getURLFilteringData = function(details) {
|
||||||
const compileTemporaryException = function(filter) {
|
const compileTemporaryException = function(filter) {
|
||||||
const parser = new vAPI.StaticFilteringParser();
|
const parser = new vAPI.StaticFilteringParser();
|
||||||
parser.analyze(filter);
|
parser.analyze(filter);
|
||||||
if ( parser.shouldDiscard() ) { return {}; }
|
if ( parser.shouldDiscard() ) { return; }
|
||||||
let selector = parser.result.compiled;
|
return µb.staticExtFilteringEngine.compileTemporary(parser);
|
||||||
let session;
|
|
||||||
if ( (parser.flavorBits & parser.BITFlavorExtScriptlet) !== 0 ) {
|
|
||||||
session = µb.scriptletFilteringEngine.getSession();
|
|
||||||
} else if ( (parser.flavorBits & parser.BITFlavorExtHTML) !== 0 ) {
|
|
||||||
session = µb.htmlFilteringEngine.getSession();
|
|
||||||
} else {
|
|
||||||
session = µb.cosmeticFilteringEngine.getSession();
|
|
||||||
}
|
|
||||||
return { session, selector };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleTemporaryException = function(details) {
|
const toggleTemporaryException = function(details) {
|
||||||
const { session, selector } = compileTemporaryException(details.filter);
|
const result = compileTemporaryException(details.filter);
|
||||||
|
if ( result === undefined ) { return false; }
|
||||||
|
const { session, selector } = result;
|
||||||
if ( session.has(1, selector) ) {
|
if ( session.has(1, selector) ) {
|
||||||
session.remove(1, selector);
|
session.remove(1, selector);
|
||||||
return false;
|
return false;
|
||||||
|
@ -1464,7 +1457,9 @@ const toggleTemporaryException = function(details) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasTemporaryException = function(details) {
|
const hasTemporaryException = function(details) {
|
||||||
const { session, selector } = compileTemporaryException(details.filter);
|
const result = compileTemporaryException(details.filter);
|
||||||
|
if ( result === undefined ) { return false; }
|
||||||
|
const { session, selector } = result;
|
||||||
return session && session.has(1, selector);
|
return session && session.has(1, selector);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -221,6 +221,7 @@ if (
|
||||||
// Generic exception
|
// Generic exception
|
||||||
case 8:
|
case 8:
|
||||||
// HTML filtering
|
// HTML filtering
|
||||||
|
// Response header filtering
|
||||||
case 64:
|
case 64:
|
||||||
if ( exception !== ((fargs[2] & 0b001) !== 0) ) { break; }
|
if ( exception !== ((fargs[2] & 0b001) !== 0) ) { break; }
|
||||||
isProcedural = (fargs[2] & 0b010) !== 0;
|
isProcedural = (fargs[2] & 0b010) !== 0;
|
||||||
|
|
|
@ -206,12 +206,12 @@
|
||||||
µBlock.filteringContext
|
µBlock.filteringContext
|
||||||
.duplicate()
|
.duplicate()
|
||||||
.fromTabId(details.tabId)
|
.fromTabId(details.tabId)
|
||||||
.setRealm('cosmetic')
|
.setRealm('extended')
|
||||||
.setType('dom')
|
.setType('dom')
|
||||||
.setURL(details.url)
|
.setURL(details.url)
|
||||||
.setDocOriginFromURL(details.url)
|
.setDocOriginFromURL(details.url)
|
||||||
.setFilter({
|
.setFilter({
|
||||||
source: 'cosmetic',
|
source: 'extended',
|
||||||
raw: (isException ? '#@#' : '##') + `+js(${token})`
|
raw: (isException ? '#@#' : '##') + `+js(${token})`
|
||||||
})
|
})
|
||||||
.toLogger();
|
.toLogger();
|
||||||
|
@ -263,6 +263,13 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
api.compileTemporary = function(parser) {
|
||||||
|
return {
|
||||||
|
session: sessionScriptletDB,
|
||||||
|
selector: parser.result.compiled,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// 01234567890123456789
|
// 01234567890123456789
|
||||||
// +js(token[, arg[, ...]])
|
// +js(token[, arg[, ...]])
|
||||||
// ^ ^
|
// ^ ^
|
||||||
|
@ -302,15 +309,6 @@
|
||||||
|
|
||||||
const 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.
|
|
||||||
if (
|
|
||||||
µb.userSettings.advancedUserEnabled &&
|
|
||||||
µb.sessionFirewall.evaluateCellZY(hostname, hostname, '*') === 2
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scriptlets.clear();
|
$scriptlets.clear();
|
||||||
$exceptions.clear();
|
$exceptions.clear();
|
||||||
|
|
||||||
|
@ -324,6 +322,15 @@
|
||||||
scriptletDB.retrieve(entity, [ $scriptlets, $exceptions ], 1);
|
scriptletDB.retrieve(entity, [ $scriptlets, $exceptions ], 1);
|
||||||
if ( $scriptlets.size === 0 ) { return; }
|
if ( $scriptlets.size === 0 ) { return; }
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/2835
|
||||||
|
// Do not inject scriptlets if the site is under an `allow` rule.
|
||||||
|
if (
|
||||||
|
µb.userSettings.advancedUserEnabled &&
|
||||||
|
µb.sessionFirewall.evaluateCellZY(hostname, hostname, '*') === 2
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const loggerEnabled = µb.logger.enabled;
|
const loggerEnabled = µb.logger.enabled;
|
||||||
|
|
||||||
// Wholly disable scriptlet injection?
|
// Wholly disable scriptlet injection?
|
||||||
|
|
|
@ -59,11 +59,13 @@
|
||||||
get acceptedCount() {
|
get acceptedCount() {
|
||||||
return µb.cosmeticFilteringEngine.acceptedCount +
|
return µb.cosmeticFilteringEngine.acceptedCount +
|
||||||
µb.scriptletFilteringEngine.acceptedCount +
|
µb.scriptletFilteringEngine.acceptedCount +
|
||||||
|
µb.httpheaderFilteringEngine.acceptedCount +
|
||||||
µb.htmlFilteringEngine.acceptedCount;
|
µb.htmlFilteringEngine.acceptedCount;
|
||||||
},
|
},
|
||||||
get discardedCount() {
|
get discardedCount() {
|
||||||
return µb.cosmeticFilteringEngine.discardedCount +
|
return µb.cosmeticFilteringEngine.discardedCount +
|
||||||
µb.scriptletFilteringEngine.discardedCount +
|
µb.scriptletFilteringEngine.discardedCount +
|
||||||
|
µb.httpheaderFilteringEngine.discardedCount +
|
||||||
µb.htmlFilteringEngine.discardedCount;
|
µb.htmlFilteringEngine.discardedCount;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -137,7 +139,7 @@
|
||||||
this.timer = undefined;
|
this.timer = undefined;
|
||||||
this.strToIdMap.clear();
|
this.strToIdMap.clear();
|
||||||
},
|
},
|
||||||
{ timeout: 10000 }
|
{ timeout: 5000 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +183,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
fromSelfie(selfie) {
|
fromSelfie(selfie) {
|
||||||
|
if ( selfie === undefined ) { return; }
|
||||||
this.hostnameToSlotIdMap = new Map(selfie.hostnameToSlotIdMap);
|
this.hostnameToSlotIdMap = new Map(selfie.hostnameToSlotIdMap);
|
||||||
this.hostnameSlots = selfie.hostnameSlots;
|
this.hostnameSlots = selfie.hostnameSlots;
|
||||||
this.strSlots = selfie.strSlots;
|
this.strSlots = selfie.strSlots;
|
||||||
|
@ -239,12 +242,14 @@
|
||||||
api.reset = function() {
|
api.reset = function() {
|
||||||
µb.cosmeticFilteringEngine.reset();
|
µb.cosmeticFilteringEngine.reset();
|
||||||
µb.scriptletFilteringEngine.reset();
|
µb.scriptletFilteringEngine.reset();
|
||||||
|
µb.httpheaderFilteringEngine.reset();
|
||||||
µb.htmlFilteringEngine.reset();
|
µb.htmlFilteringEngine.reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
api.freeze = function() {
|
api.freeze = function() {
|
||||||
µb.cosmeticFilteringEngine.freeze();
|
µb.cosmeticFilteringEngine.freeze();
|
||||||
µb.scriptletFilteringEngine.freeze();
|
µb.scriptletFilteringEngine.freeze();
|
||||||
|
µb.httpheaderFilteringEngine.freeze();
|
||||||
µb.htmlFilteringEngine.freeze();
|
µb.htmlFilteringEngine.freeze();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -267,6 +272,12 @@
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Response header filtering
|
||||||
|
if ( (parser.flavorBits & parser.BITFlavorExtResponseHeader) !== 0 ) {
|
||||||
|
µb.httpheaderFilteringEngine.compile(parser, writer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// HTML filtering
|
// HTML filtering
|
||||||
// TODO: evaluate converting Adguard's `$$` syntax into uBO's HTML
|
// TODO: evaluate converting Adguard's `$$` syntax into uBO's HTML
|
||||||
// filtering syntax.
|
// filtering syntax.
|
||||||
|
@ -280,9 +291,23 @@
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
api.compileTemporary = function(parser) {
|
||||||
|
if ( (parser.flavorBits & parser.BITFlavorExtScriptlet) !== 0 ) {
|
||||||
|
return µb.scriptletFilteringEngine.compileTemporary(parser);
|
||||||
|
}
|
||||||
|
if ( (parser.flavorBits & parser.BITFlavorExtResponseHeader) !== 0 ) {
|
||||||
|
return µb.httpheaderFilteringEngine.compileTemporary(parser);
|
||||||
|
}
|
||||||
|
if ( (parser.flavorBits & parser.BITFlavorExtHTML) !== 0 ) {
|
||||||
|
return µb.htmlFilteringEngine.compileTemporary(parser);
|
||||||
|
}
|
||||||
|
return µb.cosmeticFilteringEngine.compileTemporary(parser);
|
||||||
|
};
|
||||||
|
|
||||||
api.fromCompiledContent = function(reader, options) {
|
api.fromCompiledContent = function(reader, options) {
|
||||||
µb.cosmeticFilteringEngine.fromCompiledContent(reader, options);
|
µb.cosmeticFilteringEngine.fromCompiledContent(reader, options);
|
||||||
µb.scriptletFilteringEngine.fromCompiledContent(reader, options);
|
µb.scriptletFilteringEngine.fromCompiledContent(reader, options);
|
||||||
|
µb.httpheaderFilteringEngine.fromCompiledContent(reader, options);
|
||||||
µb.htmlFilteringEngine.fromCompiledContent(reader, options);
|
µb.htmlFilteringEngine.fromCompiledContent(reader, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -292,7 +317,8 @@
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
cosmetic: µb.cosmeticFilteringEngine.toSelfie(),
|
cosmetic: µb.cosmeticFilteringEngine.toSelfie(),
|
||||||
scriptlets: µb.scriptletFilteringEngine.toSelfie(),
|
scriptlets: µb.scriptletFilteringEngine.toSelfie(),
|
||||||
html: µb.htmlFilteringEngine.toSelfie()
|
httpHeaders: µb.httpheaderFilteringEngine.toSelfie(),
|
||||||
|
html: µb.htmlFilteringEngine.toSelfie(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -307,6 +333,7 @@
|
||||||
if ( selfie instanceof Object === false ) { return false; }
|
if ( selfie instanceof Object === false ) { return false; }
|
||||||
µb.cosmeticFilteringEngine.fromSelfie(selfie.cosmetic);
|
µb.cosmeticFilteringEngine.fromSelfie(selfie.cosmetic);
|
||||||
µb.scriptletFilteringEngine.fromSelfie(selfie.scriptlets);
|
µb.scriptletFilteringEngine.fromSelfie(selfie.scriptlets);
|
||||||
|
µb.httpheaderFilteringEngine.fromSelfie(selfie.httpHeaders);
|
||||||
µb.htmlFilteringEngine.fromSelfie(selfie.html);
|
µb.htmlFilteringEngine.fromSelfie(selfie.html);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
|
@ -304,6 +304,20 @@ const Parser = class {
|
||||||
}
|
}
|
||||||
// ##^...
|
// ##^...
|
||||||
if ( hasBits(this.slices[i], BITCaret) ) {
|
if ( hasBits(this.slices[i], BITCaret) ) {
|
||||||
|
// ##^responseheader(...)
|
||||||
|
if (
|
||||||
|
selector.startsWith('^responseheader(') &&
|
||||||
|
selector.endsWith(')')
|
||||||
|
) {
|
||||||
|
this.flavorBits |= BITFlavorExtResponseHeader;
|
||||||
|
this.result.raw = selector.slice(1);
|
||||||
|
const headerName = selector.slice(16, -1).trim().toLowerCase();
|
||||||
|
this.result.compiled = `responseheader(${headerName})`;
|
||||||
|
if ( this.removableHTTPHeaders.has(headerName) === false ) {
|
||||||
|
this.flavorBits |= BITFlavorUnsupported;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.flavorBits |= BITFlavorExtHTML;
|
this.flavorBits |= BITFlavorExtHTML;
|
||||||
selector = selector.slice(1);
|
selector = selector.slice(1);
|
||||||
}
|
}
|
||||||
|
@ -1244,6 +1258,16 @@ const Parser = class {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
Parser.removableHTTPHeaders = Parser.prototype.removableHTTPHeaders = new Set([
|
||||||
|
'',
|
||||||
|
'location',
|
||||||
|
'report-to',
|
||||||
|
'refresh',
|
||||||
|
'set-cookie',
|
||||||
|
]);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/1004
|
// https://github.com/chrisaljoudi/uBlock/issues/1004
|
||||||
// Detect and report invalid CSS selectors.
|
// Detect and report invalid CSS selectors.
|
||||||
|
|
||||||
|
@ -1978,6 +2002,7 @@ const BITFlavorExtStrong = 1 << 8;
|
||||||
const BITFlavorExtCosmetic = 1 << 9;
|
const BITFlavorExtCosmetic = 1 << 9;
|
||||||
const BITFlavorExtScriptlet = 1 << 10;
|
const BITFlavorExtScriptlet = 1 << 10;
|
||||||
const BITFlavorExtHTML = 1 << 11;
|
const BITFlavorExtHTML = 1 << 11;
|
||||||
|
const BITFlavorExtResponseHeader = 1 << 12;
|
||||||
const BITFlavorIgnore = 1 << 29;
|
const BITFlavorIgnore = 1 << 29;
|
||||||
const BITFlavorUnsupported = 1 << 30;
|
const BITFlavorUnsupported = 1 << 30;
|
||||||
const BITFlavorError = 1 << 31;
|
const BITFlavorError = 1 << 31;
|
||||||
|
@ -2087,6 +2112,7 @@ Parser.prototype.BITFlavorExtStrong = BITFlavorExtStrong;
|
||||||
Parser.prototype.BITFlavorExtCosmetic = BITFlavorExtCosmetic;
|
Parser.prototype.BITFlavorExtCosmetic = BITFlavorExtCosmetic;
|
||||||
Parser.prototype.BITFlavorExtScriptlet = BITFlavorExtScriptlet;
|
Parser.prototype.BITFlavorExtScriptlet = BITFlavorExtScriptlet;
|
||||||
Parser.prototype.BITFlavorExtHTML = BITFlavorExtHTML;
|
Parser.prototype.BITFlavorExtHTML = BITFlavorExtHTML;
|
||||||
|
Parser.prototype.BITFlavorExtResponseHeader = BITFlavorExtResponseHeader;
|
||||||
Parser.prototype.BITFlavorIgnore = BITFlavorIgnore;
|
Parser.prototype.BITFlavorIgnore = BITFlavorIgnore;
|
||||||
Parser.prototype.BITFlavorUnsupported = BITFlavorUnsupported;
|
Parser.prototype.BITFlavorUnsupported = BITFlavorUnsupported;
|
||||||
Parser.prototype.BITFlavorError = BITFlavorError;
|
Parser.prototype.BITFlavorError = BITFlavorError;
|
||||||
|
|
|
@ -508,7 +508,8 @@ const onHeadersReceived = function(details) {
|
||||||
// Keep in mind response headers will be modified in-place if needed, so
|
// Keep in mind response headers will be modified in-place if needed, so
|
||||||
// `details.responseHeaders` will always point to the modified response
|
// `details.responseHeaders` will always point to the modified response
|
||||||
// headers.
|
// headers.
|
||||||
const responseHeaders = details.responseHeaders;
|
const { responseHeaders } = details;
|
||||||
|
if ( Array.isArray(responseHeaders) === false ) { return; }
|
||||||
|
|
||||||
if ( isRootDoc === false && µb.hiddenSettings.filterOnHeaders === true ) {
|
if ( isRootDoc === false && µb.hiddenSettings.filterOnHeaders === true ) {
|
||||||
const result = pageStore.filterOnHeaders(fctxt, responseHeaders);
|
const result = pageStore.filterOnHeaders(fctxt, responseHeaders);
|
||||||
|
@ -540,10 +541,16 @@ const onHeadersReceived = function(details) {
|
||||||
|
|
||||||
// At this point we have a HTML document.
|
// At this point we have a HTML document.
|
||||||
|
|
||||||
const filteredHTML = µb.canFilterResponseData &&
|
const filteredHTML =
|
||||||
filterDocument(pageStore, fctxt, details) === true;
|
µb.canFilterResponseData && filterDocument(fctxt, details) === true;
|
||||||
|
|
||||||
let modifiedHeaders = injectCSP(fctxt, pageStore, responseHeaders) === true;
|
let modifiedHeaders = false;
|
||||||
|
if ( µb.httpheaderFilteringEngine.apply(fctxt, responseHeaders) === true ) {
|
||||||
|
modifiedHeaders = true;
|
||||||
|
}
|
||||||
|
if ( injectCSP(fctxt, pageStore, responseHeaders) === true ) {
|
||||||
|
modifiedHeaders = true;
|
||||||
|
}
|
||||||
|
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1376932
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1376932
|
||||||
// Prevent document from being cached by the browser if we modified it,
|
// Prevent document from being cached by the browser if we modified it,
|
||||||
|
@ -552,7 +559,7 @@ const onHeadersReceived = function(details) {
|
||||||
// Use `no-cache` instead of `no-cache, no-store, must-revalidate`, this
|
// Use `no-cache` instead of `no-cache, no-store, must-revalidate`, this
|
||||||
// allows Firefox's offline mode to work as expected.
|
// allows Firefox's offline mode to work as expected.
|
||||||
if ( (filteredHTML || modifiedHeaders) && dontCacheResponseHeaders ) {
|
if ( (filteredHTML || modifiedHeaders) && dontCacheResponseHeaders ) {
|
||||||
let cacheControl = µb.hiddenSettings.cacheControlForFirefox1376932;
|
const cacheControl = µb.hiddenSettings.cacheControlForFirefox1376932;
|
||||||
if ( cacheControl !== 'unset' ) {
|
if ( cacheControl !== 'unset' ) {
|
||||||
let i = headerIndexFromName('cache-control', responseHeaders);
|
let i = headerIndexFromName('cache-control', responseHeaders);
|
||||||
if ( i !== -1 ) {
|
if ( i !== -1 ) {
|
||||||
|
@ -565,7 +572,7 @@ const onHeadersReceived = function(details) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( modifiedHeaders ) {
|
if ( modifiedHeaders ) {
|
||||||
return { responseHeaders: responseHeaders };
|
return { responseHeaders };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -614,7 +621,7 @@ const normalizeBehindTheSceneResponseHeaders = function(details) {
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
const filterDocument = (function() {
|
const filterDocument = (( ) => {
|
||||||
const µb = µBlock;
|
const µb = µBlock;
|
||||||
const filterers = new Map();
|
const filterers = new Map();
|
||||||
let domParser, xmlSerializer,
|
let domParser, xmlSerializer,
|
||||||
|
@ -805,7 +812,7 @@ const filterDocument = (function() {
|
||||||
filterers.delete(this);
|
filterers.delete(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
return function(pageStore, fctxt, extras) {
|
return function(fctxt, extras) {
|
||||||
// https://github.com/gorhill/uBlock/issues/3478
|
// https://github.com/gorhill/uBlock/issues/3478
|
||||||
const statusCode = extras.statusCode || 0;
|
const statusCode = extras.statusCode || 0;
|
||||||
if ( statusCode !== 0 && (statusCode < 200 || statusCode >= 300) ) {
|
if ( statusCode !== 0 && (statusCode < 200 || statusCode >= 300) ) {
|
||||||
|
|
Loading…
Reference in New Issue