diff options
Diffstat (limited to 'chrome/browser/extensions')
3 files changed, 253 insertions, 9 deletions
diff --git a/chrome/browser/extensions/api/gcd_private/gcd_private_api.cc b/chrome/browser/extensions/api/gcd_private/gcd_private_api.cc index b17b984..5d3dba1 100644 --- a/chrome/browser/extensions/api/gcd_private/gcd_private_api.cc +++ b/chrome/browser/extensions/api/gcd_private/gcd_private_api.cc @@ -9,6 +9,7 @@ #include "chrome/browser/local_discovery/cloud_device_list.h" #include "chrome/browser/local_discovery/cloud_print_printer_list.h" #include "chrome/browser/local_discovery/gcd_constants.h" +#include "chrome/browser/local_discovery/privet_device_lister_impl.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/signin/signin_manager_factory.h" @@ -18,14 +19,25 @@ namespace extensions { -using extensions::api::gcd_private::GCDDevice; +namespace gcd_private = api::gcd_private; namespace { +scoped_ptr<Event> MakeCloudDeviceStateChangedEvent( + bool available, + const gcd_private::GCDDevice& device) { + scoped_ptr<base::ListValue> params = + gcd_private::OnCloudDeviceStateChanged::Create(available, device); + scoped_ptr<Event> event(new Event( + gcd_private::OnCloudDeviceStateChanged::kEventName, params.Pass())); + return event.Pass(); +} + const int kNumRequestsNeeded = 2; const char kIDPrefixCloudPrinter[] = "cloudprint:"; const char kIDPrefixGcd[] = "gcd:"; +const char kIDPrefixMdns[] = "mdns:"; GcdPrivateAPI::GCDApiFlowFactoryForTests* g_gcd_api_flow_factory = NULL; @@ -54,7 +66,10 @@ scoped_ptr<local_discovery::GCDApiFlow> MakeGCDApiFlow(Profile* profile) { } // namespace GcdPrivateAPI::GcdPrivateAPI(content::BrowserContext* context) - : browser_context_(context) { + : num_device_listeners_(0), browser_context_(context) { + DCHECK(browser_context_); + EventRouter::Get(context)->RegisterObserver( + this, gcd_private::OnCloudDeviceStateChanged::kEventName); } GcdPrivateAPI::~GcdPrivateAPI() { @@ -66,6 +81,74 @@ GcdPrivateAPI::GetFactoryInstance() { return g_factory.Pointer(); } +void GcdPrivateAPI::OnListenerAdded(const EventListenerInfo& details) { + num_device_listeners_++; + + if (num_device_listeners_ == 1) { + service_discovery_client_ = + local_discovery::ServiceDiscoverySharedClient::GetInstance(); + privet_device_lister_.reset(new local_discovery::PrivetDeviceListerImpl( + service_discovery_client_.get(), this)); + privet_device_lister_->Start(); + } + + for (GCDDeviceMap::iterator i = known_devices_.begin(); + i != known_devices_.end(); + i++) { + EventRouter::Get(browser_context_)->DispatchEventToExtension( + details.extension_id, + MakeCloudDeviceStateChangedEvent(true, *i->second)); + } +} + +void GcdPrivateAPI::OnListenerRemoved(const EventListenerInfo& details) { + num_device_listeners_--; + + if (num_device_listeners_ == 0) { + privet_device_lister_.reset(); + service_discovery_client_ = NULL; + } +} + +void GcdPrivateAPI::DeviceChanged( + bool added, + const std::string& name, + const local_discovery::DeviceDescription& description) { + linked_ptr<gcd_private::GCDDevice> device(new gcd_private::GCDDevice); + device->setup_type = gcd_private::SETUP_TYPE_MDNS; + device->id_string = kIDPrefixMdns + name; + device->device_type = description.type; + device->device_name = description.name; + device->device_description = description.description; + if (!description.id.empty()) + device->cloud_id.reset(new std::string(description.id)); + + known_devices_[device->id_string] = device; + + EventRouter::Get(browser_context_) + ->BroadcastEvent(MakeCloudDeviceStateChangedEvent(true, *device)); +} + +void GcdPrivateAPI::DeviceRemoved(const std::string& name) { + GCDDeviceMap::iterator found = known_devices_.find(kIDPrefixMdns + name); + linked_ptr<gcd_private::GCDDevice> device = found->second; + known_devices_.erase(found); + + EventRouter::Get(browser_context_) + ->BroadcastEvent(MakeCloudDeviceStateChangedEvent(false, *device)); +} + +void GcdPrivateAPI::DeviceCacheFlushed() { + for (GCDDeviceMap::iterator i = known_devices_.begin(); + i != known_devices_.end(); + i++) { + EventRouter::Get(browser_context_) + ->BroadcastEvent(MakeCloudDeviceStateChangedEvent(false, *i->second)); + } + + known_devices_.clear(); +} + // static void GcdPrivateAPI::SetGCDApiFlowFactoryForTests( GCDApiFlowFactoryForTests* factory) { @@ -122,11 +205,11 @@ void GcdPrivateGetCloudDeviceListFunction::CheckListingDone() { return; } - std::vector<linked_ptr<GCDDevice> > devices; + std::vector<linked_ptr<gcd_private::GCDDevice> > devices; for (DeviceList::iterator i = devices_.begin(); i != devices_.end(); i++) { - linked_ptr<GCDDevice> device(new GCDDevice); - device->setup_type = extensions::api::gcd_private::SETUP_TYPE_CLOUD; + linked_ptr<gcd_private::GCDDevice> device(new gcd_private::GCDDevice); + device->setup_type = gcd_private::SETUP_TYPE_CLOUD; if (i->type == local_discovery::kGCDTypePrinter) { device->id_string = kIDPrefixCloudPrinter + i->id; } else { @@ -141,8 +224,7 @@ void GcdPrivateGetCloudDeviceListFunction::CheckListingDone() { devices.push_back(device); } - results_ = extensions::api::gcd_private::GetCloudDeviceList::Results::Create( - devices); + results_ = gcd_private::GetCloudDeviceList::Results::Create(devices); SendResponse(true); Release(); diff --git a/chrome/browser/extensions/api/gcd_private/gcd_private_api.h b/chrome/browser/extensions/api/gcd_private/gcd_private_api.h index d8c6f46..61f0db6 100644 --- a/chrome/browser/extensions/api/gcd_private/gcd_private_api.h +++ b/chrome/browser/extensions/api/gcd_private/gcd_private_api.h @@ -5,16 +5,22 @@ #ifndef CHROME_BROWSER_EXTENSIONS_API_GCD_PRIVATE_GCD_PRIVATE_API_H_ #define CHROME_BROWSER_EXTENSIONS_API_GCD_PRIVATE_GCD_PRIVATE_API_H_ +#include "base/memory/linked_ptr.h" #include "base/memory/scoped_ptr.h" #include "chrome/browser/extensions/chrome_extension_function.h" #include "chrome/browser/local_discovery/cloud_device_list_delegate.h" #include "chrome/browser/local_discovery/gcd_api_flow.h" +#include "chrome/browser/local_discovery/privet_device_lister.h" +#include "chrome/browser/local_discovery/service_discovery_shared_client.h" #include "chrome/common/extensions/api/gcd_private.h" #include "extensions/browser/browser_context_keyed_api_factory.h" +#include "extensions/browser/event_router.h" namespace extensions { -class GcdPrivateAPI : public BrowserContextKeyedAPI { +class GcdPrivateAPI : public BrowserContextKeyedAPI, + public EventRouter::Observer, + public local_discovery::PrivetDeviceLister::Delegate { public: class GCDApiFlowFactoryForTests { public: @@ -34,9 +40,30 @@ class GcdPrivateAPI : public BrowserContextKeyedAPI { private: friend class BrowserContextKeyedAPIFactory<GcdPrivateAPI>; + typedef std::map<std::string /* id_string */, + linked_ptr<api::gcd_private::GCDDevice> > GCDDeviceMap; + + // EventRouter::Observer implementation. + virtual void OnListenerAdded(const EventListenerInfo& details) OVERRIDE; + virtual void OnListenerRemoved(const EventListenerInfo& details) OVERRIDE; + // BrowserContextKeyedAPI implementation. static const char* service_name() { return "GcdPrivateAPI"; } + // local_discovery::PrivetDeviceLister implementation. + virtual void DeviceChanged( + bool added, + const std::string& name, + const local_discovery::DeviceDescription& description) OVERRIDE; + virtual void DeviceRemoved(const std::string& name) OVERRIDE; + virtual void DeviceCacheFlushed() OVERRIDE; + + int num_device_listeners_; + scoped_refptr<local_discovery::ServiceDiscoverySharedClient> + service_discovery_client_; + scoped_ptr<local_discovery::PrivetDeviceLister> privet_device_lister_; + GCDDeviceMap known_devices_; + content::BrowserContext* const browser_context_; }; diff --git a/chrome/browser/extensions/api/gcd_private/gcd_private_apitest.cc b/chrome/browser/extensions/api/gcd_private/gcd_private_apitest.cc index 7e6ae8b..193a498 100644 --- a/chrome/browser/extensions/api/gcd_private/gcd_private_apitest.cc +++ b/chrome/browser/extensions/api/gcd_private/gcd_private_apitest.cc @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/bind.h" #include "base/command_line.h" #include "base/json/json_reader.h" +#include "base/message_loop/message_loop_proxy.h" #include "chrome/browser/extensions/api/gcd_private/gcd_private_api.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_service.h" @@ -11,6 +13,10 @@ #include "extensions/common/switches.h" #include "testing/gmock/include/gmock/gmock.h" +#if defined(ENABLE_MDNS) +#include "chrome/browser/local_discovery/test_service_discovery_client.h" +#endif // ENABLE_MDNS + namespace api = extensions::api; namespace { @@ -48,6 +54,84 @@ const char kGCDResponse[] = " \"maxRole\": \"owner\"" " }}]}"; +#if defined(ENABLE_MDNS) + +const uint8 kAnnouncePacket[] = { + // Header + 0x00, 0x00, // ID is zeroed out + 0x80, 0x00, // Standard query response, no error + 0x00, 0x00, // No questions (for simplicity) + 0x00, 0x05, // 5 RR (answers) + 0x00, 0x00, // 0 authority RRs + 0x00, 0x00, // 0 additional RRs + 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', 0x04, '_', + 't', 'c', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, + 0x00, 0x0c, // TYPE is PTR. + 0x00, 0x01, // CLASS is IN. + 0x00, 0x00, // TTL (4 bytes) is 32768 second. + 0x10, 0x00, 0x00, 0x0c, // RDLENGTH is 12 bytes. + 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', + 0xc0, 0x0c, 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', + 'c', 'e', 0xc0, 0x0c, 0x00, 0x10, // TYPE is TXT. + 0x00, 0x01, // CLASS is IN. + 0x00, 0x00, // TTL (4 bytes) is 32768 seconds. + 0x01, 0x00, 0x00, 0x41, // RDLENGTH is 69 bytes. + 0x03, 'i', 'd', '=', 0x10, 't', 'y', '=', 'S', 'a', + 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', + 'e', 0x1e, 'n', 'o', 't', 'e', '=', 'S', 'a', 'm', + 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', + ' ', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', + 'o', 'n', 0x0c, 't', 'y', 'p', 'e', '=', 'p', 'r', + 'i', 'n', 't', 'e', 'r', 0x09, 'm', 'y', 'S', 'e', + 'r', 'v', 'i', 'c', 'e', 0xc0, 0x0c, 0x00, 0x21, // Type is SRV + 0x00, 0x01, // CLASS is IN + 0x00, 0x00, // TTL (4 bytes) is 32768 second. + 0x10, 0x00, 0x00, 0x17, // RDLENGTH is 23 + 0x00, 0x00, 0x00, 0x00, 0x22, 0xb8, // port 8888 + 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', + 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0x09, 'm', 'y', + 'S', 'e', 'r', 'v', 'i', 'c', 'e', 0x05, 'l', 'o', + 'c', 'a', 'l', 0x00, 0x00, 0x01, // Type is A + 0x00, 0x01, // CLASS is IN + 0x00, 0x00, // TTL (4 bytes) is 32768 second. + 0x10, 0x00, 0x00, 0x04, // RDLENGTH is 4 + 0x01, 0x02, 0x03, 0x04, // 1.2.3.4 + 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', + 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0x00, 0x1C, // Type is AAAA + 0x00, 0x01, // CLASS is IN + 0x00, 0x00, // TTL (4 bytes) is 32768 second. + 0x10, 0x00, 0x00, 0x10, // RDLENGTH is 16 + 0x01, 0x02, 0x03, 0x04, // 1.2.3.4 + 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, + 0x03, 0x04, +}; + +const uint8 kGoodbyePacket[] = { + // Header + 0x00, 0x00, // ID is zeroed out + 0x80, 0x00, // Standard query response, RA, no error + 0x00, 0x00, // No questions (for simplicity) + 0x00, 0x02, // 1 RR (answers) + 0x00, 0x00, // 0 authority RRs + 0x00, 0x00, // 0 additional RRs + 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', 0x04, '_', 't', 'c', + 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0x00, 0x0c, // TYPE is PTR. + 0x00, 0x01, // CLASS is IN. + 0x00, 0x00, // TTL (4 bytes) is 0 seconds. + 0x00, 0x00, 0x00, 0x0c, // RDLENGTH is 12 bytes. + 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 0xc0, 0x0c, + 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 0xc0, 0x0c, + 0x00, 0x21, // Type is SRV + 0x00, 0x01, // CLASS is IN + 0x00, 0x00, // TTL (4 bytes) is 0 seconds. + 0x00, 0x00, 0x00, 0x17, // RDLENGTH is 23 + 0x00, 0x00, 0x00, 0x00, 0x22, 0xb8, // port 8888 + 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 0x05, 'l', + 'o', 'c', 'a', 'l', 0x00, +}; + +#endif // ENABLE_MDNS + // Sentinel value to signify the request should fail. const char kResponseValueFailure[] = "FAILURE"; @@ -105,10 +189,21 @@ class FakeGCDApiFlowFactory class GcdPrivateAPITest : public ExtensionApiTest { public: - GcdPrivateAPITest() {} + GcdPrivateAPITest() { +#if defined(ENABLE_MDNS) + test_service_discovery_client_ = + new local_discovery::TestServiceDiscoveryClient(); + test_service_discovery_client_->Start(); +#endif // ENABLE_MDNS + } protected: FakeGCDApiFlowFactory api_flow_factory_; + +#if defined(ENABLE_MDNS) + scoped_refptr<local_discovery::TestServiceDiscoveryClient> + test_service_discovery_client_; +#endif // ENABLE_MDNS }; IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, GetCloudList) { @@ -121,4 +216,44 @@ IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, GetCloudList) { EXPECT_TRUE(RunExtensionSubtest("gcd_private/api", "get_cloud_list.html")); } +#if defined(ENABLE_MDNS) + +IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, AddBefore) { + test_service_discovery_client_->SimulateReceive(kAnnouncePacket, + sizeof(kAnnouncePacket)); + + EXPECT_TRUE( + RunExtensionSubtest("gcd_private/api", "receive_new_device.html")); +} + +IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, AddAfter) { + base::MessageLoopProxy::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&local_discovery::TestServiceDiscoveryClient::SimulateReceive, + test_service_discovery_client_, + kAnnouncePacket, + sizeof(kAnnouncePacket)), + base::TimeDelta::FromSeconds(1)); + + EXPECT_TRUE( + RunExtensionSubtest("gcd_private/api", "receive_new_device.html")); +} + +IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, AddRemove) { + test_service_discovery_client_->SimulateReceive(kAnnouncePacket, + sizeof(kAnnouncePacket)); + + base::MessageLoopProxy::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&local_discovery::TestServiceDiscoveryClient::SimulateReceive, + test_service_discovery_client_, + kGoodbyePacket, + sizeof(kGoodbyePacket)), + base::TimeDelta::FromSeconds(1)); + + EXPECT_TRUE(RunExtensionSubtest("gcd_private/api", "remove_device.html")); +} + +#endif // ENABLE_MDNS + } // namespace |