Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / permission / PermissionStatusSink.cpp
blob81824b65c049de92471f3836e183b8dd2308a70e
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)) {
32 return;
35 nsCOMPtr<nsIPrincipal> principal = global->PrincipalOrNull();
36 if (NS_WARN_IF(!principal)) {
37 return;
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();
75 });
78 bool PermissionStatusSink::MaybeUpdatedByOnMainThread(
79 nsIPermission* aPermission) {
80 MOZ_ASSERT(NS_IsMainThread());
82 if (!mPrincipalForPermission) {
83 return false;
86 nsCOMPtr<nsIPrincipal> permissionPrincipal;
87 aPermission->GetPrincipal(getter_AddRefs(permissionPrincipal));
88 if (!permissionPrincipal) {
89 return false;
92 return mPrincipalForPermission->Equals(permissionPrincipal);
95 bool PermissionStatusSink::MaybeUpdatedByNotifyOnlyOnMainThread(
96 nsPIDOMWindowInner* aInnerWindow) {
97 MOZ_ASSERT(NS_IsMainThread());
98 return false;
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;
130 }));
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
143 // example)
145 if (mSerialEventTarget->IsOnCurrentThread()) {
146 if (!mPermissionStatus) {
147 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE,
148 __func__);
151 RefPtr<nsGlobalWindowInner> window = mPermissionStatus->GetOwnerWindow();
152 return ComputeStateOnMainThreadInternal(window);
155 nsCOMPtr<nsPIDOMWindowInner> ancestorWindow;
156 nsCOMPtr<nsIPrincipal> workerPrincipal;
159 MutexAutoLock lock(mMutex);
161 if (!mWorkerRef) {
162 // We have been disentangled.
163 return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE,
164 __func__);
167 // If we have mWorkerRef, we haven't received the WorkerRef notification
168 // yet.
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