| // 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 "chrome/browser/ash/crosapi/document_scan_ash.h" |
| |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "ash/constants/ash_features.h" |
| #include "base/memory/weak_ptr.h" |
| #include "chrome/browser/ash/crosapi/document_scan_ash_type_converters.h" |
| #include "chrome/browser/ash/scanning/lorgnette_scanner_manager.h" |
| #include "chrome/browser/ash/scanning/lorgnette_scanner_manager_factory.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chromeos/ash/components/dbus/lorgnette/lorgnette_service.pb.h" |
| #include "components/user_manager/user_manager.h" |
| |
| namespace crosapi { |
| |
| namespace { |
| |
| Profile* GetProfile() { |
| if (!user_manager::UserManager::IsInitialized() || |
| !user_manager::UserManager::Get()->IsUserLoggedIn()) { |
| return nullptr; |
| } |
| return ProfileManager::GetPrimaryUserProfile(); |
| } |
| |
| void GetScannerNamesAdapter(DocumentScanAsh::GetScannerNamesCallback callback, |
| std::vector<std::string> scanner_names) { |
| std::move(callback).Run(scanner_names); |
| } |
| |
| // Supports the static_cast() in ProtobufResultToMojoResult() below. |
| static_assert(lorgnette::SCAN_FAILURE_MODE_NO_FAILURE == |
| static_cast<int>(mojom::ScanFailureMode::kNoFailure)); |
| static_assert(lorgnette::SCAN_FAILURE_MODE_UNKNOWN == |
| static_cast<int>(mojom::ScanFailureMode::kUnknown)); |
| static_assert(lorgnette::SCAN_FAILURE_MODE_DEVICE_BUSY == |
| static_cast<int>(mojom::ScanFailureMode::kDeviceBusy)); |
| static_assert(lorgnette::SCAN_FAILURE_MODE_ADF_JAMMED == |
| static_cast<int>(mojom::ScanFailureMode::kAdfJammed)); |
| static_assert(lorgnette::SCAN_FAILURE_MODE_ADF_EMPTY == |
| static_cast<int>(mojom::ScanFailureMode::kAdfEmpty)); |
| static_assert(lorgnette::SCAN_FAILURE_MODE_FLATBED_OPEN == |
| static_cast<int>(mojom::ScanFailureMode::kFlatbedOpen)); |
| static_assert(lorgnette::SCAN_FAILURE_MODE_IO_ERROR == |
| static_cast<int>(mojom::ScanFailureMode::kIoError)); |
| |
| mojom::ScanFailureMode ProtobufResultToMojoResult( |
| lorgnette::ScanFailureMode failure_mode) { |
| // The static_assert() checks above make this cast safe. |
| return static_cast<mojom::ScanFailureMode>(failure_mode); |
| } |
| |
| // Wrapper around `data` that allows this to be a WeakPtr. |
| struct ScanResult { |
| public: |
| ScanResult() = default; |
| ScanResult(const ScanResult&) = delete; |
| ScanResult& operator=(const ScanResult&) = delete; |
| ~ScanResult() = default; |
| |
| base::WeakPtr<ScanResult> AsWeakPtr() { |
| return weak_ptr_factory.GetWeakPtr(); |
| } |
| |
| std::optional<std::string> data; |
| |
| private: |
| base::WeakPtrFactory<ScanResult> weak_ptr_factory{this}; |
| }; |
| |
| void OnPageReceived(base::WeakPtr<ScanResult> scan_result, |
| std::string scanned_image, |
| uint32_t /*page_number*/) { |
| if (!scan_result) |
| return; |
| |
| // Take only the first page of the scan. |
| if (scan_result->data.has_value()) |
| return; |
| |
| scan_result->data = std::move(scanned_image); |
| } |
| |
| // As a standalone function, this will always run `callback`. If this was a |
| // DocumentScanAsh method instead, then that method bound to a |
| // base::WeakPtr<DocumentScanAsh> may sometimes not run `callback`. |
| void OnScanCompleted(DocumentScanAsh::ScanFirstPageCallback callback, |
| std::unique_ptr<ScanResult> scan_result, |
| lorgnette::ScanFailureMode failure_mode) { |
| std::move(callback).Run(ProtobufResultToMojoResult(failure_mode), |
| std::move(scan_result->data)); |
| } |
| |
| void GetScannerListAdapter( |
| DocumentScanAsh::GetScannerListCallback callback, |
| const std::optional<lorgnette::ListScannersResponse>& response_in) { |
| if (!response_in) { |
| auto response_out = mojom::GetScannerListResponse::New(); |
| response_out->result = mojom::ScannerOperationResult::kInternalError; |
| std::move(callback).Run(std::move(response_out)); |
| return; |
| } |
| std::move(callback).Run( |
| mojom::GetScannerListResponse::From(response_in.value())); |
| } |
| |
| void OpenScannerAdapter( |
| const std::string& scanner_id, |
| DocumentScanAsh::OpenScannerCallback callback, |
| const std::optional<lorgnette::OpenScannerResponse>& response_in) { |
| if (!response_in) { |
| auto response_out = mojom::OpenScannerResponse::New(); |
| response_out->scanner_id = scanner_id; |
| response_out->result = mojom::ScannerOperationResult::kInternalError; |
| std::move(callback).Run(std::move(response_out)); |
| return; |
| } |
| std::move(callback).Run( |
| mojom::OpenScannerResponse::From(response_in.value())); |
| } |
| |
| void CloseScannerAdapter( |
| const std::string& scanner_handle, |
| DocumentScanAsh::CloseScannerCallback callback, |
| const std::optional<lorgnette::CloseScannerResponse>& response_in) { |
| if (!response_in) { |
| auto response_out = mojom::CloseScannerResponse::New(); |
| response_out->scanner_handle = scanner_handle; |
| response_out->result = mojom::ScannerOperationResult::kInternalError; |
| std::move(callback).Run(std::move(response_out)); |
| return; |
| } |
| std::move(callback).Run( |
| mojom::CloseScannerResponse::From(response_in.value())); |
| } |
| |
| void StartPreparedScanAdapter( |
| const std::string& scanner_handle, |
| DocumentScanAsh::StartPreparedScanCallback callback, |
| const std::optional<lorgnette::StartPreparedScanResponse>& response_in) { |
| if (!response_in) { |
| auto response = mojom::StartPreparedScanResponse::New(); |
| response->result = mojom::ScannerOperationResult::kInternalError; |
| response->scanner_handle = scanner_handle; |
| std::move(callback).Run(std::move(response)); |
| return; |
| } |
| std::move(callback).Run( |
| mojom::StartPreparedScanResponse::From(response_in.value())); |
| } |
| |
| void ReadScanDataAdapter( |
| const std::string& job_handle, |
| DocumentScanAsh::ReadScanDataCallback callback, |
| const std::optional<lorgnette::ReadScanDataResponse>& response_in) { |
| if (!response_in) { |
| auto response = mojom::ReadScanDataResponse::New(); |
| response->result = mojom::ScannerOperationResult::kInternalError; |
| response->job_handle = job_handle; |
| std::move(callback).Run(std::move(response)); |
| return; |
| } |
| std::move(callback).Run( |
| mojom::ReadScanDataResponse::From(response_in.value())); |
| } |
| |
| void SetOptionsAdapter( |
| const std::string& scanner_handle, |
| std::vector<std::string> option_names, |
| std::vector<std::string> invalid_option_names, |
| DocumentScanAsh::SetOptionsCallback callback, |
| const std::optional<lorgnette::SetOptionsResponse>& response_in) { |
| if (!response_in) { |
| auto response = mojom::SetOptionsResponse::New(); |
| response->scanner_handle = scanner_handle; |
| for (const std::string& option_name : option_names) { |
| auto result = mojom::SetOptionResult::New(); |
| result->name = option_name; |
| result->result = mojom::ScannerOperationResult::kInternalError; |
| response->results.emplace_back(std::move(result)); |
| } |
| std::move(callback).Run(std::move(response)); |
| return; |
| } |
| lorgnette::SetOptionsResponse response = response_in.value(); |
| for (const std::string& invalid_name : invalid_option_names) { |
| (*response.mutable_results())[invalid_name] = |
| lorgnette::OperationResult::OPERATION_RESULT_WRONG_TYPE; |
| } |
| std::move(callback).Run(mojom::SetOptionsResponse::From(response)); |
| } |
| |
| void GetOptionGroupsAdapter( |
| const std::string& scanner_handle, |
| DocumentScanAsh::GetOptionGroupsCallback callback, |
| const std::optional<lorgnette::GetCurrentConfigResponse>& response_in) { |
| if (!response_in) { |
| auto response = mojom::GetOptionGroupsResponse::New(); |
| response->result = mojom::ScannerOperationResult::kInternalError; |
| response->scanner_handle = scanner_handle; |
| std::move(callback).Run(std::move(response)); |
| return; |
| } |
| std::move(callback).Run( |
| mojom::GetOptionGroupsResponse::From(response_in.value())); |
| } |
| |
| void CancelScanAdapter( |
| const std::string& job_handle, |
| DocumentScanAsh::CancelScanCallback callback, |
| const std::optional<lorgnette::CancelScanResponse>& response_in) { |
| if (!response_in) { |
| auto response = mojom::CancelScanResponse::New(); |
| response->job_handle = job_handle; |
| response->result = mojom::ScannerOperationResult::kInternalError; |
| std::move(callback).Run(std::move(response)); |
| return; |
| } |
| std::move(callback).Run(mojom::CancelScanResponse::From(response_in.value())); |
| } |
| |
| } // namespace |
| |
| DocumentScanAsh::DocumentScanAsh() = default; |
| |
| DocumentScanAsh::~DocumentScanAsh() = default; |
| |
| void DocumentScanAsh::BindReceiver( |
| mojo::PendingReceiver<mojom::DocumentScan> pending_receiver) { |
| receivers_.Add(this, std::move(pending_receiver)); |
| } |
| |
| void DocumentScanAsh::GetScannerNames(GetScannerNamesCallback callback) { |
| ash::LorgnetteScannerManagerFactory::GetForBrowserContext(GetProfile()) |
| ->GetScannerNames( |
| base::BindOnce(GetScannerNamesAdapter, std::move(callback))); |
| } |
| |
| void DocumentScanAsh::ScanFirstPage(const std::string& scanner_name, |
| ScanFirstPageCallback callback) { |
| lorgnette::ScanSettings settings; |
| settings.set_color_mode(lorgnette::MODE_COLOR); // Hardcoded for now. |
| |
| auto scan_result = std::make_unique<ScanResult>(); |
| auto scan_result_weak_ptr = scan_result->AsWeakPtr(); |
| ash::LorgnetteScannerManagerFactory::GetForBrowserContext(GetProfile()) |
| ->Scan(scanner_name, settings, base::NullCallback(), |
| base::BindRepeating(&OnPageReceived, scan_result_weak_ptr), |
| base::BindOnce(&OnScanCompleted, std::move(callback), |
| std::move(scan_result))); |
| } |
| |
| void DocumentScanAsh::GetScannerList(const std::string& client_id, |
| mojom::ScannerEnumFilterPtr filter, |
| GetScannerListCallback callback) { |
| using LocalScannerFilter = ash::LorgnetteScannerManager::LocalScannerFilter; |
| using SecureScannerFilter = ash::LorgnetteScannerManager::SecureScannerFilter; |
| |
| ash::LorgnetteScannerManagerFactory::GetForBrowserContext(GetProfile()) |
| ->GetScannerInfoList( |
| client_id, |
| filter->local ? LocalScannerFilter::kLocalScannersOnly |
| : LocalScannerFilter::kIncludeNetworkScanners, |
| filter->secure ? SecureScannerFilter::kSecureScannersOnly |
| : SecureScannerFilter::kIncludeUnsecureScanners, |
| base::BindOnce(&GetScannerListAdapter, std::move(callback))); |
| } |
| |
| void DocumentScanAsh::OpenScanner(const std::string& client_id, |
| const std::string& scanner_id, |
| OpenScannerCallback callback) { |
| lorgnette::OpenScannerRequest request; |
| request.mutable_scanner_id()->set_connection_string(scanner_id); |
| request.set_client_id(client_id); |
| ash::LorgnetteScannerManagerFactory::GetForBrowserContext(GetProfile()) |
| ->OpenScanner( |
| std::move(request), |
| base::BindOnce(&OpenScannerAdapter, scanner_id, std::move(callback))); |
| } |
| |
| void DocumentScanAsh::CloseScanner(const std::string& scanner_handle, |
| CloseScannerCallback callback) { |
| lorgnette::CloseScannerRequest request; |
| request.mutable_scanner()->set_token(scanner_handle); |
| ash::LorgnetteScannerManagerFactory::GetForBrowserContext(GetProfile()) |
| ->CloseScanner(std::move(request), |
| base::BindOnce(&CloseScannerAdapter, scanner_handle, |
| std::move(callback))); |
| } |
| |
| void DocumentScanAsh::StartPreparedScan(const std::string& scanner_handle, |
| mojom::StartScanOptionsPtr options, |
| StartPreparedScanCallback callback) { |
| lorgnette::StartPreparedScanRequest request; |
| request.mutable_scanner()->set_token(scanner_handle); |
| request.set_image_format(options->format); |
| if (options->max_read_size) { |
| request.set_max_read_size(*options->max_read_size); |
| } |
| |
| ash::LorgnetteScannerManagerFactory::GetForBrowserContext(GetProfile()) |
| ->StartPreparedScan( |
| request, base::BindOnce(&StartPreparedScanAdapter, scanner_handle, |
| std::move(callback))); |
| } |
| |
| void DocumentScanAsh::ReadScanData(const std::string& job_handle, |
| ReadScanDataCallback callback) { |
| lorgnette::ReadScanDataRequest request; |
| request.mutable_job_handle()->set_token(job_handle); |
| |
| ash::LorgnetteScannerManagerFactory::GetForBrowserContext(GetProfile()) |
| ->ReadScanData(request, base::BindOnce(&ReadScanDataAdapter, job_handle, |
| std::move(callback))); |
| } |
| |
| void DocumentScanAsh::SetOptions(const std::string& scanner_handle, |
| std::vector<mojom::OptionSettingPtr> options, |
| SetOptionsCallback callback) { |
| lorgnette::SetOptionsRequest request; |
| request.mutable_scanner()->set_token(scanner_handle); |
| // Keep track of all of the option names. This is used if we don't get a |
| // valid response from the backend. All of these options will get sent back |
| // to the caller with an error result. |
| std::vector<std::string> option_names; |
| // Separately, keep track of any invalid options names (where the type |
| // specified for the value does not equal the type of the option). These |
| // options will get sent back to the caller with an appropriate error result. |
| std::vector<std::string> invalid_option_names; |
| for (const mojom::OptionSettingPtr& option_request : options) { |
| option_names.emplace_back(option_request->name); |
| |
| auto maybe_option = |
| option_request.To<std::optional<lorgnette::ScannerOption>>(); |
| if (maybe_option.has_value()) { |
| *request.add_options() = maybe_option.value(); |
| } else { |
| invalid_option_names.emplace_back(option_request->name); |
| } |
| } |
| |
| ash::LorgnetteScannerManagerFactory::GetForBrowserContext(GetProfile()) |
| ->SetOptions(request, base::BindOnce(&SetOptionsAdapter, scanner_handle, |
| option_names, invalid_option_names, |
| std::move(callback))); |
| } |
| |
| void DocumentScanAsh::GetOptionGroups(const std::string& scanner_handle, |
| GetOptionGroupsCallback callback) { |
| lorgnette::GetCurrentConfigRequest request; |
| request.mutable_scanner()->set_token(scanner_handle); |
| |
| ash::LorgnetteScannerManagerFactory::GetForBrowserContext(GetProfile()) |
| ->GetCurrentConfig(request, |
| base::BindOnce(&GetOptionGroupsAdapter, scanner_handle, |
| std::move(callback))); |
| } |
| |
| void DocumentScanAsh::CancelScan(const std::string& job_handle, |
| CancelScanCallback callback) { |
| lorgnette::CancelScanRequest request; |
| request.mutable_job_handle()->set_token(job_handle); |
| |
| ash::LorgnetteScannerManagerFactory::GetForBrowserContext(GetProfile()) |
| ->CancelScan(request, base::BindOnce(&CancelScanAdapter, job_handle, |
| std::move(callback))); |
| } |
| |
| } // namespace crosapi |