/* -*- 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.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ /* * JS reflection of the HTML FORM elements. * * Brendan Eich, 9/27/95 */ #include "lm.h" #include "lo_ele.h" /* #include "netutils.h" */ #include "mkutils.h" #include "layout.h" #include "pa_tags.h" #include "shist.h" enum form_array_slot { FORM_ARRAY_LENGTH = -1 }; static JSPropertySpec form_array_props[] = { {lm_length_str, FORM_ARRAY_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT}, {0} }; extern JSClass lm_form_array_class; PR_STATIC_CALLBACK(JSBool) form_array_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSObjectArray *array; MochaDecoder *decoder; MWContext *context; jsint count, slot; lo_FormData *form_data; int32 active_layer_id; if (!JSVAL_IS_INT(id)) return JS_TRUE; slot = JSVAL_TO_INT(id); array = JS_GetInstancePrivate(cx, obj, &lm_form_array_class, NULL); if (!array) return JS_TRUE; decoder = array->decoder; context = decoder->window_context; if (!context) return JS_TRUE; LO_LockLayout(); switch (slot) { case FORM_ARRAY_LENGTH: active_layer_id = LM_GetActiveLayer(context); LM_SetActiveLayer(context, array->layer_id); count = LO_EnumerateForms(context, array->layer_id); LM_SetActiveLayer(context, active_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; } if (slot >= array->length) array->length = slot + 1; /* NB: form IDs start at 1, not 0. */ form_data = LO_GetFormDataByID(context, array->layer_id, slot + 1); if (form_data) *vp = OBJECT_TO_JSVAL(LM_ReflectForm(context, form_data, NULL, array->layer_id, 0)); break; } LO_UnlockLayout(); return JS_TRUE; } PR_STATIC_CALLBACK(void) form_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_form_array_class = { "FormArray", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, form_array_getProperty, form_array_getProperty, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, form_array_finalize }; PR_STATIC_CALLBACK(JSBool) FormArray(JSContext *cx, JSObject *obj, uint argc, jsval *argv, jsval *rval) { return JS_TRUE; } JSObject * lm_GetFormArray(MochaDecoder *decoder, JSObject *document) { JSObject *obj; JSContext *cx; JSObjectArray *array; JSDocument *doc; cx = decoder->js_context; doc = JS_GetPrivate(cx, document); if (!doc) return NULL; obj = doc->forms; if (obj) return obj; array = JS_malloc(cx, sizeof *array); if (!array) return NULL; XP_BZERO(array, sizeof *array); obj = JS_NewObject(cx, &lm_form_array_class, NULL, document); if (!obj || !JS_SetPrivate(cx, obj, array)) { JS_free(cx, array); return NULL; } if (!JS_DefineProperties(cx, obj, form_array_props)) return NULL; array->decoder = HOLD_BACK_COUNT(decoder); array->layer_id = doc->layer_id; doc->forms = obj; return obj; } /* * Forms can be treated as arrays of their elements, so all named properties * have negative slot numbers < -1. */ enum form_slot { FORM_LENGTH = -1, FORM_NAME = -2, FORM_ELEMENTS = -3, FORM_METHOD = -4, FORM_ACTION = -5, FORM_ENCODING = -6, FORM_TARGET = -7 }; static char form_action_str[] = "action"; static JSPropertySpec form_props[] = { {"length", FORM_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY}, {"name", FORM_NAME, JSPROP_ENUMERATE}, {"elements", FORM_ELEMENTS, JSPROP_ENUMERATE | JSPROP_READONLY}, {"method", FORM_METHOD, JSPROP_ENUMERATE}, {form_action_str, FORM_ACTION, JSPROP_ENUMERATE}, {"encoding", FORM_ENCODING, JSPROP_ENUMERATE}, {"target", FORM_TARGET, JSPROP_ENUMERATE}, {0} }; typedef struct JSForm { JSObjectArray object_array; JSObject *form_object; uint32 form_event_mask; int32 layer_id; intn form_id; JSString *name; PRHashTable *form_element_map; /* Map from element id to object */ } JSForm; #define form_decoder object_array.decoder #define form_length object_array.length typedef struct FormMethodMap { char *name; uint code; } FormMethodMap; static FormMethodMap form_method_map[] = { {"get", FORM_METHOD_GET}, {"post", FORM_METHOD_POST}, {0} }; static char * form_method_name(uint code) { FormMethodMap *mm; for (mm = form_method_map; mm->name; mm++) if (mm->code == code) return mm->name; return "unknown"; } static int form_method_code(const char *name) { FormMethodMap *mm; for (mm = form_method_map; mm->name; mm++) if (XP_STRCASECMP(mm->name, name) == 0) return mm->code; return -1; } extern JSClass lm_form_class; PR_STATIC_CALLBACK(JSBool) form_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSForm *form; MWContext *context; lo_FormData *form_data; jsint count; JSString *str; LO_Element **ele_list; LO_FormElementStruct *first_ele; uint first_index; jsint slot; if (!JSVAL_IS_INT(id)) return JS_TRUE; slot = JSVAL_TO_INT(id); form = JS_GetInstancePrivate(cx, obj, &lm_form_class, NULL); if (!form) return JS_TRUE; context = form->form_decoder->window_context; if (!context) return JS_TRUE; LO_LockLayout(); form_data = LO_GetFormDataByID(context, form->layer_id, form->form_id); if (!form_data) goto good; switch (slot) { case FORM_LENGTH: count = LO_EnumerateFormElements(context, form_data); if (count > form->form_length) form->form_length = count; *vp = INT_TO_JSVAL(form->form_length); goto good; case FORM_NAME: str = lm_LocalEncodingToStr(context, (char *)form_data->name); break; case FORM_ELEMENTS: *vp = OBJECT_TO_JSVAL(form->form_object); goto good; case FORM_METHOD: str = JS_NewStringCopyZ(cx, form_method_name(form_data->method)); break; case FORM_ACTION: str = JS_NewStringCopyZ(cx, (char *)form_data->action); break; case FORM_ENCODING: str = JS_NewStringCopyZ(cx, (char *)form_data->encoding); break; case FORM_TARGET: str = JS_NewStringCopyZ(cx, (char *)form_data->window_target); break; default: if ((uint)slot >= (uint)form_data->form_ele_cnt) { /* Don't mess with a user-defined or method property. */ goto good; } PA_LOCK(ele_list, LO_Element **, form_data->form_elements); first_ele = (LO_FormElementStruct *)ele_list[0]; first_index = (uint)first_ele->element_index; PA_UNLOCK(form_data->form_elements); *vp = OBJECT_TO_JSVAL(LM_ReflectFormElement(context, form->layer_id, form->form_id, first_index + slot, NULL)); goto good; } LO_UnlockLayout(); /* Common tail code for string-type properties. */ if (!str) return JS_FALSE; *vp = STRING_TO_JSVAL(str); return JS_TRUE; good: LO_UnlockLayout(); return JS_TRUE; } PR_STATIC_CALLBACK(JSBool) form_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSForm *form; MWContext *context; lo_FormData *form_data; const char *value; jsint slot; form = JS_GetInstancePrivate(cx, obj, &lm_form_class, NULL); if (!form) return JS_TRUE; context = form->form_decoder->window_context; if (!context) return JS_TRUE; if (!JSVAL_IS_INT(id)) return JS_TRUE; slot = JSVAL_TO_INT(id); LO_LockLayout(); form_data = LO_GetFormDataByID(context, form->layer_id, form->form_id); if (!form_data) goto good; if (!JSVAL_IS_STRING(*vp) && !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) { goto bad; } value = JS_GetStringBytes(JSVAL_TO_STRING(*vp)); switch (slot) { case FORM_METHOD: form_data->method = form_method_code(value); break; case FORM_ACTION: value = lm_CheckURL(cx, value, JS_TRUE); if (!value) goto bad; if (!lm_SaveParamString(cx, &form_data->action, value)) goto bad; XP_FREE((char *)value); break; case FORM_ENCODING: if (!lm_SaveParamString(cx, &form_data->encoding, value)) goto bad; break; case FORM_TARGET: if (!lm_CheckWindowName(cx, value)) goto bad; if (!lm_SaveParamString(cx, &form_data->window_target, value)) goto bad; break; } LO_UnlockLayout(); return form_getProperty(cx, obj, id, vp); good: LO_UnlockLayout(); return JS_TRUE; bad: LO_UnlockLayout(); return JS_FALSE; } PR_STATIC_CALLBACK(JSBool) form_list_properties(JSContext *cx, JSObject *obj) { JSForm *form; MWContext *context; lo_FormData *form_data; form = JS_GetPrivate(cx, obj); if (!form) return JS_TRUE; context = form->form_decoder->window_context; if (!context) return JS_TRUE; LO_LockLayout(); form_data = LO_GetFormDataByID(context, form->layer_id, form->form_id); if (!form_data) { LO_UnlockLayout(); return JS_TRUE; } /* XXX should return FALSE on reflection error */ (void) LO_EnumerateFormElements(context, form_data); LO_UnlockLayout(); return JS_TRUE; } PR_STATIC_CALLBACK(JSBool) form_resolve_name(JSContext *cx, JSObject *obj, jsval id) { JSForm *form; MWContext *context; lo_FormData *form_data; LO_Element **ele_list; LO_FormElementStruct *form_ele; lo_FormElementMinimalData *min_data; int32 i; const char * name; if (!JSVAL_IS_STRING(id)) return JS_TRUE; name = JS_GetStringBytes(JSVAL_TO_STRING(id)); form = JS_GetPrivate(cx, obj); if (!form) return JS_TRUE; context = form->form_decoder->window_context; if (!context) return JS_TRUE; LO_LockLayout(); form_data = LO_GetFormDataByID(context, form->layer_id, form->form_id); if (!form_data) { LO_UnlockLayout(); return JS_TRUE; } PA_LOCK(ele_list, LO_Element **, form_data->form_elements); for (i = 0; i < form_data->form_ele_cnt; i++) { if (ele_list[i]->type != LO_FORM_ELE) continue; form_ele = (LO_FormElementStruct *)ele_list[i]; if (!form_ele->element_data) continue; min_data = &form_ele->element_data->ele_minimal; if (min_data->name && XP_STRCMP((char *)min_data->name, name) == 0) (void) LM_ReflectFormElement(context, form->layer_id, form->form_id, form_ele->element_index, NULL); } PA_UNLOCK(form_data->form_elements); LO_UnlockLayout(); return JS_TRUE; } PR_STATIC_CALLBACK(void) form_finalize(JSContext *cx, JSObject *obj) { JSForm *form; MochaDecoder *decoder; MWContext *context; lo_FormData *form_data; form = JS_GetPrivate(cx, obj); if (!form) return; decoder = form->form_decoder; context = decoder->window_context; if (context) { LO_LockLayout(); form_data = LO_GetFormDataByID(context, form->layer_id, form->form_id); if (form_data && form_data->mocha_object == obj) form_data->mocha_object = NULL; LO_UnlockLayout(); } DROP_BACK_COUNT(decoder); if (form->form_element_map) PR_HashTableDestroy(form->form_element_map); JS_free(cx, form); } JSClass lm_form_class = { "Form", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, form_getProperty, form_setProperty, form_list_properties, form_resolve_name, JS_ConvertStub, form_finalize }; PR_STATIC_CALLBACK(JSBool) Form(JSContext *cx, JSObject *obj, uint argc, jsval *argv, jsval *rval) { return JS_TRUE; } static JSBool form_native_prolog(JSContext *cx, JSObject *obj, jsval *argv, JSForm **formp, lo_FormData **form_datap) { JSForm *form; MochaDecoder *decoder; lo_FormData *form_data; if (!JS_InstanceOf(cx, obj, &lm_form_class, argv)) return JS_FALSE; form = JS_GetPrivate(cx, obj); if (!form) { *formp = NULL; *form_datap = NULL; return JS_TRUE; } decoder = form->form_decoder; form_data = decoder->window_context ? LO_GetFormDataByID(decoder->window_context, form->layer_id, form->form_id) : 0; *formp = form; *form_datap = form_data; return JS_TRUE; } PR_STATIC_CALLBACK(JSBool) form_reset(JSContext *cx, JSObject *obj, uint argc, jsval *argv, jsval *rval) { JSForm *form; lo_FormData *form_data; LO_Element **ele_list; if (!form_native_prolog(cx, obj, argv, &form, &form_data)) return JS_FALSE; if (form_data && form_data->form_ele_cnt > 0) { /* There is no form LO_Element; use the first thing in the form. */ PA_LOCK(ele_list, LO_Element **, form_data->form_elements); ET_lo_ResetForm(form->form_decoder->window_context, ele_list[0]); PA_UNLOCK(form_data->form_elements); } return JS_TRUE; } PR_STATIC_CALLBACK(JSBool) form_submit(JSContext *cx, JSObject *obj, uint argc, jsval *argv, jsval *rval) { JSForm *form; lo_FormData *form_data; JSBool ok; MochaDecoder *decoder; MWContext *context; LO_Element **ele_list, *element; if (!form_native_prolog(cx, obj, argv, &form, &form_data)) return JS_FALSE; if (!form_data) return JS_TRUE; switch (NET_URL_Type((const char *) form_data->action)) { case MAILTO_TYPE_URL: case NEWS_TYPE_URL: /* only OK if we are a signed script */ ok = lm_CanAccessTarget(cx, JSTARGET_UNIVERSAL_SEND_MAIL); break; default: ok = JS_TRUE; break; } if (!ok) { /* XXX silently fail this mailto: or news: form post. */ return JS_TRUE; } if (form_data->form_ele_cnt > 0) { decoder = form->form_decoder; context = decoder->window_context; PA_LOCK(ele_list, LO_Element **, form_data->form_elements); element = ele_list[0]; PA_UNLOCK(form_data->form_elements); ET_fe_SubmitInputElement(context, element); } return JS_TRUE; } static JSFunctionSpec form_methods[] = { {"reset", form_reset, 0}, {"submit", form_submit, 0}, {0} }; static JSBool form_event(MWContext *context, LO_Element *element, JSEvent *event, char *method) { lo_FormData *form_data; JSObject *obj; JSForm *form; JSBool ok; jsval rval; if (element->type != LO_FORM_ELE && (element->type != LO_IMAGE || !element->lo_image.image_attr)) { LO_UnlockLayout(); return JS_TRUE; } form_data = LO_GetFormDataByID(context, (element->type == LO_IMAGE) ? element->lo_image.image_attr->layer_id : element->lo_form.layer_id, (element->type == LO_IMAGE) ? element->lo_image.image_attr->form_id : element->lo_form.form_id); if (!form_data || !(obj = form_data->mocha_object)) { LO_UnlockLayout(); return JS_TRUE; } LO_UnlockLayout(); form = JS_GetPrivate(context->mocha_context, obj); if (!form) return JS_TRUE; if (form->form_event_mask & event->type) return JS_TRUE; form->form_event_mask |= event->type; ok = lm_SendEvent(context, obj, event, &rval); form->form_event_mask &= ~event->type; if (ok && rval == JSVAL_FALSE) return JS_FALSE; return JS_TRUE; } JSBool lm_SendOnReset(MWContext *context, JSEvent *event, LO_Element *element) { return form_event(context, element, event, lm_onReset_str); } JSBool lm_SendOnSubmit(MWContext *context, JSEvent *event, LO_Element *element) { return form_event(context, element, event, lm_onSubmit_str); } #ifdef DEBUG_brendan static char * form_name(lo_FormData *form_data) { static char buf[20]; if (form_data->name) return (char *)form_data->name; PR_snprintf(buf, sizeof buf, "$form%d", form_data->id - 1); return buf; } #endif JSObject * LM_ReflectForm(MWContext *context, lo_FormData *form_data, PA_Tag * tag, int32 layer_id, uint index) { JSObject *obj, *array_obj, *prototype, *document; MochaDecoder *decoder; JSContext *cx; JSForm *form; JSBool ok; lo_TopState *top_state; PRHashTable *map; /* if we got passed an index get the form data by index and don't * trust the lo_FormData that was passed in */ if (index) form_data = LO_GetFormDataByID(context, layer_id, index); if (!form_data) return NULL; obj = form_data->mocha_object; if (obj) return obj; decoder = LM_GetMochaDecoder(context); if (!decoder) return NULL; cx = decoder->js_context; top_state = lo_GetMochaTopState(context); if (top_state->resize_reload) { map = lm_GetIdToObjectMap(decoder); if (map) obj = (JSObject *)PR_HashTableLookup(map, LM_GET_MAPPING_KEY(LM_FORMS, layer_id, index)); if (obj) { form_data->mocha_object = obj; goto out; } } /* Get the document object that will hold this form */ document = lm_GetDocumentFromLayerId(decoder, layer_id); if (!document) goto out; array_obj = lm_GetFormArray(decoder, document); if (!array_obj) goto out; prototype = decoder->form_prototype; if (!prototype) { prototype = JS_InitClass(cx, decoder->window_object, decoder->event_receiver_prototype, &lm_form_class, Form, 0, form_props, form_methods, NULL, NULL); if (!prototype) goto out; decoder->form_prototype = prototype; } form = JS_malloc(cx, sizeof *form); if (!form) goto out; XP_BZERO(form, sizeof *form); obj = JS_NewObject(cx, &lm_form_class, prototype, document); if (!obj || !JS_SetPrivate(cx, obj, form)) { JS_free(cx, form); obj = NULL; goto out; } if (form_data->name) { ok = JS_DefineProperty(cx, document, (char *) form_data->name, OBJECT_TO_JSVAL(obj), NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY); if (!ok) { obj = NULL; goto out; } } /* put it in the form array */ if (!lm_AddObjectToArray(cx, array_obj, (char *) form_data->name, index - 1, obj)) { obj = NULL; goto out; } /* Put it in the index to object hash table */ map = lm_GetIdToObjectMap(decoder); if (map) PR_HashTableAdd(map, LM_GET_MAPPING_KEY(LM_FORMS, layer_id, index), obj); form->form_decoder = HOLD_BACK_COUNT(decoder); form->form_element_map = NULL; form->form_object = obj; form->form_id = form_data->id; form->layer_id = layer_id; form_data->mocha_object = obj; /* see if there are any other attributes that we should be * adding to this object */ if(tag) { PA_Block onreset, onsubmit, id; /* don't hold the layout lock across compiles */ LO_UnlockLayout(); onreset = lo_FetchParamValue(context, tag, PARAM_ONRESET); onsubmit = lo_FetchParamValue(context, tag, PARAM_ONSUBMIT); id = lo_FetchParamValue(context, tag, PARAM_ID); ok = JS_TRUE; if (onsubmit) { ok = lm_CompileEventHandler(decoder, id, tag->data, tag->newline_count, obj, PARAM_ONSUBMIT, onsubmit); PA_FREE(onsubmit); } if (onreset) { ok &= lm_CompileEventHandler(decoder, id, tag->data, tag->newline_count, obj, PARAM_ONRESET, onreset); PA_FREE(onreset); } if (!ok) obj = NULL; if (id) PA_FREE(id); LO_LockLayout(); } out: LM_PutMochaDecoder(decoder); return obj; } JSObject * lm_GetFormObjectByID(MWContext * context, int32 layer_id, uint form_id) { lo_FormData *form_data; form_data = LO_GetFormDataByID(context, layer_id, form_id); if (!form_data) return NULL; return form_data->mocha_object; } LO_FormElementStruct * lm_GetFormElementByIndex(JSContext * cx, JSObject *form_obj, int32 index) { JSForm *form; MWContext *context; lo_FormData *form_data; if (!form_obj) return NULL; form = JS_GetPrivate(cx, form_obj); if (!form) return NULL; context = form->form_decoder->window_context; if (!context) return NULL; form_data = LO_GetFormDataByID(context, form->layer_id, form->form_id); if (!form_data) return NULL; return LO_GetFormElementByIndex(form_data, index); } PRIVATE JSBool lm_normalize_element_index(lo_FormData *form_data, uint *index) { LO_Element **ele_list; LO_FormElementStruct *first_ele; uint first_index; /* XXX confine this to laymocha.c where LO_GetFormByElementIndex() lives */ if (form_data->form_ele_cnt == 0) { *index = 0; } else { ele_list = (LO_Element **) form_data->form_elements; first_ele = (LO_FormElementStruct *)ele_list[0]; first_index = (uint)first_ele->element_index; XP_ASSERT(*index >= first_index); if (*index < first_index) return JS_FALSE; *index -= first_index; } return JS_TRUE; } JSBool lm_AddFormElement(JSContext *cx, JSObject *form_obj, JSObject *ele_obj, char *name, uint index) { JSForm *form; MWContext *context; lo_FormData *form_data; form = JS_GetPrivate(cx, form_obj); if (!form) return JS_TRUE; context = form->form_decoder->window_context; if (!context) return JS_FALSE; form_data = LO_GetFormDataByID(context, form->layer_id, form->form_id); if (!form_data) return JS_FALSE; if (!lm_normalize_element_index(form_data, &index)) return JS_FALSE; if (!form->form_element_map) form->form_element_map = PR_NewHashTable(LM_FORM_ELEMENT_MAP_SIZE, lm_KeyHash, PR_CompareValues, PR_CompareValues, NULL, NULL); if (!form->form_element_map) return JS_FALSE; PR_HashTableAdd(form->form_element_map, (void *)index, ele_obj); /* put it in the form elememt array */ return (lm_AddObjectToArray(cx, form_obj, name, index, ele_obj)); } JSObject * lm_GetFormElementFromMapping(JSContext *cx, JSObject *form_obj, uint32 index) { JSForm *form; MWContext *context; lo_FormData *form_data; form = JS_GetPrivate(cx, form_obj); if (!form || !form->form_element_map) return NULL; context = form->form_decoder->window_context; if (!context) return NULL; form_data = LO_GetFormDataByID(context, form->layer_id, form->form_id); if (!form_data) return NULL; /* * This converts element_id to index within this form. Need to do * this here, because it also happens when we put it into the hash * table. */ if (!lm_normalize_element_index(form_data, (uint *)&index)) return NULL; return (JSObject *)PR_HashTableLookup(form->form_element_map, (void *)index); } JSBool lm_ReflectRadioButtonArray(MWContext *context, int32 layer_id, intn form_id, const char *name, PA_Tag * tag) { lo_FormData *form_data; JSBool ok; LO_Element **ele_list; int32 i, element_index; LO_FormElementStruct *form_ele; LO_FormElementData *data; form_data = LO_GetFormDataByID(context, layer_id, form_id); if (!form_data) return JS_FALSE; ok = JS_TRUE; for (i = 0; i < form_data->form_ele_cnt; i++) { /* * Resample since the reflect call may release the layout * lock and the element list might get reallocated */ ele_list = (LO_Element **) form_data->form_elements; form_ele = (LO_FormElementStruct *)ele_list[i]; /* * If both the current element name and the passed in name are * non-NULL they must be equal. If name is NULL reflect all * radio buttons */ data = form_ele->element_data; if (data && ((data->ele_minimal.name && name) ? !XP_STRCMP((char *)data->ele_minimal.name, name) : data->type == FORM_TYPE_RADIO) && form_ele->mocha_object == NULL) { element_index = form_ele->element_index; ok = (JSBool) (LM_ReflectFormElement(context, layer_id, form_id, element_index, (tag->lo_data == (void *)element_index) ? tag : NULL) != NULL); if (!ok) break; } } return ok; }