Keyboard Maestro Browser Control (Basics)
Keyboard Maestro 6 introduced new actions (and a few tokens) to control Safari and Chrome. In this first part of the tutorial we'll look at these actions and see what can be done with them and how (more specifically we'll see how to fill out and submit basic forms). In the second part, we'll look into using these actions for more advanced purposes.
Is this tutorial for you?
If you're a web developer, you probably don't need most of it, but some info about KM itself might still be useful to you. If you're not a developer but have some understanding of how HTML works, then this tutorial was made for you. Finally, if you have no idea at all of what happens behind the scene when you view a web page, then Controlling Safari will make sense to you, but you might want to read some introduction to HTML (such as the one at w3schools.com) before going to Controlling the current page.
The actions for Safari and Google Chrome are distinct (and can be found respectively in the Safari Control and the Google Chrome Control categories), but except for the application they control, they're completely identical. In this tutorial we'll use Safari, but everything could be transposed to Chrome simply by selecting the equivalent actions from the Google Chrome Control category.
The actions can be divided into two groups:
- those used to control the browser
- those used to control the current page
1. Controlling Safari
The actions for controlling Safari itself are:
• New Safari Window
Opens a new window in the current space (optionally loading an URL that can come from a variable).
• New Safari Tab
Creates a new tab in the current window (optionally loading an URL that can come from a variable).
• Next Safari Tab
Selects the next tab (next to the right) in the current window.
• Previous Safari Tab
Selects the previous tab (next to the left) in the current window.
• Select Safari Tab
Selects a tab by its position (accepts variables) in the current window.
• Set Safari URL
Loads a new URL in the current window or tab (also accepts variables).
Everything here is pretty obvious and shouldn't require any extra explanation.
And of course, you can always do things not specific to Safari the usual way. For example if you wanted to set a hot key to toggle the Toolbar visibility, you'd just use a Select or "Show a Menu Item" action like this:
2. Controlling the current page
These actions are focused on the contents of the current page (that is the page in the current window or tab).
• Set Safari Title
Changes the title of the current page shown in the title bar and in the tabs bar.
Note: keep Title empty to display the url instead of a title.
• Wait For Safari To Finish Loading
Useful when you want to do something in the page once it's done loading (more about this action later).
• Click Safari Link
Looks for a link by its text in the current page and simulate clicking on it to load the document it points to.
• Focus Safari Field
Locates a field and gives it focus (useful if you want the field to have focus so you can start typing immediately).
• Select Safari Field
Select the contents of a text field (field of type text, email, date, number, password, etc.).
• Set Variable to Safari Field
Reads into a KM variable the contents of a field.
• Set Safari Field to Text
Sets the value of a field to a string or to the contents of a KM variable.
Note: works also with select elements (popups and lists) as long as you give it the value, not the text.
• Set Safari Checkbox
Sets a given checkbox to checked or unchecked (optionally by using a variable)
• Set Safari Radio Button
Checks a given radio button (optionally by using a variable)
• Submit Safari Form
Submits a given form.
• Reset Safari Form
Empties out a given form.
There is also a set of tokens for each browser. We'll see how to use them in the second part of the tutorial, but here's their list:
• Safari Document Title
Evaluates to the title of the current webpage.
• Safari Document URL
Evaluates to the URL of the current webpage.
• Safari Ready State
Evaluates to the readystate of the current webpage.
Note: the value can be either "uninitialized", "loading", "interactive" or "complete". More info about what they mean here.
• Safari Field
Evaluate to the current value of the targeted field.
So let's see how to use these actions, then.
But fIrst, do you see the Develop menu in Safari's menu bar? If not, open Safari preferences window, click on Advanced, then check the "Show Develop menu in menu bar" checkbox. We won't be using that menu, but enabling it also enables the "Inspect Element" command in the contextual menu, and we are going to use this one. You should see it at the bottom of the contextual menu when you do a right-click (or ctrl-click) anywhere in a web page. If you're using Google Chrome, the Inspect Element item should always be enabled and present in the contextual menu.
We'll also need a web page with a form to use as example. So I made this one:￼
Click this link to open that test page in a new window.
So let's say that we want to create a macro that will fill out and submit that form. Create a new macro in a group enabled only when Safari is in front, give it a hot key, and add to it a "Set Safari Field to Text" action. By default, this action looks like this:
document.forms means "the first field of the first form of the document". In this case it's the field labelled "First Name".
OK, I guess that requires some explanations (if not, you can jump to Building the macro).
Understanding the references
Now you may wonder why I said that
forms means the first element of the first form. Why is it not
forms? This is because unlike us humans, who start counting from one, computers start counting from zero in most cases. So
forms references the first item of the forms collection (that is the first form) and
forms references the first item of that form's elements collection (that is its first element). Similarly,
forms would reference the second element of the second form and
forms would reference the fourth element of the second form. Makes sense?
Now click on the Safari popup menu at the top right of the action, and (assuming our test page is the one in front in Safari) that menu opens:
When you clicked on the popup, KM analyzed the DOM of the frontmost document in Safari, and this shows you the fields it found that can be set from this action. As you can see, in this case the fields are not referenced by index as forms was, but by identifier. Go to our test web page in Safari, right-click (or ctrl-click) into the first field, and select Inspect Element from the contextual menu. The source code of the page will be displayed and will look something like this:
Safari selected the line corresponding to the field you clicked in, and as you can see, that input tag has an id attribute set to "first_name". Look for the opening tag of the form that contains this input tag, and you'll see that it also has an id attribute, set to "form_one". Now look again in KM's Safari popup and notice that the first item reads
forms["form_one"]["first_name"] (text). See how the identifiers match? That literally means the field whose identifier is "first_name" of the form whose identifier is "form_one". Clearly, it's the one we clicked into.
Both notations, by index and by identifier, are interchangeable. So
forms["form_one"] would be equally valid and would both reference that same field. The reason why KM defaulted to using the notation by index in Set field is because it didn't analyze the page when you inserted that action into the macro; instead it just used a hardcoded example of what the reference might look like. If there was no form in the page, that suggestion would still be the same but it wouldn't reference anything. Also, in this case the Safari popup wouldn't open, since it would be empty.
So when should we use one rather than the other, then? Well, it depends on the web page. When all the forms and their elements have an id attribute, it's definitely better to use those identifiers because they are required to be unique. If you use the index instead, the day the webmaster adds to the page a new form that appears earlier in the DOM than the one you were targeting is the day your macro stops working. Because it will then target the wrong form. On the other hand, if a form or an element has no identifier (it happens), you'll have no other choice than using the index.
Building the macro
There is another important thing worth noticing in KM's Safari popup. Let's look at it again:
Do you see it?
Yes, that's right, the page doesn't actually contain one form, but three of them (or at least three that have a field this action can set). The DOM references "form_one" (which we already saw), "286219_497867", and "third_form". You might have noticed that there are three submit buttons on the web page, but it doesn't necessarily mean there are three forms. It could have been a designer's choice (admittedly not a very good one) to have a single form with three submit buttons.
Alternatively, KM could show forms or elements that are not visible on the page. For example if you click the Safari popup in KM when the present page is frontmost in Safari, you will see references to a login and a registration forms that are not visible in the page (they only become visible when you click on Login/Register at the top and on Sign Up in the login form). However if they appeared in the DOM before the form you wanted to target, referencing this form with forms wouldn't produce the intended result, because that would target the wrong (hidden) form.
OK, back to our macro, then. The implication of having three forms instead of one is that the macro will need three submit actions. And if each submit causes the page to reload (which they do, I can tell you), we'll have to fill out and submit each form in turn rather than filling out the three forms at once before submitting them.
Add a second "Set Safari Field to Text" action to the macro, and using the Safari popups set them to target respectively the "first_name" and the "last_name" elements and enter your first name and last name. You should get something like this:
Then, looking at the form on the web page, we see the Gender radio buttons, but they don't appear in the Safari popup. It's because radio buttons require a different action: "Set Safari Radio Button". So insert that action into the macro, click its Safari popup, and select
forms.["form_one"]["gender"] (it's obviously the one we want as there isn't any other choice):
But now you may wonder why KM found only one gender element when there are three radio buttons. That's because radio buttons in a same group are required to have the same identifier. This is how pressing a radio button automatically releases the previously pressed one so you can only have one radio button pressed at a time.
How do you target a given radio button, then? Let's see what KM offers: click the "to:" popup at the bottom left of the action. The menu contains "value", "1", "2", and "0". What does that mean? Let's have a look at the source code of the page. Take it to the front, right-click on the Female button, for example, and look at the source:
You can see that the input tag for Female has a value attribute set to "1", the one for Male has a value attribute set to "2", and the one for Others has it set to "0". So that's where KM got those from. And it makes obvious which one you'll want to select in the action.
At this point you can take the web page back to the front, close the html inspector, and trigger the macro. The first three fields should be set to the values you chose.
OK, so now we want to submit the first form. Insert a "Submit Safari Form" action, and set it to target
document.forms["form_one"] (we saw earlier that "form_one" is the identifier of the first form).
And since we know that will cause the page to reload, we'll want to make sure the page is done reloading before we start filling out the second form. This is done with the "Wait For Safari to Finish Loading" action. So insert one of these after the submit action.
At this point, running the macro would fill out the first form and submit it. Well, actually in this case it would just reload the page because the form tag has no action attribute giving the browser the URL to post to, but in a real website it would. So you'd probably want to disable the submit action for now (by pressing the √ button below the actions pane) and enable it only when you've tested the whole macro.
Or you could start the macro with an action that sets a variable (for example "debugging") to 1, and enclose each submit action into an "If Then Else" action to execute it only when that variable is set to 0. This way the submit actions wouldn't be executed until you were done testing and you set the variable to 0 in the first action.
Since the first visible element in the second form is Email (a text field), insert a "Set Safari Field to Text" action at the bottom of the macro. By now you've learned that the next thing to do is to click its Safari popup, so go ahead and do it.
The third element shown by KM in the popup has weird identifiers, though:
forms["286219_497867"]["286219_497867_34077"] (email) Why is that?
Many web pages are dynamically generated, including the forms they contain, and depending on the framework that does the job in the backend, identifiers can be dynamically generated too. And in some cases, they will look as cryptic as these. So you may have to make sure your choice references the field you expect (although in this case it's easy because "(email)" tells you that the type of the corresponding input element is "email", and since there's only one field labelled Email in the page, chances are good that this is the one).
Again, use the Inspect Element command from the contextual menu in Safari to view the source corresponding to this field. And indeed, it will confirm that you want to target the field with the identifier
286219_497867_34077 of the form with the identifier
Add an email address to the "to:" field and you're done with this action.
Next we have a checkbox. So insert a "Set Safari Checkbox" action into the macro, and again click its Safari popup. KM detects only one checkbox in the page, so we can be pretty sure it's this one (besides, the form identifier shows that it is in the same form as the email field). Unlike Set Safari Radio Button, this action shows "checked" and "unchecked" in the "to:" field, so again the choice is easy.
Now whether you want to check that checkbox or not is your own choice. But if you don't, you won't need an action for it at all. Since it's unchecked by default in the webpage, without an action modifying it, this checkbox will just stay unchecked.
So now we just need a "Submit Safari Form" action and a "Wait for Safari to Finish Loading" action. Select those you inserted for the first form, copy them, paste them at the bottom of the macro, and update the submit action to make it target the second form.
The third form only contains a popup menu. But where's the "Set Safari Popup Menu" action? Well, there isn't one, because the "Set Safari Field to Text" action can do it (remember the
forms["third_form"]["continent"] item you saw in the Safari popup earlier? that's the one). So insert a "Set Safari Field to Text" action at the bottom of the macro, and set it to target
Now if you're on the European continent and saw that the popup in the webpage contains "Europe", you might be tempted to type "Europe" into the "to:" field of the action, right? But that wouldn't work. Why not? Well, look at the source code:
As you see, each item in the menu appears as an option tag. And each has a value attribute, set (for most of them) to the lowercased name of a continent. So from the DOM's point of view (which KM adopts), there is no option tag whose value is the capitalized "Europe". This is why you'd actually have to type "europe" into the "to:" field.
Finally, we need a "Submit Safari Form" action, so insert one, make it target the third form (
document.forms["third_form"]), and temporarily disable it.
That's it. Try running the macro, and it should fill out the three forms (but not submit anything until you enable the submit actions). Then enable those submit actions and try again. This time the page should reload after each submission.
document.getElementsByName("third_form").submit() would work as you'd expect and submit the third form.
This concludes the first part of this tutorial. Thanks for reading this far!
We have explored the various actions KM provides for controlling the browser and its contents; we've seen how to locate, reference, and set the basic form elements, along with submitting forms; and what we learned should allow us to do the same with the form elements we didn't encounter here.
The forms we worked with are pretty basic, and they don't really represent the kind of stuff we'd want to do on a real website, but in the second part of this tutorial, we'll use the knowledge we acquired to put together much more complex macros, the kind we'd want to use for real.
Go to the Second part.
Here are the actions of the macro we built: