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 "ServiceWorkerThreadProxy.h"
28
29#if ENABLE(SERVICE_WORKER)
30
31#include "CacheStorageProvider.h"
32#include "EventNames.h"
33#include "FetchLoader.h"
34#include "Frame.h"
35#include "FrameLoader.h"
36#include "LoaderStrategy.h"
37#include "PlatformStrategies.h"
38#include "ServiceWorkerClientIdentifier.h"
39#include "Settings.h"
40#include "WorkerGlobalScope.h"
41#include <pal/SessionID.h>
42#include <wtf/MainThread.h>
43#include <wtf/RunLoop.h>
44
45namespace WebCore {
46
47URL static inline topOriginURL(const SecurityOrigin& origin)
48{
49 URL url;
50 url.setProtocol(origin.protocol());
51 url.setHost(origin.host());
52 if (origin.port())
53 url.setPort(*origin.port());
54 return url;
55}
56
57static inline UniqueRef<Page> createPageForServiceWorker(PageConfiguration&& configuration, const ServiceWorkerContextData& data, SecurityOrigin::StorageBlockingPolicy storageBlockingPolicy, PAL::SessionID sessionID)
58{
59 auto page = makeUniqueRef<Page>(WTFMove(configuration));
60 page->setSessionID(sessionID);
61
62 auto& mainFrame = page->mainFrame();
63 mainFrame.loader().initForSynthesizedDocument({ });
64 auto document = Document::createNonRenderedPlaceholder(mainFrame, data.scriptURL);
65 document->createDOMWindow();
66
67 document->mutableSettings().setStorageBlockingPolicy(storageBlockingPolicy);
68 document->storageBlockingStateDidChange();
69
70 auto origin = data.registration.key.topOrigin().securityOrigin();
71 origin->setStorageBlockingPolicy(storageBlockingPolicy);
72
73 document->setSiteForCookies(topOriginURL(origin));
74 document->setFirstPartyForCookies(data.scriptURL);
75 document->setDomainForCachePartition(origin->domainForCachePartition());
76
77 if (auto policy = parseReferrerPolicy(data.referrerPolicy, ReferrerPolicySource::HTTPHeader))
78 document->setReferrerPolicy(*policy);
79
80 mainFrame.setDocument(WTFMove(document));
81 return page;
82}
83
84static inline IDBClient::IDBConnectionProxy* idbConnectionProxy(Document& document)
85{
86#if ENABLE(INDEXED_DATABASE)
87 return document.idbConnectionProxy();
88#else
89 return nullptr;
90#endif
91}
92
93static HashSet<ServiceWorkerThreadProxy*>& allServiceWorkerThreadProxies()
94{
95 static NeverDestroyed<HashSet<ServiceWorkerThreadProxy*>> set;
96 return set;
97}
98
99ServiceWorkerThreadProxy::ServiceWorkerThreadProxy(PageConfiguration&& pageConfiguration, const ServiceWorkerContextData& data, PAL::SessionID sessionID, String&& userAgent, CacheStorageProvider& cacheStorageProvider, SecurityOrigin::StorageBlockingPolicy storageBlockingPolicy)
100 : m_page(createPageForServiceWorker(WTFMove(pageConfiguration), data, storageBlockingPolicy, data.sessionID))
101 , m_document(*m_page->mainFrame().document())
102 , m_serviceWorkerThread(ServiceWorkerThread::create(data, sessionID, WTFMove(userAgent), *this, *this, idbConnectionProxy(m_document), m_document->socketProvider()))
103 , m_cacheStorageProvider(cacheStorageProvider)
104 , m_sessionID(sessionID)
105 , m_inspectorProxy(*this)
106{
107 static bool addedListener;
108 if (!addedListener) {
109 platformStrategies()->loaderStrategy()->addOnlineStateChangeListener(&networkStateChanged);
110 addedListener = true;
111 }
112
113 ASSERT(!allServiceWorkerThreadProxies().contains(this));
114 allServiceWorkerThreadProxies().add(this);
115
116#if ENABLE(REMOTE_INSPECTOR)
117 m_remoteDebuggable = std::make_unique<ServiceWorkerDebuggable>(*this, data);
118 m_remoteDebuggable->setRemoteDebuggingAllowed(true);
119 m_remoteDebuggable->init();
120#endif
121}
122
123ServiceWorkerThreadProxy::~ServiceWorkerThreadProxy()
124{
125 ASSERT(allServiceWorkerThreadProxies().contains(this));
126 allServiceWorkerThreadProxies().remove(this);
127}
128
129bool ServiceWorkerThreadProxy::postTaskForModeToWorkerGlobalScope(ScriptExecutionContext::Task&& task, const String& mode)
130{
131 if (m_isTerminatingOrTerminated)
132 return false;
133
134 m_serviceWorkerThread->runLoop().postTaskForMode(WTFMove(task), mode);
135 return true;
136}
137
138void ServiceWorkerThreadProxy::postTaskToLoader(ScriptExecutionContext::Task&& task)
139{
140 callOnMainThread([task = WTFMove(task), this, protectedThis = makeRef(*this)] () mutable {
141 task.performTask(m_document.get());
142 });
143}
144
145void ServiceWorkerThreadProxy::postMessageToDebugger(const String& message)
146{
147 RunLoop::main().dispatch([this, protectedThis = makeRef(*this), message = message.isolatedCopy()] {
148 // FIXME: Handle terminated case.
149 m_inspectorProxy.sendMessageFromWorkerToFrontend(message);
150 });
151}
152
153void ServiceWorkerThreadProxy::setResourceCachingDisabled(bool disabled)
154{
155 postTaskToLoader([this, protectedThis = makeRef(*this), disabled] (ScriptExecutionContext&) {
156 ASSERT(isMainThread());
157 m_page->setResourceCachingDisabled(disabled);
158 });
159}
160
161Ref<CacheStorageConnection> ServiceWorkerThreadProxy::createCacheStorageConnection()
162{
163 ASSERT(isMainThread());
164 if (!m_cacheStorageConnection)
165 m_cacheStorageConnection = m_cacheStorageProvider.createCacheStorageConnection(m_sessionID);
166 return *m_cacheStorageConnection;
167}
168
169std::unique_ptr<FetchLoader> ServiceWorkerThreadProxy::createBlobLoader(FetchLoaderClient& client, const URL& blobURL)
170{
171 auto loader = std::make_unique<FetchLoader>(client, nullptr);
172 loader->startLoadingBlobURL(m_document, blobURL);
173 if (!loader->isStarted())
174 return nullptr;
175 return loader;
176}
177
178void ServiceWorkerThreadProxy::networkStateChanged(bool isOnLine)
179{
180 for (auto* proxy : allServiceWorkerThreadProxies())
181 proxy->notifyNetworkStateChange(isOnLine);
182}
183
184void ServiceWorkerThreadProxy::notifyNetworkStateChange(bool isOnline)
185{
186 if (m_isTerminatingOrTerminated)
187 return;
188
189 postTaskForModeToWorkerGlobalScope([isOnline] (ScriptExecutionContext& context) {
190 auto& globalScope = downcast<WorkerGlobalScope>(context);
191 globalScope.setIsOnline(isOnline);
192 globalScope.dispatchEvent(Event::create(isOnline ? eventNames().onlineEvent : eventNames().offlineEvent, Event::CanBubble::No, Event::IsCancelable::No));
193 }, WorkerRunLoop::defaultMode());
194}
195
196void ServiceWorkerThreadProxy::startFetch(SWServerConnectionIdentifier connectionIdentifier, FetchIdentifier fetchIdentifier, Ref<ServiceWorkerFetch::Client>&& client, Optional<ServiceWorkerClientIdentifier>&& clientId, ResourceRequest&& request, String&& referrer, FetchOptions&& options)
197{
198 auto key = std::make_pair(connectionIdentifier, fetchIdentifier);
199
200 ASSERT(!m_ongoingFetchTasks.contains(key));
201 m_ongoingFetchTasks.add(key, client.copyRef());
202 thread().postFetchTask(WTFMove(client), WTFMove(clientId), WTFMove(request), WTFMove(referrer), WTFMove(options));
203}
204
205void ServiceWorkerThreadProxy::cancelFetch(SWServerConnectionIdentifier connectionIdentifier, FetchIdentifier fetchIdentifier)
206{
207 auto client = m_ongoingFetchTasks.take(std::make_pair(connectionIdentifier, fetchIdentifier));
208 if (!client)
209 return;
210
211 postTaskForModeToWorkerGlobalScope([client = WTFMove(client.value())] (ScriptExecutionContext&) {
212 client->cancel();
213 }, WorkerRunLoop::defaultMode());
214}
215
216void ServiceWorkerThreadProxy::continueDidReceiveFetchResponse(SWServerConnectionIdentifier connectionIdentifier, FetchIdentifier fetchIdentifier)
217{
218 auto client = m_ongoingFetchTasks.get(std::make_pair(connectionIdentifier, fetchIdentifier));
219 if (!client)
220 return;
221
222 postTaskForModeToWorkerGlobalScope([client = makeRef(*client)] (ScriptExecutionContext&) {
223 client->continueDidReceiveResponse();
224 }, WorkerRunLoop::defaultMode());
225}
226
227void ServiceWorkerThreadProxy::removeFetch(SWServerConnectionIdentifier connectionIdentifier, FetchIdentifier fetchIdentifier)
228{
229 m_ongoingFetchTasks.remove(std::make_pair(connectionIdentifier, fetchIdentifier));
230}
231
232} // namespace WebCore
233
234#endif // ENABLE(SERVICE_WORKER)
235