feat: add Chrome flags for automated recording without user interaction

- Add getChromeRestartCommand() with --allowlisted-extension-id and --auto-accept-this-tab-capture flags
- Move EXTENSION_IDS to utils.ts for cleaner imports
- Add sessionId support for multi-tab recording
- Add helpful error message when activeTab permission is missing
- Add null check for empty extension result
- Document offscreen recording flow with ASCII diagram
This commit is contained in:
Tommy D. Rossi
2026-01-24 17:05:24 +01:00
parent 8449006e59
commit df63f418e5
7 changed files with 136 additions and 33 deletions
+2 -1
View File
@@ -1116,8 +1116,9 @@ async function handleStartRecording(params: StartRecordingParams): Promise<Start
if (chrome.runtime.lastError) {
const errorMsg = chrome.runtime.lastError.message || 'Unknown error'
// Chrome returns this error when activeTab permission hasn't been granted
// User must click the extension icon at least once per session - this is a Chrome security requirement
if (errorMsg.includes('Extension has not been invoked') || errorMsg.includes('activeTab')) {
reject(new Error(`${errorMsg}. Click the Playwriter extension icon on this tab to enable recording, then try again.`))
reject(new Error(`${errorMsg}. Click the Playwriter extension icon on this tab to enable recording.`))
} else {
reject(new Error(errorMsg))
}
+36 -4
View File
@@ -1,8 +1,40 @@
/**
* Offscreen document for Playwriter extension.
* Handles operations that require DOM APIs not available in service workers:
* - Screen recording via chrome.tabCapture + MediaRecorder
* - Future: audio processing, canvas operations, etc.
* Offscreen document for Playwriter screen recording.
*
* WHY OFFSCREEN DOCUMENT?
* Manifest V3 service workers cannot use MediaRecorder or getUserMedia directly.
* This hidden document provides access to Web APIs while the service worker orchestrates.
*
* RECORDING FLOW:
*
* ┌─────────────────┐ HTTP ┌─────────────────┐ WebSocket ┌─────────────────┐
* │ User Code │ ────────────► │ Relay Server │ ───────────────►│ Extension │
* │ startRecording │ │ /recording/* │ │ background.ts │
* └─────────────────┘ └─────────────────┘ └────────┬────────┘
* │
* ┌─────────────────────────────────────┘
* ▼
* ┌─────────────────┐
* │ Offscreen Doc │ ◄── MediaRecorder
* │ (this file) │
* └─────────────────┘
*
* STEP BY STEP:
* 1. User calls startRecording() → HTTP POST to relay server
* 2. Relay server forwards to extension via WebSocket
* 3. Extension calls chrome.tabCapture.getMediaStreamId() to get capture permission
* - Requires --allowlisted-extension-id flag OR user clicking extension icon
* 4. Extension creates this offscreen document via chrome.offscreen.createDocument()
* 5. Extension sends streamId to offscreen document
* 6. Offscreen calls navigator.mediaDevices.getUserMedia() with streamId
* 7. Offscreen creates MediaRecorder and starts encoding to webm
* 8. Chunks are sent back to extension → relay server → written to output file
*
* KEY APIS:
* - chrome.tabCapture.getMediaStreamId() - Extension API, gets capture permission
* - chrome.offscreen.createDocument() - Extension API, creates this document
* - navigator.mediaDevices.getUserMedia() - Web API, gets MediaStream from streamId
* - MediaRecorder - Web API, encodes video to webm
*/
interface OffscreenRecordingState {