Greasy Fork

Greasy Fork is available in English.

Porn Blocker | 色情内容过滤器

A powerful content blocker that helps protect you from inappropriate websites. Features: Auto-detection of adult content, Multi-language support, Smart scoring system, Safe browsing protection.

当前为 2025-02-16 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Porn Blocker | 色情内容过滤器
// @name:en      Porn Blocker
// @name:zh-CN   色情内容过滤器
// @name:zh-TW   色情內容過濾器
// @name:zh-HK   色情內容過濾器
// @name:ja      アダルトコンテンツブロッカー
// @name:ko      성인 컨텐츠 차단기
// @name:ru      Блокировщик порнографии
// @namespace    https://noctiro.moe
// @version      2.0.2
// @description     A powerful content blocker that helps protect you from inappropriate websites. Features: Auto-detection of adult content, Multi-language support, Smart scoring system, Safe browsing protection.
// @description:en     A powerful content blocker that helps protect you from inappropriate websites. Features: Auto-detection of adult content, Multi-language support, Smart scoring system, Safe browsing protection.
// @description:zh-CN 强大的网页过滤工具,帮助你远离不良网站。功能特点:智能检测色情内容,多语言支持,评分系统,安全浏览保护,支持自定义过滤规则。为了更好的网络环境,从我做起。
// @description:zh-TW 強大的網頁過濾工具,幫助你遠離不良網站。功能特點:智能檢測色情內容,多語言支持,評分系統,安全瀏覽保護,支持自定義過濾規則。為了更好的網絡環境,從我做起。
// @description:zh-HK 強大的網頁過濾工具,幫助你遠離不良網站。功能特點:智能檢測色情內容,多語言支持,評分系統,安全瀏覽保護,支持自定義過濾規則。為了更好的網絡環境,從我做起。
// @description:ja   アダルトコンテンツを自動的にブロックする強力なツールです。機能:アダルトコンテンツの自動検出、多言語対応、スコアリングシステム、カスタマイズ可能なフィルタリング。より良いインターネット環境のために。
// @description:ko   성인 컨텐츠를 자동으로 차단하는 강력한 도구입니다. 기능: 성인 컨텐츠 자동 감지, 다국어 지원, 점수 시스템, 안전 브라우징 보호, 맞춤형 필터링 규칙。
// @description:ru   Мощный инструмент для блокировки неприемлемого контента. Функции: автоматическое определение, многоязычная поддержка, система оценки, настраиваемые правила фильтрации。
// @license      Apache-2.0
// @match        *://*/*
// @run-at       document-start
// @run-at       document-end
// @run-at       document-idle
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    // 多语言支持
    const i18n = {
        'en': {
            title: '🚫 Access Blocked',
            message: 'This webpage has been identified as inappropriate content.',
            redirect: 'Redirecting in <span class="countdown">4</span> seconds...',
            footer: 'Stay healthy · Stay away from harmful content'
        },
        'zh-CN': {
            title: '🚫 访问已被拦截',
            message: '该网页已被识别为不健康内容。',
            redirect: '<span class="countdown">4</span> 秒后自动跳转...',
            footer: '注意身心健康 · 远离不良网站'
        },
        'zh-TW': {
            title: '🚫 訪問已被攔截',
            message: '該網頁已被識別為不健康內容。',
            redirect: '<span class="countdown">4</span> 秒後自動跳轉...',
            footer: '注意身心健康 · 遠離不良網站'
        },
        'zh-HK': {
            title: '🚫 訪問已被攔截',
            message: '該網頁已被識別為不健康內容。',
            redirect: '<span class="countdown">4</span> 秒後自動跳轉...',
            footer: '注意身心健康 · 遠離不良網站'
        },
        'ja': {
            title: '🚫 アクセスがブロックされました',
            message: 'このページは不適切なコンテンツとして識別されました。',
            redirect: '<span class="countdown">4</span> 秒後にリダイレクトします...',
            footer: '健康に注意 · 有害サイトに近づかない'
        },
        'ko': {
            title: '🚫 접근이 차단됨',
            message: '이 웹페이지가 부적절한 콘텐츠로 식별되었습니다.',
            redirect: '<span class="countdown">4</span>초 후 자동으로 이동됩니다...',
            footer: '건강 관리 · 유해 사이트 멀리하기'
        },
        'ru': {
            title: '🚫 Доступ заблокирован',
            message: 'Эта веб-страница определена как неподходящий контент.',
            redirect: 'Перенаправление через <span class="countdown">4</span> секунды...',
            footer: 'Будьте здоровы · Держитесь подальше от вредного контента'
        }
    };

    // 获取用户语言
    const getUserLanguage = () => {
        const lang = navigator.language || navigator.userLanguage;

        // 检查完整语言代码
        if (i18n[lang]) return lang;

        // 处理中文的特殊情况
        if (lang.startsWith('zh')) {
            const region = lang.toLowerCase();
            if (region.includes('tw') || region.includes('hant')) return 'zh-TW';
            if (region.includes('hk')) return 'zh-HK';
            return 'zh-CN';
        }

        // 检查简单语言代码
        const shortLang = lang.split('-')[0];
        if (i18n[shortLang]) return shortLang;

        return 'en';
    };

    // 浏览器检测函数
    const getBrowserType = () => {
        const ua = navigator.userAgent.toLowerCase();

        // Add more browser detection
        if (ua.includes('ucbrowser')) return 'uc';
        if (ua.includes('qqbrowser')) return 'qq';
        if (ua.includes('2345explorer')) return '2345';
        if (ua.includes('360') || ua.includes('qihu')) return '360';
        if (ua.includes('maxthon')) return 'maxthon';
        if (ua.includes('firefox')) return 'firefox';
        if (ua.includes('edg')) return 'edge';
        if (ua.includes('opr') || ua.includes('opera')) return 'opera';
        if (ua.includes('brave')) return 'brave';
        if (ua.includes('vivaldi')) return 'vivaldi';
        if (ua.includes('yabrowser')) return 'yandex';
        if (ua.includes('chrome')) return 'chrome';
        if (ua.includes('safari') && !ua.includes('chrome')) return 'safari';

        return 'other';
    };

    // 获取浏览器主页URL
    const getHomePageUrl = () => {
        switch (getBrowserType()) {
            case 'firefox':
                return 'about:home';
            case 'chrome':
                return 'chrome://newtab';
            case 'edge':
                return 'edge://newtab';
            case 'safari':
                return 'topsites://';
            case 'opera':
                return 'opera://startpage';
            case 'brave':
                return 'brave://newtab';
            case 'vivaldi':
                return 'vivaldi://newtab';
            case 'yandex':
                return 'yandex://newtab';
            case 'uc':
                return 'ucenterhome://';
            case 'qq':
                return 'qbrowser://home';
            case '360':
                return 'se://newtab';
            case 'maxthon':
                return 'mx://newtab';
            case '2345':
                return '2345explorer://newtab';
            default:
                // Fallback to a safe default
                return 'about:blank';
        }
    };

    // ----------------- 预编译正则规则 (性能优化) -----------------
    const regexCache = {
        // 色情关键词正则(预编译,避免重复生成)
        pornRegex: null,
        // 白名单正则(预编译)
        whitelistRegex: null,
        // .xxx后缀正则
        xxxRegex: /\.xxx$/i
    };

    // ----------------- 配置项(用户可按需修改) -----------------
    const config = {
        // ================== 域名专用黑名单词汇 ==================
        domainKeywords: {
            // 常见成人网站域名关键词(权重4)
            'pornhub': 4, 'xvideo': 4, 'redtube': 4,
            'xnxx': 4, 'xhamster': 4, '4tube': 4,
            'youporn': 4, 'spankbang': 4,
            'myfreecams': 4, 'missav': 4,
            'rule34': 4, 'youjizz': 4,
            'onlyfans': 4, 'paidaa': 4,
            '3c5g9': 4,

            // 核心违规词(权重3-4)
            'porn': 3, 'nsfw': 3, 'hentai': 3,
            'incest': 4, 'rape': 4, 'childporn': 4,

            // 身体部位关键词(权重2)
            'pussy': 2, 'cock': 2, 'dick': 2,
            'boobs': 2, 'tits': 2, 'ass': 2,
            'beaver': 1,

            // 特定群体(权重2-3)
            'teen': 3, 'sis': 2, 'milf': 2,
            'cuckold': 3, 'virgin': 2, 'luoli': 2,
            'gay': 2,

            // 具体违规行为(权重2-3)
            'anal': 3, 'blowjob': 3, 'creampie': 2,
            'bdsm': 2, 'masturbat': 2, 'handjob': 3,
            'footjob': 3, 'rimjob': 3,

            // 其他相关词汇(权重1-2)
            'camgirl': 2, 'webcam': 2, 'leaked': 2,
            'fap': 2, 'erotic': 1, 'escort': 3,
            'nude': 3, 'naked': 3, 'upskirt': 2,

            // 特定地区成人站点域名特征(权重4)
            'jav': 4,

            // 域名变体检测(权重3)
            'p0rn': 3, 'pr0n': 3, 'pron': 3,
            's3x': 3, 'sexx': 3,
        },

        // ================== 内容检测关键词 ==================
        contentKeywords: {
            // 核心违规词(权重3-4)
            'porn': 3, 'nsfw': 3, 'hentai': 3,
            'incest': 4, 'rape': 4, 'childporn': 4,

            // 身体部位关键词(权重2)
            'pussy': 2, 'cock': 2, 'dick': 2,
            'boobs': 2, 'tits': 2, 'ass': 2,
            'beaver': 1,

            // 特定群体(权重2-3)
            'teen': 3, 'sis': 2, 'milf': 2,
            'cuckold': 3, 'virgin': 2, 'luoli': 2,
            'gay': 2,

            // 具体违规行为(权重2-3)
            'anal': 3, 'blowjob': 3, 'creampie': 2,
            'bdsm': 2, 'masturbat': 2, 'handjob': 3,
            'footjob': 3, 'rimjob': 3,

            // 其他相关词汇(权重1-2)
            'camgirl': 2, 'webcam': 2, 'leaked': 2,
            'fap': 2, 'erotic': 1, 'escort': 3,
            'nude': 3, 'naked': 3, 'upskirt': 2,
            'onlyfans': 3, '海角社区': 4,

            // 多语言支持
            '情色': 3, '成人': 3, '做爱': 4,
            'セックス': 3, 'エロ': 3, '淫': 4,
            'секс': 3, 'порн': 3, '性爱': 3,
            '無修正': 3, 'ポルノ': 3, 'порно': 3,
            '色情': 3, '骚': 1, '啪啪': 2,
            '自慰': 3, '口交': 3, '肛交': 3,
            '吞精': 3, '诱惑': 1, '全裸': 3,
            '内射': 3, '乳交': 3, '射精': 3,
            '反差': 0.5, '调教': 1.5, '性交': 3,
            '性奴': 3, '高潮': 0.3, '白虎': 0.8,
            '少女': 0.1, '女友': 0.1, '狂操': 3,
            '捆绑': 0.1, '约炮': 3, '鸡吧': 3,
            '鸡巴': 3, '阴茎': 1, '阴道': 1,
            '女优': 3, '裸体': 3, '男优': 3,
            '乱伦': 3, '偷情': 2, '母狗': 3,
            '内射': 4, '喷水': 0.8, '潮吹': 3,
            '轮奸': 2, '少妇': 2, '熟女': 2,
        },

        // ================== 白名单减分规则 ==================
        whitelist: {
            // 强豁免词(权重-10)
            'edu': -10, 'health': -10, 'medical': -10, 'science': -10,
            'gov': -10, 'org': -10, 'official': -10,

            // 常用场景豁免(权重-7)
            'academy': -7, 'clinic': -7, 'therapy': -7,
            'university': -4, 'research': -7, 'news': -7,
            'dictionary': -7, 'library': -7, 'museum': -7,

            // 动物/自然相关(权重-1)
            'animal': -4, 'zoo': -1, 'cat': -1, 'dog': -1,
            'pet': -6, 'bird': -1, 'vet': -1,

            // 科技类(权重-5)
            'tech': -5, 'cloud': -5, 'software': -5, 'cyber': -3,
        },

        // ================== 阈值配置 ==================
        thresholds: {
            // 总分触发阈值(建议3~4)
            block: 3,
            // URL路径加分阈值
            path: 2,
            // 进行白名单减分的最低阈值
            whitelist: 2
        },

        // ================== 域名正则表达式规则 ==================
        domainPatterns: [
            /^mogu\d+\.[a-z]{2,3}$/i,
        ],

        // ================== 内容检测规则 ==================
        // 需要内容检测的域名规则
        contentCheckDomains: [
            /\d{3}[a-z]{2,3}/i,
            /[a-z]{2,3}\d{3}/i,
            /[a-z]{1,3}\d{0,3}[a-z]{1,3}/i,
        ],

        // 内容检测相关配置
        contentCheck: {
            // 成人内容分数
            adultContentThreshold: 20,
            suspiciousTagNames: [
                // 主要内容区域
                'article', 'main', 'section', 'content',
                // 文本块
                'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
                // 列表和表格
                'li', 'td', 'th', 'figcaption',
                // 链接和按钮文本
                'a', 'button',
                // 通用容器
                'div.content', 'div.text', 'div.description',
                'span.text', 'span.content'
            ],
            // 文本节点最小长度
            textNodeMinLength: 5,
            // 防抖等待时间(毫秒) 
            debounceWait: 1000,
            // 观察者最大运行时间(毫秒)
            observerTimeout: 30000,
        }
    };

    // 域名正则检测函数
    const checkDomainPatterns = (hostname) => {
        return config.domainPatterns.some(pattern => pattern.test(hostname));
    };

    // 检查是否需要进行内容检测
    const shouldCheckContent = (hostname) => {
        return config.contentCheckDomains.some(pattern => pattern.test(hostname));
    };

    // 内容检测辅助函数
    const contentUtils = {
        // 优化文本获取算法
        getAllText: (element) => {
            if (!element) return "";
            
            // 使用Set去重
            const textSet = new Set();
            
            try {
                const walker = document.createTreeWalker(
                    element,
                    NodeFilter.SHOW_TEXT,
                    {
                        acceptNode: (node) => {
                            const parent = node.parentElement;
                            
                            // 优化过滤条件
                            if (!parent || 
                                /^(SCRIPT|STYLE|NOSCRIPT|IFRAME|META|LINK)$/i.test(parent.tagName) ||
                                parent.hidden ||
                                getComputedStyle(parent).display === 'none') {
                                return NodeFilter.FILTER_REJECT;
                            }
                            
                            const text = node.textContent.trim();
                            if (!text || text.length < config.contentCheck.textNodeMinLength) {
                                return NodeFilter.FILTER_REJECT;
                            }
                            
                            return NodeFilter.FILTER_ACCEPT;
                        }
                    }
                );

                let node;
                while (node = walker.nextNode()) {
                    textSet.add(node.textContent.trim());
                }
            } catch (e) {
                console.error('Error in getAllText:', e);
            }

            return Array.from(textSet).join(' ');
        },

        // 优化可疑元素获取
        getSuspiciousElements: () => {
            try {
                const elements = new Set();
                
                // 使用更高效的选择器
                const fastSelectors = [
                    'article', 'main', '.content', 
                    '[class*="content"]', '[class*="text"]',
                    'h1', 'h2', 'h3'
                ];
                
                fastSelectors.forEach(selector => {
                    document.querySelectorAll(selector).forEach(el => elements.add(el));
                });
                
                return Array.from(elements);
            } catch (e) {
                console.error('Error in getSuspiciousElements:', e);
                return [];
            }
        }
    };

    // 优化内容检测函数
    function detectAdultContent() {
        let totalScore = 0;
        
        try {
            // 使用WeakMap缓存已计算的元素分数
            const scoreCache = new WeakMap();
            
            // 主要内容区域检测
            const mainElements = contentUtils.getSuspiciousElements();
            for (const element of mainElements) {
                if (scoreCache.has(element)) {
                    totalScore += scoreCache.get(element);
                    continue;
                }
                
                const text = contentUtils.getAllText(element);
                const score = calculateScore(text);
                scoreCache.set(element, score);
                totalScore += score;
                
                // 提前退出优化
                if (totalScore >= config.contentCheck.adultContentThreshold) {
                    return true;
                }
            }
            
            // 优化图片检测
            const images = document.querySelectorAll('img[alt], img[title]');
            for (const img of images) {
                const imgText = `${img.alt} ${img.title}`.trim();
                if (imgText) {
                    totalScore += calculateScore(imgText) * 0.5; // 降低图片文本权重
                }
            }
            
            // 优化元数据检测
            const metaTags = document.querySelectorAll('meta[name="description"], meta[name="keywords"]');
            for (const meta of metaTags) {
                const content = meta.content;
                if (content) {
                    totalScore += calculateScore(content) * 0.3; // 降低元数据权重
                }
            }
            
        } catch (e) {
            console.error('Error in detectAdultContent:', e);
            return false;
        }
        
        return totalScore >= config.contentCheck.adultContentThreshold;
    }

    // Refactored content detector using helper function
    const checkPageContent = () => {
        return detectAdultContent();
    };

    // 预处理正则(仅初始化一次)
    (function initRegex() {
        // 域名关键词正则
        const domainTerms = Object.keys(config.domainKeywords).join('|');
        regexCache.domainRegex = new RegExp(`(${domainTerms})`, 'gi');

        // 内容关键词正则
        const contentTerms = Object.keys(config.contentKeywords).join('|');
        regexCache.contentRegex = new RegExp(`(${contentTerms})`, 'gi');

        // 白名单正则
        const whitelistTerms = Object.keys(config.whitelist).join('|');
        regexCache.whitelistRegex = new RegExp(`(${whitelistTerms})`, 'gi');
    })();

    // Helper function to sum weights from regex matches
    function sumMatches(text, regex, weightMap) {
        return (text.match(regex) || []).reduce((total, match) => {
            return total + (weightMap[match.toLowerCase()] || 0);
        }, 0);
    }

    // 优化后的评分计算函数
    const calculateScore = (text, isDomain = false) => {
        let score = isDomain
            ? sumMatches(text, regexCache.domainRegex, config.domainKeywords)
            : sumMatches(text, regexCache.contentRegex, config.contentKeywords);

        if (score >= config.thresholds.whitelist) {
            score += sumMatches(text, regexCache.whitelistRegex, config.whitelist);
        }

        return score;
    };

    // 添加内容检测防抖函数
    const debounce = (func, wait) => {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    };

    // Helper to check mutation text and trigger blocking if needed
    function checkMutationText(text) {
        if (!text.trim() || text.trim().length < config.contentCheck.textNodeMinLength) return false;
        const score = calculateScore(text);
        return score >= config.contentCheck.adultContentThreshold;
    }

    // 检测结果处理函数
    const handleBlockedContent = () => {
        const lang = getUserLanguage();
        const text = i18n[lang];
        window.stop();
        document.documentElement.innerHTML = `
            <body>
                <div class="container">
                    <div class="card">
                        <div class="icon-wrapper">
                            <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
                            </svg>
                        </div>
                        <h1>${text.title}</h1>
                        <p>${text.message}<br>${text.redirect}</p>
                        <div class="footer">${text.footer}</div>
                    </div>
                </div>
                <style>
                    :root {
                        --bg-light: #f0f2f5;
                        --card-light: #ffffff;
                        --text-light: #2d3436;
                        --text-secondary-light: #636e72;
                        --text-muted-light: #b2bec3;
                        --accent-light: #ff4757;
 
                        --bg-dark: #1a1a1a;
                        --card-dark: #2d2d2d;
                        --text-dark: #ffffff;
                        --text-secondary-dark: #a0a0a0;
                        --text-muted-dark: #808080;
                        --accent-dark: #ff6b6b;
                    }
 
                    @media (prefers-color-scheme: dark) {
                        body {
                            background: var(--bg-dark) !important;
                        }
                        .card {
                            background: var(--card-dark) !important;
                            box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important;
                        }
                        h1 { color: var(--text-dark) !important; }
                        p { color: var(--text-secondary-dark) !important; }
                        .footer { color: var(--text-muted-dark) !important; }
                        .icon-wrapper {
                            background: var(--accent-dark) !重要;
                        }
                        .countdown {
                            color: var(--accent-dark);
                        }
                    }
 
                    body {
                        background: var(--bg-light);
                        margin: 0;
                        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
                        min-height: 100vh;
                        display: flex;
                        align-items: center;
                        justify-content: center;
                    }
 
                    .container {
                        max-width: 500px;
                        width: 100%;
                    }
 
                    .card {
                        background: var(--card-light);
                        border-radius: 16px;
                        box-shadow: 0 4px 12px rgba(0,0,0,0.1);
                        padding: 32px;
                        text-align: center;
                        animation: slideIn 0.5s ease-out;
                    }
 
                    .icon-wrapper {
                        width: 64px;
                        height: 64px;
                        background: var(--accent-light);
                        border-radius: 50%;
                        display: flex;
                        align-items: center;
                        justify-content: center;
                        margin: 0 auto 24px;
                        animation: pulse 2s infinite;
                    }
 
                    .icon-wrapper svg {
                        stroke: white;
                    }
 
                    h1 {
                        color: var(--text-light);
                        margin: 0 0 16px;
                        font-size: 24px;
                        font-weight: 600;
                    }
 
                    p {
                        color: var(--text-secondary-light);
                        margin: 0 0 24px;
                        line-height: 1.6;
                        font-size: 16px;
                    }
 
                    .footer {
                        color: var(--text-muted-light);
                        font-size: 14px;
                        animation: fadeIn 1s ease-out;
                    }
 
                    .countdown {
                        font-weight: bold;
                        color: var(--accent-light);
                    }
 
                    @keyframes slideIn {
                        from { transform: translateY(20px); opacity: 0; }
                        to { transform: translateY(0); opacity: 1; }
                    }
 
                    @keyframes pulse {
                        0% { transform: scale(1); }
                        50% { transform: scale(1.05); }
                        100% { transform: scale(1); }
                    }
 
                    @keyframes fadeIn {
                        from { opacity: 0; }
                        to { opacity: 1; }
                    }
                </style>
            </body>
        `;
        let timeLeft = 4;
        const countdownEl = document.querySelector('.countdown');
        const countdownInterval = setInterval(() => {
            timeLeft--;
            if (countdownEl) countdownEl.textContent = timeLeft;
            if (timeLeft <= 0) {
                clearInterval(countdownInterval);
                try {
                    const homeUrl = getHomePageUrl();
                    if (window.history.length > 1) {
                        const iframe = document.createElement('iframe');
                        iframe.style.display = 'none';
                        document.body.appendChild(iframe);

                        iframe.onload = () => {
                            try {
                                const prevUrl = iframe.contentWindow.location.href;
                                const prevScore = calculateScore(new URL(prevUrl).hostname, true);

                                if (prevScore >= config.thresholds.block) {
                                    window.location.href = homeUrl;
                                } else {
                                    window.history.back();
                                }
                            } catch (e) {
                                window.location.href = homeUrl;
                            }
                            document.body.removeChild(iframe);
                        };

                        iframe.src = 'about:blank';
                    } else {
                        window.location.href = homeUrl;
                    }
                } catch (e) {
                    window.location.href = getHomePageUrl();
                }
            }
        }, 1000);
    };

    // 优化动态内容检测
    const setupDynamicContentCheck = () => {
        if (!document.body) return;

        let pendingCheck = false;
        let observer = null;

        const checkContent = debounce(() => {
            if (pendingCheck) return;
            pendingCheck = true;

            try {
                const hostname = window.location.hostname;
                if (detectAdultContent()) {
                    blacklistManager.addToBlacklist(hostname);
                    observer?.disconnect();
                    handleBlockedContent();
                }
            } finally {
                pendingCheck = false;
            }
        }, config.contentCheck.debounceWait);

        try {
            observer = new MutationObserver((mutations) => {
                // 过滤无关变化
                const hasRelevantChanges = mutations.some(mutation => {
                    return mutation.addedNodes.length > 0 || 
                           (mutation.type === 'characterData' && 
                            mutation.target.textContent.trim().length >= config.contentCheck.textNodeMinLength);
                });

                if (hasRelevantChanges) {
                    checkContent();
                }
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true,
                characterData: true
            });

            // 清理机制
            setTimeout(() => {
                observer?.disconnect();
                observer = null;
            }, config.contentCheck.observerTimeout);

        } catch (e) {
            console.error('Error in setupDynamicContentCheck:', e);
        }

        return observer;
    };

    // ----------------- 黑名单储存优化 -----------------
    const blacklistManager = {
        BLACKLIST_KEY: 'pornblocker-blacklist',
        BLACKLIST_VERSION: '1',  // 添加版本控制
        
        // 获取黑名单
        getBlacklist() {
            try {
                const data = localStorage.getItem(this.BLACKLIST_KEY);
                if (!data) return [];
                
                const parsed = JSON.parse(data);
                return Array.isArray(parsed) ? parsed : [];
            } catch (e) {
                console.error('Error reading blacklist:', e);
                return [];
            }
        },
        
        // 添加到黑名单
        addToBlacklist(hostname) {
            try {
                if (!hostname) return false;
                
                const blacklist = this.getBlacklist();
                if (blacklist.includes(hostname)) return true;
                
                blacklist.push(hostname);
                localStorage.setItem(this.BLACKLIST_KEY, JSON.stringify(blacklist));
                return true;
            } catch (e) {
                console.error('Error adding to blacklist:', e);
                return false;
            }
        },
        
        // 检查是否在黑名单中
        isBlacklisted(hostname) {
            try {
                if (!hostname) return false;
                
                const blacklist = this.getBlacklist();
                return blacklist.includes(hostname);
            } catch (e) {
                console.error('Error checking blacklist:', e);
                return false;
            }
        },
        
        // 从黑名单中移除
        removeFromBlacklist(hostname) {
            try {
                const blacklist = this.getBlacklist();
                const index = blacklist.indexOf(hostname);
                if (index > -1) {
                    blacklist.splice(index, 1);
                    localStorage.setItem(this.BLACKLIST_KEY, JSON.stringify(blacklist));
                    return true;
                }
                return false;
            } catch (e) {
                console.error('Error removing from blacklist:', e);
                return false;
            }
        }
    };

    // ----------------- 主检测逻辑 -----------------
    const checkUrl = () => {
        const url = new URL(window.location.href);
        const hostname = url.hostname;

        // 优化黑名单检查
        if (blacklistManager.isBlacklisted(hostname)) {
            return {
                shouldBlock: true,
                url: url,
                reason: 'blacklist'
            };
        }

        // 如果域名匹配正则
        if (checkDomainPatterns(url.hostname)) {
            return {
                shouldBlock: true,
                url: url
            };
        }

        // 检查是否需要进行内容检测
        if (shouldCheckContent(url.hostname)) {
            if (document.body) {
                const hasAdultContent = checkPageContent();
                if (hasAdultContent) {
                    blacklistManager.addToBlacklist(hostname);
                    return {
                        shouldBlock: true,
                        url: url,
                        reason: 'content'
                    };
                }
                setupDynamicContentCheck();
            } else {
                document.addEventListener('DOMContentLoaded', () => {
                    if (checkPageContent()) {
                        blacklistManager.addToBlacklist(hostname);
                        handleBlockedContent();
                    }
                    setupDynamicContentCheck();
                });
            }
        }

        let score = 0;

        // 检查域名
        const pornMatches = url.hostname.match(regexCache.domainRegex) || [];
        pornMatches.forEach(match => {
            const keyword = match.toLowerCase();
            score += config.domainKeywords[keyword] || 0;
        });

        // 检查路径
        const path = url.pathname + url.search;
        const pathScore = calculateScore(path) * 0.4;
        score += pathScore;

        // 检查标题
        const titlePornScore = calculateScore(document.title || "");
        score += titlePornScore;

        // 如果超过阈值则进行白名单扣分
        if (score >= config.thresholds.whitelist) {
            const hostMatches = url.hostname.match(regexCache.whitelistRegex) || [];
            const titleMatches = document.title.match(regexCache.whitelistRegex) || [];
            const whitelistMatchCount = (matches) => {
                matches.forEach(match => {
                    const term = match.toLowerCase();
                    score += config.whitelist[term] || 0;
                });
            };
            whitelistMatchCount(hostMatches);
            whitelistMatchCount(titleMatches);
        }

        return {
            shouldBlock: score >= config.thresholds.block,
            url: url
        };
    };

    // 获取结果
    const { shouldBlock, url: currentUrl, reason } = checkUrl();

    if (shouldBlock || regexCache.xxxRegex.test(currentUrl.hostname)) {
        if (!blacklistManager.isBlacklisted(currentUrl.hostname)) {
            blacklistManager.addToBlacklist(currentUrl.hostname);
        }
        handleBlockedContent();
    }
})();