tor-browser

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

Permissions.cpp (7485B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/Permissions.h"
      8 
      9 #include "PermissionUtils.h"
     10 #include "mozilla/StaticPrefs_permissions.h"
     11 #include "mozilla/dom/Document.h"
     12 #include "mozilla/dom/MidiPermissionStatus.h"
     13 #include "mozilla/dom/PermissionSetParametersBinding.h"
     14 #include "mozilla/dom/PermissionStatus.h"
     15 #include "mozilla/dom/PermissionsBinding.h"
     16 #include "mozilla/dom/Promise.h"
     17 #include "mozilla/dom/RootedDictionary.h"
     18 #include "mozilla/dom/StorageAccessPermissionStatus.h"
     19 
     20 namespace mozilla::dom {
     21 
     22 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Permissions)
     23  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     24  NS_INTERFACE_MAP_ENTRY(nsISupports)
     25 NS_INTERFACE_MAP_END
     26 
     27 NS_IMPL_CYCLE_COLLECTING_ADDREF(Permissions)
     28 NS_IMPL_CYCLE_COLLECTING_RELEASE(Permissions)
     29 
     30 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(Permissions)
     31 
     32 Permissions::Permissions(nsIGlobalObject* aGlobal)
     33    : GlobalTeardownObserver(aGlobal) {}
     34 
     35 Permissions::~Permissions() = default;
     36 
     37 JSObject* Permissions::WrapObject(JSContext* aCx,
     38                                  JS::Handle<JSObject*> aGivenProto) {
     39  return Permissions_Binding::Wrap(aCx, this, aGivenProto);
     40 }
     41 
     42 namespace {
     43 
     44 // Steps to parse PermissionDescriptor in
     45 // https://w3c.github.io/permissions/#query-method and relevant WebDriver
     46 // commands
     47 RefPtr<PermissionStatus> CreatePermissionStatus(
     48    JSContext* aCx, JS::Handle<JSObject*> aPermissionDesc,
     49    nsIGlobalObject* aGlobal, ErrorResult& aRv) {
     50  // Step 2: Let rootDesc be the object permissionDesc refers to, converted to
     51  // an IDL value of type PermissionDescriptor.
     52  PermissionDescriptor rootDesc;
     53  JS::Rooted<JS::Value> permissionDescValue(
     54      aCx, JS::ObjectOrNullValue(aPermissionDesc));
     55  if (NS_WARN_IF(!rootDesc.Init(aCx, permissionDescValue))) {
     56    // Step 3: If the conversion throws an exception, return a promise rejected
     57    // with that exception.
     58    // Step 4: If rootDesc["name"] is not supported, return a promise rejected
     59    // with a TypeError. (This is done by `enum PermissionName`, as the spec
     60    // note says: "implementers are encouraged to use their own custom enum
     61    // here")
     62    aRv.NoteJSContextException(aCx);
     63    return nullptr;
     64  }
     65 
     66  // Step 5: Let typedDescriptor be the object permissionDesc refers to,
     67  // converted to an IDL value of rootDesc's name's permission descriptor type.
     68  // Step 6: If the conversion throws an exception, return a promise rejected
     69  // with that exception.
     70  // Step 8.1: Let status be create a PermissionStatus with typedDescriptor.
     71  // (The rest is done by the caller)
     72  switch (rootDesc.mName) {
     73    case PermissionName::Midi: {
     74      MidiPermissionDescriptor midiPerm;
     75      if (NS_WARN_IF(!midiPerm.Init(aCx, permissionDescValue))) {
     76        aRv.NoteJSContextException(aCx);
     77        return nullptr;
     78      }
     79 
     80      return new MidiPermissionStatus(aGlobal, midiPerm.mSysex);
     81    }
     82    case PermissionName::Storage_access:
     83      return new StorageAccessPermissionStatus(aGlobal);
     84    case PermissionName::Geolocation:
     85    case PermissionName::Notifications:
     86    case PermissionName::Push:
     87    case PermissionName::Persistent_storage:
     88    case PermissionName::Screen_wake_lock:
     89      return new PermissionStatus(aGlobal, rootDesc.mName);
     90    case PermissionName::Camera:
     91      if (!StaticPrefs::permissions_media_query_enabled()) {
     92        aRv.ThrowTypeError(
     93            "'camera' (value of 'name' member of PermissionDescriptor) is not "
     94            "a valid value for enumeration PermissionName.");
     95        return nullptr;
     96      }
     97      return new PermissionStatus(aGlobal, rootDesc.mName);
     98    case PermissionName::Microphone:
     99      if (!StaticPrefs::permissions_media_query_enabled()) {
    100        aRv.ThrowTypeError(
    101            "'microphone' (value of 'name' member of PermissionDescriptor) is "
    102            "not a valid value for enumeration PermissionName.");
    103        return nullptr;
    104      }
    105      return new PermissionStatus(aGlobal, rootDesc.mName);
    106    default:
    107      MOZ_ASSERT_UNREACHABLE("Unhandled type");
    108      aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
    109      return nullptr;
    110  }
    111 }
    112 
    113 }  // namespace
    114 
    115 // https://w3c.github.io/permissions/#query-method
    116 already_AddRefed<Promise> Permissions::Query(JSContext* aCx,
    117                                             JS::Handle<JSObject*> aPermission,
    118                                             ErrorResult& aRv) {
    119  // Step 1: If this's relevant global object is a Window object, then:
    120  // Step 1.1: If the current settings object's associated Document is not fully
    121  // active, return a promise rejected with an "InvalidStateError" DOMException.
    122 
    123  nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
    124  if (NS_WARN_IF(!global)) {
    125    aRv.ThrowInvalidStateError("The context is not fully active.");
    126    return nullptr;
    127  }
    128 
    129  if (NS_IsMainThread()) {
    130    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
    131    if (!window || !window->IsFullyActive()) {
    132      aRv.ThrowInvalidStateError("The document is not fully active.");
    133      return nullptr;
    134    }
    135  }
    136 
    137  // Step 2 - 6 and 8.1:
    138  RefPtr<PermissionStatus> status =
    139      CreatePermissionStatus(aCx, aPermission, global, aRv);
    140  if (!status) {
    141    return nullptr;
    142  }
    143 
    144  // Step 7: Let promise be a new promise.
    145  RefPtr<Promise> promise = Promise::Create(global, aRv);
    146  if (NS_WARN_IF(aRv.Failed())) {
    147    return nullptr;
    148  }
    149 
    150  // Step 8.2 - 8.3: (Done by the Init method)
    151  // Step 8.4: Queue a global task on the permissions task source with this's
    152  // relevant global object to resolve promise with status.
    153  status->Init()->Then(
    154      GetCurrentSerialEventTarget(), __func__,
    155      [status, promise]() {
    156        promise->MaybeResolve(status);
    157        return;
    158      },
    159      [promise](nsresult aError) {
    160        MOZ_ASSERT(NS_FAILED(aError));
    161        NS_WARNING("Failed PermissionStatus creation");
    162        promise->MaybeReject(aError);
    163        return;
    164      });
    165 
    166  return promise.forget();
    167 }
    168 
    169 already_AddRefed<PermissionStatus> Permissions::ParseSetParameters(
    170    JSContext* aCx, const PermissionSetParameters& aParameters,
    171    ErrorResult& aRv) {
    172  // Step 1: Let parametersDict be the parameters argument, converted to an IDL
    173  // value of type PermissionSetParameters. If this throws an exception,
    174  // return an invalid argument error.
    175  // (Done by IDL layer, and the error type should be handled by the caller)
    176 
    177  // Step 2: If parametersDict.state is an inappropriate permission state for
    178  // any implementation-defined reason, return a invalid argument error.
    179  // (We don't do this)
    180 
    181  // Step 3: Let rootDesc be parametersDict.descriptor.
    182  JS::Rooted<JSObject*> rootDesc(aCx, aParameters.mDescriptor);
    183 
    184  // Step 4: Let typedDescriptor be the object rootDesc refers to, converted
    185  // to an IDL value of rootDesc.name's permission descriptor type. If this
    186  // throws an exception, return a invalid argument error.
    187  //
    188  // We use PermissionStatus as the typed object.
    189  RefPtr<PermissionStatus> status =
    190      CreatePermissionStatus(aCx, rootDesc, nullptr, aRv);
    191  if (aRv.Failed()) {
    192    return nullptr;
    193  }
    194 
    195  // Set the state too so that the caller can use it for step 5.
    196  status->SetState(aParameters.mState);
    197 
    198  return status.forget();
    199 }
    200 
    201 }  // namespace mozilla::dom