Commit 5a8efdff authored by Jesse Schettler's avatar Jesse Schettler Committed by Commit Bot

document_scan: Refactor the Document Scan API

The Document Scan API is only supported on Chrome OS, so ensure it is
only built on Chrome OS. Additionally, remove the unnecessary
DocumentScanInterface layer now that there doesn't have to be interface
implementations for Chrome OS and non-Chrome OS builds.

Bug: None
Test: Perform a scan using the Scan extension
Change-Id: Ie18d15cac752e9166be6ee538cae3a7e3c8fe4bb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2296642
Commit-Queue: Jesse Schettler <jschettler@chromium.org>
Reviewed-by: default avatarDavid Bertoni <dbertoni@chromium.org>
Reviewed-by: default avatarSteven Bennetts <stevenjb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#788820}
parent bc279ff4
...@@ -30,24 +30,20 @@ void FakeLorgnetteManagerClient::ScanImageToString( ...@@ -30,24 +30,20 @@ void FakeLorgnetteManagerClient::ScanImageToString(
std::string device_name, std::string device_name,
const ScanProperties& properties, const ScanProperties& properties,
DBusMethodCallback<std::string> callback) { DBusMethodCallback<std::string> callback) {
auto it = scan_data_.find(
std::make_tuple(device_name, properties.mode, properties.resolution_dpi));
auto data =
it == scan_data_.end() ? base::nullopt : base::make_optional(it->second);
base::ThreadTaskRunnerHandle::Get()->PostTask( base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), std::move(data))); FROM_HERE,
} base::BindOnce(std::move(callback), std::move(scan_image_response_)));
void FakeLorgnetteManagerClient::AddScanData(const std::string& device_name,
const ScanProperties& properties,
const std::string& data) {
scan_data_[std::make_tuple(device_name, properties.mode,
properties.resolution_dpi)] = data;
} }
void FakeLorgnetteManagerClient::SetListScannersResponse( void FakeLorgnetteManagerClient::SetListScannersResponse(
const lorgnette::ListScannersResponse& list_scanners_response) { const base::Optional<lorgnette::ListScannersResponse>&
list_scanners_response) {
list_scanners_response_ = list_scanners_response; list_scanners_response_ = list_scanners_response;
} }
void FakeLorgnetteManagerClient::SetScanImageToStringResponse(
const base::Optional<std::string>& scan_image_response) {
scan_image_response_ = scan_image_response;
}
} // namespace chromeos } // namespace chromeos
...@@ -5,17 +5,14 @@ ...@@ -5,17 +5,14 @@
#ifndef CHROMEOS_DBUS_FAKE_LORGNETTE_MANAGER_CLIENT_H_ #ifndef CHROMEOS_DBUS_FAKE_LORGNETTE_MANAGER_CLIENT_H_
#define CHROMEOS_DBUS_FAKE_LORGNETTE_MANAGER_CLIENT_H_ #define CHROMEOS_DBUS_FAKE_LORGNETTE_MANAGER_CLIENT_H_
#include <map>
#include <string> #include <string>
#include <tuple>
#include "base/optional.h"
#include "chromeos/dbus/lorgnette/lorgnette_service.pb.h" #include "chromeos/dbus/lorgnette/lorgnette_service.pb.h"
#include "chromeos/dbus/lorgnette_manager_client.h" #include "chromeos/dbus/lorgnette_manager_client.h"
namespace chromeos { namespace chromeos {
// Lorgnette LorgnetteManagerClient implementation used on Linux desktop,
// which does nothing.
class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeLorgnetteManagerClient class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeLorgnetteManagerClient
: public LorgnetteManagerClient { : public LorgnetteManagerClient {
public: public:
...@@ -33,24 +30,18 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeLorgnetteManagerClient ...@@ -33,24 +30,18 @@ class COMPONENT_EXPORT(CHROMEOS_DBUS) FakeLorgnetteManagerClient
const ScanProperties& properties, const ScanProperties& properties,
DBusMethodCallback<std::string> callback) override; DBusMethodCallback<std::string> callback) override;
// Adds a fake scan data, which will be returned by ScanImageToString(),
// if |device_name| and |properties| are matched.
void AddScanData(const std::string& device_name,
const ScanProperties& properties,
const std::string& data);
// Sets the response returned by ListScanners(). // Sets the response returned by ListScanners().
void SetListScannersResponse( void SetListScannersResponse(
const lorgnette::ListScannersResponse& list_scanners_response); const base::Optional<lorgnette::ListScannersResponse>&
list_scanners_response);
private: // Sets the response returned by ScanImageToString().
lorgnette::ListScannersResponse list_scanners_response_; void SetScanImageToStringResponse(
const base::Optional<std::string>& scan_image_response);
// Use tuple for a map below, which has pre-defined "less", for convenience. private:
using ScanDataKey = std::tuple<std::string /* device_name */, base::Optional<lorgnette::ListScannersResponse> list_scanners_response_;
std::string /* ScanProperties.mode */, base::Optional<std::string> scan_image_response_;
int /* Scanproperties.resolution_dpi */>;
std::map<ScanDataKey, std::string /* data */> scan_data_;
}; };
} // namespace chromeos } // namespace chromeos
......
...@@ -624,9 +624,6 @@ source_set("unit_tests") { ...@@ -624,9 +624,6 @@ source_set("unit_tests") {
"api/declarative_net_request/ruleset_matcher_unittest.cc", "api/declarative_net_request/ruleset_matcher_unittest.cc",
"api/declarative_webrequest/webrequest_condition_attribute_unittest.cc", "api/declarative_webrequest/webrequest_condition_attribute_unittest.cc",
"api/declarative_webrequest/webrequest_condition_unittest.cc", "api/declarative_webrequest/webrequest_condition_unittest.cc",
"api/document_scan/document_scan_api_unittest.cc",
"api/document_scan/fake_document_scan_interface.cc",
"api/document_scan/fake_document_scan_interface.h",
"api/file_handlers/app_file_handler_util_unittest.cc", "api/file_handlers/app_file_handler_util_unittest.cc",
"api/file_handlers/directory_util_unittest.cc", "api/file_handlers/directory_util_unittest.cc",
"api/file_handlers/mime_util_unittest.cc", "api/file_handlers/mime_util_unittest.cc",
...@@ -750,7 +747,7 @@ source_set("unit_tests") { ...@@ -750,7 +747,7 @@ source_set("unit_tests") {
if (is_chromeos) { if (is_chromeos) {
sources += [ sources += [
"api/audio/audio_device_id_calculator_unittest.cc", "api/audio/audio_device_id_calculator_unittest.cc",
"api/document_scan/document_scan_interface_chromeos_unittest.cc", "api/document_scan/document_scan_api_unittest.cc",
"api/feedback_private/access_rate_limiter_chromeos_unittest.cc", "api/feedback_private/access_rate_limiter_chromeos_unittest.cc",
"api/feedback_private/feedback_private_api_chromeos_unittest.cc", "api/feedback_private/feedback_private_api_chromeos_unittest.cc",
"api/feedback_private/feedback_private_api_unittest_base_chromeos.cc", "api/feedback_private/feedback_private_api_unittest_base_chromeos.cc",
......
...@@ -55,7 +55,6 @@ source_set("api") { ...@@ -55,7 +55,6 @@ source_set("api") {
"//extensions/browser/api/declarative_webrequest", "//extensions/browser/api/declarative_webrequest",
"//extensions/browser/api/display_source", "//extensions/browser/api/display_source",
"//extensions/browser/api/dns", "//extensions/browser/api/dns",
"//extensions/browser/api/document_scan",
"//extensions/browser/api/feedback_private", "//extensions/browser/api/feedback_private",
"//extensions/browser/api/file_handlers", "//extensions/browser/api/file_handlers",
"//extensions/browser/api/file_system", "//extensions/browser/api/file_system",
...@@ -133,6 +132,7 @@ source_set("api") { ...@@ -133,6 +132,7 @@ source_set("api") {
"//extensions/browser/api/clipboard", "//extensions/browser/api/clipboard",
"//extensions/browser/api/crash_report_private", "//extensions/browser/api/crash_report_private",
"//extensions/browser/api/diagnostics", "//extensions/browser/api/diagnostics",
"//extensions/browser/api/document_scan",
"//extensions/browser/api/networking_config", "//extensions/browser/api/networking_config",
"//extensions/browser/api/system_power_source", "//extensions/browser/api/system_power_source",
"//extensions/browser/api/virtual_keyboard", "//extensions/browser/api/virtual_keyboard",
......
...@@ -7,28 +7,18 @@ import("//extensions/buildflags/buildflags.gni") ...@@ -7,28 +7,18 @@ import("//extensions/buildflags/buildflags.gni")
assert(enable_extensions, assert(enable_extensions,
"Cannot depend on extensions because enable_extensions=false.") "Cannot depend on extensions because enable_extensions=false.")
assert(is_chromeos)
source_set("document_scan") { source_set("document_scan") {
sources = [ sources = [
"document_scan_api.cc", "document_scan_api.cc",
"document_scan_api.h", "document_scan_api.h",
"document_scan_interface.cc",
"document_scan_interface.h",
] ]
if (is_chromeos) { deps = [
sources += [ "//chromeos/dbus:lorgnette_proto",
"document_scan_interface_chromeos.cc", "//extensions/common/api",
"document_scan_interface_chromeos.h", ]
]
} else {
sources += [ "document_scan_interface_nonchromeos.cc" ]
}
deps = [ "//extensions/common/api" ]
if (is_chromeos) {
deps += [ "//chromeos/dbus:lorgnette_proto" ]
}
public_deps = [ "//extensions/browser:browser_sources" ] public_deps = [ "//extensions/browser:browser_sources" ]
} }
...@@ -4,24 +4,47 @@ ...@@ -4,24 +4,47 @@
#include "extensions/browser/api/document_scan/document_scan_api.h" #include "extensions/browser/api/document_scan/document_scan_api.h"
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/logging.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/lorgnette_manager_client.h"
#include "third_party/cros_system_api/dbus/lorgnette/dbus-constants.h"
namespace extensions {
namespace api {
namespace { namespace {
const char kScannerNotAvailable[] = "Scanner not available"; // Error messages that can be included in a response when scanning fails.
const char kUserGestureRequiredError[] = constexpr char kUserGestureRequiredError[] =
"User gesture required to perform scan"; "User gesture required to perform scan";
constexpr char kListScannersError[] = "Failed to obtain list of scanners";
constexpr char kNoScannersAvailableError[] = "No scanners available";
constexpr char kUnsupportedMimeTypesError[] = "Unsupported MIME types";
constexpr char kScanImageError[] = "Failed to scan image";
} // namespace // The PNG MIME type.
constexpr char kScannerImageMimeTypePng[] = "image/png";
namespace extensions { // The PNG image data URL prefix of a scanned image.
namespace api { constexpr char kPngImageDataUrlPrefix[] = "data:image/png;base64,";
chromeos::LorgnetteManagerClient* GetLorgnetteManagerClient() {
DCHECK(chromeos::DBusThreadManager::IsInitialized());
return chromeos::DBusThreadManager::Get()->GetLorgnetteManagerClient();
}
} // namespace
DocumentScanScanFunction::DocumentScanScanFunction() DocumentScanScanFunction::DocumentScanScanFunction() = default;
: document_scan_interface_(DocumentScanInterface::CreateInstance()) {}
DocumentScanScanFunction::~DocumentScanScanFunction() {} DocumentScanScanFunction::~DocumentScanScanFunction() = default;
ExtensionFunction::ResponseAction DocumentScanScanFunction::Run() { ExtensionFunction::ResponseAction DocumentScanScanFunction::Run() {
params_ = document_scan::Scan::Params::Create(*args_); params_ = document_scan::Scan::Params::Create(*args_);
...@@ -30,62 +53,63 @@ ExtensionFunction::ResponseAction DocumentScanScanFunction::Run() { ...@@ -30,62 +53,63 @@ ExtensionFunction::ResponseAction DocumentScanScanFunction::Run() {
if (!user_gesture()) if (!user_gesture())
return RespondNow(Error(kUserGestureRequiredError)); return RespondNow(Error(kUserGestureRequiredError));
document_scan_interface_->ListScanners( GetLorgnetteManagerClient()->ListScanners(
base::BindOnce(&DocumentScanScanFunction::OnScannerListReceived, this)); base::BindOnce(&DocumentScanScanFunction::OnScannerListReceived, this));
return did_respond() ? AlreadyResponded() : RespondLater(); return did_respond() ? AlreadyResponded() : RespondLater();
} }
void DocumentScanScanFunction::OnScannerListReceived( void DocumentScanScanFunction::OnScannerListReceived(
const std::vector<DocumentScanInterface::ScannerDescription>& base::Optional<lorgnette::ListScannersResponse> response) {
scanner_descriptions, if (!response) {
const std::string& error) { Respond(Error(kListScannersError));
auto scanner_i = scanner_descriptions.cbegin(); return;
// If no |scanner_descriptions| is empty, this is an error. If no
// MIME types are specified, the first scanner is chosen. If MIME
// types are specified, the first scanner that supports one of these
// MIME types is selected.
if (params_->options.mime_types) {
std::vector<std::string>& mime_types = *params_->options.mime_types;
for (; scanner_i != scanner_descriptions.end(); ++scanner_i) {
if (base::Contains(mime_types, scanner_i->image_mime_type)) {
break;
}
}
} }
if (scanner_i == scanner_descriptions.end()) { if (response->scanners_size() == 0) {
Respond(Error(kScannerNotAvailable)); Respond(Error(kNoScannersAvailableError));
return; return;
} }
// PNG is currently the only supported MIME type.
if (params_->options.mime_types) {
std::vector<std::string>& mime_types = *params_->options.mime_types;
if (!base::Contains(mime_types, kScannerImageMimeTypePng)) {
Respond(Error(kUnsupportedMimeTypesError));
return;
}
}
// TODO(pstew): Call a delegate method here to select a scanner and options. // TODO(pstew): Call a delegate method here to select a scanner and options.
document_scan_interface_->Scan( // The first scanner supporting one of the requested MIME types used to be
scanner_i->name, DocumentScanInterface::kScanModeColor, 0, // selected. Since all of the scanners only support PNG, this results in
// selecting the first scanner in the list.
const auto& scanner = response->scanners()[0];
chromeos::LorgnetteManagerClient::ScanProperties properties;
properties.mode = lorgnette::kScanPropertyModeColor;
GetLorgnetteManagerClient()->ScanImageToString(
scanner.name(), properties,
base::BindOnce(&DocumentScanScanFunction::OnResultsReceived, this)); base::BindOnce(&DocumentScanScanFunction::OnResultsReceived, this));
} }
void DocumentScanScanFunction::OnResultsReceived( void DocumentScanScanFunction::OnResultsReceived(
const std::string& scanned_image, base::Optional<std::string> scanned_image) {
const std::string& mime_type, // TODO(pstew): Enlist a delegate to display received scan in the UI and
const std::string& error) { // confirm that this scan should be sent to the caller. If this is a
// TODO(pstew): Enlist a delegate to display received scan in the UI // multi-page scan, provide a means for adding additional scanned images up to
// and confirm that this scan should be sent to the caller. If this // the requested limit.
// is a multi-page scan, provide a means for adding additional scanned if (!scanned_image.has_value()) {
// images up to the requested limit. Respond(Error(kScanImageError));
if (!error.empty()) {
Respond(Error(error));
return; return;
} }
std::string image_base64;
base::Base64Encode(scanned_image.value(), &image_base64);
document_scan::ScanResults scan_results; document_scan::ScanResults scan_results;
if (!scanned_image.empty()) { scan_results.data_urls.push_back(kPngImageDataUrlPrefix + image_base64);
scan_results.data_urls.push_back(scanned_image); scan_results.mime_type = kScannerImageMimeTypePng;
}
scan_results.mime_type = mime_type;
Respond(ArgumentList(document_scan::Scan::Results::Create(scan_results))); Respond(ArgumentList(document_scan::Scan::Results::Create(scan_results)));
} }
} // namespace api } // namespace api
} // namespace extensions } // namespace extensions
...@@ -7,13 +7,14 @@ ...@@ -7,13 +7,14 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector>
#include "extensions/browser/api/document_scan/document_scan_interface.h" #include "base/optional.h"
#include "chromeos/dbus/lorgnette/lorgnette_service.pb.h"
#include "extensions/browser/extension_function.h" #include "extensions/browser/extension_function.h"
#include "extensions/common/api/document_scan.h" #include "extensions/common/api/document_scan.h"
namespace extensions { namespace extensions {
namespace api { namespace api {
class DocumentScanScanFunction : public ExtensionFunction { class DocumentScanScanFunction : public ExtensionFunction {
...@@ -33,18 +34,14 @@ class DocumentScanScanFunction : public ExtensionFunction { ...@@ -33,18 +34,14 @@ class DocumentScanScanFunction : public ExtensionFunction {
friend class DocumentScanScanFunctionTest; friend class DocumentScanScanFunctionTest;
void OnScannerListReceived( void OnScannerListReceived(
const std::vector<DocumentScanInterface::ScannerDescription>& base::Optional<lorgnette::ListScannersResponse> response);
scanner_descriptions, void OnResultsReceived(base::Optional<std::string> scanned_image);
const std::string& error);
void OnResultsReceived(const std::string& scanned_image,
const std::string& mime_type,
const std::string& error);
std::unique_ptr<document_scan::Scan::Params> params_; std::unique_ptr<document_scan::Scan::Params> params_;
std::unique_ptr<DocumentScanInterface> document_scan_interface_;
}; };
} // namespace api } // namespace api
} // namespace extensions } // namespace extensions
#endif // EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_DOCUMENT_SCAN_API_H_ #endif // EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_DOCUMENT_SCAN_API_H_
...@@ -2,33 +2,61 @@ ...@@ -2,33 +2,61 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "extensions/browser/api/document_scan/document_scan_api.h"
#include <string> #include <string>
#include <vector> #include <utility>
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "extensions/browser/api/document_scan/fake_document_scan_interface.h" #include "base/optional.h"
#include "base/values.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_lorgnette_manager_client.h"
#include "chromeos/dbus/lorgnette/lorgnette_service.pb.h"
#include "extensions/browser/api/document_scan/document_scan_api.h"
#include "extensions/browser/api_test_utils.h" #include "extensions/browser/api_test_utils.h"
#include "extensions/browser/api_unittest.h" #include "extensions/browser/api_unittest.h"
#include "testing/gmock/include/gmock/gmock-matchers.h" #include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/lorgnette/dbus-constants.h"
namespace extensions { namespace extensions {
namespace api { namespace api {
// Tests of networking_private_crypto support for Networking Private API. namespace {
lorgnette::ListScannersResponse CreateListScannersResponse() {
lorgnette::ScannerInfo scanner;
scanner.set_name("Dank Scanner");
scanner.set_manufacturer("Scanners, Inc.");
scanner.set_model("TX1000");
scanner.set_type("Flatbed");
lorgnette::ListScannersResponse response;
*response.add_scanners() = std::move(scanner);
return response;
}
} // namespace
class DocumentScanScanFunctionTest : public ApiUnitTest { class DocumentScanScanFunctionTest : public ApiUnitTest {
public: public:
DocumentScanScanFunctionTest() DocumentScanScanFunctionTest()
: function_(base::MakeRefCounted<DocumentScanScanFunction>()), : function_(base::MakeRefCounted<DocumentScanScanFunction>()) {}
document_scan_interface_(new FakeDocumentScanInterface()) {}
~DocumentScanScanFunctionTest() override {} ~DocumentScanScanFunctionTest() override {}
void SetUp() override { void SetUp() override {
ApiUnitTest::SetUp(); ApiUnitTest::SetUp();
// Passes ownership. chromeos::DBusThreadManager::Initialize();
function_->document_scan_interface_.reset(document_scan_interface_); function_->set_user_gesture(true);
}
void TearDown() override {
chromeos::DBusThreadManager::Shutdown();
ApiUnitTest::TearDown();
}
chromeos::FakeLorgnetteManagerClient* GetLorgnetteManagerClient() {
return static_cast<chromeos::FakeLorgnetteManagerClient*>(
chromeos::DBusThreadManager::Get()->GetLorgnetteManagerClient());
} }
protected: protected:
...@@ -40,64 +68,58 @@ class DocumentScanScanFunctionTest : public ApiUnitTest { ...@@ -40,64 +68,58 @@ class DocumentScanScanFunctionTest : public ApiUnitTest {
} }
scoped_refptr<DocumentScanScanFunction> function_; scoped_refptr<DocumentScanScanFunction> function_;
FakeDocumentScanInterface* document_scan_interface_; // Owned by function_.
}; };
TEST_F(DocumentScanScanFunctionTest, GestureRequired) { TEST_F(DocumentScanScanFunctionTest, UserGestureRequiredError) {
function_->set_user_gesture(false);
EXPECT_EQ("User gesture required to perform scan", EXPECT_EQ("User gesture required to perform scan",
RunFunctionAndReturnError("[{}]")); RunFunctionAndReturnError("[{}]"));
} }
TEST_F(DocumentScanScanFunctionTest, NoScanners) { TEST_F(DocumentScanScanFunctionTest, ListScannersError) {
function_->set_user_gesture(true); GetLorgnetteManagerClient()->SetListScannersResponse(base::nullopt);
document_scan_interface_->SetListScannersResult({}, ""); EXPECT_EQ("Failed to obtain list of scanners",
EXPECT_EQ("Scanner not available", RunFunctionAndReturnError("[{}]")); RunFunctionAndReturnError("[{}]"));
}
TEST_F(DocumentScanScanFunctionTest, NoScannersAvailableError) {
lorgnette::ListScannersResponse response;
GetLorgnetteManagerClient()->SetListScannersResponse(response);
EXPECT_EQ("No scanners available", RunFunctionAndReturnError("[{}]"));
} }
TEST_F(DocumentScanScanFunctionTest, NoMatchingScanners) { TEST_F(DocumentScanScanFunctionTest, UnsupportedMimeTypesError) {
function_->set_user_gesture(true); GetLorgnetteManagerClient()->SetListScannersResponse(
std::vector<DocumentScanInterface::ScannerDescription> scanner_list; CreateListScannersResponse());
DocumentScanInterface::ScannerDescription scanner; EXPECT_EQ("Unsupported MIME types",
scanner.image_mime_type = "img/fresco"; RunFunctionAndReturnError("[{\"mimeTypes\": [\"image/tiff\"]}]"));
scanner_list.push_back(scanner);
document_scan_interface_->SetListScannersResult(scanner_list, "");
EXPECT_EQ(
"Scanner not available",
RunFunctionAndReturnError("[{\"mimeTypes\": [\"img/silverpoint\"]}]"));
} }
TEST_F(DocumentScanScanFunctionTest, ScanFailure) { TEST_F(DocumentScanScanFunctionTest, ScanImageError) {
function_->set_user_gesture(true); GetLorgnetteManagerClient()->SetListScannersResponse(
std::vector<DocumentScanInterface::ScannerDescription> scanner_list; CreateListScannersResponse());
DocumentScanInterface::ScannerDescription scanner; GetLorgnetteManagerClient()->SetScanImageToStringResponse(base::nullopt);
const char kMimeType[] = "img/tempera"; EXPECT_EQ("Failed to scan image",
const char kScannerName[] = "Michelangelo"; RunFunctionAndReturnError("[{\"mimeTypes\": [\"image/png\"]}]"));
scanner.name = kScannerName;
scanner.image_mime_type = kMimeType;
scanner_list.push_back(scanner);
document_scan_interface_->SetListScannersResult(scanner_list, "");
const char kScanError[] = "Someone ate all the eggs";
document_scan_interface_->SetScanResult("", "", kScanError);
EXPECT_EQ(kScanError,
RunFunctionAndReturnError("[{\"mimeTypes\": [\"img/tempera\"]}]"));
} }
TEST_F(DocumentScanScanFunctionTest, Success) { TEST_F(DocumentScanScanFunctionTest, Success) {
std::vector<DocumentScanInterface::ScannerDescription> scanner_list; GetLorgnetteManagerClient()->SetListScannersResponse(
scanner_list.push_back(DocumentScanInterface::ScannerDescription()); CreateListScannersResponse());
document_scan_interface_->SetListScannersResult(scanner_list, ""); GetLorgnetteManagerClient()->SetScanImageToStringResponse("PrettyPicture");
const char kScanData[] = "A beautiful picture"; std::unique_ptr<base::DictionaryValue> result(RunFunctionAndReturnDictionary(
const char kMimeType[] = "img/encaustic"; function_.get(), "[{\"mimeTypes\": [\"image/png\"]}]"));
document_scan_interface_->SetScanResult(kScanData, kMimeType, "");
function_->set_user_gesture(true);
std::unique_ptr<base::DictionaryValue> result(
RunFunctionAndReturnDictionary(function_.get(), "[{}]"));
ASSERT_NE(nullptr, result.get()); ASSERT_NE(nullptr, result.get());
document_scan::ScanResults scan_results; document_scan::ScanResults scan_results;
EXPECT_TRUE(document_scan::ScanResults::Populate(*result, &scan_results)); EXPECT_TRUE(document_scan::ScanResults::Populate(*result, &scan_results));
EXPECT_THAT(scan_results.data_urls, testing::ElementsAre(kScanData)); // Verify the image data URL is the PNG image data URL prefix plus the base64
EXPECT_EQ(kMimeType, scan_results.mime_type); // representation of "PrettyPicture".
EXPECT_THAT(
scan_results.data_urls,
testing::ElementsAre("data:image/png;base64,UHJldHR5UGljdHVyZQ=="));
EXPECT_EQ("image/png", scan_results.mime_type);
} }
} // namespace api } // namespace api
} // namespace extensions } // namespace extensions
// Copyright 2014 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 "extensions/browser/api/document_scan/document_scan_interface.h"
namespace extensions {
namespace api {
DocumentScanInterface::DocumentScanInterface() {
}
DocumentScanInterface::~DocumentScanInterface() {
}
DocumentScanInterface::ScannerDescription::ScannerDescription() {
}
DocumentScanInterface::ScannerDescription::ScannerDescription(
const ScannerDescription& other) = default;
DocumentScanInterface::ScannerDescription::~ScannerDescription() {
}
} // namespace api
} // namespace extensions
// Copyright 2014 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.
#ifndef EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_DOCUMENT_SCAN_INTERFACE_H_
#define EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_DOCUMENT_SCAN_INTERFACE_H_
#include <memory>
#include <string>
#include <vector>
#include "base/callback.h"
namespace extensions {
namespace api {
class DocumentScanInterface {
public:
struct ScannerDescription {
ScannerDescription();
ScannerDescription(const ScannerDescription& other);
~ScannerDescription();
std::string name;
std::string manufacturer;
std::string model;
std::string scanner_type;
std::string image_mime_type;
};
enum ScanMode { kScanModeColor, kScanModeGray, kScanModeLineart };
using ListScannersResultsCallback = base::OnceCallback<void(
const std::vector<ScannerDescription>& scanner_descriptions,
const std::string& error)>;
using ScanResultsCallback =
base::OnceCallback<void(const std::string& scanned_image,
const std::string& mime_type,
const std::string& error)>;
virtual ~DocumentScanInterface();
virtual void Scan(const std::string& scanner_name,
ScanMode mode,
int resolution_dpi,
ScanResultsCallback callback) = 0;
virtual void ListScanners(ListScannersResultsCallback callback) = 0;
// Creates a platform-specific DocumentScanInterface instance.
static DocumentScanInterface* CreateInstance();
protected:
DocumentScanInterface();
};
} // namespace api
} // namespace extensions
#endif // EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_DOCUMENT_SCAN_INTERFACE_H_
// Copyright 2014 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 "extensions/browser/api/document_scan/document_scan_interface_chromeos.h"
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/bind.h"
#include "base/logging.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/lorgnette_manager_client.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace {
constexpr char kImageScanFailedError[] = "Image scan failed";
constexpr char kScannerImageMimeTypePng[] = "image/png";
constexpr char kPngImageDataUrlPrefix[] = "data:image/png;base64,";
chromeos::LorgnetteManagerClient* GetLorgnetteManagerClient() {
DCHECK(chromeos::DBusThreadManager::IsInitialized());
return chromeos::DBusThreadManager::Get()->GetLorgnetteManagerClient();
}
} // namespace
namespace extensions {
namespace api {
DocumentScanInterfaceChromeos::DocumentScanInterfaceChromeos() = default;
DocumentScanInterfaceChromeos::~DocumentScanInterfaceChromeos() = default;
void DocumentScanInterfaceChromeos::ListScanners(
ListScannersResultsCallback callback) {
GetLorgnetteManagerClient()->ListScanners(
base::BindOnce(&DocumentScanInterfaceChromeos::OnScannerListReceived,
base::Unretained(this), std::move(callback)));
}
void DocumentScanInterfaceChromeos::OnScannerListReceived(
ListScannersResultsCallback callback,
base::Optional<lorgnette::ListScannersResponse> response) {
std::vector<ScannerDescription> scanner_descriptions;
if (response) {
for (const auto& scanner : response->scanners()) {
ScannerDescription description;
description.name = scanner.name();
description.manufacturer = scanner.manufacturer();
description.model = scanner.model();
description.scanner_type = scanner.type();
description.image_mime_type = kScannerImageMimeTypePng;
scanner_descriptions.push_back(description);
}
}
const std::string kNoError;
std::move(callback).Run(scanner_descriptions, kNoError);
}
void DocumentScanInterfaceChromeos::Scan(const std::string& scanner_name,
ScanMode mode,
int resolution_dpi,
ScanResultsCallback callback) {
VLOG(1) << "Choosing scanner " << scanner_name;
chromeos::LorgnetteManagerClient::ScanProperties properties;
switch (mode) {
case kScanModeColor:
properties.mode = lorgnette::kScanPropertyModeColor;
break;
case kScanModeGray:
properties.mode = lorgnette::kScanPropertyModeGray;
break;
case kScanModeLineart:
properties.mode = lorgnette::kScanPropertyModeLineart;
break;
}
if (resolution_dpi != 0) {
properties.resolution_dpi = resolution_dpi;
}
GetLorgnetteManagerClient()->ScanImageToString(
scanner_name, properties,
base::BindOnce(&DocumentScanInterfaceChromeos::OnScanCompleted,
base::Unretained(this), std::move(callback)));
}
void DocumentScanInterfaceChromeos::OnScanCompleted(
ScanResultsCallback callback,
base::Optional<std::string> image_data) {
VLOG(1) << "ScanImage returns " << image_data.has_value();
if (!image_data.has_value()) {
std::move(callback).Run(std::string(), std::string(),
kImageScanFailedError);
return;
}
std::string image_base64;
base::Base64Encode(image_data.value(), &image_base64);
std::move(callback).Run(kPngImageDataUrlPrefix + image_base64,
kScannerImageMimeTypePng, std::string() /* error */);
}
// static
DocumentScanInterface* DocumentScanInterface::CreateInstance() {
return new DocumentScanInterfaceChromeos();
}
} // namespace api
} // namespace extensions
// Copyright 2014 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.
#ifndef EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_DOCUMENT_SCAN_INTERFACE_CHROMEOS_H_
#define EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_DOCUMENT_SCAN_INTERFACE_CHROMEOS_H_
#include <string>
#include "base/optional.h"
#include "chromeos/dbus/lorgnette/lorgnette_service.pb.h"
#include "extensions/browser/api/document_scan/document_scan_interface.h"
namespace extensions {
namespace api {
class DocumentScanInterfaceChromeos : public DocumentScanInterface {
public:
DocumentScanInterfaceChromeos();
DocumentScanInterfaceChromeos(const DocumentScanInterfaceChromeos&) = delete;
DocumentScanInterfaceChromeos& operator=(
const DocumentScanInterfaceChromeos&) = delete;
~DocumentScanInterfaceChromeos() override;
// DocumentScanInterface:
void ListScanners(ListScannersResultsCallback callback) override;
void Scan(const std::string& scanner_name,
ScanMode mode,
int resolution_dpi,
ScanResultsCallback callback) override;
private:
void OnScannerListReceived(
ListScannersResultsCallback callback,
base::Optional<lorgnette::ListScannersResponse> response);
void OnScanCompleted(ScanResultsCallback callback,
base::Optional<std::string> image_data);
};
} // namespace api
} // namespace extensions
#endif // EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_DOCUMENT_SCAN_INTERFACE_CHROMEOS_H_
// Copyright 2014 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 "extensions/browser/api/document_scan/document_scan_interface_chromeos.h"
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_lorgnette_manager_client.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace extensions {
namespace api {
// Tests of networking_private_crypto support for Networking Private API.
class DocumentScanInterfaceChromeosTest : public testing::Test {
public:
DocumentScanInterfaceChromeosTest() = default;
~DocumentScanInterfaceChromeosTest() override = default;
void SetUp() override { chromeos::DBusThreadManager::Initialize(); }
void TearDown() override { chromeos::DBusThreadManager::Shutdown(); }
chromeos::FakeLorgnetteManagerClient* GetLorgnetteManagerClient() {
return static_cast<chromeos::FakeLorgnetteManagerClient*>(
chromeos::DBusThreadManager::Get()->GetLorgnetteManagerClient());
}
protected:
base::test::TaskEnvironment task_environment_;
DocumentScanInterfaceChromeos scan_interface_;
};
TEST_F(DocumentScanInterfaceChromeosTest, ListScanners) {
// Use constexpr const char* instead of constexpr char[] because implicit
// conversion in lambda doesn't work.
constexpr const char* kScannerName = "Monet";
constexpr const char* kScannerManufacturer = "Jacques-Louis David";
constexpr const char* kScannerModel = "Le Havre";
constexpr const char* kScannerType = "Impressionism";
lorgnette::ScannerInfo scanner;
scanner.set_name(kScannerName);
scanner.set_manufacturer(kScannerManufacturer);
scanner.set_model(kScannerModel);
scanner.set_type(kScannerType);
lorgnette::ListScannersResponse response;
*response.add_scanners() = std::move(scanner);
GetLorgnetteManagerClient()->SetListScannersResponse(response);
base::RunLoop run_loop;
scan_interface_.ListScanners(base::BindOnce(
[](base::RunLoop* run_loop,
const std::vector<DocumentScanInterface::ScannerDescription>&
descriptions,
const std::string& error) {
run_loop->Quit();
ASSERT_EQ(1u, descriptions.size());
// Wrap by std::string explicitly, because const reference of the
// constexpr in the enclosing scope, which EXPECT_EQ macro uses,
// cannot be taken.
EXPECT_EQ(std::string(kScannerName), descriptions[0].name);
EXPECT_EQ(std::string(kScannerManufacturer),
descriptions[0].manufacturer);
EXPECT_EQ(std::string(kScannerModel), descriptions[0].model);
EXPECT_EQ(std::string(kScannerType), descriptions[0].scanner_type);
EXPECT_EQ("image/png", descriptions[0].image_mime_type);
EXPECT_EQ("", error);
},
&run_loop));
run_loop.Run();
}
TEST_F(DocumentScanInterfaceChromeosTest, ScanFailure) {
base::RunLoop run_loop;
scan_interface_.Scan(
"Monet", DocumentScanInterface::kScanModeColor, 4096,
base::BindOnce(
[](base::RunLoop* run_loop, const std::string& scanned_image,
const std::string& mime_type, const std::string& error) {
run_loop->Quit();
EXPECT_EQ("", scanned_image);
EXPECT_EQ("", mime_type);
EXPECT_EQ("Image scan failed", error);
},
&run_loop));
run_loop.Run();
}
TEST_F(DocumentScanInterfaceChromeosTest, ScanSuccess) {
constexpr char kScannerName[] = "Monet";
constexpr int kResolution = 4096;
GetLorgnetteManagerClient()->AddScanData(
kScannerName,
chromeos::LorgnetteManagerClient::ScanProperties{
lorgnette::kScanPropertyModeColor, kResolution},
"PrettyPicture");
base::RunLoop run_loop;
scan_interface_.Scan(
kScannerName, DocumentScanInterface::kScanModeColor, kResolution,
base::BindOnce(
[](base::RunLoop* run_loop, const std::string& scanned_image,
const std::string& mime_type, const std::string& error) {
run_loop->Quit();
// Data URL plus base64 representation of "PrettyPicture".
EXPECT_EQ("data:image/png;base64,UHJldHR5UGljdHVyZQ==",
scanned_image);
EXPECT_EQ("image/png", mime_type);
EXPECT_EQ("", error);
},
&run_loop));
run_loop.Run();
}
} // namespace api
} // namespace extensions
// Copyright 2014 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 <utility>
#include "extensions/browser/api/document_scan/document_scan_interface.h"
namespace {
const char kScanFunctionNotImplementedError[] = "Scan function not implemented";
} // namespace
namespace extensions {
namespace api {
class DocumentScanInterfaceImpl : public DocumentScanInterface {
public:
DocumentScanInterfaceImpl() = default;
~DocumentScanInterfaceImpl() override = default;
DocumentScanInterfaceImpl(const DocumentScanInterfaceImpl&) = delete;
DocumentScanInterfaceImpl& operator=(const DocumentScanInterfaceImpl&) =
delete;
void ListScanners(ListScannersResultsCallback callback) override {
std::move(callback).Run(std::vector<ScannerDescription>(), "");
}
void Scan(const std::string& scanner_name,
ScanMode mode,
int resolution_dpi,
ScanResultsCallback callback) override {
std::move(callback).Run("", "", kScanFunctionNotImplementedError);
}
};
// static
DocumentScanInterface* DocumentScanInterface::CreateInstance() {
return new DocumentScanInterfaceImpl();
}
} // namespace api
} // namespace extensions
// Copyright 2019 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 "extensions/browser/api/document_scan/fake_document_scan_interface.h"
#include <utility>
namespace extensions {
namespace api {
FakeDocumentScanInterface::FakeDocumentScanInterface() = default;
FakeDocumentScanInterface::~FakeDocumentScanInterface() = default;
void FakeDocumentScanInterface::SetListScannersResult(
const std::vector<ScannerDescription>& scanner_descriptions,
const std::string& error) {
scanner_descriptions_ = scanner_descriptions;
error_ = error;
}
void FakeDocumentScanInterface::SetScanResult(const std::string& scanned_image,
const std::string& mime_type,
const std::string& error) {
scanned_image_ = scanned_image;
mime_type_ = mime_type;
error_ = error;
}
void FakeDocumentScanInterface::ListScanners(
ListScannersResultsCallback callback) {
std::move(callback).Run(scanner_descriptions_, error_);
}
void FakeDocumentScanInterface::Scan(const std::string& scanner_name,
ScanMode mode,
int resolution_dpi,
ScanResultsCallback callback) {
std::move(callback).Run(scanned_image_, mime_type_, error_);
}
} // namespace api
} // namespace extensions
// Copyright 2019 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.
#ifndef EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_FAKE_DOCUMENT_SCAN_INTERFACE_H_
#define EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_FAKE_DOCUMENT_SCAN_INTERFACE_H_
#include <string>
#include "extensions/browser/api/document_scan/document_scan_interface.h"
namespace extensions {
namespace api {
class FakeDocumentScanInterface : public DocumentScanInterface {
public:
FakeDocumentScanInterface();
~FakeDocumentScanInterface() override;
void SetListScannersResult(
const std::vector<ScannerDescription>& scanner_descriptions,
const std::string& error);
void SetScanResult(const std::string& scanned_image,
const std::string& mime_type,
const std::string& error);
// DocumentScanInterface:
void ListScanners(ListScannersResultsCallback callback) override;
void Scan(const std::string& scanner_name,
ScanMode mode,
int resolution_dpi,
ScanResultsCallback callback) override;
private:
std::vector<ScannerDescription> scanner_descriptions_;
std::string scanned_image_;
std::string mime_type_;
std::string error_;
};
} // namespace api
} // namespace extensions
#endif // EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_FAKE_DOCUMENT_SCAN_INTERFACE_H_
...@@ -21,7 +21,6 @@ extensions_api_schema_files_ = [ ...@@ -21,7 +21,6 @@ extensions_api_schema_files_ = [
"declarative_net_request.idl", "declarative_net_request.idl",
"display_source.idl", "display_source.idl",
"dns.idl", "dns.idl",
"document_scan.idl",
"events.json", "events.json",
"extensions_manifest_types.json", "extensions_manifest_types.json",
"extension_options_internal.idl", "extension_options_internal.idl",
...@@ -65,6 +64,7 @@ if (is_chromeos) { ...@@ -65,6 +64,7 @@ if (is_chromeos) {
extensions_api_schema_files_ += [ extensions_api_schema_files_ += [
"crash_report_private.idl", "crash_report_private.idl",
"diagnostics.idl", "diagnostics.idl",
"document_scan.idl",
"lock_screen_data.idl", "lock_screen_data.idl",
"media_perception_private.idl", "media_perception_private.idl",
"networking_config.idl", "networking_config.idl",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment