Commit a5045732 authored by johnme's avatar johnme Committed by Commit bot

Make Web Push use InstanceID tokens instead of GCM registrations

On both desktop and Android, the Push API will now be backed by
InstanceID tokens instead of GCM registrations.

This should have no compatibility impact, since GCM was already
silently converting Push API registrations to InstanceID format
server-side.

Part of a series of patches:
1. https://codereview.chromium.org/1832833002 adds InstanceIDWithSubtype
2. https://codereview.chromium.org/1830983002 adds JNI bindings
3. https://codereview.chromium.org/1829023002 adds fake and test
4. https://codereview.chromium.org/1899753002 fixes strict mode violations
5. https://codereview.chromium.org/1854093002 enables InstanceID by default
6. https://codereview.chromium.org/1953273002 extends the GCMKeyStore
7. https://codereview.chromium.org/1923953002 integrates IIDs with crypto
8. https://codereview.chromium.org/2111973002 adds IID subtypes on desktop
9. this patch

Also depends on
- https://codereview.chromium.org/1945753002 transactional SW userdata
- https://codereview.chromium.org/1944863002 fix sender ID storage

BUG=589461, 533498

Review-Url: https://codereview.chromium.org/1851423003
Cr-Commit-Position: refs/heads/master@{#417323}
parent 30d5ad80
...@@ -410,6 +410,8 @@ android_library("chrome_test_java") { ...@@ -410,6 +410,8 @@ android_library("chrome_test_java") {
"//components/bookmarks/common/android:bookmarks_java", "//components/bookmarks/common/android:bookmarks_java",
"//components/dom_distiller/core/android:dom_distiller_core_java", "//components/dom_distiller/core/android:dom_distiller_core_java",
"//components/gcm_driver/android:gcm_driver_java", "//components/gcm_driver/android:gcm_driver_java",
"//components/gcm_driver/instance_id/android:instance_id_driver_java",
"//components/gcm_driver/instance_id/android:instance_id_driver_test_support_java",
"//components/invalidation/impl:java", "//components/invalidation/impl:java",
"//components/invalidation/impl:javatests", "//components/invalidation/impl:javatests",
"//components/location/android:location_java", "//components/location/android:location_java",
......
...@@ -2,6 +2,7 @@ include_rules = [ ...@@ -2,6 +2,7 @@ include_rules = [
"+components/bookmarks/common/android/java/src/org/chromium/components/bookmarks", "+components/bookmarks/common/android/java/src/org/chromium/components/bookmarks",
"+components/dom_distiller/core/android/java/src/org/chromium/components/dom_distiller/core", "+components/dom_distiller/core/android/java/src/org/chromium/components/dom_distiller/core",
"+components/gcm_driver/android/java/src/org/chromium/components/gcm_driver", "+components/gcm_driver/android/java/src/org/chromium/components/gcm_driver",
"+components/gcm_driver/instance_id/android/javatests/src/org/chromium/components/gcm_driver/instance_id",
"+components/location/android/java", "+components/location/android/java",
"+components/navigation_interception", "+components/navigation_interception",
"+components/precache/android/javatests", "+components/precache/android/javatests",
......
...@@ -13,6 +13,7 @@ import android.os.Bundle; ...@@ -13,6 +13,7 @@ import android.os.Bundle;
import android.test.MoreAsserts; import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.MediumTest;
import android.util.Pair;
import org.chromium.base.ThreadUtils; import org.chromium.base.ThreadUtils;
import org.chromium.base.library_loader.ProcessInitException; import org.chromium.base.library_loader.ProcessInitException;
...@@ -25,8 +26,8 @@ import org.chromium.chrome.browser.tab.Tab; ...@@ -25,8 +26,8 @@ import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.util.InfoBarUtil; import org.chromium.chrome.test.util.InfoBarUtil;
import org.chromium.chrome.test.util.browser.TabTitleObserver; import org.chromium.chrome.test.util.browser.TabTitleObserver;
import org.chromium.chrome.test.util.browser.notifications.MockNotificationManagerProxy.NotificationEntry; import org.chromium.chrome.test.util.browser.notifications.MockNotificationManagerProxy.NotificationEntry;
import org.chromium.components.gcm_driver.FakeGoogleCloudMessagingSubscriber;
import org.chromium.components.gcm_driver.GCMDriver; import org.chromium.components.gcm_driver.GCMDriver;
import org.chromium.components.gcm_driver.instance_id.FakeInstanceIDWithSubtype;
import org.chromium.content.browser.test.util.CallbackHelper; import org.chromium.content.browser.test.util.CallbackHelper;
import org.chromium.content.browser.test.util.Criteria; import org.chromium.content.browser.test.util.Criteria;
import org.chromium.content.browser.test.util.CriteriaHelper; import org.chromium.content.browser.test.util.CriteriaHelper;
...@@ -63,6 +64,7 @@ public class PushMessagingTest ...@@ -63,6 +64,7 @@ public class PushMessagingTest
ThreadUtils.runOnUiThreadBlocking(new Runnable() { ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override @Override
public void run() { public void run() {
FakeInstanceIDWithSubtype.clearDataAndSetEnabled(true);
PushMessagingServiceObserver.setListenerForTesting(listener); PushMessagingServiceObserver.setListenerForTesting(listener);
} }
}); });
...@@ -75,6 +77,7 @@ public class PushMessagingTest ...@@ -75,6 +77,7 @@ public class PushMessagingTest
@Override @Override
public void run() { public void run() {
PushMessagingServiceObserver.setListenerForTesting(null); PushMessagingServiceObserver.setListenerForTesting(null);
FakeInstanceIDWithSubtype.clearDataAndSetEnabled(false);
} }
}); });
super.tearDown(); super.tearDown();
...@@ -91,9 +94,6 @@ public class PushMessagingTest ...@@ -91,9 +94,6 @@ public class PushMessagingTest
@MediumTest @MediumTest
@Feature({"Browser", "PushMessaging"}) @Feature({"Browser", "PushMessaging"})
public void testPushPermissionInfobar() throws InterruptedException, TimeoutException { public void testPushPermissionInfobar() throws InterruptedException, TimeoutException {
FakeGoogleCloudMessagingSubscriber subscriber = new FakeGoogleCloudMessagingSubscriber();
GCMDriver.overrideSubscriberForTesting(subscriber);
loadUrl(mPushTestPage); loadUrl(mPushTestPage);
WebContents webContents = getActivity().getActivityTab().getWebContents(); WebContents webContents = getActivity().getActivityTab().getWebContents();
assertEquals(0, getInfoBars().size()); assertEquals(0, getInfoBars().size());
...@@ -128,15 +128,13 @@ public class PushMessagingTest ...@@ -128,15 +128,13 @@ public class PushMessagingTest
@MediumTest @MediumTest
@Feature({"Browser", "PushMessaging"}) @Feature({"Browser", "PushMessaging"})
public void testPushAndShowNotification() throws InterruptedException, TimeoutException { public void testPushAndShowNotification() throws InterruptedException, TimeoutException {
FakeGoogleCloudMessagingSubscriber subscriber = new FakeGoogleCloudMessagingSubscriber();
GCMDriver.overrideSubscriberForTesting(subscriber);
loadUrl(mPushTestPage); loadUrl(mPushTestPage);
setNotificationContentSettingForCurrentOrigin(ContentSetting.ALLOW); setNotificationContentSettingForCurrentOrigin(ContentSetting.ALLOW);
runScriptAndWaitForTitle("subscribePush()", "subscribe ok"); runScriptAndWaitForTitle("subscribePush()", "subscribe ok");
sendPushAndWaitForCallback( Pair<String, String> appIdAndSenderId =
subscriber.getLastSubscribeSubtype(), subscriber.getLastSubscribeSource()); FakeInstanceIDWithSubtype.getSubtypeAndAuthorizedEntityOfOnlyToken();
sendPushAndWaitForCallback(appIdAndSenderId);
NotificationEntry notificationEntry = waitForNotification(); NotificationEntry notificationEntry = waitForNotification();
assertEquals("push notification 1", assertEquals("push notification 1",
notificationEntry.notification.extras.getString(Notification.EXTRA_TITLE)); notificationEntry.notification.extras.getString(Notification.EXTRA_TITLE));
...@@ -149,9 +147,6 @@ public class PushMessagingTest ...@@ -149,9 +147,6 @@ public class PushMessagingTest
@LargeTest @LargeTest
@Feature({"Browser", "PushMessaging"}) @Feature({"Browser", "PushMessaging"})
public void testDefaultNotification() throws InterruptedException, TimeoutException { public void testDefaultNotification() throws InterruptedException, TimeoutException {
FakeGoogleCloudMessagingSubscriber subscriber = new FakeGoogleCloudMessagingSubscriber();
GCMDriver.overrideSubscriberForTesting(subscriber);
// Load the push test page into the first tab. // Load the push test page into the first tab.
loadUrl(mPushTestPage); loadUrl(mPushTestPage);
assertEquals(1, getActivity().getCurrentTabModel().getCount()); assertEquals(1, getActivity().getCurrentTabModel().getCount());
...@@ -162,8 +157,8 @@ public class PushMessagingTest ...@@ -162,8 +157,8 @@ public class PushMessagingTest
// Set up the push subscription and capture its details. // Set up the push subscription and capture its details.
setNotificationContentSettingForCurrentOrigin(ContentSetting.ALLOW); setNotificationContentSettingForCurrentOrigin(ContentSetting.ALLOW);
runScriptAndWaitForTitle("subscribePush()", "subscribe ok"); runScriptAndWaitForTitle("subscribePush()", "subscribe ok");
String appId = subscriber.getLastSubscribeSubtype(); Pair<String, String> appIdAndSenderId =
String senderId = subscriber.getLastSubscribeSource(); FakeInstanceIDWithSubtype.getSubtypeAndAuthorizedEntityOfOnlyToken();
// Make the tab invisible by opening another one with a different origin. // Make the tab invisible by opening another one with a different origin.
loadUrlInNewTab(ABOUT_BLANK); loadUrlInNewTab(ABOUT_BLANK);
...@@ -174,17 +169,17 @@ public class PushMessagingTest ...@@ -174,17 +169,17 @@ public class PushMessagingTest
// The first time a push event is fired and no notification is shown from the service // The first time a push event is fired and no notification is shown from the service
// worker, grace permits it so no default notification is shown. // worker, grace permits it so no default notification is shown.
runScriptAndWaitForTitle("setNotifyOnPush(false)", "setNotifyOnPush false ok", tab); runScriptAndWaitForTitle("setNotifyOnPush(false)", "setNotifyOnPush false ok", tab);
sendPushAndWaitForCallback(appId, senderId); sendPushAndWaitForCallback(appIdAndSenderId);
// After grace runs out a default notification will be shown. // After grace runs out a default notification will be shown.
sendPushAndWaitForCallback(appId, senderId); sendPushAndWaitForCallback(appIdAndSenderId);
NotificationEntry notificationEntry = waitForNotification(); NotificationEntry notificationEntry = waitForNotification();
MoreAsserts.assertContainsRegex("user_visible_auto_notification", notificationEntry.tag); MoreAsserts.assertContainsRegex("user_visible_auto_notification", notificationEntry.tag);
// When another push does show a notification, the default notification is automatically // When another push does show a notification, the default notification is automatically
// dismissed (an additional mutation) so there is only one left in the end. // dismissed (an additional mutation) so there is only one left in the end.
runScriptAndWaitForTitle("setNotifyOnPush(true)", "setNotifyOnPush true ok", tab); runScriptAndWaitForTitle("setNotifyOnPush(true)", "setNotifyOnPush true ok", tab);
sendPushAndWaitForCallback(appId, senderId); sendPushAndWaitForCallback(appIdAndSenderId);
waitForNotificationManagerMutation(); waitForNotificationManagerMutation();
notificationEntry = waitForNotification(); notificationEntry = waitForNotification();
assertEquals("push notification 1", assertEquals("push notification 1",
...@@ -210,8 +205,10 @@ public class PushMessagingTest ...@@ -210,8 +205,10 @@ public class PushMessagingTest
waitForTitle(tab, expectedTitle); waitForTitle(tab, expectedTitle);
} }
private void sendPushAndWaitForCallback(final String appId, final String senderId) private void sendPushAndWaitForCallback(Pair<String, String> appIdAndSenderId)
throws InterruptedException, TimeoutException { throws InterruptedException, TimeoutException {
final String appId = appIdAndSenderId.first;
final String senderId = appIdAndSenderId.second;
ThreadUtils.runOnUiThreadBlocking(new Runnable() { ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override @Override
public void run() { public void run() {
......
...@@ -23,18 +23,6 @@ using extensions::ResultCatcher; ...@@ -23,18 +23,6 @@ using extensions::ResultCatcher;
namespace extensions { namespace extensions {
namespace {
std::unique_ptr<KeyedService> BuildFakeGCMProfileService(
content::BrowserContext* context) {
std::unique_ptr<gcm::FakeGCMProfileService> service(
new gcm::FakeGCMProfileService(Profile::FromBrowserContext(context)));
service->SetDriverForTesting(new instance_id::FakeGCMDriverForInstanceID());
return std::move(service);
}
} // namespace
class InstanceIDApiTest : public ExtensionApiTest { class InstanceIDApiTest : public ExtensionApiTest {
public: public:
InstanceIDApiTest(); InstanceIDApiTest();
...@@ -51,7 +39,7 @@ InstanceIDApiTest::InstanceIDApiTest() { ...@@ -51,7 +39,7 @@ InstanceIDApiTest::InstanceIDApiTest() {
void InstanceIDApiTest::SetUpOnMainThread() { void InstanceIDApiTest::SetUpOnMainThread() {
gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactory( gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactory(
browser()->profile(), &BuildFakeGCMProfileService); browser()->profile(), &gcm::FakeGCMProfileService::Build);
ExtensionApiTest::SetUpOnMainThread(); ExtensionApiTest::SetUpOnMainThread();
} }
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "chrome/browser/services/gcm/gcm_profile_service_factory.h" #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/ui_test_utils.h" #include "chrome/test/base/ui_test_utils.h"
#include "components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.h"
#include "components/version_info/version_info.h" #include "components/version_info/version_info.h"
#include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h" #include "content/public/browser/navigation_entry.h"
...@@ -196,7 +197,7 @@ class ServiceWorkerBackgroundSyncTest : public ServiceWorkerTest { ...@@ -196,7 +197,7 @@ class ServiceWorkerBackgroundSyncTest : public ServiceWorkerTest {
class ServiceWorkerPushMessagingTest : public ServiceWorkerTest { class ServiceWorkerPushMessagingTest : public ServiceWorkerTest {
public: public:
ServiceWorkerPushMessagingTest() ServiceWorkerPushMessagingTest()
: gcm_service_(nullptr), push_service_(nullptr) {} : gcm_driver_(nullptr), push_service_(nullptr) {}
~ServiceWorkerPushMessagingTest() override {} ~ServiceWorkerPushMessagingTest() override {}
void GrantNotificationPermissionForTest(const GURL& url) { void GrantNotificationPermissionForTest(const GURL& url) {
...@@ -225,20 +226,25 @@ class ServiceWorkerPushMessagingTest : public ServiceWorkerTest { ...@@ -225,20 +226,25 @@ class ServiceWorkerPushMessagingTest : public ServiceWorkerTest {
ServiceWorkerTest::SetUpCommandLine(command_line); ServiceWorkerTest::SetUpCommandLine(command_line);
} }
void SetUpOnMainThread() override { void SetUpOnMainThread() override {
gcm_service_ = static_cast<gcm::FakeGCMProfileService*>( gcm::FakeGCMProfileService* gcm_service =
gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse( static_cast<gcm::FakeGCMProfileService*>(
profile(), &gcm::FakeGCMProfileService::Build)); gcm::GCMProfileServiceFactory::GetInstance()
gcm_service_->set_collect(true); ->SetTestingFactoryAndUse(profile(),
&gcm::FakeGCMProfileService::Build));
gcm_driver_ = static_cast<instance_id::FakeGCMDriverForInstanceID*>(
gcm_service->driver());
push_service_ = PushMessagingServiceFactory::GetForProfile(profile()); push_service_ = PushMessagingServiceFactory::GetForProfile(profile());
ServiceWorkerTest::SetUpOnMainThread(); ServiceWorkerTest::SetUpOnMainThread();
} }
gcm::FakeGCMProfileService* gcm_service() const { return gcm_service_; } instance_id::FakeGCMDriverForInstanceID* gcm_driver() const {
return gcm_driver_;
}
PushMessagingServiceImpl* push_service() const { return push_service_; } PushMessagingServiceImpl* push_service() const { return push_service_; }
private: private:
gcm::FakeGCMProfileService* gcm_service_; instance_id::FakeGCMDriverForInstanceID* gcm_driver_;
PushMessagingServiceImpl* push_service_; PushMessagingServiceImpl* push_service_;
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerPushMessagingTest); DISALLOW_COPY_AND_ASSIGN(ServiceWorkerPushMessagingTest);
...@@ -766,8 +772,8 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerPushMessagingTest, OnPush) { ...@@ -766,8 +772,8 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerPushMessagingTest, OnPush) {
PushMessagingAppIdentifier app_identifier = PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL, extension_url); GetAppIdentifierForServiceWorkerRegistration(0LL, extension_url);
ASSERT_EQ(app_identifier.app_id(), gcm_service()->last_registered_app_id()); ASSERT_EQ(app_identifier.app_id(), gcm_driver()->last_gettoken_app_id());
EXPECT_EQ("1234567890", gcm_service()->last_registered_sender_ids()[0]); EXPECT_EQ("1234567890", gcm_driver()->last_gettoken_authorized_entity());
base::RunLoop run_loop; base::RunLoop run_loop;
// Send a push message via gcm and expect the ServiceWorker to receive it. // Send a push message via gcm and expect the ServiceWorker to receive it.
......
...@@ -19,11 +19,13 @@ ...@@ -19,11 +19,13 @@
#include "components/prefs/scoped_user_pref_update.h" #include "components/prefs/scoped_user_pref_update.h"
const char kPushMessagingAppIdentifierPrefix[] = "wp:"; const char kPushMessagingAppIdentifierPrefix[] = "wp:";
const char kInstanceIDGuidSuffix[] = "-V2";
namespace { namespace {
// sizeof is strlen + 1 since it's null-terminated. // sizeof is strlen + 1 since it's null-terminated.
const size_t kPrefixLength = sizeof(kPushMessagingAppIdentifierPrefix) - 1; const size_t kPrefixLength = sizeof(kPushMessagingAppIdentifierPrefix) - 1;
const size_t kGuidSuffixLength = sizeof(kInstanceIDGuidSuffix) - 1;
const char kSeparator = '#'; // Ok as only the origin of the url is used. const char kSeparator = '#'; // Ok as only the origin of the url is used.
const size_t kGuidLength = 36; // "%08X-%04X-%04X-%04X-%012llX" const size_t kGuidLength = 36; // "%08X-%04X-%04X-%04X-%012llX"
...@@ -62,6 +64,12 @@ void PushMessagingAppIdentifier::RegisterProfilePrefs( ...@@ -62,6 +64,12 @@ void PushMessagingAppIdentifier::RegisterProfilePrefs(
registry->RegisterDictionaryPref(prefs::kPushMessagingAppIdentifierMap); registry->RegisterDictionaryPref(prefs::kPushMessagingAppIdentifierMap);
} }
// static
bool PushMessagingAppIdentifier::UseInstanceID(const std::string& app_id) {
return base::EndsWith(app_id, kInstanceIDGuidSuffix,
base::CompareCase::SENSITIVE);
}
// static // static
PushMessagingAppIdentifier PushMessagingAppIdentifier::Generate( PushMessagingAppIdentifier PushMessagingAppIdentifier::Generate(
const GURL& origin, const GURL& origin,
...@@ -69,6 +77,9 @@ PushMessagingAppIdentifier PushMessagingAppIdentifier::Generate( ...@@ -69,6 +77,9 @@ PushMessagingAppIdentifier PushMessagingAppIdentifier::Generate(
// Use uppercase GUID for consistency with GUIDs Push has already sent to GCM. // Use uppercase GUID for consistency with GUIDs Push has already sent to GCM.
// Also allows detecting case mangling; see code commented "crbug.com/461867". // Also allows detecting case mangling; see code commented "crbug.com/461867".
std::string guid = base::ToUpperASCII(base::GenerateGUID()); std::string guid = base::ToUpperASCII(base::GenerateGUID());
// All new push subscriptions are Instance ID tokens.
guid.replace(guid.size() - kGuidSuffixLength, kGuidSuffixLength,
kInstanceIDGuidSuffix);
CHECK(!guid.empty()); CHECK(!guid.empty());
std::string app_id = std::string app_id =
kPushMessagingAppIdentifierPrefix + origin.spec() + kSeparator + guid; kPushMessagingAppIdentifierPrefix + origin.spec() + kSeparator + guid;
...@@ -196,6 +207,7 @@ void PushMessagingAppIdentifier::DeleteFromPrefs(Profile* profile) const { ...@@ -196,6 +207,7 @@ void PushMessagingAppIdentifier::DeleteFromPrefs(Profile* profile) const {
} }
void PushMessagingAppIdentifier::DCheckValid() const { void PushMessagingAppIdentifier::DCheckValid() const {
#if DCHECK_IS_ON()
DCHECK_GE(service_worker_registration_id_, 0); DCHECK_GE(service_worker_registration_id_, 0);
DCHECK(origin_.is_valid()); DCHECK(origin_.is_valid());
...@@ -204,16 +216,29 @@ void PushMessagingAppIdentifier::DCheckValid() const { ...@@ -204,16 +216,29 @@ void PushMessagingAppIdentifier::DCheckValid() const {
// "wp:" // "wp:"
DCHECK_EQ(kPushMessagingAppIdentifierPrefix, DCHECK_EQ(kPushMessagingAppIdentifierPrefix,
app_id_.substr(0, kPrefixLength)); app_id_.substr(0, kPrefixLength));
// Optional (origin.spec() + '#') // Optional (origin.spec() + '#')
if (app_id_.size() != kPrefixLength + kGuidLength) { if (app_id_.size() != kPrefixLength + kGuidLength) {
const size_t suffix_length = 1 /* kSeparator */ + kGuidLength; const size_t suffix_length = 1 /* kSeparator */ + kGuidLength;
DCHECK(app_id_.size() > kPrefixLength + suffix_length); DCHECK_GT(app_id_.size(), kPrefixLength + suffix_length);
DCHECK_EQ(origin_, GURL(app_id_.substr( DCHECK_EQ(origin_, GURL(app_id_.substr(
kPrefixLength, kPrefixLength,
app_id_.size() - kPrefixLength - suffix_length))); app_id_.size() - kPrefixLength - suffix_length)));
DCHECK_EQ(std::string(1, kSeparator), DCHECK_EQ(std::string(1, kSeparator),
app_id_.substr(app_id_.size() - suffix_length, 1)); app_id_.substr(app_id_.size() - suffix_length, 1));
} }
// GUID
DCHECK(base::IsValidGUID(app_id_.substr(app_id_.size() - kGuidLength))); // GUID. In order to distinguish them, an app_id created for an InstanceID
// based subscription has the last few characters of the GUID overwritten with
// kInstanceIDGuidSuffix (which contains non-hex characters invalid in GUIDs).
std::string guid = app_id_.substr(app_id_.size() - kGuidLength);
if (UseInstanceID(app_id_)) {
DCHECK(!base::IsValidGUID(guid));
// Replace suffix with valid hex so we can validate the rest of the string.
guid = guid.replace(guid.size() - kGuidSuffixLength, kGuidSuffixLength,
kGuidSuffixLength, 'C');
}
DCHECK(base::IsValidGUID(guid));
#endif // DCHECK_IS_ON()
} }
...@@ -34,6 +34,10 @@ class PushMessagingAppIdentifier { ...@@ -34,6 +34,10 @@ class PushMessagingAppIdentifier {
// Register profile-specific prefs. // Register profile-specific prefs.
static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
// Returns whether the modern InstanceID API should be used with this app_id
// (rather than legacy GCM registration).
static bool UseInstanceID(const std::string& app_id);
// Generates a new app identifier, with partially random app_id. // Generates a new app identifier, with partially random app_id.
static PushMessagingAppIdentifier Generate( static PushMessagingAppIdentifier Generate(
const GURL& origin, const GURL& origin,
......
...@@ -12,20 +12,28 @@ ...@@ -12,20 +12,28 @@
#include "chrome/browser/profiles/incognito_helpers.h" #include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/push_messaging/push_messaging_service_impl.h" #include "chrome/browser/push_messaging/push_messaging_service_impl.h"
#include "chrome/browser/services/gcm/fake_gcm_profile_service.h"
#include "chrome/browser/services/gcm/gcm_profile_service_factory.h" #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
#include "chrome/browser/services/gcm/instance_id/instance_id_profile_service.h"
#include "chrome/browser/services/gcm/instance_id/instance_id_profile_service_factory.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/keyed_service/content/browser_context_dependency_manager.h"
// static // static
PushMessagingServiceImpl* PushMessagingServiceFactory::GetForProfile( PushMessagingServiceImpl* PushMessagingServiceFactory::GetForProfile(
content::BrowserContext* profile) { content::BrowserContext* context) {
// The Push API is not currently supported in incognito mode. // The Push API is not currently supported in incognito mode.
// See https://crbug.com/401439. // See https://crbug.com/401439.
if (profile->IsOffTheRecord()) if (context->IsOffTheRecord())
return NULL; return nullptr;
if (!instance_id::InstanceIDProfileService::IsInstanceIDEnabled(
Profile::FromBrowserContext(context))) {
LOG(WARNING) << "PushMessagingService could not be built because "
"InstanceID is unexpectedly disabled";
return nullptr;
}
return static_cast<PushMessagingServiceImpl*>( return static_cast<PushMessagingServiceImpl*>(
GetInstance()->GetServiceForBrowserContext(profile, true)); GetInstance()->GetServiceForBrowserContext(context, true));
} }
// static // static
...@@ -39,6 +47,7 @@ PushMessagingServiceFactory::PushMessagingServiceFactory() ...@@ -39,6 +47,7 @@ PushMessagingServiceFactory::PushMessagingServiceFactory()
BrowserContextDependencyManager::GetInstance()) { BrowserContextDependencyManager::GetInstance()) {
DependsOn(BudgetManagerFactory::GetInstance()); DependsOn(BudgetManagerFactory::GetInstance());
DependsOn(gcm::GCMProfileServiceFactory::GetInstance()); DependsOn(gcm::GCMProfileServiceFactory::GetInstance());
DependsOn(instance_id::InstanceIDProfileServiceFactory::GetInstance());
DependsOn(HostContentSettingsMapFactory::GetInstance()); DependsOn(HostContentSettingsMapFactory::GetInstance());
DependsOn(PermissionManagerFactory::GetInstance()); DependsOn(PermissionManagerFactory::GetInstance());
} }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "components/gcm_driver/common/gcm_messages.h" #include "components/gcm_driver/common/gcm_messages.h"
#include "components/gcm_driver/gcm_app_handler.h" #include "components/gcm_driver/gcm_app_handler.h"
#include "components/gcm_driver/gcm_client.h" #include "components/gcm_driver/gcm_client.h"
#include "components/gcm_driver/instance_id/instance_id.h"
#include "components/keyed_service/core/keyed_service.h" #include "components/keyed_service/core/keyed_service.h"
#include "content/public/browser/push_messaging_service.h" #include "content/public/browser/push_messaging_service.h"
#include "content/public/common/push_event_payload.h" #include "content/public/common/push_event_payload.h"
...@@ -36,12 +37,15 @@ ...@@ -36,12 +37,15 @@
class Profile; class Profile;
class PushMessagingAppIdentifier; class PushMessagingAppIdentifier;
class PushMessagingServiceObserver; class PushMessagingServiceObserver;
class PushMessagingServiceTest;
class ScopedKeepAlive; class ScopedKeepAlive;
struct PushSubscriptionOptions; struct PushSubscriptionOptions;
namespace gcm { namespace gcm {
class GCMDriver; class GCMDriver;
class GCMProfileService; }
namespace instance_id {
class InstanceIDDriver;
} }
namespace user_prefs { namespace user_prefs {
...@@ -89,6 +93,7 @@ class PushMessagingServiceImpl : public content::PushMessagingService, ...@@ -89,6 +93,7 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
void GetEncryptionInfo( void GetEncryptionInfo(
const GURL& origin, const GURL& origin,
int64_t service_worker_registration_id, int64_t service_worker_registration_id,
const std::string& sender_id,
const content::PushMessagingService::EncryptionInfoCallback& callback) const content::PushMessagingService::EncryptionInfoCallback& callback)
override; override;
void Unsubscribe( void Unsubscribe(
...@@ -120,6 +125,7 @@ class PushMessagingServiceImpl : public content::PushMessagingService, ...@@ -120,6 +125,7 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
const base::Closure& callback); const base::Closure& callback);
private: private:
friend class PushMessagingBrowserTest;
FRIEND_TEST_ALL_PREFIXES(PushMessagingServiceTest, NormalizeSenderInfo); FRIEND_TEST_ALL_PREFIXES(PushMessagingServiceTest, NormalizeSenderInfo);
FRIEND_TEST_ALL_PREFIXES(PushMessagingServiceTest, PayloadEncryptionTest); FRIEND_TEST_ALL_PREFIXES(PushMessagingServiceTest, PayloadEncryptionTest);
...@@ -141,6 +147,12 @@ class PushMessagingServiceImpl : public content::PushMessagingService, ...@@ -141,6 +147,12 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
// Subscribe methods --------------------------------------------------------- // Subscribe methods ---------------------------------------------------------
void DoSubscribe(
const PushMessagingAppIdentifier& app_identifier,
const content::PushSubscriptionOptions& options,
const content::PushMessagingService::RegisterCallback& callback,
blink::mojom::PermissionStatus permission_status);
void SubscribeEnd( void SubscribeEnd(
const content::PushMessagingService::RegisterCallback& callback, const content::PushMessagingService::RegisterCallback& callback,
const std::string& subscription_id, const std::string& subscription_id,
...@@ -154,9 +166,10 @@ class PushMessagingServiceImpl : public content::PushMessagingService, ...@@ -154,9 +166,10 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
void DidSubscribe( void DidSubscribe(
const PushMessagingAppIdentifier& app_identifier, const PushMessagingAppIdentifier& app_identifier,
const std::string& sender_id,
const content::PushMessagingService::RegisterCallback& callback, const content::PushMessagingService::RegisterCallback& callback,
const std::string& subscription_id, const std::string& subscription_id,
gcm::GCMClient::Result result); instance_id::InstanceID::Result result);
void DidSubscribeWithEncryptionInfo( void DidSubscribeWithEncryptionInfo(
const PushMessagingAppIdentifier& app_identifier, const PushMessagingAppIdentifier& app_identifier,
...@@ -165,12 +178,6 @@ class PushMessagingServiceImpl : public content::PushMessagingService, ...@@ -165,12 +178,6 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
const std::string& p256dh, const std::string& p256dh,
const std::string& auth_secret); const std::string& auth_secret);
void DidRequestPermission(
const PushMessagingAppIdentifier& app_identifier,
const content::PushSubscriptionOptions& options,
const content::PushMessagingService::RegisterCallback& callback,
blink::mojom::PermissionStatus permission_status);
// GetEncryptionInfo method -------------------------------------------------- // GetEncryptionInfo method --------------------------------------------------
void DidGetEncryptionInfo( void DidGetEncryptionInfo(
...@@ -184,6 +191,17 @@ class PushMessagingServiceImpl : public content::PushMessagingService, ...@@ -184,6 +191,17 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
const std::string& sender_id, const std::string& sender_id,
const content::PushMessagingService::UnregisterCallback&); const content::PushMessagingService::UnregisterCallback&);
void DidDeleteID(const std::string& app_id,
bool was_subscribed,
const content::PushMessagingService::UnregisterCallback&,
instance_id::InstanceID::Result result);
void DidUnsubscribeInstanceID(
const std::string& app_id,
bool was_subscribed,
const content::PushMessagingService::UnregisterCallback&,
instance_id::InstanceID::Result result);
void DidUnsubscribe(bool was_subscribed, void DidUnsubscribe(bool was_subscribed,
const content::PushMessagingService::UnregisterCallback&, const content::PushMessagingService::UnregisterCallback&,
gcm::GCMClient::Result result); gcm::GCMClient::Result result);
...@@ -207,8 +225,16 @@ class PushMessagingServiceImpl : public content::PushMessagingService, ...@@ -207,8 +225,16 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
// Checks if a given origin is allowed to use Push. // Checks if a given origin is allowed to use Push.
bool IsPermissionSet(const GURL& origin); bool IsPermissionSet(const GURL& origin);
// Wrapper around {GCMDriver, InstanceID}::GetEncryptionInfo.
void GetEncryptionInfoForAppId(
const std::string& app_id,
const std::string& sender_id,
gcm::GCMEncryptionProvider::EncryptionInfoCallback callback);
gcm::GCMDriver* GetGCMDriver() const; gcm::GCMDriver* GetGCMDriver() const;
instance_id::InstanceIDDriver* GetInstanceIDDriver() const;
// Testing methods ----------------------------------------------------------- // Testing methods -----------------------------------------------------------
// Callback to be invoked when a message has been dispatched. Enables tests to // Callback to be invoked when a message has been dispatched. Enables tests to
......
...@@ -31,6 +31,11 @@ ...@@ -31,6 +31,11 @@
#include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_ANDROID)
#include "components/gcm_driver/instance_id/instance_id_android.h"
#include "components/gcm_driver/instance_id/scoped_use_fake_instance_id_android.h"
#endif // OS_ANDROID
namespace { namespace {
const char kTestOrigin[] = "https://example.com"; const char kTestOrigin[] = "https://example.com";
...@@ -101,16 +106,19 @@ class PushMessagingServiceTest : public ::testing::Test { ...@@ -101,16 +106,19 @@ class PushMessagingServiceTest : public ::testing::Test {
void DidRegister(std::string* subscription_id_out, void DidRegister(std::string* subscription_id_out,
std::vector<uint8_t>* p256dh_out, std::vector<uint8_t>* p256dh_out,
std::vector<uint8_t>* auth_out, std::vector<uint8_t>* auth_out,
base::Closure done_callback,
const std::string& registration_id, const std::string& registration_id,
const std::vector<uint8_t>& p256dh, const std::vector<uint8_t>& p256dh,
const std::vector<uint8_t>& auth, const std::vector<uint8_t>& auth,
content::PushRegistrationStatus status) { content::PushRegistrationStatus status) {
ASSERT_EQ(content::PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE, EXPECT_EQ(content::PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE,
status); status);
*subscription_id_out = registration_id; *subscription_id_out = registration_id;
*p256dh_out = p256dh; *p256dh_out = p256dh;
*auth_out = auth; *auth_out = auth;
done_callback.Run();
} }
// Callback to use when observing messages dispatched by the push service. // Callback to use when observing messages dispatched by the push service.
...@@ -134,6 +142,12 @@ class PushMessagingServiceTest : public ::testing::Test { ...@@ -134,6 +142,12 @@ class PushMessagingServiceTest : public ::testing::Test {
private: private:
content::TestBrowserThreadBundle thread_bundle_; content::TestBrowserThreadBundle thread_bundle_;
PushMessagingTestingProfile profile_; PushMessagingTestingProfile profile_;
#if defined(OS_ANDROID)
instance_id::InstanceIDAndroid::ScopedBlockOnAsyncTasksForTesting
block_async_;
instance_id::ScopedUseFakeInstanceIDAndroid use_fake_;
#endif // OS_ANDROID
}; };
TEST_F(PushMessagingServiceTest, PayloadEncryptionTest) { TEST_F(PushMessagingServiceTest, PayloadEncryptionTest) {
...@@ -149,6 +163,8 @@ TEST_F(PushMessagingServiceTest, PayloadEncryptionTest) { ...@@ -149,6 +163,8 @@ TEST_F(PushMessagingServiceTest, PayloadEncryptionTest) {
std::string subscription_id; std::string subscription_id;
std::vector<uint8_t> p256dh, auth; std::vector<uint8_t> p256dh, auth;
base::RunLoop run_loop;
// (2) Subscribe for Push Messaging, and verify that we've got the required // (2) Subscribe for Push Messaging, and verify that we've got the required
// information in order to be able to create encrypted messages. // information in order to be able to create encrypted messages.
content::PushSubscriptionOptions options; content::PushSubscriptionOptions options;
...@@ -157,11 +173,11 @@ TEST_F(PushMessagingServiceTest, PayloadEncryptionTest) { ...@@ -157,11 +173,11 @@ TEST_F(PushMessagingServiceTest, PayloadEncryptionTest) {
push_service->SubscribeFromWorker( push_service->SubscribeFromWorker(
origin, kTestServiceWorkerId, options, origin, kTestServiceWorkerId, options,
base::Bind(&PushMessagingServiceTest::DidRegister, base::Unretained(this), base::Bind(&PushMessagingServiceTest::DidRegister, base::Unretained(this),
&subscription_id, &p256dh, &auth)); &subscription_id, &p256dh, &auth, run_loop.QuitClosure()));
EXPECT_EQ(0u, subscription_id.size()); // this must be asynchronous EXPECT_EQ(0u, subscription_id.size()); // this must be asynchronous
base::RunLoop().RunUntilIdle(); run_loop.Run();
ASSERT_GT(subscription_id.size(), 0u); ASSERT_GT(subscription_id.size(), 0u);
ASSERT_GT(p256dh.size(), 0u); ASSERT_GT(p256dh.size(), 0u);
......
...@@ -16,15 +16,14 @@ ...@@ -16,15 +16,14 @@
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "components/gcm_driver/fake_gcm_client_factory.h" #include "components/gcm_driver/fake_gcm_client_factory.h"
#include "components/gcm_driver/fake_gcm_driver.h" #include "components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.h"
#include "components/gcm_driver/gcm_driver.h"
#include "content/public/browser/browser_context.h" #include "content/public/browser/browser_context.h"
namespace gcm { namespace gcm {
namespace { namespace {
class CustomFakeGCMDriver : public FakeGCMDriver { class CustomFakeGCMDriver : public instance_id::FakeGCMDriverForInstanceID {
public: public:
explicit CustomFakeGCMDriver(FakeGCMProfileService* service); explicit CustomFakeGCMDriver(FakeGCMProfileService* service);
~CustomFakeGCMDriver() override; ~CustomFakeGCMDriver() override;
...@@ -42,7 +41,7 @@ class CustomFakeGCMDriver : public FakeGCMDriver { ...@@ -42,7 +41,7 @@ class CustomFakeGCMDriver : public FakeGCMDriver {
const IncomingMessage& message); const IncomingMessage& message);
protected: protected:
// FakeGCMDriver overrides: // FakeGCMDriverForInstanceID overrides:
void RegisterImpl(const std::string& app_id, void RegisterImpl(const std::string& app_id,
const std::vector<std::string>& sender_ids) override; const std::vector<std::string>& sender_ids) override;
void UnregisterImpl(const std::string& app_id) override; void UnregisterImpl(const std::string& app_id) override;
...@@ -59,9 +58,9 @@ class CustomFakeGCMDriver : public FakeGCMDriver { ...@@ -59,9 +58,9 @@ class CustomFakeGCMDriver : public FakeGCMDriver {
}; };
CustomFakeGCMDriver::CustomFakeGCMDriver(FakeGCMProfileService* service) CustomFakeGCMDriver::CustomFakeGCMDriver(FakeGCMProfileService* service)
: FakeGCMDriver(base::ThreadTaskRunnerHandle::Get()), : instance_id::FakeGCMDriverForInstanceID(
service_(service) { base::ThreadTaskRunnerHandle::Get()),
} service_(service) {}
CustomFakeGCMDriver::~CustomFakeGCMDriver() { CustomFakeGCMDriver::~CustomFakeGCMDriver() {
} }
......
...@@ -3443,6 +3443,8 @@ test("unit_tests") { ...@@ -3443,6 +3443,8 @@ test("unit_tests") {
sources -= [ "../browser/policy/policy_path_parser_unittest.cc" ] sources -= [ "../browser/policy/policy_path_parser_unittest.cc" ]
deps += [ deps += [
":unit_tests_java", ":unit_tests_java",
"//components/gcm_driver/instance_id/android:instance_id_driver_java",
"//components/gcm_driver/instance_id/android:instance_id_driver_test_support_java",
"//components/offline_pages:test_support", "//components/offline_pages:test_support",
"//v8:v8_external_startup_data_assets", "//v8:v8_external_startup_data_assets",
] ]
......
...@@ -22,7 +22,6 @@ android_library("gcm_driver_java") { ...@@ -22,7 +22,6 @@ android_library("gcm_driver_java") {
] ]
java_files = [ java_files = [
"java/src/org/chromium/components/gcm_driver/FakeGoogleCloudMessagingSubscriber.java",
"java/src/org/chromium/components/gcm_driver/GCMDriver.java", "java/src/org/chromium/components/gcm_driver/GCMDriver.java",
"java/src/org/chromium/components/gcm_driver/GoogleCloudMessagingSubscriber.java", "java/src/org/chromium/components/gcm_driver/GoogleCloudMessagingSubscriber.java",
"java/src/org/chromium/components/gcm_driver/GoogleCloudMessagingV2.java", "java/src/org/chromium/components/gcm_driver/GoogleCloudMessagingV2.java",
......
// 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.
package org.chromium.components.gcm_driver;
import android.os.Bundle;
import org.chromium.base.VisibleForTesting;
import java.io.IOException;
import javax.annotation.Nullable;
/**
* A fake subscriber for test purposes.
*/
public class FakeGoogleCloudMessagingSubscriber implements GoogleCloudMessagingSubscriber {
private static final String FAKE_REGISTRATION_ID = "FAKE_REGISTRATION_ID";
private String mLastSubscribeSource;
private String mLastSubscribeSubtype;
@Override
public String subscribe(String source, String subtype, @Nullable Bundle data)
throws IOException {
mLastSubscribeSource = source;
mLastSubscribeSubtype = subtype;
return FAKE_REGISTRATION_ID;
}
@Override
public void unsubscribe(String source, String subtype, @Nullable Bundle data)
throws IOException {
// No need to do anything here yet.
}
/**
* The source (sender id) passed to #subscribe the last time it was called, or null if it was
* never called.
*/
@Nullable
@VisibleForTesting
public String getLastSubscribeSource() {
return mLastSubscribeSource;
}
/**
* The subtype (app id) passed to #subscribe the last time it was called, or null if it was
* never called.
*/
@Nullable
@VisibleForTesting
public String getLastSubscribeSubtype() {
return mLastSubscribeSubtype;
}
}
...@@ -9,6 +9,10 @@ ...@@ -9,6 +9,10 @@
#include "base/macros.h" #include "base/macros.h"
#include "components/gcm_driver/gcm_driver.h" #include "components/gcm_driver/gcm_driver.h"
namespace base {
class SequencedTaskRunner;
}
namespace gcm { namespace gcm {
class FakeGCMDriver : public GCMDriver { class FakeGCMDriver : public GCMDriver {
......
...@@ -26,7 +26,7 @@ public class InstanceIDWithSubtype extends InstanceID { ...@@ -26,7 +26,7 @@ public class InstanceIDWithSubtype extends InstanceID {
/** Cached instances. May be accessed from multiple threads; synchronize on InstanceID.class. */ /** Cached instances. May be accessed from multiple threads; synchronize on InstanceID.class. */
@VisibleForTesting @VisibleForTesting
@SuppressFBWarnings("MS_MUTABLE_COLLECTION_PKGPROTECT") @SuppressFBWarnings("MS_MUTABLE_COLLECTION_PKGPROTECT")
protected static final Map<String, InstanceIDWithSubtype> sSubtypeInstances = new HashMap<>(); public static final Map<String, InstanceIDWithSubtype> sSubtypeInstances = new HashMap<>();
/** Fake subclasses can set this so getInstance creates instances of them. */ /** Fake subclasses can set this so getInstance creates instances of them. */
@VisibleForTesting @VisibleForTesting
......
...@@ -7,6 +7,7 @@ package org.chromium.components.gcm_driver.instance_id; ...@@ -7,6 +7,7 @@ package org.chromium.components.gcm_driver.instance_id;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.os.Looper; import android.os.Looper;
import android.util.Pair;
import com.google.android.gms.iid.InstanceID; import com.google.android.gms.iid.InstanceID;
...@@ -27,6 +28,8 @@ import java.util.Random; ...@@ -27,6 +28,8 @@ import java.util.Random;
public class FakeInstanceIDWithSubtype extends InstanceIDWithSubtype { public class FakeInstanceIDWithSubtype extends InstanceIDWithSubtype {
private String mId = null; private String mId = null;
private long mCreationTime = 0; private long mCreationTime = 0;
/** Map from (subtype + ',' + authorizedEntity + ',' + scope) to token. */
private Map<String, String> mTokens = new HashMap<>(); private Map<String, String> mTokens = new HashMap<>();
/** /**
...@@ -52,6 +55,30 @@ public class FakeInstanceIDWithSubtype extends InstanceIDWithSubtype { ...@@ -52,6 +55,30 @@ public class FakeInstanceIDWithSubtype extends InstanceIDWithSubtype {
} }
} }
/**
* If exactly one instance of InstanceID exists, and it has exactly one token, this returns
* the subtype of the InstanceID and the authorizedEntity of the token. Otherwise it throws.
* If a test fails with no InstanceID or no tokens, it probably means subscribing failed, or
* that the test subscribed in the wrong way (e.g. a GCM registration rather than an InstanceID
* token). If a test fails with too many InstanceIDs/tokens, the test subscribed too many times.
*/
public static Pair<String, String> getSubtypeAndAuthorizedEntityOfOnlyToken() {
if (InstanceIDWithSubtype.sSubtypeInstances.size() != 1) {
throw new IllegalStateException("Expected exactly one InstanceID, but there are "
+ InstanceIDWithSubtype.sSubtypeInstances.size());
}
FakeInstanceIDWithSubtype iid =
(FakeInstanceIDWithSubtype) InstanceIDWithSubtype.sSubtypeInstances.values()
.iterator()
.next();
if (iid.mTokens.size() != 1) {
throw new IllegalStateException(
"Expected exactly one token, but there are " + iid.mTokens.size());
}
String authorizedEntity = iid.mTokens.keySet().iterator().next().split(",", 3)[1];
return Pair.create(iid.getSubtype(), authorizedEntity);
}
private FakeInstanceIDWithSubtype(Context context, String subtype) { private FakeInstanceIDWithSubtype(Context context, String subtype) {
super(context, subtype); super(context, subtype);
......
...@@ -17,6 +17,10 @@ namespace instance_id { ...@@ -17,6 +17,10 @@ namespace instance_id {
FakeGCMDriverForInstanceID::FakeGCMDriverForInstanceID() FakeGCMDriverForInstanceID::FakeGCMDriverForInstanceID()
: gcm::FakeGCMDriver(base::ThreadTaskRunnerHandle::Get()) {} : gcm::FakeGCMDriver(base::ThreadTaskRunnerHandle::Get()) {}
FakeGCMDriverForInstanceID::FakeGCMDriverForInstanceID(
const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
: FakeGCMDriver(blocking_task_runner) {}
FakeGCMDriverForInstanceID::~FakeGCMDriverForInstanceID() { FakeGCMDriverForInstanceID::~FakeGCMDriverForInstanceID() {
} }
...@@ -67,6 +71,9 @@ void FakeGCMDriverForInstanceID::GetToken( ...@@ -67,6 +71,9 @@ void FakeGCMDriverForInstanceID::GetToken(
tokens_[key] = token; tokens_[key] = token;
} }
last_gettoken_app_id_ = app_id;
last_gettoken_authorized_entity_ = authorized_entity;
base::ThreadTaskRunnerHandle::Get()->PostTask( base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(callback, token, gcm::GCMClient::SUCCESS)); FROM_HERE, base::Bind(callback, token, gcm::GCMClient::SUCCESS));
} }
......
...@@ -13,12 +13,18 @@ ...@@ -13,12 +13,18 @@
#include "base/macros.h" #include "base/macros.h"
#include "components/gcm_driver/fake_gcm_driver.h" #include "components/gcm_driver/fake_gcm_driver.h"
namespace base {
class SequencedTaskRunner;
}
namespace instance_id { namespace instance_id {
class FakeGCMDriverForInstanceID : public gcm::FakeGCMDriver, class FakeGCMDriverForInstanceID : public gcm::FakeGCMDriver,
public gcm::InstanceIDHandler { public gcm::InstanceIDHandler {
public: public:
FakeGCMDriverForInstanceID(); FakeGCMDriverForInstanceID();
explicit FakeGCMDriverForInstanceID(
const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner);
~FakeGCMDriverForInstanceID() override; ~FakeGCMDriverForInstanceID() override;
// FakeGCMDriver overrides: // FakeGCMDriver overrides:
...@@ -42,9 +48,19 @@ class FakeGCMDriverForInstanceID : public gcm::FakeGCMDriver, ...@@ -42,9 +48,19 @@ class FakeGCMDriverForInstanceID : public gcm::FakeGCMDriver,
const std::string& app_id, const std::string& app_id,
const GetInstanceIDDataCallback& callback) override; const GetInstanceIDDataCallback& callback) override;
const std::string& last_gettoken_app_id() const {
return last_gettoken_app_id_;
}
const std::string& last_gettoken_authorized_entity() const {
return last_gettoken_authorized_entity_;
}
private: private:
std::map<std::string, std::pair<std::string, std::string>> instance_id_data_; std::map<std::string, std::pair<std::string, std::string>> instance_id_data_;
std::map<std::string, std::string> tokens_; std::map<std::string, std::string> tokens_;
std::string last_gettoken_app_id_;
std::string last_gettoken_authorized_entity_;
DISALLOW_COPY_AND_ASSIGN(FakeGCMDriverForInstanceID); DISALLOW_COPY_AND_ASSIGN(FakeGCMDriverForInstanceID);
}; };
......
...@@ -36,8 +36,9 @@ ...@@ -36,8 +36,9 @@
namespace content { namespace content {
// Service Worker database keys. If a registration ID is stored, the stored // Service Worker database keys. If a registration ID is stored, the stored
// sender ID must be the one used to subscribe. Unfortunately, this isn't always // sender ID must be the one used to register. Unfortunately, this isn't always
// true of subscriptions previously stored in the database. // true of pre-InstanceID registrations previously stored in the database, but
// fortunately it's less important for their sender ID to be accurate.
const char kPushSenderIdServiceWorkerKey[] = "push_sender_id"; const char kPushSenderIdServiceWorkerKey[] = "push_sender_id";
const char kPushRegistrationIdServiceWorkerKey[] = "push_registration_id"; const char kPushRegistrationIdServiceWorkerKey[] = "push_registration_id";
...@@ -136,6 +137,7 @@ class PushMessagingMessageFilter::Core { ...@@ -136,6 +137,7 @@ class PushMessagingMessageFilter::Core {
void GetEncryptionInfoOnUI( void GetEncryptionInfoOnUI(
const GURL& origin, const GURL& origin,
int64_t service_worker_registration_id, int64_t service_worker_registration_id,
const std::string& sender_id,
const PushMessagingService::EncryptionInfoCallback& io_thread_callback); const PushMessagingService::EncryptionInfoCallback& io_thread_callback);
// Called (directly) from both the UI and IO threads. // Called (directly) from both the UI and IO threads.
...@@ -304,7 +306,8 @@ void PushMessagingMessageFilter::DidCheckForExistingRegistration( ...@@ -304,7 +306,8 @@ void PushMessagingMessageFilter::DidCheckForExistingRegistration(
BrowserThread::UI, FROM_HERE, BrowserThread::UI, FROM_HERE,
base::Bind(&Core::GetEncryptionInfoOnUI, base::Bind(&Core::GetEncryptionInfoOnUI,
base::Unretained(ui_core_.get()), data.requesting_origin, base::Unretained(ui_core_.get()), data.requesting_origin,
data.service_worker_registration_id, callback)); data.service_worker_registration_id,
data.options.sender_info, callback));
return; return;
} }
// TODO(johnme): The spec allows the register algorithm to reject with an // TODO(johnme): The spec allows the register algorithm to reject with an
...@@ -365,6 +368,8 @@ void PushMessagingMessageFilter::Core::RegisterOnUI( ...@@ -365,6 +368,8 @@ void PushMessagingMessageFilter::Core::RegisterOnUI(
PushMessagingService* push_service = service(); PushMessagingService* push_service = service();
if (!push_service) { if (!push_service) {
if (!is_incognito()) { if (!is_incognito()) {
// This might happen if InstanceIDProfileService::IsInstanceIDEnabled
// returns false because the Instance ID kill switch was enabled.
// TODO(johnme): Might be better not to expose the API in this case. // TODO(johnme): Might be better not to expose the API in this case.
BrowserThread::PostTask( BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE, BrowserThread::IO, FROM_HERE,
...@@ -753,6 +758,11 @@ void PushMessagingMessageFilter::DidGetSubscription( ...@@ -753,6 +758,11 @@ void PushMessagingMessageFilter::DidGetSubscription(
break; break;
} }
ServiceWorkerRegistration* registration =
service_worker_context_->GetLiveRegistration(
service_worker_registration_id);
const GURL origin = registration->pattern().GetOrigin();
const bool uses_standard_protocol = const bool uses_standard_protocol =
IsApplicationServerKey(push_subscription_id_and_sender_info[1]); IsApplicationServerKey(push_subscription_id_and_sender_info[1]);
const GURL endpoint = CreateEndpoint( const GURL endpoint = CreateEndpoint(
...@@ -763,16 +773,12 @@ void PushMessagingMessageFilter::DidGetSubscription( ...@@ -763,16 +773,12 @@ void PushMessagingMessageFilter::DidGetSubscription(
weak_factory_io_to_io_.GetWeakPtr(), request_id, endpoint, weak_factory_io_to_io_.GetWeakPtr(), request_id, endpoint,
push_subscription_id_and_sender_info[1]); push_subscription_id_and_sender_info[1]);
ServiceWorkerRegistration* registration =
service_worker_context_->GetLiveRegistration(
service_worker_registration_id);
const GURL origin = registration->pattern().GetOrigin();
BrowserThread::PostTask( BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE, BrowserThread::UI, FROM_HERE,
base::Bind(&Core::GetEncryptionInfoOnUI, base::Bind(&Core::GetEncryptionInfoOnUI,
base::Unretained(ui_core_.get()), origin, base::Unretained(ui_core_.get()), origin,
service_worker_registration_id, callback)); service_worker_registration_id,
push_subscription_id_and_sender_info[1], callback));
return; return;
} }
...@@ -900,12 +906,13 @@ void PushMessagingMessageFilter::Core::GetPermissionStatusOnUI( ...@@ -900,12 +906,13 @@ void PushMessagingMessageFilter::Core::GetPermissionStatusOnUI(
void PushMessagingMessageFilter::Core::GetEncryptionInfoOnUI( void PushMessagingMessageFilter::Core::GetEncryptionInfoOnUI(
const GURL& origin, const GURL& origin,
int64_t service_worker_registration_id, int64_t service_worker_registration_id,
const std::string& sender_id,
const PushMessagingService::EncryptionInfoCallback& io_thread_callback) { const PushMessagingService::EncryptionInfoCallback& io_thread_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
PushMessagingService* push_service = service(); PushMessagingService* push_service = service();
if (push_service) { if (push_service) {
push_service->GetEncryptionInfo( push_service->GetEncryptionInfo(
origin, service_worker_registration_id, origin, service_worker_registration_id, sender_id,
base::Bind(&ForwardEncryptionInfoToIOThreadProxy, io_thread_callback)); base::Bind(&ForwardEncryptionInfoToIOThreadProxy, io_thread_callback));
return; return;
} }
......
...@@ -59,6 +59,22 @@ void ClearPushSubscriptionIDOnIO( ...@@ -59,6 +59,22 @@ void ClearPushSubscriptionIDOnIO(
base::Bind(&CallClosureFromIO, callback)); base::Bind(&CallClosureFromIO, callback));
} }
void StorePushSubscriptionOnIOForTesting(
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
int64_t service_worker_registration_id,
const GURL& origin,
const std::string& subscription_id,
const std::string& sender_id,
const base::Closure& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
service_worker_context->StoreRegistrationUserData(
service_worker_registration_id, origin,
{{kPushRegistrationIdServiceWorkerKey, subscription_id},
{kPushSenderIdServiceWorkerKey, sender_id}},
base::Bind(&CallClosureFromIO, callback));
}
scoped_refptr<ServiceWorkerContextWrapper> GetServiceWorkerContext( scoped_refptr<ServiceWorkerContextWrapper> GetServiceWorkerContext(
BrowserContext* browser_context, const GURL& origin) { BrowserContext* browser_context, const GURL& origin) {
StoragePartition* partition = StoragePartition* partition =
...@@ -102,4 +118,21 @@ void PushMessagingService::ClearPushSubscriptionID( ...@@ -102,4 +118,21 @@ void PushMessagingService::ClearPushSubscriptionID(
callback)); callback));
} }
// static
void PushMessagingService::StorePushSubscriptionForTesting(
BrowserContext* browser_context,
const GURL& origin,
int64_t service_worker_registration_id,
const std::string& subscription_id,
const std::string& sender_id,
const base::Closure& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&StorePushSubscriptionOnIOForTesting,
GetServiceWorkerContext(browser_context, origin),
service_worker_registration_id, origin, subscription_id,
sender_id, callback));
}
} // namespace content } // namespace content
...@@ -67,9 +67,12 @@ class CONTENT_EXPORT PushMessagingService { ...@@ -67,9 +67,12 @@ class CONTENT_EXPORT PushMessagingService {
const RegisterCallback& callback) = 0; const RegisterCallback& callback) = 0;
// Retrieves the encryption information associated with the subscription // Retrieves the encryption information associated with the subscription
// associated to |origin| and |service_worker_registration_id|. // associated to |origin| and |service_worker_registration_id|. |sender_id| is
// also required since an InstanceID might have multiple tokens associated
// with different senders, though in practice Push doesn't yet use that.
virtual void GetEncryptionInfo(const GURL& origin, virtual void GetEncryptionInfo(const GURL& origin,
int64_t service_worker_registration_id, int64_t service_worker_registration_id,
const std::string& sender_id,
const EncryptionInfoCallback& callback) = 0; const EncryptionInfoCallback& callback) = 0;
// Unsubscribe the given |sender_id| from the push messaging service. The // Unsubscribe the given |sender_id| from the push messaging service. The
...@@ -104,6 +107,16 @@ class CONTENT_EXPORT PushMessagingService { ...@@ -104,6 +107,16 @@ class CONTENT_EXPORT PushMessagingService {
const GURL& origin, const GURL& origin,
int64_t service_worker_registration_id, int64_t service_worker_registration_id,
const base::Closure& callback); const base::Closure& callback);
// Stores a push subscription in the service worker for the given |origin|.
// Must only be used by tests.
static void StorePushSubscriptionForTesting(
BrowserContext* browser_context,
const GURL& origin,
int64_t service_worker_registration_id,
const std::string& subscription_id,
const std::string& sender_id,
const base::Closure& callback);
}; };
} // namespace content } // namespace content
......
...@@ -101,6 +101,7 @@ void LayoutTestPushMessagingService::SubscribeFromWorker( ...@@ -101,6 +101,7 @@ void LayoutTestPushMessagingService::SubscribeFromWorker(
void LayoutTestPushMessagingService::GetEncryptionInfo( void LayoutTestPushMessagingService::GetEncryptionInfo(
const GURL& origin, const GURL& origin,
int64_t service_worker_registration_id, int64_t service_worker_registration_id,
const std::string& sender_id,
const EncryptionInfoCallback& callback) { const EncryptionInfoCallback& callback) {
std::vector<uint8_t> p256dh( std::vector<uint8_t> p256dh(
kTestP256Key, kTestP256Key + arraysize(kTestP256Key)); kTestP256Key, kTestP256Key + arraysize(kTestP256Key));
......
...@@ -41,6 +41,7 @@ class LayoutTestPushMessagingService : public PushMessagingService { ...@@ -41,6 +41,7 @@ class LayoutTestPushMessagingService : public PushMessagingService {
void GetEncryptionInfo( void GetEncryptionInfo(
const GURL& origin, const GURL& origin,
int64_t service_worker_registration_id, int64_t service_worker_registration_id,
const std::string& sender_id,
const PushMessagingService::EncryptionInfoCallback& callback) override; const PushMessagingService::EncryptionInfoCallback& callback) override;
blink::WebPushPermissionStatus GetPermissionStatus(const GURL& origin, blink::WebPushPermissionStatus GetPermissionStatus(const GURL& origin,
bool user_visible) bool user_visible)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment