No Ad IFrame

No Ad IFrame created by Stay

// ==UserScript==
// @name         No Ad IFrame
// @namespace    https://staybrowser.com/
// @version      0.1.1

// @description         No Ad IFrame created by Stay
// @description:en      No Ad IFrame created by Stay
// @description:ja      No Ad IFrame created by Stay
// @description:zh-TW   No Ad IFrame created by Stay
// @description:zh-CN   No Ad IFrame created by Stay

// @author       You
// @match        *://*/*
// @grant        none
// @inject-into page
// @unwrap
// @run-at              document-start
// @license             MIT
// @compatible          chrome
// @compatible          firefox
// @compatible          opera
// @compatible          edge
// @compatible          safari
// @allFrames           true
// ==/UserScript==
(function () {
    'use strict';

    const TURN_ON_BLOCK_REMOVAL = true;

    if (Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, "__src437__")) return;

    Object.defineProperty(HTMLIFrameElement.prototype, "__src437__", {
        value: undefined,
        writable: true,
        configurable: false,
        enumerable: false
    });

    if (!Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, "__src437__")) return;

    const pd1 = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, "src");
    const pd2 = Object.assign({}, pd1, {
        configurable: true,
        enumerable: true
    });

    const wSnb = Symbol();

    const adFilter = {

        'iframe[src*="doubleclick.net"]': 1,
        'iframe[src*="googlesyndication.com"]': 1,
        'iframe[src*="googleadservices.com"]': 1,
        'iframe[src*="googletagservices.com"]': 1,
        'iframe[src*="adservice.google.com"]': 1,
        'iframe[src*="adservice.yahoo.com"]': 1,
        'iframe[src*="amazon-adsystem.com"]': 1,
        'iframe[src*="adroll.com"]': 1,
        'iframe[src*="ads-twitter.com"]': 1,
        'iframe[src*="criteo.com"]': 1,
        'iframe[src*="taboola.com"]': 1,
        'iframe[src*="outbrain.com"]': 1,
        'iframe[src*="smartadserver.com"]': 1,
        'iframe[src*="openx.net"]': 1,
        'iframe[src*="rubiconproject.com"]': 1,

        'iframe[id^="aswift_"][src*="ads"][3]': 1,
        'iframe[id^="google_ads_iframe_"]': 1,
        'iframe[name^="google_ads_iframe_"]': 1,
        // 'iframe[src*="doubleclick.net"]': 1,
        // 'iframe[src*="googlesyndication.com"]': 1,
        'iframe[src*="adservice"]': 1,
        'iframe[src*="adserver"]': 1,
        // 'iframe[src*="/ads/"]': 1,
        // 'iframe[class*="ad-"]': 1,
        'iframe[data-ad-slot]': 1,

        // Additional patterns
        'iframe[id*="adframe"]': 1,         // Common variation like "adframe"
        'iframe[id*="ad_iframe"]': 1,       // "ad_iframe" naming pattern
        'iframe[id*="ad_container"]': 1,    // Container-like naming often used for ads
        'iframe[id*="ad_wrapper"]': 1,      // Another container naming pattern

        'iframe[class*="adslot"]': 1,       // "adslot" is a known pattern (e.g., GPT ad slots)
        'iframe[class*="adsense"]': 1,      // "adsense" typically indicates Google AdSense
        'iframe[class*="ad_frame"]': 1,     // Variation with underscore
        // 'iframe[class*="advert"]': 1,       // "advert" is another clue

        // Attribute patterns
        'iframe[data-ad-client]': 1,         // Data attribute used by some ad scripts
        'iframe[data-ad-region]': 1,         // Another ad-related data attribute

    };

    function createBase64DataURL(title) {
        // Construct a minimal HTML5 page with the given title
        const html = `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"/><title>${title}</title></head><body></body></html>`;

        // Encode the HTML string into Base64
        const base64String = btoa(html);

        // Return as a Data URL
        return `data:text/html;base64,${base64String}`;
    }


    const dynamicGeneration = () => {
        const key = Math.floor(Math.random() * 314159265359 + 314159265359).toString(36);
        return createBase64DataURL(key);
    }

    const caching = new Map();

    const hKey = `e-${Math.floor(Math.random() * 314159265359 + 314159265359).toString(36)}`;

    const checkOnly = (elm) => {

        let noP = true;
        let noQ = true;
        let t = elm || 0;
        while (t = t.previousSibling) {

            if (t instanceof Text) {
                if (t.textContent.trim().length > 0) {
                    noP = false;
                    break;
                }
            } else if (t instanceof Element) {
                const nd = t.nodeName;
                if (nd === 'NOSCRIPT' || nd === 'SCRIPT' || nd === 'STYLE') continue;
                noP = false;
                break;
            } else if (t instanceof Comment) {

            } else {
                noP = false;
                break;
            }
        }
        if (noP === false) return false;

        t = elm || 0;
        while (t = t.nextSibling) {

            if (t instanceof Text) {
                if (t.textContent.trim().length > 0) {
                    noQ = false;
                    break;
                }
            } else if (t instanceof Element) {
                const nd = t.nodeName;
                if (nd === 'NOSCRIPT' || nd === 'SCRIPT' || nd === 'STYLE') continue;
                noQ = false;
                break;
            } else if (t instanceof Comment) {

            } else {
                noQ = false;
                break;
            }
        }

        if (noQ === false) return false;

        return true;


    };
    const hideIframe = (iframe) => {
        let noscriptWrapOK = false;
        try {
            if (iframe instanceof HTMLIFrameElement && iframe.isConnected === true && (iframe.parentNode || 0).nodeName !== 'NOSCRIPT') {
                const noscript = document.createElement('noscript');
                iframe.replaceWith(noscript);
                noscript.appendChild(iframe);
                noscriptWrapOK = true;
            }
        } catch (e) {
            console.warn(e);
        }

        if (TURN_ON_BLOCK_REMOVAL && noscriptWrapOK) {
            let layerNMax = 8;
            let parent = iframe;
            let lastSuccess = iframe;
            while (parent instanceof HTMLElement && checkOnly(parent)) {
                lastSuccess = parent;
                parent = parent.parentNode;
                layerNMax--;
                if (!layerNMax) break;
            }

            const effectNode = parent instanceof HTMLElement ? parent : lastSuccess;

            if ((effectNode instanceof HTMLElement) && effectNode.nodeName !== "NOSCRIPT") {
                effectNode.setAttribute(hKey, '');
                let noscriptWrapOK = false;
                try {
                    const noscript = document.createElement('noscript');
                    effectNode.replaceWith(noscript);
                    noscript.appendChild(effectNode);
                    noscriptWrapOK = true;
                } catch (e) { }
                if (!noscriptWrapOK) {
                    effectNode.style.setProperty('position', 'fixed', 'important');
                    effectNode.style.setProperty('left', '-130vw', 'important');
                    effectNode.style.setProperty('top', '-140vh', 'important');
                }
            }

        }

    };


    const convertionUrl = (nv, iframe) => {

        let btt = 0;

        if (typeof nv == 'string' && nv.length > 15 && iframe instanceof HTMLIFrameElement) {
            if ((iframe.parentNode || 0).nodeName === 'NOSCRIPT') return null;
            if (nv.length >= 22 && nv.startsWith('data:text/html;base64,')) return null;
            btt = 1;
        } else if ((nv || '') === '' && iframe instanceof HTMLIFrameElement) {
            btt = 2;
        }


        if (btt > 0) {
            if (btt === 1) {
                const cv = caching.get(nv);
                if (cv !== undefined) return cv;
            }
            for (const adf of Object.keys(adFilter)) {
                const adk = adf.replace(/\[\d+\w*\]/g, '');
                if (iframe.matches(adk)) {
                    const w = adFilter[adf];
                    const bv = typeof w === 'string' ? w : dynamicGeneration();
                    if (btt === 1) {
                        caching.set(nv, bv);
                        caching.set(bv, bv);
                    }
                    return bv;
                }
            }
            if (btt === 1) {
                caching.set(nv, null);
            }
        }
        return null;
    };
    const pd3 = {
        set(nv) {
            if (typeof nv === 'string') {
                if (this[wSnb] === nv) return true;
                if (nv.length >= 22 && nv.startsWith('data:text/html;base64,')) {
                } else if (nv.length > 15) {
                    const bv = convertionUrl(nv, this);
                    if (bv) {
                        hideIframe(this);
                        if ((this.parentNode || 0).nodeName !== 'NOSCRIPT') nv = bv;
                    }
                }
            }
            this[wSnb] = nv;
            return true;
        },
        get() {
            return this[wSnb];
        },
        configurable: true,
        enumerable: true
    };

    Object.defineProperty(HTMLIFrameElement.prototype, wSnb, pd2);


    Object.defineProperty(HTMLIFrameElement.prototype, "src", pd3);


    document.addEventListener('load', function (evt) {
        const evtTarget = evt.target;
        if (evtTarget instanceof HTMLIFrameElement) {
            const bv = convertionUrl(evtTarget.src, evtTarget);
            if (bv) {
                hideIframe(evtTarget);
                if ((evtTarget.parentNode || 0).nodeName !== 'NOSCRIPT') evtTarget.src = bv;
                evt.stopPropagation();
                evt.stopImmediatePropagation();
                evt.preventDefault();
            }
        }
    }, true);

    const um = new WeakSet();

    const iframeAll = document.getElementsByTagName('iframe');
    let iframeLC = 0;
    new MutationObserver(() => {
        const iframeTC = iframeAll.length;
        if (iframeTC !== iframeLC) {
            iframeLC = iframeTC;
            for (const iframe of iframeAll) {
                if (um.has(iframe)) continue;
                um.add(iframe);
                const bv = convertionUrl(iframe.src, iframe);
                if (bv) {
                    hideIframe(iframe);
                    if ((iframe.parentNode || 0).nodeName !== 'NOSCRIPT') iframe.src = bv;
                }
            }
        }
    }).observe(document, { subtree: true, childList: true });

    // Your code here...
})();