Avoid dynamically injecting the same script multiple times when using chrome.tabs.executeScript(…)

前端 未结 2 531
既然无缘
既然无缘 2020-12-10 18:18

I\'m building a Google Chrome extension. The basic setup is I have a Browser action button that injects jQuery and another bit of JavaScript into the active tab when it is

相关标签:
2条回答
  • 2020-12-10 18:41

    I believe the better way might be to do it on extension level. If you include third party libraries, then you would be re-including them as well, and never know what happens then.

    contentScript.js

    (function () {
    
      var Sz = Sizzle;
    
      //listen for events from the extension
      chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    
        if (!request._extension || request._extension !== 'my-extension-name') {
          console.log('Non extension request');
          return;
        }
    
        var channel = (request._channel) ? request._channel : false;
    
        if (channel === 'is-loaded') {
    
          sendResponse({isLoaded: true});
          return true;
    
        } else if (channel === 'example') {
          //do stuff 
        }
    
        return true;
    
      });
    
      //other code goes here
    
    });
    

    Background.js

    var util = {};
    
    util.sendMessage = function (data, cb) {
    
      chrome.tabs.query({active: true, currentWindow: true}, tabsQuery_cb);
    
      data['_extension'] = 'my-extension-name';
    
      function tabsQuery_cb(tabs) {
    
        var tab = tabs[0];
    
        console.log('sending message to ' + tab.id);
        console.log(data);
    
        //let the content script know to open the ad page
        chrome.tabs.sendMessage(tabs[0].id, data, cb);
    
      }
    
    }
    
    util.loadContentScript = function(cb) {
    
      var attempts = 0;
    
    
      checkIfLoaded();
    
       //checks if ContentScript is loaded by sending a message to the current
      function checkIfLoaded() {
    
        util.sendMessage({ _channel: 'is-loaded'}, sendMessage_cb);
    
      }
    
      function sendMessage_cb(response) {
    
        console.log('isLoadedCheck')
        console.log(response);
    
        if (response && response.isLoaded === true) {
          console.log('Script already loaded!');
          cb(true);
        } else {
          console.log('loading scripts');
          loadScripts();
        }
    
      }
    
      function loadScripts() {
    
        attempts++;
        console.log('loadScripts, attempt no. ' + attempts);
    
        if (attempts > 1) {
          console.log('Script couldnt be loaded');
          return cb(false);
        }
    
        var scripts = [
          "assets/sizzle-2.3.4.min.js",
          "app/contentScript.js"
        ];
    
        var i = -1;
    
        scriptLoaded();
    
        function scriptLoaded() {
    
          i++;
    
          if (i > scripts.length) {
            //all scripts have loaded at this point, check if replies with message
            checkIfLoaded();
          } else {
            console.log('Loading script ' + scripts[i]);
            chrome.tabs.executeScript(null, { file: scripts[i] }, scriptLoaded);
          }
    
    
        }
    
      }
    
    }
    

    Of course including third party libraries seems like a bad practice anyway, because it can skew with website's scripts. But what other way is there. Perhaps you could go to the extreme, and create a bootstrap content script which would check for presence of libraries and tell the extension script what exactly needs to be included. That's what I would consider on a more serious project.

    0 讨论(0)
  • 2020-12-10 18:47

    It is absolutely a legitimate concern.

    The easy way would be to use a mechanism similar to #ifndef include guards in C.

    Define a flag that gets set once the content script gets executed, and check if it's defined before doing anything. All scripts injected from the same extension share the JS execution context and therefore global scope.

    Your content script might look like this:

    if (window.contentScriptInjected !== true) {
        window.contentScriptInjected = true; // global scope
    
        /* Do your initialization and work */
    }
    
    /* global function definitions */
    

    The check should use an explicit true value to avoid false positives on pages that happen to have an element with id attribute that accidentally equals the variable name - browsers create an implicit global variable that points to that DOM element so for an if check it'll be truthy.

    0 讨论(0)
提交回复
热议问题