docs/patch-upgrading-guide.md
This guide provides step-by-step instructions for updating Camoufox patches when upgrading Firefox versions. Patches frequently break due to Firefox API changes, file reorganizations, and line number shifts.
All patches are located in the patches/ directory. There are no separate context patches to merge—per-context functionality is already built into the patches.
All Camoufox patches are in the patches/ directory:
0-playwright.patch, 1-leak-fixes.patch, etc.webrtc-ip-spoofing.patch, anti-font-fingerprinting.patch, etc.Historical Note: Context patches (e.g., font-fingerprinting.context.patch, webrtc.context.patch) were previously separate but have been merged into their base patches as of Firefox 146. You will not find .context.patch files in the repository.
IMPORTANT: Always use make clean to reset to fresh Firefox source:
make clean
DO NOT use git reset or git clean commands directly in the Firefox source directory - these can delete untracked files needed for the build.
Check which patches exist:
ls patches/*.patch
Some patches depend on others being applied first:
1-leak-fixes.patch requires 0-playwright.patchcd camoufox-<version>
patch -p1 < ../patches/patch-name.patch
Find reject files:
find . -name '*.rej' -type f
Read the reject file to understand what failed:
cat path/to/file.cpp.rej
Reject files show:
@@ lines: Line numbers where patch expected to apply- lines: What the patch expected to find (old code)+ lines: What the patch wanted to add (new code)The line numbers in rejects are usually wrong for the new Firefox version. You need to:
Use the Edit tool to apply the rejected changes to the correct location.
After fixing all rejects:
rm -f path/to/file.cpp.rej
find . -name '*.rej' -type f # Verify all removed
# Add any new files first
git add new/file.cpp new/file.h
# Generate patch with both staged and unstaged changes
git diff --cached --binary > /tmp/patch-name.patch
git diff --binary >> /tmp/patch-name.patch
# Copy to patches directory
cp /tmp/patch-name.patch ../patches/patch-name.patch
cd ..
make clean
cd camoufox-<version>
patch -p1 < ../patches/patch-name.patch
find . -name '*.rej' -type f # Should return nothing
Symptom: Reject shows failed #include additions
Example Reject:
@@ -325,6 +325,7 @@
#include "xpcpublic.h"
+#include "WebRTCIPManager.h"
#include "nsDocShell.h"
How to Fix:
xpcpublic.h)Example:
// Find this in the actual file:
#include "xpcpublic.h"
// Add the missing includes after it:
#include "xpcpublic.h"
#include "WebRTCIPManager.h"
#include "nsDocShell.h"
#include "mozilla/OriginAttributes.h"
Symptom: Reject shows function call with changed parameters
Example Reject:
- mouseOrPointerEvent.mButton = aButton;
+ mouseOrPointerEvent.mJugglerEventId = aMouseEventData.mJugglerEventId;
Common Causes:
How to Fix:
Example - Firefox 146 Mouse Event Refactoring:
Old Firefox 144 API (individual parameters):
void SynthesizeMouseEvent(int x, int y, int button, ...)
New Firefox 146 API (structured data):
void SynthesizeMouseEvent(SynthesizeMouseEventData& aData,
SynthesizeMouseEventOptions& aOptions)
Port the patch:
// Old patch code:
mouseEvent.mButton = aButton;
mouseEvent.jugglerEventId = aJugglerEventId;
// New patch code for Firefox 146:
mouseOrPointerEvent.mButton = aMouseEventData.mButton;
mouseOrPointerEvent.mJugglerEventId = aMouseEventData.mJugglerEventId;
mouseOrPointerEvent.convertToPointer = aOptions.mConvertToPointer;
Symptom: Reject shows context that doesn't exist in the file
How to Fix:
# Search across the codebase
grep -r "FunctionName" camoufox-<version>/ --include="*.cpp"
Symptom: Reject shows function call, but Firefox added/removed parameters
Example - MakeTextRun userContextId:
Old call:
MakeTextRun(text, len, drawTarget, appUnitsPerDevPixel, flags, recorder);
New Firefox expects:
MakeTextRun(text, len, drawTarget, appUnitsPerDevPixel, flags, recorder, userContextId);
How to Fix:
Standard userContextId Extraction Pattern:
uint32_t userContextId = 0;
if (mozilla::dom::Document* doc = presContext->Document()) {
if (nsIPrincipal* principal = doc->NodePrincipal()) {
auto* bp = mozilla::BasePrincipal::Cast(principal);
if (bp) {
userContextId = bp->OriginAttributesRef().mUserContextId;
}
}
}
// Now pass userContextId to the function
MakeTextRun(..., userContextId);
Symptom: Reject shows patch tried to apply at wrong line number, but code is identical
How to Fix:
Simply apply the patch manually at the correct line number. The code hasn't changed, just the location.
NOTE: As of Firefox 146, all context patches have been merged into their base patches. This section is kept for historical reference and understanding how the patches evolved. Future Firefox updates will only need to update patches in the patches/ directory.
Historical Context: Context patches previously added per-user-context functionality to base patches. The workflow was different from simple patch updates.
Merge all changes from *.context.patch into the corresponding base patch so there's only one comprehensive patch file.
Reset to clean Firefox:
make clean
Apply base patch first:
cd camoufox-<version>
patch -p1 < ../patches/anti-font-fingerprinting.patch
Apply context patch on top:
patch -p1 < ../font-fingerprinting.context.patch
Fix any rejects (usually include conflicts since base patch may have some overlapping changes)
Generate combined patch:
# Add new files (e.g., FontSpacingSeedManager.cpp/h)
git add dom/base/FontSpacingSeedManager.cpp
git add dom/base/FontSpacingSeedManager.h
git add dom/base/RoverfoxStorageManager.cpp
git add dom/base/RoverfoxStorageManager.h
# Generate combined patch
git diff --cached --binary > /tmp/anti-font-fingerprinting.patch
git diff --binary >> /tmp/anti-font-fingerprinting.patch
# Replace base patch
cp /tmp/anti-font-fingerprinting.patch ../patches/anti-font-fingerprinting.patch
Verify combined patch:
cd ..
make clean
cd camoufox-<version>
patch -p1 < ../patches/anti-font-fingerprinting.patch
find . -name '*.rej' -type f # Should be empty
Context patches typically add:
Manager classes (e.g., FontSpacingSeedManager, WebRTCIPManager):
Window.webidl functions:
setFontSpacingSeed(), setWebRTCIPv4()nsGlobalWindowInner.cpp implementations:
Core logic changes:
After updating a patch, always verify:
Patch applies cleanly:
make clean
cd camoufox-<version>
patch -p1 < ../patches/patch-name.patch
find . -name '*.rej' -type f
No reject files remain
Build compiles (if feasible):
cd ..
make build
For critical patches, test with actual Playwright scenarios after building.
make clean to reset Firefox sourcegit reset or git clean on Firefox source directorygit add before generating patchmake clean.rej files after fixingHere's a complete example of updating 0-playwright.patch from Firefox 144 to Firefox 146:
make clean
cd camoufox-146.0.1-beta.25
patch -p1 < ../patches/0-playwright.patch
find . -name '*.rej' -type f
Output shows 20 reject files.
cat dom/base/Navigator.cpp.rej
Shows parameter order changed in GetAcceptLanguages.
Search for the function in the actual file, understand the new signature, apply changes manually.
Work through each reject systematically.
Firefox 146 refactored mouse events from individual parameters to SynthesizeMouseEventData and SynthesizeMouseEventOptions. Port all mouse event logic to new API.
rm -f dom/base/Navigator.cpp.rej dom/base/Element.cpp.rej ...
find . -name '*.rej' -type f # Verify empty
git diff --binary > /tmp/0-playwright.patch
cp /tmp/0-playwright.patch ../patches/0-playwright.patch
cd ..
make clean
cd camoufox-146.0.1-beta.25
patch -p1 < ../patches/0-playwright.patch
find . -name '*.rej' -type f # Should be empty
# Find files by name
find . -name "Navigator.cpp" -type f
# Find files containing a symbol
grep -r "GetAcceptLanguages" . --include="*.cpp"
# Find class definitions
grep -r "class Navigator" . --include="*.h"
dom/: DOM implementation
dom/base/: Core DOM classes (Window, Document, Navigator, etc.)dom/webidl/: WebIDL interface definitionsdom/media/webrtc/: WebRTC implementationgfx/: Graphics and font rendering
gfx/thebes/: Text rendering (fonts, glyphs, shaping)layout/: Layout engine
layout/generic/: Text frameslayout/mathml/: MathML renderingUser Context ID Extraction:
uint32_t userContextId = 0;
if (Document* doc = GetDocument()) {
if (nsIPrincipal* principal = doc->NodePrincipal()) {
auto* bp = mozilla::BasePrincipal::Cast(principal);
if (bp) {
userContextId = bp->OriginAttributesRef().mUserContextId;
}
}
}
Three-tier userContextId fallback (for WebIDL functions):
// 1) Document's principal (preferred)
if (Document* doc = win->GetDoc()) {
if (nsIPrincipal* p = doc->NodePrincipal()) {
userContextId = p->OriginAttributesRef().mUserContextId;
}
}
// 2) DocShell origin attributes
if (userContextId == 0) {
if (nsIDocShell* ds = win->GetDocShell()) {
auto* concrete = static_cast<nsDocShell*>(ds);
userContextId = concrete->GetOriginAttributes().mUserContextId;
}
}
// 3) Top browsing context
if (userContextId == 0) {
if (BrowsingContext* bc = win->GetBrowsingContext()) {
RefPtr<BrowsingContext> top = bc->Top();
// ... extract from top window
}
}
When updating patches for a new Firefox version:
make clean to reset to fresh Firefox source.rej files after fixinggit addgit diff --cached --binary + git diff --binaryLast Updated: December 2025 (Firefox 146 upgrade)