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

#include "chrome/browser/sessions/session_restore_delegate.h"

#include <stddef.h>

#include <array>
#include <utility>

#include "base/compiler_specific.h"
#include "base/metrics/field_trial.h"
#include "base/strings/string_util.h"
#include "chrome/browser/performance_manager/public/background_tab_loading_policy.h"
#include "chrome/browser/sessions/session_restore_stats_collector.h"
#include "chrome/common/url_constants.h"
#include "components/tab_groups/tab_group_id.h"
#include "components/tab_groups/tab_group_visual_data.h"
#include "content/public/browser/web_contents.h"

namespace {

bool IsInternalPage(const GURL& url) {
  // There are many chrome:// UI URLs, but only look for the ones that users
  // are likely to have open. Most of the benefit is from the NTP URL.
  const auto kReloadableUrlPrefixes = std::to_array<const char*>({
      chrome::kChromeUIDownloadsURL,
      chrome::kChromeUIHistoryURL,
      chrome::kChromeUINewTabURL,
      chrome::kChromeUISettingsURL,
  });
  // Prefix-match against the table above.
  for (const char* prefix : kReloadableUrlPrefixes) {
    if (base::StartsWith(url.spec(), prefix)) {
      return true;
    }
  }
  return false;
}

}  // namespace

SessionRestoreDelegate::RestoredTab::RestoredTab(
    content::WebContents* contents,
    bool is_active,
    bool is_app,
    bool is_pinned,
    const std::optional<tab_groups::TabGroupId>& group,
    const std::optional<split_tabs::SplitTabId>& split)
    : contents_(contents->GetWeakPtr()),
      is_active_(is_active),
      is_app_(is_app),
      is_internal_page_(IsInternalPage(contents->GetLastCommittedURL())),
      is_pinned_(is_pinned),
      group_(group),
      split_(split) {}

SessionRestoreDelegate::RestoredTab::RestoredTab(const RestoredTab&) = default;

SessionRestoreDelegate::RestoredTab&
SessionRestoreDelegate::RestoredTab::operator=(const RestoredTab&) = default;

SessionRestoreDelegate::RestoredTab::~RestoredTab() = default;

bool SessionRestoreDelegate::RestoredTab::operator<(
    const RestoredTab& right) const {
  // Tab with internal web UI like NTP or Settings are good choices to
  // defer loading.
  if (is_internal_page_ != right.is_internal_page_)
    return !is_internal_page_;
  // Pinned tabs should be loaded first.
  if (is_pinned_ != right.is_pinned_)
    return is_pinned_;
  // Apps should be loaded before normal tabs.
  if (is_app_ != right.is_app_)
    return is_app_;
  // Finally, older tabs should be deferred first.
  return contents_->GetLastActiveTimeTicks() >
         right.contents_->GetLastActiveTimeTicks();
}

// static
void SessionRestoreDelegate::RestoreTabs(
    const std::vector<RestoredTab>& tabs,
    const base::TimeTicks& restore_started) {
  if (tabs.empty())
    return;

  SessionRestoreStatsCollector::GetOrCreateInstance(
      restore_started,
      std::make_unique<
          SessionRestoreStatsCollector::UmaStatsReportingDelegate>())
      ->TrackTabs(tabs);

  std::vector<content::WebContents*> web_contents_vector;
  web_contents_vector.reserve(tabs.size());
  for (const auto& tab : tabs) {
    CHECK(tab.contents());
    web_contents_vector.push_back(tab.contents());
  }
  performance_manager::policies::ScheduleLoadForRestoredTabs(
      std::move(web_contents_vector));
}
