gecko-dev/lib/libmocha/lm_tree.c

1396 lines
38 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
/*
* JS reflection of builtins in the current document.
*
* rjc@netscape.com, 9/21/98
*/
#include "lm.h"
#include "xp.h"
#include "layout.h"
#include "prlog.h"
#include "htrdf.h"
/* XXXbe move to layout.h */
extern uint
LO_EnumerateBuiltins(MWContext *context, int32 layer_id);
/* Global XXX #1: don't hold LO_BuiltinStruct pointers, use ids, to avoid
dangling refs when mocha thread races with doc-discard */
/* Global XXX #2: don't hold the layout lock across JS_* callouts, or if not
necesary to fondle LO_BuiltinStruct etc. (see above) */
extern PRLogModuleInfo* Moja;
#define warn PR_LOG_WARN
#define debug PR_LOG_DEBUG
enum builtins_array_slot {
BUILTINS_ARRAY_LENGTH = -1
};
enum builtin_element_array_slot {
BUILTIN_ELEMENT_ARRAY_LENGTH = -1
};
enum builtins_node_array_slot {
BUILTIN_NODE_ARRAY_LENGTH = -1,
BUILTINS_NODE_ARRAY_NAME = -2,
BUILTINS_NODE_ARRAY_URL = -3,
BUILTINS_NODE_ARRAY_SELECTED = -4,
BUILTINS_NODE_ARRAY_CONTAINER = -5,
BUILTINS_NODE_ARRAY_CONTAINER_OPEN = -6,
BUILTINS_NODE_ARRAY_SEPARATOR = -7,
BUILTINS_NODE_ARRAY_ENABLED = -8
};
enum builtins_slot {
BUILTINS_NAME = -1,
BUILTINS_DATA = -2,
BUILTINS_TARGET = -3,
BUILTINS_LENGTH = -4,
BUILTINS_SELECTEDINDEX = -5,
BUILTINS_ELEMENTS = -6,
BUILTINS_NODES = -7
};
enum builtins_element_slot {
BUILTINS_ELEMENT_NAME = -1,
BUILTINS_ELEMENT_URL = -2,
BUILTINS_ELEMENT_SELECTED = -3,
BUILTINS_ELEMENT_CONTAINER = -4,
BUILTINS_ELEMENT_CONTAINER_OPEN = -5,
BUILTINS_ELEMENT_SEPARATOR = -6,
BUILTINS_ELEMENT_ENABLED = -7
};
enum builtins_node_slot {
BUILTINS_NODE_NAME = -1,
BUILTINS_NODE_URL = -2,
BUILTINS_NODE_SELECTED = -3,
BUILTINS_NODE_CONTAINER = -4,
BUILTINS_NODE_CONTAINER_OPEN = -5,
BUILTINS_NODE_SEPARATOR = -6,
BUILTINS_NODE_ENABLED = -7
};
static JSPropertySpec builtins_array_props[] = {
{lm_length_str, BUILTINS_ARRAY_LENGTH,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{0}
};
static JSPropertySpec builtin_element_array_props[] = {
{lm_length_str, BUILTIN_ELEMENT_ARRAY_LENGTH,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{0}
};
static JSPropertySpec builtin_node_array_props[] = {
{lm_length_str, BUILTIN_NODE_ARRAY_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"name", BUILTINS_NODE_ARRAY_NAME, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"url", BUILTINS_NODE_ARRAY_URL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"selected", BUILTINS_NODE_ARRAY_SELECTED, JSPROP_ENUMERATE},
{"container", BUILTINS_NODE_ARRAY_CONTAINER, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"open", BUILTINS_NODE_ARRAY_CONTAINER_OPEN, JSPROP_ENUMERATE},
{"separator", BUILTINS_NODE_ARRAY_SEPARATOR, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"enabled", BUILTINS_NODE_ARRAY_ENABLED, JSPROP_ENUMERATE},
{0}
};
static JSPropertySpec builtins_props[] = {
{"name", BUILTINS_NAME, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"data", BUILTINS_DATA, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"target", BUILTINS_TARGET, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{lm_length_str, BUILTINS_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"selectedIndex", BUILTINS_SELECTEDINDEX, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"elements", BUILTINS_ELEMENTS, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"nodes", BUILTINS_NODES, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{0}
};
static JSPropertySpec builtin_element_props[] = {
{"name", BUILTINS_ELEMENT_NAME, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"url", BUILTINS_ELEMENT_URL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"selected", BUILTINS_ELEMENT_SELECTED, JSPROP_ENUMERATE},
{"container", BUILTINS_ELEMENT_CONTAINER, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"open", BUILTINS_ELEMENT_CONTAINER_OPEN, JSPROP_ENUMERATE},
{"separator", BUILTINS_ELEMENT_SEPARATOR, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"enabled", BUILTINS_ELEMENT_ENABLED, JSPROP_ENUMERATE},
{0}
};
static JSPropertySpec builtin_node_props[] = {
{"name", BUILTINS_NODE_NAME, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"url", BUILTINS_NODE_URL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"selected", BUILTINS_NODE_SELECTED, JSPROP_ENUMERATE},
{"container", BUILTINS_NODE_CONTAINER, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"open", BUILTINS_NODE_CONTAINER_OPEN, JSPROP_ENUMERATE},
{"separator", BUILTINS_NODE_SEPARATOR, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{"enabled", BUILTINS_NODE_ENABLED, JSPROP_ENUMERATE},
{0}
};
typedef struct JSBuiltin {
MochaDecoder *decoder;
LO_BuiltinStruct *builtin;
} JSBuiltin;
typedef struct JSElement {
MochaDecoder *decoder;
LO_BuiltinStruct *builtin;
uint32 slot;
} JSElement;
typedef struct JSNode {
MochaDecoder *decoder;
LO_BuiltinStruct *builtin;
HT_Resource node;
} JSNode;
extern JSClass lm_builtins_array_class;
extern JSClass lm_builtins_class;
extern JSClass lm_builtin_element_array_class;
extern JSClass lm_builtin_element_class;
extern JSClass lm_builtin_node_array_class;
extern JSClass lm_builtin_node_class;
JSObject *
lm_GetBuiltinsArray(MochaDecoder *decoder, JSObject *document)
{
JSContext *cx = decoder->js_context;
JSObject *obj;
JSObjectArray *array;
JSDocument *doc;
doc = JS_GetPrivate(cx, document);
if (!doc)
return NULL;
obj = doc->builtins;
if (obj)
return obj;
array = JS_malloc(cx, sizeof *array);
if (!array)
return NULL;
array->decoder = NULL; /* in case of error below */
obj = JS_NewObject(cx, &lm_builtins_array_class, NULL, document);
if (!obj || !JS_SetPrivate(cx, obj, array)) {
JS_free(cx, array);
return NULL;
}
if (!JS_DefineProperties(cx, obj, builtins_array_props))
return NULL;
array->decoder = HOLD_BACK_COUNT(decoder);
array->length = 0;
array->layer_id = doc->layer_id;
doc->builtins = obj;
return obj;
}
JSObject *
LM_ReflectBuiltin(MWContext *context, LO_BuiltinStruct *lo_builtin,
PA_Tag * tag, int32 layer_id, uint index)
{
JSObject *obj, *array_obj, *outer_obj, *document;
JSBuiltin *builtin;
MochaDecoder *decoder;
JSContext *cx;
char *name;
uint32 i;
obj = lo_builtin->mocha_object;
if (obj)
return obj;
decoder = LM_GetMochaDecoder(context);
if (!decoder)
return NULL;
cx = decoder->js_context;
/* get the name */
name = 0;
for (i = 0; i < lo_builtin->attributes.n; i++) {
if (!XP_STRCASECMP(lo_builtin->attributes.names[i], "name")) {
name = strdup(lo_builtin->attributes.values[i]);
break;
}
}
/* Get the document object that will hold this builtin */
document = lm_GetDocumentFromLayerId(decoder, layer_id);
if (!document) {
LM_PutMochaDecoder(decoder);
return NULL;
}
array_obj = lm_GetBuiltinsArray(decoder, document);
if (!array_obj) {
LM_PutMochaDecoder(decoder);
return NULL;
}
/* XXX */
outer_obj = lm_GetOuterObject(decoder);
obj = JS_NewObject(cx, &lm_builtins_class, NULL, document);
if (!obj)
goto out;
builtin = JS_malloc(cx, sizeof *builtin);
if (!builtin)
goto out;
builtin->decoder = JS_GetPrivate(cx, JS_GetGlobalObject(cx));
builtin->decoder = HOLD_BACK_COUNT(decoder);
builtin->builtin = lo_builtin;
if (!JS_SetPrivate(cx, obj, builtin))
{
obj = NULL;
goto out;
}
/* put it in the builtin array */
if (!lm_AddObjectToArray(cx, array_obj, name, index, obj)) {
obj = NULL;
goto out;
}
if (!JS_DefineProperties(cx, obj, builtins_props))
{
obj = NULL;
goto out;
}
/* put it in the document scope */
if (name && !JS_DefineProperty(cx, outer_obj, name, OBJECT_TO_JSVAL(obj),
NULL, NULL,
JSPROP_ENUMERATE | JSPROP_READONLY)) {
PR_LOG(Moja, warn, ("failed to define builtin 0x%x as %s\n",
lo_builtin, name));
/* XXX remove it altogether? */
}
/* cache it in layout data structure */
lo_builtin->mocha_object = obj;
out:
LM_PutMochaDecoder(decoder);
return obj;
}
PR_STATIC_CALLBACK(JSBool)
builtins_array_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSObjectArray *array;
MochaDecoder *decoder;
MWContext *context;
jsint count, slot;
LO_BuiltinStruct *builtin_data;
JSObject *newobj;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
array = JS_GetPrivate(cx, obj);
if (!array)
return JS_TRUE;
decoder = array->decoder;
context = decoder->window_context;
if (!context)
return JS_TRUE;
LO_LockLayout();
switch (slot) {
case BUILTINS_ARRAY_LENGTH:
count = LO_EnumerateBuiltins(context, array->layer_id);
if (count > array->length)
array->length = count;
*vp = INT_TO_JSVAL(array->length);
break;
default:
if (slot < 0) {
/* Don't mess with user-defined or method properties. */
LO_UnlockLayout();
return JS_TRUE;
}
builtin_data = LO_GetBuiltinByIndex(context, array->layer_id, (uint)slot);
if (!builtin_data) {
JS_ReportError(cx, "no builtin with index %d\n");
goto err;
}
newobj = LM_ReflectBuiltin(context, builtin_data, NULL,
array->layer_id, (uint)slot);
if (!newobj) {
JS_ReportError(cx,
"unable to reflect builtin with index %d - not loaded yet?",
(uint) slot);
goto err;
}
*vp = OBJECT_TO_JSVAL(newobj);
XP_ASSERT(slot < array->length);
break;
}
LO_UnlockLayout();
return JS_TRUE;
err:
LO_UnlockLayout();
return JS_FALSE;
}
PR_STATIC_CALLBACK(JSBool)
builtin_element_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSElement *element;
MochaDecoder *decoder;
MWContext *context;
jsint slot;
HT_Pane pane;
HT_View view;
HT_Resource node;
element = JS_GetPrivate(cx, obj);
if (!element)
return JS_TRUE;
decoder = element->decoder;
context = decoder->window_context;
if (!context)
return JS_TRUE;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
switch (slot) {
case BUILTINS_ELEMENT_SELECTED:
if (!JSVAL_IS_BOOLEAN(*vp))
return JS_TRUE;
if ((pane = element->builtin->htPane) != NULL)
{
if ((view = HT_GetNthView(pane, 0)) != NULL)
{
if ((node = HT_GetNthItem(view, element->slot)) != NULL)
{
HT_SetSelectedState(node, JSVAL_TO_BOOLEAN(*vp));
}
}
}
break;
case BUILTINS_ELEMENT_CONTAINER_OPEN:
if (!JSVAL_IS_BOOLEAN(*vp))
return JS_TRUE;
if ((pane = element->builtin->htPane) != NULL)
{
if ((view = HT_GetNthView(pane, 0)) != NULL)
{
if ((node = HT_GetNthItem(view, element->slot)) != NULL)
{
HT_SetOpenState(node, JSVAL_TO_BOOLEAN(*vp));
}
}
}
break;
case BUILTINS_ELEMENT_ENABLED:
if (!JSVAL_IS_BOOLEAN(*vp))
return JS_TRUE;
if ((pane = element->builtin->htPane) != NULL)
{
if ((view = HT_GetNthView(pane, 0)) != NULL)
{
if ((node = HT_GetNthItem(view, element->slot)) != NULL)
{
HT_SetEnabledState(node, JSVAL_TO_BOOLEAN(*vp));
}
}
}
break;
default:;
}
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
builtin_element_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSElement *element;
MochaDecoder *decoder;
MWContext *context;
JSString *jstr;
jsint slot;
HT_Pane pane;
HT_View view;
HT_Resource node;
char *name;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
element = JS_GetPrivate(cx, obj);
if (!element)
return JS_TRUE;
decoder = element->decoder;
context = decoder->window_context;
if (!context)
return JS_TRUE;
LO_LockLayout();
switch (slot) {
case BUILTINS_ELEMENT_NAME:
if ((pane = element->builtin->htPane) != NULL)
{
if ((view = HT_GetNthView(pane, 0)) != NULL)
{
if ((node = HT_GetNthItem(view, element->slot)) != NULL)
{
if ((name = HT_GetNodeName(node)) != NULL)
{
if ((jstr = lm_LocalEncodingToStr(context, name)) != NULL)
{
*vp = STRING_TO_JSVAL(jstr);
}
}
}
}
}
break;
case BUILTINS_ELEMENT_URL:
if ((pane = element->builtin->htPane) != NULL)
{
if ((view = HT_GetNthView(pane, 0)) != NULL)
{
if ((node = HT_GetNthItem(view, element->slot)) != NULL)
{
if ((name = HT_GetNodeURL(node)) != NULL)
{
if ((jstr = lm_LocalEncodingToStr(context, name)) != NULL)
{
*vp = STRING_TO_JSVAL(jstr);
}
}
}
}
}
break;
case BUILTINS_ELEMENT_SELECTED:
if ((pane = element->builtin->htPane) != NULL)
{
if ((view = HT_GetNthView(pane, 0)) != NULL)
{
if ((node = HT_GetNthItem(view, element->slot)) != NULL)
{
*vp = BOOLEAN_TO_JSVAL(HT_IsSelected(node));
}
}
}
break;
case BUILTINS_ELEMENT_CONTAINER:
if ((pane = element->builtin->htPane) != NULL)
{
if ((view = HT_GetNthView(pane, 0)) != NULL)
{
if ((node = HT_GetNthItem(view, element->slot)) != NULL)
{
*vp = BOOLEAN_TO_JSVAL(HT_IsContainer(node));
}
}
}
break;
case BUILTINS_ELEMENT_CONTAINER_OPEN:
if ((pane = element->builtin->htPane) != NULL)
{
if ((view = HT_GetNthView(pane, 0)) != NULL)
{
if ((node = HT_GetNthItem(view, element->slot)) != NULL)
{
*vp = BOOLEAN_TO_JSVAL(HT_IsContainerOpen(node));
}
}
}
break;
case BUILTINS_ELEMENT_SEPARATOR:
if ((pane = element->builtin->htPane) != NULL)
{
if ((view = HT_GetNthView(pane, 0)) != NULL)
{
if ((node = HT_GetNthItem(view, element->slot)) != NULL)
{
*vp = BOOLEAN_TO_JSVAL(HT_IsSeparator(node));
}
}
}
break;
case BUILTINS_ELEMENT_ENABLED:
if ((pane = element->builtin->htPane) != NULL)
{
if ((view = HT_GetNthView(pane, 0)) != NULL)
{
if ((node = HT_GetNthItem(view, element->slot)) != NULL)
{
*vp = BOOLEAN_TO_JSVAL(HT_IsEnabled(node));
}
}
}
break;
default:;
}
LO_UnlockLayout();
return JS_TRUE;
}
PR_STATIC_CALLBACK(void)
builtin_element_finalize(JSContext *cx, JSObject *obj)
{
JSObjectArray *array;
array = JS_GetPrivate(cx, obj);
if (!array)
return;
DROP_BACK_COUNT(array->decoder);
JS_free(cx, array);
}
JSClass lm_builtin_element_class = {
"TreeElement", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
builtin_element_getProperty, builtin_element_setProperty, JS_EnumerateStub,
JS_ResolveStub, JS_ConvertStub, builtin_element_finalize
};
PR_STATIC_CALLBACK(JSBool)
builtin_element_array_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSBuiltin *builtin;
JSElement *element;
JSObject *element_obj;
MochaDecoder *decoder;
MWContext *context;
jsint slot;
int theIndex;
HT_Pane pane;
HT_View view;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
builtin = JS_GetPrivate(cx, obj);
if (!builtin)
return JS_TRUE;
decoder = builtin->decoder;
context = decoder->window_context;
if (!context)
return JS_TRUE;
LO_LockLayout();
switch (slot) {
case BUILTIN_ELEMENT_ARRAY_LENGTH:
theIndex = 0;
if ((pane = builtin->builtin->htPane) != NULL)
{
if ((view = HT_GetNthView(pane, 0)) != NULL)
{
theIndex = HT_GetItemListCount(view);
}
}
*vp = INT_TO_JSVAL(theIndex);
break;
default:
if (slot < 0) {
/* Don't mess with user-defined or method properties. */
LO_UnlockLayout();
return JS_TRUE;
}
element = JS_malloc(cx, sizeof *element);
if (!element)
goto err;
element_obj = JS_NewObject(cx, &lm_builtin_element_class,
decoder->builtin_element_prototype, obj);
if (!element_obj || !JS_SetPrivate(cx, element_obj, element))
{
JS_free(cx, element);
goto err;
}
if (!JS_DefineProperties(cx, obj, builtin_element_props))
{
JS_free(cx, element);
goto err;
}
element->decoder = HOLD_BACK_COUNT(decoder);
element->builtin = builtin->builtin;
element->slot = slot;
*vp = OBJECT_TO_JSVAL(element_obj);
break;
}
LO_UnlockLayout();
return JS_TRUE;
err:
LO_UnlockLayout();
return JS_FALSE;
}
PR_STATIC_CALLBACK(JSBool)
builtin_node_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSNode *jsNode;
MochaDecoder *decoder;
MWContext *context;
jsint slot;
HT_Resource node;
jsNode = JS_GetPrivate(cx, obj);
if (!jsNode)
return JS_TRUE;
decoder = jsNode->decoder;
context = decoder->window_context;
if (!context) return JS_TRUE;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
switch (slot) {
case BUILTINS_NODE_SELECTED:
if (!JSVAL_IS_BOOLEAN(*vp))
return JS_TRUE;
if ((node = jsNode->node) != NULL)
{
HT_SetSelectedState(node, JSVAL_TO_BOOLEAN(*vp));
}
break;
case BUILTINS_NODE_CONTAINER_OPEN:
if (!JSVAL_IS_BOOLEAN(*vp))
return JS_TRUE;
if ((node = jsNode->node) != NULL)
{
HT_SetOpenState(node, JSVAL_TO_BOOLEAN(*vp));
}
break;
case BUILTINS_NODE_ENABLED:
if (!JSVAL_IS_BOOLEAN(*vp))
return JS_TRUE;
if ((node = jsNode->node) != NULL)
{
HT_SetEnabledState(node, JSVAL_TO_BOOLEAN(*vp));
}
break;
default:;
}
return(JS_TRUE);
}
PR_STATIC_CALLBACK(JSBool)
builtin_node_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSNode *jsNode;
MochaDecoder *decoder;
MWContext *context;
JSString *jstr;
jsint slot;
HT_Resource node;
char *name;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
jsNode = JS_GetPrivate(cx, obj);
if (!jsNode)
return JS_TRUE;
decoder = jsNode->decoder;
context = decoder->window_context;
if (!context)
return JS_TRUE;
LO_LockLayout();
switch (slot) {
case BUILTINS_NODE_NAME:
if ((node = jsNode->node) != NULL)
{
if ((name = HT_GetNodeName(node)) != NULL)
{
if ((jstr = lm_LocalEncodingToStr(context, name)) != NULL)
{
*vp = STRING_TO_JSVAL(jstr);
}
}
}
break;
case BUILTINS_NODE_URL:
if ((node = jsNode->node) != NULL)
{
if ((name = HT_GetNodeURL(node)) != NULL)
{
if ((jstr = lm_LocalEncodingToStr(context, name)) != NULL)
{
*vp = STRING_TO_JSVAL(jstr);
}
}
}
break;
case BUILTINS_NODE_SELECTED:
if ((node = jsNode->node) != NULL)
{
*vp = BOOLEAN_TO_JSVAL(HT_IsSelected(node));
}
break;
case BUILTINS_NODE_CONTAINER:
if ((node = jsNode->node) != NULL)
{
*vp = BOOLEAN_TO_JSVAL(HT_IsContainer(node));
}
break;
case BUILTINS_NODE_CONTAINER_OPEN:
if ((node = jsNode->node) != NULL)
{
*vp = BOOLEAN_TO_JSVAL(HT_IsContainerOpen(node));
}
break;
case BUILTINS_NODE_SEPARATOR:
if ((node = jsNode->node) != NULL)
{
*vp = BOOLEAN_TO_JSVAL(HT_IsSeparator(node));
}
break;
case BUILTINS_NODE_ENABLED:
if ((node = jsNode->node) != NULL)
{
*vp = BOOLEAN_TO_JSVAL(HT_IsEnabled(node));
}
break;
default:;
}
LO_UnlockLayout();
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
builtin_node_children(JSContext *cx, JSObject *obj, uint argc, jsval *argv,
jsval *rval)
{
JSNode *jsNode, *newJSNode;
JSObject *jsnode_obj;
MochaDecoder *decoder;
HT_Resource node;
if (!JS_InstanceOf(cx, obj, &lm_builtin_node_class, argv))
return JS_FALSE;
jsNode = JS_GetPrivate(cx, obj);
if (!jsNode)
return JS_TRUE;
decoder = jsNode->decoder;
if ((node = jsNode->node) == NULL)
return JS_TRUE; /* XXXbe no silent error */
if (!HT_IsContainer(node))
*rval = JSVAL_NULL;
else if (!HT_IsContainerOpen(node))
*rval = JSVAL_NULL;
else
{
newJSNode = JS_malloc(cx, sizeof *newJSNode);
if (!newJSNode)
return JS_FALSE;
jsnode_obj = JS_NewObject(cx, &lm_builtin_node_array_class,
decoder->builtin_node_prototype, obj);
if (!jsnode_obj || !JS_SetPrivate(cx, jsnode_obj, newJSNode))
{
JS_free(cx, newJSNode);
return JS_FALSE;
}
if (!JS_DefineProperties(cx, jsnode_obj, builtin_node_array_props))
{
JS_free(cx, newJSNode);
return JS_FALSE;
}
newJSNode->decoder = HOLD_BACK_COUNT(decoder);
newJSNode->builtin = jsNode->builtin;
newJSNode->node = node;
*rval = OBJECT_TO_JSVAL(jsnode_obj);
}
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
builtin_node_parent(JSContext *cx, JSObject *obj, uint argc, jsval *argv,
jsval *rval)
{
JSNode *jsNode, *newJSNode;
JSObject *jsnode_obj;
MochaDecoder *decoder;
HT_Resource node;
if (!JS_InstanceOf(cx, obj, &lm_builtin_node_class, argv))
return JS_FALSE;
jsNode = JS_GetPrivate(cx, obj);
if (!jsNode)
return JS_TRUE;
decoder = jsNode->decoder;
if ((node = jsNode->node) == NULL)
return JS_TRUE; /* XXXbe no silent error */
newJSNode = JS_malloc(cx, sizeof *newJSNode);
if (!newJSNode)
return(JS_FALSE);
jsnode_obj = JS_NewObject(cx, &lm_builtin_node_array_class,
decoder->builtin_node_prototype, obj);
if (!jsnode_obj || !JS_SetPrivate(cx, jsnode_obj, newJSNode))
{
JS_free(cx, newJSNode);
return JS_FALSE;
}
if (!JS_DefineProperties(cx, jsnode_obj, builtin_node_array_props))
{
JS_free(cx, newJSNode);
return JS_FALSE;
}
newJSNode->decoder = HOLD_BACK_COUNT(decoder);
newJSNode->builtin = jsNode->builtin;
newJSNode->node = HT_GetParent(node);
*rval = OBJECT_TO_JSVAL(jsnode_obj);
return(JS_TRUE);
}
PR_STATIC_CALLBACK(JSBool)
builtin_element_parentIndex(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
JSElement *element;
MochaDecoder *decoder;
HT_Pane pane;
HT_View view;
HT_Resource node, parent;
int32 parentIndex = -1;
if (!JS_InstanceOf(cx, obj, &lm_builtin_element_class, argv))
return JS_FALSE;
if (!(element = JS_GetPrivate(cx, obj)))
return JS_TRUE;
decoder = element->decoder;
if ((pane = element->builtin->htPane) != NULL)
{
if ((view = HT_GetNthView(pane, 0)) != NULL)
{
if ((node = HT_GetNthItem(view, element->slot)) != NULL)
{
if ((parent = HT_GetParent(node)) != NULL)
{
if (parent != HT_TopNode(view))
{
parentIndex = HT_GetNodeIndex(view, parent);
}
}
}
}
}
*rval = INT_TO_JSVAL(parentIndex);
return JS_TRUE;
}
static JSFunctionSpec builtin_element_methods[] = {
{"parentIndex", builtin_element_parentIndex, 0},
{0}
};
static JSFunctionSpec builtin_node_methods[] = {
{"children", builtin_node_children, 0},
{"parent", builtin_node_parent, 0},
{0}
};
PR_STATIC_CALLBACK(void)
builtin_node_finalize(JSContext *cx, JSObject *obj)
{
JSObjectArray *array;
array = JS_GetPrivate(cx, obj);
if (!array)
return;
DROP_BACK_COUNT(array->decoder);
JS_free(cx, array);
}
JSClass lm_builtin_node_class = {
"TreeNode", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
builtin_node_getProperty, builtin_node_setProperty, JS_EnumerateStub,
JS_ResolveStub, JS_ConvertStub, builtin_node_finalize
};
PR_STATIC_CALLBACK(JSBool)
builtin_node_array_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSNode *jsNode, *newJSNode;
JSObject *jsnode_obj;
MochaDecoder *decoder;
MWContext *context;
jsint slot;
JSString *jstr;
char *name;
uint32 theCount;
HT_Resource node;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
jsNode = JS_GetPrivate(cx, obj);
if (!jsNode)
return JS_TRUE;
decoder = jsNode->decoder;
context = decoder->window_context;
if (!context)
return JS_TRUE;
LO_LockLayout();
switch (slot) {
case BUILTIN_NODE_ARRAY_LENGTH:
if ((node = jsNode->node) != NULL)
{
theCount = HT_GetCountDirectChildren(jsNode->node);
*vp = INT_TO_JSVAL(theCount);
}
break;
case BUILTINS_NODE_ARRAY_NAME:
if ((node = jsNode->node) != NULL)
{
if ((name = HT_GetNodeName(node)) != NULL)
{
if ((jstr = lm_LocalEncodingToStr(context, name)) != NULL)
{
*vp = STRING_TO_JSVAL(jstr);
}
}
}
break;
case BUILTINS_NODE_ARRAY_URL:
if ((node = jsNode->node) != NULL)
{
if ((name = HT_GetNodeURL(node)) != NULL)
{
if ((jstr = lm_LocalEncodingToStr(context, name)) != NULL)
{
*vp = STRING_TO_JSVAL(jstr);
}
}
}
break;
case BUILTINS_NODE_ARRAY_SELECTED:
if ((node = jsNode->node) != NULL)
{
*vp = BOOLEAN_TO_JSVAL(HT_IsSelected(node));
}
break;
case BUILTINS_NODE_ARRAY_CONTAINER:
if ((node = jsNode->node) != NULL)
{
*vp = BOOLEAN_TO_JSVAL(HT_IsContainer(node));
}
break;
case BUILTINS_NODE_ARRAY_CONTAINER_OPEN:
if ((node = jsNode->node) != NULL)
{
*vp = BOOLEAN_TO_JSVAL(HT_IsContainerOpen(node));
}
break;
case BUILTINS_NODE_ARRAY_SEPARATOR:
if ((node = jsNode->node) != NULL)
{
*vp = BOOLEAN_TO_JSVAL(HT_IsSeparator(node));
}
break;
case BUILTINS_NODE_ARRAY_ENABLED:
if ((node = jsNode->node) != NULL)
{
*vp = BOOLEAN_TO_JSVAL(HT_IsEnabled(node));
}
break;
default:
if (slot < 0) {
/* Don't mess with user-defined or method properties. */
LO_UnlockLayout();
return JS_TRUE;
}
newJSNode = JS_malloc(cx, sizeof *newJSNode);
if (!newJSNode)
goto err;
jsnode_obj = JS_NewObject(cx, &lm_builtin_node_class,
decoder->builtin_node_prototype, obj);
if (!jsnode_obj || !JS_SetPrivate(cx, jsnode_obj, newJSNode))
{
JS_free(cx, newJSNode);
goto err;
}
if (!JS_DefineProperties(cx, obj, builtin_node_props))
{
JS_free(cx, newJSNode);
goto err;
}
newJSNode->decoder = HOLD_BACK_COUNT(decoder);
newJSNode->builtin = jsNode->builtin;
newJSNode->node = HT_GetContainerItem(jsNode->node, slot);
*vp = OBJECT_TO_JSVAL(jsnode_obj);
break;
}
LO_UnlockLayout();
return JS_TRUE;
err:
LO_UnlockLayout();
return JS_FALSE;
}
PR_STATIC_CALLBACK(void)
builtin_element_array_finalize(JSContext *cx, JSObject *obj)
{
JSObjectArray *array;
array = JS_GetPrivate(cx, obj);
if (!array)
return;
DROP_BACK_COUNT(array->decoder);
JS_free(cx, array);
}
PR_STATIC_CALLBACK(void)
builtin_node_array_finalize(JSContext *cx, JSObject *obj)
{
JSObjectArray *array;
array = JS_GetPrivate(cx, obj);
if (!array)
return;
DROP_BACK_COUNT(array->decoder);
JS_free(cx, array);
}
JSClass lm_builtin_element_array_class = {
"TreeElementArray", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
builtin_element_array_getProperty, builtin_element_array_getProperty, JS_EnumerateStub,
JS_ResolveStub, JS_ConvertStub, builtin_element_array_finalize
};
JSClass lm_builtin_node_array_class = {
"TreeNodeArray", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
builtin_node_array_getProperty, builtin_node_array_getProperty, JS_EnumerateStub,
JS_ResolveStub, JS_ConvertStub, builtin_node_array_finalize
};
PR_STATIC_CALLBACK(JSBool)
builtin_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSBuiltin *builtin, *element;
JSNode *jsNode;
JSObject *element_obj, *jsNode_obj;
MochaDecoder *decoder;
MWContext *context;
jsint slot;
JSString *jstr;
uint32 i;
int theIndex;
HT_Pane pane;
HT_View view;
HT_Resource node = NULL;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
builtin = JS_GetPrivate(cx, obj);
if (!builtin)
return JS_TRUE;
decoder = builtin->decoder;
context = decoder->window_context;
if (!context)
return JS_TRUE;
LO_LockLayout();
switch (slot) {
case BUILTINS_NAME:
for (i = 0; i < builtin->builtin->attributes.n; i++)
{
if (!XP_STRCASECMP(builtin->builtin->attributes.names[i], "name"))
{
if ((jstr = lm_LocalEncodingToStr(context,
builtin->builtin->attributes.values[i])) != NULL)
{
*vp = STRING_TO_JSVAL(jstr);
}
break;
}
}
break;
case BUILTINS_DATA:
for (i = 0; i < builtin->builtin->attributes.n; i++)
{
if (!XP_STRCASECMP(builtin->builtin->attributes.names[i], "data"))
{
if ((jstr = JS_NewStringCopyZ(cx,
builtin->builtin->attributes.values[i])) != NULL)
{
*vp = STRING_TO_JSVAL(jstr);
}
break;
}
}
break;
case BUILTINS_TARGET:
for (i = 0; i < builtin->builtin->attributes.n; i++)
{
if (!XP_STRCASECMP(builtin->builtin->attributes.names[i], "target"))
{
if ((jstr = JS_NewStringCopyZ(cx,
builtin->builtin->attributes.values[i])) != NULL)
{
*vp = STRING_TO_JSVAL(jstr);
}
break;
}
}
break;
case BUILTINS_LENGTH:
theIndex = 0;
if ((pane = builtin->builtin->htPane) != NULL)
{
if ((view = HT_GetNthView(pane, 0)) != NULL)
{
theIndex = HT_GetItemListCount(view);
}
}
*vp = INT_TO_JSVAL(theIndex);
break;
case BUILTINS_SELECTEDINDEX:
theIndex = -1;
if ((pane = builtin->builtin->htPane) != NULL)
{
if ((view = HT_GetNthView(pane, 0)) != NULL)
{
node = NULL;
if ((node = HT_GetNextSelection(view, node)) != NULL)
{
theIndex = HT_GetNodeIndex(view, node);
}
}
}
*vp = INT_TO_JSVAL(theIndex);
break;
case BUILTINS_ELEMENTS:
element = JS_malloc(cx, sizeof *element);
if (!element)
goto err;
element_obj = JS_NewObject(cx, &lm_builtin_element_array_class, decoder->builtin_prototype, obj);
if (!element_obj || !JS_SetPrivate(cx, element_obj, element))
{
JS_free(cx, element);
goto err;
}
if (!JS_DefineProperties(cx, element_obj, builtin_element_array_props))
{
JS_free(cx, element);
goto err;
}
element->decoder = HOLD_BACK_COUNT(decoder);
element->builtin = builtin->builtin;
*vp = OBJECT_TO_JSVAL(element_obj);
break;
case BUILTINS_NODES:
jsNode = JS_malloc(cx, sizeof *jsNode);
if (!jsNode)
goto err;
jsNode_obj = JS_NewObject(cx, &lm_builtin_node_array_class, decoder->builtin_node_prototype, obj);
if (!jsNode_obj || !JS_SetPrivate(cx, jsNode_obj, jsNode))
{
JS_free(cx, jsNode);
goto err;
}
if (!JS_DefineProperties(cx, jsNode_obj, builtin_node_array_props))
{
JS_free(cx, jsNode);
goto err;
}
jsNode->decoder = HOLD_BACK_COUNT(decoder);
jsNode->builtin = builtin->builtin;
jsNode->node = HT_TopNode(HT_GetNthView(builtin->builtin->htPane, 0));
*vp = OBJECT_TO_JSVAL(jsNode_obj);
break;
default:;
}
LO_UnlockLayout();
return JS_TRUE;
err:
LO_UnlockLayout();
return JS_FALSE;
}
PR_STATIC_CALLBACK(void)
builtins_array_finalize(JSContext *cx, JSObject *obj)
{
JSObjectArray *array;
array = JS_GetPrivate(cx, obj);
if (!array)
return;
DROP_BACK_COUNT(array->decoder);
JS_free(cx, array);
}
PR_STATIC_CALLBACK(void)
builtin_finalize(JSContext *cx, JSObject *obj)
{
JSObjectArray *array;
array = JS_GetPrivate(cx, obj);
if (!array)
return;
DROP_BACK_COUNT(array->decoder);
JS_free(cx, array);
}
JSClass lm_builtins_array_class = {
"TreesArray", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
builtins_array_getProperty, builtins_array_getProperty, JS_EnumerateStub,
JS_ResolveStub, JS_ConvertStub, builtins_array_finalize
};
JSClass lm_builtins_class = {
"Trees", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
builtin_getProperty, builtin_getProperty, JS_EnumerateStub,
JS_ResolveStub, JS_ConvertStub, builtin_finalize
};
PR_STATIC_CALLBACK(JSBool)
Builtin(JSContext *cx, JSObject *obj, uint argc, jsval *argv, jsval *rval)
{
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
BuiltinElement(JSContext *cx, JSObject *obj, uint argc, jsval *argv,
jsval *rval)
{
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
BuiltinNode(JSContext *cx, JSObject *obj, uint argc, jsval *argv, jsval *rval)
{
return JS_TRUE;
}
JSBool
lm_InitBuiltinClass(MochaDecoder *decoder)
{
JSContext *cx;
JSObject *prototype;
cx = decoder->js_context;
prototype = JS_InitClass(cx, decoder->window_object,
decoder->event_receiver_prototype, &lm_builtins_class,
Builtin, 0, builtins_props, NULL, NULL, NULL);
if (!prototype)
return JS_FALSE;
decoder->builtin_prototype = prototype;
prototype = JS_InitClass(cx, decoder->window_object,
decoder->event_receiver_prototype, &lm_builtin_element_class,
BuiltinElement, 0, builtin_element_props, builtin_element_methods, NULL, NULL);
if (!prototype)
return JS_FALSE;
decoder->builtin_element_prototype = prototype;
prototype = JS_InitClass(cx, decoder->window_object,
decoder->event_receiver_prototype, &lm_builtin_node_class,
BuiltinNode, 0, builtin_node_props, builtin_node_methods, NULL, NULL);
if (!prototype)
return JS_FALSE;
decoder->builtin_node_prototype = prototype;
return JS_TRUE;
}