hyperframes 0.7.55 → 0.7.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -942,22 +942,26 @@
942
942
  // actually moved anything and the whole audit run is unreliable. Deliberately
943
943
  // a single opaque string (not a structured array) since Node only ever needs
944
944
  // equality, not per-element diffing.
945
- // Pixel-only canvas motion (a 2D/WebGL canvas repainting without any element
946
- // moving) is invisible to a geometry+opacity fingerprint and false-positived
947
- // sweep_static (wild report: 4K WebGL comp needed a transparent moving DOM
948
- // sentinel to pass). Downsample each visible canvas to 8x8 and fold its
949
- // pixels into the fingerprint. Tainted/zero-sized/unreadable canvases hash
950
- // to a constant — no worse than today, never a new false NEGATIVE for
951
- // DOM-motion comps.
952
- function canvasPixelHash(canvas) {
945
+ // Pixel-only media motion (a 2D/WebGL canvas repainting or a playing video
946
+ // without any element moving) is invisible to a geometry+opacity fingerprint
947
+ // and false-positives sweep_static. Downsample each visible canvas/video to
948
+ // 8x8 and fold its pixels into the fingerprint. Tainted, zero-sized, or
949
+ // unreadable media hashes to a constant — no worse than geometry-only
950
+ // detection and never a new false negative for DOM-motion compositions.
951
+ // Media inside iframes is intentionally outside this fingerprint: it lives
952
+ // in a separate document, and cross-origin frames are inaccessible under SOP.
953
+ function mediaPixelHash(element) {
953
954
  try {
954
- if (!canvas.width || !canvas.height) return "x";
955
+ const rect = element.getBoundingClientRect();
956
+ const sourceWidth = element.videoWidth || element.width || rect.width;
957
+ const sourceHeight = element.videoHeight || element.height || rect.height;
958
+ if (!sourceWidth || !sourceHeight) return "x";
955
959
  const off = document.createElement("canvas");
956
960
  off.width = 8;
957
961
  off.height = 8;
958
962
  const ctx = off.getContext("2d");
959
963
  if (!ctx) return "x";
960
- ctx.drawImage(canvas, 0, 0, 8, 8);
964
+ ctx.drawImage(element, 0, 0, 8, 8);
961
965
  const data = ctx.getImageData(0, 0, 8, 8).data;
962
966
  let hash = 0;
963
967
  for (let i = 0; i < data.length; i++) hash = (hash * 31 + data[i]) >>> 0;
@@ -980,9 +984,9 @@
980
984
  const opacity = round(opacityChain(element));
981
985
  return `${rect.left},${rect.top},${rect.width},${rect.height},${opacity}`;
982
986
  });
983
- for (const canvas of root.querySelectorAll("canvas")) {
984
- if (!isVisibleElement(canvas)) continue;
985
- parts.push(`c:${canvasPixelHash(canvas)}`);
987
+ for (const media of root.querySelectorAll("canvas, video")) {
988
+ if (!isVisibleElement(media)) continue;
989
+ parts.push(`p:${mediaPixelHash(media)}`);
986
990
  }
987
991
  return parts.join("|");
988
992
  };