mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-30 05:35:31 +00:00
994 lines
25 KiB
C
994 lines
25 KiB
C
/* -*- 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;
|
|
}
|