From 7a9581c156a8d04493a12eeac0bd211e89ec737d Mon Sep 17 00:00:00 2001 From: James Hobin <hobinjk@mit.edu> Date: Sun, 13 Aug 2017 09:22:10 -0700 Subject: [PATCH] Asynchronously migrate extension data (#1385) * Asynchronously migrate extension data * Add storage migration warning; Default to unlimited storage * Bump version to 7.7.6-less-broken * Skip update if current value is longer * copying -> migrating for clarity * Bump version * Excise misleading comment --- Firefox/package.json | 2 +- Firefox/sync-data.js | 46 ++++++++++++++++++++++++------ Firefox/webextension/background.js | 46 ++++++++++++++++++++++-------- Firefox/webextension/manifest.json | 2 +- WebExtension/bridge.js | 12 +++++++- 5 files changed, 84 insertions(+), 24 deletions(-) diff --git a/Firefox/package.json b/Firefox/package.json index 7024664cf..f54908386 100644 --- a/Firefox/package.json +++ b/Firefox/package.json @@ -2,7 +2,7 @@ "title": "New XKit", "name": "new-xkit", "fullName": "New XKit", - "version": "7.7.6", + "version": "7.7.7", "description": "Fork of XKit, the Tumblr extension framework", "main": "./main.js", "author": "", diff --git a/Firefox/sync-data.js b/Firefox/sync-data.js index 0dc9884a8..1e43cef06 100644 --- a/Firefox/sync-data.js +++ b/Firefox/sync-data.js @@ -1,16 +1,44 @@ -/* globals require, exports */ +/* globals require, exports, Promise */ let prefs = require('sdk/preferences/service'); +let {setTimeout} = require('sdk/timers'); let prefRoot = 'extensions.xkit7.'; +function migratePreference(port, prefKey) { + let name = prefKey.substring(prefRoot.length); + let value = prefs.get(prefKey, null); + let msg = {}; + msg[name] = value; + // postMessage doesn't return a promise even though it could :( + port.postMessage(msg); + return new Promise(function(resolve, reject) { + setTimeout(function() { + if (port.error) { + reject(port.error); + } else { + resolve(); + } + }, 10); + }); +} + +function migratePreferences(port, existingKeys) { + if (existingKeys.length === 0) { + return Promise.resolve(); + } + let key = existingKeys.pop(); + return migratePreference(port, key).then(function() { + return migratePreferences(port, existingKeys); + }); +} + exports.setSyncLegacyDataPort = function(port) { - // Get all and broadcast to webext + // Get all and broadcast to webext one at a time let existingKeys = prefs.keys(prefRoot); - let xkitStorage = {}; - for (let properName of existingKeys) { - let name = properName.substring(prefRoot.length); - let value = prefs.get(properName, null); - xkitStorage[name] = value; - } - port.postMessage(xkitStorage); + + migratePreferences(port, existingKeys).then(() => { + return port.postMessage({isProperlyMigrated: true}); + }).catch(err => { + console.error('preferences not migrated', err); + }); }; diff --git a/Firefox/webextension/background.js b/Firefox/webextension/background.js index beffcc804..fe4b2eb37 100644 --- a/Firefox/webextension/background.js +++ b/Firefox/webextension/background.js @@ -1,21 +1,43 @@ -/* global browser */ +/* global browser, Promise */ // From the MDN WebExtensions hybrid addon example // Ask to the legacy part to dump the needed data and send it back // to the background page... -var port = browser.runtime.connect({name: "sync-legacy-addon-data"}); -port.onMessage.addListener((msg) => { - if (msg) { - browser.storage.local.get('isMigrated').then(function(item) { - if (item.isMigrated) { - console.warn('Skipping migration'); - return; +browser.storage.local.get('isProperlyMigrated').then(function(item) { + if (item.isProperlyMigrated) { + console.warn('Skipping migration', item); + return; + } + + var port = browser.runtime.connect({name: "sync-legacy-addon-data"}); + function onMessage(msg) { + if (!msg) { + return; + } + let setPromises = Object.keys(msg).map(function(key) { + return browser.storage.local.get({key: null}).then(function(msgItem) { + if (typeof(msgItem[key]) === 'string') { + if (msgItem[key].length > msg[key].length) { + // Do not update the storage + return Promise.resolve(); + } + } + // The corresponding code in XKit is GM_setValue(name, value) -> set({name: value}) + return browser.storage.local.set(msg); + }); + }); + Promise.all(setPromises).catch(function(err) { + let errorMessage = "XKit failed to migrate your preferences to the new storage system. The migration reported the following error messag: " + err + ". Please restart your browser to try again."; + if (typeof(XKit) === 'undefined') { + alert(errorMessage); + } else { + XKit.window.show("Storage migration in progress", errorMessage, "error", "<div class=\"xkit-button default\">OK</div>"); } - // The corresponding code in XKit is GM_setValue(name, value) -> set({name: value}) - msg.isMigrated = true; - console.log('Migrating pref storage', msg); - browser.storage.local.set(msg); + browser.storage.local.set({isProperlyMigrated: false}); + port.onMessage.removeListener(onMessage); }); } + + port.onMessage.addListener(onMessage); }); diff --git a/Firefox/webextension/manifest.json b/Firefox/webextension/manifest.json index 9574cff1a..8dc1e47f5 100644 --- a/Firefox/webextension/manifest.json +++ b/Firefox/webextension/manifest.json @@ -15,7 +15,7 @@ "manifest_version": 2, "name": "New XKit", "permissions": ["*://*/*", "*://*/", "storage", "http://*.tumblr.com/", "https://*.tumblr.com/" ], - "version": "7.7.6", + "version": "7.7.7", "web_accessible_resources": [ "manifest.json", "editor.js" ], "background": { "scripts": ["background.js"] diff --git a/WebExtension/bridge.js b/WebExtension/bridge.js index 9fbfb4b44..8d68c3a51 100644 --- a/WebExtension/bridge.js +++ b/WebExtension/bridge.js @@ -26,7 +26,7 @@ try { var storage = browser.storage.local; var storage_loaded = false; var framework_version = getVersion(); - var storage_used = -1; + var storage_used = 0; var storage_max = -1; init_bridge(); } catch (e) { @@ -90,6 +90,16 @@ function init_bridge() { }); return; } + if (typeof(chrome) === 'undefined') { + if (!items.isProperlyMigrated) { + XKit.window.show("Storage migration in progress", "XKit is still busy migrating your preferences from the old storage system to the new one. Please check back in a couple seconds by refreshing the page.", "warning", "<div class=\"xkit-button default\" id=\"xkit-bridge-refresh\">Refresh</div>"); + $("#xkit-bridge-refresh").click(function() { + window.location = window.location; + }); + return; + } + + } for (var key in items) { xkit_storage[key] = items[key]; }