Bug 1502954 - Remove livemarks code from toolkit. r=mak
authorMark Banner <standard8@mozilla.com>
Tue, 20 Nov 2018 13:29:54 +0000
changeset 503637 b2654a2729e932aef6946cdaf406689b9fcf9065
parent 503636 53ffafb6d58cfa360a0a5cfe0eaa4d3c02d2a174
child 503638 4a765b8ac85d718e5965938bcdff18b17405839c
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs1502954
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1502954 - Remove livemarks code from toolkit. r=mak Differential Revision: https://phabricator.services.mozilla.com/D12118
browser/components/places/content/bookmarkProperties.js
browser/components/places/content/controller.js
browser/installer/package-manifest.in
build/pgo/server-locations.txt
toolkit/components/build/nsToolkitCompsCID.h
toolkit/components/places/Bookmarks.jsm
toolkit/components/places/PlacesTransactions.jsm
toolkit/components/places/PlacesUtils.jsm
toolkit/components/places/moz.build
toolkit/components/places/mozIAsyncLivemarks.idl
toolkit/components/places/nsLivemarkService.js
toolkit/components/places/nsNavBookmarks.cpp
toolkit/components/places/nsNavBookmarks.h
toolkit/components/places/tests/unit/test_multi_word_tags.js
toolkit/components/places/tests/unit/test_null_interfaces.js
toolkit/components/places/toolkitplaces.manifest
--- a/browser/components/places/content/bookmarkProperties.js
+++ b/browser/components/places/content/bookmarkProperties.js
@@ -91,18 +91,16 @@ var BookmarkPropertiesPanel = {
   _action: null,
   _itemType: null,
   _uri: null,
   _title: "",
   _URIs: [],
   _keyword: "",
   _postData: null,
   _charSet: "",
-  _feedURI: null,
-  _siteURI: null,
 
   _defaultInsertionPoint: null,
   _hiddenRows: [],
 
   /**
    * This method returns the correct label for the dialog's "accept"
    * button based on the variant of the dialog.
    */
--- a/browser/components/places/content/controller.js
+++ b/browser/components/places/content/controller.js
@@ -922,25 +922,25 @@ PlacesController.prototype = {
   setDataTransfer: function PC_setDataTransfer(aEvent) {
     let dt = aEvent.dataTransfer;
 
     let result = this._view.result;
     let didSuppressNotifications = result.suppressNotifications;
     if (!didSuppressNotifications)
       result.suppressNotifications = true;
 
-    function addData(type, index, feedURI) {
-      let wrapNode = PlacesUtils.wrapNode(node, type, feedURI);
+    function addData(type, index) {
+      let wrapNode = PlacesUtils.wrapNode(node, type);
       dt.mozSetDataAt(type, wrapNode, index);
     }
 
-    function addURIData(index, feedURI) {
-      addData(PlacesUtils.TYPE_X_MOZ_URL, index, feedURI);
-      addData(PlacesUtils.TYPE_UNICODE, index, feedURI);
-      addData(PlacesUtils.TYPE_HTML, index, feedURI);
+    function addURIData(index) {
+      addData(PlacesUtils.TYPE_X_MOZ_URL, index);
+      addData(PlacesUtils.TYPE_UNICODE, index);
+      addData(PlacesUtils.TYPE_HTML, index);
     }
 
     try {
       let nodes = this._view.draggableSelection;
       for (let i = 0; i < nodes.length; ++i) {
         var node = nodes[i];
 
         // This order is _important_! It controls how this and other
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -236,17 +236,16 @@
 @RESPATH@/components/simpleServices.js
 @RESPATH@/components/pluginGlue.manifest
 @RESPATH@/components/ProcessSingleton.manifest
 @RESPATH@/components/MainProcessSingleton.js
 @RESPATH@/components/ContentProcessSingleton.js
 @RESPATH@/components/nsURLFormatter.manifest
 @RESPATH@/components/nsURLFormatter.js
 @RESPATH@/components/toolkitplaces.manifest
-@RESPATH@/components/nsLivemarkService.js
 @RESPATH@/components/nsTaggingService.js
 @RESPATH@/components/UnifiedComplete.js
 @RESPATH@/components/nsPlacesExpiration.js
 @RESPATH@/components/PageIconProtocolHandler.js
 @RESPATH@/components/PlacesCategoriesStarter.js
 @RESPATH@/components/ColorAnalyzer.js
 @RESPATH@/components/PageThumbsStorageService.js
 @RESPATH@/components/mozProtocolHandler.js
--- a/build/pgo/server-locations.txt
+++ b/build/pgo/server-locations.txt
@@ -113,20 +113,16 @@ https://untrusted.example.com:443      p
 https://expired.example.com:443        privileged,cert=expired
 https://requestclientcert.example.com:443         privileged,clientauth=request
 https://requireclientcert.example.com:443         privileged,clientauth=require
 https://mismatch.expired.example.com:443	privileged,cert=expired
 https://mismatch.untrusted.example.com:443	privileged,cert=untrusted
 https://untrusted-expired.example.com:443	privileged,cert=untrustedandexpired
 https://mismatch.untrusted-expired.example.com:443	privileged,cert=untrustedandexpired
 
-# This is here so that we don't load the default live bookmark over
-# the network in every test suite.
-http://fxfeeds.mozilla.com:80
-
 # Prevent safebrowsing tests from hitting the network for its-a-trap.html and
 # its-an-attack.html.
 http://www.itisatrap.org:80
 https://www.itisatrap.org:443
 
 #
 # These are subdomains of <ält.example.org>.
 #
--- a/toolkit/components/build/nsToolkitCompsCID.h
+++ b/toolkit/components/build/nsToolkitCompsCID.h
@@ -58,19 +58,16 @@
   "@mozilla.org/browser/nav-history-service;1"
 
 #define NS_ANNOTATIONSERVICE_CONTRACTID \
   "@mozilla.org/browser/annotation-service;1"
 
 #define NS_NAVBOOKMARKSSERVICE_CONTRACTID \
   "@mozilla.org/browser/nav-bookmarks-service;1"
 
-#define NS_LIVEMARKSERVICE_CONTRACTID \
-  "@mozilla.org/browser/livemark-service;2"
-
 #define NS_TAGGINGSERVICE_CONTRACTID \
 "@mozilla.org/browser/tagging-service;1"
 
 #define NS_FAVICONSERVICE_CONTRACTID \
   "@mozilla.org/browser/favicon-service;1"
 
 #define NS_APPSTARTUP_CONTRACTID \
   "@mozilla.org/toolkit/app-startup;1"
--- a/toolkit/components/places/Bookmarks.jsm
+++ b/toolkit/components/places/Bookmarks.jsm
@@ -87,23 +87,16 @@ async function promiseTagsFolderId() {
   );
   return gTagsFolderId = rows[0].getResultByName("id");
 }
 
 const MATCH_ANYWHERE_UNMODIFIED = Ci.mozIPlacesAutoComplete.MATCH_ANYWHERE_UNMODIFIED;
 const BEHAVIOR_BOOKMARK = Ci.mozIPlacesAutoComplete.BEHAVIOR_BOOKMARK;
 const SQLITE_MAX_VARIABLE_NUMBER = 999;
 
-// Annotations which insertTree currently accepts. These should be going away
-// soon, see bug 1460577.
-const ACCEPTED_ANNOTATIONS = [
-  PlacesUtils.LMANNO_FEEDURI,
-  PlacesUtils.LMANNO_SITEURI,
-];
-
 var Bookmarks = Object.freeze({
   /**
    * Item's type constants.
    * These should stay consistent with nsINavBookmarksService.idl
    */
   TYPE_BOOKMARK: 1,
   TYPE_FOLDER: 2,
   TYPE_SEPARATOR: 3,
@@ -438,18 +431,16 @@ var Bookmarks = Object.freeze({
           dateAdded: { defaultValue: time,
                        validIf: b => !b.lastModified ||
                                      b.dateAdded <= b.lastModified },
           lastModified: { defaultValue: time,
                           validIf: b => (!b.dateAdded && b.lastModified >= time) ||
                                         (b.dateAdded && b.lastModified >= b.dateAdded) },
           index: { replaceWith: indexToUse++ },
           source: { replaceWith: source },
-          annos: { validIf: b => false,
-                   fixup: b => b.annos = b.annos.filter(anno => ACCEPTED_ANNOTATIONS.includes(anno.name))},
           keyword: { validIf: b => b.type == TYPE_BOOKMARK },
           charset: { validIf: b => b.type == TYPE_BOOKMARK },
           postData: { validIf: b => b.type == TYPE_BOOKMARK },
           tags: { validIf: b => b.type == TYPE_BOOKMARK },
           children: { validIf: b => b.type == TYPE_FOLDER && Array.isArray(b.children) },
         };
         if (fixupOrSkipInvalidEntries) {
           insertInfo.guid.fixup = b => b.guid = PlacesUtils.history.makeGuid();
@@ -563,19 +554,16 @@ var Bookmarks = Object.freeze({
           title: item.title,
           dateAdded: item.dateAdded,
           guid: item.guid,
           parentGuid: item.parentGuid,
           source: item.source,
           isTagging: false,
         }));
 
-        // Note, annotations for livemark data are deleted from insertInfo
-        // within appendInsertionInfoForInfoArray, so we won't be duplicating
-        // the insertions here.
         try {
           await handleBookmarkItemSpecialData(itemId, item);
         } catch (ex) {
           // This is not critical, regardless the bookmark has been created
           // and we should continue notifying the next ones.
           Cu.reportError(`An error occured while handling special bookmark data: ${ex}`);
         }
 
--- a/toolkit/components/places/PlacesTransactions.jsm
+++ b/toolkit/components/places/PlacesTransactions.jsm
@@ -54,19 +54,16 @@ var EXPORTED_SYMBOLS = ["PlacesTransacti
  * but optional for the NewBookmark transaction).
  *
  * To make things simple, a given input property has the same basic meaning and
  * valid values across all transactions which accept it in the input object.
  * Here is a list of all supported input properties along with their expected
  * values:
  *  - url: a URL object, an nsIURI object, or a href.
  *  - urls: an array of urls, as above.
- *  - feedUrl: an url (as above), holding the url for a live bookmark.
- *  - siteUrl an url (as above), holding the url for the site with which
- *            a live bookmark is associated.
  *  - tag - a string.
  *  - tags: an array of strings.
  *  - guid, parentGuid, newParentGuid: a valid Places GUID string.
  *  - guids: an array of valid Places GUID strings.
  *  - title: a string
  *  - index, newIndex: the position of an item in its containing folder,
  *    starting from 0.
  *    integer and PlacesUtils.bookmarks.DEFAULT_INDEX
@@ -894,17 +891,17 @@ DefineTransaction.verifyInput = function
     fixedInput[prop] = this.validatePropertyValue(prop, input, false);
   }
 
   return fixedInput;
 };
 
 // Update the documentation at the top of this module if you add or
 // remove properties.
-DefineTransaction.defineInputProps(["url", "feedUrl", "siteUrl"],
+DefineTransaction.defineInputProps(["url"],
                                    DefineTransaction.urlValidate, null);
 DefineTransaction.defineInputProps(["guid", "parentGuid", "newParentGuid"],
                                    DefineTransaction.guidValidate);
 DefineTransaction.defineInputProps(["title", "postData"],
                                    DefineTransaction.strOrNullValidate, null);
 DefineTransaction.defineInputProps(["keyword", "oldKeyword", "oldTag", "tag",
                                     "excludingAnnotation"],
                                    DefineTransaction.strValidate, "");
@@ -1384,28 +1381,23 @@ PT.Remove.prototype = {
         removedItems.push(await PlacesUtils.promiseBookmarksTree(guid));
       } catch (ex) {
         throw new Error("Failed to get info for the specified item (guid: " +
                           guid + "): " + ex);
       }
     }
 
     let removeThem = async function() {
-      let bmsToRemove = [];
-      for (let info of removedItems) {
-        if (info.annos &&
-            info.annos.some(anno => anno.name == PlacesUtils.LMANNO_FEEDURI)) {
-          await PlacesUtils.livemarks.removeLivemark({ guid: info.guid });
-        } else {
-          bmsToRemove.push({guid: info.guid});
-        }
-      }
-
-      if (bmsToRemove.length) {
-        await PlacesUtils.bookmarks.remove(bmsToRemove);
+      if (removedItems.length) {
+        // We have to pass just the guids as although remove() accepts full
+        // info items, promiseBookmarksTree returns dateAdded and lastModified
+        // as PRTime rather than date types.
+        await PlacesUtils.bookmarks.remove(removedItems.map(info => {
+          return { guid: info.guid};
+        }));
       }
     };
     await removeThem();
 
     this.undo = async function() {
       for (let info of removedItems) {
         await createItemsFromBookmarksTree(info, true);
       }
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -254,18 +254,17 @@ const BOOKMARK_VALIDATORS = Object.freez
 });
 
 // Sync bookmark records can contain additional properties.
 const SYNC_BOOKMARK_VALIDATORS = Object.freeze({
   // Sync uses Places GUIDs for all records except roots.
   recordId: simpleValidateFunc(v => typeof v == "string" && (
                                 (PlacesSyncUtils.bookmarks.ROOTS.includes(v) || PlacesUtils.isValidGuid(v)))),
   parentRecordId: v => SYNC_BOOKMARK_VALIDATORS.recordId(v),
-  // Sync uses kinds instead of types, which distinguish between livemarks and
-  // queries.
+  // Sync uses kinds instead of types.
   kind: simpleValidateFunc(v => typeof v == "string" &&
                                 Object.values(PlacesSyncUtils.bookmarks.KINDS).includes(v)),
   query: simpleValidateFunc(v => v === null || (typeof v == "string" && v)),
   folder: simpleValidateFunc(v => typeof v == "string" && v &&
                                   v.length <= PlacesUtils.bookmarks.MAX_TAG_LENGTH),
   tags: v => {
     if (v === null) {
       return [];
@@ -395,16 +394,17 @@ var PlacesUtils = {
   TYPE_X_MOZ_URL: "text/x-moz-url",
   // Place entries formatted as HTML anchors
   TYPE_HTML: "text/html",
   // Place entries as raw URL text
   TYPE_UNICODE: "text/unicode",
   // Used to track the action that populated the clipboard.
   TYPE_X_MOZ_PLACE_ACTION: "text/x-moz-place-action",
 
+  // Deprecated: Remaining only for supporting migration of old livemarks.
   LMANNO_FEEDURI: "livemark/feedURI",
   LMANNO_SITEURI: "livemark/siteURI",
   CHARSET_ANNO: "URIProperties/characterSet",
   // Deprecated: This is only used for supporting import from older datasets.
   MOBILE_ROOT_ANNO: "mobile/bookmarksRoot",
 
   TOPIC_SHUTDOWN: "places-shutdown",
   TOPIC_INIT_COMPLETE: "places-init-complete",
@@ -1822,20 +1822,16 @@ XPCOMUtils.defineLazyGetter(PlacesUtils,
 XPCOMUtils.defineLazyServiceGetter(PlacesUtils, "annotations",
                                    "@mozilla.org/browser/annotation-service;1",
                                    "nsIAnnotationService");
 
 XPCOMUtils.defineLazyServiceGetter(PlacesUtils, "tagging",
                                    "@mozilla.org/browser/tagging-service;1",
                                    "nsITaggingService");
 
-XPCOMUtils.defineLazyServiceGetter(PlacesUtils, "livemarks",
-                                   "@mozilla.org/browser/livemark-service;2",
-                                   "mozIAsyncLivemarks");
-
 XPCOMUtils.defineLazyGetter(this, "bundle", function() {
   const PLACES_STRING_BUNDLE_URI = "chrome://places/locale/places.properties";
   return Services.strings.createBundle(PLACES_STRING_BUNDLE_URI);
 });
 
 // This is just used as a reasonably-random value for copy & paste / drag operations.
 XPCOMUtils.defineLazyGetter(PlacesUtils, "instanceId", () => {
   return PlacesUtils.history.makeGuid();
--- a/toolkit/components/places/moz.build
+++ b/toolkit/components/places/moz.build
@@ -11,17 +11,16 @@ XPIDL_SOURCES += [
     'nsINavHistoryService.idl',
 ]
 
 XPIDL_MODULE = 'places'
 
 if CONFIG['MOZ_PLACES']:
     XPIDL_SOURCES += [
         'mozIAsyncHistory.idl',
-        'mozIAsyncLivemarks.idl',
         'mozIColorAnalyzer.idl',
         'mozIPlacesAutoComplete.idl',
         'mozIPlacesPendingOperation.idl',
         'nsIAnnotationService.idl',
         'nsIFaviconService.idl',
         'nsINavBookmarksService.idl',
         'nsITaggingService.idl',
     ]
@@ -72,17 +71,16 @@ if CONFIG['MOZ_PLACES']:
         'PlacesSyncUtils.jsm',
         'PlacesTransactions.jsm',
         'PlacesUtils.jsm',
         'SyncedBookmarksMirror.jsm',
     ]
 
     EXTRA_COMPONENTS += [
         'ColorAnalyzer.js',
-        'nsLivemarkService.js',
         'nsPlacesExpiration.js',
         'nsTaggingService.js',
         'PageIconProtocolHandler.js',
         'PlacesCategoriesStarter.js',
         'toolkitplaces.manifest',
         'UnifiedComplete.js',
     ]
 
deleted file mode 100644
--- a/toolkit/components/places/mozIAsyncLivemarks.idl
+++ /dev/null
@@ -1,181 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-interface nsIURI;
-
-interface mozILivemarkInfo;
-interface mozILivemark;
-
-interface nsINavHistoryResultObserver;
-
-[scriptable, uuid(672387b7-a75d-4e8f-9b49-5c1dcbfff46b)]
-interface mozIAsyncLivemarks : nsISupports
-{
-  /**
-   * Removes an existing livemark.
-   *
-   * @param aLivemarkInfo
-   *        mozILivemarkInfo object containing either an id or a guid of the
-   *        livemark to remove.
-   *
-   * @return {Promise}
-   * @throws NS_ERROR_INVALID_ARG if the id/guid is invalid.
-   */
-  jsval removeLivemark(in jsval aLivemarkInfo);
-
-  /**
-   * Gets an existing livemark.
-   *
-   * @param aLivemarkInfo
-   *        mozILivemarkInfo object containing either an id or a guid of the
-   *        livemark to retrieve.
-   *
-   * @return {Promise}
-   * @throws NS_ERROR_INVALID_ARG if the id/guid is invalid or an invalid
-   *         callback is provided.
-   */
-  jsval getLivemark(in jsval aLivemarkInfo);
-
-  /**
-   * Reloads all livemarks if they are expired or if forced to do so.
-   *
-   * @param [optional]aForceUpdate
-   *        If set to true forces a reload even if contents are still valid.
-   *
-   * @note The update process is asynchronous, observers registered through
-   *       registerForUpdates will be notified of updated contents.
-   */
-  void reloadLivemarks([optional]in boolean aForceUpdate);
-
-  void handlePlacesEvents(in jsval aEvents);
-
-  jsval invalidateCachedLivemarks();
-};
-
-[scriptable, uuid(3a3c5e8f-ec4a-4086-ae0a-d16420d30c9f)]
-interface mozILivemarkInfo : nsISupports
-{
-  /**
-   * Id of the bookmarks folder representing this livemark.
-   *
-   * @deprecated Use guid instead.
-   */
-  readonly attribute long long id;
-
-  /**
-   * The globally unique identifier of this livemark.
-   */
-  readonly attribute ACString guid;
-
-  /**
-   * Title of this livemark.
-   */
-  readonly attribute AString title;
-
-  /**
-   * Id of the bookmarks parent folder containing this livemark.
-   *
-   * @deprecated Use parentGuid instead.
-   */
-  readonly attribute long long parentId;
-
-  /**
-   * Guid of the bookmarks parent folder containing this livemark.
-   */
-  readonly attribute long long parentGuid;
-
-  /**
-   * The position of this livemark in the bookmarks parent folder.
-   */
-  readonly attribute long index;
-
-  /**
-   * Time this livemark was created.
-   */
-  readonly attribute PRTime dateAdded;
-
-  /**
-   * Time this livemark's details were last modified.  Doesn't track changes to
-   * the livemark contents.
-   */
-  readonly attribute PRTime lastModified;
-
-  /**
-   * The URI of the syndication feed associated with this livemark.
-   */
-  readonly attribute nsIURI feedURI;
-
-  /**
-   * The URI of the website associated with this livemark.
-   */
-  readonly attribute nsIURI siteURI;
-};
-
-[scriptable, uuid(9f6fdfae-db9a-4bd8-bde1-148758cf1b18)]
-interface mozILivemark : mozILivemarkInfo
-{
-  // Indicates the livemark is inactive.
-  const unsigned short STATUS_READY = 0;
-  // Indicates the livemark is fetching new contents.
-  const unsigned short STATUS_LOADING = 1;
-  // Indicates the livemark failed to fetch new contents.
-  const unsigned short STATUS_FAILED = 2;
-
-  /**
-   * Status of this livemark.  One of the STATUS_* constants above.
-   */
-  readonly attribute unsigned short status;
-
-  /**
-   * Reload livemark contents if they are expired or if forced to do so.
-   *
-   * @param [optional]aForceUpdate
-   *        If set to true forces a reload even if contents are still valid.
-   *
-   * @note The update process is asynchronous, it's possible to register a
-   *       result observer to be notified of updated contents through
-   *       registerForUpdates.
-   */
-  void reload([optional]in boolean aForceUpdate);
-
-  /**
-   * Returns an array of nsINavHistoryResultNode objects, representing children
-   * of this livemark.  The nodes will have aContainerNode as parent.
-   *
-   * @param aContainerNode
-   *        Object implementing nsINavHistoryContainerResultNode, to be used as
-   *        parent of the livemark nodes.
-   */
-  jsval getNodesForContainer(in jsval aContainerNode);
-
-  /**
-   * Registers a container node for updates on this livemark.
-   * When the livemark contents change, an invalidateContainer(aContainerNode)
-   * request is sent to aResultObserver.
-   *
-   * @param aContainerNode
-   *        Object implementing nsINavHistoryContainerResultNode, representing
-   *        this livemark.
-   * @param aResultObserver
-   *        The nsINavHistoryResultObserver that should be notified of changes
-   *        to the livemark contents.
-   */
-  void registerForUpdates(in jsval aContainerNode,
-                          in nsINavHistoryResultObserver aResultObserver);
-
-  /**
-   * Unregisters a previously registered container node.
-   *
-   * @param aContainerNode
-   *        Object implementing nsINavHistoryContainerResultNode, representing
-   *        this livemark.
-   *
-   * @note it's suggested to always unregister containers that are no more used,
-   *       to free up the associated resources.  A good time to do so is when
-   *       the container gets closed.
-   */
-  void unregisterForUpdates(in jsval aContainerNode);
-};
deleted file mode 100644
--- a/toolkit/components/places/nsLivemarkService.js
+++ /dev/null
@@ -1,841 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// Modules and services.
-
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.defineModuleGetter(this, "PlacesUtils",
-                               "resource://gre/modules/PlacesUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "NetUtil",
-                               "resource://gre/modules/NetUtil.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "history", function() {
-  let livemarks = PlacesUtils.livemarks;
-  // Lazily add an history observer when it's actually needed.
-  PlacesUtils.history.addObserver(livemarks, true);
-  return PlacesUtils.history;
-});
-
-// Constants
-
-// Delay between reloads of consecute livemarks.
-const RELOAD_DELAY_MS = 500;
-// Expire livemarks after this time.
-const EXPIRE_TIME_MS = 3600000; // 1 hour.
-// Expire livemarks after this time on error.
-const ONERROR_EXPIRE_TIME_MS = 300000; // 5 minutes.
-
-// Livemarks cache.
-
-XPCOMUtils.defineLazyGetter(this, "CACHE_SQL", () => {
-  function getAnnoSQLFragment(aAnnoParam) {
-    return `SELECT a.content
-            FROM moz_items_annos a
-            JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id
-            WHERE a.item_id = b.id
-              AND n.name = ${aAnnoParam}`;
-  }
-
-  return `SELECT b.id, b.title, b.parent As parentId, b.position AS 'index',
-                 b.guid, b.dateAdded, b.lastModified, p.guid AS parentGuid,
-                 ( ${getAnnoSQLFragment(":feedURI_anno")} ) AS feedURI,
-                 ( ${getAnnoSQLFragment(":siteURI_anno")} ) AS siteURI
-          FROM moz_bookmarks b
-          JOIN moz_bookmarks p ON b.parent = p.id
-          JOIN moz_items_annos a ON a.item_id = b.id
-          JOIN moz_anno_attributes n ON a.anno_attribute_id = n.id
-          WHERE b.type = :folder_type
-            AND n.name = :feedURI_anno`;
-});
-
-/**
- * Convert a Date object to a PRTime (microseconds).
- *
- * @param date
- *        the Date object to convert.
- * @return microseconds from the epoch.
- */
-function toPRTime(date) {
-  return date * 1000;
-}
-
-/**
- * Convert a PRTime to a Date object.
- *
- * @param time
- *        microseconds from the epoch.
- * @return a Date object or undefined if time was not defined.
- */
-function toDate(time) {
-  return time ? new Date(parseInt(time / 1000)) : undefined;
-}
-
-// LivemarkService
-
-function LivemarkService() {
-  // Cleanup on shutdown.
-  Services.obs.addObserver(this, PlacesUtils.TOPIC_SHUTDOWN, true);
-
-  // Observe bookmarks but don't init the service just for that.
-  PlacesUtils.bookmarks.addObserver(this, true);
-
-  this._placesListener = new PlacesWeakCallbackWrapper(
-    this.handlePlacesEvents.bind(this));
-  PlacesObservers.addListener(["page-visited"], this._placesListener);
-
-  this._livemarksMap = null;
-  this._promiseLivemarksMapReady = Promise.resolve();
-}
-
-LivemarkService.prototype = {
-  _withLivemarksMap(func) {
-    let promise = this._promiseLivemarksMapReady.then(async () => {
-      if (!this._livemarksMap) {
-        this._livemarksMap = new Map();
-        let conn = await PlacesUtils.promiseDBConnection();
-        let rows = await conn.executeCached(CACHE_SQL,
-          { folder_type: Ci.nsINavBookmarksService.TYPE_FOLDER,
-            feedURI_anno: PlacesUtils.LMANNO_FEEDURI,
-            siteURI_anno: PlacesUtils.LMANNO_SITEURI });
-        for (let row of rows) {
-          let siteURI = row.getResultByName("siteURI");
-          let livemark = new Livemark({
-            id: row.getResultByName("id"),
-            guid: row.getResultByName("guid"),
-            title: row.getResultByName("title"),
-            parentId: row.getResultByName("parentId"),
-            parentGuid: row.getResultByName("parentGuid"),
-            index: row.getResultByName("index"),
-            dateAdded: row.getResultByName("dateAdded"),
-            lastModified: row.getResultByName("lastModified"),
-            feedURI: NetUtil.newURI(row.getResultByName("feedURI")),
-            siteURI: siteURI ? NetUtil.newURI(siteURI) : null,
-          });
-          this._livemarksMap.set(livemark.guid, livemark);
-        }
-      }
-      return func(this._livemarksMap);
-    });
-    this._promiseLivemarksMapReady = promise.catch(_ => {});
-    return promise;
-  },
-
-  _reloading: false,
-  _startReloadTimer(livemarksMap, forceUpdate, reloaded) {
-    if (this._reloadTimer) {
-      this._reloadTimer.cancel();
-    } else {
-      this._reloadTimer = Cc["@mozilla.org/timer;1"]
-                            .createInstance(Ci.nsITimer);
-    }
-
-    this._reloading = true;
-    this._reloadTimer.initWithCallback(() => {
-      // Find first livemark to be reloaded.
-      for (let [ guid, livemark ] of livemarksMap) {
-        if (!reloaded.has(guid)) {
-          reloaded.add(guid);
-          livemark.reload(forceUpdate);
-          this._startReloadTimer(livemarksMap, forceUpdate, reloaded);
-          return;
-        }
-      }
-      // All livemarks have been reloaded.
-      this._reloading = false;
-      this._forceUpdate = false;
-    }, RELOAD_DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT);
-  },
-
-  // nsIObserver
-
-  observe(aSubject, aTopic, aData) {
-    if (aTopic == PlacesUtils.TOPIC_SHUTDOWN) {
-      this._invalidateCachedLivemarks({
-        // No need to restart the reload timer on shutdown.
-        keepReloading: false,
-      }).catch(Cu.reportError);
-    }
-  },
-
-  // mozIAsyncLivemarks
-
-  removeLivemark(aLivemarkInfo) {
-    if (!aLivemarkInfo) {
-      throw new Components.Exception("Invalid arguments", Cr.NS_ERROR_INVALID_ARG);
-    }
-    // Accept either a guid or an id.
-    let hasGuid = "guid" in aLivemarkInfo;
-    let hasId = "id" in aLivemarkInfo;
-    if ((hasGuid && !/^[a-zA-Z0-9\-_]{12}$/.test(aLivemarkInfo.guid)) ||
-        (hasId && aLivemarkInfo.id < 1) ||
-        (!hasId && !hasGuid)) {
-      throw new Components.Exception("Invalid arguments", Cr.NS_ERROR_INVALID_ARG);
-    }
-
-    return this._withLivemarksMap(async livemarksMap => {
-      if (!aLivemarkInfo.guid)
-        aLivemarkInfo.guid = await PlacesUtils.promiseItemGuid(aLivemarkInfo.id);
-
-      if (!livemarksMap.has(aLivemarkInfo.guid))
-        throw new Components.Exception("Invalid livemark", Cr.NS_ERROR_INVALID_ARG);
-
-      await PlacesUtils.bookmarks.remove(aLivemarkInfo.guid,
-                                         { source: aLivemarkInfo.source });
-    });
-  },
-
-  reloadLivemarks(aForceUpdate) {
-    // Check if there's a currently running reload, to save some useless work.
-    let notWorthRestarting =
-      this._forceUpdate || // We're already forceUpdating.
-      !aForceUpdate; // The caller didn't request a forced update.
-    if (this._reloading && notWorthRestarting) {
-      // Ignore this call.
-      return;
-    }
-
-    this._withLivemarksMap(livemarksMap => {
-      this._forceUpdate = !!aForceUpdate;
-      // Livemarks reloads happen on a timer for performance reasons.
-      this._startReloadTimer(livemarksMap, this._forceUpdate, new Set());
-    });
-  },
-
-  getLivemark(aLivemarkInfo) {
-    if (!aLivemarkInfo) {
-      throw new Components.Exception("Invalid arguments", Cr.NS_ERROR_INVALID_ARG);
-    }
-    // Accept either a guid or an id.
-    let hasGuid = "guid" in aLivemarkInfo;
-    let hasId = "id" in aLivemarkInfo;
-    if ((hasGuid && !/^[a-zA-Z0-9\-_]{12}$/.test(aLivemarkInfo.guid)) ||
-        (hasId && aLivemarkInfo.id < 1) ||
-        (!hasId && !hasGuid)) {
-      throw new Components.Exception("Invalid arguments", Cr.NS_ERROR_INVALID_ARG);
-    }
-
-    return this._withLivemarksMap(async livemarksMap => {
-      if (!aLivemarkInfo.guid)
-        aLivemarkInfo.guid = await PlacesUtils.promiseItemGuid(aLivemarkInfo.id);
-
-      if (!livemarksMap.has(aLivemarkInfo.guid))
-        throw new Components.Exception("Invalid livemark", Cr.NS_ERROR_INVALID_ARG);
-
-      return livemarksMap.get(aLivemarkInfo.guid);
-    });
-  },
-
-  _invalidateCachedLivemarks({ keepReloading = true } = {}) {
-    // Cancel pending reloads, since any livemarks we're currently reloading
-    // might no longer be valid.
-    let wasReloading = this._reloading;
-    this._reloading = false;
-
-    let wasForceUpdating = this._forceUpdate;
-    this._forceUpdate = false;
-
-    if (this._reloadTimer) {
-      this._reloadTimer.cancel();
-    }
-
-    // Clear out the livemarks cache.
-    let promise = this._promiseLivemarksMapReady.then(() => {
-      let livemarksMap = this._livemarksMap;
-      this._livemarksMap = null;
-      if (livemarksMap) {
-        // Stop any ongoing network fetch.
-        for (let livemark of livemarksMap.values()) {
-          livemark.terminate();
-        }
-      }
-    });
-    this._promiseLivemarksMapReady = promise.catch(_ => {});
-
-    // Restart the timer if we were reloading before invalidating.
-    if (keepReloading) {
-      if (wasReloading) {
-        this.reloadLivemarks(wasForceUpdating);
-      }
-    } else {
-      delete this._reloadTimer;
-    }
-
-    return promise;
-  },
-
-  invalidateCachedLivemarks() {
-    return this._invalidateCachedLivemarks();
-  },
-
-  handlePlacesEvents(aEvents) {
-    if (!aEvents) {
-      throw new Components.Exception("Invalid arguments",
-                                     Cr.NS_ERROR_INVALID_ARG);
-    }
-
-    this._withLivemarksMap(livemarksMap => {
-      for (let event of aEvents) {
-        for (let livemark of livemarksMap.values()) {
-          livemark.updateURIVisitedStatus(event.url, true);
-        }
-      }
-    });
-  },
-
-  // nsINavBookmarkObserver
-
-  onBeginUpdateBatch() {},
-  onEndUpdateBatch() {},
-  onItemVisited() {},
-
-  onItemChanged(id, property, isAnno, value, lastModified, itemType, parentId,
-                guid, parentGuid) {
-    if (itemType != Ci.nsINavBookmarksService.TYPE_FOLDER)
-      return;
-
-    this._withLivemarksMap(livemarksMap => {
-      if (livemarksMap.has(guid)) {
-        let livemark = livemarksMap.get(guid);
-        if (property == "title") {
-          livemark.title = value;
-        }
-        livemark.lastModified = lastModified;
-      }
-    });
-  },
-
-  onItemMoved(id, parentId, oldIndex, newParentId, newIndex, itemType, guid,
-              oldParentGuid, newParentGuid) {
-    if (itemType != Ci.nsINavBookmarksService.TYPE_FOLDER)
-      return;
-
-    this._withLivemarksMap(livemarksMap => {
-      if (livemarksMap.has(guid)) {
-        let livemark = livemarksMap.get(guid);
-        livemark.parentId = newParentId;
-        livemark.parentGuid = newParentGuid;
-        livemark.index = newIndex;
-      }
-    });
-  },
-
-  onItemRemoved(id, parentId, index, itemType, uri, guid, parentGuid) {
-    if (itemType != Ci.nsINavBookmarksService.TYPE_FOLDER)
-      return;
-
-    this._withLivemarksMap(livemarksMap => {
-      if (livemarksMap.has(guid)) {
-        let livemark = livemarksMap.get(guid);
-        livemark.terminate();
-        livemarksMap.delete(guid);
-      }
-    });
-  },
-
-  skipDescendantsOnItemRemoval: false,
-  skipTags: true,
-
-  // nsINavHistoryObserver
-
-  onPageChanged() {},
-  onTitleChanged() {},
-  onDeleteVisits() {},
-
-  onClearHistory() {
-    this._withLivemarksMap(livemarksMap => {
-      for (let livemark of livemarksMap.values()) {
-        livemark.updateURIVisitedStatus(null, false);
-      }
-    });
-  },
-
-  onDeleteURI(aURI) {
-    this._withLivemarksMap(livemarksMap => {
-      for (let livemark of livemarksMap.values()) {
-        livemark.updateURIVisitedStatus(aURI.spec, false);
-      }
-    });
-  },
-
-  // nsISupports
-
-  classID: Components.ID("{dca61eb5-c7cd-4df1-b0fb-d0722baba251}"),
-
-  _xpcom_factory: XPCOMUtils.generateSingletonFactory(LivemarkService),
-
-  QueryInterface: ChromeUtils.generateQI([
-    Ci.mozIAsyncLivemarks,
-    Ci.nsINavBookmarkObserver,
-    Ci.nsINavHistoryObserver,
-    Ci.nsIObserver,
-    Ci.nsISupportsWeakReference,
-  ]),
-};
-
-// Livemark
-
-/**
- * Object used internally to represent a livemark.
- *
- * @param aLivemarkInfo
- *        Object containing information on the livemark.  If the livemark is
- *        not included in the object, a new livemark will be created.
- *
- * @note terminate() must be invoked before getting rid of this object.
- */
-function Livemark(aLivemarkInfo) {
-  this.id = aLivemarkInfo.id;
-  this.guid = aLivemarkInfo.guid;
-  this.feedURI = aLivemarkInfo.feedURI;
-  this.siteURI = aLivemarkInfo.siteURI || null;
-  this.title = aLivemarkInfo.title;
-  this.parentId = aLivemarkInfo.parentId;
-  this.parentGuid = aLivemarkInfo.parentGuid;
-  this.index = aLivemarkInfo.index;
-  this.dateAdded = aLivemarkInfo.dateAdded;
-  this.lastModified = aLivemarkInfo.lastModified;
-
-  this._status = Ci.mozILivemark.STATUS_READY;
-
-  // Hash of resultObservers, hashed by container.
-  this._resultObservers = new Map();
-
-  // Sorted array of objects representing livemark children in the form
-  // { uri, title, visited }.
-  this._children = [];
-
-  // Keeps a separate array of nodes for each requesting container, hashed by
-  // the container itself.
-  this._nodes = new Map();
-
-  this.loadGroup = null;
-  this.expireTime = 0;
-}
-
-Livemark.prototype = {
-  get status() {
-    return this._status;
-  },
-  set status(val) {
-    if (this._status != val) {
-      this._status = val;
-      this._invalidateRegisteredContainers();
-    }
-    return this._status;
-  },
-
-  writeSiteURI(aSiteURI, aSource) {
-    if (!aSiteURI) {
-      PlacesUtils.annotations.removeItemAnnotation(this.id,
-                                                   PlacesUtils.LMANNO_SITEURI,
-                                                   aSource);
-      this.siteURI = null;
-      return;
-    }
-
-    // Security check the site URI against the feed URI principal.
-    let secMan = Services.scriptSecurityManager;
-    let feedPrincipal = secMan.createCodebasePrincipal(this.feedURI, {});
-    try {
-      secMan.checkLoadURIWithPrincipal(feedPrincipal, aSiteURI,
-                                       Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
-    } catch (ex) {
-      return;
-    }
-
-    PlacesUtils.annotations
-               .setItemAnnotation(this.id, PlacesUtils.LMANNO_SITEURI,
-                                  aSiteURI.spec,
-                                  0, PlacesUtils.annotations.EXPIRE_NEVER,
-                                  aSource, true);
-    this.siteURI = aSiteURI;
-  },
-
-  /**
-   * Tries to updates the livemark if needed.
-   * The update process is asynchronous.
-   *
-   * @param [optional] aForceUpdate
-   *        If true will try to update the livemark even if its contents have
-   *        not yet expired.
-   */
-  updateChildren(aForceUpdate) {
-    // Check if the livemark is already updating.
-    if (this.status == Ci.mozILivemark.STATUS_LOADING)
-      return;
-
-    // Check the TTL/expiration on this, to check if there is no need to update
-    // this livemark.
-    if (!aForceUpdate && this.children.length && this.expireTime > Date.now())
-      return;
-
-    this.status = Ci.mozILivemark.STATUS_LOADING;
-
-    // Setting the status notifies observers that may remove the livemark.
-    if (this._terminated)
-      return;
-
-    try {
-      // Create a load group for the request.  This will allow us to
-      // automatically keep track of redirects, so we can always
-      // cancel the channel.
-      let loadgroup = Cc["@mozilla.org/network/load-group;1"].
-                      createInstance(Ci.nsILoadGroup);
-      // Creating a CodeBasePrincipal and using it as the loadingPrincipal
-      // is *not* desired and is only tolerated within this file.
-      // TODO: Find the right OriginAttributes and pass something other
-      // than {} to .createCodeBasePrincipal().
-      let channel = NetUtil.newChannel({
-        uri: this.feedURI,
-        loadingPrincipal: Services.scriptSecurityManager.createCodebasePrincipal(this.feedURI, {}),
-        securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
-        contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_XMLHTTPREQUEST,
-      }).QueryInterface(Ci.nsIHttpChannel);
-      channel.loadGroup = loadgroup;
-      channel.loadFlags |= Ci.nsIRequest.LOAD_BACKGROUND |
-                           Ci.nsIRequest.LOAD_BYPASS_CACHE;
-      channel.requestMethod = "GET";
-      channel.setRequestHeader("X-Moz", "livebookmarks", false);
-
-      // Stream the result to the feed parser with this listener
-      let listener = new LivemarkLoadListener(this);
-      channel.notificationCallbacks = listener;
-      channel.asyncOpen2(listener);
-
-      this.loadGroup = loadgroup;
-    } catch (ex) {
-      this.status = Ci.mozILivemark.STATUS_FAILED;
-    }
-  },
-
-  reload(aForceUpdate) {
-    this.updateChildren(aForceUpdate);
-  },
-
-  get children() {
-    return this._children;
-  },
-  set children(val) {
-    this._children = val;
-
-    // Discard the previous cached nodes, new ones should be generated.
-    for (let container of this._resultObservers.keys()) {
-      this._nodes.delete(container);
-    }
-
-    // Update visited status for each entry.
-    for (let child of this._children) {
-      history.hasVisits(child.uri).then(isVisited => {
-        this.updateURIVisitedStatus(child.uri.spec, isVisited);
-      }).catch(Cu.reportError);
-    }
-
-    return this._children;
-  },
-
-  _isURIVisited(aURI) {
-    return this.children.some(child => child.uri.equals(aURI) && child.visited);
-  },
-
-  getNodesForContainer(aContainerNode) {
-    if (this._nodes.has(aContainerNode)) {
-      return this._nodes.get(aContainerNode);
-    }
-
-    let livemark = this;
-    let nodes = [];
-    let now = Date.now() * 1000;
-    for (let child of this.children) {
-      let node = {
-        // The QueryInterface is needed cause aContainerNode is a jsval.
-        // This is required to avoid issues with scriptable wrappers that would
-        // not allow the view to correctly set expandos.
-        get parent() {
-          return aContainerNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
-        },
-        get parentResult() {
-          return this.parent.parentResult;
-        },
-        get uri() {
-          return child.uri.spec;
-        },
-        get type() {
-          return Ci.nsINavHistoryResultNode.RESULT_TYPE_URI;
-        },
-        get title() {
-          return child.title;
-        },
-        get accessCount() {
-          return Number(livemark._isURIVisited(NetUtil.newURI(this.uri)));
-        },
-        get time() {
-          return 0;
-        },
-        get icon() {
-          return "";
-        },
-        get indentLevel() {
-          return this.parent.indentLevel + 1;
-        },
-        get bookmarkIndex() {
-            return -1;
-        },
-        get itemId() {
-            return -1;
-        },
-        get dateAdded() {
-          return now;
-        },
-        get lastModified() {
-          return now;
-        },
-        get tags() {
-          return PlacesUtils.tagging.getTagsForURI(NetUtil.newURI(this.uri)).join(", ");
-        },
-        QueryInterface: ChromeUtils.generateQI([Ci.nsINavHistoryResultNode]),
-      };
-      nodes.push(node);
-    }
-    this._nodes.set(aContainerNode, nodes);
-    return nodes;
-  },
-
-  registerForUpdates(aContainerNode, aResultObserver) {
-    this._resultObservers.set(aContainerNode, aResultObserver);
-  },
-
-  unregisterForUpdates(aContainerNode) {
-    this._resultObservers.delete(aContainerNode);
-    this._nodes.delete(aContainerNode);
-  },
-
-  _invalidateRegisteredContainers() {
-    for (let [ container, observer ] of this._resultObservers) {
-      observer.invalidateContainer(container);
-    }
-  },
-
-  /**
-   * Updates the visited status of nodes observing this livemark.
-   *
-   * @param href
-   *        If provided will update nodes having the given uri,
-   *        otherwise any node.
-   * @param visitedStatus
-   *        Whether the nodes should be set as visited.
-   */
-  updateURIVisitedStatus(href, visitedStatus) {
-    let wasVisited = false;
-    for (let child of this.children) {
-      if (!href || child.uri.spec == href) {
-        wasVisited = child.visited;
-        child.visited = visitedStatus;
-      }
-    }
-
-    for (let [ container, observer ] of this._resultObservers) {
-      if (this._nodes.has(container)) {
-        let nodes = this._nodes.get(container);
-        for (let node of nodes) {
-          if (!href || node.uri == href) {
-            Services.tm.dispatchToMainThread(() => {
-              observer.nodeHistoryDetailsChanged(node, node.time, wasVisited);
-            });
-          }
-        }
-      }
-    }
-  },
-
-  /**
-   * Terminates the livemark entry, cancelling any ongoing load.
-   * Must be invoked before destroying the entry.
-   */
-  terminate() {
-    // Avoid handling any updateChildren request from now on.
-    this._terminated = true;
-    this.abort();
-  },
-
-  /**
-   * Aborts the livemark loading if needed.
-   */
-  abort() {
-    this.status = Ci.mozILivemark.STATUS_FAILED;
-    if (this.loadGroup) {
-      this.loadGroup.cancel(Cr.NS_BINDING_ABORTED);
-      this.loadGroup = null;
-    }
-  },
-
-  QueryInterface: ChromeUtils.generateQI([
-    Ci.mozILivemark,
-  ]),
-};
-
-// LivemarkLoadListener
-
-/**
- * Object used internally to handle loading a livemark's contents.
- *
- * @param aLivemark
- *        The Livemark that is loading.
- */
-function LivemarkLoadListener(aLivemark) {
-  this._livemark = aLivemark;
-  this._processor = null;
-  this._isAborted = false;
-  this._ttl = EXPIRE_TIME_MS;
-}
-
-LivemarkLoadListener.prototype = {
-  abort(aException) {
-    if (!this._isAborted) {
-      this._isAborted = true;
-      this._livemark.abort();
-      this._setResourceTTL(ONERROR_EXPIRE_TIME_MS);
-    }
-  },
-
-  // nsIFeedResultListener
-  handleResult(aResult) {
-    if (this._isAborted) {
-      return;
-    }
-
-    try {
-      // We need this to make sure the item links are safe
-      let feedPrincipal =
-        Services.scriptSecurityManager
-                .createCodebasePrincipal(this._livemark.feedURI, {});
-
-      // Enforce well-formedness because the existing code does
-      if (!aResult || !aResult.doc || aResult.bozo) {
-        throw new Components.Exception("", Cr.NS_ERROR_FAILURE);
-      }
-
-      let feed = aResult.doc.QueryInterface(Ci.nsIFeed);
-      let siteURI = this._livemark.siteURI;
-      if (feed.link && (!siteURI || !feed.link.equals(siteURI))) {
-        siteURI = feed.link;
-        this._livemark.writeSiteURI(siteURI);
-      }
-
-      // Insert feed items.
-      let livemarkChildren = [];
-      for (let i = 0; i < feed.items.length; ++i) {
-        let entry = feed.items.queryElementAt(i, Ci.nsIFeedEntry);
-        let uri = entry.link || siteURI;
-        if (!uri) {
-          continue;
-        }
-
-        try {
-          Services.scriptSecurityManager
-                  .checkLoadURIWithPrincipal(feedPrincipal, uri,
-                                             Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
-        } catch (ex) {
-          continue;
-        }
-
-        let title = entry.title ? entry.title.plainText() : "";
-        livemarkChildren.push({ uri, title, visited: false });
-      }
-
-      this._livemark.children = livemarkChildren;
-    } catch (ex) {
-      Cu.reportError(ex);
-      this.abort(ex);
-    } finally {
-      this._processor.listener = null;
-      this._processor = null;
-    }
-  },
-
-  onDataAvailable(aRequest, aContext, aInputStream, aSourceOffset, aCount) {
-    if (this._processor) {
-      this._processor.onDataAvailable(aRequest, aContext, aInputStream,
-                                      aSourceOffset, aCount);
-    }
-  },
-
-  onStartRequest(aRequest, aContext) {
-    if (this._isAborted) {
-      throw new Components.Exception("", Cr.NS_ERROR_UNEXPECTED);
-    }
-
-    let channel = aRequest.QueryInterface(Ci.nsIChannel);
-    try {
-      // Parse feed data as it comes in
-      this._processor = Cc["@mozilla.org/feed-processor;1"].
-                        createInstance(Ci.nsIFeedProcessor);
-      this._processor.listener = this;
-      this._processor.parseAsync(null, channel.URI);
-      this._processor.onStartRequest(aRequest, aContext);
-    } catch (ex) {
-      Cu.reportError("Livemark Service: feed processor received an invalid channel for " + channel.URI.spec);
-      this.abort(ex);
-    }
-  },
-
-  onStopRequest(aRequest, aContext, aStatus) {
-    if (!Components.isSuccessCode(aStatus)) {
-      this.abort();
-      return;
-    }
-
-    // Set an expiration on the livemark, to reloading the data in future.
-    try {
-      if (this._processor) {
-        this._processor.onStopRequest(aRequest, aContext, aStatus);
-      }
-
-      // Calculate a new ttl
-      let channel = aRequest.QueryInterface(Ci.nsICachingChannel);
-      if (channel) {
-        let entryInfo = channel.cacheToken.QueryInterface(Ci.nsICacheEntry);
-        if (entryInfo) {
-          // nsICacheEntry returns value as seconds.
-          let expireTime = entryInfo.expirationTime * 1000;
-          let nowTime = Date.now();
-          // Note, expireTime can be 0, see bug 383538.
-          if (expireTime > nowTime) {
-            this._setResourceTTL(Math.max((expireTime - nowTime),
-                                          EXPIRE_TIME_MS));
-            return;
-          }
-        }
-      }
-      this._setResourceTTL(EXPIRE_TIME_MS);
-    } catch (ex) {
-      this.abort(ex);
-    } finally {
-      if (this._livemark.status == Ci.mozILivemark.STATUS_LOADING) {
-        this._livemark.status = Ci.mozILivemark.STATUS_READY;
-      }
-      this._livemark.locked = false;
-      this._livemark.loadGroup = null;
-    }
-  },
-
-  _setResourceTTL(aMilliseconds) {
-    this._livemark.expireTime = Date.now() + aMilliseconds;
-  },
-
-  // nsIInterfaceRequestor
-  getInterface(aIID) {
-    return this.QueryInterface(aIID);
-  },
-
-  // nsISupports
-  QueryInterface: ChromeUtils.generateQI([
-    Ci.nsIFeedResultListener,
-    Ci.nsIStreamListener,
-    Ci.nsIRequestObserver,
-    Ci.nsIInterfaceRequestor,
-  ]),
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([LivemarkService]);
--- a/toolkit/components/places/nsNavBookmarks.cpp
+++ b/toolkit/components/places/nsNavBookmarks.cpp
@@ -33,17 +33,16 @@ const int32_t nsNavBookmarks::kGetChildr
 const int32_t nsNavBookmarks::kGetChildrenIndex_SyncStatus = 22;
 
 using namespace mozilla::places;
 
 PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsNavBookmarks, gBookmarksService)
 
 #define BOOKMARKS_ANNO_PREFIX "bookmarks/"
 #define BOOKMARKS_TOOLBAR_FOLDER_ANNO NS_LITERAL_CSTRING(BOOKMARKS_ANNO_PREFIX "toolbarFolder")
-#define FEED_URI_ANNO NS_LITERAL_CSTRING("livemark/feedURI")
 #define SYNC_PARENT_ANNO "sync/parent"
 #define SQLITE_MAX_VARIABLE_NUMBER 999
 
 
 namespace {
 
 #define SKIP_TAGS(condition) ((condition) ? SkipTags : DontSkip)
 
@@ -816,28 +815,16 @@ nsNavBookmarks::CreateFolder(int64_t aPa
     MOZ_RELEASE_ASSERT(success);
 
     PlacesObservers::NotifyListeners(events);
   }
 
   return NS_OK;
 }
 
-bool nsNavBookmarks::IsLivemark(int64_t aFolderId)
-{
-  nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
-  NS_ENSURE_TRUE(annosvc, false);
-  bool isLivemark;
-  nsresult rv = annosvc->ItemHasAnnotation(aFolderId,
-                                           FEED_URI_ANNO,
-                                           &isLivemark);
-  NS_ENSURE_SUCCESS(rv, false);
-  return isLivemark;
-}
-
 nsresult
 nsNavBookmarks::GetDescendantChildren(int64_t aFolderId,
                                       const nsACString& aFolderGuid,
                                       int64_t aGrandParentId,
                                       nsTArray<BookmarkData>& aFolderChildrenArray) {
   // New children will be added from this index on.
   uint32_t startIndex = aFolderChildrenArray.Length();
   nsresult rv;
@@ -1717,23 +1704,16 @@ nsNavBookmarks::ProcessFolderNodeRow(
     uint32_t nodeType;
     node->GetType(&nodeType);
     if (nodeType == nsINavHistoryResultNode::RESULT_TYPE_QUERY &&
         aOptions->ExcludeQueries()) {
       return NS_OK;
     }
   }
   else if (itemType == TYPE_FOLDER) {
-    // ExcludeReadOnlyFolders currently means "ExcludeLivemarks" (to be fixed in
-    // bug 1072833)
-    if (aOptions->ExcludeReadOnlyFolders()) {
-      if (IsLivemark(id))
-        return NS_OK;
-    }
-
     nsAutoCString title;
     bool isNull;
     rv = aRow->GetIsNull(nsNavHistory::kGetInfoIndex_Title, &isNull);
     NS_ENSURE_SUCCESS(rv, rv);
     if (!isNull) {
       rv = aRow->GetUTF8String(nsNavHistory::kGetInfoIndex_Title, title);
       NS_ENSURE_SUCCESS(rv, rv);
     }
--- a/toolkit/components/places/nsNavBookmarks.h
+++ b/toolkit/components/places/nsNavBookmarks.h
@@ -230,25 +230,16 @@ public:
   static mozilla::Atomic<int64_t> sTotalSyncChanges;
   static void NoteSyncChange();
 
 private:
   static nsNavBookmarks* gBookmarksService;
 
   ~nsNavBookmarks();
 
-  /**
-   * Checks whether or not aFolderId points to a live bookmark.
-   *
-   * @param aFolderId
-   *        the item-id of the folder to check.
-   * @return true if aFolderId points to live bookmarks, false otherwise.
-   */
-  bool IsLivemark(int64_t aFolderId);
-
   nsresult AdjustIndices(int64_t aFolder,
                          int32_t aStartIndex,
                          int32_t aEndIndex,
                          int32_t aDelta);
 
   nsresult AdjustSeparatorsSyncCounter(int64_t aFolderId,
                                        int32_t aStartIndex,
                                        int64_t aSyncChangeDelta);
--- a/toolkit/components/places/tests/unit/test_multi_word_tags.js
+++ b/toolkit/components/places/tests/unit/test_multi_word_tags.js
@@ -38,17 +38,17 @@ add_task(async function run_test() {
 
   tagssvc.tagURI(uri1, ["foo"]);
   tagssvc.tagURI(uri2, ["bar"]);
   tagssvc.tagURI(uri3, ["cheese"]);
   tagssvc.tagURI(uri4, ["foo bar"]);
   tagssvc.tagURI(uri5, ["bar cheese"]);
   tagssvc.tagURI(uri6, ["foo bar cheese"]);
 
-  // exclude livemark items, search for "item", should get one result
+  // Search for "item", should get one result
   var options = histsvc.getNewQueryOptions();
   options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS;
 
   var query = histsvc.getNewQuery();
   query.searchTerms = "foo";
   var result = histsvc.executeQuery(query, options);
   var root = result.root;
   root.containerOpen = true;
--- a/toolkit/components/places/tests/unit/test_null_interfaces.js
+++ b/toolkit/components/places/tests/unit/test_null_interfaces.js
@@ -12,17 +12,16 @@ var testServices = [
   ["browser/nav-history-service;1",
     ["nsINavHistoryService"],
     ["queryStringToQuery", "removePagesByTimeframe", "removePagesFromHost", "getObservers"],
   ],
   ["browser/nav-bookmarks-service;1",
     ["nsINavBookmarksService", "nsINavHistoryObserver"],
     ["createFolder", "getObservers", "onFrecencyChanged", "onTitleChanged", "onDeleteURI"],
   ],
-  ["browser/livemark-service;2", ["mozIAsyncLivemarks"], ["reloadLivemarks"]],
   ["browser/favicon-service;1", ["nsIFaviconService"], []],
   ["browser/tagging-service;1", ["nsITaggingService"], []],
 ];
 info(testServices.join("\n"));
 
 function run_test() {
   for (let [cid, ifaces, nothrow] of testServices) {
     info(`Running test with ${cid} ${ifaces.join(", ")} ${nothrow}`);
--- a/toolkit/components/places/toolkitplaces.manifest
+++ b/toolkit/components/places/toolkitplaces.manifest
@@ -1,12 +1,8 @@
-# nsLivemarkService.js
-component {dca61eb5-c7cd-4df1-b0fb-d0722baba251} nsLivemarkService.js
-contract @mozilla.org/browser/livemark-service;2 {dca61eb5-c7cd-4df1-b0fb-d0722baba251}
-
 # nsTaggingService.js
 component {bbc23860-2553-479d-8b78-94d9038334f7} nsTaggingService.js
 contract @mozilla.org/browser/tagging-service;1 {bbc23860-2553-479d-8b78-94d9038334f7}
 component {1dcc23b0-d4cb-11dc-9ad6-479d56d89593} nsTaggingService.js
 contract @mozilla.org/autocomplete/search;1?name=places-tag-autocomplete {1dcc23b0-d4cb-11dc-9ad6-479d56d89593}
 
 # nsPlacesExpiration.js
 component {705a423f-2f69-42f3-b9fe-1517e0dee56f} nsPlacesExpiration.js