Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ An in-browser **C++20 IDE** delivered as a Chrome / Chromium extension.
|---------|--------|
| Editor | Monaco Editor (the engine behind VS Code) |
| Compiler | WASM-native Clang (runs entirely in the browser, offline) |
| Terminal | xterm.js with a bash-like shell (`g++`, `./a.out`, `ls`, `mkdir`, `touch`, `cat`, …) |
| Terminal | xterm.js with a bash-like shell and live line input on Chromium and Firefox 153+ |
| File access | File System Access API on Chromium, fallback open/save/folder flows on Firefox |
| File I/O | `fstream` / `ifstream` / `ofstream` – read and write workspace files at runtime |
| Standards | C++14 · C++17 · **C++20** (selectable in the toolbar) |
Expand Down Expand Up @@ -219,8 +219,9 @@ Firefox desktop is also a supported release target, but its support contract is
different:

- compile/run, Monaco, and extension-runtime flows are supported
- programs that read `std::cin` use pre-supplied buffered stdin (up to 256 KiB);
live prompt-by-prompt stdin remains available on Chromium-family builds
- Firefox 153+ uses WebAssembly JSPI for live, line-buffered `std::cin`,
`std::getline`, and `scanf` input; Firefox 140–152 (or a runtime without
JSPI) retains the pre-supplied buffered stdin fallback (up to 256 KiB)
- file open/save and folder import use fallback browser flows rather than
Chromium File System Access APIs
- persistent folder write-back and directory-handle session restore may be
Expand All @@ -235,6 +236,7 @@ Full parity requires:
`showSaveFilePicker`)
- Web Workers and WebAssembly
- `SharedArrayBuffer` and `Atomics.waitAsync` for Chromium live interactive stdin
- `WebAssembly.Suspending` and `WebAssembly.promising` for Firefox 153+ live stdin
- Managed browser policies that allow local file read/write prompts

### Release-blocking checks
Expand Down Expand Up @@ -462,7 +464,8 @@ manual/GitHub-distributed channel.
signed artifact under `release/firefox-unlisted/`.
5. Install the signed XPI in Firefox and complete the manual QA checklist
in `docs/firefox-stdin-runtime-acceptance.md`, paying special attention to
buffered stdin and the documented workspace-persistence limitations.
JSPI live stdin, its buffered fallback, and the documented
workspace-persistence limitations.

### Manual release QA checklist

Expand Down Expand Up @@ -526,9 +529,10 @@ Copy the resulting `clang.js` and `clang.wasm` into `dist/clang/`.
script" dialog. The compiler runs in a dedicated Web Worker to avoid blocking
the UI.
- **Browser scope**: Full parity targets desktop Chrome, Edge, Brave, and
Chromium. Firefox is a compatible release target with buffered stdin and
workspace-persistence limitations; Safari is outside the current release
target.
Chromium. Firefox 153+ supports live canonical (line-buffered) stdin through
JSPI; older supported Firefox versions use buffered stdin. This is not a raw
POSIX PTY, and Firefox workspace-persistence limitations remain. Safari is
outside the current release target.
- **Managed browsers**: Enterprise policies that block File System Access prompts
prevent full local workspace read/write support.

Expand Down
92 changes: 58 additions & 34 deletions docs/firefox-stdin-runtime-acceptance.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# Firefox buffered stdin runtime acceptance
# Firefox stdin runtime acceptance

Use this procedure to prove GitHub issue #53 is fixed in a real Firefox
extension context. `npm run test:browser:firefox` validates packaging and
manifest compatibility, but it does not execute a compiled program in Firefox.
Use this procedure to validate live JSPI terminal input from GitHub issue #55
in a real Firefox extension context. `npm run test:browser:firefox` validates
packaging and manifest compatibility, but does not execute a compiled program.

Firefox 153+ should use live, canonical (line-buffered) input. Firefox 140–152,
or a runtime where JSPI is unavailable, should keep the pre-supplied buffered
fallback. Persistent folder write-back is outside this test and retains its
documented Firefox limitations.

## Setup

Expand All @@ -17,13 +22,14 @@ manifest compatibility, but it does not execute a compiled program in Firefox.
3. Choose **Load Temporary Add-on** and select `dist-firefox/manifest.json`.
For release validation, repeat with the signed XPI under
`release/firefox-unlisted/`.
4. Open browser.cpp from the extension toolbar action.
4. Open browser.cpp from the extension toolbar action and open the Browser
Console for error inspection.

Record the Firefox version and the unpacked directory or signed XPI path used.
Record the exact Firefox version and unpacked directory or signed XPI path.

## Test program
## Live-input test for Firefox 153+

Replace the editor contents with exactly:
Replace the editor contents with:

```cpp
#include <iostream>
Expand All @@ -33,56 +39,74 @@ int main() {
std::string name;
int age = 0;

std::cout << "Name? ";
std::cin >> name;
std::cout << "Age? ";
std::cout << "Name? " << std::flush;
std::getline(std::cin, name);
std::cout << "Age? " << std::flush;
std::cin >> age;

if (!std::cin) {
std::cerr << "input failed\n";
return 2;
}

std::cout << "\nHello " << name << ", next year " << (age + 1) << "\n";
return 0;
std::cout << "Hello " << name << ", next year " << (age + 1) << "\n";
}
```

Choose **Compile and Run**. In the **Pre-supplied stdin** dialog, enter exactly:
1. Choose **Compile and Run**.
2. Confirm `Name? ` appears before entering anything and that the
**Pre-supplied stdin** dialog does not open.
3. Type `Ada`, press Enter, and confirm `Age? ` appears afterward.
4. Type `41` and press Enter.
5. Confirm the final line is `Hello Ada, next year 42` and the process exits
with code `0` without stderr.

```text
Ada
41
```
This validates two independent suspend/resume cycles and prompt ordering.

Keep the final newline after `41`, then choose **Run program**.
## EOF, interruption, and clean-session tests

## Expected result
1. Run the program again. At the empty `Name? ` prompt, press Ctrl+D. Confirm
stdin reaches EOF and the program exits with `input failed` and code `2`.
2. Run again. At `Name? `, press Ctrl+C. Confirm browser.cpp reports
`Process interrupted.` and returns to its shell prompt.
3. Run once more and complete both inputs successfully. Confirm no input from
either prior run appears in the new process.

The process exits with code `0`, stderr is empty, and stdout is exactly:
## Buffered fallback test

```text
Name? Age?␠
Hello Ada, next year 42
```
Repeat in Firefox 140–152, or in a controlled environment where the worker does
not expose both `WebAssembly.Suspending` and `WebAssembly.promising`.

1. Choose **Compile and Run**.
2. Confirm the **Pre-supplied stdin** dialog opens.
3. Enter `Ada`, a newline, `41`, and a final newline; then choose **Run
program**.
4. Confirm the same successful final output.

The fallback must not attempt message-interactive stdin merely from the Firefox
version string or main-window capabilities.

The `␠` marker represents the single trailing ASCII space emitted after
`Age?`.
## Error exclusions

The terminal and browser console must not contain any of these strings:
The terminal and Browser Console must not contain unexpected instances of:

```text
Interactive stdin requires SharedArrayBuffer
Cross-Origin-Opener-Policy
Cross-Origin-Embedder-Policy
JSPI is unavailable
Ignored stdin message for an inactive session
Unhandled promise rejection
```

## PR evidence

Paste the following into the pull request:
Record the following in the pull request or follow-up release evidence:

- Firefox version
- tested artifact path
- observed stdout
- confirmation that stderr was empty
- confirmation that none of the old SharedArrayBuffer/COOP/COEP errors appeared
- Firefox version and tested artifact path
- exact observed prompt/output ordering
- confirmation that each input was entered only after its prompt appeared
- Ctrl+D, Ctrl+C, and clean-rerun results
- confirmation that the buffered dialog was absent on Firefox 153+ JSPI and
present in the no-JSPI fallback test
- confirmation that stderr and the Browser Console had no unexpected errors
5 changes: 3 additions & 2 deletions docs/release-playbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,9 @@ real Firefox desktop build:
2. Confirm Monaco renders and the default sample appears without blocking
console/runtime errors.
3. Compile and run the default sample program.
4. Complete `docs/firefox-stdin-runtime-acceptance.md` and confirm pre-supplied
buffered stdin works without the old SharedArrayBuffer/COOP/COEP error.
4. Complete `docs/firefox-stdin-runtime-acceptance.md`: confirm Firefox 153+
JSPI live stdin and the older/no-JSPI buffered fallback both work without
SharedArrayBuffer/COOP/COEP errors.
5. Open a local source file with Firefox's fallback picker and save changes.
6. Import a folder, compile a multi-file project, and confirm diagnostics appear
in the expected file.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"lint": "eslint .",
"build": "npm run build:webpack && npm run build:targets",
"build:firefox": "npm run build",
"test:e2e": "node --experimental-detect-module --test scripts/e2e-session-persistence.test.mjs scripts/e2e-session-restore-choice.test.mjs scripts/e2e-multifile-build.test.mjs scripts/e2e-workspace-file-tracking.test.mjs scripts/e2e-terminal-mkdir.test.mjs scripts/e2e-terminal-stop.test.mjs scripts/e2e-terminal-git-removal.test.mjs scripts/e2e-terminal-stop-icon.test.mjs scripts/e2e-buffered-stdin-ui.test.mjs scripts/e2e-browser-compatibility.test.mjs scripts/e2e-firefox-compatibility.test.mjs scripts/e2e-wasi-shim.test.mjs scripts/e2e-run-request.test.mjs scripts/e2e-release-packaging.test.mjs",
"test:e2e": "node --experimental-detect-module --test scripts/e2e-session-persistence.test.mjs scripts/e2e-session-restore-choice.test.mjs scripts/e2e-multifile-build.test.mjs scripts/e2e-workspace-file-tracking.test.mjs scripts/e2e-terminal-mkdir.test.mjs scripts/e2e-terminal-stop.test.mjs scripts/e2e-terminal-git-removal.test.mjs scripts/e2e-terminal-stop-icon.test.mjs scripts/e2e-buffered-stdin-ui.test.mjs scripts/e2e-browser-compatibility.test.mjs scripts/e2e-firefox-compatibility.test.mjs scripts/e2e-firefox-jspi-stdin.test.mjs scripts/e2e-wasi-shim.test.mjs scripts/e2e-run-request.test.mjs scripts/e2e-release-packaging.test.mjs",
"test:preflight-clang": "node scripts/preflight-clang-artifacts.js",
"test:browser:chrome": "npm run test:preflight-clang && node scripts/smoke-browser.mjs chrome",
"test:browser:edge": "npm run test:preflight-clang && node scripts/smoke-browser.mjs edge",
Expand Down
Loading
Loading