{
  "version": 3,
  "sources": ["../src/modules/privacy.js"],
  "sourcesContent": ["// zelvior-runtime/privacy -- client-side privacy and anti-annoyance\n// protections. ESM source of truth; bundled by build.mjs. Zero dependency\n// on core zelvior.js.\n//\n// Read this before using it. A JS module running in a page (or, for the\n// extension, a content script) cannot do everything \"privacy protection\"\n// might suggest:\n//\n// - It CANNOT block third-party ad/tracker *network requests* -- that\n//   requires intercepting requests before they're sent, which only a\n//   browser extension's declarativeNetRequest API (or a network-level\n//   proxy/DNS blocker) can do. The zelvior-extension does this\n//   separately, with a deliberately compact, documented filter list --\n//   not this npm package, which has no access to the network layer at\n//   all. If you're using this module directly on a page (not through the\n//   extension), you are not getting network-level ad/tracker blocking.\n// - It CANNOT reliably stop a page from navigating itself away via\n//   `window.location = url` -- modern browsers do not allow scripts to\n//   intercept or veto that assignment, by design (this module does not\n//   pretend otherwise). What it CAN do: remove `<meta http-equiv=\"refresh\">`\n//   redirect tags before they fire (a real, common technique that\n//   auto-redirect pages actually rely on), and block `window.open()`\n//   popups that aren't tied to a genuine user gesture.\n// - \"Stealth mode\" reduces specific, well-known fingerprinting vectors\n//   (canvas reads, WebRTC local IP leakage) -- it does not make a browser\n//   untraceable, and it has real functional trade-offs, documented at\n//   each function below. Anything claiming \"no one can track you\" from a\n//   JS snippet is overstating what's possible; this module doesn't.\n\nvar counters = { popupsBlocked: 0, metaRefreshRemoved: 0, fingerprintCallsBlocked: 0 };\n\n// --- Global Privacy Control (GPC) / Do Not Track --------------------------\n// GPC is a real, currently-in-force legal signal: California's CCPA/CPRA\n// regulations (and several other US state privacy laws) require\n// businesses to honor it as a valid \"opt out of sale/sharing\" request\n// when a site reads `navigator.globalPrivacyControl === true`. This is\n// not a hypothetical or symbolic gesture -- it is the actual mechanism\n// the law defines. What this function does NOT do: force a site to\n// comply (compliance is a legal, not technical, guarantee), or add the\n// `Sec-GPC: 1` HTTP header (a page script cannot add headers to the\n// browser's own top-level request for itself; that requires the\n// extension's declarativeNetRequest layer, done separately).\nvar gpcSet = false;\nexport function sendDoNotSellSignal() {\n  try {\n    Object.defineProperty(window.navigator, 'globalPrivacyControl', { value: true, configurable: true, writable: false });\n    gpcSet = true;\n  } catch (e) {\n    // Some engines make `navigator` properties non-configurable once set\n    // by the browser itself (a few browsers now implement GPC natively\n    // and expose it as a real, non-overridable property) -- if so, check\n    // whether it's already true rather than treating this as a failure.\n    try { gpcSet = window.navigator.globalPrivacyControl === true; } catch (e2) { gpcSet = false; }\n  }\n  try {\n    // Do Not Track: the older, non-legally-binding precursor to GPC.\n    // Largely superseded and ignored by most sites today, but still\n    // checked by a handful -- costs nothing to also set.\n    Object.defineProperty(window.navigator, 'doNotTrack', { value: '1', configurable: true });\n  } catch (e) {}\n  return gpcSet;\n}\nexport function isDoNotSellSignalActive() {\n  try { return window.navigator.globalPrivacyControl === true; } catch (e) { return gpcSet; }\n}\n\n// --- Popup blocking --------------------------------------------------------\n// Blocks `window.open()` calls that are not tied to a genuine, current\n// user gesture (`navigator.userActivation.isActive`, the real browser API\n// for this -- not a heuristic guess). This is the same mechanism browsers\n// themselves use internally to decide whether to allow a popup at all;\n// this function makes the check available to page code and lets you\n// react to (and count) blocked attempts, and lets a specific origin be\n// allowlisted.\nvar originalOpen = null;\nvar popupAllowlist = {};\n\nexport function blockPopups(opts) {\n  opts = opts || {};\n  if (originalOpen) return; // already active\n  originalOpen = window.open;\n  window.open = function (url, target, features) {\n    var hasGesture = typeof window.navigator.userActivation === 'object'\n      ? window.navigator.userActivation.isActive\n      : true; // engines without the Activation API (rare) fail open rather than breaking every window.open call\n    var origin = null;\n    try { origin = new URL(url, window.location.href).origin; } catch (e) {}\n    var allowed = hasGesture || (origin && popupAllowlist[origin]);\n    if (allowed) return originalOpen.call(window, url, target, features);\n    counters.popupsBlocked++;\n    if (typeof opts.onBlocked === 'function') {\n      try { opts.onBlocked({ url: url, origin: origin }); } catch (e) {}\n    }\n    return null; // matches what a real browser popup blocker returns\n  };\n}\nexport function restorePopups() {\n  if (originalOpen) { window.open = originalOpen; originalOpen = null; }\n}\nexport function allowPopupsFrom(origin) { popupAllowlist[origin] = true; }\nexport function isBlockingPopups() { return originalOpen !== null; }\n\n// --- Meta-refresh redirect removal -----------------------------------------\n// Removes `<meta http-equiv=\"refresh\">` tags (the classic \"you will be\n// redirected in 3 seconds\" mechanism many redirect/ad-gate pages use)\n// before the browser acts on them. This only works if called before the\n// browser's own refresh timer fires -- call it as early as possible\n// (e.g. at `document_start` in an extension content script, which is\n// exactly when the zelvior-extension calls it).\nexport function removeMetaRefresh(root) {\n  var doc = root || document;\n  var tags = doc.querySelectorAll('meta[http-equiv=\"refresh\" i]');\n  var removed = 0;\n  for (var i = 0; i < tags.length; i++) {\n    if (tags[i].parentNode) { tags[i].parentNode.removeChild(tags[i]); removed++; }\n  }\n  counters.metaRefreshRemoved += removed;\n  return removed;\n}\n\n// --- Stealth mode (opt-in anti-fingerprinting) -----------------------------\n// REAL TRADE-OFFS, same honesty standard as Z.lite -- read every line\n// before enabling. Each piece below breaks a real, legitimate use of the\n// API it touches; this is why it's a function you call, not a default.\nvar stealthActive = false;\nvar originalToDataURL = null, originalGetImageData = null, originalRTCPeerConnection = null;\n\nfunction addCanvasNoise(imageData) {\n  // Adds a single-bit, per-pixel-channel perturbation seeded per-canvas-\n  // read-call -- enough to change the resulting hash fingerprinting\n  // libraries compute from canvas output, without being visible to the\n  // human eye. This is the same category of technique browsers'\n  // themselves (Brave, Firefox's canvas-poisoning) and privacy extensions\n  // use; it is not a novel invention here.\n  var d = imageData.data;\n  for (var i = 0; i < d.length; i += 4) {\n    var n = (Math.random() < 0.5) ? -1 : 1;\n    d[i] = Math.max(0, Math.min(255, d[i] + n));\n  }\n  return imageData;\n}\n\n/**\n * Enables a bundle of anti-fingerprinting measures:\n * - Canvas noise: `HTMLCanvasElement.toDataURL()` and\n *   `CanvasRenderingContext2D.getImageData()` return imperceptibly\n *   perturbed pixel data. BREAKS: any legitimate use of canvas image\n *   export/inspection that needs exact pixel values (image editors,\n *   QR/barcode scanners reading a canvas, some canvas-based tests).\n * - WebRTC local-IP leak prevention: blocks `RTCPeerConnection` from\n *   gathering \"host\" ICE candidates, which is the specific mechanism\n *   that leaks a device's real local (and sometimes public) IP address\n *   even behind a VPN. BREAKS: legitimate WebRTC video/audio calls and\n *   peer-to-peer connections, which need those candidates to connect at\n *   all -- this will make video-calling sites stop working while active.\n */\nexport function enableStealthMode() {\n  if (stealthActive) return;\n  stealthActive = true;\n  if (typeof window.HTMLCanvasElement !== 'undefined') {\n    originalToDataURL = window.HTMLCanvasElement.prototype.toDataURL;\n    window.HTMLCanvasElement.prototype.toDataURL = function () {\n      counters.fingerprintCallsBlocked++;\n      var ctx = this.getContext && this.getContext('2d');\n      if (ctx) {\n        try {\n          var data = ctx.getImageData(0, 0, this.width, this.height);\n          ctx.putImageData(addCanvasNoise(data), 0, 0);\n        } catch (e) {}\n      }\n      return originalToDataURL.apply(this, arguments);\n    };\n  }\n  if (typeof window.CanvasRenderingContext2D !== 'undefined') {\n    originalGetImageData = window.CanvasRenderingContext2D.prototype.getImageData;\n    window.CanvasRenderingContext2D.prototype.getImageData = function () {\n      counters.fingerprintCallsBlocked++;\n      var data = originalGetImageData.apply(this, arguments);\n      return addCanvasNoise(data);\n    };\n  }\n  if (typeof window.RTCPeerConnection !== 'undefined') {\n    originalRTCPeerConnection = window.RTCPeerConnection;\n    window.RTCPeerConnection = function (config) {\n      config = config || {};\n      // Forces the relay-only ICE transport policy, which prevents host\n      // (local network) and most reflexive (public IP-revealing)\n      // candidates from ever being gathered -- the real, documented fix\n      // for the WebRTC IP-leak fingerprinting/deanonymization vector.\n      config.iceTransportPolicy = 'relay';\n      counters.fingerprintCallsBlocked++;\n      return new originalRTCPeerConnection(config);\n    };\n    window.RTCPeerConnection.prototype = originalRTCPeerConnection.prototype;\n  }\n}\n\nexport function disableStealthMode() {\n  if (!stealthActive) return;\n  if (originalToDataURL) { window.HTMLCanvasElement.prototype.toDataURL = originalToDataURL; originalToDataURL = null; }\n  if (originalGetImageData) { window.CanvasRenderingContext2D.prototype.getImageData = originalGetImageData; originalGetImageData = null; }\n  if (originalRTCPeerConnection) { window.RTCPeerConnection = originalRTCPeerConnection; originalRTCPeerConnection = null; }\n  stealthActive = false;\n}\nexport function isStealthModeActive() { return stealthActive; }\n\n/** Snapshot of what this module has actually blocked/removed/modified so far, for real display -- not simulated numbers. */\nexport function metrics() {\n  return {\n    popupsBlocked: counters.popupsBlocked,\n    metaRefreshRemoved: counters.metaRefreshRemoved,\n    fingerprintCallsBlocked: counters.fingerprintCallsBlocked,\n  };\n}\n"],
  "mappings": "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",
  "names": ["privacy_exports", "__export", "allowPopupsFrom", "blockPopups", "disableStealthMode", "enableStealthMode", "isBlockingPopups", "isDoNotSellSignalActive", "isStealthModeActive", "metrics", "removeMetaRefresh", "restorePopups", "sendDoNotSellSignal", "__toCommonJS", "counters", "gpcSet", "e2", "originalOpen", "popupAllowlist", "opts", "url", "target", "features", "hasGesture", "origin", "e", "allowed", "root", "doc", "tags", "removed", "i", "stealthActive", "originalToDataURL", "originalGetImageData", "originalRTCPeerConnection", "addCanvasNoise", "imageData", "d", "n", "ctx", "data", "config"]
}
