summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/app/generated_resources.grd17
-rw-r--r--chrome/app/theme/default_100_percent/cros/printer_notification.pngbin0 -> 214 bytes
-rw-r--r--chrome/app/theme/default_200_percent/cros/printer_notification.pngbin0 -> 365 bytes
-rw-r--r--chrome/app/theme/theme_resources.grd3
-rw-r--r--chrome/browser/chromeos/dbus/printer_service_provider.cc247
-rw-r--r--chrome/browser/chromeos/dbus/printer_service_provider.h8
-rw-r--r--chrome/browser/chromeos/dbus/printer_service_provider_unittest.cc271
-rw-r--r--chrome/browser/chromeos/profiles/profile_helper.h1
-rw-r--r--chromeos/chromeos_switches.cc6
-rw-r--r--chromeos/chromeos_switches.h1
10 files changed, 541 insertions, 13 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 213503f..b839372 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -15901,6 +15901,23 @@ Do you accept?
<message name="IDS_ENABLE_INVALID_CERT_COLLECTION_DESCRIPTION" desc="Description of the 'Enable invalid certificate collection' flag.">
Allows users to opt in to the collection of invalid TLS/SSL certificate chains.
</message>
+
+ <!-- Printer detected notification -->
+ <if expr="chromeos">
+ <message name="IDS_PRINTER_DETECTED_NOTIFICATION_TITLE" desc="Title for notification shown to the user when a printer gets plugged in to a Chrome OS device.">
+ Printer from <ph name="VENDOR_NAME">$1<ex>Google</ex></ph> detected
+ </message>
+ <message name="IDS_PRINTER_DETECTED_NOTIFICATION_TITLE_UNKNOWN_VENDOR" desc="Title for notification shown to the user when a printer gets plugged in to a Chrome OS device in the case the printer manufacturer name is unknown.">
+ Printer detected
+ </message>
+ <message name="IDS_PRINTER_DETECTED_NOTIFICATION_NO_PRINT_APP_BODY" desc="Content for notification shown to the user when a printer gets plugged in to a Chrome OS device if the active user does not have an app that supports the printer installed.">
+ Click to find a print app to use this printer.
+ </message>
+ <message name="IDS_PRINTER_DETECTED_NOTIFICATION_PRINT_APP_FOUND_BODY" desc="Content for notification shown to the user when a printer gets plugged in to a Chrome OS device if the active user already has a print app that supports the printer installed.">
+ Your printer is ready.
+ </message>
+ </if>
+
</messages>
</release>
</grit>
diff --git a/chrome/app/theme/default_100_percent/cros/printer_notification.png b/chrome/app/theme/default_100_percent/cros/printer_notification.png
new file mode 100644
index 0000000..6e14c55
--- /dev/null
+++ b/chrome/app/theme/default_100_percent/cros/printer_notification.png
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/cros/printer_notification.png b/chrome/app/theme/default_200_percent/cros/printer_notification.png
new file mode 100644
index 0000000..2b1da79
--- /dev/null
+++ b/chrome/app/theme/default_200_percent/cros/printer_notification.png
Binary files differ
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index a1f7fd8..d4a1323 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -633,6 +633,9 @@
<structure type="chrome_scaled_image" name="IDR_PHONE_FAVICON" file="common/favicon_phone.png" />
<structure type="chrome_scaled_image" name="IDR_PLUGINS_FAVICON" file="common/favicon_extensions.png" />
<structure type="chrome_scaled_image" name="IDR_PRERENDER" file="common/prerender_succeed_icon.png" />
+ <if expr="chromeos">
+ <structure type="chrome_scaled_image" name="IDR_PRINTER_NOTIFICATION" file="cros/printer_notification.png" />
+ </if>
<if expr="not _google_chrome">
<structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO" file="chromium/product_logo.png" />
<structure type="chrome_scaled_image" name="IDR_PRODUCT_LOGO_16" file="chromium/product_logo_16.png" />
diff --git a/chrome/browser/chromeos/dbus/printer_service_provider.cc b/chrome/browser/chromeos/dbus/printer_service_provider.cc
index c70305e..6310dd2 100644
--- a/chrome/browser/chromeos/dbus/printer_service_provider.cc
+++ b/chrome/browser/chromeos/dbus/printer_service_provider.cc
@@ -4,14 +4,28 @@
#include "chrome/browser/chromeos/dbus/printer_service_provider.h"
+#include <stdint.h>
+
+#include <limits>
+
#include "ash/session/session_state_delegate.h"
#include "ash/shell.h"
#include "ash/wm/window_util.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/sys_info.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/notifications/notification.h"
+#include "chrome/browser/notifications/notification_delegate.h"
+#include "chrome/browser/notifications/notification_ui_manager.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
@@ -20,19 +34,39 @@
#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_version_info.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/chromeos_switches.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "dbus/bus.h"
#include "dbus/exported_object.h"
#include "dbus/message.h"
+#include "device/usb/usb_ids.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_set.h"
+#include "extensions/common/permissions/api_permission.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "extensions/common/permissions/usb_device_permission.h"
+#include "grit/theme_resources.h"
#include "net/base/escape.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
#include "ui/aura/window.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
namespace {
const char kPrinterAdded[] = "PrinterAdded";
+const char kPrinterProviderFoundNotificationID[] =
+ "chrome://settings/printer/printer_app_found";
+
+const char kNoPrinterProviderNotificationID[] =
+ "chrome://settings/printer/no_printer_app";
+
enum PrinterServiceEvent {
PRINTER_ADDED,
PAGE_DISPLAYED,
@@ -55,7 +89,16 @@ Browser* ActivateAndGetBrowserForUrl(GURL url) {
return it.browser();
}
}
- return NULL;
+ return nullptr;
+}
+
+bool HexStringToUInt16(const std::string& input, uint16* output) {
+ uint32 output_uint = 0;
+ if (!base::HexStringToUInt(input, &output_uint) ||
+ output_uint > std::numeric_limits<uint16>::max())
+ return false;
+ *output = static_cast<uint16>(output_uint);
+ return true;
}
void FindOrOpenCloudPrintPage(const std::string& /* vendor */,
@@ -84,12 +127,173 @@ void FindOrOpenCloudPrintPage(const std::string& /* vendor */,
}
}
+base::string16 GetNotificationTitle(uint16 vendor_id, uint16 product_id) {
+ const char* vendor_name = device::UsbIds::GetVendorName(vendor_id);
+ if (vendor_name) {
+ return l10n_util::GetStringFUTF16(IDS_PRINTER_DETECTED_NOTIFICATION_TITLE,
+ base::UTF8ToUTF16(vendor_name));
+ } else {
+ return l10n_util::GetStringUTF16(
+ IDS_PRINTER_DETECTED_NOTIFICATION_TITLE_UNKNOWN_VENDOR);
+ }
+}
+
+std::string GetNotificationTag(const std::string& vendor_id,
+ const std::string& product_id) {
+ return vendor_id + ":" + product_id;
+}
+
+// Checks if there is an enabled extension with printerProvider permission and
+// usbDevices persmission for the USB (vendor_id, product_id) pair.
+bool HasAppThatSupportsPrinter(Profile* profile,
+ uint16 vendor_id,
+ uint16 product_id) {
+ const extensions::ExtensionSet& enabled_extensions =
+ extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
+ for (const auto& extension : enabled_extensions) {
+ if (!extension->permissions_data() ||
+ !extension->permissions_data()->HasAPIPermission(
+ extensions::APIPermission::kPrinterProvider) ||
+ !extension->permissions_data()->HasAPIPermission(
+ extensions::APIPermission::kUsb)) {
+ continue;
+ }
+
+ extensions::UsbDevicePermission::CheckParam param(
+ vendor_id, product_id,
+ extensions::UsbDevicePermissionData::UNSPECIFIED_INTERFACE);
+ if (extension->permissions_data()->CheckAPIPermissionWithParam(
+ extensions::APIPermission::kUsbDevice, &param)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Delegate for notification shown when a printer provider app for the plugged
+// in printer is found.
+class PrinterProviderExistsNotificationDelegate : public NotificationDelegate {
+ public:
+ PrinterProviderExistsNotificationDelegate(const std::string& vendor_id,
+ const std::string& product_id)
+ : vendor_id_(vendor_id), product_id_(product_id) {}
+
+ std::string id() const override {
+ return "system.printer.printer_provider_exists/" +
+ GetNotificationTag(vendor_id_, product_id_);
+ }
+
+ private:
+ ~PrinterProviderExistsNotificationDelegate() override = default;
+
+ const std::string vendor_id_;
+ const std::string product_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrinterProviderExistsNotificationDelegate);
+};
+
+// Delegate for notification shown when there are no printer provider apps that
+// support the plugged in printer found.
+// The notification is clickable, and clicking it is supposed to launch
+// Chrome Web Store widget listing apps that can support the plugged in printer.
+// (not implemented yet).
+class SearchPrinterAppNotificationDelegate : public NotificationDelegate {
+ public:
+ SearchPrinterAppNotificationDelegate(const std::string& vendor_id,
+ const std::string& product_id)
+ : vendor_id_(vendor_id), product_id_(product_id) {}
+
+ std::string id() const override {
+ return "system.printer.no_printer_provider_found/" +
+ GetNotificationTag(vendor_id_, product_id_);
+ }
+ bool HasClickedListener() override { return true; }
+
+ void Click() override {
+ // TODO(tbarzic): Implement this (http://crbug.com/439448).
+ }
+
+ private:
+ ~SearchPrinterAppNotificationDelegate() override = default;
+
+ std::string vendor_id_;
+ std::string product_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(SearchPrinterAppNotificationDelegate);
+};
+
+// Shows a notification for a plugged in printer.
+// If there is a printerProvider app that handles the printer's USB (vendor_id,
+// product_id) pair, the notification informs the user that the printer is ready
+// to be used, otherwise it offers the user to search the Chrome Web Store for
+// an app that can handle the printer.
+void ShowPrinterPluggedNotification(
+ NotificationUIManager* notification_ui_manager,
+ const std::string& vendor_id_str,
+ const std::string& product_id_str) {
+ uint16 vendor_id = 0;
+ uint16 product_id = 0;
+ if (!HexStringToUInt16(vendor_id_str, &vendor_id) ||
+ !HexStringToUInt16(product_id_str, &product_id)) {
+ LOG(WARNING) << "Invalid USB ID " << vendor_id_str << ":" << product_id_str;
+ return;
+ }
+
+ const user_manager::User* user =
+ user_manager::UserManager::Get()
+ ? user_manager::UserManager::Get()->GetActiveUser()
+ : nullptr;
+ if (!user || !user->HasGaiaAccount())
+ return;
+
+ Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUser(user);
+ if (!profile)
+ return;
+
+ ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+ scoped_ptr<Notification> notification;
+
+ if (HasAppThatSupportsPrinter(profile, vendor_id, product_id)) {
+ notification.reset(new Notification(
+ message_center::NOTIFICATION_TYPE_SIMPLE,
+ GURL(kPrinterProviderFoundNotificationID),
+ GetNotificationTitle(vendor_id, product_id),
+ l10n_util::GetStringUTF16(
+ IDS_PRINTER_DETECTED_NOTIFICATION_PRINT_APP_FOUND_BODY),
+ bundle.GetImageNamed(IDR_PRINTER_NOTIFICATION),
+ message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
+ kPrinterProviderFoundNotificationID),
+ base::string16(), GetNotificationTag(vendor_id_str, product_id_str),
+ message_center::RichNotificationData(),
+ new PrinterProviderExistsNotificationDelegate(vendor_id_str,
+ product_id_str)));
+ } else {
+ message_center::RichNotificationData options;
+ options.clickable = true;
+ notification.reset(new Notification(
+ message_center::NOTIFICATION_TYPE_SIMPLE,
+ GURL(kNoPrinterProviderNotificationID),
+ GetNotificationTitle(vendor_id, product_id),
+ l10n_util::GetStringUTF16(
+ IDS_PRINTER_DETECTED_NOTIFICATION_NO_PRINT_APP_BODY),
+ bundle.GetImageNamed(IDR_PRINTER_NOTIFICATION),
+ message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
+ kNoPrinterProviderNotificationID),
+ base::string16(), GetNotificationTag(vendor_id_str, product_id_str),
+ options, new SearchPrinterAppNotificationDelegate(vendor_id_str,
+ product_id_str)));
+ }
+
+ notification->SetSystemPriority();
+ notification_ui_manager->Add(*notification, profile);
+}
+
} // namespace
namespace chromeos {
PrinterServiceProvider::PrinterServiceProvider()
- : weak_ptr_factory_(this) {
+ : notification_ui_manager_(nullptr), weak_ptr_factory_(this) {
}
PrinterServiceProvider::~PrinterServiceProvider() {
@@ -109,6 +313,11 @@ void PrinterServiceProvider::Start(
weak_ptr_factory_.GetWeakPtr()));
}
+void PrinterServiceProvider::SetNotificationUIManagerForTesting(
+ NotificationUIManager* manager) {
+ notification_ui_manager_ = manager;
+}
+
void PrinterServiceProvider::OnExported(
const std::string& interface_name,
const std::string& method_name,
@@ -132,6 +341,28 @@ void PrinterServiceProvider::PrinterAdded(
dbus::ExportedObject::ResponseSender response_sender) {
DVLOG(1) << "PrinterAdded " << method_call->ToString();
+ dbus::MessageReader reader(method_call);
+
+ std::string vendor_id;
+ reader.PopString(&vendor_id);
+ StringToUpperASCII(&vendor_id);
+
+ std::string product_id;
+ reader.PopString(&product_id);
+ StringToUpperASCII(&product_id);
+
+ // Send an empty response.
+ response_sender.Run(dbus::Response::FromMethodCall(method_call));
+
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnablePrinterAppSearch)) {
+ ShowPrinterPluggedNotification(
+ notification_ui_manager_ ? notification_ui_manager_
+ : g_browser_process->notification_ui_manager(),
+ vendor_id, product_id);
+ return;
+ }
+
// Disable showing Cloudprint help on canary and dev channel, as these have
// support for printerProvider API.
// TODO(tbarzic): Remove this and offer the user to search for an extension
@@ -141,17 +372,7 @@ void PrinterServiceProvider::PrinterAdded(
chrome::VersionInfo::GetChannel() <= chrome::VersionInfo::CHANNEL_DEV)
return;
- dbus::MessageReader reader(method_call);
- std::string vendor;
- std::string product;
- // Don't check for error, parameters are optional. If some string is empty
- // web server will show generic help page.
- reader.PopString(&vendor);
- reader.PopString(&product);
- ShowCloudPrintHelp(vendor, product);
-
- // Send an empty response.
- response_sender.Run(dbus::Response::FromMethodCall(method_call));
+ ShowCloudPrintHelp(vendor_id, product_id);
}
} // namespace chromeos
diff --git a/chrome/browser/chromeos/dbus/printer_service_provider.h b/chrome/browser/chromeos/dbus/printer_service_provider.h
index 19163f2..68c1cff 100644
--- a/chrome/browser/chromeos/dbus/printer_service_provider.h
+++ b/chrome/browser/chromeos/dbus/printer_service_provider.h
@@ -11,6 +11,8 @@
#include "chromeos/dbus/services/cros_dbus_service.h"
#include "dbus/exported_object.h"
+class NotificationUIManager;
+
namespace dbus {
class MethodCall;
class Response;
@@ -57,6 +59,10 @@ class PrinterServiceProvider
const std::string& product);
private:
+ friend class PrinterServiceProviderAppSearchEnabledTest;
+
+ void SetNotificationUIManagerForTesting(NotificationUIManager* manager);
+
// Called from ExportedObject, when PrinterAdded() is exported as
// a D-Bus method, or failed to be exported.
void OnExported(const std::string& interface_name,
@@ -68,6 +74,8 @@ class PrinterServiceProvider
void PrinterAdded(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
+ NotificationUIManager* notification_ui_manager_;
+
scoped_refptr<dbus::ExportedObject> exported_object_;
base::WeakPtrFactory<PrinterServiceProvider> weak_ptr_factory_;
diff --git a/chrome/browser/chromeos/dbus/printer_service_provider_unittest.cc b/chrome/browser/chromeos/dbus/printer_service_provider_unittest.cc
index ae24e5c..7dd3e99 100644
--- a/chrome/browser/chromeos/dbus/printer_service_provider_unittest.cc
+++ b/chrome/browser/chromeos/dbus/printer_service_provider_unittest.cc
@@ -4,14 +4,35 @@
#include "chrome/browser/chromeos/dbus/printer_service_provider.h"
+#include "base/command_line.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/notifications/notification.h"
+#include "chrome/browser/notifications/notification_test_util.h"
+#include "chrome/browser/notifications/notification_ui_manager.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/services/service_provider_test_helper.h"
+#include "components/user_manager/fake_user_manager.h"
#include "dbus/message.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/common/value_builder.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
const char kPrinterAdded[] = "PrinterAdded";
+const char kTestUserId[] = "test_user";
+
+const char kPrinterAppExistsDelegateIDTemplate[] =
+ "system.printer.printer_provider_exists/%s:%s";
+
+const char kPrinterAppNotFoundDelegateIDTemplate[] =
+ "system.printer.no_printer_provider_found/%s:%s";
+
class MockPrinterServiceProvider : public PrinterServiceProvider {
public:
MOCK_METHOD2(ShowCloudPrintHelp,
@@ -31,6 +52,96 @@ class PrinterServiceProviderTest : public testing::Test {
ServiceProviderTestHelper test_helper_;
};
+class PrinterServiceProviderAppSearchEnabledTest : public testing::Test {
+ public:
+ PrinterServiceProviderAppSearchEnabledTest()
+ : user_manager_(new user_manager::FakeUserManager()),
+ user_manager_enabler_(user_manager_) {}
+
+ ~PrinterServiceProviderAppSearchEnabledTest() override = default;
+
+ void SetUp() override {
+ base::CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnablePrinterAppSearch);
+ EXPECT_CALL(service_provider_, ShowCloudPrintHelp(testing::_, testing::_))
+ .Times(0);
+ service_provider_.SetNotificationUIManagerForTesting(
+ &notification_ui_manager_);
+ }
+
+ protected:
+ void AddTestUser() {
+ const user_manager::User* user = user_manager_->AddUser(kTestUserId);
+ profile_.set_profile_name(kTestUserId);
+ chromeos::ProfileHelper::Get()->SetUserToProfileMappingForTesting(
+ user, &profile_);
+ }
+
+ bool InvokePrinterAdded(const std::string& vendor_id,
+ const std::string& product_id,
+ std::string* error) {
+ *error = std::string();
+ test_helper_.SetUp(kPrinterAdded, &service_provider_);
+
+ dbus::MethodCall method_call(kLibCrosServiceInterface, kPrinterAdded);
+ dbus::MessageWriter writer(&method_call);
+ writer.AppendString(vendor_id);
+ writer.AppendString(product_id);
+
+ // Call the PrinterAdded method.
+ scoped_ptr<dbus::Response> response(test_helper_.CallMethod(&method_call));
+
+ // An empty response should be returned.
+ bool success = true;
+ if (response.get()) {
+ dbus::MessageReader reader(response.get());
+ if (reader.HasMoreData()) {
+ *error = "Non empty response";
+ success = false;
+ }
+ } else {
+ *error = "No response.";
+ success = false;
+ }
+
+ // Tear down the test helper so it can be reused in the test.
+ test_helper_.TearDown();
+ return success;
+ }
+
+ // Creates a test extension with the provided permissions.
+ scoped_refptr<extensions::Extension> CreateTestExtension(
+ extensions::ListBuilder* permissions_builder) {
+ return extensions::ExtensionBuilder()
+ .SetID("fake_extension_id")
+ .SetManifest(
+ extensions::DictionaryBuilder()
+ .Set("name", "Printer provider extension")
+ .Set("manifest_version", 2)
+ .Set("version", "1.0")
+ // Needed to enable usb API.
+ .Set("app",
+ extensions::DictionaryBuilder().Set(
+ "background",
+ extensions::DictionaryBuilder().Set(
+ "scripts",
+ extensions::ListBuilder().Append("bg.js"))))
+ .Set("permissions", *permissions_builder))
+ .Build();
+ }
+
+ StubNotificationUIManager notification_ui_manager_;
+ TestingProfile profile_;
+ user_manager::FakeUserManager* user_manager_;
+ chromeos::ScopedUserManagerEnabler user_manager_enabler_;
+
+ MockPrinterServiceProvider service_provider_;
+ ServiceProviderTestHelper test_helper_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrinterServiceProviderAppSearchEnabledTest);
+};
+
TEST_F(PrinterServiceProviderTest, ShowCloudPrintHelp) {
dbus::MethodCall method_call(kLibCrosServiceInterface, kPrinterAdded);
dbus::MessageWriter writer(&method_call);
@@ -49,5 +160,165 @@ TEST_F(PrinterServiceProviderTest, ShowCloudPrintHelp) {
ASSERT_FALSE(reader.HasMoreData());
}
+TEST_F(PrinterServiceProviderAppSearchEnabledTest, ShowFindAppNotification) {
+ AddTestUser();
+
+ std::string error;
+ ASSERT_TRUE(InvokePrinterAdded("123", "456", &error)) << error;
+
+ ASSERT_EQ(1u, notification_ui_manager_.GetNotificationCount());
+ const Notification& notification =
+ notification_ui_manager_.GetNotificationAt(0);
+ EXPECT_EQ("123:456", notification.tag());
+ EXPECT_EQ(
+ base::StringPrintf(kPrinterAppNotFoundDelegateIDTemplate, "123", "456"),
+ notification.delegate_id());
+}
+
+TEST_F(PrinterServiceProviderAppSearchEnabledTest, ShowAppFoundNotification) {
+ AddTestUser();
+
+ scoped_refptr<extensions::Extension> extension = CreateTestExtension(
+ &extensions::ListBuilder()
+ .Append("usb")
+ .Append("printerProvider")
+ .Append(extensions::DictionaryBuilder().Set(
+ "usbDevices", extensions::ListBuilder().Append(
+ extensions::DictionaryBuilder()
+ .Set("vendorId", 0x123)
+ .Set("productId", 0x456)))));
+ ASSERT_TRUE(
+ extensions::ExtensionRegistry::Get(&profile_)->AddEnabled(extension));
+
+ std::string error;
+ ASSERT_TRUE(InvokePrinterAdded("123", "456", &error)) << error;
+
+ ASSERT_EQ(1u, notification_ui_manager_.GetNotificationCount());
+ const Notification& notification =
+ notification_ui_manager_.GetNotificationAt(0);
+ EXPECT_EQ("123:456", notification.tag());
+ EXPECT_EQ(
+ base::StringPrintf(kPrinterAppExistsDelegateIDTemplate, "123", "456"),
+ notification.delegate_id());
+}
+
+TEST_F(PrinterServiceProviderAppSearchEnabledTest,
+ UsbHandlerExists_NotPrinterProvider) {
+ AddTestUser();
+
+ scoped_refptr<extensions::Extension> extension =
+ CreateTestExtension(&extensions::ListBuilder().Append("usb").Append(
+ extensions::DictionaryBuilder().Set(
+ "usbDevices",
+ extensions::ListBuilder().Append(extensions::DictionaryBuilder()
+ .Set("vendorId", 0x123)
+ .Set("productId", 0xf56)))));
+ ASSERT_TRUE(
+ extensions::ExtensionRegistry::Get(&profile_)->AddEnabled(extension));
+
+ std::string error;
+ ASSERT_TRUE(InvokePrinterAdded("123", "f56", &error)) << error;
+
+ ASSERT_EQ(1u, notification_ui_manager_.GetNotificationCount());
+ const Notification& notification =
+ notification_ui_manager_.GetNotificationAt(0);
+ EXPECT_EQ("123:F56", notification.tag());
+ EXPECT_EQ(
+ base::StringPrintf(kPrinterAppNotFoundDelegateIDTemplate, "123", "F56"),
+ notification.delegate_id());
+}
+
+TEST_F(PrinterServiceProviderAppSearchEnabledTest,
+ PrinterProvider_DifferentUsbProductId) {
+ AddTestUser();
+
+ scoped_refptr<extensions::Extension> extension = CreateTestExtension(
+ &extensions::ListBuilder()
+ .Append("usb")
+ .Append("printerProvider")
+ .Append(extensions::DictionaryBuilder().Set(
+ "usbDevices", extensions::ListBuilder().Append(
+ extensions::DictionaryBuilder()
+ .Set("vendorId", 0x123)
+ .Set("productId", 0x001)))));
+ ASSERT_TRUE(
+ extensions::ExtensionRegistry::Get(&profile_)->AddEnabled(extension));
+
+ std::string error;
+ ASSERT_TRUE(InvokePrinterAdded("123", "456", &error)) << error;
+
+ ASSERT_EQ(1u, notification_ui_manager_.GetNotificationCount());
+ const Notification& notification =
+ notification_ui_manager_.GetNotificationAt(0);
+ EXPECT_EQ("123:456", notification.tag());
+ EXPECT_EQ(
+ base::StringPrintf(kPrinterAppNotFoundDelegateIDTemplate, "123", "456"),
+ notification.delegate_id());
+}
+
+TEST_F(PrinterServiceProviderAppSearchEnabledTest, VendorIdOutOfBounds) {
+ AddTestUser();
+
+ std::string error;
+ ASSERT_TRUE(InvokePrinterAdded("1F123", "456", &error)) << error;
+
+ EXPECT_EQ(0u, notification_ui_manager_.GetNotificationCount());
+}
+
+TEST_F(PrinterServiceProviderAppSearchEnabledTest, ProductIdNaN) {
+ AddTestUser();
+
+ std::string error;
+ ASSERT_TRUE(InvokePrinterAdded("123", "xxx", &error)) << error;
+
+ EXPECT_EQ(0u, notification_ui_manager_.GetNotificationCount());
+}
+
+TEST_F(PrinterServiceProviderAppSearchEnabledTest, VendorIdNaN) {
+ AddTestUser();
+
+ std::string error;
+ ASSERT_TRUE(InvokePrinterAdded("xxxfoo", "456", &error)) << error;
+
+ EXPECT_EQ(0u, notification_ui_manager_.GetNotificationCount());
+}
+
+TEST_F(PrinterServiceProviderAppSearchEnabledTest, ProductIdOutOfBounds) {
+ AddTestUser();
+
+ std::string error;
+ ASSERT_TRUE(InvokePrinterAdded("123", "1F456", &error)) << error;
+
+ EXPECT_EQ(0u, notification_ui_manager_.GetNotificationCount());
+}
+
+TEST_F(PrinterServiceProviderAppSearchEnabledTest, NegativeProductId) {
+ AddTestUser();
+
+ std::string error;
+ ASSERT_TRUE(InvokePrinterAdded("123", "-1", &error)) << error;
+
+ EXPECT_EQ(0u, notification_ui_manager_.GetNotificationCount());
+}
+
+TEST_F(PrinterServiceProviderAppSearchEnabledTest, PrintersAddedWithNoIdArgs) {
+ AddTestUser();
+
+ test_helper_.SetUp(kPrinterAdded, &service_provider_);
+ dbus::MethodCall method_call(kLibCrosServiceInterface, kPrinterAdded);
+ dbus::MessageWriter writer(&method_call);
+
+ // Call the PrinterAdded method.
+ scoped_ptr<dbus::Response> response(test_helper_.CallMethod(&method_call));
+
+ // An empty response should be returned.
+ ASSERT_TRUE(response.get());
+ dbus::MessageReader reader(response.get());
+ ASSERT_FALSE(reader.HasMoreData());
+ test_helper_.TearDown();
+
+ EXPECT_EQ(0u, notification_ui_manager_.GetNotificationCount());
+}
+
} // namespace chromeos
diff --git a/chrome/browser/chromeos/profiles/profile_helper.h b/chrome/browser/chromeos/profiles/profile_helper.h
index 4223666..18a0239 100644
--- a/chrome/browser/chromeos/profiles/profile_helper.h
+++ b/chrome/browser/chromeos/profiles/profile_helper.h
@@ -134,6 +134,7 @@ class ProfileHelper
friend class KioskTest;
friend class MockUserManager;
friend class MultiProfileUserControllerTest;
+ friend class PrinterServiceProviderAppSearchEnabledTest;
friend class ProfileHelperTest;
friend class ProfileListChromeOSTest;
friend class SessionStateDelegateChromeOSTest;
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index a705ae7..7b16766 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -336,6 +336,12 @@ const char kDisableWebviewSigninFlow[] = "disable-webview-signin-flow";
// Enable Chrome OS firewall hole-punching for Chrome Apps.
const char kEnableFirewallHolePunching[] = "enable-firewall-hole-punching";
+// Enables searching for an app that supports a plugged in USB printer. When a
+// user plugs in USB printer, they are shown a notification offering to search
+// Chroem Web Store for an app that has printerProvider permission and can
+// handle the plugged in printer.
+const char kEnablePrinterAppSearch[] = "enable-printer-app-search";
+
bool WakeOnWifiEnabled() {
return !base::CommandLine::ForCurrentProcess()->HasSwitch(kDisableWakeOnWifi);
}
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index 4adab58..4c03f1d 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -66,6 +66,7 @@ CHROMEOS_EXPORT extern const char kEnableKioskMode[];
CHROMEOS_EXPORT extern const char kEnableNetworkPortalNotification[];
CHROMEOS_EXPORT extern const char kEnableNewKoreanIme[];
CHROMEOS_EXPORT extern const char kEnablePhysicalKeyboardAutocorrect[];
+CHROMEOS_EXPORT extern const char kEnablePrinterAppSearch[];
CHROMEOS_EXPORT extern const char kEnableRequestTabletSite[];
CHROMEOS_EXPORT extern const char kEnableScreenshotTestingWithMode[];
CHROMEOS_EXPORT extern const char kEnableTouchpadThreeFingerClick[];