Subversion Repositories Kolibri OS

Rev

Rev 3584 | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
  3.  * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
  4.  * Copyright 2004 John Tytgat <joty@netsurf-browser.org>
  5.  * Copyright 2005-9 John-Mark Bell <jmb@netsurf-browser.org>
  6.  * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
  7.  * Copyright 2010 Michael Drake <tlsa@netsurf-browser.org>
  8.  *
  9.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  10.  *
  11.  * NetSurf is free software; you can redistribute it and/or modify
  12.  * it under the terms of the GNU General Public License as published by
  13.  * the Free Software Foundation; version 2 of the License.
  14.  *
  15.  * NetSurf is distributed in the hope that it will be useful,
  16.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.  * GNU General Public License for more details.
  19.  *
  20.  * You should have received a copy of the GNU General Public License
  21.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  22.  */
  23.  
  24. /** \file
  25.  * Form handling functions (implementation).
  26.  */
  27.  
  28. #include <assert.h>
  29. #include <ctype.h>
  30. #include <limits.h>
  31. #include <stdbool.h>
  32. #include <stdio.h>
  33. #include <string.h>
  34. #include <dom/dom.h>
  35. #include "content/fetch.h"
  36. #include "content/hlcache.h"
  37. #include "css/css.h"
  38. #include "css/utils.h"
  39. #include "desktop/browser.h"
  40. #include "desktop/gui.h"
  41. #include "desktop/mouse.h"
  42. #include "desktop/knockout.h"
  43. #include "desktop/plot_style.h"
  44. #include "desktop/plotters.h"
  45. #include "desktop/scrollbar.h"
  46. #include "render/box.h"
  47. #include "render/font.h"
  48. #include "render/form.h"
  49. #include "render/html.h"
  50. #include "render/html_internal.h"
  51. #include "render/layout.h"
  52. #include "utils/log.h"
  53. #include "utils/messages.h"
  54. #include "utils/talloc.h"
  55. #include "utils/url.h"
  56. #include "utils/utf8.h"
  57. #include "utils/utils.h"
  58.  
  59. #define MAX_SELECT_HEIGHT 210
  60. #define SELECT_LINE_SPACING 0.2
  61. #define SELECT_BORDER_WIDTH 1
  62. #define SELECT_SELECTED_COLOUR 0xDB9370
  63.  
  64. struct form_select_menu {
  65.         int line_height;
  66.         int width, height;
  67.         struct scrollbar *scrollbar;
  68.         int f_size;
  69.         bool scroll_capture;
  70.         select_menu_redraw_callback callback;
  71.         void *client_data;
  72.         struct content *c;
  73. };
  74.  
  75. static plot_style_t plot_style_fill_selected = {
  76.         .fill_type = PLOT_OP_TYPE_SOLID,
  77.         .fill_colour = SELECT_SELECTED_COLOUR,
  78. };
  79.  
  80. static plot_font_style_t plot_fstyle_entry = {
  81.         .family = PLOT_FONT_FAMILY_SANS_SERIF,
  82.         .weight = 400,
  83.         .flags = FONTF_NONE,
  84.         .background = 0xffffff,
  85.         .foreground = 0x000000,
  86. };
  87.  
  88. static char *form_textarea_value(struct form_control *textarea);
  89. static char *form_acceptable_charset(struct form *form);
  90. static char *form_encode_item(const char *item, const char *charset,
  91.                 const char *fallback);
  92. static void form_select_menu_clicked(struct form_control *control,
  93.                 int x, int y);
  94. static void form_select_menu_scroll_callback(void *client_data,
  95.                 struct scrollbar_msg_data *scrollbar_data);
  96.  
  97. /**
  98.  * Create a struct form.
  99.  *
  100.  * \param  node    DOM node associated with form
  101.  * \param  action  URL to submit form to, or NULL for default
  102.  * \param  target  Target frame of form, or NULL for default
  103.  * \param  method  method and enctype
  104.  * \param  charset acceptable encodings for form submission, or NULL
  105.  * \param  doc_charset  encoding of containing document, or NULL
  106.  * \return  a new structure, or NULL on memory exhaustion
  107.  */
  108. struct form *form_new(void *node, const char *action, const char *target,
  109.                 form_method method, const char *charset,
  110.                 const char *doc_charset)
  111. {
  112.         struct form *form;
  113.  
  114.         form = calloc(1, sizeof *form);
  115.         if (!form)
  116.                 return NULL;
  117.  
  118.         form->action = strdup(action != NULL ? action : "");
  119.         if (form->action == NULL) {
  120.                 free(form);
  121.                 return NULL;
  122.         }
  123.  
  124.         form->target = target != NULL ? strdup(target) : NULL;
  125.         if (target != NULL && form->target == NULL) {
  126.                 free(form->action);
  127.                 free(form);
  128.                 return NULL;
  129.         }
  130.  
  131.         form->method = method;
  132.  
  133.         form->accept_charsets = charset != NULL ? strdup(charset) : NULL;
  134.         if (charset != NULL && form->accept_charsets == NULL) {
  135.                 free(form->target);
  136.                 free(form->action);
  137.                 free(form);
  138.                 return NULL;
  139.         }
  140.  
  141.         form->document_charset = doc_charset != NULL ? strdup(doc_charset)
  142.                                                      : NULL;
  143.         if (doc_charset && form->document_charset == NULL) {
  144.                 free(form->accept_charsets);
  145.                 free(form->target);
  146.                 free(form->action);
  147.                 free(form);
  148.                 return NULL;
  149.         }
  150.  
  151.         form->node = node;
  152.  
  153.         return form;
  154. }
  155.  
  156. /**
  157.  * Free a form, and any controls it owns.
  158.  *
  159.  * \param form  The form to free
  160.  *
  161.  * \note There may exist controls attached to box tree nodes which are not
  162.  * associated with any form. These will leak at present. Ideally, they will
  163.  * be cleaned up when the box tree is destroyed. As that currently happens
  164.  * via talloc, this won't happen. These controls are distinguishable, as their
  165.  * form field will be NULL.
  166.  */
  167. void form_free(struct form *form)
  168. {
  169.         struct form_control *c, *d;
  170.  
  171.         for (c = form->controls; c != NULL; c = d) {
  172.                 d = c->next;
  173.  
  174.                 form_free_control(c);
  175.         }
  176.  
  177.         free(form->action);
  178.         free(form->target);
  179.         free(form->accept_charsets);
  180.         free(form->document_charset);
  181.  
  182.         free(form);
  183. }
  184.  
  185. /**
  186.  * Create a struct form_control.
  187.  *
  188.  * \param  node  Associated DOM node
  189.  * \param  type  control type
  190.  * \return  a new structure, or NULL on memory exhaustion
  191.  */
  192. struct form_control *form_new_control(void *node, form_control_type type)
  193. {
  194.         struct form_control *control;
  195.  
  196.         control = calloc(1, sizeof *control);
  197.         if (control == NULL)
  198.                 return NULL;
  199.  
  200.         control->node = node;
  201.         control->type = type;
  202.  
  203.         return control;
  204. }
  205.  
  206.  
  207. /**
  208.  * Add a control to the list of controls in a form.
  209.  *
  210.  * \param form  The form to add the control to
  211.  * \param control  The control to add
  212.  */
  213. void form_add_control(struct form *form, struct form_control *control)
  214. {
  215.         control->form = form;
  216.  
  217.         if (form->controls != NULL) {
  218.                 assert(form->last_control);
  219.  
  220.                 form->last_control->next = control;
  221.                 control->prev = form->last_control;
  222.                 control->next = NULL;
  223.                 form->last_control = control;
  224.         } else {
  225.                 form->controls = form->last_control = control;
  226.         }
  227. }
  228.  
  229.  
  230. /**
  231.  * Free a struct form_control.
  232.  *
  233.  * \param  control  structure to free
  234.  */
  235. void form_free_control(struct form_control *control)
  236. {
  237.         free(control->name);
  238.         free(control->value);
  239.         free(control->initial_value);
  240.  
  241.         if (control->type == GADGET_SELECT) {
  242.                 struct form_option *option, *next;
  243.  
  244.                 for (option = control->data.select.items; option;
  245.                                 option = next) {
  246.                         next = option->next;
  247.                         free(option->text);
  248.                         free(option->value);
  249.                         free(option);
  250.                 }
  251.                 if (control->data.select.menu != NULL)
  252.                         form_free_select_menu(control);
  253.         }
  254.  
  255.         free(control);
  256. }
  257.  
  258.  
  259. /**
  260.  * Add an option to a form select control.
  261.  *
  262.  * \param  control   form control of type GADGET_SELECT
  263.  * \param  value     value of option, used directly (not copied)
  264.  * \param  text      text for option, used directly (not copied)
  265.  * \param  selected  this option is selected
  266.  * \return  true on success, false on memory exhaustion
  267.  */
  268. bool form_add_option(struct form_control *control, char *value, char *text,
  269.                 bool selected)
  270. {
  271.         struct form_option *option;
  272.  
  273.         assert(control);
  274.         assert(control->type == GADGET_SELECT);
  275.  
  276.         option = calloc(1, sizeof *option);
  277.         if (!option)
  278.                 return false;
  279.  
  280.         option->value = value;
  281.         option->text = text;
  282.  
  283.         /* add to linked list */
  284.         if (control->data.select.items == 0)
  285.                 control->data.select.items = option;
  286.         else
  287.                 control->data.select.last_item->next = option;
  288.         control->data.select.last_item = option;
  289.  
  290.         /* set selected */
  291.         if (selected && (control->data.select.num_selected == 0 ||
  292.                         control->data.select.multiple)) {
  293.                 option->selected = option->initial_selected = true;
  294.                 control->data.select.num_selected++;
  295.                 control->data.select.current = option;
  296.         }
  297.  
  298.         control->data.select.num_items++;
  299.  
  300.         return true;
  301. }
  302.  
  303.  
  304. /**
  305.  * Identify 'successful' controls.
  306.  *
  307.  * All text strings in the successful controls list will be in the charset most
  308.  * appropriate for submission. Therefore, no utf8_to_* processing should be
  309.  * performed upon them.
  310.  *
  311.  * \todo The chosen charset needs to be made available such that it can be
  312.  * included in the submission request (e.g. in the fetch's Content-Type header)
  313.  *
  314.  * \param  form           form to search for successful controls
  315.  * \param  submit_button  control used to submit the form, if any
  316.  * \param  successful_controls  updated to point to linked list of
  317.  *                        fetch_multipart_data, 0 if no controls
  318.  * \return  true on success, false on memory exhaustion
  319.  *
  320.  * See HTML 4.01 section 17.13.2.
  321.  */
  322. bool form_successful_controls(struct form *form,
  323.                 struct form_control *submit_button,
  324.                 struct fetch_multipart_data **successful_controls)
  325. {
  326.         struct form_control *control;
  327.         struct form_option *option;
  328.         struct fetch_multipart_data sentinel, *last_success, *success_new;
  329.         char *value = NULL;
  330.         bool had_submit = false;
  331.         char *charset;
  332.  
  333.         last_success = &sentinel;
  334.         sentinel.next = NULL;
  335.  
  336.         charset = form_acceptable_charset(form);
  337.         if (!charset)
  338.                 return false;
  339.  
  340. #define ENCODE_ITEM(i) form_encode_item((i), charset, form->document_charset)
  341.  
  342.         for (control = form->controls; control; control = control->next) {
  343.                 /* ignore disabled controls */
  344.                 if (control->disabled)
  345.                         continue;
  346.  
  347.                 /* ignore controls with no name */
  348.                 if (!control->name)
  349.                         continue;
  350.  
  351.                 switch (control->type) {
  352.                         case GADGET_HIDDEN:
  353.                         case GADGET_TEXTBOX:
  354.                         case GADGET_PASSWORD:
  355.                                 if (control->value)
  356.                                         value = ENCODE_ITEM(control->value);
  357.                                 else
  358.                                         value = ENCODE_ITEM("");
  359.                                 if (!value) {
  360.                                         LOG(("failed to duplicate value"
  361.                                                 "'%s' for control %s",
  362.                                                         control->value,
  363.                                                         control->name));
  364.                                         goto no_memory;
  365.                                 }
  366.                                 break;
  367.  
  368.                         case GADGET_RADIO:
  369.                         case GADGET_CHECKBOX:
  370.                                 /* ignore checkboxes and radio buttons which
  371.                                  * aren't selected */
  372.                                 if (!control->selected)
  373.                                         continue;
  374.                                 if (control->value)
  375.                                         value = ENCODE_ITEM(control->value);
  376.                                 else
  377.                                         value = ENCODE_ITEM("on");
  378.                                 if (!value) {
  379.                                         LOG(("failed to duplicate"
  380.                                                 "value '%s' for"
  381.                                                 "control %s",
  382.                                                 control->value,
  383.                                                 control->name));
  384.                                         goto no_memory;
  385.                                 }
  386.                                 break;
  387.  
  388.                         case GADGET_SELECT:
  389.                                 /* select */
  390.                                 for (option = control->data.select.items;
  391.                                                 option != NULL;
  392.                                                 option = option->next) {
  393.                                         if (!option->selected)
  394.                                                 continue;
  395.                                         success_new =
  396.                                                 malloc(sizeof(*success_new));
  397.                                         if (!success_new) {
  398.                                                 LOG(("malloc failed"));
  399.                                                 goto no_memory;
  400.                                         }
  401.                                         success_new->file = false;
  402.                                         success_new->name =
  403.                                                 ENCODE_ITEM(control->name);
  404.                                         success_new->value =
  405.                                                 ENCODE_ITEM(option->value);
  406.                                         success_new->next = NULL;
  407.                                         last_success->next = success_new;
  408.                                         last_success = success_new;
  409.                                         if (!success_new->name ||
  410.                                                 !success_new->value) {
  411.                                                 LOG(("strdup failed"));
  412.                                                 goto no_memory;
  413.                                         }
  414.                                 }
  415.  
  416.                                 continue;
  417.                                 break;
  418.  
  419.                         case GADGET_TEXTAREA:
  420.                                 {
  421.                                 char *v2;
  422.  
  423.                                 /* textarea */
  424.                                 value = form_textarea_value(control);
  425.                                 if (!value) {
  426.                                         LOG(("failed handling textarea"));
  427.                                         goto no_memory;
  428.                                 }
  429.                                 if (value[0] == 0) {
  430.                                         free(value);
  431.                                         continue;
  432.                                 }
  433.  
  434.                                 v2 = ENCODE_ITEM(value);
  435.                                 if (!v2) {
  436.                                         LOG(("failed handling textarea"));
  437.                                         free(value);
  438.                                         goto no_memory;
  439.                                 }
  440.  
  441.                                 free(value);
  442.                                 value = v2;
  443.                                 }
  444.                                 break;
  445.  
  446.                         case GADGET_IMAGE: {
  447.                                 /* image */
  448.                                 size_t len;
  449.                                 char *name;
  450.  
  451.                                 if (control != submit_button)
  452.                                         /* only the activated submit button
  453.                                          * is successful */
  454.                                         continue;
  455.  
  456.                                 name = ENCODE_ITEM(control->name);
  457.                                 if (name == NULL)
  458.                                         goto no_memory;
  459.  
  460.                                 len = strlen(name) + 3;
  461.  
  462.                                 /* x */
  463.                                 success_new = malloc(sizeof(*success_new));
  464.                                 if (!success_new) {
  465.                                         free(name);
  466.                                         LOG(("malloc failed"));
  467.                                         goto no_memory;
  468.                                 }
  469.                                 success_new->file = false;
  470.                                 success_new->name = malloc(len);
  471.                                 success_new->value = malloc(20);
  472.                                 if (!success_new->name ||
  473.                                                 !success_new->value) {
  474.                                         free(success_new->name);
  475.                                         free(success_new->value);
  476.                                         free(success_new);
  477.                                         free(name);
  478.                                         LOG(("malloc failed"));
  479.                                         goto no_memory;
  480.                                 }
  481.                                 sprintf(success_new->name, "%s.x", name);
  482.                                 sprintf(success_new->value, "%i",
  483.                                                 control->data.image.mx);
  484.                                 success_new->next = 0;
  485.                                 last_success->next = success_new;
  486.                                 last_success = success_new;
  487.  
  488.                                 /* y */
  489.                                 success_new = malloc(sizeof(*success_new));
  490.                                 if (!success_new) {
  491.                                         free(name);
  492.                                         LOG(("malloc failed"));
  493.                                         goto no_memory;
  494.                                 }
  495.                                 success_new->file = false;
  496.                                 success_new->name = malloc(len);
  497.                                 success_new->value = malloc(20);
  498.                                 if (!success_new->name ||
  499.                                                 !success_new->value) {
  500.                                         free(success_new->name);
  501.                                         free(success_new->value);
  502.                                         free(success_new);
  503.                                         free(name);
  504.                                         LOG(("malloc failed"));
  505.                                         goto no_memory;
  506.                                 }
  507.                                 sprintf(success_new->name, "%s.y", name);
  508.                                 sprintf(success_new->value, "%i",
  509.                                                 control->data.image.my);
  510.                                 success_new->next = 0;
  511.                                 last_success->next = success_new;
  512.                                 last_success = success_new;
  513.  
  514.                                 free(name);
  515.  
  516.                                 continue;
  517.                                 break;
  518.                         }
  519.  
  520.                         case GADGET_SUBMIT:
  521.                                 if (!submit_button && !had_submit)
  522.                                         /* no submit button specified, so
  523.                                          * use first declared in form */
  524.                                         had_submit = true;
  525.                                 else if (control != submit_button)
  526.                                         /* only the activated submit button
  527.                                          * is successful */
  528.                                         continue;
  529.                                 if (control->value)
  530.                                         value = ENCODE_ITEM(control->value);
  531.                                 else
  532.                                         value = ENCODE_ITEM("");
  533.                                 if (!value) {
  534.                                         LOG(("failed to duplicate value"
  535.                                                 "'%s' for control %s",
  536.                                                         control->value,
  537.                                                         control->name));
  538.                                         goto no_memory;
  539.                                 }
  540.                                 break;
  541.  
  542.                         case GADGET_RESET:
  543.                                 /* ignore reset */
  544.                                 continue;
  545.                                 break;
  546.  
  547.                         case GADGET_FILE:
  548.                                 /* file */
  549.                                 /* Handling of blank file entries is
  550.                                  * implementation defined - we're perfectly
  551.                                  * within our rights to treat it as an
  552.                                  * unsuccessful control. Unfortunately, every
  553.                                  * other browser submits the field with
  554.                                  * a blank filename and no content. So,
  555.                                  * that's what we have to do, too.
  556.                                  */
  557.                                 success_new = malloc(sizeof(*success_new));
  558.                                 if (!success_new) {
  559.                                         LOG(("malloc failed"));
  560.                                         goto no_memory;
  561.                                 }
  562.                                 success_new->file = true;
  563.                                 success_new->name = ENCODE_ITEM(control->name);
  564.                                 success_new->value =
  565.                                                 ENCODE_ITEM(control->value ?
  566.                                                 control->value : "");
  567.                                 success_new->next = 0;
  568.                                 last_success->next = success_new;
  569.                                 last_success = success_new;
  570.                                 if (!success_new->name ||
  571.                                                 !success_new->value) {
  572.                                         LOG(("strdup failed"));
  573.                                         goto no_memory;
  574.                                 }
  575.  
  576.                                 continue;
  577.                                 break;
  578.  
  579.                         case GADGET_BUTTON:
  580.                                 /* Ignore it */
  581.                                 continue;
  582.                                 break;
  583.  
  584.                         default:
  585.                                 assert(0);
  586.                                 break;
  587.                 }
  588.  
  589.                 success_new = malloc(sizeof(*success_new));
  590.                 if (!success_new) {
  591.                         LOG(("malloc failed"));
  592.                         goto no_memory;
  593.                 }
  594.                 success_new->file = false;
  595.                 success_new->name = ENCODE_ITEM(control->name);
  596.                 success_new->value = value;
  597.                 success_new->next = NULL;
  598.                 last_success->next = success_new;
  599.                 last_success = success_new;
  600.                 if (!success_new->name) {
  601.                         LOG(("failed to duplicate name '%s'",
  602.                                         control->name));
  603.                         goto no_memory;
  604.                 }
  605.         }
  606.  
  607.         *successful_controls = sentinel.next;
  608.         return true;
  609.  
  610. no_memory:
  611.         warn_user("NoMemory", 0);
  612.         fetch_multipart_data_destroy(sentinel.next);
  613.         return false;
  614.  
  615. #undef ENCODE_ITEM
  616. }
  617.  
  618.  
  619. /**
  620.  * Find the value for a textarea control.
  621.  *
  622.  * \param  textarea  control of type GADGET_TEXTAREA
  623.  * \return  the value as a UTF-8 string on heap, or 0 on memory exhaustion
  624.  */
  625. char *form_textarea_value(struct form_control *textarea)
  626. {
  627.         unsigned int len = 0;
  628.         char *value, *s;
  629.         struct box *text_box;
  630.  
  631.         /* Textarea may have no associated box if styled with display: none */
  632.         if (textarea->box == NULL) {
  633.                 /* Return the empty string: caller treats this as a
  634.                  * non-successful control. */
  635.                 return strdup("");
  636.         }
  637.  
  638.         /* find required length */
  639.         for (text_box = textarea->box->children->children; text_box;
  640.                         text_box = text_box->next) {
  641.                 if (text_box->type == BOX_TEXT)
  642.                         len += text_box->length + 1;
  643.                 else /* BOX_BR */
  644.                         len += 2;
  645.         }
  646.  
  647.         /* construct value */
  648.         s = value = malloc(len + 1);
  649.         if (!s)
  650.                 return NULL;
  651.  
  652.         for (text_box = textarea->box->children->children; text_box;
  653.                         text_box = text_box->next) {
  654.                 if (text_box->type == BOX_TEXT) {
  655.                         strncpy(s, text_box->text, text_box->length);
  656.                         s += text_box->length;
  657.                         if (text_box->next && text_box->next->type != BOX_BR)
  658.                                 /* only add space if this isn't
  659.                                  * the last box on a line (or in the area) */
  660.                                 *s++ = ' ';
  661.                 } else { /* BOX_BR */
  662.                         *s++ = '\r';
  663.                         *s++ = '\n';
  664.                 }
  665.         }
  666.         *s = 0;
  667.  
  668.         return value;
  669. }
  670.  
  671.  
  672. /**
  673.  * Encode controls using application/x-www-form-urlencoded.
  674.  *
  675.  * \param  form  form to which successful controls relate
  676.  * \param  control  linked list of fetch_multipart_data
  677.  * \param  query_string  iff true add '?' to the start of returned data
  678.  * \return  URL-encoded form, or 0 on memory exhaustion
  679.  */
  680.  
  681. static char *form_url_encode(struct form *form,
  682.                 struct fetch_multipart_data *control,
  683.                 bool query_string)
  684. {
  685.         char *name, *value;
  686.         char *s, *s2;
  687.         unsigned int len, len1, len_init;
  688.         url_func_result url_err;
  689.  
  690.         if (query_string)
  691.                 s = malloc(2);
  692.         else
  693.                 s = malloc(1);
  694.  
  695.         if (s == NULL)
  696.                 return NULL;
  697.  
  698.         if (query_string) {
  699.                 s[0] = '?';
  700.                 s[1] = '\0';
  701.                 len_init = len = 1;
  702.         } else {
  703.                 s[0] = '\0';
  704.                 len_init = len = 0;
  705.         }
  706.  
  707.         for (; control; control = control->next) {
  708.                 url_err = url_escape(control->name, 0, true, NULL, &name);
  709.                 if (url_err == URL_FUNC_NOMEM) {
  710.                         free(s);
  711.                         return NULL;
  712.                 }
  713.  
  714.                 assert(url_err == URL_FUNC_OK);
  715.  
  716.                 url_err = url_escape(control->value, 0, true, NULL, &value);
  717.                 if (url_err == URL_FUNC_NOMEM) {
  718.                         free(name);
  719.                         free(s);
  720.                         return NULL;
  721.                 }
  722.  
  723.                 assert(url_err == URL_FUNC_OK);
  724.  
  725.                 len1 = len + strlen(name) + strlen(value) + 2;
  726.                 s2 = realloc(s, len1 + 1);
  727.                 if (!s2) {
  728.                         free(value);
  729.                         free(name);
  730.                         free(s);
  731.                         return NULL;
  732.                 }
  733.                 s = s2;
  734.                 sprintf(s + len, "%s=%s&", name, value);
  735.                 len = len1;
  736.                 free(name);
  737.                 free(value);
  738.         }
  739.  
  740.         if (len > len_init)
  741.                 /* Replace trailing '&' */
  742.                 s[len - 1] = '\0';
  743.         return s;
  744. }
  745.  
  746. /**
  747.  * Find an acceptable character set encoding with which to submit the form
  748.  *
  749.  * \param form  The form
  750.  * \return Pointer to charset name (on heap, caller should free) or NULL
  751.  */
  752. char *form_acceptable_charset(struct form *form)
  753. {
  754.         char *temp, *c;
  755.  
  756.         if (!form)
  757.                 return NULL;
  758.  
  759.         if (!form->accept_charsets) {
  760.                 /* no accept-charsets attribute for this form */
  761.                 if (form->document_charset)
  762.                         /* document charset present, so use it */
  763.                         return strdup(form->document_charset);
  764.                 else
  765.                         /* no document charset, so default to 8859-1 */
  766.                         return strdup("ISO-8859-1");
  767.         }
  768.  
  769.         /* make temporary copy of accept-charsets attribute */
  770.         temp = strdup(form->accept_charsets);
  771.         if (!temp)
  772.                 return NULL;
  773.  
  774.         /* make it upper case */
  775.         for (c = temp; *c; c++)
  776.                 *c = toupper(*c);
  777.  
  778.         /* is UTF-8 specified? */
  779.         c = strstr(temp, "UTF-8");
  780.         if (c) {
  781.                 free(temp);
  782.                 return strdup("UTF-8");
  783.         }
  784.  
  785.         /* dispense with temporary copy */
  786.         free(temp);
  787.  
  788.         /* according to RFC2070, the accept-charsets attribute of the
  789.          * form element contains a space and/or comma separated list */
  790.         c = form->accept_charsets;
  791.  
  792.         /* What would be an improvement would be to choose an encoding
  793.          * acceptable to the server which covers as much of the input
  794.          * values as possible. Additionally, we need to handle the case
  795.          * where none of the acceptable encodings cover all the textual
  796.          * input values.
  797.          * For now, we just extract the first element of the charset list
  798.          */
  799.         while (*c && !isspace(*c)) {
  800.                 if (*c == ',')
  801.                         break;
  802.                 c++;
  803.         }
  804.  
  805.         return strndup(form->accept_charsets, c - form->accept_charsets);
  806. }
  807.  
  808. /**
  809.  * Convert a string from UTF-8 to the specified charset
  810.  * As a final fallback, this will attempt to convert to ISO-8859-1.
  811.  *
  812.  * \todo Return charset used?
  813.  *
  814.  * \param item String to convert
  815.  * \param charset Destination charset
  816.  * \param fallback Fallback charset (may be NULL),
  817.  *                 used iff converting to charset fails
  818.  * \return Pointer to converted string (on heap, caller frees), or NULL
  819.  */
  820. char *form_encode_item(const char *item, const char *charset,
  821.                 const char *fallback)
  822. {
  823.         utf8_convert_ret err;
  824.         char *ret = NULL;
  825.         char cset[256];
  826.  
  827.         if (!item || !charset)
  828.                 return NULL;
  829.  
  830.         snprintf(cset, sizeof cset, "%s//TRANSLIT", charset);
  831.  
  832.         err = utf8_to_enc(item, cset, 0, &ret);
  833.         if (err == UTF8_CONVERT_BADENC) {
  834.                 /* charset not understood, try without transliteration */
  835.                 snprintf(cset, sizeof cset, "%s", charset);
  836.                 err = utf8_to_enc(item, cset, 0, &ret);
  837.  
  838.                 if (err == UTF8_CONVERT_BADENC) {
  839.                         /* nope, try fallback charset (if any) */
  840.                         if (fallback) {
  841.                                 snprintf(cset, sizeof cset,
  842.                                                 "%s//TRANSLIT", fallback);
  843.                                 err = utf8_to_enc(item, cset, 0, &ret);
  844.  
  845.                                 if (err == UTF8_CONVERT_BADENC) {
  846.                                         /* and without transliteration */
  847.                                         snprintf(cset, sizeof cset,
  848.                                                         "%s", fallback);
  849.                                         err = utf8_to_enc(item, cset, 0, &ret);
  850.                                 }
  851.                         }
  852.  
  853.                         if (err == UTF8_CONVERT_BADENC) {
  854.                                 /* that also failed, use 8859-1 */
  855.                                 err = utf8_to_enc(item, "ISO-8859-1//TRANSLIT",
  856.                                                 0, &ret);
  857.                                 if (err == UTF8_CONVERT_BADENC) {
  858.                                         /* and without transliteration */
  859.                                         err = utf8_to_enc(item, "ISO-8859-1",
  860.                                                         0, &ret);
  861.                                 }
  862.                         }
  863.                 }
  864.         }
  865.         if (err == UTF8_CONVERT_NOMEM) {
  866.                 return NULL;
  867.         }
  868.  
  869.         return ret;
  870. }
  871.  
  872. /**
  873.  * Open a select menu for a select form control, creating it if necessary.
  874.  *
  875.  * \param client_data   data passed to the redraw callback
  876.  * \param control       the select form control for which the menu is being
  877.  *                      opened
  878.  * \param callback      redraw callback for the select menu
  879.  * \param bw            the browser window in which the select menu is being
  880.  *                      opened
  881.  * \return              false on memory exhaustion, true otherwise
  882.  */
  883. bool form_open_select_menu(void *client_data,
  884.                 struct form_control *control,
  885.                 select_menu_redraw_callback callback,
  886.                 struct content *c)
  887. {
  888.         int line_height_with_spacing;
  889.         struct box *box;
  890.         plot_font_style_t fstyle;
  891.         int total_height;
  892.         struct form_select_menu *menu;
  893.  
  894.  
  895.         /* if the menu is opened for the first time */
  896.         if (control->data.select.menu == NULL) {
  897.  
  898.                 menu = calloc(1, sizeof (struct form_select_menu));
  899.                 if (menu == NULL) {
  900.                         warn_user("NoMemory", 0);
  901.                         return false;
  902.                 }
  903.  
  904.                 control->data.select.menu = menu;
  905.  
  906.                 box = control->box;
  907.  
  908.                 menu->width = box->width +
  909.                                 box->border[RIGHT].width +
  910.                                 box->border[LEFT].width +
  911.                                 box->padding[RIGHT] + box->padding[LEFT];
  912.  
  913.                 font_plot_style_from_css(control->box->style,
  914.                                 &fstyle);
  915.                 menu->f_size = fstyle.size;
  916.  
  917.                 menu->line_height = FIXTOINT(FDIV((FMUL(FLTTOFIX(1.2),
  918.                                 FMUL(nscss_screen_dpi,
  919.                                 INTTOFIX(fstyle.size / FONT_SIZE_SCALE)))),
  920.                                 F_72));
  921.  
  922.                 line_height_with_spacing = menu->line_height +
  923.                                 menu->line_height *
  924.                                 SELECT_LINE_SPACING;
  925.  
  926.                 total_height = control->data.select.num_items *
  927.                                 line_height_with_spacing;
  928.                 menu->height = total_height;
  929.  
  930.                 if (menu->height > MAX_SELECT_HEIGHT) {
  931.  
  932.                         menu->height = MAX_SELECT_HEIGHT;
  933.                 }
  934.                 menu->client_data = client_data;
  935.                 menu->callback = callback;
  936.                 if (!scrollbar_create(false,
  937.                                 menu->height,
  938.                                 total_height,
  939.                                 menu->height,
  940.                                 control,
  941.                                 form_select_menu_scroll_callback,
  942.                                 &(menu->scrollbar))) {
  943.                         free(menu);
  944.                         return false;
  945.                 }
  946.                 menu->c = c;
  947.         }
  948.         else menu = control->data.select.menu;
  949.  
  950.         menu->callback(client_data, 0, 0, menu->width, menu->height);
  951.  
  952.         return true;
  953. }
  954.  
  955.  
  956. /**
  957.  * Destroy a select menu and free allocated memory.
  958.  *
  959.  * \param control       the select form control owning the select menu being
  960.  *                      destroyed
  961.  */
  962. void form_free_select_menu(struct form_control *control)
  963. {
  964.         if (control->data.select.menu->scrollbar != NULL)
  965.                 scrollbar_destroy(control->data.select.menu->scrollbar);
  966.         free(control->data.select.menu);
  967.         control->data.select.menu = NULL;
  968. }
  969.  
  970. /**
  971.  * Redraw an opened select menu.
  972.  *
  973.  * \param control       the select menu being redrawn
  974.  * \param x             the X coordinate to draw the menu at
  975.  * \param x             the Y coordinate to draw the menu at
  976.  * \param scale         current redraw scale
  977.  * \param clip          clipping rectangle
  978.  * \param ctx           current redraw context
  979.  * \return              true on success, false otherwise
  980.  */
  981. bool form_redraw_select_menu(struct form_control *control, int x, int y,
  982.                 float scale, const struct rect *clip,
  983.                 const struct redraw_context *ctx)
  984. {
  985.         const struct plotter_table *plot = ctx->plot;
  986.         struct box *box;
  987.         struct form_select_menu *menu = control->data.select.menu;
  988.         struct form_option *option;
  989.         int line_height, line_height_with_spacing;
  990.         int width, height;
  991.         int x0, y0, x1, scrollbar_x, y1, y2, y3;
  992.         int item_y;
  993.         int text_pos_offset, text_x;
  994.         int scrollbar_width = SCROLLBAR_WIDTH;
  995.         int i;
  996.         int scroll;
  997.         int x_cp, y_cp;
  998.         struct rect r;
  999.        
  1000.         box = control->box;
  1001.        
  1002.         x_cp = x;
  1003.         y_cp = y;
  1004.         width = menu->width;
  1005.         height = menu->height;
  1006.         line_height = menu->line_height;
  1007.        
  1008.         line_height_with_spacing = line_height +
  1009.                         line_height * SELECT_LINE_SPACING;
  1010.         scroll = scrollbar_get_offset(menu->scrollbar);
  1011.        
  1012.         if (scale != 1.0) {
  1013.                 x *= scale;
  1014.                 y *= scale;
  1015.                 width *= scale;
  1016.                 height *= scale;
  1017.                 scrollbar_width *= scale;
  1018.                
  1019.                 i = scroll / line_height_with_spacing;
  1020.                 scroll -= i * line_height_with_spacing;
  1021.                 line_height *= scale;
  1022.                 line_height_with_spacing *= scale;
  1023.                 scroll *= scale;
  1024.                 scroll += i * line_height_with_spacing;
  1025.         }
  1026.        
  1027.        
  1028.         x0 = x;
  1029.         y0 = y;
  1030.         x1 = x + width - 1;
  1031.         y1 = y + height - 1;
  1032.         scrollbar_x = x1 - scrollbar_width;
  1033.  
  1034.         r.x0 = x0;
  1035.         r.y0 = y0;
  1036.         r.x1 = x1 + 1;
  1037.         r.y1 = y1 + 1;
  1038.         if (!plot->clip(&r))
  1039.                 return false;
  1040.         if (!plot->rectangle(x0, y0, x1, y1 ,plot_style_stroke_darkwbasec))
  1041.                 return false;
  1042.                
  1043.        
  1044.         x0 = x0 + SELECT_BORDER_WIDTH;
  1045.         y0 = y0 + SELECT_BORDER_WIDTH;
  1046.         x1 = x1 - SELECT_BORDER_WIDTH;
  1047.         y1 = y1 - SELECT_BORDER_WIDTH;
  1048.         height = height - 2 * SELECT_BORDER_WIDTH;
  1049.  
  1050.         r.x0 = x0;
  1051.         r.y0 = y0;
  1052.         r.x1 = x1 + 1;
  1053.         r.y1 = y1 + 1;
  1054.         if (!plot->clip(&r))
  1055.                 return false;
  1056.         if (!plot->rectangle(x0, y0, x1 + 1, y1 + 1,
  1057.                         plot_style_fill_lightwbasec))
  1058.                 return false;
  1059.         option = control->data.select.items;
  1060.         item_y = line_height_with_spacing;
  1061.        
  1062.         while (item_y < scroll) {
  1063.                 option = option->next;
  1064.                 item_y += line_height_with_spacing;
  1065.         }
  1066.         item_y -= line_height_with_spacing;
  1067.         text_pos_offset = y - scroll +
  1068.                         (int) (line_height * (0.75 + SELECT_LINE_SPACING));
  1069.         text_x = x + (box->border[LEFT].width + box->padding[LEFT]) * scale;
  1070.        
  1071.         plot_fstyle_entry.size = menu->f_size;
  1072.        
  1073.         while (option && item_y - scroll < height) {
  1074.                
  1075.                 if (option->selected) {
  1076.                         y2 = y + item_y - scroll;
  1077.                         y3 = y + item_y + line_height_with_spacing - scroll;
  1078.                         if (!plot->rectangle(x0, (y0 > y2 ? y0 : y2),
  1079.                                         scrollbar_x + 1,
  1080.                                         (y3 < y1 + 1 ? y3 : y1 + 1),
  1081.                                         &plot_style_fill_selected))
  1082.                                 return false;
  1083.                 }
  1084.                
  1085.                 y2 = text_pos_offset + item_y;
  1086.                 if (!plot->text(text_x, y2, option->text,
  1087.                                 strlen(option->text), &plot_fstyle_entry))
  1088.                         return false;
  1089.                
  1090.                 item_y += line_height_with_spacing;
  1091.                 option = option->next;
  1092.         }
  1093.                
  1094.         if (!scrollbar_redraw(menu->scrollbar,
  1095.                         x_cp + menu->width - SCROLLBAR_WIDTH,
  1096.                         y_cp,
  1097.                         clip, scale, ctx))
  1098.                 return false;
  1099.        
  1100.         return true;
  1101. }
  1102.  
  1103. /**
  1104.  * Check whether a clipping rectangle is completely contained in the
  1105.  * select menu.
  1106.  *
  1107.  * \param control       the select menu to check the clipping rectangle for
  1108.  * \param scale         the current browser window scale
  1109.  * \param clip_x0       minimum x of clipping rectangle
  1110.  * \param clip_y0       minimum y of clipping rectangle
  1111.  * \param clip_x1       maximum x of clipping rectangle
  1112.  * \param clip_y1       maximum y of clipping rectangle
  1113.  * \return              true if inside false otherwise
  1114.  */
  1115. bool form_clip_inside_select_menu(struct form_control *control, float scale,
  1116.                 const struct rect *clip)
  1117. {
  1118.         struct form_select_menu *menu = control->data.select.menu;
  1119.         int width, height;
  1120.        
  1121.  
  1122.         width = menu->width;
  1123.         height = menu->height;
  1124.        
  1125.         if (scale != 1.0) {
  1126.                 width *= scale;
  1127.                 height *= scale;
  1128.         }
  1129.        
  1130.         if (clip->x0 >= 0 && clip->x1 <= width &&
  1131.                         clip->y0 >= 0 && clip->y1 <= height)
  1132.                 return true;
  1133.  
  1134.         return false;
  1135. }
  1136.  
  1137.  
  1138. /**
  1139.  * Process a selection from a form select menu.
  1140.  *
  1141.  * \param  bw       browser window with menu
  1142.  * \param  control  form control with menu
  1143.  * \param  item     index of item selected from the menu
  1144.  */
  1145.  
  1146. static void form__select_process_selection(html_content *html,
  1147.                 struct form_control *control, int item)
  1148. {
  1149.         struct box *inline_box;
  1150.         struct form_option *o;
  1151.         int count;
  1152.  
  1153.         assert(control != NULL);
  1154.         assert(html != NULL);
  1155.  
  1156.         /** \todo Even though the form code is effectively part of the html
  1157.          *        content handler, poking around inside contents is not good */
  1158.  
  1159.         inline_box = control->box->children->children;
  1160.  
  1161.         for (count = 0, o = control->data.select.items;
  1162.                         o != NULL;
  1163.                         count++, o = o->next) {
  1164.                 if (!control->data.select.multiple)
  1165.                         o->selected = false;
  1166.                 if (count == item) {
  1167.                         if (control->data.select.multiple) {
  1168.                                 if (o->selected) {
  1169.                                         o->selected = false;
  1170.                                         control->data.select.num_selected--;
  1171.                                 } else {
  1172.                                         o->selected = true;
  1173.                                         control->data.select.num_selected++;
  1174.                                 }
  1175.                         } else {
  1176.                                 o->selected = true;
  1177.                         }
  1178.                 }
  1179.                 if (o->selected)
  1180.                         control->data.select.current = o;
  1181.         }
  1182.  
  1183.         talloc_free(inline_box->text);
  1184.         inline_box->text = 0;
  1185.         if (control->data.select.num_selected == 0)
  1186.                 inline_box->text = talloc_strdup(html->bctx,
  1187.                                 messages_get("Form_None"));
  1188.         else if (control->data.select.num_selected == 1)
  1189.                 inline_box->text = talloc_strdup(html->bctx,
  1190.                                 control->data.select.current->text);
  1191.         else
  1192.                 inline_box->text = talloc_strdup(html->bctx,
  1193.                                 messages_get("Form_Many"));
  1194.         if (!inline_box->text) {
  1195.                 warn_user("NoMemory", 0);
  1196.                 inline_box->length = 0;
  1197.         } else
  1198.                 inline_box->length = strlen(inline_box->text);
  1199.         inline_box->width = control->box->width;
  1200.  
  1201.         html__redraw_a_box(html, control->box);
  1202. }
  1203.  
  1204.  
  1205. void form_select_process_selection(hlcache_handle *h,
  1206.                 struct form_control *control, int item)
  1207. {
  1208.         assert(h != NULL);
  1209.  
  1210.         form__select_process_selection(
  1211.                         (html_content *)hlcache_handle_get_content(h),
  1212.                         control, item);
  1213. }
  1214.  
  1215. /**
  1216.  * Handle a click on the area of the currently opened select menu.
  1217.  *
  1218.  * \param control       the select menu which received the click
  1219.  * \param x             X coordinate of click
  1220.  * \param y             Y coordinate of click
  1221.  */
  1222. void form_select_menu_clicked(struct form_control *control, int x, int y)
  1223. {      
  1224.         struct form_select_menu *menu = control->data.select.menu;
  1225.         struct form_option *option;
  1226.         html_content *html = (html_content *)menu->c;
  1227.         int line_height, line_height_with_spacing;     
  1228.         int item_bottom_y;
  1229.         int scroll, i;
  1230.        
  1231.         scroll = scrollbar_get_offset(menu->scrollbar);
  1232.        
  1233.         line_height = menu->line_height;
  1234.         line_height_with_spacing = line_height +
  1235.                         line_height * SELECT_LINE_SPACING;
  1236.        
  1237.         option = control->data.select.items;
  1238.         item_bottom_y = line_height_with_spacing;
  1239.         i = 0;
  1240.         while (option && item_bottom_y < scroll + y) {
  1241.                 item_bottom_y += line_height_with_spacing;
  1242.                 option = option->next;
  1243.                 i++;
  1244.         }
  1245.        
  1246.         if (option != NULL) {
  1247.                 form__select_process_selection(html, control, i);
  1248.         }
  1249.        
  1250.         menu->callback(menu->client_data, 0, 0, menu->width, menu->height);
  1251. }
  1252.  
  1253. /**
  1254.  * Handle mouse action for the currently opened select menu.
  1255.  *
  1256.  * \param control       the select menu which received the mouse action
  1257.  * \param mouse         current mouse state
  1258.  * \param x             X coordinate of click
  1259.  * \param y             Y coordinate of click
  1260.  * \return              text for the browser status bar or NULL if the menu has
  1261.  *                      to be closed
  1262.  */
  1263. const char *form_select_mouse_action(struct form_control *control,
  1264.                 browser_mouse_state mouse, int x, int y)
  1265. {
  1266.         struct form_select_menu *menu = control->data.select.menu;
  1267.         int x0, y0, x1, y1, scrollbar_x;
  1268.         const char *status = NULL;
  1269.         bool multiple = control->data.select.multiple;
  1270.        
  1271.         x0 = 0;
  1272.         y0 = 0;
  1273.         x1 = menu->width;
  1274.         y1 = menu->height;
  1275.         scrollbar_x = x1 - SCROLLBAR_WIDTH;
  1276.        
  1277.         if (menu->scroll_capture ||
  1278.                         (x > scrollbar_x && x < x1 && y > y0 && y < y1)) {
  1279.                 /* The scroll is currently capturing all events or the mouse
  1280.                  * event is taking place on the scrollbar widget area
  1281.                  */
  1282.                 x -= scrollbar_x;
  1283.                 return scrollbar_mouse_action(menu->scrollbar,
  1284.                                     mouse, x, y);
  1285.         }
  1286.        
  1287.        
  1288.         if (x > x0 && x < scrollbar_x && y > y0 && y < y1) {
  1289.                 /* over option area */
  1290.                
  1291.                 if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2))
  1292.                         /* button 1 or 2 click */
  1293.                         form_select_menu_clicked(control, x, y);
  1294.                
  1295.                 if (!(mouse & BROWSER_MOUSE_CLICK_1 && !multiple))
  1296.                         /* anything but a button 1 click over a single select
  1297.                            menu */
  1298.                         status = messages_get(control->data.select.multiple ?
  1299.                                         "SelectMClick" : "SelectClick");
  1300.                
  1301.         } else if (!(mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)))
  1302.                 /* if not a button 1 or 2 click*/
  1303.                 status = messages_get("SelectClose");
  1304.                        
  1305.         return status;
  1306. }
  1307.  
  1308. /**
  1309.  * Handle mouse drag end for the currently opened select menu.
  1310.  *
  1311.  * \param control       the select menu which received the mouse drag end
  1312.  * \param mouse         current mouse state
  1313.  * \param x             X coordinate of drag end
  1314.  * \param y             Y coordinate of drag end
  1315.  */
  1316. void form_select_mouse_drag_end(struct form_control *control,
  1317.                 browser_mouse_state mouse, int x, int y)
  1318. {
  1319.         int x0, y0, x1, y1;
  1320.         int box_x, box_y;
  1321.         struct box *box;
  1322.         struct form_select_menu *menu = control->data.select.menu;
  1323.  
  1324.         box = control->box;
  1325.  
  1326.         /* Get global coords of scrollbar */
  1327.         box_coords(box, &box_x, &box_y);
  1328.         box_x -= box->border[LEFT].width;
  1329.         box_y += box->height + box->border[BOTTOM].width +
  1330.                         box->padding[BOTTOM] + box->padding[TOP];
  1331.  
  1332.         /* Get drag end coords relative to scrollbar */
  1333.         x = x - box_x;
  1334.         y = y - box_y;
  1335.  
  1336.         if (menu->scroll_capture) {
  1337.                 x -= menu->width - SCROLLBAR_WIDTH;
  1338.                 scrollbar_mouse_drag_end(menu->scrollbar, mouse, x, y);
  1339.                 return;
  1340.         }
  1341.        
  1342.         x0 = 0;
  1343.         y0 = 0;
  1344.         x1 = menu->width;
  1345.         y1 = menu->height;
  1346.                
  1347.        
  1348.         if (x > x0 && x < x1 - SCROLLBAR_WIDTH && y >  y0 && y < y1)
  1349.                 /* handle drag end above the option area like a regular click */
  1350.                 form_select_menu_clicked(control, x, y);
  1351. }
  1352.  
  1353. /**
  1354.  * Callback for the select menus scroll
  1355.  */
  1356. void form_select_menu_scroll_callback(void *client_data,
  1357.                 struct scrollbar_msg_data *scrollbar_data)
  1358. {
  1359.         struct form_control *control = client_data;
  1360.         struct form_select_menu *menu = control->data.select.menu;
  1361.         html_content *html = (html_content *)menu->c;
  1362.        
  1363.         switch (scrollbar_data->msg) {
  1364.                 case SCROLLBAR_MSG_MOVED:
  1365.                         menu->callback(menu->client_data,
  1366.                                         0, 0,
  1367.                                         menu->width,
  1368.                                         menu->height);
  1369.                         break;
  1370.                 case SCROLLBAR_MSG_SCROLL_START:
  1371.                 {
  1372.                         struct rect rect = {
  1373.                                 .x0 = scrollbar_data->x0,
  1374.                                 .y0 = scrollbar_data->y0,
  1375.                                 .x1 = scrollbar_data->x1,
  1376.                                 .y1 = scrollbar_data->y1
  1377.                         };
  1378.  
  1379.                         browser_window_set_drag_type(html->bw,
  1380.                                         DRAGGING_CONTENT_SCROLLBAR, &rect);
  1381.  
  1382.                         menu->scroll_capture = true;
  1383.                 }
  1384.                         break;
  1385.                 case SCROLLBAR_MSG_SCROLL_FINISHED:
  1386.                         menu->scroll_capture = false;
  1387.  
  1388.                         browser_window_set_drag_type(html->bw,
  1389.                                         DRAGGING_NONE, NULL);
  1390.                         break;
  1391.                 default:
  1392.                         break;
  1393.         }
  1394. }
  1395.  
  1396. /**
  1397.  * Get the dimensions of a select menu.
  1398.  *
  1399.  * \param control       the select menu to get the dimensions of
  1400.  * \param width         gets updated to menu width
  1401.  * \param height        gets updated to menu height
  1402.  */
  1403. void form_select_get_dimensions(struct form_control *control,
  1404.                 int *width, int *height)
  1405. {
  1406.         *width = control->data.select.menu->width;
  1407.         *height = control->data.select.menu->height;
  1408. }
  1409.  
  1410. /**
  1411.  * Callback for the core select menu.
  1412.  */
  1413. void form_select_menu_callback(void *client_data,
  1414.                 int x, int y, int width, int height)
  1415. {
  1416.         html_content *html = client_data;
  1417.         int menu_x, menu_y;
  1418.         struct box *box;
  1419.        
  1420.         box = html->visible_select_menu->box;
  1421.         box_coords(box, &menu_x, &menu_y);
  1422.                
  1423.         menu_x -= box->border[LEFT].width;
  1424.         menu_y += box->height + box->border[BOTTOM].width +
  1425.                         box->padding[BOTTOM] +
  1426.                         box->padding[TOP];
  1427.         content__request_redraw((struct content *)html, menu_x + x, menu_y + y,
  1428.                         width, height);
  1429. }
  1430.  
  1431.  
  1432. /**
  1433.  * Set a radio form control and clear the others in the group.
  1434.  *
  1435.  * \param  content  content containing the form, of type CONTENT_TYPE
  1436.  * \param  radio    form control of type GADGET_RADIO
  1437.  */
  1438.  
  1439. void form_radio_set(html_content *html,
  1440.                 struct form_control *radio)
  1441. {
  1442.         struct form_control *control;
  1443.  
  1444.         assert(html);
  1445.         assert(radio);
  1446.         if (!radio->form)
  1447.                 return;
  1448.  
  1449.         if (radio->selected)
  1450.                 return;
  1451.  
  1452.         for (control = radio->form->controls; control;
  1453.                         control = control->next) {
  1454.                 if (control->type != GADGET_RADIO)
  1455.                         continue;
  1456.                 if (control == radio)
  1457.                         continue;
  1458.                 if (strcmp(control->name, radio->name) != 0)
  1459.                         continue;
  1460.  
  1461.                 if (control->selected) {
  1462.                         control->selected = false;
  1463.                         html__redraw_a_box(html, control->box);
  1464.                 }
  1465.         }
  1466.  
  1467.         radio->selected = true;
  1468.         html__redraw_a_box(html, radio->box);
  1469. }
  1470.  
  1471.  
  1472. /**
  1473.  * Collect controls and submit a form.
  1474.  */
  1475.  
  1476. void form_submit(nsurl *page_url, struct browser_window *target,
  1477.                 struct form *form, struct form_control *submit_button)
  1478. {
  1479.         char *data = NULL;
  1480.         struct fetch_multipart_data *success;
  1481.         nsurl *action;
  1482.         nsurl *action_query;
  1483.  
  1484.         assert(form != NULL);
  1485.  
  1486.         if (form_successful_controls(form, submit_button, &success) == false) {
  1487.                 warn_user("NoMemory", 0);
  1488.                 return;
  1489.         }
  1490.  
  1491.         switch (form->method) {
  1492.         case method_GET:
  1493.                 data = form_url_encode(form, success, true);
  1494.                 if (data == NULL) {
  1495.                         fetch_multipart_data_destroy(success);
  1496.                         warn_user("NoMemory", 0);
  1497.                         return;
  1498.                 }
  1499.  
  1500.                 /* Decompose action */
  1501.                 if (nsurl_create(form->action, &action) != NSERROR_OK) {
  1502.                         free(data);
  1503.                         fetch_multipart_data_destroy(success);
  1504.                         warn_user("NoMemory", 0);
  1505.                         return;
  1506.                 }
  1507.  
  1508.                 /* Replace query segment */
  1509.                 if (nsurl_replace_query(action, data, &action_query) !=
  1510.                                 NSERROR_OK) {
  1511.                         nsurl_unref(action);
  1512.                         free(data);
  1513.                         fetch_multipart_data_destroy(success);
  1514.                         warn_user("NoMemory", 0);
  1515.                         return;
  1516.                 }
  1517.  
  1518.                 /* Construct submit url */
  1519.                 browser_window_go(target, nsurl_access(action_query),
  1520.                                 nsurl_access(page_url), true);
  1521.                 nsurl_unref(action);
  1522.                 nsurl_unref(action_query);
  1523.                 break;
  1524.  
  1525.         case method_POST_URLENC:
  1526.                 data = form_url_encode(form, success, false);
  1527.                 if (data == NULL) {
  1528.                         fetch_multipart_data_destroy(success);
  1529.                         warn_user("NoMemory", 0);
  1530.                         return;
  1531.                 }
  1532.  
  1533.                 browser_window_go_post(target, form->action, data, 0,
  1534.                                 true, nsurl_access(page_url), false, true, 0);
  1535.                 break;
  1536.  
  1537.         case method_POST_MULTIPART:
  1538.                 browser_window_go_post(target, form->action, 0, success,
  1539.                                 true, nsurl_access(page_url), false, true, 0);
  1540.                 break;
  1541.         }
  1542.  
  1543.         fetch_multipart_data_destroy(success);
  1544.         free(data);
  1545. }
  1546.