// 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.

#include "chrome/browser/extensions/updater/extension_updater.h"

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

#include <algorithm>
#include <array>
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <utility>
#include <vector>

#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/strings/escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "base/version.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/extensions/fake_crx_installer.h"
#include "chrome/browser/extensions/mock_crx_installer.h"
#include "chrome/browser/extensions/sync/extension_sync_data.h"
#include "chrome/browser/extensions/test_extension_service.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/browser/extensions/updater/chrome_extension_downloader_factory.h"
#include "chrome/browser/google/google_brand.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/crx_file/id_util.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "components/update_client/update_query_params.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/blocklist_extension_prefs.h"
#include "extensions/browser/blocklist_state.h"
#include "extensions/browser/crx_installer.h"
#include "extensions/browser/delayed_install_manager.h"
#include "extensions/browser/disable_reason.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/pending_extension_manager.h"
#include "extensions/browser/pref_names.h"
#include "extensions/browser/test_extension_prefs.h"
#include "extensions/browser/updater/extension_downloader.h"
#include "extensions/browser/updater/extension_downloader_delegate.h"
#include "extensions/browser/updater/extension_downloader_test_delegate.h"
#include "extensions/browser/updater/extension_downloader_test_helper.h"
#include "extensions/browser/updater/extension_downloader_types.h"
#include "extensions/browser/updater/extension_update_data.h"
#include "extensions/browser/updater/manifest_fetch_data.h"
#include "extensions/browser/updater/request_queue_impl.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/extension_urls.h"
#include "extensions/common/extensions_client.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_handlers/manifest_url_handlers.h"
#include "extensions/common/mojom/manifest.mojom-shared.h"
#include "extensions/common/verifier_formats.h"
#include "net/base/backoff_entry.h"
#include "net/base/load_flags.h"
#include "net/http/http_request_headers.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/network/test/test_url_loader_factory.h"
#include "services/network/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/third_party/mozilla/url_parse.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "base/files/scoped_temp_dir.h"
#include "chrome/browser/ash/login/users/user_manager_delegate_impl.h"
#include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
#include "chrome/browser/extensions/updater/chromeos_extension_cache_delegate.h"
#include "chrome/browser/extensions/updater/extension_cache_impl.h"
#include "chrome/browser/extensions/updater/local_extension_cache.h"
#include "chromeos/ash/components/settings/cros_settings.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/user_manager/user_manager_impl.h"
#include "extensions/browser/load_error_reporter.h"
#endif

static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));

using base::Time;
using content::BrowserThread;
using extensions::mojom::ManifestLocation;
using testing::_;
using testing::DoAll;
using testing::InvokeWithoutArgs;
using testing::Mock;
using testing::NiceMock;
using testing::Return;
using testing::SetArgPointee;
using update_client::UpdateQueryParams;

namespace extensions {

using Error = ExtensionDownloaderDelegate::Error;
using PingResult = ExtensionDownloaderDelegate::PingResult;

namespace {

const net::BackoffEntry::Policy kNoBackoffPolicy = {
  // Number of initial errors (in sequence) to ignore before applying
  // exponential back-off rules.
  1000,

  // Initial delay for exponential back-off in ms.
  0,

  // Factor by which the waiting time will be multiplied.
  0,

  // Fuzzing percentage. ex: 10% will spread requests randomly
  // between 90%-100% of the calculated time.
  0,

  // Maximum amount of time we are willing to delay our request in ms.
  0,

  // Time to keep an entry from being discarded even when it
  // has no significant state, -1 to never discard.
  -1,

  // Don't use initial delay unless the last request was an error.
  false,
};

const char kAuthUserQueryKey[] = "authuser";

constexpr int kExpectedLoadFlags = net::LOAD_DISABLE_CACHE;

constexpr int kExpectedLoadFlagsForDownloadWithCookies =
    net::LOAD_DISABLE_CACHE;

// Fake authentication constants
const char kFakeOAuth2Token[] = "ce n'est pas un jeton";

// Manifest key used to make multiple extensions with the same ID.
constexpr char kExtensionManifestKey[] =
    "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDowC9B4+K6zbl4PnALNyOUgra/"
    "MPdD8gZ39Fk/IAJWt03qrN7vz1gd/"
    "mmrBg0EEIsyLRmUmfyVEfvcIUOZxFqn4A9D2aaRSvNHy9qkasZMBDEql8Nt2iNZm/"
    "kGS7sizidDV6Bc/vyLNiH1gKOXBQ42JIxKjgtrmnhGV2giw2vJGwIDAQAB";

constexpr base::TimeDelta kUpdateFrequency = base::Seconds(15);

// Extracts the integer value of the |authuser| query parameter. Returns 0 if
// the parameter is not set.
int GetAuthUserQueryValue(const GURL& url) {
  std::string_view query_piece = url.query();
  url::Component query(0, query_piece.length());
  url::Component key, value;
  while (url::ExtractQueryKeyValue(query_piece, &query, &key, &value)) {
    std::string_view key_string = query_piece.substr(key.begin, key.len);
    if (key_string == kAuthUserQueryKey) {
      int user_index = 0;
      base::StringToInt(query_piece.substr(value.begin, value.len),
                        &user_index);
      return user_index;
    }
  }
  return 0;
}

// A minimal stub implementation of the ExtensionRegistrar::Delegate.
class StubExtensionRegistrarDelegate : public ExtensionRegistrar::Delegate {
 public:
  StubExtensionRegistrarDelegate() = default;
  ~StubExtensionRegistrarDelegate() override = default;

  // ExtensionRegistrar::Delegate:
  void PreAddExtension(const Extension* extension,
                       const Extension* old_extension) override {}
  void OnAddNewOrUpdatedExtension(const Extension* extension) override {}
  void PostActivateExtension(
      scoped_refptr<const Extension> extension) override {}
  void PostDeactivateExtension(
      scoped_refptr<const Extension> extension) override {}
  void PreUninstallExtension(
      scoped_refptr<const Extension> extension) override {}
  void PostUninstallExtension(scoped_refptr<const Extension> extension,
                              base::OnceClosure done_callback) override {}
  void ShowExtensionDisabledError(const Extension* extension,
                                  bool is_remote_install) override {}
  void LoadExtensionForReload(const ExtensionId& extension_id,
                              const base::FilePath& path) override {}
  void LoadExtensionForReloadWithQuietFailure(
      const ExtensionId& extension_id,
      const base::FilePath& path) override {}
  bool CanEnableExtension(const Extension* extension) override { return true; }
  bool CanDisableExtension(const Extension* extension) override { return true; }
  void GrantActivePermissions(const Extension* extension) override {}
  void UpdateExternalExtensionAlert() override {}
  void OnExtensionInstalled(const Extension* extension,
                            const syncer::StringOrdinal& page_ordinal,
                            int install_flags,
                            base::DictValue ruleset_install_prefs) override {}
};

class MockUpdateService : public UpdateService {
 public:
  MockUpdateService()
      : UpdateService(nullptr,
                      nullptr,
                      base::BindRepeating([](const std::vector<std::string>&,
                                             base::OnceClosure callback) {
                        std::move(callback).Run();
                      })) {}
  MOCK_CONST_METHOD0(IsBusy, bool());
  MOCK_METHOD3(SendUninstallPing,
               void(const std::string& id,
                    const base::Version& version,
                    int reason));
  MOCK_METHOD(void,
              StartUpdateCheck,
              (const ExtensionUpdateCheckParams& params,
               UpdateFoundCallback update_found_callback,
               base::OnceClosure callback),
              (override));
};

}  // namespace

class TestCrxInstallerFactory
    : public ExtensionUpdater::CrxInstallerFactoryForTest {
 public:
  TestCrxInstallerFactory() = default;
  TestCrxInstallerFactory(const TestCrxInstallerFactory&) = delete;
  TestCrxInstallerFactory& operator=(const TestCrxInstallerFactory&) = delete;
  ~TestCrxInstallerFactory() override = default;

  const ExtensionId& crx_extension_id() const { return crx_extension_id_; }

  const base::FilePath& crx_install_path() const { return crx_install_path_; }

  // Add a fake crx installer to be returned by a call to
  // CreateUpdateInstaller() with a specific ID.
  void AddFakeCrxInstaller(const std::string& id,
                           scoped_refptr<CrxInstaller> crx_installer) {
    fake_crx_installers_[id] = crx_installer;
  }

  // ExtensionUpdaterDelegate:
  scoped_refptr<CrxInstaller> CreateUpdateInstaller(
      const CRXFileInfo& file,
      bool file_ownership_passed) override {
    crx_extension_id_ = file.extension_id;
    crx_install_path_ = file.path;

    if (fake_crx_installers_.contains(crx_extension_id_)) {
      return fake_crx_installers_[crx_extension_id_];
    }

    return nullptr;
  }

 private:
  // Hold the set of ids that CreateUpdateInstaller() returns.
  std::map<std::string, scoped_refptr<CrxInstaller>> fake_crx_installers_;

  // The last values from the CRXFileInfo passed to CreateUpdateInstaller().
  ExtensionId crx_extension_id_;
  base::FilePath crx_install_path_;
};

// Helper to create ExtensionDownloader::Factory instances. This needs to be
// its own class, not part of ExtensionUpdaterTest, because individual tests
// create their own ExtensionDownloaderTestHelpers, and hence their own
// SharedURLLoaderFactory instances.
class TestDownloaderFactory {
 public:
  explicit TestDownloaderFactory(
      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
      : test_shared_url_loader_factory_(url_loader_factory) {}

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

  ~TestDownloaderFactory() = default;

  signin::IdentityTestEnvironment* identity_test_env() {
    return identity_test_env_.get();
  }

  const CoreAccountId& account_id() { return account_info_.account_id; }

  ExtensionDownloader::Factory GetDownloaderFactory() {
    return base::BindRepeating(
        &TestDownloaderFactory::CreateExtensionDownloader,
        base::Unretained(this));
  }

  ExtensionDownloader::Factory GetAuthenticatedDownloaderFactory() {
    return base::BindRepeating(
        &TestDownloaderFactory::CreateExtensionDownloaderWithIdentity,
        base::Unretained(this));
  }

  void OverrideDownloaderDelegate(ExtensionDownloaderDelegate* delegate) {
    downloader_delegate_override_ = delegate;
  }

 private:
  std::unique_ptr<ExtensionDownloader> CreateExtensionDownloader(
      ExtensionDownloaderDelegate* delegate) {
    std::unique_ptr<ExtensionDownloader> downloader =
        ChromeExtensionDownloaderFactory::CreateForURLLoaderFactory(
            test_shared_url_loader_factory_,
            downloader_delegate_override_ ? downloader_delegate_override_.get()
                                          : delegate,
            GetTestVerifierFormat());
    return downloader;
  }

  std::unique_ptr<ExtensionDownloader> CreateExtensionDownloaderWithIdentity(
      ExtensionDownloaderDelegate* delegate) {
    identity_test_env_ = std::make_unique<signin::IdentityTestEnvironment>();
    account_info_ = identity_test_env_->MakePrimaryAccountAvailable(
        "bobloblaw@lawblog.example.com", signin::ConsentLevel::kSignin);

    std::unique_ptr<ExtensionDownloader> downloader(
        CreateExtensionDownloader(delegate));
    downloader->SetIdentityManager(identity_test_env_->identity_manager());
    return downloader;
  }

  AccountInfo account_info_;
  std::unique_ptr<signin::IdentityTestEnvironment> identity_test_env_;

  raw_ptr<ExtensionDownloaderDelegate> downloader_delegate_override_ = nullptr;

  scoped_refptr<network::SharedURLLoaderFactory>
      test_shared_url_loader_factory_;
};

bool ShouldInstallExtensionsOnly(const Extension* extension,
                                 content::BrowserContext* context) {
  return extension->GetType() == Manifest::Type::kExtension;
}

bool ShouldInstallThemesOnly(const Extension* extension,
                             content::BrowserContext* context) {
  return extension->is_theme();
}

bool ShouldAlwaysInstall(const Extension* extension,
                         content::BrowserContext* context) {
  return true;
}

// Loads some pending extension records into a pending extension manager.
void SetupPendingExtensionManagerForTest(
    int count,
    const GURL& update_url,
    PendingExtensionManager* pending_extension_manager) {
  for (int i = 1; i <= count; ++i) {
    PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install =
        (i % 2 == 0) ? &ShouldInstallThemesOnly : &ShouldInstallExtensionsOnly;
    const bool kIsFromSync = true;
    const bool kMarkAcknowledged = false;
    const bool kRemoteInstall = false;
    std::string id =
        crx_file::id_util::GenerateId(base::StringPrintf("extension%i", i));

    pending_extension_manager->AddForTesting(PendingExtensionInfo(
        id, std::string(), update_url, base::Version(), should_allow_install,
        kIsFromSync, ManifestLocation::kInternal, Extension::NO_FLAGS,
        kMarkAcknowledged, kRemoteInstall));
  }
}

// Takes a string with KEY=VALUE parameters separated by '&' in |params| and
// puts the key/value pairs into |result|. For keys with no value, the empty
// string is used. So for "a=1&b=foo&c", result would map "a" to "1", "b" to
// "foo", and "c" to "".
static void ExtractParameters(const std::string& params,
                              std::map<std::string, std::string>* result) {
  for (const std::string& pair : base::SplitString(
           params, "&", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
    std::vector<std::string> key_val = base::SplitString(
        pair, "=", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
    if (!key_val.empty()) {
      std::string key = key_val[0];
      EXPECT_TRUE(result->find(key) == result->end());
      (*result)[key] = (key_val.size() == 2) ? key_val[1] : std::string();
    } else {
      NOTREACHED();
    }
  }
}

// Helper function to extract the ping data param values for each extension in
// a manifest fetch url, returned in a map keyed by extension id.
// E.g. for "x=id%3Dabcdef%26ping%3Ddr%253D1%2526dr%253D1024" we'd return
// {"abcdef": {"dr": set("1", "1024")}}
typedef std::map<std::string, std::set<std::string>> ParamsMap;
static std::map<std::string, ParamsMap> GetPingDataFromURL(
    const GURL& manifest_url) {
  std::map<std::string, ParamsMap> result;

  base::StringPairs toplevel_params;
  base::SplitStringIntoKeyValuePairs(manifest_url.GetQuery(), '=', '&',
                                     &toplevel_params);
  for (const auto& param : toplevel_params) {
    if (param.first != "x")
      continue;

    // We've found "x=<something>", now unescape <something> and look for
    // the "id=<id>&ping=<ping_value>" parameters within.
    std::string unescaped = base::UnescapeBinaryURLComponent(param.second);
    base::StringPairs extension_params;
    base::SplitStringIntoKeyValuePairs(unescaped, '=', '&', &extension_params);
    std::multimap<std::string, std::string> param_map;
    param_map.insert(extension_params.begin(), extension_params.end());
    if (param_map.contains("id") && param_map.contains("ping")) {
      std::string id = param_map.find("id")->second;
      result[id] = ParamsMap();

      // Pull the key=value pairs out of the ping parameter for this id and
      // put into the result.
      std::string ping =
          base::UnescapeBinaryURLComponent(param_map.find("ping")->second);
      base::StringPairs ping_params;
      base::SplitStringIntoKeyValuePairs(ping, '=', '&', &ping_params);
      for (const auto& ping_param : ping_params) {
        if (!result[id].contains(ping_param.first)) {
          result[id][ping_param.first] = std::set<std::string>();
        }
        result[id][ping_param.first].insert(ping_param.second);
      }
    }
  }
  return result;
}

static void VerifyQueryAndExtractParameters(
    const std::string& query,
    std::map<std::string, std::string>* result) {
  std::map<std::string, std::string> params;
  ExtractParameters(query, &params);

  std::string omaha_params = UpdateQueryParams::Get(UpdateQueryParams::CRX);
  std::map<std::string, std::string> expected;
  ExtractParameters(omaha_params, &expected);

  for (auto it = expected.begin(); it != expected.end(); ++it) {
    EXPECT_EQ(it->second, params[it->first]);
  }

  EXPECT_EQ(1U, params.count("x"));
  std::string decoded = base::UnescapeBinaryURLComponent(params["x"]);
  ExtractParameters(decoded, result);
}

// All of our tests that need to use private APIs of ExtensionUpdater live
// inside this class (which is a friend to ExtensionUpdater).
class ExtensionUpdaterTest : public testing::Test {
 public:
  ExtensionUpdaterTest()
      : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP) {}

  void SetUp() override {
    prefs_ = std::make_unique<TestExtensionPrefs>(
        base::SingleThreadTaskRunner::GetCurrentDefault(),
        std::make_unique<TestingProfile>());
    // The registrar needs a delegate in order to call certain methods on it.
    ExtensionRegistrar::Get(prefs_->browser_context())
        ->Init(&stub_extension_registrar_delegate_,
               /*extensions_enabled=*/true,
               base::CommandLine::ForCurrentProcess(), base::FilePath(),
               base::FilePath());
  }

  void TearDown() override {
    // Some tests create URLRequestContextGetters, whose destruction must run
    // on the IO thread. Make sure the IO loop spins before shutdown so that
    // those objects are released.
    RunUntilIdle();
    // Reset the ExtensionRegistrar delegate.
    ExtensionRegistrar::Get(prefs_->browser_context())
        ->Init(/*delegate=*/nullptr, /*extensions_enabled=*/true,
               base::CommandLine::ForCurrentProcess(), base::FilePath(),
               base::FilePath());
    prefs_.reset();
  }

  Profile* profile() {
    return Profile::FromBrowserContext(prefs_->browser_context());
  }

  ExtensionPrefs* extension_prefs() { return prefs_->prefs(); }

  PrefService* pref_service() { return prefs_->pref_service(); }

  // Creates test extensions and inserts them into list. The name and
  // version are all based on their index. If `update_url` is non-null, it
  // will be used as the update_url for each extension. If `key` is non-null,
  // it will be used as manifest_keys::kKey to force an extension to have a
  // particular ID. `key` is only valid if `count` is 1.
  // The `id` is used to distinguish extension names and make sure that
  // no two extensions share the same name.
  void CreateTestExtensions(int id,
                            int count,
                            ExtensionList* list,
                            const std::string* update_url,
                            ManifestLocation location,
                            std::optional<std::string> key = std::nullopt) {
    if (key.has_value()) {
      CHECK_EQ(count, 1) << "Can't create two extensions with the same key";
    }
    for (int i = 1; i <= count; i++) {
      base::DictValue manifest;
      manifest.Set(manifest_keys::kVersion, base::StringPrintf("%d.0.0.0", i));
      manifest.Set(manifest_keys::kName,
                   base::StringPrintf("Extension %d.%d", id, i));
      manifest.Set(manifest_keys::kManifestVersion, 2);
      if (update_url) {
        manifest.Set(manifest_keys::kUpdateURL, *update_url);
      }
      if (key.has_value()) {
        manifest.Set(manifest_keys::kKey, *key);
      }
      scoped_refptr<Extension> e =
          prefs_->AddExtensionWithManifest(manifest, location);
      ASSERT_TRUE(e.get() != nullptr);
      list->push_back(e);
    }
  }

  void SetExtensions(
      const ExtensionList& extensions,
      const ExtensionList& disabled_extensions,
      const ExtensionList& blocklisted_extensions = ExtensionList()) {
    auto* registry = ExtensionRegistry::Get(profile());
    registry->ClearAll();
    for (const auto& extension : extensions) {
      registry->AddEnabled(extension);
    }
    for (const auto& extension : disabled_extensions) {
      registry->AddDisabled(extension);
    }
    for (const auto& extension : blocklisted_extensions) {
      registry->AddBlocklisted(extension);
    }
  }

  void RunUntilIdle() {
    prefs_->pref_service()->CommitPendingWrite();
    base::RunLoop().RunUntilIdle();
  }

  void SimulateTimerFired(ExtensionUpdater* updater) { updater->NextCheck(); }

  void OverrideUpdateService(ExtensionUpdater* updater,
                             UpdateService* service) {
    updater->update_service_ = service;
  }

  bool CanUseUpdateService(ExtensionUpdater* updater,
                           const Extension* extension) {
    return updater->CanUseUpdateService(extension, nullptr);
  }

  // Adds a Result with the given data to results.
  void AddParseResult(const std::string& id,
                      const std::string& version,
                      const std::string& url,
                      UpdateManifestResults* results) {
    UpdateManifestResult result;
    result.extension_id = id;
    result.version = version;
    result.crx_url = GURL(url);
    results->update_list.push_back(result);
  }

  void StartUpdateCheck(ExtensionDownloader* downloader,
                        ManifestFetchData* fetch_data) {
    downloader->StartUpdateCheck(
        std::unique_ptr<ManifestFetchData>(fetch_data));
  }

  size_t ManifestFetchersCount(ExtensionDownloader* downloader) {
    return downloader->manifests_queue_.size() +
           (downloader->HasActiveManifestRequestForTesting() ? 1 : 0);
  }

  std::set<std::string> GetRunningInstallIds(const ExtensionUpdater& updater) {
    std::set<std::string> ret;
    for (const auto& pair : updater.running_crx_installs_)
      ret.insert(pair.second.info.extension_id);
    return ret;
  }

  const DownloadFailure* GetFailureWithId(
      const std::vector<std::pair<ExtensionDownloaderTask, DownloadFailure>>&
          failures,
      const ExtensionId& id) {
    auto it = std::ranges::find(
        failures, id, [](const auto& failure) { return failure.first.id; });
    return it == failures.end() ? nullptr : &it->second;
  }

  void TestExtensionUpdateCheckRequests(bool pending) {
    // Create an extension with an update_url.
    ExtensionDownloaderTestHelper helper;
    TestDownloaderFactory factory(helper.url_loader_factory());
    TestCrxInstallerFactory crx_installer_factory;
    std::string update_url("http://foo.com/bar");
    ExtensionList extensions;
    PendingExtensionManager* pending_extension_manager =
        PendingExtensionManager::Get(profile());
    if (pending) {
      SetupPendingExtensionManagerForTest(1, GURL(update_url),
                                          pending_extension_manager);
    } else {
      CreateTestExtensions(1, 1, &extensions, &update_url,
                           ManifestLocation::kInternal);
      SetExtensions(extensions, ExtensionList());
    }

    // Set up and start the updater.
    ExtensionUpdater updater(profile());
    updater.InitAndEnable(extension_prefs(), pref_service(), base::Days(1),
                          nullptr, factory.GetDownloaderFactory());
    updater.set_crx_installer_factory_for_test(&crx_installer_factory);
    updater.Start();

    // Tell the update that it's time to do update checks.
    SimulateTimerFired(&updater);

    // Get the url our loader was asked to fetch.
    const ManifestFetchData& fetch =
        *updater.downloader_->manifests_queue_.active_request();

    const GURL& url = fetch.full_url();

    EXPECT_FALSE(url.is_empty());
    EXPECT_TRUE(url.is_valid());
    EXPECT_TRUE(url.SchemeIs("http"));
    EXPECT_EQ("foo.com", url.GetHost());
    EXPECT_EQ("/bar", url.GetPath());

    // Validate the extension request parameters in the query. It should
    // look something like "x=id%3D<id>%26v%3D<version>%26uc".
    EXPECT_TRUE(url.has_query());
    std::map<std::string, std::string> params;
    VerifyQueryAndExtractParameters(url.GetQuery(), &params);
    if (pending) {
      EXPECT_TRUE(pending_extension_manager->IsIdPending(params["id"]));
      EXPECT_EQ("0.0.0.0", params["v"]);
    } else {
      EXPECT_EQ(extensions[0]->id(), params["id"]);
      EXPECT_EQ(extensions[0]->VersionString(), params["v"]);
    }
    EXPECT_EQ("", params["uc"]);
  }

  void TestUpdateUrlDataEmpty() {
    ExtensionDownloaderTestHelper helper;
    const std::string id(32, 'a');
    const std::string version = "1.0";

    // Make sure that an empty update URL data string does not cause a ap=
    // option to appear in the x= parameter.
    GURL kUpdateURL("http://localhost/foo");
    std::unique_ptr<ManifestFetchData> fetch_data(
        CreateManifestFetchData(kUpdateURL));
    AddExtensionToFetchDataForTesting(fetch_data.get(), id, version,
                                      kUpdateURL);

    std::map<std::string, std::string> params;
    VerifyQueryAndExtractParameters(fetch_data->full_url().GetQuery(), &params);
    EXPECT_EQ(id, params["id"]);
    EXPECT_EQ(version, params["v"]);
    EXPECT_EQ(0U, params.count("ap"));
  }

  void TestUpdateUrlDataSimple() {
    const std::string id(32, 'a');
    const std::string version = "1.0";

    // Make sure that an update URL data string causes an appropriate ap=
    // option to appear in the x= parameter.
    std::unique_ptr<ManifestFetchData> fetch_data(
        CreateManifestFetchData(GURL("http://localhost/foo")));
    fetch_data->AddExtension(id, version,
                             ExtensionDownloaderTestHelper::GetTestPingData(),
                             "bar", std::string(), ManifestLocation::kInternal,
                             DownloadFetchPriority::kBackground);
    std::map<std::string, std::string> params;
    VerifyQueryAndExtractParameters(fetch_data->full_url().GetQuery(), &params);
    EXPECT_EQ(id, params["id"]);
    EXPECT_EQ(version, params["v"]);
    EXPECT_EQ("bar", params["ap"]);
  }

  void TestUpdateUrlDataCompound() {
    const std::string id(32, 'a');
    const std::string version = "1.0";

    // Make sure that an update URL data string causes an appropriate ap=
    // option to appear in the x= parameter.
    std::unique_ptr<ManifestFetchData> fetch_data(
        CreateManifestFetchData(GURL("http://localhost/foo")));
    fetch_data->AddExtension(
        id, version, ExtensionDownloaderTestHelper::GetTestPingData(),
        "a=1&b=2&c", std::string(), ManifestLocation::kInternal,
        DownloadFetchPriority::kBackground);
    std::map<std::string, std::string> params;
    VerifyQueryAndExtractParameters(fetch_data->full_url().GetQuery(), &params);
    EXPECT_EQ(id, params["id"]);
    EXPECT_EQ(version, params["v"]);
    EXPECT_EQ("a%3D1%26b%3D2%26c", params["ap"]);
  }

  void TestUpdateUrlDataFromUrl(
      const std::string& update_url,
      DownloadFetchPriority fetch_priority,
      int num_extensions,
      bool should_include_traffic_management_headers) {
    ExtensionDownloaderTestHelper helper;
    TestDownloaderFactory factory(helper.url_loader_factory());
    TestCrxInstallerFactory crx_installer_factory;
    ExtensionList extensions;

    CreateTestExtensions(1, num_extensions, &extensions, &update_url,
                         ManifestLocation::kInternal);

    for (int i = 0; i < num_extensions; ++i) {
      const std::string& id = extensions[i]->id();
      EXPECT_CALL(helper.delegate(), GetPingDataForExtension(id, _));

      helper.downloader().AddPendingExtension(ExtensionDownloaderTask(
          id, ManifestURL::GetUpdateURL(extensions[i].get()),
          extensions[i]->location(), false, 0, fetch_priority,
          extensions[i]->version(), extensions[i]->GetType(), std::string()));
    }

    // Get the headers our loader was asked to fetch.
    base::RunLoop loop;
    net::HttpRequestHeaders last_request_headers;
    helper.test_url_loader_factory().SetInterceptor(base::BindLambdaForTesting(
        [&](const network::ResourceRequest& request) {
          last_request_headers = request.headers;
          loop.Quit();
        }));

    helper.downloader().StartAllPending(nullptr);
    EXPECT_TRUE(helper.downloader().HasActiveManifestRequestForTesting());

    loop.Run();

    // Make sure that extensions that update from the gallery ignore any
    // update URL data.
    const ManifestFetchData& fetch =
        *helper.downloader().manifests_queue_.active_request();
    const std::string& fetcher_url = fetch.full_url().spec();
    std::string::size_type x = fetcher_url.find("x=");
    EXPECT_NE(std::string::npos, x);
    std::string::size_type ap = fetcher_url.find("ap%3D", x);
    EXPECT_EQ(std::string::npos, ap);

    net::HttpRequestHeaders fetch_headers;
    std::swap(fetch_headers, last_request_headers);
    EXPECT_EQ(should_include_traffic_management_headers,
              fetch_headers.HasHeader(
                  ExtensionDownloader::kUpdateInteractivityHeader));
    EXPECT_EQ(should_include_traffic_management_headers,
              fetch_headers.HasHeader(ExtensionDownloader::kUpdateAppIdHeader));
    EXPECT_EQ(
        should_include_traffic_management_headers,
        fetch_headers.HasHeader(ExtensionDownloader::kUpdateUpdaterHeader));

    if (should_include_traffic_management_headers) {
      std::string interactivity_value =
          fetch_headers
              .GetHeader(ExtensionDownloader::kUpdateInteractivityHeader)
              .value_or(std::string());

      std::string expected_interactivity_value =
          fetch_priority == DownloadFetchPriority::kForeground ? "fg" : "bg";
      EXPECT_EQ(expected_interactivity_value, interactivity_value);

      std::string appid_value =
          fetch_headers.GetHeader(ExtensionDownloader::kUpdateAppIdHeader)
              .value_or(std::string());
      if (num_extensions > 1) {
        for (int i = 0; i < num_extensions; ++i) {
          EXPECT_TRUE(
              testing::IsSubstring("", "", extensions[i]->id(), appid_value));
        }
      } else {
        EXPECT_EQ(extensions[0]->id(), appid_value);
      }

      std::string updater_value =
          fetch_headers.GetHeader(ExtensionDownloader::kUpdateUpdaterHeader)
              .value_or(std::string());
      const std::string expected_updater_value = base::StringPrintf(
          "%s-%s", UpdateQueryParams::GetProdIdString(UpdateQueryParams::CRX),
          UpdateQueryParams::GetProdVersion().c_str());
      EXPECT_EQ(expected_updater_value, updater_value);
    }
  }

  void TestInstallSource() {
    const std::string id(32, 'a');
    const std::string version = "1.0";
    const std::string install_source = "instally";

    // Make sure that an installsource= appears in the x= parameter.
    std::unique_ptr<ManifestFetchData> fetch_data(
        CreateManifestFetchData(GURL("http://localhost/foo")));
    fetch_data->AddExtension(
        id, version, ExtensionDownloaderTestHelper::GetTestPingData(),
        ExtensionDownloaderTestHelper::kEmptyUpdateUrlData, install_source,
        ManifestLocation::kInternal, DownloadFetchPriority::kBackground);
    std::map<std::string, std::string> params;
    VerifyQueryAndExtractParameters(fetch_data->full_url().GetQuery(), &params);
    EXPECT_EQ(id, params["id"]);
    EXPECT_EQ(version, params["v"]);
    EXPECT_EQ(install_source, params["installsource"]);
  }

  void TestInstallLocation() {
    const std::string id(32, 'a');
    const std::string version = "1.0";
    const std::string install_location = "external";

    // Make sure that installedby= appears in the x= parameter.
    std::unique_ptr<ManifestFetchData> fetch_data(
        CreateManifestFetchData(GURL("http://localhost/foo")));
    fetch_data->AddExtension(
        id, version, ExtensionDownloaderTestHelper::GetTestPingData(),
        ExtensionDownloaderTestHelper::kEmptyUpdateUrlData, std::string(),
        ManifestLocation::kExternalPrefDownload,
        DownloadFetchPriority::kBackground);
    std::map<std::string, std::string> params;
    VerifyQueryAndExtractParameters(fetch_data->full_url().GetQuery(), &params);
    EXPECT_EQ(id, params["id"]);
    EXPECT_EQ(version, params["v"]);
    EXPECT_EQ(install_location, params["installedby"]);
  }

  void TestDetermineUpdates() {
    ExtensionDownloaderTestHelper helper;

    GURL kUpdateURL("http://localhost/foo");
    std::unique_ptr<ManifestFetchData> fetch_data(
        CreateManifestFetchData(kUpdateURL));

    // Check passing an empty list of parse results to DetermineUpdates
    UpdateManifestResults updates;
    std::vector<std::pair<ExtensionDownloaderTask, UpdateManifestResult*>>
        updateable;
    std::vector<std::pair<ExtensionDownloaderTask, DownloadFailure>> failures;
    helper.downloader().DetermineUpdates(fetch_data->TakeAssociatedTasks(),
                                         updates, &updateable, &failures);
    EXPECT_TRUE(updateable.empty());
    EXPECT_TRUE(failures.empty());

    // Create two updates - expect that DetermineUpdates will return the first
    // one (v1.0 installed, v1.1 available) but not the second one (both
    // installed and available at v2.0).
    const std::string id1 = crx_file::id_util::GenerateId("1");
    const std::string id2 = crx_file::id_util::GenerateId("2");
    AddExtensionToFetchDataForTesting(fetch_data.get(), id1, "1.0.0.0",
                                      kUpdateURL);
    AddParseResult(id1, "1.1", "http://localhost/e1_1.1.crx", &updates);
    AddExtensionToFetchDataForTesting(fetch_data.get(), id2, "2.0.0.0",
                                      kUpdateURL);
    AddParseResult(id2, "2.0.0.0", "http://localhost/e2_2.0.crx", &updates);

    EXPECT_CALL(helper.delegate(), IsExtensionPending(_))
        .WillRepeatedly(Return(false));
    EXPECT_CALL(helper.delegate(), GetExtensionExistingVersion(id1, _))
        .WillOnce(DoAll(SetArgPointee<1>("1.0.0.0"), Return(true)));
    EXPECT_CALL(helper.delegate(), GetExtensionExistingVersion(id2, _))
        .WillOnce(DoAll(SetArgPointee<1>("2.0.0.0"), Return(true)));

    updateable.clear();
    failures.clear();
    helper.downloader().DetermineUpdates(fetch_data->TakeAssociatedTasks(),
                                         updates, &updateable, &failures);
    ASSERT_EQ(1u, failures.size());
    EXPECT_EQ(id2, failures[0].first.id);
    EXPECT_EQ(ExtensionDownloaderDelegate::Error::NO_UPDATE_AVAILABLE,
              failures[0].second.error);
    ASSERT_EQ(1u, updateable.size());
    EXPECT_EQ("1.1", updateable[0].second->version);
  }

  void TestDetermineUpdatesError() {
    ExtensionDownloaderTestHelper helper;
    MockExtensionDownloaderDelegate& delegate = helper.delegate();

    GURL kUpdateURL("http://localhost/foo");
    std::unique_ptr<ManifestFetchData> fetch_data(
        CreateManifestFetchData(kUpdateURL));
    UpdateManifestResults updates;

    // id1 => updatable (current version (1.1) is older than update version).
    // id2 => non_updateable (current version (2.0.0.0) is the same as update
    //        version).
    // id3 => non_updateable (manifest update version is empty).
    // id4 => errors (|updates| doesn't contain id4).
    // id5 => errors (the extension is not currently installed).
    // id6 => errors (manifest update version is invalid).
    const std::string id1 = crx_file::id_util::GenerateId("1");
    const std::string id2 = crx_file::id_util::GenerateId("2");
    const std::string id3 = crx_file::id_util::GenerateId("3");
    const std::string id4 = crx_file::id_util::GenerateId("4");
    const std::string id5 = crx_file::id_util::GenerateId("5");
    const std::string id6 = crx_file::id_util::GenerateId("6");

    AddExtensionToFetchDataForTesting(fetch_data.get(), id1, "1.0.0.0",
                                      kUpdateURL);
    AddParseResult(id1, "1.1", "http://localhost/e1_1.1.crx", &updates);

    AddExtensionToFetchDataForTesting(fetch_data.get(), id2, "2.0.0.0",
                                      kUpdateURL);
    AddParseResult(id2, "2.0.0.0", "http://localhost/e2_2.0.crx", &updates);

    // Empty update version in manifest.
    AddExtensionToFetchDataForTesting(fetch_data.get(), id3, "0.0.0.0",
                                      kUpdateURL);
    AddParseResult(id3, "", "http://localhost/e3_3.0.crx", &updates);

    AddExtensionToFetchDataForTesting(fetch_data.get(), id4, "0.0.0.0",
                                      kUpdateURL);

    AddExtensionToFetchDataForTesting(fetch_data.get(), id5, "0.0.0.0",
                                      kUpdateURL);
    AddParseResult(id5, "5.0.0.0", "http://localhost/e5_5.0.crx", &updates);

    // Invalid update version in manifest.
    AddExtensionToFetchDataForTesting(fetch_data.get(), id6, "0.0.0.0",
                                      kUpdateURL);
    AddParseResult(id6, "invalid_version", "http://localhost/e6_6.0.crx",
                   &updates);

    EXPECT_CALL(delegate, IsExtensionPending(_)).WillRepeatedly(Return(false));
    EXPECT_CALL(delegate, GetExtensionExistingVersion(id1, _))
        .WillOnce(DoAll(SetArgPointee<1>("1.0.0.0"), Return(true)));
    EXPECT_CALL(delegate, GetExtensionExistingVersion(id2, _))
        .WillOnce(DoAll(SetArgPointee<1>("2.0.0.0"), Return(true)));
    EXPECT_CALL(delegate, GetExtensionExistingVersion(id3, _))
        .WillOnce(DoAll(SetArgPointee<1>("0.0.0.0"), Return(true)));
    EXPECT_CALL(delegate, GetExtensionExistingVersion(id5, _))
        .WillOnce(DoAll(SetArgPointee<1>("0.0.0.0"), Return(false)));
    EXPECT_CALL(delegate, GetExtensionExistingVersion(id6, _))
        .WillOnce(DoAll(SetArgPointee<1>("0.0.0.0"), Return(true)));

    std::vector<std::pair<ExtensionDownloaderTask, UpdateManifestResult*>>
        updateable;
    std::vector<std::pair<ExtensionDownloaderTask, DownloadFailure>> failures;
    helper.downloader().DetermineUpdates(fetch_data->TakeAssociatedTasks(),
                                         updates, &updateable, &failures);
    std::vector<ExtensionId> ids_not_updateable({id2, id3});
    for (const auto& id : ids_not_updateable) {
      const auto* failure = GetFailureWithId(failures, id);
      ASSERT_TRUE(failure);
      EXPECT_EQ(ExtensionDownloaderDelegate::Error::NO_UPDATE_AVAILABLE,
                failure->error);
    }
    std::vector<ExtensionId> ids_with_error({id4, id5, id6});
    for (const auto& id : ids_with_error) {
      const auto* failure = GetFailureWithId(failures, id);
      ASSERT_TRUE(failure);
      EXPECT_EQ(ExtensionDownloaderDelegate::Error::MANIFEST_INVALID,
                failure->error);
    }
    EXPECT_EQ(5u, failures.size());
    ASSERT_EQ(1u, updateable.size());
    EXPECT_EQ("1.1", updateable[0].second->version);
  }

  void TestDetermineUpdatesPending() {
    // Create a set of test extensions
    ExtensionDownloaderTestHelper helper;
    TestDownloaderFactory factory(helper.url_loader_factory());
    PendingExtensionManager* pending_extension_manager =
        PendingExtensionManager::Get(profile());
    SetupPendingExtensionManagerForTest(3, GURL(), pending_extension_manager);

    MockExtensionDownloaderDelegate& delegate = helper.delegate();

    GURL kUpdateURL("http://localhost/foo");
    std::unique_ptr<ManifestFetchData> fetch_data(
        CreateManifestFetchData(kUpdateURL));
    UpdateManifestResults updates;

    std::list<std::string> ids_for_update_check =
        pending_extension_manager->GetPendingIdsForUpdateCheck();

    for (const std::string& id : ids_for_update_check) {
      AddExtensionToFetchDataForTesting(fetch_data.get(), id, "1.0.0.0",
                                        kUpdateURL);
      AddParseResult(id, "1.1", "http://localhost/e1_1.1.crx", &updates);
    }

    // The delegate will tell the downloader that all the extensions are
    // pending.
    EXPECT_CALL(delegate, IsExtensionPending(_)).WillRepeatedly(Return(true));

    std::vector<std::pair<ExtensionDownloaderTask, UpdateManifestResult*>>
        updateable;
    std::vector<std::pair<ExtensionDownloaderTask, DownloadFailure>> failures;
    helper.downloader().DetermineUpdates(fetch_data->TakeAssociatedTasks(),
                                         updates, &updateable, &failures);
    // All the apps should be updateable.
    EXPECT_EQ(3u, updateable.size());
    EXPECT_TRUE(failures.empty());
  }

  void TestDetermineUpdatesDuplicates() {
    base::HistogramTester histogram_tester;
    ExtensionDownloaderTestHelper helper;
    MockExtensionDownloaderDelegate& delegate = helper.delegate();

    const std::string id1 = crx_file::id_util::GenerateId("1");
    const std::string id2 = crx_file::id_util::GenerateId("2");
    const std::string id3 = crx_file::id_util::GenerateId("3");
    const std::string id4 = crx_file::id_util::GenerateId("4");
    const std::string id5 = crx_file::id_util::GenerateId("5");
    const std::string id6 = crx_file::id_util::GenerateId("6");
    const std::string id7 = crx_file::id_util::GenerateId("7");

    GURL kUpdateURL("http://localhost/foo");
    std::unique_ptr<ManifestFetchData> fetch_data(
        CreateManifestFetchData(kUpdateURL));
    AddExtensionToFetchDataForTesting(fetch_data.get(), id1, "1.1.0.0",
                                      kUpdateURL);
    AddExtensionToFetchDataForTesting(fetch_data.get(), id2, "1.2.0.0",
                                      kUpdateURL);
    AddExtensionToFetchDataForTesting(fetch_data.get(), id3, "1.3.0.0",
                                      kUpdateURL);
    AddExtensionToFetchDataForTesting(fetch_data.get(), id4, "1.4.0.0",
                                      kUpdateURL);
    AddExtensionToFetchDataForTesting(fetch_data.get(), id5, "1.5.0.0",
                                      kUpdateURL);
    AddExtensionToFetchDataForTesting(fetch_data.get(), id6, "1.6.0.0",
                                      kUpdateURL);
    AddExtensionToFetchDataForTesting(fetch_data.get(), id7, "1.7.0.0",
                                      kUpdateURL);

    UpdateManifestResults updates;
    AddParseResult(id1, "1.1.0.0", "http://localhost/e1_1.1.crx", &updates);
    AddParseResult(id2, "1.2.0.a", "http://localhost/e2_1.1.crx", &updates);
    AddParseResult(id3, "1.3.1.0", "http://localhost/e3_1.1.crx", &updates);
    AddParseResult(id4, "1.4.0.0", "http://localhost/e4_1.1.crx", &updates);
    AddParseResult(id4, "1.4.0.a", "http://localhost/e4_1.1.crx", &updates);
    AddParseResult(id5, "1.5.0.a", "http://localhost/e5_1.1.crx", &updates);
    AddParseResult(id5, "1.5.0.b", "http://localhost/e5_1.1.crx", &updates);
    AddParseResult(id6, "1.6.0.a", "http://localhost/e6_1.1.crx", &updates);
    AddParseResult(id6, "1.6.0.0", "http://localhost/e6_1.1.crx", &updates);
    AddParseResult(id6, "1.6.1.0", "http://localhost/e6_1.1.crx", &updates);
    AddParseResult(id6, "1.6.2.0", "http://localhost/e6_1.1.crx", &updates);

    EXPECT_CALL(delegate, IsExtensionPending(_)).WillRepeatedly(Return(false));
    EXPECT_CALL(delegate, GetExtensionExistingVersion(id1, _))
        .WillOnce(DoAll(SetArgPointee<1>("1.1.0.0"), Return(true)));
    EXPECT_CALL(delegate, GetExtensionExistingVersion(id2, _))
        .WillOnce(DoAll(SetArgPointee<1>("1.2.0.0"), Return(true)));
    EXPECT_CALL(delegate, GetExtensionExistingVersion(id3, _))
        .WillOnce(DoAll(SetArgPointee<1>("1.3.0.0"), Return(true)));
    EXPECT_CALL(delegate, GetExtensionExistingVersion(id4, _))
        .WillOnce(DoAll(SetArgPointee<1>("1.4.0.0"), Return(true)));
    EXPECT_CALL(delegate, GetExtensionExistingVersion(id5, _))
        .WillOnce(DoAll(SetArgPointee<1>("1.5.0.0"), Return(true)));
    EXPECT_CALL(delegate, GetExtensionExistingVersion(id6, _))
        .WillOnce(DoAll(SetArgPointee<1>("1.6.0.0"), Return(true)));

    std::vector<std::pair<ExtensionDownloaderTask, UpdateManifestResult*>>
        updateable;
    std::vector<std::pair<ExtensionDownloaderTask, DownloadFailure>> failures;
    helper.downloader().DetermineUpdates(fetch_data->TakeAssociatedTasks(),
                                         updates, &updateable, &failures);
    std::vector<ExtensionId> ids_not_updateable({id1, id4});
    for (const auto& id : ids_not_updateable) {
      const auto* failure = GetFailureWithId(failures, id);
      ASSERT_TRUE(failure);
      EXPECT_EQ(ExtensionDownloaderDelegate::Error::NO_UPDATE_AVAILABLE,
                failure->error);
    }
    std::vector<ExtensionId> ids_with_error({id2, id5, id7});
    for (const auto& id : ids_with_error) {
      const auto* failure = GetFailureWithId(failures, id);
      ASSERT_TRUE(failure);
      EXPECT_EQ(ExtensionDownloaderDelegate::Error::MANIFEST_INVALID,
                failure->error);
    }
    EXPECT_EQ(5u, failures.size());
    ASSERT_EQ(2u, updateable.size());
    EXPECT_EQ("1.3.1.0", updateable[0].second->version);
    EXPECT_EQ("1.6.1.0", updateable[1].second->version);
  }

  void TestMultipleManifestDownloading() {
    ExtensionDownloaderTestHelper helper;
    MockExtensionDownloaderDelegate& delegate = helper.delegate();
    helper.downloader().manifests_queue_.set_backoff_policy(kNoBackoffPolicy);

    GURL kUpdateUrl("http://localhost/manifest1");

    std::unique_ptr<ManifestFetchData> fetch1(
        CreateManifestFetchData(kUpdateUrl));
    std::unique_ptr<ManifestFetchData> fetch2(
        CreateManifestFetchData(kUpdateUrl));
    std::unique_ptr<ManifestFetchData> fetch3(
        CreateManifestFetchData(kUpdateUrl));
    std::unique_ptr<ManifestFetchData> fetch4(
        CreateManifestFetchData(kUpdateUrl));
    DownloadPingData zero_days(/*rollcall=*/0, /*active=*/0, /*enabled=*/true,
                               /*disable_reasons=*/{});
    AddExtensionToFetchDataForTesting(fetch1.get(), "1111", "1.0", kUpdateUrl,
                                      zero_days);
    AddExtensionToFetchDataForTesting(fetch2.get(), "2222", "2.0", kUpdateUrl,
                                      zero_days);
    AddExtensionToFetchDataForTesting(fetch3.get(), "3333", "3.0", kUpdateUrl,
                                      zero_days);
    AddExtensionToFetchDataForTesting(fetch4.get(), "4444", "4.0", kUpdateUrl,
                                      zero_days);

    // This will start the first fetcher and queue the others. The next in queue
    // is started as each fetcher receives its response. Note that the fetchers
    // don't necessarily run in the order that they are started from here.
    GURL fetch1_url = fetch1->full_url();
    GURL fetch2_url = fetch2->full_url();
    GURL fetch3_url = fetch3->full_url();
    GURL fetch4_url = fetch4->full_url();

    // fetch1_url
    {
      helper.StartUpdateCheck(std::move(fetch1));
      RunUntilIdle();
      helper.test_url_loader_factory().AddResponse(fetch1_url.spec(), "",
                                                   net::HTTP_BAD_REQUEST);
      EXPECT_CALL(
          delegate,
          OnExtensionDownloadFailed(
              "1111", ExtensionDownloaderDelegate::Error::MANIFEST_FETCH_FAILED,
              _, _, _))
          .WillOnce(InvokeWithoutArgs(&delegate,
                                      &MockExtensionDownloaderDelegate::Quit));
      delegate.Wait();
      Mock::VerifyAndClearExpectations(&delegate);
      fetch1_url = GURL();

      RunUntilIdle();
    }

    // fetch2_url
    {
      helper.StartUpdateCheck(std::move(fetch2));
      RunUntilIdle();
      const std::string kInvalidXml = "invalid xml";
      helper.test_url_loader_factory().AddResponse(fetch2_url.spec(),
                                                   kInvalidXml, net::HTTP_OK);
      EXPECT_CALL(
          delegate,
          OnExtensionDownloadFailed(
              "2222", ExtensionDownloaderDelegate::Error::MANIFEST_INVALID, _,
              _, _))
          .WillOnce(InvokeWithoutArgs(&delegate,
                                      &MockExtensionDownloaderDelegate::Quit));
      delegate.Wait();
      Mock::VerifyAndClearExpectations(&delegate);
      fetch2_url = GURL();

      RunUntilIdle();
    }

    // fetch3_url
    {
      helper.StartUpdateCheck(std::move(fetch3));
      RunUntilIdle();
      const std::string kNoUpdate = CreateUpdateManifest(
          {UpdateManifestItem("3333")
               .version("3.0.0.0")
               .prodversionmin("3.0.0.0")
               .codebase("http://example.com/extension_3.0.0.0.crx")});
      helper.test_url_loader_factory().AddResponse(fetch3_url.spec(), kNoUpdate,
                                                   net::HTTP_OK);
      // The third fetcher doesn't have an update available.
      EXPECT_CALL(delegate, IsExtensionPending("3333")).WillOnce(Return(false));
      EXPECT_CALL(delegate, GetExtensionExistingVersion("3333", _))
          .WillOnce(DoAll(SetArgPointee<1>("3.0.0.0"), Return(true)));
      EXPECT_CALL(
          delegate,
          OnExtensionDownloadFailed(
              "3333", ExtensionDownloaderDelegate::Error::NO_UPDATE_AVAILABLE,
              _, _, _))
          .WillOnce(InvokeWithoutArgs(&delegate,
                                      &MockExtensionDownloaderDelegate::Quit));
      delegate.Wait();
      Mock::VerifyAndClearExpectations(&delegate);
      fetch3_url = GURL();

      RunUntilIdle();
    }

    // fetch4_url
    {
      helper.StartUpdateCheck(std::move(fetch4));
      RunUntilIdle();
      // The last fetcher has an update.
      const std::string kUpdateAvailable = CreateUpdateManifest(
          {UpdateManifestItem("4444")
               .version("4.0.42.0")
               .prodversionmin("4.0.42.0")
               .codebase("http://example.com/extension_1.2.3.4.crx")});
      helper.test_url_loader_factory().AddResponse(
          fetch4_url.spec(), kUpdateAvailable, net::HTTP_OK);
      EXPECT_CALL(delegate, IsExtensionPending("4444")).WillOnce(Return(false));
      EXPECT_CALL(delegate, GetExtensionExistingVersion("4444", _))
          .WillOnce(DoAll(SetArgPointee<1>("4.0.0.0"), Return(true)));

      // Verify that the downloader decided to update this extension.
      EXPECT_CALL(delegate,
                  OnExtensionUpdateFound("4444", _, base::Version("4.0.42.0")))
          .WillOnce([&delegate]() { delegate.Quit(); });
      delegate.Wait();
      Mock::VerifyAndClearExpectations(&delegate);

      fetch4_url = GURL();
    }
    if (helper.downloader().HasActiveManifestRequestForTesting())
      ADD_FAILURE() << "Unexpected load";
  }

  void TestManifestRetryDownloading() {
    ExtensionDownloaderTestHelper helper;
    MockExtensionDownloaderDelegate& delegate = helper.delegate();
    helper.downloader().manifests_queue_.set_backoff_policy(kNoBackoffPolicy);

    GURL kUpdateUrl("http://localhost/manifest1");

    std::unique_ptr<ManifestFetchData> fetch(
        CreateManifestFetchData(kUpdateUrl));
    DownloadPingData zero_days(/*rollcall=*/0, /*active=*/0, /*enabled=*/true,
                               /*disable_reasons=*/{});
    fetch->AddExtension("1111", "1.0", &zero_days,
                        ExtensionDownloaderTestHelper::kEmptyUpdateUrlData,
                        std::string(), ManifestLocation::kInternal,
                        DownloadFetchPriority::kBackground);

    // This will start the first fetcher.
    helper.StartUpdateCheck(std::move(fetch));
    RunUntilIdle();

    // ExtensionDownloader should retry kMaxRetries times and then fail.
    EXPECT_CALL(
        delegate,
        OnExtensionDownloadFailed(
            "1111", ExtensionDownloaderDelegate::Error::MANIFEST_FETCH_FAILED,
            _, _, _));
    helper.test_url_loader_factory().SetInterceptor(base::BindLambdaForTesting(
        [&](const network::ResourceRequest& request) {
          EXPECT_TRUE(request.load_flags == kExpectedLoadFlags);
          EXPECT_EQ(network::mojom::CredentialsMode::kInclude,
                    request.credentials_mode);
        }));
    for (int i = 0; i <= ExtensionDownloader::kMaxRetries; ++i) {
      // All fetches will fail.
      auto* request = helper.GetPendingRequest(0);
      // Code 5xx causes ExtensionDownloader to retry.
      helper.test_url_loader_factory().SimulateResponseForPendingRequest(
          request->request.url, network::URLLoaderCompletionStatus(net::OK),
          network::CreateURLResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), "");
      RunUntilIdle();
    }
    Mock::VerifyAndClearExpectations(&delegate);

    // For response codes that are not in the 5xx range ExtensionDownloader
    // should not retry.
    fetch.reset(CreateManifestFetchData(kUpdateUrl));
    fetch->AddExtension("1111", "1.0", &zero_days,
                        ExtensionDownloaderTestHelper::kEmptyUpdateUrlData,
                        std::string(), ManifestLocation::kInternal,
                        DownloadFetchPriority::kBackground);

    // This will start the first fetcher.
    helper.StartUpdateCheck(std::move(fetch));
    RunUntilIdle();

    EXPECT_CALL(
        delegate,
        OnExtensionDownloadFailed(
            "1111", ExtensionDownloaderDelegate::Error::MANIFEST_FETCH_FAILED,
            _, _, _));

    // The first fetch will fail, and require retrying.
    {
      auto* request = helper.GetPendingRequest(0);
      helper.test_url_loader_factory().SimulateResponseForPendingRequest(
          request->request.url, network::URLLoaderCompletionStatus(net::OK),
          network::CreateURLResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), "");
    }
    RunUntilIdle();

    // The second fetch will fail with response 400 and should not cause
    // ExtensionDownloader to retry.
    {
      auto* request = helper.GetPendingRequest(0);
      helper.test_url_loader_factory().SimulateResponseForPendingRequest(
          request->request.url, network::URLLoaderCompletionStatus(net::OK),
          network::CreateURLResponseHead(net::HTTP_BAD_REQUEST), "");
    }
    RunUntilIdle();

    Mock::VerifyAndClearExpectations(&delegate);
  }

  void TestManifestCredentialsNonWebstore() {
    ExtensionDownloaderTestHelper helper;
    helper.downloader().manifests_queue_.set_backoff_policy(kNoBackoffPolicy);

    GURL kUpdateUrl("http://localhost/manifest1");

    std::unique_ptr<ManifestFetchData> fetch(
        CreateManifestFetchData(kUpdateUrl));
    DownloadPingData zero_days(/*rollcall=*/0, /*active=*/0, /*enabled=*/true,
                               /*disable_reasons=*/{});
    fetch->AddExtension("1111", "1.0", &zero_days,
                        ExtensionDownloaderTestHelper::kEmptyUpdateUrlData,
                        std::string(), ManifestLocation::kInternal,
                        DownloadFetchPriority::kBackground);

    helper.StartUpdateCheck(std::move(fetch));
    RunUntilIdle();

    helper.test_url_loader_factory().SetInterceptor(base::BindLambdaForTesting(
        [&](const network::ResourceRequest& request) {
          EXPECT_EQ(network::mojom::CredentialsMode::kInclude,
                    request.credentials_mode);
        }));
    auto* request = helper.GetPendingRequest(0);
    helper.test_url_loader_factory().SimulateResponseForPendingRequest(
        request->request.url, network::URLLoaderCompletionStatus(net::OK),
        network::CreateURLResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), "");
    RunUntilIdle();
  }

  void TestManifestCredentialsWebstore() {
    ExtensionDownloaderTestHelper helper;
    helper.downloader().manifests_queue_.set_backoff_policy(kNoBackoffPolicy);

    GURL kUpdateUrl(extension_urls::kChromeWebstoreUpdateURL);

    std::unique_ptr<ManifestFetchData> fetch(
        CreateManifestFetchData(kUpdateUrl));
    DownloadPingData zero_days(/*rollcall=*/0, /*active=*/0, /*enabled=*/true,
                               /*disable_reasons=*/{});
    fetch->AddExtension("1111", "1.0", &zero_days,
                        ExtensionDownloaderTestHelper::kEmptyUpdateUrlData,
                        std::string(), ManifestLocation::kInternal,
                        DownloadFetchPriority::kBackground);

    helper.StartUpdateCheck(std::move(fetch));
    RunUntilIdle();

    helper.test_url_loader_factory().SetInterceptor(base::BindLambdaForTesting(
        [&](const network::ResourceRequest& request) {
          EXPECT_EQ(network::mojom::CredentialsMode::kOmit,
                    request.credentials_mode);
        }));
    auto* request = helper.GetPendingRequest(0);
    helper.test_url_loader_factory().SimulateResponseForPendingRequest(
        request->request.url, network::URLLoaderCompletionStatus(net::OK),
        network::CreateURLResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), "");
    RunUntilIdle();
  }

  // Checks that the manifest is fetched with a priority of |net::MEDIUM| (which
  // maps to |TaskPriority::USER_BLOCKING| for the task runner) when the active
  // request's |fetch_priority| is in the FOREGROUND.
  void TestManifestFetchPriority(DownloadFetchPriority fetch_priority) {
    ExtensionDownloaderTestHelper helper;
    helper.downloader().manifests_queue_.set_backoff_policy(kNoBackoffPolicy);
    GURL test_url("http://localhost/manifest1");
    std::unique_ptr<ManifestFetchData> fetch(
        CreateManifestFetchData(test_url));
    DownloadPingData zero_days(/*rollcall=*/0, /*active=*/0, /*enabled=*/true,
                               /*disable_reasons=*/{});

    fetch->AddExtension("1111", "1.0", &zero_days,
                        ExtensionDownloaderTestHelper::kEmptyUpdateUrlData,
                        std::string(), ManifestLocation::kInternal,
                        fetch_priority);

    helper.StartUpdateCheck(std::move(fetch));
    RunUntilIdle();

    auto* request = helper.GetPendingRequest(0);
    helper.test_url_loader_factory().SimulateResponseForPendingRequest(
        request->request.url, network::URLLoaderCompletionStatus(net::OK),
        network::CreateURLResponseHead(net::HTTP_INTERNAL_SERVER_ERROR), "");
    RunUntilIdle();

    if (fetch_priority == DownloadFetchPriority::kForeground) {
      EXPECT_EQ(net::MEDIUM, request->request.priority);
    } else {
      EXPECT_EQ(net::IDLE, request->request.priority);
    }
  }

  // Checks that the crx of a given extension is downloaded with a priority of
  // |net::MEDIUM| (maps to |TaskPriority::USER_BLOCKING| for the task runner)
  // when the active request's |fetch_priority| is in the FOREGROUND.
  void TestSingleExtensionDownloadingPriority(
      DownloadFetchPriority fetch_priority) {
    ExtensionUpdater::ScopedSkipScheduledCheckForTest skip_scheduled_checks;
    ExtensionDownloaderTestHelper helper;
    TestDownloaderFactory factory(helper.url_loader_factory());
    TestCrxInstallerFactory crx_installer_factory;
    ExtensionUpdater updater(profile());
    updater.InitAndEnable(extension_prefs(), pref_service(), kUpdateFrequency,
                          nullptr, factory.GetDownloaderFactory());
    updater.set_crx_installer_factory_for_test(&crx_installer_factory);
    MockExtensionDownloaderDelegate downloader_delegate;
    downloader_delegate.DelegateTo(&updater);
    factory.OverrideDownloaderDelegate(&downloader_delegate);
    updater.Start();
    updater.EnsureDownloaderCreated();
    updater.downloader_->extensions_queue_.set_backoff_policy(kNoBackoffPolicy);

    GURL test_url("http://localhost/extension.crx");
    const std::string id(32, 'a');
    std::string hash;
    CRXFileInfo crx_file_info;
    base::Version version("0.0.1");
    ExtensionDownloaderTask task = CreateDownloaderTask(id);
    task.fetch_priority = fetch_priority;
    std::unique_ptr<ExtensionDownloader::ExtensionFetch> fetch =
        std::make_unique<ExtensionDownloader::ExtensionFetch>(
            std::move(task), test_url, hash, version.GetString(),
            fetch_priority);

    updater.downloader_->FetchUpdatedExtension(std::move(fetch), std::nullopt);

    auto* request = helper.GetPendingRequest(0);
    if (fetch_priority == DownloadFetchPriority::kForeground) {
      EXPECT_EQ(net::MEDIUM, request->request.priority);
    } else {
      EXPECT_EQ(net::IDLE, request->request.priority);
    }
  }

  void TestSingleExtensionDownloading(bool pending, bool retry, bool fail) {
    ExtensionUpdater::ScopedSkipScheduledCheckForTest skip_scheduled_checks;
    ExtensionDownloaderTestHelper helper;
    TestDownloaderFactory factory(helper.url_loader_factory());
    TestCrxInstallerFactory crx_installer_factory;
    ExtensionUpdater updater(profile());
    updater.InitAndEnable(extension_prefs(), pref_service(), kUpdateFrequency,
                          nullptr, factory.GetDownloaderFactory());
    updater.set_crx_installer_factory_for_test(&crx_installer_factory);
    MockExtensionDownloaderDelegate downloader_delegate;
    downloader_delegate.DelegateTo(&updater);
    factory.OverrideDownloaderDelegate(&downloader_delegate);
    updater.Start();
    updater.EnsureDownloaderCreated();
    updater.downloader_->extensions_queue_.set_backoff_policy(kNoBackoffPolicy);

    GURL test_url("http://localhost/extension.crx");

    const std::string id(32, 'a');
    std::string hash;
    CRXFileInfo crx_file_info;
    base::Version version("0.0.1");
    std::set<int> requests;
    requests.insert(0);
    std::unique_ptr<ExtensionDownloader::ExtensionFetch> fetch =
        std::make_unique<ExtensionDownloader::ExtensionFetch>(
            CreateDownloaderTask(id), test_url, hash, version.GetString(),
            DownloadFetchPriority::kBackground);
    updater.downloader_->FetchUpdatedExtension(std::move(fetch), std::nullopt);

    if (pending) {
      const bool kIsFromSync = true;
      const bool kMarkAcknowledged = false;
      const bool kRemoteInstall = false;
      PendingExtensionManager* pending_extension_manager =
          PendingExtensionManager::Get(profile());
      pending_extension_manager->AddForTesting(PendingExtensionInfo(
          id, std::string(), test_url, version, &ShouldAlwaysInstall,
          kIsFromSync, ManifestLocation::kInternal, Extension::NO_FLAGS,
          kMarkAcknowledged, kRemoteInstall));
    }

    if (retry) {
      EXPECT_CALL(downloader_delegate, OnExtensionDownloadRetryForTests())
          .WillOnce(DoAll(
              InvokeWithoutArgs(&downloader_delegate,
                                &MockExtensionDownloaderDelegate::Quit),
              InvokeWithoutArgs(&helper, &ExtensionDownloaderTestHelper::
                                             ClearURLLoaderFactoryResponses)));
      helper.test_url_loader_factory().AddResponse(
          test_url.spec(), "", net::HTTP_INTERNAL_SERVER_ERROR);
      downloader_delegate.Wait();
      EXPECT_TRUE(updater.downloader_->extension_loader_);
    }

    if (fail) {
      EXPECT_CALL(downloader_delegate,
                  OnExtensionDownloadFailed(id, _, _, requests, _))
          .WillOnce(DoAll(
              InvokeWithoutArgs(&downloader_delegate,
                                &MockExtensionDownloaderDelegate::Quit),
              InvokeWithoutArgs(&helper, &ExtensionDownloaderTestHelper::
                                             ClearURLLoaderFactoryResponses)));
      helper.test_url_loader_factory().AddResponse(
          test_url.spec(), "Any content. It is irrelevant.",
          net::HTTP_NOT_FOUND);
      downloader_delegate.Wait();
    } else {
      EXPECT_TRUE(updater.downloader_->extension_loader_);
      EXPECT_CALL(downloader_delegate,
                  OnExtensionDownloadFinished_(_, _, _, _, requests, _))
          .WillOnce(
              DoAll(testing::SaveArg<0>(&crx_file_info),
                    InvokeWithoutArgs(&downloader_delegate,
                                      &MockExtensionDownloaderDelegate::Quit)));
      helper.test_url_loader_factory().AddResponse(
          test_url.spec(), "Any content. It is irrelevant.");
      downloader_delegate.Wait();
      EXPECT_EQ(version, crx_file_info.expected_version);
    }

    if (fail) {
      // Don't expect any extension to have been installed.
      EXPECT_TRUE(crx_installer_factory.crx_extension_id().empty());
    } else {
      // Expect that ExtensionUpdater asked the mock extensions service to
      // install a file with the test data for the right id.
      EXPECT_EQ(id, crx_file_info.extension_id);
      base::FilePath tmpfile_path = crx_file_info.path;
      EXPECT_FALSE(tmpfile_path.empty());
    }
  }

  // Helper to create a new test file of zeros.
  void CreateFile(const base::FilePath& file,
                  size_t size,
                  const base::Time& timestamp) {
    const std::string data(size, 0);
    EXPECT_TRUE(base::WriteFile(file, data));
    EXPECT_TRUE(base::TouchFile(file, timestamp, timestamp));
  }

#if BUILDFLAG(IS_CHROMEOS)
  // This tests the condition when the entry for the crx file is already
  // present in the cache but the crx file is itself corrupted. In this case,
  // after detecting the corruption of the crx file, it's entry should be
  // removed from the cache and re-downloaded from the link in the update
  // manifest.
  void TestCacheCorruption() {
    const char kTestExtensionId[] = "test_app";
    const std::string version = "1.1";
    const std::string hash = "abcd";
    ExtensionDownloaderTestHelper helper;
    TestDownloaderFactory factory(helper.url_loader_factory());

    // Set update manifest fetch data and result.
    GURL kUpdateURL("http://localhost/foo");
    std::unique_ptr<ManifestFetchData> fetch(
        CreateManifestFetchData(kUpdateURL));
    AddExtensionToFetchDataForTesting(fetch.get(), kTestExtensionId, "1.0",
                                      kUpdateURL);
    const std::string manifest = CreateUpdateManifest(
        {UpdateManifestItem(kTestExtensionId)
             .version(version)
             .hash(hash)
             .codebase("http://example.com/extension_1.2.3.4.crx")
             .prodversionmin("1.1")});
    helper.test_url_loader_factory().AddResponse(fetch->full_url().spec(),
                                                 manifest, net::HTTP_OK);

    // We need crx installer to install the crx file and also for mock extension
    // service. CrxInstallers require a real ExtensionService.  Create one on
    // the testing profile.  Any action the CrxInstallers take is on the testing
    // profile's extension service, not on our mock |service|.
    TestingProfile profile;
    // TODO(crbug.com/396722906): Delete this when CrxInstaller no longer has
    // dependencies on ExtensionService.
    static_cast<TestExtensionSystem*>(ExtensionSystem::Get(&profile))
        ->CreateExtensionService(base::CommandLine::ForCurrentProcess(),
                                 base::FilePath(), false);
    scoped_refptr<MockCrxInstaller> mock_installer =
        base::MakeRefCounted<MockCrxInstaller>(&profile);

    // Do nothing when called the first time, by ExtensionUpdater
    // But do the real action when called later on by this test code.
    EXPECT_CALL(*mock_installer, InstallCrxFile(_))
        .WillOnce(Return())
        .WillOnce([&mock_installer](const CRXFileInfo& info) {
          return mock_installer->CrxInstaller::InstallCrxFile(info);
        });
    // Just let the real CrxInstaller implementation have the callback.
    EXPECT_CALL(*mock_installer, AddInstallerCallback(_))
        .WillOnce([&](CrxInstaller::InstallerResultCallback callback) {
          mock_installer->CrxInstaller::AddInstallerCallback(
              std::move(callback));
        });

    mock_installer->set_expected_id(kTestExtensionId);
    mock_installer->set_expected_hash(hash);

    // Create mock extension service for test. We need this mock service so that
    // the extension updater process can be intercepted before the installer
    // which is then called explicitly.
    TestCrxInstallerFactory crx_installer_factory;
    crx_installer_factory.AddFakeCrxInstaller(kTestExtensionId, mock_installer);

    ExtensionUpdater updater(this->profile());
    updater.InitAndEnable(extension_prefs(), pref_service(), kUpdateFrequency,
                          nullptr, factory.GetDownloaderFactory());
    updater.set_crx_installer_factory_for_test(&crx_installer_factory);
    MockExtensionDownloaderDelegate& downloader_delegate = helper.delegate();
    downloader_delegate.DelegateTo(&updater);
    factory.OverrideDownloaderDelegate(&downloader_delegate);
    updater.Start();

    // Create and initialize local cache.
    const base::Time now = base::Time::Now();
    base::ScopedTempDir cache_dir;
    ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
    const base::FilePath cache_path = cache_dir.GetPath();
    CreateFile(cache_path.Append(LocalExtensionCache::kCacheReadyFlagFileName),
               0, now);

    ExtensionCacheImpl test_extension_cache(
        std::make_unique<ChromeOSExtensionCacheDelegate>(cache_path));
    base::RunLoop cache_init_run_loop;
    test_extension_cache.Start(cache_init_run_loop.QuitClosure());
    cache_init_run_loop.Run();

    // Create crx file in a temp directory.
    base::ScopedTempDir tmp_dir;
    ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
    const base::FilePath tmp_path = tmp_dir.GetPath();
    const base::FilePath filename =
        tmp_path.Append(LocalExtensionCache::ExtensionFileName(
            kTestExtensionId, version, "" /* hash */));
    // Create a small file of zeroes, e.g. 100 bytes size.
    CreateFile(filename, 100, now - base::Seconds(3));

    // Add crx file entry in the cache.
    base::RunLoop put_extension_run_loop;
    base::FilePath cached_crx_path;
    test_extension_cache.AllowCaching("test_app");
    test_extension_cache.PutExtension(
        kTestExtensionId, "" /* expected hash*/, filename, version,
        base::BindLambdaForTesting(
            [&put_extension_run_loop, &cached_crx_path](
                const base::FilePath& file_path, bool file_ownership_passed) {
              cached_crx_path = file_path;
              put_extension_run_loop.Quit();
            }));
    put_extension_run_loop.Run();

    // Set cache in extension downloader.
    helper.downloader().StartAllPending(&test_extension_cache);

    EXPECT_CALL(downloader_delegate, IsExtensionPending(kTestExtensionId))
        .WillOnce(Return(true));
    // Download the update manifest for the extension, find the same extension
    // version in the cache, start installing the cached crx file which fails
    // due to unpacker error and is hence, removed from the cache and
    // re-downlaoded for installation.
    testing::Sequence sequence;
    EXPECT_CALL(downloader_delegate,
                OnExtensionDownloadStageChanged(
                    kTestExtensionId,
                    ExtensionDownloaderDelegate::Stage::QUEUED_FOR_MANIFEST))
        .Times(testing::AnyNumber());
    EXPECT_CALL(downloader_delegate,
                OnExtensionDownloadStageChanged(
                    kTestExtensionId,
                    ExtensionDownloaderDelegate::Stage::DOWNLOADING_MANIFEST))
        .InSequence(sequence);
    EXPECT_CALL(downloader_delegate,
                OnExtensionDownloadStageChanged(
                    kTestExtensionId,
                    ExtensionDownloaderDelegate::Stage::PARSING_MANIFEST))
        .InSequence(sequence);
    EXPECT_CALL(downloader_delegate,
                OnExtensionDownloadStageChanged(
                    kTestExtensionId,
                    ExtensionDownloaderDelegate::Stage::MANIFEST_LOADED))
        .InSequence(sequence);
    EXPECT_CALL(downloader_delegate,
                OnExtensionDownloadCacheStatusRetrieved(
                    kTestExtensionId,
                    ExtensionDownloaderDelegate::CacheStatus::CACHE_HIT))
        .InSequence(sequence);
    EXPECT_CALL(
        downloader_delegate,
        OnExtensionDownloadStageChanged(
            kTestExtensionId, ExtensionDownloaderDelegate::Stage::FINISHED))
        .InSequence(sequence);
    EXPECT_CALL(downloader_delegate,
                OnExtensionDownloadStageChanged(
                    kTestExtensionId,
                    ExtensionDownloaderDelegate::Stage::DOWNLOADING_CRX))
        .InSequence(sequence);

    helper.StartUpdateCheck(std::move(fetch));

    content::RunAllTasksUntilIdle();

    LoadErrorReporter::Init(false);

    updater.SetExtensionCacheForTesting(&test_extension_cache);
    CRXFileInfo crx_info(cached_crx_path, GetTestVerifierFormat());
    crx_info.extension_id = kTestExtensionId;
    crx_info.expected_hash = hash;

    // This time call the real InstallCrxFile implementation (see expectation
    // set above for mock_installer).
    mock_installer->InstallCrxFile(crx_info);

    content::RunAllTasksUntilIdle();

    testing::Mock::VerifyAndClearExpectations(&downloader_delegate);
  }
#endif

  // Update a single extension in an environment where the download request
  // initially responds with a 403 status. If |identity_provider| is not NULL,
  // this will first expect a request which includes an Authorization header
  // with an OAuth2 bearer token; otherwise, or if OAuth2 failure is simulated,
  // this expects the downloader to fall back onto cookie-based credentials.
  void TestProtectedDownload(
      const std::string& url_prefix,
      bool enable_oauth2,
      bool succeed_with_oauth2,
      int valid_authuser,
      int max_authuser) {
    ExtensionDownloaderTestHelper helper;
    TestDownloaderFactory factory(helper.url_loader_factory());
    TestCrxInstallerFactory crx_installer_factory;
    const ExtensionDownloader::Factory& downloader_factory =
        enable_oauth2 ? factory.GetAuthenticatedDownloaderFactory()
                      : factory.GetDownloaderFactory();
    ExtensionUpdater updater(profile());
    updater.InitAndEnable(extension_prefs(), pref_service(), kUpdateFrequency,
                          nullptr, downloader_factory);
    updater.set_crx_installer_factory_for_test(&crx_installer_factory);

    MockExtensionDownloaderDelegate downloader_delegate;
    downloader_delegate.DelegateTo(&updater);
    factory.OverrideDownloaderDelegate(&downloader_delegate);

    updater.Start();
    updater.EnsureDownloaderCreated();
    updater.downloader_->extensions_queue_.set_backoff_policy(kNoBackoffPolicy);

    GURL test_url(base::StringPrintf("%s/extension.crx", url_prefix.c_str()));

    const std::string id(32, 'a');
    std::string hash;
    base::Version version("0.0.1");
    std::unique_ptr<ExtensionDownloader::ExtensionFetch> extension_fetch =
        std::make_unique<ExtensionDownloader::ExtensionFetch>(
            CreateDownloaderTask(id), test_url, hash, version.GetString(),
            DownloadFetchPriority::kBackground);
    updater.downloader_->FetchUpdatedExtension(std::move(extension_fetch),
                                               std::nullopt);

    EXPECT_EQ(
        kExpectedLoadFlags,
        updater.downloader_->last_extension_loader_load_flags_for_testing_);

    // Fake a 403 response.
    EXPECT_CALL(downloader_delegate, OnExtensionDownloadRetryForTests())
        .WillOnce(DoAll(
            InvokeWithoutArgs(&downloader_delegate,
                              &MockExtensionDownloaderDelegate::Quit),
            InvokeWithoutArgs(&helper, &ExtensionDownloaderTestHelper::
                                           ClearURLLoaderFactoryResponses)));
    helper.test_url_loader_factory().AddResponse(test_url.spec(), "",
                                                 net::HTTP_FORBIDDEN);
    downloader_delegate.Wait();

    // Only call out to WaitForAccessTokenRequest(...) method below if
    // HTTPS is in use in a google domain and oauth is explicitly enabled.
    // Otherwise, test will await an access token request that have not
    // (and will not) happen.
    //
    // Note that in case the condition below isn't satisfied, the download
    // proceeds normally, but the request does not carry an 'Authorization'
    // HTTP header.
    if (enable_oauth2 && test_url.DomainIs("google.com") &&
        test_url.SchemeIsCryptographic()) {
      factory.identity_test_env()
          ->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
              factory.account_id(), kFakeOAuth2Token, base::Time::Now());
    }

    bool using_oauth2 = false;
    int expected_load_flags = kExpectedLoadFlags;

    // Verify that the fetch has had its credentials properly incremented.
    EXPECT_TRUE(updater.downloader_->extension_loader_);
    net::HttpRequestHeaders fetch_headers =
        updater.downloader_
            ->last_extension_loader_resource_request_headers_for_testing_;
    // If the download URL is not https, no credentials should be provided.
    if (!test_url.SchemeIsCryptographic()) {
      // No cookies.
      EXPECT_EQ(
          kExpectedLoadFlags,
          updater.downloader_->last_extension_loader_load_flags_for_testing_);
      // No Authorization header.
      EXPECT_FALSE(
          fetch_headers.HasHeader(net::HttpRequestHeaders::kAuthorization));
      expected_load_flags = kExpectedLoadFlags;
    } else {
      // HTTPS is in use, so credentials are allowed.
      if (enable_oauth2 && test_url.DomainIs("google.com")) {
        // If an IdentityProvider is present and the URL is a google.com
        // URL, the fetcher should be in OAuth2 mode after the intitial
        // challenge.
        EXPECT_TRUE(fetch_headers.HasHeader(
            net::HttpRequestHeaders::kAuthorization));
        std::string expected_header_value = base::StringPrintf("Bearer %s",
            kFakeOAuth2Token);
        std::string actual_header_value =
            fetch_headers.GetHeader(net::HttpRequestHeaders::kAuthorization)
                .value_or(std::string());
        EXPECT_EQ(expected_header_value, actual_header_value);
        using_oauth2 = true;
      } else {
        // No IdentityProvider (or no google.com), so expect cookies instead of
        // an Authorization header.
        EXPECT_FALSE(fetch_headers.HasHeader(
            net::HttpRequestHeaders::kAuthorization));
        EXPECT_EQ(
            kExpectedLoadFlagsForDownloadWithCookies,
            updater.downloader_->last_extension_loader_load_flags_for_testing_);
        expected_load_flags = kExpectedLoadFlagsForDownloadWithCookies;
      }
    }

    bool success = false;
    if (using_oauth2) {
      if (succeed_with_oauth2) {
        success = true;
      } else {
        // Simulate OAuth2 failure and ensure that we fall back on cookies.
        EXPECT_CALL(downloader_delegate, OnExtensionDownloadRetryForTests())
            .WillOnce(
                DoAll(InvokeWithoutArgs(&downloader_delegate,
                                        &MockExtensionDownloaderDelegate::Quit),
                      InvokeWithoutArgs(&helper,
                                        &ExtensionDownloaderTestHelper::
                                            ClearURLLoaderFactoryResponses)));
        helper.test_url_loader_factory().AddResponse(test_url.spec(), "",
                                                     net::HTTP_FORBIDDEN);
        downloader_delegate.Wait();

        const ExtensionDownloader::ExtensionFetch& fetch =
            *updater.downloader_->extensions_queue_.active_request();
        EXPECT_EQ(0, GetAuthUserQueryValue(fetch.url));
        EXPECT_EQ(ExtensionDownloader::ExtensionFetch::CREDENTIALS_COOKIES,
                  fetch.credentials);

        EXPECT_TRUE(updater.downloader_->extension_loader_);
        fetch_headers =
            updater.downloader_
                ->last_extension_loader_resource_request_headers_for_testing_;
        EXPECT_FALSE(
            fetch_headers.HasHeader(net::HttpRequestHeaders::kAuthorization));
        EXPECT_EQ(
            kExpectedLoadFlagsForDownloadWithCookies,
            updater.downloader_->last_extension_loader_load_flags_for_testing_);
        expected_load_flags = kExpectedLoadFlagsForDownloadWithCookies;
      }
    }

    if (!success) {
      // Not yet ready to simulate a successful fetch. At this point we begin
      // simulating cookie-based authentication with increasing values of
      // authuser (starting from 0.)
      int user_index = 0;
      for (; user_index <= max_authuser; ++user_index) {
        const ExtensionDownloader::ExtensionFetch& fetch =
            *updater.downloader_->extensions_queue_.active_request();
        EXPECT_EQ(user_index, GetAuthUserQueryValue(fetch.url));
        if (user_index == valid_authuser) {
          success = true;
          break;
        }
        // Simulate an authorization failure which should elicit an increment
        // of the authuser value.
        EXPECT_TRUE(updater.downloader_->extension_loader_);
        EXPECT_EQ(
            expected_load_flags,
            updater.downloader_->last_extension_loader_load_flags_for_testing_);
        EXPECT_CALL(downloader_delegate, OnExtensionDownloadRetryForTests())
            .WillOnce(
                DoAll(InvokeWithoutArgs(&downloader_delegate,
                                        &MockExtensionDownloaderDelegate::Quit),
                      InvokeWithoutArgs(&helper,
                                        &ExtensionDownloaderTestHelper::
                                            ClearURLLoaderFactoryResponses)));
        helper.test_url_loader_factory().AddResponse(
            fetch.url.spec(), "whatever", net::HTTP_FORBIDDEN);
        downloader_delegate.Wait();
      }

      // Simulate exhaustion of all available authusers.
      if (!success && user_index > max_authuser) {
        const ExtensionDownloader::ExtensionFetch& fetch =
            *updater.downloader_->extensions_queue_.active_request();
        EXPECT_TRUE(updater.downloader_->extension_loader_);
        helper.test_url_loader_factory().AddResponse(
            fetch.url.spec(), std::string(), net::HTTP_UNAUTHORIZED);
        EXPECT_CALL(downloader_delegate,
                    OnExtensionDownloadFailed(_, _, _, _, _))
            .WillOnce(InvokeWithoutArgs(
                &downloader_delegate, &MockExtensionDownloaderDelegate::Quit));
        downloader_delegate.Wait();
      }
    }

    // Simulate successful authorization with a 200 response.
    if (success) {
      EXPECT_TRUE(updater.downloader_->extension_loader_);
      const ExtensionDownloader::ExtensionFetch& fetch =
          *updater.downloader_->extensions_queue_.active_request();

      CRXFileInfo crx_file_info;
      EXPECT_CALL(downloader_delegate,
                  OnExtensionDownloadFinished_(_, _, _, _, _, _))
          .WillOnce(
              DoAll(testing::SaveArg<0>(&crx_file_info),
                    InvokeWithoutArgs(&downloader_delegate,
                                      &MockExtensionDownloaderDelegate::Quit)));
      helper.test_url_loader_factory().AddResponse(fetch.url.spec(),
                                                   "whatever");
      downloader_delegate.Wait();

      // Verify installation would proceed as normal.
      EXPECT_EQ(id, crx_file_info.extension_id);
      base::FilePath tmpfile_path = crx_file_info.path;
      EXPECT_FALSE(tmpfile_path.empty());
    }
  }

  // Two extensions are updated.  If |updates_start_running| is true, the
  // mock extensions service has CreateUpdateInstaller(...) return the
  // fake CrxInstallers created by the test.  Otherwise, CreateUpdateInstaller()
  // returns nullptr, signaling install failures.
  void TestMultipleExtensionDownloading(bool updates_start_running) {
    ExtensionDownloaderTestHelper helper;
    TestDownloaderFactory factory(helper.url_loader_factory());
    TestCrxInstallerFactory crx_installer_factory;
    ExtensionUpdater updater(profile());
    updater.InitAndEnable(extension_prefs(), pref_service(), kUpdateFrequency,
                          nullptr, factory.GetDownloaderFactory());
    updater.set_crx_installer_factory_for_test(&crx_installer_factory);
    updater.Start();
    updater.EnsureDownloaderCreated();
    updater.downloader_->extensions_queue_.set_backoff_policy(kNoBackoffPolicy);

    EXPECT_THAT(GetRunningInstallIds(updater), testing::IsEmpty());

    GURL url1("http://localhost/extension1.crx");
    GURL url2("http://localhost/extension2.crx");

    const std::string id1(32, 'a');
    const std::string id2(32, 'b');

    std::string hash1;
    std::string hash2;

    std::string version1 = "0.1";
    std::string version2 = "0.1";

    // Start two fetches
    std::unique_ptr<ExtensionDownloader::ExtensionFetch> fetch1 =
        std::make_unique<ExtensionDownloader::ExtensionFetch>(
            CreateDownloaderTask(id1), url1, hash1, version1,
            DownloadFetchPriority::kBackground);
    std::unique_ptr<ExtensionDownloader::ExtensionFetch> fetch2 =
        std::make_unique<ExtensionDownloader::ExtensionFetch>(
            CreateDownloaderTask(id2), url2, hash2, version2,
            DownloadFetchPriority::kBackground);
    updater.downloader_->FetchUpdatedExtension(std::move(fetch1),
                                               std::optional<std::string>());
    updater.downloader_->FetchUpdatedExtension(std::move(fetch2),
                                               std::optional<std::string>());

    // Make the first fetch complete.
    EXPECT_TRUE(updater.downloader_->extension_loader_);
    EXPECT_EQ(
        kExpectedLoadFlags,
        updater.downloader_->last_extension_loader_load_flags_for_testing_);

    // We need some CrxInstallers, and CrxInstallers require a real
    // ExtensionService.  Create one on the testing profile.  Any action
    // the CrxInstallers take is on the testing profile's extension
    // service, not on our mock |service|.  This allows us to fake
    // the CrxInstaller actions we want.
    TestingProfile profile;
    // TODO(crbug.com/396722906): Delete this when CrxInstaller no longer has
    // dependencies on ExtensionService.
    static_cast<TestExtensionSystem*>(ExtensionSystem::Get(&profile))
        ->CreateExtensionService(base::CommandLine::ForCurrentProcess(),
                                 base::FilePath(), false);

    scoped_refptr<FakeCrxInstaller> fake_crx1 =
        base::MakeRefCounted<FakeCrxInstaller>(&profile);
    scoped_refptr<FakeCrxInstaller> fake_crx2 =
        base::MakeRefCounted<FakeCrxInstaller>(&profile);

    if (updates_start_running) {
      // Add mock CrxInstaller to be returned by
      // delegate.CreateUpdateInstaller().
      crx_installer_factory.AddFakeCrxInstaller(id1, fake_crx1);
      crx_installer_factory.AddFakeCrxInstaller(id2, fake_crx2);
    } else {
      // If we don't add mock CRX installers, the mock delegate will just return
      // nullptr, meaning a failure.
    }

    helper.test_url_loader_factory().AddResponse(
        url1.spec(), "Any content. This is irrelevant.", net::HTTP_OK);
    content::RunAllTasksUntilIdle();

    // Expect that the delegate was asked to do an install with the right data.
    base::FilePath tmpfile_path = crx_installer_factory.crx_install_path();
    EXPECT_FALSE(tmpfile_path.empty());
    EXPECT_EQ(id1, crx_installer_factory.crx_extension_id());
    RunUntilIdle();

    // Make sure the second fetch finished and asked the service to do an
    // update.
    EXPECT_TRUE(updater.downloader_->extension_loader_);
    EXPECT_EQ(
        kExpectedLoadFlags,
        updater.downloader_->last_extension_loader_load_flags_for_testing_);

    helper.test_url_loader_factory().AddResponse(
        url2.spec(), "Any other content. This is irrelevant.", net::HTTP_OK);
    content::RunAllTasksUntilIdle();

    if (updates_start_running) {
      // Both installations should have launched in parallel.
      EXPECT_THAT(GetRunningInstallIds(updater),
                  testing::UnorderedElementsAre(id1, id2));

      // Fake install notice.  This should end the first installation.
      fake_crx1->RunInstallerCallbacks(CrxInstallError(
          CrxInstallErrorType::OTHER, CrxInstallErrorDetail::NONE));
      content::RunAllTasksUntilIdle();
      EXPECT_THAT(GetRunningInstallIds(updater),
                  testing::UnorderedElementsAre(id2));

      fake_crx2->RunInstallerCallbacks(CrxInstallError(
          CrxInstallErrorType::OTHER, CrxInstallErrorDetail::NONE));
      content::RunAllTasksUntilIdle();
    }
    EXPECT_THAT(GetRunningInstallIds(updater), testing::IsEmpty());
  }

  void TestGalleryRequestsWithBrand(bool use_organic_brand_code) {
    google_brand::BrandForTesting brand_for_testing(
        use_organic_brand_code ? "GGLS" : "TEST");

    // We want to test a variety of combinations of expected ping conditions for
    // rollcall and active pings.
    auto ping_cases = std::to_array<int>({
        ManifestFetchData::kNeverPinged,
        0,
        1,
        5,
    });

    for (size_t i = 0; i < std::size(ping_cases); i++) {
      for (size_t j = 0; j < std::size(ping_cases); j++) {
        for (size_t k = 0; k < 2; k++) {
          int rollcall_ping_days = ping_cases[i];
          int active_ping_days = ping_cases[j];
          // Skip cases where rollcall_ping_days == -1, but
          // active_ping_days > 0, because rollcall_ping_days == -1 means the
          // app was just installed and this is the first update check after
          // installation.
          if (rollcall_ping_days == ManifestFetchData::kNeverPinged &&
              active_ping_days > 0)
            continue;

          bool active_bit = k > 0;
          TestGalleryRequests(rollcall_ping_days, active_ping_days, active_bit,
                              !use_organic_brand_code);
          ASSERT_FALSE(HasFailure()) <<
            " rollcall_ping_days=" << ping_cases[i] <<
            " active_ping_days=" << ping_cases[j] <<
            " active_bit=" << active_bit;
        }
      }
    }
  }

  // Test requests to both a Google server and a non-google server. This allows
  // us to test various combinations of installed (ie roll call) and active
  // (ie app launch) ping scenarios. The invariant is that each type of ping
  // value should be present at most once per day, and can be calculated based
  // on the delta between now and the last ping time (or in the case of active
  // pings, that delta plus whether the app has been active).
  void TestGalleryRequests(int rollcall_ping_days,
                           int active_ping_days,
                           bool active_bit,
                           bool expect_brand_code) {
    // Set up 2 mock extensions, one with a google.com update url and one
    // without.
    prefs_ = std::make_unique<TestExtensionPrefs>(
        base::SingleThreadTaskRunner::GetCurrentDefault(),
        std::make_unique<TestingProfile>());
    ExtensionDownloaderTestHelper helper;
    TestDownloaderFactory factory(helper.url_loader_factory());
    TestCrxInstallerFactory crx_installer_factory;
    ExtensionList tmp;
    GURL url1("http://clients2.google.com/service/update2/crx");
    GURL url2("http://www.somewebsite.com");
    CreateTestExtensions(1, 1, &tmp, &url1.possibly_invalid_spec(),
                         ManifestLocation::kInternal);
    CreateTestExtensions(2, 1, &tmp, &url2.possibly_invalid_spec(),
                         ManifestLocation::kInternal);
    EXPECT_EQ(2u, tmp.size());
    SetExtensions(tmp, ExtensionList());

    ExtensionPrefs* prefs = extension_prefs();
    const std::string& id = tmp[0]->id();
    Time now = Time::Now();
    if (rollcall_ping_days == 0) {
      prefs->SetLastPingDay(id, now - base::Seconds(15));
    } else if (rollcall_ping_days > 0) {
      Time last_ping_day =
          now - base::Days(rollcall_ping_days) - base::Seconds(15);
      prefs->SetLastPingDay(id, last_ping_day);
    }

    // Store a value for the last day we sent an active ping.
    if (active_ping_days == 0) {
      prefs->SetLastActivePingDay(id, now - base::Seconds(15));
    } else if (active_ping_days > 0) {
      Time last_active_ping_day =
          now - base::Days(active_ping_days) - base::Seconds(15);
      prefs->SetLastActivePingDay(id, last_active_ping_day);
    }
    if (active_bit)
      prefs->SetActiveBit(id, true);

    ExtensionUpdater updater(profile());
    updater.InitAndEnable(extension_prefs(), pref_service(), kUpdateFrequency,
                          nullptr, factory.GetDownloaderFactory());
    updater.set_crx_installer_factory_for_test(&crx_installer_factory);
    updater.Start();
    updater.CheckNow(ExtensionUpdater::CheckParams());

    // Make the updater do manifest fetching, and note the urls it tries to
    // fetch.
    std::vector<GURL> fetched_urls;
    ASSERT_TRUE(updater.downloader_->HasActiveManifestRequestForTesting());
    const ManifestFetchData& fetch =
        *updater.downloader_->manifests_queue_.active_request();
    fetched_urls.push_back(fetch.full_url());
    helper.test_url_loader_factory().AddResponse(
        fetched_urls[0].spec(), std::string(), net::HTTP_INTERNAL_SERVER_ERROR);
    RunUntilIdle();

    const ManifestFetchData& fetch2 =
        *updater.downloader_->manifests_queue_.active_request();
    fetched_urls.push_back(fetch2.full_url());

    // The urls could have been fetched in either order, so use the host to
    // tell them apart and note the query each used.
    GURL url1_fetch_url;
    GURL url2_fetch_url;
    std::string url1_query;
    std::string url2_query;
    if (fetched_urls[0].GetHost() == url1.GetHost()) {
      url1_fetch_url = fetched_urls[0];
      url2_fetch_url = fetched_urls[1];

      url1_query = fetched_urls[0].GetQuery();
      url2_query = fetched_urls[1].GetQuery();
    } else if (fetched_urls[0].GetHost() == url2.GetHost()) {
      url1_fetch_url = fetched_urls[1];
      url2_fetch_url = fetched_urls[0];
      url1_query = fetched_urls[1].GetQuery();
      url2_query = fetched_urls[0].GetQuery();
    } else {
      NOTREACHED();
    }

    std::map<std::string, ParamsMap> url1_ping_data =
        GetPingDataFromURL(url1_fetch_url);
    ParamsMap url1_params = ParamsMap();
    if (!url1_ping_data.empty() && url1_ping_data.contains(id)) {
      url1_params = url1_ping_data[id];
    }

    // First make sure the non-google query had no ping parameter.
    EXPECT_TRUE(GetPingDataFromURL(url2_fetch_url).empty());

    // Now make sure the google query had the correct ping parameter.
    bool did_rollcall = false;
    if (rollcall_ping_days != 0) {
      ASSERT_TRUE(url1_params.contains("r"));
      ASSERT_EQ(1u, url1_params["r"].size());
      EXPECT_EQ(base::NumberToString(rollcall_ping_days),
                *url1_params["r"].begin());
      did_rollcall = true;
    }
    if (active_bit && active_ping_days != 0 && did_rollcall) {
      ASSERT_TRUE(url1_params.contains("a"));
      ASSERT_EQ(1u, url1_params["a"].size());
      EXPECT_EQ(base::NumberToString(active_ping_days),
                *url1_params["a"].begin());
    }

    // Make sure the non-google query has no brand parameter.
    const std::string brand_string = "brand%3D";
    EXPECT_TRUE(url2_query.find(brand_string) == std::string::npos);

#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
    // Make sure the google query has a brand parameter, but only if the
    // brand is non-organic.
    if (expect_brand_code) {
      EXPECT_TRUE(url1_query.find(brand_string) != std::string::npos);
    } else {
      EXPECT_TRUE(url1_query.find(brand_string) == std::string::npos);
    }
#else
    // Chromium builds never add the brand to the parameter, even for google
    // queries.
    EXPECT_TRUE(url1_query.find(brand_string) == std::string::npos);
#endif

    RunUntilIdle();
  }

  // This makes sure that the extension updater properly stores the results
  // of a <daystart> tag from a manifest fetch in one of two cases: 1) This is
  // the first time we fetched the extension, or 2) We sent a ping value of
  // >= 1 day for the extension.
  void TestHandleManifestResults() {
    ExtensionDownloaderTestHelper helper;
    TestDownloaderFactory factory(helper.url_loader_factory());
    TestCrxInstallerFactory crx_installer_factory;
    GURL update_url("http://www.google.com/manifest");
    ExtensionList tmp;
    CreateTestExtensions(1, 1, &tmp, &update_url.spec(),
                         ManifestLocation::kInternal);
    SetExtensions(tmp, ExtensionList());

    ExtensionUpdater updater(profile());
    updater.InitAndEnable(extension_prefs(), pref_service(), kUpdateFrequency,
                          nullptr, factory.GetDownloaderFactory());
    updater.set_crx_installer_factory_for_test(&crx_installer_factory);
    updater.Start();
    updater.EnsureDownloaderCreated();

    std::unique_ptr<ManifestFetchData> fetch_data(
        CreateManifestFetchData(update_url));
    const Extension* extension = tmp[0].get();
    AddExtensionToFetchDataForTesting(fetch_data.get(), extension->id(),
                                      extension->VersionString(), update_url);
    auto results = std::make_unique<UpdateManifestResults>();
    constexpr int kDaystartElapsedSeconds = 750;
    results->daystart_elapsed_seconds = kDaystartElapsedSeconds;

    updater.downloader_->HandleManifestResults(std::move(fetch_data),
                                               std::move(results),
                                               /*error=*/std::nullopt);
    Time last_ping_day = extension_prefs()->LastPingDay(extension->id());
    EXPECT_FALSE(last_ping_day.is_null());
    int64_t seconds_diff = (Time::Now() - last_ping_day).InSeconds();
    EXPECT_LT(seconds_diff - kDaystartElapsedSeconds, 5);
  }

  void TestManifestAddExtension(DownloadFetchPriority data_priority,
                                DownloadFetchPriority extension_priority,
                                DownloadFetchPriority expected_priority) {
    const std::string id(32, 'a');
    const std::string version = "1.0";

    std::unique_ptr<ManifestFetchData> fetch_data(
        CreateManifestFetchData(GURL("http://localhost/foo"), data_priority));
    ASSERT_TRUE(fetch_data->AddExtension(
        id, version, ExtensionDownloaderTestHelper::GetTestPingData(),
        std::string(), std::string(), ManifestLocation::kInternal,
        extension_priority));
    ASSERT_EQ(expected_priority, fetch_data->fetch_priority());
  }

  void TestManifestMerge(DownloadFetchPriority data_priority,
                         DownloadFetchPriority other_priority,
                         DownloadFetchPriority expected_priority) {
    std::unique_ptr<ManifestFetchData> fetch_data(
        CreateManifestFetchData(GURL("http://localhost/foo"), data_priority));

    std::unique_ptr<ManifestFetchData> fetch_other(
        CreateManifestFetchData(GURL("http://localhost/foo"), other_priority));

    fetch_data->Merge(std::move(fetch_other));
    ASSERT_EQ(expected_priority, fetch_data->fetch_priority());
  }

 protected:
  std::unique_ptr<TestExtensionPrefs> prefs_;
  content::BrowserTaskEnvironment task_environment_;

  ManifestFetchData* CreateManifestFetchData(
      const GURL& update_url,
      DownloadFetchPriority fetch_priority) {
    return new ManifestFetchData(update_url, 0, "",
                                 UpdateQueryParams::Get(UpdateQueryParams::CRX),
                                 ManifestFetchData::PING, fetch_priority);
  }

  ManifestFetchData* CreateManifestFetchData(const GURL& update_url) {
    return CreateManifestFetchData(update_url,
                                   DownloadFetchPriority::kBackground);
  }

 private:
  content::InProcessUtilityThreadHelper in_process_utility_thread_helper_;

#if BUILDFLAG(IS_CHROMEOS)
  ash::ScopedCrosSettingsTestHelper cros_settings_test_helper_;
  user_manager::ScopedUserManager user_manager_{
      std::make_unique<user_manager::UserManagerImpl>(
          std::make_unique<ash::UserManagerDelegateImpl>(),
          TestingBrowserProcess::GetGlobal()->local_state(),
          ash::CrosSettings::Get())};
#endif

  StubExtensionRegistrarDelegate stub_extension_registrar_delegate_;
};

// Because we test some private methods of ExtensionUpdater, it's easier for the
// actual test code to live in ExtensionUpdaterTest methods instead of TEST_F
// subclasses where friendship with ExtensionUpdater is not inherited.

TEST_F(ExtensionUpdaterTest, TestExtensionUpdateCheckRequests) {
  TestExtensionUpdateCheckRequests(false);
}

TEST_F(ExtensionUpdaterTest, TestExtensionUpdateCheckRequestsPending) {
  TestExtensionUpdateCheckRequests(true);
}

TEST_F(ExtensionUpdaterTest, TestUpdateUrlData) {
  TestUpdateUrlDataEmpty();
  TestUpdateUrlDataSimple();
  TestUpdateUrlDataCompound();
  std::string gallery_url_spec = extension_urls::GetWebstoreUpdateUrl().spec();
  TestUpdateUrlDataFromUrl(gallery_url_spec, DownloadFetchPriority::kBackground,
                           1, true);
  TestUpdateUrlDataFromUrl(gallery_url_spec, DownloadFetchPriority::kForeground,
                           1, true);
  TestUpdateUrlDataFromUrl(gallery_url_spec, DownloadFetchPriority::kBackground,
                           2, true);
  TestUpdateUrlDataFromUrl(gallery_url_spec, DownloadFetchPriority::kForeground,
                           4, true);
  TestUpdateUrlDataFromUrl("http://example.com/update",
                           DownloadFetchPriority::kForeground, 4, false);
}

TEST_F(ExtensionUpdaterTest, TestInstallSource) {
  TestInstallSource();
}

TEST_F(ExtensionUpdaterTest, TestInstallLocation) {
  TestInstallLocation();
}

TEST_F(ExtensionUpdaterTest, TestDetermineUpdates) {
  TestDetermineUpdates();
}

TEST_F(ExtensionUpdaterTest, TestDetermineUpdatesPending) {
  TestDetermineUpdatesPending();
}

TEST_F(ExtensionUpdaterTest, TestDetermineUpdatesDuplicates) {
  TestDetermineUpdatesDuplicates();
}

TEST_F(ExtensionUpdaterTest, TestDetermineUpdatesError) {
  TestDetermineUpdatesError();
}

TEST_F(ExtensionUpdaterTest, TestMultipleManifestDownloading) {
  TestMultipleManifestDownloading();
}

TEST_F(ExtensionUpdaterTest, TestSingleExtensionDownloading) {
  TestSingleExtensionDownloading(false, false, false);
}

TEST_F(ExtensionUpdaterTest, TestSingleExtensionDownloadingPending) {
  TestSingleExtensionDownloading(true, false, false);
}

TEST_F(ExtensionUpdaterTest, TestSingleExtensionDownloadingWithRetry) {
  TestSingleExtensionDownloading(false, true, false);
}

TEST_F(ExtensionUpdaterTest, TestSingleExtensionDownloadingPendingWithRetry) {
  TestSingleExtensionDownloading(true, true, false);
}

TEST_F(ExtensionUpdaterTest, TestSingleExtensionDownloadingFailure) {
  TestSingleExtensionDownloading(false, false, true);
}

TEST_F(ExtensionUpdaterTest, TestSingleExtensionDownloadingFailureWithRetry) {
  TestSingleExtensionDownloading(false, true, true);
}

TEST_F(ExtensionUpdaterTest, TestSingleExtensionDownloadingFailurePending) {
  TestSingleExtensionDownloading(true, false, true);
}

#if BUILDFLAG(IS_CHROMEOS)
TEST_F(ExtensionUpdaterTest, TestCacheCorruptionCrxDownload) {
  TestCacheCorruption();
}
#endif

TEST_F(ExtensionUpdaterTest, ProtectedDownloadCookieAuth) {
  TestProtectedDownload(
      "https://chrome.google.com/webstore/download",
      false, false,  // No OAuth2 support
      0, 0);
}

TEST_F(ExtensionUpdaterTest, ProtectedDownloadCookieFailure) {
  TestProtectedDownload(
      "https://chrome.google.com/webstore/download",
      false, false,  // No OAuth2 support
      0, -1);  // max_authuser=-1 simulates no valid authuser value.
}

TEST_F(ExtensionUpdaterTest, ProtectedDownloadWithNonDefaultAuthUser1) {
  TestProtectedDownload("https://google.com", false, false, 1, 1);
}

TEST_F(ExtensionUpdaterTest, ProtectedDownloadWithNonDefaultAuthUser2) {
  TestProtectedDownload("https://google.com", false, false, 2, 2);
}

TEST_F(ExtensionUpdaterTest, ProtectedDownloadAuthUserExhaustionFailure) {
  TestProtectedDownload("https://google.com", false, false, 2, 5);
}

TEST_F(ExtensionUpdaterTest, ProtectedDownloadWithOAuth2Token) {
  TestProtectedDownload(
      "https://google.com",
      true, true,
      0, -1);
}

TEST_F(ExtensionUpdaterTest, ProtectedDownloadWithOAuth2Failure) {
  TestProtectedDownload(
      "https://google.com",
      true, false,
      0, -1);
}

TEST_F(ExtensionUpdaterTest, ProtectedDownloadNoOAuth2WithNonGoogleDomain) {
  TestProtectedDownload(
      "https://not-google.com",
      true, true,
      0, -1);
}

TEST_F(ExtensionUpdaterTest, ProtectedDownloadFailWithoutHTTPS) {
  TestProtectedDownload(
      "http://google.com",
      true, true,
      0, 0);
}

TEST_F(ExtensionUpdaterTest, TestMultipleExtensionDownloadingUpdatesFail) {
  TestMultipleExtensionDownloading(false);
}
TEST_F(ExtensionUpdaterTest, TestMultipleExtensionDownloadingUpdatesSucceed) {
  TestMultipleExtensionDownloading(true);
}

TEST_F(ExtensionUpdaterTest, TestManifestRetryDownloading) {
  TestManifestRetryDownloading();
}

TEST_F(ExtensionUpdaterTest, DISABLED_TestGalleryRequestsWithOrganicBrand) {
  TestGalleryRequestsWithBrand(true);
}

TEST_F(ExtensionUpdaterTest, DISABLED_TestGalleryRequestsWithNonOrganicBrand) {
  TestGalleryRequestsWithBrand(false);
}

TEST_F(ExtensionUpdaterTest, TestHandleManifestResults) {
  TestHandleManifestResults();
}

TEST_F(ExtensionUpdaterTest, TestNonAutoUpdateableLocations) {
  ExtensionDownloaderTestHelper helper;
  TestDownloaderFactory factory(helper.url_loader_factory());
  TestCrxInstallerFactory crx_installer_factory;
  ExtensionUpdater updater(profile());
  updater.InitAndEnable(extension_prefs(), pref_service(), kUpdateFrequency,
                        nullptr, factory.GetDownloaderFactory());
  updater.set_crx_installer_factory_for_test(&crx_installer_factory);
  MockExtensionDownloaderDelegate downloader_delegate;
  factory.OverrideDownloaderDelegate(&downloader_delegate);

  // Non-internal non-external extensions should be rejected.
  ExtensionList extensions;
  CreateTestExtensions(1, 1, &extensions, nullptr,
                       ManifestLocation::kInvalidLocation);
  ASSERT_EQ(1u, extensions.size());
  // The test will fail with unexpected calls if the delegate's methods are
  // invoked for the extension.
  SetExtensions(extensions, ExtensionList());
  updater.Start();
  updater.CheckNow(ExtensionUpdater::CheckParams());
}

TEST_F(ExtensionUpdaterTest, TestUpdatingDisabledExtensions) {
  ExtensionDownloaderTestHelper helper;
  TestDownloaderFactory factory(helper.url_loader_factory());
  TestCrxInstallerFactory crx_installer_factory;
  ExtensionUpdater updater(profile());
  updater.InitAndEnable(extension_prefs(), pref_service(), kUpdateFrequency,
                        nullptr, factory.GetDownloaderFactory());
  updater.set_crx_installer_factory_for_test(&crx_installer_factory);
  NiceMock<MockUpdateService> update_service;
  OverrideUpdateService(&updater, &update_service);

  // Non-internal non-external extensions should be rejected.
  ExtensionList enabled_extensions;
  ExtensionList disabled_extensions;
  CreateTestExtensions(1, 1, &enabled_extensions, nullptr,
                       ManifestLocation::kInternal);
  CreateTestExtensions(2, 1, &disabled_extensions, nullptr,
                       ManifestLocation::kInternal);
  ASSERT_EQ(1u, enabled_extensions.size());
  ASSERT_EQ(1u, disabled_extensions.size());

  // We expect that both enabled and disabled extensions are auto-updated.
  EXPECT_CALL(update_service,
              StartUpdateCheck(
                  ::testing::Field(&ExtensionUpdateCheckParams::update_info,
                                   ::testing::SizeIs(2)),
                  _, _));

  SetExtensions(enabled_extensions, disabled_extensions);
  updater.Start();
  updater.CheckNow(ExtensionUpdater::CheckParams());
}

// crbug.com/40137079: Tests that removely disabled extensions that are part of
// the blocklisted extensions are still receive updates.
TEST_F(ExtensionUpdaterTest, TestUpdatingRemotelyDisabledExtensions) {
  ExtensionDownloaderTestHelper helper;
  TestDownloaderFactory factory(helper.url_loader_factory());
  TestCrxInstallerFactory crx_installer_factory;
  ExtensionUpdater updater(profile());
  updater.InitAndEnable(extension_prefs(), pref_service(), kUpdateFrequency,
                        nullptr, factory.GetDownloaderFactory());
  updater.set_crx_installer_factory_for_test(&crx_installer_factory);
  NiceMock<MockUpdateService> update_service;
  OverrideUpdateService(&updater, &update_service);

  ExtensionList enabled_extensions;
  ExtensionList blocklisted_extensions;
  CreateTestExtensions(1, 1, &enabled_extensions, nullptr,
                       ManifestLocation::kInternal);
  CreateTestExtensions(2, 1, &blocklisted_extensions, nullptr,
                       ManifestLocation::kInternal);
  CreateTestExtensions(3, 1, &blocklisted_extensions, nullptr,
                       ManifestLocation::kInternal);
  ASSERT_EQ(1u, enabled_extensions.size());
  ASSERT_EQ(2u, blocklisted_extensions.size());
  const std::string& remotely_blocklisted_id = blocklisted_extensions[0]->id();
  blocklist_prefs::SetSafeBrowsingExtensionBlocklistState(
      remotely_blocklisted_id, BitMapBlocklistState::BLOCKLISTED_MALWARE,
      extension_prefs());
  blocklist_prefs::AddOmahaBlocklistState(
      remotely_blocklisted_id, BitMapBlocklistState::BLOCKLISTED_MALWARE,
      extension_prefs());

  // We expect that both enabled and remotely blocklisted extensions are
  // auto-updated.
  EXPECT_CALL(update_service,
              StartUpdateCheck(
                  ::testing::Field(&ExtensionUpdateCheckParams::update_info,
                                   ::testing::SizeIs(3)),
                  _, _));

  SetExtensions(enabled_extensions, ExtensionList(), blocklisted_extensions);
  updater.Start();
  updater.CheckNow(ExtensionUpdater::CheckParams());
}

TEST_F(ExtensionUpdaterTest, TestPendingInstall) {
  // Add an extension as a pending update with a higher version number.
  base::DictValue manifest;
  manifest.Set(manifest_keys::kKey, kExtensionManifestKey);
  manifest.Set(manifest_keys::kName, "Fake extension");
  manifest.Set(manifest_keys::kVersion, "1.0.0.1");
  manifest.Set(manifest_keys::kManifestVersion, 2);
  scoped_refptr<Extension> pending_update =
      prefs_->AddExtensionWithManifest(manifest, ManifestLocation::kInternal);
  DelayedInstallManager::Get(profile())->Insert(pending_update);

  ExtensionDownloaderTestHelper helper;
  TestDownloaderFactory factory(helper.url_loader_factory());
  TestCrxInstallerFactory crx_installer_factory;
  ExtensionUpdater updater(profile());
  updater.InitAndEnable(extension_prefs(), pref_service(), kUpdateFrequency,
                        nullptr, factory.GetDownloaderFactory());
  updater.set_crx_installer_factory_for_test(&crx_installer_factory);
  NiceMock<MockUpdateService> update_service;
  OverrideUpdateService(&updater, &update_service);

  ExtensionList enabled_extensions;
  std::string extension_key(kExtensionManifestKey);
  CreateTestExtensions(1, 1, &enabled_extensions, nullptr,
                       ManifestLocation::kInternal, extension_key);
  ASSERT_EQ(1u, enabled_extensions.size());
  ASSERT_EQ(enabled_extensions[0]->VersionString(), "1.0.0.0");

  // When StartUpdateCheck is called, we expect the pending version is used.
  EXPECT_CALL(update_service,
              StartUpdateCheck(
                  ::testing::Field(
                      &ExtensionUpdateCheckParams::update_info,
                      ::testing::ElementsAre(::testing::Pair(
                          enabled_extensions[0]->id(),
                          ::testing::FieldsAre(
                              "", false,
                              ::testing::Optional(std::string("1.0.0.1")))))),
                  _, _));

  SetExtensions(enabled_extensions, ExtensionList());
  updater.Start();
  updater.CheckNow(ExtensionUpdater::CheckParams());
}

TEST_F(ExtensionUpdaterTest, TestManifestFetchesBuilderAddExtension) {
  auto helper = std::make_unique<ExtensionDownloaderTestHelper>();
  EXPECT_EQ(0u, ManifestFetchersCount(&helper->downloader()));

  // First, verify that adding valid extensions does invoke the callbacks on
  // the delegate.
  std::string id = crx_file::id_util::GenerateId("foo");
  EXPECT_CALL(helper->delegate(), GetPingDataForExtension(id, _))
      .WillOnce(Return(false));
  EXPECT_TRUE(helper->downloader().AddPendingExtension(
      CreateDownloaderTask(id, GURL("http://example.com/update"))));
  helper->downloader().StartAllPending(nullptr);
  Mock::VerifyAndClearExpectations(&helper->delegate());
  EXPECT_EQ(1u, ManifestFetchersCount(&helper->downloader()));

  // Extensions with invalid update URLs should be rejected.
  id = crx_file::id_util::GenerateId("foo2");
  EXPECT_FALSE(helper->downloader().AddPendingExtension(
      CreateDownloaderTask(id, GURL("http:google.com:foo"))));
  helper->downloader().StartAllPending(nullptr);
  EXPECT_EQ(1u, ManifestFetchersCount(&helper->downloader()));

  // Extensions with empty IDs should be rejected.
  EXPECT_FALSE(helper->downloader().AddPendingExtension(
      CreateDownloaderTask(std::string(), GURL())));
  helper->downloader().StartAllPending(nullptr);
  EXPECT_EQ(1u, ManifestFetchersCount(&helper->downloader()));

  // TODO(akalin): Test that extensions with empty update URLs
  // converted from user scripts are rejected.

  // Reset the ExtensionDownloader so that it drops the current fetcher.
  helper = std::make_unique<ExtensionDownloaderTestHelper>();
  EXPECT_EQ(0u, ManifestFetchersCount(&helper->downloader()));

  // Extensions with empty update URLs should have a default one
  // filled in.
  id = crx_file::id_util::GenerateId("foo3");
  EXPECT_CALL(helper->delegate(), GetPingDataForExtension(id, _))
      .WillOnce(Return(false));
  EXPECT_TRUE(helper->downloader().AddPendingExtension(
      CreateDownloaderTask(id, GURL())));
  helper->downloader().StartAllPending(nullptr);
  EXPECT_EQ(1u, ManifestFetchersCount(&helper->downloader()));

  RunUntilIdle();
  auto* request = &(*helper->test_url_loader_factory().pending_requests())[0];
  ASSERT_TRUE(request);
  EXPECT_FALSE(request->request.url.is_empty());
}

TEST_F(ExtensionUpdaterTest, TestAddPendingExtensionWithVersion) {
  constexpr char kVersion[] = "1.2.3.4";
  auto helper = std::make_unique<ExtensionDownloaderTestHelper>();
  EXPECT_EQ(0u, ManifestFetchersCount(&helper->downloader()));

  {
    // First, verify that adding valid extensions does invoke the callbacks on
    // the delegate.
    ExtensionId id = crx_file::id_util::GenerateId("foo");
    EXPECT_CALL(helper->delegate(), GetPingDataForExtension(id, _))
        .WillOnce(Return(false));
    ExtensionDownloaderTask task =
        CreateDownloaderTask(id, GURL("http://example.com/update"));
    task.version = base::Version(kVersion);
    EXPECT_TRUE(helper->downloader().AddPendingExtension(std::move(task)));
    helper->downloader().StartAllPending(nullptr);
    Mock::VerifyAndClearExpectations(&helper->delegate());
    EXPECT_EQ(1u, ManifestFetchersCount(&helper->downloader()));
  }

  {
    // Extensions with invalid update URLs should be rejected.
    ExtensionId id = crx_file::id_util::GenerateId("foo2");
    ExtensionDownloaderTask task =
        CreateDownloaderTask(id, GURL("http:google.com:foo"));
    task.version = base::Version(kVersion);
    EXPECT_FALSE(helper->downloader().AddPendingExtension(std::move(task)));
    helper->downloader().StartAllPending(nullptr);
    EXPECT_EQ(1u, ManifestFetchersCount(&helper->downloader()));
  }

  {
    // Extensions with empty IDs should be rejected.
    ExtensionDownloaderTask task = CreateDownloaderTask(std::string(), GURL());
    task.version = base::Version(kVersion);
    EXPECT_FALSE(helper->downloader().AddPendingExtension(std::move(task)));
    helper->downloader().StartAllPending(nullptr);
    EXPECT_EQ(1u, ManifestFetchersCount(&helper->downloader()));
  }

  // Reset the ExtensionDownloader so that it drops the current fetcher.
  helper = std::make_unique<ExtensionDownloaderTestHelper>();
  EXPECT_EQ(0u, ManifestFetchersCount(&helper->downloader()));

  {
    // Extensions with empty update URLs should have a default one
    // filled in.
    ExtensionId id = crx_file::id_util::GenerateId("foo3");
    EXPECT_CALL(helper->delegate(), GetPingDataForExtension(id, _))
        .WillOnce(Return(false));
    ExtensionDownloaderTask task = CreateDownloaderTask(id, GURL());
    task.version = base::Version(kVersion);
    EXPECT_TRUE(helper->downloader().AddPendingExtension(std::move(task)));
    helper->downloader().StartAllPending(nullptr);
    EXPECT_EQ(1u, ManifestFetchersCount(&helper->downloader()));
  }

  RunUntilIdle();
  auto* request = &(*helper->test_url_loader_factory().pending_requests())[0];
  ASSERT_TRUE(request);
  EXPECT_FALSE(request->request.url.is_empty());
  EXPECT_THAT(request->request.url.possibly_invalid_spec(),
              testing::HasSubstr(kVersion));
}

TEST_F(ExtensionUpdaterTest, TestStartUpdateCheckMemory) {
  ExtensionDownloaderTestHelper helper;

  StartUpdateCheck(&helper.downloader(),
                   CreateManifestFetchData(GURL("http://localhost/foo")));
  // This should delete the newly-created ManifestFetchData.
  StartUpdateCheck(&helper.downloader(),
                   CreateManifestFetchData(GURL("http://localhost/foo")));
  // This should add into |manifests_pending_|.
  StartUpdateCheck(&helper.downloader(),
                   CreateManifestFetchData(GURL("http://www.google.com")));
  // The dtor of |downloader| should delete the pending fetchers.
}

TEST_F(ExtensionUpdaterTest, TestCheckSoon) {
  ExtensionDownloaderTestHelper helper;
  TestDownloaderFactory factory(helper.url_loader_factory());
  TestCrxInstallerFactory crx_installer_factory;
  ExtensionUpdater updater(profile());
  updater.InitAndEnable(extension_prefs(), pref_service(), kUpdateFrequency,
                        nullptr, factory.GetDownloaderFactory());
  updater.set_crx_installer_factory_for_test(&crx_installer_factory);
  EXPECT_FALSE(updater.WillCheckSoon());
  updater.Start();
  EXPECT_TRUE(updater.WillCheckSoon());
  RunUntilIdle();
  EXPECT_FALSE(updater.WillCheckSoon());
  updater.CheckSoon();
  EXPECT_TRUE(updater.WillCheckSoon());
  updater.CheckSoon();
  EXPECT_TRUE(updater.WillCheckSoon());
  RunUntilIdle();
  EXPECT_FALSE(updater.WillCheckSoon());
  updater.CheckSoon();
  EXPECT_TRUE(updater.WillCheckSoon());
  updater.Stop();
  EXPECT_FALSE(updater.WillCheckSoon());
}

// Verifies that HasFullCheckInProgress() distinguishes full update checks from
// targeted single-extension update checks, remaining false during targeted
// checks and transitioning to true during full checks.
TEST_F(ExtensionUpdaterTest, TestHasFullCheckInProgress) {
  ExtensionDownloaderTestHelper helper;
  TestDownloaderFactory factory(helper.url_loader_factory());
  TestCrxInstallerFactory crx_installer_factory;
  ExtensionList tmp;
  CreateTestExtensions(2, 2, &tmp, nullptr, ManifestLocation::kInternal);
  SetExtensions(tmp, ExtensionList());

  ASSERT_EQ(2u, tmp.size());
  ExtensionId id1 = tmp[0]->id();
  ExtensionId id2 = tmp[1]->id();
  ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
  ASSERT_TRUE(registry->enabled_extensions().GetByID(id1));
  ASSERT_TRUE(registry->enabled_extensions().GetByID(id2));

  ExtensionUpdater updater(profile());
  updater.InitAndEnable(extension_prefs(), pref_service(), kUpdateFrequency,
                        nullptr, factory.GetDownloaderFactory());
  updater.set_crx_installer_factory_for_test(&crx_installer_factory);

  updater.Start();
  EXPECT_FALSE(updater.HasFullCheckInProgress());

  // A targeted check must not report as a full check.
  ExtensionUpdater::CheckParams params;
  params.ids = {id1};
  updater.CheckNow(std::move(params));
  EXPECT_FALSE(updater.HasFullCheckInProgress());

  // A parameterless check covers all extensions and must report as a full
  // check.
  updater.CheckNow(ExtensionUpdater::CheckParams());
  EXPECT_TRUE(updater.HasFullCheckInProgress());

  updater.Stop();
  EXPECT_FALSE(updater.HasFullCheckInProgress());
}

TEST_F(ExtensionUpdaterTest, TestUninstallWhileUpdateCheck) {
  ExtensionDownloaderTestHelper helper;
  TestDownloaderFactory factory(helper.url_loader_factory());
  TestCrxInstallerFactory crx_installer_factory;
  ExtensionList tmp;
  CreateTestExtensions(1, 1, &tmp, nullptr, ManifestLocation::kInternal);
  SetExtensions(tmp, ExtensionList());

  ASSERT_EQ(1u, tmp.size());
  ExtensionId id = tmp.front()->id();
  ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
  ASSERT_TRUE(registry->enabled_extensions().GetByID(id));

  ExtensionUpdater updater(profile());
  updater.InitAndEnable(extension_prefs(), pref_service(), kUpdateFrequency,
                        nullptr, factory.GetDownloaderFactory());
  updater.set_crx_installer_factory_for_test(&crx_installer_factory);
  ExtensionUpdater::CheckParams params;
  params.ids = {id};
  updater.Start();
  updater.CheckNow(std::move(params));

  SetExtensions(ExtensionList(), ExtensionList());
  ASSERT_FALSE(registry->enabled_extensions().GetByID(id));

  // RunUntilIdle is needed to make sure that the UpdateService instance that
  // runs the extension update process has a chance to exit gracefully; without
  // it, the test would crash.
  RunUntilIdle();
}

TEST_F(ExtensionUpdaterTest, TestManifestFetchDataAddExtension) {
  TestManifestAddExtension(DownloadFetchPriority::kBackground,
                           DownloadFetchPriority::kBackground,
                           DownloadFetchPriority::kBackground);

  TestManifestAddExtension(DownloadFetchPriority::kBackground,
                           DownloadFetchPriority::kForeground,
                           DownloadFetchPriority::kForeground);

  TestManifestAddExtension(DownloadFetchPriority::kForeground,
                           DownloadFetchPriority::kBackground,
                           DownloadFetchPriority::kForeground);

  TestManifestAddExtension(DownloadFetchPriority::kForeground,
                           DownloadFetchPriority::kForeground,
                           DownloadFetchPriority::kForeground);
}

TEST_F(ExtensionUpdaterTest, TestManifestFetchDataMerge) {
  TestManifestMerge(DownloadFetchPriority::kBackground,
                    DownloadFetchPriority::kBackground,
                    DownloadFetchPriority::kBackground);

  TestManifestMerge(DownloadFetchPriority::kBackground,
                    DownloadFetchPriority::kForeground,
                    DownloadFetchPriority::kForeground);

  TestManifestMerge(DownloadFetchPriority::kForeground,
                    DownloadFetchPriority::kBackground,
                    DownloadFetchPriority::kForeground);

  TestManifestMerge(DownloadFetchPriority::kForeground,
                    DownloadFetchPriority::kForeground,
                    DownloadFetchPriority::kForeground);
}

TEST_F(ExtensionUpdaterTest, TestManifestFetchCredentials) {
  TestManifestCredentialsWebstore();
  TestManifestCredentialsNonWebstore();
}

TEST_F(ExtensionUpdaterTest, TestManifestFetchPriority) {
  TestManifestFetchPriority(DownloadFetchPriority::kBackground);
  TestManifestFetchPriority(DownloadFetchPriority::kForeground);
}

TEST_F(ExtensionUpdaterTest, TestExtensionPriority) {
  TestSingleExtensionDownloadingPriority(DownloadFetchPriority::kBackground);
  TestSingleExtensionDownloadingPriority(DownloadFetchPriority::kForeground);
}

#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
TEST_F(ExtensionUpdaterTest, TestProfileDestruction) {
  ExtensionUpdater updater(profile());
  // Create an active ProfileManager, and do NOT make it the owner of profile().
  // This causes ScopedProfileKeepAlive::TryAcquire() to fail.
  auto testing_profile_manager = std::make_unique<TestingProfileManager>(
      TestingBrowserProcess::GetGlobal());
  ASSERT_TRUE(testing_profile_manager->SetUp());

  ExtensionDownloaderTestHelper helper;
  TestDownloaderFactory factory(helper.url_loader_factory());
  // Verify that CheckNow() doesn't do anything in this state.
  updater.SetUpdatingStartedCallbackForTesting(base::BindLambdaForTesting(
      [&]() { ADD_FAILURE() << "Updating should not have started."; }));
  updater.InitAndEnable(extension_prefs(), pref_service(), kUpdateFrequency,
                        nullptr, factory.GetDownloaderFactory());
  updater.Start();
  updater.CheckNow(ExtensionUpdater::CheckParams());
  base::RunLoop().RunUntilIdle();
}
#endif  // !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)

class CanUseUpdateServiceTest : public ExtensionUpdaterTest {
 public:
  CanUseUpdateServiceTest() = default;
  ~CanUseUpdateServiceTest() override = default;

  void SetUp() override {
    ExtensionUpdaterTest::SetUp();

    factory_ = std::make_unique<TestDownloaderFactory>(
        downloader_test_helper_.url_loader_factory());
    crx_installer_factory_ = std::make_unique<TestCrxInstallerFactory>();
    updater_ = std::make_unique<ExtensionUpdater>(profile());
    updater_->InitAndEnable(extension_prefs(), pref_service(), kUpdateFrequency,
                            nullptr, factory_->GetDownloaderFactory());
    updater_->set_crx_installer_factory_for_test(crx_installer_factory_.get());

    store_extension_ =
        ExtensionBuilder("store_extension")
            .SetManifestKey(
                "update_url",
                extension_urls::GetDefaultWebstoreUpdateUrl().spec())
            .Build();
    offstore_extension_ =
        ExtensionBuilder("offstore_extension")
            .SetManifestKey("update_url", "http://localhost/test/updates.xml")
            .Build();
    emptyurl_extension_ = ExtensionBuilder("emptyurl_extension").Build();
    userscript_extension_ =
        ExtensionBuilder("userscript_extension")
            .SetManifestKey("converted_from_user_script", true)
            .Build();

    ASSERT_TRUE(store_extension_.get());
    auto* registry = ExtensionRegistry::Get(profile());
    ASSERT_TRUE(registry->AddEnabled(store_extension_));
    ASSERT_TRUE(offstore_extension_.get());
    ASSERT_TRUE(registry->AddEnabled(offstore_extension_));
    ASSERT_TRUE(emptyurl_extension_.get());
    ASSERT_TRUE(registry->AddEnabled(emptyurl_extension_));
    ASSERT_TRUE(userscript_extension_.get());
    ASSERT_TRUE(registry->AddEnabled(userscript_extension_));
  }

  void TearDown() override {
    // Avoid dangling pointers.
    updater_->set_crx_installer_factory_for_test(nullptr);
    updater_.reset();
    crx_installer_factory_.reset();
    factory_.reset();
    ExtensionUpdaterTest::TearDown();
  }

 protected:
  ExtensionUpdater* updater() { return updater_.get(); }

  ExtensionUpdater::ScopedSkipScheduledCheckForTest skip_scheduled_checks_;
  ExtensionDownloaderTestHelper downloader_test_helper_;
  std::unique_ptr<TestDownloaderFactory> factory_;
  std::unique_ptr<TestCrxInstallerFactory> crx_installer_factory_;
  std::unique_ptr<ExtensionUpdater> updater_;

  scoped_refptr<const Extension> store_extension_;
  scoped_refptr<const Extension> offstore_extension_;
  scoped_refptr<const Extension> emptyurl_extension_;
  scoped_refptr<const Extension> userscript_extension_;
};

class UpdateServiceCanUpdateFeatureEnabledNonDefaultUpdateUrl
    : public CanUseUpdateServiceTest {
 public:
  void SetUp() override {
    CanUseUpdateServiceTest::SetUp();

    // Change the webstore update url.
    auto* command_line = base::CommandLine::ForCurrentProcess();
    // Note: |offstore_extension_|'s update url is the same.
    command_line->AppendSwitchASCII("apps-gallery-update-url",
                                    "http://localhost/test2/updates.xml");
    ExtensionsClient::Get()->InitializeWebStoreUrls(
        base::CommandLine::ForCurrentProcess());
  }
};

TEST_F(CanUseUpdateServiceTest, TestDefaults) {
  // Update service can only update webstore extensions when enabled.
  EXPECT_TRUE(CanUseUpdateService(updater(), store_extension_.get()));
  // ... and extensions with empty update URL.
  EXPECT_TRUE(CanUseUpdateService(updater(), emptyurl_extension_.get()));
  // It can't update off-store extensions.
  EXPECT_FALSE(CanUseUpdateService(updater(), offstore_extension_.get()));
  // ... or extensions that don't exist.
  EXPECT_FALSE(CanUseUpdateService(updater(), nullptr));
}

TEST_F(UpdateServiceCanUpdateFeatureEnabledNonDefaultUpdateUrl,
       CanUseUpdateServiceFeatureEnabledNonDefaultUpdateUrl) {
  // Update service can update extensions when the default webstore update url
  // is changed.
  EXPECT_FALSE(CanUseUpdateService(updater(), store_extension_.get()));
  EXPECT_TRUE(CanUseUpdateService(updater(), emptyurl_extension_.get()));
  EXPECT_FALSE(CanUseUpdateService(updater(), offstore_extension_.get()));
  EXPECT_FALSE(CanUseUpdateService(updater(), nullptr));
}

// TODO(asargent) - (http://crbug.com/40809955) add tests for:
// -prodversionmin (shouldn't update if browser version too old)
// -manifests & updates arriving out of order / interleaved
// -malformed update url (empty, file://, has query, has a # fragment, etc.)
// -An extension gets uninstalled while updates are in progress (so it doesn't
//  "come back from the dead")
// -An extension gets manually updated to v3 while we're downloading v2 (ie
//  you don't get downgraded accidentally)
// -An update manifest mentions multiple updates

}  // namespace extensions
