mirror of
https://forge.fsky.io/oneflux/omegafox.git
synced 2026-02-10 16:02:04 -08:00
feat: Add human-like cursor movement & cursor highlighter #19 beta.10
All info in README.
This commit is contained in:
parent
782a2157bd
commit
80b084a9b3
7 changed files with 380 additions and 23 deletions
36
README.md
36
README.md
|
|
@ -21,6 +21,7 @@ Camoufox aims to be a minimalistic browser for robust fingerprint injection & an
|
|||
- Spoof properties of device, viewport, screen, WebGL, battery API, location, etc. ✅
|
||||
- Font spoofing & anti-fingerprinting ✅
|
||||
- WebRTC IP spoofing ✅
|
||||
- Human-like mouse movement 🖱️
|
||||
- Blocks & circumvents ads 🛡️
|
||||
- Debloated & optimized for memory and speed ⚡
|
||||
- [PyPi package](https://pypi.org/project/camoufox/) for updates & auto fingerprint injection 📦
|
||||
|
|
@ -68,7 +69,7 @@ Camoufox is built on top of Firefox/Juggler instead of Chromium because:
|
|||
- Continue research on potential leaks
|
||||
- Remote hosting Camoufox as a Playwright server
|
||||
- Integrate into [hrequests](https://github.com/daijro/hrequests)
|
||||
- Human-like typing & mouse movement
|
||||
- Human-like typing & ~~mouse movement~~ ✔️
|
||||
- WebGL fingerprint spoofing through ANGLE rendering
|
||||
- Create integration tests
|
||||
- Chromium port (long term)
|
||||
|
|
@ -130,6 +131,34 @@ Camoufox will automatically add the following default fonts associated your spoo
|
|||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Cursor movement
|
||||
</summary>
|
||||
|
||||
### Human-like Cursor movement
|
||||
|
||||
Camoufox supports use human-like cursor movement. The natural motion algorithm was originally from [rifosnake's HumanCursor](https://github.com/riflosnake/HumanCursor), but has been rewritten in C++ and modified for more distance-aware trajectories.
|
||||
|
||||
### Demo
|
||||
|
||||
<video src="https://i.imgur.com/JxWRVtm.mp4" width="500px" alt="demo" autoplay loop muted></video>
|
||||
|
||||
### Properties
|
||||
|
||||
| Property | Supported | Description |
|
||||
| ---------------- | --------- | ------------------------------------------------------------------- |
|
||||
| humanize | ✅ | Enable/disable human-like cursor movement. Defaults to False. |
|
||||
| humanize:maxTime | ✅ | Maximum time in seconds for the cursor movement. Defaults to `1.5`. |
|
||||
| showcursor | ✅ | Show/hide the cursor. Defaults to True. |
|
||||
|
||||
**Notes:**
|
||||
|
||||
- Trajectories are capped at taking 1.5 seconds to complete.
|
||||
- The cursor highlighter is **not** ran in the page context. It will not be visible to the page. You don't have to worry about it leaking.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Fonts
|
||||
|
|
@ -483,7 +512,7 @@ Camoufox performs well against every major WAF I've tested. (Original test sites
|
|||
| **Font Fingerprinting** | ✔️ |
|
||||
| ‣ [Browserleaks Fonts](https://browserleaks.net/fonts) | ✔️ Rotates all metrics. |
|
||||
| ‣ [CreepJS TextMetrics](https://abrahamjuliot.github.io/creepjs/tests/fonts.html) | ✔️ Rotates all metrics. |
|
||||
| [**Incolumitas**](https://bot.incolumitas.com/) | ✔️ 0.8-r1.0 |
|
||||
| [**Incolumitas**](https://bot.incolumitas.com/) | ✔️ 0.8-1.0 |
|
||||
| [**SannySoft**](https://bot.sannysoft.com/) | ✔️ |
|
||||
| [**Fingerprint.com**](https://fingerprint.com/products/bot-detection/) | ✔️ |
|
||||
| [**IpHey**](https://iphey.com/) | ✔️ |
|
||||
|
|
@ -741,4 +770,5 @@ Patches can be edited, created, removed, and managed through here.
|
|||
- [Jamir-boop/minimalisticfox](https://github.com/Jamir-boop/minimalisticfox) - Inspired Camoufox's minimalistic theming
|
||||
- [nicoth-in/Dark-Space-Theme](https://github.com/nicoth-in/Dark-Space-Theme) - Camoufox's dark theme
|
||||
- [Playwright](https://github.com/microsoft/playwright/tree/main/browser_patches/firefox), [Puppeteer/Juggler](https://github.com/puppeteer/juggler) - Original Juggler implementation
|
||||
- [CreepJS](https://github.com/abrahamjuliot/creepjs), [Browserleaks](https://browserleaks.com) - Valuable leak testing sites
|
||||
- [CreepJS](https://github.com/abrahamjuliot/creepjs), [Browserleaks](https://browserleaks.com), [BrowserScan](https://www.browserscan.net/) - Valuable leak testing sites
|
||||
- [riflosnake/HumanCursor](https://github.com/riflosnake/HumanCursor) - Original human-like cursor movement algorithm
|
||||
|
|
|
|||
230
additions/camoucfg/MouseTrajectories.hpp
Normal file
230
additions/camoucfg/MouseTrajectories.hpp
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <random>
|
||||
#include <ctime>
|
||||
#include "MaskConfig.hpp"
|
||||
|
||||
/**
|
||||
* Human-like mouse movement generator. Ported from:
|
||||
* https://github.com/riflosnake/HumanCursor/blob/main/humancursor/utilities/human_curve_generator.py
|
||||
* Modified to use a more human-like easing function.
|
||||
*/
|
||||
|
||||
class BezierCalculator {
|
||||
public:
|
||||
static long long factorial(int n) {
|
||||
if (n < 0) return -1; // Indicate error
|
||||
long long result = 1;
|
||||
for (int i = 2; i <= n; i++) result *= i;
|
||||
return result;
|
||||
}
|
||||
|
||||
static double binomial(int n, int k) {
|
||||
return static_cast<double>(factorial(n)) /
|
||||
(factorial(k) * factorial(n - k));
|
||||
}
|
||||
|
||||
static double bernsteinPolynomialPoint(double x, int i, int n) {
|
||||
return binomial(n, i) * std::pow(x, i) * std::pow(1 - x, n - i);
|
||||
}
|
||||
|
||||
static std::vector<double> bernsteinPolynomial(
|
||||
const std::vector<std::pair<double, double>>& points, double t) {
|
||||
int n = static_cast<int>(points.size()) - 1;
|
||||
double x = 0.0;
|
||||
double y = 0.0;
|
||||
for (int i = 0; i <= n; i++) {
|
||||
double bern = bernsteinPolynomialPoint(t, i, n);
|
||||
x += points[i].first * bern;
|
||||
y += points[i].second * bern;
|
||||
}
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
static std::vector<std::vector<double>> calculatePointsInCurve(
|
||||
int nPoints, const std::vector<std::pair<double, double>>& points) {
|
||||
std::vector<std::vector<double>> curvePoints;
|
||||
for (int i = 0; i < nPoints; i++) {
|
||||
double t = static_cast<double>(i) / (nPoints - 1);
|
||||
curvePoints.push_back(bernsteinPolynomial(points, t));
|
||||
}
|
||||
return curvePoints;
|
||||
}
|
||||
};
|
||||
|
||||
class HumanizeMouseTrajectory {
|
||||
public:
|
||||
HumanizeMouseTrajectory(const std::pair<double, double>& fromPoint,
|
||||
const std::pair<double, double>& toPoint)
|
||||
: fromPoint(fromPoint), toPoint(toPoint) {
|
||||
generateCurve();
|
||||
}
|
||||
|
||||
std::vector<int> getPoints() const {
|
||||
std::vector<int> flatPoints;
|
||||
flatPoints.reserve(points.size() * 2);
|
||||
|
||||
for (const auto& point : points) {
|
||||
flatPoints.push_back(static_cast<int>(std::round(point[0])));
|
||||
flatPoints.push_back(static_cast<int>(std::round(point[1])));
|
||||
}
|
||||
|
||||
return flatPoints;
|
||||
}
|
||||
|
||||
private:
|
||||
std::pair<double, double> fromPoint;
|
||||
std::pair<double, double> toPoint;
|
||||
std::vector<std::vector<double>> points;
|
||||
|
||||
void generateCurve() {
|
||||
double leftBoundary = std::min(fromPoint.first, toPoint.first) - 80.0;
|
||||
double rightBoundary = std::max(fromPoint.first, toPoint.first) + 80.0;
|
||||
double downBoundary = std::min(fromPoint.second, toPoint.second) - 80.0;
|
||||
double upBoundary = std::max(fromPoint.second, toPoint.second) + 80.0;
|
||||
|
||||
std::vector<std::pair<double, double>> internalKnots =
|
||||
generateInternalKnots(leftBoundary, rightBoundary, downBoundary,
|
||||
upBoundary, 2);
|
||||
|
||||
std::vector<std::vector<double>> curvePoints =
|
||||
generatePoints(internalKnots);
|
||||
curvePoints = distortPoints(curvePoints, 1.0, 1.0, 0.5);
|
||||
points = tweenPoints(curvePoints);
|
||||
}
|
||||
|
||||
double easeOutQuad(double n) const {
|
||||
assert(n >= 0.0 && n <= 1.0 && "Argument must be between 0.0 and 1.0.");
|
||||
return -n * (n - 2);
|
||||
}
|
||||
|
||||
std::vector<std::pair<double, double>> generateInternalKnots(
|
||||
double lBoundary, double rBoundary, double dBoundary, double uBoundary,
|
||||
int knotsCount) const {
|
||||
assert(isNumeric(lBoundary) && isNumeric(rBoundary) &&
|
||||
isNumeric(dBoundary) && isNumeric(uBoundary) &&
|
||||
"Boundaries must be numeric values");
|
||||
assert(knotsCount >= 0 && "knotsCount must be non-negative");
|
||||
assert(lBoundary <= rBoundary &&
|
||||
"Left boundary must be less than or equal to right boundary");
|
||||
assert(dBoundary <= uBoundary &&
|
||||
"Down boundary must be less than or equal to upper boundary");
|
||||
|
||||
std::vector<double> knotsX =
|
||||
randomChoiceDoubles(lBoundary, rBoundary, knotsCount);
|
||||
std::vector<double> knotsY =
|
||||
randomChoiceDoubles(dBoundary, uBoundary, knotsCount);
|
||||
|
||||
std::vector<std::pair<double, double>> knots;
|
||||
for (int i = 0; i < knotsCount; i++) {
|
||||
knots.emplace_back(knotsX[i], knotsY[i]);
|
||||
}
|
||||
return knots;
|
||||
}
|
||||
|
||||
std::vector<double> randomChoiceDoubles(double min, double max,
|
||||
int size) const {
|
||||
std::vector<double> choices;
|
||||
std::uniform_real_distribution<double> dist(min, max);
|
||||
for (int i = 0; i < size; i++) {
|
||||
choices.push_back(dist(randomEngine));
|
||||
}
|
||||
return choices;
|
||||
}
|
||||
|
||||
std::vector<std::vector<double>> generatePoints(
|
||||
const std::vector<std::pair<double, double>>& knots) const {
|
||||
assert(isListOfPoints(knots) && "Knots must be a valid list of points");
|
||||
int midPtsCnt = static_cast<int>(
|
||||
std::max({std::abs(fromPoint.first - toPoint.first),
|
||||
std::abs(fromPoint.second - toPoint.second), 2.0}));
|
||||
std::vector<std::pair<double, double>> controlPoints = knots;
|
||||
controlPoints.insert(controlPoints.begin(), fromPoint);
|
||||
controlPoints.push_back(toPoint);
|
||||
return BezierCalculator::calculatePointsInCurve(midPtsCnt, controlPoints);
|
||||
}
|
||||
|
||||
std::vector<std::vector<double>> distortPoints(
|
||||
const std::vector<std::vector<double>>& points, double distortionMean,
|
||||
double distortionStDev, double distortionFrequency) const {
|
||||
assert(isNumeric(distortionMean) && isNumeric(distortionStDev) &&
|
||||
isNumeric(distortionFrequency) && "Distortions must be numeric");
|
||||
assert(isListOfPoints(points) && "Points must be a valid list of points");
|
||||
assert(0.0 <= distortionFrequency && distortionFrequency <= 1.0 &&
|
||||
"distortion_frequency must be in range [0,1]");
|
||||
|
||||
std::vector<std::vector<double>> distorted;
|
||||
distorted.push_back(points.front());
|
||||
|
||||
std::normal_distribution<double> normalDist(distortionMean,
|
||||
distortionStDev);
|
||||
std::uniform_real_distribution<double> uniformDist(0.0, 1.0);
|
||||
|
||||
for (size_t i = 1; i < points.size() - 1; i++) {
|
||||
double x = points[i][0];
|
||||
double y = points[i][1];
|
||||
double delta = 0.0;
|
||||
if (uniformDist(randomEngine) < distortionFrequency) {
|
||||
delta = std::round(normalDist(randomEngine));
|
||||
}
|
||||
distorted.push_back({x, y + delta});
|
||||
}
|
||||
distorted.push_back(points.back());
|
||||
return distorted;
|
||||
}
|
||||
|
||||
int32_t getMaxTime() const {
|
||||
if (auto maxTime = MaskConfig::GetDouble("humanize:maxTime")) {
|
||||
return static_cast<int32_t>(maxTime.value() * 100);
|
||||
}
|
||||
return 150;
|
||||
}
|
||||
|
||||
std::vector<std::vector<double>> tweenPoints(
|
||||
const std::vector<std::vector<double>>& points) const {
|
||||
assert(isListOfPoints(points) && "List of points not valid");
|
||||
|
||||
double totalLength = 0.0;
|
||||
for (size_t i = 1; i < points.size(); ++i) {
|
||||
double dx = points[i][0] - points[i - 1][0];
|
||||
double dy = points[i][1] - points[i - 1][1];
|
||||
totalLength += std::sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
// Uses a power scale to keep the speed consistent
|
||||
int targetPoints = std::min(
|
||||
getMaxTime(),
|
||||
std::max(2, static_cast<int>(std::pow(totalLength, 0.25) * 20)));
|
||||
|
||||
std::vector<std::vector<double>> res;
|
||||
for (int i = 0; i < targetPoints; i++) {
|
||||
double t = static_cast<double>(i) / (targetPoints - 1);
|
||||
double easedT = easeOutQuad(t);
|
||||
int index = static_cast<int>(easedT * (points.size() - 1));
|
||||
res.push_back(points[index]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool isNumeric(double val) const { return !std::isnan(val); }
|
||||
|
||||
bool isListOfPoints(
|
||||
const std::vector<std::pair<double, double>>& points) const {
|
||||
for (const auto& p : points) {
|
||||
if (!isNumeric(p.first) || !isNumeric(p.second)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isListOfPoints(const std::vector<std::vector<double>>& points) const {
|
||||
for (const auto& p : points) {
|
||||
if (p.size() != 2 || !isNumeric(p[0]) || !isNumeric(p[1])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
mutable std::default_random_engine randomEngine{
|
||||
static_cast<unsigned long>(std::time(nullptr))};
|
||||
};
|
||||
|
|
@ -9,10 +9,10 @@ with Files("**"):
|
|||
|
||||
EXPORTS += [
|
||||
"MaskConfig.hpp",
|
||||
"MouseTrajectories.hpp",
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
"/dom/base",
|
||||
"/camoucfg",
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
|
|
@ -81,6 +81,7 @@ class PageHandler {
|
|||
|
||||
this._isDragging = false;
|
||||
this._lastMousePosition = { x: 0, y: 0 };
|
||||
this._lastTrackedPos = { x: 0, y: 0 };
|
||||
|
||||
this._reportedFrameIds = new Set();
|
||||
this._networkEventsForUnreportedFrameIds = new Map();
|
||||
|
|
@ -500,13 +501,11 @@ class PageHandler {
|
|||
await helper.awaitTopic('apz-repaints-flushed');
|
||||
|
||||
const watcher = new EventWatcher(this._pageEventSink, types, this._pendingEventWatchers);
|
||||
const promises = [];
|
||||
for (const type of types) {
|
||||
// This dispatches to the renderer synchronously.
|
||||
const sendMouseEvent = async (eventType, eventX, eventY) => {
|
||||
const jugglerEventId = win.windowUtils.jugglerSendMouseEvent(
|
||||
type,
|
||||
x + boundingBox.left,
|
||||
y + boundingBox.top,
|
||||
eventType,
|
||||
eventX + boundingBox.left,
|
||||
eventY + boundingBox.top,
|
||||
button,
|
||||
clickCount,
|
||||
modifiers,
|
||||
|
|
@ -519,9 +518,26 @@ class PageHandler {
|
|||
win.windowUtils.DEFAULT_MOUSE_POINTER_ID /* pointerIdentifier */,
|
||||
false /* disablePointerEvent */
|
||||
);
|
||||
promises.push(watcher.ensureEvent(type, eventObject => eventObject.jugglerEventId === jugglerEventId));
|
||||
await watcher.ensureEvent(eventType, eventObject => eventObject.jugglerEventId === jugglerEventId);
|
||||
};
|
||||
for (const type of types) {
|
||||
if (type === 'mousemove' && ChromeUtils.camouGetBool('humanize', false)) {
|
||||
let trajectory = ChromeUtils.camouGetMouseTrajectory(this._lastTrackedPos.x, this._lastTrackedPos.y, x, y);
|
||||
for (let i = 2; i < trajectory.length - 2; i += 2) {
|
||||
let currentX = trajectory[i];
|
||||
let currentY = trajectory[i + 1];
|
||||
// Skip movement that is out of bounds
|
||||
if (currentX < 0 || currentY < 0 || currentX > boundingBox.width || currentY > boundingBox.height) {
|
||||
continue;
|
||||
}
|
||||
await sendMouseEvent(type, currentX, currentY);
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
}
|
||||
} else {
|
||||
// Call the function for the current event
|
||||
await sendMouseEvent(type, x, y);
|
||||
}
|
||||
}
|
||||
await Promise.all(promises);
|
||||
await watcher.dispose();
|
||||
};
|
||||
|
||||
|
|
@ -579,6 +595,7 @@ class PageHandler {
|
|||
|
||||
const watcher = new EventWatcher(this._pageEventSink, ['dragstart', 'juggler-drag-finalized'], this._pendingEventWatchers);
|
||||
await sendEvents(['mousemove']);
|
||||
this._lastTrackedPos = { x, y };
|
||||
|
||||
// The order of events after 'mousemove' is sent:
|
||||
// 1. [dragstart] - might or might NOT be emitted
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp
|
||||
index 6833d2227f..0b2ea99615 100644
|
||||
index 6833d2227f..9f88bd3d34 100644
|
||||
--- a/dom/base/ChromeUtils.cpp
|
||||
+++ b/dom/base/ChromeUtils.cpp
|
||||
@@ -5,6 +5,7 @@
|
||||
@@ -5,6 +5,8 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ChromeUtils.h"
|
||||
+#include "MaskConfig.hpp"
|
||||
+#include "MouseTrajectories.hpp"
|
||||
|
||||
#include "JSOracleParent.h"
|
||||
#include "js/CallAndConstruct.h" // JS::Call
|
||||
@@ -2068,6 +2069,24 @@ bool ChromeUtils::IsDarkBackground(GlobalObject&, Element& aElement) {
|
||||
@@ -2068,6 +2070,24 @@ bool ChromeUtils::IsDarkBackground(GlobalObject&, Element& aElement) {
|
||||
return nsNativeTheme::IsDarkBackground(f);
|
||||
}
|
||||
|
||||
|
|
@ -35,7 +36,7 @@ index 6833d2227f..0b2ea99615 100644
|
|||
double ChromeUtils::DateNow(GlobalObject&) { return JS_Now() / 1000.0; }
|
||||
|
||||
/* static */
|
||||
@@ -2094,6 +2113,39 @@ void ChromeUtils::GetAllPossibleUtilityActorNames(GlobalObject& aGlobal,
|
||||
@@ -2094,6 +2114,62 @@ void ChromeUtils::GetAllPossibleUtilityActorNames(GlobalObject& aGlobal,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -61,6 +62,17 @@ index 6833d2227f..0b2ea99615 100644
|
|||
+}
|
||||
+
|
||||
+/* static */
|
||||
+bool ChromeUtils::CamouGetBool(GlobalObject& aGlobal,
|
||||
+ const nsAString& aVarName,
|
||||
+ bool aDefaultValue) {
|
||||
+ NS_ConvertUTF16toUTF8 utf8VarName(aVarName);
|
||||
+ if (auto value = MaskConfig::GetBool(utf8VarName.get())) {
|
||||
+ return value.value();
|
||||
+ }
|
||||
+ return aDefaultValue;
|
||||
+}
|
||||
+
|
||||
+/* static */
|
||||
+void ChromeUtils::CamouGetString(GlobalObject& aGlobal,
|
||||
+ const nsAString& aVarName,
|
||||
+ nsAString& aRetVal) {
|
||||
|
|
@ -71,12 +83,24 @@ index 6833d2227f..0b2ea99615 100644
|
|||
+ aRetVal.Truncate();
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/* static */
|
||||
+void ChromeUtils::CamouGetMouseTrajectory(GlobalObject& aGlobal, long aFromX,
|
||||
+ long aFromY, long aToX, long aToY,
|
||||
+ nsTArray<int32_t>& aPoints) {
|
||||
+ HumanizeMouseTrajectory trajectory(std::make_pair(aFromX, aFromY),
|
||||
+ std::make_pair(aToX, aToY));
|
||||
+ std::vector<int> flattenedPoints = trajectory.getPoints();
|
||||
+
|
||||
+ aPoints.Clear();
|
||||
+ aPoints.AppendElements(flattenedPoints.data(), flattenedPoints.size());
|
||||
+}
|
||||
+
|
||||
/* static */
|
||||
bool ChromeUtils::ShouldResistFingerprinting(
|
||||
GlobalObject& aGlobal, JSRFPTarget aTarget,
|
||||
diff --git a/dom/base/ChromeUtils.h b/dom/base/ChromeUtils.h
|
||||
index 0150c59670..e3d203ed1a 100644
|
||||
index 0150c59670..3a244a80e4 100644
|
||||
--- a/dom/base/ChromeUtils.h
|
||||
+++ b/dom/base/ChromeUtils.h
|
||||
@@ -301,6 +301,10 @@ class ChromeUtils {
|
||||
|
|
@ -90,7 +114,7 @@ index 0150c59670..e3d203ed1a 100644
|
|||
static double DateNow(GlobalObject&);
|
||||
|
||||
static void EnsureJSOracleStarted(GlobalObject&);
|
||||
@@ -310,6 +314,14 @@ class ChromeUtils {
|
||||
@@ -310,6 +314,21 @@ class ChromeUtils {
|
||||
static void GetAllPossibleUtilityActorNames(GlobalObject& aGlobal,
|
||||
nsTArray<nsCString>& aNames);
|
||||
|
||||
|
|
@ -99,14 +123,21 @@ index 0150c59670..e3d203ed1a 100644
|
|||
+ static double CamouGetDouble(GlobalObject& aGlobal, const nsAString& aVarName,
|
||||
+ double aDefaultValue);
|
||||
+
|
||||
+ static bool CamouGetBool(GlobalObject& aGlobal, const nsAString& aVarName,
|
||||
+ bool aDefaultValue);
|
||||
+
|
||||
+ static void CamouGetString(GlobalObject& aGlobal, const nsAString& aVarName,
|
||||
+ nsAString& aRetVal);
|
||||
+
|
||||
+ static void CamouGetMouseTrajectory(GlobalObject& aGlobal, long aFromX,
|
||||
+ long aFromY, long aToX, long aToY,
|
||||
+ nsTArray<int32_t>& aPoints);
|
||||
+
|
||||
static bool ShouldResistFingerprinting(
|
||||
GlobalObject& aGlobal, JSRFPTarget aTarget,
|
||||
const Nullable<uint64_t>& aOverriddenFingerprintingSettings);
|
||||
diff --git a/dom/chrome-webidl/ChromeUtils.webidl b/dom/chrome-webidl/ChromeUtils.webidl
|
||||
index bf196f039d..04e0cdcabd 100644
|
||||
index bf196f039d..e134d816b3 100644
|
||||
--- a/dom/chrome-webidl/ChromeUtils.webidl
|
||||
+++ b/dom/chrome-webidl/ChromeUtils.webidl
|
||||
@@ -746,6 +746,13 @@ partial namespace ChromeUtils {
|
||||
|
|
@ -123,7 +154,7 @@ index bf196f039d..04e0cdcabd 100644
|
|||
/**
|
||||
* Starts the JSOracle process for ORB JavaScript validation, if it hasn't started already.
|
||||
*/
|
||||
@@ -757,6 +764,21 @@ partial namespace ChromeUtils {
|
||||
@@ -757,6 +764,31 @@ partial namespace ChromeUtils {
|
||||
[ChromeOnly]
|
||||
readonly attribute unsigned long aliveUtilityProcesses;
|
||||
|
||||
|
|
@ -138,9 +169,19 @@ index bf196f039d..04e0cdcabd 100644
|
|||
+ double camouGetDouble(DOMString varName, double defaultValue);
|
||||
+
|
||||
+ /**
|
||||
+ * Get a bool value from Camoufox MaskConfig.
|
||||
+ */
|
||||
+ boolean camouGetBool(DOMString varName, boolean defaultValue);
|
||||
+
|
||||
+ /**
|
||||
+ * Get a string value from Camoufox MaskConfig.
|
||||
+ */
|
||||
+ DOMString camouGetString(DOMString varName);
|
||||
+
|
||||
+ /**
|
||||
+ * Calculate a human-like mouse trajectory between two points.
|
||||
+ */
|
||||
+ sequence<long> camouGetMouseTrajectory(long fromX, long fromY, long toX, long toY);
|
||||
+
|
||||
/**
|
||||
* Get a list of all possible Utility process Actor Names ; mostly useful to
|
||||
|
|
|
|||
36
patches/cursor-highlighter.patch
Normal file
36
patches/cursor-highlighter.patch
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
diff --git a/browser/base/content/browser-init.js b/browser/base/content/browser-init.js
|
||||
index 63c9c39741..cc7fa914c0 100644
|
||||
--- a/browser/base/content/browser-init.js
|
||||
+++ b/browser/base/content/browser-init.js
|
||||
@@ -292,6 +292,31 @@ var gBrowserInit = {
|
||||
}
|
||||
}
|
||||
|
||||
+ if (ChromeUtils.camouGetBool("showcursor", true)) {
|
||||
+ let cursorFollower = document.createElement("div");
|
||||
+ cursorFollower.id = "cursor-highlighter";
|
||||
+ cursorFollower.style.cssText = `
|
||||
+ position: fixed;
|
||||
+ width: 10px;
|
||||
+ height: 10px;
|
||||
+ background-color: rgba(255,105,105,0.8);
|
||||
+ border-radius: 50%;
|
||||
+ pointer-events: none;
|
||||
+ z-index: 2147483647;
|
||||
+ transform: translate(-50%, -50%);
|
||||
+ box-shadow:
|
||||
+ 0 0 0 5px rgba(255,105,105,0.5),
|
||||
+ 0 0 0 10px rgba(255,105,105,0.3),
|
||||
+ 0 0 0 15px rgba(255,105,105,0.1);
|
||||
+ `;
|
||||
+ document.documentElement.appendChild(cursorFollower);
|
||||
+
|
||||
+ window.addEventListener('mousemove', e => {
|
||||
+ cursorFollower.style.left = `${e.clientX}px`;
|
||||
+ cursorFollower.style.top = `${e.clientY}px`;
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
// Wait until chrome is painted before executing code not critical to making the window visible
|
||||
this._boundDelayedStartup = this._delayedStartup.bind(this);
|
||||
window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
|
||||
|
|
@ -61,5 +61,8 @@
|
|||
{ "property": "timezone", "type": "str" },
|
||||
{ "property": "locale:language", "type": "str" },
|
||||
{ "property": "locale:region", "type": "str" },
|
||||
{ "property": "locale:script", "type": "str" }
|
||||
{ "property": "locale:script", "type": "str" },
|
||||
{ "property": "humanize", "type": "bool" },
|
||||
{ "property": "humanize:maxTime", "type": "double" },
|
||||
{ "property": "showcursor", "type": "bool" }
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue