Bug 1527716 - Update GVE and Documentation to reflect proper handling of Notification permissions. r=geckoview-reviewers,snorp
authorEmily Toop <etoop@mozilla.com>
Mon, 11 Mar 2019 15:35:42 +0000
changeset 521397 0a192c810854
parent 521396 2ec035f862bc
child 521398 f1c6fa74a15e
push id10866
push usernerli@mozilla.com
push dateTue, 12 Mar 2019 18:59:09 +0000
treeherdermozilla-beta@445c24a51727 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgeckoview-reviewers, snorp
bugs1527716
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1527716 - Update GVE and Documentation to reflect proper handling of Notification permissions. r=geckoview-reviewers,snorp Differential Revision: https://phabricator.services.mozilla.com/D21947
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/BasicGeckoViewPrompt.java
mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -3807,33 +3807,38 @@ public class GeckoSession implements Par
     /**
      * GeckoSession applications implement this interface to handle requests for permissions
      * from content, such as geolocation and notifications. For each permission, usually
      * two requests are generated: one request for the Android app permission through
      * requestAppPermissions, which is typically handled by a system permission dialog;
      * and another request for the content permission (e.g. through
      * requestContentPermission), which is typically handled by an app-specific
      * permission dialog.
+     *
+     *
+     * When denying an Android app permission, the response is not stored by GeckoView.
+     * It is the responsibility of the consumer to store the response state and therefore prevent
+     * further requests from being presented to the user.
      **/
     public interface PermissionDelegate {
         @Retention(RetentionPolicy.SOURCE)
         @IntDef({PERMISSION_GEOLOCATION, PERMISSION_DESKTOP_NOTIFICATION})
         /* package */ @interface Permission {}
 
         /**
          * Permission for using the geolocation API.
          * See: https://developer.mozilla.org/en-US/docs/Web/API/Geolocation
          */
-        public static final int PERMISSION_GEOLOCATION = 0;
+        int PERMISSION_GEOLOCATION = 0;
 
         /**
          * Permission for using the notifications API.
          * See: https://developer.mozilla.org/en-US/docs/Web/API/notification
          */
-        public static final int PERMISSION_DESKTOP_NOTIFICATION = 1;
+        int PERMISSION_DESKTOP_NOTIFICATION = 1;
 
         /**
          * Callback interface for notifying the result of a permission request.
          */
         interface Callback {
             /**
              * Called by the implementation after permissions are granted; the
              * implementation must call either grant() or reject() for every request.
--- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/BasicGeckoViewPrompt.java
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/BasicGeckoViewPrompt.java
@@ -804,31 +804,30 @@ final class BasicGeckoViewPrompt impleme
                                     final GeckoSession.PermissionDelegate.Callback callback) {
         final Activity activity = mActivity;
         if (activity == null) {
             callback.reject();
             return;
         }
         final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
         builder.setTitle(title)
-               .setNegativeButton(android.R.string.cancel, /* onClickListener */ null)
+               .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+                   @Override
+                   public void onClick(final DialogInterface dialog, final int which) {
+                       callback.reject();
+                   }
+               })
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(final DialogInterface dialog, final int which) {
                        callback.grant();
                    }
                });
 
         final AlertDialog dialog = builder.create();
-        dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
-                   @Override
-                   public void onDismiss(final DialogInterface dialog) {
-                       callback.reject();
-                   }
-               });
         dialog.show();
     }
 
     private Spinner addMediaSpinner(final Context context, final ViewGroup container,
                                     final MediaSource[] sources, final String[] sourceNames) {
         final ArrayAdapter<MediaSource> adapter = new ArrayAdapter<MediaSource>(
                 context, android.R.layout.simple_spinner_item) {
             private View convertView(final int position, final View view) {
--- 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
@@ -63,16 +63,18 @@ public class GeckoViewActivity extends A
     private GeckoView mGeckoView;
     private boolean mUseMultiprocess;
     private boolean mFullAccessibilityTree;
     private boolean mUseTrackingProtection;
     private boolean mUsePrivateBrowsing;
     private boolean mEnableRemoteDebugging;
     private boolean mKillProcessOnDestroy;
 
+    private boolean mShowNotificationsRejected;
+
     private LocationView mLocationView;
     private String mCurrentUri;
     private boolean mCanGoBack;
     private boolean mCanGoForward;
     private boolean mFullScreen;
 
     private ProgressBar mProgressView;
 
@@ -575,16 +577,35 @@ public class GeckoViewActivity extends A
         }
     }
 
     private class ExamplePermissionDelegate implements GeckoSession.PermissionDelegate {
 
         public int androidPermissionRequestCode = 1;
         private Callback mCallback;
 
+        class ExampleNotificationCallback implements GeckoSession.PermissionDelegate.Callback {
+            private final GeckoSession.PermissionDelegate.Callback mCallback;
+            ExampleNotificationCallback(final GeckoSession.PermissionDelegate.Callback callback) {
+                mCallback = callback;
+            }
+
+            @Override
+            public void reject() {
+                mShowNotificationsRejected = true;
+                mCallback.reject();
+            }
+
+            @Override
+            public void grant() {
+                mShowNotificationsRejected = false;
+                mCallback.grant();
+            }
+        }
+
         public void onRequestPermissionsResult(final String[] permissions,
                                                final int[] grantResults) {
             if (mCallback == null) {
                 return;
             }
 
             final Callback cb = mCallback;
             mCallback = null;
@@ -609,30 +630,37 @@ public class GeckoViewActivity extends A
                 callback.grant();
             }
         }
 
         @Override
         public void onContentPermissionRequest(final GeckoSession session, final String uri,
                                              final int type, final Callback callback) {
             final int resId;
+            Callback contentPermissionCallback = callback;
             if (PERMISSION_GEOLOCATION == type) {
                 resId = R.string.request_geolocation;
             } else if (PERMISSION_DESKTOP_NOTIFICATION == type) {
+                if (mShowNotificationsRejected) {
+                    Log.w(LOGTAG, "Desktop notifications already denied by user.");
+                    callback.reject();
+                    return;
+                }
                 resId = R.string.request_notification;
+                contentPermissionCallback = new ExampleNotificationCallback(callback);
             } else {
                 Log.w(LOGTAG, "Unknown permission: " + type);
                 callback.reject();
                 return;
             }
 
             final String title = getString(resId, Uri.parse(uri).getAuthority());
             final BasicGeckoViewPrompt prompt = (BasicGeckoViewPrompt)
                     mGeckoSession.getPromptDelegate();
-            prompt.onPermissionPrompt(session, title, callback);
+            prompt.onPermissionPrompt(session, title, contentPermissionCallback);
         }
 
         private String[] normalizeMediaName(final MediaSource[] sources) {
             if (sources == null) {
                 return null;
             }
 
             String[] res = new String[sources.length];