| //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 | |
| // | |
| ///////////////////////////////////////////////////////////////////////////////// | |
| #include "widgetman.h" | |
| #ifdef GPAC_HAS_SPIDERMONKEY | |
| JSBool SMJS_FUNCTION(widget_has_feature) | |
| { | |
| char *feat_name; | |
| SMJS_OBJ | |
| SMJS_ARGS | |
| GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj); | |
| if (!wid) return JS_FALSE; | |
| if (!argc || !JSVAL_IS_STRING(argv[0])) return JS_TRUE; | |
| feat_name = SMJS_CHARS(c, argv[0]); | |
| SMJS_SET_RVAL( BOOLEAN_TO_JSVAL( JS_FALSE ) ); | |
| if (!strcmp(feat_name, "urn:mpeg:systems:mpeg-u:2009")) SMJS_SET_RVAL( BOOLEAN_TO_JSVAL( JS_TRUE )); | |
| SMJS_FREE(c, feat_name); | |
| return JS_TRUE; | |
| } | |
| JSBool SMJS_FUNCTION(widget_open_url) | |
| { | |
| GF_Event evt; | |
| SMJS_OBJ | |
| SMJS_ARGS | |
| GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj); | |
| if (!wid || !argc || !JSVAL_IS_STRING(argv[0])) return JS_TRUE; | |
| memset(&evt, 0, sizeof(GF_Event)); | |
| evt.type = GF_EVENT_NAVIGATE; | |
| evt.navigate.to_url = SMJS_CHARS(c, argv[0]); | |
| gf_term_send_event(wid->widget->wm->term, &evt); | |
| SMJS_FREE(c, (char *)evt.navigate.to_url); | |
| return JS_TRUE; | |
| } | |
| JSBool SMJS_FUNCTION(widget_get_attention) | |
| { | |
| jsval fval; | |
| SMJS_OBJ | |
| GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj); | |
| if (!wid) return JS_TRUE; | |
| if ((JS_LookupProperty(c, wid->widget->wm->obj, "getAttention", &fval)==JS_TRUE) && JSVAL_IS_OBJECT(fval)) { | |
| jsval args[1]; | |
| args[0] = OBJECT_TO_JSVAL(wid->obj); | |
| JS_CallFunctionValue(c, wid->widget->wm->obj, fval, 1, args, SMJS_GET_RVAL); | |
| } | |
| return JS_TRUE; | |
| } | |
| JSBool SMJS_FUNCTION(widget_show_notification) | |
| { | |
| jsval fval; | |
| SMJS_OBJ | |
| SMJS_ARGS | |
| GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj); | |
| if (!wid) return JS_TRUE; | |
| if ((JS_LookupProperty(c, wid->widget->wm->obj, "showNotification", &fval)==JS_TRUE) && JSVAL_IS_OBJECT(fval)) { | |
| jsval *vars; | |
| u32 i; | |
| vars = (jsval*)gf_malloc(sizeof(jsval)*(argc+1)); | |
| vars[0] = OBJECT_TO_JSVAL(wid->obj); | |
| for (i=0; i<argc; i++) | |
| vars[i+1] = argv[i]; | |
| JS_CallFunctionValue(c, wid->widget->wm->obj, fval, argc+1, vars, SMJS_GET_RVAL); | |
| } | |
| return JS_TRUE; | |
| } | |
| static JSBool SMJS_FUNCTION(widget_call_message_reply_callback) | |
| { | |
| JSObject *list; | |
| jsval *vals, fval; | |
| u32 i, count; | |
| SMJS_OBJ | |
| SMJS_ARGS | |
| GF_WidgetMessage *msg = SMJS_GET_PRIVATE(c, obj); | |
| if (!msg || !argc || !JSVAL_IS_OBJECT(argv[0]) ) return JS_FALSE; | |
| if ((JS_LookupProperty(c, obj, "replyCallback", &fval)==JS_TRUE) && JSVAL_IS_OBJECT(fval)) { | |
| list = JSVAL_TO_OBJECT(argv[0]); | |
| JS_GetArrayLength(c, list, (jsuint*) &count); | |
| vals = (jsval*)gf_malloc(sizeof(jsval)*(count+1)); | |
| vals[0] = OBJECT_TO_JSVAL(obj); | |
| for (i=0; i<count; i++) { | |
| JS_GetElement(c, list, (jsint) i, &vals[i+1]); | |
| } | |
| JS_CallFunctionValue(c, obj, fval, count, vals, SMJS_GET_RVAL); | |
| gf_free(vals); | |
| } | |
| return JS_TRUE; | |
| } | |
| static JSBool SMJS_FUNCTION(widget_message_handler_factory) | |
| { | |
| char *msg_name; | |
| u32 i, count; | |
| SMJS_OBJ | |
| SMJS_ARGS | |
| GF_WidgetInterfaceInstance *bifce = (GF_WidgetInterfaceInstance *)SMJS_GET_PRIVATE(c, obj); | |
| if (!bifce) return JS_FALSE; | |
| if (!argc) return JS_FALSE; | |
| if (!JSVAL_IS_STRING(argv[0])) return JS_FALSE; | |
| msg_name = SMJS_CHARS(c, argv[0]); | |
| if (!msg_name ) return JS_FALSE; | |
| SMJS_SET_RVAL( JSVAL_NULL ); | |
| count = gf_list_count(bifce->ifce->messages); | |
| for (i=0; i<count; i++) { | |
| GF_WidgetMessage *msg = (GF_WidgetMessage*)gf_list_get(bifce->ifce->messages, i); | |
| if (!strcmp(msg->name, msg_name)) { | |
| JSObject *an_obj = JS_NewObject(c, &bifce->wid->widget->wm->widgetAnyClass._class, 0, 0); | |
| SMJS_SET_PRIVATE(c, an_obj, msg); | |
| JS_DefineProperty(c, an_obj, "msgName", STRING_TO_JSVAL( JS_NewStringCopyZ(c, msg->name) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); | |
| JS_DefineProperty(c, an_obj, "interfaceHandler", OBJECT_TO_JSVAL( obj ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); | |
| JS_DefineFunction(c, an_obj, "onInvokeReply", widget_call_message_reply_callback, 1, 0); | |
| if ((argc==2) && JSVAL_IS_OBJECT(argv[1]) && !JSVAL_IS_NULL(argv[1])) | |
| JS_DefineProperty(c, an_obj, "replyCallback", argv[1], 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); | |
| SMJS_SET_RVAL( OBJECT_TO_JSVAL(an_obj) ); | |
| } | |
| } | |
| SMJS_FREE(c, msg_name); | |
| return JS_TRUE; | |
| } | |
| static JSBool SMJS_FUNCTION(widget_invoke_message) | |
| { | |
| jsval oval; | |
| GF_WidgetMessage *msg = NULL; | |
| SMJS_OBJ | |
| SMJS_ARGS | |
| GF_WidgetInterfaceInstance *bifce = (GF_WidgetInterfaceInstance *)SMJS_GET_PRIVATE(c, obj); | |
| if (!bifce) return JS_FALSE; | |
| SMJS_SET_RVAL( JSVAL_NULL ); | |
| if (!JSVAL_IS_OBJECT(argv[0])) return JS_FALSE; | |
| if (JSVAL_IS_NULL(argv[0])) return JS_FALSE; | |
| msg = (GF_WidgetMessage *)SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[0]) ); | |
| if (!msg) return JS_FALSE; | |
| /*look for JS Callback "invoke" in the widget manager script*/ | |
| if (JS_LookupProperty(c, bifce->ifce->obj, "invoke", &oval)==JS_TRUE) { | |
| if (JSVAL_IS_OBJECT(oval)) { | |
| JS_CallFunctionValue(bifce->wid->widget->wm->ctx, bifce->ifce->obj, oval, argc, argv, SMJS_GET_RVAL ); | |
| } | |
| } | |
| return JS_TRUE; | |
| } | |
| static JSBool SMJS_FUNCTION(widget_invoke_message_reply) | |
| { | |
| jsval oval; | |
| SMJS_OBJ | |
| SMJS_ARGS | |
| GF_WidgetMessage *msg = NULL; | |
| GF_WidgetInterfaceInstance *bifce = (GF_WidgetInterfaceInstance *)SMJS_GET_PRIVATE(c, obj); | |
| if (!bifce) return JS_FALSE; | |
| SMJS_SET_RVAL( JSVAL_NULL ); | |
| if (!JSVAL_IS_OBJECT(argv[0])) return JS_FALSE; | |
| if (JSVAL_IS_NULL(argv[0])) return JS_FALSE; | |
| msg = (GF_WidgetMessage *)SMJS_GET_PRIVATE(c, JSVAL_TO_OBJECT(argv[0]) ); | |
| if (!msg) return JS_FALSE; | |
| /*look for JS Callback "invokeReply" in the widget manager script*/ | |
| if (JS_LookupProperty(c, bifce->ifce->obj, "invokeReply", &oval)==JS_TRUE) { | |
| if (JSVAL_IS_OBJECT(oval)) { | |
| JS_CallFunctionValue(bifce->wid->widget->wm->ctx, bifce->ifce->obj, oval, argc, argv, SMJS_GET_RVAL); | |
| } | |
| } | |
| return JS_TRUE; | |
| } | |
| static void widget_interface_js_bind(JSContext *c, GF_WidgetInterfaceInstance *ifce) | |
| { | |
| if (!ifce->obj) { | |
| ifce->obj = JS_NewObject(c, &ifce->wid->widget->wm->widgetAnyClass._class, 0, 0); | |
| SMJS_SET_PRIVATE(c, ifce->obj, ifce); | |
| gf_js_add_root(c, &ifce->obj, GF_JSGC_OBJECT); | |
| JS_DefineProperty(c, ifce->obj, "type", STRING_TO_JSVAL( JS_NewStringCopyZ(c, ifce->ifce->type) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); | |
| JS_DefineProperty(c, ifce->obj, "bound", STRING_TO_JSVAL( JS_NewStringCopyZ(c, ifce->hostname) ), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT); | |
| JS_DefineFunction(c, ifce->obj, "invoke", widget_invoke_message, 1, 0); | |
| JS_DefineFunction(c, ifce->obj, "msgHandlerFactory", widget_message_handler_factory, 1, 0); | |
| JS_DefineFunction(c, ifce->obj, "invokeReply", widget_invoke_message_reply, 1, 0); | |
| } | |
| } | |
| static JSBool SMJS_FUNCTION(widget_get_interfaces) | |
| { | |
| u32 i, count; | |
| char *ifce_name; | |
| JSObject *list; | |
| jsuint idx; | |
| jsval v; | |
| SMJS_OBJ | |
| SMJS_ARGS | |
| GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj); | |
| if (!wid) return JS_FALSE; | |
| if (!argc || !JSVAL_IS_STRING(argv[0])) return JS_TRUE; | |
| ifce_name = SMJS_CHARS(c, argv[0]); | |
| list = JS_NewArrayObject(c, 0, 0); | |
| count = gf_list_count(wid->bound_ifces); | |
| for (i=0; i<count; i++) { | |
| GF_WidgetInterfaceInstance *ifce = (GF_WidgetInterfaceInstance*)gf_list_get(wid->bound_ifces, i); | |
| if (strcmp(ifce->ifce->type, ifce_name)) continue; | |
| widget_interface_js_bind(c, ifce); | |
| JS_GetArrayLength(c, list, &idx); | |
| v = OBJECT_TO_JSVAL(ifce->obj); | |
| JS_SetElement(c, list, idx, &v); | |
| } | |
| SMJS_SET_RVAL( OBJECT_TO_JSVAL(list) ); | |
| SMJS_FREE(c, ifce_name); | |
| return JS_TRUE; | |
| } | |
| static JSBool SMJS_FUNCTION_EXT(widget_activate_component, Bool is_deactivate) | |
| { | |
| u32 i, count; | |
| char *comp_id; | |
| SMJS_OBJ | |
| SMJS_ARGS | |
| GF_WidgetInstance *wid = (GF_WidgetInstance *)SMJS_GET_PRIVATE(c, obj); | |
| if (!wid) return JS_FALSE; | |
| if (!argc || !JSVAL_IS_STRING(argv[0])) return JS_TRUE; | |
| comp_id = SMJS_CHARS(c, argv[0]); | |
| 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; | |
| if (is_deactivate) { | |
| wm_deactivate_component(c, wid, comp, NULL); | |
| } else { | |
| wm_activate_component(c, wid, comp, GF_FALSE); | |
| } | |
| break; | |
| } | |
| SMJS_FREE(c, comp_id); | |
| return JS_TRUE; | |
| } | |
| static JSBool SMJS_FUNCTION(widget_activate_widget) | |
| { | |
| return widget_activate_component(SMJS_CALL_ARGS, GF_FALSE); | |
| } | |
| static JSBool SMJS_FUNCTION(widget_deactivate_widget) | |
| { | |
| return widget_activate_component(SMJS_CALL_ARGS, GF_TRUE); | |
| } | |
| void widget_on_interface_bind(GF_WidgetInterfaceInstance *ifce, Bool unbind) | |
| { | |
| jsval funval, rval, argv[1]; | |
| const char *fun_name = unbind ? "onInterfaceUnbind" : "onInterfaceBind"; | |
| if (!ifce || !ifce->wid || !ifce->wid->scene_context) return; | |
| /*look for JS Callback "invoke" in the widget manager script*/ | |
| if (JS_LookupProperty(ifce->wid->scene_context, ifce->wid->scene_obj, fun_name, &funval)!=JS_TRUE) | |
| return; | |
| if (!JSVAL_IS_OBJECT(funval)) return; | |
| widget_interface_js_bind(ifce->wid->widget->wm->ctx, ifce); | |
| argv[0] = OBJECT_TO_JSVAL(ifce->obj); | |
| JS_CallFunctionValue(ifce->wid->widget->wm->ctx, ifce->ifce->obj, funval, 1, argv, &rval); | |
| } | |
| SMJS_FUNC_PROP_GET(widget_getProperty) | |
| const char *opt; | |
| 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)); | |
| if (!prop_name) return JS_FALSE; | |
| if (!strcmp(prop_name, "viewMode")) { | |
| *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, "floating") ); | |
| } | |
| else if (!strcmp(prop_name, "locale")) { | |
| opt = gf_opts_get_key("core", "lang"); | |
| if (!opt) opt = "und"; | |
| *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, opt) ); | |
| } | |
| else if (!strcmp(prop_name, "identifier")) { | |
| if (wid->widget->identifier) *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, wid->widget->identifier) ); | |
| } | |
| else if (!strcmp(prop_name, "authorName")) { | |
| if (wid->widget->authorName) *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, wid->widget->authorName) ); | |
| } | |
| else if (!strcmp(prop_name, "authorEmail")) { | |
| if (wid->widget->authorEmail) *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, wid->widget->authorEmail) ); | |
| } | |
| else if (!strcmp(prop_name, "authorHref")) { | |
| if (wid->widget->authorHref) *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, wid->widget->authorHref) ); | |
| } | |
| else if (!strcmp(prop_name, "name")) { | |
| if (wid->widget->name) *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, wid->widget->name) ); | |
| } | |
| else if (!strcmp(prop_name, "version")) { | |
| if (wid->widget->version) *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, wid->widget->version) ); | |
| } | |
| else if (!strcmp(prop_name, "description")) { | |
| if (wid->widget->description) *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, wid->widget->description) ); | |
| } | |
| else if (!strcmp(prop_name, "width")) { | |
| opt = gf_opts_get_key((const char *) wid->secname, "width"); | |
| *vp = INT_TO_JSVAL( (opt ? atoi(opt) : 0) ); | |
| } | |
| else if (!strcmp(prop_name, "height")) { | |
| opt = gf_opts_get_key((const char *) wid->secname, "height"); | |
| *vp = INT_TO_JSVAL( (opt ? atoi(opt) : 0) ); | |
| } | |
| else if (!strcmp(prop_name, "preferences")) { | |
| } | |
| SMJS_FREE(c, prop_name); | |
| return JS_TRUE; | |
| } | |
| SMJS_FUNC_PROP_SET( widget_setProperty) | |
| /*avoids GCC warning*/ | |
| if (!obj) obj = NULL; | |
| #ifndef GPAC_CONFIG_DARWIN | |
| if (!id) id=0; | |
| #endif | |
| if (!vp) vp=0; | |
| return JS_TRUE; | |
| } | |
| void widget_load(GF_WidgetManager *wm, GF_SceneGraph *scene, JSContext *c, JSObject *global, Bool unload) | |
| { | |
| u32 i, count; | |
| GF_WidgetInstance *wi; | |
| /*Is this scenegraph a widget or not ? To find out, browse all widget instances*/ | |
| i=0; | |
| while ((wi = (GF_WidgetInstance*)gf_list_enum(wm->widget_instances, &i))) { | |
| if (!wi->scene || (wi->scene != scene)) continue; | |
| break; | |
| } | |
| if (!wi) return; | |
| /*OK we found our widget*/ | |
| if (unload) { | |
| /*detach all bound interfaces from this script*/ | |
| count = gf_list_count(wi->bound_ifces); | |
| for (i=0; i<count; i++) { | |
| GF_WidgetInterfaceInstance *ifce = (GF_WidgetInterfaceInstance*)gf_list_get(wi->bound_ifces, i); | |
| if (ifce->obj) { | |
| SMJS_SET_PRIVATE(c, ifce->obj, NULL); | |
| gf_js_remove_root(c, &ifce->obj, GF_JSGC_OBJECT); | |
| ifce->obj = NULL; | |
| } | |
| } | |
| return; | |
| } else { | |
| JSPropertySpec widgetClassProps[] = { | |
| SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0) | |
| }; | |
| JSFunctionSpec widgetClassFuncs[] = { | |
| /*W3C*/ | |
| SMJS_FUNCTION_SPEC("has_feature", widget_has_feature, 1), | |
| SMJS_FUNCTION_SPEC("openURL", widget_open_url, 1), | |
| SMJS_FUNCTION_SPEC("getAttention", widget_get_attention, 0), | |
| SMJS_FUNCTION_SPEC("showNotification", widget_show_notification, 0), | |
| /*MPEG*/ | |
| SMJS_FUNCTION_SPEC("getInterfaceHandlersByType", widget_get_interfaces, 1), | |
| SMJS_FUNCTION_SPEC("activateComponentWidget", widget_activate_widget, 1), | |
| SMJS_FUNCTION_SPEC("deactivateComponentWidget", widget_deactivate_widget, 1), | |
| SMJS_FUNCTION_SPEC(0, 0, 0) | |
| }; | |
| GF_JS_InitClass(c, global, 0, &wm->widgetClass, 0, 0,widgetClassProps, widgetClassFuncs, 0, 0); | |
| wi->scene_obj = JS_DefineObject(c, global, "widget", &wm->widgetClass._class, 0, 0); | |
| //JS_AliasProperty(c, global, "widget", "MPEGWidget"); | |
| SMJS_SET_PRIVATE(c, wi->scene_obj, wi); | |
| /*and remember the script*/ | |
| wi->scene_context = c; | |
| wi->scene_global = global; | |
| } | |
| } | |
| #endif |