Chrome extension: How to remove orphaned script after chrom extension update

Since the orphaned content script can still receive DOM messages, send one from your new working content script to the ghosted content script via window, for example. Upon receiving the message you’ll unregister all listeners (and nullify any global variables) which will also make your old script eligible for automatic garbage collection.

content.js:

var orphanMessageId = chrome.runtime.id + 'orphanCheck';
window.dispatchEvent(new Event(orphanMessageId));
window.addEventListener(orphanMessageId, unregisterOrphan);

// register all listeners with named functions to preserve their object reference
chrome.runtime.onMessage.addListener(onMessage);
document.addEventListener('mousemove', onMouseMove);

// the popup script checks it to see if a usable instance of content script is running
window.running = true;

function unregisterOrphan() {
  if (chrome.runtime.id) {
    // someone tried to kick us out but we're not orphaned! 
    return;
  }
  window.removeEventListener(orphanMessageId, unregisterOrphan);
  document.removeEventListener('mousemove', onMouseMove);
  try {
    // 'try' is needed to avoid an exception being thrown in some cases 
    chrome.runtime.onMessage.removeListener(onMessage);
  } catch (e) {}
  return true;
});

function onMessage(msg, sender, sendResponse) {
  //...........
}

function onMouseMove(event) {
  // DOM events still fire in the orphaned content script after the extension
  // was disabled/removed and before it's re-enabled or re-installed
  if (unregisterOrphan()) { return }
  //...........
}

popup.js should ensure a content script is injected before sending a message:

async function sendMessage(data) {
  const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
  if (await ensureContentScript(tab.id)) {
    return await chrome.tabs.sendMessage(tab.id, data);
  }
}

async function ensureContentScript(tabId) {
  try {
    const [{result}] = await chrome.scripting.executeScript({
      target: {tabId},
      func: () => window.running === true,
    });
    if (!result) {
      await chrome.scripting.executeScript({
        target: {tabId},
        files: ['content.js'],
      });
    }
    return true;
  } catch (e) {}
}

Leave a Comment