@@ -263,6 +263,7 @@ export class Model {
263
263
264
264
return this . _run_loader ( folder , async ( ) => {
265
265
const children = await browser . bookmarks . getChildren ( folder . id ) ;
266
+ fixupIndexes ( children ) ;
266
267
for ( const c of children ) this . _upsertNode ( c ) ;
267
268
268
269
folder . isLoaded = true ;
@@ -318,6 +319,7 @@ export class Model {
318
319
// we might allow for multiple loaders for the same child to run.
319
320
// Worse, the other loader might be non-recursive, so loadedSubtree()
320
321
// wouldn't actually perform as expected.
322
+ fixupIndexes ( children ) ;
321
323
for ( const n of children ) {
322
324
const f = this . _upsertNode ( n ) as Folder ;
323
325
const c = n . children ;
@@ -528,6 +530,8 @@ export class Model {
528
530
if ( toIndex > position . index ) toIndex -- ;
529
531
}
530
532
}
533
+ const oldParent = node . position ?. parent ;
534
+ const oldIndex = node . position ?. index ;
531
535
await browser . bookmarks . move ( node . id , {
532
536
parentId : toParent . id ,
533
537
index : toIndex ,
@@ -536,7 +540,19 @@ export class Model {
536
540
const pos = node . position ;
537
541
/* c8 ignore next -- race avoidance */
538
542
if ( ! pos ) tryAgain ( ) ;
539
- if ( pos . parent !== toParent || pos . index !== toIndex ) tryAgain ( ) ;
543
+
544
+ // We assume the bookmark move has happened even if the bookmark did not
545
+ // move to where we expect. This is because the Firefox bookmark DB might
546
+ // have incorrect indexes in it, so the bookmark might not move to the
547
+ // position we expect even if no other concurrent moves are happening.
548
+ // There is unfortunately little we can do about this, so we ignore it.
549
+ if ( pos . parent !== oldParent || pos . index !== oldIndex ) return ;
550
+
551
+ // It's possible the old and new parents/indexes are the same. If so, the
552
+ // bookmark will have "moved", but the check above will still fail.
553
+ if ( pos . parent === toParent && pos . index === toIndex ) return ;
554
+
555
+ tryAgain ( ) ;
540
556
} ) ;
541
557
542
558
await this . maybeCleanupEmptyFolder ( position . parent ) ;
@@ -1054,3 +1070,11 @@ function isBrowserBTNFolder(bm: Bookmarks.BookmarkTreeNode): boolean {
1054
1070
if ( ! ( "type" in bm ) && ! ( "url" in bm ) ) return true ; // for Chrome
1055
1071
return false ;
1056
1072
}
1073
+
1074
+ /** Firefox sometimes returns a list of bookmark children that have negative
1075
+ * indexes, indexes with gaps, or duplicate indexes. We try to normalize these
1076
+ * here so the rest of Tab Stash doesn't complain. See:
1077
+ * https://github.com/josh-berry/tab-stash/issues/542 */
1078
+ function fixupIndexes ( nodes : Bookmarks . BookmarkTreeNode [ ] ) : void {
1079
+ for ( let i = 0 ; i < nodes . length ; ++ i ) nodes [ i ] . index = i ;
1080
+ }
0 commit comments