<!-- m -->http://www.autohotke...topic46974.html<!-- m -->
and
<!-- m -->http://www.autohotke...topic53134.html<!-- m -->
I present here an alternative method which I have not found discussed previously. The method works without needing extensions installed and thus may be suitable for applications where assumptions cannot be made about what extensions are available - for example system administration. However it may not be as powerful as extension-based methods and is not presented as a better solution, rather as an alternative that may be more suited to certain applications.
The method is based on the following observations:
1. Firefox is somewhat resistant to automation with AHK. It does not expose UI elements in a way that AHK can recognise. Any method that interacts with the Firefox UI by, for example, sending key clicks is doing so 'blind' as it cannot directly see the results. It must therefore reduce the assumptions about what is happening in the UI to a minimum. This method only interacts with the location bar (or the Open Web Location dialog if the location bar is unavailable) and the window title.
2. Javascript can be run on the current page by using a commandlet. This is just prepending the javascript with 'javascript:' and entering it in the location bar.
3. The window title can be changed using javascript. It can be set to the value of a variable, DOM node or the return value of a function. As the window title can easily be read using AHK, this is a way that information can be passed out of Firefox.
4. All dialogs and other elements of the Firefox UI can be opened as if they are web pages using urls such as chrome://browser/content/places/places.xul. This includes the main window itself (chrome://browser/content/browser.xul).
5. chrome:// locations are xml documents. They have a DOM, contain javascript functions and can be interacted with using javascript/commandlets exactly as if they are a web page.
Using these observations it is possible to see how a deep penetration in to the Firefox UI can be achieved within a minimal 'footprint' of assumptions about what we can interact with directly from AHK.
I have written a set of utility functions that will interact with Firefox on this basis:
SetTitleMatchMode RegEx
firefoxString = Mozilla Firefox ; The string that appears at the end of the window title
GroupAdd, firefoxWindow, .*%firefoxString% ahk_class MozillaWindowClass ; Firefox 4
GroupAdd, firefoxWindow, .*%firefoxString% ahk_class MozillaUIWindowClass ; Firefox 3. See notes at firefox_open(url)
GroupAdd, firefoxDialog, ahk_class MozillaDialogClass ; This will however also affect dialogs from eg. Thunderbird
GroupAdd, firefoxOpenDialog, Open Web Location ahk_class MozillaDialogClass ; Appears if the location toolbar is hidden
firefoxUsesOpenDialog := false ; This will be set to true if Firefox is using the Open Web Location dialog instead of the location toolbar
; Start an instance of the Firefox program, if one doesn't exist already
firefox_start()
{
IfWinExist ahk_group firefoxWindow
{
firefox_checkUsesOpenDialog()
Return ; An instance already exists so no need to create another one
}
Process, Exist, firefox.exe
If ErrorLevel
{
WinWait, ahk_group firefoxWindow, , 30 ; Firefox may be starting up so wait for the window to open
If ErrorLevel
{
Process, Exist, firefox.exe ; Check if the process is still there - maybe it closed
If ErrorLevel
{
Process, Close, firefox.exe ; Firefox is probably stuck so zap it
If ErrorLevel = 0
firefox_throw("Firefox process appears to be stuck")
}
}
Else
{
firefox_checkUsesOpenDialog()
Return ; Firefox is now open
}
}
RegRead, version, HKLM, SOFTWARE\Mozilla\Mozilla Firefox, CurrentVersion ; Get Firefox version
RegRead, path, HKLM, SOFTWARE\Mozilla\Mozilla Firefox\%version%\Main, PathToExe ; Get Firefox exe location
If ErrorLevel
{
firefox_throw("Could not find Firefox installation")
}
Run %path% about:blank ; Start Firefox with a blank page
count = 0
Loop ; Loop until Firefox window opens
{
Sleep 50
WinClose ahk_group firefoxDialog ; Close any annoying dialogs that might want to get in the way
IfWinExist ahk_group firefoxWindow
{
Break
}
count += 1
If count > 600 ; Allow approx thirty seconds to open
{
firefox_throw("Could not start Firefox")
}
}
firefox_checkUsesOpenDialog()
}
; Close all instances of Firefox
firefox_close()
{
WinClose ahk_group firefoxDialog
count = 0
Loop ; Loop until all tabs are closed. This avoids dialogs about closing multiple tabs.
{
IfWinExist ahk_group firefoxWindow
{
BlockInput On
WinActivate
SendPlay ^w ; Closes a tab
BlockInput Off
Sleep 100
}
Else
{
Break
}
count += 1
if count > 100
{
Break
}
}
Process, WaitClose, firefox.exe, 10 ; Give process time to exit normally
Loop ; Loop until all remaining processes are killed
{
Process, Close, firefox.exe
If ErrorLevel = 0
{
Break
}
}
}
; Test to see if Firefox is using the Open Web Location dialog
firefox_checkUsesOpenDialog()
{
global firefoxUsesOpenDialog
BlockInput On
WinActivate ahk_group firefoxWindow
SendPlay {F6}^l
WinWait, ahk_group firefoxOpenDialog, , 2
If ErrorLevel = 0
{
firefoxUsesOpenDialog := true
WinClose ahk_group firefoxOpenDialog
}
BlockInput Off
}
; Open the url given or use the text as a search term. The url can be a chrome
; location to access parts of the user interface, eg:
; chrome://browser/content/browser.xul - the main browser window*
; chrome://browser/content/pageinfo/pageInfo.xul - page info**
; chrome://browser/content/places/places.xul - bookmarks and history
; chrome://browser/content/preferences/preferences.xul - preferences and settings
; chrome://browser/content/sanitize.xul - clear private information
; chrome://global/content/console.xul - error console*
; chrome://global/content/customizeToolbar.xul - customise toolbar**
; chrome://global/content/viewSource.xul - view source**
; chrome://mozapps/content/downloads/downloads.xul - download management
; chrome://mozapps/content/extensions/extensions.xul - add-on and extension management
; chrome://mozapps/content/update/updates.xul - check for updates to Firefox
; Locations marked with * will capture F6 in Firefox 3 and so will not work correctly
; with this function after they are opened.
; Locations marked with ** will not function normally without being opened with
; an appropriate context.
firefox_open(url)
{
global firefoxUsesOpenDialog
IfWinNotExist ahk_group firefoxWindow
firefox_throw("Firefox window not found")
WinClose ahk_group firefoxDialog ; Close any annoying dialogs that might want to get in the way
BlockInput On
ClipSaved := ClipboardAll
Clipboard := url
WinActivate ahk_group firefoxWindow
; F6 ensures topmost window is selected if eg chrome://browser/content/browser.xul
; has been opened (But see note about Firefox 3 above). ^l selects the location bar.
; ^v pastes from clipboard. SendPlay is more reliable for Firefox than SendInput.
SendPlay {F6}^l
If firefoxUsesOpenDialog
{
WinWait ahk_group firefoxOpenDialog
WinActivate
SendPlay ^v{enter}
}
Else
{
SendPlay ^v{enter}
}
Clipboard := ClipSaved
ClipSaved =
BlockInput Off
}
; Execute the command as javascript in the current page, eg:
; firefox_execute("document.body.innerHTML = '<H1>Hello World!</H1>'")
; The command is wrapped in an anonymous function to prevent the content
; of the page being changed to the result of the command. If that is the
; behaviour you want, use firefox_open to execute a commandlet.
firefox_execute(command)
{
command := "javascript:(function(){" . command . "})();"
firefox_open(command)
}
; Return the value of a DOM node or a javascript function, eg:
; foo := firefox_query("document.width")
; bar := firefox_query("Date()")
; Because this uses document.title to retrieve the value, you can't use this
; method to query document.title itself. You could use WinGetTitle after
; loading the page instead.
firefox_query(node)
{
global firefoxString
Random, rnd, 1000000000, 2147483647 ; First set the title bar to something random
command := "document.title='" . rnd . "'" ; to make sure that when we then change it to
firefox_execute(command) ; the value we actually want, we know that we
count = 0 ; aren't just getting the previous value.
loop ; Loop until the title bar is what we set it to
{
Sleep 50
WinGetTitle, title, ahk_group firefoxWindow
If title = %rnd% - %firefoxString%
{
Break
}
count += 1
If count > 100 ; Allow approx five seconds
{
firefox_throw("Could not complete firefox_execute()")
}
}
command := "document.title=" . node
firefox_execute(command)
count = 0
loop ; Loop until the title bar changes from what we set it to earlier
{
Sleep 50
WinGetTitle, title, ahk_group firefoxWindow
If title <> %rnd% - %firefoxString%
{
Break
}
count += 1
If count > 100 ; Allow approx five seconds
{
firefox_throw("Could not complete firefox_execute()")
}
}
StringReplace, title, title, %A_Space%- %firefoxString%
return title
}
firefox_throw(exception)
{
MsgBox Error: %exception%
firefox_close()
ExitApp
}Warning: The two example scripts below will close all the Firefox windows you have open and the second will clear your history without confirmation! Make sure you understand what they do before running them. They also assume that the code above is in a file called firefox.ahk in the same directory.
This script will give a quick demo of what they do:
#include %A_ScriptDir%\firefox.ahk
firefox_start()
MsgBox Open chrome location
firefox_open("chrome://mozapps/content/extensions/extensions.xul")
MsgBox Open url
firefox_open("http://www.google.co.uk/")
MsgBox Execute javascript
firefox_execute("document.body.innerHTML = '<H1>Hello World!</H1>'")
MsgBox Query DOM
foo := firefox_query("document.width")
MsgBox Document Width is %foo%
MsgBox Query javascript function
foo := firefox_query("Date()")
MsgBox The date is %foo%
firefox_close()While this is a less trivial example that will clear the Firefox history:
; Previously selected privacy options will be cleared
#include %A_ScriptDir%\firefox.ahk
firefox_start()
firefox_open("chrome://browser/content/sanitize.xul") ; Open the Clear History dialog
Loop ; Keep looping until the dialog has loaded.
{
check := firefox_query("(gSanitizePromptDialog)?true:false")
if (check = "true")
break
Sleep 50
}
firefox_execute("document.getElementById('sanitizeDurationChoice').value = 0") ; The duration option in Firefox 4
firefox_execute("document.getElementById('SanitizeDialog').acceptDialog()") ; Accept the dialog and close the windowThere are some rough edges with the functions at present:
1. You may struggle to get effective results with Firefox 3 as many of the chrome locations you will want to open capture F6 and thus render most of the functions ineffective. If anyone can think of a way round this then I would love to hear it. Anyway, use Firefox 4 it is much better :)
2. They assume that 'Mozilla Firefox' appears at the end of the title bar. This might not be true for all installations.
3. You can't query document.title as they use this to pass return values back out of Firefox.
4. If you have Thunderbird open and it pops open a dialog, these will likely close it.
5. While SendPlay is a lot more reliable than SendInput for Firefox, I still find that it occasionally loses key clicks. This might be solved by tweaking the key delay.
6. They assume they have free reign over any and all Firefox windows that may already be open. They could be refined to open their own window and only interact with that one.
7. Probably a bunch of things I haven't even thought of.
Anyway, Hi. First-time poster - constructive criticism very welcome :)




