netscape-revival
lib/libmocha/lm_input.c
/*
** Mocha input focus and event notifiers.
**
** Brendan Eich, 9/27/95
**
** XXX SIZE, MAXLENGTH attributes
*/
#include "lm.h"
#include "xp.h"
#include "fe_proto.h"
#include "lo_ele.h"
#include "pa_tags.h"
#include "layout.h" /* XXX just for S_FORM_TYPE_... names */
#include "prmem.h"
#include "prprf.h"
enum input_slot {
INPUT_TYPE = -1,
INPUT_NAME = -2,
INPUT_FORM = -3,
INPUT_VALUE = -4,
INPUT_DEFAULT_VALUE = -5,
INPUT_LENGTH = -6,
INPUT_OPTIONS = -7,
INPUT_SELECTED_INDEX = -8,
INPUT_STATUS = -9,
INPUT_DEFAULT_STATUS = -10
};
static char lm_options_str[] = "options";
static MochaPropertySpec input_props[] = {
{"type", INPUT_TYPE, MDF_ENUMERATE|MDF_READONLY},
{"name", INPUT_NAME, MDF_ENUMERATE|MDF_TAINTED},
{"form", INPUT_FORM, MDF_BACKEDGE|MDF_ENUMERATE|
MDF_READONLY},
{"value", INPUT_VALUE, MDF_ENUMERATE|MDF_TAINTED},
{"defaultValue", INPUT_DEFAULT_VALUE, MDF_ENUMERATE|MDF_TAINTED},
{lm_length_str, INPUT_LENGTH, MDF_ENUMERATE},
{lm_options_str, INPUT_OPTIONS, MDF_BACKEDGE|MDF_ENUMERATE|
MDF_READONLY},
{"selectedIndex", INPUT_SELECTED_INDEX, MDF_ENUMERATE|MDF_TAINTED},
{"status", INPUT_STATUS, MDF_TAINTED},
{"defaultStatus", INPUT_DEFAULT_STATUS, MDF_TAINTED},
{PARAM_CHECKED, INPUT_STATUS, MDF_ENUMERATE|MDF_TAINTED},
{"defaultChecked", INPUT_DEFAULT_STATUS, MDF_ENUMERATE|MDF_TAINTED},
{0}
};
/*
** Base input element type.
*/
typedef struct MochaInput {
MochaInputHandler handler;
int32 index;
} MochaInput;
#define input_decoder handler.base_decoder
#define input_type handler.base_type
#define input_object handler.object
#define input_event_mask handler.event_mask
/*
** Text and textarea input type.
*/
typedef struct MochaTextInput {
MochaInput input;
} MochaTextInput;
/*
** Select option tag reflected type.
*/
enum option_slot {
OPTION_INDEX = -1,
OPTION_TEXT = -2,
OPTION_VALUE = -3,
OPTION_DEFAULT_SELECTED = -4,
OPTION_SELECTED = -5
};
static MochaPropertySpec option_props[] = {
{"index", OPTION_INDEX, MDF_ENUMERATE|MDF_READONLY},
{"text", OPTION_TEXT, MDF_ENUMERATE|MDF_TAINTED},
{"value", OPTION_VALUE, MDF_ENUMERATE|MDF_TAINTED},
{"defaultSelected", OPTION_DEFAULT_SELECTED, MDF_ENUMERATE|MDF_TAINTED},
{"selected", OPTION_SELECTED, MDF_ENUMERATE|MDF_TAINTED},
{0}
};
typedef struct MochaSelectOption {
MochaDecoder *decoder;
MochaObject *object;
uint32 index;
int32 indexInForm;
lo_FormElementOptionData *data;
} MochaSelectOption;
static MochaBoolean
option_get_property(MochaContext *mc, MochaObject *obj, MochaSlot slot,
MochaDatum *dp)
{
MochaSelectOption *option;
lo_FormElementOptionData *optionData;
lo_FormElementSelectData *selectData;
LO_FormElementStruct *form_element;
enum option_slot option_slot;
MochaAtom *atom;
char *value;
option = obj->data;
optionData = option->data;
if (optionData) {
selectData = 0;
form_element = 0;
} else {
if (!obj->parent)
return MOCHA_TRUE;
form_element = lm_GetFormElementByIndex(obj->parent->parent,
option->indexInForm);
if (!form_element)
return MOCHA_TRUE;
selectData = &form_element->element_data->ele_select;
}
option_slot = slot;
switch (option_slot) {
case OPTION_INDEX:
MOCHA_INIT_DATUM(mc, dp, MOCHA_NUMBER, u.fval, option->index);
break;
case OPTION_TEXT:
case OPTION_VALUE:
if (selectData) {
PA_LOCK(optionData, lo_FormElementOptionData *,
selectData->options);
}
if (slot == OPTION_TEXT) {
value = (char *)optionData[option->index].text_value;
atom = MOCHA_Atomize(mc, value);
} else {
value = (char *)optionData[option->index].value;
atom = MOCHA_Atomize(mc, value);
}
if (selectData)
PA_UNLOCK(selectData->options);
if (!atom)
return MOCHA_FALSE;
MOCHA_INIT_DATUM(mc, dp, MOCHA_STRING, u.atom, atom);
break;
case OPTION_DEFAULT_SELECTED:
case OPTION_SELECTED:
if (selectData) {
PA_LOCK(optionData, lo_FormElementOptionData *,
selectData->options);
}
MOCHA_INIT_DATUM(mc, dp, MOCHA_BOOLEAN, u.bval,
(option_slot == OPTION_DEFAULT_SELECTED)
? optionData[option->index].def_selected
: optionData[option->index].selected);
if (selectData)
PA_UNLOCK(selectData->options);
break;
default:
/* Don't mess with a user-defined or method property. */
break;
}
if ((dp->flags & MDF_TAINTED) && dp->taint == MOCHA_TAINT_IDENTITY)
return lm_GetTaint(mc, option->decoder, &dp->taint);
return MOCHA_TRUE;
}
static MochaBoolean
option_set_property(MochaContext *mc, MochaObject *obj, MochaSlot slot,
MochaDatum *dp)
{
MochaSelectOption *option;
lo_FormElementOptionData *optionData;
lo_FormElementSelectData *selectData;
LO_FormElementStruct *form_element;
enum option_slot option_slot;
MochaBoolean showChange;
int32 i;
option = obj->data;
optionData = option->data;
if (optionData) {
selectData = 0;
form_element = 0;
} else {
if (!obj->parent)
return MOCHA_TRUE;
form_element = lm_GetFormElementByIndex(obj->parent->parent,
option->indexInForm);
if (!form_element)
return TRUE;
selectData = &form_element->element_data->ele_select;
}
option_slot = slot;
showChange = MOCHA_FALSE;
switch (option_slot) {
case OPTION_TEXT:
case OPTION_VALUE:
if (dp->tag != MOCHA_STRING &&
!MOCHA_ConvertDatum(mc, *dp, MOCHA_STRING, dp)) {
return MOCHA_FALSE;
}
if (selectData) {
PA_LOCK(optionData, lo_FormElementOptionData *,
selectData->options);
}
if (option_slot == OPTION_TEXT) {
if (!lm_SaveParamString(mc, &optionData[option->index].text_value,
MOCHA_GetAtomName(mc, dp->u.atom))) {
return MOCHA_FALSE;
}
showChange = MOCHA_TRUE;
} else {
if (!lm_SaveParamString(mc, &optionData[option->index].value,
MOCHA_GetAtomName(mc, dp->u.atom))) {
return MOCHA_FALSE;
}
}
if (selectData)
PA_UNLOCK(selectData->options);
break;
case OPTION_DEFAULT_SELECTED:
case OPTION_SELECTED:
if (dp->tag != MOCHA_BOOLEAN &&
!MOCHA_ConvertDatum(mc, *dp, MOCHA_BOOLEAN, dp)) {
return MOCHA_FALSE;
}
if (selectData) {
PA_LOCK(optionData, lo_FormElementOptionData *,
selectData->options);
}
if (option_slot == OPTION_DEFAULT_SELECTED)
optionData[option->index].def_selected = dp->u.bval;
else
optionData[option->index].selected = dp->u.bval;
if (selectData) {
if (dp->u.bval && !selectData->multiple) {
/* Clear all the others. */
for (i = 0; i < selectData->option_cnt; i++) {
if ((uint32)i == option->index)
continue;
if (option_slot == OPTION_DEFAULT_SELECTED)
optionData[i].def_selected = FALSE;
else
optionData[i].selected = FALSE;
}
}
PA_UNLOCK(selectData->options);
}
if (option_slot == OPTION_SELECTED)
showChange = MOCHA_TRUE;
break;
default:
/* Don't mess with a user-defined property. */
return TRUE;
}
if (showChange && option->decoder->window_context && form_element) {
FE_ChangeInputElement(option->decoder->window_context,
(LO_Element *)form_element);
}
return option_get_property(mc, obj, slot, dp);
}
static void
option_finalize(MochaContext *mc, MochaObject *obj)
{
MochaSelectOption *option;
lo_FormElementOptionData *optionData;
option = obj->data;
if (!option) return;
LM_UNLINK_OBJECT(option->decoder, obj);
optionData = option->data;
if (optionData) {
if (optionData->text_value)
MOCHA_free(mc, optionData->text_value);
if (optionData->value)
MOCHA_free(mc, optionData->value);
MOCHA_free(mc, optionData);
}
MOCHA_free(mc, option);
}
static MochaClass option_class = {
"Option",
option_get_property, option_set_property, MOCHA_ListPropStub,
MOCHA_ResolveStub, MOCHA_ConvertStub, option_finalize
};
/*
** Select option constructor, can be called any of these ways:
** opt = new Option()
** opt = new Option(text)
** opt = new Option(text, value)
** opt = new Option(text, value, defaultSelected)
** opt = new Option(text, value, defaultSelected, selected)
** Where opt can be selectData.options[i] for any nonnegative integer i.
*/
static MochaBoolean
Option(MochaContext *mc, MochaObject *obj,
uint argc, MochaDatum *argv, MochaDatum *rval)
{
MochaDecoder *decoder;
MochaSelectOption *option;
lo_FormElementOptionData *optionData;
MochaAtom *atom;
MochaBoolean bval;
if (obj->clazz != &option_class) {
*rval = MOCHA_null;
return MOCHA_TRUE;
}
decoder = MOCHA_GetGlobalObject(mc)->data;
option = MOCHA_malloc(mc, sizeof *option);
if (!option)
return MOCHA_FALSE;
XP_BZERO(option, sizeof *option);
obj->data = option;
LM_LINK_OBJECT(decoder, obj, "anOption");
optionData = MOCHA_malloc(mc, sizeof *optionData);
if (!optionData)
goto fail;
XP_BZERO(optionData, sizeof *optionData);
option->data = optionData;
if (argc >= 4) {
if (argv[3].tag != MOCHA_BOOLEAN &&
!MOCHA_DatumToBoolean(mc, argv[3], &bval)) {
goto fail;
}
optionData->selected = bval;
}
if (argc >= 3) {
if (argv[2].tag != MOCHA_BOOLEAN &&
!MOCHA_DatumToBoolean(mc, argv[2], &bval)) {
goto fail;
}
optionData->def_selected = bval;
}
if (argc >= 2) {
if (argv[1].tag == MOCHA_STRING)
atom = MOCHA_HoldAtom(mc, argv[1].u.atom);
else if (!MOCHA_DatumToString(mc, argv[1], &atom))
goto fail;
optionData->value =
(PA_Block)MOCHA_strdup(mc, MOCHA_GetAtomName(mc, atom));
MOCHA_DropAtom(mc, atom);
if (!optionData->value)
goto fail;
}
if (argc >= 1) {
if (argv[0].tag == MOCHA_STRING)
atom = MOCHA_HoldAtom(mc, argv[0].u.atom);
else if (!MOCHA_DatumToString(mc, argv[0], &atom))
goto fail;
optionData->text_value =
(PA_Block)MOCHA_strdup(mc, MOCHA_GetAtomName(mc, atom));
MOCHA_DropAtom(mc, atom);
if (!optionData->text_value)
goto fail;
}
option->decoder = decoder;
option->object = obj;
option->index = 0; /* so option->data[option->index] works */
option->indexInForm = -1;
return MOCHA_TRUE;
fail:
option_finalize(mc, obj);
obj->data = 0;
return MOCHA_FALSE;
}
static char *typenames[] = {
"none",
S_FORM_TYPE_TEXT,
S_FORM_TYPE_RADIO,
S_FORM_TYPE_CHECKBOX,
S_FORM_TYPE_HIDDEN,
S_FORM_TYPE_SUBMIT,
S_FORM_TYPE_RESET,
S_FORM_TYPE_PASSWORD,
S_FORM_TYPE_BUTTON,
S_FORM_TYPE_JOT,
"select-one",
"select-multiple",
"textarea",
"isindex",
S_FORM_TYPE_IMAGE,
S_FORM_TYPE_FILE,
"keygen",
S_FORM_TYPE_READONLY
};
/*
** Note early returns below, to avoid common string-valued property code at
** the bottom of the function.
*/
static MochaBoolean
input_get_property(MochaContext *mc, MochaObject *obj, MochaSlot slot,
MochaDatum *dp)
{
MochaInput *input;
enum input_slot input_slot;
LO_FormElementStruct *form_element;
MochaObject *option_obj;
MochaAtom *atom;
input = obj->data;
input_slot = slot;
if (input_slot == INPUT_FORM) {
/* Each input in a form has a back-pointer to its form. */
MOCHA_INIT_DATUM(mc, dp, MOCHA_OBJECT, u.obj, obj->parent);
return MOCHA_TRUE;
}
form_element = lm_GetFormElementByIndex(obj->parent, input->index);
if (!form_element)
return MOCHA_TRUE;
if (input_slot == INPUT_TYPE) {
uint type_index;
type_index = form_element->element_data->type;
if (type_index >= sizeof typenames / sizeof typenames[0]) {
MOCHA_ReportError(mc, "unknown form element type %u", type_index);
return MOCHA_FALSE;
}
atom = MOCHA_Atomize(mc, typenames[type_index]);
if (!atom) return MOCHA_FALSE;
MOCHA_INIT_DATUM(mc, dp, MOCHA_STRING, u.atom, atom);
return MOCHA_TRUE;
}
switch (form_element->element_data->type) {
case FORM_TYPE_TEXT:
case FORM_TYPE_TEXTAREA: /* XXX we ASSUME common struct prefixes */
case FORM_TYPE_FILE: /* XXX as above, also get-only, set is no-op */
case FORM_TYPE_PASSWORD:
{
lo_FormElementTextData *text;
text = &form_element->element_data->ele_text;
switch (input_slot) {
case INPUT_NAME:
/* XXX should PA_LOCK/PA_UNLOCK but why bother? */
atom = MOCHA_Atomize(mc, (char *)text->name);
break;
case INPUT_VALUE:
atom = MOCHA_Atomize(mc, (char *)text->current_text);
break;
case INPUT_DEFAULT_VALUE:
atom = MOCHA_Atomize(mc, (char *)text->default_text);
break;
default:
/* Don't mess with a user-defined property. */
return MOCHA_TRUE;
}
}
break;
case FORM_TYPE_SELECT_ONE:
case FORM_TYPE_SELECT_MULT:
{
lo_FormElementSelectData *selectData;
lo_FormElementOptionData *optionData;
int32 i;
MochaSelectOption *option;
selectData = &form_element->element_data->ele_select;
switch (input_slot) {
case INPUT_NAME:
atom = MOCHA_Atomize(mc, (char *)selectData->name);
break;
case INPUT_LENGTH:
MOCHA_INIT_DATUM(mc, dp, MOCHA_NUMBER, u.fval,
selectData->option_cnt);
return MOCHA_TRUE;
case INPUT_OPTIONS:
MOCHA_INIT_DATUM(mc, dp, MOCHA_OBJECT, u.obj,
input->input_object);
return MOCHA_TRUE;
case INPUT_SELECTED_INDEX:
MOCHA_INIT_DATUM(mc, dp, MOCHA_NUMBER, u.fval, -1);
PA_LOCK(optionData, lo_FormElementOptionData *,
selectData->options);
for (i = 0; i < selectData->option_cnt; i++) {
if (optionData[i].selected) {
dp->u.fval = i;
break;
}
}
PA_UNLOCK(selectData->options);
return MOCHA_TRUE;
default:
if ((uint32)slot >= (uint32)selectData->option_cnt)
return MOCHA_TRUE;
if (dp->tag == MOCHA_OBJECT && dp->u.obj) {
XP_ASSERT(dp->u.obj->clazz == &option_class);
return MOCHA_TRUE;
}
option = MOCHA_malloc(mc, sizeof *option);
if (!option)
return MOCHA_FALSE;
option_obj =
MOCHA_NewObject(mc, &option_class, option,
input->input_decoder->option_prototype, obj,
0, 0);
if (!option_obj) {
MOCHA_free(mc, option);
return MOCHA_FALSE;
}
LM_LINK_OBJECT(input->input_decoder, option_obj, "anOption");
option->decoder = input->input_decoder;
option->object = option_obj;
option->index = (uint32)slot;
option->indexInForm = form_element->element_index;
option->data = 0;
MOCHA_INIT_DATUM(mc, dp, MOCHA_OBJECT, u.obj, option_obj);
return MOCHA_TRUE;
}
}
break;
case FORM_TYPE_RADIO:
case FORM_TYPE_CHECKBOX:
{
lo_FormElementToggleData *toggle;
toggle = &form_element->element_data->ele_toggle;
switch (input_slot) {
case INPUT_NAME:
atom = MOCHA_Atomize(mc, (char *)toggle->name);
break;
case INPUT_VALUE:
atom = MOCHA_Atomize(mc, (char *)toggle->value);
break;
case INPUT_STATUS:
MOCHA_INIT_DATUM(mc, dp, MOCHA_BOOLEAN, u.bval, toggle->toggled);
return MOCHA_TRUE;
case INPUT_DEFAULT_STATUS:
MOCHA_INIT_DATUM(mc, dp, MOCHA_BOOLEAN, u.bval,
toggle->default_toggle);
return MOCHA_TRUE;
default:
/* Don't mess with a user-defined property. */
return MOCHA_TRUE;
}
}
break;
default:
{
lo_FormElementMinimalData *minimal;
minimal = &form_element->element_data->ele_minimal;
switch (input_slot) {
case INPUT_NAME:
atom = MOCHA_Atomize(mc, (char *)minimal->name);
break;
case INPUT_VALUE:
atom = MOCHA_Atomize(mc, (char *)minimal->value);
break;
default:
/* Don't mess with a user-defined property. */
return MOCHA_TRUE;
}
}
break;
}
if (!atom)
return MOCHA_FALSE;
MOCHA_INIT_DATUM(mc, dp, MOCHA_STRING, u.atom, atom);
if ((dp->flags & MDF_TAINTED) && dp->taint == MOCHA_TAINT_IDENTITY)
return lm_GetTaint(mc, input->input_decoder, &dp->taint);
return MOCHA_TRUE;
}
static char *
lm_fix_newlines(MochaContext *mc, const char *value)
{
size_t size;
const char *cp;
char *tp, *new_value;
#if defined XP_PC
size = 1;
for (cp = value; *cp != '\0'; cp++) {
switch (*cp) {
case '\r':
if (cp[1] != '\n')
size++;
break;
case '\n':
if (cp > value && cp[-1] != '\r')
size++;
break;
}
}
size += cp - value;
#else
size = XP_STRLEN(value) + 1;
#endif
new_value = MOCHA_malloc(mc, size);
if (!new_value)
return 0;
for (cp = value, tp = new_value; *cp != '\0'; cp++) {
#if defined XP_MAC
if (*cp == '\n') {
if (cp > value && cp[-1] != '\r')
*tp++ = '\r';
} else {
*tp++ = *cp;
}
#elif defined XP_PC
switch (*cp) {
case '\r':
*tp++ = '\r';
if (cp[1] != '\n')
*tp++ = '\n';
break;
case '\n':
if (cp > value && cp[-1] != '\r')
*tp++ = '\r';
*tp++ = '\n';
break;
default:
*tp++ = *cp;
break;
}
#else /* XP_UNIX */
if (*cp == '\r') {
if (cp[1] != '\n')
*tp++ = '\n';
} else {
*tp++ = *cp;
}
#endif
}
*tp = '\0';
return new_value;
}
static MochaBoolean
input_set_property(MochaContext *mc, MochaObject *obj, MochaSlot slot,
MochaDatum *dp)
{
MochaInput *input;
enum input_slot input_slot;
const char *value;
LO_FormElementStruct *form_element;
MWContext *context;
input = obj->data;
input_slot = slot;
switch (input_slot) {
case INPUT_TYPE:
case INPUT_FORM:
case INPUT_OPTIONS:
/* These are immutable. */
break;
case INPUT_NAME:
case INPUT_VALUE:
case INPUT_DEFAULT_VALUE:
/* These are string-valued. */
if (dp->tag != MOCHA_STRING &&
!MOCHA_ConvertDatum(mc, *dp, MOCHA_STRING, dp)) {
return MOCHA_FALSE;
}
value = MOCHA_GetAtomName(mc, dp->u.atom);
break;
case INPUT_STATUS:
case INPUT_DEFAULT_STATUS:
/* These must be Booleans. */
if (dp->tag != MOCHA_BOOLEAN &&
!MOCHA_ConvertDatum(mc, *dp, MOCHA_BOOLEAN, dp)) {
return MOCHA_FALSE;
}
break;
case INPUT_LENGTH:
case INPUT_SELECTED_INDEX:
/* These should be a number. */
if (dp->tag != MOCHA_NUMBER &&
!MOCHA_ConvertDatum(mc, *dp, MOCHA_NUMBER, dp)) {
return MOCHA_FALSE;
}
break;
}
form_element = lm_GetFormElementByIndex(obj->parent, input->index);
if (!form_element)
return MOCHA_TRUE;
context = input->input_decoder->window_context;
switch (form_element->element_data->type) {
case FORM_TYPE_TEXT:
case FORM_TYPE_TEXTAREA: /* XXX we ASSUME common struct prefixes */
case FORM_TYPE_PASSWORD:
{
lo_FormElementTextData *text;
MochaBoolean ok;
text = &form_element->element_data->ele_text;
switch (input_slot) {
case INPUT_NAME:
if (!lm_SaveParamString(mc, &text->name, value))
return MOCHA_FALSE;
break;
case INPUT_VALUE:
case INPUT_DEFAULT_VALUE:
value = lm_fix_newlines(mc, value);
if (!value)
return MOCHA_FALSE;
ok = (input_slot == INPUT_VALUE)
? lm_SaveParamString(mc, &text->current_text, value)
: lm_SaveParamString(mc, &text->default_text, value);
MOCHA_free(mc, (char *)value);
if (!ok)
return MOCHA_FALSE;
if (input_slot == INPUT_VALUE && context)
FE_ChangeInputElement(context, (LO_Element *)form_element);
break;
default:
/* Don't mess with option or user-defined property. */
return MOCHA_TRUE;
}
}
break;
case FORM_TYPE_SELECT_ONE:
case FORM_TYPE_SELECT_MULT:
{
lo_FormElementSelectData *selectData;
lo_FormElementOptionData *optionData;
MochaSelectOption *option;
int32 i, old_option_cnt;
selectData = &form_element->element_data->ele_select;
switch (slot) {
case INPUT_NAME:
if (!lm_SaveParamString(mc, &selectData->name, value))
return MOCHA_FALSE;
break;
case INPUT_LENGTH:
old_option_cnt = selectData->option_cnt;
selectData->option_cnt = (int32)dp->u.fval;
if (!LO_ResizeSelectOptions(selectData)) {
selectData->option_cnt = old_option_cnt;
MOCHA_ReportOutOfMemory(mc);
return MOCHA_FALSE;
}
/* Remove truncated slots, or clear extended element data. */
if (selectData->option_cnt < old_option_cnt) {
for (i = selectData->option_cnt; i < old_option_cnt; i++)
MOCHA_RemoveSlot(mc, obj, (MochaSlot)i);
} else if (selectData->option_cnt > old_option_cnt) {
PA_LOCK(optionData, lo_FormElementOptionData *,
selectData->options);
XP_BZERO(&optionData[old_option_cnt],
(selectData->option_cnt - old_option_cnt)
* sizeof *optionData);
PA_UNLOCK(selectData->options);
}
/* Tell the FE about it. */
if (context)
FE_ChangeInputElement(context, (LO_Element *)form_element);
break;
case INPUT_OPTIONS:
break;
case INPUT_SELECTED_INDEX:
PA_LOCK(optionData, lo_FormElementOptionData *,
selectData->options);
for (i = 0; i < selectData->option_cnt; i++)
optionData[i].selected = (i == (int32)dp->u.fval);
PA_UNLOCK(selectData->options);
/* Tell the FE about it. */
if (context)
FE_ChangeInputElement(context, (LO_Element *)form_element);
break;
default:
if (slot < 0) {
/* Don't mess with a user-defined, named property. */
return MOCHA_TRUE;
}
/* The dp arg must refer to an object of the right class. */
if (dp->tag != MOCHA_OBJECT &&
!MOCHA_ConvertDatum(mc, *dp, MOCHA_OBJECT, dp)) {
return MOCHA_FALSE;
}
if (!dp->u.obj) {
int32 count, limit;
MochaDatum datum;
MochaBoolean ok = MOCHA_TRUE;
if (slot >= selectData->option_cnt)
return ok;
/* Clear the option and compress the options array. */
PA_LOCK(optionData, lo_FormElementOptionData *,
selectData->options);
count = selectData->option_cnt - (slot + 1);
if (count > 0) {
/* Copy down option data above slot. */
XP_MEMMOVE(&optionData[slot], &optionData[slot+1],
count * sizeof *optionData);
/* And do the same for Mocha object reflections. */
for (limit = slot + count; slot < limit; slot++) {
ok = MOCHA_GetSlot(mc, obj, slot + 1, &datum);
if (!ok)
break;
MOCHA_SetSlot(mc, obj, slot, datum);
/* Fix each option's index-in-select property. */
XP_ASSERT(datum.tag == MOCHA_OBJECT);
option = datum.u.obj->data;
option->index = slot;
}
if (ok)
MOCHA_RemoveSlot(mc, obj, slot);
}
PA_UNLOCK(selectData->options);
/* Shrink the select element data's options array. */
if (ok) {
selectData->option_cnt--;
ok = LO_ResizeSelectOptions(selectData);
if (!ok) {
MOCHA_ReportOutOfMemory(mc);
} else if (context) {
FE_ChangeInputElement(context,
(LO_Element *)form_element);
}
}
return ok;
}
if (dp->u.obj->clazz != &option_class) {
MOCHA_ReportError(mc, "cannot set %s.%s to incompatible %s",
obj->clazz->name, lm_options_str,
dp->u.obj->clazz->name);
return MOCHA_FALSE;
}
option = dp->u.obj->data;
XP_ASSERT(option->data ||
option->index < (uint32)selectData->option_cnt);
/* Grow the option array if necessary. */
old_option_cnt = selectData->option_cnt;
if (slot >= old_option_cnt) {
selectData->option_cnt = slot + 1;
if (!LO_ResizeSelectOptions(selectData)) {
selectData->option_cnt = old_option_cnt;
MOCHA_ReportOutOfMemory(mc);
return MOCHA_FALSE;
}
}
/* Clear any option structs in the gap, then set slot. */
PA_LOCK(optionData, lo_FormElementOptionData *,
selectData->options);
if (slot > old_option_cnt) {
XP_BZERO(&optionData[old_option_cnt],
(slot - old_option_cnt) * sizeof *optionData);
}
if (option->data) {
XP_MEMCPY(&optionData[slot], option->data,
sizeof(lo_FormElementOptionData));
} else if ((uint32)slot != option->index) {
XP_MEMCPY(&optionData[slot], &optionData[option->index],
sizeof(lo_FormElementOptionData));
}
PA_UNLOCK(selectData->options);
/* Update the option to point at its form and form element. */
dp->u.obj->parent = obj;
option->index = (uint32)slot;
option->indexInForm = form_element->element_index;
if (option->data) {
MOCHA_free(mc, option->data);
option->data = 0;
}
/* Tell the FE about it. */
if (context)
FE_ChangeInputElement(context, (LO_Element *)form_element);
break;
}
}
break;
case FORM_TYPE_RADIO:
case FORM_TYPE_CHECKBOX:
{
lo_FormElementToggleData *toggle;
toggle = &form_element->element_data->ele_toggle;
switch (input_slot) {
case INPUT_NAME:
if (!lm_SaveParamString(mc, &toggle->name, value))
return MOCHA_FALSE;
break;
case INPUT_VALUE:
if (!lm_SaveParamString(mc, &toggle->value, value))
return MOCHA_FALSE;
break;
case INPUT_STATUS:
if (dp->tag == MOCHA_BOOLEAN)
toggle->toggled = dp->u.bval;
/* Tell the FE about it (the FE keeps radio-sets consistent). */
if (context)
FE_ChangeInputElement(context, (LO_Element *)form_element);
break;
case INPUT_DEFAULT_STATUS:
if (dp->tag == MOCHA_BOOLEAN)
toggle->default_toggle = dp->u.bval;
break;
default:
/* Don't mess with a user-defined property. */
return MOCHA_TRUE;
}
}
break;
case FORM_TYPE_READONLY:
case FORM_TYPE_FILE:
/* Don't allow modification of readonly and file fields. */
break;
default:
{
lo_FormElementMinimalData *minimal;
minimal = &form_element->element_data->ele_minimal;
switch (input_slot) {
case INPUT_NAME:
if (!lm_SaveParamString(mc, &minimal->name, value))
return MOCHA_FALSE;
break;
case INPUT_VALUE:
if (!lm_SaveParamString(mc, &minimal->value, value))
return MOCHA_FALSE;
if (context)
FE_ChangeInputElement(context, (LO_Element *)form_element);
break;
default:
/* Don't mess with a user-defined property. */
return MOCHA_TRUE;
}
}
break;
}
return input_get_property(mc, obj, slot, dp);
}
static void
input_finalize(MochaContext *mc, MochaObject *obj)
{
MochaInput *input = obj->data;
LO_FormElementStruct *form_element;
if (!input) return;
form_element = lm_GetFormElementByIndex(obj->parent, input->index);
if (form_element)
form_element->mocha_object = 0;
LM_UNLINK_OBJECT(input->input_decoder, obj);
MOCHA_free(mc, input);
}
static MochaClass input_class = {
"Input",
input_get_property, input_set_property, MOCHA_ListPropStub,
MOCHA_ResolveStub, MOCHA_ConvertStub, input_finalize
};
static MochaBoolean
Input(MochaContext *mc, MochaObject *obj,
uint argc, MochaDatum *argv, MochaDatum *rval)
{
return MOCHA_TRUE;
}
static MochaBoolean
input_to_string(MochaContext *mc, MochaObject *obj,
uint argc, MochaDatum *argv, MochaDatum *rval)
{
MochaInput *input;
LO_FormElementStruct *form_element;
uint type;
char *typename, *string, *value;
size_t length;
long truelong;
MochaDatum result;
uint16 taint;
MochaAtom *atom;
if (!MOCHA_InstanceOf(mc, obj, &input_class, argv[-1].u.fun))
return MOCHA_FALSE;
input = obj->data;
form_element = lm_GetFormElementByIndex(obj->parent, input->index);
if (!form_element) {
*rval = MOCHA_empty;
return MOCHA_TRUE;
}
type = form_element->element_data->type;
if (type >= sizeof typenames / sizeof typenames[0]) {
MOCHA_ReportError(mc, "unknown form element type %u", type);
return MOCHA_FALSE;
}
typename = typenames[type];
string = PR_sprintf_append(0, "<");
switch (type) {
case FORM_TYPE_TEXT:
{
lo_FormElementTextData *text;
text = &form_element->element_data->ele_text;
string = PR_sprintf_append(string, "%s %s=\"%s\"",
PT_INPUT, PARAM_TYPE, typename);
if (text->name) {
string = PR_sprintf_append(string, " %s=\"%s\"",
PARAM_NAME, (char *)text->name);
}
if (text->default_text) {
string = PR_sprintf_append(string, " %s=\"%s\"",
PARAM_VALUE,
(char *)text->default_text);
}
if (text->size) {
truelong = text->size;
string = PR_sprintf_append(string, " %s=%ld\"",
PARAM_SIZE, truelong);
}
if (text->max_size) {
truelong = text->max_size;
string = PR_sprintf_append(string, " %s=%ld\"",
PARAM_MAXLENGTH, truelong);
}
}
break;
case FORM_TYPE_TEXTAREA: /* XXX we ASSUME common struct prefixes */
{
lo_FormElementTextareaData *textarea;
textarea = &form_element->element_data->ele_textarea;
string = PR_sprintf_append(string, PT_TEXTAREA);
if (textarea->name) {
string = PR_sprintf_append(string, " %s=\"%s\"",
PARAM_NAME, (char *)textarea->name);
}
if (textarea->default_text) {
string = PR_sprintf_append(string, " %s=\"%s\"",
PARAM_VALUE,
(char *)textarea->default_text);
}
if (textarea->rows) {
truelong = textarea->rows;
string = PR_sprintf_append(string, " %s=%ld\"",
PARAM_SIZE, truelong);
}
if (textarea->cols) {
truelong = textarea->cols;
string = PR_sprintf_append(string, " %s=%ld\"",
PARAM_SIZE, truelong);
}
if (textarea->auto_wrap) {
switch (textarea->auto_wrap) {
case TEXTAREA_WRAP_OFF:
value = "off";
break;
case TEXTAREA_WRAP_HARD:
value = "hard";
break;
case TEXTAREA_WRAP_SOFT:
value = "soft";
break;
default:
value = "unknown";
break;
}
string = PR_sprintf_append(string, " %s=\"%s\"",
PARAM_WRAP, value);
}
}
break;
case FORM_TYPE_SELECT_ONE:
case FORM_TYPE_SELECT_MULT:
{
lo_FormElementSelectData *selectData;
lo_FormElementOptionData *optionData;
int32 i;
selectData = &form_element->element_data->ele_select;
string = PR_sprintf_append(string, PT_SELECT);
if (selectData->name) {
string = PR_sprintf_append(string, " %s=\"%s\"",
PARAM_NAME,
(char *)selectData->name);
}
if (selectData->size) {
truelong = selectData->size;
string = PR_sprintf_append(string, " %s=%ld\"",
PARAM_SIZE, truelong);
}
if (selectData->multiple) {
string = PR_sprintf_append(string, " %s", PARAM_MULTIPLE);
}
string = PR_sprintf_append(string, ">\n");
PA_LOCK(optionData, lo_FormElementOptionData *,
selectData->options);
for (i = 0; i < selectData->option_cnt; i++) {
string = PR_sprintf_append(string, "<%s", PT_OPTION);
if (optionData[i].value) {
string = PR_sprintf_append(string, " %s=\"%s\"",
PARAM_VALUE,
optionData[i].value);
}
if (optionData[i].def_selected)
string = PR_sprintf_append(string, " %s", PARAM_SELECTED);
string = PR_sprintf_append(string, ">");
if (optionData[i].text_value) {
string = PR_sprintf_append(string, "%s",
optionData[i].text_value);
}
string = PR_sprintf_append(string, "\n");
}
PA_UNLOCK(selectData->options);
string = PR_sprintf_append(string, "</%s", PT_SELECT);
}
break;
case FORM_TYPE_RADIO:
case FORM_TYPE_CHECKBOX:
{
lo_FormElementToggleData *toggle;
toggle = &form_element->element_data->ele_toggle;
string = PR_sprintf_append(string, "%s %s=\"%s\"",
PT_INPUT, PARAM_TYPE, typename);
if (toggle->name) {
string = PR_sprintf_append(string, " %s=\"%s\"",
PARAM_NAME, (char *)toggle->name);
}
if (toggle->value) {
string = PR_sprintf_append(string, " %s=\"%s\"",
PARAM_VALUE, (char *)toggle->value);
}
if (toggle->default_toggle)
string = PR_sprintf_append(string, " %s", PARAM_CHECKED);
}
break;
default:
{
lo_FormElementMinimalData *minimal;
minimal = &form_element->element_data->ele_minimal;
string = PR_sprintf_append(string, "%s %s=\"%s\"",
PT_INPUT, PARAM_TYPE, typename);
if (minimal->name) {
string = PR_sprintf_append(string, " %s=\"%s\"",
PARAM_NAME, (char *)minimal->name);
}
if (minimal->value) {
string = PR_sprintf_append(string, " %s=\"%s\"",
PARAM_VALUE, (char *)minimal->value);
}
}
break;
}
#define FROB(param) { \
if (!MOCHA_LookupName(mc, input->input_object, param, &result)) { \
PR_FREEIF(string); \
return MOCHA_FALSE; \
} \
if (result.tag == MOCHA_FUNCTION) { \
if (!MOCHA_DecompileFunctionBody(mc, result.u.fun, 0, &value)) { \
PR_FREEIF(string); \
return MOCHA_FALSE; \
} \
length = strlen(value); \
if (length && value[length-1] == '\n') length--; \
string = PR_sprintf_append(string," %s='%.*s'", param, length, value);\
MOCHA_free(mc, value); \
} \
}
FROB(lm_onFocus_str);
FROB(lm_onBlur_str);
FROB(lm_onSelect_str);
FROB(lm_onChange_str);
FROB(lm_onClick_str);
FROB(lm_onScroll_str);
#undef FROB
string = PR_sprintf_append(string, ">");
if (!string) {
MOCHA_ReportOutOfMemory(mc);
return MOCHA_FALSE;
}
if (!lm_GetTaint(mc, input->input_decoder, &taint)) {
XP_FREE(string);
return MOCHA_FALSE;
}
atom = MOCHA_Atomize(mc, string);
XP_FREE(string);
if (!atom) return MOCHA_FALSE;
MOCHA_INIT_FULL_DATUM(mc, rval, MOCHA_STRING, MDF_TAINTED, taint,
u.atom, atom);
return MOCHA_TRUE;
}
static MochaBoolean
input_method(MochaContext *mc, MochaObject *obj, MochaDatum *argv,
uint32 event, void (*fefun)(MWContext *, LO_Element *))
{
MochaInput *input;
MWContext *context;
LO_FormElementStruct *form_element;
if (!MOCHA_InstanceOf(mc, obj, &input_class, argv[-1].u.fun))
return MOCHA_FALSE;
input = obj->data;
context = input->input_decoder->window_context;
if (!context)
return MOCHA_TRUE;
form_element = lm_GetFormElementByIndex(obj->parent, input->index);
if (!form_element)
return MOCHA_TRUE;
input->input_event_mask |= event;
(*fefun)(context, (LO_Element *)form_element);
input->input_event_mask &= ~event;
return MOCHA_TRUE;
}
static MochaBoolean
input_focus(MochaContext *mc, MochaObject *obj,
uint argc, MochaDatum *argv, MochaDatum *rval)
{
return input_method(mc, obj, argv, EVENT_FOCUS, FE_FocusInputElement);
}
static MochaBoolean
input_blur(MochaContext *mc, MochaObject *obj,
uint argc, MochaDatum *argv, MochaDatum *rval)
{
return input_method(mc, obj, argv, EVENT_BLUR, FE_BlurInputElement);
}
static MochaBoolean
input_select(MochaContext *mc, MochaObject *obj,
uint argc, MochaDatum *argv, MochaDatum *rval)
{
return input_method(mc, obj, argv, EVENT_SELECT, FE_SelectInputElement);
}
static MochaBoolean
input_click(MochaContext *mc, MochaObject *obj,
uint argc, MochaDatum *argv, MochaDatum *rval)
{
return input_method(mc, obj, argv, EVENT_CLICK, FE_ClickInputElement);
}
#ifdef NOTYET
static MochaBoolean
input_enable(MochaContext *mc, MochaObject *obj,
uint argc, MochaDatum *argv, MochaDatum *rval)
{
return input_method(mc, obj, argv, 0, FE_EnableInputElement);
}
static MochaBoolean
input_disable(MochaContext *mc, MochaObject *obj,
uint argc, MochaDatum *argv, MochaDatum *rval)
{
return input_method(mc, obj, argv, 0, FE_DisableInputElement);
}
#endif /* NOTYET */
static MochaFunctionSpec input_methods[] = {
{lm_toString_str, input_to_string, 0},
{"focus", input_focus, 0},
{"blur", input_blur, 0},
{"select", input_select, 0},
{"click", input_click, 0},
#ifdef NOTYET
{"enable", input_enable, 0},
{"disable", input_disable, 0},
#endif /* NOTYET */
{0}
};
/*
** XXX move me somewhere else...
*/
enum input_array_slot {
INPUT_ARRAY_LENGTH = -1
};
static MochaPropertySpec input_array_props[] = {
{lm_length_str, INPUT_ARRAY_LENGTH, MDF_ENUMERATE | MDF_READONLY},
{0}
};
typedef struct MochaInputArray {
MochaInputBase base;
uint length;
} MochaInputArray;
static MochaBoolean
input_array_get_property(MochaContext *mc, MochaObject *obj, MochaSlot slot,
MochaDatum *dp)
{
MochaInputArray *array = obj->data;
switch (slot) {
case INPUT_ARRAY_LENGTH:
MOCHA_INIT_DATUM(mc, dp, MOCHA_NUMBER, u.fval, array->length);
break;
}
return MOCHA_TRUE;
}
static void
input_array_finalize(MochaContext *mc, MochaObject *obj)
{
MochaInputArray *array = obj->data;
LM_UNLINK_OBJECT(array->base_decoder, obj);
MOCHA_free(mc, array);
}
static MochaClass input_array_class = {
"InputArray",
input_array_get_property, input_array_get_property, MOCHA_ListPropStub,
MOCHA_ResolveStub, MOCHA_ConvertStub, input_array_finalize
};
#ifdef NOTYET
static MochaBoolean
InputArray(MochaContext *mc, MochaObject *obj,
uint argc, MochaDatum *argv, MochaDatum *rval)
{
return MOCHA_TRUE;
}
#endif
#define ANTI_RECURSIVE_KLUDGE ((MochaObject *)1)
/*
** Reflect a bunch of different types of form elements into Mocha.
*/
MochaObject *
LM_ReflectFormElement(MWContext *context, LO_FormElementStruct *form_element)
{
MochaObject *obj, *form_obj, *prototype, *old_obj, *array_obj;
LO_FormElementData *data;
MochaDecoder *decoder;
MochaContext *mc;
int32 type;
char *name;
MochaBoolean ok;
size_t size;
MochaInput *input;
MochaInputBase *base;
MochaInputArray *array;
MochaDatum datum;
static uint recurring; /* XXX thread-unsafe */
obj = form_element->mocha_object;
if (obj) {
if (obj == ANTI_RECURSIVE_KLUDGE)
return 0;
return obj;
}
data = form_element->element_data;
if (!data)
return 0;
decoder = LM_GetMochaDecoder(context);
if (!decoder)
return 0;
form_obj = lm_GetFormObjectByID(decoder, (uint)form_element->form_id);
if (!form_obj) {
LM_PutMochaDecoder(decoder);
return 0;
}
mc = decoder->mocha_context;
prototype = decoder->input_prototype;
type = data->type;
name = (char *)data->ele_minimal.name;
switch (type) {
case FORM_TYPE_TEXT:
case FORM_TYPE_TEXTAREA:
size = sizeof(MochaTextInput);
break;
case FORM_TYPE_RADIO:
if (!recurring) {
recurring++;
ok = lm_ReflectRadioButtonArray(context, form_element->form_id,
name);
recurring--;
obj = form_element->mocha_object;
if (obj) {
LM_PutMochaDecoder(decoder);
return obj;
}
}
/* FALL THROUGH */
default:
size = sizeof(MochaInput);
break;
}
input = MOCHA_malloc(mc, size);
if (!input) {
LM_PutMochaDecoder(decoder);
return 0;
}
XP_BZERO(input, size);
input->input_decoder = decoder;
input->input_type = type;
array_obj = 0;
if (!name) {
obj = MOCHA_NewObject(mc, &input_class, input, prototype, form_obj,
0, 0);
LM_LINK_OBJECT(decoder, obj, "anInput");
} else {
form_element->mocha_object = ANTI_RECURSIVE_KLUDGE;
ok = MOCHA_LookupName(mc, form_obj, name, &datum);
form_element->mocha_object = 0;
if (!ok) {
LM_PutMochaDecoder(decoder);
return 0;
}
old_obj = (datum.tag == MOCHA_OBJECT) ? datum.u.obj : 0;
if (old_obj) {
datum.flags |= MDF_ENUMERATE;
base = old_obj->data;
if ((base->type & FORM_TYPE_ARRAY) == 0) {
/* Make an array out of the previous element and this one. */
array = MOCHA_malloc(mc, sizeof *array);
if (!array) {
MOCHA_free(mc, input);
LM_PutMochaDecoder(decoder);
return 0;
}
XP_BZERO(array, sizeof *array);
array->base_decoder = decoder;
array->base_type = base->type | FORM_TYPE_ARRAY;
/*
** Hold old_obj temporarily until we remove it from form_obj
** and add it as a property of the radio button array.
*/
old_obj = MOCHA_HoldObject(mc, old_obj);
MOCHA_UndefineName(mc, form_obj, name);
/* XXX use MOCHA_InitClass instead of this! */
array_obj = MOCHA_DefineNewObject(mc, form_obj, name,
&input_array_class, array,
0/*XXXproto*/, MDF_ENUMERATE,
input_array_props, 0);
if (!array_obj) {
MOCHA_DropObject(mc, old_obj);
MOCHA_free(mc, array);
MOCHA_free(mc, input);
LM_PutMochaDecoder(decoder);
return 0;
}
/* Insert old_obj (referred to by datum) into the array. */
LM_LINK_OBJECT(decoder, array_obj, "anInputArray");
MOCHA_SetProperty(mc, array_obj, 0, array->length, datum);
array->length++;
/* Finally, we can drop old_obj because it's in array. */
MOCHA_DropObject(mc, old_obj);
} else {
array_obj = old_obj;
array = (MochaInputArray *)base;
}
/* Clear name so we don't rebind it in lm_AddFormElement(). */
name = 0;
/* Finally, create obj and put it in the last slot in array. */
obj = MOCHA_NewObject(mc, &input_class, input, prototype, form_obj,
0, 0);
if (!obj) {
MOCHA_free(mc, input);
LM_PutMochaDecoder(decoder);
return 0;
}
LM_LINK_OBJECT(decoder, obj, name);
datum.u.obj = obj;
MOCHA_SetProperty(mc, array_obj, 0, array->length, datum);
array->length++;
} else {
obj = MOCHA_NewObject(mc, &input_class, input, prototype, form_obj,
0, 0);
LM_LINK_OBJECT(decoder, obj, name);
}
}
if (!obj) {
MOCHA_free(mc, input);
LM_PutMochaDecoder(decoder);
return 0;
}
input->input_object = obj;
input->index = form_element->element_index;
form_element->mocha_object = obj;
if (!lm_AddFormElement(mc, form_obj, obj, name, input->index)) {
/* XXX undefine name if it's non-null? */
}
LM_PutMochaDecoder(decoder);
return obj;
}
MochaBoolean
lm_InitInputClasses(MochaDecoder *decoder)
{
MochaContext *mc;
MochaObject *prototype;
mc = decoder->mocha_context;
prototype = MOCHA_InitClass(mc, decoder->window_object, &input_class,
0, Input, 0, input_props, input_methods, 0, 0);
if (!prototype)
return MOCHA_FALSE;
decoder->input_prototype = MOCHA_HoldObject(mc, prototype);
prototype = MOCHA_InitClass(mc, decoder->window_object, &option_class,
0, Option, 0, option_props, 0, 0, 0);
if (!prototype)
return MOCHA_FALSE;
decoder->option_prototype = MOCHA_HoldObject(mc, prototype);
return MOCHA_TRUE;
}
uint16
lm_GetFormElementTaint(MochaContext *mc, LO_FormElementStruct *form_element)
{
uint16 taint;
MochaObject *obj, *option_obj;
LO_FormElementData *data;
MochaPropertySpec *ps;
MochaDatum datum;
MochaSlot length, slot;
taint = MOCHA_TAINT_IDENTITY;
obj = form_element->mocha_object;
data = form_element->element_data;
if (!obj || !data)
return taint;
for (ps = input_props; ps->name; ps++) {
if ((ps->flags & MDF_TAINTED) &&
MOCHA_LookupName(mc, obj, ps->name, &datum)) {
MOCHA_MIX_TAINT(mc, taint, datum.taint);
}
}
switch (data->type) {
case FORM_TYPE_SELECT_ONE:
case FORM_TYPE_SELECT_MULT:
length = data->ele_select.option_cnt;
for (slot = 0; slot < length; slot++) {
if (!MOCHA_GetSlot(mc, obj, slot, &datum))
continue;
if (datum.tag != MOCHA_OBJECT || !(option_obj = datum.u.obj))
continue;
for (ps = option_props; ps->name; ps++) {
if ((ps->flags & MDF_TAINTED) &&
MOCHA_LookupName(mc, option_obj, ps->name, &datum)) {
MOCHA_MIX_TAINT(mc, taint, datum.taint);
}
}
}
break;
default:;
}
return taint;
}
void
lm_ResetFormElementTaint(MochaContext *mc, LO_FormElementStruct *form_element)
{
MochaObject *obj, *option_obj;
LO_FormElementData *data;
MochaPropertySpec *ps;
MochaSlot length, slot;
MochaDatum datum;
obj = form_element->mocha_object;
data = form_element->element_data;
if (!obj || !data)
return;
for (ps = input_props; ps->name; ps++) {
if (ps->flags & MDF_TAINTED)
MOCHA_SetPropertyTaint(mc, obj, ps->name, MOCHA_TAINT_IDENTITY);
}
switch (data->type) {
case FORM_TYPE_SELECT_ONE:
case FORM_TYPE_SELECT_MULT:
length = data->ele_select.option_cnt;
for (slot = 0; slot < length; slot++) {
if (!MOCHA_GetSlot(mc, obj, slot, &datum))
continue;
if (datum.tag != MOCHA_OBJECT || !(option_obj = datum.u.obj))
continue;
for (ps = option_props; ps->name; ps++) {
if (ps->flags & MDF_TAINTED) {
MOCHA_SetPropertyTaint(mc, option_obj, ps->name,
MOCHA_TAINT_IDENTITY);
}
}
}
break;
default:;
}
}
static MochaBoolean
input_event(MWContext *context, LO_Element *element, uint32 event, char *method,
MochaDatum *rval)
{
MochaContext *mc;
MochaDecoder *decoder;
MochaBoolean ok;
LO_AnchorData *anchor;
MochaObject *obj;
MochaInputHandler *handler;
*rval = MOCHA_void;
mc = context->mocha_context;
if (!mc)
return MOCHA_TRUE;
if (!element) {
decoder = LM_GetMochaDecoder(context);
if (!decoder)
return MOCHA_FALSE;
if (decoder->event_mask & event) {
ok = MOCHA_TRUE;
} else {
decoder->event_mask |= event;
ok = lm_SendEvent(context, decoder->window_object, method, rval);
decoder->event_mask &= ~event;
}
LM_PutMochaDecoder(decoder);
return ok;
}
switch (element->type) {
case LO_TEXT:
anchor = element->lo_text.anchor_href;
if (!anchor)
return MOCHA_FALSE;
obj = anchor->mocha_object;
break;
case LO_IMAGE:
anchor = element->lo_image.anchor_href;
if (!anchor)
return MOCHA_FALSE;
obj = anchor->mocha_object;
break;
case LO_FORM_ELE:
obj = element->lo_form.mocha_object;
break;
default:
return MOCHA_FALSE;
}
if (!obj)
return MOCHA_FALSE;
handler = obj->data;
if (!handler)
return MOCHA_FALSE; /* jwz -- guessing... */
if (handler->event_mask & event)
return MOCHA_FALSE;
MOCHA_HoldObject(mc, obj);
handler->event_mask |= event;
ok = lm_SendEvent(context, obj, method, rval);
handler->event_mask &= ~event;
MOCHA_DropObject(mc, obj);
return ok;
}
void
LM_SendOnFocus(MWContext *context, LO_Element *element)
{
MochaDatum rval;
(void) input_event(context, element, EVENT_FOCUS, lm_onFocus_str, &rval);
}
void
LM_SendOnBlur(MWContext *context, LO_Element *element)
{
MochaDatum rval;
(void) input_event(context, element, EVENT_BLUR, lm_onBlur_str, &rval);
}
void
LM_SendOnSelect(MWContext *context, LO_Element *element)
{
MochaDatum rval;
(void) input_event(context, element, EVENT_SELECT, lm_onSelect_str, &rval);
}
void
LM_SendOnChange(MWContext *context, LO_Element *element)
{
MochaDatum rval;
(void) input_event(context, element, EVENT_CHANGE, lm_onChange_str, &rval);
}
MochaBoolean
LM_SendOnClick(MWContext *context, LO_Element *element)
{
MochaDatum rval;
if (input_event(context, element, EVENT_CLICK, lm_onClick_str, &rval) &&
rval.tag == MOCHA_BOOLEAN && rval.u.bval == MOCHA_FALSE) {
/* User wants to cancel this click gesture. */
return MOCHA_FALSE;
}
return MOCHA_TRUE;
}
MochaBoolean
LM_SendOnClickToAnchor(MWContext *context, LO_AnchorData *anchor)
{
LO_Element element;
element.lo_text.type = LO_TEXT;
element.lo_text.anchor_href = anchor;
return LM_SendOnClick(context, &element);
}
MochaBoolean
LM_SendMouseOver(MWContext *context, LO_Element *element)
{
MochaDatum rval;
if (input_event(context, element, EVENT_MOUSEOVER, lm_onMouseOver_str,
&rval) &&
rval.tag == MOCHA_BOOLEAN && rval.u.bval == MOCHA_TRUE) {
/* User wants to manage the status bar. */
return MOCHA_TRUE;
}
return MOCHA_FALSE;
}
MochaBoolean
LM_SendMouseOverAnchor(MWContext *context, LO_AnchorData *anchor)
{
LO_Element element;
element.lo_text.type = LO_TEXT;
element.lo_text.anchor_href = anchor;
return LM_SendMouseOver(context, &element);
}
void
LM_SendMouseOut(MWContext *context, LO_Element *element)
{
MochaDatum rval;
(void) input_event(context, element, EVENT_MOUSEOUT, lm_onMouseOut_str,
&rval);
}
void
LM_SendMouseOutOfAnchor(MWContext *context, LO_AnchorData *anchor)
{
LO_Element element;
element.lo_text.type = LO_TEXT;
element.lo_text.anchor_href = anchor;
LM_SendMouseOut(context, &element);
}