1/*
2 * Copyright (C) 2017 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WebSWServerConnection.h"
28
29#if ENABLE(SERVICE_WORKER)
30
31#include "DataReference.h"
32#include "FormDataReference.h"
33#include "Logging.h"
34#include "NetworkConnectionToWebProcessMessages.h"
35#include "NetworkProcess.h"
36#include "ServiceWorkerClientFetchMessages.h"
37#include "WebCoreArgumentCoders.h"
38#include "WebProcess.h"
39#include "WebProcessMessages.h"
40#include "WebSWClientConnectionMessages.h"
41#include "WebSWContextManagerConnectionMessages.h"
42#include "WebSWServerConnectionMessages.h"
43#include "WebSWServerToContextConnection.h"
44#include <WebCore/DocumentIdentifier.h>
45#include <WebCore/ExceptionData.h>
46#include <WebCore/NotImplemented.h>
47#include <WebCore/SWServerRegistration.h>
48#include <WebCore/SecurityOrigin.h>
49#include <WebCore/ServiceWorkerClientData.h>
50#include <WebCore/ServiceWorkerClientIdentifier.h>
51#include <WebCore/ServiceWorkerContextData.h>
52#include <WebCore/ServiceWorkerJobData.h>
53#include <WebCore/ServiceWorkerUpdateViaCache.h>
54#include <wtf/Algorithms.h>
55#include <wtf/MainThread.h>
56
57namespace WebKit {
58using namespace PAL;
59using namespace WebCore;
60
61#define SWSERVERCONNECTION_RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(m_sessionID.isAlwaysOnLoggingAllowed(), ServiceWorker, "%p - WebSWServerConnection::" fmt, this, ##__VA_ARGS__)
62#define SWSERVERCONNECTION_RELEASE_LOG_ERROR_IF_ALLOWED(fmt, ...) RELEASE_LOG_ERROR_IF(m_sessionID.isAlwaysOnLoggingAllowed(), ServiceWorker, "%p - WebSWServerConnection::" fmt, this, ##__VA_ARGS__)
63
64WebSWServerConnection::WebSWServerConnection(NetworkProcess& networkProcess, SWServer& server, IPC::Connection& connection, SessionID sessionID)
65 : SWServer::Connection(server)
66 , m_sessionID(sessionID)
67 , m_contentConnection(connection)
68 , m_networkProcess(networkProcess)
69{
70 networkProcess.registerSWServerConnection(*this);
71}
72
73WebSWServerConnection::~WebSWServerConnection()
74{
75 m_networkProcess->unregisterSWServerConnection(*this);
76 for (const auto& keyValue : m_clientOrigins)
77 server().unregisterServiceWorkerClient(keyValue.value, keyValue.key);
78}
79
80void WebSWServerConnection::rejectJobInClient(ServiceWorkerJobIdentifier jobIdentifier, const ExceptionData& exceptionData)
81{
82 send(Messages::WebSWClientConnection::JobRejectedInServer(jobIdentifier, exceptionData));
83}
84
85void WebSWServerConnection::resolveRegistrationJobInClient(ServiceWorkerJobIdentifier jobIdentifier, const ServiceWorkerRegistrationData& registrationData, ShouldNotifyWhenResolved shouldNotifyWhenResolved)
86{
87 send(Messages::WebSWClientConnection::RegistrationJobResolvedInServer(jobIdentifier, registrationData, shouldNotifyWhenResolved));
88}
89
90void WebSWServerConnection::resolveUnregistrationJobInClient(ServiceWorkerJobIdentifier jobIdentifier, const ServiceWorkerRegistrationKey& registrationKey, bool unregistrationResult)
91{
92 send(Messages::WebSWClientConnection::UnregistrationJobResolvedInServer(jobIdentifier, unregistrationResult));
93}
94
95void WebSWServerConnection::startScriptFetchInClient(ServiceWorkerJobIdentifier jobIdentifier, const ServiceWorkerRegistrationKey& registrationKey, FetchOptions::Cache cachePolicy)
96{
97 send(Messages::WebSWClientConnection::StartScriptFetchForServer(jobIdentifier, registrationKey, cachePolicy));
98}
99
100void WebSWServerConnection::updateRegistrationStateInClient(ServiceWorkerRegistrationIdentifier identifier, ServiceWorkerRegistrationState state, const Optional<ServiceWorkerData>& serviceWorkerData)
101{
102 send(Messages::WebSWClientConnection::UpdateRegistrationState(identifier, state, serviceWorkerData));
103}
104
105void WebSWServerConnection::fireUpdateFoundEvent(ServiceWorkerRegistrationIdentifier identifier)
106{
107 send(Messages::WebSWClientConnection::FireUpdateFoundEvent(identifier));
108}
109
110void WebSWServerConnection::setRegistrationLastUpdateTime(ServiceWorkerRegistrationIdentifier identifier, WallTime lastUpdateTime)
111{
112 send(Messages::WebSWClientConnection::SetRegistrationLastUpdateTime(identifier, lastUpdateTime));
113}
114
115void WebSWServerConnection::setRegistrationUpdateViaCache(ServiceWorkerRegistrationIdentifier identifier, ServiceWorkerUpdateViaCache updateViaCache)
116{
117 send(Messages::WebSWClientConnection::SetRegistrationUpdateViaCache(identifier, updateViaCache));
118}
119
120void WebSWServerConnection::notifyClientsOfControllerChange(const HashSet<DocumentIdentifier>& contextIdentifiers, const ServiceWorkerData& newController)
121{
122 send(Messages::WebSWClientConnection::NotifyClientsOfControllerChange(contextIdentifiers, newController));
123}
124
125void WebSWServerConnection::updateWorkerStateInClient(ServiceWorkerIdentifier worker, ServiceWorkerState state)
126{
127 send(Messages::WebSWClientConnection::UpdateWorkerState(worker, state));
128}
129
130void WebSWServerConnection::cancelFetch(ServiceWorkerRegistrationIdentifier serviceWorkerRegistrationIdentifier, FetchIdentifier fetchIdentifier)
131{
132 SWSERVERCONNECTION_RELEASE_LOG_IF_ALLOWED("cancelFetch: %s", fetchIdentifier.loggingString().utf8().data());
133 auto* worker = server().activeWorkerFromRegistrationID(serviceWorkerRegistrationIdentifier);
134 if (!worker || !worker->isRunning())
135 return;
136
137 auto serviceWorkerIdentifier = worker->identifier();
138 server().runServiceWorkerIfNecessary(serviceWorkerIdentifier, [weakThis = makeWeakPtr(this), this, serviceWorkerIdentifier, fetchIdentifier](auto* contextConnection) mutable {
139 if (weakThis && contextConnection)
140 static_cast<WebSWServerToContextConnection&>(*contextConnection).cancelFetch(this->identifier(), fetchIdentifier, serviceWorkerIdentifier);
141 });
142}
143
144void WebSWServerConnection::continueDidReceiveFetchResponse(ServiceWorkerRegistrationIdentifier serviceWorkerRegistrationIdentifier, FetchIdentifier fetchIdentifier)
145{
146 auto* worker = server().activeWorkerFromRegistrationID(serviceWorkerRegistrationIdentifier);
147 if (!worker || !worker->isRunning())
148 return;
149
150 auto serviceWorkerIdentifier = worker->identifier();
151 server().runServiceWorkerIfNecessary(serviceWorkerIdentifier, [weakThis = makeWeakPtr(this), this, serviceWorkerIdentifier, fetchIdentifier](auto* contextConnection) mutable {
152 if (weakThis && contextConnection)
153 static_cast<WebSWServerToContextConnection&>(*contextConnection).continueDidReceiveFetchResponse(this->identifier(), fetchIdentifier, serviceWorkerIdentifier);
154 });
155}
156
157void WebSWServerConnection::startFetch(ServiceWorkerRegistrationIdentifier serviceWorkerRegistrationIdentifier, FetchIdentifier fetchIdentifier, ResourceRequest&& request, FetchOptions&& options, IPC::FormDataReference&& formData, String&& referrer)
158{
159 auto* worker = server().activeWorkerFromRegistrationID(serviceWorkerRegistrationIdentifier);
160 if (!worker) {
161 SWSERVERCONNECTION_RELEASE_LOG_ERROR_IF_ALLOWED("startFetch: fetchIdentifier: %s -> DidNotHandle because no active worker", fetchIdentifier.loggingString().utf8().data());
162 m_contentConnection->send(Messages::ServiceWorkerClientFetch::DidNotHandle { }, fetchIdentifier);
163 return;
164 }
165 auto serviceWorkerIdentifier = worker->identifier();
166
167 auto runServerWorkerAndStartFetch = [weakThis = makeWeakPtr(this), this, fetchIdentifier, serviceWorkerIdentifier, request = WTFMove(request), options = WTFMove(options), formData = WTFMove(formData), referrer = WTFMove(referrer)](bool success) mutable {
168 if (!weakThis)
169 return;
170
171 if (!success) {
172 SWSERVERCONNECTION_RELEASE_LOG_ERROR_IF_ALLOWED("startFetch: fetchIdentifier: %s DidNotHandle because worker did not become activated", fetchIdentifier.loggingString().utf8().data());
173 m_contentConnection->send(Messages::ServiceWorkerClientFetch::DidNotHandle { }, fetchIdentifier);
174 return;
175 }
176
177 auto* worker = server().workerByID(serviceWorkerIdentifier);
178 if (!worker) {
179 m_contentConnection->send(Messages::ServiceWorkerClientFetch::DidNotHandle { }, fetchIdentifier);
180 return;
181 }
182
183 if (!worker->contextConnection())
184 m_networkProcess->createServerToContextConnection(worker->registrableDomain(), server().sessionID());
185
186 server().runServiceWorkerIfNecessary(serviceWorkerIdentifier, [weakThis = WTFMove(weakThis), this, fetchIdentifier, serviceWorkerIdentifier, request = WTFMove(request), options = WTFMove(options), formData = WTFMove(formData), referrer = WTFMove(referrer)](auto* contextConnection) {
187 if (!weakThis)
188 return;
189
190 if (contextConnection) {
191 SWSERVERCONNECTION_RELEASE_LOG_IF_ALLOWED("startFetch: Starting fetch %s via service worker %s", fetchIdentifier.loggingString().utf8().data(), serviceWorkerIdentifier.loggingString().utf8().data());
192 static_cast<WebSWServerToContextConnection&>(*contextConnection).startFetch(m_sessionID, m_contentConnection.get(), this->identifier(), fetchIdentifier, serviceWorkerIdentifier, request, options, formData, referrer);
193 } else {
194 SWSERVERCONNECTION_RELEASE_LOG_ERROR_IF_ALLOWED("startFetch: fetchIdentifier: %s DidNotHandle because failed to run service worker", fetchIdentifier.loggingString().utf8().data());
195 m_contentConnection->send(Messages::ServiceWorkerClientFetch::DidNotHandle { }, fetchIdentifier);
196 }
197 });
198 };
199
200 if (worker->state() == ServiceWorkerState::Activating) {
201 worker->whenActivated(WTFMove(runServerWorkerAndStartFetch));
202 return;
203 }
204 ASSERT(worker->state() == ServiceWorkerState::Activated);
205 runServerWorkerAndStartFetch(true);
206}
207
208void WebSWServerConnection::postMessageToServiceWorker(ServiceWorkerIdentifier destinationIdentifier, MessageWithMessagePorts&& message, const ServiceWorkerOrClientIdentifier& sourceIdentifier)
209{
210 auto* destinationWorker = server().workerByID(destinationIdentifier);
211 if (!destinationWorker)
212 return;
213
214 Optional<ServiceWorkerOrClientData> sourceData;
215 WTF::switchOn(sourceIdentifier, [&](ServiceWorkerIdentifier identifier) {
216 if (auto* sourceWorker = server().workerByID(identifier))
217 sourceData = ServiceWorkerOrClientData { sourceWorker->data() };
218 }, [&](ServiceWorkerClientIdentifier identifier) {
219 if (auto clientData = destinationWorker->findClientByIdentifier(identifier))
220 sourceData = ServiceWorkerOrClientData { *clientData };
221 });
222
223 if (!sourceData)
224 return;
225
226 if (!destinationWorker->contextConnection())
227 m_networkProcess->createServerToContextConnection(destinationWorker->registrableDomain(), server().sessionID());
228
229 // It's possible this specific worker cannot be re-run (e.g. its registration has been removed)
230 server().runServiceWorkerIfNecessary(destinationIdentifier, [destinationIdentifier, message = WTFMove(message), sourceData = WTFMove(*sourceData)](auto* contextConnection) mutable {
231 if (contextConnection)
232 sendToContextProcess(*contextConnection, Messages::WebSWContextManagerConnection::PostMessageToServiceWorker { destinationIdentifier, WTFMove(message), WTFMove(sourceData) });
233 });
234}
235
236void WebSWServerConnection::scheduleJobInServer(ServiceWorkerJobData&& jobData)
237{
238 RegistrableDomain registrableDomain(jobData.scriptURL);
239 if (!m_networkProcess->serverToContextConnectionForRegistrableDomain(registrableDomain))
240 m_networkProcess->createServerToContextConnection(registrableDomain, server().sessionID());
241
242 SWSERVERCONNECTION_RELEASE_LOG_IF_ALLOWED("Scheduling ServiceWorker job %s in server", jobData.identifier().loggingString().utf8().data());
243 ASSERT(identifier() == jobData.connectionIdentifier());
244
245 server().scheduleJob(WTFMove(jobData));
246}
247
248void WebSWServerConnection::postMessageToServiceWorkerClient(DocumentIdentifier destinationContextIdentifier, MessageWithMessagePorts&& message, ServiceWorkerIdentifier sourceIdentifier, const String& sourceOrigin)
249{
250 auto* sourceServiceWorker = server().workerByID(sourceIdentifier);
251 if (!sourceServiceWorker)
252 return;
253
254 send(Messages::WebSWClientConnection::PostMessageToServiceWorkerClient { destinationContextIdentifier, WTFMove(message), sourceServiceWorker->data(), sourceOrigin });
255}
256
257void WebSWServerConnection::matchRegistration(uint64_t registrationMatchRequestIdentifier, const SecurityOriginData& topOrigin, const URL& clientURL)
258{
259 if (auto* registration = doRegistrationMatching(topOrigin, clientURL)) {
260 send(Messages::WebSWClientConnection::DidMatchRegistration { registrationMatchRequestIdentifier, registration->data() });
261 return;
262 }
263 send(Messages::WebSWClientConnection::DidMatchRegistration { registrationMatchRequestIdentifier, WTF::nullopt });
264}
265
266void WebSWServerConnection::registrationReady(uint64_t registrationReadyRequestIdentifier, ServiceWorkerRegistrationData&& registrationData)
267{
268 send(Messages::WebSWClientConnection::RegistrationReady { registrationReadyRequestIdentifier, WTFMove(registrationData) });
269}
270
271void WebSWServerConnection::getRegistrations(uint64_t registrationMatchRequestIdentifier, const SecurityOriginData& topOrigin, const URL& clientURL)
272{
273 auto registrations = server().getRegistrations(topOrigin, clientURL);
274 send(Messages::WebSWClientConnection::DidGetRegistrations { registrationMatchRequestIdentifier, registrations });
275}
276
277void WebSWServerConnection::registerServiceWorkerClient(SecurityOriginData&& topOrigin, ServiceWorkerClientData&& data, const Optional<ServiceWorkerRegistrationIdentifier>& controllingServiceWorkerRegistrationIdentifier, String&& userAgent)
278{
279 auto clientOrigin = ClientOrigin { WTFMove(topOrigin), SecurityOriginData::fromURL(data.url) };
280 m_clientOrigins.add(data.identifier, clientOrigin);
281 server().registerServiceWorkerClient(WTFMove(clientOrigin), WTFMove(data), controllingServiceWorkerRegistrationIdentifier, WTFMove(userAgent));
282
283 if (!m_isThrottleable)
284 updateThrottleState();
285}
286
287void WebSWServerConnection::unregisterServiceWorkerClient(const ServiceWorkerClientIdentifier& clientIdentifier)
288{
289 auto iterator = m_clientOrigins.find(clientIdentifier);
290 if (iterator == m_clientOrigins.end())
291 return;
292
293 server().unregisterServiceWorkerClient(iterator->value, clientIdentifier);
294 m_clientOrigins.remove(iterator);
295
296 if (!m_isThrottleable)
297 updateThrottleState();
298}
299
300bool WebSWServerConnection::hasMatchingClient(const RegistrableDomain& domain) const
301{
302 return WTF::anyOf(m_clientOrigins.values(), [&domain](auto& origin) {
303 return domain.matches(origin.clientOrigin);
304 });
305}
306
307bool WebSWServerConnection::computeThrottleState(const RegistrableDomain& domain) const
308{
309 return WTF::allOf(server().connections().values(), [&domain](auto& serverConnection) {
310 auto& connection = static_cast<WebSWServerConnection&>(*serverConnection);
311 return connection.isThrottleable() || !connection.hasMatchingClient(domain);
312 });
313}
314
315void WebSWServerConnection::setThrottleState(bool isThrottleable)
316{
317 m_isThrottleable = isThrottleable;
318 updateThrottleState();
319}
320
321void WebSWServerConnection::updateThrottleState()
322{
323 HashSet<SecurityOriginData> origins;
324 for (auto& origin : m_clientOrigins.values())
325 origins.add(origin.clientOrigin);
326
327 for (auto& origin : origins) {
328 if (auto* contextConnection = SWServerToContextConnection::connectionForRegistrableDomain(RegistrableDomain { origin })) {
329 auto& connection = static_cast<WebSWServerToContextConnection&>(*contextConnection);
330
331 if (connection.isThrottleable() == m_isThrottleable)
332 continue;
333 bool newThrottleState = computeThrottleState(connection.registrableDomain());
334 if (connection.isThrottleable() == newThrottleState)
335 continue;
336 connection.setThrottleState(newThrottleState);
337 }
338 }
339}
340
341void WebSWServerConnection::serverToContextConnectionCreated(WebCore::SWServerToContextConnection& contextConnection)
342{
343 auto& connection = static_cast<WebSWServerToContextConnection&>(contextConnection);
344 connection.setThrottleState(computeThrottleState(connection.registrableDomain()));
345}
346
347void WebSWServerConnection::syncTerminateWorkerFromClient(WebCore::ServiceWorkerIdentifier&& identifier, CompletionHandler<void()>&& completionHandler)
348{
349 syncTerminateWorker(WTFMove(identifier));
350 completionHandler();
351}
352
353template<typename U> void WebSWServerConnection::sendToContextProcess(WebCore::SWServerToContextConnection& connection, U&& message)
354{
355 static_cast<WebSWServerToContextConnection&>(connection).send(WTFMove(message));
356}
357
358} // namespace WebKit
359
360#endif // ENABLE(SERVICE_WORKER)
361