80

From a content script, is it possible to access that tab's id?

I want to send a message to the background page from the content script that tells my extension to "do something with this tab" using the chrome.tabs.* API.

A tabID is needed, and there is no point in doing a bunch of logic in the background page to hunt for a tabID when my content script can simply tell it the tabID in the message contents.

ted
  • 13,596
  • 9
  • 65
  • 107
void.pointer
  • 24,859
  • 31
  • 132
  • 243

6 Answers6

130

Tab id is automatically passed inside MessageSender object:

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    console.log("sent from tab.id=", sender.tab.id);
});

Note: According to docs, this property is not always available:

This property will only be present when the connection was opened from a tab (including content scripts), and only if the receiver is an extension, not an app.

Gangula
  • 5,193
  • 4
  • 30
  • 59
serg
  • 109,619
  • 77
  • 317
  • 330
  • Thanks... I completely overlooked the sender parameter. I thought I had to do this.tabId or something inside of my content script. – void.pointer Jun 01 '11 at 17:16
  • For posterity: unfortunately the tabIds reported in chrome.WebRequest response details objects are consistently -1 for requests initiated from background pages (as of the time of this comment). – MrChrisRodriguez Jun 30 '14 at 15:25
  • 25
    @serg: What if I need tabID outside message response processor? – c00000fd Oct 09 '14 at 04:24
  • I get following: Uncaught Error: sendRequest and onRequest are obsolete. Please use sendMessage and onMessage instead – Stepan Yakovenko May 19 '16 at 08:48
  • @c00000fd: See my answer below. – Aidin May 31 '19 at 22:38
  • 3
    Hmm... for me sender is my extension's page, not the activepage – avalanche1 Oct 26 '19 at 16:44
  • I was not able to get `sender.tab`. After reading the [docs](https://developer.chrome.com/docs/extensions/reference/runtime/#type-MessageSender) I realized that it was because I was sending the message from popup. – Gangula Aug 22 '22 at 16:28
41

If you want the tabId of yourself (in my case in Content Script) without requiring "tabs" permission, a workaround is to have Content Script send a dummy message to the background script, then have background script respond with sender.tab.id back to the Content Script!

e.g. in content.js:

chrome.runtime.sendMessage({ text: "what is my tab_id?" }, tabId => {
   console.log('My tabId is', tabId);
});

and in background.js:

chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
    if (msg.text == "what is my tab_id?") {
        sendResponse({tab: sender.tab.id});
     }
});

it's a stupid workaround that worked for me. :)

PS. Oh, if you have tabs permission then you can run this async query very easily:

chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
    var myTabId = tabs[0].id;
    chrome.tabs.sendMessage(myTabId, {text: "hi"}, function(response) {
        alert(response);
    });
});
Aidin
  • 25,146
  • 8
  • 76
  • 67
  • "sendResponse() will be removed from the W3C spec. The popular webextension-polyfill library has already removed the sendResponse() function", says [MDN](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage) – Kong Kao Sep 25 '21 at 09:43
  • @KongKao I haven't being able to use `sendResponse()` successfully in Firefox, but also can't find that text in your link where it says that `sendResponse()` will be removed – Madacol Apr 16 '22 at 21:24
  • 4
    chrome.tabs.query cannot be used from within a content script. It is only available in background and popup scripts. – momaji Jul 13 '22 at 15:29
13

If you're using Ports for two-way long-lived connections, the second argument in the callback is a Port object, so to access the tab ID is then:

chrome.runtime.onConnect.addListener(port => {
  if (port.name === "foo") {
    port.onMessage.addListener((msg, sendingPort) => {
      console.log("sent from tab.id=", sendingPort.sender.tab.id);
    });
  }
});
Lounge9
  • 1,213
  • 11
  • 22
3

Correction of the selected answer for nowadays:

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    console.log("sent from tab.id=", sender.id);
});
Psartek
  • 481
  • 3
  • 9
  • according to [docs](https://developer.chrome.com/docs/extensions/reference/runtime/#type-MessageSender), `sender.id` returns the id of the extension/app, not the id – Gangula Aug 22 '22 at 16:24
3

If a content script is injected programmatically, another approach is to store tabId in a global variable:

const injectTabId = (callback) => chrome.tabs.executeScript(
  tabId,
  {code: `window.tabId = ${tabId}`},
  callback
);

const injectFile = () => chrome.tabs.executeScript(
  tabId,
  {file: 'content.js'}
);

injectTabId(injectFile);

In the content script, access it with window.tabId. The variable won't be exposed to page scripts because of content scripts living in an isolated world.

Vitaly Kuznetsov
  • 1,515
  • 1
  • 16
  • 15
0

If you are using manifest_version: 3 and a content script is injected programmatically, another approach is to store tabId in a global variable:

chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
    await chrome.scripting.executeScript({
      target: { tabId: tabId },
      args: [tabId],
      func: (id) => {
        window.tabId = id;
      }
    });
    await chrome.scripting.executeScript({
      target: { tabId: tabId },
      files: ['content.js']
    });
});