Bug 376008 bookmarks html import/export needs to move to /browser (r=mano)
authordietrich@mozilla.com
Thu, 03 May 2007 01:56:41 -0700
changeset 1043 b09c9394329748be5732bd1cda7a016ad9ac8223
parent 1042 53026308a40b420229df4fc60b520967605b539d
child 1044 dfb7563651dfbdcf9d8313f47620c37305ad4ecc
push idunknown
push userunknown
push dateunknown
reviewersmano
bugs376008
milestone1.9a5pre
Bug 376008 bookmarks html import/export needs to move to /browser (r=mano)
browser/app/profile/firefox.js
browser/components/build/Makefile.in
browser/components/build/nsBrowserCompsCID.h
browser/components/build/nsModule.cpp
browser/components/migration/src/Makefile.in
browser/components/migration/src/nsBrowserProfileMigratorUtils.cpp
browser/components/nsBrowserGlue.js
browser/components/places/Makefile.in
browser/components/places/content/places.js
browser/components/places/public/Makefile.in
browser/components/places/public/nsIPlacesImportExportService.idl
browser/components/places/src/Makefile.in
browser/components/places/src/nsPlacesImportExportService.cpp
browser/components/places/src/nsPlacesImportExportService.h
browser/components/places/tests/Makefile.in
browser/components/places/tests/unit/bookmarks.preplaces.html
browser/components/places/tests/unit/head_bookmarks.js
browser/components/places/tests/unit/tail_bookmarks.js
browser/components/places/tests/unit/test_bookmarks_html.js
browser/installer/unix/packages-static
browser/installer/windows/packages-static
browser/locales/en-US/chrome/browser/places/default_places.html
toolkit/components/places/public/nsIAnnotationService.idl
toolkit/components/places/public/nsINavBookmarksService.idl
toolkit/components/places/src/Makefile.in
toolkit/components/places/src/nsAnnotationService.cpp
toolkit/components/places/src/nsAnnotationService.h
toolkit/components/places/src/nsBookmarksHTML.cpp
toolkit/components/places/src/nsNavBookmarks.cpp
toolkit/components/places/src/nsNavBookmarks.h
toolkit/components/places/tests/bookmarks/test_bookmarks.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -535,8 +535,11 @@ pref("browser.sessionstore.postdata", 0)
 // on which sites to save text data, POSTDATA and cookies
 // 0 = everywhere, 1 = unencrypted sites, 2 = nowhere
 pref("browser.sessionstore.privacy_level", 1);
 // how many tabs can be reopened (per window)
 pref("browser.sessionstore.max_tabs_undo", 10);
 
 // allow META refresh by default
 pref("accessibility.blockautorefresh", false);
+
+// import bookmarks.html into Places bookmarks
+pref("browser.places.importBookmarksHTML", true);
--- a/browser/components/build/Makefile.in
+++ b/browser/components/build/Makefile.in
@@ -28,53 +28,65 @@ REQUIRES = \
 	necko \
 	nkcache \
 	txmgr \
 	chardet \
 	bookmarks \
 	migration \
 	shellservice \
 	xulapp \
+	places \
+	browserplaces \
 	$(NULL)
 
 EXPORTS = nsBrowserCompsCID.h
 
 CPPSRCS = nsModule.cpp \
           $(NULL)
 
 ifeq ($(OS_ARCH),WINNT)
 DEFINES += -DPSTOREC_DLL=\"$(subst \,\\,$(WINDIR))\\system32\\pstorec.dll\"
 
 OS_LIBS	+= $(call EXPAND_LIBNAME,ole32 shell32)
 endif
 
 LOCAL_INCLUDES = \
-	-I$(srcdir)/../bookmarks/src \
 	-I$(srcdir)/../migration/src \
 	-I$(srcdir)/../shell/src \
 	-I$(srcdir)/../feeds/src \
 	$(NULL)
 
+ifndef MOZ_PLACES_BOOKMARKS
+LOCAL_INCLUDES += -I$(srcdir)/../bookmarks/src
+else
+LOCAL_INCLUDES += -I$(srcdir)/../places/src
+endif
+
 ifeq ($(OS_ARCH),WINNT)
 OS_LIBS += $(call EXPAND_LIBNAME,version)
 endif
 
 SHARED_LIBRARY_LIBS = \
 	../migration/src/$(LIB_PREFIX)migration_s.$(LIB_SUFFIX) \
 	../feeds/src/$(LIB_PREFIX)browser_feeds_s.$(LIB_SUFFIX) \
 	$(NULL)
 
 ifneq (,$(filter windows mac cocoa gtk2, $(MOZ_WIDGET_TOOLKIT)))
-SHARED_LIBRARY_LIBS += ../shell/src/$(LIB_PREFIX)shellservice_s.$(LIB_SUFFIX)
+SHARED_LIBRARY_LIBS += ../shell/src/$(LIB_PREFIX)shellservice_s.$(LIB_SUFFIX) \
+	$(NULL)
 endif
 
 ifndef MOZ_PLACES_BOOKMARKS
 SHARED_LIBRARY_LIBS += \
 	../bookmarks/src/$(LIB_PREFIX)bookmarks_s.$(LIB_SUFFIX) \
 	$(NULL)
+else
+SHARED_LIBRARY_LIBS += \
+	../places/src/$(LIB_PREFIX)browserplaces_s.$(LIB_SUFFIX) \
+	$(NULL)
 endif
 
 ifdef MOZ_SAFE_BROWSING
 REQUIRES += safebrowsing
 LOCAL_INCLUDES += -I$(srcdir)/../safebrowsing/src
 SHARED_LIBRARY_LIBS += ../safebrowsing/src/$(LIB_PREFIX)safebrowsing_s.$(LIB_SUFFIX)
 endif
 
--- a/browser/components/build/nsBrowserCompsCID.h
+++ b/browser/components/build/nsBrowserCompsCID.h
@@ -91,8 +91,15 @@
 #define NS_ABOUTFEEDS_CID \
 { 0x12ff56ec, 0x58be, 0x402c, { 0xb0, 0x57, 0x1, 0xf9, 0x61, 0xde, 0x96, 0x9b } }
 
 #define NS_DOCNAVSTARTPROGRESSLISTENER_CID \
 { 0x7baf8179, 0xa4fd, 0x4bc0, { 0xbe, 0x43, 0xa9, 0xb1, 0x22, 0xc5, 0xde, 0xb6 } }
 
 #define NS_DOCNAVSTARTPROGRESSLISTENER_CONTRACTID \
   "@mozilla.org/browser/safebrowsing/navstartlistener;1"
+
+// 6fb0c970-e1b1-11db-8314-0800200c9a66
+#define NS_PLACESIMPORTEXPORTSERVICE_CID \
+{ 0x6fb0c970, 0xe1b1, 0x11db, { 0x83, 0x14, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }
+
+#define NS_PLACESIMPORTEXPORTSERVICE_CONTRACTID \
+  "@mozilla.org/browser/places/import-export-service;1"
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -37,16 +37,18 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIGenericFactory.h"
 
 #include "nsBrowserCompsCID.h"
 #ifndef MOZ_PLACES_BOOKMARKS
 #include "nsBookmarksService.h"
 #include "nsForwardProxyDataSource.h"
+#else
+#include "nsPlacesImportExportService.h"
 #endif
 #ifdef XP_WIN
 #include "nsWindowsShellService.h"
 #elif defined(XP_MACOSX)
 #include "nsMacShellService.h"
 #elif defined(MOZ_WIDGET_GTK2)
 #include "nsGNOMEShellService.h"
 #endif
@@ -76,16 +78,18 @@
 #include "nsDocNavStartProgressListener.h"
 #endif
 
 /////////////////////////////////////////////////////////////////////////////
 
 #ifndef MOZ_PLACES_BOOKMARKS
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsBookmarksService, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsForwardProxyDataSource, Init)
+#else
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsPlacesImportExportService)
 #endif
 #ifdef XP_WIN
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindowsShellService)
 #elif defined(XP_MACOSX)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMacShellService)
 #elif defined(MOZ_WIDGET_GTK2)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGNOMEShellService, Init)
 #endif
@@ -147,16 +151,23 @@ static const nsModuleComponentInfo compo
     "@mozilla.org/embeddor.implemented/bookmark-charset-resolver;1",
     nsBookmarksServiceConstructor },
 
   { "Bookmarks Forward Proxy Inference Data Source",
     NS_RDF_FORWARDPROXY_INFER_DATASOURCE_CID,
     NS_RDF_INFER_DATASOURCE_CONTRACTID_PREFIX "forward-proxy",
     nsForwardProxyDataSourceConstructor },
 
+#else
+
+  { "Places Import/Export Service",
+    NS_PLACESIMPORTEXPORTSERVICE_CID,
+    NS_PLACESIMPORTEXPORTSERVICE_CONTRACTID,
+    nsPlacesImportExportServiceConstructor},
+
 #endif
 
   { "Feed Sniffer",
     NS_FEEDSNIFFER_CID,
     NS_FEEDSNIFFER_CONTRACTID,
     nsFeedSnifferConstructor,
     nsFeedSniffer::Register },
 
--- a/browser/components/migration/src/Makefile.in
+++ b/browser/components/migration/src/Makefile.in
@@ -66,16 +66,18 @@ REQUIRES	= \
 		  xulapp \
 		  $(NULL)
 
 ifdef MOZ_PLACES
 REQUIRES += places
 endif
 ifndef MOZ_PLACES_BOOKMARKS
 REQUIRES += bookmarks
+else
+REQUIRES += browserplaces 
 endif
 
 CPPSRCS  = nsProfileMigrator.cpp \
            nsBrowserProfileMigratorUtils.cpp \
            nsNetscapeProfileMigratorBase.cpp \
            nsSeamonkeyProfileMigrator.cpp \
            nsPhoenixProfileMigrator.cpp \
            $(NULL)
--- a/browser/components/migration/src/nsBrowserProfileMigratorUtils.cpp
+++ b/browser/components/migration/src/nsBrowserProfileMigratorUtils.cpp
@@ -36,16 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsBrowserProfileMigratorUtils.h"
 #ifdef MOZ_PLACES_BOOKMARKS
 #include "nsINavBookmarksService.h"
 #include "nsBrowserCompsCID.h"
 #include "nsToolkitCompsCID.h"
+#include "nsIPlacesImportExportService.h"
 #else
 #include "nsIBookmarksService.h"
 #endif
 #include "nsIFile.h"
 #include "nsIInputStream.h"
 #include "nsILineInputStream.h"
 #include "nsIProperties.h"
 #include "nsIProfileMigrator.h"
@@ -278,32 +279,31 @@ ImportBookmarksHTML(nsIFile* aBookmarksF
                                getter_Copies(importedBookmarksTitle));
 
 #ifdef MOZ_PLACES_BOOKMARKS
   // Get the bookmarks service
   nsCOMPtr<nsINavBookmarksService> bms =
     do_GetService(NS_NAVBOOKMARKSSERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Get the file:// uri for the bookmarks file.
-  nsCOMPtr<nsIURI> fileURI;
-  rv = NS_NewFileURI(getter_AddRefs(fileURI), aBookmarksFile);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // Create an imported bookmarks folder under the bookmarks menu.
   PRInt64 root;
   rv = bms->GetBookmarksRoot(&root);
   NS_ENSURE_SUCCESS(rv, rv);
 
   PRInt64 folder;
   rv = bms->CreateFolder(root, importedBookmarksTitle, -1, &folder);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Import the bookmarks into the folder.
-  rv = bms->ImportBookmarksHTMLToFolder(fileURI, folder);
+  nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(aBookmarksFile));
+  NS_ENSURE_TRUE(localFile, NS_ERROR_FAILURE);
+  nsCOMPtr<nsIPlacesImportExportService> importer = do_GetService(NS_PLACESIMPORTEXPORTSERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = importer->ImportHTMLFromFileToFolder(localFile, folder);
   return rv;
 #else
   nsCOMPtr<nsIRDFResource> folder;
   bms->CreateFolderInContainer(importedBookmarksTitle.get(), root, -1,
                                getter_AddRefs(folder));
 
   nsCOMPtr<nsIRDFResource> folderProp;
   rv = rdfs->GetResource(NC_URI(Folder), getter_AddRefs(folderProp));
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -36,16 +36,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 
 
 // Constructor
 
 function BrowserGlue() {
   this._init();
+  this._profileStarted = false;
 }
 
 BrowserGlue.prototype = {
   QueryInterface: function(iid) 
   {
      xpcomCheckInterfaces(iid, kServiceIIds, Components.results.NS_ERROR_NO_INTERFACE);
      return this;
   }
@@ -122,21 +123,33 @@ BrowserGlue.prototype = {
     var app = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo)
                         .QueryInterface(Components.interfaces.nsIXULRuntime);
     if (app.inSafeMode) {
       var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                          .getService(Components.interfaces.nsIWindowWatcher);
       ww.openWindow(null, "chrome://browser/content/safeMode.xul", 
                     "_blank", "chrome,centerscreen,modal,resizable=no", null);
     }
+
+    // initialize Places
+    this._initPlaces();
+
+    // indicate that the profile was initialized
+    this._profileStarted = true;
   },
 
   // profile shutdown handler (contains profile cleanup routines)
   _onProfileShutdown: function() 
   {
+    // this block is for code that depends on _onProfileStartup() having been called.
+    if (this._profileStarted) {
+      // final places cleanup
+      this._shutdownPlaces();
+    }
+
     // here we enter last survival area, in order to avoid multiple
     // "quit-application" notifications caused by late window closings
     const appStartup = Components.classes['@mozilla.org/toolkit/app-startup;1']
                                  .getService(Components.interfaces.nsIAppStartup);
     try {
       appStartup.enterLastWindowClosingSurvivalArea();
 
       this.Sanitizer.onShutdown();
@@ -152,26 +165,90 @@ BrowserGlue.prototype = {
   {
     if(typeof(Sanitizer) != "function") { // we should dynamically load the script
       Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
                 .getService(Components.interfaces.mozIJSSubScriptLoader)
                 .loadSubScript("chrome://browser/content/sanitize.js", null);
     }
     return Sanitizer;
   },
+
+  /**
+   * Initialize Places
+   * - imports bookmarks.html if bookmarks datastore is empty
+   */
+  _initPlaces: function bg__initPlaces() {
+#ifdef MOZ_PLACES_BOOKMARKS
+    var importBookmarks = false;
+    try {
+      var prefService = Components.classes["@mozilla.org/preferences-service;1"]
+                                  .getService(Components.interfaces.nsIPrefBranch);
+      importBookmarks = prefService.getBoolPref("browser.places.importBookmarksHTML");
+    } catch(ex) {}
+
+    if (!importBookmarks)
+      return;
+
+    var dirService = Components.classes["@mozilla.org/file/directory_service;1"]
+                               .getService(Components.interfaces.nsIProperties);
+    var profDir = dirService.get("ProfD", Components.interfaces.nsILocalFile);
+
+    var bookmarksFile = profDir.clone(); // bookmarks.html
+    bookmarksFile.append("bookmarks.html");
+
+    if (bookmarksFile.exists()) {
+      // import bookmarks.html
+      try {
+        var importer = 
+          Components.classes["@mozilla.org/browser/places/import-export-service;1"]
+                    .getService(Components.interfaces.nsIPlacesImportExportService);
+        importer.importHTMLFromFile(bookmarksFile);
+      } catch(ex) {
+      } finally {
+        prefService.setBoolPref("browser.places.importBookmarksHTML", false);
+      }
+
+      // backup pre-places bookmarks.html
+      // XXXtodo remove this before betas, after import/export is solid
+      var bookmarksBackup = profDir.clone();
+      bookmarksBackup.append("bookmarks.preplaces.html");
+      if (!bookmarksBackup.exists()) {
+        // save old bookmarks.html file as bookmarks.preplaces.html
+        try {
+          bookmarksFile.copyTo(profDir, "bookmarks.preplaces.html");
+        } catch(ex) {
+          dump("nsBrowserGlue::_initPlaces(): copy of bookmarks.html to bookmarks.preplaces.html failed: " + ex + "\n");
+        }
+      }
+    }
+#endif
+  },
+
+  /**
+   * Places shut-down tasks
+   * - back up and archive bookmarks
+   */
+  _shutdownPlaces: function bg__shutdownPlaces() {
+#ifdef MOZ_PLACES_BOOKMARKS
+    // backup bookmarks to bookmarks.html
+    var importer =
+      Components.classes["@mozilla.org/browser/places/import-export-service;1"]
+                .getService(Components.interfaces.nsIPlacesImportExportService);
+    importer.backupBookmarksFile();
+#endif
+  },
   
   // ------------------------------
   // public nsIBrowserGlue members
   // ------------------------------
   
   sanitize: function(aParentWindow) 
   {
     this.Sanitizer.sanitize(aParentWindow);
   }
-
 }
 
 
 // XPCOM Scaffolding code
 
 // component defined in this file
 
 const kServiceName = "Firefox Browser Glue Service";
--- a/browser/components/places/Makefile.in
+++ b/browser/components/places/Makefile.in
@@ -38,9 +38,15 @@
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
+DIRS = public src
+
+ifdef ENABLE_TESTS
+	DIRS += tests
+endif
+
 include $(topsrcdir)/config/rules.mk
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -282,35 +282,40 @@ var PlacesOrganizer = {
    */
   importFromFile: function PO_importFromFile() {
     var fp = Cc["@mozilla.org/filepicker;1"].
              createInstance(Ci.nsIFilePicker);
     fp.init(window, PlacesUtils.getString("SelectImport"),
             Ci.nsIFilePicker.modeOpen);
     fp.appendFilters(Ci.nsIFilePicker.filterHTML | Ci.nsIFilePicker.filterAll);
     if (fp.show() != Ci.nsIFilePicker.returnCancel) {
-      var ioService = Cc["@mozilla.org/network/io-service;1"].
-                      getService(Ci.nsIIOService);
-      if (fp.file)
-        PlacesUtils.bookmarks.importBookmarksHTML(ioService.newFileURI(fp.file));
+      if (fp.file) {
+        var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
+                       getService(Ci.nsIPlacesImportExportService);
+        var file = fp.file.QueryInterface(nsILocalFile);
+        importer.importHTMLFromFile(file);
+      }
     }
   },
 
   /**
    * Allows simple exporting of bookmarks.
    */
   exportBookmarks: function PO_exportBookmarks() {
     var fp = Cc["@mozilla.org/filepicker;1"].
              createInstance(Ci.nsIFilePicker);
     fp.init(window, PlacesUtils.getString("EnterExport"),
             Ci.nsIFilePicker.modeSave);
     fp.appendFilters(Ci.nsIFilePicker.filterHTML);
     fp.defaultString = "bookmarks.html";
-    if (fp.show() != Ci.nsIFilePicker.returnCancel)
-      PlacesUtils.bookmarks.exportBookmarksHTML(fp.file);
+    if (fp.show() != Ci.nsIFilePicker.returnCancel) {
+      var exporter = Cc["@mozilla.org/browser/places/import-export-service;1"].
+                     getService(Ci.nsIPlacesImportExportService);
+      exporter.exportHTMLToFile(fp.file);
+    }
   },
 
   updateStatusBarForView: function PO_updateStatusBarForView(aView) {
     var statusText = "";
     var selectedNode = aView.selectedNode;
     if (selectedNode) {
       if (PlacesUtils.nodeIsFolder(selectedNode)) {
         var childsCount =
new file mode 100644
--- /dev/null
+++ b/browser/components/places/public/Makefile.in
@@ -0,0 +1,51 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Places code.
+#
+# The Initial Developer of the Original Code is
+# Google Inc.
+# Portions created by the Initial Developer are Copyright (C) 2005
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Brett Wilson <brettw@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = browserplaces
+XPIDL_MODULE = browserplaces
+
+XPIDLSRCS  = nsIPlacesImportExportService.idl
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/components/places/public/nsIPlacesImportExportService.idl
@@ -0,0 +1,73 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Places.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corp.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Dietrich Ayala <dietrich@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+interface nsILocalFile;
+
+/**
+ * The PlacesImportExport interface provides methods for importing
+ * and exporting Places data.
+ */
+
+[scriptable, uuid(212fd626-e07e-11db-8314-0800200c9a66)]
+interface nsIPlacesImportExportService: nsISupports
+{
+  /**
+   * Loads the given bookmarks.html file and merges it with the current
+   * bookmarks hierarchy.
+   */
+  void importHTMLFromFile(in nsILocalFile aFile);
+
+
+  /**
+   * Loads the given bookmarks.html file and puts it in the given folder
+   */
+  void importHTMLFromFileToFolder(in nsILocalFile aFile, in PRInt64 aFolder);
+
+
+  /**
+   * Saves the current bookmarks hierarchy to a bookmarks.html file.
+   */
+  void exportHTMLToFile(in nsILocalFile aFile);
+
+  /**
+   * Backup and archive the bookmarks.html file.
+   */
+  void backupBookmarksFile();
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/places/src/Makefile.in
@@ -0,0 +1,68 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Places code
+#
+# The Initial Developer of the Original Code is
+# Google Inc.
+# Portions created by the Initial Developer are Copyright (C) 2005
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Brett Wilson <brettw@gmail.com>
+#   Dietrich Ayala <dietrich@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = browserplaces
+LIBRARY_NAME  = browserplaces_s
+FORCE_STATIC_LIB = 1
+FORCE_USE_PIC = 1
+USE_STATIC_LIBS = 1
+
+REQUIRES	= \
+		  xpcom \
+		  string \
+		  docshell \
+		  necko \
+			browsercomps \
+			toolkitcomps \
+		  unicharutil \
+			htmlparser \
+			content \
+			places \
+		  $(NULL)
+
+CPPSRCS = nsPlacesImportExportService.cpp
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/components/places/src/nsPlacesImportExportService.cpp
@@ -0,0 +1,2145 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla History System
+ *
+ * The Initial Developer of the Original Code is
+ * Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brett Wilson <brettw@gmail.com>
+ *   Dietrich Ayala <dietrich@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * Importer/exporter between the mozStorage-based bookmarks and the old-style
+ * "bookmarks.html"
+ *
+ * Format:
+ *
+ * Primary heading := h1
+ *   Old version used this to set attributes on the bookmarks RDF root, such
+ *   as the last modified date. We only use H1 to check for the attribute
+ *   PLACES_ROOT, which tells us that this hierarchy root is the places root.
+ *   For backwards compatability, if we don't find this, we assume that the
+ *   hierarchy is rooted at the bookmarks menu.
+ * Heading := any heading other than h1
+ *   Old version used this to set attributes on the current container. We only
+ *   care about the content of the heading container, which contains the title
+ *   of the bookmark container.
+ * Bookmark := a
+ *   HREF is the destination of the bookmark
+ *   FEEDURL is the URI of the RSS feed if this is a livemark.
+ *   LAST_CHARSET should be stored as an annotation (FIXME bug 334408) so that the
+ *     next time we go to that page we remember the user's preference.
+ *   WEB_PANEL is set to "true" if the bookmark should be loaded in the sidebar.
+ *   ICON will be stored in the favicon service
+ *   ICON_URI is new for places bookmarks.html, it refers to the original
+ *     URI of the favicon so we don't have to make up favicon URLs.
+ *   Text of the <a> container is the name of the bookmark
+ *   Ignored: ADD_DATE, LAST_VISIT, LAST_MODIFIED, ID
+ * Bookmark comment := dd
+ *   This affects the previosly added bookmark
+ * Separator := hr
+ *   Insert a separator into the current container
+ * The folder hierarchy is defined by <dl>/<ul>/<menu> (the old importing code
+ *     handles all these cases, when we write, use <dl>).
+ *
+ * Overall design
+ * --------------
+ *
+ * We need to emulate a recursive parser. A "Bookmark import frame" is created
+ * corresponding to each folder we encounter. These are arranged in a stack,
+ * and contain all the state we need to keep track of.
+ *
+ * A frame is created when we find a heading, which defines a new container.
+ * The frame also keeps track of the nesting of <DL>s, (in well-formed
+ * bookmarks files, these will have a 1-1 correspondence with frames, but we
+ * try to be a little more flexible here). When the nesting count decreases
+ * 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 "nsNetUtil.h"
+#include "nsParserCIID.h"
+#include "nsStringAPI.h"
+#include "nsUnicharUtils.h"
+#include "plbase64.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsIPrefService.h"
+#include "nsToolkitCompsCID.h"
+#include "nsIHTMLContentSink.h"
+#include "nsIParser.h"
+
+static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
+
+#define KEY_TOOLBARFOLDER_LOWER "personal_toolbar_folder"
+#define KEY_BOOKMARKSSMENU_LOWER "bookmarks_menu"
+#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_ID_LOWER "id"
+
+#define LOAD_IN_SIDEBAR_ANNO NS_LITERAL_CSTRING("bookmarkProperties/loadInSidebar")
+
+#define BOOKMARKSS_MENU_ICON_URI "chrome://browser/skin/places/bookmarksMenu.png"
+#define BOOKMARKSS_TOOLBAR_ICON_URI "chrome://browser/skin/places/bookmarksToolbar.png"
+
+// define to get debugging messages on console about import
+//#define DEBUG_IMPORT
+
+#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(PR_FALSE)
+  {
+  }
+
+  enum ContainerType { Container_Normal,
+                       Container_Places,
+                       Container_Menu,
+                       Container_Toolbar };
+
+  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;
+
+  // Container Id, see above
+  PRInt64 mLastContainerId;
+
+  // 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 preceeding
+  // <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.
+  PRBool 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, PRInt64* aContainerId)
+  {
+    *aHeading = mPreviousText;
+    *aContainerType = mLastContainerType;
+    *aContainerId = mLastContainerId;
+    mPreviousText.Truncate();
+  }
+
+  // Contains the id of an imported, or newly created bookmark.
+  PRInt64 mPreviousId;
+};
+
+/**
+ * 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 *rv = (char *) nsMemory::Alloc(strlen(string) * 6 + 1);
+	char *ptr = rv;
+
+	if(rv)
+	  {
+		for(; *string != '\0'; string++)
+		  {
+			if(*string == '<')
+			  {
+				*ptr++ = '&';
+				*ptr++ = 'l';
+				*ptr++ = 't';
+				*ptr++ = ';';
+			  }
+			else if(*string == '>')
+			  {
+				*ptr++ = '&';
+				*ptr++ = 'g';
+				*ptr++ = 't';
+				*ptr++ = ';';
+			  }
+			else if(*string == '&')
+			  {
+				*ptr++ = '&';
+				*ptr++ = 'a';
+				*ptr++ = 'm';
+				*ptr++ = 'p';
+				*ptr++ = ';';
+			  }
+			else if (*string == '"')
+			  {
+				*ptr++ = '&';
+				*ptr++ = 'q';
+				*ptr++ = 'u';
+				*ptr++ = 'o';
+				*ptr++ = 't';
+				*ptr++ = ';';
+			  }			
+			else if (*string == '\'')
+			  {
+				*ptr++ = '&';
+				*ptr++ = '#';
+				*ptr++ = '3';
+				*ptr++ = '9';
+				*ptr++ = ';';
+			  }
+			else
+			  {
+				*ptr++ = *string;
+			  }
+		  }
+		*ptr = '\0';
+	  }
+
+	return(rv);
+}
+
+NS_IMPL_ISUPPORTS1(nsPlacesImportExportService, nsIPlacesImportExportService)
+
+nsPlacesImportExportService::nsPlacesImportExportService()
+  : mPlacesRoot(0), mBookmarksRoot(0), mToolbarFolder(0)
+{
+  nsresult rv;
+  mHistoryService = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID, &rv);
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "could not get history service");
+  mFaviconService = do_GetService(NS_FAVICONSERVICE_CONTRACTID, &rv);
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "could not get favicon service");
+  mAnnotationService = do_GetService(NS_ANNOTATIONSERVICE_CONTRACTID, &rv);
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "could not get annotation service");
+  mBookmarksService = do_GetService(NS_NAVBOOKMARKSSERVICE_CONTRACTID, &rv);
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "could not get bookmarks service");
+  mLivemarkService = do_GetService(NS_LIVEMARKSERVICE_CONTRACTID, &rv);
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "could not get livemark service");
+
+  mBookmarksService->GetPlacesRoot(&mPlacesRoot);
+  mBookmarksService->GetBookmarksRoot(&mBookmarksRoot);
+  mBookmarksService->GetToolbarFolder(&mToolbarFolder);
+}
+
+nsPlacesImportExportService::~nsPlacesImportExportService()
+{
+}
+
+/**
+ * The content sink stuff is based loosely on 
+ */
+class BookmarkContentSink : public nsIHTMLContentSink
+{
+public:
+  nsresult Init(PRBool aAllowRootChanges,
+                nsINavBookmarksService* bookmarkService,
+                PRInt64 aFolder,
+                PRBool aIsImportDefaults);
+
+  NS_DECL_ISUPPORTS
+
+  // nsIContentSink (superclass of nsIHTMLContentSink)
+  NS_IMETHOD WillTokenize() { return NS_OK; }
+  NS_IMETHOD WillBuildModel() { return NS_OK; }
+  NS_IMETHOD DidBuildModel() { return NS_OK; }
+  NS_IMETHOD WillInterrupt() { return NS_OK; }
+  NS_IMETHOD WillResume() { return NS_OK; }
+  NS_IMETHOD SetParser(nsIParser* 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, PRBool* aReturn)
+    { *aReturn = PR_TRUE; return NS_OK; }
+  NS_IMETHOD WillProcessTokens() { 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 AddComment(const nsIParserNode& aNode) { return NS_OK; }
+  NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode) { return NS_OK; }
+  NS_IMETHOD AddDocTypeDecl(const nsIParserNode& aNode) { return NS_OK; }
+  NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode) { return NS_OK; }
+  NS_IMETHOD_(PRBool) IsFormOnStack() { return PR_FALSE; }
+
+protected:
+  nsCOMPtr<nsINavBookmarksService> mBookmarksService;
+  nsCOMPtr<nsINavHistoryService> mHistoryService;
+  nsCOMPtr<nsIAnnotationService> mAnnotationService;
+  nsCOMPtr<nsILivemarkService> 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.
+  PRBool mAllowRootChanges;
+
+  // if set, this is an import of initial bookmarks.html content,
+  // so we don't want to kick off HTTP traffic
+  PRBool 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.
+  PRBool 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();
+
+  // 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];
+  }
+  nsresult NewFrame();
+  nsresult PopFrame();
+
+  nsresult SetFaviconForURI(nsIURI* aPageURI, nsIURI* aFaviconURI,
+                            const nsCString& aData);
+  nsresult SetFaviconForFolder(PRInt64 aFolder, const nsACString& aFavicon);
+
+  PRInt64 ConvertImportedIdToInternalId(const nsCString& aId);
+
+#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::Init
+//
+//    Note that the bookmark service pointer is passed in. We can not create
+//    the bookmark service from here because this can be called from bookmark
+//    service creation, making a weird reentrant loop.
+
+nsresult
+BookmarkContentSink::Init(PRBool aAllowRootChanges,
+                          nsINavBookmarksService* bookmarkService,
+                          PRInt64 aFolder,
+                          PRBool aIsImportDefaults)
+{
+  nsresult rv;
+  mBookmarksService = bookmarkService;
+  mHistoryService = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  mAnnotationService = do_GetService(NS_ANNOTATIONSERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  mLivemarkService = do_GetService(NS_LIVEMARKSERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mAllowRootChanges = aAllowRootChanges;
+  mIsImportDefaults = aIsImportDefaults;
+
+  // initialize the root frame with the menu root
+  PRInt64 menuRoot;
+  if (aFolder == 0) {
+    rv = mBookmarksService->GetBookmarksRoot(&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)
+
+// nsIContentSink **************************************************************
+
+NS_IMETHODIMP
+BookmarkContentSink::OpenContainer(const nsIParserNode& aNode)
+{
+  // 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.
+  BookmarkImportFrame& frame = CurFrame();
+  if (frame.mInDescription) {
+    frame.mPreviousText.Trim(kWhitespace); // important!
+    if (!frame.mPreviousText.IsEmpty()) {
+      // FIXME bug 334758: This description should be stored as an annotation
+      // on the URL or folder. We should probably not overwrite existing
+      // annotations.
+      frame.mPreviousText.Truncate();
+    }
+    frame.mInDescription = PR_FALSE;
+  }
+
+  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;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BookmarkContentSink::CloseContainer(const nsHTMLTag aTag)
+{
+  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();
+    break;
+  }
+
+  return NS_OK;
+}
+
+// BookmarkContentSink::HandleContainerBegin
+
+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)
+    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 mPlacesRoot;
+      mBookmarksService->GetPlacesRoot(&mPlacesRoot);
+      CurFrame().mContainerID = mPlacesRoot;
+      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;
+  frame.mLastContainerId = 0;
+
+  // 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;
+  if (!mFolderSpecified) {
+    for (PRInt32 i = 0; i < attrCount; i ++) {
+      if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_TOOLBARFOLDER_LOWER)) {
+        frame.mLastContainerType = BookmarkImportFrame::Container_Toolbar;
+        break;
+      } else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_BOOKMARKSSMENU_LOWER)) {
+        frame.mLastContainerType = BookmarkImportFrame::Container_Menu;
+        break;
+      } else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_PLACESROOT_LOWER)) {
+        frame.mLastContainerType = BookmarkImportFrame::Container_Places;
+        break;
+      } else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_ID_LOWER)) {
+        frame.mLastContainerId =
+          ConvertImportedIdToInternalId(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)
+{
+  nsresult rv;
+
+  BookmarkImportFrame& frame = CurFrame();
+
+  // We need to make sure that the feed URIs from previous frames are emptied. 
+  frame.mPreviousFeed = nsnull;
+
+  // We need to make sure that the bookmark id from previous frames are emptied. 
+  frame.mPreviousId = 0;
+
+  // mPreviousText will hold our link text, clear it so that can be appended to
+  frame.mPreviousText.Truncate();
+
+  // get the attributes we care about
+  nsAutoString href;
+  nsAutoString feedUrl;
+  nsAutoString icon;
+  nsAutoString iconUri;
+  nsAutoString lastCharset;
+  nsAutoString keyword;
+  nsAutoString webPanel;
+  nsAutoString id;
+  PRInt32 attrCount = node.GetAttributeCount();
+  for (PRInt32 i = 0; i < attrCount; i ++) {
+    const nsAString& key = node.GetKeyAt(i);
+    if (key.LowerCaseEqualsLiteral(KEY_HREF_LOWER)) {
+      href = node.GetValueAt(i);
+    } else if (key.LowerCaseEqualsLiteral(KEY_FEEDURL_LOWER)) {
+      feedUrl = node.GetValueAt(i);
+    } else if (key.LowerCaseEqualsLiteral(KEY_ICON_LOWER)) {
+      icon = node.GetValueAt(i);
+    } else if (key.LowerCaseEqualsLiteral(KEY_ICON_URI_LOWER)) {
+      iconUri = node.GetValueAt(i);
+    } else if (key.LowerCaseEqualsLiteral(KEY_LASTCHARSET_LOWER)) {
+      lastCharset = node.GetValueAt(i);
+    } else if (key.LowerCaseEqualsLiteral(KEY_SHORTCUTURL_LOWER)) {
+      keyword = node.GetValueAt(i);
+    } else if (key.LowerCaseEqualsLiteral(KEY_WEB_PANEL_LOWER)) {
+      webPanel = node.GetValueAt(i);
+    } else if (key.LowerCaseEqualsLiteral(KEY_ID_LOWER)) {
+      id = node.GetValueAt(i);
+    }
+  }
+  href.Trim(kWhitespace);
+  feedUrl.Trim(kWhitespace);
+  icon.Trim(kWhitespace);
+  iconUri.Trim(kWhitespace);
+  lastCharset.Trim(kWhitespace);
+  keyword.Trim(kWhitespace);
+  webPanel.Trim(kWhitespace);
+  id.Trim(kWhitespace);
+
+  // For feeds, get the feed URL. If it is invalid, it will leave mPreviousFeed
+  // NULL and we'll continue trying to 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: we don't know what to do with them.
+  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 this so the link text and descriptions can be associated with it.
+    // Note that we ignore errors if this is a feed: URLs aren't strictly
+    // necessary in these cases.
+    nsresult rv = NS_NewURI(getter_AddRefs(frame.mPreviousLink),
+                   href, nsnull);
+    if (NS_FAILED(rv) && !frame.mPreviousFeed) {
+      frame.mPreviousLink = nsnull;
+      return; // invalid link
+    }
+  }
+
+  // if there's a pre-existing Places bookmark id, use it
+  frame.mPreviousId = ConvertImportedIdToInternalId(NS_ConvertUTF16toUTF8(id));
+
+  // if there is a feedURL, this is a livemark, which is a special case
+  // that we handle in HandleLinkEnd(): don't create normal bookmarks
+  if (frame.mPreviousFeed)
+    return;
+
+  // attempt to get a property for the supposedly pre-existing bookmark
+  if (frame.mPreviousId > 0) {
+    PRInt32 index;
+    rv = mBookmarksService->GetItemIndex(frame.mPreviousId, &index);
+    if (NS_FAILED(rv))
+      frame.mPreviousId = 0;
+  }
+
+  // if no previous id (or a legacy id), create a new bookmark
+  if (frame.mPreviousId == 0) {
+    // create the bookmark
+    rv = mBookmarksService->InsertItem(frame.mContainerID, frame.mPreviousLink,
+                                       mBookmarksService->DEFAULT_INDEX, &frame.mPreviousId);
+    NS_ASSERTION(NS_SUCCEEDED(rv), "InsertItem failed");
+  }
+
+  // save the favicon, ignore errors
+  if (!icon.IsEmpty() || !iconUri.IsEmpty()) {
+    nsCOMPtr<nsIURI> iconUriObject;
+    NS_NewURI(getter_AddRefs(iconUriObject), iconUri);
+    if (!icon.IsEmpty() || iconUriObject) {
+      rv = SetFaviconForURI(frame.mPreviousLink, iconUriObject,
+                            NS_ConvertUTF16toUTF8(icon));
+    }
+  }
+
+  // save the keyword, ignore errors
+  if (!keyword.IsEmpty())
+    mBookmarksService->SetKeywordForBookmark(frame.mPreviousId, keyword);
+
+  if (webPanel.LowerCaseEqualsLiteral("true")) {
+    // set load-in-sidebar annotation for the bookmark
+
+    nsCOMPtr<nsIURI> placeURI;
+    rv = mBookmarksService->GetItemURI(frame.mPreviousId,
+                                       getter_AddRefs(placeURI));
+    NS_ASSERTION(NS_SUCCEEDED(rv),
+                 "failed to get a place: uri for a new bookmark");
+    if (NS_SUCCEEDED(rv)) {
+      mAnnotationService->SetAnnotationInt32(placeURI, LOAD_IN_SIDEBAR_ANNO,
+                                             1, 0,
+                                             nsIAnnotationService::EXPIRE_NEVER);
+    }
+  }
+  // FIXME bug 334408: save the last charset
+}
+
+
+// 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 bookmark is actually a livemark.  Create it here.
+    // (It gets created here instead of in HandleLinkBegin()
+    // because we need to know the title before creating it.)
+    PRInt64 folderId;
+
+    if (frame.mPreviousId > 0) {
+      // It's a pre-existing livemark, so update its properties
+      rv = mLivemarkService->SetSiteURI(frame.mPreviousId, frame.mPreviousLink);
+      NS_ASSERTION(NS_SUCCEEDED(rv), "SetSiteURI failed!");
+      rv = mLivemarkService->SetFeedURI(frame.mPreviousId, frame.mPreviousFeed);
+      NS_ASSERTION(NS_SUCCEEDED(rv), "SetFeedURI failed!");
+      rv = mBookmarksService->SetFolderTitle(frame.mPreviousId, frame.mPreviousText);
+      NS_ASSERTION(NS_SUCCEEDED(rv), "SetFolderTitle failed!");
+    } else {
+      if (mIsImportDefaults) {
+        rv = mLivemarkService->CreateLivemarkFolderOnly(mBookmarksService,
+                                                   frame.mContainerID,
+                                                   frame.mPreviousText,
+                                                   frame.mPreviousLink,
+                                                   frame.mPreviousFeed,
+                                                   -1,
+                                                   &folderId);
+        NS_ASSERTION(NS_SUCCEEDED(rv), "CreateLivemarkFolderOnly failed!");
+      } else {
+        rv = mLivemarkService->CreateLivemark(frame.mContainerID,
+                                         frame.mPreviousText,
+                                         frame.mPreviousLink,
+                                         frame.mPreviousFeed,
+                                         -1,
+                                         &folderId);
+        NS_ASSERTION(NS_SUCCEEDED(rv), "CreateLivemark failed!");
+      }
+    }
+#ifdef DEBUG_IMPORT
+    PrintNesting();
+    printf("Creating livemark '%s'\n",
+           NS_ConvertUTF16toUTF8(frame.mPreviousText).get());
+#endif
+  }
+  else if (frame.mPreviousLink) {
+#ifdef DEBUG_IMPORT
+    PrintNesting();
+    printf("Creating bookmark '%s'\n",
+           NS_ConvertUTF16toUTF8(frame.mPreviousText).get());
+#endif
+    mBookmarksService->SetItemTitle(frame.mPreviousId, frame.mPreviousText);
+  }
+  frame.mPreviousText.Truncate();
+}
+
+
+// BookmarkContentSink::HandleSeparator
+//
+//    Inserts a separator into the current container
+void
+BookmarkContentSink::HandleSeparator()
+{
+  BookmarkImportFrame& frame = CurFrame();
+
+  // bookmarks.html contains a separator between the toolbar menu and the
+  // rest of the items.  Since we pull the toolbar menu out into the top level,
+  // we want to skip over this separator since it looks out of place.
+  if (frame.mLastContainerType != BookmarkImportFrame::Container_Toolbar) {
+    // create the separator
+#ifdef DEBUG_IMPORT
+    PrintNesting();
+    printf("--------\n");
+#endif
+    mBookmarksService->InsertSeparator(frame.mContainerID,
+                                       mBookmarksService->DEFAULT_INDEX);
+  }
+}
+
+
+// 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;
+  CurFrame().ConsumeHeading(&containerName, &containerType, &ourID);
+
+  PRBool updateFolder = PR_FALSE;
+  if (ourID == 0) {
+    switch (containerType) {
+      case BookmarkImportFrame::Container_Normal:
+        // regular folder: use an existing folder if that name already exists
+        rv = mBookmarksService->GetChildFolder(CurFrame().mContainerID,
+                                               containerName, &ourID);
+        NS_ENSURE_SUCCESS(rv, rv);
+        if (ourID == 0) {
+          // need to append a new folder
+          rv = mBookmarksService->CreateFolder(CurFrame().mContainerID,
+                                              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 root
+        rv = mBookmarksService->GetBookmarksRoot(&ourID);
+        NS_ENSURE_SUCCESS(rv, rv);
+        if (mAllowRootChanges) {
+          updateFolder = PR_TRUE;
+          rv = SetFaviconForFolder(ourID, NS_LITERAL_CSTRING(BOOKMARKSS_MENU_ICON_URI));
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
+        break;
+      case BookmarkImportFrame::Container_Toolbar:
+        // get toolbar folder
+        PRInt64 bookmarkToolbarFolder;
+        rv = mBookmarksService->GetToolbarFolder(&bookmarkToolbarFolder);
+        NS_ENSURE_SUCCESS(rv, rv);
+        if (!bookmarkToolbarFolder) {
+          // create new folder
+          rv = mBookmarksService->CreateFolder(CurFrame().mContainerID,
+                                              containerName,
+                                              mBookmarksService->DEFAULT_INDEX, &ourID);
+          NS_ENSURE_SUCCESS(rv, rv);
+          // there's no toolbar folder, so make us the toolbar folder
+          rv = mBookmarksService->SetToolbarFolder(ourID);
+          NS_ENSURE_SUCCESS(rv, rv);
+          // set favicon
+          rv = SetFaviconForFolder(ourID, NS_LITERAL_CSTRING(BOOKMARKSS_TOOLBAR_ICON_URI));
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
+        else {
+          ourID = bookmarkToolbarFolder;
+        }
+        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
+    mBookmarksService->MoveFolder(ourID, CurFrame().mContainerID, -1);
+    mBookmarksService->SetFolderTitle(ourID, containerName);
+#ifdef DEBUG_IMPORT
+    printf(" [reparenting]");
+#endif
+  }
+
+#ifdef DEBUG_IMPORT
+  printf("\n");
+#endif
+
+  if (!mFrames.AppendElement(BookmarkImportFrame(ourID)))
+    return NS_ERROR_OUT_OF_MEMORY;
+  return NS_OK;
+}
+
+
+// BookmarkContentSink::PopFrame
+//
+
+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 nsCString& aData)
+{
+  nsresult rv;
+  static PRUint32 serialNumber = 0; // for made-up favicon URIs
+
+  nsCOMPtr<nsIFaviconService> faviconService(do_GetService(NS_FAVICONSERVICE_CONTRACTID, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // 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("-");
+    faviconSpec.AppendInt(PR_Now());
+    rv = NS_NewURI(getter_AddRefs(faviconURI), faviconSpec);
+    NS_ENSURE_SUCCESS(rv, rv);
+    serialNumber ++;
+  }
+
+  nsCOMPtr<nsIURI> dataURI;
+  rv = NS_NewURI(getter_AddRefs(dataURI), aData);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // use the data: protocol handler to convert the data
+  nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIProtocolHandler> protocolHandler;
+  rv = ioService->GetProtocolHandler("data", getter_AddRefs(protocolHandler));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIChannel> channel;
+  rv = protocolHandler->NewChannel(dataURI, getter_AddRefs(channel));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // blocking stream is OK for data URIs
+  nsCOMPtr<nsIInputStream> stream;
+  rv = channel->Open(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 available;
+  rv = stream->Available(&available);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (available == 0)
+    return NS_ERROR_FAILURE;
+
+  // read all the decoded data
+  PRUint8* buffer = NS_STATIC_CAST(PRUint8*,
+                                   nsMemory::Alloc(sizeof(PRUint8) * available));
+  if (!buffer)
+    return NS_ERROR_OUT_OF_MEMORY;
+  PRUint32 numRead;
+  rv = stream->Read(NS_REINTERPRET_CAST(char*, buffer), available, &numRead);
+  if (NS_FAILED(rv) || numRead != available) {
+    nsMemory::Free(buffer);
+    return rv;
+  }
+
+  nsCAutoString mimeType;
+  rv = channel->GetContentType(mimeType);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // save in service
+  rv = faviconService->SetFaviconData(faviconURI, buffer, available, mimeType, 0);
+  nsMemory::Free(buffer);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = faviconService->SetFaviconUrlForPage(aPageURI, faviconURI);
+  return NS_OK; 
+}
+
+
+// BookmarkContentSink::SetFaviconForFolder
+//
+//    This sets the given favicon URI for the given folder. It is used to
+//    initialize the favicons for the bookmarks menu and toolbar. We don't
+//    actually set any data here because we assume the URI is a chrome: URI.
+//    These do not have to contain any data for them to work.
+
+nsresult
+BookmarkContentSink::SetFaviconForFolder(PRInt64 aFolder,
+                                         const nsACString& aFavicon)
+{
+  nsresult rv;
+  nsCOMPtr<nsIFaviconService> faviconService(do_GetService(NS_FAVICONSERVICE_CONTRACTID, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIURI> folderURI;
+  rv = mBookmarksService->GetFolderURI(aFolder,
+                                                getter_AddRefs(folderURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIURI> faviconURI;
+  rv = NS_NewURI(getter_AddRefs(faviconURI), aFavicon);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return faviconService->SetFaviconUrlForPage(folderURI, faviconURI);
+}
+
+// Converts a string id (legacy rdf or contemporary) into an int id
+PRInt64
+BookmarkContentSink::ConvertImportedIdToInternalId(const nsCString& aId) {
+  PRInt64 intId = 0;
+  if (aId.IsEmpty() || Substring(aId, 0, 4).Equals(NS_LITERAL_CSTRING("rdf:"), CaseInsensitiveCompare))
+    return intId;
+  nsresult rv;
+  intId = aId.ToInteger(&rv);
+  if (NS_FAILED(rv))
+    intId = 0;
+  return intId;
+}
+
+
+// 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)
+{
+  nsresult channelStatus;
+  channel->GetStatus(&channelStatus);
+  if (NS_FAILED(channelStatus))
+    return channelStatus;
+
+  if (NS_SUCCEEDED(status))
+    return NS_OK; // caller and the channel are happy
+
+  // channel was OK, but caller wasn't: set the channel state
+  channel->Cancel(status);
+  return status;
+}
+
+
+static char kFileIntro[] =
+    "<!DOCTYPE NETSCAPE-Bookmark-file-1>" NS_LINEBREAK
+    // Note: we write bookmarks in UTF-8
+    "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">" NS_LINEBREAK
+    "<TITLE>Bookmarks</TITLE>" NS_LINEBREAK;
+static const char kRootIntro[] = "<H1";
+static const char kCloseRootH1[] = "</H1>" NS_LINEBREAK NS_LINEBREAK;
+
+static const char kBookmarkIntro[] = "<DL><p>" NS_LINEBREAK;
+static const char kBookmarkClose[] = "</DL><p>" NS_LINEBREAK;
+static const char kContainerIntro[] = "<DT><H3";
+static const char kContainerClose[] = "</H3>" NS_LINEBREAK;
+static const char kItemOpen[] = "<DT><A";
+static const char kItemClose[] = "</A>" NS_LINEBREAK;
+static const char kSeparator[] = "<HR>" NS_LINEBREAK;
+static const char kQuoteStr[] = "\"";
+static const char kCloseAngle[] = ">";
+static const char kIndent[] = "    ";
+
+static const char kPlacesRootAttribute[] = " PLACES_ROOT=\"true\"";
+static const char kBookmarksRootAttribute[] = " BOOKMARKSS_MENU=\"true\"";
+static const char kToolbarFolderAttribute[] = " PERSONAL_TOOLBAR_FOLDER=\"true\"";
+static const char kIconAttribute[] = " ICON=\"";
+static const char kIconURIAttribute[] = " ICON_URI=\"";
+static const char kHrefAttribute[] = " HREF=\"";
+static const char kFeedURIAttribute[] = " FEEDURL=\"";
+static const char kWebPanelAttribute[] = " WEB_PANEL=\"true\"";
+static const char kKeywordAttribute[] = " SHORTCUTURL=\"";
+static const char kIdAttribute[] = " ID=\"";
+
+// WriteContainerPrologue
+//
+//    <DL><p>
+//
+//    Goes after the container header (<H3...) but before the contents
+
+static nsresult
+WriteContainerPrologue(const nsACString& aIndent, nsIOutputStream* aOutput)
+{
+  PRUint32 dummy;
+  nsresult rv = aOutput->Write(PromiseFlatCString(aIndent).get(), aIndent.Length(), &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aOutput->Write(kBookmarkIntro, sizeof(kBookmarkIntro)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return NS_OK;
+}
+
+
+// WriteContainerEpilogue
+//
+//    </DL><p>
+//
+//    Goes after the container contents to close the container
+
+static nsresult
+WriteContainerEpilogue(const nsACString& aIndent, nsIOutputStream* aOutput)
+{
+  PRUint32 dummy;
+  nsresult rv = aOutput->Write(PromiseFlatCString(aIndent).get(), aIndent.Length(), &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aOutput->Write(kBookmarkClose, sizeof(kBookmarkClose)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return NS_OK;
+}
+
+
+// DataToDataURI
+
+static nsresult
+DataToDataURI(PRUint8* aData, PRUint32 aDataLen, const nsACString& aMimeType,
+              nsACString& aDataURI)
+{
+  char* encoded = PL_Base64Encode(NS_REINTERPRET_CAST(const char*, aData),
+                                  aDataLen, nsnull);
+  if (!encoded)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  aDataURI.AssignLiteral("data:");
+  aDataURI.Append(aMimeType);
+  aDataURI.AppendLiteral(";base64,");
+  aDataURI.Append(encoded);
+
+  nsMemory::Free(encoded);
+  return NS_OK;
+}
+
+
+// WriteFaviconAttribute
+//
+//    This writes the 'ICON="data:asdlfkjas;ldkfja;skdljfasdf"' attribute for
+//    an item. We special-case chrome favicon URIs by just writing the chrome:
+//    URI.
+
+static nsresult
+WriteFaviconAttribute(const nsACString& aURI, nsIOutputStream* aOutput)
+{
+  nsresult rv;
+  PRUint32 dummy;
+
+  nsCOMPtr<nsIURI> uri;
+  rv = NS_NewURI(getter_AddRefs(uri), aURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // get favicon
+  nsCOMPtr<nsIFaviconService> faviconService = do_GetService(NS_FAVICONSERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIURI> faviconURI;
+  rv = faviconService->GetFaviconForPage(uri, getter_AddRefs(faviconURI));
+  if (rv == NS_ERROR_NOT_AVAILABLE)
+    return NS_OK; // no favicon
+  NS_ENSURE_SUCCESS(rv, rv); // anything else is error
+
+  nsCAutoString faviconScheme;
+  nsCAutoString faviconSpec;
+  rv = faviconURI->GetSpec(faviconSpec);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = faviconURI->GetScheme(faviconScheme);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // write favicon URI: 'ICON_URI="..."'
+  rv = aOutput->Write(kIconURIAttribute, sizeof(kIconURIAttribute)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = WriteEscapedUrl(faviconSpec, aOutput);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!faviconScheme.EqualsLiteral("chrome")) {
+    // only store data for non-chrome URIs
+
+    // get the data - BE SURE TO FREE
+    nsCAutoString mimeType;
+    PRUint32 dataLen;
+    PRUint8* data;
+    rv = faviconService->GetFaviconData(faviconURI, mimeType, &dataLen, &data);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (dataLen > 0) {
+      // convert to URI
+      nsCString faviconContents;
+      rv = DataToDataURI(data, dataLen, mimeType, faviconContents);
+      nsMemory::Free(data);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = aOutput->Write(kIconAttribute, sizeof(kIconAttribute)-1, &dummy);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = aOutput->Write(faviconContents.get(), faviconContents.Length(), &dummy);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+  return NS_OK;
+}
+
+
+// nsPlacesImportExportService::WriteContainer
+//
+//    Writes out all the necessary parts of a bookmarks folder.
+
+nsresult
+nsPlacesImportExportService::WriteContainer(PRInt64 aFolder, const nsACString& aIndent,
+                               nsIOutputStream* aOutput)
+{
+  nsresult rv = WriteContainerHeader(aFolder, aIndent, aOutput);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // FIXME bug 334758: write container description here as a <DD>
+
+  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
+//
+//    This writes '<DL><H3>Title</H3>'
+//    Remember folders can also have favicons, which we put in the H3 tag
+
+nsresult
+nsPlacesImportExportService::WriteContainerHeader(PRInt64 aFolder, const nsACString& aIndent,
+                                     nsIOutputStream* aOutput)
+{
+  PRUint32 dummy;
+  nsresult rv;
+
+  // indent
+  if (!aIndent.IsEmpty()) {
+    rv = aOutput->Write(PromiseFlatCString(aIndent).get(), aIndent.Length(), &dummy);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // "<DL H3"
+  rv = aOutput->Write(kContainerIntro, sizeof(kContainerIntro)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // " PERSONAL_TOOLBAR_FOLDER="true"", etc.
+  if (aFolder == mPlacesRoot) {
+    rv = aOutput->Write(kPlacesRootAttribute, sizeof(kPlacesRootAttribute)-1, &dummy);
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else if (aFolder == mBookmarksRoot) {
+    rv = aOutput->Write(kBookmarksRootAttribute, sizeof(kBookmarksRootAttribute)-1, &dummy);
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else if (aFolder == mToolbarFolder) {
+    rv = aOutput->Write(kToolbarFolderAttribute, sizeof(kToolbarFolderAttribute)-1, &dummy);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // id
+  rv = aOutput->Write(kIdAttribute, sizeof(kIdAttribute)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCAutoString id;
+  id.AppendInt(aFolder);
+  rv = aOutput->Write(id.get(), id.Length(), &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // favicon (most folders won't have one)
+  nsCOMPtr<nsIURI> folderURI;
+  rv = mBookmarksService->GetFolderURI(aFolder, getter_AddRefs(folderURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCAutoString folderSpec;
+  rv = folderURI->GetSpec(folderSpec);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = WriteFaviconAttribute(folderSpec, aOutput);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // ">"
+  rv = aOutput->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // title
+  rv = WriteContainerTitle(aFolder, aOutput);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // "</H3>\n"
+  rv = aOutput->Write(kContainerClose, sizeof(kContainerClose)-1, &dummy);
+  return rv;
+}
+
+
+// nsPlacesImportExportService::WriteContainerTitle
+//
+//    Retrieves, escapes and writes the container title to the stream.
+
+nsresult
+nsPlacesImportExportService::WriteContainerTitle(PRInt64 aFolder, nsIOutputStream* aOutput)
+{
+  nsAutoString title;
+  nsresult rv;
+  
+  rv = mBookmarksService->GetFolderTitle(aFolder, title);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  char* escapedTitle = nsEscapeHTML(NS_ConvertUTF16toUTF8(title).get());
+  if (escapedTitle) {
+    PRUint32 dummy;
+    rv = aOutput->Write(escapedTitle, strlen(escapedTitle), &dummy);
+    nsMemory::Free(escapedTitle);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  return NS_OK;
+}
+
+
+// nsBookmarks::WriteItem
+//
+//    "<DT><A HREF="..." ICON="...">Name</A>"
+
+nsresult
+nsPlacesImportExportService::WriteItem(nsINavHistoryResultNode* aItem,
+                          const nsACString& aIndent,
+                          nsIOutputStream* aOutput)
+{
+  PRUint32 dummy;
+  nsresult rv;
+
+  // indent
+  if (!aIndent.IsEmpty()) {
+    rv = aOutput->Write(PromiseFlatCString(aIndent).get(), aIndent.Length(), &dummy);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // '<DT><A'
+  rv = aOutput->Write(kItemOpen, sizeof(kItemOpen)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // ' HREF="http://..."' - note that we need to call GetURI on the result
+  // node because some nodes (eg queries) generate this lazily.
+  rv = aOutput->Write(kHrefAttribute, sizeof(kHrefAttribute)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCAutoString uri;
+  rv = aItem->GetUri(uri);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = WriteEscapedUrl(uri, aOutput);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // ' ICON="..."'
+  rv = WriteFaviconAttribute(uri, aOutput);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // get bookmark id 
+  PRInt64 bookmarkId;
+  rv = aItem->GetBookmarkId(&bookmarkId);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // write id
+  rv = aOutput->Write(kIdAttribute, sizeof(kIdAttribute)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCAutoString id;
+  id.AppendInt(bookmarkId);
+  rv = aOutput->Write(id.get(), id.Length(), &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // keyword (shortcuturl)
+  nsAutoString keyword;
+  rv = mBookmarksService->GetKeywordForBookmark(bookmarkId, keyword);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!keyword.IsEmpty()) {
+    rv = aOutput->Write(kKeywordAttribute, sizeof(kKeywordAttribute)-1, &dummy);
+    NS_ENSURE_SUCCESS(rv, rv);
+    char* escapedKeyword = nsEscapeHTML(NS_ConvertUTF16toUTF8(keyword).get());
+    rv = aOutput->Write(escapedKeyword, strlen(escapedKeyword), &dummy);
+    nsMemory::Free(escapedKeyword);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // get bookmark place URI for annotations
+  nsCOMPtr<nsIURI> placeURI;
+  rv = mBookmarksService->GetItemURI(bookmarkId, getter_AddRefs(placeURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Write WEB_PANEL="true" if the load-in-sidebar annotation is set for the
+  // item
+  PRBool loadInSidebar = PR_FALSE;
+  rv = mAnnotationService->HasAnnotation(placeURI, LOAD_IN_SIDEBAR_ANNO, &loadInSidebar);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (loadInSidebar)
+    aOutput->Write(kWebPanelAttribute, sizeof(kWebPanelAttribute)-1, &dummy);
+
+  // FIXME bug 334408: write last character set here
+
+  // '>'
+  rv = aOutput->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // title
+  nsCAutoString title;
+  rv = aItem->GetTitle(title);
+  NS_ENSURE_SUCCESS(rv, rv);
+  char* escapedTitle = nsEscapeHTML(title.get());
+  if (escapedTitle) {
+    rv = aOutput->Write(escapedTitle, strlen(escapedTitle), &dummy);
+    nsMemory::Free(escapedTitle);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // '</A>\n'
+  rv = aOutput->Write(kItemClose, sizeof(kItemClose)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // FIXME bug 334758: write item description here as a <DD>
+
+  return NS_OK;
+}
+
+
+// WriteLivemark
+//
+//    Similar to WriteItem, this has an additional FEEDURL attribute and
+//    the HREF is optional and points to the source page.
+
+nsresult
+nsPlacesImportExportService::WriteLivemark(PRInt64 aFolderId, const nsACString& aIndent,
+                              nsIOutputStream* aOutput)
+{
+  PRUint32 dummy;
+  nsresult rv;
+
+  // indent
+  if (!aIndent.IsEmpty()) {
+    rv = aOutput->Write(PromiseFlatCString(aIndent).get(), aIndent.Length(), &dummy);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // '<DT><A'
+  rv = aOutput->Write(kItemOpen, sizeof(kItemOpen)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // get feed URI
+  nsCOMPtr<nsIURI> feedURI;
+  rv = mLivemarkService->GetFeedURI(aFolderId, getter_AddRefs(feedURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCString feedSpec;
+  rv = feedURI->GetSpec(feedSpec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // write feed URI
+  rv = aOutput->Write(kFeedURIAttribute, sizeof(kFeedURIAttribute)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = WriteEscapedUrl(feedSpec, aOutput);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // get the optional site URI
+  nsCOMPtr<nsIURI> siteURI;
+  rv = mLivemarkService->GetSiteURI(aFolderId, getter_AddRefs(siteURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (siteURI) {
+    nsCString siteSpec;
+    rv = siteURI->GetSpec(siteSpec);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // write site URI
+    rv = aOutput->Write(kHrefAttribute, sizeof(kHrefAttribute)-1, &dummy);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = WriteEscapedUrl(siteSpec, aOutput);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // write id
+  rv = aOutput->Write(kIdAttribute, sizeof(kIdAttribute)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCAutoString id;
+  id.AppendInt(aFolderId);
+  rv = aOutput->Write(id.get(), id.Length(), &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // '>'
+  rv = aOutput->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // title
+  rv = WriteContainerTitle(aFolderId, aOutput);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // '</A>\n'
+  rv = aOutput->Write(kItemClose, sizeof(kItemClose)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+
+// WriteSeparator
+//
+//    "<HR>"
+
+nsresult
+WriteSeparator(const nsCString& aIndent, nsIOutputStream* aOutput)
+{
+  PRUint32 dummy;
+  nsresult rv;
+
+  // indent
+  if (!aIndent.IsEmpty()) {
+    rv = aOutput->Write(aIndent.get(), aIndent.Length(), &dummy);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  rv = aOutput->Write(kSeparator, sizeof(kSeparator)-1, &dummy);
+  return rv;
+}
+
+
+// WriteEscapedUrl
+//
+//    Writes the given string to the stream escaped as necessary for URLs.
+//
+//    Unfortunately, the old bookmarks system uses a custom hardcoded and
+//    braindead escaping scheme that we need to emulate. It just replaces
+//    quotes with %22 and that's it.
+
+nsresult
+WriteEscapedUrl(const nsCString& aString, nsIOutputStream* aOutput)
+{
+  nsCAutoString escaped(aString);
+  PRInt32 offset;
+  while ((offset = escaped.FindChar('\"')) >= 0) {
+    escaped.Cut(offset, 1);
+    escaped.Insert(NS_LITERAL_CSTRING("%22"), offset);
+  }
+  PRUint32 dummy;
+  return aOutput->Write(escaped.get(), escaped.Length(), &dummy);
+}
+
+
+// nsPlacesImportExportService::WriteContainerContents
+//
+//    The indent here is the indent of the parent. We will add an additional
+//    indent before writing data.
+
+nsresult
+nsPlacesImportExportService::WriteContainerContents(PRInt64 aFolder, const nsACString& aIndent,
+                                       nsIOutputStream* aOutput)
+{
+  nsCAutoString myIndent(aIndent);
+  myIndent.Append(kIndent);
+
+  // get empty options
+  nsresult rv;
+  nsCOMPtr<nsINavHistoryQueryOptions> optionsInterface;
+  rv = mHistoryService->GetNewQueryOptions(getter_AddRefs(optionsInterface));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // QueryFolderChildren requires a concrete options
+  nsCOMPtr<nsINavHistoryQueryOptions> options = do_QueryInterface(optionsInterface);
+  NS_ENSURE_TRUE(options, NS_ERROR_UNEXPECTED);
+
+  // get the query object
+  nsCOMPtr<nsINavHistoryQuery> query;
+  rv = mHistoryService->GetNewQuery(getter_AddRefs(query));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // query for just this folder
+  rv = query->SetFolders(&aFolder, 1);
+  NS_ENSURE_SUCCESS(rv, rv);
+  // query for just bookmarks (necessary?)
+  rv = query->SetOnlyBookmarked(PR_TRUE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // group by folder (necessary? doesn't SetFolders trigger this?)
+  const PRUint16 groupMode = nsINavHistoryQueryOptions::GROUP_BY_FOLDER;
+  rv = options->SetGroupingMode(&groupMode, 1);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // execute query
+  nsCOMPtr<nsINavHistoryResult> result;
+  rv = mHistoryService->ExecuteQuery(query, options, getter_AddRefs(result));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // get root (folder) node
+  nsCOMPtr<nsINavHistoryQueryResultNode> rootNode;
+  rv = result->GetRoot(getter_AddRefs(rootNode));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = rootNode->SetContainerOpen(PR_TRUE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 childCount = 0;
+  rootNode->GetChildCount(&childCount);
+  for (PRUint32 i = 0; i < childCount; ++i) {
+    nsCOMPtr<nsINavHistoryResultNode> child;
+    rv = rootNode->GetChild(i, getter_AddRefs(child));
+    NS_ENSURE_SUCCESS(rv, rv);
+    PRUint32 type = 0;
+    rv = child->GetType(&type);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (type == nsINavHistoryResultNode::RESULT_TYPE_FOLDER) {
+      // bookmarks folder
+      nsCOMPtr<nsINavHistoryFolderResultNode> folderNode = do_QueryInterface(child);
+      PRInt64 folderId;
+      rv = folderNode->GetFolderId(&folderId);
+      NS_ENSURE_SUCCESS(rv, rv);
+      if (aFolder == mPlacesRoot && (folderId == mToolbarFolder ||
+                               folderId == mBookmarksRoot)) {
+        // don't write out the bookmarks menu folder from the
+        // places root. When writing to bookmarks.html, it is reparented
+        // to the menu, which is the root of the namespace. This provides
+        // better backwards compatability.
+        continue;
+      }
+
+      // it could be a regular folder or it could be a livemark
+      PRBool isLivemark;
+      rv = mLivemarkService->IsLivemark(folderId, &isLivemark);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      if (isLivemark)
+        rv = WriteLivemark(folderId, myIndent, aOutput);
+      else
+        rv = WriteContainer(folderId, myIndent, aOutput);
+    } else if (type == nsINavHistoryResultNode::RESULT_TYPE_SEPARATOR) {
+      rv = WriteSeparator(myIndent, aOutput);
+    } else {
+      rv = WriteItem(child, myIndent, aOutput);
+    }
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  return NS_OK;
+}
+
+// nsIPlacesImportExportService::ImportHTMLFromFile
+//
+NS_IMETHODIMP
+nsPlacesImportExportService::ImportHTMLFromFile(nsILocalFile* aFile)
+{
+  // this version is exposed on the interface and disallows changing of roots
+  return ImportHTMLFromFileInternal(aFile, PR_FALSE, 0, PR_FALSE);
+}
+
+// nsIPlacesImportExportService::ImportHTMLFromFileToFolder
+//
+NS_IMETHODIMP
+nsPlacesImportExportService::ImportHTMLFromFileToFolder(nsILocalFile* aFile, PRInt64 aFolderId)
+{
+  // this version is exposed on the interface and disallows changing of roots
+  return ImportHTMLFromFileInternal(aFile, PR_FALSE, aFolderId, PR_FALSE);
+}
+
+nsresult
+nsPlacesImportExportService::ImportHTMLFromFileInternal(nsILocalFile* aFile,
+                                       PRBool aAllowRootChanges,
+                                       PRInt64 aFolder,
+                                       PRBool aIsImportDefaults)
+{
+  nsCOMPtr<nsIFile> file(do_QueryInterface(aFile));
+#ifdef DEBUG_IMPORT
+  nsAutoString path;
+  file->GetPath(path);
+  printf("\nImporting %s\n", NS_ConvertUTF16toUTF8(path).get());
+#endif
+
+  // wrap the import in a transaction to make it faster
+  nsresult rv;
+  mBookmarksService->BeginUpdateBatch();
+
+  nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<BookmarkContentSink> sink = new BookmarkContentSink;
+  NS_ENSURE_TRUE(sink, NS_ERROR_OUT_OF_MEMORY);
+  rv = sink->Init(aAllowRootChanges, mBookmarksService, aFolder, aIsImportDefaults);
+  NS_ENSURE_SUCCESS(rv, rv);
+  parser->SetContentSink(sink);
+
+  // channel: note we have to set the content type or the default "unknown" type
+  // will confuse the parser
+  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);
+  nsCOMPtr<nsIChannel> channel;
+  rv = ioservice->NewChannelFromURI(fileURI, getter_AddRefs(channel));
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = channel->SetContentType(NS_LITERAL_CSTRING("text/html"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // streams
+  nsCOMPtr<nsIInputStream> stream;
+  rv = channel->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);
+
+  // init parser
+  rv = parser->Parse(fileURI, nsnull);
+  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(parser, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = listener->OnStartRequest(channel, nsnull);
+  rv = SyncChannelStatus(channel, rv);
+  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)) {
+      channel->Cancel(rv);
+      break;
+    }
+    if (!available)
+      break; // blocking input stream has none available when done
+
+    rv = listener->OnDataAvailable(channel, nsnull, bufferedstream, 0, available);
+    rv = SyncChannelStatus(channel, rv);
+    if (NS_FAILED(rv))
+      break;
+  }
+  listener->OnStopRequest(channel, nsnull, rv);
+  // commit transaction
+  mBookmarksService->EndUpdateBatch();
+  return NS_OK;
+}
+
+// nsIPlacesImportExportService::ExportHMTLToFile
+//
+NS_IMETHODIMP
+nsPlacesImportExportService::ExportHTMLToFile(nsILocalFile* aBookmarksFile)
+{
+  if (!aBookmarksFile)
+    return NS_ERROR_NULL_POINTER;
+
+  // get a safe output stream, so we don't clobber the bookmarks file unless
+  // all the writes succeeded.
+  nsCOMPtr<nsIOutputStream> out;
+  nsresult rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out),
+                                                aBookmarksFile,
+                                                PR_WRONLY | PR_CREATE_FILE,
+                                                /*octal*/ 0600,
+                                                0);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // We need a buffered output stream for performance.
+  // See bug 202477.
+  nsCOMPtr<nsIOutputStream> strm;
+  rv = NS_NewBufferedOutputStream(getter_AddRefs(strm), out, 4096);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // file header
+  PRUint32 dummy;
+  rv = strm->Write(kFileIntro, sizeof(kFileIntro)-1, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // '<H1'
+  rv = strm->Write(kRootIntro, sizeof(kRootIntro)-1, &dummy); // <H1
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // bookmarks menu favicon
+  nsCOMPtr<nsIURI> folderURI;
+  rv = mBookmarksService->GetFolderURI(mBookmarksRoot, getter_AddRefs(folderURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCAutoString folderSpec;
+  rv = folderURI->GetSpec(folderSpec);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = WriteFaviconAttribute(folderSpec, strm);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // '>Bookmarks</H1>
+  rv = strm->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy); // >
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = WriteContainerTitle(mBookmarksRoot, strm);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = strm->Write(kCloseRootH1, sizeof(kCloseRootH1)-1, &dummy); // </H1>
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // prologue
+  rv = WriteContainerPrologue(EmptyCString(), strm);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // indents
+  nsCAutoString indent;
+  indent.Assign(kIndent);
+
+  // places root
+  rv = WriteContainer(mPlacesRoot, indent, strm);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // bookmarks menu contents
+  rv = WriteContainerContents(mBookmarksRoot, EmptyCString(), strm);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // epilogue
+  rv = WriteContainerEpilogue(EmptyCString(), strm);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // commit the write
+  nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(strm, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return safeStream->Finish();
+}
+
+NS_IMETHODIMP
+nsPlacesImportExportService::BackupBookmarksFile()
+{
+  // get bookmarks file
+  nsCOMPtr<nsIFile> bookmarksFileDir;
+  nsresult rv = NS_GetSpecialDirectory(NS_APP_BOOKMARKS_50_FILE,
+                                       getter_AddRefs(bookmarksFileDir));
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsILocalFile> bookmarksFile(do_QueryInterface(bookmarksFileDir));
+
+  // create if it doesn't exist
+  PRBool exists;
+  rv = bookmarksFile->Exists(&exists);
+  if (NS_FAILED(rv)) {
+    rv = bookmarksFile->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
+    NS_ASSERTION(rv, "Unable to create bookmarks.html!");
+    return rv;
+  }
+
+  // export bookmarks.html
+  rv = ExportHTMLToFile(bookmarksFile);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // archive if needed
+  nsCOMPtr<nsIPrefService> prefServ(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIPrefBranch> bookmarksPrefs;
+  rv = prefServ->GetBranch("browser.bookmarks.", getter_AddRefs(bookmarksPrefs));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRInt32 numberOfBackups;
+  rv = bookmarksPrefs->GetIntPref("max_backups", &numberOfBackups);
+  if (NS_FAILED(rv))
+    numberOfBackups = 5;
+
+  if (numberOfBackups > 0) {
+    rv = ArchiveBookmarksFile(numberOfBackups, PR_FALSE);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
+/**
+ *  ArchiveBookmarksFile()
+ *
+ *  Creates a dated backup once a day in <profile>/bookmarkbackups
+ *
+ *  PRInt32 numberOfBackups - the maximum number of backups to keep
+ *
+ *  PRBool forceArchive - forces creating an archive even if one was 
+ *                        already created that day (overwrites)
+ */
+nsresult
+nsPlacesImportExportService::ArchiveBookmarksFile(PRInt32 numberOfBackups,
+                                         PRBool forceArchive)
+{
+  nsCOMPtr<nsIFile> bookmarksBackupDir;
+  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                       getter_AddRefs(bookmarksBackupDir));
+  NS_ENSURE_SUCCESS(rv, rv);
+  
+  nsDependentCString dirName("bookmarkbackups");
+  rv = bookmarksBackupDir->AppendNative(dirName);
+  NS_ENSURE_SUCCESS(rv, rv);
+  
+  PRBool exists;
+  rv = bookmarksBackupDir->Exists(&exists);
+  if (NS_FAILED(rv) || !exists) {
+    rv = bookmarksBackupDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
+    
+    // if there's no backup folder, there's no backup, fail
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // construct the new leafname
+  PRTime          now64 = PR_Now();
+  PRExplodedTime  nowInfo;
+  PR_ExplodeTime(now64, PR_LocalTimeParameters, &nowInfo);
+  PR_NormalizeTime(&nowInfo, PR_LocalTimeParameters);
+
+  char timeString[128];
+  
+  PR_FormatTime(timeString, 128, "bookmarks-%Y-%m-%d.html", &nowInfo);
+
+  //nsCAutoString backupFilenameCString(timeString);
+  //nsAutoString backupFilenameString = NS_ConvertUTF8toUTF16(backupFilenameCString);
+  nsAutoString backupFilenameString = NS_ConvertUTF8toUTF16((timeString));
+
+  nsCOMPtr<nsIFile> backupFile;
+  if (forceArchive) {
+    // if we have a backup from today, nuke it
+    nsCOMPtr<nsIFile> currentBackup;
+    rv = bookmarksBackupDir->Clone(getter_AddRefs(currentBackup));
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = currentBackup->Append(backupFilenameString);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = currentBackup->Exists(&exists);
+    if (NS_SUCCEEDED(rv) && exists) {
+      rv = currentBackup->Remove(PR_FALSE);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  } else {
+    nsCOMPtr<nsISimpleEnumerator> existingBackups;
+    rv = bookmarksBackupDir->GetDirectoryEntries(getter_AddRefs(existingBackups));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsStringArray backupFileNames;
+
+    PRBool hasMoreElements = PR_FALSE;
+    PRBool hasCurrentBackup = PR_FALSE;
+    
+    while (NS_SUCCEEDED(existingBackups->HasMoreElements(&hasMoreElements)) &&
+           hasMoreElements)
+    {
+      rv = existingBackups->GetNext(getter_AddRefs(backupFile));
+      NS_ENSURE_SUCCESS(rv, rv);
+      nsAutoString backupName;
+      rv = backupFile->GetLeafName(backupName);
+      NS_ENSURE_SUCCESS(rv, rv);
+      
+      // the backup for today exists, do not create later
+      if (backupName == backupFilenameString) {
+        hasCurrentBackup = PR_TRUE;
+        continue;
+      }
+
+      // mark the rest for possible removal
+      if (Substring(backupName, 0, 10) == NS_LITERAL_STRING("bookmarks-"))
+        backupFileNames.AppendString(backupName);
+    }
+
+    if (numberOfBackups > 0 && backupFileNames.Count() >= numberOfBackups) {
+      PRInt32 numberOfBackupsToDelete = backupFileNames.Count() - numberOfBackups + 1;
+      backupFileNames.Sort();
+
+      while (numberOfBackupsToDelete--) {
+        (void)bookmarksBackupDir->Clone(getter_AddRefs(backupFile));
+        (void)backupFile->Append(*backupFileNames[0]);
+        (void)backupFile->Remove(PR_FALSE);
+        backupFileNames.RemoveStringAt(0);
+      }
+    }
+
+    if (hasCurrentBackup)
+      return NS_OK;
+  }
+
+  nsCOMPtr<nsIFile> bookmarksFile;
+  rv = NS_GetSpecialDirectory(NS_APP_BOOKMARKS_50_FILE,
+                              getter_AddRefs(bookmarksFile));
+  NS_ENSURE_SUCCESS(rv, rv);
+  
+  rv = bookmarksFile->CopyTo(bookmarksBackupDir, backupFilenameString);
+  // at least dump something out in case this fails in a debug build
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return rv;
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/places/src/nsPlacesImportExportService.h
@@ -0,0 +1,48 @@
+#ifndef nsPlacesImportExportService_h__
+#define nsPlacesImportExportService_h__
+
+#include "nsIPlacesImportExportService.h"
+
+#include "nsCOMPtr.h"
+#include "nsILocalFile.h"
+#include "nsIOutputStream.h"
+#include "nsIFaviconService.h"
+#include "nsIAnnotationService.h"
+#include "nsILivemarkService.h"
+#include "nsINavHistoryService.h"
+#include "nsINavBookmarksService.h"
+
+class nsPlacesImportExportService : public nsIPlacesImportExportService
+{
+  public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIPLACESIMPORTEXPORTSERVICE
+    nsPlacesImportExportService();
+
+  private:
+    virtual ~nsPlacesImportExportService();
+
+  protected:
+    nsCOMPtr<nsIFaviconService> mFaviconService;
+    nsCOMPtr<nsIAnnotationService> mAnnotationService;
+    nsCOMPtr<nsINavBookmarksService> mBookmarksService;
+    nsCOMPtr<nsINavHistoryService> mHistoryService;
+    nsCOMPtr<nsILivemarkService> mLivemarkService;
+
+    PRInt64 mPlacesRoot;
+    PRInt64 mBookmarksRoot;
+    PRInt64 mToolbarFolder;
+
+    nsresult ImportHTMLFromFileInternal(nsILocalFile* aFile, PRBool aAllowRootChanges,
+                                       PRInt64 aFolder, PRBool aIsImportDefaults);
+    nsresult WriteContainer(PRInt64 aFolder, const nsACString& aIndent, nsIOutputStream* aOutput);
+    nsresult WriteContainerHeader(PRInt64 aFolder, const nsACString& aIndent, nsIOutputStream* aOutput);
+    nsresult WriteContainerTitle(PRInt64 aFolder, nsIOutputStream* aOutput);
+    nsresult WriteItem(nsINavHistoryResultNode* aItem, const nsACString& aIndent, nsIOutputStream* aOutput);
+    nsresult WriteLivemark(PRInt64 aFolderId, const nsACString& aIndent, nsIOutputStream* aOutput);
+    nsresult WriteContainerContents(PRInt64 aFolder, const nsACString& aIndent, nsIOutputStream* aOutput);
+
+    nsresult ArchiveBookmarksFile(PRInt32 aNumberOfBackups, PRBool aForceArchive);
+};
+
+#endif // nsPlacesImportExportService_h__
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/Makefile.in
@@ -0,0 +1,50 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla.org.
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Dietrich Ayala <dietrich@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE		= test_browser_places
+
+XPCSHELL_TESTS = unit
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/unit/bookmarks.preplaces.html
@@ -0,0 +1,33 @@
+<!DOCTYPE NETSCAPE-Bookmark-file-1>
+<!-- This is an automatically generated file.
+     It will be read and overwritten.
+     DO NOT EDIT! -->
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
+<TITLE>Bookmarks</TITLE>
+<H1 LAST_MODIFIED="1177541029">Bookmarks</H1>
+
+<DL><p>
+    <DT><A HREF="http://en-US.add-ons.mozilla.com/en-US/firefox/bookmarks/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M%2F3AAAABGdBTUEAANbY1E9YMgAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAPkSURBVHjaYmAAgrjyOnOGiKxqxT9%2F%2FvwHCCCGuNJKLpAo49KTL%2F5%2F%2F8PMABBADJFZFWwXnn%2F%2FDxJYeOLNf0aQ9AIg48%2Ff%2Fwwfvv1hAAggZpBAYlWdnrqJLcPVE4e%2Bsuy7%2FfH%2F%2B88%2FGdjY2Bj%2BcCqHMey6%2Ben%2F379%2F%2F%2F8B6unZ9ew%2Fy54jV249f6%2Bm9uXnX4Y9qyaoAAQAhAB7%2FwEAAAAAY3h%2BG1RdbeMMCgkB9%2Fr%2BAPL2%2FAC3vsyi5NG6YQFcbnwdZ3F44uru9gAAAQAAUjEVALPT7wDu9v4A5erz%2FgL19vr16PD6AAUHBgDu9PwA%2F%2F8AAO%2F2%2FgD0%2BP0A7e7x8QPYzsX38vj9g%2BPk6hkLFiAxy%2BP4AeHj5%2FXFtp9GonxaagII7AawXyprpf%2F%2FZ5L5%2Fe%2Fv9%2B%2Fff91ZN7nrG0icJSqrkknJxHm1h5Nl0J8%2F%2Fxg%2B%2FwDa%2Febzv39%2FWKQ2TG97ycIvq%2Bvn52oVxMHGxHDj8RcGQT4uEGZyCct98e3LL3YmJ2enNYxAi%2B48%2B8QQaizGIMLFBLaSlYWZgYWDWZaJhY2V%2BcvPfwz%2BeiIMf%2F%2F%2BY9CV4GAQ42Zh%2BPPvP8O%2Fv%2F%2BZmG7cff7u49c%2FDNtufGZgYmJiOHLvG8Pt1z8Yfv3%2Bz%2FDn19%2B3TCd2LNV7%2F%2FU3w7vPvxkWnHzDcOPFd4ZvQBPv3L79aM%2BS3nfMN88d%2BfyXkW0Lq6BiGAs7J8fHT9%2F%2FXTy%2BY82Lp0cdb5889hcgQJNU85JYFMXP%2B5aHqRmmZJ9kKMGAEBgtDCYYY6BFa%2BlrPc6yRf0LYYtZzG4YaNGibUNJVLuIcBNUTLMQM8ZoppdiaXnf9Xlf5z4ounDu4p57f%2Ff8Pt50SH9ZEfUuLehy93yMRBNroVAg6PV2yBbO9c94tK5v7suF3%2FlMs1o8oU27ltvIMic7fJv7uuqLJGa2UpPxlCILICBtGz1pYWooakeoDaTFgBtNWm04zl%2Fkbs53FnZ%2FZO%2BldGbFP5aaP50cj41pigi8XFjF2zp8ivpgsFMFHp0GgrQZL4DuYGCE6f3pzoBnUwRB8sYi4QGKHf7b5d8HiHWpMBsPvLKDeFiHmVEPBN0yMJyMIUhfb6gXbMkr4xtq1J6Z36eLpmiDH508LNShbDzB4kTIATguNsBqA1CHElJDhGdCGWsDkYY%2FTJh3lUelu384yTlzrtgDWVaggvG8qhDnYcEwwWi0wET%2FTNTh9Gh%2FvVn7v%2B2I%2BHlpWXS59ORgfOr7UGRkVNMUAWPtCMnHdbjjATFNKJeKpdLZYQY0crDzLUvfbHxdqfllj6a7p2VVjUqyGhYwPpZFqxYlf6hZ%2F7X3c736%2Fv4LV1blv94gEvsAAAAASUVORK5CYII%3D" ID="rdf:#$CnoJ1">Get Bookmark Add-ons</A>
+    <HR>
+    <DT><H3 LAST_MODIFIED="1177541040" PERSONAL_TOOLBAR_FOLDER="true" ID="rdf:#$FvPhC3">Bookmarks Toolbar Folder</H3>
+<DD>Add bookmarks to this folder to see them displayed on the Bookmarks Toolbar
+    <DL><p>
+        <DT><A HREF="http://en-US.www.mozilla.com/en-US/firefox/central/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" ID="rdf:#$GvPhC3">Getting Started</A>
+        <DT><A HREF="http://en-US.fxfeeds.mozilla.com/en-US/firefox/livebookmarks/" LAST_MODIFIED="1177541035" FEEDURL="http://en-US.fxfeeds.mozilla.com/en-US/firefox/headlines.xml" ID="rdf:#$HvPhC3">Latest Headlines</A>
+    </DL><p>
+    <HR>
+    <DT><H3 ID="rdf:#$ZvPhC3">Mozilla Firefox</H3>
+    <DL><p>
+        <DT><A HREF="http://en-US.www.mozilla.com/en-US/firefox/help/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" ID="rdf:#$22iCK1">Help and Tutorials</A>
+        <DT><A HREF="http://en-US.www.mozilla.com/en-US/firefox/customize/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" ID="rdf:#$32iCK1">Customize Firefox</A>
+        <DT><A HREF="http://en-US.www.mozilla.com/en-US/firefox/community/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" ID="rdf:#$42iCK1">Get Involved</A>
+        <DT><A HREF="http://en-US.www.mozilla.com/en-US/firefox/about/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==" ID="rdf:#$52iCK1">About Us</A>
+    </DL><p>
+    <DT><H3 ADD_DATE="1177541020" LAST_MODIFIED="1177541047" ID="rdf:#$74Gpx2">test</H3>
+    <DL><p>
+        <DT><A HREF="http://test/post" ADD_DATE="1177375336" LAST_MODIFIED="1177375423" SHORTCUTURL="test" WEB_PANEL="true" POST_DATA="hidden1%3Dbar&amp;text1%3D%25s" LAST_CHARSET="ISO-8859-1" ID="rdf:#$pYFe7">test post keyword</A>
+<DD>description
+        <DT><A HREF="http://test/micsum" ADD_DATE="1177375377" LAST_MODIFIED="1177541023" LAST_CHARSET="ISO-8859-1" MICSUM_GEN_URI="urn:source:http://dietrich.ganx4.com/mozilla/test-microsummary.xml" MICSUM_EXPIRATION="1177542823620" GENERATED_TITLE="id:462fd9a2e5b66" ID="rdf:#$qYFe7">test microsummary</A>
+<DD>test microsummary
+    </DL><p>
+</DL><p>
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/unit/head_bookmarks.js
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Places.
+ *
+ * The Initial Developer of the Original Code is
+ * Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Brian Ryner <bryner@brianryner.com>
+ *  Dietrich Ayala <dietrich@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const NS_APP_USER_PROFILE_50_DIR = "ProfD";
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cr = Components.results;
+
+function LOG(aMsg) {
+  aMsg = ("*** PLACES TESTS: " + aMsg);
+  Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).
+                                      logStringMessage(aMsg);
+  print(aMsg);
+}
+
+// If there's no location registered for the profile direcotry, register one now.
+var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
+var profileDir = null;
+try {
+  profileDir = dirSvc.get(NS_APP_USER_PROFILE_50_DIR, Ci.nsIFile);
+} catch (e) {}
+if (!profileDir) {
+  // Register our own provider for the profile directory.
+  // It will simply return the current directory.
+  var provider = {
+    getFile: function(prop, persistent) {
+      persistent.value = true;
+      if (prop == NS_APP_USER_PROFILE_50_DIR) {
+        return dirSvc.get("CurProcD", Ci.nsIFile);
+      }
+      throw Cr.NS_ERROR_FAILURE;
+    },
+    QueryInterface: function(iid) {
+      if (iid.equals(Ci.nsIDirectoryServiceProvider) ||
+          iid.equals(Ci.nsISupports)) {
+        return this;
+      }
+      throw Cr.NS_ERROR_NO_INTERFACE;
+    }
+  };
+  dirSvc.QueryInterface(Ci.nsIDirectoryService).registerProvider(provider);
+}
+
+var iosvc = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+
+function uri(spec) {
+  return iosvc.newURI(spec, null, null);
+}
+
+// Delete a previously created sqlite file
+function clearDB() {
+  try {
+    var file = dirSvc.get('ProfD', Ci.nsIFile);
+    file.append("places.sqlite");
+    if (file.exists())
+      file.remove(false);
+  } catch(ex) { dump("Exception: " + ex); }
+}
+clearDB();
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/unit/tail_bookmarks.js
@@ -0,0 +1,41 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Places.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla.org
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Dietrich Ayala <dietrich@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// put cleanup of the bookmarks test here.
+
+// remove bookmarks file
+clearDB();
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/unit/test_bookmarks_html.js
@@ -0,0 +1,170 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.com code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corp.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Dietrich Ayala <dietrich@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// get history service
+try {
+  var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].getService(Ci.nsINavHistoryService);
+} catch(ex) {
+  do_throw("Could not get nav-history-service\n");
+}
+
+// Get bookmark service
+try {
+  var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
+} catch(ex) {
+  do_throw("Could not get nav-bookmarks-service\n");
+}
+
+// main
+function run_test() {
+  // get places import/export service
+  var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].getService(Ci.nsIPlacesImportExportService);
+
+  // get file pointers
+  var bookmarksFileOld = do_get_file("browser/components/places/tests/unit/bookmarks.preplaces.html");
+  var bookmarksFileNew = dirSvc.get("ProfD", Ci.nsILocalFile);
+  bookmarksFileNew.append("bookmarks.exported.html");
+
+  // create bookmarks.exported.html if necessary
+  if (!bookmarksFileOld.exists())
+    bookmarksFileOld.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, 0600);
+  if (!bookmarksFileOld.exists())
+    do_throw("couldn't create file: bookmarks.exported.html");
+
+  // Test importing a non-Places canonical bookmarks file.
+  // 1. import bookmarks.preplaces.html
+  // 2. run the test-suite
+  bmsvc.removeFolderChildren(bmsvc.bookmarksRoot);
+  importer.importHTMLFromFile(bookmarksFileOld);
+  testCanonicalBookmarks(bmsvc.bookmarksRoot); 
+
+  // Test exporting a Places canonical bookmarks file.
+  // 1. export to bookmarks.exported.html
+  // 2. import bookmarks.exported.html
+  // 3. run the test-suite
+  /*
+  importer.exportHTMLToFile(bookmarksFileNew);
+  bmsvc.removeFolderChildren(bmsvc.bookmarksRoot);
+  importer.importHTMLFromFile(bookmarksFileNew);
+  testCanonicalBookmarks(bmsvc.bookmarksRoot); 
+  */
+
+  // Test importing a Places canonical bookmarks file to a specific folder.
+  // 1. create a new folder
+  // 2. import bookmarks.exported.html to that folder
+  // 3. run the test-suite
+  /*
+  var testFolder = bmsvc.createFolder(bmsvc.bookmarksRoot, "test", bmsvc.DEFAULT_INDEX);
+  importer.importHTMLFromFileToFolder(bookmarksFileNew, testFolder);
+  testCanonicalBookmarks(testFolder); 
+  */
+
+  /*
+  XXX if there are new fields we add to the bookmarks HTML format
+  then test them here
+  Test importing a Places canonical bookmarks file
+  1. empty the bookmarks datastore
+  2. import bookmarks.places.html
+  3. run the test-suite
+  4. run the additional-test-suite
+  */
+
+  /*
+  XXX could write another more generic testsuite:
+  - a function that fetches all descendant nodes of a folder with all non-static
+    node properties removed, and serializes w/ toSource()
+  - import a file, get serialization
+  - export it, re-import, get serialization
+  - do_check_eq(str1, str2)
+  */
+}
+
+// Tests a bookmarks datastore that has a set of bookmarks, etc
+// that flex each supported field and feature.
+function testCanonicalBookmarks(aFolder) {
+  // query to see if the deleted folder and items have been imported
+  try {
+    var query = histsvc.getNewQuery();
+    query.setFolders([bmsvc.bookmarksRoot], 1);
+    var result = histsvc.executeQuery(query, histsvc.getNewQueryOptions());
+    var rootNode = result.root;
+    rootNode.containerOpen = true;
+    // get test folder
+    var idx = 3; // XXX why does passing the value directly fail?
+    var testFolder = rootNode.getChild(idx);
+    do_check_eq(testFolder.type, testFolder.RESULT_TYPE_FOLDER);
+    do_check_eq(testFolder.title, "test");
+    testFolder = testFolder.QueryInterface(Ci.nsINavHistoryFolderResultNode);
+    do_check_eq(testFolder.hasChildren, true);
+    // open test folder, and test the children 
+    testFolder.containerOpen = true;
+    var cc = testFolder.childCount;
+    do_check_eq(cc, 2);
+    // test bookmark 1
+    var testBookmark1 = testFolder.getChild(0);
+    // url
+    do_check_eq("http://test/post", testBookmark1.uri);
+    // title
+    do_check_eq("test post keyword", testBookmark1.title);
+    // keyword
+    do_check_eq("test", bmsvc.getKeywordForBookmark(testBookmark1.bookmarkId));
+    // sidebar
+    // add date 
+    // last modified
+    // post data
+    // last charset 
+    // description 
+
+    // test bookmark 2
+    var testBookmark2 = testFolder.getChild(1);
+    // url
+    do_check_eq("http://test/micsum", testBookmark2.uri);
+    // title
+    do_check_eq("test microsummary", testBookmark2.title);
+    // micsum uri
+    // micsum expiration
+    // micsum generated title
+
+    // clean up
+    testFolder.containerOpen = false;
+    rootNode.containerOpen = false;
+  }
+  catch(ex) {
+    do_throw("bookmarks query tests failed: " + ex);
+  }
+}
--- a/browser/installer/unix/packages-static
+++ b/browser/installer/unix/packages-static
@@ -55,16 +55,17 @@ bin/xpicleanup
 ; [Components]
 bin/components/accessibility.xpt
 bin/components/appshell.xpt
 bin/components/appstartup.xpt
 bin/components/autocomplete.xpt
 bin/components/autoconfig.xpt
 bin/components/bookmarks.xpt
 bin/components/browsercompsbase.xpt
+bin/components/browserplaces.xpt
 bin/components/browsersearch.xpt
 bin/components/browser-feeds.xpt
 bin/components/caps.xpt
 bin/components/chardet.xpt
 bin/components/chrome.xpt
 bin/components/commandhandler.xpt
 bin/components/commandlines.xpt
 bin/components/composer.xpt
--- a/browser/installer/windows/packages-static
+++ b/browser/installer/windows/packages-static
@@ -61,16 +61,17 @@ bin\AccessibleMarshal.dll
 bin\components\accessibility.xpt
 bin\components\accessibility-msaa.xpt
 bin\components\appshell.xpt
 bin\components\appstartup.xpt
 bin\components\autocomplete.xpt
 bin\components\autoconfig.xpt
 bin\components\bookmarks.xpt
 bin\components\browsercompsbase.xpt
+bin\components\browserplaces.xpt
 bin\components\browsersearch.xpt
 bin\components\browser-feeds.xpt
 bin\components\caps.xpt
 bin\components\chardet.xpt
 bin\components\chrome.xpt
 bin\components\commandhandler.xpt
 bin\components\commandlines.xpt
 bin\components\composer.xpt
deleted file mode 100644
--- a/browser/locales/en-US/chrome/browser/places/default_places.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE NETSCAPE-Bookmark-file-1>
-<!-- This is an automatically generated file.
-     It will be read and overwritten.
-     DO NOT EDIT! -->
-<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
-<TITLE>Bookmarks and History</TITLE>
-<H1 PLACES_ROOT="true">Bookmarks and History</H1>
-<DL><p>
-    <DT><A HREF="place:&beginTime=-2592000000000&beginTimeRef=1&endTime=7200000000&endTimeRef=2&sort=4&type=1">History</A>
-    <DD>Shows all browsing history
-    <DT><H3 BOOKMARKS_MENU="true">Bookmarks</H3>
-    <DD>Add bookmarks to this folder to see them displayed on the Bookmarks Menu
-    <DL><p>
-      <DT><H3 PERSONAL_TOOLBAR_FOLDER="true">Bookmarks Toolbar Folder</H3>
-      <DD>Add bookmarks to this folder to see them displayed on the Bookmarks Toolbar
-      <DL><p>
-      </DL><p>
-    </DL><p>
-    <DT><A HREF="place:&annotation=livemark%2FfeedURI" ICON_URI="chrome://browser/skin/places/livemarkItem.png">Subscriptions</A>
-    <DD>Shows all Subscribed Feeds
-</DL><p>
--- a/toolkit/components/places/public/nsIAnnotationService.idl
+++ b/toolkit/components/places/public/nsIAnnotationService.idl
@@ -36,23 +36,21 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 %{C++
 #include "nsTArray.h"
 #include "nsCOMArray.h"
-#include "nsString.h"
 %}
 
 interface nsIURI;
 interface nsIVariant;
 
-[ptr] native CStringArray(nsTArray<nsCString>);
 [ptr] native URIArray(nsCOMArray<nsIURI>);
 
 [scriptable, uuid(d41c9510-2377-4728-b275-bdad6a0521f8)]
 interface nsIAnnotationObserver : nsISupports
 {
     /**
      * Called when an annotation value is set. It could be a new annotation,
      * or it could be a new value for an existing annotation.
@@ -62,17 +60,17 @@ interface nsIAnnotationObserver : nsISup
     /**
      * Called when an annotation is deleted. If aName is empty, then ALL
      * annotations for the given URI have been deleted. This is not called when
      * annotations are expired (normally happens when the app exits).
      */
     void onAnnotationRemoved(in nsIURI aURI, in AUTF8String aName);
 };
 
-[scriptable, uuid(e19a463a-0bd5-4e36-b552-e52aa4220b27)]
+[scriptable, uuid(15999472-f158-11db-8314-0800200c9a66)]
 interface nsIAnnotationService : nsISupports
 {
     /**
      * Valid values for aExpiration, which sets the expiration policy for your
      * annotation. These times are measured since the last visit date of the
      * page in question. This means that if you set an annotation with anything
      * but session expiration, it will not expire so long as the user keeps
      * visiting the page from time to time.
@@ -264,21 +262,16 @@ interface nsIAnnotationService : nsISupp
      *   var annotations = annotator.getPageAnnotations(myURI, {});
      * You probably don't want to use this from C++, use
      * GetPageAnnotationsTArray instead.
      */
     void getPageAnnotationNames(in nsIURI aURI, out PRUint32 count,
       [retval, array, size_is(count)] out nsIVariant result);
 
     /**
-     * TArray version of getPageAnnotationNames for ease of use in C++ code.
-     */
-    [noscript] void GetPageAnnotationNamesTArray(in nsIURI aURI, in CStringArray aResult);
-
-    /**
      * Test for annotation existance.
      */
     boolean hasAnnotation(in nsIURI aURI, in AUTF8String aName);
 
     /**
      * Removes a specific annotation. Succeeds even if the annotation is
      * not found.
      */
--- a/toolkit/components/places/public/nsINavBookmarksService.idl
+++ b/toolkit/components/places/public/nsINavBookmarksService.idl
@@ -176,17 +176,17 @@ interface nsINavBookmarkObserver : nsISu
 };
 
 /**
  * The BookmarksService interface provides methods for managing bookmarked
  * history items.  Bookmarks consist of a set of user-customizable
  * folders.  A URI in history can be contained in one or more such folders.
  */
 
-[scriptable, uuid(7a17cbcb-b4c8-4485-a254-9e8d58663fe7)]
+[scriptable, uuid(48f31924-f158-11db-8314-0800200c9a66)]
 interface nsINavBookmarksService : nsISupports
 {
   /**
    * The folder ID of the Places root.
    */
   readonly attribute PRInt64 placesRoot;
 
   /**
@@ -491,28 +491,9 @@ interface nsINavBookmarksService : nsISu
    */
   void beginUpdateBatch();
 
   /**
    * Causes observers to be notified of an endUpdateBatch when a batch is
    * done changing. Should match beginUpdateBatch or bad things will happen.
    */
   void endUpdateBatch();
-
-
-  /**
-   * Loads the given bookmarks.html file and merges it with the current
-   * bookmarks hierarchy.
-   */
-  void importBookmarksHTML(in nsIURI url);
-
-
-  /**
-   * Loads the given bookmarks.html file and puts it in the given folder
-   */
-  void importBookmarksHTMLToFolder(in nsIURI url, in PRInt64 folder);
-
-
-  /**
-   * Saves the current bookmarks hierarchy to a bookmarks.html file.
-   */
-  void exportBookmarksHTML(in nsIFile file);
 };
--- a/toolkit/components/places/src/Makefile.in
+++ b/toolkit/components/places/src/Makefile.in
@@ -76,17 +76,16 @@ REQUIRES = xpcom \
            widget \
            url-classifier \
            microsummaries \
            $(NULL)
 
 CPPSRCS = \
           nsAnnoProtocolHandler.cpp \
           nsAnnotationService.cpp \
-          nsBookmarksHTML.cpp \
           nsFaviconService.cpp \
           nsNavHistory.cpp \
           nsNavHistoryAutoComplete.cpp \
           nsNavHistoryExpire.cpp \
           nsNavHistoryQuery.cpp \
           nsNavHistoryResult.cpp \
           nsNavBookmarks.cpp \
           nsMaybeWeakPtr.cpp \
--- a/toolkit/components/places/src/nsAnnotationService.cpp
+++ b/toolkit/components/places/src/nsAnnotationService.cpp
@@ -632,17 +632,17 @@ nsAnnotationService::GetPageAnnotationNa
   *aCount = names.Length();
 
   return NS_OK;
 }
 
 
 // nsAnnotationService::GetPageAnnotationNamesTArray
 
-NS_IMETHODIMP
+nsresult
 nsAnnotationService::GetPageAnnotationNamesTArray(nsIURI* aURI,
                                                   nsTArray<nsCString>* aResult)
 {
   aResult->Clear();
 
   nsNavHistory* history = nsNavHistory::GetHistoryService();
   NS_ENSURE_TRUE(history, NS_ERROR_FAILURE);
 
--- a/toolkit/components/places/src/nsAnnotationService.h
+++ b/toolkit/components/places/src/nsAnnotationService.h
@@ -115,11 +115,12 @@ protected:
                                      const nsACString& aName);
   nsresult StartSetAnnotation(nsIURI* aURI,
                               const nsACString& aName,
                               PRInt32 aFlags, PRInt32 aExpiration,
                               PRInt32 aType, mozIStorageStatement** aStatement);
   void CallSetObservers(nsIURI* aURI, const nsACString& aName);
 
   static nsresult MigrateFromAlpha1(mozIStorageConnection* aDBConn);
+  nsresult GetPageAnnotationNamesTArray(nsIURI* aURI, nsTArray<nsCString>* aResult);
 };
 
 #endif /* nsAnnotationService_h___ */
deleted file mode 100644
--- a/toolkit/components/places/src/nsBookmarksHTML.cpp
+++ /dev/null
@@ -1,1879 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla History System
- *
- * The Initial Developer of the Original Code is
- * Google Inc.
- * Portions created by the Initial Developer are Copyright (C) 2005
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Brett Wilson <brettw@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/**
- * Importer/exporter between the mozStorage-based bookmarks and the old-style
- * "bookmarks.html"
- *
- * Format:
- *
- * Primary heading := h1
- *   Old version used this to set attributes on the bookmarks RDF root, such
- *   as the last modified date. We only use H1 to check for the attribute
- *   PLACES_ROOT, which tells us that this hierarchy root is the places root.
- *   For backwards compatability, if we don't find this, we assume that the
- *   hierarchy is rooted at the bookmarks menu.
- * Heading := any heading other than h1
- *   Old version used this to set attributes on the current container. We only
- *   care about the content of the heading container, which contains the title
- *   of the bookmark container.
- * Bookmark := a
- *   HREF is the destination of the bookmark
- *   FEEDURL is the URI of the RSS feed if this is a livemark.
- *   LAST_CHARSET should be stored as an annotation (FIXME bug 334408) so that the
- *     next time we go to that page we remember the user's preference.
- *   WEB_PANEL is set to "true" if the bookmark should be loaded in the sidebar.
- *   ICON will be stored in the favicon service
- *   ICON_URI is new for places bookmarks.html, it refers to the original
- *     URI of the favicon so we don't have to make up favicon URLs.
- *   Text of the <a> container is the name of the bookmark
- *   Ignored: ADD_DATE, LAST_VISIT, LAST_MODIFIED, ID
- * Bookmark comment := dd
- *   This affects the previosly added bookmark
- * Separator := hr
- *   Insert a separator into the current container
- * The folder hierarchy is defined by <dl>/<ul>/<menu> (the old importing code
- *     handles all these cases, when we write, use <dl>).
- *
- * Overall design
- * --------------
- *
- * We need to emulate a recursive parser. A "Bookmark import frame" is created
- * corresponding to each folder we encounter. These are arranged in a stack,
- * and contain all the state we need to keep track of.
- *
- * A frame is created when we find a heading, which defines a new container.
- * The frame also keeps track of the nesting of <DL>s, (in well-formed
- * bookmarks files, these will have a 1-1 correspondence with frames, but we
- * try to be a little more flexible here). When the nesting count decreases
- * 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 "nsToolkitCompsCID.h"
-#include "nsCOMPtr.h"
-#include "nsCRT.h"
-#include "nsEscape.h"
-#include "nsFaviconService.h"
-#include "nsIAnnotationService.h"
-#include "nsIFile.h"
-#include "nsIHTMLContentSink.h"
-#include "nsILivemarkService.h"
-#include "nsIParser.h"
-#include "nsIServiceManager.h"
-#include "nsNavBookmarks.h"
-#include "nsNavHistory.h"
-#include "nsAnnotationService.h"
-#include "nsNavHistoryResult.h"
-#include "nsNetUtil.h"
-#include "nsParserCIID.h"
-#include "nsString.h"
-#include "nsTArray.h"
-#include "nsUnicharUtils.h"
-#include "mozStorageHelper.h"
-#include "plbase64.h"
-#include "prtime.h"
-
-static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
-
-#define KEY_TOOLBARFOLDER_LOWER "personal_toolbar_folder"
-#define KEY_BOOKMARKSMENU_LOWER "bookmarks_menu"
-#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_ID_LOWER "id"
-
-#define LOAD_IN_SIDEBAR_ANNO NS_LITERAL_CSTRING("bookmarkProperties/loadInSidebar")
-
-#define BOOKMARKS_MENU_ICON_URI "chrome://browser/skin/places/bookmarksMenu.png"
-#define BOOKMARKS_TOOLBAR_ICON_URI "chrome://browser/skin/places/bookmarksToolbar.png"
-
-// define to get debugging messages on console about import
-//#define DEBUG_IMPORT
-
-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(PR_FALSE)
-  {
-  }
-
-  enum ContainerType { Container_Normal,
-                       Container_Places,
-                       Container_Menu,
-                       Container_Toolbar };
-
-  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;
-
-  // Container Id, see above
-  PRInt64 mLastContainerId;
-
-  // 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 preceeding
-  // <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.
-  PRBool 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, PRInt64* aContainerId)
-  {
-    *aHeading = mPreviousText;
-    *aContainerType = mLastContainerType;
-    *aContainerId = mLastContainerId;
-    mPreviousText.Truncate(0);
-  }
-
-  // Contains the id of an imported, or newly created bookmark.
-  PRInt64 mPreviousId;
-};
-
-
-/**
- * The content sink stuff is based loosely on 
- */
-class BookmarkContentSink : public nsIHTMLContentSink
-{
-public:
-  nsresult Init(PRBool aAllowRootChanges,
-                nsINavBookmarksService* bookmarkService,
-                PRInt64 aFolder,
-                PRBool aIsImportDefaults);
-
-  NS_DECL_ISUPPORTS
-
-  // nsIContentSink (superclass of nsIHTMLContentSink)
-  NS_IMETHOD WillTokenize() { return NS_OK; }
-  NS_IMETHOD WillBuildModel() { return NS_OK; }
-  NS_IMETHOD DidBuildModel() { return NS_OK; }
-  NS_IMETHOD WillInterrupt() { return NS_OK; }
-  NS_IMETHOD WillResume() { return NS_OK; }
-  NS_IMETHOD SetParser(nsIParser* aParser) { return NS_OK; }
-  virtual void FlushPendingNotifications(mozFlushType aType) { }
-  NS_IMETHOD SetDocumentCharset(nsACString& aCharset) { return NS_OK; }
-  virtual nsISupports *GetTarget() { return nsnull; }
-
-  // nsIHTMLContentSink
-#ifdef MOZILLA_1_8_BRANCH
-  NS_IMETHOD SetTitle(const nsString& aValue) { return NS_OK; }
-  NS_IMETHOD OpenHTML(const nsIParserNode& aNode) { return NS_OK; }
-  NS_IMETHOD CloseHTML() { return NS_OK; }
-  NS_IMETHOD OpenHead(const nsIParserNode& aNode) { return NS_OK; }
-  NS_IMETHOD CloseHead() { return NS_OK; }
-  NS_IMETHOD OpenBody(const nsIParserNode& aNode) { return NS_OK; }
-  NS_IMETHOD CloseBody() { return NS_OK; }
-  NS_IMETHOD OpenForm(const nsIParserNode& aNode) { return NS_OK; }
-  NS_IMETHOD CloseForm() { return NS_OK; }
-  NS_IMETHOD OpenMap(const nsIParserNode& aNode) { return NS_OK; }
-  NS_IMETHOD CloseMap() { return NS_OK; }
-  NS_IMETHOD OpenFrameset(const nsIParserNode& aNode) { return NS_OK; }
-  NS_IMETHOD CloseFrameset() { return NS_OK; }
-  NS_IMETHOD AddHeadContent(const nsIParserNode& aNode) { return NS_OK; }
-#else
-  NS_IMETHOD OpenHead() { return NS_OK; }
-#endif
-  NS_IMETHOD BeginContext(PRInt32 aPosition) { return NS_OK; }
-  NS_IMETHOD EndContext(PRInt32 aPosition) { return NS_OK; }
-  NS_IMETHOD IsEnabled(PRInt32 aTag, PRBool* aReturn)
-    { *aReturn = PR_TRUE; return NS_OK; }
-  NS_IMETHOD WillProcessTokens() { 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 AddComment(const nsIParserNode& aNode) { return NS_OK; }
-  NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode) { return NS_OK; }
-  NS_IMETHOD AddDocTypeDecl(const nsIParserNode& aNode) { return NS_OK; }
-  NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode) { return NS_OK; }
-  NS_IMETHOD_(PRBool) IsFormOnStack() { return PR_FALSE; }
-
-protected:
-  nsCOMPtr<nsINavBookmarksService> mBookmarksService;
-  nsCOMPtr<nsINavHistoryService> mHistoryService;
-  nsCOMPtr<nsIAnnotationService> mAnnotationService;
-  nsCOMPtr<nsILivemarkService> 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.
-  PRBool mAllowRootChanges;
-
-  // if set, this is an import of initial bookmarks.html content,
-  // so we don't want to kick off HTTP traffic
-  PRBool 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.
-  PRBool 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();
-
-  // 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];
-  }
-  nsresult NewFrame();
-  nsresult PopFrame();
-
-  nsresult SetFaviconForURI(nsIURI* aPageURI, nsIURI* aFaviconURI,
-                            const nsCString& aData);
-  nsresult SetFaviconForFolder(PRInt64 aFolder, const nsACString& aFavicon);
-
-  PRInt64 ConvertImportedIdToInternalId(const nsCString& aId);
-
-#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::Init
-//
-//    Note that the bookmark service pointer is passed in. We can not create
-//    the bookmark service from here because this can be called from bookmark
-//    service creation, making a weird reentrant loop.
-
-nsresult
-BookmarkContentSink::Init(PRBool aAllowRootChanges,
-                          nsINavBookmarksService* bookmarkService,
-                          PRInt64 aFolder,
-                          PRBool aIsImportDefaults)
-{
-  nsresult rv;
-  mBookmarksService = bookmarkService;
-  mHistoryService = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  mAnnotationService = do_GetService(NS_ANNOTATIONSERVICE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  mLivemarkService = do_GetService(NS_LIVEMARKSERVICE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mAllowRootChanges = aAllowRootChanges;
-  mIsImportDefaults = aIsImportDefaults;
-
-  // initialize the root frame with the menu root
-  PRInt64 menuRoot;
-  if (aFolder == 0) {
-    rv = mBookmarksService->GetBookmarksRoot(&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)
-
-// nsIContentSink **************************************************************
-
-NS_IMETHODIMP
-BookmarkContentSink::OpenContainer(const nsIParserNode& aNode)
-{
-  // 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.
-  BookmarkImportFrame& frame = CurFrame();
-  if (frame.mInDescription) {
-    frame.mPreviousText.Trim(kWhitespace); // important!
-    if (! frame.mPreviousText.IsEmpty()) {
-      // FIXME bug 334758: This description should be stored as an annotation
-      // on the URL or folder. We should probably not overwrite existing
-      // annotations.
-      frame.mPreviousText.Truncate(0);
-    }
-    frame.mInDescription = PR_FALSE;
-  }
-
-  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;
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-BookmarkContentSink::CloseContainer(const nsHTMLTag aTag)
-{
-  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();
-    break;
-  }
-
-  return NS_OK;
-}
-
-// BookmarkContentSink::HandleContainerBegin
-
-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)
-    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 mPlacesRoot;
-      mBookmarksService->GetPlacesRoot(&mPlacesRoot);
-      CurFrame().mContainerID = mPlacesRoot;
-      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;
-  frame.mLastContainerId = 0;
-
-  // 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;
-  if (!mFolderSpecified) {
-    for (PRInt32 i = 0; i < attrCount; i ++) {
-      if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_TOOLBARFOLDER_LOWER)) {
-        frame.mLastContainerType = BookmarkImportFrame::Container_Toolbar;
-        break;
-      } else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_BOOKMARKSMENU_LOWER)) {
-        frame.mLastContainerType = BookmarkImportFrame::Container_Menu;
-        break;
-      } else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_PLACESROOT_LOWER)) {
-        frame.mLastContainerType = BookmarkImportFrame::Container_Places;
-        break;
-      } else if (node.GetKeyAt(i).LowerCaseEqualsLiteral(KEY_ID_LOWER)) {
-        frame.mLastContainerId =
-          ConvertImportedIdToInternalId(NS_ConvertUTF16toUTF8(node.GetValueAt(i)));
-      }
-    }
-  }
-  CurFrame().mPreviousText.Truncate(0);
-}
-
-
-// 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)
-{
-  nsresult rv;
-
-  BookmarkImportFrame& frame = CurFrame();
-
-  // We need to make sure that the feed URIs from previous frames are emptied. 
-  frame.mPreviousFeed = nsnull;
-
-  // We need to make sure that the bookmark id from previous frames are emptied. 
-  frame.mPreviousId = 0;
-
-  // mPreviousText will hold our link text, clear it so that can be appended to
-  frame.mPreviousText.Truncate();
-
-  // get the attributes we care about
-  nsAutoString href;
-  nsAutoString feedUrl;
-  nsAutoString icon;
-  nsAutoString iconUri;
-  nsAutoString lastCharset;
-  nsAutoString keyword;
-  nsAutoString webPanel;
-  nsAutoString id;
-  PRInt32 attrCount = node.GetAttributeCount();
-  for (PRInt32 i = 0; i < attrCount; i ++) {
-    const nsAString& key = node.GetKeyAt(i);
-    if (key.LowerCaseEqualsLiteral(KEY_HREF_LOWER)) {
-      href = node.GetValueAt(i);
-    } else if (key.LowerCaseEqualsLiteral(KEY_FEEDURL_LOWER)) {
-      feedUrl = node.GetValueAt(i);
-    } else if (key.LowerCaseEqualsLiteral(KEY_ICON_LOWER)) {
-      icon = node.GetValueAt(i);
-    } else if (key.LowerCaseEqualsLiteral(KEY_ICON_URI_LOWER)) {
-      iconUri = node.GetValueAt(i);
-    } else if (key.LowerCaseEqualsLiteral(KEY_LASTCHARSET_LOWER)) {
-      lastCharset = node.GetValueAt(i);
-    } else if (key.LowerCaseEqualsLiteral(KEY_SHORTCUTURL_LOWER)) {
-      keyword = node.GetValueAt(i);
-    } else if (key.LowerCaseEqualsLiteral(KEY_WEB_PANEL_LOWER)) {
-      webPanel = node.GetValueAt(i);
-    } else if (key.LowerCaseEqualsLiteral(KEY_ID_LOWER)) {
-      id = node.GetValueAt(i);
-    }
-  }
-  href.Trim(kWhitespace);
-  feedUrl.Trim(kWhitespace);
-  icon.Trim(kWhitespace);
-  iconUri.Trim(kWhitespace);
-  lastCharset.Trim(kWhitespace);
-  keyword.Trim(kWhitespace);
-  webPanel.Trim(kWhitespace);
-  id.Trim(kWhitespace);
-
-  // For feeds, get the feed URL. If it is invalid, it will leave mPreviousFeed
-  // NULL and we'll continue trying to 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: we don't know what to do with them.
-  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 this so the link text and descriptions can be associated with it.
-    // Note that we ignore errors if this is a feed: URLs aren't strictly
-    // necessary in these cases.
-    nsresult rv = NS_NewURI(getter_AddRefs(frame.mPreviousLink),
-                   href, nsnull);
-    if (NS_FAILED(rv) && ! frame.mPreviousFeed) {
-      frame.mPreviousLink = nsnull;
-      return; // invalid link
-    }
-  }
-
-  // if there's a pre-existing Places bookmark id, use it
-  frame.mPreviousId = ConvertImportedIdToInternalId(NS_ConvertUTF16toUTF8(id));
-
-  // if there is a feedURL, this is a livemark, which is a special case
-  // that we handle in HandleLinkEnd(): don't create normal bookmarks
-  if (frame.mPreviousFeed)
-    return;
-
-  // if no previous id (or a legacy id), create a new bookmark
-  if (frame.mPreviousId == 0) {
-    // create the bookmark
-    rv = mBookmarksService->InsertItem(frame.mContainerID, frame.mPreviousLink,
-                                       mBookmarksService->DEFAULT_INDEX, &frame.mPreviousId);
-    NS_ASSERTION(NS_SUCCEEDED(rv), "InsertItem failed");
-  }
-
-  // save the favicon, ignore errors
-  if (! icon.IsEmpty() || ! iconUri.IsEmpty()) {
-    nsCOMPtr<nsIURI> iconUriObject;
-    NS_NewURI(getter_AddRefs(iconUriObject), iconUri);
-    if (! icon.IsEmpty() || iconUriObject)
-      SetFaviconForURI(frame.mPreviousLink, iconUriObject,
-                       NS_ConvertUTF16toUTF8(icon));
-  }
-
-  // save the keyword, ignore errors
-  if (! keyword.IsEmpty())
-    mBookmarksService->SetKeywordForBookmark(frame.mPreviousId, keyword);
-
-  if (webPanel.LowerCaseEqualsLiteral("true")) {
-    // set load-in-sidebar annotation for the bookmark
-
-    nsCOMPtr<nsIURI> placeURI;
-    rv = mBookmarksService->GetItemURI(frame.mPreviousId,
-                                       getter_AddRefs(placeURI));
-    NS_ASSERTION(NS_SUCCEEDED(rv),
-                 "failed to get a place: uri for a new bookmark");
-    if (NS_SUCCEEDED(rv)) {
-      mAnnotationService->SetAnnotationInt32(placeURI, LOAD_IN_SIDEBAR_ANNO,
-                                             1, 0,
-                                             nsIAnnotationService::EXPIRE_NEVER);
-    }
-  }
-  // FIXME bug 334408: save the last charset
-}
-
-
-// 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 bookmark is actually a livemark.  Create it here.
-    // (It gets created here instead of in HandleLinkBegin()
-    // because we need to know the title before creating it.)
-    PRInt64 folderId;
-
-    if (frame.mPreviousId > 0) {
-      // It's a pre-existing livemark, so update its properties
-      rv = mLivemarkService->SetSiteURI(frame.mPreviousId, frame.mPreviousLink);
-      NS_ASSERTION(NS_SUCCEEDED(rv), "SetSiteURI failed!");
-      rv = mLivemarkService->SetFeedURI(frame.mPreviousId, frame.mPreviousFeed);
-      NS_ASSERTION(NS_SUCCEEDED(rv), "SetFeedURI failed!");
-      rv = mBookmarksService->SetFolderTitle(frame.mPreviousId, frame.mPreviousText);
-      NS_ASSERTION(NS_SUCCEEDED(rv), "SetFolderTitle failed!");
-    } else {
-      if (mIsImportDefaults) {
-        rv = mLivemarkService->CreateLivemarkFolderOnly(mBookmarksService,
-                                                   frame.mContainerID,
-                                                   frame.mPreviousText,
-                                                   frame.mPreviousLink,
-                                                   frame.mPreviousFeed,
-                                                   -1,
-                                                   &folderId);
-        NS_ASSERTION(NS_SUCCEEDED(rv), "CreateLivemarkFolderOnly failed!");
-      } else {
-        rv = mLivemarkService->CreateLivemark(frame.mContainerID,
-                                         frame.mPreviousText,
-                                         frame.mPreviousLink,
-                                         frame.mPreviousFeed,
-                                         -1,
-                                         &folderId);
-        NS_ASSERTION(NS_SUCCEEDED(rv), "CreateLivemark failed!");
-      }
-    }
-#ifdef DEBUG_IMPORT
-    PrintNesting();
-    printf("Creating livemark '%s'\n",
-           NS_ConvertUTF16toUTF8(frame.mPreviousText).get());
-#endif
-  }
-  else if (frame.mPreviousLink) {
-#ifdef DEBUG_IMPORT
-    PrintNesting();
-    printf("Creating bookmark '%s'\n",
-           NS_ConvertUTF16toUTF8(frame.mPreviousText).get());
-#endif
-    mBookmarksService->SetItemTitle(frame.mPreviousId, frame.mPreviousText);
-  }
-  frame.mPreviousText.Truncate(0);
-}
-
-
-// BookmarkContentSink::HandleSeparator
-//
-//    Inserts a separator into the current container
-void
-BookmarkContentSink::HandleSeparator()
-{
-  BookmarkImportFrame& frame = CurFrame();
-
-  // bookmarks.html contains a separator between the toolbar menu and the
-  // rest of the items.  Since we pull the toolbar menu out into the top level,
-  // we want to skip over this separator since it looks out of place.
-  if (frame.mLastContainerType != BookmarkImportFrame::Container_Toolbar) {
-    // create the separator
-#ifdef DEBUG_IMPORT
-    PrintNesting();
-    printf("--------\n");
-#endif
-    mBookmarksService->InsertSeparator(frame.mContainerID,
-                                       mBookmarksService->DEFAULT_INDEX);
-  }
-}
-
-
-// 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;
-  CurFrame().ConsumeHeading(&containerName, &containerType, &ourID);
-
-  PRBool updateFolder = PR_FALSE;
-  if (ourID == 0) {
-    switch (containerType) {
-      case BookmarkImportFrame::Container_Normal:
-        // regular folder: use an existing folder if that name already exists
-        rv = mBookmarksService->GetChildFolder(CurFrame().mContainerID,
-                                               containerName, &ourID);
-        NS_ENSURE_SUCCESS(rv, rv);
-        if (ourID == 0) {
-          // need to append a new folder
-          rv = mBookmarksService->CreateFolder(CurFrame().mContainerID,
-                                              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 root
-        rv = mBookmarksService->GetBookmarksRoot(&ourID);
-        NS_ENSURE_SUCCESS(rv, rv);
-        if (mAllowRootChanges) {
-          updateFolder = PR_TRUE;
-          rv = SetFaviconForFolder(ourID, NS_LITERAL_CSTRING(BOOKMARKS_MENU_ICON_URI));
-          NS_ENSURE_SUCCESS(rv, rv);
-        }
-        break;
-      case BookmarkImportFrame::Container_Toolbar:
-        // get toolbar folder
-        PRInt64 bookmarkToolbarFolder;
-        rv = mBookmarksService->GetToolbarFolder(&bookmarkToolbarFolder);
-        NS_ENSURE_SUCCESS(rv, rv);
-        if (!bookmarkToolbarFolder) {
-          // create new folder
-          rv = mBookmarksService->CreateFolder(CurFrame().mContainerID,
-                                              containerName,
-                                              mBookmarksService->DEFAULT_INDEX, &ourID);
-          NS_ENSURE_SUCCESS(rv, rv);
-          // there's no toolbar folder, so make us the toolbar folder
-          rv = mBookmarksService->SetToolbarFolder(ourID);
-          NS_ENSURE_SUCCESS(rv, rv);
-          // set favicon
-          rv = SetFaviconForFolder(ourID, NS_LITERAL_CSTRING(BOOKMARKS_TOOLBAR_ICON_URI));
-          NS_ENSURE_SUCCESS(rv, rv);
-        }
-        else {
-          ourID = bookmarkToolbarFolder;
-        }
-        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
-    mBookmarksService->MoveFolder(ourID, CurFrame().mContainerID, -1);
-    mBookmarksService->SetFolderTitle(ourID, containerName);
-#ifdef DEBUG_IMPORT
-    printf(" [reparenting]");
-#endif
-  }
-
-#ifdef DEBUG_IMPORT
-  printf("\n");
-#endif
-
-  if (! mFrames.AppendElement(BookmarkImportFrame(ourID)))
-    return NS_ERROR_OUT_OF_MEMORY;
-  return NS_OK;
-}
-
-
-// BookmarkContentSink::PopFrame
-//
-
-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 nsCString& aData)
-{
-  nsresult rv;
-  static PRUint32 serialNumber = 0; // for made-up favicon URIs
-
-  nsFaviconService* faviconService = nsFaviconService::GetFaviconService();
-  if (! faviconService)
-    return NS_ERROR_NO_INTERFACE;
-
-  // 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("-");
-    faviconSpec.AppendInt(PR_Now());
-    rv = NS_NewURI(getter_AddRefs(faviconURI), faviconSpec);
-    NS_ENSURE_SUCCESS(rv, rv);
-    serialNumber ++;
-  }
-
-  nsCOMPtr<nsIURI> dataURI;
-  rv = NS_NewURI(getter_AddRefs(dataURI), aData);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // use the data: protocol handler to convert the data
-  nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsIProtocolHandler> protocolHandler;
-  rv = ioService->GetProtocolHandler("data", getter_AddRefs(protocolHandler));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIChannel> channel;
-  rv = protocolHandler->NewChannel(dataURI, getter_AddRefs(channel));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // blocking stream is OK for data URIs
-  nsCOMPtr<nsIInputStream> stream;
-  rv = channel->Open(getter_AddRefs(stream));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRUint32 available;
-  rv = stream->Available(&available);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (available == 0)
-    return NS_ERROR_FAILURE;
-
-  // read all the decoded data
-  PRUint8* buffer = NS_STATIC_CAST(PRUint8*,
-                                   nsMemory::Alloc(sizeof(PRUint8) * available));
-  if (! buffer)
-    return NS_ERROR_OUT_OF_MEMORY;
-  PRUint32 numRead;
-  rv = stream->Read(NS_REINTERPRET_CAST(char*, buffer), available, &numRead);
-  if (NS_FAILED(rv) || numRead != available) {
-    nsMemory::Free(buffer);
-    return rv;
-  }
-
-  nsCAutoString mimeType;
-  rv = channel->GetContentType(mimeType);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // save in service
-  rv = faviconService->SetFaviconData(faviconURI, buffer, available, mimeType, 0);
-  nsMemory::Free(buffer);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return faviconService->SetFaviconUrlForPage(aPageURI, faviconURI);
-}
-
-
-// BookmarkContentSink::SetFaviconForFolder
-//
-//    This sets the given favicon URI for the given folder. It is used to
-//    initialize the favicons for the bookmarks menu and toolbar. We don't
-//    actually set any data here because we assume the URI is a chrome: URI.
-//    These do not have to contain any data for them to work.
-
-nsresult
-BookmarkContentSink::SetFaviconForFolder(PRInt64 aFolder,
-                                         const nsACString& aFavicon)
-{
-  nsFaviconService* faviconService = nsFaviconService::GetFaviconService();
-  NS_ENSURE_TRUE(faviconService, NS_ERROR_OUT_OF_MEMORY);
-
-  nsCOMPtr<nsIURI> folderURI;
-  nsresult rv = mBookmarksService->GetFolderURI(aFolder,
-                                                getter_AddRefs(folderURI));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIURI> faviconURI;
-  rv = NS_NewURI(getter_AddRefs(faviconURI), aFavicon);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return faviconService->SetFaviconUrlForPage(folderURI, faviconURI);
-}
-
-// Converts a string id (legacy rdf or contemporary) into an int id
-PRInt64
-BookmarkContentSink::ConvertImportedIdToInternalId(const nsCString& aId) {
-  PRInt64 intId = 0;
-  if (aId.IsEmpty() || nsCRT::strncasecmp("rdf:", aId.get(), 4) == 0)
-    return intId;
-  PRInt32 rv;
-  intId = aId.ToInteger(&rv);
-  if (NS_FAILED(rv))
-    intId = 0;
-  return intId;
-}
-
-
-// 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)
-{
-  nsresult channelStatus;
-  channel->GetStatus(&channelStatus);
-  if (NS_FAILED(channelStatus))
-    return channelStatus;
-
-  if (NS_SUCCEEDED(status))
-    return NS_OK; // caller and the channel are happy
-
-  // channel was OK, but caller wasn't: set the channel state
-  channel->Cancel(status);
-  return status;
-}
-
-
-// nsNavBookmarks::ImportBookmarksHTML
-
-NS_IMETHODIMP
-nsNavBookmarks::ImportBookmarksHTML(nsIURI* aURL)
-{
-  // this version is exposed on the interface and disallows changing of roots
-  return ImportBookmarksHTMLInternal(aURL, PR_FALSE, 0, PR_FALSE);
-}
-
-NS_IMETHODIMP
-nsNavBookmarks::ImportBookmarksHTMLToFolder(nsIURI* aURL, PRInt64 aFolder)
-{
-  // this version is exposed on the interface and disallows changing of roots
-  return ImportBookmarksHTMLInternal(aURL, PR_FALSE, aFolder, PR_FALSE);
-}
-
-nsresult
-nsNavBookmarks::ImportBookmarksHTMLInternal(nsIURI* aURL,
-                                            PRBool aAllowRootChanges,
-                                            PRInt64 aFolder,
-                                            PRBool aIsImportDefaults)
-{
-  // wrap the import in a transaction to make it faster
-  mozStorageTransaction transaction(DBConn(), PR_FALSE);
-
-#ifdef DEBUG_IMPORT
-  nsCAutoString spec;
-  aURL->GetSpec(spec);
-  printf("\nImporting %s\n", spec.get());
-#endif
-
-  nsresult rv;
-  nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<BookmarkContentSink> sink = new BookmarkContentSink;
-  NS_ENSURE_TRUE(sink, NS_ERROR_OUT_OF_MEMORY);
-  rv = sink->Init(aAllowRootChanges, this, aFolder, aIsImportDefaults);
-  NS_ENSURE_SUCCESS(rv, rv);
-  parser->SetContentSink(sink);
-
-  // channel: note we have to set the content type or the default "unknown" type
-  // will confuse the parser
-  nsCOMPtr<nsIIOService> ioservice = do_GetIOService(&rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsIChannel> channel;
-  rv = ioservice->NewChannelFromURI(aURL, getter_AddRefs(channel));
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = channel->SetContentType(NS_LITERAL_CSTRING("text/html"));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // streams
-  nsCOMPtr<nsIInputStream> stream;
-  rv = channel->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);
-
-  // init parser
-#ifdef MOZILLA_1_8_BRANCH
-  rv = parser->Parse(aURL, nsnull, PR_FALSE);
-#else
-  rv = parser->Parse(aURL, nsnull);
-#endif
-  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(parser, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = listener->OnStartRequest(channel, nsnull);
-  rv = SyncChannelStatus(channel, rv);
-  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)) {
-      channel->Cancel(rv);
-      break;
-    }
-    if (! available)
-      break; // blocking input stream has none available when done
-
-    rv = listener->OnDataAvailable(channel, nsnull, bufferedstream, 0, available);
-    rv = SyncChannelStatus(channel, rv);
-    if (NS_FAILED(rv))
-      break;
-  }
-  listener->OnStopRequest(channel, nsnull, rv);
-  transaction.Commit();
-  return NS_OK;
-}
-
-static char kFileIntro[] =
-    "<!DOCTYPE NETSCAPE-Bookmark-file-1>" NS_LINEBREAK
-    // Note: we write bookmarks in UTF-8
-    "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">" NS_LINEBREAK
-    "<TITLE>Bookmarks</TITLE>" NS_LINEBREAK;
-static const char kRootIntro[] = "<H1";
-static const char kCloseRootH1[] = "</H1>" NS_LINEBREAK NS_LINEBREAK;
-
-static const char kBookmarkIntro[] = "<DL><p>" NS_LINEBREAK;
-static const char kBookmarkClose[] = "</DL><p>" NS_LINEBREAK;
-static const char kContainerIntro[] = "<DT><H3";
-static const char kContainerClose[] = "</H3>" NS_LINEBREAK;
-static const char kItemOpen[] = "<DT><A";
-static const char kItemClose[] = "</A>" NS_LINEBREAK;
-static const char kSeparator[] = "<HR>" NS_LINEBREAK;
-static const char kQuoteStr[] = "\"";
-static const char kCloseAngle[] = ">";
-static const char kIndent[] = "    ";
-
-static const char kPlacesRootAttribute[] = " PLACES_ROOT=\"true\"";
-static const char kBookmarksRootAttribute[] = " BOOKMARKS_MENU=\"true\"";
-static const char kToolbarFolderAttribute[] = " PERSONAL_TOOLBAR_FOLDER=\"true\"";
-static const char kIconAttribute[] = " ICON=\"";
-static const char kIconURIAttribute[] = " ICON_URI=\"";
-static const char kHrefAttribute[] = " HREF=\"";
-static const char kFeedURIAttribute[] = " FEEDURL=\"";
-static const char kWebPanelAttribute[] = " WEB_PANEL=\"true\"";
-static const char kKeywordAttribute[] = " SHORTCUTURL=\"";
-static const char kIdAttribute[] = " ID=\"";
-
-// WriteContainerPrologue
-//
-//    <DL><p>
-//
-//    Goes after the container header (<H3...) but before the contents
-
-static nsresult
-WriteContainerPrologue(const nsCString& aIndent, nsIOutputStream* aOutput)
-{
-  PRUint32 dummy;
-  nsresult rv = aOutput->Write(aIndent.get(), aIndent.Length(), &dummy);
-  if (NS_FAILED(rv)) return rv;
-  rv = aOutput->Write(kBookmarkIntro, sizeof(kBookmarkIntro)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-  return NS_OK;
-}
-
-
-// WriteContainerEpilogue
-//
-//    </DL><p>
-//
-//    Goes after the container contents to close the container
-
-static nsresult
-WriteContainerEpilogue(const nsCString& aIndent, nsIOutputStream* aOutput)
-{
-  PRUint32 dummy;
-  nsresult rv = aOutput->Write(aIndent.get(), aIndent.Length(), &dummy);
-  if (NS_FAILED(rv)) return rv;
-  rv = aOutput->Write(kBookmarkClose, sizeof(kBookmarkClose)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-  return NS_OK;
-}
-
-
-// DataToDataURI
-
-static nsresult
-DataToDataURI(PRUint8* aData, PRUint32 aDataLen, const nsACString& aMimeType,
-              nsACString& aDataURI)
-{
-  char* encoded = PL_Base64Encode(NS_REINTERPRET_CAST(const char*, aData),
-                                  aDataLen, nsnull);
-  if (! encoded)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  aDataURI.AssignLiteral("data:");
-  aDataURI.Append(aMimeType);
-  aDataURI.AppendLiteral(";base64,");
-  aDataURI.Append(encoded);
-
-  nsMemory::Free(encoded);
-  return NS_OK;
-}
-
-
-// WriteFaviconAttribute
-//
-//    This writes the 'ICON="data:asdlfkjas;ldkfja;skdljfasdf"' attribute for
-//    an item. We special-case chrome favicon URIs by just writing the chrome:
-//    URI.
-
-static nsresult
-WriteFaviconAttribute(const nsACString& aURI, nsIOutputStream* aOutput)
-{
-  nsresult rv;
-  PRUint32 dummy;
-
-  nsFaviconService* faviconService = nsFaviconService::GetFaviconService();
-  NS_ENSURE_TRUE(faviconService, NS_ERROR_UNEXPECTED);
-
-  nsCOMPtr<nsIURI> uri;
-  rv = NS_NewURI(getter_AddRefs(uri), aURI);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // get favicon
-  nsCOMPtr<nsIURI> faviconURI;
-  rv = faviconService->GetFaviconForPage(uri, getter_AddRefs(faviconURI));
-  if (rv == NS_ERROR_NOT_AVAILABLE)
-    return NS_OK; // no favicon
-  NS_ENSURE_SUCCESS(rv, rv); // anything else is error
-
-  nsCAutoString faviconScheme;
-  nsCAutoString faviconSpec;
-  rv = faviconURI->GetSpec(faviconSpec);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = faviconURI->GetScheme(faviconScheme);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // write favicon URI: 'ICON_URI="..."'
-  rv = aOutput->Write(kIconURIAttribute, sizeof(kIconURIAttribute)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-  rv = WriteEscapedUrl(faviconSpec, aOutput);
-  if (NS_FAILED(rv)) return rv;
-  rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-
-  if (! faviconScheme.EqualsLiteral("chrome")) {
-    // only store data for non-chrome URIs
-
-    // get the data - BE SURE TO FREE
-    nsCAutoString mimeType;
-    PRUint32 dataLen;
-    PRUint8* data;
-    rv = faviconService->GetFaviconData(faviconURI, mimeType, &dataLen, &data);
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (dataLen > 0) {
-      // convert to URI
-      nsCString faviconContents;
-      rv = DataToDataURI(data, dataLen, mimeType, faviconContents);
-      nsMemory::Free(data);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      rv = aOutput->Write(kIconAttribute, sizeof(kIconAttribute)-1, &dummy);
-      if (NS_FAILED(rv)) return rv;
-      rv = aOutput->Write(faviconContents.get(), faviconContents.Length(), &dummy);
-      if (NS_FAILED(rv)) return rv;
-      rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
-      if (NS_FAILED(rv)) return rv;
-    }
-  }
-  return NS_OK;
-}
-
-
-// nsNavBookmarks::WriteContainer
-//
-//    Writes out all the necessary parts of a bookmarks folder.
-
-nsresult
-nsNavBookmarks::WriteContainer(PRInt64 aFolder, const nsCString& aIndent,
-                               nsIOutputStream* aOutput)
-{
-  nsresult rv = WriteContainerHeader(aFolder, aIndent, aOutput);
-  if (NS_FAILED(rv)) return rv;
-
-  // FIXME bug 334758: write container description here as a <DD>
-
-  rv = WriteContainerPrologue(aIndent, aOutput);
-  if (NS_FAILED(rv)) return rv;
-  rv = WriteContainerContents(aFolder, aIndent, aOutput);
-  if (NS_FAILED(rv)) return rv;
-  rv = WriteContainerEpilogue(aIndent, aOutput);
-  if (NS_FAILED(rv)) return rv;
-  return NS_OK;
-}
-
-
-// nsNavBookmarks::WriteContainerHeader
-//
-//    This writes '<DL><H3>Title</H3>'
-//    Remember folders can also have favicons, which we put in the H3 tag
-
-nsresult
-nsNavBookmarks::WriteContainerHeader(PRInt64 aFolder, const nsCString& aIndent,
-                                     nsIOutputStream* aOutput)
-{
-  PRUint32 dummy;
-  nsresult rv;
-
-  // indent
-  if (! aIndent.IsEmpty()) {
-    rv = aOutput->Write(aIndent.get(), aIndent.Length(), &dummy);
-    if (NS_FAILED(rv)) return rv;
-  }
-
-  // "<DL H3"
-  rv = aOutput->Write(kContainerIntro, sizeof(kContainerIntro)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-
-  // " PERSONAL_TOOLBAR_FOLDER="true"", etc.
-  if (aFolder == mRoot) {
-    rv = aOutput->Write(kPlacesRootAttribute, sizeof(kPlacesRootAttribute)-1, &dummy);
-    if (NS_FAILED(rv)) return rv;
-  } else if (aFolder == mBookmarksRoot) {
-    rv = aOutput->Write(kBookmarksRootAttribute, sizeof(kBookmarksRootAttribute)-1, &dummy);
-    if (NS_FAILED(rv)) return rv;
-  } else if (aFolder == mToolbarFolder) {
-    rv = aOutput->Write(kToolbarFolderAttribute, sizeof(kToolbarFolderAttribute)-1, &dummy);
-    if (NS_FAILED(rv)) return rv;
-  }
-
-  // id
-  rv = aOutput->Write(kIdAttribute, sizeof(kIdAttribute)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-  nsCAutoString id;
-  id.AppendInt(aFolder);
-  rv = aOutput->Write(id.get(), id.Length(), &dummy);
-  if (NS_FAILED(rv)) return rv;
-  rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-
-  // favicon (most folders won't have one)
-  nsCOMPtr<nsIURI> folderURI;
-  rv = GetFolderURI(aFolder, getter_AddRefs(folderURI));
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsCAutoString folderSpec;
-  rv = folderURI->GetSpec(folderSpec);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = WriteFaviconAttribute(folderSpec, aOutput);
-  if (NS_FAILED(rv)) return rv;
-
-  // ">"
-  rv = aOutput->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-
-  // title
-  rv = WriteContainerTitle(aFolder, aOutput);
-  if (NS_FAILED(rv)) return rv;
-
-  // "</H3>\n"
-  rv = aOutput->Write(kContainerClose, sizeof(kContainerClose)-1, &dummy);
-  return rv;
-}
-
-
-// nsNavBookmarks::WriteContainerTitle
-//
-//    Retrieves, escapes and writes the container title to the stream.
-
-nsresult
-nsNavBookmarks::WriteContainerTitle(PRInt64 aFolder, nsIOutputStream* aOutput)
-{
-  nsAutoString title;
-  nsresult rv = GetFolderTitle(aFolder, title);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  char* escapedTitle = nsEscapeHTML(NS_ConvertUTF16toUTF8(title).get());
-  if (escapedTitle) {
-    PRUint32 dummy;
-    rv = aOutput->Write(escapedTitle, strlen(escapedTitle), &dummy);
-    nsMemory::Free(escapedTitle);
-    if (NS_FAILED(rv)) return rv;
-  }
-  return NS_OK;
-}
-
-
-// nsNavBookmarks::WriteItem
-//
-//    "<DT><A HREF="..." ICON="...">Name</A>"
-
-nsresult
-nsNavBookmarks::WriteItem(nsNavHistoryResultNode* aItem,
-                          const nsCString& aIndent,
-                          nsIOutputStream* aOutput)
-{
-  PRUint32 dummy;
-  nsresult rv;
-
-  // indent
-  if (! aIndent.IsEmpty()) {
-    rv = aOutput->Write(aIndent.get(), aIndent.Length(), &dummy);
-    if (NS_FAILED(rv)) return rv;
-  }
-
-  // '<DT><A'
-  rv = aOutput->Write(kItemOpen, sizeof(kItemOpen)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-
-  // ' HREF="http://..."' - note that we need to call GetURI on the result
-  // node because some nodes (eg queries) generate this lazily.
-  rv = aOutput->Write(kHrefAttribute, sizeof(kHrefAttribute)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-  nsCAutoString uri;
-  rv = aItem->GetUri(uri);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = WriteEscapedUrl(uri, aOutput);
-  if (NS_FAILED(rv)) return rv;
-  rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-
-  // ' ICON="..."'
-  rv = WriteFaviconAttribute(uri, aOutput);
-  if (NS_FAILED(rv)) return rv;
-
-  // get bookmark id 
-  PRInt64 bookmarkId;
-  rv = aItem->GetBookmarkId(&bookmarkId);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // write id
-  rv = aOutput->Write(kIdAttribute, sizeof(kIdAttribute)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-  nsCAutoString id;
-  id.AppendInt(bookmarkId);
-  rv = aOutput->Write(id.get(), id.Length(), &dummy);
-  if (NS_FAILED(rv)) return rv;
-  rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-
-  // keyword (shortcuturl)
-  nsAutoString keyword;
-  rv = GetKeywordForBookmark(bookmarkId, keyword);
-  if (NS_FAILED(rv)) return rv;
-  if (!keyword.IsEmpty()) {
-    rv = aOutput->Write(kKeywordAttribute, sizeof(kKeywordAttribute)-1, &dummy);
-    if (NS_FAILED(rv)) return rv;
-    char* escapedKeyword = nsEscapeHTML(NS_ConvertUTF16toUTF8(keyword).get());
-    rv = aOutput->Write(escapedKeyword, strlen(escapedKeyword), &dummy);
-    nsMemory::Free(escapedKeyword);
-    if (NS_FAILED(rv)) return rv;
-    rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
-    if (NS_FAILED(rv)) return rv;
-  }
-
-  // get bookmark place URI for annotations
-  nsCOMPtr<nsIURI> placeURI;
-  rv = GetItemURI(bookmarkId, getter_AddRefs(placeURI));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
-  NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
-
-  // Write WEB_PANEL="true" if the load-in-sidebar annotation is set for the
-  // item
-  PRBool loadInSidebar = PR_FALSE;
-  rv = annosvc->HasAnnotation(placeURI, LOAD_IN_SIDEBAR_ANNO, &loadInSidebar);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (loadInSidebar)
-    aOutput->Write(kWebPanelAttribute, sizeof(kWebPanelAttribute)-1, &dummy);
-
-  // FIXME bug 334408: write last character set here
-
-  // '>'
-  rv = aOutput->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-
-  // title
-  char* escapedTitle = nsEscapeHTML(aItem->mTitle.get());
-  if (escapedTitle) {
-    rv = aOutput->Write(escapedTitle, strlen(escapedTitle), &dummy);
-    nsMemory::Free(escapedTitle);
-    if (NS_FAILED(rv)) return rv;
-  }
-
-  // '</A>\n'
-  rv = aOutput->Write(kItemClose, sizeof(kItemClose)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-
-  // FIXME bug 334758: write item description here as a <DD>
-
-  return NS_OK;
-}
-
-
-// WriteLivemark
-//
-//    Similar to WriteItem, this has an additional FEEDURL attribute and
-//    the HREF is optional and points to the source page.
-
-nsresult
-nsNavBookmarks::WriteLivemark(PRInt64 aFolderId, const nsCString& aIndent,
-                              nsIOutputStream* aOutput)
-{
-  PRUint32 dummy;
-  nsresult rv;
-
-  nsCOMPtr<nsILivemarkService> livemarkService(do_GetService(NS_LIVEMARKSERVICE_CONTRACTID));
-  NS_ENSURE_TRUE(livemarkService, NS_ERROR_UNEXPECTED);
-
-  // indent
-  if (! aIndent.IsEmpty()) {
-    rv = aOutput->Write(aIndent.get(), aIndent.Length(), &dummy);
-    if (NS_FAILED(rv)) return rv;
-  }
-
-  // '<DT><A'
-  rv = aOutput->Write(kItemOpen, sizeof(kItemOpen)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-
-  // get feed URI
-  nsCOMPtr<nsIURI> feedURI;
-  rv = livemarkService->GetFeedURI(aFolderId, getter_AddRefs(feedURI));
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsCString feedSpec;
-  rv = feedURI->GetSpec(feedSpec);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // write feed URI
-  rv = aOutput->Write(kFeedURIAttribute, sizeof(kFeedURIAttribute)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-  rv = WriteEscapedUrl(feedSpec, aOutput);
-  if (NS_FAILED(rv)) return rv;
-  rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-
-  // get the optional site URI
-  nsCOMPtr<nsIURI> siteURI;
-  rv = livemarkService->GetSiteURI(aFolderId, getter_AddRefs(siteURI));
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (siteURI) {
-    nsCString siteSpec;
-    rv = siteURI->GetSpec(siteSpec);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // write site URI
-    rv = aOutput->Write(kHrefAttribute, sizeof(kHrefAttribute)-1, &dummy);
-    if (NS_FAILED(rv)) return rv;
-    rv = WriteEscapedUrl(siteSpec, aOutput);
-    if (NS_FAILED(rv)) return rv;
-    rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
-    if (NS_FAILED(rv)) return rv;
-  }
-
-  // write id
-  rv = aOutput->Write(kIdAttribute, sizeof(kIdAttribute)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-  nsCAutoString id;
-  id.AppendInt(aFolderId);
-  rv = aOutput->Write(id.get(), id.Length(), &dummy);
-  if (NS_FAILED(rv)) return rv;
-  rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-
-  // '>'
-  rv = aOutput->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-
-  // title
-  rv = WriteContainerTitle(aFolderId, aOutput);
-  if (NS_FAILED(rv)) return rv;
-
-  // '</A>\n'
-  rv = aOutput->Write(kItemClose, sizeof(kItemClose)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-
-  return NS_OK;
-}
-
-
-// WriteSeparator
-//
-//    "<HR>"
-
-nsresult
-WriteSeparator(const nsCString& aIndent, nsIOutputStream* aOutput)
-{
-  PRUint32 dummy;
-  nsresult rv;
-
-  // indent
-  if (! aIndent.IsEmpty()) {
-    rv = aOutput->Write(aIndent.get(), aIndent.Length(), &dummy);
-    if (NS_FAILED(rv)) return rv;
-  }
-
-  rv = aOutput->Write(kSeparator, sizeof(kSeparator)-1, &dummy);
-  return rv;
-}
-
-
-// WriteEscapedUrl
-//
-//    Writes the given string to the stream escaped as necessary for URLs.
-//
-//    Unfortunately, the old bookmarks system uses a custom hardcoded and
-//    braindead escaping scheme that we need to emulate. It just replaces
-//    quotes with %22 and that's it.
-
-nsresult
-WriteEscapedUrl(const nsCString& aString, nsIOutputStream* aOutput)
-{
-  nsCAutoString escaped(aString);
-  PRInt32 offset;
-  while ((offset = escaped.FindChar('\"')) >= 0) {
-    escaped.Cut(offset, 1);
-    escaped.Insert(NS_LITERAL_CSTRING("%22"), offset);
-  }
-  PRUint32 dummy;
-  return aOutput->Write(escaped.get(), escaped.Length(), &dummy);
-}
-
-
-// nsNavBookmarks::WriteContainerContents
-//
-//    The indent here is the indent of the parent. We will add an additional
-//    indent before writing data.
-
-nsresult
-nsNavBookmarks::WriteContainerContents(PRInt64 aFolder, const nsCString& aIndent,
-                                       nsIOutputStream* aOutput)
-{
-  nsCString myIndent = aIndent;
-  myIndent.Append(kIndent);
-
-  nsNavHistory* historyService = nsNavHistory::GetHistoryService();
-  NS_ENSURE_TRUE(historyService, NS_ERROR_UNEXPECTED);
-
-  // get empty options
-  nsCOMPtr<nsINavHistoryQueryOptions> optionsInterface;
-  nsresult rv = historyService->GetNewQueryOptions(getter_AddRefs(optionsInterface));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // QueryFolderChildren requires a concrete options
-  nsCOMPtr<nsNavHistoryQueryOptions> options = do_QueryInterface(optionsInterface);
-  NS_ENSURE_TRUE(options, NS_ERROR_UNEXPECTED);
-
-  // get contents of folder
-  nsCOMArray<nsNavHistoryResultNode> items;
-  rv = QueryFolderChildren(aFolder, options, &items);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // write each item
-  for (PRInt32 i = 0; i < items.Count(); i ++) {
-    if (items[i]->IsFolder()) {
-      // bookmarks folder
-      PRInt64 folderId = items[i]->GetAsFolder()->mFolderId;
-      if (aFolder == mRoot && (folderId == mToolbarFolder ||
-                               folderId == mBookmarksRoot)) {
-        // don't write out the bookmarks menu folder from the
-        // places root. When writing to bookmarks.html, it is reparented
-        // to the menu, which is the root of the namespace. This provides
-        // better backwards compatability.
-        continue;
-      }
-
-      // it could be a regular folder or it could be a livemark
-      nsCOMPtr<nsILivemarkService> livemarkService(do_GetService(NS_LIVEMARKSERVICE_CONTRACTID));
-      NS_ENSURE_TRUE(livemarkService, NS_ERROR_UNEXPECTED);
-      PRBool isLivemark;
-      rv = livemarkService->IsLivemark(folderId, &isLivemark);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      if (isLivemark)
-        rv = WriteLivemark(folderId, myIndent, aOutput);
-      else
-        rv = WriteContainer(folderId, myIndent, aOutput);
-    } else if (items[i]->IsSeparator()) {
-      rv = WriteSeparator(myIndent, aOutput);
-    } else {
-      rv = WriteItem(items[i], myIndent, aOutput);
-    }
-    if (NS_FAILED(rv)) return rv;
-  }
-  return NS_OK;
-}
-
-
-// nsNavBookmarks::ExportBookmarksHTML
-//
-
-NS_IMETHODIMP
-nsNavBookmarks::ExportBookmarksHTML(nsIFile* aBookmarksFile)
-{
-  if (! aBookmarksFile)
-    return NS_ERROR_NULL_POINTER;
-
-  // get a safe output stream, so we don't clobber the bookmarks file unless
-  // all the writes succeeded.
-  nsCOMPtr<nsIOutputStream> out;
-  nsresult rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out),
-                                                aBookmarksFile,
-                                                PR_WRONLY | PR_CREATE_FILE,
-                                                /*octal*/ 0600,
-                                                0);
-  if (NS_FAILED(rv)) return rv;
-
-  // We need a buffered output stream for performance.
-  // See bug 202477.
-  nsCOMPtr<nsIOutputStream> strm;
-  rv = NS_NewBufferedOutputStream(getter_AddRefs(strm), out, 4096);
-  if (NS_FAILED(rv)) return rv;
-
-  // file header
-  PRUint32 dummy;
-  rv = strm->Write(kFileIntro, sizeof(kFileIntro)-1, &dummy);
-  if (NS_FAILED(rv)) return rv;
-
-  // '<H1'
-  rv = strm->Write(kRootIntro, sizeof(kRootIntro)-1, &dummy); // <H1
-  if (NS_FAILED(rv)) return rv;
-
-  // bookmarks menu favicon
-  nsCOMPtr<nsIURI> folderURI;
-  rv = GetFolderURI(mBookmarksRoot, getter_AddRefs(folderURI));
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsCAutoString folderSpec;
-  rv = folderURI->GetSpec(folderSpec);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = WriteFaviconAttribute(folderSpec, strm);
-  if (NS_FAILED(rv)) return rv;
-
-  // '>Bookmarks</H1>
-  rv = strm->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy); // >
-  if (NS_FAILED(rv)) return rv;
-  rv = WriteContainerTitle(mBookmarksRoot, strm);
-  if (NS_FAILED(rv)) return rv;
-  rv = strm->Write(kCloseRootH1, sizeof(kCloseRootH1)-1, &dummy); // </H1>
-  if (NS_FAILED(rv)) return rv;
-
-  // prologue
-  rv = WriteContainerPrologue(EmptyCString(), strm);
-  if (NS_FAILED(rv)) return rv;
-
-  // indents
-  nsCAutoString indent;
-  indent.Assign(kIndent);
-
-  // places root
-  rv = WriteContainer(mRoot, indent, strm);
-  if (NS_FAILED(rv)) return rv;
-
-  // bookmarks menu contents
-  rv = WriteContainerContents(mBookmarksRoot, EmptyCString(), strm);
-  if (NS_FAILED(rv)) return rv;
-
-  // epilogue
-  rv = WriteContainerEpilogue(EmptyCString(), strm);
-  if (NS_FAILED(rv)) return rv;
-
-  // commit the write
-  nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(strm, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return safeStream->Finish();
-}
--- a/toolkit/components/places/src/nsNavBookmarks.cpp
+++ b/toolkit/components/places/src/nsNavBookmarks.cpp
@@ -421,38 +421,16 @@ nsNavBookmarks::InitRoots()
   NS_ENSURE_SUCCESS(rv, rv);
 
 #ifdef MOZ_PLACES_BOOKMARKS
   if (importDefaults) {
     // when there is no places root, we should define the hierarchy by
     // importing the default one.
     rv = InitDefaults();
     NS_ENSURE_SUCCESS(rv, rv);
-
-    // migrate the user's old bookmarks
-    // FIXME: move somewhere else to some profile migrator thingy
-    nsCOMPtr<nsIFile> bookmarksFile;
-    rv = NS_GetSpecialDirectory(NS_APP_BOOKMARKS_50_FILE,
-                                getter_AddRefs(bookmarksFile));
-    if (bookmarksFile) {
-      PRBool bookmarksFileExists;
-      rv = bookmarksFile->Exists(&bookmarksFileExists);
-      if (NS_SUCCEEDED(rv) && bookmarksFileExists) {
-        nsCOMPtr<nsIIOService> ioservice = do_GetService(
-                                    "@mozilla.org/network/io-service;1", &rv);
-        NS_ENSURE_SUCCESS(rv, rv);
-        nsCOMPtr<nsIURI> bookmarksFileURI;
-        rv = ioservice->NewFileURI(bookmarksFile,
-                                   getter_AddRefs(bookmarksFileURI));
-        NS_ENSURE_SUCCESS(rv, rv);
-        rv = ImportBookmarksHTMLInternal(bookmarksFileURI, PR_FALSE,
-                                         0, PR_TRUE);
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-    }
   }
 #endif
   return NS_OK;
 }
 
 // nsNavBookmarks::InitDefaults
 //
 // Initializes default bookmarks and containers.
@@ -648,19 +626,19 @@ nsNavBookmarks::AddBookmarkToHash(PRInt6
     return NS_ERROR_OUT_OF_MEMORY;
   return RecursiveAddBookmarkHash(aBookmarkId, aBookmarkId, aMinTime);
 }
 
 
 // nsNavBookmkars::RecursiveAddBookmarkHash
 //
 //    Used to add a new level of redirect information to the bookmark hash.
-//    Given a source bookmark 'aBookmark' and 'aCurrentSouce' that has already
+//    Given a source bookmark 'aBookmark' and 'aCurrentSource' that has already
 //    been added to the hashtable, this will add all redirect destinations of
-//    'aCurrentSort'. Will call itself recursively to walk down the chain.
+//    'aCurrentSource'. Will call itself recursively to walk down the chain.
 //
 //    'aMinTime' is the minimum time to consider visits from. Visits previous
 //    to this will not be considered. This allows the search to be much more
 //    efficient if you know something happened recently. Use 0 for the min time
 //    to search all history for redirects.
 
 nsresult
 nsNavBookmarks::RecursiveAddBookmarkHash(PRInt64 aBookmarkID,
@@ -2351,174 +2329,19 @@ nsNavBookmarks::RemoveObserver(nsINavBoo
 }
 
 /**
  * Called by the History service when shutting down
  */
 nsresult
 nsNavBookmarks::OnQuit()
 {
-#ifdef MOZ_PLACES_BOOKMARKS
-  // get bookmarks file
-  nsCOMPtr<nsIFile> bookmarksFile;
-  nsresult rv = NS_GetSpecialDirectory(NS_APP_BOOKMARKS_50_FILE,
-                                       getter_AddRefs(bookmarksFile));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // create if it doesn't exist
-  PRBool exists;
-  rv = bookmarksFile->Exists(&exists);
-  if (NS_FAILED(rv) || !exists) {
-    rv = bookmarksFile->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
-    NS_ASSERTION(rv, "Unable to create bookmarks.html!");
-    return rv;
-  }
-
-  // export bookmarks.html
-  rv = ExportBookmarksHTML(bookmarksFile);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // archive if needed
-  nsCOMPtr<nsIPrefService> prefServ(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsIPrefBranch> bookmarksPrefs;
-  rv = prefServ->GetBranch("browser.bookmarks.", getter_AddRefs(bookmarksPrefs));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRInt32 numberOfBackups;
-  rv = bookmarksPrefs->GetIntPref("max_backups", &numberOfBackups);
-  if (NS_FAILED(rv))
-    numberOfBackups = 5;
-
-  if (numberOfBackups > 0) {
-    rv = ArchiveBookmarksFile(numberOfBackups, PR_FALSE);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-#endif
-
   return NS_OK;
 }
 
-/**
- *  ArchiveBookmarksFile()
- *
- *  Creates a dated backup once a day in <profile>/bookmarkbackups
- *
- *  PRInt32 numberOfBackups - the maximum number of backups to keep
- *
- *  PRBool forceArchive - forces creating an archive even if one was 
- *                        already created that day (overwrites)
- */
-nsresult
-nsNavBookmarks::ArchiveBookmarksFile(PRInt32 numberOfBackups,
-                                         PRBool forceArchive)
-{
-  nsCOMPtr<nsIFile> bookmarksBackupDir;
-  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
-                                       getter_AddRefs(bookmarksBackupDir));
-  NS_ENSURE_SUCCESS(rv, rv);
-  
-  nsDependentCString dirName("bookmarkbackups");
-  rv = bookmarksBackupDir->AppendNative(dirName);
-  NS_ENSURE_SUCCESS(rv, rv);
-  
-  PRBool exists;
-  rv = bookmarksBackupDir->Exists(&exists);
-  if (NS_FAILED(rv) || !exists) {
-    rv = bookmarksBackupDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
-    
-    // if there's no backup folder, there's no backup, fail
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  // construct the new leafname
-  PRTime          now64 = PR_Now();
-  PRExplodedTime  nowInfo;
-  PR_ExplodeTime(now64, PR_LocalTimeParameters, &nowInfo);
-  PR_NormalizeTime(&nowInfo, PR_LocalTimeParameters);
-
-  char timeString[128];
-  
-  PR_FormatTime(timeString, 128, "bookmarks-%Y-%m-%d.html", &nowInfo);
-
-  nsCAutoString backupFilenameCString(timeString);
-  nsAutoString backupFilenameString = NS_ConvertUTF8toUTF16(backupFilenameCString);
-
-  nsCOMPtr<nsIFile> backupFile;
-  if (forceArchive) {
-    // if we have a backup from today, nuke it
-    nsCOMPtr<nsIFile> currentBackup;
-    rv = bookmarksBackupDir->Clone(getter_AddRefs(currentBackup));
-    NS_ENSURE_SUCCESS(rv, rv);
-    rv = currentBackup->Append(backupFilenameString);
-    NS_ENSURE_SUCCESS(rv, rv);
-    rv = currentBackup->Exists(&exists);
-    if (!NS_FAILED(rv) && exists) {
-      rv = currentBackup->Remove(PR_FALSE);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-  } else {
-    nsCOMPtr<nsISimpleEnumerator> existingBackups;
-    rv = bookmarksBackupDir->GetDirectoryEntries(getter_AddRefs(existingBackups));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsStringArray backupFileNames;
-
-    PRBool hasMoreElements = PR_FALSE;
-    PRBool hasCurrentBackup = PR_FALSE;
-    
-    while (NS_SUCCEEDED(existingBackups->HasMoreElements(&hasMoreElements)) &&
-           hasMoreElements)
-    {
-      rv = existingBackups->GetNext(getter_AddRefs(backupFile));
-      NS_ENSURE_SUCCESS(rv, rv);
-      nsAutoString backupName;
-      rv = backupFile->GetLeafName(backupName);
-      NS_ENSURE_SUCCESS(rv, rv);
-      
-      // the backup for today exists, do not create later
-      if (backupName == backupFilenameString) {
-        hasCurrentBackup = PR_TRUE;
-        continue;
-      }
-
-      // mark the rest for possible removal
-      if (Substring(backupName, 0, 10) == NS_LITERAL_STRING("bookmarks-"))
-        backupFileNames.AppendString(backupName);
-    }
-
-    if (numberOfBackups > 0 && backupFileNames.Count() >= numberOfBackups) {
-      PRInt32 numberOfBackupsToDelete = backupFileNames.Count() - numberOfBackups + 1;
-      backupFileNames.Sort();
-
-      while (numberOfBackupsToDelete--) {
-        (void)bookmarksBackupDir->Clone(getter_AddRefs(backupFile));
-        (void)backupFile->Append(*backupFileNames[0]);
-        (void)backupFile->Remove(PR_FALSE);
-        backupFileNames.RemoveStringAt(0);
-      }
-    }
-
-    if (hasCurrentBackup)
-      return NS_OK;
-  }
-
-  nsCOMPtr<nsIFile> bookmarksFile;
-  rv = NS_GetSpecialDirectory(NS_APP_BOOKMARKS_50_FILE,
-                              getter_AddRefs(bookmarksFile));
-  NS_ENSURE_SUCCESS(rv, rv);
-  
-  rv = bookmarksFile->CopyTo(bookmarksBackupDir, backupFilenameString);
-  // at least dump something out in case this fails in a debug build
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return rv;
-}
-
-
 // nsNavBookmarks::nsINavHistoryObserver
 
 NS_IMETHODIMP
 nsNavBookmarks::OnBeginUpdateBatch()
 {
   // These aren't passed through to bookmark observers currently.
   return NS_OK;
 }
--- a/toolkit/components/places/src/nsNavBookmarks.h
+++ b/toolkit/components/places/src/nsNavBookmarks.h
@@ -105,17 +105,16 @@ public:
   mozIStorageStatement* DBGetFolderInfo() { return mDBGetFolderInfo; }
   // constants for the above statement
   static const PRInt32 kGetFolderInfoIndex_FolderID;
   static const PRInt32 kGetFolderInfoIndex_Title;
   static const PRInt32 kGetFolderInfoIndex_Type;
 
   // Called by History service when quitting.
   nsresult OnQuit();
-  nsresult ArchiveBookmarksFile(PRInt32 aNumberOfBackups, PRBool aForceArchive);
 
 private:
   static nsNavBookmarks *sInstance;
 
   ~nsNavBookmarks();
 
   nsresult InitRoots();
   nsresult InitDefaults();
@@ -251,33 +250,16 @@ private:
 
   private:
     PRInt64 mID;
     PRInt64 mParent;
     nsString mTitle;
     nsCString mType;
     PRInt32 mIndex;
   };
-
-  // in nsBookmarksHTML
-  nsresult ImportBookmarksHTMLInternal(nsIURI* aURL,
-                                       PRBool aAllowRootChanges,
-                                       PRInt64 aFolder,
-                                       PRBool aIsImportDefaults);
-  nsresult WriteItem(nsNavHistoryResultNode* aItem, const nsCString& aIndent,
-                     nsIOutputStream* aOutput);
-  nsresult WriteContainer(PRInt64 aFolder, const nsCString& aIndent,
-                          nsIOutputStream* aOutput);
-  nsresult WriteContainerHeader(PRInt64 aFolder, const nsCString& aIndent,
-                                nsIOutputStream* aOutput);
-  nsresult WriteContainerTitle(PRInt64 aFolder, nsIOutputStream* aOutput);
-  nsresult WriteLivemark(PRInt64 aFolderId, const nsCString& aIndent,
-                         nsIOutputStream* aOutput);
-  nsresult WriteContainerContents(PRInt64 aFolder, const nsCString& aIndent,
-                                  nsIOutputStream* aOutput);
 };
 
 struct nsBookmarksUpdateBatcher
 {
   nsBookmarksUpdateBatcher() { nsNavBookmarks::GetBookmarksService()->BeginUpdateBatch(); }
   ~nsBookmarksUpdateBatcher() { nsNavBookmarks::GetBookmarksService()->EndUpdateBatch(); }
 };
 
--- a/toolkit/components/places/tests/bookmarks/test_bookmarks.js
+++ b/toolkit/components/places/tests/bookmarks/test_bookmarks.js
@@ -120,17 +120,17 @@ var observer = {
   },
 };
 bmsvc.addObserver(observer, false);
 
 // get bookmarks root index
 var root = bmsvc.bookmarksRoot;
 
 // index at which items should begin
-var bmStartIndex = 4;
+var bmStartIndex = 1;
 
 // main
 function run_test() {
   // test roots
   do_check_true(bmsvc.placesRoot > 0);
   do_check_true(bmsvc.bookmarksRoot > 0);
   do_check_true(bmsvc.toolbarFolder > 0);