File: tab_strip_model.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (1385 lines) | stat: -rw-r--r-- 61,977 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_UI_TABS_TAB_STRIP_MODEL_H_
#define CHROME_BROWSER_UI_TABS_TAB_STRIP_MODEL_H_

#include <stddef.h>
#include <stdint.h>

#include <map>
#include <memory>
#include <optional>
#include <string>
#include <variant>
#include <vector>

#include "base/containers/span.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/scoped_multi_source_observation.h"
#include "base/scoped_observation.h"
#include "build/build_config.h"
#include "chrome/browser/ui/tabs/tab_model.h"
#include "chrome/browser/ui/tabs/tab_strip_scrubbing_metrics.h"
#include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h"
#include "chrome/common/buildflags.h"
#include "components/sessions/core/session_id.h"
#include "components/tab_groups/tab_group_id.h"
#include "components/tab_groups/tab_group_visual_data.h"
#include "components/tabs/public/tab_collection.h"
#include "components/tabs/public/tab_interface.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h"
#include "ui/base/models/list_selection_model.h"
#include "ui/base/page_transition_types.h"
#include "ui/gfx/range/range.h"

#if BUILDFLAG(IS_ANDROID)
#error This file should only be included on desktop.
#endif

class DraggingTabsSession;
class Profile;
class TabGroupModel;
class TabStripModelDelegate;
class TabStripModelObserver;

namespace content {
class WebContents;
}

namespace split_tabs {
class SplitTabData;
class SplitTabVisualData;
enum class SplitTabLayout;
}

namespace tabs {
class SplitTabCollection;
class TabStripCollection;
class TabGroupTabCollection;
}

namespace tabs_api {
class MojoTreeBuilder;
class TabStripModelAdapterImpl;
}

class TabGroupModelFactory {
 public:
  TabGroupModelFactory();
  TabGroupModelFactory(const TabGroupModelFactory&) = delete;
  TabGroupModelFactory& operator=(const TabGroupModelFactory&) = delete;

  static TabGroupModelFactory* GetInstance();
  std::unique_ptr<TabGroupModel> Create();
};

// Have DetachedTabCollection object as a container of the `collection_` so
// client does not need to worry or deal with the collection object.
struct DetachedTabCollection {
  DetachedTabCollection(
      std::variant<std::unique_ptr<tabs::TabGroupTabCollection>,
                   std::unique_ptr<tabs::SplitTabCollection>> collection,
      std::optional<int> active_index,
      bool pinned_);
  DetachedTabCollection(const DetachedTabCollection&) = delete;
  DetachedTabCollection& operator=(const DetachedTabCollection&) = delete;
  ~DetachedTabCollection();
  DetachedTabCollection(DetachedTabCollection&&);
  std::variant<std::unique_ptr<tabs::TabGroupTabCollection>,
               std::unique_ptr<tabs::SplitTabCollection>>
      collection_;
  // Store the index of tab that was active in the detached group.
  std::optional<int> active_index_ = std::nullopt;
  bool pinned_ = false;
};

// Holds state for a tab that has been detached from the tab strip.
// Will also handle tab deletion if `remove_reason` is kDeleted.
struct DetachedTab {
  DetachedTab(int index_before_any_removals,
              int index_at_time_of_removal,
              std::unique_ptr<tabs::TabModel> tab,
              TabStripModelChange::RemoveReason remove_reason,
              tabs::TabInterface::DetachReason tab_detach_reason,
              std::optional<SessionID> id);
  DetachedTab(const DetachedTab&) = delete;
  DetachedTab& operator=(const DetachedTab&) = delete;
  ~DetachedTab();
  DetachedTab(DetachedTab&&);

  std::unique_ptr<tabs::TabModel> tab;

  // The index of the tab in the original selection model of the tab
  // strip [prior to any tabs being removed, if multiple tabs are being
  // simultaneously removed].
  const int index_before_any_removals;

  // The index of the tab at the time it is being removed. If multiple
  // tabs are being simultaneously removed, the index reflects previously
  // removed tabs in this batch.
  const int index_at_time_of_removal;

  // Reasons for detaching a tab. These may differ, for e.g. when a
  // tab is detached for re-insertion into a browser of different type,
  // in which case the TabInterface is destroyed but the WebContents is
  // retained.
  TabStripModelChange::RemoveReason remove_reason;
  tabs::TabInterface::DetachReason tab_detach_reason;

  // The |contents| associated optional SessionID, used as key for
  // ClosedTabCache. We only cache |contents| if |remove_reason| is kCached.
  //
  // TODO(crbug.com/377537302): The ClosedTabCache feature is gone, but it's
  // unclear if the session ID is needed for other things as well.
  std::optional<SessionID> id;
};

// A feature which wants to show tabstrip-modal UI should call
// TabStripController::ShowModalUI and keep alive the instance of
// ScopedTabStripModalUI for the duration of the tabstrip-modal UI.
class ScopedTabStripModalUI {
 public:
  ScopedTabStripModalUI() = default;
  virtual ~ScopedTabStripModalUI() = default;
};

////////////////////////////////////////////////////////////////////////////////
//
// TabStripModel
//
// A model & low level controller of a Browser Window tabstrip. Holds a vector
// of WebContents, and provides an API for adding, removing and
// shuffling them, as well as a higher level API for doing specific Browser-
// related tasks like adding new Tabs from just a URL, etc.
//
// Each tab may be pinned. Pinned tabs are locked to the left side of the tab
// strip and rendered differently (small tabs with only a favicon). The model
// makes sure all pinned tabs are at the beginning of the tab strip. For
// example, if a non-pinned tab is added it is forced to be with non-pinned
// tabs. Requests to move tabs outside the range of the tab type are ignored.
// For example, a request to move a pinned tab after non-pinned tabs is ignored.
//
// A TabStripModel has one delegate that it relies on to perform certain tasks
// like creating new TabStripModels (probably hosted in Browser windows) when
// required. See TabStripDelegate above for more information.
//
// A TabStripModel also has N observers (see TabStripModelObserver above),
// which can be registered via Add/RemoveObserver. An Observer is notified of
// tab creations, removals, moves, and other interesting events. The
// TabStrip implements this interface to know when to create new tabs in
// the View, and the Browser object likewise implements to be able to update
// its bookkeeping when such events happen.
//
// This implementation of TabStripModel is not thread-safe and should only be
// accessed on the UI thread.
//
////////////////////////////////////////////////////////////////////////////////
class TabStripModel {
 public:
  using TabIterator = tabs::TabCollection::TabIterator;

  // TODO(crbug.com/40881446): Remove this, and use std::optional<size_t> (or at
  // least std::optional<int>) in its place.
  static constexpr int kNoTab = -1;

  TabStripModel() = delete;

  // Construct a TabStripModel with a delegate to help it do certain things
  // (see the TabStripModelDelegate documentation). |delegate| cannot be NULL.
  // the TabGroupModelFactory can be replaced with a nullptr to set the
  // group_model to null in cases where groups are not supported.
  explicit TabStripModel(TabStripModelDelegate* delegate,
                         Profile* profile,
                         TabGroupModelFactory* group_model_factory =
                             TabGroupModelFactory::GetInstance());

  TabStripModel(const TabStripModel&) = delete;
  TabStripModel& operator=(const TabStripModel&) = delete;

  ~TabStripModel();

  // Retrieves the TabStripModelDelegate associated with this TabStripModel.
  TabStripModelDelegate* delegate() const { return delegate_; }

  // Sets the TabStripModelObserver used by the UI showing the tabs. As other
  // observers may query the UI for state, the UI's observer must be first.
  void SetTabStripUI(TabStripModelObserver* observer);

  // Add and remove observers to changes within this TabStripModel.
  void AddObserver(TabStripModelObserver* observer);
  void RemoveObserver(TabStripModelObserver* observer);

  // Retrieve the number of WebContentses/emptiness of the TabStripModel.
  int count() const;

  // TODO(crbug.com/417291958) remove this function since its the same as
  // count().
  int GetTabCount() const;

  bool empty() const;

  // Retrieve the Profile associated with this TabStripModel.
  Profile* profile() const { return profile_; }

  // Retrieve the index of the currently active WebContents. The only time this
  // is kNoTab is if the tab strip is being initialized or destroyed. Note that
  // tab strip destruction is an asynchronous process.
  int active_index() const {
    return selection_model_.active().has_value()
               ? static_cast<int>(selection_model_.active().value())
               : kNoTab;
  }

  // Returns true if the tabstrip is currently closing all open tabs (via a
  // call to CloseAllTabs). As tabs close, the selection in the tabstrip
  // changes which notifies observers, which can use this as an optimization to
  // avoid doing meaningless or unhelpful work.
  bool closing_all() const { return closing_all_; }

  // Basic API /////////////////////////////////////////////////////////////////

  // Determines if the specified index is contained within the TabStripModel.
  bool ContainsIndex(int index) const;

  // Adds the specified WebContents in the default location. Tabs opened
  // in the foreground inherit the opener of the previously active tab.
  // Use of the detached tab is preferred over webcontents, so when possible
  // use AppendTab instead of this method.
  void AppendWebContents(std::unique_ptr<content::WebContents> contents,
                         bool foreground);

  // Adds the specified Tab at the end of the Tabstrip. Tabs opened
  // in the foreground inherit the opener of the previously active tab and
  // become the active tab.
  void AppendTab(std::unique_ptr<tabs::TabModel> tab, bool foreground);

  // Adds the specified WebContents at the specified location.
  // |add_types| is a bitmask of AddTabTypes; see it for details.
  //
  // All append/insert methods end up in this method.
  //
  // NOTE: adding a tab using this method does NOT query the order controller,
  // as such the ADD_FORCE_INDEX AddTabTypes is meaningless here. The only time
  // the |index| is changed is if using the index would result in breaking the
  // constraint that all pinned tabs occur before non-pinned tabs. It returns
  // the index the web contents is actually inserted to. See also
  // AddWebContents.
  int InsertWebContentsAt(
      int index,
      std::unique_ptr<content::WebContents> contents,
      int add_types,
      std::optional<tab_groups::TabGroupId> group = std::nullopt);

  // Creates a group object so that group_model can link it with once group
  // collection owns it.
  // TODO(crbug.com/392952244): Remove this after replacing callers with
  // detaching and attaching groups.
  void AddTabGroup(const tab_groups::TabGroupId group_id,
                   tab_groups::TabGroupVisualData visual_data);

  // Adds a TabModel from another tabstrip at the specified location. See
  // InsertWebContentsAt.
  int InsertDetachedTabAt(
      int index,
      std::unique_ptr<tabs::TabModel> tab,
      int add_types,
      std::optional<tab_groups::TabGroupId> group = std::nullopt);

  // Removes the group collection from the collection hierarchy and passes it to
  // the client. The client can re-insert into another tabstrip using
  // `InsertDetachedGroupAt` without destroying the group.
  std::unique_ptr<DetachedTabCollection> DetachTabGroupForInsertion(
      const tab_groups::TabGroupId group_id);

  // Inserts a detached tab group into the tabstrip starting at `index`.
  gfx::Range InsertDetachedTabGroupAt(
      std::unique_ptr<DetachedTabCollection> group,
      int index);

  // Removes the split collection from the collection hierarchy and passes it to
  // the client. The client can re-insert into another tabstrip using
  // `InsertDetachedSplitTabAt` without destroying the split.
  std::unique_ptr<DetachedTabCollection> DetachSplitTabForInsertion(
      const split_tabs::SplitTabId split_id);

  // Inserts a detached split tab into the tabstrip starting at `index`.
  // `pinned` and `group` information are used to insert it in the right place
  // in the collection hierarchy.
  gfx::Range InsertDetachedSplitTabAt(
      std::unique_ptr<DetachedTabCollection> split,
      int index,
      bool pinned,
      std::optional<tab_groups::TabGroupId> group_id = std::nullopt);

  // Closes the WebContents at the specified index. This causes the
  // WebContents to be destroyed, but it may not happen immediately.
  // |close_types| is a bitmask of CloseTypes.
  // TODO(crbug.com/392950857): Currently many call sites of CloseWebContentsAt
  // convert a tab/webcontents to an index, which gets converted back to a
  // webcontents within this function. Provide a CloseWebContents function that
  // directly closes a web contents so that we don't have to convert back and
  // forth.
  void CloseWebContentsAt(int index, uint32_t close_types);

  // Discards the WebContents at |index| and replaces it with |new_contents|.
  // The WebContents that was at |index| is returned and its ownership returns
  // to the caller.
  std::unique_ptr<content::WebContents> DiscardWebContentsAt(
      int index,
      std::unique_ptr<content::WebContents> new_contents);

  // Detaches the tab at the specified index for reinsertion into another tab
  // strip. Returns the detached tab.
  std::unique_ptr<tabs::TabModel> DetachTabAtForInsertion(int index);

  // Detaches the WebContents at the specified index for re-insertion into
  // another browser of a different type, destroying the owning TabModel in the
  // process.
  //
  // This works as follows:
  //   - the contents is extracted from the source browser and the owning tab is
  //     destroyed (performed by DetachWebContentsAtForInsertion())
  //   - the contents is added to the new browser, creating a new tab model
  //
  // TODO(crbug.com/334281979): This is done to avoid TabFeatures having to deal
  // with changing browser types during tab moves. This should no longer be
  // necessary once non-normal browser windows do not use Browser, TabStripModel
  // or TabModel.
  std::unique_ptr<content::WebContents> DetachWebContentsAtForInsertion(
      int index);

  // Detaches the WebContents at the specified index and immediately deletes it.
  void DetachAndDeleteWebContentsAt(int index);

  // Makes the tab at the specified index the active tab. |gesture_detail.type|
  // contains the gesture type that triggers the tab activation.
  // |gesture_detail.time_stamp| contains the timestamp of the user gesture, if
  // any.
  void ActivateTabAt(
      int index,
      TabStripUserGestureDetails gesture_detail = TabStripUserGestureDetails(
          TabStripUserGestureDetails::GestureType::kNone));

  // Move the WebContents at the specified index to another index. This
  // method does NOT send Detached/Attached notifications, rather it moves the
  // WebContents inline and sends a Moved notification instead.
  // EnsureGroupContiguity() is called after the move, so this will never result
  // in non-contiguous group (though the moved tab's group may change).
  // If |select_after_move| is false, whatever tab was selected before the move
  // will still be selected, but its index may have incremented or decremented
  // one slot. It returns the index the web contents is actually moved to.
  int MoveWebContentsAt(int index, int to_position, bool select_after_move);

  // This is similar to `MoveWebContentsAt` but takes in an additional `group`
  // parameter that the tab is assigned to with the move. This does not make a
  // best case effort to ensure group contiguity and would rather CHECK if it
  // breaks group contiguity.
  int MoveWebContentsAt(int index,
                        int to_position,
                        bool select_after_move,
                        std::optional<tab_groups::TabGroupId> group);

  // Moves the selected tabs to |index|. |index| is treated as if the tab strip
  // did not contain any of the selected tabs. For example, if the tabstrip
  // contains [A b c D E f] (upper case selected) and this is invoked with 1 the
  // result is [b A D E c f].
  // This method maintains that all pinned tabs occur before non-pinned tabs.
  // When pinned tabs are selected the move is processed in two chunks: first
  // pinned tabs are moved, then non-pinned tabs are moved. If the index is
  // after (pinned-tab-count - selected-pinned-tab-count), then the index the
  // non-pinned selected tabs are moved to is (index +
  // selected-pinned-tab-count). For example, if the model consists of
  // [A b c D E f] (A b c are pinned) and this is invoked with 2, the result is
  // [b c A D E f]. In this example nothing special happened because the target
  // index was <= (pinned-tab-count - selected-pinned-tab-count). If the target
  // index were 3, then the result would be [b c A f D F]. A, being pinned, can
  // move no further than index 2. The non-pinned tabs are moved to the target
  // index + selected-pinned tab-count (3 + 1).
  void MoveSelectedTabsTo(int index,
                          std::optional<tab_groups::TabGroupId> group);

  // Moves all tabs in `group` to `to_index`. This has no checks to make sure
  // the position is valid for a group to move to.
  void MoveGroupTo(const tab_groups::TabGroupId& group, int to_index);

  // Moves all tabs in split with `split_id` to `to_index` with  properties
  // `pinned` and `group_id`. This has no checks to make sure the position is
  // valid for a split to move to.
  void MoveSplitTo(const split_tabs::SplitTabId& split_id,
                   int to_index,
                   bool pinned,
                   std::optional<tab_groups::TabGroupId> group_id);

  // Returns the currently active WebContents, or NULL if there is none.
  content::WebContents* GetActiveWebContents() const;

  // Returns the currently active Tab, or NULL if there is none.
  tabs::TabInterface* GetActiveTab() const;

  // Returns the WebContents at the specified index, or NULL if there is
  // none.
  content::WebContents* GetWebContentsAt(int index) const;

  // Returns the index of the specified WebContents, or TabStripModel::kNoTab
  // if the WebContents is not in this TabStripModel.
  int GetIndexOfWebContents(const content::WebContents* contents) const;

  // Notify any observers that the tab has changed in some way. See
  // TabChangeType for details of |change_type|.'
  void NotifyTabChanged(const tabs::TabInterface* const tab,
                        TabChangeType change_type);

  // Notify any observers that the WebContents at the specified index has
  // changed in some way. See TabChangeType for details of |change_type|.
  void UpdateWebContentsStateAt(int index, TabChangeType change_type);

  // Cause a tab to display a UI indication to the user that it needs their
  // attention.
  void SetTabNeedsAttentionAt(int index, bool attention);

  // Close all tabs at once. Code can use closing_all() above to defer
  // operations that might otherwise by invoked by the flurry of detach/select
  // notifications this method causes.
  void CloseAllTabs();

  // Close all tabs in the given |group| at once.
  void CloseAllTabsInGroup(const tab_groups::TabGroupId& group);

  // Returns true if there are any WebContentses that are currently loading
  // and should be shown on the UI.
  bool TabsNeedLoadingUI() const;

  // Returns the WebContents that opened the WebContents at |index|, or NULL if
  // there is no opener on record.
  tabs::TabInterface* GetOpenerOfTabAt(const int index) const;

  // Changes the |opener| of the WebContents at |index|.
  // Note: |opener| must be in this tab strip. Also a tab must not be its own
  // opener.
  void SetOpenerOfWebContentsAt(int index, content::WebContents* opener);

  // Returns the index of the last WebContents in the model opened by the
  // specified opener, starting at |start_index|.
  int GetIndexOfLastWebContentsOpenedBy(const content::WebContents* opener,
                                        int start_index) const;

  // To be called when a navigation is about to occur in the specified
  // WebContents. Depending on the tab, and the transition type of the
  // navigation, the TabStripModel may adjust its selection behavior and opener
  // inheritance.
  void TabNavigating(content::WebContents* contents,
                     ui::PageTransition transition);

  // Changes the blocked state of the tab at |index|.
  void SetTabBlocked(int index, bool blocked);

  // Changes the pinned state of the tab at `index`. See description above
  // class for details on this. Returns the index the tab is now at (it may have
  // been moved to maintain contiguity of pinned tabs at the beginning of the
  // tabstrip).)
  int SetTabPinned(int index, bool pinned);

  // Returns true if the tab at |index| is pinned.
  // See description above class for details on pinned tabs.
  bool IsTabPinned(int index) const;

  bool IsTabCollapsed(int index) const;

  bool IsGroupCollapsed(const tab_groups::TabGroupId& group) const;

  // Returns true if the tab at |index| is blocked by a tab modal dialog.
  bool IsTabBlocked(int index) const;

  // Returns true if the tab at |index| is allowed to be closed.
  bool IsTabClosable(int index) const;

  // Returns true if the tab corresponding to |contents| is allowed to be
  // closed.
  bool IsTabClosable(const content::WebContents* contents) const;

  split_tabs::SplitTabData* GetSplitData(split_tabs::SplitTabId split_id) const;

  bool ContainsSplit(split_tabs::SplitTabId split_id) const;

  std::optional<split_tabs::SplitTabId> GetSplitForTab(int index) const;

  // Returns the group that contains the tab at |index|, or nullopt if the tab
  // index is invalid or not grouped.
  std::optional<tab_groups::TabGroupId> GetTabGroupForTab(int index) const;

  // If a tab inserted at |index| would be within a tab group, return that
  // group's ID. Otherwise, return nullopt. If |index| points to the first tab
  // in a group, it will return nullopt since a new tab would be either between
  // two different groups or just after a non-grouped tab.
  std::optional<tab_groups::TabGroupId> GetSurroundingTabGroup(int index) const;

  // Returns the index of the first tab that is not a pinned tab. This returns
  // |count()| if all of the tabs are pinned tabs, and 0 if none of the tabs are
  // pinned tabs.
  int IndexOfFirstNonPinnedTab() const;

  // Extends the selection from the anchor to |index|.
  void ExtendSelectionTo(int index);

  // This can fail if the tabstrip is not editable.
  void SelectTabAt(int index);

  // This can fail if the tabstrip is not editable.
  void DeselectTabAt(int index);

  // Makes sure the tabs from the anchor to |index| are selected. This adds to
  // the selection if there is an anchor and resets the selection to |index| if
  // there is not an anchor.
  void AddSelectionFromAnchorTo(int index);

  // Returns true if the tab at |index| is selected.
  bool IsTabSelected(int index) const;

  // Sets the selection to match that of |source|.
  void SetSelectionFromModel(ui::ListSelectionModel source);

  const ui::ListSelectionModel& selection_model() const;

  // Features that want to show tabstrip-modal UI are mutually exclusive.
  // Before showing a modal UI first check `CanShowModalUI`. Then call
  // ShowModalUI() and keep `ScopedTabStripModal` alive to prevent other
  // features from showing tabstrip-modal UI.
  bool CanShowModalUI() const;
  std::unique_ptr<ScopedTabStripModalUI> ShowModalUI();
  void ForceShowingModalUIForTesting(bool showing);

  // Command level API /////////////////////////////////////////////////////////

  // Adds a WebContents at the best position in the TabStripModel given
  // the specified insertion index, transition, etc. |add_types| is a bitmask of
  // AddTabTypes; see it for details. This method ends up calling into
  // InsertWebContentsAt to do the actual insertion. Pass kNoTab for |index| to
  // append the contents to the end of the tab strip.
  void AddWebContents(
      std::unique_ptr<content::WebContents> contents,
      int index,
      ui::PageTransition transition,
      int add_types,
      std::optional<tab_groups::TabGroupId> group = std::nullopt);
  void AddTab(std::unique_ptr<tabs::TabModel> tab,
              int index,
              ui::PageTransition transition,
              int add_types,
              std::optional<tab_groups::TabGroupId> group = std::nullopt);

  // Closes the selected tabs.
  void CloseSelectedTabs();

  // Select adjacent tabs
  void SelectNextTab(
      TabStripUserGestureDetails detail = TabStripUserGestureDetails(
          TabStripUserGestureDetails::GestureType::kOther));
  void SelectPreviousTab(
      TabStripUserGestureDetails detail = TabStripUserGestureDetails(
          TabStripUserGestureDetails::GestureType::kOther));

  // Selects the last tab in the tab strip.
  void SelectLastTab(
      TabStripUserGestureDetails detail = TabStripUserGestureDetails(
          TabStripUserGestureDetails::GestureType::kOther));

  // Moves the active in the specified direction. Respects group boundaries.
  void MoveTabNext();
  void MoveTabPrevious();

  // This is used by the `tab_drag_controller` to help precompute the group the
  // selected tabs would be a part of if they moved to a destination index. It
  // returns the index of the tabs in the current model that would end up being
  // the adjacent tabs of the selected unpinned tabs post move operation.
  std::pair<std::optional<int>, std::optional<int>>
  GetAdjacentTabsAfterSelectedMove(base::PassKey<DraggingTabsSession>,
                                   int destination_index);

  // Updates the layout for the tabs with `split_id` and notifies observers.
  void UpdateSplitLayout(split_tabs::SplitTabId split_id,
                         split_tabs::SplitTabLayout tab_layout);

  // Updates the ratio for the tabs with `split_id` and notifies observers.
  void UpdateSplitRatio(split_tabs::SplitTabId split_id,
                        double start_content_ratio);

  // Updates the split tab at index `split_index` with the tab at
  // `update_index`. The split that includes `split_index` must include the
  // active tab in the tab strip.
  enum class SplitUpdateType { kReplace, kSwap };
  void UpdateTabInSplit(tabs::TabInterface* split_tab,
                        int update_index,
                        SplitUpdateType update_type);

  // Reverses the order of tabs with `split_id`.
  void ReverseTabsInSplit(split_tabs::SplitTabId split_id);

  // Create a new split view with the active tab and add the set of tabs pointed
  // to by |indices| to it. Reorders the tabs so they are contiguous. |indices|
  // must be sorted in ascending order.
  split_tabs::SplitTabId AddToNewSplit(
      const std::vector<int> indices,
      split_tabs::SplitTabVisualData visual_data);

  // Create a new tab group and add the set of tabs pointed to be |indices| to
  // it. Pins all of the tabs if any of them were pinned, and reorders the tabs
  // so they are contiguous and do not split an existing group in half. Returns
  // the new group. |indices| must be sorted in ascending order.
  tab_groups::TabGroupId AddToNewGroup(const std::vector<int> indices);

  // Add the set of tabs pointed to by |indices| to the given tab group |group|.
  // The tabs take on the pinnedness of the tabs already in the group. Tabs
  // before the group will move to the start, while tabs after the group will
  // move to the end. If |add_to_end| is true, all tabs will instead move to
  // the end. |indices| must be sorted in ascending order.
  void AddToExistingGroup(const std::vector<int> indices,
                          const tab_groups::TabGroupId group,
                          const bool add_to_end = false);

  // Similar to AddToExistingGroup(), but creates a group with id |group| if it
  // doesn't exist. This is only intended to be called from session restore
  // code.
  void AddToGroupForRestore(const std::vector<int>& indices,
                            const tab_groups::TabGroupId& group);

  // Removes the set of tabs pointed to by |indices| from the the groups they
  // are in, if any. The tabs are moved out of the group if necessary. |indices|
  // must be sorted in ascending order.
  void RemoveFromGroup(const std::vector<int>& indices);

  // Unsplits all the tabs that are part of the split with `split_id`. The tabs
  // maintain their group and pin properties.
  void RemoveSplit(split_tabs::SplitTabId split_id);

  TabGroupModel* group_model() const { return group_model_.get(); }

  bool SupportsTabGroups() const { return group_model_.get() != nullptr; }

  // Returns true if one or more of the tabs pointed to by |indices| are
  // supported by read later.
  bool IsReadLaterSupportedForAny(const std::vector<int>& indices);

  // Saves tabs with url supported by Read Later.
  void AddToReadLater(const std::vector<int>& indices);

  // Notifies all group observers that the TabGroupEditor is opening. This is
  // used by Views that want to force the editor to open without having to find
  // the group's header view in the Tab Strip.
  void OpenTabGroupEditor(const tab_groups::TabGroupId& group);

  // Updates the group visuals and notifies observers.
  void ChangeTabGroupVisuals(const tab_groups::TabGroupId& group,
                             tab_groups::TabGroupVisualData visual_data,
                             bool is_customized = false);

  // Returns iterators for traversing through all the tabs in the tabstrip.
  TabIterator begin() const;
  TabIterator end() const;

  // Gets the root of the tab strip model. Used to traverse the tab topology.
  const tabs::TabCollection* Root(
      base::PassKey<tabs_api::MojoTreeBuilder> key) const;

  // Finds the group id for a tab collection. Note that this API can be error
  // prone. Make sure to read and understand the potential problems with
  // relying on group id.
  std::optional<const tab_groups::TabGroupId> FindGroupIdFor(
      const tabs::TabCollection::Handle& collection_handle,
      base::PassKey<tabs_api::TabStripModelAdapterImpl>) const;

  // View API //////////////////////////////////////////////////////////////////

  // Context menu functions. Tab groups uses command ids following CommandLast
  // for entries in the 'Add to existing group' submenu.
  enum ContextMenuCommand {
    CommandFirst,
    CommandNewTabToRight,
    CommandReload,
    CommandDuplicate,
    CommandCloseTab,
    CommandCloseOtherTabs,
    CommandCloseTabsToRight,
    CommandTogglePinned,
    CommandToggleGrouped,
    CommandToggleSiteMuted,
    CommandSendTabToSelf,
    CommandAddNote,
    CommandAddToReadLater,
    CommandAddToNewGroup,
    CommandAddToExistingGroup,
    CommandAddToNewGroupFromMenuItem,
    CommandAddToNewComparisonTable,
    CommandAddToExistingComparisonTable,
    CommandAddToSplit,
    CommandSwapWithActiveSplit,
    CommandArrangeSplit,
    CommandRemoveFromGroup,
    CommandMoveToExistingWindow,
    CommandMoveTabsToNewWindow,
    CommandOrganizeTabs,
    CommandCopyURL,
    CommandGoBack,
    CommandCloseAllTabs,
    CommandCommerceProductSpecifications,
#if BUILDFLAG(ENABLE_GLIC)
    CommandGlicShareLimit,
    CommandGlicStartShare,
    CommandGlicStopShare,
#endif
    CommandLast
  };

  // Returns true if the specified command is enabled. If |context_index| is
  // selected the response applies to all selected tabs.
  bool IsContextMenuCommandEnabled(int context_index,
                                   ContextMenuCommand command_id) const;

  // Performs the action associated with the specified command for the given
  // TabStripModel index |context_index|.  If |context_index| is selected the
  // command applies to all selected tabs.
  void ExecuteContextMenuCommand(int context_index,
                                 ContextMenuCommand command_id);

  // Returns a list of the group ids that are going to be deleted if a given
  // list of tab indexes are removed from the group. used by context menu
  // commands to decide whether to confirm group deletion.
  std::vector<tab_groups::TabGroupId> GetGroupsDestroyedFromRemovingIndices(
      const std::vector<int>& indices) const;

  // This should be called after GetGroupsDestroyedFromRemovingIndices(). Marks
  // all groups in `group_ids` as closing. This is useful in the event you need
  // to know if a group is currently closing or not such as when a grouped tab
  // is closed which has an unload handler.
  void MarkTabGroupsForClosing(
      const std::vector<tab_groups::TabGroupId> group_ids);

  // There are multiple commands that close by indices. They all must check the
  // Group affiliation of the indices, confirm that they can delete groups, and
  // then perform the close of the indices. When true `delete_groups` also
  // deletes any saved groups that are closing. When false, groups will close
  // normally but continue to be saved.
  void ExecuteCloseTabsByIndicesCommand(
      base::RepeatingCallback<std::vector<int>()> get_indices_to_close,
      bool delete_groups);

  // Adds the tab at |context_index| to the given tab group |group|. If
  // |context_index| is selected the command applies to all selected tabs.
  void ExecuteAddToExistingGroupCommand(int context_index,
                                        const tab_groups::TabGroupId& group);

  // Adds the tab at |context_index| to the browser window at |browser_index|.
  // If |context_index| is selected the command applies to all selected tabs.
  void ExecuteAddToExistingWindowCommand(int context_index, int browser_index);

  // Returns true if 'CommandToggleSiteMuted' will mute. |index| is the
  // index supplied to |ExecuteContextMenuCommand|.
  bool WillContextMenuMuteSites(int index);

  // Returns true if 'CommandTogglePinned' will pin. |index| is the index
  // supplied to |ExecuteContextMenuCommand|.
  bool WillContextMenuPin(int index);

  // Returns true if 'CommandToggleGrouped' will group. |index| is the index
  // supplied to |ExecuteContextMenuCommand|.
  bool WillContextMenuGroup(int index);

  // Convert a ContextMenuCommand into a browser command. Returns true if a
  // corresponding browser command exists, false otherwise.
  static bool ContextMenuCommandToBrowserCommand(int cmd_id, int* browser_cmd);

  // Returns the index of the next tab spawned by the specified tabs in
  // `block_tab_range`.
  int GetIndexOfNextWebContentsOpenedBy(
      const gfx::Range& block_tab_range) const;

  // Returns the index of the next tab spawned by the opener of the specified
  // tabs in `block_tab_range`.
  int GetIndexOfNextWebContentsOpenedByOpenerOf(
      const gfx::Range& block_tab_range) const;

  // Finds the next available tab to switch to as the active tab starting at
  // a block of tabs. The methods will check the indices to
  // the right of the block before checking the indices to the left of the
  // block. Index within the block cannot be returned. Returns std::nullopt if
  // there are no valid tabs.
  std::optional<int> GetNextExpandedActiveTab(
      const gfx::Range& block_tab_range) const;
  std::optional<int> GetNextExpandedActiveTab(
      tab_groups::TabGroupId collapsing_group) const;

  // Forget all opener relationships, to reduce unpredictable tab switching
  // behavior in complex session states.
  void ForgetAllOpeners();

  // Forgets the opener relationship of the specified WebContents.
  void ForgetOpener(content::WebContents* contents);

  // Determine where to place a newly opened tab by using the supplied
  // transition and foreground flag to figure out how it was opened.
  int DetermineInsertionIndex(ui::PageTransition transition, bool foreground);

  // If a tab is in a group and the tab failed to close, this method will be
  // called from the unload_controller. Ungroup the group to maintain
  // consistency with the user's intended action (to get rid of the group).
  void GroupCloseStopped(const tab_groups::TabGroupId& group);

  // Serialise this object into a trace.
  void WriteIntoTrace(perfetto::TracedValue context) const;

  // Convert between tabs and indices.
  int GetIndexOfTab(const tabs::TabInterface* tab) const;
  tabs::TabInterface* GetTabAtIndex(int index) const;

  // TODO(349161508) remove this method once tabs dont need to be converted
  // into webcontents.
  tabs::TabInterface* GetTabForWebContents(
      const content::WebContents* contents) const;

 private:
  FRIEND_TEST_ALL_PREFIXES(TabStripModelTest, GetIndicesClosedByCommand);
  // Temporary private API.
  FRIEND_TEST_ALL_PREFIXES(TabStripModelTest, FindGroupIdFor);

  struct DetachNotifications;
  struct MoveNotification {
    int initial_index;
    std::optional<tab_groups::TabGroupId> intial_group;
    bool initial_pinned;
    raw_ptr<const tabs::TabInterface> tab;
    TabStripSelectionChange selection_change;
  };

  // Tracks whether a tabstrip-modal UI is showing.
  class ScopedTabStripModalUIImpl : public ScopedTabStripModalUI {
   public:
    explicit ScopedTabStripModalUIImpl(TabStripModel* model);
    ~ScopedTabStripModalUIImpl() override;

   private:
    // Owns this.
    raw_ptr<TabStripModel> model_;
  };

  tabs::TabModel* GetTabModelAtIndex(int index) const;

  // Perform tasks associated with changes to the model. Change the Active Index
  // and notify observers.
  void OnChange(const TabStripModelChange& change,
                const TabStripSelectionChange& selection);

  // Notify observers that a `group` was created.
  void NotifyTabGroupVisualsChanged(const tab_groups::TabGroupId& group_id,
                                    TabGroupChange::VisualsChange visuals);

  // Notify observers that a `group` was created.
  void NotifyTabGroupCreated(const tab_groups::TabGroupId& group);

  // Notify observers that a `group` was closed.
  void NotifyTabGroupClosed(const tab_groups::TabGroupId& group);

  // Notify observers that `group` is moved.
  void NotifyTabGroupMoved(const tab_groups::TabGroupId& group);

  // Notify observers that `group` is detached from the model. This also sends
  // split related observations within the group.
  void NotifyTabGroupDetached(
      tabs::TabGroupTabCollection* group_collection,
      std::map<split_tabs::SplitTabId,
               std::vector<std::pair<tabs::TabInterface*, int>>>
          splits_in_group);

  // Notify observers that `group` is attached to the model. This also sends
  // split related observations within the group.
  void NotifyTabGroupAttached(tabs::TabGroupTabCollection* group_collection);

  // Notify observers that split with `split_id` has been created.
  void NotifySplitTabCreated(
      split_tabs::SplitTabId split_id,
      const std::vector<std::pair<tabs::TabInterface*, int>>& tabs_with_indices,
      SplitTabChange::SplitTabAddReason reason,
      const split_tabs::SplitTabVisualData& visual_data);

  // Notify observers that visual data for a split has changed.
  void NotifySplitTabVisualsChanged(
      split_tabs::SplitTabId split_id,
      const split_tabs::SplitTabVisualData& old_visual_data,
      const split_tabs::SplitTabVisualData& new_visual_data);

  // Notify observers that contents of a split has been reordered.
  void NotifySplitTabContentsUpdated(
      split_tabs::SplitTabId split_id,
      const std::vector<std::pair<tabs::TabInterface*, int>>& prev_tabs,
      const std::vector<std::pair<tabs::TabInterface*, int>>& new_tabs);

  // Notify observers that split with `split_id` has been removed.
  void NotifySplitTabRemoved(
      split_tabs::SplitTabId split_id,
      const std::vector<std::pair<tabs::TabInterface*, int>>& tabs_with_indices,
      SplitTabChange::SplitTabRemoveReason reason);

  // Notify observers that a split was detached from this tabstrip model.
  // This also sends any group related notification.
  void NotifySplitTabDetached(
      tabs::SplitTabCollection* split_collection,
      std::vector<std::pair<tabs::TabInterface*, int>> tabs_in_split,
      std::optional<tab_groups::TabGroupId> previous_group_state);

  // Notify observers that a split was attached to this tabstrip model.
  // This also sends any group related notification.
  void NotifySplitTabAttached(tabs::SplitTabCollection* split_collection);

  // Detaches the tab at the specified `index` from this strip.
  // `web_contents_remove_reason` is used to indicate to observers what is going
  // to happen to the WebContents (i.e. deleted or reinserted into another tab
  // strip). `tab_detach_reason` is used to indicate to observers what is going
  // to happen to the TabModel owning the WebContents. These reasons may not
  // always match (a WebContents may be retained for re-insertion while its
  // owning TabModel may be destroyed).
  std::unique_ptr<DetachedTab> DetachTabWithReasonAt(
      int index,
      TabStripModelChange::RemoveReason web_contents_remove_reason,
      tabs::TabInterface::DetachReason tab_detach_reason);

  // Performs all the work to detach a TabModel instance but avoids sending
  // most notifications. TabClosingAt() and TabDetachedAt() are sent because
  // observers are reliant on the selection model being accurate at the time
  // that TabDetachedAt() is called.
  std::unique_ptr<DetachedTab> DetachTabImpl(
      int index_before_any_removals,
      int index_at_time_of_removal,
      bool create_historical_tab,
      TabStripModelChange::RemoveReason web_contents_remove_reason,
      tabs::TabInterface::DetachReason tab_detach_reason);

  // Removes a tab collection from `contents_data_` using
  // `execute_detach_collection_operation`. Also sends collection specific
  // observation using `execute_tabs_notify_observer_operation` like group and
  // split related observation calls. `TabStripModelChange` and
  // `TabStripSelectionChange` observation calls are handled as common code.
  std::unique_ptr<tabs::TabCollection> DetachTabCollectionImpl(
      tabs::TabCollection* collection,
      base::OnceCallback<std::unique_ptr<tabs::TabCollection>()>
          execute_detach_collection_operation,
      base::OnceClosure execute_tabs_notify_observer_operation);

  // Helper method performing tasks like notification, fixing opener and
  // returning back a Remove struct before actually detaching the set of
  // tab_indices.
  TabStripModelChange::Remove ProcessTabsForDetach(gfx::Range tab_indices);

  // Helper method for updating the selection model after detaching a collection
  // from `contents_data_`.
  void UpdateSelectionModelForDetach(gfx::Range tab_indices,
                                     std::optional<int> next_selected_index);

  // Attaches a tab collection to `contents_data_` using
  // `execute_insert_detached_tabs_operation`. Also sends collection specific
  // observation using `execute_tabs_notify_observer_operation` like group and
  // split related observation calls. `TabStripModelChange` and
  // `TabStripSelectionChange` observation calls are handled as common code.
  gfx::Range InsertDetachedCollectionImpl(
      tabs::TabCollection* collection,
      std::optional<int> active_index,
      base::OnceClosure execute_insert_detached_tabs_operation,
      base::OnceClosure execute_tabs_notify_observer_operation);

  // This is the callback used as `execute_insert_detached_tabs_operation` in
  // `InsertDetachedCollectionImpl` when a group is inserted into a tabstrip. It
  // updates the `group_model_` and inserts the `group_collection` into
  // `contents_data_`.
  void InsertDetachedTabGroupImpl(
      std::unique_ptr<tabs::TabGroupTabCollection> group_collection,
      int index);

  // We batch send notifications. This has two benefits:
  //   1) This allows us to send the minimal number of necessary notifications.
  //   This is important because some notifications cause the main thread to
  //   synchronously communicate with the GPU process and cause jank.
  //   https://crbug.com/826287.
  //   2) This allows us to avoid some problems caused by re-entrancy [e.g.
  //   using destroyed WebContents instances]. Ideally, this second check
  //   wouldn't be necessary because we would enforce that there is no
  //   re-entrancy in the TabStripModel, but that condition is currently
  //   violated in tests [and possibly in the wild as well].
  void SendDetachWebContentsNotifications(DetachNotifications* notifications);

  bool RunUnloadListenerBeforeClosing(content::WebContents* contents);
  bool ShouldRunUnloadListenerBeforeClosing(content::WebContents* contents);

  int ConstrainInsertionIndex(int index, bool pinned_tab) const;

  int ConstrainMoveIndex(int index, bool pinned_tab) const;

  // If |index| is selected all the selected indices are returned, otherwise a
  // vector with |index| is returned. This is used when executing commands to
  // determine which indices the command applies to. Indices are sorted in
  // increasing order.
  std::vector<int> GetIndicesForCommand(int index) const;

  // Returns a vector of indices of the tabs that will close when executing the
  // command |id| for the tab at |index|. The returned indices are sorted in
  // descending order.
  std::vector<int> GetIndicesClosedByCommand(int index,
                                             ContextMenuCommand id) const;

  // Returns true if the specified WebContents is a New Tab at the end of
  // the tabstrip. We check for this because opener relationships are _not_
  // forgotten for the New Tab page opened as a result of a New Tab gesture
  // (e.g. Ctrl+T, etc) since the user may open a tab transiently to look up
  // something related to their current activity.
  bool IsNewTabAtEndOfTabStrip(content::WebContents* contents) const;

  // Adds the specified TabModel at the specified location.
  // |add_types| is a bitmask of AddTabTypes; see it for details.
  //
  // All append/insert methods end up in this method.
  //
  // NOTE: adding a tab using this method does NOT query the order controller,
  // as such the ADD_FORCE_INDEX AddTabTypes is meaningless here. The only time
  // the |index| is changed is if using the index would result in breaking the
  // constraint that all pinned tabs occur before non-pinned tabs. It returns
  // the index the tab is actually inserted to. See also AddWebContents.
  int InsertTabAtImpl(int index,
                      std::unique_ptr<tabs::TabModel> tab,
                      int add_types,
                      std::optional<tab_groups::TabGroupId> group);

  // Closes the WebContentses at the specified indices. This causes the
  // WebContentses to be destroyed, but it may not happen immediately. If
  // the page in question has an unload event the WebContents will not be
  // destroyed until after the event has completed, which will then call back
  // into this method.
  void CloseTabs(base::span<content::WebContents* const> items,
                 uint32_t close_types);

  // Executes a call to CloseTabs on the web contentses contained in tabs
  // returned from |get_indices_to_close|. This is a helper method
  // bound by ExecuteCloseTabsByIndicesCommand in order to properly
  // protect the stack from reentrancy.
  void ExecuteCloseTabsByIndices(
      base::RepeatingCallback<std::vector<int>()> get_indices_to_close,
      uint32_t close_types);

  // |close_types| is a bitmask of the types in CloseTypes.
  // Returns true if all the tabs have been deleted. A return value of false
  // means some portion (potentially none) of the WebContents were deleted.
  // WebContents not deleted by this function are processing unload handlers
  // which may eventually be deleted based on the results of the unload handler.
  // Additionally processing the unload handlers may result in needing to show
  // UI for the WebContents. See UnloadController for details on how unload
  // handlers are processed.
  bool CloseWebContentses(base::span<content::WebContents* const> items,
                          uint32_t close_types,
                          DetachNotifications* notifications);

  // Returns the WebContentses at the specified indices. This does no checking
  // of the indices, it is assumed they are valid.
  std::vector<content::WebContents*> GetWebContentsesByIndices(
      const std::vector<int>& indices) const;

  // Sets the selection to |new_model| and notifies any observers.
  // Note: This function might end up sending 0 to 3 notifications in the
  // following order: TabDeactivated, ActiveTabChanged, TabSelectionChanged.
  // |selection| will be filled with information corresponding to 3 notification
  // above. When it's |triggered_by_other_operation|, This won't notify
  // observers that selection was changed. Callers should notify it by
  // themselves.
  TabStripSelectionChange SetSelection(
      ui::ListSelectionModel new_model,
      TabStripModelObserver::ChangeReason reason,
      bool triggered_by_other_operation);

  // Direction of relative tab movements or selections. kNext indicates moving
  // forward (positive increment) in the tab strip. kPrevious indicates
  // backward (negative increment).
  enum class TabRelativeDirection {
    kNext,
    kPrevious,
  };

  // Selects either the next tab (kNext), or the previous tab (kPrevious).
  void SelectRelativeTab(TabRelativeDirection direction,
                         TabStripUserGestureDetails detail);

  // Moves the active tabs into the next slot (kNext), or the
  // previous slot (kPrevious). Respects group boundaries and creates
  // movement slots into and out of groups.
  void MoveTabRelative(TabRelativeDirection direction);

  // Implementation of MoveSelectedTabsTo. Moves |length| of the selected tabs
  // starting at |start| to |index|. See MoveSelectedTabsTo for more details.
  void MoveSelectedTabsToImpl(int index, size_t start, size_t length);

  std::vector<int> GetSelectedPinnedTabs();
  std::vector<int> GetSelectedUnpinnedTabs();

  split_tabs::SplitTabId AddToSplitImpl(
      split_tabs::SplitTabId split_id,
      std::vector<int> indices,
      split_tabs::SplitTabVisualData visual_data,
      SplitTabChange::SplitTabAddReason reasons);

  void RemoveSplitImpl(split_tabs::SplitTabId split_id,
                       SplitTabChange::SplitTabRemoveReason reason);

  void UpdateTabInSplitImpl(tabs::TabInterface* split_tab,
                            int update_index,
                            SplitUpdateType update_type);

  // Adds tabs to newly-allocated group id |new_group|. This group must be new
  // and have no tabs in it.
  void AddToNewGroupImpl(
      const std::vector<int>& indices,
      const tab_groups::TabGroupId& new_group,
      std::optional<tab_groups::TabGroupVisualData> visual_data = std::nullopt);

  void MoveGroupToImpl(const tab_groups::TabGroupId& group, int to_index);

  // Adds tabs to existing group |group|. This group must have been initialized
  // by a previous call to |AddToNewGroupImpl()|.
  void AddToExistingGroupImpl(const std::vector<int>& indices,
                              const tab_groups::TabGroupId& group,
                              const bool add_to_end = false);

  // Adds all selected indices provided by `context_index` into a new tab group.
  void AddToNewGroupFromContextIndex(int context_index);

  // Implementation of MoveTabsAndSetPropertiesImpl. Moves the set of tabs in
  // |indices| to the |destination_index| and updates the tabs to the
  // appropriate |group| and |pinned| properties.
  // Note: |destination_index| refers to a place in the tabstrip prior to the
  // move operation.
  void MoveTabsAndSetPropertiesImpl(const std::vector<int>& indices,
                                    int destination_index,
                                    std::optional<tab_groups::TabGroupId> group,
                                    bool pinned);

  void AddToReadLaterImpl(const std::vector<int>& indices);

  // Updates the `contents_data_` and sends out observer notifications for
  // inserting a new tab in  the tabstrip.
  void InsertTabAtIndexImpl(std::unique_ptr<tabs::TabModel> tab_model,
                            int index,
                            std::optional<tab_groups::TabGroupId> group,
                            bool pin,
                            bool active);

  // Updates the `contents_data_` and sends out observer notifications for
  // removing an existing tab in  the tabstrip.
  std::unique_ptr<tabs::TabModel> RemoveTabFromIndexImpl(
      int index,
      tabs::TabInterface::DetachReason tab_detach_reason);

  // Updates the `contents_data_` and sends out observer notifications for
  // updating the index, pinned state or group property.
  void MoveTabToIndexImpl(int initial_index,
                          int final_index,
                          const std::optional<tab_groups::TabGroupId> group,
                          bool pin,
                          bool select_after_move);

  // Similar to `MoveTabToIndexImpl` but this is used for multiple tabs either
  // being moved or having their group updated. `tab_indices` should be sorted.
  // Tabs are inserted at `destination_index` after they are removed.
  void MoveTabsToIndexImpl(const std::vector<int>& tab_indices,
                           int destination_index,
                           const std::optional<tab_groups::TabGroupId> group);

  // Sends group notifications for a tab at `index` based on its initial_group
  // and `final_group` and updates the `group_model_`.
  void TabGroupStateChanged(
      int index,
      tabs::TabInterface* tab,
      const std::optional<tab_groups::TabGroupId> initial_group,
      const std::optional<tab_groups::TabGroupId> new_group);

  // Updates the `group_model` by decrementing the tab count of `group`.
  void RemoveTabFromGroupModel(const tab_groups::TabGroupId& group);

  // Updates the `group_model` by incrementing the tab count of `group`.
  void AddTabToGroupModel(const tab_groups::TabGroupId& group);

  // Checks if the `contents_data_` is in a valid order. This checks for
  // pinned tabs placement, group contiguity and selected tabs validity.
  void ValidateTabStripModel();

  void SendMoveNotificationForTab(
      int index,
      int to_position,
      tabs::TabInterface* tab,
      const TabStripSelectionChange& selection_change);

  void UpdateSelectionModelForMove(int initial_index,
                                   int final_index,
                                   bool select_after_move);

  void UpdateSelectionModelForMoves(const std::vector<int>& tab_indices,
                                    int destination_index);

  // Clears any previous selection and sets the selected index. This takes into
  // account split tabs so both will be selected if `index` is a split tab.
  void SetSelectedIndex(ui::ListSelectionModel* selection, int index);

  // Returns the range of indices between the anchor and a provided index, that
  // takes into account split tabs. If the anchor or the tab at index is part of
  // a split, the range will include that split. The start and end indices are
  // inclusive.
  std::pair<int, int> GetSelectionRangeFromAnchorToIndex(int index);

  // Generates the MoveNotifications for `MoveTabsToIndexImpl` and updates the
  // selection model and openers.
  std::vector<TabStripModel::MoveNotification> PrepareTabsToMoveToIndex(
      const std::vector<int>& tab_indices,
      int destination_index);

  // Generates a sequence of initial and destination index for tabs in
  // `tab_indices` when the tabs need to move to `destination_index`.
  std::vector<std::pair<int, int>> CalculateIncrementalTabMoves(
      const std::vector<int>& tab_indices,
      int destination_index) const;

  // Changes the pinned state of all tabs at `indices`, moving them in the
  // process if necessary. If indices contains all tabs in a split, the whole
  // split is pinned/unpinned. Otherwise, the tabs will be individually
  // processed, resulting in the split being unsplit.
  void SetTabsPinned(const std::vector<int> indices, bool pinned);

  // Implementation for setting the pinned state of the tab at `index`.
  int SetTabPinnedImpl(int indices, bool pinned);

  // Changes the pinned state of a split collection, moving it in the process if
  // necessary.
  void SetSplitPinnedImpl(tabs::SplitTabCollection* split, bool pinned);

  // Wrapper for bulk move operations to make them send out the appropriate
  // change notifications.
  void MoveTabsWithNotifications(std::vector<int> tab_indices,
                                 int destination_index,
                                 base::OnceClosure execute_tabs_move_operation);

  // Sets the sound content setting for each site at the |indices|.
  void SetSitesMuted(const std::vector<int>& indices, bool mute) const;

  // Sets the opener of any tabs that reference the tab at |index| to that tab's
  // opener or null if there's a cycle.
  void FixOpeners(int index);

  // Returns a group when the index of a tab is updated from `index` to
  // `to_position` that would not break group contiguity. The group returned
  // keeps the original group if it is valid at the `to_position` and otherwise
  // returns a valid group.
  std::optional<tab_groups::TabGroupId> GetGroupToAssign(int index,
                                                         int to_position);

  // Private API for now, because this API can be difficult to use correctly.
  // Interim stop gap until we have a handle based API. Use PassKey to access
  // this.
  // One notable deficiencies is that it doesn't work for all tab collection
  // types (e.g.: unpinned collection, tab strip collection, and split tab
  // collection). The onus is on the caller to handle those cases correctly.
  std::optional<const tab_groups::TabGroupId> FindGroupIdFor(
      const tabs::TabCollection::Handle& collection_handle) const;

  // Returns a valid index to be selected after the tabs in `block_tabs` are
  // closed. If index is after the block, index is adjusted to reflect the fact
  // that the block is going away.
  int GetTabIndexAfterClosing(int index, const gfx::Range& block_tabs) const;

  // Takes the |selection| change and decides whether to forget the openers.
  void OnActiveTabChanged(const TabStripSelectionChange& selection);

  // Checks if policy allows a tab to be closed.
  bool PolicyAllowsTabClosing(content::WebContents* contents) const;

  // Determine where to shift selection after a tab or collection is closed.
  std::optional<int> DetermineNewSelectedIndex(
      std::variant<tabs::TabInterface*, tabs::TabCollection*> tab_or_collection)
      const;

  std::vector<std::pair<tabs::TabInterface*, int>> GetTabsAndIndicesInSplit(
      split_tabs::SplitTabId split_id);

  // Returns [start, end) where the leftmost tab in the split has index start
  // and the rightmost tab in the split has index end - 1.
  gfx::Range GetIndexRangeOfSplit(split_tabs::SplitTabId split_id) const;

  // If inserting at `index` breaks a split, returns its id, otherwise nullopt.
  std::optional<split_tabs::SplitTabId> InsertionBreaksSplitContiguity(
      int index);

  // Helper to determine if moving a block of tabs from `start_index` with block
  // size `length` to `final_index` breaks contiguity.
  std::optional<split_tabs::SplitTabId>
  MoveBreaksSplitContiguity(int start_index, int length, int final_index);

  void MaybeRemoveSplitsForMove(
      int initial_index,
      int final_index,
      const std::optional<tab_groups::TabGroupId> group,
      bool pin);

  // The WebContents data currently hosted within this TabStripModel. This must
  // be kept in sync with |selection_model_|.
  std::unique_ptr<tabs::TabStripCollection> contents_data_;

  // The model for tab groups hosted within this TabStripModel.
  std::unique_ptr<TabGroupModel> group_model_;

  raw_ptr<TabStripModelDelegate> delegate_;

  bool tab_strip_ui_was_set_ = false;

  base::ObserverList<TabStripModelObserver>::UncheckedAndDanglingUntriaged
      observers_;

  // A profile associated with this TabStripModel.
  raw_ptr<Profile, AcrossTasksDanglingUntriaged> profile_;

  // True if all tabs are currently being closed via CloseAllTabs.
  bool closing_all_ = false;

  // This must be kept in sync with |contents_data_|.
  ui::ListSelectionModel selection_model_;

  // TabStripModel is not re-entrancy safe. This member is used to guard public
  // methods that mutate state of |selection_model_| or |contents_data_|.
  bool reentrancy_guard_ = false;

  TabStripScrubbingMetrics scrubbing_metrics_;

  // Tracks whether a modal UI is showing.
  bool showing_modal_ui_ = false;

  base::WeakPtrFactory<TabStripModel> weak_factory_{this};
};

// Forbid construction of ScopedObservation and ScopedMultiSourceObservation
// with TabStripModel: TabStripModelObserver already implements their
// functionality natively.
namespace base {

template <>
class ScopedObservation<TabStripModel, TabStripModelObserver> {
 public:
  // Deleting the constructor gives a clear error message traceable back to
  // here.
  explicit ScopedObservation(TabStripModelObserver* observer) = delete;
};

template <>
class ScopedMultiSourceObservation<TabStripModel, TabStripModelObserver> {
 public:
  // Deleting the constructor gives a clear error message traceable back to
  // here.
  explicit ScopedMultiSourceObservation(TabStripModelObserver* observer) =
      delete;
};

}  // namespace base

#endif  // CHROME_BROWSER_UI_TABS_TAB_STRIP_MODEL_H_