Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Re-inject content scripts after update

I have a chrome extension which injects an iframe into every open tab. I have a chrome.runtime.onInstalled listener in my background.js which manually injects the required scripts as follows (Details of the API here : http://developer.chrome.com/extensions/runtime.html#event-onInstalled ) :

background.js

  var injectIframeInAllTabs = function(){
    console.log("reinject content scripts into all tabs");
    var manifest = chrome.app.getDetails();
    chrome.windows.getAll({},function(windows){
      for( var win in windows ){
        chrome.tabs.getAllInWindow(win.id, function reloadTabs(tabs) {
          for (var i in tabs) {
            var scripts = manifest.content_scripts[0].js;
            console.log("content scripts ", scripts);
            var k = 0, s = scripts.length;
            for( ; k < s; k++ ) {
              chrome.tabs.executeScript(tabs[i].id, {
                file: scripts[k]
              });
            }

          }
        });
      }
    });
  };

This works fine when I first install the extension. I want to do the same when my extension is updated. If I run the same script on update as well, I do not see a new iframe injected. Not only that, if I try to send a message to my content script AFTER the update, none of the messages go through to the content script. I have seen other people also running into the same issue on SO (Chrome: message content-script on runtime.onInstalled). What is the correct way of removing old content scripts and injecting new ones after chrome extension update?

like image 220
Trunal Bhanse Avatar asked Aug 28 '13 18:08

Trunal Bhanse


2 Answers

When the extension is updated Chrome automatically cuts off all the "old" content scripts from talking to the background page and they also throw an exception if the old content script does try to communicate with the runtime. This was the missing piece for me. All I did was, in chrome.runtime.onInstalled in bg.js, I call the same method as posted in the question. That injects another iframe that talks to the correct runtime. At some point in time, the old content scripts tries to talk to the runtime which fails. I catch that exception and just wipeout the old content script. Also note that, each iframe gets injected into its own "isolated world" (Isolated world is explained here: http://www.youtube.com/watch?v=laLudeUmXHM) hence newly injected iframe cannot clear out the old lingering iframe.

Hope this helps someone in future!

like image 77
Trunal Bhanse Avatar answered Nov 01 '22 00:11

Trunal Bhanse


There is no way to "remove" old content scripts (Apart from reloading the page in question using window.location.reload, which would be bad)

If you want to be more flexible about what code you execute in your content script, use the "code" parameter in the executeScript function, that lets you pass in a raw string with javascript code. If your content script is just one big function (i.e. content_script_function) which lives in background.js

in background.js:

function content_script_function(relevant_background_script_info) {
   // this function will be serialized as a string using .toString()
   // and will be called in the context of the content script page
   // do your content script stuff here...
}

function execute_script_in_content_page(info) {
  chrome.tabs.executeScript(tabid,
    {code: "(" + content_script_function.toString() + ")(" +
     JSON.stringify(info) + ");"});
}

chrome.tabs.onUpdated.addListener(
  execute_script_in_content_page.bind( { reason: 'onUpdated',
                                         otherinfo: chrome.app.getDetails() });

chrome.runtime.onInstalled.addListener(
  execute_script_in_content_page.bind( { reason: 'onInstalled',
                                         otherinfo: chrome.app.getDetails() });
)

Where relevant_background_script_info contains information about the background page, i.e. which version it is, whether there was an upgrade event, and why the function is being called. The content script page still maintains all its relevant state. This way you have full control over how to handle an "upgrade" event.

like image 23
kzahel Avatar answered Oct 31 '22 23:10

kzahel