Bug 1467720 - Fix "dead object" error raised from ext-storage.js on browser.storage.local API calls. draft
authorLuca Greco <lgreco@mozilla.com>
Fri, 08 Jun 2018 05:19:34 +0200
changeset 806352 7adc254ce99dc49ae0267580389e2b0596ce2141
parent 805204 199a085199815cc99daa658956a7c9436e1d436b
push id112875
push userluca.greco@alcacoop.it
push dateSat, 09 Jun 2018 13:36:15 +0000
bugs1467720
milestone62.0a1
Bug 1467720 - Fix "dead object" error raised from ext-storage.js on browser.storage.local API calls. When ExtensionStorageIDB.selectBackend is called from a child process, it calls the main process and cache the result as a promise, to be reused for the other contexts for the same extension that are running in the same process. The cached promise should not be tied to a particular extension context's cloneScope, otherwise accessing it will raise "can't access dead object" errors after that cloneScope has been destroyed. MozReview-Commit-ID: GJuul8sQmlF
toolkit/components/extensions/ExtensionStorageIDB.jsm
--- a/toolkit/components/extensions/ExtensionStorageIDB.jsm
+++ b/toolkit/components/extensions/ExtensionStorageIDB.jsm
@@ -359,32 +359,38 @@ this.ExtensionStorageIDB = {
    */
   selectBackend(context) {
     const {extension} = context;
 
     if (!this.selectedBackendPromises.has(extension)) {
       let promise;
 
       if (context.childManager) {
-        // Ask the parent process if the new backend is enabled for the
-        // running extension.
-        promise = context.childManager.callParentAsyncFunction(
-          "storage.local.IDBBackend.selectBackend", []
-        ).then(result => {
+        // Create a promise object that is not tied to the current extension context, because
+        // we are caching it for the entire life of the extension in the current process (and
+        // the promise returned by context.childManager.callParentAsyncFunction would become
+        // a dead object when the context.cloneScope has been destroyed).
+        promise = (async () => {
+          // Ask the parent process if the new backend is enabled for the
+          // running extension.
+          let result = await context.childManager.callParentAsyncFunction(
+            "storage.local.IDBBackend.selectBackend", []
+          );
+
           if (!result.backendEnabled) {
             return {backendEnabled: false};
           }
 
           return {
             ...result,
             // In the child process, we need to deserialize the storagePrincipal
             // from the StructuredCloneHolder used to send it across the processes.
             storagePrincipal: result.storagePrincipal.deserialize(this),
           };
-        });
+        })();
       } else {
         // If migrating to the IDB backend is not enabled by the preference, then we
         // don't need to migrate any data and the new backend is not enabled.
         if (!this.isBackendEnabled) {
           return Promise.resolve({backendEnabled: false});
         }
 
         // In the main process, lazily create a storagePrincipal isolated in a