Really advanced FireWatir – XUL!

by Matt 23. December 2009 15:49

This is a belated follow up to my previous couple of posts about taking FireWatir (or rather Jssh) further than simply accessing and manipulating the DOM. They were following the train of thought that since Firefox extensions have very deep access to the internals of the browser; they are written in JavaScript; and given that Jssh is just an extension, then the code you get Jssh to execute is going to be executed in the extensions context, and should allow you to do anything an extension can do.

That lead to getting a hold of Mozilla XPCOM interfaces and doing all sorts of interesting stuff – adding custom headers, changing Firefox preferences, manipulating stored cookies, or whatever else you can think of.

And now we get to take this to its logical conclusion.

Extensions are written in JavaScript and can have user interfaces, so the UI must be scriptable. And that would be XUL (think Ghostbusters), the XML User Interface Language, according to Wikipedia. It’s an XML dialect, very much analogous to HTML, but focussed on the user interface, not the page.

And Firefox exposes a DOM to manipulate it. You can iterate over the open windows using Jssh’s getWindows() function and examine the location. If it happens to start “chrome://” then the document property of that window is an XUL DOM.

Here’s a bit of script to automate the download save dialog, given the window from getWindows():

save_dialog.document.getElementById("save").click();
save_dialog.document.documentElement.getButton("accept").click();

This finds the “save” radio button element, clicks it, then clicks the accept button. Looks just like scripting the HTML DOM, doesn’t it?

Go to the C:\Program Files\Mozilla Firefox\chrome folder. Make a copy of toolkit.jar and rename it to toolkit.zip and extract the contents. Go to content\mozapps\downloads and there you’ll find the downloads.xul file with accompanying scripts and css. This is the content required to display the file download dialog. Open it in an editor to get an idea of what the object model will be like. And if you check for the location of the file download dialog from the getWindows() function, it’s at chrome://mozapps/content/downloads – so it’s fairly easy to find the appropriate xul file for the given location.

Here’s the Ruby code that will automate the download dialog:

def save_file_download
  jssh_command = "var save_dialog=null;"
  jssh_command += "for (i=0;i<getWindows().length;i++)"
  jssh_command += "{"
  jssh_command += "  var window = getWindows()[i];"
  jssh_command += "  if (window.location.toString().substring(0, 35) "
  jssh_command += "    == \"chrome://mozapps/content/downloads/\")"
  jssh_command += "  { save_dialog = window; break; }"
  jssh_command += "}"
  jssh_command += "if (save_dialog != null) {";
  jssh_command += "  save_dialog.document.getElementById(\"save\").click();"
  jssh_command += "  var acceptButton = save_dialog.document."
  jssh_command += "    documentElement.getButton(\"accept\");"
  jssh_command += "  acceptButton.disabled=false;"
  jssh_command += "  acceptButton.click();"
  jssh_command += "  true;"
  jssh_command += "} else { false; }"
  $jssh_socket.send("#{jssh_command}\n", 0)
  result = read_socket()
end

One very important point to make here: there is only one carriage return. Jssh prints out a prompt for each carriage return, and you need to do a read_socket() for each. Save it, and just do it once. This took me a while to figure out.

In fact, you could do a lot worse than taking a look at Ethan’s work on JsshObject which is some very nifty Ruby meta-programming that allows you to effectively write the JavaScript directly in the Ruby code. Very cool.

(The very observant among you might notice that I’m enabling the button before I click it. For some reason, the accept button is disabled unless the window has focus. I really can’t think of a good reason for this.

And if you’ve been paying particularly close attention to these posts (barring the two month gap!), I don’t actually use this code. I use the add_string_preference function from last post to add the expected mime type to the list of browser.helperApps.neverAsk.saveToDisk)

But this is a good example of the technique. It shows that browser dialogs can be automated – so hopefully no more frozen tests while displaying a JavaScript alert!

Tags: , , ,

Rel=Me

Month List

RecentComments

Comment RSS