Add option to filter by HTTP method in static network filters

Related issue:
- https://github.com/uBlockOrigin/uBlock-issues/issues/2117

Option: `method=`
Value: a list of `|`-separated lowercased method names. Negated
method names are allowed. These are valid methods:

- connect
- delete
- get
- head
- options
- patch
- post
- put

As per DNR's own documentation:
- https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/#type-RequestMethod

The logger shows the method used for every network request. It's
possible to filter the logger output for most-common methods: `get`,
`head`, `post`.
This commit is contained in:
Raymond Hill 2022-12-18 15:36:59 -05:00
parent e5a9b066ec
commit b6981877ba
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
8 changed files with 305 additions and 89 deletions

View File

@ -5,6 +5,7 @@
"esversion": 8,
"globals": {
"chrome": false, // global variable in Chromium, Chrome, Opera
"globalThis": false,
"self": false,
"vAPI": false,
"URLSearchParams": false,

View File

@ -176,8 +176,8 @@ const µBlock = { // jshint ignore:line
// Read-only
systemSettings: {
compiledMagic: 50, // Increase when compiled format changes
selfieMagic: 50, // Increase when selfie format changes
compiledMagic: 51, // Increase when compiled format changes
selfieMagic: 51, // Increase when selfie format changes
},
// https://github.com/uBlockOrigin/uBlock-issues/issues/759#issuecomment-546654501
@ -285,6 +285,7 @@ const µBlock = { // jshint ignore:line
this.fromTabId(tabId); // Must be called AFTER tab context management
this.realm = '';
this.id = details.requestId;
this.setMethod(details.method);
this.setURL(details.url);
this.aliasURL = details.aliasURL || undefined;
if ( this.itype !== this.SUB_FRAME ) {
@ -337,24 +338,31 @@ const µBlock = { // jshint ignore:line
}
toLogger() {
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();
}
const filters = this.filter;
const details = {
id: this.id,
tstamp: Date.now(),
realm: this.realm,
method: this.getMethodName(),
type: this.stype,
tabId: this.tabId,
tabDomain: this.getTabDomain(),
tabHostname: this.getTabHostname(),
docDomain: this.getDocDomain(),
docHostname: this.getDocHostname(),
domain: this.getDomain(),
hostname: this.getHostname(),
url: this.url,
aliasURL: this.aliasURL,
filter: undefined,
};
// Many filters may have been applied to the current context
if ( Array.isArray(filters) === false ) {
return logger.writeOne(this);
if ( Array.isArray(this.filter) === false ) {
details.filter = this.filter;
return logger.writeOne(details);
}
for ( const filter of filters ) {
this.filter = filter;
logger.writeOne(this);
for ( const filter of this.filter ) {
details.filter = filter;
logger.writeOne(details);
}
}
};

View File

@ -84,6 +84,48 @@ const typeStrToIntMap = {
'other': OTHER,
};
const METHOD_NONE = 0;
const METHOD_CONNECT = 1 << 1;
const METHOD_DELETE = 1 << 2;
const METHOD_GET = 1 << 3;
const METHOD_HEAD = 1 << 4;
const METHOD_OPTIONS = 1 << 5;
const METHOD_PATCH = 1 << 6;
const METHOD_POST = 1 << 7;
const METHOD_PUT = 1 << 8;
const methodStrToBitMap = {
'': METHOD_NONE,
'connect': METHOD_CONNECT,
'delete': METHOD_DELETE,
'get': METHOD_GET,
'head': METHOD_HEAD,
'options': METHOD_OPTIONS,
'patch': METHOD_PATCH,
'post': METHOD_POST,
'put': METHOD_PUT,
'CONNECT': METHOD_CONNECT,
'DELETE': METHOD_DELETE,
'GET': METHOD_GET,
'HEAD': METHOD_HEAD,
'OPTIONS': METHOD_OPTIONS,
'PATCH': METHOD_PATCH,
'POST': METHOD_POST,
'PUT': METHOD_PUT,
};
const methodBitToStrMap = new Map([
[ METHOD_NONE, '' ],
[ METHOD_CONNECT, 'connect' ],
[ METHOD_DELETE, 'delete' ],
[ METHOD_GET, 'get' ],
[ METHOD_HEAD, 'head' ],
[ METHOD_OPTIONS, 'options' ],
[ METHOD_PATCH, 'patch' ],
[ METHOD_POST, 'post' ],
[ METHOD_PUT, 'put' ],
]);
/******************************************************************************/
const FilteringContext = class {
@ -94,7 +136,8 @@ const FilteringContext = class {
this.tstamp = 0;
this.realm = '';
this.id = undefined;
this.itype = 0;
this.method = 0;
this.itype = NO_TYPE;
this.stype = undefined;
this.url = undefined;
this.aliasURL = undefined;
@ -133,6 +176,7 @@ const FilteringContext = class {
fromFilteringContext(other) {
this.realm = other.realm;
this.type = other.type;
this.method = other.method;
this.url = other.url;
this.hostname = other.hostname;
this.domain = other.domain;
@ -358,6 +402,23 @@ const FilteringContext = class {
}
return this;
}
setMethod(a) {
this.method = methodStrToBitMap[a] || 0;
return this;
}
getMethodName() {
return FilteringContext.getMethodName(this.method);
}
static getMethod(a) {
return methodStrToBitMap[a] || 0;
}
static getMethodName(a) {
return methodBitToStrMap.get(a) || '';
}
};
/******************************************************************************/
@ -386,6 +447,16 @@ FilteringContext.prototype.INLINE_ANY = FilteringContext.INLINE_ANY = INLINE_ANY
FilteringContext.prototype.PING_ANY = FilteringContext.PING_ANY = PING_ANY;
FilteringContext.prototype.SCRIPT_ANY = FilteringContext.SCRIPT_ANY = SCRIPT_ANY;
FilteringContext.prototype.METHOD_NONE = FilteringContext.METHOD_NONE = METHOD_NONE;
FilteringContext.prototype.METHOD_CONNECT = FilteringContext.METHOD_CONNECT = METHOD_CONNECT;
FilteringContext.prototype.METHOD_DELETE = FilteringContext.METHOD_DELETE = METHOD_DELETE;
FilteringContext.prototype.METHOD_GET = FilteringContext.METHOD_GET = METHOD_GET;
FilteringContext.prototype.METHOD_HEAD = FilteringContext.METHOD_HEAD = METHOD_HEAD;
FilteringContext.prototype.METHOD_OPTIONS = FilteringContext.METHOD_OPTIONS = METHOD_OPTIONS;
FilteringContext.prototype.METHOD_PATCH = FilteringContext.METHOD_PATCH = METHOD_PATCH;
FilteringContext.prototype.METHOD_POST = FilteringContext.METHOD_POST = METHOD_POST;
FilteringContext.prototype.METHOD_PUT = FilteringContext.METHOD_PUT = METHOD_PUT;
/******************************************************************************/
export { FilteringContext };

View File

@ -36,6 +36,16 @@ const logDate = new Date();
const logDateTimezoneOffset = logDate.getTimezoneOffset() * 60000;
const loggerEntries = [];
const COLUMN_TIMESTAMP = 0;
const COLUMN_FILTER = 1;
const COLUMN_MESSAGE = 1;
const COLUMN_RESULT = 2;
const COLUMN_INITIATOR = 3;
const COLUMN_PARTYNESS = 4;
const COLUMN_METHOD = 5;
const COLUMN_TYPE = 6;
const COLUMN_URL = 7;
let filteredLoggerEntries = [];
let filteredLoggerEntryVoidedCount = 0;
@ -211,7 +221,6 @@ const LogEntry = function(details) {
this[prop] = details[prop];
}
}
this.type = details.stype || details.type;
if ( details.aliasURL !== undefined ) {
this.aliased = true;
}
@ -233,6 +242,7 @@ LogEntry.prototype = {
domain: '',
filter: undefined,
id: '',
method: '',
realm: '',
tabDomain: '',
tabHostname: '',
@ -413,17 +423,20 @@ const parseLogEntry = function(details) {
textContent.push('');
}
// Cell 5
// Cell 5: method
textContent.push(entry.method || '');
// Cell 6
textContent.push(
normalizeToStr(prettyRequestTypes[entry.type] || entry.type)
);
// Cell 6
// Cell 7
textContent.push(normalizeToStr(details.url));
// Hidden cells -- useful for row-filtering purpose
// Cell 7
// Cell 8
if ( entry.aliased ) {
textContent.push(`aliasURL=${details.aliasURL}`);
}
@ -534,49 +547,56 @@ const viewPort = (( ) => {
: 0;
});
const reservedWidth =
cellWidths[0] + cellWidths[2] + cellWidths[4] + cellWidths[5];
cellWidths[6] = 0.5;
if ( cellWidths[1] === 0 && cellWidths[3] === 0 ) {
cellWidths[6] = 1;
} else if ( cellWidths[1] === 0 ) {
cellWidths[3] = 0.35;
cellWidths[6] = 0.65;
} else if ( cellWidths[3] === 0 ) {
cellWidths[1] = 0.35;
cellWidths[6] = 0.65;
cellWidths[COLUMN_TIMESTAMP] +
cellWidths[COLUMN_RESULT] +
cellWidths[COLUMN_PARTYNESS] +
cellWidths[COLUMN_METHOD] +
cellWidths[COLUMN_TYPE];
cellWidths[COLUMN_URL] = 0.5;
if ( cellWidths[COLUMN_FILTER] === 0 && cellWidths[COLUMN_INITIATOR] === 0 ) {
cellWidths[COLUMN_URL] = 1;
} else if ( cellWidths[COLUMN_FILTER] === 0 ) {
cellWidths[COLUMN_INITIATOR] = 0.35;
cellWidths[COLUMN_URL] = 0.65;
} else if ( cellWidths[COLUMN_INITIATOR] === 0 ) {
cellWidths[COLUMN_FILTER] = 0.35;
cellWidths[COLUMN_URL] = 0.65;
} else {
cellWidths[1] = 0.25;
cellWidths[3] = 0.25;
cellWidths[6] = 0.5;
cellWidths[COLUMN_FILTER] = 0.25;
cellWidths[COLUMN_INITIATOR] = 0.25;
cellWidths[COLUMN_URL] = 0.5;
}
const style = qs$('#vwRendererRuntimeStyles');
const cssRules = [
'#vwContent .logEntry {',
` height: ${newLineHeight}px;`,
'}',
'#vwContent .logEntry > div > span:nth-of-type(1) {',
` width: ${cellWidths[0]}px;`,
`#vwContent .logEntry > div > span:nth-of-type(${COLUMN_TIMESTAMP+1}) {`,
` width: ${cellWidths[COLUMN_TIMESTAMP]}px;`,
'}',
'#vwContent .logEntry > div > span:nth-of-type(2) {',
` width: calc(calc(100% - ${reservedWidth}px) * ${cellWidths[1]});`,
`#vwContent .logEntry > div > span:nth-of-type(${COLUMN_FILTER+1}) {`,
` width: calc(calc(100% - ${reservedWidth}px) * ${cellWidths[COLUMN_FILTER]});`,
'}',
'#vwContent .logEntry > div.messageRealm > span:nth-of-type(2) {',
` width: calc(100% - ${cellWidths[0]}px);`,
`#vwContent .logEntry > div.messageRealm > span:nth-of-type(${COLUMN_MESSAGE+1}) {`,
` width: calc(100% - ${cellWidths[COLUMN_MESSAGE]}px);`,
'}',
'#vwContent .logEntry > div > span:nth-of-type(3) {',
` width: ${cellWidths[2]}px;`,
`#vwContent .logEntry > div > span:nth-of-type(${COLUMN_RESULT+1}) {`,
` width: ${cellWidths[COLUMN_RESULT]}px;`,
'}',
'#vwContent .logEntry > div > span:nth-of-type(4) {',
` width: calc(calc(100% - ${reservedWidth}px) * ${cellWidths[3]});`,
`#vwContent .logEntry > div > span:nth-of-type(${COLUMN_INITIATOR+1}) {`,
` width: calc(calc(100% - ${reservedWidth}px) * ${cellWidths[COLUMN_INITIATOR]});`,
'}',
'#vwContent .logEntry > div > span:nth-of-type(5) {',
` width: ${cellWidths[4]}px;`,
`#vwContent .logEntry > div > span:nth-of-type(${COLUMN_PARTYNESS+1}) {`,
` width: ${cellWidths[COLUMN_PARTYNESS]}px;`,
'}',
'#vwContent .logEntry > div > span:nth-of-type(6) {',
` width: ${cellWidths[5]}px;`,
`#vwContent .logEntry > div > span:nth-of-type(${COLUMN_METHOD+1}) {`,
` width: ${cellWidths[COLUMN_METHOD]}px;`,
'}',
'#vwContent .logEntry > div > span:nth-of-type(7) {',
` width: calc(calc(100% - ${reservedWidth}px) * ${cellWidths[6]});`,
`#vwContent .logEntry > div > span:nth-of-type(${COLUMN_TYPE+1}) {`,
` width: ${cellWidths[COLUMN_TYPE]}px;`,
'}',
`#vwContent .logEntry > div > span:nth-of-type(${COLUMN_URL+1}) {`,
` width: calc(calc(100% - ${reservedWidth}px) * ${cellWidths[COLUMN_URL]});`,
'}',
'',
];
@ -651,8 +671,8 @@ const viewPort = (( ) => {
}
// Timestamp
span = div.children[0];
span.textContent = cells[0];
span = div.children[COLUMN_TIMESTAMP];
span.textContent = cells[COLUMN_TIMESTAMP];
// Tab id
if ( details.tabId !== undefined ) {
@ -666,8 +686,8 @@ const viewPort = (( ) => {
if ( details.type !== undefined ) {
dom.attr(div, 'data-type', details.type);
}
span = div.children[1];
span.textContent = cells[1];
span = div.children[COLUMN_MESSAGE];
span.textContent = cells[COLUMN_MESSAGE];
return div;
}
@ -692,23 +712,23 @@ const viewPort = (( ) => {
dom.attr(div, 'data-modifier', '');
}
}
span = div.children[1];
if ( renderFilterToSpan(span, cells[1]) === false ) {
span.textContent = cells[1];
span = div.children[COLUMN_FILTER];
if ( renderFilterToSpan(span, cells[COLUMN_FILTER]) === false ) {
span.textContent = cells[COLUMN_FILTER];
}
// Event
if ( cells[2] === '--' ) {
if ( cells[COLUMN_RESULT] === '--' ) {
dom.attr(div, 'data-status', '1');
} else if ( cells[2] === '++' ) {
} else if ( cells[COLUMN_RESULT] === '++' ) {
dom.attr(div, 'data-status', '2');
} else if ( cells[2] === '**' ) {
} else if ( cells[COLUMN_RESULT] === '**' ) {
dom.attr(div, 'data-status', '3');
} else if ( cells[2] === '<<' ) {
} else if ( cells[COLUMN_RESULT] === '<<' ) {
divcl.add('redirect');
}
span = div.children[2];
span.textContent = cells[2];
span = div.children[COLUMN_RESULT];
span.textContent = cells[COLUMN_RESULT];
// Origins
if ( details.tabHostname ) {
@ -717,12 +737,12 @@ const viewPort = (( ) => {
if ( details.docHostname ) {
dom.attr(div, 'data-dochn', details.docHostname);
}
span = div.children[3];
span.textContent = cells[3];
span = div.children[COLUMN_INITIATOR];
span.textContent = cells[COLUMN_INITIATOR];
// Partyness
if (
cells[4] !== '' &&
cells[COLUMN_PARTYNESS] !== '' &&
details.realm === 'network' &&
details.domain !== undefined
) {
@ -733,12 +753,16 @@ const viewPort = (( ) => {
text += ` \u21d2 ${details.domain}`;
dom.attr(div, 'data-parties', text);
}
span = div.children[4];
span.textContent = cells[4];
span = div.children[COLUMN_PARTYNESS];
span.textContent = cells[COLUMN_PARTYNESS];
// Method
span = div.children[COLUMN_METHOD];
span.textContent = cells[COLUMN_METHOD];
// Type
span = div.children[5];
span.textContent = cells[5];
span = div.children[COLUMN_TYPE];
span.textContent = cells[COLUMN_TYPE];
// URL
let re;
@ -747,7 +771,7 @@ const viewPort = (( ) => {
} else if ( filteringType === 'dynamicUrl' ) {
re = regexFromURLFilteringResult(filter.rule.join(' '));
}
nodeFromURL(div.children[6], cells[6], re);
nodeFromURL(div.children[COLUMN_URL], cells[COLUMN_URL], re);
// Alias URL (CNAME, etc.)
if ( cells.length > 7 ) {
@ -1468,7 +1492,7 @@ const reloadTab = function(ev) {
};
const filterFromTargetRow = function() {
return dom.text(targetRow.children[1]);
return dom.text(targetRow.children[COLUMN_FILTER]);
};
const aliasURLFromID = function(id) {
@ -1476,13 +1500,13 @@ const reloadTab = function(ev) {
for ( const entry of loggerEntries ) {
if ( entry.id !== id || entry.aliased ) { continue; }
const fields = entry.textContent.split('\t');
return fields[6] || '';
return fields[COLUMN_URL] || '';
}
return '';
};
const toSummaryPaneFilterNode = async function(receiver, filter) {
receiver.children[1].textContent = filter;
receiver.children[COLUMN_FILTER].textContent = filter;
if ( dom.cl.has(targetRow, 'canLookup') === false ) { return; }
const isException = reIsExceptionFilter.test(filter);
let isExcepted = false;
@ -1498,7 +1522,7 @@ const reloadTab = function(ev) {
};
const fillSummaryPaneFilterList = async function(rows) {
const rawFilter = targetRow.children[1].textContent;
const rawFilter = targetRow.children[COLUMN_FILTER].textContent;
const nodeFromFilter = function(filter, lists) {
const fragment = document.createDocumentFragment();
@ -1561,7 +1585,7 @@ const reloadTab = function(ev) {
} else if ( dom.cl.has(targetRow, 'extendedRealm') ) {
const response = await messaging.send('loggerUI', {
what: 'listsFromCosmeticFilter',
url: targetRow.children[6].textContent,
url: targetRow.children[COLUMN_URL].textContent,
rawFilter: rawFilter,
});
handleResponse(response);
@ -1619,19 +1643,19 @@ const reloadTab = function(ev) {
// Partyness
text = dom.attr(tr, 'data-parties') || '';
if ( text !== '' ) {
rows[5].children[1].textContent = `(${trch[4].textContent})\u2002${text}`;
rows[5].children[1].textContent = `(${trch[COLUMN_PARTYNESS].textContent})\u2002${text}`;
} else {
rows[5].style.display = 'none';
}
// Type
text = trch[5].textContent;
text = trch[COLUMN_TYPE].textContent;
if ( text !== '' ) {
rows[6].children[1].textContent = text;
} else {
rows[6].style.display = 'none';
}
// URL
const canonicalURL = trch[6].textContent;
const canonicalURL = trch[COLUMN_URL].textContent;
if ( canonicalURL !== '' ) {
const attr = dom.attr(tr, 'data-status') || '';
if ( attr !== '' ) {
@ -1640,7 +1664,7 @@ const reloadTab = function(ev) {
dom.attr(rows[7], 'data-modifier', '');
}
}
rows[7].children[1].appendChild(dom.clone(trch[6]));
rows[7].children[1].appendChild(dom.clone(trch[COLUMN_URL]));
} else {
rows[7].style.display = 'none';
}
@ -1846,8 +1870,8 @@ const reloadTab = function(ev) {
if ( targetRow === null ) { return; }
ev.stopPropagation();
targetTabId = tabIdFromAttribute(targetRow);
targetType = targetRow.children[5].textContent.trim() || '';
targetURLs = createTargetURLs(targetRow.children[6].textContent);
targetType = targetRow.children[COLUMN_TYPE].textContent.trim() || '';
targetURLs = createTargetURLs(targetRow.children[COLUMN_URL].textContent);
targetPageHostname = dom.attr(targetRow, 'data-tabhn') || '';
targetFrameHostname = dom.attr(targetRow, 'data-dochn') || '';
@ -2640,7 +2664,7 @@ const loggerSettings = (( ) => {
maxEntryCount: 2000, // per-tab
maxLoadCount: 20, // per-tab
},
columns: [ true, true, true, true, true, true, true, true ],
columns: [ true, true, true, true, true, true, true, true, true ],
linesPerEntry: 4,
};

View File

@ -511,7 +511,7 @@ browser.runtime.onUpdateAvailable.addListener(details => {
if ( selfieIsValid ) {
µb.supportStats.allReadyAfter += ' (selfie)';
}
ubolog(`All ready ${µb.supportStats.allReadyAfter} ms after launch`);
ubolog(`All ready ${µb.supportStats.allReadyAfter} after launch`);
// <<<<< end of private scope
})();

View File

@ -2377,7 +2377,8 @@ const OPTTokenShide = 37;
const OPTTokenXhr = 38;
const OPTTokenWebrtc = 39;
const OPTTokenWebsocket = 40;
const OPTTokenCount = 41;
const OPTTokenMethod = 41;
const OPTTokenCount = 42;
//const OPTPerOptionMask = 0x0000ff00;
const OPTCanNegate = 1 << 8;
@ -2481,6 +2482,7 @@ Parser.prototype.OPTTokenFrame = OPTTokenFrame;
Parser.prototype.OPTTokenXhr = OPTTokenXhr;
Parser.prototype.OPTTokenWebrtc = OPTTokenWebrtc;
Parser.prototype.OPTTokenWebsocket = OPTTokenWebsocket;
Parser.prototype.OPTTokenMethod = OPTTokenMethod;
Parser.prototype.OPTCanNegate = OPTCanNegate;
Parser.prototype.OPTBlockOnly = OPTBlockOnly;
@ -2527,6 +2529,7 @@ const netOptionTokenDescriptors = new Map([
[ 'inline-script', OPTTokenInlineScript | OPTNonNetworkType | OPTCanNegate | OPTNonCspableType | OPTNonRedirectableType ],
[ 'match-case', OPTTokenMatchCase ],
[ 'media', OPTTokenMedia | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ],
[ 'method', OPTTokenMethod | OPTNetworkType | OPTMustAssign ],
[ 'mp4', OPTTokenMp4 | OPTNetworkType | OPTBlockOnly | OPTModifierType ],
[ '_', OPTTokenNoop ],
[ 'object', OPTTokenObject | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ],
@ -2586,6 +2589,7 @@ Parser.netOptionTokenIds = new Map([
[ 'inline-script', OPTTokenInlineScript ],
[ 'match-case', OPTTokenMatchCase ],
[ 'media', OPTTokenMedia ],
[ 'method', OPTTokenMethod ],
[ 'mp4', OPTTokenMp4 ],
[ '_', OPTTokenNoop ],
[ 'object', OPTTokenObject ],
@ -2635,6 +2639,7 @@ Parser.netOptionTokenNames = new Map([
[ OPTTokenInlineScript, 'inline-script' ],
[ OPTTokenMatchCase, 'match-case' ],
[ OPTTokenMedia, 'media' ],
[ OPTTokenMethod, 'method' ],
[ OPTTokenMp4, 'mp4' ],
[ OPTTokenNoop, '_' ],
[ OPTTokenObject, 'object' ],

View File

@ -199,6 +199,7 @@ const INVALID_TOKEN_HASH = 0xFFFFFFFF;
// See the following as short-lived registers, used during evaluation. They are
// valid until the next evaluation.
let $requestMethodBit = 0;
let $requestTypeValue = 0;
let $requestURL = '';
let $requestURLRaw = '';
@ -1265,6 +1266,82 @@ registerFilterClass(FilterRegex);
/******************************************************************************/
const FilterMethod = class {
static match(idata) {
if ( $requestMethodBit === 0 ) { return false; }
const methodBits = filterData[idata+1];
const notMethodBits = filterData[idata+2];
return (methodBits !== 0 && ($requestMethodBit & methodBits) !== 0) ||
(notMethodBits !== 0 && ($requestMethodBit & notMethodBits) === 0);
}
static compile(details) {
return [ FilterMethod.fid, details.methodBits, details.notMethodBits ];
}
static fromCompiled(args) {
const idata = filterDataAllocLen(3);
filterData[idata+0] = args[0]; // fid
filterData[idata+1] = args[1]; // methodBits
filterData[idata+2] = args[2]; // notMethodBits
return idata;
}
static dnrFromCompiled(args, rule) {
rule.condition = rule.condition || {};
const rc = rule.condition;
let methodBits = args[1];
let notMethodBits = args[2];
if ( methodBits !== 0 && rc.requestMethods === undefined ) {
rc.requestMethods = [];
}
if ( notMethodBits !== 0 && rc.excludedRequestMethods === undefined ) {
rc.excludedRequestMethods = [];
}
for ( let i = 1; methodBits !== 0 || notMethodBits !== 0; i++ ) {
const bit = 1 << i;
const methodName = FilteringContext.getMethodName(bit);
if ( (methodBits & bit) !== 0 ) {
methodBits &= ~bit;
rc.requestMethods.push(methodName);
} else if ( (notMethodBits & bit) !== 0 ) {
notMethodBits &= ~bit;
rc.excludedRequestMethods.push(methodName);
}
}
}
static keyFromArgs(args) {
return `${args[1]} ${args[2]}`;
}
static logData(idata, details) {
const methods = [];
let methodBits = filterData[idata+1];
let notMethodBits = filterData[idata+2];
for ( let i = 0; methodBits !== 0 || notMethodBits !== 0; i++ ) {
const bit = 1 << i;
const methodName = FilteringContext.getMethodName(bit);
if ( (methodBits & bit) !== 0 ) {
methodBits &= ~bit;
methods.push(methodName);
} else if ( (notMethodBits & bit) !== 0 ) {
notMethodBits &= ~bit;
methods.push(`~${methodName}`);
}
}
details.options.push(`method=${methods.join('|')}`);
}
static dumpInfo(idata) {
return `0b${filterData[idata+1].toString(2)} 0b${filterData[idata+2].toString(2)}`;
}
};
registerFilterClass(FilterMethod);
/******************************************************************************/
// stylesheet: 1 => bit 0
// image: 2 => bit 1
// object: 3 => bit 2
@ -3025,6 +3102,8 @@ class FilterCompiler {
this.tokenBeg = 0;
this.typeBits = 0;
this.notTypeBits = 0;
this.methodBits = 0;
this.notMethodBits = 0;
this.wildcardPos = -1;
this.caretPos = -1;
return this;
@ -3055,6 +3134,21 @@ class FilterCompiler {
}
}
processMethodOption(value) {
for ( const method of value.split('|') ) {
if ( method.charCodeAt(0) === 0x7E /* '~' */ ) {
const bit = FilteringContext.getMethod(method.slice(1)) || 0;
if ( bit === 0 ) { continue; }
this.notMethodBits |= bit;
} else {
const bit = FilteringContext.getMethod(method) || 0;
if ( bit === 0 ) { continue; }
this.methodBits |= bit;
}
}
this.methodBits &= ~this.notMethodBits;
}
// https://github.com/chrisaljoudi/uBlock/issues/589
// Be ready to handle multiple negated types
@ -3225,6 +3319,9 @@ class FilterCompiler {
}
this.optionUnitBits |= this.REDIRECT_BIT;
break;
case parser.OPTTokenMethod:
this.processMethodOption(val);
break;
case parser.OPTTokenInvalid:
return false;
default:
@ -3573,6 +3670,11 @@ class FilterCompiler {
units.push(FilterAnchorRight.compile());
}
// Method(s)
if ( this.methodBits !== 0 || this.notMethodBits !== 0 ) {
units.push(FilterMethod.compile(this));
}
// Not types
if ( this.notTypeBits !== 0 ) {
units.push(FilterNotType.compile(this));
@ -4566,6 +4668,7 @@ FilterContainer.prototype.matchAndFetchModifiers = function(
$docHostname = fctxt.getDocHostname();
$docDomain = fctxt.getDocDomain();
$requestHostname = fctxt.getHostname();
$requestMethodBit = fctxt.method || 0;
$requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset;
const partyBits = fctxt.is3rdPartyToDoc() ? ThirdParty : FirstParty;
@ -4866,6 +4969,7 @@ FilterContainer.prototype.matchRequestReverse = function(type, url) {
// Prime tokenizer: we get a normalized URL in return.
$requestURL = urlTokenizer.setURL(url);
$requestURLRaw = url;
$requestMethodBit = 0;
$requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset;
$isBlockImportant = false;
this.$filterUnit = 0;
@ -4933,6 +5037,7 @@ FilterContainer.prototype.matchRequest = function(fctxt, modifiers = 0) {
$docHostname = fctxt.getDocHostname();
$docDomain = fctxt.getDocDomain();
$requestHostname = fctxt.getHostname();
$requestMethodBit = fctxt.method || 0;
$requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset;
$isBlockImportant = false;
@ -4967,6 +5072,7 @@ FilterContainer.prototype.matchHeaders = function(fctxt, headers) {
$docHostname = fctxt.getDocHostname();
$docDomain = fctxt.getDocDomain();
$requestHostname = fctxt.getHostname();
$requestMethodBit = fctxt.method || 0;
$requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset;
$httpHeaders.init(headers);

View File

@ -66,6 +66,7 @@
</span>
</div>
<div><span data-filtex="!" data-i18n="loggerRowFiltererBuiltinNot"></span><span data-filtex="\t(?:0,)?1\t" data-i18n="loggerRowFiltererBuiltin1p"></span><span data-filtex="\t(?:3(?:,\d)?|0,3)\t" data-i18n="loggerRowFiltererBuiltin3p"></span></div>
<div><span data-filtex="!" data-i18n="loggerRowFiltererBuiltinNot"></span><span data-filtex="\tget\t">get</span><span data-filtex="\thead\t">head</span><span data-filtex="\tpost\t">post</span></div>
<div id="filterExprCnameOf" style="display:none"><span data-filtex="!" data-i18n="loggerRowFiltererBuiltinNot"></span><span data-filtex="\taliasURL=.">CNAME</span></div>
</div>
</span>
@ -85,7 +86,7 @@
</div>
</div>
<div id="vwLineSizer">
<div class="logEntry oneLine"><div><span>00:00:00</span><span>&nbsp;</span><span>**</span><span>&nbsp;</span><span>3,3</span><span>inline-script</span><span>&nbsp;</span></div></div>
<div class="logEntry oneLine"><div><span>00:00:00</span><span>&nbsp;</span><span>**</span><span>&nbsp;</span><span>3,3</span><span>options</span><span>inline-script</span><span>&nbsp;</span></div></div>
</div>
</div>
</div>
@ -101,7 +102,7 @@
</div>
<div id="templates" style="display: none;">
<div id="logEntryTemplate"><div><span></span>&#8203;<span></span>&#8203;<span></span>&#8203;<span></span>&#8203;<span></span>&#8203;<span></span>&#8203;<span></span></div></div>
<div id="logEntryTemplate"><div><span></span>&#8203;<span></span>&#8203;<span></span>&#8203;<span></span>&#8203;<span></span>&#8203;<span></span>&#8203;<span></span>&#8203;<span></span></div></div>
<div id="netFilteringDialog" data-pane="details">
<div class="hide preview"><span>click to preview</span></div>