diff options
author | sudarsana.nagineni <sudarsana.nagineni@intel.com> | 2015-02-09 06:12:47 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-09 14:13:16 +0000 |
commit | dc80f4b7a1e17819a54b32a246f40766335cdff0 (patch) | |
tree | 1cf64907a8785a947bb69677e6c77562896a3fe1 /extensions | |
parent | 0d3f0b88b3700e1979d0b7af999269e58261c9a2 (diff) | |
download | chromium_src-dc80f4b7a1e17819a54b32a246f40766335cdff0.zip chromium_src-dc80f4b7a1e17819a54b32a246f40766335cdff0.tar.gz chromium_src-dc80f4b7a1e17819a54b32a246f40766335cdff0.tar.bz2 |
Move chrome.documentScan API to extensions/.
Move the chrome.documentScan extension API from chrome/ to extensions/.
BUG=455695
Review URL: https://codereview.chromium.org/899523004
Cr-Commit-Position: refs/heads/master@{#315293}
Diffstat (limited to 'extensions')
20 files changed, 866 insertions, 1 deletions
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn index b29dea2..e8c1b5a 100644 --- a/extensions/browser/BUILD.gn +++ b/extensions/browser/BUILD.gn @@ -141,6 +141,10 @@ source_set("browser") { "api/dns/dns_api.h", "api/dns/host_resolver_wrapper.cc", "api/dns/host_resolver_wrapper.h", + "api/document_scan/document_scan_api.cc", + "api/document_scan/document_scan_api.h", + "api/document_scan/document_scan_interface.cc", + "api/document_scan/document_scan_interface.h", "api/execute_code_function.cc", "api/execute_code_function.h", "api/extensions_api_client.cc", @@ -581,6 +585,7 @@ source_set("browser") { "api/diagnostics/diagnostics_api.cc", "api/diagnostics/diagnostics_api.h", "api/diagnostics/diagnostics_api_chromeos.cc", + "api/document_scan/document_scan_interface_chromeos.cc", "api/networking_config/networking_config_api.cc", "api/networking_config/networking_config_api.h", "api/networking_config/networking_config_service.cc", @@ -593,6 +598,8 @@ source_set("browser") { "api/vpn_provider/vpn_service.h", "api/vpn_provider/vpn_service_factory.h", ] + } else { + sources += [ "api/document_scan/document_scan_interface_nonchromeos.cc" ] } if (is_desktop_linux) { diff --git a/extensions/browser/api/document_scan/DEPS b/extensions/browser/api/document_scan/DEPS new file mode 100644 index 0000000..a0e2dba --- /dev/null +++ b/extensions/browser/api/document_scan/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+third_party/cros_system_api", +] diff --git a/extensions/browser/api/document_scan/document_scan_api.cc b/extensions/browser/api/document_scan/document_scan_api.cc new file mode 100644 index 0000000..464b7db --- /dev/null +++ b/extensions/browser/api/document_scan/document_scan_api.cc @@ -0,0 +1,124 @@ +// 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_api.h" + +#include <algorithm> + +#include "content/public/browser/browser_thread.h" +#include "extensions/browser/extension_system.h" + +using content::BrowserThread; + +namespace { + +const char kScannerNotAvailable[] = "Scanner not available"; +const char kUserGestureRequiredError[] = + "User gesture required to perform scan"; + +} // namespace + +namespace extensions { + +namespace core_api { + +DocumentScanScanFunction::DocumentScanScanFunction() + : document_scan_interface_(DocumentScanInterface::CreateInstance()) { +} + +DocumentScanScanFunction::~DocumentScanScanFunction() { +} + +bool DocumentScanScanFunction::Prepare() { + set_work_thread_id(BrowserThread::FILE); + params_ = document_scan::Scan::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(params_.get()); + return true; +} + +void DocumentScanScanFunction::AsyncWorkStart() { + if (!user_gesture()) { + error_ = kUserGestureRequiredError; + AsyncWorkCompleted(); + return; + } + + // Add a reference, which is balanced in OnScannerListReceived to keep the + // object around and allow the callback to be invoked. + AddRef(); + + document_scan_interface_->ListScanners( + base::Bind(&DocumentScanScanFunction::OnScannerListReceived, + base::Unretained(this))); +} + +void DocumentScanScanFunction::OnScannerListReceived( + const std::vector<DocumentScanInterface::ScannerDescription>& + scanner_descriptions, + const std::string& error) { + std::vector<DocumentScanInterface::ScannerDescription>::const_iterator + scanner_i = scanner_descriptions.begin(); + + // 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.get(); + for (; scanner_i != scanner_descriptions.end(); ++scanner_i) { + if (std::find(mime_types.begin(), mime_types.end(), + scanner_i->image_mime_type) != mime_types.end()) { + break; + } + } + } + + if (scanner_i == scanner_descriptions.end()) { + error_ = kScannerNotAvailable; + AsyncWorkCompleted(); + + // Balance the AddRef in AsyncWorkStart(). + Release(); + return; + } + + // TODO(pstew): Call a delegate method here to select a scanner and options. + + document_scan_interface_->Scan( + scanner_i->name, DocumentScanInterface::kScanModeColor, 0, + base::Bind(&DocumentScanScanFunction::OnResultsReceived, + base::Unretained(this))); +} + +void DocumentScanScanFunction::OnResultsReceived( + const std::string& scanned_image, + const std::string& mime_type, + const std::string& error) { + // 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 (error.empty()) { + document_scan::ScanResults scan_results; + if (!scanned_image.empty()) { + scan_results.data_urls.push_back(scanned_image); + } + scan_results.mime_type = mime_type; + results_ = document_scan::Scan::Results::Create(scan_results); + } + error_ = error; + AsyncWorkCompleted(); + + // Balance the AddRef in AsyncWorkStart(). + Release(); +} + +bool DocumentScanScanFunction::Respond() { + return error_.empty(); +} + +} // namespace core_api + +} // namespace extensions diff --git a/extensions/browser/api/document_scan/document_scan_api.h b/extensions/browser/api/document_scan/document_scan_api.h new file mode 100644 index 0000000..806d768 --- /dev/null +++ b/extensions/browser/api/document_scan/document_scan_api.h @@ -0,0 +1,54 @@ +// 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_API_H_ +#define EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_DOCUMENT_SCAN_API_H_ + +#include <string> +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "extensions/browser/api/async_api_function.h" +#include "extensions/browser/api/document_scan/document_scan_interface.h" +#include "extensions/common/api/document_scan.h" + +namespace extensions { + +namespace core_api { + +class DocumentScanScanFunction : public AsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("documentScan.scan", DOCUMENT_SCAN_SCAN) + DocumentScanScanFunction(); + + protected: + ~DocumentScanScanFunction() override; + + // AsyncApiFunction: + bool Prepare() override; + void AsyncWorkStart() override; + bool Respond() override; + + private: + friend class DocumentScanScanFunctionTest; + + void OnScannerListReceived( + const std::vector<DocumentScanInterface::ScannerDescription>& + scanner_descriptions, + const std::string& error); + void OnResultsReceived(const std::string& scanned_image, + const std::string& mime_type, + const std::string& error); + + scoped_ptr<document_scan::Scan::Params> params_; + scoped_ptr<DocumentScanInterface> document_scan_interface_; + + DISALLOW_COPY_AND_ASSIGN(DocumentScanScanFunction); +}; + +} // namespace core_api + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_DOCUMENT_SCAN_API_H_ diff --git a/extensions/browser/api/document_scan/document_scan_api_unittest.cc b/extensions/browser/api/document_scan/document_scan_api_unittest.cc new file mode 100644 index 0000000..1c8f597 --- /dev/null +++ b/extensions/browser/api/document_scan/document_scan_api_unittest.cc @@ -0,0 +1,120 @@ +// 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_api.h" + +#include <string> +#include <vector> + +#include "extensions/browser/api/document_scan/mock_document_scan_interface.h" +#include "extensions/browser/api_test_utils.h" +#include "extensions/browser/api_unittest.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; + +namespace extensions { + +namespace core_api { + +// Tests of networking_private_crypto support for Networking Private API. +class DocumentScanScanFunctionTest : public ApiUnitTest { + public: + DocumentScanScanFunctionTest() + : function_(new DocumentScanScanFunction()), + document_scan_interface_(new MockDocumentScanInterface()) {} + ~DocumentScanScanFunctionTest() override {} + + void SetUp() override { + ApiUnitTest::SetUp(); + // Passes ownership. + function_->document_scan_interface_.reset(document_scan_interface_); + } + + protected: + std::string RunFunctionAndReturnError(const std::string& args) { + function_->set_extension(extension()); + std::string error = api_test_utils::RunFunctionAndReturnError( + function_, args, browser_context(), api_test_utils::NONE); + return error; + } + + DocumentScanScanFunction* function_; + MockDocumentScanInterface* document_scan_interface_; // Owned by function_. +}; + +ACTION_P2(InvokeListScannersCallback, scanner_list, error) { + ::std::tr1::get<0>(args).Run(scanner_list, error); +} + +ACTION_P3(InvokeScanCallback, data, mime_type, error) { + ::std::tr1::get<3>(args).Run(data, mime_type, error); +} + +TEST_F(DocumentScanScanFunctionTest, GestureRequired) { + EXPECT_EQ("User gesture required to perform scan", + RunFunctionAndReturnError("[{}]")); +} + +TEST_F(DocumentScanScanFunctionTest, NoScanners) { + function_->set_user_gesture(true); + EXPECT_CALL(*document_scan_interface_, ListScanners(_)) + .WillOnce(InvokeListScannersCallback( + std::vector<DocumentScanInterface::ScannerDescription>(), "")); + EXPECT_EQ("Scanner not available", RunFunctionAndReturnError("[{}]")); +} + +TEST_F(DocumentScanScanFunctionTest, NoMatchingScanners) { + function_->set_user_gesture(true); + std::vector<DocumentScanInterface::ScannerDescription> scanner_list; + DocumentScanInterface::ScannerDescription scanner; + scanner.image_mime_type = "img/fresco"; + scanner_list.push_back(scanner); + EXPECT_CALL(*document_scan_interface_, ListScanners(_)) + .WillOnce(InvokeListScannersCallback(scanner_list, "")); + EXPECT_EQ( + "Scanner not available", + RunFunctionAndReturnError("[{\"mimeTypes\": [\"img/silverpoint\"]}]")); +} + +TEST_F(DocumentScanScanFunctionTest, ScanFailure) { + function_->set_user_gesture(true); + std::vector<DocumentScanInterface::ScannerDescription> scanner_list; + DocumentScanInterface::ScannerDescription scanner; + const char kMimeType[] = "img/tempera"; + const char kScannerName[] = "Michelangelo"; + scanner.name = kScannerName; + scanner.image_mime_type = kMimeType; + scanner_list.push_back(scanner); + EXPECT_CALL(*document_scan_interface_, ListScanners(_)) + .WillOnce(InvokeListScannersCallback(scanner_list, "")); + const char kScanError[] = "Someone ate all the eggs"; + EXPECT_CALL(*document_scan_interface_, Scan(kScannerName, _, _, _)) + .WillOnce(InvokeScanCallback("", "", kScanError)); + EXPECT_EQ(kScanError, + RunFunctionAndReturnError("[{\"mimeTypes\": [\"img/tempera\"]}]")); +} + +TEST_F(DocumentScanScanFunctionTest, Success) { + std::vector<DocumentScanInterface::ScannerDescription> scanner_list; + scanner_list.push_back(DocumentScanInterface::ScannerDescription()); + EXPECT_CALL(*document_scan_interface_, ListScanners(_)) + .WillOnce(InvokeListScannersCallback(scanner_list, "")); + const char kScanData[] = "A beautiful picture"; + const char kMimeType[] = "img/encaustic"; + EXPECT_CALL(*document_scan_interface_, Scan(_, _, _, _)) + .WillOnce(InvokeScanCallback(kScanData, kMimeType, "")); + function_->set_user_gesture(true); + scoped_ptr<base::DictionaryValue> result( + RunFunctionAndReturnDictionary(function_, "[{}]")); + ASSERT_NE(nullptr, result.get()); + document_scan::ScanResults scan_results; + EXPECT_TRUE(document_scan::ScanResults::Populate(*result, &scan_results)); + EXPECT_THAT(scan_results.data_urls, testing::ElementsAre(kScanData)); + EXPECT_EQ(kMimeType, scan_results.mime_type); +} + +} // namespace core_api + +} // namespace extensions diff --git a/extensions/browser/api/document_scan/document_scan_interface.cc b/extensions/browser/api/document_scan/document_scan_interface.cc new file mode 100644 index 0000000..2a599a3 --- /dev/null +++ b/extensions/browser/api/document_scan/document_scan_interface.cc @@ -0,0 +1,25 @@ +// 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 core_api { + +DocumentScanInterface::DocumentScanInterface() { +} + +DocumentScanInterface::~DocumentScanInterface() { +} + +DocumentScanInterface::ScannerDescription::ScannerDescription() { +} + +DocumentScanInterface::ScannerDescription::~ScannerDescription() { +} + +} // namespace core_api + +} // namespace extensions diff --git a/extensions/browser/api/document_scan/document_scan_interface.h b/extensions/browser/api/document_scan/document_scan_interface.h new file mode 100644 index 0000000..9b8699d --- /dev/null +++ b/extensions/browser/api/document_scan/document_scan_interface.h @@ -0,0 +1,59 @@ +// 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 <string> +#include <vector> + +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" + +namespace extensions { + +namespace core_api { + +class DocumentScanInterface { + public: + struct ScannerDescription { + ScannerDescription(); + ~ScannerDescription(); + std::string name; + std::string manufacturer; + std::string model; + std::string scanner_type; + std::string image_mime_type; + }; + + enum ScanMode { kScanModeColor, kScanModeGray, kScanModeLineart }; + + typedef base::Callback<void( + const std::vector<ScannerDescription>& scanner_descriptions, + const std::string& error)> ListScannersResultsCallback; + + typedef base::Callback<void(const std::string& scanned_image, + const std::string& mime_type, + const std::string& error)> ScanResultsCallback; + + virtual ~DocumentScanInterface(); + + virtual void Scan(const std::string& scanner_name, + ScanMode mode, + int resolution_dpi, + const ScanResultsCallback& callback) = 0; + virtual void ListScanners(const ListScannersResultsCallback& callback) = 0; + + // Creates a platform-specific DocumentScanInterface instance. + static DocumentScanInterface* CreateInstance(); + + protected: + DocumentScanInterface(); +}; + +} // namespace core_api + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_DOCUMENT_SCAN_INTERFACE_H_ diff --git a/extensions/browser/api/document_scan/document_scan_interface_chromeos.cc b/extensions/browser/api/document_scan/document_scan_interface_chromeos.cc new file mode 100644 index 0000000..b270a3f --- /dev/null +++ b/extensions/browser/api/document_scan/document_scan_interface_chromeos.cc @@ -0,0 +1,131 @@ +// 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 "base/base64.h" +#include "base/bind.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 { + +const char kImageScanFailedError[] = "Image scan failed"; +const char kScannerImageMimeTypePng[] = "image/png"; +const char kPngImageDataUrlPrefix[] = "data:image/png;base64,"; + +} // namespace + +namespace extensions { + +namespace core_api { + +DocumentScanInterfaceChromeos::DocumentScanInterfaceChromeos() + : lorgnette_manager_client_(nullptr) { +} + +DocumentScanInterfaceChromeos::~DocumentScanInterfaceChromeos() { +} + +void DocumentScanInterfaceChromeos::ListScanners( + const ListScannersResultsCallback& callback) { + GetLorgnetteManagerClient()->ListScanners( + base::Bind(&DocumentScanInterfaceChromeos::OnScannerListReceived, + base::Unretained(this), callback)); +} + +void DocumentScanInterfaceChromeos::OnScannerListReceived( + const ListScannersResultsCallback& callback, + bool succeeded, + const chromeos::LorgnetteManagerClient::ScannerTable& scanners) { + std::vector<ScannerDescription> scanner_descriptions; + for (const auto& scanner : scanners) { + ScannerDescription description; + description.name = scanner.first; + const auto& entry = scanner.second; + auto info_it = entry.find(lorgnette::kScannerPropertyManufacturer); + if (info_it != entry.end()) { + description.manufacturer = info_it->second; + } + info_it = entry.find(lorgnette::kScannerPropertyModel); + if (info_it != entry.end()) { + description.model = info_it->second; + } + info_it = entry.find(lorgnette::kScannerPropertyType); + if (info_it != entry.end()) { + description.scanner_type = info_it->second; + } + description.image_mime_type = kScannerImageMimeTypePng; + scanner_descriptions.push_back(description); + } + const std::string kNoError; + callback.Run(scanner_descriptions, kNoError); +} + +void DocumentScanInterfaceChromeos::Scan(const std::string& scanner_name, + ScanMode mode, + int resolution_dpi, + const 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::Bind(&DocumentScanInterfaceChromeos::OnScanCompleted, + base::Unretained(this), callback)); +} + +void DocumentScanInterfaceChromeos::OnScanCompleted( + const ScanResultsCallback& callback, + bool succeeded, + const std::string& image_data) { + VLOG(1) << "ScanImage returns " << succeeded; + std::string error_string; + if (!succeeded) { + error_string = kImageScanFailedError; + } + + std::string image_base64; + base::Base64Encode(image_data, &image_base64); + std::string image_url(std::string(kPngImageDataUrlPrefix) + image_base64); + + callback.Run(image_url, kScannerImageMimeTypePng, error_string); +} + +chromeos::LorgnetteManagerClient* +DocumentScanInterfaceChromeos::GetLorgnetteManagerClient() { + if (!lorgnette_manager_client_) { + lorgnette_manager_client_ = + chromeos::DBusThreadManager::Get()->GetLorgnetteManagerClient(); + CHECK(lorgnette_manager_client_); + } + return lorgnette_manager_client_; +} + +// static +DocumentScanInterface* DocumentScanInterface::CreateInstance() { + return new DocumentScanInterfaceChromeos(); +} + +} // namespace core_api + +} // namespace extensions diff --git a/extensions/browser/api/document_scan/document_scan_interface_chromeos.h b/extensions/browser/api/document_scan/document_scan_interface_chromeos.h new file mode 100644 index 0000000..cd50d50 --- /dev/null +++ b/extensions/browser/api/document_scan/document_scan_interface_chromeos.h @@ -0,0 +1,48 @@ +// 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 "extensions/browser/api/document_scan/document_scan_interface.h" +#include "chromeos/dbus/lorgnette_manager_client.h" + +namespace extensions { + +namespace core_api { + +class DocumentScanInterfaceChromeos : public DocumentScanInterface { + public: + DocumentScanInterfaceChromeos(); + ~DocumentScanInterfaceChromeos() override; + + void ListScanners(const ListScannersResultsCallback& callback) override; + void Scan(const std::string& scanner_name, + ScanMode mode, + int resolution_dpi, + const ScanResultsCallback& callback) override; + + private: + friend class DocumentScanInterfaceChromeosTest; + + void OnScannerListReceived( + const ListScannersResultsCallback& callback, + bool succeeded, + const chromeos::LorgnetteManagerClient::ScannerTable& scanners); + void OnScanCompleted(const ScanResultsCallback& callback, + bool succeeded, + const std::string& image_data); + chromeos::LorgnetteManagerClient* GetLorgnetteManagerClient(); + + // Guaranteed to outlive |this|. + chromeos::LorgnetteManagerClient* lorgnette_manager_client_; + + DISALLOW_COPY_AND_ASSIGN(DocumentScanInterfaceChromeos); +}; + +} // namespace core_api + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_DOCUMENT_SCAN_INTERFACE_CHROMEOS_H_ diff --git a/extensions/browser/api/document_scan/document_scan_interface_chromeos_unittest.cc b/extensions/browser/api/document_scan/document_scan_interface_chromeos_unittest.cc new file mode 100644 index 0000000..e34de36 --- /dev/null +++ b/extensions/browser/api/document_scan/document_scan_interface_chromeos_unittest.cc @@ -0,0 +1,127 @@ +// 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 "chromeos/dbus/mock_lorgnette_manager_client.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +using testing::_; + +namespace extensions { + +namespace core_api { + +// Tests of networking_private_crypto support for Networking Private API. +class DocumentScanInterfaceChromeosTest : public testing::Test { + public: + DocumentScanInterfaceChromeosTest() + : client_(new chromeos::MockLorgnetteManagerClient()) {} + ~DocumentScanInterfaceChromeosTest() override {} + + void SetUp() override { + scan_interface_.lorgnette_manager_client_ = client_.get(); + } + + MOCK_METHOD2(OnListScannersResultReceived, + void(const std::vector< + DocumentScanInterface::ScannerDescription>& scanners, + const std::string& error)); + + MOCK_METHOD3(OnScanCompleted, + void(const std::string& scanned_image, + const std::string& mime_type, + const std::string& error)); + + protected: + DocumentScanInterfaceChromeos scan_interface_; + scoped_ptr<chromeos::MockLorgnetteManagerClient> client_; +}; + +ACTION_P2(InvokeListScannersCallback, scanner_list, error) { + ::std::tr1::get<0>(args).Run(scanner_list, error); +} + +ACTION_P2(InvokeScanCallback, succeeded, image_data) { + ::std::tr1::get<2>(args).Run(succeeded, image_data); +} + +MATCHER_P5(IsScannerDescription, name, manufacturer, model, type, mime, "") { + return arg.name == name && arg.manufacturer == manufacturer && + arg.model == model && arg.scanner_type == type && + arg.image_mime_type == mime; +} + +MATCHER_P2(IsScannerProperties, mode, resolution, "") { + return arg.mode == mode && arg.resolution_dpi == resolution; +} + +TEST_F(DocumentScanInterfaceChromeosTest, ListScanners) { + chromeos::LorgnetteManagerClient::ScannerTable scanners; + const char kScannerName[] = "Monet"; + chromeos::LorgnetteManagerClient::ScannerTableEntry entry; + const char kScannerManufacturer[] = "Jacques-Louis David"; + entry[lorgnette::kScannerPropertyManufacturer] = kScannerManufacturer; + const char kScannerModel[] = "Le Havre"; + entry[lorgnette::kScannerPropertyModel] = kScannerModel; + const char kScannerType[] = "Impressionism"; + entry[lorgnette::kScannerPropertyType] = kScannerType; + scanners[kScannerName] = entry; + EXPECT_CALL(*client_, ListScanners(_)) + .WillOnce(InvokeListScannersCallback(true, scanners)); + EXPECT_CALL(*this, OnListScannersResultReceived( + testing::ElementsAre(IsScannerDescription( + kScannerName, kScannerManufacturer, kScannerModel, + kScannerType, "image/png")), + "")); + scan_interface_.ListScanners(base::Bind( + &DocumentScanInterfaceChromeosTest::OnListScannersResultReceived, + base::Unretained(this))); +} + +TEST_F(DocumentScanInterfaceChromeosTest, ScanFailure) { + const char kScannerName[] = "Monet"; + const int kResolution = 4096; + EXPECT_CALL(*client_, ScanImageToString( + kScannerName, + IsScannerProperties( + lorgnette::kScanPropertyModeColor, kResolution), + _)).WillOnce(InvokeScanCallback(false, "")); + EXPECT_CALL(*this, OnScanCompleted("data:image/png;base64,", "image/png", + "Image scan failed")); + scan_interface_.Scan( + kScannerName, DocumentScanInterface::kScanModeColor, kResolution, + base::Bind(&DocumentScanInterfaceChromeosTest::OnScanCompleted, + base::Unretained(this))); +} + +TEST_F(DocumentScanInterfaceChromeosTest, ScanSuccess) { + const char kScannerName[] = "Monet"; + const int kResolution = 4096; + EXPECT_CALL( + *client_, + ScanImageToString( + kScannerName, + IsScannerProperties(lorgnette::kScanPropertyModeColor, kResolution), + _)).WillOnce(InvokeScanCallback(true, std::string("PrettyPicture"))); + + // Data URL plus base64 representation of "PrettyPicture". + const char kExpectedImageData[] = + "data:image/png;base64,UHJldHR5UGljdHVyZQ=="; + + EXPECT_CALL(*this, OnScanCompleted(kExpectedImageData, "image/png", "")); + scan_interface_.Scan( + kScannerName, DocumentScanInterface::kScanModeColor, kResolution, + base::Bind(&DocumentScanInterfaceChromeosTest::OnScanCompleted, + base::Unretained(this))); +} + +} // namespace core_api + +} // namespace extensions diff --git a/extensions/browser/api/document_scan/document_scan_interface_nonchromeos.cc b/extensions/browser/api/document_scan/document_scan_interface_nonchromeos.cc new file mode 100644 index 0000000..6e8e50b --- /dev/null +++ b/extensions/browser/api/document_scan/document_scan_interface_nonchromeos.cc @@ -0,0 +1,43 @@ +// 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 { + +const char kScanFunctionNotImplementedError[] = "Scan function not implemented"; + +} // namespace + +namespace extensions { + +namespace core_api { + +class DocumentScanInterfaceImpl : public DocumentScanInterface { + public: + DocumentScanInterfaceImpl() {} + ~DocumentScanInterfaceImpl() override {} + + void ListScanners(const ListScannersResultsCallback& callback) override { + callback.Run(std::vector<ScannerDescription>(), ""); + } + void Scan(const std::string& scanner_name, + ScanMode mode, + int resolution_dpi, + const ScanResultsCallback& callback) override { + callback.Run("", "", kScanFunctionNotImplementedError); + } + + private: + DISALLOW_COPY_AND_ASSIGN(DocumentScanInterfaceImpl); +}; + +// static +DocumentScanInterface* DocumentScanInterface::CreateInstance() { + return new DocumentScanInterfaceImpl(); +} + +} // namespace core_api + +} // namespace extensions diff --git a/extensions/browser/api/document_scan/mock_document_scan_interface.cc b/extensions/browser/api/document_scan/mock_document_scan_interface.cc new file mode 100644 index 0000000..6734840 --- /dev/null +++ b/extensions/browser/api/document_scan/mock_document_scan_interface.cc @@ -0,0 +1,19 @@ +// 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/mock_document_scan_interface.h" + +namespace extensions { + +namespace core_api { + +MockDocumentScanInterface::MockDocumentScanInterface() { +} + +MockDocumentScanInterface::~MockDocumentScanInterface() { +} + +} // namespace core_api + +} // namespace extensions diff --git a/extensions/browser/api/document_scan/mock_document_scan_interface.h b/extensions/browser/api/document_scan/mock_document_scan_interface.h new file mode 100644 index 0000000..3ced6c7 --- /dev/null +++ b/extensions/browser/api/document_scan/mock_document_scan_interface.h @@ -0,0 +1,36 @@ +// 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_MOCK_DOCUMENT_SCAN_INTERFACE_H_ +#define EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_MOCK_DOCUMENT_SCAN_INTERFACE_H_ + +#include <string> + +#include <gmock/gmock.h> + +#include "extensions/browser/api/document_scan/document_scan_interface.h" + +namespace extensions { + +namespace core_api { + +class MockDocumentScanInterface : public DocumentScanInterface { + public: + MockDocumentScanInterface(); + ~MockDocumentScanInterface() override; + + MOCK_METHOD4(Scan, + void(const std::string& scanner_name, + ScanMode mode, + int resolution_dpi, + const ScanResultsCallback& callback)); + MOCK_METHOD1(ListScanners, void(const ListScannersResultsCallback& callback)); + MOCK_CONST_METHOD0(GetImageMimeType, std::string()); +}; + +} // namespace core_api + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_DOCUMENT_SCAN_MOCK_DOCUMENT_SCAN_INTERFACE_H_ diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json index efcb46e..b49624a 100644 --- a/extensions/common/api/_api_features.json +++ b/extensions/common/api/_api_features.json @@ -115,6 +115,11 @@ "dependencies": ["permission:dns"], "contexts": ["blessed_extension"] }, + "documentScan": { + "platforms": ["chromeos"], + "dependencies": ["permission:documentScan"], + "contexts": ["blessed_extension"] + }, // This is not a real API, only here for documentation purposes. // See http://crbug.com/275944 for background. "extensionTypes": { diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json index d2f39ff..59407f1 100644 --- a/extensions/common/api/_permission_features.json +++ b/extensions/common/api/_permission_features.json @@ -181,6 +181,10 @@ ] } ], + "documentScan": { + "channel": "dev", + "extension_types": ["extension", "platform_app"] + }, "extensionview": { "channel": "dev", "extension_types": ["platform_app"] diff --git a/extensions/common/api/document_scan.idl b/extensions/common/api/document_scan.idl new file mode 100644 index 0000000..a2bdcca --- /dev/null +++ b/extensions/common/api/document_scan.idl @@ -0,0 +1,37 @@ +// 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. + +// Use the <code>chrome.documentScan</code> API to discover and retrieve +// images from attached paper document scanners. +namespace documentScan { + dictionary ScanOptions { + // The MIME types that are accepted by the caller. + DOMString[]? mimeTypes; + + // The number of scanned images allowed (defaults to 1). + long? maxImages; + }; + + dictionary ScanResults { + // The data image URLs in a form that can be passed as the "src" value to + // an image tag. + DOMString[] dataUrls; + + // The MIME type of <code>dataUrls</code>. + DOMString mimeType; + }; + + // Callback from the <code>scan</code> method. + // |result| The results from the scan, if successful. + // Otherwise will return null and set runtime.lastError. + callback ScanCallback = void (ScanResults result); + + interface Functions { + // Performs a document scan. On success, the PNG data will be + // sent to the callback. + // |options| : Object containing scan parameters. + // |callback| : Called with the result and data from the scan. + static void scan(ScanOptions options, ScanCallback callback); + }; +}; diff --git a/extensions/common/api/schemas.gypi b/extensions/common/api/schemas.gypi index 0d170f9..ee46bd2 100644 --- a/extensions/common/api/schemas.gypi +++ b/extensions/common/api/schemas.gypi @@ -20,6 +20,7 @@ 'bluetooth_private.json', 'bluetooth_socket.idl', 'cast_channel.idl', + 'document_scan.idl', 'dns.idl', 'events.json', 'extensions_manifest_types.json', diff --git a/extensions/common/permissions/extensions_api_permissions.cc b/extensions/common/permissions/extensions_api_permissions.cc index d9a811e..869e313 100644 --- a/extensions/common/permissions/extensions_api_permissions.cc +++ b/extensions/common/permissions/extensions_api_permissions.cc @@ -65,6 +65,11 @@ std::vector<APIPermissionInfo*> ExtensionsAPIPermissions::GetAllPermissions() "diagnostics", APIPermissionInfo::kFlagCannotBeOptional}, {APIPermission::kDns, "dns"}, + {APIPermission::kDocumentScan, + "documentScan", + APIPermissionInfo::kFlagNone, + IDS_EXTENSION_PROMPT_WARNING_DOCUMENT_SCAN, + PermissionMessage::kDocumentScan}, {APIPermission::kExtensionView, "extensionview", APIPermissionInfo::kFlagCannotBeOptional}, diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp index ace05eaf..e4597a3 100644 --- a/extensions/extensions.gyp +++ b/extensions/extensions.gyp @@ -463,6 +463,11 @@ 'browser/api/dns/dns_api.h', 'browser/api/dns/host_resolver_wrapper.cc', 'browser/api/dns/host_resolver_wrapper.h', + 'browser/api/document_scan/document_scan_api.cc', + 'browser/api/document_scan/document_scan_api.h', + 'browser/api/document_scan/document_scan_interface.cc', + 'browser/api/document_scan/document_scan_interface.h', + 'browser/api/document_scan/document_scan_interface_chromeos.cc', 'browser/api/execute_code_function.cc', 'browser/api/execute_code_function.h', 'browser/api/extension_view/extension_view_internal_api.cc', @@ -901,7 +906,11 @@ 'browser/api/vpn_provider/vpn_service.cc', 'browser/api/vpn_provider/vpn_service.h', 'browser/api/vpn_provider/vpn_service_factory.h' - ] + ], + }, { # chromeos==0 + 'sources': [ + 'browser/api/document_scan/document_scan_interface_nonchromeos.cc', + ], }], ['OS == "win" or OS == "mac"', { 'dependencies': [ diff --git a/extensions/extensions_tests.gyp b/extensions/extensions_tests.gyp index a66e54c..30a8330 100644 --- a/extensions/extensions_tests.gyp +++ b/extensions/extensions_tests.gyp @@ -61,6 +61,9 @@ 'browser/api/declarative/rules_registry_unittest.cc', 'browser/api/declarative_webrequest/webrequest_condition_unittest.cc', 'browser/api/declarative_webrequest/webrequest_condition_attribute_unittest.cc', + 'browser/api/document_scan/document_scan_api_unittest.cc', + 'browser/api/document_scan/document_scan_interface_chromeos_unittest.cc', + 'browser/api/document_scan/mock_document_scan_interface.cc', 'browser/api/idle/idle_api_unittest.cc', 'browser/api/mime_handler_private/mime_handler_private_unittest.cc', 'browser/api/networking_config/networking_config_service_chromeos_unittest.cc', @@ -162,6 +165,11 @@ '../base/allocator/allocator.gyp:allocator', ], }], + ['chromeos==1', { + 'dependencies': [ + '<(DEPTH)/chromeos/chromeos.gyp:chromeos_test_support', + ], + }], ], }, { |