| 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 "SWServerWorker.h" |
| 28 | |
| 29 | #if ENABLE(SERVICE_WORKER) |
| 30 | |
| 31 | #include "SWServer.h" |
| 32 | #include "SWServerRegistration.h" |
| 33 | #include "SWServerToContextConnection.h" |
| 34 | #include <wtf/NeverDestroyed.h> |
| 35 | |
| 36 | namespace WebCore { |
| 37 | |
| 38 | HashMap<ServiceWorkerIdentifier, SWServerWorker*>& SWServerWorker::allWorkers() |
| 39 | { |
| 40 | static NeverDestroyed<HashMap<ServiceWorkerIdentifier, SWServerWorker*>> workers; |
| 41 | return workers; |
| 42 | } |
| 43 | |
| 44 | SWServerWorker* SWServerWorker::existingWorkerForIdentifier(ServiceWorkerIdentifier identifier) |
| 45 | { |
| 46 | return allWorkers().get(identifier); |
| 47 | } |
| 48 | |
| 49 | // FIXME: Use r-value references for script and contentSecurityPolicy |
| 50 | SWServerWorker::(SWServer& server, SWServerRegistration& registration, const URL& scriptURL, const String& script, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicy, String&& referrerPolicy, WorkerType type, ServiceWorkerIdentifier identifier, HashMap<URL, ServiceWorkerContextData::ImportedScript>&& scriptResourceMap) |
| 51 | : m_server(server) |
| 52 | , m_registrationKey(registration.key()) |
| 53 | , m_data { identifier, scriptURL, ServiceWorkerState::Redundant, type, registration.identifier() } |
| 54 | , m_script(script) |
| 55 | , m_contentSecurityPolicy(contentSecurityPolicy) |
| 56 | , m_referrerPolicy(WTFMove(referrerPolicy)) |
| 57 | , m_registrableDomain(m_data.scriptURL) |
| 58 | , m_scriptResourceMap(WTFMove(scriptResourceMap)) |
| 59 | { |
| 60 | m_data.scriptURL.removeFragmentIdentifier(); |
| 61 | |
| 62 | auto result = allWorkers().add(identifier, this); |
| 63 | ASSERT_UNUSED(result, result.isNewEntry); |
| 64 | |
| 65 | ASSERT(m_server.getRegistration(m_registrationKey)); |
| 66 | } |
| 67 | |
| 68 | SWServerWorker::~SWServerWorker() |
| 69 | { |
| 70 | callWhenActivatedHandler(false); |
| 71 | |
| 72 | auto taken = allWorkers().take(identifier()); |
| 73 | ASSERT_UNUSED(taken, taken == this); |
| 74 | } |
| 75 | |
| 76 | ServiceWorkerContextData SWServerWorker::contextData() const |
| 77 | { |
| 78 | auto* registration = m_server.getRegistration(m_registrationKey); |
| 79 | ASSERT(registration); |
| 80 | |
| 81 | return { WTF::nullopt, registration->data(), m_data.identifier, m_script, m_contentSecurityPolicy, m_referrerPolicy, m_data.scriptURL, m_data.type, m_server.sessionID(), false, m_scriptResourceMap }; |
| 82 | } |
| 83 | |
| 84 | void SWServerWorker::terminate() |
| 85 | { |
| 86 | if (isRunning()) |
| 87 | m_server.terminateWorker(*this); |
| 88 | } |
| 89 | |
| 90 | const ClientOrigin& SWServerWorker::origin() const |
| 91 | { |
| 92 | if (!m_origin) |
| 93 | m_origin = ClientOrigin { m_registrationKey.topOrigin(), SecurityOriginData::fromURL(m_data.scriptURL) }; |
| 94 | |
| 95 | return *m_origin; |
| 96 | } |
| 97 | |
| 98 | SWServerToContextConnection* SWServerWorker::contextConnection() |
| 99 | { |
| 100 | return SWServerToContextConnection::connectionForRegistrableDomain(registrableDomain()); |
| 101 | } |
| 102 | |
| 103 | void SWServerWorker::scriptContextFailedToStart(const Optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, const String& message) |
| 104 | { |
| 105 | m_server.scriptContextFailedToStart(jobDataIdentifier, *this, message); |
| 106 | } |
| 107 | |
| 108 | void SWServerWorker::scriptContextStarted(const Optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier) |
| 109 | { |
| 110 | m_server.scriptContextStarted(jobDataIdentifier, *this); |
| 111 | } |
| 112 | |
| 113 | void SWServerWorker::didFinishInstall(const Optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, bool wasSuccessful) |
| 114 | { |
| 115 | m_server.didFinishInstall(jobDataIdentifier, *this, wasSuccessful); |
| 116 | } |
| 117 | |
| 118 | void SWServerWorker::didFinishActivation() |
| 119 | { |
| 120 | m_server.didFinishActivation(*this); |
| 121 | } |
| 122 | |
| 123 | void SWServerWorker::contextTerminated() |
| 124 | { |
| 125 | m_server.workerContextTerminated(*this); |
| 126 | } |
| 127 | |
| 128 | Optional<ServiceWorkerClientData> SWServerWorker::findClientByIdentifier(const ServiceWorkerClientIdentifier& clientId) const |
| 129 | { |
| 130 | return m_server.serviceWorkerClientWithOriginByID(origin(), clientId); |
| 131 | } |
| 132 | |
| 133 | void SWServerWorker::matchAll(const ServiceWorkerClientQueryOptions& options, ServiceWorkerClientsMatchAllCallback&& callback) |
| 134 | { |
| 135 | return m_server.matchAll(*this, options, WTFMove(callback)); |
| 136 | } |
| 137 | |
| 138 | String SWServerWorker::userAgent() const |
| 139 | { |
| 140 | return m_server.serviceWorkerClientUserAgent(origin()); |
| 141 | } |
| 142 | |
| 143 | void SWServerWorker::claim() |
| 144 | { |
| 145 | return m_server.claim(*this); |
| 146 | } |
| 147 | |
| 148 | void SWServerWorker::setScriptResource(URL&& url, ServiceWorkerContextData::ImportedScript&& script) |
| 149 | { |
| 150 | m_scriptResourceMap.set(WTFMove(url), WTFMove(script)); |
| 151 | } |
| 152 | |
| 153 | void SWServerWorker::skipWaiting() |
| 154 | { |
| 155 | m_isSkipWaitingFlagSet = true; |
| 156 | |
| 157 | auto* registration = m_server.getRegistration(m_registrationKey); |
| 158 | ASSERT(registration || isTerminating()); |
| 159 | if (registration) |
| 160 | registration->tryActivate(); |
| 161 | } |
| 162 | |
| 163 | void SWServerWorker::setHasPendingEvents(bool hasPendingEvents) |
| 164 | { |
| 165 | if (m_hasPendingEvents == hasPendingEvents) |
| 166 | return; |
| 167 | |
| 168 | m_hasPendingEvents = hasPendingEvents; |
| 169 | if (m_hasPendingEvents) |
| 170 | return; |
| 171 | |
| 172 | // Do tryClear/tryActivate, as per https://w3c.github.io/ServiceWorker/#wait-until-method. |
| 173 | auto* registration = m_server.getRegistration(m_registrationKey); |
| 174 | if (!registration) |
| 175 | return; |
| 176 | |
| 177 | if (registration->isUninstalling() && registration->tryClear()) |
| 178 | return; |
| 179 | registration->tryActivate(); |
| 180 | } |
| 181 | |
| 182 | void SWServerWorker::whenActivated(WTF::Function<void(bool)>&& handler) |
| 183 | { |
| 184 | if (state() == ServiceWorkerState::Activated) { |
| 185 | handler(true); |
| 186 | return; |
| 187 | } |
| 188 | m_whenActivatedHandlers.append(WTFMove(handler)); |
| 189 | } |
| 190 | |
| 191 | void SWServerWorker::setState(ServiceWorkerState state) |
| 192 | { |
| 193 | if (state == ServiceWorkerState::Redundant) |
| 194 | terminate(); |
| 195 | |
| 196 | m_data.state = state; |
| 197 | |
| 198 | auto* registration = m_server.getRegistration(m_registrationKey); |
| 199 | ASSERT(registration || state == ServiceWorkerState::Redundant); |
| 200 | if (registration) { |
| 201 | registration->forEachConnection([&](auto& connection) { |
| 202 | connection.updateWorkerStateInClient(this->identifier(), state); |
| 203 | }); |
| 204 | } |
| 205 | |
| 206 | if (state == ServiceWorkerState::Activated || state == ServiceWorkerState::Redundant) |
| 207 | callWhenActivatedHandler(state == ServiceWorkerState::Activated); |
| 208 | } |
| 209 | |
| 210 | void SWServerWorker::callWhenActivatedHandler(bool success) |
| 211 | { |
| 212 | auto whenActivatedHandlers = WTFMove(m_whenActivatedHandlers); |
| 213 | for (auto& handler : whenActivatedHandlers) |
| 214 | handler(success); |
| 215 | } |
| 216 | |
| 217 | void SWServerWorker::setState(State state) |
| 218 | { |
| 219 | ASSERT(state != State::Running || m_server.getRegistration(m_registrationKey)); |
| 220 | m_state = state; |
| 221 | } |
| 222 | |
| 223 | } // namespace WebCore |
| 224 | |
| 225 | #endif // ENABLE(SERVICE_WORKER) |
| 226 | |