9

Background

I have a Chrome extension with a browser action to launch index.html in a new tab.

I'd like to update the extension to open index.html in a popup first, and then include a button users can click to optionally open the app in a new tab.

I don't want this button to show when it's not a popup (since it wouldn't make sense), which means the content script needs to know whether it is a popup in order to show the button.

Questions

This is a two part question:

  1. How does a Chrome extension popup know it's a popup?
  2. How do I pass that information to a content script before the popup is rendered?

What I've tried

I've tried to use chrome.extension.getViews in background.js to firstly determine if a popup is open. Then, I send a message to the content script which then shows the button. However I haven't gotten it to work - views is always an empty array, and the message doesn't seem to ever be received by the content script.

Here are the relevant parts of my manifest.json file:

"background": {
  "scripts": ["background.js"]
},

"browser_action": {
  "default_icon": {   
    "19": "img/icon19.png",
    "38": "img/icon38.png"
  },

  "default_title": "Super Simple Tasks",

  "default_popup": "index.html"
}

And here's what I've been trying in my background.js:

// Get all popups
var views = chrome.extension.getViews({ type: "popup" });

// Send a message if there is a popup
if (views.length > 0){
  chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
    chrome.tabs.sendMessage(tabs[0].id, {action: "popup_open"}, function(response) {});  
  });
};

And then in my content script, I listen for the message and then add a class to the body:

// Listen for the message
chrome.extension.onMessage.addListener(function(msg, sender, sendResponse) {
  if (msg.action === 'popup_open') {
    // My code here to show the button
  }
});
Benjamin Humphrey
  • 3,770
  • 2
  • 23
  • 41
  • hi, is the index.html a popup window now for your code? I think it should be a popup instead of a new tab. And if you want to just not show the button in the new tab page, you can just add a url parameter like "?ispopup=false" in the link you open new tab when click the button. – Surely May 02 '15 at 03:18
  • Ah thanks, a friend of mine said the same thing - the URL parameter approach works well. I've added it as an answer! – Benjamin Humphrey May 02 '15 at 03:56
  • Simply include it as a url parameter to the popup – Zig Mandel May 02 '15 at 13:09

4 Answers4

10

After talking with a friend I discovered an elegant solution that doesn't involve messaging or even a background.js script at all.

I can specify ?popup=true in manifest.json and check for that parameter in my extension's content script. Here's the code:

manifest.json now looks like this:

"browser_action": {
  "default_icon": {   
    "19": "img/icon19.png",
    "38": "img/icon38.png"
  },

  "default_title": "Super Simple Tasks",

  "default_popup": "index.html?popup=true"
}

The following code in my content script (taken from this answer) checks for ?popup=true. Worth noting that this function can handle multiple URL parameters split by the & character.

function getUrlParameter(sParam) {
  var sPageURL = window.location.search.substring(1);
  var sURLVariables = sPageURL.split('&');
  for (var i = 0; i < sURLVariables.length; i++) {
    var sParameterName = sURLVariables[i].split('=');
    if (sParameterName[0] == sParam) {
      return sParameterName[1];
    }
  }
}

var isPopup;
isPopup = getUrlParameter('popup') === 'true';

Finally, add a class to the body if it's a popup:

$('body').toggleClass('popup', isPopup)
Community
  • 1
  • 1
Benjamin Humphrey
  • 3,770
  • 2
  • 23
  • 41
7

In the manifest file add a hash to the url:

"browser_action": {
  "default_popup": "index.html#popup"
}

In JavaScript:

if (location.hash === '#popup') 
    // do something awesome!
kano
  • 5,626
  • 3
  • 33
  • 48
Thoran
  • 8,884
  • 7
  • 41
  • 50
5

I needed something similar as i wanted to create some cross-compatible code for all script types.

I found that this worked quite well.

const SCRIPT_TYPE = (() => {
    if (chrome && chrome.extension && chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() === window) {
        return 'BACKGROUND';
    } else if (chrome && chrome.extension && chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() !== window) {
        return 'POPUP';
    } else if (!chrome || !chrome.runtime || !chrome.runtime.onMessage) {
        return 'WEB';
    } else {
        return 'CONTENT';
    }
})();
Chad Cache
  • 9,668
  • 3
  • 56
  • 48
  • 1
    Unfortunately this can't tell the difference between the popup and an extension page like moz-extension://hash – Bufke Jun 13 '18 at 23:25
  • This is absolutely great. Thanks for sharing this. One thing though - Isn't WEB same same as CONTENT_SCRIPT ( or what is web here ? ) and also what about OPTIONS page ? – bhavya_w Jul 09 '19 at 06:19
  • 1
    @bhavya_w this would be if you were also injecting the script into the actual WEBPAGE using a script tag or eval. I am not sure about OPTIONS. – Chad Cache Jul 09 '19 at 06:56
2
chrome.tabs.getCurrent(function(tab) {
    if(tab == undefined)
        document.getElementById('mButton').style.display = 'inline-block';
});

I initially set the button's display: none; if the returned tab is undefined, means it's not a tab (so it is popup) and then I display button. You can reverse it of course.

======

Well the sending parameter also works, which in that case you won't need to add the query string in the manifest, just adding it in button's click listener would suffice.

btn.addEventListener('click', function() {
    chrome.tabs.create({url: "index.html?popup=false"});
});

And then the same process (reading the query string and comparing, etc).

======

Alternatively you can make a copy of index.html say index2.html, remove the button from index.html, use index2.html in the manifest and index.html for button click. :)

Samurai
  • 3,724
  • 5
  • 27
  • 39