welcome: please sign in
location: WritingPageModes

This article covers writing page-modes.

1. build_url_regexp

Before we start writing a page-mode, we need to cover an important utility function that gets much use in page-modes called build_url_regexp. Part of writing a page-mode is constructing a test against which an url can be matched so that Conkeror knows when to turn the page-mode on. The test can actually be either a function or a regular expression, but regular expressions are the more commonly used.

Regular expressions to match urls can be complex, so to help, we use the utility function build_url_regexp to create them. It takes several keyword arguments that specify parts of an url, and produces a regexp accordingly.

$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.

2. define_page_mode

The general form for creating a page mode is called define_page_mode. Its required arguments are a name, a test, an enable function, and a disable function. We usually name the two functions explicitly, but this is not required; it is purely a form of self-documentation. Additionally, define_page_mode takes the keywords $display_name and $doc. The display-name is shown on the right side of the minibuffer message area when the page-mode is enabled. The doc is a documentation string used for the page-mode's interactive command, and possible future uses.

define_page_mode("example-mode",
    build_url_regexp($domain = "example",
                     $allow_www = true,
                     $tlds = ["com", "net", "org"]),
    function enable (buffer) {
        // setup
    },
    function disable (buffer) {
        // teardown
    },
    $display_name = "Example",
    $doc = "A page-mode for example.com.");

2.1. test

The second argument to define_page_mode is the test. Whenever the location changes in a buffer, the url is matched against the tests of all activated page-modes, enabling all those for which the match succeeded.

The test may either be a regular expression or a function. The regular expression form is largely covered by build_url_regexp, detailed above. The function form is just a function of one argument that returns either true or false. It receives as its argument an nsIURI object, not a string. To get a string, use the 'spec' property of the argument.

3. define_keymaps_page_mode

Commonly, the purpose of a page-mode is to provide keymaps that make it easier to use a given site in Conkeror. Such page-modes can be defined in a simpler form, called a keymaps-page-mode. These are defined by define_keymaps_page_mode, which is similar in form to define_page_mode, except that instead of taking enable and disable functions, it takes a content-modalities object. A content-modalities object tells which keymaps to use in which contexts.

First, we need to define a keymap. Sticking with example.com, what can we do usefully with a key binding there? We can create a fallthrough key. 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, and this website has no form fields. But in the underlying Gecko engine, the tab key will also move focus among links, so for purposes of illustration, we will make the tab key fall through to Gecko in our example-mode.

First we make a keymap with our binding. When we give a keymap a display-name, as below, Conkeror will show this name on the right side of the minibuffer message area when this keymap is active. It is a way to provide feedback to the user, showing what context she is in.

define_keymap("example_keymap", $display_name = "example");
define_key(example_keymap, "tab", null, $fallthrough);

Now that we have a keymap, we can define our keymaps-page-mode.

define_keymaps_page_mode("example-mode",
    build_url_regexp($domain = "example",
                     $allow_www = true,
                     $tlds = ["com", "net", "org"]),
    { normal: example_keymap },
    $display_name = "Example");

The content-modalities object is a javascript object where the keys are context names, and the values are keymaps. The following context names exist:

normal
Normal context is the context when nothing is focused. Note, normal context is always loaded, even when something is focused. The more specialized context is simply higher in the keymap stack, thus may override bindings in "normal".
form
Form context is when an element inside of a form has focus.
checkbox
Context when a checkbox is focused.
radio
Context when a radio element is focused.
submit
Context when a submit button is focused.
reset
Context when a reset button is focused.
text
Text context is when a text-type editable element is focused, such as a text or password input, a textarea, or a richedit field.
textarea
Textarea context is loaded on top of text context when the editable element is a textarea.
select
Select context is when a select (aka combobox or dropdown) is focused.
anchor
Anchor context is when a link is focused.
button
Button context is when a button is focused.
embed
Embed context is when an embed is focused. Note, sometimes embedded objects can steal keyboard focus completely!
richedit
Richedit context is loaded on top of textarea and text context when the editable element is a richedit field.

4. 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.

4.1. Activating

If you want your page-mode to auto-enable when you visit a covered site, you have to activate it with a call like this:

page_mode_activate(some_mode);

4.2. 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_update.

  4. page_mode_update searches the object active_page_modes and enables all page-modes whose test matches the current url.

5. 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: WritingPageModes (last edited 2012-02-16 22:24:02 by retroj)