Initial experiment in adding js support to w3m.
This commit is contained in:
292
js/w3m_dom.c
Normal file
292
js/w3m_dom.c
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* w3m DOM Implementation
|
||||
*
|
||||
* Basic DOM implementation for JavaScript integration.
|
||||
* This provides minimal DOM functionality for Phase 1.
|
||||
*/
|
||||
|
||||
#include "fm.h"
|
||||
|
||||
#ifdef USE_JAVASCRIPT
|
||||
|
||||
#include "w3m_dom.h"
|
||||
#include "w3m_javascript.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* DOM Document Management */
|
||||
|
||||
W3MDocument *
|
||||
w3m_dom_create_document(Buffer *buf)
|
||||
{
|
||||
if (!buf) return NULL;
|
||||
|
||||
W3MDocument *doc = GC_MALLOC(sizeof(W3MDocument));
|
||||
if (!doc) return NULL;
|
||||
|
||||
doc->buffer = buf;
|
||||
doc->documentElement = NULL;
|
||||
doc->body = NULL;
|
||||
doc->head = NULL;
|
||||
|
||||
/* Set document properties */
|
||||
Str url_str = parsedURL2Str(&buf->currentURL);
|
||||
if (url_str && url_str->ptr) {
|
||||
int len = strlen(url_str->ptr);
|
||||
doc->URL = GC_MALLOC(len + 1);
|
||||
if (doc->URL) {
|
||||
strcpy(doc->URL, url_str->ptr);
|
||||
}
|
||||
} else {
|
||||
doc->URL = NULL;
|
||||
}
|
||||
|
||||
doc->title = NULL;
|
||||
doc->domain = NULL;
|
||||
|
||||
/* Initialize element collections */
|
||||
doc->all_elements = NULL;
|
||||
doc->element_count = 0;
|
||||
doc->element_capacity = 0;
|
||||
|
||||
doc->js_document = JS_NULL;
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
void
|
||||
w3m_dom_destroy_document(W3MDocument *doc)
|
||||
{
|
||||
if (!doc) return;
|
||||
|
||||
/* Free document properties */
|
||||
if (doc->URL) GC_free(doc->URL);
|
||||
if (doc->title) GC_free(doc->title);
|
||||
if (doc->domain) GC_free(doc->domain);
|
||||
|
||||
/* Free element collections */
|
||||
if (doc->all_elements) {
|
||||
for (int i = 0; i < doc->element_count; i++) {
|
||||
w3m_dom_destroy_element(doc->all_elements[i]);
|
||||
}
|
||||
GC_free(doc->all_elements);
|
||||
}
|
||||
|
||||
/* Free JavaScript object reference */
|
||||
if (doc->buffer && doc->buffer->js_state) {
|
||||
/* JavaScript object cleanup will be handled elsewhere */
|
||||
/* For Phase 1, we'll keep this simple */
|
||||
}
|
||||
|
||||
GC_free(doc);
|
||||
}
|
||||
|
||||
/* Element Management */
|
||||
|
||||
W3MElement *
|
||||
w3m_dom_create_element(const char *tagName)
|
||||
{
|
||||
if (!tagName) return NULL;
|
||||
|
||||
W3MElement *elem = GC_MALLOC(sizeof(W3MElement));
|
||||
if (!elem) return NULL;
|
||||
|
||||
/* Copy tag name to uppercase */
|
||||
int len = strlen(tagName);
|
||||
elem->tagName = GC_MALLOC(len + 1);
|
||||
if (elem->tagName) {
|
||||
for (int i = 0; i <= len; i++) {
|
||||
elem->tagName[i] = (tagName[i] >= 'a' && tagName[i] <= 'z') ?
|
||||
tagName[i] - 'a' + 'A' : tagName[i];
|
||||
}
|
||||
}
|
||||
|
||||
elem->id = NULL;
|
||||
elem->className = NULL;
|
||||
|
||||
/* Initialize tree structure */
|
||||
elem->parent = NULL;
|
||||
elem->firstChild = NULL;
|
||||
elem->lastChild = NULL;
|
||||
elem->nextSibling = NULL;
|
||||
elem->previousSibling = NULL;
|
||||
|
||||
/* Initialize w3m mappings */
|
||||
elem->line = NULL;
|
||||
elem->line_pos = 0;
|
||||
elem->anchor = NULL;
|
||||
elem->form_item = NULL;
|
||||
|
||||
/* Initialize attributes */
|
||||
elem->attributes.names = NULL;
|
||||
elem->attributes.values = NULL;
|
||||
elem->attributes.count = 0;
|
||||
elem->attributes.capacity = 0;
|
||||
|
||||
elem->textContent = NULL;
|
||||
elem->innerHTML = NULL;
|
||||
|
||||
/* Initialize JavaScript object */
|
||||
elem->js_object = JS_NULL;
|
||||
elem->js_object_valid = 0;
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
void
|
||||
w3m_dom_destroy_element(W3MElement *elem)
|
||||
{
|
||||
if (!elem) return;
|
||||
|
||||
/* Free string properties */
|
||||
if (elem->tagName) GC_free(elem->tagName);
|
||||
if (elem->id) GC_free(elem->id);
|
||||
if (elem->className) GC_free(elem->className);
|
||||
if (elem->textContent) GC_free(elem->textContent);
|
||||
if (elem->innerHTML) GC_free(elem->innerHTML);
|
||||
|
||||
/* Free attributes */
|
||||
if (elem->attributes.names) {
|
||||
for (int i = 0; i < elem->attributes.count; i++) {
|
||||
if (elem->attributes.names[i]) GC_free(elem->attributes.names[i]);
|
||||
if (elem->attributes.values[i]) GC_free(elem->attributes.values[i]);
|
||||
}
|
||||
GC_free(elem->attributes.names);
|
||||
GC_free(elem->attributes.values);
|
||||
}
|
||||
|
||||
GC_free(elem);
|
||||
}
|
||||
|
||||
/* Attribute Management */
|
||||
|
||||
const char *
|
||||
w3m_dom_get_attribute(W3MElement *elem, const char *name)
|
||||
{
|
||||
if (!elem || !name) return NULL;
|
||||
|
||||
for (int i = 0; i < elem->attributes.count; i++) {
|
||||
if (elem->attributes.names[i] &&
|
||||
strcasecmp(elem->attributes.names[i], name) == 0) {
|
||||
return elem->attributes.values[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
w3m_dom_set_attribute(W3MElement *elem, const char *name, const char *value)
|
||||
{
|
||||
if (!elem || !name) return;
|
||||
|
||||
/* Check if attribute already exists */
|
||||
for (int i = 0; i < elem->attributes.count; i++) {
|
||||
if (elem->attributes.names[i] &&
|
||||
strcasecmp(elem->attributes.names[i], name) == 0) {
|
||||
/* Update existing attribute */
|
||||
if (elem->attributes.values[i]) GC_free(elem->attributes.values[i]);
|
||||
if (value) {
|
||||
int len = strlen(value);
|
||||
elem->attributes.values[i] = GC_MALLOC(len + 1);
|
||||
if (elem->attributes.values[i]) {
|
||||
strcpy(elem->attributes.values[i], value);
|
||||
}
|
||||
} else {
|
||||
elem->attributes.values[i] = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add new attribute */
|
||||
if (elem->attributes.count >= elem->attributes.capacity) {
|
||||
/* Expand arrays */
|
||||
int new_capacity = elem->attributes.capacity + 10;
|
||||
char **new_names = GC_MALLOC(sizeof(char*) * new_capacity);
|
||||
char **new_values = GC_MALLOC(sizeof(char*) * new_capacity);
|
||||
|
||||
if (!new_names || !new_values) return;
|
||||
|
||||
if (elem->attributes.names) {
|
||||
memcpy(new_names, elem->attributes.names,
|
||||
sizeof(char*) * elem->attributes.count);
|
||||
memcpy(new_values, elem->attributes.values,
|
||||
sizeof(char*) * elem->attributes.count);
|
||||
GC_free(elem->attributes.names);
|
||||
GC_free(elem->attributes.values);
|
||||
}
|
||||
|
||||
elem->attributes.names = new_names;
|
||||
elem->attributes.values = new_values;
|
||||
elem->attributes.capacity = new_capacity;
|
||||
}
|
||||
|
||||
/* Copy name */
|
||||
int name_len = strlen(name);
|
||||
elem->attributes.names[elem->attributes.count] = GC_MALLOC(name_len + 1);
|
||||
if (elem->attributes.names[elem->attributes.count]) {
|
||||
strcpy(elem->attributes.names[elem->attributes.count], name);
|
||||
}
|
||||
|
||||
/* Copy value */
|
||||
if (value) {
|
||||
int value_len = strlen(value);
|
||||
elem->attributes.values[elem->attributes.count] = GC_MALLOC(value_len + 1);
|
||||
if (elem->attributes.values[elem->attributes.count]) {
|
||||
strcpy(elem->attributes.values[elem->attributes.count], value);
|
||||
}
|
||||
} else {
|
||||
elem->attributes.values[elem->attributes.count] = NULL;
|
||||
}
|
||||
|
||||
elem->attributes.count++;
|
||||
}
|
||||
|
||||
/* JavaScript Binding (Stubs for Phase 1) */
|
||||
|
||||
void
|
||||
w3m_dom_bind_to_js(W3MJSContext *ctx, W3MDocument *doc)
|
||||
{
|
||||
if (!ctx || !doc) return;
|
||||
|
||||
/* Create basic document object */
|
||||
JSValue document = JS_NewObject(ctx->context);
|
||||
|
||||
/* Set basic properties */
|
||||
if (doc->URL) {
|
||||
JS_SetPropertyStr(ctx->context, document, "URL",
|
||||
JS_NewString(ctx->context, doc->URL));
|
||||
}
|
||||
|
||||
/* Bind to global object */
|
||||
JS_SetPropertyStr(ctx->context, ctx->global_obj, "document", document);
|
||||
ctx->document_obj = document;
|
||||
|
||||
doc->js_document = document;
|
||||
}
|
||||
|
||||
/* JavaScript API Stubs */
|
||||
|
||||
JSValue
|
||||
js_getElementById(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
/* Phase 1: Return null for now */
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
JSValue
|
||||
js_getElementsByTagName(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
/* Phase 1: Return empty array */
|
||||
return JS_NewArray(ctx);
|
||||
}
|
||||
|
||||
JSValue
|
||||
js_createElement(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
|
||||
{
|
||||
/* Phase 1: Return empty object */
|
||||
return JS_NewObject(ctx);
|
||||
}
|
||||
|
||||
#endif /* USE_JAVASCRIPT */
|
Reference in New Issue
Block a user