// adeater-hooks.js — Security interceptors for Tabby's ad eater.
// Runs in MAIN world (world: "MAIN") so prototype overrides intercept page scripts.
// Communicates back to adeater.js (isolated world) via CustomEvents on window.
//
// NO chrome.* APIs available here. Use dispatchEvent to relay data instead.
//
// SCOPE: this file keeps ONLY hooks that actually PREVENT harm:
//   1. auto-popup blocking          (returns null for non-gesture window.open)
//   2. crypto-miner worker kill     (neuters Workers from known miner domains)
//   3. chrome:// / extension nav    (blocks window.open to privileged URLs)
//   4. scareware suppression        (fullscreen loops + alert/confirm/prompt spam)
// Detection-only "threat logging" (canvas/WebRTC/document.write/atob/exfil) was
// removed: it prevented nothing, cost perf on hot paths (atob/innerHTML), and was
// the source of repeated false positives (e.g. Cloudflare's STUN/blob workers,
// React innerHTML, legacy document.write). Network-level ad/tracker blocking lives
// in declarativeNetRequest (dnr-rules.json); DOM cleanup in adeater-sweepers.js.

(function() {
  // ---------------------------------------------------------------------------
  // Config flags (must match adeater.js)
  // ---------------------------------------------------------------------------
  const EAT_POPUPS               = true;
  const ENABLE_MINER             = true;
  const ENABLE_EXTENSION_PROTECT = true;
  const ENABLE_SCAREWARE_PROTECT = true;

  // ---------------------------------------------------------------------------
  // Whitelist — must match adeater.js
  // ---------------------------------------------------------------------------
  const WHITELIST_HOSTS = [
    "mail.google.com",
    "accounts.google.com",
    "drive.google.com",
    "docs.google.com",
    "outlook.live.com",
    "outlook.office.com",
    "bank",
    "banking",
    "paypal.com",
    "stripe.com",
    "github.com",
    "gitlab.com",
  ];

  function isWhitelisted() {
    const host = location.hostname.toLowerCase();
    return WHITELIST_HOSTS.some((w) => host.includes(w));
  }

  // Skip whitelisted domains and sub-frames.
  if (isWhitelisted()) return;
  if (window.top !== window) return;

  // Prevent double-run (e.g. if script somehow injects twice).
  if (window.__tabbyHooks) return;
  window.__tabbyHooks = true;

  // ---------------------------------------------------------------------------
  // Constants
  // ---------------------------------------------------------------------------
  const MINER_DOMAINS = [
    "coinhive.com", "coin-hive.com", "coin-hive",
    "cryptoloot.pro", "crypto-loot",
    "webmine.pro", "webminepool.com", "webminerpool",
    "minexmr.com",
    "coinerra.com",
    "coinimp.com",
    "coinblind.com",
    "coinnebula.com",
    "afminer.com",
    "monerominer.rocks",
    "gateminer.com",
    "reasedoper.pw",
    "minero.cc", "minero.pw",
    "ppoi.org",
    "mabutil.com",
    "minerrr.com",
    "crypto-webminer.com",
    "jsecoin.com",
    "coinad.cc",
    "ad-miner.com",
    "tominer.xyz",
    "cashbees.io",
    "hostero.eu",
    "dailybees.com",
    "lmodr.com",
    "bwu.pw",
    "5w.fit",
    "statdynamic.com",
    "minr.pw",
    "oceanlab.info",
  ];

  // ---------------------------------------------------------------------------
  // Communication to adeater.js (isolated world)
  // ---------------------------------------------------------------------------
  function reportHookStat(stat, count) {
    window.dispatchEvent(new CustomEvent("tabby-hook-stat", {
      detail: { stat, count: count || 1 },
    }));
  }

  function reportHookThreat(type, detail) {
    window.dispatchEvent(new CustomEvent("tabby-hook-threat", {
      detail: { type, detail },
    }));
  }

  // ---------------------------------------------------------------------------
  // 1. Popup protection — block non-gesture window.open calls
  // ---------------------------------------------------------------------------
  function setupPopupProtection() {
    if (!EAT_POPUPS) return;
    let userGesture = false;
    const gestureEnd = () => { userGesture = true; setTimeout(() => { userGesture = false; }, 600); };
    document.addEventListener("click", gestureEnd, true);
    document.addEventListener("keydown", gestureEnd, true);
    document.addEventListener("mousedown", gestureEnd, true);

    const origOpen = window.open;
    let popupBlockCount = 0;
    try {
      window.open = function(url, name, features) {
        if (!url) return origOpen.call(window, url, name, features);
        if (userGesture) return origOpen.call(window, url, name, features);
        popupBlockCount++;
        reportHookStat("redirectsBlocked");
        return null;
      };
    } catch { /* Cannot override window.open */ }
  }

  // ---------------------------------------------------------------------------
  // 2. Crypto-miner worker kill — neuter Workers loaded from known miner domains
  // ---------------------------------------------------------------------------
  function setupMinerDetection() {
    if (!ENABLE_MINER) return;

    const OrigWorker = window.Worker;
    if (!OrigWorker) return;
    try {
      window.Worker = new Proxy(OrigWorker, {
        construct(target, args) {
          const url = args[0];
          if (typeof url === "string") {
            const urlLower = url.toLowerCase();
            if (MINER_DOMAINS.some((d) => urlLower.includes(d))) {
              reportHookStat("minersDetected");
              // Replace the miner worker with one that immediately closes.
              return new OrigWorker("data:text/javascript,self.close()");
            }
          }
          return Reflect.construct(target, args);
        },
      });
    } catch { /* CSP may block Worker override */ }
  }

  // ---------------------------------------------------------------------------
  // 3. Extension / chrome URL protection — block window.open to privileged URLs
  // ---------------------------------------------------------------------------
  function setupExtensionProtection() {
    if (!ENABLE_EXTENSION_PROTECT) return;
    try {
      const origOpen = window.open;
      window.open = function(url, ...args) {
        if (typeof url === "string" && (url.startsWith("chrome://") || url.startsWith("chrome-extension://"))) {
          reportHookThreat("CHROME_URL_ACCESS", "Blocked window.open to: " + url);
          return null;
        }
        return origOpen.call(this, url, ...args);
      };
    } catch { /* CSP may block window.open override */ }
    // navigator.plugins / mimeTypes intentionally NOT intercepted — standard
    // capability APIs used by media players/codec detection; flagging them is
    // pure false positives with no security value.
  }

  // ---------------------------------------------------------------------------
  // 4. Scareware / browser-lock suppression — fullscreen loops + dialog spam
  // ---------------------------------------------------------------------------
  function setupScarewareProtection() {
    if (!ENABLE_SCAREWARE_PROTECT) return;

    let fullscreenEnterCount = 0;
    let fullscreenTimer = null;
    try {
      const origReqFullscreen = Element.prototype.requestFullscreen;
      Element.prototype.requestFullscreen = function(...args) {
        fullscreenEnterCount++;
        // Threshold 6: YouTube/Netflix routinely hit 3-5 (enter+exit+re-enter on
        // player interactions). True scareware loops push far higher than 6.
        if (fullscreenEnterCount > 6) {
          reportHookThreat("FULLSCREEN_LOOP", "Repeated fullscreen requests (" + fullscreenEnterCount + " times) - possible scareware");
          try { if (document.fullscreenElement) document.exitFullscreen(); } catch {}
          return Promise.reject(new Error("Blocked by Tabby: fullscreen loop detected"));
        }
        if (fullscreenTimer) clearTimeout(fullscreenTimer);
        fullscreenTimer = setTimeout(() => { fullscreenEnterCount = 0; }, 5000);
        return origReqFullscreen.apply(this, args);
      };
    } catch { /* CSP may block fullscreen override */ }

    let dialogCount = 0;
    let dialogTimer = null;
    const resetDialogCount = () => { dialogCount = 0; };
    const guardDialog = (orig, blockedReturn) => function(msg) {
      dialogCount++;
      if (dialogCount > 3) {
        reportHookThreat("DIALOG_LOOP", "Repeated dialog calls (" + dialogCount + " in 2s) - possible scareware");
        return blockedReturn;
      }
      if (dialogTimer) clearTimeout(dialogTimer);
      dialogTimer = setTimeout(resetDialogCount, 2000);
      return orig.call(this, msg);
    };

    try { window.alert = guardDialog(window.alert, undefined); } catch {}
    try { window.confirm = guardDialog(window.confirm, false); } catch {}
    try { window.prompt = guardDialog(window.prompt, null); } catch {}
  }

  // ---------------------------------------------------------------------------
  // Init — all interceptors run immediately at document_start
  // ---------------------------------------------------------------------------
  setupPopupProtection();
  setupMinerDetection();
  setupExtensionProtection();
  setupScarewareProtection();
})();
