Chrome Extension Content Script Re-injection After Upgrade Or Install


Answer :

There's a way to allow a content script heavy extension to continue functioning after an upgrade, and to make it work immediately upon installation.

Install

The install method is to simply iterate through all tabs in all windows, and inject some scripts programmatically into tabs with matching URLs.

Obviously, you have to do it in a background page or event page script declared in manifest.json:

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

background.js:

// Add a `manifest` property to the `chrome` object. chrome.manifest = chrome.app.getDetails();  var injectIntoTab = function (tab) {     // You could iterate through the content scripts here     var scripts = chrome.manifest.content_scripts[0].js;     var i = 0, s = scripts.length;     for( ; i < s; i++ ) {         chrome.tabs.executeScript(tab.id, {             file: scripts[i]         });     } }  // Get all windows chrome.windows.getAll({     populate: true }, function (windows) {     var i = 0, w = windows.length, currentWindow;     for( ; i < w; i++ ) {         currentWindow = windows[i];         var j = 0, t = currentWindow.tabs.length, currentTab;         for( ; j < t; j++ ) {             currentTab = currentWindow.tabs[j];             // Skip chrome:// and https:// pages             if( ! currentTab.url.match(/(chrome|https):\/\//gi) ) {                 injectIntoTab(currentTab);             }         }     } }); 

Upgrade

The upgrade method relies on the fact that the content scripts are left injected after an extension is disabled, uninstalled or upgraded.

When the port connection is made, an onDisconnect handler is added. This waits a second after the disconnect event, then attempts to reconnect. If it fails, another onDisconnect is fired so the process happens again, until a connection is made. It's not perfect, but it works.

The content script:

var port;  // Attempt to reconnect var reconnectToExtension = function () {     // Reset port     port = null;     // Attempt to reconnect after 1 second     setTimeout(connectToExtension, 1000 * 1); };  // Attempt to connect var connectToExtension = function () {      // Make the connection     port = chrome.runtime.connect({name: "my-port"});      // When extension is upgraded or disabled and renabled, the content scripts     // will still be injected, so we have to reconnect them.     // We listen for an onDisconnect event, and then wait for a second before     // trying to connect again. Becuase chrome.runtime.connect fires an onDisconnect     // event if it does not connect, an unsuccessful connection should trigger     // another attempt, 1 second later.     port.onDisconnect.addListener(reconnectToExtension);  };  // Connect for the first time connectToExtension(); 

The only way to force a content script to be injected without refreshing the page is via programatic injection.

You can get all tabs and inject code into them using the chrome tabs API. For example you can store a manifest version in local storage and every time check if the manifest version is old one (in background page), if so you can get all active tabs and inject your code programmatically, or any other solution that will make you sure that the extension is updated.

Get all tabs using:
chrome.tabs.query

and inject your code into all pages
chrome.tabs.executeScript(tabId, {file: "content_script.js"});


Try this in your background script. Many of the old methods have been deprecated now, so I have refactored the code. For my use I'm only installing single content_script file. If need you can iterate over chrome.runtime.getManifest().content_scripts array to get all .js files.

chrome.runtime.onInstalled.addListener(installScript);  function installScript(details){     // console.log('Installing content script in all tabs.');     let params = {         currentWindow: true     };     chrome.tabs.query(params, function gotTabs(tabs){         let contentjsFile = chrome.runtime.getManifest().content_scripts[0].js[0];         for (let index = 0; index < tabs.length; index++) {             chrome.tabs.executeScript(tabs[index].id, {                 file: contentjsFile             },             result => {                 const lastErr = chrome.runtime.lastError;                 if (lastErr) {                     console.error('tab: ' + tabs[index].id + ' lastError: ' + JSON.stringify(lastErr));                 }             })         }     });     } 

Comments

Popular posts from this blog

Are Regular VACUUM ANALYZE Still Recommended Under 9.1?

Can Feynman Diagrams Be Used To Represent Any Perturbation Theory?