chromium/ios/chrome/browser/tabs/model/tab_sync_util.mm

// Copyright 2023 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/tabs/model/tab_sync_util.h"

#import "base/time/time.h"
#import "ios/chrome/browser/synced_sessions/model/distant_session.h"
#import "ios/chrome/browser/synced_sessions/model/distant_tab.h"
#import "ios/chrome/browser/synced_sessions/model/synced_sessions.h"

namespace {

// The maxium number of sessions to look at.
const size_t kMaxSessionLength = 5;

// The maxium number of tabs to look at.
const unsigned int kMaxTabLength = 50;

}  // namespace

void CheckDistantTabsOrder(synced_sessions::SyncedSessions* synced_sessions) {
  base::Time previous_session_modified_time = base::Time::Now();
  for (size_t session_index = 0;
       session_index < synced_sessions->GetSessionCount(); ++session_index) {
    const synced_sessions::DistantSession* session =
        synced_sessions->GetSession(session_index);
    CHECK(previous_session_modified_time >= session->modified_time);
    previous_session_modified_time = session->modified_time;

    base::Time previous_modified_time = base::Time::Now();

    for (const auto& distant_tab : session->tabs) {
      CHECK(previous_modified_time >= distant_tab->modified_time);
      CHECK(distant_tab->modified_time >= distant_tab->last_active_time);
      previous_modified_time = distant_tab->modified_time;
    }
  }
}

LastActiveDistantTab GetLastActiveDistantTab(
    synced_sessions::SyncedSessions* synced_sessions,
    base::TimeDelta delta_threshold) {
  base::Time time_threshold = base::Time::Now() - delta_threshold;
  const synced_sessions::DistantTab* last_active_tab = nullptr;
  const synced_sessions::DistantSession* last_active_session = nullptr;

  const size_t max_sessions_to_consider =
      std::min(synced_sessions->GetSessionCount(), kMaxSessionLength);
  for (size_t session_index = 0; session_index < max_sessions_to_consider;
       ++session_index) {
    const synced_sessions::DistantSession* session =
        synced_sessions->GetSession(session_index);

    // Skip the session if its `modified_time` value doesn't meet the time
    // threshold.
    if (session->modified_time < time_threshold) {
      break;
    }

    unsigned int tab_index = 0;
    for (const auto& distant_tab : session->tabs) {
      if (tab_index++ == kMaxTabLength) {
        break;
      }

      // Tabs in sessions are sorted by their `modified_time` value.
      // Skip the session if the `modified_time` of the tab doesn't meet the
      // time threshold.
      if (distant_tab->modified_time < time_threshold) {
        break;
      }

      if (distant_tab->last_active_time > time_threshold) {
        time_threshold = distant_tab->last_active_time;
        last_active_tab = distant_tab.get();
        last_active_session = session;
      }
    }
  }

  CHECK_EQ(!!last_active_tab, !!last_active_session);
  return LastActiveDistantTab{
      .tab = last_active_tab,
      .session = last_active_session,
  };
}