2 * Copyright (C) 2017 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "SWServerRegistration.h"
29 #if ENABLE(SERVICE_WORKER)
31 #include "ExceptionData.h"
33 #include "SWServerWorker.h"
34 #include "SecurityOrigin.h"
35 #include "ServiceWorkerFetchResult.h"
36 #include "ServiceWorkerRegistrationData.h"
37 #include "ServiceWorkerUpdateViaCache.h"
38 #include "WorkerType.h"
42 SWServerRegistration::SWServerRegistration(SWServer& server, const ServiceWorkerRegistrationKey& key)
43 : m_jobTimer(*this, &SWServerRegistration::startNextJob)
45 , m_registrationKey(key)
49 SWServerRegistration::~SWServerRegistration()
51 ASSERT(m_jobQueue.isEmpty());
54 void SWServerRegistration::enqueueJob(const ServiceWorkerJobData& jobData)
56 // FIXME: Per the spec, check if this job is equivalent to the last job on the queue.
57 // If it is, stack it along with that job.
59 m_jobQueue.append(jobData);
64 if (!m_jobTimer.isActive())
65 m_jobTimer.startOneShot(0_s);
68 void SWServerRegistration::scriptFetchFinished(SWServer::Connection& connection, const ServiceWorkerFetchResult& result)
70 ASSERT(m_currentJob && m_currentJob->identifier() == result.jobIdentifier);
72 if (!result.scriptError.isNull()) {
73 rejectCurrentJob(ExceptionData { UnknownError, makeString("Script URL ", m_currentJob->scriptURL.string(), " fetch resulted in error: ", result.scriptError.localizedDescription()) });
75 // If newestWorker is null, invoke Clear Registration algorithm passing this registration as its argument.
76 // FIXME: We don't have "clear registration" yet.
81 m_lastUpdateTime = currentTime();
83 // FIXME: If the script data matches byte-for-byte with the existing newestWorker,
84 // then resolve and finish the job without doing anything further.
86 // FIXME: Support the proper worker type (classic vs module)
87 m_server.createWorker(connection, m_registrationKey, m_currentJob->scriptURL, result.script, WorkerType::Classic);
90 void SWServerRegistration::scriptContextFailedToStart(SWServer::Connection&, const String& workerID, const String& message)
92 UNUSED_PARAM(workerID);
94 rejectCurrentJob(ExceptionData { UnknownError, message });
97 void SWServerRegistration::scriptContextStarted(SWServer::Connection&, uint64_t identifier, const String& workerID)
99 UNUSED_PARAM(workerID);
100 resolveCurrentJob(ServiceWorkerRegistrationData { m_registrationKey, identifier, m_scopeURL, m_updateViaCache.value_or(ServiceWorkerUpdateViaCache::Imports) });
103 void SWServerRegistration::startNextJob()
105 ASSERT(isMainThread());
106 ASSERT(!m_currentJob);
107 ASSERT(!m_jobQueue.isEmpty());
109 m_currentJob = std::make_unique<ServiceWorkerJobData>(m_jobQueue.takeFirst().isolatedCopy());
111 switch (m_currentJob->type) {
112 case ServiceWorkerJobType::Register:
113 m_server.postTask(createCrossThreadTask(*this, &SWServerRegistration::runRegisterJob, *m_currentJob));
117 RELEASE_ASSERT_NOT_REACHED();
120 bool SWServerRegistration::isEmpty()
122 ASSERT(!isMainThread());
124 // Having or not-having an m_updateViaCache flag is currently
125 // the signal as to whether or not this is an empty (i.e. "new") registration.
126 // There will be a more explicit signal in the near future.
127 return !m_updateViaCache;
130 SWServerWorker* SWServerRegistration::getNewestWorker()
132 ASSERT(!isMainThread());
133 if (m_installingWorker)
134 return m_installingWorker.get();
136 return m_waitingWorker.get();
138 return m_activeWorker.get();
141 void SWServerRegistration::runRegisterJob(const ServiceWorkerJobData& job)
143 ASSERT(!isMainThread());
144 ASSERT(job.type == ServiceWorkerJobType::Register);
146 if (!shouldTreatAsPotentiallyTrustworthy(job.scriptURL))
147 return rejectWithExceptionOnMainThread(ExceptionData { SecurityError, ASCIILiteral("Script URL is not potentially trustworthy") });
149 // If the origin of job’s script url is not job’s referrer's origin, then:
150 if (!protocolHostAndPortAreEqual(job.scriptURL, job.clientCreationURL))
151 return rejectWithExceptionOnMainThread(ExceptionData { SecurityError, ASCIILiteral("Script origin does not match the registering client's origin") });
153 // If the origin of job’s scope url is not job’s referrer's origin, then:
154 if (!protocolHostAndPortAreEqual(job.scopeURL, job.clientCreationURL))
155 return rejectWithExceptionOnMainThread(ExceptionData { SecurityError, ASCIILiteral("Scope origin does not match the registering client's origin") });
157 // If registration is not null (in our parlance "empty"), then:
159 ASSERT(m_updateViaCache);
161 m_uninstalling = false;
162 auto* newestWorker = getNewestWorker();
163 if (newestWorker && equalIgnoringFragmentIdentifier(job.scriptURL, newestWorker->scriptURL()) && job.registrationOptions.updateViaCache == *m_updateViaCache) {
164 resolveWithRegistrationOnMainThread();
168 m_scopeURL = job.scopeURL.isolatedCopy();
169 m_scopeURL.removeFragmentIdentifier();
170 m_updateViaCache = job.registrationOptions.updateViaCache;
176 void SWServerRegistration::runUpdateJob(const ServiceWorkerJobData& job)
178 // If registration is null (in our parlance "empty") or registration’s uninstalling flag is set, then:
180 return rejectWithExceptionOnMainThread(ExceptionData { TypeError, ASCIILiteral("Cannot update a null/nonexistent service worker registration") });
182 return rejectWithExceptionOnMainThread(ExceptionData { TypeError, ASCIILiteral("Cannot update a service worker registration that is uninstalling") });
184 // If job’s job type is update, and newestWorker’s script url does not equal job’s script url with the exclude fragments flag set, then:
185 auto* newestWorker = getNewestWorker();
186 if (newestWorker && !equalIgnoringFragmentIdentifier(job.scriptURL, newestWorker->scriptURL()))
187 return rejectWithExceptionOnMainThread(ExceptionData { TypeError, ASCIILiteral("Cannot update a service worker with a requested script URL whose newest worker has a different script URL") });
189 startScriptFetchFromMainThread();
192 void SWServerRegistration::rejectWithExceptionOnMainThread(const ExceptionData& exception)
194 ASSERT(!isMainThread());
195 m_server.postTaskReply(createCrossThreadTask(*this, &SWServerRegistration::rejectCurrentJob, exception));
198 void SWServerRegistration::resolveWithRegistrationOnMainThread()
200 ASSERT(!isMainThread());
201 m_server.postTaskReply(createCrossThreadTask(*this, &SWServerRegistration::resolveCurrentJob, data()));
204 void SWServerRegistration::startScriptFetchFromMainThread()
206 ASSERT(!isMainThread());
207 m_server.postTaskReply(createCrossThreadTask(*this, &SWServerRegistration::startScriptFetchForCurrentJob));
210 void SWServerRegistration::rejectCurrentJob(const ExceptionData& exceptionData)
212 ASSERT(isMainThread());
213 ASSERT(m_currentJob);
215 m_server.rejectJob(*m_currentJob, exceptionData);
220 void SWServerRegistration::resolveCurrentJob(const ServiceWorkerRegistrationData& data)
222 ASSERT(isMainThread());
223 ASSERT(m_currentJob);
225 m_server.resolveJob(*m_currentJob, data);
230 void SWServerRegistration::startScriptFetchForCurrentJob()
232 ASSERT(isMainThread());
233 ASSERT(m_currentJob);
235 m_server.startScriptFetch(*m_currentJob);
238 void SWServerRegistration::finishCurrentJob()
240 ASSERT(m_currentJob);
241 ASSERT(!m_jobTimer.isActive());
243 m_currentJob = nullptr;
244 if (m_jobQueue.isEmpty())
250 ServiceWorkerRegistrationData SWServerRegistration::data() const
252 return { m_registrationKey, identifier(), m_scopeURL, m_updateViaCache.value_or(ServiceWorkerUpdateViaCache::Imports) };
256 } // namespace WebCore
258 #endif // ENABLE(SERVICE_WORKER)