<?xml version="1.0"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1//EN" "http://www.oasis-open.org/docbook/xml/4.1/docbookx.dtd" [
]>
<book>
	<!-- {{{ bookinfo -->
	<bookinfo>
		<title>Writing Epiphany Extensions</title>
		<author>
			<firstname>Adam</firstname>
			<surname>Hooper</surname>
			<affiliation>
				<address>
					<email>adamh@densi.com</email>
				</address>
			</affiliation>
		</author>

		<revhistory>
			<revision>
				<revnumber>0.07</revnumber>
				<date>29-Jan-2005</date>
				<authorinitials>ah</authorinitials>
				<revremark>
					Inaugurate Pyphany.
				</revremark>
			</revision>
			<revision>
				<revnumber>0.06</revnumber>
				<date>30-Oct-2004</date>
				<authorinitials>ah</authorinitials>
				<revremark>
					Put titles on examples, and ensure all examples work with unloading.
				</revremark>
			</revision>
			<revision>
				<revnumber>0.05</revnumber>
				<date>30-Oct-2004</date>
				<authorinitials>ah</authorinitials>
				<revremark>
					Replaced ephyplugins with epiphany-extensions. Got rid of now-incorrect statements. Mention copy-template script, which saves the user from copy/pasting everything.
				</revremark>
			</revision>
			<revision>
				<revnumber>0.04</revnumber>
				<date>14-Jan-2004</date>
				<authorinitials>ah</authorinitials>
				<revremark>
					Replaced Savannah links to point to the page on Epiphany's website.
				</revremark>
			</revision>
			<revision>
				<revnumber>0.03</revnumber>
				<date>13-Jan-2004</date>
				<authorinitials>ah</authorinitials>
				<revremark>
					s/Firdbird/Firebird/
				</revremark>
			</revision>
			<revision>
				<revnumber>0.02</revnumber>
				<date>03-Jan-2004</date>
				<authorinitials>ah</authorinitials>
				<revremark>
					I've implemented all changes suggested by lwillis and chpe and done some clarifications of my own.
				</revremark>
			</revision>
			<revision>
				<revnumber>0.01</revnumber>
				<date>01-Jan-2004</date>
				<authorinitials>ah</authorinitials>
				<revremark>
					My first shot at a great HOWTO. Surelly loaded with typos and minor errors.
				</revremark>
			</revision>
		</revhistory>
	</bookinfo>
	<!-- }}} End bookinfo -->
	<!-- {{{ Introduction -->
	<chapter id="Introduction">
		<title>Introduction</title>
		<sect1>
			<title>What Epiphany Extensions Are</title>
			<para>Epiphany extensions are similar to <ulink url="http://texturizer.net/firebird/extensions/">Mozilla Firefox extensions</ulink>: they are entirely optional and change the way the browser works (usually by expanding its feature set). For example, an Epiphany extension could provide mouse gestures, track your browsing behavior, or even replace the entire Bookmarks system (though such an extension would be rather complicated).</para>
			<para>Epiphany extensions are <emphasis>different</emphasis> from Mozilla Firefox's because they are implemented in C or Python as opposed to Javascript. This gives enormous flexibility and a bit of added challenge, but it should be no problem for even a beginner to develop an extension which crashes Epiphany on startup.</para>
			<para>Epiphany extensions are at a lower level code-wise than Mozilla Firefox's; there is no simple way to convert a Mozilla Firefox extension to an Epiphany one. This should be repeated: <emphasis>Epiphany extensions are not interchangeable with Firefox extensions</emphasis>.</para>
		</sect1>
		<sect1>
			<title>Document Scope</title>
			<para>This document is <emphasis>not</emphasis> a C or Python programming tutorial. It is also not a guide to the Epiphany source code. It is just a basic guide to begin writing an extension.</para>
			<para>The document is written under the assumption that the reader wants to write an extension. The document will be an aid, but it does not replace reading actual source code from existing extensions. Many extensions are available at <ulink url="http://www.gnome.org/projects/epiphany/extensions.html"/>.</para>
		</sect1>
		<sect1 id="getting-help">
			<title>Getting Help</title>
			<para>For more help writing Epiphany Extensions, log on to <literal>#epiphany</literal> on <literal>irc.gnome.org</literal>. You can also use the regular <ulink url="http://mail.gnome.org/mailman/listinfo/epiphany-list/">Epiphany Mailing List</ulink>.</para>
			<para>As with all software projects' channels of communication, please do not ask stupid questions. If you've never read <ulink url="http://catb.org/~esr/faqs/smart-questions.html">How to Ask Questions the Smart Way</ulink> by Eric S. Raymond, read it now. It's a big pile of common sense, and it applies to virtually every open-source software project in existence.</para>
			<para>If you are a proficient hacker and don't understand some part of this document, that's a bug in the document; please email <link linkend="author">the author</link>.</para>
		</sect1>
		<sect1 id="author">
			<title>About the Author</title>
			<para>I am Adam Hooper, age 21, second-year Software Engineering student at <ulink url="http://www.mcgill.ca">McGill University</ulink> in Montreal, Quebec, Canada. I can share my experiences with several extensions, including a popup blocker, HTML error viewer and page info dialog.</para>
			<para>My email address is <email>adamh@densi.com</email>; feel free to contact me about this document (or most other things, really).</para>
		</sect1>
	</chapter>
	<!-- }}} End Introduction -->
	<!-- {{{ Preparation -->
	<chapter id="Preparation">
		<title>Preparation</title>
		<sect1>
			<title>Required Experience</title>
			<para>Knowledge of C or Python is required. Familiarity with GTK+ and Glib (especially GObjects and signals) can also be quite important. Familiarity with the Epiphany source code would be fantastic, but if you don't have it&hellip; you will before you're finished writing your extension!</para>
		</sect1>
		<sect1>
			<title>Required Programs</title>
			<para>Obviously, you will need <ulink url="http://www.gnome.org/projects/epiphany">Epiphany</ulink> installed, and all of its required libraries. Development headers must be installed, too (for most distributions, you must install both the regular library and the <filename>-dev</filename> or <filename>-devel</filename> packages&mdash; but if you don't know this already, just give up now and save yourself the confusion). You will typically need GTK, GLib and Epiphany development headers as a minimum; depending on what your extension does, this list may be much larger.</para>
			<para>This document suggests features only found in Epiphany CVS; if a particular feature does not work for you, it could be that your version of Epiphany is too old.</para>
			<para>It is highly recommended that you install <application>epiphany-extensions</application> as well. Besides providing a user interface to enable and disable extensions (so you don't have to edit the GConf keys manually), it also provides a C build framework.</para>
			<para>To compile your extension with the existing C build framework, you will need <application>GNU autoconf</application>, <application>GNU automake</application>, <application>libtool</application>, <application>gettext</application>, etc. Basically, you must be able to compile Epiphany from CVS. In fact, you might as well install Epiphany from CVS before coding.</para>
			<para>If you want to write your extension using Python, you should still install <application>epiphany-extensions</application> for its extension enabling user interface. You must also install <application>pyphany</application>, which includes Python bindings, a Python script loader, and some sample extensions.</para>
		</sect1>
		<sect1>
			<title>Required Source Code</title>
			<para>You will <emphasis>need</emphasis> the source code to Epiphany. It is available at <ulink url="http://www.gnome.org/projects/epiphany"/>. It's a good idea to code an extension for the newest version of Epiphany available; from CVS is best.</para>
			<para>The existing <ulink url="http://www.gnome.org/projects/epiphany/extensions.html">GNOME Web Browser Extensions</ulink> source code has many working (and non-working) examples.</para>
		</sect1>
		<sect1>
			<title>Required Documentation</title>
			<para>GTK+ and Glib documentation are available at <ulink url="http://gtk.org/api/"/>, but a locally-installed set of documentation is preferable. If you installed GTK+ from CVS or a recent tarball (at the time this document was written, doing so is necessary for Epiphany CVS to compile), be sure to install the gtk-doc documentation along with it.</para>
			<para>The author uses <ulink url="http://www.imendio.com/projects/devhelp">Devhelp</ulink> for quick access to Glib and GTK+ documentation. There is some Epiphany documentation available, but it is not complete.</para>
			<para>The <ulink url="http://developer.gnome.org/projects/gup/hig/">GNOME Human Interface Guidelines</ulink>: this documentation is not strictly required, but it <emphasis>should</emphasis> be treated with at least as much respect as exists towards the Bible, Koran, Torah and all other holy books.</para>
		</sect1>
		<sect1>
			<title>Before Coding</title>
			<para>As with any software project, be sure to plan what your extension should do before writing it. At the very least, you should have a good idea of what events (GLib signals) will trigger your extension, and what it will do.</para>
			<para>Read Epiphany's <filename>HACKING</filename> file. Adhere to those standards.</para>
		</sect1>
		<sect1>
			<title>Required License</title>
			<para>Epiphany extensions are classified as derivative works of Epiphany, which means they <emphasis>must</emphasis> be licensed under the GNU GPL.</para>
		</sect1>
	</chapter>
	<!-- }}} End Preparation -->
	<!-- {{{ Build-Framework -->
	<chapter id="Build-Framework">
		<title>Build Framework</title>
		<para>If you are coding your extension in C, you will have to build it at some point; this chapter will detail a simple way to do so. If you are coding your extension in Python, you can skip this chapter entirely.</para>
		<para>To avoid autoconf/automake hell, you could simply code your extension in the existing epiphany-extensions framework (download the source from <ulink url="http://www.gnome.org/projects/epiphany/extensions.html"/>). To do so:</para>
		<orderedlist>
			<listitem>
				<para>Run <userinput><filename>epiphany-extensions/tools/copy-template</filename> <replaceable>foo</replaceable></userinput>. This will copy the sample template into a new subdirectory in <filename class="directory">epiphany-extensions/extensions/</filename>.</para>
			</listitem>
			<listitem>
				<para>Edit <filename>epiphany-extensions/extensions/Makefile.am</filename> and add your SUBDIRS entry:</para>
				<programlisting>if ENABLE_<replaceable>FOO</replaceable>_EXTENSION
SUBDIRS += <replaceable>foo</replaceable>
endif</programlisting>
				<para>Also, add your extension to the end of the <literal>DIST_SUBDIRS</literal> line.</para>
			</listitem>
			<listitem>
				<para>Edit <filename>epiphany-extensions/configure.ac</filename> to add your own extension:</para>
				<itemizedlist>
					<listitem>
						<para>Add a line near the similar existing ones:</para>
						<programlisting>AM_CONDITIONAL(ENABLE_<replaceable>FOO</replaceable>_EXTENSION, echo &quot;$extensions&quot; | egrep '(^|,)<replaceable>foo</replaceable>($|,)' &gt; /dev/null)</programlisting>
					</listitem>
					<listitem>
						<para>Add <filename>extensions/<replaceable>foo</replaceable>/Makefile</filename> to the <function>AC_OUTPUT</function> call.</para>
					</listitem>
					<listitem>
						<para>Add your extension to the <literal>all_extensions=</literal> line.</para>
					</listitem>
				</itemizedlist>
			</listitem>
			<listitem>
				<para>If your extension will include strings which should be translated, edit <filename>epiphany-extensions/po/POTFILES.in</filename> and add <filename>extensions/<replaceable>foo</replaceable>/ephy-<replaceable>foo</replaceable>-extension.c</filename>. (No, the filename is not important.)</para>
			</listitem>
			<listitem>
				<para>Edit <filename>epiphany-extensions/extensions/<replaceable>foo</replaceable>/<replaceable>foo</replaceable>.xml.in.in</filename> to describe your extension.</para>
			</listitem>
		</orderedlist>
		<para>Now all work will take place in the <filename class="directory">epiphany-extensions/extensions/<replaceable>foo</replaceable>/</filename> directory. After coding is finished, run <userinput>./autogen.sh --enable-debug --enable-extensions=<replaceable>foo</replaceable></userinput> in the <filename class="directory">epiphany-extensions/</filename> directory, then <userinput>make</userinput> and <userinput>make install</userinput> as root.</para>
	</chapter>
	<!-- }}} End Build-Framework -->
	<!-- {{{ Coding -->
	<chapter id="Coding">
		<title>Coding</title>
		<note>
			<para>In any conflict between Epiphany's C coding style and this document's, Epiphany's is right and this document's is wrong. Particularly, tabs are 8 spaces wide and are <emphasis>tabs</emphasis>, and in each function declaration, each argument should appear on its own line, in line with the argument above it.</para>
			<para>There is no coding standard for Python extensions.</para>
		</note>
		<tip>
			<para>The C <function>LOG</function> macro is included in <filename>ephy-debug.h</filename>. If you use <userinput>--enable-debug</userinput> while running <command>configure</command> or <command>autogen.sh</command> from within the <filename class="directory">epiphany-extensions/</filename> directory, the messages will be printed with <function>g_print</function>, which uses a <function>printf</function>-like syntax.</para>
		</tip>
		<sect1 id="register-module">
			<title><function>register_module</function></title>
			<para>This function is only necessary for C extensions; Python coders may skip this section.</para>
			<para>Epiphany will look for a <function>register_module</function> function when loading the extension. This function is defined in <filename>extension.c</filename> if you used the <filename>copy-template</filename> script.</para>
			<para>Read over the default example to understand what is expected: <function>register_module</function> must return a GObject type. Almost always, this GObject should implement the <interface>EphyExtension</interface> interface. In the template extension, this is described in the last line of the function: <programlisting>return ephy_<replaceable>foo</replaceable>_extension_register_type (module);</programlisting>. If you wish to register other GObject types, simply put their register_type calls above this one.</para>
			<warning>
				<para>Extensions may be loaded or unloaded at any time. This means they must be registered using <function>g_type_module_register_type</function>. Also, due to a limitation in that function, if you update your extension to change the parent class of a particular GObject, unload the extension and then reload it within Epiphany, the new GObject class will fail to register. To avoid problems, when changing the parent class of a GObject in your extension, increment the <literal>version</literal> in the extension's XML description file.</para>
			</warning>
		</sect1>
		<sect1>
			<title>Header File</title>
			<para>The <filename>extension.c</filename> file above referred to <filename>ephy-<replaceable>foo</replaceable>-extension.h</filename>, so that header file must be present. An extension is a <literal>GObject</literal>. There are no surprises in the header file: it's a standard header for a GObject. It's quite possible that editing this file (after creating it with <filename>copy-template</filename>) is unnecessary.</para>
		</sect1>
		<sect1>
			<title>Minimum C Code</title>
			<para>As a GObject, the extension requires some actual code. The <filename>copy-template</filename> script will generate the basic structure for <filename>ephy-<replaceable>foo</replaceable>-extension.c</filename>, but you must fill in the details to make this extension actually do something. What to fill in is the focus of the rest of this document.</para>
			<para>Before you continue, feel free to remove all the property setting/getting code if it's unneeded.</para>
			<para>If your GObject implements the <literal>EphyExtension</literal> interface (and it should), it may implement the following functions. These are the hooks through which the extension can attach signals to accomplish pretty much anything.</para>
			<variablelist>
				<varlistentry>
					<term>attach_window</term>
					<listitem><para>Called when a new window is created. Passed the <literal>EphyExtension</literal> and <literal>EphyWindow</literal>.</para></listitem>
				</varlistentry>
				<varlistentry>
					<term>detach_window</term>
					<listitem><para>Called when a window is being finalized. Passed the <literal>EphyExtension</literal> and <literal>EphyWindow</literal>.</para></listitem>
				</varlistentry>
				<varlistentry>
					<term>attach_tab</term>
					<listitem><para>Called when a new tab is created. Passed the <literal>EphyExtension</literal>, the <literal>EphyWindow</literal> and the <literal>EphyTab</literal>.</para></listitem>
				</varlistentry>
				<varlistentry>
					<term>detach_tab</term>
					<listitem><para>Called before a tab is finalized. Passed the <literal>EphyExtension</literal>, the <literal>EphyWindow</literal> and the <literal>EphyTab</literal>.</para></listitem>
				</varlistentry>
			</variablelist>
			<para>The template file will contain empty <function>impl_attach_window</function> and <function>impl_detach_window</function> when it is first created.</para>
			<para>Python extensions may use these same functions. When using Python, the <literal>EphyExtension</literal> argument is never passed (just store data structures as globals in the Python module).</para>
		</sect1>
		<sect1>
			<title>Connecting Signals</title>
			<para>GLib has a very flexible signals system. Use it. The <function>attach_window</function> function has an <literal>EphyWindow</literal> argument. As an example, the <literal>switch_page</literal> signal will be fired on a window's tab whenever the tab changes. Put the following in <function>attach_window</function> to hook the <literal>switch_page</literal> signal to your own callback:</para>
			<example>
				<title>Connecting the switch_page signal</title>
				<programlisting language="python">def _switch_page_cb(notebook, page, page_num, data):
	print data

def attach_window(window):
	notebook = window.get_notebook()
	sig = notebook.connect('switch_page', _switch_page_cb, 'Foo')
	notebook._example_extension_sig = sig

def detach_window(window):
	notebook = window.get_notebook()
	sig = notebook._example_extension_sig
	notebook.disconnect(sig)
	del notebook._example_extension_sig</programlisting>
			</example>
			<para><function>tab_added</function> can connect signals of its own. Of particular interest are the <literal>EphyEmbed</literal> signals (prefixed by <literal>ge_</literal>). These signals are listed in <filename>epiphany/embed/ephy-embed.c</filename>. For example, to call a function when the browser changes to a new location:</para>
			<example>
				<title>Listening to location changes</title>
				<programlisting language="python">def _location_cb(embed, address):
	print 'New location: %s' % address

def attach_tab(window, tab):
	embed = tab.get_embed()
	sig = embed.connect('ge-location', _location_cb)
	embed._python_sample_location_sig = sig

def detach_tab(window, tab):
	embed = tab.get_embed()
	sig = embed._python_sample_location_sig
	del embed._python_sample_location_sig
	embed.disconnect(sig)</programlisting>
			</example>
			<warning>
				<para>Free all memory and disconnect all signals when they're not needed. In the case of this <literal>ge_location</literal> signal connection, disconnect the signal when the tab is removed.</para>
				<para>Python's garbage collector frees memory properly, but it does not disconnect signals; you <emphasis>must</emphasis> disconnect signals.</para>
			</warning>
		</sect1>
		<sect1 id="translation">
			<title>Translation</title>
			<para>If you need to translate some strings (if your extension prints or displays any messages besides debug messages), a few steps must be followed:</para>
			<itemizedlist>
				<listitem>
					<para>For every string needing translation, use the <function>N_</function>, <function>_</function> or <function>ngettext</function> functions. For example:</para>
					<example>
						<title>Basic gettext usage</title>
						<programlisting>g_print (_(&quot;This is a foobar. It says %s.&quot;), message);
g_print (ngettext (&quot;%d cow jumped over the moon&quot;, &quot;%d cows jumped over the moon&quot;, n), n);</programlisting>
					</example>
				</listitem>
				<listitem>
					<para>Add the filenames with translatable strings to <filename>epiphany-extensions/po/POTFILES.in</filename>.</para>
				</listitem>
			</itemizedlist>
		</sect1>
		<sect1>
			<title>User Interface Integration</title>
			<warning>
				<para>This section describes changing Epiphany's user interface. Epiphany developers take pride in Epiphany's simplicity and ease of use: you should continue the trend. UI additions should be simple, minor, and non-intrusive. Carefully plan each addition.</para>
			</warning>
			<para>The <classname>GtkUIManager</classname> functions allow you to add new user interface elements. Epiphany's <function>ephy_window_get_ui_manager</function> will return the appropriate <classname>GtkUIManager</classname> for an <classname>EphyWindow</classname>. Use <classname>GtkAction</classname>s to represent actions. For example, an extension could place an action at the bottom of the <guimenu>Go</guimenu> menu of Epiphany. To do so, write the following in <filename>ephy-<replaceable>foo</replaceable>-extension.c</filename>:</para>
			<example>
				<title>Adding menu entries</title>
				<programlisting language="C">#include &lt;gtk/gtkaction.&gt;
#include &lt;gtk/gtkactiongroup&gt;
#include &lt;gtk/gtkuimanager.h&gt;

#include &lt;glib/gi18n-lib.h&gt; /* see <xref linkend="translation"/> */

#define WINDOW_DATA_KEY "Ephy<replaceable>Foo</replaceable>ExtensionWindowData"

typedef struct
{
	GtkActionGroup *action_group;
	guint ui_id;
} WindowData;

static void my_action_activate_cb (GtkAction *action, EphyWindow *window);

/* ... */

static GtkActionEntry action_entries [] =
{
	{ &quot;<replaceable>MyAction</replaceable>&quot;,
	  NULL,
	  N_(&quot;<replaceable>Menu Entry Name</replaceable>&quot;),
	  NULL, /* shortcut key */
	  N_(&quot;<replaceable>Does Whatever I Want</replaceable>&quot;),
	  G_CALLBACK (my_action_activate_cb) }
};
static const guint n_action_entries = G_N_ELEMENTS (action_entries);

static void my_action_activate_cb (GtkAction *action,
				   EphyWindow *window)
{
	/* Do something */
}

/* ... */

static void
free_window_data (WindowData *data)
{
	g_object_unref (data->action_group);
	g_free (data);
}

static void
impl_attach_window (EphyExtension *extension,
		    EphyWindow *window)
{
	GtkActionGroup *action_group;
	GtkUIManager *manager;
	guint merge_id;
	WindowData *data;

	data = g_new (WindowData, 1);

	manager = GTK_UI_MANAGER (ephy_window_get_ui_manager (window));

	data->action_group = action_group =
		gtk_action_group_new (&quot;Ephy<replaceable>Foo</replaceable>ExtensionActions&quot;);

	gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
	gtk_action_group_add_actions (action_group, action_entries,
				      n_action_entries, window);

	gtk_ui_manager_insert_action_group (manager, action_group, 0);

	data->ui_id = merge_id = gtk_ui_manager_new_merge_id (manager);

	g_data_set_full (G_OBJECT (window), WINDOW_DATA_KEY, data,
			 (GDestroyNotify) free_window_data);

	gtk_ui_manager_add_ui (manager, merge_id, &quot;/menubar/GoMenu&quot;,
			       &quot;<replaceable>MyAction</replaceable>Sep1&quot;, NULL,
			       GTK_UI_MANAGER_SEPARATOR, FALSE);
	gtk_ui_manager_add_ui (manager, merge_id, &quot;/menubar/GoMenu&quot;,
			       &quot;<replaceable>MyAction</replaceable>&quot;, &quot;<replaceable>MyAction</replaceable>&quot;,
			       GTK_UI_MANAGER_MENUITEM, FALSE);
	gtk_ui_manager_add_ui (manager, merge_id, &quot;/menubar/GoMenu&quot;,
			       &quot;<replaceable>MyAction</replaceable>Sep2&quot;, NULL,
			       GTK_UI_MANAGER_SEPARATOR, FALSE);
}

static void
impl_detach_window (EphyExtension *extension,
		    EphyWindow *window)
{
	GtkUIManager *manager;
	WindowData *data;

	manager = GTK_UI_MANAGER (ephy_window_get_ui_manager (window));

	data = (WindowData *) g_object_get_data (G_OBJECT (window), WINDOW_DATA_KEY);
	g_return_if_fail (data != NULL);

	gtk_ui_manager_remove_ui (manager, data->ui_id);
	gtk_ui_manager_remove_action_group (manager, data->action_group);

	g_object_set_data (G_OBJECT (window), WINDOW_DATA_KEY, NULL);
}</programlisting>
			</example>
			<example>
				<title>Adding menu entries (Python)</title>
				<programlisting language="python"><![CDATA[import gtk

_ui_str = """
<ui>
	<menubar name="menubar">
		<menu name="GoMenu" action="Go">
			<separator/>
			<menuitem name="SampleMenuEntry"
				action="SampleMenuEntry"/>
			<separator/>
		</menu>
	</menubar>
</ui>
"""

def _menu_entry_cb(action, data):
	print data

_actions = [('SampleMenuEntry', None, 'Sample Menu Entry',
	     None, None, _menu_entry_cb)]

def attach_window(window):
	ui_manager = window.get_ui_manager()
	group = gtk.ActionGroup('SampleMenu')
	group.add_actions(_actions, 'Data')
	ui_manager.insert_action_group(group, 0)
	ui_id = ui_manager.add_ui_from_string(_ui_str)

	window._sample_menu_entry_data = (group, ui_id)

def detach_window(window):
	group, ui_id = window._sample_menu_entry_data
	del window._sample_menu_entry_data

	ui_manager = window.get_ui_manager()
	ui_manager.remove_ui(ui_id)
	ui_manager.remove_action_group(group)
	ui_manager.ensure_update()]]></programlisting>
			</example>
			<tip>
				<para>The existing epiphany menu hierarchy is described in <filename>epiphany/data/ui/epiphany-ui.xml</filename>.</para>
			</tip>
			<note>
				<para>Without a <literal>placeholder</literal> element in <filename>epiphany/data/ui/epiphany-ui.xml</filename>, extensions can only add menu entries to the top or bottom of menus.</para>
			</note>
			<warning>
				<para>Remember that extensions may be loaded or unloaded at any point in time. The above procedure detaches from windows nicely. If you decide to modify it, be sure that your extension still loads and unloads successfully.</para>
			</warning>
		</sect1>
		<sect1>
			<title>Statusbar</title>
			<warning>
				<para>The statusbar can only display one message at a time. What's more, the statusbar can be disabled. Statusbar messages from one extension can conflict with another &mdash; and all extensions can conflict with Epiphany's standard statusbar messages. There are many reasons <emphasis>not</emphasis> to write statusbar messages in your extension.</para>
			</warning>
			<para>Writing statusbar messages is one of the easiest things to do from an extension. Just be sure to understand <literal>GtkStatusbar</literal>s: in a given context, there is a stack of messages. Only the top message from <emphasis>all</emphasis> contexts can be shown at a time, and messages can be pushed and popped to and from the stack on any given context.</para>
			<para>This sample code should give some ideas:</para>
			<example>
				<title>Manipulating the statusbar</title>
				<programlisting>#define STATUSBAR_CONTEXT "Ephy<replaceable>Foo</replaceable>StatusbarContext"
					
static void
write_statusbar_message (EphyWindow *window, const char *message)
{
	GtkStatusbar *statusbar;
	guint context_id;

	statusbar = GTK_STATUSBAR (ephy_window_get_statusbar (window));
	g_return_if_fail (GTK_IS_STATUSBAR (statusbar));

	context_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (window),
							  STATUSBAR_CONTEXT));
	g_return_if_fail (context_id != 0);

	/* Keep a maximum of one element on the stack */
	gtk_statusbar_pop (statusbar, context_id);

	/* Write our message */
	gtk_statusbar_push (statusbar, context_id, message);
}

static void
remove_statusbar_message (EphyWindow *window)
{
	GtkStatusbar *statusbar;
	guint context_id;

	statusbar = GTK_STATUSBAR (ephy_window_get_statusbar (window));
	g_return_if_fail (GTK_IS_STATUSBAR (statusbar));

	context_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (window),
							  STATUSBAR_CONTEXT));
	g_return_if_fail (context_id != 0);

	gtk_statusbar_pop (statusbar, context_id);
}

static void
impl_attach_window (EphyExtension *extension,
		    EphyWindow *window)
{
	GtkStatusbar *statusbar;
	guint context_id;

	statusbar = GTK_STATUSBAR (ephy_window_get_statusbar (window));
	g_return_if_fail (GTK_IS_STATUSBAR (statusbar));

	context_id = gtk_statusbar_get_context_id (statusbar, &quot;<replaceable>Foo</replaceable> extension&quot;);

	g_object_set_data (G_OBJECT (window), &quot;<replaceable>foo</replaceable>-statusbar-cid&quot;, GUINT_TO_POINTER (context_id));
}

static void
impl_detach_window (EphyExtension *extension,
		    EphyWindow *window)
{
	remove_statusbar_message (window);
}</programlisting>
			</example>
		</sect1>
		<sect1>
			<title>GConf Settings</title>
			<para>Using <filename>eel-gconf-extensions.h</filename>, retrieving Epiphany's GConf settings is a snap. The keys used by Epiphany are all listed in <filename>epiphany/lib/ephy-prefs.h</filename> (but ephy-prefs.h is not installed alongside Epiphany, and so cannot be included by your extension). For example, the following function will return whether or not the statusbar is to be displayed:</para>
			<example>
				<title>Retrieving a GConf value</title>
				<programlisting>static gboolean
is_statusbar_wanted (void)
{
	return eel_gconf_get_boolean (&quot;/apps/epiphany/general/show_statusbar&quot;);
}</programlisting>
			</example>
			<para>Even better, how about a callback for when a GConf key is changed:</para>
			<example>
				<title>Listening to GConf</title>
				<programlisting>
struct Ephy<replaceable>Foo</replaceable>ExtensionPrivate
{
	/* ... */
	guint gconf_cnxn_id;
	/* ... */
};

static void
my_gconf_cb (GConfClient *client,
	     guint cnxn_id,
	     GConfEntry *entry,
	     gpointer user_data)
{
	/* Do something */
}

static void
ephy_<replaceable>foo</replaceable>_extension_init (Ephy<replaceable>Foo</replaceable>Extension *extension)
{
	/* ... */
	extension->priv->gconf_cnxn_id = eel_gconf_notification_add
		(&quot;/apps/epiphany/general/show_statusbar&quot;, (GConfClientNotifyFunc) my_gconf_cb, NULL);
}

static void
ephy_<replaceable>foo</replaceable>_extension_finalize (Ephy<replaceable>Foo</replaceable>Extension *extension)
{
	if (extension->priv->gconf_cnxn_id != 0)
	{
		eel_gconf_notification_remove (extension->priv->gconf_cnxn_id);
		extension->priv->gconf_cnxn_id = 0;
	}
	/* ... */
}</programlisting>
			</example>
			<para>Voila! <function>my_gconf_cb</function> will be called when the GConf key is modified from within any program. Isn't GConf great?</para>
			<para>Read <filename>epiphany-extensions/include/eel-gconf-extensions.h</filename> for a full list of functions.</para>
			<para>If your extension is written in Python, you may use <application>gnome-python</application>'s GConf bindings.</para>
		</sect1>
		<sect1>
			<title>Your Own Ideas</title>
			<para>Many of the Epiphany integration ideas above were first discovered (or in some cases, the integration functionality was coded into Epiphany itself) by the author while creating the <application>Popup Blocker</application> extension.</para>
			<para>While writing an extension, you will almost certainly need to keep looking around <filename>epiphany/src/ephy-window.c</filename>, <filename>epiphany/src/ephy-tab.c</filename>, <filename>epiphany/embed/ephy-embed.h</filename>, and others. The code is very clean; if you still have questions, IRC is a great place to ask (see <xref linkend="getting-help"/>).</para>
			<para>You may come up with great ideas when writing your own extension. Feel free to email <link linkend="author">the author</link> some sample code; a new section could be added to this document to help other extension writers.</para>
		</sect1>
		<sect1>
			<title>Hacking at Epiphany</title>
			<para>Extensions can do a heck of a lot. Since the system is still under development, new (<emphasis>good</emphasis>) ideas would likely be welcome. Mozilla integration, for example, is a bit loose. To get certain signals from Mozilla, you may have to hack at files within the <filename class="directory">epiphany/embed/</filename> directory in the Epiphany source code. If your patch is clean and logical, submit it to <ulink url="http://bugzilla.gnome.org/enter_bug.cgi?product=epiphany">GNOME's Bugzilla</ulink> and you'll have a good chance of its getting applied.</para>
			<para>Stylesheet manipulation is one very important missing feature (all the more important because Mozilla Firefox's extensions are so powerful in this respect). At the time of this document's writing, the author is hacking at Epiphany to improve CSS manipulation from within extensions (and Epiphany itself, for that matter).</para>
			<para>Are you an uber-hacker? Here are some challenges. Note: very possibly, a perfectly acceptable patch would <emphasis>not</emphasis> be applied to the official Epiphany source tree. Consider discussing on <link linkend="getting-help">developer communication channels</link> about how you would modify Epiphany; you will probably get some helpful advice.</para>
			<itemizedlist>
				<listitem>
					<para>A JavaScript extension system similar to Firefox's: this would naturally be integrated much more tightly with Mozilla, and loosely (if at all) with the Epiphany UI.</para>
					<para>Note: Some such JavaScript-heavy, UI-free extensions actually work fine with Epiphany already (flashblock is one such extension).</para>
				</listitem>
				<listitem>
					<para>A C# extension loader. The <application>epiphany-mono</application> directory in GNOME CVS has the beginnings of such a system. There are a few bugs, and there is no documentation (this document, for instance, should be updated to include C#).</para>
				</listitem>
				<listitem>
					<para>[Insert your favourite Firefox extension here. Then make it better.]</para>
				</listitem>
			</itemizedlist>
		</sect1>
	</chapter>
	<!-- }}} End Coding -->
</book>
