您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Force pages to always think the tab is visible/focused; optionally spoofs mouse as always "in page" and blocks exit/enter intent; shows a tiny popup when a visibility or mouse check is detected
// ==UserScript== // @name Anti-Visibility Cloak // @namespace https://spin.rip/ // @version 1.2.1 // @description Force pages to always think the tab is visible/focused; optionally spoofs mouse as always "in page" and blocks exit/enter intent; shows a tiny popup when a visibility or mouse check is detected // @author Spinfal // @match *://*/* // @run-at document-start // @grant none // @all-frames true // @icon https://cdn.spin.rip/r/antivisibility.png // @license gpl-3.0-or-later // ==/UserScript== (function() { 'use strict'; // easy mode: flip these first const CONFIG = { // safety levels (start here) strictMode: false, // stronger filtering + harder to undo patches paranoidMode: false, // periodic re-apply + extra guards pageRealmPatch: false, // also patch inside the page's own js realm // optional extras patchTimers: false, // smooth over background throttling (raf/idle) patchPointerCapture: true,// soften pointer-capture based exit-intent exposeApi: true, // controls/stats from API available via a random name // what to show in the tiny toast notify: { properties: true, // toast when code reads visibility props hasFocusCall: false, // toast when document.hasFocus() is called burstWindowMs: 400, // collapse duplicate toasts within this window silentBootMs: 600, // suppress toasts right after load // toast when listeners are added for these events addListener: { visibilitychange: true, webkitvisibilitychange: true, mozvisibilitychange: true, msvisibilitychange: true, pagehide: true, freeze: true, resume: true, pageshow: true, blur: false, focus: false, mouseout: true, mouseleave: true, pointerout: true, pointerleave: true, mouseover: true, mouseenter: true, pointerover: true, focusin: true, focusout: true, pointermove: false, mousemove: false }, // toast when those listeners actually run invoke: { visibilitychange: true, webkitvisibilitychange: true, mozvisibilitychange: true, msvisibilitychange: true, pagehide: true, freeze: true, resume: true, pageshow: true, blur: false, focus: false, mouseout: false, mouseleave: false, pointerout: false, pointerleave: false, mouseover: false, mouseenter: false, pointerover: false, focusin: true, focusout: true, pointermove: false, mousemove: false } }, // avoid spamming toasts when you focus text inputs suppressFocusOnEditable: true, // mouse presence + enter/exit intent controls mouse: { spoofExit: true, // block exit-intent on window/doc/html/body ignoreGlobalLeave: true, // swallow leave where relatedTarget is null blockGlobalEnter: true, // swallow enter where relatedTarget is null initialEnterOnce: true, // let exactly one global enter through after load // fake movement to look "alive" fakeMovement: true, // periodically dispatch synthetic movement fakeIntervalMs: 12000, // base interval between moves randomizeInterval: true, // add ±30% jitter to the interval moveJitterPx: 2, // small pixel jitter so it’s not robotic alsoPointerMove: true, // emit pointermove alongside mousemove lightScrollNudge: false // tiny scroll nudge (off by default) }, focus: { blockGlobalBlur: true // swallow window/document/html/body blur & focusout } }; /* ---------- just a separator to keep things clean ----------- */ // stronger source for ids used on the API surface function secureRandomString(len = 16) { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const buf = new Uint32Array(len); crypto.getRandomValues(buf); return Array.from(buf, n => chars[n % chars.length]).join(''); } // stats counters for debugging const STATS = { addBlocked: Object.create(null), invokeSwallowed: Object.create(null), synthetic: Object.create(null), redefinitions: 0 }; // notifier (stealthier) const makeNotifier = () => { let shadow, wrap, root, last, lastAt = 0, hideTimer; const bootAt = Date.now(); root = document.createElement('div'); root.style.all = 'initial'; root.style.position = 'fixed'; root.style.zIndex = '2147483647'; root.style.right = '10px'; root.style.bottom = '10px'; root.style.pointerEvents = 'none'; shadow = root.attachShadow({ mode: 'open' }); const style = document.createElement('style'); style.textContent = ` .wrap{font-family:system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif;font-size:12px;line-height:1.2;background:#111;color:#fff;border-radius:8px;padding:8px 10px;box-shadow:0 2px 12px rgba(0,0,0,.35);opacity:.95;max-width:260px;pointer-events:auto;display:flex;gap:8px;align-items:start} .dot{width:8px;height:8px;border-radius:50%;background:#4ade80;margin-top:4px;flex:0 0 8px} .msg{white-space:pre-line} .hide{animation:fadeout .4s forwards} @keyframes fadeout{to{opacity:0;transform:translateY(4px)}} `; wrap = document.createElement('div'); wrap.className = 'wrap'; wrap.innerHTML = `<div class="dot"></div><div class="msg"></div>`; shadow.append(style, wrap); const set = (text) => { if ((Date.now() - bootAt) < (CONFIG.notify.silentBootMs|0)) return; if (!root.isConnected) document.documentElement.appendChild(root); wrap.querySelector('.msg').textContent = text; wrap.classList.remove('hide'); clearTimeout(hideTimer); hideTimer = setTimeout(() => { wrap.classList.add('hide'); setTimeout(() => { if (root.isConnected) root.remove(); }, 450); }, 1600); }; return (what) => { const now = Date.now(); if (what === last && (now - lastAt) < (CONFIG.notify.burstWindowMs|0)) return; last = what; lastAt = now; set(`visibility/mouse check bypassed:\n${what}`); }; }; const notify = makeNotifier(); // helpers const define = (target, key, descriptor) => { try { const desc = Object.getOwnPropertyDescriptor(target, key); if (desc && desc.configurable === false) return false; Object.defineProperty(target, key, { configurable: true, ...descriptor }); return true; } catch { return false; } }; const harden = (target, key, lock = {}) => { try { const d = Object.getOwnPropertyDescriptor(target, key); if (!d) return false; Object.defineProperty(target, key, { ...d, configurable: !!lock.configurable ? lock.configurable : false, writable: d.writable === true ? false : d.writable }); return true; } catch { return false; } }; const freezeFn = (fn) => { try { Object.freeze(fn); } catch {} return fn; }; const asGlobalLike = (n) => (n===window || n===document || n===document.documentElement || n===document.body || n===window.visualViewport); // surface properties const forceVisibilityProps = () => { STATS.redefinitions++; const DocProto = Document.prototype; const HTMLDocProto = (window.HTMLDocument && HTMLDocument.prototype) || DocProto; const stableGet = (name, value) => function() { if (CONFIG.notify.properties) notify(`${name} read`); return value; }; // main define(DocProto, 'visibilityState', { get: stableGet('document.visibilityState','visible') }) || define(document, 'visibilityState', { get: stableGet('document.visibilityState','visible') }); define(DocProto, 'hidden', { get: stableGet('document.hidden',false) }) || define(document, 'hidden', { get: stableGet('document.hidden',false) }); // legacy + aliases [['webkitVisibilityState','visible'],['mozVisibilityState','visible'],['msVisibilityState','visible'], ['webkitHidden',false],['mozHidden',false],['msHidden',false]].forEach(([k,v])=>{ define(DocProto, k, { get: stableGet(`document.${k}`, v) }) || define(document, k, { get: stableGet(`document.${k}`, v) }); }); // prerendering flag if ('prerendering' in document || DocProto.hasOwnProperty('prerendering')) { define(DocProto, 'prerendering', { get: stableGet('document.prerendering', false) }) || define(document, 'prerendering', { get: stableGet('document.prerendering', false) }); } // make sure htmldocument sees the same if (HTMLDocProto && HTMLDocProto !== DocProto) { define(HTMLDocProto, 'visibilityState', { get: stableGet('document.visibilityState','visible') }); define(HTMLDocProto, 'hidden', { get: stableGet('document.hidden',false) }); } // hasFocus const hasFocusImpl = freezeFn(function hasFocus() { if (CONFIG.notify.hasFocusCall) notify('document.hasFocus() call'); return true; }); define(DocProto, 'hasFocus', { value: hasFocusImpl }) || define(document, 'hasFocus', { value: hasFocusImpl }); try { if (!document.__origHasFocus) Object.defineProperty(document, '__origHasFocus', { value: DocProto.hasFocus, configurable: true }); } catch {} // property-style handlers setters const propEvents = ['visibilitychange','webkitvisibilitychange','mozvisibilitychange','msvisibilitychange','focus','blur','focusin','focusout','pageshow','pagehide']; const wrapPropSetter = (host, prop) => { const key = 'on'+prop; const setWrapped = function(v){ if (typeof v === 'function') { const wrapped = function(ev){ // swallow global blur/focusout from property handlers too if (CONFIG.focus?.blockGlobalBlur && (prop === 'blur' || prop === 'focusout') && asGlobalLike(host)) { if (CONFIG.notify.invoke[prop]) notify(`${prop} ignored (global blur: prop handler)`); return; } return v.call(this, ev); }; try { Object.defineProperty(v, '__visible_wrap__', { value: wrapped }); } catch {} return origSet.call(this, wrapped); } return origSet.call(this, v); }; const desc = Object.getOwnPropertyDescriptor(host, key) || { configurable:true, enumerable:true }; const origSet = desc.set || function(fn){ this.addEventListener(prop, fn); }; define(host, key, { configurable:true, enumerable:desc.enumerable!==false, set:setWrapped, get: desc.get || function(){ return null; } }); }; propEvents.forEach(e => { wrapPropSetter(window, e); wrapPropSetter(document, e); }); if (CONFIG.strictMode) { try { harden(Document.prototype,'visibilityState'); harden(Document.prototype,'hidden'); harden(document,'visibilityState'); harden(document,'hidden'); } catch {} } }; // listener wrapping + filtering const forceVisibilityEvents = () => { const TYPES = [ 'visibilitychange','webkitvisibilitychange','mozvisibilitychange','msvisibilitychange', 'pagehide','freeze','resume','pageshow', 'blur','focus','focusin','focusout', 'mouseleave','mouseout','pointerleave','pointerout', 'mouseover','mouseenter','pointerover', 'mousemove','pointermove' ]; const EXIT_TYPES = new Set(['mouseleave','mouseout','pointerleave','pointerout']); const ENTER_TYPES = new Set(['mouseover','mouseenter','pointerover']); const BLUR_TYPES = new Set(['blur','focusout']); const isEditableTarget = (ev) => { if (!ev || !ev.target) return false; const t = ev.target; if (t.isContentEditable) return true; const tag = (t.tagName || '').toLowerCase(); return tag === 'input' || tag === 'textarea' || tag === 'select'; }; let allowOneGlobalEnter = !!CONFIG.mouse.initialEnterOnce; const ensureVisibleDescriptors = () => { try { Object.defineProperty(document, 'visibilityState', { get: () => 'visible', configurable: true }); Object.defineProperty(document, 'hidden', { get: () => false, configurable: true }); } catch {} }; const wrapHandler = (type, fn, ctx) => { const wrapped = function(event) { ensureVisibleDescriptors(); // swallow global blur/focusout if (CONFIG.focus?.blockGlobalBlur && BLUR_TYPES.has(type) && asGlobalLike(ctx)) { if (CONFIG.notify.invoke[type]) notify(`${type} ignored (global blur)`); return; // don't call site handler } // swallow global exit if (CONFIG.mouse.ignoreGlobalLeave && EXIT_TYPES.has(type) && asGlobalLike(ctx) && (!event || event.relatedTarget == null)) { STATS.invokeSwallowed[type] = (STATS.invokeSwallowed[type]||0)+1; if (CONFIG.notify.invoke[type]) notify(`${type} ignored (global exit)`); return; } // swallow global enter if (CONFIG.mouse.blockGlobalEnter && ENTER_TYPES.has(type) && asGlobalLike(ctx) && (!event || event.relatedTarget == null)) { if (allowOneGlobalEnter) { allowOneGlobalEnter = false; } else { STATS.invokeSwallowed[type] = (STATS.invokeSwallowed[type]||0)+1; if (CONFIG.notify.invoke[type]) notify(`${type} ignored (global enter)`); return; } } // strict: also treat body/html/visualViewport as global always if (CONFIG.strictMode && (EXIT_TYPES.has(type) || ENTER_TYPES.has(type)) && (ctx===document.body || ctx===document.documentElement || ctx===window.visualViewport)) { STATS.invokeSwallowed[type] = (STATS.invokeSwallowed[type]||0)+1; if (CONFIG.notify.invoke[type]) notify(`${type} ignored (strict global ${type})`); return; } if (CONFIG.notify.invoke[type]) { if (!(CONFIG.suppressFocusOnEditable && (type === 'focus' || type === 'blur' || type==='focusin' || type==='focusout') && isEditableTarget(event))) { notify(`${type} listener invoked`); } } try { return fn.call(this, event); } catch(e) { setTimeout(() => { throw e; }); } }; return freezeFn(wrapped); }; const TYPE_SET = new Set(TYPES); const makeWrapped = (t, listener, ctx) => { // normalize to a callable let fn = listener; if (typeof listener === 'object' && typeof listener.handleEvent === 'function') { fn = function(ev){ return listener.handleEvent.call(listener, ev); }; } if (typeof fn !== 'function') return null; const wrapped = wrapHandler(t, fn, ctx); try { Object.defineProperty(listener, '__visible_wrap__', { value: wrapped }); } catch {} return wrapped; }; const patchAEL = (proto, label) => { const orig = proto.addEventListener; define(proto, 'addEventListener', { value: function(type, listener, options) { const t = String(type); if (!listener || !TYPE_SET.has(t)) return orig.call(this, type, listener, options); // block registration of global blur/focusout listeners if (CONFIG.focus?.blockGlobalBlur && BLUR_TYPES.has(t) && asGlobalLike(this)) { STATS.addBlocked[t] = (STATS.addBlocked[t]||0)+1; if (CONFIG.notify.addListener[t]) notify(`${label}.addEventListener("${t}") blocked on global target`); return; } // block registration of exit/enter listeners on global-ish targets if ((CONFIG.mouse.spoofExit && EXIT_TYPES.has(t) && asGlobalLike(this)) || (CONFIG.mouse.blockGlobalEnter && ENTER_TYPES.has(t) && asGlobalLike(this)) || (CONFIG.strictMode && (EXIT_TYPES.has(t) || ENTER_TYPES.has(t)) && (this===document.body || this===document.documentElement || this===window.visualViewport))) { STATS.addBlocked[t] = (STATS.addBlocked[t]||0)+1; if (CONFIG.notify.addListener[t]) notify(`${label}.addEventListener("${t}") blocked on global target`); return; } if (CONFIG.notify.addListener[t]) notify(`${label}.addEventListener("${t}")`); const wrapped = makeWrapped(t, listener, this); // if it's not a function or EventListener object, just pass through return orig.call(this, type, wrapped || listener, options); } }); const origRel = proto.removeEventListener; define(proto, 'removeEventListener', { value: function(type, listener, options) { const l = listener && listener.__visible_wrap__ ? listener.__visible_wrap__ : listener; return origRel.call(this, type, l, options); } }); if (CONFIG.strictMode) { try { harden(proto,'addEventListener'); harden(proto,'removeEventListener'); } catch {} } }; patchAEL(EventTarget.prototype, 'EventTarget'); }; // timers normalization (lightweight) const installTimerShims = () => { if (!CONFIG.patchTimers) return; try { const _raf = window.requestAnimationFrame.bind(window); const _caf = window.cancelAnimationFrame ? window.cancelAnimationFrame.bind(window) : ()=>{}; let lastRaf = 0; const rafWrapped = freezeFn(function(cb){ const start = performance.now(); return _raf(function(t){ const dt = t - lastRaf; lastRaf = t; // if raf is obviously throttled, simulate a smoother cadence if (dt > 80) { setTimeout(()=>cb(performance.now()), 16); } else { cb(t); } }); }); define(window, 'requestAnimationFrame', { value: rafWrapped }); define(window, 'cancelAnimationFrame', { value: freezeFn(_caf) }); const _ric = window.requestIdleCallback && window.requestIdleCallback.bind(window); if (_ric) { const ricWrapped = freezeFn(function(cb, opts){ return _ric(function(deadline){ if (!deadline || typeof deadline.timeRemaining !== 'function') { try { cb({ didTimeout:false, timeRemaining:()=>10 }); } catch {} return; } if (deadline.timeRemaining() < 5) { try { cb({ didTimeout:false, timeRemaining:()=>10 }); } catch {} } else { try { cb(deadline); } catch {} } }, opts); }); define(window, 'requestIdleCallback', { value: ricWrapped }); } } catch {} }; // pointer capture softeners const installPointerCaptureShims = () => { if (!CONFIG.patchPointerCapture || !Element || !Element.prototype) return; try { const EP = Element.prototype; const _spc = EP.setPointerCapture; const _rpc = EP.releasePointerCapture; if (_spc) define(EP, 'setPointerCapture', { value: freezeFn(function(pointerId){ try { const g = this === document.documentElement || this === document.body; if (g) return; // no-op on global-ish targets } catch {} return _spc.apply(this, arguments); })}); if (_rpc) define(EP, 'releasePointerCapture', { value: freezeFn(function(pointerId){ try { return _rpc.apply(this, arguments); } catch {} })}); if (CONFIG.strictMode) { try { harden(EP, 'setPointerCapture'); harden(EP,'releasePointerCapture'); } catch {} } } catch {} }; // synthetic mouse/pointer presence const installFakeMouse = () => { if (!CONFIG.mouse.fakeMovement) return; let lastX = Math.max(10, Math.min(window.innerWidth - 10, Math.floor(window.innerWidth / 2))); let lastY = Math.max(10, Math.min(window.innerHeight - 10, Math.floor(window.innerHeight / 2))); const update = (e) => { if (!e) return; if (typeof e.clientX === 'number') lastX = e.clientX; if (typeof e.clientY === 'number') lastY = e.clientY; }; window.addEventListener('mousemove', update, { passive: true, capture: true }); const base = Math.max(4000, CONFIG.mouse.fakeIntervalMs | 0); const nextDelay = () => { if (!CONFIG.mouse.randomizeInterval) return base; const jitter = base * 0.3; return Math.max(2000, base + (Math.random()*2*jitter - jitter)); }; const tick = () => { try { const jx = (Math.random()*CONFIG.mouse.moveJitterPx*2 - CONFIG.mouse.moveJitterPx)|0; const jy = (Math.random()*CONFIG.mouse.moveJitterPx*2 - CONFIG.mouse.moveJitterPx)|0; const x = Math.max(0, Math.min(window.innerWidth - 1, lastX + jx)); const y = Math.max(0, Math.min(window.innerHeight - 1, lastY + jy)); const mouseEv = new MouseEvent('mousemove', { bubbles:true, cancelable:false, clientX:x, clientY:y, screenX:x, screenY:y, view:window }); document.dispatchEvent(mouseEv); STATS.synthetic.mousemove = (STATS.synthetic.mousemove||0)+1; if (CONFIG.notify.invoke.mousemove) notify('synthetic mousemove dispatched'); if (CONFIG.mouse.alsoPointerMove && 'PointerEvent' in window) { const ptr = new PointerEvent('pointermove', { bubbles:true, cancelable:false, clientX:x, clientY:y, pointerType:'mouse', isPrimary:true, view:window }); document.dispatchEvent(ptr); STATS.synthetic.pointermove = (STATS.synthetic.pointermove||0)+1; } if (CONFIG.mouse.lightScrollNudge && document.scrollingElement && (document.scrollingElement.scrollHeight > document.scrollingElement.clientHeight)) { const se = document.scrollingElement; const start = se.scrollTop; se.scrollTop = start + 0.5; se.scrollTop = start; STATS.synthetic.scroll = (STATS.synthetic.scroll||0)+1; } } catch {} setTimeout(tick, nextDelay()); }; setTimeout(tick, nextDelay()); }; // one-time visibility ping const dispatchInitialPing = () => { try { document.dispatchEvent(new Event('visibilitychange')); } catch {} try { document.dispatchEvent(new Event('pageshow')); } catch {} }; // focus/blur interceptors on window const installWindowFocusShims = () => { try { const no = freezeFn(function(){ /* no-op */ }); define(window, 'focus', { value: no }); define(window, 'blur', { value: no }); if (CONFIG.strictMode) { harden(window,'focus'); harden(window,'blur'); } } catch {} }; // reapply guards const installReapplyGuards = () => { const reapply = () => { try { if (document.visibilityState !== 'visible' || document.hidden !== false) forceVisibilityProps(); } catch {} }; const mo = new MutationObserver(reapply); try { mo.observe(document.documentElement, { childList: true, subtree: true }); } catch {} if (CONFIG.paranoidMode) { setInterval(() => { try { forceVisibilityProps(); } catch {} }, 1500); } }; // page-realm patch (mirrors key pieces) — respects config and doesn't swallow focusout const injectPageRealm = () => { if (!CONFIG.pageRealmPatch) return; const src = ` (function(){ try{ const BLOCK_GLOBAL_BLUR = ${JSON.stringify(!!(CONFIG.focus && CONFIG.focus.blockGlobalBlur))}; const BLOCK_GLOBAL_EXIT = ${JSON.stringify(!!(CONFIG.mouse && CONFIG.mouse.ignoreGlobalLeave))}; const BLOCK_GLOBAL_ENTER = ${JSON.stringify(!!(CONFIG.mouse && CONFIG.mouse.blockGlobalEnter))}; const ALLOW_ONE_ENTER = ${JSON.stringify(!!(CONFIG.mouse && CONFIG.mouse.initialEnterOnce))}; let allowOneGlobalEnter = ALLOW_ONE_ENTER; const define = ${define.toString()}; const freezeFn = ${freezeFn.toString()}; const asGlobalLike = ${asGlobalLike.toString()}; const stableGet=(name,val)=>function(){return val;}; const DocProto=Document.prototype; define(DocProto,'visibilityState',{get:stableGet('document.visibilityState','visible')})||define(document,'visibilityState',{get:stableGet('document.visibilityState','visible')}); define(DocProto,'hidden',{get:stableGet('document.hidden',false)})||define(document,'hidden',{get:stableGet('document.hidden',false)}); [['webkitVisibilityState','visible'],['mozVisibilityState','visible'],['msVisibilityState','visible'], ['webkitHidden',false],['mozHidden',false],['msHidden',false]].forEach(([k,v])=>{ define(DocProto,k,{get:stableGet('document.'+k,v)})||define(document,k,{get:stableGet('document.'+k,v)}); }); const hasFocusImpl = freezeFn(function hasFocus(){return true;}); define(DocProto,'hasFocus',{value:hasFocusImpl})||define(document,'hasFocus',{value:hasFocusImpl}); const TYPES=['visibilitychange','webkitvisibilitychange','mozvisibilitychange','msvisibilitychange','pagehide','freeze','resume','pageshow', 'blur','focus','focusin','focusout', 'mouseleave','mouseout','pointerleave','pointerout', 'mouseover','mouseenter','pointerover', 'mousemove','pointermove']; const EXIT_TYPES = new Set(['mouseleave','mouseout','pointerleave','pointerout']); const ENTER_TYPES = new Set(['mouseover','mouseenter','pointerover']); const BLUR_TYPES = new Set(['blur']); // key fix: don't treat focusout as a global blur const ensureVisible=()=>{try{ Object.defineProperty(document,'visibilityState',{get:()=> 'visible',configurable:true}); Object.defineProperty(document,'hidden',{get:()=> false,configurable:true}); }catch(e){}}; const wrapHandler=(type,fn,ctx)=>freezeFn(function(event){ ensureVisible(); if (BLOCK_GLOBAL_BLUR && BLUR_TYPES.has(type) && asGlobalLike(ctx)) return; if (BLOCK_GLOBAL_EXIT && EXIT_TYPES.has(type) && asGlobalLike(ctx) && (!event || event.relatedTarget==null)) return; if (BLOCK_GLOBAL_ENTER && ENTER_TYPES.has(type) && asGlobalLike(ctx) && (!event || event.relatedTarget==null)) { if (allowOneGlobalEnter) { allowOneGlobalEnter=false; } else { return; } } try { return fn.call(this,event); } catch(e){ setTimeout(()=>{ throw e; }); } }); const patchAEL=(proto)=>{ const orig=proto.addEventListener; define(proto,'addEventListener',{value:function(type,listener,options){ const t=String(type); if (!listener || TYPES.indexOf(t)===-1) return orig.call(this,type,listener,options); if (BLOCK_GLOBAL_BLUR && BLUR_TYPES.has(t) && asGlobalLike(this)) return; if (((EXIT_TYPES.has(t) && BLOCK_GLOBAL_EXIT) || (ENTER_TYPES.has(t) && BLOCK_GLOBAL_ENTER)) && asGlobalLike(this)) return; let fn=listener; if (typeof listener==='object' && typeof listener.handleEvent==='function'){ fn=function(ev){ return listener.handleEvent.call(listener,ev); }; } if (typeof fn!=='function') return orig.call(this,type,listener,options); const wrapped=wrapHandler(t,fn,this); try { Object.defineProperty(listener,'__visible_wrap__',{value:wrapped}); } catch {} return orig.call(this,type,wrapped,options); }}); const origRel=proto.removeEventListener; define(proto,'removeEventListener',{value:function(type,listener,options){ const l=listener && listener.__visible_wrap__ ? listener.__visible_wrap__ : listener; return origRel.call(this,type,l,options); }}); }; patchAEL(EventTarget.prototype); try{ document.dispatchEvent(new Event('visibilitychange')); }catch(e){} try{ document.dispatchEvent(new Event('pageshow')); }catch(e){} }catch(e){} })(); `; const blob = new Blob([src], { type: 'text/javascript' }); const url = URL.createObjectURL(blob); const s = document.createElement('script'); s.src = url; (document.head || document.documentElement).appendChild(s); s.onload = () => { try{ s.remove(); URL.revokeObjectURL(url); }catch{} }; }; // control api (randomized key + randomized method names) // note: prints the mapping to the console once so you can find it. // if you want zero console noise, delete the console.info line. const exposeApi = () => { if (!CONFIG.exposeApi) return; // build logical methods first const logical = { setStrict(v){ CONFIG.strictMode = !!v; }, setParanoid(v){ CONFIG.paranoidMode = !!v; }, setNotify(k,v){ if (k in CONFIG.notify) CONFIG.notify[k] = v; }, setMouse(k,v){ if (k in CONFIG.mouse) CONFIG.mouse[k] = v; }, stats(){ return JSON.parse(JSON.stringify(STATS)); }, reapply(){ return forceVisibilityProps(); } }; // create the randomized API object const api = {}; const nameMap = {}; // logicalName -> randomizedName for convenience for (const [logicalName, fn] of Object.entries(logical)) { const randName = secureRandomString(12); nameMap[logicalName] = randName; Object.defineProperty(api, randName, { value: freezeFn(fn), configurable: false, enumerable: false, // hide from for..in/Object.keys writable: false }); } // random API handle on window each load const apiKey = '__' + secureRandomString(14); Object.defineProperty(window, apiKey, { value: api, configurable: false, enumerable: false, writable: false }); // tells you how to call it each load try { console.info('[AVC] api key:', `window.${apiKey}`, 'methods:', nameMap); } catch {} }; // apply forceVisibilityProps(); forceVisibilityEvents(); installWindowFocusShims(); installPointerCaptureShims(); installTimerShims(); installFakeMouse(); exposeApi(); injectPageRealm(); document.addEventListener('DOMContentLoaded', dispatchInitialPing, { once: true }); installReapplyGuards(); })();