summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpstew <pstew@chromium.org>2014-10-30 11:55:25 -0700
committerCommit bot <commit-bot@chromium.org>2014-10-30 18:55:54 +0000
commitea73a785e4d97de63c928f0a574d25beb6236aae (patch)
treecd3f2973264989670a46e8bf5d19793ca47639b0
parent2243a22590179249c76e1f7a8d90d38f0f5e11fd (diff)
downloadchromium_src-ea73a785e4d97de63c928f0a574d25beb6236aae.zip
chromium_src-ea73a785e4d97de63c928f0a574d25beb6236aae.tar.gz
chromium_src-ea73a785e4d97de63c928f0a574d25beb6236aae.tar.bz2
Implement a JavaScript API for document scanning
Provide an API for callers to list available document scanners, and to acquire a single-page scan from one such device. This currently takes advantage of the lorgnette scanning API in ChromeOS but can be extended to use native document acquisition APIs on other platforms. BUG=375334 R=asargent@chromium.org, mef@chromium.org, asvitkine@chromium.org Review URL: https://codereview.chromium.org/286933006 Cr-Commit-Position: refs/heads/master@{#302122}
-rw-r--r--chrome/browser/extensions/api/document_scan/document_scan_api.cc119
-rw-r--r--chrome/browser/extensions/api/document_scan/document_scan_api.h55
-rw-r--r--chrome/browser/extensions/api/document_scan/document_scan_api_unittest.cc123
-rw-r--r--chrome/browser/extensions/api/document_scan/document_scan_interface.cc26
-rw-r--r--chrome/browser/extensions/api/document_scan/document_scan_interface.h64
-rw-r--r--chrome/browser/extensions/api/document_scan/document_scan_interface_chromeos.cc161
-rw-r--r--chrome/browser/extensions/api/document_scan/document_scan_interface_chromeos.h51
-rw-r--r--chrome/browser/extensions/api/document_scan/document_scan_interface_chromeos_unittest.cc168
-rw-r--r--chrome/browser/extensions/api/document_scan/document_scan_interface_nonchromeos.cc44
-rw-r--r--chrome/browser/extensions/api/document_scan/mock_document_scan_interface.cc20
-rw-r--r--chrome/browser/extensions/api/document_scan/mock_document_scan_interface.h35
-rw-r--r--chrome/chrome_browser_extensions.gypi6
-rw-r--r--chrome/chrome_tests_unit.gypi3
-rw-r--r--chrome/common/extensions/api/_api_features.json5
-rw-r--r--chrome/common/extensions/api/_permission_features.json4
-rw-r--r--chrome/common/extensions/api/document_scan.idl36
-rw-r--r--chrome/common/extensions/api/schemas.gypi1
-rw-r--r--chrome/common/extensions/docs/examples/api/document_scan/README.md10
-rw-r--r--chrome/common/extensions/docs/examples/api/document_scan/background.js14
-rw-r--r--chrome/common/extensions/docs/examples/api/document_scan/manifest.json13
-rw-r--r--chrome/common/extensions/docs/examples/api/document_scan/scan.css37
-rw-r--r--chrome/common/extensions/docs/examples/api/document_scan/scan.html18
-rw-r--r--chrome/common/extensions/docs/examples/api/document_scan/scan.js69
-rw-r--r--chrome/common/extensions/permissions/chrome_api_permissions.cc3
-rw-r--r--chromeos/chromeos.gyp2
-rw-r--r--chromeos/dbus/mock_lorgnette_manager_client.cc16
-rw-r--r--chromeos/dbus/mock_lorgnette_manager_client.h29
-rw-r--r--extensions/browser/extension_function_histogram_value.h1
-rw-r--r--extensions/common/permissions/api_permission.h5
-rw-r--r--extensions/common/permissions/permission_message.h3
-rw-r--r--extensions/extensions_strings.grd5
-rw-r--r--tools/metrics/histograms/histograms.xml7
32 files changed, 1151 insertions, 2 deletions
diff --git a/chrome/browser/extensions/api/document_scan/document_scan_api.cc b/chrome/browser/extensions/api/document_scan/document_scan_api.cc
new file mode 100644
index 0000000..1af7c99
--- /dev/null
+++ b/chrome/browser/extensions/api/document_scan/document_scan_api.cc
@@ -0,0 +1,119 @@
+// 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 "chrome/browser/extensions/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 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 (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): Display a user interface for choosing a scanner.
+ // This is where the scan mode, DPI, etc, would be specified.
+
+ 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): 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 api
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/document_scan/document_scan_api.h b/chrome/browser/extensions/api/document_scan/document_scan_api.h
new file mode 100644
index 0000000..30686b7
--- /dev/null
+++ b/chrome/browser/extensions/api/document_scan/document_scan_api.h
@@ -0,0 +1,55 @@
+// 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 <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/extensions/api/document_scan/document_scan_interface.h"
+#include "chrome/common/extensions/api/document_scan.h"
+#include "extensions/browser/api/async_api_function.h"
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_DOCUMENT_SCAN_DOCUMENT_SCAN_FUNCTION_H_
+#define CHROME_BROWSER_EXTENSIONS_API_DOCUMENT_SCAN_DOCUMENT_SCAN_FUNCTION_H_
+
+namespace extensions {
+
+namespace api {
+
+class DocumentScanScanFunction : public AsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("documentScan.scan",
+ DOCUMENT_SCAN_SCAN)
+ DocumentScanScanFunction();
+
+ protected:
+ ~DocumentScanScanFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() override;
+ virtual void AsyncWorkStart() override;
+ virtual 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 api
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_DOCUMENT_SCAN_DOCUMENT_SCAN_FUNCTION_H_
diff --git a/chrome/browser/extensions/api/document_scan/document_scan_api_unittest.cc b/chrome/browser/extensions/api/document_scan/document_scan_api_unittest.cc
new file mode 100644
index 0000000..40faad2
--- /dev/null
+++ b/chrome/browser/extensions/api/document_scan/document_scan_api_unittest.cc
@@ -0,0 +1,123 @@
+// 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 "chrome/browser/extensions/api/document_scan/document_scan_api.h"
+
+#include <string>
+#include <vector>
+
+#include "chrome/browser/extensions/api/document_scan/mock_document_scan_interface.h"
+#include "chrome/browser/extensions/extension_api_unittest.h"
+#include "chrome/browser/extensions/extension_function_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+
+namespace extensions {
+
+namespace api {
+
+// Tests of networking_private_crypto support for Networking Private API.
+class DocumentScanScanFunctionTest : public ExtensionApiUnittest {
+ public:
+ DocumentScanScanFunctionTest()
+ : function_(new DocumentScanScanFunction()),
+ document_scan_interface_(new MockDocumentScanInterface()) {}
+ virtual ~DocumentScanScanFunctionTest() {}
+
+ void SetUp() {
+ ExtensionApiUnittest::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 =
+ extension_function_test_utils::RunFunctionAndReturnError(
+ function_, args, browser(),
+ extension_function_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 api
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/document_scan/document_scan_interface.cc b/chrome/browser/extensions/api/document_scan/document_scan_interface.cc
new file mode 100644
index 0000000..be0149d
--- /dev/null
+++ b/chrome/browser/extensions/api/document_scan/document_scan_interface.cc
@@ -0,0 +1,26 @@
+// 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 "document_scan_interface.h"
+
+namespace extensions {
+
+namespace api {
+
+DocumentScanInterface::DocumentScanInterface() {
+}
+
+DocumentScanInterface::~DocumentScanInterface() {
+}
+
+DocumentScanInterface::ScannerDescription::ScannerDescription() {
+}
+
+DocumentScanInterface::ScannerDescription::~ScannerDescription() {
+}
+
+} // namespace api
+
+} // namespace extensions
+
diff --git a/chrome/browser/extensions/api/document_scan/document_scan_interface.h b/chrome/browser/extensions/api/document_scan/document_scan_interface.h
new file mode 100644
index 0000000..675e097
--- /dev/null
+++ b/chrome/browser/extensions/api/document_scan/document_scan_interface.h
@@ -0,0 +1,64 @@
+// 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 <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+
+#ifndef CHROME_BROWSER_EXTENSIONS_API_DOCUMENT_SCAN_DOCUMENT_SCAN_INTERFACE_H_
+#define CHROME_BROWSER_EXTENSIONS_API_DOCUMENT_SCAN_DOCUMENT_SCAN_INTERFACE_H_
+
+namespace extensions {
+
+namespace 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 api
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_DOCUMENT_SCAN_DOCUMENT_SCAN_INTERFACE_H_
diff --git a/chrome/browser/extensions/api/document_scan/document_scan_interface_chromeos.cc b/chrome/browser/extensions/api/document_scan/document_scan_interface_chromeos.cc
new file mode 100644
index 0000000..c91cb0b
--- /dev/null
+++ b/chrome/browser/extensions/api/document_scan/document_scan_interface_chromeos.cc
@@ -0,0 +1,161 @@
+// 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 "chrome/browser/extensions/api/document_scan/document_scan_interface_chromeos.h"
+
+#include "base/base64.h"
+#include "base/task_runner_util.h"
+#include "base/threading/worker_pool.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 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 (chromeos::LorgnetteManagerClient::ScannerTable::const_iterator iter =
+ scanners.begin();
+ iter != scanners.end();
+ ++iter) {
+ ScannerDescription description;
+ description.name = iter->first;
+ const chromeos::LorgnetteManagerClient::ScannerTableEntry &entry =
+ iter->second;
+ chromeos::LorgnetteManagerClient::ScannerTableEntry::const_iterator info_it;
+ 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);
+ }
+ callback.Run(scanner_descriptions, "");
+}
+
+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;
+
+ default:
+ // Leave the mode parameter empty, thereby using the default.
+ break;
+ }
+
+ if (resolution_dpi != 0) {
+ properties.resolution_dpi = resolution_dpi;
+ }
+
+ const bool kTasksAreSlow = true;
+ scoped_refptr<base::TaskRunner> task_runner =
+ base::WorkerPool::GetTaskRunner(kTasksAreSlow);
+
+ pipe_reader_.reset(new chromeos::PipeReaderForString(
+ task_runner,
+ base::Bind(&DocumentScanInterfaceChromeos::OnScanDataCompleted,
+ base::Unretained(this))));
+ base::File file = pipe_reader_->StartIO();
+ base::PlatformFile platform_file = file.TakePlatformFile();
+ VLOG(1) << "ScanImage platform_file is " << platform_file;
+ GetLorgnetteManagerClient()->ScanImage(
+ scanner_name, platform_file, properties,
+ base::Bind(&DocumentScanInterfaceChromeos::OnScanCompleted,
+ base::Unretained(this),
+ callback));
+}
+
+void DocumentScanInterfaceChromeos::OnScanCompleted(
+ const ScanResultsCallback &callback, bool succeeded) {
+ VLOG(1) << "ScanImage returns " << succeeded;
+ if (pipe_reader_.get()) {
+ pipe_reader_->OnDataReady(-1); // terminate data stream
+ }
+
+ std::string error_string;
+ if (!succeeded) {
+ error_string = kImageScanFailedError;
+ }
+
+ callback.Run(GetImageURL(scanned_image_data_), kScannerImageMimeTypePng,
+ error_string);
+}
+
+std::string DocumentScanInterfaceChromeos::GetImageURL(std::string image_data) {
+ std::string image_data_base64;
+ base::Base64Encode(image_data, &image_data_base64);
+ return std::string(kPngImageDataUrlPrefix) + image_data_base64;
+}
+
+void DocumentScanInterfaceChromeos::OnScanDataCompleted() {
+ pipe_reader_->GetData(&scanned_image_data_);
+ pipe_reader_.reset();
+}
+
+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 api
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/document_scan/document_scan_interface_chromeos.h b/chrome/browser/extensions/api/document_scan/document_scan_interface_chromeos.h
new file mode 100644
index 0000000..d54bbd1
--- /dev/null
+++ b/chrome/browser/extensions/api/document_scan/document_scan_interface_chromeos.h
@@ -0,0 +1,51 @@
+// 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 CHROME_BROWSER_EXTENSIONS_API_DOCUMENT_SCAN_DOCUMENT_SCAN_INTERFACE_CHROMEOS_H_
+#define CHROME_BROWSER_EXTENSIONS_API_DOCUMENT_SCAN_DOCUMENT_SCAN_INTERFACE_CHROMEOS_H_
+
+#include "chrome/browser/extensions/api/document_scan/document_scan_interface.h"
+#include "chromeos/dbus/lorgnette_manager_client.h"
+#include "chromeos/dbus/pipe_reader.h"
+
+namespace extensions {
+
+namespace api {
+
+class DocumentScanInterfaceChromeos : public DocumentScanInterface {
+ public:
+ DocumentScanInterfaceChromeos();
+ virtual ~DocumentScanInterfaceChromeos();
+
+ virtual void ListScanners(
+ const ListScannersResultsCallback& callback) override;
+ virtual 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);
+ void OnScanDataCompleted();
+ std::string GetImageURL(std::string image_data);
+ chromeos::LorgnetteManagerClient* GetLorgnetteManagerClient();
+
+ scoped_ptr<chromeos::PipeReaderForString> pipe_reader_;
+ std::string scanned_image_data_;
+ chromeos::LorgnetteManagerClient* lorgnette_manager_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(DocumentScanInterfaceChromeos);
+};
+
+} // namespace api
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_DOCUMENT_SCAN_DOCUMENT_SCAN_INTERFACE_CHROMEOS_H_
diff --git a/chrome/browser/extensions/api/document_scan/document_scan_interface_chromeos_unittest.cc b/chrome/browser/extensions/api/document_scan/document_scan_interface_chromeos_unittest.cc
new file mode 100644
index 0000000..a84fe0b
--- /dev/null
+++ b/chrome/browser/extensions/api/document_scan/document_scan_interface_chromeos_unittest.cc
@@ -0,0 +1,168 @@
+// 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 "chrome/browser/extensions/api/document_scan/document_scan_interface_chromeos.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/thread_task_runner_handle.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 api {
+
+// Tests of networking_private_crypto support for Networking Private API.
+class DocumentScanInterfaceChromeosTest : public testing::Test {
+ public:
+ DocumentScanInterfaceChromeosTest()
+ : client_(new chromeos::MockLorgnetteManagerClient()) {}
+ virtual ~DocumentScanInterfaceChromeosTest() {}
+
+ void SetUp() {
+ // This is needed to create a valid PipeReader object.
+ task_runner_ = new base::TestSimpleTaskRunner();
+ thread_task_runner_handle_.reset(
+ new base::ThreadTaskRunnerHandle(task_runner_));
+ scan_interface_.lorgnette_manager_client_ = client_.get();
+ }
+
+ virtual void TearDown() override {
+ thread_task_runner_handle_.reset();
+ task_runner_ = NULL;
+ }
+
+ 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));
+
+ chromeos::PipeReader *GetPipeReader() {
+ return scan_interface_.pipe_reader_.get();
+ }
+
+ protected:
+ DocumentScanInterfaceChromeos scan_interface_;
+ scoped_ptr<chromeos::MockLorgnetteManagerClient> client_;
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+ scoped_ptr<base::ThreadTaskRunnerHandle> thread_task_runner_handle_;
+};
+
+ACTION_P2(InvokeListScannersCallback, scanner_list, error) {
+ ::std::tr1::get<0>(args).Run(scanner_list, error);
+}
+
+ACTION_P2(InvokePipeReader, test, data) {
+ test->GetPipeReader()->AcceptData(data.c_str(), data.size());
+}
+
+ACTION_P(InvokeScanCallback, succeeded) {
+ ::std::tr1::get<3>(args).Run(succeeded);
+}
+
+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_TRUE(base::ThreadTaskRunnerHandle::IsSet());
+ EXPECT_CALL(*client_, ScanImage(
+ 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_TRUE(base::ThreadTaskRunnerHandle::IsSet());
+ EXPECT_CALL(*client_,
+ ScanImage(
+ kScannerName,
+ _,
+ IsScannerProperties(
+ lorgnette::kScanPropertyModeColor,
+ kResolution),
+ _))
+ .WillOnce(testing::DoAll(
+ InvokePipeReader(this, std::string("PrettyPicture")),
+ InvokeScanCallback(true)));
+
+ // 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 api
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/document_scan/document_scan_interface_nonchromeos.cc b/chrome/browser/extensions/api/document_scan/document_scan_interface_nonchromeos.cc
new file mode 100644
index 0000000..cd21037
--- /dev/null
+++ b/chrome/browser/extensions/api/document_scan/document_scan_interface_nonchromeos.cc
@@ -0,0 +1,44 @@
+// 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 "chrome/browser/extensions/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() {}
+ virtual ~DocumentScanInterfaceImpl() {}
+
+ virtual void ListScanners(
+ const ListScannersResultsCallback& callback) override {
+ callback.Run(std::vector<ScannerDescription> (), "");
+ }
+ virtual 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 api
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/api/document_scan/mock_document_scan_interface.cc b/chrome/browser/extensions/api/document_scan/mock_document_scan_interface.cc
new file mode 100644
index 0000000..830575f
--- /dev/null
+++ b/chrome/browser/extensions/api/document_scan/mock_document_scan_interface.cc
@@ -0,0 +1,20 @@
+// 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 "mock_document_scan_interface.h"
+
+namespace extensions {
+
+namespace api {
+
+MockDocumentScanInterface::MockDocumentScanInterface() {
+}
+
+MockDocumentScanInterface::~MockDocumentScanInterface() {
+}
+
+} // namespace api
+
+} // namespace extensions
+
diff --git a/chrome/browser/extensions/api/document_scan/mock_document_scan_interface.h b/chrome/browser/extensions/api/document_scan/mock_document_scan_interface.h
new file mode 100644
index 0000000..8303147
--- /dev/null
+++ b/chrome/browser/extensions/api/document_scan/mock_document_scan_interface.h
@@ -0,0 +1,35 @@
+// 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 CHROME_BROWSER_EXTENSIONS_API_DOCUMENT_SCAN_MOCK_DOCUMENT_SCAN_INTERFACE_H_
+#define CHROME_BROWSER_EXTENSIONS_API_DOCUMENT_SCAN_MOCK_DOCUMENT_SCAN_INTERFACE_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "chrome/browser/extensions/api/document_scan/document_scan_interface.h"
+
+namespace extensions {
+
+namespace api {
+
+class MockDocumentScanInterface : public DocumentScanInterface {
+ public:
+ MockDocumentScanInterface();
+ virtual ~MockDocumentScanInterface();
+
+ 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 api
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_API_DOCUMENT_SCAN_MOCK_DOCUMENT_SCAN_INTERFACE_H_
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index 68ef123..68d75b3 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -8,6 +8,7 @@
'browser/extensions/api/diagnostics/diagnostics_api.cc',
'browser/extensions/api/diagnostics/diagnostics_api.h',
'browser/extensions/api/diagnostics/diagnostics_api_chromeos.cc',
+ 'browser/extensions/api/document_scan/document_scan_interface_chromeos.cc',
'browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc',
'browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.h',
'browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc',
@@ -37,6 +38,7 @@
'browser/extensions/updater/local_extension_cache.h',
],
'chrome_browser_extensions_non_chromeos_sources': [
+ 'browser/extensions/api/document_scan/document_scan_interface_nonchromeos.cc',
'browser/extensions/api/easy_unlock_private/easy_unlock_private_crypto_delegate_stub.cc',
'browser/extensions/api/feedback_private/feedback_service_nonchromeos.cc',
'browser/extensions/api/image_writer_private/operation_nonchromeos.cc',
@@ -208,6 +210,10 @@
'browser/extensions/api/dial/dial_registry.h',
'browser/extensions/api/dial/dial_service.cc',
'browser/extensions/api/dial/dial_service.h',
+ 'browser/extensions/api/document_scan/document_scan_interface.cc',
+ 'browser/extensions/api/document_scan/document_scan_interface.h',
+ 'browser/extensions/api/document_scan/document_scan_api.cc',
+ 'browser/extensions/api/document_scan/document_scan_api.h',
'browser/extensions/api/downloads/downloads_api.cc',
'browser/extensions/api/downloads/downloads_api.h',
'browser/extensions/api/downloads_internal/downloads_internal_api.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 7098c86..53f02db 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -322,6 +322,8 @@
'browser/extensions/api/dial/dial_device_data_unittest.cc',
'browser/extensions/api/dial/dial_registry_unittest.cc',
'browser/extensions/api/dial/dial_service_unittest.cc',
+ 'browser/extensions/api/document_scan/document_scan_api_unittest.cc',
+ 'browser/extensions/api/document_scan/mock_document_scan_interface.cc',
'browser/extensions/api/easy_unlock_private/easy_unlock_private_api_chromeos_unittest.cc',
'browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc',
'browser/extensions/api/experience_sampling_private/experience_sampling_private_api_unittest.cc',
@@ -2308,6 +2310,7 @@
'browser/upgrade_detector_impl_unittest.cc',
],
'sources': [
+ 'browser/extensions/api/document_scan/document_scan_interface_chromeos_unittest.cc',
'browser/extensions/updater/local_extension_cache_unittest.cc',
'browser/metrics/chromeos_metrics_provider_unittest.cc',
],
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index 1f61fef..560d308 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -250,6 +250,11 @@
"dependencies": ["permission:dial"],
"contexts": ["blessed_extension"]
},
+ "documentScan": {
+ "platforms": ["chromeos"],
+ "dependencies": ["permission:documentScan"],
+ "contexts": ["blessed_extension"]
+ },
"downloads": {
"dependencies": ["permission:downloads"],
"contexts": ["blessed_extension"]
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index 8e109cf..88ec512 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -330,6 +330,10 @@
"channel": "stable",
"extension_types": ["extension", "platform_app"]
},
+ "documentScan": {
+ "channel": "dev",
+ "extension_types": ["extension", "platform_app"]
+ },
"downloads": {
"channel": "stable",
"extension_types": ["extension"]
diff --git a/chrome/common/extensions/api/document_scan.idl b/chrome/common/extensions/api/document_scan.idl
new file mode 100644
index 0000000..0428d4a
--- /dev/null
+++ b/chrome/common/extensions/api/document_scan.idl
@@ -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.
+
+// Use the <code>chrome.document_scan</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 |dataUrls|.
+ DOMString mimeType;
+ };
+
+ // Callback from the <code>scan</code> method; on success
+ // the results from the scan is returned in |results|.
+ callback ScanCallback = void (ScanResults results);
+
+ interface Functions {
+ // Performs a document scan. On success, the PNG data will be
+ // sent to the callback.
+ // |options| : <code>Options</code> object containing scan parameters.
+ // |callback| : Called with the result and data from the scan.
+ static void scan(ScanOptions options, ScanCallback callback);
+ };
+};
diff --git a/chrome/common/extensions/api/schemas.gypi b/chrome/common/extensions/api/schemas.gypi
index a0e2e76..680320f 100644
--- a/chrome/common/extensions/api/schemas.gypi
+++ b/chrome/common/extensions/api/schemas.gypi
@@ -32,6 +32,7 @@
'desktop_capture.json',
'developer_private.idl',
'dial.idl',
+ 'document_scan.idl',
'downloads.idl',
'downloads_internal.idl',
'easy_unlock_private.idl',
diff --git a/chrome/common/extensions/docs/examples/api/document_scan/README.md b/chrome/common/extensions/docs/examples/api/document_scan/README.md
new file mode 100644
index 0000000..22c61ca
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/api/document_scan/README.md
@@ -0,0 +1,10 @@
+# Document Scanning API Sample
+
+This demo interfaces with the Chrome document scanning API to acquire scanned
+images.
+
+## APIs
+
+* [Document scanning API](https://developer.chrome.com/apps/document_scan)
+* [Runtime](https://developer.chrome.com/apps/runtime)
+* [Window](https://developer.chrome.com/apps/app_window)
diff --git a/chrome/common/extensions/docs/examples/api/document_scan/background.js b/chrome/common/extensions/docs/examples/api/document_scan/background.js
new file mode 100644
index 0000000..7f7faf2
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/api/document_scan/background.js
@@ -0,0 +1,14 @@
+// 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.
+
+chrome.app.runtime.onLaunched.addListener(function() {
+ chrome.app.window.create('scan.html', {
+ singleton: true,
+ id: "ChromeApps-Sample-Document-Scan",
+ bounds: {
+ 'width': 480,
+ 'height': 640
+ }
+ });
+});
diff --git a/chrome/common/extensions/docs/examples/api/document_scan/manifest.json b/chrome/common/extensions/docs/examples/api/document_scan/manifest.json
new file mode 100644
index 0000000..8f788d3
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/api/document_scan/manifest.json
@@ -0,0 +1,13 @@
+{
+ "name": "Document Scanning API Sample",
+ "version": "0.1",
+ "manifest_version": 2,
+ "minimum_chrome_version": "37",
+ "app": {
+ "background": {
+ "scripts": ["background.js"]
+ }
+ },
+ "permissions": [],
+ "optional_permissions": [ "documentScan" ]
+}
diff --git a/chrome/common/extensions/docs/examples/api/document_scan/scan.css b/chrome/common/extensions/docs/examples/api/document_scan/scan.css
new file mode 100644
index 0000000..08a4385
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/api/document_scan/scan.css
@@ -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.
+ */
+
+#waitAnimation {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ height:100%;
+ width:100%;
+ z-index:1000;
+ background-color:black;
+ opacity:0.6;
+}
+
+#waitSpinner {
+ position: absolute;
+ height:60px;
+ width:60px;
+ top: 50%;
+ left: 50%;
+ margin-left: -30px;
+ margin-top: -30px;
+ -webkit-animation: rotation .6s infinite linear;
+ animation: rotation .6s infinite linear;
+ border-left:6px solid rgba(180,174,239,.15);
+ border-right:6px solid rgba(180,174,239,.15);
+ border-bottom:6px solid rgba(180,174,239,.15);
+ border-top:6px solid rgba(180,174,239,.8);
+ border-radius:100%;
+}
+
+@-webkit-keyframes rotation {
+ from {-webkit-transform: rotate(0deg);}
+ to {-webkit-transform: rotate(359deg);}
+}
diff --git a/chrome/common/extensions/docs/examples/api/document_scan/scan.html b/chrome/common/extensions/docs/examples/api/document_scan/scan.html
new file mode 100644
index 0000000..a7b24b6
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/api/document_scan/scan.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Scanner Control</title>
+ <link rel="stylesheet" type="text/css" href="scan.css">
+ </head>
+ <body>
+ <div id="waitAnimation" style="display: none;">
+ <div id="waitSpinner"></div>
+ </div>
+ </img>
+ <button id="requestButton">Request App permissions</button>
+ <button id="scanButton">Scan</button>
+ <div id="scannedImages">
+ </div>
+ <script src="scan.js"></script>
+ </body>
+</html>
diff --git a/chrome/common/extensions/docs/examples/api/document_scan/scan.js b/chrome/common/extensions/docs/examples/api/document_scan/scan.js
new file mode 100644
index 0000000..23d8495
--- /dev/null
+++ b/chrome/common/extensions/docs/examples/api/document_scan/scan.js
@@ -0,0 +1,69 @@
+// 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.
+
+var requestButton = document.getElementById("requestButton");
+var scanButton = document.getElementById('scanButton');
+var scannedImages = document.getElementById('scannedImages');
+var waitAnimation = document.getElementById('waitAnimation');
+var imageMimeType;
+
+function setOnlyChild(parent, child) {
+ while (parent.firstChild) {
+ parent.removeChild(parent.firstChild);
+ }
+ parent.appendChild(child);
+}
+
+var gotPermission = function(result) {
+ waitAnimation.style.display = 'block';
+ requestButton.style.display = 'none';
+ scanButton.style.display = 'block';
+ console.log('App was granted the "documentScan" permission.');
+ waitAnimation.style.display = 'none';
+};
+
+var permissionObj = {permissions: ['documentScan']};
+
+requestButton.addEventListener('click', function() {
+ waitAnimation.style.display = 'block';
+ chrome.permissions.request( permissionObj, function(result) {
+ if (result) {
+ gotPermission();
+ } else {
+ console.log('App was not granted the "documentScan" permission.');
+ console.log(chrome.runtime.lastError);
+ }
+ });
+});
+
+var onScanCompleted = function(scan_results) {
+ waitAnimation.style.display = 'none';
+ if (chrome.runtime.lastError) {
+ console.log('Scan failed: ' + chrome.runtime.lastError.message);
+ return;
+ }
+ numImages = scan_results.dataUrls.length;
+ console.log('Scan completed with ' + numImages + ' images.');
+ for (var i = 0; i < numImages; i++) {
+ urlData = scan_results.dataUrls[i]
+ console.log('Scan ' + i + ' data length ' +
+ urlData.length + '.');
+ console.log('URL is ' + urlData);
+ var scannedImage = document.createElement('img');
+ scannedImage.src = urlData;
+ scannedImages.insertBefore(scannedImage, scannedImages.firstChild);
+ }
+};
+
+scanButton.addEventListener('click', function() {
+ var scanProperties = {};
+ waitAnimation.style.display = 'block';
+ chrome.documentScan.scan(scanProperties, onScanCompleted);
+});
+
+chrome.permissions.contains(permissionObj, function(result) {
+ if (result) {
+ gotPermission();
+ }
+});
diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.cc b/chrome/common/extensions/permissions/chrome_api_permissions.cc
index 9c8cc20..875d2c1 100644
--- a/chrome/common/extensions/permissions/chrome_api_permissions.cc
+++ b/chrome/common/extensions/permissions/chrome_api_permissions.cc
@@ -118,6 +118,9 @@ std::vector<APIPermissionInfo*> ChromeAPIPermissions::GetAllPermissions()
IDS_EXTENSION_PROMPT_WARNING_COPRESENCE,
PermissionMessage::kCopresence},
{APIPermission::kCopresencePrivate, "copresencePrivate"},
+ {APIPermission::kDocumentScan, "documentScan",
+ APIPermissionInfo::kFlagNone, IDS_EXTENSION_PROMPT_WARNING_DOCUMENT_SCAN,
+ PermissionMessage::kDocumentScan},
{APIPermission::kEnterprisePlatformKeys, "enterprise.platformKeys"},
{APIPermission::kFileBrowserHandler,
"fileBrowserHandler",
diff --git a/chromeos/chromeos.gyp b/chromeos/chromeos.gyp
index 0fb9748..89a3580 100644
--- a/chromeos/chromeos.gyp
+++ b/chromeos/chromeos.gyp
@@ -523,6 +523,8 @@
'cryptohome/mock_homedir_methods.h',
'dbus/mock_cryptohome_client.cc',
'dbus/mock_cryptohome_client.h',
+ 'dbus/mock_lorgnette_manager_client.cc',
+ 'dbus/mock_lorgnette_manager_client.h',
'dbus/mock_session_manager_client.cc',
'dbus/mock_session_manager_client.h',
'dbus/mock_shill_manager_client.cc',
diff --git a/chromeos/dbus/mock_lorgnette_manager_client.cc b/chromeos/dbus/mock_lorgnette_manager_client.cc
new file mode 100644
index 0000000..877a347
--- /dev/null
+++ b/chromeos/dbus/mock_lorgnette_manager_client.cc
@@ -0,0 +1,16 @@
+// 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 "mock_lorgnette_manager_client.h"
+
+namespace chromeos {
+
+MockLorgnetteManagerClient::MockLorgnetteManagerClient() {
+}
+
+MockLorgnetteManagerClient::~MockLorgnetteManagerClient() {
+}
+
+} // namespace chromeos
+
diff --git a/chromeos/dbus/mock_lorgnette_manager_client.h b/chromeos/dbus/mock_lorgnette_manager_client.h
new file mode 100644
index 0000000..df80f5f3
--- /dev/null
+++ b/chromeos/dbus/mock_lorgnette_manager_client.h
@@ -0,0 +1,29 @@
+// 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 CHROMEOS_DBUS_MOCK_LORGNETTE_MANAGER_CLIENT_H_
+#define CHROMEOS_DBUS_MOCK_LORGNETTE_MANAGER_CLIENT_H_
+
+#include "lorgnette_manager_client.h"
+
+#include <gmock/gmock.h>
+
+namespace chromeos {
+
+class MockLorgnetteManagerClient : public LorgnetteManagerClient {
+ public:
+ MockLorgnetteManagerClient();
+ virtual ~MockLorgnetteManagerClient();
+
+ MOCK_METHOD1(ListScanners, void(const ListScannersCallback& callback));
+ MOCK_METHOD4(ScanImage, void(std::string device_name,
+ base::PlatformFile file,
+ const ScanProperties& properties,
+ const ScanImageCallback& callback));
+ MOCK_METHOD1(Init, void(dbus::Bus* bus));
+};
+
+} // namespace chromeos
+
+#endif // CHROMEOS_DBUS_MOCK_LORGNETTE_MANAGER_CLIENT_H_
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 8d0b10c..45b0d6f 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -969,6 +969,7 @@ enum HistogramValue {
INPUTMETHODPRIVATE_GETINPUTMETHODCONFIG,
WALLPAPERPRIVATE_GETSYNCSETTING,
COPRESENCE_SETAUTHTOKEN,
+ DOCUMENT_SCAN_SCAN,
// Last entry: Add new entries above and ensure to update
// tools/metrics/histograms/histograms.xml.
ENUM_BOUNDARY
diff --git a/extensions/common/permissions/api_permission.h b/extensions/common/permissions/api_permission.h
index fbb34d1..c60ba75 100644
--- a/extensions/common/permissions/api_permission.h
+++ b/extensions/common/permissions/api_permission.h
@@ -53,6 +53,7 @@ class APIPermission {
kBookmark,
kBookmarkManagerPrivate,
kBrailleDisplayPrivate,
+ kBrowser,
kBrowsingData,
kCast,
kCastStreaming,
@@ -77,6 +78,7 @@ class APIPermission {
kDeveloperPrivate,
kDevtools,
kDns,
+ kDocumentScan,
kDownloads,
kDownloadsInternal,
kDownloadsOpen,
@@ -99,6 +101,7 @@ class APIPermission {
kFileSystemRetainEntries,
kFileSystemWrite,
kFileSystemWriteDirectory,
+ kFirstRunPrivate,
kFontSettings,
kFullscreen,
kGcdPrivate,
@@ -188,8 +191,6 @@ class APIPermission {
kSystemNetwork,
kSystemInfoCpu,
kSystemInfoMemory,
- kFirstRunPrivate,
- kBrowser,
kEnumBoundary
};
diff --git a/extensions/common/permissions/permission_message.h b/extensions/common/permissions/permission_message.h
index 25eda75..2df53e3 100644
--- a/extensions/common/permissions/permission_message.h
+++ b/extensions/common/permissions/permission_message.h
@@ -90,6 +90,9 @@ class PermissionMessage {
kCopresence,
kTopSites,
kU2fDevices,
+ kDocumentScan,
+ // Last entry: Add new entries above and ensure to update the
+ // "ExtensionPermission2" enum in tools/metrics/histograms/histograms.xml.
kEnumBoundary,
};
COMPILE_ASSERT(PermissionMessage::kNone > PermissionMessage::kUnknown,
diff --git a/extensions/extensions_strings.grd b/extensions/extensions_strings.grd
index f2f09c7..4106ab7 100644
--- a/extensions/extensions_strings.grd
+++ b/extensions/extensions_strings.grd
@@ -174,6 +174,11 @@
<release seq="1" allow_pseudo="false">
<messages fallback_to_english="true">
+ <!-- Document Scan API strings. Please keep alphabetized. -->
+ <message name="IDS_EXTENSION_PROMPT_WARNING_DOCUMENT_SCAN" desc="Permission string for access to document scanning.">
+ Access document scanners attached via USB or on the local network
+ </message>
+
<!-- General extensions strings. Please keep alphabetized. -->
<message name="IDS_EXTENSION_CONTAINS_PRIVATE_KEY" desc="Error message when an extension includes a file containing a private key.">
This extension includes the key file '<ph name="KEY_PATH">$1<ex>relative/path/to/file.pem</ex></ph>'. You probably don't want to do that.
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 096f76d..4c02d5e 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -43417,6 +43417,7 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<int value="908" label="INPUTMETHODPRIVATE_GETINPUTMETHODCONFIG"/>
<int value="909" label="WALLPAPERPRIVATE_GETSYNCSETTING"/>
<int value="910" label="COPRESENCE_SETAUTHTOKEN"/>
+ <int value="911" label="DOCUMENT_SCAN_SCAN"/>
</enum>
<enum name="ExtensionInstallCause" type="int">
@@ -43598,6 +43599,12 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<int value="60" label="kAccessibilityFeaturesModify"/>
<int value="61" label="kAccessibilityFeaturesRead"/>
<int value="62" label="kBluetoothPrivate"/>
+ <int value="63" label="kIdentityEmail"/>
+ <int value="64" label="kExperienceSamplingPrivate"/>
+ <int value="65" label="kCopresence"/>
+ <int value="66" label="kTopSites"/>
+ <int value="67" label="kU2fDevices"/>
+ <int value="68" label="kDocumentScan"/>
</enum>
<enum name="ExtensionServiceVerifyAllSuccess" type="int">