diff options
Diffstat (limited to 'src/js/codemirror')
-rw-r--r-- | src/js/codemirror/search.js | 86 | ||||
-rw-r--r-- | src/js/codemirror/ubo-static-filtering.js | 447 |
2 files changed, 282 insertions, 251 deletions
diff --git a/src/js/codemirror/search.js b/src/js/codemirror/search.js index 477e9cc..7ee5b33 100644 --- a/src/js/codemirror/search.js +++ b/src/js/codemirror/search.js @@ -25,18 +25,25 @@ // Ctrl-G. // ===== -'use strict'; - import { dom, qs$ } from '../dom.js'; import { i18n$ } from '../i18n.js'; { const CodeMirror = self.CodeMirror; + CodeMirror.defineOption('maximizable', true, (cm, maximizable) => { + if ( typeof maximizable !== 'boolean' ) { return; } + const wrapper = cm.getWrapperElement(); + if ( wrapper === null ) { return; } + const container = wrapper.closest('.codeMirrorContainer'); + if ( container === null ) { return; } + container.dataset.maximizable = `${maximizable}`; + }); + const searchOverlay = function(query, caseInsensitive) { if ( typeof query === 'string' ) query = new RegExp( - query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), + query.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'), caseInsensitive ? 'gi' : 'g' ); else if ( !query.global ) @@ -89,8 +96,10 @@ import { i18n$ } from '../i18n.js'; state.queryTimer.offon(350); }; - const searchWidgetClickHandler = function(cm, ev) { - const tcl = ev.target.classList; + const searchWidgetClickHandler = (ev, cm) => { + if ( ev.button !== 0 ) { return; } + const target = ev.target; + const tcl = target.classList; if ( tcl.contains('cm-search-widget-up') ) { findNext(cm, -1); } else if ( tcl.contains('cm-search-widget-down') ) { @@ -99,11 +108,14 @@ import { i18n$ } from '../i18n.js'; findNextError(cm, -1); } else if ( tcl.contains('cm-linter-widget-down') ) { findNextError(cm, 1); + } else if ( tcl.contains('cm-maximize') ) { + const container = target.closest('.codeMirrorContainer'); + if ( container !== null ) { + container.classList.toggle('cm-maximized'); + } } - if ( ev.target.localName !== 'input' ) { - ev.preventDefault(); - } else { - ev.stopImmediatePropagation(); + if ( target.localName !== 'input' ) { + cm.focus(); } }; @@ -127,7 +139,9 @@ import { i18n$ } from '../i18n.js'; this.widget = widgetParent.children[0]; this.widget.addEventListener('keydown', searchWidgetKeydownHandler.bind(null, cm)); this.widget.addEventListener('input', searchWidgetInputHandler.bind(null, cm)); - this.widget.addEventListener('mousedown', searchWidgetClickHandler.bind(null, cm)); + this.widget.addEventListener('click', ev => { + searchWidgetClickHandler(ev, cm); + }); if ( typeof cm.addPanel === 'function' ) { this.panel = cm.addPanel(this.widget); } @@ -236,10 +250,7 @@ import { i18n$ } from '../i18n.js'; notation: 'compact', maximumSignificantDigits: 3 }); - if ( - intl.resolvedOptions instanceof Function && - intl.resolvedOptions().hasOwnProperty('notation') - ) { + if ( intl.resolvedOptions().notation ) { intlNumberFormat = intl; } } @@ -330,9 +341,6 @@ import { i18n$ } from '../i18n.js'; state.annotate.update(annotations); }); state.widget.setAttribute('data-query', state.queryText); - // Ensure the caret is visible - const input = state.widget.querySelector('.cm-search-widget-input input'); - input.selectionStart = input.selectionStart; }; const findNext = function(cm, dir, callback) { @@ -458,26 +466,30 @@ import { i18n$ } from '../i18n.js'; }; { - const searchWidgetTemplate = - '<div class="cm-search-widget-template" style="display:none;">' + - '<div class="cm-search-widget">' + - '<span class="cm-search-widget-input">' + - '<span class="fa-icon fa-icon-ro">search</span> ' + - '<input type="search" spellcheck="false"> ' + - '<span class="cm-search-widget-up cm-search-widget-button fa-icon">angle-up</span> ' + - '<span class="cm-search-widget-down cm-search-widget-button fa-icon fa-icon-vflipped">angle-up</span> ' + - '<span class="cm-search-widget-count"></span>' + - '</span>' + - '<span class="cm-linter-widget" data-lint="0">' + - '<span class="cm-linter-widget-count"></span> ' + - '<span class="cm-linter-widget-up cm-search-widget-button fa-icon">angle-up</span> ' + - '<span class="cm-linter-widget-down cm-search-widget-button fa-icon fa-icon-vflipped">angle-up</span> ' + - '</span>' + - '<span>' + - '<a class="fa-icon sourceURL" href>external-link</a>' + - '</span>' + - '</div>' + - '</div>'; + const searchWidgetTemplate = [ + '<div class="cm-search-widget-template" style="display:none;">', + '<div class="cm-search-widget">', + '<span class="cm-maximize"><svg viewBox="0 0 40 40"><path d="M4,16V4h12M24,4H36V16M4,24V36H16M36,24V36H24" /><path d="M14 2.5v12h-12M38 14h-12v-12M14 38v-12h-12M26 38v-12h12" /></svg></span> ', + '<span class="cm-search-widget-input">', + '<span class="searchfield">', + '<input type="search" spellcheck="false" placeholder="">', + '<span class="fa-icon">search</span>', + '</span> ', + '<span class="cm-search-widget-up cm-search-widget-button fa-icon">angle-up</span> ', + '<span class="cm-search-widget-down cm-search-widget-button fa-icon fa-icon-vflipped">angle-up</span> ', + '<span class="cm-search-widget-count"></span>', + '</span>', + '<span class="cm-linter-widget" data-lint="0">', + '<span class="cm-linter-widget-count"></span> ', + '<span class="cm-linter-widget-up cm-search-widget-button fa-icon">angle-up</span> ', + '<span class="cm-linter-widget-down cm-search-widget-button fa-icon fa-icon-vflipped">angle-up</span> ', + '</span>', + '<span>', + '<a class="fa-icon sourceURL" href>external-link</a>', + '</span>', + '</div>', + '</div>', + ].join('\n'); const domParser = new DOMParser(); const doc = domParser.parseFromString(searchWidgetTemplate, 'text/html'); const widgetTemplate = document.adoptNode(doc.body.firstElementChild); diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index ac1b048..2aaf85b 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -21,8 +21,6 @@ /* global CodeMirror */ -'use strict'; - /******************************************************************************/ import * as sfp from '../static-filtering-parser.js'; @@ -39,10 +37,10 @@ let hintHelperRegistered = false; /******************************************************************************/ -CodeMirror.defineOption('trustedSource', false, (cm, state) => { - if ( typeof state !== 'boolean' ) { return; } +CodeMirror.defineOption('trustedSource', false, (cm, trusted) => { + if ( typeof trusted !== 'boolean' ) { return; } self.dispatchEvent(new CustomEvent('trustedSource', { - detail: state, + detail: { cm, trusted }, })); }); @@ -56,219 +54,232 @@ CodeMirror.defineOption('trustedScriptletTokens', undefined, (cm, tokens) => { /******************************************************************************/ -CodeMirror.defineMode('ubo-static-filtering', function() { - const astParser = new sfp.AstFilterParser({ - interactive: true, - nativeCssHas: vAPI.webextFlavor.env.includes('native_css_has'), - }); - const astWalker = astParser.getWalker(); - let currentWalkerNode = 0; - let lastNetOptionType = 0; - - const redirectTokenStyle = node => { - const rawToken = astParser.getNodeString(node || currentWalkerNode); +const uBOStaticFilteringMode = (( ) => { + const redirectTokenStyle = (mode, node) => { + const rawToken = mode.astParser.getNodeString(node || mode.currentWalkerNode); const { token } = sfp.parseRedirectValue(rawToken); return redirectNames.has(token) ? 'value' : 'value warning'; }; - const nodeHasError = node => { - return astParser.getNodeFlags( - node || currentWalkerNode, sfp.NODE_FLAG_ERROR + const nodeHasError = (mode, node) => { + return mode.astParser.getNodeFlags( + node || mode.currentWalkerNode, sfp.NODE_FLAG_ERROR ) !== 0; }; - const colorFromAstNode = function() { - if ( astParser.nodeIsEmptyString(currentWalkerNode) ) { return '+'; } - if ( nodeHasError() ) { return 'error'; } - const nodeType = astParser.getNodeType(currentWalkerNode); + const colorFromAstNode = mode => { + if ( mode.astParser.nodeIsEmptyString(mode.currentWalkerNode) ) { return '+'; } + if ( nodeHasError(mode) ) { return 'error'; } + const nodeType = mode.astParser.getNodeType(mode.currentWalkerNode); switch ( nodeType ) { - case sfp.NODE_TYPE_WHITESPACE: - return ''; - case sfp.NODE_TYPE_COMMENT: - if ( astWalker.canGoDown() ) { break; } - return 'comment'; - case sfp.NODE_TYPE_COMMENT_URL: - return 'comment link'; - case sfp.NODE_TYPE_IGNORE: - return 'comment'; - case sfp.NODE_TYPE_PREPARSE_DIRECTIVE: - case sfp.NODE_TYPE_PREPARSE_DIRECTIVE_VALUE: - return 'directive'; - case sfp.NODE_TYPE_PREPARSE_DIRECTIVE_IF_VALUE: { - const raw = astParser.getNodeString(currentWalkerNode); - const state = sfp.utils.preparser.evaluateExpr(raw, preparseDirectiveEnv); - return state ? 'positive strong' : 'negative strong'; + case sfp.NODE_TYPE_WHITESPACE: + return ''; + case sfp.NODE_TYPE_COMMENT: + if ( mode.astWalker.canGoDown() ) { break; } + return 'comment'; + case sfp.NODE_TYPE_COMMENT_URL: + return 'comment link'; + case sfp.NODE_TYPE_IGNORE: + return 'comment'; + case sfp.NODE_TYPE_PREPARSE_DIRECTIVE: + case sfp.NODE_TYPE_PREPARSE_DIRECTIVE_VALUE: + return 'directive'; + case sfp.NODE_TYPE_PREPARSE_DIRECTIVE_IF_VALUE: { + const raw = mode.astParser.getNodeString(mode.currentWalkerNode); + const state = sfp.utils.preparser.evaluateExpr(raw, preparseDirectiveEnv); + return state ? 'positive strong' : 'negative strong'; + } + case sfp.NODE_TYPE_EXT_OPTIONS_ANCHOR: + return mode.astParser.getFlags(sfp.AST_FLAG_IS_EXCEPTION) + ? 'tag strong' + : 'def strong'; + case sfp.NODE_TYPE_EXT_DECORATION: + return 'def'; + case sfp.NODE_TYPE_EXT_PATTERN_RAW: + if ( mode.astWalker.canGoDown() ) { break; } + return 'variable'; + case sfp.NODE_TYPE_EXT_PATTERN_COSMETIC: + case sfp.NODE_TYPE_EXT_PATTERN_HTML: + return 'variable'; + case sfp.NODE_TYPE_EXT_PATTERN_RESPONSEHEADER: + case sfp.NODE_TYPE_EXT_PATTERN_SCRIPTLET: + if ( mode.astWalker.canGoDown() ) { break; } + return 'variable'; + case sfp.NODE_TYPE_EXT_PATTERN_SCRIPTLET_TOKEN: { + const token = mode.astParser.getNodeString(mode.currentWalkerNode); + if ( scriptletNames.has(token) === false ) { + return 'warning'; } - case sfp.NODE_TYPE_EXT_OPTIONS_ANCHOR: - return astParser.getFlags(sfp.AST_FLAG_IS_EXCEPTION) - ? 'tag strong' - : 'def strong'; - case sfp.NODE_TYPE_EXT_DECORATION: - return 'def'; - case sfp.NODE_TYPE_EXT_PATTERN_RAW: - if ( astWalker.canGoDown() ) { break; } - return 'variable'; - case sfp.NODE_TYPE_EXT_PATTERN_COSMETIC: - case sfp.NODE_TYPE_EXT_PATTERN_HTML: - return 'variable'; - case sfp.NODE_TYPE_EXT_PATTERN_RESPONSEHEADER: - case sfp.NODE_TYPE_EXT_PATTERN_SCRIPTLET: - if ( astWalker.canGoDown() ) { break; } - return 'variable'; - case sfp.NODE_TYPE_EXT_PATTERN_SCRIPTLET_TOKEN: { - const token = astParser.getNodeString(currentWalkerNode); - if ( scriptletNames.has(token) === false ) { - return 'warning'; + return 'variable'; + } + case sfp.NODE_TYPE_EXT_PATTERN_SCRIPTLET_ARG: + return 'variable'; + case sfp.NODE_TYPE_NET_EXCEPTION: + return 'tag strong'; + case sfp.NODE_TYPE_NET_PATTERN: + if ( mode.astWalker.canGoDown() ) { break; } + if ( mode.astParser.isRegexPattern() ) { + if ( mode.astParser.getNodeFlags(mode.currentWalkerNode, sfp.NODE_FLAG_PATTERN_UNTOKENIZABLE) !== 0 ) { + return 'variable warning'; } - return 'variable'; + return 'variable notice'; } - case sfp.NODE_TYPE_EXT_PATTERN_SCRIPTLET_ARG: - return 'variable'; - case sfp.NODE_TYPE_NET_EXCEPTION: - return 'tag strong'; - case sfp.NODE_TYPE_NET_PATTERN: - if ( astWalker.canGoDown() ) { break; } - if ( astParser.isRegexPattern() ) { - if ( astParser.getNodeFlags(currentWalkerNode, sfp.NODE_FLAG_PATTERN_UNTOKENIZABLE) !== 0 ) { - return 'variable warning'; - } - return 'variable notice'; - } - return 'variable'; - case sfp.NODE_TYPE_NET_PATTERN_PART: - return 'variable'; - case sfp.NODE_TYPE_NET_PATTERN_PART_SPECIAL: - return 'keyword strong'; - case sfp.NODE_TYPE_NET_PATTERN_PART_UNICODE: - return 'variable unicode'; - case sfp.NODE_TYPE_NET_PATTERN_LEFT_HNANCHOR: - case sfp.NODE_TYPE_NET_PATTERN_LEFT_ANCHOR: - case sfp.NODE_TYPE_NET_PATTERN_RIGHT_ANCHOR: - case sfp.NODE_TYPE_NET_OPTION_NAME_NOT: - return 'keyword strong'; - case sfp.NODE_TYPE_NET_OPTIONS_ANCHOR: - case sfp.NODE_TYPE_NET_OPTION_SEPARATOR: - lastNetOptionType = 0; - return 'def strong'; - case sfp.NODE_TYPE_NET_OPTION_NAME_UNKNOWN: - lastNetOptionType = 0; - return 'error'; - case sfp.NODE_TYPE_NET_OPTION_NAME_1P: - case sfp.NODE_TYPE_NET_OPTION_NAME_STRICT1P: - case sfp.NODE_TYPE_NET_OPTION_NAME_3P: - case sfp.NODE_TYPE_NET_OPTION_NAME_STRICT3P: - case sfp.NODE_TYPE_NET_OPTION_NAME_ALL: - case sfp.NODE_TYPE_NET_OPTION_NAME_BADFILTER: - case sfp.NODE_TYPE_NET_OPTION_NAME_CNAME: - case sfp.NODE_TYPE_NET_OPTION_NAME_CSP: - case sfp.NODE_TYPE_NET_OPTION_NAME_CSS: - case sfp.NODE_TYPE_NET_OPTION_NAME_DENYALLOW: - case sfp.NODE_TYPE_NET_OPTION_NAME_DOC: - case sfp.NODE_TYPE_NET_OPTION_NAME_EHIDE: - case sfp.NODE_TYPE_NET_OPTION_NAME_EMPTY: - case sfp.NODE_TYPE_NET_OPTION_NAME_FONT: - case sfp.NODE_TYPE_NET_OPTION_NAME_FRAME: - case sfp.NODE_TYPE_NET_OPTION_NAME_FROM: - case sfp.NODE_TYPE_NET_OPTION_NAME_GENERICBLOCK: - case sfp.NODE_TYPE_NET_OPTION_NAME_GHIDE: - case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER: - case sfp.NODE_TYPE_NET_OPTION_NAME_IMAGE: - case sfp.NODE_TYPE_NET_OPTION_NAME_IMPORTANT: - case sfp.NODE_TYPE_NET_OPTION_NAME_INLINEFONT: - case sfp.NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT: - case sfp.NODE_TYPE_NET_OPTION_NAME_MATCHCASE: - case sfp.NODE_TYPE_NET_OPTION_NAME_MEDIA: - case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD: - case sfp.NODE_TYPE_NET_OPTION_NAME_MP4: - case sfp.NODE_TYPE_NET_OPTION_NAME_NOOP: - case sfp.NODE_TYPE_NET_OPTION_NAME_OBJECT: - case sfp.NODE_TYPE_NET_OPTION_NAME_OTHER: - case sfp.NODE_TYPE_NET_OPTION_NAME_PING: - case sfp.NODE_TYPE_NET_OPTION_NAME_POPUNDER: - case sfp.NODE_TYPE_NET_OPTION_NAME_POPUP: + return 'variable'; + case sfp.NODE_TYPE_NET_PATTERN_PART: + return 'variable'; + case sfp.NODE_TYPE_NET_PATTERN_PART_SPECIAL: + return 'keyword strong'; + case sfp.NODE_TYPE_NET_PATTERN_PART_UNICODE: + return 'variable unicode'; + case sfp.NODE_TYPE_NET_PATTERN_LEFT_HNANCHOR: + case sfp.NODE_TYPE_NET_PATTERN_LEFT_ANCHOR: + case sfp.NODE_TYPE_NET_PATTERN_RIGHT_ANCHOR: + case sfp.NODE_TYPE_NET_OPTION_NAME_NOT: + return 'keyword strong'; + case sfp.NODE_TYPE_NET_OPTIONS_ANCHOR: + case sfp.NODE_TYPE_NET_OPTION_SEPARATOR: + mode.lastNetOptionType = 0; + return 'def strong'; + case sfp.NODE_TYPE_NET_OPTION_NAME_UNKNOWN: + mode.lastNetOptionType = 0; + return 'error'; + case sfp.NODE_TYPE_NET_OPTION_NAME_1P: + case sfp.NODE_TYPE_NET_OPTION_NAME_STRICT1P: + case sfp.NODE_TYPE_NET_OPTION_NAME_3P: + case sfp.NODE_TYPE_NET_OPTION_NAME_STRICT3P: + case sfp.NODE_TYPE_NET_OPTION_NAME_ALL: + case sfp.NODE_TYPE_NET_OPTION_NAME_BADFILTER: + case sfp.NODE_TYPE_NET_OPTION_NAME_CNAME: + case sfp.NODE_TYPE_NET_OPTION_NAME_CSP: + case sfp.NODE_TYPE_NET_OPTION_NAME_CSS: + case sfp.NODE_TYPE_NET_OPTION_NAME_DENYALLOW: + case sfp.NODE_TYPE_NET_OPTION_NAME_DOC: + case sfp.NODE_TYPE_NET_OPTION_NAME_EHIDE: + case sfp.NODE_TYPE_NET_OPTION_NAME_EMPTY: + case sfp.NODE_TYPE_NET_OPTION_NAME_FONT: + case sfp.NODE_TYPE_NET_OPTION_NAME_FRAME: + case sfp.NODE_TYPE_NET_OPTION_NAME_FROM: + case sfp.NODE_TYPE_NET_OPTION_NAME_GENERICBLOCK: + case sfp.NODE_TYPE_NET_OPTION_NAME_GHIDE: + case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER: + case sfp.NODE_TYPE_NET_OPTION_NAME_IMAGE: + case sfp.NODE_TYPE_NET_OPTION_NAME_IMPORTANT: + case sfp.NODE_TYPE_NET_OPTION_NAME_INLINEFONT: + case sfp.NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT: + case sfp.NODE_TYPE_NET_OPTION_NAME_MATCHCASE: + case sfp.NODE_TYPE_NET_OPTION_NAME_MEDIA: + case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD: + case sfp.NODE_TYPE_NET_OPTION_NAME_MP4: + case sfp.NODE_TYPE_NET_OPTION_NAME_NOOP: + case sfp.NODE_TYPE_NET_OPTION_NAME_OBJECT: + case sfp.NODE_TYPE_NET_OPTION_NAME_OTHER: + case sfp.NODE_TYPE_NET_OPTION_NAME_PING: + case sfp.NODE_TYPE_NET_OPTION_NAME_POPUNDER: + case sfp.NODE_TYPE_NET_OPTION_NAME_POPUP: + case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT: + case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE: + case sfp.NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM: + case sfp.NODE_TYPE_NET_OPTION_NAME_SCRIPT: + case sfp.NODE_TYPE_NET_OPTION_NAME_SHIDE: + case sfp.NODE_TYPE_NET_OPTION_NAME_TO: + case sfp.NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM: + case sfp.NODE_TYPE_NET_OPTION_NAME_XHR: + case sfp.NODE_TYPE_NET_OPTION_NAME_WEBRTC: + case sfp.NODE_TYPE_NET_OPTION_NAME_WEBSOCKET: + mode.lastNetOptionType = nodeType; + return 'def'; + case sfp.NODE_TYPE_NET_OPTION_ASSIGN: + return 'def'; + case sfp.NODE_TYPE_NET_OPTION_VALUE: + if ( mode.astWalker.canGoDown() ) { break; } + switch ( mode.lastNetOptionType ) { case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT: case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE: - case sfp.NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM: - case sfp.NODE_TYPE_NET_OPTION_NAME_SCRIPT: - case sfp.NODE_TYPE_NET_OPTION_NAME_SHIDE: - case sfp.NODE_TYPE_NET_OPTION_NAME_TO: - case sfp.NODE_TYPE_NET_OPTION_NAME_XHR: - case sfp.NODE_TYPE_NET_OPTION_NAME_WEBRTC: - case sfp.NODE_TYPE_NET_OPTION_NAME_WEBSOCKET: - lastNetOptionType = nodeType; - return 'def'; - case sfp.NODE_TYPE_NET_OPTION_ASSIGN: - return 'def'; - case sfp.NODE_TYPE_NET_OPTION_VALUE: - if ( astWalker.canGoDown() ) { break; } - switch ( lastNetOptionType ) { - case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT: - case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE: - return redirectTokenStyle(); - default: - break; - } - return 'value'; - case sfp.NODE_TYPE_OPTION_VALUE_NOT: - return 'keyword strong'; - case sfp.NODE_TYPE_OPTION_VALUE_DOMAIN: - return 'value'; - case sfp.NODE_TYPE_OPTION_VALUE_SEPARATOR: - return 'def'; + return redirectTokenStyle(mode); default: break; + } + return 'value'; + case sfp.NODE_TYPE_OPTION_VALUE_NOT: + return 'keyword strong'; + case sfp.NODE_TYPE_OPTION_VALUE_DOMAIN: + return 'value'; + case sfp.NODE_TYPE_OPTION_VALUE_SEPARATOR: + return 'def'; + default: + break; } return '+'; }; - self.addEventListener('trustedSource', ev => { - astParser.options.trustedSource = ev.detail; - }); - - self.addEventListener('trustedScriptletTokens', ev => { - astParser.options.trustedScriptletTokens = ev.detail; - }); + class ModeState { + constructor() { + this.astParser = new sfp.AstFilterParser({ + interactive: true, + nativeCssHas: vAPI.webextFlavor.env.includes('native_css_has'), + }); + this.astWalker = this.astParser.getWalker(); + this.currentWalkerNode = 0; + this.lastNetOptionType = 0; + self.addEventListener('trustedSource', ev => { + const { trusted } = ev.detail; + this.astParser.options.trustedSource = trusted; + }); + self.addEventListener('trustedScriptletTokens', ev => { + this.astParser.options.trustedScriptletTokens = ev.detail; + }); + } + } - return { - lineComment: '!', - token: function(stream) { + return { + state: null, + startState() { + if ( this.state === null ) { + this.state = new ModeState(); + } + return this.state; + }, + copyState(other) { + return other; + }, + token(stream, state) { if ( stream.sol() ) { - astParser.parse(stream.string); - if ( astParser.getFlags(sfp.AST_FLAG_UNSUPPORTED) !== 0 ) { + state.astParser.parse(stream.string); + if ( state.astParser.getFlags(sfp.AST_FLAG_UNSUPPORTED) !== 0 ) { stream.skipToEnd(); return 'error'; } - if ( astParser.getType() === sfp.AST_TYPE_NONE ) { + if ( state.astParser.getType() === sfp.AST_TYPE_NONE ) { stream.skipToEnd(); return 'comment'; } - currentWalkerNode = astWalker.reset(); - } else if ( nodeHasError() ) { - currentWalkerNode = astWalker.right(); + state.currentWalkerNode = state.astWalker.reset(); + } else if ( nodeHasError(state) ) { + state.currentWalkerNode = state.astWalker.right(); } else { - currentWalkerNode = astWalker.next(); + state.currentWalkerNode = state.astWalker.next(); } let style = ''; - while ( currentWalkerNode !== 0 ) { - style = colorFromAstNode(stream); + while ( state.currentWalkerNode !== 0 ) { + style = colorFromAstNode(state, stream); if ( style !== '+' ) { break; } - currentWalkerNode = astWalker.next(); + state.currentWalkerNode = state.astWalker.next(); } if ( style === '+' ) { stream.skipToEnd(); return null; } - stream.pos = astParser.getNodeStringEnd(currentWalkerNode); - if ( astParser.isNetworkFilter() ) { + stream.pos = state.astParser.getNodeStringEnd(state.currentWalkerNode); + if ( state.astParser.isNetworkFilter() ) { return style ? `line-cm-net ${style}` : 'line-cm-net'; } - if ( astParser.isExtendedFilter() ) { + if ( state.astParser.isExtendedFilter() ) { let flavor = ''; - if ( astParser.isCosmeticFilter() ) { + if ( state.astParser.isCosmeticFilter() ) { flavor = 'line-cm-ext-dom'; - } else if ( astParser.isScriptletFilter() ) { + } else if ( state.astParser.isScriptletFilter() ) { flavor = 'line-cm-ext-js'; - } else if ( astParser.isHtmlFilter() ) { + } else if ( state.astParser.isHtmlFilter() ) { flavor = 'line-cm-ext-html'; } if ( flavor !== '' ) { @@ -278,9 +289,11 @@ CodeMirror.defineMode('ubo-static-filtering', function() { style = style.trim(); return style !== '' ? style : null; }, - parser: astParser, + lineComment: '!', }; -}); +})(); + +CodeMirror.defineMode('ubo-static-filtering', ( ) => uBOStaticFilteringMode); /******************************************************************************/ @@ -327,7 +340,7 @@ function initHints() { }); const proceduralOperatorNames = new Map( Array.from(sfp.proceduralOperatorTokens) - .filter(item => (item[1] & 0b01) !== 0) + .filter(item => (item[1] & 0b01) !== 0) ); const excludedHints = new Set([ 'genericblock', @@ -562,7 +575,7 @@ function initHints() { const getExtScriptletHints = function(cursor, line) { const beg = cursor.ch; - const matchLeft = /#\+\js\(([^,]*)$/.exec(line.slice(0, beg)); + const matchLeft = /#\+js\(([^,]*)$/.exec(line.slice(0, beg)); const matchRight = /^([^,)]*)/.exec(line.slice(beg)); if ( matchLeft === null || matchRight === null ) { return; } const hints = []; @@ -709,38 +722,38 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => { if ( astParser.hasError() ) { let msg = 'Invalid filter'; switch ( astParser.astError ) { - case sfp.AST_ERROR_UNSUPPORTED: - msg = `${msg}: Unsupported filter syntax`; - break; - case sfp.AST_ERROR_REGEX: - msg = `${msg}: Bad regular expression`; - break; - case sfp.AST_ERROR_PATTERN: - msg = `${msg}: Bad pattern`; - break; - case sfp.AST_ERROR_DOMAIN_NAME: - msg = `${msg}: Bad domain name`; - break; - case sfp.AST_ERROR_OPTION_BADVALUE: - msg = `${msg}: Bad value assigned to a valid option`; - break; - case sfp.AST_ERROR_OPTION_DUPLICATE: - msg = `${msg}: Duplicate filter option`; - break; - case sfp.AST_ERROR_OPTION_UNKNOWN: - msg = `${msg}: Unsupported filter option`; - break; - case sfp.AST_ERROR_IF_TOKEN_UNKNOWN: - msg = `${msg}: Unknown preparsing token`; - break; - case sfp.AST_ERROR_UNTRUSTED_SOURCE: - msg = `${msg}: Filter requires trusted source`; - break; - default: - if ( astParser.isCosmeticFilter() && astParser.result.error ) { - msg = `${msg}: ${astParser.result.error}`; - } - break; + case sfp.AST_ERROR_UNSUPPORTED: + msg = `${msg}: Unsupported filter syntax`; + break; + case sfp.AST_ERROR_REGEX: + msg = `${msg}: Bad regular expression`; + break; + case sfp.AST_ERROR_PATTERN: + msg = `${msg}: Bad pattern`; + break; + case sfp.AST_ERROR_DOMAIN_NAME: + msg = `${msg}: Bad domain name`; + break; + case sfp.AST_ERROR_OPTION_BADVALUE: + msg = `${msg}: Bad value assigned to a valid option`; + break; + case sfp.AST_ERROR_OPTION_DUPLICATE: + msg = `${msg}: Duplicate filter option`; + break; + case sfp.AST_ERROR_OPTION_UNKNOWN: + msg = `${msg}: Unsupported filter option`; + break; + case sfp.AST_ERROR_IF_TOKEN_UNKNOWN: + msg = `${msg}: Unknown preparsing token`; + break; + case sfp.AST_ERROR_UNTRUSTED_SOURCE: + msg = `${msg}: Filter requires trusted source`; + break; + default: + if ( astParser.isCosmeticFilter() && astParser.result.error ) { + msg = `${msg}: ${astParser.result.error}`; + } + break; } return { lint: 'error', msg }; } @@ -877,6 +890,11 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => { ifendifSet.add(lineHandle); ifendifSetChanged = true; } + } else if ( marker.dataset.lint === 'error' ) { + if ( marker.dataset.error !== 'y' ) { + marker.dataset.error = 'y'; + errorCount += 1; + } } if ( typeof details.msg !== 'string' || details.msg === '' ) { return; } const msgElem = qs$(marker, '.msg'); @@ -1083,7 +1101,8 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => { }; self.addEventListener('trustedSource', ev => { - astParser.options.trustedSource = ev.detail; + const { trusted } = ev.detail; + astParser.options.trustedSource = trusted; }); self.addEventListener('trustedScriptletTokens', ev => { |