Firefox automation hangs with stealth plugin while Chrome works fine

I’m running into a weird issue where my automation script works perfectly with Chrome but gets stuck when using Firefox. I have a Java application that calls a Node.js script to do some web scraping.

Java Code

Here’s my Java method that runs the Node script:

public void runNodeScript(){
    try{
        System.out.println(scriptCommand);
        final long begin = System.currentTimeMillis();
        final Process proc = Runtime.getRuntime().exec(scriptCommand);
        System.out.println("WAITING FOR SCRIPT TO COMPLETE"); 
        final Stream<String> output = new BufferedReader(new InputStreamReader(proc.getInputStream())).lines().peek(System.out::println);
        System.out.println("Phase--->A"); 
        final List<String> results = output.collect(Collectors.toList());
        System.out.println("Phase--->B"); 
        final long finish = System.currentTimeMillis();
        if(results.isEmpty()){
            System.out.println("no output received in: "+(finish-begin)+" ms");
        } else {                
            System.out.println("received lines: " + results.size()+" in: "+(finish-begin)+" ms");
            results.forEach(System.out::println);
        }            
    } catch(final Exception ex){
        ex.printStackTrace();
    }
}

The script path looks like:

private final String scriptCommand = "node C:\\Users\\myuser\\Desktop\\FirefoxTest.js";

Working Chrome Version

This Node.js script works great with Chrome:

const { chromium } = require('playwright-extra')
const stealthPlugin = require('puppeteer-extra-plugin-stealth')()

chromium.use(stealthPlugin)

chromium.launch({ headless: false }).then(async browserInstance => {
  const newPage = await browserInstance.newPage()
  
  console.log('Starting stealth test...')
  await newPage.goto('https://bot.sannysoft.com', { waitUntil: 'networkidle' })
  await newPage.screenshot({ path: 'result.png', fullPage: true })
  
  console.log('Test completed successfully')
  await browserInstance.close()
})

Problematic Firefox Version

But when I switch to Firefox, it never completes:

const { firefox } = require('playwright-extra')
const stealthPlugin = require('puppeteer-extra-plugin-stealth')()

firefox.use(stealthPlugin)

firefox.launch({ headless: false }).then(async browserInstance => {
  const newPage = await browserInstance.newPage()
  
  console.log('Starting stealth test...')
  await newPage.goto('https://bot.sannysoft.com', { waitUntil: 'networkidle' })
  await newPage.screenshot({ path: 'result.png', fullPage: true })
  
  console.log('Test completed successfully')
  await browserInstance.close()
})

The Firefox version launches two browser windows instead of one and gets stuck at the page navigation step. The page loads but the script never moves past the goto command even though the network appears idle. When I run this same Firefox script directly from command line it works fine, but not when called from Java.

Anyone know why Firefox behaves differently here or how to fix this hanging issue?

I ran into the same thing when mixing Firefox automation with Java processes. Firefox handles process management differently than Chrome when launched from a child process - it spawns extra background processes that don’t properly tell the parent Java process when they’re done.

Try adding explicit timeout configs to your Firefox launch options and use waitUntil: 'domcontentloaded' instead of 'networkidle'. Firefox’s network idle detection is pretty unreliable in headless mode. Also bump up your Java process memory since Firefox is way more resource-hungry than Chrome for automation.

What actually worked for me was setting up a heartbeat system - the Node script writes status updates to a temp file that Java monitors, instead of waiting for the process stream to finish.

The stealth plugin doesn’t integrate well with Firefox when invoked from Java. I’ve encountered similar issues where the plugin causes multiple windows to open due to its improper initialization in this context. Firefox’s subprocess communication differs from Chrome’s, particularly under stealth modifications. The plugin creates additional processes that disrupt the expected parent-child relationship in your Java code. Consider removing the stealth plugin for Firefox and implement detection evasion through modified user agents and viewport adjustments. If stealth features are essential, consider running Firefox automation as a separate service or daemon that your Java application communicates with via HTTP, thereby avoiding process management complications.

Firefox + playwright-extra is buggy when you call it from Java runtime. The dual window thing happens because Firefox spawns separate profile processes that don’t shut down properly. Quick fix - add args: ['--no-remote'] to your Firefox launch options and switch from networkidle to load event. Took me weeks to figure this out but it worked.