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

#include "extensions/browser/unpacked_installer.h"

#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "build/branding_buildflags.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_service_test_base.h"
#include "chrome/browser/extensions/extension_service_test_with_install.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/pref_names.h"
#include "components/crx_file/id_util.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/load_error_reporter.h"
#include "extensions/browser/permissions_manager.h"
#include "extensions/browser/test_extension_registry_observer.h"
#include "extensions/browser/test_management_policy.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/extension_features.h"
#include "extensions/common/mojom/manifest.mojom-shared.h"
#include "extensions/common/switches.h"

static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));

namespace extensions {

class UnpackedInstallerUnitTest : public ExtensionServiceTestWithInstall,
                                  public testing::WithParamInterface<bool> {
 public:
  UnpackedInstallerUnitTest() {
    if (GetParam()) {
      feature_list_.InitAndEnableFeature(
          extensions_features::kAllowWithholdingExtensionPermissionsOnInstall);
    } else {
      feature_list_.InitAndDisableFeature(
          extensions_features::kAllowWithholdingExtensionPermissionsOnInstall);
    }
  }
  ~UnpackedInstallerUnitTest() override = default;

  ManagementPolicy* GetManagementPolicy() {
    return ExtensionSystem::Get(browser_context())->management_policy();
  }

 private:
  base::test::ScopedFeatureList feature_list_;
};

// Tests host permissions are withheld by default at installation when flag is
// enabled.
TEST_P(UnpackedInstallerUnitTest, WithheldHostPermissionsWithFlag) {
  InitializeEmptyExtensionService();

  // Load extension.
  TestExtensionRegistryObserver observer(registry());
  base::FilePath extension_path =
      data_dir().AppendASCII("api_test/simple_all_urls");
  extensions::UnpackedInstaller::Create(profile())->Load(extension_path);
  scoped_refptr<const Extension> loaded_extension =
      observer.WaitForExtensionLoaded();

  // Verify extension was installed.
  EXPECT_EQ(loaded_extension->name(), "All Urls Extension");

  // Host permissions should be withheld at installation only when flag is
  // enabled.
  bool flag_enabled = GetParam();
  PermissionsManager* permissions_manager =
      PermissionsManager::Get(browser_context());
  EXPECT_EQ(permissions_manager->HasWithheldHostPermissions(*loaded_extension),
            flag_enabled);
}


class MockLoadErrorReporterObserver : public LoadErrorReporter::Observer {
 public:
  explicit MockLoadErrorReporterObserver(base::OnceClosure quit_closure)
      : quit_closure_(std::move(quit_closure)) {}

  void OnLoadFailure(content::BrowserContext* browser_context,
                     const base::FilePath& file_path,
                     const std::u16string& error) override {
    last_file_path_ = file_path;
    last_error_ = error;
    if (quit_closure_) {
      std::move(quit_closure_).Run();
    }
  }

  base::FilePath last_file_path_;
  std::u16string last_error_;
  base::OnceClosure quit_closure_;
};

TEST_P(UnpackedInstallerUnitTest, LoadNonExistentPathPreservesPath) {
  InitializeEmptyExtensionService();

  // Setup error LoadErrorReporter observer.
  base::RunLoop run_loop;
  MockLoadErrorReporterObserver observer(run_loop.QuitClosure());
  LoadErrorReporter::GetInstance()->AddObserver(&observer);

  base::FilePath non_existent_path = data_dir().AppendASCII("not_exist");
  extensions::UnpackedInstaller::Create(profile())->Load(non_existent_path);

  // Wait for the error to be reported.
  run_loop.Run();

  EXPECT_EQ(observer.last_file_path_, non_existent_path);
  EXPECT_EQ(observer.last_error_, u"File path cannot be resolved.");

  LoadErrorReporter::GetInstance()->RemoveObserver(&observer);
}

INSTANTIATE_TEST_SUITE_P(All, UnpackedInstallerUnitTest, testing::Bool());

}  // namespace extensions
