Known Limitations
Nvirya AdGuard operates inside the constraints of browser JavaScript environments and userscript managers. This page documents known technical limitations verified in the v11.7.0 codebase and tested mobile/desktop environments.
A. YouTube Mobile Ad Delay#
Why does a YouTube advertisement sometimes appear briefly before being skipped or dismissed?#
On YouTube (youtube.com, m.youtube.com, youtu.be, and youtube-nocookie.com), Nvirya AdGuard isolates all YouTube-specific logic inside initYouTubeShield(), which runs only when Media Guard (config.playerProtection) is enabled.
The script uses two mechanisms to handle YouTube video advertisements:
- Player JSON Metadata Pruning (
installYouTubeJsonPrunerandmainWorldShield):
When YouTube loads initial player data (window.ytInitialPlayerResponse) or fetches /youtubei/v1/player, /youtubei/v1/watch, or /youtubei/v1/next via Window.fetch or XMLHttpRequest, the script inspects the JSON object and removes the adPlacements, playerAds, adSlots, adBreakHeartbeatParams, and adBreakRenderer keys while blocking standalone ad-tracking endpoints (RE_YOUTUBE_AD_ENDPOINT, such as /youtubei/v1/player/ad_break, /api/stats/ads, /pagead/, and doubleclick.net).
- DOM Player-State Fallback (
scheduleVideoAdScanandhandleAdVideo):
If YouTube serves an ad through a path that bypasses initial JSON pruning (for example, when initial page data is parsed before userscript injection completes on mobile Safari/WebKit, or when YouTube updates its player response structure), the ad begins loading inside the primary <video> element. At that point, #movie_player or .html5-video-player receives the .ad-showing or .ad-interrupting class.
When hasAdPlayerMarker(video) detects .ad-showing or .ad-interrupting, scheduleVideoAdScan() (which runs on requestAnimationFrame with a 150 ms minimum throttle and a 500 ms active follow-up timer, triggered by timeupdate, loadedmetadata, durationchange, play, ended, yt-navigate-finish, and DOM mutations) performs the following actions in handleAdVideo(video):
- Clicks visible skip controls matching
.ytp-skip-ad-button,.ytp-ad-skip-button,.ytp-ad-skip-button-modern,.ytp-ad-SkipButton,ytm-skip-ad-renderer button,.ytp-ad-overlay-close-button, or.videoAdUiSkipButton. - Saves your current audio and speed settings (
muted,volume, andplaybackRate) inactiveAdVideos. - Mutes the ad and accelerates playback: Sets
video.muted = true,video.volume = 0, andvideo.playbackRate = 16. - Seeks to the end of the ad stream: Once
video.durationis a finite number greater than0.5seconds (isFinite(duration) && duration > 0.5), the script jumps toMath.max(0, duration - 0.05)viavideo.currentTime(falling back tovideo.fastSeek()). - Restores your player state (
restoreAdVideo): As soon as.ad-showing/.ad-interruptingclears, your originalmuted,volume, andplaybackRatevalues are restored.
Why the script does not blindly block media streams#
Nvirya AdGuard explicitly whitelists media delivery endpoints (RE_YOUTUBE_CDN_WHITELIST, including googlevideo.com, youtube.com/videoplayback, ytimg.com, and youtube.com/s/player). Abruptly blocking media stream requests associated with video playback can cause the HTML5 video player to hang, buffer indefinitely, or display a fatal playback error screen. Because the fallback mechanism must wait for the player to attach the .ad-showing state and report a valid video.duration (> 0.5 s) before seeking to the end, a brief visual flash or transition can occur before the ad is dismissed.
Distinguishing Expected Behavior from Bugs#
Because YouTube frequently modifies its mobile and desktop player delivery, behavior can vary across browser versions, network speeds, and account experiments:
| Observed Behavior | What It Indicates | Recommended Action |
|---|---|---|
| Ad flashes briefly, is muted, and jumps to the end | Expected behavior of the handleAdVideo player-state fallback when an ad reaches the <video> element | No action required |
| Ad plays at normal speed with sound or remains visible | YouTube's player markup or ad indicator class changed, or Media Guard is turned off | Verify Media Guard is enabled in Settings and check Troubleshooting |
| Video buffers endlessly or displays a playback error | Possible conflict between in-page pruning, YouTube player changes, or an external network/DNS filter | Follow the YouTube recovery steps in Troubleshooting |
B. VPN, DNS Filters, Shadowrocket, Surge, and Loon#
Many mobile and desktop users run Nvirya AdGuard alongside network-level filtering tools such as Shadowrocket, Surge, Loon, AdGuard DNS, NextDNS, or a VPN with built-in ad blocking.
How network-level blocking interacts with a userscript#
When a website loads an advertising script or bait request:
- When Nvirya AdGuard handles the request alone: Its in-page hooks (
mainWorldShieldandinstallNetworkGuard) interceptfetchandXMLHttpRequestcalls to ad domains and return synthetic200 OKresponses, or intercept<script>and<img>elements to spoofonloadevents and populate mock vendor objects (window.googletag,window.OBR,window.Criteo,window._taboola,window.apstag,window.COMSCORE,window.adsbygoogle). - When a VPN or DNS filter blocks the domain first: The request is rejected at the DNS or TCP/TLS layer (
ERR_NAME_NOT_RESOLVEDorERR_CONNECTION_REFUSED) before a response reaches the browser.- For
fetch()calls, Nvirya AdGuard includes a.catch()fallback on known ad domains (doubleclick,googlesyndication,googleadservices,pagead,outbrain,taboola,criteo, etc.) that converts a network rejection into a synthetic200 OKresponse. - However, if a network-level filter blocks a native HTML
<script>,<img>, or<iframe>tag before Nvirya AdGuard's main-world hooks run—or if WebKit sandbox isolation prevents main-world injection—the browser fires a nativeerrorevent that the userscript cannot always intercept or override.
- For
Practical guidance#
- Do not assume every anti-adblock message is caused by your VPN or DNS filter. Sites use many different detection methods, and some anti-adblock walls trigger regardless of DNS settings.
- Do not disable your entire VPN or DNS protection globally. If a specific website refuses to load content because an anti-adblock script detects a DNS-blocked domain (and pausing Nvirya AdGuard alone does not resolve it), test allowing only that specific site or the specific false-positive domain in your proxy/DNS tool rather than turning off network security across all sites.
C. Android CPU and Battery Usage#
Running any userscript on complex modern websites introduces additional JavaScript execution. On Android devices, performance and battery impact depend on:
- Page complexity and DOM size: Infinite-scroll feeds, live sports/finance tickers, and heavy single-page applications generate thousands of DOM mutations per minute.
- Browser engine and WebView implementation: Standalone Android browsers (such as Firefox for Android, Cromite/Kiwi, or browsers built on Android System WebView like XBrowser) differ significantly in JavaScript JIT compilation speed, garbage collection, and userscript injection overhead.
- Active script features: DOM mutation inspection, shadow-root scanning, and video player event listeners consume more CPU on pages that continuously mutate attributes.
Built-in performance safeguards in v11.7.0#
Nvirya AdGuard includes specific architectural safeguards to bound CPU work:
- Strict YouTube Isolation (
initYouTubeShield): YouTube player listeners, JSON pruners, and cosmetic rules are never initialized on non-YouTube domains. - Search Engine Mutation Debouncing (
RE_SEARCH_DEBOUNCE_HOST): On Google Search and Bing Search, DOM mutation batches are debounced by150 msand internal framework attributes (jsaction,data-ved,jscontroller) are skipped. - Time-Sliced Chunk Processing (
processInChunks): Queued DOM nodes (capped atMAX_QUEUE = 800) and trap checks (capped at1,400) are processed in chunks with a maximum budget of8 msper frame usingrequestIdleCallbackorrequestAnimationFrame. - Fast-Exit Trap Filtering (
isInvisibleTrap): Common inline text tags (<p>,<span>,<h1>–<h6>,<li>,<td>,<tr>,<code>, etc.) and nodes inside primary article containers (article,main,#content) exit immediately before callingwindow.getComputedStyle()orgetBoundingClientRect().
How to reduce overhead or isolate a regression#
If a specific website feels warm, laggy, or battery-intensive on Android:
- Tap the Quick Action Button (or press
Alt+A) and select Pause for This Session (or Allow This Site) in the Overview tab. - If you do not need video ad skipping on a media-heavy site, you can toggle off Media Guard in the Settings tab.
- If you enabled Deep Network Filter (
strictMode) in Settings, note that it enables single-signal cosmetic hiding (signals >= 1 && config.strictMode), which inspects and hides more elements than the default configuration. - If pausing Nvirya AdGuard immediately resolves severe lag on a specific website, please report the hostname using Send Feedback so the maintainer can profile and optimize that site's mutation patterns.
D. iOS Safari and WebKit Restrictions#
iOS and iPadOS browsers (including Safari with the Stay userscript manager) run on Apple's WebKit engine, which enforces strict security and sandboxing boundaries that differ from desktop browsers and Android Chromium/Gecko browsers:
- Isolated JavaScript Worlds: Userscripts on iOS Safari often execute in an isolated content-script world rather than the page's main JavaScript world. While DOM modifications (such as hiding elements or adding
<style>tags) are shared between worlds, JavaScript variables onwindow(such aswindow.fetch,window.open, orwindow.googletag) are isolated unless a<script>tag can be injected directly into the page DOM (injectMainWorldShield). - Content Security Policy (CSP) and Injection Timing: Even when
injectMainWorldShield()inserts a<script>element intodocument.documentElement, a site's Content Security Policy (CSP) or early inline scripts may block or outrun the injected script. However, CSP is only one factor—WebKit's extension sandbox model, userscript injection timing, and lack of privileged network APIs also restrict what a userscript can intercept on iOS. - What
unsupportedMeans for Anti-Adblock Bypass:
In v11.7.0, the function checkBypassSupport() checks whether the device is an iOS/iPadOS device (isAppleDevice()) and verifies whether typeof window.__NVIRYA_MAIN_SHIELD__ !== 'undefined' is accessible in the current execution context.
- When
checkBypassSupport()returnsfalseon iOS/iPadOS:- Under About → System Capabilities, Anti-Adblock Bypass displays an
unsupportedbadge. - Under Settings, the Anti-Adblock Bypass switch is disabled (grayed out) and displays the subtitle: "Unavailable due to iOS WebKit sandbox limits".
- Under About → System Capabilities, Anti-Adblock Bypass displays an
- This indicator specifically reflects that main-world global property mocking (
__NVIRYA_MAIN_SHIELD__) is unavailable in that isolated WebKit context. Standard cosmetic CSS filtering, Hide Page Element, Clean Leftover Frames, and Unlock Page Scroll remain supported.