2 * Copyright © 2003 Marco Pesenti Gritti
3 * Copyright © 2003, 2004 Christian Persch
4 * Copyright © 2004 Adam Hooper
5 * Copyright © 2005 Crispin Flowerday
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * $Id: ephy-extensions-manager.c 6952 2007-03-11 19:42:02Z chpe $
26 #include "ephy-extensions-manager.h"
28 #include "ephy-loader.h"
29 #include "ephy-shlib-loader.h"
31 #include "ephy-node-db.h"
32 #include "ephy-shell.h"
33 #include "eel-gconf-extensions.h"
34 #include "ephy-file-helpers.h"
35 #include "ephy-object-helpers.h"
36 #include "ephy-debug.h"
38 #include "ephy-glib-compat.h"
40 #include <libxml/tree.h>
41 #include <libxml/xmlreader.h>
42 #include <libxml/globals.h>
43 #include <libxml/tree.h>
44 #include <libxml/xmlwriter.h>
45 #include <libxslt/xslt.h>
46 #include <libxslt/transform.h>
47 #include <libxslt/xsltutils.h>
49 #include <libgnomevfs/gnome-vfs-ops.h>
50 #include <libgnomevfs/gnome-vfs-utils.h>
52 #include <gconf/gconf-client.h>
59 #include "ephy-python-extension.h"
60 #include "ephy-python-loader.h"
63 #define CONF_LOADED_EXTENSIONS "/apps/epiphany/general/active_extensions"
64 #define EE_GROUP "Epiphany Extension"
65 #define DOT_INI ".ephy-extension"
66 #define RELOAD_DELAY 333 /* ms */
67 #define RELOAD_SYNC_DELAY 1 /* seconds */
69 #define ENABLE_LEGACY_FORMAT
71 #define EPHY_EXTENSIONS_MANAGER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_EXTENSIONS_MANAGER, EphyExtensionsManagerPrivate))
73 struct _EphyExtensionsManagerPrivate
82 guint active_extensions_notifier_id
;
83 guint sync_timeout_id
;
84 GHashTable
*reload_hash
;
86 #ifdef ENABLE_LEGACY_FORMAT
87 xsltStylesheetPtr xml2ini_xsl
;
93 EphyExtensionInfo info
;
98 EphyLoader
*loader
; /* NULL if never loaded */
99 GObject
*extension
; /* NULL if unloaded */
101 #ifdef ENABLE_LEGACY_FORMAT
102 guint is_legacy_format
: 1;
116 #ifdef ENABLE_LEGACY_FORMAT
129 static guint signals
[LAST_SIGNAL
] = { 0 };
131 static GObjectClass
*parent_class
= NULL
;
133 static void ephy_extensions_manager_class_init (EphyExtensionsManagerClass
*klass
);
134 static void ephy_extensions_manager_iface_init (EphyExtensionIface
*iface
);
135 static void ephy_extensions_manager_init (EphyExtensionsManager
*manager
);
138 ephy_extensions_manager_get_type (void)
140 static GType type
= 0;
142 if (G_UNLIKELY (type
== 0))
144 const GTypeInfo our_info
=
146 sizeof (EphyExtensionsManagerClass
),
147 NULL
, /* base_init */
148 NULL
, /* base_finalize */
149 (GClassInitFunc
) ephy_extensions_manager_class_init
,
151 NULL
, /* class_data */
152 sizeof (EphyExtensionsManager
),
154 (GInstanceInitFunc
) ephy_extensions_manager_init
157 const GInterfaceInfo extension_info
=
159 (GInterfaceInitFunc
) ephy_extensions_manager_iface_init
,
164 type
= g_type_register_static (G_TYPE_OBJECT
,
165 "EphyExtensionsManager",
168 g_type_add_interface_static (type
,
177 * ephy_extensions_manager_load:
178 * @manager: an #EphyExtensionsManager
179 * @name: identifier of the extension to load
181 * Loads the @name extension.
184 ephy_extensions_manager_load (EphyExtensionsManager
*manager
,
185 const char *identifier
)
189 g_return_if_fail (EPHY_IS_EXTENSIONS_MANAGER (manager
));
190 g_return_if_fail (identifier
!= NULL
);
192 LOG ("Adding '%s' to extensions", identifier
);
194 gconf_exts
= eel_gconf_get_string_list (CONF_LOADED_EXTENSIONS
);
196 if (!g_slist_find_custom (gconf_exts
, identifier
, (GCompareFunc
) strcmp
))
198 gconf_exts
= g_slist_prepend (gconf_exts
, g_strdup (identifier
));
200 eel_gconf_set_string_list (CONF_LOADED_EXTENSIONS
, gconf_exts
);
203 g_slist_foreach (gconf_exts
, (GFunc
) g_free
, NULL
);
204 g_slist_free (gconf_exts
);
208 * ephy_extensions_manager_unload:
209 * @manager: an #EphyExtensionsManager
210 * @name: filename of extension to unload, minus "lib" and "extension.so"
212 * Unloads the extension specified by @name.
214 * The extension with the same filename can afterwards be reloaded. However,
215 * if any GTypes within the extension have changed parent types, Epiphany must
219 ephy_extensions_manager_unload (EphyExtensionsManager
*manager
,
220 const char *identifier
)
225 g_return_if_fail (EPHY_IS_EXTENSIONS_MANAGER (manager
));
226 g_return_if_fail (identifier
!= NULL
);
228 LOG ("Removing '%s' from extensions", identifier
);
230 gconf_exts
= eel_gconf_get_string_list (CONF_LOADED_EXTENSIONS
);
232 l
= g_slist_find_custom (gconf_exts
, identifier
, (GCompareFunc
) strcmp
);
236 gconf_exts
= g_slist_remove_link (gconf_exts
, l
);
240 eel_gconf_set_string_list (CONF_LOADED_EXTENSIONS
, gconf_exts
);
243 g_slist_foreach (gconf_exts
, (GFunc
) g_free
, NULL
);
244 g_slist_free (gconf_exts
);
248 * ephy_extensions_manager_register:
249 * @manager: an #EphyExtensionsManager
250 * @object: an Extension
252 * Registers @object with the extensions manager. @object must implement the
253 * #EphyExtension interface.
256 ephy_extensions_manager_register (EphyExtensionsManager
*manager
,
259 g_return_if_fail (EPHY_IS_EXTENSIONS_MANAGER (manager
));
260 g_return_if_fail (EPHY_IS_EXTENSION (object
));
262 manager
->priv
->extensions
= g_list_prepend (manager
->priv
->extensions
,
263 g_object_ref (object
));
268 * ephy_extensions_manager_get_extensions:
269 * @manager: an #EphyExtensionsManager
271 * Returns the list of known extensions.
273 * Returns: a list of #EphyExtensionInfo
276 ephy_extensions_manager_get_extensions (EphyExtensionsManager
*manager
)
278 return g_list_copy (manager
->priv
->data
);
282 free_extension_info (ExtensionInfo
*info
)
284 EphyExtensionInfo
*einfo
= (EphyExtensionInfo
*) info
;
286 g_free (einfo
->identifier
);
287 g_key_file_free (einfo
->keyfile
);
288 g_free (info
->loader_type
);
290 if (info
->extension
!= NULL
)
292 g_return_if_fail (info
->loader
!= NULL
);
294 ephy_loader_release_object (info
->loader
, info
->extension
);
296 if (info
->loader
!= NULL
)
298 g_object_unref (info
->loader
);
305 free_loader_info (LoaderInfo
*info
)
308 g_object_unref (info
->loader
);
313 find_extension_info (const ExtensionInfo
*info
,
314 const char *identifier
)
316 return strcmp (info
->info
.identifier
, identifier
);
319 static ExtensionInfo
*
320 ephy_extensions_manager_parse_keyfile (EphyExtensionsManager
*manager
,
322 const char *identifier
)
325 EphyExtensionInfo
*einfo
;
328 LOG ("Parsing INI description file for '%s'", identifier
);
330 start_group
= g_key_file_get_start_group (key_file
);
331 if (start_group
== NULL
||
332 strcmp (start_group
, EE_GROUP
) != 0 ||
333 !g_key_file_has_group (key_file
, "Loader"))
335 g_warning ("Invalid extension description file for '%s'; "
336 "missing 'Epiphany Extension' or 'Loader' group",
339 g_key_file_free (key_file
);
340 g_free (start_group
);
343 g_free (start_group
);
345 if (!g_key_file_has_key (key_file
, EE_GROUP
, "Name", NULL
) ||
346 !g_key_file_has_key (key_file
, EE_GROUP
, "Description", NULL
))
348 g_warning ("Invalid extension description file for '%s'; "
349 "missing 'Name' or 'Description' keys.",
352 g_key_file_free (key_file
);
356 info
= g_new0 (ExtensionInfo
, 1);
357 einfo
= (EphyExtensionInfo
*) info
;
358 einfo
->identifier
= g_strdup (identifier
);
359 einfo
->keyfile
= key_file
;
361 info
->loader_type
= g_key_file_get_string (key_file
, "Loader", "Type", NULL
);
364 if (info
->loader_type
== NULL
|| info
->loader_type
[0] == '\0')
366 free_extension_info (info
);
370 manager
->priv
->data
= g_list_prepend (manager
->priv
->data
, info
);
372 g_signal_emit (manager
, signals
[ADDED
], 0, info
);
378 ephy_extensions_manager_load_ini_file (EphyExtensionsManager
*manager
,
379 const char *identifier
,
385 keyfile
= g_key_file_new ();
386 if (!g_key_file_load_from_file (keyfile
, path
, G_KEY_FILE_NONE
, &err
))
388 g_warning ("Could load key file for '%s': '%s'",
389 identifier
, err
->message
);
391 g_key_file_free (keyfile
);
395 ephy_extensions_manager_parse_keyfile (manager
, keyfile
, identifier
);
398 #ifdef ENABLE_LEGACY_FORMAT
401 ephy_extensions_manager_load_xml_file (EphyExtensionsManager
*manager
,
402 const char *identifier
,
405 EphyExtensionsManagerPrivate
*priv
= manager
->priv
;
408 const xmlChar
*xsl_file
;
409 xmlChar
*output
= NULL
;
410 int outlen
= -1, ret
= -1;
412 START_PROFILER ("Transforming .xml -> " DOT_INI
)
414 doc
= xmlParseFile (path
);
417 if (priv
->xml2ini_xsl
== NULL
)
419 xsl_file
= (const xmlChar
*) ephy_file ("ephy-xml2ini.xsl");
420 if (!xsl_file
) return;
422 priv
->xml2ini_xsl
= xsltParseStylesheetFile (xsl_file
);
423 if (priv
->xml2ini_xsl
== NULL
)
425 g_warning ("Couldn't parse the XSL to transform .xml extension descriptions!\n");
430 res
= xsltApplyStylesheet (priv
->xml2ini_xsl
, doc
, NULL
);
433 ret
= xsltSaveResultToString (&output
, &outlen
, res
, priv
->xml2ini_xsl
);
435 if (ret
>= 0 && output
!= NULL
&& outlen
> -1)
440 keyfile
= g_key_file_new ();
441 if (!g_key_file_load_from_data (keyfile
, (char *) output
, outlen
,
442 G_KEY_FILE_NONE
, &err
))
444 g_warning ("Could load converted key file for '%s': '%s'",
445 identifier
, err
->message
);
447 g_key_file_free (keyfile
);
451 info
= ephy_extensions_manager_parse_keyfile (manager
, keyfile
, identifier
);
454 info
->is_legacy_format
= TRUE
;
464 STOP_PROFILER ("Transforming .xml -> " DOT_INI
)
467 #endif /* ENABLE_LEGACY_FORMAT */
470 path_to_identifier (const char *path
)
472 char *identifier
, *dot
;
474 identifier
= g_path_get_basename (path
);
475 dot
= strstr (identifier
, DOT_INI
);
477 #ifdef ENABLE_LEGACY_FORMAT
480 dot
= strstr (identifier
, ".xml");
484 g_return_val_if_fail (dot
!= NULL
, NULL
);
491 static ExtensionFormat
492 format_from_path (const char *path
)
494 ExtensionFormat format
= FORMAT_UNKNOWN
;
496 if (g_str_has_suffix (path
, DOT_INI
))
500 #ifdef ENABLE_LEGACY_FORMAT
501 else if (g_str_has_suffix (path
, ".xml"))
511 ephy_extensions_manager_load_file (EphyExtensionsManager
*manager
,
516 ExtensionFormat format
;
518 identifier
= path_to_identifier (path
);
519 g_return_if_fail (identifier
!= NULL
);
520 if (identifier
== NULL
) return;
522 format
= format_from_path (path
);
523 g_return_if_fail (format
!= FORMAT_UNKNOWN
);
525 element
= g_list_find_custom (manager
->priv
->data
, identifier
,
526 (GCompareFunc
) find_extension_info
);
529 #ifdef ENABLE_LEGACY_FORMAT
530 ExtensionInfo
*info
= (ExtensionInfo
*) element
->data
;
532 /* If this is the legacy format and we already have the info
533 * read for this type from a non-legacy format file, don't
536 if (format
== FORMAT_XML
&& !info
->is_legacy_format
)
539 g_warning ("Extension description for '%s' already read!",
547 if (format
== FORMAT_INI
)
549 ephy_extensions_manager_load_ini_file (manager
, identifier
,
552 #ifdef ENABLE_LEGACY_FORMAT
553 else if (format
== FORMAT_XML
)
555 ephy_extensions_manager_load_xml_file (manager
, identifier
,
565 find_loader (const LoaderInfo
*info
,
568 return strcmp (info
->type
, type
);
572 sanitise_type (const char *string
)
576 str
= g_strdup (string
);
577 for (p
= str
; *p
!= '\0'; p
++)
579 if (!g_ascii_isalpha (*p
)) *p
= '-';
586 get_loader_for_type (EphyExtensionsManager
*manager
,
591 char *path
, *name
, *stype
, *data
;
593 EphyLoader
*shlib_loader
;
596 LOG ("Looking for loader for type '%s'", type
);
598 l
= g_list_find_custom (manager
->priv
->factories
, type
,
599 (GCompareFunc
) find_loader
);
602 info
= (LoaderInfo
*) l
->data
;
603 return g_object_ref (info
->loader
);
606 if (strcmp (type
, "shlib") == 0)
608 info
= g_new (LoaderInfo
, 1);
609 info
->type
= g_strdup (type
);
610 info
->loader
= g_object_new (EPHY_TYPE_SHLIB_LOADER
, NULL
);
612 manager
->priv
->factories
=
613 g_list_append (manager
->priv
->factories
, info
);
615 return g_object_ref (info
->loader
);
617 if (strcmp (type
, "python") == 0)
620 info
= g_new (LoaderInfo
, 1);
621 info
->type
= g_strdup (type
);
622 info
->loader
= g_object_new (EPHY_TYPE_PYTHON_LOADER
, NULL
);
624 manager
->priv
->factories
=
625 g_list_append (manager
->priv
->factories
, info
);
627 return g_object_ref (info
->loader
);
633 shlib_loader
= get_loader_for_type (manager
, "shlib");
634 g_return_val_if_fail (shlib_loader
!= NULL
, NULL
);
636 stype
= sanitise_type (type
);
637 name
= g_strconcat ("lib", stype
, "loader.", G_MODULE_SUFFIX
, NULL
);
638 path
= g_build_filename (LOADER_DIR
, name
, NULL
);
639 data
= g_strconcat ("[Loader]\nType=shlib\nLibrary=", path
, "\n", NULL
);
644 keyfile
= g_key_file_new ();
645 if (!g_key_file_load_from_data (keyfile
, data
, strlen (data
), 0, NULL
))
651 loader
= ephy_loader_get_object (shlib_loader
, keyfile
);
652 g_key_file_free (keyfile
);
654 if (EPHY_IS_LOADER (loader
))
656 info
= g_new (LoaderInfo
, 1);
657 info
->type
= g_strdup (type
);
658 info
->loader
= EPHY_LOADER (loader
);
660 manager
->priv
->factories
=
661 g_list_append (manager
->priv
->factories
, info
);
663 return g_object_ref (info
->loader
);
666 g_return_val_if_reached (NULL
);
672 attach_window (EphyWindow
*window
,
673 EphyExtension
*extension
)
677 ephy_extension_attach_window (extension
, window
);
679 tabs
= ephy_window_get_tabs (window
);
680 for (l
= tabs
; l
; l
= l
->next
)
682 ephy_extension_attach_tab (extension
, window
,
689 load_extension (EphyExtensionsManager
*manager
,
694 g_return_if_fail (info
->extension
== NULL
);
696 LOG ("Loading extension '%s'", info
->info
.identifier
);
698 /* don't try again */
699 if (info
->load_failed
) return;
702 loader
= get_loader_for_type (manager
, info
->loader_type
);
705 g_message ("No loader found for extension '%s' of type '%s'\n",
706 info
->info
.identifier
, info
->loader_type
);
710 info
->loader
= loader
;
712 info
->extension
= ephy_loader_get_object (loader
, info
->info
.keyfile
);
714 /* attach if the extension implements EphyExtensionIface */
715 if (EPHY_IS_EXTENSION (info
->extension
))
717 manager
->priv
->extensions
=
718 g_list_prepend (manager
->priv
->extensions
,
719 g_object_ref (info
->extension
));
721 g_list_foreach (manager
->priv
->windows
, (GFunc
) attach_window
,
725 if (info
->extension
!= NULL
)
727 info
->info
.active
= TRUE
;
731 info
->info
.active
= FALSE
;
732 info
->load_failed
= TRUE
;
737 detach_window (EphyWindow
*window
,
738 EphyExtension
*extension
)
742 tabs
= ephy_window_get_tabs (window
);
743 for (l
= tabs
; l
; l
= l
->next
)
745 ephy_extension_detach_tab (extension
, window
,
750 ephy_extension_detach_window (extension
, window
);
754 unload_extension (EphyExtensionsManager
*manager
,
757 g_return_if_fail (info
->loader
!= NULL
);
758 g_return_if_fail (info
->extension
!= NULL
|| info
->load_failed
);
760 LOG ("Unloading extension '%s'", info
->info
.identifier
);
762 if (info
->load_failed
) return;
764 /* detach if the extension implements EphyExtensionIface */
765 if (EPHY_IS_EXTENSION (info
->extension
))
767 g_list_foreach (manager
->priv
->windows
, (GFunc
) detach_window
,
770 manager
->priv
->extensions
=
771 g_list_remove (manager
->priv
->extensions
, info
->extension
);
773 /* we own two refs to the extension, the one we added when
774 * we added it to the priv->extensions list, and the one returned
775 * from get_object. Release object, and queue a unref, since if the
776 * extension has its own functions queued in the idle loop, the
777 * functions must exist in memory before being called.
779 ephy_object_idle_unref (info
->extension
);
782 ephy_loader_release_object (info
->loader
, G_OBJECT (info
->extension
));
784 info
->info
.active
= FALSE
;
785 info
->extension
= NULL
;
789 sync_loaded_extensions (EphyExtensionsManager
*manager
)
793 GSList
*active_extensions
= NULL
;
798 LOG ("Synching changed list of active extensions");
800 client
= gconf_client_get_default ();
801 g_return_if_fail (client
!= NULL
);
803 value
= gconf_client_get (client
, CONF_LOADED_EXTENSIONS
, NULL
);
805 /* make sure the extensions-manager-ui is loaded */
807 value
->type
!= GCONF_VALUE_LIST
||
808 gconf_value_get_list_type (value
) != GCONF_VALUE_STRING
)
810 active_extensions
= g_slist_prepend (active_extensions
,
811 g_strdup ("extensions-manager-ui"));
812 eel_gconf_set_string_list (CONF_LOADED_EXTENSIONS
, active_extensions
);
816 active_extensions
= eel_gconf_get_string_list (CONF_LOADED_EXTENSIONS
);
819 for (l
= manager
->priv
->data
; l
!= NULL
; l
= l
->next
)
823 info
= (ExtensionInfo
*) l
->data
;
825 active
= (g_slist_find_custom (active_extensions
,
826 info
->info
.identifier
,
827 (GCompareFunc
) strcmp
) != NULL
);
829 LOG ("Extension '%s' is %sactive and %sloaded",
830 info
->info
.identifier
,
831 active
? "" : "not ",
832 info
->info
.active
? "" : "not ");
834 changed
= ( info
->info
.enabled
!= active
);
836 info
->info
.enabled
= active
;
838 if (active
!= info
->info
.active
)
842 load_extension (manager
, info
);
846 unload_extension (manager
, info
);
849 if (active
== info
->info
.active
)
857 g_signal_emit (manager
, signals
[CHANGED
], 0, info
);
861 g_slist_foreach (active_extensions
, (GFunc
) g_free
, NULL
);
862 g_slist_free (active_extensions
);
866 gconf_value_free (value
);
868 g_object_unref (client
);
872 ephy_extensions_manager_unload_file (EphyExtensionsManager
*manager
,
879 identifier
= path_to_identifier (path
);
881 l
= g_list_find_custom (manager
->priv
->data
, identifier
,
882 (GCompareFunc
) find_extension_info
);
886 info
= (ExtensionInfo
*) l
->data
;
888 manager
->priv
->data
= g_list_remove (manager
->priv
->data
, info
);
890 if (info
->info
.active
== TRUE
)
892 unload_extension (manager
, info
);
895 g_signal_emit (manager
, signals
[REMOVED
], 0, info
);
897 free_extension_info (info
);
904 reload_sync_cb (EphyExtensionsManager
*manager
)
906 EphyExtensionsManagerPrivate
*priv
= manager
->priv
;
908 if (priv
->sync_timeout_id
!= 0)
910 g_source_remove (priv
->sync_timeout_id
);
911 priv
->sync_timeout_id
= 0;
914 sync_loaded_extensions (manager
);
920 reload_cb (gpointer
*data
)
922 EphyExtensionsManager
*manager
= EPHY_EXTENSIONS_MANAGER (data
[0]);
923 EphyExtensionsManagerPrivate
*priv
= manager
->priv
;
924 char *path
= data
[1];
926 LOG ("Reloading %s", path
);
928 /* We still need path and don't want to remove the timeout
929 * which will be removed automatically when we return, so
930 * just use _steal instead of _remove.
932 g_hash_table_steal (priv
->reload_hash
, path
);
934 ephy_extensions_manager_load_file (manager
, path
);
937 /* Schedule a sync of active extensions */
938 /* FIXME: just look if we need to activate *this* extension? */
940 if (priv
->sync_timeout_id
!= 0)
942 g_source_remove (priv
->sync_timeout_id
);
945 priv
->sync_timeout_id
= g_timeout_add_seconds (RELOAD_SYNC_DELAY
,
946 (GSourceFunc
) reload_sync_cb
,
952 schedule_load_from_monitor (EphyExtensionsManager
*manager
,
955 EphyExtensionsManagerPrivate
*priv
= manager
->priv
;
956 char *identifier
, *copy
;
960 /* When a file is installed, it sometimes gets CREATED empty and then
961 * gets its contents filled later (for a CHANGED signal). Theoretically
962 * I suppose we could get a CHANGED signal when the file is half-full,
963 * but I doubt that'll happen much (the files are <1000 bytes). We
964 * don't want warnings all over the place, so we just wait a bit before
965 * actually reloading the file. (We're assuming that if a file is
966 * empty it'll be filled soon and this function will be called again.)
968 * Oh, and we return if the extension is already loaded, too.
971 identifier
= path_to_identifier (path
);
972 g_return_if_fail (identifier
!= NULL
);
973 if (identifier
== NULL
) return;
975 if (g_list_find_custom (manager
->priv
->data
, identifier
,
976 (GCompareFunc
) find_extension_info
) != NULL
)
983 g_return_if_fail (priv
->reload_hash
!= NULL
);
985 data
= g_new (gpointer
, 2);
986 data
[0] = (gpointer
) manager
;
987 data
[1] = copy
= g_strdup (path
);
988 timeout_id
= g_timeout_add_full (G_PRIORITY_LOW
, RELOAD_DELAY
,
989 (GSourceFunc
) reload_cb
,
990 data
, (GDestroyNotify
) g_free
);
991 g_hash_table_replace (priv
->reload_hash
, copy
/* owns it */,
992 GUINT_TO_POINTER (timeout_id
));
996 dir_changed_cb (GnomeVFSMonitorHandle
*handle
,
997 const char *monitor_uri
,
998 const char *info_uri
,
999 GnomeVFSMonitorEventType event_type
,
1000 EphyExtensionsManager
*manager
)
1005 * We only deal with XML and INI files:
1006 * Add them to the manager when created, remove them when deleted.
1008 if (format_from_path (info_uri
) == FORMAT_UNKNOWN
) return;
1010 path
= gnome_vfs_get_local_path_from_uri (info_uri
);
1014 case GNOME_VFS_MONITOR_EVENT_CREATED
:
1015 case GNOME_VFS_MONITOR_EVENT_CHANGED
:
1016 schedule_load_from_monitor (manager
, path
);
1018 case GNOME_VFS_MONITOR_EVENT_DELETED
:
1019 ephy_extensions_manager_unload_file (manager
, path
);
1029 ephy_extensions_manager_load_dir (EphyExtensionsManager
*manager
,
1036 GnomeVFSMonitorHandle
*monitor
;
1039 LOG ("Scanning directory '%s'", path
);
1041 START_PROFILER ("Scanning directory")
1048 while ((e
= readdir (d
)) != NULL
)
1050 if (format_from_path (e
->d_name
) != FORMAT_UNKNOWN
)
1052 file_path
= g_build_filename (path
, e
->d_name
, NULL
);
1053 ephy_extensions_manager_load_file (manager
, file_path
);
1059 file_uri
= gnome_vfs_get_uri_from_local_path (path
);
1060 res
= gnome_vfs_monitor_add (&monitor
,
1062 GNOME_VFS_MONITOR_DIRECTORY
,
1063 (GnomeVFSMonitorCallback
) dir_changed_cb
,
1067 if (res
== GNOME_VFS_OK
)
1069 manager
->priv
->dir_monitors
= g_list_prepend
1070 (manager
->priv
->dir_monitors
, monitor
);
1073 STOP_PROFILER ("Scanning directory")
1077 active_extensions_notifier (GConfClient
*client
,
1080 EphyExtensionsManager
*manager
)
1082 sync_loaded_extensions (manager
);
1086 cancel_timeout (gpointer data
)
1088 guint id
= GPOINTER_TO_UINT (data
);
1090 g_source_remove (id
);
1094 ephy_extensions_manager_init (EphyExtensionsManager
*manager
)
1096 EphyExtensionsManagerPrivate
*priv
;
1098 priv
= manager
->priv
= EPHY_EXTENSIONS_MANAGER_GET_PRIVATE (manager
);
1100 priv
->reload_hash
= g_hash_table_new_full (g_str_hash
, g_str_equal
,
1101 (GDestroyNotify
) g_free
,
1102 (GDestroyNotify
) cancel_timeout
);
1106 ephy_extensions_manager_startup (EphyExtensionsManager
*manager
)
1110 g_return_if_fail (EPHY_IS_EXTENSIONS_MANAGER (manager
));
1112 LOG ("EphyExtensionsManager startup");
1114 /* load the extensions descriptions */
1115 path
= g_build_filename (ephy_dot_dir (), "extensions", NULL
);
1116 ephy_extensions_manager_load_dir (manager
, path
);
1119 ephy_extensions_manager_load_dir (manager
, EXTENSIONS_DIR
);
1121 active_extensions_notifier (NULL
, 0, NULL
, manager
);
1122 manager
->priv
->active_extensions_notifier_id
=
1123 eel_gconf_notification_add
1124 (CONF_LOADED_EXTENSIONS
,
1125 (GConfClientNotifyFunc
) active_extensions_notifier
,
1130 ephy_extensions_manager_dispose (GObject
*object
)
1132 EphyExtensionsManager
*manager
= EPHY_EXTENSIONS_MANAGER (object
);
1133 EphyExtensionsManagerPrivate
*priv
= manager
->priv
;
1135 #ifdef ENABLE_LEGACY_FORMAT
1136 if (priv
->xml2ini_xsl
!= NULL
)
1138 xsltFreeStylesheet (priv
->xml2ini_xsl
);
1139 priv
->xml2ini_xsl
= NULL
;
1143 if (priv
->active_extensions_notifier_id
!= 0)
1145 eel_gconf_notification_remove (priv
->active_extensions_notifier_id
);
1146 priv
->active_extensions_notifier_id
= 0;
1149 if (priv
->reload_hash
!= NULL
)
1151 g_hash_table_destroy (priv
->reload_hash
);
1152 priv
->reload_hash
= NULL
;
1155 if (priv
->sync_timeout_id
!= 0)
1157 g_source_remove (priv
->sync_timeout_id
);
1158 priv
->sync_timeout_id
= 0;
1161 if (priv
->dir_monitors
!= NULL
)
1163 g_list_foreach (priv
->dir_monitors
, (GFunc
) gnome_vfs_monitor_cancel
, NULL
);
1164 g_list_free (priv
->dir_monitors
);
1165 priv
->dir_monitors
= NULL
;
1168 if (priv
->extensions
!= NULL
)
1170 g_list_foreach (priv
->extensions
, (GFunc
) g_object_unref
, NULL
);
1171 g_list_free (priv
->extensions
);
1172 priv
->extensions
= NULL
;
1175 if (priv
->factories
!= NULL
)
1177 /* FIXME release loaded loaders */
1178 g_list_foreach (priv
->factories
, (GFunc
) free_loader_info
, NULL
);
1179 g_list_free (priv
->factories
);
1180 priv
->factories
= NULL
;
1183 if (priv
->data
!= NULL
)
1185 g_list_foreach (priv
->data
, (GFunc
) free_extension_info
, NULL
);
1186 g_list_free (priv
->data
);
1190 if (priv
->windows
!= NULL
)
1192 g_list_free (priv
->windows
);
1193 priv
->windows
= NULL
;
1196 parent_class
->dispose (object
);
1200 attach_extension_to_window (EphyExtension
*extension
,
1203 attach_window (window
, extension
);
1207 impl_attach_window (EphyExtension
*extension
,
1210 EphyExtensionsManager
*manager
= EPHY_EXTENSIONS_MANAGER (extension
);
1212 LOG ("Attach window %p", window
);
1214 g_list_foreach (manager
->priv
->extensions
,
1215 (GFunc
) attach_extension_to_window
, window
);
1217 manager
->priv
->windows
= g_list_prepend (manager
->priv
->windows
, window
);
1221 impl_detach_window (EphyExtension
*extension
,
1224 EphyExtensionsManager
*manager
= EPHY_EXTENSIONS_MANAGER (extension
);
1227 LOG ("Detach window %p", window
);
1229 manager
->priv
->windows
= g_list_remove (manager
->priv
->windows
, window
);
1231 g_object_ref (window
);
1233 /* Detach tabs (uses impl_detach_tab) */
1234 tabs
= ephy_window_get_tabs (window
);
1235 for (l
= tabs
; l
; l
= l
->next
)
1237 ephy_extension_detach_tab (extension
, window
,
1238 EPHY_TAB (l
->data
));
1242 /* Then detach the window */
1243 g_list_foreach (manager
->priv
->extensions
,
1244 (GFunc
) ephy_extension_detach_window
, window
);
1246 g_object_unref (window
);
1250 impl_attach_tab (EphyExtension
*extension
,
1254 EphyExtensionsManager
*manager
= EPHY_EXTENSIONS_MANAGER (extension
);
1257 LOG ("Attach window %p tab %p", window
, tab
);
1259 for (l
= manager
->priv
->extensions
; l
; l
= l
->next
)
1261 ephy_extension_attach_tab (EPHY_EXTENSION (l
->data
),
1267 impl_detach_tab (EphyExtension
*extension
,
1271 EphyExtensionsManager
*manager
= EPHY_EXTENSIONS_MANAGER (extension
);
1274 LOG ("Detach window %p tab %p", window
, tab
);
1276 g_object_ref (window
);
1279 for (l
= manager
->priv
->extensions
; l
; l
= l
->next
)
1281 ephy_extension_detach_tab (EPHY_EXTENSION (l
->data
),
1285 g_object_unref (tab
);
1286 g_object_unref (window
);
1290 ephy_extensions_manager_iface_init (EphyExtensionIface
*iface
)
1292 iface
->attach_window
= impl_attach_window
;
1293 iface
->detach_window
= impl_detach_window
;
1294 iface
->attach_tab
= impl_attach_tab
;
1295 iface
->detach_tab
= impl_detach_tab
;
1299 ephy_extensions_manager_class_init (EphyExtensionsManagerClass
*class)
1301 GObjectClass
*object_class
= G_OBJECT_CLASS (class);
1303 parent_class
= (GObjectClass
*) g_type_class_peek_parent (class);
1305 object_class
->dispose
= ephy_extensions_manager_dispose
;
1308 g_signal_new ("changed",
1309 G_OBJECT_CLASS_TYPE (object_class
),
1311 G_STRUCT_OFFSET (EphyExtensionsManagerClass
, changed
),
1313 g_cclosure_marshal_VOID__POINTER
,
1318 g_signal_new ("added",
1319 G_OBJECT_CLASS_TYPE (object_class
),
1321 G_STRUCT_OFFSET (EphyExtensionsManagerClass
, added
),
1323 g_cclosure_marshal_VOID__POINTER
,
1328 g_signal_new ("removed",
1329 G_OBJECT_CLASS_TYPE (object_class
),
1331 G_STRUCT_OFFSET (EphyExtensionsManagerClass
, removed
),
1333 g_cclosure_marshal_VOID__POINTER
,
1338 g_type_class_add_private (object_class
, sizeof (EphyExtensionsManagerPrivate
));