Coverage Report

Created: 2018-09-25 14:53

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