Repository navigation
Open the sidebar for dApp requests when "open sidebar by default" is on (Chrome) #3030
Description
Problem
Users can set Freighter to open in the sidebar by default (from #2335). This setting only changes what happens when the user clicks the toolbar icon.
When a dApp sends a request (connect, sign transaction, sign message, sign auth entry, add token), Freighter does this:
- If the sidebar is already open, the request shows in the sidebar.
- If the sidebar is closed, Freighter opens a new popup window. It does this even when the user prefers the sidebar.
MetaMask opens its side panel for dApp requests when the side panel is the preferred view (MetaMask/metamask-extension#38964). Freighter cannot do this yet.
Background
Sidebar mode was added in #2632 and improved in #2674. The design and known limits are in extension/specs/SIDEBAR_MODE.md. Those PRs and the spec do not discuss opening the sidebar for dApp requests when the sidebar is closed. This issue is new work.
Cause
Chrome only lets an extension call chrome.sidePanel.open() in response to a user gesture (for example, a click).
The dApp request goes from the page to the content script, then to the background. The background then runs async work (for example, buildStore(), network details, allow list check) before it calls openSigningWindow. Chrome no longer sees a user gesture at that point, so sidePanel.open() fails.
Related code:
extension/src/background/messageListener/freighterApiMessageListener.ts:openSigningWindowonly uses the sidebar whengetSidebarWindowId()is not null, which means the sidebar is already open.extension/src/background/index.ts:initSidebarBehavioronly callssetPanelBehavior({ openPanelOnActionClick }).extension/src/contentScript/helpers/redirectMessagesToBackground.ts: forwards dApp messages to the background.
Proposed solution
Use the same method as MetaMask: a round trip through the content script that keeps the user gesture.
-
The background gets a dApp request that needs UI. The user setting
isOpenSidebarByDefaultis on, and no sidebar is open. -
The background makes a one-time nonce for this tab. It saves the route as a "pending sidebar route". It sends a
REQUEST_OPEN_SIDEBARmessage with the nonce to the tab's content script. -
The content script checks
navigator.userActivation.isActive. If it is true, the content script sends anOPEN_SIDEBARmessage back with the nonce. If it is false, it does nothing. -
A new background listener gets
OPEN_SIDEBAR. This listener must be synchronous: noawaitbefore the open call. It checks that:- the sender is Freighter's content script,
sender.tab.idmatches the tab that made the request,- the nonce is correct and was not used before.
Then it calls
chrome.sidePanel.open({ windowId: sender.tab.windowId }). -
When the new sidebar connects (the
onConnecthandler inextension/src/background/index.ts), the background sends the pending route withSIDEBAR_NAVIGATE. It also adds the request UUID tosidebarQueueUuids, so closing the sidebar rejects the request. -
If the sidebar does not connect in about 500 ms, the background opens the popup window, as it does now.
Scope
- Chrome and other Chromium browsers only.
- Firefox: no change.
sidebarAction.open()needs a synchronous user input handler, and background messages do not count (seeextension/specs/SIDEBAR_MODE.md). Freighter keeps using the popup window on Firefox. - No change to
@stellar/freighter-api. dApps do not need to change anything.
Tasks
- Spike: confirm on current Chrome that the content script round trip keeps the user gesture and
sidePanel.open()works. - Add the new message types as typed constants in
@shared/constants/services.ts. Do not use raw strings. - Add the content script handler for
REQUEST_OPEN_SIDEBAR. - Add the synchronous background listener for
OPEN_SIDEBAR, with the nonce, tab and sender checks. - Update
openSigningWindowto try the sidebar first, then fall back to the popup window. Pass the request tab from all callers. - Send the pending route to the sidebar when it connects.
- Update the Preferences text so users know the setting also applies to dApp requests. Run
yarn build:extension:translations. - Unit tests: nonce checks, wrong tab, wrong sender, timeout fallback, pending route sent on connect.
- Manual QA on Chrome: setting on and sidebar closed, a dApp request from a click opens the sidebar. A request without a click (for example, on page load) opens the popup window.
- Update
extension/specs/SIDEBAR_MODE.mdwith the new flow.
Note: Playwright cannot test a real sidebar. The background rejects sidebar ports from pages that run in a tab, so in Playwright all requests use the popup window (see the "Testing" section of extension/specs/SIDEBAR_MODE.md). Playwright can still check the popup fallback.
Security notes
This change touches extension/src/background/ and extension/src/contentScript/. Without the nonce, tab and sender checks, any web page could open the Freighter sidebar after a user click. The background must only accept OPEN_SIDEBAR for a request that it started.
Acceptance criteria
- On Chrome, with "open sidebar by default" on and the sidebar closed, a dApp request that comes from a user click opens the Freighter sidebar and shows the request.
- If the sidebar cannot open, the popup window opens and the request still works.
- With the setting off, behavior does not change.
- On Firefox, behavior does not change.
References
- Sidebar Mode [extension] #2335, Add sidebar mode support #2632, Feature/sidebar mode audit #2674: sidebar mode
extension/specs/SIDEBAR_MODE.md: sidebar mode design and limits- feat: open sidepanel from dapp MetaMask/metamask-extension#38964: MetaMask opens the side panel for dApp confirmations
- feat: make the side panel the default surface DigitalGuards/myqrlwallet-extension#58: same method, with a nonce check
sidePanelAPI:sidePanel.close()andsidePanel.toggle()w3c/webextensions#521:sidePanel.open()user gesture limits
Activity
piyalbasu commentedon Oct 12, 2026
Previously it looked like we weren't able to do this programmatically, so before starting, quickly determine if we can do this with our current architecture