Via Css 检验

用于检验Via的Adblock规则中的Css隐藏规则是否有错误,支持自动运行、菜单操作、WebView版本检测及规则数量统计

目前為 2025-03-24 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Via Css 检验
// @namespace    https://viayoo.com/
// @version      2.9
// @license      MIT
// @description  用于检验Via的Adblock规则中的Css隐藏规则是否有错误,支持自动运行、菜单操作、WebView版本检测及规则数量统计
// @author       Copilot & Grok
// @run-at       document-end
// @match        *://*/*
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @require      https://cdn.jsdelivr.net/npm/[email protected]/js/lib/beautify-css.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/csstree.min.js
// ==/UserScript==

(function() {
    'use strict';

    function getCssFileUrl() {
        const currentHost = window.location.hostname;
        return `https://${currentHost}/via_inject_blocker.css`;
    }

    function formatCssWithJsBeautify(rawCss) {
        try {
            const formatted = css_beautify(rawCss, {
                indent_size: 2,
                selector_separator_newline: true
            });
            console.log('格式化后的CSS:', formatted);
            return formatted;
        } catch (error) {
            console.error(`CSS格式化失败:${error.message}`);
            return null;
        }
    }

    function getWebViewVersion() {
        const ua = navigator.userAgent;
        console.log('User-Agent:', ua);
        const patterns = [
            /Chrome\/([\d.]+)/i,
            /wv\).*?Version\/([\d.]+)/i,
            /Android.*?Version\/([\d.]+)/i
        ];

        for (let pattern of patterns) {
            const match = ua.match(pattern);
            if (match) {
                console.log('匹配到的版本:', match[1]);
                return match[1];
            }
        }
        return null;
    }

    function checkPseudoClassSupport(cssContent) {
        const pseudoClasses = [{
                name: ':hover',
                minVersion: 37
            },
            {
                name: ':focus',
                minVersion: 37
            },
            {
                name: ':active',
                minVersion: 37
            },
            {
                name: ':nth-child',
                minVersion: 37
            },
            {
                name: ':not',
                minVersion: 37
            },
            {
                name: ':where',
                minVersion: 88
            },
            {
                name: ':is',
                minVersion: 88
            },
            {
                name: ':has',
                minVersion: 105
            }
        ];
        const webviewVersion = getWebViewVersion();
        let unsupportedPseudo = [];

        if (!webviewVersion) {
            return "无法检测到WebView或浏览器内核版本";
        }

        const versionNum = parseFloat(webviewVersion);
        console.log('检测到的WebView版本:', versionNum);

        pseudoClasses.forEach(pseudo => {
            if (cssContent.includes(pseudo.name)) {
                if (versionNum < pseudo.minVersion) {
                    unsupportedPseudo.push(`${pseudo.name} (需要版本 ${pseudo.minVersion}+)`);
                }
            }
        });

        return unsupportedPseudo.length > 0 ?
            `当前版本(${webviewVersion})不支持以下伪类:${unsupportedPseudo.join(', ')}` :
            `当前版本(${webviewVersion})支持所有使用的伪类`;
    }

    function countCssRules(formattedCss) {
        if (!formattedCss) return 0;

        try {
            const ast = csstree.parse(formattedCss);
            let count = 0;

            csstree.walk(ast, (node) => {
                if (node.type === 'Rule' && node.prelude && node.prelude.type === 'SelectorList') {
                    const selectors = node.prelude.children.size;
                    count += selectors;
                }
            });
            console.log('计算得到的规则总数:', count);
            return count;
        } catch (e) {
            console.error('CSS规则计数失败:', e);
            return 0;
        }
    }

    function getCssPerformance(totalCssRules) {
        if (totalCssRules <= 5000) {
            return '✅CSS规则数量正常,可以流畅运行';
        } else if (totalCssRules <= 7000) {
            return '❓CSS规则数量较多,可能会导致设备运行缓慢';
        } else if (totalCssRules < 9999) {
            return '⚠️CSS规则数量接近上限,可能明显影响设备性能';
        } else {
            return '🆘CSS规则数量过多,不建议订阅此规则';
        }
    }

    function truncateErrorLine(errorLine, maxLength = 150) {
        return errorLine.length > maxLength ? errorLine.substring(0, maxLength) + "..." : errorLine;
    }

    async function fetchAndFormatCss() {
        const url = getCssFileUrl();
        console.log('尝试获取CSS文件:', url);
        try {
            const response = await fetch(url, {
                cache: 'no-store'
            });
            if (!response.ok) throw new Error(`HTTP状态: ${response.status}`);
            const text = await response.text();
            console.log('原始CSS内容:', text);
            return text;
        } catch (error) {
            console.error(`获取CSS失败:${error.message}`);
            return null;
        }
    }

    function translateErrorMessage(englishMessage) {
        const translations = {
            "Identifier is expected": "需要标识符",
            "Unexpected end of input": "输入意外结束",
            "Selector is expected": "需要选择器",
            "Invalid character": "无效字符",
            "Unexpected token": "意外的标记",
            '"]" is expected': '需要 "]"',
            '"{" is expected': '需要 "{"',
            'Unclosed block': '未闭合的块',
            'Unclosed string': '未闭合的字符串',
            'Property is expected': '需要属性名',
            'Value is expected': '需要属性值',
            "Percent sign is expected": "需要百分号 (%)",
            'Attribute selector (=, ~=, ^=, $=, *=, |=) is expected': '需要属性选择器运算符(=、~=、^=、$=、*=、|=)',
            'Semicolon is expected': '需要分号 ";"',
            'Number is expected': '需要数字',
            'Colon is expected': '需要冒号 ":"'
        };
        return translations[englishMessage] || `${englishMessage}`;
    }

    function validateCss(rawCss, formattedCss, isAutoRun = false) {
        if (!formattedCss) return;

        let hasError = false;
        const errors = [];
        const lines = formattedCss.split('\n');
        const totalCssRules = countCssRules(formattedCss);
        const cssPerformance = getCssPerformance(totalCssRules);
        const pseudoSupport = checkPseudoClassSupport(rawCss);

        try {
            csstree.parse(formattedCss, {
                onParseError(error) {
                    hasError = true;
                    const errorLine = lines[error.line - 1] || "无法提取错误行";
                    const truncatedErrorLine = truncateErrorLine(errorLine);
                    const translatedMessage = translateErrorMessage(error.message);

                    errors.push(`
CSS 解析错误:
- 位置:第 ${error.line} 行
- 错误信息:${translatedMessage}
- 错误片段:${truncatedErrorLine}
                    `.trim());
                }
            });

            const resultMessage = `
CSS验证结果:
- 规则总数:${totalCssRules}
- 性能评价:${cssPerformance}
- 伪类支持:${pseudoSupport}
${hasError ? '\n发现错误:\n' + errors.join('\n\n') : '\n未发现语法错误'}
            `.trim();

            if (isAutoRun && hasError) {
                alert(resultMessage);
            } else if (!isAutoRun) {
                alert(resultMessage);
            }
        } catch (error) {
            const translatedMessage = translateErrorMessage(error.message);
            alert(`CSS验证失败:${translatedMessage}`);
        }
    }

    async function autoRunCssValidation() {
        const rawCss = await fetchAndFormatCss();
        if (rawCss) {
            const formattedCss = formatCssWithJsBeautify(rawCss);
            if (formattedCss) {
                validateCss(rawCss, formattedCss, true);
            }
        }
    }

    function initializeScript() {
        const isAutoRunEnabled = GM_getValue("autoRun", true);

        GM_registerMenuCommand(isAutoRunEnabled ? "关闭自动运行" : "开启自动运行", () => {
            GM_setValue("autoRun", !isAutoRunEnabled);
            alert(`自动运行已${isAutoRunEnabled ? "关闭" : "开启"}!`);
        });

        GM_registerMenuCommand("验证CSS文件", async () => {
            const rawCss = await fetchAndFormatCss();
            if (rawCss) {
                const formattedCss = formatCssWithJsBeautify(rawCss);
                if (formattedCss) {
                    validateCss(rawCss, formattedCss, false);
                }
            }
        });

        if (isAutoRunEnabled) {
            autoRunCssValidation();
        }
    }

    initializeScript();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址