Source code

Revision control

Other Tools

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 "ServiceWorkerShutdownBlocker.h"
8
9
#include <utility>
10
11
#include "MainThreadUtils.h"
12
#include "nsDebug.h"
13
#include "nsError.h"
14
#include "nsIWritablePropertyBag2.h"
15
#include "nsThreadUtils.h"
16
17
#include "mozilla/Assertions.h"
18
#include "mozilla/RefPtr.h"
19
20
namespace mozilla {
21
namespace dom {
22
23
NS_IMPL_ISUPPORTS(ServiceWorkerShutdownBlocker, nsIAsyncShutdownBlocker)
24
25
NS_IMETHODIMP ServiceWorkerShutdownBlocker::GetName(nsAString& aNameOut) {
26
aNameOut = NS_LITERAL_STRING(
27
"ServiceWorkerShutdownBlocker: shutting down Service Workers");
28
return NS_OK;
29
}
30
31
NS_IMETHODIMP
32
ServiceWorkerShutdownBlocker::BlockShutdown(nsIAsyncShutdownClient* aClient) {
33
AssertIsOnMainThread();
34
MOZ_ASSERT(!mShutdownClient);
35
36
mShutdownClient = aClient;
37
MaybeUnblockShutdown();
38
39
return NS_OK;
40
}
41
42
NS_IMETHODIMP ServiceWorkerShutdownBlocker::GetState(nsIPropertyBag** aBagOut) {
43
AssertIsOnMainThread();
44
MOZ_ASSERT(aBagOut);
45
46
nsCOMPtr<nsIWritablePropertyBag2> propertyBag =
47
do_CreateInstance("@mozilla.org/hash-property-bag;1");
48
49
if (NS_WARN_IF(!propertyBag)) {
50
return NS_ERROR_OUT_OF_MEMORY;
51
}
52
53
nsresult rv = propertyBag->SetPropertyAsBool(
54
NS_LITERAL_STRING("acceptingPromises"), IsAcceptingPromises());
55
56
if (NS_WARN_IF(NS_FAILED(rv))) {
57
return rv;
58
}
59
60
rv = propertyBag->SetPropertyAsUint32(NS_LITERAL_STRING("pendingPromises"),
61
GetPendingPromises());
62
63
if (NS_WARN_IF(NS_FAILED(rv))) {
64
return rv;
65
}
66
67
propertyBag.forget(aBagOut);
68
69
return NS_OK;
70
}
71
72
/* static */ already_AddRefed<ServiceWorkerShutdownBlocker>
73
ServiceWorkerShutdownBlocker::CreateAndRegisterOn(
74
nsIAsyncShutdownClient* aShutdownBarrier) {
75
AssertIsOnMainThread();
76
MOZ_ASSERT(aShutdownBarrier);
77
78
RefPtr<ServiceWorkerShutdownBlocker> blocker =
79
new ServiceWorkerShutdownBlocker();
80
81
nsresult rv = aShutdownBarrier->AddBlocker(
82
blocker.get(), NS_LITERAL_STRING(__FILE__), __LINE__,
83
NS_LITERAL_STRING("Service Workers shutdown"));
84
85
if (NS_WARN_IF(NS_FAILED(rv))) {
86
return nullptr;
87
}
88
89
return blocker.forget();
90
}
91
92
void ServiceWorkerShutdownBlocker::WaitOnPromise(
93
GenericNonExclusivePromise* aPromise) {
94
AssertIsOnMainThread();
95
MOZ_DIAGNOSTIC_ASSERT(IsAcceptingPromises());
96
MOZ_ASSERT(aPromise);
97
98
++mState.as<AcceptingPromises>().mPendingPromises;
99
100
RefPtr<ServiceWorkerShutdownBlocker> self = this;
101
102
aPromise->Then(GetCurrentThreadSerialEventTarget(), __func__,
103
[self = std::move(self)](
104
const GenericNonExclusivePromise::ResolveOrRejectValue&) {
105
if (!self->PromiseSettled()) {
106
self->MaybeUnblockShutdown();
107
}
108
});
109
}
110
111
void ServiceWorkerShutdownBlocker::StopAcceptingPromises() {
112
AssertIsOnMainThread();
113
MOZ_ASSERT(IsAcceptingPromises());
114
115
mState = AsVariant(NotAcceptingPromises(mState.as<AcceptingPromises>()));
116
}
117
118
ServiceWorkerShutdownBlocker::ServiceWorkerShutdownBlocker()
119
: mState(VariantType<AcceptingPromises>()) {
120
AssertIsOnMainThread();
121
}
122
123
ServiceWorkerShutdownBlocker::~ServiceWorkerShutdownBlocker() {
124
MOZ_ASSERT(!IsAcceptingPromises());
125
MOZ_ASSERT(!GetPendingPromises());
126
MOZ_ASSERT(!mShutdownClient);
127
}
128
129
void ServiceWorkerShutdownBlocker::MaybeUnblockShutdown() {
130
AssertIsOnMainThread();
131
132
if (!mShutdownClient || IsAcceptingPromises() || GetPendingPromises()) {
133
return;
134
}
135
136
mShutdownClient->RemoveBlocker(this);
137
mShutdownClient = nullptr;
138
}
139
140
uint32_t ServiceWorkerShutdownBlocker::PromiseSettled() {
141
AssertIsOnMainThread();
142
MOZ_ASSERT(GetPendingPromises());
143
144
if (IsAcceptingPromises()) {
145
return --mState.as<AcceptingPromises>().mPendingPromises;
146
}
147
148
return --mState.as<NotAcceptingPromises>().mPendingPromises;
149
}
150
151
bool ServiceWorkerShutdownBlocker::IsAcceptingPromises() const {
152
AssertIsOnMainThread();
153
154
return mState.is<AcceptingPromises>();
155
}
156
157
uint32_t ServiceWorkerShutdownBlocker::GetPendingPromises() const {
158
AssertIsOnMainThread();
159
160
if (IsAcceptingPromises()) {
161
return mState.as<AcceptingPromises>().mPendingPromises;
162
}
163
164
return mState.as<NotAcceptingPromises>().mPendingPromises;
165
}
166
167
ServiceWorkerShutdownBlocker::NotAcceptingPromises::NotAcceptingPromises(
168
AcceptingPromises aPreviousState)
169
: mPendingPromises(aPreviousState.mPendingPromises) {
170
AssertIsOnMainThread();
171
}
172
173
} // namespace dom
174
} // namespace mozilla