Add a manifest property declaring USB printers supported by an app.
This new manifest property "usb_printers" declares a set of USB device
filters matching the printers supported by an app. This will be used by
the printerProvider API to allow the user to select a printer that can
be supported by the app and in so doing grant the app permission to
connect to the printer.
BUG=468955
Review URL: https://codereview.chromium.org/1123093003
Cr-Commit-Position: refs/heads/master@{#328845}
diff --git a/chrome/common/extensions/docs/templates/articles/manifest/usb_printers.html b/chrome/common/extensions/docs/templates/articles/manifest/usb_printers.html
new file mode 100644
index 0000000..c8b9635
--- /dev/null
+++ b/chrome/common/extensions/docs/templates/articles/manifest/usb_printers.html
@@ -0,0 +1,28 @@
+<h1>Manifest - USB Printers</h1>
+
+<p>
+The <code>usbPrinters</code> manifest property declares which USB printers are supported by an app using the $(ref:printerProvider) API.
+</p>
+
+<h2 id="manifest">Sample manifest.json</h2>
+<pre data-filename="manifest.json">
+{
+ "name": "My printer {{platform}}",
+ "usb_printers": {
+ "filters": [
+ // This app can print to the Nexus One and any printer made by Google.
+ { "vendorId": 6353, "productId": 19985 },
+ { "vendorId": 6353, "interfaceClass": 7 }
+ ]
+ },
+ ...
+}
+</pre>
+
+<section>
+<h2 id="reference">Reference</h2>
+<p class="api_reference">
+{{+partials.manifest_type
+ type:apis.apps.extensionsManifestTypes.byName.UsbPrinters/}}
+</p>
+</section>
diff --git a/chrome/common/extensions/docs/templates/json/manifest.json b/chrome/common/extensions/docs/templates/json/manifest.json
index 1df8724..1b66baf 100644
--- a/chrome/common/extensions/docs/templates/json/manifest.json
+++ b/chrome/common/extensions/docs/templates/json/manifest.json
@@ -214,6 +214,12 @@
"documentation": "manifest/url_handlers",
"example": {}
},
+ "usb_printers": {
+ "documentation": "manifest/usb_printers",
+ "example": {
+ "filters": []
+ }
+ },
"version": {
"documentation": "manifest/version",
"example": "versionString",
diff --git a/chrome/common/extensions/docs/templates/public/apps/manifest/usb_printers.html b/chrome/common/extensions/docs/templates/public/apps/manifest/usb_printers.html
new file mode 100644
index 0000000..c2bf377
--- /dev/null
+++ b/chrome/common/extensions/docs/templates/public/apps/manifest/usb_printers.html
@@ -0,0 +1 @@
+{{+partials.standard_apps_article article:articles.manifest/usb_printers/}}
diff --git a/device/usb/usb_device_filter.cc b/device/usb/usb_device_filter.cc
index 970bbd0..b52a4a3 100644
--- a/device/usb/usb_device_filter.cc
+++ b/device/usb/usb_device_filter.cc
@@ -96,7 +96,7 @@
return true;
}
-base::Value* UsbDeviceFilter::ToValue() const {
+scoped_ptr<base::Value> UsbDeviceFilter::ToValue() const {
scoped_ptr<base::DictionaryValue> obj(new base::DictionaryValue());
if (vendor_id_set_) {
@@ -116,7 +116,7 @@
}
}
- return obj.release();
+ return obj.Pass();
}
// static
diff --git a/device/usb/usb_device_filter.h b/device/usb/usb_device_filter.h
index 6525f8f..a40c85968 100644
--- a/device/usb/usb_device_filter.h
+++ b/device/usb/usb_device_filter.h
@@ -8,6 +8,7 @@
#include <vector>
#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
namespace base {
class Value;
@@ -29,7 +30,7 @@
void SetInterfaceProtocol(uint8 interface_protocol);
bool Matches(scoped_refptr<UsbDevice> device) const;
- base::Value* ToValue() const;
+ scoped_ptr<base::Value> ToValue() const;
static bool MatchesAny(scoped_refptr<UsbDevice> device,
const std::vector<UsbDeviceFilter>& filters);
diff --git a/extensions/common/api/_manifest_features.json b/extensions/common/api/_manifest_features.json
index 68a02d8..b9950f04 100644
--- a/extensions/common/api/_manifest_features.json
+++ b/extensions/common/api/_manifest_features.json
@@ -269,6 +269,10 @@
"channel": "stable",
"extension_types": ["platform_app"]
},
+ "usb_printers": {
+ "channel": "dev",
+ "extension_types": ["platform_app"]
+ },
"version": {
"channel": "stable",
"extension_types": "all"
diff --git a/extensions/common/api/extensions_manifest_types.json b/extensions/common/api/extensions_manifest_types.json
index cbf735c..d698273 100644
--- a/extensions/common/api/extensions_manifest_types.json
+++ b/extensions/common/api/extensions_manifest_types.json
@@ -167,6 +167,46 @@
"optional": true
}
}
+ },
+ {
+ "id": "UsbPrinters",
+ "type": "object",
+ "description": "The <code>usb_printers</code> manifest property lists the USB printers supported by an app implementing the $(ref:printerProvider) API.",
+ "properties": {
+ "filters": {
+ "description": "A list of $(ref:usb.DeviceFilter USB device filters) matching supported devices. A device only needs to match one of the provided filters. A <code>vendorId</code> is required and only one of <code>productId</code> or <code>interfaceClass</code> may be provided.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "vendorId": {
+ "description": "USB vendor ID of matching devices",
+ "type": "integer"
+ },
+ "productId": {
+ "description": "USB product ID of matching devices",
+ "type": "integer",
+ "optional": true
+ },
+ "interfaceClass": {
+ "description": "USB interface class implemented by any interface of a matching device.",
+ "type": "integer",
+ "optional": true
+ },
+ "interfaceSubclass": {
+ "description": "USB interface sub-class implemented by the interface matching $(ref:interfaceClass).",
+ "type": "integer",
+ "optional": true
+ },
+ "interfaceProtocol": {
+ "description": "USB interface protocol implemented by the interface matching $(ref:interfaceClass) and $(ref:interfaceSubclass).",
+ "type": "integer",
+ "optional": true
+ }
+ }
+ }
+ }
+ }
}
]
}
diff --git a/extensions/common/api/printer_provider/usb_printer_manifest_data.cc b/extensions/common/api/printer_provider/usb_printer_manifest_data.cc
new file mode 100644
index 0000000..61dc9e83
--- /dev/null
+++ b/extensions/common/api/printer_provider/usb_printer_manifest_data.cc
@@ -0,0 +1,64 @@
+// Copyright 2015 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/common/api/printer_provider/usb_printer_manifest_data.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "device/usb/usb_device_filter.h"
+#include "extensions/common/api/extensions_manifest_types.h"
+#include "extensions/common/manifest_constants.h"
+
+namespace extensions {
+
+UsbPrinterManifestData::UsbPrinterManifestData() {
+}
+
+UsbPrinterManifestData::~UsbPrinterManifestData() {
+}
+
+// static
+const UsbPrinterManifestData* UsbPrinterManifestData::Get(
+ const Extension* extension) {
+ return static_cast<UsbPrinterManifestData*>(
+ extension->GetManifestData(manifest_keys::kUsbPrinters));
+}
+
+// static
+scoped_ptr<UsbPrinterManifestData> UsbPrinterManifestData::FromValue(
+ const base::Value& value,
+ base::string16* error) {
+ scoped_ptr<core_api::extensions_manifest_types::UsbPrinters> usb_printers =
+ core_api::extensions_manifest_types::UsbPrinters::FromValue(value, error);
+ if (!usb_printers) {
+ return nullptr;
+ }
+
+ scoped_ptr<UsbPrinterManifestData> result(new UsbPrinterManifestData());
+ for (const auto& input : usb_printers->filters) {
+ DCHECK(input.get());
+ device::UsbDeviceFilter output;
+ output.SetVendorId(input->vendor_id);
+ if (input->product_id && input->interface_class) {
+ *error = base::ASCIIToUTF16(
+ "Only one of productId or interfaceClass may be specified.");
+ return nullptr;
+ }
+ if (input->product_id) {
+ output.SetProductId(*input->product_id);
+ }
+ if (input->interface_class) {
+ output.SetInterfaceClass(*input->interface_class);
+ if (input->interface_subclass) {
+ output.SetInterfaceSubclass(*input->interface_subclass);
+ if (input->interface_protocol) {
+ output.SetInterfaceProtocol(*input->interface_protocol);
+ }
+ }
+ }
+ result->filters_.push_back(output);
+ }
+ return result.Pass();
+}
+
+} // namespace extensions
diff --git a/extensions/common/api/printer_provider/usb_printer_manifest_data.h b/extensions/common/api/printer_provider/usb_printer_manifest_data.h
new file mode 100644
index 0000000..fae6053
--- /dev/null
+++ b/extensions/common/api/printer_provider/usb_printer_manifest_data.h
@@ -0,0 +1,43 @@
+// Copyright 2015 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_COMMON_API_PRINTER_PROVIDER_USB_PRINTER_MANIFEST_DATA_H_
+#define EXTENSIONS_COMMON_API_PRINTER_PROVIDER_USB_PRINTER_MANIFEST_DATA_H_
+
+#include <vector>
+
+#include "extensions/common/extension.h"
+
+namespace device {
+class UsbDeviceFilter;
+}
+
+namespace extensions {
+
+// The parsed form of the "usb_printers" manifest entry.
+class UsbPrinterManifestData : public Extension::ManifestData {
+ public:
+ UsbPrinterManifestData();
+ ~UsbPrinterManifestData() override;
+
+ // Gets the UsbPrinterManifestData for |extension|, or NULL if none was
+ // specified.
+ static const UsbPrinterManifestData* Get(const Extension* extension);
+
+ // Parses the data stored in |value|. Sets |error| and returns an empty
+ // scoped_ptr on failure.
+ static scoped_ptr<UsbPrinterManifestData> FromValue(const base::Value& value,
+ base::string16* error);
+
+ const std::vector<device::UsbDeviceFilter>& filters() const {
+ return filters_;
+ }
+
+ private:
+ std::vector<device::UsbDeviceFilter> filters_;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_API_PRINTER_PROVIDER_USB_PRINTER_MANIFEST_DATA_H_
diff --git a/extensions/common/api/printer_provider/usb_printer_manifest_handler.cc b/extensions/common/api/printer_provider/usb_printer_manifest_handler.cc
new file mode 100644
index 0000000..e356050b
--- /dev/null
+++ b/extensions/common/api/printer_provider/usb_printer_manifest_handler.cc
@@ -0,0 +1,37 @@
+// Copyright 2015 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/common/api/printer_provider/usb_printer_manifest_handler.h"
+
+#include "extensions/common/api/printer_provider/usb_printer_manifest_data.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/manifest_constants.h"
+
+namespace extensions {
+
+UsbPrinterManifestHandler::UsbPrinterManifestHandler() {
+}
+
+UsbPrinterManifestHandler::~UsbPrinterManifestHandler() {
+}
+
+bool UsbPrinterManifestHandler::Parse(Extension* extension,
+ base::string16* error) {
+ const base::Value* usb_printers = nullptr;
+ CHECK(extension->manifest()->Get(manifest_keys::kUsbPrinters, &usb_printers));
+ scoped_ptr<UsbPrinterManifestData> data =
+ UsbPrinterManifestData::FromValue(*usb_printers, error);
+ if (!data) {
+ return false;
+ }
+
+ extension->SetManifestData(manifest_keys::kUsbPrinters, data.release());
+ return true;
+}
+
+const std::vector<std::string> UsbPrinterManifestHandler::Keys() const {
+ return SingleKey(manifest_keys::kUsbPrinters);
+}
+
+} // namespace extensions
diff --git a/extensions/common/api/printer_provider/usb_printer_manifest_handler.h b/extensions/common/api/printer_provider/usb_printer_manifest_handler.h
new file mode 100644
index 0000000..beccce2
--- /dev/null
+++ b/extensions/common/api/printer_provider/usb_printer_manifest_handler.h
@@ -0,0 +1,26 @@
+// Copyright 2015 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_COMMON_API_PRINTER_PROVIDER_USB_PRINTER_MANIFEST_HANDLER_H_
+#define EXTENSIONS_COMMON_API_PRINTER_PROVIDER_USB_PRINTER_MANIFEST_HANDLER_H_
+
+#include "extensions/common/manifest_handler.h"
+
+namespace extensions {
+
+// Parses the "usb_printers" manifest key.
+class UsbPrinterManifestHandler : public ManifestHandler {
+ public:
+ UsbPrinterManifestHandler();
+ ~UsbPrinterManifestHandler() override;
+
+ private:
+ // ManifestHandler overrides.
+ bool Parse(Extension* extension, base::string16* error) override;
+ const std::vector<std::string> Keys() const override;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_COMMON_API_PRINTER_PROVIDER_USB_PRINTER_MANIFEST_HANDLER_H_
diff --git a/extensions/common/api/printer_provider/usb_printer_manifest_unittest.cc b/extensions/common/api/printer_provider/usb_printer_manifest_unittest.cc
new file mode 100644
index 0000000..cd35af1
--- /dev/null
+++ b/extensions/common/api/printer_provider/usb_printer_manifest_unittest.cc
@@ -0,0 +1,45 @@
+// Copyright 2015 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 "device/usb/usb_device_filter.h"
+#include "extensions/common/api/printer_provider/usb_printer_manifest_data.h"
+#include "extensions/common/manifest_test.h"
+#include "extensions/common/value_builder.h"
+
+namespace extensions {
+
+class UsbPrinterManifestTest : public ManifestTest {
+ public:
+ UsbPrinterManifestTest() {}
+ ~UsbPrinterManifestTest() override {}
+};
+
+TEST_F(UsbPrinterManifestTest, Filters) {
+ scoped_refptr<Extension> extension =
+ LoadAndExpectSuccess("usb_printers_filters.json");
+ const UsbPrinterManifestData* manifest_data =
+ UsbPrinterManifestData::Get(extension.get());
+ ASSERT_TRUE(manifest_data);
+ EXPECT_EQ(2u, manifest_data->filters().size());
+ EXPECT_TRUE(DictionaryBuilder()
+ .Set("vendorId", 1)
+ .Set("productId", 2)
+ .Build()
+ ->Equals(manifest_data->filters()[0].ToValue().get()));
+ EXPECT_TRUE(DictionaryBuilder()
+ .Set("vendorId", 1)
+ .Set("interfaceClass", 2)
+ .Set("interfaceSubclass", 3)
+ .Set("interfaceProtocol", 4)
+ .Build()
+ ->Equals(manifest_data->filters()[1].ToValue().get()));
+}
+
+TEST_F(UsbPrinterManifestTest, InvalidFilter) {
+ LoadAndExpectError(
+ "usb_printers_invalid_filter.json",
+ "Only one of productId or interfaceClass may be specified.");
+}
+
+} // namespace extensions
diff --git a/extensions/common/common_manifest_handlers.cc b/extensions/common/common_manifest_handlers.cc
index db137ffc..3331a0d5 100644
--- a/extensions/common/common_manifest_handlers.cc
+++ b/extensions/common/common_manifest_handlers.cc
@@ -5,6 +5,7 @@
#include "extensions/common/common_manifest_handlers.h"
#include "extensions/common/api/bluetooth/bluetooth_manifest_handler.h"
+#include "extensions/common/api/printer_provider/usb_printer_manifest_handler.h"
#include "extensions/common/api/sockets/sockets_manifest_handler.h"
#include "extensions/common/manifest_handler.h"
#include "extensions/common/manifest_handlers/background_info.h"
@@ -51,6 +52,7 @@
(new SandboxedPageHandler)->Register();
(new SharedModuleHandler)->Register();
(new SocketsManifestHandler)->Register();
+ (new UsbPrinterManifestHandler)->Register();
(new WebAccessibleResourcesHandler)->Register();
(new WebviewHandler)->Register();
}
diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc
index 528307b..534cc5b 100644
--- a/extensions/common/manifest_constants.cc
+++ b/extensions/common/manifest_constants.cc
@@ -174,6 +174,7 @@
const char kUpdateURL[] = "update_url";
const char kUrlHandlers[] = "url_handlers";
const char kUrlHandlerTitle[] = "title";
+const char kUsbPrinters[] = "usb_printers";
const char kVersion[] = "version";
const char kVersionName[] = "version_name";
const char kWebAccessibleResources[] = "web_accessible_resources";
diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h
index fa587015..bed5b91a 100644
--- a/extensions/common/manifest_constants.h
+++ b/extensions/common/manifest_constants.h
@@ -175,6 +175,7 @@
extern const char kUpdateURL[];
extern const char kUrlHandlers[];
extern const char kUrlHandlerTitle[];
+extern const char kUsbPrinters[];
extern const char kVersion[];
extern const char kVersionName[];
extern const char kWebAccessibleResources[];
diff --git a/extensions/extensions.gypi b/extensions/extensions.gypi
index 106fc44..3ec8a4d 100644
--- a/extensions/extensions.gypi
+++ b/extensions/extensions.gypi
@@ -20,6 +20,10 @@
'common/api/bluetooth/bluetooth_manifest_permission.cc',
'common/api/bluetooth/bluetooth_manifest_permission.h',
'common/api/messaging/message.h',
+ 'common/api/printer_provider/usb_printer_manifest_data.cc',
+ 'common/api/printer_provider/usb_printer_manifest_data.h',
+ 'common/api/printer_provider/usb_printer_manifest_handler.cc',
+ 'common/api/printer_provider/usb_printer_manifest_handler.h',
'common/api/sockets/sockets_manifest_data.cc',
'common/api/sockets/sockets_manifest_data.h',
'common/api/sockets/sockets_manifest_handler.cc',
diff --git a/extensions/extensions_tests.gypi b/extensions/extensions_tests.gypi
index deb11cc..9220e91 100644
--- a/extensions/extensions_tests.gypi
+++ b/extensions/extensions_tests.gypi
@@ -101,6 +101,7 @@
'browser/value_store/value_store_unittest.h',
'browser/verified_contents_unittest.cc',
'browser/warning_service_unittest.cc',
+ 'common/api/printer_provider/usb_printer_manifest_unittest.cc',
'common/api/sockets/sockets_manifest_permission_unittest.cc',
'common/csp_validator_unittest.cc',
'common/event_filter_unittest.cc',
diff --git a/extensions/test/data/manifest_tests/usb_printers_filters.json b/extensions/test/data/manifest_tests/usb_printers_filters.json
new file mode 100644
index 0000000..673c684
--- /dev/null
+++ b/extensions/test/data/manifest_tests/usb_printers_filters.json
@@ -0,0 +1,21 @@
+{
+ "name": "extensions_unittests --gtest_filter=UsbPrinterManifestTest.InvalidFilter",
+ "version": "1",
+ "manifest_version": 2,
+ "app": {
+ "background": {
+ "scripts": ["background.js"]
+ }
+ },
+ "usb_printers": {
+ "filters": [
+ { "vendorId": 1, "productId": 2 },
+ {
+ "vendorId": 1,
+ "interfaceClass": 2,
+ "interfaceSubclass": 3,
+ "interfaceProtocol": 4
+ }
+ ]
+ }
+}
diff --git a/extensions/test/data/manifest_tests/usb_printers_invalid_filter.json b/extensions/test/data/manifest_tests/usb_printers_invalid_filter.json
new file mode 100644
index 0000000..e117779
--- /dev/null
+++ b/extensions/test/data/manifest_tests/usb_printers_invalid_filter.json
@@ -0,0 +1,16 @@
+{
+ "name": "extensions_unittests --gtest_filter=UsbPrinterManifestTest.InvalidFilter",
+ "version": "1",
+ "manifest_version": 2,
+ "app": {
+ "background": {
+ "scripts": ["background.js"]
+ }
+ },
+ "usb_printers": {
+ "filters": [
+ // Cannot specify both productId and interfaceClass.
+ { "vendorId": 0, "productId": 0, "interfaceClass": 0 }
+ ]
+ }
+}