feat: add Edge/Chromium browser extension support#138
feat: add Edge/Chromium browser extension support#138phelix001 wants to merge 3 commits intolinux-credentials:mainfrom
Conversation
Port the Firefox web extension to Edge/Chromium (MV3, Chrome 111+). Key architectural differences from Firefox version: - Two content scripts: MAIN world (overrides navigator.credentials) and ISOLATED world (bridges to background via chrome.runtime) - window.postMessage bridge between MAIN and ISOLATED worlds (Firefox uses exportFunction/cloneInto which don't exist in Chromium) - Base64url encoding via btoa/atob helpers instead of Uint8Array.toBase64/fromBase64 (not available in Chromium) - Service worker background script instead of persistent background page - chrome.* namespace instead of browser.* New files: - webext/add-on-edge/ - Complete Edge/Chromium extension - webext/app/credential_manager_shim_edge.json.in - Native messaging manifest template for Chromium-based browsers Updated README with Edge/Chromium setup instructions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
iinuwa
left a comment
There was a problem hiding this comment.
Hello! Thanks for looking into this!
I haven't gotten the chance to test this out yet, but I did an initial look through, and there's quite a bit of duplicated code. We hope not to have to keep this around long term, but I still think it'd be helpful not to duplicate the code.
I think this means that I'd like to see if we can keep all the JavaScript files in the one add-on folder, with different manifests and "utils" files that contain the differences between Firefox and Chromium, and a check at runtime to import the correct one. If that means creating the extra "bridge" port in Firefox and/or a shim of cloneInto() for Chromium even if it's technically unnecessary, then that's fine with me.
Then we'd use Meson to bundle the add-ons for each browser platform.
I can help with the Meson parts; would you be willing to look into merging these two folders together?
webext/add-on-edge/background.js
Outdated
| function arrayBufferToBase64url(buffer) { | ||
| const bytes = new Uint8Array(buffer); | ||
| let binary = ''; | ||
| for (let i = 0; i < bytes.length; i++) { | ||
| binary += String.fromCharCode(bytes[i]); | ||
| } | ||
| return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); | ||
| } | ||
|
|
||
| function base64urlToBytes(str) { | ||
| if (!str) return null; | ||
| const padded = str.replace(/-/g, '+').replace(/_/g, '/'); | ||
| const binary = atob(padded); | ||
| const bytes = new Uint8Array(binary.length); | ||
| for (let i = 0; i < binary.length; i++) { | ||
| bytes[i] = binary.charCodeAt(i); | ||
| } | ||
| return bytes; | ||
| } |
There was a problem hiding this comment.
This is built into Chromium as Uint8Array.from/toBase64; can we use those?
There was a problem hiding this comment.
Done — switched to native Uint8Array.toBase64() / fromBase64() with {alphabet: "base64url", omitPadding: true} throughout. The manual btoa/atob helpers are removed entirely.
webext/add-on-edge/content-main.js
Outdated
| // Base64url helpers (Chromium doesn't have Uint8Array.toBase64/fromBase64) | ||
| function arrayBufferToBase64url(buffer) { | ||
| const bytes = new Uint8Array(buffer); | ||
| let binary = ''; | ||
| for (let i = 0; i < bytes.length; i++) { | ||
| binary += String.fromCharCode(bytes[i]); | ||
| } | ||
| return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); | ||
| } | ||
|
|
||
| function base64urlToArrayBuffer(str) { | ||
| if (!str) return null; | ||
| const padded = str.replace(/-/g, '+').replace(/_/g, '/'); | ||
| const binary = atob(padded); | ||
| const bytes = new Uint8Array(binary.length); | ||
| for (let i = 0; i < binary.length; i++) { | ||
| bytes[i] = binary.charCodeAt(i); | ||
| } | ||
| return bytes.buffer; | ||
| } |
There was a problem hiding this comment.
Same here; the comment is out of date: Chrome has had these for about 6 months.
There was a problem hiding this comment.
Fixed — removed the outdated comment and the manual helpers. Using native Uint8Array.toBase64() / fromBase64() here as well.
Address PR review feedback to eliminate code duplication between webext/add-on/ (Firefox) and webext/add-on-edge/ (Chromium). Key changes: - Unified architecture: both browsers now use MAIN + ISOLATED world content scripts with window.postMessage bridge, eliminating the need for Firefox-specific cloneInto()/exportFunction() APIs - Use native Uint8Array.toBase64()/fromBase64() for base64url encoding/decoding (supported in both Firefox 140+ and Chrome 111+) - Simplified background.js: ArrayBuffer serialization now happens in content-main.js, so background just forwards messages - Browser-specific manifests: manifest.firefox.json (background scripts) and manifest.chromium.json (service worker) - Browser API detection via globalThis.browser || globalThis.chrome in content-bridge.js and background.js Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Thanks for the review! I've pushed a commit that addresses all the feedback: Merged into a single
Browser-specific manifests: Native Browser API detection via Firefox now uses the same MAIN + ISOLATED world architecture as Chromium (with the bridge port as you suggested), which eliminates the need for I left a TODO in meson.build for the Chromium build target — happy to take your help on that part. |
|
Pushed two new commits on top of the refactor: docs: update READMEs for unified browser extension (
ci: pin Rust toolchain, update actions, add audit and JS checks (
Happy to split the CI/toolchain changes into a separate PR if you'd prefer to keep this one focused on the web extension work. |
- Update README to document Edge/Chromium support alongside Firefox - Fix webext/README references to deleted add-on-edge/ directory - Add manifest.json copy step for both Firefox and Chromium dev workflows - Add webext/add-on/manifest.json to .gitignore (generated for local dev) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1f2f730 to
612f201
Compare
|
Thanks for the update! I'm hoping to get to this sometime this weekend. In the meantime can you remove the CLAUDE.md? We're not ready to declare a policy on AI usage yet. The unrelated CI changes should also move to a separate PR to make this one easier to review. |
612f201 to
58d581e
Compare
|
Done! Two changes:
This PR is now 3 commits, all focused on the web extension work. @iinuwa ready when you are this weekend. |
Summary
Adds Edge/Chromium (Chrome 111+, Edge 111+) support to the browser extension, unified with the existing Firefox extension into a single codebase.
webext/add-on/with shared JS and browser-specific manifestsArchitecture
Both Firefox and Chromium now share the same content script architecture:
content-main.jsnavigator.credentials.create/get, serializes ArrayBuffers via nativeUint8Array.toBase64()/fromBase64()content-bridge.jswindow.postMessagetoruntime.connect()for native messagingbackground.jsBrowser differences handled via:
manifest.firefox.json— background scripts,gecko.strict_min_version: 140.0manifest.chromium.json— service worker, Chrome 111+globalThis.browser || globalThis.chromefor API detectionThe Python native messaging host (
credential_manager_shim.py) is reused unchanged.Commits
add-on-edge/directorycloneInto()/exportFunction()in favor of shared MAIN+ISOLATED architectureadd-on-edge/references in webext/READMETest plan
edge://extensions→ "Load unpacked" (copymanifest.chromium.jsontomanifest.jsonfirst)chrome://extensions) with equivalent setupmanifest.firefox.jsontomanifest.json)