From ef86a18cb9777a7785ef6b398c171763da5fd3b7 Mon Sep 17 00:00:00 2001 From: Rostyslav Nihrutsa Date: Tue, 28 Oct 2025 12:56:56 +0200 Subject: [PATCH 1/6] fix(tabs): remove unsupported `documentId` option in sendTabMessage for Firefox --- src/browser.ts | 11 +++++++++++ src/tabs.ts | 5 ++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/browser.ts b/src/browser.ts index a98adad..f44cbe6 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -7,3 +7,14 @@ export const browser = (): typeof chrome => { return chrome; }; + +export const isFirefox = (): boolean => { + let isFirefox = false; + + try { + // @ts-expect-error + isFirefox = !!browser().runtime.getBrowserInfo; + } catch (_e) {} + + return isFirefox; +}; diff --git a/src/tabs.ts b/src/tabs.ts index 006095f..2e672a3 100644 --- a/src/tabs.ts +++ b/src/tabs.ts @@ -1,4 +1,4 @@ -import {browser} from "./browser"; +import {browser, isFirefox} from "./browser"; import {throwRuntimeError} from "./runtime"; import {handleListener} from "./utils"; @@ -270,6 +270,9 @@ export const sendTabMessage = ( options: MessageSendOptions = {} ): Promise => new Promise((resolve, reject) => { + // Firefox does not support `documentId` option + if (isFirefox()) delete options.documentId; + tabs().sendMessage(tabId, message, options, response => { try { throwRuntimeError(); From 8658e48a02054e6686ce16c62bfcc8a905723037 Mon Sep 17 00:00:00 2001 From: Rostyslav Nihrutsa Date: Tue, 28 Oct 2025 13:23:50 +0200 Subject: [PATCH 2/6] Revert "fix(tabs): remove unsupported `documentId` option in sendTabMessage for Firefox" This reverts commit ef86a18cb9777a7785ef6b398c171763da5fd3b7. --- src/browser.ts | 11 ----------- src/tabs.ts | 5 +---- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/src/browser.ts b/src/browser.ts index f44cbe6..a98adad 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -7,14 +7,3 @@ export const browser = (): typeof chrome => { return chrome; }; - -export const isFirefox = (): boolean => { - let isFirefox = false; - - try { - // @ts-expect-error - isFirefox = !!browser().runtime.getBrowserInfo; - } catch (_e) {} - - return isFirefox; -}; diff --git a/src/tabs.ts b/src/tabs.ts index 2e672a3..006095f 100644 --- a/src/tabs.ts +++ b/src/tabs.ts @@ -1,4 +1,4 @@ -import {browser, isFirefox} from "./browser"; +import {browser} from "./browser"; import {throwRuntimeError} from "./runtime"; import {handleListener} from "./utils"; @@ -270,9 +270,6 @@ export const sendTabMessage = ( options: MessageSendOptions = {} ): Promise => new Promise((resolve, reject) => { - // Firefox does not support `documentId` option - if (isFirefox()) delete options.documentId; - tabs().sendMessage(tabId, message, options, response => { try { throwRuntimeError(); From f2b16586e68a746ed64e008d5deb9be327338864 Mon Sep 17 00:00:00 2001 From: Rostyslav Nihrutsa Date: Wed, 18 Feb 2026 18:32:51 +0200 Subject: [PATCH 3/6] feat(sidebar): enhance API support and add error handling for unsupported features --- package-lock.json | 2 +- package.json | 2 +- src/sidebar.ts | 131 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 99 insertions(+), 36 deletions(-) diff --git a/package-lock.json b/package-lock.json index efc9d48..5bb1226 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@commitlint/cli": "^20.0.0", "@commitlint/config-conventional": "^20.0.0", "@release-it/conventional-changelog": "^10.0.1", - "@types/chrome": "^0.1.12", + "@types/chrome": "^0.1.36", "@types/jest": "^30.0.0", "husky": "^9.1.7", "jest": "^30.1.3", diff --git a/package.json b/package.json index 8317bac..32dc0d6 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "@commitlint/cli": "^20.0.0", "@commitlint/config-conventional": "^20.0.0", "@release-it/conventional-changelog": "^10.0.1", - "@types/chrome": "^0.1.12", + "@types/chrome": "^0.1.36", "@types/jest": "^30.0.0", "husky": "^9.1.7", "jest": "^30.1.3", diff --git a/src/sidebar.ts b/src/sidebar.ts index b5dbfaa..db2e919 100644 --- a/src/sidebar.ts +++ b/src/sidebar.ts @@ -6,6 +6,7 @@ type Color = string | ColorArray; type ColorArray = chrome.extensionTypes.ColorArray; type OpenOptions = chrome.sidePanel.OpenOptions; +type CloseOptions = chrome.sidePanel.CloseOptions; type PanelOptions = chrome.sidePanel.PanelOptions; type PanelBehavior = chrome.sidePanel.PanelBehavior; type IconDetails = opr.sidebarAction.IconDetails; @@ -19,6 +20,8 @@ const isAvailableOperaSidebar = (): boolean => globalThis?.opr?.sidebarAction != // Chromium standard const sidePanel = (): typeof chrome.sidePanel | undefined => browser().sidePanel; +export class SidebarError extends Error {} + // Methods export const getSidebarOptions = (tabId?: number): Promise => callWithPromise(cb => { @@ -56,8 +59,22 @@ export const canOpenSidebar = (): boolean => { return false; }; +export const canCloseSidebar = (): boolean => { + if (sidePanel()) { + return true; + } + + const sb = (sidebarAction() as FirefoxSidebarAction) || undefined; + + if (sb) { + return !!sb.close; + } + + return false; +}; + export const openSidebar = (options: OpenOptions): Promise => - callWithPromise(cb => { + callWithPromise(async cb => { const sp = sidePanel(); if (sp) { @@ -67,12 +84,39 @@ export const openSidebar = (options: OpenOptions): Promise => const sb = sidebarAction() as FirefoxSidebarAction | undefined; if (sb?.open) { - return sb.open(); + const result = sb.open(); + + if (result instanceof Promise) { + await result; + } + + return cb(); + } + + throw new SidebarError("The sidebarAction.open API is not supported in this browser"); + }); + +export const closeSidebar = (options: CloseOptions): Promise => + callWithPromise(async cb => { + const sp = sidePanel(); + + if (sp) { + return sp.close(options, cb); } - console.warn("The sidebar open API is not supported in this browser"); + const sb = sidebarAction() as FirefoxSidebarAction | undefined; + + if (sb?.close) { + const result = sb.close(); - cb(); + if (result instanceof Promise) { + await result; + } + + return cb(); + } + + throw new SidebarError("The sidebarAction.close API is not supported in this browser"); }); export const setSidebarOptions = (options?: PanelOptions): Promise => @@ -80,9 +124,7 @@ export const setSidebarOptions = (options?: PanelOptions): Promise => const sp = sidePanel(); if (!sp) { - console.warn("The chrome.sidePanel.setOptions API is not supported for this browser"); - - return cb(); + throw new SidebarError("The chrome.sidePanel.setOptions API is not supported for this browser"); } sp.setOptions(options || {}, cb); @@ -93,12 +135,44 @@ export const setSidebarBehavior = (behavior?: PanelBehavior): Promise => const sp = sidePanel(); if (!sp) { - console.warn("The chrome.sidePanel.setPanelBehavior API is not supported in this browser"); + throw new SidebarError("The chrome.sidePanel.setPanelBehavior API is not supported in this browser"); + } + + sp.setPanelBehavior(behavior || {}, cb); + }); + +export const isOpenSidebar = (windowId?: number): Promise => + callWithPromise(async cb => { + const sb = sidebarAction() as FirefoxSidebarAction | undefined; + + if (sb?.isOpen) { + const result = sb.isOpen({windowId}); + + if (result instanceof Promise) { + return cb(await result); + } else { + return cb(result); + } + } + + throw new SidebarError("The sidebarAction.isOpen API is not supported in this browser"); + }); + +export const toggleSidebar = (): Promise => + callWithPromise(async cb => { + const sb = sidebarAction() as FirefoxSidebarAction | undefined; + + if (sb?.toggle) { + const result = sb.toggle(); + + if (result instanceof Promise) { + await result; + } return cb(); } - sp.setPanelBehavior(behavior || {}, cb); + throw new SidebarError("The sidebarAction.toggle API is not supported in this browser"); }); // Customs methods @@ -153,19 +227,16 @@ export const setSidebarTitle = (title: string | number, tabId?: number): Promise callWithPromise(async cb => { const sb = sidebarAction(); - if (!sb) { - console.warn("The sidebarAction.setTitle API is supported only in Opera or Firefox"); + if (sb?.setTitle) { + const result = sb.setTitle({tabId, title: title.toString()}); + if (result instanceof Promise) { + await result; + } return cb(); } - const result = sb.setTitle({tabId, title: title.toString()}); - - if (result instanceof Promise) { - await result; - } - - cb(); + throw new SidebarError("The sidebarAction.setTitle API is supported only in Opera or Firefox"); }); export const setSidebarBadgeText = (text: string | number, tabId?: number): Promise => @@ -178,9 +249,7 @@ export const setSidebarBadgeText = (text: string | number, tabId?: number): Prom return cb(); } - console.warn("The opr.sidebarAction.setBadgeText API is supported only in Opera"); - - cb(); + throw new SidebarError("The sidebarAction.setBadgeText API is supported only in Opera"); }); export const clearSidebarBadgeText = (tabId?: number): Promise => setSidebarBadgeText("", tabId); @@ -211,9 +280,7 @@ export const setSidebarIcon = (details: IconDetails): Promise => return cb(); } - console.warn("The sidebarAction.setIcon API is supported only in Opera or Firefox"); - - cb(); + throw new SidebarError("The sidebarAction.setIcon API is supported only in Opera or Firefox"); }); export const setSidebarBadgeTextColor = (color: Color, tabId?: number): Promise => @@ -226,9 +293,7 @@ export const setSidebarBadgeTextColor = (color: Color, tabId?: number): Promise< return cb(); } - console.warn("The opr.sidebarAction.setBadgeTextColor API is supported only in Opera"); - - cb(); + throw new SidebarError("The sidebarAction.setBadgeTextColor API is supported only in Opera"); }); export const setSidebarBadgeBgColor = (color: Color, tabId?: number): Promise => @@ -241,9 +306,7 @@ export const setSidebarBadgeBgColor = (color: Color, tabId?: number): Promise => @@ -258,7 +321,7 @@ export const getSidebarTitle = (tabId?: number): Promise => return (sb as any).getTitle({tabId}); } - throw new Error("The sidebarAction.getTitle API not available"); + throw new SidebarError("The sidebarAction.getTitle API not available"); }); export const getSidebarBadgeText = (tabId?: number): Promise => @@ -269,7 +332,7 @@ export const getSidebarBadgeText = (tabId?: number): Promise => return sb.getBadgeText({tabId}, cb); } - throw new Error("The opr.sidebarAction.getBadgeText API is supported only in Opera"); + throw new SidebarError("The sidebarAction.getBadgeText API is supported only in Opera"); }); export const getSidebarBadgeTextColor = (tabId?: number): Promise => @@ -280,7 +343,7 @@ export const getSidebarBadgeTextColor = (tabId?: number): Promise => return sb.getBadgeTextColor({tabId}, cb); } - throw new Error("The opr.sidebarAction.getBadgeTextColor API is supported only in Opera"); + throw new SidebarError("The sidebarAction.getBadgeTextColor API is supported only in Opera"); }); export const getSidebarBadgeBgColor = (tabId?: number): Promise => @@ -291,5 +354,5 @@ export const getSidebarBadgeBgColor = (tabId?: number): Promise => return sb.getBadgeBackgroundColor({tabId}, cb); } - throw new Error("The opr.sidebarAction.getBadgeBackgroundColor API is supported only in Opera"); + throw new SidebarError("The sidebarAction.getBadgeBackgroundColor API is supported only in Opera"); }); From 645b47c2655967aad331f7f42fbb7178ce6ae1b2 Mon Sep 17 00:00:00 2001 From: Anjey Tsibylskij <130153594+atldays@users.noreply.github.com> Date: Wed, 18 Feb 2026 18:46:39 +0200 Subject: [PATCH 4/6] chore(biome): exclude `addon` folder from linting --- biome.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/biome.json b/biome.json index c652017..53b2fa4 100644 --- a/biome.json +++ b/biome.json @@ -25,7 +25,8 @@ "includes": [ "src/**/*.{ts,tsx,js,jsx}", "**/*.{json,jsonc,md,mdx,cjs,mjs}", - "!dist/**/*" + "!dist/**/*", + "!addon/**/*" ] }, "formatter": { From 2cfbe447d2ac1be0cc946443642d653b4ba5ecab Mon Sep 17 00:00:00 2001 From: Rostyslav Nihrutsa Date: Thu, 19 Feb 2026 12:12:33 +0200 Subject: [PATCH 5/6] feat(sidebar): improve `isOpenSidebar` for available in Chrome --- src/sidebar.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/sidebar.ts b/src/sidebar.ts index db2e919..da08cc0 100644 --- a/src/sidebar.ts +++ b/src/sidebar.ts @@ -1,4 +1,5 @@ import {browser} from "./browser"; +import {getContexts} from "./runtime"; import {callWithPromise} from "./utils"; import type {FirefoxSidebarAction, OperaSidebarAction, SidebarAction} from "./types"; @@ -9,6 +10,7 @@ type OpenOptions = chrome.sidePanel.OpenOptions; type CloseOptions = chrome.sidePanel.CloseOptions; type PanelOptions = chrome.sidePanel.PanelOptions; type PanelBehavior = chrome.sidePanel.PanelBehavior; +type ContextFilter = chrome.runtime.ContextFilter; type IconDetails = opr.sidebarAction.IconDetails; // Available in Firefox and Opera @@ -141,8 +143,16 @@ export const setSidebarBehavior = (behavior?: PanelBehavior): Promise => sp.setPanelBehavior(behavior || {}, cb); }); -export const isOpenSidebar = (windowId?: number): Promise => - callWithPromise(async cb => { +export const isOpenSidebar = async (windowId?: number): Promise => { + if (sidePanel()) { + const filter: ContextFilter = {contextTypes: ["SIDE_PANEL"]}; + + if (windowId) filter.windowIds = [windowId]; + + return (await getContexts(filter)).length !== 0; + } + + return callWithPromise(async cb => { const sb = sidebarAction() as FirefoxSidebarAction | undefined; if (sb?.isOpen) { @@ -157,6 +167,7 @@ export const isOpenSidebar = (windowId?: number): Promise => throw new SidebarError("The sidebarAction.isOpen API is not supported in this browser"); }); +}; export const toggleSidebar = (): Promise => callWithPromise(async cb => { From 654275da8b31989213218305be3b8c4c8402e93d Mon Sep 17 00:00:00 2001 From: Anjey Tsibylskij <130153594+atldays@users.noreply.github.com> Date: Thu, 19 Feb 2026 14:34:00 +0200 Subject: [PATCH 6/6] docs(sidebar): update API details and improve documentation structure --- docs/sidebar.md | 94 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 22 deletions(-) diff --git a/docs/sidebar.md b/docs/sidebar.md index 6712134..397d7c2 100644 --- a/docs/sidebar.md +++ b/docs/sidebar.md @@ -6,16 +6,26 @@ Documentation: - [Opera Sidebar Action API](https://help.opera.com/en/extensions/sidebar-action-api/) - [Firefox Sidebar Action API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/sidebarAction) -A promise-based wrapper around Chrome's side panel (`chrome.sidePanel`, MV3) and Firefox/Opera `sidebarAction` APIs. Methods provide unified behavior to get/set options and behavior, open the panel, and, where supported, manage title and icon (Firefox & Opera) and badge (Opera). +A promise-based wrapper around Chromium's side panel (`chrome.sidePanel`, MV3) and Firefox/Opera `sidebarAction` APIs. Methods provide unified behavior to get/set options and behavior, open the panel, and, where supported, manage title and icon (Firefox & Opera) and badge (Opera). Support includes Chrome, Edge, Firefox, and Opera. + +## Classes + +### SidebarError + +Custom error class thrown when an API method is not supported or fails. ## Methods -- [getSidebarOptions(tabId?)](#getSidebarOptions) -- [getSidebarBehavior()](#getSidebarBehavior) +- [getSidebarOptions(tabId?)](#getSidebarOptions) [Chromium] +- [getSidebarBehavior()](#getSidebarBehavior) [Chromium] - [canOpenSidebar()](#canOpenSidebar) +- [canCloseSidebar()](#canCloseSidebar) - [openSidebar(options)](#openSidebar) -- [setSidebarOptions(options?)](#setSidebarOptions) -- [setSidebarBehavior(behavior?)](#setSidebarBehavior) +- [closeSidebar(options)](#closeSidebar) +- [setSidebarOptions(options?)](#setSidebarOptions) [Chromium] +- [setSidebarBehavior(behavior?)](#setSidebarBehavior) [Chromium] +- [isOpenSidebar(windowId?)](#isOpenSidebar) +- [toggleSidebar()](#toggleSidebar) [Firefox] - [setSidebarPath(path, tabId?)](#setSidebarPath) - [getSidebarPath(tabId?)](#getSidebarPath) - [setSidebarTitle(title, tabId?)](#setSidebarTitle) [Firefox, Opera] @@ -33,23 +43,23 @@ A promise-based wrapper around Chrome's side panel (`chrome.sidePanel`, MV3) and -### getSidebarOptions +### getSidebarOptions [Chromium] ``` getSidebarOptions(tabId?: number): Promise ``` -Retrieves the side panel options (e.g., `path`) for the specified tab. Throws if the Side Panel API isn't supported. [MV3] +Retrieves the side panel options (e.g., `path`) for the specified tab. Throws if the Side Panel API isn't supported (requires Chromium-based browsers like Chrome, Edge, or Opera MV3). [MV3] -### getSidebarBehavior +### getSidebarBehavior [Chromium] ``` getSidebarBehavior(): Promise ``` -Gets the current side panel behavior settings. Throws if unsupported. [MV3] +Gets the current side panel behavior settings. Throws if unsupported (requires Chromium-based browsers like Chrome, Edge, or Opera MV3). [MV3] @@ -59,7 +69,17 @@ Gets the current side panel behavior settings. Throws if unsupported. [MV3] canOpenSidebar(): boolean ``` -Returns `true` if `chrome.sidePanel` (MV3) is available, or if `browser.sidebarAction.open` is available (Firefox). +Returns `true` if `chrome.sidePanel` (Chromium MV3) is available, or if `sidebarAction.open` is available (Firefox/Opera). + + + +### canCloseSidebar + +``` +canCloseSidebar(): boolean +``` + +Returns `true` if `chrome.sidePanel` (Chromium MV3) is available, or if `sidebarAction.close` is available (Firefox/Opera). @@ -69,27 +89,37 @@ Returns `true` if `chrome.sidePanel` (MV3) is available, or if `browser.sidebarA openSidebar(options: chrome.sidePanel.OpenOptions): Promise ``` -Opens the side panel with the given options in Chrome (MV3). Falls back to `browser.sidebarAction.open()` in Firefox. Logs a warning and resolves as a no-op if unsupported. +Opens the side panel with the given options in Chromium-based browsers (MV3). Falls back to `sidebarAction.open()` in Firefox/Opera. Throws if unsupported. + + + +### closeSidebar + +``` +closeSidebar(options: chrome.sidePanel.CloseOptions): Promise +``` + +Closes the side panel with the given options in Chromium-based browsers (MV3). Falls back to `sidebarAction.close()` in Firefox/Opera. Throws if unsupported. -### setSidebarOptions +### setSidebarOptions [Chromium] ``` setSidebarOptions(options?: chrome.sidePanel.PanelOptions): Promise ``` -Sets side panel options (e.g., `path`) in Chrome (MV3). Logs a warning and resolves as a no-op if unsupported. [MV3] +Sets side panel options (e.g., `path`) in Chromium-based browsers (MV3). Throws if unsupported. [MV3] -### setSidebarBehavior +### setSidebarBehavior [Chromium] ``` setSidebarBehavior(behavior?: chrome.sidePanel.PanelBehavior): Promise ``` -Updates default panel behavior in Chrome (MV3). Logs a warning and resolves as a no-op if unsupported. [MV3] +Updates default panel behavior in Chromium-based browsers (MV3). Throws if unsupported. [MV3] @@ -99,7 +129,7 @@ Updates default panel behavior in Chrome (MV3). Logs a warning and resolves as a setSidebarPath(path: string, tabId?: number): Promise ``` -Sets the sidebar path in Chrome via `setOptions` (MV3) or via `sidebarAction.setPanel()` in Firefox/Opera. Throws if unsupported. +Sets the sidebar path in Chromium-based browsers via `setOptions` (MV3) or via `sidebarAction.setPanel()` in Firefox/Opera. Throws if unsupported. @@ -109,7 +139,27 @@ Sets the sidebar path in Chrome via `setOptions` (MV3) or via `sidebarAction.set getSidebarPath(tabId?: number): Promise ``` -Retrieves the sidebar path from Chrome (MV3) or parses from `sidebarAction.getPanel()` in Firefox/Opera. Throws if unsupported. +Retrieves the sidebar path from Chromium-based browsers (MV3) or parses from `sidebarAction.getPanel()` in Firefox/Opera. Throws if unsupported. + + + +### isOpenSidebar + +``` +isOpenSidebar(windowId?: number): Promise +``` + +Checks if the sidebar is open for the given window in Chromium-based browsers (MV3) using `getContexts` and in Firefox/Opera using `sidebarAction.isOpen()`. Throws if unsupported. + + + +### toggleSidebar [Firefox] + +``` +toggleSidebar(): Promise +``` + +Toggles the sidebar in Firefox. Throws if unsupported. @@ -119,7 +169,7 @@ Retrieves the sidebar path from Chrome (MV3) or parses from `sidebarAction.getPa setSidebarTitle(title: string | number, tabId?: number): Promise ``` -Sets the sidebar title via `sidebarAction.setTitle()` (Firefox/Opera). Logs a warning if unsupported. +Sets the sidebar title via `sidebarAction.setTitle()` (Firefox/Opera). Throws if unsupported. @@ -129,7 +179,7 @@ Sets the sidebar title via `sidebarAction.setTitle()` (Firefox/Opera). Logs a wa setSidebarIcon(details: opr.sidebarAction.IconDetails): Promise ``` -Sets the sidebar icon via `sidebarAction.setIcon()` (Firefox/Opera). Logs a warning if unsupported. +Sets the sidebar icon via `sidebarAction.setIcon()` (Firefox/Opera). Throws if unsupported. > Known issue (Opera): The `opr.sidebarAction.setIcon` API is currently broken and may fail with "Access to extension API denied". > See: https://forums.opera.com/topic/75680/opr-sidebaraction-seticon-api-is-broken-access-to-extension-api-denied @@ -142,7 +192,7 @@ Sets the sidebar icon via `sidebarAction.setIcon()` (Firefox/Opera). Logs a warn setSidebarBadgeText(text: string | number, tabId?: number): Promise ``` -Sets the sidebar badge text via `opr.sidebarAction.setBadgeText()` (Opera only). Logs a warning if unsupported. +Sets the sidebar badge text via `opr.sidebarAction.setBadgeText()` (Opera only). Throws if unsupported. @@ -162,7 +212,7 @@ Clears the sidebar badge text (equivalent to setting an empty string) via `opr.s setSidebarBadgeTextColor(color: string | [number, number, number, number], tabId?: number): Promise ``` -Sets the sidebar badge text color via `opr.sidebarAction.setBadgeTextColor()` (Opera only). Logs a warning if unsupported. +Sets the sidebar badge text color via `opr.sidebarAction.setBadgeTextColor()` (Opera only). Throws if unsupported. @@ -172,7 +222,7 @@ Sets the sidebar badge text color via `opr.sidebarAction.setBadgeTextColor()` (O setSidebarBadgeBgColor(color: string | [number, number, number, number], tabId?: number): Promise ``` -Sets the sidebar badge background color via `opr.sidebarAction.setBadgeBackgroundColor()` (Opera only). Logs a warning if unsupported. +Sets the sidebar badge background color via `opr.sidebarAction.setBadgeBackgroundColor()` (Opera only). Throws if unsupported.