tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

permissions.rst (15573B)


      1 .. -*- Mode: rst; fill-column: 80; -*-
      2 
      3 =============================
      4 Working with Site Permissions
      5 =============================
      6 
      7 When a website wants to access certain services on a user’s device, it
      8 will send out a permissions request. This document will explain how to
      9 use GeckoView to receive those requests, and respond to them by granting
     10 or denying those permissions.
     11 
     12 .. contents:: :local:
     13 
     14 The Permission Delegate
     15 -----------------------
     16 
     17 The way an app interacts with site permissions in GeckoView is through
     18 the
     19 `PermissionDelegate <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html>`_.
     20 There are three broad categories of permission that the
     21 ``PermissionDelegate`` handles, Android Permissions, Content Permissions
     22 and Media Permissions. All site permissions handled by GeckoView fall
     23 into one of these three categories.
     24 
     25 To get notified about permission requests, you need to implement the
     26 ``PermissionDelegate`` interface:
     27 
     28 .. code:: java
     29 
     30   private class ExamplePermissionDelegate implements GeckoSession.PermissionDelegate {
     31       @Override
     32       public void onAndroidPermissionsRequest(final GeckoSession session,
     33                                               final String[] permissions,
     34                                               final Callback callback) { }
     35 
     36       @Override
     37       public void onContentPermissionRequest(final GeckoSession session,
     38                                              final String uri,
     39                                              final int type, final Callback callback) { }
     40 
     41       @Override
     42       public void onMediaPermissionRequest(final GeckoSession session,
     43                                            final String uri,
     44                                            final MediaSource[] video,
     45                                            final MediaSource[] audio,
     46                                            final MediaCallback callback) { }
     47   }
     48 
     49 You will then need to register the delegate with your
     50 `GeckoSession <https://mozilla.github.io/geckoview/https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.html>`_
     51 instance.
     52 
     53 .. code:: java
     54 
     55   public class GeckoViewActivity extends AppCompatActivity {
     56       @Override
     57       protected void onCreate(Bundle savedInstanceState) {
     58           super.onCreate(savedInstanceState);
     59 
     60           ...
     61 
     62           final ExamplePermissionDelegate permission = new ExamplePermissionDelegate();
     63           session.setPermissionDelegate(permission);
     64 
     65           ...
     66       }
     67   }
     68 
     69 Android Permissions
     70 ~~~~~~~~~~~~~~~~~~~
     71 
     72 Android permissions are requested whenever a site wants access to a
     73 device’s navigation or input capabilities.
     74 
     75 The user will often need to grant these Android permissions to the app
     76 alongside granting the Content or Media site permissions.
     77 
     78 When you receive an
     79 `onAndroidPermissionsRequest <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#onAndroidPermissionsRequest(org.mozilla.geckoview.GeckoSession,java.lang.String[],org.mozilla.geckoview.GeckoSession.PermissionDelegate.Callback)>`_
     80 call, you will also receive the ``GeckoSession`` the request was sent
     81 from, an array containing the permissions that are being requested, and
     82 a
     83 `Callback`_
     84 to respond to the request. It is then up to the app to request those
     85 permissions from the device, which can be done using
     86 `requestPermissions <https://developer.android.com/reference/android/app/Activity#requestPermissions(java.lang.String%5B%5D,%2520int)>`_.
     87 
     88 Possible ``permissions`` values are:
     89 `ACCESS_COARSE_LOCATION <https://developer.android.com/reference/android/Manifest.permission.html#ACCESS_COARSE_LOCATION>`_,
     90 `ACCESS_FINE_LOCATION <https://developer.android.com/reference/android/Manifest.permission.html#ACCESS_FINE_LOCATION>`_,
     91 `CAMERA <https://developer.android.com/reference/android/Manifest.permission.html#CAMERA>`_
     92 or
     93 `RECORD_AUDIO <https://developer.android.com/reference/android/Manifest.permission.html#RECORD_AUDIO>`_.
     94 
     95 .. code:: java
     96 
     97   private class ExamplePermissionDelegate implements GeckoSession.PermissionDelegate {
     98       private Callback mCallback;
     99 
    100       public void onRequestPermissionsResult(final String[] permissions,
    101                                              final int[] grantResults) {
    102           if (mCallback == null) { return; }
    103 
    104           final Callback cb = mCallback;
    105           mCallback = null;
    106           for (final int result : grantResults) {
    107               if (result != PackageManager.PERMISSION_GRANTED) {
    108                   // At least one permission was not granted.
    109                   cb.reject();
    110                   return;
    111               }
    112           }
    113           cb.grant();
    114       }
    115 
    116       @Override
    117       public void onAndroidPermissionsRequest(final GeckoSession session,
    118                                               final String[] permissions,
    119                                               final Callback callback) {
    120           mCallback = callback;
    121           requestPermissions(permissions, androidPermissionRequestCode);
    122       }
    123   }
    124 
    125   public class GeckoViewActivity extends AppCompatActivity {
    126       @Override
    127       public void onRequestPermissionsResult(final int requestCode,
    128                                              final String[] permissions,
    129                                              final int[] grantResults) {
    130           if (requestCode == REQUEST_PERMISSIONS ||
    131               requestCode == REQUEST_WRITE_EXTERNAL_STORAGE) {
    132               final ExamplePermissionDelegate permission = (ExamplePermissionDelegate)
    133                       getCurrentSession().getPermissionDelegate();
    134               permission.onRequestPermissionsResult(permissions, grantResults);
    135           } else {
    136               super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    137           }
    138       }
    139   }
    140 
    141 Content Permissions
    142 ~~~~~~~~~~~~~~~~~~~
    143 
    144 Content permissions are requested whenever a site wants access to
    145 content that is stored on the device. The content permissions that can
    146 be requested through GeckoView are:
    147 `Geolocation <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#PERMISSION_GEOLOCATION>`_,
    148 `Site Notifications <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#PERMISSION_DESKTOP_NOTIFICATION>`_,
    149 `Persistent Storage <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#PERMISSION_PERSISTENT_STORAGE>`_,
    150 `XR <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#PERMISSION_XR>`_,
    151 `Autoplay Inaudible <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#PERMISSION_AUTOPLAY_INAUDIBLE>`_,
    152 `Autoplay Audible <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#PERMISSION_AUTOPLAY_AUDIBLE>`_,
    153 and
    154 `DRM Media access <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#PERMISSION_MEDIA_KEY_SYSTEM_ACCESS>`_.
    155 Additionally, `tracking protection exceptions <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#PERMISSION_TRACKING>`_
    156 are treated as a type of content permission.
    157 
    158 When you receive an
    159 `onContentPermissionRequest <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#onContentPermissionRequest(org.mozilla.geckoview.GeckoSession,org.mozilla.geckoview.GeckoSession.PermissionDelegate.ContentPermission)>`_
    160 call, you will also receive the ``GeckoSession`` the request was sent
    161 from, and all relevant information about the permission being requested
    162 stored in a `ContentPermission <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.ContentPermission.html>`_.
    163 It is then up to the app to present UI to the user asking for the
    164 permissions, and to notify GeckoView of the response via the returned
    165 ``GeckoResult``.
    166 
    167 Once a permission has been set in this fashion, GeckoView will persist it
    168 across sessions until it is cleared or modified. When a page is loaded,
    169 the active permissions associated with it (both allowed and denied) will
    170 be reported in `onLocationChange <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.NavigationDelegate.html#onLocationChange(org.mozilla.geckoview.GeckoSession,java.lang.String,java.util.List)>`_
    171 as a list of ``ContentPermission`` objects; additionally, one may check all stored
    172 content permissions by calling `getAllPermissions <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/StorageController.html#getAllPermissions()>`_
    173 and the content permissions associated with a given URI by calling
    174 `getPermissions <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/StorageController.html#getPermissions(java.lang.String,java.lang.String)>`_.
    175 In order to modify an existing permission, you will need the associated
    176 ``ContentPermission`` (which can be retrieved from any of the above methods);
    177 then, call `setPermission <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/StorageController.html#setPermission(org.mozilla.geckoview.GeckoSession.PermissionDelegate.ContentPermission,int)>`_
    178 with the desired new value, or `VALUE_PROMPT <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.ContentPermission.html#VALUE_PROMPT>`_
    179 if you wish to unset the permission and let the site request it again in the future.
    180 
    181 Media Permissions
    182 ~~~~~~~~~~~~~~~~~
    183 
    184 Media permissions are requested whenever a site wants access to play or
    185 record media from the device’s camera and microphone.
    186 
    187 When you receive an
    188 `onMediaPermissionRequest <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.html#onMediaPermissionRequest(org.mozilla.geckoview.GeckoSession,java.lang.String,org.mozilla.geckoview.GeckoSession.PermissionDelegate.MediaSource[],org.mozilla.geckoview.GeckoSession.PermissionDelegate.MediaSource[],org.mozilla.geckoview.GeckoSession.PermissionDelegate.MediaCallback)>`_
    189 call, you will also receive the ``GeckoSession`` the request was sent
    190 from, the URI of the site that requested the permission, as a String,
    191 the list of video devices available, if requesting video, the list of
    192 audio devices available, if requesting audio, and a
    193 `MediaCallback <https://searchfox.org/mozilla-central/source/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java#686>`_
    194 to respond to the request.
    195 
    196 It is up to the app to present UI to the user asking for the
    197 permissions, and to notify GeckoView of the response via the
    198 ``MediaCallback``.
    199 
    200 *Please note, media permissions will still be requested if the
    201 associated device permissions have been denied if there are video or
    202 audio sources in that category that can still be accessed when listed.
    203 It is the responsibility of consumers to ensure that media permission
    204 requests are not displayed in this case.*
    205 
    206 .. code:: java
    207 
    208   private class ExamplePermissionDelegate implements GeckoSession.PermissionDelegate {
    209       @Override
    210       public void onMediaPermissionRequest(final GeckoSession session,
    211                                            final String uri,
    212                                            final MediaSource[] video,
    213                                            final MediaSource[] audio,
    214                                            final MediaCallback callback) {
    215           // Reject permission if Android permission has been previously denied.
    216           if ((audio != null
    217                   && ContextCompat.checkSelfPermission(GeckoViewActivity.this,
    218                       Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED)
    219               || (video != null
    220                   && ContextCompat.checkSelfPermission(GeckoViewActivity.this,
    221                       Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)) {
    222               callback.reject();
    223               return;
    224           }
    225 
    226           final String host = Uri.parse(uri).getAuthority();
    227           final String title;
    228           if (audio == null) {
    229               title = getString(R.string.request_video, host);
    230           } else if (video == null) {
    231               title = getString(R.string.request_audio, host);
    232           } else {
    233               title = getString(R.string.request_media, host);
    234           }
    235 
    236           // Get the media device name from the `MediaDevice`
    237           String[] videoNames = normalizeMediaName(video);
    238           String[] audioNames = normalizeMediaName(audio);
    239 
    240           final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
    241 
    242           // Create drop down boxes to allow users to select which device to grant permission to
    243           final LinearLayout container = addStandardLayout(builder, title, null);
    244           final Spinner videoSpinner;
    245           if (video != null) {
    246               videoSpinner = addMediaSpinner(builder.getContext(), container, video, videoNames); // create spinner and add to alert UI
    247           } else {
    248               videoSpinner = null;
    249           }
    250 
    251           final Spinner audioSpinner;
    252           if (audio != null) {
    253               audioSpinner = addMediaSpinner(builder.getContext(), container, audio, audioNames); // create spinner and add to alert UI
    254           } else {
    255               audioSpinner = null;
    256           }
    257 
    258           builder.setNegativeButton(android.R.string.cancel, null)
    259                  .setPositiveButton(android.R.string.ok,
    260                                     new DialogInterface.OnClickListener() {
    261                       @Override
    262                       public void onClick(final DialogInterface dialog, final int which) {
    263                           // gather selected media devices and grant access
    264                           final MediaSource video = (videoSpinner != null)
    265                                   ? (MediaSource) videoSpinner.getSelectedItem() : null;
    266                           final MediaSource audio = (audioSpinner != null)
    267                                   ? (MediaSource) audioSpinner.getSelectedItem() : null;
    268                           callback.grant(video, audio);
    269                       }
    270                   });
    271 
    272           final AlertDialog dialog = builder.create();
    273           dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
    274                       @Override
    275                       public void onDismiss(final DialogInterface dialog) {
    276                           callback.reject();
    277                       }
    278                   });
    279           dialog.show();
    280       }
    281   }
    282 
    283 To see the ``PermissionsDelegate`` in action, you can find the full
    284 example implementation in the `GeckoView example
    285 app <https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.MediaCallback.html>`_.
    286 
    287 .. _Callback: https://mozilla.github.io/geckoview/javadoc/mozilla-central/org/mozilla/geckoview/GeckoSession.PermissionDelegate.Callback.html