diff --git a/src/js/1p-filters.js b/src/js/1p-filters.js index adf179f88..7d70b85ad 100644 --- a/src/js/1p-filters.js +++ b/src/js/1p-filters.js @@ -54,6 +54,7 @@ vAPI.messaging.send('dashboard', { }).then(response => { if ( response instanceof Object === false ) { return; } const mode = cmEditor.getMode(); + // TODO: listen to changes in currently opened set of tabs? if ( mode.setHints instanceof Function ) { mode.setHints(response); } diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index b4a74239c..c4b5d68e6 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -34,6 +34,7 @@ const redirectNames = new Map(); const scriptletNames = new Map(); const preparseDirectiveTokens = new Map(); const preparseDirectiveHints = []; +const originHints = []; /******************************************************************************/ @@ -362,6 +363,9 @@ CodeMirror.defineMode('ubo-static-filtering', function() { preparseDirectiveTokens.set(a, b); }); preparseDirectiveHints.push(...details.preparseDirectiveHints); + for ( const hint of details.originHints ) { + originHints.push(hint); + } initHints(); }, get parser() { @@ -418,7 +422,23 @@ const initHints = function() { }; }; - const getNetOptionHints = function(cursor, isNegated, seedLeft, seedRight) { + const getOriginHints = function(cursor, line) { + const beg = cursor.ch; + const matchLeft = /[^,|=~]*$/.exec(line.slice(0, beg)); + const matchRight = /^[^#,|]*/.exec(line.slice(beg)); + if ( matchLeft === null || matchRight === null ) { return; } + const hints = []; + for ( const text of originHints ) { + hints.push(text); + } + return pickBestHints(cursor, matchLeft[0], matchRight[0], hints); + }; + + const getNetOptionHints = function(cursor, seedLeft, seedRight) { + const isNegated = seedLeft.startsWith('~'); + if ( isNegated ) { + seedLeft = seedLeft.slice(1); + } const assignPos = seedRight.indexOf('='); if ( assignPos !== -1 ) { seedRight = seedRight.slice(0, assignPos); } const isException = parser.isException(); @@ -448,26 +468,32 @@ const initHints = function() { const getNetHints = function(cursor, line) { const beg = cursor.ch; - if ( beg < parser.optionsSpan ) { return; } + if ( parser.optionsSpan.len === 0 ) { + if ( /[^\w\x80-\xF4#,.-]/.test(line) === false ) { + return getOriginHints(cursor, line); + } + return; + } + if ( beg < parser.slices[parser.optionsSpan.i+1] ) { return; } const lineBefore = line.slice(0, beg); const lineAfter = line.slice(beg); - let matchLeft = /~?([^$,~]*)$/.exec(lineBefore); - let matchRight = /^([^,]*)/.exec(lineAfter); + let matchLeft = /[^$,]*$/.exec(lineBefore); + let matchRight = /^[^,]*/.exec(lineAfter); if ( matchLeft === null || matchRight === null ) { return; } - let pos = matchLeft[1].indexOf('='); - if ( pos === -1 ) { - return getNetOptionHints( + const assignPos = matchLeft[0].indexOf('='); + if ( assignPos === -1 ) { + return getNetOptionHints(cursor, matchLeft[0], matchRight[0]); + } + if ( /^redirect(-rule)?=/.test(matchLeft[0]) ) { + return getNetRedirectHints( cursor, - matchLeft[0].startsWith('~'), - matchLeft[1], - matchRight[1] + matchLeft[0].slice(assignPos + 1), + matchRight[0] ); } - return getNetRedirectHints( - cursor, - matchLeft[1].slice(pos + 1), - matchRight[1] - ); + if ( matchLeft[0].startsWith('domain=') ) { + return getOriginHints(cursor, line); + } }; const getExtSelectorHints = function(cursor, line) { @@ -527,10 +553,15 @@ const initHints = function() { const line = cm.getLine(cursor.line); parser.analyze(line); if ( parser.category === parser.CATStaticExtFilter ) { - if ( parser.hasFlavor(parser.BITFlavorExtScriptlet) ) { - return getExtScriptletHints(cursor, line); + let hints; + if ( cursor.ch <= parser.slices[parser.optionsAnchorSpan.i+1] ) { + hints = getOriginHints(cursor, line); + } else { + hints = parser.hasFlavor(parser.BITFlavorExtScriptlet) + ? getExtScriptletHints(cursor, line) + : getExtSelectorHints(cursor, line); } - return getExtSelectorHints(cursor, line); + return hints; } if ( parser.category === parser.CATStaticNetFilter ) { return getNetHints(cursor, line); @@ -538,6 +569,9 @@ const initHints = function() { if ( parser.category === parser.CATComment ) { return getCommentHints(cursor, line); } + if ( parser.category === parser.CATNone ) { + return getOriginHints(cursor, line); + } }); }; diff --git a/src/js/messaging.js b/src/js/messaging.js index 3ba2d9dff..5af3d539f 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -1000,7 +1000,7 @@ const resetUserData = async function() { vAPI.app.restart(); }; -// 3rd-party filters +// Filter lists const prepListEntries = function(entries) { const µburi = µb.URI; for ( const k in entries ) { @@ -1041,6 +1041,26 @@ const getLists = async function(callback) { callback(r); }; +// My filters + +// TODO: also return origin of embedded frames? +const getOriginHints = function() { + const punycode = self.punycode; + const out = []; + for ( const tabId of µb.pageStores.keys() ) { + if ( tabId === -1 ) { continue; } + const tabContext = µb.tabContextManager.lookup(tabId); + if ( tabContext === null ) { continue; } + let { rootDomain, rootHostname } = tabContext; + if ( rootDomain.endsWith('-scheme') ) { continue; } + const isPunycode = rootHostname.includes('xn--'); + out.push(isPunycode ? punycode.toUnicode(rootDomain) : rootDomain); + if ( rootHostname === rootDomain ) { continue; } + out.push(isPunycode ? punycode.toUnicode(rootHostname) : rootHostname); + } + return out; +}; + // My rules const getRules = function() { return { @@ -1185,6 +1205,7 @@ const onMessage = function(request, sender, callback) { redirectResources: µb.redirectEngine.getResourceDetails(), preparseDirectiveTokens: µb.preparseDirectives.getTokens(), preparseDirectiveHints: µb.preparseDirectives.getHints(), + originHints: getOriginHints(), expertMode: µb.hiddenSettings.filterAuthorMode, }; break;