pjs/lib/libmocha/lm_input.c

2576 строки
68 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 input focus and event notifiers.
*
* Brendan Eich, 9/27/95
*
* XXX SIZE, MAXLENGTH attributes
*/
#include "lm.h"
#include "xp.h"
#include "lo_ele.h"
#include "pa_tags.h"
#include "layout.h"
#include "prmem.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
#if DISABLED_READONLY_SUPPORT
INPUT_DISABLED = -11,
INPUT_READONLY = -12
#endif
};
static char lm_options_str[] = "options";
static JSPropertySpec input_props[] = {
{"type", INPUT_TYPE, JSPROP_ENUMERATE|JSPROP_READONLY},
{"name", INPUT_NAME, JSPROP_ENUMERATE},
{"form", INPUT_FORM, JSPROP_ENUMERATE|JSPROP_READONLY},
{"value", INPUT_VALUE, JSPROP_ENUMERATE},
{"defaultValue", INPUT_DEFAULT_VALUE, JSPROP_ENUMERATE},
{lm_length_str, INPUT_LENGTH, JSPROP_ENUMERATE},
{lm_options_str, INPUT_OPTIONS, JSPROP_ENUMERATE|JSPROP_READONLY},
{"selectedIndex", INPUT_SELECTED_INDEX, JSPROP_ENUMERATE},
{"status", INPUT_STATUS, 0},
{"defaultStatus", INPUT_DEFAULT_STATUS, 0},
{PARAM_CHECKED, INPUT_STATUS, JSPROP_ENUMERATE},
{"defaultChecked", INPUT_DEFAULT_STATUS, JSPROP_ENUMERATE},
#if DISABLED_READONLY_SUPPORT
{"disabled", INPUT_DISABLED, JSPROP_ENUMERATE},
{"readonly", INPUT_READONLY, JSPROP_ENUMERATE},
#endif
{0}
};
/*
* Base input element type.
*/
typedef struct JSInput {
JSInputHandler handler;
int32 index;
} JSInput;
#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 JSTextInput {
JSInput input;
} JSTextInput;
/*
* 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 JSPropertySpec option_props[] = {
{"index", OPTION_INDEX, JSPROP_ENUMERATE|JSPROP_READONLY},
{"text", OPTION_TEXT, JSPROP_ENUMERATE},
{"value", OPTION_VALUE, JSPROP_ENUMERATE},
{"defaultSelected", OPTION_DEFAULT_SELECTED, JSPROP_ENUMERATE},
{"selected", OPTION_SELECTED, JSPROP_ENUMERATE},
{0}
};
typedef struct JSSelectOption {
MochaDecoder *decoder;
JSObject *object;
uint32 index;
int32 indexInForm;
lo_FormElementOptionData *data;
} JSSelectOption;
extern JSClass lm_option_class;
PR_STATIC_CALLBACK(JSBool)
option_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSSelectOption *option;
lo_FormElementOptionData *optionData;
lo_FormElementSelectData *selectData;
LO_FormElementStruct *form_element;
enum option_slot option_slot;
JSString *str;
char *value;
jsint slot;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
option = JS_GetInstancePrivate(cx, obj, &lm_option_class, NULL);
if (!option)
return JS_TRUE;
LO_LockLayout();
optionData = option->data;
if (optionData) {
selectData = 0;
form_element = 0;
} else {
JSObject * parent = JS_GetParent(cx, obj);
if (!parent)
goto good;
form_element = lm_GetFormElementByIndex(cx, JS_GetParent(cx, parent),
option->indexInForm);
if (!form_element)
goto good;
selectData = &form_element->element_data->ele_select;
}
option_slot = slot;
switch (option_slot) {
case OPTION_INDEX:
*vp = INT_TO_JSVAL(option->index);
break;
case OPTION_TEXT:
case OPTION_VALUE:
if (selectData)
optionData = (lo_FormElementOptionData *) selectData->options;
if (slot == OPTION_TEXT)
value = (char *)optionData[option->index].text_value;
else
value = (char *)optionData[option->index].value;
str = lm_LocalEncodingToStr(option->decoder->window_context,
value);
if (!str)
goto bad;
*vp = STRING_TO_JSVAL(str);
break;
case OPTION_DEFAULT_SELECTED:
case OPTION_SELECTED:
if (selectData)
optionData = (lo_FormElementOptionData *) selectData->options;
*vp = BOOLEAN_TO_JSVAL((option_slot == OPTION_DEFAULT_SELECTED)
? optionData[option->index].def_selected
: optionData[option->index].selected);
break;
default:
/* Don't mess with a user-defined or method property. */
break;
}
good:
LO_UnlockLayout();
return JS_TRUE;
bad:
LO_UnlockLayout();
return JS_FALSE;
}
PR_STATIC_CALLBACK(JSBool)
option_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSSelectOption *option;
lo_FormElementOptionData *optionData;
lo_FormElementSelectData *selectData;
LO_FormElementStruct *form_element;
enum option_slot option_slot;
JSBool showChange;
int32 i;
jsint slot;
char * value = NULL;
MWContext * context;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
option = JS_GetInstancePrivate(cx, obj, &lm_option_class, NULL);
if (!option)
return JS_TRUE;
context = option->decoder->window_context;
optionData = option->data;
LO_LockLayout();
if (optionData) {
selectData = 0;
form_element = 0;
} else {
JSObject * parent = JS_GetParent(cx, obj);
if (!parent)
goto good;
form_element = lm_GetFormElementByIndex(cx, JS_GetParent(cx, parent),
option->indexInForm);
if (!form_element)
goto good;
selectData = &form_element->element_data->ele_select;
}
if (selectData && option->index >= (uint32) selectData->option_cnt)
goto good;
option_slot = slot;
showChange = JS_FALSE;
switch (option_slot) {
case OPTION_TEXT:
case OPTION_VALUE:
if (!JSVAL_IS_STRING(*vp) &&
!JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) {
goto bad;
}
if (selectData)
optionData = (lo_FormElementOptionData *) selectData->options;
value = lm_StrToLocalEncoding(context, JSVAL_TO_STRING(*vp));
if (!value)
goto bad;
if (option_slot == OPTION_TEXT) {
if (!lm_SaveParamString(cx, &optionData[option->index].text_value,
value)) {
goto bad;
}
showChange = JS_TRUE;
} else {
if (!lm_SaveParamString(cx, &optionData[option->index].value,
value)) {
goto bad;
}
}
XP_FREE(value);
break;
case OPTION_DEFAULT_SELECTED:
case OPTION_SELECTED:
if (!JSVAL_IS_BOOLEAN(*vp) &&
!JS_ConvertValue(cx, *vp, JSTYPE_BOOLEAN, vp)) {
goto bad;
}
if (selectData)
optionData = (lo_FormElementOptionData *) selectData->options;
if (option_slot == OPTION_DEFAULT_SELECTED)
optionData[option->index].def_selected = JSVAL_TO_BOOLEAN(*vp);
else
optionData[option->index].selected = JSVAL_TO_BOOLEAN(*vp);
if (selectData) {
if (JSVAL_TO_BOOLEAN(*vp) && !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;
}
}
}
if (option_slot == OPTION_SELECTED)
showChange = JS_TRUE;
break;
default:
/* Don't mess with a user-defined property. */
goto good;
}
if (showChange && context && form_element) {
ET_PostManipulateForm(context,
(LO_Element *)form_element,
EVENT_CHANGE);
}
good:
LO_UnlockLayout();
return JS_TRUE;
bad:
XP_FREEIF(value);
LO_UnlockLayout();
return JS_FALSE;
}
PR_STATIC_CALLBACK(void)
option_finalize(JSContext *cx, JSObject *obj)
{
JSSelectOption *option;
lo_FormElementOptionData *optionData;
option = JS_GetPrivate(cx, obj);
if (!option)
return;
optionData = option->data;
if (optionData) {
if (optionData->text_value)
JS_free(cx, optionData->text_value);
if (optionData->value)
JS_free(cx, optionData->value);
JS_free(cx, optionData);
}
DROP_BACK_COUNT(option->decoder);
JS_free(cx, option);
}
JSClass lm_option_class = {
"Option", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, option_getProperty, option_setProperty,
JS_EnumerateStub, JS_ResolveStub, JS_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.
*/
PR_STATIC_CALLBACK(JSBool)
Option(JSContext *cx, JSObject *obj, uint argc, jsval *argv, jsval *rval)
{
MochaDecoder *decoder;
JSSelectOption *option;
lo_FormElementOptionData *optionData;
JSString *str;
JSBool bval;
MWContext *context;
XP_ASSERT(JS_InstanceOf(cx, obj, &lm_option_class, NULL));
decoder = JS_GetPrivate(cx, JS_GetGlobalObject(cx));
context = decoder->window_context;
option = JS_malloc(cx, sizeof *option);
if (!option)
return JS_TRUE;
XP_BZERO(option, sizeof *option);
if (!JS_SetPrivate(cx, obj, option)) {
JS_free(cx, option);
return JS_FALSE;
}
optionData = JS_malloc(cx, sizeof *optionData);
if (!optionData)
return JS_FALSE;
XP_BZERO(optionData, sizeof *optionData);
option->data = optionData;
if (argc >= 4) {
if (!JSVAL_IS_BOOLEAN(argv[3]) &&
!JS_ValueToBoolean(cx, argv[3], &bval)) {
return JS_FALSE;
}
optionData->selected = bval;
}
if (argc >= 3) {
if (!JSVAL_IS_BOOLEAN(argv[2]) &&
!JS_ValueToBoolean(cx, argv[2], &bval)) {
return JS_FALSE;
}
optionData->def_selected = bval;
}
if (argc >= 2) {
if (JSVAL_IS_STRING(argv[1]))
str = JSVAL_TO_STRING(argv[1]);
else if (!(str = JS_ValueToString(cx, argv[1])))
return JS_FALSE;
optionData->value =
(PA_Block)lm_StrToLocalEncoding(context, str);
if (!optionData->value)
return JS_FALSE;
}
if (argc >= 1) {
if (JSVAL_IS_STRING(argv[0]))
str = JSVAL_TO_STRING(argv[0]);
else if (!(str = JS_ValueToString(cx, argv[0])))
return JS_FALSE;
optionData->text_value =
(PA_Block)lm_StrToLocalEncoding(context, str);
if (!optionData->text_value)
return JS_FALSE;
}
option->decoder = HOLD_BACK_COUNT(decoder);
option->object = obj;
option->index = 0; /* so option->data[option->index] works */
option->indexInForm = -1;
return JS_TRUE;
}
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
};
extern JSClass lm_input_class;
/*
* Note early returns below, to avoid common string-valued property code at
* the bottom of the function.
*/
PR_STATIC_CALLBACK(JSBool)
input_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSInput *input;
MWContext *context;
enum input_slot input_slot;
LO_FormElementStruct *form_element;
JSObject *option_obj;
JSString *str;
jsint slot;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
input = JS_GetInstancePrivate(cx, obj, &lm_input_class, NULL);
if (!input)
return JS_TRUE;
input_slot = slot;
if (input_slot == INPUT_FORM) {
/* Each input in a form has a back-pointer to its form. */
*vp = OBJECT_TO_JSVAL(JS_GetParent(cx, obj));
return JS_TRUE;
}
LO_LockLayout();
form_element = lm_GetFormElementByIndex(cx, JS_GetParent(cx, obj),
input->index);
if (!form_element)
goto good;
if (input_slot == INPUT_TYPE) {
uint type_index;
type_index = form_element->element_data->type;
if (type_index >= sizeof typenames / sizeof typenames[0]) {
JS_ReportError(cx, "unknown form element type %u", type_index);
goto bad;
}
str = JS_NewStringCopyZ(cx, typenames[type_index]);
if (!str)
goto bad;
*vp = STRING_TO_JSVAL(str);
goto good;
}
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_FILE: /* XXX as above, also get-only without signing */
case FORM_TYPE_PASSWORD:
#ifdef ENDER
case FORM_TYPE_HTMLAREA :
#endif /*ENDER*/
{
lo_FormElementTextData *text;
text = &form_element->element_data->ele_text;
switch (input_slot) {
case INPUT_NAME:
str = lm_LocalEncodingToStr(context,
(char *)text->name);
break;
case INPUT_VALUE:
str = lm_LocalEncodingToStr(context,
(char *)text->current_text);
break;
case INPUT_DEFAULT_VALUE:
str = lm_LocalEncodingToStr(context,
(char *)text->default_text);
break;
#if DISABLED_READONLY_SUPPORT
case INPUT_DISABLED:
*vp = BOOLEAN_TO_JSVAL(text->disabled);
goto good;
case INPUT_READONLY:
*vp = BOOLEAN_TO_JSVAL(text->read_only);
goto good;
#endif
default:
/* Don't mess with a user-defined property. */
goto good;
}
}
break;
case FORM_TYPE_SELECT_ONE:
case FORM_TYPE_SELECT_MULT:
{
lo_FormElementSelectData *selectData;
lo_FormElementOptionData *optionData;
int32 i;
JSSelectOption *option;
selectData = &form_element->element_data->ele_select;
switch (input_slot) {
case INPUT_NAME:
str = lm_LocalEncodingToStr(context,
(char *)selectData->name);
break;
case INPUT_LENGTH:
*vp = INT_TO_JSVAL(selectData->option_cnt);
goto good;
case INPUT_OPTIONS:
*vp = OBJECT_TO_JSVAL(input->input_object);
goto good;
case INPUT_SELECTED_INDEX:
*vp = INT_TO_JSVAL(-1);
optionData = (lo_FormElementOptionData *) selectData->options;
for (i = 0; i < selectData->option_cnt; i++) {
if (optionData[i].selected) {
*vp = INT_TO_JSVAL(i);
break;
}
}
goto good;
#if DISABLED_READONLY_SUPPORT
case INPUT_DISABLED:
*vp = BOOLEAN_TO_JSVAL(selectData->disabled);
goto good;
case INPUT_READONLY:
*vp = BOOLEAN_TO_JSVAL(FALSE);
goto good;
#endif
default:
if ((uint32)slot >= (uint32)selectData->option_cnt) {
*vp = JSVAL_NULL;
goto good;
}
if (JSVAL_IS_OBJECT(*vp) && JSVAL_TO_OBJECT(*vp)) {
XP_ASSERT(JS_InstanceOf(cx, JSVAL_TO_OBJECT(*vp),
&lm_option_class, NULL));
goto good;
}
option = JS_malloc(cx, sizeof *option);
if (!option)
goto bad;
option_obj =
JS_NewObject(cx, &lm_option_class,
input->input_decoder->option_prototype, obj);
if (!option_obj || !JS_SetPrivate(cx, option_obj, option)) {
JS_free(cx, option);
goto bad;
}
option->decoder = HOLD_BACK_COUNT(input->input_decoder);
option->object = option_obj;
option->index = (uint32)slot;
option->indexInForm = form_element->element_index;
option->data = NULL;
*vp = OBJECT_TO_JSVAL(option_obj);
goto good;
}
}
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:
str = lm_LocalEncodingToStr(context,
(char *)toggle->name);
break;
case INPUT_VALUE:
str = lm_LocalEncodingToStr(context,
(char *)toggle->value);
break;
case INPUT_STATUS:
*vp = BOOLEAN_TO_JSVAL(toggle->toggled);
goto good;
case INPUT_DEFAULT_STATUS:
*vp = BOOLEAN_TO_JSVAL(toggle->default_toggle);
goto good;
#if DISABLED_READONLY_SUPPORT
case INPUT_DISABLED:
*vp = BOOLEAN_TO_JSVAL(toggle->disabled);
goto good;
case INPUT_READONLY:
*vp = BOOLEAN_TO_JSVAL(FALSE);
goto good;
#endif
default:
/* Don't mess with a user-defined property. */
goto good;
}
}
break;
default:
{
lo_FormElementMinimalData *minimal;
minimal = &form_element->element_data->ele_minimal;
switch (input_slot) {
case INPUT_NAME:
str = lm_LocalEncodingToStr(context,
(char *)minimal->name);
break;
case INPUT_VALUE:
str = lm_LocalEncodingToStr(context,
(char *)minimal->value);
break;
#if DISABLED_READONLY_SUPPORT
case INPUT_DISABLED:
*vp = BOOLEAN_TO_JSVAL(minimal->disabled);
goto good;
case INPUT_READONLY:
*vp = BOOLEAN_TO_JSVAL(FALSE); /* minimal elements don't have the readonly attribute. */
goto good;
#endif
default:
/* Don't mess with a user-defined property. */
goto good;
}
}
break;
}
if (!str)
goto bad;
*vp = STRING_TO_JSVAL(str);
good:
LO_UnlockLayout();
return JS_TRUE;
bad:
LO_UnlockLayout();
return JS_FALSE;
}
char *
lm_FixNewlines(JSContext *cx, const char *value, JSBool formElement)
{
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 = JS_malloc(cx, size);
if (!new_value)
return NULL;
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' && formElement)
*tp++ = '\n';
break;
case '\n':
if (cp > value && cp[-1] != '\r' && formElement)
*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;
}
PR_STATIC_CALLBACK(JSBool)
input_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSInput *input;
enum input_slot input_slot;
const char *prop_name;
char *value = NULL;
LO_FormElementStruct *form_element;
MochaDecoder *decoder;
MWContext *context;
int32 intval;
jsint slot;
input = JS_GetInstancePrivate(cx, obj, &lm_input_class, NULL);
if (!input)
return JS_TRUE;
/* If the property is seting a key handler we find out now so
* that we can tell the front end to send the event. */
if (JSVAL_IS_STRING(id)) {
prop_name = JS_GetStringBytes(JSVAL_TO_STRING(id));
/* XXX use lm_onKeyDown_str etc. initialized by PARAM_ONKEYDOWN */
if (XP_STRCASECMP(prop_name, "onkeydown") == 0 ||
XP_STRCASECMP(prop_name, "onkeyup") == 0 ||
XP_STRCASECMP(prop_name, "onkeypress") == 0) {
form_element = lm_GetFormElementByIndex(cx, JS_GetParent(cx, obj),
input->index);
form_element->event_handler_present = TRUE;
}
return JS_TRUE;
}
XP_ASSERT(JSVAL_IS_INT(id));
slot = JSVAL_TO_INT(id);
decoder = input->input_decoder;
context = decoder->window_context;
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 (!JSVAL_IS_STRING(*vp) &&
!JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) {
return JS_FALSE;
}
value = lm_StrToLocalEncoding(context, JSVAL_TO_STRING(*vp));
break;
case INPUT_STATUS:
case INPUT_DEFAULT_STATUS:
#if DISABLED_READONLY_SUPPORT
case INPUT_READONLY:
case INPUT_DISABLED:
#endif
/* These must be Booleans. */
if (!JSVAL_IS_BOOLEAN(*vp) &&
!JS_ConvertValue(cx, *vp, JSTYPE_BOOLEAN, vp)) {
return JS_FALSE;
}
break;
case INPUT_LENGTH:
case INPUT_SELECTED_INDEX:
/* These should be integers. */
if (JSVAL_IS_INT(*vp))
intval = JSVAL_TO_INT(*vp);
else if (!JS_ValueToInt32(cx, *vp, &intval)) {
return JS_FALSE;
}
break;
}
LO_LockLayout();
form_element = lm_GetFormElementByIndex(cx, JS_GetParent(cx, obj),
input->index);
if (!form_element)
goto good;
switch (form_element->element_data->type) {
case FORM_TYPE_FILE:
/* if we try to set a file upload widget we better be a signed script */
if (!lm_CanAccessTarget(cx, JSTARGET_UNIVERSAL_FILE_READ))
break;
/* else fall through... */
case FORM_TYPE_TEXT:
case FORM_TYPE_TEXTAREA: /* XXX we ASSUME common struct prefixes */
case FORM_TYPE_PASSWORD:
#ifdef ENDER
case FORM_TYPE_HTMLAREA :
#endif /*ENDER*/
{
lo_FormElementTextData *text;
JSBool ok;
char * fixed_string;
text = &form_element->element_data->ele_text;
switch (input_slot) {
case INPUT_NAME:
if (!lm_SaveParamString(cx, &text->name, value))
goto bad;
break;
case INPUT_VALUE:
case INPUT_DEFAULT_VALUE:
fixed_string = lm_FixNewlines(cx, value, JS_TRUE);
if (!fixed_string)
goto bad;
ok = (input_slot == INPUT_VALUE)
? lm_SaveParamString(cx, &text->current_text, fixed_string)
: lm_SaveParamString(cx, &text->default_text, fixed_string);
JS_free(cx, (char *)fixed_string);
if (!ok)
goto bad;
if (input_slot == INPUT_VALUE && context) {
ET_PostManipulateForm(context, (LO_Element *)form_element,
EVENT_CHANGE);
}
break;
#if DISABLED_READONLY_SUPPORT
case INPUT_DISABLED:
text->disabled = JSVAL_TO_BOOLEAN(*vp);
if (context) {
ET_PostManipulateForm(context, (LO_Element *)form_element,
EVENT_CHANGE);
}
break;
case INPUT_READONLY:
if (form_element->element_data->type == FORM_TYPE_FILE)
break;
text->read_only = JSVAL_TO_BOOLEAN(*vp);
if (context) {
ET_PostManipulateForm(context, (LO_Element *)form_element,
EVENT_CHANGE);
}
break;
#endif
default:
/* Don't mess with option or user-defined property. */
goto good;
}
}
break;
case FORM_TYPE_SELECT_ONE:
case FORM_TYPE_SELECT_MULT:
{
lo_FormElementSelectData *selectData;
lo_FormElementOptionData *optionData;
JSSelectOption *option;
int32 i, new_option_cnt, old_option_cnt;
selectData = &form_element->element_data->ele_select;
switch (slot) {
case INPUT_NAME:
if (!lm_SaveParamString(cx, &selectData->name, value))
goto bad;
break;
case INPUT_LENGTH:
new_option_cnt = intval;
old_option_cnt = selectData->option_cnt;
optionData = (lo_FormElementOptionData *) selectData->options;
/* Remove truncated slots, or clear extended element data. */
if (new_option_cnt < old_option_cnt) {
/*
* Make truncated options stand alone in case someone else
* in case someone else has a reference to one.
*/
for (i = new_option_cnt; i < old_option_cnt; i++) {
jsval oval;
JSObject * option_obj;
if (!JS_LookupElement(cx, obj, i, &oval))
goto bad;
if (JSVAL_IS_OBJECT(oval) &&
(option_obj = JSVAL_TO_OBJECT(oval))) {
lo_FormElementOptionData *myData;
myData =
JS_malloc(cx, sizeof(lo_FormElementOptionData));
if (!myData)
goto bad;
XP_MEMCPY(myData, &optionData[i],
sizeof(lo_FormElementOptionData));
option = JS_GetPrivate(cx, option_obj);
option->data = myData;
}
JS_DeleteElement(cx, obj, i);
}
}
/* Get layout to reallocate the options array. */
selectData->option_cnt = new_option_cnt;
if (!LO_ResizeSelectOptions(selectData)) {
selectData->option_cnt = old_option_cnt;
JS_ReportOutOfMemory(cx);
goto bad;
}
/* Handle the grow case by clearing the new options. */
if (new_option_cnt > old_option_cnt) {
XP_BZERO(&optionData[old_option_cnt],
(new_option_cnt - old_option_cnt)
* sizeof *optionData);
}
/* Tell the FE about it. */
if (context) {
ET_PostManipulateForm(context, (LO_Element *)form_element,
EVENT_CHANGE);
}
break;
case INPUT_OPTIONS:
break;
case INPUT_SELECTED_INDEX:
optionData = (lo_FormElementOptionData *)
selectData->options;
for (i = 0; i < selectData->option_cnt; i++)
optionData[i].selected = (i == intval);
/* Tell the FE about it. */
if (context)
ET_PostManipulateForm(context, (LO_Element *)form_element,
EVENT_CHANGE);
break;
#if DISABLED_READONLY_SUPPORT
case INPUT_DISABLED:
selectData->disabled = JSVAL_TO_BOOLEAN(*vp);
if (context) {
ET_PostManipulateForm(context, (LO_Element *)form_element,
EVENT_CHANGE);
}
break;
case INPUT_READONLY:
/* silenty ignore updates to the READONLY attribute. */
break;
#endif
default:
if (slot < 0) {
/* Don't mess with a user-defined, named property. */
goto good;
}
/* The vp arg must refer to an object of the right class. */
if (!JSVAL_IS_OBJECT(*vp) &&
!JS_ConvertValue(cx, *vp, JSTYPE_OBJECT, vp)) {
goto bad;
}
if (JSVAL_IS_NULL(*vp)) {
int32 count, limit;
JSBool ok = JS_TRUE;
if (slot >= selectData->option_cnt)
goto good;
/* Clear the option and compress the options array. */
optionData = (lo_FormElementOptionData *)
selectData->options;
count = selectData->option_cnt - (slot + 1);
if (count > 0) {
/*
* Move down the options that were after the option
* we are deleting. Note, the JS_GetElement()
* and SetElement() calls will make sure the
* layout-based data gets copied too.
*/
for (limit = slot + count; slot < limit; slot++) {
jsval v;
ok = JS_GetElement(cx, obj, slot + 1, &v);
if (!ok)
break;
JS_SetElement(cx, obj, slot, &v);
/* Fix each option's index-in-select property. */
XP_ASSERT(JSVAL_IS_OBJECT(v));
option = JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
option->index = slot;
}
if (ok)
JS_DeleteElement(cx, obj, slot);
}
/* Shrink the select element data's options array. */
if (ok) {
selectData->option_cnt--;
ok = (JSBool)LO_ResizeSelectOptions(selectData);
if (!ok) {
JS_ReportOutOfMemory(cx);
} else if (context) {
ET_PostManipulateForm(context,
(LO_Element *)form_element,
EVENT_CHANGE);
}
}
LO_UnlockLayout();
return ok;
}
if (!JS_InstanceOf(cx, JSVAL_TO_OBJECT(*vp), &lm_option_class,
NULL)) {
JS_ReportError(cx, "cannot set %s.%s to incompatible %s",
JS_GetClass(obj)->name, lm_options_str,
JS_GetClass(JSVAL_TO_OBJECT(*vp))->name);
goto bad;
}
option = JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp));
if (!option)
goto good;
if (!option->data &&
JS_GetParent(cx, option->object) != obj) {
JS_ReportError(cx, "can't share options between select elements");
goto bad;
}
/* 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;
JS_ReportOutOfMemory(cx);
goto bad;
}
}
/* Clear any option structs in the gap, then set slot. */
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));
}
/* Update the option to point at its form and form element. */
JS_SetParent(cx, JSVAL_TO_OBJECT(*vp), obj);
option->index = (uint32)slot;
option->indexInForm = form_element->element_index;
if (option->data) {
JS_free(cx, option->data);
option->data = NULL;
}
/* Tell the FE about it. */
if (context)
ET_PostManipulateForm(context, (LO_Element *)form_element,
EVENT_CHANGE);
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(cx, &toggle->name, value))
goto bad;
break;
case INPUT_VALUE:
if (!lm_SaveParamString(cx, &toggle->value, value))
goto bad;
break;
case INPUT_STATUS:
if (JSVAL_IS_BOOLEAN(*vp))
toggle->toggled = JSVAL_TO_BOOLEAN(*vp);
/* Tell the FE about it (the FE keeps radio-sets consistent). */
if (context)
ET_PostManipulateForm(context, (LO_Element *)form_element,
EVENT_CHANGE);
break;
case INPUT_DEFAULT_STATUS:
if (JSVAL_IS_BOOLEAN(*vp))
toggle->default_toggle = JSVAL_TO_BOOLEAN(*vp);
break;
#if DISABLED_READONLY_SUPPORT
case INPUT_DISABLED:
toggle->disabled = JSVAL_TO_BOOLEAN(*vp);
if (context) {
ET_PostManipulateForm(context, (LO_Element *)form_element,
EVENT_CHANGE);
}
break;
case INPUT_READONLY:
/* silenty ignore updates to the READONLY attribute. */
break;
#endif
default:
/* Don't mess with a user-defined property. */
goto good;
}
}
break;
case FORM_TYPE_READONLY:
/* Don't allow modification of readonly fields. */
break;
default:
{
lo_FormElementMinimalData *minimal;
minimal = &form_element->element_data->ele_minimal;
switch (input_slot) {
case INPUT_NAME:
if (!lm_SaveParamString(cx, &minimal->name, value))
goto bad;
break;
case INPUT_VALUE:
if (!lm_SaveParamString(cx, &minimal->value, value))
goto bad;
if (context) {
ET_PostManipulateForm(context, (LO_Element *)form_element,
EVENT_CHANGE);
}
break;
#if DISABLED_READONLY_SUPPORT
case INPUT_DISABLED:
minimal->disabled = JSVAL_TO_BOOLEAN(*vp);
if (context) {
ET_PostManipulateForm(context, (LO_Element *)form_element,
EVENT_CHANGE);
}
break;
case INPUT_READONLY:
/* silenty ignore updates to the READONLY attribute. */
break;
#endif
default:
/* Don't mess with a user-defined property. */
goto good;
}
}
break;
}
good:
XP_FREEIF(value);
LO_UnlockLayout();
return JS_TRUE;
bad:
XP_FREEIF(value);
LO_UnlockLayout();
return JS_FALSE;
}
PR_STATIC_CALLBACK(void)
input_finalize(JSContext *cx, JSObject *obj)
{
JSInput *input;
LO_FormElementStruct *form_element;
input = JS_GetPrivate(cx, obj);
if (!input)
return;
LO_LockLayout();
form_element = lm_GetFormElementByIndex(cx, JS_GetParent(cx, obj),
input->index);
if (form_element && form_element->mocha_object == obj)
form_element->mocha_object = NULL;
LO_UnlockLayout();
DROP_BACK_COUNT(input->input_decoder);
JS_free(cx, input);
}
JSClass lm_input_class = {
"Input", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, input_getProperty, input_setProperty,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, input_finalize
};
PR_STATIC_CALLBACK(JSBool)
Input(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
input_toString(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
JSInput *input;
LO_FormElementStruct *form_element;
uint type;
char *typename, *string, *value;
size_t length;
long truelong;
jsval result;
JSString *str;
if (!JS_InstanceOf(cx, obj, &lm_input_class, argv))
return JS_FALSE;
input = JS_GetPrivate(cx, obj);
if (!input)
return JS_TRUE;
LO_LockLayout();
form_element = lm_GetFormElementByIndex(cx, JS_GetParent(cx, obj),
input->index);
if (!form_element) {
*rval = JS_GetEmptyStringValue(cx);
goto bad;
}
type = form_element->element_data->type;
if (type >= sizeof typenames / sizeof typenames[0]) {
JS_ReportError(cx, "unknown form element type %u", type);
goto bad;
}
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 */
#ifdef ENDER
case FORM_TYPE_HTMLAREA :
#endif /*ENDER*/
{
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 (!JS_LookupProperty(cx, input->input_object, param, &result)) { \
PR_FREEIF(string); \
return JS_FALSE; \
} \
if (JS_TypeOfValue(cx, result) == JSTYPE_FUNCTION) { \
JSFunction *fun = JS_ValueToFunction(cx, result); \
if (!fun) { \
PR_FREEIF(string); \
return JS_FALSE; \
} \
str = JS_DecompileFunctionBody(cx, fun, 0); \
value = JS_GetStringBytes(str); \
length = strlen(value); \
if (length && value[length-1] == '\n') length--; \
string = PR_sprintf_append(string," %s='%.*s'", param, length, 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
LO_UnlockLayout();
string = PR_sprintf_append(string, ">");
if (!string) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
str = lm_LocalEncodingToStr(input->input_decoder->window_context,
string);
XP_FREE(string);
if (!str)
return JS_FALSE;
*rval = STRING_TO_JSVAL(str);
return JS_TRUE;
bad:
LO_UnlockLayout();
return JS_FALSE;
}
static JSBool
input_method(JSContext *cx, JSObject *obj, jsval *argv,
uint32 event)
{
JSInput *input;
MWContext *context;
LO_FormElementStruct *form_element;
if (!JS_InstanceOf(cx, obj, &lm_input_class, argv))
return JS_FALSE;
input = JS_GetPrivate(cx, obj);
if (!input)
return JS_TRUE;
context = input->input_decoder->window_context;
if (!context)
return JS_TRUE;
LO_LockLayout();
form_element = lm_GetFormElementByIndex(cx, JS_GetParent(cx, obj),
input->index);
if (!form_element) {
LO_UnlockLayout();
return JS_TRUE;
}
input->input_event_mask |= event;
ET_PostManipulateForm(context, (LO_Element *)form_element, event);
input->input_event_mask &= ~event;
LO_UnlockLayout();
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
input_focus(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return input_method(cx, obj, argv, EVENT_FOCUS);
}
PR_STATIC_CALLBACK(JSBool)
input_blur(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return input_method(cx, obj, argv, EVENT_BLUR);
}
PR_STATIC_CALLBACK(JSBool)
input_select(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return input_method(cx, obj, argv, EVENT_SELECT);
}
PR_STATIC_CALLBACK(JSBool)
input_click(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return input_method(cx, obj, argv, EVENT_CLICK);
}
#ifdef NOTYET
PR_STATIC_CALLBACK(JSBool)
input_enable(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return input_method(cx, obj, argv, EVENT_ENABLE);
}
PR_STATIC_CALLBACK(JSBool)
input_disable(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return input_method(cx, obj, argv, EVENT_DISABLE);
}
#endif /* NOTYET */
static JSFunctionSpec input_methods[] = {
{lm_toString_str, input_toString, 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 JSPropertySpec input_array_props[] = {
{lm_length_str, INPUT_ARRAY_LENGTH,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
{0}
};
typedef struct JSInputArray {
JSInputBase base;
uint length;
} JSInputArray;
extern JSClass lm_input_array_class;
PR_STATIC_CALLBACK(JSBool)
input_array_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSInputArray *array;
jsint slot;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
slot = JSVAL_TO_INT(id);
array = JS_GetInstancePrivate(cx, obj, &lm_input_array_class, NULL);
if (!array)
return JS_TRUE;
switch (slot) {
case INPUT_ARRAY_LENGTH:
*vp = INT_TO_JSVAL(array->length);
break;
}
return JS_TRUE;
}
PR_STATIC_CALLBACK(void)
input_array_finalize(JSContext *cx, JSObject *obj)
{
JSInputArray *array;
array = JS_GetPrivate(cx, obj);
if (!array)
return;
DROP_BACK_COUNT(array->base_decoder);
JS_free(cx, array);
}
JSClass lm_input_array_class = {
"InputArray", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
input_array_getProperty, input_array_getProperty, JS_EnumerateStub,
JS_ResolveStub, JS_ConvertStub, input_array_finalize
};
#ifdef NOTYET
PR_STATIC_CALLBACK(JSBool)
InputArray(JSContext *cx, JSObject *obj,
uint argc, jsval *argv, jsval *rval)
{
return JS_TRUE;
}
#endif
static void
lm_compile_event_handlers(MochaDecoder * decoder,
LO_FormElementStruct * form_element,
JSObject *obj,
PA_Tag *tag)
{
MWContext * context = decoder->window_context;
PA_Block method, id, keydown, keypress, keyup;
JSInputBase *base;
JSContext *cx;
cx = decoder->js_context;
base = JS_GetPrivate(cx, obj);
keydown = lo_FetchParamValue(context, tag, PARAM_ONKEYDOWN);
keypress = lo_FetchParamValue(context, tag, PARAM_ONKEYPRESS);
keyup = lo_FetchParamValue(context, tag, PARAM_ONKEYUP);
/* Text fields need this info. */
if (keydown || keypress || keyup)
form_element->event_handler_present = TRUE;
LO_UnlockLayout();
id = lo_FetchParamValue(context, tag, PARAM_ID);
method = lo_FetchParamValue(context, tag, PARAM_ONCLICK);
if (method) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONCLICK, method);
base->handlers |= HANDLER_ONCLICK;
PA_FREE(method);
}
method = lo_FetchParamValue(context, tag, PARAM_ONFOCUS);
if (method) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONFOCUS, method);
base->handlers |= HANDLER_ONFOCUS;
PA_FREE(method);
}
method = lo_FetchParamValue(context, tag, PARAM_ONBLUR);
if (method) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONBLUR, method);
base->handlers |= HANDLER_ONBLUR;
PA_FREE(method);
}
method = lo_FetchParamValue(context, tag, PARAM_ONCHANGE);
if (method) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONCHANGE, method);
base->handlers |= HANDLER_ONCHANGE;
PA_FREE(method);
}
method = lo_FetchParamValue(context, tag, PARAM_ONSELECT);
if (method) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONSELECT, method);
base->handlers |= HANDLER_ONSELECT;
PA_FREE(method);
}
method = lo_FetchParamValue(context, tag, PARAM_ONSCROLL);
if (method) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONSCROLL, method);
base->handlers |= HANDLER_ONSCROLL;
PA_FREE(method);
}
method = lo_FetchParamValue(context, tag, PARAM_ONMOUSEDOWN);
if (method) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONMOUSEDOWN, method);
base->handlers |= HANDLER_ONMOUSEDOWN;
PA_FREE(method);
}
method = lo_FetchParamValue(context, tag, PARAM_ONMOUSEUP);
if (method) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONMOUSEUP, method);
base->handlers |= HANDLER_ONMOUSEUP;
PA_FREE(method);
}
if (keydown) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONKEYDOWN, keydown);
base->handlers |= HANDLER_ONKEYDOWN;
PA_FREE(keydown);
}
if (keyup) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONKEYUP, keyup);
base->handlers |= HANDLER_ONKEYUP;
PA_FREE(keyup);
}
if (keypress) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONKEYPRESS, keypress);
base->handlers |= HANDLER_ONKEYPRESS;
PA_FREE(keypress);
}
method = lo_FetchParamValue(context, tag, PARAM_ONDBLCLICK);
if (method) {
(void) lm_CompileEventHandler(decoder, id, tag->data,
tag->newline_count, obj,
PARAM_ONDBLCLICK, method);
base->handlers |= HANDLER_ONDBLCLICK;
PA_FREE(method);
}
if (id)
PA_FREE(id);
LO_LockLayout();
}
#define ANTI_RECURSIVE_KLUDGE ((JSObject *)1)
/*
* Reflect a bunch of different types of form elements into JS.
*/
JSObject *
LM_ReflectFormElement(MWContext *context, int32 layer_id, int32 form_id,
int32 element_id, PA_Tag * tag)
{
JSObject *obj, *form_obj, *prototype, *old_obj, *array_obj;
LO_FormElementData *data;
LO_FormElementStruct *form_element;
MochaDecoder *decoder;
JSContext *cx;
int32 type;
char *name = NULL;
JSBool ok;
size_t size;
JSInput *input;
JSClass *clasp;
JSInputBase *base;
JSInputArray *array;
jsval val;
static uint recurring; /* XXX thread-unsafe */
lo_FormData * form_data;
lo_TopState *top_state;
int32 element_index;
/* reflect the form */
if (!LM_ReflectForm(context, NULL, NULL, layer_id, form_id))
return NULL;
/* make the form the active form */
decoder = LM_GetMochaDecoder(context);
if (!decoder)
return NULL;
LM_PutMochaDecoder(decoder);
/* if this is a radio button we're gonna need to get this later */
if (tag)
((PA_Tag *)tag)->lo_data = (void*)element_id;
form_data = LO_GetFormDataByID(context, layer_id, form_id);
if (!form_data || !form_data->mocha_object)
return NULL;
form_obj = form_data->mocha_object;
form_element = LO_GetFormElementByIndex(form_data, element_id);
if (!form_element || !form_element->element_data)
return NULL;
data = form_element->element_data;
/* see if we've already reflected it (or are reflecting it) */
obj = form_element->mocha_object;
if (obj) {
if (obj == ANTI_RECURSIVE_KLUDGE)
return NULL;
/*
* This object might have already gotten reflected but it might
* not have had its tag (and thus event handlers) at the time
* it was reflected
*/
if (tag)
lm_compile_event_handlers(decoder, form_element, obj, tag);
return obj;
}
decoder = LM_GetMochaDecoder(context);
if (!decoder)
return NULL;
cx = decoder->js_context;
top_state = lo_GetMochaTopState(context);
if (top_state->resize_reload) {
obj = lm_GetFormElementFromMapping(cx, form_obj, element_id);
if (obj) {
form_element->mocha_object = obj;
LM_PutMochaDecoder(decoder);
return obj;
}
}
prototype = decoder->input_prototype;
type = data->type;
if ((char *)data->ele_minimal.name)
name = XP_STRDUP((char *)data->ele_minimal.name);
switch (type) {
case FORM_TYPE_TEXT:
case FORM_TYPE_TEXTAREA:
#ifdef ENDER
case FORM_TYPE_HTMLAREA:
#endif /*ENDER*/
size = sizeof(JSTextInput);
break;
case FORM_TYPE_RADIO:
if (!recurring) {
recurring++;
ok = lm_ReflectRadioButtonArray(context, layer_id,
form_element->form_id,
name, tag);
recurring--;
obj = form_element->mocha_object;
if (obj) {
LM_PutMochaDecoder(decoder);
return obj;
}
}
/* FALL THROUGH */
default:
size = sizeof(JSInput);
break;
}
input = JS_malloc(cx, size);
if (!input)
goto fail;
XP_BZERO(input, size);
obj = JS_NewObject(cx, &lm_input_class, prototype, form_obj);
if (!obj || !JS_SetPrivate(cx, obj, input)) {
JS_free(cx, input);
goto fail;
}
/*
* get val before we lose the form_element since
* lm_compile_event_handlers() is going to lose the layout lock
*/
if (name) {
form_element->mocha_object = ANTI_RECURSIVE_KLUDGE;
ok = JS_LookupProperty(cx, form_obj, name, &val);
form_element->mocha_object = NULL;
if (!ok) {
LM_PutMochaDecoder(decoder);
return NULL;
}
}
element_index = form_element->element_index;
/* see if there are any event handlers we need to compile */
if (tag)
lm_compile_event_handlers(decoder, form_element, obj, tag);
/*
* In 3.0 we would reflect hidden elements only if they had event
* handlers, not just a name attribute.
*/
if (type == FORM_TYPE_HIDDEN && JS_GetVersion(cx) < JSVERSION_1_2) {
base = JS_GetPrivate(cx, obj);
if (!base || (!name && !base->handlers))
goto fail;
}
array_obj = NULL;
if (name) {
old_obj = JSVAL_IS_OBJECT(val) ? JSVAL_TO_OBJECT(val) : NULL;
if (old_obj) {
clasp = JS_GetClass(old_obj);
if (clasp != &lm_input_class && clasp != &lm_input_array_class)
old_obj = NULL;
}
if (old_obj) {
base = JS_GetPrivate(cx, old_obj);
if (!base)
goto fail;
if (JS_GetVersion(cx) < JSVERSION_1_2 &&
base->type == FORM_TYPE_HIDDEN) {
/*
* We have two or more elements of the form with the same name.
* For JavaScript1.1 or earlier some peculiarities apply to a
* set of form elements with the same name. If any elements in
* the set had handlers, then only those elements with handlers
* would be reflected. Otherwise, all form elements in the set
* are reflected.
*/
JSObject *temp_obj;
jsval result;
JSBool currentHasHandler;
JSBool accumulatedHasHandlers;
JSInputBase *currentBase;
currentBase = JS_GetPrivate(cx, obj);
if (!currentBase)
goto fail;
currentHasHandler = (JSBool)(currentBase->handlers != 0);
temp_obj = old_obj;
if (clasp == &lm_input_array_class) {
JS_GetElement(cx, old_obj, 0, &result);
temp_obj = JSVAL_TO_OBJECT(result);
}
accumulatedHasHandlers = (JSBool)(
(JS_LookupProperty(cx, temp_obj, lm_onClick_str, &result) &&
JS_TypeOfValue(cx, result) == JSTYPE_FUNCTION) ||
(JS_LookupProperty(cx, temp_obj, lm_onFocus_str, &result) &&
JS_TypeOfValue(cx, result) == JSTYPE_FUNCTION) ||
(JS_LookupProperty(cx, temp_obj, lm_onBlur_str, &result) &&
JS_TypeOfValue(cx, result) == JSTYPE_FUNCTION) ||
(JS_LookupProperty(cx, temp_obj, lm_onChange_str, &result) &&
JS_TypeOfValue(cx, result) == JSTYPE_FUNCTION) ||
(JS_LookupProperty(cx, temp_obj, lm_onSelect_str, &result) &&
JS_TypeOfValue(cx, result) == JSTYPE_FUNCTION) ||
(JS_LookupProperty(cx, temp_obj, lm_onScroll_str, &result) &&
JS_TypeOfValue(cx, result) == JSTYPE_FUNCTION));
if (currentHasHandler && !accumulatedHasHandlers) {
/*
* Replace the accumulated form elements with this one.
* That way, we will create an array for form elements with
* the same name, adding only those elements that have
* handlers, unless no elements have handlers, in which
* case all are reflected.
*/
JS_DeleteProperty(cx, form_obj, name);
old_obj = NULL;
} else if (!currentHasHandler && accumulatedHasHandlers) {
/* Don't add the current form element to the array. */
goto fail;
}
}
}
if (old_obj) {
if (clasp == &lm_input_class) {
/* Make an array out of the previous element and this one. */
array = JS_malloc(cx, sizeof *array);
if (!array)
goto fail;
XP_BZERO(array, sizeof *array);
/*
* Lock old_obj temporarily until we remove it from form_obj
* and add it as a property of the radio button array.
*/
JS_LockGCThing(cx, old_obj);
JS_DeleteProperty(cx, form_obj, name);
/* XXXbe use JS_InitClass instead of this! */
array_obj = JS_DefineObject(cx, form_obj, name,
&lm_input_array_class,
NULL,
JSPROP_ENUMERATE|JSPROP_READONLY);
if (array_obj && !JS_SetPrivate(cx, array_obj, array))
array_obj = NULL;
if (array_obj &&
!JS_DefineProperties(cx, array_obj, input_array_props)) {
array_obj = NULL;
}
if (!array_obj) {
JS_UnlockGCThing(cx, old_obj);
JS_free(cx, array);
goto fail;
}
array->base_decoder = HOLD_BACK_COUNT(decoder);
array->base_type = base->type;
/* Insert old_obj (referred to by val) into the array. */
if (!JS_DefineElement(cx, array_obj, (jsint) array->length,
val, NULL, NULL,
JSPROP_ENUMERATE|JSPROP_READONLY)) {
JS_UnlockGCThing(cx, old_obj);
goto fail;
}
array->length++;
JS_UnlockGCThing(cx, old_obj);
} else {
array_obj = old_obj;
array = (JSInputArray *)base;
}
/* ugly hack to prevent rebinding in lm_AddFormElement */
name = NULL;
if (!JS_DefineElement(cx, array_obj, (jsint) array->length,
OBJECT_TO_JSVAL(obj), NULL, NULL,
JSPROP_ENUMERATE|JSPROP_READONLY)) {
goto fail;
}
array->length++;
}
}
input->input_decoder = HOLD_BACK_COUNT(decoder);
input->input_type = type;
input->input_object = obj;
input->index = element_index;
/*
* get the form_element again incase it changed when we release the
* layout lock
*/
form_element = LO_GetFormElementByIndex(form_data, element_id);
if (form_element)
form_element->mocha_object = obj;
if (!lm_AddFormElement(cx, form_obj, obj, name, input->index)) {
/* XXX undefine name if it's non-null? */
}
LM_PutMochaDecoder(decoder);
XP_FREEIF(name);
return obj;
fail:
LM_PutMochaDecoder(decoder);
XP_FREEIF(name);
return NULL;
}
JSBool
lm_InitInputClasses(MochaDecoder *decoder)
{
JSContext *cx;
JSObject *prototype;
cx = decoder->js_context;
prototype = JS_InitClass(cx, decoder->window_object,
decoder->event_receiver_prototype, &lm_input_class,
Input, 0, input_props, input_methods, NULL, NULL);
if (!prototype)
return JS_FALSE;
decoder->input_prototype = prototype;
prototype = JS_InitClass(cx, decoder->window_object, NULL, &lm_option_class,
Option, 0, option_props, NULL, NULL, NULL);
if (!prototype)
return JS_FALSE;
decoder->option_prototype = prototype;
return JS_TRUE;
}
#define MAX_KEY_NUM 256
#define KEY_STATE_DOWN 0x00000001
#define KEY_STATE_UP 0x00000002
#define KEY_STATE_PRESS 0x00000004 /* user is mousing over a link */
#define KEY_STATE_CANCEL 0x00000008 /* user is mousing out of a link */
static uint8 key_state[MAX_KEY_NUM];
/* We need to look here to see if any KEYPRESS events coming in were cancelled
* at the KEYDOWN phase and should be blocked. We also need to use the KEYDOWN
* and KEYUP messages to update this state. After this we can normally process
* through lm_InputEvent.
*/
JSBool
lm_KeyInputEvent(MWContext *context, LO_Element *element, JSEvent *pEvent, jsval *rval)
{
JSBool ok = JS_TRUE;
if (pEvent->which > 255)
return lm_InputEvent(context, element, pEvent, rval);
switch (pEvent->type) {
case EVENT_KEYDOWN:
key_state[pEvent->which] = (uint8)KEY_STATE_DOWN;
break;
case EVENT_KEYPRESS:
if (key_state[pEvent->which] == KEY_STATE_CANCEL) {
*rval = BOOLEAN_TO_JSVAL(JS_FALSE);
LO_UnlockLayout();
return JS_TRUE;
}
key_state[pEvent->which] = (uint8)KEY_STATE_PRESS;
break;
case EVENT_KEYUP:
key_state[pEvent->which] = (uint8)KEY_STATE_UP;
break;
default:
break;
}
ok = lm_InputEvent(context, element, pEvent, rval);
if (pEvent->type == EVENT_KEYDOWN && *rval == JSVAL_FALSE)
key_state[pEvent->which] = (uint8)KEY_STATE_CANCEL;
return ok;
}
#define MAX_MOUSE_NUM 4
#define MOUSE_STATE_DOWN 0x00000001
#define MOUSE_STATE_UP 0x00000002
#define MOUSE_STATE_CANCEL 0x00000004
#define MOUSE_STATE_DBLCLICK 0x00000008
static uint8 mouse_state[MAX_MOUSE_NUM];
/* If a mousedown is cancelled we do not allow mouseups to do anything. This is
* because all Navigator mouse responses are based on a down followed by an up.
* This has the net effect of returning false from all mouseups if the previous
* mousedown was cancelled.
*/
JSBool
lm_MouseInputEvent(MWContext *context, LO_Element *element, JSEvent *pEvent, jsval *rval)
{
JSBool ok = JS_TRUE;
JSEvent *dcEvent;
switch (pEvent->type) {
case EVENT_MOUSEDOWN:
mouse_state[pEvent->which] = (uint8)MOUSE_STATE_DOWN;
break;
case EVENT_DBLCLICK:
mouse_state[pEvent->which] = (uint8)MOUSE_STATE_DBLCLICK;
pEvent->type = EVENT_MOUSEDOWN;
break;
default:
break;
}
ok = lm_InputEvent(context, element, pEvent, rval);
switch (pEvent->type) {
case EVENT_MOUSEDOWN:
if (*rval == JSVAL_FALSE)
mouse_state[pEvent->which] = (uint8)MOUSE_STATE_CANCEL;
break;
case EVENT_MOUSEUP:
if (mouse_state[pEvent->which] == MOUSE_STATE_CANCEL) {
*rval = BOOLEAN_TO_JSVAL(JS_FALSE);
}
else if (*rval != JSVAL_FALSE &&
mouse_state[pEvent->which] == MOUSE_STATE_DBLCLICK) {
dcEvent = XP_NEW_ZAP(JSEvent);
dcEvent->type = EVENT_DBLCLICK;
dcEvent->x = pEvent->x;
dcEvent->y = pEvent->y;
dcEvent->docx = pEvent->docx;
dcEvent->docy = pEvent->docy;
dcEvent->screenx = pEvent->screenx;
dcEvent->screeny = pEvent->screeny;
dcEvent->which = pEvent->which;
dcEvent->modifiers = pEvent->modifiers;
dcEvent->layer_id = pEvent->layer_id;
LO_LockLayout();
ok = lm_InputEvent(context, element, dcEvent, rval);
if (!dcEvent->saved)
XP_FREE(dcEvent);
}
mouse_state[pEvent->which] = (uint8)MOUSE_STATE_UP;
break;
default:
break;
}
return ok;
}
/*
* OK, we assume our caller has locked layout so that we can hold
* on to the element pointer. As soon as we are done with the
* element pointer it is up to us to make sure we unlock layout.
* Unlock layout before we call lm_SendEvent() so that we don't go
* re-entrant into the mozilla thread (and also so we hold the
* lock for as little time as possible)
*/
JSBool
lm_InputEvent(MWContext *context, LO_Element *element, JSEvent *pEvent,
jsval *rval)
{
JSContext *cx;
MochaDecoder *decoder = NULL;
JSBool ok;
LO_AnchorData *anchor;
JSObject *obj;
JSDocument *doc;
JSEventCapturer *cap;
JSEventReceiver *rec;
JSInputHandler *handler;
LO_FormElementData *data;
lo_FormData *form_data;
JSString *str;
char *re_input_bytes = NULL;
JSBool multiline = JS_FALSE;
int16 type;
JSBool event_receiver_type = JS_FALSE;
int32 layer_id, active_layer_id;
*rval = JSVAL_VOID;
cx = context->mocha_context;
if (!cx) {
LO_UnlockLayout();
return JS_TRUE;
}
/*
* If the event is has no element and is one of the event types listed
* in the if statement it is being sent to the layer or window. Handle
* these first.
*/
if (!element && (pEvent->type == EVENT_FOCUS || pEvent->type == EVENT_BLUR ||
pEvent->type == EVENT_MOUSEOVER ||
pEvent->type == EVENT_MOUSEOUT)) {
if (pEvent->layer_id == LO_DOCUMENT_LAYER_ID) {
decoder = LM_GetMochaDecoder(context);
if (!decoder) {
LO_UnlockLayout();
return JS_FALSE;
}
/* Send event to the window. */
obj = decoder->window_object;
LO_UnlockLayout();
if (decoder->event_mask & pEvent->type) {
ok = JS_TRUE;
}
else {
decoder->event_mask |= pEvent->type;
ok = lm_SendEvent(context, obj, pEvent, rval);
decoder->event_mask &= ~pEvent->type;
}
LM_PutMochaDecoder(decoder);
}
else {
/* Send event to the layer matching the layer_id. */
obj = LO_GetLayerMochaObjectFromId(context, pEvent->layer_id);
LO_UnlockLayout();
if (!obj)
return JS_FALSE;
cap = JS_GetPrivate(cx, obj);
if (!cap)
return JS_FALSE;
if (cap->base.event_mask & pEvent->type){
ok = JS_TRUE;
}
else {
cap->base.event_mask |= pEvent->type;
ok = lm_SendEvent(context, obj, pEvent, rval);
cap->base.event_mask &= ~pEvent->type;
}
}
return ok;
}
type = element ? element->type : LO_NONE;
/* If we're over plain text its easier to do this now than in the switch */
if (type == LO_TEXT && !element->lo_text.anchor_href &&
LM_EventCaptureCheck(context, pEvent->type)) {
type = LO_NONE;
}
switch (type) {
case LO_TEXT:
anchor = element->lo_text.text ? element->lo_text.anchor_href : 0;
obj = anchor ? anchor->mocha_object : 0;
#ifdef DOM
/* If this layout element is within a span, set the mocha object to
the containing SPAN's mocha object */
if (LO_IsWithinSpan( element ))
{
obj = LO_GetMochaObjectOfParentSpan( element );
}
#endif
if (!obj) {
if (!LM_EventCaptureCheck(context, pEvent->type) || !anchor) {
LO_UnlockLayout();
return JS_TRUE;
}
/* Reflect the anchor now because someone is capturing */
layer_id = LO_GetIdFromLayer(context, anchor->layer);
active_layer_id = LM_GetActiveLayer(context);
LM_SetActiveLayer(context, pEvent->layer_id);
LO_EnumerateLinks(context, pEvent->layer_id);
LM_SetActiveLayer(context, active_layer_id);
obj = anchor->mocha_object;
}
re_input_bytes = (char *)element->lo_text.text;
multiline = JS_TRUE;
break;
case LO_IMAGE:
anchor = element->lo_image.image_attr ? element->lo_image.anchor_href : 0;
if (anchor) {
obj = anchor->mocha_object;
}
else {
obj = element->lo_image.image_attr ? element->lo_image.mocha_object : 0;
event_receiver_type = JS_TRUE;
}
if (!obj) {
if (!LM_EventCaptureCheck(context, pEvent->type) ||
!element->lo_image.image_attr) {
LO_UnlockLayout();
return JS_TRUE;
}
/* Reflect the object now because someone is capturing */
if (anchor) {
layer_id = LO_GetIdFromLayer(context, anchor->layer);
active_layer_id = LM_GetActiveLayer(context);
LM_SetActiveLayer(context, layer_id);
LO_EnumerateLinks(context, layer_id);
LM_SetActiveLayer(context, active_layer_id);
obj = anchor->mocha_object;
}
else {
active_layer_id = LM_GetActiveLayer(context);
LM_SetActiveLayer(context, element->lo_image.layer_id);
LO_EnumerateImages(context, element->lo_image.layer_id);
LM_SetActiveLayer(context, active_layer_id);
obj = element->lo_image.mocha_object;
event_receiver_type = JS_TRUE;
}
}
break;
case LO_FORM_ELE:
obj = element->lo_form.element_data ? element->lo_form.mocha_object:0;
if (!obj) {
if (!LM_EventCaptureCheck(context, pEvent->type) ||
!element->lo_form.element_data) {
LO_UnlockLayout();
return JS_TRUE;
}
/* Reflect the object now because someone is capturing */
active_layer_id = LM_GetActiveLayer(context);
LM_SetActiveLayer(context, element->lo_form.layer_id);
LO_EnumerateForms(context, element->lo_form.layer_id);
form_data = LO_GetFormDataByID(context, element->lo_form.layer_id,
element->lo_form.form_id);
if (!form_data) {
LM_SetActiveLayer(context, active_layer_id);
LO_UnlockLayout();
return JS_TRUE;
}
LO_EnumerateFormElements(context, form_data);
LM_SetActiveLayer(context, active_layer_id);
obj = element->lo_form.mocha_object;
}
data = element->lo_form.element_data;
switch (data->type) {
case FORM_TYPE_TEXT:
re_input_bytes = (char *)data->ele_text.current_text;
break;
case FORM_TYPE_TEXTAREA:
#ifdef ENDER
case FORM_TYPE_HTMLAREA:
#endif /*ENDER*/
re_input_bytes = (char *)data->ele_textarea.current_text;
multiline = JS_TRUE;
break;
case FORM_TYPE_SELECT_ONE:
case FORM_TYPE_SELECT_MULT:
{
lo_FormElementSelectData *selectData;
lo_FormElementOptionData *optionData;
int32 i;
selectData = &data->ele_select;
optionData = (lo_FormElementOptionData *) selectData->options;
for (i = 0; i < selectData->option_cnt; i++) {
if (optionData[i].selected) {
re_input_bytes = (char *)optionData[i].text_value;
break;
}
}
}
break;
}
break;
default:
/* Any event over nothing or a non-reflectable layout element (linefeeds,
* horizontal rules, etc) goes to the main document or layer document.
*/
decoder = LM_GetMochaDecoder(context);
if (!decoder) {
LO_UnlockLayout();
return JS_FALSE;
}
obj = lm_GetDocumentFromLayerId(decoder, pEvent->layer_id);
LO_UnlockLayout();
LM_PutMochaDecoder(decoder);
if (!obj)
return JS_FALSE;
doc = JS_GetPrivate(cx, obj);
if (!doc)
return JS_FALSE;
if (doc->capturer.base.event_mask & pEvent->type) {
ok = JS_TRUE;
}
else {
doc->capturer.base.event_mask |= pEvent->type;
ok = lm_SendEvent(context, obj, pEvent, rval);
doc->capturer.base.event_mask &= ~pEvent->type;
}
return ok;
}
/* whether we got an object or not we are done with the element ptr */
LO_UnlockLayout();
if (!obj) {
XP_ASSERT(0);
return JS_FALSE;
}
/* Images do not have the same base private data structure as the
* other input elements do so we must use a different private data
* structs. Eventually these should be unified for all event receivers.
*/
if (event_receiver_type) {
rec = JS_GetPrivate(cx, obj);
if (!rec || rec->event_mask & pEvent->type)
return JS_FALSE;
}
else {
handler = JS_GetPrivate(cx, obj);
if (!handler || handler->event_mask & pEvent->type)
return JS_FALSE;
}
decoder = LM_GetMochaDecoder(context);
if (!decoder)
return JS_FALSE;
decoder->event_receiver = obj;
LM_PutMochaDecoder(decoder);
if (re_input_bytes) {
str = lm_LocalEncodingToStr(context, re_input_bytes);
if (!str)
return JS_FALSE;
JS_SetRegExpInput(cx, str, multiline);
}
if (event_receiver_type)
rec->event_mask |= pEvent->type;
else
handler->event_mask |= pEvent->type;
ok = lm_SendEvent(context, obj, pEvent, rval);
if (event_receiver_type)
rec->event_mask &= ~pEvent->type;
else
handler->event_mask &= ~pEvent->type;
if (re_input_bytes)
JS_ClearRegExpStatics(cx);
return ok;
}