diff -urN -a --binary trunk/configure.ac epiphany-extensions-trunk/configure.ac
--- trunk/configure.ac	2007-09-03 04:33:40.000000000 +0000
+++ epiphany-extensions-trunk/configure.ac	2007-09-03 04:34:18.000000000 +0000
@@ -153,8 +153,8 @@
 
 AC_MSG_CHECKING([which extensions to build])
 
-ALL_EXTENSIONS="actions adblock auto-reload auto-scroller certificates error-viewer extensions-manager-ui gestures greasemonkey java-console livehttpheaders page-info permissions push-scroller rss sample sample-mozilla select-stylesheet sidebar smart-bookmarks tab-groups tab-states"
-USEFUL_EXTENSIONS="actions adblock auto-reload auto-scroller certificates error-viewer extensions-manager-ui gestures java-console page-info push-scroller select-stylesheet sidebar smart-bookmarks tab-groups tab-states"
+ALL_EXTENSIONS="actions adblock auto-reload auto-scroller certificates error-viewer extensions-manager-ui gestures greasemonkey hotkeys java-console livehttpheaders page-info permissions push-scroller rss sample sample-mozilla select-stylesheet sidebar smart-bookmarks tab-groups tab-states"
+USEFUL_EXTENSIONS="actions adblock auto-reload auto-scroller certificates error-viewer extensions-manager-ui gestures hotkeys java-console page-info push-scroller select-stylesheet sidebar smart-bookmarks tab-groups tab-states"
 DEFAULT_EXTENSIONS="actions adblock auto-scroller certificates error-viewer extensions-manager-ui gestures java-console page-info push-scroller select-stylesheet sidebar smart-bookmarks tab-groups tab-states"
 
 PYTHON_ALL_EXTENSIONS="python-console sample-python favicon cc-license-viewer epilicious"
@@ -180,7 +180,7 @@
 			  build the specified extensions. Available:
 			  actions, adblock, auto-reload, auto-scroller, cc-license-viewer,
 			  certificates, error-viewer, extensions-manager-ui, favicon,
-			  gestures, greasemonkey, java-console, livehttpheaders, page-info,
+			  gestures, greasemonkey, hotkeys, java-console, livehttpheaders, page-info,
 			  permissions, push-scroller, python-console, rss, sample,
 			  sample-mozilla, sample-python, select-stylesheet, sidebar,
 			  smart-bookmarks, tab-groups, tab-states,
@@ -366,6 +366,8 @@
 extensions/favicon/Makefile
 extensions/greasemonkey/Makefile
 extensions/greasemonkey/mozilla/Makefile
+extensions/hotkeys/Makefile
+extensions/hotkeys/mozilla/Makefile
 extensions/livehttpheaders/Makefile
 extensions/livehttpheaders/mozilla/Makefile
 extensions/page-info/Makefile
diff -urN -a --binary trunk/data/icons/Makefile.am epiphany-extensions-trunk/data/icons/Makefile.am
--- trunk/data/icons/Makefile.am	2007-09-03 04:33:40.000000000 +0000
+++ epiphany-extensions-trunk/data/icons/Makefile.am	2007-09-03 04:34:25.000000000 +0000
@@ -6,6 +6,11 @@
 	hicolor_status_24x24_feed-presence.png \
 	hicolor_status_32x32_feed-presence.png \
 	hicolor_status_scalable_feed-presence.svg \
+	hicolor_status_16x16_hotkeys.png \
+	hicolor_status_22x22_hotkeys.png \
+	hicolor_status_24x24_hotkeys.png \
+	hicolor_status_32x32_hotkeys.png \
+	hicolor_status_scalable_hotkeys.svg \
 	$(NULL)
 
 noinst_DATA = \
diff -urN -a --binary trunk/data/icons/README epiphany-extensions-trunk/data/icons/README
--- trunk/data/icons/README	1970-01-01 00:00:00.000000000 +0000
+++ epiphany-extensions-trunk/data/icons/README	2007-09-03 04:34:18.000000000 +0000
@@ -0,0 +1,11 @@
+When the SVG version of an icon has been updated, it is possible to get
+automatically the PNG updated by running the following:
+
+  for file in *scalable*.svg; do
+    for size in 16 22 24 32; do
+      inkscape -w $size -e `echo $file | sed -e s/scalable/${size}x${size}/ -e s/svg/png/` $file
+    done
+  done
+
+  -- Cyril Brulebois <cyril.brulebois@enst-bretagne.fr>  Mon, 03 Sep 2007 03:04:19 +0200
+
diff -urN -a --binary trunk/data/icons/hicolor_status_16x16_hotkeys.png epiphany-extensions-trunk/data/icons/hicolor_status_16x16_hotkeys.png
--- trunk/data/icons/hicolor_status_16x16_hotkeys.png	1970-01-01 00:00:00.000000000 +0000
+++ epiphany-extensions-trunk/data/icons/hicolor_status_16x16_hotkeys.png	2007-09-03 04:24:54.000000000 +0000
@@ -0,0 +1,5 @@
+PNG
+
+   IHDR         a   sBIT|d   	pHYs  v  v}Ղ   tEXtSoftware www.inkscape.org<  KIDAT81@B:A/`g(`r!Ȼ@tN,$ :yE%̲DTM ; h*+q0#U 
+I0h4cNɄ^/L{nfCEyTU\@D ^
+ÐpHTy^6j"c%Átїf Pl[ZVjZru~,q,nW i6^%C1MS ,K7(ضMP ˡir۶uԋ?A h4z1GEvWˤ]_B8e6i    IENDB`
\ No newline at end of file
diff -urN -a --binary trunk/data/icons/hicolor_status_22x22_hotkeys.png epiphany-extensions-trunk/data/icons/hicolor_status_22x22_hotkeys.png
--- trunk/data/icons/hicolor_status_22x22_hotkeys.png	1970-01-01 00:00:00.000000000 +0000
+++ epiphany-extensions-trunk/data/icons/hicolor_status_22x22_hotkeys.png	2007-09-03 04:24:54.000000000 +0000
@@ -0,0 +1,3 @@
+PNG
+
+   IHDR         Ĵl;   sBIT|d   	pHYs    ϐ   tEXtSoftware www.inkscape.org<  IDAT8PK()fp@0]4Jw;[! 1M. \'̝f.ΆkEDPEv" d2$G}Պrvmۏq\0|<+Rm"",VR '+ԤǡnRyKRhffCۥZb6׺"" R,cGjq<FxGGUU\e^W\($'uZM 뺎iO%:N}jxi0d2 qt:% \e\~H^Ӊb!.,+qx7o0`& 0h|>'nl6*oC'+2`_SNyLsTCDn x.>    IENDB`
\ No newline at end of file
diff -urN -a --binary trunk/data/icons/hicolor_status_24x24_hotkeys.png epiphany-extensions-trunk/data/icons/hicolor_status_24x24_hotkeys.png
--- trunk/data/icons/hicolor_status_24x24_hotkeys.png	1970-01-01 00:00:00.000000000 +0000
+++ epiphany-extensions-trunk/data/icons/hicolor_status_24x24_hotkeys.png	2007-09-03 04:24:54.000000000 +0000
@@ -0,0 +1,5 @@
+PNG
+
+   IHDR         w=   sBIT|d   	pHYs  1  1(R   tEXtSoftware www.inkscape.org<   IDATHŕ=KpyPR(HP(8fUpwBE
+t*ĥtlQ)4RCq0Z6r??p"""(JGq6y *8 C:kH |Vboo\.G>R| fXQ}zVNNNNe ^i L4/h>}lfwwcv2F eNR f\^^vifjuZ7RP۶8::QՒX,:88 es&| A{"d2 "ڥ""2Ų3~{{[A Kal6EDu8.";4a躎R%O/*hYC0DUU,p{{=A(
+Lt%I+W(IJ!~R,S;    IENDB`
\ No newline at end of file
diff -urN -a --binary trunk/data/icons/hicolor_status_32x32_hotkeys.png epiphany-extensions-trunk/data/icons/hicolor_status_32x32_hotkeys.png
--- trunk/data/icons/hicolor_status_32x32_hotkeys.png	1970-01-01 00:00:00.000000000 +0000
+++ epiphany-extensions-trunk/data/icons/hicolor_status_32x32_hotkeys.png	2007-09-03 04:24:54.000000000 +0000
@@ -0,0 +1,5 @@
+PNG
+
+   IHDR           szz   sBIT|d   	pHYs    u85   tEXtSoftware www.inkscape.org<  xIDATX헿K#Q?#U6ʦb	h'^`)*#aႶ	dcQ	.EHXV]Ws&D/<؝y|<f$;HH6 1Kc3Fz)5hY^
+N&ˉ>T9.RZ pl6;0Mt:'[\\*OovwwZrxxͱE&	HlvfC=t]G4t]'L&yxxȖo8lllp~~'vEΨT*b㵕#`vd݌8lnn}\TGFcOWשjP41M]4mVVV}7774T*5P}Z`'77U_#	TUNnsrrՕ'+JEMDwwwBCp]W!sw"򍀪H4TVPh?hK2m{% P(z]QUU  a&QF,|>?q`YgE8W=X{
+ afDD_?J    IENDB`
\ No newline at end of file
diff -urN -a --binary trunk/data/icons/hicolor_status_scalable_hotkeys.svg epiphany-extensions-trunk/data/icons/hicolor_status_scalable_hotkeys.svg
--- trunk/data/icons/hicolor_status_scalable_hotkeys.svg	1970-01-01 00:00:00.000000000 +0000
+++ epiphany-extensions-trunk/data/icons/hicolor_status_scalable_hotkeys.svg	2007-09-03 04:34:18.000000000 +0000
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="64px"
+   height="64px"
+   id="svg17818"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   sodipodi:docbase="/home/kibi/hacking/epiphany-extensions.git/extensions/hotkeys/icons/scalable/status"
+   sodipodi:docname="hotkeys.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs17820" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="3.8890873"
+     inkscape:cx="29.875524"
+     inkscape:cy="29.36108"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:window-width="1024"
+     inkscape:window-height="721"
+     inkscape:window-x="0"
+     inkscape:window-y="31">
+    <sodipodi:guide
+       orientation="horizontal"
+       position="67.454545"
+       id="guide18797" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata17823">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <cc:license
+           rdf:resource="http://web.resource.org/cc/PublicDomain" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Cyril Brulebois &lt;cyril.brulebois@enst-bretagne.fr&gt;</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:description />
+        <dc:title>H(ot)key.</dc:title>
+      </cc:Work>
+      <cc:License
+         rdf:about="http://web.resource.org/cc/PublicDomain">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <rect
+       style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:6.423419;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect18799"
+       width="52.321667"
+       height="52.321667"
+       x="5.8391662"
+       y="5.8391662" />
+    <path
+       style="font-size:66.42156982px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:FreeMono"
+       d="M 14.708254,16.047754 C 14.708253,14.018244 16.047753,13.003471 18.726758,13.003433 L 25.789584,13.003433 L 25.789584,27.311744 C 28.225027,24.835722 31.005505,23.597699 34.131026,23.597672 C 37.378279,23.597699 39.976097,24.531291 41.924489,26.398448 C 43.913416,28.225064 44.907893,30.660519 44.907924,33.70482 L 44.907924,44.907924 L 45.273243,44.907924 C 47.952212,44.90793 49.291712,45.922702 49.291747,47.952245 C 49.291712,49.981794 47.952212,50.996567 45.273243,50.996567 L 38.453962,50.996567 C 35.734346,50.996567 34.374551,49.981794 34.374571,47.952245 C 34.374551,45.922702 35.734346,44.90793 38.453962,44.907924 L 38.819281,44.907924 L 38.819281,34.496344 C 38.819256,32.953905 38.453938,31.776769 37.723325,30.96493 C 37.033256,30.112541 35.612574,29.686337 33.461275,29.686315 C 32.040573,29.686337 30.843141,29.950178 29.868975,30.477839 C 28.894777,31.005541 27.534982,32.101496 25.789584,33.765706 L 25.789584,44.907924 L 26.154903,44.907924 C 28.874482,44.90793 30.234278,45.922702 30.234294,47.952245 C 30.234278,49.981794 28.874482,50.996567 26.154903,50.996567 L 19.335622,50.996567 C 16.656617,50.996567 15.317116,49.981794 15.317118,47.952245 C 15.317116,45.922702 16.656617,44.90793 19.335622,44.907924 L 19.700941,44.907924 L 19.700941,19.092076 L 18.726758,19.092076 C 17.427844,19.092108 16.433367,18.889153 15.743323,18.483212 C 15.053276,18.036744 14.708253,17.224926 14.708254,16.047754"
+       id="text18801" />
+  </g>
+</svg>
diff -urN -a --binary trunk/extensions/hotkeys/Makefile.am epiphany-extensions-trunk/extensions/hotkeys/Makefile.am
--- trunk/extensions/hotkeys/Makefile.am	1970-01-01 00:00:00.000000000 +0000
+++ epiphany-extensions-trunk/extensions/hotkeys/Makefile.am	2007-09-03 04:34:18.000000000 +0000
@@ -0,0 +1,45 @@
+SUBDIRS = mozilla
+
+extensiondir = $(EXTENSIONS_DIR)
+extension_LTLIBRARIES = libhotkeysmozillaextension.la
+
+libhotkeysmozillaextension_la_SOURCES = \
+	ephy-hotkeys-extension.c	\
+	ephy-hotkeys-extension.h	\
+	hotkeys-mozilla.c
+
+libhotkeysmozillaextension_la_CPPFLAGS = \
+        -I$(top_srcdir)/include					\
+	-I$(top_srcdir)/extensions/hotkeys/mozilla	\
+	-DSHARE_DIR=\"$(pkgdatadir)\"				\
+	-DEPHY_EXTENSIONS_LOCALEDIR=\"$(datadir)/locale\"  	\
+	$(AM_CPPFLAGS)
+
+libhotkeysmozillaextension_la_CFLAGS = \
+	$(EPIPHANY_DEPENDENCY_CFLAGS)	\
+	$(AM_CFLAGS)
+
+libhotkeysmozillaextension_la_LIBADD = \
+	mozilla/libhotkeysmozillamozilla.la	\
+	$(MOZILLA_COMPONENT_LIBS)
+
+libhotkeysmozillaextension_la_LDFLAGS = \
+	-module -avoid-version \
+	-export-symbols $(top_srcdir)/ephy-extension.symbols \
+	$(AM_LDFLAGS)
+
+extensioninidir = $(extensiondir)
+extensionini_in_files = hotkeys.ephy-extension.in.in
+extensionini_DATA = $(extensionini_in_files:.ephy-extension.in.in=.ephy-extension)
+
+%.ephy-extension.in: %.ephy-extension.in.in $(extension_LTLIBRARIES)
+	sed -e "s|%LIBRARY%|`. ./$(extension_LTLIBRARIES) && echo $$dlname`|" \
+	    -e "s|%EXTENSION_DIR%|$(extensiondir)|" \
+	$< > $@
+
+@EPIPHANY_EXTENSION_RULE@
+
+CLEANFILES = $(extensionini_DATA)
+DISTCLEANFILES = $(extensionini_DATA)
+
+EXTRA_DIST = $(extensionini_in_files)
diff -urN -a --binary trunk/extensions/hotkeys/ephy-hotkeys-extension.c epiphany-extensions-trunk/extensions/hotkeys/ephy-hotkeys-extension.c
--- trunk/extensions/hotkeys/ephy-hotkeys-extension.c	1970-01-01 00:00:00.000000000 +0000
+++ epiphany-extensions-trunk/extensions/hotkeys/ephy-hotkeys-extension.c	2007-09-03 04:34:18.000000000 +0000
@@ -0,0 +1,496 @@
+/*
+ *  Copyright © 2007 Cyril Brulebois
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ *  Based on the following file from the sample-mozilla extension
+ *  (released under the same license).
+ *
+ *  $Id: ephy-sample2-extension.c 1376 2006-09-13 19:01:42Z chpe $
+ *
+ *  Copyright © 2003 Marco Pesenti Gritti
+ *  Copyright © 2003 Christian Persch
+ */
+
+#include "config.h"
+
+#include "ephy-hotkeys-extension.h"
+#include "mozilla-hotkeys.h"
+#include "ephy-debug.h"
+
+#include <epiphany/ephy-extension.h>
+#include <epiphany/ephy-statusbar.h>
+
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+
+#include <gdk/gdkkeysyms.h>  /* GDK_Escape    */
+#include <gtk/gtkeventbox.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtklabel.h>
+
+#include <glib.h>
+#ifdef LOG
+#undef LOG
+#endif
+#define LOG(...) g_print(__VA_ARGS__); g_print("\n")
+
+#define EPHY_HOTKEYS_EXTENSION_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_HOTKEYS_EXTENSION, EphyHotkeysExtensionPrivate))
+
+struct _EphyHotkeysExtensionPrivate
+{
+	gpointer	dummy;
+	gboolean	display_hotkeys;
+	EphyEmbed	*embed;
+	gpointer	hotkeys_data;
+};
+
+enum
+{
+	PROP_0
+};
+
+static GObjectClass *parent_class = NULL;
+static GType type = 0;
+
+/* Status Bar stuff */
+#define WINDOW_DATA_KEY	"EphyHotkeysExtensionWindowData"
+#define HOTKEYS_ICON	"hotkeys"
+//#define HOTKEYS_ICON_ABSOLUTE_FILENAME "/usr/share/epiphany-browser/icons/hicolor/scalable/status/hotkeys.svg"
+
+/* Mostly used for the Status Bar at the moment */
+typedef struct
+{
+	EphyHotkeysExtension *extension;
+	GtkWidget *evbox;
+	GtkWidget *label;
+} WindowData;
+
+static guint
+ephy_toggle_hotkeys (EphyWindow *window, gboolean value)
+{
+	WindowData* data;
+	EphyHotkeysExtension *extension;
+	EphyEmbed *embed;
+	guint link_count;
+
+	/* Get the data from the window */
+	data = (WindowData *) g_object_get_data (G_OBJECT (window),
+						 WINDOW_DATA_KEY);
+	g_return_val_if_fail (data != NULL, 0);
+
+	/* Get the embed from the window */
+	embed = ephy_window_get_active_embed (window);
+	g_return_val_if_fail (embed != NULL, 0);
+
+	/* Then get the extension from the WindowData */
+	extension = data->extension;
+	g_return_val_if_fail (extension != NULL, 0);
+
+	/* Visibility is set to FALSE since setting it to TRUE is only
+	   needed when enabling and the caller has to do it, by
+	   refreshing the label first, with the returned guint */
+	g_object_set (data->evbox, "visible", FALSE, NULL);
+
+	/* Enable the hotkeys, only if needed */
+	if (extension->priv->display_hotkeys == FALSE
+	    && value == TRUE)
+	{
+		link_count = mozilla_enable_hotkeys (embed,
+						     &extension->priv->hotkeys_data);
+
+		/* As late as possible to ensure that no hotkey
+		   disabling happens when mozilla_enable_hotkeys is
+		   still running */
+		extension->priv->display_hotkeys = TRUE;
+
+		return link_count;
+	}
+
+	/* Disable the hotkeys, only if needed */
+	if (extension->priv->display_hotkeys == TRUE
+	    && value == FALSE)
+	{
+		/* As early as possible to ensure that
+		   mozilla_disable_hotkey isn't run twice in a row */
+		extension->priv->display_hotkeys = FALSE;
+
+		mozilla_disable_hotkeys (embed,
+					 &extension->priv->hotkeys_data);
+
+		g_object_set (data->evbox, "visible", FALSE, NULL);
+
+		return 1;
+	}
+
+	/* No-op otherwise */
+	return 0;
+}
+
+static gboolean
+ephy_hotkeys_ge_content_cb (EphyEmbed *embed,
+			    char *uri,
+			    EphyWindow *window)
+{
+	LOG ("Content changed callback");
+
+	/* Disable hotkeys */
+	ephy_toggle_hotkeys (window, FALSE);
+
+	return TRUE;
+}
+
+static gboolean
+ephy_hotkeys_sync_active_tab (EphyWindow *window,
+			      GParamSpec *pspec,
+			      gpointer dummy)
+{
+	LOG ("Sync active tabs");
+
+	/* Disable hotkeys */
+	ephy_toggle_hotkeys (window, FALSE);
+
+	return TRUE;
+}
+
+
+static gboolean
+ephy_hotkeys_statusbar_icon_clicked_cb (GtkWidget *widget,
+					GdkEventButton *event,
+					EphyWindow *window)
+{
+	if (event->button == 1)
+	{
+		/* ephy_hotkeys_dialog_display (window); */
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void
+ephy_hotkeys_create_statusbar_icon (EphyWindow *window,
+				    WindowData *data)
+{
+	EphyStatusbar *statusbar;
+	GtkWidget *icon;
+	GtkWidget *hbox;
+
+	statusbar = EPHY_STATUSBAR (ephy_window_get_statusbar (window));
+	g_return_if_fail (statusbar != NULL);
+
+	data->evbox = gtk_event_box_new ();
+	gtk_event_box_set_visible_window (GTK_EVENT_BOX (data->evbox), FALSE);
+
+	/* TODO: Check whether there is a nice constant for this */
+	hbox = gtk_hbox_new (FALSE, 5);
+	gtk_container_add (GTK_CONTAINER (data->evbox), hbox);
+	gtk_widget_show (hbox);
+
+	icon = gtk_image_new_from_icon_name (HOTKEYS_ICON, GTK_ICON_SIZE_MENU);
+	gtk_container_add (GTK_CONTAINER (hbox), icon);
+	gtk_widget_show (icon);
+
+	data->label = gtk_label_new (_("0 link found"));
+	gtk_container_add (GTK_CONTAINER (hbox), data->label);
+	gtk_widget_show (data->label);
+
+	ephy_statusbar_add_widget (statusbar, data->evbox);
+
+	/* TODO: Might be nice to disconnect it, although not needed */
+	g_signal_connect_after (data->evbox, "button-press-event",
+				G_CALLBACK (ephy_hotkeys_statusbar_icon_clicked_cb),
+				window);
+}
+
+static void
+ephy_hotkeys_destroy_statusbar_icon (EphyWindow *window,
+				     WindowData *data)
+{
+	EphyStatusbar *statusbar;
+
+	statusbar = EPHY_STATUSBAR (ephy_window_get_statusbar (window));
+	g_return_if_fail (statusbar != NULL);
+
+	g_return_if_fail (data->evbox != NULL);	
+
+	ephy_statusbar_remove_widget (statusbar, GTK_WIDGET (data->evbox));
+}
+
+static void
+ephy_hotkeys_extension_init (EphyHotkeysExtension *extension)
+{
+	extension->priv = EPHY_HOTKEYS_EXTENSION_GET_PRIVATE (extension);
+	extension->priv->display_hotkeys = FALSE;
+	extension->priv->hotkeys_data = NULL;
+
+	LOG ("EphyHotkeysExtension initialising");
+}
+
+static void
+ephy_hotkeys_extension_finalize (GObject *object)
+{
+	EphyHotkeysExtension *extension = EPHY_HOTKEYS_EXTENSION (object);
+
+	LOG ("EphyHotkeysExtension finalising");
+
+	/* No need to ensure extension->priv->hotkeys_data is freed
+	   before this point, since the sync_active_tab callback has
+	   been called automatically */
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+dom_mouse_down_cb (EphyEmbed *embed,
+		   EphyEmbedEvent *event,
+		   EphyHotkeysExtension *extension)
+{
+	gpointer dom_event;
+
+	dom_event = ephy_embed_event_get_dom_event (event);
+
+	LOG ("DOM Event %p", dom_event);
+
+	mozilla_do_something (dom_event);
+
+	return FALSE;
+}
+
+/* Prototype taken from ephy-gesture.c */
+static gboolean
+dom_key_press_cb (GtkWidget *widget,
+		  GdkEventKey *event,
+		  EphyWindow *window)
+{
+	WindowData		*data;
+	EphyHotkeysExtension	*extension;
+	EphyEmbed		*embed;
+	const char*		new_url;
+	guint			link_count;
+
+	/* Get the data from the window */
+	data = (WindowData *) g_object_get_data (G_OBJECT (window), WINDOW_DATA_KEY);
+	g_return_val_if_fail (data != NULL, FALSE);
+
+	/* Then get the extension from the WindowData */
+	extension = data->extension;
+
+	/* Get the embed from the window */
+	embed = ephy_window_get_active_embed (window);
+	g_return_val_if_fail (embed != NULL, FALSE);
+
+	/* Switch the hotkeys display 'on'? */
+	if (extension->priv->display_hotkeys == FALSE
+	    && event->keyval == 'h')
+	{
+		link_count = ephy_toggle_hotkeys (window, TRUE);
+
+		gtk_label_set_label (GTK_LABEL (data->label),
+				     g_strdup_printf (_("%u hotlinks"), link_count));
+
+		g_object_set (data->evbox, "visible", TRUE, NULL);
+
+		return TRUE;
+	}
+
+	/* Switch the hotkeys display 'off'? */
+	if (extension->priv->display_hotkeys == TRUE
+	    && event->keyval == GDK_Escape)
+	{
+		ephy_toggle_hotkeys (window, FALSE);
+		return TRUE;
+	}
+
+	/* Hotkeys 'on' and key in the mapping? */
+	if (extension->priv->display_hotkeys == TRUE)
+	{
+		new_url = mozilla_is_a_hotkey (event->keyval,
+					       &extension->priv->hotkeys_data);
+		if (new_url)
+		{
+			ephy_toggle_hotkeys (window, FALSE);
+			ephy_embed_load_url (embed, new_url);
+			g_free (new_url);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static void
+impl_attach_window (EphyExtension *ext,
+		    EphyWindow *window)
+{
+	WindowData *data;
+
+	LOG ("EphyHotkeysExtension attach_window");
+
+	/* Store data */
+	data = g_new (WindowData, 1);
+	data->extension = (EphyHotkeysExtension *) ext;
+
+	g_object_set_data_full (G_OBJECT (window), WINDOW_DATA_KEY, data,
+				(GDestroyNotify) g_free);
+
+	/* Create the status bar icon */
+	ephy_hotkeys_create_statusbar_icon (window, data);
+
+	/* Register for tab switch events */
+	g_signal_connect_after (window, "notify::active-tab",
+				G_CALLBACK (ephy_hotkeys_sync_active_tab), NULL);
+}
+
+static void
+impl_detach_window (EphyExtension *ext,
+		    EphyWindow *window)
+{
+	WindowData *data;
+
+	LOG ("EphyHotkeysExtension detach_window");
+
+	/* Remove the data */
+	data = (WindowData *) g_object_get_data (G_OBJECT (window), WINDOW_DATA_KEY);
+	g_return_if_fail (data != NULL);
+
+	/* Remove the status bar icon */
+	ephy_hotkeys_destroy_statusbar_icon (window, data);
+
+	/* Destroy data (new association => destruction of the old one */
+	g_object_set_data (G_OBJECT (window), WINDOW_DATA_KEY, NULL);
+
+	/* Remove the tab switch notification */
+	g_signal_handlers_disconnect_by_func
+		(window, G_CALLBACK (ephy_hotkeys_sync_active_tab), NULL);
+}
+
+static void
+impl_attach_tab (EphyExtension *ext,
+		 EphyWindow *window,
+		 EphyTab *tab)
+{
+	EphyEmbed *embed;
+
+	LOG ("impl_attach_tab");
+
+	embed = ephy_tab_get_embed (tab);
+	g_return_if_fail (EPHY_IS_EMBED (embed));
+
+	/* TODO: Check the usage of connect/connect_after */
+	g_signal_connect (embed, "ge_dom_mouse_down",
+			  G_CALLBACK (dom_mouse_down_cb), ext);
+
+	g_signal_connect (embed, "ge-search-key-press",
+			  G_CALLBACK (dom_key_press_cb), window);
+
+	g_signal_connect_after (embed, "ge-content-change",
+				G_CALLBACK (ephy_hotkeys_ge_content_cb), window);
+
+	/* Force sync */
+	ephy_hotkeys_sync_active_tab (window, NULL, NULL);
+}
+
+static void
+impl_detach_tab (EphyExtension *ext,
+		 EphyWindow *window,
+		 EphyTab *tab)
+{
+	EphyEmbed *embed;
+
+	LOG ("impl_detach_tab");
+
+	embed = ephy_tab_get_embed (tab);
+	g_return_if_fail (EPHY_IS_EMBED (embed));
+
+	g_signal_handlers_disconnect_by_func
+		(embed, G_CALLBACK (dom_mouse_down_cb), ext);
+
+	/* TODO: There's a missing disconnect here: dom_key_press_cb
+	   but that isn't very important, besides connect/disconnect
+	   symmetry */
+
+	g_signal_handlers_disconnect_by_func
+		(embed, G_CALLBACK (ephy_hotkeys_ge_content_cb), window);
+
+	/* Force sync */
+	ephy_hotkeys_sync_active_tab (window, NULL, NULL);
+}
+
+static void
+ephy_hotkeys_extension_iface_init (EphyExtensionIface *iface)
+{
+	iface->attach_window = impl_attach_window;
+	iface->detach_window = impl_detach_window;
+	iface->attach_tab = impl_attach_tab;
+	iface->detach_tab = impl_detach_tab;
+}
+
+static void
+ephy_hotkeys_extension_class_init (EphyHotkeysExtensionClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class->finalize = ephy_hotkeys_extension_finalize;
+
+	g_type_class_add_private (object_class, sizeof (EphyHotkeysExtensionPrivate));
+}
+
+GType
+ephy_hotkeys_extension_get_type (void)
+{
+	return type;
+}
+
+GType
+ephy_hotkeys_extension_register_type (GTypeModule *module)
+{
+	const GTypeInfo our_info =
+	{
+		sizeof (EphyHotkeysExtensionClass),
+		NULL, /* base_init */
+		NULL, /* base_finalize */
+		(GClassInitFunc) ephy_hotkeys_extension_class_init,
+		NULL,
+		NULL, /* class_data */
+		sizeof (EphyHotkeysExtension),
+		0, /* n_preallocs */
+		(GInstanceInitFunc) ephy_hotkeys_extension_init
+	};
+
+	const GInterfaceInfo extension_info =
+	{
+		(GInterfaceInitFunc) ephy_hotkeys_extension_iface_init,
+		NULL,
+		NULL
+	};
+
+	type = g_type_module_register_type (module,
+					    G_TYPE_OBJECT,
+					    "EphyHotkeysExtension",
+					    &our_info, 0);
+
+	g_type_module_add_interface (module,
+				     type,
+				     EPHY_TYPE_EXTENSION,
+				     &extension_info);
+
+	return type;
+}
diff -urN -a --binary trunk/extensions/hotkeys/ephy-hotkeys-extension.h epiphany-extensions-trunk/extensions/hotkeys/ephy-hotkeys-extension.h
--- trunk/extensions/hotkeys/ephy-hotkeys-extension.h	1970-01-01 00:00:00.000000000 +0000
+++ epiphany-extensions-trunk/extensions/hotkeys/ephy-hotkeys-extension.h	2007-09-03 04:34:18.000000000 +0000
@@ -0,0 +1,66 @@
+/*
+ *  Copyright © 2007 Cyril Brulebois
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ *  Based on the following file from the sample-mozilla extension
+ *  (released under the same license).
+ *
+ *  $Id: ephy-sample2-extension.h 1376 2006-09-13 19:01:42Z chpe $
+ *
+ *  Copyright © 2003 Marco Pesenti Gritti
+ *  Copyright © 2003 Christian Persch
+ */
+
+#ifndef EPHY_HOTKEYS_EXTENSION_H
+#define EPHY_HOTKEYS_EXTENSION_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_HOTKEYS_EXTENSION		(ephy_hotkeys_extension_get_type ())
+#define EPHY_HOTKEYS_EXTENSION(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), EPHY_TYPE_HOTKEYS_EXTENSION, EphyHotkeysExtension))
+#define EPHY_HOTKEYS_EXTENSION_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST((k), EPHY_TYPE_HOTKEYS_EXTENSION, EphyHotkeysExtensionClass))
+#define EPHY_IS_HOTKEYS_EXTENSION(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), EPHY_TYPE_HOTKEYS_EXTENSION))
+#define EPHY_IS_HOTKEYS_EXTENSION_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), EPHY_TYPE_HOTKEYS_EXTENSION))
+#define EPHY_HOTKEYS_EXTENSION_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), EPHY_TYPE_HOTKEYS_EXTENSION, EphyHotkeysExtensionClass))
+
+typedef struct _EphyHotkeysExtension		EphyHotkeysExtension;
+typedef struct _EphyHotkeysExtensionClass	EphyHotkeysExtensionClass;
+typedef struct _EphyHotkeysExtensionPrivate	EphyHotkeysExtensionPrivate;
+
+struct _EphyHotkeysExtensionClass
+{
+	GObjectClass parent_class;
+};
+
+struct _EphyHotkeysExtension
+{
+	GObject parent_instance;
+
+	/*< private >*/
+	EphyHotkeysExtensionPrivate *priv;
+};
+
+GType	ephy_hotkeys_extension_get_type		(void);
+
+GType	ephy_hotkeys_extension_register_type	(GTypeModule *module);
+
+G_END_DECLS
+
+#endif
diff -urN -a --binary trunk/extensions/hotkeys/hotkeys-mozilla.c epiphany-extensions-trunk/extensions/hotkeys/hotkeys-mozilla.c
--- trunk/extensions/hotkeys/hotkeys-mozilla.c	1970-01-01 00:00:00.000000000 +0000
+++ epiphany-extensions-trunk/extensions/hotkeys/hotkeys-mozilla.c	2007-09-03 04:34:18.000000000 +0000
@@ -0,0 +1,50 @@
+/*
+ *  Copyright © 2007 Cyril Brulebois
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ *  Based on the following file from the sample-mozilla extension
+ *  (released under the same license).
+ *
+ *  $Id: sample-mozilla.c 1376 2006-09-13 19:01:42Z chpe $
+ *
+ *  Copyright © 2003 Marco Pesenti Gritti
+ *  Copyright © 2003 Christian Persch
+ */
+
+#include "config.h"
+
+#include "ephy-hotkeys-extension.h"
+#include "ephy-debug.h"
+
+#include <gmodule.h>
+#include <glib/gi18n-lib.h>
+
+G_MODULE_EXPORT GType register_module (GTypeModule *module);
+
+G_MODULE_EXPORT GType
+register_module (GTypeModule *module)
+{
+	LOG ("Registering EphyHotkeysExtension");
+
+#ifdef ENABLE_NLS
+       /* Initialise the i18n stuff */
+        bindtextdomain (GETTEXT_PACKAGE, EPHY_EXTENSIONS_LOCALEDIR);
+        bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");	
+#endif /* ENABLE_NLS */
+
+	return ephy_hotkeys_extension_register_type (module);
+}
diff -urN -a --binary trunk/extensions/hotkeys/hotkeys.ephy-extension.in.in epiphany-extensions-trunk/extensions/hotkeys/hotkeys.ephy-extension.in.in
--- trunk/extensions/hotkeys/hotkeys.ephy-extension.in.in	1970-01-01 00:00:00.000000000 +0000
+++ epiphany-extensions-trunk/extensions/hotkeys/hotkeys.ephy-extension.in.in	2007-09-03 04:34:18.000000000 +0000
@@ -0,0 +1,11 @@
+[Epiphany Extension]
+_Name=Hotkeys (Mozilla)
+_Description=A hotkeys extension (with mozilla backend) to ease keyboard-driven navigation
+Authors=Cyril Brulebois <cyril.brulebois@enst-bretagne.fr>
+Version=1
+URL=http://www.ikibiki.org/projects/epiphany-accessibility/
+
+
+[Loader]
+Type=shlib
+Library=%EXTENSION_DIR%/%LIBRARY%
diff -urN -a --binary trunk/extensions/hotkeys/mozilla/Makefile.am epiphany-extensions-trunk/extensions/hotkeys/mozilla/Makefile.am
--- trunk/extensions/hotkeys/mozilla/Makefile.am	1970-01-01 00:00:00.000000000 +0000
+++ epiphany-extensions-trunk/extensions/hotkeys/mozilla/Makefile.am	2007-09-03 04:34:18.000000000 +0000
@@ -0,0 +1,25 @@
+noinst_LTLIBRARIES = libhotkeysmozillamozilla.la
+
+libhotkeysmozillamozilla_la_SOURCES = 	\
+	mozilla-hotkeys.cpp		\
+	mozilla-hotkeys.h
+
+mozilla_include_subdirs = \
+	.		\
+	content		\
+	docshell	\
+	dom		\
+	xpcom
+
+libhotkeysmozillamozilla_la_CPPFLAGS = \
+	-I..			\
+	-I$(top_srcdir)/include	\
+	$(addprefix -I$(MOZILLA_INCLUDE_ROOT)/,$(mozilla_include_subdirs))	\
+	-DALLOW_PRIVATE_API		\
+	-DSHARE_DIR=\"$(pkgdatadir)\"   \
+	$(AM_CPPFLAGS)
+
+libhotkeysmozillamozilla_la_CXXFLAGS = \
+	$(MOZILLA_COMPONENT_CFLAGS)	\
+	$(EPIPHANY_DEPENDENCY_CFLAGS) 	\
+	$(AM_CXXFLAGS)
diff -urN -a --binary trunk/extensions/hotkeys/mozilla/mozilla-hotkeys.cpp epiphany-extensions-trunk/extensions/hotkeys/mozilla/mozilla-hotkeys.cpp
--- trunk/extensions/hotkeys/mozilla/mozilla-hotkeys.cpp	1970-01-01 00:00:00.000000000 +0000
+++ epiphany-extensions-trunk/extensions/hotkeys/mozilla/mozilla-hotkeys.cpp	2007-09-03 04:34:18.000000000 +0000
@@ -0,0 +1,532 @@
+/*
+ *  Copyright © 2007 Cyril Brulebois
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ *  Based on the following file from the sample-mozilla extension
+ *  (released under the same license).
+ *
+ *  $Id: mozilla-sample.cpp 1415 2006-12-23 21:22:06Z jframeau $
+ *
+ *  Copyright © 2003 Marco Pesenti Gritti
+ *  Copyright © 2003 Christian Persch
+ */
+
+#include "mozilla-config.h"
+#include "config.h"
+
+#include <glib.h>
+
+#include <nsCOMPtr.h>
+#include <nsIDOMDocument.h>
+#include <nsIDOMEvent.h>
+#include <nsIDOMHTMLAnchorElement.h>
+#include <nsIDOMMouseEvent.h>
+#include <nsIDOMNodeList.h>
+#include <nsIDOMWindow.h>
+#include <nsIDOMNSHTMLElement.h>
+#include <nsIWebBrowser.h>
+#include <nsStringAPI.h>
+
+#include <gtkmozembed.h>
+#include <gtkmozembed_internal.h> /* gtk_moz_embed_get_nsIWebBrowser */
+
+#include <gdk/gdkkeysyms.h> /* GDK_*_Enter */
+
+#include "mozilla-hotkeys.h"
+
+
+/* Arbitrary strings to emphasize the hotkeys */
+
+const nsString BEGIN_NORMAL_ITEM   = NS_LITERAL_STRING("<span class=\"epiphany-extensions-hotkeys-normal\">");
+const nsString BEGIN_SELECTED_ITEM = NS_LITERAL_STRING("<span class=\"epiphany-extensions-hotkeys-selected\">");
+const nsString END_NORMAL_ITEM     = NS_LITERAL_STRING("</span>");
+const nsString END_SELECTED_ITEM   = NS_LITERAL_STRING("</span>");
+
+
+
+/* The following data structures are private,
+   and don't belong to mozilla-hotkeys.h */
+
+struct _MozillaHotkeysNode
+{
+	guint			keyval;
+	guint			offset;
+	nsString		*hotkey_html;
+	nsString		*backup_html;
+	nsString		*href;
+	nsIDOMNSHTMLElement	*element;
+};
+
+typedef struct _MozillaHotkeysNode	MozillaHotkeysNode;
+
+struct _MozillaHotkeysData
+{
+	GSList*			nodes;
+	GHashTable*		mapping; /* keyval -> MozillaHotKeysNode array */
+	MozillaHotkeysNode*	previous_node; /* used for disabling the emphasis */
+	guint			previous_keyval;
+	guint			current_offset;
+};
+
+typedef struct _MozillaHotkeysData	MozillaHotkeysData;
+
+
+
+/* The following functions are private, and
+   don't belong to mozilla-hotkeys.h */
+
+gboolean
+mozilla_emphasize_node (MozillaHotkeysNode* node, gboolean selected)
+{
+	nsString new_inner_html;
+	/* A copy/clone + replace might do the job more nicely than
+	   using substrings */
+	new_inner_html.Append (Substring (*node->backup_html, 0, node->offset));
+	new_inner_html.Append (selected == TRUE ? BEGIN_SELECTED_ITEM : BEGIN_NORMAL_ITEM);
+	new_inner_html.Append (Substring (*node->backup_html, node->offset, 1));
+	new_inner_html.Append (selected == TRUE ? END_SELECTED_ITEM : END_NORMAL_ITEM);
+	new_inner_html.Append (Substring (*node->backup_html, node->offset + 1));
+
+	node->hotkey_html->Assign (new_inner_html);
+	node->element->SetInnerHTML (*node->hotkey_html);
+
+	/* At the moment, Blur() unconditionally. It will be better to
+	   use an ENUM instead of a gboolean, so as to be able to pass
+	   one of the following:
+	     EMPHASIS_NORMAL, EMPHASIS_BLUR, EMPHASIS_FOCUS
+	*/
+
+	/* Actually, just make the page scroll down if needed, do no
+	   leave any trace */
+	if (selected == TRUE)
+	{
+		/* Query the nsIDOMHTMLAnchorElement interface */
+		nsCOMPtr<nsIDOMHTMLAnchorElement> link (do_QueryInterface (node->element));
+		NS_ENSURE_TRUE (link, FALSE);
+
+		link->Focus();
+		link->Blur();
+	}
+
+	return TRUE;
+}
+
+gboolean
+remove_hash_table_entry (gpointer key, gpointer value, gpointer user_data)
+{
+	guint *guint_key = (guint *) key;
+	g_free (guint_key);
+
+	GPtrArray *array_value = (GPtrArray *) value;
+	g_ptr_array_free (array_value, TRUE);
+
+	return TRUE;
+}
+
+
+
+/* The following functions are public */
+
+void
+mozilla_do_something (gpointer dom_event)
+{
+	nsCOMPtr<nsIDOMEvent> ev = static_cast<nsIDOMEvent*>(dom_event);
+	NS_ENSURE_TRUE (ev,);
+	nsCOMPtr<nsIDOMMouseEvent> mev = do_QueryInterface (ev);
+	NS_ENSURE_TRUE (mev,);
+
+	nsresult rv;
+	PRUint16 button;
+	rv = mev->GetButton (&button);
+	NS_ENSURE_SUCCESS (rv,);
+
+	g_print ("Button %u\n", button);
+}
+
+guint
+mozilla_enable_hotkeys (EphyEmbed *embed, gpointer *data)
+{
+	g_print("Enabling hotkeys\n");
+
+	/* Plan:
+	   1. Browse the DOM.
+	   2. Extract links, create a backup.
+	   3. Construct the hotkey -- url mapping.
+	   4. Modify the DOM.
+	   5. Make the Embed refresh the page if needed.
+	   6. Add proper return value testing everywhere...
+	*/
+
+	/* Sanity check */
+	NS_ENSURE_TRUE (*data == NULL, 0);
+
+	/* Browse the DOM */
+	nsCOMPtr<nsIWebBrowser> browser;
+	gtk_moz_embed_get_nsIWebBrowser (GTK_MOZ_EMBED (embed),
+					 getter_AddRefs (browser));
+	NS_ENSURE_TRUE (browser, 0);
+
+	nsCOMPtr<nsIDOMWindow> dom_window;
+	browser->GetContentDOMWindow (getter_AddRefs (dom_window));
+	NS_ENSURE_TRUE (dom_window, 0);
+
+	nsCOMPtr<nsIDOMDocument> doc;
+	dom_window->GetDocument (getter_AddRefs (doc));
+	NS_ENSURE_TRUE (doc, 0);
+
+	nsCOMPtr<nsIDOMNodeList> link_list;
+	doc->GetElementsByTagName(NS_LITERAL_STRING("a"),
+				  getter_AddRefs(link_list));
+
+	/* Check the list exists... */
+	if (!link_list)
+		return 0;
+
+	PRUint32 count;
+	nsresult result;
+	result = link_list->GetLength(&count);
+	NS_ENSURE_SUCCESS (result, 0);
+
+	/* ... and that it isn't empty */
+	if (count == 0)
+		return 0;
+
+	/* Create the data object, it is needed */
+	MozillaHotkeysData *hk_data;
+	hk_data = g_new (MozillaHotkeysData, 1);
+	hk_data->nodes			= (GSList *) NULL;
+	hk_data->previous_node		= (MozillaHotkeysNode *) NULL;
+	hk_data->previous_keyval	= 0;
+	hk_data->current_offset		= 0;
+
+	hk_data->mapping = g_hash_table_new (g_int_hash, g_int_equal);
+
+	/* Browse the links */
+	guint link_count=0;
+	for (PRUint32 i=0; i<count; i++)
+	{
+		nsCOMPtr<nsIDOMNode> link_node;
+		link_list->Item(i, getter_AddRefs(link_node));
+
+		nsCOMPtr<nsIDOMHTMLAnchorElement> link (do_QueryInterface (link_node));
+		nsCOMPtr<nsIDOMNSHTMLElement> element (do_QueryInterface (link_node));
+		if (link && element)
+		{
+			/* Get the pointed href */
+			nsString href;
+			result = link->GetHref (href);
+			NS_ENSURE_SUCCESS (result, 0);
+
+			/* Get the inner HTML */
+			nsString inner_html;
+			result = element->GetInnerHTML (inner_html);
+			NS_ENSURE_SUCCESS (result, 0);
+
+			/* Create the actual hotkey */
+			MozillaHotkeysNode *hotkey_node = g_new (MozillaHotkeysNode, 1);
+			hotkey_node->keyval       = 0;
+
+			hotkey_node->hotkey_html  = new nsString();
+			hotkey_node->hotkey_html->Assign (inner_html);
+
+			hotkey_node->backup_html  = new nsString();
+			hotkey_node->backup_html->Assign (inner_html);
+
+			hotkey_node->href         = new nsString();
+			hotkey_node->href->Assign (href);
+
+			hotkey_node->element      = element;
+			NS_ADDREF(hotkey_node->element);
+
+			/* Loop on the inner HTML to find a character
+			   appopriate for hotkey-ing. Exit roughly if
+			   there's a '<' there. */
+			gunichar keyval = 0;
+			guint offset = 0;
+			guint length = inner_html.Length();
+
+			/* Just cycle to the next item if there's no
+			   inner HTML */
+			if (length == 0)
+				continue;
+
+			while (keyval==0
+			       && offset < length)
+			{
+				/* Check whether this is the beginning
+				   of a tag, and eventually skip it
+				 */
+
+				if (Substring (inner_html, offset, 1).Equals (NS_LITERAL_STRING("<")))
+				{
+					/* Search for a matching '>' */
+					guint i;
+					for (i=offset; i<length; i++)
+						if (Substring (inner_html, i, 1).Equals (NS_LITERAL_STRING (">")))
+							break;
+
+					if (i < length)
+						/* Found, skip this tag */
+						offset = i+1;
+					else
+					{
+						/* No tag, skip this character.
+						   Shouldn't happen!
+						 */
+						offset++;
+						g_print ("WARNING: Strange fake tag, please report!\n");
+					}
+
+					/* Not needed, but left for symmetry
+					   with the other tests
+					 */
+					continue;
+				}
+
+				/* Check whether this it the beginning of an
+				   entity, and eventually skip it.
+
+				   NB: The whole string is encoded using
+				   entities.
+				 */
+
+				if (Substring (inner_html, offset, 1).Equals (NS_LITERAL_STRING ("&")))
+				{
+					/* Search for a matching ';' */
+					guint i;
+					for (i=offset; i<length; i++)
+						if (Substring (inner_html, i, 1).Equals (NS_LITERAL_STRING (";")))
+							break;
+
+					if (i < length)
+						/* Found, skip this entity */
+						offset = i+1;
+					else
+					{
+						/* No entity, skip this character.
+						   Shouldn't happen!
+						 */
+						offset++;
+						g_print ("WARNING: Strange fake entity, please report!\n");
+					}
+
+					/* Needed here, the next character
+					   could be a '<', which is tested
+					   earlier in the loop
+					 */
+					continue;
+				}
+
+				/* Extract the Nth character. To try
+				   and avoid conversion problems,
+				   another nsCString help is used */
+				nsCString c_inner_char;
+				NS_UTF16ToCString (Substring (inner_html, offset, 1),
+						   NS_CSTRING_ENCODING_UTF8,
+						   c_inner_char);
+
+				/* Sanity check */
+				if (c_inner_char.Length() == 0)
+				{
+					g_print ("WARNING: An XPCOM string conversion gave an awful result, please report!\n");
+					break;
+				}
+
+				/* Get the uppercase'd UTF-8 character */
+				keyval = g_utf8_get_char (c_inner_char.get ());
+				keyval = g_unichar_toupper (keyval);
+
+				/* Try the next character if needed */
+				if (g_unichar_isalnum (keyval) == FALSE)
+				{
+					keyval = 0;
+					offset++;
+				}
+			}
+
+			/* Only consider alphanumerical */
+			if (keyval != 0)
+			{
+				/* Attempt a lookup */
+				GPtrArray *ptr_array = (GPtrArray *) g_hash_table_lookup (hk_data->mapping,
+											  (guint *) &keyval);
+				if (ptr_array == NULL)
+				{
+					/* Create a new pointer array */
+					guint *keyval_copy = g_new (guint, 1);
+					*keyval_copy = keyval;
+					ptr_array = g_ptr_array_new ();
+					g_hash_table_insert (hk_data->mapping,
+							     keyval_copy,
+							     ptr_array);
+				}
+
+				/* Append */
+				g_ptr_array_add (ptr_array, hotkey_node);
+
+				/* Only done here since the keyval might be a modified one */
+				hotkey_node->keyval = keyval;
+				hotkey_node->offset = offset;
+
+				/* Normal emphasis (not 'selected') */
+				mozilla_emphasize_node (hotkey_node, FALSE);
+
+				link_count++;
+			}
+			else
+			{
+				nsCString c_inner_html;
+				NS_UTF16ToCString (Substring (inner_html, offset, 1),
+						   NS_CSTRING_ENCODING_UTF8,
+						   c_inner_html);
+				g_print ("** No hotkey added for %s\n",
+					 c_inner_html.get());
+			}
+
+			/* Prepend finally */
+			hk_data->nodes = g_slist_prepend ((GSList*) hk_data->nodes,
+							  hotkey_node);
+		} /* if (link && element) */
+	} /* for */
+
+
+	/* Be efficient, prepend everytime, reverse once */
+	hk_data->nodes = g_slist_reverse ((GSList*) hk_data->nodes);
+
+	/* Let the caller know about our object */
+	*data = hk_data;
+
+	return link_count;
+}
+
+gboolean
+mozilla_disable_hotkeys (EphyEmbed *embed, gpointer *data)
+{
+	g_print("Disabling hotkeys\n");
+
+	MozillaHotkeysData *hk_data = (MozillaHotkeysData *) *data;
+	GSList *node = hk_data->nodes;
+	while (node != NULL)
+	{
+		/* Repair the DOM */
+		MozillaHotkeysNode *hk_node = (MozillaHotkeysNode *) node->data;
+		hk_node->element->SetInnerHTML (*hk_node->backup_html);
+		NS_RELEASE (hk_node->element);
+
+		/* Free the elements, then the container */
+		delete (hk_node->hotkey_html);
+		delete (hk_node->backup_html);
+		delete (hk_node->href);
+		delete (hk_node);
+
+		node = node->next;
+	}
+
+	/* Free the pointer arrays, then the hash table itself */
+	g_hash_table_foreach_remove (hk_data->mapping,
+				     remove_hash_table_entry, NULL);
+	g_hash_table_destroy (hk_data->mapping);
+
+	/* GSList* empty, GHashTable* empty, final deletion */
+	g_free (hk_data);
+
+	/* Let the caller know */
+	*data = NULL;
+
+	return TRUE;
+}
+
+const char*
+mozilla_is_a_hotkey (guint keyval, gpointer *data)
+{
+	/* Convention used when computing and storing the keyvals */
+	keyval = g_unichar_toupper (keyval);
+
+	MozillaHotkeysData *hk_data = (MozillaHotkeysData *) *data;
+
+	/* Attempt to validate a previously-selected node if Enter is pressed */
+	if ((keyval == GDK_KP_Enter)
+	    || (keyval == GDK_ISO_Enter)
+	    || (keyval == GDK_3270_Enter)
+	    || (keyval == GDK_Return))
+	{
+		/* Check whether there was a selected item */
+		if (hk_data->previous_node != NULL)
+		{
+			/* nsString to gchar conversion */
+			nsCString c_href;
+			NS_UTF16ToCString (*hk_data->previous_node->href,
+					   NS_CSTRING_ENCODING_UTF8, c_href);
+			return g_strdup (c_href.get ());
+		}
+
+		g_print ("ENTER hitted\n");
+		return NULL;
+	}
+
+	/* Disable the 'selected' emphasis on the previous node,
+	   if any, and forget about it  */
+	if (hk_data->previous_node != NULL)
+	{
+		mozilla_emphasize_node (hk_data->previous_node, FALSE);
+		hk_data->previous_node = NULL;
+	}
+
+	/* Attempt a lookup */
+	GPtrArray *lookup = (GPtrArray *) g_hash_table_lookup (hk_data->mapping,
+							       (guint *) &keyval);
+	if (lookup == NULL)
+		return NULL;
+
+	/* Only one entry? */
+	if (lookup->len == 1)
+	{
+		/* Use the first (and only) item */
+		MozillaHotkeysNode *node = (MozillaHotkeysNode *) g_ptr_array_index (lookup, 0);
+
+		/* nsString to gchar conversion */
+		nsCString c_href;
+		NS_UTF16ToCString (*node->href, NS_CSTRING_ENCODING_UTF8,
+				   c_href);
+		return g_strdup (c_href.get ());
+	}
+
+	/* Same key as the last one? */
+	if (hk_data->previous_keyval == keyval)
+	{
+		/* Cycle to the next item */
+		hk_data->current_offset = (hk_data->current_offset + 1) % lookup->len;
+
+		MozillaHotkeysNode *node = (MozillaHotkeysNode *) g_ptr_array_index (lookup,
+										     hk_data->current_offset);
+		mozilla_emphasize_node(node, TRUE);
+		hk_data->previous_node = node;
+
+		return NULL;
+	}
+	else
+	{
+		/* Use the first item */
+		MozillaHotkeysNode *node = (MozillaHotkeysNode *) g_ptr_array_index (lookup, 0);
+		mozilla_emphasize_node(node, TRUE);
+		hk_data->current_offset  = 0;
+		hk_data->previous_keyval = keyval;
+		hk_data->previous_node   = node;
+
+		return NULL;
+	}
+}
diff -urN -a --binary trunk/extensions/hotkeys/mozilla/mozilla-hotkeys.h epiphany-extensions-trunk/extensions/hotkeys/mozilla/mozilla-hotkeys.h
--- trunk/extensions/hotkeys/mozilla/mozilla-hotkeys.h	1970-01-01 00:00:00.000000000 +0000
+++ epiphany-extensions-trunk/extensions/hotkeys/mozilla/mozilla-hotkeys.h	2007-09-03 04:34:18.000000000 +0000
@@ -0,0 +1,43 @@
+/*
+ *  Copyright © 2007 Cyril Brulebois
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ *  Based on the following file from the sample-mozilla extension
+ *  (released under the same license).
+ *
+ *  $Id: mozilla-sample.h 1376 2006-09-13 19:01:42Z chpe $
+ *
+ *  Copyright © 2003 Marco Pesenti Gritti
+ *  Copyright © 2003 Christian Persch
+ */
+
+#ifndef MOZILLA_HOTKEYS_H
+#define MOZILLA_HOTKEYS_H
+
+#include <glib.h>
+#include <epiphany/ephy-extension.h> /* EmphyEmbed */
+
+G_BEGIN_DECLS
+
+void		mozilla_do_something (gpointer dom_event);
+guint		mozilla_enable_hotkeys (EphyEmbed *embed, gpointer *mapping);
+gboolean	mozilla_disable_hotkeys (EphyEmbed *embed, gpointer *mapping);
+const char*	mozilla_is_a_hotkey (guint keyval, gpointer *mapping);
+
+G_END_DECLS
+
+#endif
diff -urN -a --binary trunk/help/C/epiphany-extensions.xml epiphany-extensions-trunk/help/C/epiphany-extensions.xml
--- trunk/help/C/epiphany-extensions.xml	2007-09-03 04:33:40.000000000 +0000
+++ epiphany-extensions-trunk/help/C/epiphany-extensions.xml	2007-09-03 04:34:18.000000000 +0000
@@ -872,7 +872,88 @@
 			</sect3>
 		</sect2>
 	</sect1>
-	
+
+	<sect1 id="epi-ext-hotkeys">
+		<title>Hotkeys</title>
+
+		<sect2 id="epi-ext-hotkeys-intro">
+			<title>Introduction</title>
+
+			<para>Hotkeys extension is targeted at making easier
+			the navigation through links, using only the
+			keyboard.</para>
+		</sect2>
+
+		<sect2 id="epi-ext-hotkeys-usage">
+			<title>Usage</title>
+
+			<para>When a given key is pressed (currently the <keycap>h</keycap> key,
+			which is not configurable yet), the hotkeys mode gets enabled, and an icon
+			appears in the statusbar, as well as the number of links available through
+			the hotkeys. Once the extension has been configured, the links as well as
+			their hotkeys get emphasized. The hotkeys mode remains enabled until the
+			<keycap>ESC</keycap> key is pressed.</para>
+
+			<para>Then on each key press, if they're a single link matching, this URL
+			gets loaded. If they are several, the first link is highlighted, and each
+			time the same key is pressed, the highlighted link cycles through the
+			available links matching it. The currently highlighted link is loaded when
+			the <keycap>Enter</keycap> key is pressed.</para>
+		</sect2>
+
+		<sect2 id="epi-ext-hotkeys-config">
+			<title>Configuration</title>
+
+			<para>The extension uses CSS properties to emphasize the hotkeys and the
+			highlighted link (if any). Since it might be uneasy to disable some
+			hardcoded values, no default CSS property is provided, but rather an
+			an example below, as well as instructions to set such CSS properties.</para>
+
+			<para>First, choose <menuchoice> <guimenu>Edit</guimenu>
+			<guimenuitem>Preferences</guimenuitem></menuchoice>. Then move to the
+			<guilabel>Fonts and Styles</guilabel> tab.</para>
+
+			<para>You have to tick the <guilabel>Use custom stylesheet</guilabel>
+			checkbox, and then edit the custom stylesheet using the
+			<guibutton>Edit Stylesheet</guibutton> button.</para>
+
+			<para>An example of configuration follows, featuring hotkeys displayed as
+			white text on black background, with a grey border, toggling to black text
+			on white background when the hotkey is highlighted.</para>
+
+			<programlisting>
+span.epiphany-extensions-hotkeys-normal {
+	text-decoration: none;
+	padding: 3px;
+	border: solid 2px grey;
+	font-weight: bold;
+	background-color: black;
+	color: white;
+}
+
+span.epiphany-extensions-hotkeys-selected {
+	text-decoration: none;
+	padding: 3px;
+	border: solid 2px grey;
+	font-weight: bold;
+	background-color: white;
+	color: black;
+}</programlisting>
+		</sect2>
+
+		<sect2 id="epi-ext-hotkeys-limitation">
+			<title>Limitations</title>
+
+			<para>It is not (yet) possible to grab some keys like modifiers (e.g. the
+			<keycap>Control</keycap> key), which might better fit this purpose than the
+			current key.</para>
+
+			<para>Also, when a textbox (in an HTML form) has the focus, it is needed to
+			move the focus away from it to be able to enable the hotkeys mode, which
+			can be achieved by pressing the <keycap>Tab</keycap>.</para>
+		</sect2>
+	</sect1>
+
 	<sect1 id="epi-ext-javaconsole">
 		<title>Java Console</title>	
 
diff -urN -a --binary trunk/help/ChangeLog epiphany-extensions-trunk/help/ChangeLog
--- trunk/help/ChangeLog	2007-09-03 04:33:40.000000000 +0000
+++ epiphany-extensions-trunk/help/ChangeLog	2007-09-03 04:34:18.000000000 +0000
@@ -1,3 +1,9 @@
+2007-09-03  Cyril Brulebois <cyril.brulebois@enst-bretagne.fr>
+
+	* C/epiphany-extensions.xml:
+
+	Document the hotkeys extension.
+
 2007-05-04  Daniel Nylander <po@danielnylander.se>
 
 	* sv/sv.po: Updated Swedish translation.
