diff --git a/README.md b/README.md index 381bcbb..f04cb19 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,8 @@ 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 - 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 @@ -232,7 +234,7 @@ Full parity requires: - File System Access APIs (`showOpenFilePicker`, `showDirectoryPicker`, `showSaveFilePicker`) - Web Workers and WebAssembly -- `SharedArrayBuffer` and `Atomics.waitAsync` for interactive stdin +- `SharedArrayBuffer` and `Atomics.waitAsync` for Chromium live interactive stdin - Managed browser policies that allow local file read/write prompts ### Release-blocking checks @@ -459,8 +461,8 @@ manual/GitHub-distributed channel. 4. For self-distribution, verify that the protected release workflow produced a signed artifact under `release/firefox-unlisted/`. 5. Install the signed XPI in Firefox and complete the manual QA checklist - below, paying special attention to the documented workspace-persistence - limitations. + in `docs/firefox-stdin-runtime-acceptance.md`, paying special attention to + buffered stdin and the documented workspace-persistence limitations. ### Manual release QA checklist @@ -524,7 +526,9 @@ 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 and Safari are outside the current release target. + Chromium. Firefox is a compatible release target with buffered stdin and + workspace-persistence limitations; Safari is outside the current release + target. - **Managed browsers**: Enterprise policies that block File System Access prompts prevent full local workspace read/write support. diff --git a/docs/firefox-stdin-runtime-acceptance.md b/docs/firefox-stdin-runtime-acceptance.md new file mode 100644 index 0000000..561787b --- /dev/null +++ b/docs/firefox-stdin-runtime-acceptance.md @@ -0,0 +1,88 @@ +# Firefox buffered 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. + +## Setup + +1. Run: + + ```bash + npm run build + npm run test:browser:firefox + ``` + +2. Open `about:debugging#/runtime/this-firefox` 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. + +Record the Firefox version and the unpacked directory or signed XPI path used. + +## Test program + +Replace the editor contents with exactly: + +```cpp +#include +#include + +int main() { + std::string name; + int age = 0; + + std::cout << "Name? "; + std::cin >> name; + std::cout << "Age? "; + std::cin >> age; + + if (!std::cin) { + std::cerr << "input failed\n"; + return 2; + } + + std::cout << "\nHello " << name << ", next year " << (age + 1) << "\n"; + return 0; +} +``` + +Choose **Compile and Run**. In the **Pre-supplied stdin** dialog, enter exactly: + +```text +Ada +41 +``` + +Keep the final newline after `41`, then choose **Run program**. + +## Expected result + +The process exits with code `0`, stderr is empty, and stdout is exactly: + +```text +Name? Age?␠ +Hello Ada, next year 42 +``` + +The `␠` marker represents the single trailing ASCII space emitted after +`Age?`. + +The terminal and browser console must not contain any of these strings: + +```text +Interactive stdin requires SharedArrayBuffer +Cross-Origin-Opener-Policy +Cross-Origin-Embedder-Policy +``` + +## PR evidence + +Paste the following into the pull request: + +- Firefox version +- tested artifact path +- observed stdout +- confirmation that stderr was empty +- confirmation that none of the old SharedArrayBuffer/COOP/COEP errors appeared diff --git a/docs/release-playbook.md b/docs/release-playbook.md index 3915296..8b4f5be 100644 --- a/docs/release-playbook.md +++ b/docs/release-playbook.md @@ -87,7 +87,8 @@ 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. Run a program that reads stdin and confirm terminal interaction works. +4. Complete `docs/firefox-stdin-runtime-acceptance.md` and confirm pre-supplied + buffered stdin works without the old SharedArrayBuffer/COOP/COEP error. 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. diff --git a/manifest.json b/manifest.json index b3e4664..68946aa 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "name": "browser.cpp", "short_name": "browser.cpp", "description": "In-browser C++20 IDE powered by Monaco Editor and WASM Clang", - "version": "0.3.2", + "version": "0.4.0", "minimum_chrome_version": "105", "icons": { "16": "icons/icon16.png", diff --git a/package-lock.json b/package-lock.json index 2b2a06c..dc9be4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "browser.cpp", - "version": "0.3.2", + "version": "0.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "browser.cpp", - "version": "0.3.2", + "version": "0.4.0", "dependencies": { "@xterm/addon-fit": "^0.11.0", "@xterm/addon-web-links": "^0.12.0", diff --git a/package.json b/package.json index fee44ec..47d8f83 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "browser.cpp", - "version": "0.3.2", + "version": "0.4.0", "description": "In-browser C++20 IDE with WASM Clang toolchain", "private": true, "scripts": { @@ -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-browser-compatibility.test.mjs scripts/e2e-firefox-compatibility.test.mjs scripts/e2e-wasi-shim.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-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", diff --git a/scripts/build-target-manifest.js b/scripts/build-target-manifest.js index b8d5a13..ee19c67 100644 --- a/scripts/build-target-manifest.js +++ b/scripts/build-target-manifest.js @@ -42,6 +42,8 @@ function createChromiumManifest(baseManifest) { function createFirefoxManifest(baseManifest, firefoxOverlay) { const manifest = mergeObjects(baseManifest, firefoxOverlay); delete manifest.minimum_chrome_version; + delete manifest.cross_origin_opener_policy; + delete manifest.cross_origin_embedder_policy; manifest.background = { scripts: ['firefox-background.js'], }; diff --git a/scripts/e2e-browser-compatibility.test.mjs b/scripts/e2e-browser-compatibility.test.mjs index b328d1e..a04fe2b 100644 --- a/scripts/e2e-browser-compatibility.test.mjs +++ b/scripts/e2e-browser-compatibility.test.mjs @@ -46,6 +46,8 @@ test('e2e: reports full Chromium-family capability support', () => { assert.deepEqual(report.limitations, []); assert.equal(report.capabilities.directoryPicker, true); assert.equal(report.capabilities.sharedArrayBuffer, true); + assert.equal(report.capabilities.interactiveStdin, true); + assert.equal(report.capabilities.stdinMode, 'interactive'); }); test('e2e: reports missing full-parity browser APIs', () => { diff --git a/scripts/e2e-buffered-stdin-ui.test.mjs b/scripts/e2e-buffered-stdin-ui.test.mjs new file mode 100644 index 0000000..ac89b73 --- /dev/null +++ b/scripts/e2e-buffered-stdin-ui.test.mjs @@ -0,0 +1,25 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; + +test('e2e: buffered stdin dialog has accessible controls and status feedback', async () => { + const html = await readFile('src/ui/index.html', 'utf8'); + const dialog = html.match(//)?.[0]; + + assert.ok(dialog, 'buffered stdin dialog markup should exist'); + assert.match(dialog, /aria-labelledby="buffered-stdin-title"/); + assert.match(dialog, /