// Copyright 2017 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/api/file_system/chrome_file_system_delegate.h"

#include <string>
#include <utility>

#include "apps/saved_files_service.h"
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/notreached.h"
#include "base/path_service.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_core_service.h"
#include "chrome/browser/download/download_core_service_factory.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/extensions/api/file_system/file_entry_picker.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/apps/directory_access_confirmation_dialog.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/api/file_system/saved_files_service_interface.h"
#include "extensions/browser/app_window/app_window.h"
#include "extensions/browser/app_window/app_window_registry.h"
#include "extensions/common/extension.h"
#include "storage/common/file_system/file_system_util.h"

#if BUILDFLAG(IS_MAC)
#include <CoreFoundation/CoreFoundation.h>
#include "base/apple/foundation_util.h"
#endif

#if BUILDFLAG(IS_CHROMEOS)
#include "extensions/browser/event_router.h"
#endif  // BUILDFLAG(IS_CHROMEOS)

namespace extensions {

namespace file_system = api::file_system;

#if BUILDFLAG(IS_CHROMEOS)

namespace file_system_api {

const char kConsentImpossible[] =
    "Impossible to ask for user consent as there is no app window visible.";
const char kNotSupportedOnNonKioskSessionError[] =
    "Operation only supported for kiosk apps running in a kiosk session.";
const char kRequiresFileSystemWriteError[] =
    "Operation requires fileSystem.write permission";
const char kSecurityError[] = "Security error.";
const char kVolumeNotFoundError[] = "Volume not found.";

// Returns error message, or null if none.
const char* ConsentResultToError(ConsentProvider::Consent result) {
  switch (result) {
    case ConsentProvider::CONSENT_REJECTED:
      return kSecurityError;
    case ConsentProvider::CONSENT_IMPOSSIBLE:
      return kConsentImpossible;
    case ConsentProvider::CONSENT_GRANTED:
      return nullptr;
  }
  NOTREACHED();
}

}  // namespace file_system_api
#endif  // BUILDFLAG(IS_CHROMEOS)

/******** ChromeFileSystemDelegate ********/

ChromeFileSystemDelegate::ChromeFileSystemDelegate() = default;

ChromeFileSystemDelegate::~ChromeFileSystemDelegate() = default;

base::FilePath ChromeFileSystemDelegate::GetDefaultDirectory() {
  base::FilePath documents_dir;
  base::PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir);
  return documents_dir;
}

base::FilePath ChromeFileSystemDelegate::GetManagedSaveAsDirectory(
    content::BrowserContext* browser_context,
    const Extension& extension) {
  if (extension.id() != extension_misc::kPdfExtensionId) {
    return base::FilePath();
  }

  ChromeDownloadManagerDelegate* download_manager =
      DownloadCoreServiceFactory::GetForBrowserContext(browser_context)
          ->GetDownloadManagerDelegate();
  DownloadPrefs* download_prefs = download_manager->download_prefs();
  if (!download_prefs->IsDownloadPathManaged()) {
    return base::FilePath();
  }
  return download_prefs->DownloadPath();
}

bool ChromeFileSystemDelegate::ShowSelectFileDialog(
    scoped_refptr<ExtensionFunction> extension_function,
    ui::SelectFileDialog::Type type,
    const base::FilePath& default_path,
    const ui::SelectFileDialog::FileTypeInfo* file_types,
    FileSystemDelegate::FilesSelectedCallback files_selected_callback,
    base::OnceClosure file_selection_canceled_callback) {
  const Extension* extension = extension_function->extension();
  content::WebContents* web_contents =
      extension_function->GetSenderWebContents();

  if (!web_contents) {
    return false;
  }

  // TODO(asargent/benwells) - As a short term remediation for
  // crbug.com/40964929 we're adding the ability for a allowlisted extension to
  // use this API since chrome.fileBrowserHandler.selectFile is ChromeOS-only.
  // Eventually we'd like a better solution and likely this code will go back
  // to being platform-app only.

  // Make sure there is an app window associated with the web contents, so that
  // platform apps cannot open the file picker from a background page.
  // TODO(michaelpg): As a workaround for https://crbug.com/40527613, allow this
  // to work from a background page for non-platform apps (which, in practice,
  // is restricted to allowlisted extensions).
  if (extension->is_platform_app() &&
      !AppWindowRegistry::Get(extension_function->browser_context())
           ->GetAppWindowForWebContents(web_contents)) {
    return false;
  }

  // The file picker will hold a reference to the ExtensionFunction
  // instance, preventing its destruction (and subsequent sending of the
  // function response) until the user has selected a file or cancelled the
  // picker. At that point, the picker will delete itself, which will also free
  // the function instance.
  new FileEntryPicker(web_contents, default_path, *file_types, type,
                      std::move(files_selected_callback),
                      std::move(file_selection_canceled_callback));
  return true;
}

void ChromeFileSystemDelegate::ConfirmSensitiveDirectoryAccess(
    bool has_write_permission,
    const std::u16string& app_name,
    content::WebContents* web_contents,
    base::OnceClosure on_accept,
    base::OnceClosure on_cancel) {
  CreateDirectoryAccessConfirmationDialog(has_write_permission, app_name,
                                          web_contents, std::move(on_accept),
                                          std::move(on_cancel));
}

int ChromeFileSystemDelegate::GetDescriptionIdForAcceptType(
    const std::string& accept_type) {
  if (accept_type == "image/*") {
    return IDS_IMAGE_FILES;
  }
  if (accept_type == "audio/*") {
    return IDS_AUDIO_FILES;
  }
  if (accept_type == "video/*") {
    return IDS_VIDEO_FILES;
  }
  return 0;
}

#if BUILDFLAG(IS_CHROMEOS)
void ChromeFileSystemDelegate::RequestFileSystem(
    content::BrowserContext* browser_context,
    scoped_refptr<ExtensionFunction> requester,
    ConsentProvider* consent_provider,
    const Extension& extension,
    std::string volume_id,
    bool writable,
    FileSystemCallback success_callback,
    ErrorCallback error_callback) {}

void ChromeFileSystemDelegate::GetVolumeList(
    content::BrowserContext* browser_context,
    VolumeListCallback success_callback,
    ErrorCallback error_callback) {}
#endif  // BUILDFLAG(IS_CHROMEOS)

SavedFilesServiceInterface* ChromeFileSystemDelegate::GetSavedFilesService(
    content::BrowserContext* browser_context) {
  return apps::SavedFilesService::Get(browser_context);
}

}  // namespace extensions
