welcome: please sign in
location: WritingPageModes09x

Basic Page Mode

We will walk through building a whiz-bang little page mode for that most ubiquitous of Internet grottos, example.com.

define_page_mode

The main form of any page mode is the define_page_mode form. Its two required arguments are the symbolic name of the page mode, and the human-readable name. In addition to the required arguments, it accepts several optional keyword arguments. Typically, you will use both $enable and $disable, which give the setup and teardown functions for your mode. Each takes a buffer as its single argument.

define_page_mode("example_mode", "Example",
                 $enable = function (buffer) {
                     // setup
                 },
                 $disable = function (buffer) {
                     // teardown
                 });

auto_mode_list

Telling Conkeror when to turn on your page mode is as simple as adding a handler to auto_mode_list. You make a regular expression that matches the urls for which your page mode is intended, and push that, in association with your page mode function, onto auto_mode_list. This registration should be the last thing in the javascript file for your page mode. We'll start with a short example, then we'll pick it apart.

let (re = build_url_regex($domain = "example",
                          $tlds = ["com", "net", "org"],
                          $allow_www = true)) {
    auto_mode_list.push([re, example_mode]);
};

build_url_regex

Since creating regular expressions to match urls has patterns that are used over and over again, Conkeror has a function to simplify the creation of these regular expressions, called build_url_regex. It takes several keyword arguments that specify parts of an url, and produces a regexp.

$domain

A regexp or a literal string to match the domain name, not including the top-level domain (.com, .net, .org, etc), and not including www., unless the www. is required.

$allow_www

Boolean where true means that the domain name may optionally have the subdomain www.. Default is false.

$tlds

A list of allowed top-level domains. The default is the list ["com"].

$path

A regexp or a literal string to match against the path portion of the url, excluding the initial /. The default matches any path.

Adding a Keymap

If you go to example.com and hit tab, you will see Conkeror's normal error message, No form field found because tab is bound to browser-focus-next-form-field. But in the underlying Gecko engine, the tab key will also move focus among links. For reasons we need not explain, we will add a key binding to example-mode that lets the tab key fall through to Gecko, instead of letting it be processed by Conkeror.

First we make a keymap with our binding. We set the $parent of our keymap to content_buffer_normal_keymap because we will be overriding that keymap, and we want any keys not bound in our new keymap to still be looked up in that one.

define_keymap("example_keymap", $parent = content_buffer_normal_keymap);
define_key(example_keymap, "tab", null, $fallthrough);

Now we will modify the define_page_mode form that we gave earlier to declare this keymap as an override for normal_input_mode. The $enable and $disable functions have been removed, because we are not using them.

define_page_mode("example_mode", "Example",
                 $keymaps = {normal_input_mode: example_keymap});

Invocation of Page Modes

If you are going to write a page mode, you will very well want to know when and how a page mode gets run.

Auto-Enabling

In Conkeror, web browsing gets done in content buffers. There are other kinds of buffers, like those for displaying progress of downloads, and for showing help, but page modes only exist in content buffers.

  1. The content_buffer data type has a method called onLocationChange, which is an event handler that gets called by Mozilla when the location of the buffer's xul:browser element changes.

  2. The onLocationChange event handler runs the hook content_buffer_location_change_hook.

  3. One of the functions attached to content_buffer_location_change_hook is page_mode_auto_update.

  4. page_mode_auto_update searches the associative list auto_mode_list for a regexp key which matches the new url of the buffer. When it finds one, it calls the function associated with that key to enable the page mode.

Auto-Disabling

Page modes are a type of buffer-mode. (The mode-class is page_mode.) Buffer modes within any given mode-class are mutually exclusive. Thus if you enable one page mode, the underlying buffer-mode mechanism makes sure that any other page mode gets disabled first. But there is one special corner case particular to page modes. If page_mode_auto_update did not find a page mode to enable, then the mutual exclusivity mechanism of buffer modes cannot clean up the old mode. In that case, page_mode_auto_update will disable the old mode itself.

Thus only one page-mode can be current in a buffer at one time, and it is also possible to have no current page mode.

Tips for Writing Good Page Modes

Don't make assumptions about the page being completely downloaded

Try to make your page mode handle partially-downloaded content reasonably. Waiting until the entire page has loaded before allowing basic functionality should be considered the nuclear option, and only used when absolutely necessary.

Avoid using wrappedJSObject

Whatever you are trying to do, there is probably a better way. Any use of wrappedJSObject on content DOM can provide a vector of attack from malicious content. Even if you trust the site in question, steering clear of dodgy techniques like this makes your code more robust and trustworthy.

Conkeror.org: WritingPageModes09x (last edited 2012-01-28 05:12:06 by retroj)