Issue with Side Panel Not Receiving `chrome.storage.sync.onChanged` Event When Not Visible

187 views
Skip to first unread message

John Gordon

unread,
Oct 2, 2024, 11:39:25 AMOct 2
to Chromium Extensions

Hi everyone,

I'm experiencing an issue with my Chrome extension's side panel and was hoping someone might be able to help.

Problem Description:

  • Event Listener Setup:

    chrome.storage.sync.onChanged.addListener(storageChangedHandler);
  • Expected Behavior: The storageChangedHandler function should execute when changes occur in chrome.storage.sync, causing the side panel to close.

  • Actual Behavior:

    • When the side panel is visible on the screen: The onChanged event is received, and the side panel closes as expected.
    • When the side panel is not visible:
      • The onChanged event is not received, so the side panel doesn't close.
      • However, if the developer tools are open for the side panel:
        • The side panel will close even if it's not on the screen.
        • The side panel's DevTools window also closes when this happens.

What I've Tried:

  • Verified that the event listener is correctly set up.
  • Confirmed that changes are indeed happening in chrome.storage.sync.
  • Checked for any errors in the console—none found.
  • Observed that opening the developer tools affects the behavior.

Questions:

  1. Is it expected behavior that the side panel doesn't receive events when it's not visible?
  2. Why does having the developer tools open for the side panel cause it to receive events and close even when not visible?
  3. Is there a way to ensure the side panel receives the onChanged event even when it's not displayed?
  4. Are there any workarounds or best practices to handle this scenario?

Any insights or suggestions would be greatly appreciated!

Thank you in advance for your help!

John Gordon

unread,
Oct 2, 2024, 11:43:04 AMOct 2
to Chromium Extensions, John Gordon

I wanted to add some additional details to my previous message:


Trigger for the Event:

The chrome.storage.sync.onChanged event is fired when the user changes a setting for the extension.

Event Listener Context:

The event listener is running within the side panel code, which is built as a React application.

woxxom

unread,
Oct 2, 2024, 3:34:08 PMOct 2
to Chromium Extensions, John Gordon
I guess React, being a visual framework, uses requestAnimationFrame() internally, which is fired only when the document is visible, so React never calls your hooks until then. The solution would be to call chrome.storage.sync.onChanged.addListener outside of React, e.g. at right the beginning of the script.

John Gordon

unread,
Oct 3, 2024, 12:33:51 PMOct 3
to Chromium Extensions, woxxom, John Gordon

I wanted to provide an update on the issue I mentioned earlier regarding the side panel not receiving the chrome.storage.sync.onChanged event when it’s not visible.

New Observation:

  • Putting the listener for storage change anywhere in the Sidepanel -- in a script tag outside of React, or anywhere -- didn't change the behaviour. The sidepnanel still doesn't close
  • I attempted to programmatically close the side panel by calling chrome.sidePanel.setOptions({ path: "", tabId }), where tabId is the ID of the tab where the side panel is open.
  • Unfortunately, executing this code causes Chrome to crash completely.

What I Tried:

  • Setting the Side Panel for Specific Tabs:
  • When opening the side panel, I was specifying the tabId to target specific tabs:
chrome.sidePanel.setOptions( { path: "path-to-html-file", tabId: tab.id!, }, openSidePanelFunction, );
  • Attempting to Close the Side Panels:
const closeSidePanels = () => {
  chrome.tabs.query({}, (tabs) => {
    tabs.forEach((tab) => {
      chrome.sidePanel.setOptions({ path: "", tabId: tab.id }, () => { //<-- this crashes chrome when setting on an open side panel
        if (chrome.runtime.lastError) {
          console.error(`Error closing side panel for tab ${tab.id}: ${chrome.runtime.lastError.message}`);
        }
      });
    });
  });

Result:

  • When I execute this code, Chrome crashes entirely.

Additional Details from Our Investigation:

  • Possible Suspension of Side Panel Scripts:
    • Initially, we thought that the side panel’s code might be suspended when it’s not visible, affecting event handling.
    • However, we couldn’t find explicit documentation confirming this behavior.
  • Alternative Approaches:
  • Developer Tools Observation:
    • Notably, when the developer tools are open for the side panel, the side panel closes as expected, even if it’s not visible on the screen.
    • This might indicate that having the developer tools open keeps the side panel’s JavaScript context active.

Questions:

  1. Is there a known issue with using chrome.sidePanel.setOptions({ path: "", tabId }) to close side panels in specific tabs when the side panel is already open?
  2. Is there an alternative method to programmatically close the side panel in specific tabs without causing Chrome to crash outside of the two ways I've tried: setting sidePanel options or having a listener in the side panel code call window.close()?
  3. Could this be a bug in the Chrome browser or the Extensions API that needs to be reported?
  4. Has anyone else experienced similar issues when trying to close side panels programmatically?

Roberto Oneto

unread,
Oct 5, 2024, 2:37:26 PMOct 5
to Chromium Extensions, John Gordon, woxxom

chrome.storage.sysnc.onChanged does not exist
I think you meant chrome.storage.onChanged

John Gordon

unread,
Oct 18, 2024, 10:13:30 AMOct 18
to Chromium Extensions, Roberto Oneto, John Gordon, woxxom
in my testing, chrome.storage.sync or opening a long-running port and sending a message from the service worker to the side panel doesn't work unless the side panel is visible. My workaround is to add a listener to the DOM document's "visibilitychange" event. This does close the side panel,  but the user will see the side panel close when they revisit a tab with an open side panel. Here's the React custom hook code:

import { useEffect } from "react";
import { getExtensionMode } from "../storage";
import { ExtensionMode } from "../domain";
import { closeWindow } from "./functions/closeWindow.ts";

/**
 * When the visibility of the side panel changes, it checks
 * the extension mode in the extension config, closing the side
 * panel if the setting isn't equal to the provided panelMode
 *
 * @param panelMode
 */
export const useClosePanelMonitor = (panelMode: ExtensionMode) => {
  useEffect(() => {
    const handler = async () => {
      try {
        const result = await getExtensionMode();
        if (result !== panelMode) {
          closeWindow();
        }
      } catch (e) {
        console.log("error reading extension mode", e);
      }
    };

    document.addEventListener("visibilitychange", handler);

    return () => {
      document.removeEventListener("visibilitychange", handler);
    };
  }, [panelMode]);
};

Reply all
Reply to author
Forward
0 new messages