Backed out 5 changesets (bug 1343678) for causing multiple web-platform failures CLOSED TREE

Backed out changeset 4b77646bc788 (bug 1343678)
Backed out changeset 7396789341b1 (bug 1343678)
Backed out changeset 85526faefe6d (bug 1343678)
Backed out changeset 07b757a21222 (bug 1343678)
Backed out changeset 3551cc55620d (bug 1343678)
This commit is contained in:
Noemi Erli 2019-08-30 00:36:40 +03:00
Родитель 257da8f0d3
Коммит 478b1751c7
30 изменённых файлов: 587 добавлений и 1310 удалений

Просмотреть файл

@ -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!

Просмотреть файл

@ -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 },

Просмотреть файл

@ -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 },

Просмотреть файл

@ -42,7 +42,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);

Просмотреть файл

@ -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}

Просмотреть файл

@ -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]);

Просмотреть файл

@ -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",

Просмотреть файл

@ -24,7 +24,6 @@ EXTRA_COMPONENTS += [
'GeckoViewExternalAppService.js',
'GeckoViewPermission.js',
'GeckoViewPrompt.js',
'GeckoViewPush.js',
'GeckoViewStartup.js',
]

Просмотреть файл

@ -85,9 +85,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;
@ -318,7 +315,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);
@ -1277,29 +1273,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<WebPushSubscription> onGetSubscription(@NonNull String);
method @UiThread @Nullable default public GeckoResult<WebPushSubscription> onSubscribe(@NonNull String, @Nullable byte[]);
method @UiThread @Nullable default public GeckoResult<Void> 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<WebPushSubscription> 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;

Просмотреть файл

@ -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);

Просмотреть файл

@ -1,9 +0,0 @@
<html>
<head>
<title>Push API test</title>
</head>
<body>
<p>Hello, world!</p>
<script src="push.js"></script>
</body>
</html>

Просмотреть файл

@ -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);
}
});
});
};

Просмотреть файл

@ -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" });
});
});

Просмотреть файл

@ -60,7 +60,6 @@ open class BaseSessionTest(noErrorCollector: Boolean = false) {
const val FIXED_BOTTOM = "/assets/www/fixedbottom.html"
const val STORAGE_TITLE_HTML_PATH = "/assets/www/reflect_local_storage_into_title.html"
const val HUNG_SCRIPT = "/assets/www/hungScript.html"
const val PUSH_HTML_PATH = "/assets/www/push/push.html"
}
@get:Rule val sessionRule = GeckoSessionTestRule()

Просмотреть файл

@ -1,212 +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.rule.GeckoSessionTestRule.RejectedPromiseException
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(expected = RejectedPromiseException::class)
fun subscribeNullDelegate() {
sessionRule.runtime.webPushController.setDelegate(null)
mainSession.evaluatePromiseJS("window.doSubscribe()").value as JSONObject
}
@Test(expected = RejectedPromiseException::class)
fun getSubscriptionNullDelegate() {
sessionRule.runtime.webPushController.setDelegate(null)
mainSession.evaluatePromiseJS("window.doGetSubscription()").value as JSONObject
}
@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<WebPushSubscription>? {
return GeckoResult.fromValue(storedSubscription)
}
override fun onUnsubscribe(scope: String): GeckoResult<Void>? {
storedSubscription = null
return GeckoResult.fromValue(null)
}
override fun onSubscribe(scope: String, appServerKey: ByteArray?): GeckoResult<WebPushSubscription>? {
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)
}
}
}

Просмотреть файл

@ -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 <a href="https://tools.ietf.org/html/rfc8291">Message Encryption for Web Push</a>
*/
/* 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);
}
}
}

Просмотреть файл

@ -318,12 +318,7 @@ public final class EventDispatcher extends JNIObject {
return true;
}
final String error = "No listener for " + type;
if (callback != null) {
callback.sendError(error);
}
Log.w(LOGTAG, error);
Log.w(LOGTAG, "No listener for " + type);
return false;
}

Просмотреть файл

@ -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);
}

Просмотреть файл

@ -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() {

Просмотреть файл

@ -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<WebPushSubscription> 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<Void> 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<WebPushSubscription> 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;
}
}
}
}
}

Просмотреть файл

@ -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 <a href="http://w3c.github.io/push-api/#dom-pushmanager-subscribe">subscribe()</a>
* @see <a href="http://w3c.github.io/push-api/#dom-pushsubscriptionoptionsinit-applicationserverkey">Application server key</a>
*/
@UiThread
default @Nullable GeckoResult<WebPushSubscription> 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 <a href="http://w3c.github.io/push-api/#dom-pushmanager-getsubscription">getSubscription()</a>
*/
@UiThread
default @Nullable GeckoResult<WebPushSubscription> 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 <a href="http://w3c.github.io/push-api/#dom-pushsubscription-unsubscribe">unsubscribe()</a>
*/
@UiThread
default @Nullable GeckoResult<Void> onUnsubscribe(@NonNull String scope) {
return 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 <a href="https://www.w3.org/TR/push-api/">Web Push API</a> 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 <a href="https://tools.ietf.org/html/rfc8291">RFC 8291</a>.
*
* 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 <a href="https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register">ServiceWorker registration</a>
*/
@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 <a href="https://tools.ietf.org/html/rfc8030#section-5">RFC 8030</a>
*/
@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 <a href="https://tools.ietf.org/html/rfc8292">RFC 8292</a>.
*
* @see <a href="https://www.w3.org/TR/push-api/#dom-pushsubscriptionoptions-applicationserverkey">applicationServerKey</a>
* @see <a href="https://tools.ietf.org/html/rfc8291">Message Encryption for Web Push</a>
*/
@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 <a href="https://www.w3.org/TR/push-api/#dom-pushencryptionkeyname-p256dh">PushEncryptionKeyName - p256dh</a>
* @see <a href="https://tools.ietf.org/html/rfc8291#section-3.1">RFC 8291 section 3.1</a>
*/
@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 <a href="https://www.w3.org/TR/push-api/#dom-pushencryptionkeyname-auth">PushEncryptionKeyName - auth</a>
* @see <a href="https://tools.ietf.org/html/rfc8291#section-3.2">RFC 8291, section 3.2</a>
*/
@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<WebPushSubscription> CREATOR = new Parcelable.Creator<WebPushSubscription>() {
@Override
@AnyThread
public WebPushSubscription createFromParcel(final Parcel parcel) {
return new WebPushSubscription(parcel);
}
@Override
@AnyThread
public WebPushSubscription[] newArray(final int size) {
return new WebPushSubscription[size];
}
};
}

Просмотреть файл

@ -56,8 +56,6 @@ exclude: true
([bug 1565782]({{bugzilla}}1565782))
- Added onSlowScript to [`ContentDelegate`][70.23] which allows handling of slow and hung scripts.
([bug 1621094]({{bugzilla}}1621094))
- Added support for Web Push via [`WebPushController`][70.24], [`WebPushDelegate`][70.25], and
[`WebPushSubscription`][70.26].
[70.1]: {{javadoc_uri}}/GeckoSessionSettings.Builder.html#contextId-java.lang.String-
[70.2]: {{javadoc_uri}}/StorageController.html#clearDataForSessionContext-java.lang.String-
@ -82,9 +80,6 @@ exclude: true
[70.21]: {{javadoc_uri}}/WebExtensionController.TabDelegate.html#onCloseTab-org.mozilla.geckoview.WebExtension-org.mozilla.geckoview.GeckoSession-
[70.22]: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/remove
[70.23]: {{javadoc_uri}}/GeckoSession.ContentDelegate.html
[70.24]: {{javadoc_uri}}/WebPushController.html
[70.25]: {{javadoc_uri}}/WebPushDelegate.html
[70.26]: {{javadoc_uri}}/WebPushSubscription.html
## v69
- Modified behavior of ['setAutomaticFontSizeAdjustment'][69.1] so that it no
@ -328,4 +323,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]: 2ef7c30341c4cae55cdce87f62c19ae606435430
[api-version]: 4199a7a889674d4402926366a7f4fb2f93de5c46

Просмотреть файл

@ -44,9 +44,7 @@ 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;
@ -67,11 +65,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;

Просмотреть файл

@ -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

Просмотреть файл

@ -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;
}
}
},
};

Просмотреть файл

@ -18,7 +18,6 @@ EXTRA_JS_MODULES += [
'GeckoViewNavigation.jsm',
'GeckoViewProcessHangMonitor.jsm',
'GeckoViewProgress.jsm',
'GeckoViewPushController.jsm',
'GeckoViewRemoteDebugger.jsm',
'GeckoViewSettings.jsm',
'GeckoViewStorageController.jsm',

Просмотреть файл

@ -14,6 +14,146 @@
[PushManager interface: attribute supportedContentEncodings]
expected: FAIL
[PushManager must be primary interface of registration.pushManager]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: attribute options]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: registration.pushManager must inherit property "supportedContentEncodings" with the proper type]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: existence and properties of interface prototype object's @@unscopables property]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: attribute endpoint]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: operation permissionState(PushSubscriptionOptionsInit)]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: operation getSubscription()]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: existence and properties of interface object]
expected:
if (os == "android") and e10s: FAIL
[Stringification of registration.pushManager]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: registration.pushManager must inherit property "getSubscription()" with the proper type]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: operation getKey(PushEncryptionKeyName)]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: calling permissionState(PushSubscriptionOptionsInit) on registration.pushManager with too few arguments must throw TypeError]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface object name]
expected:
if (os == "android") and e10s: FAIL
[ServiceWorkerRegistration interface: registration must inherit property "pushManager" with the proper type]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: operation subscribe(PushSubscriptionOptionsInit)]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: existence and properties of interface object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: existence and properties of interface prototype object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: operation toJSON()]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: existence and properties of interface prototype object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: operation unsubscribe()]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: attribute applicationServerKey]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: existence and properties of interface prototype object's "constructor" property]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: calling subscribe(PushSubscriptionOptionsInit) on registration.pushManager with too few arguments must throw TypeError]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface object name]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: registration.pushManager must inherit property "subscribe(PushSubscriptionOptionsInit)" with the proper type]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: existence and properties of interface prototype object's "constructor" property]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: existence and properties of interface object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: existence and properties of interface prototype object's @@unscopables property]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: registration.pushManager must inherit property "permissionState(PushSubscriptionOptionsInit)" with the proper type]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface object length]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: existence and properties of interface prototype object's "constructor" property]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: existence and properties of interface prototype object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: existence and properties of interface prototype object's @@unscopables property]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface object name]
expected:
if (os == "android") and e10s: FAIL
[ServiceWorkerRegistration interface: attribute pushManager]
expected:
if (os == "android") and e10s: FAIL
[idlharness.https.any.worker.html]
[PushManager interface object length]
@ -31,6 +171,111 @@
[PushManager interface: attribute supportedContentEncodings]
expected: FAIL
[PushSubscription interface: attribute options]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: operation getKey(PushEncryptionKeyName)]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: existence and properties of interface prototype object's @@unscopables property]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: operation permissionState(PushSubscriptionOptionsInit)]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: operation getSubscription()]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: existence and properties of interface object]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: operation subscribe(PushSubscriptionOptionsInit)]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface object name]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: existence and properties of interface object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: existence and properties of interface prototype object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: operation toJSON()]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: existence and properties of interface prototype object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: operation unsubscribe()]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: attribute applicationServerKey]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: existence and properties of interface prototype object's "constructor" property]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface object name]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: attribute endpoint]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: existence and properties of interface prototype object's "constructor" property]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: existence and properties of interface object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: existence and properties of interface prototype object's @@unscopables property]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface object length]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: existence and properties of interface prototype object's "constructor" property]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: existence and properties of interface prototype object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: existence and properties of interface prototype object's @@unscopables property]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface object name]
expected:
if (os == "android") and e10s: FAIL
[ServiceWorkerRegistration interface: attribute pushManager]
expected:
if (os == "android") and e10s: FAIL
[idlharness.https.any.serviceworker.html]
[PushSubscriptionChangeEvent must be primary interface of new PushSubscriptionChangeEvent("pushsubscriptionchange")]
expected: FAIL
@ -83,6 +328,227 @@
[PushSubscriptionChangeEvent interface object name]
expected: FAIL
[PushManager must be primary interface of registration.pushManager]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: attribute options]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: registration.pushManager must inherit property "supportedContentEncodings" with the proper type]
expected:
if (os == "android") and e10s: FAIL
[PushMessageData interface: operation text()]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: existence and properties of interface prototype object's @@unscopables property]
expected:
if (os == "android") and e10s: FAIL
[PushMessageData interface object length]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: attribute endpoint]
expected:
if (os == "android") and e10s: FAIL
[PushEvent interface object length]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: operation permissionState(PushSubscriptionOptionsInit)]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: operation getSubscription()]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: existence and properties of interface object]
expected:
if (os == "android") and e10s: FAIL
[Stringification of registration.pushManager]
expected:
if (os == "android") and e10s: FAIL
[PushEvent interface: existence and properties of interface prototype object]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: registration.pushManager must inherit property "getSubscription()" with the proper type]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: operation getKey(PushEncryptionKeyName)]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: calling permissionState(PushSubscriptionOptionsInit) on registration.pushManager with too few arguments must throw TypeError]
expected:
if (os == "android") and e10s: FAIL
[PushEvent interface: new PushEvent("type") must inherit property "data" with the proper type]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface object name]
expected:
if (os == "android") and e10s: FAIL
[PushMessageData interface: existence and properties of interface prototype object's "constructor" property]
expected:
if (os == "android") and e10s: FAIL
[PushMessageData interface: existence and properties of interface prototype object]
expected:
if (os == "android") and e10s: FAIL
[PushEvent interface object name]
expected:
if (os == "android") and e10s: FAIL
[PushEvent interface: existence and properties of interface prototype object's "constructor" property]
expected:
if (os == "android") and e10s: FAIL
[ServiceWorkerRegistration interface: registration must inherit property "pushManager" with the proper type]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: operation subscribe(PushSubscriptionOptionsInit)]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: existence and properties of interface object]
expected:
if (os == "android") and e10s: FAIL
[PushEvent must be primary interface of new PushEvent("type")]
expected:
if (os == "android") and e10s: FAIL
[PushEvent interface: attribute data]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: existence and properties of interface prototype object]
expected:
if (os == "android") and e10s: FAIL
[PushMessageData interface: operation arrayBuffer()]
expected:
if (os == "android") and e10s: FAIL
[PushMessageData interface: existence and properties of interface prototype object's @@unscopables property]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: operation toJSON()]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: existence and properties of interface prototype object]
expected:
if (os == "android") and e10s: FAIL
[PushMessageData interface: operation json()]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: operation unsubscribe()]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: attribute applicationServerKey]
expected:
if (os == "android") and e10s: FAIL
[PushMessageData interface object name]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: existence and properties of interface prototype object's "constructor" property]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: calling subscribe(PushSubscriptionOptionsInit) on registration.pushManager with too few arguments must throw TypeError]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface object name]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: registration.pushManager must inherit property "subscribe(PushSubscriptionOptionsInit)" with the proper type]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: existence and properties of interface prototype object's "constructor" property]
expected:
if (os == "android") and e10s: FAIL
[PushEvent interface: existence and properties of interface object]
expected:
if (os == "android") and e10s: FAIL
[PushMessageData interface: existence and properties of interface object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: existence and properties of interface object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: existence and properties of interface prototype object's @@unscopables property]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: registration.pushManager must inherit property "permissionState(PushSubscriptionOptionsInit)" with the proper type]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface object length]
expected:
if (os == "android") and e10s: FAIL
[Stringification of new PushEvent("type")]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: existence and properties of interface prototype object's "constructor" property]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: existence and properties of interface prototype object]
expected:
if (os == "android") and e10s: FAIL
[PushMessageData interface: operation blob()]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: existence and properties of interface prototype object's @@unscopables property]
expected:
if (os == "android") and e10s: FAIL
[PushEvent interface: existence and properties of interface prototype object's @@unscopables property]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface object name]
expected:
if (os == "android") and e10s: FAIL
[ServiceWorkerRegistration interface: attribute pushManager]
expected:
if (os == "android") and e10s: FAIL
[idlharness.https.any.sharedworker.html]
[PushManager interface object length]
expected: FAIL
@ -97,4 +563,109 @@
expected: FAIL
[PushManager interface: attribute supportedContentEncodings]
expected: FAIL
expected: FAIL
[PushSubscription interface: attribute options]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: operation getKey(PushEncryptionKeyName)]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: existence and properties of interface prototype object's @@unscopables property]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: operation permissionState(PushSubscriptionOptionsInit)]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: operation getSubscription()]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: existence and properties of interface object]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: operation subscribe(PushSubscriptionOptionsInit)]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface object name]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: existence and properties of interface object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: existence and properties of interface prototype object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: operation toJSON()]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: existence and properties of interface prototype object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: operation unsubscribe()]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: attribute applicationServerKey]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: existence and properties of interface prototype object's "constructor" property]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface object name]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: attribute endpoint]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: existence and properties of interface prototype object's "constructor" property]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: existence and properties of interface object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscription interface: existence and properties of interface prototype object's @@unscopables property]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface object length]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: existence and properties of interface prototype object's "constructor" property]
expected:
if (os == "android") and e10s: FAIL
[PushManager interface: existence and properties of interface prototype object]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface: existence and properties of interface prototype object's @@unscopables property]
expected:
if (os == "android") and e10s: FAIL
[PushSubscriptionOptions interface object name]
expected:
if (os == "android") and e10s: FAIL
[ServiceWorkerRegistration interface: attribute pushManager]
expected:
if (os == "android") and e10s: FAIL

Просмотреть файл

@ -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<Base64UtilsSupport> {
public:
static jni::ByteArray::LocalRef Decode(jni::String::Param data) {
FallibleTArray<uint8_t> 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<int8_t> 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__

Просмотреть файл

@ -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();