移动端增强型广告拦截器

一个手机端via浏览器能用的强大的广告拦截器

// ==UserScript==
// @name         移动端增强型广告拦截器
// @namespace    http://tampermonkey.net/
// @version      3.9.7
// @description  一个手机端via浏览器能用的强大的广告拦截器
// @match        *://*/*
// @license      MIT
// @grant        unsafeWindow
// @grant        GM_registerMenuCommand
// @grant        GM_notification
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_getResourceText
// @grant        GM_addStyle
// @grant        GM_openInTab
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';
    const HOSTNAME = location.hostname.replace(/\./g, '\\.');
    const REGEX = {
        dynamicId: /^(?:([0-9a-f]{4}-?){4,}|[0-9a-f]{16,}|[\dA-F-]{18,}|[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12})$/i,
        jsAdPattern: /(?:window\.open|document\.write|createElement\(['"]script['"]|location\.replace|setTimeout\s*\([^)]*?\b(?:window\.open|document\.write)|eval\s*\(|new\s+Function\s*\(|appendChild\s*\(.*?\bscript\b|on(?:click|load)\s*=\s*['"]?\s*(?:window\.open))/i,
        adAttributes: /(推广|广告|gg|sponsor|推荐|guanggao|syad|bfad|弹窗|悬浮|葡京|banner|pop(?:up|under)|track(?:ing)?)(?:$|[_-])/i,
        thirdParty: new RegExp(`^(https?:\\/\\/(.*\\.)?${HOSTNAME}(\\/|$)|data:|about:blank)`, 'i'),
        obfuscationPatterns: [
            { type: '字符串混淆属性访问', pattern: /\\x73\\x72\\x63\\s*=/i, riskLevel: 3 },
            { type: '数学对象长属性访问', pattern: /Math\[\s*['"]\w{5,}['"]\s*\]/im, riskLevel: 2 },
            { type: '十六进制/Unicode转义序列', pattern: /(?:\\x[0-9a-f]{2}(?:\\x[0-9a-f]{2}){5,}|\\u[0-9a-f]{4}(?:\\u[0-9a-f]{4}){3,})/gi, riskLevel: 3 },
            { type: '数组数值操作', pattern: /\[(?:0x[0-9a-f]+|\d+),?(?:0x[0x][0-9a-f]+|\d+)\]\.(?:join|reverse|map)/i, riskLevel: 2 },
            { type: '动态代码执行', pattern: /(eval|(?:new\s+Function\s*\(\s*(['"])\s*)\2|set(?:Timeout|Interval|Immediate)\s*\(\s*function\s*\(\)\s*\{)/i, riskLevel: 4 },
            { type: '文档对象长属性访问', pattern: /(?:document|window)\[\s*['"]\w{5,}['"]\s*\]/im, riskLevel: 3 },
            { type: '短变量名长字符串赋值', pattern: /\b[a-z\d]{1,3}\s*=\s*['"]\w{16,}['']/gi, riskLevel: 2 },
            { type: '可疑变量初始化', pattern: /(var|let|const)\s+([a-z\d]{2,4})\s*=\s*(?:\d{8,}|"(?:[0-9a-f]{16,}|[\w+/=]{20,})")/gi, riskLevel: 3 },
            { type: '多重属性混淆', pattern: /(?:'src'|''data-rc'')\.forEach\(function\(a\)\{this\.setAttribute\(a,/im, riskLevel: 4 },
            { type: '定时器混淆链', pattern: /setTimeout\(function\(\)\{setInterval\(function\(\)\{new\s+Function\(.*?\)/gim, riskLevel: 5 },
            { type: '编码payload执行', pattern: /(?:eval\(\s*atob\(\s*["'][A-Za-z0-9+/=]{40,}["']\s*\)|eval\(\s*decodeURIComponent\(\s*escape\(\s*atob\(\s*["'][A-Za-z0-9+/=]{40,}["']\s*\)\s*\)\s*\))/gi, riskLevel: 5 },
            { type: '长Base64字符串', pattern: /[A-Za-z0-9+/=]{60,}/gi, riskLevel: 3 },
            { type: '多级属性访问', pattern: /window\[\w{5,}\]\[\w{5,}\]\[\w{5,}\]/gim, riskLevel: 3 },
            { type: '异常函数调用链', pattern: /function\s+\w{1,3}\(\)\{return\s+\w{1,3}\(\)\.call\(this\)\}\(\)\.apply\(window\)/gim, riskLevel: 4 },
            { type: '异常事件监听', pattern: /addEventListener\s*\(["']click["'],\s*function\s*\(\)\{[^}]*eval\s*\(/gim, riskLevel: 4 },
            { type: '环境检测代码', pattern: /navigator\.\w+\s*===?\s*undefined/g, riskLevel: 5 },
            { type: '时间戳混淆', pattern: /0x[a-f0-9]{8}(?:\+0x[a-f0-9]{8}){3,}/g, riskLevel: 3 },
            { type: '反格式化字符串', pattern: /%[0-9a-f]{2}%[0-9a-f]{2}%[0-9a-f]{2}/g, riskLevel: 3 }
        ],
    };
    const DEFAULT_MODULES = {
        main: false,
        dynamicSystem: false,
        layoutSystem: false,
        mergedMediaSystem: false,
        thirdPartyBlock: false,
        specialUA: true,
        coreProcessor: true
    };
    const DEFAULT_CSP_RULES = [
        { id: 1, name: '仅允许同源脚本', rule: "script-src 'self'", enabled: true },
        { id: 2, name: '禁止内联脚本', rule: "script-src 'unsafe-inline'", enabled: false },
        { id: 3, name: '禁止eval执行', rule: "script-src 'unsafe-eval'", enabled: false },
        { id: 4, name: '阻止外部样式加载', rule: "style-src 'self'", enabled: false },
        { id: 5, name: '阻止内联样式执行', rule: "style-src 'unsafe-inline'", enabled: false },
        { id: 6, name: '阻止外部图片加载', rule: "img-src 'self'", enabled: true },
        { id: 7, name: '禁止所有框架加载', rule: "frame-src 'none'", enabled: false },
        { id: 8, name: '禁止媒体资源加载', rule: "media-src 'none'", enabled: false },
        { id: 9, name: '禁止对象嵌入', rule: "object-src 'none'", enabled: false }
    ];
    const CONFIG = {
        modules: { ...DEFAULT_MODULES },
        protectionRules: {
            dynamicIdLength: 32,
            zIndexThreshold: 9999
        },
        csp: {
            enabled: false,
            rules: DEFAULT_CSP_RULES.map(rule => ({ ...rule }))
        },
        mobileOptimizations: {
            lazyLoadImages: true,
            removeImagePlaceholders: true
        },
        moduleNames: {
            dynamicSystem: '动态检测系统',
            layoutSystem: '布局检测系统',
            mergedMediaSystem: '图片广告移除',
            thirdPartyBlock: '第三方拦截',
            specialUA: '添加特殊UA',
            coreProcessor: '核心检测'
        },
        modulePriority: [
            'thirdPartyBlock',
            'specialUA',
            'dynamicSystem',
            'mergedMediaSystem',
            'layoutSystem',
            'coreProcessor'
        ],
        performance: {
            highRiskModules: ['thirdPartyBlock', 'dynamicSystem', 'mergedMediaSystem', 'layoutSystem', 'coreProcessor'],
            visibleAreaPriority: true,
            styleCacheTimeout: 600000,
            mutationProcessLimit: 40,
            mutationProcessTimeLimit: 10,
            idleCallbackTimeout: 2500,
            throttleScrollDelay: 200,
            debounceMutationDelay: 300,
            longTaskThreshold: 500,
            degradeThreshold: 5
        },
        originalUA: {
            userAgent: navigator.userAgent,
            platform: navigator.platform
        },
        whitelist: {}
    };
    const Utils = {
        throttle(fn, delay) {
            let last = 0, timer = null;
            return function(...args) {
                const now = Date.now();
                if (now - last >= delay) {
                    fn.apply(this, args);
                    last = now;
                } else {
                    clearTimeout(timer);
                    timer = setTimeout(() => {
                        fn.apply(this, args);
                        last = now;
                    }, delay - (now - last));
                }
            };
        },
        debounce(fn, delay) {
            let timer = null;
            return function(...args) {
                clearTimeout(timer);
                timer = setTimeout(() => {
                    try {
                        fn.apply(this, args);
                    } catch(e) {
                        Logs.add('error', this, {
                            type: 'debounceError',
                            detail: e.message
                        });
                    }
                }, delay);
            }
        }
    };
    const SharedUtils = {
        generateElementPattern(element) {
            try {
                if (!element || !(element instanceof Element)) return '';
                let pattern = element.tagName || '';
                if (element.id) pattern += `#${element.id}`;
                if (element.className) {
                    const firstClass = element.className.split(/\s+/)[0];
                    if (firstClass) pattern += `.${firstClass}`;
                }
                return pattern;
            } catch(e) {
                return '';
            }
        },
        getElementContent(element) {
            try {
                if (!element || !(element instanceof Element)) return '[无效元素]';
                if (element.tagName === 'SCRIPT') {
                    const src = element.src;
                    const content = element.textContent;
                    if (src) return `SCRIPT_SRC: ${src.slice(0, 200)}` + (src.length > 200 ? '...' : '');
                    if (content) return `SCRIPT_CONTENT: ${content.replace(/\s+/g, ' ').slice(0, 200)}` + (content.length > 200 ? '...' : '');
                    return 'SCRIPT_EMPTY';
                }
                 if (element.tagName === 'IFRAME') {
                    const src = element.src;
                     if (src) return `IFRAME_SRC: ${src.slice(0, 200)}` + (src.length > 200 ? '...' : '');
                     return 'IFRAME_EMPTY';
                 }
                 if (element.tagName === 'IMG') {
                    const src = element.src || element.dataset.src;
                    if (src) return `IMG_SRC: ${src.slice(0, 200)}` + (src.length > 200 ? '...' : '');
                    return 'IMG_EMPTY';
                 }
                const outerHTML = element.outerHTML;
                if (outerHTML) {
                   return outerHTML.replace(/\s+/g, ' ')
                       .replace(/<!--.*?-->/g, '')
                       .slice(0, 200) +
                       (outerHTML.length > 200 ? '...' : '');
                }
                return '[无法获取内容]';
            } catch(e) {
                return '[安全限制无法获取内容]';
            }
        }
    };
    const Logs = {
        logs: [],
        processedForLog: new WeakSet(),
        maxLogs: 50,
        add(moduleKey, element, reason) {
            if (!(element instanceof Element)) return;
             if (this.processedForLog.has(element)) {
                 return;
             }
            const currentDomain = new URL(location.href).hostname;
            const getElementInfoForLogDisplay = (el) => {
                try {
                    const attrs = {};
                    const attributeNames = ['id', 'class', 'src', 'href', 'data-src', 'style'];
                     for (const name of attributeNames) {
                         if (el.hasAttribute(name)) {
                             attrs[name] = el.getAttribute(name).slice(0, 100) + (el.getAttribute(name).length > 100 ? '...' : '');
                         }
                     }
                    return {
                        tag: el.tagName || '未知标签',
                        id: el.id ? `#${el.id}` : '',
                        class: el.className ? `.${el.className.split(/\s+/).join('.')}` : '',
                        src: el.src || el.href || el.dataset.src || '',
                        attributes: attrs
                    };
                } catch(e) {
                    return { error: '元素解析失败' };
                }
            };
            const elementInfoDisplay = getElementInfoForLogDisplay(element);
            const fullContent = SharedUtils.getElementContent(element);
             if (fullContent.trim() === '[无效元素]' || fullContent.trim() === '[无法获取内容]' || fullContent.includes('[安全限制无法获取内容]')) {
                 return;
             }
            this.processedForLog.add(element);
            this.logs.push({
                module: CONFIG.moduleNames[moduleKey] || moduleKey,
                elementSummary: `${elementInfoDisplay.tag}${elementInfoDisplay.id}${elementInfoDisplay.class}`,
                content: fullContent,
                context: {
                    src: (elementInfoDisplay.src || '').slice(0, 200),
                    attributes: elementInfoDisplay.attributes || {},
                    parentTag: element.parentElement ? element.parentElement.tagName : '无父节点',
                    domain: currentDomain
                },
                reason: {
                    type: reason.type || '自动拦截',
                    detail: (reason.detail || '').slice(0, 150),
                    pattern: reason.regex ? reason.regex.map(r => r.source.slice(0, 100)) : []
                },
                timestamp: Date.now(),
            });
            if (this.logs.length > this.maxLogs) {
                this.logs = this.logs.slice(this.logs.length - this.maxLogs);
            }
        },
        clear() {
            this.logs = [];
            this.processedForLog = new WeakSet();
            GM_notification('日志已清空');
        },
        showInAlert() {
            const logText = this.logs.map((log, index) => {
                const parts = [];
                if (log.reason && log.reason.type) parts.push(`类型: ${log.reason.type}`);
                if (log.reason && log.reason.detail) parts.push(`详细: ${log.reason.detail}`);
                if (log.reason && log.reason.pattern && log.reason.pattern.length) parts.push(`匹配规则: ${log.reason.pattern.join(', ')}`);
                const contextParts = [];
                if (log.context && log.context.src) contextParts.push(`来源: ${log.context.src}`);
                if (log.context && log.context.parentTag) contextParts.push(`父节点: ${log.context.parentTag}`);
                const contextString = contextParts.length > 0 ? `上下文: ${contextParts.join(' ')}` : '';
                return [
                    `序号: ${index + 1}`,
                    `模块: ${log.module}`,
                    `元素: ${log.elementSummary}`,
                    `内容: ${log.content}`,
                    contextString,
                    ...parts
                ].filter(line => line.trim() !== '').join('\n');
            }).join('\n\n');
            const promptMessage = `广告拦截日志(最近${this.logs.length}条):\n\n${logText || '暂无日志'}\n\n请输入序号(多个序号如 123 或 1,3,5),或输入 0 清空当前域名的白名单:`;
            let input = prompt(promptMessage, "");
            if (input === null) return;
            input = input.trim();
            if (input === "0") {
                Whitelist.clearCurrentDomain();
                GM_notification('白名单已清空', `当前域名 (${location.hostname}) 的白名单已清空`);
                location.reload();
            } else {
                let inputIndices;
                if (input.includes(',')) {
                    inputIndices = input.split(',')
                                        .map(s => parseInt(s.trim(), 10))
                                        .filter(n => !isNaN(n))
                                        .map(n => n - 1);
                } else {
                    inputIndices = input.split('')
                                        .map(s => parseInt(s, 10))
                                        .filter(n => !isNaN(n))
                                        .map(n => n - 1);
                }
                const validIndices = [...new Set(inputIndices)].filter(index => index >= 0 && index < this.logs.length);
                let addedCount = 0;
                validIndices.forEach(index => {
                    const logEntry = this.logs[index];
                     if (logEntry && logEntry.content) {
                         Whitelist.add(location.hostname, logEntry.content);
                         addedCount++;
                     } else if (logEntry) {
                          console.warn('Log entry has no content to whitelist:', logEntry);
                     }
                });
                if (addedCount > 0) {
                     UIController.saveConfig();
                     GM_notification('已添加到白名单', `${addedCount} 项内容已添加到白名单`);
                     location.reload();
                } else if (validIndices.length > 0) {
                     alert('未添加任何内容到白名单,请检查输入或日志条目是否有效');
                }
            }
        }
    };
    const Whitelist = {
        init() {
             if (!CONFIG.whitelist) {
                 CONFIG.whitelist = {};
             }
            const currentDomain = new URL(location.href).hostname;
             if (!CONFIG.whitelist[currentDomain]) {
                 CONFIG.whitelist[currentDomain] = [];
             }
             CONFIG.whitelist[currentDomain] = CONFIG.whitelist[currentDomain].filter(content => content && content.trim() !== '' && !content.includes('[无法获取内容]') && !content.includes('[安全限制无法获取内容]'));
        },
        add(domain, content) {
             if (!domain || !content || content.trim() === '' || content.includes('[无法获取内容]') || content.includes('[安全限制无法获取内容]')) {
                 return;
             }
             if (!CONFIG.whitelist[domain]) {
                 CONFIG.whitelist[domain] = [];
             }
             if (!CONFIG.whitelist[domain].includes(content)) {
                 CONFIG.whitelist[domain].push(content);
             }
        },
        isWhitelisted(element) {
             if (!element || !(element instanceof Element)) return false;
             const currentDomain = new URL(location.href).hostname;
             if (!CONFIG.whitelist[currentDomain] || CONFIG.whitelist[currentDomain].length === 0) {
                 return false;
             }
             const elementContent = SharedUtils.getElementContent(element);
             return CONFIG.whitelist[currentDomain].includes(elementContent);
        },
        clear(domain) {
             if (CONFIG.whitelist[domain]) {
                 CONFIG.whitelist[domain] = [];
             }
        },
        clearCurrentDomain() {
             const currentDomain = new URL(location.href).hostname;
             this.clear(currentDomain);
             UIController.saveConfig();
        },
        clearAll() {
             CONFIG.whitelist = {};
             UIController.saveConfig();
        }
    };
    const perf = {
        adElements: new Set(),
        processed: new WeakSet(),
        styleCache: new Map(),
        lastStyleCacheClear: Date.now(),
        performanceEntries: [],
        degraded: false,
        isProcessing: false
    };
    const ModuleManager = {
        modules: {},
        register(name, module) {
            this.modules[name] = module;
        },
        init() {
            Object.values(this.modules).forEach(module => {
                if (typeof module.init === 'function') {
                    module.init();
                }
            });
        },
        run(moduleKey, element) {
            if (!CONFIG.modules.main || !CONFIG.modules[moduleKey]) return false;
            if (Whitelist.isWhitelisted(element)) return false;
            if (element.dataset.adCleaned || element.dataset.adNuclearRemoved) return false;
            const module = this.modules[moduleKey];
            if (module && typeof module.check === 'function') {
                 const isBlocked = module.check(element);
                 if(isBlocked) {
                     perf.processed.add(element);
                 }
                return isBlocked;
            }
            return false;
        }
    };
    const AdUtils = {
        originalAppendChild: Node.prototype.appendChild,
        originalCreateElement: document.createElement,
        abortControllers: new WeakMap(),
        scriptObserver: null,
        isScriptHooksActive: false,
        removeElement(element, moduleKey, reason, isNuclear = false) {
             if (!(element instanceof Element)) return false;
             if (Whitelist.isWhitelisted(element)) {
                 perf.processed.add(element);
                 return false;
             }
             if (element.dataset.adCleaned || element.dataset.adNuclearRemoved) return false;
            Logs.add(moduleKey, element, {
                ...reason,
                detail: (isNuclear ? '[原子级移除] ' : '') + (reason.detail || ''),
                nuclear: isNuclear
            });
            perf.processed.add(element);
            try {
                if (element.parentNode) {
                    if (isNuclear) {
                        const unsafeAttributes = element.tagName === 'SCRIPT' ?
                            ['src', 'onload', 'onerror', 'integrity', 'type'] :
                            ['src', 'href', 'data-src'];
                        unsafeAttributes.forEach(attr => {
                            if (element.hasAttribute(attr)) {
                                try {
                                    element.removeAttribute(attr);
                                } catch(e) {}
                            }
                        });
                        try {
                             const comment = document.createComment(`Removed ad by ${moduleKey} [Nuclear]: ${reason.type}`);
                             if (element.parentNode) {
                                 element.parentNode.replaceChild(comment, element);
                             }
                        } catch (e) {
                             try {
                                 element.setAttribute('data-ad-cleaned', 'true');
                                 element.dataset.adNuclearRemoved = 'failed';
                             } catch (e2) {
                                  element.dataset.adRemovalFailed = 'true';
                             }
                        }
                         element.dataset.adNuclearRemoved = 'true';
                    } else {
                        element.setAttribute('data-ad-cleaned', 'true');
                    }
                    return true;
                } else {
                    element.remove();
                    element.dataset.adNuclearRemoved = 'true';
                    return true;
                }
            } catch(e) {
                if (element instanceof Element) {
                    element.dataset.adRemovalFailed = 'true';
                }
                return false;
            }
        },
        safeRemove(element, moduleKey, reason) {
            return this.removeElement(element, moduleKey, reason, false);
        },
        nuclearRemove(element, moduleKey, reason) {
            return this.removeElement(element, moduleKey, reason, true);
        },
        initScriptHooks() {
            if (this.isScriptHooksActive) return;
            this.isScriptHooksActive = true;
            const self = this;
             if (!Node.prototype._originalAppendChild) Node.prototype._originalAppendChild = Node.prototype.appendChild;
             if (!document._originalCreateElement) document._originalCreateElement = document.createElement;
            Node.prototype.appendChild = function(node) {
                 if (node.nodeType === 1 && node.tagName === 'SCRIPT') {
                    if (Whitelist.isWhitelisted(node)) {
                         return Node.prototype._originalAppendChild.call(this, node);
                    }
                    if (!CONFIG.modules.main || perf.degraded || node.dataset.adCleaned || node.dataset.adNuclearRemoved) {
                         return Node.prototype._originalAppendChild.call(this, node);
                    }
                    self.processScriptElementHook(node);
                    if (node.dataset.adCleaned || node.dataset.adNuclearRemoved) return node;
                 }
                return Node.prototype._originalAppendChild.call(this, node);
            };
            document.createElement = function(tagName) {
                const el = document._originalCreateElement.call(document, tagName);
                if (tagName.toLowerCase() === 'script') {
                    if (Whitelist.isWhitelisted(el)) return el;
                    if (!CONFIG.modules.main || perf.degraded || el.dataset.adCleaned || el.dataset.adNuclearRemoved) {
                         return el;
                    }
                    return self.createScriptProxy(el);
                }
                return el;
            };
            this.scriptObserver = new MutationObserver(mutations => {
                mutations.forEach(mutation => {
                    mutation.addedNodes.forEach(node => {
                        if (node.nodeType === 1 && node.tagName === 'SCRIPT') {
                             if (Whitelist.isWhitelisted(node)) return;
                             if (!CONFIG.modules.main || perf.degraded || node.dataset.adCleaned || node.dataset.adNuclearRemoved) {
                                  return;
                             }
                            self.processScriptElementHook(node);
                        }
                    });
                });
            });
             if (document.documentElement) {
                 this.scriptObserver.observe(document.documentElement, {
                     childList: true,
                     subtree: true
                 });
             } else {
                 window.addEventListener('DOMContentLoaded', () => {
                      this.scriptObserver.observe(document.documentElement, {
                          childList: true,
                          subtree: true
                      });
                 });
             }
        },
        cleanupScriptHooks() {
            if (!this.isScriptHooksActive) return;
            this.isScriptHooksActive = false;
             if (Node.prototype._originalAppendChild) Node.prototype.appendChild = Node.prototype._originalAppendChild;
             if (document._originalCreateElement) document.createElement = document._originalCreateElement;
            if (this.scriptObserver) {
                this.scriptObserver.disconnect();
            }
             this.abortControllers.forEach(controller => controller.abort());
             this.abortControllers.clear();
        },
        createScriptProxy(scriptElement) {
             if (Whitelist.isWhitelisted(scriptElement)) return scriptElement;
            return new Proxy(scriptElement, {
                set: (target, prop, value) => {
                     if (Whitelist.isWhitelisted(target)) {
                        target[prop] = value;
                        return true;
                     }
                     if (!CONFIG.modules.main || perf.degraded || target.dataset.adCleaned || target.dataset.adNuclearRemoved) {
                        target[prop] = value;
                        return true;
                     }
                    if ((prop === 'src' || prop === 'innerHTML') && typeof value === 'string') {
                         AdUtils.processScriptSource(target, prop, value);
                    }
                    target[prop] = value;
                    return true;
                },
                 get: (target, prop) => {
                     return Reflect.get(target, prop);
                 }
            });
        },
        processScriptElementHook(scriptElement) {
             if (Whitelist.isWhitelisted(scriptElement) || scriptElement.dataset.adCleaned || scriptElement.dataset.adNuclearRemoved) return;
             if (!CONFIG.modules.main || perf.degraded) return;
             scriptElement.dataset.adProcessedHook = 'true';
             if (CONFIG.modules.thirdPartyBlock && ModuleManager.modules.thirdPartyBlock?.isThirdParty(scriptElement.src || scriptElement.getAttribute('data-src') || '')) {
                  AdUtils.nuclearRemove(scriptElement, 'thirdPartyBlock', {
                      type: '第三方脚本标签',
                      detail: `SRC: ${(scriptElement.src || scriptElement.getAttribute('data-src') || '').slice(0, 100)}`
                  });
                  return;
             }
             if (CONFIG.modules.dynamicSystem && ModuleManager.modules.dynamicSystem) {
                  ModuleManager.modules.dynamicSystem.handleScriptElement(scriptElement);
             }
        },
        processScriptSource(scriptElement, prop, value) {
             if (Whitelist.isWhitelisted(scriptElement) || scriptElement.dataset.adCleaned || scriptElement.dataset.adNuclearRemoved) return;
             if (!CONFIG.modules.main || perf.degraded) return;
             scriptElement.dataset.adProcessedSource = 'true';
             if (CONFIG.modules.thirdPartyBlock && ModuleManager.modules.thirdPartyBlock?.isThirdParty(value)) {
                  AdUtils.nuclearRemove(scriptElement, 'thirdPartyBlock', {
                      type: '第三方脚本内容',
                      detail: `${prop}: ${value.slice(0, 100)}`
                  });
                  return;
             }
             if (CONFIG.modules.dynamicSystem && ModuleManager.modules.dynamicSystem) {
                  ModuleManager.modules.dynamicSystem.handleScriptSource(scriptElement, value);
             }
        }
    };
    const StyleManager = {
        styleElement: null,
        injectedSheets: new Set(),
        inject() {
            if (this.injectedSheets.size > 0) return;
            try {
                const sheet = new CSSStyleSheet();
                sheet.replaceSync(`
                    [data-ad-cleaned="true"] {
                        all: initial !important;
                        display: none !important;
                        visibility: hidden !important;
                        opacity: 0 !important;
                        position: absolute !important;
                        z-index: -9999 !important;
                        width: 0 !important;
                        height: 0 !important;
                        pointer-events: none !important;
                        overflow: hidden !important;
                        transform: translate(-9999px, -9999px) !important;
                        contain: layout style !important;
                        animation: none !important;
                        transition: none !important;
                        filter: none !important;
                        will-change: auto !important;
                    }
                    [data-ad-cleaned="true"] *,
                    [data-ad-cleaned="true"] *::before,
                    [data-ad-cleaned="true"] *::after {
                         all: unset !important;
                         display: none !important;
                         visibility: hidden !important;
                         opacity: 0 !important;
                         width: 0 !important;
                         height: 0 !important;
                         pointer-events: none !important;
                         z-index: -9999 !important;
                         contain: layout style !important;
                         animation: none !important;
                         transition: none !important;
                         filter: none !important;
                         will-change: auto !important;
                    }
                `);
                document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
                this.injectedSheets.add(sheet);
            } catch(e) {
            }
            const style = document.createElement('style');
            style.textContent = `
                @layer adBlocker {
                     *[data-ad-permanent="true"] {
                        animation: none !important;
                        transition: none !important;
                    }
                }
            `;
            style.setAttribute('data-ad-permanent', 'true');
            if (document.head) {
                document.head.appendChild(style);
            } else {
                document.documentElement.insertBefore(style, document.documentElement.firstChild);
            }
             const checkStyles = () => {
                 if (document.head) {
                     if (!document.head.contains(style)) {
                         document.head.prepend(style);
                     }
                 } else {
                     if (!document.documentElement.contains(style)) {
                         document.documentElement.prepend(style);
                     }
                 }
                 let foundAdopted = false;
                 for(const sheet of document.adoptedStyleSheets) {
                      if (this.injectedSheets.has(sheet)) {
                          foundAdopted = true;
                          break;
                      }
                 }
                 if (!foundAdopted) {
                      const sheet = new CSSStyleSheet();
                       sheet.replaceSync(`
                            [data-ad-cleaned="true"] {
                                all: initial !important;
                                display: none !important;
                                visibility: hidden !important;
                                opacity: 0 !important;
                                position: absolute !important;
                                z-index: -9999 !important;
                                width: 0 !important;
                                height: 0 !important;
                                pointer-events: none !important;
                                overflow: hidden !important;
                                transform: translate(-9999px, -9999px) !important;
                                contain: layout style !important;
                                animation: none !important;
                                transition: none !important;
                                filter: none !important;
                                will-change: auto !important;
                            }
                            [data-ad-cleaned="true"] *,
                            [data-ad-cleaned="true"] *::before,
                            [data-ad-cleaned="true"] *::after {
                                 all: unset !important;
                                 display: none !important;
                                 visibility: hidden !important;
                                 opacity: 0 !important;
                                 width: 0 !important;
                                 height: 0 !important;
                                 pointer-events: none !important;
                                 z-index: -9999 !important;
                                 contain: layout style !important;
                                 animation: none !important;
                                 transition: none !important;
                                 filter: none !important;
                                 will-change: auto !important;
                            }
                       `);
                       document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
                       this.injectedSheets.add(sheet);
                 }
                 requestAnimationFrame(checkStyles);
            };
            checkStyles();
        },
        remove() {
            this.injectedSheets.forEach(sheet => {
                document.adoptedStyleSheets = document.adoptedStyleSheets.filter(s => s !== sheet);
            });
            document.querySelectorAll('style[data-ad-permanent]').forEach(s => s.remove());
        },
        toggle(enable) {
            enable ? this.inject() : this.remove();
        }
    };
    const CSPManager = {
        currentPolicy: null,
        generatePolicy() {
            const merged = {};
            CONFIG.csp.rules.filter(rule => rule.enabled).forEach(rule => {
                const [directive, ...values] = rule.rule.split(/\s+/);
                merged[directive] = (merged[directive] || []).concat(values);
            });
            return Object.entries(merged)
                .map(([k, v]) => `${k} ${[...new Set(v)].join(' ')}`)
                .join('; ');
        },
        inject() {
            this.remove();
            if (!CONFIG.csp.enabled) return;
            const policy = this.generatePolicy();
            this.currentPolicy = policy;
            const meta = document.createElement('meta');
            meta.httpEquiv = "Content-Security-Policy";
            meta.content = policy;
            if (document.head) {
                document.head.appendChild(meta);
            } else {
                document.documentElement.insertBefore(meta, document.documentElement.firstChild);
            }
        },
        remove() {
            const meta = document.querySelector('meta[http-equiv="Content-Security-Policy"]');
            if (meta) meta.remove();
        },
        toggleRule(ruleId, enable) {
            const rule = CONFIG.csp.rules.find(r => r.id === ruleId);
            if (rule) rule.enabled = enable;
        },
        injectInitialCSP() {
            if (!CONFIG.csp.enabled) return;
            const initialRules = CONFIG.csp.rules
                .filter(rule => [1, 6].includes(rule.id) && rule.enabled)
                .map(rule => rule.rule)
                .join('; ');
            if (initialRules) {
                const meta = document.createElement('meta');
                meta.httpEquiv = "Content-Security-Policy";
                meta.content = initialRules;
                if (document.head) {
                    document.head.appendChild(meta);
                } else {
                    document.documentElement.insertBefore(meta, document.documentElement.firstChild);
                }
            }
        }
    };
    const Detector = {
        getCachedStyle(el) {
            if (Date.now() - perf.lastStyleCacheClear > CONFIG.performance.styleCacheTimeout) {
                perf.styleCache.clear();
                perf.lastStyleCacheClear = Date.now();
            }
             const key = `${el.nodeName}#${el.id}.${Array.from(el.classList).join('.')}`;
            if (!perf.styleCache.has(key)) {
                try {
                    perf.styleCache.set(key, getComputedStyle(el));
                } catch (e) {
                    return {};
                }
            }
            return perf.styleCache.get(key);
        },
        isVisible(el) {
             if (!(el instanceof Element)) return false;
            const style = Detector.getCachedStyle(el);
            if (style.display === 'none' || style.visibility === 'hidden' || parseFloat(style.opacity) < 0.01) return false;
            const rect = el.getBoundingClientRect();
            return !(
                rect.width === 0 ||
                rect.height === 0 ||
                (rect.top >= window.innerHeight || rect.bottom <= 0) ||
                (rect.left >= window.innerWidth || rect.right <= 0)
            );
        }
    };
    const Processor = {
        scheduleProcess() {
             if (perf.isProcessing) return;
             perf.isProcessing = true;
            requestIdleCallback(() => {
                 if (!CONFIG.modules.main || perf.degraded) {
                      perf.isProcessing = false;
                      setTimeout(() => Processor.scheduleProcess(), 1000);
                      return;
                 }
                const startTime = performance.now();
                const elementsToProcess = Array.from(perf.adElements).filter(el => !(el instanceof Element) || (!el.dataset.adCleaned && !el.dataset.adNuclearRemoved && !Whitelist.isWhitelisted(el)));
                perf.adElements.clear();
                let processedCount = 0;
                for (const element of elementsToProcess) {
                    if (performance.now() - startTime > CONFIG.performance.mutationProcessTimeLimit ||
                        processedCount >= CONFIG.performance.mutationProcessLimit) {
                        for (let i = processedCount; i < elementsToProcess.length; i++) {
                            if (!((elementsToProcess[i] instanceof Element) && (elementsToProcess[i].dataset.adCleaned || elementsToProcess[i].dataset.adNuclearRemoved)) && !Whitelist.isWhitelisted(elementsToProcess[i])) {
                                perf.adElements.add(elementsToProcess[i]);
                            }
                        }
                        perf.isProcessing = false;
                        Processor.scheduleProcess();
                        return;
                    }
                    if (!(element instanceof Element) || element.dataset.adCleaned || element.dataset.adNuclearRemoved || Whitelist.isWhitelisted(element)) {
                         continue;
                    }
                    this.processElement(element);
                    processedCount++;
                }
                perf.isProcessing = false;
                 if (perf.adElements.size > 0) {
                     Processor.scheduleProcess();
                 }
            }, { timeout: CONFIG.performance.idleCallbackTimeout });
        },
        processElement(element) {
             if (!(element instanceof Element) || element.dataset.adCleaned || element.dataset.adNuclearRemoved || Whitelist.isWhitelisted(element)) return;
             let modulePriority = [...CONFIG.modulePriority];
             if (CONFIG.performance.visibleAreaPriority && Detector.isVisible(element)) {
                 const highRisk = CONFIG.performance.highRiskModules;
                 modulePriority = [...highRisk, ...modulePriority.filter(m => !highRisk.includes(m))];
             }
            if (CONFIG.modules.coreProcessor) {
                 if (this.checkSuspiciousAttributes(element)) {
                      return;
                 }
                 if (this.checkSuspiciousIdAndLinks(element)) {
                      return;
                 }
            }
            for (const moduleKey of modulePriority) {
                 if (!CONFIG.modules[moduleKey] || moduleKey === 'coreProcessor') continue;
                 if (moduleKey === 'dynamicSystem' && element.tagName === 'SCRIPT') {
                      continue;
                 }
                 if (moduleKey === 'thirdPartyBlock' && (element.tagName === 'SCRIPT' || element.tagName === 'IFRAME' || element.tagName === 'IMG')) {
                      continue;
                 }
                if (ModuleManager.run(moduleKey, element)) {
                    return;
                }
            }
        },
        checkSuspiciousAttributes(el) {
             if (!CONFIG.modules.coreProcessor) return false;
             const shortVarPattern = /^[a-z\d]{1,3}$/i;
             const signaturePattern = /^(?:[\da-f]{32}|[\w+/=]{40,})$/i;
             const alphanumAlternate = /(?:[a-z]\d|\d[a-z]){5,}/gi;
             const isObfuscated = Array.from(el.attributes).some(attr => {
                 const isShortVar = shortVarPattern.test(attr.name);
                 const isObfuscatedValue = signaturePattern.test(attr.value) ||
                     (typeof attr.value === 'string' && attr.value.match(alphanumAlternate)?.length > 3);
                 const isDynamicName = /\[(?:0x[a-f\d]+|\d+)\]/.test(attr.name);
                 return (isShortVar && isObfuscatedValue) ||
                     attr.name.length > 20 ||
                     isDynamicName ||
                     (typeof attr.value === 'string' && attr.value.includes(';') && attr.value.length > 60);
             });
             if (isObfuscated) {
                  return AdUtils.safeRemove(el, 'coreProcessor', {
                      type: '可疑混淆属性',
                      detail: `元素属性疑似混淆`
                  });
             }
             return false;
        },
        checkSuspiciousIdAndLinks(el) {
             if (!CONFIG.modules.coreProcessor) return false;
             if (el.id && (
                 el.id.length > CONFIG.protectionRules.dynamicIdLength ||
                 REGEX.dynamicId.test(el.id)
             )) {
                 return AdUtils.safeRemove(el, 'coreProcessor', {
                     type: '可疑动态ID',
                     detail: `ID特征: ${el.id.slice(0, 50)}`
                 });
             }
             if (el.tagName === 'A') {
                 const href = el.getAttribute('href') || '';
                 if (href.startsWith('javascript:') && REGEX.jsAdPattern.test(href)) {
                     return AdUtils.safeRemove(el, 'coreProcessor', {
                         type: '可疑JS链接广告',
                         detail: `执行代码: ${href.slice(0, 100)}`,
                         regex: [REGEX.jsAdPattern.source]
                     });
                 }
             }
             return false;
        },
        collectInitialElements() {
             const treeWalker = document.createTreeWalker(
                 document.documentElement,
                 NodeFilter.SHOW_ELEMENT,
                 null,
                 false
             );
             while (treeWalker.nextNode()) {
                 const element = treeWalker.currentNode;
                 if (!(element instanceof Element)) continue;
                 if (!element.dataset.adCleaned && !element.dataset.adNuclearRemoved && !Whitelist.isWhitelisted(element)) {
                     perf.adElements.add(element);
                 }
             }
        }
    };
    const mainObserver = new MutationObserver(mutations => {
        if (!CONFIG.modules.main || perf.degraded) return;
        mutations.forEach(mutation => {
            if (mutation.type === 'childList') {
                 mutation.addedNodes.forEach(node => {
                    if (node.nodeType === 8 && node.data.includes('Removed ad')) {
                         try { node.parentNode.removeChild(node); } catch(e){}
                    }
                });
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === 1) {
                         if (Whitelist.isWhitelisted(node)) return;
                        if (node.matches('[data-ad-permanent],[data-ad-cleaned],[data-ad-nuclear-removed]')) {
                            AdUtils.safeRemove(node, 'mutationClean', {
                                type: 'DOM清理',
                                detail: '检测到被移除元素的重新插入'
                            });
                            return;
                        }
                         const walker = document.createTreeWalker(
                            node,
                            NodeFilter.SHOW_ELEMENT,
                            {
                                acceptNode: (n) => {
                                     if (!(n instanceof Element) || Whitelist.isWhitelisted(n)) return NodeFilter.FILTER_SKIP;
                                    return n.hasAttribute('data-ad-permanent') ||
                                           n.hasAttribute('data-ad-cleaned') ||
                                           n.hasAttribute('data-ad-nuclear-removed') ?
                                        NodeFilter.FILTER_ACCEPT :
                                        NodeFilter.FILTER_SKIP;
                                }
                            }
                        );
                        let currentNode;
                        while ((currentNode = walker.nextNode())) {
                             AdUtils.safeRemove(currentNode, 'mutationClean', {
                                 type: 'DOM深度清理',
                                 detail: '检测到被移除元素的子元素'
                             });
                        }
                         if (!node.dataset.adCleaned && !node.dataset.adNuclearRemoved && !Whitelist.isWhitelisted(node)) {
                             perf.adElements.add(node);
                         }
                    }
                });
            } else if (mutation.type === 'attributes') {
                 if (Whitelist.isWhitelisted(mutation.target)) return;
                if (mutation.target.hasAttribute('data-ad-permanent') &&
                    mutation.attributeName !== 'data-ad-permanent') {
                    try { mutation.target.removeAttribute(mutation.attributeName); } catch(e){}
                }
                 if (!(mutation.target instanceof Element) || (!mutation.target.dataset.adCleaned && !mutation.target.dataset.adNuclearRemoved)) {
                    perf.adElements.add(mutation.target);
                 }
            }
        });
        if (perf.adElements.size > 0 && !perf.isProcessing) {
            debouncedScheduleProcess();
        }
    });
    const debouncedScheduleProcess = Utils.debounce(Processor.scheduleProcess, CONFIG.performance.debounceMutationDelay);
    const UIController = {
        init() {
            this.loadConfig();
            Whitelist.init();
            this.registerMainSwitch();
            this.registerModuleSwitches();
            this.registerLogCommands();
            this.registerCSPCommands();
            this.registerResetCommand();
            StyleManager.toggle(CONFIG.modules.main);
        },
        registerMainSwitch() {
            GM_registerMenuCommand(
                `🔘 主开关 [${CONFIG.modules.main ? '✅' : '❌'}]`,
                () => this.toggleMain()
            );
        },
        registerModuleSwitches() {
            Object.entries(CONFIG.moduleNames).forEach(([key, name]) => {
                 if (key === 'coreProcessor') return;
                GM_registerMenuCommand(
                    `${name} [${CONFIG.modules[key] ? '✅' : '❌'}]`,
                    () => this.toggleModule(key, name)
                );
            });
        },
        registerLogCommands() {
            GM_registerMenuCommand('📜 查看拦截日志', () => Logs.showInAlert());
            GM_registerMenuCommand('🧹 清空日志', () => Logs.clear());
            GM_registerMenuCommand('🚫 清空当前域名白名单', () => {
                if (confirm(`确定清空当前域名 (${location.hostname}) 的白名单吗?`)) {
                    Whitelist.clearCurrentDomain();
                     GM_notification('白名单已清空', `当前域名 (${location.hostname}) 的白名单已清空`);
                    location.reload();
                }
            });
        },
        registerCSPCommands() {
            GM_registerMenuCommand('🛡️ CSP策略管理', () => this.manageCSP());
        },
        registerResetCommand() {
            GM_registerMenuCommand('🔄 重置设置', () => this.resetSettings());
        },
        toggleMain() {
            const newState = !CONFIG.modules.main;
            Object.keys(CONFIG.modules).forEach(key => {
                 if (key !== 'main') {
                     CONFIG.modules[key] = newState;
                 }
            });
            CONFIG.modules.main = newState;
             CONFIG.modules.coreProcessor = newState;
            this.saveConfig();
            StyleManager.toggle(newState);
            GM_notification(`主开关已${newState ? '启用' : '禁用'}`, `所有模块${newState ? '✅ 已激活' : '❌ 已停用'}`);
            location.reload();
        },
        toggleModule(key, name) {
             if (key === 'main' || key === 'coreProcessor') return;
            CONFIG.modules[key] = !CONFIG.modules[key];
            const anyModuleOn = Object.entries(CONFIG.modules).some(([modKey, modValue]) => modKey !== 'main' && modValue);
            CONFIG.modules.main = anyModuleOn;
             CONFIG.modules.coreProcessor = CONFIG.modules.main;
            this.saveConfig();
            StyleManager.toggle(CONFIG.modules.main);
            GM_notification(`${name} ${CONFIG.modules[key] ? '✅ 已启用' : '❌ 已禁用'}`);
            location.reload();
        },
        manageCSP() {
            const rulesDisplay = CONFIG.csp.rules
                .map(r => `${r.id}. ${r.name} (${r.enabled ? '✅' : '❌'})`)
                .join('\n');
            let input = prompt(
                `当前CSP策略状态: ${CONFIG.csp.enabled ? '✅ 已启用' : '❌ 已禁用'}\n\n` +
                "当前策略规则:\n" + rulesDisplay + "\n\n" +
                "输入选项:\nenable: 开启策略\ndisable: 关闭策略\n" +
                "修改规则示例:1on 启用规则1\n23off 禁用规则2和规则3\n\n"+
                "请输入你的选择:",
                ""
            );
            if (input === null) return;
            input = input.trim().toLowerCase();
            if (input === 'enable') {
                CONFIG.csp.enabled = true;
                this.saveConfig();
                CSPManager.inject();
                alert("CSP策略已启用");
                location.reload();
            } else if (input === 'disable') {
                CONFIG.csp.enabled = false;
                this.saveConfig();
                CSPManager.remove();
                alert("CSP策略已禁用");
                location.reload();
            } else if (/^\d+(?:on|off)$/i.test(input)) {
                 this.modifyCSPRules(input);
            } else {
                alert("无效输入");
            }
        },
        modifyCSPRules(actionInput) {
            const matches = actionInput.match(/^(\d+)(on|off)$/i);
            if (matches) {
                const ruleIds = matches[1].split('').map(Number);
                const enable = matches[2].toLowerCase() === 'on';
                let updatedRules = [];
                ruleIds.forEach(id => {
                    const rule = CONFIG.csp.rules.find(r => r.id === id);
                    if (rule) {
                        rule.enabled = enable;
                        updatedRules.push(id);
                    }
                });
                if (updatedRules.length > 0) {
                     this.saveConfig();
                     CSPManager.inject();
                     alert(`规则 ${updatedRules.join(',')} 已更新为 ${enable ? '启用' : '禁用'}`);
                     location.reload();
                } else {
                     alert("未找到匹配的规则ID");
                }
            } else {
                alert("输入格式错误,请按示例输入如'1on'或'123off'");
            }
        },
        resetSettings() {
            if (confirm("确定要重置所有设置吗?这将清空设置和白名单")) {
                Object.assign(CONFIG.modules, DEFAULT_MODULES);
                 CONFIG.modules.coreProcessor = CONFIG.modules.main;
                CONFIG.csp.enabled = false;
                CONFIG.csp.rules = DEFAULT_CSP_RULES.map(rule => ({ ...rule }));
                Whitelist.clearAll();
                this.saveConfig();
                CSPManager.remove();
                Logs.clear();
                alert("所有设置已重置为默认值");
                location.reload();
            }
        },
        saveConfig() {
            try {
                GM_setValue(`adblockConfig_${location.hostname}`, JSON.stringify({
                    modules: CONFIG.modules,
                    csp: CONFIG.csp,
                     whitelist: CONFIG.whitelist
                }));
            } catch (e) {
            }
        },
        loadConfig() {
            try {
                const savedConfig = JSON.parse(GM_getValue(`adblockConfig_${location.hostname}`, '{}'));
                if (savedConfig.modules) {
                    Object.keys(CONFIG.modules).forEach(key => {
                        if (savedConfig.modules.hasOwnProperty(key)) {
                            CONFIG.modules[key] = savedConfig.modules[key];
                        }
                    });
                }
                if (savedConfig.csp) {
                    Object.assign(CONFIG.csp, savedConfig.csp);
                }
                 if (savedConfig.whitelist) {
                     CONFIG.whitelist = savedConfig.whitelist;
                 }
                 CONFIG.modules.coreProcessor = CONFIG.modules.main;
            } catch (e) {
            }
        }
    };
    const ASTAnalyzer = (function() {
        return {
            getObfuscationRules: function(minLevel) {
                return REGEX.obfuscationPatterns.filter(r => r.riskLevel >= minLevel);
            },
            checkObfuscation: function(code, minLevel) {
                const rules = this.getObfuscationRules(minLevel);
                for (let i = 0; i < rules.length; i++) {
                    try {
                        if (rules[i].pattern.test(code)) {
                            return true;
                        }
                    } catch (e) {
                    }
                }
                return false;
            },
            checkHighRiskPatterns: function(code) {
                 return REGEX.obfuscationPatterns.some(p =>
                     p.riskLevel >= 3 && p.pattern.test(code)
                 );
             },
             calculateDangerLevel(content) {
                 let dangerLevel = 0;
                 if (this.checkObfuscation(content, 2)) dangerLevel += 2;
                 if (this.checkObfuscation(content, 3)) dangerLevel += 3;
                 if (REGEX.jsAdPattern.test(content)) dangerLevel += 2;
                 const highRiskPatterns = this.getObfuscationRules(3);
                 if (highRiskPatterns.some(p => p.pattern.test(content.slice(0, 500)))) {
                     dangerLevel += 3;
                 }
                 return dangerLevel;
             }
        };
    })();
    ModuleManager.register('thirdPartyBlock', {
        originals: {
            createElement: document.createElement,
            setAttribute: Element.prototype.setAttribute,
            appendChild: Node.prototype.appendChild,
            insertBefore: Node.prototype.insertBefore,
            replaceChild: Node.prototype.replaceChild,
            fetch: window.fetch,
            xhrOpen: XMLHttpRequest.prototype.open,
            xhrSend: XMLHttpRequest.prototype.send,
            documentWrite: document.write,
            documentWriteln: document.writeln,
            insertAdjacentHTML: Element.prototype.insertAdjacentHTML
        },
        blockedUrls: new Set(),
        hostCache: new Map(),
        requestInterceptors: new WeakMap(),
        interceptedRequests: new WeakSet(),
        enabled: false,
        init() {
            if (CONFIG.modules.thirdPartyBlock) {
                 this.enable();
             } else {
                 this.disable();
             }
        },
        enable() {
            if (this.enabled) return;
            this.enabled = true;
            this.setupInterceptionHooks();
            document.addEventListener('beforescriptexecute', this.handleBeforeScriptExecute.bind(this), true);
        },
        disable() {
            if (!this.enabled) return;
            this.enabled = false;
            this.cleanupInterceptionHooks();
            document.removeEventListener('beforescriptexecute', this.handleBeforeScriptExecute, true);
        },
        setupInterceptionHooks() {
            const self = this;
             if (!Node.prototype._originalAppendChildT) Node.prototype._originalAppendChildT = Node.prototype.appendChild;
             if (!document._originalCreateElementT) document._originalCreateElementT = document.createElement;
             if (!Element.prototype._originalSetAttributeT) Element.prototype._originalSetAttributeT = Element.prototype.setAttribute;
             if (!Node.prototype._originalInsertBeforeT) Node.prototype._originalInsertBeforeT = Node.prototype.insertBefore;
             if (!Node.prototype._originalReplaceChildT) Node.prototype._originalReplaceChildT = Node.prototype.replaceChild;

            const interceptRequest = (target, url, requestType) => {
                 if (target instanceof Element && Whitelist.isWhitelisted(target)) return false;
                if (!url || this.blockedUrls.has(url)) return true;
                if (!this.isThirdParty(url)) return false;
                this.blockedUrls.add(url);
                if (target instanceof Element) this.interceptedRequests.add(target);
                if (target?.tagName === 'SCRIPT') {
                    const abort = new AbortController();
                    AdUtils.abortControllers.set(target, abort);
                    if (target.src) {
                        fetch(target.src, { signal: abort.signal }).catch(() => {});
                        abort.abort();
                    }
                    this.logRemoval(target, 'thirdPartyBlock', {
                        type: '第三方脚本拦截',
                        detail: `原子级移除脚本: ${url.slice(0, 80)}`,
                        context: { requestType }
                    });
                    return AdUtils.nuclearRemove(target, 'thirdPartyBlock', {type: '第三方脚本拦截'});
                } else if (target instanceof Element && ['IFRAME', 'IMG'].includes(target.tagName)) {
                    this.logRemoval(target, 'thirdPartyBlock', {
                         type: '第三方元素拦截',
                         detail: `拦截元素源: ${url.slice(0, 80)}`,
                         context: { requestType, tag: target.tagName }
                    });
                    return AdUtils.nuclearRemove(target, 'thirdPartyBlock', {type: '第三方元素拦截'});
                } else {
                    this.logRemoval(null, 'thirdPartyBlock', {
                         type: '第三方请求拦截',
                         detail: `拦截请求URL: ${url?.slice(0, 150) || 'unknown'}`,
                         context: { requestType }
                    });
                    return true;
                }
            };
            const unifiedRequestHandler = (target, url, requestType) => {
                if (target instanceof Element && Whitelist.isWhitelisted(target)) return false;
                if (target instanceof Element && (target.dataset.adCleaned || target.dataset.adNuclearRemoved)) return true;
                return interceptRequest(target, url, requestType);
            };
            window.fetch = function(input, init) {
                const url = typeof input === 'string' ? input : input?.url;
                if (CONFIG.modules.main && CONFIG.modules.thirdPartyBlock && !perf.degraded && unifiedRequestHandler(null, url, 'Fetch')) {
                    return Promise.reject(new Error('TPI: 第三方请求被拦截'));
                }
                return self.originals.fetch(input, init);
            };
            XMLHttpRequest.prototype.open = function(method, url) {
                if (CONFIG.modules.main && CONFIG.modules.thirdPartyBlock && !perf.degraded && unifiedRequestHandler(this, url, 'XHR')) {
                    self.requestInterceptors.set(this, true);
                    return;
                }
                return self.originals.xhrOpen.call(this, method, url);
            };
            XMLHttpRequest.prototype.send = function(body) {
                if (self.requestInterceptors.has(this)) {
                    self.requestInterceptors.delete(this);
                    return;
                }
                return self.originals.xhrSend.call(this, body);
            };
            document.createElement = function(tagName) {
                const element = document._originalCreateElementT.call(document, tagName);
                if (['script', 'iframe', 'img'].includes(tagName.toLowerCase())) {
                    return new Proxy(element, {
                        set: (target, prop, value) => {
                             if (Whitelist.isWhitelisted(target)) {
                                target[prop] = value;
                                return true;
                             }
                             if (target.dataset.adCleaned || target.dataset.adNuclearRemoved) {
                                 target[prop] = value;
                                 return true;
                             }
                            if (prop === 'src' && typeof value === 'string' && unifiedRequestHandler(target, value, '元素创建')) {
                                return true;
                            }
                            target[prop] = value;
                            return true;
                        }
                    });
                }
                return element;
            };
            Element.prototype.setAttribute = function(name, value) {
                 if (Whitelist.isWhitelisted(this)) {
                     return Element.prototype._originalSetAttributeT.call(this, name, value);
                 }
                 if (this.dataset.adCleaned || this.dataset.adNuclearRemoved) {
                      return Element.prototype._originalSetAttributeT.call(this, name, value);
                 }
                if (name === 'src' && typeof value === 'string' && unifiedRequestHandler(this, value, '属性设置')) {
                    return;
                }
                return Element.prototype._originalSetAttributeT.call(this, name, value);
            };
            const interceptDOMOperation = (originalMethod, operationType) => {
                return function(node, ...args) {
                    if (node.nodeType === 1) {
                         if (Whitelist.isWhitelisted(node)) {
                            return originalMethod.call(this, node, ...args);
                         }
                         if (node.dataset.adCleaned || node.dataset.adNuclearRemoved) {
                            return originalMethod.call(this, node, ...args);
                         }
                        const src = node.src || node.getAttribute('data-src') || node.getAttribute('href') || '';
                        if (src && unifiedRequestHandler(node, src, operationType)) {
                            return node;
                        }
                    }
                    return originalMethod.call(this, node, ...args);
                };
            };
            Node.prototype.appendChild = interceptDOMOperation(Node.prototype._originalAppendChildT, 'appendChild');
            Node.prototype.insertBefore = interceptDOMOperation(Node.prototype._originalInsertBeforeT, 'insertBefore');
            Node.prototype.replaceChild = interceptDOMOperation(Node.prototype._originalReplaceChildT, 'replaceChild');
            const handleHTMLInsertion = (html, insertionType, originalFunction) => {
                 if (!this.enabled) return originalFunction(html);
                try {
                    const tempDiv = document.createElement('div');
                    tempDiv.innerHTML = html;
                    Array.from(tempDiv.querySelectorAll('script, iframe, img')).forEach(el => {
                        const src = el.src || el.getAttribute('src') || '';
                         if (Whitelist.isWhitelisted(el)) return;
                        if (this.isThirdParty(src)) {
                            this.logRemoval(el, 'thirdPartyBlock', {
                                type: '动态插入拦截',
                                detail: `插入方式: ${insertionType}, 源: ${src.slice(0, 80)}`,
                                context: { originalHTML: html.slice(0, 200) }
                            });
                            if (el.parentNode) {
                                el.parentNode.removeChild(el);
                            }
                        }
                    });
                     return originalFunction.call(this, tempDiv.innerHTML);
                } catch(e) {
                     return originalFunction.call(this, html);
                }
            };
            document.write = function(content) {
                handleHTMLInsertion.call(self, content, 'document.write', self.originals.documentWrite);
            };
            document.writeln = function(content) {
                handleHTMLInsertion.call(self, content, 'document.writeln', self.originals.documentWriteln);
            };
            Element.prototype.insertAdjacentHTML = function(position, html) {
                handleHTMLInsertion.call(self, html, 'insertAdjacentHTML',
                    (html) => self.originals.insertAdjacentHTML.call(this, position, html));
            };
        },
        cleanupInterceptionHooks() {
            AdUtils.abortControllers.forEach(controller => controller.abort());
            AdUtils.abortControllers.clear();
            window.fetch = this.originals.fetch;
            XMLHttpRequest.prototype.open = this.originals.xhrOpen;
            XMLHttpRequest.prototype.send = this.originals.xhrSend;
            document.write = this.originals.documentWrite;
            document.writeln = this.originals.documentWriteln;
             if (Node.prototype._originalAppendChildT) Node.prototype.appendChild = Node.prototype._originalAppendChildT;
             if (Node.prototype._originalInsertBeforeT) Node.prototype.insertBefore = Node.prototype._originalInsertBeforeT;
             if (Node.prototype._originalReplaceChildT) Node.prototype.replaceChild = Node.prototype._originalReplaceChildT;
             if (Element.prototype._originalSetAttributeT) Element.prototype.setAttribute = Element.prototype._originalSetAttributeT;
             if (document._originalCreateElementT) document.createElement = document._originalCreateElementT;
            Element.prototype.insertAdjacentHTML = this.originals.insertAdjacentHTML;
        },
        handleBeforeScriptExecute(e) {
            const script = e.target;
            if (Whitelist.isWhitelisted(script) || script.dataset.adCleaned || script.dataset.adNuclearRemoved) return;
            if (!CONFIG.modules.main || !CONFIG.modules.thirdPartyBlock || perf.degraded) return;
            const src = script.src || script.getAttribute('data-src') || '';
            if (this.isThirdParty(src)) {
                 Logs.add('thirdPartyBlock', script, {
                     type: '预执行脚本拦截',
                     detail: `类型: ${script.type || '常规'}, SRC: ${src.slice(0, 100)}`
                 });
                 e.preventDefault();
                 e.stopImmediatePropagation();
                 AdUtils.nuclearRemove(script, 'thirdPartyBlock', {
                     type: '预执行脚本拦截',
                     detail: `拦截执行并原子级移除`
                 });
            }
        },
        isThirdParty(url) {
            try {
                if (!url || url.startsWith('data:') || url.startsWith('blob:') || url.startsWith('#')) return false;
                const cacheKey = url.split('?')[0].split('#')[0];
                if (this.hostCache.has(cacheKey)) return this.hostCache.get(cacheKey);
                const currentUrl = new URL(location.href);
                const resourceUrl = new URL(url, currentUrl.href);
                if (!['http:', 'https:'].includes(resourceUrl.protocol)) return false;
                const currentHost = currentUrl.hostname;
                const resourceHost = resourceUrl.hostname;
                const isThirdParty = !(
                    resourceHost === currentHost ||
                    resourceHost.endsWith(`.${currentHost}`) ||
                    REGEX.thirdParty.test(resourceHost) ||
                    currentHost.endsWith(resourceHost)
                );
                this.hostCache.set(cacheKey, isThirdParty);
                return isThirdParty;
            } catch(e) {
                return true;
            }
        },
        logRemoval(element, moduleKey, reason) {
             if (element instanceof Element && Whitelist.isWhitelisted(element)) return;
            try {
                 Logs.add(moduleKey, element, reason);
            } catch(e) {
            }
        },
        check(el) {
             return false;
        }
    });
    ModuleManager.register('dynamicSystem', {
        checkedScripts: new WeakSet(),
        scriptAnalysisQueue: [],
        isProcessingAnalysisQueue: false,
        enabled: false,
        init() {
            if (CONFIG.modules.dynamicSystem) {
                this.enable();
            } else {
                this.disable();
            }
            this.startScriptRecoveryCheck();
            this.startAnalysisQueueProcessor();
        },
        enable() {
            if (this.enabled) return;
            this.enabled = true;
            AdUtils.initScriptHooks();
        },
        disable() {
            if (!this.enabled) return;
            this.enabled = false;
            this.scriptAnalysisQueue = [];
            AdUtils.cleanupScriptHooks();
        },
        handleScriptElement(scriptEl) {
             if (Whitelist.isWhitelisted(scriptEl) || scriptEl.dataset.adCleaned || scriptEl.dataset.adNuclearRemoved) return;
             if (!this.enabled || !CONFIG.modules.main || perf.degraded) return;
             this.checkedScripts.add(scriptEl);
            if (this.quickDetect(scriptEl)) {
                AdUtils.nuclearRemove(scriptEl, 'dynamicSystem', {
                    type: '动态脚本快速检测',
                    detail: '命中快速检测规则'
                });
                return;
            }
             this.scriptAnalysisQueue.push(scriptEl);
             if (!this.isProcessingAnalysisQueue) {
                 this.processAnalysisQueue();
             }
        },
        handleScriptSource(scriptEl, sourceValue) {
             if (Whitelist.isWhitelisted(scriptEl) || scriptEl.dataset.adCleaned || scriptEl.dataset.adNuclearRemoved) return;
             if (!this.enabled || !CONFIG.modules.main || perf.degraded) return;
             this.checkedScripts.add(scriptEl);
             if (ASTAnalyzer.checkHighRiskPatterns(sourceValue.slice(0, 500))) {
                  AdUtils.nuclearRemove(scriptEl, 'dynamicSystem', {
                      type: '动态脚本源快速检测',
                      detail: '源内容命中高危模式'
                  });
                  return;
             }
             this.scriptAnalysisQueue.push(scriptEl);
             if (!this.isProcessingAnalysisQueue) {
                  this.processAnalysisQueue();
              }
        },
        async processAnalysisQueue() {
            if (this.isProcessingAnalysisQueue || this.scriptAnalysisQueue.length === 0) return;
            this.isProcessingAnalysisQueue = true;
            while (this.scriptAnalysisQueue.length > 0) {
                const script = this.scriptAnalysisQueue.shift();
                if (!document.contains(script) || Whitelist.isWhitelisted(script) || script.dataset.adCleaned || script.dataset.adNuclearRemoved) {
                    continue;
                }
                await this.analyzeScript(script);
                 if (this.scriptAnalysisQueue.length > 5 || performance.now() % 100 < 10) {
                     await new Promise(resolve => requestIdleCallback(resolve, { timeout: 50 }));
                 }
            }
            this.isProcessingAnalysisQueue = false;
        },
        async analyzeScript(scriptEl) {
             if (Whitelist.isWhitelisted(scriptEl) || scriptEl.dataset.adCleaned || scriptEl.dataset.adNuclearRemoved) return;
             if (!this.enabled || !CONFIG.modules.main || perf.degraded) return;
            try {
                if (scriptEl.src) {
                    await this.analyzeRemoteScript(scriptEl);
                } else {
                    await this.analyzeInlineScript(scriptEl);
                }
            } catch (e) {
            }
        },
        async analyzeRemoteScript(scriptEl) {
             if (Whitelist.isWhitelisted(scriptEl) || scriptEl.dataset.adCleaned || scriptEl.dataset.adNuclearRemoved) return;
             if (!this.enabled || !CONFIG.modules.main || perf.degraded) return;
            const controller = new AbortController();
            AdUtils.abortControllers.set(scriptEl, controller);
            try {
                const response = await fetch(scriptEl.src, {
                    signal: controller.signal,
                    priority: 'low'
                });
                const reader = response.body.getReader();
                const { value, done } = await reader.read();
                if (done) return;
                const chunk = new TextDecoder().decode(value);
                reader.cancel();
                if (ASTAnalyzer.checkHighRiskPatterns(chunk)) {
                    AdUtils.nuclearRemove(scriptEl, 'dynamicSystem', {
                        type: '流式特征阻断',
                        detail: `检测到脚本前${chunk.length}字节风险内容`
                    });
                }
            } catch(e) {
                 if (e.name === 'AbortError') {
                 } else {
                 }
            } finally {
                AdUtils.abortControllers.delete(scriptEl);
            }
        },
        async analyzeInlineScript(scriptEl) {
             if (Whitelist.isWhitelisted(scriptEl) || scriptEl.dataset.adCleaned || scriptEl.dataset.adNuclearRemoved) return;
             if (!this.enabled || !CONFIG.modules.main || perf.degraded) return;
            const content = scriptEl.textContent;
            const dangerLevel = ASTAnalyzer.calculateDangerLevel(content);
            if (dangerLevel >= 3) {
                AdUtils.nuclearRemove(scriptEl, 'dynamicSystem', {
                    type: '内联脚本风险',
                    detail: `危险级别: ${dangerLevel}`
                });
            }
        },
        quickDetect(scriptEl) {
             if (Whitelist.isWhitelisted(scriptEl) || scriptEl.dataset.adCleaned || scriptEl.dataset.adNuclearRemoved) return false;
             if (!this.enabled || !CONFIG.modules.main || perf.degraded) return false;
            const attrs = Array.from(scriptEl.attributes);
            const shortVarPattern = /^[a-z\d]{1,3}$/i;
            const signaturePattern = /^(?:[\da-f]{32}|[\w+/=]{40,})$/i;
            const alphanumAlternate = /(?:[a-z]\d|\d[a-z]){5,}/gi;
            const hasObfuscatedAttribute = attrs.some(attr => {
                const isShortVar = shortVarPattern.test(attr.name);
                 const isObfuscatedValue = signaturePattern.test(attr.value) ||
                     (typeof attr.value === 'string' && attr.value.match(alphanumAlternate)?.length > 3);
                const isDynamicName = /\[(?:0x[a-f\d]+|\d+)\]/.test(attr.name);
                return (isShortVar && isObfuscatedValue) ||
                    attr.name.length > 20 ||
                    isDynamicName ||
                    (typeof attr.value === 'string' && attr.value.includes(';') && attr.value.length > 60);
            });
            if (hasObfuscatedAttribute) {
                 Logs.add('dynamicSystem', scriptEl, {
                     type: '混淆属性快速检测',
                     detail: `属性列表: ${attrs.map(a => a.name).join(', ')}`
                 });
                 return true;
            }
            if (!scriptEl.src && scriptEl.textContent) {
                const content = scriptEl.textContent.slice(0, 200);
                return ASTAnalyzer.checkHighRiskPatterns(content);
            }
            return false;
        },
        startScriptRecoveryCheck() {
            setInterval(() => {
                if (!this.enabled || !CONFIG.modules.main || perf.degraded) return;
                document.querySelectorAll('script:not([data-ad-cleaned]):not([data-ad-nuclear-removed]):not([data-ad-processed-hook]):not([data-ad-processed-source])').forEach(script => {
                     if (Whitelist.isWhitelisted(script)) return;
                    this.handleScriptElement(script);
                });
            }, 1000);
        },
         startAnalysisQueueProcessor() {
             setInterval(() => {
                 if (!this.enabled || !CONFIG.modules.main || perf.degraded) return;
                 if (!this.isProcessingAnalysisQueue && this.scriptAnalysisQueue.length > 0) {
                     this.processAnalysisQueue();
                 }
             }, 500);
         },
        check(el) {
             return false;
        }
    });
    ModuleManager.register('layoutSystem', {
        enabled: false,
        init() {
             if (CONFIG.modules.layoutSystem) this.enable();
             else this.disable();
        },
        enable() { this.enabled = true; },
        disable() { this.enabled = false; },
        check(el) {
             if (Whitelist.isWhitelisted(el) || !(el instanceof Element)) return false;
            if (!this.enabled || !CONFIG.modules.main || perf.degraded) return false;
            const style = Detector.getCachedStyle(el);
             if (!style) return false;
            const zIndex = parseInt(style.zIndex, 10);
            if (!isNaN(zIndex) && zIndex > CONFIG.protectionRules.zIndexThreshold) {
                const parent = el.parentElement;
                const isLegitPopup = (
                    parent &&
                    (parent.classList.contains('modal') ||
                     parent.getAttribute('role') === 'dialog' ||
                     el.classList.contains('modal') ||
                     el.getAttribute('role') === 'dialog')
                );
                if (!isLegitPopup) {
                    return AdUtils.safeRemove(el, 'layoutSystem', {
                        type: '高Z轴元素',
                        detail: `z-index: ${zIndex}`
                    });
                }
            }
            if (style.overflow === 'hidden' && el.scrollHeight > el.clientHeight && el.clientHeight > 0) {
                return AdUtils.safeRemove(el, 'layoutSystem', {
                    type: '隐藏溢出内容的容器'
                });
            }
            const rect = el.getBoundingClientRect();
             if (rect.width >= 0 && rect.height >= 0 && el.children.length === 0 &&
                el.textContent.trim() === '' &&
                rect.width < 5 &&
                rect.height < 5 &&
                (style.width === '0px' || style.height === '0px' || style.display === 'none' || style.visibility === 'hidden' || parseFloat(style.opacity) < 0.01)) {
                return AdUtils.safeRemove(el, 'layoutSystem', {
                    type: '空的不可见容器'
                });
            }
            if (el.children.length > 0 &&
                Array.from(el.children).every(child => {
                     if (!(child instanceof Element)) return true;
                    const childStyle = Detector.getCachedStyle(child);
                    return childStyle && (childStyle.display === 'none' || childStyle.visibility === 'hidden' || parseFloat(childStyle.opacity) < 0.01);
                })) {
                return AdUtils.safeRemove(el, 'layoutSystem', {
                    type: '子元素的不可见容器'
                });
            }
            if (['fixed', 'sticky'].includes(style.position)) {
                 const isLargeFixed = (
                     rect.width >= window.innerWidth * 0.8 &&
                     rect.height >= window.innerHeight * 0.8
                 );
                 const isBottomFloat = (
                    rect.bottom > window.innerHeight - 50 &&
                    rect.height < 100 &&
                    rect.width > window.innerWidth * 0.5
                 );
                 const isTopFloat = (
                     rect.top < 50 &&
                     rect.height < 100 &&
                     rect.width > window.innerWidth * 0.5
                 );
                 if (isLargeFixed) {
                      return AdUtils.safeRemove(el, 'layoutSystem', {
                         type: '大型固定定位元素',
                         detail: `位置: ${style.position}, 尺寸: ${rect.width}x${rect.height}px`
                     });
                 }
                 if (isBottomFloat) {
                      return AdUtils.safeRemove(el, 'layoutSystem', {
                         type: '底部悬浮元素',
                         detail: `位置: bottom=${rect.bottom}px`
                     });
                 }
                 if (isTopFloat) {
                     return AdUtils.safeRemove(el, 'layoutSystem', {
                         type: '顶部悬浮元素',
                         detail: `位置: top=${rect.top}px`
                     });
                 }
            }
            return false;
        }
    });
    ModuleManager.register('mergedMediaSystem', {
        imageExtensions: /\.(gif|webp|png|jpe?g|svg)(\?.*)?$/i,
        jsExtensionPattern: /\.js(\?|$)/i,
        visibilityThreshold: 0.01,
        minValidSize: 16,
        mobileAdRatios: [
            { w: 320, h: 50 }, { w: 300, h: 250 }, { w: 336, h: 280 },
             { w: 728, h: 90 }, { w: 468, h: 60 }, { w: 120, h: 600 },
             { w: 160, h: 600 }, { w: 300, h: 600 }, { w: 1, h: 1 }
        ],
        mobileRatioThreshold: { min: 0.05, max: 20 },
        enabled: false,
        init() {
             if (CONFIG.modules.mergedMediaSystem) this.enable();
             else this.disable();
        },
        enable() { this.enabled = true; },
        disable() { this.enabled = false; },
        check(el) {
             if (Whitelist.isWhitelisted(el) || !(el instanceof Element)) return false;
            if (!this.enabled || !CONFIG.modules.main || perf.degraded) return false;
            if (el.tagName === 'IMG' || el.tagName === 'IMAGE') {
                return this.checkImageElement(el);
            }
            if (el.tagName === 'DIV' && this.hasImageBackground(el)) {
                 return this.checkImageBackgroundElement(el);
            }
             if (this.isSuspiciousContainer(el)) {
                return this.handleSuspiciousContainer(el);
            }
            return false;
        },
        checkImageElement(el) {
             if (Whitelist.isWhitelisted(el) || !(el instanceof Element)) return false;
            const src = el.src || el.getAttribute('data-src') || '';
            if (this.jsExtensionPattern.test(src)) {
                return this.handleAd(el, 'JS扩展名图片',
                    `异常图片源: ${src.slice(0, 80)}`, 3);
            }
            if (!this.isVisible(el)) return false;
            const rect = el.getBoundingClientRect();
             if (rect.width > 0 && rect.height > 0 && (rect.width < this.minValidSize || rect.height < this.minValidSize)) {
                 if (el.tagName === 'IMG' && rect.width === 1 && rect.height === 1) {
                     return this.handleAd(el, '追踪像素',
                          `尺寸: ${rect.width}x${rect.height}px`, 2);
                 }
                 return false;
             }
            const naturalWidth = el.naturalWidth || rect.width;
            const naturalHeight = el.naturalHeight || rect.height;
            if (naturalWidth > 0 && naturalHeight > 0) {
                 if (this.isCommonAdSize(naturalWidth, naturalHeight)) {
                     return this.handleAd(el, '标准广告尺寸图片',
                         `${naturalWidth}x${naturalHeight}`, 2);
                 }
                 const aspectRatio = naturalWidth / naturalHeight;
                 if (aspectRatio < this.mobileRatioThreshold.min ||
                     aspectRatio > this.mobileRatioThreshold.max) {
                     return this.handleAd(el, '异常宽高比图片',
                         `比例:${aspectRatio.toFixed(1)} (${naturalWidth}x${naturalHeight})`, 2);
                 }
            }
            return false;
        },
         checkImageBackgroundElement(el) {
             if (Whitelist.isWhitelisted(el) || !(el instanceof Element)) return false;
             if (!this.isVisible(el)) return false;
             const style = Detector.getCachedStyle(el);
             if (style.backgroundImage === 'none' || !this.imageExtensions.test(style.backgroundImage)) return false;
              const rect = el.getBoundingClientRect();
             if (rect.width > 0 && rect.height > 0 && (rect.width < this.minValidSize * 2 || rect.height < this.minValidSize * 2)) {
                 return this.handleAd(el, '微型背景图片',
                     `尺寸: ${rect.width}x${rect.height}px`, 2);
             }
             if (typeof style.backgroundImage === 'string' && REGEX.adAttributes.test(style.backgroundImage)) {
                 return this.handleAd(el, '广告背景图片源', `背景源: ${style.backgroundImage.slice(0, 80)}...`, 3);
             }
             return false;
         },
        isVisible(el) {
             if (Whitelist.isWhitelisted(el) || !(el instanceof Element)) return true;
            const style = Detector.getCachedStyle(el);
            const rect = el.getBoundingClientRect();
            return (
                parseFloat(style.opacity) > this.visibilityThreshold &&
                style.visibility !== 'hidden' &&
                style.display !== 'none' &&
                rect.width > 0 &&
                rect.height > 0
            );
        },
        isCommonAdSize(width, height) {
            return this.mobileAdRatios.some(ratio => {
                if (ratio.w === 1 && ratio.h === 1) return width === 1 && height === 1;
                const targetRatio = ratio.w / ratio.h;
                const currentRatio = width / height;
                return Math.abs(currentRatio - targetRatio) < 0.2 &&
                       Math.abs(width - ratio.w) < 20 &&
                       Math.abs(height - ratio.h) < 20;
            });
        },
        hasImageBackground(el) {
             if (!(el instanceof Element)) return false;
            const style = Detector.getCachedStyle(el);
            return (
                style &&
                style.backgroundImage &&
                style.backgroundImage !== 'none' &&
                style.backgroundImage.includes('url(')
            );
        },
        isSuspiciousContainer(el) {
             if (Whitelist.isWhitelisted(el) || !(el instanceof Element)) return false;
            const style = Detector.getCachedStyle(el);
             if (!style) return false;
             const zIndex = parseInt(style.zIndex, 10);
            return (
                zIndex > CONFIG.protectionRules.zIndexThreshold ||
                 this.containsTrackingPixel(el)
            );
        },
        containsTrackingPixel(el) {
             if (!(el instanceof Element)) return false;
            return el.querySelector('img[width="1" i][height="1" i]') !== null;
        },
        handleSuspiciousContainer(el) {
             if (Whitelist.isWhitelisted(el) || !(el instanceof Element)) return false;
            const style = Detector.getCachedStyle(el);
             const zIndex = parseInt(style.zIndex, 10);
            if (zIndex > CONFIG.protectionRules.zIndexThreshold) {
                 return this.handleAd(el, '极高Z轴容器', `z-index: ${zIndex}`, 4);
            }
             if (this.containsTrackingPixel(el)) {
                  return this.handleAd(el, '包含跟踪像素', '找到1x1图片', 3);
             }
             return false;
        },
         handleAd(el, type, detail, priority = 1) {
             return AdUtils.safeRemove(el, 'mergedMediaSystem', {
                 type: `媒体检测[${type}]`,
                 detail: detail,
                 priority: priority
             });
         }
    });
    ModuleManager.register('specialUA', {
        navigatorProxy: null,
        originalNavigator: Object.create(navigator),
        fakeUA: 'NokiaE7-00/5.0 UCWEB/2.0 Mozilla/5.0 (Symbian/3; Series60/5.2; Windows Phone 10.0; Android 14; Microsoft; Lumia 950 XL Dual SIM; Java) Gecko/131 Firefox/131 SearchCraft/3.10.2 (Baidu; P1 13) baiduboxapp/4.3.0.10',
        fakePlatform: 'Win32',
        fakeAppVersion: 'Mozilla/5.0 (Linux; Android 12; LIO-AN00 Build/HUAWEILIO-AN00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 Mobile Safari/537.36',
        init() {
            this.createProxy();
            if (CONFIG.modules.specialUA) this.enable();
        },
        createProxy() {
            this.navigatorProxy = new Proxy(navigator, {
                get: (target, prop) => {
                    if (!CONFIG.modules.specialUA)
                        return Reflect.get(target, prop);
                    switch(prop) {
                        case 'userAgent':
                            return this.fakeUA;
                        case 'platform':
                            return this.fakePlatform;
                        case 'appVersion':
                            return this.fakeAppVersion;
                        case 'vendor':
                            return 'Google Inc.';
                        default:
                            return Reflect.get(target, prop);
                    }
                }
            });
            Object.defineProperty(window, 'navigator', {
                value: this.navigatorProxy,
                configurable: true,
                writable: false
            });
        },
        enable() {
            CONFIG.modules.specialUA = true;
        },
        disable() {
            CONFIG.modules.specialUA = false;
            Object.defineProperty(window, 'navigator', {
                value: this.originalNavigator,
                configurable: true,
                writable: false
            });
        },
        check() {
            return false;
        }
    });
    ModuleManager.register('performanceOptimizer', {
        performanceEntries: [],
        degradationThreshold: CONFIG.performance.degradeThreshold,
        isMonitoring: false,
        perfObserver: null,
        init() {
            this.setupPerformanceMonitor();
            this.startPerformanceObserver();
        },
        setupPerformanceMonitor() {
            if (this.isMonitoring) return;
            this.isMonitoring = true;
            const checkPerformance = () => {
                const now = performance.now();
                this.performanceEntries = this.performanceEntries.filter(
                    entry => entry.startTime > now - 10000
                );
                const longTasks = this.performanceEntries.filter(
                    entry => entry.duration > CONFIG.performance.longTaskThreshold
                );
                if (longTasks.length >= this.degradationThreshold && !perf.degraded) {
                    this.activateDegradedMode();
                } else if (longTasks.length < this.degradationThreshold && perf.degraded) {
                    this.deactivateDegradedMode();
                }
                requestAnimationFrame(checkPerformance);
            };
            checkPerformance();
        },
        startPerformanceObserver() {
             if (typeof PerformanceObserver === 'function') {
                 this.perfObserver = new PerformanceObserver(list => {
                     this.performanceEntries.push(...list.getEntries());
                 });
                 try {
                     this.perfObserver.observe({ entryTypes: ['longtask'] });
                 } catch(e) {
                 }
             }
        },
        activateDegradedMode() {
            if (perf.degraded) return;
            perf.degraded = true;
            mainObserver.disconnect();
            AdUtils.cleanupScriptHooks();
            ModuleManager.modules.dynamicSystem?.disable();
            GM_notification('AdBlocker', '性能降级模式:动态检测已暂停');
        },
        deactivateDegradedMode() {
            if (!perf.degraded) return;
            perf.degraded = false;
            if (document.documentElement) {
                mainObserver.observe(document.documentElement, {
                    childList: true,
                    subtree: true,
                    attributes: true,
                    attributeFilter: ['id', 'class', 'data-ad', 'src', 'style']
                });
            }
            if (CONFIG.modules.dynamicSystem) {
                ModuleManager.modules.dynamicSystem?.enable();
            }
            GM_notification('AdBlocker', '性能降级模式结束:动态检测已恢复');
             Processor.collectInitialElements();
             Processor.scheduleProcess();
        },
        check() {
            return false;
        }
    });
    (function ensureDOMInjection() {
        if (document.head) return;
        const originalCreateElement = document.createElement;
        document.createElement = function(tagName) {
            const element = originalCreateElement.call(document, tagName);
            if (tagName.toLowerCase() === 'meta') {
                document.documentElement.insertBefore(element, document.documentElement.firstChild);
            }
            return element;
        };
        const originalAppendChild = Node.prototype.appendChild;
        Node.prototype.appendChild = function(node) {
            if (node.nodeType === 1 && node.tagName === 'STYLE') {
                 if (document.head) return originalAppendChild.call(this, node);
                return document.documentElement.insertBefore(node, document.documentElement.firstChild);
            }
            return originalAppendChild.call(this, node);
        };
    })();
    function init() {
        if (!window.requestIdleCallback) {
            window.requestIdleCallback = (callback, options) => {
                const options_timeout = options?.timeout || 0;
                const start = Date.now();
                return setTimeout(() => {
                    callback({
                        didTimeout: Date.now() - start > 50,
                        timeRemaining: () => Math.max(0, 50 - (Date.now() - start))
                    });
                }, options_timeout);
            };
            window.cancelIdleCallback = (id) => clearTimeout(id);
        }
        UIController.init();
        CSPManager.injectInitialCSP();
        ModuleManager.init();
        if (CONFIG.modules.main) {
             Processor.collectInitialElements();
             Processor.scheduleProcess();
             if (document.documentElement) {
                 mainObserver.observe(document.documentElement, {
                     childList: true,
                     subtree: true,
                     attributes: true,
                     attributeFilter: ['id', 'class', 'data-ad', 'src', 'style']
                 });
             } else {
                 window.addEventListener('DOMContentLoaded', () => {
                      mainObserver.observe(document.documentElement, {
                          childList: true,
                          subtree: true,
                          attributes: true,
                          attributeFilter: ['id', 'class', 'data-ad', 'src', 'style']
                      });
                 });
             }
             window.addEventListener('scroll', Utils.throttle(() => {
                 if (CONFIG.modules.main && !perf.degraded) {
                     Processor.collectInitialElements();
                     Processor.scheduleProcess();
                 }
             }, CONFIG.performance.throttleScrollDelay));
             window.addEventListener('resize', Utils.debounce(() => {
                 if (CONFIG.modules.main && !perf.degraded) {
                     perf.styleCache.clear();
                     Processor.collectInitialElements();
                     Processor.scheduleProcess();
                 }
             }, 150));
        } else {
             mainObserver.disconnect();
             AdUtils.cleanupScriptHooks();
        }
        if (CONFIG.mobileOptimizations.lazyLoadImages) {
            document.addEventListener('DOMContentLoaded', () => {
                document.querySelectorAll('img[data-src]').forEach(img => {
                     if (!(img instanceof Element) || img.src || Whitelist.isWhitelisted(img)) return;
                    if (img.dataset.src) {
                        img.src = img.dataset.src;
                        img.removeAttribute('data-src');
                    }
                });
            });
        }
        if (CONFIG.mobileOptimizations.removeImagePlaceholders) {
            document.addEventListener('DOMContentLoaded', () => {
                 const placeholders = document.querySelectorAll('.image-placeholder, [placeholder], [aria-hidden="true"]');
                 placeholders.forEach(ph => {
                      if (!(ph instanceof Element) || Whitelist.isWhitelisted(ph)) return;
                     try {
                          if (ph.tagName === 'INPUT' || ph.tagName === 'TEXTAREA' || ph.contains(document.activeElement)) return;
                           const rect = ph.getBoundingClientRect();
                           const style = getComputedStyle(ph);
                           if (rect.width < 5 && rect.height < 5 && style.position === 'absolute' && parseFloat(style.opacity) < 0.1) {
                                if (ph.parentNode) ph.parentNode.removeChild(ph);
                           } else if (ph.textContent.trim() === '' && ph.children.length === 0 && style.display === 'none') {
                                if (ph.parentNode) ph.parentNode.removeChild(ph);
                           }
                     } catch(e) { }
                 });
            });
        }
         if (CONFIG.modules.main) {
             StyleManager.inject();
         }
    }
    init();
})();

QingJ © 2025

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