// Copyright 2020 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/ash/extensions/file_manager/private_api_sharesheet.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "chrome/browser/ash/drive/file_system_util.h"
#include "chrome/browser/ash/extensions/file_manager/private_api_util.h"
#include "chrome/browser/ash/file_manager/fileapi_util.h"
#include "chrome/browser/ash/fileapi/file_system_backend.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sharesheet/sharesheet_service.h"
#include "chrome/browser/sharesheet/sharesheet_service_factory.h"
#include "chrome/common/extensions/api/file_manager_private.h"
#include "components/drive/drive_api_util.h"
#include "components/services/app_service/public/cpp/intent_util.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/api/file_handlers/directory_util.h"
#include "extensions/browser/api/file_handlers/mime_util.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_url.h"
using storage::FileSystemURL;
namespace {
using extensions::api::file_manager_private::SharesheetLaunchSource;
sharesheet::LaunchSource GetLaunchSource(SharesheetLaunchSource launch_source) {
switch (launch_source) {
case (SharesheetLaunchSource::kSharesheetButton):
return sharesheet::LaunchSource::kFilesAppShareButton;
case (SharesheetLaunchSource::kContextMenu):
return sharesheet::LaunchSource::kFilesAppContextMenu;
case (SharesheetLaunchSource::kUnknown):
case (SharesheetLaunchSource::kNone):
return sharesheet::LaunchSource::kUnknown;
}
}
} // namespace
namespace extensions {
FileManagerPrivateSharesheetHasTargetsFunction::
FileManagerPrivateSharesheetHasTargetsFunction() = default;
FileManagerPrivateSharesheetHasTargetsFunction::
~FileManagerPrivateSharesheetHasTargetsFunction() = default;
ExtensionFunction::ResponseAction
FileManagerPrivateSharesheetHasTargetsFunction::Run() {
using extensions::api::file_manager_private::SharesheetHasTargets::Params;
const std::optional<Params> params = Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
if (params->file_urls.empty()) {
return RespondNow(Error("No URLs provided"));
}
profile_ = Profile::FromBrowserContext(browser_context());
const scoped_refptr<storage::FileSystemContext> file_system_context =
file_manager::util::GetFileSystemContextForRenderFrameHost(
profile_, render_frame_host());
// Collect all the URLs, convert them to GURLs, and crack all the urls into
// file paths.
for (const auto& url_as_string : params->file_urls) {
const GURL url(url_as_string);
storage::FileSystemURL file_system_url(
file_system_context->CrackURLInFirstPartyContext(url));
if (!ash::FileSystemBackend::CanHandleURL(file_system_url)) {
continue;
}
urls_.push_back(url);
file_system_urls_.push_back(file_system_url);
}
mime_type_collector_ =
std::make_unique<app_file_handler_util::MimeTypeCollector>(profile_);
mime_type_collector_->CollectForURLs(
file_system_urls_,
base::BindOnce(
&FileManagerPrivateSharesheetHasTargetsFunction::OnMimeTypesCollected,
this));
return RespondLater();
}
void FileManagerPrivateSharesheetHasTargetsFunction::OnMimeTypesCollected(
std::unique_ptr<std::vector<std::string>> mime_types) {
sharesheet::SharesheetService* sharesheet_service =
sharesheet::SharesheetServiceFactory::GetForProfile(profile_);
bool result = false;
if (!sharesheet_service) {
LOG(ERROR) << "Couldn't get Sharesheet Service for profile";
Respond(ArgumentList(extensions::api::file_manager_private::
SharesheetHasTargets::Results::Create(result)));
return;
}
if (file_system_urls_.size() == 1 &&
file_system_urls_[0].type() == storage::kFileSystemTypeDriveFs) {
using drive::util::ConnectionStatus;
const ConnectionStatus status = drive::util::GetDriveConnectionStatus(
Profile::FromBrowserContext(browser_context()));
using enum ConnectionStatus;
if (status == kMetered || status == kConnected) {
file_manager::util::SingleEntryPropertiesGetterForDriveFs::Start(
file_system_urls_[0], profile_,
base::BindOnce(&FileManagerPrivateSharesheetHasTargetsFunction::
OnDrivePropertyCollected,
this, std::move(mime_types)));
return;
}
}
result = sharesheet_service->HasShareTargets(
apps_util::MakeShareIntent(urls_, *mime_types));
Respond(ArgumentList(extensions::api::file_manager_private::
SharesheetHasTargets::Results::Create(result)));
}
void FileManagerPrivateSharesheetHasTargetsFunction::OnDrivePropertyCollected(
std::unique_ptr<std::vector<std::string>> mime_types,
std::unique_ptr<api::file_manager_private::EntryProperties> properties,
base::File::Error error) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (error != base::File::FILE_OK) {
LOG(ERROR) << "Error reading file properties in Drive: " << error;
Respond(ArgumentList(extensions::api::file_manager_private::
SharesheetHasTargets::Results::Create(false)));
return;
}
is_directory_collector_ =
std::make_unique<app_file_handler_util::IsDirectoryCollector>(profile_);
is_directory_collector_->CollectForEntriesPaths(
std::vector<base::FilePath>{file_system_urls_[0].path()},
base::BindOnce(&FileManagerPrivateSharesheetHasTargetsFunction::
OnIsDirectoryCollected,
this, std::move(mime_types), std::move(properties)));
}
void FileManagerPrivateSharesheetHasTargetsFunction::OnIsDirectoryCollected(
std::unique_ptr<std::vector<std::string>> mime_types,
std::unique_ptr<api::file_manager_private::EntryProperties> properties,
std::unique_ptr<std::set<base::FilePath>> path_directory_set) {
bool is_directory = path_directory_set->find(file_system_urls_[0].path()) !=
path_directory_set->end();
sharesheet::SharesheetService* sharesheet_service =
sharesheet::SharesheetServiceFactory::GetForProfile(profile_);
GURL share_url =
(properties->can_share && *properties->can_share && properties->share_url)
? GURL(*properties->share_url)
: GURL();
bool result = sharesheet_service->HasShareTargets(apps_util::MakeShareIntent(
urls_[0], (*mime_types)[0], share_url, is_directory));
Respond(ArgumentList(extensions::api::file_manager_private::
SharesheetHasTargets::Results::Create(result)));
}
FileManagerPrivateInvokeSharesheetFunction::
FileManagerPrivateInvokeSharesheetFunction() = default;
FileManagerPrivateInvokeSharesheetFunction::
~FileManagerPrivateInvokeSharesheetFunction() = default;
ExtensionFunction::ResponseAction
FileManagerPrivateInvokeSharesheetFunction::Run() {
using extensions::api::file_manager_private::InvokeSharesheet::Params;
const std::optional<Params> params = Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
if (params->file_urls.empty()) {
return RespondNow(Error("No URLs provided"));
}
if (params->dlp_source_urls.size() != params->file_urls.size()) {
return RespondNow(Error("Mismatching URLs and DLP source URLs provided"));
}
profile_ = Profile::FromBrowserContext(browser_context());
const scoped_refptr<storage::FileSystemContext> file_system_context =
file_manager::util::GetFileSystemContextForRenderFrameHost(
profile_, render_frame_host());
// Collect all the URLs, convert them to GURLs, and crack all the urls into
// file paths.
for (const auto& url_string : params->file_urls) {
const GURL url(url_string);
storage::FileSystemURL file_system_url(
file_system_context->CrackURLInFirstPartyContext(url));
if (!ash::FileSystemBackend::CanHandleURL(file_system_url)) {
continue;
}
urls_.push_back(url);
file_system_urls_.push_back(file_system_url);
}
dlp_source_urls_ = std::move(params->dlp_source_urls);
mime_type_collector_ =
std::make_unique<app_file_handler_util::MimeTypeCollector>(profile_);
mime_type_collector_->CollectForURLs(
file_system_urls_,
base::BindOnce(
&FileManagerPrivateInvokeSharesheetFunction::OnMimeTypesCollected,
this, GetLaunchSource(params->launch_source)));
return RespondLater();
}
void FileManagerPrivateInvokeSharesheetFunction::OnMimeTypesCollected(
sharesheet::LaunchSource launch_source,
std::unique_ptr<std::vector<std::string>> mime_types) {
// On button press show sharesheet bubble.
sharesheet::SharesheetService* sharesheet_service =
sharesheet::SharesheetServiceFactory::GetForProfile(profile_);
if (!sharesheet_service) {
Respond(Error("Cannot find sharesheet service"));
return;
}
if (file_system_urls_.size() == 1 &&
file_system_urls_[0].type() == storage::kFileSystemTypeDriveFs) {
using drive::util::ConnectionStatus;
const ConnectionStatus status = drive::util::GetDriveConnectionStatus(
Profile::FromBrowserContext(browser_context()));
using enum ConnectionStatus;
if (status == kMetered || status == kConnected) {
file_manager::util::SingleEntryPropertiesGetterForDriveFs::Start(
file_system_urls_[0], profile_,
base::BindOnce(&FileManagerPrivateInvokeSharesheetFunction::
OnDrivePropertyCollected,
this, launch_source, std::move(mime_types)));
return;
}
}
sharesheet_service->ShowBubble(
GetSenderWebContents(),
apps_util::MakeShareIntent(urls_, *mime_types, dlp_source_urls_),
launch_source, base::DoNothing());
Respond(NoArguments());
}
void FileManagerPrivateInvokeSharesheetFunction::OnDrivePropertyCollected(
sharesheet::LaunchSource launch_source,
std::unique_ptr<std::vector<std::string>> mime_types,
std::unique_ptr<api::file_manager_private::EntryProperties> properties,
base::File::Error error) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (error != base::File::FILE_OK) {
Respond(Error("Drive File Error"));
return;
}
is_directory_collector_ =
std::make_unique<app_file_handler_util::IsDirectoryCollector>(profile_);
is_directory_collector_->CollectForEntriesPaths(
std::vector<base::FilePath>{file_system_urls_[0].path()},
base::BindOnce(
&FileManagerPrivateInvokeSharesheetFunction::OnIsDirectoryCollected,
this, launch_source, std::move(mime_types), std::move(properties)));
}
void FileManagerPrivateInvokeSharesheetFunction::OnIsDirectoryCollected(
sharesheet::LaunchSource launch_source,
std::unique_ptr<std::vector<std::string>> mime_types,
std::unique_ptr<api::file_manager_private::EntryProperties> properties,
std::unique_ptr<std::set<base::FilePath>> path_directory_set) {
bool is_directory = path_directory_set->find(file_system_urls_[0].path()) !=
path_directory_set->end();
sharesheet::SharesheetService* sharesheet_service =
sharesheet::SharesheetServiceFactory::GetForProfile(profile_);
if (!sharesheet_service) {
Respond(Error("Cannot find sharesheet service"));
return;
}
GURL share_url =
(properties->can_share && *properties->can_share && properties->share_url)
? GURL(*properties->share_url)
: GURL();
sharesheet_service->ShowBubble(
GetSenderWebContents(),
apps_util::MakeShareIntent(urls_[0], (*mime_types)[0], share_url,
is_directory),
launch_source, base::DoNothing());
Respond(NoArguments());
}
} // namespace extensions