小说漫画网页广告拦截器

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

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

(function() {
    'use strict';
    const DEFAULT_MODULE_STATE = {
        specialUA: true,
        removeInlineScripts: false,
        removeExternalScripts: false,
        interceptThirdPartyResources: false,
        manageCSP: false,
    };
    const MODULE_NAMES = {
        specialUA: '添加特殊UA',
        removeInlineScripts: '移除内嵌脚本',
        removeExternalScripts: '移除外联脚本',
        interceptThirdPartyResources: '拦截第三方资源',
        manageCSP: 'CSP策略管理',
    };
    const DEFAULT_CSP_RULES_TEMPLATE = [
        { id: 1, name: '仅允许同源脚本', rule: "script-src 'self'", enabled: true },
        { id: 2, name: '禁止内联脚本', rule: "script-src 'unsafe-inline'", enabled: false },
        { id: 3, name: '禁止eval执行', rule: "script-src 'unsafe-eval'", enabled: false },
        { id: 4, name: '阻止外部样式加载', rule: "style-src 'self'", enabled: false },
        { id: 5, name: '阻止内联样式执行', rule: "style-src 'unsafe-inline'", enabled: false },
        { id: 6, name: '阻止外部图片加载', rule: "img-src 'self'", enabled: true },
        { id: 7, name: '禁止所有框架加载', rule: "frame-src 'none'", enabled: false },
        { id: 8, name: '阻止媒体资源加载', rule: "media-src 'none'", enabled: false },
        { id: 9, name: '阻止对象嵌入', rule: "object-src 'none'", enabled: false }
    ];
    const CONFIG_STORAGE_KEY = `customAdBlockerConfig_${location.hostname}`;
    let currentConfig = {
        modules: { ...DEFAULT_MODULE_STATE },
        cspRules: DEFAULT_CSP_RULES_TEMPLATE.map(rule => ({ ...rule })),
        whitelist: {},
    };
    const Utils = {
        throttle(fn, delay) {
            let last = 0;
            return function(...args) {
                const now = Date.now();
                if (now - last >= delay) {
                    fn.apply(this, args);
                    last = now;
                }
            };
        },
        debounce(fn, delay) {
            let timer = null;
            return function(...args) {
                clearTimeout(timer);
                timer = setTimeout(() => fn.apply(this, args), delay);
            };
        },
        truncateString(str, maxLength) {
            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 Utils.truncateString(scriptElement.textContent.replace(/\s+/g, ' '), 100);
        },
        getIframeSrcPreview(iframeElement) {
            if (!iframeElement || iframeElement.tagName !== 'IFRAME') return '';
            return Utils.truncateString(iframeElement.src, 100);
        },
        isThirdPartyHost(url, currentHost) {
            if (!url || typeof url !== 'string') return false;
            if (url.startsWith('data:') || url.startsWith('blob:') || url.startsWith('about:blank')) return false;
            try {
                const resourceHost = new URL(url, location.href).hostname;
                return !(resourceHost.endsWith(`.${currentHost}`) || resourceHost === currentHost);
            } catch (e) {
                return true;
            }
        },
        getResourceURL(url) {
            if (!url) return '';
            try {
                return new URL(url, location.href).href;
            } catch (e) {
                return url;
            }
        }
    };
    const LogManager = {
        logs: [],
        maxLogs: 100,
        logEntryData: {},
        loggedContentIdentifiers: new Set(),
        add(moduleKey, element, reason) {
            if (!currentConfig.modules.interceptThirdPartyResources && !currentConfig.modules.removeInlineScripts && !currentConfig.modules.removeExternalScripts) {
                return;
            }
            if (!Utils.isElement(element) && element !== null) return;
            const currentDomain = Utils.getCurrentHostname();
            if (Whitelisting.isElementWhitelisted(element) || Whitelisting.isReasonWhitelisted(reason)) {
                return;
            }
            let elementIdentifier = '[未知元素]';
            let interceptedContent = '[无法获取内容]';
            let contentIdentifier = '';
            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}`;
                if (tagName === 'SCRIPT') {
                    if (element.src) {
                        interceptedContent = `SRC: ${Utils.truncateString(element.src, 100)}`;
                        contentIdentifier = `SCRIPT_SRC: ${Utils.truncateString(element.src, 100)}`;
                    } else {
                        interceptedContent = Utils.getScriptContentPreview(element);
                        contentIdentifier = `SCRIPT_CONTENT: ${Utils.truncateString(element.textContent.replace(/\s+/g, ' '), 100)}`;
                    }
                } else if (tagName === 'IFRAME') {
                    interceptedContent = Utils.getIframeSrcPreview(element);
                    contentIdentifier = `IFRAME_SRC: ${Utils.truncateString(element.src, 100)}`;
                } else if (tagName === 'IMG') {
                    const src = element.src || element.dataset.src;
                    interceptedContent = Utils.truncateString(src || '', 100);
                    if (src) {
                        contentIdentifier = `IMG_SRC: ${Utils.truncateString(src, 100)}`;
                    }
                } else if (tagName === 'A') {
                    interceptedContent = Utils.truncateString(element.href || '', 100);
                    if (element.href) {
                        contentIdentifier = `A_HREF: ${Utils.truncateString(element.href, 100)}`;
                    }
                } else {
                    interceptedContent = Utils.truncateString(element.outerHTML, 100);
                }
            } else if (reason && typeof reason.detail === 'string') {
                interceptedContent = Utils.truncateString(reason.detail, 100);
                if (reason.type) {
                    elementIdentifier = `[${reason.type}]`;
                }
                if (reason.detail.startsWith('SRC:')) {
                    contentIdentifier = `${reason.type || 'INTERCEPTED'}_SRC: ${Utils.truncateString(reason.detail.substring(4).trim(), 100)}`;
                } else if (reason.detail.startsWith('URL:')) {
                    contentIdentifier = `${reason.type || 'INTERCEPTED'}_URL: ${Utils.truncateString(reason.detail.substring(5).trim(), 100)}`;
                } else {
                    contentIdentifier = `LOG_DETAIL: ${Utils.truncateString(reason.detail, 100)}`;
                }
            }
            if (!contentIdentifier) return;
            if (this.loggedContentIdentifiers.has(contentIdentifier)) {
                return;
            }
            const logId = this.logs.length + 1;
            this.logs.push({
                id: logId,
                module: MODULE_NAMES[moduleKey] || moduleKey,
                element: elementIdentifier,
                content: interceptedContent,
            });
            this.logEntryData[logId] = {
                contentIdentifier: contentIdentifier,
                element: element,
                reason: reason,
            };
            this.loggedContentIdentifiers.add(contentIdentifier);
            if (this.logs.length > this.maxLogs) {
                const removedLogId = this.logs[0].id;
                const removedLogEntry = this.logs.shift();
                delete this.logEntryData[removedLogId];
                const removedContentIdentifier = (() => {
                    if (removedLogEntry.content.startsWith('SRC: ')) return `SCRIPT_SRC: ${removedLogEntry.content.substring(5)}`;
                    if (removedLogEntry.content.startsWith('内容: ')) return `SCRIPT_CONTENT: ${removedLogEntry.content.substring(4)}`;
                    if (removedLogEntry.content.startsWith('URL: ')) return `FETCH_URL: ${removedLogEntry.content.substring(5)}`;
                    return removedLogEntry.content;
                })();
                this.loggedContentIdentifiers.delete(removedContentIdentifier);
            }
        },
        showInAlert() {
            const currentDomain = Utils.getCurrentHostname();
            const logEntries = this.logs.map(log =>
                `序号: ${log.id}\n` +
                `模块: ${log.module}\n` +
                `元素: ${log.element}\n` +
                `内容: ${log.content}`
            ).join('\n\n');
            const promptMessage = `广告拦截日志(最近${this.logs.length}条):\n\n${logEntries || '暂无日志'}\n\n` +
                `--- 白名单操作 ---\n` +
                `输入序号(如 1-3, 1,3,5)将对应内容加入当前域名 (${currentDomain}) 白名单。\n` +
                `输入 0 清空当前域名 (${currentDomain}) 的所有白名单条目。\n` +
                `请输入您的选择:`;
            let input = prompt(promptMessage, "");
            if (input === null) return;
            input = input.trim();
            if (input === "0") {
                if (confirm(`确定清空当前域名 (${currentDomain}) 的白名单吗?`)) {
                    Whitelisting.clearDomainWhitelist(currentDomain);
                    StorageManager.saveConfig();
                    alert("当前域名的白名单已清空。页面将刷新。");
                    location.reload();
                }
            } else {
                const indicesToWhitelist = new Set();
                const parts = input.replace(/,/g, ',').split(/[\s,]+/);
                for (const part of parts) {
                    if (part.includes('-')) {
                        const range = part.split('-').map(Number);
                        if (range.length === 2 && !isNaN(range[0]) && !isNaN(range[1])) {
                            const start = Math.min(range[0], range[1]);
                            const end = Math.max(range[0], range[1]);
                            for (let i = start; i <= end; i++) {
                                indicesToWhitelist.add(i);
                            }
                        }
                    } else {
                        const num = parseInt(part, 10);
                        if (!isNaN(num)) {
                            indicesToWhitelist.add(num);
                        }
                    }
                }
                let addedCount = 0;
                indicesToWhitelist.forEach(index => {
                    const logEntryInfo = this.logEntryData[index];
                    if (logEntryInfo && logEntryInfo.contentIdentifier) {
                        const { contentIdentifier, element, reason } = logEntryInfo;
                        if (!Whitelisting.isContentWhitelisted(currentDomain, contentIdentifier)) {
                            Whitelisting.add(currentDomain, contentIdentifier);
                            addedCount++;
                        }
                    }
                });
                if (addedCount > 0) {
                    StorageManager.saveConfig();
                    alert(`${addedCount} 项内容已添加到当前域名 (${currentDomain}) 的白名单。页面将刷新。`);
                    location.reload();
                } else if (input.length > 0) {
                    alert("未找到匹配的日志序号或内容已在白名单中。");
                }
            }
        }
    };
    const Whitelisting = {
        isElementWhitelisted(element) {
            if (!element || !Utils.isElement(element)) return false;
            const currentDomain = Utils.getCurrentHostname();
            const elementContent = (() => {
                if (element.tagName === 'SCRIPT' && !element.src) return `SCRIPT_CONTENT: ${Utils.getScriptContentPreview(element)}`;
                if (element.tagName === 'SCRIPT' && element.src) return `SCRIPT_SRC: ${Utils.truncateString(element.src, 100)}`;
                if (element.tagName === 'IFRAME' && element.src) return `IFRAME_SRC: ${Utils.truncateString(element.src, 100)}`;
                if (element.tagName === 'IMG' && (element.src || element.dataset.src)) return `IMG_SRC: ${Utils.truncateString(element.src || element.dataset.src, 100)}`;
                if (element.tagName === 'A' && element.href) return `A_HREF: ${Utils.truncateString(element.href, 100)}`;
                return null;
            })();
            if (elementContent && currentDomain && currentConfig.whitelist[currentDomain]) {
                return currentConfig.whitelist[currentDomain].includes(elementContent);
            }
            return false;
        },
        isReasonWhitelisted(reason) {
            if (!reason || !reason.detail || typeof reason.detail !== 'string') return false;
            const currentDomain = Utils.getCurrentHostname();
            const identifier = (() => {
                if (reason.detail.startsWith('SRC:')) {
                    return `${reason.type || 'INTERCEPTED'}_SRC: ${Utils.truncateString(reason.detail.substring(4).trim(), 100)}`;
                } else if (reason.detail.startsWith('URL:')) {
                    return `${reason.type || 'INTERCEPTED'}_URL: ${Utils.truncateString(reason.detail.substring(5).trim(), 100)}`;
                }
                return null;
            })();
            if (identifier && currentDomain && currentConfig.whitelist[currentDomain]) {
                return currentConfig.whitelist[currentDomain].includes(identifier);
            }
            return false;
        },
        isContentWhitelisted(domain, content) {
            if (!domain || !content) return false;
            return currentConfig.whitelist[domain] && currentConfig.whitelist[domain].includes(content);
        },
        add(domain, content) {
            if (!domain || !content || content.trim() === '') return;
            if (!currentConfig.whitelist[domain]) {
                currentConfig.whitelist[domain] = [];
            }
            if (!currentConfig.whitelist[domain].includes(content)) {
                currentConfig.whitelist[domain].push(content);
            }
        },
        clearDomainWhitelist(domain) {
            if (currentConfig.whitelist[domain]) {
                currentConfig.whitelist[domain] = [];
            }
        },
        clearAllWhitelists() {
            currentConfig.whitelist = {};
        }
    };
    const StorageManager = {
        loadConfig() {
            try {
                const savedConfig = JSON.parse(GM_getValue(CONFIG_STORAGE_KEY, '{}'));
                if (savedConfig) {
                    if (savedConfig.modules) {
                        Object.assign(currentConfig.modules, savedConfig.modules);
                    }
                    if (savedConfig.cspRules) {
                        currentConfig.cspRules = savedConfig.cspRules.map(rule => ({ ...rule }));
                    }
                    if (savedConfig.whitelist) {
                        currentConfig.whitelist = savedConfig.whitelist;
                    }
                }
            } catch (e) {
                console.error("AdBlocker: Error loading config, using defaults:", e);
                Object.assign(currentConfig.modules, DEFAULT_MODULE_STATE);
                currentConfig.cspRules = DEFAULT_CSP_RULES_TEMPLATE.map(rule => ({ ...rule }));
                currentConfig.whitelist = {};
            }
            if (currentConfig.modules.specialUA === undefined) {
                currentConfig.modules.specialUA = true;
            }
        },
        saveConfig() {
            try {
                GM_setValue(CONFIG_STORAGE_KEY, JSON.stringify({
                    modules: currentConfig.modules,
                    cspRules: currentConfig.cspRules,
                    whitelist: currentConfig.whitelist
                }));
            } catch (e) {
                console.error("AdBlocker: Error saving config:", e);
            }
        }
    };
    const SpecialUAModule = {
        originalNavigator: window.navigator,
        fakeUA: 'NokiaE7-00/5.0 UCWEB/2.0 Mozilla/5.0 (Symbian/3; Series60/5.2; Windows Phone 10.0; Android 14; Microsoft; Lumia 950 XL Dual SIM; Java) Gecko/131 Firefox/131 SearchCraft/3.10.2 (Baidu; P1 13) baiduboxapp/4.3.0.10',
        fakePlatform: 'Win32',
        fakeAppVersion: 'NokiaE7-00/5.0 UCWEB/2.0 Mozilla/5.0 (Symbian/3; Series60/5.2; Windows Phone 10.0; Android 14; Microsoft; Lumia 950 XL Dual SIM; Java) Gecko/131 Firefox/131 SearchCraft/3.10.2 (Baidu; P1 13) baiduboxapp/4.3.0.10',
        navigatorProxy: null,
        originalDescriptor: null,
        init() {
            if (currentConfig.modules.specialUA) {
                this.enable();
            } else {
                this.disable();
            }
        },
        enable() {
            if (!this.navigatorProxy) {
                this.navigatorProxy = new Proxy(this.originalNavigator, {
                    get: (target, prop) => {
                        if (prop === 'userAgent') return this.fakeUA;
                        if (prop === 'platform') return this.fakePlatform;
                        if (prop === 'appVersion') return this.fakeAppVersion;
                        return Reflect.get(target, prop);
                    }
                });
            }
            if (window.navigator !== this.navigatorProxy) {
                try {
                    this.originalDescriptor = Object.getOwnPropertyDescriptor(window, 'navigator');
                    Object.defineProperty(window, 'navigator', {
                        value: this.navigatorProxy,
                        configurable: true,
                        writable: this.originalDescriptor ? this.originalDescriptor.writable : false
                    });
                } catch (e) {
                    console.warn("AdBlocker: Failed to override navigator for special UA.");
                }
            }
        },
        disable() {
            if (this.navigatorProxy && window.navigator === this.navigatorProxy) {
                try {
                    if (this.originalDescriptor) {
                        Object.defineProperty(window, 'navigator', this.originalDescriptor);
                    } else {
                        Object.defineProperty(window, 'navigator', {
                            value: this.originalNavigator,
                            configurable: true,
                            writable: false
                        });
                    }
                } catch (e) {
                    console.warn("AdBlocker: Failed to restore original navigator.");
                }
            }
        },
        check(element) {
            return false;
        }
    };
    const RemoveInlineScriptsModule = {
        mutationObserver: null,
        init() {
            if (currentConfig.modules.removeInlineScripts) {
                this.enable();
            } else {
                this.disable();
            }
        },
        enable() {
            this.mutationObserver = new MutationObserver(mutations => {
                mutations.forEach(mutation => {
                    mutation.addedNodes.forEach(node => {
                        if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'SCRIPT' && !node.src) {
                            if (node.dataset.adblockProcessed === 'true') return;
                            const elementContent = Utils.getScriptContentPreview(node);
                            const contentIdentifier = `SCRIPT_CONTENT: ${elementContent}`;
                            if (!Whitelisting.isContentWhitelisted(Utils.getCurrentHostname(), contentIdentifier)) {
                                LogManager.add('removeInlineScripts', node, { type: '内嵌脚本移除', detail: `内容: ${elementContent}` });
                                node.dataset.adblockProcessed = 'true';
                                node.remove();
                            }
                        }
                    });
                });
            });
            this.mutationObserver.observe(document.documentElement, { childList: true, subtree: true });
            document.querySelectorAll('script:not([src])').forEach(script => {
                if (script.dataset.adblockProcessed === 'true') return;
                const elementContent = Utils.getScriptContentPreview(script);
                const contentIdentifier = `SCRIPT_CONTENT: ${elementContent}`;
                if (!Whitelisting.isContentWhitelisted(Utils.getCurrentHostname(), contentIdentifier)) {
                    LogManager.add('removeInlineScripts', script, { type: '内嵌脚本移除', detail: `内容: ${elementContent}` });
                    script.dataset.adblockProcessed = 'true';
                    script.remove();
                }
            });
        },
        disable() {
            if (this.mutationObserver) {
                this.mutationObserver.disconnect();
                this.mutationObserver = null;
            }
        },
        check(element) {
            if (!currentConfig.modules.removeInlineScripts) return false;
            if (element.dataset.adblockProcessed === 'true') return false;
            if (element.tagName === 'SCRIPT' && !element.src) {
                const elementContent = Utils.getScriptContentPreview(element);
                const contentIdentifier = `SCRIPT_CONTENT: ${elementContent}`;
                if (!Whitelisting.isContentWhitelisted(Utils.getCurrentHostname(), contentIdentifier)) {
                    LogManager.add('removeInlineScripts', element, { type: '内嵌脚本移除', detail: `内容: ${elementContent}` });
                    element.dataset.adblockProcessed = 'true';
                    return true;
                }
            }
            return false;
        }
    };
    const RemoveExternalScriptsModule = {
        mutationObserver: null,
        init() {
            if (currentConfig.modules.removeExternalScripts) {
                this.enable();
            } else {
                this.disable();
            }
        },
        enable() {
            this.mutationObserver = new MutationObserver(mutations => {
                mutations.forEach(mutation => {
                    mutation.addedNodes.forEach(node => {
                        if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'SCRIPT' && node.src) {
                            if (node.dataset.adblockProcessed === 'true') return;
                            const src = Utils.truncateString(node.src, 100);
                            const contentIdentifier = `SCRIPT_SRC: ${src}`;
                            if (!Whitelisting.isContentWhitelisted(Utils.getCurrentHostname(), contentIdentifier)) {
                                LogManager.add('removeExternalScripts', node, { type: '外联脚本移除', detail: `SRC: ${src}` });
                                node.dataset.adblockProcessed = 'true';
                                node.remove();
                            }
                        }
                    });
                });
            });
            this.mutationObserver.observe(document.documentElement, { childList: true, subtree: true });
            document.querySelectorAll('script[src]').forEach(script => {
                if (script.dataset.adblockProcessed === 'true') return;
                const src = Utils.truncateString(script.src, 100);
                const contentIdentifier = `SCRIPT_SRC: ${src}`;
                if (!Whitelisting.isContentWhitelisted(Utils.getCurrentHostname(), contentIdentifier)) {
                    LogManager.add('removeExternalScripts', script, { type: '外联脚本移除', detail: `SRC: ${src}` });
                    script.dataset.adblockProcessed = 'true';
                    script.remove();
                }
            });
        },
        disable() {
            if (this.mutationObserver) {
                this.mutationObserver.disconnect();
                this.mutationObserver = null;
            }
        },
        check(element) {
            if (!currentConfig.modules.removeExternalScripts) return false;
            if (element.dataset.adblockProcessed === 'true') return false;
            if (element.tagName === 'SCRIPT' && element.src) {
                const src = Utils.truncateString(element.src, 100);
                const contentIdentifier = `SCRIPT_SRC: ${src}`;
                if (!Whitelisting.isContentWhitelisted(Utils.getCurrentHostname(), contentIdentifier)) {
                    LogManager.add('removeExternalScripts', element, { type: '外联脚本移除', detail: `SRC: ${src}` });
                    element.dataset.adblockProcessed = 'true';
                    return true;
                }
            }
            return false;
        }
    };
    const ThirdPartyModule = {
        originalFetch: null,
        originalXhrOpen: null,
        originalXhrSend: null,
        originalCreateElement: null,
        originalAppendChild: null,
        originalDocumentWrite: null,
        overrideEval: false,
        overrideFunction: false,
        init() {
            if (currentConfig.modules.interceptThirdPartyResources) {
                this.enable();
            } else {
                this.disable();
            }
        },
        enable() {
            this.overrideEval = !currentConfig.cspRules.find(rule => rule.id === 3)?.enabled;
            this.overrideFunction = !currentConfig.cspRules.find(rule => rule.id === 3)?.enabled;
            this.originalFetch = window.fetch;
            window.fetch = (input, init) => {
                if (!currentConfig.modules.interceptThirdPartyResources) {
                    return this.originalFetch.apply(this, arguments);
                }
                const url = typeof input === 'string' ? input : input.url;
                const resourceURL = Utils.getResourceURL(url);
                const contentIdentifier = `FETCH_URL: ${Utils.truncateString(resourceURL, 100)}`;
                if (Utils.isThirdPartyHost(resourceURL, Utils.getCurrentHostname())) {
                    if (!Whitelisting.isContentWhitelisted(Utils.getCurrentHostname(), contentIdentifier)) {
                        LogManager.add('interceptThirdPartyResources', null, { type: '第三方请求拦截', detail: `URL: ${Utils.truncateString(resourceURL, 100)}` });
                        return Promise.reject(new Error('Third-party resource blocked by AdBlocker.'));
                    }
                }
                return this.originalFetch.apply(this, arguments);
            };
            this.originalXhrOpen = XMLHttpRequest.prototype.open;
            XMLHttpRequest.prototype.open = function(method, url) {
                if (!currentConfig.modules.interceptThirdPartyResources) {
                    return this.originalXhrOpen.apply(this, arguments);
                }
                const resourceURL = Utils.getResourceURL(url);
                const contentIdentifier = `XHR_URL: ${Utils.truncateString(resourceURL, 100)}`;
                if (Utils.isThirdPartyHost(resourceURL, Utils.getCurrentHostname())) {
                    if (!Whitelisting.isContentWhitelisted(Utils.getCurrentHostname(), contentIdentifier)) {
                        LogManager.add('interceptThirdPartyResources', null, { type: '第三方请求拦截', detail: `URL: ${Utils.truncateString(resourceURL, 100)}` });
                        this._adblockBlocked = true;
                        return;
                    }
                }
                return this.originalXhrOpen.apply(this, arguments);
            };
            this.originalXhrSend = XMLHttpRequest.prototype.send;
            XMLHttpRequest.prototype.send = function() {
                if (this._adblockBlocked) {
                    return;
                }
                this.originalXhrSend.apply(this, arguments);
            };
            this.originalCreateElement = document.createElement;
            document.createElement = (tagName, options) => {
                const element = this.originalCreateElement.call(document, tagName, options);
                if (!currentConfig.modules.interceptThirdPartyResources || element.dataset.adblockProcessed === 'true') {
                    return element;
                }
                const processElement = (el) => {
                    const src = el.src || el.getAttribute('data-src') || el.href || el.action || '';
                    if (src) {
                        const resourceURL = Utils.getResourceURL(src);
                        if (Utils.isThirdPartyHost(resourceURL, Utils.getCurrentHostname())) {
                            const contentIdentifier = `${el.tagName}_SRC: ${Utils.truncateString(resourceURL, 100)}`;
                            if (!Whitelisting.isContentWhitelisted(Utils.getCurrentHostname(), contentIdentifier)) {
                                LogManager.add('interceptThirdPartyResources', el, { type: '第三方资源拦截', detail: `SRC: ${Utils.truncateString(resourceURL, 100)}` });
                                el.dataset.adblockProcessed = 'true';
                                el.remove();
                                return true;
                            }
                        }
                    }
                    return false;
                };
                if (['SCRIPT', 'IFRAME', 'IMG', 'A', 'LINK', 'FORM', 'VIDEO', 'AUDIO', 'SOURCE'].includes(tagName.toUpperCase())) {
                    if (processElement(element)) return element;
                }
                return element;
            };
            this.originalAppendChild = Node.prototype.appendChild;
            Node.prototype.appendChild = function(node) {
                if (!currentConfig.modules.interceptThirdPartyResources || node.dataset.adblockProcessed === 'true') {
                    return this.originalAppendChild.call(this, node);
                }
                if (node.nodeType === Node.ELEMENT_NODE) {
                    const tagName = node.tagName;
                    const src = node.src || node.getAttribute('data-src') || node.href || node.action || '';
                    if (src) {
                        const resourceURL = Utils.getResourceURL(src);
                        if (Utils.isThirdPartyHost(resourceURL, Utils.getCurrentHostname())) {
                            const contentIdentifier = `${tagName}_SRC: ${Utils.truncateString(resourceURL, 100)}`;
                            if (!Whitelisting.isContentWhitelisted(Utils.getCurrentHostname(), contentIdentifier)) {
                                LogManager.add('interceptThirdPartyResources', node, { type: '第三方资源拦截', detail: `SRC: ${Utils.truncateString(resourceURL, 100)}` });
                                node.dataset.adblockProcessed = 'true';
                                return node;
                            }
                        }
                    }
                }
                return this.originalAppendChild.call(this, node);
            };
            this.originalDocumentWrite = document.write;
            document.write = function(content) {
                if (!currentConfig.modules.interceptThirdPartyResources) {
                    return this.originalDocumentWrite.call(document, content);
                }
                const tempDiv = document.createElement('div');
                tempDiv.innerHTML = content;
                const scripts = tempDiv.querySelectorAll('script[src]');
                for (const script of scripts) {
                    const src = script.src;
                    if (src) {
                        const resourceURL = Utils.getResourceURL(src);
                        if (Utils.isThirdPartyHost(resourceURL, Utils.getCurrentHostname())) {
                            const contentIdentifier = `DOC_WRITE_SCRIPT_SRC: ${Utils.truncateString(resourceURL, 100)}`;
                            if (!Whitelisting.isContentWhitelisted(Utils.getCurrentHostname(), contentIdentifier)) {
                                LogManager.add('interceptThirdPartyResources', null, { type: '第三方脚本通过document.write拦截', detail: `SRC: ${Utils.truncateString(resourceURL, 100)}` });
                                script.remove();
                                return;
                            }
                        }
                    }
                }
                if (tempDiv.innerHTML !== content) {
                    content = tempDiv.innerHTML;
                }
                this.originalDocumentWrite.call(document, content);
            }.bind(this);
            this.overrideEvalAndFunction();
        },
        disable() {
            if (this.originalFetch) window.fetch = this.originalFetch;
            if (this.originalXhrOpen) XMLHttpRequest.prototype.open = this.originalXhrOpen;
            if (this.originalXhrSend) XMLHttpRequest.prototype.send = this.originalXhrSend;
            if (this.originalCreateElement) document.createElement = this.originalCreateElement;
            if (this.originalAppendChild) Node.prototype.appendChild = this.originalAppendChild;
            if (this.originalDocumentWrite) document.write = this.originalDocumentWrite;
            this.restoreEvalAndFunction();
        },
        overrideEvalAndFunction() {
            if (this.overrideEval) {
                const originalEval = unsafeWindow.eval;
                unsafeWindow.eval = function(code) {
                    const contentIdentifier = `EVAL_CODE: ${Utils.truncateString(code.replace(/\s+/g, ' '), 100)}`;
                    if (Utils.isThirdPartyHost(location.href, Utils.getCurrentHostname())) {
                        if (!Whitelisting.isContentWhitelisted(Utils.getCurrentHostname(), contentIdentifier)) {
                            LogManager.add('interceptThirdPartyResources', null, { type: 'eval拦截', detail: `代码: ${Utils.truncateString(code, 100)}` });
                            return undefined;
                        }
                    }
                    return originalEval.call(unsafeWindow, code);
                };
            }
            if (this.overrideFunction) {
                const originalFunction = unsafeWindow.Function;
                unsafeWindow.Function = function(...args) {
                    const code = args.length > 0 ? args[args.length - 1] : '';
                    const contentIdentifier = `FUNCTION_CODE: ${Utils.truncateString(code.replace(/\s+/g, ' '), 100)}`;
                    if (Utils.isThirdPartyHost(location.href, Utils.getCurrentHostname())) {
                        if (!Whitelisting.isContentWhitelisted(Utils.getCurrentHostname(), contentIdentifier)) {
                            LogManager.add('interceptThirdPartyResources', null, { type: 'Function构造拦截', detail: `代码: ${Utils.truncateString(code, 100)}` });
                            return () => {};
                        }
                    }
                    return originalFunction.apply(unsafeWindow, args);
                };
            }
        },
        restoreEvalAndFunction() {
            if (this.overrideEval) unsafeWindow.eval = unsafeWindow.originalEval;
            if (this.overrideFunction) unsafeWindow.Function = unsafeWindow.originalFunction;
        },
        check(element) {
            if (!currentConfig.modules.interceptThirdPartyResources || element.dataset.adblockProcessed === 'true') return false;
            const processElement = (el) => {
                const src = el.src || el.getAttribute('data-src') || el.href || el.action || '';
                if (src) {
                    const resourceURL = Utils.getResourceURL(src);
                    if (Utils.isThirdPartyHost(resourceURL, Utils.getCurrentHostname())) {
                        const contentIdentifier = `${el.tagName}_SRC: ${Utils.truncateString(resourceURL, 100)}`;
                        if (!Whitelisting.isContentWhitelisted(Utils.getCurrentHostname(), contentIdentifier)) {
                            LogManager.add('interceptThirdPartyResources', el, { type: '第三方资源拦截', detail: `SRC: ${Utils.truncateString(resourceURL, 100)}` });
                            el.dataset.adblockProcessed = 'true';
                            return true;
                        }
                    }
                }
                return false;
            };
            if (['SCRIPT', 'IFRAME', 'IMG', 'A', 'LINK', 'FORM', 'VIDEO', 'AUDIO', 'SOURCE'].includes(element.tagName.toUpperCase())) {
                if (processElement(element)) return true;
            }
            return false;
        }
    };
    const CSPModule = {
        init() {
        },
        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 policyString = enabledRules.map(rule => rule.rule).join('; ');
            const meta = document.createElement('meta');
            meta.httpEquiv = "Content-Security-Policy";
            meta.content = policyString;
            if (document.head) {
                document.head.appendChild(meta);
            } else {
                document.documentElement.prepend(meta);
            }
        },
        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;
            }
        },
        showManagementPanel() {
            const rulesDisplay = currentConfig.cspRules
                .map(r => `${r.id}. ${r.name} (${r.enabled ? '✅' : '❌'})`)
                .join('\n');
            const promptMessage = `CSP策略管理:\n` +
                `当前状态: ${currentConfig.modules.manageCSP ? '启用' : '禁用'}\n\n` +
                `可用规则:\n${rulesDisplay}\n\n` +
                `输入指令:\n` +
                `enable: 启用CSP策略\n` +
                `disable: 禁用CSP策略\n` +
                `1on: 启用规则1\n` +
                `23off: 禁用规则2和3\n` +
                `allon: 启用所有规则\n` +
                `alloff: 禁用所有规则\n\n` +
                `请输入您的指令:`;
            let input = prompt(promptMessage, "");
            if (input === null) return;
            input = input.trim().toLowerCase();
            let needsReload = false;
            if (input === 'enable') {
                currentConfig.modules.manageCSP = true;
                this.applyCSP();
                alert("CSP策略已启用。");
                needsReload = true;
            } else if (input === 'disable') {
                currentConfig.modules.manageCSP = false;
                this.removeCSP();
                alert("CSP策略已禁用。");
                needsReload = true;
            } else if (input === 'allon') {
                currentConfig.cspRules.forEach(rule => rule.enabled = true);
                if (currentConfig.modules.manageCSP) this.applyCSP();
                alert("所有CSP规则已启用。");
                needsReload = true;
            } else if (input === 'alloff') {
                currentConfig.cspRules.forEach(rule => rule.enabled = false);
                if (currentConfig.modules.manageCSP) this.removeCSP();
                alert("所有CSP规则已禁用。");
                needsReload = true;
            } else if (/^(\d+|all)(\d*on|off)$/.test(input)) {
                const match = input.match(/^(\d+|all)(\d*on|off)$/);
                if (match) {
                    const ruleSpec = match[1];
                    const action = match[2];
                    const enable = action.endsWith('on');
                    let ruleIdsToModify = [];
                    if (ruleSpec === 'all') {
                        ruleIdsToModify = currentConfig.cspRules.map(r => r.id);
                    } else {
                        for (const char of ruleSpec) {
                            const id = parseInt(char, 10);
                            if (!isNaN(id)) ruleIdsToModify.push(id);
                        }
                    }
                    let modified = false;
                    ruleIdsToModify.forEach(id => {
                        const rule = currentConfig.cspRules.find(r => r.id === id);
                        if (rule && rule.enabled !== enable) {
                            rule.enabled = enable;
                            modified = true;
                        }
                    });
                    if (modified) {
                        if (currentConfig.modules.manageCSP) this.applyCSP();
                        alert(`CSP规则 ${ruleIdsToModify.join(', ')} 已${enable ? '启用' : '禁用'}。`);
                        needsReload = true;
                    } else {
                        alert("未找到指定的CSP规则或规则状态已是目标状态。");
                    }
                } else {
                    alert("无效的CSP规则指令格式。");
                }
            } else {
                alert("无效指令。");
            }
            if (needsReload) {
                StorageManager.saveConfig();
                location.reload();
            }
        }
    };
    const UIController = {
        mutationObserver: null,
        init() {
            StorageManager.loadConfig();
            this.applyInitialModuleStates();
            this.registerMenuCommands();
            this.applyModuleSettings();
            this.setupObservers();
        },
        applyInitialModuleStates() {
            Object.keys(DEFAULT_MODULE_STATE).forEach(key => {
                if (currentConfig.modules[key] === undefined) {
                    currentConfig.modules[key] = DEFAULT_MODULE_STATE[key];
                }
            });
            if (currentConfig.modules.specialUA === undefined) {
                currentConfig.modules.specialUA = true;
            }
        },
        registerMenuCommands() {
            GM_registerMenuCommand(`🔘 广告拦截 [${this.isAnyModuleEnabled() ? '✅' : '❌'}]`, () => this.toggleAllModules());
            Object.keys(MODULE_NAMES).forEach(key => {
                GM_registerMenuCommand(
                    `${MODULE_NAMES[key]} [${currentConfig.modules[key] ? '✅' : '❌'}]`,
                    () => this.toggleModule(key)
                );
            });
            GM_registerMenuCommand('📜 查看拦截日志', () => LogManager.showInAlert());
            GM_registerMenuCommand('🚫 清空当前域名白名单', () => {
                const currentDomain = Utils.getCurrentHostname();
                if (confirm(`确定清空当前域名 (${currentDomain}) 的白名单吗?`)) {
                    Whitelisting.clearDomainWhitelist(currentDomain);
                    StorageManager.saveConfig();
                    alert("当前域名的白名单已清空。页面将刷新。");
                    location.reload();
                }
            });
            GM_registerMenuCommand('🛡️ CSP策略管理', () => CSPModule.showManagementPanel());
            GM_registerMenuCommand('🔄 重置所有设置', () => this.resetSettings());
        },
        isAnyModuleEnabled() {
            return Object.keys(MODULE_NAMES).some(key => currentConfig.modules[key]);
        },
        toggleAllModules() {
            const newState = !this.isAnyModuleEnabled();
            Object.keys(MODULE_NAMES).forEach(key => {
                currentConfig.modules[key] = newState;
            });
            this.applyModuleSettings();
            StorageManager.saveConfig();
            alert(`广告拦截已${newState ? '启用' : '禁用'}。页面将刷新。`);
            location.reload();
        },
        toggleModule(key) {
            currentConfig.modules[key] = !currentConfig.modules[key];
            this.applyModuleSettings();
            StorageManager.saveConfig();
            alert(`${MODULE_NAMES[key]} 已${currentConfig.modules[key] ? '启用' : '禁用'}。页面将刷新。`);
            location.reload();
        },
        applyModuleSettings() {
            SpecialUAModule.init();
            RemoveInlineScriptsModule.init();
            RemoveExternalScriptsModule.init();
            ThirdPartyModule.init();
            CSPModule.init();
        },
        setupObservers() {
            const relevantModulesEnabled = Object.keys(MODULE_NAMES).some(key =>
                currentConfig.modules[key] && (
                    key === 'removeInlineScripts' ||
                    key === 'removeExternalScripts' ||
                    key === 'interceptThirdPartyResources'
                )
            );
            if (relevantModulesEnabled) {
                this.mutationObserver = new MutationObserver(mutations => {
                    mutations.forEach(mutation => {
                        if (mutation.addedNodes) {
                            mutation.addedNodes.forEach(node => {
                                if (node.nodeType === Node.ELEMENT_NODE && node.dataset.adblockProcessed !== 'true') {
                                    if (node.tagName === 'SCRIPT') {
                                        if (currentConfig.modules.removeInlineScripts && !node.src) {
                                            if (RemoveInlineScriptsModule.check(node)) {
                                                node.remove();
                                            }
                                        } else if (currentConfig.modules.removeExternalScripts && node.src) {
                                            if (RemoveExternalScriptsModule.check(node)) {
                                                node.remove();
                                            }
                                        } else if (currentConfig.modules.interceptThirdPartyResources) {
                                            if (ThirdPartyModule.check(node)) {
                                                node.remove();
                                            }
                                        }
                                    } else if (currentConfig.modules.interceptThirdPartyResources) {
                                        if (ThirdPartyModule.check(node)) {
                                            node.remove();
                                        }
                                    }
                                }
                            });
                        }
                    });
                });
                this.mutationObserver.observe(document.documentElement, {
                    childList: true,
                    subtree: true,
                });
            }
        },
        resetSettings() {
            if (confirm("确定要重置所有设置吗?这将清除所有配置和白名单。")) {
                Object.assign(currentConfig.modules, DEFAULT_MODULE_STATE);
                currentConfig.cspRules = DEFAULT_CSP_RULES_TEMPLATE.map(rule => ({ ...rule }));
                Whitelisting.clearAllWhitelists();
                CSPModule.removeCSP();
                StorageManager.saveConfig();
                alert("所有设置已重置为默认值。页面将刷新。");
                location.reload();
            }
        }
    };
    function initializeAdBlocker() {
        UIController.init();
        if (currentConfig.modules.manageCSP) {
            CSPModule.applyCSP();
        }
    }
    StorageManager.loadConfig();
    if (currentConfig.modules.manageCSP) {
        CSPModule.applyCSP();
    }
    initializeAdBlocker();
})();