My team owns a Chrome extension, and the other team has a similar extension but sooner will be deprecated, so we will take over all their existing users. Here comes the question - is there a way to migrate users from their extension to ours seamless? I.e. is there a way to auto-upgrade to our extension from user end?
-
Not unless you want to maintain two separate versions. – Daniel Herr Dec 16 '15 at 21:25
1 Answers
There is no seamless way, as for obvious security reasons extensions can't install other extensions (even with "management"
permission), but here's a possible upgrade path.
I'll call the "old" extension A and assume its ID is "extension_a_id"
, and the "new" extension B and assume its ID is `extension_b_id".
Make sure that extensions can talk to each other. Unless you specifically define
"externally_connectable"
in the manifest, that should be the case by default; if you do, make sure that extension B's includes the"extension_a_id"
ID and vice versa.Update extension B to include code responding to requests from A in all following steps. Do not proceed until you're reasonably sure most of the install base has this version (maybe wait a week or so).
//// Extension B (background code) //// chrome.runtime.onMessageExternal.addListener( function(message, sender, sendResponse) { if(sender.id && sender.id == "extension_A_id") { /* ..processing code.. */ } } );
In extension A, add a check that extension B is installed. Do so by pinging it:
//// Extension A //// chrome.runtime.onStartup.addListener(function() { isNewInstalled(function(response) { if(response) { transferPreferences(response.versionB); // See step 5 } else { // Nag user to install, see step 4 } }); }); function isNewInstalled(callback) { chrome.runtime.sendMessage( "extension_B_id", {areYouThere: true}, passResponseOrFalse(callback) ); } function passResponseOrFalse(callback) { return function(response) { // It's important to evaluate chrome.runtime.lastError // to prevent uncatchable exception, see http://stackoverflow.com/q/28431505 if(chrome.runtime.lastError || !response) { callback(false); } else { callback(response); } } } //// Extension B (processing code) //// // Send version number just in case if(message.areYouThere) sendResponse({ versionB: chrome.runtime.getManifest().version });
If extension B is not installed in step 3, nag the user to install it: show a page that explains why it is needed to upgrade and link to the CWS listing. This step requires user input.
If the extension B is already installed in step 3, transfer user options over and self-uninstall:
//// Extension A //// function transferPreferences(versionB) { /* ..validate version of B.. */ var prefs = {}; /* ..fill prefs with data that needs to be transfered (JSON-encodable).. */ chrome.runtime.sendMessage( "extension_B_id", { versionA: chrome.runtime.getManifest().version, preferences: prefs }, passResponseOrFalse(goodbyeCruelWorld) ); } function goodbyeCruelWorld(response) { if(response.processed) { // Does not require management permission chrome.management.uninstallSelf(); } else { console.warn("It is not my time to die yet."); } } //// Extension B, processing code //// if(message.preferences) { /* ..validate message.versionA and process message.preferences.. */ sendResponse({processed: true}); }
When extension B is installed, message the (possibly installed) extension A that the transfer can start immediately:
//// Extension B, background code //// chrome.runtime.onInstalled.addListener(function(details) { /* ..maybe check details.reason and new version.. */ chrome.runtime.sendMessage( "extension_A_id", {iAmHere: true, versionB: chrome.runtime.getManifest().version}, ignoreResponse ); }); function ignoreResponse(response) { // Again, evaluate to avoid exceptions; // if needed, this is the place to check if A is still installed return chrome.runtime.lastError; } //// Extension A, background code //// chrome.runtime.onMessageExternal.addListener( function(message, sender, sendResponse) { if(sender.id && sender.id == "extension_B_id") { if(message.iAmHere) { sendResponse({ versionA: chrome.runtime.getManifest().version }); transferPreferences(message.versionB); } } } );
Publish an update to extension B with all of the above.
Result:
- Users that have B installed won't notice anything, since
ignoreResponse
will gobble up the messaging error; - Users that have both installed will initiate transfer as soon as B updates and it will quietly finish;
- Users that only have A will be nagged on every extension restart to install B instead, upon which the transfer will start automatically.
One last concern is not to clobber the preferences of B with ones from A; left as an exercise to the reader (and also depends on the implementation).

- 74,770
- 16
- 179
- 206
-
Please note: I have not actually tested the code above, it may contain silly mistakes and/or typos; edits to that effect are welcome. – Xan Dec 17 '15 at 14:01
-