Featured

Running Before the Page Runs

Ahead of the Page, Not After It

ExecuteJavaScript runs against a page that has already loaded. InjectScript registers a script that runs first, on every document from then on, before any of the site own code. That ordering is the whole point: after load a page has already read whatever it wanted to read, and anything a workflow does then is a reaction. Running first is what makes it possible to patch what a fingerprinter reads, hook fetch or XHR to record what a page asks for, stub out what hangs a workflow, or seed state before an app boots.

// registered before the browser exists. GPAL applies it the moment the engine

// answers, still ahead of the first navigation

browser

.InjectScript("window.__weWereHereFirst = true;")

.GoTo("https://www.example.com");


// or from a file, or several of them

browser.InjectScript((GPALFile)GPAL.File.WithFileName(@"C:scripts*.js"));

NOTE

Measured on nike.com across four engine and browser pairings, 2026-08-27. An injected script read window.fetch as native and 34 characters long on the blank document, then as 179 characters and not native once the page had loaded. Same browser, same run, seconds apart. The page wrapped fetch, and only a script that ran before the page could see the difference. A script evaluated after load reads 179 and has no way to tell whether the browser or the page put it there.

It Persists Until You Take It Off

A registered script is not a one-off. It runs on the first document and on every document after it, through navigations, new tabs and frames, until ClearInjectedScripts removes it. That is what separates it from ExecuteJavaScript, which runs once against the page in front of it and is gone. A workflow that wants a hook in place for a whole run registers once at the top and forgets about it.

browser.InjectScript(recorder) // in place for every page from here

.GoTo("https://www.example.com/one")

.GoTo("https://www.example.com/two") // still there

.ClearInjectedScripts() // and now it is not

.GoTo("https://www.example.com/three");

On Every Engine, By Different Roads

Selenium and Puppeteer register the script over the DevTools protocol. OttoMagic registers it through the browser extension, which needs no debug port and no driver, so it reaches a browser running on a real profile with real history and a real signed-in session. Verified on Chrome with all three engines and on Edge with OttoMagic, 2026-08-27. Firefox has no CDP and GPAL has no BiDi path yet, so it is the one combination this does not reach.

WARNING

The extension registers through the Chrome userScripts API, which Chrome keeps switched off until it is allowed for the extension by hand, per profile. A throwaway profile has never had it set, so on OttoMagic this is a named profile feature. See Allowing User Scripts on OttoMagic in Getting Started.

💬 Ask GPAL