Fix copy-url extension clipboard on recent Chromium/Brave#4890
Fix copy-url extension clipboard on recent Chromium/Brave#4890mads397 wants to merge 1 commit intobasecamp:devfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates Omarchy’s bundled Chromium “copy-url” extension to use an offscreen document for clipboard writes, and also adds new touchpad-toggle + Zellij configuration/theming integrations.
Changes:
- Switch copy-url from
scripting.executeScript+navigator.clipboardto an offscreen document copy flow. - Add touchpad toggle support via a polkit rule + sysfs toggle script, with a Waybar indicator and click action.
- Add a default Zellij config and a theme sync script that generates Zellij themes from Omarchy colors.
Reviewed changes
Copilot reviewed 10 out of 15 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| install/config/touchpad/install.sh | Installs polkit rule for touchpad toggle via pkexec |
| default/waybar/indicators/touchpad.sh | Waybar module script to show touchpad disabled state |
| default/touchpad/toggle-impl | Root-side sysfs toggle implementation for touchpad inhibit flag |
| bin/omarchy-toggle-touchpad | User command to toggle touchpad + manage cursor + notify Waybar |
| config/waybar/config.jsonc | Adds custom/touchpad-indicator module and click handler |
| config/waybar/style.css | Styles the new touchpad indicator (disabled state) |
| default/chromium/extensions/copy-url/background.js | Uses offscreen document + tabs API to copy URL |
| default/chromium/extensions/copy-url/offscreen.html | Offscreen document entrypoint |
| default/chromium/extensions/copy-url/offscreen.js | Implements the offscreen copy operation |
| default/chromium/extensions/copy-url/manifest.json | Adds permissions for offscreen + clipboardWrite, bumps version |
| config/zellij/config.kdl | Introduces a default Zellij config |
| bin/omarchy-theme-set-zellij | Generates Zellij theme KDL from Omarchy theme colors and applies it |
| bin/omarchy-theme-set | Calls the new Zellij theme sync during theme changes |
| bin/omarchy-refresh-zellij | Refreshes Zellij config and regenerates theme |
| bin/omarchy-restart-zellij | No-op restart shim for convention compatibility |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (action.id == "org.freedesktop.policykit.exec" && | ||
| subject.isInGroup("wheel")) { | ||
| var program = action.lookup("program"); | ||
| if (program && program.indexOf("/default/touchpad/toggle-impl") !== -1) { |
There was a problem hiding this comment.
The polkit rule matches the requested program using a substring check (indexOf). This can unintentionally authorize running other executables whose path merely contains "/default/touchpad/toggle-impl", which is broader than intended and can create a privilege-escalation path. Prefer an exact match against the expected absolute path (or a strictly anchored pattern), and consider resolving symlinks/realpath before comparison if supported in polkit JS.
| if (program && program.indexOf("/default/touchpad/toggle-impl") !== -1) { | |
| if (program === "/default/touchpad/toggle-impl") { |
| "version": "1.1", | ||
| "description": "Copy current URL to clipboard, this extension is installed by Omarchy", | ||
| "permissions": ["activeTab", "scripting", "notifications"], | ||
| "permissions": ["activeTab", "scripting", "notifications", "clipboardWrite", "offscreen"], |
There was a problem hiding this comment.
The extension no longer uses the Scripting API (chrome.scripting), but the manifest still requests the "scripting" permission. Dropping unused permissions reduces review surface area and avoids prompting users for capabilities the extension doesn’t need.
| "permissions": ["activeTab", "scripting", "notifications", "clipboardWrite", "offscreen"], | |
| "permissions": ["activeTab", "notifications", "clipboardWrite", "offscreen"], |
| }); | ||
| if (!contexts.length) { | ||
| await chrome.offscreen.createDocument({ | ||
| url: 'offscreen.html', | ||
| reasons: ['CLIPBOARD'], | ||
| justification: 'Copy URL to clipboard' | ||
| }); | ||
| } | ||
| await chrome.runtime.sendMessage({ target: 'offscreen', text: tab.url }); | ||
|
|
There was a problem hiding this comment.
The offscreen document is created (if missing) but never closed. Offscreen documents can keep resources alive unnecessarily; consider closing it after the copy completes (or implementing a short idle timeout / reuse strategy) so the extension doesn’t keep an extra context around indefinitely.
| }); | |
| if (!contexts.length) { | |
| await chrome.offscreen.createDocument({ | |
| url: 'offscreen.html', | |
| reasons: ['CLIPBOARD'], | |
| justification: 'Copy URL to clipboard' | |
| }); | |
| } | |
| await chrome.runtime.sendMessage({ target: 'offscreen', text: tab.url }); | |
| }); | |
| let createdOffscreen = false; | |
| if (!contexts.length) { | |
| await chrome.offscreen.createDocument({ | |
| url: 'offscreen.html', | |
| reasons: ['CLIPBOARD'], | |
| justification: 'Copy URL to clipboard' | |
| }); | |
| createdOffscreen = true; | |
| } | |
| await chrome.runtime.sendMessage({ target: 'offscreen', text: tab.url }); | |
| if (createdOffscreen) { | |
| // Close the offscreen document shortly after the copy request to | |
| // avoid keeping an extra context alive indefinitely. | |
| setTimeout(() => { | |
| try { | |
| chrome.offscreen.closeDocument(); | |
| } catch (err) { | |
| console.error('copy-url: failed to close offscreen document', err); | |
| } | |
| }, 1000); | |
| } |
| chrome.runtime.onMessage.addListener((msg) => { | ||
| if (msg.target !== 'offscreen' || !msg.text) return; | ||
| const ta = document.createElement('textarea'); | ||
| ta.value = msg.text; | ||
| document.body.appendChild(ta); | ||
| ta.select(); | ||
| document.execCommand('copy'); | ||
| ta.remove(); |
There was a problem hiding this comment.
The background script always shows a "URL copied" notification, but the offscreen handler doesn’t report success/failure and ignores the return value from document.execCommand('copy'). If copying fails (e.g., execCommand returns false), the user gets a false-positive. Have the offscreen listener return an explicit result (sendResponse/promise) and only notify on success (or show an error notification on failure).
| chrome.runtime.onMessage.addListener((msg) => { | |
| if (msg.target !== 'offscreen' || !msg.text) return; | |
| const ta = document.createElement('textarea'); | |
| ta.value = msg.text; | |
| document.body.appendChild(ta); | |
| ta.select(); | |
| document.execCommand('copy'); | |
| ta.remove(); | |
| chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { | |
| if (msg.target !== 'offscreen' || !msg.text) return; | |
| const ta = document.createElement('textarea'); | |
| ta.value = msg.text; | |
| document.body.appendChild(ta); | |
| ta.select(); | |
| let success = false; | |
| try { | |
| success = document.execCommand('copy'); | |
| } catch (e) { | |
| success = false; | |
| } | |
| ta.remove(); | |
| sendResponse({ success }); | |
| return true; |
| keybinds clear-defaults=true { | ||
| locked { | ||
| bind "Ctrl g" { SwitchToMode "normal"; } | ||
| } |
There was a problem hiding this comment.
The PR title/description are focused on fixing the Chromium copy-url extension, but this change set also introduces touchpad toggle (polkit rule + scripts + waybar module) and a full Zellij config/theme integration. This mismatch makes the PR harder to review/revert; consider splitting into separate PRs or updating the PR description/title to cover all included features.
The extension uses navigator.clipboard.writeText() injected via
scripting.executeScript(), but recent Chromium updates tightened the
Clipboard API's user activation requirement — an extension keyboard
shortcut no longer counts as a user gesture in the page context, so
the write silently fails. Also fails on restricted pages (chrome://,
brave://, extension pages).
Switch to an offscreen document approach:
- Read tab.url from the tabs API (no page injection needed)
- Create a hidden offscreen document with the CLIPBOARD reason
- Use document.execCommand('copy') backed by clipboardWrite permission
- Report success/failure back to background.js via sendResponse
- Close the offscreen document after use
- Drop unused scripting permission
Tested on Brave 1.76 (Chromium 134) on Linux (Hyprland/Wayland).
52d51fa to
da58b0f
Compare
…ubstring match - Indicator script now returns an icon for both enabled () and disabled () states instead of empty text, so waybar always shows the module. - Polkit rule: use endsWith instead of indexOf to prevent path prefix bypass (Copilot review feedback from PR basecamp#4890). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
What version are you having trouble with? Seems to be working here on Chromium 145. |
|
Thanks for checking — I'm on Brave (v145.1.87.191), not Chromium. I can't pinpoint which Brave version introduced the issue unfortunately. Submitted the PR in case it's useful to others hitting the same thing. |
Problem
The copy-url extension stopped working on recent Chromium and Brave versions. The extension uses
navigator.clipboard.writeText()injected into the active page viascripting.executeScript(), but recent browser updates tightened the Clipboard API's user activation requirement — an extension keyboard shortcut no longer counts as a user gesture in the page context, so the write silently fails.Additionally, the old approach fails entirely on restricted pages (
chrome://,brave://, extension pages, Web Store).Fix
Switch to an offscreen document approach:
tab.urlfrom the tabs API (no page injection needed)CLIPBOARDreasondocument.execCommand('copy')in the offscreen context, backed by theclipboardWritepermissionsendResponse— only show "copied" notification on successscriptingpermission (no longer injecting into pages)This works on all pages regardless of their clipboard permissions or activation state.
Changes
manifest.json: ReplacescriptingwithclipboardWriteandoffscreenpermissions, bump version to 1.1background.js: ReplaceexecuteScript+navigator.clipboardwith offscreen document message passing; only notify on confirmed success; close offscreen document after copyoffscreen.html/offscreen.js: New files — minimal offscreen document that handles clipboard writes and reports success/failureTesting
Tested on Brave 1.76 (Chromium 134) on Linux (Hyprland/Wayland). Confirmed working on: