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 | |
57 | namespace WebKit { |
58 | using namespace PAL; |
59 | using 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 | |
64 | WebSWServerConnection::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 | |
73 | WebSWServerConnection::~WebSWServerConnection() |
74 | { |
75 | m_networkProcess->unregisterSWServerConnection(*this); |
76 | for (const auto& keyValue : m_clientOrigins) |
77 | server().unregisterServiceWorkerClient(keyValue.value, keyValue.key); |
78 | } |
79 | |
80 | void WebSWServerConnection::rejectJobInClient(ServiceWorkerJobIdentifier jobIdentifier, const ExceptionData& exceptionData) |
81 | { |
82 | send(Messages::WebSWClientConnection::JobRejectedInServer(jobIdentifier, exceptionData)); |
83 | } |
84 | |
85 | void WebSWServerConnection::resolveRegistrationJobInClient(ServiceWorkerJobIdentifier jobIdentifier, const ServiceWorkerRegistrationData& registrationData, ShouldNotifyWhenResolved shouldNotifyWhenResolved) |
86 | { |
87 | send(Messages::WebSWClientConnection::RegistrationJobResolvedInServer(jobIdentifier, registrationData, shouldNotifyWhenResolved)); |
88 | } |
89 | |
90 | void WebSWServerConnection::resolveUnregistrationJobInClient(ServiceWorkerJobIdentifier jobIdentifier, const ServiceWorkerRegistrationKey& registrationKey, bool unregistrationResult) |
91 | { |
92 | send(Messages::WebSWClientConnection::UnregistrationJobResolvedInServer(jobIdentifier, unregistrationResult)); |
93 | } |
94 | |
95 | void WebSWServerConnection::startScriptFetchInClient(ServiceWorkerJobIdentifier jobIdentifier, const ServiceWorkerRegistrationKey& registrationKey, FetchOptions::Cache cachePolicy) |
96 | { |
97 | send(Messages::WebSWClientConnection::StartScriptFetchForServer(jobIdentifier, registrationKey, cachePolicy)); |
98 | } |
99 | |
100 | void WebSWServerConnection::updateRegistrationStateInClient(ServiceWorkerRegistrationIdentifier identifier, ServiceWorkerRegistrationState state, const Optional<ServiceWorkerData>& serviceWorkerData) |
101 | { |
102 | send(Messages::WebSWClientConnection::UpdateRegistrationState(identifier, state, serviceWorkerData)); |
103 | } |
104 | |
105 | void WebSWServerConnection::fireUpdateFoundEvent(ServiceWorkerRegistrationIdentifier identifier) |
106 | { |
107 | send(Messages::WebSWClientConnection::FireUpdateFoundEvent(identifier)); |
108 | } |
109 | |
110 | void WebSWServerConnection::setRegistrationLastUpdateTime(ServiceWorkerRegistrationIdentifier identifier, WallTime lastUpdateTime) |
111 | { |
112 | send(Messages::WebSWClientConnection::SetRegistrationLastUpdateTime(identifier, lastUpdateTime)); |
113 | } |
114 | |
115 | void WebSWServerConnection::setRegistrationUpdateViaCache(ServiceWorkerRegistrationIdentifier identifier, ServiceWorkerUpdateViaCache updateViaCache) |
116 | { |
117 | send(Messages::WebSWClientConnection::SetRegistrationUpdateViaCache(identifier, updateViaCache)); |
118 | } |
119 | |
120 | void WebSWServerConnection::notifyClientsOfControllerChange(const HashSet<DocumentIdentifier>& contextIdentifiers, const ServiceWorkerData& newController) |
121 | { |
122 | send(Messages::WebSWClientConnection::NotifyClientsOfControllerChange(contextIdentifiers, newController)); |
123 | } |
124 | |
125 | void WebSWServerConnection::updateWorkerStateInClient(ServiceWorkerIdentifier worker, ServiceWorkerState state) |
126 | { |
127 | send(Messages::WebSWClientConnection::UpdateWorkerState(worker, state)); |
128 | } |
129 | |
130 | void 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 | |
144 | void 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 | |
157 | void 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 | |
208 | void 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 | |
236 | void 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 | |
248 | void 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 | |
257 | void 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 | |
266 | void WebSWServerConnection::registrationReady(uint64_t registrationReadyRequestIdentifier, ServiceWorkerRegistrationData&& registrationData) |
267 | { |
268 | send(Messages::WebSWClientConnection::RegistrationReady { registrationReadyRequestIdentifier, WTFMove(registrationData) }); |
269 | } |
270 | |
271 | void 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 | |
277 | void 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 | |
287 | void 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 | |
300 | bool 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 | |
307 | bool 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 | |
315 | void WebSWServerConnection::setThrottleState(bool isThrottleable) |
316 | { |
317 | m_isThrottleable = isThrottleable; |
318 | updateThrottleState(); |
319 | } |
320 | |
321 | void 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 | |
341 | void WebSWServerConnection::serverToContextConnectionCreated(WebCore::SWServerToContextConnection& contextConnection) |
342 | { |
343 | auto& connection = static_cast<WebSWServerToContextConnection&>(contextConnection); |
344 | connection.setThrottleState(computeThrottleState(connection.registrableDomain())); |
345 | } |
346 | |
347 | void WebSWServerConnection::syncTerminateWorkerFromClient(WebCore::ServiceWorkerIdentifier&& identifier, CompletionHandler<void()>&& completionHandler) |
348 | { |
349 | syncTerminateWorker(WTFMove(identifier)); |
350 | completionHandler(); |
351 | } |
352 | |
353 | template<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 | |