Skip to content

Commit ef3d044

Browse files
authored
Merge pull request #661 from embedpdf/fix/sandboxed-form-submission
Use explicit submit handlers for sandboxed iframes
2 parents bf009e3 + b4da3f2 commit ef3d044

4 files changed

Lines changed: 37 additions & 22 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@embedpdf/snippet": patch
3+
---
4+
5+
Fix UI actions that relied on native HTML form submission failing inside sandboxed iframes without the `allow-forms` permission. The comment input, zoom percentage input, and link modal now trigger their handlers via explicit button clicks and Enter keydown instead of form submission, so they work in sandboxed contexts while behaving identically everywhere else.

viewers/snippet/src/components/comment-sidebar/annotation-input.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,37 @@ export const AnnotationInput = ({
1616
}: AnnotationInputProps) => {
1717
const [text, setText] = useState('');
1818

19-
const handleSubmit = (e?: h.JSX.TargetedEvent<HTMLFormElement, Event>) => {
20-
e?.preventDefault();
19+
const handleSubmit = () => {
2120
if (text.trim()) {
2221
onSubmit(text);
2322
setText('');
2423
}
2524
};
2625

2726
return (
28-
<form
27+
<div
2928
className="border-border-subtle mt-4 flex items-end space-x-2 border-t pt-4"
3029
onClick={(e) => e.stopPropagation()}
31-
onSubmit={handleSubmit}
3230
>
3331
<input
3432
ref={inputRef}
3533
type="text"
3634
placeholder={placeholder}
3735
value={text}
3836
onInput={(e) => setText(e.currentTarget.value)}
37+
onKeyDown={(e) => {
38+
if (e.key === 'Enter') {
39+
e.preventDefault();
40+
handleSubmit();
41+
}
42+
}}
3943
className={`bg-bg-input text-fg-primary placeholder-fg-muted w-full rounded-lg border px-3 py-1 text-base transition-colors focus:border-transparent focus:outline-none focus:ring-2 ${
4044
isFocused ? 'border-accent focus:ring-accent' : 'border-border-default focus:ring-accent'
4145
}`}
4246
/>
4347
<button
44-
type="submit"
48+
type="button"
49+
onClick={() => handleSubmit()}
4550
disabled={!text.trim()}
4651
className="bg-accent text-fg-on-accent hover:bg-accent-hover disabled:bg-interactive-disabled rounded-lg p-2 transition-colors disabled:cursor-not-allowed"
4752
>
@@ -54,6 +59,6 @@ export const AnnotationInput = ({
5459
/>
5560
</svg>
5661
</button>
57-
</form>
62+
</div>
5863
);
5964
};

viewers/snippet/src/components/custom-zoom-toolbar.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,8 @@ export function CustomZoomToolbar({ documentId }: CustomZoomToolbarProps) {
3737
setInputValue(zoomPercentage.toString());
3838
}, [zoomPercentage]);
3939

40-
const handleZoomChange = (e: Event) => {
41-
e.preventDefault();
42-
const form = e.target as HTMLFormElement;
43-
const formData = new FormData(form);
44-
const value = parseFloat((formData.get('zoom') as string) || inputValue);
40+
const commitZoom = () => {
41+
const value = parseFloat(inputValue);
4542

4643
if (!isNaN(value) && value > 0) {
4744
provides.requestZoom(value / 100);
@@ -66,10 +63,7 @@ export function CustomZoomToolbar({ documentId }: CustomZoomToolbarProps) {
6663
<div className="relative">
6764
<div className="bg-interactive-hover flex items-center rounded">
6865
{/* Editable Zoom Percentage Input */}
69-
<form
70-
onSubmit={handleZoomChange}
71-
className="flex min-w-0 flex-nowrap items-center overflow-hidden whitespace-nowrap"
72-
>
66+
<div className="flex min-w-0 flex-nowrap items-center overflow-hidden whitespace-nowrap">
7367
<input
7468
name="zoom"
7569
type="text"
@@ -81,9 +75,15 @@ export function CustomZoomToolbar({ documentId }: CustomZoomToolbarProps) {
8175
value={inputValue}
8276
onInput={handleInputChange}
8377
onBlur={handleBlur}
78+
onKeyDown={(e) => {
79+
if (e.key === 'Enter') {
80+
e.preventDefault();
81+
commitZoom();
82+
}
83+
}}
8484
/>
8585
<span className="shrink-0 text-sm">%</span>
86-
</form>
86+
</div>
8787
<CommandButton
8888
commandId="zoom:toggle-menu"
8989
documentId={documentId}

viewers/snippet/src/components/link-modal.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,11 @@ export function LinkModal({ documentId, isOpen, onClose, onExited, source }: Lin
180180

181181
const canSubmit = activeTab === 'page' || url.trim().length > 0;
182182

183-
const handleFormSubmit = (e: Event) => {
184-
e.preventDefault();
185-
handleSubmit();
183+
const handleInputKeyDown = (e: h.JSX.TargetedKeyboardEvent<HTMLInputElement>) => {
184+
if (e.key === 'Enter' && canSubmit) {
185+
e.preventDefault();
186+
handleSubmit();
187+
}
186188
};
187189

188190
return (
@@ -193,7 +195,7 @@ export function LinkModal({ documentId, isOpen, onClose, onExited, source }: Lin
193195
onExited={onExited}
194196
className="md:w-[28rem]"
195197
>
196-
<form onSubmit={handleFormSubmit} className="space-y-6">
198+
<div className="space-y-6">
197199
{/* Tab buttons */}
198200
<div className="border-border-subtle flex border-b">
199201
<TabButton active={activeTab === 'url'} onClick={() => setActiveTab('url')}>
@@ -215,6 +217,7 @@ export function LinkModal({ documentId, isOpen, onClose, onExited, source }: Lin
215217
type="url"
216218
value={url}
217219
onInput={(e) => setUrl((e.target as HTMLInputElement).value)}
220+
onKeyDown={handleInputKeyDown}
218221
placeholder="https://example.com"
219222
className="border-border-default bg-bg-input text-fg-primary focus:border-accent focus:ring-accent w-full rounded-md border px-3 py-2 text-base focus:outline-none focus:ring-1"
220223
autoFocus
@@ -234,6 +237,7 @@ export function LinkModal({ documentId, isOpen, onClose, onExited, source }: Lin
234237
const val = parseInt((e.target as HTMLInputElement).value, 10);
235238
if (!isNaN(val)) setPageNumber(Math.max(1, Math.min(totalPages, val)));
236239
}}
240+
onKeyDown={handleInputKeyDown}
237241
className="border-border-default bg-bg-input text-fg-primary focus:border-accent focus:ring-accent w-full rounded-md border px-3 py-2 text-base focus:outline-none focus:ring-1"
238242
autoFocus
239243
/>
@@ -255,14 +259,15 @@ export function LinkModal({ documentId, isOpen, onClose, onExited, source }: Lin
255259
{translate('common.cancel') || 'Cancel'}
256260
</Button>
257261
<Button
258-
type="submit"
262+
type="button"
263+
onClick={handleSubmit}
259264
disabled={!canSubmit}
260265
className="bg-accent text-fg-on-accent hover:!bg-accent-hover rounded-md border border-transparent px-4 py-2 text-sm disabled:cursor-not-allowed disabled:opacity-50"
261266
>
262267
{translate('link.link') || 'Link'}
263268
</Button>
264269
</div>
265-
</form>
270+
</div>
266271
</Dialog>
267272
);
268273
}

0 commit comments

Comments
 (0)