1/*
2 * Copyright (C) 2009, 2010 Google 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 are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32
33#include "core/loader/WorkerThreadableLoader.h"
34
35#include "core/dom/CrossThreadTask.h"
36#include "core/dom/Document.h"
37#include "core/loader/DocumentThreadableLoader.h"
38#include "core/loader/WorkerLoaderClientBridgeSyncHelper.h"
39#include "core/workers/WorkerGlobalScope.h"
40#include "core/workers/WorkerLoaderProxy.h"
41#include "core/workers/WorkerThread.h"
42#include "platform/network/ResourceError.h"
43#include "platform/network/ResourceRequest.h"
44#include "platform/network/ResourceResponse.h"
45#include "platform/weborigin/Referrer.h"
46#include "public/platform/Platform.h"
47#include "public/platform/WebWaitableEvent.h"
48#include "wtf/MainThread.h"
49#include "wtf/OwnPtr.h"
50#include "wtf/Vector.h"
51
52namespace blink {
53
54WorkerThreadableLoader::WorkerThreadableLoader(WorkerGlobalScope& workerGlobalScope, PassRefPtr<ThreadableLoaderClientWrapper> clientWrapper, PassOwnPtr<ThreadableLoaderClient> clientBridge, const ResourceRequest& request, const ThreadableLoaderOptions& options, const ResourceLoaderOptions& resourceLoaderOptions)
55    : m_workerGlobalScope(&workerGlobalScope)
56    , m_workerClientWrapper(clientWrapper)
57    , m_bridge(*(new MainThreadBridge(m_workerClientWrapper, clientBridge, workerGlobalScope.thread()->workerLoaderProxy(), request, options, resourceLoaderOptions, workerGlobalScope.url().strippedForUseAsReferrer())))
58{
59}
60
61WorkerThreadableLoader::~WorkerThreadableLoader()
62{
63    m_bridge.destroy();
64}
65
66void WorkerThreadableLoader::loadResourceSynchronously(WorkerGlobalScope& workerGlobalScope, const ResourceRequest& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options, const ResourceLoaderOptions& resourceLoaderOptions)
67{
68    blink::WebWaitableEvent* shutdownEvent =
69        workerGlobalScope.thread()->shutdownEvent();
70    OwnPtr<blink::WebWaitableEvent> loaderDone =
71        adoptPtr(blink::Platform::current()->createWaitableEvent());
72
73    Vector<blink::WebWaitableEvent*> events;
74    events.append(shutdownEvent);
75    events.append(loaderDone.get());
76
77    RefPtr<ThreadableLoaderClientWrapper> clientWrapper(ThreadableLoaderClientWrapper::create(&client));
78    OwnPtr<WorkerLoaderClientBridgeSyncHelper> clientBridge(WorkerLoaderClientBridgeSyncHelper::create(client, loaderDone.release()));
79
80    // This must be valid while loader is around.
81    WorkerLoaderClientBridgeSyncHelper* clientBridgePtr = clientBridge.get();
82
83    RefPtr<WorkerThreadableLoader> loader = WorkerThreadableLoader::create(workerGlobalScope, clientWrapper, clientBridge.release(), request, options, resourceLoaderOptions);
84
85    blink::WebWaitableEvent* signalled;
86    {
87        ThreadState::SafePointScope scope(ThreadState::HeapPointersOnStack);
88        signalled = blink::Platform::current()->waitMultipleEvents(events);
89    }
90    if (signalled == shutdownEvent) {
91        loader->cancel();
92        return;
93    }
94
95    clientBridgePtr->run();
96}
97
98void WorkerThreadableLoader::overrideTimeout(unsigned long timeoutMilliseconds)
99{
100    m_bridge.overrideTimeout(timeoutMilliseconds);
101}
102
103void WorkerThreadableLoader::cancel()
104{
105    m_bridge.cancel();
106}
107
108WorkerThreadableLoader::MainThreadBridge::MainThreadBridge(
109    PassRefPtr<ThreadableLoaderClientWrapper> workerClientWrapper,
110    PassOwnPtr<ThreadableLoaderClient> clientBridge,
111    WorkerLoaderProxy& loaderProxy,
112    const ResourceRequest& request,
113    const ThreadableLoaderOptions& options,
114    const ResourceLoaderOptions& resourceLoaderOptions,
115    const String& outgoingReferrer)
116    : m_clientBridge(clientBridge)
117    , m_workerClientWrapper(workerClientWrapper)
118    , m_loaderProxy(loaderProxy)
119{
120    ASSERT(m_workerClientWrapper.get());
121    ASSERT(m_clientBridge.get());
122    m_loaderProxy.postTaskToLoader(
123        createCrossThreadTask(&MainThreadBridge::mainThreadCreateLoader, AllowCrossThreadAccess(this), request, options, resourceLoaderOptions, outgoingReferrer));
124}
125
126WorkerThreadableLoader::MainThreadBridge::~MainThreadBridge()
127{
128}
129
130void WorkerThreadableLoader::MainThreadBridge::mainThreadCreateLoader(ExecutionContext* context, MainThreadBridge* thisPtr, PassOwnPtr<CrossThreadResourceRequestData> requestData, ThreadableLoaderOptions options, ResourceLoaderOptions resourceLoaderOptions, const String& outgoingReferrer)
131{
132    ASSERT(isMainThread());
133    Document* document = toDocument(context);
134
135    OwnPtr<ResourceRequest> request(ResourceRequest::adopt(requestData));
136    request->setHTTPReferrer(Referrer(outgoingReferrer, ReferrerPolicyDefault));
137    resourceLoaderOptions.requestInitiatorContext = WorkerContext;
138    thisPtr->m_mainThreadLoader = DocumentThreadableLoader::create(*document, thisPtr, *request, options, resourceLoaderOptions);
139    if (!thisPtr->m_mainThreadLoader) {
140        // DocumentThreadableLoader::create may return 0 when the document loader has been already changed.
141        thisPtr->didFail(ResourceError(errorDomainBlinkInternal, 0, request->url().string(), "Can't create DocumentThreadableLoader"));
142    }
143}
144
145void WorkerThreadableLoader::MainThreadBridge::mainThreadDestroy(ExecutionContext* context, MainThreadBridge* thisPtr)
146{
147    ASSERT(isMainThread());
148    ASSERT_UNUSED(context, context->isDocument());
149    delete thisPtr;
150}
151
152void WorkerThreadableLoader::MainThreadBridge::destroy()
153{
154    // Ensure that no more client callbacks are done in the worker context's thread.
155    clearClientWrapper();
156
157    // "delete this" and m_mainThreadLoader::deref() on the worker object's thread.
158    m_loaderProxy.postTaskToLoader(
159        createCrossThreadTask(&MainThreadBridge::mainThreadDestroy, AllowCrossThreadAccess(this)));
160}
161
162void WorkerThreadableLoader::MainThreadBridge::mainThreadOverrideTimeout(ExecutionContext* context, MainThreadBridge* thisPtr, unsigned long timeoutMilliseconds)
163{
164    ASSERT(isMainThread());
165    ASSERT_UNUSED(context, context->isDocument());
166
167    if (!thisPtr->m_mainThreadLoader)
168        return;
169    thisPtr->m_mainThreadLoader->overrideTimeout(timeoutMilliseconds);
170}
171
172void WorkerThreadableLoader::MainThreadBridge::overrideTimeout(unsigned long timeoutMilliseconds)
173{
174    m_loaderProxy.postTaskToLoader(
175        createCrossThreadTask(&MainThreadBridge::mainThreadOverrideTimeout, AllowCrossThreadAccess(this),
176            timeoutMilliseconds));
177}
178
179void WorkerThreadableLoader::MainThreadBridge::mainThreadCancel(ExecutionContext* context, MainThreadBridge* thisPtr)
180{
181    ASSERT(isMainThread());
182    ASSERT_UNUSED(context, context->isDocument());
183
184    if (!thisPtr->m_mainThreadLoader)
185        return;
186    thisPtr->m_mainThreadLoader->cancel();
187    thisPtr->m_mainThreadLoader = nullptr;
188}
189
190void WorkerThreadableLoader::MainThreadBridge::cancel()
191{
192    m_loaderProxy.postTaskToLoader(
193        createCrossThreadTask(&MainThreadBridge::mainThreadCancel, AllowCrossThreadAccess(this)));
194    ThreadableLoaderClientWrapper* clientWrapper = m_workerClientWrapper.get();
195    if (!clientWrapper->done()) {
196        // If the client hasn't reached a termination state, then transition it by sending a cancellation error.
197        // Note: no more client callbacks will be done after this method -- the clearClientWrapper() call ensures that.
198        ResourceError error(String(), 0, String(), String());
199        error.setIsCancellation(true);
200        clientWrapper->didFail(error);
201    }
202    clearClientWrapper();
203}
204
205void WorkerThreadableLoader::MainThreadBridge::clearClientWrapper()
206{
207    m_workerClientWrapper->clearClient();
208}
209
210void WorkerThreadableLoader::MainThreadBridge::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
211{
212    m_clientBridge->didSendData(bytesSent, totalBytesToBeSent);
213}
214
215void WorkerThreadableLoader::MainThreadBridge::didReceiveResponse(unsigned long identifier, const ResourceResponse& response)
216{
217    m_clientBridge->didReceiveResponse(identifier, response);
218}
219
220void WorkerThreadableLoader::MainThreadBridge::didReceiveData(const char* data, int dataLength)
221{
222    m_clientBridge->didReceiveData(data, dataLength);
223}
224
225void WorkerThreadableLoader::MainThreadBridge::didDownloadData(int dataLength)
226{
227    m_clientBridge->didDownloadData(dataLength);
228}
229
230void WorkerThreadableLoader::MainThreadBridge::didReceiveCachedMetadata(const char* data, int dataLength)
231{
232    m_clientBridge->didReceiveCachedMetadata(data, dataLength);
233}
234
235void WorkerThreadableLoader::MainThreadBridge::didFinishLoading(unsigned long identifier, double finishTime)
236{
237    m_clientBridge->didFinishLoading(identifier, finishTime);
238}
239
240void WorkerThreadableLoader::MainThreadBridge::didFail(const ResourceError& error)
241{
242    m_clientBridge->didFail(error);
243}
244
245void WorkerThreadableLoader::MainThreadBridge::didFailAccessControlCheck(const ResourceError& error)
246{
247    m_clientBridge->didFailAccessControlCheck(error);
248}
249
250void WorkerThreadableLoader::MainThreadBridge::didFailRedirectCheck()
251{
252    m_clientBridge->didFailRedirectCheck();
253}
254
255} // namespace blink
256