/** * @file HTML filtering rule converter. */ import { type HtmlFilteringRule } from '../../nodes/index.js'; import { type NodeConversionResult } from '../base-interfaces/conversion-result.js'; import { RuleConverterBase } from '../base-interfaces/rule-converter-base.js'; /** * Error messages used in HTML filtering rule conversion. */ export declare const ERROR_MESSAGES: { readonly ABP_NOT_SUPPORTED: "Invalid rule, ABP does not support HTML filtering rules"; readonly INVALID_RULE: "Invalid HTML filtering rule: %s"; readonly MIXED_SYNTAX_ADG_UBO: "Mixed AdGuard and uBlock syntax"; readonly EMPTY_SELECTOR_LIST: "Selector list of HTML filtering rule must not be empty"; readonly EMPTY_COMPLEX_SELECTOR: "Complex selector of selector list must not be empty"; readonly INVALID_SELECTOR_COMBINATOR: "Invalid selector combinator '%s' used between selectors"; readonly UNKNOWN_SELECTOR_TYPE: "Unknown selector type '%s' found during conversion"; readonly SPECIAL_ATTRIBUTE_SELECTOR_OPERATOR_INVALID: "Special attribute selector '%s' has invalid operator '%s'"; readonly SPECIAL_ATTRIBUTE_SELECTOR_FLAG_NOT_SUPPORTED: "Special attribute selector '%s' does not support flags"; readonly SPECIAL_ATTRIBUTE_SELECTOR_VALUE_REQUIRED: "Special attribute selector '%s' requires a value"; readonly SPECIAL_ATTRIBUTE_SELECTOR_VALUE_INT: "Value of special attribute selector '%s' must be an integer, got '%s'"; readonly SPECIAL_ATTRIBUTE_SELECTOR_VALUE_POSITIVE: "Value of special attribute selector '%s' must be a positive integer, got '%s'"; readonly SPECIAL_ATTRIBUTE_SELECTOR_NOT_SUPPORTED: "Special attribute selector '%s' is not supported in conversion"; readonly SPECIAL_PSEUDO_CLASS_SELECTOR_ARGUMENT_REQUIRED: "Special pseudo-class selector '%s' requires an argument"; readonly SPECIAL_PSEUDO_CLASS_SELECTOR_ARGUMENT_INT: "Argument of special pseudo-class selector '%s' must be an integer, got '%s'"; readonly SPECIAL_PSEUDO_CLASS_SELECTOR_ARGUMENT_POSITIVE: "Argument of special pseudo-class selector '%s' must be a positive integer, got '%s'"; readonly SPECIAL_PSEUDO_CLASS_SELECTOR_ARGUMENT_TOO_LARGE: "Argument of special pseudo-class selector '%s' must not exceed %s, got '%s'"; readonly SPECIAL_PSEUDO_CLASS_SELECTOR_NOT_SUPPORTED: "Special pseudo-class selector '%s' is not supported in conversion"; }; /** * HTML filtering rule converter class. * * @todo Implement `convertToUbo` (ABP currently doesn't support HTML filtering rules). */ export declare class HtmlRuleConverter extends RuleConverterBase { /** * Converts a HTML rule to AdGuard syntax, if possible. * * Note: for AdGuard rules this is not a strict validation. AdGuard rule * bodies with an unbalanced special pseudo-class argument, e.g. * `:contains((function(...)`, are tolerated and parsed leniently by the * parser itself (CoreLibs parity, see `AdgHtmlFilteringBodyParser`); * other unparseable bodies with special selectors are kept as-is with * `isConverted: false`, so callers must not treat a returned result as * proof of rule validity. * * @param rule Rule node to convert. * * @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains * the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted. * If the rule was not converted, the result array will contain the original node with the same object reference. * * @throws If the rule is genuinely invalid — an unparseable body without * special selector markers, or a conversion error (e.g. mixed AdGuard and * uBlock syntax, invalid length values) — or cannot be converted. */ static convertToAdg(rule: HtmlFilteringRule): NodeConversionResult; /** * Converts a HTML rule to uBlock syntax, if possible. * Also can be used to convert uBlock rules to uBlock syntax to validate them. * * @param rule Rule node to convert. * * @returns An object which follows the {@link NodeConversionResult} interface. Its `result` property contains * the array of converted rule nodes, and its `isConverted` flag indicates whether the original rule was converted. * If the rule was not converted, the result array will contain the original node with the same object reference. * * @throws Error if the rule is invalid or cannot be converted. */ static convertToUbo(rule: HtmlFilteringRule): NodeConversionResult; /** * Handles special attribute selectors during AdGuard to AdGuard conversion: * - `[tag-content="content"]` -> `:contains(content)` * direct conversion, no changes to value. The value is emitted raw: * any quotes in it are literal characters of the text to match, not * a transport encoding to strip later. This mirrors CoreLibs, which * never unquotes `:contains()` arguments. * - `[wildcard="*content*"]` -> `:contains(/*.content*./s)` * convert search pattern to regular expression * - `[min-length="min"]` -> `:contains(/^(?=.{min,}$).*\/s)` * converts to a length-matching regular expression. * - `[max-length="max"]` -> `:contains(/^(?=.{0,max}$).*\/s)` * converts to a length-matching regular expression. * * Note: This attribute selector to pseudo-class selector conversion * is needed because AdGuard special attribute selectors are going * to be deprecated and removed soon. * * @param name Name of the special attribute selector. * @param value Value of the special attribute selector. * * @returns A {@link SimpleSelector} to add to the current complex selector. */ private static convertSpecialAttributeSelectorAdgToAdg; /** * Handles special pseudo-class selectors during AdGuard to AdGuard conversion: * - `:contains(text)` -> kept as-is * - `:-abp-contains(text)` -> kept as-is * - `:has-text(text)` -> `:contains(text)` * `:has-text()` is a documented synonym for `:contains()` in AdGuard products. * * @param name Name of the special pseudo-class selector. * @param argument Argument of the special pseudo-class selector. * * @returns A {@link SimpleSelector} to add to the current complex selector, * or `true` to keep the original pseudo-class selector as-is. * * @throws Rule conversion error for mixed syntax. */ private static convertSpecialPseudoClassSelectorAdgToAdg; /** * Since special attribute selectors only AdGuard-specific, * we should never encounter them in uBlock rules. * * @throws Rule conversion error for mixed syntax. */ private static convertSpecialAttributeSelectorUboToAdg; /** * Handles special pseudo-class selectors during uBlock to AdGuard conversion: * - `:has-text(text)` -> `:contains(text)` * direct conversion, no changes to argument * - `:min-text-length(min)` -> `:contains(/^(?=.{min,MAX_CONVERSION_DEFAULT}$).*\/s)` * converts to a length-matching regular expression. * * @param name Name of the special pseudo-class selector. * @param argument Argument of the special pseudo-class selector. * * @returns A {@link SimpleSelector} to add to the current complex selector. * * @throws If AdGuard-specific pseudo-class selector is found in uBlock rule. */ private static convertSpecialPseudoClassSelectorUboToAdg; /** * Handles special attribute selectors during AdGuard to uBlock conversion: * - `[tag-content="content"]` -> `:has-text(content)` * direct conversion, no changes to value * - `[wildcard="*content*"]` -> `:has-text(/*.content*./s)` * convert search pattern to regular expression * - `[min-length="min"]` -> `:min-text-length(min)` * direct conversion, no changes to value * - `[max-length]` is skipped. * * @param name Name of the special attribute selector. * @param value Value of the special attribute selector. * * @returns A {@link SimpleSelector} to add to the current complex selector, or `false` to skip it. */ private static convertSpecialAttributeSelectorAdgToUbo; /** * Handles special pseudo-class selectors during AdGuard to uBlock conversion: * - `:contains(text)` -> `:has-text(text)` * direct conversion, no changes to argument. * * @param name Name of the special pseudo-class selector. * @param argument Argument of the special pseudo-class selector. * * @returns A {@link SimpleSelector} to add to the current complex selector. * * @throws If uBlock-specific pseudo-class selector is found in AdGuard rule. */ private static convertSpecialPseudoClassSelectorAdgToUbo; /** * Pre-scans a complex selector's child selectors * for {@link AdgAttributeSelectors.MinLength} * and {@link AdgAttributeSelectors.MaxLength} attribute selectors. * * Resolves duplicates to the most *restrictive* value: * - for multiple `[min-length]` selectors, the largest value is selected; * - for multiple `[max-length]` selectors, the smallest value is selected. * * Logs a warning when duplicate length selectors are found. * * @param selectors Child selectors of a complex selector to scan. * * @returns Resolved length constraints, * or `null` if no length selectors were found. */ private static collectLengthConstraints; /** * Checks whether the raw HTML filtering rule body contains any special * attribute selector or special pseudo-class selector outside of quoted * text, where marker-like substrings may occur as plain text, e.g. the * `:contains(` in `[data-x=":contains(foo"]`. * * Quote handling: * - both single and double quotes toggle the quoted state; * - a backslash-escaped quote (`\"`) does not toggle the quoted state; * - a doubled quote (`""`) inside a quoted section is treated as an * escaped quote (AdGuard escaping convention for `[tag-content]` values). * * Used by the tolerant fallback to distinguish rules with special * selectors (which must be kept as-is if unparseable) from genuinely * invalid rules (which must be rejected). * * @param raw Raw HTML filtering rule body. * * @returns `true` if the body contains special selectors, otherwise `false`. */ private static hasSpecialSimpleSelectors; /** * Converts a HTML filtering rule body by handling special simple selectors via callbacks. * Special simple selectors are skipped in the converted selector list and should be handled from callee. * * @param body HTML filtering rule body to convert. * @param parser HTML filtering rule body parser used for parsing raw value bodies. * @param generator HTML filtering rule body generator used for generating raw value bodies. * @param onSpecialAttributeSelector Callback invoked when a special attribute selector is found. * @param onSpecialPseudoClassSelector Callback invoked when a special pseudo-class selector is found. * @param shouldMergeLengthSelectors If true, `[min-length]` and `[max-length]` attribute * selectors within the same complex selector are merged into a single `:contains()` pseudo-class. * Defaults to `false`. * * @returns Converted selector list without special simple selectors. */ private static convertBody; /** * Checks whether a [tag-content] literal can be emitted as a standalone * `:contains()` pseudo-class directly followed by another special * pseudo-class ("chain-safe"): * * - it must not be empty and must not start with a slash — a leading `/` * makes the argument look like a regular expression to CoreLibs; * - it must not contain quotes, parentheses, brackets, braces, * backslashes or colons — CoreLibs' special pseudo-class argument * scanner splits a `:contains()` chain at the next free `:`, and any * of those characters would swallow the separator, silently merging * the two pseudo-classes into a single bogus literal argument. * * A literal that is not chain-safe is still valid as a *single* * `:contains()` pseudo-class (CoreLibs takes the whole argument between * `(` and the closing `)` of the sole pseudo-class in the rule), but it * must be folded into a merged regular expression as soon as another * regex-like constraint has to be emitted as well. * * @param value [tag-content] value to check. * * @returns `true` if the literal is chain-safe, otherwise `false`. */ private static isChainSafeLiteral; /** * Emits the pseudo-classes converted from the deferred special attribute * selectors ([tag-content], [wildcard], [min-length], [max-length]) of a * single complex selector, honoring the constraints of CoreLibs' HTML * filter: * * - at most one "tag-content" constraint (a literal `:contains()`) and one * "wildcard" constraint (a regex `:contains()`) are allowed per selector, * so e.g. `[wildcard="..."]` cannot be chained with * `[max-length="..."]` as two regex `:contains()` pseudo-classes; * - a literal `:contains()` may be chained in front of a single regex * `:contains()` only when it is chain-safe, see * {@link HtmlRuleConverter.isChainSafeLiteral}. * * Combinations which cannot be represented as such a chain — an unsafe * literal plus any regex-like constraint, or several regex-like * constraints (multiple [wildcard] values, `[wildcard]` combined with * `[min/max-length]`, multiple [tag-content] values) — are merged into a * single regular expression instead: * `/^(?=.{min,max}$)(?=)(?=.*).*\/s`. * * @param convertedSelectors Converted selectors of the complex selector, * the emitted pseudo-classes are appended to it. * @param tagContentValues Deferred [tag-content] values. * @param wildcardGlobValues Deferred [wildcard] glob values. * @param lengthConstraints Deferred length constraints collected from * [min-length] / [max-length], or `null` if absent. */ private static appendSpecialAttributePseudoClasses; /** * Clones a simple selector or selector combinator node. * * @param selector Simple selector or selector combinator node to clone. * * @returns Cloned simple selector or selector combinator node. */ private static cloneSelector; /** * Creates a CSS pseudo-class selector node. * * @param name The name of the pseudo-class selector. * @param argument Optional argument of the pseudo-class selector. * * @returns CSS pseudo-class selector node. */ private static getPseudoClassSelectorNode; /** * Asserts that the given array is not empty. * * @param array Array to check. * @param errorMessage Error message to use if the array is empty. * * @throws If the array is empty. */ private static assertNotEmpty; /** * Asserts that the given special attribute / pseudo-class length value is valid. * * @param name Name of the attribute or pseudo-class. * @param value Value to parse. * @param notIntErrorMessage Error message when the value is not an integer. * @param notPositiveErrorMessage Error message when the value is not positive. * * @throws If the value is not a valid number or not positive. */ private static assertValidLengthValue; }