Greasy Fork

来自缓存

Greasy Fork is available in English.

网页广告拦截器

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         网页广告拦截器
// @namespace    http://tampermonkey.net/
// @version      5.3.0
// @author       DeepSeek&Gemini
// @description  一个手机端浏览器能用的强大的广告拦截器
// @match        *://*/*
// @license      MIT
// @grant        unsafeWindow
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_notification
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    const CONFIG = {
        Z_INDEX: 2147483640,
        CACHE_TTL: 300000,
        BATCH_SIZE: 20,
        LOG_MAX: 50,
        LOG_IDENTIFIER_TTL: 300000,
        STRONG_BLOCK_TIMEOUT: 600000,
        DEBOUNCE_WAIT: 100,
        THROTTLE_LIMIT: 100,
        RESIDUAL_MIN_WIDTH: 30,
        RESIDUAL_MIN_HEIGHT: 30,
        RESIDUAL_AD_SELECTORS: 'div, span, embed',
        URL_DYNAMIC_PATTERNS: [
            /\?.*[tT]=/,
            /\?.*timestamp/,
            /\?.*rand/,
            /\?.*rnd/,
            /\?.*[0-9]{13,}/,
            /\?.*\d{10,}/,
            /\/\d{10,}\./,
            /\/[0-9a-f]{32,}\./
        ]
    };

    const _globals = (typeof unsafeWindow !== 'undefined' ? unsafeWindow : window);
    const _document = _globals.document;
    const _location = _globals.location;
    const _MutationObserver = _globals.MutationObserver;
    const _Element = _globals.Element;
    const _Node = _globals.Node;
    const _setTimeout = _globals.setTimeout;
    const _clearTimeout = _globals.clearTimeout;
    const _requestAnimationFrame = _globals.requestAnimationFrame;
    const _cancelAnimationFrame = _globals.cancelAnimationFrame;
    const _XMLHttpRequest = _globals.XMLHttpRequest;
    const _fetch = _globals.fetch;
    const _Proxy = _globals.Proxy;
    const _Set = _globals.Set;
    const _Map = _globals.Map;
    const _IntersectionObserver = _globals.IntersectionObserver;
    const _AbortController = _globals.AbortController;
    const _requestIdleCallback = _globals.requestIdleCallback || function(cb) {
        return _setTimeout(cb, 1);
    };
    const _cancelIdleCallback = _globals.cancelIdleCallback || _clearTimeout;

    function escapeHtml(unsafe) {
        if (unsafe == null) return '';
        return String(unsafe)
            .replace(/&/g, '&')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#39;');
    }

    function generateContentHash(str, maxLength = 500) {
        if (typeof str !== 'string') return '';
        const content = str.slice(0, maxLength);
        let hash = 0;
        for (let i = 0; i < content.length; i++) {
            const char = content.charCodeAt(i);
            hash = ((hash << 5) - hash) + char;
            hash = hash & hash;
        }
        return 'hash_' + Math.abs(hash).toString(36);
    }

    if (_document.designMode === 'on' || _document.documentElement.style.pointerEvents === 'none') {
        _document.designMode = 'off';
        _document.documentElement.style.pointerEvents = '';
    }

    let activePanels = new _Set();
    let strongBlockingEnabled = false;
    let blockingTimer = null;

    const _beforeunloadHandler = function(e) {
        e.preventDefault();
        e.returnValue = '系统可能不会保存您所做的更改。';
        return '系统可能不会保存您所做的更改。';
    };

    const _errorHandler = function(e) {
        if (strongBlockingEnabled) {
            disableStrongBlocking();
            activePanels.clear();
        }
    };

    function enableStrongBlocking() {
        if (!currentConfig.navigationBlockEnabled) return;
        if (strongBlockingEnabled) return;
        _globals.addEventListener('beforeunload', _beforeunloadHandler);
        _globals.addEventListener('error', _errorHandler, true);
        strongBlockingEnabled = true;
        if (blockingTimer) _clearTimeout(blockingTimer);
        blockingTimer = _setTimeout(() => {
            if (activePanels.size > 0) {
                activePanels.clear();
                disableStrongBlocking();
            }
        }, CONFIG.STRONG_BLOCK_TIMEOUT);
    }

    function disableStrongBlocking() {
        if (!strongBlockingEnabled) return;
        _globals.removeEventListener('beforeunload', _beforeunloadHandler);
        _globals.removeEventListener('error', _errorHandler, true);
        strongBlockingEnabled = false;
        if (blockingTimer) {
            _clearTimeout(blockingTimer);
            blockingTimer = null;
        }
    }

    function setupNavigationBlocking(panelId) {
        if (!currentConfig.navigationBlockEnabled) return;
        activePanels.add(panelId);
        if (activePanels.size === 1) {
            enableStrongBlocking();
            if (currentConfig.modules.blockDynamicScripts) {
                DynamicScriptInterceptor.disable();
            }
        }
    }

    function teardownNavigationBlocking(panelId) {
        if (!currentConfig.navigationBlockEnabled) return;
        activePanels.delete(panelId);
        if (activePanels.size === 0) {
            disableStrongBlocking();
            if (currentConfig.modules.blockDynamicScripts) {
                DynamicScriptInterceptor.enable();
            }
        }
    }

    const DEFAULT_MODULE_STATE = {
        removeInlineScripts: false,
        removeExternalScripts: false,
        interceptThirdParty: false,
        blockDynamicScripts: false,
        manageCSP: false,
        scriptBlacklistMode: false
    };

    const MODULE_NAMES = {
        removeInlineScripts: '移除内嵌脚本',
        removeExternalScripts: '移除外联脚本',
        blockDynamicScripts: '拦截动态脚本',
        interceptThirdParty: '拦截第三方资源',
        manageCSP: 'CSP策略管理',
        scriptBlacklistMode: '脚本黑名单模式'
    };

    const DEFAULT_CSP_RULES_TEMPLATE = [{
        id: 1,
        name: '只允许同源外部脚本',
        rule: "script-src 'self'",
        enabled: false
    }, {
        id: 2,
        name: '只允许同源外部样式',
        rule: "style-src 'self'",
        enabled: false
    }, {
        id: 3,
        name: '只允许同源图片',
        rule: "img-src 'self'",
        enabled: false
    }, {
        id: 4,
        name: '禁止所有框架',
        rule: "frame-src 'none'",
        enabled: false
    }, {
        id: 5,
        name: '禁止所有媒体',
        rule: "media-src 'none'",
        enabled: false
    }, {
        id: 6,
        name: '禁止所有对象与嵌入',
        rule: "object-src 'none'",
        enabled: false
    }];

    let currentConfig = {
        modules: { ...DEFAULT_MODULE_STATE
        },
        cspRules: DEFAULT_CSP_RULES_TEMPLATE.map(rule => ({ ...rule
        })),
        whitelist: new Set(),
        keywordWhitelist: new Set(),
        thirdPartySettings: {},
        scriptBlacklist: new Set(),
        thirdPartyWhitelist: [],
        inlineScriptStrictMode: false,
        thirdPartyStrictMode: false,
        thirdPartyStrictMethod: false,
        spoofUAEnabled: false,
        residualCleanupEnabled: false,
        cssUniversalHideEnabled: false,
        iframeUIFix: false,
        navigationBlockEnabled: true,
        redirectBlockerEnabled: false
    };

    const StorageManager = {
        getConfigKey(domain) {
            return `adblock_unified_config_${domain}`;
        },
        loadConfig() {
            const hostname = _location.hostname;
            const key = this.getConfigKey(hostname);
            try {
                const saved = GM_getValue(key, null);
                if (saved) {
                    const data = JSON.parse(saved);
                    if (data.modules) Object.assign(currentConfig.modules, data.modules);
                    if (data.cspRules) currentConfig.cspRules = data.cspRules.map(r => ({ ...r
                    }));
                    if (Array.isArray(data.whitelist)) currentConfig.whitelist = new Set(data.whitelist);
                    if (Array.isArray(data.keywordWhitelist)) currentConfig.keywordWhitelist = new Set(data.keywordWhitelist);
                    if (data.thirdPartySettings && typeof data.thirdPartySettings === 'object') currentConfig.thirdPartySettings = data.thirdPartySettings;
                    if (Array.isArray(data.scriptBlacklist)) currentConfig.scriptBlacklist = new Set(data.scriptBlacklist);
                    if (Array.isArray(data.thirdPartyWhitelist)) currentConfig.thirdPartyWhitelist = data.thirdPartyWhitelist;
                    if (data.inlineScriptStrictMode !== undefined) currentConfig.inlineScriptStrictMode = data.inlineScriptStrictMode;
                    if (data.thirdPartyStrictMode !== undefined) currentConfig.thirdPartyStrictMode = data.thirdPartyStrictMode;
                    if (data.thirdPartyStrictMethod !== undefined) currentConfig.thirdPartyStrictMethod = data.thirdPartyStrictMethod;
                    if (data.spoofUAEnabled !== undefined) currentConfig.spoofUAEnabled = data.spoofUAEnabled;
                    if (data.residualCleanupEnabled !== undefined) currentConfig.residualCleanupEnabled = data.residualCleanupEnabled;
                    if (data.cssUniversalHideEnabled !== undefined) currentConfig.cssUniversalHideEnabled = data.cssUniversalHideEnabled;
                    if (data.iframeUIFix !== undefined) currentConfig.iframeUIFix = data.iframeUIFix;
                    if (data.navigationBlockEnabled !== undefined) currentConfig.navigationBlockEnabled = data.navigationBlockEnabled;
                    if (data.redirectBlockerEnabled !== undefined) currentConfig.redirectBlockerEnabled = data.redirectBlockerEnabled;
                }
            } catch (e) {}
            if (!currentConfig.thirdPartySettings) currentConfig.thirdPartySettings = {};
            if (!currentConfig.thirdPartyWhitelist) currentConfig.thirdPartyWhitelist = [];
            if (currentConfig.thirdPartySettings.blockParentSubDomains === undefined) currentConfig.thirdPartySettings.blockParentSubDomains = true;
            if (currentConfig.inlineScriptStrictMode === undefined) currentConfig.inlineScriptStrictMode = false;
            if (currentConfig.thirdPartyStrictMode === undefined) currentConfig.thirdPartyStrictMode = false;
            if (currentConfig.thirdPartyStrictMethod === undefined) currentConfig.thirdPartyStrictMethod = false;
            if (currentConfig.residualCleanupEnabled === undefined) currentConfig.residualCleanupEnabled = false;
            if (currentConfig.cssUniversalHideEnabled === undefined) currentConfig.cssUniversalHideEnabled = false;
            if (currentConfig.iframeUIFix === undefined) currentConfig.iframeUIFix = false;
            if (currentConfig.navigationBlockEnabled === undefined) currentConfig.navigationBlockEnabled = true;
            if (currentConfig.redirectBlockerEnabled === undefined) currentConfig.redirectBlockerEnabled = false;
            const legacySpoof = GM_getValue('spoofUAEnabled', null);
            if (legacySpoof !== null) {
                currentConfig.spoofUAEnabled = legacySpoof;
                GM_setValue('spoofUAEnabled', null);
                this.saveConfig();
            }
        },
        saveConfig() {
            const hostname = _location.hostname;
            const key = this.getConfigKey(hostname);
            const toStore = {
                modules: currentConfig.modules,
                cspRules: currentConfig.cspRules,
                whitelist: Array.from(currentConfig.whitelist),
                keywordWhitelist: Array.from(currentConfig.keywordWhitelist),
                thirdPartySettings: currentConfig.thirdPartySettings,
                scriptBlacklist: Array.from(currentConfig.scriptBlacklist),
                thirdPartyWhitelist: currentConfig.thirdPartyWhitelist,
                inlineScriptStrictMode: currentConfig.inlineScriptStrictMode,
                thirdPartyStrictMode: currentConfig.thirdPartyStrictMode,
                thirdPartyStrictMethod: currentConfig.thirdPartyStrictMethod,
                spoofUAEnabled: currentConfig.spoofUAEnabled,
                residualCleanupEnabled: currentConfig.residualCleanupEnabled,
                cssUniversalHideEnabled: currentConfig.cssUniversalHideEnabled,
                iframeUIFix: currentConfig.iframeUIFix,
                navigationBlockEnabled: currentConfig.navigationBlockEnabled,
                redirectBlockerEnabled: currentConfig.redirectBlockerEnabled
            };
            GM_setValue(key, JSON.stringify(toStore));
        },
        resetAllSettings() {
            currentConfig.modules = { ...DEFAULT_MODULE_STATE
            };
            currentConfig.cspRules = DEFAULT_CSP_RULES_TEMPLATE.map(rule => ({ ...rule
            }));
            currentConfig.whitelist.clear();
            currentConfig.keywordWhitelist.clear();
            currentConfig.thirdPartySettings = {};
            currentConfig.scriptBlacklist.clear();
            currentConfig.thirdPartyWhitelist = [];
            currentConfig.inlineScriptStrictMode = false;
            currentConfig.thirdPartyStrictMode = false;
            currentConfig.thirdPartyStrictMethod = false;
            currentConfig.spoofUAEnabled = false;
            currentConfig.residualCleanupEnabled = false;
            currentConfig.cssUniversalHideEnabled = false;
            currentConfig.iframeUIFix = false;
            currentConfig.navigationBlockEnabled = true;
            currentConfig.redirectBlockerEnabled = false;
            this.saveConfig();
            GM_setValue(`residual_cleanup_prompt_shown_${_location.hostname}`, null);
        }
    };

    class LRUCache {
        constructor(capacity = 100, defaultTTL = 0) {
            this.capacity = capacity;
            this.defaultTTL = defaultTTL;
            this.cache = new _Map();
        }
        get(key) {
            if (!this.cache.has(key)) return null;
            const entry = this.cache.get(key);
            const ttl = entry.ttl !== undefined ? entry.ttl : this.defaultTTL;
            if (ttl > 0 && (Date.now() - entry.timestamp) > ttl) {
                this.cache.delete(key);
                return null;
            }
            this.cache.delete(key);
            this.cache.set(key, entry);
            return entry.value;
        }
        set(key, value, ttl) {
            const finalTTL = ttl !== undefined ? ttl : this.defaultTTL;
            const entry = {
                value,
                timestamp: Date.now(),
                ttl: finalTTL
            };
            if (this.cache.has(key)) {
                this.cache.delete(key);
            } else if (this.cache.size >= this.capacity) {
                const firstKey = this.cache.keys().next().value;
                this.cache.delete(firstKey);
            }
            this.cache.set(key, entry);
        }
        has(key) {
            const entry = this.cache.get(key);
            if (!entry) return false;
            const ttl = entry.ttl !== undefined ? entry.ttl : this.defaultTTL;
            if (ttl > 0 && (Date.now() - entry.timestamp) > ttl) {
                this.cache.delete(key);
                return false;
            }
            return true;
        }
        delete(key) {
            return this.cache.delete(key);
        }
        clear() {
            this.cache.clear();
        }
        get size() {
            return this.cache.size;
        }
    }

    const PUBLIC_SUFFIX_LIST = new Set([
        'co.uk', 'org.uk', 'me.uk', 'ltd.uk', 'plc.uk', 'net.uk', 'sch.uk', 'ac.uk', 'gov.uk',
        'com.cn', 'net.cn', 'org.cn', 'gov.cn', 'edu.cn',
        'co.jp', 'ne.jp', 'or.jp', 'go.jp', 'ac.jp', 'ad.jp',
        'com.au', 'net.au', 'org.au', 'edu.au', 'gov.au', 'asn.au', 'id.au',
        'co.nz', 'org.nz', 'net.nz', 'edu.nz', 'govt.nz',
        'com.br', 'net.br', 'org.br', 'gov.br', 'edu.br',
        'co.in', 'net.in', 'org.in', 'gov.in', 'ac.in', 'res.in',
        'co.kr', 'ne.kr', 'or.kr', 'go.kr', 'ac.kr',
        'com.tw', 'net.tw', 'org.tw', 'gov.tw', 'edu.tw', 'idv.tw'
    ]);

    class URLResolutionCache {
        constructor() {
            this.hostnameCache = new LRUCache(1000, CONFIG.CACHE_TTL);
            this.domainCache = new LRUCache(1000, CONFIG.CACHE_TTL);
            this.absoluteUrlCache = new LRUCache(1000, CONFIG.CACHE_TTL);
            this.thirdPartyCache = new LRUCache(1000, CONFIG.CACHE_TTL);
            this.whitelistCache = new LRUCache(500, CONFIG.CACHE_TTL);
            this.urlCheckCache = new LRUCache(500, CONFIG.CACHE_TTL);
        }
        isDynamicURL(url) {
            if (!url || typeof url !== 'string') return false;
            const cacheKey = `dynamic_${url}`;
            if (this.urlCheckCache.has(cacheKey)) return this.urlCheckCache.get(cacheKey);
            const result = CONFIG.URL_DYNAMIC_PATTERNS.some(pattern => pattern.test(url));
            this.urlCheckCache.set(cacheKey, result, CONFIG.CACHE_TTL);
            return result;
        }
        getHostname(url) {
            if (!url || typeof url !== 'string') return null;
            const cacheKey = `hostname_${url}`;
            if (this.hostnameCache.has(cacheKey)) return this.hostnameCache.get(cacheKey);
            if (url.startsWith('data:') || url.startsWith('blob:') || url.startsWith('about:blank')) {
                this.hostnameCache.set(cacheKey, null, 60000);
                return null;
            }
            try {
                const hostname = new URL(url, _location.href).hostname;
                this.hostnameCache.set(cacheKey, hostname, CONFIG.CACHE_TTL);
                return hostname;
            } catch (e) {
                this.hostnameCache.set(cacheKey, null, 30000);
                return null;
            }
        }
        isIPv4(hostname) {
            return /^(\d{1,3}\.){3}\d{1,3}$/.test(hostname);
        }
        getDomain(hostname) {
            if (!hostname) return null;
            const cacheKey = `domain_${hostname}`;
            if (this.domainCache.has(cacheKey)) return this.domainCache.get(cacheKey);
            if (this.isIPv4(hostname)) {
                this.domainCache.set(cacheKey, hostname, CONFIG.CACHE_TTL);
                return hostname;
            }
            const parts = hostname.split('.');
            let domain = hostname;
            for (let i = 1; i <= parts.length; i++) {
                const candidate = parts.slice(-i).join('.');
                if (PUBLIC_SUFFIX_LIST.has(candidate)) {
                    if (i + 1 <= parts.length) {
                        domain = parts.slice(-(i + 1)).join('.');
                    } else {
                        domain = candidate;
                    }
                    break;
                }
            }
            if (domain === hostname && parts.length > 2) {
                domain = parts.slice(-2).join('.');
            }
            this.domainCache.set(cacheKey, domain, CONFIG.CACHE_TTL);
            return domain;
        }
        getAbsoluteURL(url) {
            if (!url) return '';
            const cacheKey = `absolute_${url}_${_location.href}`;
            if (this.absoluteUrlCache.has(cacheKey)) return this.absoluteUrlCache.get(cacheKey);
            try {
                const absoluteUrl = new URL(url, _location.href).href;
                const ttl = this.isDynamicURL(url) ? 30000 : CONFIG.CACHE_TTL;
                this.absoluteUrlCache.set(cacheKey, absoluteUrl, ttl);
                return absoluteUrl;
            } catch (e) {
                this.absoluteUrlCache.set(cacheKey, url, 30000);
                return url;
            }
        }
        isThirdPartyHost(resourceHostname, currentHost, blockParentSubDomains = true) {
            if (!resourceHostname) return false;
            const cacheKey = `thirdparty_${resourceHostname}_${currentHost}_${blockParentSubDomains}`;
            if (this.thirdPartyCache.has(cacheKey)) return this.thirdPartyCache.get(cacheKey);
            let isThirdParty = false;
            if (!currentHost || !resourceHostname) {
                isThirdParty = false;
            } else if (resourceHostname === currentHost) {
                isThirdParty = false;
            } else if (blockParentSubDomains) {
                isThirdParty = true;
            } else {
                const currentDomain = this.getDomain(currentHost);
                const resourceDomain = this.getDomain(resourceHostname);
                isThirdParty = currentDomain !== resourceDomain;
            }
            this.thirdPartyCache.set(cacheKey, isThirdParty, CONFIG.CACHE_TTL);
            return isThirdParty;
        }
        isWhitelisted(url, thirdPartyWhitelist) {
            if (!url || !thirdPartyWhitelist) return false;
            const cacheKey = `whitelist_${url}_${_location.hostname}`;
            if (this.whitelistCache.has(cacheKey)) return this.whitelistCache.get(cacheKey);
            let isWhitelisted = false;
            for (const pattern of thirdPartyWhitelist) {
                if (!pattern) continue;
                try {
                    if (pattern.includes('://')) {
                        if (url.includes(pattern)) {
                            isWhitelisted = true;
                            break;
                        }
                    } else {
                        const urlHost = new URL(url, _location.href).hostname;
                        let patternHost = pattern.startsWith('*.') ? pattern.substring(2) : pattern;
                        const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
                        patternHost = escapeRegExp(patternHost);
                        const regex = new RegExp(`(^|\\.)${patternHost}$`);
                        if (regex.test(urlHost)) {
                            isWhitelisted = true;
                            break;
                        }
                    }
                } catch (e) {
                    if (url.includes(pattern)) {
                        isWhitelisted = true;
                        break;
                    }
                }
            }
            this.whitelistCache.set(cacheKey, isWhitelisted, CONFIG.CACHE_TTL);
            return isWhitelisted;
        }
        clear() {
            this.hostnameCache.clear();
            this.domainCache.clear();
            this.absoluteUrlCache.clear();
            this.thirdPartyCache.clear();
            this.whitelistCache.clear();
            this.urlCheckCache.clear();
        }
    }

    const urlCache = new URLResolutionCache();

    const Utils = {
        truncateString(str, maxLength = 200) {
            if (typeof str !== 'string') return '';
            if (str.length <= maxLength) return str;
            return str.slice(0, maxLength) + '...';
        },
        getCurrentHostname() {
            return _location.hostname;
        },
        isElement(el) {
            return el instanceof _Element;
        },
        getScriptContentPreview(scriptElement) {
            if (!scriptElement || scriptElement.tagName !== 'SCRIPT') return '';
            return this.truncateString(scriptElement.textContent, 200);
        },
        getIframeSrcPreview(iframeElement) {
            if (!iframeElement || iframeElement.tagName !== 'IFRAME') return '';
            return this.truncateString(iframeElement.src, 200);
        },
        getResourceHostname(url) {
            return urlCache.getHostname(url);
        },
        getDomain(hostname) {
            return urlCache.getDomain(hostname);
        },
        isThirdPartyHost(resourceHostname, currentHost, blockParentSubDomains = true) {
            return urlCache.isThirdPartyHost(resourceHostname, currentHost, blockParentSubDomains);
        },
        getAbsoluteURL(url) {
            return urlCache.getAbsoluteURL(url);
        },
        isThirdParty(url, blockParentSubDomains = true) {
            if (!url) return false;
            const cacheKey = `isThirdParty_${url}_${_location.hostname}_${blockParentSubDomains}`;
            if (urlCache.urlCheckCache.has(cacheKey)) return urlCache.urlCheckCache.get(cacheKey);
            try {
                const urlObj = new URL(url, _location.href);
                const hostname = urlObj.hostname;
                if (!hostname || url.startsWith('data:') || url.startsWith('blob:')) {
                    urlCache.urlCheckCache.set(cacheKey, false, CONFIG.CACHE_TTL);
                    return false;
                }
                const result = urlCache.isThirdPartyHost(hostname, this.getCurrentHostname(), blockParentSubDomains);
                urlCache.urlCheckCache.set(cacheKey, result, CONFIG.CACHE_TTL);
                return result;
            } catch (e) {
                urlCache.urlCheckCache.set(cacheKey, false, CONFIG.CACHE_TTL);
                return false;
            }
        },
        isSameOrigin(hostname) {
            return hostname === this.getCurrentHostname();
        },
        shouldInterceptByModule(element, moduleKey) {
            if (!currentConfig.modules[moduleKey]) return false;
            if (ProcessedElementsCache.isProcessed(element) || this.isParentProcessed(element)) return false;
            if (Whitelisting.isElementWhitelisted(element)) return false;
            return true;
        },
        getBlockParentSubDomainsSetting() {
            return !!currentConfig.thirdPartyStrictMode;
        },
        isUIElement(el) {
            return el?.classList && (el.classList.contains('mask') || el.classList.contains('panel') || el.id === 'ad-blocker-settings-container');
        },
        isPanelElement(el) {
            return el?.classList && el.classList.contains('panel');
        },
        isPanelClick(event) {
            const path = event.composedPath();
            return path.some(el => this.isUIElement(el));
        },
        isContainerEmpty(container, ignoreProcessedFlag = true) {
            if (!this.isElement(container)) return false;
            if (container.children.length === 0 && container.textContent.trim() === '') {
                return this.isSuspiciousAdContainer(container);
            }
            let hasVisibleContent = false;
            for (const child of container.childNodes) {
                if (child.nodeType === Node.TEXT_NODE && child.textContent.trim().length > 0) {
                    hasVisibleContent = true;
                    break;
                }
                if (child.nodeType === Node.ELEMENT_NODE) {
                    const el = child;
                    if (ignoreProcessedFlag && el.dataset && el.dataset.adblockProcessed === 'true') continue;
                    const style = getComputedStyle(el);
                    if (style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0') {
                        if (el.tagName === 'IMG' || el.tagName === 'VIDEO' || el.tagName === 'CANVAS' || (el.textContent && el.textContent.trim().length > 0)) {
                            hasVisibleContent = true;
                            break;
                        }
                        if (
                            style.opacity !== '0' &&
                            style.visibility !== 'hidden' &&
                            el.offsetWidth > 0 &&
                            el.offsetHeight > 0 &&
                            el.tagName !== 'BR' &&
                            el.tagName !== 'HR'
                        ) {
                            hasVisibleContent = true;
                            break;
                        }
                    }
                }
            }
            return !hasVisibleContent;
        },
        isSuspiciousAdContainer(container) {
            if (!this.isElement(container)) return false;
            const style = getComputedStyle(container);
            const hasAdStyles = (
                (style.backgroundColor !== 'rgba(0, 0, 0, 0)' && style.backgroundColor !== 'transparent') ||
                style.backgroundImage !== 'none' ||
                style.borderWidth !== '0px' ||
                style.boxShadow !== 'none' ||
                style.paddingTop !== '0px' ||
                style.paddingBottom !== '0px' ||
                style.marginTop !== '0px' ||
                style.marginBottom !== '0px' ||
                (parseFloat(style.width) > CONFIG.RESIDUAL_MIN_WIDTH && parseFloat(style.height) > CONFIG.RESIDUAL_MIN_HEIGHT) ||
                style.position === 'relative' ||
                style.position === 'absolute' ||
                style.position === 'fixed' ||
                style.position === 'sticky'
            );
            return hasAdStyles;
        },
        quickAdFilter(container) {
            if (!Utils.isElement(container)) return false;
            if (ProcessedElementsCache.isProcessed(container)) return false;
            if (Utils.isUIElement(container)) return false;
            if (!currentConfig.residualCleanupEnabled) return false;
            const isEmpty = Utils.isContainerEmpty(container, true);
            if (!isEmpty) return false;
            return Utils.isSuspiciousAdContainer(container);
        },
        debounce(func, wait = CONFIG.DEBOUNCE_WAIT) {
            let timeout;
            return function(...args) {
                _clearTimeout(timeout);
                timeout = _setTimeout(() => func.apply(this, args), wait);
            };
        },
        throttle(func, limit = CONFIG.THROTTLE_LIMIT) {
            let inThrottle;
            return function(...args) {
                if (!inThrottle) {
                    func.apply(this, args);
                    inThrottle = true;
                    _setTimeout(() => inThrottle = false, limit);
                }
            };
        },
        isParentProcessed(element) {
            let parent = element.parentElement;
            while (parent) {
                if (parent.dataset.adblockProcessed === 'true' || ProcessedElementsCache.isProcessed(parent)) return true;
                parent = parent.parentElement;
            }
            return false;
        },
        getContentIdentifier(element, reasonType = null) {
            if (!element && !reasonType) return null;
            if (element && this.isElement(element)) {
                const tagName = element.tagName;
                const src = element.src || element.getAttribute('data-src') || element.href || element.action || '';
                if (tagName === 'SCRIPT') {
                    return element.src ? `SCRIPT_SRC: ${this.truncateString(element.src, 500)}` : `SCRIPT_CONTENT: ${this.getScriptContentPreview(element)}`;
                } else if (tagName === 'IFRAME') {
                    return `IFRAME_SRC: ${this.truncateString(element.src, 500)}`;
                } else if (tagName === 'IMG') {
                    return src ? `IMG_SRC: ${this.truncateString(src, 500)}` : null;
                } else if (tagName === 'A') {
                    return src ? `A_HREF: ${this.truncateString(src, 500)}` : null;
                } else if (tagName === 'LINK' && element.rel === 'stylesheet' && element.href) {
                    return `CSS_HREF: ${this.truncateString(element.href, 500)}`;
                } else if (tagName === 'STYLE') {
                    return `STYLE_CONTENT: ${this.truncateString(element.textContent, 500)}`;
                } else if (tagName === 'EMBED') {
                    return src ? `EMBED_SRC: ${this.truncateString(src, 500)}` : null;
                } else if (tagName === 'OBJECT') {
                    return src ? `OBJECT_DATA: ${this.truncateString(src, 500)}` : null;
                }
                return null;
            } else if (reasonType && typeof reasonType.detail === 'string') {
                if (reasonType.detail.startsWith('SRC:')) {
                    return `${reasonType.type || 'INTERCEPTED'}_SRC: ${this.truncateString(reasonType.detail.substring(4).trim(), 500)}`;
                } else if (reasonType.detail.startsWith('URL:')) {
                    return `${reasonType.type || 'INTERCEPTED'}_URL: ${this.truncateString(reasonType.detail.substring(5).trim(), 500)}`;
                } else if (reasonType.type === 'EVAL') {
                    return `EVAL_HASH: ${generateContentHash(reasonType.detail, 500)}`;
                } else if (reasonType.type === 'FUNCTION_CONSTRUCTOR') {
                    return `FUNCTION_HASH: ${generateContentHash(reasonType.detail, 500)}`;
                } else if (reasonType.type === 'DOCUMENT_WRITE') {
                    return `DOCUMENT_WRITE_HASH: ${generateContentHash(reasonType.detail, 500)}`;
                } else if (reasonType.type === 'SETTIMEOUT') {
                    return `SETTIMEOUT_HASH: ${generateContentHash(reasonType.detail, 500)}`;
                } else if (reasonType.type === 'SETINTERVAL') {
                    return `SETINTERVAL_HASH: ${generateContentHash(reasonType.detail, 500)}`;
                } else if (reasonType.type === 'REQUESTANIMATIONFRAME') {
                    return `REQUESTANIMATIONFRAME_HASH: ${generateContentHash(reasonType.detail, 500)}`;
                } else if (reasonType.type === 'THIRD_PARTY') {
                    const urlMatch = reasonType.detail.match(/(https?:\/\/[^\s]+)/);
                    if (urlMatch) return `THIRD_PARTY_URL: ${this.truncateString(urlMatch[1], 500)}`;
                    return `THIRD_PARTY_DETAIL: ${this.truncateString(reasonType.detail, 500)}`;
                } else if (reasonType.type === 'SCRIPT_BLACKLIST') {
                    return `BLACKLIST: ${this.truncateString(reasonType.detail, 500)}`;
                } else if (reasonType.type === '内联事件') {
                    return `INLINE_EVENT: ${reasonType.detail}`;
                } else if (reasonType.type === 'javascript URL') {
                    return `JAVASCRIPT_URL: ${reasonType.detail}`;
                }
                return `LOG_DETAIL: ${this.truncateString(reasonType.detail, 500)}`;
            }
            return null;
        }
    };

    function shouldBlockResource(url) {
        if (!url) return false;
        if (urlCache.isWhitelisted(url, currentConfig.thirdPartyWhitelist)) return false;
        for (const keyword of currentConfig.keywordWhitelist) {
            if (url.includes(keyword)) return false;
        }
        return Utils.isThirdParty(url, Utils.getBlockParentSubDomainsSetting());
    }

    const LogManager = {
        logs: [],
        maxLogs: CONFIG.LOG_MAX,
        loggedContentIdentifiers: new LRUCache(CONFIG.LOG_MAX, CONFIG.LOG_IDENTIFIER_TTL),
        add(moduleKey, element, reason) {
            const anyModuleEnabled = Object.values(currentConfig.modules).some(v => v === true);
            if (!anyModuleEnabled) return;
            if (!Utils.isElement(element) && element !== null && typeof reason !== 'object') return;
            if (Whitelisting.isElementWhitelisted(element) || Whitelisting.isReasonWhitelisted(reason)) return;
            let elementIdentifier = '[未知元素]',
                interceptedContent = '[无法获取内容]',
                contentIdentifier = null,
                resourceDomain = '';
            if (reason && typeof reason.detail === 'string' && (
                    reason.type === '内联事件' ||
                    reason.type === 'javascript URL' ||
                    reason.type === 'EVAL' ||
                    reason.type === 'FUNCTION_CONSTRUCTOR' ||
                    reason.type === 'DOCUMENT_WRITE' ||
                    reason.type === 'SETTIMEOUT' ||
                    reason.type === 'SETINTERVAL' ||
                    reason.type === 'REQUESTANIMATIONFRAME' ||
                    reason.type === 'THIRD_PARTY' ||
                    reason.type === 'SCRIPT_BLACKLIST' ||
                    reason.type === 'THIRD_PARTY_SCAN'
                )) {
                interceptedContent = Utils.truncateString(reason.detail, 500);
                elementIdentifier = reason.type ? `[${reason.type}]` : '[未知类型]';
                if (reason.type === 'EVAL' || reason.type === 'FUNCTION_CONSTRUCTOR' || reason.type === 'DOCUMENT_WRITE' || reason.type === 'SETTIMEOUT' || reason.type === 'SETINTERVAL' || reason.type === 'REQUESTANIMATIONFRAME') {
                    contentIdentifier = Utils.getContentIdentifier(null, {
                        type: reason.type,
                        detail: reason.rawDetail || reason.detail
                    });
                } else {
                    contentIdentifier = Utils.getContentIdentifier(null, reason);
                }
                if (reason.detail.includes('://')) {
                    try {
                        const urlMatch = reason.detail.match(/https?:\/\/[^\s]+/);
                        if (urlMatch) resourceDomain = Utils.getResourceHostname(urlMatch[0]) || '';
                    } catch (e) {}
                }
            } else if (Utils.isElement(element)) {
                const tagName = element.tagName;
                const id = element.id ? `#${element.id}` : '';
                const className = element.className ? `.${element.className.split(/\s+/).join('.')}` : '';
                elementIdentifier = `${tagName}${id}${className}`;
                contentIdentifier = Utils.getContentIdentifier(element);
                if (tagName === 'SCRIPT') {
                    if (element.src) {
                        interceptedContent = `外联脚本: ${Utils.truncateString(element.src, 500)}`;
                        resourceDomain = Utils.getResourceHostname(element.src) || '';
                    } else {
                        interceptedContent = `内嵌脚本: ${Utils.getScriptContentPreview(element)}`;
                    }
                } else if (tagName === 'IFRAME') {
                    interceptedContent = Utils.getIframeSrcPreview(element);
                    if (element.src) resourceDomain = Utils.getResourceHostname(element.src) || '';
                } else if (tagName === 'IMG') {
                    const src = element.src || element.dataset.src || element.getAttribute('data-src') || '';
                    interceptedContent = Utils.truncateString(src, 500);
                    if (src) resourceDomain = Utils.getResourceHostname(src) || '';
                } else if (tagName === 'A') {
                    interceptedContent = Utils.truncateString(element.href || '', 500);
                    if (element.href) resourceDomain = Utils.getResourceHostname(element.href) || '';
                } else if (tagName === 'LINK') {
                    interceptedContent = Utils.truncateString(element.href || '', 500);
                    if (element.href) resourceDomain = Utils.getResourceHostname(element.href) || '';
                } else if (tagName === 'STYLE') {
                    interceptedContent = Utils.truncateString(element.textContent, 500);
                } else if (tagName === 'EMBED') {
                    interceptedContent = Utils.truncateString(element.src || '', 500);
                    if (element.src) resourceDomain = Utils.getResourceHostname(element.src) || '';
                } else if (tagName === 'OBJECT') {
                    interceptedContent = Utils.truncateString(element.data || '', 500);
                    if (element.data) resourceDomain = Utils.getResourceHostname(element.data) || '';
                } else {
                    interceptedContent = Utils.truncateString(element.outerHTML, 500);
                }
            } else if (reason && typeof reason.detail === 'string') {
                interceptedContent = Utils.truncateString(reason.detail, 500);
                elementIdentifier = reason.type ? `[${reason.type}]` : '[未知类型]';
                if (reason.type === 'EVAL' || reason.type === 'FUNCTION_CONSTRUCTOR' || reason.type === 'DOCUMENT_WRITE' || reason.type === 'SETTIMEOUT' || reason.type === 'SETINTERVAL' || reason.type === 'REQUESTANIMATIONFRAME') {
                    contentIdentifier = Utils.getContentIdentifier(null, {
                        type: reason.type,
                        detail: reason.rawDetail || reason.detail
                    });
                } else {
                    contentIdentifier = Utils.getContentIdentifier(null, reason);
                }
                if (reason.detail.includes('://')) {
                    try {
                        const urlMatch = reason.detail.match(/https?:\/\/[^\s]+/);
                        if (urlMatch) resourceDomain = Utils.getResourceHostname(urlMatch[0]) || '';
                    } catch (e) {}
                }
            }
            if (!contentIdentifier) return;
            const existingIndex = this.logs.findIndex(l => l.contentIdentifier === contentIdentifier);
            if (existingIndex !== -1) {
                this.logs.splice(existingIndex, 1);
            } else if (this.loggedContentIdentifiers.has(contentIdentifier)) {
                return;
            }
            const logId = this.logs.length + 1;
            const logEntry = {
                id: logId,
                moduleKey: moduleKey,
                module: MODULE_NAMES[moduleKey] || moduleKey,
                element: elementIdentifier,
                content: interceptedContent,
                domain: resourceDomain,
                timestamp: Date.now(),
                contentIdentifier: contentIdentifier
            };
            this.logs.push(logEntry);
            this.loggedContentIdentifiers.set(contentIdentifier, true);
            if (this.logs.length > this.maxLogs) {
                const removed = this.logs.shift();
                this.loggedContentIdentifiers.delete(removed.contentIdentifier);
            }
        },
        clearLoggedIdentifiers() {
            this.loggedContentIdentifiers.clear();
        }
    };

    const Whitelisting = {
        isElementWhitelisted(element) {
            if (!element || !Utils.isElement(element)) return false;
            const contentIdentifier = Utils.getContentIdentifier(element);
            if (contentIdentifier && currentConfig.whitelist.has(contentIdentifier)) return true;
            if (currentConfig.modules.interceptThirdParty) {
                const hostname = Utils.getResourceHostname(element.src || element.href || element.action || element.data || '');
                if (hostname) {
                    const resourceUrl = element.src || element.href || element.action || element.data || '';
                    if (urlCache.isWhitelisted(resourceUrl, currentConfig.thirdPartyWhitelist)) return true;
                }
            }
            const keywordWhitelist = currentConfig.keywordWhitelist;
            if (keywordWhitelist.size > 0) {
                const scriptContent = element.textContent || '';
                const src = element.src || element.href || element.action || element.data || '';
                for (const keyword of keywordWhitelist) {
                    if (!keyword) continue;
                    if (scriptContent.includes(keyword) || src.includes(keyword)) return true;
                }
            }
            return false;
        },
        isReasonWhitelisted(reason) {
            if (!reason || typeof reason.detail !== 'string') return false;
            let contentIdentifier = null;
            if (reason.type === 'EVAL' || reason.type === 'FUNCTION_CONSTRUCTOR' || reason.type === 'DOCUMENT_WRITE' || reason.type === 'SETTIMEOUT' || reason.type === 'SETINTERVAL' || reason.type === 'REQUESTANIMATIONFRAME') {
                contentIdentifier = Utils.getContentIdentifier(null, {
                    type: reason.type,
                    detail: reason.rawDetail || reason.detail
                });
            } else {
                contentIdentifier = Utils.getContentIdentifier(null, reason);
            }
            if (contentIdentifier && currentConfig.whitelist.has(contentIdentifier)) return true;
            if (currentConfig.modules.interceptThirdParty) {
                const urlMatch = reason.detail.match(/https?:\/\/[^\s]+/);
                if (urlMatch) {
                    const url = urlMatch[0];
                    if (urlCache.isWhitelisted(url, currentConfig.thirdPartyWhitelist)) return true;
                }
            }
            const keywordWhitelist = currentConfig.keywordWhitelist;
            if (keywordWhitelist.size > 0) {
                for (const keyword of keywordWhitelist) {
                    if (!keyword) continue;
                    if (reason.detail.includes(keyword)) return true;
                }
            }
            return false;
        },
        isCodeWhitelisted(code, type) {
            if (typeof code !== 'string' || code.trim() === '') return false;
            if (currentConfig.keywordWhitelist.size > 0) {
                for (const keyword of currentConfig.keywordWhitelist) {
                    if (!keyword) continue;
                    if (code.includes(keyword)) return true;
                }
            }
            const contentIdentifier = Utils.getContentIdentifier(null, {
                type: type,
                detail: code
            });
            if (contentIdentifier && currentConfig.whitelist.has(contentIdentifier)) return true;
            return false;
        },
        add(contentIdentifier) {
            if (!contentIdentifier || contentIdentifier.trim() === '') return;
            currentConfig.whitelist.add(contentIdentifier);
            StorageManager.saveConfig();
        },
        addKeyword(keyword) {
            if (!keyword || keyword.trim() === '') return;
            currentConfig.keywordWhitelist.add(keyword.trim());
            StorageManager.saveConfig();
        },
        removeKeywordsMatchingDomain(domain) {
            let changed = false;
            const keywordsToRemove = [];
            for (const keyword of currentConfig.keywordWhitelist) {
                if (domain.includes(keyword)) keywordsToRemove.push(keyword);
            }
            keywordsToRemove.forEach(k => {
                currentConfig.keywordWhitelist.delete(k);
                changed = true;
            });
            if (changed) StorageManager.saveConfig();
        },
        clearAllWhitelists() {
            currentConfig.whitelist.clear();
            currentConfig.keywordWhitelist.clear();
            currentConfig.thirdPartyWhitelist = [];
            StorageManager.saveConfig();
        }
    };

    const ProcessedElementsCache = {
        _processedElements: new WeakSet(),
        isProcessed(element) {
            if (!Utils.isElement(element)) return false;
            return this._processedElements.has(element) || element.dataset.adblockProcessed === 'true';
        },
        markAsProcessed(element) {
            if (!Utils.isElement(element)) return;
            this._processedElements.add(element);
            element.dataset.adblockProcessed = 'true';
        },
        clear() {
            this._processedElements = new WeakSet();
        }
    };

    const ResidualCleaner = {
        _scanTimer: null,
        _dedupSet: new WeakSet(),
        observer: null,
        init() {
            const anyModuleEnabled = Object.values(currentConfig.modules).some(v => v === true);
            if (!anyModuleEnabled) return;
            if (!currentConfig.residualCleanupEnabled) {
                this.stop();
                return;
            }
            this.setupMutationObserver();
            this.initialScan();
            this.startPeriodicScan();
        },
        stop() {
            if (this.observer) {
                this.observer.disconnect();
                this.observer = null;
            }
            if (this._scanTimer !== null) {
                _cancelIdleCallback(this._scanTimer);
                this._scanTimer = null;
            }
            this._dedupSet = new WeakSet();
        },
        setupMutationObserver() {
            this.observer = new _MutationObserver((mutations) => {
                for (const mutation of mutations) {
                    if (mutation.type === 'childList') {
                        if (mutation.addedNodes.length > 0) {
                            for (const node of mutation.addedNodes) {
                                if (node.nodeType === Node.ELEMENT_NODE && Utils.isElement(node)) {
                                    this.checkAndCleanup(node);
                                }
                            }
                        }
                        if (mutation.removedNodes.length > 0 && mutation.target && Utils.isElement(mutation.target)) {
                            this.checkAndCleanup(mutation.target);
                        }
                    }
                }
            });
            this.observer.observe(_document.documentElement, {
                childList: true,
                subtree: true,
                attributes: false
            });
        },
        checkAndCleanup(element) {
            if (!element || !Utils.isElement(element) || this._dedupSet.has(element)) return;
            const width = element.offsetWidth;
            const height = element.offsetHeight;
            if (width > 0 && width < CONFIG.RESIDUAL_MIN_WIDTH && height > 0 && height < CONFIG.RESIDUAL_MIN_HEIGHT) {
                return;
            }
            if (!Utils.quickAdFilter(element)) return;
            this._dedupSet.add(element);
            this.cleanupContainer(element);
        },
        cleanupContainer(container) {
            if (!container || !Utils.isElement(container) || !container.isConnected) return;
            if (container === _document.body || container === _document.documentElement) return;
            const style = getComputedStyle(container);
            if (style.position === 'absolute' || style.position === 'fixed' || style.position === 'sticky') {
                container.style.setProperty('display', 'none', 'important');
                container.style.setProperty('visibility', 'hidden', 'important');
                container.style.setProperty('pointer-events', 'none', 'important');
                if (container.style.height !== '0px') container.style.setProperty('height', '0px', 'important');
                if (container.style.width !== '0px') container.style.setProperty('width', '0px', 'important');
                container.style.setProperty('opacity', '0', 'important');
                ProcessedElementsCache.markAsProcessed(container);
            } else {
                container.remove();
                ProcessedElementsCache.markAsProcessed(container);
            }
        },
        initialScan() {
            const elementsToScan = Array.from(_document.querySelectorAll(CONFIG.RESIDUAL_AD_SELECTORS));
            const toRemove = [];
            const toHide = [];
            for (let i = 0; i < elementsToScan.length; i++) {
                const element = elementsToScan[i];
                if (!element || !Utils.isElement(element) || this._dedupSet.has(element)) continue;
                const width = element.offsetWidth;
                const height = element.offsetHeight;
                if (width > 0 && width < CONFIG.RESIDUAL_MIN_WIDTH && height > 0 && height < CONFIG.RESIDUAL_MIN_HEIGHT) continue;
                if (!Utils.quickAdFilter(element)) continue;
                this._dedupSet.add(element);
                const style = getComputedStyle(element);
                if (style.position === 'absolute' || style.position === 'fixed' || style.position === 'sticky') {
                    toHide.push(element);
                } else {
                    toRemove.push(element);
                }
            }
            if (toRemove.length > 0) {
                const fragment = _document.createDocumentFragment();
                for (let i = 0; i < toRemove.length; i++) {
                    ProcessedElementsCache.markAsProcessed(toRemove[i]);
                    fragment.appendChild(toRemove[i]);
                }
            }
            for (let i = 0; i < toHide.length; i++) {
                const el = toHide[i];
                el.style.setProperty('display', 'none', 'important');
                el.style.setProperty('visibility', 'hidden', 'important');
                el.style.setProperty('pointer-events', 'none', 'important');
                if (el.style.height !== '0px') el.style.setProperty('height', '0px', 'important');
                if (el.style.width !== '0px') el.style.setProperty('width', '0px', 'important');
                el.style.setProperty('opacity', '0', 'important');
                ProcessedElementsCache.markAsProcessed(el);
            }
        },
        startPeriodicScan() {
            let lastScanTime = 0;
            const scanLoop = (deadline) => {
                if (!currentConfig.residualCleanupEnabled) return;
                const now = Date.now();
                if (now - lastScanTime > 3000 && deadline.timeRemaining() > 5) {
                    const elementsToScan = Array.from(_document.querySelectorAll(CONFIG.RESIDUAL_AD_SELECTORS));
                    let processedCount = 0;
                    const maxPerIdle = 20;
                    while (elementsToScan.length > 0 && deadline.timeRemaining() > 5 && processedCount < maxPerIdle) {
                        this.checkAndCleanup(elementsToScan.shift());
                        processedCount++;
                    }
                    if (processedCount > 0) lastScanTime = now;
                }
                this._scanTimer = _requestIdleCallback(scanLoop);
            };
            this._scanTimer = _requestIdleCallback(scanLoop);
        }
    };

    const CSSUniversalHider = {
        _enabled: false,
        _observer: null,
        _scanTimer: null,
        _processedSet: new WeakSet(),
        _hideClass: 'adblock-universal-hidden',
        _styleElement: null,
        _universalSelector: `[style^='left'],[style^='visibility:visible;padding:0;margin:0;-webkit-appearance:none;position:absolute !important;left:0px;top:-'],[style*='background-size: 400px 127px !important;'],[style*='position: fixed; left: 0px; transform: none; top: 0px; z-index: '],[style$='important;'],p[class],ul *,UL,H2,[style^='display: block; z-index: '],[style^='background-image: url('],[style^='display: block;'],[style$='display: block;'],[class*='_'][id*='_'],[style='height:0px;'],[style*='background: url']`,
        init() {
            if (currentConfig.cssUniversalHideEnabled) {
                this.enable();
            } else {
                this.disable();
            }
        },
        enable() {
            if (this._enabled) return;
            this._enabled = true;
            this.injectStyle();
            this.startObserver();
            this.startPeriodicScan();
            this.scanAndHide();
        },
        disable() {
            if (!this._enabled) return;
            this._enabled = false;
            if (this._observer) {
                this._observer.disconnect();
                this._observer = null;
            }
            if (this._scanTimer) {
                _clearTimeout(this._scanTimer);
                this._scanTimer = null;
            }
            if (this._styleElement && this._styleElement.parentNode) {
                this._styleElement.parentNode.removeChild(this._styleElement);
                this._styleElement = null;
            }
            const elements = _document.querySelectorAll(`.${this._hideClass}`);
            elements.forEach(el => {
                el.classList.remove(this._hideClass);
                el.style.display = '';
                el.style.visibility = '';
                el.style.opacity = '';
                el.style.position = '';
                el.style.left = '';
                el.style.top = '';
                el.style.width = '';
                el.style.height = '';
                el.style.pointerEvents = '';
            });
            this._processedSet = new WeakSet();
        },
        injectStyle() {
            if (this._styleElement) return;
            const style = _document.createElement('style');
            style.textContent = `.${this._hideClass} { display: none !important; visibility: hidden !important; opacity: 0 !important; pointer-events: none !important; position: absolute !important; left: -9999px !important; top: -9999px !important; width: 0 !important; height: 0 !important; overflow: hidden !important; z-index: -999 !important; }`;
            _document.head.appendChild(style);
            this._styleElement = style;
        },
        shouldHideElement(el) {
            if (!el || el.nodeType !== 1) return false;
            if (el.getAttribute && el.getAttribute('data-adblock-safe') === 'true') return false;
            if (Utils.isUIElement(el)) return false;
            if (this._processedSet.has(el)) return false;
            let zIndex = parseInt(window.getComputedStyle(el).zIndex);
            if (isNaN(zIndex)) zIndex = 0;
            if (zIndex > 600) return true;
            return false;
        },
        hideElement(el) {
            if (!this.shouldHideElement(el)) return;
            el.classList.add(this._hideClass);
            this._processedSet.add(el);
        },
        scanAndHide() {
            if (!this._enabled) return;
            let elements;
            try {
                elements = _document.querySelectorAll(this._universalSelector);
            } catch (e) {
                elements = [];
            }
            for (let i = 0; i < elements.length; i++) {
                const el = elements[i];
                if (this.shouldHideElement(el)) {
                    this.hideElement(el);
                }
            }
        },
        startObserver() {
            if (this._observer) this._observer.disconnect();
            this._observer = new _MutationObserver((mutations) => {
                let needsScan = false;
                for (const mutation of mutations) {
                    if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                        needsScan = true;
                        break;
                    }
                    if (mutation.type === 'attributes' && (mutation.attributeName === 'style' || mutation.attributeName === 'class')) {
                        needsScan = true;
                        break;
                    }
                }
                if (needsScan) {
                    this.scanAndHide();
                }
            });
            this._observer.observe(_document.documentElement, {
                childList: true,
                subtree: true,
                attributes: true,
                attributeFilter: ['style', 'class']
            });
        },
        startPeriodicScan() {
            const scanLoop = () => {
                try {
                    if (!this._enabled) return;
                    this.scanAndHide();
                } catch (e) {}
                this._scanTimer = _setTimeout(scanLoop, 5000);
            };
            this._scanTimer = _setTimeout(scanLoop, 5000);
        }
    };

    const ResourceCanceller = {
        cancelResourceLoading(element) {
            if (!Utils.isElement(element) || ProcessedElementsCache.isProcessed(element)) return;
            const tagName = element.tagName;
            if (tagName === 'IMG') {
                element.removeAttribute('src');
                element.removeAttribute('srcset');
                element.removeAttribute('data-src');
            } else if (tagName === 'IFRAME') {
                element.removeAttribute('src');
                element.style.display = 'none';
            } else if (tagName === 'SCRIPT') {
                element.removeAttribute('src');
            } else if (tagName === 'LINK' && element.rel === 'stylesheet') {
                element.removeAttribute('href');
            } else if (tagName === 'STYLE') {
                element.textContent = '';
            } else if (tagName === 'EMBED') {
                element.removeAttribute('src');
            } else if (tagName === 'OBJECT') {
                element.removeAttribute('data');
            }
            const parent = element.parentElement;
            if (parent && parent !== _document.body && parent !== _document.documentElement) {
                if (Utils.isContainerEmpty(parent, true) && Utils.isSuspiciousAdContainer(parent)) {
                    ResidualCleaner.checkAndCleanup(parent);
                }
            }
            if (element.parentNode) element.parentNode.removeChild(element);
            ProcessedElementsCache.markAsProcessed(element);
        }
    };

    const TAG_HANDLERS = {
        SCRIPT: {
            srcAttr: 'src',
            inlineContent: true,
            check: function(element, moduleKey, reason) {
                const contentIdentifier = Utils.getContentIdentifier(element);
                if (contentIdentifier && !currentConfig.whitelist.has(contentIdentifier)) {
                    LogManager.add(moduleKey, element, reason);
                    if (moduleKey === 'removeExternalScripts' || moduleKey === 'scriptBlacklistMode') {
                        ResourceCanceller.cancelResourceLoading(element);
                    } else if (moduleKey === 'removeInlineScripts' && !element.src) {
                        element.remove();
                    }
                    ProcessedElementsCache.markAsProcessed(element);
                    return true;
                }
                return false;
            }
        },
        IFRAME: {
            srcAttr: 'src',
            check: function(element, moduleKey) {
                if (moduleKey !== 'interceptThirdParty') return false;
                const url = element.src;
                if (url && shouldBlockResource(url)) {
                    LogManager.add(moduleKey, element, {
                        type: 'THIRD_PARTY',
                        detail: `IFRAME: ${Utils.truncateString(url, 500)}`
                    });
                    ResourceCanceller.cancelResourceLoading(element);
                    ProcessedElementsCache.markAsProcessed(element);
                    return true;
                }
                return false;
            }
        },
        IMG: {
            srcAttr: 'src',
            dataSrcAttr: 'data-src',
            check: function(element, moduleKey) {
                if (moduleKey !== 'interceptThirdParty') return false;
                const url = element.src || element.getAttribute('data-src');
                if (url && shouldBlockResource(url)) {
                    LogManager.add(moduleKey, element, {
                        type: 'THIRD_PARTY',
                        detail: `IMG: ${Utils.truncateString(url, 500)}`
                    });
                    ResourceCanceller.cancelResourceLoading(element);
                    ProcessedElementsCache.markAsProcessed(element);
                    return true;
                }
                return false;
            }
        },
        LINK: {
            srcAttr: 'href',
            check: function(element, moduleKey) {
                if (moduleKey !== 'interceptThirdParty') return false;
                const url = element.href;
                if (url && element.rel === 'stylesheet' && shouldBlockResource(url)) {
                    LogManager.add(moduleKey, element, {
                        type: 'THIRD_PARTY',
                        detail: `LINK: ${Utils.truncateString(url, 500)}`
                    });
                    ResourceCanceller.cancelResourceLoading(element);
                    ProcessedElementsCache.markAsProcessed(element);
                    return true;
                }
                return false;
            }
        },
        EMBED: {
            srcAttr: 'src',
            check: function(element, moduleKey) {
                if (moduleKey !== 'interceptThirdParty') return false;
                const url = element.src;
                if (url && shouldBlockResource(url)) {
                    LogManager.add(moduleKey, element, {
                        type: 'THIRD_PARTY',
                        detail: `EMBED: ${Utils.truncateString(url, 500)}`
                    });
                    ResourceCanceller.cancelResourceLoading(element);
                    ProcessedElementsCache.markAsProcessed(element);
                    return true;
                }
                return false;
            }
        },
        OBJECT: {
            srcAttr: 'data',
            check: function(element, moduleKey) {
                if (moduleKey !== 'interceptThirdParty') return false;
                const url = element.data;
                if (url && shouldBlockResource(url)) {
                    LogManager.add(moduleKey, element, {
                        type: 'THIRD_PARTY',
                        detail: `OBJECT: ${Utils.truncateString(url, 500)}`
                    });
                    ResourceCanceller.cancelResourceLoading(element);
                    ProcessedElementsCache.markAsProcessed(element);
                    return true;
                }
                return false;
            }
        },
        A: {
            srcAttr: 'href',
            check: function(element, moduleKey) {
                if (moduleKey !== 'interceptThirdParty') return false;
                const url = element.href;
                if (url && shouldBlockResource(url)) {
                    LogManager.add(moduleKey, element, {
                        type: 'THIRD_PARTY',
                        detail: `A: ${Utils.truncateString(url, 500)}`
                    });
                    element.href = 'javascript:void(0)';
                    element.addEventListener('click', function(e) {
                        e.preventDefault();
                        e.stopPropagation();
                        return false;
                    }, true);
                    ProcessedElementsCache.markAsProcessed(element);
                    return true;
                }
                return false;
            }
        }
    };

    class BaseModule {
        constructor(moduleKey) {
            this.moduleKey = moduleKey;
            this.enabled = false;
            this.observer = null;
        }
        init() {
            if (currentConfig.modules[this.moduleKey]) this.enable();
            else this.disable();
        }
        enable() {
            if (this.enabled) return;
            this.enabled = true;
            this.onEnable();
        }
        disable() {
            if (!this.enabled) return;
            this.enabled = false;
            this.onDisable();
        }
        onEnable() {}
        onDisable() {
            if (this.observer) {
                this.observer.disconnect();
                this.observer = null;
            }
        }
        checkElement(element) {
            if (!Utils.shouldInterceptByModule(element, this.moduleKey)) return false;
            return this._checkElement(element);
        }
        _checkElement(element) {
            throw new Error('_checkElement must be implemented by subclass');
        }
    }

    class RemoveInlineScriptsModule extends BaseModule {
        constructor() {
            super('removeInlineScripts');
            this.attributeObserver = null;
        }
        onEnable() {
            _document.querySelectorAll('script:not([src])').forEach(script => this.checkElement(script));
            if (currentConfig.inlineScriptStrictMode) {
                _document.querySelectorAll('*').forEach(el => this.sanitizeInlineEventAttributes(el));
            }
            this.observer = new _MutationObserver(mutations => {
                for (const mutation of mutations) {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType === 1) {
                            if (currentConfig.inlineScriptStrictMode) this.sanitizeInlineEventAttributes(node);
                            if (node.tagName === 'SCRIPT' && !node.src) this.checkElement(node);
                        }
                    }
                }
            });
            this.observer.observe(_document.documentElement, {
                childList: true,
                subtree: true
            });
            if (currentConfig.inlineScriptStrictMode) {
                this.attributeObserver = new _MutationObserver(mutations => {
                    for (const mutation of mutations) {
                        if (mutation.type === 'attributes' && mutation.target.nodeType === 1) {
                            const attrName = mutation.attributeName;
                            const target = mutation.target;
                            if (attrName && attrName.toLowerCase().startsWith('on')) {
                                const value = target.getAttribute(attrName);
                                if (value && typeof value === 'string' && value.trim() !== '') {
                                    const reason = {
                                        type: '内联事件',
                                        detail: `属性: ${attrName}="${value}"`
                                    };
                                    if (!Whitelisting.isReasonWhitelisted(reason)) {
                                        LogManager.add(this.moduleKey, target, reason);
                                        target.removeAttribute(attrName);
                                        ProcessedElementsCache.markAsProcessed(target);
                                    }
                                }
                            } else if (attrName === 'href' || attrName === 'src' || attrName === 'action' || attrName === 'data' || attrName === 'formaction') {
                                const value = target.getAttribute(attrName);
                                if (value && typeof value === 'string' && value.trim().toLowerCase().startsWith('javascript:')) {
                                    const reason = {
                                        type: 'javascript URL',
                                        detail: `${attrName}="${value}"`
                                    };
                                    if (!Whitelisting.isReasonWhitelisted(reason)) {
                                        LogManager.add(this.moduleKey, target, reason);
                                        target.removeAttribute(attrName);
                                        ProcessedElementsCache.markAsProcessed(target);
                                    }
                                }
                            }
                        }
                    }
                });
                this.attributeObserver.observe(_document.documentElement, {
                    attributes: true,
                    subtree: true,
                    attributeFilter: ['onclick', 'onload', 'onerror', 'onmouseover', 'onfocus', 'onblur', 'onsubmit', 'onchange', 'onkeydown', 'onkeyup', 'ontouchstart', 'ontouchend', 'ontouchmove', 'oncontextmenu', 'href', 'src', 'action', 'data', 'formaction']
                });
            }
        }
        onDisable() {
            super.onDisable();
            if (this.attributeObserver) {
                this.attributeObserver.disconnect();
                this.attributeObserver = null;
            }
        }
        _checkElement(element) {
            if (element.tagName === 'SCRIPT' && !element.src) {
                return TAG_HANDLERS.SCRIPT.check(element, this.moduleKey, {
                    type: '内嵌脚本移除',
                    detail: `内容: ${Utils.truncateString(element.textContent, 500)}`
                });
            }
            return false;
        }
        sanitizeInlineEventAttributes(element) {
            if (!Utils.isElement(element) || ProcessedElementsCache.isProcessed(element)) return false;
            let modified = false;
            const attrs = element.attributes;
            if (attrs) {
                for (let i = attrs.length - 1; i >= 0; i--) {
                    const attr = attrs[i];
                    if (attr.name.toLowerCase().startsWith('on') && typeof attr.value === 'string' && attr.value.trim() !== '') {
                        const reason = {
                            type: '内联事件',
                            detail: `属性: ${attr.name}="${attr.value}"`
                        };
                        if (!Whitelisting.isReasonWhitelisted(reason)) {
                            LogManager.add(this.moduleKey, element, reason);
                            element.removeAttribute(attr.name);
                            modified = true;
                        }
                    }
                }
            }
            const dangerousAttrs = ['href', 'src', 'action', 'data', 'formaction'];
            dangerousAttrs.forEach(attr => {
                const val = element.getAttribute(attr);
                if (val && typeof val === 'string' && val.trim().toLowerCase().startsWith('javascript:')) {
                    const reason = {
                        type: 'javascript URL',
                        detail: `${attr}="${val}"`
                    };
                    if (!Whitelisting.isReasonWhitelisted(reason)) {
                        LogManager.add(this.moduleKey, element, reason);
                        element.removeAttribute(attr);
                        modified = true;
                    }
                }
            });
            if (modified) ProcessedElementsCache.markAsProcessed(element);
            return modified;
        }
        updateStrictMode() {
            if (this.enabled) {
                this.onDisable();
                this.onEnable();
            }
        }
    }

    class RemoveExternalScriptsModule extends BaseModule {
        constructor() {
            super('removeExternalScripts');
        }
        onEnable() {
            _document.querySelectorAll('script[src]').forEach(script => this.checkElement(script));
            this.observer = new _MutationObserver(mutations => {
                for (const mutation of mutations) {
                    if (mutation.type === 'childList') {
                        for (const node of mutation.addedNodes) {
                            if (node.nodeType === 1) {
                                if (node.tagName === 'SCRIPT' && node.src) {
                                    this.checkElement(node);
                                }
                                if (node.querySelectorAll) {
                                    node.querySelectorAll('script[src]').forEach(s => this.checkElement(s));
                                }
                            }
                        }
                    } else if (mutation.type === 'attributes' && mutation.attributeName === 'src') {
                        const el = mutation.target;
                        if (el.tagName === 'SCRIPT' && el.src) {
                            this.checkElement(el);
                        }
                    }
                }
            });
            this.observer.observe(_document.documentElement, {
                childList: true,
                subtree: true,
                attributes: true,
                attributeFilter: ['src']
            });
        }
        _checkElement(element) {
            if (element.tagName === 'SCRIPT' && element.src) {
                return TAG_HANDLERS.SCRIPT.check(element, this.moduleKey, {
                    type: '外联脚本移除',
                    detail: `SRC: ${Utils.truncateString(element.src, 500)}`
                });
            }
            return false;
        }
    }

    class ScriptBlacklistModeModule extends BaseModule {
        constructor() {
            super('scriptBlacklistMode');
        }
        onEnable() {
            _document.querySelectorAll('script').forEach(script => this.checkElement(script));
            this.observer = new _MutationObserver(mutations => {
                for (const mutation of mutations) {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType === 1 && node.tagName === 'SCRIPT') this.checkElement(node);
                    }
                }
            });
            this.observer.observe(_document.documentElement, {
                childList: true,
                subtree: true
            });
        }
        _checkElement(element) {
            if (element.tagName !== 'SCRIPT') return false;
            const blacklist = currentConfig.scriptBlacklist;
            if (!blacklist || blacklist.size === 0) return false;
            const scriptContent = element.textContent;
            const scriptSrc = element.src;
            let matched = false,
                matchedKeyword = '';
            for (const keyword of blacklist) {
                if (!keyword) continue;
                if (scriptSrc && scriptSrc.includes(keyword)) {
                    matched = true;
                    matchedKeyword = keyword;
                    break;
                }
                if (!scriptSrc && scriptContent && scriptContent.includes(keyword)) {
                    matched = true;
                    matchedKeyword = keyword;
                    break;
                }
            }
            if (matched) {
                LogManager.add(this.moduleKey, element, {
                    type: 'SCRIPT_BLACKLIST',
                    detail: `命中关键词: ${matchedKeyword} - ${scriptSrc ? `SRC: ${Utils.truncateString(scriptSrc, 500)}` : `内嵌: ${Utils.truncateString(scriptContent, 500)}`}`
                });
                ResourceCanceller.cancelResourceLoading(element);
                ProcessedElementsCache.markAsProcessed(element);
                return true;
            }
            return false;
        }
    }

    class ThirdPartyInterceptionModule extends BaseModule {
        constructor() {
            super('interceptThirdParty');
            this.originalSetAttribute = null;
            this.originalFetch = null;
            this.originalXhrOpen = null;
            this.originalXhrSend = null;
            this.originalInnerHTMLSetter = null;
            this.originalInsertAdjacentHTML = null;
            this.originalCreateElement = null;
            this.originalAppendChild = null;
            this.originalInsertBefore = null;
            this.originalAttachShadow = null;
            this.restoredFns = [];
        }
        onEnable() {
            this.stopInterception();
            this.setupProxyInterception();
            this.setupNetworkInterception();
            this.setupMutationFallback();
            this.setupHTMLInterception();
            this.patchAttachShadow();
            if (currentConfig.thirdPartyStrictMethod) {
                this.setupStrictDOMInterception();
            }
        }
        onDisable() {
            this.stopInterception();
        }
        stopInterception() {
            this.restoredFns.forEach(fn => {
                try {
                    fn();
                } catch (e) {}
            });
            this.restoredFns = [];
            if (this.observer) {
                this.observer.disconnect();
                this.observer = null;
            }
            this.originalSetAttribute = null;
            this.originalFetch = null;
            this.originalXhrOpen = null;
            this.originalXhrSend = null;
            this.originalInnerHTMLSetter = null;
            this.originalInsertAdjacentHTML = null;
            this.originalCreateElement = null;
            this.originalAppendChild = null;
            this.originalInsertBefore = null;
            this.originalAttachShadow = null;
        }
        setupProxyInterception() {
            const self = this;
            const logAndCancel = (element, attr, url, tagName) => {
                LogManager.add(this.moduleKey, element, {
                    type: 'THIRD_PARTY',
                    detail: `${tagName}: ${Utils.truncateString(url, 500)}`
                });
                ResourceCanceller.cancelResourceLoading(element);
                ProcessedElementsCache.markAsProcessed(element);
            };
            try {
                this.originalSetAttribute = _Element.prototype.setAttribute;
                const setAttributeProxy = new _Proxy(_Element.prototype.setAttribute, {
                    apply(target, thisArg, args) {
                        const [name, value] = args;
                        const tagName = thisArg.tagName;
                        const shouldIntercept = TAG_HANDLERS[tagName] && TAG_HANDLERS[tagName].srcAttr === name && shouldBlockResource(value);
                        if (shouldIntercept) {
                            logAndCancel(thisArg, name, value, tagName);
                            return;
                        }
                        return Reflect.apply(target, thisArg, args);
                    }
                });
                _Element.prototype.setAttribute = setAttributeProxy;
                this.restoredFns.push(() => {
                    if (_Element.prototype.setAttribute === setAttributeProxy) {
                        _Element.prototype.setAttribute = self.originalSetAttribute;
                    }
                });
            } catch (e) {}
            for (const tagName in TAG_HANDLERS) {
                const proto = _globals[`HTML${tagName}Element`]?.prototype;
                if (!proto) continue;
                const srcAttr = TAG_HANDLERS[tagName].srcAttr;
                try {
                    const desc = Object.getOwnPropertyDescriptor(proto, srcAttr);
                    if (desc && desc.set && desc.configurable !== false) {
                        const originalSetter = desc.set;
                        const setterProxy = new _Proxy(originalSetter, {
                            apply(target, thisArg, args) {
                                const [value] = args;
                                const shouldIntercept = shouldBlockResource(value);
                                if (shouldIntercept) {
                                    logAndCancel(thisArg, srcAttr, value, tagName);
                                    return;
                                }
                                return Reflect.apply(target, thisArg, args);
                            }
                        });
                        Object.defineProperty(proto, srcAttr, {
                            set: setterProxy,
                            get: desc.get,
                            configurable: true,
                            enumerable: true
                        });
                        this.restoredFns.push(() => {
                            if (Object.getOwnPropertyDescriptor(proto, srcAttr)?.set === setterProxy) {
                                Object.defineProperty(proto, srcAttr, desc);
                            }
                        });
                    }
                } catch (e) {}
            }
        }
        setupNetworkInterception() {
            const self = this;
            try {
                this.originalFetch = _fetch;
                const fetchProxy = new _Proxy(_fetch, {
                    apply(target, thisArg, args) {
                        const input = args[0];
                        const url = typeof input === 'string' ? input : input?.url;
                        if (url && shouldBlockResource(url)) {
                            LogManager.add(self.moduleKey, null, {
                                type: 'THIRD_PARTY',
                                detail: `FETCH: ${Utils.truncateString(url, 500)}`
                            });
                            return Promise.reject(new Error('广告拦截器:拦截第三方请求'));
                        }
                        return Reflect.apply(target, thisArg, args);
                    }
                });
                _globals.fetch = fetchProxy;
                this.restoredFns.push(() => {
                    if (_globals.fetch === fetchProxy) {
                        _globals.fetch = self.originalFetch;
                    }
                });
            } catch (e) {}
            try {
                this.originalXhrOpen = _XMLHttpRequest.prototype.open;
                this.originalXhrSend = _XMLHttpRequest.prototype.send;
                const openProxy = new _Proxy(_XMLHttpRequest.prototype.open, {
                    apply(target, thisArg, args) {
                        const method = args[0];
                        const url = args[1];
                        if (url && shouldBlockResource(url)) {
                            LogManager.add(self.moduleKey, null, {
                                type: 'THIRD_PARTY',
                                detail: `XHR: ${Utils.truncateString(url, 500)}`
                            });
                            thisArg._adblockBlocked = true;
                            return;
                        }
                        thisArg._adblockBlocked = false;
                        return Reflect.apply(target, thisArg, args);
                    }
                });
                const sendProxy = new _Proxy(_XMLHttpRequest.prototype.send, {
                    apply(target, thisArg, args) {
                        if (thisArg._adblockBlocked) return;
                        return Reflect.apply(target, thisArg, args);
                    }
                });
                _XMLHttpRequest.prototype.open = openProxy;
                _XMLHttpRequest.prototype.send = sendProxy;
                this.restoredFns.push(() => {
                    if (_XMLHttpRequest.prototype.open === openProxy) {
                        _XMLHttpRequest.prototype.open = self.originalXhrOpen;
                        _XMLHttpRequest.prototype.send = self.originalXhrSend;
                    }
                });
            } catch (e) {}
        }
        setupMutationFallback() {
            const self = this;
            this.observer = new _MutationObserver((mutations) => {
                for (const mutation of mutations) {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType !== 1) continue;
                        const tagName = node.tagName;
                        if (TAG_HANDLERS[tagName]) {
                            const srcAttr = TAG_HANDLERS[tagName].srcAttr;
                            const dataSrcAttr = TAG_HANDLERS[tagName].dataSrcAttr;
                            const value = node[srcAttr] || node.getAttribute(srcAttr) || (dataSrcAttr && node.getAttribute(dataSrcAttr));
                            if (value && shouldBlockResource(value)) {
                                LogManager.add(self.moduleKey, node, {
                                    type: 'THIRD_PARTY',
                                    detail: `${tagName}: ${Utils.truncateString(value, 500)}`
                                });
                                ResourceCanceller.cancelResourceLoading(node);
                                ProcessedElementsCache.markAsProcessed(node);
                            }
                        }
                        if (node.shadowRoot) {
                            self.observeShadowRoot(node.shadowRoot);
                        }
                    }
                }
            });
            this.observer.observe(_document.documentElement, {
                childList: true,
                subtree: true
            });
        }
        observeShadowRoot(shadowRoot) {
            if (!shadowRoot || shadowRoot._adblockObserved) return;
            shadowRoot._adblockObserved = true;
            const self = this;
            const observer = new _MutationObserver((mutations) => {
                for (const mutation of mutations) {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType !== 1) continue;
                        const tagName = node.tagName;
                        if (TAG_HANDLERS[tagName]) {
                            const srcAttr = TAG_HANDLERS[tagName].srcAttr;
                            const dataSrcAttr = TAG_HANDLERS[tagName].dataSrcAttr;
                            const value = node[srcAttr] || node.getAttribute(srcAttr) || (dataSrcAttr && node.getAttribute(dataSrcAttr));
                            if (value && shouldBlockResource(value)) {
                                LogManager.add(self.moduleKey, node, {
                                    type: 'THIRD_PARTY',
                                    detail: `Shadow DOM ${tagName}: ${Utils.truncateString(value, 500)}`
                                });
                                ResourceCanceller.cancelResourceLoading(node);
                                ProcessedElementsCache.markAsProcessed(node);
                            }
                        }
                        if (node.shadowRoot) this.observeShadowRoot(node.shadowRoot);
                    }
                }
            });
            observer.observe(shadowRoot, {
                childList: true,
                subtree: true
            });
            this.restoredFns.push(() => {
                observer.disconnect();
                delete shadowRoot._adblockObserved;
            });
        }
        patchAttachShadow() {
            const self = this;
            const proto = _Element.prototype;
            try {
                if (proto.attachShadow && !proto._adblockOriginalAttachShadow) {
                    proto._adblockOriginalAttachShadow = proto.attachShadow;
                    this.originalAttachShadow = proto.attachShadow;
                    proto.attachShadow = function(init) {
                        const shadow = proto._adblockOriginalAttachShadow.call(this, init);
                        self.observeShadowRoot(shadow);
                        const originalInnerHTMLDescriptor = Object.getOwnPropertyDescriptor(shadow, 'innerHTML');
                        if (originalInnerHTMLDescriptor && originalInnerHTMLDescriptor.set && originalInnerHTMLDescriptor.configurable) {
                            const hasGetter = typeof originalInnerHTMLDescriptor.get === 'function';
                            Object.defineProperty(shadow, 'innerHTML', {
                                get: hasGetter ? function() {
                                    return originalInnerHTMLDescriptor.get.call(this);
                                } : undefined,
                                set: function(value) {
                                    const filtered = self.filterHTMLString(value);
                                    originalInnerHTMLDescriptor.set.call(this, filtered);
                                },
                                configurable: true
                            });
                        }
                        return shadow;
                    };
                    this.restoredFns.push(() => {
                        if (proto._adblockOriginalAttachShadow) {
                            proto.attachShadow = proto._adblockOriginalAttachShadow;
                            delete proto._adblockOriginalAttachShadow;
                        }
                    });
                }
            } catch (e) {}
        }
        filterHTMLString(html) {
            if (typeof html !== 'string' || !html.includes('<')) return html;
            const quickRegex = /<(?:script|link|img|iframe|embed|object|a|form|base)[^>]*(?:src|href|data|action)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
            let needsFullParse = false;
            let match;
            while ((match = quickRegex.exec(html)) !== null) {
                if (shouldBlockResource(match[1])) {
                    needsFullParse = true;
                    break;
                }
            }
            if (!needsFullParse) return html;
            try {
                const parser = new _globals.DOMParser();
                const doc = parser.parseFromString(html, 'text/html');
                const tagsToCheck = ['script', 'link', 'img', 'iframe', 'embed', 'object', 'a', 'form', 'base'];
                const elements = doc.querySelectorAll(tagsToCheck.join(','));
                elements.forEach(el => {
                    const tagName = el.tagName.toLowerCase();
                    let urlAttr = '';
                    if (['script', 'img', 'iframe', 'embed'].includes(tagName)) urlAttr = 'src';
                    else if (['link', 'a', 'base'].includes(tagName)) urlAttr = 'href';
                    else if (tagName === 'object') urlAttr = 'data';
                    else if (tagName === 'form') urlAttr = 'action';
                    if (!urlAttr) return;
                    const url = el.getAttribute(urlAttr);
                    if (!url) return;
                    if (tagName === 'link') {
                        const rel = el.getAttribute('rel');
                        if (!rel || !rel.includes('stylesheet')) return;
                    }
                    if (shouldBlockResource(url)) {
                        const detail = `阻止: ${tagName.toUpperCase()} ${Utils.truncateString(url, 500)}`;
                        LogManager.add(this.moduleKey, null, {
                            type: 'THIRD_PARTY_HTML_INJECTION',
                            detail: detail
                        });
                        if (tagName === 'img') {
                            el.setAttribute(urlAttr, 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');
                            el.removeAttribute('srcset');
                            el.removeAttribute('data-src');
                        } else if (tagName === 'a' || tagName === 'form') {
                            el.removeAttribute(urlAttr);
                        } else {
                            el.remove();
                        }
                    }
                });
                return doc.body.innerHTML;
            } catch (e) {
                return html;
            }
        }
        setupHTMLInterception() {
            const self = this;
            try {
                const innerHTMLDesc = Object.getOwnPropertyDescriptor(_Element.prototype, 'innerHTML');
                if (innerHTMLDesc && innerHTMLDesc.set) {
                    this.originalInnerHTMLSetter = innerHTMLDesc.set;
                    const newSetter = function(value) {
                        const filtered = self.filterHTMLString(value);
                        return self.originalInnerHTMLSetter.call(this, filtered);
                    };
                    Object.defineProperty(_Element.prototype, 'innerHTML', {
                        set: newSetter,
                        get: innerHTMLDesc.get,
                        configurable: true
                    });
                    this.restoredFns.push(() => {
                        if (Object.getOwnPropertyDescriptor(_Element.prototype, 'innerHTML')?.set === newSetter) {
                            Object.defineProperty(_Element.prototype, 'innerHTML', innerHTMLDesc);
                        }
                    });
                }
            } catch (e) {}
            try {
                this.originalInsertAdjacentHTML = _Element.prototype.insertAdjacentHTML;
                const newInsertAdjacentHTML = function(position, html) {
                    const filtered = self.filterHTMLString(html);
                    return self.originalInsertAdjacentHTML.call(this, position, filtered);
                };
                _Element.prototype.insertAdjacentHTML = newInsertAdjacentHTML;
                this.restoredFns.push(() => {
                    if (_Element.prototype.insertAdjacentHTML === newInsertAdjacentHTML) {
                        _Element.prototype.insertAdjacentHTML = self.originalInsertAdjacentHTML;
                    }
                });
            } catch (e) {}
        }
        setupStrictDOMInterception() {
            const self = this;
            const checkAndBlockElement = (element) => {
                if (!element || !Utils.isElement(element)) return false;
                const tagName = element.tagName;
                if (TAG_HANDLERS[tagName]) {
                    const handler = TAG_HANDLERS[tagName];
                    const srcAttr = handler.srcAttr;
                    const dataSrcAttr = handler.dataSrcAttr;
                    const url = element[srcAttr] || element.getAttribute(srcAttr) || (dataSrcAttr && element.getAttribute(dataSrcAttr));
                    if (url && shouldBlockResource(url)) {
                        LogManager.add(self.moduleKey, element, {
                            type: 'THIRD_PARTY',
                            detail: `严格拦截: ${tagName}: ${Utils.truncateString(url, 500)}`
                        });
                        ResourceCanceller.cancelResourceLoading(element);
                        ProcessedElementsCache.markAsProcessed(element);
                        return true;
                    }
                }
                return false;
            };
            try {
                this.originalAppendChild = _Node.prototype.appendChild;
                const newAppendChild = function(child) {
                    if (child && Utils.isElement(child)) {
                        if (checkAndBlockElement(child)) {
                            return child;
                        }
                    }
                    return self.originalAppendChild.call(this, child);
                };
                _Node.prototype.appendChild = newAppendChild;
                this.restoredFns.push(() => {
                    if (_Node.prototype.appendChild === newAppendChild) {
                        _Node.prototype.appendChild = self.originalAppendChild;
                    }
                });
            } catch (e) {}
            try {
                this.originalInsertBefore = _Node.prototype.insertBefore;
                const newInsertBefore = function(newNode, referenceNode) {
                    if (newNode && Utils.isElement(newNode)) {
                        if (checkAndBlockElement(newNode)) {
                            return newNode;
                        }
                    }
                    return self.originalInsertBefore.call(this, newNode, referenceNode);
                };
                _Node.prototype.insertBefore = newInsertBefore;
                this.restoredFns.push(() => {
                    if (_Node.prototype.insertBefore === newInsertBefore) {
                        _Node.prototype.insertBefore = self.originalInsertBefore;
                    }
                });
            } catch (e) {}
        }
        updateStrictMode() {
            urlCache.clear();
        }
        updateStrictMethod() {
            if (this.enabled) {
                this.onDisable();
                this.onEnable();
            }
        }
        _checkElement(element) {
            const tagName = element.tagName;
            if (!TAG_HANDLERS[tagName]) return false;
            const handler = TAG_HANDLERS[tagName];
            const srcAttr = handler.srcAttr;
            const dataSrcAttr = handler.dataSrcAttr;
            const value = element[srcAttr] || element.getAttribute(srcAttr) || (dataSrcAttr && element.getAttribute(dataSrcAttr));
            if (value && shouldBlockResource(value)) {
                LogManager.add(this.moduleKey, element, {
                    type: 'THIRD_PARTY',
                    detail: `${tagName}: ${Utils.truncateString(value, 500)}`
                });
                ResourceCanceller.cancelResourceLoading(element);
                ProcessedElementsCache.markAsProcessed(element);
                return true;
            }
            return false;
        }
    }

    const DynamicScriptInterceptor = {
        _enabled: false,
        originalEval: null,
        originalFunction: null,
        originalSetTimeout: null,
        originalSetInterval: null,
        originalClearTimeout: null,
        originalClearInterval: null,
        originalRequestAnimationFrame: null,
        originalCancelAnimationFrame: null,
        originalDocumentWrite: null,
        originalDocumentWriteln: null,
        init() {
            if (currentConfig.modules.blockDynamicScripts) this.enable();
        },
        enable() {
            if (this._enabled) return;
            this._enabled = true;
            this.originalEval = _globals.eval;
            this.originalFunction = _globals.Function;
            this.originalSetTimeout = _globals.setTimeout;
            this.originalSetInterval = _globals.setInterval;
            this.originalClearTimeout = _globals.clearTimeout;
            this.originalClearInterval = _globals.clearInterval;
            this.originalRequestAnimationFrame = _globals.requestAnimationFrame;
            this.originalCancelAnimationFrame = _globals.cancelAnimationFrame;
            this.originalDocumentWrite = _document.write;
            this.originalDocumentWriteln = _document.writeln;
            const self = this;
            try {
                _globals.eval = function(code) {
                    if (typeof code === 'string') {
                        if (Whitelisting.isCodeWhitelisted(code, 'EVAL')) {
                            return self.originalEval.call(this, code);
                        }
                        const contentIdentifier = Utils.getContentIdentifier(null, {
                            type: 'EVAL',
                            detail: code
                        });
                        if (contentIdentifier && !currentConfig.whitelist.has(contentIdentifier)) {
                            LogManager.add('blockDynamicScripts', null, {
                                type: 'EVAL',
                                detail: Utils.truncateString(code, 500),
                                rawDetail: code
                            });
                            return undefined;
                        }
                    }
                    return self.originalEval.call(this, code);
                };
            } catch (e) {}
            try {
                _globals.Function = new _Proxy(this.originalFunction, {
                    construct(target, args, newTarget) {
                        const code = (args.length > 0) ? String(args[args.length - 1]) : '';
                        if (typeof code === 'string') {
                            if (Whitelisting.isCodeWhitelisted(code, 'FUNCTION_CONSTRUCTOR')) {
                                return Reflect.construct(target, args, newTarget || target);
                            }
                            const contentIdentifier = Utils.getContentIdentifier(null, {
                                type: 'FUNCTION_CONSTRUCTOR',
                                detail: code
                            });
                            if (contentIdentifier && !currentConfig.whitelist.has(contentIdentifier)) {
                                LogManager.add('blockDynamicScripts', null, {
                                    type: 'FUNCTION_CONSTRUCTOR',
                                    detail: Utils.truncateString(code, 500),
                                    rawDetail: code
                                });
                                return Reflect.construct(target, ['return;'], newTarget || target);
                            }
                        }
                        return Reflect.construct(target, args, newTarget || target);
                    },
                    apply(target, thisArg, args) {
                        const code = (args.length > 0) ? String(args[args.length - 1]) : '';
                        if (typeof code === 'string') {
                            if (Whitelisting.isCodeWhitelisted(code, 'FUNCTION_CONSTRUCTOR')) {
                                return Reflect.apply(target, thisArg, args);
                            }
                            const contentIdentifier = Utils.getContentIdentifier(null, {
                                type: 'FUNCTION_CONSTRUCTOR',
                                detail: code
                            });
                            if (contentIdentifier && !currentConfig.whitelist.has(contentIdentifier)) {
                                LogManager.add('blockDynamicScripts', null, {
                                    type: 'FUNCTION_CONSTRUCTOR',
                                    detail: Utils.truncateString(code, 500),
                                    rawDetail: code
                                });
                                return Reflect.apply(target, thisArg, ['return;']);
                            }
                        }
                        return Reflect.apply(target, thisArg, args);
                    }
                });
            } catch (e) {}
            const checkCallback = (callback, type) => {
                if (typeof callback === 'string') {
                    if (Whitelisting.isCodeWhitelisted(callback, type)) {
                        return {
                            blocked: false
                        };
                    }
                    const contentIdentifier = Utils.getContentIdentifier(null, {
                        type: type,
                        detail: callback
                    });
                    if (contentIdentifier && !currentConfig.whitelist.has(contentIdentifier)) {
                        LogManager.add('blockDynamicScripts', null, {
                            type: type,
                            detail: Utils.truncateString(callback, 500),
                            rawDetail: callback
                        });
                        return {
                            blocked: true
                        };
                    }
                    return {
                        blocked: false
                    };
                } else if (typeof callback === 'function') {
                    const funcStr = callback.toString();
                    if (funcStr.includes('eval') || funcStr.includes('Function')) {
                        const contentIdentifier = Utils.getContentIdentifier(null, {
                            type: type,
                            detail: funcStr
                        });
                        if (contentIdentifier && !currentConfig.whitelist.has(contentIdentifier)) {
                            LogManager.add('blockDynamicScripts', null, {
                                type: type,
                                detail: Utils.truncateString(funcStr, 500),
                                rawDetail: funcStr
                            });
                            return {
                                blocked: true
                            };
                        }
                    }
                    return {
                        blocked: false
                    };
                }
                return {
                    blocked: false
                };
            };
            try {
                _globals.setTimeout = function(callback, delay, ...args) {
                    const result = checkCallback(callback, 'SETTIMEOUT');
                    if (result.blocked) {
                        return -1;
                    }
                    return self.originalSetTimeout.call(this, callback, delay, ...args);
                };
                _globals.setInterval = function(callback, delay, ...args) {
                    const result = checkCallback(callback, 'SETINTERVAL');
                    if (result.blocked) {
                        return -1;
                    }
                    return self.originalSetInterval.call(this, callback, delay, ...args);
                };
                _globals.clearTimeout = function(id) {
                    if (id === -1) return;
                    return self.originalClearTimeout.call(this, id);
                };
                _globals.clearInterval = function(id) {
                    if (id === -1) return;
                    return self.originalClearInterval.call(this, id);
                };
                _globals.requestAnimationFrame = function(callback) {
                    const result = checkCallback(callback, 'REQUESTANIMATIONFRAME');
                    if (result.blocked) {
                        return -1;
                    }
                    return self.originalRequestAnimationFrame.call(this, callback);
                };
                _globals.cancelAnimationFrame = function(id) {
                    if (id === -1) return;
                    return self.originalCancelAnimationFrame.call(this, id);
                };
            } catch (e) {}
            try {
                _document.write = function(...args) {
                    const content = args.join('');
                    if (typeof content === 'string') {
                        if (Whitelisting.isCodeWhitelisted(content, 'DOCUMENT_WRITE')) {
                            return self.originalDocumentWrite.apply(this, args);
                        }
                        const contentIdentifier = Utils.getContentIdentifier(null, {
                            type: 'DOCUMENT_WRITE',
                            detail: content
                        });
                        if (contentIdentifier && !currentConfig.whitelist.has(contentIdentifier)) {
                            LogManager.add('blockDynamicScripts', null, {
                                type: 'DOCUMENT_WRITE',
                                detail: Utils.truncateString(content, 500),
                                rawDetail: content
                            });
                            return;
                        }
                    }
                    return self.originalDocumentWrite.apply(this, args);
                };
                _document.writeln = function(...args) {
                    const content = args.join('');
                    if (typeof content === 'string') {
                        if (Whitelisting.isCodeWhitelisted(content, 'DOCUMENT_WRITELN')) {
                            return self.originalDocumentWriteln.apply(this, args);
                        }
                        const contentIdentifier = Utils.getContentIdentifier(null, {
                            type: 'DOCUMENT_WRITE',
                            detail: content
                        });
                        if (contentIdentifier && !currentConfig.whitelist.has(contentIdentifier)) {
                            LogManager.add('blockDynamicScripts', null, {
                                type: 'DOCUMENT_WRITE',
                                detail: Utils.truncateString(content, 500),
                                rawDetail: content
                            });
                            return;
                        }
                    }
                    return self.originalDocumentWriteln.apply(this, args);
                };
            } catch (e) {}
        },
        disable() {
            if (!this._enabled) return;
            this._enabled = false;
            try {
                _globals.eval = this.originalEval;
            } catch (e) {}
            try {
                _globals.Function = this.originalFunction;
            } catch (e) {}
            try {
                _globals.setTimeout = this.originalSetTimeout;
            } catch (e) {}
            try {
                _globals.setInterval = this.originalSetInterval;
            } catch (e) {}
            try {
                _globals.clearTimeout = this.originalClearTimeout;
            } catch (e) {}
            try {
                _globals.clearInterval = this.originalClearInterval;
            } catch (e) {}
            try {
                _globals.requestAnimationFrame = this.originalRequestAnimationFrame;
            } catch (e) {}
            try {
                _globals.cancelAnimationFrame = this.originalCancelAnimationFrame;
            } catch (e) {}
            try {
                _document.write = this.originalDocumentWrite;
            } catch (e) {}
            try {
                _document.writeln = this.originalDocumentWriteln;
            } catch (e) {}
            this.originalEval = this.originalFunction = this.originalSetTimeout = this.originalSetInterval = null;
            this.originalClearTimeout = this.originalClearInterval = this.originalRequestAnimationFrame = this.originalCancelAnimationFrame = null;
            this.originalDocumentWrite = this.originalDocumentWriteln = null;
        },
        check() {
            return false;
        }
    };

    const CSPModule = {
        init() {
            if (currentConfig.modules.manageCSP) this.applyCSP();
        },
        applyCSP() {
            if (!currentConfig.modules.manageCSP) return;
            const existingMeta = _document.querySelector('meta[http-equiv="Content-Security-Policy"]');
            if (existingMeta) existingMeta.remove();
            const enabledRules = currentConfig.cspRules.filter(rule => rule.enabled);
            if (enabledRules.length === 0) return;
            const directives = {};
            for (const rule of enabledRules) {
                const parts = rule.rule.split(/\s+/);
                const directive = parts[0];
                const values = parts.slice(1);
                if (!directives[directive]) directives[directive] = new _Set();
                values.forEach(value => directives[directive].add(value));
            }
            let policyString = '';
            for (const directive in directives) {
                if (directives.hasOwnProperty(directive)) {
                    const values = Array.from(directives[directive]).join(' ');
                    policyString += `${directive} ${values}; `;
                }
            }
            policyString = policyString.trim();
            if (!policyString) return;
            const injectCSP = () => {
                if (_document.head) {
                    if (_document.querySelector('meta[http-equiv="Content-Security-Policy"]')) return;
                    const meta = _document.createElement('meta');
                    meta.httpEquiv = "Content-Security-Policy";
                    meta.content = policyString;
                    _document.head.appendChild(meta);
                } else {
                    const observer = new _MutationObserver(() => {
                        if (_document.head) {
                            observer.disconnect();
                            if (_document.querySelector('meta[http-equiv="Content-Security-Policy"]')) return;
                            const meta = _document.createElement('meta');
                            meta.httpEquiv = "Content-Security-Policy";
                            meta.content = policyString;
                            _document.head.appendChild(meta);
                        }
                    });
                    observer.observe(_document.documentElement, {
                        childList: true,
                        subtree: true
                    });
                }
            };
            injectCSP();
        },
        removeCSP() {
            const existingMeta = _document.querySelector('meta[http-equiv="Content-Security-Policy"]');
            if (existingMeta) existingMeta.remove();
        },
        updateRule(ruleId, enabled) {
            const rule = currentConfig.cspRules.find(r => r.id === ruleId);
            if (rule) rule.enabled = enabled;
        }
    };

    function installRedirectBlocker() {
        let hasNotifiedUser = false;

        function showNotification(message) {
            if (window.via && typeof window.via.toast === 'function') {
                try {
                    window.via.toast(message);
                    return;
                } catch (e) {}
            }
            if (typeof GM_notification === 'function') {
                try {
                    GM_notification({
                        text: message,
                        title: '网页重定向',
                        timeout: 3000
                    });
                    return;
                } catch (e) {}
            }
        }

        function showBlockedNotification(silent = false) {
            if (silent) return;
            if (!hasNotifiedUser) {
                hasNotifiedUser = true;
                showNotification('已阻止跳转到其他网站');
                _setTimeout(() => {
                    hasNotifiedUser = false;
                }, 3000);
            }
        }

        function shouldBlockRedirect(href) {
            try {
                if (!href || href.startsWith('#') || href.startsWith('javascript:')) return false;
                const normalizedHref = href.startsWith('/') ? _location.origin + href : href.startsWith('//') ? _location.protocol + href : href;
                const targetUrl = new URL(normalizedHref, _location.href);
                const currentUrl = new URL(_location.href);
                if (targetUrl.hostname === currentUrl.hostname) return false;
                if (!currentConfig.thirdPartyStrictMode) {
                    const targetDomain = urlCache.getDomain(targetUrl.hostname);
                    const currentDomain = urlCache.getDomain(currentUrl.hostname);
                    if (targetDomain && currentDomain && targetDomain === currentDomain) return false;
                }
                return true;
            } catch (e) {
                return false;
            }
        }

        const original = {
            windowOpen: _globals.open,
            assign: _location.assign,
            replace: _location.replace,
            pushState: history.pushState,
            replaceState: history.replaceState
        };

        try {
            _globals.open = function(...args) {
                if (!currentConfig.redirectBlockerEnabled) return original.windowOpen.apply(this, args);
                if (shouldBlockRedirect(args[0])) {
                    showBlockedNotification();
                    return null;
                }
                return original.windowOpen.apply(this, args);
            };
        } catch (e) {}

        try {
            if (typeof original.assign === 'function') {
                _location.assign = function(url) {
                    if (!currentConfig.redirectBlockerEnabled) return original.assign.call(_location, url);
                    if (shouldBlockRedirect(url)) {
                        showBlockedNotification();
                        return;
                    }
                    return original.assign.call(_location, url);
                };
            }
            if (typeof original.replace === 'function') {
                _location.replace = function(url) {
                    if (!currentConfig.redirectBlockerEnabled) return original.replace.call(_location, url);
                    if (shouldBlockRedirect(url)) {
                        showBlockedNotification();
                        return;
                    }
                    return original.replace.call(_location, url);
                };
            }
        } catch (e) {}

        try {
            history.pushState = function(state, title, url) {
                if (!currentConfig.redirectBlockerEnabled) return original.pushState.apply(this, arguments);
                if (shouldBlockRedirect(url)) {
                    showBlockedNotification();
                    return;
                }
                return original.pushState.apply(this, arguments);
            };
            history.replaceState = function(state, title, url) {
                if (!currentConfig.redirectBlockerEnabled) return original.replaceState.apply(this, arguments);
                if (shouldBlockRedirect(url)) {
                    showBlockedNotification();
                    return;
                }
                return original.replaceState.apply(this, arguments);
            };
        } catch (e) {}

        try {
            const anchorProto = HTMLAnchorElement.prototype;
            const origAnchorHrefDesc = Object.getOwnPropertyDescriptor(anchorProto, 'href');
            if (origAnchorHrefDesc && origAnchorHrefDesc.set && origAnchorHrefDesc.configurable) {
                Object.defineProperty(anchorProto, 'href', {
                    configurable: true,
                    enumerable: true,
                    get() {
                        return origAnchorHrefDesc.get.call(this);
                    },
                    set(v) {
                        if (!currentConfig.redirectBlockerEnabled) return origAnchorHrefDesc.set.call(this, v);
                        if (shouldBlockRedirect(v)) {
                            const origSetAttr = _Element.prototype.setAttribute;
                            origSetAttr.call(this, 'data-blocked-redirect', v);
                            return;
                        }
                        return origAnchorHrefDesc.set.call(this, v);
                    }
                });
            }
        } catch (e) {}

        try {
            const formProto = HTMLFormElement.prototype;
            const origSubmit = formProto.submit;
            formProto.submit = function() {
                if (!currentConfig.redirectBlockerEnabled) return origSubmit.apply(this, arguments);
                const action = this.getAttribute('action');
                if (!action || action.trim() === '') {
                    if (this.hasAttribute('data-blocked-redirect-target') || this.hasAttribute('data-blocked-action')) {
                        showBlockedNotification();
                        return;
                    }
                    return origSubmit.apply(this, arguments);
                }
                if (shouldBlockRedirect(action)) {
                    showBlockedNotification();
                    return;
                }
                return origSubmit.apply(this, arguments);
            };
            const actionDesc = Object.getOwnPropertyDescriptor(formProto, 'action');
            if (actionDesc && actionDesc.set && actionDesc.configurable) {
                Object.defineProperty(formProto, 'action', {
                    configurable: true,
                    enumerable: true,
                    get() {
                        return actionDesc.get.call(this);
                    },
                    set(val) {
                        if (!currentConfig.redirectBlockerEnabled) return actionDesc.set.call(this, val);
                        if (!val || val.trim() === '') return actionDesc.set.call(this, val);
                        if (shouldBlockRedirect(val)) {
                            const origSetAttr2 = _Element.prototype.setAttribute;
                            origSetAttr2.call(this, 'data-blocked-redirect-target', val);
                        }
                        return actionDesc.set.call(this, val);
                    }
                });
            }
            _document.addEventListener('submit', function(e) {
                if (!currentConfig.redirectBlockerEnabled) return;
                const form = e.target;
                if (form && form.tagName === 'FORM') {
                    const action = form.getAttribute('action');
                    if (!action || action.trim() === '') {
                        if (form.hasAttribute('data-blocked-redirect-target') || form.hasAttribute('data-blocked-action')) {
                            showBlockedNotification();
                            e.preventDefault();
                            e.stopImmediatePropagation();
                            return false;
                        }
                        return;
                    }
                    if (shouldBlockRedirect(action)) {
                        showBlockedNotification();
                        e.preventDefault();
                        e.stopImmediatePropagation();
                        return false;
                    }
                }
            }, true);
        } catch (e) {}

        try {
            const mo = new _MutationObserver((mutations) => {
                for (const m of mutations) {
                    if (!m.addedNodes) continue;
                    m.addedNodes.forEach((node) => {
                        if (!(node instanceof _Element)) return;
                        if (node.tagName === 'META' && /refresh/i.test(node.getAttribute('http-equiv'))) {
                            if (/url\s*=/i.test(node.getAttribute('content') || '')) node.remove();
                        }
                        if (node.tagName === 'A') {
                            const href = node.getAttribute('href');
                            if (href && currentConfig.redirectBlockerEnabled && shouldBlockRedirect(href)) {
                                const origSetAttr2 = _Element.prototype.setAttribute;
                                origSetAttr2.call(node, 'data-blocked-redirect', href);
                                node.removeAttribute('href');
                            }
                        }
                        if (node.querySelectorAll) {
                            node.querySelectorAll('meta[http-equiv="refresh"]').forEach(m => m.remove());
                        }
                    });
                }
            });
            mo.observe(_document.documentElement || _document, {
                childList: true,
                subtree: true
            });
        } catch (e) {}

        try {
            _globals.addEventListener('click', (e) => {
                if (!currentConfig.redirectBlockerEnabled) return;
                const link = e.target && e.target.closest ? e.target.closest('a') : null;
                if (link) {
                    const href = link.getAttribute('href') || link.href;
                    if (shouldBlockRedirect(href)) {
                        showBlockedNotification();
                        e.preventDefault();
                        e.stopImmediatePropagation();
                        return false;
                    }
                }
            }, true);
        } catch (e) {}

        try {
            const iframeProto = HTMLIFrameElement.prototype;
            const origIframeSrcDesc = Object.getOwnPropertyDescriptor(iframeProto, 'src');
            if (origIframeSrcDesc && origIframeSrcDesc.set && origIframeSrcDesc.configurable) {
                Object.defineProperty(iframeProto, 'src', {
                    configurable: true,
                    enumerable: true,
                    get() {
                        return origIframeSrcDesc.get.call(this);
                    },
                    set(value) {
                        if (!currentConfig.redirectBlockerEnabled) return origIframeSrcDesc.set.call(this, value);
                        if (shouldBlockRedirect(value)) {
                            showBlockedNotification();
                            return;
                        }
                        origIframeSrcDesc.set.call(this, value);
                    }
                });
            }
            const frameProto = HTMLFrameElement.prototype;
            const origFrameSrcDesc = Object.getOwnPropertyDescriptor(frameProto, 'src');
            if (origFrameSrcDesc && origFrameSrcDesc.set && origFrameSrcDesc.configurable) {
                Object.defineProperty(frameProto, 'src', {
                    configurable: true,
                    enumerable: true,
                    get() {
                        return origFrameSrcDesc.get.call(this);
                    },
                    set(value) {
                        if (!currentConfig.redirectBlockerEnabled) return origFrameSrcDesc.set.call(this, value);
                        if (shouldBlockRedirect(value)) {
                            showBlockedNotification();
                            return;
                        }
                        origFrameSrcDesc.set.call(this, value);
                    }
                });
            }
        } catch (e) {}

        try {
            const baseObserver = new _MutationObserver((mutations) => {
                for (const mutation of mutations) {
                    if (mutation.type === 'childList') {
                        mutation.addedNodes.forEach(node => {
                            if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'BASE') {
                                const href = node.getAttribute('href');
                                if (href && currentConfig.redirectBlockerEnabled && shouldBlockRedirect(href)) {
                                    node.removeAttribute('href');
                                    showBlockedNotification(true);
                                }
                            }
                        });
                    } else if (mutation.type === 'attributes' && mutation.attributeName === 'href') {
                        const target = mutation.target;
                        if (target.tagName === 'BASE') {
                            const href = target.getAttribute('href');
                            if (href && currentConfig.redirectBlockerEnabled && shouldBlockRedirect(href)) {
                                target.removeAttribute('href');
                                showBlockedNotification(true);
                            }
                        }
                    }
                }
            });
            baseObserver.observe(_document.documentElement || _document, {
                childList: true,
                subtree: true,
                attributes: true,
                attributeFilter: ['href']
            });
        } catch (e) {}

        try {
            const areaProto = HTMLAreaElement.prototype;
            const origAreaHrefDesc = Object.getOwnPropertyDescriptor(areaProto, 'href');
            if (origAreaHrefDesc && origAreaHrefDesc.set && origAreaHrefDesc.configurable) {
                Object.defineProperty(areaProto, 'href', {
                    configurable: true,
                    enumerable: true,
                    get() {
                        return origAreaHrefDesc.get.call(this);
                    },
                    set(value) {
                        if (!currentConfig.redirectBlockerEnabled) return origAreaHrefDesc.set.call(this, value);
                        if (shouldBlockRedirect(value)) {
                            showBlockedNotification();
                            return;
                        }
                        origAreaHrefDesc.set.call(this, value);
                    }
                });
            }
            const embedProto = HTMLEmbedElement.prototype;
            const origEmbedSrcDesc = Object.getOwnPropertyDescriptor(embedProto, 'src');
            if (origEmbedSrcDesc && origEmbedSrcDesc.set && origEmbedSrcDesc.configurable) {
                Object.defineProperty(embedProto, 'src', {
                    configurable: true,
                    enumerable: true,
                    get() {
                        return origEmbedSrcDesc.get.call(this);
                    },
                    set(value) {
                        if (!currentConfig.redirectBlockerEnabled) return origEmbedSrcDesc.set.call(this, value);
                        if (shouldBlockRedirect(value)) {
                            showBlockedNotification();
                            return;
                        }
                        origEmbedSrcDesc.set.call(this, value);
                    }
                });
            }
            const objectProto = HTMLObjectElement.prototype;
            const origObjectDataDesc = Object.getOwnPropertyDescriptor(objectProto, 'data');
            if (origObjectDataDesc && origObjectDataDesc.set && origObjectDataDesc.configurable) {
                Object.defineProperty(objectProto, 'data', {
                    configurable: true,
                    enumerable: true,
                    get() {
                        return origObjectDataDesc.get.call(this);
                    },
                    set(value) {
                        if (!currentConfig.redirectBlockerEnabled) return origObjectDataDesc.set.call(this, value);
                        if (shouldBlockRedirect(value)) {
                            showBlockedNotification();
                            return;
                        }
                        origObjectDataDesc.set.call(this, value);
                    }
                });
            }
        } catch (e) {}
    }

    const UI_CSS = `
.mask {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0);
    backdrop-filter: blur(0px);
    z-index: ${CONFIG.Z_INDEX};
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.3s ease;
    pointer-events: auto;
    animation: fade-in 0.3s forwards;
}
.panel {
    background: #fff;
    border-radius: 20px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
    padding: 16px 12px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    width: 94vw;
    max-width: 500px;
    font-family: system-ui, -apple-system, sans-serif;
    box-sizing: border-box;
    position: relative;
    transform: scale(0.9);
    opacity: 0;
    animation: scale-in 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
    max-height: 85vh;
    overflow-y: auto;
}
.title {
    margin: 0 0 6px 0;
    font-size: 16px;
    font-weight: 700;
    color: #1a1a1a;
    text-align: center;
    word-break: break-all;
    line-height: 1.3;
    padding: 0 8px;
}
.btn-group {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 4px;
}
.btn-group button {
    flex: 1 0 calc(50% - 8px);
    min-width: 100px;
}
button {
    border: none;
    border-radius: 10px;
    padding: 10px 8px;
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    transition: all 0.2s;
    background: #f0f2f5;
    color: #444;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 40px;
}
button:hover {
    background: #e4e6e9;
    transform: translateY(-1px);
}
button:active {
    transform: scale(0.95);
}
button.primary {
    background: #007AFF;
    color: #fff;
}
button.primary:hover {
    background: #0063cc;
    box-shadow: 0 4px 12px rgba(0, 122, 255, 0.3);
}
button.danger {
    background: #ff4d4f;
    color: #fff;
}
button.danger:hover {
    background: #d9363e;
    box-shadow: 0 4px 12px rgba(255, 77, 79, 0.3);
}
textarea {
    width: 100%;
    height: 140px;
    border: 1px solid #ddd;
    border-radius: 10px;
    padding: 10px;
    font-family: monospace;
    font-size: 12px;
    resize: none;
    box-sizing: border-box;
    outline: none;
    line-height: 1.4;
}
textarea:focus {
    border-color: #007AFF;
    box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.1);
}
select {
    width: 100%;
    padding: 8px;
    border-radius: 10px;
    border: 1px solid #ddd;
    outline: none;
    font-size: 13px;
}
.footer {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 6px;
}
.module-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 8px;
    margin-bottom: 8px;
}
.module-switch {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 6px 10px;
    background: #f8f9fa;
    border-radius: 10px;
    border: 1px solid #eee;
}
.switch-label {
    font-size: 13px;
    font-weight: 600;
    color: #333;
    display: flex;
    align-items: center;
    gap: 6px;
}
.info-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    background: #007AFF;
    color: white;
    border-radius: 50%;
    font-size: 12px;
    font-weight: bold;
    cursor: pointer;
    margin-left: 4px;
}
.info-icon:hover {
    background: #0056b3;
}
.switch {
    position: relative;
    width: 40px;
    height: 24px;
    flex-shrink: 0;
}
.switch input {
    opacity: 0;
    width: 0;
    height: 0;
}
.slider {
    position: absolute;
    cursor: pointer;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: #ccc;
    transition: .4s;
    border-radius: 24px;
}
.slider:before {
    position: absolute;
    content: "";
    height: 18px;
    width: 18px;
    left: 3px;
    bottom: 3px;
    background-color: white;
    transition: .4s;
    border-radius: 50%;
}
input:checked + .slider {
    background-color: #007AFF;
}
input:checked + .slider:before {
    transform: translateX(16px);
}
.sub-panel {
    max-height: 45vh;
    overflow-y: auto;
    background: #f9f9f9;
    padding: 12px;
    border-radius: 10px;
    border: 1px solid #eee;
}
.log-entry {
    margin-bottom: 10px;
    padding: 10px;
    background: #fff;
    border-radius: 8px;
    border-left: 4px solid #007AFF;
    font-size: 12px;
    position: relative;
    min-height: 60px;
}
.log-module {
    color: #007AFF;
    font-weight: bold;
    margin-bottom: 3px;
    font-size: 13px;
}
.log-content {
    color: #666;
    word-break: break-word;
    font-size: 11px;
    max-height: 180px;
    overflow-y: auto;
    white-space: normal;
    line-height: 1.4;
    padding-right: 45px;
}
.whitelist-btn {
    position: absolute;
    top: 8px;
    right: 8px;
    background: #34C759;
    color: #fff;
    border: none;
    border-radius: 5px;
    padding: 3px 8px;
    font-size: 10px;
    cursor: pointer;
    z-index: 1;
}
.csp-rule {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px;
    background: #fff;
    border-radius: 8px;
    margin-bottom: 6px;
}
.csp-name {
    font-size: 12px;
    color: #333;
    max-width: 70%;
}
.whitelist-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: nowrap;
    width: 100%;
    padding: 8px;
    background: #fff;
    border-radius: 6px;
    margin-bottom: 5px;
}
.whitelist-text {
    font-size: 11px;
    color: #333;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1 1 auto;
    min-width: 0;
}
.whitelist-item button {
    flex-shrink: 0;
    margin-left: 8px;
}
.keyword-highlight {
    background-color: #fff9c4;
}
.panel::-webkit-scrollbar {
    width: 6px;
}
.panel::-webkit-scrollbar-track {
    background: #f1f1f1;
    border-radius: 4px;
}
.panel::-webkit-scrollbar-thumb {
    background: #c1c1c1;
    border-radius: 4px;
}
.panel::-webkit-scrollbar-thumb:hover {
    background: #a8a8a8;
}
.log-entry.whitelisted {
    background-color: #e8f5e8;
}
.log-entry.keyword-whitelisted {
    background-color: #e0f0ff;
}
.log-entry.blacklisted {
    background-color: #ffe0e0;
}
.toast-container {
    position: fixed;
    bottom: 30px;
    left: 50%;
    transform: translateX(-50%);
    z-index: ${CONFIG.Z_INDEX + 7};
    pointer-events: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}
.toast {
    background: rgba(0, 0, 0, 0.85);
    color: #fff;
    padding: 12px 20px;
    border-radius: 10px;
    font-size: 14px;
    max-width: 85vw;
    text-align: center;
    line-height: 1.5;
    opacity: 0;
    transform: translateY(10px);
    animation: toast-in 0.3s ease forwards;
    white-space: pre-line;
    word-break: break-word;
}
@keyframes toast-in {
    to {
        opacity: 1;
        transform: translateY(0);
    }
}
@keyframes toast-out {
    to {
        opacity: 0;
        transform: translateY(10px);
    }
}
@media (orientation: landscape) and (max-height: 500px) {
    .btn-group button {
        flex: 1 0 100%;
        min-width: 80px;
    }
    .module-grid {
        grid-template-columns: repeat(3, 1fr);
    }
    .panel {
        max-height: 95vh;
        padding: 12px 10px;
    }
    .sub-panel {
        max-height: 50vh;
    }
}
@media (prefers-color-scheme: dark) {
    .panel {
        background: #1c1c1e;
        color: #fff;
    }
    .title {
        color: #fff;
    }
    button {
        background: #2c2c2e;
        color: #ccc;
    }
    button:hover {
        background: #3a3a3c;
    }
    textarea {
        background: #2c2c2e;
        border-color: #444;
        color: #eee;
    }
    select {
        background: #2c2c2e;
        border-color: #444;
        color: #eee;
    }
    .module-switch {
        background: #2c2c2e;
        border-color: #444;
    }
    .switch-label {
        color: #eee;
    }
    .sub-panel {
        background: #2c2c2e;
        border-color: #444;
    }
    .log-entry {
        background: #1c1c1e;
    }
    .csp-rule {
        background: #1c1c1e;
    }
    .whitelist-item {
        background: #1c1c1e;
    }
    .whitelist-text {
        color: #eee;
    }
    .keyword-highlight {
        background-color: #4a4a2c;
    }
    .panel::-webkit-scrollbar-track {
        background: #2c2c2e;
    }
    .panel::-webkit-scrollbar-thumb {
        background: #555;
    }
    .panel::-webkit-scrollbar-thumb:hover {
        background: #666;
    }
    .log-entry.whitelisted {
        background-color: #2a4a2a;
    }
    .log-entry.keyword-whitelisted {
        background-color: #2a3a5a;
    }
    .log-entry.blacklisted {
        background-color: #5a2a2a;
    }
    .toast {
        background: rgba(255, 255, 255, 0.9);
        color: #1c1c1e;
    }
}
@keyframes fade-in {
    to {
        background: rgba(0, 0, 0, 0.3);
        backdrop-filter: blur(8px);
    }
}
@keyframes scale-in {
    to {
        transform: scale(1);
        opacity: 1;
    }
}
`;

    let panelIdCounter = 0;

    class PanelManager {
        constructor() {
            this.shadowRoot = null;
            this.settingsContainer = null;
            this.toastContainer = null;
            this.isAnimating = false;
            this.currentController = null;
        }
        ensureShadow() {
            if (this.shadowRoot) return;
            this.settingsContainer = _document.createElement('div');
            this.settingsContainer.id = 'ad-blocker-settings-container';
            this.settingsContainer.style.cssText = `position:absolute;top:0;left:0;z-index:${CONFIG.Z_INDEX + 7};pointer-events:none;`;
            this.settingsContainer.setAttribute('data-adblock-safe', 'true');
            _document.documentElement.appendChild(this.settingsContainer);
            this.shadowRoot = this.settingsContainer.attachShadow({
                mode: 'closed'
            });
            const style = _document.createElement('style');
            style.textContent = UI_CSS;
            this.shadowRoot.appendChild(style);
            this.toastContainer = _document.createElement('div');
            this.toastContainer.className = 'toast-container';
            this.shadowRoot.appendChild(this.toastContainer);
            ProcessedElementsCache.markAsProcessed(this.settingsContainer);
        }
        showToast(message, duration = 3500) {
            this.ensureShadow();
            if (!this.shadowRoot || !this.toastContainer) return;
            const toast = _document.createElement('div');
            toast.className = 'toast';
            toast.textContent = message;
            this.toastContainer.appendChild(toast);
            _setTimeout(() => {
                toast.style.animation = 'toast-out 0.3s ease forwards';
                _setTimeout(() => {
                    if (toast.parentNode) toast.parentNode.removeChild(toast);
                }, 300);
            }, duration);
        }
        closeCurrentMask() {
            const existingMask = this.shadowRoot?.querySelector('.mask');
            if (existingMask) {
                if (this.currentController) {
                    this.currentController.abort();
                    this.currentController = null;
                }
                const panelIdAttr = existingMask.getAttribute('data-panel-id');
                if (panelIdAttr) {
                    teardownNavigationBlocking(panelIdAttr);
                }
                existingMask.style.transition = 'none';
                existingMask.remove();
                this.isAnimating = false;
            }
        }
        createPanel(options) {
            if (this.isAnimating) return null;
            const {
                title,
                contentHtml,
                onClose,
                onBack,
                buttons = [],
                hideBackButton = false,
                isRootPanel = false
            } = options;
            this.ensureShadow();
            this.closeCurrentMask();
            const panelId = `panel_${++panelIdCounter}`;
            setupNavigationBlocking(panelId);
            const controller = _AbortController ? new _AbortController() : null;
            const signal = controller ? controller.signal : undefined;
            this.currentController = controller;
            const mask = _document.createElement('div');
            mask.className = 'mask';
            mask.setAttribute('data-adblock-safe', 'true');
            mask.setAttribute('data-panel-id', panelId);
            const buttonHtml = buttons.map(btn => `<button type="button" data-id="${btn.id}" class="${btn.class || ''}">${escapeHtml(btn.text)}</button>`).join('');
            const backButtonHtml = hideBackButton ? '' : `<button type="button" data-id="backBtn" class="primary">返回设置</button>`;
            mask.innerHTML = `
<div class="panel" style="max-width:600px; width:95vw;">
    <div class="title">${escapeHtml(title)}</div>
    ${contentHtml}
    <div class="btn-group">
        ${buttonHtml}
        ${backButtonHtml}
    </div>
</div>
`;
            const closePanel = (animate = true) => {
                if (!mask.parentNode) return;
                teardownNavigationBlocking(panelId);
                const performRemove = () => {
                    if (controller) controller.abort();
                    mask.remove();
                    this.isAnimating = false;
                    if (this.currentController === controller) this.currentController = null;
                    if (onClose) onClose();
                };
                if (animate) {
                    this.isAnimating = true;
                    const onTransitionEnd = () => {
                        mask.removeEventListener('transitionend', onTransitionEnd);
                        performRemove();
                    };
                    mask.addEventListener('transitionend', onTransitionEnd);
                    mask.style.opacity = '0';
                    _setTimeout(() => {
                        if (mask.parentNode) {
                            mask.removeEventListener('transitionend', onTransitionEnd);
                            performRemove();
                        }
                    }, 500);
                } else {
                    performRemove();
                }
            };
            mask._closePanel = closePanel;
            const handleBack = () => {
                if (isRootPanel) return;
                closePanel();
                if (onBack) onBack();
                else this.showSettings();
            };
            mask.addEventListener('click', (e) => {
                const path = e.composedPath();
                const isPanel = path.some(el => Utils.isPanelElement(el));
                if (!isPanel && e.target === mask) {
                    handleBack();
                    return;
                }
                if (isPanel) {
                    const isInteractive = e.target.closest('button, input, textarea, select, label, .info-icon, .whitelist-btn');
                    if (!isInteractive) {
                        handleBack();
                        return;
                    }
                }
                const button = e.target.closest('button');
                if (!button) return;
                const action = button.dataset.action || button.dataset.id;
                const index = button.dataset.index;
                if (action === 'backBtn') {
                    handleBack();
                    return;
                }
                const matchedButton = buttons.find(btn => btn.id === action);
                if (matchedButton && matchedButton.onclick) {
                    matchedButton.onclick(e, mask);
                    return;
                }
                this.handlePanelAction(action, index, button, mask, e);
            }, {
                signal
            });
            mask.addEventListener('change', (e) => {
                const target = e.target;
                if (target.matches('.module-toggle')) {
                    const key = target.dataset.key;
                    const checked = target.checked;
                    currentConfig.modules[key] = checked;
                    StorageManager.saveConfig();
                    resetAllCachesAndStates();
                    if (key === 'removeInlineScripts') {
                        if (inlineScriptsModule) inlineScriptsModule.init();
                    } else if (key === 'interceptThirdParty') {
                        if (thirdPartyModule) thirdPartyModule.init();
                    }
                    if (strongBlockingEnabled) disableStrongBlocking();
                    const anyModuleEnabled = Object.values(currentConfig.modules).some(v => v === true);
                    if (currentConfig.residualCleanupEnabled && anyModuleEnabled) {
                        ResidualCleaner.init();
                    } else {
                        ResidualCleaner.stop();
                    }
                } else if (target.matches('.csp-toggle')) {
                    const id = parseInt(target.dataset.id);
                    const enabled = target.checked;
                    CSPModule.updateRule(id, enabled);
                    currentConfig.modules.manageCSP = currentConfig.cspRules.some(rule => rule.enabled);
                    StorageManager.saveConfig();
                    _location.reload();
                } else if (target.matches('.advanced-toggle')) {
                    const key = target.dataset.key;
                    const checked = target.checked;
                    if (key === 'inlineScriptStrictMode') {
                        currentConfig.inlineScriptStrictMode = checked;
                        StorageManager.saveConfig();
                        if (inlineScriptsModule) inlineScriptsModule.updateStrictMode();
                    } else if (key === 'thirdPartyStrictMode') {
                        currentConfig.thirdPartyStrictMode = checked;
                        StorageManager.saveConfig();
                        if (thirdPartyModule) thirdPartyModule.updateStrictMode();
                    } else if (key === 'thirdPartyStrictMethod') {
                        currentConfig.thirdPartyStrictMethod = checked;
                        StorageManager.saveConfig();
                        if (thirdPartyModule) thirdPartyModule.updateStrictMethod();
                    } else if (key === 'residualCleanupEnabled') {
                        currentConfig.residualCleanupEnabled = checked;
                        StorageManager.saveConfig();
                        const anyModuleEnabled = Object.values(currentConfig.modules).some(v => v === true);
                        if (currentConfig.residualCleanupEnabled && anyModuleEnabled) {
                            ResidualCleaner.init();
                        } else {
                            ResidualCleaner.stop();
                        }
                    } else if (key === 'cssUniversalHideEnabled') {
                        currentConfig.cssUniversalHideEnabled = checked;
                        StorageManager.saveConfig();
                        CSSUniversalHider.init();
                    } else if (key === 'iframeUIFix') {
                        currentConfig.iframeUIFix = checked;
                        StorageManager.saveConfig();
                    } else if (key === 'navigationBlockEnabled') {
                        currentConfig.navigationBlockEnabled = checked;
                        StorageManager.saveConfig();
                    } else if (key === 'redirectBlockerEnabled') {
                        currentConfig.redirectBlockerEnabled = checked;
                        StorageManager.saveConfig();
                    }
                }
            }, {
                signal
            });
            this.shadowRoot.appendChild(mask);
            mask.style.pointerEvents = 'auto';
            mask.querySelector('.panel').style.pointerEvents = 'auto';
            return mask;
        }
        handlePanelAction(action, index, button, mask, event) {
            switch (action) {
                case 'deleteDiaryItem':
                    {
                        const diaryWhitelist = Array.from(currentConfig.whitelist);
                        const idx = parseInt(index);
                        if (!isNaN(idx) && idx >= 0 && idx < diaryWhitelist.length) {
                            const item = diaryWhitelist[idx];
                            if (item) {
                                currentConfig.whitelist.delete(item);
                                StorageManager.saveConfig();
                                resetAllCachesAndStates();
                                this.showDiaryWhitelistPanel();
                            }
                        }
                        break;
                    }
                case 'deleteKeywordItem':
                    {
                        const keywords = Array.from(currentConfig.keywordWhitelist);
                        const idx = parseInt(index);
                        if (!isNaN(idx) && idx >= 0 && idx < keywords.length) {
                            const kw = keywords[idx];
                            if (kw) {
                                currentConfig.keywordWhitelist.delete(kw);
                                StorageManager.saveConfig();
                                resetAllCachesAndStates();
                                this.showKeywordWhitelistPanel();
                            }
                        }
                        break;
                    }
                case 'deleteBlacklistItem':
                    {
                        const blacklist = Array.from(currentConfig.scriptBlacklist);
                        const idx = parseInt(index);
                        if (!isNaN(idx) && idx >= 0 && idx < blacklist.length) {
                            const kw = blacklist[idx];
                            if (kw) {
                                currentConfig.scriptBlacklist.delete(kw);
                                StorageManager.saveConfig();
                                resetAllCachesAndStates();
                                this.showScriptBlacklistPanel();
                            }
                        }
                        break;
                    }
                case 'deleteThirdPartyItem':
                    {
                        const whitelist = currentConfig.thirdPartyWhitelist;
                        const idx = parseInt(index);
                        if (!isNaN(idx) && idx >= 0 && idx < whitelist.length) {
                            const item = whitelist[idx];
                            if (item) {
                                whitelist.splice(idx, 1);
                                StorageManager.saveConfig();
                                Whitelisting.removeKeywordsMatchingDomain(item);
                                resetAllCachesAndStates();
                                this.showThirdPartyPanel();
                            }
                        }
                        break;
                    }
                case 'whitelistLog':
                    {
                        const logs = LogManager.logs;
                        const idx = parseInt(index);
                        if (!isNaN(idx) && idx >= 0 && idx < logs.length) {
                            const logEntry = logs[idx];
                            if (logEntry && logEntry.contentIdentifier) {
                                const isThirdPartyDomain = currentConfig.modules.interceptThirdParty && logEntry.domain;
                                if (isThirdPartyDomain) {
                                    const domain = logEntry.domain;
                                    if (!currentConfig.thirdPartyWhitelist.includes(domain)) {
                                        currentConfig.thirdPartyWhitelist.push(domain);
                                    }
                                    for (let i = 0; i < logs.length; i++) {
                                        const entry = logs[i];
                                        if (entry.domain === domain && entry.contentIdentifier !== logEntry.contentIdentifier) {
                                            if (!currentConfig.thirdPartyWhitelist.includes(entry.domain)) {
                                                currentConfig.thirdPartyWhitelist.push(entry.domain);
                                            }
                                        }
                                    }
                                    StorageManager.saveConfig();
                                    const logDivs = mask.querySelectorAll('.log-entry');
                                    for (let i = 0; i < logDivs.length; i++) {
                                        const div = logDivs[i];
                                        const btn = div.querySelector('.whitelist-btn');
                                        if (!btn) continue;
                                        const btnIndex = parseInt(btn.dataset.index);
                                        if (!isNaN(btnIndex) && logs[btnIndex] && logs[btnIndex].domain === domain) {
                                            if (!div.classList.contains('whitelisted')) {
                                                div.classList.add('whitelisted');
                                            }
                                            btn.disabled = true;
                                            btn.textContent = '已加白';
                                            btn.style.backgroundColor = '#999';
                                        }
                                    }
                                } else {
                                    if (!currentConfig.whitelist.has(logEntry.contentIdentifier)) {
                                        currentConfig.whitelist.add(logEntry.contentIdentifier);
                                    }
                                    StorageManager.saveConfig();
                                    const logDiv = button.closest('.log-entry');
                                    if (logDiv) {
                                        logDiv.classList.add('whitelisted');
                                        button.disabled = true;
                                        button.textContent = '已加白';
                                        button.style.backgroundColor = '#999';
                                    }
                                }
                                resetAllCachesAndStates();
                            }
                        }
                        break;
                    }
                case 'addToBlacklist':
                    {
                        const encodedContent = button.dataset.content;
                        if (encodedContent) {
                            const content = decodeURIComponent(encodedContent);
                            currentConfig.scriptBlacklist.add(content);
                            StorageManager.saveConfig();
                            const logDiv = button.closest('.log-entry');
                            if (logDiv) {
                                const targetBtn = logDiv.querySelector('button[data-action="addToBlacklist"]');
                                if (targetBtn) {
                                    targetBtn.disabled = true;
                                    targetBtn.textContent = '已加黑';
                                    targetBtn.style.backgroundColor = '#999';
                                }
                                logDiv.classList.add('blacklisted');
                            }
                        }
                        break;
                    }
                case 'addKeywordWhitelist':
                    {
                        const input = mask.querySelector('#keywordWhitelistInput');
                        const keyword = input.value.trim();
                        if (keyword) {
                            Whitelisting.addKeyword(keyword);
                            input.value = '';
                            this.showLogsPanel();
                        }
                        break;
                    }
                case 'addWhitelist':
                    {
                        const input = mask.querySelector('#newWhitelist');
                        let value = input.value.trim();
                        if (value) {
                            value = value.replace(/^https?:\/\//, '');
                            try {
                                const urlObj = new URL('http://' + value);
                                value = urlObj.hostname;
                            } catch (e) {}
                            const whitelist = currentConfig.thirdPartyWhitelist;
                            if (!whitelist.includes(value)) {
                                whitelist.push(value);
                                StorageManager.saveConfig();
                                Whitelisting.removeKeywordsMatchingDomain(value);
                            }
                            input.value = '';
                            resetAllCachesAndStates();
                            this.showThirdPartyPanel();
                        }
                        break;
                    }
                case 'addBlacklist':
                    {
                        const input = mask.querySelector('#newBlacklistKeyword');
                        const kw = input.value.trim();
                        if (kw) {
                            currentConfig.scriptBlacklist.add(kw);
                            StorageManager.saveConfig();
                            input.value = '';
                            resetAllCachesAndStates();
                            this.showScriptBlacklistPanel();
                        }
                        break;
                    }
                case 'addBlacklistFromDomain':
                    {
                        const domain = decodeURIComponent(button.dataset.domain);
                        if (domain) {
                            currentConfig.scriptBlacklist.add(domain);
                            StorageManager.saveConfig();
                            const logDiv = button.closest('.log-entry');
                            if (logDiv) {
                                const targetBtn = logDiv.querySelector('button[data-action="addBlacklistFromDomain"]');
                                if (targetBtn) {
                                    targetBtn.disabled = true;
                                    targetBtn.textContent = '域名已加黑';
                                    targetBtn.style.backgroundColor = '#999';
                                }
                                logDiv.classList.add('blacklisted');
                            }
                        }
                        break;
                    }
                case 'clearAllDiary':
                    {
                        if (confirm('确定清空所有日记白名单吗?')) {
                            currentConfig.whitelist.clear();
                            StorageManager.saveConfig();
                            resetAllCachesAndStates();
                            this.showDiaryWhitelistPanel();
                        }
                        break;
                    }
                case 'clearAllKeyword':
                    {
                        if (confirm('确定清空所有关键词白名单吗?')) {
                            currentConfig.keywordWhitelist.clear();
                            StorageManager.saveConfig();
                            resetAllCachesAndStates();
                            this.showKeywordWhitelistPanel();
                        }
                        break;
                    }
                case 'clearAllBlacklist':
                    {
                        if (confirm('确定清空所有脚本黑名单吗?')) {
                            currentConfig.scriptBlacklist.clear();
                            StorageManager.saveConfig();
                            resetAllCachesAndStates();
                            this.showScriptBlacklistPanel();
                        }
                        break;
                    }
                case 'clearAllThirdParty':
                    {
                        if (confirm('确定清空所有第三方白名单吗?')) {
                            currentConfig.thirdPartyWhitelist = [];
                            StorageManager.saveConfig();
                            resetAllCachesAndStates();
                            this.showThirdPartyPanel();
                        }
                        break;
                    }
                case 'showScriptList':
                    {
                        this.showScriptListPanel();
                        break;
                    }
                case 'enableCSP':
                    {
                        currentConfig.modules.manageCSP = true;
                        StorageManager.saveConfig();
                        _location.reload();
                        break;
                    }
                case 'disableCSP':
                    {
                        currentConfig.modules.manageCSP = false;
                        StorageManager.saveConfig();
                        _location.reload();
                        break;
                    }
                case 'allOn':
                    {
                        currentConfig.cspRules.forEach(rule => rule.enabled = true);
                        currentConfig.modules.manageCSP = true;
                        StorageManager.saveConfig();
                        _location.reload();
                        break;
                    }
                case 'allOff':
                    {
                        currentConfig.cspRules.forEach(rule => rule.enabled = false);
                        currentConfig.modules.manageCSP = false;
                        StorageManager.saveConfig();
                        _location.reload();
                        break;
                    }
                case 'closePanel':
                    {
                        if (mask._closePanel) mask._closePanel();
                        break;
                    }
                case 'showAdvancedSettings':
                    {
                        this.showAdvancedSettingsPanel();
                        break;
                    }
                default:
                    break;
            }
        }
        showAdvancedSettingsPanel() {
            this.closeCurrentMask();
            const contentHtml = `
<div style="margin-bottom: 12px; font-size: 13px; color: #666;">高级选项控制拦截行为的具体细节</div>
<div class="module-switch" style="margin-bottom: 8px;">
    <span class="switch-label">导航阻断(强拦截) <span class="info-icon" data-info="navBlock">!</span></span>
    <label class="switch">
        <input type="checkbox" class="advanced-toggle" data-key="navigationBlockEnabled" ${currentConfig.navigationBlockEnabled ? 'checked' : ''}>
        <span class="slider"></span>
    </label>
</div>
<div class="module-switch" style="margin-bottom: 8px;">
    <span class="switch-label">同源跳转拦截 <span class="info-icon" data-info="redirectBlocker">!</span></span>
    <label class="switch">
        <input type="checkbox" class="advanced-toggle" data-key="redirectBlockerEnabled" ${currentConfig.redirectBlockerEnabled ? 'checked' : ''}>
        <span class="slider"></span>
    </label>
</div>
<div class="module-switch" style="margin-bottom: 8px;">
    <span class="switch-label">移除内嵌脚本 - 严格模式 <span class="info-icon" data-info="inlineStrict">!</span></span>
    <label class="switch">
        <input type="checkbox" class="advanced-toggle" data-key="inlineScriptStrictMode" ${currentConfig.inlineScriptStrictMode ? 'checked' : ''}>
        <span class="slider"></span>
    </label>
</div>
<div class="module-switch" style="margin-bottom: 8px;">
    <span class="switch-label">拦截第三方资源 - 严格模式 <span class="info-icon" data-info="thirdPartyStrict">!</span></span>
    <label class="switch">
        <input type="checkbox" class="advanced-toggle" data-key="thirdPartyStrictMode" ${currentConfig.thirdPartyStrictMode ? 'checked' : ''}>
        <span class="slider"></span>
    </label>
</div>
<div class="module-switch" style="margin-bottom: 8px;">
    <span class="switch-label">拦截第三方资源 - 严格劫持方式 <span class="info-icon" data-info="thirdPartyMethod">!</span></span>
    <label class="switch">
        <input type="checkbox" class="advanced-toggle" data-key="thirdPartyStrictMethod" ${currentConfig.thirdPartyStrictMethod ? 'checked' : ''}>
        <span class="slider"></span>
    </label>
</div>
<div class="module-switch" style="margin-bottom: 8px;">
    <span class="switch-label">清空残留容器 <span class="info-icon" data-info="residualCleanup">!</span></span>
    <label class="switch">
        <input type="checkbox" class="advanced-toggle" data-key="residualCleanupEnabled" ${currentConfig.residualCleanupEnabled ? 'checked' : ''}>
        <span class="slider"></span>
    </label>
</div>
<div class="module-switch" style="margin-bottom: 8px;">
    <span class="switch-label">CSS通用隐藏 <span class="info-icon" data-info="cssUniversalHide">!</span></span>
    <label class="switch">
        <input type="checkbox" class="advanced-toggle" data-key="cssUniversalHideEnabled" ${currentConfig.cssUniversalHideEnabled ? 'checked' : ''}>
        <span class="slider"></span>
    </label>
</div>
<div class="module-switch" style="margin-bottom: 8px;">
    <span class="switch-label">禁止在嵌套框架中显示面板 <span class="info-icon" data-info="iframeUIFix">!</span></span>
    <label class="switch">
        <input type="checkbox" class="advanced-toggle" data-key="iframeUIFix" ${currentConfig.iframeUIFix ? 'checked' : ''}>
        <span class="slider"></span>
    </label>
</div>
`;
            this.createPanel({
                title: '高级设置中心',
                contentHtml: contentHtml,
                buttons: [],
                onBack: () => this.showSettings()
            });
            const maskElement = this.shadowRoot.querySelector('.mask:last-child');
            if (maskElement) {
                maskElement.querySelectorAll('.info-icon').forEach(icon => {
                    icon.addEventListener('click', (e) => {
                        e.stopPropagation();
                        const type = icon.dataset.info;
                        let message = '';
                        if (type === 'navBlock') {
                            message = '启用后,打开设置面板时会阻止页面关闭/刷新,避免操作中断。';
                        } else if (type === 'redirectBlocker') {
                            message = '启用后,拦截所有跳转到其他域名的链接、表单提交、脚本跳转等,只允许当前域名内的跳转。\n\n若关闭"拦截第三方资源-严格模式",则允许同主域的子域名跳转(如从 www.example.com 到 auth.example.com)。';
                        } else if (type === 'inlineStrict') {
                            message = '严格模式:额外拦截内联事件(如onclick)和javascript:URL,适用于拦截点击弹窗、悬浮广告等。\n宽松模式(默认):仅移除内嵌脚本内容。';
                        } else if (type === 'thirdPartyStrict') {
                            message = '严格模式:拦截所有第三方资源(包括子域名和兄弟域名)。\n宽松模式(默认):只拦截完全无关的第三方域名(主域名不同)。\n\n此选项同时影响"同源跳转拦截"的行为。';
                        } else if (type === 'thirdPartyMethod') {
                            message = '严格劫持方式:在宽松模式(默认通过 setAttribute、fetch、XHR 代理和 MutationObserver 扫描拦截)基础上,额外劫持 appendChild 方法,拦截动态创建的第三方资源。可能影响性能,建议仅在必要时开启。';
                        } else if (type === 'residualCleanup') {
                            message = '清空残留容器:在浏览器空闲时清理有明显广告样式且内容为空的容器,忽略尺寸很小的元素。';
                        } else if (type === 'cssUniversalHide') {
                            message = 'CSS通用隐藏:动态检测高z-index(>600)特征的广告元素并隐藏。可能误伤正常元素,建议仅在必要时开启。';
                        } else if (type === 'iframeUIFix') {
                            message = '当在某些视频网站打开设置面板时,如果视频播放器和网页都弹出了控制面板,开启此选项可能修复该问题。';
                        } else {
                            message = '点击查看详情';
                        }
                        this.showToast(message, 5000);
                    });
                });
            }
        }
        showSettings() {
            this.closeCurrentMask();
            const moduleSwitches = Object.keys(MODULE_NAMES).map(key => `
<div class="module-switch">
    <span class="switch-label">${escapeHtml(MODULE_NAMES[key])}</span>
    <label class="switch">
        <input type="checkbox" class="module-toggle" data-key="${key}" ${currentConfig.modules[key] ? 'checked' : ''}>
        <span class="slider"></span>
    </label>
</div>
`).join('');
            this.createPanel({
                title: '🛡️广告拦截设置',
                contentHtml: `
<div style="font-size:13px;color:#888;margin-bottom:-6px;padding-left:4px;margin-top:4px;">功能模块:</div>
<div class="module-grid">${moduleSwitches}</div>
`,
                buttons: [{
                        id: 'viewLogs',
                        text: `拦截日志 (${LogManager.logs.length})`,
                        onclick: () => {
                            this.showLogsPanel();
                        }
                    },
                    {
                        id: 'manageCSP',
                        text: 'CSP策略管理',
                        onclick: () => {
                            this.showCSPPanel();
                        }
                    },
                    {
                        id: 'manageDiaryWhitelist',
                        text: '日记白名单',
                        onclick: () => {
                            this.showDiaryWhitelistPanel();
                        }
                    },
                    {
                        id: 'manageThirdParty',
                        text: '第三方白名单',
                        onclick: () => {
                            this.showThirdPartyPanel();
                        }
                    },
                    {
                        id: 'manageKeywordWhitelist',
                        text: '关键词白名单',
                        onclick: () => {
                            this.showKeywordWhitelistPanel();
                        }
                    },
                    {
                        id: 'manageScriptBlacklist',
                        text: '脚本黑名单',
                        onclick: () => {
                            this.showScriptBlacklistPanel();
                        }
                    },
                    {
                        id: 'showAdvancedSettings',
                        text: '高级设置中心',
                        onclick: () => {
                            this.showAdvancedSettingsPanel();
                        }
                    },
                    {
                        id: 'closePanel',
                        text: '返回网页',
                        onclick: (e, mask) => {
                            if (mask._closePanel) mask._closePanel();
                        }
                    }
                ],
                hideBackButton: true,
                isRootPanel: true
            });
        }
        showDiaryWhitelistPanel() {
            this.closeCurrentMask();
            const diaryWhitelist = Array.from(currentConfig.whitelist);
            const itemsHtml = diaryWhitelist.length > 0 ? diaryWhitelist.map((item, index) => {
                let displayText = item;
                if (item.startsWith('INLINE_EVENT: ')) {
                    displayText = item.substring('INLINE_EVENT: '.length);
                } else if (item.startsWith('JAVASCRIPT_URL: ')) {
                    displayText = item.substring('JAVASCRIPT_URL: '.length);
                } else if (item.startsWith('SCRIPT_CONTENT: ')) {
                    displayText = item.substring('SCRIPT_CONTENT: '.length);
                } else if (item.startsWith('SCRIPT_SRC: ')) {
                    displayText = item.substring('SCRIPT_SRC: '.length);
                } else if (item.startsWith('EVAL_HASH: ')) {
                    displayText = 'Eval代码哈希: ' + item.substring('EVAL_HASH: '.length);
                } else if (item.startsWith('FUNCTION_HASH: ')) {
                    displayText = 'Function代码哈希: ' + item.substring('FUNCTION_HASH: '.length);
                } else if (item.startsWith('DOCUMENT_WRITE_HASH: ')) {
                    displayText = 'document.write代码哈希: ' + item.substring('DOCUMENT_WRITE_HASH: '.length);
                } else if (item.startsWith('SETTIMEOUT_HASH: ')) {
                    displayText = 'setTimeout代码哈希: ' + item.substring('SETTIMEOUT_HASH: '.length);
                } else if (item.startsWith('SETINTERVAL_HASH: ')) {
                    displayText = 'setInterval代码哈希: ' + item.substring('SETINTERVAL_HASH: '.length);
                } else if (item.startsWith('REQUESTANIMATIONFRAME_HASH: ')) {
                    displayText = 'requestAnimationFrame代码哈希: ' + item.substring('REQUESTANIMATIONFRAME_HASH: '.length);
                }
                return `
<div class="whitelist-item" data-index="${index}">
    <span class="whitelist-text">${escapeHtml(displayText)}</span>
    <button type="button" class="danger" data-action="deleteDiaryItem" data-index="${index}" style="padding:4px 8px; font-size:10px; border-radius:6px;">删除</button>
</div>
`;
            }).join('') : '<div style="text-align:center;color:#999;margin-top:20px;padding:20px;font-size:14px;">日记白名单为空</div>';
            this.createPanel({
                title: `日记白名单 (${diaryWhitelist.length}项)`,
                contentHtml: `
<div style="margin-bottom:10px;font-size:13px;color:#666;text-align:center;">查看和管理拦截日记中添加的白名单条目</div>
<div class="sub-panel" data-panel="diaryList">${itemsHtml}</div>
<div class="btn-group"><button type="button" data-id="clearAllDiary" class="danger">清空全部</button></div>
`,
                buttons: [],
                onBack: () => this.showSettings()
            });
        }
        showKeywordWhitelistPanel() {
            this.closeCurrentMask();
            const keywords = Array.from(currentConfig.keywordWhitelist);
            const itemsHtml = keywords.length > 0 ? keywords.map((kw, index) => `
<div class="whitelist-item" data-index="${index}">
    <span class="whitelist-text">${escapeHtml(kw)}</span>
    <button type="button" class="danger" data-action="deleteKeywordItem" data-index="${index}" style="padding:4px 8px; font-size:10px; border-radius:6px;">删除</button>
</div>
`).join('') : '<div style="text-align:center;color:#999;margin-top:20px;padding:20px;font-size:14px;">关键词白名单为空</div>';
            this.createPanel({
                title: `关键词白名单 (${keywords.length}项)`,
                contentHtml: `
<div style="margin-bottom:10px;font-size:13px;color:#666;text-align:center;">查看和管理通过关键词添加的白名单条目</div>
<div class="sub-panel">${itemsHtml}</div>
<div class="btn-group"><button type="button" data-id="clearAllKeyword" class="danger">清空全部</button></div>
`,
                buttons: [],
                onBack: () => this.showSettings()
            });
        }
        showScriptListPanel() {
            this.closeCurrentMask();
            const scripts = Array.from(_document.scripts);
            const scriptItems = scripts.map((script, index) => {
                const isExternal = !!script.src;
                let content = isExternal ? script.src : script.textContent;
                content = Utils.truncateString(content, 500);
                return {
                    index: index + 1,
                    isExternal,
                    content,
                    script
                };
            });
            const blacklistSet = currentConfig.scriptBlacklist;
            const listHtml = scriptItems.length > 0 ? scriptItems.map(item => {
                const scriptContentToBlacklist = item.isExternal ? item.script.src : item.script.textContent;
                const isBlacklistedByContent = blacklistSet.has(scriptContentToBlacklist);
                let domainHint = '',
                    extractButton = '',
                    isDomainBlacklisted = false;
                if (item.isExternal && item.script.src) {
                    try {
                        const url = new URL(item.script.src, _location.href);
                        const domain = url.hostname;
                        if (domain) {
                            domainHint = `<div style="font-size:11px;color:#999;margin-top:4px;">域名: ${escapeHtml(domain)}</div>`;
                            isDomainBlacklisted = blacklistSet.has(domain);
                            extractButton = `
<button type="button" data-action="addBlacklistFromDomain" data-domain="${encodeURIComponent(domain)}" data-index="${item.index}" style="position:absolute; top:8px; right:80px; background:#FF9500; color:#fff; border:none; border-radius:5px; padding:3px 8px; font-size:10px; cursor:pointer; z-index:1;" ${isDomainBlacklisted ? 'disabled style="position:absolute; top:8px; right:80px; background:#999; color:#fff; border:none; border-radius:5px; padding:3px 8px; font-size:10px; cursor:pointer; z-index:1;"' : ''}>${isDomainBlacklisted ? '域名已加黑' : '加黑域名'}</button>
`;
                        }
                    } catch (e) {}
                }
                const logEntryClass = 'log-entry' + (isBlacklistedByContent || isDomainBlacklisted ? ' blacklisted' : '');
                return `
<div class="${logEntryClass}" style="border-left-color: ${item.isExternal ? '#FF3B30' : '#007AFF'}; position: relative;">
    <div class="log-module">脚本 #${item.index} - ${item.isExternal ? '外联' : '内嵌'}</div>
    <div class="log-content" style="max-height: none; white-space: pre-wrap; word-break: break-all; padding-right: 150px;">${escapeHtml(item.content)}</div>
    ${domainHint}
    ${extractButton}
    <button type="button" data-action="addToBlacklist" data-content="${encodeURIComponent(scriptContentToBlacklist)}" data-index="${item.index}" style="position:absolute; top:8px; right:8px; background:#FF3B30; color:#fff; border:none; border-radius:5px; padding:3px 8px; font-size:10px; cursor:pointer; z-index:1;" ${isBlacklistedByContent ? 'disabled style="position:absolute; top:8px; right:8px; background:#999; color:#fff; border:none; border-radius:5px; padding:3px 8px; font-size:10px; cursor:pointer; z-index:1;"' : ''}>${isBlacklistedByContent ? '已加黑' : '加黑'}</button>
</div>
`;
            }).join('') : '<div style="text-align:center;color:#999;margin-top:20px;padding:20px;font-size:14px;">未找到任何脚本</div>';
            this.createPanel({
                title: `当前网页脚本列表 (共${scriptItems.length}个)`,
                contentHtml: `
<div style="margin-bottom:10px;font-size:13px;color:#666;text-align:center;">点击「加黑」将整个脚本内容添加到脚本黑名单;「加黑域名」将脚本域名加入黑名单</div>
<div class="sub-panel" style="max-height:60vh;">${listHtml}</div>
`,
                buttons: [],
                onBack: () => this.showScriptBlacklistPanel()
            });
        }
        showScriptBlacklistPanel() {
            this.closeCurrentMask();
            const blacklist = Array.from(currentConfig.scriptBlacklist);
            const itemsHtml = blacklist.length > 0 ? blacklist.map((kw, index) => `
<div class="whitelist-item" data-index="${index}">
    <span class="whitelist-text">${escapeHtml(kw)}</span>
    <button type="button" class="danger" data-action="deleteBlacklistItem" data-index="${index}" style="padding:4px 8px; font-size:10px; border-radius:6px;">删除</button>
</div>
`).join('') : '<div style="text-align:center;color:#999;margin-top:20px;padding:20px;font-size:14px;">脚本黑名单为空</div>';
            this.createPanel({
                title: `脚本黑名单 (${blacklist.length}项)`,
                contentHtml: `
<div style="margin-bottom:10px;font-size:13px;color:#666;text-align:center;">脚本黑名单模式将拦截内嵌/外联脚本中匹配这些关键词的资源</div>
<div class="sub-panel">${itemsHtml}</div>
<div style="display:flex;gap:8px;margin-bottom:10px;">
    <input type="text" id="newBlacklistKeyword" placeholder="输入关键词或脚本内容..." style="flex:1;padding:8px;border-radius:8px;border:1px solid #ddd;outline:none;font-size:13px;">
    <button type="button" data-id="addBlacklist" class="primary" style="padding:8px 16px;">添加</button>
</div>
<div class="btn-group">
    <button type="button" data-id="clearAllBlacklist" class="danger">清空全部</button>
    <button type="button" data-id="showScriptList" class="primary">显示所有脚本</button>
</div>
`,
                buttons: [],
                onBack: () => this.showSettings()
            });
            _setTimeout(() => {
                const maskElement = this.shadowRoot.querySelector('.mask:last-child');
                if (!maskElement) return;
                const input = maskElement.querySelector('#newBlacklistKeyword');
                if (input) {
                    input.focus();
                    input.addEventListener('keypress', (e) => {
                        if (e.key === 'Enter') {
                            const addBtn = maskElement.querySelector('[data-id="addBlacklist"]');
                            if (addBtn) addBtn.click();
                        }
                    });
                }
            }, 0);
        }
        showLogsPanel() {
            this.closeCurrentMask();
            const logs = LogManager.logs;
            const keywordWhitelist = Array.from(currentConfig.keywordWhitelist);
            const whitelistSet = currentConfig.whitelist;
            const thirdPartyWhitelist = currentConfig.thirdPartyWhitelist;
            const logsHtml = logs.length > 0 ? logs.map((log, index) => {
                const isWhitelisted = whitelistSet.has(log.contentIdentifier);
                const isKeywordWhitelisted = !isWhitelisted && keywordWhitelist.some(kw => (log.content && log.content.includes(kw)) || (log.domain && log.domain.includes(kw)));
                const isThirdPartyWhitelisted = !isWhitelisted && !isKeywordWhitelisted && log.domain && urlCache.isWhitelisted(log.domain, thirdPartyWhitelist);
                const logEntryClass = 'log-entry' + (isWhitelisted ? ' whitelisted' : '') + (isKeywordWhitelisted ? ' keyword-whitelisted' : '') + (isThirdPartyWhitelisted ? ' whitelisted' : '');
                let domainHint = log.domain ? `<div style="font-size:11px;color:#999;margin-top:4px;">域名: ${escapeHtml(log.domain)}</div>` : '';
                return `
    <div class="${logEntryClass}" data-index="${index}">
        <button type="button" class="whitelist-btn" data-action="whitelistLog" data-index="${index}" ${isWhitelisted || isKeywordWhitelisted || isThirdPartyWhitelisted ? 'disabled style="background:#999;"' : ''}>${isWhitelisted || isKeywordWhitelisted || isThirdPartyWhitelisted ? '已加白' : '加白'}</button>
        <div class="log-module">${escapeHtml(log.module)} - ${escapeHtml(log.element)}</div>
        <div class="log-content">${escapeHtml(log.content)}</div>
        ${domainHint}
    </div>
    `;
            }).join('') : '<div style="text-align:center;color:#999;margin-top:20px;padding:20px;font-size:14px;">暂无拦截记录</div>';
            this.createPanel({
                title: `拦截日志 (${logs.length}条)`,
                contentHtml: `
    <div style="margin-bottom:10px;font-size:13px;color:#666;text-align:center;">关键词加白(添加包含关键词的脚本到白名单):</div>
    <div style="display:flex;gap:8px;margin-bottom:15px;">
        <input type="text" id="keywordWhitelistInput" placeholder="输入关键词,如: abc" style="flex:1;padding:8px;border-radius:8px;border:1px solid #ddd;outline:none;font-size:13px;">
        <button type="button" data-id="addKeywordWhitelist" class="primary" style="padding:8px 16px;">添加</button>
    </div>
    <div class="sub-panel">${logsHtml}</div>
    `,
                buttons: [],
                onBack: () => this.showSettings()
            });
            _setTimeout(() => {
                const maskElement = this.shadowRoot.querySelector('.mask:last-child');
                if (!maskElement) return;
                const input = maskElement.querySelector('#keywordWhitelistInput');
                if (input) {
                    input.focus();
                    input.addEventListener('keypress', (e) => {
                        if (e.key === 'Enter') {
                            const addBtn = maskElement.querySelector('[data-id="addKeywordWhitelist"]');
                            if (addBtn) addBtn.click();
                        }
                    });
                }
            }, 0);
        }
        showCSPPanel() {
            this.closeCurrentMask();
            const rulesHtml = currentConfig.cspRules.map(rule => `
    <div class="csp-rule">
        <span class="csp-name">${escapeHtml(rule.name)}</span>
        <label class="switch">
            <input type="checkbox" class="csp-toggle" data-id="${rule.id}" ${rule.enabled ? 'checked' : ''}>
            <span class="slider"></span>
        </label>
    </div>
    `).join('');
            this.createPanel({
                title: 'CSP策略管理',
                contentHtml: `
    <div style="margin-bottom:10px;font-size:13px;color:#666;text-align:center;">当前状态: ${currentConfig.modules.manageCSP ? '✅已启用' : '❌已禁用'}</div>
    <div class="sub-panel">${rulesHtml}</div>
    <div class="btn-group">
        <button type="button" data-id="enableCSP" ${currentConfig.modules.manageCSP ? 'disabled' : ''}>启用CSP</button>
        <button type="button" data-id="disableCSP" ${!currentConfig.modules.manageCSP ? 'disabled' : ''}>禁用CSP</button>
    </div>
    <div class="btn-group">
        <button type="button" data-id="allOn">全部开启</button>
        <button type="button" data-id="allOff">全部关闭</button>
    </div>
    `,
                buttons: [],
                onBack: () => this.showSettings()
            });
        }
        showThirdPartyPanel() {
            this.closeCurrentMask();
            const whitelist = currentConfig.thirdPartyWhitelist;
            const itemsHtml = whitelist.length > 0 ? whitelist.map((item, index) => `
    <div class="whitelist-item" data-index="${index}">
        <span class="whitelist-text">${escapeHtml(item)}</span>
        <button type="button" class="danger" data-action="deleteThirdPartyItem" data-index="${index}" style="padding:4px 8px; font-size:10px; border-radius:6px;">删除</button>
    </div>
    `).join('') : '<div style="text-align:center;color:#999;margin-top:20px;padding:20px;font-size:14px;">白名单为空</div>';
            this.createPanel({
                title: `第三方白名单 (${whitelist.length}项)`,
                contentHtml: `
    <div style="margin-bottom:10px;font-size:13px;color:#666;text-align:center;">已拦截的第三方域名可以添加到白名单中</div>
    <div class="sub-panel">${itemsHtml}</div>
    <div style="display:flex;gap:8px;">
        <input type="text" id="newWhitelist" placeholder="输入域名或URL..." style="flex:1;padding:8px;border-radius:8px;border:1px solid #ddd;outline:none;font-size:13px;">
        <button type="button" data-id="addWhitelist" class="primary" style="padding:8px 16px;">添加</button>
    </div>
    <button type="button" data-id="clearAllThirdParty" class="danger" style="margin-top:8px;">清空全部</button>
    `,
                buttons: [],
                onBack: () => this.showSettings()
            });
            _setTimeout(() => {
                const maskElement = this.shadowRoot.querySelector('.mask:last-child');
                if (!maskElement) return;
                const input = maskElement.querySelector('#newWhitelist');
                if (input) {
                    input.focus();
                    input.addEventListener('keypress', (e) => {
                        if (e.key === 'Enter') {
                            const addBtn = maskElement.querySelector('[data-id="addWhitelist"]');
                            if (addBtn) addBtn.click();
                        }
                    });
                }
            }, 0);
        }
    }

    function resetAllCachesAndStates() {
        if (centralScheduler) centralScheduler.clearCache();
        urlCache.clear();
        LogManager.clearLoggedIdentifiers();
        ProcessedElementsCache.clear();
        if (UIController.initialized) {
            UIController.modules.forEach(module => {
                if (module.enabled) {
                    module.disable();
                    module.enable();
                }
            });
            DynamicScriptInterceptor.disable();
            if (currentConfig.modules.blockDynamicScripts) {
                DynamicScriptInterceptor.enable();
            }
        }
    }

    class CentralScheduler {
        constructor(modules) {
            this.modules = modules;
            this.elementCheckCache = new WeakSet();
            this.urlCheckCache = new LRUCache(500, CONFIG.CACHE_TTL);
        }
        shouldProcessElement(element) {
            if (!Utils.isElement(element)) return false;
            if (this.elementCheckCache.has(element)) return false;
            if (ProcessedElementsCache.isProcessed(element)) return false;
            if (Utils.isParentProcessed(element)) return false;
            if (element.getAttribute('data-adblock-safe') === 'true') return false;
            return true;
        }
        processElement(element) {
            if (!this.shouldProcessElement(element)) return false;
            this.elementCheckCache.add(element);
            for (const module of this.modules) {
                if (module.enabled && module.checkElement(element)) {
                    ProcessedElementsCache.markAsProcessed(element);
                    return true;
                }
            }
            return false;
        }
        processUrl(url, type) {
            if (!url) return false;
            const cacheKey = `url_${url}_${type}`;
            if (this.urlCheckCache.has(cacheKey)) return this.urlCheckCache.get(cacheKey);
            let result = false;
            if (currentConfig.modules.interceptThirdParty) {
                if (shouldBlockResource(url)) {
                    result = true;
                }
            }
            this.urlCheckCache.set(cacheKey, result, CONFIG.CACHE_TTL);
            return result;
        }
        clearCache() {
            this.elementCheckCache = new WeakSet();
            this.urlCheckCache.clear();
            urlCache.clear();
        }
    }

    let inlineScriptsModule = null;
    let thirdPartyModule = null;
    let centralScheduler = null;

    const UIController = {
        initialized: false,
        mutationObserver: null,
        batchProcessingQueue: [],
        batchSize: CONFIG.BATCH_SIZE,
        isProcessingBatch: false,
        lastProcessTime: 0,
        panelManager: null,
        modules: [],
        init() {
            if (this.initialized) return;
            this.initialized = true;
            this.applyInitialModuleStates();
            this.registerMenuCommands();
            this.panelManager = new PanelManager();
            this.createModules();
            this.applyModuleSettings();
            this.setupObservers();
            this.setupResourceScan();
            const anyModuleEnabled = Object.values(currentConfig.modules).some(v => v === true);
            if (currentConfig.residualCleanupEnabled && anyModuleEnabled) {
                ResidualCleaner.init();
            }
            CSSUniversalHider.init();
            if (currentConfig.redirectBlockerEnabled) {
                installRedirectBlocker();
            }
            this.setupNavigationCacheCleaner();
        },
        setupNavigationCacheCleaner() {
            const cleanCache = () => {
                resetAllCachesAndStates();
            };
            _globals.addEventListener('popstate', cleanCache, {
                capture: true
            });
            const originalPushState = history.pushState;
            const originalReplaceState = history.replaceState;
            history.pushState = function(...args) {
                originalPushState.apply(this, args);
                cleanCache();
            };
            history.replaceState = function(...args) {
                originalReplaceState.apply(this, args);
                cleanCache();
            };
        },
        applyInitialModuleStates() {
            Object.keys(DEFAULT_MODULE_STATE).forEach(key => {
                if (currentConfig.modules[key] === undefined) {
                    currentConfig.modules[key] = DEFAULT_MODULE_STATE[key];
                }
            });
        },
        registerMenuCommands() {
            GM_registerMenuCommand('⚙️ 广告拦截设置面板', () => this.panelManager.showSettings());
            const spoofStatus = currentConfig.spoofUAEnabled ? '✅ 已启用' : '❌ 已禁用';
            GM_registerMenuCommand(`🍎 模拟UA ${spoofStatus}`, () => {
                currentConfig.spoofUAEnabled = !currentConfig.spoofUAEnabled;
                StorageManager.saveConfig();
                GM_notification({
                    text: `UA模拟已${currentConfig.spoofUAEnabled ? '启用' : '禁用'},刷新页面生效`,
                    title: '广告拦截器'
                });
                _location.reload();
            });
            GM_registerMenuCommand('🔄 重置所有设置', () => {
                if (confirm('确定重置所有设置吗?这将清空所有白名单并关闭所有模块,恢复到初始状态。')) {
                    StorageManager.resetAllSettings();
                    resetAllCachesAndStates();
                    GM_notification({
                        text: '所有设置已重置',
                        title: '广告拦截器'
                    });
                    _location.reload();
                }
            });
            GM_registerMenuCommand('🗑️ 清空所有白名单', () => {
                if (confirm('确定清空当前域名的所有白名单(包括日记白名单、第三方白名单、关键词白名单等)吗?')) {
                    Whitelisting.clearAllWhitelists();
                    resetAllCachesAndStates();
                    GM_notification({
                        text: '所有白名单已清空',
                        title: '广告拦截器'
                    });
                    _location.reload();
                }
            });
        },
        createModules() {
            inlineScriptsModule = new RemoveInlineScriptsModule();
            thirdPartyModule = new ThirdPartyInterceptionModule();
            this.modules = [
                inlineScriptsModule,
                new RemoveExternalScriptsModule(),
                new ScriptBlacklistModeModule(),
                thirdPartyModule
            ];
        },
        applyModuleSettings() {
            this.modules.forEach(module => module.init());
            DynamicScriptInterceptor.init();
            CSPModule.init();
            centralScheduler = new CentralScheduler(this.modules);
        },
        setupObservers() {
            const relevantModulesEnabled = this.modules.some(m => m.enabled) || currentConfig.modules.blockDynamicScripts;
            if (!relevantModulesEnabled) return;
            this.mutationObserver = new _MutationObserver(Utils.throttle((mutations) => {
                for (const mutation of mutations) {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType === 1 && !ProcessedElementsCache.isProcessed(node) && !Utils.isParentProcessed(node)) {
                            this.addToBatchProcessingQueue(node);
                        }
                    }
                }
            }, CONFIG.THROTTLE_LIMIT));
            this.mutationObserver.observe(_document.documentElement, {
                childList: true,
                subtree: true
            });
            this.processExistingElementsBatch();
        },
        setupResourceScan() {
            if (currentConfig.modules.interceptThirdParty) {
                _document.addEventListener('DOMContentLoaded', () => {
                    _setTimeout(() => this.scanExistingResources(), 1000);
                });
            }
        },
        scanExistingResources() {
            const selector = 'script[src], iframe[src], img[src], img[data-src], embed[src], object[data], link[href]';
            try {
                const elements = _document.querySelectorAll(selector);
                elements.forEach(element => {
                    if (ProcessedElementsCache.isProcessed(element) || Utils.isParentProcessed(element)) return;
                    const tagName = element.tagName;
                    let url = '';
                    if (tagName === 'SCRIPT') url = element.src;
                    else if (tagName === 'IFRAME') url = element.src;
                    else if (tagName === 'IMG') url = element.src || element.getAttribute('data-src');
                    else if (tagName === 'EMBED') url = element.src;
                    else if (tagName === 'OBJECT') url = element.data;
                    else if (tagName === 'LINK') url = element.href;
                    if (url && shouldBlockResource(url)) {
                        const contentIdentifier = Utils.getContentIdentifier(element);
                        if (contentIdentifier && !currentConfig.whitelist.has(contentIdentifier)) {
                            LogManager.add('interceptThirdParty', element, {
                                type: 'THIRD_PARTY_SCAN',
                                detail: `扫描发现: ${tagName}: ${Utils.truncateString(url, 500)}`
                            });
                        }
                    }
                });
            } catch (e) {}
        },
        addToBatchProcessingQueue(element) {
            if (element.getAttribute && element.getAttribute('data-adblock-safe') === 'true') {
                ProcessedElementsCache.markAsProcessed(element);
                return;
            }
            this.batchProcessingQueue.push(element);
            if (!this.isProcessingBatch) {
                this.processBatch();
            }
        },
        processBatch() {
            if (!centralScheduler) return;
            this.isProcessingBatch = true;
            let dynamicBatchSize = this.batchSize;
            const processChunk = () => {
                const now = performance.now();
                if (this.lastProcessTime > 0) {
                    const frameTime = now - this.lastProcessTime;
                    dynamicBatchSize = frameTime > 16 ? Math.max(5, dynamicBatchSize / 2) : Math.min(50, dynamicBatchSize + 2);
                }
                this.lastProcessTime = now;
                const chunk = this.batchProcessingQueue.splice(0, dynamicBatchSize);
                chunk.forEach(node => centralScheduler.processElement(node));
                if (this.batchProcessingQueue.length > 0) {
                    _requestAnimationFrame(processChunk);
                } else {
                    this.isProcessingBatch = false;
                }
            };
            _requestAnimationFrame(processChunk);
        },
        processExistingElementsBatch() {
            const selector = 'script, iframe, img, a[href], style, link[rel="preload"], link[rel="prefetch"], embed, object, link[href]';
            const elementsToProcess = Array.from(_document.querySelectorAll(selector));
            const processInChunks = () => {
                const chunk = elementsToProcess.splice(0, this.batchSize * 2);
                chunk.forEach(element => {
                    if (!ProcessedElementsCache.isProcessed(element) && !Utils.isParentProcessed(element)) {
                        this.addToBatchProcessingQueue(element);
                    }
                });
                if (elementsToProcess.length > 0) {
                    _requestIdleCallback(processInChunks);
                }
            };
            _requestIdleCallback(processInChunks);
        }
    };

    function spoofPlatform(p = 'Mac') {
        try {
            Object.defineProperty(navigator, 'platform', {
                get: () => p,
                configurable: true,
                enumerable: true
            });
        } catch (e) {}
    }

    StorageManager.loadConfig();

    if (currentConfig.spoofUAEnabled) {
        spoofPlatform('Mac');
    }

    if (currentConfig.modules.blockDynamicScripts) DynamicScriptInterceptor.init();

    function suppressUIForIframe() {
        if (!currentConfig.iframeUIFix) return false;
        try {
            return _globals.self !== _globals.top;
        } catch (e) {
            return true;
        }
    }

    function initModulesWithoutUI() {
        if (!UIController.initialized) {
            UIController.initialized = true;
            UIController.applyInitialModuleStates();
            UIController.createModules();
            UIController.applyModuleSettings();
            UIController.setupObservers();
            UIController.setupResourceScan();
            const anyModuleEnabled = Object.values(currentConfig.modules).some(v => v === true);
            if (currentConfig.residualCleanupEnabled && anyModuleEnabled) {
                ResidualCleaner.init();
            }
            CSSUniversalHider.init();
            if (currentConfig.redirectBlockerEnabled) {
                installRedirectBlocker();
            }
            UIController.setupNavigationCacheCleaner();
        }
    }

    function safeInit() {
        if (_document.documentElement) {
            if (suppressUIForIframe()) {
                initModulesWithoutUI();
            } else {
                UIController.init();
            }
            return true;
        }
        return false;
    }

    if (!safeInit()) {
        const mo = new _MutationObserver(() => {
            if (safeInit()) mo.disconnect();
        });
        mo.observe(_document, {
            childList: true
        });
        _document.addEventListener('DOMContentLoaded', () => {
            if (!safeInit()) _setTimeout(safeInit, 100);
        }, {
            once: true
        });
        _setTimeout(() => {
            if (!safeInit()) {
                if (suppressUIForIframe()) {
                    initModulesWithoutUI();
                } else {
                    UIController.init();
                }
            }
        }, 5000);
    }
})();