diff --git a/src/background.html b/src/background.html
index 7d183dfa446e1..393b15024b8f4 100644
--- a/src/background.html
+++ b/src/background.html
@@ -34,6 +34,7 @@
+
diff --git a/src/css/logger-ui.css b/src/css/logger-ui.css
index c0941bdaf8263..9ea2b3317189f 100644
--- a/src/css/logger-ui.css
+++ b/src/css/logger-ui.css
@@ -260,11 +260,11 @@ body.colorBlind #netFilteringDialog > .panes > .details > div[data-status="2"] {
#vwRenderer .logEntry > div[data-tabid="-1"] {
text-shadow: 0 0.2em 0.4em #aaa;
}
-#vwRenderer .logEntry > div.cosmeticRealm,
+#vwRenderer .logEntry > div.extendedRealm,
#vwRenderer .logEntry > div.redirect {
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 {
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) {
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;
}
-#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;
}
-#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);
}
#vwRenderer .logEntry > div > span:nth-of-type(3) {
@@ -615,12 +615,12 @@ body[dir="rtl"] #netFilteringDialog > .headers > .tools {
#netFilteringDialog > .headers > .tools > span:hover {
background-color: #eee;
}
-#netFilteringDialog.cosmeticRealm > .headers > .dynamic,
-#netFilteringDialog.cosmeticRealm > .panes > .dynamic {
+#netFilteringDialog.extendedRealm > .headers > .dynamic,
+#netFilteringDialog.extendedRealm > .panes > .dynamic {
display: none;
}
-#netFilteringDialog.cosmeticRealm > .headers > .static,
-#netFilteringDialog.cosmeticRealm > .panes > .static {
+#netFilteringDialog.extendedRealm > .headers > .static,
+#netFilteringDialog.extendedRealm > .panes > .static {
display: none;
}
#netFilteringDialog > div.panes {
diff --git a/src/js/background.js b/src/js/background.js
index df1621f1d808b..06710d4176231 100644
--- a/src/js/background.js
+++ b/src/js/background.js
@@ -166,6 +166,7 @@ const µBlock = (( ) => { // jshint ignore:line
compiledCosmeticSection: 200,
compiledScriptletSection: 300,
compiledHTMLSection: 400,
+ compiledHTTPHeaderSection: 500,
compiledSentinelSection: 1000,
compiledBadSubsection: 1,
diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js
index 891c3c072d0bf..eebc811256fb8 100644
--- a/src/js/codemirror/ubo-static-filtering.js
+++ b/src/js/codemirror/ubo-static-filtering.js
@@ -549,6 +549,23 @@ const initHints = function() {
const getExtSelectorHints = function(cursor, line) {
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 matchRight = /^([a-z-]*)\(?/.exec(line.slice(beg));
if ( matchLeft === null || matchRight === null ) { return; }
@@ -561,6 +578,18 @@ const initHints = function() {
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 beg = cursor.ch;
const matchLeft = /#\+\js\(([^,]*)$/.exec(line.slice(0, beg));
@@ -607,10 +636,12 @@ const initHints = function() {
let hints;
if ( cursor.ch <= parser.slices[parser.optionsAnchorSpan.i+1] ) {
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 {
- hints = parser.hasFlavor(parser.BITFlavorExtScriptlet)
- ? getExtScriptletHints(cursor, line)
- : getExtSelectorHints(cursor, line);
+ hints = getExtSelectorHints(cursor, line);
}
return hints;
}
diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js
index 4a9e4cabefcef..bb4340ec141df 100644
--- a/src/js/cosmetic-filtering.js
+++ b/src/js/cosmetic-filtering.js
@@ -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) {
if ( options.skipCosmetic ) {
this.skipCompiledContent(reader);
diff --git a/src/js/html-filtering.js b/src/js/html-filtering.js
index 3d0f03cc97cc7..8431d670ce364 100644
--- a/src/js/html-filtering.js
+++ b/src/js/html-filtering.js
@@ -237,12 +237,12 @@
µBlock.filteringContext
.duplicate()
.fromTabId(details.tabId)
- .setRealm('cosmetic')
+ .setRealm('extended')
.setType('dom')
.setURL(details.url)
.setDocOriginFromURL(details.url)
.setFilter({
- source: 'cosmetic',
+ source: 'extended',
raw: `${exception === 0 ? '##' : '#@#'}^${selector}`
})
.toLogger();
@@ -322,6 +322,13 @@
}
};
+ api.compileTemporary = function(parser) {
+ return {
+ session: sessionFilterDB,
+ selector: parser.result.compiled,
+ };
+ };
+
api.fromCompiledContent = function(reader) {
// Don't bother loading filters if stream filtering is not supported.
if ( µb.canFilterResponseData === false ) { return; }
@@ -348,15 +355,6 @@
api.retrieve = function(details) {
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 procedurals = new Set();
const exceptions = new Set();
@@ -379,6 +377,15 @@
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 };
if ( exceptions.size === 0 ) {
diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js
index aae02f3e15099..59f19a2e789fe 100644
--- a/src/js/logger-ui.js
+++ b/src/js/logger-ui.js
@@ -29,6 +29,9 @@
/******************************************************************************/
+// TODO: fix the inconsistencies re. realm vs. filter source which have
+// accumulated over time.
+
const messaging = vAPI.messaging;
const logger = self.logger = { ownerId: Date.now() };
const logDate = new Date();
@@ -341,6 +344,11 @@ const processLoggerEntries = function(response) {
/******************************************************************************/
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);
// 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 vwEntries = [];
+ const detailableRealms = new Set([ 'network', 'extended' ]);
+
let vwHeight = 0;
let lineHeight = 0;
let wholeHeight = 0;
@@ -672,7 +682,7 @@ const viewPort = (( ) => {
return div;
}
- if ( details.realm === 'network' || details.realm === 'cosmetic' ) {
+ if ( detailableRealms.has(details.realm) ) {
divcl.add('canDetails');
}
@@ -685,13 +695,13 @@ const viewPort = (( ) => {
}
if ( filteringType === 'static' ) {
divcl.add('canLookup');
- if ( filter.modifier === true ) {
- div.setAttribute('data-modifier', '');
- }
- } else if ( filteringType === 'cosmetic' ) {
+ } else if ( details.realm === 'extended' ) {
divcl.add('canLookup');
divcl.toggle('isException', filter.raw.startsWith('#@#'));
}
+ if ( filter.modifier === true ) {
+ div.setAttribute('data-modifier', '');
+ }
}
span = div.children[1];
if ( renderFilterToSpan(span, cells[1]) === false ) {
@@ -1575,7 +1585,7 @@ const reloadTab = function(ev) {
rawFilter: rawFilter,
});
handleResponse(response);
- } else if ( targetRow.classList.contains('cosmeticRealm') ) {
+ } else if ( targetRow.classList.contains('extendedRealm') ) {
const response = await messaging.send('loggerUI', {
what: 'listsFromCosmeticFilter',
url: targetRow.children[6].textContent,
@@ -1583,7 +1593,7 @@ const reloadTab = function(ev) {
});
handleResponse(response);
}
- } ;
+ };
const fillSummaryPane = function() {
const rows = dialog.querySelectorAll('.pane.details > div');
@@ -1595,7 +1605,7 @@ const reloadTab = function(ev) {
text = filterFromTargetRow();
if (
(text !== '') &&
- (trcl.contains('cosmeticRealm') || trcl.contains('networkRealm'))
+ (trcl.contains('extendedRealm') || trcl.contains('networkRealm'))
) {
toSummaryPaneFilterNode(rows[0], text);
} else {
@@ -1607,7 +1617,7 @@ const reloadTab = function(ev) {
(
trcl.contains('dynamicHost') ||
trcl.contains('dynamicUrl') ||
- trcl.contains('switch')
+ trcl.contains('switchRealm')
)
) {
rows[2].children[1].textContent = text;
@@ -1677,7 +1687,9 @@ const reloadTab = function(ev) {
// Fill dynamic URL filtering pane
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
if ( targetType === 'doc' ) { return; }
@@ -1712,8 +1724,6 @@ const reloadTab = function(ev) {
}
colorize();
-
- uDom('#modalOverlayContainer [data-pane="dynamic"]').removeClass('hide');
};
const fillOriginSelect = function(select, hostname, domain) {
@@ -1733,7 +1743,9 @@ const reloadTab = function(ev) {
// Fill static filtering pane
const fillStaticPane = function() {
- if ( targetRow.classList.contains('cosmeticRealm') ) { return; }
+ if ( targetRow.classList.contains('extendedRealm') ) {
+ return;
+ }
const template = vAPI.i18n('loggerStaticFilteringSentence');
const rePlaceholder = /\{\{[^}]+?\}\}/g;
@@ -1842,8 +1854,8 @@ const reloadTab = function(ev) {
}
);
dialog.classList.toggle(
- 'cosmeticRealm',
- targetRow.classList.contains('cosmeticRealm')
+ 'extendedRealm',
+ targetRow.classList.contains('extendedRealm')
);
targetDomain = domains[0];
targetPageDomain = domains[1];
@@ -2403,10 +2415,10 @@ const popupManager = (( ) => {
// Filter hit stats' MVP ("minimum viable product")
//
const loggerStats = (( ) => {
+ const enabled = false;
const filterHits = new Map();
let dialog;
let timer;
-
const makeRow = function() {
const div = document.createElement('div');
div.appendChild(document.createElement('span'));
@@ -2470,6 +2482,7 @@ const loggerStats = (( ) => {
return {
processFilter: function(filter) {
+ if ( enabled !== true ) { return; }
if ( filter.source !== 'static' && filter.source !== 'cosmetic' ) {
return;
}
diff --git a/src/js/messaging.js b/src/js/messaging.js
index a0c9a1e4f85cd..1dce1fe312295 100644
--- a/src/js/messaging.js
+++ b/src/js/messaging.js
@@ -1440,21 +1440,14 @@ const getURLFilteringData = function(details) {
const compileTemporaryException = function(filter) {
const parser = new vAPI.StaticFilteringParser();
parser.analyze(filter);
- if ( parser.shouldDiscard() ) { return {}; }
- let selector = parser.result.compiled;
- 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 };
+ if ( parser.shouldDiscard() ) { return; }
+ return µb.staticExtFilteringEngine.compileTemporary(parser);
};
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) ) {
session.remove(1, selector);
return false;
@@ -1464,7 +1457,9 @@ const toggleTemporaryException = 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);
};
diff --git a/src/js/reverselookup.js b/src/js/reverselookup.js
index 7a228231ad227..52d79da8cfd10 100644
--- a/src/js/reverselookup.js
+++ b/src/js/reverselookup.js
@@ -221,6 +221,7 @@ if (
// Generic exception
case 8:
// HTML filtering
+ // Response header filtering
case 64:
if ( exception !== ((fargs[2] & 0b001) !== 0) ) { break; }
isProcedural = (fargs[2] & 0b010) !== 0;
diff --git a/src/js/scriptlet-filtering.js b/src/js/scriptlet-filtering.js
index 39b38396ba6d0..1f50c9b10c6ab 100644
--- a/src/js/scriptlet-filtering.js
+++ b/src/js/scriptlet-filtering.js
@@ -206,12 +206,12 @@
µBlock.filteringContext
.duplicate()
.fromTabId(details.tabId)
- .setRealm('cosmetic')
+ .setRealm('extended')
.setType('dom')
.setURL(details.url)
.setDocOriginFromURL(details.url)
.setFilter({
- source: 'cosmetic',
+ source: 'extended',
raw: (isException ? '#@#' : '##') + `+js(${token})`
})
.toLogger();
@@ -263,10 +263,17 @@
}
};
+ api.compileTemporary = function(parser) {
+ return {
+ session: sessionScriptletDB,
+ selector: parser.result.compiled,
+ };
+ };
+
// 01234567890123456789
// +js(token[, arg[, ...]])
- // ^ ^
- // 4 -1
+ // ^ ^
+ // 4 -1
api.fromCompiledContent = function(reader) {
reader.select(µb.compiledScriptletSection);
@@ -302,15 +309,6 @@
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();
$exceptions.clear();
@@ -324,6 +322,15 @@
scriptletDB.retrieve(entity, [ $scriptlets, $exceptions ], 1);
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;
// Wholly disable scriptlet injection?
diff --git a/src/js/static-ext-filtering.js b/src/js/static-ext-filtering.js
index 65a6c36bd8a15..3a07c3c4e0260 100644
--- a/src/js/static-ext-filtering.js
+++ b/src/js/static-ext-filtering.js
@@ -59,11 +59,13 @@
get acceptedCount() {
return µb.cosmeticFilteringEngine.acceptedCount +
µb.scriptletFilteringEngine.acceptedCount +
+ µb.httpheaderFilteringEngine.acceptedCount +
µb.htmlFilteringEngine.acceptedCount;
},
get discardedCount() {
return µb.cosmeticFilteringEngine.discardedCount +
µb.scriptletFilteringEngine.discardedCount +
+ µb.httpheaderFilteringEngine.discardedCount +
µb.htmlFilteringEngine.discardedCount;
},
};
@@ -137,7 +139,7 @@
this.timer = undefined;
this.strToIdMap.clear();
},
- { timeout: 10000 }
+ { timeout: 5000 }
);
}
@@ -181,6 +183,7 @@
}
fromSelfie(selfie) {
+ if ( selfie === undefined ) { return; }
this.hostnameToSlotIdMap = new Map(selfie.hostnameToSlotIdMap);
this.hostnameSlots = selfie.hostnameSlots;
this.strSlots = selfie.strSlots;
@@ -239,12 +242,14 @@
api.reset = function() {
µb.cosmeticFilteringEngine.reset();
µb.scriptletFilteringEngine.reset();
+ µb.httpheaderFilteringEngine.reset();
µb.htmlFilteringEngine.reset();
};
api.freeze = function() {
µb.cosmeticFilteringEngine.freeze();
µb.scriptletFilteringEngine.freeze();
+ µb.httpheaderFilteringEngine.freeze();
µb.htmlFilteringEngine.freeze();
};
@@ -267,6 +272,12 @@
return true;
}
+ // Response header filtering
+ if ( (parser.flavorBits & parser.BITFlavorExtResponseHeader) !== 0 ) {
+ µb.httpheaderFilteringEngine.compile(parser, writer);
+ return true;
+ }
+
// HTML filtering
// TODO: evaluate converting Adguard's `$$` syntax into uBO's HTML
// filtering syntax.
@@ -280,9 +291,23 @@
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) {
µb.cosmeticFilteringEngine.fromCompiledContent(reader, options);
µb.scriptletFilteringEngine.fromCompiledContent(reader, options);
+ µb.httpheaderFilteringEngine.fromCompiledContent(reader, options);
µb.htmlFilteringEngine.fromCompiledContent(reader, options);
};
@@ -292,7 +317,8 @@
JSON.stringify({
cosmetic: µb.cosmeticFilteringEngine.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; }
µb.cosmeticFilteringEngine.fromSelfie(selfie.cosmetic);
µb.scriptletFilteringEngine.fromSelfie(selfie.scriptlets);
+ µb.httpheaderFilteringEngine.fromSelfie(selfie.httpHeaders);
µb.htmlFilteringEngine.fromSelfie(selfie.html);
return true;
});
diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js
index e5c645b96f7ac..0599ffdb3843b 100644
--- a/src/js/static-filtering-parser.js
+++ b/src/js/static-filtering-parser.js
@@ -304,6 +304,20 @@ const Parser = class {
}
// ##^...
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;
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
// Detect and report invalid CSS selectors.
@@ -1978,6 +2002,7 @@ const BITFlavorExtStrong = 1 << 8;
const BITFlavorExtCosmetic = 1 << 9;
const BITFlavorExtScriptlet = 1 << 10;
const BITFlavorExtHTML = 1 << 11;
+const BITFlavorExtResponseHeader = 1 << 12;
const BITFlavorIgnore = 1 << 29;
const BITFlavorUnsupported = 1 << 30;
const BITFlavorError = 1 << 31;
@@ -2087,6 +2112,7 @@ Parser.prototype.BITFlavorExtStrong = BITFlavorExtStrong;
Parser.prototype.BITFlavorExtCosmetic = BITFlavorExtCosmetic;
Parser.prototype.BITFlavorExtScriptlet = BITFlavorExtScriptlet;
Parser.prototype.BITFlavorExtHTML = BITFlavorExtHTML;
+Parser.prototype.BITFlavorExtResponseHeader = BITFlavorExtResponseHeader;
Parser.prototype.BITFlavorIgnore = BITFlavorIgnore;
Parser.prototype.BITFlavorUnsupported = BITFlavorUnsupported;
Parser.prototype.BITFlavorError = BITFlavorError;
diff --git a/src/js/traffic.js b/src/js/traffic.js
index 27ab87f06111d..89b836e4d2f40 100644
--- a/src/js/traffic.js
+++ b/src/js/traffic.js
@@ -508,7 +508,8 @@ const onHeadersReceived = function(details) {
// Keep in mind response headers will be modified in-place if needed, so
// `details.responseHeaders` will always point to the modified response
// headers.
- const responseHeaders = details.responseHeaders;
+ const { responseHeaders } = details;
+ if ( Array.isArray(responseHeaders) === false ) { return; }
if ( isRootDoc === false && µb.hiddenSettings.filterOnHeaders === true ) {
const result = pageStore.filterOnHeaders(fctxt, responseHeaders);
@@ -539,11 +540,17 @@ const onHeadersReceived = function(details) {
}
// At this point we have a HTML document.
+
+ const filteredHTML =
+ µb.canFilterResponseData && filterDocument(fctxt, details) === true;
- const filteredHTML = µb.canFilterResponseData &&
- filterDocument(pageStore, 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
// 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
// allows Firefox's offline mode to work as expected.
if ( (filteredHTML || modifiedHeaders) && dontCacheResponseHeaders ) {
- let cacheControl = µb.hiddenSettings.cacheControlForFirefox1376932;
+ const cacheControl = µb.hiddenSettings.cacheControlForFirefox1376932;
if ( cacheControl !== 'unset' ) {
let i = headerIndexFromName('cache-control', responseHeaders);
if ( i !== -1 ) {
@@ -565,7 +572,7 @@ const onHeadersReceived = function(details) {
}
if ( modifiedHeaders ) {
- return { responseHeaders: responseHeaders };
+ return { responseHeaders };
}
};
@@ -614,7 +621,7 @@ const normalizeBehindTheSceneResponseHeaders = function(details) {
**/
-const filterDocument = (function() {
+const filterDocument = (( ) => {
const µb = µBlock;
const filterers = new Map();
let domParser, xmlSerializer,
@@ -805,7 +812,7 @@ const filterDocument = (function() {
filterers.delete(this);
};
- return function(pageStore, fctxt, extras) {
+ return function(fctxt, extras) {
// https://github.com/gorhill/uBlock/issues/3478
const statusCode = extras.statusCode || 0;
if ( statusCode !== 0 && (statusCode < 200 || statusCode >= 300) ) {