Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly handle chrome extension updates from content scripts

In background page we're able to detect extension updates using chrome.runtime.onInstalled.addListener.

But after extension has been updated all content scripts can't connect to the background page. And we get an error: Error connecting to extension ....

It's possible to re-inject content scripts using chrome.tabs.executeScript... But what if we have a sensitive data that should be saved before an update and used after update? What could we do?

Also if we re-inject all content scripts we should properly tear down previous content scripts.

What is the proper way to handle extension updates from content scripts without losing the user data?

like image 232
ValeriiVasin Avatar asked Oct 21 '22 12:10

ValeriiVasin


2 Answers

Once Chrome extension update happens, the "orphaned" content script is cut off from the extension completely. The only way it can still communicate is through shared DOM. If you're talking about really sensitive data, this is not secure from the page. More on that later.

First off, you can delay an update. In your background script, add a handler for the chrome.runtime.onUpdateAvailable event. As long as the listener is there, you have a chance to do cleanup.

// Background script
chrome.runtime.onUpdateAvailable.addListener(function(details) {
  // Do your work, call the callback when done
  syncRemainingData(function() {
    chrome.runtime.reload();
  });
});

Second, suppose the worst happens and you are cut off. You can still communicate using DOM events:

// Content script
// Get ready for data
window.addEventListener("SendRemainingData", function(evt) {
  processData(evt.detail);
}, false);

// Request data
var event = new CustomEvent("RequestRemainingData");
window.dispatchEvent(event);

// Be ready to send data if asked later
window.addEventListener("RequestRemainingData", function(evt) {
  var event = new CustomEvent("SendRemainingData", {detail: data});
  window.dispatchEvent(event);
}, false);

However, this communication channel is potentially eavesdropped on by the host page. And, as said previously, that eavesdropping is not something you can bypass.

Yet, you can have some out-of-band pre-shared data. Suppose that you generate a random key on first install and keep it in chrome.storage - this is not accessible by web pages by any means. Of course, once orphaned you can't read it, but you can at the moment of injection.

var PSK;
chrome.storage.local.get("preSharedKey", function(data) {
  PSK = data.preSharedKey;

  // ...

  window.addEventListener("SendRemainingData", function(evt) {
    processData(decrypt(evt.detail, PSK));
  }, false);

  // ...

  window.addEventListener("RequestRemainingData", function(evt) {
    var event = new CustomEvent("SendRemainingData", {detail: encrypt(data, PSK)});
    window.dispatchEvent(event);
  }, false);
});

This is of course proof-of-concept code. I doubt that you will need more than an onUpdateAvailable listener.

like image 159
Xan Avatar answered Oct 23 '22 01:10

Xan


If you've established a communication through var port = chrome.runtime.connect(...) (as described on https://developer.chrome.com/extensions/messaging#connect), it should be possible to listen to the runtime.Port.onDisconnect event:

tport.onDisconnect.addListener(function(msg) {...})

There you can react and, e.g. apply some sort of memoization, let's say via localStorage. But in general, I would suggest to keep content scripts as tiny as possible and perform all the data manipulations in the background, letting content only to collect/pass data and render some state, if needed.

like image 22
Oleksii Markhovskyi Avatar answered Oct 23 '22 02:10

Oleksii Markhovskyi