This commit is contained in:
Tommy D. Rossi
2026-01-29 20:30:10 +01:00
parent 1732e4e232
commit db54c35b83
2 changed files with 13 additions and 13 deletions
+12 -12
View File
@@ -1,12 +1,12 @@
/** /**
* Offscreen document for Playwriter screen recording. * Offscreen document for Playwriter screen recording.
* *
* WHY OFFSCREEN DOCUMENT? * WHY OFFSCREEN DOCUMENT?
* Manifest V3 service workers cannot use MediaRecorder or getUserMedia directly. * Manifest V3 service workers cannot use MediaRecorder or getUserMedia directly.
* This hidden document provides access to Web APIs while the service worker orchestrates. * This hidden document provides access to Web APIs while the service worker orchestrates.
* *
* RECORDING FLOW: * RECORDING FLOW:
* *
* ┌─────────────────┐ HTTP ┌─────────────────┐ WebSocket ┌─────────────────┐ * ┌─────────────────┐ HTTP ┌─────────────────┐ WebSocket ┌─────────────────┐
* │ User Code │ ────────────► │ Relay Server │ ───────────────►│ Extension │ * │ User Code │ ────────────► │ Relay Server │ ───────────────►│ Extension │
* │ startRecording │ │ /recording/* │ │ background.ts │ * │ startRecording │ │ /recording/* │ │ background.ts │
@@ -18,7 +18,7 @@
* │ Offscreen Doc │ ◄── MediaRecorder * │ Offscreen Doc │ ◄── MediaRecorder
* │ (this file) │ * │ (this file) │
* └─────────────────┘ * └─────────────────┘
* *
* STEP BY STEP: * STEP BY STEP:
* 1. User calls startRecording() → HTTP POST to relay server * 1. User calls startRecording() → HTTP POST to relay server
* 2. Relay server forwards to extension via WebSocket * 2. Relay server forwards to extension via WebSocket
@@ -27,14 +27,14 @@
* 4. Extension creates this offscreen document via chrome.offscreen.createDocument() * 4. Extension creates this offscreen document via chrome.offscreen.createDocument()
* 5. Extension sends streamId to offscreen document * 5. Extension sends streamId to offscreen document
* 6. Offscreen calls navigator.mediaDevices.getUserMedia() with streamId * 6. Offscreen calls navigator.mediaDevices.getUserMedia() with streamId
* 7. Offscreen creates MediaRecorder and starts encoding to webm * 7. Offscreen creates MediaRecorder and starts encoding to mp4
* 8. Chunks are sent back to extension → relay server → written to output file * 8. Chunks are sent back to extension → relay server → written to output file
* *
* KEY APIS: * KEY APIS:
* - chrome.tabCapture.getMediaStreamId() - Extension API, gets capture permission * - chrome.tabCapture.getMediaStreamId() - Extension API, gets capture permission
* - chrome.offscreen.createDocument() - Extension API, creates this document * - chrome.offscreen.createDocument() - Extension API, creates this document
* - navigator.mediaDevices.getUserMedia() - Web API, gets MediaStream from streamId * - navigator.mediaDevices.getUserMedia() - Web API, gets MediaStream from streamId
* - MediaRecorder - Web API, encodes video to webm * - MediaRecorder - Web API, encodes video to mp4
*/ */
import type { import type {
@@ -85,7 +85,7 @@ async function handleMessage(message: OffscreenMessage): Promise<OffscreenResult
async function handleStartRecording(params: OffscreenStartRecordingMessage): Promise<OffscreenStartRecordingResult> { async function handleStartRecording(params: OffscreenStartRecordingMessage): Promise<OffscreenStartRecordingResult> {
const { tabId } = params const { tabId } = params
if (recordings.has(tabId)) { if (recordings.has(tabId)) {
return { success: false, error: `Recording already in progress for tab ${tabId}` } return { success: false, error: `Recording already in progress for tab ${tabId}` }
} }
@@ -167,7 +167,7 @@ async function handleStartRecording(params: OffscreenStartRecordingMessage): Pro
async function handleStopRecording(params: OffscreenStopRecordingMessage): Promise<OffscreenStopRecordingResult> { async function handleStopRecording(params: OffscreenStopRecordingMessage): Promise<OffscreenStopRecordingResult> {
const { tabId } = params const { tabId } = params
const recording = recordings.get(tabId) const recording = recordings.get(tabId)
if (!recording) { if (!recording) {
return { success: false, error: `No active recording for tab ${tabId}` } return { success: false, error: `No active recording for tab ${tabId}` }
} }
@@ -215,11 +215,11 @@ async function handleStopRecording(params: OffscreenStopRecordingMessage): Promi
function handleIsRecording(params: OffscreenIsRecordingMessage): OffscreenIsRecordingResult { function handleIsRecording(params: OffscreenIsRecordingMessage): OffscreenIsRecordingResult {
const { tabId } = params const { tabId } = params
const recording = recordings.get(tabId) const recording = recordings.get(tabId)
if (!recording) { if (!recording) {
return { isRecording: false, tabId } return { isRecording: false, tabId }
} }
return { return {
isRecording: recording.recorder?.state === 'recording', isRecording: recording.recorder?.state === 'recording',
tabId, tabId,
@@ -235,7 +235,7 @@ function handleCancelRecording(params: OffscreenCancelRecordingMessage): Offscre
// Helper function to cancel recording for a specific tab - used by error handlers too // Helper function to cancel recording for a specific tab - used by error handlers too
function handleCancelRecordingForTab(tabId: number): OffscreenCancelRecordingResult { function handleCancelRecordingForTab(tabId: number): OffscreenCancelRecordingResult {
const recording = recordings.get(tabId) const recording = recordings.get(tabId)
if (!recording) { if (!recording) {
return { success: true, tabId } return { success: true, tabId }
} }
+1 -1
View File
@@ -485,7 +485,7 @@ await screenshotWithAccessibilityLabels({ page });
Labels are color-coded: yellow=links, orange=buttons, coral=inputs, pink=checkboxes, peach=sliders, salmon=menus, amber=tabs. Labels are color-coded: yellow=links, orange=buttons, coral=inputs, pink=checkboxes, peach=sliders, salmon=menus, amber=tabs.
**startRecording / stopRecording** - record the page as a video at native FPS (30-60fps). Uses `chrome.tabCapture` in the extension context, so **recording survives page navigation**. Video is saved as WebM. **startRecording / stopRecording** - record the page as a video at native FPS (30-60fps). Uses `chrome.tabCapture` in the extension context, so **recording survives page navigation**. Video is saved as mp4.
**Note**: Recording requires the user to have clicked the Playwriter extension icon on the tab. This grants `activeTab` permission needed for `chrome.tabCapture`. Recording works on tabs where the icon was clicked - if you need to record a new tab, ask the user to click the icon on it first. **Note**: Recording requires the user to have clicked the Playwriter extension icon on the tab. This grants `activeTab` permission needed for `chrome.tabCapture`. Recording works on tabs where the icon was clicked - if you need to record a new tab, ask the user to click the icon on it first.