// Copyright 2013 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/profile_resetter/profile_resetter.h"

#include <stddef.h>

#include <array>
#include <optional>
#include <string>
#include <vector>

#include "base/functional/bind.h"
#include "base/synchronization/atomic_flag.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_constants.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/file_system_access/chrome_file_system_access_permission_context.h"
#include "chrome/browser/file_system_access/file_system_access_permission_context_factory.h"
#include "chrome/browser/google/google_brand.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/profile_resetter/brandcode_config_fetcher.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/background/ntp_custom_background_service.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/translate/chrome_translate_client.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h"
#include "chrome/common/pref_names.h"
#include "components/browsing_data/content/browsing_data_helper.h"
#include "components/content_settings/core/browser/content_settings_info.h"
#include "components/content_settings/core/browser/content_settings_registry.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/browser/website_settings_info.h"
#include "components/content_settings/core/browser/website_settings_registry.h"
#include "components/language/core/browser/language_prefs.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/search_engines/enterprise/enterprise_search_manager.h"
#include "components/search_engines/search_engines_pref_names.h"
#include "components/search_engines/template_url_prepopulate_data.h"
#include "components/search_engines/template_url_service.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/storage_partition.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/management_policy.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/manifest.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "ash/constants/ash_pref_names.h"
#include "chrome/browser/ash/input_method/input_method_manager_impl.h"
#include "chromeos/ash/components/network/managed_network_configuration_handler.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "components/language/core/browser/pref_names.h"
#include "components/proxy_config/proxy_config_pref_names.h"
#include "components/spellcheck/browser/pref_names.h"
#endif  // BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_WIN)
#include "base/base_paths.h"
#include "base/path_service.h"
#include "chrome/installer/util/shell_util.h"

namespace {

void ResetShortcutsOnBlockingThread() {
  // Get full path of chrome.
  base::FilePath chrome_exe;
  if (!base::PathService::Get(base::FILE_EXE, &chrome_exe))
    return;

  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                base::BlockingType::MAY_BLOCK);
  for (int location = ShellUtil::SHORTCUT_LOCATION_FIRST;
       location <= ShellUtil::SHORTCUT_LOCATION_LAST; ++location) {
    ShellUtil::ShortcutListMaybeRemoveUnknownArgs(
        static_cast<ShellUtil::ShortcutLocation>(location),
        ShellUtil::CURRENT_USER, chrome_exe, true, nullptr, nullptr);
    ShellUtil::ResetShortcutFileAttributes(
        static_cast<ShellUtil::ShortcutLocation>(location),
        ShellUtil::CURRENT_USER, chrome_exe);
  }
}

}  // namespace
#endif  // BUILDFLAG(IS_WIN)

ProfileResetter::ProfileResetter(Profile* profile)
    : profile_(profile),
      template_url_service_(TemplateURLServiceFactory::GetForProfile(profile_)),
      pending_reset_flags_(0),
      cookies_remover_(nullptr) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(profile_);

  google_brand::GetBrand(&brandcode_);
  if (brandcode_.empty()) {
    return;
  }
  config_fetcher_ = std::make_unique<BrandcodeConfigFetcher>(
      g_browser_process->system_network_context_manager()
          ->GetURLLoaderFactory(),
      base::BindOnce(&ProfileResetter::OnDefaultSettingsFetched,
                     base::Unretained(this)),
      GURL("https://tools.google.com/service/update2"), brandcode_);
}

void ProfileResetter::OnDefaultSettingsFetched() {
  CHECK(config_fetcher_);
  DCHECK(!config_fetcher_->IsActive());
}

ProfileResetter::~ProfileResetter() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (cookies_remover_)
    cookies_remover_->RemoveObserver(this);
}

void ProfileResetter::ResetSettings(
    ProfileResetter::ResettableFlags resettable_flags,
    std::unique_ptr<BrandcodedDefaultSettings> master_settings,
    base::OnceClosure callback) {
  // TODO(b/364615847) remove master_settings parameter, it is only used in
  // tests.
  DCHECK(brandcode_.empty() || config_fetcher_);
  if (config_fetcher_ && config_fetcher_->IsActive()) {
    // Reset once the prefs are fetched.
    config_fetcher_->SetCallback(base::BindOnce(
        &ProfileResetter::ResetSettings, base::Unretained(this),
        resettable_flags, std::move(master_settings), std::move(callback)));
    return;
  }
  if (!master_settings) {
    if (config_fetcher_) {
      DCHECK(!config_fetcher_->IsActive());
      master_settings = config_fetcher_->GetSettings();
      config_fetcher_.reset();
    } else {
      DCHECK(brandcode_.empty());
    }

    // If failed to fetch BrandcodedDefaultSettings or this is an organic
    // installation, use default settings.
    if (!master_settings) {
      master_settings = std::make_unique<BrandcodedDefaultSettings>();
    }
  }

  ResetSettingsImpl(resettable_flags, std::move(master_settings),
                    std::move(callback));
}

void ProfileResetter::ResetSettingsImpl(
    ProfileResetter::ResettableFlags resettable_flags,
    std::unique_ptr<BrandcodedDefaultSettings> master_settings,
    base::OnceClosure callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(master_settings);

  // We should never be called with unknown flags.
  CHECK_EQ(static_cast<ResettableFlags>(0), resettable_flags & ~ALL);

  // We should never be called when a previous reset has not finished.
  CHECK_EQ(static_cast<ResettableFlags>(0), pending_reset_flags_);

  if (!resettable_flags) {
    content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
                                                 std::move(callback));
    return;
  }

  master_settings_.swap(master_settings);
  callback_ = std::move(callback);

  // These flags are set to false by the individual reset functions.
  pending_reset_flags_ = resettable_flags;

  struct FlagToMethod {
    Resettable flag;
    void (ProfileResetter::*method)();
  };
  auto flagToMethod = std::to_array<FlagToMethod>({
      // Ordering of resets does matter here, extensions resets should
      // always precede DNS and proxy resets as the former can impact
      // the latter.
      {DEFAULT_SEARCH_ENGINE, &ProfileResetter::ResetDefaultSearchEngine},
      {HOMEPAGE, &ProfileResetter::ResetHomepage},
      {CONTENT_SETTINGS, &ProfileResetter::ResetContentSettings},
      {COOKIES_AND_SITE_DATA, &ProfileResetter::ResetCookiesAndSiteData},
      {EXTENSIONS, &ProfileResetter::ResetExtensions},
      {STARTUP_PAGES, &ProfileResetter::ResetStartupPages},
      {PINNED_TABS, &ProfileResetter::ResetPinnedTabs},
      {SHORTCUTS, &ProfileResetter::ResetShortcuts},
      {NTP_CUSTOMIZATIONS, &ProfileResetter::ResetNtpCustomizations},
      {LANGUAGES, &ProfileResetter::ResetLanguages},
#if BUILDFLAG(IS_CHROMEOS)
      {DNS_CONFIGURATIONS, &ProfileResetter::ResetDnsConfigurations},
      {PROXY_SETTINGS, &ProfileResetter::ResetProxySettings},
      {KEYBOARD_SETTINGS, &ProfileResetter::ResetKeyboardInputSettings},
#endif  // BUILDFLAG(IS_CHROMEOS)
  });

  ResettableFlags reset_triggered_for_flags = 0;
  for (size_t i = 0; i < std::size(flagToMethod); ++i) {
    if (resettable_flags & flagToMethod[i].flag) {
      reset_triggered_for_flags |= flagToMethod[i].flag;
      (this->*flagToMethod[i].method)();
    }
  }

  DCHECK_EQ(resettable_flags, reset_triggered_for_flags);
}

bool ProfileResetter::IsActive() const {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  return pending_reset_flags_ != 0;
}

void ProfileResetter::MarkAsDone(Resettable resettable) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  // Check that we are never called twice or unexpectedly.
  CHECK(pending_reset_flags_ & resettable);

  pending_reset_flags_ &= ~resettable;

  if (!pending_reset_flags_) {
    content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
                                                 std::move(callback_));
    master_settings_.reset();
    template_url_service_subscription_ = {};
  }
}

void ProfileResetter::ResetDefaultSearchEngine() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(template_url_service_);
  // If TemplateURLServiceFactory is ready we can clean it right now.
  // Otherwise, load it and continue from ProfileResetter::Observe.
  if (template_url_service_->loaded()) {
    PrefService* prefs = profile_->GetPrefs();
    DCHECK(prefs);
    TemplateURLPrepopulateData::ClearPrepopulatedEnginesInPrefs(
        profile_->GetPrefs());
    std::optional<base::ListValue> search_engines(
        master_settings_->GetSearchProviderOverrides());
    if (search_engines.has_value()) {
      // This Chrome distribution channel provides a custom search engine. We
      // must reset to it.
      prefs->SetList(prefs::kSearchProviderOverrides,
                     std::move(search_engines).value());
    }

    template_url_service_->RepairPrepopulatedSearchEngines();
    template_url_service_->RepairStarterPackEngines();
    template_url_service_->RemoveUserAddedTemplateURLs();
    // Clearing user overrides restores recommended policy-defined site search
    // engines that were deleted or modified by the user.
    prefs->ClearPref(
        EnterpriseSearchManager::kSiteSearchSettingsOverriddenKeywordsPrefName);

    MarkAsDone(DEFAULT_SEARCH_ENGINE);
  } else {
    template_url_service_subscription_ =
        template_url_service_->RegisterOnLoadedCallback(
            base::BindOnce(&ProfileResetter::OnTemplateURLServiceLoaded,
                           weak_ptr_factory_.GetWeakPtr()));
    template_url_service_->Load();
  }
}

void ProfileResetter::ResetHomepage() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  PrefService* prefs = profile_->GetPrefs();
  DCHECK(prefs);
  std::string homepage;

  if (master_settings_->GetHomepage(&homepage))
    prefs->SetString(prefs::kHomePage, homepage);

  std::optional<bool> homepage_is_ntp = master_settings_->GetHomepageIsNewTab();
  if (homepage_is_ntp.has_value())
    prefs->SetBoolean(prefs::kHomePageIsNewTabPage, *homepage_is_ntp);
  else
    prefs->ClearPref(prefs::kHomePageIsNewTabPage);

  std::optional<bool> show_home_button = master_settings_->GetShowHomeButton();
  if (show_home_button.has_value())
    prefs->SetBoolean(prefs::kShowHomeButton, *show_home_button);
  else
    prefs->ClearPref(prefs::kShowHomeButton);
  MarkAsDone(HOMEPAGE);
}

void ProfileResetter::ResetContentSettings() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  HostContentSettingsMap* map =
      HostContentSettingsMapFactory::GetForProfile(profile_);

  for (const content_settings::WebsiteSettingsInfo* info :
       *content_settings::WebsiteSettingsRegistry::GetInstance()) {
    map->ClearSettingsForOneType(info->type());
  }

  // TODO(raymes): The default value isn't really used for website settings
  // right now, but if it were we should probably reset that here too.
  for (const content_settings::ContentSettingsInfo* info :
       *content_settings::ContentSettingsRegistry::GetInstance()) {
    map->SetDefaultContentSetting(info->website_settings_info()->type(),
                                  CONTENT_SETTING_DEFAULT);
  }

  // Active File System Access grants are kept in memory by the permission
  // context rather than in HostContentSettingsMap, so they need to be revoked
  // explicitly.
  if (auto* permission_context =
          FileSystemAccessPermissionContextFactory::GetForProfile(profile_)) {
    permission_context->RevokeAllActiveGrants();
  }

  profile_->ForEachLoadedStoragePartition(
      [](content::StoragePartition* partition) {
        partition->ClearBluetoothAllowedDevicesMap();
      });

  MarkAsDone(CONTENT_SETTINGS);
}

void ProfileResetter::ResetCookiesAndSiteData() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(!cookies_remover_);

  cookies_remover_ = profile_->GetBrowsingDataRemover();
  cookies_remover_->AddObserver(this);
  uint64_t remove_mask = chrome_browsing_data_remover::DATA_TYPE_SITE_DATA |
                         content::BrowsingDataRemover::DATA_TYPE_CACHE;
  PrefService* prefs = profile_->GetPrefs();
  DCHECK(prefs);

  cookies_remover_->RemoveAndReply(
      base::Time(), base::Time::Max(), remove_mask,
      content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB, this);
}

void ProfileResetter::ResetExtensions() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  std::vector<std::string> brandcode_extensions;
  master_settings_->GetExtensions(&brandcode_extensions);

  extensions::ExtensionService* extension_service =
      extensions::ExtensionSystem::Get(profile_)->extension_service();
  DCHECK(extension_service);
  extension_service->DisableUserExtensionsExcept(brandcode_extensions);

  // Reenable all disabled external component extensions.
  // BrandcodedDefaultSettings does not contain information about component
  // extensions, so fetch them from the existing registry. This may be not very
  // robust, as the profile resetter may be invoked when the registry is in some
  // iffy state. However, we can't enable an extension which is not in the
  // registry anyway.
  extensions::ExtensionRegistry* extension_registry =
      extensions::ExtensionRegistry::Get(profile_);
  DCHECK(extension_registry);
  std::vector<extensions::ExtensionId> extension_ids_to_reenable;
  for (const auto& extension : extension_registry->disabled_extensions()) {
    if (extension->location() ==
        extensions::mojom::ManifestLocation::kExternalComponent)
      extension_ids_to_reenable.push_back(extension->id());
  }
  auto* extension_registrar = extensions::ExtensionRegistrar::Get(profile_);
  for (const auto& extension_id : extension_ids_to_reenable) {
    extension_registrar->EnableExtension(extension_id);
  }

  MarkAsDone(EXTENSIONS);
}

void ProfileResetter::ResetStartupPages() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  PrefService* prefs = profile_->GetPrefs();
  DCHECK(prefs);
  std::optional<base::ListValue> url_list(
      master_settings_->GetUrlsToRestoreOnStartup());
  if (url_list.has_value()) {
    prefs->SetList(prefs::kURLsToRestoreOnStartup, std::move(url_list).value());
  }

  int restore_on_startup;
  if (master_settings_->GetRestoreOnStartup(&restore_on_startup))
    prefs->SetInteger(prefs::kRestoreOnStartup, restore_on_startup);
  else
    prefs->ClearPref(prefs::kRestoreOnStartup);

  MarkAsDone(STARTUP_PAGES);
}

void ProfileResetter::ResetPinnedTabs() {
  // Unpin all the tabs.
  ForEachCurrentBrowserWindowInterfaceOrderedByActivation(
      [this](BrowserWindowInterface* browser) {
        if (browser->GetType() == BrowserWindowInterface::TYPE_NORMAL &&
            browser->GetProfile() == profile_) {
          TabStripModel* const tab_model = browser->GetTabStripModel();
          // Here we assume that indexof(any mini tab) < indexof(any normal
          // tab). If we unpin the tab, it can be moved to the right. Thus
          // traversing in reverse direction is correct.
          for (int i = tab_model->count() - 1; i >= 0; --i) {
            if (tab_model->IsTabPinned(i)) {
              tab_model->SetTabPinned(i, false);
            }
          }
        }
        return true;
      });
  MarkAsDone(PINNED_TABS);
}

void ProfileResetter::ResetShortcuts() {
#if BUILDFLAG(IS_WIN)
  base::ThreadPool::CreateCOMSTATaskRunner(
      {base::MayBlock(), base::TaskPriority::USER_VISIBLE})
      ->PostTaskAndReply(
          FROM_HERE, base::BindOnce(&ResetShortcutsOnBlockingThread),
          base::BindOnce(&ProfileResetter::MarkAsDone,
                         weak_ptr_factory_.GetWeakPtr(), SHORTCUTS));
#else
  MarkAsDone(SHORTCUTS);
#endif
}

void ProfileResetter::ResetNtpCustomizations() {
  NtpCustomBackgroundService::ResetProfilePrefs(profile_);
  NewTabPageUI::ResetProfilePrefs(profile_->GetPrefs());
  MarkAsDone(NTP_CUSTOMIZATIONS);
}

void ProfileResetter::ResetLanguages() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  PrefService* prefs = profile_->GetPrefs();
  DCHECK(prefs);

  auto translate_prefs = ChromeTranslateClient::CreateTranslatePrefs(prefs);
  translate_prefs->ResetToDefaults();

  language::ResetLanguagePrefs(prefs);

  MarkAsDone(LANGUAGES);
}

void ProfileResetter::OnTemplateURLServiceLoaded() {
  // TemplateURLService has loaded. If we need to clean search engines, it's
  // time to go on.
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  template_url_service_subscription_ = {};
  if (pending_reset_flags_ & DEFAULT_SEARCH_ENGINE)
    ResetDefaultSearchEngine();
}

void ProfileResetter::OnBrowsingDataRemoverDone(uint64_t failed_data_types) {
  cookies_remover_->RemoveObserver(this);
  cookies_remover_ = nullptr;
  MarkAsDone(COOKIES_AND_SITE_DATA);
}

#if BUILDFLAG(IS_CHROMEOS)
void ProfileResetter::ResetDnsConfigurations() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  // Since certain extensions can modify DNS configurations we want
  // extensions to be reset beforehand.
  CHECK(!(pending_reset_flags_ & EXTENSIONS));

  ash::ManagedNetworkConfigurationHandler* network_configuration_handler =
      ash::NetworkHandler::Get()->managed_network_configuration_handler();
  if (!network_configuration_handler) {
    MarkAsDone(DNS_CONFIGURATIONS);
    return;
  }

  ash::NetworkStateHandler* network_state_handler =
      ash::NetworkHandler::Get()->network_state_handler();
  if (!network_state_handler) {
    MarkAsDone(DNS_CONFIGURATIONS);
    return;
  }

  // Fetch a list of all configured devices (Wifi, ethernet, etc.) for
  // a given profile.
  ash::NetworkStateHandler::NetworkStateList network_list;
  network_state_handler->GetNetworkListByType(
      ash::NetworkTypePattern::Default(), true /*configured_only*/,
      false /*visible_only*/, 0 /*no_limit*/, &network_list);

  // Use the list to reset DNS Configurations back to their default.
  for (const ash::NetworkState* network : network_list) {
    // Skip the network if the policy is managed. Unlikely to happen in
    // the backend, but still good to have as an extra check.
    if (network->IsManagedByPolicy()) {
      LOG(WARNING) << "Network is managed by policy: " << network->path();
      continue;
    }

    network_configuration_handler->ResetDNSProperties(network->path());
  }
  MarkAsDone(DNS_CONFIGURATIONS);
}

void ProfileResetter::ResetProxySettings() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  // Since certain extensions can modify proxy settings we want
  // extensions to be reset beforehand.
  CHECK(!(pending_reset_flags_ & EXTENSIONS));

  PrefService* prefs = profile_->GetPrefs();
  CHECK(prefs);

  // Check that user profile isn't managed. Unlikely to happen in
  // the backend, but still good to have as an extra check.
  if (prefs->FindPreference(proxy_config::prefs::kUseSharedProxies)
          ->IsManaged()) {
    MarkAsDone(PROXY_SETTINGS);
    return;
  }

  // Call to reset Proxy prefs set by disabling "Allow proxies for shared
  // networks" in chrome://settings. Will always write to User Prefs (the only
  // modifiable store). If a value is already set in a PrefStore with precedence
  // over User Prefs, re-reading the value might not return the value you just
  // set. A list of known sources that override User Prefs:
  // Managed Prefs (cloud policy)
  // Supervised User Prefs (parental controls)
  // Extension Prefs (extension overrides)
  // Command-line Prefs (command-line overrides)
  prefs->SetBoolean(proxy_config::prefs::kUseSharedProxies, false);
  MarkAsDone(PROXY_SETTINGS);
}

void ProfileResetter::ResetKeyboardInputSettings() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  // Since spellcheck settings depend on preferred languages, user language
  // preferences need to be reset beforehand.
  CHECK(!(pending_reset_flags_ & LANGUAGES));

  PrefService* prefs = profile_->GetPrefs();
  CHECK(prefs);

  // 1. Call to reset the language of the Input methods, from the current device
  // language.
  if (g_browser_process && g_browser_process->local_state()) {
    // Assume that the session will use the current UI locale.
    std::string locale = g_browser_process->GetApplicationLocale();

    // Derive kLanguagePreloadEngines from `locale`.
    // Uses the first input method as the most popular one.
    std::vector<std::string> input_method_ids;
    ash::input_method::InputMethodManager* manager =
        ash::input_method::InputMethodManager::Get();
    manager->GetInputMethodUtil()->GetInputMethodIdsFromLanguageCode(
        locale, ash::input_method::kAllInputMethods, &input_method_ids);
    // Save the input method in the user's preference kLanguagePreloadEngines.
    prefs->SetString(
        ash::prefs::kLanguagePreloadEngines,
        input_method_ids.empty() ? std::string() : input_method_ids[0]);
  }

  // 2. Call to reset spell check languages, matching the default language and
  // clearing the other options.
  prefs->SetList(spellcheck::prefs::kSpellCheckDictionaries,
                 base::ListValue().Append(
                     prefs->GetString(language::prefs::kPreferredLanguages)));

  MarkAsDone(KEYBOARD_SETTINGS);
}
#endif  // BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_WIN)
std::vector<ShortcutCommand> GetChromeLaunchShortcuts(
    const scoped_refptr<SharedCancellationFlag>& cancel) {
  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                base::BlockingType::MAY_BLOCK);
  // Get full path of chrome.
  base::FilePath chrome_exe;
  if (!base::PathService::Get(base::FILE_EXE, &chrome_exe))
    return std::vector<ShortcutCommand>();
  std::vector<ShortcutCommand> shortcuts;
  for (int location = ShellUtil::SHORTCUT_LOCATION_FIRST;
       location <= ShellUtil::SHORTCUT_LOCATION_LAST; ++location) {
    if (cancel.get() && cancel->data.IsSet())
      break;
    ShellUtil::ShortcutListMaybeRemoveUnknownArgs(
        static_cast<ShellUtil::ShortcutLocation>(location),
        ShellUtil::CURRENT_USER,
        chrome_exe,
        false,
        cancel,
        &shortcuts);
  }
  return shortcuts;
}
#endif  // BUILDFLAG(IS_WIN)
