Move URL from WebCore to WTF
[WebKit-https.git] / Source / WebCore / workers / service / ServiceWorkerJob.cpp
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 "ServiceWorkerJob.h"
28
29 #if ENABLE(SERVICE_WORKER)
30
31 #include "HTTPHeaderNames.h"
32 #include "JSDOMPromiseDeferred.h"
33 #include "MIMETypeRegistry.h"
34 #include "ResourceError.h"
35 #include "ResourceResponse.h"
36 #include "ServiceWorkerJobData.h"
37 #include "ServiceWorkerRegistration.h"
38
39 namespace WebCore {
40
41 ServiceWorkerJob::ServiceWorkerJob(ServiceWorkerJobClient& client, RefPtr<DeferredPromise>&& promise, ServiceWorkerJobData&& jobData)
42     : m_client(client)
43     , m_jobData(WTFMove(jobData))
44     , m_promise(WTFMove(promise))
45     , m_contextIdentifier(client.contextIdentifier())
46 {
47 }
48
49 ServiceWorkerJob::~ServiceWorkerJob()
50 {
51     ASSERT(m_creationThread.ptr() == &Thread::current());
52 }
53
54 void ServiceWorkerJob::failedWithException(const Exception& exception)
55 {
56     ASSERT(m_creationThread.ptr() == &Thread::current());
57     ASSERT(!m_completed);
58
59     m_completed = true;
60     m_client->jobFailedWithException(*this, exception);
61 }
62
63 void ServiceWorkerJob::resolvedWithRegistration(ServiceWorkerRegistrationData&& data, ShouldNotifyWhenResolved shouldNotifyWhenResolved)
64 {
65     ASSERT(m_creationThread.ptr() == &Thread::current());
66     ASSERT(!m_completed);
67
68     m_completed = true;
69     m_client->jobResolvedWithRegistration(*this, WTFMove(data), shouldNotifyWhenResolved);
70 }
71
72 void ServiceWorkerJob::resolvedWithUnregistrationResult(bool unregistrationResult)
73 {
74     ASSERT(m_creationThread.ptr() == &Thread::current());
75     ASSERT(!m_completed);
76
77     m_completed = true;
78     m_client->jobResolvedWithUnregistrationResult(*this, unregistrationResult);
79 }
80
81 void ServiceWorkerJob::startScriptFetch(FetchOptions::Cache cachePolicy)
82 {
83     ASSERT(m_creationThread.ptr() == &Thread::current());
84     ASSERT(!m_completed);
85
86     m_client->startScriptFetchForJob(*this, cachePolicy);
87 }
88
89 void ServiceWorkerJob::fetchScriptWithContext(ScriptExecutionContext& context, FetchOptions::Cache cachePolicy)
90 {
91     ASSERT(m_creationThread.ptr() == &Thread::current());
92     ASSERT(!m_completed);
93
94     // FIXME: WorkerScriptLoader is the wrong loader class to use here, but there's nothing else better right now.
95     m_scriptLoader = WorkerScriptLoader::create();
96
97     ResourceRequest request { m_jobData.scriptURL };
98     request.setInitiatorIdentifier(context.resourceRequestIdentifier());
99     request.addHTTPHeaderField("Service-Worker"_s, "script"_s);
100
101     FetchOptions options;
102     options.mode = FetchOptions::Mode::SameOrigin;
103     options.cache = cachePolicy;
104     options.redirect = FetchOptions::Redirect::Error;
105     options.destination = FetchOptions::Destination::Serviceworker;
106     m_scriptLoader->loadAsynchronously(context, WTFMove(request), WTFMove(options), ContentSecurityPolicyEnforcement::DoNotEnforce, ServiceWorkersMode::None, *this);
107 }
108
109 void ServiceWorkerJob::didReceiveResponse(unsigned long, const ResourceResponse& response)
110 {
111     ASSERT(m_creationThread.ptr() == &Thread::current());
112     ASSERT(!m_completed);
113     ASSERT(m_scriptLoader);
114
115     // Extract a MIME type from the response's header list. If this MIME type (ignoring parameters) is not a JavaScript MIME type, then:
116     if (!MIMETypeRegistry::isSupportedJavaScriptMIMEType(response.mimeType())) {
117         // Invoke Reject Job Promise with job and "SecurityError" DOMException.
118         Exception exception { SecurityError, "MIME Type is not a JavaScript MIME type"_s };
119         // Asynchronously complete these steps with a network error.
120         ResourceError error { errorDomainWebKitInternal, 0, response.url(), "Unexpected MIME type"_s };
121         m_client->jobFailedLoadingScript(*this, WTFMove(error), WTFMove(exception));
122         m_scriptLoader = nullptr;
123     }
124     String serviceWorkerAllowed = response.httpHeaderField(HTTPHeaderName::ServiceWorkerAllowed);
125     String maxScopeString;
126     if (serviceWorkerAllowed.isNull()) {
127         String path = m_jobData.scriptURL.path();
128         // Last part of the path is the script's filename.
129         maxScopeString = path.substring(0, path.reverseFind('/') + 1);
130     } else {
131         auto maxScope = URL(m_jobData.scriptURL, serviceWorkerAllowed);
132         maxScopeString = maxScope.path();
133     }
134     String scopeString = m_jobData.scopeURL.path();
135     if (!scopeString.startsWith(maxScopeString)) {
136         Exception exception { SecurityError, "Scope URL should start with the given script URL"_s };
137         ResourceError error { errorDomainWebKitInternal, 0, response.url(), "Scope URL should start with the given script URL"_s };
138         m_client->jobFailedLoadingScript(*this, WTFMove(error), WTFMove(exception));
139         m_scriptLoader = nullptr;
140     }
141 }
142
143 void ServiceWorkerJob::notifyFinished()
144 {
145     ASSERT(m_creationThread.ptr() == &Thread::current());
146     ASSERT(m_scriptLoader);
147     
148     if (!m_scriptLoader->failed())
149         m_client->jobFinishedLoadingScript(*this, m_scriptLoader->script(), m_scriptLoader->contentSecurityPolicy());
150     else {
151         auto& error =  m_scriptLoader->error();
152         ASSERT(!error.isNull());
153         m_client->jobFailedLoadingScript(*this, error, std::nullopt);
154     }
155
156     m_scriptLoader = nullptr;
157 }
158
159 void ServiceWorkerJob::cancelPendingLoad()
160 {
161     if (!m_scriptLoader)
162         return;
163     m_scriptLoader->cancel();
164     m_scriptLoader = nullptr;
165 }
166
167 } // namespace WebCore
168
169 #endif // ENABLE(SERVICE_WORKER)