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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "PermissionStatusSink.h"
8 #include "PermissionObserver.h"
9 #include "PermissionStatus.h"
11 #include "mozilla/Permission.h"
12 #include "mozilla/PermissionDelegateHandler.h"
13 #include "mozilla/PermissionManager.h"
14 #include "mozilla/dom/WorkerPrivate.h"
15 #include "mozilla/dom/WorkerRef.h"
17 namespace mozilla::dom
{
19 PermissionStatusSink::PermissionStatusSink(PermissionStatus
* aPermissionStatus
,
20 PermissionName aPermissionName
,
21 const nsACString
& aPermissionType
)
22 : mSerialEventTarget(NS_GetCurrentThread()),
23 mPermissionStatus(aPermissionStatus
),
24 mMutex("PermissionStatusSink::mMutex"),
25 mPermissionName(aPermissionName
),
26 mPermissionType(aPermissionType
) {
27 MOZ_ASSERT(aPermissionStatus
);
28 MOZ_ASSERT(mSerialEventTarget
);
30 nsCOMPtr
<nsIGlobalObject
> global
= aPermissionStatus
->GetOwnerGlobal();
31 if (NS_WARN_IF(!global
)) {
35 nsCOMPtr
<nsIPrincipal
> principal
= global
->PrincipalOrNull();
36 if (NS_WARN_IF(!principal
)) {
40 mPrincipalForPermission
= Permission::ClonePrincipalForPermission(principal
);
43 PermissionStatusSink::~PermissionStatusSink() = default;
45 RefPtr
<PermissionStatusSink::PermissionStatePromise
>
46 PermissionStatusSink::Init() {
47 if (!NS_IsMainThread()) {
48 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
49 MOZ_ASSERT(workerPrivate
);
51 MutexAutoLock
lock(mMutex
);
53 mWorkerRef
= WeakWorkerRef::Create(
54 workerPrivate
, [self
= RefPtr(this)] { self
->Disentangle(); });
57 return InvokeAsync(GetMainThreadSerialEventTarget(), __func__
,
58 [self
= RefPtr(this)] {
59 MOZ_ASSERT(!self
->mObserver
);
61 // Covers the onchange part
62 // Whenever the user agent is aware that the state of a
63 // PermissionStatus instance status has changed: ... (The
64 // observer calls PermissionChanged() to do the steps)
65 self
->mObserver
= PermissionObserver::GetInstance();
66 if (NS_WARN_IF(!self
->mObserver
)) {
67 return PermissionStatePromise::CreateAndReject(
68 NS_ERROR_FAILURE
, __func__
);
71 self
->mObserver
->AddSink(self
);
73 // Covers the query part (Step 8.2 - 8.4)
74 return self
->ComputeStateOnMainThread();
78 bool PermissionStatusSink::MaybeUpdatedByOnMainThread(
79 nsIPermission
* aPermission
) {
80 MOZ_ASSERT(NS_IsMainThread());
82 if (!mPrincipalForPermission
) {
86 nsCOMPtr
<nsIPrincipal
> permissionPrincipal
;
87 aPermission
->GetPrincipal(getter_AddRefs(permissionPrincipal
));
88 if (!permissionPrincipal
) {
92 return mPrincipalForPermission
->Equals(permissionPrincipal
);
95 bool PermissionStatusSink::MaybeUpdatedByNotifyOnlyOnMainThread(
96 nsPIDOMWindowInner
* aInnerWindow
) {
97 MOZ_ASSERT(NS_IsMainThread());
101 void PermissionStatusSink::PermissionChangedOnMainThread() {
102 MOZ_ASSERT(NS_IsMainThread());
104 ComputeStateOnMainThread()->Then(
105 mSerialEventTarget
, __func__
,
106 [self
= RefPtr(this)](
107 const PermissionStatePromise::ResolveOrRejectValue
& aResult
) {
108 if (aResult
.IsResolve() && self
->mPermissionStatus
) {
109 self
->mPermissionStatus
->PermissionChanged(aResult
.ResolveValue());
114 void PermissionStatusSink::Disentangle() {
115 MOZ_ASSERT(mSerialEventTarget
->IsOnCurrentThread());
117 mPermissionStatus
= nullptr;
120 MutexAutoLock
lock(mMutex
);
121 mWorkerRef
= nullptr;
124 NS_DispatchToMainThread(
125 NS_NewRunnableFunction(__func__
, [self
= RefPtr(this)] {
126 if (self
->mObserver
) {
127 self
->mObserver
->RemoveSink(self
);
128 self
->mObserver
= nullptr;
133 RefPtr
<PermissionStatusSink::PermissionStatePromise
>
134 PermissionStatusSink::ComputeStateOnMainThread() {
135 MOZ_ASSERT(NS_IsMainThread());
137 // Step 1: If settings wasn't passed, set it to the current settings object.
138 // Step 2: If settings is a non-secure context, return "denied".
139 // XXX(krosylight): No such steps here, and no WPT coverage?
141 // The permission handler covers the rest of the steps, although the model
142 // does not exactly match what the spec has. (Not passing "permission key" for
145 if (mSerialEventTarget
->IsOnCurrentThread()) {
146 if (!mPermissionStatus
) {
147 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE
,
151 RefPtr
<nsGlobalWindowInner
> window
= mPermissionStatus
->GetOwnerWindow();
152 return ComputeStateOnMainThreadInternal(window
);
155 nsCOMPtr
<nsPIDOMWindowInner
> ancestorWindow
;
156 nsCOMPtr
<nsIPrincipal
> workerPrincipal
;
159 MutexAutoLock
lock(mMutex
);
162 // We have been disentangled.
163 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE
,
167 // If we have mWorkerRef, we haven't received the WorkerRef notification
169 WorkerPrivate
* workerPrivate
= mWorkerRef
->GetUnsafePrivate();
170 MOZ_ASSERT(workerPrivate
);
172 ancestorWindow
= workerPrivate
->GetAncestorWindow();
173 workerPrincipal
= workerPrivate
->GetPrincipal();
176 if (ancestorWindow
) {
177 return ComputeStateOnMainThreadInternal(ancestorWindow
);
180 if (NS_WARN_IF(!workerPrincipal
)) {
181 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE
, __func__
);
184 RefPtr
<nsIPermissionManager
> permissionManager
=
185 PermissionManager::GetInstance();
186 if (!permissionManager
) {
187 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE
, __func__
);
190 uint32_t action
= nsIPermissionManager::DENY_ACTION
;
191 nsresult rv
= permissionManager
->TestPermissionFromPrincipal(
192 workerPrincipal
, mPermissionType
, &action
);
193 if (NS_WARN_IF(NS_FAILED(rv
))) {
194 return PermissionStatePromise::CreateAndReject(rv
, __func__
);
197 return PermissionStatePromise::CreateAndResolve(action
, __func__
);
200 RefPtr
<PermissionStatusSink::PermissionStatePromise
>
201 PermissionStatusSink::ComputeStateOnMainThreadInternal(
202 nsPIDOMWindowInner
* aWindow
) {
203 MOZ_ASSERT(NS_IsMainThread());
205 if (NS_WARN_IF(!aWindow
)) {
206 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE
, __func__
);
209 RefPtr
<Document
> document
= aWindow
->GetExtantDoc();
210 if (NS_WARN_IF(!document
)) {
211 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE
, __func__
);
214 uint32_t action
= nsIPermissionManager::DENY_ACTION
;
216 PermissionDelegateHandler
* permissionHandler
=
217 document
->GetPermissionDelegateHandler();
218 if (NS_WARN_IF(!permissionHandler
)) {
219 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE
, __func__
);
222 nsresult rv
= permissionHandler
->GetPermissionForPermissionsAPI(
223 mPermissionType
, &action
);
224 if (NS_WARN_IF(NS_FAILED(rv
))) {
225 return PermissionStatePromise::CreateAndReject(rv
, __func__
);
228 return PermissionStatePromise::CreateAndResolve(action
, __func__
);
231 } // namespace mozilla::dom