From 547786f1cd3e7a011fe80f6630078596e1f3944b Mon Sep 17 00:00:00 2001 From: Dorel Luca Date: Wed, 28 Aug 2019 23:09:08 +0300 Subject: [PATCH] Backed out 3 changesets (bug 1343678) for WPT failures on Android. CLOSED TREE Backed out changeset 619e7838ebfd (bug 1343678) Backed out changeset 5010684cdca4 (bug 1343678) Backed out changeset b01343e4c2eb (bug 1343678) --- .../test/test_serviceworker_interfaces.js | 10 +- .../mochitest/general/test_interfaces.js | 5 +- dom/workers/test/test_worker_interfaces.js | 5 +- mobile/android/app/geckoview-prefs.js | 2 +- .../components/geckoview/GeckoView.manifest | 4 - .../components/geckoview/GeckoViewPush.js | 253 ------------------ .../components/geckoview/GeckoViewStartup.js | 5 - mobile/android/components/geckoview/moz.build | 1 - mobile/android/geckoview/api.txt | 27 -- .../test-support/test-support.js | 2 +- .../src/androidTest/assets/www/push/push.html | 9 - .../src/androidTest/assets/www/push/push.js | 44 --- .../src/androidTest/assets/www/push/sw.js | 21 -- .../mozilla/geckoview/test/BaseSessionTest.kt | 1 - .../org/mozilla/geckoview/test/WebPushTest.kt | 199 -------------- .../mozilla/geckoview/test/WebPushUtils.java | 170 ------------ .../org/mozilla/geckoview/Base64Utils.java | 14 - .../org/mozilla/geckoview/GeckoRuntime.java | 19 -- .../mozilla/geckoview/WebPushController.java | 138 ---------- .../mozilla/geckoview/WebPushDelegate.java | 61 ----- .../geckoview/WebPushSubscription.java | 175 ------------ .../mozilla/geckoview/doc-files/CHANGELOG.md | 7 +- .../geckoview_example/GeckoViewActivity.java | 8 - mobile/android/installer/package-manifest.in | 1 - .../geckoview/GeckoViewPushController.jsm | 77 ------ mobile/android/modules/geckoview/moz.build | 1 - widget/android/Base64UtilsSupport.h | 44 --- widget/android/nsAppShell.cpp | 2 - 28 files changed, 14 insertions(+), 1291 deletions(-) delete mode 100644 mobile/android/components/geckoview/GeckoViewPush.js delete mode 100644 mobile/android/geckoview/src/androidTest/assets/www/push/push.html delete mode 100644 mobile/android/geckoview/src/androidTest/assets/www/push/push.js delete mode 100644 mobile/android/geckoview/src/androidTest/assets/www/push/sw.js delete mode 100644 mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebPushTest.kt delete mode 100644 mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebPushUtils.java delete mode 100644 mobile/android/geckoview/src/main/java/org/mozilla/geckoview/Base64Utils.java delete mode 100644 mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushController.java delete mode 100644 mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushDelegate.java delete mode 100644 mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushSubscription.java delete mode 100644 mobile/android/modules/geckoview/GeckoViewPushController.jsm delete mode 100644 widget/android/Base64UtilsSupport.h diff --git a/dom/serviceworkers/test/test_serviceworker_interfaces.js b/dom/serviceworkers/test/test_serviceworker_interfaces.js index 0c99caabba12..31c993d1d40b 100644 --- a/dom/serviceworkers/test/test_serviceworker_interfaces.js +++ b/dom/serviceworkers/test/test_serviceworker_interfaces.js @@ -209,15 +209,15 @@ var interfaceNamesInGlobalScope = [ // IMPORTANT: Do not change this list without review from a DOM peer! "PromiseRejectionEvent", // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PushEvent" }, + { name: "PushEvent", fennecOrDesktop: true }, // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PushManager" }, + { name: "PushManager", fennecOrDesktop: true }, // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PushMessageData" }, + { name: "PushMessageData", fennecOrDesktop: true }, // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PushSubscription" }, + { name: "PushSubscription", fennecOrDesktop: true }, // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PushSubscriptionOptions" }, + { name: "PushSubscriptionOptions", fennecOrDesktop: true }, // IMPORTANT: Do not change this list without review from a DOM peer! "Request", // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/tests/mochitest/general/test_interfaces.js b/dom/tests/mochitest/general/test_interfaces.js index 398d1d3a31c9..61ec61b7e360 100644 --- a/dom/tests/mochitest/general/test_interfaces.js +++ b/dom/tests/mochitest/general/test_interfaces.js @@ -918,13 +918,14 @@ var interfaceNamesInGlobalScope = [ // IMPORTANT: Do not change this list without review from a DOM peer! { name: "PublicKeyCredential" }, // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PushManager", insecureContext: true }, + { name: "PushManager", insecureContext: true, fennecOrDesktop: true }, // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PushSubscription", insecureContext: true }, + { name: "PushSubscription", insecureContext: true, fennecOrDesktop: true }, // IMPORTANT: Do not change this list without review from a DOM peer! { name: "PushSubscriptionOptions", insecureContext: true, + fennecOrDesktop: true, }, // IMPORTANT: Do not change this list without review from a DOM peer! { name: "RadioNodeList", insecureContext: true }, diff --git a/dom/workers/test/test_worker_interfaces.js b/dom/workers/test/test_worker_interfaces.js index 0e271b4b6e6f..bf74a4ee8cbf 100644 --- a/dom/workers/test/test_worker_interfaces.js +++ b/dom/workers/test/test_worker_interfaces.js @@ -213,13 +213,14 @@ var interfaceNamesInGlobalScope = [ // IMPORTANT: Do not change this list without review from a DOM peer! { name: "PromiseRejectionEvent", insecureContext: true }, // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PushManager", insecureContext: true }, + { name: "PushManager", insecureContext: true, fennecOrDesktop: true }, // IMPORTANT: Do not change this list without review from a DOM peer! - { name: "PushSubscription", insecureContext: true }, + { name: "PushSubscription", insecureContext: true, fennecOrDesktop: true }, // IMPORTANT: Do not change this list without review from a DOM peer! { name: "PushSubscriptionOptions", insecureContext: true, + fennecOrDesktop: true, }, // IMPORTANT: Do not change this list without review from a DOM peer! { name: "Request", insecureContext: true }, diff --git a/mobile/android/app/geckoview-prefs.js b/mobile/android/app/geckoview-prefs.js index 113c6d1bbce7..c721f800c7f9 100644 --- a/mobile/android/app/geckoview-prefs.js +++ b/mobile/android/app/geckoview-prefs.js @@ -39,7 +39,7 @@ pref("geckoview.console.enabled", false); pref("dom.capture.enabled", true); // Disable Web Push until we get it working -pref("dom.push.enabled", true); +pref("dom.push.enabled", false); // enable external storage API pref("dom.storageManager.enabled", true); diff --git a/mobile/android/components/geckoview/GeckoView.manifest b/mobile/android/components/geckoview/GeckoView.manifest index 95a3aa1662e6..ddf60a923e1a 100644 --- a/mobile/android/components/geckoview/GeckoView.manifest +++ b/mobile/android/components/geckoview/GeckoView.manifest @@ -23,7 +23,3 @@ contract @mozilla.org/filepicker;1 {e4565e36-f101-4bf5-950b-4be0887785a9} proces # GeckoViewExternalAppService.js component {a89eeec6-6608-42ee-a4f8-04d425992f45} GeckoViewExternalAppService.js contract @mozilla.org/uriloader/external-helper-app-service;1 {a89eeec6-6608-42ee-a4f8-04d425992f45} - -# GeckoViewPush.js -component {a54d84d7-98a4-4fec-b664-e42e512ae9cc} GeckoViewPush.js -contract @mozilla.org/push/Service;1 {a54d84d7-98a4-4fec-b664-e42e512ae9cc} diff --git a/mobile/android/components/geckoview/GeckoViewPush.js b/mobile/android/components/geckoview/GeckoViewPush.js deleted file mode 100644 index 1b4084554151..000000000000 --- a/mobile/android/components/geckoview/GeckoViewPush.js +++ /dev/null @@ -1,253 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { XPCOMUtils } = ChromeUtils.import( - "resource://gre/modules/XPCOMUtils.jsm" -); -const { GeckoViewUtils } = ChromeUtils.import( - "resource://gre/modules/GeckoViewUtils.jsm" -); - -const { debug, warn } = GeckoViewUtils.initLogging("GeckoViewPush"); // eslint-disable-line no-unused-vars - -ChromeUtils.defineModuleGetter( - this, - "EventDispatcher", - "resource://gre/modules/Messaging.jsm" -); - -// Observer notification topics for push messages and subscription status -// changes. These are duplicated and used in `nsIPushNotifier`. They're exposed -// on `nsIPushService` so that JS callers only need to import this service. -const OBSERVER_TOPIC_PUSH = "push-message"; -const OBSERVER_TOPIC_SUBSCRIPTION_CHANGE = "push-subscription-change"; -const OBSERVER_TOPIC_SUBSCRIPTION_MODIFIED = "push-subscription-modified"; - -function createSubscription({ - scope, - principal, - browserPublicKey, - authSecret, - endpoint, - appServerKey, -}) { - const decodedBrowserKey = ChromeUtils.base64URLDecode(browserPublicKey, { - padding: "ignore", - }); - const decodedAuthSecret = ChromeUtils.base64URLDecode(authSecret, { - padding: "ignore", - }); - - return new PushSubscription({ - endpoint: endpoint, - scope, - p256dhKey: decodedBrowserKey, - authenticationSecret: decodedAuthSecret, - appServerKey, - }); -} - -function scopeWithAttrs(scope, attrs) { - return scope + ChromeUtils.originAttributesToSuffix(attrs); -} - -function PushService() { - this.wrappedJSObject = this; -} - -PushService.prototype = { - classID: Components.ID("{a54d84d7-98a4-4fec-b664-e42e512ae9cc}"), - contractID: "@mozilla.org/push/Service;1", - QueryInterface: ChromeUtils.generateQI([ - Ci.nsIObserver, - Ci.nsISupportsWeakReference, - Ci.nsIPushService, - Ci.nsIPushQuotaManager, - Ci.nsIPushErrorReporter, - ]), - - pushTopic: OBSERVER_TOPIC_PUSH, - subscriptionChangeTopic: OBSERVER_TOPIC_SUBSCRIPTION_CHANGE, - subscriptionModifiedTopic: OBSERVER_TOPIC_SUBSCRIPTION_MODIFIED, - - // nsIObserver methods - - observe(subject, topic, data) {}, - - // nsIPushService methods - - subscribe(scope, principal, callback) { - this.subscribeWithKey(scope, principal, null, callback); - }, - - async subscribeWithKey(scope, principal, appServerKey, callback) { - try { - const response = await EventDispatcher.instance.sendRequestForResult({ - type: "GeckoView:PushSubscribe", - scope: scopeWithAttrs(scope, principal.originAttributes), - appServerKey: appServerKey - ? ChromeUtils.base64URLEncode(new Uint8Array(appServerKey), { - pad: true, - }) - : null, - }); - - let subscription = null; - if (response) { - subscription = createSubscription({ - ...response, - scope, - principal, - appServerKey, - }); - } - - callback.onPushSubscription(Cr.NS_OK, subscription); - } catch (e) { - callback.onPushSubscription(Cr.NS_ERROR_FAILURE, null); - } - }, - - async unsubscribe(scope, principal, callback) { - try { - await EventDispatcher.instance.sendRequestForResult({ - type: "GeckoView:PushUnsubscribe", - scope: scopeWithAttrs(scope, principal.originAttributes), - }); - - callback.onUnsubscribe(Cr.NS_OK, true); - } catch (e) { - callback.onUnsubscribe(Cr.NS_ERROR_FAILURE, false); - } - }, - - async getSubscription(scope, principal, callback) { - try { - const response = await EventDispatcher.instance.sendRequestForResult({ - type: "GeckoView:PushGetSubscription", - scope: scopeWithAttrs(scope, principal.originAttributes), - }); - - let subscription = null; - if (response) { - subscription = createSubscription({ - ...response, - scope, - principal, - }); - } - - callback.onPushSubscription(Cr.NS_OK, subscription); - } catch (e) { - callback.onPushSubscription(Cr.NS_ERROR_FAILURE, null); - } - }, - - clearForDomain(domain, callback) { - callback.onClear(Cr.NS_OK); - }, - - // nsIPushQuotaManager methods - - notificationForOriginShown(origin) {}, - - notificationForOriginClosed(origin) {}, - - // nsIPushErrorReporter methods - - reportDeliveryError(messageId, reason) {}, -}; - -/** `PushSubscription` instances are passed to all subscription callbacks. */ -function PushSubscription(props) { - this._props = props; -} - -PushSubscription.prototype = { - QueryInterface: ChromeUtils.generateQI([Ci.nsIPushSubscription]), - - /** The URL for sending messages to this subscription. */ - get endpoint() { - return this._props.endpoint; - }, - - /** The last time a message was sent to this subscription. */ - get lastPush() { - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - /** The total number of messages sent to this subscription. */ - get pushCount() { - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - /** - * The app will take care of throttling, so we don't - * care about the quota stuff here. - */ - get quota() { - return -1; - }, - - /** - * Indicates whether this subscription was created with the system principal. - * System subscriptions are exempt from the background message quota and - * permission checks. - */ - get isSystemSubscription() { - return false; - }, - - /** The private key used to decrypt incoming push messages, in JWK format */ - get p256dhPrivateKey() { - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - /** - * Indicates whether this subscription is subject to the background message - * quota. - */ - quotaApplies() { - return false; - }, - - /** - * Indicates whether this subscription exceeded the background message quota, - * or the user revoked the notification permission. The caller must request a - * new subscription to continue receiving push messages. - */ - isExpired() { - return false; - }, - - /** - * Returns a key for encrypting messages sent to this subscription. JS - * callers receive the key buffer as a return value, while C++ callers - * receive the key size and buffer as out parameters. - */ - getKey(name) { - switch (name) { - case "p256dh": - return this._getRawKey(this._props.p256dhKey); - - case "auth": - return this._getRawKey(this._props.authenticationSecret); - - case "appServer": - return this._getRawKey(this._props.appServerKey); - } - return []; - }, - - _getRawKey(key) { - if (!key) { - return []; - } - return new Uint8Array(key); - }, -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PushService]); diff --git a/mobile/android/components/geckoview/GeckoViewStartup.js b/mobile/android/components/geckoview/GeckoViewStartup.js index 61ddb854efe9..11add1458f19 100644 --- a/mobile/android/components/geckoview/GeckoViewStartup.js +++ b/mobile/android/components/geckoview/GeckoViewStartup.js @@ -72,11 +72,6 @@ GeckoViewStartup.prototype = { ], }); - GeckoViewUtils.addLazyGetter(this, "GeckoViewPushController", { - module: "resource://gre/modules/GeckoViewPushController.jsm", - ged: ["GeckoView:PushEvent", "GeckoView:PushSubscriptionChanged"], - }); - GeckoViewUtils.addLazyPrefObserver( { name: "geckoview.console.enabled", diff --git a/mobile/android/components/geckoview/moz.build b/mobile/android/components/geckoview/moz.build index 0d41258eff1c..cd29d92f5b9b 100644 --- a/mobile/android/components/geckoview/moz.build +++ b/mobile/android/components/geckoview/moz.build @@ -24,7 +24,6 @@ EXTRA_COMPONENTS += [ 'GeckoViewExternalAppService.js', 'GeckoViewPermission.js', 'GeckoViewPrompt.js', - 'GeckoViewPush.js', 'GeckoViewStartup.js', ] diff --git a/mobile/android/geckoview/api.txt b/mobile/android/geckoview/api.txt index e415eeae6184..83e208a2e39f 100644 --- a/mobile/android/geckoview/api.txt +++ b/mobile/android/geckoview/api.txt @@ -84,9 +84,6 @@ import org.mozilla.geckoview.WebExtensionEventDispatcher; import org.mozilla.geckoview.WebMessage; import org.mozilla.geckoview.WebNotification; import org.mozilla.geckoview.WebNotificationDelegate; -import org.mozilla.geckoview.WebPushController; -import org.mozilla.geckoview.WebPushDelegate; -import org.mozilla.geckoview.WebPushSubscription; import org.mozilla.geckoview.WebRequest; import org.mozilla.geckoview.WebRequestError; import org.mozilla.geckoview.WebResponse; @@ -317,7 +314,6 @@ package org.mozilla.geckoview { method @UiThread @NonNull public RuntimeTelemetry getTelemetry(); method @UiThread @NonNull public WebExtensionController getWebExtensionController(); method @UiThread @Nullable public WebNotificationDelegate getWebNotificationDelegate(); - method @UiThread @NonNull public WebPushController getWebPushController(); method @UiThread public void orientationChanged(); method @UiThread public void orientationChanged(int); method @AnyThread public void readFromParcel(@NonNull Parcel); @@ -1268,29 +1264,6 @@ package org.mozilla.geckoview { method @AnyThread default public void onShowNotification(@NonNull WebNotification); } - public class WebPushController { - method @UiThread public void onPushEvent(@NonNull WebPushSubscription); - method @UiThread public void onPushEvent(@NonNull WebPushSubscription, @Nullable byte[]); - method @UiThread public void onSubscriptionChanged(@NonNull WebPushSubscription); - method @UiThread public void setDelegate(@Nullable WebPushDelegate); - } - - public interface WebPushDelegate { - method @UiThread @Nullable default public GeckoResult onGetSubscription(@NonNull String); - method @UiThread @Nullable default public GeckoResult onSubscribe(@NonNull String, @Nullable byte[]); - method @UiThread @Nullable default public GeckoResult onUnsubscribe(@NonNull String); - } - - public class WebPushSubscription implements Parcelable { - ctor public WebPushSubscription(@NonNull String, @NonNull String, @Nullable byte[], @NonNull byte[], @NonNull byte[]); - field public static final Parcelable.Creator CREATOR; - field @Nullable public final byte[] appServerKey; - field @NonNull public final byte[] authSecret; - field @NonNull public final byte[] browserPublicKey; - field @NonNull public final String endpoint; - field @NonNull public final String scope; - } - @AnyThread public class WebRequest extends WebMessage { ctor public WebRequest(@NonNull String); field public static final int CACHE_MODE_DEFAULT = 1; diff --git a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-support.js b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-support.js index c68bcd59e2bf..019747bdffaa 100644 --- a/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-support.js +++ b/mobile/android/geckoview/src/androidTest/assets/web_extensions/test-support/test-support.js @@ -12,7 +12,7 @@ window.addEventListener("pageshow", () => { try { // Using eval here is the whole point of this WebExtension so we can // safely ignore the eslint warning. - const response = window.eval(message.eval); // eslint-disable-line no-eval + const response = eval(message.eval); // eslint-disable-line no-eval sendResponse(message.id, response); } catch (ex) { sendSyncResponse(message.id, null, ex); diff --git a/mobile/android/geckoview/src/androidTest/assets/www/push/push.html b/mobile/android/geckoview/src/androidTest/assets/www/push/push.html deleted file mode 100644 index f42363d14e0d..000000000000 --- a/mobile/android/geckoview/src/androidTest/assets/www/push/push.html +++ /dev/null @@ -1,9 +0,0 @@ - - - Push API test - - -

Hello, world!

- - - diff --git a/mobile/android/geckoview/src/androidTest/assets/www/push/push.js b/mobile/android/geckoview/src/androidTest/assets/www/push/push.js deleted file mode 100644 index f6eac1696ebb..000000000000 --- a/mobile/android/geckoview/src/androidTest/assets/www/push/push.js +++ /dev/null @@ -1,44 +0,0 @@ -window.doSubscribe = async function(applicationServerKey) { - const registration = await navigator.serviceWorker.register("./sw.js"); - const sub = await registration.pushManager.subscribe({ - applicationServerKey, - }); - return sub.toJSON(); -}; - -window.doGetSubscription = async function() { - const registration = await navigator.serviceWorker.register("./sw.js"); - const sub = await registration.pushManager.getSubscription(); - if (sub) { - return sub.toJSON(); - } - - return null; -}; - -window.doUnsubscribe = async function() { - const registration = await navigator.serviceWorker.register("./sw.js"); - const sub = await registration.pushManager.getSubscription(); - sub.unsubscribe(); - return {}; -}; - -window.doWaitForPushEvent = function() { - return new Promise(resolve => { - navigator.serviceWorker.addEventListener("message", function(e) { - if (e.data.type === "push") { - resolve(e.data.payload); - } - }); - }); -}; - -window.doWaitForSubscriptionChange = function() { - return new Promise(resolve => { - navigator.serviceWorker.addEventListener("message", function(e) { - if (e.data.type === "pushsubscriptionchange") { - resolve(e.data.type); - } - }); - }); -}; diff --git a/mobile/android/geckoview/src/androidTest/assets/www/push/sw.js b/mobile/android/geckoview/src/androidTest/assets/www/push/sw.js deleted file mode 100644 index cdf6ce690397..000000000000 --- a/mobile/android/geckoview/src/androidTest/assets/www/push/sw.js +++ /dev/null @@ -1,21 +0,0 @@ -self.addEventListener("install", function() { - self.skipWaiting(); -}); - -self.addEventListener("activate", function(e) { - e.waitUntil(self.clients.claim()); -}); - -self.addEventListener("push", async function(e) { - const clients = await self.clients.matchAll(); - clients.forEach(function(client) { - client.postMessage({ type: "push", payload: e.data.text() }); - }); -}); - -self.addEventListener("pushsubscriptionchange", async function(e) { - const clients = await self.clients.matchAll(); - clients.forEach(function(client) { - client.postMessage({ type: "pushsubscriptionchange" }); - }); -}); diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt index fa6bebce5ace..6d758ee809c6 100644 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt +++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt @@ -59,7 +59,6 @@ open class BaseSessionTest(noErrorCollector: Boolean = false) { const val COLORS_HTML_PATH = "/assets/www/colors.html" const val FIXED_BOTTOM = "/assets/www/fixedbottom.html" const val STORAGE_TITLE_HTML_PATH = "/assets/www/reflect_local_storage_into_title.html" - const val PUSH_HTML_PATH = "/assets/www/push/push.html" } @get:Rule val sessionRule = GeckoSessionTestRule() diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebPushTest.kt b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebPushTest.kt deleted file mode 100644 index 0cd28c7fb579..000000000000 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebPushTest.kt +++ /dev/null @@ -1,199 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -package org.mozilla.geckoview.test - -import android.os.Parcel -import android.support.test.filters.MediumTest -import android.support.test.runner.AndroidJUnit4 -import android.util.Base64 -import org.hamcrest.MatcherAssert.assertThat -import org.hamcrest.Matchers.* -import org.json.JSONObject -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mozilla.geckoview.* -import org.mozilla.geckoview.test.util.Callbacks -import java.math.BigInteger -import java.security.KeyPair -import java.security.KeyPairGenerator -import java.security.SecureRandom -import java.security.interfaces.ECPublicKey -import java.security.spec.ECGenParameterSpec - -@RunWith(AndroidJUnit4::class) -@MediumTest -class WebPushTest : BaseSessionTest() { - companion object { - val PUSH_ENDPOINT: String = "https://test.endpoint" - val APP_SERVER_KEY_PAIR: KeyPair = generateKeyPair() - val AUTH_SECRET: ByteArray = generateAuthSecret() - val BROWSER_KEY_PAIR: KeyPair = generateKeyPair() - - private fun generateKeyPair(): KeyPair { - try { - val spec = ECGenParameterSpec("secp256r1") - val generator = KeyPairGenerator.getInstance("EC") - generator.initialize(spec) - return generator.generateKeyPair() - } catch (e: Exception) { - throw RuntimeException(e) - } - } - - private fun generateAuthSecret(): ByteArray { - val bytes = BigInteger(128, SecureRandom()).toByteArray() - if (bytes.size > 16) { - return bytes.copyOfRange(bytes.size - 16, bytes.size) - } - - return bytes - } - } - - var delegate: TestPushDelegate? = null - - @Before - fun setup() { - // Grant "desktop notification" permission - mainSession.delegateUntilTestEnd(object : Callbacks.PermissionDelegate { - override fun onContentPermissionRequest(session: GeckoSession, uri: String?, type: Int, callback: GeckoSession.PermissionDelegate.Callback) { - assertThat("Should grant DESKTOP_NOTIFICATIONS permission", type, equalTo(GeckoSession.PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION)) - callback.grant() - } - }) - - delegate = TestPushDelegate() - - sessionRule.addExternalDelegateUntilTestEnd(WebPushDelegate::class, - { d -> sessionRule.runtime.webPushController.setDelegate(d) }, - { sessionRule.runtime.webPushController.setDelegate(null) }, delegate!!) - - - mainSession.loadTestPath(PUSH_HTML_PATH) - mainSession.waitForPageStop() - } - - @After - fun tearDown() { - sessionRule.runtime.webPushController.setDelegate(null) - delegate = null - } - - private fun verifySubscription(subscription: JSONObject) { - assertThat("Push endpoint should match", subscription.getString("endpoint"), equalTo(PUSH_ENDPOINT)) - - val keys = subscription.getJSONObject("keys") - val authSecret = Base64.decode(keys.getString("auth"), Base64.URL_SAFE) - val encryptionKey = WebPushUtils.keyFromString(keys.getString("p256dh")) - - assertThat("Auth secret should match", authSecret, equalTo(AUTH_SECRET)) - assertThat("Encryption key should match", encryptionKey, equalTo(BROWSER_KEY_PAIR.public)) - } - - @Test - fun subscribe() { - // PushManager.subscribe() - val appServerKey = WebPushUtils.keyToString(APP_SERVER_KEY_PAIR.public as ECPublicKey) - var pushSubscription = mainSession.evaluatePromiseJS("window.doSubscribe(\"$appServerKey\")").value as JSONObject - assertThat("Should have a stored subscription", delegate!!.storedSubscription, notNullValue()) - verifySubscription(pushSubscription) - - // PushManager.getSubscription() - pushSubscription = mainSession.evaluatePromiseJS("window.doGetSubscription()").value as JSONObject - verifySubscription(pushSubscription) - } - - @Test - fun subscribeNoAppServerKey() { - // PushManager.subscribe() - var pushSubscription = mainSession.evaluatePromiseJS("window.doSubscribe()").value as JSONObject - assertThat("Should have a stored subscription", delegate!!.storedSubscription, notNullValue()) - verifySubscription(pushSubscription) - - // PushManager.getSubscription() - pushSubscription = mainSession.evaluatePromiseJS("window.doGetSubscription()").value as JSONObject - verifySubscription(pushSubscription) - } - - @Test - fun unsubscribe() { - subscribe() - - // PushManager.unsubscribe() - val unsubResult = mainSession.evaluatePromiseJS("window.doUnsubscribe()").value as JSONObject - assertThat("Unsubscribe result should be non-null", unsubResult, notNullValue()) - assertThat("Should not have a stored subscription", delegate!!.storedSubscription, nullValue()) - } - - @Test - fun pushEvent() { - subscribe() - - val p = mainSession.evaluatePromiseJS("window.doWaitForPushEvent()") - - val testPayload = "The Payload"; - sessionRule.runtime.webPushController.onPushEvent(delegate!!.storedSubscription!!, testPayload.toByteArray(Charsets.UTF_8)) - - assertThat("Push data should match", p.value as String, equalTo(testPayload)) - } - - @Test - fun subscriptionChanged() { - subscribe() - - val p = mainSession.evaluatePromiseJS("window.doWaitForSubscriptionChange()") - - sessionRule.runtime.webPushController.onSubscriptionChanged(delegate!!.storedSubscription!!) - - assertThat("Result should not be null", p.value, notNullValue()) - } - - @Test(expected = IllegalArgumentException::class) - fun invalidDuplicateKeys() { - WebPushSubscription("https://scope", PUSH_ENDPOINT, - WebPushUtils.keyToBytes(APP_SERVER_KEY_PAIR.public as ECPublicKey), - WebPushUtils.keyToBytes(APP_SERVER_KEY_PAIR.public as ECPublicKey)!!, AUTH_SECRET) - } - - @Test - fun parceling() { - val testScope = "https://test.scope"; - val sub = WebPushSubscription(testScope, PUSH_ENDPOINT, - WebPushUtils.keyToBytes(APP_SERVER_KEY_PAIR.public as ECPublicKey), - WebPushUtils.keyToBytes(BROWSER_KEY_PAIR.public as ECPublicKey)!!, AUTH_SECRET) - - val parcel = Parcel.obtain() - sub.writeToParcel(parcel, 0) - parcel.setDataPosition(0) - - val sub2 = WebPushSubscription.CREATOR.createFromParcel(parcel) - assertThat("Scope should match", sub.scope, equalTo(sub2.scope)) - assertThat("Endpoint should match", sub.endpoint, equalTo(sub2.endpoint)) - assertThat("App server key should match", sub.appServerKey, equalTo(sub2.appServerKey)) - assertThat("Encryption key should match", sub.browserPublicKey, equalTo(sub2.browserPublicKey)) - assertThat("Auth secret should match", sub.authSecret, equalTo(sub2.authSecret)) - } - - class TestPushDelegate : WebPushDelegate { - var storedSubscription: WebPushSubscription? = null - - override fun onGetSubscription(scope: String): GeckoResult? { - return GeckoResult.fromValue(storedSubscription) - } - - override fun onUnsubscribe(scope: String): GeckoResult? { - storedSubscription = null - return GeckoResult.fromValue(null) - } - - override fun onSubscribe(scope: String, appServerKey: ByteArray?): GeckoResult? { - appServerKey?.let { assertThat("Application server key should match", it, equalTo(WebPushUtils.keyToBytes(APP_SERVER_KEY_PAIR.public as ECPublicKey))) } - storedSubscription = WebPushSubscription(scope, PUSH_ENDPOINT, appServerKey, WebPushUtils.keyToBytes(BROWSER_KEY_PAIR.public as ECPublicKey)!!, AUTH_SECRET) - return GeckoResult.fromValue(storedSubscription) - } - } -} diff --git a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebPushUtils.java b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebPushUtils.java deleted file mode 100644 index bb1a44329ead..000000000000 --- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/WebPushUtils.java +++ /dev/null @@ -1,170 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * vim: ts=4 sw=4 expandtab: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.geckoview.test; - -import android.support.annotation.AnyThread; -import android.support.annotation.Nullable; -import android.util.Base64; - -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyFactory; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.interfaces.ECPublicKey; -import java.security.spec.ECFieldFp; -import java.security.spec.ECGenParameterSpec; -import java.security.spec.ECParameterSpec; -import java.security.spec.ECPoint; -import java.security.spec.ECPublicKeySpec; -import java.security.spec.EllipticCurve; -import java.security.spec.InvalidKeySpecException; - -/** - * Utilities for converting {@link ECPublicKey} to/from X9.62 encoding. - * - * @see Message Encryption for Web Push - */ -/* package */ class WebPushUtils { - public static final int P256_PUBLIC_KEY_LENGTH = 65; // 1 + 32 + 32 - private static final byte NIST_HEADER = 0x04; // uncompressed format - - private static ECParameterSpec sSpec; - - private WebPushUtils() { - } - - /** - * Encodes an {@link ECPublicKey} into X9.62 format as required - * by Web Push. - * - * @param key the {@link ECPublicKey} to encode - * @return the encoded {@link ECPublicKey} - */ - @AnyThread - public static @Nullable byte[] keyToBytes(final @Nullable ECPublicKey key) { - if (key == null) { - return null; - } - - final ByteBuffer buffer = ByteBuffer.allocate(P256_PUBLIC_KEY_LENGTH); - buffer.put(NIST_HEADER); - - putUnsignedBigInteger(buffer, key.getW().getAffineX()); - putUnsignedBigInteger(buffer, key.getW().getAffineY()); - - if (buffer.position() != P256_PUBLIC_KEY_LENGTH) { - throw new RuntimeException("Unexpected key length " + buffer.position()); - } - - return buffer.array(); - } - - private static void putUnsignedBigInteger(final ByteBuffer buffer, final BigInteger value) { - final byte[] bytes = value.toByteArray(); - if (bytes.length < 32) { - buffer.put(new byte[32 - bytes.length]); - buffer.put(bytes); - } else { - buffer.put(bytes, bytes.length - 32, 32); - } - } - - /** - * Encodes an {@link ECPublicKey} into X9.62 format as required - * by Web Push, further encoded into Base64. - * - * @param key the {@link ECPublicKey} to encode - * @return the encoded {@link ECPublicKey} - */ - @AnyThread - public static @Nullable String keyToString(final @Nullable ECPublicKey key) { - return Base64.encodeToString(keyToBytes(key), Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING); - } - - /** - * @return A {@link ECParameterSpec} for P-256 (secp256r1). - */ - public static ECParameterSpec getP256Spec() { - if (sSpec == null) { - try { - final KeyPairGenerator gen = KeyPairGenerator.getInstance("EC"); - final ECGenParameterSpec genSpec = new ECGenParameterSpec("secp256r1"); - gen.initialize(genSpec); - sSpec = ((ECPublicKey) gen.generateKeyPair().getPublic()).getParams(); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } catch (InvalidAlgorithmParameterException e) { - throw new RuntimeException(e); - } - } - - return sSpec; - } - - /** - * Converts a Base64 X9.62 encoded Web Push key into a {@link ECPublicKey}. - * - * @param base64Bytes the X9.62 data as Base64 - * @return a {@link ECPublicKey} - */ - @AnyThread - public static @Nullable ECPublicKey keyFromString(final @Nullable String base64Bytes) { - if (base64Bytes == null) { - return null; - } - - return keyFromBytes(Base64.decode(base64Bytes, Base64.URL_SAFE)); - } - - private static BigInteger readUnsignedBigInteger(final byte[] bytes, final int offset, final int length) { - byte[] mag = bytes; - if (offset != 0 || length != bytes.length) { - mag = new byte[length]; - System.arraycopy(bytes, offset, mag, 0, length); - } - return new BigInteger(1, mag); - } - - /** - * Converts a X9.62 encoded Web Push key into a {@link ECPublicKey}. - * - * @param bytes the X9.62 data - * @return a {@link ECPublicKey} - */ - @AnyThread - public static @Nullable ECPublicKey keyFromBytes(final @Nullable byte[] bytes) { - if (bytes == null) { - return null; - } - - if (bytes.length != P256_PUBLIC_KEY_LENGTH) { - throw new IllegalArgumentException(String.format("Expected exactly %d bytes", P256_PUBLIC_KEY_LENGTH)); - } - - if (bytes[0] != NIST_HEADER) { - throw new IllegalArgumentException("Expected uncompressed NIST format"); - } - - try { - final BigInteger x = readUnsignedBigInteger(bytes, 1, 32); - final BigInteger y = readUnsignedBigInteger(bytes, 33, 32); - - final ECPoint point = new ECPoint(x, y); - final ECPublicKeySpec spec = new ECPublicKeySpec(point, getP256Spec()); - final KeyFactory factory = KeyFactory.getInstance("EC"); - final ECPublicKey key = (ECPublicKey) factory.generatePublic(spec); - - return key; - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } catch (InvalidKeySpecException e) { - throw new RuntimeException(e); - } - } -} diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/Base64Utils.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/Base64Utils.java deleted file mode 100644 index 279bd88790e1..000000000000 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/Base64Utils.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.mozilla.geckoview; - -import org.mozilla.gecko.annotation.WrapForJNI; - -/** - * This class exposes the Base64 URL encode/decode functions from Gecko. They are different - * from android.util.Base64 in that they always use URL encoding, no padding, and are - * constant time. The last bit is important when dealing with values that might be secret - * as we do with Web Push. - */ -/* package */ class Base64Utils { - @WrapForJNI public static native byte[] decode(final String data); - @WrapForJNI public static native String encode(final byte[] data); -} diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java index c0550f524ca8..e8e5998f2501 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java @@ -165,7 +165,6 @@ public final class GeckoRuntime implements Parcelable { private final WebExtensionEventDispatcher mWebExtensionDispatcher; private StorageController mStorageController; private final WebExtensionController mWebExtensionController; - private WebPushController mPushController; private GeckoRuntime() { mWebExtensionDispatcher = new WebExtensionEventDispatcher(); @@ -649,24 +648,6 @@ public final class GeckoRuntime implements Parcelable { return mStorageController; } - /** - * Get the Web Push controller for this runtime. - * The Web Push controller can be used to allow content - * to use the Web Push API. - * - * @return The {@link WebPushController} for this instance. - */ - @UiThread - public @NonNull WebPushController getWebPushController() { - ThreadUtils.assertOnUiThread(); - - if (mPushController == null) { - mPushController = new WebPushController(); - } - - return mPushController; - } - @Override // Parcelable @AnyThread public int describeContents() { diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushController.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushController.java deleted file mode 100644 index 27cdaa11d503..000000000000 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushController.java +++ /dev/null @@ -1,138 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * vim: ts=4 sw=4 expandtab: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.geckoview; - -import org.mozilla.gecko.EventDispatcher; -import org.mozilla.gecko.util.BundleEventListener; -import org.mozilla.gecko.util.EventCallback; -import org.mozilla.gecko.util.GeckoBundle; -import org.mozilla.gecko.util.ThreadUtils; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.UiThread; - -public class WebPushController { - private static final String LOGTAG = "WebPushController"; - - private WebPushDelegate mDelegate; - private BundleEventListener mEventListener; - - /* package */ WebPushController() { - mEventListener = new EventListener(); - EventDispatcher.getInstance().registerUiThreadListener(mEventListener, - "GeckoView:PushSubscribe", - "GeckoView:PushUnsubscribe", - "GeckoView:PushGetSubscription"); - } - - /** - * Sets the {@link WebPushDelegate} for this instance. - * - * @param delegate The {@link WebPushDelegate} instance. - */ - @UiThread - public void setDelegate(final @Nullable WebPushDelegate delegate) { - ThreadUtils.assertOnUiThread(); - mDelegate = delegate; - } - - /** - * Send a push event for a given subscription. - * - * @param subscription The WebPushSubscription that the event belongs to. - */ - @UiThread - public void onPushEvent(final @NonNull WebPushSubscription subscription) { - ThreadUtils.assertOnUiThread(); - onPushEvent(subscription, null); - } - - /** - * Send a push event with a payload for a given subscription. - * - * @param subscription The WebPushSubscription that the event belongs to. - * @param data The unencrypted payload. - */ - @UiThread - public void onPushEvent(final @NonNull WebPushSubscription subscription, final @Nullable byte[] data) { - ThreadUtils.assertOnUiThread(); - - final GeckoBundle msg = new GeckoBundle(2); - msg.putBundle("subscription", subscription.toBundle()); - msg.putString("data", Base64Utils.encode(data)); - EventDispatcher.getInstance().dispatch("GeckoView:PushEvent", msg); - } - - /** - * Notify that a given subscription has changed. This is normally a signal to the content - * that it needs to re-subscribe. - * - * @param subscription The subscription that changed. - */ - @UiThread - public void onSubscriptionChanged(final @NonNull WebPushSubscription subscription) { - ThreadUtils.assertOnUiThread(); - - final GeckoBundle msg = new GeckoBundle(1); - msg.putBundle("subscription", subscription.toBundle()); - EventDispatcher.getInstance().dispatch("GeckoView:PushSubscriptionChanged", msg); - } - - private class EventListener implements BundleEventListener { - - @Override - public void handleMessage(final String event, final GeckoBundle message, final EventCallback callback) { - if (mDelegate == null) { - callback.sendError("Not allowed"); - return; - } - - switch (event) { - case "GeckoView:PushSubscribe": { - byte[] appServerKey = null; - if (message.containsKey("appServerKey")) { - appServerKey = Base64Utils.decode(message.getString("appServerKey")); - } - - final GeckoResult result = - mDelegate.onSubscribe(message.getString("scope"), appServerKey); - - if (result == null) { - callback.sendSuccess(null); - return; - } - - result.accept(subscription -> callback.sendSuccess(subscription != null ? subscription.toBundle() : null), - error -> callback.sendSuccess(null)); - break; - } - case "GeckoView:PushUnsubscribe": { - final GeckoResult result = mDelegate.onUnsubscribe(message.getString("scope")); - if (result == null) { - callback.sendSuccess(null); - return; - } - - result.accept(val -> callback.sendSuccess(null), err -> callback.sendError(err.getMessage())); - break; - } - case "GeckoView:PushGetSubscription": { - final GeckoResult result = mDelegate.onGetSubscription(message.getString("scope")); - if (result == null) { - callback.sendSuccess(null); - return; - } - - result.accept(subscription -> callback.sendSuccess(subscription != null ? subscription.toBundle() : null), - err -> callback.sendError(err.getMessage())); - break; - } - } - } - } -} diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushDelegate.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushDelegate.java deleted file mode 100644 index 1ec97deaa9b5..000000000000 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushDelegate.java +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * vim: ts=4 sw=4 expandtab: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.geckoview; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.UiThread; - -public interface WebPushDelegate { - /** - * Creates a push subscription for the given service worker scope. A scope - * uniquely identifies a service worker. `appServerKey` optionally - * creates a restricted subscription. - * - * Applications will likely want to persist the returned {@link WebPushSubscription} in order - * to support {@link #onGetSubscription(String)}. - * - * @param scope The Service Worker scope. - * @param appServerKey An optional application server key. - * @return A {@link GeckoResult} which resolves to a {@link WebPushSubscription} - * - * @see subscribe() - * @see Application server key - */ - @UiThread - default @Nullable GeckoResult onSubscribe(@NonNull String scope, - @Nullable byte[] appServerKey) { - return null; - } - - /** - * Retrieves a subscription for the given service worker scope. - * - * @param scope The scope for the requested {@link WebPushSubscription}. - * @return A {@link GeckoResult} which resolves to a {@link WebPushSubscription} - * - * @see getSubscription() - */ - @UiThread - default @Nullable GeckoResult onGetSubscription(@NonNull String scope) { - return null; - } - - /** - * Removes a push subscription. If this fails, apps should resolve the - * returned {@link GeckoResult} with an exception. - * - * @param scope The Service Worker scope for the subscription. - * @return A {@link GeckoResult}, which if non-exceptional indicates successfully unsubscribing. - * - * @see unsubscribe() - */ - @UiThread - default @Nullable GeckoResult onUnsubscribe(@NonNull String scope) { - return null; - } -} diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushSubscription.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushSubscription.java deleted file mode 100644 index ec2369d8ad0f..000000000000 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/WebPushSubscription.java +++ /dev/null @@ -1,175 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * vim: ts=4 sw=4 expandtab: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.geckoview; - -import org.mozilla.gecko.util.GeckoBundle; - -import android.os.Parcel; -import android.os.Parcelable; -import android.support.annotation.AnyThread; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import java.util.Arrays; - -/** - * This class represents a single Web Push subscription, as described in - * the Web Push API specification. - * - * This is a low-level interface, allowing applications to do all of the heavy lifting - * themselves. It is recommended that consumers have a thorough understanding of the - * Web Push API, especially RFC 8291. - * - * Only trivial sanity checks are performed on the values held here. The application must - * ensure it is generating compliant keys/secrets itself. - */ -public class WebPushSubscription implements Parcelable { - private static final int P256_PUBLIC_KEY_LENGTH = 65; - - /** - * The Service Worker scope associated with this subscription. - * - * @see ServiceWorker registration - */ - @NonNull - public final String scope; - - /** - * The Web Push endpoint for this subscription. This is the URL of a web service which - * implements the Web Push protocol. - * - * @see RFC 8030 - */ - @NonNull - public final String endpoint; - - /** - * This is an optional public key provided by the application server to authenticate - * itself with the endpoint, formatted according to X9.62. - * - * This key is used for VAPID, the Voluntary Application Server Identification (VAPID) - * for Web Push, from RFC 8292. - * - * @see applicationServerKey - * @see Message Encryption for Web Push - */ - @Nullable - public final byte[] appServerKey; - - /** - * The P-256 EC public key, formatted as X9.62, generated by the embedder, to be provided - * to the app server for message encryption. - * - * @see PushEncryptionKeyName - p256dh - * @see RFC 8291 section 3.1 - */ - @NonNull - public final byte[] browserPublicKey; - - /** - * 16 byte secret key, generated by the embedder, to be provided to the app server for use - * in encrypting and authenticating messages sent to the {@link #endpoint}. - * - * @see PushEncryptionKeyName - auth - * @see RFC 8291, section 3.2 - */ - @NonNull - public final byte[] authSecret; - - public WebPushSubscription(final @NonNull String scope, - final @NonNull String endpoint, - final @Nullable byte[] appServerKey, - final @NonNull byte[] browserPublicKey, - final @NonNull byte[] authSecret) { - this.scope = scope; - this.endpoint = endpoint; - this.appServerKey = appServerKey; - this.browserPublicKey = browserPublicKey; - this.authSecret = authSecret; - - if (appServerKey != null) { - if (appServerKey.length != P256_PUBLIC_KEY_LENGTH) { - throw new IllegalArgumentException(String.format("appServerKey should be %d bytes", P256_PUBLIC_KEY_LENGTH)); - } - - if (Arrays.equals(appServerKey, browserPublicKey)) { - throw new IllegalArgumentException("appServerKey and browserPublicKey must differ"); - } - } - - if (browserPublicKey.length != P256_PUBLIC_KEY_LENGTH) { - throw new IllegalArgumentException(String.format("browserPublicKey should be %d bytes", P256_PUBLIC_KEY_LENGTH)); - } - - if (authSecret.length != 16) { - throw new IllegalArgumentException("authSecret must be 128 bits"); - } - } - - private WebPushSubscription(final Parcel in) { - this.scope = in.readString(); - this.endpoint = in.readString(); - - if (ParcelableUtils.readBoolean(in)) { - this.appServerKey = new byte[P256_PUBLIC_KEY_LENGTH]; - in.readByteArray(this.appServerKey); - } else { - appServerKey = null; - } - - this.browserPublicKey = new byte[P256_PUBLIC_KEY_LENGTH]; - in.readByteArray(this.browserPublicKey); - - this.authSecret = new byte[16]; - in.readByteArray(this.authSecret); - } - - /* package */ GeckoBundle toBundle() { - final GeckoBundle bundle = new GeckoBundle(5); - bundle.putString("scope", scope); - bundle.putString("endpoint", endpoint); - if (appServerKey != null) { - bundle.putString("appServerKey", Base64Utils.encode(appServerKey)); - } - bundle.putString("browserPublicKey", Base64Utils.encode(browserPublicKey)); - bundle.putString("authSecret", Base64Utils.encode(authSecret)); - return bundle; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(final Parcel out, final int flags) { - out.writeString(scope); - out.writeString(endpoint); - - ParcelableUtils.writeBoolean(out, appServerKey != null); - if (appServerKey != null) { - out.writeByteArray(appServerKey); - } - - out.writeByteArray(browserPublicKey); - out.writeByteArray(authSecret); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - @AnyThread - public WebPushSubscription createFromParcel(final Parcel parcel) { - return new WebPushSubscription(parcel); - } - - @Override - @AnyThread - public WebPushSubscription[] newArray(final int size) { - return new WebPushSubscription[size]; - } - }; -} diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md index 57e2c95c6782..49118b291936 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md @@ -48,8 +48,6 @@ exclude: true ([bug 1533057]({{bugzilla}}1533057)) - Added Social Tracking Protection support to [`ContentBlocking`][70.17]. ([bug 1568295]({{bugzilla}}1568295)) -- Added support for Web Push via [`WebPushController`][70.18], [`WebPushDelegate`][70.19], and - [`WebPushSubscription`][70.20]. [70.1]: {{javadoc_uri}}/GeckoSessionSettings.Builder.html#contextId-java.lang.String- [70.2]: {{javadoc_uri}}/StorageController.html#clearDataForSessionContext-java.lang.String- @@ -68,9 +66,6 @@ exclude: true [70.15]: {{javadoc_uri}}/WebNotification.html [70.16]: {{javadoc_uri}}/WebNotificationDelegate.html [70.17]: {{javadoc_uri}}/ContentBlocking.html -[70.18]: {{javadoc_uri}}/WebPushController.html -[70.19]: {{javadoc_uri}}/WebPushDelegate.html -[70.20]: {{javadoc_uri}}/WebPushSubscription.html ## v69 - Modified behavior of ['setAutomaticFontSizeAdjustment'][69.1] so that it no @@ -323,4 +318,4 @@ exclude: true [65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String- [65.25]: {{javadoc_uri}}/GeckoResult.html -[api-version]: 8ecf6e4cd7db924c5966e8c956df7fbc502fa4be +[api-version]: 3dc92af421d9e7e2393619f7a17c57422aa51699 diff --git a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java index a7889116893d..6d116706fd83 100644 --- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java +++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java @@ -43,11 +43,8 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; -import android.os.Handler; import android.os.SystemClock; -import android.security.keystore.KeyProperties; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationManagerCompat; @@ -66,11 +63,6 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.interfaces.ECPublicKey; -import java.security.spec.ECGenParameterSpec; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in index 97e7aa94c186..49f33a4f0336 100644 --- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -241,7 +241,6 @@ @BINPATH@/components/GeckoView.manifest @BINPATH@/components/GeckoViewExternalAppService.js @BINPATH@/components/GeckoViewPrompt.js -@BINPATH@/components/GeckoViewPush.js @BINPATH@/components/GeckoViewPermission.js @BINPATH@/components/GeckoViewStartup.js #else diff --git a/mobile/android/modules/geckoview/GeckoViewPushController.jsm b/mobile/android/modules/geckoview/GeckoViewPushController.jsm deleted file mode 100644 index b532478667bc..000000000000 --- a/mobile/android/modules/geckoview/GeckoViewPushController.jsm +++ /dev/null @@ -1,77 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -var EXPORTED_SYMBOLS = ["GeckoViewPushController"]; - -const { GeckoViewUtils } = ChromeUtils.import( - "resource://gre/modules/GeckoViewUtils.jsm" -); -const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); -const { XPCOMUtils } = ChromeUtils.import( - "resource://gre/modules/XPCOMUtils.jsm" -); - -XPCOMUtils.defineLazyServiceGetter( - this, - "PushNotifier", - "@mozilla.org/push/Notifier;1", - "nsIPushNotifier" -); - -// eslint-disable-next-line no-unused-vars -const { debug, warn } = GeckoViewUtils.initLogging("GeckoViewPushController"); - -function createScopeAndPrincipal(scopeAndAttrs) { - const [scope, attrs] = scopeAndAttrs.split("^"); - const uri = Services.io.newURI(scope); - - return [ - scope, - Services.scriptSecurityManager.createContentPrincipal( - uri, - ChromeUtils.createOriginAttributesFromOrigin(attrs) - ), - ]; -} - -const GeckoViewPushController = { - onEvent(aEvent, aData, aCallback) { - debug`onEvent ${aEvent} ${aData}`; - - switch (aEvent) { - case "GeckoView:PushEvent": { - const { - subscription: { scope }, - data, - } = aData; - - const [url, principal] = createScopeAndPrincipal(scope); - - if (!data) { - PushNotifier.notifyPush(url, principal); - return; - } - - const payload = new Uint8Array( - ChromeUtils.base64URLDecode(data, { padding: "ignore" }) - ); - - PushNotifier.notifyPushWithData(url, principal, "", payload); - break; - } - case "GeckoView:PushSubscriptionChanged": { - const { - subscription: { scope }, - } = aData; - - const [url, principal] = createScopeAndPrincipal(scope); - - PushNotifier.notifySubscriptionChange(url, principal); - break; - } - } - }, -}; diff --git a/mobile/android/modules/geckoview/moz.build b/mobile/android/modules/geckoview/moz.build index c72f14df9f34..bef30f937482 100644 --- a/mobile/android/modules/geckoview/moz.build +++ b/mobile/android/modules/geckoview/moz.build @@ -17,7 +17,6 @@ EXTRA_JS_MODULES += [ 'GeckoViewModule.jsm', 'GeckoViewNavigation.jsm', 'GeckoViewProgress.jsm', - 'GeckoViewPushController.jsm', 'GeckoViewRemoteDebugger.jsm', 'GeckoViewSettings.jsm', 'GeckoViewStorageController.jsm', diff --git a/widget/android/Base64UtilsSupport.h b/widget/android/Base64UtilsSupport.h deleted file mode 100644 index 3a1fc2cba7b6..000000000000 --- a/widget/android/Base64UtilsSupport.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef Base64UtilsSupport_h__ -#define Base64UtilsSupport_h__ - -#include "GeneratedJNINatives.h" -#include "mozilla/Base64.h" - -namespace mozilla { -namespace widget { - -class Base64UtilsSupport final - : public java::Base64Utils::Natives { - public: - static jni::ByteArray::LocalRef Decode(jni::String::Param data) { - FallibleTArray bytes; - if (NS_FAILED(Base64URLDecode( - data->ToCString(), Base64URLDecodePaddingPolicy::Ignore, bytes))) { - return nullptr; - } - - return jni::ByteArray::New((const signed char*)bytes.Elements(), - bytes.Length()); - } - - static jni::String::LocalRef Encode(jni::ByteArray::Param data) { - nsTArray bytes = data->GetElements(); - nsCString result; - if (NS_FAILED( - Base64URLEncode(data->Length(), (const uint8_t*)bytes.Elements(), - Base64URLEncodePaddingPolicy::Omit, result))) { - return nullptr; - } - return jni::StringParam(result); - } -}; - -} // namespace widget -} // namespace mozilla - -#endif // Base64UtilsSupport_h__ diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index f8355434d858..c4602dd3e46f 100644 --- a/widget/android/nsAppShell.cpp +++ b/widget/android/nsAppShell.cpp @@ -72,7 +72,6 @@ #include "fennec/MemoryMonitor.h" #include "fennec/ThumbnailHelper.h" #include "WebExecutorSupport.h" -#include "Base64UtilsSupport.h" #ifdef DEBUG_ANDROID_EVENTS # define EVLOG(args...) ALOG(args) @@ -413,7 +412,6 @@ nsAppShell::nsAppShell() mozilla::PrefsHelper::Init(); mozilla::widget::Telemetry::Init(); mozilla::widget::WebExecutorSupport::Init(); - mozilla::widget::Base64UtilsSupport::Init(); nsWindow::InitNatives(); mozilla::gl::AndroidSurfaceTexture::Init(); mozilla::WebAuthnTokenManager::Init();