Backed out changeset e0eab399db59 (bug 1417101) for Static errors in toolkit/components/places/target r=backout on a CLOSED TREE
authorNoemi Erli <nerli@mozilla.com>
Sat, 18 Nov 2017 02:48:42 +0200
changeset 392472 ab69dc1d4af312aec7b651c1df97243c42b1df2e
parent 392471 cd4fdff525dcf01c61a63bf8c80607795a5055bd
child 392552 9999a9025f644801944512c075c9ca7ccebb9988
push id55602
push usernerli@mozilla.com
push dateSat, 18 Nov 2017 00:49:19 +0000
treeherderautoland@ab69dc1d4af3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1417101
milestone59.0a1
backs oute0eab399db5982c6a13aa7d3a838aa302d8f9ec1
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
Backed out changeset e0eab399db59 (bug 1417101) for Static errors in toolkit/components/places/target r=backout on a CLOSED TREE
toolkit/components/places/nsAnnotationService.cpp
toolkit/components/places/nsAnnotationService.h
toolkit/components/places/nsIAnnotationService.idl
toolkit/components/places/nsNavBookmarks.cpp
toolkit/components/places/nsNavBookmarks.h
toolkit/components/places/tests/bookmarks/test_bookmarks_notifications.js
toolkit/components/places/tests/bookmarks/test_nsINavBookmarkObserver.js
toolkit/components/places/tests/unit/test_null_interfaces.js
--- a/toolkit/components/places/nsAnnotationService.cpp
+++ b/toolkit/components/places/nsAnnotationService.cpp
@@ -42,38 +42,16 @@ const int32_t nsAnnotationService::kAnno
 const int32_t nsAnnotationService::kAnnoIndex_NameID = 2;
 const int32_t nsAnnotationService::kAnnoIndex_Content = 3;
 const int32_t nsAnnotationService::kAnnoIndex_Flags = 4;
 const int32_t nsAnnotationService::kAnnoIndex_Expiration = 5;
 const int32_t nsAnnotationService::kAnnoIndex_Type = 6;
 const int32_t nsAnnotationService::kAnnoIndex_DateAdded = 7;
 const int32_t nsAnnotationService::kAnnoIndex_LastModified = 8;
 
-namespace {
-
-// Fires `onItemChanged` notifications for bookmark annotation changes.
-void
-NotifyItemChanged(const BookmarkData& aBookmark, const nsACString& aName,
-                  uint16_t aSource, bool aDontUpdateLastModified)
-{
-  ItemChangeData changeData;
-  changeData.bookmark = aBookmark;
-  changeData.isAnnotation = true;
-  changeData.updateLastModified = !aDontUpdateLastModified;
-  changeData.source = aSource;
-  changeData.property = aName;
-
-  nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
-  if (bookmarks) {
-    bookmarks->NotifyItemChanged(changeData);
-  }
-}
-
-}
-
 namespace mozilla {
 namespace places {
 
 ////////////////////////////////////////////////////////////////////////////////
 //// AnnotatedResult
 
 AnnotatedResult::AnnotatedResult(const nsCString& aGUID,
                                  nsIURI* aURI,
@@ -168,46 +146,42 @@ nsAnnotationService::Init()
   nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
   if (obsSvc) {
     (void)obsSvc->AddObserver(this, TOPIC_PLACES_SHUTDOWN, true);
   }
 
   return NS_OK;
 }
 
-Result<Maybe<BookmarkData>, nsresult>
+nsresult
 nsAnnotationService::SetAnnotationStringInternal(nsIURI* aURI,
                                                  int64_t aItemId,
                                                  const nsACString& aName,
                                                  const nsAString& aValue,
                                                  int32_t aFlags,
                                                  uint16_t aExpiration)
 {
   mozStorageTransaction transaction(mDB->MainConn(), false);
   nsCOMPtr<mozIStorageStatement> statement;
-
-  Result<Maybe<BookmarkData>, nsresult> result =
-    StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration,
-                       nsIAnnotationService::TYPE_STRING,
-                       statement);
-  if (result.isOk()) {
-    mozStorageStatementScoper scoper(statement);
+  nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration,
+                                   nsIAnnotationService::TYPE_STRING,
+                                   statement);
+  NS_ENSURE_SUCCESS(rv, rv);
+  mozStorageStatementScoper scoper(statement);
 
-    nsresult rv = statement->BindStringByName(NS_LITERAL_CSTRING("content"),
-                                              aValue);
-    NS_ENSURE_SUCCESS(rv, Err(rv));
+  rv = statement->BindStringByName(NS_LITERAL_CSTRING("content"), aValue);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = statement->Execute();
-    NS_ENSURE_SUCCESS(rv, Err(rv));
+  rv = statement->Execute();
+  NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = transaction.Commit();
-    NS_ENSURE_SUCCESS(rv, Err(rv));
-  }
+  rv = transaction.Commit();
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  return result;
+  return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsAnnotationService::SetPageAnnotation(nsIURI* aURI,
                                        const nsACString& aName,
                                        nsIVariant* aValue,
                                        int32_t aFlags,
@@ -377,22 +351,19 @@ NS_IMETHODIMP
 nsAnnotationService::SetPageAnnotationString(nsIURI* aURI,
                                              const nsACString& aName,
                                              const nsAString& aValue,
                                              int32_t aFlags,
                                              uint16_t aExpiration)
 {
   NS_ENSURE_ARG(aURI);
 
-  Result<Maybe<BookmarkData>, nsresult> result =
-    SetAnnotationStringInternal(aURI, 0, aName, aValue,
-                                aFlags, aExpiration);
-  if (result.isErr()) {
-    return result.unwrapErr();
-  }
+  nsresult rv = SetAnnotationStringInternal(aURI, 0, aName, aValue,
+                                            aFlags, aExpiration);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   NOTIFY_ANNOS_OBSERVERS(OnPageAnnotationSet(aURI, aName));
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
@@ -404,82 +375,67 @@ nsAnnotationService::SetItemAnnotationSt
                                              uint16_t aSource,
                                              bool aDontUpdateLastModified)
 {
   NS_ENSURE_ARG_MIN(aItemId, 1);
 
   if (aExpiration == EXPIRE_WITH_HISTORY)
     return NS_ERROR_INVALID_ARG;
 
-  Result<Maybe<BookmarkData>, nsresult> result =
-    SetAnnotationStringInternal(nullptr, aItemId, aName, aValue,
-                                aFlags, aExpiration);
-  if (result.isErr()) {
-    return result.unwrapErr();
-  }
+  nsresult rv = SetAnnotationStringInternal(nullptr, aItemId, aName, aValue,
+                                            aFlags, aExpiration);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName, aSource, aDontUpdateLastModified));
 
-  Maybe<BookmarkData> maybeBookmark = result.unwrap();
-  if (maybeBookmark) {
-    NotifyItemChanged(maybeBookmark.ref(), aName, aSource,
-                      aDontUpdateLastModified);
-  }
-
   return NS_OK;
 }
 
 
-Result<Maybe<BookmarkData>, nsresult>
+nsresult
 nsAnnotationService::SetAnnotationInt32Internal(nsIURI* aURI,
                                                 int64_t aItemId,
                                                 const nsACString& aName,
                                                 int32_t aValue,
                                                 int32_t aFlags,
                                                 uint16_t aExpiration)
 {
   mozStorageTransaction transaction(mDB->MainConn(), false);
   nsCOMPtr<mozIStorageStatement> statement;
-  Result<Maybe<BookmarkData>, nsresult> result =
-    StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration,
-                       nsIAnnotationService::TYPE_INT32,
-                       statement);
-  if (result.isOk()) {
-    mozStorageStatementScoper scoper(statement);
+  nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration,
+                                   nsIAnnotationService::TYPE_INT32,
+                                   statement);
+  NS_ENSURE_SUCCESS(rv, rv);
+  mozStorageStatementScoper scoper(statement);
 
-    nsresult rv = statement->BindInt32ByName(NS_LITERAL_CSTRING("content"),
-                                             aValue);
-    NS_ENSURE_SUCCESS(rv, Err(rv));
+  rv = statement->BindInt32ByName(NS_LITERAL_CSTRING("content"), aValue);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = statement->Execute();
-    NS_ENSURE_SUCCESS(rv, Err(rv));
+  rv = statement->Execute();
+  NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = transaction.Commit();
-    NS_ENSURE_SUCCESS(rv, Err(rv));
-  }
+  rv = transaction.Commit();
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  return result;
+  return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsAnnotationService::SetPageAnnotationInt32(nsIURI* aURI,
                                             const nsACString& aName,
                                             int32_t aValue,
                                             int32_t aFlags,
                                             uint16_t aExpiration)
 {
   NS_ENSURE_ARG(aURI);
 
-  Result<Maybe<BookmarkData>, nsresult> result =
-    SetAnnotationInt32Internal(aURI, 0, aName, aValue,
-                               aFlags, aExpiration);
-  if (result.isErr()) {
-    return result.unwrapErr();
-  }
+  nsresult rv = SetAnnotationInt32Internal(aURI, 0, aName, aValue,
+                                           aFlags, aExpiration);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   NOTIFY_ANNOS_OBSERVERS(OnPageAnnotationSet(aURI, aName));
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
@@ -491,82 +447,67 @@ nsAnnotationService::SetItemAnnotationIn
                                             uint16_t aSource,
                                             bool aDontUpdateLastModified)
 {
   NS_ENSURE_ARG_MIN(aItemId, 1);
 
   if (aExpiration == EXPIRE_WITH_HISTORY)
     return NS_ERROR_INVALID_ARG;
 
-  Result<Maybe<BookmarkData>, nsresult> result =
-    SetAnnotationInt32Internal(nullptr, aItemId, aName, aValue,
-                               aFlags, aExpiration);
-  if (result.isErr()) {
-    return result.unwrapErr();
-  }
+  nsresult rv = SetAnnotationInt32Internal(nullptr, aItemId, aName, aValue,
+                                           aFlags, aExpiration);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName, aSource, aDontUpdateLastModified));
 
-  Maybe<BookmarkData> maybeBookmark = result.unwrap();
-  if (maybeBookmark) {
-    NotifyItemChanged(maybeBookmark.ref(), aName, aSource,
-                      aDontUpdateLastModified);
-  }
-
   return NS_OK;
 }
 
 
-Result<Maybe<BookmarkData>, nsresult>
+nsresult
 nsAnnotationService::SetAnnotationInt64Internal(nsIURI* aURI,
                                                 int64_t aItemId,
                                                 const nsACString& aName,
                                                 int64_t aValue,
                                                 int32_t aFlags,
                                                 uint16_t aExpiration)
 {
   mozStorageTransaction transaction(mDB->MainConn(), false);
   nsCOMPtr<mozIStorageStatement> statement;
-  Result<Maybe<BookmarkData>, nsresult> result =
-    StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration,
-                       nsIAnnotationService::TYPE_INT64,
-                       statement);
-  if (result.isOk()) {
-    mozStorageStatementScoper scoper(statement);
+  nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration,
+                                   nsIAnnotationService::TYPE_INT64,
+                                   statement);
+  NS_ENSURE_SUCCESS(rv, rv);
+  mozStorageStatementScoper scoper(statement);
 
-    nsresult rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("content"),
-                                             aValue);
-    NS_ENSURE_SUCCESS(rv, Err(rv));
+  rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("content"), aValue);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = statement->Execute();
-    NS_ENSURE_SUCCESS(rv, Err(rv));
+  rv = statement->Execute();
+  NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = transaction.Commit();
-    NS_ENSURE_SUCCESS(rv, Err(rv));
-  }
+  rv = transaction.Commit();
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  return result;
+  return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsAnnotationService::SetPageAnnotationInt64(nsIURI* aURI,
                                             const nsACString& aName,
                                             int64_t aValue,
                                             int32_t aFlags,
                                             uint16_t aExpiration)
 {
   NS_ENSURE_ARG(aURI);
 
-  Result<Maybe<BookmarkData>, nsresult> result =
-    SetAnnotationInt64Internal(aURI, 0, aName, aValue,
-                               aFlags, aExpiration);
-  if (result.isErr()) {
-    return result.unwrapErr();
-  }
+  nsresult rv = SetAnnotationInt64Internal(aURI, 0, aName, aValue,
+                                           aFlags, aExpiration);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   NOTIFY_ANNOS_OBSERVERS(OnPageAnnotationSet(aURI, aName));
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
@@ -578,82 +519,67 @@ nsAnnotationService::SetItemAnnotationIn
                                             uint16_t aSource,
                                             bool aDontUpdateLastModified)
 {
   NS_ENSURE_ARG_MIN(aItemId, 1);
 
   if (aExpiration == EXPIRE_WITH_HISTORY)
     return NS_ERROR_INVALID_ARG;
 
-  Result<Maybe<BookmarkData>, nsresult> result =
-    SetAnnotationInt64Internal(nullptr, aItemId, aName, aValue,
-                               aFlags, aExpiration);
-  if (result.isErr()) {
-    return result.unwrapErr();
-  }
+  nsresult rv = SetAnnotationInt64Internal(nullptr, aItemId, aName, aValue,
+                                           aFlags, aExpiration);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName, aSource, aDontUpdateLastModified));
 
-  Maybe<BookmarkData> maybeBookmark = result.unwrap();
-  if (maybeBookmark) {
-    NotifyItemChanged(maybeBookmark.ref(), aName, aSource,
-                      aDontUpdateLastModified);
-  }
-
   return NS_OK;
 }
 
 
-Result<Maybe<BookmarkData>, nsresult>
+nsresult
 nsAnnotationService::SetAnnotationDoubleInternal(nsIURI* aURI,
                                                  int64_t aItemId,
                                                  const nsACString& aName,
                                                  double aValue,
                                                  int32_t aFlags,
                                                  uint16_t aExpiration)
 {
   mozStorageTransaction transaction(mDB->MainConn(), false);
   nsCOMPtr<mozIStorageStatement> statement;
-  Result<Maybe<BookmarkData>, nsresult> result =
-    StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration,
-                       nsIAnnotationService::TYPE_DOUBLE,
-                       statement);
-  if (result.isOk()) {
-    mozStorageStatementScoper scoper(statement);
+  nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration,
+                                   nsIAnnotationService::TYPE_DOUBLE,
+                                   statement);
+  NS_ENSURE_SUCCESS(rv, rv);
+  mozStorageStatementScoper scoper(statement);
 
-    nsresult rv = statement->BindDoubleByName(NS_LITERAL_CSTRING("content"),
-                                              aValue);
-    NS_ENSURE_SUCCESS(rv, Err(rv));
+  rv = statement->BindDoubleByName(NS_LITERAL_CSTRING("content"), aValue);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = statement->Execute();
-    NS_ENSURE_SUCCESS(rv, Err(rv));
+  rv = statement->Execute();
+  NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = transaction.Commit();
-    NS_ENSURE_SUCCESS(rv, Err(rv));
-  }
+  rv = transaction.Commit();
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  return result;
+  return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsAnnotationService::SetPageAnnotationDouble(nsIURI* aURI,
                                              const nsACString& aName,
                                              double aValue,
                                              int32_t aFlags,
                                              uint16_t aExpiration)
 {
   NS_ENSURE_ARG(aURI);
 
-  Result<Maybe<BookmarkData>, nsresult> result =
-    SetAnnotationDoubleInternal(aURI, 0, aName, aValue,
-                                aFlags, aExpiration);
-  if (result.isErr()) {
-    return result.unwrapErr();
-  }
+  nsresult rv = SetAnnotationDoubleInternal(aURI, 0, aName, aValue,
+                                            aFlags, aExpiration);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   NOTIFY_ANNOS_OBSERVERS(OnPageAnnotationSet(aURI, aName));
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
@@ -665,31 +591,22 @@ nsAnnotationService::SetItemAnnotationDo
                                              uint16_t aSource,
                                              bool aDontUpdateLastModified)
 {
   NS_ENSURE_ARG_MIN(aItemId, 1);
 
   if (aExpiration == EXPIRE_WITH_HISTORY)
     return NS_ERROR_INVALID_ARG;
 
-  Result<Maybe<BookmarkData>, nsresult> result =
-    SetAnnotationDoubleInternal(nullptr, aItemId, aName, aValue,
-                                aFlags, aExpiration);
-  if (result.isErr()) {
-    return result.unwrapErr();
-  }
+  nsresult rv = SetAnnotationDoubleInternal(nullptr, aItemId, aName, aValue,
+                                            aFlags, aExpiration);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aItemId, aName, aSource, aDontUpdateLastModified));
 
-  Maybe<BookmarkData> maybeBookmark = result.unwrap();
-  if (maybeBookmark) {
-    NotifyItemChanged(maybeBookmark.ref(), aName, aSource,
-                      aDontUpdateLastModified);
-  }
-
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAnnotationService::GetPageAnnotationString(nsIURI* aURI,
                                              const nsACString& aName,
                                              nsAString& _retval)
 {
@@ -1442,17 +1359,17 @@ nsAnnotationService::ItemHasAnnotation(i
 }
 
 
 /**
  * @note We don't remove anything from the moz_anno_attributes table. If we
  *       delete the last item of a given name, that item really should go away.
  *       It will be cleaned up by expiration.
  */
-Result<Maybe<BookmarkData>, nsresult>
+nsresult
 nsAnnotationService::RemoveAnnotationInternal(nsIURI* aURI,
                                               int64_t aItemId,
                                               const nsACString& aName)
 {
   bool isItemAnnotation = (aItemId > 0);
   nsCOMPtr<mozIStorageStatement> statement;
   if (isItemAnnotation) {
     statement = mDB->GetStatement(
@@ -1466,86 +1383,63 @@ nsAnnotationService::RemoveAnnotationInt
     statement = mDB->GetStatement(
       "DELETE FROM moz_annos "
       "WHERE place_id = "
           "(SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
         "AND anno_attribute_id = "
           "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name)"
     );
   }
-  NS_ENSURE_TRUE(statement, Err(NS_ERROR_UNEXPECTED));
+  NS_ENSURE_STATE(statement);
   mozStorageStatementScoper scoper(statement);
 
   nsresult rv;
-  Maybe<BookmarkData> maybeBookmark;
-  if (isItemAnnotation) {
+  if (isItemAnnotation)
     rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId);
-    NS_ENSURE_SUCCESS(rv, Err(rv));
-
-    nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
-    if (bookmarks) {
-      BookmarkData bookmark;
-      if (NS_SUCCEEDED(bookmarks->FetchItemInfo(aItemId, bookmark))) {
-        maybeBookmark.emplace(bookmark);
-      }
-    }
-  }
-  else {
+  else
     rv = URIBinder::Bind(statement, NS_LITERAL_CSTRING("page_url"), aURI);
-    NS_ENSURE_SUCCESS(rv, Err(rv));
-  }
+  NS_ENSURE_SUCCESS(rv, rv);
 
   rv = statement->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), aName);
-  NS_ENSURE_SUCCESS(rv, Err(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   rv = statement->Execute();
-  NS_ENSURE_SUCCESS(rv, Err(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  return maybeBookmark;
+  return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsAnnotationService::RemovePageAnnotation(nsIURI* aURI,
                                           const nsACString& aName)
 {
   NS_ENSURE_ARG(aURI);
 
-  Result<Maybe<BookmarkData>, nsresult> result =
-    RemoveAnnotationInternal(aURI, 0, aName);
-  if (result.isErr()) {
-    return result.unwrapErr();
-  }
+  nsresult rv = RemoveAnnotationInternal(aURI, 0, aName);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   NOTIFY_ANNOS_OBSERVERS(OnPageAnnotationRemoved(aURI, aName));
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsAnnotationService::RemoveItemAnnotation(int64_t aItemId,
                                           const nsACString& aName,
                                           uint16_t aSource)
 {
   NS_ENSURE_ARG_MIN(aItemId, 1);
 
-  Result<Maybe<BookmarkData>, nsresult> result =
-    RemoveAnnotationInternal(nullptr, aItemId, aName);
-  if (result.isErr()) {
-    return result.unwrapErr();
-  }
+  nsresult rv = RemoveAnnotationInternal(nullptr, aItemId, aName);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationRemoved(aItemId, aName, aSource));
 
-  Maybe<BookmarkData> maybeBookmark = result.unwrap();
-  if (maybeBookmark) {
-    NotifyItemChanged(maybeBookmark.ref(), aName, aSource, false);
-  }
-
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsAnnotationService::RemovePageAnnotations(nsIURI* aURI)
 {
   NS_ENSURE_ARG(aURI);
@@ -1570,44 +1464,34 @@ nsAnnotationService::RemovePageAnnotatio
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsAnnotationService::RemoveItemAnnotations(int64_t aItemId,
                                            uint16_t aSource)
 {
-  nsresult rv = RemoveItemAnnotationsWithoutNotifying(aItemId);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationRemoved(aItemId, EmptyCString(),
-                                                 aSource));
-
-  return NS_OK;
-}
-
-
-nsresult
-nsAnnotationService::RemoveItemAnnotationsWithoutNotifying(int64_t aItemId)
-{
   NS_ENSURE_ARG_MIN(aItemId, 1);
 
   // Should this be precompiled or a getter?
   nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
     "DELETE FROM moz_items_annos WHERE item_id = :item_id"
   );
   NS_ENSURE_STATE(statement);
   mozStorageStatementScoper scoper(statement);
 
   nsresult rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = statement->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationRemoved(aItemId, EmptyCString(),
+                                                 aSource));
+
   return NS_OK;
 }
 
 
 /**
  * @note If we use annotations for some standard items like GeckoFlags, it
  *       might be a good idea to blacklist these standard annotations from this
  *       copy function.
@@ -1760,24 +1644,16 @@ nsAnnotationService::CopyItemAnnotations
     NS_ENSURE_SUCCESS(rv, rv);
     rv = copyStmt->BindInt64ByName(NS_LITERAL_CSTRING("date"), PR_Now());
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = copyStmt->Execute();
     NS_ENSURE_SUCCESS(rv, rv);
 
     NOTIFY_ANNOS_OBSERVERS(OnItemAnnotationSet(aDestItemId, annoName, aSource, false));
-
-    nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
-    if (bookmarks) {
-      BookmarkData bookmark;
-      if (NS_SUCCEEDED(bookmarks->FetchItemInfo(aDestItemId, bookmark))) {
-        NotifyItemChanged(bookmark, annoName, aSource, false);
-      }
-    }
   }
 
   rv = transaction.Commit();
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
@@ -1954,17 +1830,17 @@ nsAnnotationService::StartGetAnnotation(
  * This does most of the setup work needed to set an annotation, except for
  * binding the the actual value and executing the statement.
  * It will either update an existing annotation or insert a new one.
  *
  * @note The aStatement RESULT IS NOT ADDREFED.  This is just one of the class
  *       vars, which control its scope.  DO NOT RELEASE.
  *       The caller must take care of resetting the statement if this succeeds.
  */
-Result<Maybe<BookmarkData>, nsresult>
+nsresult
 nsAnnotationService::StartSetAnnotation(nsIURI* aURI,
                                         int64_t aItemId,
                                         const nsACString& aName,
                                         int32_t aFlags,
                                         uint16_t aExpiration,
                                         uint16_t aType,
                                         nsCOMPtr<mozIStorageStatement>& aStatement)
 {
@@ -1973,155 +1849,134 @@ nsAnnotationService::StartSetAnnotation(
   if (aExpiration == EXPIRE_SESSION) {
     mHasSessionAnnotations = true;
   }
 
   // Ensure the annotation name exists.
   nsCOMPtr<mozIStorageStatement> addNameStmt = mDB->GetStatement(
     "INSERT OR IGNORE INTO moz_anno_attributes (name) VALUES (:anno_name)"
   );
-  NS_ENSURE_TRUE(addNameStmt, Err(NS_ERROR_UNEXPECTED));
+  NS_ENSURE_STATE(addNameStmt);
   mozStorageStatementScoper scoper(addNameStmt);
 
   nsresult rv = addNameStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), aName);
-  NS_ENSURE_SUCCESS(rv, Err(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
   rv = addNameStmt->Execute();
-  NS_ENSURE_SUCCESS(rv, Err(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // We have to check 2 things:
   // - if the annotation already exists we should update it.
   // - we should not allow setting annotations on invalid URIs or itemIds.
   // This query will tell us:
   // - whether the item or page exists.
   // - whether the annotation already exists.
   // - the nameID associated with the annotation name.
   // - the id and dateAdded of the old annotation, if it exists.
   nsCOMPtr<mozIStorageStatement> stmt;
   if (isItemAnnotation) {
     stmt = mDB->GetStatement(
       "SELECT b.id, "
              "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, "
-             "a.id, a.dateAdded, b.parent, b.type, b.lastModified, b.guid, p.guid "
+             "a.id, a.dateAdded "
       "FROM moz_bookmarks b "
-      "JOIN moz_bookmarks p ON p.id = b.parent "
       "LEFT JOIN moz_items_annos a ON a.item_id = b.id "
                                  "AND a.anno_attribute_id = nameid "
       "WHERE b.id = :item_id"
     );
   }
   else {
     stmt = mDB->GetStatement(
       "SELECT h.id, "
              "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, "
              "a.id, a.dateAdded "
       "FROM moz_places h "
       "LEFT JOIN moz_annos a ON a.place_id = h.id "
                            "AND a.anno_attribute_id = nameid "
       "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
     );
   }
-  if (!stmt) {
-    return Err(NS_ERROR_UNEXPECTED);
-  }
+  NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper checkAnnoScoper(stmt);
 
   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), aName);
-  NS_ENSURE_SUCCESS(rv, Err(rv));
-  if (isItemAnnotation) {
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (isItemAnnotation)
     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId);
-    NS_ENSURE_SUCCESS(rv, Err(rv));
-  }
-  else {
+  else
     rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
-    NS_ENSURE_SUCCESS(rv, Err(rv));
-  }
+  NS_ENSURE_SUCCESS(rv, rv);
 
   bool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
-  NS_ENSURE_SUCCESS(rv, Err(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
   if (!hasResult) {
     // We are trying to create an annotation on an invalid bookmark
     // or history entry.
-    return Err(NS_ERROR_INVALID_ARG);
+    return NS_ERROR_INVALID_ARG;
   }
 
   int64_t fkId = stmt->AsInt64(0);
   int64_t nameID = stmt->AsInt64(1);
   int64_t oldAnnoId = stmt->AsInt64(2);
   int64_t oldAnnoDate = stmt->AsInt64(3);
 
-  Maybe<BookmarkData> maybeBookmark;
   if (isItemAnnotation) {
     aStatement = mDB->GetStatement(
       "INSERT OR REPLACE INTO moz_items_annos "
         "(id, item_id, anno_attribute_id, content, flags, "
          "expiration, type, dateAdded, lastModified) "
       "VALUES (:id, :fk, :name_id, :content, :flags, "
       ":expiration, :type, :date_added, :last_modified)"
     );
-
-    // Since we're already querying `moz_bookmarks`, we fetch the changed
-    // bookmark's info here, instead of using `FetchItemInfo`.
-    BookmarkData bookmark;
-
-    bookmark.id = fkId;
-    bookmark.parentId = stmt->AsInt64(4);
-    bookmark.type = stmt->AsInt64(5);
-
-    bookmark.lastModified = static_cast<PRTime>(stmt->AsInt64(6));
-    if (NS_SUCCEEDED(stmt->GetUTF8String(7, bookmark.guid)) &&
-        NS_SUCCEEDED(stmt->GetUTF8String(8, bookmark.parentGuid))) {
-      maybeBookmark.emplace(bookmark);
-    }
   }
   else {
     aStatement = mDB->GetStatement(
       "INSERT OR REPLACE INTO moz_annos "
         "(id, place_id, anno_attribute_id, content, flags, "
          "expiration, type, dateAdded, lastModified) "
       "VALUES (:id, :fk, :name_id, :content, :flags, "
       ":expiration, :type, :date_added, :last_modified)"
     );
   }
-  NS_ENSURE_TRUE(aStatement, Err(NS_ERROR_UNEXPECTED));
+  NS_ENSURE_STATE(aStatement);
   mozStorageStatementScoper setAnnoScoper(aStatement);
 
   // Don't replace existing annotations.
   if (oldAnnoId > 0) {
     rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), oldAnnoId);
-    NS_ENSURE_SUCCESS(rv, Err(rv));
+    NS_ENSURE_SUCCESS(rv, rv);
     rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("date_added"), oldAnnoDate);
-    NS_ENSURE_SUCCESS(rv, Err(rv));
+    NS_ENSURE_SUCCESS(rv, rv);
   }
   else {
     rv = aStatement->BindNullByName(NS_LITERAL_CSTRING("id"));
-    NS_ENSURE_SUCCESS(rv, Err(rv));
+    NS_ENSURE_SUCCESS(rv, rv);
     rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("date_added"), RoundedPRNow());
-    NS_ENSURE_SUCCESS(rv, Err(rv));
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("fk"), fkId);
-  NS_ENSURE_SUCCESS(rv, Err(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
   rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("name_id"), nameID);
-  NS_ENSURE_SUCCESS(rv, Err(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   rv = aStatement->BindInt32ByName(NS_LITERAL_CSTRING("flags"), aFlags);
-  NS_ENSURE_SUCCESS(rv, Err(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
   rv = aStatement->BindInt32ByName(NS_LITERAL_CSTRING("expiration"), aExpiration);
-  NS_ENSURE_SUCCESS(rv, Err(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
   rv = aStatement->BindInt32ByName(NS_LITERAL_CSTRING("type"), aType);
-  NS_ENSURE_SUCCESS(rv, Err(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
   rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), RoundedPRNow());
-  NS_ENSURE_SUCCESS(rv, Err(rv));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // On success, leave the statement open, the caller will set the value
   // and execute the statement.
   setAnnoScoper.Abandon();
 
-  return maybeBookmark;
+  return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIObserver
 
 NS_IMETHODIMP
 nsAnnotationService::Observe(nsISupports *aSubject,
                              const char *aTopic,
--- a/toolkit/components/places/nsAnnotationService.h
+++ b/toolkit/components/places/nsAnnotationService.h
@@ -11,18 +11,16 @@
 #include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "nsServiceManagerUtils.h"
 #include "nsWeakReference.h"
 #include "nsToolkitCompsCID.h"
 #include "Database.h"
 #include "nsString.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/Maybe.h"
-#include "mozilla/Result.h"
 
 namespace mozilla {
 namespace places {
 
 class AnnotatedResult final : public mozIAnnotatedResult
 {
 public:
   NS_DECL_ISUPPORTS
@@ -108,63 +106,56 @@ protected:
                                  const nsACString& aName,
                                  bool* _hasAnno);
 
   nsresult StartGetAnnotation(nsIURI* aURI,
                               int64_t aItemId,
                               const nsACString& aName,
                               nsCOMPtr<mozIStorageStatement>& aStatement);
 
-  Result<Maybe<BookmarkData>, nsresult>
-  StartSetAnnotation(nsIURI* aURI,
-                     int64_t aItemId,
-                     const nsACString& aName,
-                     int32_t aFlags,
-                     uint16_t aExpiration,
-                     uint16_t aType,
-                     nsCOMPtr<mozIStorageStatement>& aStatement);
-
-  Result<Maybe<BookmarkData>, nsresult>
-  SetAnnotationStringInternal(nsIURI* aURI,
+  nsresult StartSetAnnotation(nsIURI* aURI,
                               int64_t aItemId,
                               const nsACString& aName,
-                              const nsAString& aValue,
                               int32_t aFlags,
-                              uint16_t aExpiration);
-  Result<Maybe<BookmarkData>, nsresult>
-  SetAnnotationInt32Internal(nsIURI* aURI,
-                             int64_t aItemId,
-                             const nsACString& aName,
-                             int32_t aValue,
-                             int32_t aFlags,
-                             uint16_t aExpiration);
-  Result<Maybe<BookmarkData>, nsresult>
-  SetAnnotationInt64Internal(nsIURI* aURI,
-                             int64_t aItemId,
-                             const nsACString& aName,
-                             int64_t aValue,
-                             int32_t aFlags,
-                             uint16_t aExpiration);
-  Result<Maybe<BookmarkData>, nsresult>
-  SetAnnotationDoubleInternal(nsIURI* aURI,
-                              int64_t aItemId,
-                              const nsACString& aName,
-                              double aValue,
-                              int32_t aFlags,
-                              uint16_t aExpiration);
+                              uint16_t aExpiration,
+                              uint16_t aType,
+                              nsCOMPtr<mozIStorageStatement>& aStatement);
 
-  Result<Maybe<BookmarkData>, nsresult>
-  RemoveAnnotationInternal(nsIURI* aURI,
-                           int64_t aItemId,
-                           const nsACString& aName);
+  nsresult SetAnnotationStringInternal(nsIURI* aURI,
+                                       int64_t aItemId,
+                                       const nsACString& aName,
+                                       const nsAString& aValue,
+                                       int32_t aFlags,
+                                       uint16_t aExpiration);
+  nsresult SetAnnotationInt32Internal(nsIURI* aURI,
+                                      int64_t aItemId,
+                                      const nsACString& aName,
+                                      int32_t aValue,
+                                      int32_t aFlags,
+                                      uint16_t aExpiration);
+  nsresult SetAnnotationInt64Internal(nsIURI* aURI,
+                                      int64_t aItemId,
+                                      const nsACString& aName,
+                                      int64_t aValue,
+                                      int32_t aFlags,
+                                      uint16_t aExpiration);
+  nsresult SetAnnotationDoubleInternal(nsIURI* aURI,
+                                       int64_t aItemId,
+                                       const nsACString& aName,
+                                       double aValue,
+                                       int32_t aFlags,
+                                       uint16_t aExpiration);
+
+  nsresult RemoveAnnotationInternal(nsIURI* aURI,
+                                    int64_t aItemId,
+                                    const nsACString& aName);
 
 public:
   nsresult GetPagesWithAnnotationCOMArray(const nsACString& aName,
                                           nsCOMArray<nsIURI>* _results);
   nsresult GetItemsWithAnnotationTArray(const nsACString& aName,
                                         nsTArray<int64_t>* _result);
   nsresult GetAnnotationNamesTArray(nsIURI* aURI,
                                     int64_t aItemId,
                                     nsTArray<nsCString>* _result);
-  nsresult RemoveItemAnnotationsWithoutNotifying(int64_t aItemId);
 };
 
 #endif /* nsAnnotationService_h___ */
--- a/toolkit/components/places/nsIAnnotationService.idl
+++ b/toolkit/components/places/nsIAnnotationService.idl
@@ -107,18 +107,17 @@ interface nsIAnnotationService : nsISupp
      * If there is an important annotation that the user or extension wants to
      * keep, you should add a bookmark for the page and use an EXPIRE_NEVER
      * annotation.  This will ensure the annotation exists until the item is
      * removed by the user.
      * See EXPIRE_* constants above for further information.
      *
      * For item annotations, aSource should be a change source constant from
      * nsINavBookmarksService::SOURCE_*, and defaults to SOURCE_DEFAULT if
-     * omitted. Setting an item annotation also notifies
-     * `nsINavBookmarkObserver::onItemChanged` for the affected item.
+     * omitted.
      *
      * The annotation "favicon" is special. Favicons are stored in the favicon
      * service, but are special cased in the protocol handler so they look like
      * annotations. Do not set favicons using this service, it will not work.
      *
      * Only C++ consumers may use the type-specific methods.
      *
      * @throws NS_ERROR_ILLEGAL_VALUE if the page or the bookmark doesn't exist.
@@ -336,33 +335,27 @@ interface nsIAnnotationService : nsISupp
     boolean pageHasAnnotation(in nsIURI aURI,
                               in AUTF8String aName);
     boolean itemHasAnnotation(in long long aItemId,
                               in AUTF8String aName);
 
     /**
      * Removes a specific annotation. Succeeds even if the annotation is
      * not found.
-     *
-     * Removing an item annotation also notifies
-     * `nsINavBookmarkObserver::onItemChanged` for the affected item.
      */
     void removePageAnnotation(in nsIURI aURI,
                               in AUTF8String aName);
     void removeItemAnnotation(in long long aItemId,
                               in AUTF8String aName,
                               [optional] in unsigned short aSource);
 
     /**
      * Removes all annotations for the given page/item.
      * We may want some other similar functions to get annotations with given
      * flags (once we have flags defined).
-     *
-     * Unlike the other item methods, `removeItemAnnotations` does *not* notify
-     * `nsINavBookmarkObserver::onItemChanged` for the affected item.
      */
     void removePageAnnotations(in nsIURI aURI);
     void removeItemAnnotations(in long long aItemId,
                                [optional] in unsigned short aSource);
 
     /**
      * Copies all annotations from the source to the destination URI/item. If
      * the destination already has an annotation with the same name as one on
--- a/toolkit/components/places/nsNavBookmarks.cpp
+++ b/toolkit/components/places/nsNavBookmarks.cpp
@@ -179,16 +179,17 @@ nsNavBookmarks::~nsNavBookmarks()
   if (gBookmarksService == this)
     gBookmarksService = nullptr;
 }
 
 
 NS_IMPL_ISUPPORTS(nsNavBookmarks
 , nsINavBookmarksService
 , nsINavHistoryObserver
+, nsIAnnotationObserver
 , nsIObserver
 , nsISupportsWeakReference
 )
 
 
 Atomic<int64_t> nsNavBookmarks::sLastInsertedItemId(0);
 
 
@@ -203,21 +204,27 @@ nsNavBookmarks::StoreLastInsertedId(cons
 nsresult
 nsNavBookmarks::Init()
 {
   mDB = Database::GetDatabase();
   NS_ENSURE_STATE(mDB);
 
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os) {
+    (void)os->AddObserver(this, TOPIC_PLACES_SHUTDOWN, true);
     (void)os->AddObserver(this, TOPIC_PLACES_CONNECTION_CLOSED, true);
   }
 
   mCanNotify = true;
 
+  // Observe annotations.
+  nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
+  NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
+  annosvc->AddObserver(this);
+
   // Allows us to notify on title changes. MUST BE LAST so it is impossible
   // to fail after this call, or the history service will have a reference to
   // us and we won't go away.
   nsNavHistory* history = nsNavHistory::GetHistoryService();
   NS_ENSURE_STATE(history);
   history->AddObserver(this, true);
 
   // DO NOT PUT STUFF HERE that can fail. See observer comment above.
@@ -675,25 +682,23 @@ nsNavBookmarks::RemoveItem(int64_t aItem
   NS_ENSURE_ARG(!IsRoot(aItemId));
 
   BookmarkData bookmark;
   nsresult rv = FetchItemInfo(aItemId, bookmark);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mozStorageTransaction transaction(mDB->MainConn(), false);
 
-  // First, if not a tag, remove item annotations. We remove annos without
-  // notifying to avoid firing `onItemAnnotationRemoved` for an item that
-  // we're about to remove.
+  // First, if not a tag, remove item annotations.
   int64_t tagsRootId = TagsRootId();
   bool isUntagging = bookmark.grandParentId == tagsRootId;
   if (bookmark.parentId != tagsRootId && !isUntagging) {
     nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
     NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
-    rv = annosvc->RemoveItemAnnotationsWithoutNotifying(bookmark.id);
+    rv = annosvc->RemoveItemAnnotations(bookmark.id, aSource);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (bookmark.type == TYPE_FOLDER) {
     // Remove all of the folder's children.
     rv = RemoveFolderChildren(bookmark.id, aSource);
     NS_ENSURE_SUCCESS(rv, rv);
   }
@@ -3082,50 +3087,52 @@ nsNavBookmarks::NotifyItemVisited(const 
   }
 }
 
 void
 nsNavBookmarks::NotifyItemChanged(const ItemChangeData& aData)
 {
   // A guid must always be defined.
   MOZ_ASSERT(!aData.bookmark.guid.IsEmpty());
-
-  PRTime lastModified = aData.bookmark.lastModified;
-  if (aData.updateLastModified) {
-    lastModified = RoundedPRNow();
-    MOZ_ALWAYS_SUCCEEDS(SetItemDateInternal(
-      LAST_MODIFIED, DetermineSyncChangeDelta(aData.source),
-      aData.bookmark.id, lastModified));
-  }
-
   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
                    nsINavBookmarkObserver,
                    OnItemChanged(aData.bookmark.id,
                                  aData.property,
                                  aData.isAnnotation,
                                  aData.newValue,
-                                 lastModified,
+                                 aData.bookmark.lastModified,
                                  aData.bookmark.type,
                                  aData.bookmark.parentId,
                                  aData.bookmark.guid,
                                  aData.bookmark.parentGuid,
                                  aData.oldValue,
-                                 aData.source));
+                                 // We specify the default source here because
+                                 // this method is only called for history
+                                 // visits, and we don't track sources in
+                                 // history.
+                                 SOURCE_DEFAULT));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIObserver
 
 NS_IMETHODIMP
 nsNavBookmarks::Observe(nsISupports *aSubject, const char *aTopic,
                         const char16_t *aData)
 {
   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
 
-  if (strcmp(aTopic, TOPIC_PLACES_CONNECTION_CLOSED) == 0) {
+  if (strcmp(aTopic, TOPIC_PLACES_SHUTDOWN) == 0) {
+    // Stop Observing annotations.
+    nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
+    if (annosvc) {
+      annosvc->RemoveObserver(this);
+    }
+  }
+  else if (strcmp(aTopic, TOPIC_PLACES_CONNECTION_CLOSED) == 0) {
     // Don't even try to notify observers from this point on, the category
     // cache would init services that could try to use our APIs.
     mCanNotify = false;
     mObservers.Clear();
   }
 
   return NS_OK;
 }
@@ -3294,8 +3301,68 @@ nsNavBookmarks::OnDeleteVisits(nsIURI* a
     changeData.bookmark.type = TYPE_BOOKMARK;
 
     RefPtr< AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData> > notifier =
       new AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData>(this, &nsNavBookmarks::NotifyItemChanged, changeData);
     notifier->Init();
   }
   return NS_OK;
 }
+
+
+// nsIAnnotationObserver
+
+NS_IMETHODIMP
+nsNavBookmarks::OnPageAnnotationSet(nsIURI* aPage, const nsACString& aName)
+{
+  return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsNavBookmarks::OnItemAnnotationSet(int64_t aItemId, const nsACString& aName,
+                                    uint16_t aSource, bool aDontUpdateLastModified)
+{
+  BookmarkData bookmark;
+  nsresult rv = FetchItemInfo(aItemId, bookmark);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!aDontUpdateLastModified) {
+    bookmark.lastModified = RoundedPRNow();
+    rv = SetItemDateInternal(LAST_MODIFIED, DetermineSyncChangeDelta(aSource),
+                             bookmark.id, bookmark.lastModified);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
+                   nsINavBookmarkObserver,
+                   OnItemChanged(bookmark.id,
+                                 aName,
+                                 true,
+                                 EmptyCString(),
+                                 bookmark.lastModified,
+                                 bookmark.type,
+                                 bookmark.parentId,
+                                 bookmark.guid,
+                                 bookmark.parentGuid,
+                                 EmptyCString(),
+                                 aSource));
+  return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsNavBookmarks::OnPageAnnotationRemoved(nsIURI* aPage, const nsACString& aName)
+{
+  return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsNavBookmarks::OnItemAnnotationRemoved(int64_t aItemId, const nsACString& aName,
+                                        uint16_t aSource)
+{
+  // As of now this is doing the same as OnItemAnnotationSet, so just forward
+  // the call.
+  nsresult rv = OnItemAnnotationSet(aItemId, aName, aSource, false);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return NS_OK;
+}
--- a/toolkit/components/places/nsNavBookmarks.h
+++ b/toolkit/components/places/nsNavBookmarks.h
@@ -2,16 +2,17 @@
 /* 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/. */
 
 #ifndef nsNavBookmarks_h_
 #define nsNavBookmarks_h_
 
 #include "nsINavBookmarksService.h"
+#include "nsIAnnotationService.h"
 #include "nsITransaction.h"
 #include "nsNavHistory.h"
 #include "nsToolkitCompsCID.h"
 #include "nsCategoryCache.h"
 #include "nsTHashtable.h"
 #include "nsWeakReference.h"
 #include "mozilla/Attributes.h"
 #include "prtime.h"
@@ -22,45 +23,43 @@ namespace mozilla {
 namespace places {
 
   enum BookmarkStatementId {
     DB_FIND_REDIRECTED_BOOKMARK = 0
   , DB_GET_BOOKMARKS_FOR_URI
   };
 
   struct BookmarkData {
-    int64_t id = -1;
+    int64_t id;
     nsCString url;
     nsCString title;
-    int32_t position = -1;
-    int64_t placeId = -1;
-    int64_t parentId = -1;
-    int64_t grandParentId = -1;
-    int32_t type = 0;
-    int32_t syncStatus = nsINavBookmarksService::SYNC_STATUS_UNKNOWN;
+    int32_t position;
+    int64_t placeId;
+    int64_t parentId;
+    int64_t grandParentId;
+    int32_t type;
+    int32_t syncStatus;
     nsCString serviceCID;
-    PRTime dateAdded = 0;
-    PRTime lastModified = 0;
+    PRTime dateAdded;
+    PRTime lastModified;
     nsCString guid;
     nsCString parentGuid;
   };
 
   struct ItemVisitData {
     BookmarkData bookmark;
     int64_t visitId;
     uint32_t transitionType;
     PRTime time;
   };
 
   struct ItemChangeData {
     BookmarkData bookmark;
-    bool isAnnotation = false;
-    bool updateLastModified = false;
-    uint16_t source = nsINavBookmarksService::SOURCE_DEFAULT;
     nsCString property;
+    bool isAnnotation;
     nsCString newValue;
     nsCString oldValue;
   };
 
   struct TombstoneData {
     nsCString guid;
     PRTime dateRemoved;
   };
@@ -73,23 +72,25 @@ namespace places {
   , LAST_MODIFIED
   };
 
 } // namespace places
 } // namespace mozilla
 
 class nsNavBookmarks final : public nsINavBookmarksService
                            , public nsINavHistoryObserver
+                           , public nsIAnnotationObserver
                            , public nsIObserver
                            , public nsSupportsWeakReference
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSINAVBOOKMARKSSERVICE
   NS_DECL_NSINAVHISTORYOBSERVER
+  NS_DECL_NSIANNOTATIONOBSERVER
   NS_DECL_NSIOBSERVER
 
   nsNavBookmarks();
 
   /**
    * Obtains the service's object.
    */
   static already_AddRefed<nsNavBookmarks> GetSingleton();
--- a/toolkit/components/places/tests/bookmarks/test_bookmarks_notifications.js
+++ b/toolkit/components/places/tests/bookmarks/test_bookmarks_notifications.js
@@ -226,16 +226,18 @@ add_task(async function remove_bookmark(
   let bm = await PlacesUtils.bookmarks.insert({ type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
                                                 parentGuid: PlacesUtils.bookmarks.unfiledGuid,
                                                 url: new URL("http://remove.example.com/") });
   let itemId = await PlacesUtils.promiseItemId(bm.guid);
   let parentId = await PlacesUtils.promiseItemId(bm.parentGuid);
 
   let observer = expectNotifications();
   await PlacesUtils.bookmarks.remove(bm.guid);
+  // TODO (Bug 653910): onItemAnnotationRemoved notified even if there were no
+  // annotations.
   observer.check([ { name: "onItemRemoved",
                      arguments: [ itemId, parentId, bm.index, bm.type, bm.url,
                                   bm.guid, bm.parentGuid,
                                   Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
                  ]);
 });
 
 add_task(async function remove_multiple_bookmarks() {
@@ -247,16 +249,18 @@ add_task(async function remove_multiple_
                                                  url: "http://remove1.example.com/" });
   let itemId1 = await PlacesUtils.promiseItemId(bm1.guid);
   let parentId1 = await PlacesUtils.promiseItemId(bm1.parentGuid);
   let itemId2 = await PlacesUtils.promiseItemId(bm2.guid);
   let parentId2 = await PlacesUtils.promiseItemId(bm2.parentGuid);
 
   let observer = expectNotifications();
   await PlacesUtils.bookmarks.remove([bm1, bm2]);
+  // TODO (Bug 653910): onItemAnnotationRemoved notified even if there were no
+  // annotations.
   observer.check([ { name: "onItemRemoved",
                      arguments: [ itemId1, parentId1, bm1.index, bm1.type, bm1.url,
                                   bm1.guid, bm1.parentGuid,
                                   Ci.nsINavBookmarksService.SOURCE_DEFAULT ] },
                    { name: "onItemRemoved",
                      arguments: [ itemId2, parentId2, bm2.index, bm2.type, bm2.url,
                                   bm2.guid, bm2.parentGuid,
                                   Ci.nsINavBookmarksService.SOURCE_DEFAULT ] }
--- a/toolkit/components/places/tests/bookmarks/test_nsINavBookmarkObserver.js
+++ b/toolkit/components/places/tests/bookmarks/test_nsINavBookmarkObserver.js
@@ -206,17 +206,17 @@ add_task(async function onItemAdded_fold
 add_task(async function onItemChanged_title_bookmark() {
   let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.unfiledBookmarksFolderId, 0);
   const TITLE = "New title";
   let promise = Promise.all([
     gBookmarkSkipObserver.setup([
       "onItemChanged"
     ]),
     gBookmarksObserver.setup([
-      { name: "onItemChanged",
+      { name: "onItemChanged", // This is an unfortunate effect of bug 653910.
         args: [
           { name: "itemId", check: v => typeof(v) == "number" && v > 0 },
           { name: "property", check: v => v === "title" },
           { name: "isAnno", check: v => v === false },
           { name: "newValue", check: v => v === TITLE },
           { name: "lastModified", check: v => typeof(v) == "number" && v > 0 },
           { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK },
           { name: "parentId", check: v => v === PlacesUtils.unfiledBookmarksFolderId },
@@ -388,19 +388,33 @@ add_task(async function onItemMoved_book
 
 add_task(async function onItemRemoved_bookmark() {
   let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.unfiledBookmarksFolderId, 0);
   let guid = await PlacesUtils.promiseItemGuid(id);
   let url = (await PlacesUtils.bookmarks.fetch(guid)).url;
   let uri = Services.io.newURI(url);
   let promise = Promise.all([
     gBookmarkSkipObserver.setup([
-      "onItemRemoved"
+      "onItemChanged", "onItemRemoved"
     ]),
     gBookmarksObserver.setup([
+      { name: "onItemChanged", // This is an unfortunate effect of bug 653910.
+        args: [
+          { name: "itemId", check: v => typeof(v) == "number" && v > 0 },
+          { name: "property", check: v => v === "" },
+          { name: "isAnno", check: v => v === true },
+          { name: "newValue", check: v => v === "" },
+          { name: "lastModified", check: v => typeof(v) == "number" && v > 0 },
+          { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK },
+          { name: "parentId", check: v => v === PlacesUtils.unfiledBookmarksFolderId },
+          { name: "guid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
+          { name: "parentGuid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
+          { name: "oldValue", check: v => typeof(v) == "string" },
+          { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
+        ] },
       { name: "onItemRemoved",
         args: [
           { name: "itemId", check: v => typeof(v) == "number" && v > 0 },
           { name: "parentId", check: v => v === PlacesUtils.unfiledBookmarksFolderId },
           { name: "index", check: v => v === 0 },
           { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK },
           { name: "uri", check: v => v instanceof Ci.nsIURI && v.equals(uri) },
           { name: "guid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
@@ -411,19 +425,33 @@ add_task(async function onItemRemoved_bo
   PlacesUtils.bookmarks.removeItem(id);
   await promise;
 });
 
 add_task(async function onItemRemoved_separator() {
   let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.unfiledBookmarksFolderId, 0);
   let promise = Promise.all([
     gBookmarkSkipObserver.setup([
-      "onItemRemoved"
+      "onItemChanged", "onItemRemoved"
     ]),
     gBookmarksObserver.setup([
+      { name: "onItemChanged", // This is an unfortunate effect of bug 653910.
+        args: [
+          { name: "itemId", check: v => typeof(v) == "number" && v > 0 },
+          { name: "property", check: v => v === "" },
+          { name: "isAnno", check: v => v === true },
+          { name: "newValue", check: v => v === "" },
+          { name: "lastModified", check: v => typeof(v) == "number" && v > 0 },
+          { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_SEPARATOR },
+          { name: "parentId", check: v => typeof(v) == "number" && v > 0 },
+          { name: "guid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
+          { name: "parentGuid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
+          { name: "oldValue", check: v => typeof(v) == "string" },
+          { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
+        ] },
       { name: "onItemRemoved",
         args: [
           { name: "itemId", check: v => typeof(v) == "number" && v > 0 },
           { name: "parentId", check: v => typeof(v) == "number" && v > 0 },
           { name: "index", check: v => v === 0 },
           { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_SEPARATOR },
           { name: "uri", check: v => v === null },
           { name: "guid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
@@ -434,19 +462,33 @@ add_task(async function onItemRemoved_se
   PlacesUtils.bookmarks.removeItem(id);
   await promise;
 });
 
 add_task(async function onItemRemoved_folder() {
   let id = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.unfiledBookmarksFolderId, 0);
   let promise = Promise.all([
     gBookmarkSkipObserver.setup([
-      "onItemRemoved"
+      "onItemChanged", "onItemRemoved"
     ]),
     gBookmarksObserver.setup([
+      { name: "onItemChanged", // This is an unfortunate effect of bug 653910.
+        args: [
+          { name: "itemId", check: v => typeof(v) == "number" && v > 0 },
+          { name: "property", check: v => v === "" },
+          { name: "isAnno", check: v => v === true },
+          { name: "newValue", check: v => v === "" },
+          { name: "lastModified", check: v => typeof(v) == "number" && v > 0 },
+          { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_FOLDER },
+          { name: "parentId", check: v => typeof(v) == "number" && v > 0 },
+          { name: "guid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
+          { name: "parentGuid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
+          { name: "oldValue", check: v => typeof(v) == "string" },
+          { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
+        ] },
       { name: "onItemRemoved",
         args: [
           { name: "itemId", check: v => typeof(v) == "number" && v > 0 },
           { name: "parentId", check: v => typeof(v) == "number" && v > 0 },
           { name: "index", check: v => v === 0 },
           { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_FOLDER },
           { name: "uri", check: v => v === null },
           { name: "guid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
@@ -460,17 +502,17 @@ add_task(async function onItemRemoved_fo
 
 add_task(async function onItemRemoved_folder_recursive() {
   const TITLE = "Folder 3";
   const BMTITLE = "Bookmark 1";
   let uri = NetUtil.newURI("http://1.mozilla.org/");
   let promise = Promise.all([
     gBookmarkSkipObserver.setup([
       "onItemAdded", "onItemAdded", "onItemAdded", "onItemAdded",
-      "onItemRemoved"
+      "onItemChanged", "onItemRemoved"
     ]),
     gBookmarksObserver.setup([
       { name: "onItemAdded",
         args: [
           { name: "itemId", check: v => typeof(v) == "number" && v > 0 },
           { name: "parentId", check: v => v === PlacesUtils.unfiledBookmarksFolderId },
           { name: "index", check: v => v === 0 },
           { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_FOLDER },
@@ -515,16 +557,30 @@ add_task(async function onItemRemoved_fo
           { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK },
           { name: "uri", check: v => v instanceof Ci.nsIURI && v.equals(uri) },
           { name: "title", check: v => v === BMTITLE },
           { name: "dateAdded", check: v => typeof(v) == "number" && v > 0 },
           { name: "guid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
           { name: "parentGuid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
           { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
         ] },
+      { name: "onItemChanged", // This is an unfortunate effect of bug 653910.
+        args: [
+          { name: "itemId", check: v => typeof(v) == "number" && v > 0 },
+          { name: "property", check: v => v === "" },
+          { name: "isAnno", check: v => v === true },
+          { name: "newValue", check: v => v === "" },
+          { name: "lastModified", check: v => typeof(v) == "number" && v > 0 },
+          { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_FOLDER },
+          { name: "parentId", check: v => typeof(v) == "number" && v > 0 },
+          { name: "guid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
+          { name: "parentGuid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
+          { name: "oldValue", check: v => typeof(v) == "string" },
+          { name: "source", check: v => Object.values(PlacesUtils.bookmarks.SOURCES).includes(v) },
+        ] },
       { name: "onItemRemoved",
         args: [
           { name: "itemId", check: v => typeof(v) == "number" && v > 0 },
           { name: "parentId", check: v => typeof(v) == "number" && v > 0 },
           { name: "index", check: v => v === 0 },
           { name: "itemType", check: v => v === PlacesUtils.bookmarks.TYPE_BOOKMARK },
           { name: "uri", check: v => v instanceof Ci.nsIURI && v.equals(uri) },
           { name: "guid", check: v => typeof(v) == "string" && GUID_RE.test(v) },
--- a/toolkit/components/places/tests/unit/test_null_interfaces.js
+++ b/toolkit/components/places/tests/unit/test_null_interfaces.js
@@ -9,18 +9,19 @@
 // Make an array of services to test, each specifying a class id, interface
 // and an array of function names that don't throw when passed nulls
 var testServices = [
   ["browser/nav-history-service;1",
     ["nsINavHistoryService"],
     ["queryStringToQueries", "removePagesByTimeframe", "removePagesFromHost", "getObservers"]
   ],
   ["browser/nav-bookmarks-service;1",
-    ["nsINavBookmarksService", "nsINavHistoryObserver"],
-    ["createFolder", "getObservers", "onFrecencyChanged", "onTitleChanged", "onDeleteURI"]
+    ["nsINavBookmarksService", "nsINavHistoryObserver", "nsIAnnotationObserver"],
+    ["createFolder", "getObservers", "onFrecencyChanged", "onTitleChanged",
+     "onPageAnnotationSet", "onPageAnnotationRemoved", "onDeleteURI"]
   ],
   ["browser/livemark-service;2", ["mozIAsyncLivemarks"], ["reloadLivemarks"]],
   ["browser/annotation-service;1", ["nsIAnnotationService"], ["getObservers"]],
   ["browser/favicon-service;1", ["nsIFaviconService"], []],
   ["browser/tagging-service;1", ["nsITaggingService"], []],
 ];
 do_print(testServices.join("\n"));