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 "WebSWContextManagerConnection.h"
28
29#if ENABLE(SERVICE_WORKER)
30
31#include "DataReference.h"
32#include "FormDataReference.h"
33#include "Logging.h"
34#include "NetworkProcessMessages.h"
35#include "ServiceWorkerFetchTaskMessages.h"
36#include "WebCacheStorageProvider.h"
37#include "WebCoreArgumentCoders.h"
38#include "WebDatabaseProvider.h"
39#include "WebDocumentLoader.h"
40#include "WebPreferencesKeys.h"
41#include "WebPreferencesStore.h"
42#include "WebProcess.h"
43#include "WebProcessPoolMessages.h"
44#include "WebSWServerToContextConnectionMessages.h"
45#include "WebServiceWorkerFetchTaskClient.h"
46#include "WebSocketProvider.h"
47#include <WebCore/EditorClient.h>
48#include <WebCore/EmptyClients.h>
49#include <WebCore/EmptyFrameLoaderClient.h>
50#include <WebCore/LibWebRTCProvider.h>
51#include <WebCore/MessageWithMessagePorts.h>
52#include <WebCore/PageConfiguration.h>
53#include <WebCore/RuntimeEnabledFeatures.h>
54#include <WebCore/SerializedScriptValue.h>
55#include <WebCore/ServiceWorkerClientData.h>
56#include <WebCore/ServiceWorkerClientIdentifier.h>
57#include <WebCore/ServiceWorkerClientQueryOptions.h>
58#include <WebCore/ServiceWorkerJobDataIdentifier.h>
59#include <WebCore/UserAgent.h>
60#include <pal/SessionID.h>
61#include <wtf/ProcessID.h>
62
63#if USE(QUICK_LOOK)
64#include <WebCore/PreviewLoaderClient.h>
65#endif
66
67namespace WebKit {
68using namespace PAL;
69using namespace WebCore;
70
71static const Seconds asyncWorkerTerminationTimeout { 10_s };
72static const Seconds syncWorkerTerminationTimeout { 100_ms }; // Only used by layout tests.
73
74class ServiceWorkerFrameLoaderClient final : public EmptyFrameLoaderClient {
75public:
76 ServiceWorkerFrameLoaderClient(WebSWContextManagerConnection& connection, PAL::SessionID sessionID, WebCore::PageIdentifier pageID, uint64_t frameID, const String& userAgent)
77 : m_connection(connection)
78 , m_sessionID(sessionID)
79 , m_pageID(pageID)
80 , m_frameID(frameID)
81 , m_userAgent(userAgent)
82 {
83 }
84
85 void setUserAgent(String&& userAgent) { m_userAgent = WTFMove(userAgent); }
86
87private:
88 Ref<DocumentLoader> createDocumentLoader(const ResourceRequest& request, const SubstituteData& substituteData) final
89 {
90 return WebDocumentLoader::create(request, substituteData);
91 }
92
93 void frameLoaderDestroyed() final { m_connection.removeFrameLoaderClient(*this); }
94
95 bool shouldUseCredentialStorage(DocumentLoader*, unsigned long) final { return true; }
96
97 PAL::SessionID sessionID() const final { return m_sessionID; }
98 Optional<WebCore::PageIdentifier> pageID() const final { return m_pageID; }
99 Optional<uint64_t> frameID() const final { return m_frameID; }
100 String userAgent(const URL&) final { return m_userAgent; }
101
102 WebSWContextManagerConnection& m_connection;
103 PAL::SessionID m_sessionID;
104 WebCore::PageIdentifier m_pageID;
105 uint64_t m_frameID { 0 };
106 String m_userAgent;
107};
108
109WebSWContextManagerConnection::WebSWContextManagerConnection(Ref<IPC::Connection>&& connection, uint64_t pageGroupID, PageIdentifier pageID, const WebPreferencesStore& store)
110 : m_connectionToNetworkProcess(WTFMove(connection))
111 , m_pageGroupID(pageGroupID)
112 , m_pageID(pageID)
113#if PLATFORM(COCOA)
114 , m_userAgent(standardUserAgentWithApplicationName({ }))
115#else
116 , m_userAgent(standardUserAgent())
117#endif
118{
119 updatePreferencesStore(store);
120}
121
122WebSWContextManagerConnection::~WebSWContextManagerConnection() = default;
123
124void WebSWContextManagerConnection::updatePreferencesStore(const WebPreferencesStore& store)
125{
126 RuntimeEnabledFeatures::sharedFeatures().setServiceWorkerEnabled(true);
127 RuntimeEnabledFeatures::sharedFeatures().setCacheAPIEnabled(store.getBoolValueForKey(WebPreferencesKey::cacheAPIEnabledKey()));
128 RuntimeEnabledFeatures::sharedFeatures().setFetchAPIEnabled(store.getBoolValueForKey(WebPreferencesKey::fetchAPIEnabledKey()));
129 RuntimeEnabledFeatures::sharedFeatures().setUserTimingEnabled(store.getBoolValueForKey(WebPreferencesKey::userTimingEnabledKey()));
130 RuntimeEnabledFeatures::sharedFeatures().setResourceTimingEnabled(store.getBoolValueForKey(WebPreferencesKey::resourceTimingEnabledKey()));
131 RuntimeEnabledFeatures::sharedFeatures().setFetchAPIKeepAliveEnabled(store.getBoolValueForKey(WebPreferencesKey::fetchAPIKeepAliveEnabledKey()));
132 RuntimeEnabledFeatures::sharedFeatures().setRestrictedHTTPResponseAccess(store.getBoolValueForKey(WebPreferencesKey::restrictedHTTPResponseAccessKey()));
133 RuntimeEnabledFeatures::sharedFeatures().setServerTimingEnabled(store.getBoolValueForKey(WebPreferencesKey::serverTimingEnabledKey()));
134 RuntimeEnabledFeatures::sharedFeatures().setIsSecureContextAttributeEnabled(store.getBoolValueForKey(WebPreferencesKey::isSecureContextAttributeEnabledKey()));
135
136 m_storageBlockingPolicy = static_cast<SecurityOrigin::StorageBlockingPolicy>(store.getUInt32ValueForKey(WebPreferencesKey::storageBlockingPolicyKey()));
137}
138
139void WebSWContextManagerConnection::installServiceWorker(const ServiceWorkerContextData& data, SessionID sessionID, String&& userAgent)
140{
141 LOG(ServiceWorker, "WebSWContextManagerConnection::installServiceWorker for worker %s", data.serviceWorkerIdentifier.loggingString().utf8().data());
142
143 auto pageConfiguration = pageConfigurationWithEmptyClients();
144
145#if ENABLE(INDEXED_DATABASE)
146 pageConfiguration.databaseProvider = WebDatabaseProvider::getOrCreate(m_pageGroupID);
147#endif
148
149 auto effectiveUserAgent = WTFMove(userAgent);
150 if (effectiveUserAgent.isNull())
151 effectiveUserAgent = m_userAgent;
152
153 // FIXME: This method should be moved directly to WebCore::SWContextManager::Connection
154 // If it weren't for ServiceWorkerFrameLoaderClient's dependence on WebDocumentLoader, this could already happen.
155 auto frameLoaderClient = std::make_unique<ServiceWorkerFrameLoaderClient>(*this, sessionID, m_pageID, ++m_previousServiceWorkerID, effectiveUserAgent);
156 pageConfiguration.loaderClientForMainFrame = frameLoaderClient.get();
157 m_loaders.add(WTFMove(frameLoaderClient));
158
159 auto serviceWorkerThreadProxy = ServiceWorkerThreadProxy::create(WTFMove(pageConfiguration), data, sessionID, WTFMove(effectiveUserAgent), WebProcess::singleton().cacheStorageProvider(), m_storageBlockingPolicy);
160 SWContextManager::singleton().registerServiceWorkerThreadForInstall(WTFMove(serviceWorkerThreadProxy));
161
162 LOG(ServiceWorker, "Context process PID: %i created worker thread\n", getCurrentProcessID());
163}
164
165void WebSWContextManagerConnection::setUserAgent(String&& userAgent)
166{
167 m_userAgent = WTFMove(userAgent);
168}
169
170void WebSWContextManagerConnection::removeFrameLoaderClient(ServiceWorkerFrameLoaderClient& client)
171{
172 auto result = m_loaders.remove(&client);
173 ASSERT_UNUSED(result, result);
174}
175
176void WebSWContextManagerConnection::serviceWorkerStartedWithMessage(Optional<ServiceWorkerJobDataIdentifier> jobDataIdentifier, ServiceWorkerIdentifier serviceWorkerIdentifier, const String& exceptionMessage)
177{
178 if (exceptionMessage.isEmpty())
179 m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::ScriptContextStarted(jobDataIdentifier, serviceWorkerIdentifier), 0);
180 else
181 m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::ScriptContextFailedToStart(jobDataIdentifier, serviceWorkerIdentifier, exceptionMessage), 0);
182}
183
184static inline bool isValidFetch(const ResourceRequest& request, const FetchOptions& options, const URL& serviceWorkerURL, const String& referrer)
185{
186 // For exotic service workers, do not enforce checks.
187 if (!serviceWorkerURL.protocolIsInHTTPFamily())
188 return true;
189
190 if (options.mode == FetchOptions::Mode::Navigate) {
191 if (!protocolHostAndPortAreEqual(request.url(), serviceWorkerURL)) {
192 RELEASE_LOG_ERROR(ServiceWorker, "Should not intercept a navigation load that is not same-origin as the service worker URL");
193 RELEASE_ASSERT_WITH_MESSAGE(request.url().host() == serviceWorkerURL.host(), "Hosts do not match");
194 RELEASE_ASSERT_WITH_MESSAGE(request.url().protocol() == serviceWorkerURL.protocol(), "Protocols do not match");
195 RELEASE_ASSERT_WITH_MESSAGE(request.url().port() == serviceWorkerURL.port(), "Ports do not match");
196 return false;
197 }
198 return true;
199 }
200
201 String origin = request.httpOrigin();
202 URL url { URL(), origin.isEmpty() ? referrer : origin };
203 if (url.protocolIsInHTTPFamily() && !protocolHostAndPortAreEqual(url, serviceWorkerURL)) {
204 RELEASE_LOG_ERROR(ServiceWorker, "Should not intercept a non navigation load that is not originating from a same-origin context as the service worker URL");
205 ASSERT(url.host() == serviceWorkerURL.host());
206 ASSERT(url.protocol() == serviceWorkerURL.protocol());
207 ASSERT(url.port() == serviceWorkerURL.port());
208 return false;
209 }
210 return true;
211}
212
213void WebSWContextManagerConnection::cancelFetch(SWServerConnectionIdentifier serverConnectionIdentifier, ServiceWorkerIdentifier serviceWorkerIdentifier, FetchIdentifier fetchIdentifier)
214{
215 if (auto* serviceWorkerThreadProxy = SWContextManager::singleton().serviceWorkerThreadProxy(serviceWorkerIdentifier))
216 serviceWorkerThreadProxy->cancelFetch(serverConnectionIdentifier, fetchIdentifier);
217}
218
219void WebSWContextManagerConnection::continueDidReceiveFetchResponse(SWServerConnectionIdentifier serverConnectionIdentifier, ServiceWorkerIdentifier serviceWorkerIdentifier, FetchIdentifier fetchIdentifier)
220{
221 if (auto* serviceWorkerThreadProxy = SWContextManager::singleton().serviceWorkerThreadProxy(serviceWorkerIdentifier))
222 serviceWorkerThreadProxy->continueDidReceiveFetchResponse(serverConnectionIdentifier, fetchIdentifier);
223}
224
225void WebSWContextManagerConnection::startFetch(SWServerConnectionIdentifier serverConnectionIdentifier, ServiceWorkerIdentifier serviceWorkerIdentifier, FetchIdentifier fetchIdentifier, ResourceRequest&& request, FetchOptions&& options, IPC::FormDataReference&& formData, String&& referrer)
226{
227 auto* serviceWorkerThreadProxy = SWContextManager::singleton().serviceWorkerThreadProxy(serviceWorkerIdentifier);
228 if (!serviceWorkerThreadProxy) {
229 m_connectionToNetworkProcess->send(Messages::ServiceWorkerFetchTask::DidNotHandle { }, fetchIdentifier);
230 return;
231 }
232
233 if (!isValidFetch(request, options, serviceWorkerThreadProxy->scriptURL(), referrer)) {
234 m_connectionToNetworkProcess->send(Messages::ServiceWorkerFetchTask::DidNotHandle { }, fetchIdentifier);
235 return;
236 }
237
238 auto client = WebServiceWorkerFetchTaskClient::create(m_connectionToNetworkProcess.copyRef(), serviceWorkerIdentifier, serverConnectionIdentifier, fetchIdentifier, request.requester() == ResourceRequest::Requester::Main);
239 Optional<ServiceWorkerClientIdentifier> clientId;
240 if (options.clientIdentifier)
241 clientId = ServiceWorkerClientIdentifier { serverConnectionIdentifier, options.clientIdentifier.value() };
242
243 request.setHTTPBody(formData.takeData());
244 serviceWorkerThreadProxy->startFetch(serverConnectionIdentifier, fetchIdentifier, WTFMove(client), WTFMove(clientId), WTFMove(request), WTFMove(referrer), WTFMove(options));
245}
246
247void WebSWContextManagerConnection::postMessageToServiceWorker(ServiceWorkerIdentifier destinationIdentifier, MessageWithMessagePorts&& message, ServiceWorkerOrClientData&& sourceData)
248{
249 SWContextManager::singleton().postMessageToServiceWorker(destinationIdentifier, WTFMove(message), WTFMove(sourceData));
250}
251
252void WebSWContextManagerConnection::fireInstallEvent(ServiceWorkerIdentifier identifier)
253{
254 SWContextManager::singleton().fireInstallEvent(identifier);
255}
256
257void WebSWContextManagerConnection::fireActivateEvent(ServiceWorkerIdentifier identifier)
258{
259 SWContextManager::singleton().fireActivateEvent(identifier);
260}
261
262void WebSWContextManagerConnection::terminateWorker(ServiceWorkerIdentifier identifier)
263{
264 SWContextManager::singleton().terminateWorker(identifier, asyncWorkerTerminationTimeout, nullptr);
265}
266
267void WebSWContextManagerConnection::syncTerminateWorker(ServiceWorkerIdentifier identifier, Messages::WebSWContextManagerConnection::SyncTerminateWorker::DelayedReply&& reply)
268{
269 SWContextManager::singleton().terminateWorker(identifier, syncWorkerTerminationTimeout, WTFMove(reply));
270}
271
272void WebSWContextManagerConnection::postMessageToServiceWorkerClient(const ServiceWorkerClientIdentifier& destinationIdentifier, MessageWithMessagePorts&& message, ServiceWorkerIdentifier sourceIdentifier, const String& sourceOrigin)
273{
274 // FIXME: Temporarily pipe the SW postMessage messages via the UIProcess since this is where the MessagePort registry lives
275 // and this avoids races.
276 WebProcess::singleton().send(Messages::WebProcessPool::PostMessageToServiceWorkerClient(destinationIdentifier, WTFMove(message), sourceIdentifier, sourceOrigin), 0);
277}
278
279void WebSWContextManagerConnection::didFinishInstall(Optional<ServiceWorkerJobDataIdentifier> jobDataIdentifier, ServiceWorkerIdentifier serviceWorkerIdentifier, bool wasSuccessful)
280{
281 m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::DidFinishInstall(jobDataIdentifier, serviceWorkerIdentifier, wasSuccessful), 0);
282}
283
284void WebSWContextManagerConnection::didFinishActivation(ServiceWorkerIdentifier serviceWorkerIdentifier)
285{
286 m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::DidFinishActivation(serviceWorkerIdentifier), 0);
287}
288
289void WebSWContextManagerConnection::setServiceWorkerHasPendingEvents(ServiceWorkerIdentifier serviceWorkerIdentifier, bool hasPendingEvents)
290{
291 m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::SetServiceWorkerHasPendingEvents(serviceWorkerIdentifier, hasPendingEvents), 0);
292}
293
294void WebSWContextManagerConnection::skipWaiting(ServiceWorkerIdentifier serviceWorkerIdentifier, WTF::Function<void()>&& callback)
295{
296 auto callbackID = ++m_previousRequestIdentifier;
297 m_skipWaitingRequests.add(callbackID, WTFMove(callback));
298 m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::SkipWaiting(serviceWorkerIdentifier, callbackID), 0);
299}
300
301void WebSWContextManagerConnection::setScriptResource(ServiceWorkerIdentifier serviceWorkerIdentifier, const URL& url, const ServiceWorkerContextData::ImportedScript& script)
302{
303 m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::SetScriptResource { serviceWorkerIdentifier, url, script.script, script.responseURL, script.mimeType }, 0);
304}
305
306void WebSWContextManagerConnection::workerTerminated(ServiceWorkerIdentifier serviceWorkerIdentifier)
307{
308 m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::WorkerTerminated(serviceWorkerIdentifier), 0);
309}
310
311void WebSWContextManagerConnection::findClientByIdentifier(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, ServiceWorkerClientIdentifier clientIdentifier, FindClientByIdentifierCallback&& callback)
312{
313 auto requestIdentifier = ++m_previousRequestIdentifier;
314 m_findClientByIdentifierRequests.add(requestIdentifier, WTFMove(callback));
315 m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::FindClientByIdentifier { requestIdentifier, serviceWorkerIdentifier, clientIdentifier }, 0);
316}
317
318void WebSWContextManagerConnection::findClientByIdentifierCompleted(uint64_t requestIdentifier, Optional<ServiceWorkerClientData>&& clientData, bool hasSecurityError)
319{
320 if (auto callback = m_findClientByIdentifierRequests.take(requestIdentifier)) {
321 if (hasSecurityError) {
322 callback(Exception { SecurityError });
323 return;
324 }
325 callback(WTFMove(clientData));
326 }
327}
328
329void WebSWContextManagerConnection::matchAll(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, const ServiceWorkerClientQueryOptions& options, ServiceWorkerClientsMatchAllCallback&& callback)
330{
331 auto requestIdentifier = ++m_previousRequestIdentifier;
332 m_matchAllRequests.add(requestIdentifier, WTFMove(callback));
333 m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::MatchAll { requestIdentifier, serviceWorkerIdentifier, options }, 0);
334}
335
336void WebSWContextManagerConnection::matchAllCompleted(uint64_t requestIdentifier, Vector<ServiceWorkerClientData>&& clientsData)
337{
338 if (auto callback = m_matchAllRequests.take(requestIdentifier))
339 callback(WTFMove(clientsData));
340}
341
342void WebSWContextManagerConnection::claim(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, CompletionHandler<void()>&& callback)
343{
344 auto requestIdentifier = ++m_previousRequestIdentifier;
345 m_claimRequests.add(requestIdentifier, WTFMove(callback));
346 m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::Claim { requestIdentifier, serviceWorkerIdentifier }, 0);
347}
348
349void WebSWContextManagerConnection::claimCompleted(uint64_t claimRequestIdentifier)
350{
351 if (auto callback = m_claimRequests.take(claimRequestIdentifier))
352 callback();
353}
354
355void WebSWContextManagerConnection::didFinishSkipWaiting(uint64_t callbackID)
356{
357 if (auto callback = m_skipWaitingRequests.take(callbackID))
358 callback();
359}
360
361void WebSWContextManagerConnection::terminateProcess()
362{
363 RELEASE_LOG(ServiceWorker, "Service worker process is exiting because it is no longer needed");
364 _exit(EXIT_SUCCESS);
365}
366
367void WebSWContextManagerConnection::setThrottleState(bool isThrottleable)
368{
369 RELEASE_LOG(ServiceWorker, "Service worker throttleable state is set to %d", isThrottleable);
370 m_isThrottleable = isThrottleable;
371 WebProcess::singleton().setProcessSuppressionEnabled(isThrottleable);
372}
373
374bool WebSWContextManagerConnection::isThrottleable() const
375{
376 return m_isThrottleable;
377}
378
379} // namespace WebCore
380
381#endif // ENABLE(SERVICE_WORKER)
382