Reputation: 23
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?
Upvotes: 2
Views: 458
Reputation: 77541
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:
ignoreResponse
will gobble up the messaging error;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).
Upvotes: 1