home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libmocha / lm_input.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  70.0 KB  |  2,553 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18. /*
  19.  * JS input focus and event notifiers.
  20.  *
  21.  * Brendan Eich, 9/27/95
  22.  *
  23.  * XXX SIZE, MAXLENGTH attributes
  24.  */
  25. #include "lm.h"
  26. #include "xp.h"
  27. #include "lo_ele.h"
  28. #include "pa_tags.h"
  29. #include "layout.h"
  30. #include "prmem.h"
  31.  
  32. enum input_slot {
  33.     INPUT_TYPE              = -1,
  34.     INPUT_NAME              = -2,
  35.     INPUT_FORM              = -3,
  36.     INPUT_VALUE             = -4,
  37.     INPUT_DEFAULT_VALUE     = -5,
  38.     INPUT_LENGTH            = -6,
  39.     INPUT_OPTIONS           = -7,
  40.     INPUT_SELECTED_INDEX    = -8,
  41.     INPUT_STATUS            = -9,
  42.     INPUT_DEFAULT_STATUS    = -10
  43. #if DISABLED_READONLY_SUPPORT
  44.     INPUT_DISABLED          = -11,
  45.     INPUT_READONLY          = -12
  46. #endif
  47. };
  48.  
  49. static char lm_options_str[] = "options";
  50.  
  51. static JSPropertySpec input_props[] = {
  52.     {"type",            INPUT_TYPE,           JSPROP_ENUMERATE|JSPROP_READONLY},
  53.     {"name",            INPUT_NAME,           JSPROP_ENUMERATE},
  54.     {"form",            INPUT_FORM,           JSPROP_ENUMERATE|JSPROP_READONLY},
  55.     {"value",           INPUT_VALUE,          JSPROP_ENUMERATE},
  56.     {"defaultValue",    INPUT_DEFAULT_VALUE,  JSPROP_ENUMERATE},
  57.     {lm_length_str,     INPUT_LENGTH,         JSPROP_ENUMERATE},
  58.     {lm_options_str,    INPUT_OPTIONS,        JSPROP_ENUMERATE|JSPROP_READONLY},
  59.     {"selectedIndex",   INPUT_SELECTED_INDEX, JSPROP_ENUMERATE},
  60.     {"status",          INPUT_STATUS,         0},
  61.     {"defaultStatus",   INPUT_DEFAULT_STATUS, 0},
  62.     {PARAM_CHECKED,     INPUT_STATUS,         JSPROP_ENUMERATE},
  63.     {"defaultChecked",  INPUT_DEFAULT_STATUS, JSPROP_ENUMERATE},
  64. #if DISABLED_READONLY_SUPPORT
  65.     {"disabled",    INPUT_DISABLED,       JSPROP_ENUMERATE},
  66.     {"readonly",    INPUT_READONLY,       JSPROP_ENUMERATE},
  67. #endif
  68.     {0}
  69. };
  70.  
  71. /*
  72.  * Base input element type.
  73.  */
  74. typedef struct JSInput {
  75.     JSInputHandler          handler;
  76.     int32                   index;
  77. } JSInput;
  78.  
  79. #define input_decoder       handler.base_decoder
  80. #define input_type          handler.base_type
  81. #define input_object        handler.object
  82. #define input_event_mask    handler.event_mask
  83.  
  84. /*
  85.  * Text and textarea input type.
  86.  */
  87. typedef struct JSTextInput {
  88.     JSInput              input;
  89. } JSTextInput;
  90.  
  91. /*
  92.  * Select option tag reflected type.
  93.  */
  94. enum option_slot {
  95.     OPTION_INDEX            = -1,
  96.     OPTION_TEXT             = -2,
  97.     OPTION_VALUE            = -3,
  98.     OPTION_DEFAULT_SELECTED = -4,
  99.     OPTION_SELECTED         = -5
  100. };
  101.  
  102. static JSPropertySpec option_props[] = {
  103.     {"index",           OPTION_INDEX,            JSPROP_ENUMERATE|JSPROP_READONLY},
  104.     {"text",            OPTION_TEXT,             JSPROP_ENUMERATE},
  105.     {"value",           OPTION_VALUE,            JSPROP_ENUMERATE},
  106.     {"defaultSelected", OPTION_DEFAULT_SELECTED, JSPROP_ENUMERATE},
  107.     {"selected",        OPTION_SELECTED,         JSPROP_ENUMERATE},
  108.     {0}
  109. };
  110.  
  111. typedef struct JSSelectOption {
  112.     MochaDecoder            *decoder;
  113.     JSObject                *object;
  114.     uint32                  index;
  115.     int32                   indexInForm;
  116.     lo_FormElementOptionData *data;
  117. } JSSelectOption;
  118.  
  119. extern JSClass lm_option_class;
  120.  
  121. PR_STATIC_CALLBACK(JSBool)
  122. option_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  123. {
  124.     JSSelectOption *option;
  125.     lo_FormElementOptionData *optionData;
  126.     lo_FormElementSelectData *selectData;
  127.     LO_FormElementStruct *form_element;
  128.     enum option_slot option_slot;
  129.     JSString *str;
  130.     char *value;
  131.     jsint slot;
  132.  
  133.     if (!JSVAL_IS_INT(id))
  134.     return JS_TRUE;
  135.  
  136.     slot = JSVAL_TO_INT(id);
  137.  
  138.     option = JS_GetInstancePrivate(cx, obj, &lm_option_class, NULL);
  139.     if (!option)
  140.     return JS_TRUE;
  141.  
  142.     LO_LockLayout();
  143.     optionData = option->data;
  144.     if (optionData) {
  145.     selectData = 0;
  146.     form_element = 0;
  147.     } else {
  148.     JSObject * parent = JS_GetParent(cx, obj);
  149.     if (!parent)
  150.         goto good;
  151.     form_element = lm_GetFormElementByIndex(cx, JS_GetParent(cx, parent),
  152.                         option->indexInForm);
  153.     if (!form_element)
  154.         goto good;
  155.     selectData = &form_element->element_data->ele_select;
  156.     }
  157.     option_slot = slot;
  158.     switch (option_slot) {
  159.       case OPTION_INDEX:
  160.     *vp = INT_TO_JSVAL(option->index);
  161.     break;
  162.  
  163.       case OPTION_TEXT:
  164.       case OPTION_VALUE:
  165.     if (selectData)
  166.         optionData = (lo_FormElementOptionData *) selectData->options;
  167.     if (slot == OPTION_TEXT)
  168.         value = (char *)optionData[option->index].text_value;
  169.     else
  170.         value = (char *)optionData[option->index].value;
  171.     str = lm_LocalEncodingToStr(option->decoder->window_context, 
  172.                     value);
  173.     if (!str)
  174.         goto bad;
  175.     *vp = STRING_TO_JSVAL(str);
  176.     break;
  177.  
  178.       case OPTION_DEFAULT_SELECTED:
  179.       case OPTION_SELECTED:
  180.     if (selectData)
  181.         optionData = (lo_FormElementOptionData *) selectData->options;
  182.     *vp = BOOLEAN_TO_JSVAL((option_slot == OPTION_DEFAULT_SELECTED)
  183.              ? optionData[option->index].def_selected
  184.              : optionData[option->index].selected);
  185.     break;
  186.       default:
  187.     /* Don't mess with a user-defined or method property. */
  188.     break;
  189.     }
  190.  
  191. good:
  192.     LO_UnlockLayout();
  193.     return JS_TRUE;
  194. bad:
  195.     LO_UnlockLayout();
  196.     return JS_FALSE;
  197. }
  198.  
  199. PR_STATIC_CALLBACK(JSBool)
  200. option_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  201. {
  202.     JSSelectOption *option;
  203.     lo_FormElementOptionData *optionData;
  204.     lo_FormElementSelectData *selectData;
  205.     LO_FormElementStruct *form_element;
  206.     enum option_slot option_slot;
  207.     JSBool showChange;
  208.     int32 i;
  209.     jsint slot;
  210.     char * value = NULL;
  211.     MWContext * context;
  212.  
  213.     if (!JSVAL_IS_INT(id))
  214.     return JS_TRUE;
  215.  
  216.     slot = JSVAL_TO_INT(id);
  217.  
  218.     option = JS_GetInstancePrivate(cx, obj, &lm_option_class, NULL);
  219.     if (!option)
  220.     return JS_TRUE;
  221.  
  222.     context = option->decoder->window_context;
  223.     optionData = option->data;
  224.     LO_LockLayout();
  225.     if (optionData) {
  226.     selectData = 0;
  227.     form_element = 0;
  228.     } else {
  229.     JSObject * parent = JS_GetParent(cx, obj);
  230.     if (!parent)
  231.         goto good;
  232.     form_element = lm_GetFormElementByIndex(cx, JS_GetParent(cx, parent),
  233.                         option->indexInForm);
  234.     if (!form_element)
  235.         goto good;
  236.     selectData = &form_element->element_data->ele_select;
  237.     }
  238.  
  239.     if (selectData && option->index >= (uint32) selectData->option_cnt)
  240.     goto good;
  241.  
  242.     option_slot = slot;
  243.     showChange = JS_FALSE;
  244.     switch (option_slot) {
  245.       case OPTION_TEXT:
  246.       case OPTION_VALUE:
  247.     if (!JSVAL_IS_STRING(*vp) &&
  248.         !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) {
  249.         goto bad;
  250.     }
  251.     if (selectData)
  252.         optionData = (lo_FormElementOptionData *) selectData->options;
  253.  
  254.     value = lm_StrToLocalEncoding(context, JSVAL_TO_STRING(*vp));
  255.     if (!value)
  256.         goto bad;
  257.  
  258.     if (option_slot == OPTION_TEXT) {
  259.         if (!lm_SaveParamString(cx, &optionData[option->index].text_value,
  260.                     value)) {
  261.         goto bad;
  262.         }
  263.         showChange = JS_TRUE;
  264.     } else {
  265.         if (!lm_SaveParamString(cx, &optionData[option->index].value,
  266.                     value)) {
  267.         goto bad;
  268.         }
  269.     }
  270.     XP_FREE(value);
  271.     break;
  272.  
  273.       case OPTION_DEFAULT_SELECTED:
  274.       case OPTION_SELECTED:
  275.     if (!JSVAL_IS_BOOLEAN(*vp) &&
  276.         !JS_ConvertValue(cx, *vp, JSTYPE_BOOLEAN, vp)) {
  277.         goto bad;
  278.     }
  279.  
  280.     if (selectData)
  281.         optionData = (lo_FormElementOptionData *) selectData->options;
  282.     if (option_slot == OPTION_DEFAULT_SELECTED)
  283.         optionData[option->index].def_selected = JSVAL_TO_BOOLEAN(*vp);
  284.     else
  285.         optionData[option->index].selected = JSVAL_TO_BOOLEAN(*vp);
  286.     if (selectData) {
  287.         if (JSVAL_TO_BOOLEAN(*vp) && !selectData->multiple) {
  288.         /* Clear all the others. */
  289.         for (i = 0; i < selectData->option_cnt; i++) {
  290.             if ((uint32)i == option->index)
  291.             continue;
  292.             if (option_slot == OPTION_DEFAULT_SELECTED)
  293.             optionData[i].def_selected = FALSE;
  294.             else
  295.             optionData[i].selected = FALSE;
  296.         }
  297.         }
  298.     }
  299.  
  300.     if (option_slot == OPTION_SELECTED)
  301.         showChange = JS_TRUE;
  302.     break;
  303.  
  304.       default:
  305.     /* Don't mess with a user-defined property. */
  306.     goto good;
  307.     }
  308.  
  309.     if (showChange && context && form_element) {
  310.     ET_PostManipulateForm(context,
  311.                   (LO_Element *)form_element, 
  312.                   EVENT_CHANGE);
  313.     }
  314.  
  315. good:
  316.     LO_UnlockLayout();
  317.     return JS_TRUE;
  318. bad:
  319.     XP_FREEIF(value);
  320.     LO_UnlockLayout();
  321.     return JS_FALSE;
  322. }
  323.  
  324. PR_STATIC_CALLBACK(void)
  325. option_finalize(JSContext *cx, JSObject *obj)
  326. {
  327.     JSSelectOption *option;
  328.     lo_FormElementOptionData *optionData;
  329.  
  330.     option = JS_GetPrivate(cx, obj);
  331.     if (!option)
  332.     return;
  333.     optionData = option->data;
  334.     if (optionData) {
  335.     if (optionData->text_value)
  336.         JS_free(cx, optionData->text_value);
  337.     if (optionData->value)
  338.         JS_free(cx, optionData->value);
  339.     JS_free(cx, optionData);
  340.     }
  341.     DROP_BACK_COUNT(option->decoder);
  342.     JS_free(cx, option);
  343. }
  344.  
  345. JSClass lm_option_class = {
  346.     "Option", JSCLASS_HAS_PRIVATE,
  347.     JS_PropertyStub, JS_PropertyStub, option_getProperty, option_setProperty,
  348.     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, option_finalize
  349. };
  350.  
  351. /*
  352.  * Select option constructor, can be called any of these ways:
  353.  *  opt = new Option()
  354.  *  opt = new Option(text)
  355.  *  opt = new Option(text, value)
  356.  *  opt = new Option(text, value, defaultSelected)
  357.  *  opt = new Option(text, value, defaultSelected, selected)
  358.  * Where opt can be selectData.options[i] for any nonnegative integer i.
  359.  */
  360. PR_STATIC_CALLBACK(JSBool)
  361. Option(JSContext *cx, JSObject *obj, uint argc, jsval *argv, jsval *rval)
  362. {
  363.     MochaDecoder *decoder;
  364.     JSSelectOption *option;
  365.     lo_FormElementOptionData *optionData;
  366.     JSString *str;
  367.     JSBool bval;
  368.     MWContext *context;
  369.  
  370.     XP_ASSERT(JS_InstanceOf(cx, obj, &lm_option_class, NULL));
  371.  
  372.     decoder = JS_GetPrivate(cx, JS_GetGlobalObject(cx));
  373.     context = decoder->window_context;
  374.     option = JS_malloc(cx, sizeof *option);
  375.     if (!option)
  376.     return JS_TRUE;
  377.     XP_BZERO(option, sizeof *option);
  378.     if (!JS_SetPrivate(cx, obj, option)) {
  379.     JS_free(cx, option);
  380.     return JS_FALSE;
  381.     }
  382.  
  383.     optionData = JS_malloc(cx, sizeof *optionData);
  384.     if (!optionData)
  385.     return JS_FALSE;
  386.     XP_BZERO(optionData, sizeof *optionData);
  387.     option->data = optionData;
  388.  
  389.     if (argc >= 4) {
  390.     if (!JSVAL_IS_BOOLEAN(argv[3]) &&
  391.         !JS_ValueToBoolean(cx, argv[3], &bval)) {
  392.         return JS_FALSE;
  393.     }
  394.     optionData->selected = bval;
  395.     }
  396.     if (argc >= 3) {
  397.     if (!JSVAL_IS_BOOLEAN(argv[2]) &&
  398.         !JS_ValueToBoolean(cx, argv[2], &bval)) {
  399.         return JS_FALSE;
  400.     }
  401.     optionData->def_selected = bval;
  402.     }
  403.     if (argc >= 2) {
  404.     if (JSVAL_IS_STRING(argv[1]))
  405.         str = JSVAL_TO_STRING(argv[1]);
  406.     else if (!(str = JS_ValueToString(cx, argv[1])))
  407.         return JS_FALSE;
  408.     optionData->value = 
  409.             (PA_Block)lm_StrToLocalEncoding(context, str);
  410.     if (!optionData->value)
  411.         return JS_FALSE;
  412.     }
  413.     if (argc >= 1) {
  414.     if (JSVAL_IS_STRING(argv[0]))
  415.         str = JSVAL_TO_STRING(argv[0]);
  416.     else if (!(str = JS_ValueToString(cx, argv[0])))
  417.         return JS_FALSE;
  418.     optionData->text_value =
  419.             (PA_Block)lm_StrToLocalEncoding(context, str);
  420.     if (!optionData->text_value)
  421.         return JS_FALSE;
  422.     }
  423.  
  424.     option->decoder = HOLD_BACK_COUNT(decoder);
  425.     option->object = obj;
  426.     option->index = 0;        /* so option->data[option->index] works */
  427.     option->indexInForm = -1;
  428.     return JS_TRUE;
  429. }
  430.  
  431. static char *typenames[] = {
  432.     "none",
  433.     S_FORM_TYPE_TEXT,
  434.     S_FORM_TYPE_RADIO,
  435.     S_FORM_TYPE_CHECKBOX,
  436.     S_FORM_TYPE_HIDDEN,
  437.     S_FORM_TYPE_SUBMIT,
  438.     S_FORM_TYPE_RESET,
  439.     S_FORM_TYPE_PASSWORD,
  440.     S_FORM_TYPE_BUTTON,
  441.     S_FORM_TYPE_JOT,
  442.     "select-one",
  443.     "select-multiple",
  444.     "textarea",
  445.     "isindex",
  446.     S_FORM_TYPE_IMAGE,
  447.     S_FORM_TYPE_FILE,
  448.     "keygen",
  449.     S_FORM_TYPE_READONLY
  450. };
  451.  
  452. extern JSClass lm_input_class;
  453.  
  454. /*
  455.  * Note early returns below, to avoid common string-valued property code at
  456.  * the bottom of the function.
  457.  */
  458. PR_STATIC_CALLBACK(JSBool)
  459. input_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  460. {
  461.     JSInput *input;
  462.     MWContext *context;
  463.     enum input_slot input_slot;
  464.     LO_FormElementStruct *form_element;
  465.     JSObject *option_obj;
  466.     JSString *str;
  467.     jsint slot;
  468.  
  469.     if (!JSVAL_IS_INT(id))
  470.     return JS_TRUE;
  471.  
  472.     slot = JSVAL_TO_INT(id);
  473.  
  474.     input = JS_GetInstancePrivate(cx, obj, &lm_input_class, NULL);
  475.     if (!input)
  476.     return JS_TRUE;
  477.     input_slot = slot;
  478.     if (input_slot == INPUT_FORM) {
  479.     /* Each input in a form has a back-pointer to its form. */
  480.     *vp = OBJECT_TO_JSVAL(JS_GetParent(cx, obj));
  481.     return JS_TRUE;
  482.     }
  483.  
  484.     LO_LockLayout();
  485.  
  486.     form_element = lm_GetFormElementByIndex(cx, JS_GetParent(cx, obj),
  487.                         input->index);
  488.     if (!form_element)
  489.     goto good;
  490.  
  491.     if (input_slot == INPUT_TYPE) {
  492.     uint type_index;
  493.  
  494.     type_index = form_element->element_data->type;
  495.     if (type_index >= sizeof typenames / sizeof typenames[0]) {
  496.         JS_ReportError(cx, "unknown form element type %u", type_index);
  497.         goto bad;
  498.     }
  499.     str = JS_NewStringCopyZ(cx, typenames[type_index]);
  500.     if (!str)
  501.         goto bad;
  502.     *vp = STRING_TO_JSVAL(str);
  503.     goto good;
  504.     }
  505.  
  506.     context = input->input_decoder->window_context;
  507.     switch (form_element->element_data->type) {
  508.       case FORM_TYPE_TEXT:
  509.       case FORM_TYPE_TEXTAREA:    /* XXX we ASSUME common struct prefixes */
  510.       case FORM_TYPE_FILE:    /* XXX as above, also get-only without signing */
  511.       case FORM_TYPE_PASSWORD:
  512.     {
  513.         lo_FormElementTextData *text;
  514.  
  515.         text = &form_element->element_data->ele_text;
  516.         switch (input_slot) {
  517.           case INPUT_NAME:
  518.         str = lm_LocalEncodingToStr(context, 
  519.                         (char *)text->name);
  520.         break;
  521.           case INPUT_VALUE:
  522.         str = lm_LocalEncodingToStr(context, 
  523.                         (char *)text->current_text);
  524.         break;
  525.           case INPUT_DEFAULT_VALUE:
  526.         str = lm_LocalEncodingToStr(context, 
  527.                         (char *)text->default_text);
  528.         break;
  529. #if DISABLED_READONLY_SUPPORT
  530.           case INPUT_DISABLED:
  531.             *vp = BOOLEAN_TO_JSVAL(text->disabled);
  532.         goto good;
  533.           case INPUT_READONLY:
  534.         *vp = BOOLEAN_TO_JSVAL(text->readonly);
  535.         goto good;
  536. #endif
  537.           default:
  538.         /* Don't mess with a user-defined property. */
  539.         goto good;
  540.         }
  541.     }
  542.     break;
  543.  
  544.       case FORM_TYPE_SELECT_ONE:
  545.       case FORM_TYPE_SELECT_MULT:
  546.     {
  547.         lo_FormElementSelectData *selectData;
  548.         lo_FormElementOptionData *optionData;
  549.         int32 i;
  550.         JSSelectOption *option;
  551.  
  552.         selectData = &form_element->element_data->ele_select;
  553.         switch (input_slot) {
  554.           case INPUT_NAME:
  555.         str = lm_LocalEncodingToStr(context,
  556.                         (char *)selectData->name);
  557.         break;
  558.  
  559.           case INPUT_LENGTH:
  560.         *vp = INT_TO_JSVAL(selectData->option_cnt);
  561.         goto good;
  562.  
  563.           case INPUT_OPTIONS:
  564.         *vp = OBJECT_TO_JSVAL(input->input_object);
  565.         goto good;
  566.  
  567.           case INPUT_SELECTED_INDEX:
  568.         *vp = INT_TO_JSVAL(-1);
  569.         optionData = (lo_FormElementOptionData *) selectData->options;
  570.         for (i = 0; i < selectData->option_cnt; i++) {
  571.             if (optionData[i].selected) {
  572.             *vp = INT_TO_JSVAL(i);
  573.             break;
  574.             }
  575.         }
  576.         goto good;
  577.  
  578. #if DISABLED_READONLY_SUPPORT
  579.           case INPUT_DISABLED:
  580.             *vp = BOOLEAN_TO_JSVAL(selectData->disabled);
  581.         goto good;
  582.           case INPUT_READONLY:
  583.         *vp = BOOLEAN_TO_JSVAL(FALSE);
  584.         goto good;
  585. #endif
  586.           default:
  587.         if ((uint32)slot >= (uint32)selectData->option_cnt) {
  588.             *vp = JSVAL_NULL;
  589.             goto good;
  590.         }
  591.  
  592.         if (JSVAL_IS_OBJECT(*vp) && JSVAL_TO_OBJECT(*vp)) {
  593.             XP_ASSERT(JS_InstanceOf(cx, JSVAL_TO_OBJECT(*vp),
  594.                         &lm_option_class, NULL));
  595.             goto good;
  596.         }
  597.  
  598.         option = JS_malloc(cx, sizeof *option);
  599.         if (!option)
  600.             goto bad;
  601.  
  602.         option_obj =
  603.             JS_NewObject(cx, &lm_option_class,
  604.                  input->input_decoder->option_prototype, obj);
  605.  
  606.         if (!option_obj || !JS_SetPrivate(cx, option_obj, option)) {
  607.             JS_free(cx, option);
  608.             goto bad;
  609.         }
  610.         option->decoder = HOLD_BACK_COUNT(input->input_decoder);
  611.         option->object = option_obj;
  612.         option->index = (uint32)slot;
  613.         option->indexInForm = form_element->element_index;
  614.         option->data = NULL;
  615.         *vp = OBJECT_TO_JSVAL(option_obj);
  616.         goto good;
  617.         }
  618.     }
  619.     break;
  620.  
  621.       case FORM_TYPE_RADIO:
  622.       case FORM_TYPE_CHECKBOX:
  623.     {
  624.         lo_FormElementToggleData *toggle;
  625.  
  626.         toggle = &form_element->element_data->ele_toggle;
  627.         switch (input_slot) {
  628.           case INPUT_NAME:
  629.         str = lm_LocalEncodingToStr(context, 
  630.                         (char *)toggle->name);
  631.         break;
  632.           case INPUT_VALUE:
  633.         str = lm_LocalEncodingToStr(context, 
  634.                         (char *)toggle->value);
  635.         break;
  636.           case INPUT_STATUS:
  637.         *vp = BOOLEAN_TO_JSVAL(toggle->toggled);
  638.         goto good;
  639.           case INPUT_DEFAULT_STATUS:
  640.         *vp = BOOLEAN_TO_JSVAL(toggle->default_toggle);
  641.         goto good;
  642.  
  643. #if DISABLED_READONLY_SUPPORT
  644.           case INPUT_DISABLED:
  645.             *vp = BOOLEAN_TO_JSVAL(toggle->disabled);
  646.         goto good;
  647.           case INPUT_READONLY:
  648.         *vp = BOOLEAN_TO_JSVAL(FALSE);
  649.         goto good;
  650. #endif
  651.           default:
  652.         /* Don't mess with a user-defined property. */
  653.         goto good;
  654.         }
  655.     }
  656.     break;
  657.  
  658.       default:
  659.     {
  660.         lo_FormElementMinimalData *minimal;
  661.  
  662.         minimal = &form_element->element_data->ele_minimal;
  663.         switch (input_slot) {
  664.           case INPUT_NAME:
  665.         str = lm_LocalEncodingToStr(context, 
  666.                         (char *)minimal->name);
  667.         break;
  668.           case INPUT_VALUE:
  669.         str = lm_LocalEncodingToStr(context, 
  670.                         (char *)minimal->value);
  671.         break;
  672. #if DISABLED_READONLY_SUPPORT
  673.           case INPUT_DISABLED:
  674.             *vp = BOOLEAN_TO_JSVAL(minimal->disabled);
  675.         goto good;
  676.           case INPUT_READONLY:
  677.         *vp = BOOLEAN_TO_JSVAL(FALSE); /* minimal elements don't have the readonly attribute. */
  678.         goto good;
  679. #endif
  680.           default:
  681.         /* Don't mess with a user-defined property. */
  682.         goto good;
  683.         }
  684.     }
  685.     break;
  686.     }
  687.  
  688.     if (!str)
  689.     goto bad;
  690.     *vp = STRING_TO_JSVAL(str);
  691.  
  692. good:
  693.     LO_UnlockLayout();
  694.     return JS_TRUE;
  695.  
  696. bad:
  697.     LO_UnlockLayout();
  698.     return JS_FALSE;
  699.  
  700. }
  701.  
  702. char *
  703. lm_FixNewlines(JSContext *cx, const char *value, JSBool formElement)
  704. {
  705.     size_t size;
  706.     const char *cp;
  707.     char *tp, *new_value;
  708.  
  709. #if defined XP_PC
  710.     size = 1;
  711.     for (cp = value; *cp != '\0'; cp++) {
  712.     switch (*cp) {
  713.       case '\r':
  714.         if (cp[1] != '\n')
  715.         size++;
  716.         break;
  717.       case '\n':
  718.         if (cp > value && cp[-1] != '\r')
  719.         size++;
  720.         break;
  721.     }
  722.     }
  723.     size += cp - value;
  724. #else
  725.     size = XP_STRLEN(value) + 1;
  726. #endif
  727.     new_value = JS_malloc(cx, size);
  728.     if (!new_value)
  729.     return NULL;
  730.     for (cp = value, tp = new_value; *cp != '\0'; cp++) {
  731. #if defined XP_MAC
  732.     if (*cp == '\n') {
  733.         if (cp > value && cp[-1] != '\r')
  734.         *tp++ = '\r';
  735.     } else {
  736.         *tp++ = *cp;
  737.     }
  738. #elif defined XP_PC
  739.     switch (*cp) {
  740.       case '\r':
  741.         *tp++ = '\r';
  742.         if (cp[1] != '\n' && formElement)
  743.         *tp++ = '\n';
  744.         break;
  745.       case '\n':
  746.         if (cp > value && cp[-1] != '\r' && formElement)
  747.         *tp++ = '\r';
  748.         *tp++ = '\n';
  749.         break;
  750.       default:
  751.         *tp++ = *cp;
  752.         break;
  753.     }
  754. #else /* XP_UNIX */
  755.     if (*cp == '\r') {
  756.         if (cp[1] != '\n')
  757.         *tp++ = '\n';
  758.     } else {
  759.         *tp++ = *cp;
  760.     }
  761. #endif
  762.     }
  763.     *tp = '\0';
  764.     return new_value;
  765. }
  766.  
  767. PR_STATIC_CALLBACK(JSBool)
  768. input_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  769. {
  770.     JSInput *input;
  771.     enum input_slot input_slot;
  772.     const char *prop_name;
  773.     char *value = NULL;
  774.     LO_FormElementStruct *form_element;
  775.     MochaDecoder *decoder;
  776.     MWContext *context;
  777.     int32 intval;
  778.     jsint slot;
  779.  
  780.     input = JS_GetInstancePrivate(cx, obj, &lm_input_class, NULL);
  781.     if (!input)
  782.     return JS_TRUE;
  783.  
  784.     /* If the property is seting a key handler we find out now so
  785.      * that we can tell the front end to send the event. */
  786.     if (JSVAL_IS_STRING(id)) {
  787.     prop_name = JS_GetStringBytes(JSVAL_TO_STRING(id));
  788.     /* XXX use lm_onKeyDown_str etc. initialized by PARAM_ONKEYDOWN */
  789.     if (XP_STRCASECMP(prop_name, "onkeydown") == 0 ||
  790.         XP_STRCASECMP(prop_name, "onkeyup") == 0 ||
  791.         XP_STRCASECMP(prop_name, "onkeypress") == 0) {
  792.         form_element = lm_GetFormElementByIndex(cx, JS_GetParent(cx, obj),
  793.                             input->index);
  794.         form_element->event_handler_present = TRUE;
  795.     }
  796.     return JS_TRUE;
  797.     }
  798.  
  799.     XP_ASSERT(JSVAL_IS_INT(id));
  800.     slot = JSVAL_TO_INT(id);
  801.  
  802.     decoder = input->input_decoder;
  803.     context = decoder->window_context;
  804.     input_slot = slot;
  805.     switch (input_slot) {
  806.       case INPUT_TYPE:
  807.       case INPUT_FORM:
  808.       case INPUT_OPTIONS:
  809.     /* These are immutable. */
  810.     break;
  811.       case INPUT_NAME:
  812.       case INPUT_VALUE:
  813.       case INPUT_DEFAULT_VALUE:
  814.     /* These are string-valued. */
  815.     if (!JSVAL_IS_STRING(*vp) &&
  816.         !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) {
  817.         return JS_FALSE;
  818.     }
  819.     value = lm_StrToLocalEncoding(context, JSVAL_TO_STRING(*vp));
  820.     break;
  821.       case INPUT_STATUS:
  822.       case INPUT_DEFAULT_STATUS:
  823. #if DISABLED_READONLY_SUPPORT
  824.       case INPUT_READONLY:
  825.       case INPUT_DISABLED:
  826. #endif
  827.     /* These must be Booleans. */
  828.     if (!JSVAL_IS_BOOLEAN(*vp) &&
  829.         !JS_ConvertValue(cx, *vp, JSTYPE_BOOLEAN, vp)) {
  830.         return JS_FALSE;
  831.     }
  832.     break;
  833.       case INPUT_LENGTH:
  834.       case INPUT_SELECTED_INDEX:
  835.     /* These should be integers. */
  836.     if (JSVAL_IS_INT(*vp))
  837.         intval = JSVAL_TO_INT(*vp);
  838.     else if (!JS_ValueToInt32(cx, *vp, &intval)) {
  839.         return JS_FALSE;
  840.     }
  841.     break;
  842.     }
  843.  
  844.     LO_LockLayout();
  845.  
  846.     form_element = lm_GetFormElementByIndex(cx, JS_GetParent(cx, obj),
  847.                         input->index);
  848.     if (!form_element)
  849.     goto good;
  850.  
  851.     switch (form_element->element_data->type) {
  852.       case FORM_TYPE_FILE:
  853.     /* if we try to set a file upload widget we better be a signed script */
  854.     if (!lm_CanAccessTarget(cx, JSTARGET_UNIVERSAL_FILE_READ))
  855.         break;
  856.     /* else fall through... */
  857.  
  858.       case FORM_TYPE_TEXT:
  859.       case FORM_TYPE_TEXTAREA:    /* XXX we ASSUME common struct prefixes */
  860.       case FORM_TYPE_PASSWORD:
  861.     {
  862.         lo_FormElementTextData *text;
  863.         JSBool ok;
  864.         char * fixed_string;
  865.  
  866.         text = &form_element->element_data->ele_text;
  867.         switch (input_slot) {
  868.           case INPUT_NAME:
  869.         if (!lm_SaveParamString(cx, &text->name, value))
  870.             goto bad;
  871.         break;
  872.           case INPUT_VALUE:
  873.           case INPUT_DEFAULT_VALUE:
  874.         fixed_string = lm_FixNewlines(cx, value, JS_TRUE);
  875.         if (!fixed_string)
  876.             goto bad;
  877.         ok = (input_slot == INPUT_VALUE)
  878.              ? lm_SaveParamString(cx, &text->current_text, fixed_string)
  879.              : lm_SaveParamString(cx, &text->default_text, fixed_string);
  880.  
  881.         JS_free(cx, (char *)fixed_string);
  882.         if (!ok)
  883.             goto bad;
  884.         if (input_slot == INPUT_VALUE && context) {
  885.             ET_PostManipulateForm(context, (LO_Element *)form_element,
  886.                                   EVENT_CHANGE);
  887.         }
  888.         break;
  889. #if DISABLED_READONLY_SUPPORT
  890.           case INPUT_DISABLED:
  891.         text->disabled = JSVAL_TO_BOOLEAN(*vp);
  892.         if (context) {
  893.           ET_PostManipulateForm(context, (LO_Element *)form_element,
  894.                     EVENT_CHANGE);
  895.         }
  896.         break;
  897.           case INPUT_READONLY:
  898.         if (form_element->element_data->type == FORM_TYPE_FILE)
  899.           break;
  900.         text->readonly = JSVAL_TO_BOOLEAN(*vp);
  901.         if (context) {
  902.           ET_PostManipulateForm(context, (LO_Element *)form_element,
  903.                     EVENT_CHANGE);
  904.         }
  905.         break;
  906. #endif
  907.           default:
  908.         /* Don't mess with option or user-defined property. */
  909.         goto good;
  910.         }
  911.     }
  912.     break;
  913.  
  914.       case FORM_TYPE_SELECT_ONE:
  915.       case FORM_TYPE_SELECT_MULT:
  916.     {
  917.         lo_FormElementSelectData *selectData;
  918.         lo_FormElementOptionData *optionData;
  919.         JSSelectOption *option;
  920.         int32 i, new_option_cnt, old_option_cnt;
  921.  
  922.         selectData = &form_element->element_data->ele_select;
  923.         switch (slot) {
  924.           case INPUT_NAME:
  925.         if (!lm_SaveParamString(cx, &selectData->name, value))
  926.             goto bad;
  927.         break;
  928.  
  929.           case INPUT_LENGTH:
  930.         new_option_cnt = intval;
  931.         old_option_cnt = selectData->option_cnt;
  932.         optionData = (lo_FormElementOptionData *) selectData->options;
  933.  
  934.         /* Remove truncated slots, or clear extended element data. */
  935.         if (new_option_cnt < old_option_cnt) {
  936.             /*
  937.              * Make truncated options stand alone in case someone else
  938.              *   in case someone else has a reference to one.
  939.              */
  940.             for (i = new_option_cnt; i < old_option_cnt; i++) {
  941.             jsval oval;
  942.             JSObject * option_obj;
  943.  
  944.             if (!JS_LookupElement(cx, obj, i, &oval))
  945.                 goto bad;
  946.             if (JSVAL_IS_OBJECT(oval) &&
  947.                 (option_obj = JSVAL_TO_OBJECT(oval))) {
  948.                 lo_FormElementOptionData *myData;
  949.  
  950.                 myData =
  951.                 JS_malloc(cx, sizeof(lo_FormElementOptionData));
  952.                 if (!myData)
  953.                 goto bad;
  954.                 XP_MEMCPY(myData, &optionData[i],
  955.                       sizeof(lo_FormElementOptionData));
  956.                 option = JS_GetPrivate(cx, option_obj);
  957.                 option->data = myData;
  958.             }
  959.             JS_DeleteElement(cx, obj, i);
  960.             }
  961.         }
  962.  
  963.         /* Get layout to reallocate the options array. */
  964.         selectData->option_cnt = new_option_cnt;
  965.         if (!LO_ResizeSelectOptions(selectData)) {
  966.             selectData->option_cnt = old_option_cnt;
  967.             JS_ReportOutOfMemory(cx);
  968.             goto bad;
  969.         }
  970.  
  971.         /* Handle the grow case by clearing the new options. */
  972.         if (new_option_cnt > old_option_cnt) {
  973.             XP_BZERO(&optionData[old_option_cnt],
  974.                  (new_option_cnt - old_option_cnt)
  975.                  * sizeof *optionData);
  976.         }
  977.  
  978.         /* Tell the FE about it. */
  979.         if (context) {
  980.             ET_PostManipulateForm(context, (LO_Element *)form_element,
  981.                                   EVENT_CHANGE);
  982.         }
  983.         break;
  984.  
  985.           case INPUT_OPTIONS:
  986.         break;
  987.  
  988.           case INPUT_SELECTED_INDEX:
  989.         optionData = (lo_FormElementOptionData *)
  990.                             selectData->options;
  991.         for (i = 0; i < selectData->option_cnt; i++)
  992.             optionData[i].selected = (i == intval);
  993.  
  994.         /* Tell the FE about it. */
  995.         if (context)
  996.             ET_PostManipulateForm(context, (LO_Element *)form_element,
  997.                                   EVENT_CHANGE);
  998.         break;
  999.  
  1000. #if DISABLED_READONLY_SUPPORT
  1001.           case INPUT_DISABLED:
  1002.         selectData->disabled = JSVAL_TO_BOOLEAN(*vp);
  1003.         if (context) {
  1004.           ET_PostManipulateForm(context, (LO_Element *)form_element,
  1005.                     EVENT_CHANGE);
  1006.         }
  1007.         break;
  1008.           case INPUT_READONLY:
  1009.         /* silenty ignore updates to the READONLY attribute. */
  1010.         break;
  1011. #endif
  1012.           default:
  1013.         if (slot < 0) {
  1014.             /* Don't mess with a user-defined, named property. */
  1015.             goto good;
  1016.         }
  1017.  
  1018.         /* The vp arg must refer to an object of the right class. */
  1019.         if (!JSVAL_IS_OBJECT(*vp) &&
  1020.             !JS_ConvertValue(cx, *vp, JSTYPE_OBJECT, vp)) {
  1021.             goto bad;
  1022.         }
  1023.  
  1024.         if (JSVAL_IS_NULL(*vp)) {
  1025.             int32 count, limit;
  1026.             JSBool ok = JS_TRUE;
  1027.  
  1028.             if (slot >= selectData->option_cnt)
  1029.             goto good;
  1030.  
  1031.             /* Clear the option and compress the options array. */
  1032.             optionData = (lo_FormElementOptionData *)
  1033.                             selectData->options;
  1034.             count = selectData->option_cnt - (slot + 1);
  1035.             if (count > 0) {
  1036.             /* 
  1037.              * Move down the options that were after the option 
  1038.              *   we are deleting.  Note, the JS_GetElement() 
  1039.              *   and SetElement() calls will make sure the 
  1040.              *   layout-based data gets copied too.
  1041.              */
  1042.             for (limit = slot + count; slot < limit; slot++) {
  1043.                 jsval v;
  1044.                 ok = JS_GetElement(cx, obj, slot + 1, &v);
  1045.                 if (!ok)
  1046.                 break;
  1047.                 JS_SetElement(cx, obj, slot, &v);
  1048.  
  1049.                 /* Fix each option's index-in-select property. */
  1050.                 XP_ASSERT(JSVAL_IS_OBJECT(v));
  1051.                 option = JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
  1052.                 option->index = slot;
  1053.             }
  1054.             if (ok)
  1055.                 JS_DeleteElement(cx, obj, slot);
  1056.             }
  1057.  
  1058.             /* Shrink the select element data's options array. */
  1059.             if (ok) {
  1060.             selectData->option_cnt--;
  1061.             ok = (JSBool)LO_ResizeSelectOptions(selectData);
  1062.             if (!ok) {
  1063.                 JS_ReportOutOfMemory(cx);
  1064.             } else if (context) {
  1065.                 ET_PostManipulateForm(context,
  1066.                           (LO_Element *)form_element,
  1067.                           EVENT_CHANGE);
  1068.             }
  1069.             }
  1070.             LO_UnlockLayout();
  1071.             return ok;
  1072.         }
  1073.  
  1074.         if (!JS_InstanceOf(cx, JSVAL_TO_OBJECT(*vp), &lm_option_class,
  1075.                    NULL)) {
  1076.             JS_ReportError(cx, "cannot set %s.%s to incompatible %s",
  1077.                    JS_GetClass(obj)->name, lm_options_str,
  1078.                    JS_GetClass(JSVAL_TO_OBJECT(*vp))->name);
  1079.             goto bad;
  1080.         }
  1081.         option = JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp));
  1082.         if (!option)
  1083.                 goto good;
  1084.  
  1085.         if (!option->data && 
  1086.              JS_GetParent(cx, option->object) != obj) {
  1087.             JS_ReportError(cx, "can't share options between select elements");
  1088.             goto bad;
  1089.         }
  1090.  
  1091.         /* Grow the option array if necessary. */
  1092.         old_option_cnt = selectData->option_cnt;
  1093.         if (slot >= old_option_cnt) {
  1094.             selectData->option_cnt = slot + 1;
  1095.             if (!LO_ResizeSelectOptions(selectData)) {
  1096.             selectData->option_cnt = old_option_cnt;
  1097.             JS_ReportOutOfMemory(cx);
  1098.             goto bad;
  1099.             }
  1100.         }
  1101.  
  1102.         /* Clear any option structs in the gap, then set slot. */
  1103.         optionData = (lo_FormElementOptionData *) selectData->options;
  1104.         if (slot > old_option_cnt) {
  1105.             XP_BZERO(&optionData[old_option_cnt],
  1106.                  (slot - old_option_cnt) * sizeof *optionData);
  1107.         }
  1108.         if (option->data) {
  1109.             XP_MEMCPY(&optionData[slot], option->data,
  1110.                   sizeof(lo_FormElementOptionData));
  1111.         } else if ((uint32)slot != option->index) {
  1112.             XP_MEMCPY(&optionData[slot], &optionData[option->index],
  1113.                   sizeof(lo_FormElementOptionData));
  1114.         }
  1115.  
  1116.         /* Update the option to point at its form and form element. */
  1117.         JS_SetParent(cx, JSVAL_TO_OBJECT(*vp), obj);
  1118.         option->index = (uint32)slot;
  1119.         option->indexInForm = form_element->element_index;
  1120.         if (option->data) {
  1121.             JS_free(cx, option->data);
  1122.             option->data = NULL;
  1123.         }
  1124.  
  1125.         /* Tell the FE about it. */
  1126.         if (context)
  1127.             ET_PostManipulateForm(context, (LO_Element *)form_element,
  1128.                                   EVENT_CHANGE);
  1129.         break;
  1130.         }
  1131.     }
  1132.     break;
  1133.  
  1134.       case FORM_TYPE_RADIO:
  1135.       case FORM_TYPE_CHECKBOX:
  1136.     {
  1137.         lo_FormElementToggleData *toggle;
  1138.  
  1139.         toggle = &form_element->element_data->ele_toggle;
  1140.         switch (input_slot) {
  1141.           case INPUT_NAME:
  1142.         if (!lm_SaveParamString(cx, &toggle->name, value))
  1143.             goto bad;
  1144.         break;
  1145.           case INPUT_VALUE:
  1146.         if (!lm_SaveParamString(cx, &toggle->value, value))
  1147.             goto bad;
  1148.         break;
  1149.           case INPUT_STATUS:
  1150.         if (JSVAL_IS_BOOLEAN(*vp))
  1151.             toggle->toggled = JSVAL_TO_BOOLEAN(*vp);
  1152.  
  1153.         /* Tell the FE about it (the FE keeps radio-sets consistent). */
  1154.         if (context)
  1155.             ET_PostManipulateForm(context, (LO_Element *)form_element,
  1156.                                   EVENT_CHANGE);
  1157.         break;
  1158.           case INPUT_DEFAULT_STATUS:
  1159.         if (JSVAL_IS_BOOLEAN(*vp))
  1160.             toggle->default_toggle = JSVAL_TO_BOOLEAN(*vp);
  1161.         break;
  1162. #if DISABLED_READONLY_SUPPORT
  1163.           case INPUT_DISABLED:
  1164.         toggle->disabled = JSVAL_TO_BOOLEAN(*vp);
  1165.         if (context) {
  1166.           ET_PostManipulateForm(context, (LO_Element *)form_element,
  1167.                     EVENT_CHANGE);
  1168.         }
  1169.         break;
  1170.           case INPUT_READONLY:
  1171.         /* silenty ignore updates to the READONLY attribute. */
  1172.         break;
  1173. #endif
  1174.           default:
  1175.         /* Don't mess with a user-defined property. */
  1176.         goto good;
  1177.         }
  1178.     }
  1179.     break;
  1180.  
  1181.       case FORM_TYPE_READONLY:
  1182.     /* Don't allow modification of readonly fields. */
  1183.     break;
  1184.  
  1185.       default:
  1186.     {
  1187.         lo_FormElementMinimalData *minimal;
  1188.  
  1189.         minimal = &form_element->element_data->ele_minimal;
  1190.         switch (input_slot) {
  1191.           case INPUT_NAME:
  1192.         if (!lm_SaveParamString(cx, &minimal->name, value))
  1193.             goto bad;
  1194.         break;
  1195.           case INPUT_VALUE:
  1196.         if (!lm_SaveParamString(cx, &minimal->value, value))
  1197.             goto bad;
  1198.         if (context) {
  1199.             ET_PostManipulateForm(context, (LO_Element *)form_element,
  1200.                                   EVENT_CHANGE);
  1201.         }
  1202.         break;
  1203. #if DISABLED_READONLY_SUPPORT
  1204.           case INPUT_DISABLED:
  1205.         minimal->disabled = JSVAL_TO_BOOLEAN(*vp);
  1206.         if (context) {
  1207.           ET_PostManipulateForm(context, (LO_Element *)form_element,
  1208.                     EVENT_CHANGE);
  1209.         }
  1210.         break;
  1211.           case INPUT_READONLY:
  1212.         /* silenty ignore updates to the READONLY attribute. */
  1213.         break;
  1214. #endif
  1215.           default:
  1216.         /* Don't mess with a user-defined property. */
  1217.         goto good;
  1218.         }
  1219.     }
  1220.     break;
  1221.     }
  1222.  
  1223. good:
  1224.     XP_FREEIF(value);
  1225.     LO_UnlockLayout();
  1226.     return JS_TRUE;
  1227.  
  1228. bad:
  1229.     XP_FREEIF(value);
  1230.     LO_UnlockLayout();
  1231.     return JS_FALSE;
  1232.  
  1233. }
  1234.  
  1235. PR_STATIC_CALLBACK(void)
  1236. input_finalize(JSContext *cx, JSObject *obj)
  1237. {
  1238.     JSInput *input;
  1239.     LO_FormElementStruct *form_element;
  1240.  
  1241.     input = JS_GetPrivate(cx, obj);
  1242.     if (!input)
  1243.     return;
  1244.  
  1245.     LO_LockLayout();
  1246.     form_element = lm_GetFormElementByIndex(cx, JS_GetParent(cx, obj),
  1247.                         input->index);
  1248.     if (form_element && form_element->mocha_object == obj)
  1249.     form_element->mocha_object = NULL;
  1250.     LO_UnlockLayout();
  1251.     DROP_BACK_COUNT(input->input_decoder);
  1252.     JS_free(cx, input);
  1253. }
  1254.  
  1255. JSClass lm_input_class = {
  1256.     "Input", JSCLASS_HAS_PRIVATE,
  1257.     JS_PropertyStub, JS_PropertyStub, input_getProperty, input_setProperty,
  1258.     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, input_finalize
  1259. };
  1260.  
  1261. PR_STATIC_CALLBACK(JSBool)
  1262. Input(JSContext *cx, JSObject *obj,
  1263.       uint argc, jsval *argv, jsval *rval)
  1264. {
  1265.     return JS_TRUE;
  1266. }
  1267.  
  1268. PR_STATIC_CALLBACK(JSBool)
  1269. input_toString(JSContext *cx, JSObject *obj,
  1270.         uint argc, jsval *argv, jsval *rval)
  1271. {
  1272.     JSInput *input;
  1273.     LO_FormElementStruct *form_element;
  1274.     uint type;
  1275.     char *typename, *string, *value;
  1276.     size_t length;
  1277.     long truelong;
  1278.     jsval result;
  1279.     JSString *str;
  1280.  
  1281.     if (!JS_InstanceOf(cx, obj, &lm_input_class, argv))
  1282.         return JS_FALSE;
  1283.     input = JS_GetPrivate(cx, obj);
  1284.     if (!input)
  1285.     return JS_TRUE;
  1286.  
  1287.     LO_LockLayout();
  1288.     form_element = lm_GetFormElementByIndex(cx, JS_GetParent(cx, obj),
  1289.                         input->index);
  1290.     if (!form_element) {
  1291.     *rval = JS_GetEmptyStringValue(cx);
  1292.     goto bad;
  1293.     }
  1294.  
  1295.     type = form_element->element_data->type;
  1296.     if (type >= sizeof typenames / sizeof typenames[0]) {
  1297.     JS_ReportError(cx, "unknown form element type %u", type);
  1298.     goto bad;
  1299.     }
  1300.     typename = typenames[type];
  1301.     string = PR_sprintf_append(0, "<");
  1302.     switch (type) {
  1303.       case FORM_TYPE_TEXT:
  1304.     {
  1305.         lo_FormElementTextData *text;
  1306.  
  1307.         text = &form_element->element_data->ele_text;
  1308.         string = PR_sprintf_append(string, "%s %s=\"%s\"",
  1309.                        PT_INPUT, PARAM_TYPE, typename);
  1310.         if (text->name) {
  1311.         string = PR_sprintf_append(string, " %s=\"%s\"",
  1312.                        PARAM_NAME, (char *)text->name);
  1313.         }
  1314.         if (text->default_text) {
  1315.         string = PR_sprintf_append(string, " %s=\"%s\"",
  1316.                        PARAM_VALUE,
  1317.                        (char *)text->default_text);
  1318.         }
  1319.         if (text->size) {
  1320.         truelong = text->size;
  1321.         string = PR_sprintf_append(string, " %s=%ld\"",
  1322.                        PARAM_SIZE, truelong);
  1323.         }
  1324.         if (text->max_size) {
  1325.         truelong = text->max_size;
  1326.         string = PR_sprintf_append(string, " %s=%ld\"",
  1327.                        PARAM_MAXLENGTH, truelong);
  1328.         }
  1329.     }
  1330.     break;
  1331.  
  1332.       case FORM_TYPE_TEXTAREA:    /* XXX we ASSUME common struct prefixes */
  1333.     {
  1334.         lo_FormElementTextareaData *textarea;
  1335.  
  1336.         textarea = &form_element->element_data->ele_textarea;
  1337.         string = PR_sprintf_append(string, PT_TEXTAREA);
  1338.         if (textarea->name) {
  1339.         string = PR_sprintf_append(string, " %s=\"%s\"",
  1340.                        PARAM_NAME, (char *)textarea->name);
  1341.         }
  1342.         if (textarea->default_text) {
  1343.         string = PR_sprintf_append(string, " %s=\"%s\"",
  1344.                        PARAM_VALUE,
  1345.                        (char *)textarea->default_text);
  1346.         }
  1347.         if (textarea->rows) {
  1348.         truelong = textarea->rows;
  1349.         string = PR_sprintf_append(string, " %s=%ld\"",
  1350.                        PARAM_SIZE, truelong);
  1351.         }
  1352.         if (textarea->cols) {
  1353.         truelong = textarea->cols;
  1354.         string = PR_sprintf_append(string, " %s=%ld\"",
  1355.                        PARAM_SIZE, truelong);
  1356.         }
  1357.         if (textarea->auto_wrap) {
  1358.         switch (textarea->auto_wrap) {
  1359.           case TEXTAREA_WRAP_OFF:
  1360.             value = "off";
  1361.             break;
  1362.           case TEXTAREA_WRAP_HARD:
  1363.             value = "hard";
  1364.             break;
  1365.           case TEXTAREA_WRAP_SOFT:
  1366.             value = "soft";
  1367.             break;
  1368.           default:
  1369.             value = "unknown";
  1370.             break;
  1371.         }
  1372.         string = PR_sprintf_append(string, " %s=\"%s\"",
  1373.                        PARAM_WRAP, value);
  1374.         }
  1375.     }
  1376.     break;
  1377.  
  1378.       case FORM_TYPE_SELECT_ONE:
  1379.       case FORM_TYPE_SELECT_MULT:
  1380.     {
  1381.         lo_FormElementSelectData *selectData;
  1382.         lo_FormElementOptionData *optionData;
  1383.         int32 i;
  1384.  
  1385.         selectData = &form_element->element_data->ele_select;
  1386.         string = PR_sprintf_append(string, PT_SELECT);
  1387.         if (selectData->name) {
  1388.         string = PR_sprintf_append(string, " %s=\"%s\"",
  1389.                        PARAM_NAME,
  1390.                        (char *)selectData->name);
  1391.         }
  1392.         if (selectData->size) {
  1393.         truelong = selectData->size;
  1394.         string = PR_sprintf_append(string, " %s=%ld\"",
  1395.                        PARAM_SIZE, truelong);
  1396.         }
  1397.         if (selectData->multiple) {
  1398.         string = PR_sprintf_append(string, " %s", PARAM_MULTIPLE);
  1399.         }
  1400.  
  1401.         string = PR_sprintf_append(string, ">\n");
  1402.         PA_LOCK(optionData, lo_FormElementOptionData *,
  1403.             selectData->options);
  1404.         for (i = 0; i < selectData->option_cnt; i++) {
  1405.         string = PR_sprintf_append(string, "<%s", PT_OPTION);
  1406.         if (optionData[i].value) {
  1407.             string = PR_sprintf_append(string, " %s=\"%s\"",
  1408.                            PARAM_VALUE,
  1409.                            optionData[i].value);
  1410.         }
  1411.         if (optionData[i].def_selected)
  1412.             string = PR_sprintf_append(string, " %s", PARAM_SELECTED);
  1413.         string = PR_sprintf_append(string, ">");
  1414.         if (optionData[i].text_value) {
  1415.             string = PR_sprintf_append(string, "%s",
  1416.                            optionData[i].text_value);
  1417.         }
  1418.         string = PR_sprintf_append(string, "\n");
  1419.         }
  1420.         PA_UNLOCK(selectData->options);
  1421.  
  1422.         string = PR_sprintf_append(string, "</%s", PT_SELECT);
  1423.     }
  1424.     break;
  1425.  
  1426.       case FORM_TYPE_RADIO:
  1427.       case FORM_TYPE_CHECKBOX:
  1428.     {
  1429.         lo_FormElementToggleData *toggle;
  1430.  
  1431.         toggle = &form_element->element_data->ele_toggle;
  1432.         string = PR_sprintf_append(string, "%s %s=\"%s\"",
  1433.                        PT_INPUT, PARAM_TYPE, typename);
  1434.         if (toggle->name) {
  1435.         string = PR_sprintf_append(string, " %s=\"%s\"",
  1436.                        PARAM_NAME, (char *)toggle->name);
  1437.         }
  1438.         if (toggle->value) {
  1439.         string = PR_sprintf_append(string, " %s=\"%s\"",
  1440.                        PARAM_VALUE, (char *)toggle->value);
  1441.         }
  1442.         if (toggle->default_toggle)
  1443.         string = PR_sprintf_append(string, " %s", PARAM_CHECKED);
  1444.     }
  1445.     break;
  1446.  
  1447.       default:
  1448.     {
  1449.         lo_FormElementMinimalData *minimal;
  1450.  
  1451.         minimal = &form_element->element_data->ele_minimal;
  1452.         string = PR_sprintf_append(string, "%s %s=\"%s\"",
  1453.                        PT_INPUT, PARAM_TYPE, typename);
  1454.         if (minimal->name) {
  1455.         string = PR_sprintf_append(string, " %s=\"%s\"",
  1456.                        PARAM_NAME, (char *)minimal->name);
  1457.         }
  1458.         if (minimal->value) {
  1459.         string = PR_sprintf_append(string, " %s=\"%s\"",
  1460.                        PARAM_VALUE, (char *)minimal->value);
  1461.         }
  1462.     }
  1463.     break;
  1464.     }
  1465.  
  1466. #define FROB(param) {                                                         \
  1467.     if (!JS_LookupProperty(cx, input->input_object, param, &result)) {        \
  1468.     PR_FREEIF(string);                                                    \
  1469.     return JS_FALSE;                              \
  1470.     }                                                                         \
  1471.     if (JS_TypeOfValue(cx, result) == JSTYPE_FUNCTION) {                      \
  1472.     JSFunction *fun = JS_ValueToFunction(cx, result);                     \
  1473.     if (!fun) {                                  \
  1474.         PR_FREEIF(string);                                                \
  1475.         return JS_FALSE;                              \
  1476.     }                                                                     \
  1477.     str = JS_DecompileFunctionBody(cx, fun, 0);                  \
  1478.     value = JS_GetStringBytes(str);                          \
  1479.     length = strlen(value);                                               \
  1480.     if (length && value[length-1] == '\n') length--;                      \
  1481.     string = PR_sprintf_append(string," %s='%.*s'", param, length, value);\
  1482.     }                                                                         \
  1483. }
  1484.  
  1485.     FROB(lm_onFocus_str);
  1486.     FROB(lm_onBlur_str);
  1487.     FROB(lm_onSelect_str);
  1488.     FROB(lm_onChange_str);
  1489.     FROB(lm_onClick_str);
  1490.     FROB(lm_onScroll_str);
  1491. #undef FROB
  1492.  
  1493.     LO_UnlockLayout();
  1494.  
  1495.     string = PR_sprintf_append(string, ">");
  1496.     if (!string) {
  1497.     JS_ReportOutOfMemory(cx);
  1498.     return JS_FALSE;
  1499.     }
  1500.     str = lm_LocalEncodingToStr(input->input_decoder->window_context, 
  1501.                 string);
  1502.     XP_FREE(string);
  1503.     if (!str)
  1504.     return JS_FALSE;
  1505.     *rval = STRING_TO_JSVAL(str);
  1506.     return JS_TRUE;
  1507.  
  1508. bad:
  1509.     LO_UnlockLayout();
  1510.     return JS_FALSE;
  1511. }
  1512.  
  1513. static JSBool
  1514. input_method(JSContext *cx, JSObject *obj, jsval *argv,
  1515.          uint32 event)
  1516. {
  1517.     JSInput *input;
  1518.     MWContext *context;
  1519.     LO_FormElementStruct *form_element;
  1520.  
  1521.     if (!JS_InstanceOf(cx, obj, &lm_input_class, argv))
  1522.         return JS_FALSE;
  1523.     input = JS_GetPrivate(cx, obj);
  1524.     if (!input)
  1525.     return JS_TRUE;
  1526.     context = input->input_decoder->window_context;
  1527.     if (!context)
  1528.     return JS_TRUE;
  1529.     LO_LockLayout();
  1530.     form_element = lm_GetFormElementByIndex(cx, JS_GetParent(cx, obj),
  1531.                         input->index);
  1532.     if (!form_element) {
  1533.     LO_UnlockLayout();
  1534.     return JS_TRUE;
  1535.     }
  1536.     input->input_event_mask |= event;
  1537.     ET_PostManipulateForm(context, (LO_Element *)form_element, event);
  1538.     input->input_event_mask &= ~event;
  1539.     LO_UnlockLayout();
  1540.     return JS_TRUE;
  1541. }
  1542.  
  1543. PR_STATIC_CALLBACK(JSBool)
  1544. input_focus(JSContext *cx, JSObject *obj,
  1545.         uint argc, jsval *argv, jsval *rval)
  1546. {
  1547.     return input_method(cx, obj, argv, EVENT_FOCUS);
  1548. }
  1549.  
  1550. PR_STATIC_CALLBACK(JSBool)
  1551. input_blur(JSContext *cx, JSObject *obj,
  1552.        uint argc, jsval *argv, jsval *rval)
  1553. {
  1554.     return input_method(cx, obj, argv, EVENT_BLUR);
  1555. }
  1556.  
  1557. PR_STATIC_CALLBACK(JSBool)
  1558. input_select(JSContext *cx, JSObject *obj,
  1559.          uint argc, jsval *argv, jsval *rval)
  1560. {
  1561.     return input_method(cx, obj, argv, EVENT_SELECT);
  1562. }
  1563.  
  1564. PR_STATIC_CALLBACK(JSBool)
  1565. input_click(JSContext *cx, JSObject *obj,
  1566.          uint argc, jsval *argv, jsval *rval)
  1567. {
  1568.     return input_method(cx, obj, argv, EVENT_CLICK);
  1569. }
  1570.  
  1571. #ifdef NOTYET
  1572. PR_STATIC_CALLBACK(JSBool)
  1573. input_enable(JSContext *cx, JSObject *obj,
  1574.          uint argc, jsval *argv, jsval *rval)
  1575. {
  1576.     return input_method(cx, obj, argv, EVENT_ENABLE);
  1577. }
  1578.  
  1579. PR_STATIC_CALLBACK(JSBool)
  1580. input_disable(JSContext *cx, JSObject *obj,
  1581.           uint argc, jsval *argv, jsval *rval)
  1582. {
  1583.     return input_method(cx, obj, argv, EVENT_DISABLE);
  1584. }
  1585. #endif /* NOTYET */
  1586.  
  1587. static JSFunctionSpec input_methods[] = {
  1588.     {lm_toString_str,   input_toString,        0},
  1589.     {"focus",           input_focus,            0},
  1590.     {"blur",            input_blur,             0},
  1591.     {"select",          input_select,           0},
  1592.     {"click",           input_click,            0},
  1593. #ifdef NOTYET
  1594.     {"enable",          input_enable,           0},
  1595.     {"disable",         input_disable,          0},
  1596. #endif /* NOTYET */
  1597.     {0}
  1598. };
  1599.  
  1600. /*
  1601.  * XXX move me somewhere else...
  1602.  */
  1603. enum input_array_slot {
  1604.     INPUT_ARRAY_LENGTH = -1
  1605. };
  1606.  
  1607. static JSPropertySpec input_array_props[] = {
  1608.     {lm_length_str, INPUT_ARRAY_LENGTH,
  1609.             JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
  1610.     {0}
  1611. };
  1612.  
  1613. typedef struct JSInputArray {
  1614.     JSInputBase        base;
  1615.     uint            length;
  1616. } JSInputArray;
  1617.  
  1618. extern JSClass lm_input_array_class;
  1619.  
  1620. PR_STATIC_CALLBACK(JSBool)
  1621. input_array_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  1622. {
  1623.     JSInputArray *array;
  1624.     jsint slot;
  1625.  
  1626.     if (!JSVAL_IS_INT(id))
  1627.     return JS_TRUE;
  1628.  
  1629.     slot = JSVAL_TO_INT(id);
  1630.  
  1631.     array = JS_GetInstancePrivate(cx, obj, &lm_input_array_class, NULL);
  1632.     if (!array)
  1633.     return JS_TRUE;
  1634.     switch (slot) {
  1635.       case INPUT_ARRAY_LENGTH:
  1636.     *vp = INT_TO_JSVAL(array->length);
  1637.     break;
  1638.     }
  1639.     return JS_TRUE;
  1640. }
  1641.  
  1642. PR_STATIC_CALLBACK(void)
  1643. input_array_finalize(JSContext *cx, JSObject *obj)
  1644. {
  1645.     JSInputArray *array;
  1646.  
  1647.     array = JS_GetPrivate(cx, obj);
  1648.     if (!array)
  1649.         return;
  1650.     DROP_BACK_COUNT(array->base_decoder);
  1651.     JS_free(cx, array);
  1652. }
  1653.  
  1654. JSClass lm_input_array_class = {
  1655.     "InputArray", JSCLASS_HAS_PRIVATE,
  1656.     JS_PropertyStub, JS_PropertyStub,
  1657.     input_array_getProperty, input_array_getProperty, JS_EnumerateStub,
  1658.     JS_ResolveStub, JS_ConvertStub, input_array_finalize
  1659. };
  1660.  
  1661. #ifdef NOTYET
  1662. PR_STATIC_CALLBACK(JSBool)
  1663. InputArray(JSContext *cx, JSObject *obj,
  1664.        uint argc, jsval *argv, jsval *rval)
  1665. {
  1666.     return JS_TRUE;
  1667. }
  1668. #endif
  1669.  
  1670.  
  1671. static void
  1672. lm_compile_event_handlers(MochaDecoder * decoder, 
  1673.               LO_FormElementStruct * form_element, 
  1674.               JSObject *obj,
  1675.               PA_Tag *tag)
  1676. {
  1677.     MWContext * context = decoder->window_context;
  1678.     PA_Block method, id, keydown, keypress, keyup;
  1679.     JSInputBase *base;
  1680.     JSContext *cx;
  1681.  
  1682.     cx = decoder->js_context;
  1683.     base = JS_GetPrivate(cx, obj);
  1684.  
  1685.     keydown = lo_FetchParamValue(context, tag, PARAM_ONKEYDOWN);
  1686.     keypress = lo_FetchParamValue(context, tag, PARAM_ONKEYPRESS);
  1687.     keyup = lo_FetchParamValue(context, tag, PARAM_ONKEYUP);
  1688.  
  1689.     /* Text fields need this info. */
  1690.     if (keydown || keypress || keyup)
  1691.     form_element->event_handler_present = TRUE;
  1692.  
  1693.     LO_UnlockLayout();
  1694.  
  1695.     id = lo_FetchParamValue(context, tag, PARAM_ID);
  1696.     method = lo_FetchParamValue(context, tag, PARAM_ONCLICK);
  1697.     if (method) {
  1698.     (void) lm_CompileEventHandler(decoder, id, tag->data,
  1699.                       tag->newline_count, obj,
  1700.                       PARAM_ONCLICK, method);
  1701.     base->handlers |= HANDLER_ONCLICK;
  1702.     PA_FREE(method);
  1703.     }
  1704.     method = lo_FetchParamValue(context, tag, PARAM_ONFOCUS);
  1705.     if (method) {
  1706.     (void) lm_CompileEventHandler(decoder, id, tag->data,
  1707.                       tag->newline_count, obj,
  1708.                       PARAM_ONFOCUS, method);
  1709.     base->handlers |= HANDLER_ONFOCUS;
  1710.     PA_FREE(method);
  1711.     }
  1712.     method = lo_FetchParamValue(context, tag, PARAM_ONBLUR);
  1713.     if (method) {
  1714.     (void) lm_CompileEventHandler(decoder, id, tag->data,
  1715.                       tag->newline_count, obj,
  1716.                       PARAM_ONBLUR, method);
  1717.     base->handlers |= HANDLER_ONBLUR;
  1718.     PA_FREE(method);
  1719.     }
  1720.     method = lo_FetchParamValue(context, tag, PARAM_ONCHANGE);
  1721.     if (method) {
  1722.     (void) lm_CompileEventHandler(decoder, id, tag->data,
  1723.                       tag->newline_count, obj,
  1724.                       PARAM_ONCHANGE, method);
  1725.     base->handlers |= HANDLER_ONCHANGE;
  1726.     PA_FREE(method);
  1727.     }
  1728.     method = lo_FetchParamValue(context, tag, PARAM_ONSELECT);
  1729.     if (method) {
  1730.     (void) lm_CompileEventHandler(decoder, id, tag->data,
  1731.                       tag->newline_count, obj,
  1732.                       PARAM_ONSELECT, method);
  1733.     base->handlers |= HANDLER_ONSELECT;
  1734.     PA_FREE(method);
  1735.     }
  1736.     method = lo_FetchParamValue(context, tag, PARAM_ONSCROLL);
  1737.     if (method) {
  1738.     (void) lm_CompileEventHandler(decoder, id, tag->data,
  1739.                       tag->newline_count, obj,
  1740.                       PARAM_ONSCROLL, method);
  1741.     base->handlers |= HANDLER_ONSCROLL;
  1742.     PA_FREE(method);
  1743.     }
  1744.     method = lo_FetchParamValue(context, tag, PARAM_ONMOUSEDOWN);
  1745.     if (method) {
  1746.     (void) lm_CompileEventHandler(decoder, id, tag->data,
  1747.                       tag->newline_count, obj,
  1748.                       PARAM_ONMOUSEDOWN, method);
  1749.     base->handlers |= HANDLER_ONMOUSEDOWN;
  1750.     PA_FREE(method);
  1751.     }
  1752.     method = lo_FetchParamValue(context, tag, PARAM_ONMOUSEUP);
  1753.     if (method) {
  1754.     (void) lm_CompileEventHandler(decoder, id, tag->data,
  1755.                       tag->newline_count, obj,
  1756.                       PARAM_ONMOUSEUP, method);
  1757.     base->handlers |= HANDLER_ONMOUSEUP;
  1758.     PA_FREE(method);
  1759.     }
  1760.     if (keydown) {
  1761.     (void) lm_CompileEventHandler(decoder, id, tag->data,
  1762.                       tag->newline_count, obj,
  1763.                       PARAM_ONKEYDOWN, keydown);
  1764.     base->handlers |= HANDLER_ONKEYDOWN;
  1765.     PA_FREE(keydown);
  1766.     }
  1767.     if (keyup) {
  1768.     (void) lm_CompileEventHandler(decoder, id, tag->data,
  1769.                       tag->newline_count, obj,
  1770.                       PARAM_ONKEYUP, keyup);
  1771.     base->handlers |= HANDLER_ONKEYUP;
  1772.     PA_FREE(keyup);
  1773.     }
  1774.     if (keypress) {
  1775.     (void) lm_CompileEventHandler(decoder, id, tag->data,
  1776.                       tag->newline_count, obj,
  1777.                       PARAM_ONKEYPRESS, keypress);
  1778.     base->handlers |= HANDLER_ONKEYPRESS;
  1779.     PA_FREE(keypress);
  1780.     }
  1781.     method = lo_FetchParamValue(context, tag, PARAM_ONDBLCLICK);
  1782.     if (method) {
  1783.     (void) lm_CompileEventHandler(decoder, id, tag->data,
  1784.                       tag->newline_count, obj,
  1785.                       PARAM_ONDBLCLICK, method);
  1786.     base->handlers |= HANDLER_ONDBLCLICK;
  1787.     PA_FREE(method);
  1788.     }
  1789.     if (id)
  1790.     PA_FREE(id);
  1791.     LO_LockLayout();
  1792. }
  1793.  
  1794. #define ANTI_RECURSIVE_KLUDGE    ((JSObject *)1)
  1795.  
  1796. /*
  1797.  * Reflect a bunch of different types of form elements into JS.
  1798.  */
  1799. JSObject *
  1800. LM_ReflectFormElement(MWContext *context, int32 layer_id, int32 form_id,
  1801.                       int32 element_id, PA_Tag * tag)
  1802. {
  1803.     JSObject *obj, *form_obj, *prototype, *old_obj, *array_obj;
  1804.     LO_FormElementData *data;
  1805.     LO_FormElementStruct *form_element;
  1806.     MochaDecoder *decoder;
  1807.     JSContext *cx;
  1808.     int32 type;
  1809.     char *name = NULL;
  1810.     JSBool ok;
  1811.     size_t size;
  1812.     JSInput *input;
  1813.     JSClass *clasp;
  1814.     JSInputBase *base;
  1815.     JSInputArray *array;
  1816.     jsval val;
  1817.     static uint recurring;    /* XXX thread-unsafe */
  1818.     lo_FormData * form_data;
  1819.     lo_TopState *top_state;
  1820.     int32 element_index;
  1821.  
  1822.     /* reflect the form */
  1823.     if (!LM_ReflectForm(context, NULL, NULL, layer_id, form_id)) 
  1824.     return NULL;
  1825.  
  1826.     /* make the form the active form */
  1827.     decoder = LM_GetMochaDecoder(context);
  1828.     if (!decoder)
  1829.     return NULL;
  1830.     
  1831.     LM_PutMochaDecoder(decoder);
  1832.  
  1833.     /* if this is a radio button we're gonna need to get this later */
  1834.     if (tag)
  1835.     ((PA_Tag *)tag)->lo_data = (void*)element_id;
  1836.  
  1837.     form_data = LO_GetFormDataByID(context, layer_id, form_id);
  1838.     if (!form_data || !form_data->mocha_object)
  1839.     return NULL;
  1840.  
  1841.     form_obj = form_data->mocha_object;
  1842.  
  1843.     form_element = LO_GetFormElementByIndex(form_data, element_id);
  1844.     if (!form_element || !form_element->element_data)
  1845.     return NULL;
  1846.     data = form_element->element_data;
  1847.  
  1848.     /* see if we've already reflected it (or are reflecting it) */
  1849.     obj = form_element->mocha_object;
  1850.     if (obj) {
  1851.         if (obj == ANTI_RECURSIVE_KLUDGE)
  1852.         return NULL;
  1853.  
  1854.     /*
  1855.      * This object might have already gotten reflected but it might
  1856.      *   not have had its tag (and thus event handlers) at the time
  1857.      *   it was reflected
  1858.      */
  1859.     if (tag)
  1860.             lm_compile_event_handlers(decoder, form_element, obj, tag);
  1861.  
  1862.     return obj;
  1863.     }
  1864.  
  1865.     decoder = LM_GetMochaDecoder(context);
  1866.     if (!decoder)
  1867.         return NULL;
  1868.  
  1869.     cx = decoder->js_context;
  1870.  
  1871.     top_state = lo_GetMochaTopState(context);
  1872.     if (top_state->resize_reload) {
  1873.         obj = lm_GetFormElementFromMapping(cx, form_obj, element_id);
  1874.         if (obj) {
  1875.             form_element->mocha_object = obj;
  1876.             LM_PutMochaDecoder(decoder);
  1877.             return obj;
  1878.         }
  1879.     }
  1880.  
  1881.     prototype = decoder->input_prototype;
  1882.  
  1883.     type = data->type;
  1884.     if ((char *)data->ele_minimal.name)
  1885.     name = XP_STRDUP((char *)data->ele_minimal.name);
  1886.     switch (type) {
  1887.       case FORM_TYPE_TEXT:
  1888.       case FORM_TYPE_TEXTAREA:
  1889.         size = sizeof(JSTextInput);
  1890.         break;
  1891.  
  1892.       case FORM_TYPE_RADIO:
  1893.         if (!recurring) {
  1894.         recurring++;
  1895.         ok = lm_ReflectRadioButtonArray(context, layer_id,
  1896.                                             form_element->form_id,
  1897.                                             name, tag);
  1898.         recurring--;
  1899.         obj = form_element->mocha_object;
  1900.         if (obj) {
  1901.         LM_PutMochaDecoder(decoder);
  1902.         return obj;
  1903.         }
  1904.         }
  1905.         /* FALL THROUGH */
  1906.  
  1907.       default:
  1908.         size = sizeof(JSInput);
  1909.         break;
  1910.     }
  1911.  
  1912.     input = JS_malloc(cx, size);
  1913.     if (!input)
  1914.     goto fail;
  1915.     XP_BZERO(input, size);
  1916.  
  1917.     obj = JS_NewObject(cx, &lm_input_class, prototype, form_obj);
  1918.     if (!obj || !JS_SetPrivate(cx, obj, input)) {
  1919.         JS_free(cx, input);
  1920.     goto fail;
  1921.     }
  1922.  
  1923.     /* 
  1924.      * get val before we lose the form_element since 
  1925.      * lm_compile_event_handlers() is going to lose the layout lock 
  1926.      */
  1927.     if (name) {
  1928.     form_element->mocha_object = ANTI_RECURSIVE_KLUDGE;
  1929.     ok = JS_LookupProperty(cx, form_obj, name, &val);
  1930.     form_element->mocha_object = NULL;
  1931.     if (!ok) {
  1932.         LM_PutMochaDecoder(decoder);
  1933.         return NULL;
  1934.     }
  1935.     }
  1936.  
  1937.     element_index = form_element->element_index;
  1938.  
  1939.     /* see if there are any event handlers we need to compile */
  1940.     if (tag)
  1941.     lm_compile_event_handlers(decoder, form_element, obj, tag);
  1942.  
  1943.     /*
  1944.      * In 3.0 we would reflect hidden elements only if they had event
  1945.      *   handlers, not just a name attribute.
  1946.      */
  1947.     if (type == FORM_TYPE_HIDDEN && JS_GetVersion(cx) < JSVERSION_1_2) {
  1948.     base = JS_GetPrivate(cx, obj);
  1949.     if (!base || (!name && !base->handlers)) 
  1950.         goto fail;
  1951.     }
  1952.  
  1953.     array_obj = NULL;
  1954.  
  1955.     if (name) {
  1956.     old_obj = JSVAL_IS_OBJECT(val) ? JSVAL_TO_OBJECT(val) : NULL;
  1957.     if (old_obj) {
  1958.         clasp = JS_GetClass(old_obj);
  1959.         if (clasp != &lm_input_class && clasp != &lm_input_array_class)
  1960.             old_obj = NULL;
  1961.     }
  1962.  
  1963.     if (old_obj) {
  1964.         base = JS_GetPrivate(cx, old_obj);
  1965.         if (!base) 
  1966.         goto fail;
  1967.  
  1968.         if (JS_GetVersion(cx) < JSVERSION_1_2 &&
  1969.         base->type == FORM_TYPE_HIDDEN) {
  1970.         /*
  1971.          * We have two or more elements of the form with the same name.
  1972.          * For JavaScript1.1 or    earlier some peculiarities apply to a
  1973.          * set of form elements with the same name. If any elements in
  1974.          * the set had handlers, then only those elements with handlers
  1975.          * would be reflected. Otherwise, all form elements in the set
  1976.          * are reflected.
  1977.          */
  1978.         JSObject *temp_obj;
  1979.         jsval result;
  1980.         JSBool currentHasHandler;
  1981.         JSBool accumulatedHasHandlers;
  1982.         JSInputBase *currentBase;
  1983.  
  1984.         currentBase = JS_GetPrivate(cx, obj);
  1985.         if (!currentBase)
  1986.             goto fail;
  1987.  
  1988.         currentHasHandler = (JSBool)(currentBase->handlers != 0);
  1989.         temp_obj = old_obj;
  1990.         if (clasp == &lm_input_array_class) {
  1991.                JS_GetElement(cx, old_obj, 0, &result);
  1992.             temp_obj = JSVAL_TO_OBJECT(result);
  1993.         }
  1994.         accumulatedHasHandlers = (JSBool)(
  1995.           (JS_LookupProperty(cx, temp_obj, lm_onClick_str, &result) &&
  1996.            JS_TypeOfValue(cx, result) == JSTYPE_FUNCTION) ||
  1997.           (JS_LookupProperty(cx, temp_obj, lm_onFocus_str, &result) &&
  1998.            JS_TypeOfValue(cx, result) == JSTYPE_FUNCTION) ||
  1999.           (JS_LookupProperty(cx, temp_obj, lm_onBlur_str, &result) &&
  2000.            JS_TypeOfValue(cx, result) == JSTYPE_FUNCTION) ||
  2001.           (JS_LookupProperty(cx, temp_obj, lm_onChange_str, &result) &&
  2002.            JS_TypeOfValue(cx, result) == JSTYPE_FUNCTION) ||
  2003.           (JS_LookupProperty(cx, temp_obj, lm_onSelect_str, &result) &&
  2004.            JS_TypeOfValue(cx, result) == JSTYPE_FUNCTION) ||
  2005.           (JS_LookupProperty(cx, temp_obj, lm_onScroll_str, &result) &&
  2006.            JS_TypeOfValue(cx, result) == JSTYPE_FUNCTION));
  2007.  
  2008.         if (currentHasHandler && !accumulatedHasHandlers) {
  2009.             /*
  2010.              * Replace the accumulated form elements with this one.
  2011.              * That way, we will create an array for form elements with
  2012.              * the same name, adding only those elements that have
  2013.              * handlers, unless no elements have handlers, in which
  2014.              * case all are reflected.
  2015.              */
  2016.             JS_DeleteProperty(cx, form_obj, name);
  2017.             old_obj = NULL;
  2018.         } else if (!currentHasHandler && accumulatedHasHandlers) {
  2019.             /* Don't add the current form element to the array. */
  2020.             goto fail;
  2021.         }
  2022.         }
  2023.     }
  2024.  
  2025.     if (old_obj) {
  2026.         if (clasp == &lm_input_class) {
  2027.         /* Make an array out of the previous element and this one. */
  2028.         array = JS_malloc(cx, sizeof *array);
  2029.         if (!array) 
  2030.             goto fail;
  2031.         XP_BZERO(array, sizeof *array);
  2032.  
  2033.         /*
  2034.          * Lock old_obj temporarily until we remove it from form_obj
  2035.          * and add it as a property of the radio button array.
  2036.          */
  2037.         JS_LockGCThing(cx, old_obj);
  2038.         JS_DeleteProperty(cx, form_obj, name);
  2039.  
  2040.         /* XXXbe use JS_InitClass instead of this! */
  2041.         array_obj = JS_DefineObject(cx, form_obj, name,
  2042.                         &lm_input_array_class,
  2043.                         NULL,
  2044.                         JSPROP_ENUMERATE|JSPROP_READONLY);
  2045.  
  2046.         if (array_obj && !JS_SetPrivate(cx, array_obj, array))
  2047.             array_obj = NULL;
  2048.  
  2049.         if (array_obj &&
  2050.             !JS_DefineProperties(cx, array_obj, input_array_props)) {
  2051.             array_obj = NULL;
  2052.         }
  2053.  
  2054.         if (!array_obj) {
  2055.             JS_UnlockGCThing(cx, old_obj);
  2056.             JS_free(cx, array);
  2057.             goto fail;
  2058.         }
  2059.         array->base_decoder = HOLD_BACK_COUNT(decoder);
  2060.         array->base_type = base->type;
  2061.  
  2062.         /* Insert old_obj (referred to by val) into the array. */
  2063.         if (!JS_DefineElement(cx, array_obj, (jsint) array->length,
  2064.                       val, NULL, NULL,
  2065.                       JSPROP_ENUMERATE|JSPROP_READONLY)) {
  2066.                 JS_UnlockGCThing(cx, old_obj);
  2067.             goto fail;
  2068.         }
  2069.         array->length++;
  2070.         JS_UnlockGCThing(cx, old_obj);
  2071.  
  2072.         } else {
  2073.         array_obj = old_obj;
  2074.         array = (JSInputArray *)base;
  2075.         }
  2076.  
  2077.         /* ugly hack to prevent rebinding in lm_AddFormElement */
  2078.         name = NULL;
  2079.  
  2080.         if (!JS_DefineElement(cx, array_obj, (jsint) array->length,
  2081.                   OBJECT_TO_JSVAL(obj), NULL, NULL,
  2082.                   JSPROP_ENUMERATE|JSPROP_READONLY)) {
  2083.         goto fail;
  2084.         }
  2085.         array->length++;
  2086.     }
  2087.     }
  2088.  
  2089.     input->input_decoder = HOLD_BACK_COUNT(decoder);
  2090.     input->input_type = type;
  2091.     input->input_object = obj;
  2092.     input->index = element_index;
  2093.  
  2094.     /* 
  2095.      * get the form_element again incase it changed when we release the
  2096.      *   layout lock
  2097.      */
  2098.     form_element = LO_GetFormElementByIndex(form_data, element_id);
  2099.     if (form_element)
  2100.     form_element->mocha_object = obj;
  2101.  
  2102.     if (!lm_AddFormElement(cx, form_obj, obj, name, input->index)) {
  2103.     /* XXX undefine name if it's non-null? */
  2104.     }
  2105.  
  2106.     LM_PutMochaDecoder(decoder);
  2107.     XP_FREEIF(name);
  2108.     return obj;
  2109.  
  2110. fail:
  2111.     LM_PutMochaDecoder(decoder);
  2112.     XP_FREEIF(name);
  2113.     return NULL;
  2114.  
  2115. }
  2116.  
  2117. JSBool
  2118. lm_InitInputClasses(MochaDecoder *decoder)
  2119. {
  2120.     JSContext *cx;
  2121.     JSObject *prototype;
  2122.  
  2123.     cx = decoder->js_context;
  2124.     prototype = JS_InitClass(cx, decoder->window_object,
  2125.                  decoder->event_receiver_prototype, &lm_input_class,
  2126.                  Input, 0, input_props, input_methods, NULL, NULL);
  2127.     if (!prototype)
  2128.     return JS_FALSE;
  2129.     decoder->input_prototype = prototype;
  2130.  
  2131.     prototype = JS_InitClass(cx, decoder->window_object, NULL, &lm_option_class,
  2132.                  Option, 0, option_props, NULL, NULL, NULL);
  2133.     if (!prototype)
  2134.     return JS_FALSE;
  2135.     decoder->option_prototype = prototype;
  2136.     return JS_TRUE;
  2137. }
  2138.  
  2139. #define MAX_KEY_NUM 256
  2140.  
  2141. #define KEY_STATE_DOWN        0x00000001
  2142. #define KEY_STATE_UP        0x00000002
  2143. #define KEY_STATE_PRESS        0x00000004      /* user is mousing over a link */
  2144. #define KEY_STATE_CANCEL    0x00000008      /* user is mousing out of a link */
  2145.  
  2146. static uint8 key_state[MAX_KEY_NUM];
  2147.  
  2148. /* We need to look here to see if any KEYPRESS events coming in were cancelled
  2149.  * at the KEYDOWN phase and should be blocked.  We also need to use the KEYDOWN
  2150.  * and KEYUP messages to update this state.  After this we can normally process
  2151.  * through lm_InputEvent.
  2152.  */
  2153.  
  2154. JSBool
  2155. lm_KeyInputEvent(MWContext *context, LO_Element *element, JSEvent *pEvent, jsval *rval)
  2156. {
  2157.     JSBool ok = JS_TRUE;
  2158.  
  2159.     if (pEvent->which > 255)
  2160.         return lm_InputEvent(context, element, pEvent, rval);
  2161.  
  2162.     switch (pEvent->type) {
  2163.     case EVENT_KEYDOWN:
  2164.         key_state[pEvent->which] = (uint8)KEY_STATE_DOWN;
  2165.         break;
  2166.  
  2167.     case EVENT_KEYPRESS:
  2168.         if (key_state[pEvent->which] == KEY_STATE_CANCEL) {
  2169.         *rval = BOOLEAN_TO_JSVAL(JS_FALSE);
  2170.         LO_UnlockLayout();
  2171.         return JS_TRUE;
  2172.         }
  2173.         key_state[pEvent->which] = (uint8)KEY_STATE_PRESS;
  2174.         break;
  2175.  
  2176.     case EVENT_KEYUP:
  2177.         key_state[pEvent->which] = (uint8)KEY_STATE_UP;
  2178.         break;
  2179.     default:
  2180.         break;
  2181.     }
  2182.     ok = lm_InputEvent(context, element, pEvent, rval);
  2183.  
  2184.     if (pEvent->type == EVENT_KEYDOWN && *rval == JSVAL_FALSE)
  2185.     key_state[pEvent->which] = (uint8)KEY_STATE_CANCEL;
  2186.     return ok;
  2187. }
  2188.  
  2189. #define MAX_MOUSE_NUM 4
  2190.  
  2191. #define MOUSE_STATE_DOWN    0x00000001
  2192. #define MOUSE_STATE_UP        0x00000002
  2193. #define MOUSE_STATE_CANCEL  0x00000004
  2194. #define MOUSE_STATE_DBLCLICK 0x00000008
  2195.  
  2196. static uint8 mouse_state[MAX_MOUSE_NUM];
  2197.  
  2198. /* If a mousedown is cancelled we do not allow mouseups to do anything.  This is
  2199.  * because all Navigator mouse responses are based on a down followed by an up.
  2200.  * This has the net effect of returning false from all mouseups if the previous
  2201.  * mousedown was cancelled.
  2202.  */
  2203.  
  2204. JSBool
  2205. lm_MouseInputEvent(MWContext *context, LO_Element *element, JSEvent *pEvent, jsval *rval)
  2206. {
  2207.     JSBool ok = JS_TRUE;
  2208.     JSEvent *dcEvent;
  2209.  
  2210.     switch (pEvent->type) {
  2211.     case EVENT_MOUSEDOWN:
  2212.         mouse_state[pEvent->which] = (uint8)MOUSE_STATE_DOWN;
  2213.         break;
  2214.     case EVENT_DBLCLICK:
  2215.         mouse_state[pEvent->which] = (uint8)MOUSE_STATE_DBLCLICK;
  2216.         pEvent->type = EVENT_MOUSEDOWN;
  2217.         break;
  2218.     default:
  2219.         break;
  2220.     }
  2221.  
  2222.     ok = lm_InputEvent(context, element, pEvent, rval);
  2223.  
  2224.     switch (pEvent->type) {
  2225.     case EVENT_MOUSEDOWN:
  2226.         if (*rval == JSVAL_FALSE)
  2227.         mouse_state[pEvent->which] = (uint8)MOUSE_STATE_CANCEL;
  2228.         break;
  2229.     case EVENT_MOUSEUP:
  2230.         if (mouse_state[pEvent->which] == MOUSE_STATE_CANCEL) {
  2231.         *rval = BOOLEAN_TO_JSVAL(JS_FALSE);
  2232.         }
  2233.         else if (*rval != JSVAL_FALSE &&
  2234.              mouse_state[pEvent->which] == MOUSE_STATE_DBLCLICK) {
  2235.         dcEvent = XP_NEW_ZAP(JSEvent);
  2236.         dcEvent->type = EVENT_DBLCLICK;
  2237.         dcEvent->x = pEvent->x;
  2238.         dcEvent->y = pEvent->y;
  2239.         dcEvent->docx = pEvent->docx;
  2240.         dcEvent->docy = pEvent->docy;
  2241.         dcEvent->screenx = pEvent->screenx;
  2242.         dcEvent->screeny = pEvent->screeny;
  2243.         dcEvent->which = pEvent->which;
  2244.         dcEvent->modifiers = pEvent->modifiers;
  2245.         dcEvent->layer_id = pEvent->layer_id;
  2246.  
  2247.         LO_LockLayout();
  2248.         ok = lm_InputEvent(context, element, dcEvent, rval);
  2249.  
  2250.         if (!dcEvent->saved)
  2251.             XP_FREE(dcEvent);
  2252.         }
  2253.         mouse_state[pEvent->which] = (uint8)MOUSE_STATE_UP;
  2254.         break;
  2255.     default:
  2256.         break;
  2257.     }
  2258.     return ok;
  2259. }
  2260.  
  2261. /*
  2262.  * OK, we assume our caller has locked layout so that we can hold
  2263.  *   on to the element pointer.  As soon as we are done with the
  2264.  *   element pointer it is up to us to make sure we unlock layout.
  2265.  * Unlock layout before we call lm_SendEvent() so that we don't go
  2266.  *   re-entrant into the mozilla thread (and also so we hold the
  2267.  *   lock for as little time as possible)
  2268.  */
  2269. JSBool
  2270. lm_InputEvent(MWContext *context, LO_Element *element, JSEvent *pEvent,
  2271.             jsval *rval)
  2272. {
  2273.     JSContext *cx;
  2274.     MochaDecoder *decoder = NULL;
  2275.     JSBool ok;
  2276.     LO_AnchorData *anchor;
  2277.     JSObject *obj;
  2278.     JSDocument *doc;
  2279.     JSEventCapturer *cap;
  2280.     JSEventReceiver *rec;
  2281.     JSInputHandler *handler;
  2282.     LO_FormElementData *data;
  2283.     lo_FormData *form_data;
  2284.     JSString *str;
  2285.     char *re_input_bytes = NULL;
  2286.     JSBool multiline = JS_FALSE;
  2287.     int16 type;
  2288.     JSBool event_receiver_type = JS_FALSE;
  2289.     int32 layer_id, active_layer_id;
  2290.  
  2291.     *rval = JSVAL_VOID;
  2292.     cx = context->mocha_context;
  2293.     if (!cx) {
  2294.     LO_UnlockLayout();
  2295.     return JS_TRUE;
  2296.     }
  2297.  
  2298.     /*
  2299.      * If the event is has no element and is one of the event types listed
  2300.      * in the if statement it is being sent to the layer or window.  Handle
  2301.      * these first.
  2302.      */
  2303.     if (!element && (pEvent->type == EVENT_FOCUS || pEvent->type == EVENT_BLUR ||
  2304.         pEvent->type == EVENT_MOUSEOVER ||
  2305.         pEvent->type == EVENT_MOUSEOUT)) {
  2306.  
  2307.     if (pEvent->layer_id == LO_DOCUMENT_LAYER_ID) {
  2308.  
  2309.         decoder = LM_GetMochaDecoder(context);
  2310.         if (!decoder) {
  2311.         LO_UnlockLayout();
  2312.         return JS_FALSE;
  2313.         }
  2314.  
  2315.         /* Send event to the window. */
  2316.             obj = decoder->window_object;
  2317.  
  2318.         LO_UnlockLayout();
  2319.  
  2320.         if (decoder->event_mask & pEvent->type) {
  2321.         ok = JS_TRUE;
  2322.         }
  2323.         else {
  2324.         decoder->event_mask |= pEvent->type;
  2325.         ok = lm_SendEvent(context, obj, pEvent, rval);
  2326.         decoder->event_mask &= ~pEvent->type;
  2327.         }
  2328.         LM_PutMochaDecoder(decoder);
  2329.     }
  2330.         else {
  2331.         /* Send event to the layer matching the layer_id. */
  2332.             obj = LO_GetLayerMochaObjectFromId(context, pEvent->layer_id);
  2333.  
  2334.         LO_UnlockLayout();
  2335.  
  2336.         if (!obj)
  2337.         return JS_FALSE;
  2338.  
  2339.         cap = JS_GetPrivate(cx, obj);
  2340.         if (!cap)
  2341.         return JS_FALSE;
  2342.  
  2343.         if (cap->base.event_mask & pEvent->type){
  2344.         ok = JS_TRUE;
  2345.         }
  2346.         else {
  2347.         cap->base.event_mask |= pEvent->type;
  2348.         ok = lm_SendEvent(context, obj, pEvent, rval);
  2349.         cap->base.event_mask &= ~pEvent->type;
  2350.         }
  2351.     }
  2352.  
  2353.         return ok;
  2354.     }
  2355.  
  2356.     type = element ? element->type : LO_NONE;
  2357.  
  2358.     /* If we're over plain text its easier to do this now than in the switch */
  2359.     if (type == LO_TEXT && !element->lo_text.anchor_href && 
  2360.             LM_EventCaptureCheck(context, pEvent->type))    {
  2361.     type = LO_NONE;
  2362.     }
  2363.  
  2364.     switch (type) {
  2365.       case LO_TEXT:
  2366.     anchor = element->lo_text.text ? element->lo_text.anchor_href : 0;
  2367.     obj = anchor ? anchor->mocha_object : 0;
  2368.     if (!obj) {
  2369.         if (!LM_EventCaptureCheck(context, pEvent->type) || !anchor) {
  2370.             LO_UnlockLayout();
  2371.         return JS_TRUE;
  2372.         }
  2373.         /* Reflect the anchor now because someone is capturing */
  2374.         layer_id = LO_GetIdFromLayer(context, anchor->layer);
  2375.         active_layer_id = LM_GetActiveLayer(context);
  2376.         LM_SetActiveLayer(context, pEvent->layer_id);
  2377.         LO_EnumerateLinks(context, pEvent->layer_id);
  2378.         LM_SetActiveLayer(context, active_layer_id);
  2379.         obj = anchor->mocha_object;
  2380.     }
  2381.     re_input_bytes = (char *)element->lo_text.text;
  2382.     multiline = JS_TRUE;
  2383.     break;
  2384.       case LO_IMAGE:
  2385.     anchor = element->lo_image.image_attr ? element->lo_image.anchor_href : 0;
  2386.     if (anchor) {
  2387.         obj = anchor->mocha_object;
  2388.     }
  2389.     else {
  2390.         obj = element->lo_image.image_attr ? element->lo_image.mocha_object : 0;
  2391.         event_receiver_type = JS_TRUE;
  2392.     }
  2393.     if (!obj) {
  2394.         if (!LM_EventCaptureCheck(context, pEvent->type) || 
  2395.                         !element->lo_image.image_attr) {
  2396.             LO_UnlockLayout();
  2397.         return JS_TRUE;
  2398.         }
  2399.         /* Reflect the object now because someone is capturing */
  2400.         if (anchor) {
  2401.         layer_id = LO_GetIdFromLayer(context, anchor->layer);
  2402.         active_layer_id = LM_GetActiveLayer(context);
  2403.         LM_SetActiveLayer(context, layer_id);
  2404.         LO_EnumerateLinks(context, layer_id);
  2405.         LM_SetActiveLayer(context, active_layer_id);
  2406.         obj = anchor->mocha_object;
  2407.         }
  2408.         else {
  2409.         active_layer_id = LM_GetActiveLayer(context);
  2410.         LM_SetActiveLayer(context, element->lo_image.layer_id);
  2411.         LO_EnumerateImages(context, element->lo_image.layer_id);
  2412.         LM_SetActiveLayer(context, active_layer_id);
  2413.         obj = element->lo_image.mocha_object;
  2414.         event_receiver_type = JS_TRUE;        
  2415.         }
  2416.     }
  2417.     break;
  2418.       case LO_FORM_ELE:
  2419.     obj = element->lo_form.element_data ? element->lo_form.mocha_object:0;
  2420.     if (!obj) {
  2421.         if (!LM_EventCaptureCheck(context, pEvent->type) ||
  2422.                     !element->lo_form.element_data) {
  2423.             LO_UnlockLayout();
  2424.         return JS_TRUE;
  2425.         }
  2426.         /* Reflect the object now because someone is capturing */
  2427.         active_layer_id = LM_GetActiveLayer(context);
  2428.         LM_SetActiveLayer(context, element->lo_form.layer_id);
  2429.         LO_EnumerateForms(context, element->lo_form.layer_id);
  2430.         form_data = LO_GetFormDataByID(context, element->lo_form.layer_id, 
  2431.                         element->lo_form.form_id);
  2432.         if (!form_data)    {
  2433.         LM_SetActiveLayer(context, active_layer_id);
  2434.         LO_UnlockLayout();
  2435.         return JS_TRUE;
  2436.         }
  2437.         LO_EnumerateFormElements(context, form_data);
  2438.         LM_SetActiveLayer(context, active_layer_id);
  2439.         obj = element->lo_form.mocha_object;
  2440.     }
  2441.     data = element->lo_form.element_data;
  2442.     switch (data->type) {
  2443.       case FORM_TYPE_TEXT:
  2444.         re_input_bytes = (char *)data->ele_text.current_text;
  2445.         break;
  2446.       case FORM_TYPE_TEXTAREA:
  2447.         re_input_bytes = (char *)data->ele_textarea.current_text;
  2448.         multiline = JS_TRUE;
  2449.         break;
  2450.       case FORM_TYPE_SELECT_ONE:
  2451.       case FORM_TYPE_SELECT_MULT:
  2452.         {
  2453.         lo_FormElementSelectData *selectData;
  2454.         lo_FormElementOptionData *optionData;
  2455.         int32 i;
  2456.  
  2457.         selectData = &data->ele_select;
  2458.         optionData = (lo_FormElementOptionData *) selectData->options;
  2459.         for (i = 0; i < selectData->option_cnt; i++) {
  2460.             if (optionData[i].selected) {
  2461.             re_input_bytes = (char *)optionData[i].text_value;
  2462.             break;
  2463.             }
  2464.         }
  2465.         }
  2466.         break;
  2467.     }
  2468.     break;
  2469.       default:
  2470.     /* Any event over nothing or a non-reflectable layout element (linefeeds,
  2471.      * horizontal rules, etc) goes to the main document or layer document.
  2472.      */
  2473.     decoder = LM_GetMochaDecoder(context);
  2474.     if (!decoder) {
  2475.         LO_UnlockLayout();
  2476.         return JS_FALSE;
  2477.     }
  2478.  
  2479.     obj = lm_GetDocumentFromLayerId(decoder, pEvent->layer_id);
  2480.         LO_UnlockLayout();
  2481.     LM_PutMochaDecoder(decoder);
  2482.  
  2483.     if (!obj)
  2484.         return JS_FALSE;
  2485.  
  2486.     doc = JS_GetPrivate(cx, obj);
  2487.     if (!doc) 
  2488.         return JS_FALSE;
  2489.  
  2490.     if (doc->capturer.base.event_mask & pEvent->type) {
  2491.         ok = JS_TRUE;
  2492.     }
  2493.     else {
  2494.         doc->capturer.base.event_mask |= pEvent->type;
  2495.         ok = lm_SendEvent(context, obj, pEvent, rval);
  2496.         doc->capturer.base.event_mask &= ~pEvent->type;
  2497.     }
  2498.     return ok;
  2499.     }
  2500.  
  2501.     /* whether we got an object or not we are done with the element ptr */
  2502.     LO_UnlockLayout();
  2503.  
  2504.     if (!obj) {
  2505.     XP_ASSERT(0);
  2506.     return JS_FALSE;
  2507.     }
  2508.  
  2509.     /* Images do not have the same base private data structure as the 
  2510.      * other input elements do so we must use a different private data
  2511.      * structs.  Eventually these should be unified for all event receivers.
  2512.      */
  2513.     if (event_receiver_type) {
  2514.     rec = JS_GetPrivate(cx, obj);
  2515.     if (!rec || rec->event_mask & pEvent->type)
  2516.             return JS_FALSE;
  2517.     }
  2518.     else {
  2519.     handler = JS_GetPrivate(cx, obj);
  2520.     if (!handler || handler->event_mask & pEvent->type) 
  2521.             return JS_FALSE;
  2522.     }
  2523.  
  2524.     decoder = LM_GetMochaDecoder(context);
  2525.     if (!decoder)
  2526.     return JS_FALSE;
  2527.     decoder->event_receiver = obj;
  2528.     LM_PutMochaDecoder(decoder);
  2529.  
  2530.     if (re_input_bytes) {
  2531.     str = lm_LocalEncodingToStr(context, re_input_bytes);
  2532.     if (!str)
  2533.         return JS_FALSE;
  2534.     JS_SetRegExpInput(cx, str, multiline);
  2535.     }
  2536.  
  2537.     if (event_receiver_type)
  2538.     rec->event_mask |= pEvent->type;
  2539.     else
  2540.     handler->event_mask |= pEvent->type;
  2541.  
  2542.     ok = lm_SendEvent(context, obj, pEvent, rval);
  2543.  
  2544.     if (event_receiver_type)
  2545.     rec->event_mask &= ~pEvent->type;
  2546.     else
  2547.     handler->event_mask &= ~pEvent->type;
  2548.        
  2549.     if (re_input_bytes)
  2550.     JS_ClearRegExpStatics(cx);
  2551.     return ok;
  2552. }
  2553.