feat: add Edge/Chromium web extension port#138
feat: add Edge/Chromium web extension port#138phelix001 wants to merge 2 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. |
Summary
webext/add-on-edge/with a dual content-script architecture to work around Chromium's security model (noexportFunction/cloneInto)Architecture
The Firefox extension uses
exportFunction()to directly overridenavigator.credentialsfrom a content script. Chromium doesn't support this API, so the Edge port uses two content scripts:content-main.jsnavigator.credentials.create/get, communicates viawindow.postMessagecontent-bridge.jspostMessagetochrome.runtime.connect()for native messagingbackground.jscredential_manager_shim.py)Other differences from Firefox version:
btoa/atobhelpers (Chromium lacksUint8Array.toBase64/fromBase64)chrome.*namespace instead ofbrowser.*The Python native messaging host (
credential_manager_shim.py) is completely reused — no changes needed.New files
webext/add-on-edge/manifest.json— MV3 manifest with dual content scriptswebext/add-on-edge/content-main.js— MAIN world credential overridewebext/add-on-edge/content-bridge.js— ISOLATED world IPC bridgewebext/add-on-edge/background.js— Service worker for native messagingwebext/app/credential_manager_shim_edge.json.in— Native messaging manifest templatewebext/README.mdwith Edge/Chromium setup instructionsTest plan
edge://extensions→ "Load unpacked"chrome://extensions) with equivalent setup🤖 Generated with Claude Code