chromium/ios/chrome/browser/ui/tab_switcher/tab_strip/coordinator/tab_strip_mediator_utils.mm

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/ui/tab_switcher/tab_strip/coordinator/tab_strip_mediator_utils.h"

#import "base/check.h"
#import "ios/chrome/browser/saved_tab_groups/model/ios_tab_group_sync_util.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/web_state_list/browser_util.h"
#import "ios/chrome/browser/shared/model/web_state_list/tab_group.h"
#import "ios/chrome/browser/shared/model/web_state_list/tab_utils.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_strip/ui/swift.h"
#import "ios/chrome/browser/ui/tab_switcher/web_state_tab_switcher_item.h"

TabStripItemIdentifier* CreateTabItemIdentifier(web::WebState* web_state) {
  TabSwitcherItem* tab_item =
      [[WebStateTabSwitcherItem alloc] initWithWebState:web_state];
  TabStripItemIdentifier* tab_item_identifier =
      [TabStripItemIdentifier tabIdentifier:tab_item];
  return tab_item_identifier;
}

TabStripItemIdentifier* CreateGroupItemIdentifier(
    const TabGroup* group,
    WebStateList* web_state_list) {
  TabGroupItem* group_item =
      [[TabGroupItem alloc] initWithTabGroup:group webStateList:web_state_list];
  TabStripItemIdentifier* group_item_identifier =
      [TabStripItemIdentifier groupIdentifier:group_item];
  return group_item_identifier;
}

void MoveGroupBeforeTabStripItem(const TabGroup* group,
                                 TabStripItemIdentifier* next_item_identifier,
                                 Browser* destination_browser) {
  CHECK(destination_browser);
  WebStateList* web_state_list = destination_browser->GetWebStateList();
  CHECK(web_state_list);

  // By default, the group is moved to the end of the WebStateList.
  int web_state_list_index_after_update = web_state_list->count();

  // If there is a next item, the group should be moved before that item.
  if (next_item_identifier) {
    switch (next_item_identifier.itemType) {
      case TabStripItemTypeTab: {
        // Moving the group item before a tab item.
        TabSwitcherItem* tab_switcher_item =
            next_item_identifier.tabSwitcherItem;
        int web_state_index = GetWebStateIndex(
            web_state_list,
            WebStateSearchCriteria{.identifier = tab_switcher_item.identifier});
        CHECK(web_state_list->ContainsIndex(web_state_index),
              base::NotFatalUntil::M128);
        const TabGroup* group_of_web_state =
            web_state_list->GetGroupOfWebStateAt(web_state_index);
        // In case the WebState before which the group is being inserted is
        // already in a group itself, moving the insertion index to the
        // beginning of that group.
        web_state_list_index_after_update =
            group_of_web_state ? group_of_web_state->range().range_begin()
                               : web_state_index;
        break;
      }
      case TabStripItemTypeGroup: {
        // Moving the group item before another group item.
        TabGroupItem* tab_group_item = next_item_identifier.tabGroupItem;
        CHECK(tab_group_item.tabGroup);
        web_state_list_index_after_update =
            tab_group_item.tabGroup->range().range_begin();
        break;
      }
    }
  }

  // If destination WebStateList already contains the group, call `MoveGroup`
  // directly. Otherwise, use `MoveTabGroupToBrowser` to first move the group to
  // `destination_browser`.
  if (web_state_list->ContainsGroup(group)) {
    web_state_list->MoveGroup(group, web_state_list_index_after_update);
  } else {
    tab_groups::utils::MoveTabGroupToBrowser(group, destination_browser,
                                             web_state_list_index_after_update);
  }
}