@zaplier/sdk 1.0.3 → 1.0.5
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.
- package/dist/index.cjs +120 -95
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +120 -95
- package/dist/index.esm.js.map +1 -1
- package/dist/sdk.js +120 -95
- package/dist/sdk.js.map +1 -1
- package/dist/sdk.min.js +1 -1
- package/dist/src/modules/fingerprint/canvas.d.ts +1 -1
- package/dist/src/modules/fingerprint/canvas.d.ts.map +1 -1
- package/dist/src/modules/fingerprint/hashing.d.ts.map +1 -1
- package/dist/src/utils/browser.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/sdk.js
CHANGED
|
@@ -179,8 +179,10 @@
|
|
|
179
179
|
* Hash fingerprint components into a stable identifier
|
|
180
180
|
*/
|
|
181
181
|
function hashFingerprint(components) {
|
|
182
|
-
// Convert components to canonical string representation
|
|
183
|
-
|
|
182
|
+
// Convert components to canonical string representation using deep sorting
|
|
183
|
+
// CRITICAL: Do NOT use JSON.stringify(obj, keys) as it filters out nested properties!
|
|
184
|
+
// Use canonicalizeStable instead which handles deep sorting and normalization.
|
|
185
|
+
const canonical = JSON.stringify(canonicalizeStable(components));
|
|
184
186
|
// Use MurmurHash3 x64 for consistent hashing
|
|
185
187
|
return x64hash128(canonical);
|
|
186
188
|
}
|
|
@@ -1033,7 +1035,13 @@
|
|
|
1033
1035
|
// Quick stability check
|
|
1034
1036
|
const webglContext = gl;
|
|
1035
1037
|
const renderer = webglContext.getParameter(webglContext.RENDERER);
|
|
1036
|
-
|
|
1038
|
+
const isSupported = Boolean(renderer && typeof renderer === "string");
|
|
1039
|
+
// Cleanup context to avoid "Too many active WebGL contexts" warning
|
|
1040
|
+
const ext = webglContext.getExtension("WEBGL_lose_context");
|
|
1041
|
+
if (ext) {
|
|
1042
|
+
ext.loseContext();
|
|
1043
|
+
}
|
|
1044
|
+
return isSupported;
|
|
1037
1045
|
}
|
|
1038
1046
|
catch {
|
|
1039
1047
|
return false;
|
|
@@ -1347,11 +1355,11 @@
|
|
|
1347
1355
|
* Text to render for canvas fingerprinting with maximum differentiation
|
|
1348
1356
|
*/
|
|
1349
1357
|
const CANVAS_TEXTS = [
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1358
|
+
"Zap Canvas 🎨🔒2024",
|
|
1359
|
+
"Żółć gęślą jaźń €$¢£¥",
|
|
1360
|
+
"αβγδεζηθικλμνξο",
|
|
1361
|
+
"中文测试字体渲染",
|
|
1362
|
+
"🌟🎯🚀💎🌊🎨",
|
|
1355
1363
|
];
|
|
1356
1364
|
/**
|
|
1357
1365
|
* Enhanced geometric shapes for canvas fingerprinting with sub-pixel precision
|
|
@@ -1359,19 +1367,19 @@
|
|
|
1359
1367
|
function drawAdvancedGeometry(ctx) {
|
|
1360
1368
|
// Enable high-quality rendering for sub-pixel differences
|
|
1361
1369
|
ctx.imageSmoothingEnabled = true;
|
|
1362
|
-
ctx.imageSmoothingQuality =
|
|
1370
|
+
ctx.imageSmoothingQuality = "high";
|
|
1363
1371
|
// Complex gradient with multiple stops
|
|
1364
1372
|
const radialGradient = ctx.createRadialGradient(75, 75, 0, 75, 75, 50);
|
|
1365
|
-
radialGradient.addColorStop(0,
|
|
1366
|
-
radialGradient.addColorStop(0.3,
|
|
1367
|
-
radialGradient.addColorStop(0.7,
|
|
1368
|
-
radialGradient.addColorStop(1,
|
|
1373
|
+
radialGradient.addColorStop(0, "rgba(255, 0, 0, 0.8)");
|
|
1374
|
+
radialGradient.addColorStop(0.3, "rgba(0, 255, 0, 0.6)");
|
|
1375
|
+
radialGradient.addColorStop(0.7, "rgba(0, 0, 255, 0.4)");
|
|
1376
|
+
radialGradient.addColorStop(1, "rgba(255, 255, 0, 0.2)");
|
|
1369
1377
|
ctx.fillStyle = radialGradient;
|
|
1370
1378
|
ctx.fillRect(10, 10, 130, 100);
|
|
1371
1379
|
// Sub-pixel positioned shapes for GPU/anti-aliasing differentiation
|
|
1372
|
-
ctx.fillStyle =
|
|
1380
|
+
ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
|
|
1373
1381
|
ctx.fillRect(15.5, 15.3, 49.7, 49.2);
|
|
1374
|
-
ctx.fillStyle =
|
|
1382
|
+
ctx.fillStyle = "#f60";
|
|
1375
1383
|
ctx.fillRect(70.3, 10.7, 50.1, 50.9);
|
|
1376
1384
|
// Complex bezier curves that stress different rendering engines
|
|
1377
1385
|
ctx.beginPath();
|
|
@@ -1379,21 +1387,21 @@
|
|
|
1379
1387
|
ctx.bezierCurveTo(25.2, 120.1, 75.8, 90.3, 125.5, 120.7);
|
|
1380
1388
|
ctx.bezierCurveTo(125.5, 120.7, 90.1, 150.2, 60.8, 140.9);
|
|
1381
1389
|
ctx.closePath();
|
|
1382
|
-
ctx.fillStyle =
|
|
1390
|
+
ctx.fillStyle = "rgba(200, 100, 50, 0.6)";
|
|
1383
1391
|
ctx.fill();
|
|
1384
1392
|
// Overlapping circles with complex blending
|
|
1385
|
-
ctx.globalCompositeOperation =
|
|
1393
|
+
ctx.globalCompositeOperation = "multiply";
|
|
1386
1394
|
ctx.beginPath();
|
|
1387
1395
|
ctx.arc(50.7, 80.3, 20.1, 0, Math.PI * 2);
|
|
1388
|
-
ctx.fillStyle =
|
|
1396
|
+
ctx.fillStyle = "rgba(255, 0, 100, 0.5)";
|
|
1389
1397
|
ctx.fill();
|
|
1390
1398
|
ctx.beginPath();
|
|
1391
1399
|
ctx.arc(70.3, 80.7, 20.9, 0, Math.PI * 2);
|
|
1392
|
-
ctx.fillStyle =
|
|
1400
|
+
ctx.fillStyle = "rgba(0, 255, 100, 0.5)";
|
|
1393
1401
|
ctx.fill();
|
|
1394
|
-
ctx.globalCompositeOperation =
|
|
1402
|
+
ctx.globalCompositeOperation = "source-over";
|
|
1395
1403
|
// Thin lines to test anti-aliasing
|
|
1396
|
-
ctx.strokeStyle =
|
|
1404
|
+
ctx.strokeStyle = "rgba(50, 50, 50, 0.8)";
|
|
1397
1405
|
ctx.lineWidth = 0.5;
|
|
1398
1406
|
ctx.setLineDash([2.3, 1.7]);
|
|
1399
1407
|
for (let i = 0; i < 10; i++) {
|
|
@@ -1405,29 +1413,29 @@
|
|
|
1405
1413
|
// Reset line dash
|
|
1406
1414
|
ctx.setLineDash([]);
|
|
1407
1415
|
// Pattern with complex repeating elements
|
|
1408
|
-
const patternCanvas = document.createElement(
|
|
1416
|
+
const patternCanvas = document.createElement("canvas");
|
|
1409
1417
|
patternCanvas.width = 20;
|
|
1410
1418
|
patternCanvas.height = 20;
|
|
1411
|
-
const patternCtx = patternCanvas.getContext(
|
|
1419
|
+
const patternCtx = patternCanvas.getContext("2d");
|
|
1412
1420
|
if (patternCtx) {
|
|
1413
|
-
patternCtx.fillStyle =
|
|
1421
|
+
patternCtx.fillStyle = "rgba(150, 75, 200, 0.3)";
|
|
1414
1422
|
patternCtx.fillRect(0, 0, 10, 10);
|
|
1415
1423
|
patternCtx.fillRect(10, 10, 10, 10);
|
|
1416
|
-
const pattern = ctx.createPattern(patternCanvas,
|
|
1424
|
+
const pattern = ctx.createPattern(patternCanvas, "repeat");
|
|
1417
1425
|
if (pattern) {
|
|
1418
1426
|
ctx.fillStyle = pattern;
|
|
1419
1427
|
ctx.fillRect(150, 120, 80, 60);
|
|
1420
1428
|
}
|
|
1421
1429
|
}
|
|
1422
1430
|
// Shadow effects for additional GPU differentiation
|
|
1423
|
-
ctx.shadowColor =
|
|
1431
|
+
ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
|
|
1424
1432
|
ctx.shadowBlur = 3.2;
|
|
1425
1433
|
ctx.shadowOffsetX = 2.1;
|
|
1426
1434
|
ctx.shadowOffsetY = 2.7;
|
|
1427
|
-
ctx.fillStyle =
|
|
1435
|
+
ctx.fillStyle = "rgba(100, 200, 150, 0.8)";
|
|
1428
1436
|
ctx.fillRect(160, 30, 60, 40);
|
|
1429
1437
|
// Reset shadow
|
|
1430
|
-
ctx.shadowColor =
|
|
1438
|
+
ctx.shadowColor = "transparent";
|
|
1431
1439
|
ctx.shadowBlur = 0;
|
|
1432
1440
|
ctx.shadowOffsetX = 0;
|
|
1433
1441
|
ctx.shadowOffsetY = 0;
|
|
@@ -1437,23 +1445,34 @@
|
|
|
1437
1445
|
*/
|
|
1438
1446
|
function drawAdvancedText(ctx) {
|
|
1439
1447
|
const fonts = [
|
|
1440
|
-
|
|
1448
|
+
"14px Arial, sans-serif",
|
|
1441
1449
|
'13px "Times New Roman", serif',
|
|
1442
|
-
|
|
1443
|
-
|
|
1450
|
+
"12px Georgia, serif",
|
|
1451
|
+
"15px Helvetica, Arial, sans-serif",
|
|
1444
1452
|
'11px "Courier New", monospace',
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
'12px "Comic Sans MS", cursive'
|
|
1453
|
+
"13px Verdana, sans-serif",
|
|
1454
|
+
"16px Impact, fantasy",
|
|
1455
|
+
'12px "Comic Sans MS", cursive',
|
|
1448
1456
|
];
|
|
1449
1457
|
const colors = [
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1458
|
+
"#000",
|
|
1459
|
+
"#333",
|
|
1460
|
+
"#666",
|
|
1461
|
+
"#999",
|
|
1462
|
+
"rgba(255, 0, 0, 0.8)",
|
|
1463
|
+
"rgba(0, 255, 0, 0.7)",
|
|
1464
|
+
"rgba(0, 0, 255, 0.6)",
|
|
1465
|
+
"rgba(128, 64, 192, 0.9)",
|
|
1453
1466
|
];
|
|
1454
1467
|
// Test different text baselines and alignments
|
|
1455
|
-
const baselines = [
|
|
1456
|
-
|
|
1468
|
+
const baselines = [
|
|
1469
|
+
"top",
|
|
1470
|
+
"hanging",
|
|
1471
|
+
"middle",
|
|
1472
|
+
"alphabetic",
|
|
1473
|
+
"bottom",
|
|
1474
|
+
];
|
|
1475
|
+
const alignments = ["left", "center", "right"];
|
|
1457
1476
|
let y = 250;
|
|
1458
1477
|
CANVAS_TEXTS.forEach((text, textIndex) => {
|
|
1459
1478
|
fonts.forEach((font, fontIndex) => {
|
|
@@ -1462,11 +1481,11 @@
|
|
|
1462
1481
|
const alignIndex = fontIndex % alignments.length;
|
|
1463
1482
|
ctx.font = font;
|
|
1464
1483
|
ctx.fillStyle = colors[colorIndex] || colors[0];
|
|
1465
|
-
ctx.textBaseline = baselines[baselineIndex] ||
|
|
1466
|
-
ctx.textAlign = alignments[alignIndex] ||
|
|
1484
|
+
ctx.textBaseline = baselines[baselineIndex] || "top";
|
|
1485
|
+
ctx.textAlign = alignments[alignIndex] || "left";
|
|
1467
1486
|
// Sub-pixel positioning for enhanced differentiation
|
|
1468
|
-
const x = 10 + (fontIndex * 0.7) % 200;
|
|
1469
|
-
const yPos = y + (fontIndex * 18.3) % 100;
|
|
1487
|
+
const x = 10 + ((fontIndex * 0.7) % 200);
|
|
1488
|
+
const yPos = y + ((fontIndex * 18.3) % 100);
|
|
1470
1489
|
ctx.fillText(text, x, yPos);
|
|
1471
1490
|
// Add stroke text for additional GPU stress testing
|
|
1472
1491
|
if (fontIndex % 2 === 0) {
|
|
@@ -1482,20 +1501,20 @@
|
|
|
1482
1501
|
ctx.translate(100, 400);
|
|
1483
1502
|
ctx.rotate(Math.PI / 6);
|
|
1484
1503
|
ctx.scale(1.2, 0.8);
|
|
1485
|
-
ctx.font =
|
|
1486
|
-
ctx.fillStyle =
|
|
1487
|
-
ctx.fillText(
|
|
1504
|
+
ctx.font = "italic 14px Arial";
|
|
1505
|
+
ctx.fillStyle = "rgba(200, 100, 50, 0.8)";
|
|
1506
|
+
ctx.fillText("Transformed & Rotated Text", 0, 0);
|
|
1488
1507
|
ctx.restore();
|
|
1489
1508
|
// Test very small and very large text
|
|
1490
|
-
ctx.font =
|
|
1491
|
-
ctx.fillStyle =
|
|
1492
|
-
ctx.fillText(
|
|
1493
|
-
ctx.font =
|
|
1494
|
-
ctx.fillStyle =
|
|
1495
|
-
ctx.fillText(
|
|
1509
|
+
ctx.font = "6px Arial";
|
|
1510
|
+
ctx.fillStyle = "#000";
|
|
1511
|
+
ctx.fillText("Tiny text rendering test", 10, 500);
|
|
1512
|
+
ctx.font = "32px Arial";
|
|
1513
|
+
ctx.fillStyle = "rgba(255, 0, 0, 0.5)";
|
|
1514
|
+
ctx.fillText("Large", 10, 540);
|
|
1496
1515
|
// Reset text properties
|
|
1497
|
-
ctx.textAlign =
|
|
1498
|
-
ctx.textBaseline =
|
|
1516
|
+
ctx.textAlign = "left";
|
|
1517
|
+
ctx.textBaseline = "top";
|
|
1499
1518
|
}
|
|
1500
1519
|
/**
|
|
1501
1520
|
* Analyze sub-pixel differences for enhanced GPU/anti-aliasing detection
|
|
@@ -1505,9 +1524,9 @@
|
|
|
1505
1524
|
// Create test pattern for sub-pixel analysis
|
|
1506
1525
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1507
1526
|
// Draw shapes at sub-pixel coordinates to stress anti-aliasing
|
|
1508
|
-
ctx.fillStyle =
|
|
1527
|
+
ctx.fillStyle = "rgb(100, 150, 200)";
|
|
1509
1528
|
ctx.fillRect(10.3, 10.7, 20.1, 20.9);
|
|
1510
|
-
ctx.strokeStyle =
|
|
1529
|
+
ctx.strokeStyle = "rgb(200, 100, 50)";
|
|
1511
1530
|
ctx.lineWidth = 1.3;
|
|
1512
1531
|
ctx.beginPath();
|
|
1513
1532
|
ctx.moveTo(15.2, 35.8);
|
|
@@ -1538,7 +1557,7 @@
|
|
|
1538
1557
|
return `${avgRed}-${avgGreen}-${avgBlue}-${avgVariance}`;
|
|
1539
1558
|
}
|
|
1540
1559
|
catch (error) {
|
|
1541
|
-
return
|
|
1560
|
+
return "subpixel-error";
|
|
1542
1561
|
}
|
|
1543
1562
|
}
|
|
1544
1563
|
/**
|
|
@@ -1551,47 +1570,52 @@
|
|
|
1551
1570
|
// Test 1: Check if getImageData works consistently
|
|
1552
1571
|
const imageData = ctx.getImageData(0, 0, 1, 1);
|
|
1553
1572
|
if (!imageData || imageData.data.length === 0) {
|
|
1554
|
-
reasons.push(
|
|
1573
|
+
reasons.push("getImageData-blocked");
|
|
1555
1574
|
suspiciousSignals += 3;
|
|
1556
1575
|
}
|
|
1557
1576
|
// Test 2: Check hash lengths (blocked canvas often returns default values)
|
|
1558
1577
|
if (textHash.length < 8 || geometryHash.length < 8) {
|
|
1559
|
-
reasons.push(
|
|
1578
|
+
reasons.push("short-hash");
|
|
1560
1579
|
suspiciousSignals += 2;
|
|
1561
1580
|
}
|
|
1562
1581
|
// Test 3: Check for known blocked patterns
|
|
1563
1582
|
const knownBlockedHashes = [
|
|
1564
|
-
|
|
1565
|
-
|
|
1583
|
+
"error",
|
|
1584
|
+
"blocked",
|
|
1585
|
+
"disabled",
|
|
1586
|
+
"00000000",
|
|
1587
|
+
"ffffffff",
|
|
1588
|
+
"12345678",
|
|
1566
1589
|
];
|
|
1567
|
-
if (knownBlockedHashes.includes(textHash) ||
|
|
1568
|
-
|
|
1590
|
+
if (knownBlockedHashes.includes(textHash) ||
|
|
1591
|
+
knownBlockedHashes.includes(geometryHash)) {
|
|
1592
|
+
reasons.push("known-blocked-hash");
|
|
1569
1593
|
suspiciousSignals += 3;
|
|
1570
1594
|
}
|
|
1571
1595
|
// Test 4: Sub-pixel analysis indicates blocking
|
|
1572
|
-
if (subPixelData ===
|
|
1573
|
-
reasons.push(
|
|
1596
|
+
if (subPixelData === "subpixel-error" || subPixelData.includes("0-0-0")) {
|
|
1597
|
+
reasons.push("subpixel-anomaly");
|
|
1574
1598
|
suspiciousSignals += 2;
|
|
1575
1599
|
}
|
|
1576
1600
|
// Test 5: Same hash for different operations (indicates fake canvas)
|
|
1577
|
-
if (textHash === geometryHash && textHash !==
|
|
1578
|
-
reasons.push(
|
|
1601
|
+
if (textHash === geometryHash && textHash !== "error") {
|
|
1602
|
+
reasons.push("identical-hashes");
|
|
1579
1603
|
suspiciousSignals += 2;
|
|
1580
1604
|
}
|
|
1581
1605
|
// Test 6: toDataURL performance (some privacy tools slow it down)
|
|
1582
|
-
const canvas = document.createElement(
|
|
1606
|
+
const canvas = document.createElement("canvas");
|
|
1583
1607
|
canvas.width = 10;
|
|
1584
1608
|
canvas.height = 10;
|
|
1585
|
-
const testCtx = canvas.getContext(
|
|
1609
|
+
const testCtx = canvas.getContext("2d");
|
|
1586
1610
|
if (testCtx) {
|
|
1587
1611
|
const start = performance.now();
|
|
1588
|
-
testCtx.fillStyle =
|
|
1612
|
+
testCtx.fillStyle = "red";
|
|
1589
1613
|
testCtx.fillRect(0, 0, 10, 10);
|
|
1590
1614
|
canvas.toDataURL();
|
|
1591
1615
|
const duration = performance.now() - start;
|
|
1592
1616
|
// Suspiciously slow canvas operations
|
|
1593
1617
|
if (duration > 50) {
|
|
1594
|
-
reasons.push(
|
|
1618
|
+
reasons.push("slow-canvas");
|
|
1595
1619
|
suspiciousSignals += 1;
|
|
1596
1620
|
}
|
|
1597
1621
|
}
|
|
@@ -1603,7 +1627,7 @@
|
|
|
1603
1627
|
return {
|
|
1604
1628
|
isInconsistent: true,
|
|
1605
1629
|
confidence: 1,
|
|
1606
|
-
reasons: [
|
|
1630
|
+
reasons: ["detection-error"],
|
|
1607
1631
|
};
|
|
1608
1632
|
}
|
|
1609
1633
|
}
|
|
@@ -1614,29 +1638,30 @@
|
|
|
1614
1638
|
const startTime = performance.now();
|
|
1615
1639
|
try {
|
|
1616
1640
|
// Create larger canvas for more detailed fingerprinting
|
|
1617
|
-
const canvas = document.createElement(
|
|
1641
|
+
const canvas = document.createElement("canvas");
|
|
1618
1642
|
canvas.width = 300;
|
|
1619
1643
|
canvas.height = 600;
|
|
1620
|
-
const ctx = canvas.getContext(
|
|
1644
|
+
const ctx = canvas.getContext("2d", {
|
|
1621
1645
|
alpha: true,
|
|
1622
1646
|
desynchronized: false,
|
|
1623
|
-
colorSpace:
|
|
1647
|
+
colorSpace: "srgb",
|
|
1648
|
+
willReadFrequently: true,
|
|
1624
1649
|
});
|
|
1625
1650
|
if (!ctx) {
|
|
1626
|
-
throw new Error(
|
|
1651
|
+
throw new Error("Canvas 2D context not available");
|
|
1627
1652
|
}
|
|
1628
1653
|
// Test canvas winding (different behavior across browsers/GPUs)
|
|
1629
1654
|
ctx.rect(0, 0, 10, 10);
|
|
1630
1655
|
ctx.rect(2, 2, 6, 6);
|
|
1631
|
-
const isClockwise = ctx.isPointInPath(5, 5,
|
|
1656
|
+
const isClockwise = ctx.isPointInPath(5, 5, "evenodd");
|
|
1632
1657
|
// ENHANCED: Draw advanced text with multiple fonts and effects
|
|
1633
1658
|
drawAdvancedText(ctx);
|
|
1634
|
-
const textData = canvas.toDataURL(
|
|
1659
|
+
const textData = canvas.toDataURL("image/png");
|
|
1635
1660
|
const textHash = hash32(textData);
|
|
1636
1661
|
// Clear and draw advanced geometry
|
|
1637
1662
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1638
1663
|
drawAdvancedGeometry(ctx);
|
|
1639
|
-
const geometryData = canvas.toDataURL(
|
|
1664
|
+
const geometryData = canvas.toDataURL("image/png");
|
|
1640
1665
|
const geometryHash = hash32(geometryData);
|
|
1641
1666
|
// ENHANCED: Analyze sub-pixels for GPU/anti-aliasing differentiation
|
|
1642
1667
|
const subPixelAnalysis = analyzeSubPixels(ctx, canvas);
|
|
@@ -1646,10 +1671,10 @@
|
|
|
1646
1671
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1647
1672
|
// Draw both text and geometry together
|
|
1648
1673
|
drawAdvancedText(ctx);
|
|
1649
|
-
ctx.globalCompositeOperation =
|
|
1674
|
+
ctx.globalCompositeOperation = "multiply";
|
|
1650
1675
|
drawAdvancedGeometry(ctx);
|
|
1651
|
-
ctx.globalCompositeOperation =
|
|
1652
|
-
const compositeData = canvas.toDataURL(
|
|
1676
|
+
ctx.globalCompositeOperation = "source-over";
|
|
1677
|
+
const compositeData = canvas.toDataURL("image/png");
|
|
1653
1678
|
const compositeHash = hash32(compositeData);
|
|
1654
1679
|
const endTime = performance.now();
|
|
1655
1680
|
const result = {
|
|
@@ -1661,27 +1686,27 @@
|
|
|
1661
1686
|
subPixelAnalysis, // Sub-pixel characteristics for GPU differentiation
|
|
1662
1687
|
compositeHash, // Combined text+geometry hash
|
|
1663
1688
|
inconsistencyConfidence: inconsistencyAnalysis.confidence,
|
|
1664
|
-
blockingReasons: inconsistencyAnalysis.reasons
|
|
1689
|
+
blockingReasons: inconsistencyAnalysis.reasons,
|
|
1665
1690
|
};
|
|
1666
1691
|
return {
|
|
1667
1692
|
value: result,
|
|
1668
|
-
duration: endTime - startTime
|
|
1693
|
+
duration: endTime - startTime,
|
|
1669
1694
|
};
|
|
1670
1695
|
}
|
|
1671
1696
|
catch (error) {
|
|
1672
1697
|
return {
|
|
1673
1698
|
value: {
|
|
1674
|
-
text:
|
|
1675
|
-
geometry:
|
|
1699
|
+
text: "error",
|
|
1700
|
+
geometry: "error",
|
|
1676
1701
|
winding: false,
|
|
1677
1702
|
isInconsistent: true,
|
|
1678
|
-
subPixelAnalysis:
|
|
1679
|
-
compositeHash:
|
|
1703
|
+
subPixelAnalysis: "error",
|
|
1704
|
+
compositeHash: "error",
|
|
1680
1705
|
inconsistencyConfidence: 1,
|
|
1681
|
-
blockingReasons: [
|
|
1706
|
+
blockingReasons: ["canvas-error"],
|
|
1682
1707
|
},
|
|
1683
1708
|
duration: performance.now() - startTime,
|
|
1684
|
-
error: error instanceof Error ? error.message :
|
|
1709
|
+
error: error instanceof Error ? error.message : "Canvas fingerprinting failed",
|
|
1685
1710
|
};
|
|
1686
1711
|
}
|
|
1687
1712
|
}
|
|
@@ -1690,9 +1715,9 @@
|
|
|
1690
1715
|
*/
|
|
1691
1716
|
function isCanvasAvailable$1() {
|
|
1692
1717
|
try {
|
|
1693
|
-
const canvas = document.createElement(
|
|
1694
|
-
const ctx = canvas.getContext(
|
|
1695
|
-
return ctx !== null && typeof ctx.fillText ===
|
|
1718
|
+
const canvas = document.createElement("canvas");
|
|
1719
|
+
const ctx = canvas.getContext("2d");
|
|
1720
|
+
return ctx !== null && typeof ctx.fillText === "function";
|
|
1696
1721
|
}
|
|
1697
1722
|
catch {
|
|
1698
1723
|
return false;
|
|
@@ -3790,7 +3815,7 @@
|
|
|
3790
3815
|
const canvas = iframeDoc.createElement("canvas");
|
|
3791
3816
|
canvas.width = 100;
|
|
3792
3817
|
canvas.height = 50;
|
|
3793
|
-
const ctx = canvas.getContext("2d");
|
|
3818
|
+
const ctx = canvas.getContext("2d", { willReadFrequently: true });
|
|
3794
3819
|
if (!ctx)
|
|
3795
3820
|
return { quality: 0, antiAliasing: false, smoothing: 0 };
|
|
3796
3821
|
// Draw text at sub-pixel position to trigger anti-aliasing
|
|
@@ -3843,7 +3868,7 @@
|
|
|
3843
3868
|
const canvas = iframeDoc.createElement("canvas");
|
|
3844
3869
|
canvas.width = 100;
|
|
3845
3870
|
canvas.height = 50;
|
|
3846
|
-
const ctx = canvas.getContext("2d");
|
|
3871
|
+
const ctx = canvas.getContext("2d", { willReadFrequently: true });
|
|
3847
3872
|
if (!ctx) {
|
|
3848
3873
|
return { clearType: false, hintingLevel: 0, subpixelRendering: false };
|
|
3849
3874
|
}
|