modules/widgetman/widgetman.c (3,006 lines of code) (raw):

//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); 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%08X", path, (unsigned int)((unsigned long) 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); 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); 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); 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%08X", path, (unsigned int)((unsigned long) 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); 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); 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); 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); 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); 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; } } /* 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 (uri_fragment) { icon->src = gf_malloc(strlen(icon_localized_path) + strlen(uri_fragment) + 1); strcpy(icon->src, icon_localized_path); strcat(icon->src, uri_fragment); icon->relocated_src = gf_malloc(strlen(icon_relocated_path) + strlen(uri_fragment) + 1); 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); 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); 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 optionnal 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); 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); wfp->name = npname; wfp->value = npvalue; gf_list_add(feat->params, wfp); } } } } gf_list_add(wm->widgets, widget); } GF_SAFEALLOC(wi, GF_WidgetInstance); 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; dr = gf_malloc(sizeof(GF_JSUserExtension)); memset(dr, 0, sizeof(GF_JSUserExtension)); GF_REGISTER_MODULE_INTERFACE(dr, GF_JS_USER_EXT_INTERFACE, "WidgetManager JavaScript Bindings", "gpac distribution"); GF_SAFEALLOC(wm, GF_WidgetManager); 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 )