From 02bc15161a38676e6e85be771e09b91ee5b05c8b Mon Sep 17 00:00:00 2001 From: daijro Date: Mon, 14 Oct 2024 02:12:13 -0500 Subject: [PATCH] feat: WebGL fingerprint spoofing Experimental WebGL fingerprint injection. - Allows the ability to set all WebGL parameters, supported extension list, shader precision formats, & context attributes. - Added a demo website under scripts/examples/webgl.html that can be used to generate Camoufox config data --- Makefile | 2 +- additions/camoucfg/MaskConfig.hpp | 97 +++++++- patches/fingerprint-injection.patch | 46 ---- patches/webgl-spoofing.patch | 360 ++++++++++++++++++++++++++++ scripts/examples/buttonclick.html | 63 +++++ scripts/examples/serve.sh | 1 + scripts/examples/webgl.html | 345 ++++++++++++++++++++++++++ settings/camoufox.cfg | 3 + settings/properties.json | 19 +- upstream.sh | 2 +- 10 files changed, 880 insertions(+), 58 deletions(-) create mode 100644 patches/webgl-spoofing.patch create mode 100644 scripts/examples/buttonclick.html create mode 100644 scripts/examples/serve.sh create mode 100644 scripts/examples/webgl.html diff --git a/Makefile b/Makefile index 28c1459..2842241 100644 --- a/Makefile +++ b/Makefile @@ -109,7 +109,7 @@ build: @if [ ! -f $(cf_source_dir)/_READY ]; then \ make dir; \ fi - cd $(cf_source_dir) && ./mach build + cd $(cf_source_dir) && ./mach build $(_ARGS) edits: python ./scripts/developer.py $(version) $(release) diff --git a/additions/camoucfg/MaskConfig.hpp b/additions/camoucfg/MaskConfig.hpp index 2e9bed0..3e63b43 100644 --- a/additions/camoucfg/MaskConfig.hpp +++ b/additions/camoucfg/MaskConfig.hpp @@ -14,6 +14,7 @@ Written by daijro. #include "mozilla/glue/Debug.h" #include #include +#include #ifdef _WIN32 # include @@ -76,11 +77,9 @@ inline const nlohmann::json& GetJson() { } inline bool HasKey(const std::string& key, nlohmann::json& data) { - if (!data.contains(key)) { - // printf_stderr("WARNING: Key not found: %s\n", key.c_str()); - return false; - } - return true; + // printf_stderr("Property: %s\n", key.c_str()); + // printf_stderr("WARNING: Key not found: %s\n", key.c_str()); + return data.contains(key); } inline std::optional GetString(const std::string& key) { @@ -163,8 +162,8 @@ inline std::optional> GetRect( const std::string& height) { // Make top and left default to 0 std::array, 4> values = { - GetUint32(left).value_or(0), GetUint32(top).value_or(0), - GetUint32(width), GetUint32(height)}; + GetUint32(left).value_or(0), GetUint32(top).value_or(0), GetUint32(width), + GetUint32(height)}; // If height or width is std::nullopt, return std::nullopt if (!values[2].has_value() || !values[3].has_value()) { @@ -197,4 +196,88 @@ inline std::optional> GetInt32Rect( return std::nullopt; } +// Helpers for WebGL + +inline std::optional GetNested(const std::string& domain, + std::string keyStr) { + auto data = GetJson(); + if (!data.contains(domain)) return std::nullopt; + + if (!data[domain].contains(keyStr)) return std::nullopt; + + return data[domain][keyStr]; +} + +template +inline std::optional GetAttribute(const std::string attrib, bool isWebGL2) { + auto value = MaskConfig::GetNested( + isWebGL2 ? "webgl2:contextAttributes" : "webgl:contextAttributes", + attrib); + if (!value) return std::nullopt; + return value.value().get(); +} + +inline std::optional> GLParam( + uint32_t pname, bool isWebGL2) { + auto value = + MaskConfig::GetNested(isWebGL2 ? "webgl2:parameters" : "webgl:parameters", + std::to_string(pname)); + if (!value) return std::nullopt; + auto data = value.value(); + // cast the data and return + if (data.is_number_integer()) return data.get(); + if (data.is_boolean()) return data.get(); + if (data.is_number_float()) return data.get(); + if (data.is_string()) return data.get(); + return std::nullopt; +} + +template +inline T MParamGL(uint32_t pname, T defaultValue, bool isWebGL2) { + if (auto value = MaskConfig::GetNested( + isWebGL2 ? "webgl2:parameters" : "webgl:parameters", + std::to_string(pname)); + value.has_value()) { + return value.value().get(); + } + return defaultValue; +} + +template +inline std::vector MParamGLVector(uint32_t pname, + std::vector defaultValue, + bool isWebGL2) { + if (auto value = MaskConfig::GetNested( + isWebGL2 ? "webgl2:parameters" : "webgl:parameters", + std::to_string(pname)); value.has_value()) { + if (value.value().is_array()) { + std::array result = value.value().get>(); + return std::vector(result.begin(), result.end()); + } + } + return defaultValue; +} + +inline std::optional> MShaderData( + uint32_t shaderType, uint32_t precisionType, bool isWebGL2) { + std::string valueName = + std::to_string(shaderType) + "," + std::to_string(precisionType); + if (auto value = + MaskConfig::GetNested(isWebGL2 ? "webgl2:shaderPrecisionFormats" + : "webgl:shaderPrecisionFormats", + valueName)) { + // Convert {rangeMin: int, rangeMax: int, precision: int} to array + auto data = value.value(); + // Assert rangeMin, rangeMax, and precision are present + if (!data.contains("rangeMin") || !data.contains("rangeMax") || + !data.contains("precision")) { + return std::nullopt; + } + return std::array{data["rangeMin"].get(), + data["rangeMax"].get(), + data["precision"].get()}; + } + return std::nullopt; +} + } // namespace MaskConfig \ No newline at end of file diff --git a/patches/fingerprint-injection.patch b/patches/fingerprint-injection.patch index ae44b5d..9569e9c 100644 --- a/patches/fingerprint-injection.patch +++ b/patches/fingerprint-injection.patch @@ -463,52 +463,6 @@ index 3a90c93c01..91d673039b 100644 +# DOM Mask +LOCAL_INCLUDES += ["/camoucfg"] \ No newline at end of file -diff --git a/dom/canvas/ClientWebGLContext.cpp b/dom/canvas/ClientWebGLContext.cpp -index 4e43425ef9..26bbfdc868 100644 ---- a/dom/canvas/ClientWebGLContext.cpp -+++ b/dom/canvas/ClientWebGLContext.cpp -@@ -4,6 +4,7 @@ - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - #include "ClientWebGLContext.h" -+#include "MaskConfig.hpp" - - #include - -@@ -2399,6 +2400,10 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, - - switch (pname) { - case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_RENDERER_WEBGL: -+ if (auto value = MaskConfig::GetString("webGl:renderer")) { -+ ret = Some(value.value()); -+ break; -+ } - ret = GetUnmaskedRenderer(); - if (ret && StaticPrefs::webgl_sanitize_unmasked_renderer()) { - *ret = webgl::SanitizeRenderer(*ret); -@@ -2406,6 +2411,10 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, - break; - - case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_VENDOR_WEBGL: -+ if (auto value = MaskConfig::GetString("webGl:vendor")) { -+ ret = Some(value.value()); -+ break; -+ } - ret = GetUnmaskedVendor(); - break; - -diff --git a/dom/canvas/moz.build b/dom/canvas/moz.build -index 3a533d36d1..41d74d9b3f 100644 ---- a/dom/canvas/moz.build -+++ b/dom/canvas/moz.build -@@ -221,3 +221,6 @@ if CONFIG["CC_TYPE"] == "gcc": - - # Add libFuzzer configuration directives - include("/tools/fuzzing/libfuzzer-config.mozbuild") -+ -+# DOM Mask -+LOCAL_INCLUDES += ["/camoucfg"] -\ No newline at end of file diff --git a/dom/workers/WorkerNavigator.cpp b/dom/workers/WorkerNavigator.cpp index b05e409a84..8e0f7ea657 100644 --- a/dom/workers/WorkerNavigator.cpp diff --git a/patches/webgl-spoofing.patch b/patches/webgl-spoofing.patch new file mode 100644 index 0000000..a59445e --- /dev/null +++ b/patches/webgl-spoofing.patch @@ -0,0 +1,360 @@ +diff --git a/dom/canvas/ClientWebGLContext.cpp b/dom/canvas/ClientWebGLContext.cpp +index db60868f65..afed6eeb7c 100644 +--- a/dom/canvas/ClientWebGLContext.cpp ++++ b/dom/canvas/ClientWebGLContext.cpp +@@ -4,6 +4,8 @@ + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + #include "ClientWebGLContext.h" ++#include "MaskConfig.hpp" ++#include + + #include + +@@ -744,6 +746,13 @@ void ClientWebGLContext::SetDrawingBufferColorSpace( + Run(*mDrawingBufferColorSpace); + } + ++bool ClientWebGLContext::MBoolVal(const std::string& key, bool defaultValue) { ++ if (auto value = MaskConfig::GetAttribute(key, mIsWebGL2); ++ value.has_value()) ++ return value.value(); ++ return defaultValue; ++} ++ + void ClientWebGLContext::GetContextAttributes( + dom::Nullable& retval) { + retval.SetNull(); +@@ -754,14 +763,38 @@ void ClientWebGLContext::GetContextAttributes( + + const auto& options = mNotLost->info.options; + +- result.mAlpha.Construct(options.alpha); +- result.mDepth = options.depth; +- result.mStencil = options.stencil; +- result.mAntialias.Construct(options.antialias); +- result.mPremultipliedAlpha = options.premultipliedAlpha; +- result.mPreserveDrawingBuffer = options.preserveDrawingBuffer; +- result.mFailIfMajorPerformanceCaveat = options.failIfMajorPerformanceCaveat; +- result.mPowerPreference = options.powerPreference; ++ result.mAlpha.Construct(MBoolVal("alpha", options.alpha)); ++ result.mDepth = MBoolVal("depth", options.depth); ++ result.mStencil = MBoolVal("stencil", options.stencil); ++ result.mAntialias.Construct(MBoolVal("antialias", options.antialias)); ++ result.mPremultipliedAlpha = MBoolVal( ++ "webgl:contextAttributes.premultipliedAlpha", options.premultipliedAlpha); ++ result.mPreserveDrawingBuffer = ++ MBoolVal("preserveDrawingBuffer", options.preserveDrawingBuffer); ++ result.mFailIfMajorPerformanceCaveat = MBoolVal( ++ "failIfMajorPerformanceCaveat", options.failIfMajorPerformanceCaveat); ++ if (auto value = ++ MaskConfig::GetAttribute("powerPreference", mIsWebGL2); ++ value.has_value()) { ++ // Convert to enum ++ switch (value.value()[0]) { ++ case 'd': ++ result.mPowerPreference = dom::WebGLPowerPreference::Default; ++ break; ++ case 'l': ++ result.mPowerPreference = dom::WebGLPowerPreference::Low_power; ++ break; ++ case 'h': ++ result.mPowerPreference = dom::WebGLPowerPreference::High_performance; ++ break; ++ default: ++ // Invalid value ++ result.mPowerPreference = options.powerPreference; ++ break; ++ } ++ } else { ++ result.mPowerPreference = options.powerPreference; ++ } + } + + // ----------------------- +@@ -979,18 +1012,28 @@ bool ClientWebGLContext::CreateHostContext(const uvec2& requestedSize) { + std::unordered_map webgl::MakeIsEnabledMap(const bool webgl2) { + auto ret = std::unordered_map{}; + +- ret[LOCAL_GL_BLEND] = false; +- ret[LOCAL_GL_CULL_FACE] = false; +- ret[LOCAL_GL_DEPTH_TEST] = false; +- ret[LOCAL_GL_DITHER] = true; +- ret[LOCAL_GL_POLYGON_OFFSET_FILL] = false; +- ret[LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE] = false; +- ret[LOCAL_GL_SAMPLE_COVERAGE] = false; +- ret[LOCAL_GL_SCISSOR_TEST] = false; +- ret[LOCAL_GL_STENCIL_TEST] = false; ++ ret[LOCAL_GL_BLEND] = ++ MaskConfig::MParamGL(LOCAL_GL_BLEND, false, webgl2); ++ ret[LOCAL_GL_CULL_FACE] = ++ MaskConfig::MParamGL(LOCAL_GL_CULL_FACE, false, webgl2); ++ ret[LOCAL_GL_DEPTH_TEST] = ++ MaskConfig::MParamGL(LOCAL_GL_DEPTH_TEST, false, webgl2); ++ ret[LOCAL_GL_DITHER] = ++ MaskConfig::MParamGL(LOCAL_GL_DITHER, true, webgl2); ++ ret[LOCAL_GL_POLYGON_OFFSET_FILL] = ++ MaskConfig::MParamGL(LOCAL_GL_POLYGON_OFFSET_FILL, false, webgl2); ++ ret[LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE] = MaskConfig::MParamGL( ++ LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE, false, webgl2); ++ ret[LOCAL_GL_SAMPLE_COVERAGE] = ++ MaskConfig::MParamGL(LOCAL_GL_SAMPLE_COVERAGE, false, webgl2); ++ ret[LOCAL_GL_SCISSOR_TEST] = ++ MaskConfig::MParamGL(LOCAL_GL_SCISSOR_TEST, false, webgl2); ++ ret[LOCAL_GL_STENCIL_TEST] = ++ MaskConfig::MParamGL(LOCAL_GL_STENCIL_TEST, false, webgl2); + + if (webgl2) { +- ret[LOCAL_GL_RASTERIZER_DISCARD] = false; ++ ret[LOCAL_GL_RASTERIZER_DISCARD] = ++ MaskConfig::MParamGL(LOCAL_GL_RASTERIZER_DISCARD, false, webgl2); + } + + return ret; +@@ -2058,6 +2101,28 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, + const auto& state = State(); + + // - ++ std::optional> data; ++ data = MaskConfig::GLParam(pname, mIsWebGL2); ++ ++ if (data.has_value()) { ++ const auto& value = data.value(); ++ if (std::holds_alternative(value)) { ++ retval.set(JS::NumberValue(double(std::get(value)))); ++ return; ++ } ++ if (std::holds_alternative(value)) { ++ retval.set(JS::NumberValue(std::get(value))); ++ return; ++ } ++ if (std::holds_alternative(value)) { ++ retval.set(JS::BooleanValue(std::get(value))); ++ return; ++ } ++ if (std::holds_alternative(value)) { ++ retval.set(StringValue(cx, std::get(value), rv)); ++ return; ++ } ++ } + + const auto fnSetRetval_Buffer = [&](const GLenum target) { + const auto buffer = *MaybeFind(state.mBoundBufferByTarget, target); +@@ -2163,49 +2228,84 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, + + // 2 floats + case LOCAL_GL_DEPTH_RANGE: +- retval.set(Create(cx, this, state.mDepthRange, rv)); ++ retval.set(Create( ++ cx, this, ++ MaskConfig::MParamGL>(pname, state.mDepthRange, ++ mIsWebGL2), ++ rv)); + return; + + case LOCAL_GL_ALIASED_POINT_SIZE_RANGE: +- retval.set( +- Create(cx, this, limits.pointSizeRange, rv)); ++ retval.set(Create( ++ cx, this, ++ MaskConfig::MParamGL>( ++ pname, limits.pointSizeRange, mIsWebGL2), ++ rv)); + return; + + case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE: +- retval.set( +- Create(cx, this, limits.lineWidthRange, rv)); ++ retval.set(Create( ++ cx, this, ++ MaskConfig::MParamGL>( ++ pname, limits.lineWidthRange, mIsWebGL2), ++ rv)); + return; + + // 4 floats + case LOCAL_GL_COLOR_CLEAR_VALUE: +- retval.set(Create(cx, this, state.mClearColor, rv)); ++ retval.set(Create( ++ cx, this, ++ MaskConfig::MParamGL>(pname, state.mClearColor, ++ mIsWebGL2), ++ rv)); + return; + + case LOCAL_GL_BLEND_COLOR: +- retval.set(Create(cx, this, state.mBlendColor, rv)); ++ retval.set(Create( ++ cx, this, ++ MaskConfig::MParamGL>(pname, state.mBlendColor, ++ mIsWebGL2), ++ rv)); + return; + + // 2 ints + case LOCAL_GL_MAX_VIEWPORT_DIMS: { + auto maxViewportDim = BitwiseCast(limits.maxViewportDim); + const auto dims = std::array{maxViewportDim, maxViewportDim}; +- retval.set(Create(cx, this, dims, rv)); ++ retval.set(Create( ++ cx, this, ++ MaskConfig::MParamGL>(pname, dims, ++ mIsWebGL2), ++ rv)); + return; + } + + // 4 ints + case LOCAL_GL_SCISSOR_BOX: +- retval.set(Create(cx, this, state.mScissor, rv)); ++ retval.set(Create( ++ cx, this, ++ MaskConfig::MParamGL>(pname, state.mScissor, ++ mIsWebGL2), ++ rv)); + return; + + case LOCAL_GL_VIEWPORT: +- retval.set(Create(cx, this, state.mViewport, rv)); ++ retval.set(Create( ++ cx, this, ++ MaskConfig::MParamGL>(pname, state.mViewport, ++ mIsWebGL2), ++ rv)); + return; + +- // any + case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS: +- retval.set(Create(cx, this, +- state.mCompressedTextureFormats, rv)); ++ std::vector compressedTextureUint32( ++ state.mCompressedTextureFormats.begin(), ++ state.mCompressedTextureFormats.end()); ++ retval.set(Create( ++ cx, this, ++ MaskConfig::MParamGLVector(pname, compressedTextureUint32, ++ mIsWebGL2), ++ rv)); + return; + } + +@@ -2385,6 +2485,10 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, + + switch (pname) { + case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_RENDERER_WEBGL: ++ if (auto value = MaskConfig::GetString("webGl:renderer")) { ++ ret = Some(value.value()); ++ break; ++ } + ret = GetUnmaskedRenderer(); + if (ret && StaticPrefs::webgl_sanitize_unmasked_renderer()) { + *ret = webgl::SanitizeRenderer(*ret); +@@ -2392,6 +2496,10 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, + break; + + case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_VENDOR_WEBGL: ++ if (auto value = MaskConfig::GetString("webGl:vendor")) { ++ ret = Some(value.value()); ++ break; ++ } + ret = GetUnmaskedVendor(); + break; + +@@ -2482,7 +2590,9 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname, + case LOCAL_GL_COLOR_WRITEMASK: { + const auto mask = uint8_t(*maybe); + const auto bs = std::bitset<4>(mask); +- const auto src = std::array{bs[0], bs[1], bs[2], bs[3]}; ++ const auto src = MaskConfig::MParamGL>( ++ pname, std::array{bs[0], bs[1], bs[2], bs[3]}, ++ mIsWebGL2); + JS::Rooted arr(cx); + if (!dom::ToJSValue(cx, src.data(), src.size(), &arr)) { + rv = NS_ERROR_OUT_OF_MEMORY; +@@ -2865,6 +2975,24 @@ ClientWebGLContext::GetShaderPrecisionFormat(const GLenum shadertype, + const GLenum precisiontype) { + if (IsContextLost()) return nullptr; + const auto info = [&]() { ++ // Check for spoofed value ++ if (auto value = ++ MaskConfig::MShaderData(shadertype, precisiontype, mIsWebGL2)) { ++ const auto& format = value.value(); ++ return Some(webgl::ShaderPrecisionFormat{ ++ format[0], // rangeMin ++ format[1], // rangeMax ++ format[2] // precision ++ }); ++ } ++ // Check if block if not defined is on ++ if (MaskConfig::GetBool( ++ mIsWebGL2 ? "webgl2:shaderPrecisionFormats:blockIfNotDefined" ++ : "webgl:shaderPrecisionFormats:blockIfNotDefined")) { ++ Maybe ret; ++ return ret; ++ } ++ + const auto& inProcess = mNotLost->inProcess; + if (inProcess) { + return inProcess->GetShaderPrecisionFormat(shadertype, precisiontype); +@@ -5822,6 +5950,17 @@ bool ClientWebGLContext::IsSupported(const WebGLExtensionID ext, + return false; + } + ++ if (std::vector maskValues = ++ MaskConfig::GetStringList(mIsWebGL2 ? "webgl2:supportedExtensions" ++ : "webgl:supportedExtensions"); ++ !maskValues.empty()) { ++ if (std::find(maskValues.begin(), maskValues.end(), ++ GetExtensionName(ext)) != maskValues.end()) { ++ return true; ++ } ++ return false; ++ } ++ + const auto& limits = Limits(); + return limits.supportedExtensions[ext]; + } +@@ -5833,6 +5972,17 @@ void ClientWebGLContext::GetSupportedExtensions( + if (!mNotLost) return; + + auto& retarr = retval.SetValue(); ++ ++ // Implement separately to prevent O(n^2) timing ++ if (std::vector maskValues = ++ MaskConfig::GetStringList("webgl:supportedExtensions"); ++ !maskValues.empty()) { ++ for (const auto& ext : maskValues) { ++ retarr.AppendElement(NS_ConvertUTF8toUTF16(ext)); ++ } ++ return; ++ } ++ + for (const auto i : MakeEnumeratedRange(WebGLExtensionID::Max)) { + if (!IsSupported(i, callerType)) continue; + +diff --git a/dom/canvas/ClientWebGLContext.h b/dom/canvas/ClientWebGLContext.h +index 9e8b3c0f00..16e3fc7aa5 100644 +--- a/dom/canvas/ClientWebGLContext.h ++++ b/dom/canvas/ClientWebGLContext.h +@@ -1067,6 +1067,9 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal, + + // - + ++ // Helper to get booleans set in MaskConfig ++ bool MBoolVal(const std::string& key, bool defaultValue); ++ + void GetContextAttributes(dom::Nullable& retval); + + private: +diff --git a/dom/canvas/moz.build b/dom/canvas/moz.build +index 3a533d36d1..41d74d9b3f 100644 +--- a/dom/canvas/moz.build ++++ b/dom/canvas/moz.build +@@ -221,3 +221,6 @@ if CONFIG["CC_TYPE"] == "gcc": + + # Add libFuzzer configuration directives + include("/tools/fuzzing/libfuzzer-config.mozbuild") ++ ++# DOM Mask ++LOCAL_INCLUDES += ["/camoucfg"] +\ No newline at end of file diff --git a/scripts/examples/buttonclick.html b/scripts/examples/buttonclick.html new file mode 100644 index 0000000..d12c277 --- /dev/null +++ b/scripts/examples/buttonclick.html @@ -0,0 +1,63 @@ + + + + + + Random Button Clicker + + + + + + diff --git a/scripts/examples/serve.sh b/scripts/examples/serve.sh new file mode 100644 index 0000000..9fa39bb --- /dev/null +++ b/scripts/examples/serve.sh @@ -0,0 +1 @@ +python -m http.server diff --git a/scripts/examples/webgl.html b/scripts/examples/webgl.html new file mode 100644 index 0000000..ab609a5 --- /dev/null +++ b/scripts/examples/webgl.html @@ -0,0 +1,345 @@ + + + + + + WebGL Fingerprinting - Camoufox + + + +
+

Your WebGL Information (in Camoufox format)

+
Warning: This browser is not Firefox. The fingerprint below will not run properly on Camoufox.
+
+

+      
+ + +
+
+ + + + diff --git a/settings/camoufox.cfg b/settings/camoufox.cfg index 920dd0e..bf7d937 100644 --- a/settings/camoufox.cfg +++ b/settings/camoufox.cfg @@ -194,6 +194,9 @@ pref("layout.css.always_underline_links", true); // No WebGL pref("webgl.disabled", true); +// Force software rendering +pref("webgl.forbid-hardware", true); +pref("webgl.forbid-software", false); // Debloat pt.2 (from Librewolf) diff --git a/settings/properties.json b/settings/properties.json index cb5294d..b047081 100644 --- a/settings/properties.json +++ b/settings/properties.json @@ -48,8 +48,6 @@ { "property": "webrtc:ipv4", "type": "str" }, { "property": "webrtc:ipv6", "type": "str" }, { "property": "pdfViewerEnabled", "type": "bool" }, - { "property": "webGl:renderer", "type": "str" }, - { "property": "webGl:vendor", "type": "str" }, { "property": "battery:charging", "type": "bool" }, { "property": "battery:chargingTime", "type": "double" }, { "property": "battery:dischargingTime", "type": "double" }, @@ -64,6 +62,21 @@ { "property": "locale:script", "type": "str" }, { "property": "humanize", "type": "bool" }, { "property": "humanize:maxTime", "type": "double" }, + { "property": "humanize:minTime", "type": "double" }, { "property": "showcursor", "type": "bool" }, + { "property": "AudioContext:sampleRate", "type": "uint" }, + { "property": "AudioContext:outputLatency", "type": "double" }, + { "property": "AudioContext:maxChannelCount", "type": "uint" }, + { "property": "webGl:renderer", "type": "str" }, + { "property": "webGl:vendor", "type": "str" }, + { "property": "webgl:supportedExtensions", "type": "array" }, + { "property": "webgl:parameters", "type": "dict" }, + { "property": "webgl2:parameters", "type": "dict" }, + { "property": "webgl:shaderPrecisionFormats", "type": "dict" }, + { "property": "webgl:shaderPrecisionFormats:blockIfNotDefined", "type": "bool" }, + { "property": "webgl2:shaderPrecisionFormats", "type": "dict" }, + { "property": "webgl2:shaderPrecisionFormats:blockIfNotDefined", "type": "bool" }, + { "property": "webgl:contextAttributes", "type": "dict" }, + { "property": "webgl2:contextAttributes", "type": "dict" }, { "property": "debug", "type": "bool" } -] +] \ No newline at end of file diff --git a/upstream.sh b/upstream.sh index 685e602..5a232db 100644 --- a/upstream.sh +++ b/upstream.sh @@ -1,2 +1,2 @@ version=130.0.1 -release=beta.11 +release=beta.12