refactor(cef): use dumber as its own subprocess
- commit
- 521a0b3e59fe8fb50e0af3204d03441745b126de
- author
- brice · · 2026-04-19 07:03 UTC
- committer
- brice · · 2026-04-19 07:06 UTC
- parent
- 2eb5513
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
9 files · +160 -100
M .gitignore +0 -1
| @@ -8,7 +8,6 @@ dist/ | |||
| 8 | 8 | ||
| 9 | 9 | # Binaries for programs and plugins | |
| 10 | 10 | /dumber | |
| 11 | - | /cef-helper | |
| 12 | 11 | dumber.exe | |
| 13 | 12 | ||
| 14 | 13 | # Test binary, built with `go test -c` | |
| @@ -8,7 +8,6 @@ dist/ | |||||
| 8 | 8 | ||||
| 9 | # Binaries for programs and plugins | 9 | # Binaries for programs and plugins | ||
| 10 | /dumber | 10 | /dumber | ||
| 11 | - | /cef-helper | |||
| 12 | dumber.exe | 11 | dumber.exe | ||
| 13 | 12 | ||||
| 14 | # Test binary, built with `go test -c` | 13 | # Test binary, built with `go test -c` | ||
M Makefile +9 -10
| @@ -43,7 +43,7 @@ build: build-frontend ## Build the application (pure Go, no CGO) | |||
| 43 | 43 | @echo "Building $(BINARY_NAME) $(VERSION) using $(NPROCS) cores..." | |
| 44 | 44 | @mkdir -p $(DIST_DIR) | |
| 45 | 45 | CGO_ENABLED=0 go build -p $(NPROCS) $(GCFLAGS) $(LDFLAGS) -o $(DIST_DIR)/$(BINARY_NAME) $(MAIN_PATH) | |
| 46 | - | CGO_ENABLED=0 go build -p $(NPROCS) -ldflags "-s -w" -o $(DIST_DIR)/cef-helper ./cmd/cef-helper | |
| 46 | + | @rm -f $(DIST_DIR)/cef-helper | |
| 47 | 47 | @echo "Build successful! Binary: $(DIST_DIR)/$(BINARY_NAME)" | |
| 48 | 48 | ||
| 49 | 49 | build-frontend: ## Build homepage and error pages | |
| @@ -55,22 +55,21 @@ build-quick: ## Build without frontend (faster for backend development) | |||
| 55 | 55 | @echo "Building $(BINARY_NAME) $(VERSION) (quick, no frontend)..." | |
| 56 | 56 | @mkdir -p $(DIST_DIR) | |
| 57 | 57 | CGO_ENABLED=0 go build -p $(NPROCS) $(GCFLAGS) $(LDFLAGS) -o $(DIST_DIR)/$(BINARY_NAME) $(MAIN_PATH) | |
| 58 | - | CGO_ENABLED=0 go build -p $(NPROCS) -ldflags "-s -w" -o $(DIST_DIR)/cef-helper ./cmd/cef-helper | |
| 59 | - | @echo "Build successful! Binaries: $(DIST_DIR)/$(BINARY_NAME) and $(DIST_DIR)/cef-helper" | |
| 58 | + | @rm -f $(DIST_DIR)/cef-helper | |
| 59 | + | @echo "Build successful! Binary: $(DIST_DIR)/$(BINARY_NAME)" | |
| 60 | 60 | ||
| 61 | - | install-local: build-quick ## Install dumber + cef-helper to ~/.local/bin atomically | |
| 62 | - | @echo "Installing $(BINARY_NAME) and cef-helper to $(LOCAL_BIN_DIR)..." | |
| 61 | + | install-local: build-quick ## Install dumber to ~/.local/bin atomically | |
| 62 | + | @echo "Installing $(BINARY_NAME) to $(LOCAL_BIN_DIR)..." | |
| 63 | 63 | @mkdir -p $(LOCAL_BIN_DIR) | |
| 64 | 64 | @tmp_dumber="$$(mktemp '$(LOCAL_BIN_DIR)/.dumber.tmp.XXXXXX')"; \ | |
| 65 | - | tmp_helper="$$(mktemp '$(LOCAL_BIN_DIR)/.cef-helper.tmp.XXXXXX')"; \ | |
| 66 | - | trap 'rm -f "$$tmp_dumber" "$$tmp_helper"' EXIT INT TERM; \ | |
| 65 | + | trap 'rm -f "$$tmp_dumber"' EXIT INT TERM; \ | |
| 67 | 66 | install -m 0755 $(DIST_DIR)/$(BINARY_NAME) "$$tmp_dumber"; \ | |
| 68 | - | install -m 0755 $(DIST_DIR)/cef-helper "$$tmp_helper"; \ | |
| 69 | 67 | mv -f "$$tmp_dumber" $(LOCAL_BIN_DIR)/$(BINARY_NAME); \ | |
| 70 | - | mv -f "$$tmp_helper" $(LOCAL_BIN_DIR)/cef-helper; \ | |
| 68 | + | removed_stale=0; \ | |
| 69 | + | if [ -e $(LOCAL_BIN_DIR)/cef-helper ]; then rm -f $(LOCAL_BIN_DIR)/cef-helper && removed_stale=1; fi; \ | |
| 71 | 70 | trap - EXIT INT TERM; \ | |
| 72 | 71 | echo "Installed: $(LOCAL_BIN_DIR)/$(BINARY_NAME)"; \ | |
| 73 | - | echo "Installed: $(LOCAL_BIN_DIR)/cef-helper" | |
| 72 | + | if [ "$$removed_stale" -eq 1 ]; then echo "Removed stale: $(LOCAL_BIN_DIR)/cef-helper"; fi | |
| 74 | 73 | ||
| 75 | 74 | # Development targets | |
| 76 | 75 | dev: ## Run in development mode | |
| @@ -43,7 +43,7 @@ build: build-frontend ## Build the application (pure Go, no CGO) | |||||
| 43 | @echo "Building $(BINARY_NAME) $(VERSION) using $(NPROCS) cores..." | 43 | @echo "Building $(BINARY_NAME) $(VERSION) using $(NPROCS) cores..." | ||
| 44 | @mkdir -p $(DIST_DIR) | 44 | @mkdir -p $(DIST_DIR) | ||
| 45 | CGO_ENABLED=0 go build -p $(NPROCS) $(GCFLAGS) $(LDFLAGS) -o $(DIST_DIR)/$(BINARY_NAME) $(MAIN_PATH) | 45 | CGO_ENABLED=0 go build -p $(NPROCS) $(GCFLAGS) $(LDFLAGS) -o $(DIST_DIR)/$(BINARY_NAME) $(MAIN_PATH) | ||
| 46 | - | CGO_ENABLED=0 go build -p $(NPROCS) -ldflags "-s -w" -o $(DIST_DIR)/cef-helper ./cmd/cef-helper | 46 | + | @rm -f $(DIST_DIR)/cef-helper |
| 47 | @echo "Build successful! Binary: $(DIST_DIR)/$(BINARY_NAME)" | 47 | @echo "Build successful! Binary: $(DIST_DIR)/$(BINARY_NAME)" | ||
| 48 | 48 | ||||
| 49 | build-frontend: ## Build homepage and error pages | 49 | build-frontend: ## Build homepage and error pages | ||
| @@ -55,22 +55,21 @@ build-quick: ## Build without frontend (faster for backend development) | |||||
| 55 | @echo "Building $(BINARY_NAME) $(VERSION) (quick, no frontend)..." | 55 | @echo "Building $(BINARY_NAME) $(VERSION) (quick, no frontend)..." | ||
| 56 | @mkdir -p $(DIST_DIR) | 56 | @mkdir -p $(DIST_DIR) | ||
| 57 | CGO_ENABLED=0 go build -p $(NPROCS) $(GCFLAGS) $(LDFLAGS) -o $(DIST_DIR)/$(BINARY_NAME) $(MAIN_PATH) | 57 | CGO_ENABLED=0 go build -p $(NPROCS) $(GCFLAGS) $(LDFLAGS) -o $(DIST_DIR)/$(BINARY_NAME) $(MAIN_PATH) | ||
| 58 | - | CGO_ENABLED=0 go build -p $(NPROCS) -ldflags "-s -w" -o $(DIST_DIR)/cef-helper ./cmd/cef-helper | 58 | + | @rm -f $(DIST_DIR)/cef-helper |
| 59 | - | @echo "Build successful! Binaries: $(DIST_DIR)/$(BINARY_NAME) and $(DIST_DIR)/cef-helper" | 59 | + | @echo "Build successful! Binary: $(DIST_DIR)/$(BINARY_NAME)" |
| 60 | 60 | ||||
| 61 | - | install-local: build-quick ## Install dumber + cef-helper to ~/.local/bin atomically | 61 | + | install-local: build-quick ## Install dumber to ~/.local/bin atomically |
| 62 | - | @echo "Installing $(BINARY_NAME) and cef-helper to $(LOCAL_BIN_DIR)..." | 62 | + | @echo "Installing $(BINARY_NAME) to $(LOCAL_BIN_DIR)..." |
| 63 | @mkdir -p $(LOCAL_BIN_DIR) | 63 | @mkdir -p $(LOCAL_BIN_DIR) | ||
| 64 | @tmp_dumber="$$(mktemp '$(LOCAL_BIN_DIR)/.dumber.tmp.XXXXXX')"; \ | 64 | @tmp_dumber="$$(mktemp '$(LOCAL_BIN_DIR)/.dumber.tmp.XXXXXX')"; \ | ||
| 65 | - | tmp_helper="$$(mktemp '$(LOCAL_BIN_DIR)/.cef-helper.tmp.XXXXXX')"; \ | 65 | + | trap 'rm -f "$$tmp_dumber"' EXIT INT TERM; \ |
| 66 | - | trap 'rm -f "$$tmp_dumber" "$$tmp_helper"' EXIT INT TERM; \ | |||
| 67 | install -m 0755 $(DIST_DIR)/$(BINARY_NAME) "$$tmp_dumber"; \ | 66 | install -m 0755 $(DIST_DIR)/$(BINARY_NAME) "$$tmp_dumber"; \ | ||
| 68 | - | install -m 0755 $(DIST_DIR)/cef-helper "$$tmp_helper"; \ | |||
| 69 | mv -f "$$tmp_dumber" $(LOCAL_BIN_DIR)/$(BINARY_NAME); \ | 67 | mv -f "$$tmp_dumber" $(LOCAL_BIN_DIR)/$(BINARY_NAME); \ | ||
| 70 | - | mv -f "$$tmp_helper" $(LOCAL_BIN_DIR)/cef-helper; \ | 68 | + | removed_stale=0; \ |
| 69 | + | if [ -e $(LOCAL_BIN_DIR)/cef-helper ]; then rm -f $(LOCAL_BIN_DIR)/cef-helper && removed_stale=1; fi; \ | |||
| 71 | trap - EXIT INT TERM; \ | 70 | trap - EXIT INT TERM; \ | ||
| 72 | echo "Installed: $(LOCAL_BIN_DIR)/$(BINARY_NAME)"; \ | 71 | echo "Installed: $(LOCAL_BIN_DIR)/$(BINARY_NAME)"; \ | ||
| 73 | - | echo "Installed: $(LOCAL_BIN_DIR)/cef-helper" | 72 | + | if [ "$$removed_stale" -eq 1 ]; then echo "Removed stale: $(LOCAL_BIN_DIR)/cef-helper"; fi |
| 74 | 73 | ||||
| 75 | # Development targets | 74 | # Development targets | ||
| 76 | dev: ## Run in development mode | 75 | dev: ## Run in development mode | ||
D cmd/cef-helper/main.go +0 -33
| @@ -1,33 +0,0 @@ | |||
| 1 | - | // cef-helper is a minimal subprocess binary for CEF. | |
| 2 | - | // | |
| 3 | - | // CEF spawns renderer, GPU, and utility subprocesses by re-executing a binary | |
| 4 | - | // with --type=renderer/gpu-process/utility. In Go, the main dumber binary has | |
| 5 | - | // heavy initialization (config, DB, GTK) that corrupts process state before | |
| 6 | - | // CEF gets control. This dedicated helper binary calls cef_execute_process as | |
| 7 | - | // the very first thing and exits, avoiding that problem. | |
| 8 | - | // | |
| 9 | - | // Usage: set Settings.BrowserSubprocessPath to the path of this binary. | |
| 10 | - | package main | |
| 11 | - | ||
| 12 | - | import ( | |
| 13 | - | "os" | |
| 14 | - | "runtime" | |
| 15 | - | ||
| 16 | - | infracef "github.com/bnema/dumber/internal/infrastructure/cef" | |
| 17 | - | cef "github.com/bnema/purego-cef/cef" | |
| 18 | - | ) | |
| 19 | - | ||
| 20 | - | func main() { | |
| 21 | - | // Pin to the main OS thread — CEF requires it. | |
| 22 | - | runtime.LockOSThread() | |
| 23 | - | ||
| 24 | - | // cef_execute_process will handle the subprocess lifecycle and call | |
| 25 | - | // os.Exit internally if this is a valid CEF subprocess invocation. | |
| 26 | - | // We pass the lightweight subprocess app, which intentionally returns nil | |
| 27 | - | // from GetRenderProcessHandler(), so renderer-side bridges remain disabled | |
| 28 | - | // in helper subprocesses while the OSR startup regression is under repair. | |
| 29 | - | cef.MaybeExitSubprocessWithApp(infracef.NewSubprocessApp()) | |
| 30 | - | ||
| 31 | - | // If we get here, something went wrong. | |
| 32 | - | os.Exit(1) | |
| 33 | - | } | |
| @@ -1,33 +0,0 @@ | |||||
| 1 | - | // cef-helper is a minimal subprocess binary for CEF. | |||
| 2 | - | // | |||
| 3 | - | // CEF spawns renderer, GPU, and utility subprocesses by re-executing a binary | |||
| 4 | - | // with --type=renderer/gpu-process/utility. In Go, the main dumber binary has | |||
| 5 | - | // heavy initialization (config, DB, GTK) that corrupts process state before | |||
| 6 | - | // CEF gets control. This dedicated helper binary calls cef_execute_process as | |||
| 7 | - | // the very first thing and exits, avoiding that problem. | |||
| 8 | - | // | |||
| 9 | - | // Usage: set Settings.BrowserSubprocessPath to the path of this binary. | |||
| 10 | - | package main | |||
| 11 | - | ||||
| 12 | - | import ( | |||
| 13 | - | "os" | |||
| 14 | - | "runtime" | |||
| 15 | - | ||||
| 16 | - | infracef "github.com/bnema/dumber/internal/infrastructure/cef" | |||
| 17 | - | cef "github.com/bnema/purego-cef/cef" | |||
| 18 | - | ) | |||
| 19 | - | ||||
| 20 | - | func main() { | |||
| 21 | - | // Pin to the main OS thread — CEF requires it. | |||
| 22 | - | runtime.LockOSThread() | |||
| 23 | - | ||||
| 24 | - | // cef_execute_process will handle the subprocess lifecycle and call | |||
| 25 | - | // os.Exit internally if this is a valid CEF subprocess invocation. | |||
| 26 | - | // We pass the lightweight subprocess app, which intentionally returns nil | |||
| 27 | - | // from GetRenderProcessHandler(), so renderer-side bridges remain disabled | |||
| 28 | - | // in helper subprocesses while the OSR startup regression is under repair. | |||
| 29 | - | cef.MaybeExitSubprocessWithApp(infracef.NewSubprocessApp()) | |||
| 30 | - | ||||
| 31 | - | // If we get here, something went wrong. | |||
| 32 | - | os.Exit(1) | |||
| 33 | - | } | |||
M cmd/dumber/main.go +18 -10
| @@ -18,6 +18,7 @@ import ( | |||
| 18 | 18 | "github.com/bnema/dumber/internal/domain/entity" | |
| 19 | 19 | "github.com/bnema/dumber/internal/domain/repository" | |
| 20 | 20 | "github.com/bnema/dumber/internal/infrastructure/cache" | |
| 21 | + | infracef "github.com/bnema/dumber/internal/infrastructure/cef" | |
| 21 | 22 | "github.com/bnema/dumber/internal/infrastructure/clipboard" | |
| 22 | 23 | "github.com/bnema/dumber/internal/infrastructure/config" | |
| 23 | 24 | "github.com/bnema/dumber/internal/infrastructure/deps" | |
| @@ -117,19 +118,20 @@ func configureBrowserLaunchRelay(cfg *config.Config) { | |||
| 117 | 118 | } | |
| 118 | 119 | ||
| 119 | 120 | func main() { | |
| 120 | - | enableCrashForensics() | |
| 121 | - | ||
| 122 | - | // CEF subprocess handling: when CEF re-launches the binary with | |
| 121 | + | // CEF subprocess handling: when CEF re-launches this binary with | |
| 123 | 122 | // --type=renderer/gpu/etc, we must call ExecuteProcess before anything | |
| 124 | 123 | // else (Cobra, config, arg stripping). We detect subprocesses by the | |
| 125 | - | // presence of --type= in os.Args rather than checking the engine env | |
| 126 | - | // var, because subprocesses may not inherit the environment and the | |
| 124 | + | // presence of a --type flag in os.Args rather than checking the engine | |
| 125 | + | // env var, because subprocesses may not inherit the environment and the | |
| 127 | 126 | // engine can also be selected via config. | |
| 128 | - | if isCEFSubprocess() { | |
| 129 | - | cef.MaybeExitSubprocess() | |
| 127 | + | if isCEFSubprocess(os.Args) { | |
| 128 | + | runtime.LockOSThread() | |
| 129 | + | cef.MaybeExitSubprocessWithApp(infracef.NewSubprocessApp()) | |
| 130 | 130 | os.Exit(1) | |
| 131 | 131 | } | |
| 132 | 132 | ||
| 133 | + | enableCrashForensics() | |
| 134 | + | ||
| 133 | 135 | mode, browseURL := launchModeFromArgs(os.Args) | |
| 134 | 136 | // Run GUI mode for browse command | |
| 135 | 137 | if mode == launchModeBrowse { | |
| @@ -727,13 +729,19 @@ func createUseCases(repos *repositories, cfg *config.Config) *useCases { | |||
| 727 | 729 | } | |
| 728 | 730 | } | |
| 729 | 731 | ||
| 730 | - | // isCEFSubprocess returns true if os.Args contains a --type= flag, indicating | |
| 732 | + | // isCEFSubprocess returns true if args contains a --type flag, indicating | |
| 731 | 733 | // this process was spawned by CEF as a renderer, GPU, or utility subprocess. | |
| 732 | - | func isCEFSubprocess() bool { | |
| 733 | - | for _, arg := range os.Args { | |
| 734 | + | func isCEFSubprocess(args []string) bool { | |
| 735 | + | for i, arg := range args { | |
| 734 | 736 | if strings.HasPrefix(arg, "--type=") { | |
| 735 | 737 | return true | |
| 736 | 738 | } | |
| 739 | + | if arg == "--type" && i+1 < len(args) { | |
| 740 | + | next := args[i+1] | |
| 741 | + | if next != "" && !strings.HasPrefix(next, "-") { | |
| 742 | + | return true | |
| 743 | + | } | |
| 744 | + | } | |
| 737 | 745 | } | |
| 738 | 746 | return false | |
| 739 | 747 | } | |
| @@ -18,6 +18,7 @@ import ( | |||||
| 18 | "github.com/bnema/dumber/internal/domain/entity" | 18 | "github.com/bnema/dumber/internal/domain/entity" | ||
| 19 | "github.com/bnema/dumber/internal/domain/repository" | 19 | "github.com/bnema/dumber/internal/domain/repository" | ||
| 20 | "github.com/bnema/dumber/internal/infrastructure/cache" | 20 | "github.com/bnema/dumber/internal/infrastructure/cache" | ||
| 21 | + | infracef "github.com/bnema/dumber/internal/infrastructure/cef" | |||
| 21 | "github.com/bnema/dumber/internal/infrastructure/clipboard" | 22 | "github.com/bnema/dumber/internal/infrastructure/clipboard" | ||
| 22 | "github.com/bnema/dumber/internal/infrastructure/config" | 23 | "github.com/bnema/dumber/internal/infrastructure/config" | ||
| 23 | "github.com/bnema/dumber/internal/infrastructure/deps" | 24 | "github.com/bnema/dumber/internal/infrastructure/deps" | ||
| @@ -117,19 +118,20 @@ func configureBrowserLaunchRelay(cfg *config.Config) { | |||||
| 117 | } | 118 | } | ||
| 118 | 119 | ||||
| 119 | func main() { | 120 | func main() { | ||
| 120 | - | enableCrashForensics() | 121 | + | // CEF subprocess handling: when CEF re-launches this binary with |
| 121 | - | ||||
| 122 | - | // CEF subprocess handling: when CEF re-launches the binary with | |||
| 123 | // --type=renderer/gpu/etc, we must call ExecuteProcess before anything | 122 | // --type=renderer/gpu/etc, we must call ExecuteProcess before anything | ||
| 124 | // else (Cobra, config, arg stripping). We detect subprocesses by the | 123 | // else (Cobra, config, arg stripping). We detect subprocesses by the | ||
| 125 | - | // presence of --type= in os.Args rather than checking the engine env | 124 | + | // presence of a --type flag in os.Args rather than checking the engine |
| 126 | - | // var, because subprocesses may not inherit the environment and the | 125 | + | // env var, because subprocesses may not inherit the environment and the |
| 127 | // engine can also be selected via config. | 126 | // engine can also be selected via config. | ||
| 128 | - | if isCEFSubprocess() { | 127 | + | if isCEFSubprocess(os.Args) { |
| 129 | - | cef.MaybeExitSubprocess() | 128 | + | runtime.LockOSThread() |
| 129 | + | cef.MaybeExitSubprocessWithApp(infracef.NewSubprocessApp()) | |||
| 130 | os.Exit(1) | 130 | os.Exit(1) | ||
| 131 | } | 131 | } | ||
| 132 | 132 | ||||
| 133 | + | enableCrashForensics() | |||
| 134 | + | ||||
| 133 | mode, browseURL := launchModeFromArgs(os.Args) | 135 | mode, browseURL := launchModeFromArgs(os.Args) | ||
| 134 | // Run GUI mode for browse command | 136 | // Run GUI mode for browse command | ||
| 135 | if mode == launchModeBrowse { | 137 | if mode == launchModeBrowse { | ||
| @@ -727,13 +729,19 @@ func createUseCases(repos *repositories, cfg *config.Config) *useCases { | |||||
| 727 | } | 729 | } | ||
| 728 | } | 730 | } | ||
| 729 | 731 | ||||
| 730 | - | // isCEFSubprocess returns true if os.Args contains a --type= flag, indicating | 732 | + | // isCEFSubprocess returns true if args contains a --type flag, indicating |
| 731 | // this process was spawned by CEF as a renderer, GPU, or utility subprocess. | 733 | // this process was spawned by CEF as a renderer, GPU, or utility subprocess. | ||
| 732 | - | func isCEFSubprocess() bool { | 734 | + | func isCEFSubprocess(args []string) bool { |
| 733 | - | for _, arg := range os.Args { | 735 | + | for i, arg := range args { |
| 734 | if strings.HasPrefix(arg, "--type=") { | 736 | if strings.HasPrefix(arg, "--type=") { | ||
| 735 | return true | 737 | return true | ||
| 736 | } | 738 | } | ||
| 739 | + | if arg == "--type" && i+1 < len(args) { | |||
| 740 | + | next := args[i+1] | |||
| 741 | + | if next != "" && !strings.HasPrefix(next, "-") { | |||
| 742 | + | return true | |||
| 743 | + | } | |||
| 744 | + | } | |||
| 737 | } | 745 | } | ||
| 738 | return false | 746 | return false | ||
| 739 | } | 747 | } | ||
M cmd/dumber/main_test.go +42 -0
| @@ -72,6 +72,48 @@ func TestLaunchModeFromArgs_OmniboxFlagFallsBackToCLI(t *testing.T) { | |||
| 72 | 72 | } | |
| 73 | 73 | } | |
| 74 | 74 | ||
| 75 | + | func TestIsCEFSubprocess(t *testing.T) { | |
| 76 | + | tests := []struct { | |
| 77 | + | name string | |
| 78 | + | args []string | |
| 79 | + | want bool | |
| 80 | + | }{ | |
| 81 | + | { | |
| 82 | + | name: "equals form", | |
| 83 | + | args: []string{"dumber", "--type=renderer"}, | |
| 84 | + | want: true, | |
| 85 | + | }, | |
| 86 | + | { | |
| 87 | + | name: "separate value form", | |
| 88 | + | args: []string{"dumber", "--type", "renderer"}, | |
| 89 | + | want: true, | |
| 90 | + | }, | |
| 91 | + | { | |
| 92 | + | name: "bare type", | |
| 93 | + | args: []string{"dumber", "--type"}, | |
| 94 | + | want: false, | |
| 95 | + | }, | |
| 96 | + | { | |
| 97 | + | name: "type followed by flag", | |
| 98 | + | args: []string{"dumber", "--type", "--unexpected"}, | |
| 99 | + | want: false, | |
| 100 | + | }, | |
| 101 | + | { | |
| 102 | + | name: "no type flag", | |
| 103 | + | args: []string{"dumber", "--unexpected"}, | |
| 104 | + | want: false, | |
| 105 | + | }, | |
| 106 | + | } | |
| 107 | + | ||
| 108 | + | for _, tc := range tests { | |
| 109 | + | t.Run(tc.name, func(t *testing.T) { | |
| 110 | + | if got := isCEFSubprocess(tc.args); got != tc.want { | |
| 111 | + | t.Fatalf("expected %v, got %v", tc.want, got) | |
| 112 | + | } | |
| 113 | + | }) | |
| 114 | + | } | |
| 115 | + | } | |
| 116 | + | ||
| 75 | 117 | func TestResolveCurrentExecutable_ReturnsExecutablePath(t *testing.T) { | |
| 76 | 118 | got, err := resolveCurrentExecutable(func() (string, error) { | |
| 77 | 119 | return "/usr/bin/dumber", nil | |
| @@ -72,6 +72,48 @@ func TestLaunchModeFromArgs_OmniboxFlagFallsBackToCLI(t *testing.T) { | |||||
| 72 | } | 72 | } | ||
| 73 | } | 73 | } | ||
| 74 | 74 | ||||
| 75 | + | func TestIsCEFSubprocess(t *testing.T) { | |||
| 76 | + | tests := []struct { | |||
| 77 | + | name string | |||
| 78 | + | args []string | |||
| 79 | + | want bool | |||
| 80 | + | }{ | |||
| 81 | + | { | |||
| 82 | + | name: "equals form", | |||
| 83 | + | args: []string{"dumber", "--type=renderer"}, | |||
| 84 | + | want: true, | |||
| 85 | + | }, | |||
| 86 | + | { | |||
| 87 | + | name: "separate value form", | |||
| 88 | + | args: []string{"dumber", "--type", "renderer"}, | |||
| 89 | + | want: true, | |||
| 90 | + | }, | |||
| 91 | + | { | |||
| 92 | + | name: "bare type", | |||
| 93 | + | args: []string{"dumber", "--type"}, | |||
| 94 | + | want: false, | |||
| 95 | + | }, | |||
| 96 | + | { | |||
| 97 | + | name: "type followed by flag", | |||
| 98 | + | args: []string{"dumber", "--type", "--unexpected"}, | |||
| 99 | + | want: false, | |||
| 100 | + | }, | |||
| 101 | + | { | |||
| 102 | + | name: "no type flag", | |||
| 103 | + | args: []string{"dumber", "--unexpected"}, | |||
| 104 | + | want: false, | |||
| 105 | + | }, | |||
| 106 | + | } | |||
| 107 | + | ||||
| 108 | + | for _, tc := range tests { | |||
| 109 | + | t.Run(tc.name, func(t *testing.T) { | |||
| 110 | + | if got := isCEFSubprocess(tc.args); got != tc.want { | |||
| 111 | + | t.Fatalf("expected %v, got %v", tc.want, got) | |||
| 112 | + | } | |||
| 113 | + | }) | |||
| 114 | + | } | |||
| 115 | + | } | |||
| 116 | + | ||||
| 75 | func TestResolveCurrentExecutable_ReturnsExecutablePath(t *testing.T) { | 117 | func TestResolveCurrentExecutable_ReturnsExecutablePath(t *testing.T) { | ||
| 76 | got, err := resolveCurrentExecutable(func() (string, error) { | 118 | got, err := resolveCurrentExecutable(func() (string, error) { | ||
| 77 | return "/usr/bin/dumber", nil | 119 | return "/usr/bin/dumber", nil | ||
M internal/infrastructure/cef/app.go +12 -4
| @@ -56,10 +56,10 @@ func newDumberApp(engine *Engine) purecef.App { | |||
| 56 | 56 | return app | |
| 57 | 57 | } | |
| 58 | 58 | ||
| 59 | - | // NewSubprocessApp returns a lightweight raw App implementation for helper | |
| 60 | - | // processes so cef_execute_process can wrap it itself. Keep helper processes | |
| 61 | - | // free of custom render handlers for now: the renderer bridge introduced a | |
| 62 | - | // startup regression where OSR pages stopped producing their first paint. | |
| 59 | + | // NewSubprocessApp returns a lightweight raw App implementation for CEF | |
| 60 | + | // subprocesses re-executed from the main dumber binary. Keep subprocesses free | |
| 61 | + | // of custom render handlers for now: the renderer bridge introduced a startup | |
| 62 | + | // regression where OSR pages stopped producing their first paint. | |
| 63 | 63 | func NewSubprocessApp() purecef.App { | |
| 64 | 64 | return &subprocessApp{} | |
| 65 | 65 | } | |
| @@ -122,6 +122,7 @@ func (h *dumberBPH) OnBeforeChildProcessLaunch(commandLine purecef.CommandLine) | |||
| 122 | 122 | useAngle := "" | |
| 123 | 123 | ozonePlatform := "" | |
| 124 | 124 | if commandLine != nil { | |
| 125 | + | appendSwitchIfMissing(commandLine, "no-zygote") | |
| 125 | 126 | processType = commandLine.GetSwitchValue("type") | |
| 126 | 127 | commandLineString = commandLine.GetCommandLineString() | |
| 127 | 128 | useAngle = commandLine.GetSwitchValue("use-angle") | |
| @@ -130,6 +131,13 @@ func (h *dumberBPH) OnBeforeChildProcessLaunch(commandLine purecef.CommandLine) | |||
| 130 | 131 | h.engine.recordChildProcessLaunch(processType, useAngle, ozonePlatform, commandLineString) | |
| 131 | 132 | } | |
| 132 | 133 | ||
| 134 | + | func appendSwitchIfMissing(commandLine purecef.CommandLine, name string) { | |
| 135 | + | if commandLine == nil || commandLine.HasSwitch(name) { | |
| 136 | + | return | |
| 137 | + | } | |
| 138 | + | commandLine.AppendSwitch(name) | |
| 139 | + | } | |
| 140 | + | ||
| 133 | 141 | func parseRelaunchCommandLineArgs(commandLine string) []string { | |
| 134 | 142 | args := make([]string, 0, 4) | |
| 135 | 143 | var current strings.Builder | |
| @@ -56,10 +56,10 @@ func newDumberApp(engine *Engine) purecef.App { | |||||
| 56 | return app | 56 | return app | ||
| 57 | } | 57 | } | ||
| 58 | 58 | ||||
| 59 | - | // NewSubprocessApp returns a lightweight raw App implementation for helper | 59 | + | // NewSubprocessApp returns a lightweight raw App implementation for CEF |
| 60 | - | // processes so cef_execute_process can wrap it itself. Keep helper processes | 60 | + | // subprocesses re-executed from the main dumber binary. Keep subprocesses free |
| 61 | - | // free of custom render handlers for now: the renderer bridge introduced a | 61 | + | // of custom render handlers for now: the renderer bridge introduced a startup |
| 62 | - | // startup regression where OSR pages stopped producing their first paint. | 62 | + | // regression where OSR pages stopped producing their first paint. |
| 63 | func NewSubprocessApp() purecef.App { | 63 | func NewSubprocessApp() purecef.App { | ||
| 64 | return &subprocessApp{} | 64 | return &subprocessApp{} | ||
| 65 | } | 65 | } | ||
| @@ -122,6 +122,7 @@ func (h *dumberBPH) OnBeforeChildProcessLaunch(commandLine purecef.CommandLine) | |||||
| 122 | useAngle := "" | 122 | useAngle := "" | ||
| 123 | ozonePlatform := "" | 123 | ozonePlatform := "" | ||
| 124 | if commandLine != nil { | 124 | if commandLine != nil { | ||
| 125 | + | appendSwitchIfMissing(commandLine, "no-zygote") | |||
| 125 | processType = commandLine.GetSwitchValue("type") | 126 | processType = commandLine.GetSwitchValue("type") | ||
| 126 | commandLineString = commandLine.GetCommandLineString() | 127 | commandLineString = commandLine.GetCommandLineString() | ||
| 127 | useAngle = commandLine.GetSwitchValue("use-angle") | 128 | useAngle = commandLine.GetSwitchValue("use-angle") | ||
| @@ -130,6 +131,13 @@ func (h *dumberBPH) OnBeforeChildProcessLaunch(commandLine purecef.CommandLine) | |||||
| 130 | h.engine.recordChildProcessLaunch(processType, useAngle, ozonePlatform, commandLineString) | 131 | h.engine.recordChildProcessLaunch(processType, useAngle, ozonePlatform, commandLineString) | ||
| 131 | } | 132 | } | ||
| 132 | 133 | ||||
| 134 | + | func appendSwitchIfMissing(commandLine purecef.CommandLine, name string) { | |||
| 135 | + | if commandLine == nil || commandLine.HasSwitch(name) { | |||
| 136 | + | return | |||
| 137 | + | } | |||
| 138 | + | commandLine.AppendSwitch(name) | |||
| 139 | + | } | |||
| 140 | + | ||||
| 133 | func parseRelaunchCommandLineArgs(commandLine string) []string { | 141 | func parseRelaunchCommandLineArgs(commandLine string) []string { | ||
| 134 | args := make([]string, 0, 4) | 142 | args := make([]string, 0, 4) | ||
| 135 | var current strings.Builder | 143 | var current strings.Builder | ||
M internal/infrastructure/cef/app_test.go +78 -21
| @@ -1,6 +1,7 @@ | |||
| 1 | 1 | package cef | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | + | "context" | |
| 4 | 5 | "testing" | |
| 5 | 6 | "unsafe" | |
| 6 | 7 | ||
| @@ -24,27 +25,56 @@ type relaunchCommandLineStub struct { | |||
| 24 | 25 | commandLineString string | |
| 25 | 26 | } | |
| 26 | 27 | ||
| 27 | - | func (c relaunchCommandLineStub) IsValid() bool { return true } | |
| 28 | - | func (c relaunchCommandLineStub) IsReadOnly() bool { return true } | |
| 29 | - | func (c relaunchCommandLineStub) Copy() purecef.CommandLine { return c } | |
| 30 | - | func (c relaunchCommandLineStub) InitFromArgv(int32, unsafe.Pointer) {} | |
| 31 | - | func (c relaunchCommandLineStub) InitFromString(string) {} | |
| 32 | - | func (c relaunchCommandLineStub) Reset() {} | |
| 33 | - | func (c relaunchCommandLineStub) GetArgv(uintptr) {} | |
| 34 | - | func (c relaunchCommandLineStub) GetCommandLineString() string { return c.commandLineString } | |
| 35 | - | func (c relaunchCommandLineStub) GetProgram() string { return "dumber" } | |
| 36 | - | func (c relaunchCommandLineStub) SetProgram(string) {} | |
| 37 | - | func (c relaunchCommandLineStub) HasSwitches() bool { return false } | |
| 38 | - | func (c relaunchCommandLineStub) HasSwitch(string) bool { return false } | |
| 39 | - | func (c relaunchCommandLineStub) GetSwitchValue(string) string { return "" } | |
| 40 | - | func (c relaunchCommandLineStub) GetSwitches(uintptr) {} | |
| 41 | - | func (c relaunchCommandLineStub) AppendSwitch(string) {} | |
| 42 | - | func (c relaunchCommandLineStub) AppendSwitchWithValue(string, string) {} | |
| 43 | - | func (c relaunchCommandLineStub) HasArguments() bool { return true } | |
| 44 | - | func (c relaunchCommandLineStub) GetArguments(uintptr) {} | |
| 45 | - | func (c relaunchCommandLineStub) AppendArgument(string) {} | |
| 46 | - | func (c relaunchCommandLineStub) PrependWrapper(string) {} | |
| 47 | - | func (c relaunchCommandLineStub) RemoveSwitch(string) {} | |
| 28 | + | func (c relaunchCommandLineStub) IsValid() bool { return true } | |
| 29 | + | func (c relaunchCommandLineStub) IsReadOnly() bool { return true } | |
| 30 | + | func (c relaunchCommandLineStub) Copy() purecef.CommandLine { return c } | |
| 31 | + | func (c relaunchCommandLineStub) InitFromArgv(int32, unsafe.Pointer) {} | |
| 32 | + | func (c relaunchCommandLineStub) InitFromString(string) {} | |
| 33 | + | func (c relaunchCommandLineStub) Reset() {} | |
| 34 | + | func (c relaunchCommandLineStub) GetArgv(uintptr) {} | |
| 35 | + | func (c relaunchCommandLineStub) GetCommandLineString() string { return c.commandLineString } | |
| 36 | + | func (c relaunchCommandLineStub) GetProgram() string { return "dumber" } | |
| 37 | + | func (c relaunchCommandLineStub) SetProgram(string) {} | |
| 38 | + | func (c relaunchCommandLineStub) HasSwitches() bool { return false } | |
| 39 | + | func (c relaunchCommandLineStub) HasSwitch(string) bool { return false } | |
| 40 | + | func (c relaunchCommandLineStub) GetSwitchValue(string) string { return "" } | |
| 41 | + | func (c relaunchCommandLineStub) GetSwitches(uintptr) {} | |
| 42 | + | func (c relaunchCommandLineStub) AppendSwitch(string) {} | |
| 43 | + | func (c relaunchCommandLineStub) AppendSwitchWithValue(string, string) { | |
| 44 | + | } | |
| 45 | + | func (c relaunchCommandLineStub) HasArguments() bool { return true } | |
| 46 | + | func (c relaunchCommandLineStub) GetArguments(uintptr) {} | |
| 47 | + | func (c relaunchCommandLineStub) AppendArgument(string) { | |
| 48 | + | } | |
| 49 | + | func (c relaunchCommandLineStub) PrependWrapper(string) {} | |
| 50 | + | func (c relaunchCommandLineStub) RemoveSwitch(string) {} | |
| 51 | + | ||
| 52 | + | type mutableCommandLineStub struct { | |
| 53 | + | relaunchCommandLineStub | |
| 54 | + | switches map[string]string | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | func newMutableCommandLineStub() *mutableCommandLineStub { | |
| 58 | + | return &mutableCommandLineStub{switches: make(map[string]string)} | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | func (c *mutableCommandLineStub) IsReadOnly() bool { return false } | |
| 62 | + | func (c *mutableCommandLineStub) Copy() purecef.CommandLine { return c } | |
| 63 | + | func (c *mutableCommandLineStub) Reset() { c.switches = make(map[string]string) } | |
| 64 | + | func (c *mutableCommandLineStub) HasSwitches() bool { return len(c.switches) > 0 } | |
| 65 | + | func (c *mutableCommandLineStub) HasSwitch(name string) bool { | |
| 66 | + | _, ok := c.switches[name] | |
| 67 | + | return ok | |
| 68 | + | } | |
| 69 | + | func (c *mutableCommandLineStub) GetSwitchValue(name string) string { return c.switches[name] } | |
| 70 | + | func (c *mutableCommandLineStub) AppendSwitch(name string) { c.switches[name] = "" } | |
| 71 | + | func (c *mutableCommandLineStub) AppendSwitchWithValue(name, value string) { | |
| 72 | + | c.switches[name] = value | |
| 73 | + | } | |
| 74 | + | func (c *mutableCommandLineStub) HasArguments() bool { return false } | |
| 75 | + | func (c *mutableCommandLineStub) RemoveSwitch(name string) { | |
| 76 | + | delete(c.switches, name) | |
| 77 | + | } | |
| 48 | 78 | ||
| 49 | 79 | func TestRegisterDumbScheme(t *testing.T) { | |
| 50 | 80 | t.Parallel() | |
| @@ -143,3 +173,30 @@ func TestDumberBPH_OnAlreadyRunningAppRelaunch_DoesNotForwardNonBrowse(t *testin | |||
| 143 | 173 | t.Fatal("handler should not be called for non-browse relaunch") | |
| 144 | 174 | } | |
| 145 | 175 | } | |
| 176 | + | ||
| 177 | + | func TestConfigureCommandLine_AppendsExpectedSwitches(t *testing.T) { | |
| 178 | + | t.Parallel() | |
| 179 | + | ||
| 180 | + | commandLine := newMutableCommandLineStub() | |
| 181 | + | configureCommandLine(commandLine) | |
| 182 | + | ||
| 183 | + | if !commandLine.HasSwitch("enable-smooth-scrolling") { | |
| 184 | + | t.Fatal("expected enable-smooth-scrolling switch to be appended") | |
| 185 | + | } | |
| 186 | + | if got := commandLine.GetSwitchValue("autoplay-policy"); got != "no-user-gesture-required" { | |
| 187 | + | t.Fatalf("autoplay-policy = %q, want %q", got, "no-user-gesture-required") | |
| 188 | + | } | |
| 189 | + | } | |
| 190 | + | ||
| 191 | + | func TestDumberBPH_OnBeforeChildProcessLaunch_AppendsNoZygote(t *testing.T) { | |
| 192 | + | t.Parallel() | |
| 193 | + | ||
| 194 | + | commandLine := newMutableCommandLineStub() | |
| 195 | + | commandLine.AppendSwitchWithValue("type", "renderer") | |
| 196 | + | ||
| 197 | + | (&dumberBPH{engine: &Engine{ctx: context.Background()}}).OnBeforeChildProcessLaunch(commandLine) | |
| 198 | + | ||
| 199 | + | if !commandLine.HasSwitch("no-zygote") { | |
| 200 | + | t.Fatal("expected no-zygote switch to be appended for child processes") | |
| 201 | + | } | |
| 202 | + | } | |
| @@ -1,6 +1,7 @@ | |||||
| 1 | package cef | 1 | package cef | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | + | "context" | |||
| 4 | "testing" | 5 | "testing" | ||
| 5 | "unsafe" | 6 | "unsafe" | ||
| 6 | 7 | ||||
| @@ -24,27 +25,56 @@ type relaunchCommandLineStub struct { | |||||
| 24 | commandLineString string | 25 | commandLineString string | ||
| 25 | } | 26 | } | ||
| 26 | 27 | ||||
| 27 | - | func (c relaunchCommandLineStub) IsValid() bool { return true } | 28 | + | func (c relaunchCommandLineStub) IsValid() bool { return true } |
| 28 | - | func (c relaunchCommandLineStub) IsReadOnly() bool { return true } | 29 | + | func (c relaunchCommandLineStub) IsReadOnly() bool { return true } |
| 29 | - | func (c relaunchCommandLineStub) Copy() purecef.CommandLine { return c } | 30 | + | func (c relaunchCommandLineStub) Copy() purecef.CommandLine { return c } |
| 30 | - | func (c relaunchCommandLineStub) InitFromArgv(int32, unsafe.Pointer) {} | 31 | + | func (c relaunchCommandLineStub) InitFromArgv(int32, unsafe.Pointer) {} |
| 31 | - | func (c relaunchCommandLineStub) InitFromString(string) {} | 32 | + | func (c relaunchCommandLineStub) InitFromString(string) {} |
| 32 | - | func (c relaunchCommandLineStub) Reset() {} | 33 | + | func (c relaunchCommandLineStub) Reset() {} |
| 33 | - | func (c relaunchCommandLineStub) GetArgv(uintptr) {} | 34 | + | func (c relaunchCommandLineStub) GetArgv(uintptr) {} |
| 34 | - | func (c relaunchCommandLineStub) GetCommandLineString() string { return c.commandLineString } | 35 | + | func (c relaunchCommandLineStub) GetCommandLineString() string { return c.commandLineString } |
| 35 | - | func (c relaunchCommandLineStub) GetProgram() string { return "dumber" } | 36 | + | func (c relaunchCommandLineStub) GetProgram() string { return "dumber" } |
| 36 | - | func (c relaunchCommandLineStub) SetProgram(string) {} | 37 | + | func (c relaunchCommandLineStub) SetProgram(string) {} |
| 37 | - | func (c relaunchCommandLineStub) HasSwitches() bool { return false } | 38 | + | func (c relaunchCommandLineStub) HasSwitches() bool { return false } |
| 38 | - | func (c relaunchCommandLineStub) HasSwitch(string) bool { return false } | 39 | + | func (c relaunchCommandLineStub) HasSwitch(string) bool { return false } |
| 39 | - | func (c relaunchCommandLineStub) GetSwitchValue(string) string { return "" } | 40 | + | func (c relaunchCommandLineStub) GetSwitchValue(string) string { return "" } |
| 40 | - | func (c relaunchCommandLineStub) GetSwitches(uintptr) {} | 41 | + | func (c relaunchCommandLineStub) GetSwitches(uintptr) {} |
| 41 | - | func (c relaunchCommandLineStub) AppendSwitch(string) {} | 42 | + | func (c relaunchCommandLineStub) AppendSwitch(string) {} |
| 42 | - | func (c relaunchCommandLineStub) AppendSwitchWithValue(string, string) {} | 43 | + | func (c relaunchCommandLineStub) AppendSwitchWithValue(string, string) { |
| 43 | - | func (c relaunchCommandLineStub) HasArguments() bool { return true } | 44 | + | } |
| 44 | - | func (c relaunchCommandLineStub) GetArguments(uintptr) {} | 45 | + | func (c relaunchCommandLineStub) HasArguments() bool { return true } |
| 45 | - | func (c relaunchCommandLineStub) AppendArgument(string) {} | 46 | + | func (c relaunchCommandLineStub) GetArguments(uintptr) {} |
| 46 | - | func (c relaunchCommandLineStub) PrependWrapper(string) {} | 47 | + | func (c relaunchCommandLineStub) AppendArgument(string) { |
| 47 | - | func (c relaunchCommandLineStub) RemoveSwitch(string) {} | 48 | + | } |
| 49 | + | func (c relaunchCommandLineStub) PrependWrapper(string) {} | |||
| 50 | + | func (c relaunchCommandLineStub) RemoveSwitch(string) {} | |||
| 51 | + | ||||
| 52 | + | type mutableCommandLineStub struct { | |||
| 53 | + | relaunchCommandLineStub | |||
| 54 | + | switches map[string]string | |||
| 55 | + | } | |||
| 56 | + | ||||
| 57 | + | func newMutableCommandLineStub() *mutableCommandLineStub { | |||
| 58 | + | return &mutableCommandLineStub{switches: make(map[string]string)} | |||
| 59 | + | } | |||
| 60 | + | ||||
| 61 | + | func (c *mutableCommandLineStub) IsReadOnly() bool { return false } | |||
| 62 | + | func (c *mutableCommandLineStub) Copy() purecef.CommandLine { return c } | |||
| 63 | + | func (c *mutableCommandLineStub) Reset() { c.switches = make(map[string]string) } | |||
| 64 | + | func (c *mutableCommandLineStub) HasSwitches() bool { return len(c.switches) > 0 } | |||
| 65 | + | func (c *mutableCommandLineStub) HasSwitch(name string) bool { | |||
| 66 | + | _, ok := c.switches[name] | |||
| 67 | + | return ok | |||
| 68 | + | } | |||
| 69 | + | func (c *mutableCommandLineStub) GetSwitchValue(name string) string { return c.switches[name] } | |||
| 70 | + | func (c *mutableCommandLineStub) AppendSwitch(name string) { c.switches[name] = "" } | |||
| 71 | + | func (c *mutableCommandLineStub) AppendSwitchWithValue(name, value string) { | |||
| 72 | + | c.switches[name] = value | |||
| 73 | + | } | |||
| 74 | + | func (c *mutableCommandLineStub) HasArguments() bool { return false } | |||
| 75 | + | func (c *mutableCommandLineStub) RemoveSwitch(name string) { | |||
| 76 | + | delete(c.switches, name) | |||
| 77 | + | } | |||
| 48 | 78 | ||||
| 49 | func TestRegisterDumbScheme(t *testing.T) { | 79 | func TestRegisterDumbScheme(t *testing.T) { | ||
| 50 | t.Parallel() | 80 | t.Parallel() | ||
| @@ -143,3 +173,30 @@ func TestDumberBPH_OnAlreadyRunningAppRelaunch_DoesNotForwardNonBrowse(t *testin | |||||
| 143 | t.Fatal("handler should not be called for non-browse relaunch") | 173 | t.Fatal("handler should not be called for non-browse relaunch") | ||
| 144 | } | 174 | } | ||
| 145 | } | 175 | } | ||
| 176 | + | ||||
| 177 | + | func TestConfigureCommandLine_AppendsExpectedSwitches(t *testing.T) { | |||
| 178 | + | t.Parallel() | |||
| 179 | + | ||||
| 180 | + | commandLine := newMutableCommandLineStub() | |||
| 181 | + | configureCommandLine(commandLine) | |||
| 182 | + | ||||
| 183 | + | if !commandLine.HasSwitch("enable-smooth-scrolling") { | |||
| 184 | + | t.Fatal("expected enable-smooth-scrolling switch to be appended") | |||
| 185 | + | } | |||
| 186 | + | if got := commandLine.GetSwitchValue("autoplay-policy"); got != "no-user-gesture-required" { | |||
| 187 | + | t.Fatalf("autoplay-policy = %q, want %q", got, "no-user-gesture-required") | |||
| 188 | + | } | |||
| 189 | + | } | |||
| 190 | + | ||||
| 191 | + | func TestDumberBPH_OnBeforeChildProcessLaunch_AppendsNoZygote(t *testing.T) { | |||
| 192 | + | t.Parallel() | |||
| 193 | + | ||||
| 194 | + | commandLine := newMutableCommandLineStub() | |||
| 195 | + | commandLine.AppendSwitchWithValue("type", "renderer") | |||
| 196 | + | ||||
| 197 | + | (&dumberBPH{engine: &Engine{ctx: context.Background()}}).OnBeforeChildProcessLaunch(commandLine) | |||
| 198 | + | ||||
| 199 | + | if !commandLine.HasSwitch("no-zygote") { | |||
| 200 | + | t.Fatal("expected no-zygote switch to be appended for child processes") | |||
| 201 | + | } | |||
| 202 | + | } | |||
M internal/infrastructure/cef/engine_init.go +0 -21
| @@ -153,12 +153,6 @@ func prepareCEFSettings( | |||
| 153 | 153 | if cfg.LogSeverity != 0 { | |
| 154 | 154 | settings.LogSeverity = cfg.LogSeverity | |
| 155 | 155 | } | |
| 156 | - | if helperPath := findHelperBinary(); helperPath != "" { | |
| 157 | - | settings.BrowserSubprocessPath = helperPath | |
| 158 | - | logger.Debug().Str("path", helperPath).Msg("cef: using subprocess helper") | |
| 159 | - | } else { | |
| 160 | - | logger.Warn().Msg("cef: subprocess helper not found, falling back to main binary") | |
| 161 | - | } | |
| 162 | 156 | return settings, nil | |
| 163 | 157 | } | |
| 164 | 158 | ||
| @@ -413,21 +407,6 @@ func cleanStaleSingletonLocks(logger *zerolog.Logger, dir string) { | |||
| 413 | 407 | } | |
| 414 | 408 | } | |
| 415 | 409 | ||
| 416 | - | // findHelperBinary looks for the cef-helper binary next to the running | |
| 417 | - | // executable. Returns empty string if not found. | |
| 418 | - | func findHelperBinary() string { | |
| 419 | - | exe, err := os.Executable() | |
| 420 | - | if err != nil { | |
| 421 | - | return "" | |
| 422 | - | } | |
| 423 | - | dir := filepath.Dir(exe) | |
| 424 | - | helper := filepath.Join(dir, "cef-helper") | |
| 425 | - | if _, err := os.Stat(helper); err == nil { | |
| 426 | - | return helper | |
| 427 | - | } | |
| 428 | - | return "" | |
| 429 | - | } | |
| 430 | - | ||
| 431 | 410 | const ( | |
| 432 | 411 | dirPerm = 0o700 | |
| 433 | 412 | filePerm = 0o600 | |
| @@ -153,12 +153,6 @@ func prepareCEFSettings( | |||||
| 153 | if cfg.LogSeverity != 0 { | 153 | if cfg.LogSeverity != 0 { | ||
| 154 | settings.LogSeverity = cfg.LogSeverity | 154 | settings.LogSeverity = cfg.LogSeverity | ||
| 155 | } | 155 | } | ||
| 156 | - | if helperPath := findHelperBinary(); helperPath != "" { | |||
| 157 | - | settings.BrowserSubprocessPath = helperPath | |||
| 158 | - | logger.Debug().Str("path", helperPath).Msg("cef: using subprocess helper") | |||
| 159 | - | } else { | |||
| 160 | - | logger.Warn().Msg("cef: subprocess helper not found, falling back to main binary") | |||
| 161 | - | } | |||
| 162 | return settings, nil | 156 | return settings, nil | ||
| 163 | } | 157 | } | ||
| 164 | 158 | ||||
| @@ -413,21 +407,6 @@ func cleanStaleSingletonLocks(logger *zerolog.Logger, dir string) { | |||||
| 413 | } | 407 | } | ||
| 414 | } | 408 | } | ||
| 415 | 409 | ||||
| 416 | - | // findHelperBinary looks for the cef-helper binary next to the running | |||
| 417 | - | // executable. Returns empty string if not found. | |||
| 418 | - | func findHelperBinary() string { | |||
| 419 | - | exe, err := os.Executable() | |||
| 420 | - | if err != nil { | |||
| 421 | - | return "" | |||
| 422 | - | } | |||
| 423 | - | dir := filepath.Dir(exe) | |||
| 424 | - | helper := filepath.Join(dir, "cef-helper") | |||
| 425 | - | if _, err := os.Stat(helper); err == nil { | |||
| 426 | - | return helper | |||
| 427 | - | } | |||
| 428 | - | return "" | |||
| 429 | - | } | |||
| 430 | - | ||||
| 431 | const ( | 410 | const ( | ||
| 432 | dirPerm = 0o700 | 411 | dirPerm = 0o700 | ||
| 433 | filePerm = 0o600 | 412 | filePerm = 0o600 | ||
M internal/infrastructure/cef/engine_init_test.go +1 -0
| @@ -42,6 +42,7 @@ func TestPrepareCEFSettings_UsesResolvedProfilePaths(t *testing.T) { | |||
| 42 | 42 | require.NoError(t, err) | |
| 43 | 43 | require.Equal(t, profile.CEFUserDataDir(), settings.RootCachePath) | |
| 44 | 44 | require.Equal(t, profile.CEFLogFile(), settings.LogFile) | |
| 45 | + | require.Empty(t, settings.BrowserSubprocessPath) | |
| 45 | 46 | } | |
| 46 | 47 | ||
| 47 | 48 | func TestPrepareCEFSettings_RejectsNonDefaultCookiePolicy(t *testing.T) { | |
| @@ -42,6 +42,7 @@ func TestPrepareCEFSettings_UsesResolvedProfilePaths(t *testing.T) { | |||||
| 42 | require.NoError(t, err) | 42 | require.NoError(t, err) | ||
| 43 | require.Equal(t, profile.CEFUserDataDir(), settings.RootCachePath) | 43 | require.Equal(t, profile.CEFUserDataDir(), settings.RootCachePath) | ||
| 44 | require.Equal(t, profile.CEFLogFile(), settings.LogFile) | 44 | require.Equal(t, profile.CEFLogFile(), settings.LogFile) | ||
| 45 | + | require.Empty(t, settings.BrowserSubprocessPath) | |||
| 45 | } | 46 | } | ||
| 46 | 47 | ||||
| 47 | func TestPrepareCEFSettings_RejectsNonDefaultCookiePolicy(t *testing.T) { | 48 | func TestPrepareCEFSettings_RejectsNonDefaultCookiePolicy(t *testing.T) { | ||