PermissionStatusSink.cpp (8891B)
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/. */ 6 7 #include "PermissionStatusSink.h" 8 9 #include "PermissionObserver.h" 10 #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" 16 #include "nsGlobalWindowInner.h" 17 18 namespace mozilla::dom { 19 20 PermissionStatusSink::PermissionStatusSink(PermissionStatus* aPermissionStatus, 21 PermissionName aPermissionName, 22 const nsACString& aPermissionType) 23 : mSerialEventTarget(NS_GetCurrentThread()), 24 mPermissionStatus(aPermissionStatus), 25 mMutex("PermissionStatusSink::mMutex"), 26 mPermissionName(aPermissionName), 27 mPermissionType(aPermissionType) { 28 MOZ_ASSERT(aPermissionStatus); 29 MOZ_ASSERT(mSerialEventTarget); 30 31 nsCOMPtr<nsIGlobalObject> global = aPermissionStatus->GetOwnerGlobal(); 32 if (NS_WARN_IF(!global)) { 33 return; 34 } 35 36 nsCOMPtr<nsIPrincipal> principal = global->PrincipalOrNull(); 37 if (NS_WARN_IF(!principal)) { 38 return; 39 } 40 41 mPrincipalForPermission = Permission::ClonePrincipalForPermission(principal); 42 } 43 44 PermissionStatusSink::~PermissionStatusSink() = default; 45 46 RefPtr<PermissionStatusSink::PermissionStatePromise> 47 PermissionStatusSink::Init() { 48 if (!NS_IsMainThread()) { 49 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 50 MOZ_ASSERT(workerPrivate); 51 52 MutexAutoLock lock(mMutex); 53 54 RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create( 55 workerPrivate, "PermissionStatusSink", 56 [self = RefPtr(this)]() { self->Disentangle(); }); 57 if (NS_WARN_IF(!workerRef)) { 58 // If WorkerRef creation fails, the Worker has started shutting down. But 59 // we are on the Worker thread, promise handlers in 60 // PermissionStatus::Init()/Permissions::Query() can still be dispatched 61 // to the Worker thread for outer promise rejection. 62 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, 63 __func__); 64 ; 65 } 66 67 mWorkerRef = new ThreadSafeWorkerRef(workerRef); 68 } 69 70 // On the Worker thread, so the below async function must be executed before 71 // WorkerRef callback which should also be on the Worker thread. So the above 72 // created WorkerRef should protect the outer promise handling can be 73 // dispatched on the Worker thread. 74 return InvokeAsync(GetMainThreadSerialEventTarget(), __func__, 75 [self = RefPtr(this)] { 76 MOZ_ASSERT(!self->mObserver); 77 78 // Covers the onchange part 79 // Whenever the user agent is aware that the state of a 80 // PermissionStatus instance status has changed: ... (The 81 // observer calls PermissionChanged() to do the steps) 82 self->mObserver = PermissionObserver::GetInstance(); 83 if (NS_WARN_IF(!self->mObserver)) { 84 return PermissionStatePromise::CreateAndReject( 85 NS_ERROR_FAILURE, __func__); 86 } 87 88 self->mObserver->AddSink(self); 89 90 // Covers the query part (Step 8.2 - 8.4) 91 return self->ComputeStateOnMainThread(); 92 }); 93 } 94 95 bool PermissionStatusSink::MaybeUpdatedByOnMainThread( 96 nsIPermission* aPermission) { 97 MOZ_ASSERT(NS_IsMainThread()); 98 99 if (!mPrincipalForPermission) { 100 return false; 101 } 102 103 nsCOMPtr<nsIPrincipal> permissionPrincipal; 104 aPermission->GetPrincipal(getter_AddRefs(permissionPrincipal)); 105 if (!permissionPrincipal) { 106 return false; 107 } 108 109 return mPrincipalForPermission->Equals(permissionPrincipal); 110 } 111 112 bool PermissionStatusSink::MaybeUpdatedByNotifyOnlyOnMainThread( 113 nsPIDOMWindowInner* aInnerWindow) { 114 MOZ_ASSERT(NS_IsMainThread()); 115 return false; 116 } 117 118 void PermissionStatusSink::PermissionChangedOnMainThread() { 119 MOZ_ASSERT(NS_IsMainThread()); 120 121 // Nothing to do if Worker had shutted down. 122 if (!mSerialEventTarget->IsOnCurrentThread()) { 123 MutexAutoLock lock(mMutex); 124 if (!mWorkerRef) { 125 return; 126 } 127 } 128 129 // mWorkerRef is not nullptr, it will protect the promise handling can be 130 // dispatched to the Worker thread, even though the Worker starts shutdown, 131 // because mWorkerRef is nullify on the main thread. 132 ComputeStateOnMainThread()->Then( 133 mSerialEventTarget, __func__, 134 [self = RefPtr(this)]( 135 const PermissionStatePromise::ResolveOrRejectValue& aResult) { 136 if (aResult.IsResolve() && self->mPermissionStatus) { 137 self->mPermissionStatus->PermissionChanged(aResult.ResolveValue()); 138 } 139 }); 140 } 141 142 void PermissionStatusSink::Disentangle() { 143 MOZ_ASSERT(mSerialEventTarget->IsOnCurrentThread()); 144 145 mPermissionStatus = nullptr; 146 147 NS_DispatchToMainThread( 148 NS_NewRunnableFunction(__func__, [self = RefPtr(this)] { 149 if (self->mObserver) { 150 self->mObserver->RemoveSink(self); 151 self->mObserver = nullptr; 152 } 153 { 154 MutexAutoLock lock(self->mMutex); 155 self->mWorkerRef = nullptr; 156 } 157 })); 158 } 159 160 RefPtr<PermissionStatusSink::PermissionStatePromise> 161 PermissionStatusSink::ComputeStateOnMainThread() { 162 MOZ_ASSERT(NS_IsMainThread()); 163 164 // Step 1: If settings wasn't passed, set it to the current settings object. 165 // Step 2: If settings is a non-secure context, return "denied". 166 // XXX(krosylight): No such steps here, and no WPT coverage? 167 168 // The permission handler covers the rest of the steps, although the model 169 // does not exactly match what the spec has. (Not passing "permission key" for 170 // example) 171 172 if (mSerialEventTarget->IsOnCurrentThread()) { 173 if (!mPermissionStatus) { 174 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, 175 __func__); 176 } 177 178 RefPtr<nsGlobalWindowInner> window = mPermissionStatus->GetOwnerWindow(); 179 return ComputeStateOnMainThreadInternal(window); 180 } 181 182 nsCOMPtr<nsPIDOMWindowInner> ancestorWindow; 183 nsCOMPtr<nsIPrincipal> workerPrincipal; 184 185 { 186 MutexAutoLock lock(mMutex); 187 188 if (!mWorkerRef) { 189 // We have been disentangled. 190 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, 191 __func__); 192 } 193 194 // If we have mWorkerRef, we haven't received the WorkerRef notification 195 // yet. 196 WorkerPrivate* workerPrivate = mWorkerRef->Private(); 197 MOZ_ASSERT(workerPrivate); 198 199 ancestorWindow = workerPrivate->GetAncestorWindow(); 200 workerPrincipal = workerPrivate->GetPrincipal(); 201 } 202 203 if (ancestorWindow) { 204 return ComputeStateOnMainThreadInternal(ancestorWindow); 205 } 206 207 if (NS_WARN_IF(!workerPrincipal)) { 208 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 209 } 210 211 RefPtr<nsIPermissionManager> permissionManager = 212 PermissionManager::GetInstance(); 213 if (!permissionManager) { 214 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 215 } 216 217 uint32_t action = nsIPermissionManager::DENY_ACTION; 218 nsresult rv = permissionManager->TestPermissionFromPrincipal( 219 workerPrincipal, mPermissionType, &action); 220 if (NS_WARN_IF(NS_FAILED(rv))) { 221 return PermissionStatePromise::CreateAndReject(rv, __func__); 222 } 223 224 return PermissionStatePromise::CreateAndResolve(action, __func__); 225 } 226 227 RefPtr<PermissionStatusSink::PermissionStatePromise> 228 PermissionStatusSink::ComputeStateOnMainThreadInternal( 229 nsPIDOMWindowInner* aWindow) { 230 MOZ_ASSERT(NS_IsMainThread()); 231 232 if (NS_WARN_IF(!aWindow)) { 233 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 234 } 235 236 RefPtr<Document> document = aWindow->GetExtantDoc(); 237 if (NS_WARN_IF(!document)) { 238 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 239 } 240 241 uint32_t action = nsIPermissionManager::DENY_ACTION; 242 243 PermissionDelegateHandler* permissionHandler = 244 document->GetPermissionDelegateHandler(); 245 if (NS_WARN_IF(!permissionHandler)) { 246 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 247 } 248 249 nsresult rv = permissionHandler->GetPermissionForPermissionsAPI( 250 mPermissionType, &action); 251 if (NS_WARN_IF(NS_FAILED(rv))) { 252 return PermissionStatePromise::CreateAndReject(rv, __func__); 253 } 254 255 return PermissionStatePromise::CreateAndResolve(action, __func__); 256 } 257 258 } // namespace mozilla::dom