/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * 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 Navigator Components. * * Created: Tom Pixley, 4/22/97 * */ #include "lm.h" #include "prmem.h" #include "np.h" #include "net.h" #include "fe_proto.h" /* * ----------------------------------------------------------------------- * * Data types * * ----------------------------------------------------------------------- */ typedef struct JSComponent { MochaDecoder *decoder; JSObject *obj; JSString *name; ETBoolPtrFunc active_callback; ETVoidPtrFunc startup_callback; } JSComponent; typedef struct JSComponentArray { MochaDecoder *decoder; JSObject *obj; jsint length; } JSComponentArray; typedef struct JSPreDefComponent { const char *name; ETVerifyComponentFunc func; } JSPreDefComponent; static JSPreDefComponent predef_components[] = { {0} }; /* * ----------------------------------------------------------------------- * * Reflection of an installed component. * * ----------------------------------------------------------------------- */ enum component_slot { COMPONENT_NAME = -1, COMPONENT_ACTIVE = -2 }; static JSPropertySpec component_props[] = { {"name", COMPONENT_NAME, JSPROP_ENUMERATE | JSPROP_READONLY}, {"active", COMPONENT_ACTIVE, JSPROP_ENUMERATE | JSPROP_READONLY}, {0} }; extern JSClass lm_component_class; PR_STATIC_CALLBACK(JSBool) component_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSComponent *component; JSString *str; jsint slot; if (!JSVAL_IS_INT(id)) return JS_TRUE; slot = JSVAL_TO_INT(id); component = JS_GetInstancePrivate(cx, obj, &lm_component_class, NULL); if (!component) return JS_TRUE; switch (slot) { case COMPONENT_NAME: str = component->name; if (str) *vp = STRING_TO_JSVAL(str); else *vp = JS_GetEmptyStringValue(cx); break; case COMPONENT_ACTIVE: *vp = JSVAL_FALSE; if (ET_moz_CallFunctionBool((ETBoolPtrFunc)component->active_callback, NULL)) *vp = JSVAL_TRUE; break; default: break; } return JS_TRUE; } PR_STATIC_CALLBACK(JSBool) component_mozilla_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { char *name, *type_str, *get_str; jsval type, func; if (!JSVAL_IS_STRING(id)) return JS_TRUE; name = JS_GetStringBytes(JSVAL_TO_STRING(id)); type_str = JS_malloc(cx, XP_STRLEN(lm_typePrefix_str) + XP_STRLEN(name) + 1); get_str = JS_malloc(cx, XP_STRLEN(lm_getPrefix_str) + XP_STRLEN(name) + 1); if (!type_str || !get_str) return JS_TRUE; XP_STRCPY(type_str, lm_typePrefix_str); XP_STRCAT(type_str, name); XP_STRCPY(get_str, lm_getPrefix_str); XP_STRCAT(get_str, name); if (!JS_GetProperty(cx, obj, type_str, &type) || !JSVAL_IS_INT(type) || !JS_GetProperty(cx, obj, get_str, &func)) return JS_TRUE; JS_free(cx, type_str); JS_free(cx, get_str); switch(JSVAL_TO_INT(type)) { case ARGTYPE_INT32: *vp = INT_TO_JSVAL((int32)ET_moz_CompGetterFunction((ETCompPropGetterFunc)JSVAL_TO_INT(func), name)); break; case ARGTYPE_BOOL: *vp = BOOLEAN_TO_JSVAL((JSBool)ET_moz_CompGetterFunction((ETCompPropGetterFunc)JSVAL_TO_INT(func), name)); break; case ARGTYPE_STRING: *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, (char*)ET_moz_CompGetterFunction((ETCompPropGetterFunc)JSVAL_TO_INT(func), name))); break; } return JS_TRUE; } PR_STATIC_CALLBACK(JSBool) component_mozilla_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { char *name, *type_str, *set_str, *prop_val; jsval type, func; if (!JSVAL_IS_STRING(id)) return JS_TRUE; name = JS_GetStringBytes(JSVAL_TO_STRING(id)); type_str = JS_malloc(cx, XP_STRLEN(lm_typePrefix_str) + XP_STRLEN(name) + 1); set_str = JS_malloc(cx, XP_STRLEN(lm_setPrefix_str) + XP_STRLEN(name) + 1); if (!type_str || !set_str) return JS_TRUE; XP_STRCPY(type_str, lm_typePrefix_str); XP_STRCAT(type_str, name); XP_STRCPY(set_str, lm_setPrefix_str); XP_STRCAT(set_str, name); if (!JS_GetProperty(cx, obj, type_str, &type) || !JSVAL_IS_INT(type) || !JS_GetProperty(cx, obj, set_str, &func)) return JS_TRUE; JS_free(cx, type_str); JS_free(cx, set_str); switch(JSVAL_TO_INT(type)) { case ARGTYPE_INT32: if (JSVAL_IS_INT(*vp)) ET_moz_CompSetterFunction((ETCompPropSetterFunc)JSVAL_TO_INT(func), name, (void*)JSVAL_TO_INT(*vp)); break; case ARGTYPE_BOOL: if (JSVAL_IS_BOOLEAN(*vp)) ET_moz_CompSetterFunction((ETCompPropSetterFunc)JSVAL_TO_INT(func), name, (void*)JSVAL_TO_BOOLEAN(*vp)); break; case ARGTYPE_STRING: if (JSVAL_IS_STRING(*vp)) { prop_val = JS_GetStringBytes(JSVAL_TO_STRING(*vp)); ET_moz_CompSetterFunction((ETCompPropSetterFunc)JSVAL_TO_INT(func), name, (void*)prop_val); } break; } return JS_TRUE; } void lm_RegisterComponentProp(const char *comp, const char *targetName, uint8 retType, ETCompPropSetterFunc setter, ETCompPropGetterFunc getter) { JSContext *cx; JSObject *arrayObj, *obj; jsval val; char *type, *set, *get; MochaDecoder *cd = LM_GetCrippledDecoder(); if (!comp || !targetName || !(cx = cd->js_context)) return; arrayObj = lm_DefineComponents(cd); if (!arrayObj) return; if (!JS_GetProperty(cx, arrayObj, comp, &val) || !JSVAL_IS_OBJECT(val)) return; obj = JSVAL_TO_OBJECT(val); if (!JS_DefineProperty(cx, obj, targetName, JSVAL_VOID, component_mozilla_getProperty, component_mozilla_setProperty, 0)) { } type = JS_malloc(cx, XP_STRLEN(lm_typePrefix_str) + XP_STRLEN(targetName) + 1); if (type) { XP_STRCPY(type, lm_typePrefix_str); XP_STRCAT(type, targetName); if (!JS_DefineProperty(cx, obj, type, INT_TO_JSVAL((int32)retType), 0, 0, JSPROP_READONLY)) { } JS_free(cx, type); } get = JS_malloc(cx, XP_STRLEN(lm_getPrefix_str) + XP_STRLEN(targetName) + 1); if (get) { XP_STRCPY(get, lm_getPrefix_str); XP_STRCAT(get, targetName); if (!JS_DefineProperty(cx, obj, get, INT_TO_JSVAL(getter), 0, 0, JSPROP_READONLY)) { } JS_free(cx, get); } set = JS_malloc(cx, XP_STRLEN(lm_setPrefix_str) + XP_STRLEN(targetName) + 1); if (set) { XP_STRCPY(set, lm_setPrefix_str); XP_STRCAT(set, targetName); if (!JS_DefineProperty(cx, obj, set, INT_TO_JSVAL(setter), 0, 0, JSPROP_READONLY)) { } JS_free(cx, set); } } PR_STATIC_CALLBACK(JSBool) component_resolve_name(JSContext *cx, JSObject *obj, jsval id) { return JS_TRUE; } PR_STATIC_CALLBACK(void) component_finalize(JSContext *cx, JSObject *obj) { JSComponent* component; component = JS_GetPrivate(cx, obj); if (!component) return; JS_UnlockGCThing(cx, component->name); DROP_BACK_COUNT(component->decoder); JS_free(cx, component); } JSClass lm_component_class = { "Component", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, component_getProperty, component_getProperty, JS_EnumerateStub, component_resolve_name, JS_ConvertStub, component_finalize }; PR_STATIC_CALLBACK(JSBool) component_activate(JSContext *cx, JSObject *obj, uint argc, jsval *argv, jsval *rval) { JSComponent *component; component = JS_GetInstancePrivate(cx, obj, &lm_component_class, argv); if (!component) return JS_FALSE; ET_moz_CallFunctionAsync((ETVoidPtrFunc)component->startup_callback, NULL); return JS_TRUE; } static JSFunctionSpec component_methods[] = { {"activate", component_activate, 0}, {0} }; PR_STATIC_CALLBACK(JSBool) component_mozilla_method_stub(JSContext *cx, JSObject *obj, uint argc, jsval *argv, jsval *rval) { JSFunction *func; JSObject *func_obj; jsval type, native; uint i; JSCompArg *comp_argv; func = JS_ValueToFunction(cx, argv[-2]); func_obj = JS_GetFunctionObject(func); if (!JS_GetProperty(cx, func_obj, lm_typePrefix_str, &type) || !JSVAL_IS_INT(type) || !JS_GetProperty(cx, func_obj, lm_methodPrefix_str, &native) || !(comp_argv = JS_malloc(cx, argc * sizeof(JSCompArg)))) return JS_TRUE; for (i=0; ijs_context)) return; arrayObj = lm_DefineComponents(cd); if (!arrayObj) return; if (!JS_GetProperty(cx, arrayObj, comp, &val) || !JSVAL_IS_OBJECT(val)) return; obj = JSVAL_TO_OBJECT(val); if (!(func = JS_DefineFunction(cx, obj, targetName, component_mozilla_method_stub, argc, 0))){ } func_obj = JS_GetFunctionObject(func); if (!JS_DefineProperty(cx, func_obj, lm_typePrefix_str, INT_TO_JSVAL((int32)retType), 0, 0, JSPROP_READONLY)) return; if (!JS_DefineProperty(cx, func_obj, lm_methodPrefix_str, INT_TO_JSVAL(method), 0, 0, JSPROP_READONLY)) return; if (!JS_DefineProperty(cx, func_obj, lm_methodArgc_str, INT_TO_JSVAL(argc), 0, 0, JSPROP_READONLY)) return; } /* Constructor method for a JSComponent object */ static JSComponent* component_create_self(JSContext *cx, MochaDecoder* decoder, JSComponent *component, const char *name) { JSObject *obj; /* JSComponent may be malloc'd previous to this to make it easier * to fill in the struct with data from the Mozilla thread */ if (!component) { component = JS_malloc(cx, sizeof(JSComponent)); if (!component) return NULL; } obj = JS_NewObject(cx, &lm_component_class, NULL, NULL); if (!obj || !JS_SetPrivate(cx, obj, component)) { JS_free(cx, component); return NULL; } if (!JS_DefineProperties(cx, obj, component_props)) return NULL; if (!JS_DefineFunctions(cx, obj, component_methods)) return NULL; /* Fill out static property fields */ component->decoder = HOLD_BACK_COUNT(decoder); component->obj = obj; component->name = JS_NewStringCopyZ(cx, name); if (!component->name || !JS_LockGCThing(cx, component->name)) return NULL; return component; } /* * ----------------------------------------------------------------------- * * Reflection of the list of installed components. * The only static property is the array length; * the array elements (JSComponents) are added * lazily when referenced. * * ----------------------------------------------------------------------- */ enum componentarray_slot { COMPONENTLIST_ARRAY_LENGTH = -1 }; static JSPropertySpec componentarray_props[] = { {"length", COMPONENTLIST_ARRAY_LENGTH, JSPROP_READONLY}, {0} }; /* Look up the component for the specified slot of the plug-in list */ static JSComponent* componentarray_create_component(JSContext *cx, JSComponentArray *array, JSComponent *component, const char *targetName, jsint targetSlot) { component = component_create_self(cx, array->decoder, component, targetName); if (component) { char *name; jsval val; name = JS_GetStringBytes(component->name); val = OBJECT_TO_JSVAL(component->obj); JS_DefineProperty(cx, array->obj, name, val, NULL, NULL, JSPROP_ENUMERATE); JS_AliasElement(cx, array->obj, name, targetSlot); array->length++; return component; } return NULL; } extern JSClass lm_component_array_class; void lm_RegisterComponent(const char *targetName, ETBoolPtrFunc active_callback, ETVoidPtrFunc startup_callback) { JSContext *cx; JSObject *arrayObj; JSComponentArray *array; JSComponent *component; jsval val; MochaDecoder *cd = LM_GetCrippledDecoder(); if (!(cx = cd->js_context) || !targetName) return; arrayObj = lm_DefineComponents(cd); if (!arrayObj) return; array = JS_GetInstancePrivate(cx, arrayObj, &lm_component_array_class, NULL); if (!array) return; if (JS_GetProperty(cx, arrayObj, targetName, &val) && JSVAL_IS_OBJECT(val)) { /* We already have a component by this name. Update the active and * startup callback funcs in case ours are out of date */ component = JS_GetPrivate(cx, JSVAL_TO_OBJECT(val)); if (!component) return; component->active_callback = active_callback; component->startup_callback = startup_callback; return; } component = JS_malloc(cx, sizeof(JSComponent)); if (!component) return; component->active_callback = active_callback; component->startup_callback = startup_callback; componentarray_create_component(cx, array, component, targetName, array->length); } PR_STATIC_CALLBACK(JSBool) componentarray_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSComponentArray *array; jsint slot; if (!JSVAL_IS_INT(id)) return JS_TRUE; slot = JSVAL_TO_INT(id); array = JS_GetInstancePrivate(cx, obj, &lm_component_array_class, NULL); if (!array) return JS_TRUE; switch (slot) { case COMPONENTLIST_ARRAY_LENGTH: *vp = INT_TO_JSVAL(array->length); break; default: /* Don't mess with a user-defined property. */ if (slot >= 0 && slot < (jsint) array->length) { /* Search for an existing JSComponent for this slot */ JSObject* componentObj = NULL; jsval val = *vp; if (JSVAL_IS_OBJECT(val)) { componentObj = JSVAL_TO_OBJECT(val); } else { JSComponent* component; component = componentarray_create_component(cx, array, NULL, NULL, slot); if (component) componentObj = component->obj; } *vp = OBJECT_TO_JSVAL(componentObj); break; } } return JS_TRUE; } PR_STATIC_CALLBACK(void) componentarray_finalize(JSContext *cx, JSObject *obj) { JSComponentArray* array; array = JS_GetPrivate(cx, obj); if (!array) return; DROP_BACK_COUNT(array->decoder); JS_free(cx, array); } JSClass lm_component_array_class = { "ComponentArray", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, componentarray_getProperty, componentarray_getProperty, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, componentarray_finalize }; JSObject* lm_DefineComponents(MochaDecoder *decoder) { JSObject *obj; JSComponentArray *array; JSContext *cx = decoder->js_context; JSPreDefComponent def_comps; JSComponent *component; jsint slot; obj = decoder->components; if (obj) return obj; array = JS_malloc(cx, sizeof(JSComponentArray)); if (!array) return NULL; XP_BZERO(array, sizeof *array); obj = JS_NewObject(cx, &lm_component_array_class, NULL, decoder->window_object); if (!obj || !JS_SetPrivate(cx, obj, array)) { JS_free(cx, array); return NULL; } if (!JS_DefineProperties(cx, obj, componentarray_props)) return NULL; array->decoder = HOLD_BACK_COUNT(decoder); array->obj = obj; /* Components can be added dynamically but some are predefined */ slot = 0; array->length = 0; def_comps = predef_components[slot]; while (def_comps.name) { component = JS_malloc(cx, sizeof(JSComponent)); if (!component) return NULL; if (ET_moz_VerifyComponentFunction(def_comps.func, &(component->active_callback), &(component->startup_callback))) { componentarray_create_component(cx, array, component, def_comps.name, array->length); } else { /*Component call failed somewhere.*/ JS_free(cx, component); } def_comps = predef_components[++slot]; } return obj; }