Unreviewed, rolling out r221461.
[WebKit-https.git] / Source / WebCore / workers / service / server / SWServerRegistration.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 "SWServerRegistration.h"
28
29 #if ENABLE(SERVICE_WORKER)
30
31 #include "ExceptionData.h"
32 #include "SWServer.h"
33 #include "SWServerWorker.h"
34 #include "SecurityOrigin.h"
35 #include "ServiceWorkerRegistrationData.h"
36 #include "WorkerType.h"
37
38 namespace WebCore {
39
40 SWServerRegistration::SWServerRegistration(SWServer& server, const ServiceWorkerRegistrationKey& key)
41     : m_jobTimer(*this, &SWServerRegistration::startNextJob)
42     , m_server(server)
43     , m_registrationKey(key)
44 {
45 }
46
47 SWServerRegistration::~SWServerRegistration()
48 {
49     ASSERT(m_jobQueue.isEmpty());
50 }
51
52 void SWServerRegistration::enqueueJob(const ServiceWorkerJobData& jobData)
53 {
54     // FIXME: Per the spec, check if this job is equivalent to the last job on the queue.
55     // If it is, stack it along with that job.
56
57     m_jobQueue.append(jobData);
58
59     if (m_currentJob)
60         return;
61
62     if (!m_jobTimer.isActive())
63         m_jobTimer.startOneShot(0_s);
64 }
65
66 void SWServerRegistration::startNextJob()
67 {
68     ASSERT(isMainThread());
69     ASSERT(!m_currentJob);
70     ASSERT(!m_jobQueue.isEmpty());
71
72     m_currentJob = std::make_unique<ServiceWorkerJobData>(m_jobQueue.takeFirst().isolatedCopy());
73
74     switch (m_currentJob->type) {
75     case ServiceWorkerJobType::Register:
76         m_server.postTask(createCrossThreadTask(*this, &SWServerRegistration::runRegisterJob, *m_currentJob));
77         return;
78     }
79
80     RELEASE_ASSERT_NOT_REACHED();
81 }
82
83 bool SWServerRegistration::isEmpty()
84 {
85     ASSERT(!isMainThread());
86
87     // Having or not-having an m_updateViaCache flag is currently
88     // the signal as to whether or not this is an empty (i.e. "new") registration.
89     // There will be a more explicit signal in the near future.
90     return !m_updateViaCache;
91 }
92
93 SWServerWorker* SWServerRegistration::getNewestWorker()
94 {
95     ASSERT(!isMainThread());
96     if (m_installingWorker)
97         return m_installingWorker.get();
98     if (m_waitingWorker)
99         return m_waitingWorker.get();
100
101     return m_activeWorker.get();
102 }
103
104 void SWServerRegistration::runRegisterJob(const ServiceWorkerJobData& job)
105 {
106     ASSERT(!isMainThread());
107     ASSERT(job.type == ServiceWorkerJobType::Register);
108
109     if (!shouldTreatAsPotentiallyTrustworthy(job.scriptURL))
110         return rejectWithExceptionOnMainThread(ExceptionData { SecurityError, ASCIILiteral("Script URL is not potentially trustworthy") });
111
112     // If the origin of job’s script url is not job’s referrer's origin, then:
113     if (!protocolHostAndPortAreEqual(job.scriptURL, job.clientCreationURL))
114         return rejectWithExceptionOnMainThread(ExceptionData { SecurityError, ASCIILiteral("Script origin does not match the registering client's origin") });
115
116     // If the origin of job’s scope url is not job’s referrer's origin, then:
117     if (!protocolHostAndPortAreEqual(job.scopeURL, job.clientCreationURL))
118         return rejectWithExceptionOnMainThread(ExceptionData { SecurityError, ASCIILiteral("Scope origin does not match the registering client's origin") });
119
120     // If registration is not null (in our parlance "empty"), then:
121     if (!isEmpty()) {
122         ASSERT(m_updateViaCache);
123
124         m_uninstalling = false;
125         auto* newestWorker = getNewestWorker();
126         if (newestWorker && equalIgnoringFragmentIdentifier(job.scriptURL, newestWorker->scriptURL()) && job.registrationOptions.updateViaCache == *m_updateViaCache) {
127             resolveWithRegistrationOnMainThread();
128             return;
129         }
130     } else {
131         m_scopeURL = job.scopeURL.isolatedCopy();
132         m_scopeURL.removeFragmentIdentifier();
133         m_updateViaCache = job.registrationOptions.updateViaCache;
134     }
135
136     runUpdateJob(job);
137 }
138
139 void SWServerRegistration::runUpdateJob(const ServiceWorkerJobData& job)
140 {
141     // If registration is null (in our parlance "empty") or registration’s uninstalling flag is set, then:
142     if (isEmpty())
143         return rejectWithExceptionOnMainThread(ExceptionData { TypeError, ASCIILiteral("Cannot update a null/nonexistent service worker registration") });
144     if (m_uninstalling)
145         return rejectWithExceptionOnMainThread(ExceptionData { TypeError, ASCIILiteral("Cannot update a service worker registration that is uninstalling") });
146
147     // 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:
148     auto* newestWorker = getNewestWorker();
149     if (newestWorker && !equalIgnoringFragmentIdentifier(job.scriptURL, newestWorker->scriptURL()))
150         return rejectWithExceptionOnMainThread(ExceptionData { TypeError, ASCIILiteral("Cannot update a service worker with a requested script URL whose newest worker has a different script URL") });
151
152     // FIXME: At this point we are ready to actually fetch the script for the worker in the registering context.
153     // For now we're still hard coding the same rejection we have so far.
154     rejectWithExceptionOnMainThread(ExceptionData { UnknownError, ASCIILiteral("serviceWorker job scheduling is not yet implemented") });
155 }
156
157 void SWServerRegistration::rejectWithExceptionOnMainThread(const ExceptionData& exception)
158 {
159     ASSERT(!isMainThread());
160     m_server.postTaskReply(createCrossThreadTask(*this, &SWServerRegistration::rejectCurrentJob, exception));
161 }
162
163 void SWServerRegistration::resolveWithRegistrationOnMainThread()
164 {
165     ASSERT(!isMainThread());
166     m_server.postTaskReply(createCrossThreadTask(*this, &SWServerRegistration::resolveCurrentJob, data()));
167 }
168
169 void SWServerRegistration::rejectCurrentJob(const ExceptionData& exceptionData)
170 {
171     ASSERT(isMainThread());
172     ASSERT(m_currentJob);
173
174     m_server.rejectJob(*m_currentJob, exceptionData);
175
176     finishCurrentJob();
177 }
178
179 void SWServerRegistration::resolveCurrentJob(const ServiceWorkerRegistrationData& data)
180 {
181     ASSERT(isMainThread());
182     ASSERT(m_currentJob);
183
184     m_server.resolveJob(*m_currentJob, data);
185
186     finishCurrentJob();
187 }
188
189 void SWServerRegistration::finishCurrentJob()
190 {
191     ASSERT(m_currentJob);
192     ASSERT(!m_jobTimer.isActive());
193
194     m_currentJob = nullptr;
195     if (m_jobQueue.isEmpty())
196         return;
197
198     startNextJob();
199 }
200
201 ServiceWorkerRegistrationData SWServerRegistration::data() const
202 {
203     return { m_registrationKey, identifier() };
204 }
205
206
207 } // namespace WebCore
208
209 #endif // ENABLE(SERVICE_WORKER)