1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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/. */
7 #include "mozilla/dom/LockManager.h"
8 #include "mozilla/dom/WorkerCommon.h"
9 #include "mozilla/dom/locks/LockManagerChild.h"
10 #include "mozilla/dom/locks/LockRequestChild.h"
11 #include "mozilla/Assertions.h"
12 #include "mozilla/ErrorResult.h"
13 #include "mozilla/dom/LockManagerBinding.h"
14 #include "mozilla/dom/Promise.h"
15 #include "mozilla/dom/locks/PLockManager.h"
16 #include "mozilla/ipc/BackgroundChild.h"
17 #include "mozilla/ipc/BackgroundUtils.h"
18 #include "mozilla/ipc/PBackgroundChild.h"
20 namespace mozilla::dom
{
22 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(LockManager
, mOwner
, mActor
)
23 NS_IMPL_CYCLE_COLLECTING_ADDREF(LockManager
)
24 NS_IMPL_CYCLE_COLLECTING_RELEASE(LockManager
)
25 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LockManager
)
26 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
27 NS_INTERFACE_MAP_ENTRY(nsISupports
)
30 JSObject
* LockManager::WrapObject(JSContext
* aCx
,
31 JS::Handle
<JSObject
*> aGivenProto
) {
32 return LockManager_Binding::Wrap(aCx
, this, aGivenProto
);
35 LockManager::LockManager(nsIGlobalObject
* aGlobal
) : mOwner(aGlobal
) {
36 Maybe
<ClientInfo
> clientInfo
= aGlobal
->GetClientInfo();
41 const mozilla::ipc::PrincipalInfo
& principalInfo
=
42 clientInfo
->PrincipalInfo();
44 if (principalInfo
.type() !=
45 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo
) {
49 mozilla::ipc::PBackgroundChild
* backgroundActor
=
50 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
51 mActor
= new locks::LockManagerChild(aGlobal
);
52 backgroundActor
->SendPLockManagerConstructor(mActor
, principalInfo
,
55 if (!NS_IsMainThread()) {
56 mWorkerRef
= WeakWorkerRef::Create(GetCurrentThreadWorkerPrivate(),
57 [self
= RefPtr(this)]() {
58 // Others may grab a strong reference
59 // and block immediate destruction.
60 // Shutdown early as we don't have to
63 self
->mWorkerRef
= nullptr;
68 static bool ValidateRequestArguments(const nsAString
& name
,
69 const LockOptions
& options
,
71 if (name
.Length() > 0 && name
.First() == u
'-') {
72 aRv
.ThrowNotSupportedError("Names starting with `-` are reserved");
76 if (options
.mIfAvailable
) {
77 aRv
.ThrowNotSupportedError(
78 "`steal` and `ifAvailable` cannot be used together");
81 if (options
.mMode
!= LockMode::Exclusive
) {
82 aRv
.ThrowNotSupportedError(
83 "`steal` is only supported for exclusive lock requests");
87 if (options
.mSignal
.WasPassed()) {
89 aRv
.ThrowNotSupportedError(
90 "`steal` and `signal` cannot be used together");
93 if (options
.mIfAvailable
) {
94 aRv
.ThrowNotSupportedError(
95 "`ifAvailable` and `signal` cannot be used together");
98 if (options
.mSignal
.Value().Aborted()) {
99 aRv
.ThrowAbortError("The lock request is aborted");
106 already_AddRefed
<Promise
> LockManager::Request(const nsAString
& aName
,
107 LockGrantedCallback
& aCallback
,
109 return Request(aName
, LockOptions(), aCallback
, aRv
);
111 already_AddRefed
<Promise
> LockManager::Request(const nsAString
& aName
,
112 const LockOptions
& aOptions
,
113 LockGrantedCallback
& aCallback
,
115 if (!mOwner
->GetClientInfo()) {
116 // We do have nsPIDOMWindowInner::IsFullyActive for this kind of check,
117 // but this should be sufficient here as unloaded iframe is the only
118 // non-fully-active case that Web Locks should worry about (since it does
119 // not enter bfcache).
120 aRv
.ThrowInvalidStateError(
121 "The document of the lock manager is not fully active");
125 if (mOwner
->GetStorageAccess() <= StorageAccess::eDeny
) {
126 // Step 4: If origin is an opaque origin, then return a promise rejected
127 // with a "SecurityError" DOMException.
128 // But per https://wicg.github.io/web-locks/#lock-managers this really means
129 // whether it has storage access.
130 aRv
.ThrowSecurityError("request() is not allowed in this context");
135 aRv
.ThrowNotSupportedError(
136 "Web Locks API is not enabled for this kind of document");
140 if (!ValidateRequestArguments(aName
, aOptions
, aRv
)) {
144 RefPtr
<Promise
> promise
= Promise::Create(mOwner
, aRv
);
149 mActor
->RequestLock({nsString(aName
), promise
, &aCallback
}, aOptions
);
150 return promise
.forget();
153 already_AddRefed
<Promise
> LockManager::Query(ErrorResult
& aRv
) {
154 if (!mOwner
->GetClientInfo()) {
155 aRv
.ThrowInvalidStateError(
156 "The document of the lock manager is not fully active");
160 if (mOwner
->GetStorageAccess() <= StorageAccess::eDeny
) {
161 aRv
.ThrowSecurityError("query() is not allowed in this context");
166 aRv
.ThrowNotSupportedError(
167 "Web Locks API is not enabled for this kind of document");
171 RefPtr
<Promise
> promise
= Promise::Create(mOwner
, aRv
);
176 mActor
->SendQuery()->Then(
177 GetCurrentSerialEventTarget(), __func__
,
178 [promise
](locks::LockManagerChild::QueryPromise::ResolveOrRejectValue
&&
180 if (aResult
.IsResolve()) {
181 promise
->MaybeResolve(aResult
.ResolveValue());
183 promise
->MaybeRejectWithUnknownError("Query failed");
186 return promise
.forget();
189 void LockManager::Shutdown() {
191 locks::PLockManagerChild::Send__delete__(mActor
);
196 } // namespace mozilla::dom