Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Opening SidePanel with tabId Results in Global SidePanel #987

Closed
eboraks opened this issue Jul 21, 2023 · 16 comments
Closed

Opening SidePanel with tabId Results in Global SidePanel #987

eboraks opened this issue Jul 21, 2023 · 16 comments

Comments

@eboraks
Copy link

eboraks commented Jul 21, 2023

Chrome Version
Version 117.0.5901.0 (Official Build) canary (64-bit)

Use Case
This is about an extension that summarizes text from a webpage. When the user clicks the action button, the background script opens a side panel to display the summarized text. To accomplish this, I want the side panel to appear only after the action button is clicked and only for the tabs where the action button was clicked.

Describe the bug
When opening a side-panel and setting the option to use tabId, I expect that the sidePanel will be limited to the tab. But, that isn't the case.
According to the docs
When using sidePanel.open(), you must choose the context in which it should open. Use windowId to open a global side panel. Alternatively, set the tabId to open the side panel only on a specific tab.

background.js

  console.log(
    'background.js got message. Action Clicked {tab.id: ' + tab.id + '}'
  )
  // This will open a tab-specific side panel only on the current tab.
  chrome.sidePanel.open({ tabId: tab.id })
  chrome.sidePanel.setOptions({
    tabId: tab.id,
    path: 'sidepanel/sidepanel.html',
    enabled: true,
  })
})

manifest

"background": {
    "service_worker": "src/background.js"
  },
  "side_panel": {
    "default_path": "sidepanel/sidepanel.html"
  },
  "permissions": ["tabs", "sidePanel"],

To Reproduce

  1. Clone this repo [email protected]:eboraks/learning-chrome-extension.git
  2. Upload extension
  3. Open two tabs
  4. Click the brain action button
  5. Navigate between the tabs.

Expected behavior
Since the tab was open with tabId the expected behavior is for the side panel to close when moving between tabs.

@eboraks
Copy link
Author

eboraks commented Jul 21, 2023

I added the following code to see the relationship between tabId and sidePanel

chrome.tabs.onActivated.addListener(async ({ tabId }) => { const { path } = await chrome.sidePanel.getOptions({ tabId }) console.log('tabId: ' + tabId + ' path: ' + path) })

As I moved between tabs that console printed the following.

background.js:31 tabId: 1101905827 path: sidepanel/sidepanel.html
background.js:31 tabId: 1101905829 path: sidepanel/sidepanel.html
background.js:31 tabId: 1101905856 path: sidepanel/sidepanel.html

@zakariaelh
Copy link

hey there, were you able to resolve this? I'm having the same issue and it looks like the documentation says that this behavior is somehow expected.

The tab in which to open the side panel. If the corresponding tab has a tab-specific side panel, the panel will only be open for that tab. If there is not a tab-specific panel, the global panel will be open in the specified tab and any other tabs without a currently-open tab- specific panel. This will override any currently-active side panel (global or tab-specific) in the corresponding tab. At least one of this and windowId must be provided.

link

@AmySteam
Copy link
Contributor

@eboraks Have you tried removing the default side panel declaration in the manifest?

  "side_panel": {
    "default_path": "sidepanel/sidepanel.html"
  },

@zakariaelh
Copy link

This works, thank you @AmySteam!

@ehynds
Copy link

ehynds commented Nov 2, 2023

For other users who might land here, if you have code that looks similar to this:

await chrome.sidePanel.setOptions({
  tabId,
  path: 'panel.html',
  enabled: true,
});

await chrome.sidePanel.open({
  tabId,
});

And are receiving this error:

Uncaught (in promise) Error: No active side panel for tabId

Make sure you're calling setOptions before open. After making that change, you might start experiencing this error:

Error: `sidePanel.open()` may only be called in response to a user gesture

To fix, remove await from the setOptions call. Final working code (for me anyway):

chrome.sidePanel.setOptions({
  tabId,
  path: 'panel.html',
  enabled: true,
});

await chrome.sidePanel.open({
  tabId,
});

@bmz1
Copy link

bmz1 commented Jun 10, 2024

@ehynds Thanks, it's indeed working. However, the type definitions seem a bit off for open(), as it indicates that it's not a promise.

@belthaZornv
Copy link

belthaZornv commented Jun 10, 2024

chrome.sidePanel.setOptions({
tabId,
path: 'panel.html',
enabled: true,
});

await chrome.sidePanel.open({
tabId,
});

If I do that, this won't work:

  // Get the currently active tab
  const activeTabs = await chrome.tabs.query({ active: true, currentWindow: true });
  const activeTab = activeTabs[0];

  // Check if there is an active tab
  if (!activeTab) {
    console.error('No active tab found.');
    return;
  }

  // Disable side panel for all tabs
  const allTabs = await chrome.tabs.query({});
  for (const tab of allTabs) {
    await chrome.sidePanel.setOptions({
      tabId: tab.id,
      enabled: false
    });
  }

  // Enable side panel for the active tab
  await chrome.sidePanel.setOptions({
    tabId: activeTab.id,
    path: 'sidebar.html',
    enabled: true
  });
});

Still surprised how complicated it is to just set a sidepanel to stay active on the initial tab 🤦‍♂️

@belthaZornv
Copy link

Tried this too:

chrome.action.onClicked.addListener(async (tab) => {
  // Get the currently active tab
    const activeTabs = await chrome.tabs.query({ active: true, currentWindow: true });
    const activeTab = activeTabs[0];
    
    chrome.sidePanel.setOptions({
      activeTab.id,
      path: 'sidebar.html',
      enabled: true,
    });

    await chrome.sidePanel.open({
      activeTab.id,
    });
});

To no avail, @ehynds do you see anything wrong from what I'm trying to achieve? :D

@oliverdunk
Copy link
Member

@belthaZornv, you should be able to use tab.id (from the event listener arguments) rather than querying for the active tab explicitly :)

That will also allow you to remove the await and call open synchronously.

@belthaZornv
Copy link

@belthaZornv, you should be able to use tab.id (from the event listener arguments) rather than querying for the active tab explicitly :)

That will also allow you to remove the await and call open synchronously.

oops, what a blonde moment that is. hahaha! let me try with that!

@belthaZornv
Copy link

Cleaned up the code - still opening on all tabs though lol!

chrome.action.onClicked.addListener(async (tab) => {
    chrome.sidePanel.setOptions({
      tab.id,
      path: 'sidebar.html',
      enabled: true,
    });

    chrome.sidePanel.open({
      tab.id,
    });
});

@oliverdunk
Copy link
Member

still opening on all tabs though lol

Yeah, this seems to be the behavior today. I don't remember off the top of my head if that was intentional - I'll try to follow-up on #1179.

@belthaZornv
Copy link

Thanks @oliverdunk, appreciate it 🙏

@onslauth
Copy link

@belthaZornv

I have it working using this

chrome.action.onClicked.addListener( ( tab ) => {

  chrome.sidePanel.setOptions( { 
      tabId: tab.id,
      path: "sidepanel.html",
      enabled: true 
  } );
  chrome.sidePanel.open( { tabId: tab.id } );

} );

@Mohamed3nan
Copy link

Wow, I can't believe how long it took to figure this out! 😄
Thank you!

--

Here's what finally worked for me after spending the whole day on it :D

function openSidePanel(windowId, tabId, scope) {
    const sidePanelPath = 'sidepanel/sidepanel.htmll';
    const options = {
        path: sidePanelPath,
        enabled: true,
        ...(tabId !== undefined ? { tabId } : { windowId })
    };

    if (scope === 'tab-specific' && tabId !== undefined) {
        chrome.sidePanel.setOptions(options, () => {
            chrome.sidePanel.open({ tabId });
        });
    } else {
        chrome.sidePanel.setOptions({ path: sidePanelPath, enabled: true }, () => {
            chrome.sidePanel.open({ windowId });
        });
    }
}

and in the Manifest file remove this:

  "side_panel": {
    "default_path": "sidepanel/sidepanel.html"
  },

finally, if you made any edits to the scope you must reload the extension

@munr0
Copy link

munr0 commented Nov 16, 2024

@Mohamed3nan, this workaround worked for me! It seems like the issue comes down to the thing in the manifest.

unfortunately however using .setOptions before .open to emulate this manifest declaration takes too long when combined with my other code and now I'm running into this...

CC: #1179

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants