OK, so the last post was a bit heavy. Er, detailed. The upshot is that FireWatir, a Ruby library for automating Firefox via the JSSh (JavaScript Shell) extension can get access to the same APIs used by JavaScript Firefox extensions, allowing for some very low level automation of Firefox. The example in that post showed how to send arbitrary extra headers when navigating to a page:
# Essentially a copy of goto(uri) but can pass headers through the request
# Pass a list of headers, e.g. [ "X-Forwarded-For: 10.15.142.22", "cheese: toast" ]
# (Note that we should also be able to post data through this mozilla method)
def goto_with_headers(url, headers)
#set_defaults()
get_window_number()
set_browser_document()
h = ""
headers.each {|value| h += "#{value}\\r\\n" }
# Load the given url.
jssh_command = "var headers ="
jssh_command += "Components.classes['@mozilla.org/io/string-input-stream;1']"
jssh_command += ".createInstance(Components.interfaces.nsIStringInputStream);"
jssh_command += " headers.setData(\"#{h}\", #{h.length});"
# first null is referrer, second is postData
jssh_command += " #{BROWSER_VAR}.webNavigation.loadURI(\"#{url}\", 0, null,”
jssh_command += "null, headers);"
$jssh_socket.send("#{jssh_command}\n", 0)
read_socket()
wait()
end
I thought it might be useful to show another example, without having to explain the background. If you’re interested in details, read the last post.
Right. Let’s automate Firefox’s preferences. Again, searching for what extensions do, they use the nsIPrefBranch interface to get and set preferences. We’ll do the same.
Instead of using the createInstance method to get the interface, we’re going to use getService (presumably because the preferences service object is already in memory, and we want to get a handle on that, rather than a new instance). In Javascript:
var prefs = Components.classes[\"@mozilla.org/preferences-service;1\"]
.getService(Components.interfaces.nsIPrefBranch);"
prefs.setBoolPref("app.update.enabled", true);
And the Ruby helper method from my version of FireWatir::Firefox (note the escaped quotes):
def set_bool_preference(key, value)
jssh_command = "var prefs = Components"
jssh_command += ".classes[\"@mozilla.org/preferences-service;1\"]"
jssh_command += ".getService(Components.interfaces.nsIPrefBranch);"
jssh_command += " prefs.setBoolPref(\"#{key}\", #{value});"
$jssh_socket.send("#{jssh_command}\n", 0)
read_socket()
end
Other preferences are stored as strings and as comma separated string values:
def set_string_preference(key, value)
jssh_command = "var prefs = Components"
jssh_command += ".classes[\"@mozilla.org/preferences-service;1\"]"
jssh_command += ".getService(Components.interfaces.nsIPrefBranch);"
jssh_command += " prefs.setCharPref(\"#{key}\", \"#{value}\");"
$jssh_socket.send("#{jssh_command}\n", 0)
read_socket()
end
def add_string_preference(key, value)
jssh_command = "var prefs = Components"
jssh_command += ".classes[\"@mozilla.org/preferences-service;1\"]"
jssh_command += ".getService(Components.interfaces.nsIPrefBranch);"
jssh_command += " var value = prefs.getCharPref(\"#{key}\");"
jssh_command += " if (value === '') { value = \"#{value}\"; } else {"
jssh_command += " var values = value.split(\",\");"
jssh_command += " if (values.indexOf(\"#{value}\") === -1) {"
jssh_command += " values.push(\"#{value}\");"
jssh_command += " }"
jssh_command += " value = values.join(\",\");"
jssh_command += " }"
jssh_command += " prefs.setCharPref(\"#{key}\", value);"
$jssh_socket.send("#{jssh_command}\n", 0)
read_socket()
end
These can be used to make sure that your test environment’s version of Firefox is correctly setup before using. And if you’re wondering what to use for “key”, then take a browse through Firefox’s about:config (then Google it to see what it does). Here’s what I’m using at work:
browser.set_bool_preference("browser.download.manager.showWhenStarting", false)
# Convert "/" to "\". Double escaped because we're putting it into a JS string
browser.set_string_preference("browser.download.dir",
default_download_dir.gsub(/\//, '\\\\\\'))
browser.add_string_preference("browser.helperApps.neverAsk.saveToDisk",
"application/zip")
browser.add_string_preference("browser.helperApps.neverAsk.saveToDisk",
"application/postscript")
browser.set_bool_preference("app.update.enabled", false)
browser.set_bool_preference("browser.search.update", false)
browser.set_bool_preference("extensions.update.enabled", false)
browser.set_bool_preference("browser.sessionstore.resume_from_crash", false)
browser.add_string_preference("network.automatic-ntlm-auth.trusted-uris",
"http://xxx.example.com")
browser.add_string_preference("network.automatic-ntlm-auth.trusted-uris",
"http://yyy.examplecom")
browser.add_string_preference("network.automatic-ntlm-auth.trusted-uris",
"http://zzz.example.com")
Most useful keys?
- network.automatic-ntlm-auth.trusted-uris. With this, I can add a domain to automatically get NTLM authentication, so I know my tests won’t get stuck at an authentication prompt.
- browser.helperApps.neverAsk.saveToDisk. I didn’t even know this one existed, but it’s great. This is a comma separated list of mime types that will always get saved to disk, so again, I know that I won’t get stuck at the confirm download prompt. (This is my favourite – previously, we’ve had to set these up by hand by actually downloading an instance of the file. I’m very happy to be automating this.)
Handy, eh?
One more post to go – let’s dive a bit deeper.