Generated from the provided project files.
Page Capture is a Chrome Manifest V3 extension for capturing full pages or scrollable areas, then exporting PNG, image PDF, or native selectable-text PDF output. Its primary users are people who need full-page captures, scrollable-element captures, document-like page exports, lightweight annotation tools, or a native browser PDF with selectable text.
Core functionality:
- Capture the top-level page by scrolling the window and stitching visible slices.
- Capture scrollable containers by auto-detecting an element, dragging a rectangle, or clicking an element.
- Save a native full-page PDF through Chrome DevTools Protocol
Page.printToPDF; this does not stitch screenshot images and preserves selectable text where Chrome prints text as text. - Open an extension-hosted editor for crop, drawing, shape, eraser, and text annotations.
- Export editor output as PNG, paged PDF, continuous PDF, or detected document pages.
- Redact common sensitive fields and user-provided CSS selectors before capture.
- Run local syntax checks and a Playwright-based extension test harness.
Technology stack:
- Chrome Extension Manifest V3.
- Vanilla JavaScript ES modules.
- Chrome extension APIs:
runtime,tabs,scripting,storage,downloads,offscreen,debugger. - Offscreen document and browser canvas APIs.
- jsPDF 4.2.1 vendored in
extension/lib/jspdf.umd.min.js. - Poppins font files vendored under
extension/fonts/. - Playwright for browser-based extension tests.
- Node.js scripts for syntax validation.
High-level architecture:
- The popup collects settings and sends a
START_CAPTUREmessage to the background service worker. - The background service worker orchestrates capture, injects the content script, coordinates offscreen rendering, opens the editor, and starts downloads.
- The content script manipulates the target page during capture: scrolling, selection, redaction, sticky hiding, scroll locking, and HUD display.
- The offscreen document stores captured slices and builds previews/exports using canvas and jsPDF.
- The editor page displays previews and captures user edits before requesting export.
Current maturity:
- Inferred: This is a functional local-development Chrome extension with automated syntax and browser harness tests.
- It has CI configuration and dependency audit checks.
- It does not yet have production packaging, Chrome Web Store release automation, load/performance tests, or exhaustive native PDF regression tests.
| Path | Type | Purpose | Key Dependencies |
|---|---|---|---|
README.md |
Documentation | Short project entry point and verification commands. | docs/codebase-documentation.md |
CONTRIBUTING.md |
Documentation | Contributor setup, checks, and change guidelines. | npm scripts, extension conventions |
SECURITY.md |
Documentation | Vulnerability reporting guidance and security notes. | GitHub Security Advisories/issues |
docs/codebase-documentation.md |
Documentation | Full codebase documentation. | Project source files |
docs/release-checklist.md |
Documentation | GitHub publication, release, manifest, manual QA, and store packaging checklist. | README, manifest, npm scripts |
package.json |
Configuration | Node metadata and test scripts. | @playwright/test, scripts/check-syntax.mjs |
package-lock.json |
Configuration | Locked dependency graph for npm. | npm |
playwright.config.js |
Testing config | Configures Playwright browser tests under tests/browser. |
@playwright/test |
.github/workflows/ci.yml |
CI config | Runs syntax check, npm audit, and browser tests on push/PR. | GitHub Actions, Node 22, Playwright Chromium |
.gitignore |
Configuration | Ignores OS/editor files, node modules, build output, extension packages, temp files, and env files. | Git |
.gitattributes |
Configuration | Normalizes text line endings and marks binary assets. | Git |
.editorconfig |
Configuration | Documents shared editor whitespace and line-ending behavior. | EditorConfig-compatible editors |
scripts/check-syntax.mjs |
Testing utility | Recursively checks JavaScript syntax with node --check, excluding vendored jsPDF. |
Node fs, path, child_process |
extension/manifest.json |
Core extension config | Defines MV3 extension name, popup, service worker, icons, and permissions. | Chrome MV3 |
extension/popup.html |
Core UI | Popup form for mode and capture settings. | ui.css, popup.css, popup.js |
extension/popup.js |
Core UI logic | Loads/stores settings and sends START_CAPTURE. |
common.js, form-utils.js, Chrome storage/runtime APIs |
extension/popup.css |
Styling | Popup-specific layout and sizing. | ui.css variables |
extension/editor.html |
Core UI | Extension-hosted editor page. | ui.css, editor.css, editor.js |
extension/editor.js |
Core UI logic | Loads preview, handles tools, annotations, crop, zoom, undo, export. | CanvasRenderer, EditorToolLogic, form-utils.js, common.js, Chrome runtime API |
extension/editor.css |
Styling | Editor workspace, panels, tool controls, canvas layout. | ui.css variables |
extension/ui.css |
Shared styling | Global black/white theme, Poppins fonts, forms, buttons, shared link buttons. | Vendored fonts |
extension/background.js |
Core orchestration | Message router, capture coordinator, editor/download lifecycle, and native PDF download flow. | Chrome tabs/runtime/scripting/storage/downloads/offscreen APIs, native-pdf.js |
extension/content.js |
Core page integration | Runs in target tabs to collect metrics, scroll, select containers, redact content, freeze animations, show HUD. | DOM APIs, Chrome runtime connect/sendMessage |
extension/offscreen.html |
Core offscreen host | Loads offscreen runtime. | offscreen.js |
extension/offscreen.js |
Core rendering runtime | Stores capture slices, builds previews, detects pages, builds exports, returns object URLs. | SessionManager, image-ops.js, page-detect.js, pdf-builder.js |
extension/form-utils.js |
Supporting utility | Maps settings to popup/editor forms and reads forms back into objects. | DOM form elements |
extension/lib/utils/common.js |
Supporting utility | Default settings, allowlists, normalization, deep merge, filename helpers. | None beyond JS runtime |
extension/lib/core/session.js |
Core state | Holds captured slices and export session state in offscreen context. | computeSourceRect() |
extension/lib/core/image-ops.js |
Core rendering utility | Bitmap decoding, canvas cropping, slice stitching, preview and raster export helpers. | Canvas, FileReader, ImageBitmap |
extension/lib/core/pdf-builder.js |
Core export utility | Converts captured slices and annotations into jsPDF PDF or PNG blobs. | jsPDF, image-ops.js, page-detect.js, common.js |
extension/lib/core/native-pdf.js |
Core native export utility | Wraps Chrome DevTools Protocol Page.printToPDF and native PDF sizing/error handling. |
Chrome debugger API |
extension/lib/core/page-detect.js |
Core document detection | Detects document-like page rectangles from raster luminance analysis. | Canvas image data, image-ops.js |
extension/lib/ui/canvas-renderer.js |
Editor UI utility | Draws preview image, annotations, crop overlay, and selection handles. | Canvas |
extension/lib/ui/editor-tools.js |
Editor UI utility | Hit-testing, selection handles, geometry helpers. | DOM pointer event geometry |
extension/lib/jspdf.umd.min.js |
Vendored runtime | jsPDF library used for PDF output. | jsPDF license file |
extension/lib/jspdf-LICENSE.txt |
License | jsPDF license text. | None |
extension/fonts/* |
Vendored runtime assets | Poppins ExtraLight and bold font files plus license. | ui.css |
extension/public/logo.png |
Asset | Extension icon used in manifest. | manifest.json |
extension/THIRD_PARTY.md |
Documentation | Runtime third-party notices for jsPDF and Poppins. | Vendored assets |
extension/tests/runner.html |
Test harness UI | Extension-internal automated test page. | runner.js |
extension/tests/runner.js |
Test harness logic | Tests settings normalization, manifest permissions, offscreen validation, PDF/PNG export cases. | Chrome runtime/offscreen APIs |
extension/tests/e2e-bridge.html |
Test helper page | Extension page for future E2E capture bridging. | e2e-bridge.js |
extension/tests/e2e-bridge.js |
Test helper logic | Opens target tabs and drives capture/export through runtime messages. | Chrome tabs/runtime APIs |
tests/browser/extension-runner.spec.js |
Browser test | Loads unpacked extension in Chromium and runs extension/tests/runner.html. |
Playwright |
This project is a client-only Chrome extension. There is no backend server, database, ORM, server API layer, or server-side authentication flow visible in the provided code.
Architecture layers:
- Browser extension UI: popup and editor pages.
- Background service worker: central coordinator and message router.
- Content script: page-facing code that runs in the active tab.
- Offscreen document: hidden extension document for canvas, bitmap, blob, and PDF work.
- Shared utilities: settings normalization, form mapping, image operations, PDF building, page detection, editor hit-testing.
- Test harness: browser-loaded extension page driven by Playwright.
ASCII architecture diagram:
User
|
v
Popup UI
| START_CAPTURE
v
Background service worker
| inject/send messages | build/download
v v
Content script in active tab <---- slices ---- Offscreen document
| |
| page metrics, scroll, selection | SessionManager
| redaction, HUD, sticky hiding | image-ops, page-detect, pdf-builder
|
+---- native PDF mode ----> chrome.debugger Page.printToPDF
Background service worker
|
v
Editor page
| GET_EDITOR_DATA / EXPORT_CAPTURE
v
Background + offscreen export/download
Client-server interaction:
- Not applicable. There is no server.
API layer:
- There are no HTTP APIs.
- The internal API is Chrome extension message passing through
chrome.runtime.sendMessage,chrome.tabs.sendMessage, andchrome.runtime.onMessage.
Service/business logic layer:
extension/background.jsis the service/orchestration layer.extension/offscreen.js,extension/lib/core/pdf-builder.js,extension/lib/core/image-ops.js, andextension/lib/core/page-detect.jsprovide rendering/export logic.extension/lib/core/native-pdf.jsowns the Chrome DevTools Protocol native PDF print flow used bybackground.js.
Data access layer:
- There is no database.
- Persistent settings are stored in
chrome.storage.local. - Current editor/capture session data is stored in memory and mirrored to
chrome.storage.sessionaspendingCapture.
Authentication/authorization:
- No user login or roles exist.
- Security boundaries are Chrome extension permissions and sender checks.
getTrustedDebugOptions()only accepts debug tab overrides from extension pages.
External dependencies:
- Runtime: Chrome extension platform and vendored jsPDF/Poppins.
- Development/test:
@playwright/test. - No remote APIs are called by application code.
Configuration/environment variables:
- No
.envvariables are used. EXTENSION_HEADLESS=1can alter Playwright's persistent Chromium launch mode.
Error handling strategy:
- Message handlers catch async errors and return
{ error: error.message }. - The background worker notifies the popup/editor with
CAPTURE_ERROR,CAPTURE_CANCELLED, andCAPTURE_DONE. - Content-script HUD displays failure messages during normal capture modes.
- Offscreen validation rejects malformed slices and unknown messages.
Logging strategy:
- There is no structured application logging.
- Test output comes from npm/Playwright and the extension test page.
- Chrome extension runtime errors are inspected through Chrome extension service worker/page DevTools.
Security boundaries:
- The extension avoids
<all_urls>host permissions. - Active page access relies on
activeTaband content-script injection for the active tab. debuggeris required for native PDF export and is attached only during that operation, then detached infinally.
Files:
extension/manifest.json
Responsibilities:
- Declares Manifest V3.
- Names the extension
Page Capture. - Describes the extension as capturing full pages or scrollable areas and saving PNG, image PDF, or native selectable-text PDF output.
- Registers
popup.htmlas the default action popup. - Registers
background.jsas a module service worker. - Declares icon assets.
- Declares required permissions:
activeTab,debugger,scripting,storage,downloads,offscreen.
Important implementation detail:
debuggeris required because Chrome does not allow that permission to be optional. It is needed forPage.printToPDF.- There are no host permissions and no
<all_urls>grant.
Security implications:
debuggeris powerful and increases permission warning severity.- The implementation mitigates by attaching only during native PDF export and detaching in
finally.
Files:
extension/popup.htmlextension/popup.jsextension/popup.cssextension/form-utils.jsextension/lib/utils/common.js
Responsibilities:
- Presents capture mode controls.
- Captures user settings for page capture, scrollable box capture, redaction, waits, safety limits, and export behavior.
- Loads existing settings from
chrome.storage.local. - Normalizes settings through
mergeSettings(). - Persists updated settings.
- Sends
START_CAPTUREtobackground.js. - Receives progress and completion messages from the background worker.
- Shows two icon-only external links: GitHub repository and "Check out my website".
Main flow:
popup.jscallsinit().init()readssettingsfromchrome.storage.local.mergeSettings()applies defaults and clamps/allowlists values.populateCaptureForm()updates DOM controls.- On submit,
readCaptureForm()creates a partial settings object. deepMerge()merges the partial values into normalized stored settings.chrome.runtime.sendMessage()sendsSTART_CAPTURE.
Edge cases handled:
- If old stored defaults version is detected, some defaults are reset.
- Missing selected mode falls back to
full. - Capture status is restored when background emits completion/error/cancel events.
Known gaps:
- There is no explicit UI warning for
debuggerpermission before native PDF export. - There is no settings import/export feature.
File:
extension/background.js
Responsibilities:
- Owns capture lifecycle.
- Prevents concurrent capture/editor sessions with
captureInProgressandeditorInProgress. - Injects the content script if needed.
- Opens and closes the offscreen document.
- Routes messages to tab/offscreen/editor/popup contexts.
- Captures full-page screenshots by scrolling and using
chrome.tabs.captureVisibleTab. - Captures scrollable boxes by selecting and scrolling container content.
- Starts native PDF downloads and delegates Chrome DevTools Protocol printing to
extension/lib/core/native-pdf.js. - Opens editor and stores pending capture metadata.
- Handles editor export and cancellation.
- Validates export payload structure.
- Starts downloads and waits for download completion.
Important constants:
MAX_EXPORT_ANNOTATIONS = 500MAX_PATH_POINTS = 10000MAX_TEXT_LENGTH = 20000native-pdf.jsowns native print constants such asMAX_NATIVE_PDF_DIMENSION_IN = 200andNATIVE_PDF_GUARD_BAND_PX = 32- Capture throttle starts at
200ms, backs off to1200ms, and recovers by0.92.
Non-obvious logic:
- Native PDF mode (
mode === 'fullText') bypasses offscreen image capture and editor. It prepares page redaction/background options, callsdownloadNativePagePdf(), which delegates printing toprintPageToPdf()innative-pdf.js, restores page changes, and downloads directly. - Screenshot modes call
PREPARE_CAPTURE, collect page metrics, scroll/capture slices, send slices to offscreen, restore the page, then open the editor. captureVisibleClean()attempts a capture; if capture quota is hit, it backs off and retries.waitForDownloadCompletion()both listens todownloads.onChangedand polls the download item to avoid missing instant completion events.getTrustedDebugOptions()only accepts test/debug tab overrides from extension pages, not arbitrary senders.
Failure modes:
- Restricted pages cannot be scripted or debugged.
- Chrome capture quota can force retry/backoff.
- Device pixel ratio changes during capture abort the capture.
- Excessive slice count or height throws user-facing errors.
- Downloads can be interrupted.
Security notes:
- Sender validation exists for debug options.
- Export annotation payloads are normalized and capped.
- Native PDF errors are converted into safer user-facing messages.
File:
extension/content.js
Responsibilities:
- Handles
GET_METRICS,PREPARE_CAPTURE,RESTORE_CAPTURE,SCROLL_TO,SELECT_SCROLL_CONTAINER,SCROLL_CONTAINER,HUD_*, and selection messages. - Measures page and viewport dimensions.
- Scrolls top-level windows and containers.
- Supports iframe-related selection metadata.
- Shows capture HUD and selection overlay.
- Freezes animations and optionally hides sticky/fixed elements.
- Applies redaction to common sensitive fields and custom selectors.
- Suppresses backgrounds when requested.
- Locks scroll during capture where needed.
- Waits for images and layout stability.
- Maintains a keepalive port during long captures.
Important functions:
getMetrics(): returns viewport/page dimensions and DPR.prepareCapture(): applies capture-side page mutations.restoreCapture(): reverses those mutations.handleScrollTo(): scrolls the window and waits for stability.beginSelection(): resolves scrollable target selection.autoDetectScrollContainer(): finds the best scrollable element.applyRedaction(): masks sensitive fields and user selectors.waitForStability(): waits for images/idle timing around viewport changes.
Assumptions:
- Page DOM is accessible through the content script.
- Cross-origin iframe internals are not directly accessible.
- Visible captured pixels are sufficient for screenshot/export modes.
Edge cases handled:
- Scrollable container selection cancellation.
- Sticky/fixed restoration.
- Background restoration.
- Scroll-lock restoration.
- Cross-origin iframe selection metadata fallback.
Known gaps:
- Redaction is heuristic and cannot detect every site-specific secret.
- CSS selector errors in custom redaction are guarded by parsing, but semantic mistakes remain user responsibility.
Files:
extension/offscreen.htmlextension/offscreen.jsextension/lib/core/session.jsextension/lib/core/image-ops.jsextension/lib/core/pdf-builder.jsextension/lib/core/page-detect.js
Responsibilities:
- Holds capture slices in
SessionManager. - Converts data URLs from
captureVisibleTabintoImageBitmapobjects. - Builds editor previews.
- Detects page rectangles for document-like exports.
- Builds PDF/PNG blobs and object URLs.
- Validates slice payloads, crop rectangles, manual page rectangles, and numeric ranges.
Important message types:
RESET_SESSIONADD_SLICEDETECT_PAGESSET_MANUAL_PAGE_RECTSET_CROP_RECTBUILD_PREVIEWBUILD_EXPORTBUILD_AND_DOWNLOAD_EXPORTBUILD_PDFBUILD_AND_DOWNLOAD_PDFGET_SESSION_METRICS
Non-obvious logic:
- Object URLs are revoked after 60 seconds.
normalizeSlicePayload()rejects non-image data URLs.- Slice DPR is clamped to
0.25through8. - Session bitmaps are closed on reset to release memory.
Files:
extension/editor.htmlextension/editor.jsextension/editor.cssextension/lib/ui/canvas-renderer.jsextension/lib/ui/editor-tools.js
Responsibilities:
- Loads pending capture data through
GET_EDITOR_DATA. - Requests preview image from offscreen.
- Renders preview image and annotations on a canvas.
- Supports selection, crop, draw, shape, erase, and text tools.
- Maintains undo state for annotations.
- Supports zoom and preview scale controls.
- Reads export settings and sends
EXPORT_CAPTURE. - Shows GitHub and website icon links.
Important implementation choices:
- Editor annotations are stored in preview/base coordinates and scaled for rendering.
CanvasRendererdraws background, annotations, active action, crop overlay, and selection overlay.EditorToolLogiccentralizes hit testing and geometry so editor UI code does not duplicate shape math.- Text editing uses a positioned text input over the canvas, then commits to annotation data.
Failure modes:
- Preview loading may fail if there is no pending capture or offscreen runtime fails.
- Export can fail because of canvas size, PDF size, malformed edits, or download interruption.
Files:
extension/lib/utils/common.jsextension/form-utils.js
Responsibilities:
- Define defaults and allowlists.
- Normalize settings and prevent unsafe merge keys.
- Clamp numeric settings.
- Sanitize filenames.
- Convert forms to/from settings objects.
Important security detail:
deepMerge()skips__proto__,prototype, andconstructorkeys to reduce prototype pollution risk.
There are no HTTP routes or REST endpoints in the provided code. The internal API is Chrome extension messaging.
Purpose:
Starts a capture or native PDF export.
Transport:
chrome.runtime.sendMessage() to background.js.
Auth required: No app auth. Requires extension context and Chrome permissions.
Request body:
{
"target": "background",
"type": "START_CAPTURE",
"options": {
"mode": "full"
}
}Success response:
{
"ok": true,
"editor": true
}For native PDF:
{
"ok": true,
"downloaded": true
}Possible errors:
| Status | Cause | Response |
|---|---|---|
| Not applicable | Capture already running | { "error": "A capture or editor is already running." } |
| Not applicable | Restricted page | { "error": "Native PDF export cannot access this page. Try a normal web page tab." } |
| Not applicable | Download interrupted | { "error": "Download was interrupted." } |
Related files:
extension/popup.jsextension/background.jsextension/content.jsextension/offscreen.js
Purpose:
Returns pending capture metadata to the editor page.
Transport:
chrome.runtime.sendMessage() to background.js.
Request body:
{
"target": "background",
"type": "GET_EDITOR_DATA"
}Success response:
{
"settings": {},
"mode": "full",
"tabId": 123,
"tabTitle": "Example"
}Possible errors:
| Status | Cause | Response |
|---|---|---|
| Not applicable | No pending capture | { "error": "No capture is waiting for editing." } |
Purpose:
Exports edited capture data from the editor.
Transport:
chrome.runtime.sendMessage() to background.js.
Request body:
{
"target": "background",
"type": "EXPORT_CAPTURE",
"payload": {
"settings": {},
"edits": {
"cropRect": null,
"annotations": []
}
}
}Success response:
{
"ok": true,
"downloaded": true
}Possible errors:
| Status | Cause | Response |
|---|---|---|
| Not applicable | No pending capture | { "error": "No capture is waiting for export." } |
| Not applicable | Invalid export | { "error": "Failed to build export." } |
| Not applicable | Download interrupted | { "error": "Download was interrupted." } |
Purpose:
Closes or finalizes the editor state and clears pending capture.
Transport:
chrome.runtime.sendMessage() to background.js.
Request body:
{
"target": "background",
"type": "CANCEL_EDITOR"
}Success response:
{
"ok": true
}Purpose:
Returns target page dimensions, viewport dimensions, DPR, and scrollability.
Transport:
chrome.tabs.sendMessage() to content.js.
Request body:
{
"type": "GET_METRICS"
}Success response:
Shape is inferred from content.js: viewport width/height, total width/height, scroll position, DPR, and scrollability metadata.
Purpose:
Apply and undo page mutations for capture.
Transport:
chrome.tabs.sendMessage() to content.js.
Request body:
{
"type": "PREPARE_CAPTURE",
"options": {}
}Effects:
- Freezes animations when configured.
- Hides sticky/fixed elements when configured.
- Applies redaction when configured.
- Suppresses backgrounds when requested.
- Starts content keepalive.
RESTORE_CAPTURE reverses these effects.
Purpose:
Scrolls the target page to a specific y coordinate and waits for stability.
Transport:
chrome.tabs.sendMessage() to content.js.
Request body:
{
"type": "SCROLL_TO",
"y": 1200,
"waitMs": 150
}Possible errors:
| Cause | Response |
|---|---|
| Page cannot scroll as expected | Error propagated to background |
| DPR changes | Background throws Page zoom or display scale changed during capture. |
Purpose:
Selects a scrollable region using auto, rectangle, or element selection mode.
Transport:
chrome.tabs.sendMessage() to content.js.
Request body:
{
"type": "SELECT_SCROLL_CONTAINER",
"selectionMode": "auto",
"message": "Select a scrollable area"
}Possible errors:
| Cause | Response |
|---|---|
| User cancels | Selection cancelled error |
| No scrollable container found | Error propagated to background |
All offscreen messages use:
{
"target": "offscreen",
"type": "MESSAGE_NAME",
"payload": {}
}| Type | Purpose | Main Validation |
|---|---|---|
RESET_SESSION |
Clears existing bitmaps and starts a mode. | Mode must be full, box, fullText, or null. |
ADD_SLICE |
Converts an image data URL into an ImageBitmap and stores it. |
dataUrl must start with data:image/; cropRect required. |
DETECT_PAGES |
Runs page detection on captured slices. | Uses document options and max page limit. |
SET_MANUAL_PAGE_RECT |
Stores a manually selected page rectangle. | Rect must be positive. |
SET_CROP_RECT |
Stores editor crop rectangle. | Rect must be positive or null. |
BUILD_PREVIEW |
Builds preview data URL from slices. | Requires at least one slice. |
BUILD_EXPORT |
Builds PDF/PNG and returns data URL. | Delegates to buildExportBlob(). |
BUILD_AND_DOWNLOAD_EXPORT |
Builds blob and returns object URL/filename. | Object URL is revoked after 60 seconds. |
BUILD_PDF |
Builds PDF and returns buffer plus metrics. | Delegates to buildPdf(). |
GET_SESSION_METRICS |
Returns slice count and dimensions. | None. |
There is no database, schema, ORM, migration system, or persistent user model visible in the provided code.
Persistent data:
chrome.storage.local.settings: user capture/export settings.chrome.storage.session.pendingCapture: transient editor metadata.
In-memory data:
SessionManager.session.slices: capturedImageBitmapslices and metadata.SessionManager.session.detectedCandidates: page detection candidates.SessionManager.session.detectedPages: ordered detected pages.SessionManager.session.manualRect: manually selected document page rectangle.SessionManager.session.cropRect: editor crop rectangle.- Editor annotations in
editor.jsmemory until export.
Settings model:
| Field | Type | Source | Notes |
|---|---|---|---|
mode |
string | DEFAULT_SETTINGS.mode |
full, box, or fullText. |
pdf.pageSize |
string | DEFAULT_SETTINGS.pdf |
One of A3/A4/A5/Legal/Letter/Tabloid/Ledger. |
pdf.orientation |
string | DEFAULT_SETTINGS.pdf |
portrait or landscape. |
pdf.margins |
string | DEFAULT_SETTINGS.pdf |
none, small, normal. |
pdf.scaleMode |
string | DEFAULT_SETTINGS.pdf |
fit, 100, custom. |
pdf.scale |
number | DEFAULT_SETTINGS.pdf |
Clamped 0.1 to 3. |
pdf.layout |
string | DEFAULT_SETTINGS.pdf |
paged, continuous, detected. |
capture.disableAnimations |
boolean | DEFAULT_SETTINGS.capture |
Default true. |
capture.hideSticky |
boolean | DEFAULT_SETTINGS.capture |
Default false. |
capture.redactSensitive |
boolean | DEFAULT_SETTINGS.capture |
Default false. |
capture.redactSelectors |
string | DEFAULT_SETTINGS.capture |
Max 500 chars. |
capture.overlap |
number | DEFAULT_SETTINGS.capture |
Clamped 20 to 200. |
scrollBox.selectionMode |
string | DEFAULT_SETTINGS.scrollBox |
auto, rect, element. |
document.maxPages |
number | DEFAULT_SETTINGS.document |
Clamped 1 to 500; default 200. |
safety.maxSlices |
number | DEFAULT_SETTINGS.safety |
Clamped 1 to 1000; default 400. |
safety.maxHeight |
number | DEFAULT_SETTINGS.safety |
Clamped 1000 to 500000; default 300000. |
safety.maxExportPixels |
number | DEFAULT_SETTINGS.safety |
Clamped 1000000 to 500000000; default 120000000. |
filename.pattern |
string | DEFAULT_SETTINGS.filename |
Max 180 chars; supports {title} and {date}. |
Annotation model:
| Type | Fields | Notes |
|---|---|---|
path |
points, color, strokeWidth |
Points capped in background export normalization. |
rect |
x, y, width, height, color, strokeWidth, fillColor |
Shape annotation. |
ellipse |
Same as rect |
Shape annotation. |
triangle |
Same as rect |
Shape annotation. |
arrow |
Same as rect |
Shape annotation. |
text |
x, y, text, color, fontSize, text style fields |
Text length capped in background export normalization. |
Sensitive fields:
- There are no saved passwords or tokens.
- Captured page pixels/PDFs may include sensitive information from the active page unless redaction is configured.
chrome.storage.local.settingscan contain custom redaction selectors and preferences, but not user secrets by design.
There is no application-level authentication or authorization.
Login/register flow:
- Not visible from provided code.
Token/session handling:
- Not visible from provided code.
Password handling:
- Not visible from provided code.
Protected routes:
- No HTTP routes exist.
Permission-based access:
- Chrome extension permissions gate access to browser APIs.
activeTaballows temporary access to the active tab after user interaction.scriptingallows the extension to injectcontent.js.storagepersists settings and pending session metadata.downloadsstarts file downloads.offscreenallows hidden rendering/export runtime.debuggerallows native PDF export through DevTools Protocol.
Authorization decisions:
getTrustedDebugOptions()ensures test/debug tab override options only come from extension pages.- Content and offscreen handlers require message target values where appropriate.
Common failure cases:
- Chrome restricted pages cannot be scripted.
- Another debugger already attached to a tab prevents native PDF export.
- User can deny or remove extension permissions outside this code.
- Trigger: user selects
Full Page (scroll window)in popup and submits. - Entry point:
popup.jsonSubmit(). - Files/functions:
popup.js:onSubmit()background.js:startCapture(),captureFullPage(),captureVisibleClean(),openEditor()content.js:getMetrics(),prepareCapture(),handleScrollTo(),restoreCapture()offscreen.js:RESET_SESSION,ADD_SLICE
- Sequence:
Popup -> Background: START_CAPTURE
Background -> Content: PREPARE_CAPTURE
Background -> Content: GET_METRICS
Background -> Offscreen: RESET_SESSION
loop:
Background -> Content: SCROLL_TO
Background -> Chrome tabs API: captureVisibleTab
Background -> Offscreen: ADD_SLICE
Background -> Content: RESTORE_CAPTURE
Background -> Editor: open editor.html
- Data transformations:
- Visible screenshot data URL becomes
ImageBitmap. - Slice metadata records crop rectangle, scroll offset, crop top/bottom, and DPR.
- Visible screenshot data URL becomes
- Output:
- Editor opens with pending capture.
- Failure cases:
- DPR changes.
- Too many slices.
- Capture height exceeds safety limit.
- Chrome capture quota requires retry or eventually fails.
- Trigger: user selects
Scrollable Box. - Entry point:
background.jscaptureScrollableBox(). - Files/functions:
content.js:beginSelection(),autoDetectScrollContainer(),selectRect(),selectElement(),handleScrollContainer()background.js:captureScrollableSelection()offscreen.js:ADD_SLICE
- Sequence:
Background -> Content: SELECT_SCROLL_CONTAINER
Content -> User: overlay selection if needed
Background -> Content: SCROLL_CONTAINER for each scroll position
Background -> Chrome: captureVisibleTab
Background -> Offscreen: ADD_SLICE with cropRect from selection
Background -> Editor: open editor.html
- Data transformations:
- Element/rectangle geometry is clamped to viewport.
- Captured viewport image is cropped by offscreen/PDF rendering using
cropRect.
- Output:
- Editor opens with scrollable region capture.
- Failure cases:
- No scrollable element found.
- Selection cancelled.
- Cross-origin iframe internals unavailable.
- Trigger: user selects
Native Full Page PDF. - Entry point:
background.jsstartCapture(). - Files/functions:
background.js:downloadNativePagePdf()native-pdf.js:printPageToPdf(), native size resolution, DevTools Protocol error mappingcontent.js:prepareCapture(),restoreCapture()
- Sequence:
Popup -> Background: START_CAPTURE mode fullText
Background -> Content: PREPARE_CAPTURE
Background -> chrome.debugger: attach
Background -> DevTools Protocol: Page.enable, Runtime.enable
Background -> DevTools Protocol: Emulation.setEmulatedMedia screen
Background -> DevTools Protocol: Page.getLayoutMetrics
Background -> DevTools Protocol: Runtime.evaluate DOM scroll dimensions
Background -> DevTools Protocol: Page.printToPDF
Background -> chrome.debugger: detach
Background -> Chrome downloads: download data URL PDF
Background -> Content: RESTORE_CAPTURE
- Data transformations:
- CSS pixel dimensions are converted to inches using
96 CSS px = 1 inch. - A 32px guard band is added to reduce blank trailing page risk.
- A single page range is requested when not dimension-capped.
- CSS pixel dimensions are converted to inches using
- Output:
- PDF downloaded directly, no editor.
- Failure cases:
- Restricted pages.
- Another debugger attached.
- Chrome print engine paginates extreme pages.
- Canvas-rendered text remains graphics.
- Trigger: user clicks export in editor.
- Entry point:
editor.jsonExport(). - Files/functions:
form-utils.js:readExportForm()background.js:handleExportCapture(),downloadCaptureExport()offscreen.js:BUILD_AND_DOWNLOAD_EXPORTpdf-builder.js:buildExportBlob(),buildPdf()
- Sequence:
Editor -> Background: EXPORT_CAPTURE with settings and edits
Background -> Offscreen: BUILD_AND_DOWNLOAD_EXPORT
Offscreen -> pdf-builder/image-ops: build Blob
Offscreen -> Background: object URL and filename
Background -> Chrome downloads: download
Background -> Editor: EXPORT_DONE or EXPORT_ERROR
Background: finalize editor state
- Data transformations:
- Editor annotations are normalized/capped.
- Crop rect is normalized.
- PDF/PNG blob is created in offscreen context.
- Output:
- Downloaded file.
- Failure cases:
- Export pixel cap exceeded.
- Browser canvas dimension cap exceeded.
- No captured content.
- Download interrupted.
- Trigger:
npm run test:browser. - Entry point:
tests/browser/extension-runner.spec.js. - Files/functions:
- Playwright persistent Chromium context.
extension/tests/runner.htmlextension/tests/runner.jsrunTests()
- Sequence:
npm -> Playwright
Playwright -> Chromium persistent context with extension loaded
Playwright -> extension/tests/runner.html
Runner -> offscreen API and runtime messages
Runner -> assertions
Playwright -> fail/pass result
- Output:
- Playwright pass/fail result.
- Failure cases:
- Chromium extension launch fails.
- Offscreen API unavailable.
- Export metrics/assertions fail.
File: extension/background.js
Purpose:
Main capture entry point. Normalizes mode, resolves the active tab, prepares the page, dispatches either native PDF export or screenshot capture, restores page state, opens editor or downloads output, and reports status.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
options |
object | No | User settings and optional test debug target. |
sender |
object | No | Chrome message sender for debug option trust checks. |
Returns:
Promise resolving to { ok: true, editor: true }, { ok: true, downloaded: true }, { cancelled: true }, or { error }.
Side effects:
Injects content script, creates offscreen document, scrolls page, captures visible tab, opens editor tab, starts downloads, mutates page during capture then restores it.
Dependencies:
mergeSettings(), ensureContentScript(), sendToTab(), ensureOffscreenDocument(), captureFullPage(), captureScrollableBox(), downloadNativePagePdf().
Errors:
Converts thrown errors into popup notifications and response objects.
File: extension/background.js
Purpose:
Scrolls a top-level page and captures visible slices with overlap.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
tabId |
number | Yes | Target tab. |
windowId |
number | Yes | Window for captureVisibleTab. |
settings |
object | Yes | Normalized settings. |
metrics |
object | Yes | Page metrics from content script. |
Returns:
Promise resolving to { modeOverride }.
Side effects:
Scrolls the target page and sends slices to offscreen.
Failure modes:
Too many slices, excessive height, DPR instability, quota failures.
File: extension/background.js
Purpose:
Captures selected scrollable element/region by scrolling its content and cropping visible tab images.
Parameters:
Target tab/window, settings, selection metadata, and capture options.
Returns:
Promise resolving to { modeOverride }.
Side effects:
Scrolls selected container and sends cropped slice metadata to offscreen.
File: extension/lib/core/native-pdf.js
Purpose:
Uses Chrome DevTools Protocol to print a tab to PDF without screenshot stitching.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
tabId |
number | Yes | Target tab. |
settings |
object | Yes | PDF settings. |
Returns:
Base64 PDF data from Chrome.
Side effects:
Attaches debugger, enables DevTools domains, emulates screen media, calls Page.printToPDF, then detaches debugger.
Errors:
Restricted page, debugger conflict, missing PDF data.
File: extension/background.js
Purpose:
Waits for Chrome download completion or interruption.
Parameters:
downloadId from chrome.downloads.download().
Returns:
complete, interrupted, or timeout.
Notes:
Uses both downloads.onChanged and polling because generated file downloads can complete before listeners observe a state transition.
File: extension/content.js
Purpose:
Applies page-side capture settings before screenshot or native PDF export.
Parameters:
Capture options object.
Side effects:
Freezes animations, hides sticky elements, redacts sensitive fields, suppresses backgrounds, starts keepalive, adds Escape cancel handler.
File: extension/content.js
Purpose:
Restores all page mutations made by prepareCapture().
Side effects:
Restores hidden elements, redacted nodes, animation styles, backgrounds, scroll locks, overlays, HUD, and keepalive.
File: extension/content.js
Purpose:
Computes viewport and page dimensions used by full-page capture.
Returns:
Metrics including viewport dimensions, total dimensions, scroll state, DPR, and scrollability.
File: extension/content.js
Purpose:
Starts scrollable container selection in auto, rectangle, or element mode.
Returns:
Selection metadata with crop rect and scroll dimensions.
File: extension/lib/core/session.js
Purpose:
Owns offscreen capture session state.
Constructor:
Initializes empty session by calling reset().
Important methods:
| Method | Purpose |
|---|---|
reset(mode) |
Clears slices and closes bitmaps. |
addSlice(slice) |
Appends an image slice and metadata. |
getMetrics() |
Returns slice count, base dimensions, mode, and DPR. |
Side effects:
Closes ImageBitmap resources on reset.
File: extension/lib/core/pdf-builder.js
Purpose:
Builds either PNG/JPEG-style image blob or PDF blob from captured slices and editor edits.
Parameters:
slices, export payload, and sessionGlobal.
Returns:
{ blob, mime }.
Errors:
No captured content, export pixel cap exceeded, PDF generation failure.
File: extension/lib/core/pdf-builder.js
Purpose:
Builds PDF output in paged, continuous, or detected layout.
Parameters:
Captured slices, export payload, and session state.
Returns:
Object containing PDF buffer and optional debug metrics.
Dependencies:
jsPDF, detectPages(), image stitching helpers, annotation drawing helpers.
File: extension/lib/core/page-detect.js
Purpose:
Detects document-like page rectangles from captured raster slices.
Parameters:
Captured slices and options for profile, dedupe, and max pages.
Returns:
ordered, candidates, pageCount, and candidateCount.
Algorithm:
Downsamples bitmap to max width 320, computes luminance, estimates page/background contrast, segments rows/columns, falls back to borderless detection when needed, and sorts/dedupes candidates.
File: extension/lib/utils/common.js
Purpose:
Combines defaults and overrides, then normalizes the result.
Security notes:
Calls deepMerge(), which ignores __proto__, prototype, and constructor.
File: extension/lib/utils/common.js
Purpose:
Allowlists string options, clamps numeric values, normalizes layout flags, and applies defaults.
Returns:
Complete normalized settings object.
File: extension/lib/ui/canvas-renderer.js
Purpose:
Renders editor preview canvas with background image, annotations, active action, crop overlay, and selection overlay.
Important methods:
| Method | Purpose |
|---|---|
init(width, height) |
Sets canvas dimensions. |
setAnnotations(list) |
Stores annotations and re-renders. |
setCropRect(rect) |
Stores crop rect and re-renders. |
updateZoom(zoom) |
Changes displayed CSS size. |
render() |
Draws the full canvas state. |
File: extension/lib/ui/editor-tools.js
Purpose:
Provides geometry and hit-testing logic for annotations and selection handles.
Important methods:
| Method | Purpose |
|---|---|
getCanvasPoint(event, canvas) |
Converts pointer event to canvas coordinates. |
hitTestAnnotation(point, annotation) |
Tests whether a point hits an annotation. |
findAnnotationIndex(point, annotations) |
Finds top-most annotation at a point. |
getSelectionHandle(point, annotation) |
Detects resize handle hit. |
No .env file or runtime environment variables are required by application code.
| Variable | Required | Example | Purpose | Security Notes |
|---|---|---|---|---|
EXTENSION_HEADLESS |
No | 1 |
When set to 1, Playwright extension test launches persistent Chromium with headless: true. |
Test-only. |
Package scripts:
| Script | Command | Purpose |
|---|---|---|
npm test |
npm run test:syntax |
Default syntax-only test. |
npm run test:syntax |
node scripts/check-syntax.mjs |
Checks JS syntax with Node. |
npm run test:browser |
playwright test |
Runs extension browser harness. |
npm run test:ci |
syntax, audit, browser | Local equivalent of CI checks. |
Manifest permissions:
| Permission | Purpose | Security Notes |
|---|---|---|
activeTab |
Temporary active tab access after user action. | Avoids broad host permissions. |
debugger |
Native PDF export through Page.printToPDF. |
Powerful permission; attach/detach tightly. |
scripting |
Inject content.js. |
Needed for page capture. |
storage |
Store settings and pending capture metadata. | Do not store secrets. |
downloads |
Save generated files. | Filenames are sanitized. |
offscreen |
Build previews and exports in hidden document. | Needed for canvas/blob work. |
Validation strategy:
- Settings are normalized in
common.js. - Offscreen slice and rectangle payloads are validated in
offscreen.js. - Export edits are normalized and capped in
background.js. - Filenames are sanitized in
buildFilename()andsanitizeFilename(). - Content script parses custom selector lists and ignores invalid selector fragments.
Common validation failures:
- Unknown mode falls back to
full. - Invalid numeric settings fall back to defaults or are clamped.
- Invalid offscreen slice data URL throws
Invalid slice image data. - Missing crop metadata throws
Invalid slice crop metadata. - Too-large raster export throws an export size error.
Error response format:
- Internal messages usually return
{ error: "message" }. - Successful messages usually return
{ ok: true }plus optional fields. - There is no HTTP status code layer.
Exception handling approach:
background.jswraps capture flow intry/catch/finally.offscreen.jswraps async handlers and returns errors throughsendResponse.popup.jsandeditor.jsdisplay user-facing status text.
Known gaps:
- There is no typed schema library for runtime messages.
- There is no central error class taxonomy.
- There is no structured error telemetry.
Suggested improvements:
- Add a small message schema module for
START_CAPTURE,EXPORT_CAPTURE, offscreen messages, and content messages. - Add predictable error codes alongside user-facing messages.
- Add tests for failure states: restricted pages, interrupted download, native debugger conflict.
What is logged:
- There is no structured runtime logging in source code.
- Test logs are emitted by npm and Playwright.
- Extension runtime errors are visible in Chrome extension DevTools.
Where to inspect logs locally:
- Popup: right-click popup, inspect.
- Background service worker:
chrome://extensions-> Page Capture -> service worker inspect. - Editor/offscreen/test pages: inspect extension pages directly.
- Playwright traces: retained on test failure per
playwright.config.js.
Debugging common issues:
| Issue | Where to look | Likely cause |
|---|---|---|
| Extension does not load | chrome://extensions |
Wrong folder selected; select extension/, not repo root. |
| Native PDF fails | Background service worker console | Restricted page or debugger conflict. |
| Capture stops midway | Popup/HUD/background console | DPR change, capture quota, excessive page height. |
| Export fails | Editor status/offscreen console | Canvas size or export pixel cap. |
| Tests fail locally | Playwright output | Chromium install, extension load, offscreen API availability. |
Suggested monitoring metrics:
- Not visible from provided code.
- Inferred: useful local metrics would include slice count, total capture height, export duration, PDF page count, download outcome, and memory-related failures.
Health checks:
- Not visible from provided code.
Authentication risks:
- No app authentication exists. This is acceptable for a local Chrome extension, but there are no user accounts or identity checks.
Authorization risks:
debuggeris broad and powerful. The implementation uses it only in native PDF flow and detaches infinally.getTrustedDebugOptions()limits tab override options to extension pages.- No
<all_urls>permission is present, reducing broad page access.
Input validation risks:
- Settings normalization is strong for known settings.
- Full message schema validation is still incomplete. Unknown message types now receive explicit errors in the background service worker, content script, and offscreen document, but payload schemas remain distributed and incomplete.
- Editor annotations are capped and normalized during export, which limits memory abuse.
Sensitive data exposure:
- Captures may include sensitive visible page content.
- Redaction exists but is opt-in and heuristic.
- Downloads are saved through Chrome downloads API, outside extension control after save.
Secrets management:
.envand.env.*are ignored.- No hardcoded secrets were visible in provided application code.
Password storage:
- Not applicable.
Token expiration and refresh:
- Not applicable.
CORS:
- No network API calls are made by application code.
- Not applicable from provided code.
Rate limiting:
- There is capture throttling around
captureVisibleTab. - There is no user-level or request-level rate limiting because there is no server.
SQL/NoSQL injection:
- No database or query layer exists.
XSS/CSRF:
- No remote HTML is intentionally inserted into extension pages.
- Extension pages use static HTML and text content.
- Content script manipulates target pages, but does not render untrusted page HTML into extension UI.
File upload risks:
- No file upload feature exists.
Dependency risks:
- Runtime jsPDF is vendored. It should be periodically reviewed and updated.
- Playwright is dev-only.
npm audit --omit=optionalis part of CI.
Recommended improvements:
- Add runtime schemas for all message types.
- Add tests for malformed message payloads and content-script message edge cases.
- Add a user-facing warning near native PDF mode explaining
debuggeruse. - Add a safer "redaction preview" or default redaction option for sensitive contexts.
- Add dependency update automation.
Test framework:
- Node syntax checker script.
- Playwright browser tests.
- Extension-internal test harness in
extension/tests/runner.js.
Test files:
scripts/check-syntax.mjstests/browser/extension-runner.spec.jsextension/tests/runner.htmlextension/tests/runner.jsextension/tests/e2e-bridge.htmlextension/tests/e2e-bridge.js
How to run:
npm run test:syntax
npm run test:browser
npm run test:ciWhat is covered:
- JS syntax for non-vendored project JS files.
- Manifest permissions: required
debugger, no optionaldebugger, no<all_urls>. - Settings normalization and prototype pollution guard.
- Offscreen invalid slice rejection and unknown-message error response.
- Continuous PDF export single page behavior.
- PNG export behavior.
- Detected/manual PDF crop export behavior.
- High-DPR continuous PDF behavior.
- Tall continuous PDF scaling behavior.
What is not covered:
- Real active-tab capture against a normal website.
- Native
Page.printToPDFoutput text selection. - Restricted Chrome pages.
- Download interruption.
- Cross-origin iframe behavior.
- Manual editor interactions through pointer events.
- Visual regression screenshots.
- Performance/load behavior on very tall pages.
Test coverage table:
| Feature | Existing Tests | Missing Tests | Priority |
|---|---|---|---|
| Settings normalization | testSettingsNormalization() |
UI migration edge cases | Medium |
| Manifest permissions | testManifestPermissions() |
Chrome permission prompt UX | Low |
| Message/offscreen validation | Invalid slice and unknown-message tests | Full malformed payload matrix and content-script edge cases | High |
| Continuous PDF | Several PDF metrics tests | Visual/text extraction assertions | High |
| Detected/manual PDF crop | Synthetic crop export test | Real detected-page fixture coverage | Medium |
| PNG export | PNG export test | JPEG lossy branch | Medium |
| Native PDF | Mode preserved and manifest permission | Real Page.printToPDF E2E with text extraction |
High |
| Editor | Not directly covered | Pointer interaction tests | High |
| Scrollable box | Not directly covered | Auto/rect/element E2E | High |
| Redaction | Not directly covered | Sensitive input/custom selector capture tests | High |
Mocking/stubbing strategy:
- The extension test harness uses real extension APIs where possible.
- It builds synthetic canvas slices instead of capturing real web pages for most export tests.
Prerequisites:
- Google Chrome or Chromium.
- Node.js compatible with the project. CI uses Node 22.
- npm.
Installation:
npm ci
npx playwright install chromiumLocal development setup:
- Open
chrome://extensions. - Enable Developer mode.
- Click Load unpacked.
- Select the
extension/directory.
Run:
- The extension runs directly from files in
extension/. - There is no build step.
Test:
npm run test:syntax
npm run test:browser
npm audit --omit=optionalProduction build:
- Not visible from provided code.
- Inferred: package the
extension/directory as a Chrome extension zip after excluding dev/test files if desired.
Deployment assumptions:
- No server deployment.
- Chrome Web Store packaging/signing workflow is not visible from provided code.
Common setup errors:
| Error | Cause | Fix |
|---|---|---|
| Manifest missing/unreadable | Loaded repo root instead of extension folder. | Load extension/. |
debugger optional warning |
Old manifest cached or invalid manifest version. | Reload extension after manifest update. |
| Browser test fails to launch | Playwright Chromium missing. | Run npx playwright install chromium. |
| Extension page cannot access target | Restricted page. | Test on a normal web page. |
| Dependency | Type | Used For | Location | Risks/Notes |
|---|---|---|---|---|
| Chrome extension APIs | Runtime platform | Extension UI, service worker, content scripts, downloads, offscreen, native PDF. | extension/*.js, manifest.json |
API behavior differs across Chrome versions. |
| jsPDF 4.2.1 | Vendored runtime | Building image-based PDF exports. | extension/lib/jspdf.umd.min.js, pdf-builder.js |
Keep license and update periodically. |
| Poppins fonts | Vendored runtime asset | Black/white minimal UI typography. | extension/fonts, ui.css |
Adds package size; no remote font dependency. |
@playwright/test 1.59.1 |
Development only | Browser extension test harness. | package.json, tests/browser |
Needs installed Chromium. |
| Node.js | Development only | Syntax checker and npm scripts. | scripts/check-syntax.mjs |
CI uses Node 22. |
Possible alternatives:
- Use TypeScript plus a schema validator for stronger message contracts.
- Use a smaller PDF library only if it supports required PDF layout and image embedding needs.
- Use Chrome native PDF only for text-PDF mode, as currently done, because image-mode editor exports need raster composition.
Manifest V3:
- Chosen by Chrome extension platform requirements.
- Trade-off: service worker lifetime requires keepalive patterns and session persistence.
No build step:
- Simpler local development and unpacked loading.
- Trade-off: no bundler checks, no tree shaking, and vendored assets must be manually maintained.
Offscreen document:
- Needed for canvas, blobs, and PDF generation outside the service worker.
- Trade-off: extra message boundary and object URL lifecycle to manage.
Scroll-stitch image capture:
- Works for rendered pixels and editor annotations.
- Trade-off: cannot preserve selectable text and is sensitive to sticky elements, animations, lazy loading, DPR changes, and capture quotas.
Native PDF mode:
- Preserves selectable text where Chrome can print text.
- Trade-off: requires
debuggerpermission and follows Chrome print behavior rather than exact screenshot pixels.
Settings normalization:
- Lightweight manual allowlists avoid bringing in a runtime schema dependency.
- Trade-off: message schemas are still distributed across modules.
Black/white Poppins UI:
- Provides a minimal visual identity.
- Trade-off: low color vocabulary requires careful affordance styling for errors and states.
Known technical debt:
background.jsis still large and combines orchestration, downloads, export validation, and editor lifecycle, though native PDF printing has been moved tonative-pdf.js.- Message contracts are implicit.
- Native PDF has limited automated coverage.
Expected files:
extension/lib/utils/common.js: add mode toALLOWED.modeand defaults if needed.extension/popup.html: add radio option.extension/form-utils.js: ensure form read/populate supports it.extension/background.js: branch insidestartCapture().extension/offscreen.js: allow mode if offscreen uses it.extension/tests/runner.js: add normalization and behavior tests.
Common mistakes:
- Forgetting to normalize mode.
- Letting new mode fall through to image capture logic incorrectly.
- Failing to restore page mutations after errors.
Expected files:
extension/lib/utils/common.js: add toALLOWED.exportFormat.extension/form-utils.js: add form mapping.extension/editor.html: add UI control.extension/lib/core/pdf-builder.jsor new export module: implement blob creation.extension/offscreen.js: route export build.extension/tests/runner.js: add export test.
Common mistakes:
- Returning data URLs instead of object URLs for large files.
- Forgetting download filename extension.
- Skipping size limits.
Expected files:
extension/editor.html: add tool button and controls.extension/editor.js: add tool state, pointer behavior, undo behavior, export annotation data.extension/lib/ui/canvas-renderer.js: draw annotation preview.extension/lib/ui/editor-tools.js: hit testing and selection handles.extension/lib/core/pdf-builder.js: draw annotation in final export.extension/tests/runner.js: add export serialization test if possible.
Common mistakes:
- Drawing tool in preview but not in export.
- Forgetting scale conversions between preview and base coordinates.
- Letting annotations grow unbounded.
Expected files:
- New
extension/lib/utils/messages.jsor similar. - Use it from
background.js,content.js,offscreen.js,popup.js, andeditor.js.
Common mistakes:
- Validating only popup messages while editor/offscreen messages remain loose.
- Rejecting legacy stored settings without migration.
Expected files:
extension/tests/runner.jsfor extension-internal logic.tests/browser/*.spec.jsfor Playwright browser tests.- Potential fixture pages under
tests/fixturesor extension test pages.
Common mistakes:
- Testing only pure functions while skipping extension API flows.
- Not running tests with extension loaded.
Code conventions:
- Vanilla ES modules.
- Existing style uses two-space indentation in many UI/background files and four-space indentation in some core utility files. Keep local file style.
- Prefer small focused helpers over broad rewrites.
- Avoid remote runtime dependencies because extension runs unpacked with vendored assets.
Naming conventions:
- Message names use uppercase strings such as
START_CAPTURE. - Settings use camelCase nested objects.
- Mode values are lowercase strings:
full,box,fullText.
Folder conventions:
- Core capture/export logic belongs under
extension/lib/core. - UI-only helper logic belongs under
extension/lib/ui. - Shared settings/form utilities are top-level extension modules or under
lib/utils. - Browser tests live under
tests/browser. - Extension-internal tests live under
extension/tests.
Fragile areas:
- Native PDF sizing and page range logic.
- Capture scroll overlap/cropping math.
- DPR stability assumptions.
- Cross-origin iframe handling.
- Large canvas/PDF memory usage.
- Service worker/offscreen lifetime behavior.
Upgrade considerations:
- jsPDF upgrades need PDF compatibility tests for paged, continuous, detected pages, annotations, and high-DPR output.
- Chrome API changes can affect offscreen documents and debugger commands.
- Playwright upgrades can affect extension launch behavior.
Refactoring opportunities:
- Split remaining
background.jsresponsibilities into capture, download, editor session, and message router modules. - Extract shared message validators.
- Add typed documentation comments or TypeScript gradually.
- Add automated fixture pages for capture scenarios.
Current limitations:
- Native PDF requires
debuggerpermission. - Native PDF follows Chrome print behavior, not exact screenshot rendering.
- Screenshot modes are raster and cannot preserve selectable page text.
- Redaction is heuristic and opt-in.
- Very long pages can hit canvas, PDF, memory, or Chrome print limits.
- Cross-origin iframe content is limited.
- No production packaging workflow is visible.
- No structured logging/telemetry.
- No full runtime schema validation.
Roadmap:
| Priority | Improvement | Reason | Suggested Approach |
|---|---|---|---|
| High | Add message schema validation | Reduces malformed message risk. | Central messages.js validators used by all contexts. |
| High | Add native PDF E2E test | Current requested behavior depends on Chrome print behavior. | Use a local fixture page, export native PDF, verify page count/text. |
| High | Add editor interaction tests | Editor is a core user workflow. | Playwright pointer tests on extension editor page. |
| High | Add redaction tests | Prevents sensitive data leakage regressions. | Fixture page with password/token fields and custom selectors. |
| Medium | Continue splitting background.js |
Native PDF printing is now isolated; downloads, image capture, and editor session logic remain in the service worker. | Move download/capture/editor functions to focused modules after adding fixture-based E2E tests. |
| Medium | Add visual regression tests | Captures are visual by nature. | Use Playwright screenshots for popup/editor and fixture exports. |
| Medium | Add packaging script | Reduces release mistakes. | Zip curated extension/ files, exclude tests if desired. |
| Medium | Add dependency update process | jsPDF is vendored. | Document update script/checklist and add release tests. |
| Low | Improve user-facing permission explanation | debugger warning can surprise users. |
Add concise note near native PDF mode. |
Hard-coded and configuration-based limits:
| Constraint | Current Value | Source in Code | Impact | Risk Level | Suggested Improvement |
|---|---|---|---|---|---|
| Max configured slices | Default 400, clamp 1 to 1000 |
extension/lib/utils/common.js DEFAULT_SETTINGS.safety.maxSlices, normalizeSettings() |
Long pages stop when estimated slices exceed limit. | Medium | Surface estimated slice count before capture. |
| Max configured capture height | Default 300000, clamp 1000 to 500000 CSS px |
common.js, background.js enforceSliceLimits() |
Very long pages fail early. | Medium | Add per-site presets and clearer UI warning. |
| Max export pixels | Default 120000000, clamp 1000000 to 500000000 |
common.js, pdf-builder.js enforceExportPixelLimit() |
Large PNG/JPEG exports are blocked. | Medium | Estimate export size in editor before export. |
| Max annotations | 500 |
background.js MAX_EXPORT_ANNOTATIONS |
Extra annotations are ignored during export normalization. | Low | Show warning when limit reached. |
| Max path points | 10000 |
background.js MAX_PATH_POINTS |
Long freehand paths are truncated. | Low | Simplify paths as user draws. |
| Max text annotation length | 20000 |
background.js MAX_TEXT_LENGTH |
Huge text annotations are truncated. | Low | Add UI counter or cap input. |
| Native PDF max dimension | 200 inches |
native-pdf.js MAX_NATIVE_PDF_DIMENSION_IN |
Very tall/wide native PDF pages may scale or paginate. | Medium | Add explicit native PDF page count warning. |
| Native PDF guard band | 32 CSS px |
native-pdf.js NATIVE_PDF_GUARD_BAND_PX |
Reduces blank trailing page risk. | Low | Add native PDF regression tests. |
| Native PDF CSS px per inch | 96 |
native-pdf.js CSS_PX_PER_IN |
Converts layout metrics to print paper dimensions. | Low | Keep aligned with Chrome print assumptions. |
| Capture throttle | 200ms min, 1200ms max, 1.5 backoff, 0.92 recover |
background.js CAPTURE_THROTTLE |
Slows capture to avoid Chrome quota. | Medium | Record quota retry metrics. |
| Download wait timeout | 120000ms |
background.js waitForDownloadCompletion() |
Long/stuck downloads resolve as timeout. | Medium | Return timeout-specific UI message. |
| Offscreen keepalive ping | 20000ms |
offscreen.js |
Keeps offscreen communication alive. | Low | Monitor service worker/offscreen lifecycle failures. |
| Content keepalive ping | Not fully visible in summary table; implementation exists in content.js |
content.js startKeepAlive() |
Helps long capture sessions. | Medium | Add tests for long capture lifecycle. |
| Preview max canvas height | 12000 px |
image-ops.js MAX_CANVAS_HEIGHT |
Previews are downscaled for very tall captures. | Low | Display preview scale to user. |
| Export max canvas height | 32767 px |
image-ops.js MAX_EXPORT_CANVAS_HEIGHT |
Raster export fails above browser canvas limits. | Medium | Auto-suggest PDF for large exports. |
| PDF image max height | 2048 px |
pdf-builder.js MAX_PDF_IMAGE_PX_HEIGHT |
PDF image chunks are constrained. | Medium | Document chunking algorithm in code comments. |
| PDF image max width | 3000 px |
pdf-builder.js MAX_PDF_IMAGE_PX_WIDTH |
Wide content is chunked/scaled. | Medium | Add wide-page export tests. |
| PDF image max pixels | 6000000 |
pdf-builder.js MAX_PDF_IMAGE_PIXELS |
Controls image chunk size in PDF. | Medium | Tune with performance tests. |
| PDF max dimension | 14400 pt |
pdf-builder.js MAX_PDF_DIMENSION_PT |
Continuous PDF may scale to fit jsPDF/PDF limits. | Medium | Warn when scaling changes final dimensions. |
| Document max pages | Default 200, clamp 1 to 500 |
common.js document settings |
Detected page output is capped. | Medium | Add UI message when candidate pages exceed cap. |
| Redact selector length | 500 chars |
common.js normalizeSettings() |
Long custom selector list is truncated. | Low | Validate per-selector and show errors. |
| Filename pattern length | 180 chars |
common.js normalizeSettings() |
Long filename patterns are truncated. | Low | Show preview filename. |
| Object URL revoke delay | 60000ms |
offscreen.js |
Downloads must start before URL revoked. | Medium | Revoke after download confirmation when possible. |
Unknown or infrastructure limits:
| Constraint | Current Value | Source in Code | Impact | Risk Level | Suggested Improvement |
|---|---|---|---|---|---|
| Maximum concurrent users | Not visible from provided code | No server/infrastructure config | Extension runs per browser profile; no shared server concurrency. | Low | Not applicable unless backend is added. |
| Maximum requests per second | Not visible from provided code | No HTTP server | No server RPS limit. Chrome capture API quota still applies. | Medium | Keep capture throttle and add retry metrics. |
| API payload size limit | Not visible from provided code | Chrome message APIs used | Large messages can fail or affect memory. | Medium | Prefer object URLs/blob handles over large data URLs. |
| File upload size limit | Not applicable | No upload feature | None. | Low | Not needed unless upload/import is added. |
| Database connection pool | Not applicable | No database | None. | Low | Not needed unless backend is added. |
| Memory ceiling | Not visible from provided code | Browser/runtime dependent | Large captures can exhaust renderer/offscreen memory. | High | Add measured memory/performance tests. |
| CPU-intensive operations | Inferred estimate | Canvas stitching, luminance detection, PDF generation | Large exports can block extension pages/offscreen. | Medium | Use chunking and progress reporting. |
| Long-running operation timeout | Partial: download timeout 120000ms; waits configured |
background.js, common.js |
Captures may run until user cancels or limits hit. | Medium | Add global capture timeout setting. |
| Cache size/TTL | Not visible from provided code | No cache beyond object URL revoke | Memory retained until session reset. | Medium | Add explicit cleanup on all terminal paths. |
| Session/token expiration | Not applicable | No auth | None. | Low | Not needed unless auth added. |
| Storage limits | Not visible from provided code | Chrome storage APIs | Settings are small; session metadata is small. | Low | Avoid storing slice data in storage. |
| Deployment platform limits | Not visible from provided code | No deployment config beyond CI | Chrome extension store constraints unknown. | Medium | Add packaging/release docs. |
| Third-party API limits | Chrome API dependent | Chrome tabs/debugger/downloads APIs | Capture quotas and debugger behavior are browser-defined. | Medium | Test on supported Chrome versions. |
Behavior when limits are exceeded:
- Capture safety limits throw and show popup/HUD error.
- Export pixel/canvas limits throw and show editor export error.
- Native PDF dimension cap scales or paginates depending on Chrome print behavior.
- Download timeout currently resolves to
timeout; interrupted downloads throw in caller paths where checked.
- No automated Chrome Web Store packaging script or store listing files were provided.
- No production deployment configuration exists because this is a client-only extension.
- No fixture pages for real capture scenarios were provided.
- No performance profiles, memory traces, or load tests were provided.
- No browser compatibility matrix was provided.
- No user-facing privacy policy or store listing copy was provided.
- No project license has been selected.
- No design source files were provided.
- No typed schemas or generated API documentation are present.
- Add
docs/testing-fixtures.mddocumenting recommended local pages for sticky headers, lazy images, iframes, long pages, and sensitive fields. - Add
docs/native-pdf-compatibility.mdwith Chrome version behavior, page-size limits, selectable text expectations, and known print-layout differences. - Add
docs/privacy-policy.mdor store listing copy before Chrome Web Store submission. - Add inline JSDoc to
native-pdf.jsnative PDF helpers andbackground.jscapture orchestration helpers. - Add diagrams for editor annotation coordinate conversion and PDF chunking after those modules are split or typed.