blob: 11ca328cbd7b03a174554d236dd3cc01839f3f15 [file] [log] [blame]
// Copyright 2014 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/document_scan/document_scan_api.h"
#include <string>
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/bind.h"
#include "base/containers/contains.h"
#include "chromeos/crosapi/mojom/document_scan.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/crosapi/crosapi_ash.h"
#include "chrome/browser/ash/crosapi/crosapi_manager.h"
#include "chrome/browser/ash/crosapi/document_scan_ash.h"
#elif BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chromeos/lacros/lacros_service.h"
#endif
namespace extensions::api {
namespace {
// Error messages that can be included in a response when scanning fails.
constexpr char kUserGestureRequiredError[] =
"User gesture required to perform scan";
constexpr char kNoScannersAvailableError[] = "No scanners available";
constexpr char kUnsupportedMimeTypesError[] = "Unsupported MIME types";
constexpr char kScanImageError[] = "Failed to scan image";
constexpr char kVirtualPrinterUnavailableError[] =
"Virtual USB printer unavailable";
// The name of the virtual USB printer used for testing.
constexpr char kVirtualUSBPrinter[] = "DavieV Virtual USB Printer (USB)";
// The testing MIME type.
constexpr char kTestingMimeType[] = "testing";
// The PNG MIME type.
constexpr char kScannerImageMimeTypePng[] = "image/png";
// The PNG image data URL prefix of a scanned image.
constexpr char kPngImageDataUrlPrefix[] = "data:image/png;base64,";
} // namespace
DocumentScanScanFunction::DocumentScanScanFunction() = default;
DocumentScanScanFunction::~DocumentScanScanFunction() = default;
void DocumentScanScanFunction::SetMojoInterfaceForTesting(
crosapi::mojom::DocumentScan* document_scan) {
document_scan_ = document_scan;
}
ExtensionFunction::ResponseAction DocumentScanScanFunction::Run() {
params_ = document_scan::Scan::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params_.get());
if (!user_gesture())
return RespondNow(Error(kUserGestureRequiredError));
MaybeInitializeMojoInterface();
if (!document_scan_)
return RespondNow(Error(kScanImageError));
document_scan_->GetScannerNames(
base::BindOnce(&DocumentScanScanFunction::OnNamesReceived, this));
return did_respond() ? AlreadyResponded() : RespondLater();
}
void DocumentScanScanFunction::MaybeInitializeMojoInterface() {
// Check if SetMojoInterfaceForTesting() already initialized `document_scan_`.
if (document_scan_)
return;
#if BUILDFLAG(IS_CHROMEOS_ASH)
DCHECK(crosapi::CrosapiManager::IsInitialized());
document_scan_ =
crosapi::CrosapiManager::Get()->crosapi_ash()->document_scan_ash();
#else
auto* service = chromeos::LacrosService::Get();
if (service->IsAvailable<crosapi::mojom::DocumentScan>()) {
document_scan_ = service->GetRemote<crosapi::mojom::DocumentScan>().get();
} else {
LOG(ERROR) << "Document scan not available";
}
#endif
}
void DocumentScanScanFunction::OnNamesReceived(
const std::vector<std::string>& scanner_names) {
if (scanner_names.empty()) {
Respond(Error(kNoScannersAvailableError));
return;
}
bool should_use_virtual_usb_printer = false;
if (params_->options.mime_types) {
std::vector<std::string>& mime_types = *params_->options.mime_types;
if (base::Contains(mime_types, kTestingMimeType)) {
should_use_virtual_usb_printer = true;
} else if (!base::Contains(mime_types, kScannerImageMimeTypePng)) {
Respond(Error(kUnsupportedMimeTypesError));
return;
}
}
// TODO(pstew): Call a delegate method here to select a scanner and options.
// The first scanner supporting one of the requested MIME types used to be
// selected. The testing MIME type dictates that the virtual USB printer
// should be used if available. Otherwise, since all of the scanners only
// support PNG, select the first scanner in the list.
std::string scanner_name;
if (should_use_virtual_usb_printer) {
if (!base::Contains(scanner_names, kVirtualUSBPrinter)) {
Respond(Error(kVirtualPrinterUnavailableError));
return;
}
scanner_name = kVirtualUSBPrinter;
} else {
scanner_name = scanner_names[0];
}
document_scan_->ScanFirstPage(
scanner_name,
base::BindOnce(&DocumentScanScanFunction::OnScanCompleted, this));
}
void DocumentScanScanFunction::OnScanCompleted(
crosapi::mojom::ScanFailureMode failure_mode,
const absl::optional<std::string>& scan_data) {
// TODO(pstew): Enlist a delegate to display received scan in the UI and
// confirm that this scan should be sent to the caller. If this is a
// multi-page scan, provide a means for adding additional scanned images up to
// the requested limit.
if (!scan_data.has_value() ||
failure_mode != crosapi::mojom::ScanFailureMode::kNoFailure) {
Respond(Error(kScanImageError));
return;
}
std::string image_base64;
base::Base64Encode(scan_data.value(), &image_base64);
document_scan::ScanResults scan_results;
scan_results.data_urls.push_back(kPngImageDataUrlPrefix + image_base64);
scan_results.mime_type = kScannerImageMimeTypePng;
Respond(ArgumentList(document_scan::Scan::Results::Create(scan_results)));
}
} // namespace extensions::api