Bug 1353783 - updatePlaces doesn't correctly notify and count embed visits. r=gijs draft
authorMarco Bonardo <mbonardo@mozilla.com>
Thu, 06 Apr 2017 14:24:25 +0200
changeset 557747 04821b56abb0b8a2fa27c26ec62f41302c30a243
parent 556622 ec8d1d3db50c85037e8077c32c8403570a5df493
child 623141 fbc142c294ff8bfe70b11bf1e6a49a5a7e771e45
push id52811
push usermak77@bonardo.net
push dateFri, 07 Apr 2017 08:26:01 +0000
reviewersgijs
bugs1353783
milestone55.0a1
Bug 1353783 - updatePlaces doesn't correctly notify and count embed visits. r=gijs Additionally, move some history tests to the history folder, split insertMany tests into their own test file. Also, remove some no more needed android annotations, Firefox for Android doesn't use nor build Places anymore. MozReview-Commit-ID: 6p4mazeUjsw
toolkit/components/places/History.cpp
toolkit/components/places/tests/expiration/xpcshell.ini
toolkit/components/places/tests/favicons/xpcshell.ini
toolkit/components/places/tests/history/test_async_history_api.js
toolkit/components/places/tests/history/test_insert.js
toolkit/components/places/tests/history/test_insertMany.js
toolkit/components/places/tests/history/test_sameUri_titleChanged.js
toolkit/components/places/tests/history/test_updatePlaces_embed.js
toolkit/components/places/tests/history/test_updatePlaces_sameUri_titleChanged.js
toolkit/components/places/tests/history/xpcshell.ini
toolkit/components/places/tests/queries/xpcshell.ini
toolkit/components/places/tests/unit/test_async_history_api.js
toolkit/components/places/tests/unit/xpcshell.ini
--- a/toolkit/components/places/History.cpp
+++ b/toolkit/components/places/History.cpp
@@ -904,17 +904,18 @@ public:
    *        The callback to notify about the visit.
    * @param [optional] aGroupNotifications
    *        Whether to group any observer notifications rather than
    *        sending them out individually.
    */
   static nsresult Start(mozIStorageConnection* aConnection,
                         nsTArray<VisitData>& aPlaces,
                         mozIVisitInfoCallback* aCallback = nullptr,
-                        bool aGroupNotifications = false)
+                        bool aGroupNotifications = false,
+                        uint32_t aInitialUpdatedCount = 0)
   {
     MOZ_ASSERT(NS_IsMainThread(), "This should be called on the main thread");
     MOZ_ASSERT(aPlaces.Length() > 0, "Must pass a non-empty array!");
 
     // Make sure nsNavHistory service is up before proceeding:
     nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
     MOZ_ASSERT(navHistory, "Could not get nsNavHistory?!");
     if (!navHistory) {
@@ -928,17 +929,17 @@ public:
       // We ignore errors from either of these methods in case old JS consumers
       // don't implement them (in which case they will get error/result
       // notifications as normal).
       Unused << aCallback->GetIgnoreErrors(&ignoreErrors);
       Unused << aCallback->GetIgnoreResults(&ignoreResults);
     }
     RefPtr<InsertVisitedURIs> event =
       new InsertVisitedURIs(aConnection, aPlaces, callback, aGroupNotifications,
-                            ignoreErrors, ignoreResults);
+                            ignoreErrors, ignoreResults, aInitialUpdatedCount);
 
     // Get the target thread, and then start the work!
     nsCOMPtr<nsIEventTarget> target = do_GetInterface(aConnection);
     NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
     nsresult rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
@@ -1061,23 +1062,24 @@ public:
     return NS_OK;
   }
 private:
   InsertVisitedURIs(mozIStorageConnection* aConnection,
                     nsTArray<VisitData>& aPlaces,
                     const nsMainThreadPtrHandle<mozIVisitInfoCallback>& aCallback,
                     bool aGroupNotifications,
                     bool aIgnoreErrors,
-                    bool aIgnoreResults)
+                    bool aIgnoreResults,
+                    uint32_t aInitialUpdatedCount)
   : mDBConn(aConnection)
   , mCallback(aCallback)
   , mGroupNotifications(aGroupNotifications)
   , mIgnoreErrors(aIgnoreErrors)
   , mIgnoreResults(aIgnoreResults)
-  , mSuccessfulUpdatedCount(0)
+  , mSuccessfulUpdatedCount(aInitialUpdatedCount)
   , mHistory(History::GetService())
   {
     MOZ_ASSERT(NS_IsMainThread(), "This should be called on the main thread");
 
     mPlaces.SwapElements(aPlaces);
 
 #ifdef DEBUG
     for (nsTArray<VisitData>::size_type i = 0; i < mPlaces.Length(); i++) {
@@ -1943,19 +1945,23 @@ StoreAndNotifyEmbedVisit(VisitData& aPla
     return;
   }
 
   navHistory->registerEmbedVisit(uri, aPlace.visitTime);
 
   if (!!aCallback) {
     nsMainThreadPtrHandle<mozIVisitInfoCallback>
       callback(new nsMainThreadPtrHolder<mozIVisitInfoCallback>(aCallback));
-    nsCOMPtr<nsIRunnable> event =
-      new NotifyPlaceInfoCallback(callback, aPlace, true, NS_OK);
-    (void)NS_DispatchToMainThread(event);
+    bool ignoreResults = false;
+    Unused << aCallback->GetIgnoreResults(&ignoreResults);
+    if (!ignoreResults) {
+      nsCOMPtr<nsIRunnable> event =
+        new NotifyPlaceInfoCallback(callback, aPlace, true, NS_OK);
+      (void)NS_DispatchToMainThread(event);
+    }
   }
 
   nsCOMPtr<nsIRunnable> event = new NotifyVisitObservers(aPlace);
   (void)NS_DispatchToMainThread(event);
 }
 
 } // namespace
 
@@ -2891,16 +2897,18 @@ History::UpdatePlaces(JS::Handle<JS::Val
   NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED);
   NS_ENSURE_TRUE(!aPlaceInfos.isPrimitive(), NS_ERROR_INVALID_ARG);
 
   uint32_t infosLength;
   JS::Rooted<JSObject*> infos(aCtx);
   nsresult rv = GetJSArrayFromJSValue(aPlaceInfos, aCtx, &infos, &infosLength);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  uint32_t initialUpdatedCount = 0;
+
   nsTArray<VisitData> visitData;
   for (uint32_t i = 0; i < infosLength; i++) {
     JS::Rooted<JSObject*> info(aCtx);
     nsresult rv = GetJSObjectFromArray(aCtx, infos, i, &info);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIURI> uri = GetURIFromJSObject(aCtx, info, "uri");
     nsCString guid;
@@ -2978,16 +2986,17 @@ History::UpdatePlaces(JS::Handle<JS::Val
       data.SetTransitionType(transitionType);
       data.hidden = GetHiddenState(false, transitionType);
 
       // If the visit is an embed visit, we do not actually add it to the
       // database.
       if (transitionType == nsINavHistoryService::TRANSITION_EMBED) {
         StoreAndNotifyEmbedVisit(data, aCallback);
         visitData.RemoveElementAt(visitData.Length() - 1);
+        initialUpdatedCount++;
         continue;
       }
 
       // The referrer is optional.
       nsCOMPtr<nsIURI> referrer = GetURIFromJSObject(aCtx, visit,
                                                      "referrerURI");
       if (referrer) {
         (void)referrer->GetSpec(data.referrerSpec);
@@ -3001,30 +3010,32 @@ History::UpdatePlaces(JS::Handle<JS::Val
   nsMainThreadPtrHandle<mozIVisitInfoCallback>
     callback(new nsMainThreadPtrHolder<mozIVisitInfoCallback>(aCallback));
 
   // It is possible that all of the visits we were passed were dissallowed by
   // CanAddURI, which isn't an error.  If we have no visits to add, however,
   // we should not call InsertVisitedURIs::Start.
   if (visitData.Length()) {
     nsresult rv = InsertVisitedURIs::Start(dbConn, visitData,
-                                           callback, aGroupNotifications);
+                                           callback, aGroupNotifications,
+                                           initialUpdatedCount);
     NS_ENSURE_SUCCESS(rv, rv);
   } else if (aCallback) {
     // Be sure to notify that all of our operations are complete.  This
     // is dispatched to the background thread first and redirected to the
     // main thread from there to make sure that all database notifications
     // and all embed or canAddURI notifications have finished.
 
     // Note: if we're inserting anything, it's the responsibility of
     // InsertVisitedURIs to call the completion callback, as here we won't
     // know how yet many items we will successfully insert/update.
     nsCOMPtr<nsIEventTarget> backgroundThread = do_GetInterface(dbConn);
     NS_ENSURE_TRUE(backgroundThread, NS_ERROR_UNEXPECTED);
-    nsCOMPtr<nsIRunnable> event = new NotifyCompletion(callback);
+    nsCOMPtr<nsIRunnable> event = new NotifyCompletion(callback,
+                                                       initialUpdatedCount);
     return backgroundThread->Dispatch(event, NS_DISPATCH_NORMAL);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 History::IsURIVisited(nsIURI* aURI,
--- a/toolkit/components/places/tests/expiration/xpcshell.ini
+++ b/toolkit/components/places/tests/expiration/xpcshell.ini
@@ -1,15 +1,13 @@
 [DEFAULT]
 head = head_expiration.js
 skip-if = toolkit == 'android'
 
 [test_analyze_runs.js]
-# Bug 676989: test hangs consistently on Android
-skip-if = os == "android"
 [test_annos_expire_history.js]
 [test_annos_expire_never.js]
 [test_annos_expire_policy.js]
 [test_annos_expire_session.js]
 [test_clearHistory.js]
 [test_debug_expiration.js]
 [test_idle_daily.js]
 [test_notifications.js]
--- a/toolkit/components/places/tests/favicons/xpcshell.ini
+++ b/toolkit/components/places/tests/favicons/xpcshell.ini
@@ -15,17 +15,15 @@ support-files =
   favicon-big64.png
   favicon-normal16.png
   favicon-normal32.png
   favicon-scale160x3.jpg
   favicon-scale3x160.jpg
 
 [test_expireAllFavicons.js]
 [test_favicons_conversions.js]
-# Bug 676989: test fails consistently on Android
-fail-if = os == "android"
 [test_getFaviconDataForPage.js]
 [test_getFaviconURLForPage.js]
 [test_moz-anno_favicon_mime_type.js]
 [test_page-icon_protocol.js]
 [test_query_result_favicon_changed_on_child.js]
 [test_replaceFaviconData.js]
 [test_replaceFaviconDataFromDataURL.js]
rename from toolkit/components/places/tests/unit/test_async_history_api.js
rename to toolkit/components/places/tests/history/test_async_history_api.js
--- a/toolkit/components/places/tests/history/test_insert.js
+++ b/toolkit/components/places/tests/history/test_insert.js
@@ -1,12 +1,12 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
 
-// Tests for `History.insert` and `History.insertMany`, as implemented in History.jsm
+// Tests for `History.insert` as implemented in History.jsm
 
 "use strict";
 
 add_task(function* test_insert_error_cases() {
   const TEST_URL = "http://mozilla.com";
 
   Assert.throws(
     () => PlacesUtils.history.insert(),
@@ -144,114 +144,8 @@ add_task(function* test_history_insert()
           yield inserter("Testing History.insert() with a URL object", x => new URL(x.spec), referrer, date, transition);
         }
       }
     }
   } finally {
     yield PlacesTestUtils.clearHistory();
   }
 });
-
-add_task(function* test_insert_multiple_error_cases() {
-  let validPageInfo = {
-    url: "http://mozilla.com",
-    visits: [
-      {transition: TRANSITION_LINK}
-    ]
-  };
-
-  Assert.throws(
-    () => PlacesUtils.history.insertMany(),
-    /TypeError: pageInfos must be an array/,
-    "passing a null into History.insertMany should throw a TypeError"
-  );
-  Assert.throws(
-    () => PlacesUtils.history.insertMany([]),
-    /TypeError: pageInfos may not be an empty array/,
-    "passing an empty array into History.insertMany should throw a TypeError"
-  );
-  Assert.throws(
-    () => PlacesUtils.history.insertMany([validPageInfo, {}]),
-    /TypeError: PageInfo object must have a url property/,
-    "passing a second invalid PageInfo object to History.insertMany should throw a TypeError"
-  );
-});
-
-add_task(function* test_history_insertMany() {
-  const BAD_URLS = ["about:config", "chrome://browser/content/browser.xul"];
-  const GOOD_URLS = [1, 2, 3].map(x => { return `http://mozilla.com/${x}`; });
-
-  let makePageInfos = Task.async(function*(urls, filter = x => x) {
-    let pageInfos = [];
-    for (let url of urls) {
-      let uri = NetUtil.newURI(url);
-
-      let pageInfo = {
-        title: `Visit to ${url}`,
-        visits: [
-          {transition: TRANSITION_LINK}
-        ]
-      };
-
-      pageInfo.url = yield filter(uri);
-      pageInfos.push(pageInfo);
-    }
-    return pageInfos;
-  });
-
-  let inserter = Task.async(function*(name, filter, useCallbacks) {
-    do_print(name);
-    do_print(`filter: ${filter}`);
-    do_print(`useCallbacks: ${useCallbacks}`);
-    yield PlacesTestUtils.clearHistory();
-
-    let result;
-    let allUrls = GOOD_URLS.concat(BAD_URLS);
-    let pageInfos = yield makePageInfos(allUrls, filter);
-
-    if (useCallbacks) {
-      let onResultUrls = [];
-      let onErrorUrls = [];
-      result = yield PlacesUtils.history.insertMany(pageInfos, pageInfo => {
-        let url = pageInfo.url.href;
-        Assert.ok(GOOD_URLS.includes(url), "onResult callback called for correct url");
-        onResultUrls.push(url);
-        Assert.equal(`Visit to ${url}`, pageInfo.title, "onResult callback provides the correct title");
-        Assert.ok(PlacesUtils.isValidGuid(pageInfo.guid), "onResult callback provides a valid guid");
-      }, pageInfo => {
-        let url = pageInfo.url.href;
-        Assert.ok(BAD_URLS.includes(url), "onError callback called for correct uri");
-        onErrorUrls.push(url);
-        Assert.equal(undefined, pageInfo.title, "onError callback provides the correct title");
-        Assert.equal(undefined, pageInfo.guid, "onError callback provides the expected guid");
-      });
-      Assert.equal(GOOD_URLS.sort().toString(), onResultUrls.sort().toString(), "onResult callback was called for each good url");
-      Assert.equal(BAD_URLS.sort().toString(), onErrorUrls.sort().toString(), "onError callback was called for each bad url");
-    } else {
-      result = yield PlacesUtils.history.insertMany(pageInfos);
-    }
-
-    Assert.equal(undefined, result, "insertMany returned undefined");
-
-    for (let url of allUrls) {
-      let expected = GOOD_URLS.includes(url);
-      Assert.equal(expected, yield PlacesTestUtils.isPageInDB(url), `isPageInDB for ${url} is ${expected}`);
-      Assert.equal(expected, yield PlacesTestUtils.visitsInDB(url), `visitsInDB for ${url} is ${expected}`);
-    }
-  });
-
-  try {
-    for (let useCallbacks of [false, true]) {
-      yield inserter("Testing History.insertMany() with an nsIURI", x => x, useCallbacks);
-      yield inserter("Testing History.insertMany() with a string url", x => x.spec, useCallbacks);
-      yield inserter("Testing History.insertMany() with a URL object", x => new URL(x.spec), useCallbacks);
-    }
-    // Test rejection when no items added
-    let pageInfos = yield makePageInfos(BAD_URLS);
-    PlacesUtils.history.insertMany(pageInfos).then(() => {
-      Assert.ok(false, "History.insertMany rejected promise with all bad URLs");
-    }, error => {
-      Assert.equal("No items were added to history.", error.message, "History.insertMany rejected promise with all bad URLs");
-    });
-  } finally {
-    yield PlacesTestUtils.clearHistory();
-  }
-});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/history/test_insertMany.js
@@ -0,0 +1,130 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests for `History.insertMany` as implemented in History.jsm
+
+"use strict";
+
+add_task(function* test_error_cases() {
+  let validPageInfo = {
+    url: "http://mozilla.com",
+    visits: [
+      {transition: TRANSITION_LINK}
+    ]
+  };
+
+  Assert.throws(
+    () => PlacesUtils.history.insertMany(),
+    /TypeError: pageInfos must be an array/,
+    "passing a null into History.insertMany should throw a TypeError"
+  );
+  Assert.throws(
+    () => PlacesUtils.history.insertMany([]),
+    /TypeError: pageInfos may not be an empty array/,
+    "passing an empty array into History.insertMany should throw a TypeError"
+  );
+  Assert.throws(
+    () => PlacesUtils.history.insertMany([validPageInfo, {}]),
+    /TypeError: PageInfo object must have a url property/,
+    "passing a second invalid PageInfo object to History.insertMany should throw a TypeError"
+  );
+});
+
+add_task(function* test_insertMany() {
+  const BAD_URLS = ["about:config", "chrome://browser/content/browser.xul"];
+  const GOOD_URLS = [1, 2, 3].map(x => { return `http://mozilla.com/${x}`; });
+
+  let makePageInfos = Task.async(function*(urls, filter = x => x) {
+    let pageInfos = [];
+    for (let url of urls) {
+      let uri = NetUtil.newURI(url);
+
+      let pageInfo = {
+        title: `Visit to ${url}`,
+        visits: [
+          {transition: TRANSITION_LINK}
+        ]
+      };
+
+      pageInfo.url = yield filter(uri);
+      pageInfos.push(pageInfo);
+    }
+    return pageInfos;
+  });
+
+  let inserter = Task.async(function*(name, filter, useCallbacks) {
+    do_print(name);
+    do_print(`filter: ${filter}`);
+    do_print(`useCallbacks: ${useCallbacks}`);
+    yield PlacesTestUtils.clearHistory();
+
+    let result;
+    let allUrls = GOOD_URLS.concat(BAD_URLS);
+    let pageInfos = yield makePageInfos(allUrls, filter);
+
+    if (useCallbacks) {
+      let onResultUrls = [];
+      let onErrorUrls = [];
+      result = yield PlacesUtils.history.insertMany(pageInfos, pageInfo => {
+        let url = pageInfo.url.href;
+        Assert.ok(GOOD_URLS.includes(url), "onResult callback called for correct url");
+        onResultUrls.push(url);
+        Assert.equal(`Visit to ${url}`, pageInfo.title, "onResult callback provides the correct title");
+        Assert.ok(PlacesUtils.isValidGuid(pageInfo.guid), "onResult callback provides a valid guid");
+      }, pageInfo => {
+        let url = pageInfo.url.href;
+        Assert.ok(BAD_URLS.includes(url), "onError callback called for correct uri");
+        onErrorUrls.push(url);
+        Assert.equal(undefined, pageInfo.title, "onError callback provides the correct title");
+        Assert.equal(undefined, pageInfo.guid, "onError callback provides the expected guid");
+      });
+      Assert.equal(GOOD_URLS.sort().toString(), onResultUrls.sort().toString(), "onResult callback was called for each good url");
+      Assert.equal(BAD_URLS.sort().toString(), onErrorUrls.sort().toString(), "onError callback was called for each bad url");
+    } else {
+      result = yield PlacesUtils.history.insertMany(pageInfos);
+    }
+
+    Assert.equal(undefined, result, "insertMany returned undefined");
+
+    for (let url of allUrls) {
+      let expected = GOOD_URLS.includes(url);
+      Assert.equal(expected, yield PlacesTestUtils.isPageInDB(url), `isPageInDB for ${url} is ${expected}`);
+      Assert.equal(expected, yield PlacesTestUtils.visitsInDB(url), `visitsInDB for ${url} is ${expected}`);
+    }
+  });
+
+  try {
+    for (let useCallbacks of [false, true]) {
+      yield inserter("Testing History.insertMany() with an nsIURI", x => x, useCallbacks);
+      yield inserter("Testing History.insertMany() with a string url", x => x.spec, useCallbacks);
+      yield inserter("Testing History.insertMany() with a URL object", x => new URL(x.spec), useCallbacks);
+    }
+    // Test rejection when no items added
+    let pageInfos = yield makePageInfos(BAD_URLS);
+    PlacesUtils.history.insertMany(pageInfos).then(() => {
+      Assert.ok(false, "History.insertMany rejected promise with all bad URLs");
+    }, error => {
+      Assert.equal("No items were added to history.", error.message, "History.insertMany rejected promise with all bad URLs");
+    });
+  } finally {
+    yield PlacesTestUtils.clearHistory();
+  }
+});
+
+add_task(function* test_transitions() {
+  const places = Object.keys(PlacesUtils.history.TRANSITIONS).map(transition => {
+    return { url: `http://places.test/${transition}`,
+             visits: [
+               { transition: PlacesUtils.history.TRANSITIONS[transition] }
+             ]
+           };
+  });
+  // Should not reject.
+  yield PlacesUtils.history.insertMany(places);
+  // Check callbacks.
+  let count = 0;
+  yield PlacesUtils.history.insertMany(places, pageInfo => {
+    ++count;
+  });
+  Assert.equal(count, Object.keys(PlacesUtils.history.TRANSITIONS).length);
+});
rename from toolkit/components/places/tests/history/test_updatePlaces_sameUri_titleChanged.js
rename to toolkit/components/places/tests/history/test_sameUri_titleChanged.js
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/history/test_updatePlaces_embed.js
@@ -0,0 +1,68 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that updatePlaces properly handled callbacks for embed visits.
+
+"use strict"
+
+add_task(function* test_embed_visit() {
+  let place = {
+    uri: NetUtil.newURI("http://places.test/"),
+    visits: [
+      { transitionType: PlacesUtils.history.TRANSITIONS.EMBED,
+        visitDate: PlacesUtils.toPRTime(new Date()) }
+    ],
+  };
+  let errors = 0;
+  let results = 0;
+  let updated = yield new Promise(resolve => {
+    PlacesUtils.asyncHistory.updatePlaces(place, {
+      ignoreErrors: true,
+      ignoreResults: true,
+      handleError(aResultCode, aPlace) {
+        errors++;
+      },
+      handleResult(aPlace) {
+        results++;
+      },
+      handleCompletion(resultCount) {
+        resolve(resultCount);
+      }
+    });
+  });
+  Assert.equal(errors, 0, "There should be no error callback");
+  Assert.equal(results, 0, "There should be no result callback");
+  Assert.equal(updated, 1, "The visit should have been added");
+});
+
+add_task(function* test_misc_visits() {
+  let place = {
+    uri: NetUtil.newURI("http://places.test/"),
+    visits: [
+      { transitionType: PlacesUtils.history.TRANSITIONS.EMBED,
+        visitDate: PlacesUtils.toPRTime(new Date()) },
+      { transitionType: PlacesUtils.history.TRANSITIONS.LINK,
+        visitDate: PlacesUtils.toPRTime(new Date()) }
+    ],
+  };
+  let errors = 0;
+  let results = 0;
+  let updated = yield new Promise(resolve => {
+    PlacesUtils.asyncHistory.updatePlaces(place, {
+      ignoreErrors: true,
+      ignoreResults: true,
+      handleError(aResultCode, aPlace) {
+        errors++;
+      },
+      handleResult(aPlace) {
+        results++;
+      },
+      handleCompletion(resultCount) {
+        resolve(resultCount);
+      }
+    });
+  });
+  Assert.equal(errors, 0, "There should be no error callback");
+  Assert.equal(results, 0, "There should be no result callback");
+  Assert.equal(updated, 2, "The visit should have been added");
+});
--- a/toolkit/components/places/tests/history/xpcshell.ini
+++ b/toolkit/components/places/tests/history/xpcshell.ini
@@ -1,8 +1,11 @@
 [DEFAULT]
 head = head_history.js
 
+[test_async_history_api.js]
 [test_insert.js]
+[test_insertMany.js]
 [test_remove.js]
 [test_removeVisits.js]
 [test_removeVisitsByFilter.js]
-[test_updatePlaces_sameUri_titleChanged.js]
+[test_sameUri_titleChanged.js]
+[test_updatePlaces_embed.js]
--- a/toolkit/components/places/tests/queries/xpcshell.ini
+++ b/toolkit/components/places/tests/queries/xpcshell.ini
@@ -8,26 +8,18 @@ skip-if = toolkit == 'android'
 [test_async.js]
 [test_containersQueries_sorting.js]
 [test_history_queries_tags_liveUpdate.js]
 [test_history_queries_titles_liveUpdate.js]
 [test_onlyBookmarked.js]
 [test_queryMultipleFolder.js]
 [test_querySerialization.js]
 [test_redirects.js]
-# Bug 676989: test hangs consistently on Android
-skip-if = os == "android"
 [test_results-as-tag-contents-query.js]
 [test_results-as-visit.js]
 [test_searchterms-domain.js]
 [test_searchterms-uri.js]
 [test_searchterms-bookmarklets.js]
 [test_sort-date-site-grouping.js]
 [test_sorting.js]
-# Bug 676989: test hangs consistently on Android
-skip-if = os == "android" 
 [test_tags.js]
-# Bug 676989: test hangs consistently on Android
-skip-if = os == "android"
 [test_transitions.js]
-# Bug 676989: test hangs consistently on Android
-skip-if = os == "android"
 [test_searchTerms_includeHidden.js]
--- a/toolkit/components/places/tests/unit/xpcshell.ini
+++ b/toolkit/components/places/tests/unit/xpcshell.ini
@@ -18,36 +18,28 @@ support-files =
   nsDummyObserver.js
   nsDummyObserver.manifest
   places.sparse.sqlite
   sync_utils_bookmarks.html
   sync_utils_bookmarks.json
 
 [test_000_frecency.js]
 [test_317472.js]
-# Bug 676989: test hangs consistently on Android
-skip-if = os == "android"
 [test_331487.js]
 [test_384370.js]
 [test_385397.js]
-# Bug 676989: test fails consistently on Android
-fail-if = os == "android"
 [test_399264_query_to_string.js]
 [test_399264_string_to_query.js]
 [test_399266.js]
-# Bug 676989: test fails consistently on Android
-fail-if = os == "android"
 # Bug 821781: test fails intermittently on Linux
 skip-if = os == "linux"
 [test_402799.js]
 [test_405497.js]
 [test_408221.js]
 [test_412132.js]
-# Bug 676989: test hangs consistently on Android
-skip-if = os == "android"
 [test_413784.js]
 [test_415460.js]
 [test_415757.js]
 [test_418643_removeFolderChildren.js]
 [test_419731.js]
 [test_419792_node_tags_property.js]
 [test_425563.js]
 [test_429505_remove_shortcuts.js]
@@ -57,26 +49,20 @@ skip-if = os == "android"
 [test_454977.js]
 [test_463863.js]
 [test_485442_crash_bug_nsNavHistoryQuery_GetUri.js]
 [test_486978_sort_by_date_queries.js]
 [test_536081.js]
 [test_1085291.js]
 [test_1105208.js]
 [test_1105866.js]
-[test_adaptive.js]
-# Bug 676989: test hangs consistently on Android
-skip-if = os == "android"
 [test_adaptive_bug527311.js]
 [test_analyze.js]
 [test_annotations.js]
 [test_asyncExecuteLegacyQueries.js]
-# Bug 676989: test hangs consistently on Android
-skip-if = os == "android"
-[test_async_history_api.js]
 [test_async_in_batchmode.js]
 [test_async_transactions.js]
 skip-if = (os == "win" && os_version == "5.1") # Bug 1158887
 [test_autocomplete_stopSearch_no_throw.js]
 [test_bookmark_catobs.js]
 [test_bookmarks_json.js]
 [test_bookmarks_html.js]
 [test_bookmarks_html_corrupt.js]
@@ -86,78 +72,62 @@ skip-if = (os == "win" && os_version == 
 [test_bookmarks_setNullTitle.js]
 [test_broken_folderShortcut_result.js]
 [test_browserhistory.js]
 [test_bug636917_isLivemark.js]
 [test_childlessTags.js]
 [test_crash_476292.js]
 [test_database_replaceOnStartup.js]
 [test_download_history.js]
-# Bug 676989: test fails consistently on Android
-fail-if = os == "android"
 [test_frecency.js]
 [test_frecency_decay.js]
 [test_frecency_zero_updated.js]
-# Bug 676989: test hangs consistently on Android
-skip-if = os == "android"
 [test_getChildIndex.js]
 [test_getPlacesInfo.js]
 [test_history.js]
 [test_history_autocomplete_tags.js]
 [test_history_catobs.js]
 [test_history_clear.js]
 [test_history_notifications.js]
 [test_history_observer.js]
-# Bug 676989: test hangs consistently on Android
-skip-if = os == "android"
 [test_history_sidebar.js]
 [test_hosts_triggers.js]
 [test_import_mobile_bookmarks.js]
 [test_isPageInDB.js]
 [test_isURIVisited.js]
 [test_isvisited.js]
 [test_keywords.js]
 [test_lastModified.js]
 [test_markpageas.js]
 [test_mozIAsyncLivemarks.js]
 [test_multi_queries.js]
-# Bug 676989: test fails consistently on Android
-fail-if = os == "android"
 [test_multi_word_tags.js]
 [test_nsINavHistoryViewer.js]
 # Bug 902248: intermittent timeouts on all platforms
 skip-if = true
 [test_null_interfaces.js]
 [test_onItemChanged_tags.js]
 [test_pageGuid_bookmarkGuid.js]
 [test_frecency_observers.js]
 [test_placeURIs.js]
 [test_PlacesSearchAutocompleteProvider.js]
 [test_PlacesUtils_asyncGetBookmarkIds.js]
 [test_PlacesUtils_invalidateCachedGuidFor.js]
 [test_PlacesUtils_lazyobservers.js]
 [test_placesTxn.js]
 [test_preventive_maintenance.js]
-# Bug 676989: test hangs consistently on Android
-skip-if = os == "android"
 [test_preventive_maintenance_checkAndFixDatabase.js]
-# Bug 676989: test hangs consistently on Android
-skip-if = os == "android"
 [test_preventive_maintenance_runTasks.js]
 [test_promiseBookmarksTree.js]
-# Bug 676989: test hangs consistently on Android
-skip-if = os == "android"
 [test_resolveNullBookmarkTitles.js]
 [test_result_sort.js]
 [test_resultsAsVisit_details.js]
 [test_sql_guid_functions.js]
 [test_svg_favicon.js]
 [test_sync_utils.js]
 [test_tag_autocomplete_search.js]
 [test_tagging.js]
 [test_telemetry.js]
 [test_update_frecency_after_delete.js]
-# Bug 676989: test hangs consistently on Android
-skip-if = os == "android"
 [test_utils_backups_create.js]
 [test_utils_getURLsForContainerNode.js]
 [test_utils_setAnnotationsFor.js]
 [test_visitsInDB.js]