#include "config.h" #include #include #include #include #include #include "web_gtkhtml2.h" #include "web_gtkhtml2_js.h" typedef struct { gchar *type; JSFunction *function; gboolean useCapture; WebGtkhtml2PageData *parent; DomEventListener *listener; DomNode *node; } WebGtkhtml2JSEvent; static void web_gtkhtml2_js_dom2_event_cb (DomEventListener *listener, DomEvent *event, WebGtkhtml2JSEvent *data); static JSBool web_gtkhtml2_js_HTMLElement_getProperty ( JSContext *cx, JSObject *obj, jsval id, jsval *vp); static JSBool web_gtkhtml2_js_HTMLElement_addEventListener ( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool web_gtkhtml2_js_HTMLElement_appendChild ( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool web_gtkhtml2_js_HTMLElement_insertBefore ( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool web_gtkhtml2_js_HTMLElement_getAttribute ( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool web_gtkhtml2_js_HTMLElement_hasAttribute ( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool web_gtkhtml2_js_HTMLElement_setAttribute ( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool web_gtkhtml2_js_HTMLDocument_getProperty ( JSContext *cx, JSObject *obj, jsval id, jsval *vp); static JSBool web_gtkhtml2_js_HTMLDocument_createElement ( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool web_gtkhtml2_js_HTMLDocument_createTextNode ( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool web_gtkhtml2_js_HTMLDocument_write ( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool web_gtkhtml2_js_HTMLDocument_writeln ( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool web_gtkhtml2_js_HTMLDocument_getElementsByTagName ( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool web_gtkhtml2_js_Attr_getProperty ( JSContext *cx, JSObject *obj, jsval id, jsval *vp); static JSBool web_gtkhtml2_js_Style_getProperty ( JSContext *cx, JSObject *obj, jsval id, jsval *vp); static JSBool web_gtkhtml2_js_Style_setProperty ( JSContext *cx, JSObject *obj, jsval id, jsval *vp); static JSObject *js_HTMLElement_proto = NULL; static JSObject *js_HTMLDocument_proto = NULL; static JSObject *js_HTMLHtmlElement_proto = NULL; static JSObject *js_Attr_proto = NULL; static JSObject *js_Style_proto = NULL; static struct JSClass js_HTMLDocument = { "HTMLDocument", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, web_gtkhtml2_js_HTMLDocument_getProperty, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub }; static JSFunctionSpec js_HTMLDocument_methods[] = { {"createElement", web_gtkhtml2_js_HTMLDocument_createElement, 1, 0, 0}, {"createTextNode", web_gtkhtml2_js_HTMLDocument_createTextNode, 1, 0, 0}, {"getElementsByTagName", web_gtkhtml2_js_HTMLDocument_getElementsByTagName, 1, 0, 0}, {"write", web_gtkhtml2_js_HTMLDocument_write, 1, 0, 0}, {"writeln", web_gtkhtml2_js_HTMLDocument_writeln, 1, 0, 0}, {0} }; enum js_HTMLDocument_tinyid { JS_DOC_URL, JS_DOC_TITLE, JS_DOC_DOMAIN, JS_DOC_BODY }; static JSPropertySpec js_HTMLDocument_props[] = { {"URL", JS_DOC_URL, JSPROP_READONLY | JSPROP_PERMANENT}, {"title", JS_DOC_TITLE, JSPROP_READONLY | JSPROP_PERMANENT}, {"domain", JS_DOC_DOMAIN, JSPROP_READONLY | JSPROP_PERMANENT}, {"body", JS_DOC_BODY, JSPROP_READONLY | JSPROP_PERMANENT}, {0} }; static struct JSClass js_HTMLHtmlElement = { "HTMLHtmlElement", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub }; static JSPropertySpec js_HTMLHtmlElement_methods[] = { {0} }; static JSPropertySpec js_HTMLHtmlElement_props[] = { {0} }; static struct JSClass js_HTMLElement = { "HTMLElement", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, web_gtkhtml2_js_HTMLElement_getProperty, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub }; enum js_HTMLElement_tinyid { JS_FIRST_CHILD, JS_LAST_CHILD, JS_NEXT_SIBLING, JS_PREVIOUS_SIBLING, JS_PARENT_NODE, JS_CHILD_NODES, JS_NODE_NAME, JS_NODE_VALUE, JS_NODE_TYPE, JS_TAG_NAME, JS_STYLE, JS_ELEMENT_NODE, JS_ATTRIBUTE_NODE, JS_TEXT_NODE, JS_CDATA_SECTION_NODE, JS_ENTITY_REFERENCE_NODE, JS_ENTITY_NODE, JS_PROCESSING_INSTRUCTION_NODE, JS_COMMENT_NODE, JS_DOCUMENT_NODE, JS_DOCUMENT_TYPE_NODE, JS_DOCUMENT_FRAGMENT_NODE, JS_NOTATION_NODE }; static JSFunctionSpec js_HTMLElement_methods[] = { {"addEventListener", web_gtkhtml2_js_HTMLElement_addEventListener, 1, 0, 0}, {"appendChild", web_gtkhtml2_js_HTMLElement_appendChild, 1, 0, 0}, {"insertBefore", web_gtkhtml2_js_HTMLElement_insertBefore, 1, 0, 0}, {"getAttribute", web_gtkhtml2_js_HTMLElement_getAttribute, 1, 0, 0}, {"hasAttribute", web_gtkhtml2_js_HTMLElement_hasAttribute, 1, 0, 0}, {"setAttribute", web_gtkhtml2_js_HTMLElement_setAttribute, 1, 0, 0}, {0} }; static JSPropertySpec js_HTMLElement_props[] = { {"firstChild", JS_FIRST_CHILD, JSPROP_READONLY | JSPROP_PERMANENT}, {"lastChild", JS_LAST_CHILD, JSPROP_READONLY | JSPROP_PERMANENT}, {"nextSibling", JS_NEXT_SIBLING, JSPROP_READONLY | JSPROP_PERMANENT}, {"previousSibling", JS_PREVIOUS_SIBLING, JSPROP_READONLY | JSPROP_PERMANENT}, {"parentNode", JS_PARENT_NODE, JSPROP_READONLY | JSPROP_PERMANENT}, {"childNodes", JS_CHILD_NODES, JSPROP_READONLY | JSPROP_PERMANENT}, {"nodeName", JS_NODE_NAME, JSPROP_READONLY | JSPROP_PERMANENT}, {"nodeValue", JS_NODE_VALUE, JSPROP_READONLY | JSPROP_PERMANENT}, {"nodeType", JS_NODE_TYPE, JSPROP_READONLY | JSPROP_PERMANENT}, {"tagName", JS_TAG_NAME, JSPROP_READONLY | JSPROP_PERMANENT}, {"style", JS_STYLE, JSPROP_READONLY | JSPROP_PERMANENT}, {"ELEMENT_NODE", JS_ELEMENT_NODE, JSPROP_READONLY | JSPROP_PERMANENT}, {"ATTRIBUTE_NODE", JS_ATTRIBUTE_NODE, JSPROP_READONLY | JSPROP_PERMANENT}, {"TEXT_NODE", JS_TEXT_NODE, JSPROP_READONLY | JSPROP_PERMANENT}, {"CDATA_SECTION_NODE", JS_CDATA_SECTION_NODE, JSPROP_READONLY | JSPROP_PERMANENT}, {"ENTITY_REFERENCE_NODE", JS_ENTITY_REFERENCE_NODE, JSPROP_READONLY | JSPROP_PERMANENT}, {"ENTITY_NODE", JS_ENTITY_NODE, JSPROP_READONLY | JSPROP_PERMANENT}, {"PROCESSING_INSTRUCTION_NODE", JS_PROCESSING_INSTRUCTION_NODE, JSPROP_READONLY | JSPROP_PERMANENT}, {"COMMENT_NODE", JS_COMMENT_NODE, JSPROP_READONLY | JSPROP_PERMANENT}, {"DOCUMENT_NODE", JS_DOCUMENT_NODE, JSPROP_READONLY | JSPROP_PERMANENT}, {"DOCUMENT_TYPE_NODE", JS_DOCUMENT_TYPE_NODE, JSPROP_READONLY | JSPROP_PERMANENT}, {"DOCUMENT_FRAGMENT_NODE", JS_DOCUMENT_FRAGMENT_NODE, JSPROP_READONLY | JSPROP_PERMANENT}, {"NOTATION_NODE", JS_NOTATION_NODE, JSPROP_READONLY | JSPROP_PERMANENT}, {0} }; static struct JSClass js_Attr = { "Attr", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub }; enum js_Attr_tinyid { JS_ATTR_NAME, JS_ATTR_VALUE }; static JSPropertySpec js_Attr_methods[] = { {0} }; static JSPropertySpec js_Attr_props[] = { {"name", JS_ATTR_NAME, JSPROP_READONLY | JSPROP_PERMANENT}, {"value", JS_ATTR_VALUE, JSPROP_READONLY | JSPROP_PERMANENT}, {0} }; static struct JSClass js_Style = { "Style", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, web_gtkhtml2_js_Style_getProperty, web_gtkhtml2_js_Style_setProperty, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub }; enum js_Style_tinyid { JS_STYLE_DISPLAY, JS_STYLE_TEXT_DECORATION, JS_STYLE_CURSOR }; static JSPropertySpec js_Style_methods[] = { {0} }; static JSPropertySpec js_Style_props[] = { {"display", JS_STYLE_DISPLAY, JSPROP_PERMANENT}, {"textDecoration", JS_STYLE_TEXT_DECORATION, JSPROP_PERMANENT}, {"cursor", JS_STYLE_CURSOR, JSPROP_PERMANENT}, {0} }; typedef enum { JS_DOM_NODE, JS_HTML_STYLE } WebGtkhtml2JSObject; typedef struct { gpointer key; gpointer data; WebGtkhtml2JSObject type; } WebGtkhtml2JSObjFindData; typedef struct { WebGtkhtml2JSObject type; gpointer data; } WebGtkhtml2JSObjectData; static gboolean web_gtkhtml2_js_find_object (gpointer key, gpointer value, gpointer data) { WebGtkhtml2JSObjFindData *find_data = (WebGtkhtml2JSObjFindData *)data; WebGtkhtml2JSObjectData *obj_data = value; if ((obj_data->data == find_data->data) && (obj_data->type == find_data->type)) { find_data->key = key; return TRUE; } else return FALSE; } static gpointer web_gtkhtml2_js_obj_lookup (GHashTable *table, gpointer key) { WebGtkhtml2JSObjectData *obj_data = g_hash_table_lookup (table, key); if (obj_data) return obj_data->data; else return NULL; } static JSObject * web_gtkhtml2_js_get_object (WebGtkhtml2PageData *data, WebGtkhtml2JSObject type, gpointer gobject) { WebGtkhtml2JSObjFindData find_data; WebGtkhtml2JSObjectData *obj_data; JSObject *object; find_data.type = type; find_data.data = gobject; obj_data = g_hash_table_find (data->js_objects, web_gtkhtml2_js_find_object, &find_data); if (obj_data) return (JSObject *)find_data.key; switch (type) { case JS_DOM_NODE : { DomNode *node = DOM_NODE (gobject); DomString *name; if (DOM_IS_ATTR (gobject)) { object = JS_NewObject ( (JSContext *)data->js_context, &js_Attr, js_Attr_proto, (JSObject *)data->js_document); break; } name = dom_Node__get_nodeName (node); if ((name) && (strcmp (name, "html") == 0)) { object = JS_NewObject ( (JSContext *)data->js_context, &js_HTMLHtmlElement, js_HTMLHtmlElement_proto, (JSObject *)data->js_document); } else { object = JS_NewObject ( (JSContext *)data->js_context, &js_HTMLElement, js_HTMLElement_proto, (JSObject *)data->js_document); } g_free (name); break; } case JS_HTML_STYLE : { object = JS_NewObject ( (JSContext *)data->js_context, &js_Style, js_Style_proto, (JSObject *)data->js_document); break; } } if (object) { obj_data = g_new (WebGtkhtml2JSObjectData, 1); obj_data->type = type; obj_data->data = gobject; g_hash_table_insert (data->js_objects, object, obj_data); JS_SetPrivate ((JSContext *)data->js_context, object, data); } return object; } static JSBool web_gtkhtml2_js_HTMLElement_getProperty (JSContext *cx, JSObject *obj, jsval id, jsval *vp) { WebGtkhtml2PageData *data = JS_GetPrivate (cx, obj); DomNode *node, *node2 = NULL; DomString *string = NULL; node = web_gtkhtml2_js_obj_lookup (data->js_objects, obj); g_assert (node); if (JSVAL_IS_INT (id)) { switch (JSVAL_TO_INT (id)) { case JS_FIRST_CHILD : node2 = dom_Node__get_firstChild (node); if (!node2) { *vp = JSVAL_NULL; break; } case JS_LAST_CHILD : if (!node2) { node2 = dom_Node__get_lastChild (node); if (!node2) { *vp = JSVAL_NULL; break; } } case JS_NEXT_SIBLING : if (!node2) { node2 = dom_Node__get_nextSibling ( node); if (!node2) { *vp = JSVAL_NULL; break; } } case JS_PREVIOUS_SIBLING : if (!node2) { node2 = dom_Node__get_previousSibling ( node); if (!node2) { *vp = JSVAL_NULL; break; } } case JS_PARENT_NODE : { JSObject *object; if (!node2) node2 = dom_Node__get_parentNode ( node); if (!node2) { *vp = JSVAL_NULL; break; } object = web_gtkhtml2_js_get_object ( data, JS_DOM_NODE, node2); *vp = OBJECT_TO_JSVAL (object); break; } case JS_CHILD_NODES : { gulong i, length; JSObject *array; DomNodeList *node_list = dom_Node__get_childNodes (node); if (!node_list) { *vp = JSVAL_NULL; break; } length = dom_NodeList__get_length (node_list); if (length == 0) { g_object_unref (node_list); *vp = JSVAL_NULL; break; } array = JS_NewArrayObject (cx, 0, NULL); for (i = 0; i < length; i++) { JSObject *object; jsval val; node2 = dom_NodeList__get_item ( node_list, i); object = web_gtkhtml2_js_get_object ( data, JS_DOM_NODE, G_OBJECT (node2)); val = OBJECT_TO_JSVAL (object); JS_SetElement ( cx, array, (jsint)i, &val); } g_object_unref (node_list); *vp = OBJECT_TO_JSVAL (array); break; } case JS_NODE_NAME : { string = dom_Node__get_nodeName (node); if (!string) { *vp = JSVAL_NULL; break; } } case JS_TAG_NAME : { if (!string) { if (DOM_IS_ELEMENT (node)) { gchar *tag = dom_Element__get_tagName ( DOM_ELEMENT (node)); string = g_ascii_strup ( tag, -1); g_free (tag); } else { *vp = JSVAL_NULL; break; } } } case JS_NODE_VALUE : { if (!string) string = dom_Node__get_nodeValue ( node, NULL); if (!string) { *vp = JSVAL_NULL; break; } *vp = STRING_TO_JSVAL (JS_NewStringCopyZ ( cx, string)); g_free (string); break; } case JS_STYLE : { JSObject *object; object = web_gtkhtml2_js_get_object ( data, JS_HTML_STYLE, node); *vp = OBJECT_TO_JSVAL (object); break; } case JS_NODE_TYPE : { *vp = INT_TO_JSVAL ( (int)dom_Node__get_nodeType (node)); break; } case JS_ELEMENT_NODE : { *vp = INT_TO_JSVAL (DOM_ELEMENT_NODE); break; } case JS_ATTRIBUTE_NODE : { *vp = INT_TO_JSVAL (DOM_ATTRIBUTE_NODE); break; } case JS_TEXT_NODE : { *vp = INT_TO_JSVAL (DOM_TEXT_NODE); break; } case JS_CDATA_SECTION_NODE : { *vp = INT_TO_JSVAL (DOM_CDATA_SECTION_NODE); break; } case JS_ENTITY_REFERENCE_NODE : { *vp = INT_TO_JSVAL (DOM_ENTITY_REFERENCE_NODE); break; } case JS_ENTITY_NODE : { *vp = INT_TO_JSVAL (DOM_ENTITY_NODE); break; } case JS_PROCESSING_INSTRUCTION_NODE : { *vp = INT_TO_JSVAL ( DOM_PROCESSING_INSTRUCTION_NODE); break; } case JS_COMMENT_NODE : { *vp = INT_TO_JSVAL (DOM_COMMENT_NODE); break; } case JS_DOCUMENT_NODE : { *vp = INT_TO_JSVAL (DOM_DOCUMENT_NODE); break; } case JS_DOCUMENT_TYPE_NODE : { *vp = INT_TO_JSVAL (DOM_DOCUMENT_TYPE_NODE); break; } case JS_DOCUMENT_FRAGMENT_NODE : { *vp = INT_TO_JSVAL (DOM_DOCUMENT_FRAGMENT_NODE); break; } case JS_NOTATION_NODE : { *vp = INT_TO_JSVAL (DOM_NOTATION_NODE); break; } } } return JS_TRUE; } static JSBool web_gtkhtml2_js_HTMLElement_addEventListener (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { WebGtkhtml2PageData *data = JS_GetPrivate (cx, obj); DomNode *node = DOM_NODE (web_gtkhtml2_js_obj_lookup ( data->js_objects, obj)); WebGtkhtml2JSEvent *event; DomEventListener *listener; JSBool useCapture; if (argc < 3) return JS_TRUE; event = g_new (WebGtkhtml2JSEvent, 1); JS_ValueToBoolean (cx, argv[2], &useCapture); event->type = g_strdup (JS_GetStringBytes ( JS_ValueToString (cx, argv[0]))); event->function = JS_ValueToFunction (cx, argv[1]); event->useCapture = useCapture; event->parent = data; event->node = node; event->listener = dom_event_listener_signal_new (); g_list_prepend (data->js_events, event); g_signal_connect (G_OBJECT (event->listener), "event", G_CALLBACK (web_gtkhtml2_js_dom2_event_cb), event); dom_EventTarget_addEventListener (DOM_EVENT_TARGET (node), event->type, event->listener, event->useCapture); return JS_TRUE; } static JSBool web_gtkhtml2_js_HTMLElement_appendChild (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { WebGtkhtml2PageData *pdata; DomNode *node, *node2; if ((argc < 1) || (!JSVAL_IS_OBJECT (argv[0]))) return JS_TRUE; pdata = JS_GetPrivate (cx, obj); node = DOM_NODE (web_gtkhtml2_js_obj_lookup (pdata->js_objects, obj)); node2 = DOM_NODE (web_gtkhtml2_js_obj_lookup (pdata->js_objects, JSVAL_TO_OBJECT (argv[0]))); if (node2) { JSObject *object; node2 = dom_Node_appendChild (node, node2, NULL); if (node2) { object = web_gtkhtml2_js_get_object ( pdata, JS_DOM_NODE, node2); *rval = OBJECT_TO_JSVAL (object); g_debug ("Appended child"); } } return JS_TRUE; } static JSBool web_gtkhtml2_js_HTMLElement_insertBefore (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { WebGtkhtml2PageData *pdata; DomNode *node, *node2, *target; if ((argc < 2) || (!JSVAL_IS_OBJECT (argv[0])) || (!JSVAL_IS_OBJECT (argv[1]))) return JS_TRUE; pdata = JS_GetPrivate (cx, obj); node = DOM_NODE (web_gtkhtml2_js_obj_lookup (pdata->js_objects, obj)); node2 = DOM_NODE (web_gtkhtml2_js_obj_lookup (pdata->js_objects, JSVAL_TO_OBJECT (argv[0]))); target = DOM_NODE (web_gtkhtml2_js_obj_lookup (pdata->js_objects, JSVAL_TO_OBJECT (argv[1]))); if (node2 && target) { JSObject *object; node2 = dom_Node_insertBefore (node, node2, target, NULL); if (node2) { object = web_gtkhtml2_js_get_object ( pdata, JS_DOM_NODE, node2); *rval = OBJECT_TO_JSVAL (object); g_debug ("Inserted before"); } } return JS_TRUE; } static JSBool web_gtkhtml2_js_HTMLElement_hasAttribute (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { WebGtkhtml2PageData *data = JS_GetPrivate (cx, obj); DomNode *node = DOM_NODE (web_gtkhtml2_js_obj_lookup ( data->js_objects, obj)); const gchar *attr_name; if (argc < 1) return JS_TRUE; attr_name = JS_GetStringBytes (JS_ValueToString (cx, argv[0])); *rval = BOOLEAN_TO_JSVAL (dom_Element_hasAttribute (DOM_ELEMENT (node), attr_name)); return JS_TRUE; } static JSBool web_gtkhtml2_js_HTMLElement_getAttribute (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { WebGtkhtml2PageData *data = JS_GetPrivate (cx, obj); DomNode *node = DOM_NODE (web_gtkhtml2_js_obj_lookup ( data->js_objects, obj)); const gchar *attr_name; gchar *attr_value; if (argc < 1) return JS_TRUE; attr_name = JS_GetStringBytes (JS_ValueToString (cx, argv[0])); attr_value = dom_Element_getAttribute (DOM_ELEMENT (node), attr_name); *rval = STRING_TO_JSVAL (JS_NewStringCopyZ (cx, attr_value)); g_free (attr_value); return JS_TRUE; } static JSBool web_gtkhtml2_js_HTMLElement_setAttribute (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { WebGtkhtml2PageData *data = JS_GetPrivate (cx, obj); DomNode *node = DOM_NODE (web_gtkhtml2_js_obj_lookup ( data->js_objects, obj)); const gchar *attr_name, *attr_value; if (argc < 2) return JS_TRUE; attr_name = JS_GetStringBytes (JS_ValueToString (cx, argv[0])); attr_value = JS_GetStringBytes (JS_ValueToString (cx, argv[1])); dom_Element_setAttribute (DOM_ELEMENT (node), attr_name, attr_value); return JS_TRUE; } static JSBool web_gtkhtml2_js_Attr_getProperty (JSContext *cx, JSObject *obj, jsval id, jsval *vp) { WebGtkhtml2PageData *data = JS_GetPrivate (cx, obj); DomAttr *attr; DomString *string = NULL; attr = DOM_ATTR (web_gtkhtml2_js_obj_lookup (data->js_objects, obj)); if (JSVAL_IS_INT (id)) { switch (JSVAL_TO_INT (id)) { case JS_ATTR_NAME : string = dom_Attr__get_name (attr); case JS_ATTR_VALUE : if (!string) string = dom_Attr__get_value (attr); if (!string) { *vp = JSVAL_NULL; break; } *vp = STRING_TO_JSVAL (JS_NewString ( cx, string, strlen (string))); g_free (string); break; } } return JS_TRUE; } static JSBool web_gtkhtml2_js_HTMLDocument_createElement (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { WebGtkhtml2PageData *pdata; DomNode *node, *new_node; if ((argc < 1) || (!JSVAL_IS_STRING (argv[0]))) return JS_TRUE; pdata = JS_GetPrivate (cx, obj); node = DOM_NODE (web_gtkhtml2_js_obj_lookup (pdata->js_objects, obj)); new_node = DOM_NODE (dom_Document_createElement (DOM_DOCUMENT (node), (const DomString *)JS_GetStringBytes ( JS_ValueToString (cx, argv[0])))); if (new_node) { JSObject *object = web_gtkhtml2_js_get_object ( pdata, JS_DOM_NODE, new_node); *rval = OBJECT_TO_JSVAL (object); } return JS_TRUE; } static JSBool web_gtkhtml2_js_HTMLDocument_createTextNode (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { WebGtkhtml2PageData *pdata; DomNode *node, *new_node; if ((argc < 1) || (!JSVAL_IS_STRING (argv[0]))) return JS_TRUE; pdata = JS_GetPrivate (cx, obj); node = DOM_NODE (web_gtkhtml2_js_obj_lookup (pdata->js_objects, obj)); new_node = DOM_NODE (dom_Document_createTextNode (DOM_DOCUMENT (node), (const DomString *)JS_GetStringBytes ( JS_ValueToString (cx, argv[0])))); if (new_node) { JSObject *object = web_gtkhtml2_js_get_object ( pdata, JS_DOM_NODE, new_node); *rval = OBJECT_TO_JSVAL (object); } return JS_TRUE; } typedef struct { const gchar *name; GList *nodes; } ElementsByTagNameData; static void web_gtkhtml2_js_get_elements_by_tag_name_cb (DomNode *node, ElementsByTagNameData *data) { gchar *name; if (!DOM_IS_ELEMENT (node)) return; name = dom_Element__get_tagName (DOM_ELEMENT (node)); if (strcasecmp (name, data->name) == 0) { data->nodes = g_list_prepend (data->nodes, node); } g_free (name); } static JSBool web_gtkhtml2_js_HTMLDocument_getElementsByTagName (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { WebGtkhtml2PageData *pdata; ElementsByTagNameData data; GList *e; DomNode *node; JSObject *array; gint i; if ((argc < 1) || (!JSVAL_IS_STRING (argv[0]))) return JS_TRUE; pdata = JS_GetPrivate (cx, obj); node = DOM_NODE (web_gtkhtml2_js_obj_lookup (pdata->js_objects, obj)); data.nodes = NULL; data.name = JS_GetStringBytes (JS_ValueToString (cx, argv[0])); web_gtkhtml2_dom_foreach (node, (GFunc)web_gtkhtml2_js_get_elements_by_tag_name_cb, &data); if (!data.nodes) return JS_TRUE; array = JS_NewArrayObject (cx, 0, NULL); for (e = data.nodes, i = 0; e; e = e->next, i++) { JSObject *object; jsval val; object = web_gtkhtml2_js_get_object (pdata, JS_DOM_NODE, G_OBJECT (e->data)); val = OBJECT_TO_JSVAL (object); JS_SetElement (cx, array, (jsint)i, &val); } g_list_free (data.nodes); *rval = OBJECT_TO_JSVAL (array); return JS_TRUE; } static JSBool web_gtkhtml2_js_HTMLDocument_write (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { gint i; xmlNodePtr list; DomNode *node, *new_node, *sibling, *parent; GList *s, *removed = NULL; WebGtkhtml2PageData *data; data = JS_GetPrivate (cx, obj); node = data->node; parent = dom_Node__get_parentNode (node); for (i = 0; i < argc; i++) { if (JSVAL_IS_STRING (argv[i])) { const gchar *string = JS_GetStringBytes ( JS_ValueToString (cx, argv[i])); /* g_debug ("Printing %s to document", string);*/ if (!data->inject) data->inject = g_strdup (string); else { data->inject = g_realloc (data->inject, strlen (data->inject) + strlen (string) + 1); memmove (data->inject + strlen (data->inject), string, strlen (string) + 1); } } } return JS_TRUE; } static JSBool web_gtkhtml2_js_HTMLDocument_writeln (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { gint i; xmlNodePtr list; DomNode *node, *new_node, *sibling, *parent; GList *s, *removed = NULL; WebGtkhtml2PageData *data; data = JS_GetPrivate (cx, obj); node = data->node; parent = dom_Node__get_parentNode (node); for (i = 0; i < argc; i++) { if (JSVAL_IS_STRING (argv[i])) { gchar *string = g_strdup_printf ("%s\n", JS_GetStringBytes (JS_ValueToString ( cx, argv[i]))); /* g_debug ("Printing %s to document", string);*/ if (!data->inject) data->inject = string; else { data->inject = g_realloc (data->inject, strlen (data->inject) + strlen (string) + 1); memmove (data->inject + strlen (data->inject), string, strlen (string) + 1); g_free (string); } } } return JS_TRUE; } gboolean web_gtkhtml2_js_find_by_tagName (gpointer a, gpointer b) { DomNode *node = (DomNode *)a; const gchar *find = (const gchar *)b; DomString *tag; if (!DOM_IS_ELEMENT (node)) return FALSE; tag = dom_Element__get_tagName (DOM_ELEMENT (node)); if (strcasecmp (tag, find) == 0) { g_free (tag); return TRUE; } else { g_free (tag); return FALSE; } } static JSBool web_gtkhtml2_js_HTMLDocument_getProperty (JSContext *cx, JSObject *obj, jsval id, jsval *vp) { WebGtkhtml2PageData *data = JS_GetPrivate (cx, obj); WebHistory *history = g_list_last (data->data->history)->data; if (JSVAL_IS_INT (id)) { switch (JSVAL_TO_INT (id)) { case JS_DOC_URL : { *vp = STRING_TO_JSVAL (JS_NewString ( cx, data->data->url, strlen (data->data->url))); break; } case JS_DOC_TITLE : { *vp = STRING_TO_JSVAL (JS_NewString ( cx, history->title, strlen (history->title))); break; } case JS_DOC_DOMAIN : { gchar *domain; gchar *base = web_get_base_url ( data->data->url); domain = strstr (base, "://"); if (domain) { gchar *end = strchr (domain + 3, '/'); if (end) *end = '\0'; *vp = STRING_TO_JSVAL ( JS_NewStringCopyZ ( cx, domain + 3)); } g_free (base); break; } case JS_DOC_BODY : { DomNode *node; node = web_gtkhtml2_dom_find (DOM_NODE ( data->document->dom_document), web_gtkhtml2_js_find_by_tagName, "body"); if (node) { JSObject *object; object = web_gtkhtml2_js_get_object ( data, JS_DOM_NODE, G_OBJECT (node)); *vp = OBJECT_TO_JSVAL (object); } break; } } } return JS_TRUE; } static JSBool web_gtkhtml2_js_Style_getProperty (JSContext *cx, JSObject *obj, jsval id, jsval *vp) { WebGtkhtml2PageData *data = JS_GetPrivate (cx, obj); DomNode *node = (DomNode *) web_gtkhtml2_js_obj_lookup (data->js_objects, obj); HtmlStyle *style = node->style; if (JSVAL_IS_INT (id)) { switch (JSVAL_TO_INT (id)) { case JS_STYLE_DISPLAY : { switch (style->display) { case HTML_DISPLAY_INLINE : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "inline", strlen ("inline"))); break; case HTML_DISPLAY_BLOCK : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "block", strlen ("block"))); break; case HTML_DISPLAY_LIST_ITEM : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "list-item", strlen ("list-item"))); break; case HTML_DISPLAY_RUN_IN : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "run-in", strlen ("run-in"))); break; case HTML_DISPLAY_COMPACT : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "compact", strlen ("compact"))); break; case HTML_DISPLAY_MARKER : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "marker", strlen ("marker"))); break; case HTML_DISPLAY_TABLE : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "table", strlen ("table"))); break; case HTML_DISPLAY_INLINE_TABLE : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "inline-table", strlen ( "inline-table"))); break; case HTML_DISPLAY_TABLE_ROW_GROUP : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "table-row-group", strlen ( "table-row-group"))); break; case HTML_DISPLAY_TABLE_HEADER_GROUP : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "table-header-group", strlen ( "table-header-group"))); break; case HTML_DISPLAY_TABLE_FOOTER_GROUP : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "table-footer-group", strlen ( "table-footer-group"))); break; case HTML_DISPLAY_TABLE_ROW : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "table-row", strlen ("table-row"))); break; case HTML_DISPLAY_TABLE_COLUMN_GROUP : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "table-column-group", strlen ( "table-column-group"))); break; case HTML_DISPLAY_TABLE_COLUMN : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "table-column", strlen ( "table-column"))); break; case HTML_DISPLAY_TABLE_CELL : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "table-cell", strlen ("table-cell"))); break; case HTML_DISPLAY_TABLE_CAPTION : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "table-caption", strlen ( "table-caption"))); break; case HTML_DISPLAY_NONE : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "none", strlen ("none"))); break; } break; } case JS_STYLE_TEXT_DECORATION : { guint dec = style->inherited-> font_spec->decoration; gchar *string = NULL; if (dec == 0) { *vp = STRING_TO_JSVAL (JS_NewString ( cx, "none", strlen ("none"))); break; } if (dec & HTML_FONT_DECORATION_UNDERLINE) { gchar *oldstring = string; string = g_strjoin (", ", string, "underline", NULL); g_free (oldstring); } if (dec & HTML_FONT_DECORATION_OVERLINE) { gchar *oldstring = string; string = g_strjoin (", ", string, "overline", NULL); g_free (oldstring); } if (dec & HTML_FONT_DECORATION_LINETHROUGH) { gchar *oldstring = string; string = g_strjoin (", ", string, "line-through", NULL); g_free (oldstring); } *vp = STRING_TO_JSVAL (JS_NewString ( cx, string, strlen (string))); g_free (string); break; } case JS_STYLE_CURSOR : { switch (style->inherited->cursor) { case HTML_CURSOR_AUTO : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "auto", strlen ("auto"))); break; case HTML_CURSOR_CROSSHAIR : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "crosshair", strlen ("crosshair"))); break; case HTML_CURSOR_DEFAULT : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "default", strlen ("default"))); break; case HTML_CURSOR_POINTER : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "pointer", strlen ("pointer"))); break; case HTML_CURSOR_MOVE : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "move", strlen ("move"))); break; case HTML_CURSOR_E_RESIZE : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "e-resize", strlen ("e-resize"))); break; case HTML_CURSOR_NE_RESIZE : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "ne-resize", strlen ("ne-resize"))); break; case HTML_CURSOR_NW_RESIZE : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "nw-resize", strlen ("nw-resize"))); break; case HTML_CURSOR_N_RESIZE : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "n-resize", strlen ("n-resize"))); break; case HTML_CURSOR_SE_RESIZE : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "se-resize", strlen ("se-resize"))); break; case HTML_CURSOR_SW_RESIZE : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "sw-resize", strlen ("sw-resize"))); break; case HTML_CURSOR_S_RESIZE : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "s-resize", strlen ("s-resize"))); break; case HTML_CURSOR_W_RESIZE : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "w-resize", strlen ("w-resize"))); break; case HTML_CURSOR_TEXT : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "text", strlen ("text"))); break; case HTML_CURSOR_WAIT : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "wait", strlen ("wait"))); break; case HTML_CURSOR_HELP : *vp = STRING_TO_JSVAL ( JS_NewString ( cx, "help", strlen ("help"))); break; } break; } } } return JS_TRUE; } static JSBool web_gtkhtml2_js_Style_setProperty (JSContext *cx, JSObject *obj, jsval id, jsval *vp) { WebGtkhtml2PageData *data = JS_GetPrivate (cx, obj); DomNode *node; HtmlStyle *style; const gchar *string; if ((!JSVAL_IS_INT (id)) || (!JSVAL_IS_STRING (*vp))) return JS_TRUE; node = (DomNode *)web_gtkhtml2_js_obj_lookup (data->js_objects, obj); style = dom_Node__get_style (node); /* FIXME: Handle unset style? */ if (!style) return JS_TRUE; style = html_style_dup (style); string = JS_GetStringBytes (JS_ValueToString (cx, *vp)); switch (JSVAL_TO_INT (id)) { case JS_STYLE_DISPLAY : { if (strcmp (string, "inline") == 0) { style->display = HTML_DISPLAY_INLINE; } else if (strcmp (string, "block") == 0) { style->display = HTML_DISPLAY_BLOCK; } else if (strcmp (string, "list-item") == 0) { style->display = HTML_DISPLAY_LIST_ITEM; } else if (strcmp (string, "run-in") == 0) { style->display = HTML_DISPLAY_RUN_IN; } else if (strcmp (string, "compact") == 0) { style->display = HTML_DISPLAY_COMPACT; } else if (strcmp (string, "marker") == 0) { style->display = HTML_DISPLAY_MARKER; } else if (strcmp (string, "table") == 0) { style->display = HTML_DISPLAY_TABLE; } else if (strcmp (string, "inline-table") == 0) { style->display = HTML_DISPLAY_INLINE_TABLE; } else if (strcmp (string, "table-row-group") == 0) { style->display = HTML_DISPLAY_TABLE_ROW_GROUP; } else if (strcmp (string, "table-header-group") == 0) { style->display = HTML_DISPLAY_TABLE_HEADER_GROUP; } else if (strcmp (string, "table-footer-group") == 0) { style->display = HTML_DISPLAY_TABLE_FOOTER_GROUP; } else if (strcmp (string, "table-row") == 0) { style->display = HTML_DISPLAY_TABLE_ROW; } else if (strcmp (string, "table-column-group") == 0) { style->display = HTML_DISPLAY_TABLE_COLUMN_GROUP; } else if (strcmp (string, "table-column") == 0) { style->display = HTML_DISPLAY_TABLE_COLUMN; } else if (strcmp (string, "table-cell") == 0) { style->display = HTML_DISPLAY_TABLE_CELL; } else if (strcmp (string, "table-caption") == 0) { style->display = HTML_DISPLAY_TABLE_CAPTION; } else if (strcmp (string, "none") == 0) { style->display = HTML_DISPLAY_NONE; } break; } case JS_STYLE_TEXT_DECORATION : { g_debug ("Trying to set style.textDecoration"); break; } case JS_STYLE_CURSOR : { g_debug ("Trying to set style.cursor"); break; } } dom_Node__set_style (node, style); return JS_TRUE; } static void web_gtkhtml2_js_event_free (WebGtkhtml2JSEvent *event) { g_object_unref (event->listener); g_free (event->type); g_free (event); } void web_gtkhtml2_js_parse (WebGtkhtml2Data *backend_data, WebGtkhtml2PageData *page_data, const char *script, gint script_size) { jsval rval; /* g_debug ("Parsing script (%d): %s", script_size, script);*/ JS_EvaluateScript ( (JSContext *)page_data->js_context, (JSObject *)page_data->js_global, script, script_size, G_STRFUNC, G_STRLOC, &rval); } static void web_gtkhtml2_js_dom2_event_cb (DomEventListener *listener, DomEvent *event, WebGtkhtml2JSEvent *data) { jsval rval; JSObject *object = web_gtkhtml2_js_get_object ( data->parent, JS_DOM_NODE, G_OBJECT (data->node)); g_debug ("Event listener triggered"); /* TODO: Create the event object and pass as argument */ /* TODO: Check return value for cancellable events */ JS_CallFunction ((JSContext *)data->parent->js_context, object, data->function, 0, NULL, &rval); } static void web_gtkhtml2_js_run_from_attr (DomNode *node, const gchar *attr, WebGtkhtml2PageData *pdata) { const gchar *script; if (!DOM_IS_ELEMENT (node)) return; script = (const gchar *)dom_Element_getAttribute ( DOM_ELEMENT (node), attr); if (script) { g_debug ("Executing script: %s", script); web_gtkhtml2_js_parse ( pdata->parent, pdata, script, strlen (script)); } } static void web_gtkhtml2_js_dom_event_cb (DomEventListener *listener, DomEvent *event, WebGtkhtml2PageData *pdata) { gchar *type = dom_Event__get_type (event); DomNode *node = DOM_NODE (dom_Event__get_currentTarget (event)); gboolean return_value = FALSE; if (!node) node = DOM_NODE (dom_Event__get_target (event)); if (strcmp (type, "mousedown") == 0) { web_gtkhtml2_js_run_from_attr (node, "onmousedown", pdata); if (return_value) dom_Event_preventDefault (event); } else if (strcmp (type, "mouseup") == 0) { web_gtkhtml2_js_run_from_attr (node, "onmouseup", pdata); if (return_value) dom_Event_preventDefault (event); } else if (strcmp (type, "click") == 0) { web_gtkhtml2_js_run_from_attr (node, "onmouseclick", pdata); if (return_value) dom_Event_preventDefault (event); } else if (strcmp (type, "mouseover") == 0) { web_gtkhtml2_js_run_from_attr (node, "onmouseover", pdata); if (return_value) dom_Event_preventDefault (event); } else if (strcmp (type, "mouseout") == 0) { web_gtkhtml2_js_run_from_attr (node, "onmouseout", pdata); if (return_value) dom_Event_preventDefault (event); } } #define web_gtkhtml2_js_define_base_class(c,s,x) \ x##_proto = JS_InitClass (\ c, s,\ NULL,\ &x, NULL, 0,\ &x##_props, &x##_methods,\ NULL, NULL);\ #define web_gtkhtml2_js_define_base_class_with_constructor(c,s,x) \ x##_proto = JS_InitClass (\ c, s,\ NULL,\ &x, web_gtkhtml2_##x##_constructor, 0,\ &x##_props, &x##_methods,\ NULL, NULL);\ #define web_gtkhtml2_js_define_class(c,s,x,y) \ x##_proto = JS_InitClass (\ c, s,\ y##_proto,\ &x, NULL, 0,\ &x##_props, &x##_methods,\ NULL, NULL);\ #define web_gtkhtml2_js_define_class_with_constructor(c,s,x,y) \ x##_proto = JS_InitClass (\ c, s,\ y##_proto,\ &x, web_gtkhtml2_##x##_constructor, 0,\ &x##_props, &x##_methods,\ NULL, NULL);\ #define web_gtkhtml2_js_define_props(c,x) \ JS_DefineProperties (\ c,\ x##_proto,\ x##_props);\ JS_DefineFunctions (\ c,\ x##_proto,\ x##_methods) void web_gtkhtml2_js_init (gpointer bdata, gpointer pdata) { static jsval rval; WebGtkhtml2Data *backend_data = bdata; WebGtkhtml2PageData *page_data = pdata; WebGtkhtml2JSObjectData *obj_data; if (!backend_data->js_runtime) { backend_data->js_runtime = JS_NewRuntime (1024L*1024L); if (!backend_data->js_runtime) g_critical ("JavaScript initialisation failed\n"); } /* TODO/FIXME: Check what needs freeing here */ /* Initialise JS */ if (!page_data->js_context) { page_data->js_context = JS_NewContext ( (JSRuntime *)backend_data->js_runtime, 8192); page_data->js_global = JS_NewObject ( page_data->js_context, NULL, NULL, NULL); page_data->js_objects = g_hash_table_new_full ( NULL, NULL, NULL, (GDestroyNotify)g_free); } g_hash_table_foreach_remove (page_data->js_objects, gtk_true, NULL); /* Standard init */ JS_ClearScope ((JSContext *)page_data->js_context, (JSObject *)page_data->js_global); if (!JS_InitStandardClasses ((JSContext *)page_data->js_context, (JSObject *)page_data->js_global)) g_warning ("Failed to initialise standard JS classes"); /* Init HTML DOM classes */ /* TODO: Write some macros for this... */ web_gtkhtml2_js_define_base_class ( (JSContext *)page_data->js_context, (JSObject *)page_data->js_global, js_HTMLElement); web_gtkhtml2_js_define_props ( (JSContext *)page_data->js_context, js_HTMLElement); web_gtkhtml2_js_define_class ( (JSContext *)page_data->js_context, (JSObject *)page_data->js_global, js_HTMLDocument, js_HTMLElement); web_gtkhtml2_js_define_props ( (JSContext *)page_data->js_context, js_HTMLDocument); web_gtkhtml2_js_define_class ( (JSContext *)page_data->js_context, (JSObject *)page_data->js_global, js_HTMLHtmlElement, js_HTMLElement); web_gtkhtml2_js_define_props ( (JSContext *)page_data->js_context, js_HTMLHtmlElement); web_gtkhtml2_js_define_class ( (JSContext *)page_data->js_context, (JSObject *)page_data->js_global, js_Attr, js_HTMLElement); web_gtkhtml2_js_define_props ( (JSContext *)page_data->js_context, js_Attr); web_gtkhtml2_js_define_base_class ( (JSContext *)page_data->js_context, (JSObject *)page_data->js_global, js_Style); web_gtkhtml2_js_define_props ( (JSContext *)page_data->js_context, js_Style); /* Add document object */ page_data->js_document = JS_DefineObject ( (JSContext *)page_data->js_context, page_data->js_global, "document", &js_HTMLDocument, js_HTMLDocument_proto, JSPROP_ENUMERATE); JS_SetPrivate ((JSContext *)page_data->js_context, (JSObject *)page_data->js_document, pdata); obj_data = g_new (WebGtkhtml2JSObjectData, 1); obj_data->type = JS_DOM_NODE; obj_data->data = page_data->document->dom_document; g_hash_table_insert (page_data->js_objects, page_data->js_document, obj_data); } static void web_gtkhtml2_js_add_events (DomNode *node, DomEventListener *listener) { /* Add event listeners */ dom_EventTarget_addEventListener (DOM_EVENT_TARGET (node), "mousedown", listener, FALSE); dom_EventTarget_addEventListener (DOM_EVENT_TARGET (node), "mouseup", listener, FALSE); dom_EventTarget_addEventListener (DOM_EVENT_TARGET (node), "click", listener, FALSE); dom_EventTarget_addEventListener (DOM_EVENT_TARGET (node), "mouseover", listener, FALSE); dom_EventTarget_addEventListener (DOM_EVENT_TARGET (node), "mouseout", listener, FALSE); } static void web_gtkhtml2_js_remove_events (DomNode *node, DomEventListener *listener) { /* Remove event listeners */ dom_EventTarget_removeEventListener (DOM_EVENT_TARGET (node), "mousedown", listener, FALSE); dom_EventTarget_removeEventListener (DOM_EVENT_TARGET (node), "mouseup", listener, FALSE); dom_EventTarget_removeEventListener (DOM_EVENT_TARGET (node), "click", listener, FALSE); dom_EventTarget_removeEventListener (DOM_EVENT_TARGET (node), "mouseover", listener, FALSE); dom_EventTarget_removeEventListener (DOM_EVENT_TARGET (node), "mouseout", listener, FALSE); } void web_gtkhtml2_js_init_events (gpointer bdata, gpointer pdata) { WebGtkhtml2Data *backend_data = bdata; WebGtkhtml2PageData *page_data = pdata; DomEventListener *listener; /* FIXME: Probably shouldn't rely on this behaviour? */ listener = g_object_get_data (G_OBJECT (page_data->document), "dom-event-listener"); if (!listener) return; g_signal_connect (G_OBJECT (listener), "event", G_CALLBACK (web_gtkhtml2_js_dom_event_cb), page_data); /* Register for DOM signals */ web_gtkhtml2_dom_foreach (DOM_NODE (page_data->document->dom_document), (GFunc)web_gtkhtml2_js_add_events, listener); } void web_gtkhtml2_js_clear (gpointer bdata, gpointer pdata) { WebGtkhtml2Data *backend_data = bdata; WebGtkhtml2PageData *page_data = pdata; DomEventListener *listener; g_list_foreach (page_data->js_events, (GFunc)web_gtkhtml2_js_event_free, NULL); page_data->js_events = NULL; /* FIXME: Probably shouldn't rely on this behaviour? */ listener = g_object_get_data (G_OBJECT (page_data->document), "dom-event-listener"); if (!listener) return; g_signal_handlers_disconnect_by_func (G_OBJECT (listener), G_CALLBACK (web_gtkhtml2_js_dom_event_cb), page_data); /* Remove event listeners */ web_gtkhtml2_dom_foreach (DOM_NODE (page_data->document->dom_document), (GFunc)web_gtkhtml2_js_remove_events, listener); } typedef struct { WebGtkhtml2PageData *pdata; WebRequest *request; DomNode *node; char *script; /* Script for when it needs to be fetched */ gint script_size; } WebGtkhtml2JSData; static void web_gtkhtml2_js_src_cancel_cb (HtmlStream *stream, gpointer user_data, gpointer cancel_data) { WebGtkhtml2JSData *data = user_data; data->pdata->ext_js = g_list_remove (data->pdata->ext_js, data->request); data->pdata->stalling = FALSE; /* Parse any html that got buffered while waiting on this script */ if (data->pdata->buffer) { gint buffer_size = data->pdata->buffer_size; gchar *buffer = g_malloc (buffer_size); memcpy (buffer, data->pdata->buffer, buffer_size); g_free (data->pdata->buffer); data->pdata->buffer = NULL; data->pdata->buffer_size = 0; web_gtkhtml2_write (data->pdata->parent, data->pdata, data->pdata->page_request, buffer, buffer_size); g_free (buffer); } else if (data->pdata->stalling_close) { data->pdata->stalling_close = FALSE; web_gtkhtml2_close (data->pdata->parent, data->pdata, data->pdata->page_request); } g_free (data->script); g_free (data); } static void web_gtkhtml2_js_src_close_cb (HtmlStream *stream, gpointer user_data) { WebGtkhtml2JSData *data = user_data; /* g_debug ("Evaluating JavaScript from src...");*/ data->pdata->node = data->node; /* if (data->script) web_gtkhtml2_js_parse (data->pdata->parent, data->pdata, data->script, data->script_size);*/ web_gtkhtml2_js_src_cancel_cb (stream, user_data, NULL); } static void web_gtkhtml2_js_src_write_cb (HtmlStream *stream, const gchar *buffer, guint size, gpointer user_data) { WebGtkhtml2JSData *data = user_data; data->script = g_realloc (data->script, data->script_size + size); memcpy (data->script + data->script_size, buffer, size); data->script_size += size; } void web_gtkhtml2_js_run (WebGtkhtml2Data *backend_data, WebGtkhtml2PageData *page_data, DomNode *node) { DomNode *child; DomNamedNodeMap *map; DomNode *attr; jsval rval; const gchar *src; gboolean use_src = FALSE; gint i; child = dom_Node__get_firstChild (node); /* Check for script type */ map = dom_Node__get_attributes (node); for (i = 0, attr = dom_NamedNodeMap__get_item (map, i); i < dom_NamedNodeMap__get_length (map); i++, attr = dom_NamedNodeMap__get_item (map, i)) { const gchar *name = dom_Attr__get_name (attr); const gchar *value = dom_Attr__get_value (attr); if (strcmp ("type", name) == 0) if (!strcasestr (value, "javascript") && !strcasestr (value, "ecmascript")) return; if (strcmp ("src", name) == 0) { use_src = TRUE; src = value; } } /* Evaluate JavaScript */ page_data->node = node; if (use_src) { WebRequest *request; WebGtkhtml2JSData *data; HtmlStream *ext_js; request = g_new0 (WebRequest, 1); data = g_new0 (WebGtkhtml2JSData, 1); data->pdata = page_data; data->node = node; data->request = request; ext_js = html_stream_new ( web_gtkhtml2_js_src_write_cb, web_gtkhtml2_js_src_close_cb, data); html_stream_set_cancel_func (ext_js, web_gtkhtml2_js_src_cancel_cb, NULL); request->url = web_get_abs_url ( page_data->data->url, src); request->parent = page_data->data; request->type = WEB_DATA; request->data = ext_js; page_data->ext_js = g_list_prepend ( page_data->ext_js, request); page_data->stalling = TRUE; g_debug ("Fetching external JS (%s)", request->url); web_request_perform (request); } for (; child; child = dom_Node__get_nextSibling (child)) { if (dom_Node__get_nodeType (child) != DOM_TEXT_NODE && dom_Node__get_nodeType (child) != DOM_COMMENT_NODE) continue; /* g_debug ("Evaluating JavaScript (%d, %s)...", strlen (child->xmlnode->content), child->xmlnode->content);*/ web_gtkhtml2_js_parse (backend_data, page_data, child->xmlnode->content, strlen (child->xmlnode->content)); } return; }