Taking a screenshot with Firefox headless

My general experience with Selenium and Firefox running “headless” through xvfb has been quite poor. Last April Chrome support for a real headless options came out and it was with quite some pleasure that we ported part of our code to make use of it. Especially the component responsible to take screenshots. Using chrome headless has proved a solid choice for us, if it were not for the inability to use extensions.

So, recently, when I heard about Firefox 56 shipping the headless feature I was thrilled to try it and find out if it would support extensions. Currently Firefox 56 was available as a nightly release: https://www.mozilla.org/en-US/firefox/56.0a1/releasenotes/

Taking a screenshot

I am going to use selenium in ruby to take a screenshot. Other languages have a similar API so it should fairly simple to port.

First, let’s take a screenshot with firefox as we would do normally. Download the nightly build from the link above and unzip it in a location of your choice. In my case it will be in /home/eurico/firefox56.0a1/firefox. The path is important as you will see below.

Copy and paste the code below in a file take_screenshot.rb:

require 'selenium-webdriver'

# this step is necessary to make sure Selenium uses the nightly build we just downloaded
Selenium::WebDriver::Firefox::Binary.path='/home/eurico/firefox56.0a1/firefox'
driver = Selenium::WebDriver.for(:firefox)
driver.navigate.to("http://google.com")
driver.save_screenshot("headless_screenshot.png")
driver.quit

Except for loading firefox binary, that’s pretty much we would normally do with Selenium.
Now to run it with headless enabled, the only thing you need to do is to run it with the ENV variable: MOZ_HEADLESS=1.

1MOZ_HEADLESS=1 ruby take_screenshot.rb

Notice how a few seconds later you now have a screenshot in your directory. A flag, that’s all it took.

Enabling extensions

I’m not going to enable extensions one by one. Instead I suggest you to open your firefox and parameterize it the way you want. Once you’re done, your settings should be saved in a folder in your home directory. In Ubuntu that would be ~/.mozilla/firefox.

There you can see some weird names, e.g. rqo3tqtf.default. Ignore, the part before the ., the rest of the name, in this case default, is the name of your profile.

Back to our script, let’s had the following lines:

options = Selenium::WebDriver::Firefox::Options.new 
options.profile = "default" # same as the profile you found in ~/.mozilla/firefox
driver = Selenium::WebDriver.for(:firefox, options: options)

With this, our headless browser is now using the same settings we defined visually in firefox. You could set some adblocks and what not, they would (hopefully) be working as expected.

Simulating an iPhone

Let’s take it a step further and try to simulate an iPhone. To do this, we need to change the screen size and set the proper user agent. The user agent is set on the profile, but the to change the screen size we need to manipulate the window directly:

# same as above, load our profile
options.profile = "default" 
# alternatively, create a new one:
# options.profile = Selenium::WebDriver::Firefox::Profile.new

# set the user agent to an iPhone
options.profile['general.useragent.override'] = "Mozilla/5.0 (iPhone; CPU iPhone OS 10_2_1 like Mac OS X) AppleWebKit/602.4.6 (KHTML, like Gecko) Version/10.0 Mobile/14D27 Safari/602.1" 

# start the browser and change the window size
driver = Selenium::WebDriver.for(:firefox, options: options)
driver.manage.window.resize_to(375, 667) 

# take the screenshot as usual
driver.navigate.to("http://google.com")
driver.save_screenshot("headless_iphone.png")

Conclusion

We’ve seen how easy it is to now take screenshots from Firefox in headless mode.
Firefox 56 will be released officially in September 2017, so after that point we won’t need to fiddle around with the binary path.

While Chrome headless didn’t allow us to use extensions, in Firefox it’s not only possible, but it is also very easy to setup visually and load in our scripts.