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") {
"//components/bookmarks/common/android:bookmarks_java",
"//components/dom_distiller/core/android:dom_distiller_core_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:javatests",
"//components/location/android:location_java",
......
......@@ -2,6 +2,7 @@ include_rules = [
"+components/bookmarks/common/android/java/src/org/chromium/components/bookmarks",
"+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/instance_id/android/javatests/src/org/chromium/components/gcm_driver/instance_id",
"+components/location/android/java",
"+components/navigation_interception",
"+components/precache/android/javatests",
......
......@@ -13,6 +13,7 @@ import android.os.Bundle;
import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Pair;
import org.chromium.base.ThreadUtils;
import org.chromium.base.library_loader.ProcessInitException;
......@@ -25,8 +26,8 @@ import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.util.InfoBarUtil;
import org.chromium.chrome.test.util.browser.TabTitleObserver;
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.instance_id.FakeInstanceIDWithSubtype;
import org.chromium.content.browser.test.util.CallbackHelper;
import org.chromium.content.browser.test.util.Criteria;
import org.chromium.content.browser.test.util.CriteriaHelper;
......@@ -63,6 +64,7 @@ public class PushMessagingTest
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
FakeInstanceIDWithSubtype.clearDataAndSetEnabled(true);
PushMessagingServiceObserver.setListenerForTesting(listener);
}
});
......@@ -75,6 +77,7 @@ public class PushMessagingTest
@Override
public void run() {
PushMessagingServiceObserver.setListenerForTesting(null);
FakeInstanceIDWithSubtype.clearDataAndSetEnabled(false);
}
});
super.tearDown();
......@@ -91,9 +94,6 @@ public class PushMessagingTest
@MediumTest
@Feature({"Browser", "PushMessaging"})
public void testPushPermissionInfobar() throws InterruptedException, TimeoutException {
FakeGoogleCloudMessagingSubscriber subscriber = new FakeGoogleCloudMessagingSubscriber();
GCMDriver.overrideSubscriberForTesting(subscriber);
loadUrl(mPushTestPage);
WebContents webContents = getActivity().getActivityTab().getWebContents();
assertEquals(0, getInfoBars().size());
......@@ -128,15 +128,13 @@ public class PushMessagingTest
@MediumTest
@Feature({"Browser", "PushMessaging"})
public void testPushAndShowNotification() throws InterruptedException, TimeoutException {
FakeGoogleCloudMessagingSubscriber subscriber = new FakeGoogleCloudMessagingSubscriber();
GCMDriver.overrideSubscriberForTesting(subscriber);
loadUrl(mPushTestPage);
setNotificationContentSettingForCurrentOrigin(ContentSetting.ALLOW);
runScriptAndWaitForTitle("subscribePush()", "subscribe ok");
sendPushAndWaitForCallback(
subscriber.getLastSubscribeSubtype(), subscriber.getLastSubscribeSource());
Pair<String, String> appIdAndSenderId =
FakeInstanceIDWithSubtype.getSubtypeAndAuthorizedEntityOfOnlyToken();
sendPushAndWaitForCallback(appIdAndSenderId);
NotificationEntry notificationEntry = waitForNotification();
assertEquals("push notification 1",
notificationEntry.notification.extras.getString(Notification.EXTRA_TITLE));
......@@ -149,9 +147,6 @@ public class PushMessagingTest
@LargeTest
@Feature({"Browser", "PushMessaging"})
public void testDefaultNotification() throws InterruptedException, TimeoutException {
FakeGoogleCloudMessagingSubscriber subscriber = new FakeGoogleCloudMessagingSubscriber();
GCMDriver.overrideSubscriberForTesting(subscriber);
// Load the push test page into the first tab.
loadUrl(mPushTestPage);
assertEquals(1, getActivity().getCurrentTabModel().getCount());
......@@ -162,8 +157,8 @@ public class PushMessagingTest
// Set up the push subscription and capture its details.
setNotificationContentSettingForCurrentOrigin(ContentSetting.ALLOW);
runScriptAndWaitForTitle("subscribePush()", "subscribe ok");
String appId = subscriber.getLastSubscribeSubtype();
String senderId = subscriber.getLastSubscribeSource();
Pair<String, String> appIdAndSenderId =
FakeInstanceIDWithSubtype.getSubtypeAndAuthorizedEntityOfOnlyToken();
// Make the tab invisible by opening another one with a different origin.
loadUrlInNewTab(ABOUT_BLANK);
......@@ -174,17 +169,17 @@ public class PushMessagingTest
// 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.
runScriptAndWaitForTitle("setNotifyOnPush(false)", "setNotifyOnPush false ok", tab);
sendPushAndWaitForCallback(appId, senderId);
sendPushAndWaitForCallback(appIdAndSenderId);
// After grace runs out a default notification will be shown.
sendPushAndWaitForCallback(appId, senderId);
sendPushAndWaitForCallback(appIdAndSenderId);
NotificationEntry notificationEntry = waitForNotification();
MoreAsserts.assertContainsRegex("user_visible_auto_notification", notificationEntry.tag);
// 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.
runScriptAndWaitForTitle("setNotifyOnPush(true)", "setNotifyOnPush true ok", tab);
sendPushAndWaitForCallback(appId, senderId);
sendPushAndWaitForCallback(appIdAndSenderId);
waitForNotificationManagerMutation();
notificationEntry = waitForNotification();
assertEquals("push notification 1",
......@@ -210,8 +205,10 @@ public class PushMessagingTest
waitForTitle(tab, expectedTitle);
}
private void sendPushAndWaitForCallback(final String appId, final String senderId)
private void sendPushAndWaitForCallback(Pair<String, String> appIdAndSenderId)
throws InterruptedException, TimeoutException {
final String appId = appIdAndSenderId.first;
final String senderId = appIdAndSenderId.second;
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
......
......@@ -23,18 +23,6 @@ using extensions::ResultCatcher;
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 {
public:
InstanceIDApiTest();
......@@ -51,7 +39,7 @@ InstanceIDApiTest::InstanceIDApiTest() {
void InstanceIDApiTest::SetUpOnMainThread() {
gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactory(
browser()->profile(), &BuildFakeGCMProfileService);
browser()->profile(), &gcm::FakeGCMProfileService::Build);
ExtensionApiTest::SetUpOnMainThread();
}
......
......@@ -19,6 +19,7 @@
#include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
#include "chrome/browser/ui/tabs/tab_strip_model.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 "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
......@@ -196,7 +197,7 @@ class ServiceWorkerBackgroundSyncTest : public ServiceWorkerTest {
class ServiceWorkerPushMessagingTest : public ServiceWorkerTest {
public:
ServiceWorkerPushMessagingTest()
: gcm_service_(nullptr), push_service_(nullptr) {}
: gcm_driver_(nullptr), push_service_(nullptr) {}
~ServiceWorkerPushMessagingTest() override {}
void GrantNotificationPermissionForTest(const GURL& url) {
......@@ -225,20 +226,25 @@ class ServiceWorkerPushMessagingTest : public ServiceWorkerTest {
ServiceWorkerTest::SetUpCommandLine(command_line);
}
void SetUpOnMainThread() override {
gcm_service_ = static_cast<gcm::FakeGCMProfileService*>(
gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
profile(), &gcm::FakeGCMProfileService::Build));
gcm_service_->set_collect(true);
gcm::FakeGCMProfileService* gcm_service =
static_cast<gcm::FakeGCMProfileService*>(
gcm::GCMProfileServiceFactory::GetInstance()
->SetTestingFactoryAndUse(profile(),
&gcm::FakeGCMProfileService::Build));
gcm_driver_ = static_cast<instance_id::FakeGCMDriverForInstanceID*>(
gcm_service->driver());
push_service_ = PushMessagingServiceFactory::GetForProfile(profile());
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_; }
private:
gcm::FakeGCMProfileService* gcm_service_;
instance_id::FakeGCMDriverForInstanceID* gcm_driver_;
PushMessagingServiceImpl* push_service_;
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerPushMessagingTest);
......@@ -766,8 +772,8 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerPushMessagingTest, OnPush) {
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL, extension_url);
ASSERT_EQ(app_identifier.app_id(), gcm_service()->last_registered_app_id());
EXPECT_EQ("1234567890", gcm_service()->last_registered_sender_ids()[0]);
ASSERT_EQ(app_identifier.app_id(), gcm_driver()->last_gettoken_app_id());
EXPECT_EQ("1234567890", gcm_driver()->last_gettoken_authorized_entity());
base::RunLoop run_loop;
// Send a push message via gcm and expect the ServiceWorker to receive it.
......
......@@ -19,11 +19,13 @@
#include "components/prefs/scoped_user_pref_update.h"
const char kPushMessagingAppIdentifierPrefix[] = "wp:";
const char kInstanceIDGuidSuffix[] = "-V2";
namespace {
// sizeof is strlen + 1 since it's null-terminated.
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 size_t kGuidLength = 36; // "%08X-%04X-%04X-%04X-%012llX"
......@@ -62,6 +64,12 @@ void PushMessagingAppIdentifier::RegisterProfilePrefs(
registry->RegisterDictionaryPref(prefs::kPushMessagingAppIdentifierMap);
}
// static
bool PushMessagingAppIdentifier::UseInstanceID(const std::string& app_id) {
return base::EndsWith(app_id, kInstanceIDGuidSuffix,
base::CompareCase::SENSITIVE);
}
// static
PushMessagingAppIdentifier PushMessagingAppIdentifier::Generate(
const GURL& origin,
......@@ -69,6 +77,9 @@ PushMessagingAppIdentifier PushMessagingAppIdentifier::Generate(
// Use uppercase GUID for consistency with GUIDs Push has already sent to GCM.
// Also allows detecting case mangling; see code commented "crbug.com/461867".
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());
std::string app_id =
kPushMessagingAppIdentifierPrefix + origin.spec() + kSeparator + guid;
......@@ -196,6 +207,7 @@ void PushMessagingAppIdentifier::DeleteFromPrefs(Profile* profile) const {
}
void PushMessagingAppIdentifier::DCheckValid() const {
#if DCHECK_IS_ON()
DCHECK_GE(service_worker_registration_id_, 0);
DCHECK(origin_.is_valid());
......@@ -204,16 +216,29 @@ void PushMessagingAppIdentifier::DCheckValid() const {
// "wp:"
DCHECK_EQ(kPushMessagingAppIdentifierPrefix,
app_id_.substr(0, kPrefixLength));
// Optional (origin.spec() + '#')
if (app_id_.size() != kPrefixLength + 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(
kPrefixLength,
app_id_.size() - kPrefixLength - suffix_length)));
DCHECK_EQ(std::string(1, kSeparator),
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 {
// Register profile-specific prefs.
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.
static PushMessagingAppIdentifier Generate(
const GURL& origin,
......
......@@ -47,11 +47,13 @@
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/gcm_driver/common/gcm_messages.h"
#include "components/gcm_driver/gcm_client.h"
#include "components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/base/window_open_disposition.h"
#if BUILDFLAG(ENABLE_BACKGROUND)
......@@ -60,6 +62,8 @@
namespace {
const char kManifestSenderId[] = "1234567890";
// NIST P-256 public key made available to tests. Must be an uncompressed
// point in accordance with SEC1 2.3.3.
const uint8_t kApplicationServerKey[65] = {
......@@ -80,33 +84,23 @@ std::string GetTestApplicationServerKey() {
kApplicationServerKey + arraysize(kApplicationServerKey));
}
// Class to instantiate on the stack that is meant to be used with
// FakeGCMProfileService. The ::Run() method follows the signature of
// FakeGCMProfileService::UnregisterCallback.
class UnregistrationCallback {
public:
UnregistrationCallback()
: message_loop_runner_(new content::MessageLoopRunner) {}
void Run(const std::string& app_id) {
app_id_ = app_id;
message_loop_runner_->Quit();
}
void WaitUntilSatisfied() { message_loop_runner_->Run(); }
const std::string& app_id() { return app_id_; }
private:
scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
std::string app_id_;
};
void LegacyRegisterCallback(const base::Closure& done_callback,
std::string* out_registration_id,
gcm::GCMClient::Result* out_result,
const std::string& registration_id,
gcm::GCMClient::Result result) {
if (out_registration_id)
*out_registration_id = registration_id;
if (out_result)
*out_result = result;
done_callback.Run();
}
} // namespace
class PushMessagingBrowserTest : public InProcessBrowserTest {
public:
PushMessagingBrowserTest() : gcm_service_(nullptr) {}
PushMessagingBrowserTest() : gcm_service_(nullptr), gcm_driver_(nullptr) {}
~PushMessagingBrowserTest() override {}
// InProcessBrowserTest:
......@@ -135,7 +129,9 @@ class PushMessagingBrowserTest : public InProcessBrowserTest {
gcm_service_ = static_cast<gcm::FakeGCMProfileService*>(
gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
GetBrowser()->profile(), &gcm::FakeGCMProfileService::Build));
gcm_service_->set_collect(true);
gcm_driver_ = static_cast<instance_id::FakeGCMDriverForInstanceID*>(
gcm_service_->driver());
push_service_ =
PushMessagingServiceFactory::GetForProfile(GetBrowser()->profile());
#if defined(ENABLE_NOTIFICATIONS)
......@@ -149,6 +145,7 @@ class PushMessagingBrowserTest : public InProcessBrowserTest {
InProcessBrowserTest::SetUpOnMainThread();
}
// Calls should be wrapped in the ASSERT_NO_FATAL_FAILURE() macro.
void RestartPushService() {
Profile* profile = GetBrowser()->profile();
PushMessagingServiceFactory::GetInstance()->SetTestingFactory(profile,
......@@ -202,8 +199,7 @@ class PushMessagingBrowserTest : public InProcessBrowserTest {
}
gcm::GCMAppHandler* GetAppHandler() {
return gcm_service()->driver()->GetAppHandler(
kPushMessagingAppIdentifierPrefix);
return gcm_driver_->GetAppHandler(kPushMessagingAppIdentifierPrefix);
}
PermissionRequestManager* GetPermissionRequestManager() {
......@@ -211,18 +207,26 @@ class PushMessagingBrowserTest : public InProcessBrowserTest {
GetBrowser()->tab_strip_model()->GetActiveWebContents());
}
// Calls should be wrapped in the ASSERT_NO_FATAL_FAILURE() macro.
void RequestAndAcceptPermission();
// Calls should be wrapped in the ASSERT_NO_FATAL_FAILURE() macro.
void RequestAndDenyPermission();
void TryToSubscribeSuccessfully(
const std::string& expected_push_subscription_info,
bool use_key = true);
// Sets out_token to the subscription token (not including server URL).
// Calls should be wrapped in the ASSERT_NO_FATAL_FAILURE() macro.
void SubscribeSuccessfully(bool use_key = true,
std::string* out_token = nullptr);
std::string GetEndpointForSubscriptionId(const std::string& subscription_id,
bool standard_protocol = true) {
return push_service()->GetEndpoint(standard_protocol).spec() +
subscription_id;
}
// Legacy subscribe path using GCMDriver rather than Instance IDs. Only
// for testing that we maintain support for existing stored registrations.
// Calls should be wrapped in the ASSERT_NO_FATAL_FAILURE() macro.
void LegacySubscribeSuccessfully(std::string* out_subscription_id = nullptr);
// Strips server URL from a registration endpoint to get subscription token.
// Calls should be wrapped in the ASSERT_NO_FATAL_FAILURE() macro.
void EndpointToToken(const std::string& endpoint,
bool standard_protocol = true,
std::string* out_token = nullptr);
PushMessagingAppIdentifier GetAppIdentifierForServiceWorkerRegistration(
int64_t service_worker_registration_id);
......@@ -233,8 +237,6 @@ class PushMessagingBrowserTest : public InProcessBrowserTest {
net::EmbeddedTestServer* https_server() const { return https_server_.get(); }
gcm::FakeGCMProfileService* gcm_service() const { return gcm_service_; }
#if defined(ENABLE_NOTIFICATIONS)
// To be called when delivery of a push message has finished. The |run_loop|
// will be told to quit after |messages_required| messages were received.
......@@ -270,9 +272,10 @@ class PushMessagingBrowserTest : public InProcessBrowserTest {
virtual Browser* GetBrowser() const { return browser(); }
private:
protected:
std::unique_ptr<net::EmbeddedTestServer> https_server_;
gcm::FakeGCMProfileService* gcm_service_;
instance_id::FakeGCMDriverForInstanceID* gcm_driver_;
PushMessagingServiceImpl* push_service_;
#if defined(ENABLE_NOTIFICATIONS)
......@@ -294,41 +297,99 @@ void PushMessagingBrowserTest::RequestAndAcceptPermission() {
std::string script_result;
GetPermissionRequestManager()->set_auto_response_for_test(
PermissionRequestManager::ACCEPT_ALL);
EXPECT_TRUE(RunScript("requestNotificationPermission();", &script_result));
EXPECT_EQ("permission status - granted", script_result);
ASSERT_TRUE(RunScript("requestNotificationPermission();", &script_result));
ASSERT_EQ("permission status - granted", script_result);
}
void PushMessagingBrowserTest::RequestAndDenyPermission() {
std::string script_result;
GetPermissionRequestManager()->set_auto_response_for_test(
PermissionRequestManager::DENY_ALL);
EXPECT_TRUE(RunScript("requestNotificationPermission();", &script_result));
EXPECT_EQ("permission status - denied", script_result);
ASSERT_TRUE(RunScript("requestNotificationPermission();", &script_result));
ASSERT_EQ("permission status - denied", script_result);
}
void PushMessagingBrowserTest::TryToSubscribeSuccessfully(
const std::string& expected_push_subscription_info,
bool use_key) {
void PushMessagingBrowserTest::SubscribeSuccessfully(bool use_key,
std::string* out_token) {
std::string script_result;
EXPECT_TRUE(RunScript("registerServiceWorker()", &script_result));
EXPECT_EQ("ok - service worker registered", script_result);
ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
ASSERT_EQ("ok - service worker registered", script_result);
RequestAndAcceptPermission();
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
if (use_key) {
ASSERT_TRUE(RunScript("removeManifest()", &script_result));
ASSERT_EQ("manifest removed", script_result);
EXPECT_TRUE(RunScript("documentSubscribePush()", &script_result));
ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
} else {
// Test backwards compatibility with old ID based subscriptions.
EXPECT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
}
EXPECT_EQ(
GetEndpointForSubscriptionId(expected_push_subscription_info, use_key),
script_result);
ASSERT_NO_FATAL_FAILURE(EndpointToToken(script_result, use_key, out_token));
}
void PushMessagingBrowserTest::LegacySubscribeSuccessfully(
std::string* out_subscription_id) {
// Create a non-InstanceID GCM registration. Have to directly access
// GCMDriver, since this codepath has been deleted from Push.
std::string script_result;
ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
ASSERT_EQ("ok - service worker registered", script_result);
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
GURL requesting_origin = https_server()->GetURL("/").GetOrigin();
int64_t service_worker_registration_id = 0LL;
PushMessagingAppIdentifier app_identifier =
PushMessagingAppIdentifier::Generate(requesting_origin,
service_worker_registration_id);
push_service_->IncreasePushSubscriptionCount(1, true /* is_pending */);
std::string subscription_id;
{
base::RunLoop run_loop;
gcm::GCMClient::Result register_result = gcm::GCMClient::UNKNOWN_ERROR;
gcm_driver_->Register(
app_identifier.app_id(), {kManifestSenderId},
base::Bind(&LegacyRegisterCallback, run_loop.QuitClosure(),
&subscription_id, &register_result));
run_loop.Run();
ASSERT_EQ(gcm::GCMClient::SUCCESS, register_result);
}
app_identifier.PersistToPrefs(GetBrowser()->profile());
push_service_->IncreasePushSubscriptionCount(1, false /* is_pending */);
push_service_->DecreasePushSubscriptionCount(1, true /* was_pending */);
{
base::RunLoop run_loop;
push_service_->StorePushSubscriptionForTesting(
GetBrowser()->profile(), requesting_origin,
service_worker_registration_id, subscription_id, kManifestSenderId,
run_loop.QuitClosure());
run_loop.Run();
}
if (out_subscription_id)
*out_subscription_id = subscription_id;
}
void PushMessagingBrowserTest::EndpointToToken(const std::string& endpoint,
bool standard_protocol,
std::string* out_token) {
size_t last_slash = endpoint.rfind('/');
ASSERT_EQ(endpoint.substr(0, last_slash + 1),
push_service()->GetEndpoint(standard_protocol).spec());
ASSERT_LT(last_slash + 1, endpoint.length()); // Token must not be empty.
if (out_token)
*out_token = endpoint.substr(last_slash + 1);
}
PushMessagingAppIdentifier
......@@ -353,23 +414,19 @@ void PushMessagingBrowserTest::SendMessageAndWaitUntilHandled(
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
SubscribeWithoutKeySuccessNotificationsGranted) {
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */, false);
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
EXPECT_EQ(app_identifier.app_id(), gcm_service()->last_registered_app_id());
EXPECT_EQ("1234567890", gcm_service()->last_registered_sender_ids()[0]);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully(false /* use_key */));
EXPECT_EQ(kManifestSenderId, gcm_driver_->last_gettoken_authorized_entity());
EXPECT_EQ(GetAppIdentifierForServiceWorkerRegistration(0LL).app_id(),
gcm_driver_->last_gettoken_app_id());
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
SubscribeSuccessNotificationsGranted) {
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
EXPECT_EQ(app_identifier.app_id(), gcm_service()->last_registered_app_id());
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
EXPECT_EQ(kEncodedApplicationServerKey,
gcm_service()->last_registered_sender_ids()[0]);
gcm_driver_->last_gettoken_authorized_entity());
EXPECT_EQ(GetAppIdentifierForServiceWorkerRegistration(0LL).app_id(),
gcm_driver_->last_gettoken_app_id());
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
......@@ -382,13 +439,9 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
GetPermissionRequestManager()->set_auto_response_for_test(
PermissionRequestManager::ACCEPT_ALL);
ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
EXPECT_EQ(GetEndpointForSubscriptionId("1-0"), script_result);
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
EXPECT_EQ(app_identifier.app_id(), gcm_service()->last_registered_app_id());
EXPECT_EQ(kEncodedApplicationServerKey,
gcm_service()->last_registered_sender_ids()[0]);
// Both of these methods EXPECT that they succeed.
ASSERT_NO_FATAL_FAILURE(EndpointToToken(script_result));
GetAppIdentifierForServiceWorkerRegistration(0LL);
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeFailureBadKey) {
......@@ -397,7 +450,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeFailureBadKey) {
ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
ASSERT_EQ("ok - service worker registered", script_result);
RequestAndAcceptPermission();
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
ASSERT_TRUE(RunScript("documentSubscribePushBadKey()", &script_result));
EXPECT_EQ(
......@@ -413,7 +466,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
ASSERT_EQ("ok - service worker registered", script_result);
RequestAndDenyPermission();
ASSERT_NO_FATAL_FAILURE(RequestAndDenyPermission());
ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
EXPECT_EQ("NotAllowedError - Registration failed - permission denied",
......@@ -426,7 +479,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeFailureNoManifest) {
ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
ASSERT_EQ("ok - service worker registered", script_result);
RequestAndAcceptPermission();
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
ASSERT_TRUE(RunScript("removeManifest()", &script_result));
ASSERT_EQ("manifest removed", script_result);
......@@ -444,7 +497,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeFailureNoSenderId) {
ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
ASSERT_EQ("ok - service worker registered", script_result);
RequestAndAcceptPermission();
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
ASSERT_TRUE(RunScript("swapManifestNoSenderId()", &script_result));
ASSERT_EQ("sender id removed from manifest", script_result);
......@@ -463,7 +516,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTestEmptySubscriptionOptions,
ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
ASSERT_EQ("ok - service worker registered", script_result);
RequestAndAcceptPermission();
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
EXPECT_EQ("NotAllowedError - Registration failed - permission denied",
......@@ -476,7 +529,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeWorker) {
ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
ASSERT_EQ("ok - service worker registered", script_result);
RequestAndAcceptPermission();
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
LoadTestPage(); // Reload to become controlled.
......@@ -492,7 +545,9 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeWorker) {
// Now run the subscribe from the service worker with a key. This
// should succeed, and write the key to the datastore.
ASSERT_TRUE(RunScript("workerSubscribePush()", &script_result));
EXPECT_EQ(GetEndpointForSubscriptionId("1-0"), script_result);
std::string token1;
ASSERT_NO_FATAL_FAILURE(
EndpointToToken(script_result, true /* standard_protocol */, &token1));
ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
EXPECT_EQ("unsubscribe result: true", script_result);
......@@ -501,7 +556,10 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeWorker) {
// Now run the subscribe from the service worker without a key.
// In this case, the key will be read from the datastore.
ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
EXPECT_EQ(GetEndpointForSubscriptionId("1-1"), script_result);
std::string token2;
ASSERT_NO_FATAL_FAILURE(
EndpointToToken(script_result, true /* standard_protocol */, &token2));
EXPECT_NE(token1, token2);
ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
EXPECT_EQ("unsubscribe result: true", script_result);
......@@ -514,7 +572,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeWorkerUsingManifest) {
ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
ASSERT_EQ("ok - service worker registered", script_result);
RequestAndAcceptPermission();
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
LoadTestPage(); // Reload to become controlled.
......@@ -533,7 +591,9 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeWorkerUsingManifest) {
// the code to read sender id from the manifest and will write it to the
// datastore.
ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
EXPECT_EQ(GetEndpointForSubscriptionId("1-0", false), script_result);
std::string token1;
ASSERT_NO_FATAL_FAILURE(
EndpointToToken(script_result, false /* standard_protocol */, &token1));
ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
EXPECT_EQ("unsubscribe result: true", script_result);
......@@ -542,7 +602,10 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeWorkerUsingManifest) {
// Now run the subscribe from the service worker without a key.
// In this case, the sender id will be read from the datastore.
ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
EXPECT_EQ(GetEndpointForSubscriptionId("1-1", false), script_result);
std::string token2;
ASSERT_NO_FATAL_FAILURE(
EndpointToToken(script_result, false /* standard_protocol */, &token2));
EXPECT_NE(token1, token2);
ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
EXPECT_EQ("unsubscribe result: true", script_result);
......@@ -563,10 +626,11 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, MAYBE_SubscribePersisted) {
// assigned in order of push subscription (even when these orders are
// different).
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
std::string token1;
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully(true /* use_key */, &token1));
PushMessagingAppIdentifier sw0_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
EXPECT_EQ(sw0_identifier.app_id(), gcm_service()->last_registered_app_id());
EXPECT_EQ(sw0_identifier.app_id(), gcm_driver_->last_gettoken_app_id());
LoadTestPage("/push_messaging/subscope1/test.html");
ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
......@@ -580,16 +644,21 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, MAYBE_SubscribePersisted) {
// navigator.serviceWorker.ready is going to be resolved with the parent
// Service Worker which still controls the page.
LoadTestPage("/push_messaging/subscope2/test.html");
TryToSubscribeSuccessfully("1-1" /* expected_push_subscription_id */);
std::string token2;
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully(true /* use_key */, &token2));
EXPECT_NE(token1, token2);
PushMessagingAppIdentifier sw2_identifier =
GetAppIdentifierForServiceWorkerRegistration(2LL);
EXPECT_EQ(sw2_identifier.app_id(), gcm_service()->last_registered_app_id());
EXPECT_EQ(sw2_identifier.app_id(), gcm_driver_->last_gettoken_app_id());
LoadTestPage("/push_messaging/subscope1/test.html");
TryToSubscribeSuccessfully("1-2" /* expected_push_subscription_id */);
std::string token3;
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully(true /* use_key */, &token3));
EXPECT_NE(token1, token3);
EXPECT_NE(token2, token3);
PushMessagingAppIdentifier sw1_identifier =
GetAppIdentifierForServiceWorkerRegistration(1LL);
EXPECT_EQ(sw1_identifier.app_id(), gcm_service()->last_registered_app_id());
EXPECT_EQ(sw1_identifier.app_id(), gcm_driver_->last_gettoken_app_id());
// Now test that the Service Worker registration IDs and push subscription IDs
// generated above were persisted to SW storage, by checking that they are
......@@ -600,16 +669,22 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, MAYBE_SubscribePersisted) {
// so we wouldn't be able to load the test pages with the same origin.
LoadTestPage("/push_messaging/subscope1/test.html");
TryToSubscribeSuccessfully("1-2" /* expected_push_subscription_id */);
EXPECT_EQ(sw1_identifier.app_id(), gcm_service()->last_registered_app_id());
std::string token4;
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully(true /* use_key */, &token4));
EXPECT_EQ(token3, token4);
EXPECT_EQ(sw1_identifier.app_id(), gcm_driver_->last_gettoken_app_id());
LoadTestPage("/push_messaging/subscope2/test.html");
TryToSubscribeSuccessfully("1-1" /* expected_push_subscription_id */);
EXPECT_EQ(sw1_identifier.app_id(), gcm_service()->last_registered_app_id());
std::string token5;
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully(true /* use_key */, &token5));
EXPECT_EQ(token2, token5);
EXPECT_EQ(sw1_identifier.app_id(), gcm_driver_->last_gettoken_app_id());
LoadTestPage();
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
EXPECT_EQ(sw1_identifier.app_id(), gcm_service()->last_registered_app_id());
std::string token6;
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully(true /* use_key */, &token6));
EXPECT_EQ(token1, token6);
EXPECT_EQ(sw1_identifier.app_id(), gcm_driver_->last_gettoken_app_id());
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, AppHandlerOnlyIfSubscribed) {
......@@ -619,7 +694,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, AppHandlerOnlyIfSubscribed) {
ASSERT_NO_FATAL_FAILURE(RestartPushService());
EXPECT_NE(push_service(), GetAppHandler());
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
EXPECT_EQ(push_service(), GetAppHandler());
ASSERT_NO_FATAL_FAILURE(RestartPushService());
......@@ -627,7 +702,6 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, AppHandlerOnlyIfSubscribed) {
// Unsubscribe.
std::string script_result;
gcm_service()->AddExpectedUnregisterResponse(gcm::GCMClient::SUCCESS);
ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
EXPECT_EQ("unsubscribe result: true", script_result);
......@@ -639,13 +713,9 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, AppHandlerOnlyIfSubscribed) {
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventSuccess) {
std::string script_result;
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
EXPECT_EQ(app_identifier.app_id(), gcm_service()->last_registered_app_id());
EXPECT_EQ(kEncodedApplicationServerKey,
gcm_service()->last_registered_sender_ids()[0]);
ASSERT_TRUE(RunScript("isControlled()", &script_result));
ASSERT_EQ("false - is not controlled", script_result);
......@@ -669,13 +739,9 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventSuccess) {
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventWithoutPayload) {
std::string script_result;
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
EXPECT_EQ(app_identifier.app_id(), gcm_service()->last_registered_app_id());
EXPECT_EQ(kEncodedApplicationServerKey,
gcm_service()->last_registered_sender_ids()[0]);
ASSERT_TRUE(RunScript("isControlled()", &script_result));
ASSERT_EQ("false - is not controlled", script_result);
......@@ -694,16 +760,36 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventWithoutPayload) {
EXPECT_EQ("[NULL]", script_result);
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventNoServiceWorker) {
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, LegacyPushEvent) {
std::string script_result;
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(LegacySubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
ASSERT_TRUE(RunScript("isControlled()", &script_result));
ASSERT_EQ("false - is not controlled", script_result);
LoadTestPage(); // Reload to become controlled.
ASSERT_TRUE(RunScript("isControlled()", &script_result));
ASSERT_EQ("true - is controlled", script_result);
gcm::IncomingMessage message;
message.sender_id = kManifestSenderId;
message.decrypted = false;
push_service()->OnMessage(app_identifier.app_id(), message);
ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
EXPECT_EQ("[NULL]", script_result);
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventNoServiceWorker) {
std::string script_result;
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
EXPECT_EQ(app_identifier.app_id(), gcm_service()->last_registered_app_id());
EXPECT_EQ(kEncodedApplicationServerKey,
gcm_service()->last_registered_sender_ids()[0]);
ASSERT_TRUE(RunScript("isControlled()", &script_result));
ASSERT_EQ("false - is not controlled", script_result);
......@@ -717,22 +803,36 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventNoServiceWorker) {
ASSERT_TRUE(RunScript("unregisterServiceWorker()", &script_result));
ASSERT_EQ("service worker unregistration status: true", script_result);
// When the push service will receive it next message, given that there is no
// SW available, it should unregister |app_identifier.app_id()|.
UnregistrationCallback callback;
gcm_service()->SetUnregisterCallback(
base::Bind(&UnregistrationCallback::Run, base::Unretained(&callback)));
// Unregistering the service worker doesn't yet unsubscribe from push (though
// it should), and FindByServiceWorker doesn't require a live SW.
GURL origin = https_server()->GetURL("/").GetOrigin();
PushMessagingAppIdentifier app_identifier2 =
PushMessagingAppIdentifier::FindByServiceWorker(
GetBrowser()->profile(), origin,
0LL /* service_worker_registration_id */);
EXPECT_FALSE(app_identifier2.is_null());
EXPECT_EQ(app_identifier.app_id(), app_identifier2.app_id());
gcm::IncomingMessage message;
message.sender_id = GetTestApplicationServerKey();
message.raw_data = "testdata";
message.decrypted = true;
base::RunLoop run_loop;
push_service()->SetMessageCallbackForTesting(run_loop.QuitClosure());
EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));
push_service()->OnMessage(app_identifier.app_id(), message);
EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(true));
callback.WaitUntilSatisfied();
run_loop.Run();
EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));
EXPECT_EQ(app_identifier.app_id(), callback.app_id());
// Now the push service has received a message and failed to find its service
// worker, it should have automatically unsubscribed app_identifier.app_id().
PushMessagingAppIdentifier app_identifier3 =
PushMessagingAppIdentifier::FindByServiceWorker(
GetBrowser()->profile(), origin,
0LL /* service_worker_registration_id */);
EXPECT_TRUE(app_identifier3.is_null());
// No push data should have been received.
ASSERT_TRUE(RunScript("resultQueue.popImmediately()", &script_result));
......@@ -744,13 +844,9 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
PushEventEnforcesUserVisibleNotification) {
std::string script_result;
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
EXPECT_EQ(app_identifier.app_id(), gcm_service()->last_registered_app_id());
EXPECT_EQ(kEncodedApplicationServerKey,
gcm_service()->last_registered_sender_ids()[0]);
ASSERT_TRUE(RunScript("isControlled()", &script_result));
ASSERT_EQ("false - is not controlled", script_result);
......@@ -849,13 +945,12 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
PushEventAllowSilentPushCommandLineFlag) {
std::string script_result;
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
EXPECT_EQ(app_identifier.app_id(), gcm_service()->last_registered_app_id());
EXPECT_EQ(app_identifier.app_id(), gcm_driver_->last_gettoken_app_id());
EXPECT_EQ(kEncodedApplicationServerKey,
gcm_service()->last_registered_sender_ids()[0]);
gcm_driver_->last_gettoken_authorized_entity());
ASSERT_TRUE(RunScript("isControlled()", &script_result));
ASSERT_EQ("false - is not controlled", script_result);
......@@ -921,13 +1016,9 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
PushEventEnforcesUserVisibleNotificationAfterQueue) {
std::string script_result;
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
EXPECT_EQ(app_identifier.app_id(), gcm_service()->last_registered_app_id());
EXPECT_EQ(kEncodedApplicationServerKey,
gcm_service()->last_registered_sender_ids()[0]);
ASSERT_TRUE(RunScript("isControlled()", &script_result));
ASSERT_EQ("false - is not controlled", script_result);
......@@ -975,13 +1066,9 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
content::WebContents* web_contents =
GetBrowser()->tab_strip_model()->GetActiveWebContents();
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
EXPECT_EQ(app_identifier.app_id(), gcm_service()->last_registered_app_id());
EXPECT_EQ(kEncodedApplicationServerKey,
gcm_service()->last_registered_sender_ids()[0]);
ASSERT_TRUE(RunScript("isControlled()", &script_result));
ASSERT_EQ("false - is not controlled", script_result);
......@@ -1035,10 +1122,10 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PermissionStateSaysGranted) {
ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
ASSERT_EQ("ok - service worker registered", script_result);
RequestAndAcceptPermission();
ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
EXPECT_EQ(GetEndpointForSubscriptionId("1-0"), script_result);
ASSERT_NO_FATAL_FAILURE(EndpointToToken(script_result));
ASSERT_TRUE(RunScript("permissionState()", &script_result));
EXPECT_EQ("permission status - granted", script_result);
......@@ -1050,7 +1137,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PermissionStateSaysDenied) {
ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
ASSERT_EQ("ok - service worker registered", script_result);
RequestAndDenyPermission();
ASSERT_NO_FATAL_FAILURE(RequestAndDenyPermission());
ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
EXPECT_EQ("NotAllowedError - Registration failed - permission denied",
......@@ -1067,8 +1154,40 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeSuccess) {
EXPECT_EQ("ok - service worker registered", script_result);
// Resolves true if there was a subscription.
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */, false);
gcm_service()->AddExpectedUnregisterResponse(gcm::GCMClient::SUCCESS);
std::string token1;
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully(false /* use_key */, &token1));
ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
EXPECT_EQ("unsubscribe result: true", script_result);
// Resolves false if there was no longer a subscription.
ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
EXPECT_EQ("unsubscribe result: false", script_result);
// TODO(johnme): Test that doesn't reject if there was a network error (should
// deactivate subscription locally anyway).
// TODO(johnme): Test that doesn't reject if there were other push service
// errors (should deactivate subscription locally anyway).
// Unsubscribing (with an existing reference to a PushSubscription), after
// unregistering the Service Worker, just means push subscription isn't found.
std::string token2;
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully(false /* use_key */, &token2));
EXPECT_NE(token1, token2);
ASSERT_TRUE(RunScript("unregisterServiceWorker()", &script_result));
ASSERT_EQ("service worker unregistration status: true", script_result);
ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
EXPECT_EQ("unsubscribe result: false", script_result);
}
// Push subscriptions used to be non-InstanceID GCM registrations. Still need
// to be able to unsubscribe these, even though new ones are no longer created.
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, LegacyUnsubscribeSuccess) {
std::string script_result;
// Resolves true if there was a subscription.
std::string subscription_id1;
ASSERT_NO_FATAL_FAILURE(LegacySubscribeSuccessfully(&subscription_id1));
gcm_service_->AddExpectedUnregisterResponse(gcm::GCMClient::SUCCESS);
ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
EXPECT_EQ("unsubscribe result: true", script_result);
......@@ -1078,8 +1197,10 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeSuccess) {
// Doesn't reject if there was a network error (deactivates subscription
// locally anyway).
TryToSubscribeSuccessfully("1-1" /* expected_push_subscription_id */, false);
gcm_service()->AddExpectedUnregisterResponse(gcm::GCMClient::NETWORK_ERROR);
std::string subscription_id2;
ASSERT_NO_FATAL_FAILURE(LegacySubscribeSuccessfully(&subscription_id2));
EXPECT_NE(subscription_id1, subscription_id2);
gcm_service_->AddExpectedUnregisterResponse(gcm::GCMClient::NETWORK_ERROR);
ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
EXPECT_EQ("unsubscribe result: true", script_result);
ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
......@@ -1087,15 +1208,22 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeSuccess) {
// Doesn't reject if there were other push service errors (deactivates
// subscription locally anyway).
TryToSubscribeSuccessfully("1-2" /* expected_push_subscription_id */, false);
gcm_service()->AddExpectedUnregisterResponse(
std::string subscription_id3;
ASSERT_NO_FATAL_FAILURE(LegacySubscribeSuccessfully(&subscription_id3));
EXPECT_NE(subscription_id1, subscription_id3);
EXPECT_NE(subscription_id2, subscription_id3);
gcm_service_->AddExpectedUnregisterResponse(
gcm::GCMClient::INVALID_PARAMETER);
ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
EXPECT_EQ("unsubscribe result: true", script_result);
// Unsubscribing (with an existing reference to a PushSubscription), after
// unregistering the Service Worker, just means push subscription isn't found.
TryToSubscribeSuccessfully("1-3" /* expected_push_subscription_id */, false);
std::string subscription_id4;
ASSERT_NO_FATAL_FAILURE(LegacySubscribeSuccessfully(&subscription_id4));
EXPECT_NE(subscription_id1, subscription_id4);
EXPECT_NE(subscription_id2, subscription_id4);
EXPECT_NE(subscription_id3, subscription_id4);
ASSERT_TRUE(RunScript("unregisterServiceWorker()", &script_result));
ASSERT_EQ("service worker unregistration status: true", script_result);
ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
......@@ -1106,7 +1234,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
GlobalResetPushPermissionUnsubscribes) {
std::string script_result;
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
EXPECT_EQ("true - subscribed", script_result);
......@@ -1135,7 +1263,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
LocalResetPushPermissionUnsubscribes) {
std::string script_result;
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
EXPECT_EQ("true - subscribed", script_result);
......@@ -1167,7 +1295,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
DenyPushPermissionUnsubscribes) {
std::string script_result;
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
EXPECT_EQ("true - subscribed", script_result);
......@@ -1199,7 +1327,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
GlobalResetNotificationsPermissionUnsubscribes) {
std::string script_result;
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
EXPECT_EQ("true - subscribed", script_result);
......@@ -1228,7 +1356,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
LocalResetNotificationsPermissionUnsubscribes) {
std::string script_result;
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
EXPECT_EQ("true - subscribed", script_result);
......@@ -1260,7 +1388,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
DenyNotificationsPermissionUnsubscribes) {
std::string script_result;
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
EXPECT_EQ("true - subscribed", script_result);
......@@ -1292,7 +1420,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
GrantAlreadyGrantedPermissionDoesNotUnsubscribe) {
std::string script_result;
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
EXPECT_EQ("true - subscribed", script_result);
......@@ -1328,7 +1456,7 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
AutomaticUnsubscriptionFollowsContentSettingRules) {
std::string script_result;
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
EXPECT_EQ("true - subscribed", script_result);
......@@ -1364,17 +1492,16 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
EXPECT_EQ("true - subscribed", script_result);
}
// Checks that automatically unsubscribing due to a revoked permission is
// handled well if the sender ID needed to unsubscribe was already deleted.
// Checks automatically unsubscribing due to a revoked permission after
// previously clearing site data.
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
ResetPushPermissionAfterClearingSiteData) {
std::string script_result;
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
PushMessagingAppIdentifier app_identifier =
GetAppIdentifierForServiceWorkerRegistration(0LL);
EXPECT_EQ(app_identifier.app_id(), gcm_service()->last_registered_app_id());
PushMessagingAppIdentifier stored_app_identifier =
PushMessagingAppIdentifier::FindByAppId(GetBrowser()->profile(),
app_identifier.app_id());
......@@ -1394,8 +1521,9 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
run_loop.QuitClosure());
// This shouldn't (asynchronously) cause a DCHECK.
// TODO(johnme): Get this test running on Android, which has a different
// codepath due to sender_id being required for unsubscribing there.
// TODO(johnme): Get this test running on Android with legacy GCM
// registrations, which have a different codepath due to sender_id being
// required for unsubscribing there.
HostContentSettingsMapFactory::GetForProfile(GetBrowser()->profile())
->ClearSettingsForOneType(CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
......@@ -1409,18 +1537,20 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
}
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, EncryptionKeyUniqueness) {
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */, false);
std::string token1;
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully(false /* use_key */, &token1));
std::string first_public_key;
ASSERT_TRUE(RunScript("GetP256dh()", &first_public_key));
EXPECT_GE(first_public_key.size(), 32u);
std::string script_result;
gcm_service()->AddExpectedUnregisterResponse(gcm::GCMClient::SUCCESS);
ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
EXPECT_EQ("unsubscribe result: true", script_result);
TryToSubscribeSuccessfully("1-1" /* expected_push_subscription_id */);
std::string token2;
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully(true /* use_key */, &token2));
EXPECT_NE(token1, token2);
std::string second_public_key;
ASSERT_TRUE(RunScript("GetP256dh()", &second_public_key));
......@@ -1472,12 +1602,11 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
ASSERT_FALSE(background_mode_manager->IsBackgroundModeActive());
// Once there is a push subscription background mode is still inactive.
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
ASSERT_FALSE(background_mode_manager->IsBackgroundModeActive());
// After dropping the last subscription it is still inactive.
std::string script_result;
gcm_service()->AddExpectedUnregisterResponse(gcm::GCMClient::SUCCESS);
ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
EXPECT_EQ("unsubscribe result: true", script_result);
ASSERT_FALSE(background_mode_manager->IsBackgroundModeActive());
......@@ -1504,12 +1633,11 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBackgroundModeEnabledBrowserTest,
ASSERT_FALSE(background_mode_manager->IsBackgroundModeActive());
// Once there is a push subscription background mode is active.
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
ASSERT_TRUE(background_mode_manager->IsBackgroundModeActive());
// Dropping the last subscription deactivates background mode.
std::string script_result;
gcm_service()->AddExpectedUnregisterResponse(gcm::GCMClient::SUCCESS);
ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
EXPECT_EQ("unsubscribe result: true", script_result);
ASSERT_FALSE(background_mode_manager->IsBackgroundModeActive());
......@@ -1536,12 +1664,11 @@ IN_PROC_BROWSER_TEST_F(PushMessagingBackgroundModeDisabledBrowserTest,
ASSERT_FALSE(background_mode_manager->IsBackgroundModeActive());
// Once there is a push subscription background mode is still inactive.
TryToSubscribeSuccessfully("1-0" /* expected_push_subscription_id */);
ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
ASSERT_FALSE(background_mode_manager->IsBackgroundModeActive());
// After dropping the last subscription background mode is still inactive.
std::string script_result;
gcm_service()->AddExpectedUnregisterResponse(gcm::GCMClient::SUCCESS);
ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
EXPECT_EQ("unsubscribe result: true", script_result);
ASSERT_FALSE(background_mode_manager->IsBackgroundModeActive());
......
......@@ -12,20 +12,28 @@
#include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.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/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"
// static
PushMessagingServiceImpl* PushMessagingServiceFactory::GetForProfile(
content::BrowserContext* profile) {
content::BrowserContext* context) {
// The Push API is not currently supported in incognito mode.
// See https://crbug.com/401439.
if (profile->IsOffTheRecord())
return NULL;
if (context->IsOffTheRecord())
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*>(
GetInstance()->GetServiceForBrowserContext(profile, true));
GetInstance()->GetServiceForBrowserContext(context, true));
}
// static
......@@ -39,6 +47,7 @@ PushMessagingServiceFactory::PushMessagingServiceFactory()
BrowserContextDependencyManager::GetInstance()) {
DependsOn(BudgetManagerFactory::GetInstance());
DependsOn(gcm::GCMProfileServiceFactory::GetInstance());
DependsOn(instance_id::InstanceIDProfileServiceFactory::GetInstance());
DependsOn(HostContentSettingsMapFactory::GetInstance());
DependsOn(PermissionManagerFactory::GetInstance());
}
......
......@@ -15,6 +15,7 @@
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
......@@ -25,6 +26,8 @@
#include "chrome/browser/push_messaging/push_messaging_service_factory.h"
#include "chrome/browser/push_messaging/push_messaging_service_observer.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 "chrome/browser/ui/chrome_pages.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_switches.h"
......@@ -34,6 +37,8 @@
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/gcm_driver/gcm_driver.h"
#include "components/gcm_driver/gcm_profile_service.h"
#include "components/gcm_driver/instance_id/instance_id.h"
#include "components/gcm_driver/instance_id/instance_id_driver.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/rappor/rappor_utils.h"
......@@ -55,7 +60,14 @@
#include "chrome/browser/lifetime/scoped_keep_alive.h"
#endif
using instance_id::InstanceID;
namespace {
// Scope passed to getToken to obtain GCM registration tokens.
// Must match Java GoogleCloudMessaging.INSTANCE_ID_SCOPE.
const char kGCMScope[] = "GCM";
const int kMaxRegistrations = 1000000;
// Chrome does not yet support silent push messages, and requires websites to
......@@ -181,7 +193,8 @@ void PushMessagingServiceImpl::DecreasePushSubscriptionCount(int subtract,
}
bool PushMessagingServiceImpl::CanHandle(const std::string& app_id) const {
return !PushMessagingAppIdentifier::FindByAppId(profile_, app_id).is_null();
return base::StartsWith(app_id, kPushMessagingAppIdentifierPrefix,
base::CompareCase::INSENSITIVE_ASCII);
}
void PushMessagingServiceImpl::ShutdownHandler() {
......@@ -408,7 +421,7 @@ void PushMessagingServiceImpl::SubscribeFromDocument(
profile_->GetPermissionManager()->RequestPermission(
content::PermissionType::PUSH_MESSAGING, web_contents->GetMainFrame(),
requesting_origin, true /* user_gesture */,
base::Bind(&PushMessagingServiceImpl::DidRequestPermission,
base::Bind(&PushMessagingServiceImpl::DoSubscribe,
weak_factory_.GetWeakPtr(), app_identifier, options,
callback));
}
......@@ -430,8 +443,7 @@ void PushMessagingServiceImpl::SubscribeFromWorker(
}
blink::WebPushPermissionStatus permission_status =
PushMessagingServiceImpl::GetPermissionStatus(requesting_origin,
options.user_visible_only);
GetPermissionStatus(requesting_origin, options.user_visible_only);
if (permission_status != blink::WebPushPermissionStatusGranted) {
SubscribeEndWithError(register_callback,
......@@ -439,14 +451,8 @@ void PushMessagingServiceImpl::SubscribeFromWorker(
return;
}
IncreasePushSubscriptionCount(1, true /* is_pending */);
std::vector<std::string> sender_ids(1,
NormalizeSenderInfo(options.sender_info));
GetGCMDriver()->Register(app_identifier.app_id(), sender_ids,
base::Bind(&PushMessagingServiceImpl::DidSubscribe,
weak_factory_.GetWeakPtr(),
app_identifier, register_callback));
DoSubscribe(app_identifier, options, register_callback,
blink::mojom::PermissionStatus::GRANTED);
}
blink::WebPushPermissionStatus PushMessagingServiceImpl::GetPermissionStatus(
......@@ -466,6 +472,28 @@ bool PushMessagingServiceImpl::SupportNonVisibleMessages() {
return false;
}
void PushMessagingServiceImpl::DoSubscribe(
const PushMessagingAppIdentifier& app_identifier,
const content::PushSubscriptionOptions& options,
const content::PushMessagingService::RegisterCallback& register_callback,
blink::mojom::PermissionStatus permission_status) {
if (permission_status != blink::mojom::PermissionStatus::GRANTED) {
SubscribeEndWithError(register_callback,
content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
return;
}
IncreasePushSubscriptionCount(1, true /* is_pending */);
GetInstanceIDDriver()
->GetInstanceID(app_identifier.app_id())
->GetToken(NormalizeSenderInfo(options.sender_info), kGCMScope,
std::map<std::string, std::string>() /* options */,
base::Bind(&PushMessagingServiceImpl::DidSubscribe,
weak_factory_.GetWeakPtr(), app_identifier,
options.sender_info, register_callback));
}
void PushMessagingServiceImpl::SubscribeEnd(
const content::PushMessagingService::RegisterCallback& callback,
const std::string& subscription_id,
......@@ -485,35 +513,36 @@ void PushMessagingServiceImpl::SubscribeEndWithError(
void PushMessagingServiceImpl::DidSubscribe(
const PushMessagingAppIdentifier& app_identifier,
const std::string& sender_id,
const content::PushMessagingService::RegisterCallback& callback,
const std::string& subscription_id,
gcm::GCMClient::Result result) {
InstanceID::Result result) {
DecreasePushSubscriptionCount(1, true /* was_pending */);
content::PushRegistrationStatus status =
content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR;
switch (result) {
case gcm::GCMClient::SUCCESS:
case InstanceID::SUCCESS:
// Make sure that this subscription has associated encryption keys prior
// to returning it to the developer - they'll need this information in
// order to send payloads to the user.
GetGCMDriver()->GetEncryptionInfo(
app_identifier.app_id(),
GetEncryptionInfoForAppId(
app_identifier.app_id(), sender_id,
base::Bind(&PushMessagingServiceImpl::DidSubscribeWithEncryptionInfo,
weak_factory_.GetWeakPtr(), app_identifier, callback,
subscription_id));
return;
case gcm::GCMClient::INVALID_PARAMETER:
case gcm::GCMClient::GCM_DISABLED:
case gcm::GCMClient::ASYNC_OPERATION_PENDING:
case gcm::GCMClient::SERVER_ERROR:
case gcm::GCMClient::UNKNOWN_ERROR:
case InstanceID::INVALID_PARAMETER:
case InstanceID::DISABLED:
case InstanceID::ASYNC_OPERATION_PENDING:
case InstanceID::SERVER_ERROR:
case InstanceID::UNKNOWN_ERROR:
DLOG(ERROR) << "Push messaging subscription failed; InstanceID::Result = "
<< result;
status = content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR;
break;
case gcm::GCMClient::NETWORK_ERROR:
case gcm::GCMClient::TTL_EXCEEDED:
case InstanceID::NETWORK_ERROR:
status = content::PUSH_REGISTRATION_STATUS_NETWORK_ERROR;
break;
}
......@@ -543,32 +572,12 @@ void PushMessagingServiceImpl::DidSubscribeWithEncryptionInfo(
content::PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE);
}
void PushMessagingServiceImpl::DidRequestPermission(
const PushMessagingAppIdentifier& app_identifier,
const content::PushSubscriptionOptions& options,
const content::PushMessagingService::RegisterCallback& register_callback,
blink::mojom::PermissionStatus permission_status) {
if (permission_status != blink::mojom::PermissionStatus::GRANTED) {
SubscribeEndWithError(register_callback,
content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
return;
}
IncreasePushSubscriptionCount(1, true /* is_pending */);
std::vector<std::string> sender_ids(1,
NormalizeSenderInfo(options.sender_info));
GetGCMDriver()->Register(app_identifier.app_id(), sender_ids,
base::Bind(&PushMessagingServiceImpl::DidSubscribe,
weak_factory_.GetWeakPtr(),
app_identifier, register_callback));
}
// GetEncryptionInfo methods ---------------------------------------------------
void PushMessagingServiceImpl::GetEncryptionInfo(
const GURL& origin,
int64_t service_worker_registration_id,
const std::string& sender_id,
const PushMessagingService::EncryptionInfoCallback& callback) {
PushMessagingAppIdentifier app_identifier =
PushMessagingAppIdentifier::FindByServiceWorker(
......@@ -576,8 +585,8 @@ void PushMessagingServiceImpl::GetEncryptionInfo(
DCHECK(!app_identifier.is_null());
GetGCMDriver()->GetEncryptionInfo(
app_identifier.app_id(),
GetEncryptionInfoForAppId(
app_identifier.app_id(), sender_id,
base::Bind(&PushMessagingServiceImpl::DidGetEncryptionInfo,
weak_factory_.GetWeakPtr(), callback));
}
......@@ -604,10 +613,8 @@ void PushMessagingServiceImpl::Unsubscribe(
PushMessagingAppIdentifier::FindByServiceWorker(
profile_, requesting_origin, service_worker_registration_id);
if (app_identifier.is_null()) {
if (!callback.is_null()) {
callback.Run(
content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
}
callback.Run(
content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
return;
}
......@@ -624,37 +631,58 @@ void PushMessagingServiceImpl::Unsubscribe(
// retry unregistration if it fails due to network errors (crbug.com/465399).
PushMessagingAppIdentifier app_identifier =
PushMessagingAppIdentifier::FindByAppId(profile_, app_id);
bool was_registered = !app_identifier.is_null();
if (was_registered)
bool was_subscribed = !app_identifier.is_null();
if (was_subscribed)
app_identifier.DeleteFromPrefs(profile_);
const auto& unregister_callback =
base::Bind(&PushMessagingServiceImpl::DidUnsubscribe,
weak_factory_.GetWeakPtr(), was_registered, callback);
#if defined(OS_ANDROID)
// On Android the backend is different, and requires the original sender_id.
// UnsubscribeBecausePermissionRevoked sometimes calls us with an empty one.
if (sender_id.empty()) {
unregister_callback.Run(gcm::GCMClient::INVALID_PARAMETER);
if (PushMessagingAppIdentifier::UseInstanceID(app_id)) {
GetInstanceIDDriver()->GetInstanceID(app_id)->DeleteID(base::Bind(
&PushMessagingServiceImpl::DidDeleteID, weak_factory_.GetWeakPtr(),
app_id, was_subscribed, callback));
} else {
GetGCMDriver()->UnregisterWithSenderId(
app_id, NormalizeSenderInfo(sender_id), unregister_callback);
}
auto unregister_callback =
base::Bind(&PushMessagingServiceImpl::DidUnsubscribe,
weak_factory_.GetWeakPtr(), was_subscribed, callback);
#if defined(OS_ANDROID)
// On Android the backend is different, and requires the original sender_id.
// UnsubscribeBecausePermissionRevoked sometimes calls us with an empty one.
if (sender_id.empty()) {
unregister_callback.Run(gcm::GCMClient::INVALID_PARAMETER);
} else {
GetGCMDriver()->UnregisterWithSenderId(
app_id, NormalizeSenderInfo(sender_id), unregister_callback);
}
#else
GetGCMDriver()->Unregister(app_id, unregister_callback);
GetGCMDriver()->Unregister(app_id, unregister_callback);
#endif
}
}
void PushMessagingServiceImpl::DidUnsubscribe(
void PushMessagingServiceImpl::DidDeleteID(
const std::string& app_id,
bool was_subscribed,
const content::PushMessagingService::UnregisterCallback& callback,
InstanceID::Result result) {
// DidUnsubscribeInstanceID must be run asynchronously, since it calls
// InstanceIDDriver::RemoveInstanceID which deletes the InstanceID itself.
// Calling that immediately would cause a use-after-free in our caller.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&PushMessagingServiceImpl::DidUnsubscribeInstanceID,
weak_factory_.GetWeakPtr(), app_id, was_subscribed,
callback, result));
}
void PushMessagingServiceImpl::DidUnsubscribeInstanceID(
const std::string& app_id,
bool was_subscribed,
const content::PushMessagingService::UnregisterCallback& callback,
gcm::GCMClient::Result result) {
InstanceID::Result result) {
GetInstanceIDDriver()->RemoveInstanceID(app_id);
if (was_subscribed)
DecreasePushSubscriptionCount(1, false /* was_pending */);
// Internal calls pass a null callback.
if (callback.is_null())
return;
DCHECK(!callback.is_null());
if (!was_subscribed) {
callback.Run(
......@@ -662,6 +690,37 @@ void PushMessagingServiceImpl::DidUnsubscribe(
return;
}
switch (result) {
case InstanceID::SUCCESS:
callback.Run(content::PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED);
break;
case InstanceID::INVALID_PARAMETER:
case InstanceID::DISABLED:
case InstanceID::SERVER_ERROR:
case InstanceID::UNKNOWN_ERROR:
callback.Run(content::PUSH_UNREGISTRATION_STATUS_PENDING_SERVICE_ERROR);
break;
case InstanceID::ASYNC_OPERATION_PENDING:
case InstanceID::NETWORK_ERROR:
callback.Run(content::PUSH_UNREGISTRATION_STATUS_PENDING_NETWORK_ERROR);
break;
}
}
void PushMessagingServiceImpl::DidUnsubscribe(
bool was_subscribed,
const content::PushMessagingService::UnregisterCallback& callback,
gcm::GCMClient::Result gcm_result) {
if (was_subscribed)
DecreasePushSubscriptionCount(1, false /* was_pending */);
DCHECK(!callback.is_null());
if (!was_subscribed) {
callback.Run(
content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
return;
}
switch (gcm_result) {
case gcm::GCMClient::SUCCESS:
callback.Run(content::PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED);
break;
......@@ -715,12 +774,23 @@ void PushMessagingServiceImpl::OnContentSettingChanged(
continue;
}
GetSenderId(
profile_, app_identifier.origin(),
app_identifier.service_worker_registration_id(),
base::Bind(
&PushMessagingServiceImpl::UnsubscribeBecausePermissionRevoked,
weak_factory_.GetWeakPtr(), app_identifier, barrier_closure));
bool need_sender_id = false;
#if defined(OS_ANDROID)
need_sender_id =
!PushMessagingAppIdentifier::UseInstanceID(app_identifier.app_id());
#endif
if (need_sender_id) {
GetSenderId(
profile_, app_identifier.origin(),
app_identifier.service_worker_registration_id(),
base::Bind(
&PushMessagingServiceImpl::UnsubscribeBecausePermissionRevoked,
weak_factory_.GetWeakPtr(), app_identifier, barrier_closure));
} else {
UnsubscribeBecausePermissionRevoked(
app_identifier, barrier_closure, "" /* sender_id */,
true /* success */, true /* not_found */);
}
}
}
......@@ -735,8 +805,9 @@ void PushMessagingServiceImpl::UnsubscribeBecausePermissionRevoked(
// Unsubscribe the PushMessagingAppIdentifier with the push service.
// It's possible for GetSenderId to have failed and sender_id to be empty, if
// cookies (and the SW database) for an origin got cleared before permissions
// are cleared for the origin. In that case Unsubscribe will just delete the
// app identifier to block future messages.
// are cleared for the origin. In that case for legacy GCM registrations on
// Android, Unsubscribe will just delete the app identifier to block future
// messages.
// TODO(johnme): Auto-unregister before SW DB is cleared
// (https://crbug.com/402458).
Unsubscribe(app_identifier.app_id(), sender_id,
......@@ -799,6 +870,18 @@ bool PushMessagingServiceImpl::IsPermissionSet(const GURL& origin) {
blink::WebPushPermissionStatusGranted;
}
void PushMessagingServiceImpl::GetEncryptionInfoForAppId(
const std::string& app_id,
const std::string& sender_id,
gcm::GCMEncryptionProvider::EncryptionInfoCallback callback) {
if (PushMessagingAppIdentifier::UseInstanceID(app_id)) {
GetInstanceIDDriver()->GetInstanceID(app_id)->GetEncryptionInfo(
NormalizeSenderInfo(sender_id), callback);
} else {
GetGCMDriver()->GetEncryptionInfo(app_id, callback);
}
}
gcm::GCMDriver* PushMessagingServiceImpl::GetGCMDriver() const {
gcm::GCMProfileService* gcm_profile_service =
gcm::GCMProfileServiceFactory::GetForProfile(profile_);
......@@ -806,3 +889,12 @@ gcm::GCMDriver* PushMessagingServiceImpl::GetGCMDriver() const {
CHECK(gcm_profile_service->driver());
return gcm_profile_service->driver();
}
instance_id::InstanceIDDriver* PushMessagingServiceImpl::GetInstanceIDDriver()
const {
instance_id::InstanceIDProfileService* instance_id_profile_service =
instance_id::InstanceIDProfileServiceFactory::GetForProfile(profile_);
CHECK(instance_id_profile_service);
CHECK(instance_id_profile_service->driver());
return instance_id_profile_service->driver();
}
......@@ -22,6 +22,7 @@
#include "components/gcm_driver/common/gcm_messages.h"
#include "components/gcm_driver/gcm_app_handler.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 "content/public/browser/push_messaging_service.h"
#include "content/public/common/push_event_payload.h"
......@@ -36,12 +37,15 @@
class Profile;
class PushMessagingAppIdentifier;
class PushMessagingServiceObserver;
class PushMessagingServiceTest;
class ScopedKeepAlive;
struct PushSubscriptionOptions;
namespace gcm {
class GCMDriver;
class GCMProfileService;
}
namespace instance_id {
class InstanceIDDriver;
}
namespace user_prefs {
......@@ -89,6 +93,7 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
void GetEncryptionInfo(
const GURL& origin,
int64_t service_worker_registration_id,
const std::string& sender_id,
const content::PushMessagingService::EncryptionInfoCallback& callback)
override;
void Unsubscribe(
......@@ -120,6 +125,7 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
const base::Closure& callback);
private:
friend class PushMessagingBrowserTest;
FRIEND_TEST_ALL_PREFIXES(PushMessagingServiceTest, NormalizeSenderInfo);
FRIEND_TEST_ALL_PREFIXES(PushMessagingServiceTest, PayloadEncryptionTest);
......@@ -141,6 +147,12 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
// Subscribe methods ---------------------------------------------------------
void DoSubscribe(
const PushMessagingAppIdentifier& app_identifier,
const content::PushSubscriptionOptions& options,
const content::PushMessagingService::RegisterCallback& callback,
blink::mojom::PermissionStatus permission_status);
void SubscribeEnd(
const content::PushMessagingService::RegisterCallback& callback,
const std::string& subscription_id,
......@@ -154,9 +166,10 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
void DidSubscribe(
const PushMessagingAppIdentifier& app_identifier,
const std::string& sender_id,
const content::PushMessagingService::RegisterCallback& callback,
const std::string& subscription_id,
gcm::GCMClient::Result result);
instance_id::InstanceID::Result result);
void DidSubscribeWithEncryptionInfo(
const PushMessagingAppIdentifier& app_identifier,
......@@ -165,12 +178,6 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
const std::string& p256dh,
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 --------------------------------------------------
void DidGetEncryptionInfo(
......@@ -184,6 +191,17 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
const std::string& sender_id,
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,
const content::PushMessagingService::UnregisterCallback&,
gcm::GCMClient::Result result);
......@@ -207,8 +225,16 @@ class PushMessagingServiceImpl : public content::PushMessagingService,
// Checks if a given origin is allowed to use Push.
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;
instance_id::InstanceIDDriver* GetInstanceIDDriver() const;
// Testing methods -----------------------------------------------------------
// Callback to be invoked when a message has been dispatched. Enables tests to
......
......@@ -31,6 +31,11 @@
#include "content/public/test/test_browser_thread_bundle.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 {
const char kTestOrigin[] = "https://example.com";
......@@ -101,16 +106,19 @@ class PushMessagingServiceTest : public ::testing::Test {
void DidRegister(std::string* subscription_id_out,
std::vector<uint8_t>* p256dh_out,
std::vector<uint8_t>* auth_out,
base::Closure done_callback,
const std::string& registration_id,
const std::vector<uint8_t>& p256dh,
const std::vector<uint8_t>& auth,
content::PushRegistrationStatus status) {
ASSERT_EQ(content::PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE,
EXPECT_EQ(content::PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE,
status);
*subscription_id_out = registration_id;
*p256dh_out = p256dh;
*auth_out = auth;
done_callback.Run();
}
// Callback to use when observing messages dispatched by the push service.
......@@ -134,6 +142,12 @@ class PushMessagingServiceTest : public ::testing::Test {
private:
content::TestBrowserThreadBundle thread_bundle_;
PushMessagingTestingProfile profile_;
#if defined(OS_ANDROID)
instance_id::InstanceIDAndroid::ScopedBlockOnAsyncTasksForTesting
block_async_;
instance_id::ScopedUseFakeInstanceIDAndroid use_fake_;
#endif // OS_ANDROID
};
TEST_F(PushMessagingServiceTest, PayloadEncryptionTest) {
......@@ -149,6 +163,8 @@ TEST_F(PushMessagingServiceTest, PayloadEncryptionTest) {
std::string subscription_id;
std::vector<uint8_t> p256dh, auth;
base::RunLoop run_loop;
// (2) Subscribe for Push Messaging, and verify that we've got the required
// information in order to be able to create encrypted messages.
content::PushSubscriptionOptions options;
......@@ -157,11 +173,11 @@ TEST_F(PushMessagingServiceTest, PayloadEncryptionTest) {
push_service->SubscribeFromWorker(
origin, kTestServiceWorkerId, options,
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
base::RunLoop().RunUntilIdle();
run_loop.Run();
ASSERT_GT(subscription_id.size(), 0u);
ASSERT_GT(p256dh.size(), 0u);
......
......@@ -16,15 +16,14 @@
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/profiles/profile.h"
#include "components/gcm_driver/fake_gcm_client_factory.h"
#include "components/gcm_driver/fake_gcm_driver.h"
#include "components/gcm_driver/gcm_driver.h"
#include "components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.h"
#include "content/public/browser/browser_context.h"
namespace gcm {
namespace {
class CustomFakeGCMDriver : public FakeGCMDriver {
class CustomFakeGCMDriver : public instance_id::FakeGCMDriverForInstanceID {
public:
explicit CustomFakeGCMDriver(FakeGCMProfileService* service);
~CustomFakeGCMDriver() override;
......@@ -42,7 +41,7 @@ class CustomFakeGCMDriver : public FakeGCMDriver {
const IncomingMessage& message);
protected:
// FakeGCMDriver overrides:
// FakeGCMDriverForInstanceID overrides:
void RegisterImpl(const std::string& app_id,
const std::vector<std::string>& sender_ids) override;
void UnregisterImpl(const std::string& app_id) override;
......@@ -59,9 +58,9 @@ class CustomFakeGCMDriver : public FakeGCMDriver {
};
CustomFakeGCMDriver::CustomFakeGCMDriver(FakeGCMProfileService* service)
: FakeGCMDriver(base::ThreadTaskRunnerHandle::Get()),
service_(service) {
}
: instance_id::FakeGCMDriverForInstanceID(
base::ThreadTaskRunnerHandle::Get()),
service_(service) {}
CustomFakeGCMDriver::~CustomFakeGCMDriver() {
}
......
......@@ -3443,6 +3443,8 @@ test("unit_tests") {
sources -= [ "../browser/policy/policy_path_parser_unittest.cc" ]
deps += [
":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",
"//v8:v8_external_startup_data_assets",
]
......
......@@ -22,7 +22,6 @@ android_library("gcm_driver_java") {
]
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/GoogleCloudMessagingSubscriber.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 @@
#include "base/macros.h"
#include "components/gcm_driver/gcm_driver.h"
namespace base {
class SequencedTaskRunner;
}
namespace gcm {
class FakeGCMDriver : public GCMDriver {
......
......@@ -26,7 +26,7 @@ public class InstanceIDWithSubtype extends InstanceID {
/** Cached instances. May be accessed from multiple threads; synchronize on InstanceID.class. */
@VisibleForTesting
@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. */
@VisibleForTesting
......
......@@ -7,6 +7,7 @@ package org.chromium.components.gcm_driver.instance_id;
import android.content.Context;
import android.os.Bundle;
import android.os.Looper;
import android.util.Pair;
import com.google.android.gms.iid.InstanceID;
......@@ -27,6 +28,8 @@ import java.util.Random;
public class FakeInstanceIDWithSubtype extends InstanceIDWithSubtype {
private String mId = null;
private long mCreationTime = 0;
/** Map from (subtype + ',' + authorizedEntity + ',' + scope) to token. */
private Map<String, String> mTokens = new HashMap<>();
/**
......@@ -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) {
super(context, subtype);
......
......@@ -17,6 +17,10 @@ namespace instance_id {
FakeGCMDriverForInstanceID::FakeGCMDriverForInstanceID()
: gcm::FakeGCMDriver(base::ThreadTaskRunnerHandle::Get()) {}
FakeGCMDriverForInstanceID::FakeGCMDriverForInstanceID(
const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
: FakeGCMDriver(blocking_task_runner) {}
FakeGCMDriverForInstanceID::~FakeGCMDriverForInstanceID() {
}
......@@ -67,6 +71,9 @@ void FakeGCMDriverForInstanceID::GetToken(
tokens_[key] = token;
}
last_gettoken_app_id_ = app_id;
last_gettoken_authorized_entity_ = authorized_entity;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(callback, token, gcm::GCMClient::SUCCESS));
}
......
......@@ -13,12 +13,18 @@
#include "base/macros.h"
#include "components/gcm_driver/fake_gcm_driver.h"
namespace base {
class SequencedTaskRunner;
}
namespace instance_id {
class FakeGCMDriverForInstanceID : public gcm::FakeGCMDriver,
public gcm::InstanceIDHandler {
public:
FakeGCMDriverForInstanceID();
explicit FakeGCMDriverForInstanceID(
const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner);
~FakeGCMDriverForInstanceID() override;
// FakeGCMDriver overrides:
......@@ -42,9 +48,19 @@ class FakeGCMDriverForInstanceID : public gcm::FakeGCMDriver,
const std::string& app_id,
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:
std::map<std::string, std::pair<std::string, std::string>> instance_id_data_;
std::map<std::string, std::string> tokens_;
std::string last_gettoken_app_id_;
std::string last_gettoken_authorized_entity_;
DISALLOW_COPY_AND_ASSIGN(FakeGCMDriverForInstanceID);
};
......
......@@ -36,8 +36,9 @@
namespace content {
// 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
// true of subscriptions previously stored in the database.
// sender ID must be the one used to register. Unfortunately, this isn't always
// 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 kPushRegistrationIdServiceWorkerKey[] = "push_registration_id";
......@@ -136,6 +137,7 @@ class PushMessagingMessageFilter::Core {
void GetEncryptionInfoOnUI(
const GURL& origin,
int64_t service_worker_registration_id,
const std::string& sender_id,
const PushMessagingService::EncryptionInfoCallback& io_thread_callback);
// Called (directly) from both the UI and IO threads.
......@@ -304,7 +306,8 @@ void PushMessagingMessageFilter::DidCheckForExistingRegistration(
BrowserThread::UI, FROM_HERE,
base::Bind(&Core::GetEncryptionInfoOnUI,
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;
}
// TODO(johnme): The spec allows the register algorithm to reject with an
......@@ -365,6 +368,8 @@ void PushMessagingMessageFilter::Core::RegisterOnUI(
PushMessagingService* push_service = service();
if (!push_service) {
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.
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
......@@ -753,6 +758,11 @@ void PushMessagingMessageFilter::DidGetSubscription(
break;
}
ServiceWorkerRegistration* registration =
service_worker_context_->GetLiveRegistration(
service_worker_registration_id);
const GURL origin = registration->pattern().GetOrigin();
const bool uses_standard_protocol =
IsApplicationServerKey(push_subscription_id_and_sender_info[1]);
const GURL endpoint = CreateEndpoint(
......@@ -763,16 +773,12 @@ void PushMessagingMessageFilter::DidGetSubscription(
weak_factory_io_to_io_.GetWeakPtr(), request_id, endpoint,
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::UI, FROM_HERE,
base::Bind(&Core::GetEncryptionInfoOnUI,
base::Unretained(ui_core_.get()), origin,
service_worker_registration_id, callback));
service_worker_registration_id,
push_subscription_id_and_sender_info[1], callback));
return;
}
......@@ -900,12 +906,13 @@ void PushMessagingMessageFilter::Core::GetPermissionStatusOnUI(
void PushMessagingMessageFilter::Core::GetEncryptionInfoOnUI(
const GURL& origin,
int64_t service_worker_registration_id,
const std::string& sender_id,
const PushMessagingService::EncryptionInfoCallback& io_thread_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
PushMessagingService* push_service = service();
if (push_service) {
push_service->GetEncryptionInfo(
origin, service_worker_registration_id,
origin, service_worker_registration_id, sender_id,
base::Bind(&ForwardEncryptionInfoToIOThreadProxy, io_thread_callback));
return;
}
......
......@@ -59,6 +59,22 @@ void ClearPushSubscriptionIDOnIO(
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(
BrowserContext* browser_context, const GURL& origin) {
StoragePartition* partition =
......@@ -102,4 +118,21 @@ void PushMessagingService::ClearPushSubscriptionID(
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
......@@ -67,9 +67,12 @@ class CONTENT_EXPORT PushMessagingService {
const RegisterCallback& callback) = 0;
// 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,
int64_t service_worker_registration_id,
const std::string& sender_id,
const EncryptionInfoCallback& callback) = 0;
// Unsubscribe the given |sender_id| from the push messaging service. The
......@@ -104,6 +107,16 @@ class CONTENT_EXPORT PushMessagingService {
const GURL& origin,
int64_t service_worker_registration_id,
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
......
......@@ -101,6 +101,7 @@ void LayoutTestPushMessagingService::SubscribeFromWorker(
void LayoutTestPushMessagingService::GetEncryptionInfo(
const GURL& origin,
int64_t service_worker_registration_id,
const std::string& sender_id,
const EncryptionInfoCallback& callback) {
std::vector<uint8_t> p256dh(
kTestP256Key, kTestP256Key + arraysize(kTestP256Key));
......
......@@ -41,6 +41,7 @@ class LayoutTestPushMessagingService : public PushMessagingService {
void GetEncryptionInfo(
const GURL& origin,
int64_t service_worker_registration_id,
const std::string& sender_id,
const PushMessagingService::EncryptionInfoCallback& callback) override;
blink::WebPushPermissionStatus GetPermissionStatus(const GURL& origin,
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