Juggler: Add logging & fix frame execution issues #3 #6

- Add back frame execution contexts. Fixes leaks by using an isolated context. #3 #6
- Fixed other small errors in Juggler (viewport size & error message stack)
- Add debugging functionality to Juggler
- Add launcher argument to write stderr to a log file
- Bumped to v130.0-beta.5
This commit is contained in:
daijro 2024-09-11 04:06:05 -05:00
parent ea84f792df
commit 021b2f895d
12 changed files with 164 additions and 17 deletions

1
.gitignore vendored
View file

@ -14,3 +14,4 @@ __pycache__/
*.pyc
wget-log
*.kate-swp
*.log

View file

@ -140,11 +140,13 @@ package-windows:
--fonts macos linux
run-launcher:
rm -rf $(cf_source_dir)/obj-x86_64-pc-linux-gnu/dist/bin/launch;
make build-launcher arch=x86_64 os=linux;
cp launcher/dist/launch $(cf_source_dir)/obj-x86_64-pc-linux-gnu/dist/bin/launch;
$(cf_source_dir)/obj-x86_64-pc-linux-gnu/dist/bin/launch
run-pw:
rm -rf $(cf_source_dir)/obj-x86_64-pc-linux-gnu/dist/bin/launch;
make build-launcher arch=x86_64 os=linux;
python3 scripts/run-pw.py \
--version $(version) \

View file

@ -571,6 +571,8 @@ class PageTarget {
// default viewport.
// Do not allow default viewport size if Camoufox set it first
const viewportSize = this._viewportSize || this._browserContext.defaultViewportSize;
if (
!viewportSize &&
this._browserContext.defaultViewportSize && (
@ -582,7 +584,6 @@ class PageTarget {
return;
}
const viewportSize = this._viewportSize || this._browserContext.defaultViewportSize;
if (viewportSize) {
const {width, height} = viewportSize;
this._linkedBrowser.style.setProperty('width', width + 'px');

View file

@ -131,15 +131,20 @@ 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;

View file

@ -564,15 +564,11 @@ class Frame {
webSocketService.removeListener(this._webSocketListenerInnerWindowId, this._webSocketListener);
this._webSocketListenerInnerWindowId = this.domWindow().windowGlobalChild.innerWindowId;
webSocketService.addListener(this._webSocketListenerInnerWindowId, this._webSocketListener);
// Camoufox: Causes leaks.
// for (const context of this._worldNameToContext.values())
// this._runtime.destroyExecutionContext(context);
// this._worldNameToContext.clear();
// this._worldNameToContext.set('', this._runtime.createExecutionContext(this.domWindow(), this.domWindow(), {
// frameId: this._frameId,
// name: '',
// }));
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('');
for (const [name, world] of this._frameTree._isolatedWorlds) {
if (name)
this._createIsolatedContext(name);

View file

@ -164,7 +164,7 @@ class Runtime {
emitEvent(this.events.onRuntimeError, {
executionContext,
message: message.errorMessage,
stack: message.stack.toString(),
stack: message.stack?.toString() || '',
});
}
},

View file

@ -6,6 +6,8 @@ const {protocol, checkScheme} = ChromeUtils.import("chrome://juggler/content/pro
const {Helper} = ChromeUtils.import('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 {
/**
@ -44,6 +46,11 @@ 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;
@ -86,6 +93,13 @@ 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`);
@ -136,3 +150,44 @@ class ProtocolSession {
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}]`;
}
}

View file

@ -67,7 +67,7 @@ func filterOutput(r io.Reader, w io.Writer) {
}
// Run Camoufox
func runCamoufox(execName string, args []string, addonsList []string) {
func runCamoufox(execName string, args []string, addonsList []string, stderrPath string) {
// If addons are specified, get the debug port
var debugPortInt int
if len(addonsList) > 0 {
@ -130,8 +130,18 @@ func runCamoufox(execName string, args []string, addonsList []string) {
done <- true
}()
go func() {
// If stderrPath is not empty, write to the file
fmt.Printf("Setting stderr to file: %s\n", stderrPath)
if stderrPath != "" {
file, err := os.Create(stderrPath)
if err != nil {
fmt.Printf("Error creating stderr file: %v\n", err)
os.Exit(1)
}
defer file.Close()
filterOutput(stderr, file)
}
filterOutput(stderr, os.Stderr)
done <- true
}()
<-done

View file

@ -18,6 +18,7 @@ func main() {
configPath := parseArgs("--config", "{}", &args, true)
addons := parseArgs("--addons", "[]", &args, true)
excludeAddons := parseArgs("--exclude-addons", "[]", &args, true)
stderrPath := parseArgs("--stderr", "", &args, true)
//*** PARSE CONFIG ***//
@ -26,6 +27,11 @@ func main() {
parseJson(configPath, &configMap)
validateConfig(configMap)
// Add "debug: True" to the config
if stderrPath != "" {
configMap["debug"] = true
}
//*** PARSE ADDONS ***//
// If addons are passed, handle them
@ -58,7 +64,7 @@ func main() {
fmt.Printf("Error setting executable permissions: %v\n", err)
os.Exit(1)
}
runCamoufox(execName, args, addonsList)
runCamoufox(execName, args, addonsList, stderrPath)
}
// Returns the absolute path relative to the launcher
@ -93,13 +99,20 @@ func parseArgs(param string, defaultValue string, args *[]string, removeFromArgs
return defaultValue
}
// fileExists checks if a file exists
func fileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
// Parses a JSON string or file into a map
func parseJson(argv string, target interface{}) {
// Unmarshal the config input into a map
var data []byte
var err error
// Check if the input is a file path or inline JSON
if _, err := os.Stat(argv); err == nil {
if fileExists(argv) {
data, err = os.ReadFile(argv)
if err != nil {
fmt.Printf("Error reading config file: %v\n", err)

63
patches/debug.patch Normal file
View file

@ -0,0 +1,63 @@
diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp
index 71d897a0d0..2234834f3e 100644
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -2069,6 +2069,25 @@ bool ChromeUtils::IsDarkBackground(GlobalObject&, Element& aElement) {
return nsNativeTheme::IsDarkBackground(f);
}
+/* static */
+void ChromeUtils::CamouDebug(GlobalObject& aGlobal,
+ const nsAString& aVarName) {
+ if (auto value = MaskConfig::GetBool("debug");
+ value.has_value() && !value.value()) {
+ return;
+ }
+ NS_ConvertUTF16toUTF8 utf8VarName(aVarName);
+ printf_stderr("DEBUG: %s\n", utf8VarName.get());
+}
+
+bool ChromeUtils::IsCamouDebug(GlobalObject& aGlobal) {
+ if (auto value = MaskConfig::GetBool("debug");
+ value.has_value() && value.value()) {
+ return true;
+ }
+ return false;
+}
+
double ChromeUtils::DateNow(GlobalObject&) { return JS_Now() / 1000.0; }
/* static */
diff --git a/dom/base/ChromeUtils.h b/dom/base/ChromeUtils.h
index 42b74131d6..9f151ca2e7 100644
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -301,6 +301,10 @@ class ChromeUtils {
static bool IsDarkBackground(GlobalObject&, Element&);
+ static void CamouDebug(GlobalObject& aGlobal, const nsAString& aVarName);
+
+ static bool IsCamouDebug(GlobalObject& aGlobal);
+
static double DateNow(GlobalObject&);
static void EnsureJSOracleStarted(GlobalObject&);
diff --git a/dom/chrome-webidl/ChromeUtils.webidl b/dom/chrome-webidl/ChromeUtils.webidl
index f761be86a4..c6409bd56e 100644
--- a/dom/chrome-webidl/ChromeUtils.webidl
+++ b/dom/chrome-webidl/ChromeUtils.webidl
@@ -746,6 +746,13 @@ partial namespace ChromeUtils {
*/
boolean isDarkBackground(Element element);
+ /**
+ * Camoufox debug commands
+ */
+ undefined camouDebug(DOMString varName);
+
+ boolean isCamouDebug();
+
/**
* Starts the JSOracle process for ORB JavaScript validation, if it hasn't started already.
*/

View file

@ -19,6 +19,7 @@ pref("media.peerconnection.ice.no_host", true);
// Force enable content isolation (WAFs can detect this!)
pref("fission.autostart", true);
pref("fission.webContentIsolationStrategy", 2);
// Use dark theme by default
pref("ui.systemUsesDarkTheme", 1);
@ -450,7 +451,7 @@ pref("datareporting.policy.dataSubmissionPolicyBypassNotification", true);
pref("network.auth.use_redirect_for_retries", false);
// Disable cross-process iframes, but not cross-process navigations.
pref("fission.webContentIsolationStrategy", 0);
// pref("fission.webContentIsolationStrategy", 0);
// Disable BFCache in parent process.
// We also separately disable BFCache in content via docSchell property.

View file

@ -1,2 +1,2 @@
version=130.0
release=beta.4
release=beta.5