1/* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5/* This is a JavaScript module (JSM) to be imported via 6 * Components.utils.import() and acts as a singleton. Only the following 7 * listed symbols will exposed on import, and only when and where imported. 8 */ 9 10var EXPORTED_SYMBOLS = [ 11 "PlacesItem", 12 "Bookmark", 13 "Separator", 14 "BookmarkFolder", 15 "DumpBookmarks", 16]; 17 18const { PlacesBackups } = ChromeUtils.import( 19 "resource://gre/modules/PlacesBackups.jsm" 20); 21const { PlacesSyncUtils } = ChromeUtils.import( 22 "resource://gre/modules/PlacesSyncUtils.jsm" 23); 24const { PlacesUtils } = ChromeUtils.import( 25 "resource://gre/modules/PlacesUtils.jsm" 26); 27const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); 28const { Logger } = ChromeUtils.import("resource://tps/logger.jsm"); 29 30async function DumpBookmarks() { 31 let [bookmarks] = await PlacesBackups.getBookmarksTree(); 32 Logger.logInfo( 33 "Dumping Bookmarks...\n" + JSON.stringify(bookmarks, undefined, 2) + "\n\n" 34 ); 35} 36 37/** 38 * extend, causes a child object to inherit from a parent 39 */ 40function extend(child, supertype) { 41 child.prototype.__proto__ = supertype.prototype; 42} 43/** 44 * PlacesItemProps object, holds properties for places items 45 */ 46function PlacesItemProps(props) { 47 this.location = null; 48 this.uri = null; 49 this.keyword = null; 50 this.title = null; 51 this.after = null; 52 this.before = null; 53 this.folder = null; 54 this.position = null; 55 this.delete = false; 56 this.tags = null; 57 this.last_item_pos = null; 58 this.type = null; 59 60 for (var prop in props) { 61 if (prop in this) { 62 this[prop] = props[prop]; 63 } 64 } 65} 66 67/** 68 * PlacesItem object. Base class for places items. 69 */ 70function PlacesItem(props) { 71 this.props = new PlacesItemProps(props); 72 if (this.props.location == null) { 73 this.props.location = "menu"; 74 } 75 if ("changes" in props) { 76 this.updateProps = new PlacesItemProps(props.changes); 77 } else { 78 this.updateProps = null; 79 } 80} 81 82/** 83 * Instance methods for generic places items. 84 */ 85PlacesItem.prototype = { 86 // an array of possible root folders for places items 87 _bookmarkFolders: { 88 places: PlacesUtils.bookmarks.rootGuid, 89 menu: PlacesUtils.bookmarks.menuGuid, 90 tags: PlacesUtils.bookmarks.tagsGuid, 91 unfiled: PlacesUtils.bookmarks.unfiledGuid, 92 toolbar: PlacesUtils.bookmarks.toolbarGuid, 93 mobile: PlacesUtils.bookmarks.mobileGuid, 94 }, 95 96 _typeMap: new Map([ 97 [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER, PlacesUtils.bookmarks.TYPE_FOLDER], 98 [ 99 PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR, 100 PlacesUtils.bookmarks.TYPE_SEPARATOR, 101 ], 102 [PlacesUtils.TYPE_X_MOZ_PLACE, PlacesUtils.bookmarks.TYPE_BOOKMARK], 103 ]), 104 105 toString() { 106 var that = this; 107 var props = ["uri", "title", "location", "folder"]; 108 var string = 109 (this.props.type ? this.props.type + " " : "") + 110 "(" + 111 (function() { 112 var ret = []; 113 for (var i in props) { 114 if (that.props[props[i]]) { 115 ret.push(props[i] + ": " + that.props[props[i]]); 116 } 117 } 118 return ret; 119 })().join(", ") + 120 ")"; 121 return string; 122 }, 123 124 /** 125 * GetPlacesChildGuid 126 * 127 * Finds the guid of the an item with the specified properties in the places 128 * database under the specified parent. 129 * 130 * @param folder The guid of the folder to search 131 * @param type The type of the item to find, or null to match any item; 132 * this is one of the PlacesUtils.bookmarks.TYPE_* values 133 * @param title The title of the item to find, or null to match any title 134 * @param uri The uri of the item to find, or null to match any uri 135 * 136 * @return the node id if the item was found, otherwise null 137 */ 138 async GetPlacesChildGuid(folder, type, title, uri) { 139 let children = (await PlacesUtils.promiseBookmarksTree(folder)).children; 140 if (!children) { 141 return null; 142 } 143 let guid = null; 144 for (let node of children) { 145 if (node.title == title) { 146 let nodeType = this._typeMap.get(node.type); 147 if (type == null || type == undefined || nodeType == type) { 148 if (uri == undefined || uri == null || node.uri.spec == uri.spec) { 149 // Note that this is suspect as we return the *last* matching 150 // child, which some tests rely on (ie, an early-return here causes 151 // at least 1 test to fail). But that's a yak for another day. 152 guid = node.guid; 153 } 154 } 155 } 156 } 157 return guid; 158 }, 159 160 /** 161 * IsAdjacentTo 162 * 163 * Determines if this object is immediately adjacent to another. 164 * 165 * @param itemName The name of the other object; this may be any kind of 166 * places item 167 * @param relativePos The relative position of the other object. If -1, 168 * it means the other object should precede this one, if +1, 169 * the other object should come after this one 170 * @return true if this object is immediately adjacent to the other object, 171 * otherwise false 172 */ 173 async IsAdjacentTo(itemName, relativePos) { 174 Logger.AssertTrue( 175 this.props.folder_id != -1 && this.props.guid != null, 176 "Either folder_id or guid was invalid" 177 ); 178 let otherGuid = await this.GetPlacesChildGuid( 179 this.props.parentGuid, 180 null, 181 itemName 182 ); 183 Logger.AssertTrue(otherGuid, "item " + itemName + " not found"); 184 let other_pos = (await PlacesUtils.bookmarks.fetch(otherGuid)).index; 185 let this_pos = (await PlacesUtils.bookmarks.fetch(this.props.guid)).index; 186 if (other_pos + relativePos != this_pos) { 187 Logger.logPotentialError( 188 "Invalid position - " + 189 (this.props.title ? this.props.title : this.props.folder) + 190 " not " + 191 (relativePos == 1 ? "after " : "before ") + 192 itemName + 193 " for " + 194 this.toString() 195 ); 196 return false; 197 } 198 return true; 199 }, 200 201 /** 202 * GetItemIndex 203 * 204 * Gets the item index for this places item. 205 * 206 * @return the item index, or -1 if there's an error 207 */ 208 async GetItemIndex() { 209 if (this.props.guid == null) { 210 return -1; 211 } 212 return (await PlacesUtils.bookmarks.fetch(this.props.guid)).index; 213 }, 214 215 /** 216 * GetFolder 217 * 218 * Gets the folder guid for the specified bookmark folder 219 * 220 * @param location The full path of the folder, which must begin 221 * with one of the bookmark root folders 222 * @return the folder guid if the folder is found, otherwise null 223 */ 224 async GetFolder(location) { 225 let folder_parts = location.split("/"); 226 if (!(folder_parts[0] in this._bookmarkFolders)) { 227 return null; 228 } 229 let folderGuid = this._bookmarkFolders[folder_parts[0]]; 230 for (let i = 1; i < folder_parts.length; i++) { 231 let guid = await this.GetPlacesChildGuid( 232 folderGuid, 233 PlacesUtils.bookmarks.TYPE_FOLDER, 234 folder_parts[i] 235 ); 236 if (guid == null) { 237 return null; 238 } 239 folderGuid = guid; 240 } 241 return folderGuid; 242 }, 243 244 /** 245 * CreateFolder 246 * 247 * Creates a bookmark folder. 248 * 249 * @param location The full path of the folder, which must begin 250 * with one of the bookmark root folders 251 * @return the folder id if the folder was created, otherwise -1 252 */ 253 async CreateFolder(location) { 254 let folder_parts = location.split("/"); 255 if (!(folder_parts[0] in this._bookmarkFolders)) { 256 return -1; 257 } 258 let folderGuid = this._bookmarkFolders[folder_parts[0]]; 259 for (let i = 1; i < folder_parts.length; i++) { 260 let subfolderGuid = await this.GetPlacesChildGuid( 261 folderGuid, 262 PlacesUtils.bookmarks.TYPE_FOLDER, 263 folder_parts[i] 264 ); 265 if (subfolderGuid == null) { 266 let { guid } = await PlacesUtils.bookmarks.insert({ 267 parentGuid: folderGuid, 268 name: folder_parts[i], 269 type: PlacesUtils.bookmarks.TYPE_FOLDER, 270 }); 271 folderGuid = guid; 272 } else { 273 folderGuid = subfolderGuid; 274 } 275 } 276 return folderGuid; 277 }, 278 279 /** 280 * GetOrCreateFolder 281 * 282 * Locates the specified folder; if not found it is created. 283 * 284 * @param location The full path of the folder, which must begin 285 * with one of the bookmark root folders 286 * @return the folder id if the folder was found or created, otherwise -1 287 */ 288 async GetOrCreateFolder(location) { 289 let parentGuid = await this.GetFolder(location); 290 if (parentGuid == null) { 291 parentGuid = await this.CreateFolder(location); 292 } 293 return parentGuid; 294 }, 295 296 /** 297 * CheckPosition 298 * 299 * Verifies the position of this places item. 300 * 301 * @param before The name of the places item that this item should be 302 before, or null if this check should be skipped 303 * @param after The name of the places item that this item should be 304 after, or null if this check should be skipped 305 * @param last_item_pos The index of the places item above this one, 306 * or null if this check should be skipped 307 * @return true if this item is in the correct position, otherwise false 308 */ 309 async CheckPosition(before, after, last_item_pos) { 310 if (after) { 311 if (!(await this.IsAdjacentTo(after, 1))) { 312 return false; 313 } 314 } 315 if (before) { 316 if (!(await this.IsAdjacentTo(before, -1))) { 317 return false; 318 } 319 } 320 if (last_item_pos != null && last_item_pos > -1) { 321 let index = await this.GetItemIndex(); 322 if (index != last_item_pos + 1) { 323 Logger.logPotentialError( 324 "Item not found at the expected index, got " + 325 index + 326 ", expected " + 327 (last_item_pos + 1) + 328 " for " + 329 this.toString() 330 ); 331 return false; 332 } 333 } 334 return true; 335 }, 336 337 /** 338 * SetLocation 339 * 340 * Moves this places item to a different folder. 341 * 342 * @param location The full path of the folder to which to move this 343 * places item, which must begin with one of the bookmark root 344 * folders; if null, no changes are made 345 * @return nothing if successful, otherwise an exception is thrown 346 */ 347 async SetLocation(location) { 348 if (location != null) { 349 let newfolderGuid = await this.GetOrCreateFolder(location); 350 Logger.AssertTrue( 351 newfolderGuid, 352 "Location " + location + " doesn't exist; can't change item's location" 353 ); 354 await PlacesUtils.bookmarks.update({ 355 guid: this.props.guid, 356 parentGuid: newfolderGuid, 357 index: PlacesUtils.bookmarks.DEFAULT_INDEX, 358 }); 359 this.props.parentGuid = newfolderGuid; 360 } 361 }, 362 363 /** 364 * SetPosition 365 * 366 * Updates the position of this places item within this item's current 367 * folder. Use SetLocation to change folders. 368 * 369 * @param position The new index this item should be moved to; if null, 370 * no changes are made; if -1, this item is moved to the bottom of 371 * the current folder. Otherwise, must be a string which is the 372 * title of an existing item in the folder, who's current position 373 * is used as the index. 374 * @return nothing if successful, otherwise an exception is thrown 375 */ 376 async SetPosition(position) { 377 if (position == null) { 378 return; 379 } 380 let index = -1; 381 if (position != -1) { 382 let existingGuid = await this.GetPlacesChildGuid( 383 this.props.parentGuid, 384 null, 385 position 386 ); 387 if (existingGuid) { 388 index = (await PlacesUtils.bookmarks.fetch(existingGuid)).index; 389 } 390 Logger.AssertTrue( 391 index != -1, 392 "position " + position + " is invalid; unable to change position" 393 ); 394 } 395 await PlacesUtils.bookmarks.update({ guid: this.props.guid, index }); 396 }, 397 398 /** 399 * Update the title of this places item 400 * 401 * @param title The new title to set for this item; if null, no changes 402 * are made 403 * @return nothing 404 */ 405 async SetTitle(title) { 406 if (title != null) { 407 await PlacesUtils.bookmarks.update({ guid: this.props.guid, title }); 408 } 409 }, 410}; 411 412/** 413 * Bookmark class constructor. Initializes instance properties. 414 */ 415function Bookmark(props) { 416 PlacesItem.call(this, props); 417 if (this.props.title == null) { 418 this.props.title = this.props.uri; 419 } 420 this.props.type = "bookmark"; 421} 422 423/** 424 * Bookmark instance methods. 425 */ 426Bookmark.prototype = { 427 /** 428 * SetKeyword 429 * 430 * Update this bookmark's keyword. 431 * 432 * @param keyword The keyword to set for this bookmark; if null, no 433 * changes are made 434 * @return nothing 435 */ 436 async SetKeyword(keyword) { 437 if (keyword != null) { 438 // Mirror logic from PlacesSyncUtils's updateBookmarkMetadata 439 let entry = await PlacesUtils.keywords.fetch({ url: this.props.uri }); 440 if (entry) { 441 await PlacesUtils.keywords.remove(entry); 442 } 443 await PlacesUtils.keywords.insert({ keyword, url: this.props.uri }); 444 } 445 }, 446 447 /** 448 * SetUri 449 * 450 * Updates this bookmark's URI. 451 * 452 * @param uri The new URI to set for this boomark; if null, no changes 453 * are made 454 * @return nothing 455 */ 456 async SetUri(uri) { 457 if (uri) { 458 let url = Services.io.newURI(uri); 459 await PlacesUtils.bookmarks.update({ guid: this.props.guid, url }); 460 } 461 }, 462 463 /** 464 * SetTags 465 * 466 * Updates this bookmark's tags. 467 * 468 * @param tags An array of tags which should be associated with this 469 * bookmark; any previous tags are removed; if this param is null, 470 * no changes are made. If this param is an empty array, all 471 * tags are removed from this bookmark. 472 * @return nothing 473 */ 474 SetTags(tags) { 475 if (tags != null) { 476 let URI = Services.io.newURI(this.props.uri); 477 PlacesUtils.tagging.untagURI(URI, null); 478 if (tags.length > 0) { 479 PlacesUtils.tagging.tagURI(URI, tags); 480 } 481 } 482 }, 483 484 /** 485 * Create 486 * 487 * Creates the bookmark described by this object's properties. 488 * 489 * @return the id of the created bookmark 490 */ 491 async Create() { 492 this.props.parentGuid = await this.GetOrCreateFolder(this.props.location); 493 Logger.AssertTrue( 494 this.props.parentGuid, 495 "Unable to create " + 496 "bookmark, error creating folder " + 497 this.props.location 498 ); 499 let bookmarkURI = Services.io.newURI(this.props.uri); 500 let { guid } = await PlacesUtils.bookmarks.insert({ 501 parentGuid: this.props.parentGuid, 502 url: bookmarkURI, 503 title: this.props.title, 504 }); 505 this.props.guid = guid; 506 await this.SetKeyword(this.props.keyword); 507 await this.SetTags(this.props.tags); 508 return this.props.guid; 509 }, 510 511 /** 512 * Update 513 * 514 * Updates this bookmark's properties according the properties on this 515 * object's 'updateProps' property. 516 * 517 * @return nothing 518 */ 519 async Update() { 520 Logger.AssertTrue(this.props.guid, "Invalid guid during Update"); 521 await this.SetTitle(this.updateProps.title); 522 await this.SetUri(this.updateProps.uri); 523 await this.SetKeyword(this.updateProps.keyword); 524 await this.SetTags(this.updateProps.tags); 525 await this.SetLocation(this.updateProps.location); 526 await this.SetPosition(this.updateProps.position); 527 }, 528 529 /** 530 * Find 531 * 532 * Locates the bookmark which corresponds to this object's properties. 533 * 534 * @return the bookmark guid if the bookmark was found, otherwise null 535 */ 536 async Find() { 537 this.props.parentGuid = await this.GetFolder(this.props.location); 538 539 if (this.props.parentGuid == null) { 540 Logger.logError("Unable to find folder " + this.props.location); 541 return null; 542 } 543 let bookmarkTitle = this.props.title; 544 this.props.guid = await this.GetPlacesChildGuid( 545 this.props.parentGuid, 546 null, 547 bookmarkTitle, 548 this.props.uri 549 ); 550 551 if (!this.props.guid) { 552 Logger.logPotentialError(this.toString() + " not found"); 553 return null; 554 } 555 if (this.props.keyword != null) { 556 let { keyword } = await PlacesSyncUtils.bookmarks.fetch(this.props.guid); 557 if (keyword != this.props.keyword) { 558 Logger.logPotentialError( 559 "Incorrect keyword - expected: " + 560 this.props.keyword + 561 ", actual: " + 562 keyword + 563 " for " + 564 this.toString() 565 ); 566 return null; 567 } 568 } 569 if (this.props.tags != null) { 570 try { 571 let URI = Services.io.newURI(this.props.uri); 572 let tags = PlacesUtils.tagging.getTagsForURI(URI); 573 tags.sort(); 574 this.props.tags.sort(); 575 if (JSON.stringify(tags) != JSON.stringify(this.props.tags)) { 576 Logger.logPotentialError( 577 "Wrong tags - expected: " + 578 JSON.stringify(this.props.tags) + 579 ", actual: " + 580 JSON.stringify(tags) + 581 " for " + 582 this.toString() 583 ); 584 return null; 585 } 586 } catch (e) { 587 Logger.logPotentialError("error processing tags " + e); 588 return null; 589 } 590 } 591 if ( 592 !(await this.CheckPosition( 593 this.props.before, 594 this.props.after, 595 this.props.last_item_pos 596 )) 597 ) { 598 return null; 599 } 600 return this.props.guid; 601 }, 602 603 /** 604 * Remove 605 * 606 * Removes this bookmark. The bookmark should have been located previously 607 * by a call to Find. 608 * 609 * @return nothing 610 */ 611 async Remove() { 612 Logger.AssertTrue(this.props.guid, "Invalid guid during Remove"); 613 await PlacesUtils.bookmarks.remove(this.props.guid); 614 }, 615}; 616 617extend(Bookmark, PlacesItem); 618 619/** 620 * BookmarkFolder class constructor. Initializes instance properties. 621 */ 622function BookmarkFolder(props) { 623 PlacesItem.call(this, props); 624 this.props.type = "folder"; 625} 626 627/** 628 * BookmarkFolder instance methods 629 */ 630BookmarkFolder.prototype = { 631 /** 632 * Create 633 * 634 * Creates the bookmark folder described by this object's properties. 635 * 636 * @return the id of the created bookmark folder 637 */ 638 async Create() { 639 this.props.parentGuid = await this.GetOrCreateFolder(this.props.location); 640 Logger.AssertTrue( 641 this.props.parentGuid, 642 "Unable to create " + 643 "folder, error creating parent folder " + 644 this.props.location 645 ); 646 let { guid } = await PlacesUtils.bookmarks.insert({ 647 parentGuid: this.props.parentGuid, 648 title: this.props.folder, 649 index: PlacesUtils.bookmarks.DEFAULT_INDEX, 650 type: PlacesUtils.bookmarks.TYPE_FOLDER, 651 }); 652 this.props.guid = guid; 653 return this.props.parentGuid; 654 }, 655 656 /** 657 * Find 658 * 659 * Locates the bookmark folder which corresponds to this object's 660 * properties. 661 * 662 * @return the folder guid if the folder was found, otherwise null 663 */ 664 async Find() { 665 this.props.parentGuid = await this.GetFolder(this.props.location); 666 if (this.props.parentGuid == null) { 667 Logger.logError("Unable to find folder " + this.props.location); 668 return null; 669 } 670 this.props.guid = await this.GetPlacesChildGuid( 671 this.props.parentGuid, 672 PlacesUtils.bookmarks.TYPE_FOLDER, 673 this.props.folder 674 ); 675 if (this.props.guid == null) { 676 return null; 677 } 678 if ( 679 !(await this.CheckPosition( 680 this.props.before, 681 this.props.after, 682 this.props.last_item_pos 683 )) 684 ) { 685 return null; 686 } 687 return this.props.guid; 688 }, 689 690 /** 691 * Remove 692 * 693 * Removes this folder. The folder should have been located previously 694 * by a call to Find. 695 * 696 * @return nothing 697 */ 698 async Remove() { 699 Logger.AssertTrue(this.props.guid, "Invalid guid during Remove"); 700 await PlacesUtils.bookmarks.remove(this.props.guid); 701 }, 702 703 /** 704 * Update 705 * 706 * Updates this bookmark's properties according the properties on this 707 * object's 'updateProps' property. 708 * 709 * @return nothing 710 */ 711 async Update() { 712 Logger.AssertTrue(this.props.guid, "Invalid guid during Update"); 713 await this.SetLocation(this.updateProps.location); 714 await this.SetPosition(this.updateProps.position); 715 await this.SetTitle(this.updateProps.folder); 716 }, 717}; 718 719extend(BookmarkFolder, PlacesItem); 720 721/** 722 * Separator class constructor. Initializes instance properties. 723 */ 724function Separator(props) { 725 PlacesItem.call(this, props); 726 this.props.type = "separator"; 727} 728 729/** 730 * Separator instance methods. 731 */ 732Separator.prototype = { 733 /** 734 * Create 735 * 736 * Creates the bookmark separator described by this object's properties. 737 * 738 * @return the id of the created separator 739 */ 740 async Create() { 741 this.props.parentGuid = await this.GetOrCreateFolder(this.props.location); 742 Logger.AssertTrue( 743 this.props.parentGuid, 744 "Unable to create " + 745 "folder, error creating parent folder " + 746 this.props.location 747 ); 748 let { guid } = await PlacesUtils.bookmarks.insert({ 749 parentGuid: this.props.parentGuid, 750 type: PlacesUtils.bookmarks.TYPE_SEPARATOR, 751 }); 752 this.props.guid = guid; 753 return guid; 754 }, 755 756 /** 757 * Find 758 * 759 * Locates the bookmark separator which corresponds to this object's 760 * properties. 761 * 762 * @return the item guid if the separator was found, otherwise null 763 */ 764 async Find() { 765 this.props.parentGuid = await this.GetFolder(this.props.location); 766 if (this.props.parentGuid == null) { 767 Logger.logError("Unable to find folder " + this.props.location); 768 return null; 769 } 770 if (this.props.before == null && this.props.last_item_pos == null) { 771 Logger.logPotentialError( 772 "Separator requires 'before' attribute if it's the" + 773 "first item in the list" 774 ); 775 return null; 776 } 777 let expected_pos = -1; 778 if (this.props.before) { 779 let otherGuid = this.GetPlacesChildGuid( 780 this.props.parentGuid, 781 null, 782 this.props.before 783 ); 784 if (otherGuid == null) { 785 Logger.logPotentialError( 786 "Can't find places item " + 787 this.props.before + 788 " for locating separator" 789 ); 790 return null; 791 } 792 expected_pos = (await PlacesUtils.bookmarks.fetch(otherGuid)).index - 1; 793 } else { 794 expected_pos = this.props.last_item_pos + 1; 795 } 796 // Note these are IDs instead of GUIDs. 797 let children = await PlacesSyncUtils.bookmarks.fetchChildRecordIds( 798 this.props.parentGuid 799 ); 800 this.props.guid = children[expected_pos]; 801 if (this.props.guid == null) { 802 Logger.logPotentialError( 803 "No separator found at position " + expected_pos 804 ); 805 return null; 806 } 807 let info = await PlacesUtils.bookmarks.fetch(this.props.guid); 808 if (info.type != PlacesUtils.bookmarks.TYPE_SEPARATOR) { 809 Logger.logPotentialError( 810 "Places item at position " + expected_pos + " is not a separator" 811 ); 812 return null; 813 } 814 return this.props.guid; 815 }, 816 817 /** 818 * Update 819 * 820 * Updates this separator's properties according the properties on this 821 * object's 'updateProps' property. 822 * 823 * @return nothing 824 */ 825 async Update() { 826 Logger.AssertTrue(this.props.guid, "Invalid guid during Update"); 827 await this.SetLocation(this.updateProps.location); 828 await this.SetPosition(this.updateProps.position); 829 return true; 830 }, 831 832 /** 833 * Remove 834 * 835 * Removes this separator. The separator should have been located 836 * previously by a call to Find. 837 * 838 * @return nothing 839 */ 840 async Remove() { 841 Logger.AssertTrue(this.props.guid, "Invalid guid during Update"); 842 await PlacesUtils.bookmarks.remove(this.props.guid); 843 }, 844}; 845 846extend(Separator, PlacesItem); 847