@@ -44,4 +44,5 @@ private
|
||||
closedsrc
|
||||
|
||||
.venv
|
||||
bundle
|
||||
bundle
|
||||
*.pem
|
||||
@@ -11,7 +11,7 @@ pacman := python python-pip p7zip go msitools wget aria2
|
||||
.PHONY: help fetch setup setup-minimal clean set-target distclean build package \
|
||||
build-launcher check-arch revert edits run bootstrap mozbootstrap dir \
|
||||
package-linux package-macos package-windows vcredist_arch patch unpatch \
|
||||
workspace check-arg edit-cfg ff-dbg tests update-ubo-assets
|
||||
workspace check-arg edit-cfg ff-dbg tests update-ubo-assets generate-assets-car
|
||||
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
@@ -260,6 +260,9 @@ path:
|
||||
update-ubo-assets:
|
||||
bash ./scripts/update-ubo-assets.sh
|
||||
|
||||
generate-assets-car:
|
||||
bash ./scripts/generate-assets-car.sh
|
||||
|
||||
upload:
|
||||
# ===============================
|
||||
# This is only for internal use. You can ignore this.
|
||||
|
||||
@@ -20,8 +20,13 @@ Camoufox is an open source anti-detect browser for robust fingerprint injection
|
||||
|
||||
Camoufox is the most modern, effective & future-proof open source solution for avoiding bot detection and intelligent fingerprint rotation. It outperforms most commercial anti-bot browsers.
|
||||
|
||||
## <span style="color:yellow">Firefox Version Upgrade Notice</span>
|
||||
The current main branch is built for Firefox v146. It is an experimental change and may contain several bugs. If you are building from source and require a stable production version, use branch `releases/135`.
|
||||
|
||||
---
|
||||
|
||||
## Sponsors
|
||||
|
||||
<a href="https://scrapfly.io/?utm_source=github&utm_medium=sponsoring&utm_campaign=camoufox" target="_blank">
|
||||
<img src="https://raw.githubusercontent.com/daijro/camoufox/main/assets/scrapfly.png" alt="Scrapfly.io" width="149">
|
||||
</a>
|
||||
@@ -30,6 +35,14 @@ Camoufox is the most modern, effective & future-proof open source solution for a
|
||||
|
||||
---
|
||||
|
||||
|
||||
<a href="https://cloverlabs.ai/?utm_source=github&utm_medium=sponsoring&utm_campaign=camoufox" target="_blank">
|
||||
<img src="https://i.imgur.com/I3oe7xG.jpeg" alt="cloverlabs.ai" width="149">
|
||||
</a>
|
||||
|
||||
[Clover Labs](https://cloverlabs.ai/?utm_source=github&utm_medium=sponsoring&utm_campaign=camoufox) is a Toronto based venture studio building AI agents for growth and distribution.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- Invisible to **all anti-bot systems** 🎭
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "icon_16x16.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_16x16@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_32x32.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_32x32@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_128x128.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_128x128@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_256x256.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_256x256@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_512x512.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_512x512@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 830 B |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 262 KiB |
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,10 @@
|
||||
# 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/.
|
||||
|
||||
# there is a possible patch to consider when changing this:
|
||||
# see: patches/browser-confvars.patch
|
||||
# Note: MOZ_APP_VENDOR and MOZ_APP_PROFILE must be set via imply_option() in browser/moz.configure
|
||||
# See patches/librewolf/disable-data-reporting-at-compile-time.patch
|
||||
|
||||
MOZ_APP_NAME=camoufox
|
||||
MOZ_APP_BASENAME=Camoufox
|
||||
MOZ_APP_PROFILE=camoufox
|
||||
MOZ_APP_VENDOR=Camoufox
|
||||
MOZ_APP_DISPLAYNAME=Camoufox
|
||||
MOZ_APP_REMOTINGNAME=camoufox
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/* 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/. */
|
||||
|
||||
import { ComponentUtils } from "resource://gre/modules/ComponentUtils.sys.mjs";
|
||||
|
||||
const Cm = Components.manager;
|
||||
|
||||
/**
|
||||
* This is a nsIChannelEventSink implementation that monitors channel redirects.
|
||||
* This has been forked from:
|
||||
* https://searchfox.org/mozilla-central/source/devtools/server/actors/network-monitor/channel-event-sink.js
|
||||
* The rest of this module is also more or less forking:
|
||||
* https://searchfox.org/mozilla-central/source/devtools/server/actors/network-monitor/network-observer.js
|
||||
* TODO(try to re-unify /remote/ with /devtools code)
|
||||
*/
|
||||
const SINK_CLASS_DESCRIPTION = "NetworkMonitor Channel Event Sink";
|
||||
const SINK_CLASS_ID = Components.ID("{c2b4c83e-607a-405a-beab-0ef5dbfb7617}");
|
||||
const SINK_CONTRACT_ID = "@mozilla.org/network/monitor/channeleventsink;1";
|
||||
const SINK_CATEGORY_NAME = "net-channel-event-sinks";
|
||||
|
||||
function ChannelEventSink() {
|
||||
this.wrappedJSObject = this;
|
||||
this.collectors = new Set();
|
||||
}
|
||||
|
||||
ChannelEventSink.prototype = {
|
||||
QueryInterface: ChromeUtils.generateQI(["nsIChannelEventSink"]),
|
||||
|
||||
registerCollector(collector) {
|
||||
this.collectors.add(collector);
|
||||
},
|
||||
|
||||
unregisterCollector(collector) {
|
||||
this.collectors.delete(collector);
|
||||
|
||||
if (this.collectors.size == 0) {
|
||||
ChannelEventSinkFactory.unregister();
|
||||
}
|
||||
},
|
||||
|
||||
asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) {
|
||||
for (const collector of this.collectors) {
|
||||
try {
|
||||
collector._onChannelRedirect(oldChannel, newChannel, flags);
|
||||
} catch (ex) {
|
||||
console.error(
|
||||
"StackTraceCollector.onChannelRedirect threw an exception",
|
||||
ex
|
||||
);
|
||||
}
|
||||
}
|
||||
callback.onRedirectVerifyCallback(Cr.NS_OK);
|
||||
},
|
||||
};
|
||||
|
||||
export const ChannelEventSinkFactory =
|
||||
ComponentUtils.generateSingletonFactory(ChannelEventSink);
|
||||
|
||||
ChannelEventSinkFactory.register = function () {
|
||||
const registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
if (registrar.isCIDRegistered(SINK_CLASS_ID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
registrar.registerFactory(
|
||||
SINK_CLASS_ID,
|
||||
SINK_CLASS_DESCRIPTION,
|
||||
SINK_CONTRACT_ID,
|
||||
ChannelEventSinkFactory
|
||||
);
|
||||
|
||||
Services.catMan.addCategoryEntry(
|
||||
SINK_CATEGORY_NAME,
|
||||
SINK_CONTRACT_ID,
|
||||
SINK_CONTRACT_ID,
|
||||
false,
|
||||
true
|
||||
);
|
||||
};
|
||||
|
||||
ChannelEventSinkFactory.unregister = function () {
|
||||
const registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
registrar.unregisterFactory(SINK_CLASS_ID, ChannelEventSinkFactory);
|
||||
|
||||
Services.catMan.deleteCategoryEntry(
|
||||
SINK_CATEGORY_NAME,
|
||||
SINK_CONTRACT_ID,
|
||||
false
|
||||
);
|
||||
};
|
||||
|
||||
ChannelEventSinkFactory.getService = function () {
|
||||
// Make sure the ChannelEventSink service is registered before accessing it
|
||||
ChannelEventSinkFactory.register();
|
||||
|
||||
return Cc[SINK_CONTRACT_ID].getService(Ci.nsIChannelEventSink)
|
||||
.wrappedJSObject;
|
||||
};
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
const uuidGen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
|
||||
|
||||
class Helper {
|
||||
export class Helper {
|
||||
decorateAsEventEmitter(objectToDecorate) {
|
||||
const { EventEmitter } = ChromeUtils.import('resource://gre/modules/EventEmitter.jsm');
|
||||
const { EventEmitter } = ChromeUtils.importESModule('resource://gre/modules/EventEmitter.sys.mjs');
|
||||
const emitter = new EventEmitter();
|
||||
objectToDecorate.on = emitter.on.bind(emitter);
|
||||
objectToDecorate.addEventListener = emitter.on.bind(emitter);
|
||||
@@ -172,7 +172,7 @@ class Helper {
|
||||
|
||||
const helper = new Helper();
|
||||
|
||||
class EventWatcher {
|
||||
export class EventWatcher {
|
||||
constructor(receiver, eventNames, pendingEventWatchers = new Set()) {
|
||||
this._pendingEventWatchers = pendingEventWatchers;
|
||||
this._pendingEventWatchers.add(this);
|
||||
@@ -233,7 +233,3 @@ class EventWatcher {
|
||||
}
|
||||
}
|
||||
|
||||
var EXPORTED_SYMBOLS = [ "Helper", "EventWatcher" ];
|
||||
this.Helper = Helper;
|
||||
this.EventWatcher = EventWatcher;
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
"use strict";
|
||||
|
||||
const { TargetRegistry } = ChromeUtils.import('chrome://juggler/content/TargetRegistry.js');
|
||||
const { Helper } = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const { TargetRegistry } = ChromeUtils.importESModule('chrome://juggler/content/TargetRegistry.js');
|
||||
const { Helper } = ChromeUtils.importESModule('chrome://juggler/content/Helper.js');
|
||||
|
||||
const helper = new Helper();
|
||||
|
||||
var EXPORTED_SYMBOLS = ['JugglerFrameParent'];
|
||||
|
||||
class JugglerFrameParent extends JSWindowActorParent {
|
||||
export class JugglerFrameParent extends JSWindowActorParent {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
@@ -18,7 +16,7 @@ class JugglerFrameParent extends JSWindowActorParent {
|
||||
// Actors are registered per the WindowGlobalParent / WindowGlobalChild pair. We are only
|
||||
// interested in those WindowGlobalParent actors that are matching current browsingContext
|
||||
// window global.
|
||||
// See https://github.com/mozilla/gecko-dev/blob/cd2121e7d83af1b421c95e8c923db70e692dab5f/testing/mochitest/BrowserTestUtils/BrowserTestUtilsParent.sys.mjs#L15
|
||||
// See https://github.com/mozilla-firefox/firefox/blob/35e22180b0b61413dd8eccf6c00b1c6fac073eee/testing/mochitest/BrowserTestUtils/BrowserTestUtilsParent.sys.mjs#L15
|
||||
if (!this.manager?.isCurrentGlobal)
|
||||
return;
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
"use strict";
|
||||
|
||||
const { TargetRegistry } = ChromeUtils.importESModule('chrome://juggler/content/TargetRegistry.js');
|
||||
const { Helper } = ChromeUtils.importESModule('chrome://juggler/content/Helper.js');
|
||||
|
||||
const helper = new Helper();
|
||||
|
||||
export class JugglerFrameParent extends JSWindowActorParent {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
receiveMessage() { }
|
||||
|
||||
async actorCreated() {
|
||||
// Actors are registered per the WindowGlobalParent / WindowGlobalChild pair. We are only
|
||||
// interested in those WindowGlobalParent actors that are matching current browsingContext
|
||||
// window global.
|
||||
// See https://github.com/mozilla/gecko-dev/blob/cd2121e7d83af1b421c95e8c923db70e692dab5f/testing/mochitest/BrowserTestUtils/BrowserTestUtilsParent.sys.mjs#L15
|
||||
if (!this.manager?.isCurrentGlobal)
|
||||
return;
|
||||
|
||||
// Only interested in main frames for now.
|
||||
if (this.browsingContext.parent)
|
||||
return;
|
||||
|
||||
this._target = TargetRegistry.instance()?.targetForBrowserId(this.browsingContext.browserId);
|
||||
if (!this._target)
|
||||
return;
|
||||
|
||||
this.actorName = `browser::page[${this._target.id()}]/${this.browsingContext.browserId}/${this.browsingContext.id}/${this._target.nextActorSequenceNumber()}`;
|
||||
this._target.setActor(this);
|
||||
}
|
||||
|
||||
didDestroy() {
|
||||
if (!this._target)
|
||||
return;
|
||||
this._target.removeActor(this);
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm');
|
||||
const { ChannelEventSinkFactory } = ChromeUtils.import("chrome://remote/content/cdp/observers/ChannelEventSink.jsm");
|
||||
const {Helper} = ChromeUtils.importESModule('chrome://juggler/content/Helper.js');
|
||||
const {NetUtil} = ChromeUtils.importESModule('resource://gre/modules/NetUtil.sys.mjs');
|
||||
const { ChannelEventSinkFactory } = ChromeUtils.importESModule("chrome://juggler/content/ChannelEventSink.sys.mjs");
|
||||
|
||||
|
||||
const Cc = Components.classes;
|
||||
@@ -28,7 +28,7 @@ const MAX_RESPONSE_STORAGE_SIZE = 100 * 1024 * 1024;
|
||||
|
||||
const pageNetworkSymbol = Symbol('PageNetwork');
|
||||
|
||||
class PageNetwork {
|
||||
export class PageNetwork {
|
||||
static forPageTarget(target) {
|
||||
if (!target)
|
||||
return undefined;
|
||||
@@ -143,9 +143,10 @@ class NetworkRequest {
|
||||
const target = this._networkObserver._targetRegistry.targetForBrowserId(browsingContext.browserId);
|
||||
this._pageNetwork = PageNetwork.forPageTarget(target);
|
||||
}
|
||||
this._expectingInterception = false;
|
||||
this._shouldYieldInterceptionToServiceWorker = false;
|
||||
this._expectingResumedRequest = undefined; // { method, headers, postData }
|
||||
this._overriddenHeadersForRedirect = redirectedFrom?._overriddenHeadersForRedirect;
|
||||
this._sentOnRequest = false;
|
||||
this._sentOnResponse = false;
|
||||
this._fulfilled = false;
|
||||
|
||||
@@ -204,11 +205,13 @@ class NetworkRequest {
|
||||
this._interceptedChannel.synthesizeHeader(header.name, header.value);
|
||||
if (header.name.toLowerCase() === 'set-cookie') {
|
||||
Services.cookies.QueryInterface(Ci.nsICookieService);
|
||||
Services.cookies.setCookieStringFromHttp(this.httpChannel.URI, header.value, this.httpChannel);
|
||||
for (const cookieString of header.value.split('\n'))
|
||||
Services.cookies.setCookieStringFromHttp(this.httpChannel.URI, cookieString, this.httpChannel);
|
||||
}
|
||||
}
|
||||
const synthesized = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
|
||||
synthesized.data = base64body ? atob(base64body) : '';
|
||||
if (base64body)
|
||||
synthesized.setByteStringData(atob(base64body));
|
||||
this._interceptedChannel.startSynthesizedResponse(synthesized, null, null, '', false);
|
||||
this._interceptedChannel.finishSynthesizedResponse();
|
||||
this._interceptedChannel = undefined;
|
||||
@@ -316,9 +319,8 @@ class NetworkRequest {
|
||||
const interceptController = this._fallThroughInterceptController();
|
||||
if (interceptController && interceptController.shouldPrepareForIntercept(aURI, channel)) {
|
||||
// We assume that interceptController is a service worker if there is one,
|
||||
// and yield interception to it. We are not going to intercept ourselves,
|
||||
// so we send onRequest now.
|
||||
this._sendOnRequest(false);
|
||||
// and yield interception to it.
|
||||
this._shouldYieldInterceptionToServiceWorker = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -327,12 +329,6 @@ class NetworkRequest {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We do not want to intercept any redirects, because we are not able
|
||||
// to intercept subresource redirects, and it's unreliable for main requests.
|
||||
// We do not sendOnRequest here, because redirects do that in constructor.
|
||||
if (this.redirectedFromId)
|
||||
return false;
|
||||
|
||||
const shouldIntercept = this._shouldIntercept();
|
||||
if (!shouldIntercept) {
|
||||
// We are not intercepting - ready to issue onRequest.
|
||||
@@ -340,21 +336,24 @@ class NetworkRequest {
|
||||
return false;
|
||||
}
|
||||
|
||||
this._expectingInterception = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// nsINetworkInterceptController
|
||||
channelIntercepted(intercepted) {
|
||||
if (!this._expectingInterception) {
|
||||
// We are not intercepting, fall-through.
|
||||
const interceptController = this._fallThroughInterceptController();
|
||||
if (interceptController)
|
||||
interceptController.channelIntercepted(intercepted);
|
||||
// Yield to a service worker if determined so in shouldPrepareForIntercept().
|
||||
const serviceWorker = this._shouldYieldInterceptionToServiceWorker ? this._fallThroughInterceptController() : undefined;
|
||||
// Clear the flag to avoid an infinite loop. After service worker, we should intercept ourselves.
|
||||
this._shouldYieldInterceptionToServiceWorker = false;
|
||||
|
||||
if (serviceWorker) {
|
||||
const interceptedChannel = intercepted.QueryInterface(Ci.nsIInterceptedChannel);
|
||||
// If service worker will not actually intercept the request, we want to be called again.
|
||||
interceptedChannel.interceptAfterServiceWorkerResets();
|
||||
serviceWorker.channelIntercepted(intercepted);
|
||||
return;
|
||||
}
|
||||
|
||||
this._expectingInterception = false;
|
||||
this._interceptedChannel = intercepted.QueryInterface(Ci.nsIInterceptedChannel);
|
||||
|
||||
const pageNetwork = this._pageNetwork;
|
||||
@@ -408,6 +407,7 @@ class NetworkRequest {
|
||||
// See https://github.com/microsoft/playwright/issues/9418#issuecomment-944836244
|
||||
if (aRequest !== this.httpChannel)
|
||||
return;
|
||||
this._sendOnRequest(false);
|
||||
try {
|
||||
this._originalListener.onStartRequest(aRequest);
|
||||
} catch (e) {
|
||||
@@ -445,6 +445,10 @@ class NetworkRequest {
|
||||
}
|
||||
|
||||
_shouldIntercept() {
|
||||
// We do not want to intercept any redirects, because we are not able
|
||||
// to intercept subresource redirects, and it's unreliable for main requests.
|
||||
if (this.redirectedFromId)
|
||||
return false;
|
||||
const pageNetwork = this._pageNetwork;
|
||||
if (!pageNetwork)
|
||||
return false;
|
||||
@@ -465,8 +469,15 @@ class NetworkRequest {
|
||||
}
|
||||
|
||||
_sendOnRequest(isIntercepted) {
|
||||
// Note: we call _sendOnRequest either after we intercepted the request,
|
||||
// or at the first moment we know that we are not going to intercept.
|
||||
if (this._sentOnRequest) {
|
||||
// We can come here twice because:
|
||||
// - Redirects call _sendOnRequest in the constructor and from inside interception.
|
||||
// - All other requests might call _sendOnRequest from onStartRequest and from inside interception.
|
||||
// - All requests call _sendOnRequest from _sendOnResponse to avoid responses without requests.
|
||||
return;
|
||||
}
|
||||
this._sentOnRequest = true;
|
||||
|
||||
const pageNetwork = this._pageNetwork;
|
||||
if (!pageNetwork)
|
||||
return;
|
||||
@@ -489,11 +500,21 @@ class NetworkRequest {
|
||||
}
|
||||
|
||||
_sendOnResponse(fromCache, opt_statusCode, opt_statusText) {
|
||||
// For internal redirects, and perhaps something else that we lack test coverage for,
|
||||
// we can arrive here before onStartRequest has fired. Make sure we
|
||||
// notify about the request first.
|
||||
this._sendOnRequest(false);
|
||||
|
||||
if (this._sentOnResponse) {
|
||||
// We can come here twice because of internal redirects, e.g. service workers.
|
||||
// We can come here twice because of an internal redirect, for example:
|
||||
// - request was intercepted by a service worker;
|
||||
// - HSTS redirect;
|
||||
// - CORS preflight;
|
||||
// - who knows what else?
|
||||
return;
|
||||
}
|
||||
this._sentOnResponse = true;
|
||||
|
||||
const pageNetwork = this._pageNetwork;
|
||||
if (!pageNetwork)
|
||||
return;
|
||||
@@ -512,6 +533,8 @@ class NetworkRequest {
|
||||
};
|
||||
|
||||
const { status, statusText, headers } = responseHead(this.httpChannel, opt_statusCode, opt_statusText);
|
||||
if (redirectStatus.includes(status) && this._overriddenHeadersForRedirect)
|
||||
this._overriddenHeadersForRedirect = filterHeadersForRedirect(this._overriddenHeadersForRedirect, this.httpChannel.requestMethod, status);
|
||||
let remoteIPAddress = undefined;
|
||||
let remotePort = undefined;
|
||||
try {
|
||||
@@ -574,7 +597,7 @@ class NetworkRequest {
|
||||
}
|
||||
}
|
||||
|
||||
class NetworkObserver {
|
||||
export class NetworkObserver {
|
||||
static instance() {
|
||||
return NetworkObserver._instance || null;
|
||||
}
|
||||
@@ -774,6 +797,10 @@ function clearRequestHeaders(httpChannel) {
|
||||
// We cannot remove the "host" header.
|
||||
if (header.name.toLowerCase() === 'host')
|
||||
continue;
|
||||
// Keep the "cookie" header. If there is an override, it will be set anyway.
|
||||
// Otherwise, we may delete a cookie that was set for a redirect.
|
||||
if (header.name.toLowerCase() === 'cookie')
|
||||
continue;
|
||||
httpChannel.setRequestHeader(header.name, '', false /* merge */);
|
||||
}
|
||||
}
|
||||
@@ -783,6 +810,18 @@ function overrideRequestHeaders(httpChannel, headers) {
|
||||
appendExtraHTTPHeaders(httpChannel, headers);
|
||||
}
|
||||
|
||||
const redirectStatus = [301, 302, 303, 307, 308];
|
||||
|
||||
function filterHeadersForRedirect(headers, requestMethod, status) {
|
||||
// HTTP-redirect fetch step 13 (https://fetch.spec.whatwg.org/#http-redirect-fetch)
|
||||
if ((status === 301 || status === 302) && requestMethod === 'POST' ||
|
||||
status === 303 && !['GET', 'HEAD'].includes(requestMethod)) {
|
||||
const requestBodyHeaders = ['content-encoding', 'content-language', 'content-length', 'content-location', 'content-type'];
|
||||
return headers.filter(header => !requestBodyHeaders.includes(header.name.toLowerCase()));
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
function causeTypeToString(causeType) {
|
||||
for (let key in Ci.nsIContentPolicy) {
|
||||
if (Ci.nsIContentPolicy[key] === causeType)
|
||||
@@ -870,7 +909,7 @@ function setPostData(httpChannel, postData, headers) {
|
||||
return;
|
||||
const synthesized = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
|
||||
const body = atob(postData);
|
||||
synthesized.setByteStringData(body, body.length);
|
||||
synthesized.setByteStringData(body);
|
||||
|
||||
const overriddenHeader = (lowerCaseName) => {
|
||||
if (headers) {
|
||||
@@ -902,7 +941,7 @@ function convertString(s, source, dest) {
|
||||
const is = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
|
||||
Ci.nsIStringInputStream
|
||||
);
|
||||
is.setByteStringData(s, s.length);
|
||||
is.setByteStringData(s);
|
||||
const listener = Cc["@mozilla.org/network/stream-loader;1"].createInstance(
|
||||
Ci.nsIStreamLoader
|
||||
);
|
||||
@@ -962,6 +1001,3 @@ PageNetwork.Events = {
|
||||
RequestFailed: Symbol('PageNetwork.Events.RequestFailed'),
|
||||
};
|
||||
|
||||
var EXPORTED_SYMBOLS = ['NetworkObserver', 'PageNetwork'];
|
||||
this.NetworkObserver = NetworkObserver;
|
||||
this.PageNetwork = PageNetwork;
|
||||
@@ -79,7 +79,7 @@ class SimpleChannel {
|
||||
|
||||
_setTimeout(cb, timeout) {
|
||||
// Lazy load on first call.
|
||||
this._setTimeout = ChromeUtils.import('resource://gre/modules/Timer.jsm').setTimeout;
|
||||
this._setTimeout = ChromeUtils.importESModule('resource://gre/modules/Timer.sys.mjs').setTimeout;
|
||||
this._setTimeout(cb, timeout);
|
||||
}
|
||||
|
||||
@@ -251,6 +251,3 @@ class SimpleChannel {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var EXPORTED_SYMBOLS = ['SimpleChannel'];
|
||||
this.SimpleChannel = SimpleChannel;
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
/* 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/. */
|
||||
|
||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js');
|
||||
const {Preferences} = ChromeUtils.import("resource://gre/modules/Preferences.jsm");
|
||||
const {ContextualIdentityService} = ChromeUtils.import("resource://gre/modules/ContextualIdentityService.jsm");
|
||||
const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm');
|
||||
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
const {Helper} = ChromeUtils.importESModule('chrome://juggler/content/Helper.js');
|
||||
const {Preferences} = ChromeUtils.importESModule("resource://gre/modules/Preferences.sys.mjs");
|
||||
const {ContextualIdentityService} = ChromeUtils.importESModule("resource://gre/modules/ContextualIdentityService.sys.mjs");
|
||||
const {NetUtil} = ChromeUtils.importESModule('resource://gre/modules/NetUtil.sys.mjs');
|
||||
const {AppConstants} = ChromeUtils.importESModule("resource://gre/modules/AppConstants.sys.mjs");
|
||||
|
||||
const Cr = Components.results;
|
||||
|
||||
const helper = new Helper();
|
||||
|
||||
const IDENTITY_NAME = 'Camoufox ';
|
||||
const IDENTITY_NAME = 'JUGGLER ';
|
||||
const HUNDRED_YEARS = 60 * 60 * 24 * 365 * 100;
|
||||
|
||||
const ALL_PERMISSIONS = [
|
||||
@@ -22,6 +21,9 @@ const ALL_PERMISSIONS = [
|
||||
];
|
||||
|
||||
let globalTabAndWindowActivationChain = Promise.resolve();
|
||||
// This is a workaround for https://github.com/microsoft/playwright/issues/34586
|
||||
let didCreateFirstPage = false;
|
||||
let globalNewPageChain = Promise.resolve();
|
||||
|
||||
class DownloadInterceptor {
|
||||
constructor(registry) {
|
||||
@@ -102,7 +104,7 @@ class DownloadInterceptor {
|
||||
|
||||
const screencastService = Cc['@mozilla.org/juggler/screencast;1'].getService(Ci.nsIScreencastService);
|
||||
|
||||
class TargetRegistry {
|
||||
export class TargetRegistry {
|
||||
static instance() {
|
||||
return TargetRegistry._instance || null;
|
||||
}
|
||||
@@ -308,6 +310,16 @@ class TargetRegistry {
|
||||
}
|
||||
|
||||
async newPage({browserContextId}) {
|
||||
// When creating the very first page, we cannot create multiple in parallel.
|
||||
// See https://github.com/microsoft/playwright/issues/34586.
|
||||
if (didCreateFirstPage)
|
||||
return this._newPageInternal({browserContextId});
|
||||
const result = globalNewPageChain.then(() => this._newPageInternal({browserContextId}));
|
||||
globalNewPageChain = result.catch(error => { /* swallow errors to keep chain running */ });
|
||||
return result;
|
||||
}
|
||||
|
||||
async _newPageInternal({browserContextId}) {
|
||||
const browserContext = this.browserContextForId(browserContextId);
|
||||
const features = "chrome,dialog=no,all";
|
||||
// See _callWithURIToLoad in browser.js for the structure of window.arguments
|
||||
@@ -356,6 +368,7 @@ class TargetRegistry {
|
||||
if (await target.hasFailedToOverrideTimezone())
|
||||
throw new Error('Failed to override timezone');
|
||||
}
|
||||
didCreateFirstPage = true;
|
||||
return target.id();
|
||||
}
|
||||
|
||||
@@ -372,7 +385,7 @@ class TargetRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
class PageTarget {
|
||||
export class PageTarget {
|
||||
constructor(registry, win, tab, browserContext, opener) {
|
||||
helper.decorateAsEventEmitter(this);
|
||||
|
||||
@@ -384,16 +397,7 @@ class PageTarget {
|
||||
this._linkedBrowser = tab.linkedBrowser;
|
||||
this._browserContext = browserContext;
|
||||
this._viewportSize = undefined;
|
||||
// Set the viewport size to Camoufox's default value.
|
||||
if (
|
||||
ChromeUtils.camouGetInt("window.innerWidth")
|
||||
|| ChromeUtils.camouGetInt("window.innerHeight")
|
||||
) {
|
||||
this._viewportSize = {
|
||||
width: ChromeUtils.camouGetInt("window.innerWidth") || 1280,
|
||||
height: ChromeUtils.camouGetInt("window.innerHeight") || 720,
|
||||
};
|
||||
}
|
||||
this._zoom = 1;
|
||||
this._initialDPPX = this._linkedBrowser.browsingContext.overrideDPPX;
|
||||
this._url = 'about:blank';
|
||||
this._openerId = opener ? opener.id() : undefined;
|
||||
@@ -506,9 +510,11 @@ class PageTarget {
|
||||
this.updateUserAgent(browsingContext);
|
||||
this.updatePlatform(browsingContext);
|
||||
this.updateDPPXOverride(browsingContext);
|
||||
this.updateZoom(browsingContext);
|
||||
this.updateEmulatedMedia(browsingContext);
|
||||
this.updateColorSchemeOverride(browsingContext);
|
||||
this.updateReducedMotionOverride(browsingContext);
|
||||
this.updateContrastOverride(browsingContext);
|
||||
this.updateForcedColorsOverride(browsingContext);
|
||||
this.updateForceOffline(browsingContext);
|
||||
this.updateCacheDisabled(browsingContext);
|
||||
@@ -544,7 +550,16 @@ class PageTarget {
|
||||
}
|
||||
|
||||
updateDPPXOverride(browsingContext = undefined) {
|
||||
(browsingContext || this._linkedBrowser.browsingContext).overrideDPPX = this._browserContext.deviceScaleFactor || this._initialDPPX;
|
||||
browsingContext ||= this._linkedBrowser.browsingContext;
|
||||
const dppx = this._zoom * (this._browserContext.deviceScaleFactor || this._initialDPPX);
|
||||
browsingContext.overrideDPPX = dppx;
|
||||
}
|
||||
|
||||
async updateZoom(browsingContext = undefined) {
|
||||
browsingContext ||= this._linkedBrowser.browsingContext;
|
||||
// Update dpr first, and then UI zoom.
|
||||
this.updateDPPXOverride(browsingContext);
|
||||
browsingContext.fullZoom = this._zoom;
|
||||
}
|
||||
|
||||
_updateModalDialogs() {
|
||||
@@ -579,19 +594,7 @@ class PageTarget {
|
||||
// The "default size" (1) is only respected when the page is opened.
|
||||
// Otherwise, explicitly set page viewport prevales over browser context
|
||||
// default viewport.
|
||||
|
||||
// Camoufox is already handling viewport size, so we don't need to set it here.
|
||||
if (
|
||||
ChromeUtils.camouGetInt("window.outerWidth") ||
|
||||
ChromeUtils.camouGetInt("window.outerHeight") ||
|
||||
ChromeUtils.camouGetInt("window.innerWidth") ||
|
||||
ChromeUtils.camouGetInt("window.innerHeight")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewportSize = this._viewportSize || this._browserContext.defaultViewportSize;
|
||||
|
||||
if (viewportSize) {
|
||||
const {width, height} = viewportSize;
|
||||
this._linkedBrowser.style.setProperty('width', width + 'px');
|
||||
@@ -606,7 +609,7 @@ class PageTarget {
|
||||
const toolbarTop = stackRect.y;
|
||||
this._window.resizeBy(width - this._window.innerWidth, height + toolbarTop - this._window.innerHeight);
|
||||
|
||||
await this._channel.connect('').send('awaitViewportDimensions', { width, height });
|
||||
await this._channel.connect('').send('awaitViewportDimensions', { width: width / this._zoom, height: height / this._zoom });
|
||||
} else {
|
||||
this._linkedBrowser.style.removeProperty('width');
|
||||
this._linkedBrowser.style.removeProperty('height');
|
||||
@@ -618,8 +621,8 @@ class PageTarget {
|
||||
|
||||
const actualSize = this._linkedBrowser.getBoundingClientRect();
|
||||
await this._channel.connect('').send('awaitViewportDimensions', {
|
||||
width: actualSize.width,
|
||||
height: actualSize.height,
|
||||
width: actualSize.width / this._zoom,
|
||||
height: actualSize.height / this._zoom,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -639,7 +642,7 @@ class PageTarget {
|
||||
}
|
||||
|
||||
updateColorSchemeOverride(browsingContext = undefined) {
|
||||
(browsingContext || this._linkedBrowser.browsingContext).prefersColorSchemeOverride = this.colorScheme || 'dark';
|
||||
(browsingContext || this._linkedBrowser.browsingContext).prefersColorSchemeOverride = this.colorScheme || this._browserContext.colorScheme || 'none';
|
||||
}
|
||||
|
||||
setReducedMotion(reducedMotion) {
|
||||
@@ -651,6 +654,15 @@ class PageTarget {
|
||||
(browsingContext || this._linkedBrowser.browsingContext).prefersReducedMotionOverride = this.reducedMotion || this._browserContext.reducedMotion || 'none';
|
||||
}
|
||||
|
||||
setContrast(contrast) {
|
||||
this.contrast = fromProtocolContrast(contrast);
|
||||
this.updateContrastOverride();
|
||||
}
|
||||
|
||||
updateContrastOverride(browsingContext = undefined) {
|
||||
(browsingContext || this._linkedBrowser.browsingContext).prefersContrastOverride = this.contrast || this._browserContext.contrast || 'none';
|
||||
}
|
||||
|
||||
setForcedColors(forcedColors) {
|
||||
this.forcedColors = fromProtocolForcedColors(forcedColors);
|
||||
this.updateForcedColorsOverride();
|
||||
@@ -672,6 +684,14 @@ class PageTarget {
|
||||
await this.updateViewportSize();
|
||||
}
|
||||
|
||||
async setZoom(zoom) {
|
||||
// This is default range from the ZoomManager.
|
||||
if (zoom < 0.3 || zoom > 5)
|
||||
throw new Error('Invalid zoom value, must be between 0.3 and 5');
|
||||
this._zoom = zoom;
|
||||
await this.updateZoom();
|
||||
}
|
||||
|
||||
close(runBeforeUnload = false) {
|
||||
this._gBrowser.removeTab(this._tab, {
|
||||
skipPermitUnload: !runBeforeUnload,
|
||||
@@ -878,6 +898,14 @@ function fromProtocolReducedMotion(reducedMotion) {
|
||||
throw new Error('Unknown reduced motion: ' + reducedMotion);
|
||||
}
|
||||
|
||||
function fromProtocolContrast(contrast) {
|
||||
if (contrast === 'more' || contrast === 'less' || contrast === 'custom' || contrast === 'no-preference')
|
||||
return contrast;
|
||||
if (contrast === null)
|
||||
return undefined;
|
||||
throw new Error('Unknown contrast: ' + contrast);
|
||||
}
|
||||
|
||||
function fromProtocolForcedColors(forcedColors) {
|
||||
if (forcedColors === 'active' || forcedColors === 'none')
|
||||
return forcedColors;
|
||||
@@ -918,6 +946,7 @@ class BrowserContext {
|
||||
this.colorScheme = 'none';
|
||||
this.forcedColors = 'none';
|
||||
this.reducedMotion = 'none';
|
||||
this.contrast = 'none';
|
||||
this.videoRecordingOptions = undefined;
|
||||
this.crossProcessCookie = {
|
||||
initScripts: [],
|
||||
@@ -944,6 +973,12 @@ class BrowserContext {
|
||||
page.updateReducedMotionOverride();
|
||||
}
|
||||
|
||||
setContrast(contrast) {
|
||||
this.contrast = fromProtocolContrast(contrast);
|
||||
for (const page of this.pages)
|
||||
page.updateContrastOverride();
|
||||
}
|
||||
|
||||
setForcedColors(forcedColors) {
|
||||
this.forcedColors = fromProtocolForcedColors(forcedColors);
|
||||
for (const page of this.pages)
|
||||
@@ -988,9 +1023,9 @@ class BrowserContext {
|
||||
if (ignoreHTTPSErrors) {
|
||||
Preferences.set("network.stricttransportsecurity.preloadlist", false);
|
||||
Preferences.set("security.cert_pinning.enforcement_level", 0);
|
||||
certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(true, this.userContextId);
|
||||
certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyDataForUserContext(this.userContextId, true);
|
||||
} else {
|
||||
certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(false, this.userContextId);
|
||||
certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyDataForUserContext(this.userContextId, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1025,15 +1060,6 @@ class BrowserContext {
|
||||
}
|
||||
|
||||
async setDefaultViewport(viewport) {
|
||||
// Camoufox: only override the set viewport if a new one was passed
|
||||
if (
|
||||
ChromeUtils.camouGetInt("window.innerWidth")
|
||||
|| ChromeUtils.camouGetInt("window.innerHeight")
|
||||
) {
|
||||
if (viewport.viewportSize?.width == 1280 && viewport.viewportSize?.height == 720) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.defaultViewportSize = viewport ? viewport.viewportSize : undefined;
|
||||
this.deviceScaleFactor = viewport ? viewport.deviceScaleFactor : undefined;
|
||||
await Promise.all(Array.from(this.pages).map(page => page.updateViewportSize()));
|
||||
@@ -1098,7 +1124,8 @@ class BrowserContext {
|
||||
|
||||
setCookies(cookies) {
|
||||
const protocolToSameSite = {
|
||||
[undefined]: Ci.nsICookie.SAMESITE_NONE,
|
||||
[undefined]: Ci.nsICookie.SAMESITE_UNSET,
|
||||
'None': Ci.nsICookie.SAMESITE_UNSET,
|
||||
'Lax': Ci.nsICookie.SAMESITE_LAX,
|
||||
'Strict': Ci.nsICookie.SAMESITE_STRICT,
|
||||
};
|
||||
@@ -1126,7 +1153,7 @@ class BrowserContext {
|
||||
secure,
|
||||
cookie.httpOnly || false,
|
||||
cookie.expires === undefined || cookie.expires === -1 /* isSession */,
|
||||
cookie.expires === undefined ? Date.now() + HUNDRED_YEARS : cookie.expires,
|
||||
cookie.expires === undefined ? Date.now() + HUNDRED_YEARS : cookie.expires * 1000,
|
||||
{ userContextId: this.userContextId || undefined } /* originAttributes */,
|
||||
protocolToSameSite[cookie.sameSite],
|
||||
Ci.nsICookie.SCHEME_UNSET
|
||||
@@ -1141,6 +1168,7 @@ class BrowserContext {
|
||||
getCookies() {
|
||||
const result = [];
|
||||
const sameSiteToProtocol = {
|
||||
[Ci.nsICookie.SAMESITE_UNSET]: 'None',
|
||||
[Ci.nsICookie.SAMESITE_NONE]: 'None',
|
||||
[Ci.nsICookie.SAMESITE_LAX]: 'Lax',
|
||||
[Ci.nsICookie.SAMESITE_STRICT]: 'Strict',
|
||||
@@ -1155,7 +1183,7 @@ class BrowserContext {
|
||||
value: cookie.value,
|
||||
domain: cookie.host,
|
||||
path: cookie.path,
|
||||
expires: cookie.isSession ? -1 : cookie.expiry,
|
||||
expires: cookie.isSession ? -1 : cookie.expiry / 1000,
|
||||
size: cookie.name.length + cookie.value.length,
|
||||
httpOnly: cookie.isHttpOnly,
|
||||
secure: cookie.isSecure,
|
||||
@@ -1267,7 +1295,3 @@ TargetRegistry.Events = {
|
||||
DownloadFinished: Symbol('TargetRegistry.Events.DownloadFinished'),
|
||||
ScreencastStopped: Symbol('TargetRegistry.ScreencastStopped'),
|
||||
};
|
||||
|
||||
var EXPORTED_SYMBOLS = ['TargetRegistry', 'PageTarget'];
|
||||
this.TargetRegistry = TargetRegistry;
|
||||
this.PageTarget = PageTarget;
|
||||
@@ -2,17 +2,16 @@
|
||||
* 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/. */
|
||||
|
||||
var EXPORTED_SYMBOLS = ["Juggler", "JugglerFactory"];
|
||||
// Services is available as a global in XPCOM component context
|
||||
|
||||
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
const {ComponentUtils} = ChromeUtils.import("resource://gre/modules/ComponentUtils.jsm");
|
||||
const {Dispatcher} = ChromeUtils.import("chrome://juggler/content/protocol/Dispatcher.js");
|
||||
const {BrowserHandler} = ChromeUtils.import("chrome://juggler/content/protocol/BrowserHandler.js");
|
||||
const {NetworkObserver} = ChromeUtils.import("chrome://juggler/content/NetworkObserver.js");
|
||||
const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js");
|
||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const {ActorManagerParent} = ChromeUtils.import('resource://gre/modules/ActorManagerParent.jsm');
|
||||
// Load SimpleChannel in browser-process global.
|
||||
Services.scriptloader.loadSubScript('chrome://juggler/content/SimpleChannel.js');
|
||||
const {Dispatcher} = ChromeUtils.importESModule("chrome://juggler/content/protocol/Dispatcher.js");
|
||||
const {BrowserHandler} = ChromeUtils.importESModule("chrome://juggler/content/protocol/BrowserHandler.js");
|
||||
const {NetworkObserver} = ChromeUtils.importESModule("chrome://juggler/content/NetworkObserver.js");
|
||||
const {TargetRegistry} = ChromeUtils.importESModule("chrome://juggler/content/TargetRegistry.js");
|
||||
const {Helper} = ChromeUtils.importESModule('chrome://juggler/content/Helper.js');
|
||||
const {ActorManagerParent} = ChromeUtils.importESModule('resource://gre/modules/ActorManagerParent.sys.mjs');
|
||||
const helper = new Helper();
|
||||
|
||||
const Cc = Components.classes;
|
||||
@@ -22,10 +21,10 @@ const Ci = Components.interfaces;
|
||||
ActorManagerParent.addJSWindowActors({
|
||||
JugglerFrame: {
|
||||
parent: {
|
||||
moduleURI: 'chrome://juggler/content/JugglerFrameParent.jsm',
|
||||
esModuleURI: 'chrome://juggler/content/JugglerFrameParent.sys.mjs',
|
||||
},
|
||||
child: {
|
||||
moduleURI: 'chrome://juggler/content/content/JugglerFrameChild.jsm',
|
||||
esModuleURI: 'chrome://juggler/content/JugglerFrameChild.sys.mjs',
|
||||
events: {
|
||||
// Normally, we instantiate an actor when a new window is created.
|
||||
DOMWindowCreated: {},
|
||||
@@ -46,7 +45,7 @@ ActorManagerParent.addJSWindowActors({
|
||||
let browserStartupFinishedCallback;
|
||||
let browserStartupFinishedPromise = new Promise(x => browserStartupFinishedCallback = x);
|
||||
|
||||
class Juggler {
|
||||
export class Juggler {
|
||||
get classDescription() { return "Sample command-line handler"; }
|
||||
get classID() { return Components.ID('{f7a74a33-e2ab-422d-b022-4fb213dd2639}'); }
|
||||
get contractID() { return "@mozilla.org/remote/juggler;1" }
|
||||
@@ -65,7 +64,7 @@ class Juggler {
|
||||
}
|
||||
|
||||
// This flow is taken from Remote agent and Marionette.
|
||||
// See https://github.com/mozilla/gecko-dev/blob/0c1b4921830e6af8bc951da01d7772de2fe60a08/remote/components/RemoteAgent.jsm#L302
|
||||
// See https://github.com/mozilla-firefox/firefox/blob/35e22180b0b61413dd8eccf6c00b1c6fac073eee/remote/components/RemoteAgent.sys.mjs#L417
|
||||
async observe(subject, topic) {
|
||||
switch (topic) {
|
||||
case "profile-after-change":
|
||||
@@ -135,20 +134,15 @@ class Juggler {
|
||||
},
|
||||
};
|
||||
pipe.init(connection);
|
||||
ChromeUtils.camouDebug('Juggler pipe initialized');
|
||||
const dispatcher = new Dispatcher(connection);
|
||||
ChromeUtils.camouDebug('Dispatcher created');
|
||||
browserHandler = new BrowserHandler(dispatcher.rootSession(), dispatcher, targetRegistry, browserStartupFinishedPromise, () => {
|
||||
ChromeUtils.camouDebug('BrowserHandler cleanup callback called');
|
||||
if (this._silent)
|
||||
Services.startup.exitLastWindowClosingSurvivalArea();
|
||||
connection.onclose();
|
||||
pipe.stop();
|
||||
pipeStopped = true;
|
||||
});
|
||||
ChromeUtils.camouDebug('BrowserHandler created');
|
||||
dispatcher.rootSession().setHandler(browserHandler);
|
||||
ChromeUtils.camouDebug('BrowserHandler set as root session handler');
|
||||
loadStyleSheet();
|
||||
dump(`\nJuggler listening to the pipe\n`);
|
||||
break;
|
||||
@@ -160,7 +154,7 @@ class Juggler {
|
||||
const jugglerInstance = new Juggler();
|
||||
|
||||
// This is used by the XPCOM codepath which expects a constructor
|
||||
var JugglerFactory = function() {
|
||||
export var JugglerFactory = function() {
|
||||
return jugglerInstance;
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ Classes = [
|
||||
"command-line-handler": "m-remote",
|
||||
"profile-after-change": "Juggler",
|
||||
},
|
||||
"jsm": "chrome://juggler/content/components/Juggler.js",
|
||||
"esModule": "chrome://juggler/content/components/Juggler.js",
|
||||
"constructor": "JugglerFactory",
|
||||
},
|
||||
]
|
||||
|
||||
@@ -7,13 +7,11 @@ const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js');
|
||||
const {Runtime} = ChromeUtils.import('chrome://juggler/content/content/Runtime.js');
|
||||
const {Helper} = ChromeUtils.importESModule('chrome://juggler/content/Helper.js');
|
||||
|
||||
const helper = new Helper();
|
||||
|
||||
class FrameTree {
|
||||
export class FrameTree {
|
||||
constructor(rootBrowsingContext) {
|
||||
helper.decorateAsEventEmitter(this);
|
||||
|
||||
@@ -411,11 +409,7 @@ class Frame {
|
||||
this._parentFrame = parentFrame;
|
||||
parentFrame._children.add(this);
|
||||
}
|
||||
|
||||
this.allowMW = ChromeUtils.camouGetBool('allowMainWorld', false);
|
||||
this.forceScopeAccess = ChromeUtils.camouGetBool('forceScopeAccess', false);
|
||||
|
||||
this.masterSandbox = undefined;
|
||||
this._lastCommittedNavigationId = null;
|
||||
this._pendingNavigationId = null;
|
||||
|
||||
@@ -509,46 +503,18 @@ class Frame {
|
||||
};
|
||||
}
|
||||
|
||||
// Camoufox: Add a "God mode" master sandbox with it's own compartment
|
||||
getMasterSandbox() {
|
||||
if (!this.masterSandbox) {
|
||||
this.masterSandbox = Cu.Sandbox(
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
{
|
||||
sandboxPrototype: this.domWindow(),
|
||||
wantComponents: false,
|
||||
wantExportHelpers: false,
|
||||
wantXrays: true,
|
||||
freshCompartment: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
return this.masterSandbox;
|
||||
}
|
||||
|
||||
_createIsolatedContext(name, useMaster=false) {
|
||||
let sandbox;
|
||||
// Camoufox: Use the master sandbox (with system principle scope access)
|
||||
if (useMaster && this.forceScopeAccess) {
|
||||
sandbox = this.getMasterSandbox();
|
||||
} else {
|
||||
// Standard access (run in domWindow principal)
|
||||
sandbox = Cu.Sandbox([this.domWindow()], {
|
||||
sandboxPrototype: this.domWindow(),
|
||||
wantComponents: false,
|
||||
wantExportHelpers: false,
|
||||
wantXrays: true,
|
||||
});
|
||||
}
|
||||
_createIsolatedContext(name) {
|
||||
const principal = [this.domWindow()]; // extended principal
|
||||
const sandbox = Cu.Sandbox(principal, {
|
||||
sandboxPrototype: this.domWindow(),
|
||||
wantComponents: false,
|
||||
wantExportHelpers: false,
|
||||
wantXrays: true,
|
||||
});
|
||||
const world = this._runtime.createExecutionContext(this.domWindow(), sandbox, {
|
||||
frameId: this.id(),
|
||||
name,
|
||||
});
|
||||
// Camoufox: Create a main world for the isolated context
|
||||
if (this.allowMW) {
|
||||
const mainWorld = this._runtime.createMW(this.domWindow(), this.domWindow());
|
||||
world.mainEquivalent = mainWorld;
|
||||
}
|
||||
this._worldNameToContext.set(name, world);
|
||||
return world;
|
||||
}
|
||||
@@ -582,11 +548,15 @@ class Frame {
|
||||
webSocketService.removeListener(this._webSocketListenerInnerWindowId, this._webSocketListener);
|
||||
this._webSocketListenerInnerWindowId = this.domWindow().windowGlobalChild.innerWindowId;
|
||||
webSocketService.addListener(this._webSocketListenerInnerWindowId, this._webSocketListener);
|
||||
|
||||
for (const context of this._worldNameToContext.values())
|
||||
this._runtime.destroyExecutionContext(context);
|
||||
this._worldNameToContext.clear();
|
||||
// Camoufox: Scope the initial execution context to prevent leaks
|
||||
this._createIsolatedContext('', true);
|
||||
|
||||
this._worldNameToContext.set('', this._runtime.createExecutionContext(this.domWindow(), this.domWindow(), {
|
||||
frameId: this._frameId,
|
||||
name: '',
|
||||
}));
|
||||
for (const [name, world] of this._frameTree._isolatedWorlds) {
|
||||
if (name)
|
||||
this._createIsolatedContext(name);
|
||||
@@ -718,6 +688,3 @@ function channelId(channel) {
|
||||
}
|
||||
|
||||
|
||||
var EXPORTED_SYMBOLS = ['FrameTree'];
|
||||
this.FrameTree = FrameTree;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
const { Helper } = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const { initialize } = ChromeUtils.import('chrome://juggler/content/content/main.js');
|
||||
const { Helper } = ChromeUtils.importESModule('chrome://juggler/content/Helper.js');
|
||||
const { initialize } = ChromeUtils.importESModule('chrome://juggler/content/content/main.js');
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const helper = new Helper();
|
||||
@@ -10,7 +10,7 @@ let sameProcessInstanceNumber = 0;
|
||||
|
||||
const topBrowingContextToAgents = new Map();
|
||||
|
||||
class JugglerFrameChild extends JSWindowActorChild {
|
||||
export class JugglerFrameChild extends JSWindowActorChild {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -83,4 +83,3 @@ class JugglerFrameChild extends JSWindowActorChild {
|
||||
receiveMessage() { }
|
||||
}
|
||||
|
||||
var EXPORTED_SYMBOLS = ['JugglerFrameChild'];
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
"use strict";
|
||||
|
||||
const { Helper } = ChromeUtils.importESModule('chrome://juggler/content/Helper.js');
|
||||
const { initialize } = ChromeUtils.importESModule('chrome://juggler/content/content/main.js');
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const helper = new Helper();
|
||||
|
||||
let sameProcessInstanceNumber = 0;
|
||||
|
||||
const topBrowingContextToAgents = new Map();
|
||||
|
||||
export class JugglerFrameChild extends JSWindowActorChild {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._eventListeners = [];
|
||||
}
|
||||
|
||||
handleEvent(aEvent) {
|
||||
const agents = this._agents();
|
||||
if (!agents)
|
||||
return;
|
||||
if (aEvent.type === 'DOMWillOpenModalDialog') {
|
||||
agents.channel.pause();
|
||||
return;
|
||||
}
|
||||
if (aEvent.type === 'DOMModalDialogClosed') {
|
||||
agents.channel.resumeSoon();
|
||||
return;
|
||||
}
|
||||
if (aEvent.target === this.document) {
|
||||
agents.pageAgent.onWindowEvent(aEvent);
|
||||
agents.frameTree.onWindowEvent(aEvent);
|
||||
}
|
||||
}
|
||||
|
||||
_agents() {
|
||||
return topBrowingContextToAgents.get(this.browsingContext.top);
|
||||
}
|
||||
|
||||
actorCreated() {
|
||||
this.actorName = `content::${this.browsingContext.browserId}/${this.browsingContext.id}/${++sameProcessInstanceNumber}`;
|
||||
|
||||
this._eventListeners.push(helper.addEventListener(this.contentWindow, 'load', event => {
|
||||
this._agents()?.pageAgent.onWindowEvent(event);
|
||||
}));
|
||||
|
||||
if (this.document.documentURI.startsWith('moz-extension://'))
|
||||
return;
|
||||
|
||||
// Child frame events will be forwarded to related top-level agents.
|
||||
if (this.browsingContext.parent)
|
||||
return;
|
||||
|
||||
let agents = topBrowingContextToAgents.get(this.browsingContext);
|
||||
if (!agents) {
|
||||
agents = initialize(this.browsingContext, this.docShell);
|
||||
topBrowingContextToAgents.set(this.browsingContext, agents);
|
||||
}
|
||||
agents.channel.bindToActor(this);
|
||||
agents.actor = this;
|
||||
}
|
||||
|
||||
didDestroy() {
|
||||
helper.removeListeners(this._eventListeners);
|
||||
|
||||
if (this.browsingContext.parent)
|
||||
return;
|
||||
|
||||
const agents = topBrowingContextToAgents.get(this.browsingContext);
|
||||
// The agents are already re-bound to a new actor.
|
||||
if (agents?.actor !== this)
|
||||
return;
|
||||
|
||||
topBrowingContextToAgents.delete(this.browsingContext);
|
||||
|
||||
agents.channel.resetTransport();
|
||||
agents.pageAgent.dispose();
|
||||
agents.frameTree.dispose();
|
||||
}
|
||||
|
||||
receiveMessage() { }
|
||||
}
|
||||
@@ -8,9 +8,9 @@ const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm');
|
||||
const {setTimeout} = ChromeUtils.import('resource://gre/modules/Timer.jsm');
|
||||
const {Helper} = ChromeUtils.importESModule('chrome://juggler/content/Helper.js');
|
||||
const {NetUtil} = ChromeUtils.importESModule('resource://gre/modules/NetUtil.sys.mjs');
|
||||
const {setTimeout} = ChromeUtils.importESModule('resource://gre/modules/Timer.sys.mjs');
|
||||
|
||||
const dragService = Cc["@mozilla.org/widget/dragservice;1"].getService(
|
||||
Ci.nsIDragService
|
||||
@@ -51,7 +51,7 @@ class WorkerData {
|
||||
}
|
||||
}
|
||||
|
||||
class PageAgent {
|
||||
export class PageAgent {
|
||||
constructor(browserChannel, frameTree) {
|
||||
this._browserChannel = browserChannel;
|
||||
this._browserPage = browserChannel.connect('page');
|
||||
@@ -549,7 +549,7 @@ class PageAgent {
|
||||
false /*aIgnoreRootScrollFrame*/,
|
||||
0.0 /*pressure*/,
|
||||
0 /*inputSource*/,
|
||||
false /*isDOMEventSynthesized*/,
|
||||
true /*isDOMEventSynthesized*/,
|
||||
false /*isWidgetEventSynthesized*/,
|
||||
0 /*buttons*/,
|
||||
win.windowUtils.DEFAULT_MOUSE_POINTER_ID /* pointerIdentifier */,
|
||||
@@ -575,7 +575,7 @@ class PageAgent {
|
||||
// We crash by using js-ctypes and dereferencing
|
||||
// a bad pointer. The crash should happen immediately
|
||||
// upon loading this frame script.
|
||||
const { ctypes } = ChromeUtils.import('resource://gre/modules/ctypes.jsm');
|
||||
const { ctypes } = ChromeUtils.importESModule('resource://gre/modules/ctypes.sys.mjs');
|
||||
ChromeUtils.privateNoteIntentionalCrash();
|
||||
const zero = new ctypes.intptr_t(8);
|
||||
const badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
|
||||
@@ -709,6 +709,3 @@ class PageAgent {
|
||||
}
|
||||
}
|
||||
|
||||
var EXPORTED_SYMBOLS = ['PageAgent'];
|
||||
this.PageAgent = PageAgent;
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
if (!this.Debugger) {
|
||||
// Worker has a Debugger defined already.
|
||||
const {addDebuggerToGlobal} = ChromeUtils.import("resource://gre/modules/jsdebugger.jsm", {});
|
||||
addDebuggerToGlobal(Components.utils.getGlobalForObject(this));
|
||||
const {addDebuggerToGlobal} = ChromeUtils.importESModule("resource://gre/modules/jsdebugger.sys.mjs");
|
||||
addDebuggerToGlobal(Components.utils.getGlobalForObject(globalThis));
|
||||
}
|
||||
|
||||
let lastId = 0;
|
||||
@@ -99,32 +99,6 @@ class Runtime {
|
||||
const executionContext = this.findExecutionContext(executionContextId);
|
||||
if (!executionContext)
|
||||
throw new Error('Failed to find execution context with id = ' + executionContextId);
|
||||
|
||||
// Hijack the utilityScript.evaluate function to evaluate in the main world
|
||||
if (
|
||||
ChromeUtils.camouGetBool('allowMainWorld', false) &&
|
||||
functionDeclaration.includes('utilityScript.evaluate') &&
|
||||
args.length >= 4 &&
|
||||
args[3].value &&
|
||||
typeof args[3].value === 'string' &&
|
||||
args[3].value.startsWith('mw:')) {
|
||||
ChromeUtils.camouDebug(`Evaluating in main world: ${args[3].value}`);
|
||||
const mainWorldScript = args[3].value.substring(3);
|
||||
|
||||
// Get the main world execution context
|
||||
const mainContext = executionContext.mainEquivalent;
|
||||
if (!mainContext) {
|
||||
throw new Error(`Main world injection is not enabled.`);
|
||||
}
|
||||
// Extract arguments for the main world function
|
||||
const functionArgs = args[5]?.value?.a || [];
|
||||
const exceptionDetails = {};
|
||||
const result = mainContext.executeInGlobal(mainWorldScript, functionArgs, exceptionDetails);
|
||||
if (!result)
|
||||
return {exceptionDetails};
|
||||
return {result};
|
||||
}
|
||||
|
||||
const exceptionDetails = {};
|
||||
let result = await executionContext.evaluateFunction(functionDeclaration, args, exceptionDetails);
|
||||
if (!result)
|
||||
@@ -190,7 +164,7 @@ class Runtime {
|
||||
emitEvent(this.events.onRuntimeError, {
|
||||
executionContext,
|
||||
message: message.errorMessage,
|
||||
stack: message.stack?.toString() || '',
|
||||
stack: message.stack.toString(),
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -308,11 +282,6 @@ class Runtime {
|
||||
return context;
|
||||
}
|
||||
|
||||
createMW(domWindow, contextGlobal) {
|
||||
const context = new MainWorldContext(this, domWindow, contextGlobal);
|
||||
return context;
|
||||
}
|
||||
|
||||
findExecutionContext(executionContextId) {
|
||||
const executionContext = this._executionContexts.get(executionContextId);
|
||||
if (!executionContext)
|
||||
@@ -337,68 +306,6 @@ class Runtime {
|
||||
}
|
||||
}
|
||||
|
||||
class MainWorldContext {
|
||||
constructor(runtime, domWindow, contextGlobal) {
|
||||
this._runtime = runtime;
|
||||
this._domWindow = domWindow;
|
||||
this._contextGlobal = contextGlobal;
|
||||
this._debuggee = runtime._debugger.addDebuggee(contextGlobal);
|
||||
}
|
||||
|
||||
_getResult(completionValue, exceptionDetails = {}) {
|
||||
if (!completionValue) {
|
||||
exceptionDetails.text = "Evaluation terminated";
|
||||
return {success: false, obj: null};
|
||||
}
|
||||
|
||||
if (completionValue.throw) {
|
||||
const result = this._debuggee.executeInGlobalWithBindings(`
|
||||
(function(error) {
|
||||
try {
|
||||
if (error instanceof Error) {
|
||||
return error.toString();
|
||||
}
|
||||
return String(error);
|
||||
} catch(e) {
|
||||
return "Unknown error occurred";
|
||||
}
|
||||
})(e)
|
||||
`, { e: completionValue.throw });
|
||||
|
||||
exceptionDetails.text = result.return || "Unknown error";
|
||||
return {success: false, obj: null};
|
||||
}
|
||||
|
||||
return {success: true, obj: completionValue.return};
|
||||
}
|
||||
|
||||
executeInGlobal(script, args = [], exceptionDetails = {}) {
|
||||
try {
|
||||
const wrappedScript = `
|
||||
(() => {
|
||||
let _s = (${script});
|
||||
let _r = typeof _s === 'function'
|
||||
? _s(${args.map(arg => JSON.stringify(arg)).join(', ')})
|
||||
: _s;
|
||||
return JSON.stringify({value: _r});
|
||||
})()
|
||||
`;
|
||||
|
||||
const result = this._debuggee.executeInGlobal(wrappedScript);
|
||||
|
||||
let {success, obj} = this._getResult(result, exceptionDetails);
|
||||
if (!success) {
|
||||
return {exceptionDetails};
|
||||
}
|
||||
return JSON.parse(obj);
|
||||
} catch (e) {
|
||||
exceptionDetails.text = e.message;
|
||||
exceptionDetails.stack = e.stack;
|
||||
return {exceptionDetails};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ExecutionContext {
|
||||
constructor(runtime, domWindow, contextGlobal, auxData) {
|
||||
this._runtime = runtime;
|
||||
@@ -431,8 +338,6 @@ class ExecutionContext {
|
||||
|
||||
return hasSymbol ? undefined : result;
|
||||
}).bind(null, JSON.stringify.bind(JSON))`).return;
|
||||
|
||||
this.mainEquivalent = undefined;
|
||||
}
|
||||
|
||||
id() {
|
||||
@@ -691,5 +596,5 @@ function emitEvent(event, ...args) {
|
||||
listener.call(null, ...args);
|
||||
}
|
||||
|
||||
var EXPORTED_SYMBOLS = ['Runtime'];
|
||||
this.Runtime = Runtime;
|
||||
// Export Runtime to global.
|
||||
globalThis.Runtime = Runtime;
|
||||
|
||||
@@ -2,20 +2,28 @@
|
||||
* 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/. */
|
||||
|
||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const {FrameTree} = ChromeUtils.import('chrome://juggler/content/content/FrameTree.js');
|
||||
const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js');
|
||||
const {PageAgent} = ChromeUtils.import('chrome://juggler/content/content/PageAgent.js');
|
||||
// Services is available as a global
|
||||
|
||||
// Load SimpleChannel and Runtime in content process's global.
|
||||
// NOTE: since these have to exist in both Worker and main threads, and we do
|
||||
// not know a way to load ES Modules in worker threads, we have to use the loadSubScript
|
||||
// utility instead.
|
||||
Services.scriptloader.loadSubScript('chrome://juggler/content/SimpleChannel.js');
|
||||
Services.scriptloader.loadSubScript('chrome://juggler/content/content/Runtime.js');
|
||||
|
||||
const {Helper} = ChromeUtils.importESModule('chrome://juggler/content/Helper.js');
|
||||
const {FrameTree} = ChromeUtils.importESModule('chrome://juggler/content/content/FrameTree.js');
|
||||
const {PageAgent} = ChromeUtils.importESModule('chrome://juggler/content/content/PageAgent.js');
|
||||
|
||||
const helper = new Helper();
|
||||
|
||||
function initialize(browsingContext, docShell) {
|
||||
export function initialize(browsingContext, docShell) {
|
||||
const data = { channel: undefined, pageAgent: undefined, frameTree: undefined, failedToOverrideTimezone: false };
|
||||
|
||||
const applySetting = {
|
||||
geolocation: (geolocation) => {
|
||||
if (geolocation) {
|
||||
docShell.setGeolocationOverride({
|
||||
browsingContext.setGeolocationServiceOverride({
|
||||
coords: {
|
||||
latitude: geolocation.latitude,
|
||||
longitude: geolocation.longitude,
|
||||
@@ -25,14 +33,12 @@ function initialize(browsingContext, docShell) {
|
||||
heading: NaN,
|
||||
speed: NaN,
|
||||
},
|
||||
address: null,
|
||||
timestamp: Date.now()
|
||||
timestamp: Date.now() + 24 * 60 * 60 * 1000, // Make sure it does not expire for a day.
|
||||
});
|
||||
} else {
|
||||
docShell.setGeolocationOverride(null);
|
||||
browsingContext.setGeolocationServiceOverride();
|
||||
}
|
||||
},
|
||||
|
||||
bypassCSP: (bypassCSP) => {
|
||||
docShell.bypassCSPEnabled = bypassCSP;
|
||||
},
|
||||
@@ -95,16 +101,20 @@ function initialize(browsingContext, docShell) {
|
||||
},
|
||||
|
||||
async awaitViewportDimensions({width, height}) {
|
||||
const win = docShell.domWindow;
|
||||
if (win.innerWidth === width && win.innerHeight === height)
|
||||
return;
|
||||
await new Promise(resolve => {
|
||||
const listener = helper.addEventListener(win, 'resize', () => {
|
||||
if (win.innerWidth === width && win.innerHeight === height) {
|
||||
helper.removeListeners([listener]);
|
||||
const listeners = [];
|
||||
const check = () => {
|
||||
helper.removeListeners(listeners);
|
||||
if (docShell.domWindow.innerWidth === width && docShell.domWindow.innerHeight === height) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
});
|
||||
// Note: "domWindow" listeners are often removed upon navigation, as specced.
|
||||
// To survive viewport changes across navigations, re-install listeners upon commit.
|
||||
listeners.push(helper.addEventListener(docShell.domWindow, 'resize', check));
|
||||
listeners.push(helper.addEventListener(data.frameTree, 'navigationcommitted', check));
|
||||
};
|
||||
check();
|
||||
});
|
||||
},
|
||||
|
||||
@@ -114,6 +124,3 @@ function initialize(browsingContext, docShell) {
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
var EXPORTED_SYMBOLS = ['initialize'];
|
||||
this.initialize = initialize;
|
||||
|
||||
@@ -9,15 +9,18 @@ juggler.jar:
|
||||
|
||||
content/Helper.js (Helper.js)
|
||||
content/NetworkObserver.js (NetworkObserver.js)
|
||||
content/ChannelEventSink.sys.mjs (ChannelEventSink.sys.mjs)
|
||||
content/TargetRegistry.js (TargetRegistry.js)
|
||||
content/SimpleChannel.js (SimpleChannel.js)
|
||||
content/JugglerFrameParent.jsm (JugglerFrameParent.jsm)
|
||||
content/JugglerFrameParent.sys.mjs (JugglerFrameParent.sys.mjs)
|
||||
content/protocol/PrimitiveTypes.js (protocol/PrimitiveTypes.js)
|
||||
content/protocol/Protocol.js (protocol/Protocol.js)
|
||||
content/protocol/Dispatcher.js (protocol/Dispatcher.js)
|
||||
content/protocol/PageHandler.js (protocol/PageHandler.js)
|
||||
content/protocol/BrowserHandler.js (protocol/BrowserHandler.js)
|
||||
content/content/JugglerFrameChild.jsm (content/JugglerFrameChild.jsm)
|
||||
content/JugglerFrameChild.jsm (content/JugglerFrameChild.jsm)
|
||||
content/JugglerFrameChild.sys.mjs (content/JugglerFrameChild.sys.mjs)
|
||||
content/content/main.js (content/main.js)
|
||||
content/content/FrameTree.js (content/FrameTree.js)
|
||||
content/content/PageAgent.js (content/PageAgent.js)
|
||||
|
||||
@@ -4,15 +4,16 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
|
||||
const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js");
|
||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const {PageHandler} = ChromeUtils.import("chrome://juggler/content/protocol/PageHandler.js");
|
||||
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
const {AddonManager} = ChromeUtils.importESModule("resource://gre/modules/AddonManager.sys.mjs");
|
||||
const {XPIProvider} = ChromeUtils.importESModule("resource://gre/modules/addons/XPIProvider.sys.mjs");
|
||||
const {TargetRegistry} = ChromeUtils.importESModule("chrome://juggler/content/TargetRegistry.js");
|
||||
const {Helper} = ChromeUtils.importESModule('chrome://juggler/content/Helper.js');
|
||||
const {PageHandler} = ChromeUtils.importESModule("chrome://juggler/content/protocol/PageHandler.js");
|
||||
const {AppConstants} = ChromeUtils.importESModule("resource://gre/modules/AppConstants.sys.mjs");
|
||||
|
||||
const helper = new Helper();
|
||||
|
||||
class BrowserHandler {
|
||||
export class BrowserHandler {
|
||||
constructor(session, dispatcher, targetRegistry, startCompletePromise, onclose) {
|
||||
this._session = session;
|
||||
this._dispatcher = dispatcher;
|
||||
@@ -147,6 +148,10 @@ class BrowserHandler {
|
||||
]);
|
||||
}
|
||||
await this._startCompletePromise;
|
||||
await Promise.all([
|
||||
...XPIProvider.startupPromises,
|
||||
...XPIProvider.enabledAddonsStartupPromises,
|
||||
]);
|
||||
this._onclose();
|
||||
Services.startup.quit(Ci.nsIAppStartup.eForceQuit);
|
||||
}
|
||||
@@ -166,7 +171,9 @@ class BrowserHandler {
|
||||
['Browser.clearCache']() {
|
||||
// Clearing only the context cache does not work: https://bugzilla.mozilla.org/show_bug.cgi?id=1819147
|
||||
Services.cache2.clear();
|
||||
ChromeUtils.clearStyleSheetCache();
|
||||
ChromeUtils.clearResourceCache({
|
||||
types: ["stylesheet"],
|
||||
});
|
||||
}
|
||||
|
||||
['Browser.setHTTPCredentials']({browserContextId, credentials}) {
|
||||
@@ -220,7 +227,7 @@ class BrowserHandler {
|
||||
}
|
||||
|
||||
async ['Browser.setContrast']({browserContextId, contrast}) {
|
||||
return; // TODO: Implement
|
||||
await this._targetRegistry.browserContextForId(browserContextId).setContrast(nullToUndefined(contrast));
|
||||
}
|
||||
|
||||
async ['Browser.setVideoRecordingOptions']({browserContextId, options}) {
|
||||
@@ -313,6 +320,3 @@ async function waitForWindowClosed(browserWindow) {
|
||||
function nullToUndefined(value) {
|
||||
return value === null ? undefined : value;
|
||||
}
|
||||
|
||||
var EXPORTED_SYMBOLS = ['BrowserHandler'];
|
||||
this.BrowserHandler = BrowserHandler;
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
* 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/. */
|
||||
|
||||
const {protocol, checkScheme} = ChromeUtils.import("chrome://juggler/content/protocol/Protocol.js");
|
||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const {protocol} = ChromeUtils.importESModule("chrome://juggler/content/protocol/Protocol.js");
|
||||
const {checkScheme} = ChromeUtils.importESModule("chrome://juggler/content/protocol/PrimitiveTypes.js");
|
||||
const {Helper} = ChromeUtils.importESModule('chrome://juggler/content/Helper.js');
|
||||
|
||||
const helper = new Helper();
|
||||
// Camoufox: Exclude redundant internal events from logs.
|
||||
const EXCLUDED_DBG = ['Page.navigationStarted', 'Page.frameAttached', 'Runtime.executionContextCreated', 'Runtime.console', 'Page.navigationAborted', 'Page.eventFired'];
|
||||
|
||||
class Dispatcher {
|
||||
export class Dispatcher {
|
||||
/**
|
||||
* @param {Connection} connection
|
||||
*/
|
||||
@@ -46,11 +45,6 @@ class Dispatcher {
|
||||
|
||||
async _dispatch(event) {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
if (ChromeUtils.isCamouDebug())
|
||||
ChromeUtils.camouDebug(`[${new Date().toLocaleString()}]`
|
||||
+ `\nReceived message: ${safeJsonStringify(data)}`);
|
||||
|
||||
const id = data.id;
|
||||
const sessionId = data.sessionId;
|
||||
delete data.sessionId;
|
||||
@@ -93,13 +87,6 @@ class Dispatcher {
|
||||
|
||||
_emitEvent(sessionId, eventName, params) {
|
||||
const [domain, eName] = eventName.split('.');
|
||||
|
||||
// Camoufox: Log internal events
|
||||
if (ChromeUtils.isCamouDebug() && !EXCLUDED_DBG.includes(eventName) && domain !== 'Network') {
|
||||
ChromeUtils.camouDebug(`[${new Date().toLocaleString()}]`
|
||||
+ `\nInternal event: ${eventName}\nParams: ${JSON.stringify(params, null, 2)}`);
|
||||
}
|
||||
|
||||
const scheme = protocol.domains[domain] ? protocol.domains[domain].events[eName] : null;
|
||||
if (!scheme)
|
||||
throw new Error(`ERROR: event '${eventName}' is not supported`);
|
||||
@@ -146,48 +133,3 @@ class ProtocolSession {
|
||||
return await this._handler[method](params);
|
||||
}
|
||||
}
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['Dispatcher'];
|
||||
this.Dispatcher = Dispatcher;
|
||||
|
||||
|
||||
function formatDate(date) {
|
||||
const pad = (num) => String(num).padStart(2, '0');
|
||||
return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
||||
}
|
||||
|
||||
function truncateObject(obj, maxDepth = 8, maxLength = 100) {
|
||||
if (maxDepth < 0) return '[Max Depth Reached]';
|
||||
|
||||
if (typeof obj !== 'object' || obj === null) {
|
||||
return typeof obj === 'string' ? truncateString(obj, maxLength) : obj;
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.slice(0, 10).map(item => truncateObject(item, maxDepth - 1, maxLength));
|
||||
}
|
||||
|
||||
const truncated = {};
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (Object.keys(truncated).length >= 10) {
|
||||
truncated['...'] = '[Truncated]';
|
||||
break;
|
||||
}
|
||||
truncated[key] = truncateObject(value, maxDepth - 1, maxLength);
|
||||
}
|
||||
return truncated;
|
||||
}
|
||||
|
||||
function truncateString(str, maxLength) {
|
||||
if (str.length <= maxLength) return str;
|
||||
ChromeUtils.camouDebug(`String length: ${str.length}`);
|
||||
return str.substr(0, maxLength) + '... [truncated]';
|
||||
}
|
||||
|
||||
function safeJsonStringify(data) {
|
||||
try {
|
||||
return JSON.stringify(truncateObject(data), null, 2);
|
||||
} catch (error) {
|
||||
return `[Unable to stringify: ${error.message}]`;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
/* 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";
|
||||
|
||||
const {Helper, EventWatcher} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm');
|
||||
const {NetworkObserver, PageNetwork} = ChromeUtils.import('chrome://juggler/content/NetworkObserver.js');
|
||||
const {PageTarget} = ChromeUtils.import('chrome://juggler/content/TargetRegistry.js');
|
||||
const {setTimeout} = ChromeUtils.import('resource://gre/modules/Timer.jsm');
|
||||
const {Helper, EventWatcher} = ChromeUtils.importESModule('chrome://juggler/content/Helper.js');
|
||||
const {NetUtil} = ChromeUtils.importESModule('resource://gre/modules/NetUtil.sys.mjs');
|
||||
const {NetworkObserver, PageNetwork} = ChromeUtils.importESModule('chrome://juggler/content/NetworkObserver.js');
|
||||
const {PageTarget} = ChromeUtils.importESModule('chrome://juggler/content/TargetRegistry.js');
|
||||
const {setTimeout} = ChromeUtils.importESModule('resource://gre/modules/Timer.sys.mjs');
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
@@ -65,7 +65,7 @@ class WorkerHandler {
|
||||
}
|
||||
}
|
||||
|
||||
class PageHandler {
|
||||
export class PageHandler {
|
||||
constructor(target, session, contentChannel) {
|
||||
this._session = session;
|
||||
this._contentChannel = contentChannel;
|
||||
@@ -80,17 +80,7 @@ class PageHandler {
|
||||
}
|
||||
|
||||
this._isDragging = false;
|
||||
|
||||
// Camoufox: set a random default cursor position
|
||||
let random_val = (max_val) => Math.floor(Math.random() * max_val);
|
||||
|
||||
// Try to fetch the viewport size
|
||||
this._defaultCursorPos = {
|
||||
x: random_val(this._pageTarget._viewportSize?.width || 1280),
|
||||
y: random_val(this._pageTarget._viewportSize?.height || 720),
|
||||
};
|
||||
this._lastMousePosition = { ...this._defaultCursorPos };
|
||||
this._lastTrackedPos = { ...this._defaultCursorPos };
|
||||
this._lastMousePosition = { x: 0, y: 0 };
|
||||
|
||||
this._reportedFrameIds = new Set();
|
||||
this._networkEventsForUnreportedFrameIds = new Map();
|
||||
@@ -250,6 +240,10 @@ class PageHandler {
|
||||
await this._pageTarget.setViewportSize(viewportSize === null ? undefined : viewportSize);
|
||||
}
|
||||
|
||||
async ['Page.setZoom']({zoom}) {
|
||||
await this._pageTarget.setZoom(zoom);
|
||||
}
|
||||
|
||||
async ['Runtime.evaluate'](options) {
|
||||
return await this._contentPage.send('evaluate', options);
|
||||
}
|
||||
@@ -308,10 +302,11 @@ class PageHandler {
|
||||
return await this._contentPage.send('setFileInputFiles', options);
|
||||
}
|
||||
|
||||
async ['Page.setEmulatedMedia']({colorScheme, type, reducedMotion, forcedColors}) {
|
||||
async ['Page.setEmulatedMedia']({colorScheme, type, reducedMotion, forcedColors, contrast}) {
|
||||
this._pageTarget.setColorScheme(colorScheme || null);
|
||||
this._pageTarget.setReducedMotion(reducedMotion || null);
|
||||
this._pageTarget.setForcedColors(forcedColors || null);
|
||||
this._pageTarget.setContrast(contrast || null);
|
||||
this._pageTarget.setEmulatedMedia(type);
|
||||
}
|
||||
|
||||
@@ -432,14 +427,6 @@ class PageHandler {
|
||||
});
|
||||
unsubscribe();
|
||||
|
||||
if (ChromeUtils.camouGetBool('memorysaver', false)) {
|
||||
ChromeUtils.camouDebug('Clearing all memory...');
|
||||
Services.obs.notifyObservers(null, "child-gc-request");
|
||||
Cu.forceGC();
|
||||
Services.obs.notifyObservers(null, "child-cc-request");
|
||||
Cu.forceCC();
|
||||
}
|
||||
|
||||
return {
|
||||
navigationId: sameDocumentNavigation ? null : navigationId,
|
||||
};
|
||||
@@ -518,43 +505,28 @@ class PageHandler {
|
||||
await helper.awaitTopic('apz-repaints-flushed');
|
||||
|
||||
const watcher = new EventWatcher(this._pageEventSink, types, this._pendingEventWatchers);
|
||||
const sendMouseEvent = async (eventType, eventX, eventY) => {
|
||||
const promises = [];
|
||||
for (const type of types) {
|
||||
// This dispatches to the renderer synchronously.
|
||||
const jugglerEventId = win.windowUtils.jugglerSendMouseEvent(
|
||||
eventType,
|
||||
eventX + boundingBox.left,
|
||||
eventY + boundingBox.top,
|
||||
type,
|
||||
x + boundingBox.left,
|
||||
y + boundingBox.top,
|
||||
button,
|
||||
clickCount,
|
||||
modifiers,
|
||||
false /* aIgnoreRootScrollFrame */,
|
||||
0.0 /* pressure */,
|
||||
0 /* inputSource */,
|
||||
false /* isDOMEventSynthesized */,
|
||||
true /* isDOMEventSynthesized */,
|
||||
false /* isWidgetEventSynthesized */,
|
||||
buttons,
|
||||
win.windowUtils.DEFAULT_MOUSE_POINTER_ID /* pointerIdentifier */,
|
||||
false /* disablePointerEvent */
|
||||
);
|
||||
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);
|
||||
}
|
||||
promises.push(watcher.ensureEvent(type, eventObject => eventObject.jugglerEventId === jugglerEventId));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
await watcher.dispose();
|
||||
};
|
||||
|
||||
@@ -575,15 +547,15 @@ class PageHandler {
|
||||
// NOTE: since this won't go inside the renderer, there's no need to wait for ACK.
|
||||
win.windowUtils.sendMouseEvent(
|
||||
'mousemove',
|
||||
this._defaultCursorPos.x,
|
||||
this._defaultCursorPos.y,
|
||||
0 /* x */,
|
||||
0 /* y */,
|
||||
button,
|
||||
clickCount,
|
||||
modifiers,
|
||||
false /* aIgnoreRootScrollFrame */,
|
||||
0.0 /* pressure */,
|
||||
0 /* inputSource */,
|
||||
false /* isDOMEventSynthesized */,
|
||||
true /* isDOMEventSynthesized */,
|
||||
false /* isWidgetEventSynthesized */,
|
||||
buttons,
|
||||
win.windowUtils.DEFAULT_MOUSE_POINTER_ID /* pointerIdentifier */,
|
||||
@@ -612,7 +584,6 @@ 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
|
||||
@@ -720,6 +691,3 @@ class PageHandler {
|
||||
return await worker.sendMessage(JSON.parse(message));
|
||||
}
|
||||
}
|
||||
|
||||
var EXPORTED_SYMBOLS = ['PageHandler'];
|
||||
this.PageHandler = PageHandler;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 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/. */
|
||||
|
||||
const t = {};
|
||||
export const t = {};
|
||||
|
||||
t.String = function(x, details = {}, path = ['<root>']) {
|
||||
if (typeof x === 'string' || typeof x === 'String')
|
||||
@@ -96,7 +96,7 @@ function beauty(path, obj) {
|
||||
return `property "${path.join('.')}" - ${JSON.stringify(obj, null, 2)}`;
|
||||
}
|
||||
|
||||
function checkScheme(scheme, x, details = {}, path = ['<root>']) {
|
||||
export function checkScheme(scheme, x, details = {}, path = ['<root>']) {
|
||||
if (!scheme)
|
||||
throw new Error(`ILLDEFINED SCHEME: ${path.join('.')}`);
|
||||
if (typeof scheme === 'object') {
|
||||
@@ -142,6 +142,3 @@ test(t.Either(t.String, t.Number), {});
|
||||
|
||||
*/
|
||||
|
||||
this.t = t;
|
||||
this.checkScheme = checkScheme;
|
||||
this.EXPORTED_SYMBOLS = ['t', 'checkScheme'];
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 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/. */
|
||||
|
||||
const {t, checkScheme} = ChromeUtils.import('chrome://juggler/content/protocol/PrimitiveTypes.js');
|
||||
const {t} = ChromeUtils.importESModule('chrome://juggler/content/protocol/PrimitiveTypes.js');
|
||||
|
||||
// Protocol-specific types.
|
||||
const browserTypes = {};
|
||||
@@ -800,6 +800,11 @@ const Page = {
|
||||
viewportSize: t.Nullable(pageTypes.Size),
|
||||
},
|
||||
},
|
||||
'setZoom': {
|
||||
params: {
|
||||
zoom: t.Number,
|
||||
},
|
||||
},
|
||||
'bringToFront': {
|
||||
params: {
|
||||
},
|
||||
@@ -810,6 +815,7 @@ const Page = {
|
||||
colorScheme: t.Optional(t.Enum(['dark', 'light', 'no-preference'])),
|
||||
reducedMotion: t.Optional(t.Enum(['reduce', 'no-preference'])),
|
||||
forcedColors: t.Optional(t.Enum(['active', 'none'])),
|
||||
contrast: t.Optional(t.Enum(['less', 'more', 'custom', 'no-preference'])),
|
||||
},
|
||||
},
|
||||
'setCacheDisabled': {
|
||||
@@ -1012,8 +1018,6 @@ const Accessibility = {
|
||||
}
|
||||
}
|
||||
|
||||
this.protocol = {
|
||||
export const protocol = {
|
||||
domains: {Browser, Heap, Page, Runtime, Network, Accessibility},
|
||||
};
|
||||
this.checkScheme = checkScheme;
|
||||
this.EXPORTED_SYMBOLS = ['protocol', 'checkScheme'];
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "nsScreencastService.h"
|
||||
|
||||
#include "gfxPlatform.h"
|
||||
#include "ScreencastEncoder.h"
|
||||
#include "HeadlessWidget.h"
|
||||
#include "HeadlessWindowCapturer.h"
|
||||
@@ -343,10 +344,17 @@ nsresult nsScreencastService::StartVideoRecording(nsIScreencastServiceClient* aC
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
gfx::IntMargin margin;
|
||||
auto bounds = widget->GetScreenBounds().ToUnknownRect();
|
||||
// Screen bounds is the widget location on screen.
|
||||
auto screenBounds = widget->GetScreenBounds().ToUnknownRect();
|
||||
// Client bounds is the content location, in terms of parent widget.
|
||||
// To use it, we need to translate it to screen coordinates first.
|
||||
auto clientBounds = widget->GetClientBounds().ToUnknownRect();
|
||||
for (auto parent = widget->GetParent(); parent != nullptr; parent = parent->GetParent()) {
|
||||
auto pb = parent->GetClientBounds().ToUnknownRect();
|
||||
clientBounds.MoveBy(pb.X(), pb.Y());
|
||||
}
|
||||
// Crop the image to exclude frame (if any).
|
||||
margin = bounds - clientBounds;
|
||||
margin = screenBounds - clientBounds;
|
||||
// Crop the image to exclude controls.
|
||||
margin.top += offsetTop;
|
||||
|
||||
|
||||
@@ -0,0 +1,596 @@
|
||||
# Firefox Patch Upgrading Guide for LLMs
|
||||
|
||||
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.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Understanding the Patch System](#understanding-the-patch-system)
|
||||
2. [Preparation](#preparation)
|
||||
3. [General Workflow](#general-workflow)
|
||||
4. [Fixing Common Reject Types](#fixing-common-reject-types)
|
||||
5. [Context Patch Merging](#context-patch-merging) *(Historical - skip for future updates)*
|
||||
6. [Testing and Validation](#testing-and-validation)
|
||||
7. [Best Practices](#best-practices)
|
||||
|
||||
---
|
||||
|
||||
## Understanding the Patch System
|
||||
|
||||
### Patch Categories
|
||||
|
||||
All Camoufox patches are in the `patches/` directory:
|
||||
|
||||
- **Core Patches**: `0-playwright.patch`, `1-leak-fixes.patch`, etc.
|
||||
- **Feature Patches**: `webrtc-ip-spoofing.patch`, `anti-font-fingerprinting.patch`, etc.
|
||||
- All patches now include per-user-context (per-Playwright-context) support built-in
|
||||
|
||||
**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.
|
||||
|
||||
### Key Infrastructure Files
|
||||
|
||||
- **RoverfoxStorageManager.cpp/h**: Thread-safe key-value storage for per-context data
|
||||
- **Manager Classes**: FontSpacingSeedManager, WebRTCIPManager, etc.
|
||||
- **Window.webidl**: Exposes JavaScript APIs to Playwright
|
||||
|
||||
---
|
||||
|
||||
## Preparation
|
||||
|
||||
### 1. Reset to Clean State
|
||||
|
||||
**IMPORTANT**: Always use `make clean` to reset to fresh Firefox source:
|
||||
|
||||
```bash
|
||||
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.
|
||||
|
||||
### 2. Identify Patches to Update
|
||||
|
||||
Check which patches exist:
|
||||
|
||||
```bash
|
||||
ls patches/*.patch
|
||||
```
|
||||
|
||||
### 3. Understand Patch Dependencies
|
||||
|
||||
Some patches depend on others being applied first:
|
||||
- `1-leak-fixes.patch` requires `0-playwright.patch`
|
||||
- Check the Makefile or patch comments for dependency chains
|
||||
|
||||
---
|
||||
|
||||
## General Workflow
|
||||
|
||||
### Step 1: Apply Base Patch and Identify Rejects
|
||||
|
||||
```bash
|
||||
cd camoufox-<version>
|
||||
patch -p1 < ../patches/patch-name.patch
|
||||
```
|
||||
|
||||
Find reject files:
|
||||
|
||||
```bash
|
||||
find . -name '*.rej' -type f
|
||||
```
|
||||
|
||||
### Step 2: Analyze Each Reject File
|
||||
|
||||
Read the reject file to understand what failed:
|
||||
|
||||
```bash
|
||||
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)
|
||||
|
||||
### Step 3: Locate the Correct Position in Firefox Code
|
||||
|
||||
The line numbers in rejects are usually wrong for the new Firefox version. You need to:
|
||||
|
||||
1. **Search for unique context** around the reject
|
||||
2. **Understand what the patch is doing**
|
||||
3. **Find equivalent location** in new Firefox code
|
||||
|
||||
### Step 4: Apply Changes Manually
|
||||
|
||||
Use the Edit tool to apply the rejected changes to the correct location.
|
||||
|
||||
### Step 5: Remove Reject Files
|
||||
|
||||
After fixing all rejects:
|
||||
|
||||
```bash
|
||||
rm -f path/to/file.cpp.rej
|
||||
find . -name '*.rej' -type f # Verify all removed
|
||||
```
|
||||
|
||||
### Step 6: Generate Updated Patch
|
||||
|
||||
```bash
|
||||
# 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
|
||||
```
|
||||
|
||||
### Step 7: Verify Patch Applies Cleanly
|
||||
|
||||
```bash
|
||||
cd ..
|
||||
make clean
|
||||
cd camoufox-<version>
|
||||
patch -p1 < ../patches/patch-name.patch
|
||||
find . -name '*.rej' -type f # Should return nothing
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fixing Common Reject Types
|
||||
|
||||
### Type 1: Include Directive Rejects
|
||||
|
||||
**Symptom**: Reject shows failed `#include` additions
|
||||
|
||||
**Example Reject**:
|
||||
```
|
||||
@@ -325,6 +325,7 @@
|
||||
#include "xpcpublic.h"
|
||||
|
||||
+#include "WebRTCIPManager.h"
|
||||
#include "nsDocShell.h"
|
||||
```
|
||||
|
||||
**How to Fix**:
|
||||
|
||||
1. Read the actual file to find the includes section
|
||||
2. Search for nearby includes (e.g., `xpcpublic.h`)
|
||||
3. Add the new include in the appropriate location
|
||||
4. Firefox include order: system headers, then Mozilla headers, alphabetically within groups
|
||||
|
||||
**Example**:
|
||||
|
||||
```cpp
|
||||
// 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"
|
||||
```
|
||||
|
||||
### Type 2: Function Signature Changes
|
||||
|
||||
**Symptom**: Reject shows function call with changed parameters
|
||||
|
||||
**Example Reject**:
|
||||
```
|
||||
- mouseOrPointerEvent.mButton = aButton;
|
||||
+ mouseOrPointerEvent.mJugglerEventId = aMouseEventData.mJugglerEventId;
|
||||
```
|
||||
|
||||
**Common Causes**:
|
||||
- Firefox refactored the API
|
||||
- Parameters moved from individual args to struct/data object
|
||||
- Parameter order changed
|
||||
|
||||
**How to Fix**:
|
||||
|
||||
1. Search for the function definition in Firefox source
|
||||
2. Understand the new API structure
|
||||
3. Port the patch logic to the new API
|
||||
|
||||
**Example - Firefox 146 Mouse Event Refactoring**:
|
||||
|
||||
Old Firefox 144 API (individual parameters):
|
||||
```cpp
|
||||
void SynthesizeMouseEvent(int x, int y, int button, ...)
|
||||
```
|
||||
|
||||
New Firefox 146 API (structured data):
|
||||
```cpp
|
||||
void SynthesizeMouseEvent(SynthesizeMouseEventData& aData,
|
||||
SynthesizeMouseEventOptions& aOptions)
|
||||
```
|
||||
|
||||
Port the patch:
|
||||
```cpp
|
||||
// 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;
|
||||
```
|
||||
|
||||
### Type 3: Missing Context - Code Moved
|
||||
|
||||
**Symptom**: Reject shows context that doesn't exist in the file
|
||||
|
||||
**How to Fix**:
|
||||
|
||||
1. Use grep to search for unique function names or variables in the reject
|
||||
2. Find where Firefox moved the code
|
||||
3. Apply the patch to the new location
|
||||
|
||||
```bash
|
||||
# Search across the codebase
|
||||
grep -r "FunctionName" camoufox-<version>/ --include="*.cpp"
|
||||
```
|
||||
|
||||
### Type 4: New Parameter Added to Function Calls
|
||||
|
||||
**Symptom**: Reject shows function call, but Firefox added/removed parameters
|
||||
|
||||
**Example - MakeTextRun userContextId**:
|
||||
|
||||
Old call:
|
||||
```cpp
|
||||
MakeTextRun(text, len, drawTarget, appUnitsPerDevPixel, flags, recorder);
|
||||
```
|
||||
|
||||
New Firefox expects:
|
||||
```cpp
|
||||
MakeTextRun(text, len, drawTarget, appUnitsPerDevPixel, flags, recorder, userContextId);
|
||||
```
|
||||
|
||||
**How to Fix**:
|
||||
|
||||
1. Extract userContextId from available context (Document, PresContext, etc.)
|
||||
2. Add proper extraction code before the call
|
||||
3. Pass userContextId as the last parameter
|
||||
|
||||
**Standard userContextId Extraction Pattern**:
|
||||
|
||||
```cpp
|
||||
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);
|
||||
```
|
||||
|
||||
### Type 5: Line Number Shifts (No Code Changes)
|
||||
|
||||
**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.
|
||||
|
||||
---
|
||||
|
||||
## Context Patch Merging (Historical - Not Applicable for Future Updates)
|
||||
|
||||
**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.
|
||||
|
||||
### Historical Goal
|
||||
|
||||
Merge all changes from `*.context.patch` into the corresponding base patch so there's only one comprehensive patch file.
|
||||
|
||||
### Historical Example: font-fingerprinting.context.patch → anti-font-fingerprinting.patch
|
||||
|
||||
### Workflow
|
||||
|
||||
1. **Reset to clean Firefox**:
|
||||
```bash
|
||||
make clean
|
||||
```
|
||||
|
||||
2. **Apply base patch first**:
|
||||
```bash
|
||||
cd camoufox-<version>
|
||||
patch -p1 < ../patches/anti-font-fingerprinting.patch
|
||||
```
|
||||
|
||||
3. **Apply context patch on top**:
|
||||
```bash
|
||||
patch -p1 < ../font-fingerprinting.context.patch
|
||||
```
|
||||
|
||||
4. **Fix any rejects** (usually include conflicts since base patch may have some overlapping changes)
|
||||
|
||||
5. **Generate combined patch**:
|
||||
```bash
|
||||
# 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
|
||||
```
|
||||
|
||||
6. **Verify combined patch**:
|
||||
```bash
|
||||
cd ..
|
||||
make clean
|
||||
cd camoufox-<version>
|
||||
patch -p1 < ../patches/anti-font-fingerprinting.patch
|
||||
find . -name '*.rej' -type f # Should be empty
|
||||
```
|
||||
|
||||
### What Context Patches Add
|
||||
|
||||
Context patches typically add:
|
||||
|
||||
1. **Manager classes** (e.g., FontSpacingSeedManager, WebRTCIPManager):
|
||||
- Store per-context settings using RoverfoxStorageManager
|
||||
- Provide WebIDL-compatible enable/disable checks
|
||||
- Handle self-destructing functions
|
||||
|
||||
2. **Window.webidl functions**:
|
||||
- JavaScript APIs exposed to Playwright
|
||||
- Examples: `setFontSpacingSeed()`, `setWebRTCIPv4()`
|
||||
|
||||
3. **nsGlobalWindowInner.cpp implementations**:
|
||||
- Extract userContextId from window/document/docshell
|
||||
- Call manager classes
|
||||
- Self-destruct logic (remove function after first use)
|
||||
|
||||
4. **Core logic changes**:
|
||||
- Replace global config (MaskConfig) with per-context manager
|
||||
- Pass userContextId through call chains
|
||||
- Query manager for per-context values
|
||||
|
||||
---
|
||||
|
||||
## Testing and Validation
|
||||
|
||||
### Minimal Verification
|
||||
|
||||
After updating a patch, always verify:
|
||||
|
||||
1. **Patch applies cleanly**:
|
||||
```bash
|
||||
make clean
|
||||
cd camoufox-<version>
|
||||
patch -p1 < ../patches/patch-name.patch
|
||||
find . -name '*.rej' -type f
|
||||
```
|
||||
|
||||
2. **No reject files remain**
|
||||
|
||||
3. **Build compiles** (if feasible):
|
||||
```bash
|
||||
cd ..
|
||||
make build
|
||||
```
|
||||
|
||||
### Full Testing
|
||||
|
||||
For critical patches, test with actual Playwright scenarios after building.
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### DO:
|
||||
|
||||
1. ✅ **Always use `make clean`** to reset Firefox source
|
||||
2. ✅ **Read and understand** what the patch is trying to do before fixing rejects
|
||||
3. ✅ **Search for API changes** in Firefox release notes when functions have changed
|
||||
4. ✅ **Use grep/search** extensively to find where code moved
|
||||
5. ✅ **Extract userContextId properly** using the standard pattern
|
||||
6. ✅ **Test patches apply cleanly** before considering them done
|
||||
7. ✅ **Keep commits atomic** - one patch fix per session
|
||||
8. ✅ **Document major API changes** you discover
|
||||
|
||||
### DON'T:
|
||||
|
||||
1. ❌ **Don't use `git reset` or `git clean`** on Firefox source directory
|
||||
2. ❌ **Don't leave TODO comments** - fix things properly as you go
|
||||
3. ❌ **Don't guess parameter values** - extract them properly or investigate
|
||||
4. ❌ **Don't skip verification** - always test the patch applies cleanly
|
||||
5. ❌ **Don't batch multiple patch updates** - do them one at a time
|
||||
6. ❌ **Don't assume line numbers are correct** in reject files
|
||||
7. ❌ **Don't ignore warnings** during patch application
|
||||
|
||||
### Common Pitfalls
|
||||
|
||||
1. **Assuming reject line numbers are accurate**: They're usually wrong in new Firefox versions
|
||||
2. **Not understanding API changes**: Firefox refactors often - read the new code
|
||||
3. **Forgetting to add new files**: Use `git add` before generating patch
|
||||
4. **Not testing on clean source**: Always verify with `make clean`
|
||||
5. **Leaving reject files**: Remove all `.rej` files after fixing
|
||||
|
||||
---
|
||||
|
||||
## Example: Complete Patch Update Session
|
||||
|
||||
Here's a complete example of updating `0-playwright.patch` from Firefox 144 to Firefox 146:
|
||||
|
||||
### 1. Reset and Apply
|
||||
|
||||
```bash
|
||||
make clean
|
||||
cd camoufox-146.0.1-beta.25
|
||||
patch -p1 < ../patches/0-playwright.patch
|
||||
```
|
||||
|
||||
### 2. Find Rejects
|
||||
|
||||
```bash
|
||||
find . -name '*.rej' -type f
|
||||
```
|
||||
|
||||
Output shows 20 reject files.
|
||||
|
||||
### 3. Analyze First Reject
|
||||
|
||||
```bash
|
||||
cat dom/base/Navigator.cpp.rej
|
||||
```
|
||||
|
||||
Shows parameter order changed in `GetAcceptLanguages`.
|
||||
|
||||
### 4. Fix the Reject
|
||||
|
||||
Search for the function in the actual file, understand the new signature, apply changes manually.
|
||||
|
||||
### 5. Repeat for All Rejects
|
||||
|
||||
Work through each reject systematically.
|
||||
|
||||
### 6. Discover API Change
|
||||
|
||||
Firefox 146 refactored mouse events from individual parameters to `SynthesizeMouseEventData` and `SynthesizeMouseEventOptions`. Port all mouse event logic to new API.
|
||||
|
||||
### 7. Remove Rejects
|
||||
|
||||
```bash
|
||||
rm -f dom/base/Navigator.cpp.rej dom/base/Element.cpp.rej ...
|
||||
find . -name '*.rej' -type f # Verify empty
|
||||
```
|
||||
|
||||
### 8. Generate New Patch
|
||||
|
||||
```bash
|
||||
git diff --binary > /tmp/0-playwright.patch
|
||||
cp /tmp/0-playwright.patch ../patches/0-playwright.patch
|
||||
```
|
||||
|
||||
### 9. Verify
|
||||
|
||||
```bash
|
||||
cd ..
|
||||
make clean
|
||||
cd camoufox-146.0.1-beta.25
|
||||
patch -p1 < ../patches/0-playwright.patch
|
||||
find . -name '*.rej' -type f # Should be empty
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Appendix: Firefox Source Navigation
|
||||
|
||||
### Finding Files
|
||||
|
||||
```bash
|
||||
# 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"
|
||||
```
|
||||
|
||||
### Understanding Firefox Code Structure
|
||||
|
||||
- `dom/`: DOM implementation
|
||||
- `dom/base/`: Core DOM classes (Window, Document, Navigator, etc.)
|
||||
- `dom/webidl/`: WebIDL interface definitions
|
||||
- `dom/media/webrtc/`: WebRTC implementation
|
||||
- `gfx/`: Graphics and font rendering
|
||||
- `gfx/thebes/`: Text rendering (fonts, glyphs, shaping)
|
||||
- `layout/`: Layout engine
|
||||
- `layout/generic/`: Text frames
|
||||
- `layout/mathml/`: MathML rendering
|
||||
|
||||
### Common Firefox Patterns
|
||||
|
||||
**User Context ID Extraction**:
|
||||
```cpp
|
||||
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):
|
||||
```cpp
|
||||
// 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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary Checklist
|
||||
|
||||
When updating patches for a new Firefox version:
|
||||
|
||||
- [ ] Use `make clean` to reset to fresh Firefox source
|
||||
- [ ] Apply patch and identify all reject files
|
||||
- [ ] Analyze each reject to understand what changed
|
||||
- [ ] Search Firefox source for moved/refactored code
|
||||
- [ ] Fix rejects by porting logic to new Firefox APIs
|
||||
- [ ] Extract userContextId properly using standard patterns
|
||||
- [ ] Don't leave TODO comments - fix everything immediately
|
||||
- [ ] Remove all `.rej` files after fixing
|
||||
- [ ] Add any new files with `git add`
|
||||
- [ ] Generate new patch with `git diff --cached --binary` + `git diff --binary`
|
||||
- [ ] Verify patch applies cleanly to fresh source
|
||||
- [ ] Document any major API changes discovered
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- Firefox source: https://searchfox.org/
|
||||
- Firefox API documentation: https://firefox-source-docs.mozilla.org/
|
||||
- Mercurial repository: https://hg.mozilla.org/mozilla-central/
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: December 2025 (Firefox 146 upgrade)
|
||||
@@ -0,0 +1,165 @@
|
||||
# Playwright Maintenance Guide
|
||||
|
||||
This document describes how to maintain Playwright integration in Camoufox.
|
||||
|
||||
## Overview
|
||||
|
||||
Camoufox integrates Playwright's browser automation capabilities through patches and additional files. These need to be kept in sync with upstream Playwright development.
|
||||
|
||||
## Patch Files
|
||||
|
||||
Location: `patches/playwright/`
|
||||
|
||||
| File | Purpose |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `0-playwright.patch` | Playwright's upstream patches. Must be kept up to date with [bootstrap.diff](https://github.com/microsoft/playwright/blob/main/browser_patches/firefox/patches/bootstrap.diff) |
|
||||
| `1-leak-fixes.patch` | Undos certain patches from `0-playwright.patch` to fix memory leaks |
|
||||
|
||||
## Addition Files
|
||||
|
||||
Location: `additions/juggler/`
|
||||
|
||||
The `juggler` directory contains Playwright's Juggler protocol implementation. These files must be kept in sync with:
|
||||
|
||||
**Upstream Source:** https://github.com/microsoft/playwright/tree/main/browser_patches/firefox/juggler
|
||||
|
||||
### Key Files
|
||||
|
||||
- **`components/Juggler.js`** - Main Juggler component (legacy JSM format)
|
||||
- **`components/Juggler.sys.mjs`** - ESM wrapper for Firefox 146+ compatibility
|
||||
- **`components/components.conf`** - XPCOM component registration
|
||||
|
||||
### Firefox 146 ESM Migration
|
||||
|
||||
Firefox 146 removed JSM (JavaScript Module) support in favor of ESM (ES Modules). To maintain compatibility:
|
||||
|
||||
1. **`components.conf`** uses `esModule` field instead of deprecated `jsm` field:
|
||||
```python
|
||||
{
|
||||
"esModule": "chrome://juggler/content/components/Juggler.sys.mjs",
|
||||
"constructor": "JugglerFactory",
|
||||
}
|
||||
```
|
||||
|
||||
2. **`Juggler.sys.mjs`** acts as an ESM wrapper that imports the legacy JSM file:
|
||||
```javascript
|
||||
const { JugglerFactory } = ChromeUtils.import(
|
||||
"chrome://juggler/content/components/Juggler.js"
|
||||
);
|
||||
export { JugglerFactory };
|
||||
```
|
||||
|
||||
This maintains backward compatibility while satisfying Firefox 146's static component generator requirements.
|
||||
|
||||
## Updating Playwright Integration
|
||||
|
||||
### 1. Update Upstream Patches
|
||||
|
||||
Compare the current `patches/playwright/0-playwright.patch` with Playwright's [bootstrap.diff](https://github.com/microsoft/playwright/blob/main/browser_patches/firefox/patches/bootstrap.diff).
|
||||
|
||||
If changes are needed:
|
||||
```bash
|
||||
# Download latest bootstrap.diff
|
||||
curl -o patches/playwright/0-playwright.patch \
|
||||
https://raw.githubusercontent.com/microsoft/playwright/main/browser_patches/firefox/patches/bootstrap.diff
|
||||
|
||||
# Test the build
|
||||
make clean && make dir && make build
|
||||
```
|
||||
|
||||
### 2. Update Juggler Files
|
||||
|
||||
Sync `additions/juggler/` with upstream:
|
||||
|
||||
```bash
|
||||
# Clone Playwright repository
|
||||
git clone https://github.com/microsoft/playwright.git /tmp/playwright
|
||||
|
||||
# Compare directories
|
||||
diff -r additions/juggler/ /tmp/playwright/browser_patches/firefox/juggler/
|
||||
|
||||
# Copy updated files (example)
|
||||
cp -r /tmp/playwright/browser_patches/firefox/juggler/* additions/juggler/
|
||||
|
||||
# IMPORTANT: Preserve Firefox 146 ESM compatibility
|
||||
# - Keep additions/juggler/components/Juggler.sys.mjs
|
||||
# - Keep additions/juggler/components/components.conf with esModule field
|
||||
```
|
||||
|
||||
### 3. Verify ESM Wrapper Compatibility
|
||||
|
||||
After updating from upstream, ensure the ESM wrapper remains functional:
|
||||
|
||||
1. Check that `Juggler.js` still exports `JugglerFactory`:
|
||||
```javascript
|
||||
var EXPORTED_SYMBOLS = ["Juggler", "JugglerFactory"];
|
||||
var JugglerFactory = function() { /* ... */ };
|
||||
```
|
||||
|
||||
2. If upstream changed the export name, update `Juggler.sys.mjs` accordingly.
|
||||
|
||||
3. Verify `components.conf` matches the format above (not upstream's format).
|
||||
|
||||
### 4. Test Build
|
||||
|
||||
```bash
|
||||
# Clean build to verify component registration
|
||||
cd camoufox-146.0.1-beta.25
|
||||
make clean
|
||||
cd ..
|
||||
make dir
|
||||
cd camoufox-146.0.1-beta.25
|
||||
./mach build
|
||||
```
|
||||
|
||||
**Expected:** No linker errors about `mozCreateComponent<nsICommandLineHandler>`.
|
||||
|
||||
**Common Error:** If you see:
|
||||
```
|
||||
ld64.lld: error: undefined symbol: already_AddRefed<nsISupports> mozCreateComponent<nsICommandLineHandler>()
|
||||
```
|
||||
|
||||
This means `components.conf` is not using ESM format. Fix by ensuring it has:
|
||||
- `"esModule"` field (not `"jsm"`)
|
||||
- `"constructor": "JugglerFactory"` field (not `"type"`)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Component Registration Errors
|
||||
|
||||
**Error:** `Externally-constructed components may not specify 'constructor' or 'legacy_constructor' properties`
|
||||
|
||||
**Cause:** Using `"jsm"` field which is unsupported in Firefox 146.
|
||||
|
||||
**Fix:** Use `"esModule"` field instead.
|
||||
|
||||
---
|
||||
|
||||
**Error:** `Externally-constructed components must specify a type other than nsISupports`
|
||||
|
||||
**Cause:** Using external component without proper type specification.
|
||||
|
||||
**Fix:** Convert to ESM component with constructor.
|
||||
|
||||
---
|
||||
|
||||
**Error:** `JavaScript components must specify a constructor`
|
||||
|
||||
**Cause:** ESM component missing constructor field.
|
||||
|
||||
**Fix:** Add `"constructor": "JugglerFactory"` to components.conf.
|
||||
|
||||
### Build Failures
|
||||
|
||||
If the build fails after updating Juggler files:
|
||||
|
||||
1. Check that all JSM imports in `Juggler.js` are still valid
|
||||
2. Verify the ESM wrapper exports match what's imported
|
||||
3. Ensure no file paths changed in upstream
|
||||
4. Check for Firefox API changes that might require patches
|
||||
|
||||
## References
|
||||
|
||||
- [Playwright Firefox Patches](https://github.com/microsoft/playwright/tree/main/browser_patches/firefox)
|
||||
- [Firefox 146 Component Registration](https://firefox-source-docs.mozilla.org/toolkit/components/extensions/webextensions/basics.html)
|
||||
- [Firefox ESM Migration Guide](https://firefox-source-docs.mozilla.org/dom/script_loader/index.html)
|
||||
@@ -48,6 +48,11 @@ class BSYS:
|
||||
"""Bootstrap the build system"""
|
||||
run('make bootstrap')
|
||||
|
||||
@staticmethod
|
||||
def generate_assets_car():
|
||||
"""Generate Assets.car for macOS builds"""
|
||||
run('make generate-assets-car')
|
||||
|
||||
def build(self):
|
||||
"""Build the Camoufox source code"""
|
||||
os.environ['BUILD_TARGET'] = f'{self.target},{self.arch}'
|
||||
@@ -119,11 +124,18 @@ def main():
|
||||
# Clean if requested
|
||||
if args.clean:
|
||||
BSYS.clean()
|
||||
# Generate Assets.car if building for macOS
|
||||
if 'macos' in args.target:
|
||||
BSYS.generate_assets_car()
|
||||
|
||||
# Ensure dist directory exists
|
||||
os.makedirs('dist', exist_ok=True)
|
||||
|
||||
# Run build
|
||||
for target in args.target:
|
||||
for arch in args.arch:
|
||||
if (target, arch) in [("windows", "arm64"), ("macos", "i686")]:
|
||||
print(f"Skipping {target} {arch}: Unsuported architecture.")
|
||||
print(f"Skipping {target} {arch}: Unsupported architecture.")
|
||||
continue
|
||||
run_build(target, arch)
|
||||
|
||||
|
||||
@@ -23,13 +23,14 @@ diff --git a/dom/media/moz.build b/dom/media/moz.build
|
||||
index 21063575d7..9e7c9f3e84 100644
|
||||
--- a/dom/media/moz.build
|
||||
+++ b/dom/media/moz.build
|
||||
@@ -417,3 +417,5 @@ if CONFIG["CC_TYPE"] in ("clang", "gcc"):
|
||||
@@ -418,5 +418,7 @@
|
||||
CXXFLAGS += ["-Werror=switch"]
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
+# DOM Mask
|
||||
+LOCAL_INCLUDES += ["/camoucfg"]
|
||||
\ No newline at end of file
|
||||
|
||||
SPHINX_TREES["/media"] = "docs"
|
||||
diff --git a/dom/media/webaudio/AudioContext.cpp b/dom/media/webaudio/AudioContext.cpp
|
||||
index 66184b683b..daf30882ea 100644
|
||||
--- a/dom/media/webaudio/AudioContext.cpp
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
|
||||
index bb0f71dd60..3ad3d84b82 100644
|
||||
index 6bb2e64906..fbdb0b26ea 100644
|
||||
--- a/browser/installer/package-manifest.in
|
||||
+++ b/browser/installer/package-manifest.in
|
||||
@@ -260,6 +260,11 @@
|
||||
@@ -250,6 +250,11 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -15,7 +15,7 @@ index bb0f71dd60..3ad3d84b82 100644
|
||||
; All the pref files must be part of base to prevent migration bugs
|
||||
@RESPATH@/browser/@PREF_DIR@/firefox.js
|
||||
diff --git a/lw/moz.build b/lw/moz.build
|
||||
index e69de29bb2..208184547e 100644
|
||||
index e69de29bb2..5459e4ac36 100644
|
||||
--- a/lw/moz.build
|
||||
+++ b/lw/moz.build
|
||||
@@ -0,0 +1,14 @@
|
||||
@@ -34,11 +34,13 @@ index e69de29bb2..208184547e 100644
|
||||
+ "local-settings.js",
|
||||
+]
|
||||
diff --git a/moz.build b/moz.build
|
||||
index 9db27fd056..e1ec1dc872 100644
|
||||
index 4d0f3d6ba9..0f07f37e92 100644
|
||||
--- a/moz.build
|
||||
+++ b/moz.build
|
||||
@@ -228,3 +228,5 @@ SPHINX_TREES["jsloader"] = "docs/jsloader"
|
||||
@@ -235,3 +235,5 @@ SPHINX_TREES["content-security"] = "docs/content-security"
|
||||
SPHINX_TREES["jsloader"] = "docs/jsloader"
|
||||
|
||||
include("build/templates.mozbuild")
|
||||
+
|
||||
+DIRS += ["lw"]
|
||||
+DIRS += ["lw"]
|
||||
\ No newline at end of file
|
||||
|
||||
@@ -1,28 +1,31 @@
|
||||
diff --git a/browser/app/moz.build b/browser/app/moz.build
|
||||
index 434167c996..e18a16aee0 100644
|
||||
index a23bb7812a..58942ccb13 100644
|
||||
--- a/browser/app/moz.build
|
||||
+++ b/browser/app/moz.build
|
||||
@@ -174,3 +174,6 @@ for icon in ("firefox", "document", "newwindow", "newtab", "pbmode", "document_p
|
||||
CONFIG["MOZ_BRANDING_DIRECTORY"],
|
||||
@@ -182,6 +182,9 @@ for icon in ("firefox", "document", "newwindow", "newtab", "pbmode", "document_p
|
||||
icon,
|
||||
)
|
||||
|
||||
+
|
||||
+# DOM Mask
|
||||
+LOCAL_INCLUDES += ["/camoucfg"]
|
||||
\ No newline at end of file
|
||||
if CONFIG["MOZ_ASAN"] or CONFIG["MOZ_DEBUG"]:
|
||||
WINCONSOLE = True
|
||||
else:
|
||||
diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp
|
||||
index 807cb4ec25..3df0e0f70f 100644
|
||||
index 101590d8c8..f004930b20 100644
|
||||
--- a/dom/base/Element.cpp
|
||||
+++ b/dom/base/Element.cpp
|
||||
@@ -12,6 +12,7 @@
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/ElementInlines.h"
|
||||
+#include "MaskConfig.hpp"
|
||||
|
||||
+#include "MaskConfig.hpp"
|
||||
+
|
||||
#include <inttypes.h>
|
||||
#include <initializer_list>
|
||||
@@ -984,6 +985,18 @@ nsRect Element::GetClientAreaRect() {
|
||||
|
||||
#include <cstddef>
|
||||
@@ -1006,6 +1008,18 @@ nsRect Element::GetClientAreaRect() {
|
||||
Document* doc = OwnerDoc();
|
||||
nsPresContext* presContext = doc->GetPresContext();
|
||||
|
||||
@@ -41,195 +44,11 @@ index 807cb4ec25..3df0e0f70f 100644
|
||||
// We can avoid a layout flush if this is the scrolling element of the
|
||||
// document, we have overlay scrollbars, and we aren't embedded in another
|
||||
// document
|
||||
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
|
||||
index 8e577bd9fd..3ed748a75f 100644
|
||||
--- a/dom/base/Navigator.cpp
|
||||
+++ b/dom/base/Navigator.cpp
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "base/basictypes.h"
|
||||
|
||||
#include "Navigator.h"
|
||||
+#include "MaskConfig.hpp"
|
||||
#include "nsIXULAppInfo.h"
|
||||
#include "nsPluginArray.h"
|
||||
#include "nsMimeTypeArray.h"
|
||||
@@ -267,6 +268,8 @@ void Navigator::Invalidate() {
|
||||
|
||||
void Navigator::GetUserAgent(nsAString& aUserAgent, CallerType aCallerType,
|
||||
ErrorResult& aRv) const {
|
||||
+ if (auto value = MaskConfig::GetString("navigator.userAgent"))
|
||||
+ return aUserAgent.Assign(NS_ConvertUTF8toUTF16(value.value()));
|
||||
nsCOMPtr<nsPIDOMWindowInner> window;
|
||||
|
||||
if (mWindow) {
|
||||
@@ -293,6 +296,8 @@ void Navigator::GetUserAgent(nsAString& aUserAgent, CallerType aCallerType,
|
||||
}
|
||||
|
||||
void Navigator::GetAppCodeName(nsAString& aAppCodeName, ErrorResult& aRv) {
|
||||
+ if (auto value = MaskConfig::GetString("navigator.appCodeName"))
|
||||
+ return aAppCodeName.Assign(NS_ConvertUTF8toUTF16(value.value()));
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIHttpProtocolHandler> service(
|
||||
@@ -314,6 +319,8 @@ void Navigator::GetAppCodeName(nsAString& aAppCodeName, ErrorResult& aRv) {
|
||||
|
||||
void Navigator::GetAppVersion(nsAString& aAppVersion, CallerType aCallerType,
|
||||
ErrorResult& aRv) const {
|
||||
+ if (auto value = MaskConfig::GetString("navigator.appVersion"))
|
||||
+ return aAppVersion.Assign(NS_ConvertUTF8toUTF16(value.value()));
|
||||
nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
|
||||
|
||||
nsresult rv = GetAppVersion(
|
||||
@@ -325,6 +332,8 @@ void Navigator::GetAppVersion(nsAString& aAppVersion, CallerType aCallerType,
|
||||
}
|
||||
|
||||
void Navigator::GetAppName(nsAString& aAppName) const {
|
||||
+ if (auto value = MaskConfig::GetString("navigator.appName"))
|
||||
+ return aAppName.Assign(NS_ConvertUTF8toUTF16(value.value()));
|
||||
aAppName.AssignLiteral("Netscape");
|
||||
}
|
||||
|
||||
@@ -350,6 +359,15 @@ void Navigator::GetAcceptLanguages(const nsString* aLanguageOverride,
|
||||
|
||||
aLanguages.Clear();
|
||||
|
||||
+ if (std::vector<std::string> maskValues =
|
||||
+ MaskConfig::GetStringList("navigator.languages");
|
||||
+ !maskValues.empty()) {
|
||||
+ for (const auto& lang : maskValues) {
|
||||
+ aLanguages.AppendElement(NS_ConvertUTF8toUTF16(lang));
|
||||
+ }
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
// E.g. "de-de, en-us,en".
|
||||
nsAutoString acceptLang;
|
||||
if (aLanguageOverride && aLanguageOverride->Length())
|
||||
@@ -400,6 +418,8 @@ void Navigator::GetAcceptLanguages(const nsString* aLanguageOverride,
|
||||
* Full details above in GetAcceptLanguages.
|
||||
*/
|
||||
void Navigator::GetLanguage(nsAString& aLanguage) {
|
||||
+ if (auto value = MaskConfig::GetString("navigator.language"))
|
||||
+ return aLanguage.Assign(NS_ConvertUTF8toUTF16(value.value()));
|
||||
nsTArray<nsString> languages;
|
||||
GetLanguages(languages);
|
||||
MOZ_ASSERT(languages.Length() >= 1);
|
||||
@@ -423,6 +443,8 @@ void Navigator::GetLanguages(nsTArray<nsString>& aLanguages) {
|
||||
|
||||
void Navigator::GetPlatform(nsAString& aPlatform, CallerType aCallerType,
|
||||
ErrorResult& aRv) const {
|
||||
+ if (auto value = MaskConfig::GetString("navigator.platform"))
|
||||
+ return aPlatform.Assign(NS_ConvertUTF8toUTF16(value.value()));
|
||||
if (mWindow) {
|
||||
BrowsingContext* bc = mWindow->GetBrowsingContext();
|
||||
nsString customPlatform;
|
||||
@@ -449,6 +471,8 @@ void Navigator::GetPlatform(nsAString& aPlatform, CallerType aCallerType,
|
||||
void Navigator::GetOscpu(nsAString& aOSCPU, CallerType aCallerType,
|
||||
ErrorResult& aRv) const {
|
||||
if (aCallerType != CallerType::System) {
|
||||
+ if (auto value = MaskConfig::GetString("navigator.oscpu"))
|
||||
+ return aOSCPU.Assign(NS_ConvertUTF8toUTF16(value.value()));
|
||||
// If fingerprinting resistance is on, we will spoof this value. See
|
||||
// nsRFPService.h for details about spoofed values.
|
||||
if (nsContentUtils::ShouldResistFingerprinting(GetDocShell(),
|
||||
@@ -488,10 +512,14 @@ void Navigator::GetVendor(nsAString& aVendor) { aVendor.Truncate(); }
|
||||
void Navigator::GetVendorSub(nsAString& aVendorSub) { aVendorSub.Truncate(); }
|
||||
|
||||
void Navigator::GetProduct(nsAString& aProduct) {
|
||||
+ if (auto value = MaskConfig::GetString("navigator.product"))
|
||||
+ return aProduct.Assign(NS_ConvertUTF8toUTF16(value.value()));
|
||||
aProduct.AssignLiteral("Gecko");
|
||||
}
|
||||
|
||||
void Navigator::GetProductSub(nsAString& aProductSub) {
|
||||
+ if (auto value = MaskConfig::GetString("navigator.productSub"))
|
||||
+ return aProductSub.Assign(NS_ConvertUTF8toUTF16(value.value()));
|
||||
// Legacy build date hardcoded for backward compatibility (bug 776376)
|
||||
aProductSub.AssignLiteral(LEGACY_UA_GECKO_TRAIL);
|
||||
}
|
||||
@@ -517,7 +545,11 @@ nsPluginArray* Navigator::GetPlugins(ErrorResult& aRv) {
|
||||
return mPlugins;
|
||||
}
|
||||
|
||||
-bool Navigator::PdfViewerEnabled() { return !StaticPrefs::pdfjs_disabled(); }
|
||||
+bool Navigator::PdfViewerEnabled() {
|
||||
+ if (auto value = MaskConfig::GetBool("pdfViewerEnabled"); value.has_value())
|
||||
+ return value.value();
|
||||
+ return !StaticPrefs::pdfjs_disabled();
|
||||
+}
|
||||
|
||||
Permissions* Navigator::GetPermissions(ErrorResult& aRv) {
|
||||
if (!mWindow) {
|
||||
@@ -543,6 +575,9 @@ StorageManager* Navigator::Storage() {
|
||||
}
|
||||
|
||||
bool Navigator::CookieEnabled() {
|
||||
+ if (auto value = MaskConfig::GetBool("navigator.cookieEnabled");
|
||||
+ value.has_value())
|
||||
+ return value.value();
|
||||
// Check whether an exception overrides the global cookie behavior
|
||||
// Note that the code for getting the URI here matches that in
|
||||
// nsHTMLDocument::SetCookie.
|
||||
@@ -589,6 +624,8 @@ bool Navigator::CookieEnabled() {
|
||||
}
|
||||
|
||||
bool Navigator::OnLine() {
|
||||
+ if (auto value = MaskConfig::GetBool("navigator.onLine"); value.has_value())
|
||||
+ return value.value();
|
||||
if (mWindow) {
|
||||
// Check if this tab is set to be offline.
|
||||
BrowsingContext* bc = mWindow->GetBrowsingContext();
|
||||
@@ -603,6 +640,8 @@ bool Navigator::OnLine() {
|
||||
void Navigator::GetBuildID(nsAString& aBuildID, CallerType aCallerType,
|
||||
ErrorResult& aRv) const {
|
||||
if (aCallerType != CallerType::System) {
|
||||
+ if (auto value = MaskConfig::GetString("navigator.buildID"))
|
||||
+ return aBuildID.Assign(NS_ConvertUTF8toUTF16(value.value()));
|
||||
// If fingerprinting resistance is on, we will spoof this value. See
|
||||
// nsRFPService.h for details about spoofed values.
|
||||
if (nsContentUtils::ShouldResistFingerprinting(
|
||||
@@ -659,6 +698,8 @@ void Navigator::GetBuildID(nsAString& aBuildID, CallerType aCallerType,
|
||||
}
|
||||
|
||||
void Navigator::GetDoNotTrack(nsAString& aResult) {
|
||||
+ if (auto value = MaskConfig::GetString("navigator.doNotTrack"))
|
||||
+ return aResult.Assign(NS_ConvertUTF8toUTF16(value.value()));
|
||||
bool doNotTrack = StaticPrefs::privacy_donottrackheader_enabled();
|
||||
if (!doNotTrack) {
|
||||
nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(mWindow);
|
||||
@@ -673,6 +714,9 @@ void Navigator::GetDoNotTrack(nsAString& aResult) {
|
||||
}
|
||||
|
||||
bool Navigator::GlobalPrivacyControl() {
|
||||
+ if (auto value = MaskConfig::GetBool("navigator.globalPrivacyControl");
|
||||
+ value.has_value())
|
||||
+ return value.value();
|
||||
bool gpcStatus = StaticPrefs::privacy_globalprivacycontrol_enabled();
|
||||
if (!gpcStatus) {
|
||||
nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(mWindow);
|
||||
@@ -684,6 +728,8 @@ bool Navigator::GlobalPrivacyControl() {
|
||||
}
|
||||
|
||||
uint64_t Navigator::HardwareConcurrency() {
|
||||
+ if (auto value = MaskConfig::GetUint64("navigator.hardwareConcurrency"))
|
||||
+ return value.value();
|
||||
workerinternals::RuntimeService* rts =
|
||||
workerinternals::RuntimeService::GetOrCreateService();
|
||||
if (!rts) {
|
||||
@@ -882,6 +928,8 @@ bool Navigator::Vibrate(const nsTArray<uint32_t>& aPattern) {
|
||||
//*****************************************************************************
|
||||
|
||||
uint32_t Navigator::MaxTouchPoints(CallerType aCallerType) {
|
||||
+ if (auto value = MaskConfig::GetUint32("navigator.maxTouchPoints"))
|
||||
+ return value.value();
|
||||
nsIDocShell* docshell = GetDocShell();
|
||||
BrowsingContext* bc = docshell ? docshell->GetBrowsingContext() : nullptr;
|
||||
|
||||
diff --git a/dom/base/moz.build b/dom/base/moz.build
|
||||
index 114402c4a1..55daeabe7a 100644
|
||||
index cd9090cda3..37a053579e 100644
|
||||
--- a/dom/base/moz.build
|
||||
+++ b/dom/base/moz.build
|
||||
@@ -641,3 +641,6 @@ GeneratedFile(
|
||||
@@ -634,3 +634,6 @@ GeneratedFile(
|
||||
"/servo/components/style/properties/counted_unknown_properties.py",
|
||||
],
|
||||
)
|
||||
@@ -238,7 +57,7 @@ index 114402c4a1..55daeabe7a 100644
|
||||
+LOCAL_INCLUDES += ["/camoucfg"]
|
||||
\ No newline at end of file
|
||||
diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp
|
||||
index 3678ebc59c..03a72fe242 100644
|
||||
index 330a707789..241f87780b 100644
|
||||
--- a/dom/base/nsGlobalWindowInner.cpp
|
||||
+++ b/dom/base/nsGlobalWindowInner.cpp
|
||||
@@ -5,6 +5,7 @@
|
||||
@@ -249,7 +68,7 @@ index 3678ebc59c..03a72fe242 100644
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
@@ -3410,6 +3411,8 @@ void nsGlobalWindowInner::SetName(const nsAString& aName,
|
||||
@@ -3439,6 +3440,8 @@ void nsGlobalWindowInner::SetName(const nsAString& aName,
|
||||
}
|
||||
|
||||
double nsGlobalWindowInner::GetInnerWidth(ErrorResult& aError) {
|
||||
@@ -258,7 +77,7 @@ index 3678ebc59c..03a72fe242 100644
|
||||
FORWARD_TO_OUTER_OR_THROW(GetInnerWidthOuter, (aError), aError, 0);
|
||||
}
|
||||
|
||||
@@ -3421,6 +3424,8 @@ nsresult nsGlobalWindowInner::GetInnerWidth(double* aWidth) {
|
||||
@@ -3450,6 +3453,8 @@ nsresult nsGlobalWindowInner::GetInnerWidth(double* aWidth) {
|
||||
}
|
||||
|
||||
double nsGlobalWindowInner::GetInnerHeight(ErrorResult& aError) {
|
||||
@@ -267,7 +86,7 @@ index 3678ebc59c..03a72fe242 100644
|
||||
// We ignore aCallerType; we only have that argument because some other things
|
||||
// called by GetReplaceableWindowCoord need it. If this ever changes, fix
|
||||
// nsresult nsGlobalWindowInner::GetInnerHeight(double* aInnerWidth)
|
||||
@@ -3437,12 +3442,16 @@ nsresult nsGlobalWindowInner::GetInnerHeight(double* aHeight) {
|
||||
@@ -3466,12 +3471,16 @@ nsresult nsGlobalWindowInner::GetInnerHeight(double* aHeight) {
|
||||
|
||||
int32_t nsGlobalWindowInner::GetOuterWidth(CallerType aCallerType,
|
||||
ErrorResult& aError) {
|
||||
@@ -284,7 +103,7 @@ index 3678ebc59c..03a72fe242 100644
|
||||
FORWARD_TO_OUTER_OR_THROW(GetOuterHeightOuter, (aCallerType, aError), aError,
|
||||
0);
|
||||
}
|
||||
@@ -3457,11 +3466,13 @@ double nsGlobalWindowInner::ScreenEdgeSlopY() const {
|
||||
@@ -3486,11 +3495,13 @@ double nsGlobalWindowInner::ScreenEdgeSlopY() const {
|
||||
|
||||
int32_t nsGlobalWindowInner::GetScreenX(CallerType aCallerType,
|
||||
ErrorResult& aError) {
|
||||
@@ -298,7 +117,7 @@ index 3678ebc59c..03a72fe242 100644
|
||||
FORWARD_TO_OUTER_OR_THROW(GetScreenYOuter, (aCallerType, aError), aError, 0);
|
||||
}
|
||||
|
||||
@@ -3495,6 +3506,8 @@ static nsPresContext* GetPresContextForRatio(Document* aDoc) {
|
||||
@@ -3524,6 +3535,8 @@ static nsPresContext* GetPresContextForRatio(Document* aDoc) {
|
||||
double nsGlobalWindowInner::GetDevicePixelRatio(CallerType aCallerType,
|
||||
ErrorResult& aError) {
|
||||
ENSURE_ACTIVE_DOCUMENT(aError, 0.0);
|
||||
@@ -307,7 +126,7 @@ index 3678ebc59c..03a72fe242 100644
|
||||
|
||||
RefPtr<nsPresContext> presContext = GetPresContextForRatio(mDoc);
|
||||
if (NS_WARN_IF(!presContext)) {
|
||||
@@ -3565,26 +3578,38 @@ already_AddRefed<MediaQueryList> nsGlobalWindowInner::MatchMedia(
|
||||
@@ -3594,26 +3607,38 @@ already_AddRefed<MediaQueryList> nsGlobalWindowInner::MatchMedia(
|
||||
}
|
||||
|
||||
int32_t nsGlobalWindowInner::GetScrollMinX(ErrorResult& aError) {
|
||||
@@ -347,7 +166,7 @@ index 3678ebc59c..03a72fe242 100644
|
||||
}
|
||||
|
||||
diff --git a/dom/base/nsHistory.cpp b/dom/base/nsHistory.cpp
|
||||
index 99994a73cc..837214d872 100644
|
||||
index e2fd8e6389..e5ddf4e08c 100644
|
||||
--- a/dom/base/nsHistory.cpp
|
||||
+++ b/dom/base/nsHistory.cpp
|
||||
@@ -5,6 +5,7 @@
|
||||
@@ -357,8 +176,8 @@ index 99994a73cc..837214d872 100644
|
||||
+#include "MaskConfig.hpp"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "nsCOMPtr.h"
|
||||
@@ -55,6 +56,8 @@ JSObject* nsHistory::WrapObject(JSContext* aCx,
|
||||
#include "mozilla/RefPtr.h"
|
||||
@@ -64,6 +65,8 @@ JSObject* nsHistory::WrapObject(JSContext* aCx,
|
||||
}
|
||||
|
||||
uint32_t nsHistory::GetLength(ErrorResult& aRv) const {
|
||||
@@ -368,18 +187,19 @@ index 99994a73cc..837214d872 100644
|
||||
if (!win || !win->HasActiveDocument()) {
|
||||
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
diff --git a/dom/base/nsScreen.cpp b/dom/base/nsScreen.cpp
|
||||
index 4bd1e0c964..a5a95d3fdf 100644
|
||||
index 306ab35772..7f7728a6cc 100644
|
||||
--- a/dom/base/nsScreen.cpp
|
||||
+++ b/dom/base/nsScreen.cpp
|
||||
@@ -4,6 +4,7 @@
|
||||
* 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/. */
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include "nsScreen.h"
|
||||
|
||||
+#include "MaskConfig.hpp"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsScreen.h"
|
||||
+
|
||||
#include "mozilla/GeckoBindings.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
@@ -37,6 +38,10 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(nsScreen, DOMEventTargetHelper,
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
@@ -40,6 +42,10 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(nsScreen, DOMEventTargetHelper,
|
||||
mScreenOrientation)
|
||||
|
||||
int32_t nsScreen::PixelDepth() {
|
||||
@@ -390,7 +210,7 @@ index 4bd1e0c964..a5a95d3fdf 100644
|
||||
// Return 24 to prevent fingerprinting.
|
||||
if (ShouldResistFingerprinting(RFPTarget::ScreenPixelDepth)) {
|
||||
return 24;
|
||||
@@ -89,6 +94,12 @@ CSSIntRect nsScreen::GetRect() {
|
||||
@@ -89,6 +95,12 @@ CSSIntRect nsScreen::GetRect() {
|
||||
}
|
||||
|
||||
CSSIntRect nsScreen::GetAvailRect() {
|
||||
@@ -402,20 +222,20 @@ index 4bd1e0c964..a5a95d3fdf 100644
|
||||
+ }
|
||||
// Return window inner rect to prevent fingerprinting.
|
||||
if (ShouldResistFingerprinting(RFPTarget::ScreenAvailRect)) {
|
||||
return GetWindowInnerRect();
|
||||
return GetTopWindowInnerRectForRFP();
|
||||
diff --git a/dom/battery/BatteryManager.cpp b/dom/battery/BatteryManager.cpp
|
||||
index a42ac3017d..fadae4a714 100644
|
||||
index 6322093fd9..264cc91bd1 100644
|
||||
--- a/dom/battery/BatteryManager.cpp
|
||||
+++ b/dom/battery/BatteryManager.cpp
|
||||
@@ -7,6 +7,7 @@
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include "BatteryManager.h"
|
||||
|
||||
+#include "MaskConfig.hpp"
|
||||
#include "Constants.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/Hal.h"
|
||||
@@ -51,6 +52,9 @@ JSObject* BatteryManager::WrapObject(JSContext* aCx,
|
||||
@@ -53,6 +54,9 @@ JSObject* BatteryManager::WrapObject(JSContext* aCx,
|
||||
|
||||
bool BatteryManager::Charging() const {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
@@ -425,7 +245,7 @@ index a42ac3017d..fadae4a714 100644
|
||||
// For testing, unable to report the battery status information
|
||||
if (Preferences::GetBool("dom.battery.test.default", false)) {
|
||||
return true;
|
||||
@@ -67,6 +71,8 @@ bool BatteryManager::Charging() const {
|
||||
@@ -69,6 +73,8 @@ bool BatteryManager::Charging() const {
|
||||
|
||||
double BatteryManager::DischargingTime() const {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
@@ -434,7 +254,7 @@ index a42ac3017d..fadae4a714 100644
|
||||
// For testing, unable to report the battery status information
|
||||
if (Preferences::GetBool("dom.battery.test.default", false)) {
|
||||
return std::numeric_limits<double>::infinity();
|
||||
@@ -84,6 +90,8 @@ double BatteryManager::DischargingTime() const {
|
||||
@@ -86,6 +92,8 @@ double BatteryManager::DischargingTime() const {
|
||||
|
||||
double BatteryManager::ChargingTime() const {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
@@ -443,7 +263,7 @@ index a42ac3017d..fadae4a714 100644
|
||||
// For testing, unable to report the battery status information
|
||||
if (Preferences::GetBool("dom.battery.test.default", false)) {
|
||||
return 0.0;
|
||||
@@ -101,6 +109,7 @@ double BatteryManager::ChargingTime() const {
|
||||
@@ -103,6 +111,7 @@ double BatteryManager::ChargingTime() const {
|
||||
|
||||
double BatteryManager::Level() const {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
@@ -464,7 +284,7 @@ index 3a90c93c01..91d673039b 100644
|
||||
+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
|
||||
index 622724b529..c9a65ff013 100644
|
||||
--- a/dom/workers/WorkerNavigator.cpp
|
||||
+++ b/dom/workers/WorkerNavigator.cpp
|
||||
@@ -5,6 +5,7 @@
|
||||
@@ -475,7 +295,7 @@ index b05e409a84..8e0f7ea657 100644
|
||||
|
||||
#include <utility>
|
||||
|
||||
@@ -92,6 +93,9 @@ JSObject* WorkerNavigator::WrapObject(JSContext* aCx,
|
||||
@@ -103,6 +104,9 @@ JSObject* WorkerNavigator::WrapObject(JSContext* aCx,
|
||||
}
|
||||
|
||||
bool WorkerNavigator::GlobalPrivacyControl() const {
|
||||
@@ -485,7 +305,7 @@ index b05e409a84..8e0f7ea657 100644
|
||||
bool gpcStatus = StaticPrefs::privacy_globalprivacycontrol_enabled();
|
||||
if (!gpcStatus) {
|
||||
JSObject* jso = GetWrapper();
|
||||
@@ -114,6 +118,8 @@ void WorkerNavigator::SetLanguages(const nsTArray<nsString>& aLanguages) {
|
||||
@@ -125,6 +129,8 @@ void WorkerNavigator::SetLanguages(const nsTArray<nsString>& aLanguages) {
|
||||
void WorkerNavigator::GetAppVersion(nsString& aAppVersion,
|
||||
CallerType aCallerType,
|
||||
ErrorResult& aRv) const {
|
||||
@@ -494,7 +314,7 @@ index b05e409a84..8e0f7ea657 100644
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
@@ -136,6 +142,8 @@ void WorkerNavigator::GetAppVersion(nsString& aAppVersion,
|
||||
@@ -147,6 +153,8 @@ void WorkerNavigator::GetAppVersion(nsString& aAppVersion,
|
||||
|
||||
void WorkerNavigator::GetPlatform(nsString& aPlatform, CallerType aCallerType,
|
||||
ErrorResult& aRv) const {
|
||||
@@ -503,7 +323,7 @@ index b05e409a84..8e0f7ea657 100644
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
@@ -200,6 +208,8 @@ class GetUserAgentRunnable final : public WorkerMainThreadRunnable {
|
||||
@@ -207,6 +215,8 @@ class GetUserAgentRunnable final : public WorkerMainThreadRunnable {
|
||||
|
||||
void WorkerNavigator::GetUserAgent(nsString& aUserAgent, CallerType aCallerType,
|
||||
ErrorResult& aRv) const {
|
||||
@@ -512,7 +332,7 @@ index b05e409a84..8e0f7ea657 100644
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
@@ -211,6 +221,8 @@ void WorkerNavigator::GetUserAgent(nsString& aUserAgent, CallerType aCallerType,
|
||||
@@ -218,6 +228,8 @@ void WorkerNavigator::GetUserAgent(nsString& aUserAgent, CallerType aCallerType,
|
||||
}
|
||||
|
||||
uint64_t WorkerNavigator::HardwareConcurrency() const {
|
||||
@@ -522,10 +342,10 @@ index b05e409a84..8e0f7ea657 100644
|
||||
MOZ_ASSERT(rts);
|
||||
|
||||
diff --git a/dom/workers/moz.build b/dom/workers/moz.build
|
||||
index c7818826d1..2e2e6526e7 100644
|
||||
index 2f2948a729..b8ad10403f 100644
|
||||
--- a/dom/workers/moz.build
|
||||
+++ b/dom/workers/moz.build
|
||||
@@ -112,3 +112,6 @@ MARIONETTE_MANIFESTS += ["test/marionette/manifest.toml"]
|
||||
@@ -114,3 +114,6 @@ MARIONETTE_MANIFESTS += ["test/marionette/manifest.toml"]
|
||||
XPCSHELL_TESTS_MANIFESTS += ["test/xpcshell/xpcshell.toml"]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ["test/browser.toml"]
|
||||
|
||||
@@ -31,9 +31,9 @@ diff --git a/gfx/thebes/moz.build b/gfx/thebes/moz.build
|
||||
index 3d99906827..a9d1191688 100644
|
||||
--- a/gfx/thebes/moz.build
|
||||
+++ b/gfx/thebes/moz.build
|
||||
@@ -305,3 +305,6 @@ DEFINES["GRAPHITE2_STATIC"] = True
|
||||
@@ -301,3 +301,6 @@ DEFINES["GRAPHITE2_STATIC"] = True
|
||||
CXXFLAGS += ["-Werror=switch"]
|
||||
|
||||
|
||||
include("/tools/fuzzing/libfuzzer-config.mozbuild")
|
||||
+
|
||||
+# DOM Mask
|
||||
@@ -84,7 +84,7 @@ diff --git a/layout/style/FontFaceImpl.cpp b/layout/style/FontFaceImpl.cpp
|
||||
index 9d1f7c8c5b..926315ca2f 100644
|
||||
--- a/layout/style/FontFaceImpl.cpp
|
||||
+++ b/layout/style/FontFaceImpl.cpp
|
||||
@@ -357,21 +357,15 @@ void FontFaceImpl::DoLoad() {
|
||||
@@ -357,22 +357,16 @@ void FontFaceImpl::DoLoad() {
|
||||
void FontFaceImpl::SetStatus(FontFaceLoadStatus aStatus) {
|
||||
gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked();
|
||||
|
||||
@@ -112,19 +112,20 @@ index 9d1f7c8c5b..926315ca2f 100644
|
||||
if (mInFontFaceSet) {
|
||||
mFontFaceSet->OnFontFaceStatusChanged(this);
|
||||
}
|
||||
|
||||
diff --git a/layout/style/FontFaceImpl.h b/layout/style/FontFaceImpl.h
|
||||
index 70c06609e9..66b2666621 100644
|
||||
--- a/layout/style/FontFaceImpl.h
|
||||
+++ b/layout/style/FontFaceImpl.h
|
||||
@@ -8,6 +8,7 @@
|
||||
#define mozilla_dom_FontFaceImpl_h
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "mozilla/ServoStyleConsts.h"
|
||||
#include "mozilla/dom/FontFaceBinding.h"
|
||||
+#include "MaskConfig.hpp"
|
||||
#include "mozilla/FontPropertyTypes.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
@@ -35,6 +36,20 @@ class UTF8StringOrArrayBufferOrArrayBufferView;
|
||||
#include "nsTHashSet.h"
|
||||
|
||||
class gfxFontFaceBufferSource;
|
||||
|
||||
@@ -33,6 +34,20 @@
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
@@ -149,11 +150,11 @@ diff --git a/layout/style/moz.build b/layout/style/moz.build
|
||||
index a14ab6a7ac..6e6a727abf 100644
|
||||
--- a/layout/style/moz.build
|
||||
+++ b/layout/style/moz.build
|
||||
@@ -351,3 +351,6 @@ if CONFIG["COMPILE_ENVIRONMENT"]:
|
||||
CONFIGURE_SUBST_FILES += [
|
||||
"extra-bindgen-flags",
|
||||
]
|
||||
@@ -378,4 +378,7 @@
|
||||
CbindgenHeader(
|
||||
"ServoStyleConsts.h",
|
||||
inputs=["/servo/ports/geckolib", "/servo/components/style"],
|
||||
)
|
||||
+
|
||||
+# DOM Mask
|
||||
+LOCAL_INCLUDES += ["/camoucfg"]
|
||||
\ No newline at end of file
|
||||
|
||||
@@ -73,19 +73,20 @@ diff --git a/dom/geolocation/moz.build b/dom/geolocation/moz.build
|
||||
index 2d6b6b5fab..0c7ed74c6d 100644
|
||||
--- a/dom/geolocation/moz.build
|
||||
+++ b/dom/geolocation/moz.build
|
||||
@@ -58,3 +58,6 @@ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
|
||||
if CONFIG["MOZ_ENABLE_DBUS"]:
|
||||
LOCAL_INCLUDES += ["/dom/system/linux"]
|
||||
CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
|
||||
@@ -85,4 +85,7 @@
|
||||
BROWSER_CHROME_MANIFESTS += ["test/browser/browser.toml"]
|
||||
MOCHITEST_CHROME_MANIFESTS += ["test/chrome/chrome.toml"]
|
||||
MOCHITEST_MANIFESTS += ["test/mochitest/mochitest.toml"]
|
||||
XPCSHELL_TESTS_MANIFESTS += ["test/unit/xpcshell.toml"]
|
||||
+
|
||||
+# DOM Mask
|
||||
+LOCAL_INCLUDES += ["/camoucfg"]
|
||||
\ No newline at end of file
|
||||
|
||||
diff --git a/dom/system/NetworkGeolocationProvider.sys.mjs b/dom/system/NetworkGeolocationProvider.sys.mjs
|
||||
index 7edc62b4a6..19b77aa6b6 100644
|
||||
--- a/dom/system/NetworkGeolocationProvider.sys.mjs
|
||||
+++ b/dom/system/NetworkGeolocationProvider.sys.mjs
|
||||
@@ -442,17 +442,55 @@ NetworkGeolocationProvider.prototype = {
|
||||
@@ -342,17 +342,55 @@ NetworkGeolocationProvider.prototype = {
|
||||
let url = Services.urlFormatter.formatURLPref("geo.provider.network.url");
|
||||
LOG("Sending request");
|
||||
|
||||
|
||||
@@ -12,15 +12,11 @@ index ceded6b755..90aa3abe36 100644
|
||||
+++ b/browser/components/asrouter/modules/OnboardingMessageProvider.sys.mjs
|
||||
@@ -1213,8 +1213,7 @@ const BASE_MESSAGES = () => [
|
||||
];
|
||||
|
||||
|
||||
// Eventually, move Feature Callout messages to their own provider
|
||||
-const ONBOARDING_MESSAGES = () =>
|
||||
- BASE_MESSAGES().concat(FeatureCalloutMessages.getMessages());
|
||||
+const ONBOARDING_MESSAGES = () => ([]);
|
||||
|
||||
|
||||
export const OnboardingMessageProvider = {
|
||||
async getExtraAttributes() {
|
||||
|
||||
--
|
||||
2.39.2 (Apple Git-143)
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
From: Jenkins <jenkins@magrathea>
|
||||
Date: Fri, 3 Mar 2023 09:03:01 +0100
|
||||
Subject: Disable Onboarding Messages
|
||||
|
||||
---
|
||||
browser/components/asrouter/modules/OnboardingMessageProvider.sys.mjs | 3 +--
|
||||
1 file changed, 1 insertion(+), 2 deletions(-)
|
||||
|
||||
diff --git a/browser/components/asrouter/modules/OnboardingMessageProvider.sys.mjs b/browser/components/asrouter/modules/OnboardingMessageProvider.sys.mjs
|
||||
index ceded6b755..90aa3abe36 100644
|
||||
--- a/browser/components/asrouter/modules/OnboardingMessageProvider.sys.mjs
|
||||
+++ b/browser/components/asrouter/modules/OnboardingMessageProvider.sys.mjs
|
||||
@@ -1213,11 +1213,9 @@ const BASE_MESSAGES = () => [
|
||||
];
|
||||
|
||||
// Eventually, move Feature Callout messages to their own provider
|
||||
-const ONBOARDING_MESSAGES = () =>
|
||||
- BASE_MESSAGES().concat(FeatureCalloutMessages.getMessages());
|
||||
+const ONBOARDING_MESSAGES = () => ([]);
|
||||
|
||||
export const OnboardingMessageProvider = {
|
||||
async getExtraAttributes() {
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
diff --git a/browser/base/content/browser-context.inc b/browser/base/content/browser-context.inc
|
||||
index 3a58888b28b4..471c238aa284 100644
|
||||
--- a/browser/base/content/browser-context.inc
|
||||
+++ b/browser/base/content/browser-context.inc
|
||||
@@ -107,9 +107,6 @@
|
||||
<menuitem id="context-savelink"
|
||||
data-l10n-id="main-context-menu-save-link"
|
||||
/>
|
||||
- <menuitem id="context-savelinktopocket"
|
||||
- data-l10n-id="main-context-menu-save-link-to-pocket"
|
||||
- />
|
||||
<menuitem id="context-copyemail"
|
||||
data-l10n-id="main-context-menu-copy-email"
|
||||
/>
|
||||
@@ -264,9 +261,6 @@
|
||||
<menuitem id="context-savepage"
|
||||
data-l10n-id="main-context-menu-page-save"
|
||||
/>
|
||||
- <menuitem id="context-pocket"
|
||||
- data-l10n-id="main-context-menu-save-to-pocket"
|
||||
- />
|
||||
<menu id="context-sendpagetodevice"
|
||||
class="sync-ui-item"
|
||||
data-l10n-id="main-context-menu-send-to-device"
|
||||
@@ -1,17 +1,19 @@
|
||||
diff --git a/toolkit/components/extensions/parent/ext-storage.js b/toolkit/components/extensions/parent/ext-storage.js
|
||||
index d76fe33c06a2..e08769e4821c 100644
|
||||
--- a/toolkit/components/extensions/parent/ext-storage.js
|
||||
+++ b/toolkit/components/extensions/parent/ext-storage.js
|
||||
@@ -177,6 +177,14 @@ this.storage = class extends ExtensionAPI {
|
||||
|
||||
let data = await lookup;
|
||||
if (!data) {
|
||||
+ const assetsBootstrapLocation = Services.prefs.getStringPref("camoufox.uBO.assetsBootstrapLocation", undefined);
|
||||
+ if (extension.id == "uBlock0@raymondhill.net" && assetsBootstrapLocation) {
|
||||
+ return {
|
||||
+ adminSettings: {
|
||||
+ assetsBootstrapLocation
|
||||
+ }
|
||||
@@ -408,6 +408,14 @@ this.storage = class extends ExtensionAPIPersistent {
|
||||
managed: {
|
||||
async get(keys) {
|
||||
enforceNoTemporaryAddon(extension.id);
|
||||
+ const assetsBootstrapLocation = Services.prefs.getStringPref("librewolf.uBO.assetsBootstrapLocation", undefined);
|
||||
+ if (extension.id == "uBlock0@raymondhill.net" && assetsBootstrapLocation) {
|
||||
+ return {
|
||||
+ adminSettings: {
|
||||
+ assetsBootstrapLocation
|
||||
+ }
|
||||
+ }
|
||||
return Promise.reject({
|
||||
message: "Managed storage manifest not found",
|
||||
});
|
||||
+ }
|
||||
let data = await getManagedStorageManifestData(extension, context);
|
||||
return ExtensionStorage._filterProperties(extension.id, data, keys);
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@ index aad9d7054af8..a926d8b6abdc 100644
|
||||
|
||||
aDestinationName =
|
||||
- nsPrintfCString("org.mozilla.%s.%s", aProgram, profileName.get());
|
||||
+ nsPrintfCString("com.camoufox.%s.%s", aProgram, profileName.get());
|
||||
+ nsPrintfCString("io.gitlab.%s.%s", aProgram, profileName.get());
|
||||
if (aDestinationName.Length() > DBUS_MAXIMUM_NAME_LENGTH)
|
||||
aDestinationName.Truncate(DBUS_MAXIMUM_NAME_LENGTH);
|
||||
|
||||
@@ -16,7 +16,7 @@ index aad9d7054af8..a926d8b6abdc 100644
|
||||
// We don't have a valid busName yet - try to create a default one.
|
||||
aDestinationName =
|
||||
- nsPrintfCString("org.mozilla.%s.%s", aProgram, "default");
|
||||
+ nsPrintfCString("com.camoufox.%s.%s", aProgram, "default");
|
||||
+ nsPrintfCString("io.gitlab.%s.%s", aProgram, "default");
|
||||
if (!sDBusValidateBusName(aDestinationName.get(), nullptr)) {
|
||||
// We failed completelly to get a valid bus name - just quit
|
||||
// to prevent crash at dbus_bus_request_name().
|
||||
@@ -34,7 +34,7 @@ index aad9d7054af8..a926d8b6abdc 100644
|
||||
|
||||
nsAutoCString remoteInterfaceName;
|
||||
- remoteInterfaceName = nsPrintfCString("org.mozilla.%s", appName.get());
|
||||
+ remoteInterfaceName = nsPrintfCString("com.camoufox.%s", appName.get());
|
||||
+ remoteInterfaceName = nsPrintfCString("io.gitlab.%s", appName.get());
|
||||
|
||||
LOG(" DBus destination: %s\n", destinationName.get());
|
||||
LOG(" DBus path: %s\n", pathName.get());
|
||||
@@ -47,7 +47,7 @@ index 4afb8381d0bc..bd927334b817 100644
|
||||
"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
|
||||
"<node>\n"
|
||||
- " <interface name=\"org.mozilla.%s\">\n"
|
||||
+ " <interface name=\"com.camoufox.%s\">\n"
|
||||
+ " <interface name=\"io.gitlab.%s\">\n"
|
||||
" <method name=\"OpenURL\">\n"
|
||||
" <arg name=\"url\" direction=\"in\" type=\"ay\"/>\n"
|
||||
" </method>\n"
|
||||
@@ -56,7 +56,7 @@ index 4afb8381d0bc..bd927334b817 100644
|
||||
const gchar* aMethodName,
|
||||
Span<const gchar> aParam) {
|
||||
- nsPrintfCString ourInterfaceName("org.mozilla.%s", mAppName.get());
|
||||
+ nsPrintfCString ourInterfaceName("com.camoufox.%s", mAppName.get());
|
||||
+ nsPrintfCString ourInterfaceName("io.gitlab.%s", mAppName.get());
|
||||
|
||||
if ((strcmp("OpenURL", aMethodName) != 0) ||
|
||||
(strcmp(ourInterfaceName.get(), aInterfaceName) != 0)) {
|
||||
@@ -65,7 +65,7 @@ index 4afb8381d0bc..bd927334b817 100644
|
||||
|
||||
void nsDBusRemoteServer::OnBusAcquired(GDBusConnection* aConnection) {
|
||||
- mPathName = nsPrintfCString("/org/mozilla/%s/Remote", mAppName.get());
|
||||
+ mPathName = nsPrintfCString("/com/camoufox/%s/Remote", mAppName.get());
|
||||
+ mPathName = nsPrintfCString("/io/gitlab/%s/Remote", mAppName.get());
|
||||
static auto sDBusValidatePathName = (bool (*)(const char*, DBusError*))dlsym(
|
||||
RTLD_DEFAULT, "dbus_validate_path");
|
||||
if (!sDBusValidatePathName ||
|
||||
@@ -75,7 +75,7 @@ index 4afb8381d0bc..bd927334b817 100644
|
||||
|
||||
- nsPrintfCString busName("org.mozilla.%s.%s", mAppName.get(),
|
||||
- profileName.get());
|
||||
+ nsPrintfCString busName("com.camoufox.%s.%s", mAppName.get(), profileName.get());
|
||||
+ nsPrintfCString busName("io.gitlab.%s.%s", mAppName.get(), profileName.get());
|
||||
if (busName.Length() > DBUS_MAXIMUM_NAME_LENGTH) {
|
||||
busName.Truncate(DBUS_MAXIMUM_NAME_LENGTH);
|
||||
}
|
||||
@@ -84,7 +84,7 @@ index 4afb8381d0bc..bd927334b817 100644
|
||||
// We don't have a valid busName yet - try to create a default one.
|
||||
if (!sDBusValidateBusName(busName.get(), nullptr)) {
|
||||
- busName = nsPrintfCString("org.mozilla.%s.%s", mAppName.get(), "default");
|
||||
+ busName = nsPrintfCString("com.camoufox.%s.%s", mAppName.get(), "default");
|
||||
+ busName = nsPrintfCString("io.gitlab.%s.%s", mAppName.get(), "default");
|
||||
if (!sDBusValidateBusName(busName.get(), nullptr)) {
|
||||
// We failed completelly to get a valid bus name - just quit
|
||||
// to prevent crash at dbus_bus_request_name().
|
||||
|
||||
@@ -7,7 +7,7 @@ index d91f463..3c1ba5a 100644
|
||||
// Note that the client avoids trying to call attach if already attached.
|
||||
// But just in case, avoid any possible duplicate call to attach.
|
||||
- if (this.alreadyAttached) {
|
||||
+ let forceDetach = Services.prefs.getBoolPref("camoufox.debugger.force_detach", false);
|
||||
+ let forceDetach = Services.prefs.getBoolPref("librewolf.debugger.force_detach", false);
|
||||
+ if (this.alreadyAttached || forceDetach) {
|
||||
return;
|
||||
}
|
||||
@@ -16,12 +16,12 @@ diff --git a/devtools/server/actors/webconsole/listeners/console-api.js b/devtoo
|
||||
index 3e5d0bc..8260e2b 100644
|
||||
--- a/devtools/server/actors/webconsole/listeners/console-api.js
|
||||
+++ b/devtools/server/actors/webconsole/listeners/console-api.js
|
||||
@@ -97,7 +97,8 @@ class ConsoleAPIListener {
|
||||
@@ -97,6 +97,7 @@ class ConsoleAPIListener {
|
||||
* The message object receives from the ConsoleAPIStorage service.
|
||||
*/
|
||||
onConsoleAPILogEvent(message) {
|
||||
- if (!this.handler) {
|
||||
+ let disableConsole = Services.prefs.getBoolPref("camoufox.console.logging_disabled", false);
|
||||
+ let disableConsole = Services.prefs.getBoolPref("librewolf.console.logging_disabled", false);
|
||||
+ if (!this.handler || disableConsole) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/browser/moz.configure b/browser/moz.configure
|
||||
index e605019172ad..73268e5d353d 100644
|
||||
index e8b401a7df..7b63573c17 100644
|
||||
--- a/browser/moz.configure
|
||||
+++ b/browser/moz.configure
|
||||
@@ -5,11 +5,11 @@
|
||||
@@ -5,15 +5,16 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
imply_option("MOZ_PLACES", True)
|
||||
@@ -16,11 +16,17 @@ index e605019172ad..73268e5d353d 100644
|
||||
imply_option("MOZ_PROFILE_MIGRATOR", True)
|
||||
|
||||
|
||||
-imply_option("MOZ_APP_VENDOR", "Mozilla")
|
||||
+imply_option("MOZ_APP_VENDOR", "Camoufox")
|
||||
+imply_option("MOZ_APP_PROFILE", "camoufox")
|
||||
imply_option("MOZ_APP_ID", "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}")
|
||||
# Include the DevTools client, not just the server (which is the default)
|
||||
imply_option("MOZ_DEVTOOLS", "all")
|
||||
diff --git a/python/mach/mach/telemetry.py b/python/mach/mach/telemetry.py
|
||||
index 40d5cc221644..36db9a9e45b1 100644
|
||||
index bdc3b958b3..1d42c0b927 100644
|
||||
--- a/python/mach/mach/telemetry.py
|
||||
+++ b/python/mach/mach/telemetry.py
|
||||
@@ -98,10 +98,7 @@ def is_applicable_telemetry_environment():
|
||||
@@ -92,10 +92,7 @@ def is_applicable_telemetry_environment():
|
||||
|
||||
|
||||
def is_telemetry_enabled(settings):
|
||||
|
||||
@@ -1,74 +1,157 @@
|
||||
diff --git a/toolkit/components/extensions/NativeManifests.sys.mjs b/toolkit/components/extensions/NativeManifests.sys.mjs
|
||||
index 59313e821f..f3a7caa057 100644
|
||||
--- a/toolkit/components/extensions/NativeManifests.sys.mjs
|
||||
+++ b/toolkit/components/extensions/NativeManifests.sys.mjs
|
||||
@@ -40,6 +40,8 @@
|
||||
let dirs = [
|
||||
Services.dirsvc.get("XREUserNativeManifests", Ci.nsIFile).path,
|
||||
Services.dirsvc.get("XRESysNativeManifests", Ci.nsIFile).path,
|
||||
+ Services.dirsvc.get("XREMozUserNativeManifests", Ci.nsIFile).path,
|
||||
+ Services.dirsvc.get("XREMozSysNativeManifests", Ci.nsIFile).path,
|
||||
];
|
||||
this._lookup = (type, name, context) =>
|
||||
this._tryPaths(type, name, dirs, context);
|
||||
diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp
|
||||
index 9c94cb8808..af2dbdbf3f 100644
|
||||
--- a/toolkit/xre/nsXREDirProvider.cpp
|
||||
+++ b/toolkit/xre/nsXREDirProvider.cpp
|
||||
@@ -300,16 +300,16 @@ static nsresult GetSystemParentDirectory(nsIFile** aFile) {
|
||||
@@ -278,23 +278,25 @@
|
||||
* On Linux this is /usr/{lib,lib64}/mozilla
|
||||
* (for 32- and 64-bit systems respsectively)
|
||||
*/
|
||||
-static nsresult GetSystemParentDirectory(nsIFile** aFile) {
|
||||
+static nsresult GetSystemParentDirectory(nsIFile** aFile,
|
||||
+ nsCString aName = "LibreWolf"_ns) {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIFile> localDir;
|
||||
# if defined(XP_MACOSX)
|
||||
rv = GetOSXFolderType(kOnSystemDisk, kApplicationSupportFolderType,
|
||||
getter_AddRefs(localDir));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
- rv = localDir->AppendNative("Mozilla"_ns);
|
||||
+ rv = localDir->AppendNative("Camoufox"_ns);
|
||||
+ rv = localDir->AppendNative(aName);
|
||||
}
|
||||
# else
|
||||
constexpr auto dirname =
|
||||
- constexpr auto dirname =
|
||||
+ ToLowerCase(aName);
|
||||
+ nsCString dirname =
|
||||
# ifdef HAVE_USR_LIB64_DIR
|
||||
- "/usr/lib64/mozilla"_ns
|
||||
+ "/usr/lib64/camoufox"_ns
|
||||
+ "/usr/lib64/"_ns + aName
|
||||
# elif defined(__OpenBSD__) || defined(__FreeBSD__)
|
||||
- "/usr/local/lib/mozilla"_ns
|
||||
+ "/usr/local/lib/camoufox"_ns
|
||||
+ "/usr/local/lib/"_ns + aName
|
||||
# else
|
||||
- "/usr/lib/mozilla"_ns
|
||||
+ "/usr/lib/camoufox"_ns
|
||||
+ "/usr/lib/"_ns + aName
|
||||
# endif
|
||||
;
|
||||
rv = NS_NewNativeLocalFile(dirname, false, getter_AddRefs(localDir));
|
||||
@@ -371,9 +371,9 @@ nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent,
|
||||
rv = NS_NewNativeLocalFile(dirname, getter_AddRefs(localDir));
|
||||
@@ -362,10 +364,20 @@
|
||||
#if defined(XP_UNIX) || defined(XP_MACOSX)
|
||||
else if (!strcmp(aProperty, XRE_SYS_NATIVE_MANIFESTS)) {
|
||||
rv = ::GetSystemParentDirectory(getter_AddRefs(file));
|
||||
+ } else if (!strcmp(aProperty, XRE_MOZ_SYS_NATIVE_MANIFESTS)) {
|
||||
+ rv = ::GetSystemParentDirectory(getter_AddRefs(file), "Mozilla"_ns);
|
||||
} else if (!strcmp(aProperty, XRE_USER_NATIVE_MANIFESTS)) {
|
||||
rv = GetUserDataDirectoryHome(getter_AddRefs(file), false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
# if defined(XP_MACOSX)
|
||||
- rv = file->AppendNative("Mozilla"_ns);
|
||||
+ rv = file->AppendNative("Camoufox"_ns);
|
||||
+ rv = file->AppendNative("LibreWolf"_ns);
|
||||
+# else // defined(XP_MACOSX)
|
||||
+ rv = file->AppendNative(".librewolf"_ns);
|
||||
+# endif // defined(XP_MACOSX)
|
||||
+ } else if (!strcmp(aProperty, XRE_MOZ_USER_NATIVE_MANIFESTS)) {
|
||||
+ rv = GetUserDataDirectoryHome(getter_AddRefs(file), false);
|
||||
+ NS_ENSURE_SUCCESS(rv, rv);
|
||||
+# if defined(XP_MACOSX)
|
||||
rv = file->AppendNative("Mozilla"_ns);
|
||||
# else // defined(XP_MACOSX)
|
||||
- rv = file->AppendNative(".mozilla"_ns);
|
||||
+ rv = file->AppendNative(".camoufox"_ns);
|
||||
# endif // defined(XP_MACOSX)
|
||||
}
|
||||
#endif // defined(XP_UNIX) || defined(XP_MACOSX)
|
||||
@@ -411,9 +411,9 @@ nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent,
|
||||
rv = file->AppendNative(".mozilla"_ns);
|
||||
@@ -398,9 +410,10 @@
|
||||
else if (!strcmp(aProperty, XRE_SYS_SHARE_EXTENSION_PARENT_DIR)) {
|
||||
# ifdef ENABLE_SYSTEM_EXTENSION_DIRS
|
||||
# if defined(__OpenBSD__) || defined(__FreeBSD__)
|
||||
- static const char* const sysLExtDir = "/usr/local/share/mozilla/extensions";
|
||||
+ static const char* const sysLExtDir = "/usr/local/share/camoufox/extensions";
|
||||
+ static const char* const sysLExtDir =
|
||||
+ "/usr/local/share/librewolf/extensions";
|
||||
# else
|
||||
- static const char* const sysLExtDir = "/usr/share/mozilla/extensions";
|
||||
+ static const char* const sysLExtDir = "/usr/share/camoufox/extensions";
|
||||
+ static const char* const sysLExtDir = "/usr/share/librewolf/extensions";
|
||||
# endif
|
||||
rv = NS_NewNativeLocalFile(nsDependentCString(sysLExtDir), false,
|
||||
rv = NS_NewNativeLocalFile(nsDependentCString(sysLExtDir),
|
||||
getter_AddRefs(file));
|
||||
@@ -1114,7 +1114,7 @@ nsresult nsXREDirProvider::GetUpdateRootDir(nsIFile** aResult,
|
||||
nsDependentCString(hasVendor ? GetAppVendor() : GetAppName())))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@@ -926,13 +939,7 @@
|
||||
}
|
||||
appDirPath = Substring(appDirPath, 1, dotIndex - 1);
|
||||
|
||||
- bool hasVendor = GetAppVendor() && strlen(GetAppVendor()) != 0;
|
||||
- if (hasVendor || GetAppName()) {
|
||||
- if (NS_FAILED(localDir->AppendNative(
|
||||
- nsDependentCString(hasVendor ? GetAppVendor() : GetAppName())))) {
|
||||
- return NS_ERROR_FAILURE;
|
||||
- }
|
||||
- } else if (NS_FAILED(localDir->AppendNative("Mozilla"_ns))) {
|
||||
+ } else if (NS_FAILED(localDir->AppendNative("Camoufox"_ns))) {
|
||||
+ if (NS_FAILED(localDir->AppendNative("LibreWolf"_ns))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
@@ -1363,7 +1363,7 @@ nsresult nsXREDirProvider::AppendSysUserExtensionPath(nsIFile* aFile) {
|
||||
@@ -1192,7 +1199,7 @@
|
||||
|
||||
#if defined(XP_MACOSX) || defined(XP_WIN)
|
||||
|
||||
- static const char* const sXR = "Mozilla";
|
||||
+ static const char* const sXR = "Camoufox";
|
||||
+ static const char* const sXR = "LibreWolf";
|
||||
rv = aFile->AppendNative(nsDependentCString(sXR));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@@ -1373,7 +1373,7 @@ nsresult nsXREDirProvider::AppendSysUserExtensionPath(nsIFile* aFile) {
|
||||
@@ -1202,7 +1209,7 @@
|
||||
|
||||
#elif defined(XP_UNIX)
|
||||
|
||||
- static const char* const sXR = ".mozilla";
|
||||
+ static const char* const sXR = ".camoufox";
|
||||
+ static const char* const sXR = ".librewolf";
|
||||
rv = aFile->AppendNative(nsDependentCString(sXR));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@@ -1253,10 +1260,6 @@
|
||||
if (!profile.IsEmpty()) {
|
||||
rv = AppendProfileString(aFile, profile.get());
|
||||
} else {
|
||||
- if (!vendor.IsEmpty()) {
|
||||
- rv = aFile->AppendNative(vendor);
|
||||
- NS_ENSURE_SUCCESS(rv, rv);
|
||||
- }
|
||||
rv = aFile->AppendNative(appName);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@@ -1288,16 +1291,6 @@
|
||||
|
||||
rv = AppendProfileString(aFile, folder.BeginReading());
|
||||
} else {
|
||||
- if (!vendor.IsEmpty()) {
|
||||
- folder.Append(vendor);
|
||||
- ToLowerCase(folder);
|
||||
-
|
||||
- rv = aFile->AppendNative(folder);
|
||||
- NS_ENSURE_SUCCESS(rv, rv);
|
||||
-
|
||||
- folder.Truncate();
|
||||
- }
|
||||
-
|
||||
// This can be the case in tests.
|
||||
if (!appName.IsEmpty()) {
|
||||
folder.Append(appName);
|
||||
diff --git a/xpcom/build/nsXULAppAPI.h b/xpcom/build/nsXULAppAPI.h
|
||||
index 28a2a68c63..b0917fb9b8 100644
|
||||
--- a/xpcom/build/nsXULAppAPI.h
|
||||
+++ b/xpcom/build/nsXULAppAPI.h
|
||||
@@ -116,6 +116,8 @@
|
||||
*/
|
||||
# define XRE_SYS_NATIVE_MANIFESTS "XRESysNativeManifests"
|
||||
# define XRE_USER_NATIVE_MANIFESTS "XREUserNativeManifests"
|
||||
+# define XRE_MOZ_SYS_NATIVE_MANIFESTS "XREMozSysNativeManifests"
|
||||
+# define XRE_MOZ_USER_NATIVE_MANIFESTS "XREMozUserNativeManifests"
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,9 +22,13 @@ def tzstd_path(path):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Download and build a Visual Studio artifact")
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Download and build a Visual Studio artifact"
|
||||
)
|
||||
parser.add_argument("manifest", help="YAML manifest of the contents to download")
|
||||
parser.add_argument("-o", dest="output", type=tzstd_path, required=True, help="Output file")
|
||||
parser.add_argument(
|
||||
"-o", dest="output", type=tzstd_path, required=True, help="Output file"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.manifest) as f:
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
diff --git a/browser/extensions/moz.build b/browser/extensions/moz.build
|
||||
index 3c6e7eb88689..d923333aa100 100644
|
||||
--- a/browser/extensions/moz.build
|
||||
+++ b/browser/extensions/moz.build
|
||||
@@ -8,7 +8,6 @@ DIRS += [
|
||||
"formautofill",
|
||||
"screenshots",
|
||||
"webcompat",
|
||||
- "report-site-issue",
|
||||
"pictureinpicture",
|
||||
"search-detection",
|
||||
]
|
||||
diff --git a/browser/locales/Makefile.in b/browser/locales/Makefile.in
|
||||
index 7296ca447757..1278582e79d7 100644
|
||||
--- a/browser/locales/Makefile.in
|
||||
+++ b/browser/locales/Makefile.in
|
||||
@@ -56,7 +56,6 @@ l10n-%:
|
||||
ifneq (,$(wildcard ../extensions/formautofill/locales))
|
||||
@$(MAKE) -C ../extensions/formautofill/locales AB_CD=$* XPI_NAME=locale-$*
|
||||
endif
|
||||
- @$(MAKE) -C ../extensions/report-site-issue/locales AB_CD=$* XPI_NAME=locale-$*
|
||||
@$(MAKE) -C ../../devtools/client/locales AB_CD=$* XPI_NAME=locale-$* XPI_ROOT_APPID='$(XPI_ROOT_APPID)'
|
||||
@$(MAKE) -C ../../devtools/startup/locales AB_CD=$* XPI_NAME=locale-$* XPI_ROOT_APPID='$(XPI_ROOT_APPID)'
|
||||
@$(MAKE) l10n AB_CD=$* XPI_NAME=locale-$* PREF_DIR=$(PREF_DIR)
|
||||
@@ -78,7 +77,6 @@ endif
|
||||
@$(MAKE) -C ../../devtools/startup/locales chrome AB_CD=$*
|
||||
@$(MAKE) chrome AB_CD=$*
|
||||
@$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales chrome AB_CD=$*
|
||||
- @$(MAKE) -C ../extensions/report-site-issue/locales chrome AB_CD=$*
|
||||
|
||||
package-win32-installer: $(SUBMAKEFILES)
|
||||
$(MAKE) -C ../installer/windows CONFIG_DIR=l10ngen ZIP_IN='$(ZIP_OUT)' installer
|
||||
diff --git a/browser/locales/filter.py b/browser/locales/filter.py
|
||||
index 22eb5cbdb177..5e1f09ff383e 100644
|
||||
--- a/browser/locales/filter.py
|
||||
+++ b/browser/locales/filter.py
|
||||
@@ -17,7 +17,6 @@ def test(mod, path, entity=None):
|
||||
"devtools/startup",
|
||||
"browser",
|
||||
"browser/extensions/formautofill",
|
||||
- "browser/extensions/report-site-issue",
|
||||
"extensions/spellcheck",
|
||||
"other-licenses/branding/firefox",
|
||||
"browser/branding/official",
|
||||
diff --git a/browser/locales/l10n.ini b/browser/locales/l10n.ini
|
||||
index 7a6599740b20..7f976679eefc 100644
|
||||
--- a/browser/locales/l10n.ini
|
||||
+++ b/browser/locales/l10n.ini
|
||||
@@ -13,7 +13,6 @@ dirs = browser
|
||||
devtools/client
|
||||
devtools/startup
|
||||
browser/extensions/formautofill
|
||||
- browser/extensions/report-site-issue
|
||||
|
||||
[includes]
|
||||
# non-central apps might want to use %(topsrcdir)s here, or other vars
|
||||
diff --git a/browser/locales/l10n.toml b/browser/locales/l10n.toml
|
||||
index e9d50107cb10..914a2f1a6310 100644
|
||||
--- a/browser/locales/l10n.toml
|
||||
+++ b/browser/locales/l10n.toml
|
||||
@@ -133,10 +133,6 @@ locales = [
|
||||
reference = "browser/extensions/formautofill/locales/en-US/**"
|
||||
l10n = "{l}browser/extensions/formautofill/**"
|
||||
|
||||
-[[paths]]
|
||||
- reference = "browser/extensions/report-site-issue/locales/en-US/**"
|
||||
- l10n = "{l}browser/extensions/report-site-issue/**"
|
||||
-
|
||||
|
||||
[[includes]]
|
||||
path = "toolkit/locales/l10n.toml"
|
||||
--
|
||||
2.37.3
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/build/moz.configure/rust.configure b/build/moz.configure/rust.configure
|
||||
index 7a2fd1ae70..7abdcaa2c1 100644
|
||||
index 6c1c891817..692c8d3ef3 100644
|
||||
--- a/build/moz.configure/rust.configure
|
||||
+++ b/build/moz.configure/rust.configure
|
||||
@@ -404,11 +404,21 @@ def detect_rustc_target(
|
||||
@@ -412,11 +412,21 @@ def detect_rustc_target(
|
||||
if len(narrowed) == 1:
|
||||
return narrowed[0].rust_target
|
||||
|
||||
@@ -24,4 +24,4 @@ index 7a2fd1ae70..7abdcaa2c1 100644
|
||||
+
|
||||
return None
|
||||
|
||||
rustc_target = find_candidate(candidates)
|
||||
rustc_target = find_candidate(candidates)
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
diff --git a/browser/components/enterprisepolicies/schemas/policies-schema.json b/browser/components/enterprisepolicies/schemas/policies-schema.json
|
||||
index d436cf1ca1..ecd6e53b9e 100644
|
||||
--- a/browser/components/enterprisepolicies/schemas/policies-schema.json
|
||||
+++ b/browser/components/enterprisepolicies/schemas/policies-schema.json
|
||||
@@ -1074,7 +1074,7 @@
|
||||
},
|
||||
|
||||
"SearchEngines": {
|
||||
- "enterprise_only": true,
|
||||
+ "enterprise_only": false,
|
||||
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1,36 +0,0 @@
|
||||
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
|
||||
index 43c84c3..4b4aad2 100644
|
||||
--- a/browser/base/content/browser.js
|
||||
+++ b/browser/base/content/browser.js
|
||||
@@ -5478,7 +5478,6 @@ var XULBrowserWindow = {
|
||||
|
||||
SafeBrowsingNotificationBox.onLocationChange(aLocationURI);
|
||||
|
||||
- SaveToPocket.onLocationChange(window);
|
||||
|
||||
let originalURI;
|
||||
if (
|
||||
diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.sys.mjs
|
||||
index b2479db..7670f08 100644
|
||||
--- a/browser/components/BrowserGlue.sys.mjs
|
||||
+++ b/browser/components/BrowserGlue.sys.mjs
|
||||
@@ -1274,7 +1274,6 @@ BrowserGlue.prototype = {
|
||||
lazy.Normandy.init();
|
||||
}
|
||||
|
||||
- lazy.SaveToPocket.init();
|
||||
|
||||
AboutHomeStartupCache.init();
|
||||
|
||||
diff --git a/browser/components/moz.build b/browser/components/moz.build
|
||||
index 3b2126e..32b7831 100644
|
||||
--- a/browser/components/moz.build
|
||||
+++ b/browser/components/moz.build
|
||||
@@ -44,7 +44,6 @@ DIRS += [
|
||||
"originattributes",
|
||||
"pagedata",
|
||||
"places",
|
||||
- "pocket",
|
||||
"preferences",
|
||||
"privatebrowsing",
|
||||
"prompts",
|
||||
@@ -1,37 +1,13 @@
|
||||
diff --git a/browser/components/asrouter/content/asrouter-admin.bundle.js b/browser/components/asrouter/content/asrouter-admin.bundle.js
|
||||
index 7d0ed87430..ea7ed22ade 100644
|
||||
--- a/browser/components/asrouter/content/asrouter-admin.bundle.js
|
||||
+++ b/browser/components/asrouter/content/asrouter-admin.bundle.js
|
||||
@@ -1641,7 +1641,7 @@ class ASRouterAdminInner extends (react__WEBPACK_IMPORTED_MODULE_1___default().P
|
||||
@@ -1154,7 +1154,7 @@ class ASRouterAdminInner extends (react__WEBPACK_IMPORTED_MODULE_1___default().P
|
||||
label = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("span", null, "remote settings (", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1___default().createElement("a", {
|
||||
className: "providerUrl",
|
||||
className: "small-text",
|
||||
target: "_blank",
|
||||
- href: "https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/nimbus-desktop-experiments/records",
|
||||
+ href: "https://see.ticket.about.this.invalid",
|
||||
rel: "noopener noreferrer"
|
||||
}, "nimbus-desktop-experiments"), ")");
|
||||
}
|
||||
--- a/services/settings/Utils.sys.mjs
|
||||
+++ b/services/settings/Utils.sys.mjs
|
||||
@@ -51,6 +51,9 @@ ChromeUtils.defineLazyGetter(lazy, "isRunningTests", () => {
|
||||
// Overriding the server URL is normally disabled on Beta and Release channels,
|
||||
// except under some conditions.
|
||||
ChromeUtils.defineLazyGetter(lazy, "allowServerURLOverride", () => {
|
||||
+
|
||||
+ return true; // always override in LW
|
||||
+
|
||||
if (!AppConstants.RELEASE_OR_BETA) {
|
||||
// Always allow to override the server URL on Nightly/DevEdition.
|
||||
return true;
|
||||
--- a/toolkit/components/search/SearchUtils.sys.mjs
|
||||
+++ b/toolkit/components/search/SearchUtils.sys.mjs
|
||||
@@ -193,9 +193,9 @@ export var SearchUtils = {
|
||||
|
||||
ENGINES_URLS: {
|
||||
"prod-main":
|
||||
- "https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/search-config/records",
|
||||
+ "https://%.invalid",
|
||||
"prod-preview":
|
||||
- "https://firefox.settings.services.mozilla.com/v1/buckets/main-preview/collections/search-config/records",
|
||||
+ "https://%.invalid",
|
||||
"stage-main":
|
||||
"https://firefox.settings.services.allizom.org/v1/buckets/main/collections/search-config/records",
|
||||
"stage-preview":
|
||||
|
||||
@@ -1,22 +1,102 @@
|
||||
diff --git a/browser/components/preferences/main.inc.xhtml b/browser/components/preferences/main.inc.xhtml
|
||||
index 8485423..b007b96 100644
|
||||
--- a/browser/components/preferences/main.inc.xhtml
|
||||
+++ b/browser/components/preferences/main.inc.xhtml
|
||||
@@ -29,6 +29,8 @@
|
||||
<vbox id="startupPageBox">
|
||||
<checkbox id="browserRestoreSession"
|
||||
data-l10n-id="startup-restore-windows-and-tabs"/>
|
||||
+ <html:a is="moz-support-link" support-page="session-restore" data-l10n-id="session-restore-learn-more" />
|
||||
+
|
||||
#ifdef XP_WIN
|
||||
<hbox id="windowsLaunchOnLoginBox" align="center" hidden="true">
|
||||
<checkbox id="windowsLaunchOnLogin"
|
||||
@@ -46,7 +48,7 @@
|
||||
</vbox>
|
||||
diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js
|
||||
index 46283d3..9e14d59 100644
|
||||
--- a/browser/components/preferences/main.js
|
||||
+++ b/browser/components/preferences/main.js
|
||||
@@ -1000,65 +1000,6 @@ const DefaultBrowserHelper = {
|
||||
},
|
||||
};
|
||||
|
||||
#ifdef HAVE_SHELL_SERVICE
|
||||
- <vbox id="defaultBrowserBox">
|
||||
+ <vbox id="defaultBrowserBox" hidden="true">
|
||||
<checkbox id="alwaysCheckDefault" preference="browser.shell.checkDefaultBrowser"
|
||||
disabled="true"
|
||||
data-l10n-id="always-check-default"/>
|
||||
-Preferences.addSetting({
|
||||
- id: "alwaysCheckDefault",
|
||||
- pref: "browser.shell.checkDefaultBrowser",
|
||||
- setup: emitChange => {
|
||||
- if (!DefaultBrowserHelper.canCheck) {
|
||||
- return;
|
||||
- }
|
||||
- DefaultBrowserHelper.pollForDefaultChanges(emitChange);
|
||||
- // eslint-disable-next-line consistent-return
|
||||
- return () => DefaultBrowserHelper.clearPollingForDefaultChanges();
|
||||
- },
|
||||
- /**
|
||||
- * Show button for setting browser as default browser or information that
|
||||
- * browser is already the default browser.
|
||||
- */
|
||||
- visible: () => DefaultBrowserHelper.canCheck,
|
||||
- disabled: (deps, setting) =>
|
||||
- !DefaultBrowserHelper.canCheck ||
|
||||
- setting.locked ||
|
||||
- DefaultBrowserHelper.isBrowserDefault,
|
||||
-});
|
||||
-
|
||||
-Preferences.addSetting({
|
||||
- id: "isDefaultPane",
|
||||
- deps: ["alwaysCheckDefault"],
|
||||
- visible: () =>
|
||||
- DefaultBrowserHelper.canCheck && DefaultBrowserHelper.isBrowserDefault,
|
||||
-});
|
||||
-
|
||||
-Preferences.addSetting({
|
||||
- id: "isNotDefaultPane",
|
||||
- deps: ["alwaysCheckDefault"],
|
||||
- visible: () =>
|
||||
- DefaultBrowserHelper.canCheck && !DefaultBrowserHelper.isBrowserDefault,
|
||||
- onUserClick: (e, { alwaysCheckDefault }) => {
|
||||
- if (!DefaultBrowserHelper.canCheck) {
|
||||
- return;
|
||||
- }
|
||||
- const setDefaultButton = /** @type {MozButton} */ (e.target);
|
||||
-
|
||||
- if (!setDefaultButton) {
|
||||
- return;
|
||||
- }
|
||||
- if (setDefaultButton.disabled) {
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- /**
|
||||
- * Disable the set default button, so that the user
|
||||
- * doesn't try to hit it again while browser is being set to default.
|
||||
- */
|
||||
- setDefaultButton.disabled = true;
|
||||
- alwaysCheckDefault.value = true;
|
||||
- DefaultBrowserHelper.setDefaultBrowser().finally(() => {
|
||||
- setDefaultButton.disabled = false;
|
||||
- });
|
||||
- },
|
||||
-});
|
||||
-
|
||||
// Performance settings
|
||||
Preferences.addSetting({
|
||||
id: "contentProcessCount",
|
||||
@@ -1141,31 +1082,6 @@ let SETTINGS_CONFIG = {
|
||||
control: "moz-message-bar",
|
||||
l10nId: "startup-windows-launch-on-login-profile-disabled",
|
||||
},
|
||||
- {
|
||||
- id: "alwaysCheckDefault",
|
||||
- l10nId: "always-check-default",
|
||||
- },
|
||||
- {
|
||||
- id: "isDefaultPane",
|
||||
- l10nId: "is-default-browser",
|
||||
- control: "moz-promo",
|
||||
- },
|
||||
- {
|
||||
- id: "isNotDefaultPane",
|
||||
- l10nId: "is-not-default-browser",
|
||||
- control: "moz-promo",
|
||||
- options: [
|
||||
- {
|
||||
- control: "moz-button",
|
||||
- l10nId: "set-as-my-default-browser",
|
||||
- id: "setDefaultButton",
|
||||
- controlAttrs: {
|
||||
- slot: "actions",
|
||||
- type: "primary",
|
||||
- },
|
||||
- },
|
||||
- ],
|
||||
- },
|
||||
],
|
||||
},
|
||||
zoom: {
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
--- a/browser/locales/en-US/browser/browser.ftl
|
||||
+++ b/browser/locales/en-US/browser/browser.ftl
|
||||
@@ -543,7 +543,7 @@ urlbar-result-action-search-tabs = Searc
|
||||
# A label shown above the "Firefox Suggest" (bookmarks/history) group in the
|
||||
# urlbar results.
|
||||
urlbar-group-firefox-suggest =
|
||||
- .label = { -firefox-suggest-brand-name }
|
||||
+ .label = Suggestions
|
||||
|
||||
# A label shown above the search suggestions group in the urlbar results. It
|
||||
# should use sentence case.
|
||||
@@ -1,20 +1,23 @@
|
||||
--- a/browser/components/preferences/main.inc.xhtml
|
||||
+++ b/browser/components/preferences/main.inc.xhtml
|
||||
@@ -742,7 +742,7 @@
|
||||
support-page="media-keyboard-control"
|
||||
/>
|
||||
</hbox>
|
||||
- <hbox align="center" data-subcategory="cfraddons">
|
||||
+ <hbox align="center" data-subcategory="cfraddons" hidden="true">
|
||||
<checkbox id="cfrRecommendations"
|
||||
class="tail-with-learn-more"
|
||||
data-l10n-id="browsing-cfr-recommendations"
|
||||
@@ -752,7 +752,7 @@
|
||||
support-page="extensionrecommendations"
|
||||
/>
|
||||
</hbox>
|
||||
- <hbox align="center" data-subcategory="cfrfeatures">
|
||||
+ <hbox align="center" data-subcategory="cfrfeatures" hidden="true">
|
||||
<checkbox id="cfrRecommendations-features"
|
||||
class="tail-with-learn-more"
|
||||
data-l10n-id="browsing-cfr-features"
|
||||
diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js
|
||||
index a60831d930..e8d5d9581e 100644
|
||||
--- a/browser/components/preferences/main.js
|
||||
+++ b/browser/components/preferences/main.js
|
||||
@@ -379,18 +379,6 @@
|
||||
supportPage: "media-keyboard-control",
|
||||
},
|
||||
{
|
||||
- id: "cfrRecommendations",
|
||||
- l10nId: "browsing-cfr-recommendations",
|
||||
- supportPage: "extensionrecommendations",
|
||||
- subcategory: "cfraddons",
|
||||
- },
|
||||
- {
|
||||
- id: "cfrRecommendations-features",
|
||||
- l10nId: "browsing-cfr-features",
|
||||
- supportPage: "extensionrecommendations",
|
||||
- subcategory: "cfrfeatures",
|
||||
- },
|
||||
- {
|
||||
id: "linkPreviewEnabled",
|
||||
l10nId: "link-preview-settings-enable",
|
||||
subcategory: "link-preview",
|
||||
|
||||
@@ -2,7 +2,7 @@ diff --git a/browser/components/urlbar/UrlbarProviderInterventions.sys.mjs b/bro
|
||||
index 575205158bb8..e0c76c682a7d 100644
|
||||
--- a/browser/components/urlbar/UrlbarProviderInterventions.sys.mjs
|
||||
+++ b/browser/components/urlbar/UrlbarProviderInterventions.sys.mjs
|
||||
@@ -454,7 +454,7 @@ class ProviderInterventions extends UrlbarProvider {
|
||||
@@ -454,7 +454,7 @@ export class UrlbarProviderInterventions extends UrlbarProvider {
|
||||
]),
|
||||
});
|
||||
for (let [id, phrases] of Object.entries(DOCUMENTS)) {
|
||||
@@ -11,6 +11,3 @@ index 575205158bb8..e0c76c682a7d 100644
|
||||
}
|
||||
return queryScorer;
|
||||
});
|
||||
--
|
||||
2.37.3
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/browser/components/urlbar/UrlbarProviderInterventions.sys.mjs b/browser/components/urlbar/UrlbarProviderInterventions.sys.mjs
|
||||
index 575205158bb8..e0c76c682a7d 100644
|
||||
--- a/browser/components/urlbar/UrlbarProviderInterventions.sys.mjs
|
||||
+++ b/browser/components/urlbar/UrlbarProviderInterventions.sys.mjs
|
||||
@@ -454,9 +454,8 @@ export class UrlbarProviderInterventions extends UrlbarProvider {
|
||||
]),
|
||||
});
|
||||
for (let [id, phrases] of Object.entries(DOCUMENTS)) {
|
||||
- queryScorer.addDocument({ id, phrases });
|
||||
+// queryScorer.addDocument({ id, phrases });
|
||||
}
|
||||
return queryScorer;
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
# macOS Sandbox Crash Fix
|
||||
|
||||
## Purpose
|
||||
|
||||
Fixes a crash on macOS where Camoufox segfaults during content process launch due to `MOZ_CRASH` being triggered when `GetRepoDir()` or `GetObjDir()` fail in non-packaged builds.
|
||||
|
||||
## Problem
|
||||
|
||||
In `CacheSandboxParams()`, the code attempts to get repository and object directory paths for sandbox whitelisting in non-packaged builds:
|
||||
|
||||
```cpp
|
||||
if (!mozilla::IsPackagedBuild()) {
|
||||
rv = nsMacUtilsImpl::GetRepoDir(getter_AddRefs(repoDir));
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_CRASH("Failed to get path to repo dir"); // ← CRASH HERE
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
These functions (`GetRepoDir` and `GetObjDir`) read paths from the `.app` bundle's `Info.plist` file. In cross-compiled builds or unusual build configurations, these plist keys may not exist, causing the functions to fail and trigger `MOZ_CRASH`.
|
||||
|
||||
## Root Cause
|
||||
|
||||
1. **Cross-compilation from Ubuntu to macOS**: The build process may not populate the development-specific plist keys (`MAC_DEV_REPO_KEY`, `MAC_DEV_OBJ_KEY`)
|
||||
2. **IsPackagedBuild() detection**: In some build configurations, `IsPackagedBuild()` may incorrectly return `false`
|
||||
3. **Missing plist keys**: The Info.plist doesn't contain the required development directory keys
|
||||
|
||||
When any of these occur, the code crashes instead of gracefully handling the missing paths.
|
||||
|
||||
## Solution
|
||||
|
||||
Changed the code from crashing on failure to logging a warning and continuing without those sandbox paths:
|
||||
|
||||
```cpp
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsCString repoDirPath;
|
||||
(void)repoDir->GetNativePath(repoDirPath);
|
||||
info.testingReadPath3 = repoDirPath.get();
|
||||
} else {
|
||||
NS_WARNING("Failed to get repo dir path for sandbox, skipping testingReadPath3");
|
||||
}
|
||||
```
|
||||
|
||||
Since `testingReadPath3` and `testingReadPath4` are optional sandbox parameters (only used for whitelisting in development builds), it's safe to skip them if they can't be determined.
|
||||
|
||||
## Files Modified
|
||||
|
||||
- `dom/ipc/ContentParent.cpp`: Changed `MOZ_CRASH` to `NS_WARNING` for `GetRepoDir()` and `GetObjDir()` failures
|
||||
|
||||
## Platform
|
||||
|
||||
macOS only (wrapped in `#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)`)
|
||||
|
||||
## Apply Order
|
||||
|
||||
Apply after the Playwright patches (0-playwright.patch, 1-leak-fixes.patch) in the build sequence.
|
||||
|
||||
## Testing
|
||||
|
||||
The fix can be verified by:
|
||||
|
||||
1. Building Camoufox with the patch via cross-compilation from Ubuntu
|
||||
2. Running the macOS binary without `MOZ_DISABLE_CONTENT_SANDBOX=1`
|
||||
3. Verifying the browser launches without crashing
|
||||
4. Checking that content processes spawn correctly via `about:processes`
|
||||
5. Looking for warning messages in console output (if running from terminal)
|
||||
|
||||
## Notes
|
||||
|
||||
- This is a defensive fix that handles missing development directory paths gracefully
|
||||
- The sandbox will still function correctly without `testingReadPath3` and `testingReadPath4`
|
||||
- These paths are only used in non-packaged builds for development/testing purposes
|
||||
- Production/packaged builds won't even reach this code path due to `IsPackagedBuild()` check
|
||||
@@ -0,0 +1,39 @@
|
||||
diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
|
||||
index fbd24a5d00..1ff3195235 100644
|
||||
--- a/dom/ipc/ContentParent.cpp
|
||||
+++ b/dom/ipc/ContentParent.cpp
|
||||
@@ -2324,22 +2324,24 @@ static void CacheSandboxParams(std::vector<std::string>& aCachedParams) {
|
||||
// Repo dir
|
||||
nsCOMPtr<nsIFile> repoDir;
|
||||
rv = nsMacUtilsImpl::GetRepoDir(getter_AddRefs(repoDir));
|
||||
- if (NS_FAILED(rv)) {
|
||||
- MOZ_CRASH("Failed to get path to repo dir");
|
||||
+ if (NS_SUCCEEDED(rv)) {
|
||||
+ nsCString repoDirPath;
|
||||
+ (void)repoDir->GetNativePath(repoDirPath);
|
||||
+ info.testingReadPath3 = repoDirPath.get();
|
||||
+ } else {
|
||||
+ NS_WARNING("Failed to get repo dir path for sandbox, skipping testingReadPath3");
|
||||
}
|
||||
- nsCString repoDirPath;
|
||||
- (void)repoDir->GetNativePath(repoDirPath);
|
||||
- info.testingReadPath3 = repoDirPath.get();
|
||||
|
||||
// Object dir
|
||||
nsCOMPtr<nsIFile> objDir;
|
||||
rv = nsMacUtilsImpl::GetObjDir(getter_AddRefs(objDir));
|
||||
- if (NS_FAILED(rv)) {
|
||||
- MOZ_CRASH("Failed to get path to build object dir");
|
||||
+ if (NS_SUCCEEDED(rv)) {
|
||||
+ nsCString objDirPath;
|
||||
+ (void)objDir->GetNativePath(objDirPath);
|
||||
+ info.testingReadPath4 = objDirPath.get();
|
||||
+ } else {
|
||||
+ NS_WARNING("Failed to get obj dir path for sandbox, skipping testingReadPath4");
|
||||
}
|
||||
- nsCString objDirPath;
|
||||
- (void)objDir->GetNativePath(objDirPath);
|
||||
- info.testingReadPath4 = objDirPath.get();
|
||||
}
|
||||
|
||||
// DEBUG_WRITE_DIR
|
||||
@@ -38,25 +38,27 @@ index d0aebdf965..01472ec205 100644
|
||||
if (aShouldResistFingerprinting && !mSpoofedUserAgent.IsEmpty()) {
|
||||
LOG(("using spoofed userAgent : %s\n", mSpoofedUserAgent.get()));
|
||||
return mSpoofedUserAgent;
|
||||
@@ -1834,6 +1844,10 @@ nsresult nsHttpHandler::SetAcceptLanguages() {
|
||||
@@ -2077,6 +2087,10 @@ nsresult nsHttpHandler::SetAcceptLanguages() {
|
||||
}
|
||||
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
+ if (auto value = MaskConfig::GetString("headers.Accept-Language")) {
|
||||
+ mAcceptLanguages.Assign(nsCString(value.value().c_str()));
|
||||
+ return NS_OK;
|
||||
+ }
|
||||
|
||||
|
||||
mAcceptLanguagesIsDirty = false;
|
||||
|
||||
@@ -1850,6 +1864,10 @@ nsresult nsHttpHandler::SetAcceptLanguages() {
|
||||
|
||||
|
||||
@@ -2098,6 +2112,10 @@ nsresult nsHttpHandler::SetAcceptLanguages() {
|
||||
|
||||
nsresult nsHttpHandler::SetAcceptEncodings(const char* aAcceptEncodings,
|
||||
bool isSecure) {
|
||||
bool isSecure, bool isDictionary) {
|
||||
+ if (auto value = MaskConfig::GetString("headers.Accept-Encoding")) {
|
||||
+ mHttpsAcceptEncodings.Assign(nsCString(value.value().c_str()));
|
||||
+ return NS_OK;
|
||||
+ }
|
||||
if (isSecure) {
|
||||
if (isDictionary) {
|
||||
mDictionaryAcceptEncodings = aAcceptEncodings;
|
||||
} else if (isSecure) {
|
||||
mHttpsAcceptEncodings = aAcceptEncodings;
|
||||
} else {
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
diff --git a/browser/components/urlbar/UrlbarProviderInterventions.sys.mjs b/browser/components/urlbar/UrlbarProviderInterventions.sys.mjs
|
||||
index 988f5831f2..62f64589a4 100644
|
||||
index e5bda9f62c..f8988a4430 100644
|
||||
--- a/browser/components/urlbar/UrlbarProviderInterventions.sys.mjs
|
||||
+++ b/browser/components/urlbar/UrlbarProviderInterventions.sys.mjs
|
||||
@@ -494,7 +494,7 @@ class ProviderInterventions extends UrlbarProvider {
|
||||
* @returns {boolean} Whether this provider should be invoked for the search.
|
||||
@@ -482,7 +482,7 @@ export class UrlbarProviderInterventions extends UrlbarProvider {
|
||||
* @param {UrlbarQueryContext} queryContext The query context object
|
||||
*/
|
||||
isActive(queryContext) {
|
||||
async isActive(queryContext) {
|
||||
- if (
|
||||
+ if (true ||
|
||||
!queryContext.searchString ||
|
||||
queryContext.searchString.length > UrlbarUtils.MAX_TEXT_LENGTH ||
|
||||
lazy.UrlbarTokenizer.REGEXP_LIKE_PROTOCOL.test(
|
||||
lazy.UrlUtils.REGEXP_LIKE_PROTOCOL.test(queryContext.searchString) ||
|
||||
diff --git a/toolkit/components/search/SearchEngineSelector.sys.mjs b/toolkit/components/search/SearchEngineSelector.sys.mjs
|
||||
index 3dab73ee2f..c5c1259217 100644
|
||||
index 85c9d83282..15cfeac054 100644
|
||||
--- a/toolkit/components/search/SearchEngineSelector.sys.mjs
|
||||
+++ b/toolkit/components/search/SearchEngineSelector.sys.mjs
|
||||
@@ -158,6 +158,21 @@ export class SearchEngineSelector {
|
||||
@@ -331,6 +331,21 @@ export class SearchEngineSelector {
|
||||
* could be obtained.
|
||||
*/
|
||||
async _getConfiguration(firstTime = true) {
|
||||
async #getConfiguration(firstTime = true) {
|
||||
+ if (true) {
|
||||
+ return [
|
||||
+ {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
|
||||
index e0cbb3f1f8..d7868e7ae0 100644
|
||||
index 9880047350..318d2edba8 100644
|
||||
--- a/dom/base/Navigator.cpp
|
||||
+++ b/dom/base/Navigator.cpp
|
||||
@@ -2288,28 +2288,7 @@ dom::PrivateAttribution* Navigator::PrivateAttribution() {
|
||||
@@ -2316,28 +2316,7 @@ dom::PrivateAttribution* Navigator::PrivateAttribution() {
|
||||
|
||||
/* static */
|
||||
bool Navigator::Webdriver() {
|
||||
@@ -33,19 +33,17 @@ index e0cbb3f1f8..d7868e7ae0 100644
|
||||
|
||||
AutoplayPolicy Navigator::GetAutoplayPolicy(AutoplayPolicyMediaType aType) {
|
||||
diff --git a/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs b/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
|
||||
index 0eeb5924c4..8b975a8b11 100644
|
||||
index cf5ae51466..97efc14262 100644
|
||||
--- a/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
|
||||
+++ b/toolkit/components/enterprisepolicies/EnterprisePoliciesParent.sys.mjs
|
||||
@@ -108,12 +108,6 @@ EnterprisePoliciesManager.prototype = {
|
||||
@@ -106,9 +106,7 @@ EnterprisePoliciesManager.prototype = {
|
||||
Services.prefs.clearUserPref(PREF_POLICIES_APPLIED);
|
||||
}
|
||||
|
||||
- // Playwright: Disable enterprise policies
|
||||
- if (true) {
|
||||
- this.status = Ci.nsIEnterprisePolicies.INACTIVE;
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
let provider = this._chooseProvider();
|
||||
- // --- Playwright begin ---
|
||||
- let provider = new PlaywrightPoliciesProvider();
|
||||
- // --- Playwright end ---
|
||||
+ let provider = this._chooseProvider();
|
||||
|
||||
if (provider.failed) {
|
||||
this.status = Ci.nsIEnterprisePolicies.FAILED;
|
||||
|
||||
@@ -1,814 +0,0 @@
|
||||
diff --git a/dom/base/WebRTCIPManager.cpp b/dom/base/WebRTCIPManager.cpp
|
||||
new file mode 100644
|
||||
index 0000000000..94575f2631
|
||||
--- /dev/null
|
||||
+++ b/dom/base/WebRTCIPManager.cpp
|
||||
@@ -0,0 +1,218 @@
|
||||
+#include "WebRTCIPManager.h"
|
||||
+#include "nsPrintfCString.h"
|
||||
+#include "nsGlobalWindowInner.h"
|
||||
+#include "xpcpublic.h"
|
||||
+
|
||||
+namespace mozilla {
|
||||
+namespace dom {
|
||||
+
|
||||
+/* static */ nsString
|
||||
+WebRTCIPManager::IPv4KeyForUserContext(uint32_t userContextId) {
|
||||
+ nsString key;
|
||||
+ key.AppendLiteral(u"webrtc_ipv4_");
|
||||
+ key.AppendInt(userContextId);
|
||||
+ return key;
|
||||
+}
|
||||
+
|
||||
+/* static */ nsString
|
||||
+WebRTCIPManager::IPv6KeyForUserContext(uint32_t userContextId) {
|
||||
+ nsString key;
|
||||
+ key.AppendLiteral(u"webrtc_ipv6_");
|
||||
+ key.AppendInt(userContextId);
|
||||
+ return key;
|
||||
+}
|
||||
+
|
||||
+/* static */ nsString
|
||||
+WebRTCIPManager::IPv4DisabledKeyForUserContext(uint32_t userContextId) {
|
||||
+ nsString key;
|
||||
+ key.AppendLiteral(u"webrtc_ipv4_disabled_");
|
||||
+ key.AppendInt(userContextId);
|
||||
+ return key;
|
||||
+}
|
||||
+
|
||||
+/* static */ nsString
|
||||
+WebRTCIPManager::IPv6DisabledKeyForUserContext(uint32_t userContextId) {
|
||||
+ nsString key;
|
||||
+ key.AppendLiteral(u"webrtc_ipv6_disabled_");
|
||||
+ key.AppendInt(userContextId);
|
||||
+ return key;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+
|
||||
+/* static */ void
|
||||
+WebRTCIPManager::SetIPv4(uint32_t userContextId, const nsAString& ipv4) {
|
||||
+ nsString key = IPv4KeyForUserContext(userContextId);
|
||||
+ RoverfoxStorageManager::PutString(key, ipv4);
|
||||
+
|
||||
+ // Mark the function as disabled for this context after first use
|
||||
+ DisableIPv4Function(userContextId);
|
||||
+}
|
||||
+
|
||||
+/* static */ bool
|
||||
+WebRTCIPManager::GetIPv4(uint32_t userContextId, nsAString& outIPv4) {
|
||||
+ nsString key = IPv4KeyForUserContext(userContextId);
|
||||
+ return RoverfoxStorageManager::GetString(key, outIPv4);
|
||||
+}
|
||||
+
|
||||
+/* static */ void
|
||||
+WebRTCIPManager::SetIPv6(uint32_t userContextId, const nsAString& ipv6) {
|
||||
+ nsString key = IPv6KeyForUserContext(userContextId);
|
||||
+ RoverfoxStorageManager::PutString(key, ipv6);
|
||||
+
|
||||
+ // Mark the function as disabled for this context after first use
|
||||
+ DisableIPv6Function(userContextId);
|
||||
+}
|
||||
+
|
||||
+/* static */ bool
|
||||
+WebRTCIPManager::GetIPv6(uint32_t userContextId, nsAString& outIPv6) {
|
||||
+ nsString key = IPv6KeyForUserContext(userContextId);
|
||||
+ return RoverfoxStorageManager::GetString(key, outIPv6);
|
||||
+}
|
||||
+
|
||||
+
|
||||
+
|
||||
+/* static */ bool
|
||||
+WebRTCIPManager::IsIPv4FunctionDisabled(uint32_t userContextId) {
|
||||
+ nsString key = IPv4DisabledKeyForUserContext(userContextId);
|
||||
+ bool disabled = false;
|
||||
+ return RoverfoxStorageManager::GetBool(key, disabled) && disabled;
|
||||
+}
|
||||
+
|
||||
+/* static */ void
|
||||
+WebRTCIPManager::DisableIPv4Function(uint32_t userContextId) {
|
||||
+ nsString key = IPv4DisabledKeyForUserContext(userContextId);
|
||||
+ RoverfoxStorageManager::PutBool(key, true);
|
||||
+}
|
||||
+
|
||||
+/* static */ bool
|
||||
+WebRTCIPManager::IsIPv6FunctionDisabled(uint32_t userContextId) {
|
||||
+ nsString key = IPv6DisabledKeyForUserContext(userContextId);
|
||||
+ bool disabled = false;
|
||||
+ return RoverfoxStorageManager::GetBool(key, disabled) && disabled;
|
||||
+}
|
||||
+
|
||||
+/* static */ void
|
||||
+WebRTCIPManager::DisableIPv6Function(uint32_t userContextId) {
|
||||
+ nsString key = IPv6DisabledKeyForUserContext(userContextId);
|
||||
+ RoverfoxStorageManager::PutBool(key, true);
|
||||
+}
|
||||
+
|
||||
+
|
||||
+
|
||||
+/* static */ bool
|
||||
+WebRTCIPManager::IsIPv4FunctionEnabledForWebIDL(JSContext* aCx, JSObject* aObj) {
|
||||
+ // Get the window from the JS object
|
||||
+ nsGlobalWindowInner* win = xpc::WindowOrNull(aObj);
|
||||
+ if (!win) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ // Get the user context ID for this window
|
||||
+ uint32_t userContextId = 0;
|
||||
+
|
||||
+ // 1) Prefer the document's principal when available (content contexts)
|
||||
+ if (Document* doc = win->GetDoc()) {
|
||||
+ if (nsIPrincipal* p = doc->NodePrincipal()) {
|
||||
+ userContextId = p->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 2) Fallback: this inner window's docshell origin attributes
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* ds = win->GetDocShell()) {
|
||||
+ auto* concrete = static_cast<nsDocShell*>(ds);
|
||||
+ userContextId = concrete->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 3) Fallback: top browsing context's current inner window document/docshell
|
||||
+ if (userContextId == 0) {
|
||||
+ if (BrowsingContext* bc = win->GetBrowsingContext()) {
|
||||
+ RefPtr<BrowsingContext> top = bc->Top();
|
||||
+ if (top) {
|
||||
+ if (nsPIDOMWindowOuter* topOuter = top->GetDOMWindow()) {
|
||||
+ if (nsPIDOMWindowInner* topInner = topOuter->GetCurrentInnerWindow()) {
|
||||
+ if (Document* topDoc = topInner->GetExtantDoc()) {
|
||||
+ if (nsIPrincipal* tp = topDoc->NodePrincipal()) {
|
||||
+ userContextId = tp->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* topDS = topInner->GetDocShell()) {
|
||||
+ auto* concreteTop = static_cast<nsDocShell*>(topDS);
|
||||
+ userContextId = concreteTop->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Check if the function is disabled for this context
|
||||
+ bool disabled = false;
|
||||
+ RoverfoxStorageManager::GetBool(IPv4DisabledKeyForUserContext(userContextId), disabled);
|
||||
+ return !disabled;
|
||||
+}
|
||||
+
|
||||
+/* static */ bool
|
||||
+WebRTCIPManager::IsIPv6FunctionEnabledForWebIDL(JSContext* aCx, JSObject* aObj) {
|
||||
+ // Get the window from the JS object
|
||||
+ nsGlobalWindowInner* win = xpc::WindowOrNull(aObj);
|
||||
+ if (!win) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ // Get the user context ID for this window
|
||||
+ uint32_t userContextId = 0;
|
||||
+
|
||||
+ // 1) Prefer the document's principal when available (content contexts)
|
||||
+ if (Document* doc = win->GetDoc()) {
|
||||
+ if (nsIPrincipal* p = doc->NodePrincipal()) {
|
||||
+ userContextId = p->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 2) Fallback: this inner window's docshell origin attributes
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* ds = win->GetDocShell()) {
|
||||
+ auto* concrete = static_cast<nsDocShell*>(ds);
|
||||
+ userContextId = concrete->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 3) Fallback: top browsing context's current inner window document/docshell
|
||||
+ if (userContextId == 0) {
|
||||
+ if (BrowsingContext* bc = win->GetBrowsingContext()) {
|
||||
+ RefPtr<BrowsingContext> top = bc->Top();
|
||||
+ if (top) {
|
||||
+ if (nsPIDOMWindowOuter* topOuter = top->GetDOMWindow()) {
|
||||
+ if (nsPIDOMWindowInner* topInner = topOuter->GetCurrentInnerWindow()) {
|
||||
+ if (Document* topDoc = topInner->GetExtantDoc()) {
|
||||
+ if (nsIPrincipal* tp = topDoc->NodePrincipal()) {
|
||||
+ userContextId = tp->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* topDS = topInner->GetDocShell()) {
|
||||
+ auto* concreteTop = static_cast<nsDocShell*>(topDS);
|
||||
+ userContextId = concreteTop->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Check if the function is disabled for this context
|
||||
+ bool disabled = false;
|
||||
+ RoverfoxStorageManager::GetBool(IPv6DisabledKeyForUserContext(userContextId), disabled);
|
||||
+ return !disabled;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+
|
||||
+} // namespace dom
|
||||
+} // namespace mozilla
|
||||
diff --git a/dom/base/WebRTCIPManager.h b/dom/base/WebRTCIPManager.h
|
||||
new file mode 100644
|
||||
index 0000000000..c20cb032f6
|
||||
--- /dev/null
|
||||
+++ b/dom/base/WebRTCIPManager.h
|
||||
@@ -0,0 +1,105 @@
|
||||
+#ifndef mozilla_dom_WebRTCIPManager_h
|
||||
+#define mozilla_dom_WebRTCIPManager_h
|
||||
+
|
||||
+#include "nsString.h"
|
||||
+#include "RoverfoxStorageManager.h"
|
||||
+
|
||||
+namespace mozilla {
|
||||
+namespace dom {
|
||||
+
|
||||
+/**
|
||||
+ * WebRTCIPManager manages WebRTC IP addresses per user context.
|
||||
+ * This enables privacy-preserving WebRTC IP spoofing by allowing deterministic
|
||||
+ * IP address overrides that are isolated by user context.
|
||||
+ */
|
||||
+class WebRTCIPManager {
|
||||
+public:
|
||||
+ /**
|
||||
+ * Set the WebRTC IPv4 address for a given user context.
|
||||
+ * @param userContextId The user context ID (0 for default context)
|
||||
+ * @param ipv4 The IPv4 address to use for WebRTC connections
|
||||
+ */
|
||||
+ static void SetIPv4(uint32_t userContextId, const nsAString& ipv4);
|
||||
+
|
||||
+ /**
|
||||
+ * Get the WebRTC IPv4 address for a given user context.
|
||||
+ * @param userContextId The user context ID
|
||||
+ * @param outIPv4 The IPv4 address, empty if no address has been set
|
||||
+ * @return true if an IPv4 address has been set, false otherwise
|
||||
+ */
|
||||
+ static bool GetIPv4(uint32_t userContextId, nsAString& outIPv4);
|
||||
+
|
||||
+ /**
|
||||
+ * Set the WebRTC IPv6 address for a given user context.
|
||||
+ * @param userContextId The user context ID (0 for default context)
|
||||
+ * @param ipv6 The IPv6 address to use for WebRTC connections
|
||||
+ */
|
||||
+ static void SetIPv6(uint32_t userContextId, const nsAString& ipv6);
|
||||
+
|
||||
+ /**
|
||||
+ * Get the WebRTC IPv6 address for a given user context.
|
||||
+ * @param userContextId The user context ID
|
||||
+ * @param outIPv6 The IPv6 address, empty if no address has been set
|
||||
+ * @return true if an IPv6 address has been set, false otherwise
|
||||
+ */
|
||||
+ static bool GetIPv6(uint32_t userContextId, nsAString& outIPv6);
|
||||
+
|
||||
+
|
||||
+
|
||||
+ /**
|
||||
+ * Check if the setWebRTCIPv4 function has been used and should be disabled for a context.
|
||||
+ * @param userContextId The user context ID
|
||||
+ * @return true if the function has been used for this context and should not appear on new windows
|
||||
+ */
|
||||
+ static bool IsIPv4FunctionDisabled(uint32_t userContextId);
|
||||
+
|
||||
+ /**
|
||||
+ * Mark the setWebRTCIPv4 function as used/disabled for a context.
|
||||
+ * @param userContextId The user context ID
|
||||
+ */
|
||||
+ static void DisableIPv4Function(uint32_t userContextId);
|
||||
+
|
||||
+ /**
|
||||
+ * Check if the setWebRTCIPv6 function has been used and should be disabled for a context.
|
||||
+ * @param userContextId The user context ID
|
||||
+ * @return true if the function has been used for this context and should not appear on new windows
|
||||
+ */
|
||||
+ static bool IsIPv6FunctionDisabled(uint32_t userContextId);
|
||||
+
|
||||
+ /**
|
||||
+ * Mark the setWebRTCIPv6 function as used/disabled for a context.
|
||||
+ * @param userContextId The user context ID
|
||||
+ */
|
||||
+ static void DisableIPv6Function(uint32_t userContextId);
|
||||
+
|
||||
+
|
||||
+
|
||||
+ /**
|
||||
+ * WebIDL-compatible function to check if setWebRTCIPv4 should be enabled.
|
||||
+ * @param aCx The JavaScript context
|
||||
+ * @param aObj The JavaScript object (window)
|
||||
+ * @return true if the function should be enabled, false otherwise
|
||||
+ */
|
||||
+ static bool IsIPv4FunctionEnabledForWebIDL(JSContext* aCx, JSObject* aObj);
|
||||
+
|
||||
+ /**
|
||||
+ * WebIDL-compatible function to check if setWebRTCIPv6 should be enabled.
|
||||
+ * @param aCx The JavaScript context
|
||||
+ * @param aObj The JavaScript object (window)
|
||||
+ * @return true if the function should be enabled, false otherwise
|
||||
+ */
|
||||
+ static bool IsIPv6FunctionEnabledForWebIDL(JSContext* aCx, JSObject* aObj);
|
||||
+
|
||||
+
|
||||
+
|
||||
+private:
|
||||
+ static nsString IPv4KeyForUserContext(uint32_t userContextId);
|
||||
+ static nsString IPv6KeyForUserContext(uint32_t userContextId);
|
||||
+ static nsString IPv4DisabledKeyForUserContext(uint32_t userContextId);
|
||||
+ static nsString IPv6DisabledKeyForUserContext(uint32_t userContextId);
|
||||
+};
|
||||
+
|
||||
+} // namespace dom
|
||||
+} // namespace mozilla
|
||||
+
|
||||
+#endif // mozilla_dom_WebRTCIPManager_h
|
||||
diff --git a/dom/base/moz.build b/dom/base/moz.build
|
||||
index 59917269e5..66788dce3e 100644
|
||||
--- a/dom/base/moz.build
|
||||
+++ b/dom/base/moz.build
|
||||
@@ -1,3 +1,8 @@
|
||||
+# Link system SQLite for RoverfoxStorageManager embedded usage
|
||||
+OS_LIBS += [
|
||||
+ 'sqlite3',
|
||||
+]
|
||||
+
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
@@ -264,6 +269,7 @@ EXPORTS.mozilla.dom += [
|
||||
"RequestCallbackManager.h",
|
||||
"ResizeObserver.h",
|
||||
"ResponsiveImageSelector.h",
|
||||
+ "RoverfoxStorageManager.h",
|
||||
"SameProcessMessageQueue.h",
|
||||
"ScreenLuminance.h",
|
||||
"ScreenOrientation.h",
|
||||
@@ -296,6 +302,7 @@ EXPORTS.mozilla.dom += [
|
||||
"VideoFrameProvider.h",
|
||||
"ViewportMetaData.h",
|
||||
"VisualViewport.h",
|
||||
+ "WebRTCIPManager.h",
|
||||
"WindowFeatures.h",
|
||||
"WindowProxyHolder.h",
|
||||
]
|
||||
@@ -468,6 +475,7 @@ UNIFIED_SOURCES += [
|
||||
"RemoteOuterWindowProxy.cpp",
|
||||
"ResizeObserver.cpp",
|
||||
"ResponsiveImageSelector.cpp",
|
||||
+ "RoverfoxStorageManager.cpp",
|
||||
"SameProcessMessageQueue.cpp",
|
||||
"ScreenLuminance.cpp",
|
||||
"ScreenOrientation.cpp",
|
||||
@@ -502,6 +510,7 @@ UNIFIED_SOURCES += [
|
||||
"UserActivation.cpp",
|
||||
"ViewportMetaData.cpp",
|
||||
"VisualViewport.cpp",
|
||||
+ "WebRTCIPManager.cpp",
|
||||
"WindowDestroyedEvent.cpp",
|
||||
"WindowFeatures.cpp",
|
||||
"WindowNamedPropertiesHandler.cpp",
|
||||
diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp
|
||||
index 605f8bbb9f..83755fd8d5 100644
|
||||
--- a/dom/base/nsGlobalWindowInner.cpp
|
||||
+++ b/dom/base/nsGlobalWindowInner.cpp
|
||||
@@ -325,6 +325,7 @@
|
||||
#include "xpcpublic.h"
|
||||
|
||||
#include "FontSpacingSeedManager.h"
|
||||
+#include "WebRTCIPManager.h"
|
||||
#include "nsDocShell.h"
|
||||
#include "mozilla/OriginAttributes.h"
|
||||
#include "nsIDOMXULControlElement.h"
|
||||
@@ -7531,6 +7532,126 @@ void nsGlobalWindowInner::SetFontSpacingSeed(uint32_t seed, ErrorResult& aRv) {
|
||||
}
|
||||
}
|
||||
|
||||
+void nsGlobalWindowInner::SetWebRTCIPv4(const nsAString& ipv4, ErrorResult& aRv) {
|
||||
+ uint32_t userContextId = 0;
|
||||
+
|
||||
+ // 1) Prefer the document's principal when available (content contexts)
|
||||
+ if (Document* doc = GetDoc()) {
|
||||
+ if (nsIPrincipal* p = doc->NodePrincipal()) {
|
||||
+ userContextId = p->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 2) Fallback: this inner window's docshell origin attributes
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* ds = GetDocShell()) {
|
||||
+ auto* concrete = static_cast<nsDocShell*>(ds);
|
||||
+ userContextId = concrete->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 3) Fallback: top browsing context's current inner window document/docshell
|
||||
+ if (userContextId == 0) {
|
||||
+ if (BrowsingContext* bc = GetBrowsingContext()) {
|
||||
+ RefPtr<BrowsingContext> top = bc->Top();
|
||||
+ if (top) {
|
||||
+ if (nsPIDOMWindowOuter* topOuter = top->GetDOMWindow()) {
|
||||
+ if (nsPIDOMWindowInner* topInner = topOuter->GetCurrentInnerWindow()) {
|
||||
+ if (Document* topDoc = topInner->GetExtantDoc()) {
|
||||
+ if (nsIPrincipal* tp = topDoc->NodePrincipal()) {
|
||||
+ userContextId = tp->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* topDS = topInner->GetDocShell()) {
|
||||
+ auto* concreteTop = static_cast<nsDocShell*>(topDS);
|
||||
+ userContextId =
|
||||
+ concreteTop->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (userContextId == 0) {
|
||||
+ aRv.ThrowSecurityError("Unable to resolve user context ID for this context");
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ WebRTCIPManager::SetIPv4(userContextId, ipv4);
|
||||
+
|
||||
+ // Self-destruct: remove this function from the window object after first use
|
||||
+ if (JSContext* cx = nsContentUtils::GetCurrentJSContext()) {
|
||||
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
+ if (global) {
|
||||
+ JS_DeleteProperty(cx, global, "setWebRTCIPv4");
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void nsGlobalWindowInner::SetWebRTCIPv6(const nsAString& ipv6, ErrorResult& aRv) {
|
||||
+ uint32_t userContextId = 0;
|
||||
+
|
||||
+ // 1) Prefer the document's principal when available (content contexts)
|
||||
+ if (Document* doc = GetDoc()) {
|
||||
+ if (nsIPrincipal* p = doc->NodePrincipal()) {
|
||||
+ userContextId = p->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 2) Fallback: this inner window's docshell origin attributes
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* ds = GetDocShell()) {
|
||||
+ auto* concrete = static_cast<nsDocShell*>(ds);
|
||||
+ userContextId = concrete->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 3) Fallback: top browsing context's current inner window document/docshell
|
||||
+ if (userContextId == 0) {
|
||||
+ if (BrowsingContext* bc = GetBrowsingContext()) {
|
||||
+ RefPtr<BrowsingContext> top = bc->Top();
|
||||
+ if (top) {
|
||||
+ if (nsPIDOMWindowOuter* topOuter = top->GetDOMWindow()) {
|
||||
+ if (nsPIDOMWindowInner* topInner = topOuter->GetCurrentInnerWindow()) {
|
||||
+ if (Document* topDoc = topInner->GetExtantDoc()) {
|
||||
+ if (nsIPrincipal* tp = topDoc->NodePrincipal()) {
|
||||
+ userContextId = tp->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* topDS = topInner->GetDocShell()) {
|
||||
+ auto* concreteTop = static_cast<nsDocShell*>(topDS);
|
||||
+ userContextId =
|
||||
+ concreteTop->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (userContextId == 0) {
|
||||
+ aRv.ThrowSecurityError("Unable to resolve user context ID for this context");
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ WebRTCIPManager::SetIPv6(userContextId, ipv6);
|
||||
+
|
||||
+ // Self-destruct: remove this function from the window object after first use
|
||||
+ if (JSContext* cx = nsContentUtils::GetCurrentJSContext()) {
|
||||
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
+ if (global) {
|
||||
+ JS_DeleteProperty(cx, global, "setWebRTCIPv6");
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+
|
||||
+
|
||||
void nsGlobalWindowInner::StoreSharedWorker(SharedWorker* aSharedWorker) {
|
||||
MOZ_ASSERT(aSharedWorker);
|
||||
MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
|
||||
diff --git a/dom/base/nsGlobalWindowInner.h b/dom/base/nsGlobalWindowInner.h
|
||||
index e2daf33843..cfcff69332 100644
|
||||
--- a/dom/base/nsGlobalWindowInner.h
|
||||
+++ b/dom/base/nsGlobalWindowInner.h
|
||||
@@ -685,6 +685,10 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
|
||||
// Font spacing seed for privacy-preserving font fingerprinting
|
||||
void SetFontSpacingSeed(uint32_t seed, mozilla::ErrorResult& aRv);
|
||||
|
||||
+ // WebRTC IP addresses for privacy-preserving IP spoofing
|
||||
+ void SetWebRTCIPv4(const nsAString& ipv4, mozilla::ErrorResult& aRv);
|
||||
+ void SetWebRTCIPv6(const nsAString& ipv6, mozilla::ErrorResult& aRv);
|
||||
+
|
||||
void StoreSharedWorker(mozilla::dom::SharedWorker* aSharedWorker);
|
||||
|
||||
void ForgetSharedWorker(mozilla::dom::SharedWorker* aSharedWorker);
|
||||
diff --git a/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp b/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp
|
||||
index d0102a6746..ed524583bd 100644
|
||||
--- a/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp
|
||||
+++ b/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp
|
||||
@@ -118,6 +118,8 @@
|
||||
#include "mozilla/net/WebrtcProxyConfig.h"
|
||||
#include "MaskConfig.hpp"
|
||||
#include "mozilla/RustRegex.h"
|
||||
+#include "WebRTCIPManager.h"
|
||||
+#include "nsDocShell.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
// We need to undef the MS macro again in case the windows include file
|
||||
@@ -3329,11 +3331,56 @@ void PeerConnectionImpl::SendLocalIceCandidateToContent(
|
||||
}
|
||||
|
||||
bool PeerConnectionImpl::ShouldSpoofCandidateIP() const {
|
||||
- // Checks if either webrtc:ipv4 or webrtc:ipv6 is set in the config
|
||||
- return MaskConfig::GetString("webrtc:ipv4").has_value() ||
|
||||
- MaskConfig::GetString("webrtc:ipv6").has_value() ||
|
||||
- MaskConfig::GetString("webrtc:localipv4").has_value() ||
|
||||
- MaskConfig::GetString("webrtc:localipv6").has_value();
|
||||
+ if (!mWindow) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ // Get the user context ID for this window using the same logic as WebRTCIPManager
|
||||
+ uint32_t userContextId = 0;
|
||||
+
|
||||
+ // 1) Prefer the document's principal when available (content contexts)
|
||||
+ if (Document* doc = mWindow->GetDoc()) {
|
||||
+ if (nsIPrincipal* p = doc->NodePrincipal()) {
|
||||
+ userContextId = p->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 2) Fallback: this inner window's docshell origin attributes
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* ds = mWindow->GetDocShell()) {
|
||||
+ auto* concrete = static_cast<nsDocShell*>(ds);
|
||||
+ userContextId = concrete->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 3) Fallback: top browsing context's current inner window document/docshell
|
||||
+ if (userContextId == 0) {
|
||||
+ if (BrowsingContext* bc = mWindow->GetBrowsingContext()) {
|
||||
+ RefPtr<BrowsingContext> top = bc->Top();
|
||||
+ if (top) {
|
||||
+ if (nsPIDOMWindowOuter* topOuter = top->GetDOMWindow()) {
|
||||
+ if (nsPIDOMWindowInner* topInner = topOuter->GetCurrentInnerWindow()) {
|
||||
+ if (Document* topDoc = topInner->GetExtantDoc()) {
|
||||
+ if (nsIPrincipal* tp = topDoc->NodePrincipal()) {
|
||||
+ userContextId = tp->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* topDS = topInner->GetDocShell()) {
|
||||
+ auto* concreteTop = static_cast<nsDocShell*>(topDS);
|
||||
+ userContextId = concreteTop->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Check if either IPv4 or IPv6 addresses have been set for this user context
|
||||
+ nsString ipv4, ipv6;
|
||||
+ return WebRTCIPManager::GetIPv4(userContextId, ipv4) ||
|
||||
+ WebRTCIPManager::GetIPv6(userContextId, ipv6);
|
||||
}
|
||||
|
||||
// *****
|
||||
@@ -3369,29 +3416,25 @@ bool isPrivateIP(const std::string& ip) {
|
||||
}
|
||||
}
|
||||
|
||||
-std::string getMaskForIP(const std::string& ip) {
|
||||
+std::string getMaskForIP(const std::string& ip, uint32_t userContextId) {
|
||||
// Get the corresponding mask for an ip address
|
||||
if (isSpecialIP(ip)) {
|
||||
return ip; // dont mask special IPs
|
||||
}
|
||||
bool isIPv6 = (ip.find(':') != std::string::npos);
|
||||
- bool isPrivate = isPrivateIP(ip);
|
||||
- auto ipv4Value = MaskConfig::GetString("webrtc:ipv4");
|
||||
- auto ipv6Value = MaskConfig::GetString("webrtc:ipv6");
|
||||
- auto localIpv4Value = MaskConfig::GetString("webrtc:localipv4");
|
||||
- auto localIpv6Value = MaskConfig::GetString("webrtc:localipv6");
|
||||
+
|
||||
+ // Get IP addresses from WebRTCIPManager for this user context
|
||||
+ nsString ipv4Value, ipv6Value;
|
||||
+ bool hasIPv4 = WebRTCIPManager::GetIPv4(userContextId, ipv4Value);
|
||||
+ bool hasIPv6 = WebRTCIPManager::GetIPv6(userContextId, ipv6Value);
|
||||
|
||||
if (isIPv6) {
|
||||
- if (isPrivate && localIpv6Value) {
|
||||
- return localIpv6Value.value();
|
||||
- } else if (ipv6Value) {
|
||||
- return ipv6Value.value();
|
||||
+ if (hasIPv6) {
|
||||
+ return NS_ConvertUTF16toUTF8(ipv6Value).get();
|
||||
}
|
||||
} else {
|
||||
- if (isPrivate && localIpv4Value) {
|
||||
- return localIpv4Value.value();
|
||||
- } else if (ipv4Value) {
|
||||
- return ipv4Value.value();
|
||||
+ if (hasIPv4) {
|
||||
+ return NS_ConvertUTF16toUTF8(ipv4Value).get();
|
||||
}
|
||||
}
|
||||
// return original ip if no mask is available
|
||||
@@ -3399,7 +3442,7 @@ std::string getMaskForIP(const std::string& ip) {
|
||||
}
|
||||
|
||||
std::string replaceIPAddresses(
|
||||
- const std::string& input, const char* pattern) {
|
||||
+ const std::string& input, const char* pattern, uint32_t userContextId) {
|
||||
// Replace IP addresses in a output line
|
||||
mozilla::RustRegex regex(pattern);
|
||||
if (!regex.IsValid()) {
|
||||
@@ -3412,7 +3455,7 @@ std::string replaceIPAddresses(
|
||||
|
||||
while (auto match = iter.Next()) {
|
||||
std::string ip = input.substr(match->start, match->end - match->start);
|
||||
- std::string mask = getMaskForIP(ip);
|
||||
+ std::string mask = getMaskForIP(ip, userContextId);
|
||||
|
||||
if (mask != ip) {
|
||||
result.append(input, lastEnd, match->start - lastEnd);
|
||||
@@ -3433,27 +3476,108 @@ std::string PeerConnectionImpl::SpoofCandidateIP(const std::string& candidate) {
|
||||
if (!ShouldSpoofCandidateIP() || candidate.empty()) {
|
||||
return candidate;
|
||||
}
|
||||
+
|
||||
+ // Get the user context ID for this PeerConnection
|
||||
+ uint32_t userContextId = 0;
|
||||
+ if (mWindow) {
|
||||
+ // 1) Prefer the document's principal when available (content contexts)
|
||||
+ if (Document* doc = mWindow->GetDoc()) {
|
||||
+ if (nsIPrincipal* p = doc->NodePrincipal()) {
|
||||
+ userContextId = p->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 2) Fallback: this inner window's docshell origin attributes
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* ds = mWindow->GetDocShell()) {
|
||||
+ auto* concrete = static_cast<nsDocShell*>(ds);
|
||||
+ userContextId = concrete->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 3) Fallback: top browsing context's current inner window document/docshell
|
||||
+ if (userContextId == 0) {
|
||||
+ if (BrowsingContext* bc = mWindow->GetBrowsingContext()) {
|
||||
+ RefPtr<BrowsingContext> top = bc->Top();
|
||||
+ if (top) {
|
||||
+ if (nsPIDOMWindowOuter* topOuter = top->GetDOMWindow()) {
|
||||
+ if (nsPIDOMWindowInner* topInner = topOuter->GetCurrentInnerWindow()) {
|
||||
+ if (Document* topDoc = topInner->GetExtantDoc()) {
|
||||
+ if (nsIPrincipal* tp = topDoc->NodePrincipal()) {
|
||||
+ userContextId = tp->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* topDS = topInner->GetDocShell()) {
|
||||
+ auto* concreteTop = static_cast<nsDocShell*>(topDS);
|
||||
+ userContextId = concreteTop->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
// ipv4
|
||||
const char* ipv4Pattern = "(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)";
|
||||
- std::string result = replaceIPAddresses(candidate, ipv4Pattern);
|
||||
+ std::string result = replaceIPAddresses(candidate, ipv4Pattern, userContextId);
|
||||
// ipv6
|
||||
const char* ipv6Pattern = "(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}";
|
||||
- result = replaceIPAddresses(result, ipv6Pattern);
|
||||
+ result = replaceIPAddresses(result, ipv6Pattern, userContextId);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
nsresult PeerConnectionImpl::SanitizeSDPForIPLeak(std::string& sdp) {
|
||||
// Sanitize sdp
|
||||
- auto ipv4Value = MaskConfig::GetString("webrtc:ipv4");
|
||||
- auto ipv6Value = MaskConfig::GetString("webrtc:ipv6");
|
||||
- auto localIpv4Value = MaskConfig::GetString("webrtc:localipv4");
|
||||
- auto localIpv6Value = MaskConfig::GetString("webrtc:localipv6");
|
||||
-
|
||||
- if (!ipv4Value && !ipv6Value && !localIpv4Value && !localIpv6Value) {
|
||||
+ if (!ShouldSpoofCandidateIP()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
+ // Resolve user context ID using the same logic as other functions
|
||||
+ uint32_t userContextId = 0;
|
||||
+
|
||||
+ // 1) Prefer the document's principal when available (content contexts)
|
||||
+ if (Document* doc = mWindow->GetDoc()) {
|
||||
+ if (nsIPrincipal* p = doc->NodePrincipal()) {
|
||||
+ userContextId = p->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 2) Fallback: this inner window's docshell origin attributes
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* ds = mWindow->GetDocShell()) {
|
||||
+ auto* concrete = static_cast<nsDocShell*>(ds);
|
||||
+ userContextId = concrete->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 3) Fallback: top browsing context's current inner window document/docshell
|
||||
+ if (userContextId == 0) {
|
||||
+ if (BrowsingContext* bc = mWindow->GetBrowsingContext()) {
|
||||
+ RefPtr<BrowsingContext> top = bc->Top();
|
||||
+ if (top) {
|
||||
+ if (nsPIDOMWindowOuter* topOuter = top->GetDOMWindow()) {
|
||||
+ if (nsPIDOMWindowInner* topInner = topOuter->GetCurrentInnerWindow()) {
|
||||
+ if (Document* topDoc = topInner->GetExtantDoc()) {
|
||||
+ if (nsIPrincipal* tp = topDoc->NodePrincipal()) {
|
||||
+ userContextId = tp->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* topDS = topInner->GetDocShell()) {
|
||||
+ auto* concreteTop = static_cast<nsDocShell*>(topDS);
|
||||
+ userContextId = concreteTop->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
// process the SDP line by line to handle candidate lines
|
||||
std::istringstream iss(sdp);
|
||||
std::ostringstream oss;
|
||||
@@ -3480,10 +3604,10 @@ nsresult PeerConnectionImpl::SanitizeSDPForIPLeak(std::string& sdp) {
|
||||
|
||||
// ipv4
|
||||
const char* ipv4Pattern = "(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)";
|
||||
- processedSdp = replaceIPAddresses(processedSdp, ipv4Pattern);
|
||||
+ processedSdp = replaceIPAddresses(processedSdp, ipv4Pattern, userContextId);
|
||||
// ipv6
|
||||
const char* ipv6Pattern = "(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}";
|
||||
- processedSdp = replaceIPAddresses(processedSdp, ipv6Pattern);
|
||||
+ processedSdp = replaceIPAddresses(processedSdp, ipv6Pattern, userContextId);
|
||||
|
||||
if (processedSdp != originalSdp) {
|
||||
sdp = processedSdp;
|
||||
diff --git a/dom/webidl/Window.webidl b/dom/webidl/Window.webidl
|
||||
index 2161066be1..018dcc18e0 100644
|
||||
--- a/dom/webidl/Window.webidl
|
||||
+++ b/dom/webidl/Window.webidl
|
||||
@@ -800,6 +800,12 @@ partial interface Window {
|
||||
undefined setFontSpacingSeed(unsigned long seed);
|
||||
};
|
||||
|
||||
+// WebRTC IP interfaces for privacy-preserving IP spoofing
|
||||
+partial interface Window {
|
||||
+ [Throws, Func="mozilla::dom::WebRTCIPManager::IsIPv4FunctionEnabledForWebIDL"]
|
||||
+ undefined setWebRTCIPv4(DOMString ipv4);
|
||||
+};
|
||||
+
|
||||
// Used to assign marks to appear on the scrollbar when
|
||||
// finding on a page.
|
||||
partial interface Window {
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/intl/components/moz.build b/intl/components/moz.build
|
||||
index b0af60b999..28c221136e 100644
|
||||
index 8e854bba70..72213b13cf 100644
|
||||
--- a/intl/components/moz.build
|
||||
+++ b/intl/components/moz.build
|
||||
@@ -89,3 +89,6 @@ if CONFIG["MOZ_ICU4X"]:
|
||||
@@ -85,3 +85,6 @@ if not CONFIG["MOZ_SYSTEM_ICU"]:
|
||||
DEFINES["MOZ_HAS_MOZGLUE"] = True
|
||||
|
||||
Library("intlcomponents")
|
||||
@@ -11,7 +11,7 @@ index b0af60b999..28c221136e 100644
|
||||
+LOCAL_INCLUDES += ["/camoucfg"]
|
||||
\ No newline at end of file
|
||||
diff --git a/intl/components/src/TimeZone.cpp b/intl/components/src/TimeZone.cpp
|
||||
index 5b09dfdcc5..98a78b33cc 100644
|
||||
index fb4c0657ba..1b18d87681 100644
|
||||
--- a/intl/components/src/TimeZone.cpp
|
||||
+++ b/intl/components/src/TimeZone.cpp
|
||||
@@ -8,7 +8,8 @@
|
||||
@@ -24,9 +24,9 @@ index 5b09dfdcc5..98a78b33cc 100644
|
||||
#include "unicode/uenum.h"
|
||||
#if MOZ_INTL_USE_ICU_CPP_TIMEZONE
|
||||
# include "unicode/basictz.h"
|
||||
@@ -22,7 +23,21 @@ Result<UniquePtr<TimeZone>, ICUError> TimeZone::TryCreate(
|
||||
Maybe<Span<const char16_t>> aTimeZoneOverride) {
|
||||
const UChar* zoneID = nullptr;
|
||||
@@ -21,7 +22,21 @@ Result<UniquePtr<TimeZone>, ICUError> TimeZone::TryCreate(
|
||||
Maybe<Span<const char>> aTimeZoneOverride) {
|
||||
const char* zoneID = nullptr;
|
||||
int32_t zoneIDLen = 0;
|
||||
- if (aTimeZoneOverride) {
|
||||
+
|
||||
@@ -42,7 +42,7 @@ index 5b09dfdcc5..98a78b33cc 100644
|
||||
+ }
|
||||
+
|
||||
+ zoneIDLen = uniStr.length();
|
||||
+ zoneID = uniStr.getBuffer();
|
||||
+ zoneID = reinterpret_cast<const char*>(uniStr.getBuffer());
|
||||
+ } else if (aTimeZoneOverride) {
|
||||
zoneIDLen = static_cast<int32_t>(aTimeZoneOverride->Length());
|
||||
zoneID = aTimeZoneOverride->Elements();
|
||||
|
||||
@@ -2,27 +2,28 @@ diff --git a/dom/media/webspeech/synth/moz.build b/dom/media/webspeech/synth/moz
|
||||
index 2cf19982b2..dcdcdb5cbf 100644
|
||||
--- a/dom/media/webspeech/synth/moz.build
|
||||
+++ b/dom/media/webspeech/synth/moz.build
|
||||
@@ -63,3 +63,6 @@ FINAL_LIBRARY = "xul"
|
||||
@@ -61,3 +61,6 @@ FINAL_LIBRARY = "xul"
|
||||
LOCAL_INCLUDES += [
|
||||
"ipc",
|
||||
]
|
||||
+
|
||||
+# DOM Mask
|
||||
+LOCAL_INCLUDES += ['/camoucfg']
|
||||
\ No newline at end of file
|
||||
|
||||
diff --git a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
|
||||
index e5a1353d6b..4a4a5b080b 100644
|
||||
--- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
|
||||
+++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
|
||||
@@ -27,6 +27,7 @@
|
||||
@@ -26,7 +26,8 @@
|
||||
#include "nsSpeechTask.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include "SpeechSynthesisChild.h"
|
||||
#include "SpeechSynthesisParent.h"
|
||||
+#include "MaskConfig.hpp"
|
||||
|
||||
using mozilla::intl::LocaleService;
|
||||
|
||||
@@ -169,6 +170,20 @@ nsSynthVoiceRegistry* nsSynthVoiceRegistry::GetInstance() {
|
||||
namespace mozilla::dom {
|
||||
|
||||
@@ -169,6 +171,20 @@
|
||||
// Start up all speech synth services.
|
||||
NS_CreateServicesFromCategory(NS_SPEECH_SYNTH_STARTED, nullptr,
|
||||
NS_SPEECH_SYNTH_STARTED);
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
diff --git a/dom/canvas/moz.build b/dom/canvas/moz.build
|
||||
index xxxxxxxxxx..yyyyyyyyyy 100644
|
||||
--- a/dom/canvas/moz.build
|
||||
+++ b/dom/canvas/moz.build
|
||||
@@ -188,6 +188,8 @@ SOURCES += [
|
||||
# Suppress warnings from third-party code.
|
||||
SOURCES["MurmurHash3.cpp"].flags += ["-Wno-implicit-fallthrough"]
|
||||
|
||||
+LOCAL_INCLUDES += ["/camoucfg"]
|
||||
+
|
||||
LOCAL_INCLUDES += [
|
||||
"/js/xpconnect/wrappers",
|
||||
]
|
||||
diff --git a/dom/canvas/ClientWebGLContext.h b/dom/canvas/ClientWebGLContext.h
|
||||
index xxxxxxxxxx..yyyyyyyyyy 100644
|
||||
--- a/dom/canvas/ClientWebGLContext.h
|
||||
+++ b/dom/canvas/ClientWebGLContext.h
|
||||
@@ -1083,6 +1083,9 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal,
|
||||
void GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval);
|
||||
|
||||
private:
|
||||
+ // Helper for mask config values
|
||||
+ bool MBoolVal(const std::string& key, bool defaultValue);
|
||||
+
|
||||
webgl::SwapChainOptions PrepareAsyncSwapChainOptions(
|
||||
WebGLFramebufferJS* fb, bool webvr,
|
||||
const webgl::SwapChainOptions& options = webgl::SwapChainOptions());
|
||||
diff --git a/dom/canvas/ClientWebGLContext.cpp b/dom/canvas/ClientWebGLContext.cpp
|
||||
index a1e5c4792d..be99491bf8 100644
|
||||
index 11578f425f..6c77acd7f3 100644
|
||||
--- a/dom/canvas/ClientWebGLContext.cpp
|
||||
+++ b/dom/canvas/ClientWebGLContext.cpp
|
||||
@@ -4,6 +4,9 @@
|
||||
@@ -10,9 +37,9 @@ index a1e5c4792d..be99491bf8 100644
|
||||
+#include <algorithm>
|
||||
+#include <cstddef>
|
||||
|
||||
#include <bitset>
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -750,6 +753,13 @@ void ClientWebGLContext::SetUnpackColorSpace(
|
||||
@@ -754,6 +757,13 @@ void ClientWebGLContext::SetUnpackColorSpace(
|
||||
Run<RPROC(SetUnpackColorSpace)>(*mUnpackColorSpace);
|
||||
}
|
||||
|
||||
@@ -26,7 +53,7 @@ index a1e5c4792d..be99491bf8 100644
|
||||
void ClientWebGLContext::GetContextAttributes(
|
||||
dom::Nullable<dom::WebGLContextAttributes>& retval) {
|
||||
retval.SetNull();
|
||||
@@ -760,15 +770,40 @@ void ClientWebGLContext::GetContextAttributes(
|
||||
@@ -764,15 +774,40 @@ void ClientWebGLContext::GetContextAttributes(
|
||||
|
||||
const auto& options = mNotLost->info.options;
|
||||
|
||||
@@ -76,7 +103,7 @@ index a1e5c4792d..be99491bf8 100644
|
||||
}
|
||||
|
||||
// -----------------------
|
||||
@@ -986,18 +1021,28 @@ bool ClientWebGLContext::CreateHostContext(const uvec2& requestedSize) {
|
||||
@@ -991,18 +1026,28 @@ bool ClientWebGLContext::CreateHostContext(const uvec2& requestedSize) {
|
||||
std::unordered_map<GLenum, bool> webgl::MakeIsEnabledMap(const bool webgl2) {
|
||||
auto ret = std::unordered_map<GLenum, bool>{};
|
||||
|
||||
@@ -115,7 +142,7 @@ index a1e5c4792d..be99491bf8 100644
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -2066,6 +2111,57 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname,
|
||||
@@ -2114,6 +2159,57 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname,
|
||||
const auto& state = State();
|
||||
|
||||
// -
|
||||
@@ -173,7 +200,7 @@ index a1e5c4792d..be99491bf8 100644
|
||||
|
||||
const auto fnSetRetval_Buffer = [&](const GLenum target) {
|
||||
const auto buffer = *MaybeFind(state.mBoundBufferByTarget, target);
|
||||
@@ -2171,49 +2267,84 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname,
|
||||
@@ -2219,49 +2315,84 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname,
|
||||
|
||||
// 2 floats
|
||||
case LOCAL_GL_DEPTH_RANGE:
|
||||
@@ -271,7 +298,7 @@ index a1e5c4792d..be99491bf8 100644
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2393,6 +2524,10 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname,
|
||||
@@ -2441,6 +2572,10 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname,
|
||||
|
||||
switch (pname) {
|
||||
case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_RENDERER_WEBGL:
|
||||
@@ -279,10 +306,10 @@ index a1e5c4792d..be99491bf8 100644
|
||||
+ ret = Some(value.value());
|
||||
+ break;
|
||||
+ }
|
||||
ret = GetUnmaskedRenderer();
|
||||
if (ret && StaticPrefs::webgl_sanitize_unmasked_renderer()) {
|
||||
*ret = webgl::SanitizeRenderer(*ret);
|
||||
@@ -2400,6 +2535,10 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname,
|
||||
if (ShouldResistFingerprinting(RFPTarget::WebGLRenderInfo)) {
|
||||
ret = Some("Mozilla"_ns);
|
||||
} else {
|
||||
@@ -2452,6 +2587,10 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname,
|
||||
break;
|
||||
|
||||
case dom::WEBGL_debug_renderer_info_Binding::UNMASKED_VENDOR_WEBGL:
|
||||
@@ -290,10 +317,10 @@ index a1e5c4792d..be99491bf8 100644
|
||||
+ ret = Some(value.value());
|
||||
+ break;
|
||||
+ }
|
||||
ret = GetUnmaskedVendor();
|
||||
break;
|
||||
|
||||
@@ -2490,7 +2629,9 @@ void ClientWebGLContext::GetParameter(JSContext* cx, GLenum pname,
|
||||
ret = ShouldResistFingerprinting(RFPTarget::WebGLRenderInfo)
|
||||
? Some("Mozilla"_ns)
|
||||
: GetUnmaskedVendor();
|
||||
@@ -2545,7 +2684,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);
|
||||
@@ -304,32 +331,32 @@ index a1e5c4792d..be99491bf8 100644
|
||||
JS::Rooted<JS::Value> arr(cx);
|
||||
if (!dom::ToJSValue(cx, src.data(), src.size(), &arr)) {
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
@@ -2873,6 +3014,24 @@ ClientWebGLContext::GetShaderPrecisionFormat(const GLenum shadertype,
|
||||
const GLenum precisiontype) {
|
||||
@@ -2960,6 +3101,24 @@ ClientWebGLContext::GetShaderPrecisionFormat(const GLenum shadertype,
|
||||
const FuncScope funcScope(*this, "getShaderPrecisionFormat");
|
||||
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::CheckBool(
|
||||
+ mIsWebGL2 ? "webGl2:shaderPrecisionFormats:blockIfNotDefined"
|
||||
+ : "webGl:shaderPrecisionFormats:blockIfNotDefined")) {
|
||||
+ Maybe<webgl::ShaderPrecisionFormat> ret;
|
||||
+ return ret;
|
||||
+ }
|
||||
|
||||
+ // Check for spoofed value
|
||||
+ if (auto value =
|
||||
+ MaskConfig::MShaderData(shadertype, precisiontype, mIsWebGL2)) {
|
||||
+ const auto& format = value.value();
|
||||
+ const auto spoofed = webgl::ShaderPrecisionFormat{
|
||||
+ static_cast<uint8_t>(format[0]), // rangeMin
|
||||
+ static_cast<uint8_t>(format[1]), // rangeMax
|
||||
+ static_cast<uint8_t>(format[2]) // precision
|
||||
+ };
|
||||
+ return AsAddRefed(new WebGLShaderPrecisionFormatJS(spoofed));
|
||||
+ }
|
||||
+ // Check if block if not defined is on
|
||||
+ if (MaskConfig::CheckBool(
|
||||
+ mIsWebGL2 ? "webGl2:shaderPrecisionFormats:blockIfNotDefined"
|
||||
+ : "webGl:shaderPrecisionFormats:blockIfNotDefined")) {
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+
|
||||
const auto& inProcess = mNotLost->inProcess;
|
||||
if (inProcess) {
|
||||
return inProcess->GetShaderPrecisionFormat(shadertype, precisiontype);
|
||||
@@ -5853,6 +6012,17 @@ bool ClientWebGLContext::IsSupported(const WebGLExtensionID ext,
|
||||
const auto& shaderPrecisions = *notLost->info.shaderPrecisions;
|
||||
const auto args =
|
||||
webgl::GetShaderPrecisionFormatArgs{shadertype, precisiontype};
|
||||
@@ -6101,6 +6260,17 @@ bool ClientWebGLContext::IsSupported(const WebGLExtensionID ext,
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -347,8 +374,8 @@ index a1e5c4792d..be99491bf8 100644
|
||||
const auto& limits = Limits();
|
||||
return limits.supportedExtensions[ext];
|
||||
}
|
||||
@@ -5864,6 +6034,18 @@ void ClientWebGLContext::GetSupportedExtensions(
|
||||
if (!mNotLost) return;
|
||||
@@ -6112,6 +6282,18 @@ void ClientWebGLContext::GetSupportedExtensions(
|
||||
if (IsContextLost()) return;
|
||||
|
||||
auto& retarr = retval.SetValue();
|
||||
+
|
||||
@@ -366,29 +393,3 @@ index a1e5c4792d..be99491bf8 100644
|
||||
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 62957ee445..dfb2b8ff53 100644
|
||||
--- a/dom/canvas/ClientWebGLContext.h
|
||||
+++ b/dom/canvas/ClientWebGLContext.h
|
||||
@@ -1074,6 +1074,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<dom::WebGLContextAttributes>& 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
|
||||
|
||||
@@ -1,17 +1,556 @@
|
||||
diff --git a/dom/base/WebRTCIPManager.cpp b/dom/base/WebRTCIPManager.cpp
|
||||
new file mode 100644
|
||||
index 0000000000..94575f2631
|
||||
--- /dev/null
|
||||
+++ b/dom/base/WebRTCIPManager.cpp
|
||||
@@ -0,0 +1,218 @@
|
||||
+#include "WebRTCIPManager.h"
|
||||
+#include "nsPrintfCString.h"
|
||||
+#include "nsGlobalWindowInner.h"
|
||||
+#include "xpcpublic.h"
|
||||
+
|
||||
+namespace mozilla {
|
||||
+namespace dom {
|
||||
+
|
||||
+/* static */ nsString
|
||||
+WebRTCIPManager::IPv4KeyForUserContext(uint32_t userContextId) {
|
||||
+ nsString key;
|
||||
+ key.AppendLiteral(u"webrtc_ipv4_");
|
||||
+ key.AppendInt(userContextId);
|
||||
+ return key;
|
||||
+}
|
||||
+
|
||||
+/* static */ nsString
|
||||
+WebRTCIPManager::IPv6KeyForUserContext(uint32_t userContextId) {
|
||||
+ nsString key;
|
||||
+ key.AppendLiteral(u"webrtc_ipv6_");
|
||||
+ key.AppendInt(userContextId);
|
||||
+ return key;
|
||||
+}
|
||||
+
|
||||
+/* static */ nsString
|
||||
+WebRTCIPManager::IPv4DisabledKeyForUserContext(uint32_t userContextId) {
|
||||
+ nsString key;
|
||||
+ key.AppendLiteral(u"webrtc_ipv4_disabled_");
|
||||
+ key.AppendInt(userContextId);
|
||||
+ return key;
|
||||
+}
|
||||
+
|
||||
+/* static */ nsString
|
||||
+WebRTCIPManager::IPv6DisabledKeyForUserContext(uint32_t userContextId) {
|
||||
+ nsString key;
|
||||
+ key.AppendLiteral(u"webrtc_ipv6_disabled_");
|
||||
+ key.AppendInt(userContextId);
|
||||
+ return key;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+
|
||||
+/* static */ void
|
||||
+WebRTCIPManager::SetIPv4(uint32_t userContextId, const nsAString& ipv4) {
|
||||
+ nsString key = IPv4KeyForUserContext(userContextId);
|
||||
+ RoverfoxStorageManager::PutString(key, ipv4);
|
||||
+
|
||||
+ // Mark the function as disabled for this context after first use
|
||||
+ DisableIPv4Function(userContextId);
|
||||
+}
|
||||
+
|
||||
+/* static */ bool
|
||||
+WebRTCIPManager::GetIPv4(uint32_t userContextId, nsAString& outIPv4) {
|
||||
+ nsString key = IPv4KeyForUserContext(userContextId);
|
||||
+ return RoverfoxStorageManager::GetString(key, outIPv4);
|
||||
+}
|
||||
+
|
||||
+/* static */ void
|
||||
+WebRTCIPManager::SetIPv6(uint32_t userContextId, const nsAString& ipv6) {
|
||||
+ nsString key = IPv6KeyForUserContext(userContextId);
|
||||
+ RoverfoxStorageManager::PutString(key, ipv6);
|
||||
+
|
||||
+ // Mark the function as disabled for this context after first use
|
||||
+ DisableIPv6Function(userContextId);
|
||||
+}
|
||||
+
|
||||
+/* static */ bool
|
||||
+WebRTCIPManager::GetIPv6(uint32_t userContextId, nsAString& outIPv6) {
|
||||
+ nsString key = IPv6KeyForUserContext(userContextId);
|
||||
+ return RoverfoxStorageManager::GetString(key, outIPv6);
|
||||
+}
|
||||
+
|
||||
+
|
||||
+
|
||||
+/* static */ bool
|
||||
+WebRTCIPManager::IsIPv4FunctionDisabled(uint32_t userContextId) {
|
||||
+ nsString key = IPv4DisabledKeyForUserContext(userContextId);
|
||||
+ bool disabled = false;
|
||||
+ return RoverfoxStorageManager::GetBool(key, disabled) && disabled;
|
||||
+}
|
||||
+
|
||||
+/* static */ void
|
||||
+WebRTCIPManager::DisableIPv4Function(uint32_t userContextId) {
|
||||
+ nsString key = IPv4DisabledKeyForUserContext(userContextId);
|
||||
+ RoverfoxStorageManager::PutBool(key, true);
|
||||
+}
|
||||
+
|
||||
+/* static */ bool
|
||||
+WebRTCIPManager::IsIPv6FunctionDisabled(uint32_t userContextId) {
|
||||
+ nsString key = IPv6DisabledKeyForUserContext(userContextId);
|
||||
+ bool disabled = false;
|
||||
+ return RoverfoxStorageManager::GetBool(key, disabled) && disabled;
|
||||
+}
|
||||
+
|
||||
+/* static */ void
|
||||
+WebRTCIPManager::DisableIPv6Function(uint32_t userContextId) {
|
||||
+ nsString key = IPv6DisabledKeyForUserContext(userContextId);
|
||||
+ RoverfoxStorageManager::PutBool(key, true);
|
||||
+}
|
||||
+
|
||||
+
|
||||
+
|
||||
+/* static */ bool
|
||||
+WebRTCIPManager::IsIPv4FunctionEnabledForWebIDL(JSContext* aCx, JSObject* aObj) {
|
||||
+ // Get the window from the JS object
|
||||
+ nsGlobalWindowInner* win = xpc::WindowOrNull(aObj);
|
||||
+ if (!win) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ // Get the user context ID for this window
|
||||
+ uint32_t userContextId = 0;
|
||||
+
|
||||
+ // 1) Prefer the document's principal when available (content contexts)
|
||||
+ if (Document* doc = win->GetDoc()) {
|
||||
+ if (nsIPrincipal* p = doc->NodePrincipal()) {
|
||||
+ userContextId = p->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 2) Fallback: this inner window's docshell origin attributes
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* ds = win->GetDocShell()) {
|
||||
+ auto* concrete = static_cast<nsDocShell*>(ds);
|
||||
+ userContextId = concrete->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 3) Fallback: top browsing context's current inner window document/docshell
|
||||
+ if (userContextId == 0) {
|
||||
+ if (BrowsingContext* bc = win->GetBrowsingContext()) {
|
||||
+ RefPtr<BrowsingContext> top = bc->Top();
|
||||
+ if (top) {
|
||||
+ if (nsPIDOMWindowOuter* topOuter = top->GetDOMWindow()) {
|
||||
+ if (nsPIDOMWindowInner* topInner = topOuter->GetCurrentInnerWindow()) {
|
||||
+ if (Document* topDoc = topInner->GetExtantDoc()) {
|
||||
+ if (nsIPrincipal* tp = topDoc->NodePrincipal()) {
|
||||
+ userContextId = tp->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* topDS = topInner->GetDocShell()) {
|
||||
+ auto* concreteTop = static_cast<nsDocShell*>(topDS);
|
||||
+ userContextId = concreteTop->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Check if the function is disabled for this context
|
||||
+ bool disabled = false;
|
||||
+ RoverfoxStorageManager::GetBool(IPv4DisabledKeyForUserContext(userContextId), disabled);
|
||||
+ return !disabled;
|
||||
+}
|
||||
+
|
||||
+/* static */ bool
|
||||
+WebRTCIPManager::IsIPv6FunctionEnabledForWebIDL(JSContext* aCx, JSObject* aObj) {
|
||||
+ // Get the window from the JS object
|
||||
+ nsGlobalWindowInner* win = xpc::WindowOrNull(aObj);
|
||||
+ if (!win) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ // Get the user context ID for this window
|
||||
+ uint32_t userContextId = 0;
|
||||
+
|
||||
+ // 1) Prefer the document's principal when available (content contexts)
|
||||
+ if (Document* doc = win->GetDoc()) {
|
||||
+ if (nsIPrincipal* p = doc->NodePrincipal()) {
|
||||
+ userContextId = p->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 2) Fallback: this inner window's docshell origin attributes
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* ds = win->GetDocShell()) {
|
||||
+ auto* concrete = static_cast<nsDocShell*>(ds);
|
||||
+ userContextId = concrete->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 3) Fallback: top browsing context's current inner window document/docshell
|
||||
+ if (userContextId == 0) {
|
||||
+ if (BrowsingContext* bc = win->GetBrowsingContext()) {
|
||||
+ RefPtr<BrowsingContext> top = bc->Top();
|
||||
+ if (top) {
|
||||
+ if (nsPIDOMWindowOuter* topOuter = top->GetDOMWindow()) {
|
||||
+ if (nsPIDOMWindowInner* topInner = topOuter->GetCurrentInnerWindow()) {
|
||||
+ if (Document* topDoc = topInner->GetExtantDoc()) {
|
||||
+ if (nsIPrincipal* tp = topDoc->NodePrincipal()) {
|
||||
+ userContextId = tp->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* topDS = topInner->GetDocShell()) {
|
||||
+ auto* concreteTop = static_cast<nsDocShell*>(topDS);
|
||||
+ userContextId = concreteTop->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Check if the function is disabled for this context
|
||||
+ bool disabled = false;
|
||||
+ RoverfoxStorageManager::GetBool(IPv6DisabledKeyForUserContext(userContextId), disabled);
|
||||
+ return !disabled;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+
|
||||
+} // namespace dom
|
||||
+} // namespace mozilla
|
||||
diff --git a/dom/base/WebRTCIPManager.h b/dom/base/WebRTCIPManager.h
|
||||
new file mode 100644
|
||||
index 0000000000..c20cb032f6
|
||||
--- /dev/null
|
||||
+++ b/dom/base/WebRTCIPManager.h
|
||||
@@ -0,0 +1,105 @@
|
||||
+#ifndef mozilla_dom_WebRTCIPManager_h
|
||||
+#define mozilla_dom_WebRTCIPManager_h
|
||||
+
|
||||
+#include "nsString.h"
|
||||
+#include "RoverfoxStorageManager.h"
|
||||
+
|
||||
+namespace mozilla {
|
||||
+namespace dom {
|
||||
+
|
||||
+/**
|
||||
+ * WebRTCIPManager manages WebRTC IP addresses per user context.
|
||||
+ * This enables privacy-preserving WebRTC IP spoofing by allowing deterministic
|
||||
+ * IP address overrides that are isolated by user context.
|
||||
+ */
|
||||
+class WebRTCIPManager {
|
||||
+public:
|
||||
+ /**
|
||||
+ * Set the WebRTC IPv4 address for a given user context.
|
||||
+ * @param userContextId The user context ID (0 for default context)
|
||||
+ * @param ipv4 The IPv4 address to use for WebRTC connections
|
||||
+ */
|
||||
+ static void SetIPv4(uint32_t userContextId, const nsAString& ipv4);
|
||||
+
|
||||
+ /**
|
||||
+ * Get the WebRTC IPv4 address for a given user context.
|
||||
+ * @param userContextId The user context ID
|
||||
+ * @param outIPv4 The IPv4 address, empty if no address has been set
|
||||
+ * @return true if an IPv4 address has been set, false otherwise
|
||||
+ */
|
||||
+ static bool GetIPv4(uint32_t userContextId, nsAString& outIPv4);
|
||||
+
|
||||
+ /**
|
||||
+ * Set the WebRTC IPv6 address for a given user context.
|
||||
+ * @param userContextId The user context ID (0 for default context)
|
||||
+ * @param ipv6 The IPv6 address to use for WebRTC connections
|
||||
+ */
|
||||
+ static void SetIPv6(uint32_t userContextId, const nsAString& ipv6);
|
||||
+
|
||||
+ /**
|
||||
+ * Get the WebRTC IPv6 address for a given user context.
|
||||
+ * @param userContextId The user context ID
|
||||
+ * @param outIPv6 The IPv6 address, empty if no address has been set
|
||||
+ * @return true if an IPv6 address has been set, false otherwise
|
||||
+ */
|
||||
+ static bool GetIPv6(uint32_t userContextId, nsAString& outIPv6);
|
||||
+
|
||||
+
|
||||
+
|
||||
+ /**
|
||||
+ * Check if the setWebRTCIPv4 function has been used and should be disabled for a context.
|
||||
+ * @param userContextId The user context ID
|
||||
+ * @return true if the function has been used for this context and should not appear on new windows
|
||||
+ */
|
||||
+ static bool IsIPv4FunctionDisabled(uint32_t userContextId);
|
||||
+
|
||||
+ /**
|
||||
+ * Mark the setWebRTCIPv4 function as used/disabled for a context.
|
||||
+ * @param userContextId The user context ID
|
||||
+ */
|
||||
+ static void DisableIPv4Function(uint32_t userContextId);
|
||||
+
|
||||
+ /**
|
||||
+ * Check if the setWebRTCIPv6 function has been used and should be disabled for a context.
|
||||
+ * @param userContextId The user context ID
|
||||
+ * @return true if the function has been used for this context and should not appear on new windows
|
||||
+ */
|
||||
+ static bool IsIPv6FunctionDisabled(uint32_t userContextId);
|
||||
+
|
||||
+ /**
|
||||
+ * Mark the setWebRTCIPv6 function as used/disabled for a context.
|
||||
+ * @param userContextId The user context ID
|
||||
+ */
|
||||
+ static void DisableIPv6Function(uint32_t userContextId);
|
||||
+
|
||||
+
|
||||
+
|
||||
+ /**
|
||||
+ * WebIDL-compatible function to check if setWebRTCIPv4 should be enabled.
|
||||
+ * @param aCx The JavaScript context
|
||||
+ * @param aObj The JavaScript object (window)
|
||||
+ * @return true if the function should be enabled, false otherwise
|
||||
+ */
|
||||
+ static bool IsIPv4FunctionEnabledForWebIDL(JSContext* aCx, JSObject* aObj);
|
||||
+
|
||||
+ /**
|
||||
+ * WebIDL-compatible function to check if setWebRTCIPv6 should be enabled.
|
||||
+ * @param aCx The JavaScript context
|
||||
+ * @param aObj The JavaScript object (window)
|
||||
+ * @return true if the function should be enabled, false otherwise
|
||||
+ */
|
||||
+ static bool IsIPv6FunctionEnabledForWebIDL(JSContext* aCx, JSObject* aObj);
|
||||
+
|
||||
+
|
||||
+
|
||||
+private:
|
||||
+ static nsString IPv4KeyForUserContext(uint32_t userContextId);
|
||||
+ static nsString IPv6KeyForUserContext(uint32_t userContextId);
|
||||
+ static nsString IPv4DisabledKeyForUserContext(uint32_t userContextId);
|
||||
+ static nsString IPv6DisabledKeyForUserContext(uint32_t userContextId);
|
||||
+};
|
||||
+
|
||||
+} // namespace dom
|
||||
+} // namespace mozilla
|
||||
+
|
||||
+#endif // mozilla_dom_WebRTCIPManager_h
|
||||
diff --git a/dom/base/moz.build b/dom/base/moz.build
|
||||
index cd9090cda3..93cea20206 100644
|
||||
--- a/dom/base/moz.build
|
||||
+++ b/dom/base/moz.build
|
||||
@@ -1,3 +1,8 @@
|
||||
+# Link system SQLite for RoverfoxStorageManager embedded usage
|
||||
+OS_LIBS += [
|
||||
+ 'sqlite3',
|
||||
+]
|
||||
+
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
@@ -255,6 +260,7 @@ EXPORTS.mozilla.dom += [
|
||||
"RequestCallbackManager.h",
|
||||
"ResizeObserver.h",
|
||||
"ResponsiveImageSelector.h",
|
||||
+ "RoverfoxStorageManager.h",
|
||||
"SameProcessMessageQueue.h",
|
||||
"ScreenLuminance.h",
|
||||
"ScreenOrientation.h",
|
||||
@@ -289,6 +295,7 @@ EXPORTS.mozilla.dom += [
|
||||
"VideoFrameProvider.h",
|
||||
"ViewportMetaData.h",
|
||||
"VisualViewport.h",
|
||||
+ "WebRTCIPManager.h",
|
||||
"WindowFeatures.h",
|
||||
"WindowProxyHolder.h",
|
||||
]
|
||||
@@ -459,6 +466,7 @@ UNIFIED_SOURCES += [
|
||||
"RemoteOuterWindowProxy.cpp",
|
||||
"ResizeObserver.cpp",
|
||||
"ResponsiveImageSelector.cpp",
|
||||
+ "RoverfoxStorageManager.cpp",
|
||||
"SameProcessMessageQueue.cpp",
|
||||
"ScreenLuminance.cpp",
|
||||
"ScreenOrientation.cpp",
|
||||
@@ -494,6 +502,7 @@ UNIFIED_SOURCES += [
|
||||
"UserActivation.cpp",
|
||||
"ViewportMetaData.cpp",
|
||||
"VisualViewport.cpp",
|
||||
+ "WebRTCIPManager.cpp",
|
||||
"WindowDestroyedEvent.cpp",
|
||||
"WindowFeatures.cpp",
|
||||
"WindowNamedPropertiesHandler.cpp",
|
||||
diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp
|
||||
index 330a707789..4f87317dbb 100644
|
||||
--- a/dom/base/nsGlobalWindowInner.cpp
|
||||
+++ b/dom/base/nsGlobalWindowInner.cpp
|
||||
@@ -323,6 +323,10 @@
|
||||
#include "xpcprivate.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
+#include "WebRTCIPManager.h"
|
||||
+#include "nsDocShell.h"
|
||||
+#include "mozilla/OriginAttributes.h"
|
||||
+
|
||||
#ifdef NS_PRINTING
|
||||
# include "nsIPrintSettings.h"
|
||||
#endif
|
||||
@@ -7463,6 +7467,126 @@ IntlUtils* nsGlobalWindowInner::GetIntlUtils(ErrorResult& aError) {
|
||||
return mIntlUtils;
|
||||
}
|
||||
|
||||
+void nsGlobalWindowInner::SetWebRTCIPv4(const nsAString& ipv4, ErrorResult& aRv) {
|
||||
+ uint32_t userContextId = 0;
|
||||
+
|
||||
+ // 1) Prefer the document's principal when available (content contexts)
|
||||
+ if (Document* doc = GetDoc()) {
|
||||
+ if (nsIPrincipal* p = doc->NodePrincipal()) {
|
||||
+ userContextId = p->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 2) Fallback: this inner window's docshell origin attributes
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* ds = GetDocShell()) {
|
||||
+ auto* concrete = static_cast<nsDocShell*>(ds);
|
||||
+ userContextId = concrete->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 3) Fallback: top browsing context's current inner window document/docshell
|
||||
+ if (userContextId == 0) {
|
||||
+ if (BrowsingContext* bc = GetBrowsingContext()) {
|
||||
+ RefPtr<BrowsingContext> top = bc->Top();
|
||||
+ if (top) {
|
||||
+ if (nsPIDOMWindowOuter* topOuter = top->GetDOMWindow()) {
|
||||
+ if (nsPIDOMWindowInner* topInner = topOuter->GetCurrentInnerWindow()) {
|
||||
+ if (Document* topDoc = topInner->GetExtantDoc()) {
|
||||
+ if (nsIPrincipal* tp = topDoc->NodePrincipal()) {
|
||||
+ userContextId = tp->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* topDS = topInner->GetDocShell()) {
|
||||
+ auto* concreteTop = static_cast<nsDocShell*>(topDS);
|
||||
+ userContextId =
|
||||
+ concreteTop->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (userContextId == 0) {
|
||||
+ aRv.ThrowSecurityError("Unable to resolve user context ID for this context");
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ WebRTCIPManager::SetIPv4(userContextId, ipv4);
|
||||
+
|
||||
+ // Self-destruct: remove this function from the window object after first use
|
||||
+ if (JSContext* cx = nsContentUtils::GetCurrentJSContext()) {
|
||||
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
+ if (global) {
|
||||
+ JS_DeleteProperty(cx, global, "setWebRTCIPv4");
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void nsGlobalWindowInner::SetWebRTCIPv6(const nsAString& ipv6, ErrorResult& aRv) {
|
||||
+ uint32_t userContextId = 0;
|
||||
+
|
||||
+ // 1) Prefer the document's principal when available (content contexts)
|
||||
+ if (Document* doc = GetDoc()) {
|
||||
+ if (nsIPrincipal* p = doc->NodePrincipal()) {
|
||||
+ userContextId = p->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 2) Fallback: this inner window's docshell origin attributes
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* ds = GetDocShell()) {
|
||||
+ auto* concrete = static_cast<nsDocShell*>(ds);
|
||||
+ userContextId = concrete->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 3) Fallback: top browsing context's current inner window document/docshell
|
||||
+ if (userContextId == 0) {
|
||||
+ if (BrowsingContext* bc = GetBrowsingContext()) {
|
||||
+ RefPtr<BrowsingContext> top = bc->Top();
|
||||
+ if (top) {
|
||||
+ if (nsPIDOMWindowOuter* topOuter = top->GetDOMWindow()) {
|
||||
+ if (nsPIDOMWindowInner* topInner = topOuter->GetCurrentInnerWindow()) {
|
||||
+ if (Document* topDoc = topInner->GetExtantDoc()) {
|
||||
+ if (nsIPrincipal* tp = topDoc->NodePrincipal()) {
|
||||
+ userContextId = tp->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* topDS = topInner->GetDocShell()) {
|
||||
+ auto* concreteTop = static_cast<nsDocShell*>(topDS);
|
||||
+ userContextId =
|
||||
+ concreteTop->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (userContextId == 0) {
|
||||
+ aRv.ThrowSecurityError("Unable to resolve user context ID for this context");
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ WebRTCIPManager::SetIPv6(userContextId, ipv6);
|
||||
+
|
||||
+ // Self-destruct: remove this function from the window object after first use
|
||||
+ if (JSContext* cx = nsContentUtils::GetCurrentJSContext()) {
|
||||
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
+ if (global) {
|
||||
+ JS_DeleteProperty(cx, global, "setWebRTCIPv6");
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+
|
||||
+
|
||||
void nsGlobalWindowInner::StoreSharedWorker(SharedWorker* aSharedWorker) {
|
||||
MOZ_ASSERT(aSharedWorker);
|
||||
MOZ_ASSERT(!mSharedWorkers.Contains(aSharedWorker));
|
||||
diff --git a/dom/base/nsGlobalWindowInner.h b/dom/base/nsGlobalWindowInner.h
|
||||
index 648bcda859..c562b95f87 100644
|
||||
--- a/dom/base/nsGlobalWindowInner.h
|
||||
+++ b/dom/base/nsGlobalWindowInner.h
|
||||
@@ -681,6 +681,10 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
|
||||
|
||||
mozilla::dom::IntlUtils* GetIntlUtils(mozilla::ErrorResult& aRv);
|
||||
|
||||
+ // WebRTC IP addresses for privacy-preserving IP spoofing
|
||||
+ void SetWebRTCIPv4(const nsAString& ipv4, mozilla::ErrorResult& aRv);
|
||||
+ void SetWebRTCIPv6(const nsAString& ipv6, mozilla::ErrorResult& aRv);
|
||||
+
|
||||
void StoreSharedWorker(mozilla::dom::SharedWorker* aSharedWorker);
|
||||
|
||||
void ForgetSharedWorker(mozilla::dom::SharedWorker* aSharedWorker);
|
||||
diff --git a/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp b/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp
|
||||
index 235d5dde8f..d0102a6746 100644
|
||||
index ca237977cd..abfc081ae7 100644
|
||||
--- a/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp
|
||||
+++ b/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp
|
||||
@@ -116,6 +116,8 @@
|
||||
|
||||
#include "mozilla/dom/BrowserChild.h"
|
||||
@@ -99,6 +99,10 @@
|
||||
#include "mozilla/glean/DomMediaWebrtcMetrics.h"
|
||||
#include "mozilla/net/DataChannelProtocol.h"
|
||||
#include "mozilla/net/WebrtcProxyConfig.h"
|
||||
+#include "MaskConfig.hpp"
|
||||
+#include "mozilla/RustRegex.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
// We need to undef the MS macro again in case the windows include file
|
||||
@@ -1702,7 +1704,15 @@ PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP) {
|
||||
+#include "WebRTCIPManager.h"
|
||||
+#include "nsDocShell.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMJSUtils.h"
|
||||
#include "nsGlobalWindowInner.h"
|
||||
@@ -1581,7 +1585,15 @@ PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP) {
|
||||
}
|
||||
};
|
||||
|
||||
@@ -28,7 +567,7 @@ index 235d5dde8f..d0102a6746 100644
|
||||
|
||||
SyncToJsep();
|
||||
|
||||
@@ -3224,12 +3234,22 @@ const std::string& PeerConnectionImpl::GetName() {
|
||||
@@ -3229,12 +3241,22 @@ const std::string& PeerConnectionImpl::GetName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
@@ -52,7 +591,7 @@ index 235d5dde8f..d0102a6746 100644
|
||||
if (mForceIceTcp && std::string::npos != candidate.find(" UDP ")) {
|
||||
CSFLogWarn(LOGTAG, "Blocking local UDP candidate: %s", candidate.c_str());
|
||||
STAMP_TIMECARD(mTimeCard, "UDP Ice Candidate blocked");
|
||||
@@ -3292,10 +3312,184 @@ void PeerConnectionImpl::SendLocalIceCandidateToContent(
|
||||
@@ -3297,10 +3319,306 @@ void PeerConnectionImpl::SendLocalIceCandidateToContent(
|
||||
uint16_t level, const std::string& mid, const std::string& candidate,
|
||||
const std::string& ufrag) {
|
||||
STAMP_TIMECARD(mTimeCard, "Send Ice Candidate to content");
|
||||
@@ -77,11 +616,56 @@ index 235d5dde8f..d0102a6746 100644
|
||||
+}
|
||||
+
|
||||
+bool PeerConnectionImpl::ShouldSpoofCandidateIP() const {
|
||||
+ // Checks if either webrtc:ipv4 or webrtc:ipv6 is set in the config
|
||||
+ return MaskConfig::GetString("webrtc:ipv4").has_value() ||
|
||||
+ MaskConfig::GetString("webrtc:ipv6").has_value() ||
|
||||
+ MaskConfig::GetString("webrtc:localipv4").has_value() ||
|
||||
+ MaskConfig::GetString("webrtc:localipv6").has_value();
|
||||
+ if (!mWindow) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ // Get the user context ID for this window using the same logic as WebRTCIPManager
|
||||
+ uint32_t userContextId = 0;
|
||||
+
|
||||
+ // 1) Prefer the document's principal when available (content contexts)
|
||||
+ if (Document* doc = mWindow->GetDoc()) {
|
||||
+ if (nsIPrincipal* p = doc->NodePrincipal()) {
|
||||
+ userContextId = p->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 2) Fallback: this inner window's docshell origin attributes
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* ds = mWindow->GetDocShell()) {
|
||||
+ auto* concrete = static_cast<nsDocShell*>(ds);
|
||||
+ userContextId = concrete->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 3) Fallback: top browsing context's current inner window document/docshell
|
||||
+ if (userContextId == 0) {
|
||||
+ if (BrowsingContext* bc = mWindow->GetBrowsingContext()) {
|
||||
+ RefPtr<BrowsingContext> top = bc->Top();
|
||||
+ if (top) {
|
||||
+ if (nsPIDOMWindowOuter* topOuter = top->GetDOMWindow()) {
|
||||
+ if (nsPIDOMWindowInner* topInner = topOuter->GetCurrentInnerWindow()) {
|
||||
+ if (Document* topDoc = topInner->GetExtantDoc()) {
|
||||
+ if (nsIPrincipal* tp = topDoc->NodePrincipal()) {
|
||||
+ userContextId = tp->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* topDS = topInner->GetDocShell()) {
|
||||
+ auto* concreteTop = static_cast<nsDocShell*>(topDS);
|
||||
+ userContextId = concreteTop->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Check if either IPv4 or IPv6 addresses have been set for this user context
|
||||
+ nsString ipv4, ipv6;
|
||||
+ return WebRTCIPManager::GetIPv4(userContextId, ipv4) ||
|
||||
+ WebRTCIPManager::GetIPv6(userContextId, ipv6);
|
||||
+}
|
||||
+
|
||||
+// *****
|
||||
@@ -117,29 +701,25 @@ index 235d5dde8f..d0102a6746 100644
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+std::string getMaskForIP(const std::string& ip) {
|
||||
+std::string getMaskForIP(const std::string& ip, uint32_t userContextId) {
|
||||
+ // Get the corresponding mask for an ip address
|
||||
+ if (isSpecialIP(ip)) {
|
||||
+ return ip; // dont mask special IPs
|
||||
+ }
|
||||
+ bool isIPv6 = (ip.find(':') != std::string::npos);
|
||||
+ bool isPrivate = isPrivateIP(ip);
|
||||
+ auto ipv4Value = MaskConfig::GetString("webrtc:ipv4");
|
||||
+ auto ipv6Value = MaskConfig::GetString("webrtc:ipv6");
|
||||
+ auto localIpv4Value = MaskConfig::GetString("webrtc:localipv4");
|
||||
+ auto localIpv6Value = MaskConfig::GetString("webrtc:localipv6");
|
||||
+
|
||||
+ // Get IP addresses from WebRTCIPManager for this user context
|
||||
+ nsString ipv4Value, ipv6Value;
|
||||
+ bool hasIPv4 = WebRTCIPManager::GetIPv4(userContextId, ipv4Value);
|
||||
+ bool hasIPv6 = WebRTCIPManager::GetIPv6(userContextId, ipv6Value);
|
||||
+
|
||||
+ if (isIPv6) {
|
||||
+ if (isPrivate && localIpv6Value) {
|
||||
+ return localIpv6Value.value();
|
||||
+ } else if (ipv6Value) {
|
||||
+ return ipv6Value.value();
|
||||
+ if (hasIPv6) {
|
||||
+ return NS_ConvertUTF16toUTF8(ipv6Value).get();
|
||||
+ }
|
||||
+ } else {
|
||||
+ if (isPrivate && localIpv4Value) {
|
||||
+ return localIpv4Value.value();
|
||||
+ } else if (ipv4Value) {
|
||||
+ return ipv4Value.value();
|
||||
+ if (hasIPv4) {
|
||||
+ return NS_ConvertUTF16toUTF8(ipv4Value).get();
|
||||
+ }
|
||||
+ }
|
||||
+ // return original ip if no mask is available
|
||||
@@ -147,7 +727,7 @@ index 235d5dde8f..d0102a6746 100644
|
||||
+}
|
||||
+
|
||||
+std::string replaceIPAddresses(
|
||||
+ const std::string& input, const char* pattern) {
|
||||
+ const std::string& input, const char* pattern, uint32_t userContextId) {
|
||||
+ // Replace IP addresses in a output line
|
||||
+ mozilla::RustRegex regex(pattern);
|
||||
+ if (!regex.IsValid()) {
|
||||
@@ -160,7 +740,7 @@ index 235d5dde8f..d0102a6746 100644
|
||||
+
|
||||
+ while (auto match = iter.Next()) {
|
||||
+ std::string ip = input.substr(match->start, match->end - match->start);
|
||||
+ std::string mask = getMaskForIP(ip);
|
||||
+ std::string mask = getMaskForIP(ip, userContextId);
|
||||
+
|
||||
+ if (mask != ip) {
|
||||
+ result.append(input, lastEnd, match->start - lastEnd);
|
||||
@@ -181,27 +761,108 @@ index 235d5dde8f..d0102a6746 100644
|
||||
+ if (!ShouldSpoofCandidateIP() || candidate.empty()) {
|
||||
+ return candidate;
|
||||
+ }
|
||||
+
|
||||
+ // Get the user context ID for this PeerConnection
|
||||
+ uint32_t userContextId = 0;
|
||||
+ if (mWindow) {
|
||||
+ // 1) Prefer the document's principal when available (content contexts)
|
||||
+ if (Document* doc = mWindow->GetDoc()) {
|
||||
+ if (nsIPrincipal* p = doc->NodePrincipal()) {
|
||||
+ userContextId = p->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 2) Fallback: this inner window's docshell origin attributes
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* ds = mWindow->GetDocShell()) {
|
||||
+ auto* concrete = static_cast<nsDocShell*>(ds);
|
||||
+ userContextId = concrete->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 3) Fallback: top browsing context's current inner window document/docshell
|
||||
+ if (userContextId == 0) {
|
||||
+ if (BrowsingContext* bc = mWindow->GetBrowsingContext()) {
|
||||
+ RefPtr<BrowsingContext> top = bc->Top();
|
||||
+ if (top) {
|
||||
+ if (nsPIDOMWindowOuter* topOuter = top->GetDOMWindow()) {
|
||||
+ if (nsPIDOMWindowInner* topInner = topOuter->GetCurrentInnerWindow()) {
|
||||
+ if (Document* topDoc = topInner->GetExtantDoc()) {
|
||||
+ if (nsIPrincipal* tp = topDoc->NodePrincipal()) {
|
||||
+ userContextId = tp->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* topDS = topInner->GetDocShell()) {
|
||||
+ auto* concreteTop = static_cast<nsDocShell*>(topDS);
|
||||
+ userContextId = concreteTop->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // ipv4
|
||||
+ const char* ipv4Pattern = "(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)";
|
||||
+ std::string result = replaceIPAddresses(candidate, ipv4Pattern);
|
||||
+ std::string result = replaceIPAddresses(candidate, ipv4Pattern, userContextId);
|
||||
+ // ipv6
|
||||
+ const char* ipv6Pattern = "(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}";
|
||||
+ result = replaceIPAddresses(result, ipv6Pattern);
|
||||
+ result = replaceIPAddresses(result, ipv6Pattern, userContextId);
|
||||
+
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
+nsresult PeerConnectionImpl::SanitizeSDPForIPLeak(std::string& sdp) {
|
||||
+ // Sanitize sdp
|
||||
+ auto ipv4Value = MaskConfig::GetString("webrtc:ipv4");
|
||||
+ auto ipv6Value = MaskConfig::GetString("webrtc:ipv6");
|
||||
+ auto localIpv4Value = MaskConfig::GetString("webrtc:localipv4");
|
||||
+ auto localIpv6Value = MaskConfig::GetString("webrtc:localipv6");
|
||||
+
|
||||
+ if (!ipv4Value && !ipv6Value && !localIpv4Value && !localIpv6Value) {
|
||||
+ if (!ShouldSpoofCandidateIP()) {
|
||||
+ return NS_OK;
|
||||
+ }
|
||||
+
|
||||
+ // Resolve user context ID using the same logic as other functions
|
||||
+ uint32_t userContextId = 0;
|
||||
+
|
||||
+ // 1) Prefer the document's principal when available (content contexts)
|
||||
+ if (Document* doc = mWindow->GetDoc()) {
|
||||
+ if (nsIPrincipal* p = doc->NodePrincipal()) {
|
||||
+ userContextId = p->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 2) Fallback: this inner window's docshell origin attributes
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* ds = mWindow->GetDocShell()) {
|
||||
+ auto* concrete = static_cast<nsDocShell*>(ds);
|
||||
+ userContextId = concrete->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // 3) Fallback: top browsing context's current inner window document/docshell
|
||||
+ if (userContextId == 0) {
|
||||
+ if (BrowsingContext* bc = mWindow->GetBrowsingContext()) {
|
||||
+ RefPtr<BrowsingContext> top = bc->Top();
|
||||
+ if (top) {
|
||||
+ if (nsPIDOMWindowOuter* topOuter = top->GetDOMWindow()) {
|
||||
+ if (nsPIDOMWindowInner* topInner = topOuter->GetCurrentInnerWindow()) {
|
||||
+ if (Document* topDoc = topInner->GetExtantDoc()) {
|
||||
+ if (nsIPrincipal* tp = topDoc->NodePrincipal()) {
|
||||
+ userContextId = tp->OriginAttributesRef().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ if (userContextId == 0) {
|
||||
+ if (nsIDocShell* topDS = topInner->GetDocShell()) {
|
||||
+ auto* concreteTop = static_cast<nsDocShell*>(topDS);
|
||||
+ userContextId = concreteTop->GetOriginAttributes().mUserContextId;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // process the SDP line by line to handle candidate lines
|
||||
+ std::istringstream iss(sdp);
|
||||
+ std::ostringstream oss;
|
||||
@@ -228,10 +889,10 @@ index 235d5dde8f..d0102a6746 100644
|
||||
+
|
||||
+ // ipv4
|
||||
+ const char* ipv4Pattern = "(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)";
|
||||
+ processedSdp = replaceIPAddresses(processedSdp, ipv4Pattern);
|
||||
+ processedSdp = replaceIPAddresses(processedSdp, ipv4Pattern, userContextId);
|
||||
+ // ipv6
|
||||
+ const char* ipv6Pattern = "(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}";
|
||||
+ processedSdp = replaceIPAddresses(processedSdp, ipv6Pattern);
|
||||
+ processedSdp = replaceIPAddresses(processedSdp, ipv6Pattern, userContextId);
|
||||
+
|
||||
+ if (processedSdp != originalSdp) {
|
||||
+ sdp = processedSdp;
|
||||
@@ -241,7 +902,7 @@ index 235d5dde8f..d0102a6746 100644
|
||||
}
|
||||
|
||||
void PeerConnectionImpl::IceConnectionStateChange(
|
||||
@@ -3625,11 +3819,27 @@ void PeerConnectionImpl::UpdateDefaultCandidate(
|
||||
@@ -3630,11 +3948,27 @@ void PeerConnectionImpl::UpdateDefaultCandidate(
|
||||
const std::string& defaultRtcpAddr, uint16_t defaultRtcpPort,
|
||||
const std::string& transportId) {
|
||||
CSFLogDebug(LOGTAG, "%s", __FUNCTION__);
|
||||
@@ -271,7 +932,7 @@ index 235d5dde8f..d0102a6746 100644
|
||||
transportId);
|
||||
}
|
||||
}
|
||||
@@ -4399,8 +4609,10 @@ bool PeerConnectionImpl::GetPrefDefaultAddressOnly() const {
|
||||
@@ -4405,8 +4739,10 @@ bool PeerConnectionImpl::GetPrefDefaultAddressOnly() const {
|
||||
|
||||
uint64_t winId = mWindow->WindowID();
|
||||
|
||||
@@ -285,10 +946,10 @@ index 235d5dde8f..d0102a6746 100644
|
||||
!MediaManager::Get()->IsActivelyCapturingOrHasAPermission(winId);
|
||||
return default_address_only;
|
||||
diff --git a/dom/media/webrtc/jsapi/PeerConnectionImpl.h b/dom/media/webrtc/jsapi/PeerConnectionImpl.h
|
||||
index 138cc07bd9..91cc12f87e 100644
|
||||
index 25fc44d757..7902015d73 100644
|
||||
--- a/dom/media/webrtc/jsapi/PeerConnectionImpl.h
|
||||
+++ b/dom/media/webrtc/jsapi/PeerConnectionImpl.h
|
||||
@@ -621,8 +621,12 @@ class PeerConnectionImpl final
|
||||
@@ -622,8 +622,12 @@ class PeerConnectionImpl final
|
||||
RefPtr<MediaPipeline> GetMediaPipelineForTrack(
|
||||
dom::MediaStreamTrack& aRecvTrack);
|
||||
|
||||
@@ -303,10 +964,10 @@ index 138cc07bd9..91cc12f87e 100644
|
||||
const std::string& candidate,
|
||||
const std::string& ufrag);
|
||||
diff --git a/dom/media/webrtc/jsapi/moz.build b/dom/media/webrtc/jsapi/moz.build
|
||||
index 925190505a..878853f0f0 100644
|
||||
index 62bb6eecff..da26fe05ee 100644
|
||||
--- a/dom/media/webrtc/jsapi/moz.build
|
||||
+++ b/dom/media/webrtc/jsapi/moz.build
|
||||
@@ -62,3 +62,6 @@ EXPORTS.mozilla.dom += [
|
||||
@@ -68,3 +68,6 @@ EXPORTS.mozilla.dom += [
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
@@ -314,3 +975,20 @@ index 925190505a..878853f0f0 100644
|
||||
+# DOM Mask
|
||||
+LOCAL_INCLUDES += ["/camoucfg"]
|
||||
\ No newline at end of file
|
||||
diff --git a/dom/webidl/Window.webidl b/dom/webidl/Window.webidl
|
||||
index 7c509eb87e..0a9010c2d1 100644
|
||||
--- a/dom/webidl/Window.webidl
|
||||
+++ b/dom/webidl/Window.webidl
|
||||
@@ -892,6 +892,12 @@ partial interface Window {
|
||||
readonly attribute VisualViewport visualViewport;
|
||||
};
|
||||
|
||||
+// WebRTC IP interfaces for privacy-preserving IP spoofing
|
||||
+partial interface Window {
|
||||
+ [Throws, Func="mozilla::dom::WebRTCIPManager::IsIPv4FunctionEnabledForWebIDL"]
|
||||
+ undefined setWebRTCIPv4(DOMString ipv4);
|
||||
+};
|
||||
+
|
||||
// Used to assign marks to appear on the scrollbar when
|
||||
// finding on a page.
|
||||
partial interface Window {
|
||||
|
||||
@@ -1,36 +1,29 @@
|
||||
# LibreWolf windows-theming-bug.patch
|
||||
#
|
||||
# Author: Malte Jürgens <maltejur@dismail.de>
|
||||
# Description: Fix broken titlebar on Windows
|
||||
# Last Updated: 2023-03-17
|
||||
# License: MPL 2.0
|
||||
#
|
||||
# If we use `--with-app-name=librewolf`, Firefox will think it is running on a
|
||||
# super old windows version. This will cause it to make some changes
|
||||
# which break the titlebar styling (and probably also some other things).
|
||||
#
|
||||
# See:
|
||||
# https://gitlab.com/librewolf-community/browser/windows/-/issues/146
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=1801640
|
||||
diff --git a/browser/app/Makefile.in b/browser/app/Makefile.in
|
||||
index 54d6b43fe126..116410bb89d9 100644
|
||||
index d11c35ff9d..0aa4b9058b 100644
|
||||
--- a/browser/app/Makefile.in
|
||||
+++ b/browser/app/Makefile.in
|
||||
@@ -31,7 +31,7 @@ ifeq ($(OS_ARCH),WINNT)
|
||||
@@ -23,7 +23,7 @@ ifeq ($(OS_ARCH),WINNT)
|
||||
# (this dependency should really be just for firefox.exe, not other targets)
|
||||
# Note the manifest file exists in the tree, so we use the explicit filename
|
||||
# here.
|
||||
-EXTRA_DEPS += $(srcdir)/firefox.exe.manifest
|
||||
+EXTRA_DEPS += $(srcdir)/camoufox.exe.manifest
|
||||
endif
|
||||
|
||||
|
||||
PROGRAMS_DEST = $(DIST)/bin
|
||||
diff --git a/browser/app/firefox.exe.manifest b/browser/app/camoufox.manifest
|
||||
similarity index 96%
|
||||
rename from browser/app/firefox.exe.manifest
|
||||
rename to browser/app/camoufox.exe.manifest
|
||||
@@ -92,7 +92,7 @@ tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME) $(objdir)/macbuild/Contents/MacOS-
|
||||
rsync -aL $(DIST)/bin/$(MOZ_APP_NAME) '$(dist_dest)/Contents/MacOS'
|
||||
cp -RL $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/firefox.icns '$(dist_dest)/Contents/Resources/firefox.icns'
|
||||
cp -RL $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/document.icns '$(dist_dest)/Contents/Resources/document.icns'
|
||||
- cp -RL $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/Assets.car '$(dist_dest)/Contents/Resources/Assets.car'
|
||||
+ test ! -f $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/Assets.car || cp -RL $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/Assets.car '$(dist_dest)/Contents/Resources/Assets.car'
|
||||
$(MKDIR) -p '$(dist_dest)/Contents/Library/LaunchServices'
|
||||
ifdef MOZ_UPDATER
|
||||
cp -f '$(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater' '$(dist_dest)/Contents/Library/LaunchServices'
|
||||
diff --git a/browser/app/firefox.exe.manifest b/browser/app/firefox.exe.manifest
|
||||
index 995b2fc869..4a03da3064 100644
|
||||
--- a/browser/app/firefox.exe.manifest
|
||||
+++ b/browser/app/camoufox.exe.manifest
|
||||
+++ b/browser/app/firefox.exe.manifest
|
||||
@@ -3,10 +3,10 @@
|
||||
<assemblyIdentity
|
||||
version="1.0.0.0"
|
||||
|
||||
@@ -40,6 +40,12 @@ run 'cp -v ../../settings/properties.json .'
|
||||
run 'touch moz.build'
|
||||
popd > /dev/null
|
||||
|
||||
# Generate Assets.car for macOS builds (if on macOS) or ensure it exists
|
||||
if [[ ! -f ../additions/browser/branding/camoufox/Assets.car ]]; then
|
||||
echo "Generating Assets.car..."
|
||||
bash ../scripts/generate-assets-car.sh
|
||||
fi
|
||||
|
||||
# Copy ALL new files/folders from ../additions to .
|
||||
run 'cp -r ../additions/* .'
|
||||
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
#!/bin/bash
|
||||
# Generates Assets.car for macOS builds from the camoufox branding icons
|
||||
# This script must be run on macOS (requires actool from Xcode)
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BRANDING_DIR="$SCRIPT_DIR/../additions/browser/branding/camoufox"
|
||||
XCASSETS_DIR="$BRANDING_DIR/Assets.xcassets"
|
||||
APPICONSET_DIR="$XCASSETS_DIR/AppIcon.appiconset"
|
||||
OUTPUT_FILE="$BRANDING_DIR/Assets.car"
|
||||
|
||||
# Check if running on macOS
|
||||
if [[ "$(uname)" != "Darwin" ]]; then
|
||||
echo "Warning: Assets.car can only be generated on macOS."
|
||||
echo "Using existing Assets.car if available, or copying from official branding."
|
||||
|
||||
# If Assets.car doesn't exist, try to copy from official branding
|
||||
if [[ ! -f "$OUTPUT_FILE" ]]; then
|
||||
OFFICIAL_ASSETS="$SCRIPT_DIR/../camoufox-*/browser/branding/official/Assets.car"
|
||||
if ls $OFFICIAL_ASSETS 1>/dev/null 2>&1; then
|
||||
cp $(ls $OFFICIAL_ASSETS | head -1) "$OUTPUT_FILE"
|
||||
echo "Copied Assets.car from official branding."
|
||||
else
|
||||
echo "Error: No Assets.car available and cannot generate on non-macOS."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check for actool
|
||||
if ! command -v actool &>/dev/null && ! xcrun --find actool &>/dev/null; then
|
||||
echo "Error: actool not found. Please install Xcode Command Line Tools."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ACTOOL=$(xcrun --find actool 2>/dev/null || echo "actool")
|
||||
|
||||
echo "Generating Assets.car from camoufox branding icons..."
|
||||
|
||||
# Create xcassets structure
|
||||
mkdir -p "$APPICONSET_DIR"
|
||||
|
||||
# Create Contents.json for the asset catalog
|
||||
cat > "$XCASSETS_DIR/Contents.json" << 'EOF'
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Create Contents.json for AppIcon with all icon sizes macOS needs
|
||||
cat > "$APPICONSET_DIR/Contents.json" << 'EOF'
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "icon_16x16.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_16x16@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_32x32.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_32x32@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_128x128.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_128x128@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_256x256.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_256x256@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_512x512.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_512x512@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Copy/resize icons to the required sizes
|
||||
# We'll use sips (macOS built-in) to resize if needed
|
||||
copy_or_resize() {
|
||||
local src="$1"
|
||||
local dst="$2"
|
||||
local size="$3"
|
||||
|
||||
if [[ -f "$src" ]]; then
|
||||
cp "$src" "$dst"
|
||||
sips -z "$size" "$size" "$dst" >/dev/null 2>&1 || true
|
||||
elif [[ -f "$BRANDING_DIR/default256.png" ]]; then
|
||||
# Fallback to default256 and resize
|
||||
cp "$BRANDING_DIR/default256.png" "$dst"
|
||||
sips -z "$size" "$size" "$dst" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
|
||||
# Map branding icons to xcassets icons
|
||||
copy_or_resize "$BRANDING_DIR/default16.png" "$APPICONSET_DIR/icon_16x16.png" 16
|
||||
copy_or_resize "$BRANDING_DIR/default32.png" "$APPICONSET_DIR/icon_16x16@2x.png" 32
|
||||
copy_or_resize "$BRANDING_DIR/default32.png" "$APPICONSET_DIR/icon_32x32.png" 32
|
||||
copy_or_resize "$BRANDING_DIR/default64.png" "$APPICONSET_DIR/icon_32x32@2x.png" 64
|
||||
copy_or_resize "$BRANDING_DIR/default128.png" "$APPICONSET_DIR/icon_128x128.png" 128
|
||||
copy_or_resize "$BRANDING_DIR/default256.png" "$APPICONSET_DIR/icon_128x128@2x.png" 256
|
||||
copy_or_resize "$BRANDING_DIR/default256.png" "$APPICONSET_DIR/icon_256x256.png" 256
|
||||
copy_or_resize "$BRANDING_DIR/default256.png" "$APPICONSET_DIR/icon_256x256@2x.png" 512
|
||||
copy_or_resize "$BRANDING_DIR/default256.png" "$APPICONSET_DIR/icon_512x512.png" 512
|
||||
copy_or_resize "$BRANDING_DIR/default256.png" "$APPICONSET_DIR/icon_512x512@2x.png" 1024
|
||||
|
||||
# Generate Assets.car using actool
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
"$ACTOOL" \
|
||||
--compile "$TEMP_DIR" \
|
||||
--platform macosx \
|
||||
--minimum-deployment-target 10.15 \
|
||||
--app-icon AppIcon \
|
||||
--output-partial-info-plist "$TEMP_DIR/Info.plist" \
|
||||
"$XCASSETS_DIR"
|
||||
|
||||
# Move the generated Assets.car
|
||||
if [[ -f "$TEMP_DIR/Assets.car" ]]; then
|
||||
mv "$TEMP_DIR/Assets.car" "$OUTPUT_FILE"
|
||||
echo "Successfully generated: $OUTPUT_FILE"
|
||||
else
|
||||
echo "Error: Failed to generate Assets.car"
|
||||
rm -rf "$TEMP_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
rm -rf "$TEMP_DIR"
|
||||
|
||||
echo "Done!"
|
||||
@@ -11,7 +11,9 @@ Run:
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
|
||||
@@ -45,6 +47,14 @@ class Patcher:
|
||||
"""
|
||||
version, release = extract_args()
|
||||
with temp_cd(find_src_dir('.', version, release)):
|
||||
# Reset to unpatched state first (like "Find broken patches")
|
||||
print("Resetting to unpatched state...")
|
||||
run('git clean -fdx && ./mach clobber && git reset --hard unpatched', exit_on_fail=False)
|
||||
|
||||
# Re-copy additions and settings after reset
|
||||
print("Re-copying additions and settings...")
|
||||
run(f'bash ../scripts/copy-additions.sh {version} {release}')
|
||||
|
||||
# Create the base mozconfig file
|
||||
run('cp -v ../assets/base.mozconfig mozconfig')
|
||||
# Set cross building target
|
||||
@@ -65,15 +75,74 @@ class Patcher:
|
||||
else:
|
||||
non_roverfox.append(p)
|
||||
|
||||
# Track patch failures
|
||||
failed_patches = []
|
||||
|
||||
# Apply non-roverfox patches first
|
||||
for patch_file in non_roverfox:
|
||||
patch(patch_file)
|
||||
rejects = self._apply_and_check(patch_file)
|
||||
if rejects:
|
||||
failed_patches.append((patch_file, rejects))
|
||||
|
||||
# Apply roverfox patches last
|
||||
for patch_file in roverfox:
|
||||
patch(patch_file)
|
||||
rejects = self._apply_and_check(patch_file)
|
||||
if rejects:
|
||||
failed_patches.append((patch_file, rejects))
|
||||
|
||||
# Report failures
|
||||
if failed_patches:
|
||||
print('\n' + '='*70)
|
||||
print(f'ERROR: {len(failed_patches)} patch(es) failed to apply cleanly:')
|
||||
print('='*70)
|
||||
for patch_file, rejects in failed_patches:
|
||||
print(f'\n{patch_file}:')
|
||||
for reject in rejects:
|
||||
print(f' - {reject}')
|
||||
print('='*70)
|
||||
sys.exit(1)
|
||||
|
||||
print('Complete!')
|
||||
|
||||
def _apply_and_check(self, patch_file):
|
||||
"""
|
||||
Apply a patch and check for reject files.
|
||||
Returns list of reject files if any, empty list otherwise.
|
||||
"""
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
print(f"\n*** -> patch -p1 -i {patch_file}")
|
||||
sys.stdout.flush()
|
||||
|
||||
# Apply patch interactively - don't capture stdout/stderr at all
|
||||
# This allows prompts to show immediately and user can respond
|
||||
# --forward flag: skip patches that appear to be already applied
|
||||
# --binary flag: preserve line endings (helps with CRLF vs LF differences)
|
||||
# -l flag: ignore whitespace differences
|
||||
result = subprocess.run(
|
||||
['patch', '-p1', '--forward', '-l', '--binary', '-i', patch_file],
|
||||
stdin=sys.stdin,
|
||||
stdout=sys.stdout,
|
||||
stderr=sys.stderr,
|
||||
text=True
|
||||
)
|
||||
|
||||
# After patch completes, search for any .rej files created
|
||||
rejects = []
|
||||
for root, dirs, files in os.walk('.'):
|
||||
for file in files:
|
||||
if file.endswith('.rej'):
|
||||
# Check if this is a newly created reject file
|
||||
reject_path = os.path.join(root, file)
|
||||
# Only include if it was just created (within last minute)
|
||||
if os.path.exists(reject_path):
|
||||
import time
|
||||
if time.time() - os.path.getmtime(reject_path) < 60:
|
||||
rejects.append(reject_path)
|
||||
|
||||
return rejects
|
||||
|
||||
def _update_mozconfig(self):
|
||||
"""
|
||||
Helper for adding additional mozconfig code from assets/<target>.mozconfig
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version=135.0.1
|
||||
release=beta.24
|
||||
version=146.0.1
|
||||
release=beta.25
|
||||
closedsrc_rev=1.0.0
|
||||
|
||||