From 611e5407571e19353a98292d953e90e63073ba61 Mon Sep 17 00:00:00 2001 From: Christian Persch Date: Sun, 17 Oct 2004 13:39:12 +0000 Subject: R lib/ephy-module-loader.c: R lib/ephy-module-loader.h: A 2004-10-17 Christian Persch * lib/Makefile.am: R lib/ephy-module-loader.c: R lib/ephy-module-loader.h: A lib/ephy-module.c: (ephy_module_get_type), (ephy_module_load), (ephy_module_unload), (ephy_module_get_path), (ephy_module_new_object), (ephy_module_init), (ephy_module_finalize), (ephy_module_class_init), (ephy_module_new): A lib/ephy-module.h: s/EphyModuleLoader/EphyModule/g since "loader" now means something different. A lib/ephy-loader.c: (ephy_loader_get_type), (ephy_loader_type), (ephy_loader_get_object), (ephy_loader_release_object): A lib/ephy-loader.h: Generic object loader. A lib/ephy-shlib-loader.c: (ephy_shlib_loader_get_type), (free_loader_data), (ephy_shlib_loader_init), (ephy_shlib_loader_finalize), (find_library), (find_object), (idle_unref), (impl_get_object), (impl_release_object), (ephy_shlib_loader_iface_init), (ephy_shlib_loader_class_init): A lib/ephy-shlib-loader.h: A .so loader. * src/Makefile.am: * src/ephy-extensions-manager.c: (ephy_extensions_manager_load), (ephy_extensions_manager_unload), (ephy_extensions_manager_register), (ephy_extensions_manager_get_extensions), (free_extension_info), (free_loader_info), (find_extension_info), (ephy_extensions_manager_load_file), (find_loader), (get_loader_for_type), (attach_window), (load_extension), (detach_window), (unload_extension), (ephy_extensions_manager_load_dir), (active_extensions_notifier), (ephy_extensions_manager_init), (ephy_extensions_manager_finalize), (impl_attach_window), (impl_detach_window), (ephy_extensions_manager_class_init): * src/ephy-extensions-manager.h: Read extension descriptions from .xml, load them with the specified loader (for now, just only .so is supported). * src/ephy-shell.c: (ephy_shell_finalize), (ephy_shell_get_session), (ephy_shell_get_extensions_manager): Minor API change in extensions manager. * data/epiphany.schemas.in: Add extensions-manager-ui as default active extension. 2004-10-10 Marco Pesenti Gritti --- src/ephy-extensions-manager.c | 867 ++++++++++++++++++++++++++++++------------ 1 file changed, 630 insertions(+), 237 deletions(-) (limited to 'src/ephy-extensions-manager.c') diff --git a/src/ephy-extensions-manager.c b/src/ephy-extensions-manager.c index 7b66f289e..0fc15bcbd 100644 --- a/src/ephy-extensions-manager.c +++ b/src/ephy-extensions-manager.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2003 Marco Pesenti Gritti - * Copyright (C) 2003 Christian Persch + * Copyright (C) 2003, 2004 Christian Persch + * Copyright (C) 2004 Adam Hooper * * 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 @@ -25,12 +26,16 @@ #include "ephy-extensions-manager.h" +#include "ephy-loader.h" +#include "ephy-shlib-loader.h" + +#include "ephy-node-db.h" #include "ephy-shell.h" -#include "ephy-session.h" /* Weird (session is an extension) but it works */ -#include "ephy-module-loader.h" +#include "eel-gconf-extensions.h" +#include "ephy-file-helpers.h" #include "ephy-debug.h" -#include "eel-gconf-extensions.h" +#include #include #include @@ -40,18 +45,46 @@ #define EPHY_EXTENSIONS_MANAGER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_EXTENSIONS_MANAGER, EphyExtensionsManagerPrivate)) -struct EphyExtensionsManagerPrivate +struct _EphyExtensionsManagerPrivate { - GHashTable *extensions; - GSList *internal_extensions; + gboolean initialised; + + GList *data; + GList *factories; + GList *extensions; + GList *windows; guint active_extensions_notifier_id; }; typedef struct { - EphyModuleLoader *loader; /* NULL if never loaded */ - EphyExtension *extension; /* NULL if unloaded */ -} ExtInfo; + EphyExtensionInfo info; + guint version; + gboolean load_deferred; + gboolean load_failed; + + xmlChar *gettext_domain; + xmlChar *locale_directory; + xmlChar *loader_type; + GData *loader_attributes; + + EphyLoader *loader; /* NULL if never loaded */ + GObject *extension; /* NULL if unloaded */ +} ExtensionInfo; + +typedef struct +{ + char *type; + EphyLoader *loader; +} LoaderInfo; + +enum +{ + CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; static GObjectClass *parent_class = NULL; @@ -98,103 +131,29 @@ ephy_extensions_manager_get_type (void) return type; } -static void -free_ext_info (ExtInfo *info) -{ - if (info->extension) - { - g_object_unref (info->extension); - } - g_free (info); -} - -static void -windows_foreach (GFunc func, EphyExtension *extension) -{ - EphySession *session; - GList *windows; - - session = EPHY_SESSION (ephy_shell_get_session (ephy_shell)); - - windows = ephy_session_get_windows (session); - - g_list_foreach (windows, func, extension); - - g_list_free (windows); -} - -static void -attach_window (EphyWindow *window, - EphyExtension *extension) -{ - ephy_extension_attach_window (extension, window); -} - -static void -detach_window (EphyWindow *window, - EphyExtension *extension) -{ - ephy_extension_detach_window (extension, window); -} - -static EphyExtension * -instantiate_extension (EphyModuleLoader *loader) -{ - EphyExtension *extension; - - extension = EPHY_EXTENSION (ephy_module_loader_factory (loader)); - - if (EPHY_IS_EXTENSION (extension)) - { - windows_foreach ((GFunc) attach_window, extension); - - return extension; - } - - return NULL; -} - -static void -real_load (ExtInfo *info) -{ - if (info->extension != NULL) return; - - if (g_type_module_use (G_TYPE_MODULE (info->loader)) == FALSE) - { - g_warning ("Could not load extension file at %s\n", - ephy_module_loader_get_path (info->loader)); - return; - } - - info->extension = instantiate_extension (info->loader); - - if (info->extension == NULL) - { - g_warning ("Could not load extension at %s\n", - ephy_module_loader_get_path (info->loader)); - } - - g_type_module_unuse (G_TYPE_MODULE (info->loader)); -} - /** * ephy_extensions_manager_load: * @manager: an #EphyExtensionsManager - * @filename: filename of an extension to load, minus "lib" and "extension.so" + * @name: identifier of the extension to load * - * Loads the @filename extension. + * Loads the @name extension. **/ void ephy_extensions_manager_load (EphyExtensionsManager *manager, - const char *filename) + const char *identifier) { GSList *gconf_exts; + g_return_if_fail (EPHY_IS_EXTENSIONS_MANAGER (manager)); + g_return_if_fail (identifier != NULL); + + LOG ("Adding '%s' to extensions", identifier) + gconf_exts = eel_gconf_get_string_list (CONF_LOADED_EXTENSIONS); - if (!g_slist_find_custom (gconf_exts, filename, (GCompareFunc) strcmp)) + if (!g_slist_find_custom (gconf_exts, identifier, (GCompareFunc) strcmp)) { - gconf_exts = g_slist_prepend (gconf_exts, g_strdup (filename)); + gconf_exts = g_slist_prepend (gconf_exts, g_strdup (identifier)); eel_gconf_set_string_list (CONF_LOADED_EXTENSIONS, gconf_exts); } @@ -203,28 +162,12 @@ ephy_extensions_manager_load (EphyExtensionsManager *manager, g_slist_free (gconf_exts); } -static void -real_unload (ExtInfo *info) -{ - if (info->extension == NULL) return; /* not loaded */ - - windows_foreach ((GFunc) detach_window, info->extension); - - /* - * Only unref the extension in the idle loop; if the extension has its - * own functions queued in the idle loop, the functions must exist in - * memory before being called. - */ - g_idle_add ((GSourceFunc) g_object_unref, info->extension); - info->extension = NULL; -} - /** * ephy_extensions_manager_unload: * @manager: an #EphyExtensionsManager - * @filename: filename of extension to unload, minus "lib" and "extension.so" + * @name: filename of extension to unload, minus "lib" and "extension.so" * - * Unloads the extension specified by @filename. + * Unloads the extension specified by @name. * * The extension with the same filename can afterwards be reloaded. However, * if any GTypes within the extension have changed parent types, Epiphany must @@ -232,14 +175,19 @@ real_unload (ExtInfo *info) **/ void ephy_extensions_manager_unload (EphyExtensionsManager *manager, - const char *filename) + const char *identifier) { GSList *gconf_exts; GSList *l; - + + g_return_if_fail (EPHY_IS_EXTENSIONS_MANAGER (manager)); + g_return_if_fail (identifier != NULL); + + LOG ("Removing '%s' from extensions", identifier) + gconf_exts = eel_gconf_get_string_list (CONF_LOADED_EXTENSIONS); - l = g_slist_find_custom (gconf_exts, filename, (GCompareFunc) strcmp); + l = g_slist_find_custom (gconf_exts, identifier, (GCompareFunc) strcmp); if (l != NULL) { @@ -255,119 +203,537 @@ ephy_extensions_manager_unload (EphyExtensionsManager *manager, } /** - * ephy_extensions_manager_add: + * ephy_extensions_manager_register: * @manager: an #EphyExtensionsManager - * @type: GType of the extension to add + * @object: an Extension * - * Creates a new instance of @type (which must be an #EphyExtension) and adds - * it to @manager. This is only used to load internal Epiphany extensions. - * - * Return value: a new instance of @type + * Registers @object with the extensions manager. @object must implement the + * #EphyExtension interface. **/ -EphyExtension * -ephy_extensions_manager_add (EphyExtensionsManager *manager, - GType type) +void +ephy_extensions_manager_register (EphyExtensionsManager *manager, + GObject *object) { - EphyExtension *extension; + g_return_if_fail (EPHY_IS_EXTENSIONS_MANAGER (manager)); + g_return_if_fail (EPHY_IS_EXTENSION (object)); - LOG ("adding extensions of type %s", g_type_name (type)) - - extension = EPHY_EXTENSION (g_object_new (type, NULL)); - if (!EPHY_IS_EXTENSION (extension)) - { - g_object_unref (extension); + LOG ("Registering internal extension of type %s", + g_type_name (((GTypeClass *) object)->g_type)) - return NULL; - } + manager->priv->extensions = g_list_prepend (manager->priv->extensions, + g_object_ref (object)); +} - manager->priv->internal_extensions = - g_slist_append (manager->priv->internal_extensions, extension); - return extension; +/** + * ephy_extensions_manager_get_extensions: + * @manager: an #EphyExtensionsManager + * + * Returns the list of known extensions. + * + * Returns: a list of #EphyExtensionInfo + **/ +GList * +ephy_extensions_manager_get_extensions (EphyExtensionsManager *manager) +{ + return g_list_copy (manager->priv->data); } static void -sync_one_extension (const char *name, - ExtInfo *info, - GSList *wanted_exts) +free_extension_info (ExtensionInfo *info) { - if (g_slist_find_custom (wanted_exts, name, (GCompareFunc) strcmp)) + EphyExtensionInfo *einfo = (EphyExtensionInfo *) info; + + g_free (einfo->identifier); + xmlFree ((xmlChar *) einfo->name); + xmlFree ((xmlChar *) einfo->description); + g_list_foreach (einfo->authors, (GFunc) xmlFree, NULL); + g_list_free (einfo->authors); + xmlFree ((xmlChar *) einfo->url); + xmlFree ((xmlChar *) info->gettext_domain); + xmlFree ((xmlChar *) info->locale_directory); + xmlFree ((xmlChar *) info->loader_type); + g_datalist_clear (&info->loader_attributes); + + if (info->extension != NULL) { - real_load (info); + g_return_if_fail (info->loader != NULL); + + ephy_loader_release_object (info->loader, info->extension); } - else + if (info->loader != NULL) { - real_unload (info); + g_object_unref (info->loader); } + + g_free (info); } static void -ephy_extensions_manager_sync_gconf (EphyExtensionsManager *manager) +free_loader_info (LoaderInfo *info) { - GSList *wanted_exts; - - wanted_exts = eel_gconf_get_string_list (CONF_LOADED_EXTENSIONS); - - g_hash_table_foreach (manager->priv->extensions, - (GHFunc) sync_one_extension, - wanted_exts); + g_free (info->type); + g_object_unref (info->loader); + g_free (info); +} - g_slist_foreach (wanted_exts, (GFunc) g_free, NULL); - g_slist_free (wanted_exts); +static int +find_extension_info (const ExtensionInfo *info, + const char *identifier) +{ + return strcmp (info->info.identifier, identifier); } +typedef enum +{ + STATE_START, + STATE_STOP, + STATE_ERROR, + STATE_EXTENSION, + STATE_NAME, + STATE_DESCRIPTION, + STATE_VERSION, + STATE_AUTHOR, + STATE_URL, + STATE_GETTEXT_DOMAIN, + STATE_LOCALE_DIRECTORY, + STATE_LOADER, + STATE_LOADER_ATTRIBUTE, + STATE_LOAD_DEFERRED, +} ParserState; + static void ephy_extensions_manager_load_file (EphyExtensionsManager *manager, const char *dir, const char *filename) { - ExtInfo *info; - char *name; - char *path; + xmlTextReaderPtr reader; + ParserState state = STATE_START; + GQuark attr_quark = 0; + EphyExtensionInfo *einfo; + ExtensionInfo *info; + int ret; + char *identifier, *dot, *path; + + LOG ("Loading description file '%s'", filename) + + identifier = g_path_get_basename (filename); + dot = strstr (identifier, ".xml"); + g_return_if_fail (dot != NULL); + *dot = '\0'; + + if (g_list_find_custom (manager->priv->data, identifier, + (GCompareFunc) find_extension_info) != NULL) + { + g_warning ("Extension description for '%s' already read!\n", + identifier); + g_free (identifier); + return; + } - /* Must match "libBLAHextension.so" */ - if (!g_str_has_prefix (filename, "lib") - || !g_str_has_suffix (filename, "extension." G_MODULE_SUFFIX)) + path = g_build_filename (dir, filename, NULL); + if (g_file_test (path, G_FILE_TEST_EXISTS) == FALSE) { + g_warning ("'%s' doesn't exist\n", filename); + g_free (identifier); + g_free (path); return; } + + reader = xmlNewTextReaderFilename (path); + g_free (path); - name = g_strndup (filename + 3, - strlen(filename) - 13 - strlen(G_MODULE_SUFFIX)); + if (reader == NULL) + { + g_warning ("Couldn't read '%s'\n", filename); + g_free (identifier); + return; + } + + info = g_new0 (ExtensionInfo, 1); + einfo = (EphyExtensionInfo *) info; + einfo->identifier = identifier; + g_datalist_init (&info->loader_attributes); - if (g_hash_table_lookup (manager->priv->extensions, name) != NULL) + ret = xmlTextReaderRead (reader); + + while (ret == 1) { - /* We already have another version stored */ - g_free (name); + const xmlChar *tag; + xmlReaderTypes type; + + tag = xmlTextReaderConstName (reader); + type = xmlTextReaderNodeType (reader); + + if (state == STATE_LOADER && + type == XML_READER_TYPE_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "attribute")) + { + xmlChar *name; + + state = STATE_LOADER_ATTRIBUTE; + + name = xmlTextReaderGetAttribute (reader, (const xmlChar *) "name"); + attr_quark = g_quark_from_string ((const char *) name); + xmlFree (name); + } + else if (state == STATE_EXTENSION && + type == XML_READER_TYPE_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "author")) + { + state = STATE_AUTHOR; + } + else if (state == STATE_EXTENSION && + type == XML_READER_TYPE_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "description")) + { + state = STATE_DESCRIPTION; + } + else if (state == STATE_EXTENSION && + type == XML_READER_TYPE_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "gettext-domain")) + { + state = STATE_GETTEXT_DOMAIN; + } + else if (state == STATE_EXTENSION && + type == XML_READER_TYPE_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "load-deferred")) + { + state = STATE_LOAD_DEFERRED; + } + else if (state == STATE_EXTENSION && + type == XML_READER_TYPE_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "locale-directory")) + { + state = STATE_LOCALE_DIRECTORY; + } + else if (state == STATE_EXTENSION && + type == XML_READER_TYPE_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "name")) + { + state = STATE_NAME; + } + else if (state == STATE_EXTENSION && + type == XML_READER_TYPE_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "url")) + { + state = STATE_URL; + } + else if (state == STATE_EXTENSION && + type == XML_READER_TYPE_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "version")) + { + state = STATE_VERSION; + } + else if (state == STATE_EXTENSION && + type == XML_READER_TYPE_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "loader")) + { + state = STATE_LOADER; + + info->loader_type = xmlTextReaderGetAttribute (reader, (const xmlChar *) "type"); + } + else if (state == STATE_LOADER_ATTRIBUTE && + type == XML_READER_TYPE_TEXT && + attr_quark != 0) + { + xmlChar *value; + + value = xmlTextReaderValue (reader); + + g_datalist_id_set_data_full (&info->loader_attributes, + attr_quark, value, + (GDestroyNotify) xmlFree); + attr_quark = 0; + } + else if (state == STATE_LOADER_ATTRIBUTE && + type == XML_READER_TYPE_END_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "attribute")) + { + state = STATE_LOADER; + } + else if (state == STATE_AUTHOR && + type == XML_READER_TYPE_TEXT) + { + einfo->authors = g_list_prepend + (einfo->authors, xmlTextReaderValue (reader)); + } + else if (state == STATE_DESCRIPTION && + type == XML_READER_TYPE_TEXT) + { + + einfo->description = xmlTextReaderValue (reader); + } + else if (state == STATE_GETTEXT_DOMAIN && + type == XML_READER_TYPE_TEXT) + { + info->gettext_domain = xmlTextReaderValue (reader); + } + else if (state == STATE_LOAD_DEFERRED && + type == XML_READER_TYPE_TEXT) + { + const xmlChar *value; + + value = xmlTextReaderConstValue (reader); + info->load_deferred = + (value != NULL && xmlStrEqual (value, (const xmlChar *) "true")); + } + else if (state == STATE_LOCALE_DIRECTORY && + type == XML_READER_TYPE_TEXT) + { + info->locale_directory = xmlTextReaderValue (reader); + } + else if (state == STATE_NAME && + type == XML_READER_TYPE_TEXT) + { + einfo->name = xmlTextReaderValue (reader); + } + else if (state == STATE_VERSION && + type == XML_READER_TYPE_TEXT) + { + info->version = (guint) strtol ((const char *) xmlTextReaderConstValue (reader), NULL, 10); + } + else if (state == STATE_URL && + type == XML_READER_TYPE_TEXT) + { + einfo->url = xmlTextReaderValue (reader); + } + else if (state == STATE_AUTHOR && + type == XML_READER_TYPE_END_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "author")) + { + state = STATE_EXTENSION; + } + else if (state == STATE_DESCRIPTION && + type == XML_READER_TYPE_END_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "description")) + { + state = STATE_EXTENSION; + } + else if (state == STATE_GETTEXT_DOMAIN && + type == XML_READER_TYPE_END_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "gettext-domain")) + { + state = STATE_EXTENSION; + } + else if (state == STATE_LOCALE_DIRECTORY && + type == XML_READER_TYPE_END_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "locale-directory")) + { + state = STATE_EXTENSION; + } + else if (state == STATE_LOADER && + type == XML_READER_TYPE_END_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "loader")) + { + state = STATE_EXTENSION; + } + else if (state == STATE_NAME && + type == XML_READER_TYPE_END_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "name")) + { + state = STATE_EXTENSION; + } + else if (state == STATE_URL && + type == XML_READER_TYPE_END_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "url")) + { + state = STATE_EXTENSION; + } + else if (state == STATE_VERSION && + type == XML_READER_TYPE_END_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "version")) + { + state = STATE_EXTENSION; + } + else if (type == XML_READER_TYPE_SIGNIFICANT_WHITESPACE || + type == XML_READER_TYPE_WHITESPACE || + type == XML_READER_TYPE_TEXT) + { + /* eat it */ + } + else if (state == STATE_START && + type == XML_READER_TYPE_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "extension")) + { + state = STATE_EXTENSION; + } + else if (state == STATE_EXTENSION && + type == XML_READER_TYPE_END_ELEMENT && + xmlStrEqual (tag, (const xmlChar *) "extension")) + { + state = STATE_STOP; + } + else + { + const xmlChar *content; + + content = xmlTextReaderConstValue (reader); + g_warning ("tag '%s' of type %d in state %d with content '%s' was unexpected!", + tag, type, state, content ? (char *) content : "(null)"); + + state = STATE_ERROR; + break; + } + + ret = xmlTextReaderRead (reader); + } + + xmlFreeTextReader (reader); + + /* sanity check */ + if (ret < 0 || state != STATE_STOP || + einfo->name == NULL || einfo->description == NULL || + info->loader_type == NULL || info->loader_type[0] == '\0') + { + free_extension_info (info); return; } - path = g_build_filename (dir, filename, NULL); + manager->priv->data = g_list_prepend (manager->priv->data, info); +} - info = g_new0 (ExtInfo, 1); - info->loader = ephy_module_loader_new (path); +static int +find_loader (const LoaderInfo *info, + const char *type) +{ + return strcmp (info->type, type); +} - g_free (path); +static EphyLoader * +get_loader_for_type (EphyExtensionsManager *manager, + const char *type) +{ + LoaderInfo *info; + GList *l; + + LOG ("Looking for loader for type '%s'", type) - g_hash_table_insert (manager->priv->extensions, name, info); + l = g_list_find_custom (manager->priv->factories, type, + (GCompareFunc) find_loader); + if (l != NULL) + { + info = (LoaderInfo *) l->data; + return g_object_ref (info->loader); + } + + if (strcmp (type, "shlib") == 0) + { + info = g_new (LoaderInfo, 1); + info->type = g_strdup (type); + info->loader = g_object_new (EPHY_TYPE_SHLIB_LOADER, NULL); + + manager->priv->factories = + g_list_append (manager->priv->factories, info); + + return g_object_ref (info->loader); + } + + /* try to load a loader */ + g_return_val_if_reached (NULL); + + return NULL; } -/** - * ephy_extensions_manager_load_dir: - * @manager: an #EphyExtensionsManager - * @path: directory to load - * - * Searches @path for all files matching the pattern - * "libEXTextension.so" and stores them in @manager, ready to be - * loaded. - **/ -void + +static void +attach_window (EphyWindow *window, + EphyExtension *extension) +{ + ephy_extension_attach_window (extension, window); +} + +static void +load_extension (EphyExtensionsManager *manager, + ExtensionInfo *info) +{ + + EphyLoader *loader; + + g_return_if_fail (info->extension == NULL); + + LOG ("Loading extension '%s'", info->info.identifier) + + /* don't try again */ + if (info->load_failed) return; + + /* get a loader */ + loader = get_loader_for_type (manager, info->loader_type); + if (loader == NULL) + { + g_warning ("No loader found for extension '%s' of type '%s'\n", + info->info.identifier, info->loader_type); + return; + } + + info->loader = loader; + + info->extension = ephy_loader_get_object (loader, + &info->loader_attributes); + + if (info->extension != NULL) + { + manager->priv->extensions = + g_list_prepend (manager->priv->extensions, + g_object_ref (info->extension)); + + g_list_foreach (manager->priv->windows, (GFunc) attach_window, + info->extension); + + info->info.active = TRUE; + + g_signal_emit (manager, signals[CHANGED], 0, info); + } + else + { + info->load_failed = TRUE; + } +} + +static void +detach_window (EphyWindow *window, + EphyExtension *extension) +{ + ephy_extension_detach_window (extension, window); +} + +static void +unload_extension (EphyExtensionsManager *manager, + ExtensionInfo *info) +{ + g_return_if_fail (info->loader != NULL); + g_return_if_fail (info->extension != NULL || info->load_failed); + + LOG ("Unloading extension '%s'", info->info.identifier) + + if (info->load_failed) return; + + g_list_foreach (manager->priv->windows, (GFunc) detach_window, + info->extension); + + manager->priv->extensions = + g_list_remove (manager->priv->extensions, info->extension); + + ephy_loader_release_object (info->loader, G_OBJECT (info->extension)); + g_object_unref (info->extension); + + info->info.active = FALSE; + info->extension = NULL; + + g_signal_emit (manager, signals[CHANGED], 0, info); +} + +static void ephy_extensions_manager_load_dir (EphyExtensionsManager *manager, const char *path) { DIR *d; struct dirent *e; + LOG ("Scanning directory '%s'", path) + + START_PROFILER ("Scanning directory") + d = opendir (path); if (d == NULL) { @@ -375,11 +741,14 @@ ephy_extensions_manager_load_dir (EphyExtensionsManager *manager, } while ((e = readdir (d)) != NULL) { - ephy_extensions_manager_load_file (manager, path, e->d_name); + if (g_str_has_suffix (e->d_name, ".xml")) + { + ephy_extensions_manager_load_file (manager, path, e->d_name); + } } closedir (d); - ephy_extensions_manager_sync_gconf (manager); + STOP_PROFILER ("Scanning directory") } static void @@ -388,57 +757,91 @@ active_extensions_notifier (GConfClient *client, GConfEntry *entry, EphyExtensionsManager *manager) { - ephy_extensions_manager_sync_gconf (manager); + GSList *active_extensions; + GList *l; + gboolean active; + ExtensionInfo *info; + + LOG ("Synching changed list of active extensions") + + active_extensions = eel_gconf_get_string_list (CONF_LOADED_EXTENSIONS); + + for (l = manager->priv->data; l != NULL; l = l->next) + { + info = (ExtensionInfo *) l->data; + + active = (g_slist_find_custom (active_extensions, + info->info.identifier, + (GCompareFunc) strcmp) != NULL); + + LOG ("Extension '%s' is %sactive and %sloaded", + info->info.identifier, + active ? "" : "not ", + info->info.active ? "" : "not ") + + if (active != info->info.active) + { + if (active) + { + load_extension (manager, info); + } + else + { + unload_extension (manager, info); + } + } + } + + g_slist_foreach (active_extensions, (GFunc) g_free, NULL); + g_slist_free (active_extensions); } static void ephy_extensions_manager_init (EphyExtensionsManager *manager) { + char *path; + manager->priv = EPHY_EXTENSIONS_MANAGER_GET_PRIVATE (manager); LOG ("EphyExtensionsManager initialising") - manager->priv->extensions = g_hash_table_new_full - (g_str_hash, g_str_equal, - (GDestroyNotify) g_free, (GDestroyNotify) free_ext_info); + /* load the extensions descriptions */ + path = g_build_filename (ephy_dot_dir (), "extensions", NULL); + ephy_extensions_manager_load_dir (manager, path); + g_free (path); - manager->priv->internal_extensions = NULL; + ephy_extensions_manager_load_dir (manager, EXTENSIONS_DIR); + active_extensions_notifier (NULL, 0, NULL, manager); manager->priv->active_extensions_notifier_id = - eel_gconf_notification_add (CONF_LOADED_EXTENSIONS, - (GConfClientNotifyFunc) - active_extensions_notifier, - manager); + eel_gconf_notification_add + (CONF_LOADED_EXTENSIONS, + (GConfClientNotifyFunc) active_extensions_notifier, + manager); } static void ephy_extensions_manager_finalize (GObject *object) { EphyExtensionsManager *manager = EPHY_EXTENSIONS_MANAGER (object); + EphyExtensionsManagerPrivate *priv = manager->priv; LOG ("EphyExtensionsManager finalising") - g_hash_table_destroy (manager->priv->extensions); + eel_gconf_notification_remove (manager->priv->active_extensions_notifier_id); - g_slist_foreach (manager->priv->internal_extensions, - (GFunc) g_object_unref, NULL); - g_slist_free (manager->priv->internal_extensions); + g_list_foreach (priv->extensions, (GFunc) g_object_unref, NULL); + g_list_free (priv->extensions); - eel_gconf_notification_remove - (manager->priv->active_extensions_notifier_id); + g_list_foreach (priv->factories, (GFunc) free_loader_info, NULL); + g_list_free (priv->factories); - G_OBJECT_CLASS (parent_class)->finalize (object); -} + g_list_foreach (priv->data, (GFunc) free_extension_info, NULL); + g_list_free (priv->data); -static void -attach_window_to_info (const char *key, - ExtInfo *info, - EphyWindow *window) -{ - if (info->extension) - { - ephy_extension_attach_window (info->extension, window); - } + g_list_free (priv->windows); + + parent_class->finalize (object); } static void @@ -447,25 +850,12 @@ impl_attach_window (EphyExtension *extension, { EphyExtensionsManager *manager = EPHY_EXTENSIONS_MANAGER (extension); - LOG ("multiplexing attach_window") + LOG ("Attach") - g_slist_foreach (manager->priv->internal_extensions, - (GFunc) ephy_extension_attach_window, window); + g_list_foreach (manager->priv->extensions, + (GFunc) ephy_extension_attach_window, window); - g_hash_table_foreach (manager->priv->extensions, - (GHFunc) attach_window_to_info, - window); -} - -static void -detach_window_from_info (const char *key, - ExtInfo *info, - EphyWindow *window) -{ - if (info->extension) - { - ephy_extension_detach_window (info->extension, window); - } + manager->priv->windows = g_list_prepend (manager->priv->windows, window); } static void @@ -474,16 +864,14 @@ impl_detach_window (EphyExtension *extension, { EphyExtensionsManager *manager = EPHY_EXTENSIONS_MANAGER (extension); - LOG ("multiplexing detach_window") + LOG ("Detach") - g_object_ref (window); + manager->priv->windows = g_list_remove (manager->priv->windows, window); - g_slist_foreach (manager->priv->internal_extensions, - (GFunc) ephy_extension_detach_window, window); + g_object_ref (window); - g_hash_table_foreach (manager->priv->extensions, - (GHFunc) detach_window_from_info, - window); + g_list_foreach (manager->priv->extensions, + (GFunc) ephy_extension_detach_window, window); g_object_unref (window); } @@ -504,11 +892,16 @@ ephy_extensions_manager_class_init (EphyExtensionsManagerClass *class) object_class->finalize = ephy_extensions_manager_finalize; + signals[CHANGED] = + g_signal_new ("changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EphyExtensionsManagerClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, + G_TYPE_POINTER); + g_type_class_add_private (object_class, sizeof (EphyExtensionsManagerPrivate)); } - -EphyExtensionsManager * -ephy_extensions_manager_new (void) -{ - return EPHY_EXTENSIONS_MANAGER (g_object_new (EPHY_TYPE_EXTENSIONS_MANAGER, NULL)); -} -- cgit v1.2.3