1/*
2 * Copyright (C) 2009, 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2016 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33#include "WorkerThreadableLoader.h"
34
35#include "ContentSecurityPolicy.h"
36#include "Document.h"
37#include "DocumentThreadableLoader.h"
38#include "InspectorInstrumentation.h"
39#include "Performance.h"
40#include "ResourceError.h"
41#include "ResourceRequest.h"
42#include "ResourceResponse.h"
43#include "ResourceTiming.h"
44#include "SecurityOrigin.h"
45#include "ServiceWorker.h"
46#include "ThreadableLoader.h"
47#include "WorkerGlobalScope.h"
48#include "WorkerLoaderProxy.h"
49#include "WorkerThread.h"
50#include <wtf/MainThread.h>
51#include <wtf/Vector.h>
52
53namespace WebCore {
54
55WorkerThreadableLoader::WorkerThreadableLoader(WorkerGlobalScope& workerGlobalScope, ThreadableLoaderClient& client, const String& taskMode, ResourceRequest&& request, const ThreadableLoaderOptions& options, const String& referrer)
56 : m_workerGlobalScope(workerGlobalScope)
57 , m_workerClientWrapper(ThreadableLoaderClientWrapper::create(client, options.initiator))
58 , m_bridge(*new MainThreadBridge(m_workerClientWrapper.get(), workerGlobalScope.thread().workerLoaderProxy(), taskMode, WTFMove(request), options, referrer.isEmpty() ? workerGlobalScope.url().strippedForUseAsReferrer() : referrer, workerGlobalScope))
59{
60}
61
62WorkerThreadableLoader::~WorkerThreadableLoader()
63{
64 m_bridge.destroy();
65}
66
67void WorkerThreadableLoader::loadResourceSynchronously(WorkerGlobalScope& workerGlobalScope, ResourceRequest&& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options)
68{
69 WorkerRunLoop& runLoop = workerGlobalScope.thread().runLoop();
70
71 // Create a unique mode just for this synchronous resource load.
72 String mode = makeString("loadResourceSynchronouslyMode", runLoop.createUniqueId());
73
74 auto loader = WorkerThreadableLoader::create(workerGlobalScope, client, mode, WTFMove(request), options, String());
75 MessageQueueWaitResult result = MessageQueueMessageReceived;
76 while (!loader->done() && result != MessageQueueTerminated)
77 result = runLoop.runInMode(&workerGlobalScope, mode);
78
79 if (!loader->done() && result == MessageQueueTerminated)
80 loader->cancel();
81}
82
83void WorkerThreadableLoader::cancel()
84{
85 m_bridge.cancel();
86}
87
88struct LoaderTaskOptions {
89 WTF_MAKE_STRUCT_FAST_ALLOCATED;
90
91 LoaderTaskOptions(const ThreadableLoaderOptions&, const String&, Ref<SecurityOrigin>&&);
92 ThreadableLoaderOptions options;
93 String referrer;
94 Ref<SecurityOrigin> origin;
95};
96
97LoaderTaskOptions::LoaderTaskOptions(const ThreadableLoaderOptions& options, const String& referrer, Ref<SecurityOrigin>&& origin)
98 : options(options.isolatedCopy())
99 , referrer(referrer.isolatedCopy())
100 , origin(WTFMove(origin))
101{
102}
103
104WorkerThreadableLoader::MainThreadBridge::MainThreadBridge(ThreadableLoaderClientWrapper& workerClientWrapper, WorkerLoaderProxy& loaderProxy, const String& taskMode,
105 ResourceRequest&& request, const ThreadableLoaderOptions& options, const String& outgoingReferrer, WorkerGlobalScope& globalScope)
106 : m_workerClientWrapper(&workerClientWrapper)
107 , m_loaderProxy(loaderProxy)
108 , m_taskMode(taskMode.isolatedCopy())
109 , m_workerRequestIdentifier(globalScope.createUniqueIdentifier())
110{
111 auto* securityOrigin = globalScope.securityOrigin();
112 auto* contentSecurityPolicy = globalScope.contentSecurityPolicy();
113
114 ASSERT(securityOrigin);
115 ASSERT(contentSecurityPolicy);
116
117 auto securityOriginCopy = securityOrigin->isolatedCopy();
118 auto contentSecurityPolicyCopy = std::make_unique<ContentSecurityPolicy>(globalScope.url().isolatedCopy());
119 contentSecurityPolicyCopy->copyStateFrom(contentSecurityPolicy);
120 contentSecurityPolicyCopy->copyUpgradeInsecureRequestStateFrom(*contentSecurityPolicy);
121
122 auto optionsCopy = std::make_unique<LoaderTaskOptions>(options, request.httpReferrer().isNull() ? outgoingReferrer : request.httpReferrer(), WTFMove(securityOriginCopy));
123
124 // All loads start out as Document. Inside WorkerThreadableLoader we upgrade this to a Worker load.
125 ASSERT(optionsCopy->options.initiatorContext == InitiatorContext::Document);
126 optionsCopy->options.initiatorContext = InitiatorContext::Worker;
127
128#if ENABLE(SERVICE_WORKER)
129 optionsCopy->options.serviceWorkersMode = globalScope.isServiceWorkerGlobalScope() ? ServiceWorkersMode::None : ServiceWorkersMode::All;
130 if (auto* activeServiceWorker = globalScope.activeServiceWorker())
131 optionsCopy->options.serviceWorkerRegistrationIdentifier = activeServiceWorker->registrationIdentifier();
132#endif
133
134 InspectorInstrumentation::willSendRequest(globalScope, m_workerRequestIdentifier, request);
135
136 // Can we benefit from request being an r-value to create more efficiently its isolated copy?
137 m_loaderProxy.postTaskToLoader([this, request = request.isolatedCopy(), options = WTFMove(optionsCopy), contentSecurityPolicyCopy = WTFMove(contentSecurityPolicyCopy)](ScriptExecutionContext& context) mutable {
138 ASSERT(isMainThread());
139 Document& document = downcast<Document>(context);
140
141 // FIXME: If the site requests a local resource, then this will return a non-zero value but the sync path will return a 0 value.
142 // Either this should return 0 or the other code path should call a failure callback.
143 m_mainThreadLoader = DocumentThreadableLoader::create(document, *this, WTFMove(request), options->options, WTFMove(options->origin), WTFMove(contentSecurityPolicyCopy), WTFMove(options->referrer), DocumentThreadableLoader::ShouldLogError::No);
144 ASSERT(m_mainThreadLoader || m_loadingFinished);
145 });
146}
147
148void WorkerThreadableLoader::MainThreadBridge::destroy()
149{
150 // Ensure that no more client callbacks are done in the worker context's thread.
151 clearClientWrapper();
152
153 // "delete this" and m_mainThreadLoader::deref() on the worker object's thread.
154 m_loaderProxy.postTaskToLoader([self = std::unique_ptr<WorkerThreadableLoader::MainThreadBridge>(this)] (ScriptExecutionContext& context) {
155 ASSERT(isMainThread());
156 ASSERT_UNUSED(context, context.isDocument());
157 });
158}
159
160void WorkerThreadableLoader::MainThreadBridge::cancel()
161{
162 m_loaderProxy.postTaskToLoader([this] (ScriptExecutionContext& context) {
163 ASSERT(isMainThread());
164 ASSERT_UNUSED(context, context.isDocument());
165
166 if (!m_mainThreadLoader)
167 return;
168 m_mainThreadLoader->cancel();
169 m_mainThreadLoader = nullptr;
170 });
171
172 if (m_workerClientWrapper->done()) {
173 clearClientWrapper();
174 return;
175 }
176 // Taking a ref of client wrapper as call to didFail may take out the last reference of it.
177 Ref<ThreadableLoaderClientWrapper> protectedWorkerClientWrapper(*m_workerClientWrapper);
178 // If the client hasn't reached a termination state, then transition it by sending a cancellation error.
179 // Note: no more client callbacks will be done after this method -- we clear the client wrapper to ensure that.
180 ResourceError error(ResourceError::Type::Cancellation);
181 protectedWorkerClientWrapper->didFail(error);
182 protectedWorkerClientWrapper->clearClient();
183}
184
185void WorkerThreadableLoader::MainThreadBridge::clearClientWrapper()
186{
187 m_workerClientWrapper->clearClient();
188}
189
190void WorkerThreadableLoader::MainThreadBridge::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
191{
192 m_loaderProxy.postTaskForModeToWorkerGlobalScope([protectedWorkerClientWrapper = makeRef(*m_workerClientWrapper), bytesSent, totalBytesToBeSent] (ScriptExecutionContext& context) mutable {
193 ASSERT_UNUSED(context, context.isWorkerGlobalScope());
194 protectedWorkerClientWrapper->didSendData(bytesSent, totalBytesToBeSent);
195 }, m_taskMode);
196}
197
198void WorkerThreadableLoader::MainThreadBridge::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
199{
200 m_loaderProxy.postTaskForModeToWorkerGlobalScope([protectedWorkerClientWrapper = makeRef(*m_workerClientWrapper), workerRequestIdentifier = m_workerRequestIdentifier, identifier, responseData = response.crossThreadData()] (ScriptExecutionContext& context) mutable {
201 ASSERT(context.isWorkerGlobalScope());
202 auto response = ResourceResponse::fromCrossThreadData(WTFMove(responseData));
203 protectedWorkerClientWrapper->didReceiveResponse(identifier, response);
204 InspectorInstrumentation::didReceiveResourceResponse(downcast<WorkerGlobalScope>(context), workerRequestIdentifier, response);
205 }, m_taskMode);
206}
207
208void WorkerThreadableLoader::MainThreadBridge::didReceiveData(const char* data, int dataLength)
209{
210 Vector<char> buffer(dataLength);
211 memcpy(buffer.data(), data, dataLength);
212 m_loaderProxy.postTaskForModeToWorkerGlobalScope([protectedWorkerClientWrapper = makeRef(*m_workerClientWrapper), workerRequestIdentifier = m_workerRequestIdentifier, buffer = WTFMove(buffer)] (ScriptExecutionContext& context) mutable {
213 ASSERT(context.isWorkerGlobalScope());
214 protectedWorkerClientWrapper->didReceiveData(buffer.data(), buffer.size());
215 InspectorInstrumentation::didReceiveData(downcast<WorkerGlobalScope>(context), workerRequestIdentifier, buffer.data(), buffer.size());
216 }, m_taskMode);
217}
218
219void WorkerThreadableLoader::MainThreadBridge::didFinishLoading(unsigned long identifier)
220{
221 m_loadingFinished = true;
222 m_loaderProxy.postTaskForModeToWorkerGlobalScope([protectedWorkerClientWrapper = makeRef(*m_workerClientWrapper), workerRequestIdentifier = m_workerRequestIdentifier, networkLoadMetrics = m_networkLoadMetrics.isolatedCopy(), identifier] (ScriptExecutionContext& context) mutable {
223 ASSERT(context.isWorkerGlobalScope());
224 protectedWorkerClientWrapper->didFinishLoading(identifier);
225 InspectorInstrumentation::didFinishLoading(downcast<WorkerGlobalScope>(context), workerRequestIdentifier, networkLoadMetrics);
226 }, m_taskMode);
227}
228
229void WorkerThreadableLoader::MainThreadBridge::didFail(const ResourceError& error)
230{
231 m_loadingFinished = true;
232 m_loaderProxy.postTaskForModeToWorkerGlobalScope([protectedWorkerClientWrapper = makeRef(*m_workerClientWrapper), workerRequestIdentifier = m_workerRequestIdentifier, error = error.isolatedCopy()] (ScriptExecutionContext& context) mutable {
233 ASSERT(context.isWorkerGlobalScope());
234 ThreadableLoader::logError(context, error, protectedWorkerClientWrapper->initiator());
235 protectedWorkerClientWrapper->didFail(error);
236 InspectorInstrumentation::didFailLoading(downcast<WorkerGlobalScope>(context), workerRequestIdentifier, error);
237 }, m_taskMode);
238}
239
240void WorkerThreadableLoader::MainThreadBridge::didFinishTiming(const ResourceTiming& resourceTiming)
241{
242 m_networkLoadMetrics = resourceTiming.networkLoadMetrics();
243 m_loaderProxy.postTaskForModeToWorkerGlobalScope([protectedWorkerClientWrapper = makeRef(*m_workerClientWrapper), resourceTiming = resourceTiming.isolatedCopy()] (ScriptExecutionContext& context) mutable {
244 ASSERT(context.isWorkerGlobalScope());
245 ASSERT(!resourceTiming.initiator().isEmpty());
246
247 // No need to notify clients, just add the performance timing entry.
248 downcast<WorkerGlobalScope>(context).performance().addResourceTiming(WTFMove(resourceTiming));
249 }, m_taskMode);
250}
251
252} // namespace WebCore
253