Commit efebc62
authored
feat: proxy AbortSignal across iframe boundary for V3 plugin provider callbacks (#1276)
## PR Checklist
- Required Checks
- [x] Have you added type definitions?
- [x] Have you tested your changes?
- [x] Have you checked that it won't break any existing features?
- [ ] If your PR uses models[^1], if true, check the following:
- [ ] Have you checked if it works normally in all models?
- [ ] Have you checked if it works normally in all web, local, and node
hosted versions? If it doesn't, have you blocked it in those versions?
- [x] If your PR is highly ai generated[^2], check the following:
- [x] Have you understanded what the code does?
- [x] Have you cleaned up any unnecessary or redundant code?
- [x] Is is not a huge change?
- We currently do not accept highly ai generated PRs that are large
changes.
[^1]: Modifies the behavior of prompting, requesting or handling
responses from ai models.
[^2]: Almost over 80% of the code is ai generated.
## Summary
Follow-up to #1273. Instead of discarding `AbortSignal` (replacing with
`null`), this PR proxies it across the iframe boundary using an ID-based
message protocol, restoring abort/cancel functionality for V3 plugin
providers.
## Related Issues
- PR #1273 — Initial fix that sanitized `AbortSignal` to `null` to
prevent `DataCloneError`. This PR builds on that foundation.
## Changes
`AbortSignal` is not structured-cloneable (HTML spec), so it cannot be
passed via `postMessage`. PR #1273 solved the crash by replacing it with
`null`, but this meant plugins lost all abort/cancel functionality.
This PR introduces a lightweight proxy protocol within factory.ts
(single file, ~46 lines added):
**Host side** (`SandboxHost.deserializeArgs`):
- `AbortSignal` → serializable `AbortSignalRef` object `{ __type:
'ABORT_SIGNAL_REF', abortId, aborted }`
- Attaches a one-shot `abort` event listener on the real signal that
sends an `ABORT_SIGNAL` message to the guest iframe when triggered
**Guest side** (`GUEST_BRIDGE_SCRIPT`):
- New `abortControllers` registry (Map)
- `INVOKE_CALLBACK` handler deserializes `AbortSignalRef` → local
`AbortController`, stores in registry, returns `controller.signal` to
the plugin callback
- If `aborted: true` in ref, immediately calls `controller.abort()`
- New `ABORT_SIGNAL` message handler: looks up controller by `abortId`,
calls `.abort()`, removes from registry
**Type additions**:
- `'ABORT_SIGNAL'` added to `MsgType` union
- `AbortSignalRef` interface added
### Data flow
```
Host (real AbortSignal) Guest (reconstructed AbortSignal)
─────────────────────── ────────────────────────────────
AbortSignal AbortController + .signal
│ │
│ INVOKE_CALLBACK │
├──→ { __type: 'ABORT_SIGNAL_REF', ──→ new AbortController()
│ abortId: 'abc', abortControllers.set('abc', ctrl)
│ aborted: false } return ctrl.signal to plugin
│ │
│ (user clicks Stop) │
│ abort event fires │
├──→ postMessage({ │
│ type: 'ABORT_SIGNAL', ────→ ctrl.abort()
│ abortId: 'abc' abortControllers.delete('abc')
│ }) │
```
## Impact
**Backward compatible** — no breaking changes:
- Plugins that **ignore** `abortSignal` (pass it as unused param or
check for null): no behavior change. They received `null` before, now
they receive a real `AbortSignal` that simply never fires if the host
doesn't abort.
- Plugins that **use** `abortSignal` (e.g., `signal: abortSignal` in
fetch, or `.addEventListener('abort', ...)`): these now work correctly,
whereas before they had no abort capability.
- The `ABORT_SIGNAL` message type is ignored by older guest bridge code
that doesn't recognize it, so forward compatibility is maintained.
- No sandbox flags are modified (`allow-scripts allow-modals
allow-downloads` only).
- No host objects or references leak to the guest — only a plain `{
__type, abortId, aborted }` object crosses the boundary.
## Additional Notes
- This change is scoped entirely to factory.ts — no other files are
modified.
- The protocol is unidirectional (host→guest only). Guest-to-host
AbortSignal proxying (e.g., plugin passing `signal` in `nativeFetch`
options) is not covered and would require a separate change. (It will
probably be resolved by #1274)
- The `aborted` field in `AbortSignalRef` handles the edge case where
the signal is already aborted at serialization time.
---1 file changed
Lines changed: 62 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
| 7 | + | |
| 8 | + | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
| |||
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
| 18 | + | |
17 | 19 | | |
18 | 20 | | |
19 | 21 | | |
| |||
26 | 28 | | |
27 | 29 | | |
28 | 30 | | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
29 | 37 | | |
30 | 38 | | |
31 | 39 | | |
32 | 40 | | |
33 | 41 | | |
34 | 42 | | |
| 43 | + | |
35 | 44 | | |
36 | 45 | | |
37 | 46 | | |
| |||
150 | 159 | | |
151 | 160 | | |
152 | 161 | | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
153 | 170 | | |
154 | 171 | | |
155 | 172 | | |
| 173 | + | |
156 | 174 | | |
157 | 175 | | |
158 | 176 | | |
159 | | - | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
160 | 188 | | |
161 | 189 | | |
162 | 190 | | |
163 | 191 | | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
164 | 196 | | |
165 | 197 | | |
166 | 198 | | |
| |||
351 | 383 | | |
352 | 384 | | |
353 | 385 | | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
354 | 412 | | |
355 | 413 | | |
356 | 414 | | |
357 | 415 | | |
358 | | - | |
| 416 | + | |
359 | 417 | | |
360 | 418 | | |
361 | 419 | | |
| |||
499 | 557 | | |
500 | 558 | | |
501 | 559 | | |
502 | | - | |
| 560 | + | |
0 commit comments