diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-05-12 13:39:52 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-05-12 13:39:52 +0300 |
commit | 9a51c0d0a17b9270542fef133040a02868bbaea9 (patch) | |
tree | fdbc5a60b171d2618e3bb9391a568f3fa80fce0e /src/ui | |
parent | 8e2126cae79e8182562d1796b61fefc8ba74f4b8 (diff) |
DocumentWidget: Input query length validation
Enforce the maximum length of 1024 bytes for Gemini URLs. The input query prompt shows how many bytes are remaining.
Diffstat (limited to 'src/ui')
-rw-r--r-- | src/ui/documentwidget.c | 57 | ||||
-rw-r--r-- | src/ui/inputwidget.c | 25 | ||||
-rw-r--r-- | src/ui/inputwidget.h | 4 | ||||
-rw-r--r-- | src/ui/util.c | 3 |
4 files changed, 75 insertions, 14 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 4706033a..e138af6f 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -1549,6 +1549,43 @@ static void togglePreFold_DocumentWidget_(iDocumentWidget *d, uint16_t preId) { | |||
1549 | refresh_Widget(as_Widget(d)); | 1549 | refresh_Widget(as_Widget(d)); |
1550 | } | 1550 | } |
1551 | 1551 | ||
1552 | static iString *makeQueryUrl_DocumentWidget_(const iDocumentWidget *d, | ||
1553 | const iString *userEnteredText) { | ||
1554 | iString *url = copy_String(d->mod.url); | ||
1555 | /* Remove the existing query string. */ | ||
1556 | const size_t qPos = indexOfCStr_String(url, "?"); | ||
1557 | if (qPos != iInvalidPos) { | ||
1558 | remove_Block(&url->chars, qPos, iInvalidSize); | ||
1559 | } | ||
1560 | appendCStr_String(url, "?"); | ||
1561 | append_String(url, collect_String(urlEncode_String(userEnteredText))); | ||
1562 | return url; | ||
1563 | } | ||
1564 | |||
1565 | static void inputQueryValidator_(iInputWidget *input, void *context) { | ||
1566 | iDocumentWidget *d = context; | ||
1567 | iString *url = makeQueryUrl_DocumentWidget_(d, text_InputWidget(input)); | ||
1568 | iWidget *dlg = parent_Widget(input); | ||
1569 | iLabelWidget *counter = findChild_Widget(dlg, "valueinput.counter"); | ||
1570 | iAssert(counter); | ||
1571 | int avail = 1024 - (int) size_String(url); | ||
1572 | setFlags_Widget(findChild_Widget(dlg, "default"), disabled_WidgetFlag, avail < 0); | ||
1573 | setEnterKeyEnabled_InputWidget(input, avail >= 0); | ||
1574 | int len = length_String(text_InputWidget(input)); | ||
1575 | if (len > 1024) { | ||
1576 | iString *trunc = copy_String(text_InputWidget(input)); | ||
1577 | truncate_String(trunc, 1024); | ||
1578 | setText_InputWidget(input, trunc); | ||
1579 | delete_String(trunc); | ||
1580 | } | ||
1581 | setTextCStr_LabelWidget(counter, format_CStr("%d", avail)); /* Gemini URL maxlen */ | ||
1582 | setTextColor_LabelWidget(counter, | ||
1583 | avail < 0 ? uiTextCaution_ColorId : | ||
1584 | avail < 128 ? uiTextStrong_ColorId | ||
1585 | : uiTextDim_ColorId); | ||
1586 | delete_String(url); | ||
1587 | } | ||
1588 | |||
1552 | static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | 1589 | static void checkResponse_DocumentWidget_(iDocumentWidget *d) { |
1553 | if (!d->request) { | 1590 | if (!d->request) { |
1554 | return; | 1591 | return; |
@@ -1578,6 +1615,11 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
1578 | : cstr_String(&resp->meta), | 1615 | : cstr_String(&resp->meta), |
1579 | uiTextCaution_ColorEscape "${dlg.input.send}", | 1616 | uiTextCaution_ColorEscape "${dlg.input.send}", |
1580 | format_CStr("!document.input.submit doc:%p", d)); | 1617 | format_CStr("!document.input.submit doc:%p", d)); |
1618 | setId_Widget(addChildPosFlags_Widget(findChild_Widget(dlg, "dialogbuttons"), | ||
1619 | iClob(new_LabelWidget("", NULL)), | ||
1620 | front_WidgetAddPos, frameless_WidgetFlag), | ||
1621 | "valueinput.counter"); | ||
1622 | setValidator_InputWidget(findChild_Widget(dlg, "input"), inputQueryValidator_, d); | ||
1581 | setSensitiveContent_InputWidget(findChild_Widget(dlg, "input"), | 1623 | setSensitiveContent_InputWidget(findChild_Widget(dlg, "input"), |
1582 | statusCode == sensitiveInput_GmStatusCode); | 1624 | statusCode == sensitiveInput_GmStatusCode); |
1583 | if (document_App() != d) { | 1625 | if (document_App() != d) { |
@@ -2209,17 +2251,10 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2209 | return iTrue; | 2251 | return iTrue; |
2210 | } | 2252 | } |
2211 | else if (equal_Command(cmd, "document.input.submit") && document_Command(cmd) == d) { | 2253 | else if (equal_Command(cmd, "document.input.submit") && document_Command(cmd) == d) { |
2212 | iString *value = suffix_Command(cmd, "value"); | 2254 | postCommandf_Root(w->root, |
2213 | set_String(value, collect_String(urlEncode_String(value))); | 2255 | "open url:%s", |
2214 | iString *url = collect_String(copy_String(d->mod.url)); | 2256 | cstrCollect_String(makeQueryUrl_DocumentWidget_ |
2215 | const size_t qPos = indexOfCStr_String(url, "?"); | 2257 | (d, collect_String(suffix_Command(cmd, "value"))))); |
2216 | if (qPos != iInvalidPos) { | ||
2217 | remove_Block(&url->chars, qPos, iInvalidSize); | ||
2218 | } | ||
2219 | appendCStr_String(url, "?"); | ||
2220 | append_String(url, value); | ||
2221 | postCommandf_Root(w->root, "open url:%s", cstr_String(url)); | ||
2222 | delete_String(value); | ||
2223 | return iTrue; | 2258 | return iTrue; |
2224 | } | 2259 | } |
2225 | else if (equal_Command(cmd, "valueinput.cancelled") && | 2260 | else if (equal_Command(cmd, "valueinput.cancelled") && |
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 72f9f706..38d025b9 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -75,6 +75,7 @@ enum iInputWidgetFlag { | |||
75 | isMarking_InputWidgetFlag = iBit(7), | 75 | isMarking_InputWidgetFlag = iBit(7), |
76 | markWords_InputWidgetFlag = iBit(8), | 76 | markWords_InputWidgetFlag = iBit(8), |
77 | needUpdateBuffer_InputWidgetFlag = iBit(9), | 77 | needUpdateBuffer_InputWidgetFlag = iBit(9), |
78 | enterKeyEnabled_InputWidgetFlag = iBit(10), | ||
78 | }; | 79 | }; |
79 | 80 | ||
80 | /*----------------------------------------------------------------------------------------------*/ | 81 | /*----------------------------------------------------------------------------------------------*/ |
@@ -124,6 +125,8 @@ struct Impl_InputWidget { | |||
124 | int cursorVis; | 125 | int cursorVis; |
125 | uint32_t timer; | 126 | uint32_t timer; |
126 | iTextBuf * buffered; | 127 | iTextBuf * buffered; |
128 | iInputWidgetValidatorFunc validator; | ||
129 | void * validatorContext; | ||
127 | }; | 130 | }; |
128 | 131 | ||
129 | iDefineObjectConstructionArgs(InputWidget, (size_t maxLen), maxLen) | 132 | iDefineObjectConstructionArgs(InputWidget, (size_t maxLen), maxLen) |
@@ -295,6 +298,8 @@ static void updateLinesAndResize_InputWidget_(iInputWidget *d) { | |||
295 | void init_InputWidget(iInputWidget *d, size_t maxLen) { | 298 | void init_InputWidget(iInputWidget *d, size_t maxLen) { |
296 | iWidget *w = &d->widget; | 299 | iWidget *w = &d->widget; |
297 | init_Widget(w); | 300 | init_Widget(w); |
301 | d->validator = NULL; | ||
302 | d->validatorContext = NULL; | ||
298 | setFlags_Widget(w, focusable_WidgetFlag | hover_WidgetFlag | touchDrag_WidgetFlag, iTrue); | 303 | setFlags_Widget(w, focusable_WidgetFlag | hover_WidgetFlag | touchDrag_WidgetFlag, iTrue); |
299 | #if defined (iPlatformMobile) | 304 | #if defined (iPlatformMobile) |
300 | setFlags_Widget(w, extraPadding_WidgetFlag, iTrue); | 305 | setFlags_Widget(w, extraPadding_WidgetFlag, iTrue); |
@@ -312,7 +317,7 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) { | |||
312 | d->lastCursor = 0; | 317 | d->lastCursor = 0; |
313 | d->cursorLine = 0; | 318 | d->cursorLine = 0; |
314 | d->verticalMoveX = -1; /* TODO: Use this. */ | 319 | d->verticalMoveX = -1; /* TODO: Use this. */ |
315 | d->inFlags = eatEscape_InputWidgetFlag; | 320 | d->inFlags = eatEscape_InputWidgetFlag | enterKeyEnabled_InputWidgetFlag; |
316 | iZap(d->mark); | 321 | iZap(d->mark); |
317 | setMaxLen_InputWidget(d, maxLen); | 322 | setMaxLen_InputWidget(d, maxLen); |
318 | d->maxLayoutLines = iInvalidSize; | 323 | d->maxLayoutLines = iInvalidSize; |
@@ -423,6 +428,15 @@ void setMaxLayoutLines_InputWidget(iInputWidget *d, size_t maxLayoutLines) { | |||
423 | updateMetrics_InputWidget_(d); | 428 | updateMetrics_InputWidget_(d); |
424 | } | 429 | } |
425 | 430 | ||
431 | void setValidator_InputWidget(iInputWidget *d, iInputWidgetValidatorFunc validator, void *context) { | ||
432 | d->validator = validator; | ||
433 | d->validatorContext = context; | ||
434 | } | ||
435 | |||
436 | void setEnterKeyEnabled_InputWidget(iInputWidget *d, iBool enterKeyEnabled) { | ||
437 | iChangeFlags(d->inFlags, enterKeyEnabled_InputWidgetFlag, enterKeyEnabled); | ||
438 | } | ||
439 | |||
426 | void setHint_InputWidget(iInputWidget *d, const char *hintText) { | 440 | void setHint_InputWidget(iInputWidget *d, const char *hintText) { |
427 | /* Keep original for retranslations. */ | 441 | /* Keep original for retranslations. */ |
428 | setCStr_String(&d->srcHint, hintText); | 442 | setCStr_String(&d->srcHint, hintText); |
@@ -715,6 +729,9 @@ static iRanges mark_InputWidget_(const iInputWidget *d) { | |||
715 | } | 729 | } |
716 | 730 | ||
717 | static void contentsWereChanged_InputWidget_(iInputWidget *d) { | 731 | static void contentsWereChanged_InputWidget_(iInputWidget *d) { |
732 | if (d->validator) { | ||
733 | d->validator(d, d->validatorContext); /* this may change the contents */ | ||
734 | } | ||
718 | updateLinesAndResize_InputWidget_(d); | 735 | updateLinesAndResize_InputWidget_(d); |
719 | if (d->inFlags & notifyEdits_InputWidgetFlag) { | 736 | if (d->inFlags & notifyEdits_InputWidgetFlag) { |
720 | postCommand_Widget(d, "input.edited id:%s", cstr_String(id_Widget(constAs_Widget(d)))); | 737 | postCommand_Widget(d, "input.edited id:%s", cstr_String(id_Widget(constAs_Widget(d)))); |
@@ -1068,8 +1085,10 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { | |||
1068 | contentsWereChanged_InputWidget_(d); | 1085 | contentsWereChanged_InputWidget_(d); |
1069 | return iTrue; | 1086 | return iTrue; |
1070 | } | 1087 | } |
1071 | d->inFlags |= enterPressed_InputWidgetFlag; | 1088 | if (d->inFlags & enterKeyEnabled_InputWidgetFlag) { |
1072 | setFocus_Widget(NULL); | 1089 | d->inFlags |= enterPressed_InputWidgetFlag; |
1090 | setFocus_Widget(NULL); | ||
1091 | } | ||
1073 | return iTrue; | 1092 | return iTrue; |
1074 | case SDLK_ESCAPE: | 1093 | case SDLK_ESCAPE: |
1075 | end_InputWidget(d, iFalse); | 1094 | end_InputWidget(d, iFalse); |
diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h index a632e230..cb32a29c 100644 --- a/src/ui/inputwidget.h +++ b/src/ui/inputwidget.h | |||
@@ -39,6 +39,8 @@ struct Impl_InputWidgetContentPadding { | |||
39 | int right; | 39 | int right; |
40 | }; | 40 | }; |
41 | 41 | ||
42 | typedef void (*iInputWidgetValidatorFunc)(iInputWidget *, void *context); | ||
43 | |||
42 | void setHint_InputWidget (iInputWidget *, const char *hintText); | 44 | void setHint_InputWidget (iInputWidget *, const char *hintText); |
43 | void setMode_InputWidget (iInputWidget *, enum iInputMode mode); | 45 | void setMode_InputWidget (iInputWidget *, enum iInputMode mode); |
44 | void setMaxLen_InputWidget (iInputWidget *, size_t maxLen); | 46 | void setMaxLen_InputWidget (iInputWidget *, size_t maxLen); |
@@ -48,6 +50,8 @@ void setFont_InputWidget (iInputWidget *, int fontId); | |||
48 | void setCursor_InputWidget (iInputWidget *, size_t pos); | 50 | void setCursor_InputWidget (iInputWidget *, size_t pos); |
49 | void setContentPadding_InputWidget (iInputWidget *, int left, int right); /* only affects the text entry */ | 51 | void setContentPadding_InputWidget (iInputWidget *, int left, int right); /* only affects the text entry */ |
50 | void setMaxLayoutLines_InputWidget (iInputWidget *, size_t maxLayoutLines); | 52 | void setMaxLayoutLines_InputWidget (iInputWidget *, size_t maxLayoutLines); |
53 | void setValidator_InputWidget (iInputWidget *, iInputWidgetValidatorFunc validator, void *context); | ||
54 | void setEnterKeyEnabled_InputWidget (iInputWidget *, iBool enterKeyEnabled); | ||
51 | void begin_InputWidget (iInputWidget *); | 55 | void begin_InputWidget (iInputWidget *); |
52 | void end_InputWidget (iInputWidget *, iBool accept); | 56 | void end_InputWidget (iInputWidget *, iBool accept); |
53 | void selectAll_InputWidget (iInputWidget *); | 57 | void selectAll_InputWidget (iInputWidget *); |
diff --git a/src/ui/util.c b/src/ui/util.c index f20338b5..ece527e1 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -1901,6 +1901,9 @@ iWidget *makeDialogButtons_Widget(const iMenuItem *actions, size_t numActions) { | |||
1901 | } | 1901 | } |
1902 | iLabelWidget *button = | 1902 | iLabelWidget *button = |
1903 | addChild_Widget(div, iClob(newKeyMods_LabelWidget(label, key, kmods, cmd))); | 1903 | addChild_Widget(div, iClob(newKeyMods_LabelWidget(label, key, kmods, cmd))); |
1904 | if (isDefault) { | ||
1905 | setId_Widget(as_Widget(button), "default"); | ||
1906 | } | ||
1904 | setFlags_Widget(as_Widget(button), alignLeft_WidgetFlag | drawKey_WidgetFlag, isDefault); | 1907 | setFlags_Widget(as_Widget(button), alignLeft_WidgetFlag | drawKey_WidgetFlag, isDefault); |
1905 | setFont_LabelWidget(button, isDefault ? fonts[1] : fonts[0]); | 1908 | setFont_LabelWidget(button, isDefault ? fonts[1] : fonts[0]); |
1906 | } | 1909 | } |