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 file,
5
* You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "ServiceWorker.h"
8
9
#include "mozilla/dom/Document.h"
10
#include "nsPIDOMWindow.h"
11
#include "RemoteServiceWorkerImpl.h"
12
#include "ServiceWorkerCloneData.h"
13
#include "ServiceWorkerImpl.h"
14
#include "ServiceWorkerManager.h"
15
#include "ServiceWorkerPrivate.h"
16
#include "ServiceWorkerRegistration.h"
17
#include "ServiceWorkerUtils.h"
18
19
#include "mozilla/dom/ClientIPCTypes.h"
20
#include "mozilla/dom/ClientState.h"
21
#include "mozilla/dom/MessagePortBinding.h"
22
#include "mozilla/dom/Promise.h"
23
#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
24
#include "mozilla/dom/WorkerPrivate.h"
25
#include "mozilla/StaticPrefs_dom.h"
26
#include "mozilla/StorageAccess.h"
27
28
#ifdef XP_WIN
29
# undef PostMessage
30
#endif
31
32
using mozilla::ErrorResult;
33
using namespace mozilla::dom;
34
35
namespace mozilla {
36
namespace dom {
37
38
bool ServiceWorkerVisible(JSContext* aCx, JSObject* aObj) {
39
if (NS_IsMainThread()) {
40
return StaticPrefs::dom_serviceWorkers_enabled();
41
}
42
43
return IS_INSTANCE_OF(ServiceWorkerGlobalScope, aObj);
44
}
45
46
// static
47
already_AddRefed<ServiceWorker> ServiceWorker::Create(
48
nsIGlobalObject* aOwner, const ServiceWorkerDescriptor& aDescriptor) {
49
RefPtr<ServiceWorker> ref;
50
RefPtr<ServiceWorker::Inner> inner;
51
52
if (ServiceWorkerParentInterceptEnabled()) {
53
inner = new RemoteServiceWorkerImpl(aDescriptor);
54
} else {
55
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
56
NS_ENSURE_TRUE(swm, nullptr);
57
58
RefPtr<ServiceWorkerRegistrationInfo> reg =
59
swm->GetRegistration(aDescriptor.PrincipalInfo(), aDescriptor.Scope());
60
NS_ENSURE_TRUE(reg, nullptr);
61
62
RefPtr<ServiceWorkerInfo> info = reg->GetByDescriptor(aDescriptor);
63
NS_ENSURE_TRUE(info, nullptr);
64
65
inner = new ServiceWorkerImpl(info, reg);
66
}
67
68
NS_ENSURE_TRUE(inner, nullptr);
69
70
ref = new ServiceWorker(aOwner, aDescriptor, inner);
71
return ref.forget();
72
}
73
74
ServiceWorker::ServiceWorker(nsIGlobalObject* aGlobal,
75
const ServiceWorkerDescriptor& aDescriptor,
76
ServiceWorker::Inner* aInner)
77
: DOMEventTargetHelper(aGlobal),
78
mDescriptor(aDescriptor),
79
mInner(aInner),
80
mLastNotifiedState(ServiceWorkerState::Installing) {
81
MOZ_ASSERT(NS_IsMainThread());
82
MOZ_DIAGNOSTIC_ASSERT(aGlobal);
83
MOZ_DIAGNOSTIC_ASSERT(mInner);
84
85
KeepAliveIfHasListenersFor(NS_LITERAL_STRING("statechange"));
86
87
// The error event handler is required by the spec currently, but is not used
88
// anywhere. Don't keep the object alive in that case.
89
90
// This will update our state too.
91
mInner->AddServiceWorker(this);
92
93
// Attempt to get an existing binding object for the registration
94
// associated with this ServiceWorker.
95
RefPtr<ServiceWorkerRegistration> reg =
96
aGlobal->GetServiceWorkerRegistration(ServiceWorkerRegistrationDescriptor(
97
mDescriptor.RegistrationId(), mDescriptor.RegistrationVersion(),
98
mDescriptor.PrincipalInfo(), mDescriptor.Scope(),
99
ServiceWorkerUpdateViaCache::Imports));
100
if (reg) {
101
MaybeAttachToRegistration(reg);
102
} else {
103
RefPtr<ServiceWorker> self = this;
104
105
mInner->GetRegistration(
106
[self = std::move(self)](
107
const ServiceWorkerRegistrationDescriptor& aDescriptor) {
108
nsIGlobalObject* global = self->GetParentObject();
109
NS_ENSURE_TRUE_VOID(global);
110
RefPtr<ServiceWorkerRegistration> reg =
111
global->GetOrCreateServiceWorkerRegistration(aDescriptor);
112
self->MaybeAttachToRegistration(reg);
113
},
114
[](ErrorResult& aRv) {
115
// do nothing
116
aRv.SuppressException();
117
});
118
}
119
}
120
121
ServiceWorker::~ServiceWorker() {
122
MOZ_ASSERT(NS_IsMainThread());
123
mInner->RemoveServiceWorker(this);
124
}
125
126
NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorker, DOMEventTargetHelper,
127
mRegistration);
128
129
NS_IMPL_ADDREF_INHERITED(ServiceWorker, DOMEventTargetHelper)
130
NS_IMPL_RELEASE_INHERITED(ServiceWorker, DOMEventTargetHelper)
131
132
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorker)
133
NS_INTERFACE_MAP_ENTRY(ServiceWorker)
134
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
135
136
JSObject* ServiceWorker::WrapObject(JSContext* aCx,
137
JS::Handle<JSObject*> aGivenProto) {
138
MOZ_ASSERT(NS_IsMainThread());
139
140
return ServiceWorker_Binding::Wrap(aCx, this, aGivenProto);
141
}
142
143
ServiceWorkerState ServiceWorker::State() const { return mDescriptor.State(); }
144
145
void ServiceWorker::SetState(ServiceWorkerState aState) {
146
NS_ENSURE_TRUE_VOID(aState >= mDescriptor.State());
147
mDescriptor.SetState(aState);
148
}
149
150
void ServiceWorker::MaybeDispatchStateChangeEvent() {
151
if (mDescriptor.State() <= mLastNotifiedState || !GetParentObject()) {
152
return;
153
}
154
mLastNotifiedState = mDescriptor.State();
155
156
DOMEventTargetHelper::DispatchTrustedEvent(NS_LITERAL_STRING("statechange"));
157
158
// Once we have transitioned to the redundant state then no
159
// more statechange events will occur. We can allow the DOM
160
// object to GC if script is not holding it alive.
161
if (mLastNotifiedState == ServiceWorkerState::Redundant) {
162
IgnoreKeepAliveIfHasListenersFor(NS_LITERAL_STRING("statechange"));
163
}
164
}
165
166
void ServiceWorker::GetScriptURL(nsString& aURL) const {
167
CopyUTF8toUTF16(mDescriptor.ScriptURL(), aURL);
168
}
169
170
void ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
171
const Sequence<JSObject*>& aTransferable,
172
ErrorResult& aRv) {
173
if (State() == ServiceWorkerState::Redundant) {
174
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
175
return;
176
}
177
178
nsPIDOMWindowInner* window = GetOwner();
179
if (NS_WARN_IF(!window || !window->GetExtantDoc())) {
180
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
181
return;
182
}
183
184
auto storageAllowed = StorageAllowedForWindow(window);
185
if (storageAllowed != StorageAccess::eAllow) {
186
ServiceWorkerManager::LocalizeAndReportToAllClients(
187
mDescriptor.Scope(), "ServiceWorkerPostMessageStorageError",
188
nsTArray<nsString>{NS_ConvertUTF8toUTF16(mDescriptor.Scope())});
189
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
190
return;
191
}
192
193
Maybe<ClientInfo> clientInfo = window->GetClientInfo();
194
Maybe<ClientState> clientState = window->GetClientState();
195
if (NS_WARN_IF(clientInfo.isNothing() || clientState.isNothing())) {
196
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
197
return;
198
}
199
200
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
201
aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
202
&transferable);
203
if (aRv.Failed()) {
204
return;
205
}
206
207
RefPtr<ServiceWorkerCloneData> data = new ServiceWorkerCloneData();
208
data->Write(aCx, aMessage, transferable, aRv);
209
if (aRv.Failed()) {
210
return;
211
}
212
213
mInner->PostMessage(std::move(data), clientInfo.ref(), clientState.ref());
214
}
215
216
void ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
217
const PostMessageOptions& aOptions,
218
ErrorResult& aRv) {
219
PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
220
}
221
222
const ServiceWorkerDescriptor& ServiceWorker::Descriptor() const {
223
return mDescriptor;
224
}
225
226
void ServiceWorker::DisconnectFromOwner() {
227
DOMEventTargetHelper::DisconnectFromOwner();
228
}
229
230
void ServiceWorker::MaybeAttachToRegistration(
231
ServiceWorkerRegistration* aRegistration) {
232
MOZ_DIAGNOSTIC_ASSERT(aRegistration);
233
MOZ_DIAGNOSTIC_ASSERT(!mRegistration);
234
235
// If the registration no longer actually references this ServiceWorker
236
// then we must be in the redundant state.
237
if (!aRegistration->Descriptor().HasWorker(mDescriptor)) {
238
SetState(ServiceWorkerState::Redundant);
239
MaybeDispatchStateChangeEvent();
240
return;
241
}
242
243
mRegistration = aRegistration;
244
}
245
246
} // namespace dom
247
} // namespace mozilla