Foreword

Since epiphany is moving away from Gecko, this page will likely be unmaintained for quite some time. Hopefully, that extension will then get ported to WebKit.

Goal

Make epiphany able to emphasize some shortcuts to ease the navigation through the links of a given page, so that it becomes possible to access them with a single key, instead of a bunch of TAB hits. The goal is to clone konqueror's Control key behaviour.

Definition

The term hotkey will be preferred to accesskey, which is defined by the W3 Consortium, but looks like mostly unsupported. This term might be changed as soon as a better one is found.

Documentation

Many links are available.

Plans

As an epiphany extension as much as possible, but it looks like needed to add some code in epiphany too. It seems that there's no way to catch key presses in the embed mozilla widget, so that would have to be added (probably in embed/).

According to the GtkMozEmbed signal reference there are no mouse click callbacks, but there are in EphyEmbed signal reference: ge-dom-mouse-click and ge-dom-mouse-down. So it should be possible to add something like ge-dom-key-down, so as to determine the pressed key.

Once it is done, the proposed way is the following: On Ctrl press, the accessibility mode is toggled. Off by default, when it turns to on, the DOM is modified so as to emphasize some given characters in the links, possibly using the first letter of each, but also avoiding hotkey collision. To ease customization, using a span tag could be use at least at the beginning, so that changing a bit of CSS is sufficient to have a nice display. When it is switch back to off, a simple way to remove the added information would be to reload the page, which would flush this.

At the moment, a javascript DOM modification is quite easy, based on the one published by Christian Schmidt, but it would be better to do it on epiphany side, so that the hotkey—URL mapping is kept where it is computed. This javascript basic implementation could still be used until the key handling is implemented, so as to get an overview.

Progress

Key presses

In embed/mozilla/mozilla-embed.cpp, the following mapping happens:

  • mozilla_embed_dom_mouse_click_cbge_dom_mouse_click through mozilla_embed_emit_mouse_signal;
  • mozilla_embed_dom_mouse_down_cbge_dom_mouse_down through mozilla_embed_emit_mouse_signal;
  • and mozilla_embed_dom_key_press_cbge-search-key-press through g_signal_emit_by_name.

So it looks that no modification to the mozilla code in epiphany is needed. There's still a need of exposing an additional signal in epiphany.

Key presses (bis)

Just grep in epiphany-extensions for key-press | key_press, and here it is:

/* Prototype taken from ephy-gesture.c */
static gboolean
dom_key_press_cb (GtkWidget *widget,
                  GdkEventKey *event,
                  EphySample2Extension *extension)

Unfortunately, it looks like it is not possible to receive the events when the focus is in an input text field, but that sounds like a good start.

Now let's work on epiphany-extensions only, using sample-mozilla as a basis: git repository.

Proposed behaviour

h enables the hotkeys, Esc then disables them. Once enabled, if a key is pressed, an eventual mapping is checked. If it's mapped, load the link in the embed, if not, ignore it.

To store the mapping, the proposed structure is proposed: a linked list of structures, containing: an guint (the key value), the old nsIDOMHTMLLinkElement (backuped so that it can be restored once the hotkeys are disabled), and the new nsIDOMHTMLLinkElement (which could be replaced by a const char*, being the URL to load.

At the beginning, restoring the old nsIDOMHTMLLinkElement could be ignored.

At the beginning, when a hotkey matches several times, the first occurrence is considered. Then, one could consider having a cycling through all occurrences until Enter is pressed, and a direct loading when there's only one occurrence. Another way is to use additional keys, as konqueror does, but that means additional code to pick the right one.

Browse the DOM to find the Anchors

It is done by getting the DOM toplevel, then querying each node, checking the Anchor (and not Link) ones, and modifying them, while keeping a copy of them, so that they can be restored once the hotkeys are disabled.

Here is an example of hotkeys enabled on http://www.google.fr/ with an hardcoded (quick & dirty) CSS style:

Google — Example 1

0.1 release

As of this release (git tag), it is possible to enable/disable the hotkeys using h/Esc, and to browse the links on a first matching occurrence basis.

Next step: make it possible to browse the links when there are multiple key value occurrences, use only uppercase characters.

Proposed implementations:

  1. When there's only a match, load the link directly. When there are several matches, cycle through them, and only link when Enter is pressed.

  2. When there are multiple matches, try and use another character of the link for the next occurrences. The implementation using GSList * could be a bit inefficient, beware. Also, what kind of character to use when all characters are already taken? A bit harder, left for later.

Later goals:

  • Make the CSS style customizable.

Unicode handling, uppercase

The following functions are used to determine the key value to store/look for.

  • gchar g_ascii_toupper (gchar c);
  • gboolean g_unichar_isalnum (gunichar c);

Later...

Maybe update the statusbar when there are several links, explaining one has to use Enter to validate the current link.

Note to self

Don't forget to copy the objects passed to g_hash_table_insert, especially if they are integers.

As of 0.2

[Fixed] Troubles with some pages, e.g. on http://news.google.fr/, but might be unrelated to the number of links, though.

[Fixed] When going back and forth, the extension toggling appears to be buggy. Hooking another even might be sufficient to fix this.

An additional feature could be back-cycling, which could be achieved when Shift, which can be detected thanks to:

 guint state; a bit-mask representing the state of the modifier
 keys (e.g. Control, Shift and Alt) and the pointer buttons. See
 GdkModifierType.

Another alternative/configuration option

To have a visible emphasis, it is possible to call Focus() on the selected item, but the way it is focussed might be unpleasant. That's why it has been implemented the following way: Focus() is called so that the page scrolls to the appropriate location if needed, then Blur() is called so that there's no visible trace of the previsou Focus() call.

0.3 release

Just two things:

  • [Improvement] Focus()/Blur() calls.
  • [Major fix] Fixes crash when an empty link is encountered, by initializing keyval explicitely to 0. Hopefully epiphany shouldn't crash anymore.

Next targets:

  • Handle nested tags (e.g. contained <b>, <strong>, etc. tags).
  • Handle links beginning with spaces (either [space], &nbsp; and so on).

Known bug

  • Gmail sounds like not functional, and might not be trivial to debug, there's a lot of javascript and not that much HTML.

Installation on debian sid (and maybe lenny)

Just do the following the first time:

git-clone git://git.debian.org/git/users/kibi-guest/epiphany-extensions.git
cd epiphany-extensions
DEB_BUILD_OPTIONS=nostrip debuild -us -uc -b -nc # Some Build-Depends may
                                                 # need to be installed.
sudo debi                  # Installs the generated binary package
pkill epiphany && epiphany # Then Tools>Extensions, and activate Hotkeys

To update:

cd extensions/hotkeys      # Or in the inverse order, not important
git-pull                   # Merge remote changes into local working copy
make                       # Optional, make install will call it if needed
sudo make install          # Update the .so in /usr/lib/epiphany/2.18
pkill epiphany && epiphany

0.4 release

  • Moved from extensions/sample-mozilla to extensions/hotkeys, with proper copyright entries. Restored the ancient tree.
  • Added an icon indicating the current hotkeys status.
  • Fixed bad tab handling. Now the extension should just behave fine with several tabs (that was just an horror, really). There are still problems when several windows are involved, but at least it sounds less critical than non-functional tabs.

Next features/TODOs

  • [Done] Add a textbox displaying the number of links.
  • [Done] Factor some parts of the code (hotkeys enabling/disabling), remove the embed from the structure if one is certain (or can make certain) that the tab sync is called when finalizing. Putting a call in the ephy_hotkeys_extension_finalize() function should be sufficient, so that the structures are freed before closing.
  • Implement the above-mentioned next targets.
  • Add configuration items (accessible through an UI?)
  • Try using additional CSS style to let the user customize the hotkeys display.

How to implement these features

  1. [Done] 1st target: first character isn't alphanum()

    1. Move to the next element. If it is '<', call continue (at least until the 2nd target is implemented).

    2. Otherwise, see whether that other character is alphanum(), then move to a if needed.

  2. 2nd target: nested element: If there are nested elements, dig further if there's only a hierarchy of modifier elements (b, i, strong, font), until... what?

    1. Maybe until InnerHTML doesn't contain any '<' anymore?

    2. Maybe until there's no more HTMLElement child(ren)?

Problems/remarks

[Done] What if a link contains entity-encoded characters? No problem since the InnerHTML string is encoded using HTML entities, so an opening ‘&’ should always match a closing ‘;’.

0.5 release

  • [New] Statusbar gets an icon and a label displaying the number of links when the hotkeys are enabled.
  • [New] CSS can be customised through the user stylesheet in Epiphany's preferences. No hardcoded default properties are used anymore. See the README file in the sources for an example (and full instructions).
  • [New] Handle the cases where the first character of a link isn't alphanumerical. Previously, the link was dropped, now the next characters are considered. Nested tags aren't supported yet, though.
  • Code cleanup (with no user-visible change).

Idea to handle nested tags

To be able to handle the nested tags, it is of course possible to analyze the tree, but an alternative way could be used: when an < character is encountered, search for a matching >. The problem is that it might break on things like that:

<a href="/location"><img   src="someimage>.png"  >Foo</a>

which would result in having the p of png being defined as the hotkey, and the additional <span> being embedded in the src attribute. Hopefully such things get escaped (and extra spaces get munged):

<img src="someimage%3E.png">Foo</a>

Further tasks

  • [Done] Add help accessible through the Help menu.
  • I18N/L10N.
  • Use the clickable item to popup the instructions to set the CSS and an example?
  • Investigate the disparition of the RSS icon when the hotkeys get activated.

0.6 release

  • [New] Handle nested tags (e.g. <a href="..."><b>Bold links</b></a>).
  • [Fix] Re-ordered instructions for the status of hotkeys display, so that it is not possible to either disable the hotkeys while they are still being computed, or disable the hotkeys twice in a row. That prevents two kinds of (SEGV) crashes.

Fetching the sources

It looks like it is not possible to mix git-svn and git-fetch… Well, see the first post tagged epiphany.

0.7 release

  • [New] A manual entry has been added with introduction, usage, configuration (with an example), and limitations.

Further ideas

  • [Done] Add backcycling when Shift is pressed.

  • Open in a new tab when Ctrl is pressed. (It might be possible to open a new window, but which key would it be? Alt maybe?) Interesting thinks are in ephy-link.h (public API): ephy_link_open(), with some nice flags. A problem, though: it might be difficult (if not impossible) to substract the Control key from the keyval sent to mozilla_is_a_hotkey(), so this function might have to handle several Enter cases, with or without Control and/or Shift pressed, for example. Hmm. After some tests, it looks like Control+Enter doesn't reach the mozilla_is_a_hotkey() function. Not seen as a GdkEvent by the DOM, maybe?

  • When either the hotkeys get activated or the browser/extension gets started (whatever is OK with both performance and usability), see whether the user (custom) stylesheet contains a given identifier. If it does, just use the <span> tags as they are used now. If the CSS doesn't contain such identifier, use an hardcoded default style in the style= attribute, so that the extension is usable without any configuration, and remains fully customizable to ones needs. Hopefully ephy-embed-prefs.h contains the USER_STYLESHEET_FILENAME define. Unfortunately, it would be needed to have access to the (private) ephy_dot_dir() for it to be useful, so that we can concat'em (it is located in libs/ephy-file-helpers.c). Given the code of ephy_file_helpers_init() (same file), it is unlikely to be duplicated. Deadend until one assumes a particular location? :/ (e.g. ~/.gnome2/epiphany).

  • The question is: how would one benefit from the USER_STYLESHEET_FILENAME define when one doesn't know in which directory it can be found? By the way, ephy-embed-prefs.h is only about GConf keys, besides this define.

  • Add an option to always have the same behaviour: pressing Enter is needed even if there's only one link.