// ==UserScript== // @name 移动端增强型广告拦截器 // @namespace http://tampermonkey.net/ // @version 3.9.9 // @author DeepSeek&Gemini // @description 一个手机端via浏览器能用的强大的广告拦截器 // @match *://*/* // @license MIT // @grant unsafeWindow // @grant GM_registerMenuCommand // @grant GM_notification // @grant GM_getValue // @grant GM_setValue // @grant GM_getResourceText // @grant GM_addStyle // @grant GM_openInTab // @run-at document-start // @downloadURL https://update.greasyfork.icu/scripts/525345/%E7%A7%BB%E5%8A%A8%E7%AB%AF%E5%A2%9E%E5%BC%BA%E5%9E%8B%E5%B9%BF%E5%91%8A%E6%8B%A6%E6%88%AA%E5%99%A8.user.js // @updateURL https://update.greasyfork.icu/scripts/525345/%E7%A7%BB%E5%8A%A8%E7%AB%AF%E5%A2%9E%E5%BC%BA%E5%9E%8B%E5%B9%BF%E5%91%8A%E6%8B%A6%E6%88%AA%E5%99%A8.meta.js // ==/UserScript== (function() { 'use strict'; const HOSTNAME = location.hostname.replace(/\./g, '\\.'); const REGEX = { dynamicId: /^(?:([0-9a-f]{4}-?){4,}|[0-9a-f]{16,}|[\dA-F-]{18,}|[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12})$/i, jsAdPattern: /(?:window\.open|document\.write|createElement\(['"]script['"]|location\.replace|setTimeout\s*\([^)]*?\b(?:window\.open|document\.write)|eval\s*\(|new\s+Function\s*\(|appendChild\s*\(.*?\bscript\b|on(?:click|load)\s*=\s*['"]?\s*(?:window\.open))/i, adAttributes: /(推广|广告|gg|sponsor|推荐|guanggao|syad|bfad|弹窗|悬浮|葡京|banner|pop(?:up|under)|track(?:ing)?)(?:$|[_-])/i, thirdPartyHostCheck: new RegExp(`^(https?:\\/\\/(.*\\.)?${HOSTNAME}(\\/|$)|data:|about:blank)`, 'i'), obfuscationPatterns: [ { type: '字符串/十六进制/Unicode混淆', pattern: /(?:\\(?:x[0-9a-f]{2}|u[0-9a-f]{4}){3,})/gi, riskLevel: 3 }, { type: 'Base64/编码Payload执行', pattern: /['"](?:data:application\/javascript;base64,|(?:btoa|atob)\s*\(\s*['"][A-Za-z0-9+/=]{100,}['"])|(?:eval\(\s*(?:atob|decodeURIComponent\(\s*escape\(\s*atob\))\s*\(\s*["'][A-Za-z0-9+/=]{40,}["'])|[A-Za-z0-9+/=]{60,}/gi, riskLevel: 5 }, { type: '动态代码执行', pattern: /(eval|new\s+Function\s*\(|set(?:Timeout|Interval|Immediate)\s*\(\s*function\s*\(\)\s*\{)/i, riskLevel: 4 }, { type: '动态脚本注入', pattern: /(?:document\.(?:createElement\s*\(['"]script['"]\)\.src\s*=|(?:head|body)\.appendChild\s*\(|getElementsByTagName\(['"]head['"]\)\s*\[0\]\.appendChild)|document\s*\[\s*['"]createElement['"]\s*\]\s*\(\s*['"]script['"]\s*\))/gi, riskLevel: 4 }, { type: '数学/日期/数值/数组/属性混淆', pattern: /(?:Math\[\s*['"]\w{5,}['"]\s*\]|new\s+Date\s*\(\s*\)\s*\[\s*['"]getTime['"]\s*\]|Math\.(?:round|floor|ceil)\s*\[\s*['"]constructor['"]\s*\]|0x[a-f0-9]{8}(?:\+0x[a-f0-9]{8}){3,}|\[(?:0x[0-9a-f]+|\d+),?(?:0x[0-9a-f]+|\d+)\]\.(?:join|reverse|map)|\b\w+\s*\[\s*(?:0x[0-9a-f]+|\d+|['"][^'"]{5,}['"])\s*\])/gi, riskLevel: 3 }, { type: '随机数/时间混淆', pattern: /Math\.floor\(\s*(?:\d+\s*\*\s*)?Math\.random\(\s*\)(?:\s*\*\s*\w+\.length)?\s*\)|Math\.floor\(\s*\(\s*new\s+Date\s*\(\s*\)\.getTime\(\s*\)/i, riskLevel: 3 } ], }; const DEFAULT_MODULES = { main: false, dynamicSystem: false, layoutSystem: false, mergedMediaSystem: false, thirdPartyBlock: false, specialUA: true }; const DEFAULT_CSP_RULES = [ { id: 1, name: '仅允许同源脚本', rule: "script-src 'self'", enabled: true }, { id: 2, name: '禁止内联脚本', rule: "script-src 'unsafe-inline'", enabled: false }, { id: 3, name: '禁止eval执行', rule: "script-src 'unsafe-eval'", enabled: false }, { id: 4, name: '阻止外部样式加载', rule: "style-src 'self'", enabled: false }, { id: 5, name: '阻止内联样式执行', rule: "style-src 'unsafe-inline'", enabled: false }, { id: 6, name: '阻止外部图片加载', rule: "img-src 'self'", enabled: true }, { id: 7, name: '禁止所有框架加载', rule: "frame-src 'none'", enabled: false }, { id: 8, name: '禁止媒体资源加载', rule: "media-src 'none'", enabled: false }, { id: 9, name: '禁止对象嵌入', rule: "object-src 'none'", enabled: false } ]; const CONFIG = { modules: { ...DEFAULT_MODULES }, protectionRules: { dynamicIdLength: 32, zIndexThreshold: 9999 }, csp: { enabled: false, rules: DEFAULT_CSP_RULES.map(rule => ({ ...rule })) }, mobileOptimizations: { lazyLoadImages: true, removeImagePlaceholders: true }, moduleNames: { dynamicSystem: '动态检测系统', layoutSystem: '布局检测系统', mergedMediaSystem: '图片广告移除', thirdPartyBlock: '第三方拦截', specialUA: '添加特殊UA' }, modulePriority: [ 'thirdPartyBlock', 'specialUA', 'dynamicSystem', 'mergedMediaSystem', 'layoutSystem' ], performance: { highRiskModules: ['thirdPartyBlock', 'dynamicSystem', 'mergedMediaSystem', 'layoutSystem'], visibleAreaPriority: true, styleCacheTimeout: 600000, mutationProcessLimit: 40, mutationProcessTimeLimit: 10, idleCallbackTimeout: 2500, throttleScrollDelay: 200, debounceMutationDelay: 300, longTaskThreshold: 500, degradeThreshold: 5 }, originalUA: { userAgent: navigator.userAgent, platform: navigator.platform }, whitelist: {} }; const ElementFlags = { LOGGED: 1 << 0, WHITELISTED_EXPLICIT: 1 << 1, WHITELISTED_IMPLICIT: 1 << 2, }; const Utils = { throttle(fn, delay) { let last = 0, timer = null; return function(...args) { const now = Date.now(); if (now - last >= delay) { fn.apply(this, args); last = now; } else { clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args); last = now; }, delay - (now - last)); } }; }, debounce(fn, delay) { let timer = null; return function(...args) { clearTimeout(timer); timer = setTimeout(() => { try { fn.apply(this, args); } catch (e) { Logs.add('error', this, { type: 'debounceError', detail: e.message }); } }, delay); } }, truncateString(str, maxLength) { if (typeof str !== 'string') return ''; if (str.length <= maxLength) return str; return str.slice(0, maxLength) + '...'; }, removeAttributeSafely(element, attributeName) { if (element instanceof Element && element.hasAttribute(attributeName)) { try { element.removeAttribute(attributeName); } catch (e) {} } }, isAdCharacteristic(element) { if (!(element instanceof Element)) return false; return REGEX.adAttributes.test(element.id) || REGEX.adAttributes.test(element.className) || Array.from(element.attributes).some(attr => REGEX.adAttributes.test(attr.name) || REGEX.adAttributes.test(attr.value)); } }; const elementStateTracker = { _flagsMap: new WeakMap(), setFlag(element, flag) { if (!(element instanceof Element)) return; let currentFlags = this._flagsMap.get(element) || 0; currentFlags |= flag; this._flagsMap.set(element, currentFlags); }, hasFlag(element, flag) { if (!(element instanceof Element)) return false; const currentFlags = this._flagsMap.get(element); return (currentFlags & flag) === flag; } }; const SharedUtils = { generateElementPattern(element) { try { if (!element || !(element instanceof Element)) return ''; let pattern = element.tagName || ''; if (element.id) pattern += `#${element.id}`; if (element.className) { const firstClass = element.className.split(/\s+/)[0]; if (firstClass) pattern += `.${firstClass}`; } return pattern; } catch (e) { return ''; } }, getElementContent(element) { if (!element || !(element instanceof Element)) return '[无效元素]'; let baseIdentifier = ''; if (element.id) { baseIdentifier = `ID:${element.id}`; } else if (element.className) { baseIdentifier = `CLASS:${element.className.split(/\s+/)[0]}`; } if (element.dataset.url && element.dataset.tag) { let content = `${element.dataset.tag}: ${Utils.truncateString(element.dataset.url, 200)}`; if (element.dataset.detail) { content += `, ${element.dataset.detail}`; } return content; } if (element.dataset.insertionType && element.dataset.originalHTML) { return `DYNAMIC_INSERT (${element.dataset.insertionType}${baseIdentifier ? `, ${baseIdentifier}` : ''}): ${Utils.truncateString(element.dataset.originalHTML, 2000)}`; } if (element.tagName === 'SCRIPT') { const src = element.src; const content = element.textContent; if (src) return `SCRIPT_SRC${baseIdentifier ? ` (${baseIdentifier})` : ''}: ${Utils.truncateString(src, 200)}`; if (content) return `SCRIPT_CONTENT${baseIdentifier ? ` (${baseIdentifier})` : ''}: ${Utils.truncateString(content.replace(/\s+/g, ' '), 2000)}`; return `SCRIPT_EMPTY${baseIdentifier ? ` (${baseIdentifier})` : ''}`; } if (element.tagName === 'IFRAME') { const src = element.src; if (src) return `IFRAME_SRC${baseIdentifier ? ` (${baseIdentifier})` : ''}: ${Utils.truncateString(src, 200)}`; return `IFRAME_EMPTY${baseIdentifier ? ` (${baseIdentifier})` : ''}`; } if (element.tagName === 'IMG') { const src = element.src || element.dataset.src; if (src) return `IMG_SRC${baseIdentifier ? ` (${baseIdentifier})` : ''}: ${Utils.truncateString(src, 200)}`; return `IMG_EMPTY${baseIdentifier ? ` (${baseIdentifier})` : ''}`; } const outerHTML = element.outerHTML; if (outerHTML) { return `ELEMENT_HTML${baseIdentifier ? ` (${baseIdentifier})` : ''}: ${Utils.truncateString(outerHTML.replace(/\s+/g, ' ').replace(//g, ''), 200)}`; } return '[无法获取内容]'; } }; const Logs = { logs: [], maxLogs: 50, add(moduleKey, element, reason) { if (!(element instanceof Element) && element !== null) return; if (element !== null && (Whitelist.isWhitelisted(element) || Whitelist.isAncestorWhitelisted(element))) return; let fullContent; if (element !== null) { fullContent = SharedUtils.getElementContent(element); if (fullContent.trim() === '[无效元素]' || fullContent.trim() === '[无法获取内容]' || fullContent.includes('[安全限制无法获取内容]')) { return; } if (elementStateTracker.hasFlag(element, ElementFlags.LOGGED)) { return; } elementStateTracker.setFlag(element, ElementFlags.LOGGED); } else { fullContent = reason.detail || 'N/A'; } const currentDomain = new URL(location.href).hostname; const getElementInfoForLogDisplay = (el) => { if (el === null) return { tag: 'N/A', id: '', class: '', src: '', attributes: {} }; try { const attrs = {}; const attributeNames = ['id', 'class', 'src', 'href', 'data-src', 'style']; for (const name of attributeNames) { if (el.hasAttribute(name)) { attrs[name] = Utils.truncateString(el.getAttribute(name), 100); } } return { tag: el.tagName || '未知标签', id: el.id ? `#${el.id}` : '', class: el.className ? `.${el.className.split(/\s+/).join('.')}` : '', src: el.src || el.href || el.dataset.src || '', attributes: attrs }; } catch (e) { return { tag: '未知标签', id: '', class: '', src: '', attributes: {}, error: '元素解析失败' }; } }; const elementInfoDisplay = getElementInfoForLogDisplay(element); let specificDetailsText = ''; if (reason.type === '动态脚本风险' || reason.type === '流式特征阻断' || reason.type === '动态脚本源快速检测' || reason.type === '内联脚本快速检测' || reason.type === '内联脚本结构性混淆') { specificDetailsText = `AST阻断: ${reason.ruleName || reason.type}`; } else if (reason.ruleName) { specificDetailsText = reason.ruleName; } else if (reason.regex && reason.regex.length > 0) { let matchedRuleTypes = new Set(); for (const patternSource of reason.regex) { const matchedRule = REGEX.obfuscationPatterns.find(rule => rule.pattern.source === patternSource); if (matchedRule) { matchedRuleTypes.add(matchedRule.type); } else if (patternSource === REGEX.jsAdPattern.source) { matchedRuleTypes.add('可疑JS广告模式'); } } if (matchedRuleTypes.size > 0) { specificDetailsText = Array.from(matchedRuleTypes).join('; '); } else { specificDetailsText = reason.detail || reason.type || '自动拦截'; } } else { specificDetailsText = reason.detail || reason.type || '自动拦截'; } specificDetailsText = Utils.truncateString(specificDetailsText, 150); this.logs.push({ module: CONFIG.moduleNames[moduleKey] || moduleKey, elementSummary: `${elementInfoDisplay.tag}${elementInfoDisplay.id}${elementInfoDisplay.class}`, content: fullContent, specificDetailsForDisplay: specificDetailsText, originalReason: { type: reason.type || '自动拦截', detail: Utils.truncateString(reason.detail || '', 150), pattern: reason.regex ? reason.regex.map(r => Utils.truncateString(r.source, 100)) : [], ruleName: reason.ruleName }, contextInfo: { src: Utils.truncateString(elementInfoDisplay.src || '', 200), attributes: elementInfoDisplay.attributes || {}, parentTag: element ? (element.parentElement ? element.parentElement.tagName : '无父节点') : 'N/A', domain: currentDomain }, timestamp: Date.now(), }); if (this.logs.length > this.maxLogs) { this.logs = this.logs.slice(this.logs.length - this.maxLogs); } }, clear() { this.logs = []; GM_notification('日志已清空'); }, showInAlert() { const logText = this.logs.map((log, index) => { const parts = []; if (log.specificDetailsForDisplay) parts.push(`具体详情: ${log.specificDetailsForDisplay}`); return [ `序号: ${index + 1}`, `模块: ${log.module}`, `元素: ${log.elementSummary}`, `内容: ${log.content}`, ...parts ].filter(line => line.trim() !== '').join('\n'); }).join('\n\n'); const promptMessage = `广告拦截日志(最近${this.logs.length}条):\n\n${logText || '暂无日志'}\n\n请输入序号,支持格式:\n123(序号1, 2, 3)\n1,3,5(序号1, 3, 5)\n1-12(序号1到12)\n输入 0 清空当前域名的白名单:`; let input = prompt(promptMessage, ""); if (input === null) return; input = input.trim(); if (input === "0") { if (confirm(`确定清空当前域名 (${location.hostname}) 的白名单吗?`)) { Whitelist.clearCurrentDomain(); GM_notification('白名单已清空', `当前域名 (${location.hostname}) 的白名单已清空`); location.reload(); } } else { let inputIndices = []; const cleanedInput = input.replace(/,/g, ','); if (cleanedInput.includes('-')) { const parts = cleanedInput.split('-'); if (parts.length === 2) { const start = parseInt(parts[0].trim(), 10); const end = parseInt(parts[1].trim(), 10); if (!isNaN(start) && !isNaN(end) && start >= 1 && end >= start) { for (let i = start; i <= end; i++) { inputIndices.push(i - 1); } } else { alert("无效的范围格式。请使用 '开始序号-结束序号',例如 '1-12'"); return; } } else { alert("无效的范围格式。请使用 '开始序号-结束序号',例如 '1-12'"); return; } } else if (cleanedInput.includes(',')) { inputIndices = cleanedInput.split(',') .map(s => parseInt(s.trim(), 10)) .filter(n => !isNaN(n) && n >= 1) .map(n => n - 1); } else if (/^\d+$/.test(cleanedInput)) { inputIndices = cleanedInput.split('') .map(s => parseInt(s, 10)) .filter(n => !isNaN(n) && n >= 1) .map(n => n - 1); } else { alert("无效的输入格式。请按提示输入序号、范围或 0"); return; } const validIndices = [...new Set(inputIndices)] .filter(index => index >= 0 && index < this.logs.length); let addedCount = 0; if (validIndices.length > 0) { validIndices.forEach(index => { const logEntry = this.logs[index]; if (logEntry && logEntry.content && !Whitelist.isWhitelistedContent(location.hostname, logEntry.content)) { Whitelist.add(location.hostname, logEntry.content); addedCount++; } }); if (addedCount > 0) { UIController.saveConfig(); GM_notification('已添加到白名单', `${addedCount} 项内容已添加到白名单`); location.reload(); } else { alert('未添加任何新的内容到白名单'); } } else if (input.length > 0) { alert('未找到匹配的日志序号或输入格式无效'); } } } }; const Whitelist = { init() { if (!CONFIG.whitelist) { CONFIG.whitelist = {}; } const currentDomain = new URL(location.href).hostname; if (!CONFIG.whitelist[currentDomain]) { CONFIG.whitelist[currentDomain] = []; } CONFIG.whitelist[currentDomain] = CONFIG.whitelist[currentDomain].filter(content => content && content.trim() !== '' && !content.includes('[无法获取内容]') && !content.includes('[安全限制无法获取内容]')); }, add(domain, content) { if (!domain || !content || content.trim() === '' || content.includes('[无法获取内容]') || content.includes('[安全限制无法获取内容]')) { return; } if (!CONFIG.whitelist[domain]) { CONFIG.whitelist[domain] = []; } if (!CONFIG.whitelist[domain].includes(content)) { CONFIG.whitelist[domain].push(content); } }, isWhitelisted(element) { if (!(element instanceof Element)) return false; const currentDomain = new URL(location.href).hostname; if (!CONFIG.whitelist[currentDomain] || CONFIG.whitelist[currentDomain].length === 0) { return false; } if (elementStateTracker.hasFlag(element, ElementFlags.WHITELISTED_EXPLICIT)) return true; const elementContent = SharedUtils.getElementContent(element); const isWhitelisted = CONFIG.whitelist[currentDomain].includes(elementContent); if (isWhitelisted) { elementStateTracker.setFlag(element, ElementFlags.WHITELISTED_EXPLICIT); } return isWhitelisted; }, isWhitelistedContent(domain, content) { if (!domain || !content || content.trim() === '') return false; if (!CONFIG.whitelist[domain]) { return false; } return CONFIG.whitelist[domain].includes(content); }, isAncestorWhitelisted(element) { if (!(element instanceof Element)) return false; if (elementStateTracker.hasFlag(element, ElementFlags.WHITELISTED_IMPLICIT)) { return true; } let current = element.parentElement; while (current) { if (this.isWhitelisted(current)) { elementStateTracker.setFlag(element, ElementFlags.WHITELISTED_IMPLICIT); return true; } current = current.parentElement; } return false; }, clear(domain) { if (CONFIG.whitelist[domain]) { CONFIG.whitelist[domain] = []; } }, clearCurrentDomain() { const currentDomain = new URL(location.href).hostname; this.clear(currentDomain); UIController.saveConfig(); }, clearAll() { CONFIG.whitelist = {}; UIController.saveConfig(); } }; const perf = { adElements: new Set(), processed: new WeakSet(), styleCache: new Map(), lastStyleCacheClear: Date.now(), performanceEntries: [], degraded: false, isProcessing: false }; const canProcessElement = (element) => { if (!CONFIG.modules.main || perf.degraded) return false; if (!(element instanceof Element)) return false; if (Whitelist.isWhitelisted(element) || Whitelist.isAncestorWhitelisted(element)) { return false; } if (element.dataset.adCleaned === 'true' || element.dataset.adNuclearRemoved === 'true') return false; return true; }; const Detector = { getCachedStyle(el) { if (!canProcessElement(el)) return {}; if (Date.now() - perf.lastStyleCacheClear > CONFIG.performance.styleCacheTimeout) { perf.styleCache.clear(); perf.lastStyleCacheClear = Date.now(); } const key = `${el.nodeName}#${el.id}.${Array.from(el.classList).join('.')}`; if (!perf.styleCache.has(key)) { try { perf.styleCache.set(key, getComputedStyle(el)); } catch (e) { return {}; } } return perf.styleCache.get(key); }, isVisible(el) { if (!canProcessElement(el)) return false; const style = Detector.getCachedStyle(el); if (style.display === 'none' || style.visibility === 'hidden' || parseFloat(style.opacity) < 0.01) return false; const rect = el.getBoundingClientRect(); return !( rect.width === 0 || rect.height === 0 || (rect.top >= window.innerHeight || rect.bottom <= 0) || (rect.left >= window.innerWidth || rect.right <= 0) ); } }; const ModuleManager = { modules: {}, register(name, module) { this.modules[name] = module; }, init() { Object.values(this.modules).forEach(module => { if (typeof module.init === 'function') { module.init(); } }); }, run(moduleKey, element) { if (!canProcessElement(element)) return false; if (!CONFIG.modules[moduleKey] || element.dataset.adCleaned === 'true' || element.dataset.adNuclearRemoved === 'true') return false; const module = this.modules[moduleKey]; if (module && typeof module.check === 'function') { const isBlocked = module.check(element); if (isBlocked) { perf.processed.add(element); } return isBlocked; } return false; } }; const AdUtils = { abortControllers: new WeakMap(), scriptObserver: null, isScriptHooksActive: false, _originalInnerHTMLGetter: null, _originalInnerHTMLSetter: null, _originalAppendChild: null, _originalCreateElement: null, _originalSetAttribute: null, removeElement(element, moduleKey, reason, isNuclear = false) { if (!canProcessElement(element)) return false; if (isNuclear) { element.dataset.adNuclearRemoved = 'true'; } else { element.dataset.adCleaned = 'true'; } Logs.add(moduleKey, element, { ...reason, detail: (isNuclear ? '[原子级移除] ' : '') + (reason.detail || ''), nuclear: isNuclear }); perf.processed.add(element); try { if (isNuclear) { const unsafeAttributes = element.tagName === 'SCRIPT' ? ['src', 'onload', 'onerror', 'integrity', 'type'] : ['src', 'href', 'data-src']; unsafeAttributes.forEach(attr => Utils.removeAttributeSafely(element, attr)); if (element.parentNode) { try { const comment = document.createComment(`Removed ad by ${moduleKey} [Nuclear]: ${reason.type}`); element.parentNode.replaceChild(comment, element); } catch (e) { element.remove(); } } else { element.remove(); } } else { if (element.parentNode) { element.parentNode.removeChild(element); } else { element.remove(); } } return true; } catch (e) { if (element instanceof Element) { element.dataset.adRemovalFailed = 'true'; } return false; } }, safeRemove(element, moduleKey, reason) { return this.removeElement(element, moduleKey, reason, false); }, nuclearRemove(element, moduleKey, reason) { return this.removeElement(element, moduleKey, reason, true); }, initScriptHooks() { if (this.isScriptHooksActive || perf.degraded || !CONFIG.modules.main) return; this.isScriptHooksActive = true; const self = this; if (!this._originalAppendChild) this._originalAppendChild = Node.prototype.appendChild; if (!this._originalCreateElement) this._originalCreateElement = document.createElement; if (!this._originalSetAttribute) this._originalSetAttribute = Element.prototype.setAttribute; Node.prototype.appendChild = function(node) { if (!canProcessElement(this) || !canProcessElement(node)) { return self._originalAppendChild.call(this, node); } if (node.nodeType === 1) { if (node.tagName === 'SCRIPT') { self.processScriptElementHook(node); if (node.dataset.adCleaned === 'true' || node.dataset.adNuclearRemoved === 'true') { return node; } } else { const potentialHTML = node.innerHTML; if (potentialHTML && REGEX.jsAdPattern.test(potentialHTML)) { const tempDiv = self._originalCreateElement.call(document, 'div'); tempDiv.innerHTML = potentialHTML; if (self.checkDynamicContent(tempDiv, node)) { AdUtils.safeRemove(node, 'dynamicSystem', { type: '非脚本元素可疑HTML内容', detail: `父元素appendChild: ${Utils.truncateString(potentialHTML, 100)}`, }); return null; } } } } return self._originalAppendChild.call(this, node); }; document.createElement = function(tagName) { const el = self._originalCreateElement.call(document, tagName); if (tagName.toLowerCase() === 'script') { if (!canProcessElement(el)) { return el; } return self.createScriptProxy(el); } return el; }; Element.prototype.setAttribute = function(name, value) { if (!canProcessElement(this)) { return self._originalSetAttribute.call(this, name, value); } if (name === 'innerHTML' && typeof value === 'string') { const tempDiv = self._originalCreateElement.call(document, 'div'); tempDiv.innerHTML = value; if (self.checkDynamicContent(tempDiv, this)) { AdUtils.safeRemove(this, 'dynamicSystem', { type: 'innerHTML注入广告', detail: `内容命中混淆/广告模式: ${Utils.truncateString(value, 100)}`, }); return; } } return self._originalSetAttribute.call(this, name, value); }; const innerHTMLDescriptor = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML'); if (innerHTMLDescriptor && innerHTMLDescriptor.set && innerHTMLDescriptor.get) { self._originalInnerHTMLSetter = innerHTMLDescriptor.set; self._originalInnerHTMLGetter = innerHTMLDescriptor.get; Object.defineProperty(Element.prototype, 'innerHTML', { get: function() { return self._originalInnerHTMLGetter.call(this); }, set: function(value) { if (!canProcessElement(this)) { return self._originalInnerHTMLSetter.call(this, value); } if (typeof value === 'string') { const tempDiv = self._originalCreateElement.call(document, 'div'); tempDiv.innerHTML = value; if (self.checkDynamicContent(tempDiv, this)) { AdUtils.safeRemove(this, 'dynamicSystem', { type: 'innerHTML属性注入广告', detail: `属性设置命中混淆/广告模式: ${Utils.truncateString(value, 100)}`, }); return self._originalInnerHTMLSetter.call(this, ''); } } return self._originalInnerHTMLSetter.call(this, value); }, configurable: true, enumerable: innerHTMLDescriptor.enumerable }); } this.scriptObserver = new MutationObserver(mutations => { if (!CONFIG.modules.main || perf.degraded) return; mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === 1 && node.tagName === 'SCRIPT') { if (!canProcessElement(node)) return; self.processScriptElementHook(node); } }); }); }); if (document.documentElement) { this.scriptObserver.observe(document.documentElement, { childList: true, subtree: true }); } else { window.addEventListener('DOMContentLoaded', () => { if (!CONFIG.modules.main || perf.degraded) return; this.scriptObserver.observe(document.documentElement, { childList: true, subtree: true }); }, { once: true }); } }, cleanupScriptHooks() { if (!this.isScriptHooksActive) return; this.isScriptHooksActive = false; if (this._originalAppendChild) { Node.prototype.appendChild = this._originalAppendChild; this._originalAppendChild = null; } if (this._originalCreateElement) { document.createElement = this._originalCreateElement; this._originalCreateElement = null; } if (this._originalSetAttribute) { Element.prototype.setAttribute = this._originalSetAttribute; this._originalSetAttribute = null; } if (this._originalInnerHTMLGetter && this._originalInnerHTMLSetter) { Object.defineProperty(Element.prototype, 'innerHTML', { get: this._originalInnerHTMLGetter, set: this._originalInnerHTMLSetter, configurable: true, enumerable: Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML')?.enumerable || true }); this._originalInnerHTMLGetter = null; this._originalInnerHTMLSetter = null; } if (this.scriptObserver) { this.scriptObserver.disconnect(); this.scriptObserver = null; } this.abortControllers.forEach(controller => controller.abort()); this.abortControllers.clear(); }, createScriptProxy(scriptElement) { return new Proxy(scriptElement, { set: (target, prop, value) => { if (!canProcessElement(target)) { target[prop] = value; return true; } if ((prop === 'src' || prop === 'innerHTML') && typeof value === 'string') { AdUtils.processScriptSource(target, prop, value); } target[prop] = value; return true; }, get: (target, prop) => { if (!canProcessElement(target)) { return Reflect.get(target, prop); } return Reflect.get(target, prop); } }); }, processScriptElementHook(scriptElement) { if (!canProcessElement(scriptElement)) return; scriptElement.dataset.adProcessedHook = 'true'; if (CONFIG.modules.thirdPartyBlock && ModuleManager.modules.thirdPartyBlock?.isThirdParty(scriptElement.src || scriptElement.getAttribute('data-src') || '')) { AdUtils.nuclearRemove(scriptElement, 'thirdPartyBlock', { type: '第三方脚本标签', detail: `SRC: ${Utils.truncateString(scriptElement.src || scriptElement.getAttribute('data-src') || '', 100)}` }); return; } if (CONFIG.modules.dynamicSystem && ModuleManager.modules.dynamicSystem) { ModuleManager.modules.dynamicSystem.handleScriptElement(scriptElement); } }, processScriptSource(scriptElement, prop, value) { if (!canProcessElement(scriptElement)) return; scriptElement.dataset.adProcessedSource = 'true'; if (CONFIG.modules.thirdPartyBlock && ModuleManager.modules.thirdPartyBlock?.isThirdParty(value)) { AdUtils.nuclearRemove(scriptElement, 'thirdPartyBlock', { type: '第三方脚本内容', detail: `${prop}: ${Utils.truncateString(value, 100)}` }); return; } if (CONFIG.modules.dynamicSystem && ModuleManager.modules.dynamicSystem) { ModuleManager.modules.dynamicSystem.handleScriptSource(scriptElement, value); } }, checkDynamicContent(htmlElement, contextElement = null) { if (!htmlElement || !(htmlElement instanceof Element)) return false; const dummyLogElementForContentCheck = document.createElement('span'); dummyLogElementForContentCheck.dataset.insertionType = contextElement ? contextElement.tagName : 'UNKNOWN_INSERT'; dummyLogElementForContentCheck.dataset.originalHTML = htmlElement.innerHTML; if (contextElement instanceof Element) { if (contextElement.id) dummyLogElementForContentCheck.id = contextElement.id; if (contextElement.className) dummyLogElementForContentCheck.className = contextElement.className; } const contentForWhitelistComparison = SharedUtils.getElementContent(dummyLogElementForContentCheck); if (Whitelist.isWhitelistedContent(location.hostname, contentForWhitelistComparison)) { return false; } const scripts = htmlElement.querySelectorAll('script'); for (const script of scripts) { if (Whitelist.isWhitelisted(script)) continue; if (script.src) { if (ModuleManager.modules.thirdPartyBlock?.isThirdParty(script.src)) return true; } else if (script.textContent) { const { dangerLevel } = ASTAnalyzer.calculateDangerLevel(script.textContent); if (dangerLevel >= 3) return true; } } const iframes = htmlElement.querySelectorAll('iframe'); for (const iframe of iframes) { if (Whitelist.isWhitelisted(iframe)) continue; if (iframe.src && ModuleManager.modules.thirdPartyBlock?.isThirdParty(iframe.src)) return true; } if (REGEX.adAttributes.test(htmlElement.outerHTML) || REGEX.jsAdPattern.test(htmlElement.outerHTML)) return true; return false; } }; const StyleManager = { styleElement: null, injectedSheets: new Set(), inject() { if (!CONFIG.modules.main || perf.degraded) return this.remove(); if (typeof CSSStyleSheet === 'function') { let isAdopted = false; for (const sheet of document.adoptedStyleSheets) { if (this.injectedSheets.has(sheet)) { isAdopted = true; break; } } if (!isAdopted) { try { const sheet = new CSSStyleSheet(); sheet.replaceSync(` [data-ad-cleaned="true"], [data-ad-nuclear-removed="true"] { all: initial !important; display: none !important; visibility: hidden !important; opacity: 0 !important; position: absolute !important; z-index: -9999 !important; width: 0 !important; height: 0 !important; pointer-events: none !important; overflow: hidden !important; transform: translate(-9999px, -9999px) !important; contain: layout style !important; animation: none !important; transition: none !important; filter: none !important; will-change: auto !important; } [data-ad-cleaned="true"] *, [data-ad-cleaned="true"] *::before, [data-ad-cleaned="true"] *::after, [data-ad-nuclear-removed="true"] *, [data-ad-nuclear-removed="true"] *::before, [data-ad-nuclear-removed="true"] *::after { all: unset !important; display: none !important; visibility: hidden !important; opacity: 0 !important; width: 0 !important; height: 0 !important; pointer-events: none !important; z-index: -9999 !important; contain: layout style !important; animation: none !important; transition: none !important; filter: none !important; will-change: auto !important; } `); document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet]; this.injectedSheets.add(sheet); } catch (e) {} } } if (!this.styleElement || !document.head || !document.head.contains(this.styleElement)) { this.styleElement = document.createElement('style'); this.styleElement.textContent = ` @layer adBlocker { *[data-ad-permanent="true"] { animation: none !important; transition: none !important; } } `; this.styleElement.setAttribute('data-ad-permanent', 'true'); if (document.head) { document.head.appendChild(this.styleElement); } else if (document.documentElement) { document.documentElement.insertBefore(this.styleElement, document.documentElement.firstChild); } } const checkStylesContinuously = () => { if (!CONFIG.modules.main || perf.degraded) return; if (this.styleElement && (!document.head || !document.head.contains(this.styleElement))) { if (document.head) { document.head.prepend(this.styleElement); } else if (document.documentElement) { document.documentElement.prepend(this.styleElement); } } if (typeof CSSStyleSheet === 'function') { let foundAdopted = false; for (const sheet of document.adoptedStyleSheets) { if (this.injectedSheets.has(sheet)) { foundAdopted = true; break; } } if (!foundAdopted && this.injectedSheets.size > 0) { const firstSheet = Array.from(this.injectedSheets)[0]; if (firstSheet) { document.adoptedStyleSheets = [...document.adoptedStyleSheets, firstSheet]; } } } requestAnimationFrame(checkStylesContinuously); }; if (CONFIG.modules.main && !perf.degraded) { requestAnimationFrame(checkStylesContinuously); } }, remove() { this.injectedSheets.forEach(sheet => { document.adoptedStyleSheets = document.adoptedStyleSheets.filter(s => s !== sheet); }); this.injectedSheets.clear(); if (this.styleElement) { this.styleElement.remove(); this.styleElement = null; } document.querySelectorAll('style[data-ad-permanent]').forEach(s => s.remove()); }, toggle(enable) { enable ? this.inject() : this.remove(); } }; const CSPManager = { currentPolicy: null, generatePolicy() { const merged = {}; CONFIG.csp.rules.filter(rule => rule.enabled).forEach(rule => { const [directive, ...values] = rule.rule.split(/\s+/); merged[directive] = (merged[directive] || []).concat(values); }); return Object.entries(merged) .map(([k, v]) => `${k} ${[...new Set(v)].join(' ')}`) .join('; '); }, inject() { this.remove(); if (!CONFIG.csp.enabled || perf.degraded) return; const policy = this.generatePolicy(); this.currentPolicy = policy; const meta = document.createElement('meta'); meta.httpEquiv = "Content-Security-Policy"; meta.content = policy; if (document.head) { document.head.appendChild(meta); } else { document.documentElement.insertBefore(meta, document.documentElement.firstChild); } }, remove() { const meta = document.querySelector('meta[http-equiv="Content-Security-Policy"]'); if (meta) meta.remove(); }, toggleRule(ruleId, enable) { const rule = CONFIG.csp.rules.find(r => r.id === ruleId); if (rule) rule.enabled = enable; }, injectInitialCSP() { if (!CONFIG.csp.enabled || perf.degraded) return; const initialRules = CONFIG.csp.rules .filter(rule => [1, 6].includes(rule.id) && rule.enabled) .map(rule => rule.rule) .join('; '); if (initialRules) { const meta = document.createElement('meta'); meta.httpEquiv = "Content-Security-Policy"; meta.content = initialRules; if (document.head) { document.head.appendChild(meta); } else { document.documentElement.insertBefore(meta, document.documentElement.firstChild); } } } }; const Processor = { scheduleProcess() { if (!CONFIG.modules.main || perf.isProcessing || perf.degraded) return; perf.isProcessing = true; requestIdleCallback(() => { if (!CONFIG.modules.main || perf.degraded) { perf.isProcessing = false; setTimeout(() => Processor.scheduleProcess(), 1000); return; } const startTime = performance.now(); const elementsToProcess = Array.from(perf.adElements).filter(el => canProcessElement(el)); perf.adElements.clear(); let processedCount = 0; for (const element of elementsToProcess) { if (performance.now() - startTime > CONFIG.performance.mutationProcessTimeLimit || processedCount >= CONFIG.performance.mutationProcessLimit) { for (let i = processedCount; i < elementsToProcess.length; i++) { if (canProcessElement(elementsToProcess[i])) { perf.adElements.add(elementsToProcess[i]); } } perf.isProcessing = false; Processor.scheduleProcess(); return; } if (!canProcessElement(element)) { continue; } this.processElement(element); processedCount++; } perf.isProcessing = false; if (perf.adElements.size > 0) { Processor.scheduleProcess(); } }, { timeout: CONFIG.performance.idleCallbackTimeout }); }, processElement(element) { if (!canProcessElement(element)) return; let modulePriority = [...CONFIG.modulePriority]; if (CONFIG.performance.visibleAreaPriority && Detector.isVisible(element)) { const highRisk = CONFIG.performance.highRiskModules; modulePriority = [...highRisk, ...modulePriority.filter(m => !highRisk.includes(m))]; } for (const moduleKey of modulePriority) { if (moduleKey === 'specialUA') continue; if (moduleKey === 'dynamicSystem' && element.tagName === 'SCRIPT') { continue; } if (moduleKey === 'thirdPartyBlock' && ['SCRIPT', 'IFRAME', 'IMG', 'LINK'].includes(element.tagName.toUpperCase())) { continue; } if (ModuleManager.run(moduleKey, element)) { return; } } if (ModuleManager.modules.dynamicSystem.checkCoreProcessor(element)) { return; } }, collectInitialElements() { if (!CONFIG.modules.main || perf.degraded) return; const treeWalker = document.createTreeWalker( document.documentElement, NodeFilter.SHOW_ELEMENT, null, false ); while (treeWalker.nextNode()) { const element = treeWalker.currentNode; if (!canProcessElement(element)) continue; perf.adElements.add(element); } } }; const mainObserver = new MutationObserver(mutations => { if (!CONFIG.modules.main || perf.degraded) return; mutations.forEach(mutation => { if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { if (node.nodeType === 8 && typeof node.data === 'string' && node.data.includes('Removed ad')) { try { if (node.parentNode) node.parentNode.removeChild(node); } catch (e) {} } }); mutation.addedNodes.forEach(node => { if (node.nodeType === 1) { if (!canProcessElement(node)) return; if (node.matches('[data-ad-permanent="true"],[data-ad-cleaned="true"],[data-ad-nuclear-removed="true"]')) { if (AdUtils.safeRemove(node, 'mutationClean', { type: 'DOM清理', detail: '检测到被移除元素的重新插入' })) return; } const walker = document.createTreeWalker( node, NodeFilter.SHOW_ELEMENT, { acceptNode: (n) => { if (!(n instanceof Element)) return NodeFilter.FILTER_SKIP; if (!canProcessElement(n)) return NodeFilter.FILTER_SKIP; return n.hasAttribute('data-ad-permanent') || n.hasAttribute('data-ad-cleaned') || n.hasAttribute('data-ad-nuclear-removed') ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; } } ); let currentNode; while ((currentNode = walker.nextNode())) { AdUtils.safeRemove(currentNode, 'mutationDepthClean', { type: 'DOM深度清理', detail: '检测到被移除元素的子元素' }); } if (canProcessElement(node)) { perf.adElements.add(node); } } }); } else if (mutation.type === 'attributes') { if (!canProcessElement(mutation.target)) return; if (mutation.target instanceof Element) { if (mutation.target.hasAttribute('data-ad-permanent') && mutation.attributeName !== 'data-ad-permanent') { Utils.removeAttributeSafely(mutation.target, mutation.attributeName); } if (canProcessElement(mutation.target)) { perf.adElements.add(mutation.target); } } } }); if (perf.adElements.size > 0 && !perf.isProcessing) { debouncedScheduleProcess(); } }); const debouncedScheduleProcess = Utils.debounce(Processor.scheduleProcess, CONFIG.performance.debounceMutationDelay); const UIController = { init() { this.loadConfig(); this._applyModuleDependencies(); Whitelist.init(); this.registerMenuCommands(); StyleManager.toggle(CONFIG.modules.main); }, _applyModuleDependencies() { const detectionModulesControlledByMain = ['dynamicSystem', 'layoutSystem', 'mergedMediaSystem', 'thirdPartyBlock']; CONFIG.modules.main = detectionModulesControlledByMain.some(key => CONFIG.modules[key]); }, registerMenuCommands() { GM_registerMenuCommand( `🔘 主开关 [${CONFIG.modules.main ? '✅' : '❌'}]`, () => this.toggleMain() ); Object.entries(CONFIG.moduleNames).forEach(([key, name]) => { if (key === 'main') return; GM_registerMenuCommand( `${name} [${CONFIG.modules[key] ? '✅' : '❌'}]`, () => this.toggleModule(key, name) ); }); GM_registerMenuCommand('📜 查看拦截日志', () => Logs.showInAlert()); GM_registerMenuCommand('🧹 清空日志', () => Logs.clear()); GM_registerMenuCommand('🚫 清空当前域名白名单', () => { if (confirm(`确定清空当前域名 (${location.hostname}) 的白名单吗?`)) { Whitelist.clearCurrentDomain(); GM_notification('白名单已清空', `当前域名 (${location.hostname}) 的白名单已清空`); location.reload(); } }); GM_registerMenuCommand('🛡️ CSP策略管理', () => this.manageCSP()); GM_registerMenuCommand('🔄 重置设置', () => this.resetSettings()); }, toggleMain() { const newState = !CONFIG.modules.main; const modulesToToggle = ['dynamicSystem', 'layoutSystem', 'mergedMediaSystem', 'thirdPartyBlock']; modulesToToggle.forEach(key => { CONFIG.modules[key] = newState; }); this._applyModuleDependencies(); this.saveConfig(); location.reload(); }, toggleModule(key, name) { CONFIG.modules[key] = !CONFIG.modules[key]; this._applyModuleDependencies(); this.saveConfig(); location.reload(); }, manageCSP() { const rulesDisplay = CONFIG.csp.rules .map(r => `${r.id}. ${r.name} (${r.enabled ? '✅' : '❌'})`) .join('\n'); let input = prompt( `当前CSP策略状态: ${CONFIG.csp.enabled ? '✅' : '❌'}\n\n` + "当前策略规则:\n" + rulesDisplay + "\n\n" + "输入选项:\nenable: 开启策略\ndisable: 关闭策略\n" + "修改规则示例:1on 启用规则1\n23off 禁用规则2和规则3\n\n" + "请输入你的选择:", "" ); if (input === null) return; input = input.trim().toLowerCase(); if (input === 'enable') { CONFIG.csp.enabled = true; this.saveConfig(); CSPManager.inject(); alert("CSP策略已启用"); location.reload(); } else if (input === 'disable') { CONFIG.csp.enabled = false; this.saveConfig(); CSPManager.remove(); alert("CSP策略已禁用"); location.reload(); } else if (/^\d+(?:on|off)$/i.test(input)) { this.modifyCSPRules(input); } else { alert("无效输入"); } }, modifyCSPRules(actionInput) { const matches = actionInput.match(/^(\d+)(on|off)$/i); if (matches) { const ruleIds = matches[1].split('').map(Number); const enable = matches[2].toLowerCase() === 'on'; let updatedRules = []; ruleIds.forEach(id => { const rule = CONFIG.csp.rules.find(r => r.id === id); if (rule) { rule.enabled = enable; updatedRules.push(id); } }); if (updatedRules.length > 0) { this.saveConfig(); CSPManager.inject(); alert(`规则 ${updatedRules.join(',')} 已更新为 ${enable ? '启用' : '禁用'}`); location.reload(); } else { alert("未找到匹配的规则ID"); } } else { alert("输入格式错误,请按示例输入如'1on'或'123off'"); } }, resetSettings() { if (confirm("确定要重置所有设置吗?这将清空设置和白名单")) { Object.assign(CONFIG.modules, DEFAULT_MODULES); this._applyModuleDependencies(); CONFIG.csp.enabled = false; CONFIG.csp.rules = DEFAULT_CSP_RULES.map(rule => ({ ...rule })); Whitelist.clearAll(); this.saveConfig(); CSPManager.remove(); Logs.clear(); alert("所有设置已重置为默认值"); location.reload(); } }, saveConfig() { try { GM_setValue(`adblockConfig_${location.hostname}`, JSON.stringify({ modules: CONFIG.modules, csp: CONFIG.csp, whitelist: CONFIG.whitelist })); } catch (e) { console.error("Error saving adblock config:", e); } }, loadConfig() { try { const savedConfig = JSON.parse(GM_getValue(`adblockConfig_${location.hostname}`, '{}')); if (savedConfig.modules) { Object.keys(CONFIG.modules).forEach(key => { if (savedConfig.modules.hasOwnProperty(key)) { CONFIG.modules[key] = savedConfig.modules[key]; } }); } if (savedConfig.csp) { CONFIG.csp.enabled = savedConfig.csp.enabled; CONFIG.csp.rules = DEFAULT_CSP_RULES.map(defaultRule => { const savedRule = savedConfig.csp.rules.find(sr => sr.id === defaultRule.id); return savedRule ? { ...defaultRule, enabled: savedRule.enabled } : defaultRule; }); } if (savedConfig.whitelist) { CONFIG.whitelist = savedConfig.whitelist; } } catch (e) { console.error("Error loading adblock config, resetting to default:", e); Object.assign(CONFIG.modules, DEFAULT_MODULES); CONFIG.csp.enabled = false; CONFIG.csp.rules = DEFAULT_CSP_RULES.map(rule => ({ ...rule })); CONFIG.whitelist = {}; } } }; const ASTAnalyzer = (function() { return { getObfuscationRules: function(minLevel) { return REGEX.obfuscationPatterns.filter(r => r.riskLevel >= minLevel); }, checkObfuscation: function(code, minLevel) { const rules = this.getObfuscationRules(minLevel); for (let i = 0; i < rules.length; i++) { try { if (rules[i].pattern.test(code)) { return { found: true, type: rules[i].type, ruleName: rules[i].type }; } } catch (e) {} } return { found: false, type: null, ruleName: null }; }, checkHighRiskPatterns: function(code) { for (const p of REGEX.obfuscationPatterns) { if (p.riskLevel >= 3 && p.pattern.test(code)) { return { found: true, type: p.type, ruleName: p.type }; } } return { found: false, type: null, ruleName: null }; }, calculateDangerLevel: function(content) { let dangerLevel = 0; let specificRules = new Set(); const generalObfResult = this.checkObfuscation(content, 2); if (generalObfResult.found) { dangerLevel += 2; specificRules.add(generalObfResult.type); } const higherObfResult = this.checkObfuscation(content, 3); if (higherObfResult.found) { dangerLevel += 3; specificRules.add(higherObfResult.type); } if (REGEX.jsAdPattern.test(content)) { dangerLevel += 2; specificRules.add('可疑JS广告模式'); } const highRiskPatternsResult = this.checkHighRiskPatterns(content); if (highRiskPatternsResult.found) { if (!specificRules.has(highRiskPatternsResult.type)) { dangerLevel += 3; } specificRules.add(highRiskPatternsResult.type); } return { dangerLevel, specificRules: Array.from(specificRules) }; } }; })(); ModuleManager.register('thirdPartyBlock', { originals: { createElement: null, setAttribute: null, appendChild: null, fetch: null, xhrOpen: null, documentWrite: null, documentWriteln: null, insertAdjacentHTML: null }, blockedUrls: new Set(), enabled: false, hostCache: new Map(), init() { if (CONFIG.modules.thirdPartyBlock) this.enable(); else this.disable(); }, enable() { if (this.enabled) return; this.enabled = true; this.originals.createElement = document.createElement; this.originals.setAttribute = Element.prototype.setAttribute; this.originals.appendChild = Node.prototype.appendChild; this.originals.fetch = window.fetch; this.originals.xhrOpen = XMLHttpRequest.prototype.open; this.originals.documentWrite = document.write; this.originals.documentWriteln = document.writeln; this.originals.insertAdjacentHTML = Element.prototype.insertAdjacentHTML; document.createElement = this.wrapElementCreation.bind(this); Element.prototype.setAttribute = this.wrapSetAttribute.bind(this); Node.prototype.appendChild = this.wrapAppendChild.bind(this); window.fetch = this.wrapFetch.bind(this); XMLHttpRequest.prototype.open = this.wrapXHROpen.bind(this); document.write = this.wrapDocumentWrite.bind(this); document.writeln = this.wrapDocumentWriteln.bind(this); Element.prototype.insertAdjacentHTML = this.wrapInsertAdjacentHTML.bind(this); document.addEventListener('beforescriptexecute', this.handleBeforeScriptExecute.bind(this)); }, disable() { if (!this.enabled) return; this.enabled = false; document.createElement = this.originals.createElement; Element.prototype.setAttribute = this.originals.setAttribute; Node.prototype.appendChild = this.originals.appendChild; window.fetch = this.originals.fetch; XMLHttpRequest.prototype.open = this.originals.xhrOpen; document.write = this.originals.documentWrite; document.writeln = this.originals.documentWriteln; Element.prototype.insertAdjacentHTML = this.originals.insertAdjacentHTML; document.removeEventListener('beforescriptexecute', this.handleBeforeScriptExecute); }, logAndBlock(element, type, url, detail = '') { if (!canProcessElement(element)) { return false; } if (this.blockedUrls.has(url)) { return false; } this.blockedUrls.add(url); const finalDetail = `URL: ${Utils.truncateString(url, 150)}` + (detail ? `, ${detail}` : ''); const isScriptOrIframe = element?.tagName === 'SCRIPT' || element?.tagName === 'IFRAME'; const removed = isScriptOrIframe ? AdUtils.nuclearRemove(element, 'thirdPartyBlock', { type: `第三方${type}`, detail: finalDetail }) : AdUtils.safeRemove(element, 'thirdPartyBlock', { type: `第三方${type}`, detail: finalDetail }); return removed; }, isThirdParty(url) { if (!url || typeof url !== 'string') return false; if (url.startsWith('data:') || url.startsWith('blob:') || url.startsWith('about:blank')) return false; const currentHost = new URL(location.href).hostname; if (this.hostCache.has(url)) { return this.hostCache.get(url); } try { const resourceHost = new URL(url, location.href).hostname; const isTP = !(resourceHost.endsWith(`.${currentHost}`) || resourceHost === currentHost); this.hostCache.set(url, isTP); return isTP; } catch (e) { this.hostCache.set(url, true); return true; } }, wrapSetAttribute(name, value) { if (!canProcessElement(this) || perf.degraded) return this.originals.setAttribute.call(this, name, value); if (name === 'src' || name === 'href' || name === 'data-src') { if (typeof value === 'string' && this.isThirdParty(value)) { if (this.logAndBlock(this, `${this.tagName}属性拦截`, value, `属性: ${name}`)) { return; } } } return this.originals.setAttribute.call(this, name, value); }, wrapAppendChild(node) { if (!canProcessElement(this) || !canProcessElement(node) || perf.degraded) { return this.originals.appendChild.call(this, node); } if (node.nodeType === 1) { const src = node.src || node.getAttribute('data-src') || node.href || ''; if (src && this.isThirdParty(src)) { if (this.logAndBlock(node, `${node.tagName}插入拦截`, src, `父元素: ${this.tagName}`)) { return null; } } } return this.originals.appendChild.call(this, node); }, wrapElementCreation(tagName) { const element = this.originals.createElement.call(document, tagName); const lowerTag = tagName.toLowerCase(); if (['script', 'iframe', 'img', 'link'].includes(lowerTag)) { return new Proxy(element, { set: (target, prop, value) => { if (!canProcessElement(target) || perf.degraded) { target[prop] = value; return true; } if ((prop === 'src' || prop === 'href' || prop === 'data-src') && typeof value === 'string') { if (this.isThirdParty(value)) { this.logAndBlock(target, `${target.tagName}创建属性设置`, value, `属性: ${prop}`); target[prop] = ''; target.setAttribute('data-tpi-blocked', 'true'); return true; } } target[prop] = value; return true; }, get: (target, prop) => { if (!canProcessElement(target) || perf.degraded) { return Reflect.get(target, prop); } if ((prop === 'src' || prop === 'href' || prop === 'data-src') && target.hasAttribute('data-tpi-blocked')) { return ''; } return Reflect.get(target, prop); } }); } return element; }, wrapFetch(input, init) { if (perf.degraded) return this.originals.fetch(input, init); const url = typeof input === 'string' ? input : input?.url; if (url && typeof url === 'string') { const dummyElement = document.createElement('span'); dummyElement.dataset.url = url; dummyElement.dataset.tag = 'FETCH'; dummyElement.dataset.detail = `Method: ${init?.method || 'GET'}`; const logContent = SharedUtils.getElementContent(dummyElement); if (Whitelist.isWhitelistedContent(location.hostname, logContent)) { return this.originals.fetch(input, init); } if (this.isThirdParty(url)) { this.logAndBlock(dummyElement, 'Fetch请求', url, `Method: ${init?.method || 'GET'}`); return Promise.reject(new Error('第三方请求被拦截')); } } return this.originals.fetch(input, init); }, wrapXHROpen(method, url) { if (perf.degraded) return this.originals.xhrOpen.call(this, method, url); if (url && typeof url === 'string') { const dummyElement = document.createElement('span'); dummyElement.dataset.url = url; dummyElement.dataset.tag = 'XHR'; dummyElement.dataset.detail = `Method: ${method}`; const logContent = SharedUtils.getElementContent(dummyElement); if (Whitelist.isWhitelistedContent(location.hostname, logContent)) { return this.originals.xhrOpen.call(this, method, url); } if (this.isThirdParty(url)) { this.logAndBlock(dummyElement, 'XHR请求', url, `Method: ${method}`); this._blocked = true; return; } } return this.originals.xhrOpen.call(this, method, url); }, handleHTMLInsertion(html, insertionType, originalFunction, contextElement = null) { if (perf.degraded) return originalFunction(html); const dummyLogElementForContentCheck = document.createElement('span'); dummyLogElementForContentCheck.dataset.insertionType = insertionType; dummyLogElementForContentCheck.dataset.originalHTML = html; if (contextElement instanceof Element) { if (contextElement.id) dummyLogElementForContentCheck.id = contextElement.id; if (contextElement.className) dummyLogElementForContentCheck.className = contextElement.className; } const contentForWhitelistComparison = SharedUtils.getElementContent(dummyLogElementForContentCheck); if (Whitelist.isWhitelistedContent(location.hostname, contentForWhitelistComparison)) { return originalFunction(html); } const tempDiv = document.createElement('div'); tempDiv.innerHTML = html; if (AdUtils.checkDynamicContent(tempDiv, contextElement)) { AdUtils.safeRemove(dummyLogElementForContentCheck, 'thirdPartyBlock', { type: `动态插入HTML内容`, detail: `插入方式: ${insertionType}, 包含可疑内容` }); return; } return originalFunction(html); }, wrapDocumentWrite(content) { return this.handleHTMLInsertion(content, 'document.write', this.originals.documentWrite); }, wrapDocumentWriteln(content) { return this.handleHTMLInsertion(content, 'document.writeln', this.originals.documentWriteln); }, wrapInsertAdjacentHTML(position, html) { return this.handleHTMLInsertion(html, `insertAdjacentHTML:${position}`, (processedHtml) => this.originals.insertAdjacentHTML.call(this, position, processedHtml), this); }, handleBeforeScriptExecute(e) { const script = e.target; if (!canProcessElement(script) || perf.degraded) return; const src = script.src || script.getAttribute('data-src') || ''; if (src && this.isThirdParty(src)) { if (this.logAndBlock(script, '预执行脚本', src)) { e.preventDefault(); e.stopImmediatePropagation(); script.remove(); return; } } }, check(el) { if (!canProcessElement(el) || perf.degraded || !this.enabled) return false; if (!(el instanceof Element)) return false; if (el.dataset.adCleaned || el.dataset.adNuclearRemoved) return false; const src = el.src || el.getAttribute('data-src') || el.href || ''; if (src && this.isThirdParty(src)) { const tagName = el.tagName; if (['SCRIPT', 'IFRAME', 'IMG', 'LINK'].includes(tagName.toUpperCase())) { return this.logAndBlock(el, 'DOM元素检查', src, `标签: ${tagName}`); } } const style = Detector.getCachedStyle(el); if (style?.backgroundImage && style.backgroundImage.includes('url(')) { const bgUrlMatch = style.backgroundImage.match(/url\((['"]?)(.*?)\1\)/); if (bgUrlMatch && bgUrlMatch[2] && this.isThirdParty(bgUrlMatch[2])) { return this.logAndBlock(el, '背景图片', bgUrlMatch[2], `CSS背景: ${el.tagName}`); } } return false; } }); ModuleManager.register('dynamicSystem', { checkedScripts: new WeakSet(), scriptAnalysisQueue: [], isProcessingAnalysisQueue: false, enabled: false, init() { if (CONFIG.modules.dynamicSystem) { this.enable(); } else { this.disable(); } this.startScriptRecoveryCheck(); this.startAnalysisQueueProcessor(); }, enable() { if (this.enabled || perf.degraded) return; this.enabled = true; AdUtils.initScriptHooks(); }, disable() { if (!this.enabled) return; this.enabled = false; this.scriptAnalysisQueue = []; AdUtils.cleanupScriptHooks(); }, handleScriptElement(scriptEl) { if (!canProcessElement(scriptEl) || perf.degraded) return; if (scriptEl.dataset.adCleaned || scriptEl.dataset.adNuclearRemoved) return; if (!this.enabled) return; this.checkedScripts.add(scriptEl); if (this.quickDetect(scriptEl)) { return; } this.scriptAnalysisQueue.push(scriptEl); if (!this.isProcessingAnalysisQueue) { this.processAnalysisQueue(); } }, handleScriptSource(scriptEl, sourceValue) { if (!canProcessElement(scriptEl) || perf.degraded) return; if (scriptEl.dataset.adCleaned || scriptEl.dataset.adNuclearRemoved) return; if (!this.enabled) return; this.checkedScripts.add(scriptEl); const highRiskResult = ASTAnalyzer.checkHighRiskPatterns(sourceValue); if (highRiskResult.found) { AdUtils.nuclearRemove(scriptEl, 'dynamicSystem', { type: '动态脚本源快速检测', detail: '源内容命中高危模式', ruleName: highRiskResult.type }); return; } this.scriptAnalysisQueue.push(scriptEl); if (!this.isProcessingAnalysisQueue) { this.processAnalysisQueue(); } }, async processAnalysisQueue() { if (!this.enabled || this.isProcessingAnalysisQueue || this.scriptAnalysisQueue.length === 0 || perf.degraded) return; this.isProcessingAnalysisQueue = true; while (this.scriptAnalysisQueue.length > 0) { const script = this.scriptAnalysisQueue.shift(); if (!document.contains(script) || !canProcessElement(script) || script.dataset.adCleaned || script.dataset.adNuclearRemoved) { continue; } await this.analyzeScript(script); if (this.scriptAnalysisQueue.length > 5 || performance.now() % 100 < 10) { await new Promise(resolve => requestIdleCallback(resolve, { timeout: 50 })); } } this.isProcessingAnalysisQueue = false; }, async analyzeScript(scriptEl) { if (!canProcessElement(scriptEl) || perf.degraded) return; if (scriptEl.dataset.adCleaned || scriptEl.dataset.adNuclearRemoved) return; if (!this.enabled) return; try { if (scriptEl.src) { await this.analyzeRemoteScript(scriptEl); } else { await this.analyzeInlineScript(scriptEl); } } catch (e) {} }, async analyzeRemoteScript(scriptEl) { if (!canProcessElement(scriptEl) || perf.degraded) return; if (scriptEl.dataset.adCleaned || scriptEl.dataset.adNuclearRemoved) return; if (!this.enabled) return; const controller = new AbortController(); AdUtils.abortControllers.set(scriptEl, controller); try { const response = await fetch(scriptEl.src, { signal: controller.signal, priority: 'low' }); const reader = response.body.getReader(); const { value, done } = await reader.read(); if (done) return; const chunk = new TextDecoder().decode(value); reader.cancel(); const highRiskResult = ASTAnalyzer.checkHighRiskPatterns(chunk); if (highRiskResult.found) { AdUtils.nuclearRemove(scriptEl, 'dynamicSystem', { type: '流式特征阻断', detail: `检测到脚本前${chunk.length}字节风险内容`, ruleName: highRiskResult.type }); } } catch (e) { if (e.name === 'AbortError') { } else { } } finally { AdUtils.abortControllers.delete(scriptEl); } }, async analyzeInlineScript(scriptEl) { if (!canProcessElement(scriptEl) || perf.degraded) return; if (scriptEl.dataset.adCleaned || scriptEl.dataset.adNuclearRemoved) return; if (!this.enabled) return; const content = scriptEl.textContent; const { dangerLevel, specificRules } = ASTAnalyzer.calculateDangerLevel(content); if (dangerLevel >= 3) { AdUtils.nuclearRemove(scriptEl, 'dynamicSystem', { type: '内联脚本风险', detail: `危险级别: ${dangerLevel}`, ruleName: specificRules.join('; ') }); } }, quickDetect(scriptEl) { if (!canProcessElement(scriptEl) || perf.degraded) return false; if (scriptEl.dataset.adCleaned || scriptEl.dataset.adNuclearRemoved) return false; if (!this.enabled) return false; const attrs = Array.from(scriptEl.attributes); const shortVarPattern = /^[a-z\d]{1,3}$/i; const signaturePattern = /^(?:[\da-f]{32}|[\w+/=]{40,})$/i; const alphanumAlternate = /(?:[a-z]\d|\d[a-z]){5,}/gi; let removed = false; for (const attr of attrs) { const isShortVar = shortVarPattern.test(attr.name); const isSignatureValue = signaturePattern.test(attr.value); const isAlphanumAlternateValue = (typeof attr.value === 'string' && attr.value.match(alphanumAlternate)?.length > 3); const isDynamicName = /\[(?:0x[a-f\d]+|\d+)\]/.test(attr.name); const isLongComplexValue = (typeof attr.value === 'string' && attr.value.includes(';') && attr.value.length > 60); const isLongName = attr.name.length > 20; let ruleName = ''; let detail = ''; if (isShortVar && (isSignatureValue || isAlphanumAlternateValue)) { ruleName = `短变量名/${isSignatureValue ? '签名' : '字母数字交替'}混淆`; detail = `短变量名结合签名/编码值: ${attr.name}=${attr.value.slice(0, 50)}`; } else if (isLongName) { ruleName = '超长属性名混淆'; detail = `超长属性名: ${attr.name.slice(0, 50)}`; } else if (isDynamicName) { ruleName = '动态属性名混淆'; detail = `动态属性名: ${attr.name.slice(0, 50)}`; } else if (isLongComplexValue) { ruleName = '复杂内联属性值混淆'; detail = `复杂内联属性值: ${attr.name}=${attr.value.slice(0, 50)}`; } if (ruleName) { removed = AdUtils.nuclearRemove(scriptEl, 'dynamicSystem', { type: '属性混淆快速检测', detail: detail, ruleName: ruleName }); if (removed) return true; } } if (!scriptEl.src && scriptEl.textContent) { const content = scriptEl.textContent; const highRiskResult = ASTAnalyzer.checkHighRiskPatterns(content); if (highRiskResult.found) { removed = AdUtils.nuclearRemove(scriptEl, 'dynamicSystem', { type: '内联脚本快速检测', detail: '内联脚本命中高危模式', ruleName: highRiskResult.type }); if (removed) return true; } const specificObfResult = ASTAnalyzer.checkObfuscation(content, 5); if (specificObfResult.found) { removed = AdUtils.nuclearRemove(scriptEl, 'dynamicSystem', { type: '内联脚本结构性混淆', detail: `检测到高风险结构性混淆: ${specificObfResult.ruleName}`, ruleName: specificObfResult.ruleName }); if (removed) return true; } } return removed; }, startScriptRecoveryCheck() { setInterval(() => { if (!this.enabled || perf.degraded) return; document.querySelectorAll('script:not([data-ad-cleaned]):not([data-ad-nuclear-removed]):not([data-ad-processed-hook]):not([data-ad-processed-source])').forEach(script => { if (!canProcessElement(script) || perf.degraded) return; this.handleScriptElement(script); }); }, 1000); }, startAnalysisQueueProcessor() { setInterval(() => { if (!this.enabled || perf.degraded) return; if (!this.isProcessingAnalysisQueue && this.scriptAnalysisQueue.length > 0) { this.processAnalysisQueue(); } }, 500); }, check(el) { if (!canProcessElement(el) || perf.degraded || !this.enabled) return false; return false; }, checkCoreProcessor(el) { if (!canProcessElement(el) || !this.enabled || perf.degraded) return false; const shortVarPattern = /^[a-z\d]{1,3}$/i; const signaturePattern = /^(?:[\da-f]{32}|[\w+/=]{40,})$/i; const alphanumAlternate = /(?:[a-z]\d|\d[a-z]){5,}/gi; const isObfuscated = Array.from(el.attributes).some(attr => { const isShortVar = shortVarPattern.test(attr.name); const isObfuscatedValue = signaturePattern.test(attr.value) || (typeof attr.value === 'string' && attr.value.match(alphanumAlternate)?.length > 3); const isDynamicName = /\[(?:0x[a-f\d]+|\d+)\]/.test(attr.name); return (isShortVar && isObfuscatedValue) || attr.name.length > 20 || isDynamicName || (typeof attr.value === 'string' && attr.value.includes(';') && attr.value.length > 60); }); if (isObfuscated) { return AdUtils.safeRemove(el, 'dynamicSystem', { type: '核心检测: 可疑混淆属性', detail: `元素属性疑似混淆` }); } if (el.id && ( el.id.length > CONFIG.protectionRules.dynamicIdLength || REGEX.dynamicId.test(el.id) )) { return AdUtils.safeRemove(el, 'dynamicSystem', { type: '核心检测: 可疑动态ID', detail: `ID特征: ${Utils.truncateString(el.id, 50)}` }); } if (el.tagName === 'A') { const href = el.getAttribute('href') || ''; if (href.startsWith('javascript:') && REGEX.jsAdPattern.test(href)) { return AdUtils.nuclearRemove(el, 'dynamicSystem', { type: '核心检测: 可疑JS链接广告', detail: `执行代码: ${Utils.truncateString(href, 100)}`, regex: [REGEX.jsAdPattern.source] }); } } return false; } }); ModuleManager.register('layoutSystem', { enabled: false, LAYOUT_SEMANTIC_WHITELIST_TAGS: new Set([ 'HEADER', 'FOOTER', 'NAV', 'ASIDE', 'MAIN', 'ARTICLE', 'SECTION', 'DIV', 'SPAN', 'BUTTON', 'A', 'INPUT', 'TEXTAREA', 'SELECT', 'FORM', 'LABEL', 'OPTION', 'DATALIST', 'OPTGROUP', 'IMG', 'SVG', 'I', 'B', 'STRONG', 'EM', 'SMALL', 'BR', 'HR', 'UL', 'OL', 'LI', 'DL', 'DT', 'DD', 'TABLE', 'THEAD', 'TBODY', 'TR', 'TH', 'TD', 'TFOOT', 'CAPTION', 'COLGROUP', 'COL', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'P', 'BLOCKQUOTE', 'PRE', 'CODE', 'PROGRESS', 'CANVAS', 'AUDIO', 'VIDEO', 'SOURCE', 'TRACK', 'EMBED', 'OBJECT', 'DIALOG', 'DETAILS', 'SUMMARY', 'FIGURE', 'FIGCAPTION', 'PICTURE' ]), LAYOUT_SEMANTIC_WHITELIST_CLASSES: new Set([ 'modal', 'navbar', 'toolbar', 'menu', 'button', 'link', 'icon', 'spinner', 'loader', 'carousel', 'slider', 'tab', 'tabs', 'accordion', 'overlay', 'backdrop', 'dropdown-menu', 'tooltip', 'popover', 'alert', 'toast', 'notification', 'header', 'footer', 'nav', 'sidebar', 'content', 'main', 'wrapper', 'container', 'item', 'card', 'box', 'panel', 'widget', 'title', 'text', 'image', 'scroll-container', 'scroller', 'lazyload', 'placeholder', 'skeleton', 'skip-link', 'sr-only', 'btn', 'form-control', 'input-group', 'badge', 'chip', 'tag', 'avatar', 'thumbnail', 'product-image', 'user-icon', 'post-content', 'news-article', 'article-body', 'ad-container-false', 'footer-bottom', 'back-to-top', 'scroll-top', 'cookie-banner', 'cookie-consent', 'gdpr-banner', 'privacy-policy', 'message-bar', 'mobile-nav', 'offcanvas-menu' ]), init() { if (CONFIG.modules.layoutSystem) this.enable(); else this.disable(); }, enable() { if (this.enabled || perf.degraded) return; this.enabled = true; }, disable() { this.enabled = false; }, isLegitimateSemanticElement(el) { if (!el || !(el instanceof Element)) return false; const tagName = el.tagName.toUpperCase(); const classList = Array.from(el.classList); if (this.LAYOUT_SEMANTIC_WHITELIST_TAGS.has(tagName)) { if (['DIV', 'SPAN'].includes(tagName)) { if (classList.some(cls => this.LAYOUT_SEMANTIC_WHITELIST_CLASSES.has(cls.toLowerCase())) || el.hasAttribute('role') || el.hasAttribute('aria-label') || el.hasAttribute('aria-hidden') || el.hasAttribute('tabindex')) { return true; } if (el.textContent.trim().length > 50 && el.children.length > 2 && !Utils.isAdCharacteristic(el)) { return true; } const rect = el.getBoundingClientRect(); if ((rect.width > 0 && rect.width < 20 && rect.height > 0 && rect.height < 20) && (classList.some(cls => cls.includes('icon') || cls.includes('spinner') || cls.includes('loader') || cls.includes('placeholder')) || el.hasAttribute('aria-hidden'))) { return true; } } else if (['IMG', 'SVG', 'I', 'PICTURE'].includes(tagName)) { const altText = el.getAttribute('alt')?.toLowerCase() || ''; const ariaLabel = el.getAttribute('aria-label')?.toLowerCase() || ''; if (altText.length > 5 && !REGEX.adAttributes.test(altText) || ariaLabel.length > 5 && !REGEX.adAttributes.test(ariaLabel) || classList.some(cls => cls.includes('icon') || cls.includes('logo') || cls.includes('avatar') || cls.includes('thumbnail')) || el.hasAttribute('aria-hidden') ) { return true; } const rect = el.getBoundingClientRect(); if (rect.width === 1 && rect.height === 1 && !el.hasAttribute('aria-hidden') && !classList.some(cls => cls.includes('icon') || cls.includes('logo'))) { return false; } return true; } else { return true; } } const role = el.getAttribute('role'); if (role && ( role === 'dialog' || role === 'button' || role === 'link' || role === 'navigation' || role === 'banner' || role === 'contentinfo' || role === 'main' || role === 'complementary' || role === 'alert' || role === 'status' || role === 'log' || role === 'tablist' || role === 'tab' || role === 'tabpanel' || role === 'toolbar' || role === 'menu' || role === 'menubar' || role === 'menuitem' || role === 'grid' || role === 'gridcell' || role === 'row' || role === 'rowgroup' || role === 'columnheader' || role === 'rowheader' || role === 'group' || role === 'figure' )) { return true; } if (el.hasAttribute('aria-modal') || el.hasAttribute('aria-label') || el.hasAttribute('aria-labelledby') || el.hasAttribute('aria-describedby') || el.getAttribute('aria-hidden') === 'true') { return true; } if (classList.some(cls => this.LAYOUT_SEMANTIC_WHITELIST_CLASSES.has(cls.toLowerCase()))) { return true; } const textContent = el.textContent.trim(); if (textContent.length > 5 && !Utils.isAdCharacteristic(el)) { return true; } if (['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON', 'LABEL'].includes(tagName)) return true; const parent = el.parentElement; if (parent && this.isLegitimateSemanticElement(parent)) { if (!Utils.isAdCharacteristic(el)) { return true; } } return false; }, check(el) { if (!canProcessElement(el) || perf.degraded || !this.enabled) return false; if (!(el instanceof Element)) return false; if (this.isLegitimateSemanticElement(el)) { return false; } if (['HTML', 'BODY'].includes(el.tagName.toUpperCase())) return false; if (el.tagName.toUpperCase() === 'IMG') return false; const style = Detector.getCachedStyle(el); if (!style) return false; const rect = el.getBoundingClientRect(); const zIndex = parseInt(style.zIndex, 10); if (!isNaN(zIndex) && zIndex > CONFIG.protectionRules.zIndexThreshold) { const isLegitHighZUI = ( el.closest('[role="dialog"], [aria-modal="true"], dialog[open], .modal-content, .fancybox-container, .lightbox-container, .swiper-slide, .toast, .alert, .notification') || el.classList.contains('modal') || el.classList.contains('dialog') || el.classList.contains('overlay') || el.classList.contains('popup') || el.classList.contains('notification-bar') || el.classList.contains('cookie-banner') ); if (!isLegitHighZUI) { return AdUtils.safeRemove(el, 'layoutSystem', { type: '高Z轴元素', detail: `z-index: ${zIndex}` }); } } if (style.pointerEvents === 'none') { const isAdRelatedName = Utils.isAdCharacteristic(el); const isLargeArea = rect.width >= window.innerWidth * 0.5 && rect.height >= window.innerHeight * 0.5; const isFixedOrAbsolute = ['fixed', 'absolute'].includes(style.position); const hasClickableChildren = el.querySelector('a[href]:not([href="#"]), button, [onclick], [role="button"], [tabindex]') !== null; const hasSignificantContent = el.textContent.trim().length > 50 || el.children.length > 3; const isPurelyVisualChild = el.children.length === 1 && ['IMG', 'VIDEO', 'CANVAS', 'SVG'].includes(el.children[0].tagName.toUpperCase()); const isDecorativeOrLoadingElement = ( (rect.width > 0 && rect.width < 10 && rect.height > 0 && rect.height < 10) || (parseFloat(style.opacity) < 0.2 && style.backgroundImage === 'none' && style.backgroundColor === 'rgba(0, 0, 0, 0)') || el.matches('[class*="icon"], [class*="spinner"], [class*="loading"], [class*="backdrop"], [class*="overlay-transparent"]') ); if (isLargeArea && isFixedOrAbsolute && (isAdRelatedName || isPurelyVisualChild) && !isDecorativeOrLoadingElement && !hasSignificantContent && !hasClickableChildren) { if (el.parentNode === document.documentElement || el.parentNode === document.body) { return false; } return AdUtils.safeRemove(el, 'layoutSystem', { type: '不可交互覆盖层', detail: `pointer-events:none, 大尺寸固定/绝对定位, ${isAdRelatedName?'广告特征':''}${isPurelyVisualChild?'单视觉子元素':''}` }); } } const isScrollable = (el.scrollWidth > el.clientWidth && (style.overflowX === 'scroll' || style.overflowX === 'auto')) || (el.scrollHeight > el.clientHeight && (style.overflowY === 'scroll' || style.overflowY === 'auto')); if (style.overflow === 'hidden' && !isScrollable) { const isAdRelated = Utils.isAdCharacteristic(el); const isEmptyContainer = el.textContent.trim() === '' && el.children.length === 0; const isLegitHiddenContent = ( el.classList.contains('hidden') || el.classList.contains('collapse') || el.classList.contains('tab-pane') || el.hasAttribute('aria-hidden') || el.matches('[role="tabpanel"]') ); if ((isAdRelated || isEmptyContainer) && !isLegitHiddenContent) { return AdUtils.safeRemove(el, 'layoutSystem', { type: '隐藏溢出内容的容器', detail: `overflow:hidden, 非滚动, ${isAdRelated ? '广告特征' : '空容器'}` }); } } const isTiny = rect.width > 0 && rect.height > 0 && (rect.width < 5 && rect.height < 5); const isZeroSize = rect.width === 0 || rect.height === 0 || style.width === '0px' || style.height === '0px'; const isEffectivelyInvisible = style.display === 'none' || style.visibility === 'hidden' || parseFloat(style.opacity) < 0.01; const hasNoMeaningfulContent = el.textContent.trim() === '' && el.children.length === 0; if ((isTiny || isZeroSize || isEffectivelyInvisible) && hasNoMeaningfulContent) { const isLegitSmallElement = ( el.tagName === 'I' || (el.tagName === 'SPAN' && (el.matches('[class*="icon"],[class*="fa"]'))) || el.classList.contains('spacer') || el.classList.contains('divider') || el.classList.contains('clear') || el.matches('[role="separator"], [role="presentation"]') || el.hasAttribute('aria-hidden') ); if (!isLegitSmallElement) { return AdUtils.safeRemove(el, 'layoutSystem', { type: '空的不可见或极小容器', detail: `尺寸: ${rect.width}x${rect.height}, display:${style.display}, visibility:${style.visibility}, opacity:${parseFloat(style.opacity).toFixed(2)}` }); } } if (el.children.length > 0 && Array.from(el.children).every(child => { if (!canProcessElement(child)) return true; const childStyle = Detector.getCachedStyle(child); return childStyle && (childStyle.display === 'none' || childStyle.visibility === 'hidden' || parseFloat(childStyle.opacity) < 0.01); })) { const isLegitParentContainer = ( el.tagName === 'NAV' || el.tagName === 'UL' || el.tagName === 'OL' || el.tagName === 'LI' || el.tagName === 'MENU' || el.closest('.menu, .dropdown, .tabs, .accordion, .collapse, .lazyload-container, .image-wrapper') || el.classList.contains('hidden-content') || el.hasAttribute('aria-hidden') ); if (!isLegitParentContainer) { return AdUtils.safeRemove(el, 'layoutSystem', { type: '子元素全部不可见容器', detail: `所有子元素不可见且父元素可疑` }); } } if (['fixed', 'sticky'].includes(style.position)) { const isLegitFixedUI = ( el.tagName === 'NAV' || el.tagName === 'HEADER' || el.tagName === 'FOOTER' || el.tagName === 'BUTTON' || el.classList.contains('navbar') || el.classList.contains('toolbar') || el.classList.contains('scroll-to-top') || el.classList.contains('cookie-banner') || el.classList.contains('notification-bar') || el.classList.contains('message-bar') || el.matches('[role="navigation"], [role="banner"], [role="contentinfo"], [role="alert"]') || (el.children.length > 0 && !Array.from(el.children).some(c => c.tagName === 'IMG' || c.tagName === 'IFRAME')) && el.textContent.trim().length > 20 ); const isLargeFixed = (rect.width >= window.innerWidth * 0.8 && rect.height >= window.innerHeight * 0.8); const isBottomFloat = (rect.bottom > window.innerHeight - 50 && rect.height < 150 && rect.width > window.innerWidth * 0.5); const isTopFloat = (rect.top < 50 && rect.height < 150 && rect.width > window.innerWidth * 0.5); const isSideFloat = ((rect.left < 50 || rect.right > window.innerWidth - 50) && rect.height > window.innerHeight * 0.3 && rect.width < 150); if (isLargeFixed && !isLegitFixedUI) { return AdUtils.safeRemove(el, 'layoutSystem', { type: '大型固定定位元素', detail: `位置: ${style.position}, 尺寸: ${rect.width}x${rect.height}px` }); } if (isBottomFloat && Utils.isAdCharacteristic(el) && !isLegitFixedUI) { return AdUtils.safeRemove(el, 'layoutSystem', { type: '底部悬浮广告', detail: `位置: bottom=${rect.bottom}px, 尺寸: ${rect.width}x${rect.height}px` }); } if (isTopFloat && Utils.isAdCharacteristic(el) && !isLegitFixedUI) { return AdUtils.safeRemove(el, 'layoutSystem', { type: '顶部悬浮广告', detail: `位置: top=${rect.top}px, 尺寸: ${rect.width}x${rect.height}px` }); } if (isSideFloat && Utils.isAdCharacteristic(el) && !isLegitFixedUI) { return AdUtils.safeRemove(el, 'layoutSystem', { type: '侧边悬浮广告', detail: `位置: side, 尺寸: ${rect.width}x${rect.height}px` }); } } return false; } }); ModuleManager.register('mergedMediaSystem', { imageExtensions: /\.(gif|webp|png|jpe?g|svg|bmp|tiff)(\?.*)?$/i, jsExtensionPattern: /\.js(\?|$)/i, visibilityThreshold: 0.01, minValidSize: 16, minConsideredAdSize: 32, mobileAdRatios: [ { w: 320, h: 50 }, { w: 300, h: 250 }, { w: 336, h: 280 }, { w: 728, h: 90 }, { w: 468, h: 60 }, { w: 120, h: 600 }, { w: 160, h: 600 }, { w: 300, h: 600 }, { w: 1, h: 1 } ], commonBannerSizes: [ { width: 320, height: 50 }, { width: 300, height: 250 }, { width: 336, height: 280 }, { width: 728, height: 90 }, { width: 468, height: 60 }, { width: 250, height: 250 }, { width: 200, height: 200 }, { width: 180, height: 150 }, { width: 125, height: 125 }, { width: 300, height: 50 }, { width: 234, height: 60 }, { width: 120, height: 600 }, { width: 160, height: 600 }, { width: 300, height: 600 }, { width: 1, height: 1 } ], mobileRatioThreshold: { min: 0.05, max: 20 }, minClickableAdSize: 48, enabled: false, init() { if (CONFIG.modules.mergedMediaSystem) this.enable(); else this.disable(); }, enable() { if (this.enabled || perf.degraded) return; this.enabled = true; }, disable() { this.enabled = false; }, isLegitimateImageOrBackground(el, rect, srcOrBg) { if (!el || !(el instanceof Element)) return false; const tagName = el.tagName.toUpperCase(); const classList = Array.from(el.classList); const altText = el.getAttribute('alt')?.toLowerCase() || ''; const ariaLabel = el.getAttribute('aria-label')?.toLowerCase() || ''; const titleText = el.getAttribute('title')?.toLowerCase() || ''; if (el.closest('nav, header, footer, article, section, figure, picture, main, aside, dialog, .post-content, .news-article, .product-detail, .user-profile, .thumbnail-list, .gallery, .comic-page, .video-player, .content-body, .article-image')) { if (rect.width < 500 && rect.height < 500) { if (altText.includes('logo') || altText.includes('icon') || altText.includes('avatar') || altText.includes('profile') || altText.includes('thumbnail') || altText.includes('product') || altText.includes('gallery') || altText.includes('comic') || altText.includes('video') || ariaLabel.includes('logo') || ariaLabel.includes('icon') || ariaLabel.includes('avatar') || ariaLabel.includes('product') || ariaLabel.includes('gallery') || classList.some(cls => cls.includes('logo') || cls.includes('avatar') || cls.includes('thumbnail') || cls.includes('product-image') || cls.includes('user-icon') || cls.includes('gallery-item') || cls.includes('icon') || cls.includes('media-item') || cls.includes('content-image') || cls.includes('post-thumbnail') || cls.includes('comic-image')) ) { return true; } } } if (altText.length > 5 && !REGEX.adAttributes.test(altText) || ariaLabel.length > 5 && !REGEX.adAttributes.test(ariaLabel) || titleText.length > 5 && !REGEX.adAttributes.test(titleText)) { return true; } if (classList.some(cls => cls.includes('logo') || cls.includes('avatar') || cls.includes('thumbnail') || cls.includes('product-image') || cls.includes('user-icon') || cls.includes('gallery-item') || cls.includes('icon') || cls.includes('banner-false') || cls.includes('ad-off'))) { return true; } if (srcOrBg) { const lowerSrcOrBg = srcOrBg.toLowerCase(); if (lowerSrcOrBg.includes('logo') || lowerSrcOrBg.includes('avatar') || lowerSrcOrBg.includes('icon') || lowerSrcOrBg.includes('user') || lowerSrcOrBg.includes('profile') || lowerSrcOrBg.includes('gallery') || lowerSrcOrBg.includes('product') || lowerSrcOrBg.includes('thumb') || lowerSrcOrBg.includes('comic') || lowerSrcOrBg.includes('video-preview')) { return true; } } if (el.hasAttribute('aria-hidden') || el.getAttribute('role') === 'presentation') { if (!el.hasAttribute('onclick') && !el.querySelector('a[href], button, [onclick]')) { return true; } } return false; }, check(el) { if (!canProcessElement(el) || perf.degraded || !this.enabled) return false; if (!(el instanceof Element)) return false; if (['A', 'BODY', 'HTML', 'HEAD', 'TITLE', 'META', 'LINK', 'STYLE', 'SCRIPT', 'BUTTON', 'INPUT', 'TEXTAREA', 'SELECT', 'FORM', 'LABEL'].includes(el.tagName.toUpperCase())) return false; const rect = el.getBoundingClientRect(); if (rect.width <= 0 || rect.height <= 0) return false; if (el.tagName === 'IMG' || el.tagName === 'IMAGE') { if (this.checkImageElement(el, rect)) return true; } if (this.hasImageBackground(el)) { if (this.checkImageBackgroundElement(el, rect)) return true; } const isLargeEnough = rect.width >= this.minClickableAdSize && rect.height >= this.minClickableAdSize; if (this.checkClickableCharacteristics(el, rect, isLargeEnough)) { return true; } if (this.checkBackgroundCharacteristics(el, rect, isLargeEnough)) { return true; } if (this.isSuspiciousContainer(el)) { if (this.handleSuspiciousContainer(el)) return true; } return false; }, checkImageElement(el, rect) { if (!canProcessElement(el) || perf.degraded) return false; const src = el.src || el.getAttribute('data-src') || ''; if (this.isLegitimateImageOrBackground(el, rect, src)) { return false; } if (this.jsExtensionPattern.test(src) || (src.startsWith('data:image/') && REGEX.adAttributes.test(src))) { return this.handleAd(el, 'JS扩展名图片/可疑数据URL', `异常图片源: ${Utils.truncateString(src, 80)}`, 3); } if (!Detector.isVisible(el)) { if (!(rect.width === 1 && rect.height === 1)) { if (rect.width < this.minValidSize / 2 && rect.height < this.minValidSize / 2) return false; return this.handleAd(el, '不可见大尺寸图片', `尺寸: ${rect.width}x${rect.height}px`, 1); } } if (rect.width === 1 && rect.height === 1) { return this.handleAd(el, '追踪像素', `尺寸: ${rect.width}x${rect.height}px`, 2); } if (rect.width > 0 && rect.height > 0 && (rect.width < this.minValidSize || rect.height < this.minValidSize)) { return false; } const naturalWidth = el.naturalWidth || rect.width; const naturalHeight = el.naturalHeight || rect.height; if (naturalWidth > 0 && naturalHeight > 0) { if (this.isCommonAdSize(naturalWidth, naturalHeight)) { if (Utils.isAdCharacteristic(el) || !this.isLegitimateImageOrBackground(el, rect, src)) { return this.handleAd(el, '标准广告尺寸图片', `${naturalWidth}x${naturalHeight}`, 2); } } const aspectRatio = naturalWidth / naturalHeight; if (aspectRatio < this.mobileRatioThreshold.min || aspectRatio > this.mobileRatioThreshold.max) { if (Utils.isAdCharacteristic(el) || !this.isLegitimateImageOrBackground(el, rect, src)) { return this.handleAd(el, '异常宽高比图片', `比例:${aspectRatio.toFixed(1)} (${naturalWidth}x${naturalHeight})`, 2); } } } return false; }, checkImageBackgroundElement(el, rect) { if (!canProcessElement(el) || perf.degraded) return false; if (!Detector.isVisible(el)) return false; const style = Detector.getCachedStyle(el); if (!style || style.backgroundImage === 'none' || !this.imageExtensions.test(style.backgroundImage)) return false; const bgUrlMatch = style.backgroundImage.match(/url\((['"]?)(.*?)\1\)/); const bgUrl = bgUrlMatch ? bgUrlMatch[2] : ''; if (this.isLegitimateImageOrBackground(el, rect, bgUrl)) { return false; } if ((rect.width < this.minValidSize * 2 && rect.height < this.minValidSize * 2) && Utils.isAdCharacteristic(el)) { return this.handleAd(el, '微型背景图片', `尺寸: ${rect.width}x${rect.height}px`, 2); } if (typeof style.backgroundImage === 'string' && REGEX.adAttributes.test(style.backgroundImage)) { return this.handleAd(el, '广告背景图片源', `背景源: ${Utils.truncateString(style.backgroundImage, 80)}`, 3); } return false; }, isCommonAdSize(width, height) { return this.commonBannerSizes.some(size => { if (width === 1 && height === 1) return true; if (width <= 0 || height <= 0) return false; const tolerance = 0.15; return (width >= size.width * (1 - tolerance) && width <= size.width * (1 + tolerance) && height >= size.height * (1 - tolerance) && height <= size.height * (1 + tolerance)); }); }, hasImageBackground(el) { if (!canProcessElement(el) || perf.degraded) return false; const style = Detector.getCachedStyle(el); return ( style && style.backgroundImage && style.backgroundImage !== 'none' && style.backgroundImage.includes('url(') && this.imageExtensions.test(style.backgroundImage) ); }, isSuspiciousContainer(el) { if (!canProcessElement(el) || perf.degraded) return false; const rect = el.getBoundingClientRect(); if (rect.width < this.minConsideredAdSize || rect.height < this.minConsideredAdSize) return false; const style = Detector.getCachedStyle(el); if (!style) return false; const zIndex = parseInt(style.zIndex, 10); const isAdRelated = Utils.isAdCharacteristic(el); const hasTrackingPixel = this.containsTrackingPixel(el); const hasHiddenIframes = el.querySelector('iframe[width="0"][height="0"], iframe[display="none"], iframe[visibility="hidden"]') !== null; return ( (!isNaN(zIndex) && zIndex > CONFIG.protectionRules.zIndexThreshold) || (isAdRelated && (!isNaN(zIndex) && zIndex > 10 || hasTrackingPixel || hasHiddenIframes)) || hasTrackingPixel || hasHiddenIframes ); }, containsTrackingPixel(el) { if (!canProcessElement(el) || perf.degraded) return false; return el.querySelector('img[width="1"][height="1"], img[width="0"][height="0"], img[src*="track"], img[src*="adlog"], img[src*="beacon"]') !== null; }, handleSuspiciousContainer(el) { if (!canProcessElement(el) || perf.degraded) return false; const style = Detector.getCachedStyle(el); const zIndex = parseInt(style.zIndex, 10); const isAdRelated = Utils.isAdCharacteristic(el); const hasTrackingPixel = this.containsTrackingPixel(el); const hasHiddenIframes = el.querySelector('iframe[width="0"][height="0"], iframe[display="none"], iframe[visibility="hidden"]') !== null; if (!isNaN(zIndex) && zIndex > CONFIG.protectionRules.zIndexThreshold) { return this.handleAd(el, '极高Z轴容器', `z-index: ${zIndex}`, 4); } if (isAdRelated && !isNaN(zIndex) && zIndex > 10) { return this.handleAd(el, '高Z轴广告容器', `z-index: ${zIndex}`, 3); } if (hasTrackingPixel) { return this.handleAd(el, '包含跟踪像素', '找到1x1图片或其他跟踪像素', 3); } if (hasHiddenIframes) { return this.handleAd(el, '包含隐藏iframe', '找到隐藏iframe', 3); } if (isAdRelated) { return this.handleAd(el, '基础广告特征容器', `ID/Class/Attr匹配`, 2); } return false; }, checkClickableCharacteristics(el, rect, isLargeEnough) { if (!canProcessElement(el) || perf.degraded) return false; if (!Detector.isVisible(el)) return false; if (!isLargeEnough) return false; const style = Detector.getCachedStyle(el); if (!style) return false; const isLegitClickableUI = ( el.tagName === 'BUTTON' || el.tagName === 'INPUT' || el.tagName === 'SELECT' || el.tagName === 'TEXTAREA' || el.closest('nav, .pagination, .tab-bar, .menu, .dropdown-toggle, .form-control, .btn-group, .product-actions, .add-to-cart, .read-more, .view-details, .comment-button') || el.hasAttribute('aria-label') && !REGEX.adAttributes.test(el.getAttribute('aria-label')) || el.hasAttribute('title') && !REGEX.adAttributes.test(el.getAttribute('title')) || el.matches('[role="button"], [role="link"], [role="checkbox"], [role="radio"], [role="option"], [tabindex]') ); if (isLegitClickableUI) return false; const isClickable = style.cursor === 'pointer' || el.hasAttribute('onclick') || !!el.querySelector('[onclick], a[href], button, [role="button"]'); const isAdRelated = Utils.isAdCharacteristic(el); const zIndex = parseInt(style.zIndex, 10); const hasImageOrFlash = el.querySelector('img, object, embed, iframe') !== null || this.hasImageBackground(el); const hasOnlyMinimalText = el.textContent.trim().length < 50; if (isClickable && isLargeEnough && hasImageOrFlash && hasOnlyMinimalText && (isAdRelated || (!isNaN(zIndex) && zIndex > (CONFIG.protectionRules.zIndexThreshold / 2)) || this.containsTrackingPixel(el))) { let detail = `点击/尺寸:${rect.width}x${rect.height}`; if (!isNaN(zIndex)) detail += `/Z:${zIndex}`; if (isAdRelated) detail += '/广告属性'; if (hasImageOrFlash) detail += '/包含媒体'; if (this.containsTrackingPixel(el)) detail += '/跟踪像素'; return this.handleAd(el, '可疑可点击容器', detail, 3); } return false; }, checkBackgroundCharacteristics(el, rect, isLargeEnough) { if (!canProcessElement(el) || perf.degraded) return false; if (!Detector.isVisible(el)) return false; if (!isLargeEnough) return false; const style = Detector.getCachedStyle(el); if (!style) return false; const hasBackground = style.backgroundImage !== 'none' || (style.backgroundColor && style.backgroundColor !== 'rgba(0, 0, 0, 0)' && style.backgroundColor !== 'transparent'); const isGradient = typeof style.backgroundImage === 'string' && (style.backgroundImage.includes('linear-gradient') || style.backgroundImage.includes('radial-gradient')); const isSolidColor = hasBackground && !isGradient && style.backgroundImage === 'none'; const isLegitBackgroundElement = ( el.tagName === 'HEADER' || el.tagName === 'FOOTER' || el.tagName === 'NAV' || el.classList.contains('card') || el.classList.contains('bg-primary') || el.classList.contains('btn') || el.matches('[role="banner"], [role="contentinfo"], [role="region"], [role="alert"]') || el.closest('.header, .footer, .navbar, .toolbar, .card, .alert, .toast, .hero-section, .background-image-container, .post-cover') ); if (isLegitBackgroundElement) return false; const isAdRelated = Utils.isAdCharacteristic(el); const zIndex = parseInt(style.zIndex, 10); if ((isGradient || isSolidColor || this.hasImageBackground(el)) && isLargeEnough && (isAdRelated || (!isNaN(zIndex) && zIndex > (CONFIG.protectionRules.zIndexThreshold / 2)) || this.containsTrackingPixel(el) || el.hasAttribute('onclick') || !!el.querySelector('[onclick], a[href]'))) { let detail = `背景:${isSolidColor ? '纯色' : (isGradient ? '渐变' : '图片')}/尺寸:${rect.width}x${rect.height}`; if (!isNaN(zIndex)) detail += `/Z:${zIndex}`; if (isAdRelated) detail += '/广告属性'; if (this.containsTrackingPixel(el)) detail += '/跟踪像素'; if (el.hasAttribute('onclick') || !!el.querySelector('[onclick], a[href]')) detail += '/可点击'; return this.handleAd(el, '可疑背景容器', detail, 3); } return false; }, handleAd(el, type, detail, priority = 1) { return AdUtils.safeRemove(el, 'mergedMediaSystem', { type: `媒体检测[${type}]`, detail: detail, priority: priority }); } }); ModuleManager.register('specialUA', { navigatorProxy: null, originalNavigator: 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: 'Mozilla/5.0 (Linux; Android 12; LIO-AN00 Build/HUAWEILIO-AN00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 Mobile Safari/537.36', init() { this.createProxy(); }, createProxy() { if (this.navigatorProxy) return; this.navigatorProxy = new Proxy(this.originalNavigator, { get: (target, prop) => { if (!CONFIG.modules.specialUA) return Reflect.get(target, prop); switch (prop) { case 'userAgent': return this.fakeUA; case 'platform': return this.fakePlatform; case 'appVersion': return this.fakeAppVersion; case 'vendor': return 'Google Inc.'; default: return Reflect.get(target, prop); } } }); try { Object.defineProperty(window, 'navigator', { value: this.navigatorProxy, configurable: true, writable: false }); } catch (e) {} }, enable() { CONFIG.modules.specialUA = true; }, disable() { CONFIG.modules.specialUA = false; if (this.navigatorProxy && window.navigator === this.navigatorProxy) { try { Object.defineProperty(window, 'navigator', { value: this.originalNavigator, configurable: true, writable: false }); } catch (e) {} } }, check() { return false; } }); ModuleManager.register('performanceOptimizer', { performanceEntries: [], degradationThreshold: CONFIG.performance.degradeThreshold, isMonitoring: false, perfObserver: null, init() { if (CONFIG.modules.main) { this.setupPerformanceMonitor(); this.startPerformanceObserver(); } else { this.stopPerformanceMonitor(); } }, setupPerformanceMonitor() { if (this.isMonitoring || !CONFIG.modules.main) return; this.isMonitoring = true; const checkPerformance = () => { if (!CONFIG.modules.main) { this.isMonitoring = false; return; } const now = performance.now(); this.performanceEntries = this.performanceEntries.filter( entry => entry.startTime > now - 10000 ); const longTasks = this.performanceEntries.filter( entry => entry.duration > CONFIG.performance.longTaskThreshold ); if (longTasks.length >= this.degradationThreshold && !perf.degraded) { this.activateDegradedMode(); } else if (longTasks.length < this.degradationThreshold / 2 && perf.degraded) { this.deactivateDegradedMode(); } requestAnimationFrame(checkPerformance); }; requestAnimationFrame(checkPerformance); }, startPerformanceObserver() { if (typeof PerformanceObserver === 'function' && !this.perfObserver) { try { this.perfObserver = new PerformanceObserver(list => { if (!CONFIG.modules.main) return; this.performanceEntries.push(...list.getEntries()); }); this.perfObserver.observe({ entryTypes: ['longtask'] }); } catch (e) { console.warn("PerformanceObserver for longtask not supported or failed:", e); this.perfObserver = null; } } }, stopPerformanceMonitor() { this.isMonitoring = false; if (this.perfObserver) { this.perfObserver.disconnect(); this.perfObserver = null; } if (perf.degraded) { this.deactivateDegradedMode(); } }, activateDegradedMode() { if (perf.degraded || !CONFIG.modules.main) return; perf.degraded = true; mainObserver.disconnect(); AdUtils.cleanupScriptHooks(); ModuleManager.modules.dynamicSystem?.disable(); perf.adElements.clear(); perf.processed = new WeakSet(); Logs.add('performanceOptimizer', null, { type: '性能降级', detail: `检测到 ${this.degradationThreshold} 个长期任务,部分功能已暂停。` }); GM_notification('AdBlocker', '性能降级模式:部分功能已暂停'); }, deactivateDegradedMode() { if (!perf.degraded || !CONFIG.modules.main) return; perf.degraded = false; if (document.documentElement) { mainObserver.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['id', 'class', 'style', 'src', 'href', 'data-src'] }); } if (CONFIG.modules.dynamicSystem) { ModuleManager.modules.dynamicSystem?.enable(); } Processor.collectInitialElements(); Processor.scheduleProcess(); Logs.add('performanceOptimizer', null, { type: '性能恢复', detail: '性能降级模式结束:功能已恢复。' }); GM_notification('AdBlocker', '性能降级模式结束:功能已恢复'); }, check() { return false; } }); if (!window.requestIdleCallback) { window.requestIdleCallback = (callback, options) => { const options_timeout = options?.timeout || 0; const start = Date.now(); return setTimeout(() => { callback({ didTimeout: Date.now() - start > 50, timeRemaining: () => Math.max(0, 50 - (Date.now() - start)) }); }, options_timeout); }; window.cancelIdleCallback = (id) => clearTimeout(id); } function init() { UIController.init(); CSPManager.injectInitialCSP(); ModuleManager.init(); if (CONFIG.modules.main) { AdUtils.initScriptHooks(); if (document.readyState === 'loading') { window.addEventListener('DOMContentLoaded', () => { if (document.documentElement && CONFIG.modules.main && !perf.degraded) { mainObserver.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['id', 'class', 'style', 'src', 'href', 'data-src'] }); } Processor.collectInitialElements(); Processor.scheduleProcess(); }, { once: true }); } else { if (document.documentElement && CONFIG.modules.main && !perf.degraded) { mainObserver.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['id', 'class', 'style', 'src', 'href', 'data-src'] }); } Processor.collectInitialElements(); Processor.scheduleProcess(); } window.addEventListener('scroll', Utils.throttle(() => { if (CONFIG.modules.main && !perf.degraded) { Processor.collectInitialElements(); Processor.scheduleProcess(); } }, CONFIG.performance.throttleScrollDelay)); window.addEventListener('resize', Utils.debounce(() => { if (CONFIG.modules.main && !perf.degraded) { perf.styleCache.clear(); Processor.collectInitialElements(); Processor.scheduleProcess(); } }, 150)); StyleManager.inject(); } else { mainObserver.disconnect(); AdUtils.cleanupScriptHooks(); StyleManager.remove(); ModuleManager.modules.performanceOptimizer?.stopPerformanceMonitor(); } if (CONFIG.modules.main && CONFIG.mobileOptimizations.lazyLoadImages) { document.addEventListener('DOMContentLoaded', () => { document.querySelectorAll('img[data-src]:not([src])').forEach(img => { if (!(img instanceof Element) || !canProcessElement(img)) return; if (img.dataset.src) { img.src = img.dataset.src; img.removeAttribute('data-src'); } }); }, { once: true }); } if (CONFIG.modules.main && CONFIG.mobileOptimizations.removeImagePlaceholders) { document.addEventListener('DOMContentLoaded', () => { const placeholders = document.querySelectorAll('.image-placeholder, [placeholder], [aria-hidden="true"], [role="presentation"]'); placeholders.forEach(ph => { if (!(ph instanceof Element) || !canProcessElement(ph)) return; try { if (ph.tagName === 'INPUT' || ph.tagName === 'TEXTAREA' || ph === document.activeElement || ph.contains(document.activeElement)) return; const rect = ph.getBoundingClientRect(); const style = getComputedStyle(ph); if (rect.width > 0 && rect.width < 5 && rect.height > 0 && rect.height < 5 && style.position === 'absolute' && parseFloat(style.opacity) < 0.1) { if (ph.parentNode) ph.parentNode.removeChild(ph); } else if (ph.textContent.trim() === '' && ph.children.length === 0 && style.display === 'none') { if (ph.parentNode) ph.parentNode.removeChild(ph); } else if (ph.tagName === 'IMG' && (!ph.src || ph.src.startsWith('data:')) && ph.width < 100 && ph.height < 100 && ph.naturalWidth === 0 && ph.naturalHeight === 0) { if (ph.parentNode) ph.parentNode.removeChild(ph); } } catch (e) {} }); }, { once: true }); } } init(); })();