root/modules/widgetman/widgetman.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. is_same_path
  2. widget_package_extract_file
  3. package_find_res
  4. widget_package_relocate_uri
  5. widget_isom_new
  6. widget_zip_new
  7. widget_package_new
  8. widget_package_del
  9. wm_delete_message_param
  10. wm_delete_widget_content
  11. wm_delete_widget
  12. wm_delete_interface_instance
  13. wm_delete_widget_instance
  14. wm_widget_call_script
  15. wm_widget_set_scene_input_value
  16. SMJS_FUNCTION
  17. SMJS_FUNCTION
  18. SMJS_FUNCTION
  19. wm_handler_destroy
  20. wm_create_scene_listener
  21. wm_component_activation_event
  22. wm_component_activate_event
  23. wm_component_deactivate_event
  24. wm_widget_set_pref_event
  25. on_widget_activated
  26. wm_widget_load_event
  27. SMJS_FUNCTION
  28. wm_handle_dom_event
  29. SMJS_FUNCTION
  30. SMJS_FUNCTION
  31. SMJS_FUNCTION
  32. SMJS_FUNCTION
  33. SMJS_FUNCTION
  34. SMJS_FUNCTION
  35. SMJS_FUNCTION
  36. strcmp
  37. strcmp
  38. strcmp
  39. strcmp
  40. strcmp
  41. strcmp
  42. strcmp
  43. strcmp
  44. strcmp
  45. strcmp
  46. strcmp
  47. strcmp
  48. strcmp
  49. strcmp
  50. strcmp
  51. strcmp
  52. strcmp
  53. strcmp
  54. strcmp
  55. strcmp
  56. strcmp
  57. strcmp
  58. strcmp
  59. strcmp
  60. strcmp
  61. strcmp
  62. strcmp
  63. strcmp
  64. strcmp
  65. strcmp
  66. strcmp
  67. strcmp
  68. strcmp
  69. strcmp
  70. strcmp
  71. strcmp
  72. strcmp
  73. wm_widget_bind_interface_ex
  74. SMJS_FUNCTION
  75. SMJS_FUNCTION
  76. SMJS_FUNCTION
  77. wm_widget_jsbind
  78. wm_deactivate_component
  79. wm_activate_component
  80. SMJS_FUNCTION
  81. SMJS_FUNCTION
  82. SMJS_FUNCTION
  83. strcmp
  84. strcmp
  85. strcmp
  86. SMJS_FUNCTION
  87. SMJS_FUNCTION
  88. wm_xml_get_attr
  89. wm_check_language
  90. wm_xml_find
  91. wm_parse_pin
  92. wm_parse_mpegu_content_element
  93. wm_get_single_attribute
  94. wm_parse_non_neg
  95. wm_get_text_content
  96. wm_get_normalized_text_content
  97. wm_relocate_proc
  98. wm_relocate_url
  99. wm_set_default_start_file
  100. wm_add_icon
  101. wm_set_default_icon_files
  102. wm_load_widget
  103. wm_enum_widget
  104. wm_enum_dir
  105. SMJS_FUNCTION
  106. widgetmanager_load
  107. gwm_new
  108. gwm_delete
  109. QueryInterfaces
  110. LoadInterface
  111. ShutdownInterface

//This software module was originally developed by TelecomParisTech in the
//course of the development of MPEG-U Widgets (ISO/IEC 23007-1) standard.
//
//This software module is an implementation of a part of one or
//more MPEG-U Widgets (ISO/IEC 23007-1) tools as specified by the MPEG-U Widgets
//(ISO/IEC 23007-1) standard. ISO/IEC gives users of the MPEG-U Widgets
//(ISO/IEC 23007-1) free license to this software module or modifications
//thereof for use in hardware or software products claiming conformance to
//the MPEG-U Widgets (ISO/IEC 23007-1). Those intending to use this software
//module in hardware or software products are advised that its use may
//infringe existing patents.
//The original developer of this software module and his/her company, the
//subsequent editors and their companies, and ISO/IEC have no liability
//for use of this software module or modifications thereof in an implementation.
//Copyright is not released for non MPEG-U Widgets (ISO/IEC 23007-1) conforming
//products.
//Telecom ParisTech retains full right to use the code for his/her own purpose,
//assign or donate the code to a third party and to inhibit third parties from
//using the code for non MPEG-U Widgets (ISO/IEC 23007-1) conforming products.
//
//This copyright notice must be included in all copies or derivative works.
//
//Copyright (c) 2009 Telecom ParisTech.
//
// Alternatively, this software module may be redistributed and/or modified
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation; either version 2, or (at your option)
//  any later version.
//
/////////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////////
//
//      Authors:
//                                      Jean Le Feuvre, Telecom ParisTech
//                                      Cyril Concolato, Telecom ParisTech
//
/////////////////////////////////////////////////////////////////////////////////

#include "widgetman.h"

#ifdef GPAC_HAS_SPIDERMONKEY

#if !defined(__GNUC__)
# if defined(_WIN32_WCE)
#  pragma comment(lib, "js32")
# elif defined (_WIN64)
#  pragma comment(lib, "js")
# elif defined (WIN32)
#  pragma comment(lib, "js")
# endif
#endif


JSBool gf_sg_js_event_add_listener(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval, GF_Node *vrml_node);
JSBool gf_sg_js_event_remove_listener(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval, GF_Node *vrml_node);



static Bool is_same_path(const char *p1, const char *p2, u32 len)
{
        char c1, c2;
        u32 i=0;
        do {
                if (len && (i==len))
                        break;
                c1 = p1[i];
                c2 = p2[i];
                if (p1[i] != p2[i]) {
                        if ((c1=='/') && (c2=='\\')) {}
                        else if ((c1=='\\') && (c2=='/')) {}
                        else return GF_FALSE;
                }
                i++;
        } while (c1);

        return GF_TRUE;
}

static void widget_package_extract_file(GF_WidgetPackage *wpack, GF_WidgetPackageResource *res)
{
        u32 i;

        if (wpack->is_zip) {
                unz_global_info gi;
                unzFile uf = unzOpen2(wpack->package_path, NULL);
                if (!uf) return;

                unzGetGlobalInfo(uf, &gi);
                for (i=0; i<gi.number_entry; i++)  {
                        char buf[8192];
                        int err;
                        FILE *fout;
                        char filename_inzip[256];

                        unz_file_info file_info;
                        unzGetCurrentFileInfo(uf, &file_info, filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);

                        if (strcmp(res->inner_path, filename_inzip)) {
                                if ((i+1)<gi.number_entry)
                                        unzGoToNextFile(uf);
                                continue;
                        }

                        unzOpenCurrentFile3(uf, NULL, NULL, 0, NULL/*password*/);

                        fout=gf_fopen(res->extracted_path, "wb");
                        if (!fout) break;
                        do {
                                err = unzReadCurrentFile(uf,buf,8192);
                                if (err<0) break;
                                if (err>0)
                                        if (gf_fwrite(buf,err,1,fout)!=1) {
                                                //err=UNZ_ERRNO;
                                                break;
                                        }
                        } while (err>0);
                        if (fout) gf_fclose(fout);

                        res->extracted = GF_TRUE;
                        break;
                }
                unzClose(uf);
#ifndef GPAC_DISABLE_ISOM
        } else {
                u32 count;
                GF_ISOFile *isom = gf_isom_open(wpack->package_path, GF_ISOM_OPEN_READ, 0);
                if (!isom ) return;

                count = gf_isom_get_meta_item_count(isom, GF_TRUE, 0);
                for (i=0; i<count; i++)  {
                        u32 ID;
                        const char *url, *urn, *enc;
                        Bool self_ref;
                        const char *item_name;

                        gf_isom_get_meta_item_info(isom, GF_TRUE, 0, i+1, &ID, NULL, &self_ref, &item_name, NULL, &enc, &url, &urn);
                        if (strcmp(res->inner_path, item_name)) continue;

                        gf_isom_extract_meta_item(isom, GF_TRUE, 0, ID, res->extracted_path);
                        res->extracted = GF_TRUE;
                        break;
                }
                gf_isom_close(isom);
#endif /*GPAC_DISABLE_ISOM*/
        }

}

static Bool package_find_res(GF_WidgetPackage *wpack, char *res_path, char *relocated_path, char *localized_rel_path)
{
        u32 count, i;
        count = gf_list_count(wpack->resources);
        for (i=0; i<count; i++) {
                GF_WidgetPackageResource *pack_res = (GF_WidgetPackageResource*)gf_list_get(wpack->resources, i);
                if (is_same_path(res_path, pack_res->inner_path, 0)) {
                        strcpy(localized_rel_path, res_path);
                        strcpy(relocated_path, pack_res->extracted_path);
                        if (!pack_res->extracted) widget_package_extract_file(wpack, pack_res);
                        return GF_TRUE;
                }
        }
        return GF_FALSE;
}

/* Checks if a resource in the package has the given rel_path, potentially in a localized sub-folder */
static Bool widget_package_relocate_uri(void *__self, const char *parent_uri, const char *rel_path, char *relocated_path, char *localized_rel_path)
{
        char path[GF_MAX_PATH];
        const char *opt;
        GF_WidgetPackage *wpack = (GF_WidgetPackage *)__self;

        assert(parent_uri);
        /*resource belongs to our archive*/
        if (strstr(rel_path, wpack->archive_id)) {
                rel_path = strstr(rel_path, wpack->archive_id) + strlen(wpack->archive_id);
        }
        /*parent resource belongs to our archive*/
        else if (strstr(parent_uri, wpack->archive_id)) {
        }
        /*resource doesn't belong to our archive*/
        else {
                return GF_FALSE;
        }

        /* First try to locate the resource in the locales folder */
        opt = gf_cfg_get_key(wpack->wm->term->user->config, "Systems", "Language2CC");
        if (opt) {
                if (!strcmp(opt, "*") || !strcmp(opt, "un") )
                        opt = NULL;
        }

        while (opt) {
                char lan[100];
                char *sep;
                char *sep_lang = strchr(opt, ';');
                if (sep_lang) sep_lang[0] = 0;

                while (strchr(" \t", opt[0]))
                        opt++;
                strcpy(lan, opt);

                if (sep_lang) {
                        sep_lang[0] = ';';
                        opt = sep_lang+1;
                } else {
                        opt = NULL;
                }

                while (1) {
                        sep = strstr(lan, "-*");
                        if (!sep) break;
                        strncpy(sep, sep+2, strlen(sep)-2);
                }

                sprintf(path, "locales/%s/%s", lan, rel_path);
                if (package_find_res(wpack, path, relocated_path, localized_rel_path))
                        return GF_TRUE;

                /*recursively remove region (sub)tags*/
                while (1) {
                        sep = strrchr(lan, '-');
                        if (!sep) break;
                        sep[0] = 0;
                        sprintf(path, "locales/%s/%s", lan, rel_path);
                        if (package_find_res(wpack, path, relocated_path, localized_rel_path))
                                return GF_TRUE;
                }
        }

        /*no locale*/
        if (package_find_res(wpack, (char*)rel_path, relocated_path, localized_rel_path))
                return GF_TRUE;

        strcpy(localized_rel_path, "");
        strcpy(relocated_path, "");
        return GF_FALSE;
}


static GF_WidgetPackage *widget_isom_new(GF_WidgetManager *wm, const char *path)
{
#ifdef GPAC_DISABLE_ISOM
        GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Widgetman] GPAC was compiled without ISO File Format support\n"));
        return NULL;
#else
        GF_WidgetPackageResource *pack_res;
        char szPath[GF_MAX_PATH];
        const char *dir;
        GF_WidgetPackage *wzip;
        u32 brand = 0;
        u32 i, count;
        GF_ISOFile *isom = gf_isom_open(path, GF_ISOM_OPEN_READ, 0);
        if (!isom ) return NULL;

        brand = gf_isom_get_meta_type(isom, GF_TRUE, 0);
        if ((brand!=GF_4CC('m','w','g','t') )           || !gf_isom_has_meta_xml(isom, GF_TRUE, 0) ) {
                gf_isom_close(isom);
                return NULL;
        }

        GF_SAFEALLOC(wzip, GF_WidgetPackage);
        if (!wzip) return NULL;

        wzip->wm = wm;
        wzip->relocate_uri = widget_package_relocate_uri;
        wzip->resources = gf_list_new();
        dir = gf_cfg_get_key(wm->term->user->config, "General", "CacheDirectory");
        /* create the extracted path for the package root using:
           the cache dir + a CRC of the file path and the instance*/
        sprintf(wzip->root_extracted_path, "%s%p", path, wzip);
        i = gf_crc_32((char *)wzip->root_extracted_path, (u32) strlen(wzip->root_extracted_path));
        sprintf(wzip->archive_id, "GWM_%08X_", i);
        sprintf(wzip->root_extracted_path, "%s/%s", dir, wzip->archive_id);


        strcpy(szPath, wzip->root_extracted_path);
        strcat(szPath, "config.xml");
        if (gf_isom_extract_meta_xml(isom, GF_TRUE, 0, szPath, NULL) != GF_OK) {
                gf_list_del(wzip->resources);
                gf_free(wzip);
                gf_isom_close(isom);
                return NULL;
        }

        wzip->package_path = gf_strdup(path);

        GF_SAFEALLOC(pack_res, GF_WidgetPackageResource);
        if (!pack_res) {
                gf_list_del(wzip->resources);
                gf_free(wzip);
                gf_isom_close(isom);
                return NULL;
        }
        pack_res->extracted_path = gf_strdup(szPath);
        pack_res->inner_path = gf_strdup("config.xml");
        pack_res->extracted = GF_TRUE;
        gf_list_add(wzip->resources, pack_res);


        count = gf_isom_get_meta_item_count(isom, GF_TRUE, 0);
        for (i=0; i<count; i++)  {
                u32 ID;
                const char *url, *urn, *enc;
                Bool self_ref;
                char *sep;
                const char *item_name;

                gf_isom_get_meta_item_info(isom, GF_TRUE, 0, i+1, &ID, NULL, &self_ref, &item_name, NULL, &enc, &url, &urn);

                sep = strrchr(item_name, '/');
                if (!sep) sep = strrchr(item_name, '\\');
                if (sep) {
                        sep[0] = 0;
                        sprintf(szPath, "%s_%08X_%s", wzip->root_extracted_path, gf_crc_32((char*)item_name, (u32) strlen(item_name)), sep+1);
                        sep[0] = '/';
                } else {
                        strcpy(szPath, wzip->root_extracted_path);
                        strcat(szPath, item_name);
                }
                GF_SAFEALLOC(pack_res, GF_WidgetPackageResource);
                if (!pack_res) {
                        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[WidgetMan] Failed to allocate widget resource\n"));
                        continue;
                }
                pack_res->extracted_path = gf_strdup(szPath);
                pack_res->inner_path = gf_strdup(item_name);
                pack_res->extracted = GF_FALSE;
                gf_list_add(wzip->resources, pack_res);
        }
        gf_isom_close(isom);


        /* register this widget package as a relocator to enable localization of resources inside this package */
        gf_mx_p(wm->term->net_mx);
        gf_list_add(wm->term->uri_relocators, wzip);
        gf_mx_v(wm->term->net_mx);
        return wzip;
#endif /*GPAC_DISABLE_ISOM*/
}

static GF_WidgetPackage *widget_zip_new(GF_WidgetManager *wm, const char *path)
{
        unz_global_info gi;
        const char *dir;
        u32 i;
        GF_WidgetPackage *wzip;
        unzFile uf = unzOpen2(path, NULL);
        if (!uf) return NULL;

        GF_SAFEALLOC(wzip, GF_WidgetPackage);
        if (!wzip) return NULL;

        wzip->wm = wm;
        wzip->is_zip = GF_TRUE;
        wzip->relocate_uri = widget_package_relocate_uri;
        wzip->resources = gf_list_new();
        wzip->package_path = gf_strdup(path);
        dir = gf_cfg_get_key(wm->term->user->config, "General", "CacheDirectory");
        /* create the extracted path for the package root using:
           the cache dir + a CRC of the file path and the instance*/
        sprintf(wzip->root_extracted_path, "%s%p", path, wzip);
        i = gf_crc_32((char *)wzip->root_extracted_path, (u32) strlen(wzip->root_extracted_path));
        sprintf(wzip->archive_id, "GWM_%08X_", i);
        sprintf(wzip->root_extracted_path, "%s/%s", dir, wzip->archive_id);

        unzGetGlobalInfo(uf, &gi);
        for (i=0; i<gi.number_entry; i++)  {
                char szPath[GF_MAX_PATH];
                char *sep;
                GF_WidgetPackageResource *pack_res;
                char filename_inzip[256];
                unz_file_info file_info;
                unzGetCurrentFileInfo(uf, &file_info, filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);

                sep = strrchr(filename_inzip, '/');
                if (!sep) sep = strrchr(filename_inzip, '\\');
                if (sep) {
                        sep[0] = 0;
                        sprintf(szPath, "%s_%08X_%s", wzip->root_extracted_path, gf_crc_32(filename_inzip, (u32) strlen(filename_inzip)), sep+1);
                        sep[0] = '/';
                } else {
                        strcpy(szPath, wzip->root_extracted_path);
                        strcat(szPath, filename_inzip);
                }


                if (!strcmp(filename_inzip, "config.xml")) {
                        int err;
                        char buf[8192];
                        FILE *fout;
                        unzOpenCurrentFile3(uf, NULL, NULL, 0, NULL/*password*/);

                        fout=gf_fopen(szPath,"wb");
                        if (!fout) return NULL;

                        do {
                                err = unzReadCurrentFile(uf,buf,8192);
                                if (err<0) break;

                                if (err>0)
                                        if (gf_fwrite(buf,err,1,fout)!=1) {
                                                err=UNZ_ERRNO;
                                                break;
                                        }
                        } while (err>0);
                        if (fout) gf_fclose(fout);
                        if (err==0) {
                                GF_SAFEALLOC(pack_res, GF_WidgetPackageResource);
                                if (!pack_res) {
                                        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[WidgetMan] Failed to allocate widget resource\n"));
                                        continue;
                                }
                                
                                pack_res->extracted_path = gf_strdup(szPath);
                                pack_res->inner_path = gf_strdup(filename_inzip);
                                pack_res->extracted = GF_TRUE;
                                gf_list_add(wzip->resources, pack_res);
                        }
                } else {
                        GF_SAFEALLOC(pack_res, GF_WidgetPackageResource);
                        if (!pack_res) {
                                GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[WidgetMan] Failed to allocate widget resource\n"));
                                continue;
                        }
                        pack_res->extracted_path = gf_strdup(szPath);
                        pack_res->inner_path = gf_strdup(filename_inzip);
                        pack_res->extracted = GF_FALSE;
                        gf_list_add(wzip->resources, pack_res);
                }

                if ((i+1)<gi.number_entry)
                        unzGoToNextFile(uf);
        }
        unzClose(uf);

        /* register this widget package as a relocator to enable localization of resources inside this package */
        gf_mx_p(wm->term->net_mx);
        gf_list_add(wm->term->uri_relocators, wzip);
        gf_mx_v(wm->term->net_mx);
        return wzip;
}

GF_WidgetPackage *widget_package_new(GF_WidgetManager *wm, const char *path)
{
        if (gf_unzip_probe(path)) {
                return widget_zip_new(wm, path);
        }
#ifndef GPAC_DISABLE_ISOM
        /*ISOFF-based packaged widget */
        else if (gf_isom_probe_file(path)) {
                return widget_isom_new(wm, path);
        }
#endif
        return NULL;
}

static void widget_package_del(GF_WidgetManager *wm, GF_WidgetPackage *wpackage)
{
        gf_mx_p(wm->term->net_mx);
        gf_list_del_item(wm->term->uri_relocators, wpackage);
        gf_mx_v(wm->term->net_mx);

        while (gf_list_count(wpackage->resources)) {
                GF_WidgetPackageResource *wu = (GF_WidgetPackageResource*)gf_list_get(wpackage->resources, 0);
                gf_list_rem(wpackage->resources, 0);
                gf_delete_file(wu->extracted_path);
                gf_free(wu->extracted_path);
                gf_free(wu->inner_path);
                gf_free(wu);
        }
        gf_list_del(wpackage->resources);
        if (wpackage->sess) gf_dm_sess_del(wpackage->sess);
        gf_free(wpackage->package_path);
        gf_free(wpackage);
}



static void wm_delete_message_param(GF_WidgetPin *mp)
{
        if (!mp) return;
        if (mp->node) gf_free(mp->node);
        if (mp->attribute) gf_free(mp->attribute);
        if (mp->default_value) gf_free(mp->default_value);
        if (mp->name) gf_free(mp->name);
        gf_free(mp);
}

static void wm_delete_widget_content(GF_WidgetContent *content)
{
        if (!content) return;

        while (gf_list_count(content->interfaces)) {
                GF_WidgetInterface *ifce = (GF_WidgetInterface*)gf_list_last(content->interfaces);
                gf_list_rem_last(content->interfaces);

                while (gf_list_count(ifce->messages)) {
                        GF_WidgetMessage *msg = (GF_WidgetMessage*)gf_list_last(ifce->messages);
                        gf_list_rem_last(ifce->messages);

                        while (gf_list_count(msg->params)) {
                                GF_WidgetPin *par = (GF_WidgetPin*)gf_list_last(msg->params);
                                gf_list_rem_last(msg->params);
                                wm_delete_message_param(par);
                        }
                        gf_list_del(msg->params);

                        wm_delete_message_param(msg->input_action);
                        wm_delete_message_param(msg->output_trigger);
                        gf_free(msg->name);
                        gf_free(msg);
                }
                gf_list_del(ifce->messages);
                wm_delete_message_param(ifce->bind_action);
                wm_delete_message_param(ifce->unbind_action);
                if (ifce->obj) gf_js_remove_root(ifce->content->widget->wm->ctx, &ifce->obj, GF_JSGC_OBJECT);

                if (ifce->connectTo) gf_free(ifce->connectTo);
                gf_free(ifce->type);
                gf_free(ifce);
        }
        gf_list_del(content->interfaces);

        while (gf_list_count(content->components)) {
                GF_WidgetComponent *comp = (GF_WidgetComponent*)gf_list_last(content->components);
                gf_list_rem_last(content->components);

                wm_delete_message_param(comp->activateTrigger);
                wm_delete_message_param(comp->deactivateTrigger);

                while (gf_list_count(comp->required_interfaces)) {
                        char *type = (char*)gf_list_last(comp->required_interfaces);
                        gf_list_rem_last(comp->required_interfaces);
                        if (type) gf_free(type);
                }
                gf_list_del(comp->required_interfaces);
                if (comp->id) gf_free(comp->id);
                if (comp->src) gf_free(comp->src);
                gf_free(comp);
        }
        gf_list_del(content->components);

        while (gf_list_count(content->preferences)) {
                GF_WidgetPreference *pref = (GF_WidgetPreference*)gf_list_last(content->preferences);
                gf_list_rem_last(content->preferences);

                wm_delete_message_param(pref->connectTo);
                if (pref->value) gf_free(pref->value);
                gf_free(pref->name);
                gf_free(pref);
        }
        gf_list_del(content->preferences);

        wm_delete_message_param(content->saveTrigger);
        wm_delete_message_param(content->restoreTrigger);
        wm_delete_message_param(content->savedAction);
        wm_delete_message_param(content->restoredAction);

        gf_free(content->src);
        gf_free(content->relocated_src);
        gf_free(content->mimetype);
        gf_free(content->encoding);
        gf_free(content);
}

static void wm_delete_widget(GF_WidgetManager *wm, GF_Widget *wid)
{
        gf_list_del_item(wm->widgets, wid);

        if (wid->url) gf_free(wid->url);
        if (wid->manifest_path) gf_free(wid->manifest_path);
        wm_delete_widget_content(wid->main);
        if (wid->local_path) gf_free(wid->local_path);
        if (wid->name) gf_free(wid->name);
        if (wid->shortname) gf_free(wid->shortname);
        if (wid->identifier) gf_free(wid->identifier);
        if (wid->authorName) gf_free(wid->authorName);
        if (wid->authorEmail) gf_free(wid->authorEmail);
        if (wid->authorHref) gf_free(wid->authorHref);
        if (wid->description) gf_free(wid->description);
        if (wid->license) gf_free(wid->license);
        if (wid->licenseHref) gf_free(wid->licenseHref);
        if (wid->viewmodes) gf_free(wid->viewmodes);
        if (wid->version) gf_free(wid->version);
        if (wid->uuid) gf_free(wid->uuid);


        while (gf_list_count(wid->icons)) {
                GF_WidgetContent *icon = (GF_WidgetContent*)gf_list_get(wid->icons, 0);
                gf_list_rem(wid->icons, 0);
                wm_delete_widget_content(icon);
        }
        gf_list_del(wid->icons);

        while (gf_list_count(wid->features)) {
                GF_WidgetFeature *f = (GF_WidgetFeature*)gf_list_get(wid->features, 0);
                gf_list_rem(wid->features, 0);
                if (f->name) gf_free(f->name);
                while (gf_list_count(f->params)) {
                        GF_WidgetFeatureParam *p = (GF_WidgetFeatureParam*)gf_list_get(f->params, 0);
                        gf_list_rem(f->params, 0);
                        if (p->name) gf_free(p->name);
                        if (p->value) gf_free(p->value);
                        gf_free(p);
                }
                gf_free(f);
        }
        gf_list_del(wid->features);

        if (wid->wpack) widget_package_del(wm, wid->wpack);
        gf_free(wid);
}

static void wm_delete_interface_instance(GF_WidgetManager *wm, GF_WidgetInterfaceInstance *bifce)
{
        if (bifce->hostname) gf_free(bifce->hostname);
        if (bifce->obj) {
                SMJS_SET_PRIVATE(wm->ctx, bifce->obj, NULL);
                gf_js_remove_root(wm->ctx, &bifce->obj, GF_JSGC_OBJECT);
        }
        gf_free(bifce);
}

static void wm_delete_widget_instance(GF_WidgetManager *wm, GF_WidgetInstance *widg)
{

        while (gf_list_count(widg->components)) {
                GF_WidgetComponentInstance *comp = (GF_WidgetComponentInstance*)gf_list_get(widg->components, 0);
                gf_list_rem(widg->components, 0);
                if (comp->wid) wm_delete_widget_instance(wm, comp->wid);
                gf_free(comp);
        }
        gf_list_del(widg->components);

        while (gf_list_count(widg->bound_ifces)) {
                GF_WidgetInterfaceInstance *bifce = (GF_WidgetInterfaceInstance*)gf_list_get(widg->bound_ifces, 0);
                gf_list_rem(widg->bound_ifces, 0);
                wm_delete_interface_instance(wm, bifce);
        }
        gf_list_del(widg->bound_ifces);

        gf_list_del(widg->output_triggers);

        if (widg->obj) {
                SMJS_SET_PRIVATE(wm->ctx, widg->obj, NULL);
                gf_js_remove_root(wm->ctx, &widg->obj, GF_JSGC_OBJECT);
        }
        gf_list_del_item(wm->widget_instances, widg);
        widg->widget->nb_instances--;
        if (!widg->widget->nb_instances) wm_delete_widget(wm, widg->widget);

        if (!widg->permanent) {
                gf_cfg_del_section(wm->term->user->config, (const char *)widg->secname);
                gf_cfg_set_key(wm->term->user->config, "Widgets", (const char *)widg->secname, NULL);
        }
        if (widg->mpegu_context) gf_xml_dom_del(widg->mpegu_context);

        gf_free(widg);
}



static JSBool wm_widget_call_script(GF_WidgetInstance *wid, GF_WidgetPin *pin, uintN argc, jsval *argv, jsval *rval)
{
        jsval fval;
        if (!wid->scene_context || !wid->scene_global) return JS_FALSE;

        /*if on_load property is assigned to this widget, add an event listener on the root.*/
        JS_LookupProperty(wid->scene_context, wid->scene_global, pin->node, &fval);
        if (JSVAL_IS_OBJECT(fval)) {
                JS_CallFunctionValue(wid->scene_context, wid->scene_global, fval, argc, argv, rval);
        }
        return JS_TRUE;
}

static JSBool wm_widget_set_scene_input_value(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval, u32 type, GF_WidgetInstance *wid, GF_WidgetPin *param, const char *value)
{
        char *str_val;
        GF_Node *n;
        GF_FieldInfo info;
        GF_WidgetMessage *msg;
        GF_WidgetInterface *ifce;

        if (!wid && obj) wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj);
        if (!wid) return JS_FALSE;
        if (!wid->scene) return JS_TRUE;

        if (!param) {
                if (!JSVAL_IS_OBJECT(argv[0])) return JS_TRUE;

                switch (type) {
                /*set_input*/
                case 0:
                        if (argc!=2) return JS_FALSE;
                        param = SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[0]) );
                        break;
                /*call_input_action*/
                case 1:
                        msg = SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[0]) );
                        param = msg ? msg->input_action : NULL;
                        break;
                /*bind_interface*/
                case 2:
                        ifce = SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[0]) );
                        param = ifce ? ifce->bind_action : NULL;
                        break;
                /*unbind_interface*/
                case 3:
                        ifce = SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[0]) );
                        param = ifce ? ifce->unbind_action : NULL;
                        break;
                }
        }

        if (!param  || !param->node) return JS_TRUE;
        /*this is a script call*/
        if (!param->attribute) {
                return wm_widget_call_script(wid, param, 0, NULL, rval);
        }

        n = gf_sg_find_node_by_name(wid->scene, param->node);
        if (!n) return JS_TRUE;

        if (param->in_action) return JS_TRUE;

        param->in_action = GF_TRUE;

#ifndef GPAC_DISABLE_SVG
        if (n->sgprivate->tag >= GF_NODE_FIRST_DOM_NODE_TAG) {
                u32 evt_type;
                if (!type) {
                        char *_str_val = NULL;
                        if (value) {
                                str_val = (char *)value;
                        } else {
                                if (!JSVAL_IS_STRING(argv[1])) goto exit;
                                str_val = _str_val = SMJS_CHARS(c, argv[1]);
                        }

                        /* first check if the set_input refers to an attribute name or to an event name */
                        evt_type = gf_dom_event_type_by_name(param->attribute);

                        /*events are not allowed for <input> and <output>*/
                        if (evt_type == GF_EVENT_UNKNOWN) {
                                /* modify an attribute */
                                if (!strcmp(param->attribute, "textContent")) {
                                        gf_dom_set_textContent(n, (char *)str_val);
                                        gf_node_changed(n, NULL);
                                }
                                else {
                                        if (gf_node_get_attribute_by_name(n, param->attribute, 0, GF_TRUE, GF_FALSE, &info)==GF_OK) {
                                                gf_svg_parse_attribute(n, &info, (char *)str_val, 0);
                                                if (info.fieldType==XMLRI_datatype) gf_node_dirty_set(n, GF_SG_SVG_XLINK_HREF_DIRTY, GF_FALSE);
                                                gf_node_changed(n, &info);
                                        }
                                }
                        }
                        SMJS_FREE(c, _str_val);

                } else {
                        GF_DOM_Event evt;
                        memset(&evt, 0, sizeof(GF_DOM_Event));
                        /* first check if the set_input refers to an attribute name or to an event name */
                        evt_type = gf_dom_event_type_by_name(param->attribute);
                        /* launch an event */
                        if (evt_type != GF_EVENT_UNKNOWN) {
                                evt.type = evt_type;
                                gf_dom_event_fire(n, &evt);
                        } else {
                                /*should we fire a DOMAttrModified event ? to clarify in the spec*/

                                if (gf_node_get_attribute_by_name(n, param->attribute, 0, GF_TRUE, GF_FALSE, &info)==GF_OK) {
                                        evt.bubbles = 1;
                                        evt.type = GF_EVENT_ATTR_MODIFIED;
                                        evt.attr = &info;
                                        gf_dom_event_fire(n, &evt);
                                }
                        }
                }
        } else
#endif


#ifndef GPAC_DISABLE_VRML
        {
                if (gf_node_get_field_by_name(n, param->attribute, &info) != GF_OK) return JS_TRUE;

                if (!type) {
                        char *_str_val = NULL;
                        jsdouble val;

                        switch (info.fieldType) {
                        case GF_SG_VRML_SFSTRING:
                                if (value) {
                                        str_val = (char *)value;
                                } else {
                                        if (!JSVAL_IS_STRING(argv[1]))goto exit;
                                        str_val = SMJS_CHARS(c, argv[1]);
                                }
                                if ( ((SFString*)info.far_ptr)->buffer) gf_free(((SFString*)info.far_ptr)->buffer);
                                ((SFString*)info.far_ptr)->buffer = str_val ? gf_strdup(str_val) : NULL;
                                break;
                        case GF_SG_VRML_SFBOOL:
                                if (value) *((SFBool*)info.far_ptr) = (!strcmp(value, "true")) ? 1 : 0;
                                else if (JSVAL_IS_BOOLEAN(argv[1])) *((SFBool*)info.far_ptr) = JSVAL_TO_BOOLEAN(argv[1]);
                                else if (JSVAL_IS_INT(argv[1])) *((SFBool*)info.far_ptr) = JSVAL_TO_INT(argv[1]);
                                else if (JSVAL_IS_STRING(argv[1])) {
                                        str_val = SMJS_CHARS(c, argv[1]);
                                        *((SFBool*)info.far_ptr) = (str_val && !strcmp(str_val, "true")) ? 1 : 0;
                                        SMJS_FREE(c, str_val);
                                } else
                                        goto exit;
                                break;
                        case GF_SG_VRML_SFINT32:
                                if (value) *((SFInt32*)info.far_ptr) = (s32) atof(value);
                                else if (JSVAL_IS_INT(argv[1])) *((SFInt32*)info.far_ptr) = JSVAL_TO_INT(argv[1]);
                                else if (JSVAL_IS_NUMBER(argv[1])) {
                                        JS_ValueToNumber(c, argv[1], &val);
                                        *((SFInt32*)info.far_ptr) = (s32) val;
                                } else if (JSVAL_IS_STRING(argv[1])) {
                                        Double a_val;
                                        str_val = SMJS_CHARS(c, argv[1]);
                                        a_val = str_val ? atof(str_val) : 0;
                                        *((SFInt32*)info.far_ptr) = (s32) a_val;
                                        SMJS_FREE(c, str_val);
                                } else
                                        goto exit;
                                break;
                        case GF_SG_VRML_SFFLOAT:
                                if (value) *((SFFloat*)info.far_ptr) = FLT2FIX(atof(value));
                                else if (JSVAL_IS_INT(argv[1])) *((SFFloat *)info.far_ptr) = INT2FIX( JSVAL_TO_INT(argv[1]) );
                                else if (JSVAL_IS_NUMBER(argv[1])) {
                                        JS_ValueToNumber(c, argv[1], &val);
                                        *((SFFloat *)info.far_ptr) = FLT2FIX( val );
                                } else if (JSVAL_IS_STRING(argv[1])) {
                                        Double a_val;
                                        str_val = SMJS_CHARS(c, argv[1]);
                                        a_val = str_val ? atof(str_val) : 0;
                                        *((SFFloat*)info.far_ptr) = FLT2FIX(a_val);
                                        SMJS_FREE(c, str_val);
                                } else
                                        goto exit;
                                break;
                        case GF_SG_VRML_SFTIME:
                                if (value) *((SFTime*)info.far_ptr) = atof(value);
                                else if (JSVAL_IS_INT(argv[1])) *((SFTime *)info.far_ptr) = JSVAL_TO_INT(argv[1]);
                                else if (JSVAL_IS_NUMBER(argv[1])) {
                                        JS_ValueToNumber(c, argv[1], &val);
                                        *((SFTime *)info.far_ptr) = val;
                                } else if (JSVAL_IS_STRING(argv[1])) {
                                        Double a_val;
                                        str_val = SMJS_CHARS(c, argv[1]);
                                        a_val = str_val ? atof(str_val) : 0;
                                        *((SFTime *)info.far_ptr) = a_val;
                                        SMJS_FREE(c, str_val);
                                } else
                                        goto exit;
                                break;
                        case GF_SG_VRML_MFSTRING:
                                if (value) {
                                        str_val = (char *)value;
                                } else {
                                        if (!JSVAL_IS_STRING(argv[1])) goto exit;
                                        str_val = _str_val = SMJS_CHARS(c, argv[1]);
                                }
                                if ( ((GenMFField *)info.far_ptr)->count != 1) {
                                        gf_sg_vrml_mf_reset(info.far_ptr, GF_SG_VRML_MFSTRING);
                                        gf_sg_vrml_mf_alloc(info.far_ptr, GF_SG_VRML_MFSTRING, 1);
                                }
                                if ( ((MFString*)info.far_ptr)->vals[0]) gf_free( ((MFString*)info.far_ptr)->vals[0] );
                                ((MFString*)info.far_ptr)->vals[0] = str_val ? gf_strdup(str_val) : NULL;

                                SMJS_FREE(c, _str_val);
                                break;
                        }
                }

                //if this is a script eventIn call directly script
                if ((n->sgprivate->tag==TAG_MPEG4_Script)
#ifndef GPAC_DISABLE_X3D
                        || (n->sgprivate->tag==TAG_X3D_Script)
#endif
                   )
                        gf_sg_script_event_in(n, &info);

                gf_node_changed(n, &info);

                /*if this is an exposedField, route it*/
                if (info.eventType==GF_SG_EVENT_EXPOSED_FIELD) {
                        gf_node_event_out(n, info.fieldIndex);
                }
        }
#endif /*GPAC_DISABLE_VRML*/

exit:
        param->in_action = GF_FALSE;
        return JS_TRUE;
}


static JSBool SMJS_FUNCTION(wm_widget_set_input)
{
        SMJS_OBJ
        SMJS_ARGS
        SMJS_DECL_RVAL
        return wm_widget_set_scene_input_value(c, obj, argc, argv, rval, 0, NULL, NULL, NULL);
}
static JSBool SMJS_FUNCTION(wm_widget_call_input_action)
{
        SMJS_OBJ
        SMJS_ARGS
        SMJS_DECL_RVAL
        return wm_widget_set_scene_input_value(c, obj, 1, argv, rval, 1, NULL, NULL, NULL);
}


static JSBool SMJS_FUNCTION(wm_widget_call_input_script)
{
        GF_WidgetMessage *msg;
        GF_WidgetPin *param;
        SMJS_OBJ
        SMJS_ARGS
        GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj);
        if (!wid || (argc!=2) ) return JS_FALSE;
        if (!wid->scene) return JS_TRUE;

        if (!JSVAL_IS_OBJECT(argv[0])) return JS_TRUE;
        msg = SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[0]) );
        param = msg ? msg->input_action : NULL;
        if (!param || !param->node || param->attribute) return JS_FALSE;

        if (JSVAL_IS_OBJECT(argv[1])) {
                jsval *args;
                JSObject *list = JSVAL_TO_OBJECT(argv[1]);
                u32 i, count;
                JS_GetArrayLength(c, list, (jsuint*) &count);
                args = (jsval*)gf_malloc(sizeof(jsval)*count);
                for (i=0; i<count; i++) {
                        JS_GetElement(c, list, (jsint) i, &args[i] );
                }

                wm_widget_call_script(wid, param, count, args, SMJS_GET_RVAL);

                gf_free(args);
        }
        return JS_TRUE;
}


static void wm_handler_destroy(GF_Node *node, void *rs, Bool is_destroy)
{
        if (is_destroy) {
                SVG_handlerElement *handler = (SVG_handlerElement *)node;
                if (handler->js_fun_val) {
                        gf_js_remove_root(handler->js_context, &handler->js_fun_val, GF_JSGC_VAL);
                        handler->js_fun_val=0;
                }
        }
}

static SVG_handlerElement *wm_create_scene_listener(GF_WidgetInstance *wid, GF_WidgetPin *param)
{
        /*FIXME - we need to split SVG and base DOM !!*/
#ifdef GPAC_DISABLE_SVG
        return NULL;
#else
        u32 evt_type, att_name;
        GF_Node *listener;
        GF_FieldInfo info;
        GF_Node *n = NULL;
        SVG_handlerElement *handler;

        evt_type = GF_EVENT_ATTR_MODIFIED;
        n = gf_sg_find_node_by_name(wid->scene, param->node);
        if (!n)
                return NULL;

        att_name = 0;

#ifndef GPAC_DISABLE_SVG
        if (n->sgprivate->tag >= GF_NODE_FIRST_DOM_NODE_TAG) {
                /* first check if the set_input refers to an attribute name or to an event name */
                evt_type = gf_dom_event_type_by_name(param->attribute);
                if (evt_type == GF_EVENT_UNKNOWN) {
                        evt_type = GF_EVENT_ATTR_MODIFIED;

                        /* modify textContent */
                        if (!strcmp(param->attribute, "textContent")) {
                                att_name = (u32) -1;
                        }
                        /* modify an attribute */
                        else if (gf_node_get_attribute_by_name(n, param->attribute, 0, GF_TRUE, GF_FALSE, &info)==GF_OK) {
                                att_name = info.fieldIndex;
                        }
                        else {
                                return NULL;
                        }
                }
        } else
#endif
        {
                if (gf_node_get_field_by_name(n, param->attribute, &info) != GF_OK)
                        return NULL;
                att_name = info.fieldIndex;
        }

        listener = gf_node_new(wid->scene, TAG_SVG_listener);

        handler = (SVG_handlerElement *) gf_node_new(wid->scene, TAG_SVG_handler);
        /*we register the handler with the listener node to avoid modifying the DOM*/
        gf_node_register((GF_Node *)handler, listener);
        gf_node_list_add_child(& ((GF_ParentNode *)listener)->children, (GF_Node*)handler);
        handler->sgprivate->UserCallback = wm_handler_destroy;

        /*create attributes if needed*/
        gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_event, GF_TRUE, GF_FALSE, &info);
        ((XMLEV_Event*)info.far_ptr)->type = evt_type;
        ((XMLEV_Event*)info.far_ptr)->parameter = att_name;
        gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_handler, GF_TRUE, GF_FALSE, &info);
        ((XMLRI*)info.far_ptr)->target = (GF_Node*)handler;
        gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_target, GF_TRUE, GF_FALSE, &info);
        ((XMLRI*)info.far_ptr)->target = n;

        gf_node_get_attribute_by_tag((GF_Node*)handler, TAG_XMLEV_ATT_event, GF_TRUE, GF_FALSE, &info);
        ((XMLEV_Event*)info.far_ptr)->type = evt_type;
        ((XMLEV_Event*)info.far_ptr)->parameter = att_name;

        gf_node_dom_listener_add((GF_Node *) n, listener);

        return handler;
#endif
}

static void wm_component_activation_event(GF_Node *hdl, GF_DOM_Event *evt, GF_Node *observer, Bool unload)
{
        JSObject *obj;
        JSContext *c;
        GF_WidgetInstance *wid;
        GF_WidgetComponent *comp;
        SVG_handlerElement *handler = (SVG_handlerElement *)hdl;

        c = (JSContext*)handler->js_context;
        obj = (JSObject*)handler->evt_listen_obj;
        if (!c || !obj) return;
        wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj);
        if (!wid) return;

        comp = (GF_WidgetComponent *)handler->js_fun;
        if (unload) {
                wm_deactivate_component(c, wid, comp, NULL);
        } else {
                wm_activate_component(c, wid, comp, GF_FALSE);
        }
}
static void wm_component_activate_event(GF_Node *hdl, GF_DOM_Event *evt, GF_Node *observer)
{
        wm_component_activation_event(hdl, evt, observer, GF_FALSE);
}
static void wm_component_deactivate_event(GF_Node *hdl, GF_DOM_Event *evt, GF_Node *observer)
{
        wm_component_activation_event(hdl, evt, observer, GF_TRUE);
}

static void wm_widget_set_pref_event(GF_Node *hdl, GF_DOM_Event *evt, GF_Node *observer)
{
        char *att;
        SVG_handlerElement *handler = (SVG_handlerElement *)hdl;
        GF_WidgetInstance *wid = (GF_WidgetInstance *) handler->evt_listen_obj;
        GF_WidgetPreference *pref = (GF_WidgetPreference *) handler->js_fun;

        if (evt->type != GF_EVENT_ATTR_MODIFIED) return;

        if (evt->detail == (u32) -1) {
#ifndef GPAC_DISABLE_SVG
                att = gf_dom_flatten_textContent(evt->target);
#endif
        } else {
                att = gf_node_dump_attribute(evt->target, evt->attr);
        }
        if (!att) return;
        gf_cfg_set_key(wid->widget->wm->term->user->config, (const char *)wid->secname, pref->name, att);
        gf_free(att);
}

static void on_widget_activated(JSContext *c, JSObject *obj)
{
        jsval funval, rval;
        u32 i, count;
        GF_XMLNode *context = NULL;
        GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj);
        if (!wid || wid->activated) return;

        /*widget is now activated*/
        wid->activated = GF_TRUE;

        /*for all components, setup bindings on activateTrigger & deactivateTrigger*/
        count = gf_list_count(wid->widget->main->components);
        for (i=0; i<count; i++) {
                GF_WidgetComponent *comp = (GF_WidgetComponent*)gf_list_get(wid->widget->main->components, i);
                /*setup listener*/
                if (comp->activateTrigger && comp->activateTrigger->attribute) {
                        SVG_handlerElement *a_hdl = wm_create_scene_listener(wid, comp->activateTrigger);
                        if (!a_hdl) return;
                        a_hdl->evt_listen_obj = obj;
                        a_hdl->js_context = c;
                        a_hdl->js_fun = comp;
                        a_hdl->handle_event = wm_component_activate_event;
                }
                /*setup listener*/
                if (comp->deactivateTrigger && comp->deactivateTrigger->attribute) {
                        SVG_handlerElement *a_hdl = wm_create_scene_listener(wid, comp->deactivateTrigger);
                        if (!a_hdl) continue;
                        a_hdl->evt_listen_obj = obj;
                        a_hdl->js_context = c;
                        a_hdl->js_fun = comp;
                        a_hdl->handle_event = wm_component_deactivate_event;
                }
        }

        if (wid->mpegu_context)
                context = gf_xml_dom_get_root(wid->mpegu_context);

        /*load preferences*/
        count = gf_list_count(wid->widget->main->preferences);
        for (i=0; i<count; i++) {
                const char *value;
                GF_WidgetPreference *pref = (GF_WidgetPreference*)gf_list_get(wid->widget->main->preferences, i);


                /*get stored value for this preference*/
                value = gf_cfg_get_key(wid->widget->wm->term->user->config, (const char *)wid->secname, pref->name);
                /*if none found, use preference*/
                if (!value) value = pref->value;

                /*and overload with migrated context*/
                if (context) {
                        GF_XMLNode *pref_node;
                        u32 j=0;
                        while ((pref_node = (GF_XMLNode*)gf_list_enum(context->content, &j))) {
                                const char *att;
                                if (pref_node->type != GF_XML_NODE_TYPE) continue;
                                if (strcmp(pref_node->name, "preference")) continue;
                                att = wm_xml_get_attr(pref_node, "name");
                                if (!att) continue;
                                if (strcmp(att, pref->name)) continue;

                                att = wm_xml_get_attr(pref_node, "value");
                                if (att) value = att;
                                break;
                        }
                }

                if (pref->connectTo) {
                        SVG_handlerElement *hdl;

                        if (value)
                                wm_widget_set_scene_input_value(NULL, NULL, 0, 0, 0, 0, wid, pref->connectTo, value);

                        /*preference is read only, do not setup listener*/
                        if (pref->flags & GF_WM_PREF_READONLY) continue;
                        /*preference is migratable only, do not setup listener*/
                        if (!(pref->flags & GF_WM_PREF_SAVE)) continue;

                        /*create the listener*/
                        hdl = wm_create_scene_listener(wid, pref->connectTo);
                        if (!hdl) continue;

                        hdl->evt_listen_obj = wid;
                        hdl->js_fun = pref;
                        hdl->handle_event = wm_widget_set_pref_event;

                }
        }
        if (count && wid->widget->main->restoredAction) {
                wm_widget_set_scene_input_value(wid->widget->wm->ctx, wid->obj, 0, NULL, &rval, 0, wid, wid->widget->main->restoredAction, NULL);
        }

        if (wid->mpegu_context) {
                gf_xml_dom_del(wid->mpegu_context);
                wid->mpegu_context = NULL;
        }

        gf_sg_lock_javascript(wid->widget->wm->ctx, GF_TRUE);
        /*refresh all interface bindings*/
        JS_LookupProperty(wid->widget->wm->ctx, wid->widget->wm->obj, "check_bindings", &funval);
        if (JSVAL_IS_OBJECT(funval)) {
                JS_CallFunctionValue(wid->widget->wm->ctx, wid->widget->wm->obj, funval, 0, 0, &rval);
        }

        /*if on_load property is assigned to this widget, call it.*/
        JS_LookupProperty(c, obj, "on_load", &funval);
        if (JSVAL_IS_OBJECT(funval)) {
                JS_CallFunctionValue(wid->widget->wm->ctx, wid->obj, funval, 0, 0, &rval);
        }
        gf_sg_lock_javascript(wid->widget->wm->ctx, GF_FALSE);
}


static void wm_widget_load_event(GF_Node *hdl, GF_DOM_Event *evt, GF_Node *observer)
{
        JSObject *obj;
        JSContext *c;
        SVG_handlerElement *handler = (SVG_handlerElement *)hdl;

        c = (JSContext*)handler->js_context;
        obj = (JSObject*)handler->evt_listen_obj;
        if (!c || !obj) return;

        on_widget_activated(c, obj);
}


static JSBool SMJS_FUNCTION(wm_widget_activate)
{
#ifndef GPAC_DISABLE_SVG
        SVG_handlerElement *handler;
#endif
        GF_MediaObject *mo;
        Bool direct_trigger = GF_FALSE;
        MFURL url;
        SFURL _url;
        GF_Node *inl;
        SMJS_OBJ
        SMJS_ARGS
        GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj);
        if (!wid || !argc) return JS_FALSE;

        if (!JSVAL_IS_OBJECT(argv[0])) return JS_FALSE;

        inl = gf_sg_js_get_node(c, JSVAL_TO_OBJECT(argv[0]) );
        if (!inl) return JS_FALSE;

        _url.OD_ID = 0;
        _url.url = gf_url_concatenate(wid->widget->url, wid->widget->main->relocated_src);
        url.count = 1;
        url.vals = &_url;
        mo = gf_mo_register(inl, &url, GF_FALSE, GF_TRUE);
        if (mo) {
                wid->scene = gf_mo_get_scenegraph(mo);
                if (wid->scene && gf_sg_get_root_node(wid->scene)) direct_trigger = GF_TRUE;
        }
        if (_url.url) gf_free(_url.url);

        wid->anchor = inl;

        if (direct_trigger) {
                on_widget_activated(c, obj);
        } else {
#ifndef GPAC_DISABLE_SVG
                handler = gf_dom_listener_build(inl, GF_EVENT_SCENE_ATTACHED, 0);
                handler->handle_event = wm_widget_load_event;
                handler->js_context = c;
                handler->evt_listen_obj = obj;
#endif
        }

        return JS_TRUE;
}


static void wm_handle_dom_event(GF_Node *hdl, GF_DOM_Event *event, GF_Node *observer)
{
        GF_FieldInfo info;
        GF_Node *n;
        jsval argv[1], rval, jsfun;
        SVG_handlerElement *handler = (SVG_handlerElement *) hdl;
        GF_WidgetPin *param = (GF_WidgetPin*)handler->js_fun;
        GF_WidgetInstance *wid = (GF_WidgetInstance *)handler->evt_listen_obj;

        if (!wid->scene)
                return;

        n = gf_sg_find_node_by_name(wid->scene, param->node);
        if (!n) return;

        /*this is a regular event output*/
        if (event->type != GF_EVENT_ATTR_MODIFIED) {
                jsfun = (jsval) handler->js_fun_val;
                if (JSVAL_IS_OBJECT(jsfun))
                        JS_CallFunctionValue(handler->js_context, wid->obj, (jsval) handler->js_fun_val, 0, 0, &rval);
                return;
        }

        /*otherwise this is a node.attr output through DOMAttributeModified*/
        argv[0] = JSVAL_NULL;

#ifndef GPAC_DISABLE_SVG
        if (n->sgprivate->tag >= GF_NODE_FIRST_DOM_NODE_TAG) {
                if (!strcmp(param->attribute, "textContent")) {
                        char *txt = gf_dom_flatten_textContent(n);
                        argv[0] = STRING_TO_JSVAL( JS_NewStringCopyZ(handler->js_context, txt ? txt : "") );
                        if (txt) gf_free(txt);
                } else if (gf_node_get_attribute_by_name(n, param->attribute, 0, GF_TRUE, GF_FALSE, &info)==GF_OK) {
                        char *attValue;
                        if (event->attr->fieldIndex != info.fieldIndex) return;

                        attValue = gf_node_dump_attribute(n, &info);
                        argv[0] = STRING_TO_JSVAL( JS_NewStringCopyZ(handler->js_context, attValue) );
                        if (attValue) gf_free(attValue);
                } else {
                        return;
                }
        } else
#endif
        {
                if (gf_node_get_field_by_name(n, param->attribute, &info) != GF_OK) return;
                if (event->attr->fieldIndex != info.fieldIndex) return;

                switch (info.fieldType) {
                case GF_SG_VRML_SFSTRING:
                        argv[0] = STRING_TO_JSVAL( JS_NewStringCopyZ(handler->js_context, ((SFString*)info.far_ptr)->buffer ) );
                        break;
                case GF_SG_VRML_SFINT32:
                        argv[0] = INT_TO_JSVAL( *((SFInt32*)info.far_ptr) );
                        break;
                case GF_SG_VRML_SFBOOL:
                        argv[0] = BOOLEAN_TO_JSVAL( *((SFBool*)info.far_ptr) );
                        break;
                case GF_SG_VRML_SFFLOAT:
                        argv[0] = DOUBLE_TO_JSVAL( JS_NewDouble(handler->js_context, FIX2FLT( *((SFFloat*)info.far_ptr) ) ) );
                        break;
                case GF_SG_VRML_SFTIME:
                        argv[0] = DOUBLE_TO_JSVAL( JS_NewDouble(handler->js_context, *((SFTime *)info.far_ptr) ) );
                        break;
                }
        }

        jsfun = (jsval) handler->js_fun_val;
        if (JSVAL_IS_OBJECT(jsfun))
                JS_CallFunctionValue(handler->js_context, wid->obj, (jsval) handler->js_fun_val, 1, argv, &rval);
}

static JSBool SMJS_FUNCTION(wm_widget_get_param_value)
{
        GF_Node *n;
        GF_FieldInfo info;
        GF_WidgetPin *param;
        SMJS_OBJ
        SMJS_ARGS
        GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj);
        if (!wid || !wid->scene || !argc || !JSVAL_IS_OBJECT(argv[0]) ) return JS_FALSE;

        param = SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[0]) );
        if (!param) return JS_FALSE;

        if (!param->node) {
                if (param->default_value) {
                        SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, param->default_value) ) );
                        return JS_TRUE;
                }
                return JS_FALSE;
        }

        n = gf_sg_find_node_by_name(wid->scene, param->node);
        if (!n) return JS_FALSE;

#ifndef GPAC_DISABLE_SVG
        if (n->sgprivate->tag >= GF_NODE_FIRST_DOM_NODE_TAG) {
                if (!strcmp(param->attribute, "textContent")) {
                        char *txt = gf_dom_flatten_textContent(n);
                        SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, txt ? txt : "") ));
                        if (txt) gf_free(txt);
                } else if (gf_node_get_attribute_by_name(n, param->attribute, 0, GF_TRUE, GF_FALSE, &info)==GF_OK) {
                        char *attValue = gf_node_dump_attribute(n, &info);
                        SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, attValue) ));
                        if (attValue) gf_free(attValue);
                } else {
                        return JS_FALSE;
                }
        } else
#endif
        {
                if (gf_node_get_field_by_name(n, param->attribute, &info) != GF_OK) return JS_FALSE;

                switch (info.fieldType) {
                case GF_SG_VRML_SFSTRING:
                        SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, ((SFString*)info.far_ptr)->buffer ) ) );
                        break;
                case GF_SG_VRML_SFINT32:
                        SMJS_SET_RVAL( INT_TO_JSVAL( *((SFInt32*)info.far_ptr) ));
                        break;
                case GF_SG_VRML_SFBOOL:
                        SMJS_SET_RVAL(BOOLEAN_TO_JSVAL( *((SFBool*)info.far_ptr) ));
                        break;
                case GF_SG_VRML_SFFLOAT:
                        SMJS_SET_RVAL( DOUBLE_TO_JSVAL( JS_NewDouble(c, FIX2FLT( *((SFFloat*)info.far_ptr) ) ) ));
                        break;
                case GF_SG_VRML_SFTIME:
                        SMJS_SET_RVAL( DOUBLE_TO_JSVAL( JS_NewDouble(c, *((SFTime *)info.far_ptr) ) ));
                        break;
                }
        }

        return JS_TRUE;
}

static JSBool SMJS_FUNCTION(wm_widget_get_message_param)
{
        GF_WidgetPin *par = NULL;
        SMJS_OBJ
        SMJS_ARGS
        GF_WidgetMessage *msg = (GF_WidgetMessage *)SMJS_GET_PRIVATE(c, obj);
        if (!msg || !argc  ) return JS_FALSE;

        if (JSVAL_IS_INT(argv[0])) {
                u32 idx = JSVAL_TO_INT(argv[0]);
                par = gf_list_get(msg->params, idx);
        } else if (JSVAL_IS_STRING(argv[0])) {
                u32 i, count = gf_list_count(msg->params);
                char *name =  SMJS_CHARS(c, argv[0]);
                for (i=0; i<count; i++) {
                        par = gf_list_get(msg->params, i);
                        if (!strcmp(par->name, name)) break;
                        par = NULL;
                }
                SMJS_FREE(c, name);
        }

        if (par) {
                JSObject *obj = JS_NewObject(c, &msg->ifce->content->widget->wm->widgetAnyClass._class, 0, 0);
                SMJS_SET_PRIVATE(c, obj, par);
                JS_DefineProperty(c, obj, "name", STRING_TO_JSVAL( JS_NewStringCopyZ(c, par->name) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                JS_DefineProperty(c, obj, "is_input", BOOLEAN_TO_JSVAL( (par->type == GF_WM_PARAM_OUTPUT) ? JS_FALSE : JS_TRUE), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                switch (par->script_type) {
                case GF_WM_PARAM_SCRIPT_BOOL:
                        JS_DefineProperty(c, obj, "script_type", STRING_TO_JSVAL( JS_NewStringCopyZ(c, "boolean") ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        break;
                case GF_WM_PARAM_SCRIPT_NUMBER:
                        JS_DefineProperty(c, obj, "script_type", STRING_TO_JSVAL( JS_NewStringCopyZ(c, "number") ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        break;
                case GF_WM_PARAM_SCRIPT_STRING:
                default:
                        JS_DefineProperty(c, obj, "script_type", STRING_TO_JSVAL( JS_NewStringCopyZ(c, "string") ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        break;
                }
                SMJS_SET_RVAL( OBJECT_TO_JSVAL(obj) );
        }
        return JS_TRUE;
}

static JSBool SMJS_FUNCTION(wm_widget_get_message)
{
        GF_WidgetMessage *msg;
        SMJS_OBJ
        SMJS_ARGS
        GF_WidgetInterface *ifce = (GF_WidgetInterface*)SMJS_GET_PRIVATE(c, obj);
        if (!ifce || !argc) return JS_FALSE;
        msg = NULL;

        if (JSVAL_IS_INT(argv[0])) {
                u32 idx;
                idx = JSVAL_TO_INT(argv[0]);
                msg = (GF_WidgetMessage*)gf_list_get(ifce->messages, idx);
        } else if (JSVAL_IS_STRING(argv[0])) {
                u32 i, count = gf_list_count(ifce->messages);
                char *name = SMJS_CHARS(c, argv[0]);
                for (i=0; i<count; i++) {
                        msg = (GF_WidgetMessage*)gf_list_get(ifce->messages, i);
                        if (!strcmp(msg->name, name)) break;
                        msg = NULL;
                }
                SMJS_FREE(c, name);
        }
        if (msg) {
                JSObject *obj = JS_NewObject(c, &ifce->content->widget->wm->widgetAnyClass._class, 0, 0);
                SMJS_SET_PRIVATE(c, obj, msg);
                JS_DefineProperty(c, obj, "num_params", INT_TO_JSVAL( gf_list_count(msg->params) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                JS_DefineProperty(c, obj, "name", STRING_TO_JSVAL( JS_NewStringCopyZ(c, msg->name) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                JS_DefineProperty(c, obj, "is_input", BOOLEAN_TO_JSVAL( msg->is_output ? JS_FALSE : JS_TRUE ) , 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                JS_DefineProperty(c, obj, "has_output_trigger", BOOLEAN_TO_JSVAL( msg->output_trigger ? JS_TRUE : JS_FALSE) , 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                JS_DefineProperty(c, obj, "has_input_action", BOOLEAN_TO_JSVAL( (msg->input_action && msg->input_action->attribute) ? JS_TRUE : JS_FALSE) , 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                JS_DefineProperty(c, obj, "has_script_input", BOOLEAN_TO_JSVAL( (msg->input_action && !msg->input_action->attribute) ? JS_TRUE : JS_FALSE) , 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                JS_DefineFunction(c, obj, "get_param", wm_widget_get_message_param, 1, 0);

                SMJS_SET_RVAL( OBJECT_TO_JSVAL(obj) );
        }
        return JS_TRUE;
}

static JSBool SMJS_FUNCTION(wm_widget_get_component)
{
        char *comp_id;
        u32 i, count;
        GF_WidgetComponentInstance *comp_inst;
        SMJS_OBJ
        SMJS_ARGS
        GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj);
        if (!wid || !argc || !JSVAL_IS_STRING(argv[0]) ) return JS_FALSE;

        comp_id = SMJS_CHARS(c, argv[0]);
        count = gf_list_count(wid->components);
        for (i=0; i<count; i++) {
                comp_inst = (GF_WidgetComponentInstance*)gf_list_get(wid->components, i);
                if (comp_inst->comp->id && !strcmp(comp_inst->comp->id, comp_id)) {
                        SMJS_SET_RVAL( OBJECT_TO_JSVAL(comp_inst->wid->obj) );
                        SMJS_FREE(c, comp_id);
                        return JS_TRUE;
                }
        }
        /*if requested load the component*/
        if ((argc==2) && JSVAL_IS_BOOLEAN(argv[1]) && (JSVAL_TO_BOOLEAN(argv[1])==JS_TRUE) ) {
                count = gf_list_count(wid->widget->main->components);
                for (i=0; i<count; i++) {
                        GF_WidgetComponent *comp = (GF_WidgetComponent*)gf_list_get(wid->widget->main->components, i);
                        if (!comp->id  || strcmp(comp->id, comp_id)) continue;

                        comp_inst = wm_activate_component(c, wid, comp, GF_TRUE);
                        if (comp_inst) {
                                SMJS_SET_RVAL( OBJECT_TO_JSVAL(comp_inst->wid->obj) );
                                SMJS_FREE(c, comp_id);
                                return JS_TRUE;
                        }
                        SMJS_FREE(c, comp_id);
                        return JS_TRUE;
                }
        }

        SMJS_FREE(c, comp_id);
        return JS_TRUE;
}

static JSBool SMJS_FUNCTION(wm_widget_get_interface)
{
        u32 idx;
        GF_WidgetInterface *ifce;
        SMJS_OBJ
        SMJS_ARGS
        GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj);
        if (!wid || !argc || !JSVAL_IS_INT(argv[0]) ) return JS_FALSE;

        idx = JSVAL_TO_INT(argv[0]);
        ifce = (GF_WidgetInterface*)gf_list_get(wid->widget->main->interfaces, idx);

        if (ifce) {
                if (!ifce->obj) {
                        ifce->obj = JS_NewObject(c, &wid->widget->wm->widgetAnyClass._class, 0, 0);
                        SMJS_SET_PRIVATE(c, ifce->obj, ifce);
                        JS_DefineProperty(c, ifce->obj, "num_messages", INT_TO_JSVAL( gf_list_count(ifce->messages) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        JS_DefineProperty(c, ifce->obj, "type", STRING_TO_JSVAL( JS_NewStringCopyZ(c, ifce->type) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        JS_DefineProperty(c, ifce->obj, "serviceProvider", BOOLEAN_TO_JSVAL( ifce->provider ? JS_TRUE : JS_FALSE ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        JS_DefineProperty(c, ifce->obj, "multipleBinding", BOOLEAN_TO_JSVAL( ifce->multiple_binding ? JS_TRUE : JS_FALSE ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        JS_DefineFunction(c, ifce->obj, "get_message", wm_widget_get_message, 1, 0);
                        gf_js_add_root(c, &ifce->obj, GF_JSGC_OBJECT);
                }
                SMJS_SET_RVAL( OBJECT_TO_JSVAL(ifce->obj) );
        }
        return JS_TRUE;
}

static JSBool SMJS_FUNCTION(wm_widget_bind_output_trigger)
{
        GF_WidgetMessage *msg;
        GF_WidgetPin *param;
        SVG_handlerElement *handler;
        SMJS_OBJ
        SMJS_ARGS
        GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj);

        SMJS_SET_RVAL( BOOLEAN_TO_JSVAL(JS_FALSE) );
        if (!wid || !wid->scene || (argc!=3)) return JS_TRUE;

        if (!JSVAL_IS_OBJECT(argv[0])) return JS_TRUE;
        if (!JSVAL_IS_OBJECT(argv[1])) return JS_TRUE;
        if (!JSVAL_IS_OBJECT(argv[2])) return JS_TRUE;

        msg = (GF_WidgetMessage *)SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[0]));
        if (!msg) return JS_TRUE;
        param = msg->output_trigger;
        if (!param) return JS_TRUE;


        handler = wm_create_scene_listener(wid, param);
        if (!handler) return JS_TRUE;
        handler->js_fun_val = * (u64 *) & argv[1];
        gf_js_add_root(c, &handler->js_fun_val, GF_JSGC_VAL);
        handler->evt_listen_obj = wid;
        handler->js_fun = param;
        handler->js_context = c;
        handler->handle_event = wm_handle_dom_event;
        handler->sgprivate->UserPrivate = JSVAL_TO_OBJECT(argv[2]);

        gf_list_add(wid->output_triggers, handler);
        SMJS_SET_RVAL( BOOLEAN_TO_JSVAL(JS_TRUE) );

        return JS_TRUE;
}

static JSBool SMJS_FUNCTION(wm_widget_get_context)
{
        GF_BitStream *bs;
        u32 i, count;
        const char *str;
        char *att;
        SMJS_OBJ
        //SMJS_ARGS
        GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj);
        if (!wid) return JS_FALSE;
        if (!wid->scene) {
                SMJS_SET_RVAL(JSVAL_NULL);
                return JS_TRUE;
        }

        bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
        str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<contextInformation xmlns=\"urn:mpeg:mpegu:schema:widgets:contextinfo:2010\">\n";
        gf_bs_write_data(bs, (const char *) str, (u32) strlen(str) );

        count = gf_list_count(wid->widget->main->preferences);
        for (i=0; i<count; i++) {
                GF_WidgetPreference *pref = (GF_WidgetPreference*)gf_list_get(wid->widget->main->preferences, i);

                /*preference is read only, do not include in context*/
                if (pref->flags & GF_WM_PREF_READONLY) continue;
                /*preference is not migratable, do not include in context*/
                if (!(pref->flags & GF_WM_PREF_MIGRATE)) continue;

                str = " <preference name=\"";
                gf_bs_write_data(bs, (const char *) str, (u32) strlen(str) );

                gf_bs_write_data(bs, (const char *) pref->name, (u32) strlen(pref->name) );

                str = "\" value=\"";
                gf_bs_write_data(bs, (const char *) str, (u32) strlen(str) );

                /*read from node*/
                if (pref->connectTo && pref->connectTo->attribute) {
                        GF_FieldInfo info;
                        GF_Node *n = gf_sg_find_node_by_name(wid->scene, pref->connectTo->node);
                        if (n) {

#ifndef GPAC_DISABLE_SVG
                                if ((n->sgprivate->tag >= GF_NODE_FIRST_DOM_NODE_TAG) && !strcmp(pref->connectTo->attribute, "textContent")) {
                                        char *txt = gf_dom_flatten_textContent(n);
                                        gf_bs_write_data(bs, (const char *) txt, (u32) strlen(txt) );
                                } else
#endif
                                        if (gf_node_get_field_by_name(n, pref->connectTo->attribute, &info)==GF_OK) {
                                                att = gf_node_dump_attribute(n, &info);
                                                if (att) {
                                                        gf_bs_write_data(bs, (const char *)  att, (u32) strlen(att) );
                                                        gf_free(att);
                                                }
                                        }
                        }
                }
                /*read from config*/
                else {
                        att = (char *)gf_cfg_get_key(wid->widget->wm->term->user->config, (const char *) wid->secname, pref->name);
                        if (!att) att = pref->value;

                        if (att) gf_bs_write_data(bs, (const char *) att, (u32) strlen(att) );
                }

                str = "\"/>\n";
                gf_bs_write_data(bs, (const char *)  str, (u32) strlen(str) );
        }
        str = "</contextInformation>\n";
        gf_bs_write_data(bs, (const char *)  str, (u32) strlen(str) );

        gf_bs_write_u8(bs, 0);
        att = NULL;
        gf_bs_get_content(bs, &att, &count);
        gf_bs_del(bs);

        SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, att) ) );
        gf_free(att);

        return JS_TRUE;
}


static SMJS_FUNC_PROP_GET( wm_widget_getProperty)

JSString *s;
char *prop_name;
const char *opt;
GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj);
if (!wid) return JS_FALSE;

if (!SMJS_ID_IS_STRING(id)) return JS_TRUE;
prop_name = SMJS_CHARS_FROM_STRING(c, SMJS_ID_TO_STRING(id));
if (!prop_name) return JS_FALSE;

/*
                Manifest properties
*/
if (!strcmp(prop_name, "manifest")) {
        s = JS_NewStringCopyZ(c, wid->widget->manifest_path);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "url")) {
        s = JS_NewStringCopyZ(c, wid->widget->local_path ? wid->widget->local_path : wid->widget->url);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "main")) {
        s = JS_NewStringCopyZ(c, wid->widget->main->relocated_src);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "localizedSrc")) {
        s = JS_NewStringCopyZ(c, wid->widget->main->src);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "mainEncoding")) {
        s = JS_NewStringCopyZ(c, wid->widget->main->encoding);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "mainMimeType")) {
        s = JS_NewStringCopyZ(c, wid->widget->main->mimetype);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "defaultWidth")) {
        *vp = INT_TO_JSVAL(wid->widget->width);
}
else if (!strcmp(prop_name, "defaultHeight")) {
        *vp = INT_TO_JSVAL(wid->widget->height);
}
else if (!strcmp(prop_name, "icons")) {
        u32 i, count;
        JSObject *arr;
        count = gf_list_count(wid->widget->icons);
        arr = JS_NewArrayObject(c, count, NULL);
        for (i = 0; i<count; i++) {
                GF_WidgetContent *icon = (GF_WidgetContent*)gf_list_get(wid->widget->icons, i);
                if (icon) {
                        char *abs_reloc_url;
                        jsval icon_obj_val;
                        JSObject *icon_obj = JS_NewObject(c, &wid->widget->wm->widgetAnyClass._class, 0, 0);
                        SMJS_SET_PRIVATE(c, icon_obj, icon);
                        JS_DefineProperty(c, icon_obj, "src", STRING_TO_JSVAL( JS_NewStringCopyZ(c, icon->src) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        if (strlen(icon->relocated_src)) abs_reloc_url = gf_url_concatenate(wid->widget->url, icon->relocated_src);
                        else abs_reloc_url = gf_strdup("");
                        JS_DefineProperty(c, icon_obj, "relocated_src", STRING_TO_JSVAL( JS_NewStringCopyZ(c, abs_reloc_url) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        JS_DefineProperty(c, icon_obj, "width", INT_TO_JSVAL( icon->width ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        JS_DefineProperty(c, icon_obj, "height", INT_TO_JSVAL( icon->height ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        icon_obj_val = OBJECT_TO_JSVAL(icon_obj);
                        JS_SetElement(c, arr, i, &icon_obj_val);
                        gf_free(abs_reloc_url);
                }
        }
        *vp = OBJECT_TO_JSVAL(arr);
}
else if (!strcmp(prop_name, "preferences")) {
        u32 i, count;
        JSObject *arr;
        count = gf_list_count(wid->widget->main->preferences);
        arr = JS_NewArrayObject(c, count, NULL);
        for (i = 0; i<count; i++) {
                GF_WidgetPreference *pref = (GF_WidgetPreference*)gf_list_get(wid->widget->main->preferences, i);
                if (pref) {
                        jsval pref_obj_val;
                        JSObject *pref_obj = JS_NewObject(c, &wid->widget->wm->widgetAnyClass._class, 0, 0);
                        SMJS_SET_PRIVATE(c, pref_obj, pref);
                        JS_DefineProperty(c, pref_obj, "name", STRING_TO_JSVAL( JS_NewStringCopyZ(c, pref->name) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        JS_DefineProperty(c, pref_obj, "value", STRING_TO_JSVAL( JS_NewStringCopyZ(c, pref->value) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        JS_DefineProperty(c, pref_obj, "readonly", STRING_TO_JSVAL( JS_NewStringCopyZ(c, ((pref->flags & GF_WM_PREF_READONLY)?"true":"false")) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        pref_obj_val = OBJECT_TO_JSVAL(pref_obj);
                        JS_SetElement(c, arr, i, &pref_obj_val);
                }
        }
        *vp = OBJECT_TO_JSVAL(arr);
}
else if (!strcmp(prop_name, "features")) {
        u32 i, count;
        JSObject *arr;
        count = gf_list_count(wid->widget->features);
        arr = JS_NewArrayObject(c, count, NULL);
        for (i = 0; i<count; i++) {
                GF_WidgetFeature *feat = (GF_WidgetFeature*)gf_list_get(wid->widget->features, i);
                if (feat) {
                        jsval feat_obj_val;
                        JSObject *feat_obj = JS_NewObject(c, &wid->widget->wm->widgetAnyClass._class, 0, 0);
                        SMJS_SET_PRIVATE(c, feat_obj, feat);
                        JS_DefineProperty(c, feat_obj, "name", STRING_TO_JSVAL( JS_NewStringCopyZ(c, feat->name) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        JS_DefineProperty(c, feat_obj, "required", BOOLEAN_TO_JSVAL( (feat->required? JS_TRUE : JS_FALSE) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        {
                                u32 j, pcount;
                                JSObject *params_arr;
                                pcount = gf_list_count(feat->params);
                                params_arr = JS_NewArrayObject(c, pcount, NULL);
                                for (j=0; j < pcount; j++) {
                                        GF_WidgetFeatureParam *param = (GF_WidgetFeatureParam*)gf_list_get(feat->params, j);
                                        JSObject *param_obj = JS_NewObject(c, &wid->widget->wm->widgetAnyClass._class, 0, 0);
                                        jsval param_obj_val;
                                        SMJS_SET_PRIVATE(c, param_obj, param);
                                        JS_DefineProperty(c, param_obj, "name", STRING_TO_JSVAL( JS_NewStringCopyZ(c, param->name) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                                        JS_DefineProperty(c, param_obj, "value", STRING_TO_JSVAL( JS_NewStringCopyZ(c, param->value) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                                        param_obj_val = OBJECT_TO_JSVAL(param_obj);
                                        JS_SetElement(c, params_arr, j, &param_obj_val);
                                }
                                JS_DefineProperty(c, feat_obj, "params", OBJECT_TO_JSVAL(params_arr), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
                        }
                        feat_obj_val = OBJECT_TO_JSVAL(feat_obj);
                        JS_SetElement(c, arr, i, &feat_obj_val);
                }
        }
        *vp = OBJECT_TO_JSVAL(arr);
}

else if (!strcmp(prop_name, "components")) {
        u32 i, count;
        jsval val;
        JSObject *arr;
        count = gf_list_count(wid->components);
        arr = JS_NewArrayObject(c, count, NULL);
        for (i = 0; i<count; i++) {
                GF_WidgetComponentInstance *comp = (GF_WidgetComponentInstance*)gf_list_get(wid->components, i);
                val = OBJECT_TO_JSVAL(comp->wid->obj);
                JS_SetElement(c, arr, i, &val);
        }
        *vp = OBJECT_TO_JSVAL(arr);
}
else if (!strcmp(prop_name, "identifier")) {
        s = JS_NewStringCopyZ(c, wid->widget->identifier);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "name")) {
        s = JS_NewStringCopyZ(c, wid->widget->name);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "shortName")) {
        s = JS_NewStringCopyZ(c, wid->widget->shortname);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "authorName")) {
        s = JS_NewStringCopyZ(c, wid->widget->authorName);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "authorEmail")) {
        s = JS_NewStringCopyZ(c, wid->widget->authorEmail);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "authorHref")) {
        s = JS_NewStringCopyZ(c, wid->widget->authorHref);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "description")) {
        s = JS_NewStringCopyZ(c, wid->widget->description);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "viewmodes")) {
        if (wid->widget->viewmodes) s = JS_NewStringCopyZ(c, wid->widget->viewmodes);
        else s = JS_NewStringCopyZ(c, "");
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "license")) {
        s = JS_NewStringCopyZ(c, wid->widget->license);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "licenseHref")) {
        s = JS_NewStringCopyZ(c, wid->widget->licenseHref);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "version")) {
        s = JS_NewStringCopyZ(c, wid->widget->version);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "uuid")) {
        s = JS_NewStringCopyZ(c, wid->widget->uuid);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "discardable")) {
        *vp = BOOLEAN_TO_JSVAL( wid->widget->discardable ? JS_TRUE : JS_FALSE);
}
else if (!strcmp(prop_name, "multipleInstances")) {
        *vp = BOOLEAN_TO_JSVAL( wid->widget->multipleInstance ? JS_TRUE : JS_FALSE);
}

/*
                Widget Manager special properties (common to all implementations)
*/
else if (!strcmp(prop_name, "permanent")) {
        *vp = BOOLEAN_TO_JSVAL( wid->permanent ? JS_TRUE : JS_FALSE);
}
else if (!strcmp(prop_name, "is_component")) {
        *vp = BOOLEAN_TO_JSVAL( wid->parent ? JS_TRUE : JS_FALSE);
}
else if (!strcmp(prop_name, "parent")) {
        *vp = wid->parent ? OBJECT_TO_JSVAL(wid->parent->obj) : JSVAL_NULL;
}
else if (!strcmp(prop_name, "activated")) {
        *vp = BOOLEAN_TO_JSVAL( wid->activated ? JS_TRUE : JS_FALSE);
}
else if (!strcmp(prop_name, "section")) {
        s = JS_NewStringCopyZ(c, (const char *) wid->secname);
        *vp = STRING_TO_JSVAL(s);
}
else if (!strcmp(prop_name, "num_instances")) {
        *vp = INT_TO_JSVAL( wid->widget->nb_instances);
}
else if (!strcmp(prop_name, "num_interfaces")) {
        *vp = INT_TO_JSVAL( gf_list_count(wid->widget->main->interfaces));
}
else if (!strcmp(prop_name, "num_components")) {
        *vp = INT_TO_JSVAL( gf_list_count(wid->components));
}
else if (!strcmp(prop_name, "num_bound_interfaces")) {
        *vp = INT_TO_JSVAL( gf_list_count(wid->bound_ifces));
}
/*all variables used by the WidgetManager script but not stored*/
else if (!strcmp(prop_name, "originating_device_ip")
         || !strcmp(prop_name, "originating_device")
         || !strcmp(prop_name, "device")
        ) {
}
/*
                Widget properties, common to each implementation
*/
else {
        char szName[1024];
        sprintf(szName, "WM:%s", prop_name);
        opt = gf_cfg_get_key(wid->widget->wm->term->user->config, (const char *) wid->secname, szName);
        if (opt) {
                Double val=0;
                if (!strcmp(opt, "true")) *vp = BOOLEAN_TO_JSVAL(JS_TRUE);
                else if (!strcmp(opt, "false")) *vp = BOOLEAN_TO_JSVAL(JS_FALSE);
                else if (sscanf(opt, "%lf", &val)==1) {
                        *vp = DOUBLE_TO_JSVAL( JS_NewDouble(c, val) );
                } else {
                        s = JS_NewStringCopyZ(c, opt);
                        *vp = STRING_TO_JSVAL(s);
                }
        }
}
SMJS_FREE(c, prop_name);
return JS_TRUE;
}

static SMJS_FUNC_PROP_SET( wm_widget_setProperty)

char szVal[32];
jsdouble val;
char *prop_name;
GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj);
if (!wid) return JS_FALSE;

if (!SMJS_ID_IS_STRING(id)) return JS_TRUE;
prop_name = SMJS_CHARS_FROM_STRING(c, SMJS_ID_TO_STRING(id));

/*internal to WidgetManager, never stored*/
if (!strcmp(prop_name, "permanent")) {
        wid->permanent = (JSVAL_TO_BOOLEAN(*vp)==JS_TRUE) ? GF_TRUE : GF_FALSE;
}

/*any widget properties*/
else {
        char szName[1024], *value, *_val = NULL;

        if (JSVAL_IS_STRING(*vp)) {
                value = _val = SMJS_CHARS(c, *vp);
                if (!value) value = "";
        }
        else if (JSVAL_IS_BOOLEAN(*vp)) {
                strcpy(szVal, (JSVAL_TO_BOOLEAN(*vp)==JS_TRUE) ? "true" : "false");
                value = szVal;
        }
        else if (JSVAL_IS_NUMBER(*vp)) {
                JS_ValueToNumber(c, *vp, &val);
                sprintf(szVal, "%f", val);
                value = szVal;
        } else {
                SMJS_FREE(c, prop_name);
                return JS_TRUE;
        }

        sprintf(szName, "WM:%s", prop_name);
        gf_cfg_set_key(wid->widget->wm->term->user->config, (const char *) wid->secname, szName, value);
        SMJS_FREE(c, _val);
}

SMJS_FREE(c, prop_name);
return JS_TRUE;
}



static JSBool wm_widget_bind_interface_ex(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval, Bool is_unbind)
{
        u32 i, count;
        GF_WidgetInterfaceInstance *bifce;
        GF_WidgetInterface *ifce;
        GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj);
        if (!wid || !wid->scene) return JS_FALSE;

        ifce = NULL;
        if (argc) {
                if (!JSVAL_IS_OBJECT(argv[0])) return JS_FALSE;
                ifce = SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[0]) );
                if (!ifce) return JS_FALSE;
        }

        if (!is_unbind) {
                char *hostname;
                JSObject *cookie;
                if ((argc<3) || !JSVAL_IS_OBJECT(argv[1]) || !JSVAL_IS_STRING(argv[2])) return JS_FALSE;

                cookie = JSVAL_TO_OBJECT(argv[1]);
                hostname = SMJS_CHARS(c, argv[2]);
                count = gf_list_count(wid->bound_ifces);
                for (i=0; i<count; i++) {
                        bifce = (GF_WidgetInterfaceInstance*)gf_list_get(wid->bound_ifces, i);
                        if (!strcmp(bifce->ifce->type, ifce->type) && (bifce->cookie==cookie) ) {
                                SMJS_FREE(c, hostname);
                                return JS_TRUE;
                        }
                }
                GF_SAFEALLOC(bifce, GF_WidgetInterfaceInstance);
                bifce->wid = wid;
                bifce->ifce = ifce;
                bifce->cookie = cookie;
                bifce->hostname = gf_strdup(hostname);
                SMJS_FREE(c, hostname);
                gf_list_add(wid->bound_ifces, bifce);

                if (ifce->bind_action) {
                        return wm_widget_set_scene_input_value(c, obj, 1, argv, rval, 2, NULL, NULL, NULL);
                } else {
                        widget_on_interface_bind(bifce, GF_FALSE);
                }
        } else {
                JSObject *cookie = NULL;
                if ((argc==2) && JSVAL_IS_OBJECT(argv[1])) cookie = JSVAL_TO_OBJECT(argv[1]);

                count = gf_list_count(wid->bound_ifces);
                for (i=0; i<count; i++) {
                        bifce = (GF_WidgetInterfaceInstance*)gf_list_get(wid->bound_ifces, i);
                        if (!ifce || ( !strcmp(bifce->ifce->type, ifce->type) && (bifce->cookie==cookie)) ) {
                                gf_list_rem(wid->bound_ifces, i);
                                if (bifce->ifce->unbind_action) {
                                        wm_widget_set_scene_input_value(c, NULL, 0, NULL, rval, 3, wid, bifce->ifce->unbind_action, NULL);
                                } else {
                                        widget_on_interface_bind(bifce, GF_TRUE);
                                }
                                if (ifce) {
                                        /*unregister our message handlers*/
                                        count = gf_list_count(wid->output_triggers);
                                        for (i=0; i<count; i++) {
                                                u32 j, c2, found;
                                                GF_DOMHandler *handler = (GF_DOMHandler*)gf_list_get(wid->output_triggers, i);
                                                GF_WidgetPin *param = (GF_WidgetPin*)handler->js_fun;

                                                if (handler->sgprivate->UserPrivate != cookie) continue;
                                                found = 0;
                                                c2 = gf_list_count(bifce->ifce->messages);
                                                for (j=0; j<c2; j++) {
                                                        GF_WidgetMessage *msg = (GF_WidgetMessage*)gf_list_get(bifce->ifce->messages, j);
                                                        if (msg->output_trigger == param) {
                                                                found = 1;
                                                                break;
                                                        }
                                                }
                                                if (found) {
#ifndef GPAC_DISABLE_SVG
                                                        GF_Node *listener = handler->sgprivate->parents->node;
                                                        gf_dom_listener_del(listener, listener->sgprivate->UserPrivate);
#endif
                                                        gf_list_rem(wid->output_triggers, i);
                                                        i--;
                                                        count--;
                                                }
                                        }
                                }
                                wm_delete_interface_instance(wid->widget->wm, bifce);
                                if (ifce) return JS_TRUE;
                                i--;
                                count--;
                        }
                }
        }
        return JS_TRUE;
}

static JSBool SMJS_FUNCTION(wm_widget_bind_interface)
{
        SMJS_OBJ
        SMJS_ARGS
        SMJS_DECL_RVAL
        return wm_widget_bind_interface_ex(c, obj, argc, argv, rval, GF_FALSE);
}
static JSBool SMJS_FUNCTION(wm_widget_unbind_interface)
{
        SMJS_OBJ
        SMJS_ARGS
        SMJS_DECL_RVAL
        return wm_widget_bind_interface_ex(c, obj, argc, argv, rval, GF_TRUE);
}


static JSBool SMJS_FUNCTION(wm_widget_deactivate)
{
        u32 i, count;
        jsval funval;
        SMJS_OBJ
        GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj);
        if (!wid) return JS_FALSE;

        /*widget is a component of another widget, unregister*/
        if (wid->parent) {
                GF_WidgetInstance *par_wid = wid->parent;
                count = gf_list_count(par_wid->components);
                for (i=0; i<count; i++) {
                        GF_WidgetComponentInstance *comp = (GF_WidgetComponentInstance*)gf_list_get(par_wid->components, i);
                        if (comp->wid == wid) {
                                gf_list_rem(par_wid->components, i);
                                gf_free(comp);
                                break;
                        }
                }
                wid->parent = NULL;
        }

        /*remove all components*/
        while (gf_list_count(wid->components)) {
                GF_WidgetComponentInstance *comp = (GF_WidgetComponentInstance*)gf_list_get(wid->components, 0);
                wm_deactivate_component(c, wid, NULL, comp);
                gf_list_rem(wid->components, 0);
        }

        /*mark the widget as deactivated, so it is no longer valid when checking all widgets bindings*/
        wid->activated = GF_FALSE;

        /*unbind existing widgets*/
        JS_LookupProperty(wid->widget->wm->ctx, wid->widget->wm->obj, "unbind_widget", &funval);
        if (JSVAL_IS_OBJECT(funval)) {
                jsval an_argv[1];
                an_argv[0] = OBJECT_TO_JSVAL(obj);
                JS_CallFunctionValue(wid->widget->wm->ctx, wid->widget->wm->obj, funval, 1, an_argv, SMJS_GET_RVAL );
        }

        /*unbind all interfaces of this widget*/
        wm_widget_bind_interface_ex(c, obj, 0, NULL, SMJS_GET_RVAL, GF_TRUE);

        /*detach scene now that all unbind events have been sent*/
        wid->scene = NULL;
        return JS_TRUE;
}


static void wm_widget_jsbind(GF_WidgetManager *wm, GF_WidgetInstance *wid)
{
        if (wid->obj)
                return;
        wid->obj = JS_NewObject(wm->ctx, &wm->wmWidgetClass._class, 0, 0);
        SMJS_SET_PRIVATE(wm->ctx, wid->obj, wid);
        /*protect from GC*/
        gf_js_add_root(wm->ctx, &wid->obj, GF_JSGC_OBJECT);
}

void wm_deactivate_component(JSContext *c, GF_WidgetInstance *wid, GF_WidgetComponent *comp, GF_WidgetComponentInstance *comp_inst)
{
        jsval fval, rval;

        if (!comp_inst) {
                u32 i=0;
                while ((comp_inst = (GF_WidgetComponentInstance*)gf_list_enum(wid->components, &i))) {
                        if (comp_inst->comp == comp) break;
                        comp_inst = NULL;
                }
        }
        if (!comp_inst) return;

        if ((JS_LookupProperty(c, wid->widget->wm->obj, "on_widget_remove", &fval)==JS_TRUE) && JSVAL_IS_OBJECT(fval)) {
                jsval argv[1];
                argv[0] = OBJECT_TO_JSVAL(comp_inst->wid->obj);
                JS_CallFunctionValue(wid->widget->wm->ctx, wid->obj, fval, 1, argv, &rval);
        }
}

GF_WidgetComponentInstance *wm_activate_component(JSContext *c, GF_WidgetInstance *wid, GF_WidgetComponent *comp, Bool skip_wm_notification)
{
        u32 i, count;
        const char *fun_name = NULL;
        jsval fval, rval;
        GF_WidgetComponentInstance *comp_inst;
        GF_WidgetInstance *comp_wid;

        comp_wid = NULL;
        if (comp->src) {
                char *url = gf_url_concatenate(wid->widget->url, comp->src);

                count = gf_list_count(wid->widget->wm->widget_instances);
                for (i=0; i<count; i++) {
                        comp_wid = (GF_WidgetInstance*)gf_list_get(wid->widget->wm->widget_instances, i);
                        if (!strcmp(comp_wid->widget->url, url) && !comp_wid->parent) break;
                        comp_wid = NULL;
                }
                if (!comp_wid) {
                        comp_wid = wm_load_widget(wid->widget->wm, url, 0, GF_FALSE);
                        if (comp_wid) comp_wid->permanent = GF_FALSE;
                }
                gf_free(url);
        }
        if (!comp_wid) return NULL;

        if (!comp_wid->activated)
                fun_name = "on_widget_add";

        GF_SAFEALLOC(comp_inst, GF_WidgetComponentInstance);
        if (!comp_inst) return NULL;
        comp_inst->comp = comp;
        comp_inst->wid = comp_wid;
        comp_wid->parent = wid;

        gf_list_add(wid->components, comp_inst);

        if (!comp_inst->wid->obj) wm_widget_jsbind(wid->widget->wm, comp_inst->wid);

        if (!skip_wm_notification && fun_name && (JS_LookupProperty(c, wid->widget->wm->obj, fun_name, &fval)==JS_TRUE) && JSVAL_IS_OBJECT(fval)) {
                jsval argv[1];
                argv[0] = OBJECT_TO_JSVAL(comp_inst->wid->obj);
                JS_CallFunctionValue(wid->widget->wm->ctx, wid->obj, fval, 1, argv, &rval);
        }
        if (comp_inst) return comp_inst;
        return NULL;
}

static JSBool SMJS_FUNCTION(wm_widget_is_interface_bound)
{
        u32 i, count;
        JSObject *cookie;
        GF_WidgetInterface *ifce;
        SMJS_OBJ
        SMJS_ARGS
        GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj);
        if (!wid || !wid->scene || (argc<1) || !JSVAL_IS_OBJECT(argv[0]) ) return JS_FALSE;

        ifce = SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[0]) );
        if (!ifce) return JS_FALSE;
        cookie = NULL;
        if ((argc==2) && JSVAL_IS_OBJECT(argv[1]) )
                cookie = JSVAL_TO_OBJECT(argv[1]);

        SMJS_SET_RVAL(BOOLEAN_TO_JSVAL(JS_FALSE));
        count = gf_list_count(wid->bound_ifces);
        for (i=0; i<count; i++) {
                GF_WidgetInterfaceInstance *bifce = (GF_WidgetInterfaceInstance*)gf_list_get(wid->bound_ifces, i);
                if (!strcmp(bifce->ifce->type, ifce->type) && (!cookie || (bifce->cookie==cookie))) {
                        SMJS_SET_RVAL( BOOLEAN_TO_JSVAL(JS_TRUE) );
                        break;
                }
        }
        return JS_TRUE;
}


static JSBool SMJS_FUNCTION(wm_load)
{
        u32 i, count;
        char *manifest, *url, *widget_ctx;
        GF_WidgetInstance *wid;
        SMJS_OBJ
        SMJS_ARGS
        GF_WidgetManager *wm = (GF_WidgetManager *)SMJS_GET_PRIVATE(c, obj);
        if (!argc || !JSVAL_IS_STRING(argv[0])) return JS_TRUE;

        manifest = SMJS_CHARS(c, argv[0]);

        url = NULL;
        if ((argc==2) && ! JSVAL_IS_NULL(argv[1]) && JSVAL_IS_OBJECT(argv[1])) {
                GF_WidgetInstance *parent_widget;
                if (!GF_JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[1]), &wm->wmWidgetClass, NULL) ) return JS_FALSE;
                parent_widget = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[1]) );

                if (parent_widget->widget->url) url = gf_url_concatenate(parent_widget->widget->url, manifest);
        }

        widget_ctx = NULL;
        if ((argc==3) && !JSVAL_IS_NULL(argv[2]) && JSVAL_IS_STRING(argv[2])) {
                widget_ctx = SMJS_CHARS(c, argv[2]);
        }

        if (!url) {
                url = gf_strdup(manifest);
        }

        wid=NULL;
        count = gf_list_count(wm->widget_instances);
        for (i=0; i<count; i++) {
                wid = (GF_WidgetInstance*)gf_list_get(wm->widget_instances, i);
                if (!strcmp(wid->widget->url, url) && !wid->activated) break;
                wid = NULL;
        }
        if (!wid) {
                wid = wm_load_widget(wm, url, 0, GF_TRUE);
        }
        if (url) gf_free(url);

        /*parse context if any*/
        if (wid && wid->mpegu_context) {
                gf_xml_dom_del(wid->mpegu_context);
                wid->mpegu_context = NULL;
        }
        if (wid && widget_ctx && strlen(widget_ctx)) {
                GF_Err e;
                GF_XMLNode *context = NULL;
                wid->mpegu_context = gf_xml_dom_new();
                e = gf_xml_dom_parse_string(wid->mpegu_context, widget_ctx);
                if (!e) {
                        context = gf_xml_dom_get_root(wid->mpegu_context);
                        if (strcmp(context->name, "contextInformation")) context = NULL;
                } else {
                }

                if (!context && wid->mpegu_context) {
                        gf_xml_dom_del(wid->mpegu_context);
                        wid->mpegu_context = NULL;
                }
        }

        if (wid) {
                wm_widget_jsbind(wm, wid);
                SMJS_SET_RVAL(OBJECT_TO_JSVAL(wid->obj));
        }
        SMJS_FREE(c, manifest);
        SMJS_FREE(c, widget_ctx);
        return JS_TRUE;
}

static JSBool SMJS_FUNCTION(wm_unload)
{
        GF_WidgetInstance *wid;
        SMJS_OBJ
        SMJS_ARGS
        GF_WidgetManager *wm = (GF_WidgetManager *)SMJS_GET_PRIVATE(c, obj);
        if (!argc || !JSVAL_IS_OBJECT(argv[0])) return JS_TRUE;

        if (!GF_JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &wm->wmWidgetClass, NULL) ) return JS_FALSE;
        wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[0]) );
        if (!wid) return JS_TRUE;

        /*unless explecetely requested, remove the section*/
        if ((argc!=2) || !JSVAL_IS_BOOLEAN(argv[1]) || (JSVAL_TO_BOOLEAN(argv[1])==JS_TRUE) ) {
                /*create section*/
                gf_cfg_del_section(wm->term->user->config, (const char *) wid->secname);
                gf_cfg_set_key(wm->term->user->config, "Widgets", (const char *) wid->secname, NULL);
        }
        wm_delete_widget_instance(wm, wid);
        return JS_TRUE;
}



static SMJS_FUNC_PROP_GET( wm_getProperty)

char *prop_name;
GF_WidgetManager *wm = (GF_WidgetManager *)SMJS_GET_PRIVATE(c, obj);
if (!wm) return JS_FALSE;

if (!SMJS_ID_IS_STRING(id)) return JS_TRUE;
prop_name = SMJS_CHARS_FROM_STRING(c, SMJS_ID_TO_STRING(id));
if (!prop_name) return JS_FALSE;

if (!strcmp(prop_name, "num_widgets")) {
        *vp = INT_TO_JSVAL(gf_list_count(wm->widget_instances));
}
else if (!strcmp(prop_name, "last_widget_dir")) {
        const char *opt = gf_cfg_get_key(wm->term->user->config, "Widgets", "last_widget_dir");
        if (!opt) opt = "/";
        *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(c, opt));
}
SMJS_FREE(c, prop_name);
return JS_TRUE;
}


static SMJS_FUNC_PROP_SET( wm_setProperty)

char *prop_name;
GF_WidgetManager *wm = (GF_WidgetManager *)SMJS_GET_PRIVATE(c, obj);
if (!wm) return JS_FALSE;

if (!JSVAL_IS_STRING(*vp)) return JS_TRUE;
if (!SMJS_ID_IS_STRING(id)) return JS_TRUE;
prop_name = SMJS_CHARS_FROM_STRING(c, SMJS_ID_TO_STRING(id));

if (!strcmp(prop_name, "last_widget_dir")) {
        char *v = SMJS_CHARS(c, *vp);
        gf_cfg_set_key(wm->term->user->config, "Widgets", "last_widget_dir", v);
        SMJS_FREE(c, v);
}
SMJS_FREE(c, prop_name);
return JS_TRUE;
}

static JSBool SMJS_FUNCTION(wm_get)
{
        u32 i;
        GF_WidgetInstance *wid;
        SMJS_OBJ
        SMJS_ARGS
        GF_WidgetManager *wm = (GF_WidgetManager *)SMJS_GET_PRIVATE(c, obj);
        if (!argc || !JSVAL_IS_INT(argv[0])) return JS_TRUE;

        i = JSVAL_TO_INT(argv[0]);
        wid = (GF_WidgetInstance*)gf_list_get(wm->widget_instances, i);
        if (wid) SMJS_SET_RVAL( OBJECT_TO_JSVAL(wid->obj) );
        return JS_TRUE;
}

static JSBool SMJS_FUNCTION(wm_find_interface)
{
        char *ifce_name;
        u32 i;
        GF_WidgetInstance *wid;
        SMJS_OBJ
        SMJS_ARGS
        GF_WidgetManager *wm = (GF_WidgetManager *)SMJS_GET_PRIVATE(c, obj);
        if (!argc || !JSVAL_IS_STRING(argv[0])) return JS_TRUE;

        ifce_name = SMJS_CHARS(c, argv[0]);
        i=0;
        while ( (wid = (GF_WidgetInstance*)gf_list_enum(wm->widget_instances, &i) )) {
                u32 j=0;
                GF_WidgetInterface *wid_ifce;
                while ((wid_ifce = (GF_WidgetInterface*)gf_list_enum(wid->widget->main->interfaces, &j))) {
                        if (!strcmp(wid_ifce->type, ifce_name)) {
                                SMJS_SET_RVAL( OBJECT_TO_JSVAL(wid->obj) );
                                SMJS_FREE(c, ifce_name);
                                return JS_TRUE;
                        }
                }
        }
        SMJS_FREE(c, ifce_name);
        return JS_TRUE;
}


const char *wm_xml_get_attr(GF_XMLNode *root, const char *name)
{

        u32 i, count;
        count = gf_list_count(root->attributes);
        for (i=0; i<count; i++) {
                char *sep;
                GF_XMLAttribute *att = gf_list_get(root->attributes, i);
                if (!att->name) continue;
                if (!strcmp(att->name, name)) return att->value;
                sep = strchr(att->name, ':');
                if (sep && !strcmp(sep+1, name)) return att->value;
        }
        return NULL;
}

/* TODO Implement real language check according to BCP 47*/
static Bool wm_check_language(const char *xml_lang_value, const char *user_locale)
{
        Bool ret = GF_FALSE;
        char *sep, *val;
        val = (char*)xml_lang_value;
        while (!ret) {
                sep = strchr(val, ';');
                if (sep) sep[0] = 0;
                if (strstr(user_locale, val)) ret = GF_TRUE;
                if (sep) {
                        sep[0] = ';';
                        val = sep+1;
                } else {
                        break;
                }
        }
        return ret;
}

static GF_XMLNode *wm_xml_find(GF_XMLNode *root, const char *ns_prefix, const char *name, const char *user_locale)
{
        GF_XMLNode *localized = NULL;
        GF_XMLNode *non_localized = NULL;
        u32 i, count;

        if (!root) return NULL;

        count = gf_list_count(root->content);
        for (i=0; i<count; i++) {
                GF_XMLNode *n = (GF_XMLNode*)gf_list_get(root->content, i);
                if (n->type==GF_XML_NODE_TYPE && n->name && !strcmp(n->name, name) && ((!ns_prefix && !n->ns) || (ns_prefix && n->ns && !strcmp(ns_prefix, n->ns)))) {
                        const char *lang = wm_xml_get_attr(n, "xml:lang");
                        if (!lang) {
                                if (!non_localized) non_localized = n;
                        } else {
                                if (user_locale && wm_check_language(lang, user_locale) && !localized) localized = n;
                        }
                }
        }
        if (localized) return localized;
        else return non_localized;
}

static GF_WidgetPin *wm_parse_pin(const char *value, u16 type, const char *pin_name, const char *scriptType, const char *default_value)
{
        GF_WidgetPin *pin;
        char *sep;

        if (!value && !scriptType && !default_value) return NULL;

        GF_SAFEALLOC(pin, GF_WidgetPin);
        if (!pin) return NULL;
        
        pin->type = type;
        if (pin_name) pin->name = gf_strdup(pin_name);

        if (value) {
                sep = strrchr(value, '.');
                if (!sep && (type==GF_WM_PREF_CONNECT)) {
                        gf_free(pin);
                        return NULL;
                }

                /*node.event || node.attribute*/
                if (sep) {
                        sep[0] = 0;
                        pin->node = gf_strdup(value);
                        pin->attribute = gf_strdup(sep+1);
                        sep[0] = '.';
                }
                /*script function*/
                else {
                        pin->node = gf_strdup(value);
                }
        }

        if (scriptType) {
                if (!strcmp(scriptType, "boolean")) pin->script_type = GF_WM_PARAM_SCRIPT_BOOL;
                else if (!strcmp(scriptType, "number")) pin->script_type = GF_WM_PARAM_SCRIPT_NUMBER;
        } else if (default_value) {
                pin->default_value = gf_strdup(default_value);
        }
        return pin;
}

static void wm_parse_mpegu_content_element(GF_WidgetContent *content, GF_XMLNode *root, const char *ns_prefix, GF_List *global_prefs)
{
        GF_XMLNode *ifces, *context, *pref_node;
        GF_WidgetMessage *msg;
        GF_WidgetInterface *ifce;
        GF_XMLNode *ifce_node;
        GF_WidgetPreference *pref;
        const char *att;
        u32 i = 0;

        ifces = wm_xml_find(root, ns_prefix, "interfaces", NULL);
        if (ifces) {

                /*get all interface element*/
                while ((ifce_node = (GF_XMLNode*)gf_list_enum(ifces->content, &i))) {
                        GF_XMLNode *msg_node;
                        u32 j;
                        const char *ifce_type, *act;
                        if (ifce_node->type != GF_XML_NODE_TYPE) continue;
                        if (strcmp(ifce_node->name, "interface")) continue;
                        ifce_type = wm_xml_get_attr(ifce_node, "type");
                        if (!ifce_type) continue;

                        GF_SAFEALLOC(ifce, GF_WidgetInterface);
                        ifce->type = gf_strdup(ifce_type);
                        ifce->messages = gf_list_new();
                        ifce->content = content;
                        gf_list_add(content->interfaces, ifce);

                        act = wm_xml_get_attr(ifce_node, "serviceProvider");
                        if (act && !strcmp(act, "true")) ifce->provider = GF_TRUE;

                        act = wm_xml_get_attr(ifce_node, "multipleBindings");
                        if (act && !strcmp(act, "true")) ifce->multiple_binding = GF_TRUE;

                        act = wm_xml_get_attr(ifce_node, "required");
                        if (act && !strcmp(act, "true")) ifce->required = GF_TRUE;

                        act = wm_xml_get_attr(ifce_node, "connectTo");
                        if (act) ifce->connectTo = gf_strdup(act);

                        act = wm_xml_get_attr(ifce_node, "bindAction");
                        if (act) {
                                ifce->bind_action = wm_parse_pin(act, GF_WM_BIND_ACTION, NULL, NULL, NULL);
                        }
                        act = wm_xml_get_attr(ifce_node, "unbindAction");
                        if (act) {
                                ifce->unbind_action = wm_parse_pin(act, GF_WM_UNBIND_ACTION, NULL, NULL, NULL);
                        }

                        j=0;
                        while ((msg_node = (GF_XMLNode*)gf_list_enum(ifce_node->content, &j))) {
                                u32 k;
                                GF_XMLNode *par_node;
                                const char *msg_name, *action;
                                if (msg_node->type != GF_XML_NODE_TYPE) continue;
                                if (strcmp(msg_node->name, "messageIn") && strcmp(msg_node->name, "messageOut")) continue;

                                msg_name = wm_xml_get_attr(msg_node, "name");
                                if (!msg_name) continue;
                                GF_SAFEALLOC(msg, GF_WidgetMessage);
                                msg->name = gf_strdup(msg_name);
                                msg->params = gf_list_new();
                                msg->ifce = ifce;
                                if (!strcmp(msg_node->name, "messageOut")) msg->is_output = 1;

                                gf_list_add(ifce->messages, msg);

                                /*get inputAction*/
                                action = wm_xml_get_attr(msg_node, "inputAction");
                                if (action) {
                                        msg->input_action = wm_parse_pin(action, GF_WM_INPUT_ACTION, NULL, NULL, NULL);
                                }

                                /*get outputTrigger*/
                                action = wm_xml_get_attr(msg_node, "outputTrigger");
                                if (action) {
                                        msg->output_trigger = wm_parse_pin(action, GF_WM_OUTPUT_TRIGGER, NULL, NULL, NULL);
                                }

                                /*get params*/
                                k=0;
                                while ((par_node=gf_list_enum(msg_node->content, &k))) {
                                        GF_WidgetPin *wpin;
                                        u16 type;
                                        const char *par_name, *att_name;
                                        if (par_node->type != GF_XML_NODE_TYPE) continue;
                                        if (strcmp(par_node->name, "input") && strcmp(par_node->name, "output")) continue;


                                        par_name = wm_xml_get_attr(par_node, "name");
                                        /*invalid param, discard message*/
                                        if (!par_name) {
                                                gf_list_del_item(ifce->messages, msg);
                                                gf_list_del(msg->params);
                                                gf_free(msg->name);
                                                gf_free(msg);
                                                break;
                                        }

                                        if (!strcmp(par_node->name, "output")) {
                                                type = GF_WM_PARAM_OUTPUT;
                                                att_name = wm_xml_get_attr(par_node, "attributeModified");
                                        } else {
                                                type = GF_WM_PARAM_INPUT;
                                                att_name = wm_xml_get_attr(par_node, "setAttribute");
                                        }
                                        wpin = wm_parse_pin(att_name, type, par_name, wm_xml_get_attr(par_node, "scriptParamType"), wm_xml_get_attr(par_node, "default"));
                                        if (!wpin) continue;
                                        wpin->msg = msg;

                                        gf_list_add(msg->params, wpin);
                                }
                        }
                }
        }

        /*get all component elements*/
        i=0;
        while (root && (ifce_node = gf_list_enum(root->content, &i))) {
                GF_XMLNode *req_node;
                u32 j;
                const char *src, *id, *act;
                GF_WidgetComponent *comp;
                if (ifce_node->type != GF_XML_NODE_TYPE) continue;
                if (strcmp(ifce_node->name, "component")) continue;
                src = wm_xml_get_attr(ifce_node, "src");
                id = wm_xml_get_attr(ifce_node, "id");

                GF_SAFEALLOC(comp, GF_WidgetComponent);
                comp->required_interfaces = gf_list_new();
                comp->content = content;
                if (id) comp->id = gf_strdup(id);
                if (src) comp->src = gf_strdup(src);
                j=0;
                while ((req_node=gf_list_enum(ifce_node->content, &j))) {
                        const char *ifce_type;
                        if (req_node->type != GF_XML_NODE_TYPE) continue;
                        if (strcmp(req_node->name, "requiredInterface")) continue;
                        ifce_type = wm_xml_get_attr(req_node, "type");
                        if (!ifce_type) continue;
                        gf_list_add(comp->required_interfaces, gf_strdup(ifce_type));
                }

                act = wm_xml_get_attr(ifce_node, "activateTrigger");
                if (act) comp->activateTrigger = wm_parse_pin(act, GF_WM_ACTIVATE_TRIGGER, NULL, NULL, NULL);
                act = wm_xml_get_attr(ifce_node, "deactivateTrigger");
                if (act) comp->deactivateTrigger = wm_parse_pin(act, GF_WM_DEACTIVATE_TRIGGER, NULL, NULL, NULL);

                gf_list_add(content->components, comp);
        }

        /*clone global prefs for this content*/
        i=0;
        while ((pref = gf_list_enum(global_prefs, &i))) {
                GF_WidgetPreference *apref;
                GF_SAFEALLOC(apref, GF_WidgetPreference);
                apref->name = gf_strdup(pref->name);
                if (pref->value) apref->value = gf_strdup(pref->value);
                apref->flags = pref->flags;
                gf_list_add(content->preferences, apref);
        }

        context = wm_xml_find(root, ns_prefix, "contextConfiguration", NULL);
        if (context) {
                u32 j;
                /*get all preference elements*/
                i=0;
                while ((pref_node = gf_list_enum(context->content, &i))) {
                        const char *att;
                        if (pref_node->type != GF_XML_NODE_TYPE) continue;
                        if (strcmp(pref_node->name, "preferenceConnect")) continue;
                        att = wm_xml_get_attr(pref_node, "name");
                        if (!att) continue;

                        j=0;
                        while ((pref = gf_list_enum(content->preferences, &j))) {
                                if (!strcmp(pref->name, att) && !pref->connectTo) break;
                        }

                        if (!pref) {
                                GF_SAFEALLOC(pref, GF_WidgetPreference);
                                if (!pref) {
                                        GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[WidgetMan] Failed to allocate widget preference\n"));
                                        continue;
                                }

                                pref->name = gf_strdup(att);
                                gf_list_add(content->preferences, pref);
                        }
                        att = wm_xml_get_attr(pref_node, "value");
                        if (att) {
                                if (pref->value) gf_free(pref->value);
                                pref->value = gf_strdup(att);
                        }
                        att = wm_xml_get_attr(pref_node, "readOnly");
                        if (att && !strcmp(att, "true")) pref->flags |= GF_WM_PREF_READONLY;
                        att = wm_xml_get_attr(pref_node, "migratable");
                        if (att && !strcmp(att, "saveOnly")) pref->flags |= GF_WM_PREF_SAVE;
                        else if (att && !strcmp(att, "migrateOnly")) pref->flags |= GF_WM_PREF_MIGRATE;
                        else pref->flags |= GF_WM_PREF_SAVE | GF_WM_PREF_MIGRATE;

                        att = wm_xml_get_attr(pref_node, "connectTo");
                        if (att) pref->connectTo = wm_parse_pin(att, GF_WM_PREF_CONNECT, NULL, NULL, NULL);
                }

                att = wm_xml_get_attr(context, "savedAction");
                if (att) content->savedAction = wm_parse_pin(att, GF_WM_PREF_SAVEDACTION, NULL, NULL, NULL);
                att = wm_xml_get_attr(context, "restoredAction");
                if (att) content->restoredAction = wm_parse_pin(att, GF_WM_PREF_RESTOREDACTION, NULL, NULL, NULL);
                att = wm_xml_get_attr(context, "saveTrigger");
                if (att) content->saveTrigger = wm_parse_pin(att, GF_WM_PREF_SAVETRIGGER, NULL, NULL, NULL);
                att = wm_xml_get_attr(context, "restoreTrigger");
                if (att) content->restoreTrigger = wm_parse_pin(att, GF_WM_PREF_RESTORETRIGGER, NULL, NULL, NULL);
        }
}

/* Implements the W3C P&C rule for getting a single attribute value
   see http://www.w3.org/TR/widgets/#rule-for-getting-a-single-attribute-valu0
   should be different from getting normalized text but for now it's ok
   */
static char *wm_get_single_attribute(const char *input) {
        u32 i, j, len;
        char *output;
        Bool first_space_copied;

        if (!input) return gf_strdup("");

        len = (u32) strlen(input);
        output = gf_malloc(len+1);

        first_space_copied = 1;
        j = 0;
        for (i = 0; i<len; i++) {
                switch (input[i]) {
                case ' ':
                case '\t':
                case '\r':
                case '\n':
                        if (!first_space_copied) {
                                output[j] = ' ';
                                j++;
                                first_space_copied = 1;
                        }
                        break;
                default:
                        output[j] = input[i];
                        j++;
                        first_space_copied = 0;
                }
        }
        if (j && output[j-1] == ' ') output[j-1] = 0;
        output[j] = 0;
        return output;
}

/* Implements the W3C P&C rule for getting a single attribute value
   see http://www.w3.org/TR/widgets/#rule-for-getting-a-single-attribute-valu0 */
static u32 wm_parse_non_neg(const char *input) {
        u32 result = 0;
        if (strlen(input) && !strchr(input, '-')) {
                if (sscanf(input, "%u", &result) != 1) result = 0;
        }
        return result;
}

/* returns the localized concatenated text content
see http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#Node3-textContent
and http://www.w3.org/TR/widgets/#rule-for-getting-text-content0
*/
static char *wm_get_text_content(GF_XMLNode *node, char *inherited_locale, const char *user_locale) {
        if (node->type == GF_XML_TEXT_TYPE) {
                if (node->name) return gf_strdup(node->name);
                else return gf_strdup("");
        } else {
                char *xml_lang = (char *)wm_xml_get_attr(node, "xml:lang");
                if (!xml_lang) xml_lang = inherited_locale;
                /*
                if (xml_lang && user_locale && wm_check_language(xml_lang, user_locale))
                */
                {
                        u32 i, count;
                        char *text_content;
                        u32 text_content_len = 0;
                        text_content = gf_strdup("");
                        count = gf_list_count(node->content);
                        for (i=0; i<count; i++) {
                                GF_XMLNode *child = (GF_XMLNode *)gf_list_get(node->content, i);
                                char *child_content = wm_get_text_content(child, xml_lang, user_locale);
                                u32 child_content_len = (u32) strlen(child_content);
                                text_content = gf_realloc(text_content, text_content_len+child_content_len+1);
                                memcpy(text_content+text_content_len, child_content, child_content_len);
                                text_content[text_content_len+child_content_len] = 0;
                                text_content_len+=child_content_len;
                                gf_free(child_content);
                        }
                        return text_content;
                }
                /*
                } else {
                        return gf_strdup("");
                */
        }
}

static char *wm_get_normalized_text_content(GF_XMLNode *node, char *inherited_locale, const char *user_locale) {
        char *text_content, *result;
        /* first aggregate text content */
        text_content = wm_get_text_content(node, inherited_locale, user_locale);
        /* calling normalization of text content */
        result = wm_get_single_attribute(text_content);
        gf_free(text_content);
        return result;
}

/* When relocating resources over HTTP, we check if the resources for a given locale exists
  on the server by sending a HEAD message with an Accept-Language header. */
void wm_relocate_proc(void *usr_cbk, GF_NETIO_Parameter *parameter)
{
        switch (parameter->msg_type) {
        case GF_NETIO_GET_METHOD:
                parameter->name = "HEAD";
                break;
        default:
                return;
        }
}

/* relocate the given resource name (res_name) using registered relocators (e.g. locales, package folder)
   The result is the path of the file, possibly uncompressed, possibly localized.
   The parameter widget_path provides the path for converting relative resource paths into absolute paths.*/
static Bool wm_relocate_url(GF_WidgetManager *wm, const char *widget_path, const char *res_name, char *relocated_name, char *localized_res_name)
{
        Bool ok = 0;
        char *res_url;
        Bool result = gf_term_relocate_url(wm->term, res_name, widget_path, relocated_name, localized_res_name);
        if (result) return result;

        res_url = gf_url_concatenate(widget_path, res_name);

        /*try with HTTP HEAD */
        if (!strnicmp(widget_path, "http", 4)) {
                GF_Err e;
                /*fetch the remote widget manifest synchronously and load it */
                GF_DownloadSession *sess = gf_dm_sess_new(wm->term->downloader, (char *)res_url, GF_NETIO_SESSION_NOT_THREADED, wm_relocate_proc, NULL, &e);
                if (sess) {
                        e = gf_dm_sess_process(sess);
                        gf_dm_sess_del(sess);
                        if (e==GF_OK) {
                                const char *opt = gf_cfg_get_key(wm->term->user->config, "Systems", "Language2CC");
                                strcpy(relocated_name, res_url);
                                if (opt)
                                        sprintf(localized_res_name, "%s/%s", opt, res_name);
                                else
                                        strcpy(localized_res_name, res_name);
                                ok = 1;
                        }
                }
        }
        gf_free(res_url);
        return ok;
}

/* function that checks if the default start file exists in the widget (package or folder)
   according to the default start files table from the W3C P&C spec.
   The widget path attribute is used to get the associated relocator from the registered relocators */
static void wm_set_default_start_file(GF_WidgetManager *wm, GF_WidgetContent *content, const char *widget_path) {
        Bool result;
        char localized_path[GF_MAX_PATH], relocated_path[GF_MAX_PATH];
        char *mimetype = "text/html";
        result = wm_relocate_url(wm, widget_path, "index.htm", relocated_path, localized_path);
        if (result) {
                mimetype = "text/html";
        } else {
                result = wm_relocate_url(wm, widget_path, "index.html", relocated_path, localized_path);
                if (result) {
                        mimetype = "text/html";
                } else {
                        result = wm_relocate_url(wm, widget_path, "index.svg", relocated_path, localized_path);
                        if (result) {
                                mimetype = "image/svg+xml";
                        } else {
                                result = wm_relocate_url(wm, widget_path, "index.xhtml", relocated_path, localized_path);
                                if (result) {
                                        mimetype = "application/xhtml+xml";
                                } else {
                                        result = wm_relocate_url(wm, widget_path, "index.xht", relocated_path, localized_path);
                                        if (result) mimetype = "application/xhtml+xml";
                                }
                        }
                }
        }
        if (content->src) gf_free(content->src);
        content->src = gf_strdup(localized_path);
        if (content->relocated_src) gf_free(content->relocated_src);
        content->relocated_src = gf_strdup(relocated_path);
        if (content->mimetype) gf_free(content->mimetype);
        content->mimetype = gf_strdup(mimetype);
        if (content->encoding) gf_free(content->encoding);
        content->encoding = gf_strdup("utf-8");
}

static GF_WidgetContent *wm_add_icon(GF_Widget *widget, const char *icon_relocated_path, const char *icon_localized_path, const char *uri_fragment)
{
        GF_WidgetContent *icon;
        u32 i, count;
        Bool already_in = 0;

        count = gf_list_count(widget->icons);
        for (i =0; i<count; i++) {
                GF_WidgetContent *in_icon = gf_list_get(widget->icons, i);
                if (!strcmp(icon_localized_path, in_icon->src)) {
                        already_in = 1;
                        break;
                }
        }
        if (already_in) return NULL;

        GF_SAFEALLOC(icon, GF_WidgetContent);
        if (!icon) return NULL;
        
        if (uri_fragment) {
                icon->src = gf_malloc(strlen(icon_localized_path) + strlen(uri_fragment) + 1);
                if (icon->src) {
                        strcpy(icon->src, icon_localized_path);
                        strcat(icon->src, uri_fragment);
                }
                icon->relocated_src = gf_malloc(strlen(icon_relocated_path) + strlen(uri_fragment) + 1);
                if (icon->relocated_src) {
                        strcpy(icon->relocated_src, icon_relocated_path);
                        strcat(icon->relocated_src, uri_fragment);
                }
        } else {
                icon->src = gf_strdup(icon_localized_path);
                icon->relocated_src = gf_strdup(icon_relocated_path);
        }
        icon->interfaces = gf_list_new();
        icon->components = gf_list_new();
        icon->preferences = gf_list_new();
        icon->widget = widget;
        gf_list_add(widget->icons, icon);
        return icon;
}

/* Scans the W3C default icons table and add each icon */
static void wm_set_default_icon_files(GF_WidgetManager *wm, const char *widget_path, GF_Widget *widget) {
        char relocated_path[GF_MAX_PATH], localized_path[GF_MAX_PATH];
        Bool result;

        result = wm_relocate_url(wm, widget_path, "icon.svg", relocated_path, localized_path);
        if (result) wm_add_icon(widget, relocated_path, localized_path, NULL);

        result = wm_relocate_url(wm, widget_path, "icon.ico", relocated_path, localized_path);
        if (result) wm_add_icon(widget, relocated_path, localized_path, NULL);

        result = wm_relocate_url(wm, widget_path, "icon.png", relocated_path, localized_path);
        if (result) wm_add_icon(widget, relocated_path, localized_path, NULL);

        result = wm_relocate_url(wm, widget_path, "icon.gif", relocated_path, localized_path);
        if (result) wm_add_icon(widget, relocated_path, localized_path, NULL);

        result = wm_relocate_url(wm, widget_path, "icon.jpg", relocated_path, localized_path);
        if (result) wm_add_icon(widget, relocated_path, localized_path, NULL);
}

GF_WidgetInstance *wm_load_widget(GF_WidgetManager *wm, const char *path, u32 InstanceID, Bool skip_context)
{
        char szName[GF_MAX_PATH];
        u32 i, count;
        GF_Widget *widget = NULL;
        GF_WidgetInstance *wi = NULL;
        GF_XMLNode *root, *icon, *nmain, *name, *xml_node;
        GF_Err e;
        GF_DOMParser *dom = NULL;
        GF_WidgetPackage *wpackage = NULL;

        GF_DownloadSession *sess = NULL;
        const char *widget_ns_prefix = NULL;
        const char *mpegu_ns_prefix = NULL;
        const char *user_locale = gf_cfg_get_key(wm->term->user->config, "Systems", "Language2CC");

        /* Try to see if this widget is already loaded */
        e = GF_OK;
        count = gf_list_count(wm->widgets);
        for (i=0; i<count; i++) {
                widget = gf_list_get(wm->widgets, i);
                if (!strcmp(widget->url, path)) break;
                widget = NULL;
        }

        /*not found, retrieve the widget (if http), check the package if needed and parse the configuration document/widget manifest*/
        if (!widget) {
                Bool isDownloadedPackage = 0;

                /* path used to locate the widget (config.xml if unpackaged or zip/isoff package), potentially after download */
                const char *szLocalPath = path;
                /* used to locate the config document/widget manifest potentially after unzipping */
                const char *szManifestPath = path;
                /* */
                const char *szWidgetPath = path;
                const char *desc;
                GF_WidgetPreference *pref;
                GF_List *global_prefs;
                GF_WidgetContent *content;
                Bool correct_ns = 0;

                if (strstr(path, "http://")) {
                        /*fetch the remote widget manifest synchronously and load it */
                        sess = gf_dm_sess_new(wm->term->downloader, (char *)path, GF_NETIO_SESSION_NOT_THREADED, NULL, NULL, &e);
                        if (sess) {
                                e = gf_dm_sess_process(sess);
                                if (e==GF_OK) {
                                        szLocalPath = gf_dm_sess_get_cache_name(sess);

                                        if (gf_unzip_probe(szLocalPath)) {
                                                isDownloadedPackage = 1;
                                        } else {
                                                /* TODO ISOFF-based packaged widget */
                                        }
                                }
                        }
                        if (!sess || (e!=GF_OK) || !szLocalPath) goto exit;
                }

                /* Check if the widget package is a valid package and if it contains a config.xml file */
                szManifestPath = szLocalPath;

                wpackage = widget_package_new(wm, szLocalPath);
                if (wpackage) {
                        count = gf_list_count(wpackage->resources);
                        for (i=0; i<count; i++) {
                                GF_WidgetPackageResource *wu = gf_list_get(wpackage->resources, i);
                                /* According to W3C WPC, the config.xml file (lower case) shall only be located
                                   at the root of the package
                                   see http://www.w3.org/TR/widgets/#ta-dxzVDWpaWg */
                                if (!strcmp(wu->inner_path, "config.xml")) {
                                        szManifestPath = wu->extracted_path;
                                        break;
                                }
                        }
                        szWidgetPath = szManifestPath;
                }


                /* Parse the Widget Config Document as a DOM */
                dom = gf_xml_dom_new();
                e = gf_xml_dom_parse(dom, szManifestPath, NULL, NULL);
                if (e) goto exit;

                root = gf_xml_dom_get_root(dom);
                if (!root) goto exit;

                correct_ns = 0;
                count = gf_list_count(root->attributes);
                for (i=0; i<count; i++) {
                        GF_XMLAttribute *att = gf_list_get(root->attributes, i);
                        if (att->name) {
                                if (!strcmp(att->name, "xmlns")) {
                                        if (!strcmp(att->value, "http://www.w3.org/ns/widgets")) {
                                                correct_ns = 1;
                                        }
                                } else if (!strnicmp(att->name, "xmlns:", 6)) {
                                        if (!strcmp(att->value, "http://www.w3.org/ns/widgets")) {
                                                widget_ns_prefix = att->name+6;
                                                correct_ns = 1;
                                        } else if (!strcmp(att->value, "urn:mpeg:mpegu:schema:widgets:manifest:2010")) {
                                                mpegu_ns_prefix = att->name+6;
                                        }
                                }
                        }
                }
                /* According to the spec, wrong or no namespace means invalid widget
                see http://www.w3.org/TR/widgets/#ta-ACCJfDGwDQ */
                if (!correct_ns) goto exit;

                /* see http://www.w3.org/TR/widgets/#ta-ACCJfDGwDQ */
                if ((root->ns && (!widget_ns_prefix || strcmp(root->ns, widget_ns_prefix) || strcmp(root->name, "widget"))) ||
                        (!root->ns && (widget_ns_prefix || strcmp(root->name, "widget"))))
                        goto exit;


                /*pre-parse the root-level preference for use when parsing MPEG-U elements */
                global_prefs = gf_list_new();
                i=0;
                while ((nmain = gf_list_enum(root->content, &i))) {
                        const char *pname, *pvalue;
                        /* 'normalized' preference name and readonly*/
                        char *npname, *npro;
                        u32 i, count;
                        Bool pref_exists = 0;
                        Bool readOnly = 0;
                        if (nmain->type != GF_XML_NODE_TYPE) continue;
                        if (strcmp(nmain->name, "preference")) continue;
                        pname = wm_xml_get_attr(nmain, "name");
                        npname = wm_get_single_attribute(pname);
                        if (!npname || !strlen(npname)) continue;

                        count = gf_list_count(global_prefs);
                        for (i = 0; i < count; i++) {
                                GF_WidgetPreference *tmp_pref = gf_list_get(global_prefs, i);
                                if (!strcmp(tmp_pref->name, npname)) {
                                        pref_exists = 1;
                                        break;
                                }
                        }
                        if (pref_exists) continue;

                        pvalue = wm_xml_get_attr(nmain, "readonly");
                        npro = wm_get_single_attribute(pvalue);
                        if (npro && strlen(npro) && !strcmp(npro, "true")) readOnly=1;
                        if (npro) gf_free(npro);

                        pvalue = wm_xml_get_attr(nmain, "value");

                        GF_SAFEALLOC(pref, GF_WidgetPreference);
                        pref->name = npname;
                        if (pvalue) pref->value = wm_get_single_attribute(pvalue);
                        /*global preferences are save and migratable*/
                        pref->flags = GF_WM_PREF_SAVE | GF_WM_PREF_MIGRATE;
                        if (readOnly) pref->flags |= GF_WM_PREF_READONLY;
                        gf_list_add(global_prefs, pref);
                }

                /* get the content element from the XML Config document */
                GF_SAFEALLOC(content, GF_WidgetContent);
                if (!content) {
                        e = GF_OUT_OF_MEM;
                        goto exit;
                }
                content->interfaces = gf_list_new();
                content->components = gf_list_new();
                content->preferences = gf_list_new();
                nmain = wm_xml_find(root, widget_ns_prefix, "content", NULL);
                if (!nmain) {
                        /* if not found, use the default table of start files */
                        wm_set_default_start_file(wm, content, szWidgetPath);
                } else {
                        const char *src, *encoding, *mimetype;
                        src = wm_xml_get_attr(nmain, "src");

                        /*check the resource exists*/
                        if (src) {
                                Bool result;
                                char relocated_path[GF_MAX_PATH], localized_path[GF_MAX_PATH];
                                /*remove any existing fragment*/
                                char *sep = strchr(src, '#');
                                if (sep) sep[0] = 0;
                                result = wm_relocate_url(wm, szWidgetPath, src, relocated_path, localized_path);
                                if (sep) sep[0] = '#';
                                if (result) {
                                        content->relocated_src = gf_strdup(relocated_path);
                                        content->src = gf_strdup(localized_path);
                                }
                        }

                        encoding = wm_xml_get_attr(nmain, "encoding");
                        if (encoding && strlen(encoding)) content->encoding = wm_get_single_attribute(encoding);
                        else content->encoding = gf_strdup("utf-8");

                        mimetype = wm_xml_get_attr(nmain, "type");
                        if (mimetype && strlen(mimetype)) {
                                char *sep = strchr(mimetype, ';');
                                if (sep) sep[0] = 0;
                                content->mimetype = wm_get_single_attribute(mimetype);
                                if (sep) sep[0] = ';';
                        }
                        else content->mimetype = gf_strdup("text/html");

                        if (!content->relocated_src) wm_set_default_start_file(wm, content, szWidgetPath);
                }
                if (strlen(content->relocated_src) == 0) {
                        gf_list_del(content->interfaces);
                        gf_list_del(content->components);
                        gf_list_del(content->preferences);
                        gf_free(content);
                        content = NULL;
                        goto exit;
                }
                /* We need to call the parse of the MPEG-U elements to clone the global preferences into widget preferences,
                   this should probably be changed to extract the clone from that function */
                wm_parse_mpegu_content_element(content, nmain, mpegu_ns_prefix, global_prefs);

                GF_SAFEALLOC(widget, GF_Widget);
                if (!widget) {
                        e = GF_OUT_OF_MEM;
                        goto exit;
                }
                widget->url = gf_strdup(path);
                widget->manifest_path = gf_strdup(szManifestPath);
                if (isDownloadedPackage) widget->local_path = gf_strdup(szLocalPath);
                widget->wm = wm;
                widget->main = content;
                content->widget = widget;
                widget->icons = gf_list_new();
                widget->features = gf_list_new();
                if (wpackage) {
                        widget->wpack = wpackage;
                        wpackage->widget = widget;
                        /*attach downloader to our package to avoid destroying the cache file*/
                        wpackage->sess = sess;
                        sess = NULL;
                }

                /*check for icon - can be optional*/
                i=0;
                while ((icon = gf_list_enum(root->content, &i))) {
                        if (icon->type==GF_XML_NODE_TYPE &&
                                icon->name && !strcmp(icon->name, "icon") &&
                                ((!widget_ns_prefix && !icon->ns) || !strcmp(widget_ns_prefix, icon->ns))) {
                                char *sep;
                                char relocated[GF_MAX_PATH], localized_path[GF_MAX_PATH];
                                char *icon_width, *icon_height;
                                GF_WidgetContent *iconic;
                                Bool result;

                                char *pname = (char *)wm_xml_get_attr(icon, "src");
                                if (!pname || !strlen(pname)) continue;

                                /*remove any existing fragment*/
                                sep = strchr(pname, '#');
                                if (sep) sep[0] = 0;
                                result = wm_relocate_url(wm, szWidgetPath, pname, relocated, localized_path);
                                if (sep) sep[0] = '#';
                                if (!result) continue;

                                iconic = wm_add_icon(widget, relocated, localized_path, sep);
                                if (iconic) {
                                        wm_parse_mpegu_content_element(iconic, icon, mpegu_ns_prefix, global_prefs);
                                        icon_width = (char *)wm_xml_get_attr(icon, "width");
                                        if (icon_width) iconic->width = wm_parse_non_neg(icon_width);
                                        icon_height = (char *)wm_xml_get_attr(icon, "height");
                                        if (icon_height) iconic->height = wm_parse_non_neg(icon_height);
                                }
                        }
                }
                /* after processing the icon elements (wether there are valid icons or not), we use the default icon table
                  see http://dev.w3.org/2006/waf/widgets/test-suite/test-cases/ta-FAFYMEGELU/004/ */
                wm_set_default_icon_files(wm, szWidgetPath, widget);

                /*delete the root-level preference*/
                i=0;
                while ((pref = gf_list_enum(global_prefs , &i))) {
                        if (pref->value) gf_free(pref->value);
                        gf_free(pref->name);
                        gf_free(pref);
                }
                gf_list_del(global_prefs);

                /*check for optional meta data*/
                name = wm_xml_find(root, widget_ns_prefix, "name", user_locale);
                if (name) {
                        const char *shortname = wm_xml_get_attr(name, "short");
                        widget->shortname = wm_get_single_attribute(shortname);

                        widget->name = wm_get_normalized_text_content(name, NULL, user_locale);
                }

                desc = wm_xml_get_attr(root, "id");
                if (desc) {
                        /* TODO check if this is a valid IRI, for the moment, just hack to pass the test, check for ':'
                          see http://dev.w3.org/2006/waf/widgets/test-suite/test-cases/ta-RawAIWHoMs/ */
                        if (strchr(desc, ':'))
                                widget->identifier = wm_get_single_attribute(desc);
                }

                desc = wm_xml_get_attr(root, "width");
                if (desc) {
                        widget->width = wm_parse_non_neg(desc);
                }
                desc = wm_xml_get_attr(root, "height");
                if (desc) {
                        widget->height = wm_parse_non_neg(desc);
                }

                name = wm_xml_find(root, widget_ns_prefix, "description", user_locale);
                if (name) {
                        widget->description = wm_get_normalized_text_content(name, NULL, user_locale);
                }

                name = wm_xml_find(root, widget_ns_prefix, "license", user_locale);
                if (name) {
                        const char *href = wm_xml_get_attr(name, "href");
                        widget->licenseHref = wm_get_single_attribute(href);

                        /* Warning the license text content should not be normalized */
                        widget->license = wm_get_text_content(name, NULL, user_locale);
                }

                desc = wm_xml_get_attr(root, "version");
                if (desc) widget->version = wm_get_single_attribute(desc);

                desc = wm_xml_get_attr(root, "uuid");
                if (desc) widget->uuid = gf_strdup(desc);

                desc = wm_xml_get_attr(root, "discardable");
                if (desc) widget->discardable = !strcmp(desc, "true") ? 1 : 0;

                desc = wm_xml_get_attr(root, "multipleInstances");
                if (desc) widget->multipleInstance = !strcmp(desc, "true") ? 1 : 0;

                name = wm_xml_find(root, widget_ns_prefix, "author", NULL);
                if (name) {
                        desc = wm_xml_get_attr(name, "href");
                        if (desc && strchr(desc, ':')) widget->authorHref = wm_get_single_attribute(desc);

                        desc = wm_xml_get_attr(name, "email");
                        widget->authorEmail = wm_get_single_attribute(desc);

                        widget->authorName = wm_get_normalized_text_content(name, NULL, user_locale);
                }

                i=0;
                while ((xml_node = gf_list_enum(root->content, &i))) {
                        if (xml_node->type==GF_XML_NODE_TYPE &&
                                xml_node->name && !strcmp(xml_node->name, "feature") &&
                                ((!widget_ns_prefix && !xml_node->ns) || !strcmp(widget_ns_prefix, xml_node->ns))) {

                                u32 i, count;
                                Bool already_in = 0;
                                GF_WidgetFeature *feat;
                                const char *feature_name, *req_att;
                                char *nfname;
                                Bool required = 1;
                                u32 j;
                                GF_XMLNode *param_node;

                                feature_name = (char *)wm_xml_get_attr(xml_node, "name");
                                if (!feature_name || !strlen(feature_name) || !strchr(feature_name, ':')) continue;
                                nfname = wm_get_single_attribute(feature_name);

                                req_att = (char *)wm_xml_get_attr(xml_node, "required");
                                if (req_att && !strcmp(req_att, "false")) required = 0;

                                count = gf_list_count(widget->features);
                                for (i = 0; i<count; i++) {
                                        GF_WidgetFeature *tmp = gf_list_get(widget->features, i);
                                        if (!strcmp(nfname, tmp->name)) {
                                                already_in = 1;
                                                break;
                                        }
                                }
                                if (already_in) continue;

                                GF_SAFEALLOC(feat, GF_WidgetFeature);
                                if (!feat) {
                                        e = GF_OUT_OF_MEM;
                                        goto exit;
                                }
                                feat->name = nfname;
                                feat->required = required;
                                feat->params = gf_list_new();
                                gf_list_add(widget->features, feat);

                                j = 0;
                                while ((param_node = gf_list_enum(xml_node->content, &j))) {
                                        if (param_node->type==GF_XML_NODE_TYPE &&
                                                param_node->name && !strcmp(param_node->name, "param") &&
                                                ((!widget_ns_prefix && !param_node->ns) || !strcmp(widget_ns_prefix, param_node->ns))) {
                                                GF_WidgetFeatureParam *wfp;
                                                const char *param_name, *param_value;
                                                char *npname, *npvalue;

                                                param_name = (char *)wm_xml_get_attr(param_node, "name");
                                                npname = wm_get_single_attribute(param_name);
                                                if (!strlen(npname)) continue;

                                                param_value = (char *)wm_xml_get_attr(param_node, "value");
                                                npvalue = wm_get_single_attribute(param_value);
                                                if (!strlen(npvalue)) {
                                                        gf_free(npname);
                                                        continue;
                                                }

                                                GF_SAFEALLOC(wfp, GF_WidgetFeatureParam);
                                                if (!wfp) {
                                                        e = GF_OUT_OF_MEM;
                                                        goto exit;
                                                }

                                                wfp->name = npname;
                                                wfp->value = npvalue;
                                                gf_list_add(feat->params, wfp);

                                        }
                                }

                        }
                }

                gf_list_add(wm->widgets, widget);
        }

        GF_SAFEALLOC(wi, GF_WidgetInstance);
        if (!wi) {
                e = GF_OUT_OF_MEM;
                goto exit;
        }
        
        wi->widget = widget;
        wi->bound_ifces = gf_list_new();
        wi->output_triggers = gf_list_new();
        wi->components = gf_list_new();
        widget->nb_instances++;
        wi->instance_id = InstanceID;
        wi->permanent = 1;

        if (!InstanceID) {
                char szInst[20];

                count = gf_list_count(wm->widget_instances);
                for (i=0; i<count; i++) {
                        GF_WidgetInstance *awi = gf_list_get(wm->widget_instances, i);
                        if (awi->widget == wi->widget)
                                wi->instance_id = awi->instance_id;
                }
                wi->instance_id ++;

                sprintf(szName, "%s#%s#Instance%d", path, wi->widget->name, wi->instance_id);
                sprintf((char *)wi->secname, "Widget#%08X", gf_crc_32(szName, (u32) strlen(szName)));

                /*create section*/
                gf_cfg_set_key(wm->term->user->config, "Widgets", (const char *) wi->secname, " ");
                gf_cfg_set_key(wm->term->user->config, (const char *) wi->secname, "WM:Manifest", wi->widget->url);
                sprintf(szInst, "%d", wi->instance_id);
                gf_cfg_set_key(wm->term->user->config, (const char *) wi->secname, "WM:InstanceID", szInst);
        }
        gf_list_add(wm->widget_instances, wi);


        if (!skip_context && strstr(path, "http://")) {
                GF_XMLNode *context;
                GF_DownloadSession *ctx_sess = NULL;
                char *ctxPath;
                context = NULL;

                /*fetch the remote widget context synchronously and load it */
                ctxPath = gf_malloc(sizeof(char) * (strlen(path) + 1 + 15/*?mpeg-u-context*/));
                strcpy(ctxPath, path);
                if ((strchr(path, '?') == NULL) && (strstr(path, "%3f")==NULL) && (strstr(path, "%3F")==NULL) ) {
                        strcat(ctxPath, "?mpeg-u-context");
                } else {
                        strcat(ctxPath, "&mpeg-u-context");
                }

                /*try to fetch the associated context*/
                ctx_sess = gf_dm_sess_new(wm->term->downloader, (char *)ctxPath, GF_NETIO_SESSION_NOT_THREADED, NULL, NULL, &e);
                if (ctx_sess) {
                        e = gf_dm_sess_process(ctx_sess);
                        if (e==GF_OK) {
                                wi->mpegu_context = gf_xml_dom_new();
                                e = gf_xml_dom_parse(wi->mpegu_context , gf_dm_sess_get_cache_name(ctx_sess), NULL, NULL);
                                if (!e) {
                                        context = gf_xml_dom_get_root(wi->mpegu_context);
                                        if (strcmp(context->name, "contextInformation")) context = NULL;
                                }
                        }
                        gf_dm_sess_del(ctx_sess);
                        e = GF_OK;
                }
                gf_free(ctxPath);
                ctxPath = NULL;

                if (!context && wi->mpegu_context) {
                        gf_xml_dom_del(wi->mpegu_context);
                        wi->mpegu_context = NULL;
                }

        }

exit:
        if (dom) gf_xml_dom_del(dom);
        if (sess) gf_dm_sess_del(sess);
        if (e || !wi) {
                if (wi) wm_delete_widget_instance(wm, wi);
                else {
                        if (wpackage) widget_package_del(wm, wpackage);
                }
                return NULL;
        }
        return wi;
}


static Bool wm_enum_widget(void *cbk, char *file_name, char *file_path, GF_FileEnumInfo *file_info)
{
        GF_WidgetInstance *wid;
        GF_WidgetManager *wm = (GF_WidgetManager *)cbk;
        wid = wm_load_widget(wm, file_path, 0, 0);
        if (wid) {
                wm_widget_jsbind(wm, wid);
                /*remove section info*/
                gf_cfg_del_section(wm->term->user->config, (const char *) wid->secname);
                gf_cfg_set_key(wm->term->user->config, "Widgets", (const char *) wid->secname, NULL);
        }
        return 0;
}

static Bool wm_enum_dir(void *cbk, char *file_name, char *file_path, GF_FileEnumInfo *file_info)
{
        return (gf_enum_directory(file_path, 0, wm_enum_widget, cbk, "mgt")==GF_OK) ? GF_FALSE : GF_TRUE;
}


static JSBool SMJS_FUNCTION(wm_initialize)
{
        u32 i, count;
        const char*opt;
        SMJS_OBJ
        //SMJS_ARGS
        GF_WidgetManager *wm = (GF_WidgetManager *)SMJS_GET_PRIVATE(c, obj);

        count = gf_cfg_get_key_count(wm->term->user->config, "Widgets");
        for (i=0; i<count; i++) {
                const char *name = gf_cfg_get_key_name(wm->term->user->config, "Widgets", i);
                /*this is a previously loaded widgets - reload it*/
                if (!strnicmp(name, "Widget#", 7)) {
                        const char *manifest = gf_cfg_get_key(wm->term->user->config, name, "WM:Manifest");
                        if (manifest) {
                                const char *ID = gf_cfg_get_key(wm->term->user->config, name, "WM:InstanceID");
                                u32 instID = ID ? atoi(ID) : 0;
                                GF_WidgetInstance *wi = wm_load_widget(wm, manifest, instID, 0);
                                if (wi) {
                                        strcpy((char *)wi->secname, (const char *) name);
                                        wm_widget_jsbind(wm, wi);
                                }
                        }
                }
        }

        opt = gf_cfg_get_key(wm->term->user->config, "Widgets", "WidgetStore");
        if (opt) gf_enum_directory(opt, 1, wm_enum_dir, wm, NULL);

        return JS_TRUE;
}

static void widgetmanager_load(GF_JSUserExtension *jsext, GF_SceneGraph *scene, JSContext *c, JSObject *global, Bool unload)
{
        GF_WidgetManager *wm;

        GF_JSAPIParam par;
        JSPropertySpec wmClassProps[] = {
                SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0)
        };
        JSFunctionSpec wmClassFuncs[] = {
                SMJS_FUNCTION_SPEC("initialize", wm_initialize, 0),
                SMJS_FUNCTION_SPEC("load", wm_load, 2),
                SMJS_FUNCTION_SPEC("unload", wm_unload, 1),
                SMJS_FUNCTION_SPEC("get", wm_get, 1),
                SMJS_FUNCTION_SPEC("findByInterface", wm_find_interface, 1),
                SMJS_FUNCTION_SPEC(0, 0, 0)
        };

        wm = jsext->udta;
        /*widget manager is only loaded once*/
        if (wm->ctx && (wm->ctx != c)) {
                /*load the 'Widget' object in the global scope*/
                widget_load(wm, scene, c, global, unload);
                return;
        }

        /*unload widgets*/
        if (unload) {
                if (wm->obj) {
                        gf_js_remove_root(wm->ctx, &wm->obj, GF_JSGC_OBJECT);
                        wm->obj = NULL;
                }

                while (gf_list_count(wm->widget_instances)) {
                        GF_WidgetInstance *widg = gf_list_get(wm->widget_instances, 0);
                        wm_delete_widget_instance(wm, widg);
                }
                wm->ctx = NULL;
                return;
        }
        wm->ctx = c;

        if (!scene) return;

        /*setup JS bindings*/
        JS_SETUP_CLASS(wm->widmanClass, "WIDGETMANAGER", JSCLASS_HAS_PRIVATE, wm_getProperty, wm_setProperty, JS_FinalizeStub);

        GF_JS_InitClass(c, global, 0, &wm->widmanClass, 0, 0, wmClassProps, wmClassFuncs, 0, 0);
        wm->obj = JS_DefineObject(c, global, "WidgetManager", &wm->widmanClass._class, 0, 0);
        SMJS_SET_PRIVATE(c, wm->obj, wm);
        gf_js_add_root(c, &wm->obj, GF_JSGC_OBJECT);


        {
                JSPropertySpec wmWidgetClassProps[] = {
                        SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0)
                };
                JSFunctionSpec wmWidgetClassFuncs[] = {
                        SMJS_FUNCTION_SPEC("activate", wm_widget_activate, 1),
                        SMJS_FUNCTION_SPEC("deactivate", wm_widget_deactivate, 0),
                        SMJS_FUNCTION_SPEC("get_interface", wm_widget_get_interface, 1),
                        SMJS_FUNCTION_SPEC("bind_output_trigger", wm_widget_bind_output_trigger, 2),
                        SMJS_FUNCTION_SPEC("set_input", wm_widget_set_input, 2),
                        SMJS_FUNCTION_SPEC("bind_interface", wm_widget_bind_interface, 2),
                        SMJS_FUNCTION_SPEC("unbind_interface", wm_widget_unbind_interface, 1),
                        SMJS_FUNCTION_SPEC("call_input_action", wm_widget_call_input_action, 1),
                        SMJS_FUNCTION_SPEC("call_input_script", wm_widget_call_input_script, 2),
                        SMJS_FUNCTION_SPEC("is_interface_bound", wm_widget_is_interface_bound, 1),
                        SMJS_FUNCTION_SPEC("get_param_value", wm_widget_get_param_value, 1),
                        SMJS_FUNCTION_SPEC("get_context", wm_widget_get_context, 0),
                        SMJS_FUNCTION_SPEC("get_component", wm_widget_get_component, 2),

                        SMJS_FUNCTION_SPEC(0, 0, 0)
                };
                /*setup JS bindings*/
                JS_SETUP_CLASS(wm->wmWidgetClass, "WMWIDGET", JSCLASS_HAS_PRIVATE, wm_widget_getProperty, wm_widget_setProperty, JS_FinalizeStub);
                GF_JS_InitClass(c, global, 0, &wm->wmWidgetClass, 0, 0, wmWidgetClassProps, wmWidgetClassFuncs, 0, 0);

                JS_SETUP_CLASS(wm->widgetAnyClass, "WIDGETANY", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub_forSetter, JS_FinalizeStub);
                GF_JS_InitClass(c, global, 0, &wm->widgetAnyClass, 0, 0, 0, 0, 0, 0);
        }

        JS_SETUP_CLASS(wm->widgetClass, "MPEGWidget", JSCLASS_HAS_PRIVATE, widget_getProperty, widget_setProperty, JS_FinalizeStub);

        if (scene->script_action) {
                if (!scene->script_action(scene->script_action_cbck, GF_JSAPI_OP_GET_TERM, scene->RootNode, &par))
                        return;
                wm->term = par.term;
        }

}


static GF_JSUserExtension *gwm_new()
{
        GF_JSUserExtension *dr;
        GF_WidgetManager *wm;
        GF_SAFEALLOC(dr, GF_JSUserExtension);
        if (!dr) return NULL;
        GF_REGISTER_MODULE_INTERFACE(dr, GF_JS_USER_EXT_INTERFACE, "WidgetManager JavaScript Bindings", "gpac distribution");

        GF_SAFEALLOC(wm, GF_WidgetManager);
        if (!wm) {
                gf_free(dr);
                return NULL;
        }
        wm->widget_instances = gf_list_new();
        wm->widgets = gf_list_new();
        dr->load = widgetmanager_load;
        dr->udta = wm;
        return dr;
}


static void gwm_delete(GF_BaseInterface *ifce)
{
        GF_WidgetManager *wm;
        GF_JSUserExtension *dr = (GF_JSUserExtension *) ifce;
        if (!dr)
                return;
        wm = dr->udta;
        if (!wm)
                return;
        if (wm->widget_instances)
                gf_list_del(wm->widget_instances);
        wm->widget_instances = NULL;
        if (wm->widgets)
                gf_list_del(wm->widgets);
        wm->widgets = NULL;
        gf_free(wm);
        dr->udta = NULL;
        gf_free(dr);
}
#endif


GPAC_MODULE_EXPORT
const u32 *QueryInterfaces()
{
        static u32 si [] = {
#ifdef GPAC_HAS_SPIDERMONKEY
                GF_JS_USER_EXT_INTERFACE,
#ifndef GPAC_DISABLE_SVG
                GF_SCENE_DECODER_INTERFACE,
#endif

#endif
                0
        };
        return si;
}

GPAC_MODULE_EXPORT
GF_BaseInterface *LoadInterface(u32 InterfaceType)
{
#ifdef GPAC_HAS_SPIDERMONKEY
        if (InterfaceType == GF_JS_USER_EXT_INTERFACE) return (GF_BaseInterface *)gwm_new();
#ifndef GPAC_DISABLE_SVG
        else if (InterfaceType == GF_SCENE_DECODER_INTERFACE) return (GF_BaseInterface *)LoadWidgetReader();
#endif
#endif
        return NULL;
}

GPAC_MODULE_EXPORT
void ShutdownInterface(GF_BaseInterface *ifce)
{
        switch (ifce->InterfaceType) {
#ifdef GPAC_HAS_SPIDERMONKEY
        case GF_JS_USER_EXT_INTERFACE:
                gwm_delete(ifce);
                break;
#ifndef GPAC_DISABLE_SVG
        case GF_SCENE_DECODER_INTERFACE:
                ShutdownWidgetReader(ifce);
                break;
#endif

#endif
        }
}


GPAC_MODULE_STATIC_DECLARATION( widgetman )

/* [<][>][^][v][top][bottom][index][help] */