This source file includes following definitions.
- ScriptAction
- xml_get_scenegraph
- js_get_utf8
- SMJS_FUNCTION
- dom_node_changed
- define_dom_exception
- dom_throw_exception
- dom_get_node
- dom_get_element
- dom_get_doc
- dom_js_define_document_ex
- dom_js_define_document
- dom_document_construct
- dom_base_node_construct
- dom_node_construct
- dom_element_construct
- dom_text_construct
- dom_unregister_node
- dom_node_get_sibling
- dom_nodelist_construct
- SMJS_FUNCTION
- SMJS_FUNC_PROP_GET
- SMJS_ID_TO_INT
- dom_handler_remove
- sg_js_get_event_target
- sg_js_parse_event_args
- create_listener
- SMJS_FUNCTION_EXT
- SMJS_FUNCTION
- SMJS_FUNCTION_EXT
- SMJS_FUNCTION
- check_dom_parents
- dom_node_inserted
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- node_get_local_name
- node_lookup_namespace_by_tag
- get_namespace_code_by_prefix
- SMJS_ID_TO_INT
- dom_node_set_textContent
- SMJS_ID_TO_INT
- SMJS_FUNCTION
- SMJS_FUNCTION
- xml_doc_gather_nodes
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- gf_dom_add_handler_listener
- gf_dom_full_set_attribute
- gf_svg_set_attributeNS
- gf_svg_set_attribute
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_ID_IS_INT
- xml_http_get_scenegraph
- xml_http_get_event_target
- xml_http_reset_recv_hdr
- xml_http_append_recv_header
- xml_http_append_send_header
- xhr_del_array_buffer
- xml_http_del_data
- xml_http_reset_partial
- xml_http_reset
- SMJS_FUNCTION
- xml_http_fire_event
- xml_http_state_change
- SMJS_FUNCTION
- SMJS_FUNCTION
- xml_http_sax_start
- xml_http_sax_end
- xml_http_sax_text
- xml_http_terminate
- xml_http_on_data
- xml_http_process_local
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_FUNCTION
- SMJS_ID_IS_INT
- gf_set_js_eventhandler
- SMJS_ID_IS_INT
- SMJS_ID_IS_INT
- SMJS_ID_IS_INT
- dcci_prop_lookup
- SMJS_FUNCTION
- dcci_prop_collect
- SMJS_FUNCTION
- SMJS_FUNCTION
- dom_js_define_storage
- dom_js_load
- gf_sg_js_dom_pre_destroy
- dom_js_unload
- dom_js_define_event
- gf_dom_new_event
- dom_js_get_node_proto
- dom_js_get_element_proto
- dom_js_get_document_proto
- dom_js_get_event_proto
- dom_set_class_selector
- gf_sg_new_from_xml_doc
- xml_reload_node_start
- xml_reload_node_end
- xml_reload_text_content
- gf_sg_reload_xml_doc
#include <gpac/setup.h>
#include <gpac/internal/scenegraph_dev.h>
#include <gpac/nodes_svg.h>
#include <gpac/events.h>
#include <gpac/utf.h>
#include <gpac/download.h>
#include <gpac/network.h>
#include <gpac/xml.h>
#ifndef GPAC_DISABLE_SVG
#ifdef GPAC_HAS_SPIDERMONKEY
#include <gpac/html5_mse.h>
#ifdef GPAC_ANDROID
#ifndef XP_UNIX
#define XP_UNIX
#endif
#endif
#include <gpac/html5_media.h>
#include <gpac/internal/smjs_api.h>
typedef struct __xhr_context XMLHTTPContext;
GF_DOMEventTarget *xml_http_get_event_target(XMLHTTPContext *ctx);
GF_SceneGraph *xml_http_get_scenegraph(XMLHTTPContext *ctx);
static GFINLINE Bool ScriptAction(GF_SceneGraph *scene, u32 type, GF_Node *node, GF_JSAPIParam *param)
{
if (scene->script_action)
return scene->script_action(scene->script_action_cbck, type, node, param);
return GF_FALSE;
}
static GFINLINE GF_SceneGraph *xml_get_scenegraph(JSContext *c)
{
GF_SceneGraph *scene;
JSObject *global = JS_GetGlobalObject(c);
assert(global);
scene = (GF_SceneGraph *)SMJS_GET_PRIVATE(c, global);
assert(scene);
return scene;
}
#define JSVAL_CHECK_STRING(_v) (JSVAL_IS_STRING(_v) || JSVAL_IS_NULL(_v))
char *js_get_utf8(JSContext *c, jsval val)
{
#if 0
jschar *utf16;
char *txt;
u32 len;
if (!JSVAL_CHECK_STRING(val) || JSVAL_IS_NULL(val)) return NULL;
utf16 = JS_GetStringChars(JSVAL_TO_STRING(val) );
len = gf_utf8_wcslen(utf16)*2 + 1;
txt = gf_malloc(sizeof(char)*len);
len = gf_utf8_wcstombs(txt, len, (const u16**) &utf16);
if ((s32)len<0) {
gf_free(txt);
return NULL;
}
txt[len]=0;
return txt;
#elif (JS_VERSION>=185)
char *result, *txt;
if (!JSVAL_CHECK_STRING(val) || JSVAL_IS_NULL(val)) return NULL;
txt = SMJS_CHARS(c, val);
result = gf_strdup(txt);
SMJS_FREE(c, txt);
return result;
#else
if (!JSVAL_CHECK_STRING(val) || JSVAL_IS_NULL(val)) return NULL;
return gf_strdup( SMJS_CHARS(c, val) );
#endif
}
typedef struct
{
u32 nb_inst;
GF_JSClass domDocumentClass;
GF_JSClass domNodeClass;
GF_JSClass domElementClass;
GF_JSClass domTextClass;
GF_JSClass domNodeListClass;
GF_JSClass domEventClass;
GF_JSClass xmlHTTPRequestClass;
GF_JSClass DCCIClass;
GF_JSClass storageClass;
GF_JSClass htmlMediaElementClass;
void *(*get_element_class)(GF_Node *n);
void *(*get_document_class)(GF_SceneGraph *n);
GF_List *handlers;
} GF_DOMRuntime;
static GF_DOMRuntime *dom_rt = NULL;
typedef enum {
NODE_JSPROPERTY_NODENAME = -1,
NODE_JSPROPERTY_NODEVALUE = -2,
NODE_JSPROPERTY_NODETYPE = -3,
NODE_JSPROPERTY_PARENTNODE = -4,
NODE_JSPROPERTY_CHILDNODES = -5,
NODE_JSPROPERTY_FIRSTCHILD = -6,
NODE_JSPROPERTY_LASTCHILD = -7,
NODE_JSPROPERTY_PREVIOUSSIBLING = -8,
NODE_JSPROPERTY_NEXTSIBLING = -9,
NODE_JSPROPERTY_ATTRIBUTES = -10,
NODE_JSPROPERTY_OWNERDOCUMENT = -11,
NODE_JSPROPERTY_NAMESPACEURI = -12,
NODE_JSPROPERTY_PREFIX = -13,
NODE_JSPROPERTY_LOCALNAME = -14,
NODE_JSPROPERTY_BASEURI = -15,
NODE_JSPROPERTY_TEXTCONTENT = -16,
NODE_JSPROPERTY_FIRSTELEMENTCHILD = -17,
NODE_JSPROPERTY_LASTELEMENTCHILD = -18,
NODE_JSPROPERTY_PREVIOUSELEMENTSIBLING = -19,
NODE_JSPROPERTY_NEXTELEMENTSIBLING = -20
} GF_DOMNodeJSProperty;
typedef enum {
DOCUMENT_JSPROPERTY_DOCTYPE = -1,
DOCUMENT_JSPROPERTY_IMPLEMENTATION = -2,
DOCUMENT_JSPROPERTY_DOCUMENTELEMENT = -3,
DOCUMENT_JSPROPERTY_INPUTENCODING = -4,
DOCUMENT_JSPROPERTY_XMLENCODING = -5,
DOCUMENT_JSPROPERTY_XMLSTANDALONE = -6,
DOCUMENT_JSPROPERTY_XMLVERSION = -7,
DOCUMENT_JSPROPERTY_STRICTERRORCHECKING = -8,
DOCUMENT_JSPROPERTY_DOCUMENTURI = -9,
DOCUMENT_JSPROPERTY_LOCATION = -10,
DOCUMENT_JSPROPERTY_DOMCONFIG = -11,
DOCUMENT_JSPROPERTY_GLOBAL = -12
} GF_DOMDocumentJSProperty;
typedef enum {
ELEMENT_JSPROPERTY_TAGNAME = -1,
ELEMENT_JSPROPERTY_SCHEMATYPEINFO = -2,
} GF_DOMElementJSProperty;
typedef enum {
DCCI_JSPROPERTY_VALUE = -1,
DCCI_JSPROPERTY_VALUETYPE = -2,
DCCI_JSPROPERTY_PROPERTYTYPE = -3,
DCCI_JSPROPERTY_READONLY = -4,
DCCI_JSPROPERTY_DCCIMETADATAINTERFACETYPE = -5,
DCCI_JSPROPERTY_DCCIMETADATAINTERFACE = -6,
DCCI_JSPROPERTY_VERSION = -7,
} GF_DCCIJSProperty;
typedef enum {
NODELIST_JSPROPERTY_LENGTH = -1,
} GF_DOMNodeListJSProperty;
typedef enum {
EVENT_JSPROPERTY_TYPE = -1,
EVENT_JSPROPERTY_TARGET = -2,
EVENT_JSPROPERTY_CURRENTTARGET = -3,
EVENT_JSPROPERTY_EVENTPHASE = -4,
EVENT_JSPROPERTY_BUBBLES = -5,
EVENT_JSPROPERTY_CANCELABLE = -6,
EVENT_JSPROPERTY_TIMESTAMP = -7,
EVENT_JSPROPERTY_NAMESPACEURI = -8,
EVENT_JSPROPERTY_DEFAULTPREVENTED = -9,
EVENT_JSPROPERTY_DETAIL = -10,
EVENT_JSPROPERTY_DATA = -11,
EVENT_JSPROPERTY_SCREENX = -12,
EVENT_JSPROPERTY_SCREENY = -13,
EVENT_JSPROPERTY_CLIENTX = -14,
EVENT_JSPROPERTY_CLIENTY = -15,
EVENT_JSPROPERTY_BUTTON = -16,
EVENT_JSPROPERTY_RELATEDTARGET = -17,
EVENT_JSPROPERTY_WHEELDELTA = -18,
EVENT_JSPROPERTY_KEYIDENTIFIER = -19,
EVENT_JSPROPERTY_KEYCHAR = -20,
EVENT_JSPROPERTY_CHARCODE = -21,
EVENT_JSPROPERTY_LENGTHCOMPUTABLE = -22,
EVENT_JSPROPERTY_TYPEARG = -23,
EVENT_JSPROPERTY_LOADED = -24,
EVENT_JSPROPERTY_TOTAL = -25,
EVENT_JSPROPERTY_BUFFER_ON = -26,
EVENT_JSPROPERTY_BUFFERLEVEL = -27,
EVENT_JSPROPERTY_BUFFERREMAININGTIME = -28,
EVENT_JSPROPERTY_STATUS = -29,
EVENT_JSPROPERTY_WIDTH = -30,
EVENT_JSPROPERTY_HEIGHT = -31,
EVENT_JSPROPERTY_OFFSETX = -32,
EVENT_JSPROPERTY_OFFSETY = -33,
EVENT_JSPROPERTY_VPWIDTH = -34,
EVENT_JSPROPERTY_VPHEIGHT = -35,
EVENT_JSPROPERTY_TRANSLATIONX = -36,
EVENT_JSPROPERTY_TRANSLATIONY = -37,
EVENT_JSPROPERTY_TYPE3D = -38,
EVENT_JSPROPERTY_ERROR = -39,
EVENT_JSPROPERTY_DYNAMIC_SCENE = -40,
EVENT_JSPROPERTY_URL = -41,
} GF_DOMEventJSProperty;
typedef enum {
TEXT_JSPROPERTY_DATA = -1,
TEXT_JSPROPERTY_LENGTH = -2,
TEXT_JSPROPERTY_ISELEMENTCONTENTWHITESPACE = -3,
TEXT_JSPROPERTY_WHOLETEXT = -4,
} GF_DOMTextJSProperty;
static JSBool SMJS_FUNCTION(xml_dom3_not_implemented)
{
return JS_TRUE;
}
#define JS_DOM3_uDOM_FIRST_PROP 18
void dom_node_changed(GF_Node *n, Bool child_modif, GF_FieldInfo *info)
{
if (info) {
if (child_modif) {
gf_node_dirty_set(n, GF_SG_CHILD_DIRTY, GF_FALSE);
}
#ifndef GPAC_DISABLE_SVG
else {
u32 flag = gf_svg_get_modification_flags((SVG_Element *)n, info);
gf_node_dirty_set(n, flag, GF_FALSE);
}
#endif
}
gf_node_changed(n, info);
}
static void define_dom_exception(JSContext *c, JSObject *global)
{
#define DEF_EXC(_val) \
JS_DefineProperty(c, obj, #_val, INT_TO_JSVAL(GF_DOM_EXC_##_val), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
JSObject *obj = JS_DefineObject(c, global, "DOMException", NULL, 0, 0);
DEF_EXC(INDEX_SIZE_ERR);
DEF_EXC(DOMSTRING_SIZE_ERR);
DEF_EXC(HIERARCHY_REQUEST_ERR);
DEF_EXC(WRONG_DOCUMENT_ERR);
DEF_EXC(INVALID_CHARACTER_ERR);
DEF_EXC(NO_DATA_ALLOWED_ERR);
DEF_EXC(NO_MODIFICATION_ALLOWED_ERR);
DEF_EXC(NOT_FOUND_ERR);
DEF_EXC(NOT_SUPPORTED_ERR);
DEF_EXC(INUSE_ATTRIBUTE_ERR);
DEF_EXC(INVALID_STATE_ERR);
DEF_EXC(SYNTAX_ERR);
DEF_EXC(INVALID_MODIFICATION_ERR);
DEF_EXC(NAMESPACE_ERR);
DEF_EXC(INVALID_ACCESS_ERR);
DEF_EXC(VALIDATION_ERR);
DEF_EXC(TYPE_MISMATCH_ERR);
DEF_EXC(SECURITY_ERR);
DEF_EXC(NETWORK_ERR);
DEF_EXC(ABORT_ERR);
DEF_EXC(URL_MISMATCH_ERR);
DEF_EXC(QUOTA_EXCEEDED_ERR);
DEF_EXC(TIMEOUT_ERR);
DEF_EXC(INVALID_NODE_TYPE_ERR);
DEF_EXC(DATA_CLONE_ERR);
DEF_EXC(SECURITY_ERR);
#undef DEF_EXC
obj = JS_DefineObject(c, global, "EventException", NULL, 0, 0);
JS_DefineProperty(c, obj, "UNSPECIFIED_EVENT_TYPE_ERR", INT_TO_JSVAL(0), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineProperty(c, obj, "DISPATCH_REQUEST_ERR", INT_TO_JSVAL(1), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
}
JSBool dom_throw_exception(JSContext *c, u32 code)
{
JSObject *obj = JS_NewObject(c, NULL, 0, 0);
JS_DefineProperty(c, obj, "code", INT_TO_JSVAL(code), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
JS_SetPendingException(c, OBJECT_TO_JSVAL(obj));
return JS_FALSE;
}
GF_Node *dom_get_node(JSContext *c, JSObject *obj)
{
GF_Node *n = (obj ? (GF_Node *)SMJS_GET_PRIVATE(c, obj) : NULL);
if (n && n->sgprivate) return n;
return NULL;
}
GF_Node *dom_get_element(JSContext *c, JSObject *obj)
{
GF_Node *n = (GF_Node *)SMJS_GET_PRIVATE(c, obj);
if (!n || !n->sgprivate) return NULL;
if (n->sgprivate->tag==TAG_DOMText) return NULL;
return n;
}
GF_SceneGraph *dom_get_doc(JSContext *c, JSObject *obj)
{
GF_SceneGraph *sg = (GF_SceneGraph *)SMJS_GET_PRIVATE(c, obj);
if (sg && !sg->__reserved_null) return sg;
return NULL;
}
static void dom_js_define_document_ex(JSContext *c, JSObject *global, GF_SceneGraph *doc, const char *name)
{
GF_JSClass *__class;
JSObject *obj;
if (!doc || !doc->RootNode) return;
if (doc->reference_count)
doc->reference_count++;
gf_node_register(doc->RootNode, NULL);
__class = NULL;
if (dom_rt->get_document_class)
__class = (GF_JSClass *)dom_rt->get_document_class(doc);
if (!__class) __class = &dom_rt->domDocumentClass;
obj = JS_DefineObject(c, global, name, & __class->_class, 0, 0 );
SMJS_SET_PRIVATE(c, obj, doc);
doc->document = obj;
}
void dom_js_define_document(JSContext *c, JSObject *global, GF_SceneGraph *doc)
{
dom_js_define_document_ex(c, global, doc, "document");
}
static jsval dom_document_construct(JSContext *c, GF_SceneGraph *sg)
{
GF_JSClass *jsclass;
JSObject *new_obj;
if (sg->document) return OBJECT_TO_JSVAL(sg->document);
if (sg->reference_count)
sg->reference_count++;
gf_node_register(sg->RootNode, NULL);
jsclass = NULL;
if (dom_rt->get_document_class)
jsclass = (GF_JSClass *) dom_rt->get_document_class(sg);
if (!jsclass) jsclass = &dom_rt->domDocumentClass;
new_obj = JS_NewObject(c, & jsclass->_class, 0, 0);
SMJS_SET_PRIVATE(c, new_obj, sg);
sg->document = new_obj;
return OBJECT_TO_JSVAL(new_obj);
}
static jsval dom_base_node_construct(JSContext *c, GF_JSClass *_class, GF_Node *n)
{
Bool set_rooted;
GF_SceneGraph *sg;
JSObject *new_obj;
if (!n || !n->sgprivate->scenegraph) return JSVAL_VOID;
if (n->sgprivate->tag<TAG_DOMText) return JSVAL_VOID;
sg = n->sgprivate->scenegraph;
set_rooted = sg->reference_count ? GF_FALSE : GF_TRUE;
if (n->sgprivate->interact && n->sgprivate->interact->js_binding && n->sgprivate->interact->js_binding->node) {
if (set_rooted && (gf_list_find(sg->objects, n->sgprivate->interact->js_binding->node)<0)) {
const char *name = gf_node_get_name(n);
if (name) {
gf_js_add_named_root(c, &n->sgprivate->interact->js_binding->node, GF_JSGC_OBJECT, name);
} else {
gf_js_add_root(c, &n->sgprivate->interact->js_binding->node, GF_JSGC_OBJECT);
}
gf_list_add(sg->objects, n->sgprivate->interact->js_binding->node);
}
return OBJECT_TO_JSVAL(n->sgprivate->interact->js_binding->node);
}
if (n->sgprivate->scenegraph->reference_count)
n->sgprivate->scenegraph->reference_count ++;
gf_node_register(n, NULL);
new_obj = JS_NewObject(c, & _class->_class, 0, 0);
SMJS_SET_PRIVATE(c, new_obj, n);
if (n->sgprivate->tag == TAG_SVG_video || n->sgprivate->tag == TAG_SVG_audio)
{
html_media_element_js_init(c, new_obj, n);
}
if (!n->sgprivate->interact) {
GF_SAFEALLOC(n->sgprivate->interact, struct _node_interactive_ext);
if (!n->sgprivate->interact) {
return JSVAL_NULL;
}
}
if (!n->sgprivate->interact->js_binding) {
GF_SAFEALLOC(n->sgprivate->interact->js_binding, struct _node_js_binding);
if (!n->sgprivate->interact->js_binding) {
return JSVAL_NULL;
}
}
n->sgprivate->interact->js_binding->node = new_obj;
if (n->sgprivate->parents && set_rooted) {
const char *name = gf_node_get_name(n);
if (name) {
gf_js_add_named_root(c, &n->sgprivate->interact->js_binding->node, GF_JSGC_OBJECT, name);
} else {
gf_js_add_root(c, &n->sgprivate->interact->js_binding->node, GF_JSGC_OBJECT);
}
gf_list_add(sg->objects, n->sgprivate->interact->js_binding->node);
}
return OBJECT_TO_JSVAL(new_obj);
}
static jsval dom_node_construct(JSContext *c, GF_Node *n)
{
GF_JSClass *__class = NULL;
if (!n) return JSVAL_NULL;
if (n->sgprivate->scenegraph->dcci_doc)
__class = &dom_rt->DCCIClass;
else if (dom_rt->get_element_class)
__class = (GF_JSClass *) dom_rt->get_element_class(n);
if (!__class ) __class = &dom_rt->domElementClass;
return dom_base_node_construct(c, __class, n);
}
jsval dom_element_construct(JSContext *c, GF_Node *n)
{
GF_JSClass *__class = NULL;
if (!n) return JSVAL_NULL;
if (n->sgprivate->scenegraph->dcci_doc)
__class = &dom_rt->DCCIClass;
else if (dom_rt->get_element_class)
__class = (GF_JSClass *) dom_rt->get_element_class(n);
if (!__class) __class = &dom_rt->domElementClass;
return dom_base_node_construct(c, __class, n);
}
static jsval dom_text_construct(JSContext *c, GF_Node *n)
{
return dom_base_node_construct(c, &dom_rt->domTextClass, n);
}
static void dom_unregister_node(GF_Node *n)
{
GF_SceneGraph *sg = n->sgprivate->scenegraph;
if (!sg) return;
if (!n->sgprivate->num_instances) return;
gf_node_unregister(n, NULL);
if (sg->reference_count) {
sg->reference_count--;
if (!sg->reference_count)
gf_sg_del(sg);
}
}
jsval dom_node_get_sibling(JSContext *c, GF_Node *n, Bool is_prev, Bool elt_only)
{
GF_Node *val;
GF_ChildNodeItem *child;
s32 idx, cur;
GF_ParentNode *par;
if (!n) return JSVAL_NULL;
par = (GF_ParentNode *)gf_node_get_parent(n, 0);
if (!par) return JSVAL_NULL;
idx = gf_node_list_find_child(par->children, n);
if (idx<0) return JSVAL_NULL;
if (!elt_only) {
if (is_prev) {
idx--;
if (idx<0) return JSVAL_NULL;
}
else idx++;
return dom_node_construct(c, gf_node_list_get_child(par->children, idx) );
}
cur = 0;
val = NULL;
child = par->children;
while (child) {
if ((idx!=cur) && (child->node->sgprivate->tag!=TAG_DOMText)) {
val = child->node;
}
if (is_prev) {
if (idx<=cur)
break;
} else {
if (idx>=cur) val = NULL;
if (val && idx<cur)
break;
}
child = child->next;
cur++;
}
return dom_node_construct(c, val);
}
typedef struct
{
GF_ParentNode *owner;
GF_ChildNodeItem *child;
} DOMNodeList;
static jsval dom_nodelist_construct(JSContext *c, GF_ParentNode *n)
{
DOMNodeList *nl;
JSObject *new_obj;
if (!n) return JSVAL_VOID;
GF_SAFEALLOC(nl, DOMNodeList);
if (!nl) return JSVAL_NULL;
nl->owner = n;
if (n->sgprivate->scenegraph->reference_count)
n->sgprivate->scenegraph->reference_count++;
gf_node_register((GF_Node*)n, NULL);
new_obj = JS_NewObject(c, &dom_rt->domNodeListClass._class, 0, 0);
SMJS_SET_PRIVATE(c, new_obj, nl);
return OBJECT_TO_JSVAL(new_obj);
}
static DECL_FINALIZE(dom_nodelist_finalize)
DOMNodeList *nl;
if (!GF_JS_InstanceOf(c, obj, &dom_rt->domNodeListClass, NULL) )
return;
nl = (DOMNodeList *) SMJS_GET_PRIVATE(c, obj);
if (!nl) return;
if (nl->owner) {
dom_unregister_node((GF_Node*)nl->owner);
} else {
while (nl->child) {
GF_ChildNodeItem *child = nl->child;
nl->child = child->next;
dom_unregister_node(child->node);
gf_free(child);
}
}
gf_free(nl);
}
static JSBool SMJS_FUNCTION(dom_nodelist_item)
{
GF_Node *n;
u32 count;
s32 idx;
DOMNodeList *nl;
SMJS_OBJ
SMJS_ARGS
if (!GF_JS_InstanceOf(c, obj, &dom_rt->domNodeListClass, NULL) )
return JS_TRUE;
if ((argc!=1) || !JSVAL_IS_INT(argv[0]))
return JS_TRUE;
nl = (DOMNodeList *)SMJS_GET_PRIVATE(c, obj);
count = gf_node_list_get_count(nl->owner ? nl->owner->children : nl->child);
idx = JSVAL_TO_INT(argv[0]);
if ((idx<0) || ((u32) idx>=count)) {
SMJS_SET_RVAL( JSVAL_VOID );
return JS_TRUE;
}
n = gf_node_list_get_child(nl->owner ? nl->owner->children : nl->child, idx);
SMJS_SET_RVAL( dom_node_construct(c, n) );
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET( dom_nodelist_getProperty)
DOMNodeList *nl;
u32 count;
s32 idx;
if (!GF_JS_InstanceOf(c, obj, &dom_rt->domNodeListClass, NULL)) {
return JS_TRUE;
}
nl = (DOMNodeList *) SMJS_GET_PRIVATE(c, obj);
count = gf_node_list_get_count(nl->owner ? nl->owner->children : nl->child);
if (!SMJS_ID_IS_INT(id)) return JS_TRUE;
switch (SMJS_ID_TO_INT(id)) {
case NODELIST_JSPROPERTY_LENGTH:
*vp = INT_TO_JSVAL( count );
return JS_TRUE;
default:
idx = SMJS_ID_TO_INT(id);
if ((idx<0) || ((u32) idx>=count)) {
*vp = JSVAL_VOID;
return JS_TRUE;
} else {
GF_Node *n;
n = gf_node_list_get_child(nl->owner ? nl->owner->children : nl->child, idx);
*vp = dom_node_construct(c, n);
return JS_TRUE;
}
}
return JS_TRUE;
}
static SMJS_FUNC_PROP_SET_NOVP( dom_nodelist_setProperty)
if (!obj) obj=NULL;
if (!SMJS_ID_IS_INT(id)) return JS_TRUE;
return JS_TRUE;
}
#define JS_DOM3_EVENT_TARGET_INTERFACE \
SMJS_FUNCTION_SPEC("addEventListenerNS", dom_event_add_listener, 4), \
SMJS_FUNCTION_SPEC("removeEventListenerNS", dom_event_remove_listener, 4), \
SMJS_FUNCTION_SPEC("addEventListener", dom_event_add_listener, 3), \
SMJS_FUNCTION_SPEC("removeEventListener", dom_event_remove_listener, 3), \
SMJS_FUNCTION_SPEC("dispatchEvent", xml_dom3_not_implemented, 1),
static void dom_handler_remove(GF_Node *node, void *rs, Bool is_destroy)
{
if (is_destroy) {
SVG_handlerElement *handler = (SVG_handlerElement *)node;
if (handler->js_context && handler->js_fun_val) {
gf_js_remove_root((JSContext *)handler->js_context, &(handler->js_fun_val), GF_JSGC_VAL);
handler->js_fun_val=0;
gf_list_del_item(dom_rt->handlers, handler);
}
}
}
static JSBool sg_js_get_event_target(JSContext *c, JSObject *obj, GF_EventType evtType, GF_Node *vrml_node,
GF_SceneGraph **sg, GF_DOMEventTarget **target, GF_Node **n)
{
Bool is_svg_document_class(JSContext *c, JSObject *obj);
Bool is_svg_element_class(JSContext *c, JSObject *obj);
Bool gf_mse_is_mse_object(JSContext *c, JSObject *obj);
*target = NULL;
*sg = NULL;
*n = NULL;
if (gf_dom_event_get_category(evtType) == GF_DOM_EVENT_MEDIA) {
void gf_html_media_get_event_target(JSContext *c, JSObject *obj, GF_DOMEventTarget **target, GF_SceneGraph **sg);
gf_html_media_get_event_target(c, obj, target, sg);
if (*target && *sg) return JS_TRUE;
}
if (gf_dom_event_get_category(evtType) == GF_DOM_EVENT_MEDIASOURCE) {
void gf_mse_get_event_target(JSContext *c, JSObject *obj, GF_DOMEventTarget **target, GF_SceneGraph **sg);
gf_mse_get_event_target(c, obj, target, sg);
if (*target && *sg) return JS_TRUE;
}
if (GF_JS_InstanceOf(c, obj, &dom_rt->domDocumentClass, NULL) || is_svg_document_class(c, obj)) {
*sg = dom_get_doc(c, obj);
if (*sg) {
#ifndef GPAC_DISABLE_SVG
*target = (*sg)->dom_evt;
#else
return JS_TRUE;
#endif
} else {
return JS_TRUE;
}
} else if (GF_JS_InstanceOf(c, obj, &dom_rt->domElementClass, NULL) || is_svg_element_class(c, obj) || vrml_node) {
if (vrml_node) {
*n = vrml_node;
} else {
*n = dom_get_element(c, obj);
}
if (*n) {
*sg = (*n)->sgprivate->scenegraph;
if (!(*n)->sgprivate->interact->dom_evt) {
(*n)->sgprivate->interact->dom_evt = gf_dom_event_target_new(GF_DOM_EVENT_TARGET_NODE, *n);
}
*target = (*n)->sgprivate->interact->dom_evt;
} else {
return JS_TRUE;
}
} else if (GF_JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL)) {
XMLHTTPContext *ctx = (XMLHTTPContext *)SMJS_GET_PRIVATE(c, obj);
if (ctx) {
*sg = xml_get_scenegraph(c);
*target = xml_http_get_event_target(ctx);
} else {
return JS_TRUE;
}
} else {
return JS_TRUE;
}
return JS_TRUE;
}
static GF_Err sg_js_parse_event_args(JSContext *c, JSObject *obj, uintN argc, jsval *argv,
GF_EventType *evtType,
char **callback, jsval *funval, JSObject **evt_handler) {
u32 offset = 0;
char *type = NULL;
char *inNS = NULL;
*evtType = GF_EVENT_UNKNOWN;
*callback = NULL;
*funval = JSVAL_NULL;
if (evt_handler) *evt_handler = obj;
if (argc>=4) {
if (!JSVAL_CHECK_STRING(argv[0])) return GF_BAD_PARAM;
inNS = js_get_utf8(c, argv[0]);
offset = 1;
}
if (!JSVAL_CHECK_STRING(argv[offset])) goto err_exit;
type = SMJS_CHARS(c, argv[offset]);
if (JSVAL_CHECK_STRING(argv[offset+1])) {
*callback = SMJS_CHARS(c, argv[offset+1]);
if (!*callback) goto err_exit;
} else if (JSVAL_IS_OBJECT(argv[offset+1])) {
if (JS_ObjectIsFunction(c, JSVAL_TO_OBJECT(argv[offset+1]))) {
*funval = argv[offset+1];
} else {
JSBool found;
jsval evt_fun;
if (evt_handler) {
*evt_handler = JSVAL_TO_OBJECT(argv[offset+1]);
} else {
goto err_exit;
}
found = JS_GetProperty(c, *evt_handler, "handleEvent", &evt_fun);
if (!found || !JSVAL_IS_OBJECT(evt_fun) ) goto err_exit;
if (!JS_ObjectIsFunction(c, JSVAL_TO_OBJECT(evt_fun)) ) goto err_exit;
*funval = evt_fun;
}
}
*evtType = gf_dom_event_type_by_name(type);
if (*evtType==GF_EVENT_UNKNOWN) goto err_exit;
SMJS_FREE(c, type);
if (inNS) gf_free(inNS);
return GF_OK;
err_exit:
if (inNS) gf_free(inNS);
if (type) SMJS_FREE(c, type);
if (callback) SMJS_FREE(c, *callback);
*callback = NULL;
*evtType = GF_EVENT_UNKNOWN;
*funval = JSVAL_NULL;
if (evt_handler) *evt_handler = NULL;
return GF_BAD_PARAM;
}
static GF_Node *create_listener(GF_SceneGraph *sg, GF_EventType evtType, GF_Node *n, GF_Node *vrml_node,
JSContext *c, char *callback, jsval funval, JSObject *evt_handler)
{
GF_FieldInfo info;
GF_Node *listener;
SVG_handlerElement *handler;
listener = gf_node_new(sg, TAG_SVG_listener);
{
handler = (SVG_handlerElement *) gf_node_new(sg, TAG_SVG_handler);
gf_node_register((GF_Node *)handler, listener);
gf_node_list_add_child(& ((GF_ParentNode *)listener)->children, (GF_Node*)handler);
if (!callback) {
handler->js_fun = NULL;
handler->js_fun_val = *(u64 *) &funval;
if (handler->js_fun_val) {
handler->js_context = c;
gf_js_add_root((JSContext *)handler->js_context, &handler->js_fun_val, GF_JSGC_VAL);
handler->sgprivate->UserCallback = dom_handler_remove;
gf_list_add(dom_rt->handlers, handler);
}
handler->evt_listen_obj = evt_handler;
}
}
gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_event, GF_TRUE, GF_FALSE, &info);
((XMLEV_Event*)info.far_ptr)->type = evtType;
gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_handler, GF_TRUE, GF_FALSE, &info);
((XMLRI*)info.far_ptr)->target = (GF_Node*)handler;
if (n) {
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 = evtType;
if (callback) gf_dom_add_text_node((GF_Node *)handler, gf_strdup(callback));
#ifndef GPAC_DISABLE_SVG
if (handler->sgprivate->scenegraph->svg_js)
handler->handle_event = gf_sg_handle_dom_event;
#endif
if (vrml_node) {
handler->js_context = c;
#ifndef GPAC_DISABLE_VRML
if (vrml_node->sgprivate->tag <= GF_NODE_RANGE_LAST_VRML)
handler->handle_event = gf_sg_handle_dom_event_for_vrml;
#endif
}
return listener;
}
JSBool SMJS_FUNCTION_EXT(gf_sg_js_event_add_listener, GF_Node *vrml_node)
{
GF_DOMEventTarget *target = NULL;
GF_Node *listener = NULL;
GF_EventType evtType = GF_EVENT_UNKNOWN;
GF_SceneGraph *sg = NULL;
char *callback = NULL;
jsval funval = JSVAL_NULL;
GF_Node *n = NULL;
JSObject *evt_handler = NULL;
GF_Err e;
SMJS_OBJ
SMJS_ARGS
e = sg_js_parse_event_args(c, obj, argc, argv, &evtType, &callback, &funval, &evt_handler);
if (e != GF_OK) goto err_exit;
sg_js_get_event_target(c, obj, evtType, vrml_node, &sg, &target, &n);
if (!sg || !target) goto err_exit;
listener = create_listener(sg, evtType, n, vrml_node, c, callback, funval, evt_handler);
if (!listener) goto err_exit;
if (n) {
gf_sg_listener_post_add((GF_Node *) n, listener);
} else {
gf_sg_listener_add(listener, target);
}
err_exit:
if (callback) SMJS_FREE(c, callback);
return JS_TRUE;
}
JSBool SMJS_FUNCTION(dom_event_add_listener)
{
return gf_sg_js_event_add_listener(SMJS_CALL_ARGS, NULL);
}
JSBool SMJS_FUNCTION_EXT(gf_sg_js_event_remove_listener, GF_Node *vrml_node)
{
#ifndef GPAC_DISABLE_SVG
char *callback = NULL;
GF_EventType evtType = GF_EVENT_UNKNOWN;
u32 i, count;
GF_Node *node = NULL;
jsval funval = JSVAL_NULL;
GF_SceneGraph *sg = NULL;
GF_DOMEventTarget *target = NULL;
GF_Err e;
SMJS_OBJ
SMJS_ARGS
e = sg_js_parse_event_args(c, obj, argc, argv, &evtType, &callback, &funval, NULL);
if (e != GF_OK) goto err_exit;
sg_js_get_event_target(c, obj, evtType, vrml_node, &sg, &target, &node);
if (!sg || !target) return JS_TRUE;
gf_dom_listener_process_add(sg);
count = gf_list_count(target->listeners);
for (i=0; i<count; i++) {
GF_FieldInfo info;
GF_DOMText *txt;
SVG_handlerElement *hdl;
GF_Node *el = (GF_Node *)gf_list_get(target->listeners, i);
gf_node_get_attribute_by_tag(el, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info);
if (!info.far_ptr) continue;
if (((XMLEV_Event*)info.far_ptr)->type != evtType) continue;
gf_node_get_attribute_by_tag(el, TAG_XMLEV_ATT_handler, GF_FALSE, GF_FALSE, &info);
if (!info.far_ptr) continue;
hdl = (SVG_handlerElement *) ((XMLRI*)info.far_ptr)->target;
if (!hdl) continue;
if (! JSVAL_IS_NULL(funval) ) {
#if (JS_VERSION>=185)
JSBool res = JS_FALSE;
if (! JS_StrictlyEqual(c, funval, *(jsval *)&hdl->js_fun_val, &res))
continue;
#else
if (funval != *(jsval *)&hdl->js_fun_val) continue;
#endif
} else if (hdl->children) {
txt = (GF_DOMText *) hdl->children->node;
if (txt->sgprivate->tag != TAG_DOMText) continue;
if (!txt->textContent || !callback || strcmp(txt->textContent, callback)) continue;
} else {
continue;
}
gf_dom_listener_del(el, target);
break;
}
#endif
err_exit:
if (callback) SMJS_FREE(c, callback);
return JS_TRUE;
}
JSBool SMJS_FUNCTION(dom_event_remove_listener)
{
return gf_sg_js_event_remove_listener(SMJS_CALL_ARGS, NULL);
}
static DECL_FINALIZE( dom_node_finalize)
GF_Node *n = (GF_Node *) SMJS_GET_PRIVATE(c, obj);
if (!n) return;
if (!n->sgprivate) return;
SMJS_SET_PRIVATE(c, obj, NULL);
gf_list_del_item(n->sgprivate->scenegraph->objects, obj);
gf_sg_js_dom_pre_destroy(c, n->sgprivate->scenegraph, n);
dom_unregister_node(n);
}
static Bool check_dom_parents(JSContext *c, GF_Node *n, GF_Node *parent)
{
GF_ParentList *par = n->sgprivate->parents;
if (n->sgprivate->scenegraph != parent->sgprivate->scenegraph) {
dom_throw_exception(c, GF_DOM_EXC_WRONG_DOCUMENT_ERR);
return GF_FALSE;
}
while (par) {
if (par->node==parent) {
dom_throw_exception(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
return GF_FALSE;
}
if (!check_dom_parents(c, par->node, parent))
return GF_FALSE;
par = par->next;
}
return GF_TRUE;
}
static void dom_node_inserted(JSContext *c, GF_Node *n, GF_Node *parent, s32 pos)
{
GF_ParentNode *old_parent;
Bool do_init = GF_FALSE;
old_parent = (GF_ParentNode*)gf_node_get_parent(n, 0);
if (old_parent) {
n->sgprivate->num_instances++;
gf_node_list_del_child(&old_parent->children, n);
gf_node_unregister(n, (GF_Node*)old_parent);
n->sgprivate->num_instances--;
} else {
do_init = (n->sgprivate->UserCallback || n->sgprivate->UserPrivate) ? GF_FALSE : GF_TRUE;
}
if (n->sgprivate->interact && n->sgprivate->interact->js_binding) {
JSObject *nobj = (JSObject *)n->sgprivate->interact->js_binding->node;
if (nobj && (gf_list_find(n->sgprivate->scenegraph->objects, nobj)<0) ) {
const char *name = gf_node_get_name(n);
if (name) {
gf_js_add_named_root(c, &n->sgprivate->interact->js_binding->node, GF_JSGC_OBJECT, name);
} else {
gf_js_add_root(c, &n->sgprivate->interact->js_binding->node, GF_JSGC_OBJECT);
}
gf_list_add(n->sgprivate->scenegraph->objects, nobj);
}
}
if (pos<0) gf_node_list_add_child( & ((GF_ParentNode *)parent)->children, n);
else gf_node_list_insert_child( & ((GF_ParentNode *)parent)->children, n, (u32) pos);
gf_node_register(n, parent);
if (do_init) {
if (parent && (n->sgprivate->tag==TAG_SVG_handler)) {
gf_dom_listener_build_ex(parent, 0, 0, n, NULL);
}
gf_node_init(n);
#ifndef GPAC_DISABLE_SVG
if (n->sgprivate->interact && n->sgprivate->interact->dom_evt) {
GF_DOM_Event evt;
memset(&evt, 0, sizeof(GF_DOM_Event));
evt.type = GF_EVENT_LOAD;
gf_dom_event_fire(n, &evt);
}
#endif
}
if (!old_parent) gf_node_activate(n);
dom_node_changed(parent, GF_TRUE, NULL);
}
static JSBool SMJS_FUNCTION(xml_node_insert_before)
{
s32 idx;
u32 tag;
GF_Node *n, *target, *new_node;
GF_ParentNode *par;
SMJS_OBJ
SMJS_ARGS
if (!argc || !JSVAL_IS_OBJECT(argv[0]))
return JS_TRUE;
n = dom_get_node(c, obj);
if (!n) {
return dom_throw_exception(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
}
new_node = dom_get_node(c, JSVAL_TO_OBJECT(argv[0]));
if (!new_node) return JS_TRUE;
target = NULL;
if ((argc==2) && JSVAL_IS_OBJECT(argv[1]) && !JSVAL_IS_NULL(argv[1])) {
target = dom_get_node(c, JSVAL_TO_OBJECT(argv[1]));
if (!target) return JS_TRUE;
}
tag = gf_node_get_tag(n);
if (tag==TAG_DOMText) return JS_TRUE;
if (!check_dom_parents(c, n, new_node))
return JS_FALSE;
par = (GF_ParentNode*)n;
idx = -1;
if (target) {
idx = gf_node_list_find_child(par->children, target);
if (idx<0) {
return dom_throw_exception(c, GF_DOM_EXC_NOT_FOUND_ERR);
}
}
dom_node_inserted(c, new_node, n, idx);
SMJS_SET_RVAL( argv[0] );
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_node_append_child)
{
u32 tag;
GF_Node *n, *new_node;
SMJS_OBJ
SMJS_ARGS
if (!argc || !JSVAL_IS_OBJECT(argv[0])) {
return JS_TRUE;
}
n = dom_get_node(c, obj);
if (!n) {
return dom_throw_exception(c, GF_DOM_EXC_HIERARCHY_REQUEST_ERR);
}
new_node = dom_get_node(c, JSVAL_TO_OBJECT(argv[0]));
if (!new_node) {
return JS_TRUE;
}
tag = gf_node_get_tag(n);
if (tag==TAG_DOMText) {
return JS_TRUE;
}
if (!check_dom_parents(c, n, new_node)) {
return JS_FALSE;
}
dom_node_inserted(c, new_node, n, -1);
SMJS_SET_RVAL( argv[0] );
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_node_replace_child)
{
s32 idx;
u32 tag;
GF_Node *n, *new_node, *old_node;
GF_ParentNode *par;
SMJS_OBJ
SMJS_ARGS
if ((argc!=2) || !JSVAL_IS_OBJECT(argv[0]) || !JSVAL_IS_OBJECT(argv[1])) return JS_TRUE;
n = dom_get_node(c, obj);
if (!n) return JS_TRUE;
new_node = dom_get_node(c, JSVAL_TO_OBJECT(argv[0]));
if (!new_node) return JS_TRUE;
old_node = dom_get_node(c, JSVAL_TO_OBJECT(argv[1]));
if (!old_node) return JS_TRUE;
tag = gf_node_get_tag(n);
if (tag==TAG_DOMText) return JS_TRUE;
par = (GF_ParentNode*)n;
idx = gf_node_list_find_child(par->children, old_node);
if (idx<0) return JS_TRUE;
gf_node_list_del_child(&par->children, old_node);
gf_node_unregister(old_node, n);
dom_node_inserted(c, new_node, n, -1);
n->sgprivate->scenegraph->svg_js->force_gc = GF_TRUE;
SMJS_SET_RVAL( argv[0] );
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_node_remove_child)
{
u32 tag;
GF_Node *n, *old_node;
GF_ParentNode *par;
SMJS_OBJ
SMJS_ARGS
if (!argc || !JSVAL_IS_OBJECT(argv[0])) return JS_TRUE;
n = dom_get_node(c, obj);
if (!n) return JS_TRUE;
old_node = dom_get_node(c, JSVAL_TO_OBJECT(argv[0]));
if (!old_node) return JS_TRUE;
tag = gf_node_get_tag(n);
if (tag==TAG_DOMText) return JS_TRUE;
par = (GF_ParentNode*)n;
if (gf_node_list_del_child(&par->children, old_node)) {
gf_node_unregister(old_node, n);
} else {
return dom_throw_exception(c, GF_DOM_EXC_NOT_FOUND_ERR);
}
SMJS_SET_RVAL( argv[0]);
dom_node_changed(n, GF_TRUE, NULL);
n->sgprivate->scenegraph->svg_js->force_gc = GF_TRUE;
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_clone_node)
{
Bool deep;
GF_Node *n, *clone;
SMJS_OBJ
SMJS_ARGS
n = dom_get_node(c, obj);
if (!n) return JS_TRUE;
deep = argc ? (JSVAL_TO_BOOLEAN(argv[0]) ? GF_TRUE : GF_FALSE) : GF_FALSE;
clone = gf_node_clone(n->sgprivate->scenegraph, n, NULL, "", deep);
SMJS_SET_RVAL( dom_node_construct(c, clone) );
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_node_has_children)
{
GF_Node *n;
SMJS_OBJ
n = dom_get_node(c, obj);
if (!n) return JS_TRUE;
SMJS_SET_RVAL( BOOLEAN_TO_JSVAL( ((GF_ParentNode*)n)->children ? JS_TRUE : JS_FALSE) );
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_node_has_attributes)
{
u32 tag;
GF_Node *n;
SMJS_OBJ
n = dom_get_node(c, obj);
if (!n) return JS_TRUE;
tag = gf_node_get_tag(n);
if (tag>=GF_NODE_FIRST_DOM_NODE_TAG) {
SMJS_SET_RVAL( BOOLEAN_TO_JSVAL( ((GF_DOMNode*)n)->attributes ? JS_TRUE : JS_FALSE) );
} else {
SMJS_SET_RVAL( BOOLEAN_TO_JSVAL( JS_FALSE) );
}
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_node_is_same_node)
{
GF_Node *n, *a_node;
SMJS_OBJ
SMJS_ARGS
if (!argc || !JSVAL_IS_OBJECT(argv[0])) return JS_TRUE;
n = dom_get_node(c, obj);
if (!n) return JS_TRUE;
a_node = dom_get_node(c, JSVAL_TO_OBJECT(argv[0]));
if (!a_node) return JS_TRUE;
SMJS_SET_RVAL( BOOLEAN_TO_JSVAL( (a_node==n) ? JS_TRUE : JS_FALSE) );
return JS_TRUE;
}
static const char *node_get_local_name(GF_Node *node)
{
const char *res;
GF_List *l;
if (!node) return NULL;
l = node->sgprivate->scenegraph->ns;
node->sgprivate->scenegraph->ns = NULL;
res = gf_node_get_class_name(node);
node->sgprivate->scenegraph->ns = l;
return res;
}
static const char *node_lookup_namespace_by_tag(GF_Node *node, u32 tag)
{
GF_DOMAttribute *att;
if (!node) return NULL;
att = ((SVG_Element*)node)->attributes;
while (att) {
if (att->tag==TAG_DOM_ATT_any) {
GF_DOMFullAttribute *datt = (GF_DOMFullAttribute*)att;
if (datt->name && !strncmp(datt->name, "xmlns", 5)) {
char *xmlns = *(DOM_String *) datt->data;
u32 crc = gf_crc_32(xmlns, (u32) strlen(xmlns));
if (tag==crc) return xmlns;
}
}
att = att->next;
}
return node_lookup_namespace_by_tag(gf_node_get_parent(node, 0), tag);
}
#ifdef GPAC_UNUSED_FUNC
static u32 get_namespace_code_by_prefix(GF_Node *node, char *prefix)
{
GF_DOMAttribute *att;
if (!node) return 0;
att = ((SVG_Element*)node)->attributes;
while (att) {
if (att->tag==TAG_DOM_ATT_any) {
GF_DOMFullAttribute *datt = (GF_DOMFullAttribute*)att;
if (!prefix) {
if (!strcmp(datt->name, "xmlns")) return datt->xmlns;
} else if (!strncmp(datt->name, "xmlns:", 6)) {
if (!strcmp(datt->name+6, prefix)) return datt->xmlns;
}
}
att = att->next;
}
return get_namespace_code_by_prefix(gf_node_get_parent(node, 0), prefix);
}
#endif
static SMJS_FUNC_PROP_GET( dom_node_getProperty)
u32 tag;
GF_Node *n;
GF_SceneGraph *sg = NULL;
GF_ParentNode *par;
n = dom_get_node(c, obj);
if (!n) {
sg = dom_get_doc(c, obj);
if (!sg) return JS_TRUE;
}
if (!SMJS_ID_IS_INT(id)) return JS_TRUE;
tag = n ? gf_node_get_tag(n) : 0;
par = (GF_ParentNode*)n;
switch (SMJS_ID_TO_INT(id)) {
case NODE_JSPROPERTY_NODENAME:
if (sg) {
*vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, "#document") );
}
else if (tag==TAG_DOMText) {
GF_DOMText *txt = (GF_DOMText *)n;
if (txt->type==GF_DOM_TEXT_CDATA) *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, "#cdata-section") );
else *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, "#text") );
}
else {
*vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, gf_node_get_class_name(n) ) );
}
return JS_TRUE;
case NODE_JSPROPERTY_NODEVALUE:
*vp = JSVAL_VOID;
if (tag==TAG_DOMText) {
GF_DOMText *txt = (GF_DOMText *)n;
*vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, txt->textContent) );
}
return JS_TRUE;
case NODE_JSPROPERTY_NODETYPE:
if (sg) *vp = INT_TO_JSVAL(9);
else if (tag==TAG_DOMText) {
GF_DOMText *txt = (GF_DOMText *)n;
if (txt->type==GF_DOM_TEXT_CDATA) *vp = INT_TO_JSVAL(4);
else *vp = INT_TO_JSVAL(3);
}
else *vp = INT_TO_JSVAL(1);
return JS_TRUE;
case NODE_JSPROPERTY_PARENTNODE:
if (sg) {
*vp = JSVAL_NULL;
}
else if (n->sgprivate->scenegraph->RootNode==n) {
*vp = dom_document_construct(c, n->sgprivate->scenegraph);
} else {
*vp = dom_node_construct(c, gf_node_get_parent(n, 0) );
}
return JS_TRUE;
case NODE_JSPROPERTY_CHILDNODES:
if (sg) *vp = JSVAL_VOID;
else if (tag==TAG_DOMText) *vp = JSVAL_NULL;
else *vp = dom_nodelist_construct(c, par);
return JS_TRUE;
case NODE_JSPROPERTY_FIRSTCHILD:
if (sg) *vp = dom_node_construct(c, sg->RootNode);
else if (tag==TAG_DOMText) *vp = JSVAL_NULL;
else if (!par->children) {
*vp = JSVAL_NULL;
}
else *vp = dom_node_construct(c, par->children->node);
return JS_TRUE;
case NODE_JSPROPERTY_LASTCHILD:
if (sg) *vp = dom_node_construct(c, sg->RootNode);
else if ((tag==TAG_DOMText) || !par->children) *vp = JSVAL_VOID;
else *vp = dom_node_construct(c, gf_node_list_get_child(par->children, -1) );
return JS_TRUE;
case NODE_JSPROPERTY_PREVIOUSSIBLING:
*vp = dom_node_get_sibling(c, n, GF_TRUE, GF_FALSE);
return JS_TRUE;
case NODE_JSPROPERTY_NEXTSIBLING:
*vp = dom_node_get_sibling(c, n, GF_FALSE, GF_FALSE);
return JS_TRUE;
case NODE_JSPROPERTY_ATTRIBUTES:
*vp = JSVAL_VOID;
return JS_TRUE;
case NODE_JSPROPERTY_OWNERDOCUMENT:
if (sg) *vp = JSVAL_NULL;
else *vp = dom_document_construct(c, n->sgprivate->scenegraph);
return JS_TRUE;
case NODE_JSPROPERTY_NAMESPACEURI:
*vp = JSVAL_NULL;
if (!sg) {
tag = gf_xml_get_element_namespace(n);
if (tag) {
const char *xmlns = gf_sg_get_namespace(n->sgprivate->scenegraph, tag);
if (!xmlns) xmlns = node_lookup_namespace_by_tag(n, tag);
*vp = xmlns ? STRING_TO_JSVAL( JS_NewStringCopyZ(c, xmlns) ) : JSVAL_VOID;
}
}
return JS_TRUE;
case NODE_JSPROPERTY_PREFIX:
if (sg) tag = gf_sg_get_namespace_code(sg, NULL);
else tag = gf_xml_get_element_namespace(n);
*vp = JSVAL_NULL;
if (tag) {
char *xmlns = (char *)gf_sg_get_namespace_qname(sg ? sg : n->sgprivate->scenegraph, tag);
if (xmlns) *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, xmlns) );
}
return JS_TRUE;
case NODE_JSPROPERTY_LOCALNAME:
*vp = JSVAL_NULL;
if (!sg && (tag!=TAG_DOMText)) {
*vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, node_get_local_name(n) ) );
}
return JS_TRUE;
case NODE_JSPROPERTY_BASEURI:
*vp = JSVAL_NULL;
return JS_TRUE;
case NODE_JSPROPERTY_TEXTCONTENT:
*vp = JSVAL_VOID;
if (!sg) {
char *res = gf_dom_flatten_textContent(n);
*vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, res) );
gf_free(res);
}
return JS_TRUE;
case NODE_JSPROPERTY_FIRSTELEMENTCHILD:
*vp = JSVAL_NULL;
if (n->sgprivate->tag!=TAG_DOMText) {
GF_ChildNodeItem *child = ((GF_ParentNode*)n)->children;
while (child) {
if (child->node->sgprivate->tag != TAG_DOMText) {
*vp = dom_element_construct(c, child->node);
break;
}
child = child->next;
}
}
return JS_TRUE;
case NODE_JSPROPERTY_LASTELEMENTCHILD:
*vp = JSVAL_NULL;
if (n->sgprivate->tag!=TAG_DOMText) {
GF_Node *last = NULL;
GF_ChildNodeItem *child = ((GF_ParentNode*)n)->children;
while (child) {
if (child->node->sgprivate->tag != TAG_DOMText) {
last = child->node;
}
child = child->next;
}
if (last) *vp = dom_element_construct(c, last);
}
return JS_TRUE;
case NODE_JSPROPERTY_PREVIOUSELEMENTSIBLING:
*vp = dom_node_get_sibling(c, n, GF_TRUE, GF_TRUE);
return JS_TRUE;
case NODE_JSPROPERTY_NEXTELEMENTSIBLING:
*vp = dom_node_get_sibling(c, n, GF_FALSE, GF_TRUE);
return JS_TRUE;
}
return JS_TRUE;
}
void dom_node_set_textContent(GF_Node *n, char *text)
{
GF_FieldInfo info;
gf_dom_set_textContent(n, text);
gf_node_dirty_set(n, GF_SG_CHILD_DIRTY, GF_FALSE);
memset(&info, 0, sizeof(GF_FieldInfo));
info.fieldIndex = (u32) -1;
gf_node_changed(n, &info);
}
static SMJS_FUNC_PROP_SET( dom_node_setProperty)
u32 tag;
GF_Node *n;
n = dom_get_node(c, obj);
if (!n) return JS_TRUE;
if (!SMJS_ID_IS_INT(id)) return JS_TRUE;
tag = n ? gf_node_get_tag(n) : 0;
switch (SMJS_ID_TO_INT(id)) {
case NODE_JSPROPERTY_NODEVALUE:
if ((tag==TAG_DOMText) && JSVAL_CHECK_STRING(*vp)) {
GF_DOMText *txt = (GF_DOMText *)n;
if (txt->textContent) gf_free(txt->textContent);
txt->textContent = js_get_utf8(c, *vp);
dom_node_changed(n, GF_TRUE, NULL);
}
return JS_TRUE;
case NODE_JSPROPERTY_PREFIX:
return JS_TRUE;
case NODE_JSPROPERTY_TEXTCONTENT:
{
char *txt;
txt = js_get_utf8(c, *vp);
dom_node_set_textContent(n, txt);
if (txt) gf_free(txt);
}
return JS_TRUE;
}
return JS_TRUE;
}
DECL_FINALIZE(dom_document_finalize)
GF_SceneGraph *sg;
sg = (GF_SceneGraph*) SMJS_GET_PRIVATE(c, obj);
if (!sg) return;
SMJS_SET_PRIVATE(c, sg->document, NULL);
sg->document = NULL;
if (sg->RootNode) {
gf_node_unregister(sg->RootNode, NULL);
if (sg->reference_count) {
sg->reference_count--;
if (!sg->reference_count)
gf_sg_del(sg);
}
}
}
static SMJS_FUNC_PROP_GET( dom_document_getProperty )
u32 prop_id;
GF_SceneGraph *sg = dom_get_doc(c, obj);
if (!sg) return JS_TRUE;
if (!SMJS_ID_IS_INT(id)) return JS_TRUE;
prop_id = SMJS_ID_TO_INT(id);
switch (prop_id) {
case DOCUMENT_JSPROPERTY_IMPLEMENTATION:
*vp = OBJECT_TO_JSVAL( JS_GetGlobalObject(c) );
return JS_TRUE;
case DOCUMENT_JSPROPERTY_DOCUMENTELEMENT:
*vp = dom_element_construct(c, sg->RootNode);
return JS_TRUE;
case DOCUMENT_JSPROPERTY_GLOBAL:
*vp = OBJECT_TO_JSVAL( JS_GetGlobalObject(c) );
return JS_TRUE;
case DOCUMENT_JSPROPERTY_DOCTYPE:
case DOCUMENT_JSPROPERTY_INPUTENCODING:
case DOCUMENT_JSPROPERTY_XMLENCODING:
case DOCUMENT_JSPROPERTY_XMLSTANDALONE:
case DOCUMENT_JSPROPERTY_XMLVERSION:
case DOCUMENT_JSPROPERTY_STRICTERRORCHECKING:
case DOCUMENT_JSPROPERTY_DOCUMENTURI:
case DOCUMENT_JSPROPERTY_LOCATION:
case DOCUMENT_JSPROPERTY_DOMCONFIG:
*vp = JSVAL_VOID;
return JS_TRUE;
}
return JS_TRUE;
}
static SMJS_FUNC_PROP_SET_NOVP(dom_document_setProperty)
u32 prop_id;
GF_SceneGraph *sg = dom_get_doc(c, obj);
if (!sg) return JS_TRUE;
if (!SMJS_ID_IS_INT(id)) return JS_TRUE;
prop_id = SMJS_ID_TO_INT(id);
switch (prop_id) {
case DOCUMENT_JSPROPERTY_XMLSTANDALONE:
break;
case DOCUMENT_JSPROPERTY_XMLVERSION:
break;
case DOCUMENT_JSPROPERTY_STRICTERRORCHECKING:
break;
case DOCUMENT_JSPROPERTY_DOCUMENTURI:
break;
case DOCUMENT_JSPROPERTY_DOMCONFIG:
break;
}
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_document_create_element)
{
u32 tag, ns;
GF_Node *n;
char *name, *xmlns;
SMJS_OBJ
SMJS_ARGS
GF_SceneGraph *sg = dom_get_doc(c, obj);
if (!sg || !argc || !JSVAL_CHECK_STRING(argv[0]) )
return JS_TRUE;
name = NULL;
ns = 0;
xmlns = NULL;
if (argc==2) {
if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE;
xmlns = js_get_utf8(c, argv[0]);
if (xmlns) ns = gf_sg_get_namespace_code_from_name(sg, xmlns);
name = SMJS_CHARS(c, argv[1]);
} else {
name = SMJS_CHARS(c, argv[0]);
}
if (name) {
tag = gf_xml_get_element_tag(name, ns);
if (!tag) tag = TAG_DOMFullNode;
n = gf_node_new(sg, tag);
if (n && (tag == TAG_DOMFullNode)) {
GF_DOMFullNode *elt = (GF_DOMFullNode *)n;
elt->name = gf_strdup(name);
if (xmlns) {
gf_sg_add_namespace(sg, xmlns, NULL);
elt->ns = gf_sg_get_namespace_code_from_name(sg, xmlns);
}
}
SMJS_SET_RVAL( dom_element_construct(c, n) );
}
if (xmlns) gf_free(xmlns);
SMJS_FREE(c, name);
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_document_create_text)
{
GF_Node *n;
SMJS_OBJ
GF_SceneGraph *sg = dom_get_doc(c, obj);
if (!sg) return JS_TRUE;
n = gf_node_new(sg, TAG_DOMText);
if (argc) {
SMJS_ARGS
char *str = js_get_utf8(c, argv[0]);
if (!str) str = gf_strdup("");
((GF_DOMText*)n)->textContent = str;
}
SMJS_SET_RVAL( dom_text_construct(c, n) );
return JS_TRUE;
}
static void xml_doc_gather_nodes(GF_ParentNode *node, char *name, DOMNodeList *nl)
{
Bool bookmark = GF_TRUE;
GF_ChildNodeItem *child;
const char *node_name;
if (!node) return;
if (name) {
node_name = gf_node_get_class_name((GF_Node*)node);
if (strcmp(node_name, name)) bookmark = GF_FALSE;
}
if (bookmark) {
gf_node_register((GF_Node*)node, NULL);
if (node->sgprivate->scenegraph->reference_count)
node->sgprivate->scenegraph->reference_count++;
gf_node_list_add_child(&nl->child, (GF_Node*)node);
}
if (node->sgprivate->tag<GF_NODE_FIRST_PARENT_NODE_TAG) return;
child = node->children;
while (child) {
xml_doc_gather_nodes((GF_ParentNode*)child->node, name, nl);
child = child->next;
}
}
static JSBool SMJS_FUNCTION(xml_document_elements_by_tag)
{
DOMNodeList *nl;
JSObject *new_obj;
char *name;
SMJS_OBJ
SMJS_ARGS
GF_SceneGraph *sg = dom_get_doc(c, obj);
if (!sg) return JS_TRUE;
if (!argc || !JSVAL_CHECK_STRING(argv[0])) return JS_TRUE;
if (argc==2) {
if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE;
name = SMJS_CHARS(c, argv[1]);
} else {
name = SMJS_CHARS(c, argv[0]);
}
GF_SAFEALLOC(nl, DOMNodeList);
if (name && !strcmp(name, "*")) name = NULL;
xml_doc_gather_nodes((GF_ParentNode*)sg->RootNode, name, nl);
new_obj = JS_NewObject(c, &dom_rt->domNodeListClass._class, 0, 0);
SMJS_SET_PRIVATE(c, new_obj, nl);
SMJS_SET_RVAL( OBJECT_TO_JSVAL(new_obj));
SMJS_FREE(c, name);
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_document_element_by_id)
{
NodeIDedItem *reg_node;
GF_Node *n;
char *id;
SMJS_OBJ
SMJS_ARGS
GF_SceneGraph *sg = dom_get_doc(c, obj);
if (!sg) return JS_FALSE;
if (!argc || !JSVAL_CHECK_STRING(argv[0])) return JS_TRUE;
id = SMJS_CHARS(c, argv[0]);
n = NULL;
reg_node = sg->id_node;
while (reg_node) {
if (reg_node->NodeName && !strcmp(reg_node->NodeName, id)) {
n = reg_node->node;
if (n && (n->sgprivate->scenegraph->RootNode!=n) && !n->sgprivate->parents) n = NULL;
else break;
}
reg_node = reg_node->next;
}
SMJS_SET_RVAL( dom_element_construct(c, n));
SMJS_FREE(c, id);
return JS_TRUE;
}
DECL_FINALIZE( dom_element_finalize)
dom_node_finalize(c, obj);
}
static SMJS_FUNC_PROP_GET( dom_element_getProperty)
u32 prop_id;
GF_Node *n = dom_get_node(c, obj);
if (!n) return JS_TRUE;
if (!SMJS_ID_IS_INT(id)) return JS_TRUE;
prop_id = SMJS_ID_TO_INT(id);
switch (prop_id) {
case ELEMENT_JSPROPERTY_TAGNAME:
*vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, gf_node_get_class_name(n) ) );
return JS_TRUE;
case ELEMENT_JSPROPERTY_SCHEMATYPEINFO:
*vp = JSVAL_VOID;
return JS_TRUE;
}
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_element_get_attribute)
{
char *name, *ns;
SMJS_OBJ
SMJS_ARGS
GF_Node *n = dom_get_node(c, obj);
if (!n) return JS_TRUE;
if (!argc || !JSVAL_CHECK_STRING(argv[0]))
return JS_TRUE;
name = ns = NULL;
if (argc==2) {
if (!JSVAL_CHECK_STRING(argv[1]))
return JS_TRUE;
ns = js_get_utf8(c, argv[0]);
name = SMJS_CHARS(c, argv[1]);
} else {
name = SMJS_CHARS(c, argv[0]);
}
if (!name) goto exit;
if (!strcmp(name, "id") || !strcmp(name, "xml:id") ) {
char *sID = (char *) gf_node_get_name(n);
if (sID) {
SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, sID ? sID : "") ) );
goto exit;
}
}
if (n->sgprivate->tag==TAG_DOMFullNode) {
GF_DOMFullNode *node = (GF_DOMFullNode*)n;
GF_DOMFullAttribute *att = (GF_DOMFullAttribute*)node->attributes;
while (att) {
if ((att->tag==TAG_DOM_ATT_any) && !strcmp(att->name, name)) {
SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, *(char**)att->data ) ));
goto exit;
}
att = (GF_DOMFullAttribute *) att->next;
}
}
else if (n->sgprivate->tag==TAG_DOMText) {
} else if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) {
GF_FieldInfo info;
u32 ns_code = 0;
if (ns) {
ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, ns);
if (!ns_code) ns_code = gf_crc_32(ns, (u32) strlen(ns));
}
else {
ns_code = gf_xml_get_element_namespace(n);
}
if (gf_node_get_attribute_by_name(n, name, ns_code, GF_FALSE, GF_FALSE, &info)==GF_OK) {
char *szAtt = gf_svg_dump_attribute(n, &info);
SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, szAtt) ) );
if (szAtt) gf_free(szAtt);
goto exit;
}
}
SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, "") ));
exit:
if (ns) gf_free(ns);
SMJS_FREE(c, name);
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_element_has_attribute)
{
char *name, *ns;
SMJS_OBJ
SMJS_ARGS
GF_Node *n = dom_get_node(c, obj);
if (!n)
return JS_TRUE;
if (!argc || !JSVAL_CHECK_STRING(argv[0])) return JS_TRUE;
name = ns = NULL;
if (argc==2) {
if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE;
ns = js_get_utf8(c, argv[0]);
name = SMJS_CHARS(c, argv[1]);
} else {
name = SMJS_CHARS(c, argv[0]);
}
if (!name) goto exit;
if (n->sgprivate->tag==TAG_DOMFullNode) {
GF_DOMFullNode *node = (GF_DOMFullNode*)n;
GF_DOMFullAttribute *att = (GF_DOMFullAttribute*)node->attributes;
while (att) {
if ((att->tag==TAG_DOM_ATT_any) && !strcmp(att->name, name)) {
SMJS_SET_RVAL( BOOLEAN_TO_JSVAL(JS_TRUE));
goto exit;
}
att = (GF_DOMFullAttribute *) att->next;
}
}
else if (n->sgprivate->tag==TAG_DOMText) {
}
else if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) {
GF_FieldInfo info;
u32 ns_code = 0;
if (ns) ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, ns);
else ns_code = gf_sg_get_namespace_code(n->sgprivate->scenegraph, NULL);
if (gf_node_get_attribute_by_name(n, name, ns_code, GF_FALSE, GF_FALSE, &info)==GF_OK) {
SMJS_SET_RVAL( BOOLEAN_TO_JSVAL(JS_TRUE) );
goto exit;
}
}
SMJS_SET_RVAL( BOOLEAN_TO_JSVAL(JS_FALSE) );
exit:
if (ns) gf_free(ns);
SMJS_FREE(c, name);
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_element_remove_attribute)
{
u32 tag;
GF_DOMFullNode *node;
GF_DOMFullAttribute *prev, *att;
char *name, *ns;
SMJS_OBJ
SMJS_ARGS
GF_Node *n = dom_get_node(c, obj);
if (!n) return JS_TRUE;
if (!argc || !JSVAL_CHECK_STRING(argv[0])) return JS_TRUE;
name = ns = NULL;
if (argc==2) {
if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE;
ns = js_get_utf8(c, argv[0]);
name = SMJS_CHARS(c, argv[1]);
} else {
name = SMJS_CHARS(c, argv[0]);
}
if (!name) goto exit;
tag = TAG_DOM_ATT_any;
node = (GF_DOMFullNode*)n;
prev = NULL;
att = (GF_DOMFullAttribute*)node->attributes;
if (n->sgprivate->tag==TAG_DOMFullNode) tag = TAG_DOM_ATT_any;
else if (n->sgprivate->tag==TAG_DOMText) {
goto exit;
} else if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) {
u32 ns_code = 0;
if (ns) ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, ns);
else ns_code = gf_sg_get_namespace_code(n->sgprivate->scenegraph, NULL);
tag = gf_xml_get_attribute_tag(n, name, ns_code);
}
while (att) {
if ((att->tag==TAG_DOM_ATT_any) && !strcmp(att->name, name)) {
if (prev) prev->next = att->next;
else node->attributes = att->next;
if (att->data) gf_free(att->data);
gf_free(att->name);
gf_free(att);
dom_node_changed(n, GF_FALSE, NULL);
goto exit;
} else if (tag==att->tag) {
if (prev) prev->next = att->next;
else node->attributes = att->next;
gf_svg_delete_attribute_value(att->data_type, att->data, n->sgprivate->scenegraph);
gf_free(att);
dom_node_changed(n, GF_FALSE, NULL);
goto exit;
}
prev = att;
att = (GF_DOMFullAttribute *) att->next;
}
exit:
if (ns) gf_free(ns);
SMJS_FREE(c, name);
return JS_TRUE;
}
static void gf_dom_add_handler_listener(GF_Node *n, u32 evtType, char *handlerCode)
{
SVG_handlerElement *handler;
u32 i, count = gf_dom_listener_count(n);
for (i=0; i<count; i++) {
GF_FieldInfo info;
GF_DOMText *text;
GF_Node *listen = gf_dom_listener_get(n, i);
gf_node_get_attribute_by_tag(listen, TAG_XMLEV_ATT_event, GF_FALSE, GF_FALSE, &info);
if (!info.far_ptr || (((XMLEV_Event*)info.far_ptr)->type != evtType)) continue;
gf_node_get_attribute_by_tag(listen, TAG_XMLEV_ATT_handler, GF_FALSE, GF_FALSE, &info);
assert(info.far_ptr);
handler = (SVG_handlerElement *) ((XMLRI*)info.far_ptr)->target;
text = (GF_DOMText*)handler->children->node;
if (text->sgprivate->tag==TAG_DOMText) {
if (text->textContent) gf_free(text->textContent);
text->textContent = gf_strdup(handlerCode);
}
return;
}
handler = gf_dom_listener_build(n, evtType, 0);
gf_dom_add_text_node((GF_Node*)handler, gf_strdup(handlerCode));
return;
}
static void gf_dom_full_set_attribute(GF_DOMFullNode *node, char *attribute_name, char *attribute_content)
{
GF_DOMFullAttribute *prev = NULL;
GF_DOMFullAttribute *att = (GF_DOMFullAttribute*)node->attributes;
while (att) {
if ((att->tag==TAG_DOM_ATT_any) && !strcmp(att->name, attribute_name)) {
if (att->data) gf_free(att->data);
att->data = gf_strdup(attribute_content);
dom_node_changed((GF_Node *)node, GF_FALSE, NULL);
return;
}
prev = att;
att = (GF_DOMFullAttribute *) att->next;
}
GF_SAFEALLOC(att, GF_DOMFullAttribute);
if (!att) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOMJS] Failed to allocate DOM attribute\n"));
return;
}
att->name = gf_strdup(attribute_name);
att->data = gf_strdup(attribute_content);
if (prev) prev->next = (GF_DOMAttribute*) att;
else node->attributes = (GF_DOMAttribute*) att;
return;
}
void gf_svg_set_attributeNS(GF_Node *n, u32 ns_code, char *name, char *val)
{
GF_FieldInfo info;
u32 anim_value_type = 0;
if (!strcmp(name, "attributeName")) {
if (gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_attributeName, GF_FALSE, GF_FALSE, &info) == GF_OK) {
SMIL_AttributeName *attname = (SMIL_AttributeName *)info.far_ptr;
if (!attname->type) {
char *sep;
char *name = attname->name;
sep = strchr(name, ':');
if (sep) {
sep[0] = 0;
attname->type = gf_sg_get_namespace_code(n->sgprivate->scenegraph, name);
sep[0] = ':';
name = gf_strdup(sep+1);
gf_free(attname->name);
attname->name = name;
}
}
}
}
if ((n->sgprivate->tag == TAG_SVG_animateTransform) && (strstr(name, "from") || strstr(name, "to")) ) {
if (gf_node_get_attribute_by_tag((GF_Node *)n, TAG_SVG_ATT_transform_type, GF_TRUE, GF_FALSE, &info) != GF_OK) {
GF_LOG(GF_LOG_WARNING, GF_LOG_SCRIPT, ("Cannot retrieve attribute 'type' from animateTransform\n"));
return;
}
switch(*(SVG_TransformType *) info.far_ptr) {
case SVG_TRANSFORM_TRANSLATE:
anim_value_type = SVG_Transform_Translate_datatype;
break;
case SVG_TRANSFORM_SCALE:
anim_value_type = SVG_Transform_Scale_datatype;
break;
case SVG_TRANSFORM_ROTATE:
anim_value_type = SVG_Transform_Rotate_datatype;
break;
case SVG_TRANSFORM_SKEWX:
anim_value_type = SVG_Transform_SkewX_datatype;
break;
case SVG_TRANSFORM_SKEWY:
anim_value_type = SVG_Transform_SkewY_datatype;
break;
case SVG_TRANSFORM_MATRIX:
anim_value_type = SVG_Transform_datatype;
break;
default:
return;
}
}
if (gf_node_get_attribute_by_name(n, name, ns_code, GF_TRUE, GF_TRUE, &info)==GF_OK) {
GF_Err e;
if (!strcmp(name, "from") || !strcmp(name, "to") || !strcmp(name, "values") ) {
GF_FieldInfo attType;
SMIL_AttributeName *attname;
if (gf_node_get_attribute_by_tag((GF_Node *)n, TAG_SVG_ATT_attributeName, GF_FALSE, GF_FALSE, &attType) != GF_OK) {
GF_LOG(GF_LOG_WARNING, GF_LOG_SCRIPT, ("Cannot retrieve attribute 'attributeName'\n"));
return;
}
attname = (SMIL_AttributeName *)attType.far_ptr;
if (!attname->type && attname->name) {
GF_Node *anim_target = gf_smil_anim_get_target(n);
if (anim_target) {
gf_node_get_attribute_by_name((GF_Node *)anim_target, attname->name, attname->type, GF_FALSE, GF_FALSE, &attType);
attname->type = attType.fieldType;
} else {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOM] Cannot find target of the animation to parse attribute %s\n", attname->name));
}
}
anim_value_type = attname->type;
}
e = gf_svg_parse_attribute(n, &info, val, anim_value_type);
if (e != GF_OK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOM] Error parsing attribute\n"));
}
if (info.fieldType==SVG_ID_datatype) {
char *idname = *(SVG_String*)info.far_ptr;
gf_svg_parse_element_id(n, idname, GF_FALSE);
}
if (info.fieldType==XMLRI_datatype) {
gf_node_dirty_set(n, GF_SG_SVG_XLINK_HREF_DIRTY, GF_FALSE);
}
dom_node_changed(n, GF_FALSE, &info);
return;
}
}
void gf_svg_set_attribute(GF_Node *n, char * ns, char *name, char *val)
{
u32 ns_code = 0;
if (ns) {
ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, ns);
} else {
ns_code = gf_xml_get_element_namespace(n);
}
gf_svg_set_attributeNS(n, ns_code, name, val);
}
JSBool SMJS_FUNCTION(xml_element_set_attribute)
{
u32 idx;
char *name, *val, *ns, *_val;
char szVal[100];
SMJS_OBJ
SMJS_ARGS
GF_Node *n = dom_get_node(c, obj);
if (!n) return JS_TRUE;
if ((argc < 2)) return JS_TRUE;
if (!JSVAL_CHECK_STRING(argv[0]))
return JS_TRUE;
idx = 1;
_val = name = ns = NULL;
if (argc==3) {
char *sep;
if (!JSVAL_CHECK_STRING(argv[1]))
return JS_TRUE;
ns = js_get_utf8(c, argv[0]);
gf_sg_add_namespace(n->sgprivate->scenegraph, ns, NULL);
name = SMJS_CHARS(c, argv[1]);
idx = 2;
sep = strchr(name, ':');
if (sep) name = sep+1;
} else {
name = SMJS_CHARS(c, argv[0]);
}
val = NULL;
if (JSVAL_CHECK_STRING(argv[idx])) {
val = _val = SMJS_CHARS(c, argv[idx]);
} else if (JSVAL_IS_INT(argv[idx])) {
sprintf(szVal, "%d", JSVAL_TO_INT(argv[idx]));
val = szVal;
} else if (JSVAL_IS_NUMBER(argv[idx])) {
jsdouble d;
JS_ValueToNumber(c, argv[idx], &d);
sprintf(szVal, "%g", d);
val = szVal;
} else if (JSVAL_IS_BOOLEAN(argv[idx])) {
sprintf(szVal, "%s", JSVAL_TO_BOOLEAN(argv[idx]) ? "true" : "false");
val = szVal;
} else {
goto exit;
}
if (!name || !val)
goto exit;
if ((name[0]=='o') && (name[1]=='n')) {
u32 evtType = gf_dom_event_type_by_name(name + 2);
if (evtType != GF_EVENT_UNKNOWN) {
gf_dom_add_handler_listener(n, evtType, val);
goto exit;
}
}
if (n->sgprivate->tag==TAG_DOMFullNode) {
gf_dom_full_set_attribute((GF_DOMFullNode*)n, name, val);
goto exit;
}
if (n->sgprivate->tag==TAG_DOMText) {
goto exit;
}
if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) {
gf_svg_set_attribute(n, ns, name, val);
}
exit:
if (ns) gf_free(ns);
SMJS_FREE(c, name);
SMJS_FREE(c, _val);
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_element_elements_by_tag)
{
DOMNodeList *nl;
JSObject *new_obj;
char *name;
SMJS_OBJ
SMJS_ARGS
GF_Node *n = dom_get_node(c, obj);
if (!n) return JS_TRUE;
if (!argc || !JSVAL_CHECK_STRING(argv[0])) return JS_TRUE;
if (argc==2) {
if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE;
name = SMJS_CHARS(c, argv[1]);
} else {
name = SMJS_CHARS(c, argv[0]);
}
GF_SAFEALLOC(nl, DOMNodeList);
if (name && !strcmp(name, "*")) {
SMJS_FREE(c, name);
name = NULL;
}
xml_doc_gather_nodes((GF_ParentNode*)n, name, nl);
new_obj = JS_NewObject(c, &dom_rt->domNodeListClass._class, 0, 0);
SMJS_SET_PRIVATE(c, new_obj, nl);
SMJS_SET_RVAL( OBJECT_TO_JSVAL(new_obj) );
SMJS_FREE(c, name);
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_element_set_id)
{
u32 node_id;
char *name;
Bool is_id;
SMJS_OBJ
SMJS_ARGS
GF_Node *n = dom_get_node(c, obj);
if (!n) return JS_TRUE;
if ((argc<2) || !JSVAL_CHECK_STRING(argv[0])) return JS_TRUE;
if (argc==2) {
if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE;
name = SMJS_CHARS(c, argv[1]);
is_id = (JSVAL_TO_BOOLEAN(argv[2])==JS_TRUE) ? GF_TRUE : GF_FALSE;
} else {
name = SMJS_CHARS(c, argv[0]);
is_id = (JSVAL_TO_BOOLEAN(argv[1])==JS_TRUE) ? GF_TRUE : GF_FALSE;
}
gf_node_get_name_and_id(n, &node_id);
if (node_id && is_id) {
SMJS_FREE(c, name);
return JS_TRUE;
}
if (is_id) {
if (!name) return JS_TRUE;
gf_node_set_id(n, gf_sg_get_max_node_id(n->sgprivate->scenegraph) + 1, gf_strdup(name) );
} else if (node_id) {
gf_node_remove_id(n);
}
SMJS_FREE(c, name);
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET( dom_text_getProperty)
u32 prop_id;
GF_DOMText *txt = (GF_DOMText*)dom_get_node(c, obj);
if (!txt || (txt->sgprivate->tag != TAG_DOMText)) return JS_TRUE;
if (!SMJS_ID_IS_INT(id)) return JS_TRUE;
prop_id = SMJS_ID_TO_INT(id);
switch (prop_id) {
case TEXT_JSPROPERTY_DATA:
if (txt->textContent) *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, txt->textContent ) );
else *vp = STRING_TO_JSVAL( JS_NewStringCopyZ(c, "") );
return JS_TRUE;
case TEXT_JSPROPERTY_LENGTH:
*vp = INT_TO_JSVAL(txt->textContent ? strlen(txt->textContent) : 0);
return JS_TRUE;
case TEXT_JSPROPERTY_ISELEMENTCONTENTWHITESPACE:
*vp = BOOLEAN_TO_JSVAL(JS_FALSE);
return JS_TRUE;
case TEXT_JSPROPERTY_WHOLETEXT:
*vp = INT_TO_JSVAL(txt->textContent ? strlen(txt->textContent) : 0);
return JS_TRUE;
}
return JS_TRUE;
}
static SMJS_FUNC_PROP_SET( dom_text_setProperty)
u32 prop_id;
GF_DOMText *txt = (GF_DOMText*)dom_get_node(c, obj);
if (!txt || (txt->sgprivate->tag != TAG_DOMText)) return JS_TRUE;
if (!SMJS_ID_IS_INT(id)) return JS_TRUE;
prop_id = SMJS_ID_TO_INT(id);
switch (prop_id) {
case TEXT_JSPROPERTY_DATA:
if (txt->textContent) gf_free(txt->textContent);
txt->textContent = NULL;
if (JSVAL_CHECK_STRING(*vp)) {
char *str = js_get_utf8(c, *vp);
txt->textContent = str ? str : gf_strdup("" );
}
dom_node_changed((GF_Node*)txt, GF_FALSE, NULL);
return JS_TRUE;
}
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(event_stop_propagation)
{
SMJS_OBJ
GF_DOM_Event *evt = (GF_DOM_Event *)SMJS_GET_PRIVATE(c, obj);
if (!evt) return JS_TRUE;
evt->event_phase |= GF_DOM_EVENT_PHASE_CANCEL;
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(event_stop_immediate_propagation)
{
SMJS_OBJ
GF_DOM_Event *evt = (GF_DOM_Event *)SMJS_GET_PRIVATE(c, obj);
if (!evt) return JS_TRUE;
evt->event_phase |= GF_DOM_EVENT_PHASE_CANCEL_ALL;
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(event_prevent_default)
{
SMJS_OBJ
GF_DOM_Event *evt = (GF_DOM_Event *)SMJS_GET_PRIVATE(c, obj);
if (!evt) return JS_TRUE;
evt->event_phase |= GF_DOM_EVENT_PHASE_PREVENT;
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET( event_getProperty)
JSString *s;
GF_DOM_Event *evt = (GF_DOM_Event *)SMJS_GET_PRIVATE(c, obj);
if (evt==NULL) return JS_TRUE;
if (SMJS_ID_IS_INT(id)) {
switch (SMJS_ID_TO_INT(id)) {
case EVENT_JSPROPERTY_TYPE:
s = JS_NewStringCopyZ(c, gf_dom_event_get_name(evt->type) );
*vp = STRING_TO_JSVAL( s );
break;
case EVENT_JSPROPERTY_TARGET:
if (evt->is_vrml) return JS_TRUE;
switch (evt->target_type) {
case GF_DOM_EVENT_TARGET_NODE:
*vp = dom_element_construct(c, (GF_Node*) evt->target);
break;
case GF_DOM_EVENT_TARGET_DOCUMENT:
*vp = dom_document_construct(c, (GF_SceneGraph *) evt->target);
break;
case GF_DOM_EVENT_TARGET_MSE_MEDIASOURCE:
*vp = OBJECT_TO_JSVAL(((GF_HTML_MediaSource *)evt->target)->_this);
break;
case GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFER:
*vp = OBJECT_TO_JSVAL(((GF_HTML_SourceBuffer *)evt->target)->_this);
break;
case GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFERLIST:
*vp = OBJECT_TO_JSVAL(((GF_HTML_SourceBufferList *)evt->target)->_this);
break;
default:
break;
}
return JS_TRUE;
case EVENT_JSPROPERTY_CURRENTTARGET:
if (evt->is_vrml) return JS_TRUE;
switch (evt->currentTarget->ptr_type) {
case GF_DOM_EVENT_TARGET_NODE:
*vp = dom_element_construct(c, (GF_Node*) evt->currentTarget->ptr);
break;
case GF_DOM_EVENT_TARGET_DOCUMENT:
*vp = dom_document_construct(c, (GF_SceneGraph *) evt->currentTarget->ptr);
break;
case GF_DOM_EVENT_TARGET_MSE_MEDIASOURCE:
*vp = OBJECT_TO_JSVAL(((GF_HTML_MediaSource *)evt->target)->_this);
break;
case GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFER:
*vp = OBJECT_TO_JSVAL(((GF_HTML_SourceBuffer *)evt->target)->_this);
break;
case GF_DOM_EVENT_TARGET_MSE_SOURCEBUFFERLIST:
*vp = OBJECT_TO_JSVAL(((GF_HTML_SourceBufferList *)evt->target)->_this);
break;
default:
break;
}
return JS_TRUE;
case EVENT_JSPROPERTY_EVENTPHASE:
*vp = INT_TO_JSVAL( (evt->event_phase & 0x3) );
return JS_TRUE;
case EVENT_JSPROPERTY_BUBBLES:
*vp = BOOLEAN_TO_JSVAL(evt->bubbles ? JS_TRUE : JS_FALSE);
return JS_TRUE;
case EVENT_JSPROPERTY_CANCELABLE:
*vp = BOOLEAN_TO_JSVAL(evt->cancelable ? JS_TRUE : JS_FALSE);
return JS_TRUE;
case EVENT_JSPROPERTY_NAMESPACEURI:
*vp = JSVAL_NULL;
return JS_TRUE;
case EVENT_JSPROPERTY_TIMESTAMP:
*vp = JSVAL_VOID;
return JS_TRUE;
case EVENT_JSPROPERTY_DEFAULTPREVENTED:
*vp = BOOLEAN_TO_JSVAL((evt->event_phase & GF_DOM_EVENT_PHASE_PREVENT) ? JS_TRUE : JS_FALSE);
return JS_TRUE;
case EVENT_JSPROPERTY_DETAIL:
*vp = INT_TO_JSVAL(evt->detail);
return JS_TRUE;
case EVENT_JSPROPERTY_DATA:
{
u32 len;
s16 txt[2];
const u16 *srcp;
char szData[5];;
txt[0] = evt->detail;
txt[1] = 0;
srcp = (const u16 *) txt;
len = (u32) gf_utf8_wcstombs(szData, 5, &srcp);
szData[len] = 0;
s = JS_NewStringCopyZ(c, szData);
*vp = STRING_TO_JSVAL( s );
}
return JS_TRUE;
case EVENT_JSPROPERTY_SCREENX:
*vp = INT_TO_JSVAL(evt->screenX);
return JS_TRUE;
case EVENT_JSPROPERTY_SCREENY:
*vp = INT_TO_JSVAL(evt->screenY);
return JS_TRUE;
case EVENT_JSPROPERTY_CLIENTX:
*vp = INT_TO_JSVAL(evt->clientX);
return JS_TRUE;
case EVENT_JSPROPERTY_CLIENTY:
*vp = INT_TO_JSVAL(evt->clientY);
return JS_TRUE;
case EVENT_JSPROPERTY_BUTTON:
*vp = INT_TO_JSVAL(evt->button);
return JS_TRUE;
case EVENT_JSPROPERTY_RELATEDTARGET:
if (evt->is_vrml) return JS_TRUE;
*vp = dom_element_construct(c, evt->relatedTarget);
return JS_TRUE;
case EVENT_JSPROPERTY_WHEELDELTA:
*vp = INT_TO_JSVAL(FIX2INT(evt->new_scale) );
return JS_TRUE;
case EVENT_JSPROPERTY_KEYIDENTIFIER:
s = JS_NewStringCopyZ(c, gf_dom_get_key_name(evt->detail) );
*vp = STRING_TO_JSVAL( s );
return JS_TRUE;
case EVENT_JSPROPERTY_KEYCHAR:
case EVENT_JSPROPERTY_CHARCODE:
*vp = INT_TO_JSVAL(evt->detail);
return JS_TRUE;
case EVENT_JSPROPERTY_LOADED:
*vp = INT_TO_JSVAL( evt->media_event.loaded_size);
return JS_TRUE;
case EVENT_JSPROPERTY_TOTAL:
*vp = INT_TO_JSVAL( evt->media_event.total_size);
return JS_TRUE;
case EVENT_JSPROPERTY_BUFFER_ON:
*vp = BOOLEAN_TO_JSVAL( evt->media_event.bufferValid ? JS_TRUE : JS_FALSE);
return JS_TRUE;
case EVENT_JSPROPERTY_BUFFERLEVEL:
*vp = INT_TO_JSVAL( evt->media_event.level);
return JS_TRUE;
case EVENT_JSPROPERTY_BUFFERREMAININGTIME:
*vp = JS_MAKE_DOUBLE(c, evt->media_event.remaining_time);
return JS_TRUE;
case EVENT_JSPROPERTY_STATUS:
*vp = INT_TO_JSVAL( evt->media_event.status);
return JS_TRUE;
case EVENT_JSPROPERTY_WIDTH:
*vp = JS_MAKE_DOUBLE(c, FIX2FLT(evt->screen_rect.width) );
return JS_TRUE;
case EVENT_JSPROPERTY_HEIGHT:
*vp = JS_MAKE_DOUBLE(c, FIX2FLT(evt->screen_rect.height) );
return JS_TRUE;
case EVENT_JSPROPERTY_OFFSETX:
*vp = JS_MAKE_DOUBLE(c, FIX2FLT(evt->screen_rect.x) );
return JS_TRUE;
case EVENT_JSPROPERTY_OFFSETY:
*vp = JS_MAKE_DOUBLE(c, FIX2FLT(evt->screen_rect.y) );
return JS_TRUE;
case EVENT_JSPROPERTY_VPWIDTH:
*vp = JS_MAKE_DOUBLE(c, FIX2FLT(evt->prev_translate.x) );
return JS_TRUE;
case EVENT_JSPROPERTY_VPHEIGHT:
*vp = JS_MAKE_DOUBLE(c, FIX2FLT(evt->prev_translate.y) );
return JS_TRUE;
case EVENT_JSPROPERTY_TRANSLATIONX:
*vp = JS_MAKE_DOUBLE(c, FIX2FLT(evt->new_translate.x) );
return JS_TRUE;
case EVENT_JSPROPERTY_TRANSLATIONY:
*vp = JS_MAKE_DOUBLE(c, FIX2FLT(evt->new_translate.y) );
return JS_TRUE;
case EVENT_JSPROPERTY_TYPE3D:
*vp = INT_TO_JSVAL(evt->detail);
return JS_TRUE;
case EVENT_JSPROPERTY_ERROR:
*vp = INT_TO_JSVAL(evt->error_state);
return JS_TRUE;
case EVENT_JSPROPERTY_DYNAMIC_SCENE:
*vp = INT_TO_JSVAL(evt->key_flags);
return JS_TRUE;
case EVENT_JSPROPERTY_URL:
s = JS_NewStringCopyZ(c, evt->addon_url ? evt->addon_url : "");
*vp = STRING_TO_JSVAL( s );
return JS_TRUE;
default:
return JS_TRUE;
}
}
return JS_TRUE;
}
typedef enum {
XHR_ONABORT = -1,
XHR_ONERROR = -2,
XHR_ONLOAD = -3,
XHR_ONLOADEND = -4,
XHR_ONLOADSTART = -5,
XHR_ONPROGRESS = -6,
XHR_ONREADYSTATECHANGE = -7,
XHR_ONTIMEOUT = -8,
XHR_READYSTATE = -9,
XHR_RESPONSE = -10,
XHR_RESPONSETYPE = -11,
XHR_RESPONSETEXT = -12,
XHR_RESPONSEXML = -13,
XHR_STATUS = -14,
XHR_STATUSTEXT = -15,
XHR_TIMEOUT = -16,
XHR_UPLOAD = -17,
XHR_WITHCREDENTIALS = -18,
XHR_STATIC_UNSENT = -19,
XHR_STATIC_OPENED = -20,
XHR_STATIC_HEADERS_RECEIVED = -21,
XHR_STATIC_LOADING = -22,
XHR_STATIC_DONE = -23,
XHR_CACHE = -24
} XHR_JSProperty;
typedef enum {
XHR_READYSTATE_UNSENT = 0,
XHR_READYSTATE_OPENED = 1,
XHR_READYSTATE_HEADERS_RECEIVED = 2,
XHR_READYSTATE_LOADING = 3,
XHR_READYSTATE_DONE = 4
} XHR_ReadyState;
typedef enum {
XHR_RESPONSETYPE_NONE,
XHR_RESPONSETYPE_ARRAYBUFFER,
XHR_RESPONSETYPE_BLOB,
XHR_RESPONSETYPE_DOCUMENT,
XHR_RESPONSETYPE_JSON,
XHR_RESPONSETYPE_TEXT,
XHR_RESPONSETYPE_STREAM
} XHR_ResponseType;
typedef enum {
XHR_CACHETYPE_NORMAL,
XHR_CACHETYPE_NONE,
XHR_CACHETYPE_MEMORY,
} XHR_CacheType;
struct __xhr_context
{
JSContext *c;
JSObject *_this;
jsval onabort;
jsval onerror;
jsval onreadystatechange;
jsval onload;
jsval onloadstart;
jsval onloadend;
jsval onprogress;
jsval ontimeout;
XHR_ReadyState readyState;
Bool async;
XHR_CacheType cache;
char **headers;
u32 cur_header;
char **recv_headers;
char *method, *url;
GF_DownloadSession *sess;
char *data;
u32 size;
JSObject *arraybuffer;
GF_Err ret_code;
u32 html_status;
char *statusText;
char *mime;
u32 timeout;
XHR_ResponseType responseType;
Bool withCredentials;
Bool isFile;
GF_SAXParser *sax;
GF_List *node_stack;
GF_DOMEventTarget *event_target;
GF_SceneGraph *owning_graph;
GF_SceneGraph *document;
};
GF_SceneGraph *xml_http_get_scenegraph(XMLHTTPContext *ctx)
{
return ctx->owning_graph;
}
GF_DOMEventTarget *xml_http_get_event_target(XMLHTTPContext *ctx)
{
return ctx->event_target;
}
static void xml_http_reset_recv_hdr(XMLHTTPContext *ctx)
{
u32 nb_hdr = 0;
if (ctx->recv_headers) {
while (ctx->recv_headers[nb_hdr]) {
gf_free(ctx->recv_headers[nb_hdr]);
gf_free(ctx->recv_headers[nb_hdr+1]);
nb_hdr+=2;
}
gf_free(ctx->recv_headers);
ctx->recv_headers = NULL;
}
}
static void xml_http_append_recv_header(XMLHTTPContext *ctx, const char *hdr, const char *val)
{
u32 nb_hdr = 0;
if (ctx->recv_headers) {
while (ctx->recv_headers[nb_hdr]) nb_hdr+=2;
}
ctx->recv_headers = (char **)gf_realloc(ctx->recv_headers, sizeof(char*)*(nb_hdr+3));
ctx->recv_headers[nb_hdr] = gf_strdup(hdr);
ctx->recv_headers[nb_hdr+1] = gf_strdup(val ? val : "");
ctx->recv_headers[nb_hdr+2] = NULL;
}
static void xml_http_append_send_header(XMLHTTPContext *ctx, char *hdr, char *val)
{
if (!hdr) return;
if (ctx->headers) {
u32 nb_hdr = 0;
while (ctx->headers && ctx->headers[nb_hdr]) {
if (stricmp(ctx->headers[nb_hdr], hdr)) {
nb_hdr+=2;
continue;
}
if (!stricmp(hdr, "Accept-Charset")
|| !stricmp(hdr, "Accept-Encoding")
|| !stricmp(hdr, "Content-Length")
|| !stricmp(hdr, "Expect")
|| !stricmp(hdr, "Date")
|| !stricmp(hdr, "Host")
|| !stricmp(hdr, "Keep-Alive")
|| !stricmp(hdr, "Referer")
|| !stricmp(hdr, "TE")
|| !stricmp(hdr, "Trailer")
|| !stricmp(hdr, "Transfer-Encoding")
|| !stricmp(hdr, "Upgrade")
) {
return;
}
if (!stricmp(hdr, "Authorization")
|| !stricmp(hdr, "Content-Base")
|| !stricmp(hdr, "Content-Location")
|| !stricmp(hdr, "Content-MD5")
|| !stricmp(hdr, "Content-Range")
|| !stricmp(hdr, "Content-Type")
|| !stricmp(hdr, "Content-Version")
|| !stricmp(hdr, "Delta-Base")
|| !stricmp(hdr, "Depth")
|| !stricmp(hdr, "Destination")
|| !stricmp(hdr, "ETag")
|| !stricmp(hdr, "From")
|| !stricmp(hdr, "If-Modified-Since")
|| !stricmp(hdr, "If-Range")
|| !stricmp(hdr, "If-Unmodified-Since")
|| !stricmp(hdr, "Max-Forwards")
|| !stricmp(hdr, "MIME-Version")
|| !stricmp(hdr, "Overwrite")
|| !stricmp(hdr, "Proxy-Authorization")
|| !stricmp(hdr, "SOAPAction")
|| !stricmp(hdr, "Timeout") ) {
gf_free(ctx->headers[nb_hdr+1]);
ctx->headers[nb_hdr+1] = gf_strdup(val);
return;
}
else {
char *new_val = (char *)gf_malloc(sizeof(char) * (strlen(ctx->headers[nb_hdr+1])+strlen(val)+3));
sprintf(new_val, "%s, %s", ctx->headers[nb_hdr+1], val);
gf_free(ctx->headers[nb_hdr+1]);
ctx->headers[nb_hdr+1] = new_val;
return;
}
nb_hdr+=2;
}
}
xml_http_append_recv_header(ctx, hdr, val);
}
void xhr_del_array_buffer(void *udta)
{
if (udta) {
((XMLHTTPContext *)udta)->arraybuffer = NULL;
((XMLHTTPContext *)udta)->data = NULL;
}
}
static void xml_http_del_data(XMLHTTPContext *ctx)
{
if (ctx->data) {
if (ctx->arraybuffer) {
if (ctx->arraybuffer) {
JS_SetParent(ctx->c, ctx->arraybuffer, NULL);
ctx->arraybuffer = NULL;
}
} else {
gf_free(ctx->data);
}
ctx->data = NULL;
ctx->size = 0;
}
}
static void xml_http_reset_partial(XMLHTTPContext *ctx)
{
xml_http_reset_recv_hdr(ctx);
xml_http_del_data(ctx);
if (ctx->mime) {
gf_free(ctx->mime);
ctx->mime = NULL;
}
if (ctx->statusText) {
gf_free(ctx->statusText);
ctx->statusText = NULL;
}
ctx->cur_header = 0;
ctx->html_status = 0;
}
static void xml_http_reset(XMLHTTPContext *ctx)
{
if (ctx->method) {
gf_free(ctx->method);
ctx->method = NULL;
}
if (ctx->url) {
gf_free(ctx->url);
ctx->url = NULL;
}
xml_http_reset_partial(ctx);
if (ctx->sess) {
GF_DownloadSession *tmp = ctx->sess;
ctx->sess = NULL;
gf_dm_sess_abort(tmp);
gf_dm_sess_del(tmp);
}
if (ctx->url) {
gf_free(ctx->url);
ctx->url = NULL;
}
if (ctx->sax) {
gf_xml_sax_del(ctx->sax);
ctx->sax = NULL;
}
if (ctx->node_stack) {
gf_list_del(ctx->node_stack);
ctx->node_stack = NULL;
}
if (ctx->document) {
gf_node_unregister(ctx->document->RootNode, NULL);
ctx->document->reference_count--;
if (!ctx->document->reference_count) {
gf_sg_js_dom_pre_destroy(ctx->c, ctx->document, NULL);
gf_sg_del(ctx->document);
}
}
ctx->document = NULL;
ctx->size = 0;
ctx->async = GF_FALSE;
ctx->readyState = XHR_READYSTATE_UNSENT;
ctx->ret_code = GF_OK;
}
static DECL_FINALIZE(xml_http_finalize)
XMLHTTPContext *ctx;
if (!GF_JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return;
ctx = (XMLHTTPContext *)SMJS_GET_PRIVATE(c, obj);
if (ctx) {
if (! JSVAL_IS_NULL(ctx->onabort)) gf_js_remove_root(c, &(ctx->onabort), GF_JSGC_VAL);
if (! JSVAL_IS_NULL(ctx->onerror)) gf_js_remove_root(c, &(ctx->onerror), GF_JSGC_VAL);
if (! JSVAL_IS_NULL(ctx->onload)) gf_js_remove_root(c, &(ctx->onload), GF_JSGC_VAL);
if (! JSVAL_IS_NULL(ctx->onloadend)) gf_js_remove_root(c, &(ctx->onloadend), GF_JSGC_VAL);
if (! JSVAL_IS_NULL(ctx->onloadstart)) gf_js_remove_root(c, &(ctx->onloadstart), GF_JSGC_VAL);
if (! JSVAL_IS_NULL(ctx->onprogress)) gf_js_remove_root(c, &(ctx->onprogress), GF_JSGC_VAL);
if (! JSVAL_IS_NULL(ctx->onreadystatechange)) gf_js_remove_root(c, &(ctx->onreadystatechange), GF_JSGC_VAL);
if (! JSVAL_IS_NULL(ctx->ontimeout)) gf_js_remove_root(c, &(ctx->ontimeout), GF_JSGC_VAL);
xml_http_reset(ctx);
gf_dom_event_target_del(ctx->event_target);
ctx->event_target = NULL;
gf_free(ctx);
}
}
static JSBool SMJS_FUNCTION(xml_http_constructor)
{
XMLHTTPContext *p;
SMJS_OBJ_CONSTRUCTOR(&dom_rt->xmlHTTPRequestClass)
if (!GF_JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE;
GF_SAFEALLOC(p, XMLHTTPContext);
if (!p) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[WHR] Failed to allocate XHR object\n"));
return JS_FALSE;
}
p->c = c;
p->_this = obj;
p->owning_graph = xml_get_scenegraph(c);
p->event_target = gf_dom_event_target_new(GF_DOM_EVENT_TARGET_XHR, p);
p->onabort = JSVAL_NULL;
p->onerror = JSVAL_NULL;
p->onreadystatechange = JSVAL_NULL;
p->onload = JSVAL_NULL;
p->onloadstart = JSVAL_NULL;
p->onloadend = JSVAL_NULL;
p->onprogress = JSVAL_NULL;
p->ontimeout = JSVAL_NULL;
SMJS_SET_PRIVATE(c, obj, p);
SMJS_SET_RVAL( OBJECT_TO_JSVAL(obj) );
return JS_TRUE;
}
static void xml_http_fire_event(XMLHTTPContext *ctx, GF_EventType evtType)
{
GF_DOM_Event xhr_evt;
memset(&xhr_evt, 0, sizeof(GF_DOM_Event));
xhr_evt.type = evtType;
xhr_evt.target = ctx->event_target->ptr;
xhr_evt.target_type = ctx->event_target->ptr_type;
gf_sg_fire_dom_event(ctx->event_target, &xhr_evt, ctx->owning_graph, NULL);
}
static void xml_http_state_change(XMLHTTPContext *ctx)
{
#ifndef GPAC_DISABLE_VRML
GF_SceneGraph *scene;
GF_Node *n;
#endif
jsval rval;
gf_sg_lock_javascript(ctx->c, GF_TRUE);
if (! JSVAL_IS_NULL(ctx->onreadystatechange))
JS_CallFunctionValue(ctx->c, ctx->_this, ctx->onreadystatechange, 0, NULL, &rval);
gf_sg_lock_javascript(ctx->c, GF_FALSE);
#ifndef GPAC_DISABLE_VRML
scene = (GF_SceneGraph *)JS_GetContextPrivate(ctx->c);
if (scene->__reserved_null == 0) return;
n = (GF_Node *)JS_GetContextPrivate(ctx->c);
gf_js_vrml_flush_event_out(n, (GF_ScriptPriv *)n->sgprivate->UserPrivate);
#endif
}
static JSBool SMJS_FUNCTION(xml_http_open)
{
char *val;
GF_JSAPIParam par;
XMLHTTPContext *ctx;
GF_SceneGraph *scene;
SMJS_OBJ
SMJS_ARGS
if (!GF_JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE;
ctx = (XMLHTTPContext *)SMJS_GET_PRIVATE(c, obj);
if (!ctx) return JS_TRUE;
if (ctx->readyState) xml_http_reset(ctx);
if (argc<2) return JS_TRUE;
if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE;
if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE;
xml_http_reset(ctx);
val = SMJS_CHARS(c, argv[0]);
if (strcmp(val, "GET") && strcmp(val, "POST") && strcmp(val, "HEAD")
&& strcmp(val, "PUT") && strcmp(val, "DELETE") && strcmp(val, "OPTIONS") ) {
SMJS_FREE(c, val);
return JS_TRUE;
}
ctx->method = gf_strdup(val);
SMJS_FREE(c, val);
scene = xml_get_scenegraph(c);
#ifndef GPAC_DISABLE_VRML
while (scene->pOwningProto && scene->parent_scene) scene = scene->parent_scene;
#endif
par.uri.nb_params = 0;
val = SMJS_CHARS(c, argv[1]);
par.uri.url = val;
ScriptAction(scene, GF_JSAPI_OP_RESOLVE_URI, scene->RootNode, &par);
ctx->url = par.uri.url;
SMJS_FREE(c, val);
ctx->async = GF_TRUE;
if (argc>2) {
val = NULL;
ctx->async = (JSVAL_TO_BOOLEAN(argv[2])==JS_TRUE) ? GF_TRUE : GF_FALSE;
if (argc>3) {
if (!JSVAL_CHECK_STRING(argv[3])) return JS_TRUE;
if (argc>4) {
if (!JSVAL_CHECK_STRING(argv[4])) return JS_TRUE;
val = SMJS_CHARS(c, argv[4]);
} else {
val = SMJS_CHARS(c, argv[3]);
}
}
SMJS_FREE(c, val);
}
ctx->readyState = XHR_READYSTATE_OPENED;
xml_http_state_change(ctx);
xml_http_fire_event(ctx, GF_EVENT_MEDIA_LOAD_START);
if (! JSVAL_IS_NULL(ctx->onloadstart) ) {
jsval rval;
return JS_CallFunctionValue(ctx->c, ctx->_this, ctx->onloadstart, 0, NULL, &rval);
}
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_http_set_header)
{
char *hdr, *val;
XMLHTTPContext *ctx;
SMJS_OBJ
SMJS_ARGS
if (!GF_JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE;
ctx = (XMLHTTPContext *)SMJS_GET_PRIVATE(c, obj);
if (!ctx) return JS_TRUE;
if (ctx->readyState!=XHR_READYSTATE_OPENED) return JS_TRUE;
if (argc!=2) return JS_TRUE;
if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE;
if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE;
hdr = SMJS_CHARS(c, argv[0]);
val = SMJS_CHARS(c, argv[1]);
xml_http_append_send_header(ctx, hdr, val);
SMJS_FREE(c, hdr);
SMJS_FREE(c, val);
return JS_TRUE;
}
static void xml_http_sax_start(void *sax_cbck, const char *node_name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes)
{
u32 i;
GF_DOMFullAttribute *prev = NULL;
GF_DOMFullNode *par;
XMLHTTPContext *ctx = (XMLHTTPContext *)sax_cbck;
GF_DOMFullNode *node = (GF_DOMFullNode *) gf_node_new(ctx->document, TAG_DOMFullNode);
node->name = gf_strdup(node_name);
for (i=0; i<nb_attributes; i++) {
if (!stricmp(attributes[i].name, "xml:id")) {
u32 id = gf_sg_get_max_node_id(ctx->document) + 1;
gf_node_set_id((GF_Node *)node, id, attributes[i].value);
} else {
GF_DOMFullAttribute *att;
GF_SAFEALLOC(att, GF_DOMFullAttribute);
if (!att) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[XHR] Fail to allocate DOM attribute\n"));
continue;
}
att->tag = TAG_DOM_ATT_any;
att->name = gf_strdup(attributes[i].name);
att->data_type = (u16) DOM_String_datatype;
att->data = gf_svg_create_attribute_value(att->data_type);
*((char **)att->data) = gf_strdup(attributes[i].value);
if (prev) prev->next = (GF_DOMAttribute*)att;
else node->attributes = (GF_DOMAttribute*)att;
prev = att;
}
}
par = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
gf_node_register((GF_Node*)node, (GF_Node*)par);
if (par) {
gf_node_list_add_child(&par->children, (GF_Node*)node);
} else {
assert(!ctx->document->RootNode);
ctx->document->RootNode = (GF_Node*)node;
}
gf_list_add(ctx->node_stack, node);
}
static void xml_http_sax_end(void *sax_cbck, const char *node_name, const char *name_space)
{
XMLHTTPContext *ctx = (XMLHTTPContext *)sax_cbck;
GF_DOMFullNode *par = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
if (par) {
if (strcmp(par->name, node_name)) return;
gf_list_rem_last(ctx->node_stack);
}
}
static void xml_http_sax_text(void *sax_cbck, const char *content, Bool is_cdata)
{
XMLHTTPContext *ctx = (XMLHTTPContext *)sax_cbck;
GF_DOMFullNode *par = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
if (par) {
u32 i, len;
GF_DOMText *txt;
len = (u32) strlen(content);
for (i=0; i<len; i++) {
if (!strchr(" \n\r\t", content[i])) break;
}
if (i==len) return;
txt = gf_dom_add_text_node((GF_Node *)par, gf_strdup(content) );
txt->type = is_cdata ? GF_DOM_TEXT_CDATA : GF_DOM_TEXT_REGULAR;
}
}
#define USE_PROGRESSIVE_SAX 0
static void xml_http_terminate(XMLHTTPContext *ctx, GF_Err error)
{
if (ctx->sess) {
gf_dm_sess_del(ctx->sess);
ctx->sess = NULL;
}
ctx->readyState = XHR_READYSTATE_DONE;
xml_http_state_change(ctx);
xml_http_fire_event(ctx, GF_EVENT_LOAD);
xml_http_fire_event(ctx, GF_EVENT_MEDIA_LOAD_DONE);
if (! JSVAL_IS_NULL(ctx->onload)) {
jsval rval;
JS_CallFunctionValue(ctx->c, ctx->_this, ctx->onload, 0, NULL, &rval);
}
if (! JSVAL_IS_NULL(ctx->onloadend)) {
jsval rval;
JS_CallFunctionValue(ctx->c, ctx->_this, ctx->onloadend, 0, NULL, &rval);
}
}
static void xml_http_on_data(void *usr_cbk, GF_NETIO_Parameter *parameter)
{
Bool locked = GF_FALSE;
XMLHTTPContext *ctx = (XMLHTTPContext *)usr_cbk;
while (ctx->sess) {
if (gf_sg_try_lock_javascript(ctx->c) ) {
locked = GF_TRUE;
break;
}
gf_sleep(1);
}
if (!ctx->sess && !ctx->isFile) {
if (locked)
gf_sg_lock_javascript(ctx->c, GF_FALSE);
return;
}
if (!ctx->isFile) {
assert( locked );
gf_sg_lock_javascript(ctx->c, GF_FALSE);
}
switch (parameter->msg_type) {
case GF_NETIO_SETUP:
return;
case GF_NETIO_CONNECTED:
return;
case GF_NETIO_WAIT_FOR_REPLY:
xml_http_reset_partial(ctx);
ctx->readyState = XHR_READYSTATE_HEADERS_RECEIVED;
xml_http_state_change(ctx);
xml_http_fire_event(ctx, GF_EVENT_MEDIA_PROGRESS);
if (! JSVAL_IS_NULL(ctx->onprogress) ) {
jsval rval;
JS_CallFunctionValue(ctx->c, ctx->_this, ctx->onprogress, 0, NULL, &rval);
}
return;
case GF_NETIO_PARSE_REPLY:
ctx->html_status = parameter->reply;
if (parameter->value) {
ctx->statusText = gf_strdup(parameter->value);
}
ctx->readyState = XHR_READYSTATE_LOADING;
xml_http_state_change(ctx);
ctx->readyState = XHR_READYSTATE_HEADERS_RECEIVED;
xml_http_state_change(ctx);
xml_http_fire_event(ctx, GF_EVENT_MEDIA_PROGRESS);
if (! JSVAL_IS_NULL(ctx->onprogress) ) {
jsval rval;
JS_CallFunctionValue(ctx->c, ctx->_this, ctx->onprogress, 0, NULL, &rval);
}
return;
case GF_NETIO_GET_METHOD:
parameter->name = ctx->method;
return;
case GF_NETIO_GET_HEADER:
if (ctx->headers && ctx->headers[2*ctx->cur_header]) {
parameter->name = ctx->headers[2*ctx->cur_header];
parameter->value = ctx->headers[2*ctx->cur_header+1];
ctx->cur_header++;
}
return;
case GF_NETIO_GET_CONTENT:
if (ctx->data) {
parameter->data = ctx->data;
parameter->size = (u32) strlen(ctx->data);
}
return;
case GF_NETIO_PARSE_HEADER:
xml_http_append_recv_header(ctx, parameter->name, parameter->value);
if (strcmp(parameter->name, "Content-Type")) return;
if (!strncmp(parameter->value, "application/xml", 15)
|| !strncmp(parameter->value, "text/xml", 8)
|| strstr(parameter->value, "+xml")
|| strstr(parameter->value, "/xml")
) {
assert(!ctx->sax);
ctx->sax = gf_xml_sax_new(xml_http_sax_start, xml_http_sax_end, xml_http_sax_text, ctx);
ctx->node_stack = gf_list_new();
ctx->document = gf_sg_new();
ctx->document->reference_count = 1;
}
return;
case GF_NETIO_DATA_EXCHANGE:
if (parameter->data && parameter->size) {
#if USE_PROGRESSIVE_SAX
if (ctx->sax) {
GF_Err e;
if (!ctx->size) e = gf_xml_sax_init(ctx->sax, parameter->data);
else e = gf_xml_sax_parse(ctx->sax, parameter->data);
if (e) {
gf_xml_sax_del(ctx->sax);
ctx->sax = NULL;
}
}
#endif
ctx->data = (char *)gf_realloc(ctx->data, sizeof(char)*(ctx->size+parameter->size+1));
memcpy(ctx->data + ctx->size, parameter->data, sizeof(char)*parameter->size);
ctx->size += parameter->size;
ctx->data[ctx->size] = 0;
}
return;
case GF_NETIO_DATA_TRANSFERED:
if (ctx->sax) {
#if !USE_PROGRESSIVE_SAX
gf_xml_sax_init(ctx->sax, (unsigned char *) ctx->data);
#endif
}
break;
case GF_NETIO_DISCONNECTED:
return;
case GF_NETIO_STATE_ERROR:
ctx->ret_code = parameter->error;
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[XmlHttpRequest] Download error: %s\n", gf_error_to_string(parameter->error)));
break;
}
if (ctx->async) {
xml_http_terminate(ctx, parameter->error);
}
}
static GF_Err xml_http_process_local(XMLHTTPContext *ctx)
{
GF_NETIO_Parameter par;
u64 fsize;
FILE *responseFile;
if (!strnicmp(ctx->url, "file://localhost", 16)) responseFile = gf_fopen(ctx->url+16, "rb");
else if (!strnicmp(ctx->url, "file://", 7)) responseFile = gf_fopen(ctx->url+7, "rb");
else responseFile = gf_fopen(ctx->url, "rb");
if (!responseFile) {
ctx->html_status = 404;
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[XmlHttpRequest] cannot open local file %s\n", ctx->url));
xml_http_fire_event(ctx, GF_EVENT_ERROR);
if (! JSVAL_IS_NULL(ctx->onerror) ) {
jsval rval;
JS_CallFunctionValue(ctx->c, ctx->_this, ctx->onerror, 0, NULL, &rval);
}
return GF_BAD_PARAM;
}
ctx->isFile = GF_TRUE;
par.msg_type = GF_NETIO_WAIT_FOR_REPLY;
xml_http_on_data(ctx, &par);
gf_fseek(responseFile, 0, SEEK_END);
fsize = gf_ftell(responseFile);
gf_fseek(responseFile, 0, SEEK_SET);
ctx->html_status = 200;
ctx->data = (char *)gf_malloc(sizeof(char)*(size_t)(fsize+1));
fsize = fread(ctx->data, sizeof(char), (size_t)fsize, responseFile);
gf_fclose(responseFile);
ctx->data[fsize] = 0;
ctx->size = (u32)fsize;
memset(&par, 0, sizeof(GF_NETIO_Parameter));
par.msg_type = GF_NETIO_PARSE_HEADER;
par.name = "Content-Type";
if (ctx->responseType == XHR_RESPONSETYPE_DOCUMENT) {
par.value = "application/xml";
} else {
par.value = "application/octet-stream";
}
xml_http_on_data(ctx, &par);
{
char contentLengthHeader[256];
memset(&par, 0, sizeof(GF_NETIO_Parameter));
par.msg_type = GF_NETIO_PARSE_HEADER;
par.name = "Content-Length";
sprintf(contentLengthHeader, "%d", ctx->size);
par.value = contentLengthHeader;
xml_http_on_data(ctx, &par);
}
memset(&par, 0, sizeof(GF_NETIO_Parameter));
par.msg_type = GF_NETIO_DATA_TRANSFERED;
xml_http_on_data(ctx, &par);
if (!ctx->async) {
xml_http_terminate(ctx, GF_OK);
}
return GF_OK;
}
static JSBool SMJS_FUNCTION(xml_http_send)
{
GF_Err e;
GF_JSAPIParam par;
GF_SceneGraph *scene;
char *data = NULL;
XMLHTTPContext *ctx;
SMJS_OBJ
SMJS_ARGS
if (!GF_JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE;
ctx = (XMLHTTPContext *)SMJS_GET_PRIVATE(c, obj);
if (!ctx) return JS_TRUE;
if (ctx->readyState!=XHR_READYSTATE_OPENED) return JS_TRUE;
if (ctx->sess) return JS_TRUE;
scene = xml_get_scenegraph(c);
par.dnld_man = NULL;
ScriptAction(scene, GF_JSAPI_OP_GET_DOWNLOAD_MANAGER, NULL, &par);
if (!par.dnld_man) return JS_TRUE;
if (argc) {
if (JSVAL_IS_NULL(argv[0])) {
} else if (JSVAL_IS_OBJECT(argv[0])) {
return JS_TRUE;
} else {
if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE;
data = SMJS_CHARS(c, argv[0]);
}
}
xml_http_del_data(ctx);
ctx->data = data ? gf_strdup(data) : NULL;
SMJS_FREE(c, data);
if (!strncmp(ctx->url, "http://", 7)) {
u32 flags = GF_NETIO_SESSION_NOTIFY_DATA;
if (!ctx->async)
flags |= GF_NETIO_SESSION_NOT_THREADED;
if (ctx->cache != XHR_CACHETYPE_NORMAL) {
if (ctx->cache == XHR_CACHETYPE_NONE) {
flags |= GF_NETIO_SESSION_NOT_CACHED;
}
if (ctx->cache == XHR_CACHETYPE_MEMORY) {
flags |= GF_NETIO_SESSION_MEMORY_CACHE;
}
}
ctx->sess = gf_dm_sess_new(par.dnld_man, ctx->url, flags, xml_http_on_data, ctx, &e);
if (!ctx->sess) return JS_TRUE;
e = gf_dm_sess_process(ctx->sess);
if (e!=GF_OK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[XmlHttpRequest] Error processing %s: %s\n", ctx->url, gf_error_to_string(e) ));
}
if (!ctx->async) {
xml_http_terminate(ctx, e);
}
} else {
e = xml_http_process_local(ctx);
if (e!=GF_OK) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[XmlHttpRequest] Error processing %s: %s\n", ctx->url, gf_error_to_string(e) ));
}
}
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_http_abort)
{
GF_DownloadSession *sess;
XMLHTTPContext *ctx;
SMJS_OBJ
if (!GF_JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE;
ctx = (XMLHTTPContext *)SMJS_GET_PRIVATE(c, obj);
if (!ctx) return JS_TRUE;
sess = ctx->sess;
ctx->sess = NULL;
if (sess) gf_dm_sess_del(sess);
xml_http_fire_event(ctx, GF_EVENT_ABORT);
if (! JSVAL_IS_NULL(ctx->onabort)) {
jsval rval;
return JS_CallFunctionValue(ctx->c, ctx->_this, ctx->onabort, 0, NULL, &rval);
}
xml_http_reset(ctx);
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_http_wait)
{
XMLHTTPContext *ctx;
SMJS_OBJ
if (!GF_JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE;
ctx = (XMLHTTPContext *)SMJS_GET_PRIVATE(c, obj);
if (!ctx) return JS_TRUE;
gf_sleep(2000);
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_http_get_all_headers)
{
u32 nb_hdr;
char szVal[4096];
XMLHTTPContext *ctx;
SMJS_OBJ
if (!GF_JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE;
ctx = (XMLHTTPContext *)SMJS_GET_PRIVATE(c, obj);
if (!ctx) return JS_TRUE;
if (ctx->readyState<XHR_READYSTATE_LOADING) return JS_TRUE;
szVal[0] = 0;
nb_hdr = 0;
if (ctx->recv_headers) {
while (ctx->recv_headers[nb_hdr]) {
if (szVal[0]) strcat(szVal, "\r\n");
strcat(szVal, ctx->recv_headers[nb_hdr]);
strcat(szVal, ": ");
strcat(szVal, ctx->recv_headers[nb_hdr+1]);
nb_hdr+=2;
}
}
if (!szVal[0]) {
SMJS_SET_RVAL( JSVAL_VOID );
} else {
SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, szVal) ) );
}
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_http_get_header)
{
u32 nb_hdr;
char *hdr;
char szVal[2048];
XMLHTTPContext *ctx;
SMJS_OBJ
SMJS_ARGS
if (!argc || !GF_JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE;
ctx = (XMLHTTPContext *)SMJS_GET_PRIVATE(c, obj);
if (!ctx) return JS_TRUE;
if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE;
if (ctx->readyState<XHR_READYSTATE_LOADING) return JS_TRUE;
hdr = SMJS_CHARS(c, argv[0]);
szVal[0] = 0;
nb_hdr = 0;
if (ctx->recv_headers) {
while (ctx->recv_headers[nb_hdr]) {
if (!strcmp(ctx->recv_headers[nb_hdr], hdr)) {
if (szVal[0]) strcat(szVal, ", ");
strcat(szVal, ctx->recv_headers[nb_hdr+1]);
}
nb_hdr+=2;
}
}
if (!szVal[0]) {
SMJS_SET_RVAL( JSVAL_VOID );
} else {
SMJS_SET_RVAL( STRING_TO_JSVAL( JS_NewStringCopyZ(c, szVal)) );
}
SMJS_FREE(c, hdr);
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(xml_http_overrideMimeType)
{
char *mime;
XMLHTTPContext *ctx;
SMJS_OBJ
SMJS_ARGS
if (!argc || !GF_JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE;
ctx = (XMLHTTPContext *)SMJS_GET_PRIVATE(c, obj);
if (!ctx) return JS_TRUE;
if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE;
mime = SMJS_CHARS(c, argv[0]);
ctx->mime = gf_strdup(mime);
SMJS_FREE(c, mime);
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET(xml_http_getProperty)
JSString *s;
XMLHTTPContext *ctx;
if (!GF_JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE;
ctx = (XMLHTTPContext *)SMJS_GET_PRIVATE(c, obj);
if (!ctx) return JS_TRUE;
s = NULL;
if (SMJS_ID_IS_INT(id)) {
*vp = JSVAL_VOID;
switch (SMJS_ID_TO_INT(id)) {
case XHR_ONABORT:
if (! JSVAL_IS_NULL(ctx->onabort)) {
*vp = ctx->onabort;
}
return JS_TRUE;
case XHR_ONERROR:
if (! JSVAL_IS_NULL(ctx->onerror)) {
*vp = ctx->onerror;
}
return JS_TRUE;
case XHR_ONLOAD:
if (! JSVAL_IS_NULL(ctx->onload)) {
*vp = ctx->onload;
}
return JS_TRUE;
case XHR_ONLOADSTART:
if (! JSVAL_IS_NULL(ctx->onloadstart) ) {
*vp = ctx->onloadstart;
}
return JS_TRUE;
case XHR_ONLOADEND:
if (! JSVAL_IS_NULL(ctx->onloadend)) {
*vp = ctx->onloadend;
}
return JS_TRUE;
case XHR_ONPROGRESS:
if (! JSVAL_IS_NULL(ctx->onprogress) ) {
*vp = ctx->onprogress;
}
return JS_TRUE;
case XHR_ONREADYSTATECHANGE:
if (! JSVAL_IS_NULL(ctx->onreadystatechange)) {
*vp = ctx->onreadystatechange;
}
return JS_TRUE;
case XHR_ONTIMEOUT:
if (! JSVAL_IS_NULL(ctx->ontimeout)) {
*vp = ctx->ontimeout;
}
return JS_TRUE;
case XHR_READYSTATE:
*vp = INT_TO_JSVAL(ctx->readyState);
return JS_TRUE;
case XHR_RESPONSETEXT:
if (ctx->readyState<XHR_READYSTATE_LOADING) return JS_TRUE;
if (ctx->data) {
s = JS_NewStringCopyZ(c, ctx->data);
*vp = STRING_TO_JSVAL( s );
} else {
*vp = JSVAL_VOID;
}
return JS_TRUE;
case XHR_RESPONSEXML:
if (ctx->readyState<XHR_READYSTATE_LOADING) return JS_TRUE;
if (ctx->data && ctx->document) {
*vp = dom_document_construct(c, ctx->document);
} else {
*vp = JSVAL_VOID;
}
return JS_TRUE;
case XHR_RESPONSE:
if (ctx->readyState<XHR_READYSTATE_LOADING) {
return JS_TRUE;
}
if (ctx->data) {
switch(ctx->responseType)
{
case XHR_RESPONSETYPE_NONE:
case XHR_RESPONSETYPE_TEXT:
s = JS_NewStringCopyZ(c, ctx->data);
*vp = STRING_TO_JSVAL( s );
break;
case XHR_RESPONSETYPE_ARRAYBUFFER:
if (!ctx->arraybuffer) {
ctx->arraybuffer = gf_arraybuffer_js_new(c, ctx->data, ctx->size, obj);
}
*vp = OBJECT_TO_JSVAL( ctx->arraybuffer );
break;
case XHR_RESPONSETYPE_DOCUMENT:
if (ctx->data && ctx->document) {
*vp = dom_document_construct(c, ctx->document);
} else {
*vp = JSVAL_VOID;
}
break;
default:
*vp = JSVAL_VOID;
}
} else {
*vp = JSVAL_VOID;
}
return JS_TRUE;
case XHR_STATUS:
*vp = INT_TO_JSVAL(ctx->html_status);
return JS_TRUE;
case XHR_STATUSTEXT:
if (ctx->statusText) {
s = JS_NewStringCopyZ(c, ctx->statusText);
*vp = STRING_TO_JSVAL( s );
} else {
*vp = JSVAL_VOID;
}
return JS_TRUE;
case XHR_TIMEOUT:
*vp = INT_TO_JSVAL(ctx->timeout);
return JS_TRUE;
case XHR_WITHCREDENTIALS:
*vp = BOOLEAN_TO_JSVAL(ctx->withCredentials ? JS_TRUE : JS_FALSE);
return JS_TRUE;
case XHR_UPLOAD:
return JS_TRUE;
case XHR_RESPONSETYPE:
switch (ctx->responseType)
{
case XHR_RESPONSETYPE_NONE:
s = JS_NewStringCopyZ(c, "");
break;
case XHR_RESPONSETYPE_ARRAYBUFFER:
s = JS_NewStringCopyZ(c, "arraybuffer");
break;
case XHR_RESPONSETYPE_BLOB:
s = JS_NewStringCopyZ(c, "blob");
break;
case XHR_RESPONSETYPE_DOCUMENT:
s = JS_NewStringCopyZ(c, "document");
break;
case XHR_RESPONSETYPE_JSON:
s = JS_NewStringCopyZ(c, "json");
break;
case XHR_RESPONSETYPE_TEXT:
s = JS_NewStringCopyZ(c, "text");
break;
case XHR_RESPONSETYPE_STREAM:
s = JS_NewStringCopyZ(c, "stream");
break;
}
*vp = STRING_TO_JSVAL( s );
return JS_TRUE;
case XHR_STATIC_UNSENT:
*vp = INT_TO_JSVAL(XHR_READYSTATE_UNSENT);
return JS_TRUE;
case XHR_STATIC_OPENED:
*vp = INT_TO_JSVAL(XHR_READYSTATE_OPENED);
return JS_TRUE;
case XHR_STATIC_HEADERS_RECEIVED:
*vp = INT_TO_JSVAL(XHR_READYSTATE_HEADERS_RECEIVED);
return JS_TRUE;
case XHR_STATIC_LOADING:
*vp = INT_TO_JSVAL(XHR_READYSTATE_LOADING);
return JS_TRUE;
case XHR_STATIC_DONE:
*vp = INT_TO_JSVAL(XHR_READYSTATE_DONE);
return JS_TRUE;
case XHR_CACHE:
switch (ctx->cache) {
case XHR_CACHETYPE_NORMAL:
s = JS_NewStringCopyZ(c, "normal");
break;
case XHR_CACHETYPE_NONE:
s = JS_NewStringCopyZ(c, "none");
break;
case XHR_CACHETYPE_MEMORY:
s = JS_NewStringCopyZ(c, "memory");
break;
}
*vp = STRING_TO_JSVAL(s);
return JS_TRUE;
default:
return JS_TRUE;
}
}
return SMJS_CALL_PROP_STUB();
}
JSBool gf_set_js_eventhandler(JSContext *c, jsval vp, jsval *callbackfuncval) {
if (!callbackfuncval) return JS_FALSE;
if (! JSVAL_IS_NULL( *callbackfuncval )) {
gf_js_remove_root(c, callbackfuncval, GF_JSGC_VAL);
}
if (JSVAL_IS_VOID(vp)) {
return JS_TRUE;
} else if (JSVAL_CHECK_STRING(vp)) {
jsval fval;
char *callbackstr = SMJS_CHARS(c, vp);
if (! JS_LookupProperty(c, JS_GetGlobalObject(c), callbackstr, &fval)) return JS_TRUE;
*callbackfuncval = fval;
SMJS_FREE(c, callbackstr);
} else if (JSVAL_IS_OBJECT(vp)) {
*callbackfuncval = vp;
}
if (! JSVAL_IS_NULL( *callbackfuncval )) {
gf_js_add_root(c, callbackfuncval, GF_JSGC_VAL);
}
return JS_TRUE;
}
static SMJS_FUNC_PROP_SET( xml_http_setProperty)
XMLHTTPContext *ctx;
if (!GF_JS_InstanceOf(c, obj, &dom_rt->xmlHTTPRequestClass, NULL) ) return JS_TRUE;
ctx = (XMLHTTPContext *)SMJS_GET_PRIVATE(c, obj);
if (!ctx) return JS_TRUE;
if (SMJS_ID_IS_INT(id)) {
switch (SMJS_ID_TO_INT(id)) {
case XHR_ONERROR:
gf_set_js_eventhandler(c, *vp, &ctx->onerror);
break;
case XHR_ONABORT:
gf_set_js_eventhandler(c, *vp, &ctx->onabort);
break;
case XHR_ONLOAD:
gf_set_js_eventhandler(c, *vp, &ctx->onload);
break;
case XHR_ONLOADSTART:
gf_set_js_eventhandler(c, *vp, &ctx->onloadstart);
break;
case XHR_ONLOADEND:
gf_set_js_eventhandler(c, *vp, &ctx->onloadend);
break;
case XHR_ONPROGRESS:
gf_set_js_eventhandler(c, *vp, &ctx->onprogress);
break;
case XHR_ONREADYSTATECHANGE:
gf_set_js_eventhandler(c, *vp, &ctx->onreadystatechange);
break;
case XHR_ONTIMEOUT:
gf_set_js_eventhandler(c, *vp, &ctx->ontimeout);
break;
case XHR_TIMEOUT:
ctx->timeout = JSVAL_TO_INT(*vp);
return JS_TRUE;
case XHR_WITHCREDENTIALS:
ctx->withCredentials = (JSVAL_TO_BOOLEAN(*vp) == JS_TRUE ? GF_TRUE : GF_FALSE);
return JS_TRUE;
case XHR_RESPONSETYPE:
{
char *str = SMJS_CHARS(c, *vp);
if (!str) return JS_TRUE;
if (!strcmp(str, "")) {
ctx->responseType = XHR_RESPONSETYPE_NONE;
} else if (!strcmp(str, "arraybuffer")) {
ctx->responseType = XHR_RESPONSETYPE_ARRAYBUFFER;
} else if (!strcmp(str, "blob")) {
ctx->responseType = XHR_RESPONSETYPE_BLOB;
} else if (!strcmp(str, "document")) {
ctx->responseType = XHR_RESPONSETYPE_DOCUMENT;
} else if (!strcmp(str, "json")) {
ctx->responseType = XHR_RESPONSETYPE_JSON;
} else if (!strcmp(str, "text")) {
ctx->responseType = XHR_RESPONSETYPE_TEXT;
} else if (!strcmp(str, "stream")) {
ctx->responseType = XHR_RESPONSETYPE_STREAM;
}
break;
return JS_TRUE;
}
case XHR_CACHE:
{
char *str = SMJS_CHARS(c, *vp);
if (!str) return JS_TRUE;
if (!strcmp(str, "normal")) {
ctx->cache = XHR_CACHETYPE_NORMAL;
} else if (!strcmp(str, "none")) {
ctx->cache = XHR_CACHETYPE_NONE;
} else if (!strcmp(str, "memory")) {
ctx->cache = XHR_CACHETYPE_MEMORY;
}
break;
return JS_TRUE;
}
default:
return JS_TRUE;
}
}
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET(dcci_getProperty)
GF_DOMFullAttribute *att;
GF_ChildNodeItem *child;
GF_DOMFullNode *n;
char *value;
JSString *s;
if (!GF_JS_InstanceOf(c, obj, &dom_rt->DCCIClass, NULL) ) return JS_TRUE;
n = (GF_DOMFullNode*) dom_get_node(c, obj);
if (!n) return JS_TRUE;
if (SMJS_ID_IS_INT(id)) {
switch (SMJS_ID_TO_INT(id)) {
case DCCI_JSPROPERTY_VALUE:
child = n->children;
while (child) {
if (child->node && (child->node->sgprivate->tag==TAG_DOMText)) {
GF_DOMText *txt = (GF_DOMText *)child->node;
if (txt->type==GF_DOM_TEXT_REGULAR) {
s = JS_NewStringCopyZ(c, txt->textContent);
*vp = STRING_TO_JSVAL( s );
return JS_TRUE;
}
}
child = child->next;
}
*vp = JSVAL_NULL;
return JS_TRUE;
case DCCI_JSPROPERTY_VALUETYPE:
value = "DOMString";
att = (GF_DOMFullAttribute*) n->attributes;
while (att) {
if (att->name && !strcmp(att->name, "valueType") && att->data) {
value = (char *)att->data;
break;
}
att = (GF_DOMFullAttribute*) att->next;
}
s = JS_NewStringCopyZ(c, value);
*vp = STRING_TO_JSVAL( s );
return JS_TRUE;
case DCCI_JSPROPERTY_PROPERTYTYPE:
value = "DOMString";
att = (GF_DOMFullAttribute*) n->attributes;
while (att) {
if (att->name && !strcmp(att->name, "propertyType") && att->data) {
value = (char *)att->data;
break;
}
att = (GF_DOMFullAttribute*) att->next;
}
s = JS_NewStringCopyZ(c, value);
*vp = STRING_TO_JSVAL( s );
return JS_TRUE;
case DCCI_JSPROPERTY_READONLY:
att = (GF_DOMFullAttribute*) n->attributes;
while (att) {
if (att->name && !strcmp(att->name, "readOnly") && att->data && !strcmp((const char *)att->data, "true")) {
*vp = BOOLEAN_TO_JSVAL(JS_TRUE);
return JS_TRUE;
}
att = (GF_DOMFullAttribute*) att->next;
}
*vp = BOOLEAN_TO_JSVAL(JS_FALSE);
return JS_TRUE;
case DCCI_JSPROPERTY_DCCIMETADATAINTERFACETYPE:
*vp = JSVAL_NULL;
return JS_TRUE;
case DCCI_JSPROPERTY_DCCIMETADATAINTERFACE:
*vp = JSVAL_NULL;
return JS_TRUE;
case DCCI_JSPROPERTY_VERSION:
s = JS_NewStringCopyZ(c, "1.0");
*vp = STRING_TO_JSVAL( s );
return JS_TRUE;
default:
return JS_TRUE;
}
}
return SMJS_CALL_PROP_STUB();
}
static SMJS_FUNC_PROP_SET( dcci_setProperty)
GF_ChildNodeItem *child;
GF_DOMFullNode *n;
GF_DOMFullAttribute*att;
GF_DOM_Event evt;
char *str;
Bool readonly;
if (!GF_JS_InstanceOf(c, obj, &dom_rt->DCCIClass, NULL) ) return JS_TRUE;
n = (GF_DOMFullNode*) dom_get_node(c, obj);
if (!n) return JS_TRUE;
str = SMJS_CHARS(c, *vp);
if (!str) return JS_TRUE;
if (SMJS_ID_IS_INT(id)) {
switch (SMJS_ID_TO_INT(id)) {
case DCCI_JSPROPERTY_VALUE:
readonly = GF_FALSE;
att = (GF_DOMFullAttribute*) n->attributes;
while (att) {
if (att->name && !strcmp(att->name, "readOnly") && att->data && !strcmp((const char *)att->data, "true")) {
readonly = GF_TRUE;
break;
}
att = (GF_DOMFullAttribute*) att->next;
}
if (readonly) break;
child = n->children;
while (child) {
if (child->node && (child->node->sgprivate->tag==TAG_DOMText)) {
GF_DOMText *txt = (GF_DOMText *)child->node;
if (txt->type==GF_DOM_TEXT_REGULAR) {
if (txt->textContent) gf_free(txt->textContent);
txt->textContent = gf_strdup(str);
break;
}
}
child = child->next;
}
if (!child)
gf_dom_add_text_node((GF_Node*)n, gf_strdup(str) );
memset(&evt, 0, sizeof(GF_DOM_Event));
evt.type = GF_EVENT_DCCI_PROP_CHANGE;
evt.bubbles = 1;
evt.relatedNode = (GF_Node*)n;
gf_dom_event_fire((GF_Node*)n, &evt);
n->sgprivate->scenegraph->modified = GF_TRUE;
break;
case DCCI_JSPROPERTY_PROPERTYTYPE:
break;
default:
break;
}
}
SMJS_FREE(c, str);
return JS_TRUE;
}
Bool dcci_prop_lookup(GF_DOMFullNode *n, char *ns, char *name, Bool deep, Bool first)
{
Bool ok = GF_TRUE;
GF_ChildNodeItem *child = n->children;
if (strcmp(ns, "*") && n->ns && strcmp(ns, gf_sg_get_namespace(n->sgprivate->scenegraph, n->ns) ))
ok = GF_FALSE;
if (strcmp(name, "*") && n->name && strcmp(name, n->name))
ok = GF_FALSE;
if (ok) return GF_TRUE;
if (!first && !deep) return GF_FALSE;
while (child) {
if (child->node && (child->node->sgprivate->tag==TAG_DOMFullNode)) {
ok = dcci_prop_lookup((GF_DOMFullNode *)child->node, ns, name, deep, GF_FALSE);
if (ok) return GF_TRUE;
}
child = child->next;
}
return GF_FALSE;
}
static JSBool SMJS_FUNCTION(dcci_has_property)
{
GF_DOMFullNode *n;
Bool deep;
char *ns, *name;
SMJS_OBJ
SMJS_ARGS
if (!GF_JS_InstanceOf(c, obj, &dom_rt->DCCIClass, NULL) ) return JS_TRUE;
n = (GF_DOMFullNode*) dom_get_node(c, obj);
if (!n) return JS_TRUE;
if (argc!=3) return JS_TRUE;
if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE;
if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE;
ns = SMJS_CHARS(c, argv[0]);
name = SMJS_CHARS(c, argv[1]);
deep = JSVAL_TO_BOOLEAN(argv[2]) ? GF_TRUE : GF_FALSE;
deep = dcci_prop_lookup(n, ns, name, deep, GF_TRUE);
SMJS_SET_RVAL( BOOLEAN_TO_JSVAL(deep ? JS_TRUE : JS_FALSE) );
SMJS_FREE(c, ns);
SMJS_FREE(c, name);
return JS_TRUE;
}
void dcci_prop_collect(DOMNodeList *nl, GF_DOMFullNode *n, char *ns, char *name, Bool deep, Bool first)
{
Bool ok = GF_TRUE;
GF_ChildNodeItem *child = n->children;
if (strcmp(ns, "*") && n->ns && strcmp(ns, gf_sg_get_namespace(n->sgprivate->scenegraph, n->ns) ))
ok = GF_FALSE;
if (strcmp(name, "*") && n->name && strcmp(name, n->name))
ok = GF_FALSE;
if (ok) {
gf_node_register((GF_Node*)n, NULL);
if (n->sgprivate->scenegraph->reference_count)
n->sgprivate->scenegraph->reference_count++;
gf_node_list_add_child(&nl->child, (GF_Node*)n);
}
if (!first && !deep) return;
while (child) {
if (child->node && (child->node->sgprivate->tag==TAG_DOMFullNode)) {
dcci_prop_collect(nl, (GF_DOMFullNode *)child->node, ns, name, GF_TRUE, GF_FALSE);
}
child = child->next;
}
}
static JSBool SMJS_FUNCTION(dcci_search_property)
{
JSObject *new_obj;
DOMNodeList *nl;
GF_DOMFullNode *n;
Bool deep;
char *ns, *name;
SMJS_OBJ
SMJS_ARGS
if (!GF_JS_InstanceOf(c, obj, &dom_rt->DCCIClass, NULL) ) return JS_TRUE;
n = (GF_DOMFullNode*) dom_get_node(c, obj);
if (!n) return JS_TRUE;
if (argc!=4) return JS_TRUE;
if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE;
if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE;
ns = SMJS_CHARS(c, argv[0]);
name = SMJS_CHARS(c, argv[1]);
deep = JSVAL_TO_BOOLEAN(argv[3]) ? GF_TRUE : GF_FALSE;
GF_SAFEALLOC(nl, DOMNodeList);
dcci_prop_collect(nl, n, ns, name, deep, GF_TRUE);
new_obj = JS_NewObject(c, &dom_rt->domNodeListClass._class, 0, 0);
SMJS_SET_PRIVATE(c, new_obj, nl);
SMJS_SET_RVAL( OBJECT_TO_JSVAL(new_obj) );
SMJS_FREE(c, ns);
SMJS_FREE(c, name);
return JS_TRUE;
}
static SMJS_FUNC_PROP_GET( storage_getProperty)
#ifndef GPAC_CONFIG_DARWIN
if (!id) id=0;
#endif
if (!GF_JS_InstanceOf(c, obj, &dom_rt->storageClass, NULL) ) return JS_TRUE;
*vp = JSVAL_VOID;
return JS_TRUE;
}
static SMJS_FUNC_PROP_SET_NOVP( storage_setProperty)
#ifndef GPAC_CONFIG_DARWIN
if (!id) id=0;
#endif
if (!GF_JS_InstanceOf(c, obj, &dom_rt->storageClass, NULL) ) return JS_TRUE;
return JS_TRUE;
}
static JSBool SMJS_FUNCTION(storage_constructor)
{
SMJS_OBJ_CONSTRUCTOR(&dom_rt->storageClass)
if (!GF_JS_InstanceOf(c, obj, &dom_rt->storageClass, NULL) ) return JS_TRUE;
return JS_TRUE;
}
static DECL_FINALIZE( storage_finalize)
if (!c) c=NULL;
}
void dom_js_define_storage(JSContext *c, JSObject *parent_obj, const char *name)
{
JS_DefineObject(c, parent_obj, name, &dom_rt->storageClass._class, 0, 0 );
}
void dom_js_load(GF_SceneGraph *scene, JSContext *c, JSObject *global)
{
if (!dom_rt) {
GF_SAFEALLOC(dom_rt, GF_DOMRuntime);
dom_rt->handlers = gf_list_new();
JS_SETUP_CLASS(dom_rt->domNodeClass, "Node", JSCLASS_HAS_PRIVATE, dom_node_getProperty, dom_node_setProperty, dom_node_finalize);
JS_SETUP_CLASS(dom_rt->domDocumentClass, "Document", JSCLASS_HAS_PRIVATE, dom_document_getProperty, dom_document_setProperty, dom_document_finalize);
JS_SETUP_CLASS(dom_rt->domElementClass, "Element", JSCLASS_HAS_PRIVATE, dom_element_getProperty, JS_PropertyStub_forSetter, dom_node_finalize);
JS_SETUP_CLASS(dom_rt->domTextClass, "Text", JSCLASS_HAS_PRIVATE, dom_text_getProperty, dom_text_setProperty, dom_node_finalize);
JS_SETUP_CLASS(dom_rt->domEventClass , "Event", JSCLASS_HAS_PRIVATE, event_getProperty, JS_PropertyStub_forSetter, JS_FinalizeStub);
JS_SETUP_CLASS(dom_rt->domNodeListClass, "NodeList", JSCLASS_HAS_PRIVATE, dom_nodelist_getProperty, dom_nodelist_setProperty, dom_nodelist_finalize);
JS_SETUP_CLASS(dom_rt->xmlHTTPRequestClass, "XMLHttpRequest", JSCLASS_HAS_PRIVATE, xml_http_getProperty, xml_http_setProperty, xml_http_finalize);
JS_SETUP_CLASS(dom_rt->storageClass, "Storage", JSCLASS_HAS_PRIVATE, storage_getProperty, storage_setProperty, storage_finalize);
JS_SETUP_CLASS(dom_rt->DCCIClass, "DCCI", JSCLASS_HAS_PRIVATE, dcci_getProperty, dcci_setProperty, dom_node_finalize);
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] dom run-time allocated\n"));
}
dom_rt->nb_inst++;
define_dom_exception(c, global);
{
JSFunctionSpec nodeFuncs[] = {
SMJS_FUNCTION_SPEC("insertBefore", xml_node_insert_before, 2),
SMJS_FUNCTION_SPEC("replaceChild", xml_node_replace_child, 2),
SMJS_FUNCTION_SPEC("removeChild", xml_node_remove_child, 1),
SMJS_FUNCTION_SPEC("appendChild", xml_node_append_child, 1),
SMJS_FUNCTION_SPEC("hasChildNodes", xml_node_has_children, 0),
SMJS_FUNCTION_SPEC("cloneNode", xml_clone_node, 1),
SMJS_FUNCTION_SPEC("normalize", xml_dom3_not_implemented, 0),
SMJS_FUNCTION_SPEC("isSupported", xml_dom3_not_implemented, 2),
SMJS_FUNCTION_SPEC("hasAttributes", xml_node_has_attributes, 0),
SMJS_FUNCTION_SPEC("compareDocumentPosition", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC("isSameNode", xml_node_is_same_node, 1),
SMJS_FUNCTION_SPEC("lookupPrefix", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC("isDefaultNamespace", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC("lookupNamespaceURI", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC("isEqualNode", xml_node_is_same_node, 1),
SMJS_FUNCTION_SPEC("getFeature", xml_dom3_not_implemented, 2),
SMJS_FUNCTION_SPEC("setUserData", xml_dom3_not_implemented, 3),
SMJS_FUNCTION_SPEC("getUserData", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC(0, 0, 0)
};
JSPropertySpec nodeProps[] = {
SMJS_PROPERTY_SPEC("nodeName", NODE_JSPROPERTY_NODENAME, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("nodeValue", NODE_JSPROPERTY_NODEVALUE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("nodeType", NODE_JSPROPERTY_NODETYPE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("parentNode", NODE_JSPROPERTY_PARENTNODE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("childNodes", NODE_JSPROPERTY_CHILDNODES, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("firstChild", NODE_JSPROPERTY_FIRSTCHILD, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("lastChild", NODE_JSPROPERTY_LASTCHILD, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("previousSibling",NODE_JSPROPERTY_PREVIOUSSIBLING, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("nextSibling", NODE_JSPROPERTY_NEXTSIBLING, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("attributes", NODE_JSPROPERTY_ATTRIBUTES, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("ownerDocument", NODE_JSPROPERTY_OWNERDOCUMENT, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("namespaceURI", NODE_JSPROPERTY_NAMESPACEURI, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("prefix", NODE_JSPROPERTY_PREFIX, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("localName", NODE_JSPROPERTY_LOCALNAME, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("baseURI", NODE_JSPROPERTY_BASEURI, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("textContent", NODE_JSPROPERTY_TEXTCONTENT, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("firstElementChild", NODE_JSPROPERTY_FIRSTELEMENTCHILD, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("lastElementChild", NODE_JSPROPERTY_LASTELEMENTCHILD, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("previousElementSibling",NODE_JSPROPERTY_PREVIOUSELEMENTSIBLING, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("nextElementSibling", NODE_JSPROPERTY_NEXTELEMENTSIBLING, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0)
};
GF_JS_InitClass(c, global, JS_NewObject(c, NULL, NULL, NULL), &dom_rt->domNodeClass, 0, 0, nodeProps, nodeFuncs, 0, 0);
if (!dom_rt->domNodeClass._proto) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOMCore] Not enough memory to initialize JS node class\n"));
return;
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] node class initialized\n"));
JS_DefineProperty(c, dom_rt->domNodeClass._proto, "ELEMENT_NODE", INT_TO_JSVAL(1), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineProperty(c, dom_rt->domNodeClass._proto, "TEXT_NODE", INT_TO_JSVAL(3), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineProperty(c, dom_rt->domNodeClass._proto, "CDATA_SECTION_NODE", INT_TO_JSVAL(4), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineProperty(c, dom_rt->domNodeClass._proto, "DOCUMENT_NODE", INT_TO_JSVAL(9), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
}
{
JSFunctionSpec documentFuncs[] = {
SMJS_FUNCTION_SPEC("createElement", xml_document_create_element, 1),
SMJS_FUNCTION_SPEC("createDocumentFragment", xml_dom3_not_implemented, 0),
SMJS_FUNCTION_SPEC("createTextNode", xml_document_create_text, 1),
SMJS_FUNCTION_SPEC("createComment", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC("createCDATASection", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC("createProcessingInstruction", xml_dom3_not_implemented, 2),
SMJS_FUNCTION_SPEC("createAttribute", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC("createEntityReference", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC("getElementsByTagName", xml_document_elements_by_tag, 1),
SMJS_FUNCTION_SPEC("importNode", xml_dom3_not_implemented, 2),
SMJS_FUNCTION_SPEC("createElementNS", xml_document_create_element, 2),
SMJS_FUNCTION_SPEC("createAttributeNS", xml_dom3_not_implemented, 2),
SMJS_FUNCTION_SPEC("getElementsByTagNameNS", xml_document_elements_by_tag, 2),
SMJS_FUNCTION_SPEC("getElementById", xml_document_element_by_id, 1),
SMJS_FUNCTION_SPEC("adoptNode", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC("normalizeDocument", xml_dom3_not_implemented, 0),
SMJS_FUNCTION_SPEC("renameNode", xml_dom3_not_implemented, 3),
JS_DOM3_EVENT_TARGET_INTERFACE
SMJS_FUNCTION_SPEC("createEvent", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC(0, 0, 0)
};
JSPropertySpec documentProps[] = {
SMJS_PROPERTY_SPEC("doctype", DOCUMENT_JSPROPERTY_DOCTYPE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("implementation", DOCUMENT_JSPROPERTY_IMPLEMENTATION, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("documentElement", DOCUMENT_JSPROPERTY_DOCUMENTELEMENT, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("inputEncoding", DOCUMENT_JSPROPERTY_INPUTENCODING, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("xmlEncoding", DOCUMENT_JSPROPERTY_XMLENCODING, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("xmlStandalone", DOCUMENT_JSPROPERTY_XMLSTANDALONE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("xmlVersion", DOCUMENT_JSPROPERTY_XMLVERSION, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("strictErrorChecking",DOCUMENT_JSPROPERTY_STRICTERRORCHECKING, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("documentURI", DOCUMENT_JSPROPERTY_DOCUMENTURI, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("location", DOCUMENT_JSPROPERTY_LOCATION, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("domConfig", DOCUMENT_JSPROPERTY_DOMCONFIG, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("global", DOCUMENT_JSPROPERTY_GLOBAL, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0),
};
GF_JS_InitClass(c, global, dom_rt->domNodeClass._proto, &dom_rt->domDocumentClass, 0, 0, documentProps, documentFuncs, 0, 0);
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] document class initialized\n"));
}
{
JSFunctionSpec elementFuncs[] = {
SMJS_FUNCTION_SPEC("getAttribute", xml_element_get_attribute, 1),
SMJS_FUNCTION_SPEC("setAttribute", xml_element_set_attribute, 2),
SMJS_FUNCTION_SPEC("removeAttribute", xml_element_remove_attribute, 1),
SMJS_FUNCTION_SPEC("getAttributeNS", xml_element_get_attribute, 2),
SMJS_FUNCTION_SPEC("setAttributeNS", xml_element_set_attribute, 3),
SMJS_FUNCTION_SPEC("removeAttributeNS", xml_element_remove_attribute, 2),
SMJS_FUNCTION_SPEC("hasAttribute", xml_element_has_attribute, 1),
SMJS_FUNCTION_SPEC("hasAttributeNS", xml_element_has_attribute, 2),
SMJS_FUNCTION_SPEC("getElementsByTagName", xml_element_elements_by_tag, 1),
SMJS_FUNCTION_SPEC("getElementsByTagNameNS", xml_element_elements_by_tag, 2),
SMJS_FUNCTION_SPEC("setIdAttribute", xml_element_set_id, 2),
SMJS_FUNCTION_SPEC("setIdAttributeNS", xml_element_set_id, 3),
SMJS_FUNCTION_SPEC("getAttributeNode", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC("setAttributeNode", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC("removeAttributeNode", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC("getAttributeNodeNS", xml_dom3_not_implemented, 2),
SMJS_FUNCTION_SPEC("setAttributeNodeNS", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC("setIdAttributeNode", xml_dom3_not_implemented, 2),
JS_DOM3_EVENT_TARGET_INTERFACE
SMJS_FUNCTION_SPEC(0, 0, 0)
};
JSPropertySpec elementProps[] = {
SMJS_PROPERTY_SPEC("tagName", ELEMENT_JSPROPERTY_TAGNAME, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("schemaTypeInfo",ELEMENT_JSPROPERTY_SCHEMATYPEINFO, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0),
};
GF_JS_InitClass(c, global, dom_rt->domNodeClass._proto, &dom_rt->domElementClass, 0, 0, elementProps, elementFuncs, 0, 0);
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] element class initialized\n"));
}
{
JSFunctionSpec textFuncs[] = {
#if 0
SMJS_FUNCTION_SPEC("substringData", xml_dom3_not_implemented, 2),
SMJS_FUNCTION_SPEC("appendData", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC("insertData", xml_dom3_not_implemented, 2),
SMJS_FUNCTION_SPEC("deleteData", xml_dom3_not_implemented, 2),
SMJS_FUNCTION_SPEC("replaceData", xml_dom3_not_implemented, 3),
SMJS_FUNCTION_SPEC("splitText", xml_dom3_not_implemented, 1),
SMJS_FUNCTION_SPEC("replaceWholeText", xml_dom3_not_implemented, 1),
#endif
SMJS_FUNCTION_SPEC(0, 0, 0)
};
JSPropertySpec textProps[] = {
SMJS_PROPERTY_SPEC("data", TEXT_JSPROPERTY_DATA, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("length", TEXT_JSPROPERTY_LENGTH, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("isElementContentWhitespace",TEXT_JSPROPERTY_ISELEMENTCONTENTWHITESPACE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("wholeText", TEXT_JSPROPERTY_WHOLETEXT, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0),
};
GF_JS_InitClass(c, global, dom_rt->domNodeClass._proto, &dom_rt->domTextClass, 0, 0, textProps, textFuncs, 0, 0);
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] text class initialized\n"));
}
{
JSFunctionSpec eventFuncs[] = {
SMJS_FUNCTION_SPEC("stopPropagation", event_stop_propagation, 0),
SMJS_FUNCTION_SPEC("stopImmediatePropagation", event_stop_immediate_propagation, 0),
SMJS_FUNCTION_SPEC("preventDefault", event_prevent_default, 0),
#if 0
SMJS_FUNCTION_SPEC("initEvent", event_prevent_default, 3),
SMJS_FUNCTION_SPEC("initEventNS", event_prevent_default, 4),
#endif
SMJS_FUNCTION_SPEC(0, 0, 0)
};
JSPropertySpec eventProps[] = {
SMJS_PROPERTY_SPEC("type", EVENT_JSPROPERTY_TYPE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("target", EVENT_JSPROPERTY_TARGET, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("currentTarget", EVENT_JSPROPERTY_CURRENTTARGET, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("eventPhase", EVENT_JSPROPERTY_EVENTPHASE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("bubbles", EVENT_JSPROPERTY_BUBBLES, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("cancelable", EVENT_JSPROPERTY_CANCELABLE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("timeStamp", EVENT_JSPROPERTY_TIMESTAMP, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("namespaceURI", EVENT_JSPROPERTY_NAMESPACEURI, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("defaultPrevented",EVENT_JSPROPERTY_DEFAULTPREVENTED, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("detail", EVENT_JSPROPERTY_DETAIL, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("data", EVENT_JSPROPERTY_DATA, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("screenX", EVENT_JSPROPERTY_SCREENX, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("screenY", EVENT_JSPROPERTY_SCREENY, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("clientX", EVENT_JSPROPERTY_CLIENTX, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("clientY", EVENT_JSPROPERTY_CLIENTY, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("button", EVENT_JSPROPERTY_BUTTON, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("relatedTarget", EVENT_JSPROPERTY_RELATEDTARGET, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("wheelDelta", EVENT_JSPROPERTY_WHEELDELTA, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("keyIdentifier", EVENT_JSPROPERTY_KEYIDENTIFIER, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("keyChar", EVENT_JSPROPERTY_KEYCHAR, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("charCode", EVENT_JSPROPERTY_CHARCODE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("lengthComputable", EVENT_JSPROPERTY_LENGTHCOMPUTABLE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("typeArg", EVENT_JSPROPERTY_TYPEARG, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("loaded", EVENT_JSPROPERTY_LOADED, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("total", EVENT_JSPROPERTY_TOTAL, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("buffering", EVENT_JSPROPERTY_BUFFER_ON, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("bufferLevel", EVENT_JSPROPERTY_BUFFERLEVEL, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("bufferRemainingTime",EVENT_JSPROPERTY_BUFFERREMAININGTIME, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("status", EVENT_JSPROPERTY_STATUS, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("width", EVENT_JSPROPERTY_WIDTH, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("height", EVENT_JSPROPERTY_HEIGHT, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("offset_x", EVENT_JSPROPERTY_OFFSETX, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("offset_y", EVENT_JSPROPERTY_OFFSETY, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("vp_width", EVENT_JSPROPERTY_VPWIDTH, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("vp_height", EVENT_JSPROPERTY_VPHEIGHT, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("translation_x", EVENT_JSPROPERTY_TRANSLATIONX,JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("translation_y", EVENT_JSPROPERTY_TRANSLATIONY,JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("type3d", EVENT_JSPROPERTY_TYPE3D, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("error", EVENT_JSPROPERTY_ERROR, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("dynamic_scene", EVENT_JSPROPERTY_DYNAMIC_SCENE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("url", EVENT_JSPROPERTY_URL, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0),
};
GF_JS_InitClass(c, global, 0, &dom_rt->domEventClass, 0, 0, eventProps, eventFuncs, 0, 0);
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] Event class initialized\n"));
JS_DefineProperty(c, dom_rt->domEventClass._proto, "CAPTURING_PHASE", INT_TO_JSVAL(1), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineProperty(c, dom_rt->domEventClass._proto, "AT_TARGET", INT_TO_JSVAL(2), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineProperty(c, dom_rt->domEventClass._proto, "BUBBLING_PHASE", INT_TO_JSVAL(3), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineProperty(c, dom_rt->domEventClass._proto, "DOM_KEY_LOCATION_STANDARD ", INT_TO_JSVAL(0), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineProperty(c, dom_rt->domEventClass._proto, "DOM_KEY_LOCATION_LEFT", INT_TO_JSVAL(1), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineProperty(c, dom_rt->domEventClass._proto, "DOM_KEY_LOCATION_RIGHT", INT_TO_JSVAL(2), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineProperty(c, dom_rt->domEventClass._proto, "DOM_KEY_LOCATION_NUMPAD", INT_TO_JSVAL(3), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT);
}
{
JSFunctionSpec nodeListFuncs[] = {
SMJS_FUNCTION_SPEC("item", dom_nodelist_item, 1),
SMJS_FUNCTION_SPEC(0, 0, 0)
};
JSPropertySpec nodeListProps[] = {
SMJS_PROPERTY_SPEC("length", NODELIST_JSPROPERTY_LENGTH, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0)
};
GF_JS_InitClass(c, global, 0, &dom_rt->domNodeListClass, 0, 0, nodeListProps, nodeListFuncs, 0, 0);
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] nodeList class initialized\n"));
}
{
JSPropertySpec xmlHTTPRequestClassProps[] = {
SMJS_PROPERTY_SPEC("onabort", XHR_ONABORT, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("onerror", XHR_ONERROR, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("onload", XHR_ONLOAD, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("onloadend", XHR_ONLOADEND, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("onloadstart", XHR_ONLOADSTART, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("onprogress", XHR_ONPROGRESS, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("onreadystatechange", XHR_ONREADYSTATECHANGE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("ontimeout", XHR_ONTIMEOUT, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("readyState", XHR_READYSTATE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("response", XHR_RESPONSE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("responseType", XHR_RESPONSETYPE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED , 0, 0),
SMJS_PROPERTY_SPEC("responseText", XHR_RESPONSETEXT, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("responseXML", XHR_RESPONSEXML, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("status", XHR_STATUS, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("statusText", XHR_STATUSTEXT, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("timeout", XHR_TIMEOUT, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED , 0, 0),
SMJS_PROPERTY_SPEC("upload", XHR_UPLOAD, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("withCredentials", XHR_WITHCREDENTIALS, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED , 0, 0),
SMJS_PROPERTY_SPEC("cache", XHR_CACHE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED , 0, 0),
SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0)
};
JSPropertySpec xmlHTTPRequestStaticClassProps[] = {
SMJS_PROPERTY_SPEC("UNSENT", XHR_STATIC_UNSENT, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, xml_http_getProperty, 0),
SMJS_PROPERTY_SPEC("OPENED", XHR_STATIC_OPENED, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, xml_http_getProperty, 0),
SMJS_PROPERTY_SPEC("HEADERS_RECEIVED", XHR_STATIC_HEADERS_RECEIVED, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, xml_http_getProperty, 0),
SMJS_PROPERTY_SPEC("LOADING", XHR_STATIC_LOADING, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, xml_http_getProperty, 0),
SMJS_PROPERTY_SPEC("DONE", XHR_STATIC_DONE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, xml_http_getProperty, 0),
SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0)
};
JSFunctionSpec xmlHTTPRequestClassFuncs[] = {
SMJS_FUNCTION_SPEC("abort", xml_http_abort, 0),
SMJS_FUNCTION_SPEC("getAllResponseHeaders", xml_http_get_all_headers, 0),
SMJS_FUNCTION_SPEC("getResponseHeader", xml_http_get_header, 1),
SMJS_FUNCTION_SPEC("open", xml_http_open, 2),
SMJS_FUNCTION_SPEC("overrideMimeType", xml_http_overrideMimeType, 1),
SMJS_FUNCTION_SPEC("send", xml_http_send, 0),
SMJS_FUNCTION_SPEC("setRequestHeader", xml_http_set_header, 2),
SMJS_FUNCTION_SPEC("wait", xml_http_wait, 0),
JS_DOM3_EVENT_TARGET_INTERFACE
SMJS_FUNCTION_SPEC(0, 0, 0)
};
GF_JS_InitClass(c, global, NULL, &dom_rt->xmlHTTPRequestClass, xml_http_constructor, 0, xmlHTTPRequestClassProps, xmlHTTPRequestClassFuncs, xmlHTTPRequestStaticClassProps, 0);
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] XMLHttpRequest class initialized\n"));
}
{
JSPropertySpec storageClassProps[] = {
SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0)
};
JSFunctionSpec storageClassFuncs[] = {
SMJS_FUNCTION_SPEC(0, 0, 0)
};
GF_JS_InitClass(c, global, 0, &dom_rt->storageClass, storage_constructor, 0, storageClassProps, storageClassFuncs, 0, 0);
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] Storage class initialized\n"));
}
{
GF_SceneGraph *dcci;
jsval dcci_root;
dcci = NULL;
if (scene->script_action) {
GF_JSAPIParam par;
par.node = NULL;
scene->script_action(scene->script_action_cbck, GF_JSAPI_OP_GET_DCCI, NULL, &par);
dcci = par.scene;
}
if (dcci && dcci->RootNode) {
JSPropertySpec DCCIClassProps[] = {
SMJS_PROPERTY_SPEC("value", DCCI_JSPROPERTY_VALUE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("valueType", DCCI_JSPROPERTY_VALUETYPE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("propertyType", DCCI_JSPROPERTY_PROPERTYTYPE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0),
SMJS_PROPERTY_SPEC("readOnly", DCCI_JSPROPERTY_READONLY, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("DCCIMetadataInterfaceType", DCCI_JSPROPERTY_DCCIMETADATAINTERFACETYPE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("DCCIMetadataInterface", DCCI_JSPROPERTY_DCCIMETADATAINTERFACE, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC("version", DCCI_JSPROPERTY_VERSION, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_READONLY, 0, 0),
SMJS_PROPERTY_SPEC(0, 0, 0, 0, 0)
};
JSFunctionSpec DCCIClassFuncs[] = {
SMJS_FUNCTION_SPEC("hasProperty", dcci_has_property, 3),
SMJS_FUNCTION_SPEC("searchProperty", dcci_search_property, 4),
SMJS_FUNCTION_SPEC(0, 0, 0)
};
GF_JS_InitClass(c, global, dom_rt->domElementClass._proto, &dom_rt->DCCIClass, 0, 0, DCCIClassProps, DCCIClassFuncs, 0, 0);
dcci_root = dom_base_node_construct(c, &dom_rt->DCCIClass, dcci->RootNode);
JS_DefineProperty(c, global, "DCCIRoot", dcci_root, 0, 0, JSPROP_READONLY | JSPROP_PERMANENT );
dcci->dcci_doc = GF_TRUE;
GF_LOG(GF_LOG_DEBUG, GF_LOG_SCRIPT, ("[DOMCore] DCCI class initialized\n"));
}
}
}
void html_media_element_js_finalize(JSContext *c, GF_Node *n);
GF_EXPORT
void gf_sg_js_dom_pre_destroy(JSContext *c, GF_SceneGraph *sg, GF_Node *n)
{
u32 i, count;
if (n) {
if (n->sgprivate->tag == TAG_SVG_video || n->sgprivate->tag == TAG_SVG_audio) {
}
if (n->sgprivate->interact && n->sgprivate->interact->js_binding && n->sgprivate->interact->js_binding->node) {
JSObject *obj = (JSObject *)n->sgprivate->interact->js_binding->node;
SMJS_SET_PRIVATE(c, obj, NULL);
n->sgprivate->interact->js_binding->node=NULL;
if (gf_list_del_item(sg->objects, obj)>=0) {
gf_js_remove_root(c, &(n->sgprivate->interact->js_binding->node), GF_JSGC_OBJECT);
}
}
return;
}
while (gf_list_count(sg->objects)) {
GF_Node *n;
JSObject *obj = (JSObject *)gf_list_get(sg->objects, 0);
n = dom_get_node(c, obj);
if (n) {
if (n->sgprivate->tag == TAG_SVG_video || n->sgprivate->tag == TAG_SVG_audio) {
html_media_element_js_finalize(c, n);
}
SMJS_SET_PRIVATE(c, obj, NULL);
n->sgprivate->interact->js_binding->node=NULL;
gf_node_unregister(n, NULL);
gf_js_remove_root(c, &(n->sgprivate->interact->js_binding->node), GF_JSGC_OBJECT);
}
gf_list_rem(sg->objects, 0);
}
count = gf_list_count(dom_rt->handlers);
for (i=0; i<count; i++) {
SVG_handlerElement *handler = (SVG_handlerElement *)gf_list_get(dom_rt->handlers, i);
if ( (handler->js_context==c) && (!sg || (handler->sgprivate->scenegraph==sg)) ) {
gf_js_remove_root((JSContext *)handler->js_context, &(handler->js_fun_val), GF_JSGC_VAL);
handler->js_fun_val=0;
handler->js_context=0;
gf_list_rem(dom_rt->handlers, i);
i--;
count--;
}
}
if (sg->document) {
#ifdef USE_FFDEV_15
dom_document_finalize(NULL, sg->document);
#else
dom_document_finalize(c, sg->document);
#endif
}
}
void dom_js_unload()
{
if (!dom_rt) return;
dom_rt->nb_inst--;
if (!dom_rt->nb_inst) {
gf_list_del(dom_rt->handlers);
gf_free(dom_rt);
dom_rt = NULL;
}
}
JSObject *dom_js_define_event(JSContext *c, JSObject *global)
{
JSObject *obj = JS_DefineObject(c, global, "evt", &dom_rt->domEventClass._class, 0, 0 );
return obj;
}
JSObject *gf_dom_new_event(JSContext *c)
{
return JS_NewObject(c, &dom_rt->domEventClass._class, 0, 0);
}
JSObject *dom_js_get_node_proto(JSContext *c) {
return dom_rt->domNodeClass._proto;
}
JSObject *dom_js_get_element_proto(JSContext *c) {
return dom_rt->domElementClass._proto;
}
JSObject *dom_js_get_document_proto(JSContext *c) {
return dom_rt->domDocumentClass._proto;
}
JSObject *dom_js_get_event_proto(JSContext *c) {
return dom_rt->domEventClass._proto;
}
void dom_set_class_selector(JSContext *c, void *(*get_element_class)(GF_Node *n), void *(*get_document_class)(GF_SceneGraph *n) )
{
if (dom_rt) {
dom_rt->get_document_class = get_document_class;
dom_rt->get_element_class = get_element_class;
}
}
#endif
GF_Err gf_sg_new_from_xml_doc(const char *src, GF_SceneGraph **scene)
{
#ifdef GPAC_HAS_SPIDERMONKEY
GF_Err e;
XMLHTTPContext *ctx;
GF_SceneGraph *sg;
GF_SAFEALLOC(ctx, XMLHTTPContext);
if (!ctx) return GF_OUT_OF_MEM;
ctx->sax = gf_xml_sax_new(xml_http_sax_start, xml_http_sax_end, xml_http_sax_text, ctx);
ctx->node_stack = gf_list_new();
sg = gf_sg_new();
ctx->document = sg;
e = gf_xml_sax_parse_file(ctx->sax, src, NULL);
gf_xml_sax_del(ctx->sax);
gf_list_del(ctx->node_stack);
gf_free(ctx);
*scene = NULL;
if (e<0) {
gf_sg_del(sg);
return e;
}
*scene = sg;
return GF_OK;
#else
return GF_NOT_SUPPORTED;
#endif
}
#ifdef GPAC_HAS_SPIDERMONKEY
typedef struct
{
GF_SAXParser *sax;
GF_List *node_stack;
GF_SceneGraph *document;
} XMLReloadContext;
static void xml_reload_node_start(void *sax_cbck, const char *node_name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes)
{
GF_DOM_Event evt;
Bool is_root = GF_FALSE;
Bool modified = GF_FALSE;
Bool atts_modified = GF_FALSE;
Bool new_node = GF_FALSE;
u32 i;
GF_DOMFullAttribute *prev = NULL;
GF_DOMFullNode *par = NULL;
XMLReloadContext *ctx = (XMLReloadContext *)sax_cbck;
GF_DOMFullNode *node;
node = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
if (!node) {
is_root = GF_TRUE;
node = (GF_DOMFullNode *) ctx->document->RootNode;
}
if (is_root) {
Bool same_node = GF_TRUE;
if (strcmp(node->name, node_name)) same_node = GF_FALSE;
if (node->ns && name_space && strcmp(gf_sg_get_namespace(node->sgprivate->scenegraph, node->ns), name_space)) same_node = GF_FALSE;
if (!same_node) {
if (node->name) gf_free(node->name);
node->name = gf_strdup(node_name);
node->ns = 0;
if (name_space) {
gf_sg_add_namespace(node->sgprivate->scenegraph, (char *) name_space, NULL);
node->ns = gf_sg_get_namespace_code_from_name(node->sgprivate->scenegraph, (char*)name_space);
}
modified = GF_TRUE;
}
} else {
GF_ChildNodeItem *child = node->children;
node = NULL;
while (child) {
Bool same_node = GF_TRUE;
node = (GF_DOMFullNode*)child->node;
if (strcmp(node->name, node_name)) same_node = GF_FALSE;
if (node->ns && name_space && strcmp(gf_sg_get_namespace(node->sgprivate->scenegraph, node->ns), name_space)) same_node = GF_FALSE;
if (same_node) {
break;
}
node = NULL;
child = child->next;
}
if (!node) {
modified = GF_TRUE;
node = (GF_DOMFullNode *) gf_node_new(ctx->document, TAG_DOMFullNode);
node->name = gf_strdup(node_name);
if (name_space) {
gf_sg_add_namespace(node->sgprivate->scenegraph, (char *)name_space, NULL);
node->ns = gf_sg_get_namespace_code(node->sgprivate->scenegraph, (char *)name_space);
}
par = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
gf_node_register((GF_Node*)node, (GF_Node*)par);
gf_node_list_add_child(&par->children, (GF_Node*)node);
new_node = GF_TRUE;
}
}
gf_list_add(ctx->node_stack, node);
if (!modified) {
u32 count = 0;
GF_DOMFullAttribute *att = (GF_DOMFullAttribute *)node->attributes;
while (att) {
Bool found = GF_FALSE;
for (i=0; i<nb_attributes; i++) {
if (!stricmp(attributes[i].name, "xml:id")) {
const char *id = gf_node_get_name((GF_Node*)node);
if (!id || strcmp(id, attributes[i].value))
modified = GF_TRUE;
} else if (!strcmp(att->name, attributes[i].name)) {
found = GF_TRUE;
if (strcmp((const char *)att->data, attributes[i].value)) {
atts_modified = GF_TRUE;
gf_free(att->data);
att->data = gf_strdup(attributes[i].value);
}
}
}
if (!found) {
modified = GF_TRUE;
break;
}
count++;
att = (GF_DOMFullAttribute *)att->next;
}
if (count != nb_attributes)
modified = GF_TRUE;
}
if (modified) {
GF_DOMFullAttribute *tmp, *att;
att = (GF_DOMFullAttribute *)node->attributes;
while (att) {
if (att->name) gf_free(att->name);
if (att->data) gf_free(att->data);
tmp = att;
att = (GF_DOMFullAttribute *)att->next;
gf_free(tmp);
}
for (i=0; i<nb_attributes; i++) {
if (!stricmp(attributes[i].name, "xml:id")) {
u32 id = gf_sg_get_max_node_id(ctx->document) + 1;
gf_node_set_id((GF_Node *)node, id, attributes[i].value);
} else {
GF_DOMFullAttribute *att;
GF_SAFEALLOC(att, GF_DOMFullAttribute);
if (!att) {
GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOMJS] Failed to allocate DOM attribute\n"));
continue;
}
att->tag = TAG_DOM_ATT_any;
att->name = gf_strdup(attributes[i].name);
att->data = gf_strdup(attributes[i].value);
if (prev) prev->next = (GF_DOMAttribute*)att;
else node->attributes = (GF_DOMAttribute*)att;
prev = att;
}
}
atts_modified = GF_TRUE;
}
if (atts_modified || new_node) {
memset(&evt, 0, sizeof(GF_DOM_Event));
evt.type = new_node ? GF_EVENT_NODE_INSERTED : GF_EVENT_ATTR_MODIFIED;
evt.bubbles = 1;
evt.relatedNode = (GF_Node*) (GF_EVENT_NODE_INSERTED ? par : node);
gf_dom_event_fire((GF_Node*)node, &evt);
}
}
static void xml_reload_node_end(void *sax_cbck, const char *node_name, const char *name_space)
{
XMLReloadContext *ctx = (XMLReloadContext *)sax_cbck;
GF_DOMFullNode *par = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
if (par) {
if (strcmp(par->name, node_name)) return;
gf_list_rem_last(ctx->node_stack);
}
}
static void xml_reload_text_content(void *sax_cbck, const char *content, Bool is_cdata)
{
GF_DOM_Event evt;
u32 i, len;
GF_ChildNodeItem *child;
GF_DOMText *txt = NULL;
XMLReloadContext *ctx = (XMLReloadContext *)sax_cbck;
GF_DOMFullNode *par = (GF_DOMFullNode *)gf_list_last(ctx->node_stack);
if (!par) return;
len = (u32) strlen(content);
for (i=0; i<len; i++) {
if (!strchr(" \n\r\t", content[i])) break;
}
if (i==len) return;
child = par->children;
while (child) {
if (child->node->sgprivate->tag == TAG_DOMText) {
txt = (GF_DOMText *)child->node;
if (!strcmp(txt->textContent, content) && ((txt->type==GF_DOM_TEXT_REGULAR) || is_cdata))
return;
if (txt->textContent) gf_free(txt->textContent);
txt->textContent = gf_strdup(content);
txt->type = is_cdata ? GF_DOM_TEXT_CDATA : GF_DOM_TEXT_REGULAR;
break;
}
child = child->next;
}
if (!txt) {
txt = gf_dom_add_text_node((GF_Node *)par, gf_strdup(content) );
txt->type = is_cdata ? GF_DOM_TEXT_CDATA : GF_DOM_TEXT_REGULAR;
memset(&evt, 0, sizeof(GF_DOM_Event));
evt.type = GF_EVENT_NODE_INSERTED;
evt.bubbles = 1;
evt.relatedNode = (GF_Node*) par;
gf_dom_event_fire((GF_Node*)txt, &evt);
}
memset(&evt, 0, sizeof(GF_DOM_Event));
evt.type = GF_EVENT_CHAR_DATA_MODIFIED;
evt.bubbles = 1;
evt.relatedNode = (GF_Node*)par;
gf_dom_event_fire((GF_Node*)par, &evt);
memset(&evt, 0, sizeof(GF_DOM_Event));
evt.type = GF_EVENT_DCCI_PROP_CHANGE;
evt.bubbles = 1;
evt.relatedNode = (GF_Node*)par;
gf_dom_event_fire((GF_Node*)par, &evt);
}
#endif
GF_Err gf_sg_reload_xml_doc(const char *src, GF_SceneGraph *scene)
{
#ifndef GPAC_HAS_SPIDERMONKEY
return GF_NOT_SUPPORTED;
#else
GF_Err e;
XMLReloadContext ctx;
if (!src || !scene) return GF_BAD_PARAM;
memset(&ctx, 0, sizeof(XMLReloadContext));
ctx.document = scene;
ctx.node_stack = gf_list_new();
ctx.sax = gf_xml_sax_new(xml_reload_node_start, xml_reload_node_end, xml_reload_text_content, &ctx);
e = gf_xml_sax_parse_file(ctx.sax, src, NULL);
gf_list_del(ctx.node_stack);
gf_xml_sax_del(ctx.sax);
if (e<0) return e;
return GF_OK;
#endif
}
#endif