A Weird Imagination

Scripting control of web browser

Posted in

The problem#

Previously, I showed how to get Firefox to show just the web content without any of the window borders or toolbars. But there's an obvious problem: those UI elements are actually useful for doing things with the browser. We can give a single URL as an argument when we start the browser, and that may be good enough for some use-cases, but what if we want to have more control over what the browser is displaying?

The solution#

Firefox has a feature for running automated tests called Marionette which we can use for automating Firefox outside of the context of running tests. There's an official Python client:

$ pip install marionette_driver
$ firefox --marionette &
$ python
>>> from marionette_driver.marionette import Marionette
>>> client = Marionette('localhost', port=2828)
>>> client.start_session()
{'browserName': 'firefox', ... }
>>> client.navigate('https://example.com/')

If it works, you should see Firefox load the URL https://example.com/.

You can find more information on the available commands on the basics page and the documentation.

The details#

Running Firefox#

If Firefox is already running, then firefox --marionette will ignore the --marionette part and just open a new window in your existing Firefox session. You can use -no-remote to tell Firefox to not connect to an existing session. You likely also want to use -P PROFILE_NAME to specify a different profile from your default one:

firefox --marionette -no-remote -P "Other Profile"

Connecting to Firefox#

I had some trouble getting Marionette to connect in a script where I started Firefox in the background and immediately tried to connect to it. While I can't currently manage to recreate the issue, I ended up putting in a few seconds of time.sleep() before the call to client.start_session() to fix it. Now that I'm trying to recreate the error, it seems like the timing doesn't matter and, in fact, it works to run client.start_session() before starting Firefox and it will block until it finds a Firefox instance to connect to.

Do note that only one Marionette client can be connected at a time. If another tries to connect, it will block until the first disconnects.

Useful commands#

Here's some useful commands for managing Firefox windows:

The following code will generate a list of all windows with their handles, positions, and URLs:

original_window = client.current_window_handle
tabs = []
for handle in client.window_handles:
    client.switch_to_window(handle)
    tabs.append({
        "handle": handle,
        "rect": client.window_rect,
        "url": client.get_url(),
    })
client.switch_to_window(original_window)
# do something with tabs

Comments

Have something to add? Post a comment by sending an email to comments@aweirdimagination.net. You may use Markdown for formatting.

There are no comments yet.