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 });