|
| 1 | +package main |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + "os" |
| 6 | + "os/exec" |
| 7 | + "strings" |
| 8 | + "time" |
| 9 | +) |
| 10 | + |
| 11 | +// applyHeadlessDefaultFlags mirrors the legacy headless wrapper.sh: when |
| 12 | +// CHROMIUM_FLAGS is unset, fill in a curated headless+stealth flag list. |
| 13 | +// --disable-background-networking is intentionally omitted: it prevents |
| 14 | +// Chrome from fetching ExtensionInstallForcelist managed extensions. |
| 15 | +func applyHeadlessDefaultFlags() { |
| 16 | + if strings.TrimSpace(os.Getenv("CHROMIUM_FLAGS")) != "" { |
| 17 | + return |
| 18 | + } |
| 19 | + flags := strings.Join([]string{ |
| 20 | + "--accept-lang=en-US,en", |
| 21 | + "--allow-pre-commit-input", |
| 22 | + "--blink-settings=primaryHoverType=2,availableHoverTypes=2,primaryPointerType=4,availablePointerTypes=4", |
| 23 | + "--crash-dumps-dir=/tmp/chromium-dumps", |
| 24 | + "--disable-back-forward-cache", |
| 25 | + "--disable-background-timer-throttling", |
| 26 | + "--disable-backgrounding-occluded-windows", |
| 27 | + "--disable-blink-features=AutomationControlled", |
| 28 | + "--disable-breakpad", |
| 29 | + "--disable-client-side-phishing-detection", |
| 30 | + "--disable-component-extensions-with-background-pages", |
| 31 | + "--disable-crash-reporter", |
| 32 | + "--disable-crashpad", |
| 33 | + "--disable-dev-shm-usage", |
| 34 | + "--disable-features=AcceptCHFrame,AutoExpandDetailsElement,AvoidUnnecessaryBeforeUnloadCheckSync,CertificateTransparencyComponentUpdater,DeferRendererTasksAfterInput,DestroyProfileOnBrowserClose,DialMediaRouteProvider,ExtensionManifestV2Disabled,GlobalMediaControls,HttpsUpgrades,ImprovedCookieControls,LazyFrameLoading,LensOverlay,MediaRouter,PaintHolding,ThirdPartyStoragePartitioning,Translate", |
| 35 | + "--disable-field-trial-config", |
| 36 | + "--disable-gcm-registration", |
| 37 | + "--disable-gpu", |
| 38 | + "--disable-gpu-compositing", |
| 39 | + "--disable-hang-monitor", |
| 40 | + "--disable-ipc-flooding-protection", |
| 41 | + "--disable-notifications", |
| 42 | + "--disable-popup-blocking", |
| 43 | + "--disable-prompt-on-repost", |
| 44 | + "--disable-renderer-backgrounding", |
| 45 | + "--disable-search-engine-choice-screen", |
| 46 | + "--disable-software-rasterizer", |
| 47 | + "--enable-use-zoom-for-dsf=false", |
| 48 | + "--export-tagged-pdf", |
| 49 | + "--force-color-profile=srgb", |
| 50 | + "--hide-crash-restore-bubble", |
| 51 | + "--hide-scrollbars", |
| 52 | + "--metrics-recording-only", |
| 53 | + "--mute-audio", |
| 54 | + "--no-default-browser-check", |
| 55 | + "--no-first-run", |
| 56 | + "--no-sandbox", |
| 57 | + "--no-service-autorun", |
| 58 | + "--ozone-platform=headless", |
| 59 | + "--password-store=basic", |
| 60 | + "--unsafely-disable-devtools-self-xss-warnings", |
| 61 | + "--use-angle=swiftshader", |
| 62 | + "--use-gl=angle", |
| 63 | + "--use-mock-keychain", |
| 64 | + }, " ") |
| 65 | + _ = os.Setenv("CHROMIUM_FLAGS", flags) |
| 66 | +} |
| 67 | + |
| 68 | +// dismissNoSandboxWarning replicates the wrapper.sh behaviour of clicking the |
| 69 | +// "X" on the --no-sandbox infobar. Cosmetic; runs off the hot path. |
| 70 | +func dismissNoSandboxWarning() { |
| 71 | + out, err := exec.Command("xdotool", "getdisplaygeometry").Output() |
| 72 | + if err != nil { |
| 73 | + return |
| 74 | + } |
| 75 | + parts := strings.Fields(strings.TrimSpace(string(out))) |
| 76 | + if len(parts) != 2 { |
| 77 | + return |
| 78 | + } |
| 79 | + width := parts[0] |
| 80 | + x := width |
| 81 | + if w := atoi(width); w > 30 { |
| 82 | + x = fmt.Sprintf("%d", w-30) |
| 83 | + } |
| 84 | + target := "New Tab - Chromium" |
| 85 | + deadline := time.Now().Add(30 * time.Second) |
| 86 | + for time.Now().Before(deadline) { |
| 87 | + out, err := exec.Command("xdotool", "search", "--name", target).Output() |
| 88 | + if err == nil && len(strings.TrimSpace(string(out))) > 0 { |
| 89 | + id := strings.Fields(string(out))[0] |
| 90 | + if exec.Command("xdotool", "windowactivate", "--sync", id).Run() == nil { |
| 91 | + break |
| 92 | + } |
| 93 | + } |
| 94 | + time.Sleep(100 * time.Millisecond) |
| 95 | + } |
| 96 | + // Without a settle delay the click can land before the --no-sandbox infobar |
| 97 | + // has finished painting, leaving the warning on screen. The legacy |
| 98 | + // wrapper.sh slept 5s here for the same reason. Runs off the hot path |
| 99 | + // (goroutine fired post-readiness) so this doesn't extend time-to-CDP. |
| 100 | + time.Sleep(5 * time.Second) |
| 101 | + port := os.Getenv("KERNEL_IMAGES_API_PORT") |
| 102 | + if port == "" { |
| 103 | + port = defaultAPIPort |
| 104 | + } |
| 105 | + body := fmt.Sprintf(`{"x":%s,"y":115}`, x) |
| 106 | + _ = exec.Command("curl", "-s", "-o", "/dev/null", "-X", "POST", |
| 107 | + "http://localhost:"+port+"/computer/click_mouse", |
| 108 | + "-H", "Content-Type: application/json", |
| 109 | + "-d", body).Run() |
| 110 | +} |
| 111 | + |
| 112 | +func atoi(s string) int { |
| 113 | + n := 0 |
| 114 | + for _, c := range s { |
| 115 | + if c < '0' || c > '9' { |
| 116 | + return 0 |
| 117 | + } |
| 118 | + n = n*10 + int(c-'0') |
| 119 | + } |
| 120 | + return n |
| 121 | +} |
0 commit comments