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 "mozilla/dom/ContentChild.h"
10 #include "mozilla/dom/Document.h"
11 #include "mozilla/dom/PermissionMessageUtils.h"
12 #include "mozilla/dom/PermissionsBinding.h"
13 #include "mozilla/dom/PermissionStatus.h"
14 #include "mozilla/dom/Promise.h"
15 #include "mozilla/Components.h"
16 #include "nsIPermissionManager.h"
17 #include "PermissionUtils.h"
18 
19 namespace mozilla::dom {
20 
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Permissions)21 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Permissions)
22   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
23   NS_INTERFACE_MAP_ENTRY(nsISupports)
24 NS_INTERFACE_MAP_END
25 
26 NS_IMPL_CYCLE_COLLECTING_ADDREF(Permissions)
27 NS_IMPL_CYCLE_COLLECTING_RELEASE(Permissions)
28 
29 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Permissions, mWindow)
30 
31 Permissions::Permissions(nsPIDOMWindowInner* aWindow) : mWindow(aWindow) {}
32 
33 Permissions::~Permissions() = default;
34 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)35 JSObject* Permissions::WrapObject(JSContext* aCx,
36                                   JS::Handle<JSObject*> aGivenProto) {
37   return Permissions_Binding::Wrap(aCx, this, aGivenProto);
38 }
39 
40 namespace {
41 
CreatePermissionStatus(JSContext * aCx,JS::Handle<JSObject * > aPermission,nsPIDOMWindowInner * aWindow,ErrorResult & aRv)42 already_AddRefed<PermissionStatus> CreatePermissionStatus(
43     JSContext* aCx, JS::Handle<JSObject*> aPermission,
44     nsPIDOMWindowInner* aWindow, ErrorResult& aRv) {
45   PermissionDescriptor permission;
46   JS::Rooted<JS::Value> value(aCx, JS::ObjectOrNullValue(aPermission));
47   if (NS_WARN_IF(!permission.Init(aCx, value))) {
48     aRv.NoteJSContextException(aCx);
49     return nullptr;
50   }
51 
52   switch (permission.mName) {
53     case PermissionName::Geolocation:
54     case PermissionName::Notifications:
55     case PermissionName::Push:
56     case PermissionName::Persistent_storage:
57       return PermissionStatus::Create(aWindow, permission.mName, aRv);
58 
59     default:
60       MOZ_ASSERT_UNREACHABLE("Unhandled type");
61       aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
62       return nullptr;
63   }
64 }
65 
66 }  // namespace
67 
Query(JSContext * aCx,JS::Handle<JSObject * > aPermission,ErrorResult & aRv)68 already_AddRefed<Promise> Permissions::Query(JSContext* aCx,
69                                              JS::Handle<JSObject*> aPermission,
70                                              ErrorResult& aRv) {
71   if (!mWindow) {
72     aRv.Throw(NS_ERROR_UNEXPECTED);
73     return nullptr;
74   }
75 
76   RefPtr<PermissionStatus> status =
77       CreatePermissionStatus(aCx, aPermission, mWindow, aRv);
78   if (NS_WARN_IF(aRv.Failed())) {
79     MOZ_ASSERT(!status);
80     return nullptr;
81   }
82 
83   MOZ_ASSERT(status);
84   RefPtr<Promise> promise = Promise::Create(mWindow->AsGlobal(), aRv);
85   if (NS_WARN_IF(aRv.Failed())) {
86     return nullptr;
87   }
88 
89   promise->MaybeResolve(status);
90   return promise.forget();
91 }
92 
93 /* static */
RemovePermission(nsIPrincipal * aPrincipal,const nsACString & aPermissionType)94 nsresult Permissions::RemovePermission(nsIPrincipal* aPrincipal,
95                                        const nsACString& aPermissionType) {
96   MOZ_ASSERT(XRE_IsParentProcess());
97 
98   nsCOMPtr<nsIPermissionManager> permMgr =
99       components::PermissionManager::Service();
100   if (NS_WARN_IF(!permMgr)) {
101     return NS_ERROR_FAILURE;
102   }
103 
104   return permMgr->RemoveFromPrincipal(aPrincipal, aPermissionType);
105 }
106 
Revoke(JSContext * aCx,JS::Handle<JSObject * > aPermission,ErrorResult & aRv)107 already_AddRefed<Promise> Permissions::Revoke(JSContext* aCx,
108                                               JS::Handle<JSObject*> aPermission,
109                                               ErrorResult& aRv) {
110   if (!mWindow) {
111     aRv.Throw(NS_ERROR_UNEXPECTED);
112     return nullptr;
113   }
114 
115   PermissionDescriptor permission;
116   JS::Rooted<JS::Value> value(aCx, JS::ObjectOrNullValue(aPermission));
117   if (NS_WARN_IF(!permission.Init(aCx, value))) {
118     aRv.NoteJSContextException(aCx);
119     return nullptr;
120   }
121 
122   RefPtr<Promise> promise = Promise::Create(mWindow->AsGlobal(), aRv);
123   if (NS_WARN_IF(aRv.Failed())) {
124     return nullptr;
125   }
126 
127   nsCOMPtr<Document> document = mWindow->GetExtantDoc();
128   if (!document) {
129     promise->MaybeReject(NS_ERROR_UNEXPECTED);
130     return promise.forget();
131   }
132 
133   nsCOMPtr<nsIPermissionManager> permMgr =
134       components::PermissionManager::Service();
135   if (NS_WARN_IF(!permMgr)) {
136     promise->MaybeReject(NS_ERROR_FAILURE);
137     return promise.forget();
138   }
139 
140   const nsLiteralCString& permissionType =
141       PermissionNameToType(permission.mName);
142 
143   nsresult rv;
144   if (XRE_IsParentProcess()) {
145     rv = RemovePermission(document->NodePrincipal(), permissionType);
146   } else {
147     // Permissions can't be removed from the content process. Send a message
148     // to the parent; `ContentParent::RecvRemovePermission` will call
149     // `RemovePermission`.
150     ContentChild::GetSingleton()->SendRemovePermission(
151         IPC::Principal(document->NodePrincipal()), permissionType, &rv);
152   }
153 
154   if (NS_WARN_IF(NS_FAILED(rv))) {
155     promise->MaybeReject(rv);
156     return promise.forget();
157   }
158 
159   RefPtr<PermissionStatus> status =
160       CreatePermissionStatus(aCx, aPermission, mWindow, aRv);
161   if (NS_WARN_IF(aRv.Failed())) {
162     MOZ_ASSERT(!status);
163     return nullptr;
164   }
165 
166   MOZ_ASSERT(status);
167   promise->MaybeResolve(status);
168   return promise.forget();
169 }
170 
171 }  // namespace mozilla::dom
172