// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "storage/browser/fileapi/file_system_quota_client.h"

#include <algorithm>
#include <memory>

#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "base/task_runner_util.h"
#include "storage/browser/fileapi/file_system_context.h"
#include "storage/browser/fileapi/file_system_usage_cache.h"
#include "storage/browser/fileapi/sandbox_file_system_backend.h"
#include "storage/common/fileapi/file_system_util.h"
#include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
#include "url/gurl.h"

using blink::mojom::StorageType;

namespace storage {

namespace {

void GetOriginsForTypeOnFileTaskRunner(FileSystemContext* context,
                                       StorageType storage_type,
                                       std::set<url::Origin>* origins_ptr) {
  FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type);
  DCHECK(type != kFileSystemTypeUnknown);

  FileSystemQuotaUtil* quota_util = context->GetQuotaUtil(type);
  if (!quota_util)
    return;
  std::set<GURL> origins;
  quota_util->GetOriginsForTypeOnFileTaskRunner(type, &origins);
  for (auto origin : origins)
    origins_ptr->insert(url::Origin::Create(origin));
}

void GetOriginsForHostOnFileTaskRunner(FileSystemContext* context,
                                       StorageType storage_type,
                                       const std::string& host,
                                       std::set<url::Origin>* origins_ptr) {
  FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type);
  DCHECK(type != kFileSystemTypeUnknown);

  FileSystemQuotaUtil* quota_util = context->GetQuotaUtil(type);
  if (!quota_util)
    return;
  std::set<GURL> origins;
  quota_util->GetOriginsForHostOnFileTaskRunner(type, host, &origins);
  for (auto origin : origins)
    origins_ptr->insert(url::Origin::Create(origin));
}

void DidGetOrigins(storage::QuotaClient::GetOriginsCallback callback,
                   std::set<url::Origin>* origins_ptr) {
  std::move(callback).Run(*origins_ptr);
}

blink::mojom::QuotaStatusCode DeleteOriginOnFileTaskRunner(
    FileSystemContext* context,
    const url::Origin& origin,
    FileSystemType type) {
  FileSystemBackend* provider = context->GetFileSystemBackend(type);
  if (!provider || !provider->GetQuotaUtil())
    return blink::mojom::QuotaStatusCode::kErrorNotSupported;
  base::File::Error result =
      provider->GetQuotaUtil()->DeleteOriginDataOnFileTaskRunner(
          context, context->quota_manager_proxy(), origin.GetURL(), type);
  if (result == base::File::FILE_OK)
    return blink::mojom::QuotaStatusCode::kOk;
  return blink::mojom::QuotaStatusCode::kErrorInvalidModification;
}

}  // namespace

FileSystemQuotaClient::FileSystemQuotaClient(
    FileSystemContext* file_system_context,
    bool is_incognito)
    : file_system_context_(file_system_context),
      is_incognito_(is_incognito) {
}

FileSystemQuotaClient::~FileSystemQuotaClient() = default;

storage::QuotaClient::ID FileSystemQuotaClient::id() const {
  return storage::QuotaClient::kFileSystem;
}

void FileSystemQuotaClient::OnQuotaManagerDestroyed() {
  delete this;
}

void FileSystemQuotaClient::GetOriginUsage(const url::Origin& origin,
                                           StorageType storage_type,
                                           GetUsageCallback callback) {
  DCHECK(!callback.is_null());

  if (is_incognito_) {
    // We don't support FileSystem in incognito mode yet.
    std::move(callback).Run(0);
    return;
  }

  FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type);
  DCHECK(type != kFileSystemTypeUnknown);

  FileSystemQuotaUtil* quota_util = file_system_context_->GetQuotaUtil(type);
  if (!quota_util) {
    std::move(callback).Run(0);
    return;
  }

  base::PostTaskAndReplyWithResult(
      file_task_runner(), FROM_HERE,
      // It is safe to pass Unretained(quota_util) since context owns it.
      base::BindOnce(&FileSystemQuotaUtil::GetOriginUsageOnFileTaskRunner,
                     base::Unretained(quota_util),
                     base::RetainedRef(file_system_context_), origin.GetURL(),
                     type),
      std::move(callback));
}

void FileSystemQuotaClient::GetOriginsForType(StorageType storage_type,
                                              GetOriginsCallback callback) {
  DCHECK(!callback.is_null());

  if (is_incognito_) {
    // We don't support FileSystem in incognito mode yet.
    std::set<url::Origin> origins;
    std::move(callback).Run(origins);
    return;
  }

  std::set<url::Origin>* origins_ptr = new std::set<url::Origin>();
  file_task_runner()->PostTaskAndReply(
      FROM_HERE,
      base::BindOnce(&GetOriginsForTypeOnFileTaskRunner,
                     base::RetainedRef(file_system_context_), storage_type,
                     base::Unretained(origins_ptr)),
      base::BindOnce(&DidGetOrigins, std::move(callback),
                     base::Owned(origins_ptr)));
}

void FileSystemQuotaClient::GetOriginsForHost(StorageType storage_type,
                                              const std::string& host,
                                              GetOriginsCallback callback) {
  DCHECK(!callback.is_null());

  if (is_incognito_) {
    // We don't support FileSystem in incognito mode yet.
    std::set<url::Origin> origins;
    std::move(callback).Run(origins);
    return;
  }

  std::set<url::Origin>* origins_ptr = new std::set<url::Origin>();
  file_task_runner()->PostTaskAndReply(
      FROM_HERE,
      base::BindOnce(&GetOriginsForHostOnFileTaskRunner,
                     base::RetainedRef(file_system_context_), storage_type,
                     host, base::Unretained(origins_ptr)),
      base::BindOnce(&DidGetOrigins, std::move(callback),
                     base::Owned(origins_ptr)));
}

void FileSystemQuotaClient::DeleteOriginData(const url::Origin& origin,
                                             StorageType type,
                                             DeletionCallback callback) {
  FileSystemType fs_type = QuotaStorageTypeToFileSystemType(type);
  DCHECK(fs_type != kFileSystemTypeUnknown);

  base::PostTaskAndReplyWithResult(
      file_task_runner(), FROM_HERE,
      base::BindOnce(&DeleteOriginOnFileTaskRunner,
                     base::RetainedRef(file_system_context_), origin, fs_type),
      std::move(callback));
}

bool FileSystemQuotaClient::DoesSupport(StorageType storage_type) const {
  FileSystemType type = QuotaStorageTypeToFileSystemType(storage_type);
  DCHECK(type != kFileSystemTypeUnknown);
  return file_system_context_->IsSandboxFileSystem(type);
}

base::SequencedTaskRunner* FileSystemQuotaClient::file_task_runner() const {
  return file_system_context_->default_file_task_runner();
}

}  // namespace storage
