home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libmocha / lm_form.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  26.0 KB  |  993 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 reflection of the HTML FORM elements.
  20.  *
  21.  * Brendan Eich, 9/27/95
  22.  */
  23. #include "lm.h"
  24. #include "lo_ele.h"
  25. #include "mkutils.h"
  26. #include "layout.h"
  27. #include "pa_tags.h"
  28. #include "shist.h"
  29.  
  30. enum form_array_slot {
  31.     FORM_ARRAY_LENGTH = -1
  32. };
  33.  
  34. static JSPropertySpec form_array_props[] = {
  35.     {lm_length_str, FORM_ARRAY_LENGTH,
  36.             JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT},
  37.     {0}
  38. };
  39.  
  40. extern JSClass lm_form_array_class;
  41.  
  42. PR_STATIC_CALLBACK(JSBool)
  43. form_array_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  44. {
  45.     JSObjectArray *array;
  46.     MochaDecoder *decoder;
  47.     MWContext *context;
  48.     jsint count, slot;
  49.     lo_FormData *form_data;
  50.     int32 active_layer_id;
  51.  
  52.     if (!JSVAL_IS_INT(id))
  53.     return JS_TRUE;
  54.  
  55.     slot = JSVAL_TO_INT(id);
  56.  
  57.     array = JS_GetInstancePrivate(cx, obj, &lm_form_array_class, NULL);
  58.     if (!array)
  59.     return JS_TRUE;
  60.     decoder = array->decoder;
  61.     context = decoder->window_context;
  62.     if (!context) return JS_TRUE;
  63.  
  64.     LO_LockLayout();
  65.     switch (slot) {
  66.       case FORM_ARRAY_LENGTH:
  67.     active_layer_id = LM_GetActiveLayer(context);
  68.     LM_SetActiveLayer(context, array->layer_id);
  69.         count = LO_EnumerateForms(context, array->layer_id);
  70.     LM_SetActiveLayer(context, active_layer_id);
  71.         if (count > array->length)
  72.             array->length = count;
  73.         *vp = INT_TO_JSVAL(array->length);
  74.         break;
  75.  
  76.       default:
  77.     if (slot < 0) {
  78.         /* Don't mess with user-defined or method properties. */
  79.         LO_UnlockLayout();
  80.         return JS_TRUE;
  81.     }
  82.         if (slot >= array->length)
  83.         array->length = slot + 1;
  84.         /* NB: form IDs start at 1, not 0. */
  85.         form_data = LO_GetFormDataByID(context, array->layer_id, slot + 1);
  86.         if (form_data)
  87.         *vp = OBJECT_TO_JSVAL(LM_ReflectForm(context, form_data, NULL,
  88.                                                  array->layer_id, 0));
  89.         break;
  90.     }
  91.     LO_UnlockLayout();
  92.     return JS_TRUE;
  93. }
  94.  
  95. PR_STATIC_CALLBACK(void)
  96. form_array_finalize(JSContext *cx, JSObject *obj)
  97. {
  98.     JSObjectArray *array;
  99.  
  100.     array = JS_GetPrivate(cx, obj);
  101.     if (!array)
  102.     return;
  103.     DROP_BACK_COUNT(array->decoder);
  104.     JS_free(cx, array);
  105. }
  106.  
  107. JSClass lm_form_array_class = {
  108.     "FormArray", JSCLASS_HAS_PRIVATE,
  109.     JS_PropertyStub, JS_PropertyStub,
  110.     form_array_getProperty, form_array_getProperty, JS_EnumerateStub,
  111.     JS_ResolveStub, JS_ConvertStub, form_array_finalize
  112. };
  113.  
  114. PR_STATIC_CALLBACK(JSBool)
  115. FormArray(JSContext *cx, JSObject *obj,
  116.       uint argc, jsval *argv, jsval *rval)
  117. {
  118.     return JS_TRUE;
  119. }
  120.  
  121. JSObject *
  122. lm_GetFormArray(MochaDecoder *decoder, JSObject *document)
  123. {
  124.     JSObject *obj;
  125.     JSContext *cx;
  126.     JSObjectArray *array;
  127.     JSDocument *doc;
  128.  
  129.     cx = decoder->js_context;
  130.     doc = JS_GetPrivate(cx, document);
  131.     if (!doc)
  132.     return NULL;
  133.  
  134.     obj = doc->forms;
  135.     if (obj)
  136.     return obj;
  137.  
  138.     array = JS_malloc(cx, sizeof *array);
  139.     if (!array)
  140.     return NULL;
  141.     XP_BZERO(array, sizeof *array);
  142.  
  143.     obj = JS_NewObject(cx, &lm_form_array_class, NULL, document);
  144.     if (!obj || !JS_SetPrivate(cx, obj, array)) {
  145.     JS_free(cx, array);
  146.     return NULL;
  147.     }
  148.  
  149.     if (!JS_DefineProperties(cx, obj, form_array_props))
  150.     return NULL;
  151.  
  152.     array->decoder = HOLD_BACK_COUNT(decoder);
  153.     array->layer_id = doc->layer_id;
  154.     doc->forms = obj;
  155.     return obj;
  156. }
  157.  
  158. /*
  159.  * Forms can be treated as arrays of their elements, so all named properties
  160.  * have negative slot numbers < -1.
  161.  */
  162. enum form_slot {
  163.     FORM_LENGTH         = -1,
  164.     FORM_NAME           = -2,
  165.     FORM_ELEMENTS       = -3,
  166.     FORM_METHOD         = -4,
  167.     FORM_ACTION         = -5,
  168.     FORM_ENCODING       = -6,
  169.     FORM_TARGET         = -7
  170. };
  171.  
  172. static char form_action_str[] = "action";
  173.  
  174. static JSPropertySpec form_props[] = {
  175.     {"length",          FORM_LENGTH,    JSPROP_ENUMERATE | JSPROP_READONLY},
  176.     {"name",            FORM_NAME,      JSPROP_ENUMERATE},
  177.     {"elements",        FORM_ELEMENTS,  JSPROP_ENUMERATE | JSPROP_READONLY},
  178.     {"method",          FORM_METHOD,    JSPROP_ENUMERATE},
  179.     {form_action_str,   FORM_ACTION,    JSPROP_ENUMERATE},
  180.     {"encoding",        FORM_ENCODING,  JSPROP_ENUMERATE},
  181.     {"target",          FORM_TARGET,    JSPROP_ENUMERATE},
  182.     {0}
  183. };
  184.  
  185. typedef struct JSForm {
  186.     JSObjectArray   object_array;
  187.     JSObject        *form_object;
  188.     uint32        form_event_mask;
  189.     int32           layer_id;
  190.     intn            form_id;
  191.     JSString        *name;
  192.     PRHashTable     *form_element_map;   /* Map from element id to object */
  193. } JSForm;
  194.  
  195. #define form_decoder    object_array.decoder
  196. #define form_length     object_array.length
  197.  
  198. typedef struct FormMethodMap {
  199.     char                *name;
  200.     uint                code;
  201. } FormMethodMap;
  202.  
  203. static FormMethodMap form_method_map[] = {
  204.     {"get",             FORM_METHOD_GET},
  205.     {"post",            FORM_METHOD_POST},
  206.     {0}
  207. };
  208.  
  209. static char *
  210. form_method_name(uint code)
  211. {
  212.     FormMethodMap *mm;
  213.  
  214.     for (mm = form_method_map; mm->name; mm++)
  215.     if (mm->code == code)
  216.         return mm->name;
  217.     return "unknown";
  218. }
  219.  
  220. static int
  221. form_method_code(const char *name)
  222. {
  223.     FormMethodMap *mm;
  224.  
  225.     for (mm = form_method_map; mm->name; mm++)
  226.     if (XP_STRCASECMP(mm->name, name) == 0)
  227.         return mm->code;
  228.     return -1;
  229. }
  230.  
  231. extern JSClass lm_form_class;
  232.  
  233. PR_STATIC_CALLBACK(JSBool)
  234. form_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  235. {
  236.     JSForm *form;
  237.     MWContext *context;
  238.     lo_FormData *form_data;
  239.     jsint count;
  240.     JSString *str;
  241.     LO_Element **ele_list;
  242.     LO_FormElementStruct *first_ele;
  243.     uint first_index;
  244.     jsint slot;
  245.  
  246.     if (!JSVAL_IS_INT(id))
  247.     return JS_TRUE;
  248.  
  249.     slot = JSVAL_TO_INT(id);
  250.  
  251.     form = JS_GetInstancePrivate(cx, obj, &lm_form_class, NULL);
  252.     if (!form)
  253.     return JS_TRUE;
  254.     context = form->form_decoder->window_context;
  255.     if (!context) return JS_TRUE;
  256.  
  257.     LO_LockLayout();
  258.     form_data = LO_GetFormDataByID(context, form->layer_id, form->form_id);
  259.     if (!form_data)
  260.     goto good;
  261.  
  262.     switch (slot) {
  263.       case FORM_LENGTH:
  264.         count = LO_EnumerateFormElements(context, form_data);
  265.         if (count > form->form_length)
  266.             form->form_length = count;
  267.         *vp = INT_TO_JSVAL(form->form_length);
  268.         goto good;
  269.  
  270.       case FORM_NAME:
  271.     str = lm_LocalEncodingToStr(context, (char *)form_data->name);
  272.         break;
  273.  
  274.       case FORM_ELEMENTS:
  275.         *vp = OBJECT_TO_JSVAL(form->form_object);
  276.         goto good;
  277.  
  278.       case FORM_METHOD:
  279.         str = JS_NewStringCopyZ(cx, form_method_name(form_data->method));
  280.         break;
  281.  
  282.       case FORM_ACTION:
  283.         str = JS_NewStringCopyZ(cx, (char *)form_data->action);
  284.         break;
  285.  
  286.       case FORM_ENCODING:
  287.         str = JS_NewStringCopyZ(cx, (char *)form_data->encoding);
  288.         break;
  289.  
  290.       case FORM_TARGET:
  291.         str = JS_NewStringCopyZ(cx, (char *)form_data->window_target);
  292.         break;
  293.  
  294.       default:
  295.         if ((uint)slot >= (uint)form_data->form_ele_cnt) {
  296.         /* Don't mess with a user-defined or method property. */
  297.         goto good;
  298.         }
  299.  
  300.     PA_LOCK(ele_list, LO_Element **, form_data->form_elements);
  301.     first_ele = (LO_FormElementStruct *)ele_list[0];
  302.     first_index = (uint)first_ele->element_index;
  303.     PA_UNLOCK(form_data->form_elements);
  304.  
  305.     *vp = OBJECT_TO_JSVAL(LM_ReflectFormElement(context, form->layer_id,
  306.                                                     form->form_id,
  307.                                                     first_index + slot, NULL));
  308.         goto good;
  309.     }
  310.  
  311.     LO_UnlockLayout();
  312.  
  313.     /* Common tail code for string-type properties. */
  314.     if (!str)
  315.         return JS_FALSE;
  316.     *vp = STRING_TO_JSVAL(str);
  317.     return JS_TRUE;
  318.  
  319. good:
  320.     LO_UnlockLayout();
  321.     return JS_TRUE;
  322. }
  323.  
  324. PR_STATIC_CALLBACK(JSBool)
  325. form_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  326. {
  327.     JSForm *form;
  328.     MWContext *context;
  329.     lo_FormData *form_data;
  330.     const char *value;
  331.     jsint slot;
  332.  
  333.     form = JS_GetInstancePrivate(cx, obj, &lm_form_class, NULL);
  334.     if (!form)
  335.     return JS_TRUE;
  336.     context = form->form_decoder->window_context;
  337.     if (!context)
  338.     return JS_TRUE;
  339.  
  340.     if (!JSVAL_IS_INT(id))
  341.     return JS_TRUE;
  342.  
  343.     slot = JSVAL_TO_INT(id);
  344.  
  345.     LO_LockLayout();
  346.     form_data = LO_GetFormDataByID(context, form->layer_id, form->form_id);
  347.     if (!form_data)
  348.     goto good;
  349.  
  350.     if (!JSVAL_IS_STRING(*vp) &&
  351.     !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) {
  352.     goto bad;
  353.     }
  354.  
  355.     value = JS_GetStringBytes(JSVAL_TO_STRING(*vp));
  356.     switch (slot) {
  357.       case FORM_METHOD:
  358.     form_data->method = form_method_code(value);
  359.     break;
  360.  
  361.       case FORM_ACTION:
  362.     value = lm_CheckURL(cx, value, JS_TRUE);
  363.     if (!value)
  364.         goto bad;
  365.     if (!lm_SaveParamString(cx, &form_data->action, value))
  366.         goto bad;
  367.     XP_FREE((char *)value);
  368.     break;
  369.  
  370.       case FORM_ENCODING:
  371.     if (!lm_SaveParamString(cx, &form_data->encoding, value))
  372.         goto bad;
  373.     break;
  374.  
  375.       case FORM_TARGET:
  376.     if (!lm_CheckWindowName(cx, value))
  377.         goto bad;
  378.     if (!lm_SaveParamString(cx, &form_data->window_target, value))
  379.         goto bad;
  380.     break;
  381.     }
  382.  
  383.     LO_UnlockLayout();
  384.     return form_getProperty(cx, obj, id, vp);
  385. good:
  386.     LO_UnlockLayout();
  387.     return JS_TRUE;
  388. bad:
  389.     LO_UnlockLayout();
  390.     return JS_FALSE;
  391.  
  392. }
  393.  
  394. PR_STATIC_CALLBACK(JSBool)
  395. form_list_properties(JSContext *cx, JSObject *obj)
  396. {
  397.     JSForm *form;
  398.     MWContext *context;
  399.     lo_FormData *form_data;
  400.  
  401.     form = JS_GetPrivate(cx, obj);
  402.     if (!form)
  403.     return JS_TRUE;
  404.     context = form->form_decoder->window_context;
  405.     if (!context)
  406.     return JS_TRUE;
  407.  
  408.     LO_LockLayout();
  409.     form_data = LO_GetFormDataByID(context, form->layer_id, form->form_id);
  410.     if (!form_data) {
  411.     LO_UnlockLayout();
  412.     return JS_TRUE;
  413.     }
  414.     /* XXX should return FALSE on reflection error */
  415.     (void) LO_EnumerateFormElements(context, form_data);
  416.     LO_UnlockLayout();
  417.     return JS_TRUE;
  418. }
  419.  
  420. PR_STATIC_CALLBACK(JSBool)
  421. form_resolve_name(JSContext *cx, JSObject *obj, jsval id)
  422. {
  423.     JSForm *form;
  424.     MWContext *context;
  425.     lo_FormData *form_data;
  426.     LO_Element **ele_list;
  427.     LO_FormElementStruct *form_ele;
  428.     lo_FormElementMinimalData *min_data;
  429.     int32 i;
  430.     const char * name;
  431.  
  432.     if (!JSVAL_IS_STRING(id))
  433.     return JS_TRUE;
  434.  
  435.     name = JS_GetStringBytes(JSVAL_TO_STRING(id));
  436.  
  437.     form = JS_GetPrivate(cx, obj);
  438.     if (!form)
  439.     return JS_TRUE;
  440.     context = form->form_decoder->window_context;
  441.     if (!context)
  442.     return JS_TRUE;
  443.  
  444.     LO_LockLayout();
  445.     form_data = LO_GetFormDataByID(context, form->layer_id, form->form_id);
  446.     if (!form_data) {
  447.     LO_UnlockLayout();
  448.     return JS_TRUE;
  449.     }
  450.  
  451.     PA_LOCK(ele_list, LO_Element **, form_data->form_elements);
  452.     for (i = 0; i < form_data->form_ele_cnt; i++) {
  453.     if (ele_list[i]->type != LO_FORM_ELE)
  454.         continue;
  455.     form_ele = (LO_FormElementStruct *)ele_list[i];
  456.     if (!form_ele->element_data)
  457.         continue;
  458.     min_data = &form_ele->element_data->ele_minimal;
  459.     if (min_data->name && XP_STRCMP((char *)min_data->name, name) == 0)
  460.         (void) LM_ReflectFormElement(context, form->layer_id,
  461.                                          form->form_id,
  462.                      form_ele->element_index, NULL);
  463.     }
  464.  
  465.     PA_UNLOCK(form_data->form_elements);
  466.     LO_UnlockLayout();
  467.     return JS_TRUE;
  468. }
  469.  
  470. PR_STATIC_CALLBACK(void)
  471. form_finalize(JSContext *cx, JSObject *obj)
  472. {
  473.     JSForm *form;
  474.     MochaDecoder *decoder;
  475.     MWContext *context;
  476.     lo_FormData *form_data;
  477.  
  478.     form = JS_GetPrivate(cx, obj);
  479.     if (!form)
  480.     return;
  481.     decoder = form->form_decoder;
  482.     context = decoder->window_context;
  483.     if (context) {
  484.     LO_LockLayout();
  485.     form_data = LO_GetFormDataByID(context, form->layer_id, form->form_id);
  486.     if (form_data && form_data->mocha_object == obj)
  487.         form_data->mocha_object = NULL;
  488.     LO_UnlockLayout();
  489.     }
  490.     DROP_BACK_COUNT(decoder);
  491.     if (form->form_element_map)
  492.         PR_HashTableDestroy(form->form_element_map);
  493.     JS_free(cx, form);
  494. }
  495.  
  496. JSClass lm_form_class = {
  497.     "Form", JSCLASS_HAS_PRIVATE,
  498.     JS_PropertyStub, JS_PropertyStub, form_getProperty, form_setProperty,
  499.     form_list_properties, form_resolve_name, JS_ConvertStub, form_finalize
  500. };
  501.  
  502. PR_STATIC_CALLBACK(JSBool)
  503. Form(JSContext *cx, JSObject *obj,
  504.      uint argc, jsval *argv, jsval *rval)
  505. {
  506.     return JS_TRUE;
  507. }
  508.  
  509. static JSBool
  510. form_native_prolog(JSContext *cx, JSObject *obj, jsval *argv,
  511.            JSForm **formp, lo_FormData **form_datap)
  512. {
  513.     JSForm *form;
  514.     MochaDecoder *decoder;
  515.     lo_FormData *form_data;
  516.  
  517.     if (!JS_InstanceOf(cx, obj, &lm_form_class, argv))
  518.         return JS_FALSE;
  519.     form = JS_GetPrivate(cx, obj);
  520.     if (!form) {
  521.     *formp = NULL;
  522.     *form_datap = NULL;
  523.     return JS_TRUE;
  524.     }
  525.     decoder = form->form_decoder;
  526.     form_data = decoder->window_context
  527.         ? LO_GetFormDataByID(decoder->window_context,
  528.                                      form->layer_id,
  529.                                      form->form_id)
  530.         : 0;
  531.     *formp = form;
  532.     *form_datap = form_data;
  533.     return JS_TRUE;
  534. }
  535.  
  536. PR_STATIC_CALLBACK(JSBool)
  537. form_reset(JSContext *cx, JSObject *obj,
  538.        uint argc, jsval *argv, jsval *rval)
  539. {
  540.     JSForm *form;
  541.     lo_FormData *form_data;
  542.     LO_Element **ele_list;
  543.  
  544.     if (!form_native_prolog(cx, obj, argv, &form, &form_data))
  545.         return JS_FALSE;
  546.     if (form_data && form_data->form_ele_cnt > 0) {
  547.     /* There is no form LO_Element; use the first thing in the form. */
  548.     PA_LOCK(ele_list, LO_Element **, form_data->form_elements);
  549.     ET_lo_ResetForm(form->form_decoder->window_context, ele_list[0]);
  550.     PA_UNLOCK(form_data->form_elements);
  551.     }
  552.     return JS_TRUE;
  553. }
  554.  
  555. PR_STATIC_CALLBACK(JSBool)
  556. form_submit(JSContext *cx, JSObject *obj,
  557.         uint argc, jsval *argv, jsval *rval)
  558. {
  559.     JSForm *form;
  560.     lo_FormData *form_data;
  561.     JSBool ok;
  562.     MochaDecoder *decoder;
  563.     MWContext *context;
  564.     LO_Element **ele_list, *element;
  565.  
  566.     if (!form_native_prolog(cx, obj, argv, &form, &form_data))
  567.         return JS_FALSE;
  568.     if (!form_data)
  569.         return JS_TRUE;
  570.  
  571.     switch (NET_URL_Type((const char *) form_data->action)) {
  572.       case MAILTO_TYPE_URL:
  573.       case NEWS_TYPE_URL:
  574.     /* only OK if we are a signed script */
  575.     ok = lm_CanAccessTarget(cx, JSTARGET_UNIVERSAL_SEND_MAIL);
  576.     break;
  577.       default:
  578.     ok = JS_TRUE;
  579.     break;
  580.     }
  581.  
  582.     if (!ok) {
  583.     /* XXX silently fail this mailto: or news: form post. */
  584.     return JS_TRUE;
  585.     }
  586.  
  587.     if (form_data->form_ele_cnt > 0) {
  588.     decoder = form->form_decoder;
  589.     context = decoder->window_context;
  590.     PA_LOCK(ele_list, LO_Element **, form_data->form_elements);
  591.     element = ele_list[0];
  592.     PA_UNLOCK(form_data->form_elements);
  593.     ET_fe_SubmitInputElement(context, element);
  594.  
  595.     }
  596.  
  597.     return JS_TRUE;
  598. }
  599.  
  600. static JSFunctionSpec form_methods[] = {
  601.     {"reset",    form_reset,    0},
  602.     {"submit",    form_submit,    0},
  603.     {0}
  604. };
  605.  
  606. static JSBool
  607. form_event(MWContext *context, LO_Element *element, JSEvent *event, char *method)
  608. {
  609.     lo_FormData *form_data;
  610.     JSObject *obj;
  611.     JSForm *form;
  612.     JSBool ok;
  613.     jsval rval;
  614.  
  615.     if (element->type != LO_FORM_ELE &&
  616.     (element->type != LO_IMAGE || !element->lo_image.image_attr)) {
  617.         LO_UnlockLayout();
  618.     return JS_TRUE;
  619.     }
  620.     form_data = LO_GetFormDataByID(context,
  621.                    (element->type == LO_IMAGE)
  622.                                    ? element->lo_image.image_attr->layer_id
  623.                                    : element->lo_form.layer_id,
  624.                    (element->type == LO_IMAGE)
  625.                    ? element->lo_image.image_attr->form_id
  626.                    : element->lo_form.form_id);
  627.     if (!form_data || !(obj = form_data->mocha_object)) {
  628.         LO_UnlockLayout();
  629.     return JS_TRUE;
  630.     }
  631.  
  632.     
  633.     LO_UnlockLayout();
  634.  
  635.     form = JS_GetPrivate(context->mocha_context, obj);
  636.     if (!form) 
  637.     return JS_TRUE;
  638.  
  639.     if (form->form_event_mask & event->type)
  640.     return JS_TRUE;
  641.  
  642.     form->form_event_mask |= event->type;
  643.     ok = lm_SendEvent(context, obj, event, &rval);
  644.     form->form_event_mask &= ~event->type;
  645.  
  646.     if (ok && rval == JSVAL_FALSE)
  647.     return JS_FALSE;
  648.     return JS_TRUE;
  649. }
  650.  
  651. JSBool
  652. lm_SendOnReset(MWContext *context, JSEvent *event, LO_Element *element)
  653. {
  654.     return form_event(context, element, event, lm_onReset_str);
  655. }
  656.  
  657. JSBool
  658. lm_SendOnSubmit(MWContext *context, JSEvent *event, LO_Element *element)
  659. {
  660.     return form_event(context, element, event, lm_onSubmit_str);
  661. }
  662.  
  663. #ifdef DEBUG_brendan
  664. static char *
  665. form_name(lo_FormData *form_data)
  666. {
  667.     static char buf[20];
  668.  
  669.     if (form_data->name)
  670.     return (char *)form_data->name;
  671.     PR_snprintf(buf, sizeof buf, "$form%d", form_data->id - 1);
  672.     return buf;
  673. }
  674. #endif
  675.  
  676. JSObject *
  677. LM_ReflectForm(MWContext *context, lo_FormData *form_data, PA_Tag * tag,
  678.                int32 layer_id, uint index)
  679. {
  680.     JSObject *obj, *array_obj, *prototype, *document;
  681.     MochaDecoder *decoder;
  682.     JSContext *cx;
  683.     JSForm *form;
  684.     JSBool ok;
  685.     lo_TopState *top_state;
  686.     PRHashTable *map;
  687.  
  688.     /* if we got passed an index get the form data by index and don't
  689.      *   trust the lo_FormData that was passed in
  690.      */
  691.     if (index)
  692.     form_data = LO_GetFormDataByID(context, layer_id, index);
  693.  
  694.     if (!form_data)
  695.     return NULL;
  696.  
  697.     obj = form_data->mocha_object;
  698.     if (obj)
  699.         return obj;
  700.  
  701.     decoder = LM_GetMochaDecoder(context);
  702.     if (!decoder)
  703.         return NULL;
  704.     cx = decoder->js_context;
  705.  
  706.     top_state = lo_GetMochaTopState(context);
  707.     if (top_state->resize_reload) {
  708.         map = lm_GetIdToObjectMap(decoder);
  709.  
  710.         if (map)
  711.             obj = (JSObject *)PR_HashTableLookup(map,
  712.                              LM_GET_MAPPING_KEY(LM_FORMS, layer_id, index));
  713.         if (obj) {
  714.             form_data->mocha_object = obj;
  715.             goto out;
  716.         }
  717.     }
  718.  
  719.     /* Get the document object that will hold this form */
  720.     document = lm_GetDocumentFromLayerId(decoder, layer_id);
  721.     if (!document)
  722.         goto out;
  723.  
  724.     array_obj = lm_GetFormArray(decoder, document);
  725.     if (!array_obj)
  726.     goto out;
  727.  
  728.     prototype = decoder->form_prototype;
  729.     if (!prototype) {
  730.         prototype = JS_InitClass(cx, decoder->window_object,
  731.                  decoder->event_receiver_prototype,
  732.                  &lm_form_class,
  733.                  Form, 0, form_props, form_methods,
  734.                  NULL, NULL);
  735.         if (!prototype)
  736.         goto out;
  737.         decoder->form_prototype = prototype;
  738.     }
  739.  
  740.     form = JS_malloc(cx, sizeof *form);
  741.     if (!form)
  742.     goto out;
  743.     XP_BZERO(form, sizeof *form);
  744.  
  745.     obj = JS_NewObject(cx, &lm_form_class, prototype, document);
  746.     if (!obj || !JS_SetPrivate(cx, obj, form)) {
  747.         JS_free(cx, form);
  748.         obj = NULL;
  749.         goto out;
  750.     }
  751.     if (form_data->name) {
  752.         ok = JS_DefineProperty(cx, document,
  753.                    (char *) form_data->name,
  754.                    OBJECT_TO_JSVAL(obj),
  755.                    NULL, NULL,
  756.                    JSPROP_ENUMERATE | JSPROP_READONLY);
  757.         if (!ok) {
  758.         obj = NULL;
  759.         goto out;
  760.         }
  761.     }
  762.  
  763.     /* put it in the form array */
  764.     if (!lm_AddObjectToArray(cx, array_obj, (char *) form_data->name,
  765.                  index - 1, obj)) {
  766.     obj = NULL;
  767.     goto out;
  768.     }
  769.  
  770.     /* Put it in the index to object hash table */
  771.     map = lm_GetIdToObjectMap(decoder);
  772.     if (map)
  773.         PR_HashTableAdd(map, LM_GET_MAPPING_KEY(LM_FORMS, layer_id, index),
  774.                         obj);
  775.  
  776.     form->form_decoder = HOLD_BACK_COUNT(decoder);
  777.     form->form_element_map = NULL;
  778.     form->form_object = obj;
  779.     form->form_id = form_data->id;
  780.     form->layer_id = layer_id;
  781.     form_data->mocha_object = obj;
  782.  
  783.     /* see if there are any other attributes that we should be
  784.      *  adding to this object
  785.      */
  786.     if(tag) {
  787.         PA_Block onreset, onsubmit, id;
  788.  
  789.     /* don't hold the layout lock across compiles */
  790.     LO_UnlockLayout();
  791.  
  792.         onreset  = lo_FetchParamValue(context, tag, PARAM_ONRESET);
  793.         onsubmit = lo_FetchParamValue(context, tag, PARAM_ONSUBMIT);
  794.         id = lo_FetchParamValue(context, tag, PARAM_ID);
  795.  
  796.     ok = JS_TRUE;
  797.     if (onsubmit) {
  798.         ok = lm_CompileEventHandler(decoder, id, tag->data,
  799.                         tag->newline_count, obj,
  800.                         PARAM_ONSUBMIT, onsubmit);
  801.         PA_FREE(onsubmit);
  802.     }
  803.     if (onreset) {
  804.         ok &= lm_CompileEventHandler(decoder, id, tag->data,
  805.                      tag->newline_count, obj,
  806.                          PARAM_ONRESET, onreset);
  807.         PA_FREE(onreset);
  808.     }
  809.     if (!ok)
  810.         obj = NULL;
  811.     if (id)
  812.         PA_FREE(id);
  813.         
  814.     LO_LockLayout();
  815.     }
  816.  
  817. out:
  818.     LM_PutMochaDecoder(decoder);
  819.     return obj;
  820. }
  821.  
  822. JSObject *
  823. lm_GetFormObjectByID(MWContext * context, int32 layer_id, uint form_id)
  824. {
  825.     lo_FormData *form_data;
  826.  
  827.     form_data = LO_GetFormDataByID(context, layer_id, form_id);
  828.     if (!form_data)
  829.     return NULL;
  830.     return form_data->mocha_object;
  831. }
  832.  
  833. LO_FormElementStruct *
  834. lm_GetFormElementByIndex(JSContext * cx, JSObject *form_obj, int32 index)
  835. {
  836.     JSForm *form;
  837.     MWContext *context;
  838.     lo_FormData *form_data;
  839.  
  840.     if (!form_obj)
  841.     return NULL;
  842.     form = JS_GetPrivate(cx, form_obj);
  843.     if (!form)
  844.     return NULL;
  845.     context = form->form_decoder->window_context;
  846.     if (!context)
  847.     return NULL;
  848.     form_data = LO_GetFormDataByID(context, form->layer_id, form->form_id);
  849.     if (!form_data)
  850.     return NULL;
  851.     return LO_GetFormElementByIndex(form_data, index);
  852. }
  853.  
  854. PRIVATE JSBool
  855. lm_normalize_element_index(lo_FormData *form_data, uint *index)
  856. {
  857.     LO_Element **ele_list;
  858.     LO_FormElementStruct *first_ele;
  859.     uint first_index;
  860.  
  861.     /* XXX confine this to laymocha.c where LO_GetFormByElementIndex() lives */
  862.     if (form_data->form_ele_cnt == 0) {
  863.     *index = 0;
  864.     } else {
  865.     ele_list = (LO_Element **) form_data->form_elements;
  866.     first_ele = (LO_FormElementStruct *)ele_list[0];
  867.     first_index = (uint)first_ele->element_index;
  868.     XP_ASSERT(*index >= first_index);
  869.     if (*index < first_index)
  870.         return JS_FALSE;
  871.     *index -= first_index;
  872.     }
  873.  
  874.     return JS_TRUE;
  875. }
  876.  
  877. JSBool
  878. lm_AddFormElement(JSContext *cx, JSObject *form_obj, JSObject *ele_obj,
  879.           char *name, uint index)
  880. {
  881.     JSForm *form;
  882.     MWContext *context;
  883.     lo_FormData *form_data;
  884.  
  885.     form = JS_GetPrivate(cx, form_obj);
  886.     if (!form)
  887.     return JS_TRUE;
  888.     context = form->form_decoder->window_context;
  889.     if (!context)
  890.     return JS_FALSE;
  891.     form_data = LO_GetFormDataByID(context, form->layer_id, form->form_id);
  892.     if (!form_data)
  893.     return JS_FALSE;
  894.  
  895.     if (!lm_normalize_element_index(form_data, &index))
  896.         return JS_FALSE;
  897.  
  898.     if (!form->form_element_map)
  899.         form->form_element_map = PR_NewHashTable(LM_FORM_ELEMENT_MAP_SIZE,
  900.                                                  lm_KeyHash,
  901.                                                  PR_CompareValues,
  902.                                                  PR_CompareValues,
  903.                                                  NULL, NULL);
  904.     if (!form->form_element_map)
  905.         return JS_FALSE;
  906.  
  907.     PR_HashTableAdd(form->form_element_map, (void *)index, ele_obj);
  908.  
  909.     /* put it in the form elememt array */
  910.     return (lm_AddObjectToArray(cx, form_obj, name, index, ele_obj));
  911.  
  912. }
  913.  
  914. JSObject *
  915. lm_GetFormElementFromMapping(JSContext *cx, JSObject *form_obj, uint32 index)
  916. {
  917.     JSForm *form;
  918.     MWContext *context;
  919.     lo_FormData *form_data;
  920.  
  921.     form = JS_GetPrivate(cx, form_obj);
  922.     if (!form || !form->form_element_map)
  923.         return NULL;
  924.  
  925.     context = form->form_decoder->window_context;
  926.     if (!context)
  927.     return NULL;
  928.     form_data = LO_GetFormDataByID(context, form->layer_id, form->form_id);
  929.     if (!form_data)
  930.     return NULL;
  931.  
  932.     /*
  933.      * This converts element_id to index within this form. Need to do
  934.      * this here, because it also happens when we put it into the hash
  935.      * table.
  936.      */
  937.     if (!lm_normalize_element_index(form_data, (uint *)&index))
  938.         return NULL;
  939.  
  940.     return (JSObject *)PR_HashTableLookup(form->form_element_map,
  941.                                           (void *)index);
  942. }
  943.  
  944.  
  945. JSBool
  946. lm_ReflectRadioButtonArray(MWContext *context, int32 layer_id, intn form_id,
  947.                            const char *name, PA_Tag * tag)
  948. {
  949.     lo_FormData *form_data;
  950.     JSBool ok;
  951.     LO_Element **ele_list;
  952.     int32 i, element_index;
  953.     LO_FormElementStruct *form_ele;
  954.     LO_FormElementData *data;
  955.  
  956.     form_data = LO_GetFormDataByID(context, layer_id, form_id);
  957.     if (!form_data)
  958.         return JS_FALSE;
  959.     ok = JS_TRUE;
  960.     for (i = 0; i < form_data->form_ele_cnt; i++) {
  961.     /*
  962.      * Resample since the reflect call may release the layout
  963.      *   lock and the element list might get reallocated
  964.      */
  965.         ele_list = (LO_Element **) form_data->form_elements;
  966.         form_ele = (LO_FormElementStruct *)ele_list[i];
  967.  
  968.     /*
  969.      * If both the current element name and the passed in name are
  970.      *   non-NULL they must be equal.  If name is NULL reflect all
  971.      *   radio buttons
  972.      */
  973.     data = form_ele->element_data;
  974.         if (data &&
  975.             ((data->ele_minimal.name && name) ?
  976.          !XP_STRCMP((char *)data->ele_minimal.name, name) :
  977.          data->type == FORM_TYPE_RADIO) &&
  978.             form_ele->mocha_object == NULL) {
  979.  
  980.         element_index = form_ele->element_index;
  981.         ok = (JSBool)
  982.          (LM_ReflectFormElement(context, layer_id, form_id,
  983.                     element_index,
  984.                     (tag->lo_data == (void *)element_index)
  985.                     ? tag : NULL)
  986.           != NULL);
  987.             if (!ok)
  988.                 break;
  989.         }
  990.     }
  991.     return ok;
  992. }
  993.