Bug 631403 - LoginManagerStorage_mozStorage should be able to deal with an existing transaction
authorShawn Wilsher <me@shawnwilsher.com>
Tue, 08 Feb 2011 20:46:38 -0800
changeset 62255 7df11fdfc215f949a753af3f2a0b4efe9cd3bdae
parent 62192 39a631a71d18da05227af7639f74e0112fa3dd44
child 62260 8af41fd791c1968442ba08fe244306e7a2de5457
push idunknown
push userunknown
push dateunknown
bugs631403
milestone2.0b12pre
Bug 631403 - LoginManagerStorage_mozStorage should be able to deal with an existing transaction r=dolske a=blocking-fennec
toolkit/components/passwordmgr/src/storage-mozStorage.js
--- a/toolkit/components/passwordmgr/src/storage-mozStorage.js
+++ b/toolkit/components/passwordmgr/src/storage-mozStorage.js
@@ -1,8 +1,10 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=4 et lcs=trail\:.,tab\:>~ : */
 /* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
  *
@@ -45,29 +47,60 @@ const Cr = Components.results;
 const DB_VERSION = 4; // The database schema version
 
 const ENCTYPE_BASE64 = 0;
 const ENCTYPE_SDR = 1;
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
+/**
+ * Object that manages a database transaction properly so consumers don't have
+ * to worry about it throwing.
+ *
+ * @param aDatabase
+ *        The mozIStorageConnection to start a transaction on.
+ */
+function Transaction(aDatabase) {
+    this._db = aDatabase;
+
+    this._hasTransaction = false;
+    try {
+        this._db.beginTransaction();
+        this._hasTransaction = true;
+    }
+    catch(e) { /* om nom nom exceptions */ }
+}
+
+Transaction.prototype = {
+    commit : function() {
+        if (this._hasTransaction)
+            this._db.commitTransaction();
+    },
+
+    rollback : function() {
+        if (this._hasTransaction)
+            this._db.rollbackTransaction();
+    },
+};
+
+
 function LoginManagerStorage_mozStorage() { };
 
 LoginManagerStorage_mozStorage.prototype = {
 
     classID : Components.ID("{8c2023b9-175c-477e-9761-44ae7b549756}"),
     QueryInterface : XPCOMUtils.generateQI([Ci.nsILoginManagerStorage,
                                             Ci.nsIInterfaceRequestor]),
     getInterface : function(aIID) {
-      if (aIID.equals(Ci.mozIStorageConnection)) {
-        return this._dbConnection;
-      }
+        if (aIID.equals(Ci.mozIStorageConnection)) {
+            return this._dbConnection;
+        }
 
-      throw Cr.NS_ERROR_NO_INTERFACE;
+        throw Cr.NS_ERROR_NO_INTERFACE;
     },
 
     __crypto : null,  // nsILoginManagerCrypto service
     get _crypto() {
         if (!this.__crypto)
             this.__crypto = Cc["@mozilla.org/login-manager/crypto/SDR;1"].
                             getService(Ci.nsILoginManagerCrypto);
         return this.__crypto;
@@ -1021,28 +1054,28 @@ LoginManagerStorage_mozStorage.prototype
                 legacy.initWithFile(importFile, null);
             else
                 legacy.init();
 
             // Import logins and disabledHosts
             let logins = legacy.getAllEncryptedLogins();
 
             // Wrap in a transaction for better performance.
-            this._dbConnection.beginTransaction();
+            let transaction = new Transaction(this._dbConnection);
             for each (let login in logins) {
                 try {
                     this._addLogin(login, true);
                 } catch (e) {
                     this.log("_importLegacySignons failed to add login: " + e);
                 }
             }
             let disabledHosts = legacy.getAllDisabledHosts();
             for each (let hostname in disabledHosts)
                 this.setLoginSavingEnabled(hostname, false);
-            this._dbConnection.commitTransaction();
+            transaction.commit();
         } catch (e) {
             this.log("_importLegacySignons failed: " + e.name + " : " + e.message);
             throw "Import failed";
         }
     },
 
 
     /*
@@ -1136,30 +1169,32 @@ LoginManagerStorage_mozStorage.prototype
      * decrypted some login (this helps ensure the user doesn't get mysterious
      * prompts for a master password, when set).
      */
     _reencryptBase64Logins : function () {
         this._base64checked = true;
         // Ignore failures, will try again next session...
 
         this.log("Reencrypting Base64 logins");
-        this._dbConnection.beginTransaction();
+        let transaction;
         try {
             let [logins, ids] = this._searchLogins({ encType: ENCTYPE_BASE64 });
 
             if (!logins.length)
                 return;
 
             try {
                 logins = this._decryptLogins(logins);
             } catch (e) {
                 // User might have canceled master password entry, just ignore.
                 return;
             }
 
+            transaction = new Transaction(this._dbConnection);
+
             let encUsername, encPassword, stmt;
             for each (let login in logins) {
                 [encUsername, encPassword] = this._encryptLogin(login);
 
                 let query =
                     "UPDATE moz_logins " +
                     "SET encryptedUsername = :encryptedUsername, " +
                         "encryptedPassword = :encryptedPassword, " +
@@ -1179,17 +1214,19 @@ LoginManagerStorage_mozStorage.prototype
                     this.log("_reencryptBase64Logins caught error: " + e);
                 } finally {
                     stmt.reset();
                 }
             }
         } catch (e) {
             this.log("_reencryptBase64Logins failed: " + e);
         } finally {
-            this._dbConnection.commitTransaction();
+            if (transaction) {
+                transaction.commit();
+            }
         }
     },
 
 
     //**************************************************************************//
     // Database Creation & Access
 
     /*
@@ -1296,32 +1333,32 @@ LoginManagerStorage_mozStorage.prototype
             // runs the newer code again, it will see the lower version number
             // and re-upgrade (to fixup any entries the old code added).
             this._dbConnection.schemaVersion = DB_VERSION;
             return;
         }
 
         // Upgrade to newer version...
 
-        this._dbConnection.beginTransaction();
+        let transaction = new Transaction(this._dbConnection);
 
         try {
             for (let v = oldVersion + 1; v <= DB_VERSION; v++) {
                 this.log("Upgrading to version " + v + "...");
                 let migrateFunction = "_dbMigrateToVersion" + v;
                 this[migrateFunction]();
             }
         } catch (e) {
             this.log("Migration failed: "  + e);
-            this._dbConnection.rollbackTransaction();
+            transaction.rollback();
             throw e;
         }
 
         this._dbConnection.schemaVersion = DB_VERSION;
-        this._dbConnection.commitTransaction();
+        transaction.commit();
         this.log("DB migration completed.");
     },
 
 
     /*
      * _dbMigrateToVersion2
      *
      * Version 2 adds a GUID column. Existing logins are assigned a random GUID.