Bug 1406181 - Add openForPrincipal static method and changes to IDB cursor wrapping in IndexedDB.jsm. draft
authorLuca Greco <lgreco@mozilla.com>
Sat, 14 Apr 2018 15:10:29 +0200
changeset 804736 1dba24e1d58c9daa7a97338b7a166914e9f3fb98
parent 804735 cb4e8439928dc0d63fe0e1a5c69a231f8657513b
child 804737 8dd8cd8de805aaa65367d14ce498aab46b1026ba
push id112456
push userluca.greco@alcacoop.it
push dateWed, 06 Jun 2018 14:37:14 +0000
bugs1406181
milestone62.0a1
Bug 1406181 - Add openForPrincipal static method and changes to IDB cursor wrapping in IndexedDB.jsm. MozReview-Commit-ID: vicgBmHhJp
toolkit/modules/IndexedDB.jsm
--- a/toolkit/modules/IndexedDB.jsm
+++ b/toolkit/modules/IndexedDB.jsm
@@ -118,49 +118,79 @@ function forwardMethods(cls, target, met
   for (let method of methods) {
     cls.prototype[method] = function(...args) {
       return this[target][method](...args);
     };
   }
 }
 
 class Cursor {
-  constructor(cursor, source) {
-    this.cursor = cursor;
+  constructor(cursorRequest, source) {
+    this.cursorRequest = cursorRequest;
     this.source = source;
+    this.cursor = null;
+  }
+
+  get done() {
+    return !this.cursor;
+  }
+
+  // This method is used internally to wait the cursor's IDBRequest to have been
+  // completed and the internal cursor has been updated (used when we initially
+  // create the cursor from Cursed.openCursor/openKeyCursor, and in the method
+  // of this class defined by defineCursorUpdateMethods).
+  async awaitRequest() {
+    this.cursor = await wrapRequest(this.cursorRequest);
+    return this;
   }
 }
 
+/**
+ * Define the Cursor class methods that update the cursor (continue, continuePrimaryKey
+ * and advance) as async functions that call the related IDBCursor methods and
+ * await the cursor's IDBRequest to be completed.
+ *
+ * @param {function} cls
+ *        The class constructor for which to define the cursor update methods.
+ * @param {Array<string>} methods
+ *        A list of "cursor update" method names to define.
+ */
+function defineCursorUpdateMethods(cls, methods) {
+  for (let method of methods) {
+    cls.prototype[method] = async function(...args) {
+      const promise = this.awaitRequest();
+      this.cursor[method](...args);
+      await promise;
+    };
+  }
+}
+
+defineCursorUpdateMethods(Cursor, ["advance", "continue", "continuePrimaryKey"]);
+
 forwardGetters(Cursor, "cursor",
                ["direction", "key", "primaryKey"]);
-
 wrapMethods(Cursor, "cursor", ["delete", "update"]);
 
-forwardMethods(Cursor, "cursor",
-               ["advance", "continue", "continuePrimaryKey"]);
-
 class CursorWithValue extends Cursor {}
 
 forwardGetters(CursorWithValue, "cursor", ["value"]);
 
 class Cursed {
   constructor(cursed) {
     this.cursed = cursed;
   }
 
   openCursor(...args) {
-    return wrapRequest(this.cursed.openCursor(...args)).then(cursor => {
-      return new CursorWithValue(cursor, this);
-    });
+    const cursor = new CursorWithValue(this.cursed.openCursor(...args), this);
+    return cursor.awaitRequest();
   }
 
   openKeyCursor(...args) {
-    return wrapRequest(this.cursed.openKeyCursor(...args)).then(cursor => {
-      return new Cursor(cursor, this);
-    });
+    const cursor = new Cursor(this.cursed.openKeyCursor(...args), this);
+    return cursor.awaitRequest();
   }
 }
 
 wrapMethods(Cursed, "cursed",
             ["count", "get", "getAll", "getAllKeys", "getKey"]);
 
 class Index extends Cursed {
   constructor(index, objectStore) {
@@ -247,27 +277,57 @@ class IndexedDB {
    *        first parameter when the database needs to be created, or its
    *        schema needs to be upgraded. If this function is not provided, the
    *        {@link #onupgradeneeded} method will be called instead.
    *
    * @returns {Promise<IndexedDB>}
    */
   static open(dbName, options, onupgradeneeded = null) {
     let request = indexedDB.open(dbName, options);
+    return this._wrapOpenRequest(request, onupgradeneeded);
+  }
 
+  /**
+   * Opens the database for a given principal and with the given name, returns
+   * a Promise which resolves to an IndexedDB instance when the operation completes.
+   *
+   * @param {nsIPrincipal} principal
+   *        The principal to open the database for.
+   * @param {string} dbName
+   *        The name of the database to open.
+   * @param {object} options
+   *        The options with which to open the database.
+   * @param {integer} options.version
+   *        The schema version with which the database needs to be opened. If
+   *        the database does not exist, or its current schema version does
+   *        not match, the `onupgradeneeded` function will be called.
+   * @param {function} [onupgradeneeded]
+   *        A function which will be called with an IndexedDB object as its
+   *        first parameter when the database needs to be created, or its
+   *        schema needs to be upgraded. If this function is not provided, the
+   *        {@link #onupgradeneeded} method will be called instead.
+   *
+   * @returns {Promise<IndexedDB>}
+   */
+  static openForPrincipal(principal, dbName, options, onupgradeneeded = null) {
+    const request = indexedDB.openForPrincipal(principal, dbName, options);
+    return this._wrapOpenRequest(request, onupgradeneeded);
+  }
+
+  static _wrapOpenRequest(request, onupgradeneeded = null) {
     request.onupgradeneeded = event => {
       let db = new this(request.result);
       if (onupgradeneeded) {
         onupgradeneeded(db, event);
       } else {
         db.onupgradeneeded(event);
       }
     };
 
-    return wrapRequest(request).then(db => new IndexedDB(db));
+    return wrapRequest(request).then(db => new this(db));
   }
 
   constructor(db) {
     this.db = db;
   }
 
   onupgradeneeded() {}