Bug 459491 - nsPlacesDBFlush could use async queries instead of background thread
authorMarco Bonardo <mak77@bonardo.net>
Fri, 17 Oct 2008 06:12:52 -0400
changeset 20574 8d32fd798c17a985afc716507c2a8895a16beff0
parent 20573 1e3cbf009dbebbd226b5a9221e351d739fb76103
child 20575 581bd68c0b4cd0a5ff2ca02bd3752cf82f7403e8
push id2972
push usertmielczarek@mozilla.com
push dateFri, 17 Oct 2008 10:35:06 +0000
treeherdermozilla-central@f31b66fd4192 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs459491
milestone1.9.1b2pre
Bug 459491 - nsPlacesDBFlush could use async queries instead of background thread r=sdwilsh
toolkit/components/places/src/nsPlacesDBFlush.js
toolkit/components/places/tests/Makefile.in
toolkit/components/places/tests/background/head_background.js
toolkit/components/places/tests/background/test_database_sync_after_addBookmark.js
toolkit/components/places/tests/background/test_database_sync_after_addBookmark_batched.js
toolkit/components/places/tests/background/test_database_sync_after_addVisit.js
toolkit/components/places/tests/background/test_database_sync_after_addVisit_batched.js
toolkit/components/places/tests/background/test_database_sync_after_modifyBookmark.js
toolkit/components/places/tests/background/test_database_sync_after_quit_application.js
toolkit/components/places/tests/background/test_multiple_bookmarks_around_sync.js
toolkit/components/places/tests/background/test_multiple_visits_around_sync.js
toolkit/components/places/tests/sync/head_sync.js
toolkit/components/places/tests/sync/test_database_sync_after_addBookmark.js
toolkit/components/places/tests/sync/test_database_sync_after_addBookmark_batched.js
toolkit/components/places/tests/sync/test_database_sync_after_addVisit.js
toolkit/components/places/tests/sync/test_database_sync_after_addVisit_batched.js
toolkit/components/places/tests/sync/test_database_sync_after_modifyBookmark.js
toolkit/components/places/tests/sync/test_database_sync_after_quit_application.js
toolkit/components/places/tests/sync/test_multiple_bookmarks_around_sync.js
toolkit/components/places/tests/sync/test_multiple_visits_around_sync.js
--- a/toolkit/components/places/src/nsPlacesDBFlush.js
+++ b/toolkit/components/places/src/nsPlacesDBFlush.js
@@ -17,108 +17,98 @@
  *
  * The Initial Developer of the Original Code is
  * Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2008
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *   Marco Bonardo <mak77@bonardo.net>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Constants
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
-const kPlacesBackgroundShutdown = "places-background-shutdown";
+const kQuitApplication = "quit-application";
+const kSyncFinished = "places-sync-finished";
 
 const kSyncPrefName = "syncDBTableIntervalInSecs";
 const kDefaultSyncInterval = 120;
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsPlacesDBFlush class
 
 function nsPlacesDBFlush()
 {
-  //////////////////////////////////////////////////////////////////////////////
-  //// Smart Getters
-
-  this.__defineGetter__("_db", function() {
-    delete this._db;
-    return this._db = Cc["@mozilla.org/browser/nav-history-service;1"].
-                      getService(Ci.nsPIPlacesDatabase).
-                      DBConnection;
-  });
-
-  this.__defineGetter__("_bh", function() {
-    delete this._bh;
-    return this._bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
-                      getService(Ci.nsINavBookmarksService);
-  });
-
+  this._prefs = Cc["@mozilla.org/preferences-service;1"].
+              getService(Ci.nsIPrefService).
+              getBranch("places.");
 
   // Get our sync interval
-  this._prefs = Cc["@mozilla.org/preferences-service;1"].
-                getService(Ci.nsIPrefService).
-                getBranch("places.");
   try {
     // We want to silently fail if the preference does not exist, and use a
     // default to fallback to.
     this._syncInterval = this._prefs.getIntPref(kSyncPrefName);
     if (this._syncInterval <= 0)
       this._syncInterval = kDefaultSyncInterval;
   }
   catch (e) {
     // The preference did not exist, so default to two minutes.
     this._syncInterval = kDefaultSyncInterval;
   }
 
   // Register observers
-  this._bh.addObserver(this, false);
-
-  this._prefs.QueryInterface(Ci.nsIPrefBranch2).addObserver("", this, false);
+  this._bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+             getService(Ci.nsINavBookmarksService);
+  this._bs.addObserver(this, false);
 
-  let os = Cc["@mozilla.org/observer-service;1"].
-           getService(Ci.nsIObserverService);
-  os.addObserver(this, kPlacesBackgroundShutdown, false);
+  this._os = Cc["@mozilla.org/observer-service;1"].
+             getService(Ci.nsIObserverService);
+  this._os.addObserver(this, kQuitApplication, false);
+
+  this._prefs.QueryInterface(Ci.nsIPrefBranch2)
+             .addObserver("", this, false);
 
   // Create our timer to update everything
   this._timer = this._newTimer();
 }
 
 nsPlacesDBFlush.prototype = {
   //////////////////////////////////////////////////////////////////////////////
   //// nsIObserver
 
   observe: function DBFlush_observe(aSubject, aTopic, aData)
   {
-    if (aTopic == kPlacesBackgroundShutdown) {
-      this._bh.removeObserver(this);
+    if (aTopic == kQuitApplication) {
+      this._bs.removeObserver(this);
+      this._os.removeObserver(this, kQuitApplication);
+      this._prefs.QueryInterface(Ci.nsIPrefBranch2).removeObserver("", this);
       this._timer.cancel();
       this._timer = null;
-      this._syncAll();
+      this._syncTables(["places", "historyvisits"]);
     }
     else if (aTopic == "nsPref:changed" && aData == kSyncPrefName) {
       // Get the new pref value, and then update our timer
       this._syncInterval = aSubject.getIntPref(kSyncPrefName);
       if (this._syncInterval <= 0)
         this._syncInterval = kDefaultSyncInterval;
 
       // We may have canceled the timer already for batch updates, so we want to
@@ -143,109 +133,94 @@ nsPlacesDBFlush.prototype = {
     this._timer = null;
   },
 
   onEndUpdateBatch: function DBFlush_onEndUpdateBatch()
   {
     this._inBatchMode = false;
 
     // We need to sync and restore our timer now.
-    this._syncAll();
+    this._syncTables(["places", "historyvisits"]);
     this._timer = this._newTimer();
   },
 
-  onItemAdded: function() this._syncMozPlaces(),
+  onItemAdded: function() this._syncTables(["places"]),
 
   onItemChanged: function DBFlush_onItemChanged(aItemId, aProperty,
                                                          aIsAnnotationProperty,
                                                          aValue)
   {
     if (aProperty == "uri")
-      this._syncMozPlaces();
+      this._syncTables(["places"]);
   },
 
   onItemRemoved: function() { },
   onItemVisited: function() { },
   onItemMoved: function() { },
 
   //////////////////////////////////////////////////////////////////////////////
   //// nsITimerCallback
 
-  notify: function() this._syncAll(),
+  notify: function() this._syncTables(["places", "historyvisits"]),
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// mozIStorageStatementCallback
+
+  handleCompletion: function DBFlush_handleCompletion(aReason)
+  {
+    if (aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED) {
+      // Dispatch a notification that sync has finished.
+      this._os.notifyObservers(null, kSyncFinished, null);
+    }
+  },
 
   //////////////////////////////////////////////////////////////////////////////
   //// nsPlacesDBFlush
+  _syncInterval: kDefaultSyncInterval,
 
   /**
-   * Dispatches an event to the background thread to run _doSyncMozX("places").
+   * Execute async statements to sync temporary places table.
+   * @param aTableNames
+   *        array of table names that should be synced, as moz_{TableName}_temp.
    */
-  _syncMozPlaces: function DBFlush_syncMozPlaces()
+  _syncTables: function DBFlush_syncTables(aTableNames)
   {
     // No need to do extra work if we are in batch mode
     if (this._inBatchMode)
       return;
 
-    let self = this;
-    PlacesBackground.dispatch({
-      run: function() self._doSyncMozX("places")
-    }, Ci.nsIEventTarget.DISPATCH_NORMAL);
+    let statements = [];
+    for (let i = 0; i < aTableNames.length; i++)
+      statements.push(this._getSyncTableStatement(aTableNames[i]));
+
+    // Execute sync statements async in a transaction
+    this._db.executeAsync(statements, statements.length, this);
   },
 
   /**
-   * Dispatches an event to the background thread to sync all temporary tables.
+   * Generate the statement to synchronizes the moz_{aTableName} and
+   * moz_{aTableName}_temp by copying all the data from the temporary table
+   * into the permanent one.
+   * Most of the work is done through triggers defined in nsPlacesTriggers.h,
+   * they sync back to disk, then delete the data in the temporary table.
+   * @param aTableName
+   *        name of the table to build statement for, as moz_{TableName}_temp.
    */
-  _syncAll: function DBFlush_syncAll()
+  _getSyncTableStatement: function DBFlush_getSyncTableStatement(aTableName)
   {
-    let self = this;
-    PlacesBackground.dispatch({
-      run: function() {
-        // We try to get a transaction, but if we can't don't worry
-        let ourTransaction = false;
-        try {
-          this._db.beginTransaction();
-          ourTransaction = true;
-        }
-        catch (e) { }
-
-        try {
-          // This needs to also sync moz_places in order to maintain data
-          // integrity
-          self._doSyncMozX("places");
-          self._doSyncMozX("historyvisits");
-        }
-        catch (e) {
-          if (ourTransaction)
-            this._db.rollbackTransaction();
-          throw e;
-        }
-
-        if (ourTransaction)
-          this._db.commitTransaction();
-      }
-    }, Ci.nsIEventTarget.DISPATCH_NORMAL);
+    // Delete all the data in the temp table.
+    // We have triggers setup that ensure that the data is transferred over
+    // upon deletion.
+    return this._db.createStatement("DELETE FROM moz_" + aTableName + "_temp");
   },
 
   /**
-   * Synchronizes the moz_{aName} and moz_{aName}_temp by copying all the data
-   * from the temporary table into the permanent one.  It then deletes the data
-   * in the temporary table.  All of this is done in a transaction that is
-   * rolled back upon failure at any point.
-   */
-  _doSyncMozX: function DBFlush_doSyncMozX(aName)
-  {
-    // Delete all the data in the temp table.
-    // We have triggers setup that ensure that the data is transfered over
-   // upon deletion.
-   this._db.executeSimpleSQL("DELETE FROM moz_" + aName + "_temp");
-  },
-
-  /**
-   * Creates a new timer bases on this._timerInterval.
+   * Creates a new timer based on this._syncInterval.
    *
-   * @returns a REPEATING_SLACK nsITimer that runs every this._timerInterval.
+   * @returns a REPEATING_SLACK nsITimer that runs every this._syncInterval.
    */
   _newTimer: function DBFlush_newTimer()
   {
     let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
     timer.initWithCallback(this, this._syncInterval * 1000,
                            Ci.nsITimer.TYPE_REPEATING_SLACK);
     return timer;
   },
@@ -262,16 +237,26 @@ nsPlacesDBFlush.prototype = {
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIObserver,
     Ci.nsINavBookmarkObserver,
     Ci.nsITimerCallback,
   ])
 };
 
+//////////////////////////////////////////////////////////////////////////////
+//// Smart Getters
+
+nsPlacesDBFlush.prototype.__defineGetter__("_db", function() {
+  delete nsPlacesDBFlush._db;
+  return nsPlacesDBFlush._db = Cc["@mozilla.org/browser/nav-history-service;1"].
+                               getService(Ci.nsPIPlacesDatabase).
+                               DBConnection;
+});
+
 ////////////////////////////////////////////////////////////////////////////////
 //// Module Registration
 
 let components = [nsPlacesDBFlush];
 function NSGetModule(compMgr, fileSpec)
 {
   return XPCOMUtils.generateModule(components);
 }
--- a/toolkit/components/places/tests/Makefile.in
+++ b/toolkit/components/places/tests/Makefile.in
@@ -44,16 +44,17 @@ relativesrcdir	= toolkit/components/plac
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= test_places
 
 XPCSHELL_TESTS = \
                  autocomplete \
                  background \
+                 sync \
                  bookmarks \
                  queries \
                  unit \
                  $(NULL)
 
 # Simple MochiTests
 MOCHI_TESTS = mochitest/test_bug_405924.html \
 							mochitest/test_bug_411966.html \
--- a/toolkit/components/places/tests/background/head_background.js
+++ b/toolkit/components/places/tests/background/head_background.js
@@ -95,181 +95,8 @@ function clearDB() {
   try {
     var file = dirSvc.get('ProfD', Ci.nsIFile);
     file.append("places.sqlite");
     if (file.exists())
       file.remove(false);
   } catch(ex) { dump("Exception: " + ex); }
 }
 clearDB();
-
-/**
- * Dumps the rows of a table out to the console.
- *
- * @param aName
- *        The name of the table or view to output.
- */
-function dump_table(aName)
-{
-  let db = Cc["@mozilla.org/browser/nav-history-service;1"].
-           getService(Ci.nsPIPlacesDatabase).
-           DBConnection;
-  let stmt = db.createStatement("SELECT * FROM " + aName);
-
-  dump("\n*** Printing data from " + aName + ":\n");
-  let count = 0;
-  while (stmt.executeStep()) {
-    let columns = stmt.numEntries;
-
-    if (count == 0) {
-      // print the column names
-      for (let i = 0; i < columns; i++)
-        dump(stmt.getColumnName(i) + "\t");
-      dump("\n");
-    }
-
-    // print the row
-    for (let i = 0; i < columns; i++) {
-      switch (stmt.getTypeOfIndex(i)) {
-        case Ci.mozIStorageValueArray.VALUE_TYPE_NULL:
-          dump("NULL\t");
-          break;
-        case Ci.mozIStorageValueArray.VALUE_TYPE_INTEGER:
-          dump(stmt.getInt64(i) + "\t");
-          break;
-        case Ci.mozIStorageValueArray.VALUE_TYPE_FLOAT:
-          dump(stmt.getDouble(i) + "\t");
-          break;
-        case Ci.mozIStorageValueArray.VALUE_TYPE_TEXT:
-          dump(stmt.getString(i) + "\t");
-          break;
-      }
-    }
-    dump("\n");
-
-    count++;
-  }
-  dump("*** There were a total of " + count + " rows of data.\n\n");
-
-  stmt.reset();
-  stmt.finalize();
-  stmt = null;
-}
-
-/**
- * This dispatches the observer topic "quit-application" to clean up the
- * background thread.
- */
-function finish_test()
-{
-  // This next bit needs to run on the main thread
-  let tm = Cc["@mozilla.org/thread-manager;1"].
-           getService(Ci.nsIThreadManager);
-  tm.mainThread.dispatch({
-    run: function()
-    {
-      // xpcshell doesn't dispatch shutdown-application
-      let os = Cc["@mozilla.org/observer-service;1"].
-               getService(Ci.nsIObserverService);
-      os.notifyObservers(null, "quit-application", null);
-      do_test_finished();
-    }
-  }, Ci.nsIEventTarget.DISPATCH_NORMAL);
-}
-
-/**
- * Function tests to see if the place associated with the bookmark with id
- * aBookmarkId has the uri aExpectedURI.  The event will call finish_test() if
- * aFinish is true.
- *
- * @param aBookmarkId
- *        The bookmark to check against.
- * @param aExpectedURI
- *        The URI we expect to be in moz_places.
- * @param aExpected
- *        Indicates if we expect to get a result or not.
- * @param [optional] aFinish
- *        Indicates if the test should be completed or not.
- */
-function new_test_bookmark_uri_event(aBookmarkId, aExpectedURI, aExpected,                                           aFinish)
-{
-  return {
-    run: function()
-    {
-      let db = Cc["@mozilla.org/browser/nav-history-service;1"].
-               getService(Ci.nsPIPlacesDatabase).
-               DBConnection;
-      let stmt = db.createStatement(
-        "SELECT moz_places.url " +
-        "FROM moz_bookmarks INNER JOIN moz_places " +
-        "ON moz_bookmarks.fk = moz_places.id " +
-        "WHERE moz_bookmarks.id = ?1"
-      );
-      stmt.bindInt64Parameter(0, aBookmarkId);
-
-      if (aExpected) {
-        do_check_true(stmt.executeStep());
-        do_check_eq(stmt.getUTF8String(0), aExpectedURI);
-      }
-      else {
-        do_check_false(stmt.executeStep());
-      }
-      stmt.reset();
-      stmt.finalize();
-      stmt = null;
-
-      if (aFinish)
-        finish_test();
-    }
-  };
-}
-
-/**
- * Function tests to see if the place associated with the visit with id aVisitId
- * has the uri aExpectedURI.  The event will call finish_test() if aFinish is
- * true.
- *
- * @param aVisitId
- *        The visit to check against.
- * @param aExpectedURI
- *        The URI we expect to be in moz_places.
- * @param aExpected
- *        Indicates if we expect to get a result or not.
- * @param [optional] aFinish
- *        Indicates if the test should be completed or not.
- */
-function new_test_visit_uri_event(aVisitId, aExpectedURI, aExpected, aFinish)
-{
-  return {
-    run: function()
-    {
-      let db = Cc["@mozilla.org/browser/nav-history-service;1"].
-               getService(Ci.nsPIPlacesDatabase).
-               DBConnection;
-      let stmt = db.createStatement(
-        "SELECT moz_places.url " +
-        "FROM moz_historyvisits INNER JOIN moz_places " +
-        "ON moz_historyvisits.place_id = moz_places.id " +
-        "WHERE moz_historyvisits.id = ?1"
-      );
-      stmt.bindInt64Parameter(0, aVisitId);
-
-      if (aExpected) {
-        do_check_true(stmt.executeStep());
-        do_check_eq(stmt.getUTF8String(0), aExpectedURI);
-      }
-      else {
-        do_check_false(stmt.executeStep());
-      }
-      stmt.reset();
-      stmt.finalize();
-      stmt = null;
-
-      if (aFinish)
-        finish_test();
-    }
-  };
-}
-
-// profile-after-change doesn't create components in xpcshell, so we have to do
-// it ourselves
-Cc["@mozilla.org/places/sync;1"].getService(Ci.nsISupports);
-
copy from toolkit/components/places/tests/background/head_background.js
copy to toolkit/components/places/tests/sync/head_sync.js
--- a/toolkit/components/places/tests/background/head_background.js
+++ b/toolkit/components/places/tests/sync/head_sync.js
@@ -17,16 +17,18 @@
  * The Initial Developer of the Original Code is
  * Google Inc.
  * Portions created by the Initial Developer are Copyright (C) 2005
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *  Brian Ryner <bryner@brianryner.com>
  *  Dietrich Ayala <dietrich@mozilla.com>
+ *  Shawn Wilsher <me@shawnwilsher.com>
+ *  Marco Bonardo <mak77@bonardo.net>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -150,81 +152,68 @@ function dump_table(aName)
   dump("*** There were a total of " + count + " rows of data.\n\n");
 
   stmt.reset();
   stmt.finalize();
   stmt = null;
 }
 
 /**
- * This dispatches the observer topic "quit-application" to clean up the
- * background thread.
+ * This dispatches the observer topic "quit-application" to clean up the sync
+ * component.
  */
 function finish_test()
 {
-  // This next bit needs to run on the main thread
-  let tm = Cc["@mozilla.org/thread-manager;1"].
-           getService(Ci.nsIThreadManager);
-  tm.mainThread.dispatch({
-    run: function()
-    {
-      // xpcshell doesn't dispatch shutdown-application
-      let os = Cc["@mozilla.org/observer-service;1"].
-               getService(Ci.nsIObserverService);
-      os.notifyObservers(null, "quit-application", null);
-      do_test_finished();
-    }
-  }, Ci.nsIEventTarget.DISPATCH_NORMAL);
+  // xpcshell doesn't dispatch shutdown-application
+  let os = Cc["@mozilla.org/observer-service;1"].
+           getService(Ci.nsIObserverService);
+  os.notifyObservers(null, "quit-application", null);
+  do_test_finished();
 }
 
 /**
  * Function tests to see if the place associated with the bookmark with id
  * aBookmarkId has the uri aExpectedURI.  The event will call finish_test() if
  * aFinish is true.
  *
  * @param aBookmarkId
  *        The bookmark to check against.
  * @param aExpectedURI
  *        The URI we expect to be in moz_places.
  * @param aExpected
  *        Indicates if we expect to get a result or not.
  * @param [optional] aFinish
  *        Indicates if the test should be completed or not.
  */
-function new_test_bookmark_uri_event(aBookmarkId, aExpectedURI, aExpected,                                           aFinish)
+function new_test_bookmark_uri_event(aBookmarkId, aExpectedURI, aExpected, aFinish)
 {
-  return {
-    run: function()
-    {
-      let db = Cc["@mozilla.org/browser/nav-history-service;1"].
-               getService(Ci.nsPIPlacesDatabase).
-               DBConnection;
-      let stmt = db.createStatement(
-        "SELECT moz_places.url " +
-        "FROM moz_bookmarks INNER JOIN moz_places " +
-        "ON moz_bookmarks.fk = moz_places.id " +
-        "WHERE moz_bookmarks.id = ?1"
-      );
-      stmt.bindInt64Parameter(0, aBookmarkId);
+  let db = Cc["@mozilla.org/browser/nav-history-service;1"].
+           getService(Ci.nsPIPlacesDatabase).
+           DBConnection;
+  let stmt = db.createStatement(
+    "SELECT moz_places.url " +
+    "FROM moz_bookmarks INNER JOIN moz_places " +
+    "ON moz_bookmarks.fk = moz_places.id " +
+    "WHERE moz_bookmarks.id = ?1"
+  );
+  stmt.bindInt64Parameter(0, aBookmarkId);
 
-      if (aExpected) {
-        do_check_true(stmt.executeStep());
-        do_check_eq(stmt.getUTF8String(0), aExpectedURI);
-      }
-      else {
-        do_check_false(stmt.executeStep());
-      }
-      stmt.reset();
-      stmt.finalize();
-      stmt = null;
+  if (aExpected) {
+    do_check_true(stmt.executeStep());
+    do_check_eq(stmt.getUTF8String(0), aExpectedURI);
+  }
+  else {
+    do_check_false(stmt.executeStep());
+  }
+  stmt.reset();
+  stmt.finalize();
+  stmt = null;
 
-      if (aFinish)
-        finish_test();
-    }
-  };
+  if (aFinish)
+    finish_test();
 }
 
 /**
  * Function tests to see if the place associated with the visit with id aVisitId
  * has the uri aExpectedURI.  The event will call finish_test() if aFinish is
  * true.
  *
  * @param aVisitId
@@ -233,43 +222,37 @@ function new_test_bookmark_uri_event(aBo
  *        The URI we expect to be in moz_places.
  * @param aExpected
  *        Indicates if we expect to get a result or not.
  * @param [optional] aFinish
  *        Indicates if the test should be completed or not.
  */
 function new_test_visit_uri_event(aVisitId, aExpectedURI, aExpected, aFinish)
 {
-  return {
-    run: function()
-    {
-      let db = Cc["@mozilla.org/browser/nav-history-service;1"].
-               getService(Ci.nsPIPlacesDatabase).
-               DBConnection;
-      let stmt = db.createStatement(
-        "SELECT moz_places.url " +
-        "FROM moz_historyvisits INNER JOIN moz_places " +
-        "ON moz_historyvisits.place_id = moz_places.id " +
-        "WHERE moz_historyvisits.id = ?1"
-      );
-      stmt.bindInt64Parameter(0, aVisitId);
+  let db = Cc["@mozilla.org/browser/nav-history-service;1"].
+           getService(Ci.nsPIPlacesDatabase).
+           DBConnection;
+  let stmt = db.createStatement(
+    "SELECT moz_places.url " +
+    "FROM moz_historyvisits INNER JOIN moz_places " +
+    "ON moz_historyvisits.place_id = moz_places.id " +
+    "WHERE moz_historyvisits.id = ?1"
+  );
+  stmt.bindInt64Parameter(0, aVisitId);
 
-      if (aExpected) {
-        do_check_true(stmt.executeStep());
-        do_check_eq(stmt.getUTF8String(0), aExpectedURI);
-      }
-      else {
-        do_check_false(stmt.executeStep());
-      }
-      stmt.reset();
-      stmt.finalize();
-      stmt = null;
+  if (aExpected) {
+    do_check_true(stmt.executeStep());
+    do_check_eq(stmt.getUTF8String(0), aExpectedURI);
+  }
+  else {
+    do_check_false(stmt.executeStep());
+  }
+  stmt.reset();
+  stmt.finalize();
+  stmt = null;
 
-      if (aFinish)
-        finish_test();
-    }
-  };
+  if (aFinish)
+    finish_test();
 }
 
 // profile-after-change doesn't create components in xpcshell, so we have to do
 // it ourselves
 Cc["@mozilla.org/places/sync;1"].getService(Ci.nsISupports);
-
rename from toolkit/components/places/tests/background/test_database_sync_after_addBookmark.js
rename to toolkit/components/places/tests/sync/test_database_sync_after_addBookmark.js
--- a/toolkit/components/places/tests/background/test_database_sync_after_addBookmark.js
+++ b/toolkit/components/places/tests/sync/test_database_sync_after_addBookmark.js
@@ -17,39 +17,73 @@
  *
  * The Initial Developer of the Original Code is
  * Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2008
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *   Marco Bonardo <mak77@bonardo.net>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
+var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+         getService(Ci.nsINavBookmarksService);
+var os = Cc["@mozilla.org/observer-service;1"].
+         getService(Ci.nsIObserverService);
+var prefs = Cc["@mozilla.org/preferences-service;1"].
+            getService(Ci.nsIPrefService).
+            getBranch("places.");
 
 const TEST_URI = "http://test.com/";
 
+const SYNC_INTERVAL = 600; // ten minutes
+const kSyncPrefName = "syncDBTableIntervalInSecs";
+const kSyncFinished = "places-sync-finished";
+
+// Used to update observer itemId
+var bookmarksObserver = {
+  onItemAdded: function(aItemId, aNewParent, aNewIndex) {
+    observer.itemId = aItemId;
+  }
+}
+bs.addObserver(bookmarksObserver, false);
+
+var observer = {
+  itemId: -1,
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic == kSyncFinished) {
+      do_check_neq(this.itemId, -1);
+      // remove the observer, we don't need to observe sync on quit
+      os.removeObserver(this, kSyncFinished);
+      bs.removeObserver(bookmarksObserver);
+      // Check that moz_places table has been correctly synced
+      new_test_bookmark_uri_event(this.itemId, TEST_URI, true, true);
+    }
+  }
+}
+os.addObserver(observer, kSyncFinished, false);
+
 function run_test()
 {
-  // First insert it
-  let bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
-           getService(Ci.nsINavBookmarksService);
-  let id = bh.insertBookmark(bh.unfiledBookmarksFolder, uri(TEST_URI),
-                             bh.DEFAULT_INDEX, "test");
+  // First set the preference for the timer to a really large value so it won't
+  // run before the test finishes.
+  prefs.setIntPref(kSyncPrefName, SYNC_INTERVAL);
 
-  PlacesBackground.dispatch(new_test_bookmark_uri_event(id, TEST_URI, true, true),
-                            Ci.nsIEventTarget.DISPATCH_NORMAL);
+  // Insert a new bookmark
+  bs.insertBookmark(bs.unfiledBookmarksFolder, uri(TEST_URI),
+                    bs.DEFAULT_INDEX, "test");
+
   do_test_pending();
 }
rename from toolkit/components/places/tests/background/test_database_sync_after_addBookmark_batched.js
rename to toolkit/components/places/tests/sync/test_database_sync_after_addBookmark_batched.js
--- a/toolkit/components/places/tests/background/test_database_sync_after_addBookmark_batched.js
+++ b/toolkit/components/places/tests/sync/test_database_sync_after_addBookmark_batched.js
@@ -17,48 +17,94 @@
  *
  * The Initial Developer of the Original Code is
  * Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2008
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *   Marco Bonardo <mak77@bonardo.net>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
+var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+         getService(Ci.nsINavBookmarksService);
+var os = Cc["@mozilla.org/observer-service;1"].
+         getService(Ci.nsIObserverService);
+var prefs = Cc["@mozilla.org/preferences-service;1"].
+            getService(Ci.nsIPrefService).
+            getBranch("places.");
 
 const TEST_URI = "http://test.com/";
 
+const SYNC_INTERVAL = 600; // ten minutes
+const kSyncPrefName = "syncDBTableIntervalInSecs";
+const kSyncFinished = "places-sync-finished";
+
+// Used to check if we are batching and to update observer itemId
+var bookmarksObserver = {
+  _batching: false,
+  onBeginUpdateBatch: function() {
+    this._batching = true;
+  },
+  onEndUpdateBatch: function() {
+    this._batching = false;
+  },
+  onItemAdded: function(aItemId, aNewParent, aNewIndex) {
+    observer.itemId = aItemId;
+  }
+}
+bs.addObserver(bookmarksObserver, false);
+
+var observer = {
+  itemId: -1,
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic == kSyncFinished) {
+      dump(this.itemId);
+      // item id must be valid
+      do_check_neq(this.itemId, -1);
+      // Check that we are not in a batch
+      do_check_false(bookmarksObserver._batching);
+      // remove the observer, we don't need to observe sync on quit
+      os.removeObserver(this, kSyncFinished);
+      bs.removeObserver(bookmarksObserver);
+      // Check that tables have been correctly synced
+      new_test_bookmark_uri_event(this.itemId, TEST_URI, true, true);
+    }
+  }
+}
+os.addObserver(observer, kSyncFinished, false);
+
 function run_test()
 {
-  // First insert it
-  let bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
-           getService(Ci.nsINavBookmarksService);
-  let id = null;
-  bh.runInBatchMode({
+  // Set the preference for the timer to a really large value, so it won't
+  // run before the test finishes.
+  prefs.setIntPref(kSyncPrefName, SYNC_INTERVAL);
+
+  // Add a bookmark in batch mode
+  let id = -1;
+  bs.runInBatchMode({
     runBatched: function(aUserData)
     {
-      id = bh.insertBookmark(bh.unfiledBookmarksFolder, uri(TEST_URI),
-                             bh.DEFAULT_INDEX, "test");
-
-      PlacesBackground.dispatch(new_test_bookmark_uri_event(id, TEST_URI, false),
-                                Ci.nsIEventTarget.DISPATCH_SYNC);
+      id = bs.insertBookmark(bs.unfiledBookmarksFolder, uri(TEST_URI),
+                             bs.DEFAULT_INDEX, "test");
+      // We should not sync during a batch
+      new_test_bookmark_uri_event(id, TEST_URI, false);
     }
   }, null);
-  do_check_neq(id, null);
-  PlacesBackground.dispatch(new_test_bookmark_uri_event(id, TEST_URI, true, true),
-                            Ci.nsIEventTarget.DISPATCH_NORMAL);
+  // Ensure the bookmark has been added
+  do_check_neq(id, -1);
+
   do_test_pending();
 }
rename from toolkit/components/places/tests/background/test_database_sync_after_addVisit.js
rename to toolkit/components/places/tests/sync/test_database_sync_after_addVisit.js
--- a/toolkit/components/places/tests/background/test_database_sync_after_addVisit.js
+++ b/toolkit/components/places/tests/sync/test_database_sync_after_addVisit.js
@@ -17,55 +17,57 @@
  *
  * The Initial Developer of the Original Code is
  * Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2008
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *   Marco Bonardo <mak77@bonardo.net>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
+var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+         getService(Ci.nsINavHistoryService);
+var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+         getService(Ci.nsINavBookmarksService);
+var prefs = Cc["@mozilla.org/preferences-service;1"].
+            getService(Ci.nsIPrefService).
+            getBranch("places.");
 
 const TEST_URI = "http://test.com/";
+
 const kSyncPrefName = "syncDBTableIntervalInSecs";
 const SYNC_INTERVAL = 1;
 
 function run_test()
 {
   // First set the preference for the timer to a small value
-  let prefs = Cc["@mozilla.org/preferences-service;1"].
-              getService(Ci.nsIPrefService).
-              getBranch("places.");
   prefs.setIntPref(kSyncPrefName, SYNC_INTERVAL);
 
   // Now add the visit
-  let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
-           getService(Ci.nsINavHistoryService);
   let id = hs.addVisit(uri(TEST_URI), Date.now() * 1000, null,
                        hs.TRANSITION_TYPED, false, 0);
 
   // Check the visit, but after enough time has passed for the DB flush service
   // to have fired it's timer.
   let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   timer.initWithCallback({
     notify: function(aTimer)
     {
-      PlacesBackground.dispatch(new_test_visit_uri_event(id, TEST_URI, true, true),
-                                Ci.nsIEventTarget.DISPATCH_NORMAL);
+      new_test_visit_uri_event(id, TEST_URI, true, true);
     }
   }, (SYNC_INTERVAL * 1000) * 2, Ci.nsITimer.TYPE_ONE_SHOT);
   do_test_pending();
 }
rename from toolkit/components/places/tests/background/test_database_sync_after_addVisit_batched.js
rename to toolkit/components/places/tests/sync/test_database_sync_after_addVisit_batched.js
--- a/toolkit/components/places/tests/background/test_database_sync_after_addVisit_batched.js
+++ b/toolkit/components/places/tests/sync/test_database_sync_after_addVisit_batched.js
@@ -17,50 +17,93 @@
  *
  * The Initial Developer of the Original Code is
  * Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2008
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *   Marco Bonardo <mak77@bonardo.net>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
+  var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+           getService(Ci.nsINavBookmarksService);
+  var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+           getService(Ci.nsINavHistoryService);
+  var os = Cc["@mozilla.org/observer-service;1"].
+           getService(Ci.nsIObserverService);
 
 const TEST_URI = "http://test.com/";
 
+const kSyncFinished = "places-sync-finished";
+
+// Used to check if we are batching
+var bookmarksObserver = {
+  _batching: false,
+  onBeginUpdateBatch: function() {
+    this._batching = true;
+  },
+  onEndUpdateBatch: function() {
+    this._batching = false;
+  }
+}
+bs.addObserver(bookmarksObserver, false);
+
+// Used to update observer visitId
+var historyObserver = {
+  onVisit: function(aURI, aVisitId, aTime, aSessionId, aReferringId,
+                    aTransitionType, aAdded) {
+    observer.visitId = aVisitId;
+  }
+}
+hs.addObserver(historyObserver, false);
+
+var observer = {
+  visitId: -1,
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic == kSyncFinished) {
+      // visit id must be valid
+      do_check_neq(this.visitId, -1);
+      // Check that we are not in a batch
+      do_check_false(bookmarksObserver._batching);
+      // remove the observer, we don't need to observe sync on quit
+      os.removeObserver(this, kSyncFinished);
+      bs.removeObserver(bookmarksObserver);
+      hs.removeObserver(historyObserver);
+      // Check that tables have been correctly synced
+      new_test_visit_uri_event(this.visitId, TEST_URI, true, true);
+    }
+  }
+}
+os.addObserver(observer, kSyncFinished, false);
+
 function run_test()
 {
-  // Now add the visit in batch mode
-  let bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
-           getService(Ci.nsINavBookmarksService);
-  let id = null;
-  bh.runInBatchMode({
+  // Add a visit in batch mode
+  let id = -1;
+  bs.runInBatchMode({
     runBatched: function(aUserData)
     {
-      let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
-               getService(Ci.nsINavHistoryService);
-
       id = hs.addVisit(uri(TEST_URI), Date.now() * 1000, null,
                        hs.TRANSITION_TYPED, false, 0);
-      PlacesBackground.dispatch(new_test_visit_uri_event(id, TEST_URI, false),
-                                Ci.nsIEventTarget.DISPATCH_SYNC);
+      // We should not sync during a batch
+      new_test_visit_uri_event(id, TEST_URI, false);
     }
   }, null);
-  do_check_neq(id, null);
-  PlacesBackground.dispatch(new_test_visit_uri_event(id, TEST_URI, true, true),
-                            Ci.nsIEventTarget.DISPATCH_NORMAL);
+  // Ensure the visit has been added
+  do_check_neq(id, -1);
+
   do_test_pending();
 }
rename from toolkit/components/places/tests/background/test_database_sync_after_modifyBookmark.js
rename to toolkit/components/places/tests/sync/test_database_sync_after_modifyBookmark.js
--- a/toolkit/components/places/tests/background/test_database_sync_after_modifyBookmark.js
+++ b/toolkit/components/places/tests/sync/test_database_sync_after_modifyBookmark.js
@@ -17,48 +17,92 @@
  *
  * The Initial Developer of the Original Code is
  * Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2008
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *   Marco Bonardo <mak77@bonardo.net>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
+var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+         getService(Ci.nsINavBookmarksService);
+var os = Cc["@mozilla.org/observer-service;1"].
+         getService(Ci.nsIObserverService);
+var prefs = Cc["@mozilla.org/preferences-service;1"].
+            getService(Ci.nsIPrefService).
+            getBranch("places.");
 
 const TEST_URI = "http://test.com/";
 const MODIFIED_URI = "http://test.com/index.html";
 
+const SYNC_INTERVAL = 600; // ten minutes
+const kSyncPrefName = "syncDBTableIntervalInSecs";
+const kSyncFinished = "places-sync-finished";
+
+// Used to update observer itemId
+var bookmarksObserver = {
+  onItemAdded: function(aItemId, aNewParent, aNewIndex) {
+    observer.itemId = aItemId;
+  },
+  onItemChanged: function(aItemId, aProperty, aValue) {
+    if (aProperty == "uri")
+      do_check_eq(observer.itemId, aItemId);
+  }
+}
+bs.addObserver(bookmarksObserver, false);
+
+var observer = {
+  itemId: -1,
+  _runCount: 0,
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic == kSyncFinished) {
+      // item id must be valid
+      do_check_neq(this.itemId, -1);
+      if (++this._runCount == 1) {
+        // First sync is fired by adding the bookmark
+        // Check that tables have been synced after insertBookmark
+        new_test_bookmark_uri_event(this.itemId, TEST_URI, true);
+        // Now modify the bookmark
+        bs.changeBookmarkURI(this.itemId, uri(MODIFIED_URI));
+      }
+      else if (this._runCount == 2) {
+        // Second sync is fired by changing the bookmark's uri
+        // remove the observer, we don't need to observe sync on quit
+        os.removeObserver(this, kSyncFinished);
+        bs.removeObserver(bookmarksObserver);
+        // Check that tables have been synced after changeBookmarkURI
+        new_test_bookmark_uri_event(this.itemId, MODIFIED_URI, true, true);
+      }
+      else
+        do_throw("Too many places sync calls");
+    }
+  }
+}
+os.addObserver(observer, kSyncFinished, false);
+
 function run_test()
 {
-  // First insert it
-  let bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
-           getService(Ci.nsINavBookmarksService);
-  let id = bh.insertBookmark(bh.unfiledBookmarksFolder, uri(TEST_URI),
-                             bh.DEFAULT_INDEX, "test");
+  // Set the preference for the timer to a really large value, so it won't
+  // run before the test finishes.
+  prefs.setIntPref(kSyncPrefName, SYNC_INTERVAL);
 
-  // Dispatch the check synchronously so we don't modify the data before this
-  // test runs
-  PlacesBackground.dispatch(new_test_bookmark_uri_event(id, TEST_URI, true),
-                            Ci.nsIEventTarget.DISPATCH_SYNC);
+  // Insert a new bookmark
+  bs.insertBookmark(bs.unfiledBookmarksFolder, uri(TEST_URI),
+                    bs.DEFAULT_INDEX, "test");
 
-  // Now modify the bookmark
-  bh.changeBookmarkURI(id, uri(MODIFIED_URI));
-
-  PlacesBackground.dispatch(new_test_bookmark_uri_event(id, MODIFIED_URI, true, true),
-                            Ci.nsIEventTarget.DISPATCH_NORMAL);
   do_test_pending();
 }
rename from toolkit/components/places/tests/background/test_database_sync_after_quit_application.js
rename to toolkit/components/places/tests/sync/test_database_sync_after_quit_application.js
--- a/toolkit/components/places/tests/background/test_database_sync_after_quit_application.js
+++ b/toolkit/components/places/tests/sync/test_database_sync_after_quit_application.js
@@ -17,53 +17,77 @@
  *
  * The Initial Developer of the Original Code is
  * Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2008
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *   Marco Bonardo <mak77@bonardo.net>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
+var os = Cc["@mozilla.org/observer-service;1"].
+         getService(Ci.nsIObserverService);
+var prefs = Cc["@mozilla.org/preferences-service;1"].
+            getService(Ci.nsIPrefService).
+            getBranch("places.");
+var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+         getService(Ci.nsINavHistoryService);
 
 const TEST_URI = "http://test.com/";
+
 const kSyncPrefName = "syncDBTableIntervalInSecs";
 const SYNC_INTERVAL = 600; // ten minutes
+const kSyncFinished = "places-sync-finished";
+
+var historyObserver = {
+  onVisit: function(aURI, aVisitId, aTime, aSessionId, aReferringId,
+                    aTransitionType, aAdded) {
+    observer.visitId = aVisitId;
+  }
+}
+hs.addObserver(historyObserver, false);
+
+var observer = {
+  visitId: -1,
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic == kSyncFinished) {
+      // visit id must be valid
+      do_check_neq(this.visitId, -1);
+      // remove the observer, we don't need to observe sync on quit
+      os.removeObserver(this, kSyncFinished);
+      hs.removeObserver(historyObserver);
+      // Check that tables have been correctly synced
+      new_test_visit_uri_event(this.visitId, TEST_URI, true, true);
+    }
+  }
+}
+os.addObserver(observer, kSyncFinished, false);
 
 function run_test()
 {
-  // First set the preference for the timer to a really large value so it won't
+  // Set the preference for the timer to a really large value, so it won't
   // run before the test finishes.
-  let prefs = Cc["@mozilla.org/preferences-service;1"].
-              getService(Ci.nsIPrefService).
-              getBranch("places.");
   prefs.setIntPref(kSyncPrefName, SYNC_INTERVAL);
 
-  // Now add the visit
-  let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
-           getService(Ci.nsINavHistoryService);
-  let id = hs.addVisit(uri(TEST_URI), Date.now() * 1000, null,
-                       hs.TRANSITION_TYPED, false, 0);
+  // Now add a visit
+  hs.addVisit(uri(TEST_URI), Date.now() * 1000, null,
+              hs.TRANSITION_TYPED, false, 0);
 
   // Notify that we are quitting the app - we should sync!
-  let os = Cc["@mozilla.org/observer-service;1"].
-           getService(Ci.nsIObserverService);
   os.notifyObservers(null, "quit-application", null);
 
-  // Check the visit.  The background thread should have joined with the main
-  // thread by now if everything is working correctly.
-  new_test_visit_uri_event(id, TEST_URI, true, false).run();
+  do_test_pending();
 }
rename from toolkit/components/places/tests/background/test_multiple_bookmarks_around_sync.js
rename to toolkit/components/places/tests/sync/test_multiple_bookmarks_around_sync.js
--- a/toolkit/components/places/tests/background/test_multiple_bookmarks_around_sync.js
+++ b/toolkit/components/places/tests/sync/test_multiple_bookmarks_around_sync.js
@@ -17,16 +17,17 @@
  *
  * The Initial Developer of the Original Code is
  * Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2008
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *   Marco Bonardo <mak77@bonardo.net>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -38,66 +39,112 @@
  * ***** END LICENSE BLOCK ***** */
 
 /**
  * This test ensures that adding a bookmark (which has an implicit sync), then
  * adding another one that has the same place, we end up with only one entry in
  * moz_places.
  */
 
-Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
+var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+         getService(Ci.nsINavBookmarksService);
+var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+         getService(Ci.nsINavHistoryService);
+var db = Cc["@mozilla.org/browser/nav-history-service;1"].
+         getService(Ci.nsPIPlacesDatabase).
+         DBConnection;
+var os = Cc["@mozilla.org/observer-service;1"].
+         getService(Ci.nsIObserverService);
+var prefs = Cc["@mozilla.org/preferences-service;1"].
+            getService(Ci.nsIPrefService).
+            getBranch("places.");
 
 const TEST_URI = "http://test.com/";
 
-let db = Cc["@mozilla.org/browser/nav-history-service;1"].
-         getService(Ci.nsPIPlacesDatabase).
-         DBConnection;
+const SYNC_INTERVAL = 600; // ten minutes
+const kSyncPrefName = "syncDBTableIntervalInSecs";
+const kSyncFinished = "places-sync-finished";
+
+// Used to update observer itemId
+var bookmarksObserver = {
+  onItemAdded: function(aItemId, aNewParent, aNewIndex) {
+    observer.itemIds.push(aItemId);
+  }
+}
+bs.addObserver(bookmarksObserver, false);
+
+var observer = {
+  itemIds: [],
+  _placeId: -1,
+  _runCount: 0,
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic == kSyncFinished) {
+      if (++this._runCount == 1) {
+        let itemId = this.itemIds[this._runCount - 1];
+        // item id must be valid
+        do_check_neq(itemId, null);
+        // Ensure tables have been synced
+        new_test_bookmark_uri_event(itemId, TEST_URI, true);
+
+        // Get the place_id
+        let stmt = db.createStatement(
+          "SELECT fk " +
+          "FROM moz_bookmarks " +
+          "WHERE id = ?"
+        );
+        stmt.bindInt64Parameter(0, itemId);
+        do_check_true(stmt.executeStep());
+        this._placeId = stmt.getInt64(0);
+        stmt.finalize();
+        stmt = null;
+        // place id must be valid
+        do_check_true(this._placeId > 0);
+      }
+      else if (this._runCount == 2) {
+        let itemId = this.itemIds[this._runCount - 1];
+        // item id must be valid
+        do_check_neq(itemId, null);
+        // Ensure it was added
+        new_test_bookmark_uri_event(itemId, TEST_URI, true);
+
+        // Check to make sure we have the same place_id
+        stmt = db.createStatement(
+          "SELECT * " +
+          "FROM moz_bookmarks " +
+          "WHERE id = ?1 " +
+          "AND fk = ?2"
+        );
+        stmt.bindInt64Parameter(0, itemId);
+        stmt.bindInt64Parameter(1, this._placeId);
+        do_check_true(stmt.executeStep());
+        stmt.finalize();
+        stmt = null;
+
+        // remove the observer, we don't need to observe sync on quit
+        os.removeObserver(this, kSyncFinished);
+        bs.removeObserver(bookmarksObserver);
+        // test ends here
+        finish_test();
+      }
+      else
+        do_throw("Too many places sync calls");
+    }
+  }
+}
+os.addObserver(observer, kSyncFinished, false);
 
 function run_test()
 {
-  // Add the first bookmark
-  let bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
-           getService(Ci.nsINavBookmarksService);
-  let id1 = bh.insertBookmark(bh.unfiledBookmarksFolder, uri(TEST_URI),
-                              bh.DEFAULT_INDEX, "test");
-
-  // Ensure it was added
-  PlacesBackground.dispatch(new_test_bookmark_uri_event(id1, TEST_URI, true),
-                            Ci.nsIEventTarget.DISPATCH_SYNC);
+  // Set the preference for the timer to a really large value, so it won't
+  // run before the test finishes.
+  prefs.setIntPref(kSyncPrefName, SYNC_INTERVAL);
 
-  // Get the place_id
-  let stmt = db.createStatement(
-    "SELECT fk " +
-    "FROM moz_bookmarks " +
-    "WHERE id = ?"
-  );
-  stmt.bindInt64Parameter(0, id1);
-  do_check_true(stmt.executeStep());
-  let place_id = stmt.getInt64(0);
-  stmt.finalize();
-  stmt = null;
+  // Add the first bookmark
+  let id1 = bs.insertBookmark(bs.unfiledBookmarksFolder, uri(TEST_URI),
+                              bs.DEFAULT_INDEX, "test");
 
   // Now we add another bookmark to a different folder
-  let id2 = bh.insertBookmark(bh.toolbarFolder, uri(TEST_URI),
-                              bh.DEFAULT_INDEX, "test");
+  let id2 = bs.insertBookmark(bs.toolbarFolder, uri(TEST_URI),
+                              bs.DEFAULT_INDEX, "test");
   do_check_neq(id1, id2);
 
-  // Ensure it was added
-  PlacesBackground.dispatch(new_test_bookmark_uri_event(id2, TEST_URI, true),
-                            Ci.nsIEventTarget.DISPATCH_SYNC);
-
-  // Check to make sure we have the same place_id
-  stmt = db.createStatement(
-    "SELECT * " +
-    "FROM moz_bookmarks " +
-    "WHERE id = ?1 " +
-    "AND fk = ?2"
-  );
-  stmt.bindInt64Parameter(0, id2);
-  stmt.bindInt64Parameter(1, place_id);
-  do_check_true(stmt.executeStep());
-  stmt.finalize();
-  stmt = null;
-
-  // finish_test() calls do_test_finished, so we call do_test_pending()...
   do_test_pending();
-  finish_test();
 }
rename from toolkit/components/places/tests/background/test_multiple_visits_around_sync.js
rename to toolkit/components/places/tests/sync/test_multiple_visits_around_sync.js
--- a/toolkit/components/places/tests/background/test_multiple_visits_around_sync.js
+++ b/toolkit/components/places/tests/sync/test_multiple_visits_around_sync.js
@@ -17,16 +17,17 @@
  *
  * The Initial Developer of the Original Code is
  * Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2008
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *  Marco Bonardo <mak77@bonardo.net>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -38,44 +39,46 @@
  * ***** END LICENSE BLOCK ***** */
 
 /**
  * This test ensures that when adding a visit, then syncing, and adding another
  * visit to the same url creates two visits and that we only end up with one
  * entry in moz_places.
  */
 
-Components.utils.import("resource://gre/modules/PlacesBackground.jsm");
+var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+         getService(Ci.nsINavHistoryService);
+var db = Cc["@mozilla.org/browser/nav-history-service;1"].
+         getService(Ci.nsPIPlacesDatabase).
+         DBConnection;
+var prefs = Cc["@mozilla.org/preferences-service;1"].
+            getService(Ci.nsIPrefService).
+            getBranch("places.");
 
 const TEST_URI = "http://test.com/";
+
 const kSyncPrefName = "syncDBTableIntervalInSecs";
 const SYNC_INTERVAL = 1;
 
 function run_test()
 {
   // First set the preference for the timer to a small value
-  let prefs = Cc["@mozilla.org/preferences-service;1"].
-              getService(Ci.nsIPrefService).
-              getBranch("places.");
   prefs.setIntPref(kSyncPrefName, SYNC_INTERVAL);
 
   // Now add the first visit
-  let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
-           getService(Ci.nsINavHistoryService);
   let id = hs.addVisit(uri(TEST_URI), Date.now() * 1000, null,
                        hs.TRANSITION_TYPED, false, 0);
 
   // Check the visit, but after enough time has passed for the DB flush service
   // to have fired it's timer.
   let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   timer.initWithCallback({
     notify: function(aTimer)
     {
-      PlacesBackground.dispatch(new_test_visit_uri_event(id, TEST_URI, true),
-                                Ci.nsIEventTarget.DISPATCH_SYNC);
+      new_test_visit_uri_event(id, TEST_URI, true);
 
       // Get the place_id and pass it on
       let db = hs.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
       let stmt = db.createStatement(
         "SELECT place_id " +
         "FROM moz_historyvisits " +
         "WHERE id = ?"
       );
@@ -87,30 +90,27 @@ function run_test()
     }
   }, (SYNC_INTERVAL * 1000) * 2, Ci.nsITimer.TYPE_ONE_SHOT);
   do_test_pending();
 }
 
 function continue_test(aLastVisitId, aPlaceId)
 {
   // Now we add another visit
-  let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
-           getService(Ci.nsINavHistoryService);
   let id = hs.addVisit(uri(TEST_URI), Date.now() * 1000, null,
                        hs.TRANSITION_TYPED, false, 0);
   do_check_neq(aLastVisitId, id);
 
   // Check the visit, but after enough time has passed for the DB flush service
   // to have fired it's timer.
   let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   timer.initWithCallback({
     notify: function(aTimer)
     {
-      PlacesBackground.dispatch(new_test_visit_uri_event(id, TEST_URI, true),
-                                Ci.nsIEventTarget.DISPATCH_SYNC);
+      new_test_visit_uri_event(id, TEST_URI, true);
 
       // Check to make sure we have the same place_id
       let db = hs.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
       let stmt = db.createStatement(
         "SELECT * " +
         "FROM moz_historyvisits " +
         "WHERE id = ?1 " +
         "AND place_id = ?2"