8

I want to execute a content script function whenever a tab is updated. The problem is that, sometimes the tab update is ajax (without a page reload), while still changing the page's url. Therefore the old content script injected still exists on the page. The result is multiple instances of content script injected and running on the same page.

So, I'm looking for a mechanism to inject a content script, only if no same content script has been injected before. Any ideas?

Keven Wang
  • 1,208
  • 17
  • 28
  • So I'm curious if you were able to find a working solution apart from trying to send a message and wait for a response from the content script? – c00000fd Sep 15 '14 at 02:26
  • @c00000fd: revisiting this question, I do think Rob W's solution is simpler, in the sense that it doesn't rely on chrome message passing. – Keven Wang Sep 16 '14 at 05:14

2 Answers2

14

You can try sending a message to content script (Message Passing). If the content script successfully returns a response then it means that the content script is already there otherwise an empty response is returned, you can check for an empty response and inject the content script.

Background:

chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
    chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
        if (response) {
            console.log("Already there");
        }
        else {
            console.log("Not there, inject contentscript");
        }
    });
});

ContentScript:

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
        if (request.greeting == "hello")
            sendResponse({message: "hi"});
 });
Uzair Farooq
  • 2,402
  • 3
  • 24
  • 37
8

Implementing an include guard is extremely easy:

(function() {
    // Comparing to a literal value to prevent matching a DOM element with id="hasRun"
    // https://stackoverflow.com/q/3434278/do-dom-tree-elements-with-ids-become-global
    if (window.hasRun === true) return;
    window.hasRun = true;
    // Rest of code
})();

If you want to programatically inject a content script, consider using one of the webNavigation events (e.g. onCommitted) instead of chrome.tabs.onUpdated. Unlike the tabs events, the webNavigation events are also triggered for navigation within frames, and offer a way to declare an URL filter in advance.

wOxxOm
  • 65,848
  • 11
  • 132
  • 136
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • Good to know about WebNavigation! With this include guard, the code itself is still injected though. After many navigations, there might be a lot of content scripts injected into this page. Would that cause any performance problems? Is there a way to inject by checking no content script exists? – Keven Wang Aug 24 '13 at 23:05
  • 1
    All content scripts from the same extension run in the same context. Don't worry about performance. – Rob W Aug 24 '13 at 23:12
  • 1
    All content scripts from the same extension run in the same context... **until the extension is reloaded**. Be careful if you reload the extension - this will result in two extensions running. – vaughan Feb 26 '14 at 12:12
  • @RobW: Sorry to resurrect this old thread, but I'm not really following the proposed implementation. What is `window.hasRun`? And where is this code supposed to run from? – c00000fd Sep 15 '14 at 00:34
  • @c00000fd `hasRun` is an arbitrarily chosen global variable name and should wrap the content script that should execute only once. – Rob W Sep 15 '14 at 07:57