Bug 896193 - Adopt Promises in mozIAsyncLivemarks. r=mak. sr=gavin.
authorAsaf Romano <mano@mozilla.com>
Wed, 20 Nov 2013 12:00:02 +0200
changeset 165626 0c3303aa3d4040aad2d134042d31985fe2c1cd27
parent 165625 535d21847e396c8d5e6dcd49e6dbca38eab3c259
child 165627 50cfbf3d30de7789f44db29763e364c176b1dda1
push id4623
push userryanvm@gmail.com
push dateTue, 28 Jan 2014 21:48:39 +0000
treeherderfx-team@7e79536aca0a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak, gavin
bugs896193
milestone29.0a1
Bug 896193 - Adopt Promises in mozIAsyncLivemarks. r=mak. sr=gavin.
toolkit/components/places/mozIAsyncLivemarks.idl
toolkit/components/places/nsLivemarkService.js
toolkit/components/places/tests/unit/test_mozIAsyncLivemarks.js
--- a/toolkit/components/places/mozIAsyncLivemarks.idl
+++ b/toolkit/components/places/mozIAsyncLivemarks.idl
@@ -7,65 +7,67 @@
 interface nsIURI;
 
 interface mozILivemarkCallback;
 interface mozILivemarkInfo;
 interface mozILivemark;
 
 interface nsINavHistoryResultObserver;
 
-[scriptable, uuid(1dbf174c-696e-4d9b-af0f-350da50d2249)]
+[scriptable, uuid(5B48E5A2-F07A-4E64-A935-C722A3D60B65)]
 interface mozIAsyncLivemarks : nsISupports
 {
   /**
    * Creates a new livemark
    *
    * @param aLivemarkInfo
    *        mozILivemarkInfo object containing at least title, parentId,
    *        index and feedURI of the livemark to create.
    * @param [optional] aCallback
    *        Invoked when the creation process is done.  In case of failure will
    *        receive an error code.
-   *
+   * @return {Promise}
    * @throws NS_ERROR_INVALID_ARG if the supplied information is insufficient
    *         for the creation.
    */
-  void addLivemark(in jsval aLivemarkInfo,
-                   [optional]in mozILivemarkCallback aCallback);
+  jsval addLivemark(in jsval aLivemarkInfo,
+                    [optional] in mozILivemarkCallback aCallback);
 
   /**
    * Removes an existing livemark.
    *
    * @param aLivemarkInfo
    *        mozILivemarkInfo object containing either an id or a guid of the
    *        livemark to remove.
    * @param [optional] aCallback
    *        Invoked when the removal process is done.  In case of failure will
    *        receive an error code.
    *
+   * @return {Promise}
    * @throws NS_ERROR_INVALID_ARG if the id/guid is invalid.
    */
-  void removeLivemark(in jsval aLivemarkInfo,
-                      [optional]in mozILivemarkCallback aCallback);
+  jsval removeLivemark(in jsval aLivemarkInfo,
+                       [optional] in mozILivemarkCallback aCallback);
 
   /**
    * Gets an existing livemark.
    *
    * @param aLivemarkInfo
    *        mozILivemarkInfo object containing either an id or a guid of the
    *        livemark to retrieve.
-   * @param aCallback
+   * @param [optional] aCallback
    *        Invoked when the fetching process is done.  In case of failure will
    *        receive an error code.
    *
+   * @return {Promise}
    * @throws NS_ERROR_INVALID_ARG if the id/guid is invalid or an invalid
    *         callback is provided.
    */
-  void getLivemark(in jsval aLivemarkInfo,
-                   in mozILivemarkCallback aCallback);
+  jsval getLivemark(in jsval aLivemarkInfo,
+                    [optional] in mozILivemarkCallback aCallback);
 
   /**
    * Reloads all livemarks if they are expired or if forced to do so.
    *
    * @param [optional]aForceUpdate
    *        If set to true forces a reload even if contents are still valid.
    *
    * @note The update process is asynchronous, observers registered through
--- a/toolkit/components/places/nsLivemarkService.js
+++ b/toolkit/components/places/nsLivemarkService.js
@@ -11,16 +11,18 @@ const Cu = Components.utils;
 //// Modules
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+                                  "resource://gre/modules/Promise.jsm");
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Services
 
 XPCOMUtils.defineLazyServiceGetter(this, "secMan",
                                    "@mozilla.org/scriptsecuritymanager;1",
                                    "nsIScriptSecurityManager");
 XPCOMUtils.defineLazyGetter(this, "asyncHistory", function () {
@@ -44,17 +46,17 @@ const ONERROR_EXPIRE_TIME_MS = 300000; /
 
 ////////////////////////////////////////////////////////////////////////////////
 //// LivemarkService
 
 function LivemarkService()
 {
   // Cleanup on shutdown.
   Services.obs.addObserver(this, PlacesUtils.TOPIC_SHUTDOWN, true);
- 
+
   // Observe bookmarks and history, but don't init the services just for that.
   PlacesUtils.addLazyBookmarkObserver(this, true);
 
   // Asynchronously build the livemarks cache.
   this._ensureAsynchronousCache();
 }
 
 LivemarkService.prototype = {
@@ -97,17 +99,17 @@ LivemarkService.prototype = {
       handleResult: function LS_handleResult(aResults)
       {
         for (let row = aResults.getNextRow(); row; row = aResults.getNextRow()) {
           let id = row.getResultByName("id");
           let siteURL = row.getResultByName("siteURI");
           let guid = row.getResultByName("guid");
           livemarkSvc._livemarks[id] =
             new Livemark({ id: id,
-                           guid: guid,             
+                           guid: guid,
                            title: row.getResultByName("title"),
                            parentId: row.getResultByName("parent"),
                            index: row.getResultByName("position"),
                            lastModified: row.getResultByName("lastModified"),
                            feedURI: NetUtil.newURI(row.getResultByName("feedURI")),
                            siteURI: siteURL ? NetUtil.newURI(siteURL) : null,
             });
           livemarkSvc._guids[guid] = id;
@@ -208,17 +210,18 @@ LivemarkService.prototype = {
         (aLivemarkInfo.siteURI && !(aLivemarkInfo.siteURI instanceof Ci.nsIURI)) ||
         (aLivemarkInfo.guid && !/^[a-zA-Z0-9\-_]{12}$/.test(aLivemarkInfo.guid))) {
       throw Cr.NS_ERROR_INVALID_ARG;
     }
 
     // The addition is done synchronously due to the fact importExport service
     // and JSON backups require that.  The notification is async though.
     // Once bookmarks are async, this may be properly fixed.
-    let result = Cr.NS_OK;
+    let deferred = Promise.defer();
+    let addLivemarkEx = null;
     let livemark = null;
     try {
       // Disallow adding a livemark inside another livemark.
       if (aLivemarkInfo.parentId in this._livemarks) {
         throw new Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
       }
 
       // Don't pass unexpected input data to the livemark constructor.
@@ -241,28 +244,43 @@ LivemarkService.prototype = {
       }
 
       // Updating the cache even if it has not yet been populated doesn't
       // matter since it will just be overwritten.
       this._livemarks[livemark.id] = livemark;
       this._guids[aLivemarkInfo.guid] = livemark.id;
     }
     catch (ex) {
-      result = ex.result;
+      addLivemarkEx = ex;
       livemark = null;
     }
     finally {
-      if (aLivemarkCallback) {
-        this._onCacheReady(function LS_addLivemark_ETAT() {
-          try {
-            aLivemarkCallback.onCompletion(result, livemark);
-          } catch(ex2) {}
-        }, true);
-      }
+      this._onCacheReady( () => {
+        if (addLivemarkEx) {
+          if (aLivemarkCallback) {
+            try {
+              aLivemarkCallback.onCompletion(addLivemarkEx.result, livemark);
+            }
+            catch(ex2) { }
+          }
+          deferred.reject(addLivemarkEx);
+        }
+        else {
+          if (aLivemarkCallback) {
+            try {
+              aLivemarkCallback.onCompletion(Cr.NS_OK, livemark);
+            }
+            catch(ex2) { }
+          }
+          deferred.resolve(livemark);
+        }
+      }, true);
     }
+
+    return deferred.promise;
   },
 
   removeLivemark: function LS_removeLivemark(aLivemarkInfo, aLivemarkCallback)
   {
     if (!aLivemarkInfo) {
       throw Cr.NS_ERROR_INVALID_ARG;
     }
 
@@ -273,36 +291,52 @@ LivemarkService.prototype = {
         !id) {
       throw Cr.NS_ERROR_INVALID_ARG;
     }
 
     // Convert the guid to an id.
     if (id in this._guids) {
       id = this._guids[id];
     }
-    let result = Cr.NS_OK;
+
+    let deferred = Promise.defer();
+    let removeLivemarkEx = null;
     try {
       if (!(id in this._livemarks)) {
         throw new Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
       }
       this._livemarks[id].remove();
     }
     catch (ex) {
-      result = ex.result;
+      removeLivemarkEx = ex;
     }
     finally {
-      if (aLivemarkCallback) {
-        // Enqueue the notification, per interface definition.
-        this._onCacheReady(function LS_removeLivemark_ETAT() {
-          try {
-            aLivemarkCallback.onCompletion(result, null);
-          } catch(ex2) {}
-        });
-      }
+      this._onCacheReady( () => {
+        if (removeLivemarkEx) {
+          if (aLivemarkCallback) {
+            try {
+              aLivemarkCallback.onCompletion(removeLivemarkEx.result, null);
+            }
+            catch(ex2) { }
+          }
+          deferred.reject(removeLivemarkEx);
+        }
+        else {
+          if (aLivemarkCallback) {
+            try {
+              aLivemarkCallback.onCompletion(Cr.NS_OK, null);
+            }
+            catch(ex2) { }
+          }
+          deferred.resolve();
+        }
+      });
     }
+
+    return deferred.promise;
   },
 
   _reloaded: [],
   _reloadNextLivemark: function LS__reloadNextLivemark()
   {
     this._reloading = false;
     // Find first livemark to be reloaded.
     for (let id in this._livemarks) {
@@ -319,56 +353,65 @@ LivemarkService.prototype = {
   {
     // Check if there's a currently running reload, to save some useless work.
     let notWorthRestarting =
       this._forceUpdate || // We're already forceUpdating.
       !aForceUpdate;       // The caller didn't request a forced update.
     if (this._reloading && notWorthRestarting) {
       // Ignore this call.
       return;
-    } 
+    }
 
-    this._onCacheReady((function LS_reloadAllLivemarks_ETAT() {
+    this._onCacheReady( () => {
       this._forceUpdate = !!aForceUpdate;
       this._reloaded = [];
       // Livemarks reloads happen on a timer, and are delayed for performance
       // reasons.
       this._startReloadTimer();
-    }).bind(this));
+    });
   },
 
   getLivemark: function LS_getLivemark(aLivemarkInfo, aLivemarkCallback)
   {
     if (!aLivemarkInfo) {
       throw Cr.NS_ERROR_INVALID_ARG;
     }
     // Accept either a guid or an id.
     let id = aLivemarkInfo.guid || aLivemarkInfo.id;
     if (("guid" in aLivemarkInfo && !/^[a-zA-Z0-9\-_]{12}$/.test(aLivemarkInfo.guid)) ||
         ("id" in aLivemarkInfo && aLivemarkInfo.id < 1) ||
         !id) {
       throw Cr.NS_ERROR_INVALID_ARG;
     }
 
-    this._onCacheReady((function LS_getLivemark_ETAT() {
+    let deferred = Promise.defer();
+    this._onCacheReady( () => {
       // Convert the guid to an id.
       if (id in this._guids) {
         id = this._guids[id];
       }
       if (id in this._livemarks) {
-        try {
-          aLivemarkCallback.onCompletion(Cr.NS_OK, this._livemarks[id]);
-        } catch (ex) {}
+        if (aLivemarkCallback) {
+          try {
+            aLivemarkCallback.onCompletion(Cr.NS_OK, this._livemarks[id]);
+          } catch (ex) {}
+        }
+        deferred.resolve(this._livemarks[id]);
       }
       else {
-        try {
-          aLivemarkCallback.onCompletion(Cr.NS_ERROR_INVALID_ARG, null);
-        } catch (ex) {}
+        if (aLivemarkCallback) {
+          try {
+            aLivemarkCallback.onCompletion(Cr.NS_ERROR_INVALID_ARG, null);
+          } catch (ex) { }
+        }
+        deferred.reject(Components.Exception("", Cr.NS_ERROR_INVALID_ARG));
       }
-    }).bind(this));
+    });
+
+    return deferred.promise;
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// nsINavBookmarkObserver
 
   onBeginUpdateBatch:  function () {},
   onEndUpdateBatch:    function () {},
   onItemVisited:       function () {},
--- a/toolkit/components/places/tests/unit/test_mozIAsyncLivemarks.js
+++ b/toolkit/components/places/tests/unit/test_mozIAsyncLivemarks.js
@@ -1,568 +1,633 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests functionality of the mozIAsyncLivemarks interface.
 
 const FEED_URI = NetUtil.newURI("http://feed.rss/");
 const SITE_URI = NetUtil.newURI("http://site.org/");
 
-function run_test()
-{
-  run_next_test();
-}
 
-add_test(function test_addLivemark_noArguments_throws()
+add_task(function test_addLivemark_noArguments_throws()
 {
   try {
-    PlacesUtils.livemarks.addLivemark();
+    yield PlacesUtils.livemarks.addLivemark();
     do_throw("Invoking addLivemark with no arguments should throw");
   } catch (ex) {
     // The error is actually generated by XPConnect.
     do_check_eq(ex.result, Cr.NS_ERROR_XPC_NOT_ENOUGH_ARGS);
   }
-  run_next_test();
 });
 
-add_test(function test_addLivemark_emptyObject_throws()
+add_task(function test_addLivemark_emptyObject_throws()
 {
   try {
-    PlacesUtils.livemarks.addLivemark({});
+    yield PlacesUtils.livemarks.addLivemark({});
     do_throw("Invoking addLivemark with empty object should throw");
   } catch (ex) {
     do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
-  run_next_test();
 });
 
-add_test(function test_addLivemark_badParentId_throws()
+add_task(function test_addLivemark_badParentId_throws()
 {
   try {
-    PlacesUtils.livemarks.addLivemark({ parentId: "test" });
+    yield PlacesUtils.livemarks.addLivemark({ parentId: "test" });
     do_throw("Invoking addLivemark with a bad parent id should throw");
   } catch (ex) {
     do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
-  run_next_test();
 });
 
-add_test(function test_addLivemark_invalidParentId_throws()
+add_task(function test_addLivemark_invalidParentId_throws()
 {
   try {
-    PlacesUtils.livemarks.addLivemark({ parentId: -2 });
+    yield PlacesUtils.livemarks.addLivemark({ parentId: -2 });
     do_throw("Invoking addLivemark with an invalid parent id should throw");
   } catch (ex) {
     do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
-  run_next_test();
 });
 
-add_test(function test_addLivemark_noIndex_throws()
+add_task(function test_addLivemark_noIndex_throws()
 {
   try {
-    PlacesUtils.livemarks.addLivemark({ parentId: PlacesUtils.unfiledBookmarksFolderId });
+    yield PlacesUtils.livemarks.addLivemark({
+      parentId: PlacesUtils.unfiledBookmarksFolderId });
     do_throw("Invoking addLivemark with no index should throw");
   } catch (ex) {
     do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
-  run_next_test();
 });
 
-add_test(function test_addLivemark_badIndex_throws()
+add_task(function test_addLivemark_badIndex_throws()
 {
   try {
-    PlacesUtils.livemarks.addLivemark({ parentId: PlacesUtils.unfiledBookmarksFolderId
-                                      , index: "test"
-                                      });
+    yield PlacesUtils.livemarks.addLivemark(
+      { parentId: PlacesUtils.unfiledBookmarksFolderId
+      , index: "test" });
     do_throw("Invoking addLivemark with a bad index should throw");
   } catch (ex) {
     do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
-  run_next_test();
 });
 
-add_test(function test_addLivemark_invalidIndex_throws()
+add_task(function test_addLivemark_invalidIndex_throws()
 {
   try {
-    PlacesUtils.livemarks.addLivemark({ parentId: PlacesUtils.unfiledBookmarksFolderId
-                                      , index: -2
-                                      });
+    yield PlacesUtils.livemarks.addLivemark(
+      { parentId: PlacesUtils.unfiledBookmarksFolderId
+      , index: -2
+      });
     do_throw("Invoking addLivemark with an invalid index should throw");
   } catch (ex) {
     do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
-  run_next_test();
 });
 
-add_test(function test_addLivemark_noFeedURI_throws()
+add_task(function test_addLivemark_noFeedURI_throws()
 {
   try {
-    PlacesUtils.livemarks.addLivemark({ parentId: PlacesUtils.unfiledBookmarksFolderId
-                                      , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                      });
+    yield PlacesUtils.livemarks.addLivemark(
+      { parentId: PlacesUtils.unfiledBookmarksFolderId
+      , index: PlacesUtils.bookmarks.DEFAULT_INDEX });
     do_throw("Invoking addLivemark with no feedURI should throw");
   } catch (ex) {
     do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
-  run_next_test();
 });
 
-add_test(function test_addLivemark_badFeedURI_throws()
+add_task(function test_addLivemark_badFeedURI_throws()
 {
   try {
-    PlacesUtils.livemarks.addLivemark({ parentId: PlacesUtils.unfiledBookmarksFolderId
-                                      , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                      , feedURI: "test"
-                                      });
+    yield PlacesUtils.livemarks.addLivemark(
+      { parentId: PlacesUtils.unfiledBookmarksFolderId
+      , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+      , feedURI: "test" });
     do_throw("Invoking addLivemark with a bad feedURI should throw");
   } catch (ex) {
     do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
-  run_next_test();
 });
 
-add_test(function test_addLivemark_badSiteURI_throws()
+add_task(function test_addLivemark_badSiteURI_throws()
 {
   try {
-    PlacesUtils.livemarks.addLivemark({ parentId: PlacesUtils.unfiledBookmarksFolderId
-                                      , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                      , feedURI: FEED_URI
-                                      , siteURI: "test"
-                                      });
+    yield PlacesUtils.livemarks.addLivemark(
+      { parentId: PlacesUtils.unfiledBookmarksFolderId
+      , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+      , feedURI: FEED_URI
+      , siteURI: "test" });
     do_throw("Invoking addLivemark with a bad siteURI should throw");
   } catch (ex) {
     do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
-  run_next_test();
 });
 
-add_test(function test_addLivemark_badGuid_throws()
+add_task(function test_addLivemark_badGuid_throws()
 {
   try {
-    PlacesUtils.livemarks.addLivemark({ parentId: PlacesUtils.unfiledBookmarksFolderId
-                                      , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                      , feedURI: FEED_URI
-                                      , guid: "123456"
-                                      });
+    yield PlacesUtils.livemarks.addLivemark(
+      { parentId: PlacesUtils.unfiledBookmarksFolderId
+      , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+      , feedURI: FEED_URI
+      , guid: "123456" });
     do_throw("Invoking addLivemark with a bad guid should throw");
   } catch (ex) {
     do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
-  run_next_test();
 });
 
-add_test(function test_addLivemark_badCallback_throws()
+add_task(function test_addLivemark_badCallback_throws()
 {
   try {
-    PlacesUtils.livemarks.addLivemark({ parentId: PlacesUtils.unfiledBookmarksFolderId
-                                      , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                      , feedURI: FEED_URI
-                                      }, "test");
+    yield PlacesUtils.livemarks.addLivemark(
+      { parentId: PlacesUtils.unfiledBookmarksFolderId
+      , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+      , feedURI: FEED_URI
+      }, "test");
     do_throw("Invoking addLivemark with a bad callback should throw");
   } catch (ex) {
     // The error is actually generated by XPConnect.
     do_check_eq(ex.result, Cr.NS_ERROR_XPC_BAD_CONVERT_JS);
   }
-  run_next_test();
 });
 
-add_test(function test_addLivemark_noCallback_succeeds()
+add_task(function test_addLivemark_noCallback_succeeds()
 {
+  let onItemAddedCalled = false;
   PlacesUtils.bookmarks.addObserver({
+    __proto__: NavBookmarkObserver.prototype,
     onItemAdded: function onItemAdded(aItemId, aParentId, aIndex, aItemType,
                                       aURI, aTitle)
     {
+      onItemAddedCalled = true;
       PlacesUtils.bookmarks.removeObserver(this);
       do_check_eq(aParentId, PlacesUtils.unfiledBookmarksFolderId);
       do_check_eq(aIndex, 0);
       do_check_eq(aItemType, Ci.nsINavBookmarksService.TYPE_FOLDER);
       do_check_eq(aTitle, "test");
-      run_next_test();
-    },
-    onBeginUpdateBatch: function onBeginUpdateBatch() {},
-    onEndUpdateBatch: function onEndUpdateBatch() {},
-    onItemRemoved: function onItemRemoved() {},
-    onItemChanged: function onItemChanged() {},
-    onItemVisited: function onItemVisited() {},
-    onItemMoved: function onItemMoved() {},
+    }
   }, false);
-  PlacesUtils.livemarks.addLivemark({ title: "test"
-                                    , parentId: PlacesUtils.unfiledBookmarksFolderId
-                                    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                    , feedURI: FEED_URI
-                                    });
+
+  yield PlacesUtils.livemarks.addLivemark(
+    { title: "test"
+    , parentId: PlacesUtils.unfiledBookmarksFolderId
+    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+    , feedURI: FEED_URI });
+  do_check_true(onItemAddedCalled);
 });
 
-add_test(function test_addLivemark_noSiteURI_callback_succeeds()
+
+add_task(function test_addLivemark_noSiteURI_callback_succeeds()
 {
-  PlacesUtils.livemarks.addLivemark({ title: "test"
-                                    , parentId: PlacesUtils.unfiledBookmarksFolderId
-                                    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                    , feedURI: FEED_URI
-                                    }, function (aStatus, aLivemark)
-  {
-    do_check_true(Components.isSuccessCode(aStatus));
+  let checkLivemark = aLivemark => {
     do_check_true(aLivemark.id > 0);
     do_check_valid_places_guid(aLivemark.guid);
     do_check_eq(aLivemark.title, "test");
     do_check_eq(aLivemark.parentId, PlacesUtils.unfiledBookmarksFolderId);
     do_check_eq(aLivemark.index, PlacesUtils.bookmarks.getItemIndex(aLivemark.id));
     do_check_eq(aLivemark.lastModified, PlacesUtils.bookmarks.getItemLastModified(aLivemark.id));
     do_check_true(aLivemark.feedURI.equals(FEED_URI));
     do_check_eq(aLivemark.siteURI, null);
-    run_next_test();
-  });
+  };
+
+  // The deprecated callback is called before resolving the promise.
+  let callbackCalled = false;
+  let livemark = yield PlacesUtils.livemarks.addLivemark(
+    { title: "test"
+    , parentId: PlacesUtils.unfiledBookmarksFolderId
+    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+    , feedURI: FEED_URI
+    },
+    (aStatus, aLivemark) => {
+      callbackCalled = true;
+      do_check_true(Components.isSuccessCode(aStatus));
+      checkLivemark(aLivemark);
+    } );
+  do_check_true(callbackCalled);
+  checkLivemark(livemark);
 });
 
-add_test(function test_addLivemark_callback_succeeds()
+add_task(function test_addLivemark_callback_succeeds()
 {
-  PlacesUtils.livemarks.addLivemark({ title: "test"
-                                    , parentId: PlacesUtils.unfiledBookmarksFolderId
-                                    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                    , feedURI: FEED_URI
-                                    , siteURI: SITE_URI
-                                    }, function (aStatus, aLivemark)
-  {
-    do_check_true(Components.isSuccessCode(aStatus));
+  let checkLivemark = aLivemark => {
     do_check_true(aLivemark.id > 0);
     do_check_valid_places_guid(aLivemark.guid);
     do_check_eq(aLivemark.title, "test");
     do_check_eq(aLivemark.parentId, PlacesUtils.unfiledBookmarksFolderId);
     do_check_eq(aLivemark.index, PlacesUtils.bookmarks.getItemIndex(aLivemark.id));
     do_check_eq(aLivemark.lastModified, PlacesUtils.bookmarks.getItemLastModified(aLivemark.id));
     do_check_true(aLivemark.feedURI.equals(FEED_URI));
     do_check_true(aLivemark.siteURI.equals(SITE_URI));
-
     do_check_true(PlacesUtils.annotations
                              .itemHasAnnotation(aLivemark.id,
                                                 PlacesUtils.LMANNO_FEEDURI));
     do_check_true(PlacesUtils.annotations
                              .itemHasAnnotation(aLivemark.id,
                                                 PlacesUtils.LMANNO_SITEURI));
-    run_next_test();
-  });
+  };
+
+  // The deprecated callback is called before resolving the promise.
+  let callbackCalled = false;
+  let livemark = yield PlacesUtils.livemarks.addLivemark(
+    { title: "test"
+    , parentId: PlacesUtils.unfiledBookmarksFolderId
+    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+    , feedURI: FEED_URI
+    , siteURI: SITE_URI
+    },
+    (aStatus, aLivemark) => {
+      callbackCalled = true;
+      do_check_true(Components.isSuccessCode(aStatus));
+      checkLivemark(aLivemark);
+    } );
+  do_check_true(callbackCalled);
+  checkLivemark(livemark);
 });
 
-add_test(function test_addLivemark_bogusid_callback_succeeds()
+add_task(function test_addLivemark_bogusid_callback_succeeds()
 {
-  PlacesUtils.livemarks.addLivemark({ id: 100 // Should be ignored.
-                                    , title: "test"
-                                    , parentId: PlacesUtils.unfiledBookmarksFolderId
-                                    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                    , feedURI: FEED_URI
-                                    , siteURI: SITE_URI
-                                    }, function (aStatus, aLivemark)
-  {
-    do_check_true(Components.isSuccessCode(aStatus));
+  let checkLivemark = aLivemark => {
     do_check_true(aLivemark.id > 0);
     do_check_neq(aLivemark.id, 100);
+  };
 
-    run_next_test();
-  });
+  // The deprecated callback is called before resolving the promise.
+  let callbackCalled = false;
+  let livemark = yield PlacesUtils.livemarks.addLivemark(
+    { id: 100 // Should be ignored.
+    , title: "test"
+    , parentId: PlacesUtils.unfiledBookmarksFolderId
+    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+    , feedURI: FEED_URI
+    , siteURI: SITE_URI
+    },
+    (aStatus, aLivemark) => {
+      callbackCalled = true;
+      do_check_true(Components.isSuccessCode(aStatus));
+      checkLivemark(aLivemark);
+    } );
+  do_check_true(callbackCalled);
+  checkLivemark(livemark);
 });
 
-add_test(function test_addLivemark_bogusParent_callback_fails()
+add_task(function test_addLivemark_bogusParent_callback_fails()
 {
-  PlacesUtils.livemarks.addLivemark({ title: "test"
-                                    , parentId: 187
-                                    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                    , feedURI: FEED_URI
-                                    }, function (aStatus, aLivemark)
-  {
-    do_check_false(Components.isSuccessCode(aStatus));
-    do_check_eq(aLivemark, null);
-    run_next_test();
-  });
+  // The deprecated callback is called before resolving the promise.
+  let callbackCalled = false;
+  try {
+    yield PlacesUtils.livemarks.addLivemark(
+      { title: "test"
+      , parentId: 187
+      , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+      , feedURI: FEED_URI
+      },
+      (aStatus, aLivemark) => {
+        callbackCalled = true;
+        do_check_false(Components.isSuccessCode(aStatus));
+        do_check_eq(aLivemark, null);
+      } );
+    do_throw("Adding a livemark with a bogus parent should fail");
+  }
+  catch(ex) {
+    do_check_true(callbackCalled);
+  }
 });
 
-add_test(function test_addLivemark_intoLivemark_callback_fails()
+add_task(function test_addLivemark_intoLivemark_callback_fails()
 {
-  PlacesUtils.livemarks.addLivemark({ title: "test"
-                                    , parentId: PlacesUtils.unfiledBookmarksFolderId
-                                    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                    , feedURI: FEED_URI
-                                    }, function (aStatus, aLivemark)
-  {
-    do_check_true(Components.isSuccessCode(aStatus));
-    
-    PlacesUtils.livemarks.addLivemark({ title: "test"
-                                      , parentId: aLivemark.id
-                                      , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                      , feedURI: FEED_URI
-                                      }, function (aStatus, aLivemark)
-    {
-      do_check_false(Components.isSuccessCode(aStatus));
-      do_check_eq(aLivemark, null);
-      run_next_test();
-    });
-  });
+  // The deprecated callback is called before resolving the promise.
+  let callbackCalled = false;
+  let livemark = yield PlacesUtils.livemarks.addLivemark(
+    { title: "test"
+    , parentId: PlacesUtils.unfiledBookmarksFolderId
+    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+    , feedURI: FEED_URI
+    },
+    (aStatus, aLivemark) => {
+      callbackCalled = true;
+      do_check_true(Components.isSuccessCode(aStatus));
+    } );
+  do_check_true(callbackCalled);
+  do_check_true(Boolean(livemark));
+
+  callbackCalled = false;
+  try {
+    yield PlacesUtils.livemarks.addLivemark(
+      { title: "test"
+      , parentId: livemark.id
+      , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+      , feedURI: FEED_URI
+      },
+      (aStatus, aLivemark) => {
+        callbackCalled = true;
+        do_check_false(Components.isSuccessCode(aStatus));
+        do_check_eq(aLivemark, null);
+      } );
+    do_throw("Adding a livemark into a livemark should fail");
+  }
+  catch(ex) {
+    do_check_true(callbackCalled);
+  }
 });
 
-add_test(function test_addLivemark_forceGuid_callback_succeeds()
+add_task(function test_addLivemark_forceGuid_callback_succeeds()
 {
-  PlacesUtils.livemarks.addLivemark({ title: "test"
-                                    , parentId: PlacesUtils.unfiledBookmarksFolderId
-                                    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                    , feedURI: FEED_URI
-                                    , guid: "1234567890AB"
-                                    }, function (aStatus, aLivemark)
-  {
-    do_check_true(Components.isSuccessCode(aStatus));
+  let checkLivemark = aLivemark => {
     do_check_eq(aLivemark.guid, "1234567890AB");
     do_check_guid_for_bookmark(aLivemark.id, "1234567890AB");
+  };
 
-    run_next_test();
-  });
+  // The deprecated callback is called before resolving the promise.
+  let callbackCalled = false;
+  let livemark = yield PlacesUtils.livemarks.addLivemark(
+    { title: "test"
+    , parentId: PlacesUtils.unfiledBookmarksFolderId
+    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+    , feedURI: FEED_URI
+    , guid: "1234567890AB"
+    },
+    (aStatus, aLivemark) => {
+      callbackCalled = true;
+      do_check_true(Components.isSuccessCode(aStatus));
+      checkLivemark(aLivemark);
+    } );
+  do_check_true(callbackCalled);
+  checkLivemark(livemark);
 });
 
-add_test(function test_addLivemark_lastModified_callback_succeeds()
+add_task(function test_addLivemark_lastModified_callback_succeeds()
 {
   let now = Date.now() * 1000;
-  PlacesUtils.livemarks.addLivemark({ title: "test"
-                                    , parentId: PlacesUtils.unfiledBookmarksFolderId
-                                    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                    , feedURI: FEED_URI
-                                    , lastModified: now
-                                    }, function (aStatus, aLivemark)
-  {
-    do_check_true(Components.isSuccessCode(aStatus));
-    do_check_eq(aLivemark.lastModified, now);
-
-    run_next_test();
-  });
+  let callbackCalled = false;
+  let livemark = yield PlacesUtils.livemarks.addLivemark(
+    { title: "test"
+    , parentId: PlacesUtils.unfiledBookmarksFolderId
+    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+    , feedURI: FEED_URI
+    , lastModified: now
+    },
+    (aStatus, aLivemark) => {
+      callbackCalled = true;
+      do_check_true(Components.isSuccessCode(aStatus));
+      do_check_eq(aLivemark.lastModified, now);
+    } );
+  do_check_true(callbackCalled);
+  do_check_eq(livemark.lastModified, now);
 });
 
-add_test(function test_removeLivemark_emptyObject_throws()
+add_task(function test_removeLivemark_emptyObject_throws()
 {
   try {
-    PlacesUtils.livemarks.removeLivemark({});
+    yield PlacesUtils.livemarks.removeLivemark({});
     do_throw("Invoking removeLivemark with empty object should throw");
   } catch (ex) {
     do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
-  run_next_test();
 });
 
-add_test(function test_removeLivemark_noValidId_throws()
+add_task(function test_removeLivemark_noValidId_throws()
 {
   try {
-    PlacesUtils.livemarks.removeLivemark({ id: -10, guid: "test"});
+    yield PlacesUtils.livemarks.removeLivemark({ id: -10, guid: "test"});
     do_throw("Invoking removeLivemark with no valid id should throw");
   } catch (ex) {
     do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
-  run_next_test();
 });
 
-add_test(function test_removeLivemark_nonExistent_fails()
+add_task(function test_removeLivemark_nonExistent_fails()
 {
-  PlacesUtils.livemarks.removeLivemark(
-    { id: 1337 },
-    function (aStatus, aLivemark) {
-      do_check_false(Components.isSuccessCode(aStatus));
-      do_check_eq(aLivemark, null);
-      run_next_test();
-    }
-  );
+  let callbackCalled = false;
+  try {
+    yield PlacesUtils.livemarks.removeLivemark(
+      { id: 1337 },
+      (aStatus, aLivemark) => {
+        callbackCalled = true;
+        do_check_false(Components.isSuccessCode(aStatus));
+        do_check_eq(aLivemark, null);
+      } );
+    do_throw("Removing a non-existent livemark should fail");
+  }
+  catch(ex) {
+    do_check_true(callbackCalled);
+  }
 });
 
-add_test(function test_removeLivemark_guid_succeeds()
+add_task(function test_removeLivemark_guid_succeeds()
 {
-  PlacesUtils.livemarks.addLivemark({ title: "test"
-                                    , parentId: PlacesUtils.unfiledBookmarksFolderId
-                                    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                    , feedURI: FEED_URI
-                                    , guid: "234567890ABC"
-                                    }, function (aStatus, aLivemark)
-  {
-    do_check_true(Components.isSuccessCode(aStatus));
-    do_check_eq(aLivemark.guid, "234567890ABC");
-    // invalid id to check the guid wins.
-    PlacesUtils.livemarks.removeLivemark(
-      { id: 789, guid: "234567890ABC" },
-      function (aStatus, aRemovedLivemark) {
-        do_check_true(Components.isSuccessCode(aStatus));
-        do_check_eq(PlacesUtils.bookmarks.getItemIndex(aLivemark.id), -1);
-        do_check_eq(aRemovedLivemark, null);
-        run_next_test();
-      }
-    );
+  let livemark = yield PlacesUtils.livemarks.addLivemark(
+    { title: "test"
+    , parentId: PlacesUtils.unfiledBookmarksFolderId
+    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+    , feedURI: FEED_URI
+    , guid: "234567890ABC"
   });
+
+
+  do_check_eq(livemark.guid, "234567890ABC");
+
+  yield PlacesUtils.livemarks.removeLivemark({
+    id: 789, guid: "234567890ABC"
+  });
+
+  do_check_eq(PlacesUtils.bookmarks.getItemIndex(livemark.id), -1);
 });
 
-add_test(function test_removeLivemark_id_succeeds()
+add_task(function test_removeLivemark_id_succeeds()
 {
-  PlacesUtils.livemarks.addLivemark({ title: "test"
-                                    , parentId: PlacesUtils.unfiledBookmarksFolderId
-                                    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                    , feedURI: FEED_URI
-                                    }, function (aStatus, aLivemark)
-  {
-    do_check_true(Components.isSuccessCode(aStatus));
-    PlacesUtils.livemarks.removeLivemark(
-      { id: aLivemark.id },
-      function (aStatus, aRemovedLivemark) {
-        do_check_true(Components.isSuccessCode(aStatus));
-        do_check_eq(PlacesUtils.bookmarks.getItemIndex(aLivemark.id), -1);
-        do_check_eq(aRemovedLivemark, null);
-        run_next_test();
-      }
-    );
+  let livemark = yield PlacesUtils.livemarks.addLivemark(
+    { title: "test"
+    , parentId: PlacesUtils.unfiledBookmarksFolderId
+    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+    , feedURI: FEED_URI
   });
+
+  yield PlacesUtils.livemarks.removeLivemark({ id: livemark.id });
+  do_check_eq(PlacesUtils.bookmarks.getItemIndex(livemark.id), -1);
 });
 
-add_test(function test_getLivemark_emptyObject_throws()
+add_task(function test_getLivemark_emptyObject_throws()
 {
   try {
-    PlacesUtils.livemarks.getLivemark({}, function () {});
+    yield PlacesUtils.livemarks.getLivemark({});
     do_throw("Invoking getLivemark with empty object should throw");
   } catch (ex) {
     do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
-  run_next_test();
 });
 
-add_test(function test_getLivemark_noValidId_throws()
+add_task(function test_getLivemark_noValidId_throws()
 {
   try {
-    PlacesUtils.livemarks.getLivemark({ id: -10, guid: "test"}, function () {});
+    yield PlacesUtils.livemarks.getLivemark({ id: -10, guid: "test"});
     do_throw("Invoking getLivemark with no valid id should throw");
   } catch (ex) {
     do_check_eq(ex.result, Cr.NS_ERROR_INVALID_ARG);
   }
-  run_next_test();
 });
 
-add_test(function test_getLivemark_nonExistentId_fails()
+add_task(function test_getLivemark_nonExistentId_fails()
 {
-  PlacesUtils.livemarks.getLivemark({ id: 1234 },
-    function (aStatus, aLivemark){
-      do_check_false(Components.isSuccessCode(aStatus));
-      do_check_eq(aLivemark, null);
-      run_next_test();
-    }
-  );
+  let callbackCalled = false;
+  try {
+    yield PlacesUtils.livemarks.getLivemark({ id: 1234 },
+      (aStatus, aLivemark) => {
+        callbackCalled = true;
+        do_check_false(Components.isSuccessCode(aStatus));
+        do_check_eq(aLivemark, null);
+      } );
+    do_throw("getLivemark for a non existent id should fail");
+  }
+  catch(ex) {
+    do_check_true(callbackCalled);
+  }
 });
 
-add_test(function test_getLivemark_nonExistentGUID_fails()
+add_task(function test_getLivemark_nonExistentGUID_fails()
 {
-  PlacesUtils.livemarks.getLivemark({ guid: "34567890ABCD" },
-    function (aStatus, aLivemark){
-      do_check_false(Components.isSuccessCode(aStatus));
-      do_check_eq(aLivemark, null);
-      run_next_test();
-    }
-  );
+  let callbackCalled = false;
+  try {
+    yield PlacesUtils.livemarks.getLivemark({ guid: "34567890ABCD" },
+      (aStatus, aLivemark) => {
+        callbackCalled = true;
+        do_check_false(Components.isSuccessCode(aStatus));
+        do_check_eq(aLivemark, null);
+      } );
+    do_throw("getLivemark for a non-existent guid should fail");
+  }
+  catch(ex) {
+    do_check_true(callbackCalled);
+  }
 });
 
-add_test(function test_getLivemark_guid_succeeds()
+add_task(function test_getLivemark_guid_succeeds()
 {
-  PlacesUtils.livemarks.addLivemark({ title: "test"
-                                    , parentId: PlacesUtils.unfiledBookmarksFolderId
-                                    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                    , feedURI: FEED_URI
-                                    , guid: "34567890ABCD"
-                                    }, function (aStatus, aLivemark)
-  {
-    do_check_true(Components.isSuccessCode(aStatus));
+  yield PlacesUtils.livemarks.addLivemark(
+    { title: "test"
+    , parentId: PlacesUtils.unfiledBookmarksFolderId
+    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+    , feedURI: FEED_URI
+    , guid: "34567890ABCD" });
 
-    // invalid id to check the guid wins.
-    PlacesUtils.livemarks.getLivemark({ id: 789, guid: "34567890ABCD" },
-      function(aStatus, aLivemark) {
+  let checkLivemark = aLivemark => {
+    do_check_eq(aLivemark.title, "test");
+    do_check_eq(aLivemark.parentId, PlacesUtils.unfiledBookmarksFolderId);
+    do_check_eq(aLivemark.index, PlacesUtils.bookmarks.getItemIndex(aLivemark.id));
+    do_check_true(aLivemark.feedURI.equals(FEED_URI));
+    do_check_eq(aLivemark.siteURI, null);
+    do_check_eq(aLivemark.guid, "34567890ABCD");
+  };
+
+  // invalid id to check the guid wins.
+  let livemark =
+    yield PlacesUtils.livemarks.getLivemark({ id: 789, guid: "34567890ABCD" },
+      (aStatus, aLivemark) => {
+        callbackCalled = true;
         do_check_true(Components.isSuccessCode(aStatus));
-        do_check_eq(aLivemark.title, "test");
-        do_check_eq(aLivemark.parentId, PlacesUtils.unfiledBookmarksFolderId);
-        do_check_eq(aLivemark.index, PlacesUtils.bookmarks.getItemIndex(aLivemark.id));
-        do_check_true(aLivemark.feedURI.equals(FEED_URI));
-        do_check_eq(aLivemark.siteURI, null);
-        do_check_eq(aLivemark.guid, "34567890ABCD");
-        run_next_test();
-      }
-    );
-  });
+        checkLivemark(aLivemark)
+      } );
+
+  do_check_true(callbackCalled);
+  checkLivemark(livemark);
 });
 
-add_test(function test_getLivemark_id_succeeds()
+add_task(function test_getLivemark_id_succeeds()
 {
-  PlacesUtils.livemarks.addLivemark({ title: "test"
-                                    , parentId: PlacesUtils.unfiledBookmarksFolderId
-                                    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                    , feedURI: FEED_URI
-                                    }, function (aStatus, aLivemark)
-  {
-    do_check_true(Components.isSuccessCode(aStatus));
+  let livemark = yield PlacesUtils.livemarks.addLivemark(
+    { title: "test"
+    , parentId: PlacesUtils.unfiledBookmarksFolderId
+    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+    , feedURI: FEED_URI
+    });
 
-    // invalid id to check the guid wins.
-    PlacesUtils.livemarks.getLivemark({ id: aLivemark.id },
-      function(aStatus, aLivemark) {
-        do_check_true(Components.isSuccessCode(aStatus));
-        do_check_eq(aLivemark.title, "test");
-        do_check_eq(aLivemark.parentId, PlacesUtils.unfiledBookmarksFolderId);
-        do_check_eq(aLivemark.index, PlacesUtils.bookmarks.getItemIndex(aLivemark.id));
-        do_check_true(aLivemark.feedURI.equals(FEED_URI));
-        do_check_eq(aLivemark.siteURI, null);
-        do_check_guid_for_bookmark(aLivemark.id, aLivemark.guid);
-        run_next_test();
-      }
-    );
-  });
+  let checkLivemark = aLivemark => {
+    do_check_eq(aLivemark.title, "test");
+    do_check_eq(aLivemark.parentId, PlacesUtils.unfiledBookmarksFolderId);
+    do_check_eq(aLivemark.index, PlacesUtils.bookmarks.getItemIndex(aLivemark.id));
+    do_check_true(aLivemark.feedURI.equals(FEED_URI));
+    do_check_eq(aLivemark.siteURI, null);
+    do_check_guid_for_bookmark(aLivemark.id, aLivemark.guid);
+  };
+
+  let callbackCalled = false;
+  livemark = yield PlacesUtils.livemarks.getLivemark(
+    { id: livemark.id },
+    (aStatus, aLivemark) => {
+      callbackCalled = true;
+      do_check_true(Components.isSuccessCode(aStatus));
+      checkLivemark(aLivemark);
+    } );
+
+  do_check_true(callbackCalled);
+  checkLivemark(livemark);
 });
 
-add_test(function test_getLivemark_removeItem_contention()
+add_task(function test_getLivemark_removeItem_contention()
 {
   PlacesUtils.livemarks.addLivemark({ title: "test"
                                     , parentId: PlacesUtils.unfiledBookmarksFolderId
                                     , index: PlacesUtils.bookmarks.DEFAULT_INDEX
                                     , feedURI: FEED_URI
                                     });
   PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
   PlacesUtils.livemarks.addLivemark({ title: "test"
                                     , parentId: PlacesUtils.unfiledBookmarksFolderId
                                     , index: PlacesUtils.bookmarks.DEFAULT_INDEX
                                     , feedURI: FEED_URI
                                     });
   let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.unfiledBookmarksFolderId,
                                                 PlacesUtils.bookmarks.DEFAULT_INDEX);
-  
-  PlacesUtils.livemarks.getLivemark(
+
+  let checkLivemark = (aLivemark) => {
+    do_check_eq(aLivemark.title, "test");
+    do_check_eq(aLivemark.parentId, PlacesUtils.unfiledBookmarksFolderId);
+    do_check_eq(aLivemark.index, PlacesUtils.bookmarks.getItemIndex(aLivemark.id));
+    do_check_true(aLivemark.feedURI.equals(FEED_URI));
+    do_check_eq(aLivemark.siteURI, null);
+    do_check_guid_for_bookmark(aLivemark.id, aLivemark.guid);
+  };
+
+  let callbackCalled = false;
+  let livemark = yield PlacesUtils.livemarks.getLivemark(
     { id: id },
-    function(aStatus, aLivemark) {
+    (aStatus, aLivemark) => {
+      callbackCalled = true;
       do_check_true(Components.isSuccessCode(aStatus));
-      do_check_eq(aLivemark.title, "test");
-      do_check_eq(aLivemark.parentId, PlacesUtils.unfiledBookmarksFolderId);
-      do_check_eq(aLivemark.index, PlacesUtils.bookmarks.getItemIndex(aLivemark.id));
-      do_check_true(aLivemark.feedURI.equals(FEED_URI));
-      do_check_eq(aLivemark.siteURI, null);
-      do_check_guid_for_bookmark(aLivemark.id, aLivemark.guid);
-      run_next_test();
-    }
-  );
+      checkLivemark(aLivemark);
+    } );
+
+  do_check_true(callbackCalled);
+  checkLivemark(livemark);
 });
 
-add_test(function test_title_change()
+add_task(function test_title_change()
 {
-  PlacesUtils.livemarks.addLivemark({ title: "test"
-                                    , parentId: PlacesUtils.unfiledBookmarksFolderId
-                                    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                    , feedURI: FEED_URI
-                                    }, function(aStatus, aLivemark) {
-    PlacesUtils.bookmarks.setItemTitle(aLivemark.id, "test2");
-    do_check_eq(aLivemark.title, "test2");
-    run_next_test();
-  });
+  let livemark = yield PlacesUtils.livemarks.addLivemark(
+    { title: "test"
+    , parentId: PlacesUtils.unfiledBookmarksFolderId
+    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+    , feedURI: FEED_URI });
+
+  PlacesUtils.bookmarks.setItemTitle(livemark.id, "test2");
+  do_check_eq(livemark.title, "test2");
 });
 
-add_test(function test_livemark_move()
+add_task(function test_livemark_move()
 {
-  PlacesUtils.livemarks.addLivemark({ title: "test"
-                                    , parentId: PlacesUtils.unfiledBookmarksFolderId
-                                    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
-                                    , feedURI: FEED_URI
-                                    }, function(aStatus, aLivemark) {
-    PlacesUtils.bookmarks.moveItem(aLivemark.id,
-                                   PlacesUtils.toolbarFolderId,
-                                   PlacesUtils.bookmarks.DEFAULT_INDEX);
-    do_check_eq(aLivemark.parentId, PlacesUtils.toolbarFolderId);
-    do_check_eq(aLivemark.index, PlacesUtils.bookmarks.getItemIndex(aLivemark.id));
-    run_next_test();
-  });
+  let livemark = yield PlacesUtils.livemarks.addLivemark(
+    { title: "test"
+    , parentId: PlacesUtils.unfiledBookmarksFolderId
+    , index: PlacesUtils.bookmarks.DEFAULT_INDEX
+    , feedURI: FEED_URI } );
+
+  PlacesUtils.bookmarks.moveItem(livemark.id,
+                                 PlacesUtils.toolbarFolderId,
+                                 PlacesUtils.bookmarks.DEFAULT_INDEX);
+  do_check_eq(livemark.parentId, PlacesUtils.toolbarFolderId);
+  do_check_eq(livemark.index, PlacesUtils.bookmarks.getItemIndex(livemark.id));
 });
+
+function run_test() {
+  run_next_test();
+}