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