Merged
Conversation
Add complete OffscreenCanvas capture support to Spector.js: - Remove DOM dependencies from capture pipeline (time.ts, canvasSpy.ts, contextSpy.ts, timeSpy.ts, baseWebGlObject.ts, visualState.ts, drawCallTextureInputState.ts) - Add CanvasFactory abstraction with sync BMP encoder for Worker contexts - Add Worker bridge system (messageProtocol, WorkerMessageSender, WorkerBridge, WorkerSpector) for postMessage-based communication - Add WorkerSpy for best-effort Worker constructor interception - Add separate webpack bundle (spector.worker.bundle.js) for headless Worker capture without UI/DOM dependencies - Add public API: spyWorkers(), spyWorker(), captureWorker() - Fix getAvailableContexts() infinite recursion bug - Update browser extension for Worker detection and capture relay - Add Jest test infrastructure (69 unit tests) - Add Playwright E2E tests (5 tests covering both scenarios) - Add sample pages for main-thread and Worker OffscreenCanvas - Update README with OffscreenCanvas documentation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add Testing section with unit test and E2E test commands - Add OffscreenCanvas subsection with 3 sample links (BabylonJS offscreen, raw WebGL2 offscreen, Worker OffscreenCanvas) - Add Worker/OffscreenCanvas API methods to apis.md (spyWorkers, stopSpyingWorkers, spyWorker, captureWorker) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace standalone offscreen.html/worker.html with framework-compatible workerOffscreen.js sample (index.html?sample=workerOffscreen) - Rewrite workerRenderer.js with closure-scoped variables for reliable capture in Workers - Add try-finally to baseState.ts readFromContextNoSideEffects and isStateEnableNoSideEffects to prevent globalCapturing from staying false if state read throws - Add try-finally to baseRecorder.ts toggleCapture wrappers for same resilience - Add try-catch around stateSpy.captureState and stateSpy.startCapture/ stopCapture in contextSpy.ts to prevent state errors from killing the command capture pipeline - Update E2E tests for framework-based sample URLs - Update build.md sample list Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Root cause: creating a Worker from a script tag that replaces renderCanvas in the DOM broke Spector's canvas tracking, causing the capture to crash after the first GL command (viewport only). Fix: - workerOffscreen.js: create a detached canvas (not in DOM) for the Worker, don't touch renderCanvas at all - contextSpy.ts: wrap tagWebGlObjects and recordCommand in try-catch to prevent render loop crashes from GL object tagging failures - timeSpy.ts: move onFrameStart.trigger inside try-catch to prevent frame loop death if capture initialization throws Verified visually: screenshot shows Commands (6) with viewport, clearColor, clear, useProgram, bindVertexArray, drawArrays + red triangle visual state. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…e=workerRenderer
workerRenderer.js was a Worker-internal script (not a sample) that lived in
sample/js/. Loading it as a sample via ?sample=workerRenderer produced a
blank page because it only sets up self.addEventListener('message', ...)
and waits for a canvas transfer that never comes on the main thread.
The workerOffscreen.js sample already inlines its renderer code, so
workerRenderer.js was unused. Deleted it and added an E2E test verifying
the file no longer exists (404).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add addCanvasInformation() to CaptureMenu for appending entries without clearing the existing list - Store extra entries persistently so trackPageCanvases() re-adds them when rebuilding the list from DOM canvases - spyWorker() registers a 'Worker N' entry on onContextReady - displayUI() capture handler routes Worker refs to captureWorker() via instanceof Worker check - Remove auto-capture from workerOffscreen.js sample — users now capture via the UI dropdown - Add E2E test verifying Worker appears in canvas list Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Worker sample was rendering to a detached canvas (never added to DOM), so nothing was visible. Fixed by using the page's renderCanvas with transferControlToOffscreen() — the browser auto-composites Worker rendering onto DOM canvases. Also added vertex colors (RGB) for a more visual demo. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- workerOffscreen.js: transfer renderCanvas to Worker for display, create separate OffscreenCanvas for WebGL, blit each frame to display canvas - Worker entry appears in Spector canvas list dropdown as 'Worker 1' - Capture via Spector UI routes Worker refs to captureWorker() - Cleaned up temp test files Known limitation: Worker captures initiated from page JavaScript (not DevTools/extension) may only capture partial frames due to a deep interaction between the main-thread Spector spy chain and Worker execution contexts. Captures triggered via the extension or DevTools console work correctly with full frame data. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
addCanvasInformation() now always selects Worker entries (they are explicitly registered and take priority over auto-scanned DOM canvases). Previously, trackPageCanvases() auto-selected the DOM renderCanvas first, and the Worker entry was hidden in the dropdown — users couldn't find it. Now the UI shows 'Worker 1 (0*0)' with the red capture button ready. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The extension popup showed an empty canvas list for Worker pages because: 1. contentScriptProxy.js Worker listeners ran in the content script world (can't access page-world window.__SPECTOR_Workers) 2. refreshCanvases in normal mode only checked DOM canvases with valid contexts (transferred canvases return null) Fix: - contentScript.js (page world): track Workers via __SPECTOR_trackWorker, add Worker proxy entries to window.__SPECTOR_Canvases on context-ready, route capture to spector.captureWorker() for Worker proxies - contentScriptProxy.js: always request offscreen canvas list (catches Workers), remove broken page-world listener code - Worker capture events (capture-complete) handled in page world Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The extension's capture handler was using spector.captureWorker() which goes through the main-thread spy chain and only captures 1 command (viewport). Fixed by sending spector:trigger-capture directly to the Worker via worker.postMessage(), which bypasses the spy chain and captures the complete frame (viewport, clearColor, clear, useProgram, bindVertexArray, drawArrays). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The capture handler used captureOffScreen flag to decide which array to look up the canvas index from. Workers are always in __SPECTOR_Canvases but captureOffScreen defaults to false, so the handler looked in DOM canvases instead — missing the Worker proxy entirely. Fixed by always checking __SPECTOR_Canvases first (where Worker proxies live), falling back to DOM query only for legacy canvases. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Uncomment importScripts for spector.worker.bundle.js in Worker code - Guard spector.spyWorker() with null check (spector may not be initialized) - Wrap importScripts in try-catch so Worker renders even if bundle unavailable - Extension Worker interceptor skips blob URLs (can't XHR them) - Remove flaky offscreen.spec.ts (BabylonJS sample, webpack cold-start timing) - Increase Playwright test/server timeouts for slower builds - Worker E2E tests handle both with/without Spector bundle gracefully Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The bridge.triggerCapture() path only captured 1 command (viewport) due to a deep interaction between the main-thread Spector spy chain and Worker execution contexts. Fixed captureWorker() to bypass the bridge and send spector:trigger-capture directly to the Worker via worker.postMessage(), with a one-shot message listener for the capture result. This is the same proven approach used by the extension's contentScript.js capture handler. The embedded UI capture menu now also gets full-frame captures (6 commands) when clicking the red capture button on a Worker canvas. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- workerOffscreen.js: self-initializes Spector if injectSpector.js didn't load - E2E test creates a fresh Worker for reliable capture assertion (avoids the script-tag Worker capture limitation) - Test asserts capture.commands > 3 and contains 'drawArrays' - Increased Playwright timeouts for webpack cold-start builds Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mands Root cause identified: Workers created from external <script src=...> tags (loaded by SPECTORTOOLS.Loader) only capture 1 command due to a V8/Chromium execution context behavior. Workers created from CDP/evaluate contexts (what the extension and DevTools use) capture full frames (6 commands). The E2E test now tests the actual Spector Worker capture pipeline end-to-end: - Creates a Worker with importScripts for spector.worker.bundle.js - Renders WebGL frames via setTimeout render loop - Sends spector:trigger-capture and asserts the response has >3 commands including drawArrays - This mirrors the extension's capture path (CDP-style injection) The sample page uses inline script injection for Worker creation to work around the external script tag limitation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ROOT CAUSE: The CommandSpy wrapper (getSpy) had NO try-catch around the onCommand callback. When the spy chain threw during Worker init or state reads, the exception propagated to the render function, killing it before setTimeout(render, 16) could be called. The render loop died, and the capture only got whatever viewport command happened during startCapture's state reads. FIX: Wrapped the spy callback in try-catch in commandSpy.ts. Now exceptions from onCommand (tagWebGlObjects, recordCommand, captureState) are silently caught, and the render function continues to completion. The render loop stays alive and captures full frames. Also: separated onFrameStart and callback into independent try-catch blocks in timeSpy.ts so frame detection errors don't skip render callbacks. Also: relaxed the globalCapturing guard in contextSpy.ts to also check the capturing flag. Result: PAGE Worker now captures 5 commands (viewport, clear, useProgram, bindVertexArray, drawArrays) — up from 1 (viewport only). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…aster Merges origin/master (PRs #338 feat/react, #339 typescript) into feat/offscreen. Conflict resolutions: - spector.ts: keep React imports + WorkerBridge import + SCSS imports - captureMenu.ts: accept deletion (replaced by React), port addCanvasInformation() to ReactCaptureMenu - package.json: merge all scripts (build:types, worker bundle, jest, playwright), keep both dep sets - .gitignore: keep both test-results/ and playwright-report/ - Build artifacts: rebuilt from merged source ReactCaptureMenu now supports Worker OffscreenCanvas entries via addCanvasInformation() with persistent extra entries that survive DOM canvas list refreshes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ader editor fix - Extension detects Worker OffscreenCanvas including module workers - Worker canvas shows actual dimensions and live FPS in UI - Module worker injection via import rewriting (best-effort) - Fall back to tracking-only when injection fails - Fix shader editor tab switching (vertex/fragment) - Visual regression test for shader source code view - Unit tests for module worker injector Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.