1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/extensions/api/push_messaging/push_messaging_api.h" 6 7 #include "apps/launcher.h" 8 #include "base/strings/stringprintf.h" 9 #include "chrome/browser/extensions/api/push_messaging/push_messaging_invalidation_handler.h" 10 #include "chrome/browser/extensions/api/push_messaging/push_messaging_invalidation_mapper.h" 11 #include "chrome/browser/extensions/extension_apitest.h" 12 #include "chrome/browser/invalidation/fake_invalidation_service.h" 13 #include "chrome/browser/invalidation/profile_invalidation_provider_factory.h" 14 #include "chrome/browser/profiles/profile.h" 15 #include "chrome/browser/ui/browser.h" 16 #include "chrome/common/chrome_switches.h" 17 #include "chrome/test/base/ui_test_utils.h" 18 #include "components/invalidation/fake_invalidator.h" 19 #include "components/invalidation/invalidation.h" 20 #include "components/invalidation/invalidation_service.h" 21 #include "components/invalidation/profile_invalidation_provider.h" 22 #include "components/keyed_service/core/keyed_service.h" 23 #include "extensions/test/extension_test_message_listener.h" 24 #include "extensions/test/result_catcher.h" 25 #include "google/cacheinvalidation/types.pb.h" 26 #include "testing/gmock/include/gmock/gmock.h" 27 28 using ::testing::SaveArg; 29 using ::testing::StrictMock; 30 using ::testing::_; 31 32 namespace content { 33 class BrowserContext; 34 } 35 36 namespace extensions { 37 38 namespace { 39 40 invalidation::ObjectId ExtensionAndSubchannelToObjectId( 41 const std::string& extension_id, int subchannel_id) { 42 return invalidation::ObjectId( 43 ipc::invalidation::ObjectSource::CHROME_PUSH_MESSAGING, 44 base::StringPrintf("U/%s/%d", extension_id.c_str(), subchannel_id)); 45 } 46 47 KeyedService* BuildFakeProfileInvalidationProvider( 48 content::BrowserContext* context) { 49 return new invalidation::ProfileInvalidationProvider( 50 scoped_ptr<invalidation::InvalidationService>( 51 new invalidation::FakeInvalidationService)); 52 } 53 54 class MockInvalidationMapper : public PushMessagingInvalidationMapper { 55 public: 56 MockInvalidationMapper(); 57 ~MockInvalidationMapper(); 58 59 MOCK_METHOD1(SuppressInitialInvalidationsForExtension, 60 void(const std::string&)); 61 MOCK_METHOD1(RegisterExtension, void(const std::string&)); 62 MOCK_METHOD1(UnregisterExtension, void(const std::string&)); 63 }; 64 65 MockInvalidationMapper::MockInvalidationMapper() {} 66 MockInvalidationMapper::~MockInvalidationMapper() {} 67 68 } // namespace 69 70 class PushMessagingApiTest : public ExtensionApiTest { 71 public: 72 PushMessagingApiTest() 73 : fake_invalidation_service_(NULL) { 74 } 75 76 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 77 ExtensionApiTest::SetUpCommandLine(command_line); 78 } 79 80 virtual void SetUp() OVERRIDE { 81 invalidation::ProfileInvalidationProviderFactory::GetInstance()-> 82 RegisterTestingFactory(BuildFakeProfileInvalidationProvider); 83 ExtensionApiTest::SetUp(); 84 } 85 86 virtual void SetUpOnMainThread() OVERRIDE { 87 ExtensionApiTest::SetUpOnMainThread(); 88 fake_invalidation_service_ = 89 static_cast<invalidation::FakeInvalidationService*>( 90 static_cast<invalidation::ProfileInvalidationProvider*>( 91 invalidation::ProfileInvalidationProviderFactory:: 92 GetInstance()->GetForProfile(profile()))-> 93 GetInvalidationService()); 94 } 95 96 void EmitInvalidation( 97 const invalidation::ObjectId& object_id, 98 int64 version, 99 const std::string& payload) { 100 fake_invalidation_service_->EmitInvalidationForTest( 101 syncer::Invalidation::Init(object_id, version, payload)); 102 } 103 104 PushMessagingAPI* GetAPI() { 105 return PushMessagingAPI::Get(profile()); 106 } 107 108 PushMessagingEventRouter* GetEventRouter() { 109 return PushMessagingAPI::Get(profile())->GetEventRouterForTest(); 110 } 111 112 invalidation::FakeInvalidationService* fake_invalidation_service_; 113 }; 114 115 IN_PROC_BROWSER_TEST_F(PushMessagingApiTest, EventDispatch) { 116 ResultCatcher catcher; 117 catcher.RestrictToBrowserContext(profile()); 118 119 const extensions::Extension* extension = 120 LoadExtension(test_data_dir_.AppendASCII("push_messaging")); 121 ASSERT_TRUE(extension); 122 ui_test_utils::NavigateToURL( 123 browser(), extension->GetResourceURL("event_dispatch.html")); 124 125 GetEventRouter()->TriggerMessageForTest(extension->id(), 1, "payload"); 126 127 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); 128 } 129 130 // Test that a push introduced into the sync code makes it to the extension 131 // that we install. 132 IN_PROC_BROWSER_TEST_F(PushMessagingApiTest, ReceivesPush) { 133 ResultCatcher catcher; 134 catcher.RestrictToBrowserContext(profile()); 135 136 const extensions::Extension* extension = 137 LoadExtension(test_data_dir_.AppendASCII("push_messaging")); 138 ASSERT_TRUE(extension); 139 ui_test_utils::NavigateToURL( 140 browser(), extension->GetResourceURL("event_dispatch.html")); 141 142 // PushMessagingInvalidationHandler suppresses the initial invalidation on 143 // each subchannel at install, so trigger the suppressions first. 144 for (int i = 0; i < 3; ++i) { 145 EmitInvalidation( 146 ExtensionAndSubchannelToObjectId(extension->id(), i), i, std::string()); 147 } 148 149 EmitInvalidation( 150 ExtensionAndSubchannelToObjectId(extension->id(), 1), 5, "payload"); 151 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); 152 } 153 154 // Checks that an extension with the pushMessaging permission gets automatically 155 // registered for invalidations when it is loaded. 156 IN_PROC_BROWSER_TEST_F(PushMessagingApiTest, AutoRegistration) { 157 scoped_ptr<StrictMock<MockInvalidationMapper> > mapper( 158 new StrictMock<MockInvalidationMapper>); 159 StrictMock<MockInvalidationMapper>* unsafe_mapper = mapper.get(); 160 // PushMessagingEventRouter owns the mapper now. 161 GetAPI()->SetMapperForTest( 162 mapper.PassAs<PushMessagingInvalidationMapper>()); 163 164 std::string extension_id1; 165 std::string extension_id2; 166 EXPECT_CALL(*unsafe_mapper, SuppressInitialInvalidationsForExtension(_)) 167 .WillOnce(SaveArg<0>(&extension_id1)); 168 EXPECT_CALL(*unsafe_mapper, RegisterExtension(_)) 169 .WillOnce(SaveArg<0>(&extension_id2)); 170 const extensions::Extension* extension = 171 LoadExtension(test_data_dir_.AppendASCII("push_messaging")); 172 ASSERT_TRUE(extension); 173 EXPECT_EQ(extension->id(), extension_id1); 174 EXPECT_EQ(extension->id(), extension_id2); 175 EXPECT_CALL(*unsafe_mapper, UnregisterExtension(extension->id())); 176 UnloadExtension(extension->id()); 177 } 178 179 // Tests that we re-register for invalidations on restart for extensions that 180 // are already installed. 181 IN_PROC_BROWSER_TEST_F(PushMessagingApiTest, PRE_Restart) { 182 PushMessagingInvalidationHandler* handler = 183 static_cast<PushMessagingInvalidationHandler*>( 184 GetAPI()->GetMapperForTest()); 185 EXPECT_TRUE(handler->GetRegisteredExtensionsForTest().empty()); 186 ASSERT_TRUE(InstallExtension(test_data_dir_.AppendASCII("push_messaging"), 187 1 /* new install */)); 188 } 189 190 IN_PROC_BROWSER_TEST_F(PushMessagingApiTest, Restart) { 191 PushMessagingInvalidationHandler* handler = 192 static_cast<PushMessagingInvalidationHandler*>( 193 GetAPI()->GetMapperForTest()); 194 EXPECT_EQ(1U, handler->GetRegisteredExtensionsForTest().size()); 195 } 196 197 // Test that GetChannelId fails if no user is signed in. 198 IN_PROC_BROWSER_TEST_F(PushMessagingApiTest, GetChannelId) { 199 ResultCatcher catcher; 200 catcher.RestrictToBrowserContext(profile()); 201 202 const extensions::Extension* extension = 203 LoadExtension(test_data_dir_.AppendASCII("push_messaging")); 204 ASSERT_TRUE(extension); 205 ui_test_utils::NavigateToURL( 206 browser(), extension->GetResourceURL("get_channel_id.html")); 207 208 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); 209 } 210 211 } // namespace extensions 212