Bug 1533057 - Created Web Notifications API. r=geckoview-reviewers,agi,snorp

Differential Revision: https://phabricator.services.mozilla.com/D36342

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Alvina Waseem 2019-08-21 16:49:49 +00:00
parent 9f88239431
commit 32e325ac2f
11 changed files with 487 additions and 10 deletions

View File

@ -263,6 +263,12 @@ GeckoViewPermission.prototype = {
})
.then(granted => {
(granted ? aRequest.allow : aRequest.cancel)();
Services.perms.addFromPrincipal(
aRequest.principal,
"desktop-notification",
Services.perms.ALLOW_ACTION,
Services.perms.EXPIRE_SESSION
);
// Manually release the target request here to facilitate garbage collection.
aRequest = undefined;
});

View File

@ -82,6 +82,8 @@ import org.mozilla.geckoview.WebExtension;
import org.mozilla.geckoview.WebExtensionController;
import org.mozilla.geckoview.WebExtensionEventDispatcher;
import org.mozilla.geckoview.WebMessage;
import org.mozilla.geckoview.WebNotification;
import org.mozilla.geckoview.WebNotificationDelegate;
import org.mozilla.geckoview.WebRequest;
import org.mozilla.geckoview.WebRequestError;
import org.mozilla.geckoview.WebResponse;
@ -298,7 +300,6 @@ package org.mozilla.geckoview {
}
public final class GeckoRuntime implements Parcelable {
ctor public GeckoRuntime();
method @UiThread public void attachTo(@NonNull Context);
method @UiThread public void configurationChanged(@NonNull Configuration);
method @UiThread @NonNull public static GeckoRuntime create(@NonNull Context);
@ -310,11 +311,13 @@ package org.mozilla.geckoview {
method @UiThread @NonNull public StorageController getStorageController();
method @UiThread @NonNull public RuntimeTelemetry getTelemetry();
method @UiThread @NonNull public WebExtensionController getWebExtensionController();
method @UiThread @Nullable public WebNotificationDelegate getWebNotificationDelegate();
method @UiThread public void orientationChanged();
method @UiThread public void orientationChanged(int);
method @AnyThread public void readFromParcel(@NonNull Parcel);
method @UiThread @NonNull public GeckoResult<Void> registerWebExtension(@NonNull WebExtension);
method @UiThread public void setDelegate(@Nullable GeckoRuntime.Delegate);
method @UiThread public void setWebNotificationDelegate(@Nullable WebNotificationDelegate);
method @AnyThread public void shutdown();
method @UiThread @NonNull public GeckoResult<Void> unregisterWebExtension(@NonNull WebExtension);
field public static final String ACTION_CRASHED = "org.mozilla.gecko.ACTION_CRASHED";
@ -1242,6 +1245,23 @@ package org.mozilla.geckoview {
method @NonNull public WebMessage.Builder uri(@NonNull String);
}
public class WebNotification {
method @UiThread public void click();
method @UiThread public void dismiss();
field @Nullable public final String imageUrl;
field @Nullable public final String lang;
field @NonNull public final boolean requireInteraction;
field @NonNull public final String tag;
field @Nullable public final String text;
field @Nullable public final String textDirection;
field @Nullable public final String title;
}
public interface WebNotificationDelegate {
method @AnyThread default public void onCloseNotification(@NonNull WebNotification);
method @AnyThread default public void onShowNotification(@NonNull WebNotification);
}
@AnyThread public class WebRequest extends WebMessage {
ctor public WebRequest(@NonNull String);
field public static final int CACHE_MODE_DEFAULT = 1;

View File

@ -0,0 +1,140 @@
package org.mozilla.geckoview.test
import android.support.test.filters.MediumTest
import android.support.test.runner.AndroidJUnit4
import org.hamcrest.Matchers.*
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.geckoview.GeckoResult
import org.mozilla.geckoview.WebNotification
import org.mozilla.geckoview.WebNotificationDelegate
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule
@RunWith(AndroidJUnit4::class)
@MediumTest
class WebNotificationTest : BaseSessionTest() {
@Before fun setup() {
mainSession.loadTestPath(HELLO_HTML_PATH)
mainSession.waitForPageStop()
val result = mainSession.waitForJS("Notification.requestPermission()")
assertThat("Permission should be granted",
result as String, equalTo("granted"))
}
@Test fun onShowNotification() {
val runtime = sessionRule.runtime
val notificationResult = GeckoResult<Void>()
val register = { delegate: WebNotificationDelegate -> runtime.webNotificationDelegate = delegate}
val unregister = { _: WebNotificationDelegate -> runtime.webNotificationDelegate = null }
sessionRule.addExternalDelegateDuringNextWait(WebNotificationDelegate::class, register,
unregister, object : WebNotificationDelegate {
@GeckoSessionTestRule.AssertCalled
override fun onShowNotification(notification: WebNotification) {
assertThat("Title should match", notification.title, equalTo("The Title"))
assertThat("Body should match", notification.text, equalTo("The Text"))
assertThat("Tag should match", notification.tag, endsWith("Tag"))
assertThat("ImageUrl should match", notification.imageUrl, endsWith("icon.png"))
assertThat("Language should match", notification.lang, equalTo("en-US"))
assertThat("Direction should match", notification.textDirection, equalTo("ltr"))
assertThat("Require Interaction should match", notification.requireInteraction, equalTo(true))
notificationResult.complete(null)
}
})
mainSession.evaluateJS("""
new Notification('The Title', { body: 'The Text', cookie: 'Cookie',
icon: 'icon.png', tag: 'Tag', dir: 'ltr', lang: 'en-US',
requireInteraction: true });
""".trimIndent())
sessionRule.waitForResult(notificationResult)
}
@Test fun onCloseNotification() {
val runtime = sessionRule.runtime
val closeCalled = GeckoResult<Void>()
val register = { delegate: WebNotificationDelegate -> runtime.webNotificationDelegate = delegate}
val unregister = { _: WebNotificationDelegate -> runtime.webNotificationDelegate = null }
sessionRule.addExternalDelegateDuringNextWait(WebNotificationDelegate::class, register,
unregister, object : WebNotificationDelegate {
@GeckoSessionTestRule.AssertCalled
override fun onCloseNotification(notification: WebNotification) {
closeCalled.complete(null)
}
})
mainSession.evaluateJS("""
const notification = new Notification('The Title', { body: 'The Text'});
notification.close();
""".trimIndent())
sessionRule.waitForResult(closeCalled)
}
@Test fun clickNotification() {
val runtime = sessionRule.runtime
val notificationResult = GeckoResult<Void>()
val register = { delegate: WebNotificationDelegate -> runtime.webNotificationDelegate = delegate}
val unregister = { _: WebNotificationDelegate -> runtime.webNotificationDelegate = null }
var notificationShown: WebNotification? = null
sessionRule.addExternalDelegateDuringNextWait(WebNotificationDelegate::class, register,
unregister, object : WebNotificationDelegate {
@GeckoSessionTestRule.AssertCalled
override fun onShowNotification(notification: WebNotification) {
notificationShown = notification
notificationResult.complete(null)
}
})
val promiseResult = mainSession.evaluatePromiseJS("""
new Promise(resolve => {
const notification = new Notification('The Title', { body: 'The Text' });
notification.onclick = function() {
resolve(1);
}
});
""".trimIndent())
sessionRule.waitForResult(notificationResult)
notificationShown!!.click()
assertThat("Promise should have been resolved.", promiseResult.value as Double, equalTo(1.0))
}
@Test fun dismissNotification() {
val runtime = sessionRule.runtime
val notificationResult = GeckoResult<Void>()
val register = { delegate: WebNotificationDelegate -> runtime.webNotificationDelegate = delegate}
val unregister = { _: WebNotificationDelegate -> runtime.webNotificationDelegate = null }
var notificationShown: WebNotification? = null
sessionRule.addExternalDelegateDuringNextWait(WebNotificationDelegate::class, register,
unregister, object : WebNotificationDelegate {
@GeckoSessionTestRule.AssertCalled
override fun onShowNotification(notification: WebNotification) {
notificationShown = notification
notificationResult.complete(null)
}
})
val promiseResult = mainSession.evaluatePromiseJS("""
new Promise(resolve => {
const notification = new Notification('The Title', { body: 'The Text'});
notification.onclose = function() {
resolve(1);
}
});
""".trimIndent())
sessionRule.waitForResult(notificationResult)
notificationShown!!.dismiss()
assertThat("Promise should have been resolved", promiseResult.value as Double, equalTo(1.0))
}
}

View File

@ -35,6 +35,7 @@ import org.mozilla.gecko.GeckoScreenOrientation;
import org.mozilla.gecko.GeckoSystemStateListener;
import org.mozilla.gecko.GeckoThread;
import org.mozilla.gecko.PrefsHelper;
import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.ContextUtils;
import org.mozilla.gecko.util.DebugConfig;
@ -156,16 +157,28 @@ public final class GeckoRuntime implements Parcelable {
return sDefaultRuntime;
}
private static GeckoRuntime sRuntime;
private GeckoRuntimeSettings mSettings;
private Delegate mDelegate;
private WebNotificationDelegate mNotificationDelegate;
private RuntimeTelemetry mTelemetry;
private final WebExtensionEventDispatcher mWebExtensionDispatcher;
private StorageController mStorageController;
private final WebExtensionController mWebExtensionController;
public GeckoRuntime() {
private GeckoRuntime() {
mWebExtensionDispatcher = new WebExtensionEventDispatcher();
mWebExtensionController = new WebExtensionController(this, mWebExtensionDispatcher);
if (sRuntime != null) {
throw new IllegalStateException("Only one GeckoRuntime instance is allowed");
}
sRuntime = this;
}
@WrapForJNI
@UiThread
private @Nullable static GeckoRuntime getInstance() {
return sRuntime;
}
/**
@ -503,6 +516,47 @@ public final class GeckoRuntime implements Parcelable {
return mDelegate;
}
/**
* Sets the delegate to be used for handling Web Notifications.
*
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Notification">Web Notifications</a>
*/
@UiThread
public void setWebNotificationDelegate(final @Nullable WebNotificationDelegate delegate) {
mNotificationDelegate = delegate;
}
/**
* Returns the current WebNotificationDelegate, if any
*
* @return an instance of WebNotificationDelegate or null if no delegate has been set
*/
@WrapForJNI
@UiThread
public @Nullable WebNotificationDelegate getWebNotificationDelegate() {
return mNotificationDelegate;
}
@WrapForJNI
@UiThread
private void notifyOnShow(final WebNotification notification) {
ThreadUtils.getUiHandler().post(() -> {
if (mNotificationDelegate != null) {
mNotificationDelegate.onShowNotification(notification);
}
});
}
@WrapForJNI
@UiThread
private void notifyOnClose(final WebNotification notification) {
ThreadUtils.getUiHandler().post(() -> {
if (mNotificationDelegate != null) {
mNotificationDelegate.onCloseNotification(notification);
}
});
}
@AnyThread
public @NonNull GeckoRuntimeSettings getSettings() {
return mSettings;

View File

@ -0,0 +1,99 @@
package org.mozilla.geckoview;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.util.ThreadUtils;
public class WebNotification {
/**
* Title is shown at the top of the notification window.
*
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Notification/title">Web Notification - title</a>
*/
public final @Nullable String title;
/**
* Tag is the ID of the notification.
*
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Notification/tag">Web Notification - tag</a>
*/
public final @NonNull String tag;
private final @Nullable String mCookie;
/**
* Text represents the body of the notification.
*
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Notification/body">Web Notification - text</a>
*/
public final @Nullable String text;
/**
* ImageURL contains the URL of an icon to be displayed as part of the notification.
*
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Notification/icon">Web Notification - icon</a>
*/
public final @Nullable String imageUrl;
/**
* TextDirection indicates the direction that the language of the text is displayed.
* Possible values are:
* auto: adopts the browser's language setting behaviour (the default.)
* ltr: left to right.
* rtl: right to left.
*
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Notification/dir">Web Notification - dir</a>
*/
public final @Nullable String textDirection;
/**
* Lang indicates the notification's language, as specified using a DOMString
* representing a BCP 47 language tag.
*
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/DOMString">DOM String</a>
* @see <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">BCP 47</a>
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Notification/lang">Web Notification - lang</a>
*/
public final @Nullable String lang;
/**
* RequireInteraction indicates whether a notification should remain active until the user
* clicks or dismisses it, rather than closing automatically.
*
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/Notification/requireInteraction">Web Notification - requireInteraction</a>
*/
public final @NonNull boolean requireInteraction;
@WrapForJNI
/* package */ WebNotification(@Nullable final String title, @NonNull final String tag,
@Nullable final String cookie, @Nullable final String text,
@Nullable final String imageUrl, @Nullable final String textDirection,
@Nullable final String lang, @NonNull final boolean requireInteraction) {
this.tag = tag;
this.mCookie = cookie;
this.title = title;
this.text = text;
this.imageUrl = imageUrl;
this.textDirection = textDirection;
this.lang = lang;
this.requireInteraction = requireInteraction;
}
@UiThread
public void click() {
ThreadUtils.assertOnUiThread();
GeckoAppShell.onNotificationClick(tag, mCookie);
}
@UiThread
public void dismiss() {
ThreadUtils.assertOnUiThread();
GeckoAppShell.onNotificationClose(tag, mCookie);
}
}

View File

@ -0,0 +1,27 @@
package org.mozilla.geckoview;
import android.support.annotation.AnyThread;
import android.support.annotation.NonNull;
import org.mozilla.gecko.annotation.WrapForJNI;
public interface WebNotificationDelegate {
/**
* This is called when a new notification is created.
*
* @param notification The WebNotification received.
*/
@AnyThread
@WrapForJNI
default void onShowNotification(@NonNull WebNotification notification) {}
/**
* This is called when an existing notification is closed.
*
* @param notification The WebNotification received.
*/
@AnyThread
@WrapForJNI
default void onCloseNotification(@NonNull WebNotification notification) {}
}

View File

@ -44,6 +44,19 @@ exclude: true
([bug 1567268]({{bugzilla}}1567268))
- Added API for controlling Gecko logging [`GeckoRuntimeSettings.debugLogging`][70.14]
([bug 1573304]({{bugzilla}}1573304))
- Added API for session context assignment
[`GeckoSessionSettings.Builder.contextId`][70.1] and deletion of data
related to a session context
[`StorageController.clearDataForSessionContext`][70.2].
- Removed `setSession(session, runtime)` from [`GeckoView`][70.5]. With this change, `GeckoView` will no longer
manage opening/closing of the [`GeckoSession`][70.6] and instead leave that up to the app. It's also now allowed
to call [`setSession`][70.10] with a closed `GeckoSession`.
- Added an overload of [`GeckoSession.loadUri()`][70.8] that accepts a referring [`GeckoSession`][70.6]. This should be used
when the URI we're loading originates from another page. A common example of this would be long pressing
a link and then opening that in a new `GeckoSession`.
- Added capture parameter to [`onFilePrompt`][70.9] and corresponding [`CAPTURE_TYPE_*`][70.7] constants.
- Added [`WebNotification`][70.11] and [`WebNotificationDelegate`][70.12] for handling Web Notifications.
([bug 1533057]({{bugzilla}}1533057))
[70.1]: {{javadoc_uri}}/GeckoSessionSettings.Builder.html#contextId-java.lang.String-
[70.2]: {{javadoc_uri}}/StorageController.html#clearDataForSessionContext-java.lang.String-
@ -59,6 +72,8 @@ exclude: true
[70.12]: {{javadoc_uri}}/RuntimeTelemetry.Delegate.html
[70.13]: {{javadoc_uri}}/ContentBlocking.html
[70.14]: {{javadoc_uri}}/GeckoRuntimeSettings.Builder.html#debugLogging-boolean-
[70.11]: {{javadoc_uri}}/WebNotification.html
[70.12]: {{javadoc_uri}}/WebNotificationDelegate.html
## v69
- Modified behavior of ['setAutomaticFontSizeAdjustment'][69.1] so that it no
@ -311,4 +326,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]: 99204a5c93667e6d440e55d5d330d01cf4e8783f
[api-version]: 15b1503a237289c51c147c7760afd7ff726d8809

View File

@ -16,20 +16,29 @@ import org.mozilla.geckoview.GeckoRuntimeSettings;
import org.mozilla.geckoview.GeckoSession;
import org.mozilla.geckoview.GeckoSessionSettings;
import org.mozilla.geckoview.GeckoView;
import org.mozilla.geckoview.GeckoWebExecutor;
import org.mozilla.geckoview.WebExtension;
import org.mozilla.geckoview.WebExtensionController;
import org.mozilla.geckoview.WebNotification;
import org.mozilla.geckoview.WebNotificationDelegate;
import org.mozilla.geckoview.WebRequest;
import org.mozilla.geckoview.WebRequestError;
import org.mozilla.geckoview.RuntimeTelemetry;
import org.mozilla.geckoview.WebResponse;
import android.Manifest;
import android.app.Activity;
import android.app.DownloadManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@ -55,6 +64,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Locale;
@ -90,6 +100,10 @@ public class GeckoViewActivity extends AppCompatActivity {
private boolean mCanGoForward;
private boolean mFullScreen;
private HashMap<String, Integer> mNotificationIDMap = new HashMap<>();
private HashMap<Integer, WebNotification> mNotificationMap = new HashMap<>();
private int mLastID = 100;
private ProgressBar mProgressView;
private LinkedList<GeckoSession.WebResponseInfo> mPendingDownloads = new LinkedList<>();
@ -175,6 +189,58 @@ public class GeckoViewActivity extends AppCompatActivity {
return GeckoResult.fromValue(AllowOrDeny.ALLOW);
}
});
// `getSystemService` call requires API level 23
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
sGeckoRuntime.setWebNotificationDelegate(new WebNotificationDelegate() {
NotificationManager notificationManager = getSystemService(NotificationManager.class);
@Override
public void onShowNotification(@NonNull WebNotification notification) {
Intent clickIntent = new Intent(GeckoViewActivity.this, GeckoViewActivity.class);
clickIntent.putExtra("onClick",notification.tag);
PendingIntent dismissIntent = PendingIntent.getActivity(GeckoViewActivity.this, mLastID, clickIntent, 0);
Notification.Builder builder = new Notification.Builder(GeckoViewActivity.this)
.setContentTitle(notification.title)
.setContentText(notification.text)
.setSmallIcon(R.drawable.ic_status_logo)
.setContentIntent(dismissIntent)
.setAutoCancel(true);
mNotificationIDMap.put(notification.tag, mLastID);
mNotificationMap.put(mLastID, notification);
if (notification.imageUrl != null && notification.imageUrl.length() > 0) {
final GeckoWebExecutor executor = new GeckoWebExecutor(sGeckoRuntime);
GeckoResult<WebResponse> response = executor.fetch(
new WebRequest.Builder(notification.imageUrl)
.addHeader("Accept", "image")
.build());
response.accept(value -> {
Bitmap bitmap = BitmapFactory.decodeStream(value.body);
builder.setLargeIcon(Icon.createWithBitmap(bitmap));
notificationManager.notify(mLastID++, builder.build());
});
} else {
notificationManager.notify(mLastID++, builder.build());
}
}
@Override
public void onCloseNotification(@NonNull WebNotification notification) {
if (mNotificationIDMap.containsKey(notification.tag)) {
int id = mNotificationIDMap.get(notification.tag);
notificationManager.cancel(id);
mNotificationMap.remove(id);
mNotificationIDMap.remove(notification.tag);
}
}
});
}
}
if(savedInstanceState == null) {
@ -430,6 +496,7 @@ public class GeckoViewActivity extends AppCompatActivity {
super.onDestroy();
}
@Override
protected void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
@ -442,6 +509,15 @@ public class GeckoViewActivity extends AppCompatActivity {
return;
}
if (intent.hasExtra("onClick")) {
int key = intent.getExtras().getInt("onClick");
WebNotification notification = mNotificationMap.get(key);
if (notification != null) {
notification.click();
mNotificationMap.remove(key);
}
}
setIntent(intent);
if (intent.getData() != null) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

View File

@ -13,6 +13,8 @@ namespace widget {
NS_IMPL_ISUPPORTS(AndroidAlerts, nsIAlertsService)
StaticAutoPtr<AndroidAlerts::ListenerMap> AndroidAlerts::sListenerMap;
nsDataHashtable<nsStringHashKey, java::WebNotification::GlobalRef>
AndroidAlerts::mNotificationsMap;
NS_IMETHODIMP
AndroidAlerts::ShowAlertNotification(
@ -61,6 +63,18 @@ AndroidAlerts::ShowPersistentNotification(const nsAString& aPersistentData,
rv = aAlert->GetName(name);
NS_ENSURE_SUCCESS(rv, NS_OK);
nsAutoString lang;
rv = aAlert->GetLang(lang);
NS_ENSURE_SUCCESS(rv, NS_OK);
nsAutoString dir;
rv = aAlert->GetDir(dir);
NS_ENSURE_SUCCESS(rv, NS_OK);
bool requireInteraction;
rv = aAlert->GetRequireInteraction(&requireInteraction);
NS_ENSURE_SUCCESS(rv, NS_OK);
nsCOMPtr<nsIPrincipal> principal;
rv = aAlert->GetPrincipal(getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, NS_OK);
@ -76,19 +90,41 @@ AndroidAlerts::ShowPersistentNotification(const nsAString& aPersistentData,
sListenerMap->Put(name, aAlertListener);
}
java::GeckoAppShell::ShowNotification(
name, cookie, title, text, host, imageUrl,
!aPersistentData.IsEmpty() ? jni::StringParam(aPersistentData)
: jni::StringParam(nullptr));
if (jni::IsFennec()) {
java::GeckoAppShell::ShowNotification(
name, cookie, title, text, host, imageUrl,
!aPersistentData.IsEmpty() ? jni::StringParam(aPersistentData)
: jni::StringParam(nullptr));
} else {
java::WebNotification::LocalRef notification = notification->New(
title, name, cookie, text, imageUrl, dir, lang, requireInteraction);
java::GeckoRuntime::LocalRef runtime = java::GeckoRuntime::GetInstance();
if (runtime != NULL) {
runtime->NotifyOnShow(notification);
}
mNotificationsMap.Put(name, notification);
}
return NS_OK;
}
NS_IMETHODIMP
AndroidAlerts::CloseAlert(const nsAString& aAlertName,
nsIPrincipal* aPrincipal) {
// We delete the entry in sListenerMap later, when CloseNotification calls
// NotifyListener.
java::GeckoAppShell::CloseNotification(aAlertName);
if (jni::IsFennec()) {
// We delete the entry in sListenerMap later, when CloseNotification calls
// NotifyListener.
java::GeckoAppShell::CloseNotification(aAlertName);
} else {
java::WebNotification::LocalRef notification =
mNotificationsMap.Get(aAlertName);
java::GeckoRuntime::LocalRef runtime = java::GeckoRuntime::GetInstance();
if (runtime != NULL) {
runtime->NotifyOnClose(notification);
}
mNotificationsMap.Remove(aAlertName);
}
return NS_OK;
}
@ -107,6 +143,7 @@ void AndroidAlerts::NotifyListener(const nsAString& aName, const char* aTopic,
if (NS_LITERAL_CSTRING("alertfinished").Equals(aTopic)) {
sListenerMap->Remove(aName);
mNotificationsMap.Remove(aName);
}
}

View File

@ -27,6 +27,9 @@ class AndroidAlerts : public nsIAlertsService {
static void NotifyListener(const nsAString& aName, const char* aTopic,
const char16_t* aCookie);
static nsDataHashtable<nsStringHashKey, java::WebNotification::GlobalRef>
mNotificationsMap;
protected:
virtual ~AndroidAlerts() { sListenerMap = nullptr; }