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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ coverage/
test-results/
playwright-report/
.playwright-cli/
store-package/
*.js.map
.DS_Store
8 changes: 6 additions & 2 deletions PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@ The browser extension:

Chrome synchronization is not used. Browser, operating-system, backup, and enterprise-management behavior remains governed by those providers and your local configuration.

## Chrome Web Store Limited Use disclosure

Spector.GPU's use of information received from Chrome APIs complies with the Chrome Web Store User Data Policy, including the Limited Use requirements. The extension handles website content only to provide the user-requested WebGPU frame capture and inspection feature. It does not transfer that data to the developer or third parties, use it for advertising, sell it, use it for creditworthiness or lending, or allow humans to read it.

The extension does not download or execute remote extension code. All executable extension code is included in the installed package.

## Why permissions are required

| Permission | Purpose |
|---|---|
| `activeTab` | Coordinate capture with the tab selected by the user |
| `scripting` | Inject the WebGPU interception code early enough to observe resource creation |
| `storage` and `unlimitedStorage` | Store captures that can exceed normal extension quotas |
| `http://*/*`, `https://*/*`, `file://*/*` | Detect WebGPU and capture frames across development sites, hosted applications, iframes, and user-enabled local files |

Expand Down
78 changes: 66 additions & 12 deletions docs/chrome-web-store-listing.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,94 @@ This document contains the reviewed repository-side material for the Spector.GPU

## Listing copy

**Name:** Spector.GPU - WebGPU Inspector
**Name:** Spector.GPU — WebGPU Inspector

**Summary:** Capture and inspect WebGPU commands, shaders, pipelines, buffers, textures, and rendered output.

**Category:** Developer Tools

**Language:** English

**Single purpose:** Spector.GPU instruments WebGPU applications at the user's request so developers can capture one rendered frame and inspect its commands and GPU resources locally.

**Description:**

> Spector.GPU is a WebGPU frame debugger for Chrome. Open a WebGPU application, select Capture Frame, and inspect its queue submissions, render and compute passes, draw and dispatch calls, WGSL shaders, pipelines, bind groups, buffers, textures, and visual output.
>
> Captures stay in local extension storage and can be deleted from the result viewer. Spector.GPU contains no telemetry and does not send capture or browsing data to a project-operated service.
>
> Highlights:
> - One-click WebGPU frame capture
> - Render-pass, compute-pass, draw, dispatch, and state inspection
> - WGSL shader and pipeline inspection
> - Buffer, texture, sampler, bind-group, and resource cross-references
> - Texture previews, buffer hex views, and interactive 3D mesh inspection
> - Local-only storage with no analytics or telemetry
>
> Current support: Chrome and Chromium-based Edge 113 or newer. Some depth/stencil, compressed, multisampled, and 3D texture formats cannot be read back for previews.

## Permission disclosures

| Permission | Store justification |
|---|---|
| `activeTab` | Associates the user-initiated capture with the selected tab |
| `scripting` | Installs WebGPU interception early enough to observe resource creation |
| `storage` | Stores completed captures locally for the result viewer |
| `unlimitedStorage` | WebGPU buffers, textures, shaders, and screenshots can exceed Chrome's normal extension quota |
| HTTP(S) and optional file hosts | Detects and captures WebGPU on development and hosted applications, including frames |
| `http://*/*` and `https://*/*` | Installs the WebGPU capture hooks at document start so resources created during application startup and in nested frames can be captured |
| `file://*/*` | Supports developers debugging local WebGPU applications when they explicitly enable file URL access in Chrome |

The privacy-practice declaration should state that website content is processed locally for the tool's single purpose, is not sold, and is not transmitted to the developer. Use the repository's [privacy policy](../PRIVACY.md) as the public policy URL.

## Privacy practices

**Remote code:** No. All executable extension code is included in the uploaded package. Dynamic imports load only bundled extension chunks.

**Data categories handled:**

- Website content: WebGPU commands, descriptors, shaders, labels, GPU resources, and captured frame images.
- Web browsing activity: the current tab and URL context necessary to provide the user-requested capture feature.

**Data use:** Provide the user-facing WebGPU capture and inspection feature only.

**Data transfer:** No captured website content or browsing activity is transmitted to the developer or third parties.

The privacy-practice declaration should state that website content and user activity are processed locally for the tool's single purpose, are not sold, and are not transmitted to the developer. Use the repository's [privacy policy](../PRIVACY.md) as the public policy URL.
**Certifications:** Certify all Limited Use statements. The extension does not sell data, use data for advertising, use data for creditworthiness or lending, or permit humans to read captured data.

## URLs

- **Homepage:** https://github.com/sebavan/Spector.gpu
- **Support:** https://github.com/sebavan/Spector.gpu/issues
- **Privacy policy:** https://github.com/sebavan/Spector.gpu/blob/main/PRIVACY.md

## Submission assets

The publisher must supply these store-console assets outside the repository release workflow:
Generate assets with `npm run store:assets`. Upload:

- `docs/chrome-web-store/assets/store-icon-128.png`
- `docs/chrome-web-store/assets/screenshot-result-view-1280x800.png`
- `docs/chrome-web-store/assets/promo-small-440x280.png`
- `docs/chrome-web-store/assets/promo-marquee-1400x560.png` (optional)

The dashboard also accepts an optional YouTube promotional video. It is not required for submission.

## Reviewer test instructions

No credentials or paid account are required.

1. Install the extension and open `https://playground.babylonjs.com/?iswebgpu=true`.
2. Wait for the Spector.GPU toolbar icon to show the blue `GPU` badge.
3. Select the extension icon and click **Capture Frame**.
4. The result viewer opens automatically. Expand a render pass and select a draw command.
5. Verify that the Details, Shaders, and Pipeline tabs display capture information.
6. Switch the left sidebar to Resources and inspect a buffer or texture.
7. Use **Delete stored capture** in the result header to remove the local capture.

If WebGPU is unavailable in the review environment, enable hardware acceleration and use a Chrome version with WebGPU support.

## Distribution

- 128x128 store icon
- At least one 1280x800 or 640x400 screenshot
- 440x280 small promotional tile
- Support and privacy-policy URLs
- Publisher verification and store-account declarations
- **Visibility:** Public
- **Regions:** All regions
- **Mature content:** No
- **Pricing:** Free

The final submission must be reviewed in the Chrome Web Store dashboard because publishing credentials and legal attestations cannot be automated from this repository.
The final submission must be reviewed in the Chrome Web Store dashboard because publishing credentials, identity verification, and legal attestations cannot be automated from this repository.
81 changes: 81 additions & 0 deletions docs/chrome-web-store/SUBMISSION-CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Chrome Web Store 1.0 Submission Checklist

Use this checklist after the repository changes are merged into `main`.

## 1. Create the release package

1. Confirm the `main` CI workflow is green.
2. Create and push the `v1.0.0` tag.
3. Wait for the Release workflow to finish.
4. Download `spector-gpu-v1.0.0.zip` from the GitHub release.
5. Unzip it locally and load the folder from `chrome://extensions` using **Load unpacked**.
6. Capture a frame on a WebGPU page and confirm the result viewer opens.

The ZIP root must contain `manifest.json`; do not upload a ZIP containing an extra parent directory.

## 2. Prepare the publisher account

1. Open https://chrome.google.com/webstore/devconsole.
2. Register as a Chrome Web Store developer and pay the one-time fee if needed.
3. Accept the developer agreement.
4. Set the publisher name.
5. Add and verify the contact email.
6. Optionally verify `github.com/sebavan/Spector.gpu` or another owned website through Search Console for verified-publisher display.

## 3. Upload the package

1. Select **Add new item**.
2. Upload `spector-gpu-v1.0.0.zip`.
3. Confirm the dashboard reads version `1.0.0` and Manifest V3.

## 4. Complete Store listing

Copy the fields from `docs/chrome-web-store-listing.md`.

- Language: English
- Category: Developer Tools
- Homepage: https://github.com/sebavan/Spector.gpu
- Support: https://github.com/sebavan/Spector.gpu/issues
- Mature content: No

Upload:

- `assets/store-icon-128.png`
- `assets/screenshot-result-view-1280x800.png`
- `assets/promo-small-440x280.png`
- `assets/promo-marquee-1400x560.png` as the optional marquee image

Leave the promotional video empty unless a public YouTube walkthrough has been uploaded.

## 5. Complete Privacy practices

1. Paste the single-purpose statement.
2. Paste each permission and host-permission justification.
3. Select **No, I am not using remote code**.
4. Disclose website content and web browsing activity.
5. State that the data is used only for the user-requested capture and inspection feature.
6. State that captured data is processed and stored locally and is not transferred to the developer or third parties.
7. Certify every Limited Use statement.
8. Set the privacy policy URL to:
`https://github.com/sebavan/Spector.gpu/blob/main/PRIVACY.md`

## 6. Complete Test instructions

Paste the reviewer instructions from `docs/chrome-web-store-listing.md`. No credentials are required.

## 7. Complete Distribution

1. Choose **Public**.
2. Choose **All regions** unless a legal or policy requirement requires exclusions.
3. Confirm the extension is free and has no in-extension purchases.

For a cautious launch, choose deferred publishing in the final submission dialog. This lets review complete before manually making version 1.0 public.

## 8. Submit and monitor

1. Review every warning shown by the dashboard.
2. Select **Submit for Review**.
3. Keep publishing and rejection email notifications enabled.
4. If rejected, download or copy the exact policy message before modifying the package.
5. After approval, publish the staged item if deferred publishing was selected.
6. Update the README installation section with the final Chrome Web Store URL.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/chrome-web-store/assets/store-icon-128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
"test:e2e:debug": "npx playwright test --debug",
"bench:capture": "vitest bench --run test/performance/capturePerformance.bench.ts",
"check:versions": "node tools/check-versions.mjs",
"docs:screenshot": "node tools/generate-readme-screenshot.mjs"
"docs:screenshot": "node tools/generate-readme-screenshot.mjs",
"store:assets": "node tools/generate-store-assets.mjs",
"store:package": "npm run build && npm run store:assets && node tools/package-store-extension.mjs"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
Expand Down
4 changes: 1 addition & 3 deletions src/extension/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@
"version": "1.0.0",
"description": "Capture and inspect WebGPU frames on any website",
"permissions": [
"activeTab",
"storage",
"unlimitedStorage",
"scripting"
"unlimitedStorage"
],
"host_permissions": [
"http://*/*",
Expand Down
13 changes: 13 additions & 0 deletions src/extension/popup/components/PopupApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,19 @@ export function PopupApp(): React.JSX.Element {
</div>
)}

<div className="capture-disclosure">
A capture stores WebGPU commands, shaders, GPU resource contents,
and a frame image locally until you delete it.
{' '}
<a
href="https://github.com/sebavan/Spector.gpu/blob/main/PRIVACY.md"
rel="noreferrer"
target="_blank"
>
Privacy details
</a>
</div>

{/* Capture button */}
<button
className="capture-btn"
Expand Down
13 changes: 13 additions & 0 deletions src/styles/popup.scss
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,19 @@ body {
}
}

// ─── Capture disclosure ──────────────────────────────────────────────
.capture-disclosure {
color: $text-secondary;
font-size: 11px;
line-height: 1.45;

a {
color: $accent;
text-decoration: underline;
text-underline-offset: 2px;
}
}

// ─── Hint text ───────────────────────────────────────────────────────
.hint {
font-size: 11px;
Expand Down
65 changes: 65 additions & 0 deletions test/ui/PopupApp.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { PopupApp } from '@extension/popup/components/PopupApp';
import { MessageType } from '@shared/types/messages';

describe('PopupApp', () => {
const sendMessage = vi.fn();

beforeEach(() => {
sendMessage.mockReset();
Object.defineProperty(globalThis, 'chrome', {
configurable: true,
value: {
tabs: {
query: vi.fn((_query, callback) => callback([{ id: 42 }])),
},
runtime: {
sendMessage,
},
},
});
sendMessage.mockImplementation((message, callback) => {
if (message.type === MessageType.STATUS_QUERY) {
callback({
webgpuDetected: true,
adapterInfo: {
vendor: 'nvidia',
architecture: 'turing',
device: '',
description: 'WebGPU Adapter',
backend: 'd3d12',
},
isCapturing: false,
});
}
});
});

it('discloses local capture data before capture starts', async () => {
render(<PopupApp />);

await screen.findByRole('button', { name: 'Capture Frame' });
expect(
screen.getByText(/stores WebGPU commands, shaders, GPU resource contents/i),
).toBeVisible();
expect(screen.getByRole('link', { name: 'Privacy details' })).toHaveAttribute(
'href',
'https://github.com/sebavan/Spector.gpu/blob/main/PRIVACY.md',
);
});

it('sends a capture request for the active tab', async () => {
render(<PopupApp />);

fireEvent.click(await screen.findByRole('button', { name: 'Capture Frame' }));

await waitFor(() => {
expect(sendMessage).toHaveBeenCalledWith({
type: MessageType.CAPTURE_REQUEST,
tabId: 42,
payload: { quickCapture: false },
});
});
});
});
Loading