Bug 482911 - Reimplement Netscape bookmark import using the HTML5 parser. r=mak, sr=gavin.
☠☠ backed out by e793817bfbee ☠ ☠
authorHenri Sivonen <hsivonen@iki.fi>
Fri, 23 Mar 2012 09:15:36 +0200
changeset 90140 d2693e86769dfdfe5601b39161aab9b35353833f
parent 90116 ea55576a4a1e963f43f87ba5f416752ddaaee785
child 90141 fd2c598e7fde575a0d7a29b1b9b6bca917ef0120
child 90142 e793817bfbee1462fbab01fe458351bc35859bbe
push id22323
push userbmo@edmorley.co.uk
push dateSat, 24 Mar 2012 16:06:07 +0000
treeherdermozilla-central@20a01901480f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak, gavin
bugs482911
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 482911 - Reimplement Netscape bookmark import using the HTML5 parser. r=mak, sr=gavin.
browser/components/migration/src/nsBrowserProfileMigratorUtils.cpp
browser/components/migration/src/nsBrowserProfileMigratorUtils.h
browser/components/migration/src/nsIEProfileMigrator.cpp
browser/components/migration/src/nsSafariProfileMigrator.cpp
browser/components/nsBrowserGlue.js
browser/components/places/content/places.js
browser/components/places/tests/unit/test_384370.js
browser/components/places/tests/unit/test_457441-import-export-corrupt-bookmarks-html.js
browser/components/places/tests/unit/test_bookmarksRestoreNotification.js
browser/components/places/tests/unit/test_bookmarks_html.js
browser/components/places/tests/unit/test_browserGlue_migrate.js
browser/components/places/tests/unit/test_browserGlue_prefs.js
browser/components/places/tests/unit/test_browserGlue_smartBookmarks.js
toolkit/components/places/BookmarkHTMLUtils.jsm
toolkit/components/places/Helpers.cpp
toolkit/components/places/Helpers.h
toolkit/components/places/Makefile.in
toolkit/components/places/PlacesUtils.jsm
toolkit/components/places/nsIPlacesImportExportService.idl
toolkit/components/places/nsPlacesExportService.cpp
toolkit/components/places/nsPlacesExportService.h
toolkit/components/places/nsPlacesImportExportService.cpp
toolkit/components/places/nsPlacesImportExportService.h
toolkit/components/places/nsPlacesModule.cpp
--- a/browser/components/migration/src/nsBrowserProfileMigratorUtils.cpp
+++ b/browser/components/migration/src/nsBrowserProfileMigratorUtils.cpp
@@ -158,25 +158,8 @@ GetProfilePath(nsIProfileStartup* aStart
       (do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
     if (dirSvc) {
       dirSvc->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
                   (void**) getter_AddRefs(aProfileDir));
     }
   }
 }
 
-nsresult
-ImportDefaultBookmarks()
-{
-  nsCOMPtr<nsIPlacesImportExportService> importer =
-    do_GetService(NS_PLACESIMPORTEXPORTSERVICE_CONTRACTID);
-  NS_ENSURE_STATE(importer);
-
-  nsCOMPtr<nsIIOService> ioService = mozilla::services::GetIOService();
-  NS_ENSURE_STATE(ioService);
-  nsCOMPtr<nsIURI> bookmarksURI;
-  nsresult rv = ioService->NewURI(DEFAULT_BOOKMARKS, nsnull, nsnull,
-                                  getter_AddRefs(bookmarksURI));
-  if (NS_FAILED(rv))
-    return rv;
-
-  return importer->ImportHTMLFromURI(bookmarksURI, true);
-}
--- a/browser/components/migration/src/nsBrowserProfileMigratorUtils.h
+++ b/browser/components/migration/src/nsBrowserProfileMigratorUtils.h
@@ -94,15 +94,10 @@ void GetMigrateDataFromArray(MigrationDa
                              nsIFile* aSourceProfile, 
                              PRUint16* aResult);
 
 
 // get the base directory of the *target* profile
 // this is already cloned, modify it to your heart's content
 void GetProfilePath(nsIProfileStartup* aStartup, nsCOMPtr<nsIFile>& aProfileDir);
 
-/**
- * Imports default bookmarks to the profile.
- */
-nsresult ImportDefaultBookmarks();
-
 #endif
 
--- a/browser/components/migration/src/nsIEProfileMigrator.cpp
+++ b/browser/components/migration/src/nsIEProfileMigrator.cpp
@@ -1393,21 +1393,16 @@ nsIEProfileMigrator::CopyFavoritesBatche
 
     rv = bms->CreateFolder(bookmarksMenuFolderId,
                            NS_ConvertUTF16toUTF8(importedIEFavsTitle),
                            nsINavBookmarksService::DEFAULT_INDEX,
                            &folder);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   else {
-    // If importing defaults fails for whatever reason, let the import process
-    // continue.
-    DebugOnly<nsresult> rv = ImportDefaultBookmarks();
-    MOZ_ASSERT(NS_SUCCEEDED(rv), "Should be able to import default bookmarks");
-
     // Locate the Links toolbar folder, we want to replace the Personal Toolbar
     // content with Favorites in this folder.
     // On versions minor or equal to IE6 the folder name is stored in the
     // LinksFolderName registry key, but in newer versions it may be just a
     // Links subfolder inside the default Favorites folder.
     nsCOMPtr<nsIWindowsRegKey> regKey =
       do_CreateInstance("@mozilla.org/windows-registry-key;1");
     if (regKey &&
--- a/browser/components/migration/src/nsSafariProfileMigrator.cpp
+++ b/browser/components/migration/src/nsSafariProfileMigrator.cpp
@@ -969,21 +969,16 @@ nsSafariProfileMigrator::CopyBookmarksBa
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = bms->CreateFolder(bookmarksMenuFolderId,
                            NS_ConvertUTF16toUTF8(importedSafariBookmarksTitle),
                            nsINavBookmarksService::DEFAULT_INDEX, &folder);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   else {
-    // If importing defaults fails for whatever reason, let the import process
-    // continue.
-    DebugOnly<nsresult> rv = ImportDefaultBookmarks();
-    MOZ_ASSERT(NS_SUCCEEDED(rv), "Should be able to import default bookmarks");
-
     // In replace mode we are merging at the top level.
     folder = bookmarksMenuFolderId;
   }
 
   nsCOMPtr<nsIProperties> fileLocator(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
   nsCOMPtr<nsILocalFile> safariBookmarksFile;
   fileLocator->Get(NS_MAC_USER_LIB_DIR, NS_GET_IID(nsILocalFile),
                    getter_AddRefs(safariBookmarksFile));
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -57,16 +57,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/AddonManager.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils",
+                                  "resource://gre/modules/BookmarkHTMLUtils.jsm");
+
 XPCOMUtils.defineLazyModuleGetter(this, "KeywordURLResetPrompter",
                                   "resource:///modules/KeywordURLResetPrompter.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "webappsUI", 
                                   "resource:///modules/webappsUI.jsm");
 
 const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
 const PREF_PLUGINS_UPDATEURL  = "plugins.update.url";
@@ -223,20 +226,16 @@ BrowserGlue.prototype = {
         break;
       case "places-init-complete":
         this._initPlaces();
         Services.obs.removeObserver(this, "places-init-complete");
         this._isPlacesInitObserver = false;
         // no longer needed, since history was initialized completely.
         Services.obs.removeObserver(this, "places-database-locked");
         this._isPlacesLockedObserver = false;
-
-        // Now apply distribution customized bookmarks.
-        // This should always run after Places initialization.
-        this._distributionCustomizer.applyBookmarks();
         break;
       case "places-database-locked":
         this._isPlacesDatabaseLocked = true;
         // Stop observing, so further attempts to load history service
         // will not show the prompt.
         Services.obs.removeObserver(this, "places-database-locked");
         this._isPlacesLockedObserver = false;
         break;
@@ -252,23 +251,16 @@ BrowserGlue.prototype = {
         if (this._idleService.idleTime > BOOKMARKS_BACKUP_IDLE_TIME * 1000)
           this._backupBookmarks();
         break;
       case "distribution-customization-complete":
         Services.obs.removeObserver(this, "distribution-customization-complete");
         // Customization has finished, we don't need the customizer anymore.
         delete this._distributionCustomizer;
         break;
-      case "bookmarks-restore-success":
-      case "bookmarks-restore-failed":
-        Services.obs.removeObserver(this, "bookmarks-restore-success");
-        Services.obs.removeObserver(this, "bookmarks-restore-failed");
-        if (topic == "bookmarks-restore-success" && data == "html-initial")
-          this.ensurePlacesDefaultQueriesInitialized();
-        break;
       case "browser-glue-test": // used by tests
         if (data == "post-update-notification") {
           if (Services.prefs.prefHasUserValue("app.update.postupdate"))
             this._showUpdateNotification();
         }
         else if (data == "force-ui-migration") {
           this._migrateUI();
         }
@@ -283,16 +275,19 @@ BrowserGlue.prototype = {
         break;
       case "defaultURIFixup-using-keyword-pref":
         if (KeywordURLResetPrompter.shouldPrompt) {
           let keywordURI = subject.QueryInterface(Ci.nsIURI);
           KeywordURLResetPrompter.prompt(this.getMostRecentBrowserWindow(),
                                          keywordURI);
         }
         break;
+      case "initial-migration":
+        this._initialMigrationPerformed = true;
+        break;
     }
   }, 
 
   // initialization (called on application startup) 
   _init: function BG__init() {
     let os = Services.obs;
     os.addObserver(this, "xpcom-shutdown", false);
     os.addObserver(this, "prefservice:after-app-defaults", false);
@@ -985,28 +980,19 @@ BrowserGlue.prototype = {
    */
   _initPlaces: function BG__initPlaces() {
     // We must instantiate the history service since it will tell us if we
     // need to import or restore bookmarks due to first-run, corruption or
     // forced migration (due to a major schema change).
     // If the database is corrupt or has been newly created we should
     // import bookmarks.
     var dbStatus = PlacesUtils.history.databaseStatus;
-    var importBookmarks = dbStatus == PlacesUtils.history.DATABASE_STATUS_CREATE ||
-                          dbStatus == PlacesUtils.history.DATABASE_STATUS_CORRUPT;
-
-    if (dbStatus == PlacesUtils.history.DATABASE_STATUS_CREATE) {
-      // If the database has just been created, but we already have any
-      // bookmark, this is not the initial import.  This can happen after a
-      // migration from a different browser since migrators run before us.
-      // In such a case we should not import, unless some pref has been set.
-      if (PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 0) != -1 ||
-          PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0) != -1)
-        importBookmarks = false;
-    }
+    var importBookmarks = !this._initialMigrationPerformed &&
+                          (dbStatus == PlacesUtils.history.DATABASE_STATUS_CREATE ||
+                           dbStatus == PlacesUtils.history.DATABASE_STATUS_CORRUPT);
 
     // Check if user or an extension has required to import bookmarks.html
     var importBookmarksHTML = false;
     try {
       importBookmarksHTML =
         Services.prefs.getBoolPref("browser.places.importBookmarksHTML");
       if (importBookmarksHTML)
         importBookmarks = true;
@@ -1053,16 +1039,19 @@ BrowserGlue.prototype = {
     }
 
     // If bookmarks are not imported, then initialize smart bookmarks.  This
     // happens during a common startup.
     // Otherwise, if any kind of import runs, smart bookmarks creation should be
     // delayed till the import operations has finished.  Not doing so would
     // cause them to be overwritten by the newly imported bookmarks.
     if (!importBookmarks) {
+      // Now apply distribution customized bookmarks.
+      // This should always run after Places initialization.
+      this._distributionCustomizer.applyBookmarks();
       this.ensurePlacesDefaultQueriesInitialized();
     }
     else {
       // An import operation is about to run.
       // Don't try to recreate smart bookmarks if autoExportHTML is true or
       // smart bookmarks are disabled.
       var autoExportHTML = false;
       try {
@@ -1086,35 +1075,38 @@ BrowserGlue.prototype = {
       }
       else {
         var bookmarksFile = dirService.get("BMarks", Ci.nsILocalFile);
         if (bookmarksFile.exists())
           bookmarksURI = NetUtil.newURI(bookmarksFile);
       }
 
       if (bookmarksURI) {
-        // Add an import observer.  It will ensure that smart bookmarks are
-        // created once the operation is complete.
-        Services.obs.addObserver(this, "bookmarks-restore-success", false);
-        Services.obs.addObserver(this, "bookmarks-restore-failed", false);
-
         // Import from bookmarks.html file.
         try {
-          var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
-                         getService(Ci.nsIPlacesImportExportService);
-          importer.importHTMLFromURI(bookmarksURI, true /* overwrite existing */);
+          BookmarkHTMLUtils.importFromURL(bookmarksURI.spec, true, (function (success) {
+            if (success) {
+              // Now apply distribution customized bookmarks.
+              // This should always run after Places initialization.
+              this._distributionCustomizer.applyBookmarks();
+              // Ensure that smart bookmarks are created once the operation is
+              // complete.
+              this.ensurePlacesDefaultQueriesInitialized();
+            }
+            else {
+              Cu.reportError("Bookmarks.html file could be corrupt.");
+            }
+          }).bind(this));
         } catch (err) {
-          // Report the error, but ignore it.
           Cu.reportError("Bookmarks.html file could be corrupt. " + err);
-          Services.obs.removeObserver(this, "bookmarks-restore-success");
-          Services.obs.removeObserver(this, "bookmarks-restore-failed");
         }
       }
-      else
+      else {
         Cu.reportError("Unable to find bookmarks.html file.");
+      }
 
       // Reset preferences, so we won't try to import again at next run
       if (importBookmarksHTML)
         Services.prefs.setBoolPref("browser.places.importBookmarksHTML", false);
       if (restoreDefaultBookmarks)
         Services.prefs.setBoolPref("browser.bookmarks.restore_default_bookmarks",
                                    false);
     }
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -395,20 +395,19 @@ var PlacesOrganizer = {
    */
   importFromFile: function PO_importFromFile() {
     var fp = Cc["@mozilla.org/filepicker;1"].
              createInstance(Ci.nsIFilePicker);
     fp.init(window, PlacesUIUtils.getString("SelectImport"),
             Ci.nsIFilePicker.modeOpen);
     fp.appendFilters(Ci.nsIFilePicker.filterHTML);
     if (fp.show() != Ci.nsIFilePicker.returnCancel) {
-      if (fp.file) {
-        var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
-                       getService(Ci.nsIPlacesImportExportService);
-        importer.importHTMLFromFile(fp.file, false);
+      if (fp.fileURL) {
+        Components.utils.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
+        BookmarkHTMLUtils.importFromURL(fp.fileURL.spec, false);
       }
     }
   },
 
   /**
    * Allows simple exporting of bookmarks.
    */
   exportBookmarks: function PO_exportBookmarks() {
--- a/browser/components/places/tests/unit/test_384370.js
+++ b/browser/components/places/tests/unit/test_384370.js
@@ -52,18 +52,18 @@ function run_test() {
     - export as html, import, test (tests integrity of json > html)
 
     BACKUP/RESTORE SUMMARY:
     - create a bookmark in each root
     - tag multiple URIs with multiple tags
     - export as json, import, test
   */
 
-  // get places import/export service
-  var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].getService(Ci.nsIPlacesImportExportService);
+  // import the importer
+  Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
 
   // avoid creating the places smart folder during tests
   Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch).
   setIntPref("browser.places.smartBookmarksVersion", -1);
 
   // file pointer to legacy bookmarks file
   //var bookmarksFileOld = do_get_file("bookmarks.large.html");
   var bookmarksFileOld = do_get_file("bookmarks.preplaces.html");
@@ -78,28 +78,36 @@ function run_test() {
   if (!jsonFile.exists())
     do_throw("couldn't create file: bookmarks.exported.json");
 
   // Test importing a pre-Places canonical bookmarks file.
   // 1. import bookmarks.preplaces.html
   // 2. run the test-suite
   // Note: we do not empty the db before this import to catch bugs like 380999
   try {
-    importer.importHTMLFromFile(bookmarksFileOld, true);
+    BookmarkHTMLUtils.importFromFile(bookmarksFileOld, true, after_import);
   } catch(ex) { do_throw("couldn't import legacy bookmarks file: " + ex); }
+}
+
+function after_import(success) {
+  if (!success) {
+    do_throw("Couldn't import legacy bookmarks file.");
+  }
   populate();
   validate();
 
   waitForAsyncUpdates(function () {
     // Test exporting a Places canonical json file.
     // 1. export to bookmarks.exported.json
     // 2. empty bookmarks db
     // 3. import bookmarks.exported.json
     // 4. run the test-suite
     try {
+      var jsonFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
+      jsonFile.append("bookmarks.exported.json");
       PlacesUtils.backups.saveBookmarksToJSONFile(jsonFile);
     } catch(ex) { do_throw("couldn't export to file: " + ex); }
     LOG("exported json");
     try {
       PlacesUtils.restoreBookmarksFromJSONFile(jsonFile);
     } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
     LOG("imported json");
     validate();
--- a/browser/components/places/tests/unit/test_457441-import-export-corrupt-bookmarks-html.js
+++ b/browser/components/places/tests/unit/test_457441-import-export-corrupt-bookmarks-html.js
@@ -50,16 +50,17 @@ var bs = Cc["@mozilla.org/browser/nav-bo
 var as = Cc["@mozilla.org/browser/annotation-service;1"].
          getService(Ci.nsIAnnotationService);
 var icos = Cc["@mozilla.org/browser/favicon-service;1"].
            getService(Ci.nsIFaviconService);
 var ps = Cc["@mozilla.org/preferences-service;1"].
          getService(Ci.nsIPrefBranch);
 var ies = Cc["@mozilla.org/browser/places/import-export-service;1"].
           getService(Ci.nsIPlacesImportExportService);
+Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
 
 const DESCRIPTION_ANNO = "bookmarkProperties/description";
 const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
 const POST_DATA_ANNO = "bookmarkProperties/POSTData";
 
 const TEST_FAVICON_PAGE_URL = "http://en-US.www.mozilla.com/en-US/firefox/central/";
 const TEST_FAVICON_DATA_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==";
 
@@ -67,18 +68,24 @@ function run_test() {
   do_test_pending();
 
   // avoid creating the places smart folder during tests
   ps.setIntPref("browser.places.smartBookmarksVersion", -1);
 
   // import bookmarks from corrupt file
   var corruptBookmarksFile = do_get_file("bookmarks.corrupt.html");
   try {
-    ies.importHTMLFromFile(corruptBookmarksFile, true);
+    BookmarkHTMLUtils.importFromFile(corruptBookmarksFile, true, after_import);
   } catch(ex) { do_throw("couldn't import corrupt bookmarks file: " + ex); }
+}
+
+function after_import(success) {
+  if (!success) {
+    do_throw("Couldn't import corrupt bookmarks file.");
+  }
 
   // Check that every bookmark is correct
   // Corrupt bookmarks should not have been imported
   database_check();
   waitForAsyncUpdates(function() {
     // Create corruption in database
     var corruptItemId = bs.insertBookmark(bs.toolbarFolder,
                                           uri("http://test.mozilla.org"),
@@ -100,24 +107,26 @@ function run_test() {
       ies.exportHTMLToFile(bookmarksFile);
     } catch(ex) { do_throw("couldn't export to bookmarks.exported.html: " + ex); }
 
     // Clear all bookmarks
     remove_all_bookmarks();
 
     // Import bookmarks
     try {
-      ies.importHTMLFromFile(bookmarksFile, true);
+      BookmarkHTMLUtils.importFromFile(bookmarksFile, true, before_database_check);
     } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
+  });
+}
 
+function before_database_check(success) {
     // Check that every bookmark is correct
     database_check();
 
     waitForAsyncUpdates(do_test_finished);
-  });
 }
 
 /*
  * Check for imported bookmarks correctness
  */
 function database_check() {
   // BOOKMARKS MENU
   var query = hs.getNewQuery();
--- a/browser/components/places/tests/unit/test_bookmarksRestoreNotification.js
+++ b/browser/components/places/tests/unit/test_bookmarksRestoreNotification.js
@@ -31,16 +31,18 @@
  * 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 ***** */
 
+Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
+
 /**
  * Tests the bookmarks-restore-* nsIObserver notifications after restoring
  * bookmarks from JSON and HTML.  See bug 470314.
  */
 
 // The topics and data passed to nsIObserver.observe() on bookmarks restore
 const NSIOBSERVER_TOPIC_BEGIN    = "bookmarks-restore-begin";
 const NSIOBSERVER_TOPIC_SUCCESS  = "bookmarks-restore-success";
@@ -129,37 +131,45 @@ var tests = [
     desc:       "HTML restore: normal restore should succeed",
     currTopic:  NSIOBSERVER_TOPIC_BEGIN,
     finalTopic: NSIOBSERVER_TOPIC_SUCCESS,
     data:       NSIOBSERVER_DATA_HTML,
     folderId:   null,
     run:        function () {
       this.file = createFile("bookmarks-test_restoreNotification.html");
       addBookmarks();
-      importer.exportHTMLToFile(this.file);
+      exporter.exportHTMLToFile(this.file);
       remove_all_bookmarks();
       try {
-        importer.importHTMLFromFile(this.file, false);
+        BookmarkHTMLUtils.importFromFile(this.file, false, function (success) {
+          if (!success) {
+            do_throw("  Restore should not have failed");
+          }
+        });
       }
       catch (e) {
         do_throw("  Restore should not have failed");
       }
     }
   },
 
   {
     desc:       "HTML restore: empty file should succeed",
     currTopic:  NSIOBSERVER_TOPIC_BEGIN,
     finalTopic: NSIOBSERVER_TOPIC_SUCCESS,
     data:       NSIOBSERVER_DATA_HTML,
     folderId:   null,
     run:        function () {
       this.file = createFile("bookmarks-test_restoreNotification.init.html");
       try {
-        importer.importHTMLFromFile(this.file, false);
+        BookmarkHTMLUtils.importFromFile(this.file, false, function (success) {
+          if (!success) {
+            do_throw("  Restore should not have failed");            
+          }
+        });
       }
       catch (e) {
         do_throw("  Restore should not have failed");
       }
     }
   },
 
   {
@@ -167,53 +177,65 @@ var tests = [
     currTopic:  NSIOBSERVER_TOPIC_BEGIN,
     finalTopic: NSIOBSERVER_TOPIC_FAILED,
     data:       NSIOBSERVER_DATA_HTML,
     folderId:   null,
     run:        function () {
       this.file = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
       this.file.append("this file doesn't exist because nobody created it");
       try {
-        importer.importHTMLFromFile(this.file, false);
-        do_throw("  Restore should have failed");
+        BookmarkHTMLUtils.importFromFile(this.file, false, function (success) {
+          print("callback");
+          if (success) {
+            do_throw("  Restore should have failed");
+          }
+        });
       }
       catch (e) {}
     }
   },
 
   {
     desc:       "HTML initial restore: normal restore should succeed",
     currTopic:  NSIOBSERVER_TOPIC_BEGIN,
     finalTopic: NSIOBSERVER_TOPIC_SUCCESS,
     data:       NSIOBSERVER_DATA_HTML_INIT,
     folderId:   null,
     run:        function () {
       this.file = createFile("bookmarks-test_restoreNotification.init.html");
       addBookmarks();
-      importer.exportHTMLToFile(this.file);
+      exporter.exportHTMLToFile(this.file);
       remove_all_bookmarks();
       try {
-        importer.importHTMLFromFile(this.file, true);
+        BookmarkHTMLUtils.importFromFile(this.file, true, function (success) {
+          if (!success) {
+            do_throw("  Restore should not have failed");
+          }
+        });
       }
       catch (e) {
         do_throw("  Restore should not have failed");
       }
     }
   },
 
   {
     desc:       "HTML initial restore: empty file should succeed",
     currTopic:  NSIOBSERVER_TOPIC_BEGIN,
     finalTopic: NSIOBSERVER_TOPIC_SUCCESS,
     data:       NSIOBSERVER_DATA_HTML_INIT,
     folderId:   null,
     run:        function () {
       this.file = createFile("bookmarks-test_restoreNotification.init.html");
       try {
-        importer.importHTMLFromFile(this.file, true);
+        BookmarkHTMLUtils.importFromFile(this.file, true, function (success) {
+          if (!success) {
+            do_throw("  Restore should not have failed");
+          }
+        });
       }
       catch (e) {
         do_throw("  Restore should not have failed");
       }
     }
   },
 
   {
@@ -221,23 +243,25 @@ var tests = [
     currTopic:  NSIOBSERVER_TOPIC_BEGIN,
     finalTopic: NSIOBSERVER_TOPIC_FAILED,
     data:       NSIOBSERVER_DATA_HTML_INIT,
     folderId:   null,
     run:        function () {
       this.file = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
       this.file.append("this file doesn't exist because nobody created it");
       try {
-        importer.importHTMLFromFile(this.file, true);
-        do_throw("  Restore should have failed");
+        BookmarkHTMLUtils.importFromFile(this.file, true, function (success) {
+          if (success) {
+            do_throw("  Restore should have failed");
+          }
+        });
       }
       catch (e) {}
     }
   }
-
 ];
 
 // nsIObserver that observes bookmarks-restore-begin.
 var beginObserver = {
   observe: function _beginObserver(aSubject, aTopic, aData) {
     var test = tests[currTestIndex];
 
     print("  Observed " + aTopic);
@@ -276,30 +300,30 @@ var successAndFailedObserver = {
     if (aSubject) {
       do_check_eq(aSubject.QueryInterface(Ci.nsISupportsPRInt64).data,
                   test.folderId);
     }
     else
       do_check_eq(test.folderId, null);
 
     remove_all_bookmarks();
-    doNextTest();
+    do_execute_soon(doNextTest);
   }
 };
 
 // Index of the currently running test.  See doNextTest().
 var currTestIndex = -1;
 
 var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
             getService(Ci.nsINavBookmarksService);
 
 var obssvc = Cc["@mozilla.org/observer-service;1"].
              getService(Ci.nsIObserverService);
 
-var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
+var exporter = Cc["@mozilla.org/browser/places/import-export-service;1"].
                getService(Ci.nsIPlacesImportExportService);
 
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
  * Adds some bookmarks for the URIs in |uris|.
  */
 function addBookmarks() {
--- a/browser/components/places/tests/unit/test_bookmarks_html.js
+++ b/browser/components/places/tests/unit/test_bookmarks_html.js
@@ -94,19 +94,21 @@ let test_bookmarks = {
   ]
 };
 
 // Pre-Places bookmarks.html file pointer.
 let gBookmarksFileOld;
 // Places bookmarks.html file pointer.
 let gBookmarksFileNew;
 
-let importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
+let exporter = Cc["@mozilla.org/browser/places/import-export-service;1"].
                getService(Ci.nsIPlacesImportExportService);
 
+Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
+
 function run_test()
 {
   run_next_test();
 }
 
 add_test(function setup() {
   // Avoid creating smart bookmarks during the test.
   Services.prefs.setIntPref("browser.places.smartBookmarksVersion", -1);
@@ -126,131 +128,157 @@ add_test(function setup() {
   }
 
   // This test must be the first one, since it setups the new bookmarks.html.
   // Test importing a pre-Places canonical bookmarks file.
   // 1. import bookmarks.preplaces.html
   // 2. run the test-suite
   // Note: we do not empty the db before this import to catch bugs like 380999
   try {
-    importer.importHTMLFromFile(gBookmarksFileOld, true);
-  } catch(ex) { do_throw("couldn't import legacy bookmarks file: " + ex); }
+    BookmarkHTMLUtils.importFromFile(gBookmarksFileOld, true, function(success) {
+      if (success) {
+        waitForAsyncUpdates(function () {
+          testImportedBookmarks();
 
-  waitForAsyncUpdates(function () {
-    testImportedBookmarks();
+          // Prepare for next tests.
+          try {
+            exporter.exportHTMLToFile(gBookmarksFileNew);
+          } catch(ex) { do_throw("couldn't export to file: " + ex); }
 
-    // Prepare for next tests.
-    try {
-      importer.exportHTMLToFile(gBookmarksFileNew);
-    } catch(ex) { do_throw("couldn't export to file: " + ex); }
-
-    waitForAsyncUpdates(function () {
-      remove_all_bookmarks();
-      run_next_test();
+          waitForAsyncUpdates(function () {
+            remove_all_bookmarks();
+            run_next_test();
+          });
+        });
+      } else {
+        do_throw("couldn't import legacy bookmarks file.");
+      }
     });
-  });
+  } catch(ex) { do_throw("couldn't import legacy bookmarks file: " + ex); }
 });
 
 add_test(function test_import_new()
 {
   // Test importing a Places bookmarks.html file.
   // 1. import bookmarks.exported.html
   // 2. run the test-suite
 
   try {
-    importer.importHTMLFromFile(gBookmarksFileNew, true);
-  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
-
-  waitForAsyncUpdates(function () {
-    testImportedBookmarks();
+    BookmarkHTMLUtils.importFromFile(gBookmarksFileNew, true, function(success) {
+      if (success) {
+        waitForAsyncUpdates(function () {
+          testImportedBookmarks();
 
-    waitForAsyncUpdates(function () {
-      remove_all_bookmarks();
-      run_next_test();
+          waitForAsyncUpdates(function () {
+            remove_all_bookmarks();
+            run_next_test();
+          });
+        });
+      } else {
+        do_throw("couldn't import the exported file.");
+      }
     });
-  });
+  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
 });
 
 add_test(function test_emptytitle_export()
 {
   // Test exporting and importing with an empty-titled bookmark.
   // 1. import bookmarks
   // 1. create an empty-titled bookmark.
   // 2. export to bookmarks.exported.html
   // 3. empty bookmarks db
   // 4. import bookmarks.exported.html
   // 5. run the test-suite
 
   try {
-    importer.importHTMLFromFile(gBookmarksFileNew, true);
-  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
+    BookmarkHTMLUtils.importFromFile(gBookmarksFileNew, true, function(success) {
+      if (success) {
+        const NOTITLE_URL = "http://notitle.mozilla.org/";
+        let id = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
+                                                      NetUtil.newURI(NOTITLE_URL),
+                                                      PlacesUtils.bookmarks.DEFAULT_INDEX,
+                                                      "");
+        test_bookmarks.unfiled.push({ title: "", url: NOTITLE_URL });
 
-  const NOTITLE_URL = "http://notitle.mozilla.org/";
-  let id = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
-                                                NetUtil.newURI(NOTITLE_URL),
-                                                PlacesUtils.bookmarks.DEFAULT_INDEX,
-                                                "");
-  test_bookmarks.unfiled.push({ title: "", url: NOTITLE_URL });
+        try {
+          exporter.exportHTMLToFile(gBookmarksFileNew);
+        } catch(ex) { do_throw("couldn't export to file: " + ex); }
+
+        remove_all_bookmarks();
 
-  try {
-    importer.exportHTMLToFile(gBookmarksFileNew);
-  } catch(ex) { do_throw("couldn't export to file: " + ex); }
-
-  remove_all_bookmarks();
+        try {
+          BookmarkHTMLUtils.importFromFile(gBookmarksFileNew, true, function(success) {
+           if (success) {
+              waitForAsyncUpdates(function () {
+                testImportedBookmarks();
 
-  try {
-    importer.importHTMLFromFile(gBookmarksFileNew, true);
-  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
+                // Cleanup.
+                test_bookmarks.unfiled.pop();
+                PlacesUtils.bookmarks.removeItem(id);
 
-  waitForAsyncUpdates(function () {
-    testImportedBookmarks();
+                try {
+                  exporter.exportHTMLToFile(gBookmarksFileNew);
+                } catch(ex) { do_throw("couldn't export to file: " + ex); }
 
-    // Cleanup.
-    test_bookmarks.unfiled.pop();
-    PlacesUtils.bookmarks.removeItem(id);
-
-    try {
-      importer.exportHTMLToFile(gBookmarksFileNew);
-    } catch(ex) { do_throw("couldn't export to file: " + ex); }
-
-    waitForAsyncUpdates(function () {
-      remove_all_bookmarks();
-      run_next_test();
+                waitForAsyncUpdates(function () {
+                  remove_all_bookmarks();
+                  run_next_test();
+                });
+              });
+            } else {
+              do_throw("couldn't import the exported file.");
+            }
+          });
+        } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
+      } else {
+        do_throw("couldn't import the exported file.");
+      }
     });
-  });
+  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
 });
 
 add_test(function test_import_ontop()
 {
   // Test importing the exported bookmarks.html file *on top of* the existing
   // bookmarks.
   // 1. empty bookmarks db
   // 2. import the exported bookmarks file
   // 3. export to file
   // 3. import the exported bookmarks file
   // 4. run the test-suite
 
   try {
-    importer.importHTMLFromFile(gBookmarksFileNew, true);
-  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
-  try {
-    importer.exportHTMLToFile(gBookmarksFileNew);
-  } catch(ex) { do_throw("couldn't export to file: " + ex); }
-  try {
-    importer.importHTMLFromFile(gBookmarksFileNew, true);
-  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
+    BookmarkHTMLUtils.importFromFile(gBookmarksFileNew, true, function(success) {
+      if (success) {
+        try {
+          exporter.exportHTMLToFile(gBookmarksFileNew);
+        } catch(ex) { do_throw("couldn't export to file: " + ex); }
+        try {
+          BookmarkHTMLUtils.importFromFile(gBookmarksFileNew, true, function(success) {
+            if (success) {
+              waitForAsyncUpdates(function () {
+                testImportedBookmarks();
 
-  waitForAsyncUpdates(function () {
-    testImportedBookmarks();
+                waitForAsyncUpdates(function () {
+                  remove_all_bookmarks();
+                  run_next_test();
+                });
+              });
+            } else {
+              do_throw("couldn't import the exported file.");
+            }
+          });
+        } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
 
-    waitForAsyncUpdates(function () {
-      remove_all_bookmarks();
-      run_next_test();
+      } else {
+        do_throw("couldn't import the exported file.");
+      }
     });
-  });
+  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
 });
 
 function testImportedBookmarks()
 {
   for (let group in test_bookmarks) {
     do_print("[testImportedBookmarks()] Checking group '" + group + "'");
 
     let root;
--- a/browser/components/places/tests/unit/test_browserGlue_migrate.js
+++ b/browser/components/places/tests/unit/test_browserGlue_migrate.js
@@ -24,24 +24,25 @@ function run_test() {
     do_check_false(db.exists());
   }
 
   // Initialize Places through the History Service and check that a new
   // database has been created.
   do_check_eq(PlacesUtils.history.databaseStatus,
               PlacesUtils.history.DATABASE_STATUS_CREATE);
 
-  // A migrator would run before nsBrowserGlue, so we mimic that behavior
-  // adding a bookmark.
+  //A migrator would run before nsBrowserGlue Places initialization, so mimic
+  //that behavior adding a bookmark and notifying the migration.
   PlacesUtils.bookmarks.insertBookmark(PlacesUtils.bookmarks.bookmarksMenuFolder, uri("http://mozilla.org/"),
                     PlacesUtils.bookmarks.DEFAULT_INDEX, "migrated");
 
   // Initialize nsBrowserGlue.
   let bg = Cc["@mozilla.org/browser/browserglue;1"].
-           getService(Ci.nsIBrowserGlue);
+           getService(Ci.nsIObserver);
+  bg.observe(null, "initial-migration", null)
 
   let bookmarksObserver = {
     onBeginUpdateBatch: function() {},
     onEndUpdateBatch: function() {
       // Check if the currently finished batch created the smart bookmarks.
       let itemId =
         PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
       do_check_neq(itemId, -1);
--- a/browser/components/places/tests/unit/test_browserGlue_prefs.js
+++ b/browser/components/places/tests/unit/test_browserGlue_prefs.js
@@ -12,16 +12,26 @@ const PREF_SMART_BOOKMARKS_VERSION = "br
 const PREF_AUTO_EXPORT_HTML = "browser.bookmarks.autoExportHTML";
 
 const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
 const TOPICDATA_FORCE_PLACES_INIT = "force-places-init";
 
 let bg = Cc["@mozilla.org/browser/browserglue;1"].
          getService(Ci.nsIBrowserGlue);
 
+function waitForImportAndSmartBookmarks(aCallback) {
+  Services.obs.addObserver(function waitImport() {
+    Services.obs.removeObserver(waitImport, "bookmarks-restore-success");
+    // Delay to test eventual smart bookmarks creation.
+    do_execute_soon(function () {
+      waitForAsyncUpdates(aCallback);
+    });
+  }, "bookmarks-restore-success", false);
+}
+
 let gTests = [
 
   // This test must be the first one.
   function test_checkPreferences() {
     // Initialize Places through the History Service and check that a new
     // database has been created.
     do_check_eq(PlacesUtils.history.databaseStatus,
                 PlacesUtils.history.DATABASE_STATUS_CREATE);
@@ -61,29 +71,30 @@ let gTests = [
       PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
     do_check_eq(itemId, -1);
 
     // Set preferences.
     Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
 
     // Force nsBrowserGlue::_initPlaces().
     print("Simulate Places init");
+    waitForImportAndSmartBookmarks(function () {
+      // Check bookmarks.html has been imported, and a smart bookmark has been
+      // created.
+      itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
+                                                    SMART_BOOKMARKS_ON_TOOLBAR);
+      do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
+      // Check preferences have been reverted.
+      do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+
+      run_next_test();
+    });
     bg.QueryInterface(Ci.nsIObserver).observe(null,
                                               TOPIC_BROWSERGLUE_TEST,
                                               TOPICDATA_FORCE_PLACES_INIT);
-
-    // Check bookmarks.html has been imported, and a smart bookmark has been
-    // created.
-    itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
-                                                  SMART_BOOKMARKS_ON_TOOLBAR);
-    do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
-    // Check preferences have been reverted.
-    do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
-
-    run_next_test();
   },
 
   function test_import_noSmartBookmarks()
   {
     do_log_info("import from bookmarks.html, but don't create smart bookmarks \
                  if they are disabled");
 
     remove_all_bookmarks();
@@ -93,29 +104,30 @@ let gTests = [
     do_check_eq(itemId, -1);
 
     // Set preferences.
     Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, -1);
     Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
 
     // Force nsBrowserGlue::_initPlaces().
     print("Simulate Places init");
+    waitForImportAndSmartBookmarks(function () {
+      // Check bookmarks.html has been imported, but smart bookmarks have not
+      // been created.
+      itemId =
+        PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
+      do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
+      // Check preferences have been reverted.
+      do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+
+      run_next_test();
+    });
     bg.QueryInterface(Ci.nsIObserver).observe(null,
                                               TOPIC_BROWSERGLUE_TEST,
                                               TOPICDATA_FORCE_PLACES_INIT);
-
-    // Check bookmarks.html has been imported, but smart bookmarks have not
-    // been created.
-    itemId =
-      PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
-    do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
-    // Check preferences have been reverted.
-    do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
-
-    run_next_test();
   },
 
   function test_import_autoExport_updatedSmartBookmarks()
   {
     do_log_info("Import from bookmarks.html, but don't create smart bookmarks \
                  if autoExportHTML is true and they are at latest version");
 
     remove_all_bookmarks();
@@ -126,30 +138,31 @@ let gTests = [
 
     // Set preferences.
     Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 999);
     Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, true);
     Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
 
     // Force nsBrowserGlue::_initPlaces()
     print("Simulate Places init");
+    waitForImportAndSmartBookmarks(function () {
+      // Check bookmarks.html has been imported, but smart bookmarks have not
+      // been created.
+      itemId =
+        PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
+      do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
+      do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+      // Check preferences have been reverted.
+      Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
+
+      run_next_test();
+    });
     bg.QueryInterface(Ci.nsIObserver).observe(null,
                                               TOPIC_BROWSERGLUE_TEST,
                                               TOPICDATA_FORCE_PLACES_INIT);
-
-    // Check bookmarks.html has been imported, but smart bookmarks have not
-    // been created.
-    itemId =
-      PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
-    do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
-    do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
-    // Check preferences have been reverted.
-    Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
-
-    run_next_test();
   },
 
   function test_import_autoExport_oldSmartBookmarks()
   {
     do_log_info("Import from bookmarks.html, and create smart bookmarks if \
                  autoExportHTML is true and they are not at latest version.");
 
     remove_all_bookmarks();
@@ -160,31 +173,32 @@ let gTests = [
 
     // Set preferences.
     Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 0);
     Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, true);
     Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
 
     // Force nsBrowserGlue::_initPlaces()
     print("Simulate Places init");
+    waitForImportAndSmartBookmarks(function () {
+      // Check bookmarks.html has been imported, but smart bookmarks have not
+      // been created.
+      itemId =
+        PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
+                                             SMART_BOOKMARKS_ON_TOOLBAR);
+      do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
+      do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+      // Check preferences have been reverted.
+      Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
+
+      run_next_test();
+    });
     bg.QueryInterface(Ci.nsIObserver).observe(null,
                                               TOPIC_BROWSERGLUE_TEST,
                                               TOPICDATA_FORCE_PLACES_INIT);
-
-    // Check bookmarks.html has been imported, but smart bookmarks have not
-    // been created.
-    itemId =
-      PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
-                                           SMART_BOOKMARKS_ON_TOOLBAR);
-    do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
-    do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
-    // Check preferences have been reverted.
-    Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
-
-    run_next_test();
   },
 
   function test_restore()
   {
     do_log_info("restore from default bookmarks.html if \
                  restore_default_bookmarks is true.");
 
     remove_all_bookmarks();
@@ -193,29 +207,31 @@ let gTests = [
       PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
     do_check_eq(itemId, -1);
 
     // Set preferences.
     Services.prefs.setBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS, true);
 
     // Force nsBrowserGlue::_initPlaces()
     print("Simulate Places init");
+    waitForImportAndSmartBookmarks(function () {
+      // Check bookmarks.html has been restored.
+      itemId =
+        PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
+                                             SMART_BOOKMARKS_ON_TOOLBAR);
+      do_check_true(itemId > 0);
+      // Check preferences have been reverted.
+      do_check_false(Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
+
+      run_next_test();
+    });
     bg.QueryInterface(Ci.nsIObserver).observe(null,
                                               TOPIC_BROWSERGLUE_TEST,
                                               TOPICDATA_FORCE_PLACES_INIT);
 
-    // Check bookmarks.html has been restored.
-    itemId =
-      PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
-                                           SMART_BOOKMARKS_ON_TOOLBAR);
-    do_check_true(itemId > 0);
-    // Check preferences have been reverted.
-    do_check_false(Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
-
-    run_next_test();
   },
 
   function test_restore_import()
   {
     do_log_info("setting both importBookmarksHTML and \
                  restore_default_bookmarks should restore defaults.");
 
     remove_all_bookmarks();
@@ -225,30 +241,31 @@ let gTests = [
     do_check_eq(itemId, -1);
 
     // Set preferences.
     Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
     Services.prefs.setBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS, true);
 
     // Force nsBrowserGlue::_initPlaces()
     print("Simulate Places init");
+    waitForImportAndSmartBookmarks(function () {
+      // Check bookmarks.html has been restored.
+      itemId =
+        PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
+                                             SMART_BOOKMARKS_ON_TOOLBAR);
+      do_check_true(itemId > 0);
+      // Check preferences have been reverted.
+      do_check_false(Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
+      do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+
+      run_next_test();
+    });
     bg.QueryInterface(Ci.nsIObserver).observe(null,
                                               TOPIC_BROWSERGLUE_TEST,
                                               TOPICDATA_FORCE_PLACES_INIT);
-
-    // Check bookmarks.html has been restored.
-    itemId =
-      PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
-                                           SMART_BOOKMARKS_ON_TOOLBAR);
-    do_check_true(itemId > 0);
-    // Check preferences have been reverted.
-    do_check_false(Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
-    do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
-
-    run_next_test();
   }
 
 ];
 
 do_register_cleanup(function () {
   remove_all_bookmarks();
   remove_bookmarks_html();
   remove_all_JSON_backups();
--- a/browser/components/places/tests/unit/test_browserGlue_smartBookmarks.js
+++ b/browser/components/places/tests/unit/test_browserGlue_smartBookmarks.js
@@ -359,11 +359,20 @@ function run_test() {
   do_check_false(Services.prefs.getBoolPref(PREF_AUTO_EXPORT_HTML));
   do_check_false(Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
   try {
     do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
     do_throw("importBookmarksHTML pref should not exist");
   }
   catch(ex) {}
 
-  // Kick-off tests.
-  next_test();
+  waitForImportAndSmartBookmarks(next_test);
 }
+
+function waitForImportAndSmartBookmarks(aCallback) {
+  Services.obs.addObserver(function waitImport() {
+    Services.obs.removeObserver(waitImport, "bookmarks-restore-success");
+    // Delay to test eventual smart bookmarks creation.
+    do_execute_soon(function () {
+      waitForAsyncUpdates(aCallback);
+    });
+  }, "bookmarks-restore-success", false);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/BookmarkHTMLUtils.jsm
@@ -0,0 +1,775 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+const EXPORTED_SYMBOLS = [ "BookmarkHTMLUtils" ];
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/PlacesUtils.jsm");
+
+const Container_Normal = 0;
+const Container_Toolbar = 1;
+const Container_Menu = 2;
+const Container_Unfiled = 3;
+const Container_Places = 4;
+
+const RESTORE_BEGIN_NSIOBSERVER_TOPIC = "bookmarks-restore-begin";
+const RESTORE_SUCCESS_NSIOBSERVER_TOPIC = "bookmarks-restore-success";
+const RESTORE_FAILED_NSIOBSERVER_TOPIC = "bookmarks-restore-failed";
+const RESTORE_NSIOBSERVER_DATA = "html";
+const RESTORE_INITIAL_NSIOBSERVER_DATA = "html-initial";
+
+const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
+const DESCRIPTION_ANNO = "bookmarkProperties/description";
+const POST_DATA_ANNO = "bookmarkProperties/POSTData";
+
+let serialNumber = 0; // for favicons
+
+let BookmarkHTMLUtils = Object.freeze({
+  importFromURL: function importFromFile(aUrlString,
+                                         aInitialImport,
+                                         aCallback) {
+    let importer = new BookmarkImporter(aInitialImport);
+    importer.importFromURL(aUrlString, aCallback);
+  },
+
+  importFromFile: function importFromFile(aLocalFile,
+                                          aInitialImport,
+                                          aCallback) {
+    let importer = new BookmarkImporter(aInitialImport);
+    importer.importFromFile(aLocalFile, aCallback);
+  },
+});
+
+function Frame(aFrameId) {
+  this.containerId = aFrameId;
+
+  /**
+   * How many <dl>s have been nested. Each frame/container should start
+   * with a heading, and is then followed by a <dl>, <ul>, or <menu>. When
+   * that list is complete, then it is the end of this container and we need
+   * to pop back up one level for new items. If we never get an open tag for
+   * one of these things, we should assume that the container is empty and
+   * that things we find should be siblings of it. Normally, these <dl>s won't
+   * be nested so this will be 0 or 1.
+   */
+  this.containerNesting = 0;
+
+  /**
+   * when we find a heading tag, it actually affects the title of the NEXT
+   * container in the list. This stores that heading tag and whether it was
+   * special. 'consumeHeading' resets this._
+   */
+  this.lastContainerType = Container_Normal;
+
+  /**
+   * this contains the text from the last begin tag until now. It is reset
+   * at every begin tag. We can check it when we see a </a>, or </h3>
+   * to see what the text content of that node should be.
+   */
+  this.previousText = "";
+
+  /**
+   * true when we hit a <dd>, which contains the description for the preceding
+   * <a> tag. We can't just check for </dd> like we can for </a> or </h3>
+   * because if there is a sub-folder, it is actually a child of the <dd>
+   * because the tag is never explicitly closed. If this is true and we see a
+   * new open tag, that means to commit the description to the previous
+   * bookmark.
+   *
+   * Additional weirdness happens when the previous <dt> tag contains a <h3>:
+   * this means there is a new folder with the given description, and whose
+   * children are contained in the following <dl> list.
+   *
+   * This is handled in openContainer(), which commits previous text if
+   * necessary.
+   */
+  this.inDescription = false;
+
+  /**
+   * contains the URL of the previous bookmark created. This is used so that
+   * when we encounter a <dd>, we know what bookmark to associate the text with.
+   * This is cleared whenever we hit a <h3>, so that we know NOT to save this
+   * with a bookmark, but to keep it until 
+   */
+  this.previousLink = null; // nsIURI
+
+  /**
+   * contains the URL of the previous livemark, so that when the link ends,
+   * and the livemark title is known, we can create it.
+   */
+  this.previousFeed = null; // nsIURI
+
+  /**
+   * Contains the id of an imported, or newly created bookmark.
+   */
+  this.previousId = 0;
+
+  /**
+   * Contains the date-added and last-modified-date of an imported item.
+   * Used to override the values set by insertBookmark, createFolder, etc.
+   */
+  this.previousDateAdded = 0;
+  this.previousLastModifiedDate = 0;
+}
+
+function BookmarkImporter(aInitialImport) {
+  this._isImportDefaults = aInitialImport;
+  this._frames = new Array();
+  this._frames.push(new Frame(PlacesUtils.bookmarksMenuFolderId));
+}
+
+BookmarkImporter.prototype = {
+
+  _safeTrim: function safeTrim(aStr) {
+    return aStr ? aStr.trim() : aStr;
+  },
+
+  get _curFrame() {
+    return this._frames[this._frames.length - 1];
+  },
+
+  get _previousFrame() {
+    return this._frames[this._frames.length - 2];
+  },
+
+  /**
+   * This is called when there is a new folder found. The folder takes the
+   * name from the previous frame's heading.
+   */
+  _newFrame: function newFrame() {
+    let containerId = -1;
+    let frame = this._curFrame;
+    let containerTitle = frame.previousText;
+    frame.previousText = "";
+    let containerType = frame.lastContainerType;
+
+    switch (containerType) {
+      case Container_Normal:
+        // append a new folder
+        containerId = 
+          PlacesUtils.bookmarks.createFolder(frame.containerId,
+                                             containerTitle,
+                                             PlacesUtils.bookmarks.DEFAULT_INDEX);
+        break;
+      case Container_Places:
+        containerId = PlacesUtils.placesRootId;
+        break;
+      case Container_Menu:
+        containerId = PlacesUtils.bookmarksMenuFolderId;
+        break;
+      case Container_Unfiled:
+        containerId = PlacesUtils.unfiledBookmarksFolderId;
+        break;
+      case Container_Toolbar:
+        containerId = PlacesUtils.toolbarFolderId;
+        break;
+      default:
+        // NOT REACHED
+        throw new Error("Unreached");
+    }
+
+    if (frame.previousDateAdded > 0) {
+      try {
+        PlacesUtils.bookmarks.setItemDateAdded(containerId, frame.previousDateAdded);
+      } catch(e) {
+      }
+      frame.previousDateAdded = 0;
+    }
+    if (frame.previousLastModifiedDate > 0) {
+      try {
+        PlacesUtils.bookmarks.setItemLastModified(containerId, frame.previousLastModifiedDate);
+      } catch(e) {
+      }
+      // don't clear last-modified, in case there's a description
+    }
+
+    frame.previousId = containerId;
+
+    this._frames.push(new Frame(containerId));
+  },
+
+  /**
+   * Handles <H1>. We check for the attribute PLACES_ROOT and reset the
+   * container id if it's found. Otherwise, the default bookmark menu
+   * root is assumed and imported things will go into the bookmarks menu.
+   */
+  _handleHead1Begin: function handleHead1Begin(aElt) {
+    if (this._frames.length > 1) {
+      return;
+    }
+    if (aElt.hasAttribute("places_root")) {
+      this._curFrame.containerId = PlacesUtils.placesRootId;
+    }
+  },
+
+  /**
+   * Called for h2,h3,h4,h5,h6. This just stores the correct information in
+   * the current frame; the actual new frame corresponding to the container
+   * associated with the heading will be created when the tag has been closed
+   * and we know the title (we don't know to create a new folder or to merge
+   * with an existing one until we have the title).
+   */
+  _handleHeadBegin: function handleHeadBegin(aElt) {
+    let frame = this._curFrame;
+
+    // after a heading, a previous bookmark is not applicable (for example, for
+    // the descriptions contained in a <dd>). Neither is any previous head type
+    frame.previousLink = null;
+    frame.lastContainerType = Container_Normal;
+
+    // It is syntactically possible for a heading to appear after another heading
+    // but before the <dl> that encloses that folder's contents.  This should not
+    // happen in practice, as the file will contain "<dl></dl>" sequence for
+    // empty containers.
+    //
+    // Just to be on the safe side, if we encounter
+    //   <h3>FOO</h3>
+    //   <h3>BAR</h3>
+    //   <dl>...content 1...</dl>
+    //   <dl>...content 2...</dl>
+    // we'll pop the stack when we find the h3 for BAR, treating that as an
+    // implicit ending of the FOO container. The output will be FOO and BAR as
+    // siblings. If there's another <dl> following (as in "content 2"), those
+    // items will be treated as further siblings of FOO and BAR
+    if (frame.containerNesting == 0) {
+      this._frames.pop();
+    }
+
+    // We have to check for some attributes to see if this is a "special"
+    // folder, which will have different creation rules when the end tag is
+    // processed.
+    if (aElt.hasAttribute("personal_toolbar_folder")) {
+      if (this._isImportDefaults) {
+        frame.lastContainerType = Container_Toolbar;
+      }
+    } else if (aElt.hasAttribute("bookmarks_menu")) {
+      if (this._isImportDefaults) {
+        frame.lastContainerType = Container_Menu;
+      }
+    } else if (aElt.hasAttribute("unfiled_bookmarks_folder")) {
+      if (this._isImportDefaults) {
+        frame.lastContainerType = Container_Unfiled;
+      }
+    } else if (aElt.hasAttribute("places_root")) {
+      if (this._isImportDefaults) {
+        frame.lastContainerType = Container_Places;
+      }
+    } else {
+      let addDate = aElt.getAttribute("add_date");
+      if (addDate) {
+        frame.previousDateAdded =
+          this._convertImportedDateToInternalDate(addDate);
+      }
+      let modDate = aElt.getAttribute("last_modified");
+      if (modDate) {
+        frame.previousLastModifiedDate =
+          this._convertImportedDateToInternalDate(modDate);
+      }
+    }
+    this._curFrame.previousText = "";
+  },
+
+  /*
+   * Handles "<a" tags by creating a new bookmark. The title of the bookmark
+   * will be the text content, which will be stuffed in previousText for us
+   * and which will be saved by handleLinkEnd
+   */
+  _handleLinkBegin: function handleLinkBegin(aElt) {
+    let frame = this._curFrame;
+
+    // Make sure that the feed URIs from previous frames are emptied.
+    frame.previousFeed = null;
+    // Make sure that the bookmark id from previous frames are emptied.
+    frame.previousId = 0;
+    // mPreviousText will hold link text, clear it.
+    frame.previousText = "";
+
+    // Get the attributes we care about.
+    let href = this._safeTrim(aElt.getAttribute("href"));
+    let feedUrl = this._safeTrim(aElt.getAttribute("feedurl"));
+    let icon = this._safeTrim(aElt.getAttribute("icon"));
+    let iconUri = this._safeTrim(aElt.getAttribute("icon_uri"));
+    let lastCharset = this._safeTrim(aElt.getAttribute("last_charset"));
+    let keyword = this._safeTrim(aElt.getAttribute("shortcuturl"));
+    let postData = this._safeTrim(aElt.getAttribute("post_data"));
+    let webPanel = this._safeTrim(aElt.getAttribute("web_panel"));
+    let micsumGenURI = this._safeTrim(aElt.getAttribute("micsum_gen_uri"));
+    let generatedTitle = this._safeTrim(aElt.getAttribute("generated_title"));
+    let dateAdded = this._safeTrim(aElt.getAttribute("add_date"));
+    let lastModified = this._safeTrim(aElt.getAttribute("last_modified"));
+
+    // For feeds, get the feed URL.  If it is invalid, mPreviousFeed will be
+    // NULL and we'll create it as a normal bookmark.
+    if (feedUrl) {
+      frame.previousFeed = NetUtil.newURI(feedUrl);
+    }
+
+    // Ignore <a> tags that have no href.
+    if (href) {
+      // Save the address if it's valid.  Note that we ignore errors if this is a
+      // feed since href is optional for them.
+      try {
+        frame.previousLink = NetUtil.newURI(href);
+      } catch(e) {
+        if (!frame.previousFeed) {
+          frame.previousLink = null;
+          return;
+        }
+      }
+    } else {
+      frame.previousLink = null;
+      // The exception is for feeds, where the href is an optional component
+      // indicating the source web site.
+      if (!frame.previousFeed) {
+        return;
+      }
+    }
+
+    // Save bookmark's last modified date.
+    if (lastModified) {
+      frame.previousLastModifiedDate =
+        this._convertImportedDateToInternalDate(lastModified);
+    }
+
+    // If this is a live bookmark, we will handle it in HandleLinkEnd(), so we
+    // can skip bookmark creation.
+    if (frame.previousFeed) {
+      return;
+    }
+
+    // Create the bookmark.  The title is unknown for now, we will set it later.
+    try {
+      frame.previousId =
+        PlacesUtils.bookmarks.insertBookmark(frame.containerId,
+                                             frame.previousLink,
+                                             PlacesUtils.bookmarks.DEFAULT_INDEX,
+                                             "");
+    } catch(e) {
+      return;
+    }
+
+    // Set the date added value, if we have it.
+    if (dateAdded) {
+      try {
+        PlacesUtils.bookmarks.setItemDateAdded(frame.previousId,
+          this._convertImportedDateToInternalDate(dateAdded));
+      } catch(e) {
+      }
+    }
+
+    // Save the favicon.
+    if (icon || iconUri) {
+      let iconUriObject;
+      try {
+        iconUriObject = NetUtil.newURI(iconUri);
+      } catch(e) {
+      }
+      if (icon || iconUriObject) {
+        try {
+          this._setFaviconForURI(frame.previousLink, iconUriObject, icon);
+        } catch(e) {
+        }
+      }
+    }
+
+    // Save the keyword.
+    if (keyword) {
+      try {
+        PlacesUtils.bookmarks.setKeywordForBookmark(frame.previousId, keyword);
+        if (postData) {
+          PlacesUtils.annotations.setItemAnnotation(frame.previousId,
+                                                    POST_DATA_ANNO,
+                                                    postData,
+                                                    0,
+                                                    PlacesUtils.annotations.EXPIRE_NEVER);
+        }
+      } catch(e) {
+      }
+    }
+
+    // Set load-in-sidebar annotation for the bookmark.
+    if (webPanel && webPanel.toLowerCase() == "true") {
+      try {
+        PlacesUtils.annotations.setItemAnnotation(frame.previousId,
+                                                  LOAD_IN_SIDEBAR_ANNO,
+                                                  1,
+                                                  0,
+                                                  PlacesUtils.annotations.EXPIRE_NEVER);
+      } catch(e) {
+      }
+    }
+
+    // Import last charset.
+    if (lastCharset) {
+      try {
+        PlacesUtils.history.setCharsetForURI(frame.previousLink, lastCharset);
+      } catch(e) {
+      }
+    }
+
+  },
+
+  _handleContainerBegin: function handleContainerBegin() {
+    this._curFrame.containerNesting++;
+  },
+
+  /**
+   * Our "indent" count has decreased, and when we hit 0 that means that this
+   * container is complete and we need to pop back to the outer frame. Never
+   * pop the toplevel frame
+   */
+  _handleContainerEnd: function handleContainerEnd() {
+    let frame = this._curFrame;
+    if (frame.containerNesting > 0)
+      frame.containerNesting --;
+    if (this._frames.length > 1 && frame.containerNesting == 0) {
+      // we also need to re-set the imported last-modified date here. Otherwise
+      // the addition of items will override the imported field.
+      let prevFrame = this._previousFrame;
+      if (prevFrame.previousLastModifiedDate > 0) {
+        PlacesUtils.bookmarks.setItemLastModified(frame.containerId,
+                                                  prevFrame.previousLastModifiedDate);
+      }
+      this._frames.pop();
+    }
+  },
+
+  /**
+   * Creates the new frame for this heading now that we know the name of the
+   * container (tokens since the heading open tag will have been placed in
+   * previousText).
+   */
+  _handleHeadEnd: function handleHeadEnd() {
+    this._newFrame();
+  },
+
+  /**
+   * Saves the title for the given bookmark.
+   */
+  _handleLinkEnd: function handleLinkEnd() {
+    let frame = this._curFrame;
+    frame.previousText = frame.previousText.trim();
+
+    try {
+      if (frame.previousFeed) {
+        // The is a live bookmark.  We create it here since in HandleLinkBegin we
+        // don't know the title.
+        PlacesUtils.livemarks.addLivemark({
+          "title": frame.previousText,
+          "parentId": frame.containerId,
+          "index": PlacesUtils.bookmarks.DEFAULT_INDEX,
+          "feedURI": frame.previousFeed,
+          "siteURI": frame.previousLink,
+        });
+      } else if (frame.previousLink) {
+        // This is a common bookmark.
+        PlacesUtils.bookmarks.setItemTitle(frame.previousId,
+                                           frame.previousText);
+      }
+    } catch(e) {
+    }
+
+
+    // Set last modified date as the last change.
+    if (frame.previousId > 0 && frame.previousLastModifiedDate > 0) {
+      try {
+        PlacesUtils.bookmarks.setItemLastModified(frame.previousId,
+                                                  frame.previousLastModifiedDate);
+      } catch(e) {
+      }
+      // Note: don't clear previousLastModifiedDate, because if this item has a
+      // description, we'll need to set it again.
+    }
+
+    frame.previousText = "";
+
+  },
+
+  _openContainer: function openContainer(aElt) {
+    if (aElt.namespaceURI != "http://www.w3.org/1999/xhtml") {
+      return;
+    }
+    switch(aElt.localName) {
+      case "h1":
+        this._handleHead1Begin(aElt);
+        break;
+      case "h2":
+      case "h3":
+      case "h4":
+      case "h5":
+      case "h6":
+        this._handleHeadBegin(aElt);
+        break;
+      case "a":
+        this._handleLinkBegin(aElt);
+        break;
+      case "dl":
+      case "ul":
+      case "menu":
+        this._handleContainerBegin();
+        break;
+      case "dd":
+        this._curFrame.inDescription = true;
+        break;
+    }
+  },
+
+  _closeContainer: function closeContainer(aElt) {
+    let frame = this._curFrame;
+
+    // see the comment for the definition of inDescription. Basically, we commit
+    // any text in previousText to the description of the node/folder if there
+    // is any.
+    if (frame.inDescription) {
+      // NOTE ES5 trim trims more than the previous C++ trim.
+      frame.previousText = frame.previousText.trim(); // important
+      if (frame.previousText) {
+
+        let itemId = !frame.previousLink ? frame.containerId
+                                         : frame.previousId;
+
+        try {
+          if (!PlacesUtils.annotations.itemHasAnnotation(itemId, DESCRIPTION_ANNO)) {
+            PlacesUtils.annotations.setItemAnnotation(itemId,
+                                                      DESCRIPTION_ANNO,
+                                                      frame.previousText,
+                                                      0,
+                                                      PlacesUtils.annotations.EXPIRE_NEVER);
+          }
+        } catch(e) {
+        }
+        frame.previousText = "";
+
+        // Set last-modified a 2nd time for all items with descriptions
+        // we need to set last-modified as the *last* step in processing 
+        // any item type in the bookmarks.html file, so that we do
+        // not overwrite the imported value. for items without descriptions, 
+        // setting this value after setting the item title is that 
+        // last point at which we can save this value before it gets reset.
+        // for items with descriptions, it must set after that point.
+        // however, at the point at which we set the title, there's no way 
+        // to determine if there will be a description following, 
+        // so we need to set the last-modified-date at both places.
+
+        let lastModified;
+        if (!frame.previousLink) {
+          lastModified = this._previousFrame.previousLastModifiedDate;
+        } else {
+          lastModified = frame.previousLastModifiedDate;
+        }
+
+        if (itemId > 0 && lastModified > 0) {
+          PlacesUtils.bookmarks.setItemLastModified(itemId, lastModified);
+        }
+      }
+      frame.inDescription = false;
+    }
+
+    if (aElt.namespaceURI != "http://www.w3.org/1999/xhtml") {
+      return;
+    }
+    switch(aElt.localName) {
+      case "dl":
+      case "ul":
+      case "menu":
+        this._handleContainerEnd();
+        break;
+      case "dt":
+        break;
+      case "h1":
+        // ignore
+        break;
+      case "h2":
+      case "h3":
+      case "h4":
+      case "h5":
+      case "h6":
+        this._handleHeadEnd();
+        break;
+      case "a":
+        this._handleLinkEnd();
+        break;
+      default:
+        break;
+    }
+  },
+
+  _appendText: function appendText(str) {
+    this._curFrame.previousText += str;
+  },
+
+  /**
+   * data is a string that is a data URI for the favicon. Our job is to
+   * decode it and store it in the favicon service.
+   *
+   * When aIconURI is non-null, we will use that as the URI of the favicon
+   * when storing in the favicon service.
+   *
+   * When aIconURI is null, we have to make up a URI for this favicon so that
+   * it can be stored in the service. The real one will be set the next time
+   * the user visits the page. Our made up one should get expired when the
+   * page no longer references it.
+   */
+  _setFaviconForURI: function setFaviconForURI(aPageURI, aIconURI, aData) {
+    // if the input favicon URI is a chrome: URI, then we just save it and don't
+    // worry about data
+    if (aIconURI) {
+      if (aIconURI.scheme == "chrome") {
+        PlacesUtils.favicons.setFaviconUrlForPage(aPageURI, aIconURI);
+        return;
+      }
+    }
+
+    // some bookmarks have placeholder URIs that contain just "data:"
+    // ignore these
+    if (aData.length <= 5) {
+      return;
+    }
+
+    let faviconURI;
+    if (aIconURI) {
+      faviconURI = aIconURI;
+    } else {
+      // make up favicon URL
+      let faviconSpec = "http://www.mozilla.org/2005/made-up-favicon/"
+                      + serialNumber
+                      + "-"
+                      + new Date().getTime();
+      faviconURI = NetUtil.newURI(faviconSpec);
+      serialNumber++;
+    }
+
+    // save the favicon data
+    // This could fail if the favicon is bigger than defined limit, in such a
+    // case data will not be saved to the db but we will still continue.
+    PlacesUtils.favicons.setFaviconDataFromDataURL(faviconURI, aData, 0);
+
+    PlacesUtils.favicons.setFaviconUrlForPage(aPageURI, faviconURI);
+  },
+
+  /**
+   * Converts a string date in seconds to an int date in microseconds
+   */
+  _convertImportedDateToInternalDate: function convertImportedDateToInternalDate(aDate) {
+    if (aDate && !isNaN(aDate)) {
+      return parseInt(aDate) * 1000000; // in bookmarks.html this value is in seconds, not microseconds
+    } else {
+      return Date.now();
+    }
+  },
+
+  runBatched: function runBatched(aDoc) {
+    if (!aDoc) {
+      return;
+    }
+
+    if (this._isImportDefaults) {
+      PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.bookmarksMenuFolderId);
+      PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.toolbarFolderId);
+      PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
+    }
+
+    let current = aDoc;
+    let next;
+    for (;;) {
+      switch (current.nodeType) {
+        case Ci.nsIDOMNode.ELEMENT_NODE:
+          this._openContainer(current);
+          break;
+        case Ci.nsIDOMNode.TEXT_NODE:
+          this._appendText(current.data);
+          break;
+      }
+      if ((next = current.firstChild)) {
+        current = next;
+        continue;
+      }
+      for (;;) {
+        if (current.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
+          this._closeContainer(current);
+        }
+        if (current == aDoc) {
+          return;
+        }
+        if ((next = current.nextSibling)) {
+          current = next;
+          break;
+        }
+        current = current.parentNode;
+      }
+    }
+  },
+
+  _walkTreeForImport: function walkTreeForImport(aDoc) {
+    PlacesUtils.bookmarks.runInBatchMode(this, aDoc);
+  },
+
+  _notifyObservers: function notifyObservers(topic) {
+    Services.obs.notifyObservers(null,
+                                 topic,
+                                 this._isImportDefaults ?
+                                   RESTORE_INITIAL_NSIOBSERVER_DATA :
+                                   RESTORE_NSIOBSERVER_DATA);   
+  },
+
+  importFromURL: function importFromURL(aUrlString, aCallback) {
+    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
+    xhr.onload = (function onload() {
+      try {
+        this._walkTreeForImport(xhr.responseXML);
+        this._notifyObservers(RESTORE_SUCCESS_NSIOBSERVER_TOPIC);
+        if (aCallback) {
+          try {
+            aCallback(true);
+          } catch(ex) {
+          }
+        }
+      } catch(e) {
+        this._notifyObservers(RESTORE_FAILED_NSIOBSERVER_TOPIC);
+        if (aCallback) {
+          try {
+            aCallback(false);
+          } catch(ex) {
+          }
+        }
+        throw e;
+      }
+    }).bind(this);
+    xhr.onabort = xhr.onerror = xhr.ontimeout = (function handleFail() {
+      this._notifyObservers(RESTORE_FAILED_NSIOBSERVER_TOPIC);
+      if (aCallback) {
+        try {
+          aCallback(false);
+        } catch(ex) {
+        }
+      }
+    }).bind(this);
+    this._notifyObservers(RESTORE_BEGIN_NSIOBSERVER_TOPIC);
+    try {
+      xhr.open("GET", aUrlString);
+      xhr.responseType = "document";
+      xhr.overrideMimeType("text/html");
+      xhr.send();
+    } catch (e) {
+      this._notifyObservers(RESTORE_FAILED_NSIOBSERVER_TOPIC);
+      if (aCallback) {
+        try {
+          aCallback(false);
+        } catch(ex) {
+        }
+      }
+    }
+  },
+
+  importFromFile: function importFromFile(aLocalFile, aCallback) {
+    let url = NetUtil.newURI(aLocalFile);
+    this.importFromURL(url.spec, aCallback);
+  },
+
+};
--- a/toolkit/components/places/Helpers.cpp
+++ b/toolkit/components/places/Helpers.cpp
@@ -440,69 +440,10 @@ NS_IMETHODIMP
 AsyncStatementTelemetryTimer::HandleCompletion(PRUint16 aReason)
 {
   if (aReason == mozIStorageStatementCallback::REASON_FINISHED) {
     Telemetry::AccumulateTimeDelta(mHistogramId, mStart);
   }
   return NS_OK;
 }
 
-// This is a temporary converter used by nsPlacesImportExportService until
-// bug 482911 completes its js rewrite.
-jsval
-livemarkInfoToJSVal(PRInt64 aId,
-                    const nsACString& aGUID,
-                    const nsAString& aTitle,
-                    PRInt64 aParentId,
-                    PRInt32 aIndex,
-                    nsCOMPtr<nsIURI>& aFeedURI,
-                    nsCOMPtr<nsIURI>& aSiteURI)
-{
-  nsCOMPtr<nsIXPConnect> xpc = mozilla::services::GetXPConnect();
-  NS_ENSURE_TRUE(xpc, JSVAL_NULL);
-
-  nsAXPCNativeCallContext *ncc;
-  nsresult rv = xpc->GetCurrentNativeCallContext(&ncc);
-  NS_ENSURE_SUCCESS(rv, JSVAL_NULL);
-  JSContext *cx = nsnull;
-  rv = ncc->GetJSContext(&cx);
-  NS_ENSURE_SUCCESS(rv, JSVAL_NULL);
-  JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
-  NS_ENSURE_TRUE(obj, JSVAL_NULL); 
-
-  jsval id;
-  NS_ENSURE_TRUE(JS_NewNumberValue(cx, double(aId), &id), JSVAL_NULL);
-
-  JSString* guid = JS_NewStringCopyN(cx, PromiseFlatCString(aGUID).get(),
-                                     aGUID.Length());
-  NS_ENSURE_TRUE(guid, JSVAL_NULL); 
-
-  JSString* title = JS_NewUCStringCopyN(cx, PromiseFlatString(aTitle).get(),
-                                        aTitle.Length());
-  NS_ENSURE_TRUE(title, JSVAL_NULL); 
-
-  jsval parentId;
-  NS_ENSURE_TRUE(JS_NewNumberValue(cx, double(aParentId), &parentId), JSVAL_NULL);
-
-  jsval feedURI;
-  rv = nsContentUtils::WrapNative(cx, JS_GetGlobalForScopeChain(cx),
-                                  NS_ISUPPORTS_CAST(nsIURI*, aFeedURI), &feedURI);
-  NS_ENSURE_SUCCESS(rv, JSVAL_NULL);
-
-  jsval siteURI;
-  rv = nsContentUtils::WrapNative(cx, JS_GetGlobalForScopeChain(cx),
-                                  NS_ISUPPORTS_CAST(nsIURI*, aSiteURI), &siteURI);
-  NS_ENSURE_SUCCESS(rv, JSVAL_NULL);
-
-  if (!JS_DefineProperty(cx, obj, "id", id, NULL, NULL, JSPROP_ENUMERATE) ||
-      !JS_DefineProperty(cx, obj, "guid", STRING_TO_JSVAL(guid), NULL, NULL, JSPROP_ENUMERATE) ||
-      !JS_DefineProperty(cx, obj, "title", STRING_TO_JSVAL(title), NULL, NULL, JSPROP_ENUMERATE) ||
-      !JS_DefineProperty(cx, obj, "parentId", parentId, NULL, NULL, JSPROP_ENUMERATE) ||
-      !JS_DefineProperty(cx, obj, "index", INT_TO_JSVAL(aIndex), NULL, NULL, JSPROP_ENUMERATE) ||
-      !JS_DefineProperty(cx, obj, "feedURI", feedURI, NULL, NULL, JSPROP_ENUMERATE) ||
-      !JS_DefineProperty(cx, obj, "siteURI", siteURI, NULL, NULL, JSPROP_ENUMERATE)) {
-    return JSVAL_NULL;
-  }
-  return OBJECT_TO_JSVAL(obj);
-}
-
 } // namespace places
 } // namespace mozilla
--- a/toolkit/components/places/Helpers.h
+++ b/toolkit/components/places/Helpers.h
@@ -292,21 +292,12 @@ public:
 
   NS_IMETHOD HandleCompletion(PRUint16 aReason);
 
 private:
   const Telemetry::ID mHistogramId;
   const TimeStamp mStart;
 };
 
-jsval
-livemarkInfoToJSVal(PRInt64 aId,
-                    const nsACString& aGUID,
-                    const nsAString& aTitle,
-                    PRInt64 aParentId,
-                    PRInt32 aIndex,
-                    nsCOMPtr<nsIURI>& aFeedURI,
-                    nsCOMPtr<nsIURI>& aSiteURI);
-
 } // namespace places
 } // namespace mozilla
 
 #endif // mozilla_places_Helpers_h_
--- a/toolkit/components/places/Makefile.in
+++ b/toolkit/components/places/Makefile.in
@@ -87,17 +87,17 @@ CPPSRCS = \
   nsNavHistoryQuery.cpp \
   nsNavHistoryResult.cpp \
   nsNavBookmarks.cpp \
   nsMaybeWeakPtr.cpp \
   nsPlacesModule.cpp \
   SQLFunctions.cpp \
   Helpers.cpp \
   History.cpp \
-  nsPlacesImportExportService.cpp \
+  nsPlacesExportService.cpp \
   AsyncFaviconHelpers.cpp \
   PlaceInfo.cpp \
   VisitInfo.cpp \
   Database.cpp \
   $(NULL)
 
 LOCAL_INCLUDES += -I$(srcdir)/../build
 
@@ -118,16 +118,17 @@ EXTRA_COMPONENTS = \
   $(NULL)
 
 ifdef MOZ_XUL
 EXTRA_COMPONENTS += nsPlacesAutoComplete.js nsPlacesAutoComplete.manifest
 endif
 
 EXTRA_JS_MODULES = \
   PlacesDBUtils.jsm \
+  BookmarkHTMLUtils.jsm \
   $(NULL)
 
 EXTRA_PP_JS_MODULES = \
   PlacesUtils.jsm \
   $(NULL)
 
 TEST_DIRS += tests
 
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -80,17 +80,17 @@ XPCOMUtils.defineLazyGetter(this, "NetUt
 });
 
 // The minimum amount of transactions before starting a batch. Usually we do
 // do incremental updates, a batch will cause views to completely
 // refresh instead.
 const MIN_TRANSACTIONS_FOR_BATCH = 5;
 
 // The RESTORE_*_NSIOBSERVER_TOPIC constants should match the #defines of the
-// same names in browser/components/places/src/nsPlacesImportExportService.cpp
+// same names in browser/components/places/src/nsPlacesExportService.cpp
 const RESTORE_BEGIN_NSIOBSERVER_TOPIC = "bookmarks-restore-begin";
 const RESTORE_SUCCESS_NSIOBSERVER_TOPIC = "bookmarks-restore-success";
 const RESTORE_FAILED_NSIOBSERVER_TOPIC = "bookmarks-restore-failed";
 const RESTORE_NSIOBSERVER_DATA = "json";
 
 #ifdef XP_MACOSX
 // On Mac OSX, the transferable system converts "\r\n" to "\n\n", where we
 // really just want "\n".
--- a/toolkit/components/places/nsIPlacesImportExportService.idl
+++ b/toolkit/components/places/nsIPlacesImportExportService.idl
@@ -36,45 +36,26 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsILocalFile;
 interface nsIURI;
 
 /**
- * The PlacesImportExport interface provides methods for importing
- * and exporting Places data.
+ * The PlacesImportExport interface provides methods for exporting Places data.
+ * The word "Import" is in the name for legacy reasons and was kept to avoid
+ * disrupting potential extension code using the export part. The new importer
+ * lives in BookmarkHTMLUtils.jsm.
  */
 
-[scriptable, uuid(08f4626e-af3f-4e84-bd01-cf09175c4f94)]
+[scriptable, uuid(47a4a09e-c708-4e68-b2f2-664d982ce026)]
 interface nsIPlacesImportExportService: nsISupports
 {
   /**
-   * Loads the given bookmarks.html file and replaces it with the current
-   * bookmarks hierarchy (if aIsInitialImport is true) or appends it
-   * (if aIsInitialImport is false).
-   *
-   * Three nsIObserverService notifications are fired as a result of the
-   * import.  "bookmarks-restore-begin" is fired just before the import is
-   * started.  "bookmarks-restore-success" is fired right after the
-   * bookmarks are successfully imported.  "bookmarks-restore-failed" is
-   * fired right after a failure occurs when importing the bookmarks.
-   * Observers will be passed through their data parameters either "html"
-   * if aIsInitialImport is false or "html-initial" if aIsInitialImport is
-   * true.  The observer subject will be null.
-   */
-  void importHTMLFromFile(in nsILocalFile aFile, in boolean aIsInitialImport);
-
-  /**
-   * Same thing as importHTMLFromFile, but takes a URI instead
-   */
-  void importHTMLFromURI(in nsIURI aURI, in boolean aIsInitialImport);
-
-  /**
    * Saves the current bookmarks hierarchy to a bookmarks.html file.
    */
   void exportHTMLToFile(in nsILocalFile aFile);
 
   /**
    * Backup the bookmarks.html file.
    */
   void backupBookmarksFile();
rename from toolkit/components/places/nsPlacesImportExportService.cpp
rename to toolkit/components/places/nsPlacesExportService.cpp
--- a/toolkit/components/places/nsPlacesImportExportService.cpp
+++ b/toolkit/components/places/nsPlacesExportService.cpp
@@ -87,168 +87,52 @@
  * to 0, then we know a frame is complete and to pop back to the previous
  * frame.
  *
  * Note that a lot of things happen when tags are CLOSED because we need to
  * get the text from the content of the tag. For example, link and heading tags
  * both require the content (= title) before actually creating it.
  */
 
-#include "nsPlacesImportExportService.h"
+#include "nsPlacesExportService.h"
 #include "nsNetUtil.h"
 #include "nsParserCIID.h"
 #include "nsUnicharUtils.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsToolkitCompsCID.h"
-#include "nsIHTMLContentSink.h"
 #include "nsIParser.h"
 #include "prprf.h"
 #include "nsIObserverService.h"
 #include "nsISupportsPrimitives.h"
 #include "nsPlacesMacros.h"
 #include "mozilla/Util.h"
-#include "Helpers.h"
 
 using namespace mozilla;
-using namespace mozilla::places;
-
-static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
-
-#define KEY_TOOLBARFOLDER_LOWER "personal_toolbar_folder"
-#define KEY_BOOKMARKSMENU_LOWER "bookmarks_menu"
-#define KEY_UNFILEDFOLDER_LOWER "unfiled_bookmarks_folder"
-#define KEY_PLACESROOT_LOWER "places_root"
-#define KEY_HREF_LOWER "href"
-#define KEY_FEEDURL_LOWER "feedurl"
-#define KEY_WEB_PANEL_LOWER "web_panel"
-#define KEY_LASTCHARSET_LOWER "last_charset"
-#define KEY_ICON_LOWER "icon"
-#define KEY_ICON_URI_LOWER "icon_uri"
-#define KEY_SHORTCUTURL_LOWER "shortcuturl"
-#define KEY_POST_DATA_LOWER "post_data"
-#define KEY_NAME_LOWER "name"
-#define KEY_MICSUM_GEN_URI_LOWER "micsum_gen_uri"
-#define KEY_DATE_ADDED_LOWER "add_date"
-#define KEY_LAST_MODIFIED_LOWER "last_modified"
-#define KEY_GENERATED_TITLE_LOWER "generated_title"
 
 #define LOAD_IN_SIDEBAR_ANNO NS_LITERAL_CSTRING("bookmarkProperties/loadInSidebar")
 #define DESCRIPTION_ANNO NS_LITERAL_CSTRING("bookmarkProperties/description")
 #define POST_DATA_ANNO NS_LITERAL_CSTRING("bookmarkProperties/POSTData")
 
-#define BOOKMARKS_MENU_ICON_URI "chrome://browser/skin/places/bookmarksMenu.png"
-
-// The RESTORE_*_NSIOBSERVER_TOPIC #defines should match the constants of the
-// same names in toolkit/components/places/src/utils.js
-#define RESTORE_BEGIN_NSIOBSERVER_TOPIC "bookmarks-restore-begin"
-#define RESTORE_SUCCESS_NSIOBSERVER_TOPIC "bookmarks-restore-success"
-#define RESTORE_FAILED_NSIOBSERVER_TOPIC "bookmarks-restore-failed"
-#define RESTORE_NSIOBSERVER_DATA NS_LITERAL_STRING("html")
-#define RESTORE_INITIAL_NSIOBSERVER_DATA NS_LITERAL_STRING("html-initial")
-
 #define LMANNO_FEEDURI "livemark/feedURI"
 #define LMANNO_SITEURI "livemark/siteURI"
 
 // define to get debugging messages on console about import/export
-//#define DEBUG_IMPORT
 //#define DEBUG_EXPORT
 
 #if defined(XP_WIN) || defined(XP_OS2)
 #define NS_LINEBREAK "\015\012"
 #else
 #define NS_LINEBREAK "\012"
 #endif
 
 class nsIOutputStream;
 static const char kWhitespace[] = " \r\n\t\b";
 static nsresult WriteEscapedUrl(const nsCString &aString, nsIOutputStream* aOutput);
 
-class BookmarkImportFrame
-{
-public:
-  BookmarkImportFrame(PRInt64 aID) :
-      mContainerID(aID),
-      mContainerNesting(0),
-      mLastContainerType(Container_Normal),
-      mInDescription(false),
-      mPreviousId(0),
-      mPreviousDateAdded(0),
-      mPreviousLastModifiedDate(0)
-  {
-  }
-
-  enum ContainerType { Container_Normal,
-                       Container_Places,
-                       Container_Menu,
-                       Container_Toolbar,
-                       Container_Unfiled};
-
-  PRInt64 mContainerID;
-
-  // How many <dl>s have been nested. Each frame/container should start
-  // with a heading, and is then followed by a <dl>, <ul>, or <menu>. When
-  // that list is complete, then it is the end of this container and we need
-  // to pop back up one level for new items. If we never get an open tag for
-  // one of these things, we should assume that the container is empty and
-  // that things we find should be siblings of it. Normally, these <dl>s won't
-  // be nested so this will be 0 or 1.
-  PRInt32 mContainerNesting;
-
-  // when we find a heading tag, it actually affects the title of the NEXT
-  // container in the list. This stores that heading tag and whether it was
-  // special. 'ConsumeHeading' resets this.
-  ContainerType mLastContainerType;
-
-  // this contains the text from the last begin tag until now. It is reset
-  // at every begin tag. We can check it when we see a </a>, or </h3>
-  // to see what the text content of that node should be.
-  nsString mPreviousText;
-
-  // true when we hit a <dd>, which contains the description for the preceding
-  // <a> tag. We can't just check for </dd> like we can for </a> or </h3>
-  // because if there is a sub-folder, it is actually a child of the <dd>
-  // because the tag is never explicitly closed. If this is true and we see a
-  // new open tag, that means to commit the description to the previous
-  // bookmark.
-  //
-  // Additional weirdness happens when the previous <dt> tag contains a <h3>:
-  // this means there is a new folder with the given description, and whose
-  // children are contained in the following <dl> list.
-  //
-  // This is handled in OpenContainer(), which commits previous text if
-  // necessary.
-  bool mInDescription;
-
-  // contains the URL of the previous bookmark created. This is used so that
-  // when we encounter a <dd>, we know what bookmark to associate the text with.
-  // This is cleared whenever we hit a <h3>, so that we know NOT to save this
-  // with a bookmark, but to keep it until 
-  nsCOMPtr<nsIURI> mPreviousLink;
-
-  // contains the URL of the previous livemark, so that when the link ends,
-  // and the livemark title is known, we can create it.
-  nsCOMPtr<nsIURI> mPreviousFeed;
-
-  void ConsumeHeading(nsAString* aHeading, ContainerType* aContainerType)
-  {
-    *aHeading = mPreviousText;
-    *aContainerType = mLastContainerType;
-    mPreviousText.Truncate();
-  }
-
-  // Contains the id of an imported, or newly created bookmark.
-  PRInt64 mPreviousId;
-
-  // Contains the date-added and last-modified-date of an imported item.
-  // Used to override the values set by insertBookmark, createFolder, etc.
-  PRTime mPreviousDateAdded;
-  PRTime mPreviousLastModifiedDate;
-};
-
 /**
  * Copied from nsEscape.cpp, which requires internal string API.
  */
 char*
 nsEscapeHTML(const char* string)
 {
   /* XXX Hardcoded max entity len. The +1 is for the trailing null. */
   char* escaped = nsnull;
@@ -299,994 +183,52 @@ nsEscapeHTML(const char* string)
           *ptr++ = *string;
       }
     }
     *ptr = '\0';
   }
   return escaped;
 }
 
-PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsPlacesImportExportService, gImportExportService)
+PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsPlacesExportService, gExportService)
 
-NS_IMPL_ISUPPORTS2(nsPlacesImportExportService, nsIPlacesImportExportService,
-                   nsINavHistoryBatchCallback)
-
+NS_IMPL_ISUPPORTS1(nsPlacesExportService, nsIPlacesImportExportService)
 
-nsPlacesImportExportService::nsPlacesImportExportService()
+nsPlacesExportService::nsPlacesExportService()
 {
-  NS_ASSERTION(!gImportExportService,
+  NS_ASSERTION(!gExportService,
                "Attempting to create two instances of the service!");
-  gImportExportService = this;
+  gExportService = this;
 }
 
-nsPlacesImportExportService::~nsPlacesImportExportService()
+nsPlacesExportService::~nsPlacesExportService()
 {
-  NS_ASSERTION(gImportExportService == this,
+  NS_ASSERTION(gExportService == this,
                "Deleting a non-singleton instance of the service");
-  if (gImportExportService == this)
-    gImportExportService = nsnull;
+  if (gExportService == this)
+    gExportService = nsnull;
 }
 
 nsresult
-nsPlacesImportExportService::Init()
+nsPlacesExportService::Init()
 {
   // Be sure to call EnsureServiceState() before using services.
   mHistoryService = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
   NS_ENSURE_TRUE(mHistoryService, NS_ERROR_OUT_OF_MEMORY);
   mFaviconService = do_GetService(NS_FAVICONSERVICE_CONTRACTID);
   NS_ENSURE_TRUE(mFaviconService, NS_ERROR_OUT_OF_MEMORY);
   mAnnotationService = do_GetService(NS_ANNOTATIONSERVICE_CONTRACTID);
   NS_ENSURE_TRUE(mAnnotationService, NS_ERROR_OUT_OF_MEMORY);
   mBookmarksService = do_GetService(NS_NAVBOOKMARKSSERVICE_CONTRACTID);
   NS_ENSURE_TRUE(mBookmarksService, NS_ERROR_OUT_OF_MEMORY);
   mLivemarkService = do_GetService(NS_LIVEMARKSERVICE_CONTRACTID);
   NS_ENSURE_TRUE(mLivemarkService, NS_ERROR_OUT_OF_MEMORY);
   return NS_OK;
 }
 
-/**
- * The content sink stuff is based loosely on nsIHTMLContentSink.
- */
-class BookmarkContentSink : public nsIHTMLContentSink
-{
-public:
-  BookmarkContentSink();
-
-  nsresult Init(bool aAllowRootChanges,
-                PRInt64 aFolder,
-                bool aIsImportDefaults);
-
-  NS_DECL_ISUPPORTS
-
-  // nsIContentSink (superclass of nsIHTMLContentSink)
-  NS_IMETHOD WillParse() { return NS_OK; }
-  NS_IMETHOD WillInterrupt() { return NS_OK; }
-  NS_IMETHOD WillResume() { return NS_OK; }
-  NS_IMETHOD SetParser(nsParserBase* aParser) { return NS_OK; }
-  virtual void FlushPendingNotifications(mozFlushType aType) { }
-  NS_IMETHOD SetDocumentCharset(nsACString& aCharset) { return NS_OK; }
-  virtual nsISupports *GetTarget() { return nsnull; }
-
-  // nsIHTMLContentSink
-  NS_IMETHOD OpenHead() { return NS_OK; }
-  NS_IMETHOD BeginContext(PRInt32 aPosition) { return NS_OK; }
-  NS_IMETHOD EndContext(PRInt32 aPosition) { return NS_OK; }
-  NS_IMETHOD IsEnabled(PRInt32 aTag, bool* aReturn)
-    { *aReturn = true; return NS_OK; }
-  NS_IMETHOD DidProcessTokens() { return NS_OK; }
-  NS_IMETHOD WillProcessAToken() { return NS_OK; }
-  NS_IMETHOD DidProcessAToken() { return NS_OK; }
-  NS_IMETHOD OpenContainer(const nsIParserNode& aNode);
-  NS_IMETHOD CloseContainer(const nsHTMLTag aTag);
-  NS_IMETHOD AddLeaf(const nsIParserNode& aNode);
-  NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode) { return NS_OK; }
-
-protected:
-  nsCOMPtr<nsINavBookmarksService> mBookmarksService;
-  nsCOMPtr<nsINavHistoryService> mHistoryService;
-  nsCOMPtr<nsIAnnotationService> mAnnotationService;
-  nsCOMPtr<mozIAsyncLivemarks> mLivemarkService;
-
-  // If set, we will move root items to from their existing position
-  // in the hierarchy, to where we find them in the bookmarks file
-  // being imported. This should be set when we are loading 
-  // the default places html file, and should be unset when doing
-  // normal imports so that root folders will not get moved when
-  // importing bookmarks.html files.
-  bool mAllowRootChanges;
-
-  // If set, this is an import of initial bookmarks.html content,
-  // so we don't want to kick off HTTP traffic
-  // and we want the imported personal toolbar folder
-  // to be set as the personal toolbar folder. (If not set
-  // we will treat it as a normal folder.)
-  bool mIsImportDefaults;
-
-  // If a folder was specified to import into, then ignore flags to put
-  // bookmarks in the bookmarks menu or toolbar and keep them inside
-  // the folder.
-  bool mFolderSpecified;
-
-  void HandleContainerBegin(const nsIParserNode& node);
-  void HandleContainerEnd();
-  void HandleHead1Begin(const nsIParserNode& node);
-  void HandleHeadBegin(const nsIParserNode& node);
-  void HandleHeadEnd();
-  void HandleLinkBegin(const nsIParserNode& node);
-  void HandleLinkEnd();
-  void HandleSeparator(const nsIParserNode& node);
-
-  // This is a list of frames. We really want a recursive parser, but the HTML
-  // parser gives us tags as a stream. This implements all the state on a stack
-  // so we can get the recursive information we need. Use "CurFrame" to get the
-  // top "stack frame" with the current state in it.
-  nsTArray<BookmarkImportFrame> mFrames;
-  BookmarkImportFrame& CurFrame()
-  {
-    NS_ASSERTION(mFrames.Length() > 0, "Asking for frame when there are none!");
-    return mFrames[mFrames.Length() - 1];
-  }
-  BookmarkImportFrame& PreviousFrame()
-  {
-    NS_ASSERTION(mFrames.Length() > 1, "Asking for frame when there are not enough!");
-    return mFrames[mFrames.Length() - 2];
-  }
-  nsresult NewFrame();
-  nsresult PopFrame();
-
-  nsresult SetFaviconForURI(nsIURI* aPageURI, nsIURI* aFaviconURI,
-                            const nsString& aData);
-
-  PRTime ConvertImportedDateToInternalDate(const nsACString& aDate);
-
-#ifdef DEBUG_IMPORT
-  // prints spaces for indenting to the current frame depth
-  void PrintNesting()
-  {
-    for (PRUint32 i = 0; i < mFrames.Length(); i ++)
-      printf("  ");
-  }
-#endif
-};
-
-
-BookmarkContentSink::BookmarkContentSink() : mFrames(16)
-{
-}
-
-
-nsresult
-BookmarkContentSink::Init(bool aAllowRootChanges,
-                          PRInt64 aFolder,
-                          bool aIsImportDefaults)
-{
-  mBookmarksService = do_GetService(NS_NAVBOOKMARKSSERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(mBookmarksService, NS_ERROR_OUT_OF_MEMORY);
-  mHistoryService = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(mHistoryService, NS_ERROR_OUT_OF_MEMORY);
-  mAnnotationService = do_GetService(NS_ANNOTATIONSERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(mAnnotationService, NS_ERROR_OUT_OF_MEMORY);
-  mLivemarkService = do_GetService(NS_LIVEMARKSERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(mLivemarkService, NS_ERROR_OUT_OF_MEMORY);
-
-  mAllowRootChanges = aAllowRootChanges;
-  mIsImportDefaults = aIsImportDefaults;
-
-  // initialize the root frame with the menu root
-  PRInt64 menuRoot;
-  nsresult rv;
-  if (aFolder == 0) {
-    rv = mBookmarksService->GetBookmarksMenuFolder(&menuRoot);
-    NS_ENSURE_SUCCESS(rv, rv);
-    mFolderSpecified = false;
-  }
-  else {
-    menuRoot = aFolder;
-    mFolderSpecified = true;
-  }
-  if (!mFrames.AppendElement(BookmarkImportFrame(menuRoot)))
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  return NS_OK;
-}
-
-
-NS_IMPL_ISUPPORTS2(BookmarkContentSink,
-                   nsIContentSink,
-                   nsIHTMLContentSink)
-
-
-NS_IMETHODIMP
-BookmarkContentSink::OpenContainer(const nsIParserNode& aNode)
-{
-  switch(aNode.GetNodeType()) {
-    case eHTMLTag_h1:
-      HandleHead1Begin(aNode);
-      break;
-    case eHTMLTag_h2:
-    case eHTMLTag_h3:
-    case eHTMLTag_h4:
-    case eHTMLTag_h5:
-    case eHTMLTag_h6:
-      HandleHeadBegin(aNode);
-      break;
-    case eHTMLTag_a:
-      HandleLinkBegin(aNode);
-      break;
-    case eHTMLTag_dl:
-    case eHTMLTag_ul:
-    case eHTMLTag_menu:
-      HandleContainerBegin(aNode);
-      break;
-    case eHTMLTag_dd:
-      CurFrame().mInDescription = true;
-      break;
-  }
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP
-BookmarkContentSink::CloseContainer(const nsHTMLTag aTag)
-{
-  BookmarkImportFrame& frame = CurFrame();
-
-  // see the comment for the definition of mInDescription. Basically, we commit
-  // any text in mPreviousText to the description of the node/folder if there
-  // is any.
-  if (frame.mInDescription) {
-    frame.mPreviousText.Trim(kWhitespace); // important!
-    if (!frame.mPreviousText.IsEmpty()) {
-
-      PRInt64 itemId = !frame.mPreviousLink ? frame.mContainerID
-                                            : frame.mPreviousId;
-                    
-      bool hasDescription = false;
-      nsresult rv = mAnnotationService->ItemHasAnnotation(itemId,
-                                                          DESCRIPTION_ANNO,
-                                                          &hasDescription);
-      if (NS_SUCCEEDED(rv) && !hasDescription) {
-        mAnnotationService->SetItemAnnotationString(itemId, DESCRIPTION_ANNO,
-                                                    frame.mPreviousText, 0,
-                                                    nsIAnnotationService::EXPIRE_NEVER);
-      }
-      frame.mPreviousText.Truncate();
-
-      // Set last-modified a 2nd time for all items with descriptions
-      // we need to set last-modified as the *last* step in processing 
-      // any item type in the bookmarks.html file, so that we do
-      // not overwrite the imported value. for items without descriptions, 
-      // setting this value after setting the item title is that 
-      // last point at which we can save this value before it gets reset.
-      // for items with descriptions, it must set after that point.
-      // however, at the point at which we set the title, there's no way 
-      // to determine if there will be a description following, 
-      // so we need to set the last-modified-date at both places.
-
-      PRTime lastModified;
-      if (!frame.mPreviousLink) {
-        lastModified = PreviousFrame().mPreviousLastModifiedDate;
-      } else {
-        lastModified = frame.mPreviousLastModifiedDate;
-      }
-
-      if (itemId > 0 && lastModified > 0) {
-        rv = mBookmarksService->SetItemLastModified(itemId, lastModified);
-        NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemLastModified failed");
-      }
-    }
-    frame.mInDescription = false;
-  }
-
-  switch (aTag) {
-    case eHTMLTag_dl:
-    case eHTMLTag_ul:
-    case eHTMLTag_menu:
-      HandleContainerEnd();
-      break;
-    case eHTMLTag_dt:
-      break;
-    case eHTMLTag_h1:
-      // ignore
-      break;
-    case eHTMLTag_h2:
-    case eHTMLTag_h3:
-    case eHTMLTag_h4:
-    case eHTMLTag_h5:
-    case eHTMLTag_h6:
-      HandleHeadEnd();
-      break;
-    case eHTMLTag_a:
-      HandleLinkEnd();
-      break;
-    default:
-      break;
-  }
-  return NS_OK;
-}
-
-
-// BookmarkContentSink::AddLeaf
-//
-//    XXX on the branch, we should be calling CollectSkippedContent as in
-//    nsHTMLFragmentContentSink.cpp:AddLeaf when we encounter title, script,
-//    style, or server tags. Apparently if we don't, we'll leak the next DOM
-//    node. However, this requires that we keep a reference to the parser we'll
-//    introduce a circular reference because it has a reference to us.
-//
-//    This is annoying to fix and these elements are not allowed in bookmarks
-//    files anyway. So if somebody tries to import a crazy bookmarks file, it
-//    will leak a little bit.
-
-NS_IMETHODIMP
-BookmarkContentSink::AddLeaf(const nsIParserNode& aNode)
-{
-  switch (aNode.GetNodeType()) {
-  case eHTMLTag_text:
-    // save any text we find
-    CurFrame().mPreviousText += aNode.GetText();
-    break;
-  case eHTMLTag_entity: {
-    nsAutoString tmp;
-    PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp);
-    if (unicode < 0) {
-      // invalid entity - just use the text of it
-      CurFrame().mPreviousText += aNode.GetText();
-    } else {
-      CurFrame().mPreviousText.Append(unicode);
-    }
-    break;
-  }
-  case eHTMLTag_whitespace:
-    CurFrame().mPreviousText.Append(PRUnichar(' '));
-    break;
-  case eHTMLTag_hr:
-    HandleSeparator(aNode);
-    break;
-  }
-
-  return NS_OK;
-}
-
-
-void
-BookmarkContentSink::HandleContainerBegin(const nsIParserNode& node)
-{
-  CurFrame().mContainerNesting ++;
-}
-
-
-// BookmarkContentSink::HandleContainerEnd
-//
-//    Our "indent" count has decreased, and when we hit 0 that means that this
-//    container is complete and we need to pop back to the outer frame. Never
-//    pop the toplevel frame
-
-void
-BookmarkContentSink::HandleContainerEnd()
-{
-  BookmarkImportFrame& frame = CurFrame();
-  if (frame.mContainerNesting > 0)
-    frame.mContainerNesting --;
-  if (mFrames.Length() > 1 && frame.mContainerNesting == 0) {
-    // we also need to re-set the imported last-modified date here. Otherwise
-    // the addition of items will override the imported field.
-    BookmarkImportFrame& prevFrame = PreviousFrame();
-    if (prevFrame.mPreviousLastModifiedDate > 0) {
-      (void)mBookmarksService->SetItemLastModified(frame.mContainerID,
-                                                   prevFrame.mPreviousLastModifiedDate);
-    }
-    PopFrame();
-  }
-}
-
-
-// BookmarkContentSink::HandleHead1Begin
-//
-//    Handles <H1>. We check for the attribute PLACES_ROOT and reset the
-//    container id if it's found. Otherwise, the default bookmark menu
-//    root is assumed and imported things will go into the bookmarks menu.
-
-void
-BookmarkContentSink::HandleHead1Begin(const nsIParserNode& node)
-{
-  PRInt32 attrCount = node.GetAttributeCount();
-  for (PRInt32 i = 0; i < attrCount; i ++) {
-    if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_PLACESROOT_LOWER)) {
-      if (mFrames.Length() > 1) {
-        NS_WARNING("Trying to set the places root from the middle of the hierarchy. "
-                   "This can only be set at the beginning.");
-        return;
-      }
-
-      PRInt64 placesRoot;
-      DebugOnly<nsresult> rv = mBookmarksService->GetPlacesRoot(&placesRoot);
-      NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "could not get placesRoot");
-      CurFrame().mContainerID = placesRoot;
-      break;
-    }
-  }
-}
-
-
-// BookmarkContentSink::HandleHeadBegin
-//
-//    Called for h2,h3,h4,h5,h6. This just stores the correct information in
-//    the current frame; the actual new frame corresponding to the container
-//    associated with the heading will be created when the tag has been closed
-//    and we know the title (we don't know to create a new folder or to merge
-//    with an existing one until we have the title).
-
-void
-BookmarkContentSink::HandleHeadBegin(const nsIParserNode& node)
-{
-  BookmarkImportFrame& frame = CurFrame();
-
-  // after a heading, a previous bookmark is not applicable (for example, for
-  // the descriptions contained in a <dd>). Neither is any previous head type
-  frame.mPreviousLink = nsnull;
-  frame.mLastContainerType = BookmarkImportFrame::Container_Normal;
-
-  // It is syntactically possible for a heading to appear after another heading
-  // but before the <dl> that encloses that folder's contents.  This should not
-  // happen in practice, as the file will contain "<dl></dl>" sequence for
-  // empty containers.
-  //
-  // Just to be on the safe side, if we encounter
-  //   <h3>FOO</h3>
-  //   <h3>BAR</h3>
-  //   <dl>...content 1...</dl>
-  //   <dl>...content 2...</dl>
-  // we'll pop the stack when we find the h3 for BAR, treating that as an
-  // implicit ending of the FOO container. The output will be FOO and BAR as
-  // siblings. If there's another <dl> following (as in "content 2"), those
-  // items will be treated as further siblings of FOO and BAR
-  if (frame.mContainerNesting == 0)
-    PopFrame();
-
-  // We have to check for some attributes to see if this is a "special"
-  // folder, which will have different creation rules when the end tag is
-  // processed.
-  PRInt32 attrCount = node.GetAttributeCount();
-  frame.mLastContainerType = BookmarkImportFrame::Container_Normal;
-  for (PRInt32 i = 0; i < attrCount; ++i) {
-    if (!mFolderSpecified) {
-      if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_TOOLBARFOLDER_LOWER)) {
-        if (mIsImportDefaults)
-          frame.mLastContainerType = BookmarkImportFrame::Container_Toolbar;
-        break;
-      }
-      else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_BOOKMARKSMENU_LOWER)) {
-        if (mIsImportDefaults)
-          frame.mLastContainerType = BookmarkImportFrame::Container_Menu;
-        break;
-      }
-      else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_UNFILEDFOLDER_LOWER)) {
-        if (mIsImportDefaults)
-          frame.mLastContainerType = BookmarkImportFrame::Container_Unfiled;
-        break;
-      }
-      else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_PLACESROOT_LOWER)) {
-        if (mIsImportDefaults)
-          frame.mLastContainerType = BookmarkImportFrame::Container_Places;
-        break;
-      }
-    }
-
-    if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_DATE_ADDED_LOWER)) {
-      frame.mPreviousDateAdded =
-        ConvertImportedDateToInternalDate(NS_ConvertUTF16toUTF8(node.GetValueAt(i)));
-    }
-    else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_LAST_MODIFIED_LOWER)) {
-      frame.mPreviousLastModifiedDate =
-        ConvertImportedDateToInternalDate(NS_ConvertUTF16toUTF8(node.GetValueAt(i)));
-    }
-  }
-  CurFrame().mPreviousText.Truncate();
-}
-
-
-// BookmarkContentSink::HandleHeadEnd
-//
-//    Creates the new frame for this heading now that we know the name of the
-//    container (tokens since the heading open tag will have been placed in
-//    mPreviousText).
-
-void
-BookmarkContentSink::HandleHeadEnd()
-{
-  NewFrame();
-}
-
-
-// BookmarkContentSink::HandleLinkBegin
-//
-//    Handles "<a" tags by creating a new bookmark. The title of the bookmark
-//    will be the text content, which will be stuffed in mPreviousText for us
-//    and which will be saved by HandleLinkEnd
-
-void
-BookmarkContentSink::HandleLinkBegin(const nsIParserNode& node)
-{
-  BookmarkImportFrame& frame = CurFrame();
-
-  // Make sure that the feed URIs from previous frames are emptied.
-  frame.mPreviousFeed = nsnull;
-  // Make sure that the bookmark id from previous frames are emptied.
-  frame.mPreviousId = 0;
-  // mPreviousText will hold link text, clear it.
-  frame.mPreviousText.Truncate();
-
-  // Get the attributes we care about.
-  nsAutoString href;
-  nsAutoString feedUrl;
-  nsAutoString icon;
-  nsAutoString iconUri;
-  nsAutoString lastCharset;
-  nsAutoString keyword;
-  nsAutoString postData;
-  nsAutoString webPanel;
-  nsAutoString micsumGenURI;
-  nsAutoString generatedTitle;
-  nsAutoString dateAdded;
-  nsAutoString lastModified;
-
-  PRInt32 attrCount = node.GetAttributeCount();
-  for (PRInt32 i = 0; i < attrCount; i++) {
-    const nsAString& key = node.GetKeyAt(i);
-    nsAutoString value(node.GetValueAt(i));
-    value.Trim(kWhitespace);
-
-    if (key.LowerCaseEqualsLiteral(KEY_HREF_LOWER))
-      href = value;
-    else if (key.LowerCaseEqualsLiteral(KEY_FEEDURL_LOWER))
-      feedUrl = value;
-    else if (key.LowerCaseEqualsLiteral(KEY_ICON_LOWER))
-      icon = value;
-    else if (key.LowerCaseEqualsLiteral(KEY_ICON_URI_LOWER))
-      iconUri = value;
-    else if (key.LowerCaseEqualsLiteral(KEY_LASTCHARSET_LOWER))
-      lastCharset = value;
-    else if (key.LowerCaseEqualsLiteral(KEY_SHORTCUTURL_LOWER))
-      keyword = value;
-    else if (key.LowerCaseEqualsLiteral(KEY_POST_DATA_LOWER))
-      postData = value;
-    else if (key.LowerCaseEqualsLiteral(KEY_WEB_PANEL_LOWER))
-      webPanel = value;
-    else if (key.LowerCaseEqualsLiteral(KEY_MICSUM_GEN_URI_LOWER))
-      micsumGenURI = value;
-    else if (key.LowerCaseEqualsLiteral(KEY_GENERATED_TITLE_LOWER))
-      generatedTitle = value;
-    else if (key.LowerCaseEqualsLiteral(KEY_DATE_ADDED_LOWER))
-      dateAdded = value;
-    else if (key.LowerCaseEqualsLiteral(KEY_LAST_MODIFIED_LOWER))
-      lastModified = value;
-  }
-
-  // For feeds, get the feed URL.  If it is invalid, mPreviousFeed will be
-  // NULL and we'll create it as a normal bookmark.
-  if (!feedUrl.IsEmpty()) {
-    NS_NewURI(getter_AddRefs(frame.mPreviousFeed),
-              NS_ConvertUTF16toUTF8(feedUrl), nsnull);
-  }
-
-  // Ignore <a> tags that have no href.
-  if (href.IsEmpty()) {
-    frame.mPreviousLink = nsnull;
-    // The exception is for feeds, where the href is an optional component
-    // indicating the source web site.
-    if (!frame.mPreviousFeed)
-      return;
-  }
-  else {
-    // Save the address if it's valid.  Note that we ignore errors if this is a
-    // feed since href is optional for them.
-    nsresult rv = NS_NewURI(getter_AddRefs(frame.mPreviousLink), href, nsnull);
-    if (NS_FAILED(rv) && !frame.mPreviousFeed) {
-      frame.mPreviousLink = nsnull;
-      return;
-    }
-  }
-
-  // Save bookmark's last modified date.
-  if (!lastModified.IsEmpty()) {
-    frame.mPreviousLastModifiedDate =
-      ConvertImportedDateToInternalDate(NS_ConvertUTF16toUTF8(lastModified));
-  }
-
-  // If this is a live bookmark, we will handle it in HandleLinkEnd(), so we
-  // can skip bookmark creation.
-  if (frame.mPreviousFeed)
-    return;
-
-  // Create the bookmark.  The title is unknown for now, we will set it later.
-  nsresult rv = mBookmarksService->InsertBookmark(frame.mContainerID,
-                                                  frame.mPreviousLink,
-                                                  mBookmarksService->DEFAULT_INDEX,
-                                                  EmptyCString(),
-                                                  &frame.mPreviousId);
-  if (NS_FAILED(rv)) {
-    // If inserting bookmark failed, there's nothing more we can do.
-    NS_WARNING("InserBookmark failed");
-    return;
-  }
-  
-  // Set the date added value, if we have it.
-  if (!dateAdded.IsEmpty()) {
-    PRTime convertedDateAdded =
-      ConvertImportedDateToInternalDate(NS_ConvertUTF16toUTF8(dateAdded));
-    if (convertedDateAdded) {
-      rv = mBookmarksService->SetItemDateAdded(frame.mPreviousId, convertedDateAdded);
-      NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemDateAdded failed");
-    }
-  }
-
-  // Save the favicon.
-  if (!icon.IsEmpty() || !iconUri.IsEmpty()) {
-    nsCOMPtr<nsIURI> iconUriObject;
-    rv = NS_NewURI(getter_AddRefs(iconUriObject), iconUri);
-    if (!icon.IsEmpty() || NS_SUCCEEDED(rv)) {
-      rv = SetFaviconForURI(frame.mPreviousLink, iconUriObject, icon);
-      if (NS_FAILED(rv)) {
-        nsCAutoString warnMsg;
-        warnMsg.Append("Bookmarks Import: unable to set favicon '");
-        warnMsg.Append(NS_ConvertUTF16toUTF8(iconUri));
-        warnMsg.Append("' for page '");
-        nsCAutoString spec;
-        rv = frame.mPreviousLink->GetSpec(spec);
-        if (NS_SUCCEEDED(rv))
-          warnMsg.Append(spec);
-        warnMsg.Append("'");
-        NS_WARNING(warnMsg.get());
-      }
-    }
-  }
-
-  // Save the keyword.
-  if (!keyword.IsEmpty()) {
-    rv = mBookmarksService->SetKeywordForBookmark(frame.mPreviousId, keyword);
-    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetKeywordForBookmark failed");
-    if (NS_SUCCEEDED(rv) && !postData.IsEmpty()) {
-      rv = mAnnotationService->SetItemAnnotationString(frame.mPreviousId,
-                                                       POST_DATA_ANNO,
-                                                       postData, 0,
-                                                       nsIAnnotationService::EXPIRE_NEVER);
-      NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemAnnotationString failed");
-    }
-  }
-
-  // Set load-in-sidebar annotation for the bookmark.
-  if (webPanel.LowerCaseEqualsLiteral("true")) {
-
-    rv = mAnnotationService->SetItemAnnotationInt32(frame.mPreviousId,
-                                                    LOAD_IN_SIDEBAR_ANNO,
-                                                    1, 0,
-                                                    nsIAnnotationService::EXPIRE_NEVER);
-    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemAnnotationInt32 failed");
-  }
-
-  // Import last charset.
-  if (!lastCharset.IsEmpty()) {
-    rv = mHistoryService->SetCharsetForURI(frame.mPreviousLink,lastCharset);
-    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "setCharsetForURI failed");
-  }
-}
-
-
-// BookmarkContentSink::HandleLinkEnd
-//
-//    Saves the title for the given bookmark. This only writes the user title.
-//    Any previous title will be untouched. If this is a new entry, it will have
-//    an empty "official" title until you visit it.
-
-void
-BookmarkContentSink::HandleLinkEnd()
-{
-  nsresult rv;
-  BookmarkImportFrame& frame = CurFrame();
-  frame.mPreviousText.Trim(kWhitespace);
-
-  if (frame.mPreviousFeed) {
-    // The is a live bookmark.  We create it here since in HandleLinkBegin we
-    // don't know the title.
-    jsval livemark = livemarkInfoToJSVal(
-      0, EmptyCString(), frame.mPreviousText, frame.mContainerID,
-      mBookmarksService->DEFAULT_INDEX, frame.mPreviousFeed, frame.mPreviousLink
-    );
-
-    // Create the live bookmark.
-    rv = mLivemarkService->AddLivemark(livemark, nsnull);
-    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "AddLivemark failed!");
-
-#ifdef DEBUG_IMPORT
-    PrintNesting();
-    printf("Created livemark '%s'\n",
-           NS_ConvertUTF16toUTF8(frame.mPreviousText).get());
-#endif
-  }
-  else if (frame.mPreviousLink) {
-    // This is a common bookmark.
-#ifdef DEBUG_IMPORT
-    PrintNesting();
-    printf("Created bookmark '%s' %lld\n",
-           NS_ConvertUTF16toUTF8(frame.mPreviousText).get(), frame.mPreviousId);
-#endif
-    rv = mBookmarksService->SetItemTitle(frame.mPreviousId,
-                                         NS_ConvertUTF16toUTF8(frame.mPreviousText));
-    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemTitle failed");
-  }
-
-  // Set last modified date as the last change.
-  if (frame.mPreviousId > 0 && frame.mPreviousLastModifiedDate > 0) {
-    rv = mBookmarksService->SetItemLastModified(frame.mPreviousId,
-                                                frame.mPreviousLastModifiedDate);
-    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemLastModified failed");
-    // Note: don't clear mPreviousLastModifiedDate, because if this item has a
-    // description, we'll need to set it again.
-  }
-
-  frame.mPreviousText.Truncate();
-}
-
-
-// BookmarkContentSink::HandleSeparator
-//
-//    Inserts a separator into the current container
-void
-BookmarkContentSink::HandleSeparator(const nsIParserNode& aNode)
-{
-  BookmarkImportFrame& frame = CurFrame();
-
-  // create the separator
-
-#ifdef DEBUG_IMPORT
-  PrintNesting();
-  printf("--------\n");
-#endif
-
-  nsresult rv = mBookmarksService->InsertSeparator(frame.mContainerID,
-                                                   mBookmarksService->DEFAULT_INDEX,
-                                                   &frame.mPreviousId);
-  if (NS_FAILED(rv)) {
-    NS_WARNING("InsertSeparator failed");
-    return;
-  }
-  // Import separator title if set.
-  // Note that Places does not use separator titles, nor backup/restore them.
-  PRInt32 attrCount = aNode.GetAttributeCount();
-  for (PRInt32 i = 0; i < attrCount; i ++) {
-    const nsAString& key = aNode.GetKeyAt(i);
-
-    if (key.LowerCaseEqualsLiteral(KEY_NAME_LOWER)) {
-      nsAutoString name;
-      name = aNode.GetValueAt(i);
-      name.Trim(kWhitespace);
-      if (!name.IsEmpty()) {
-        rv = mBookmarksService->SetItemTitle(frame.mPreviousId,
-                                             NS_ConvertUTF16toUTF8(name));
-        NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemTitle failed");
-      }
-    }
-  }
-
-  // Note: we do not need to import ADD_DATE or LAST_MODIFIED for separators
-  // because pre-Places bookmarks does not support them.
-  // and we can't write them out because attributes other than NAME
-  // will make Firefox 2.x crash/hang due to bug #381129
-}
-
-
-// BookmarkContentSink::NewFrame
-//
-//    This is called when there is a new folder found. The folder takes the
-//    name from the previous frame's heading.
-
-nsresult
-BookmarkContentSink::NewFrame()
-{
-  nsresult rv;
-
-  PRInt64 ourID = 0;
-  nsString containerName;
-  BookmarkImportFrame::ContainerType containerType;
-  BookmarkImportFrame& frame = CurFrame();
-  frame.ConsumeHeading(&containerName, &containerType);
-
-  bool updateFolder = false;
-  
-  switch (containerType) {
-    case BookmarkImportFrame::Container_Normal:
-      // append a new folder
-      rv = mBookmarksService->CreateFolder(CurFrame().mContainerID,
-                                           NS_ConvertUTF16toUTF8(containerName),
-                                           mBookmarksService->DEFAULT_INDEX, 
-                                           &ourID);
-      NS_ENSURE_SUCCESS(rv, rv);
-      break;
-    case BookmarkImportFrame::Container_Places:
-      // places root, never reparent here, when we're building the initial
-      // hierarchy, it will only be defined at the top level
-      rv = mBookmarksService->GetPlacesRoot(&ourID);
-      NS_ENSURE_SUCCESS(rv, rv);
-      break;
-    case BookmarkImportFrame::Container_Menu:
-      // menu folder
-      rv = mBookmarksService->GetBookmarksMenuFolder(&ourID);
-      NS_ENSURE_SUCCESS(rv, rv);
-      if (mAllowRootChanges)
-        updateFolder = true;
-      break;
-    case BookmarkImportFrame::Container_Unfiled:
-      // unfiled bookmarks folder
-      rv = mBookmarksService->GetUnfiledBookmarksFolder(&ourID);
-      NS_ENSURE_SUCCESS(rv, rv);
-      if (mAllowRootChanges)
-        updateFolder = true;
-      break;
-    case BookmarkImportFrame::Container_Toolbar:
-      // get toolbar folder
-      rv = mBookmarksService->GetToolbarFolder(&ourID);
-      NS_ENSURE_SUCCESS(rv, rv);
-      
-      break;
-    default:
-      NS_NOTREACHED("Unknown container type");
-  }
-
-#ifdef DEBUG_IMPORT
-  PrintNesting();
-  printf("Folder %lld \'%s\'", ourID, NS_ConvertUTF16toUTF8(containerName).get());
-#endif
-
-  if (updateFolder) {
-    // move the menu folder to the current position
-    rv = mBookmarksService->MoveItem(ourID, CurFrame().mContainerID, -1);
-    NS_ENSURE_SUCCESS(rv, rv);
-    rv = mBookmarksService->SetItemTitle(ourID, NS_ConvertUTF16toUTF8(containerName));
-    NS_ENSURE_SUCCESS(rv, rv);
-#ifdef DEBUG_IMPORT
-    printf(" [reparenting]");
-#endif
-  }
-
-#ifdef DEBUG_IMPORT
-  printf("\n");
-#endif
-
-  if (frame.mPreviousDateAdded > 0) {
-    rv = mBookmarksService->SetItemDateAdded(ourID, frame.mPreviousDateAdded);
-    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemDateAdded failed");
-    frame.mPreviousDateAdded = 0;
-  }
-  if (frame.mPreviousLastModifiedDate > 0) {
-    rv = mBookmarksService->SetItemLastModified(ourID, frame.mPreviousLastModifiedDate);
-    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SetItemLastModified failed");
-    // don't clear last-modified, in case there's a description
-  }
-
-  frame.mPreviousId = ourID;
-
-  if (!mFrames.AppendElement(BookmarkImportFrame(ourID)))
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  return NS_OK;
-}
-
-
-nsresult
-BookmarkContentSink::PopFrame()
-{
-  // we must always have one frame
-  if (mFrames.Length() <= 1) {
-    NS_NOTREACHED("Trying to complete more bookmark folders than you started");
-    return NS_ERROR_FAILURE;
-  }
-  mFrames.RemoveElementAt(mFrames.Length() - 1);
-  return NS_OK;
-}
-
-
-// BookmarkContentSink::SetFaviconForURI
-//
-//    aData is a string that is a data URI for the favicon. Our job is to
-//    decode it and store it in the favicon service.
-//
-//    When aIconURI is non-null, we will use that as the URI of the favicon
-//    when storing in the favicon service.
-//
-//    When aIconURI is null, we have to make up a URI for this favicon so that
-//    it can be stored in the service. The real one will be set the next time
-//    the user visits the page. Our made up one should get expired when the
-//    page no longer references it.
-nsresult
-BookmarkContentSink::SetFaviconForURI(nsIURI* aPageURI, nsIURI* aIconURI,
-                                      const nsString& aData)
-{
-  nsresult rv;
-  static PRUint32 serialNumber = 0; // for made-up favicon URIs
-
-  nsCOMPtr<nsIFaviconService> faviconService =
-    do_GetService(NS_FAVICONSERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(faviconService, NS_ERROR_OUT_OF_MEMORY);
-
-  // if the input favicon URI is a chrome: URI, then we just save it and don't
-  // worry about data
-  if (aIconURI) {
-    nsCString faviconScheme;
-    aIconURI->GetScheme(faviconScheme);
-    if (faviconScheme.EqualsLiteral("chrome")) {
-      return faviconService->SetFaviconUrlForPage(aPageURI, aIconURI);
-    }
-  }
-
-  // some bookmarks have placeholder URIs that contain just "data:"
-  // ignore these
-  if (aData.Length() <= 5)
-    return NS_OK;
-
-  nsCOMPtr<nsIURI> faviconURI;
-  if (aIconURI) {
-    faviconURI = aIconURI;
-  }
-  else {
-    // make up favicon URL
-    nsCAutoString faviconSpec;
-    faviconSpec.AssignLiteral("http://www.mozilla.org/2005/made-up-favicon/");
-    faviconSpec.AppendInt(serialNumber);
-    faviconSpec.AppendLiteral("-");
-    char buf[32];
-    PR_snprintf(buf, sizeof(buf), "%lld", PR_Now());
-    faviconSpec.Append(buf);
-    rv = NS_NewURI(getter_AddRefs(faviconURI), faviconSpec);
-    if (NS_FAILED(rv)) {
-      nsCAutoString warnMsg;
-      warnMsg.Append("Bookmarks Import: Unable to make up new favicon '");
-      warnMsg.Append(faviconSpec);
-      warnMsg.Append("' for page '");
-      nsCAutoString spec;
-      rv = aPageURI->GetSpec(spec);
-      if (NS_SUCCEEDED(rv))
-        warnMsg.Append(spec);
-      warnMsg.Append("'");
-      NS_WARNING(warnMsg.get());
-      return NS_OK;
-    }
-    serialNumber++;
-  }
-
-  // save the favicon data
-  // This could fail if the favicon is bigger than defined limit, in such a
-  // case data will not be saved to the db but we will still continue.
-  (void) faviconService->SetFaviconDataFromDataURL(faviconURI, aData, 0);
-
-  rv = faviconService->SetFaviconUrlForPage(aPageURI, faviconURI);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK; 
-}
-
-
-// Converts a string date in seconds to an int date in microseconds
-PRTime
-BookmarkContentSink::ConvertImportedDateToInternalDate(const nsACString& aDate) {
-  PRTime convertedDate = 0;
-  if (!aDate.IsEmpty()) {
-    nsresult rv;
-    convertedDate = PromiseFlatCString(aDate).ToInteger(&rv);
-    if (NS_SUCCEEDED(rv)) {
-      convertedDate *= 1000000; // in bookmarks.html this value is in seconds, not microseconds
-    }
-    else {
-      convertedDate = 0;
-    }
-  }
-  return convertedDate;
-}
-
-
 // SyncChannelStatus
 //
 //    If a function returns an error, we need to set the channel status to be
 //    the same, but only if the channel doesn't have its own error. This returns
 //    the error code that should be sent to OnStopRequest.
 static nsresult
 SyncChannelStatus(nsIChannel* channel, nsresult status)
 {
@@ -1469,42 +411,42 @@ WriteDateAttribute(const char aAttribute
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 
-// nsPlacesImportExportService::WriteContainer
+// nsPlacesExportService::WriteContainer
 //
 //    Writes out all the necessary parts of a bookmarks folder.
 nsresult
-nsPlacesImportExportService::WriteContainer(nsINavHistoryResultNode* aFolder,
+nsPlacesExportService::WriteContainer(nsINavHistoryResultNode* aFolder,
                                             const nsACString& aIndent,
                                             nsIOutputStream* aOutput)
 {
   nsresult rv = WriteContainerHeader(aFolder, aIndent, aOutput);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = WriteContainerPrologue(aIndent, aOutput);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = WriteContainerContents(aFolder, aIndent, aOutput);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = WriteContainerEpilogue(aIndent, aOutput);
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
 
-// nsPlacesImportExportService::WriteContainerHeader
+// nsPlacesExportService::WriteContainerHeader
 //
 //    This writes '<DL><H3>Title</H3>'
 //    Remember folders can also have favicons, which we put in the H3 tag
 nsresult
-nsPlacesImportExportService::WriteContainerHeader(nsINavHistoryResultNode* aFolder,
+nsPlacesExportService::WriteContainerHeader(nsINavHistoryResultNode* aFolder,
                                                   const nsACString& aIndent,
                                                   nsIOutputStream* aOutput)
 {
   PRUint32 dummy;
   nsresult rv;
 
   // indent
   if (!aIndent.IsEmpty()) {
@@ -1590,21 +532,21 @@ nsPlacesImportExportService::WriteContai
   // description
   rv = WriteDescription(folderId, nsINavBookmarksService::TYPE_FOLDER, aOutput);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return rv;
 }
 
 
-// nsPlacesImportExportService::WriteTitle
+// nsPlacesExportService::WriteTitle
 //
 //    Retrieves, escapes and writes the title to the stream.
 nsresult
-nsPlacesImportExportService::WriteTitle(nsINavHistoryResultNode* aItem,
+nsPlacesExportService::WriteTitle(nsINavHistoryResultNode* aItem,
                                         nsIOutputStream* aOutput)
 {
   // XXX Bug 381767 - support titles for separators
   PRUint32 type = 0;
   nsresult rv = aItem->GetType(&type);
   NS_ENSURE_SUCCESS(rv, rv);
   if (type == nsINavHistoryResultNode::RESULT_TYPE_SEPARATOR)
     return NS_ERROR_INVALID_ARG;
@@ -1619,21 +561,21 @@ nsPlacesImportExportService::WriteTitle(
     rv = aOutput->Write(escapedTitle, strlen(escapedTitle), &dummy);
     nsMemory::Free(escapedTitle);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
 
-// nsPlacesImportExportService::WriteDescription
+// nsPlacesExportService::WriteDescription
 //
 //  Write description out for all item types.
 nsresult
-nsPlacesImportExportService::WriteDescription(PRInt64 aItemId, PRInt32 aType,
+nsPlacesExportService::WriteDescription(PRInt64 aItemId, PRInt32 aType,
                                               nsIOutputStream* aOutput)
 {
   bool hasDescription = false;
   nsresult rv = mAnnotationService->ItemHasAnnotation(aItemId,
                                                       DESCRIPTION_ANNO,
                                                       &hasDescription);
   if (NS_FAILED(rv) || !hasDescription)
     return rv;
@@ -1659,17 +601,17 @@ nsPlacesImportExportService::WriteDescri
   }
   return NS_OK;
 }
 
 // nsBookmarks::WriteItem
 //
 //    "<DT><A HREF="..." ICON="...">Name</A>"
 nsresult
-nsPlacesImportExportService::WriteItem(nsINavHistoryResultNode* aItem,
+nsPlacesExportService::WriteItem(nsINavHistoryResultNode* aItem,
                                        const nsACString& aIndent,
                                        nsIOutputStream* aOutput)
 {
   // before doing any attempt to write the item check that uri is valid, if the
   // item has a bad uri we skip it silently, otherwise we could stop while
   // exporting, generating a corrupt file.
   nsCAutoString uri;
   nsresult rv = aItem->GetUri(uri);
@@ -1819,17 +761,17 @@ nsPlacesImportExportService::WriteItem(n
 }
 
 
 // WriteLivemark
 //
 //    Similar to WriteItem, this has an additional FEEDURL attribute and
 //    the HREF is optional and points to the source page.
 nsresult
-nsPlacesImportExportService::WriteLivemark(nsINavHistoryResultNode* aFolder, const nsACString& aIndent,
+nsPlacesExportService::WriteLivemark(nsINavHistoryResultNode* aFolder, const nsACString& aIndent,
                               nsIOutputStream* aOutput)
 {
   PRUint32 dummy;
   nsresult rv;
 
   // indent
   if (!aIndent.IsEmpty()) {
     rv = aOutput->Write(PromiseFlatCString(aIndent).get(), aIndent.Length(), &dummy);
@@ -1891,21 +833,21 @@ nsPlacesImportExportService::WriteLivema
   // description
   rv = WriteDescription(folderId, nsINavBookmarksService::TYPE_BOOKMARK, aOutput);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 
-// nsPlacesImportExportService::WriteSeparator
+// nsPlacesExportService::WriteSeparator
 //
 //    "<HR NAME="...">"
 nsresult
-nsPlacesImportExportService::WriteSeparator(nsINavHistoryResultNode* aItem,
+nsPlacesExportService::WriteSeparator(nsINavHistoryResultNode* aItem,
                                             const nsACString& aIndent,
                                             nsIOutputStream* aOutput)
 {
   PRUint32 dummy;
   nsresult rv;
 
   // indent
   if (!aIndent.IsEmpty()) {
@@ -1970,22 +912,22 @@ WriteEscapedUrl(const nsCString& aString
     escaped.Cut(offset, 1);
     escaped.Insert(NS_LITERAL_CSTRING("%22"), offset);
   }
   PRUint32 dummy;
   return aOutput->Write(escaped.get(), escaped.Length(), &dummy);
 }
 
 
-// nsPlacesImportExportService::WriteContainerContents
+// nsPlacesExportService::WriteContainerContents
 //
 //    The indent here is the indent of the parent. We will add an additional
 //    indent before writing data.
 nsresult
-nsPlacesImportExportService::WriteContainerContents(nsINavHistoryResultNode* aFolder,
+nsPlacesExportService::WriteContainerContents(nsINavHistoryResultNode* aFolder,
                                                     const nsACString& aIndent,
                                                     nsIOutputStream* aOutput)
 {
   nsCAutoString myIndent(aIndent);
   myIndent.Append(kIndent);
 
   PRInt64 folderId;
   nsresult rv = aFolder->GetItemId(&folderId);
@@ -2033,257 +975,19 @@ nsPlacesImportExportService::WriteContai
       rv = WriteItem(child, myIndent, aOutput);
     }
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
 
-// NotifyImportObservers
-//
-//    Notifies bookmarks-restore observers using nsIObserverService.  This
-//    function is void and we simply return on failure because we don't want
-//    the import itself to fail if notifying observers does.
-static void
-NotifyImportObservers(const char* aTopic,
-                      PRInt64 aFolderId,
-                      bool aIsInitialImport)
-{
-  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-  if (!obs)
-    return;
-
-  nsCOMPtr<nsISupports> folderIdSupp = nsnull;
-  if (aFolderId > 0) {
-    nsCOMPtr<nsISupportsPRInt64> folderIdInt =
-      do_CreateInstance(NS_SUPPORTS_PRINT64_CONTRACTID);
-    if (!folderIdInt)
-      return;
-
-    if (NS_FAILED(folderIdInt->SetData(aFolderId)))
-      return;
-
-    folderIdSupp = do_QueryInterface(folderIdInt);
-  }
-
-  obs->NotifyObservers(folderIdSupp,
-                       aTopic,
-                       (aIsInitialImport ? RESTORE_INITIAL_NSIOBSERVER_DATA
-                                         : RESTORE_NSIOBSERVER_DATA).get());
-}
-
-
-NS_IMETHODIMP
-nsPlacesImportExportService::ImportHTMLFromFile(nsILocalFile* aFile,
-                                                bool aIsInitialImport)
-{
-  NotifyImportObservers(RESTORE_BEGIN_NSIOBSERVER_TOPIC, -1, aIsInitialImport);
-
-  // this version is exposed on the interface and disallows changing of roots
-  nsresult rv = ImportHTMLFromFileInternal(aFile,
-                                           false,
-                                           0,
-                                           aIsInitialImport);
-
-  if (NS_FAILED(rv)) {
-    NotifyImportObservers(RESTORE_FAILED_NSIOBSERVER_TOPIC,
-                          -1,
-                          aIsInitialImport);
-  }
-  else {
-    NotifyImportObservers(RESTORE_SUCCESS_NSIOBSERVER_TOPIC,
-                          -1,
-                          aIsInitialImport);
-  }
-
-  return rv;
-}
-
 
 NS_IMETHODIMP
-nsPlacesImportExportService::ImportHTMLFromURI(nsIURI* aURI,
-                                               bool aIsInitialImport)
-{
-  NotifyImportObservers(RESTORE_BEGIN_NSIOBSERVER_TOPIC, -1, aIsInitialImport);
-
-  // this version is exposed on the interface and disallows changing of roots
-  nsresult rv = ImportHTMLFromURIInternal(aURI,
-                                          false,
-                                          0,
-                                          aIsInitialImport);
-
-  if (NS_FAILED(rv)) {
-    NotifyImportObservers(RESTORE_FAILED_NSIOBSERVER_TOPIC,
-                          -1,
-                          aIsInitialImport);
-  }
-  else {
-    NotifyImportObservers(RESTORE_SUCCESS_NSIOBSERVER_TOPIC,
-                          -1,
-                          aIsInitialImport);
-  }
-
-  return rv;
-}
-
-
-nsresult
-nsPlacesImportExportService::ImportHTMLFromFileInternal(nsILocalFile* aFile,
-                                                        bool aAllowRootChanges,
-                                                        PRInt64 aFolder,
-                                                        bool aIsImportDefaults)
-{
-  nsresult rv;
-
-  nsCOMPtr<nsIFile> file = do_QueryInterface(aFile);
-  NS_ENSURE_STATE(file);
-
-#ifdef DEBUG_IMPORT
-  nsAutoString path;
-  file->GetPath(path);
-  printf("\nImporting %s\n", NS_ConvertUTF16toUTF8(path).get());
-#endif
-
-  // Confirm file to be imported exists.
-  bool exists;
-  rv = file->Exists(&exists);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (!exists) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  nsCOMPtr<nsIIOService> ioservice = do_GetIOService(&rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsIURI> fileURI;
-  rv = ioservice->NewFileURI(file, getter_AddRefs(fileURI));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return ImportHTMLFromURIInternal(fileURI, aAllowRootChanges, aFolder, aIsImportDefaults);
-}
-
-nsresult
-nsPlacesImportExportService::ImportHTMLFromURIInternal(nsIURI* aURI,
-                                                       bool aAllowRootChanges,
-                                                       PRInt64 aFolder,
-                                                       bool aIsImportDefaults)
-{
-  nsresult rv = EnsureServiceState();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID);
-  NS_ENSURE_TRUE(parser, NS_ERROR_OUT_OF_MEMORY);
-
-  nsCOMPtr<BookmarkContentSink> sink = new BookmarkContentSink();
-  NS_ENSURE_TRUE(sink, NS_ERROR_OUT_OF_MEMORY);
-  rv = sink->Init(aAllowRootChanges, aFolder, aIsImportDefaults);
-  NS_ENSURE_SUCCESS(rv, rv);
-  parser->SetContentSink(sink);
-
-  // Set the content type on the channel, otherwise the default "unknown" type
-  // will confuse the parser.
-  nsCOMPtr<nsIIOService> ioservice = do_GetIOService(&rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = ioservice->NewChannelFromURI(aURI, getter_AddRefs(mImportChannel));
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = mImportChannel->SetContentType(NS_LITERAL_CSTRING("text/html"));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Init parser.
-  rv = parser->Parse(aURI, nsnull);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Run the import in batch mode, so it will be executed in a transaction
-  // and will be faster.
-  mIsImportDefaults = aIsImportDefaults;
-  mBookmarksService->RunInBatchMode(this, parser);
-  mImportChannel = nsnull;
-
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP
-nsPlacesImportExportService::RunBatched(nsISupports* aUserData)
-{
-  nsresult rv;
-  if (mIsImportDefaults) {
-    PRInt64 bookmarksMenuFolder;
-    rv = mBookmarksService->GetBookmarksMenuFolder(&bookmarksMenuFolder);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = mBookmarksService->RemoveFolderChildren(bookmarksMenuFolder);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    PRInt64 toolbarFolder;
-    rv = mBookmarksService->GetToolbarFolder(&toolbarFolder);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = mBookmarksService->RemoveFolderChildren(toolbarFolder);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    PRInt64 unfiledBookmarksFolder;
-    rv = mBookmarksService->GetUnfiledBookmarksFolder(&unfiledBookmarksFolder);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = mBookmarksService->RemoveFolderChildren(unfiledBookmarksFolder);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  // streams
-  nsCOMPtr<nsIInputStream> stream;
-  rv = mImportChannel->Open(getter_AddRefs(stream));
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsIInputStream> bufferedstream;
-  rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedstream), stream, 4096);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // feed the parser the data
-  // Note: on error, we always need to set the channel's status to be the
-  // same, and to always call OnStopRequest with the channel error.
-  nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(aUserData, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = listener->OnStartRequest(mImportChannel, nsnull);
-  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "OnStartRequest failed");
-  rv = SyncChannelStatus(mImportChannel, rv);
-  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "SyncChannelStatus failed");
-
-  while (NS_SUCCEEDED(rv))
-  {
-    PRUint32 available;
-    rv = bufferedstream->Available(&available);
-    if (rv == NS_BASE_STREAM_CLOSED) {
-      rv = NS_OK;
-      available = 0;
-    }
-    if (NS_FAILED(rv)) {
-      mImportChannel->Cancel(rv);
-      break;
-    }
-    if (!available)
-      break; // blocking input stream has none available when done
-
-    rv = listener->OnDataAvailable(mImportChannel, nsnull, bufferedstream, 0,
-                                   available);
-    if (NS_FAILED(rv))
-      break;
-    rv = SyncChannelStatus(mImportChannel, rv);
-    if (NS_FAILED(rv))
-      break;
-  }
-
-  rv = listener->OnStopRequest(mImportChannel, nsnull, rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP
-nsPlacesImportExportService::ExportHTMLToFile(nsILocalFile* aBookmarksFile)
+nsPlacesExportService::ExportHTMLToFile(nsILocalFile* aBookmarksFile)
 {
   NS_ENSURE_ARG(aBookmarksFile);
 
 #ifdef DEBUG_EXPORT
   nsAutoString path;
   aBookmarksFile->GetPath(path);
   printf("\nExporting %s\n", NS_ConvertUTF16toUTF8(path).get());
 
@@ -2421,17 +1125,17 @@ nsPlacesImportExportService::ExportHTMLT
   printf("\nTotal time in seconds: %lld\n", (PR_Now() - startTime)/1000000);
 #endif
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
-nsPlacesImportExportService::BackupBookmarksFile()
+nsPlacesExportService::BackupBookmarksFile()
 {
   nsresult rv = EnsureServiceState();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // get bookmarks file
   nsCOMPtr<nsIFile> bookmarksFileDir;
   rv = NS_GetSpecialDirectory(NS_APP_BOOKMARKS_50_FILE,
                               getter_AddRefs(bookmarksFileDir));
rename from toolkit/components/places/nsPlacesImportExportService.h
rename to toolkit/components/places/nsPlacesExportService.h
--- a/toolkit/components/places/nsPlacesImportExportService.h
+++ b/toolkit/components/places/nsPlacesExportService.h
@@ -1,60 +1,51 @@
-#ifndef nsPlacesImportExportService_h__
-#define nsPlacesImportExportService_h__
+#ifndef nsPlacesExportService_h_
+#define nsPlacesExportService_h_
 
 #include "nsIPlacesImportExportService.h"
 
 #include "nsCOMPtr.h"
 #include "nsILocalFile.h"
 #include "nsIOutputStream.h"
 #include "nsIFaviconService.h"
 #include "nsIAnnotationService.h"
 #include "mozIAsyncLivemarks.h"
 #include "nsINavHistoryService.h"
 #include "nsINavBookmarksService.h"
 #include "nsIChannel.h"
 
-class nsPlacesImportExportService : public nsIPlacesImportExportService,
-                                    public nsINavHistoryBatchCallback
+class nsPlacesExportService : public nsIPlacesImportExportService
 {
   public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIPLACESIMPORTEXPORTSERVICE
-    NS_DECL_NSINAVHISTORYBATCHCALLBACK
-    nsPlacesImportExportService();
+    nsPlacesExportService();
 
   /**
    * Obtains the service's object.
    */
-  static nsPlacesImportExportService* GetSingleton();
+  static nsPlacesExportService* GetSingleton();
 
   /**
    * Initializes the service's object.  This should only be called once.
    */
   nsresult Init();
 
   private:
-    static nsPlacesImportExportService* gImportExportService;
-    virtual ~nsPlacesImportExportService();
+    static nsPlacesExportService* gExportService;
+    virtual ~nsPlacesExportService();
 
   protected:
     nsCOMPtr<nsIFaviconService> mFaviconService;
     nsCOMPtr<nsIAnnotationService> mAnnotationService;
     nsCOMPtr<nsINavBookmarksService> mBookmarksService;
     nsCOMPtr<nsINavHistoryService> mHistoryService;
     nsCOMPtr<mozIAsyncLivemarks> mLivemarkService;
 
-    nsCOMPtr<nsIChannel> mImportChannel;
-    bool mIsImportDefaults;
-
-    nsresult ImportHTMLFromFileInternal(nsILocalFile* aFile, bool aAllowRootChanges,
-                                       PRInt64 aFolder, bool aIsImportDefaults);
-    nsresult ImportHTMLFromURIInternal(nsIURI* aURI, bool aAllowRootChanges,
-                                       PRInt64 aFolder, bool aIsImportDefaults);
     nsresult WriteContainer(nsINavHistoryResultNode* aFolder, const nsACString& aIndent, nsIOutputStream* aOutput);
     nsresult WriteContainerHeader(nsINavHistoryResultNode* aFolder, const nsACString& aIndent, nsIOutputStream* aOutput);
     nsresult WriteTitle(nsINavHistoryResultNode* aItem, nsIOutputStream* aOutput);
     nsresult WriteItem(nsINavHistoryResultNode* aItem, const nsACString& aIndent, nsIOutputStream* aOutput);
     nsresult WriteLivemark(nsINavHistoryResultNode* aFolder, const nsACString& aIndent, nsIOutputStream* aOutput);
     nsresult WriteContainerContents(nsINavHistoryResultNode* aFolder, const nsACString& aIndent, nsIOutputStream* aOutput);
     nsresult WriteSeparator(nsINavHistoryResultNode* aItem, const nsACString& aIndent, nsIOutputStream* aOutput);
     nsresult WriteDescription(PRInt64 aId, PRInt32 aType, nsIOutputStream* aOutput);
@@ -64,9 +55,9 @@ class nsPlacesImportExportService : publ
       NS_ENSURE_STATE(mFaviconService);
       NS_ENSURE_STATE(mAnnotationService);
       NS_ENSURE_STATE(mBookmarksService);
       NS_ENSURE_STATE(mLivemarkService);
       return NS_OK;
     }
 };
 
-#endif // nsPlacesImportExportService_h__
+#endif // nsPlacesExportService_h_
--- a/toolkit/components/places/nsPlacesModule.cpp
+++ b/toolkit/components/places/nsPlacesModule.cpp
@@ -1,17 +1,17 @@
 #include "mozilla/ModuleUtils.h"
 #include "nsIClassInfoImpl.h"
 
 #include "nsAnnoProtocolHandler.h"
 #include "nsAnnotationService.h"
 #include "nsNavHistory.h"
 #include "nsNavBookmarks.h"
 #include "nsFaviconService.h"
-#include "nsPlacesImportExportService.h"
+#include "nsPlacesExportService.h"
 #include "History.h"
 #include "nsDocShellCID.h"
 
 #ifdef MOZ_ANDROID_HISTORY
 #include "nsAndroidHistory.h"
 #endif
 
 using namespace mozilla::places;
@@ -19,18 +19,18 @@ using namespace mozilla::places;
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsNavHistory,
                                          nsNavHistory::GetSingleton)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsAnnotationService,
                                          nsAnnotationService::GetSingleton)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsNavBookmarks,
                                          nsNavBookmarks::GetSingleton)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsFaviconService,
                                          nsFaviconService::GetSingleton)
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsPlacesImportExportService,
-                                         nsPlacesImportExportService::GetSingleton)
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsPlacesExportService,
+                                         nsPlacesExportService::GetSingleton)
 #ifdef MOZ_ANDROID_HISTORY
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsAndroidHistory, nsAndroidHistory::GetSingleton)
 #else
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(History, History::GetSingleton)
 #endif
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsAnnoProtocolHandler)
 NS_DEFINE_NAMED_CID(NS_NAVHISTORYSERVICE_CID);
@@ -52,17 +52,17 @@ const mozilla::Module::CIDEntry kPlacesC
   { &kNS_ANNOPROTOCOLHANDLER_CID, false, NULL, nsAnnoProtocolHandlerConstructor },
   { &kNS_NAVBOOKMARKSSERVICE_CID, false, NULL, nsNavBookmarksConstructor },
   { &kNS_FAVICONSERVICE_CID, false, NULL, nsFaviconServiceConstructor },
 #ifdef MOZ_ANDROID_HISTORY
   { &kNS_ANDROIDHISTORY_CID, false, NULL, nsAndroidHistoryConstructor },
 #else
   { &kNS_HISTORYSERVICE_CID, false, NULL, HistoryConstructor },
 #endif
-  { &kNS_PLACESIMPORTEXPORTSERVICE_CID, false, NULL, nsPlacesImportExportServiceConstructor },
+  { &kNS_PLACESIMPORTEXPORTSERVICE_CID, false, NULL, nsPlacesExportServiceConstructor },
   { NULL }
 };
 
 const mozilla::Module::ContractIDEntry kPlacesContracts[] = {
   { NS_NAVHISTORYSERVICE_CONTRACTID, &kNS_NAVHISTORYSERVICE_CID },
   { NS_GLOBALHISTORY2_CONTRACTID, &kNS_NAVHISTORYSERVICE_CID },
   { NS_ANNOTATIONSERVICE_CONTRACTID, &kNS_ANNOTATIONSERVICE_CID },
   { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "moz-anno", &kNS_ANNOPROTOCOLHANDLER_CID },