tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_bookmark_tracker.js (38832B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 const { Service } = ChromeUtils.importESModule(
      5  "resource://services-sync/service.sys.mjs"
      6 );
      7 const { PlacesTransactions } = ChromeUtils.importESModule(
      8  "resource://gre/modules/PlacesTransactions.sys.mjs"
      9 );
     10 
     11 let engine;
     12 let store;
     13 let tracker;
     14 
     15 const DAY_IN_MS = 24 * 60 * 60 * 1000;
     16 
     17 add_task(async function setup() {
     18  await Service.engineManager.switchAlternatives();
     19  engine = Service.engineManager.get("bookmarks");
     20  store = engine._store;
     21  tracker = engine._tracker;
     22 });
     23 
     24 // Test helpers.
     25 async function verifyTrackerEmpty() {
     26  await PlacesTestUtils.promiseAsyncUpdates();
     27  let changes = await tracker.getChangedIDs();
     28  deepEqual(changes, {});
     29  equal(tracker.score, 0);
     30 }
     31 
     32 async function resetTracker() {
     33  await PlacesTestUtils.markBookmarksAsSynced();
     34  tracker.resetScore();
     35 }
     36 
     37 async function cleanup() {
     38  await engine.setLastSync(0);
     39  await store.wipe();
     40  await resetTracker();
     41  await tracker.stop();
     42 }
     43 
     44 // startTracking is a signal that the test wants to notice things that happen
     45 // after this is called (ie, things already tracked should be discarded.)
     46 async function startTracking() {
     47  engine._tracker.start();
     48  await PlacesTestUtils.markBookmarksAsSynced();
     49 }
     50 
     51 async function verifyTrackedItems(tracked) {
     52  await PlacesTestUtils.promiseAsyncUpdates();
     53  let changedIDs = await tracker.getChangedIDs();
     54  let trackedIDs = new Set(Object.keys(changedIDs));
     55  for (let guid of tracked) {
     56    ok(guid in changedIDs, `${guid} should be tracked`);
     57    Assert.greater(
     58      changedIDs[guid].modified,
     59      0,
     60      `${guid} should have a modified time`
     61    );
     62    Assert.greaterOrEqual(
     63      changedIDs[guid].counter,
     64      -1,
     65      `${guid} should have a change counter`
     66    );
     67    trackedIDs.delete(guid);
     68  }
     69  equal(
     70    trackedIDs.size,
     71    0,
     72    `Unhandled tracked IDs: ${JSON.stringify(Array.from(trackedIDs))}`
     73  );
     74 }
     75 
     76 async function verifyTrackedCount(expected) {
     77  await PlacesTestUtils.promiseAsyncUpdates();
     78  let changedIDs = await tracker.getChangedIDs();
     79  do_check_attribute_count(changedIDs, expected);
     80 }
     81 
     82 // A debugging helper that dumps the full bookmarks tree.
     83 // Currently unused, but might come in handy
     84 // eslint-disable-next-line no-unused-vars
     85 async function dumpBookmarks() {
     86  let columns = [
     87    "id",
     88    "title",
     89    "guid",
     90    "syncStatus",
     91    "syncChangeCounter",
     92    "position",
     93  ];
     94  return PlacesUtils.promiseDBConnection().then(connection => {
     95    let all = [];
     96    return connection
     97      .executeCached(
     98        `SELECT ${columns.join(", ")} FROM moz_bookmarks;`,
     99        {},
    100        row => {
    101          let repr = {};
    102          for (let column of columns) {
    103            repr[column] = row.getResultByName(column);
    104          }
    105          all.push(repr);
    106        }
    107      )
    108      .then(() => {
    109        dump("All bookmarks:\n");
    110        dump(JSON.stringify(all, undefined, 2));
    111      });
    112  });
    113 }
    114 
    115 add_task(async function test_tracking() {
    116  _("Test starting and stopping the tracker");
    117 
    118  // Remove existing tracking information for roots.
    119  await startTracking();
    120 
    121  let folder = await PlacesUtils.bookmarks.insert({
    122    parentGuid: PlacesUtils.bookmarks.menuGuid,
    123    title: "Test Folder",
    124    type: PlacesUtils.bookmarks.TYPE_FOLDER,
    125  });
    126 
    127  // creating the folder should have made 2 changes - the folder itself and
    128  // the parent of the folder.
    129  await verifyTrackedCount(2);
    130  // Reset the changes as the rest of the test doesn't want to see these.
    131  await resetTracker();
    132 
    133  function createBmk() {
    134    return PlacesUtils.bookmarks.insert({
    135      parentGuid: folder.guid,
    136      url: "http://getfirefox.com",
    137      title: "Get Firefox!",
    138    });
    139  }
    140 
    141  try {
    142    _("Tell the tracker to start tracking changes.");
    143    await startTracking();
    144    await createBmk();
    145    // We expect two changed items because the containing folder
    146    // changed as well (new child).
    147    await verifyTrackedCount(2);
    148    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE);
    149 
    150    _("Notifying twice won't do any harm.");
    151    await createBmk();
    152    await verifyTrackedCount(3);
    153    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 2);
    154  } finally {
    155    _("Clean up.");
    156    await cleanup();
    157  }
    158 });
    159 
    160 add_task(async function test_tracker_sql_batching() {
    161  _(
    162    "Test tracker does the correct thing when it is forced to batch SQL queries"
    163  );
    164 
    165  const SQLITE_MAX_VARIABLE_NUMBER = 999;
    166  let numItems = SQLITE_MAX_VARIABLE_NUMBER * 2 + 10;
    167 
    168  await startTracking();
    169 
    170  let children = [];
    171  for (let i = 0; i < numItems; i++) {
    172    children.push({
    173      url: "https://example.org/" + i,
    174      title: "Sync Bookmark " + i,
    175    });
    176  }
    177  let inserted = await PlacesUtils.bookmarks.insertTree({
    178    guid: PlacesUtils.bookmarks.unfiledGuid,
    179    children: [
    180      {
    181        type: PlacesUtils.bookmarks.TYPE_FOLDER,
    182        children,
    183      },
    184    ],
    185  });
    186 
    187  Assert.equal(children.length, numItems);
    188  Assert.equal(inserted.length, numItems + 1);
    189  await verifyTrackedCount(numItems + 2); // The parent and grandparent are also tracked.
    190  await resetTracker();
    191 
    192  await PlacesUtils.bookmarks.remove(inserted[0]);
    193  await verifyTrackedCount(numItems + 2);
    194 
    195  await cleanup();
    196 });
    197 
    198 add_task(async function test_bookmarkAdded() {
    199  _("Items inserted via the synchronous bookmarks API should be tracked");
    200 
    201  try {
    202    await startTracking();
    203 
    204    _("Insert a folder using the sync API");
    205    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    206    let syncFolder = await PlacesUtils.bookmarks.insert({
    207      parentGuid: PlacesUtils.bookmarks.menuGuid,
    208      title: "Sync Folder",
    209      type: PlacesUtils.bookmarks.TYPE_FOLDER,
    210    });
    211    await verifyTrackedItems(["menu", syncFolder.guid]);
    212    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE);
    213    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 2);
    214 
    215    await resetTracker();
    216    await startTracking();
    217 
    218    _("Insert a bookmark using the sync API");
    219    totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    220    let syncBmk = await PlacesUtils.bookmarks.insert({
    221      parentGuid: syncFolder.guid,
    222      url: "https://example.org/sync",
    223      title: "Sync Bookmark",
    224    });
    225    await verifyTrackedItems([syncFolder.guid, syncBmk.guid]);
    226    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE);
    227    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 2);
    228  } finally {
    229    _("Clean up.");
    230    await cleanup();
    231  }
    232 });
    233 
    234 add_task(async function test_async_bookmarkAdded() {
    235  _("Items inserted via the asynchronous bookmarks API should be tracked");
    236 
    237  try {
    238    await startTracking();
    239 
    240    _("Insert a folder using the async API");
    241    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    242    let asyncFolder = await PlacesUtils.bookmarks.insert({
    243      type: PlacesUtils.bookmarks.TYPE_FOLDER,
    244      parentGuid: PlacesUtils.bookmarks.menuGuid,
    245      title: "Async Folder",
    246    });
    247    await verifyTrackedItems(["menu", asyncFolder.guid]);
    248    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE);
    249    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 2);
    250 
    251    await resetTracker();
    252    await startTracking();
    253 
    254    _("Insert a bookmark using the async API");
    255    totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    256    let asyncBmk = await PlacesUtils.bookmarks.insert({
    257      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    258      parentGuid: asyncFolder.guid,
    259      url: "https://example.org/async",
    260      title: "Async Bookmark",
    261    });
    262    await verifyTrackedItems([asyncFolder.guid, asyncBmk.guid]);
    263    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE);
    264    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 2);
    265 
    266    await resetTracker();
    267    await startTracking();
    268 
    269    _("Insert a separator using the async API");
    270    totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    271    let asyncSep = await PlacesUtils.bookmarks.insert({
    272      type: PlacesUtils.bookmarks.TYPE_SEPARATOR,
    273      parentGuid: PlacesUtils.bookmarks.menuGuid,
    274      index: asyncFolder.index,
    275    });
    276    await verifyTrackedItems(["menu", asyncSep.guid]);
    277    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE);
    278    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 2);
    279  } finally {
    280    _("Clean up.");
    281    await cleanup();
    282  }
    283 });
    284 
    285 add_task(async function test_async_onItemChanged() {
    286  _("Items updated using the asynchronous bookmarks API should be tracked");
    287 
    288  try {
    289    await tracker.stop();
    290 
    291    _("Insert a bookmark");
    292    let fxBmk = await PlacesUtils.bookmarks.insert({
    293      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    294      parentGuid: PlacesUtils.bookmarks.menuGuid,
    295      url: "http://getfirefox.com",
    296      title: "Get Firefox!",
    297    });
    298    _(`Firefox GUID: ${fxBmk.guid}`);
    299 
    300    await startTracking();
    301 
    302    _("Update the bookmark using the async API");
    303    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    304    await PlacesUtils.bookmarks.update({
    305      guid: fxBmk.guid,
    306      title: "Download Firefox",
    307      url: "https://www.mozilla.org/firefox",
    308      // PlacesUtils.bookmarks.update rejects last modified dates older than
    309      // the added date.
    310      lastModified: new Date(Date.now() + DAY_IN_MS),
    311    });
    312 
    313    await verifyTrackedItems([fxBmk.guid]);
    314    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 3);
    315    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 1);
    316  } finally {
    317    _("Clean up.");
    318    await cleanup();
    319  }
    320 });
    321 
    322 add_task(async function test_onItemChanged_itemDates() {
    323  _("Changes to item dates should be tracked");
    324 
    325  try {
    326    await tracker.stop();
    327 
    328    _("Insert a bookmark");
    329    let fx_bm = await PlacesUtils.bookmarks.insert({
    330      parentGuid: PlacesUtils.bookmarks.menuGuid,
    331      url: "http://getfirefox.com",
    332      title: "Get Firefox!",
    333    });
    334    _(`Firefox GUID: ${fx_bm.guid}`);
    335 
    336    await startTracking();
    337 
    338    _("Reset the bookmark's added date, should not be tracked");
    339    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    340    let dateAdded = new Date(Date.now() - DAY_IN_MS);
    341    await PlacesUtils.bookmarks.update({
    342      guid: fx_bm.guid,
    343      dateAdded,
    344    });
    345    await verifyTrackedCount(0);
    346    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE);
    347    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges);
    348 
    349    await resetTracker();
    350 
    351    _(
    352      "Reset the bookmark's added date and another property, should be tracked"
    353    );
    354    totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    355    dateAdded = new Date();
    356    await PlacesUtils.bookmarks.update({
    357      guid: fx_bm.guid,
    358      dateAdded,
    359      title: "test",
    360    });
    361    await verifyTrackedItems([fx_bm.guid]);
    362    Assert.equal(tracker.score, 2 * SCORE_INCREMENT_XLARGE);
    363    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 1);
    364 
    365    await resetTracker();
    366 
    367    _("Set the bookmark's last modified date");
    368    totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    369    let fx_id = await PlacesTestUtils.promiseItemId(fx_bm.guid);
    370    let dateModified = Date.now() * 1000;
    371    PlacesUtils.bookmarks.setItemLastModified(fx_id, dateModified);
    372    await verifyTrackedItems([fx_bm.guid]);
    373    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE);
    374    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 1);
    375  } finally {
    376    _("Clean up.");
    377    await cleanup();
    378  }
    379 });
    380 
    381 add_task(async function test_onItemTagged() {
    382  _("Items tagged using the synchronous API should be tracked");
    383 
    384  try {
    385    await tracker.stop();
    386 
    387    _("Create a folder");
    388    let folder = await PlacesUtils.bookmarks.insert({
    389      parentGuid: PlacesUtils.bookmarks.menuGuid,
    390      title: "Parent",
    391      type: PlacesUtils.bookmarks.TYPE_FOLDER,
    392    });
    393    _("Folder ID: " + folder);
    394    _("Folder GUID: " + folder.guid);
    395 
    396    _("Track changes to tags");
    397    let uri = CommonUtils.makeURI("http://getfirefox.com");
    398    let b = await PlacesUtils.bookmarks.insert({
    399      parentGuid: folder.guid,
    400      url: uri,
    401      title: "Get Firefox!",
    402    });
    403    _("New item is " + b);
    404    _("GUID: " + b.guid);
    405 
    406    await startTracking();
    407 
    408    _("Tag the item");
    409    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    410    PlacesUtils.tagging.tagURI(uri, ["foo"]);
    411 
    412    // bookmark should be tracked, folder should not be.
    413    await verifyTrackedItems([b.guid]);
    414    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 3);
    415    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 6);
    416  } finally {
    417    _("Clean up.");
    418    await cleanup();
    419  }
    420 });
    421 
    422 add_task(async function test_onItemUntagged() {
    423  _("Items untagged using the synchronous API should be tracked");
    424 
    425  try {
    426    await tracker.stop();
    427 
    428    _("Insert tagged bookmarks");
    429    let uri = CommonUtils.makeURI("http://getfirefox.com");
    430    let fx1 = await PlacesUtils.bookmarks.insert({
    431      parentGuid: PlacesUtils.bookmarks.menuGuid,
    432      url: uri,
    433      title: "Get Firefox!",
    434    });
    435    // Different parent and title; same URL.
    436    let fx2 = await PlacesUtils.bookmarks.insert({
    437      parentGuid: PlacesUtils.bookmarks.toolbarGuid,
    438      url: uri,
    439      title: "Download Firefox",
    440    });
    441    PlacesUtils.tagging.tagURI(uri, ["foo"]);
    442 
    443    await startTracking();
    444 
    445    _("Remove the tag");
    446    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    447    PlacesUtils.tagging.untagURI(uri, ["foo"]);
    448 
    449    await verifyTrackedItems([fx1.guid, fx2.guid]);
    450    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 4);
    451    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 5);
    452  } finally {
    453    _("Clean up.");
    454    await cleanup();
    455  }
    456 });
    457 
    458 add_task(async function test_async_onItemUntagged() {
    459  _("Items untagged using the asynchronous API should be tracked");
    460 
    461  try {
    462    await tracker.stop();
    463 
    464    _("Insert tagged bookmarks");
    465    let fxBmk1 = await PlacesUtils.bookmarks.insert({
    466      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    467      parentGuid: PlacesUtils.bookmarks.menuGuid,
    468      url: "http://getfirefox.com",
    469      title: "Get Firefox!",
    470    });
    471    let fxBmk2 = await PlacesUtils.bookmarks.insert({
    472      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    473      parentGuid: PlacesUtils.bookmarks.toolbarGuid,
    474      url: "http://getfirefox.com",
    475      title: "Download Firefox",
    476    });
    477    let tag = await PlacesUtils.bookmarks.insert({
    478      type: PlacesUtils.bookmarks.TYPE_FOLDER,
    479      parentGuid: PlacesUtils.bookmarks.tagsGuid,
    480      title: "some tag",
    481    });
    482    let fxTag = await PlacesUtils.bookmarks.insert({
    483      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    484      parentGuid: tag.guid,
    485      url: "http://getfirefox.com",
    486    });
    487 
    488    await startTracking();
    489 
    490    _("Remove the tag using the async bookmarks API");
    491    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    492    await PlacesUtils.bookmarks.remove(fxTag.guid);
    493 
    494    await verifyTrackedItems([fxBmk1.guid, fxBmk2.guid]);
    495    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 4);
    496    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 5);
    497  } finally {
    498    _("Clean up.");
    499    await cleanup();
    500  }
    501 });
    502 
    503 add_task(async function test_async_onItemTagged() {
    504  _("Items tagged using the asynchronous API should be tracked");
    505 
    506  try {
    507    await tracker.stop();
    508 
    509    _("Insert untagged bookmarks");
    510    let folder1 = await PlacesUtils.bookmarks.insert({
    511      type: PlacesUtils.bookmarks.TYPE_FOLDER,
    512      parentGuid: PlacesUtils.bookmarks.menuGuid,
    513      title: "Folder 1",
    514    });
    515    let fxBmk1 = await PlacesUtils.bookmarks.insert({
    516      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    517      parentGuid: folder1.guid,
    518      url: "http://getfirefox.com",
    519      title: "Get Firefox!",
    520    });
    521    let folder2 = await PlacesUtils.bookmarks.insert({
    522      type: PlacesUtils.bookmarks.TYPE_FOLDER,
    523      parentGuid: PlacesUtils.bookmarks.menuGuid,
    524      title: "Folder 2",
    525    });
    526    // Different parent and title; same URL.
    527    let fxBmk2 = await PlacesUtils.bookmarks.insert({
    528      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    529      parentGuid: folder2.guid,
    530      url: "http://getfirefox.com",
    531      title: "Download Firefox",
    532    });
    533 
    534    await startTracking();
    535 
    536    // This will change once tags are moved into a separate table (bug 424160).
    537    // We specifically test this case because Bookmarks.sys.mjs updates tagged
    538    // bookmarks and notifies observers.
    539    _("Insert a tag using the async bookmarks API");
    540    let tag = await PlacesUtils.bookmarks.insert({
    541      type: PlacesUtils.bookmarks.TYPE_FOLDER,
    542      parentGuid: PlacesUtils.bookmarks.tagsGuid,
    543      title: "some tag",
    544    });
    545 
    546    _("Tag an item using the async bookmarks API");
    547    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    548    await PlacesUtils.bookmarks.insert({
    549      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    550      parentGuid: tag.guid,
    551      url: "http://getfirefox.com",
    552    });
    553 
    554    await verifyTrackedItems([fxBmk1.guid, fxBmk2.guid]);
    555    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 4);
    556    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 5);
    557  } finally {
    558    _("Clean up.");
    559    await cleanup();
    560  }
    561 });
    562 
    563 add_task(async function test_async_onItemKeywordChanged() {
    564  _("Keyword changes via the asynchronous API should be tracked");
    565 
    566  try {
    567    await tracker.stop();
    568 
    569    _("Insert two bookmarks with the same URL");
    570    let fxBmk1 = await PlacesUtils.bookmarks.insert({
    571      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    572      parentGuid: PlacesUtils.bookmarks.menuGuid,
    573      url: "http://getfirefox.com",
    574      title: "Get Firefox!",
    575    });
    576    let fxBmk2 = await PlacesUtils.bookmarks.insert({
    577      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    578      parentGuid: PlacesUtils.bookmarks.toolbarGuid,
    579      url: "http://getfirefox.com",
    580      title: "Download Firefox",
    581    });
    582 
    583    await startTracking();
    584 
    585    _("Add a keyword for both items");
    586    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    587    await PlacesUtils.keywords.insert({
    588      keyword: "the_keyword",
    589      url: "http://getfirefox.com",
    590      postData: "postData",
    591    });
    592 
    593    await verifyTrackedItems([fxBmk1.guid, fxBmk2.guid]);
    594    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 2);
    595    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 2);
    596  } finally {
    597    _("Clean up.");
    598    await cleanup();
    599  }
    600 });
    601 
    602 add_task(async function test_async_onItemKeywordDeleted() {
    603  _("Keyword deletions via the asynchronous API should be tracked");
    604 
    605  try {
    606    await tracker.stop();
    607 
    608    _("Insert two bookmarks with the same URL and keywords");
    609    let fxBmk1 = await PlacesUtils.bookmarks.insert({
    610      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    611      parentGuid: PlacesUtils.bookmarks.menuGuid,
    612      url: "http://getfirefox.com",
    613      title: "Get Firefox!",
    614    });
    615    let fxBmk2 = await PlacesUtils.bookmarks.insert({
    616      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    617      parentGuid: PlacesUtils.bookmarks.toolbarGuid,
    618      url: "http://getfirefox.com",
    619      title: "Download Firefox",
    620    });
    621    await PlacesUtils.keywords.insert({
    622      keyword: "the_keyword",
    623      url: "http://getfirefox.com",
    624    });
    625 
    626    await startTracking();
    627 
    628    _("Remove the keyword");
    629    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    630    await PlacesUtils.keywords.remove("the_keyword");
    631 
    632    await verifyTrackedItems([fxBmk1.guid, fxBmk2.guid]);
    633    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 2);
    634    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 2);
    635  } finally {
    636    _("Clean up.");
    637    await cleanup();
    638  }
    639 });
    640 
    641 add_task(async function test_bookmarkAdded_filtered_root() {
    642  _("Items outside the change roots should not be tracked");
    643 
    644  try {
    645    await startTracking();
    646 
    647    _("Create a new root");
    648    let root = await PlacesUtils.bookmarks.insert({
    649      parentGuid: PlacesUtils.bookmarks.rootGuid,
    650      title: "New root",
    651      type: PlacesUtils.bookmarks.TYPE_FOLDER,
    652    });
    653    _(`New root GUID: ${root.guid}`);
    654 
    655    _("Insert a bookmark underneath the new root");
    656    let untrackedBmk = await PlacesUtils.bookmarks.insert({
    657      parentGuid: root.guid,
    658      url: "http://getthunderbird.com",
    659      title: "Get Thunderbird!",
    660    });
    661    _(`New untracked bookmark GUID: ${untrackedBmk.guid}`);
    662 
    663    _("Insert a bookmark underneath the Places root");
    664    let rootBmk = await PlacesUtils.bookmarks.insert({
    665      parentGuid: PlacesUtils.bookmarks.rootGuid,
    666      url: "http://getfirefox.com",
    667      title: "Get Firefox!",
    668    });
    669    _(`New Places root bookmark GUID: ${rootBmk.guid}`);
    670 
    671    _("New root and bookmark should be ignored");
    672    await verifyTrackedItems([]);
    673    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 3);
    674  } finally {
    675    _("Clean up.");
    676    await cleanup();
    677  }
    678 });
    679 
    680 add_task(async function test_onItemDeleted_filtered_root() {
    681  _("Deleted items outside the change roots should not be tracked");
    682 
    683  try {
    684    await tracker.stop();
    685 
    686    _("Insert a bookmark underneath the Places root");
    687    let rootBmk = await PlacesUtils.bookmarks.insert({
    688      parentGuid: PlacesUtils.bookmarks.rootGuid,
    689      url: "http://getfirefox.com",
    690      title: "Get Firefox!",
    691    });
    692    _(`New Places root bookmark GUID: ${rootBmk.guid}`);
    693 
    694    await startTracking();
    695 
    696    await PlacesUtils.bookmarks.remove(rootBmk);
    697 
    698    await verifyTrackedItems([]);
    699    // We'll still increment the counter for the removed item.
    700    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE);
    701  } finally {
    702    _("Clean up.");
    703    await cleanup();
    704  }
    705 });
    706 
    707 add_task(async function test_onPageAnnoChanged() {
    708  _("Page annotations should not be tracked");
    709 
    710  try {
    711    await tracker.stop();
    712 
    713    _("Insert a bookmark without an annotation");
    714    let pageURI = "http://getfirefox.com";
    715    await PlacesUtils.bookmarks.insert({
    716      parentGuid: PlacesUtils.bookmarks.menuGuid,
    717      url: pageURI,
    718      title: "Get Firefox!",
    719    });
    720 
    721    await startTracking();
    722 
    723    _("Add a page annotation");
    724    await PlacesUtils.history.update({
    725      url: pageURI,
    726      annotations: new Map([[PlacesUtils.CHARSET_ANNO, "UTF-16"]]),
    727    });
    728    await verifyTrackedItems([]);
    729    Assert.equal(tracker.score, 0);
    730    await resetTracker();
    731 
    732    _("Remove the page annotation");
    733    await PlacesUtils.history.update({
    734      url: pageURI,
    735      annotations: new Map([[PlacesUtils.CHARSET_ANNO, null]]),
    736    });
    737    await verifyTrackedItems([]);
    738    Assert.equal(tracker.score, 0);
    739  } finally {
    740    _("Clean up.");
    741    await cleanup();
    742  }
    743 });
    744 
    745 add_task(async function test_onFaviconChanged() {
    746  _("Favicon changes should not be tracked");
    747 
    748  try {
    749    await tracker.stop();
    750 
    751    let pageURI = CommonUtils.makeURI("http://getfirefox.com");
    752    let iconURI = CommonUtils.makeURI("http://getfirefox.com/icon");
    753    await PlacesUtils.bookmarks.insert({
    754      parentGuid: PlacesUtils.bookmarks.menuGuid,
    755      url: pageURI,
    756      title: "Get Firefox!",
    757    });
    758 
    759    await PlacesTestUtils.addVisits(pageURI);
    760 
    761    await startTracking();
    762 
    763    _("Favicon annotations should be ignored");
    764    let iconURL =
    765      "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAA" +
    766      "AAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==";
    767    await PlacesTestUtils.setFaviconForPage(pageURI, iconURI, iconURL);
    768    await verifyTrackedItems([]);
    769    Assert.equal(tracker.score, 0);
    770  } finally {
    771    _("Clean up.");
    772    await cleanup();
    773  }
    774 });
    775 
    776 add_task(async function test_async_onItemMoved_moveToFolder() {
    777  _("Items moved via `moveToFolder` should be tracked");
    778 
    779  try {
    780    await tracker.stop();
    781 
    782    await PlacesUtils.bookmarks.insertTree({
    783      guid: PlacesUtils.bookmarks.menuGuid,
    784      children: [
    785        {
    786          guid: "bookmarkAAAA",
    787          title: "A",
    788          url: "http://example.com/a",
    789        },
    790        {
    791          guid: "bookmarkBBBB",
    792          title: "B",
    793          url: "http://example.com/b",
    794        },
    795        {
    796          guid: "bookmarkCCCC",
    797          title: "C",
    798          url: "http://example.com/c",
    799        },
    800        {
    801          guid: "bookmarkDDDD",
    802          title: "D",
    803          url: "http://example.com/d",
    804        },
    805      ],
    806    });
    807    await PlacesUtils.bookmarks.insertTree({
    808      guid: PlacesUtils.bookmarks.toolbarGuid,
    809      children: [
    810        {
    811          guid: "bookmarkEEEE",
    812          title: "E",
    813          url: "http://example.com/e",
    814        },
    815      ],
    816    });
    817 
    818    await startTracking();
    819 
    820    _("Move (A B D) to the toolbar");
    821    await PlacesUtils.bookmarks.moveToFolder(
    822      ["bookmarkAAAA", "bookmarkBBBB", "bookmarkDDDD"],
    823      PlacesUtils.bookmarks.toolbarGuid,
    824      PlacesUtils.bookmarks.DEFAULT_INDEX
    825    );
    826 
    827    // Moving multiple bookmarks between two folders should track the old
    828    // folder, new folder, and moved bookmarks.
    829    await verifyTrackedItems([
    830      "menu",
    831      "toolbar",
    832      "bookmarkAAAA",
    833      "bookmarkBBBB",
    834      "bookmarkDDDD",
    835    ]);
    836    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 3);
    837    await resetTracker();
    838 
    839    _("Reorder toolbar children: (D A B E)");
    840    await PlacesUtils.bookmarks.moveToFolder(
    841      ["bookmarkDDDD", "bookmarkAAAA", "bookmarkBBBB"],
    842      PlacesUtils.bookmarks.toolbarGuid,
    843      0
    844    );
    845 
    846    // Reordering bookmarks in a folder should only track the folder, not the
    847    // bookmarks.
    848    await verifyTrackedItems(["toolbar"]);
    849    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 2);
    850  } finally {
    851    _("Clean up.");
    852    await cleanup();
    853  }
    854 });
    855 
    856 add_task(async function test_async_onItemMoved_update() {
    857  _("Items moved via the asynchronous API should be tracked");
    858 
    859  try {
    860    await tracker.stop();
    861 
    862    await PlacesUtils.bookmarks.insert({
    863      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    864      parentGuid: PlacesUtils.bookmarks.menuGuid,
    865      url: "http://getfirefox.com",
    866      title: "Get Firefox!",
    867    });
    868    let tbBmk = await PlacesUtils.bookmarks.insert({
    869      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    870      parentGuid: PlacesUtils.bookmarks.menuGuid,
    871      url: "http://getthunderbird.com",
    872      title: "Get Thunderbird!",
    873    });
    874 
    875    await startTracking();
    876 
    877    _("Repositioning a bookmark should track the folder");
    878    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    879    await PlacesUtils.bookmarks.update({
    880      guid: tbBmk.guid,
    881      parentGuid: PlacesUtils.bookmarks.menuGuid,
    882      index: 0,
    883    });
    884    await verifyTrackedItems(["menu"]);
    885    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE);
    886    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 1);
    887    await resetTracker();
    888 
    889    _("Reparenting a bookmark should track both folders and the bookmark");
    890    totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    891    await PlacesUtils.bookmarks.update({
    892      guid: tbBmk.guid,
    893      parentGuid: PlacesUtils.bookmarks.toolbarGuid,
    894      index: PlacesUtils.bookmarks.DEFAULT_INDEX,
    895    });
    896    await verifyTrackedItems(["menu", "toolbar", tbBmk.guid]);
    897    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE);
    898    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 3);
    899  } finally {
    900    _("Clean up.");
    901    await cleanup();
    902  }
    903 });
    904 
    905 add_task(async function test_async_onItemMoved_reorder() {
    906  _("Items reordered via the asynchronous API should be tracked");
    907 
    908  try {
    909    await tracker.stop();
    910 
    911    _("Insert out-of-order bookmarks");
    912    let fxBmk = await PlacesUtils.bookmarks.insert({
    913      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    914      parentGuid: PlacesUtils.bookmarks.menuGuid,
    915      url: "http://getfirefox.com",
    916      title: "Get Firefox!",
    917    });
    918    _(`Firefox GUID: ${fxBmk.guid}`);
    919 
    920    let tbBmk = await PlacesUtils.bookmarks.insert({
    921      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    922      parentGuid: PlacesUtils.bookmarks.menuGuid,
    923      url: "http://getthunderbird.com",
    924      title: "Get Thunderbird!",
    925    });
    926    _(`Thunderbird GUID: ${tbBmk.guid}`);
    927 
    928    let mozBmk = await PlacesUtils.bookmarks.insert({
    929      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
    930      parentGuid: PlacesUtils.bookmarks.menuGuid,
    931      url: "https://mozilla.org",
    932      title: "Mozilla",
    933    });
    934    _(`Mozilla GUID: ${mozBmk.guid}`);
    935 
    936    await startTracking();
    937 
    938    _("Reorder bookmarks");
    939    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
    940    await PlacesUtils.bookmarks.reorder(PlacesUtils.bookmarks.menuGuid, [
    941      mozBmk.guid,
    942      fxBmk.guid,
    943      tbBmk.guid,
    944    ]);
    945 
    946    // We only track the folder if we reorder its children, but we should
    947    // bump the score for every changed item.
    948    await verifyTrackedItems(["menu"]);
    949    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 3);
    950    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 1);
    951  } finally {
    952    _("Clean up.");
    953    await cleanup();
    954  }
    955 });
    956 
    957 add_task(async function test_onItemDeleted_removeFolderTransaction() {
    958  _("Folders removed in a transaction should be tracked");
    959 
    960  try {
    961    await tracker.stop();
    962 
    963    _("Create a folder with two children");
    964    let folder = await PlacesUtils.bookmarks.insert({
    965      parentGuid: PlacesUtils.bookmarks.menuGuid,
    966      title: "Test folder",
    967      type: PlacesUtils.bookmarks.TYPE_FOLDER,
    968    });
    969    _(`Folder GUID: ${folder.guid}`);
    970    let fx = await PlacesUtils.bookmarks.insert({
    971      parentGuid: folder.guid,
    972      url: "http://getfirefox.com",
    973      title: "Get Firefox!",
    974    });
    975    _(`Firefox GUID: ${fx.guid}`);
    976    let tb = await PlacesUtils.bookmarks.insert({
    977      parentGuid: folder.guid,
    978      url: "http://getthunderbird.com",
    979      title: "Get Thunderbird!",
    980    });
    981    _(`Thunderbird GUID: ${tb.guid}`);
    982 
    983    await startTracking();
    984 
    985    let txn = PlacesTransactions.Remove({ guid: folder.guid });
    986    // We haven't executed the transaction yet.
    987    await verifyTrackerEmpty();
    988 
    989    _("Execute the remove folder transaction");
    990    await txn.transact();
    991    await verifyTrackedItems(["menu", folder.guid, fx.guid, tb.guid]);
    992    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 3);
    993    await resetTracker();
    994 
    995    _("Undo the remove folder transaction");
    996    await PlacesTransactions.undo();
    997 
    998    await verifyTrackedItems(["menu", folder.guid, fx.guid, tb.guid]);
    999    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 3);
   1000    await resetTracker();
   1001 
   1002    _("Redo the transaction");
   1003    await PlacesTransactions.redo();
   1004    await verifyTrackedItems(["menu", folder.guid, fx.guid, tb.guid]);
   1005    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 3);
   1006  } finally {
   1007    _("Clean up.");
   1008    await cleanup();
   1009  }
   1010 });
   1011 
   1012 add_task(async function test_treeMoved() {
   1013  _("Moving an entire tree of bookmarks should track the parents");
   1014 
   1015  try {
   1016    // Create a couple of parent folders.
   1017    let folder1 = await PlacesUtils.bookmarks.insert({
   1018      parentGuid: PlacesUtils.bookmarks.menuGuid,
   1019      test: "First test folder",
   1020      type: PlacesUtils.bookmarks.TYPE_FOLDER,
   1021    });
   1022 
   1023    // A second folder in the first.
   1024    let folder2 = await PlacesUtils.bookmarks.insert({
   1025      parentGuid: folder1.guid,
   1026      title: "Second test folder",
   1027      type: PlacesUtils.bookmarks.TYPE_FOLDER,
   1028    });
   1029 
   1030    // Create a couple of bookmarks in the second folder.
   1031    await PlacesUtils.bookmarks.insert({
   1032      parentGuid: folder2.guid,
   1033      url: "http://getfirefox.com",
   1034      title: "Get Firefox!",
   1035    });
   1036    await PlacesUtils.bookmarks.insert({
   1037      parentGuid: folder2.guid,
   1038      url: "http://getthunderbird.com",
   1039      title: "Get Thunderbird!",
   1040    });
   1041 
   1042    await startTracking();
   1043 
   1044    // Move folder 2 to be a sibling of folder1.
   1045    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
   1046    await PlacesUtils.bookmarks.update({
   1047      guid: folder2.guid,
   1048      parentGuid: PlacesUtils.bookmarks.menuGuid,
   1049      index: 0,
   1050    });
   1051 
   1052    // the menu and both folders should be tracked, the children should not be.
   1053    await verifyTrackedItems(["menu", folder1.guid, folder2.guid]);
   1054    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE);
   1055    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 3);
   1056  } finally {
   1057    _("Clean up.");
   1058    await cleanup();
   1059  }
   1060 });
   1061 
   1062 add_task(async function test_onItemDeleted() {
   1063  _("Bookmarks deleted via the synchronous API should be tracked");
   1064 
   1065  try {
   1066    await PlacesUtils.bookmarks.insert({
   1067      parentGuid: PlacesUtils.bookmarks.menuGuid,
   1068      url: "http://getfirefox.com",
   1069      title: "Get Firefox!",
   1070    });
   1071    let tb = await PlacesUtils.bookmarks.insert({
   1072      parentGuid: PlacesUtils.bookmarks.menuGuid,
   1073      url: "http://getthunderbird.com",
   1074      title: "Get Thunderbird!",
   1075    });
   1076 
   1077    await startTracking();
   1078 
   1079    // Delete the last item - the item and parent should be tracked.
   1080    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
   1081    await PlacesUtils.bookmarks.remove(tb);
   1082 
   1083    await verifyTrackedItems(["menu", tb.guid]);
   1084    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE);
   1085    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 2);
   1086  } finally {
   1087    _("Clean up.");
   1088    await cleanup();
   1089  }
   1090 });
   1091 
   1092 add_task(async function test_async_onItemDeleted() {
   1093  _("Bookmarks deleted via the asynchronous API should be tracked");
   1094 
   1095  try {
   1096    await tracker.stop();
   1097 
   1098    let fxBmk = await PlacesUtils.bookmarks.insert({
   1099      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
   1100      parentGuid: PlacesUtils.bookmarks.menuGuid,
   1101      url: "http://getfirefox.com",
   1102      title: "Get Firefox!",
   1103    });
   1104    await PlacesUtils.bookmarks.insert({
   1105      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
   1106      parentGuid: PlacesUtils.bookmarks.menuGuid,
   1107      url: "http://getthunderbird.com",
   1108      title: "Get Thunderbird!",
   1109    });
   1110 
   1111    await startTracking();
   1112 
   1113    _("Delete the first item");
   1114    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
   1115    await PlacesUtils.bookmarks.remove(fxBmk.guid);
   1116 
   1117    await verifyTrackedItems(["menu", fxBmk.guid]);
   1118    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE);
   1119    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 2);
   1120  } finally {
   1121    _("Clean up.");
   1122    await cleanup();
   1123  }
   1124 });
   1125 
   1126 add_task(async function test_async_onItemDeleted_eraseEverything() {
   1127  _("Erasing everything should track all deleted items");
   1128 
   1129  try {
   1130    await tracker.stop();
   1131 
   1132    let fxBmk = await PlacesUtils.bookmarks.insert({
   1133      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
   1134      parentGuid: PlacesUtils.bookmarks.mobileGuid,
   1135      url: "http://getfirefox.com",
   1136      title: "Get Firefox!",
   1137    });
   1138    _(`Firefox GUID: ${fxBmk.guid}`);
   1139    let tbBmk = await PlacesUtils.bookmarks.insert({
   1140      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
   1141      parentGuid: PlacesUtils.bookmarks.mobileGuid,
   1142      url: "http://getthunderbird.com",
   1143      title: "Get Thunderbird!",
   1144    });
   1145    _(`Thunderbird GUID: ${tbBmk.guid}`);
   1146    let mozBmk = await PlacesUtils.bookmarks.insert({
   1147      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
   1148      parentGuid: PlacesUtils.bookmarks.menuGuid,
   1149      url: "https://mozilla.org",
   1150      title: "Mozilla",
   1151    });
   1152    _(`Mozilla GUID: ${mozBmk.guid}`);
   1153    let mdnBmk = await PlacesUtils.bookmarks.insert({
   1154      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
   1155      parentGuid: PlacesUtils.bookmarks.menuGuid,
   1156      url: "https://developer.mozilla.org",
   1157      title: "MDN",
   1158    });
   1159    _(`MDN GUID: ${mdnBmk.guid}`);
   1160    let bugsFolder = await PlacesUtils.bookmarks.insert({
   1161      type: PlacesUtils.bookmarks.TYPE_FOLDER,
   1162      parentGuid: PlacesUtils.bookmarks.toolbarGuid,
   1163      title: "Bugs",
   1164    });
   1165    _(`Bugs folder GUID: ${bugsFolder.guid}`);
   1166    let bzBmk = await PlacesUtils.bookmarks.insert({
   1167      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
   1168      parentGuid: bugsFolder.guid,
   1169      url: "https://bugzilla.mozilla.org",
   1170      title: "Bugzilla",
   1171    });
   1172    _(`Bugzilla GUID: ${bzBmk.guid}`);
   1173    let bugsChildFolder = await PlacesUtils.bookmarks.insert({
   1174      type: PlacesUtils.bookmarks.TYPE_FOLDER,
   1175      parentGuid: bugsFolder.guid,
   1176      title: "Bugs child",
   1177    });
   1178    _(`Bugs child GUID: ${bugsChildFolder.guid}`);
   1179    let bugsGrandChildBmk = await PlacesUtils.bookmarks.insert({
   1180      type: PlacesUtils.bookmarks.TYPE_BOOKMARK,
   1181      parentGuid: bugsChildFolder.guid,
   1182      url: "https://example.com",
   1183      title: "Bugs grandchild",
   1184    });
   1185    _(`Bugs grandchild GUID: ${bugsGrandChildBmk.guid}`);
   1186 
   1187    await startTracking();
   1188    // Simulate moving a synced item into a new folder. Deleting the folder
   1189    // should write a tombstone for the item, but not the folder.
   1190    await PlacesTestUtils.setBookmarkSyncFields({
   1191      guid: bugsChildFolder.guid,
   1192      syncStatus: PlacesUtils.bookmarks.SYNC_STATUS.NEW,
   1193    });
   1194    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
   1195    await PlacesUtils.bookmarks.eraseEverything();
   1196 
   1197    // bugsChildFolder's sync status is still "NEW", so it shouldn't be
   1198    // tracked. bugsGrandChildBmk is "NORMAL", so we *should* write a
   1199    // tombstone and track it.
   1200    await verifyTrackedItems([
   1201      "menu",
   1202      mozBmk.guid,
   1203      mdnBmk.guid,
   1204      "toolbar",
   1205      bugsFolder.guid,
   1206      "mobile",
   1207      fxBmk.guid,
   1208      tbBmk.guid,
   1209      "unfiled",
   1210      bzBmk.guid,
   1211      bugsGrandChildBmk.guid,
   1212    ]);
   1213    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 8);
   1214    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 11);
   1215  } finally {
   1216    _("Clean up.");
   1217    await cleanup();
   1218  }
   1219 });
   1220 
   1221 add_task(async function test_onItemDeleted_tree() {
   1222  _("Deleting a tree of bookmarks should track all items");
   1223 
   1224  try {
   1225    // Create a couple of parent folders.
   1226    let folder1 = await PlacesUtils.bookmarks.insert({
   1227      parentGuid: PlacesUtils.bookmarks.menuGuid,
   1228      title: "First test folder",
   1229      type: PlacesUtils.bookmarks.TYPE_FOLDER,
   1230    });
   1231 
   1232    // A second folder in the first.
   1233    let folder2 = await PlacesUtils.bookmarks.insert({
   1234      parentGuid: folder1.guid,
   1235      title: "Second test folder",
   1236      type: PlacesUtils.bookmarks.TYPE_FOLDER,
   1237    });
   1238 
   1239    // Create a couple of bookmarks in the second folder.
   1240    let fx = await PlacesUtils.bookmarks.insert({
   1241      parentGuid: folder2.guid,
   1242      url: "http://getfirefox.com",
   1243      title: "Get Firefox!",
   1244    });
   1245    let tb = await PlacesUtils.bookmarks.insert({
   1246      parentGuid: folder2.guid,
   1247      url: "http://getthunderbird.com",
   1248      title: "Get Thunderbird!",
   1249    });
   1250 
   1251    await startTracking();
   1252 
   1253    // Delete folder2 - everything we created should be tracked.
   1254    let totalSyncChanges = PlacesUtils.bookmarks.totalSyncChanges;
   1255    await PlacesUtils.bookmarks.remove(folder2);
   1256 
   1257    await verifyTrackedItems([fx.guid, tb.guid, folder1.guid, folder2.guid]);
   1258    Assert.equal(tracker.score, SCORE_INCREMENT_XLARGE * 3);
   1259    Assert.equal(PlacesUtils.bookmarks.totalSyncChanges, totalSyncChanges + 4);
   1260  } finally {
   1261    _("Clean up.");
   1262    await cleanup();
   1263  }
   1264 });