SWServerRegistration.cpp [plain text]
#include "config.h"
#include "SWServerRegistration.h"
#if ENABLE(SERVICE_WORKER)
#include "SWServer.h"
#include "SWServerWorker.h"
#include "ServiceWorkerTypes.h"
#include "ServiceWorkerUpdateViaCache.h"
namespace WebCore {
static ServiceWorkerRegistrationIdentifier generateServiceWorkerRegistrationIdentifier()
{
return generateObjectIdentifier<ServiceWorkerRegistrationIdentifierType>();
}
SWServerRegistration::SWServerRegistration(SWServer& server, const ServiceWorkerRegistrationKey& key, ServiceWorkerUpdateViaCache updateViaCache, const URL& scopeURL, const URL& scriptURL)
: m_identifier(generateServiceWorkerRegistrationIdentifier())
, m_registrationKey(key)
, m_updateViaCache(updateViaCache)
, m_scopeURL(scopeURL)
, m_scriptURL(scriptURL)
, m_server(server)
, m_creationTime(MonotonicTime::now())
{
m_scopeURL.removeFragmentIdentifier();
}
SWServerRegistration::~SWServerRegistration()
{
ASSERT(!m_preInstallationWorker || !m_preInstallationWorker->isRunning());
ASSERT(!m_installingWorker || !m_installingWorker->isRunning());
ASSERT(!m_waitingWorker || !m_waitingWorker->isRunning());
ASSERT(!m_activeWorker || !m_activeWorker->isRunning());
}
SWServerWorker* SWServerRegistration::getNewestWorker()
{
if (m_installingWorker)
return m_installingWorker.get();
if (m_waitingWorker)
return m_waitingWorker.get();
return m_activeWorker.get();
}
void SWServerRegistration::setPreInstallationWorker(SWServerWorker* worker)
{
m_preInstallationWorker = worker;
}
void SWServerRegistration::updateRegistrationState(ServiceWorkerRegistrationState state, SWServerWorker* worker)
{
LOG(ServiceWorker, "(%p) Updating registration state to %i with worker %p", this, (int)state, worker);
switch (state) {
case ServiceWorkerRegistrationState::Installing:
ASSERT(!m_installingWorker || !m_installingWorker->isRunning() || m_waitingWorker == m_installingWorker);
m_installingWorker = worker;
break;
case ServiceWorkerRegistrationState::Waiting:
ASSERT(!m_waitingWorker || !m_waitingWorker->isRunning() || m_activeWorker == m_waitingWorker);
m_waitingWorker = worker;
break;
case ServiceWorkerRegistrationState::Active:
ASSERT(!m_activeWorker || !m_activeWorker->isRunning());
m_activeWorker = worker;
break;
};
std::optional<ServiceWorkerData> serviceWorkerData;
if (worker)
serviceWorkerData = worker->data();
forEachConnection([&](auto& connection) {
connection.updateRegistrationStateInClient(this->identifier(), state, serviceWorkerData);
});
}
void SWServerRegistration::updateWorkerState(SWServerWorker& worker, ServiceWorkerState state)
{
LOG(ServiceWorker, "Updating worker %p state to %i (%p)", &worker, (int)state, this);
worker.setState(state);
}
void SWServerRegistration::setUpdateViaCache(ServiceWorkerUpdateViaCache updateViaCache)
{
m_updateViaCache = updateViaCache;
forEachConnection([&](auto& connection) {
connection.setRegistrationUpdateViaCache(this->identifier(), updateViaCache);
});
}
void SWServerRegistration::setLastUpdateTime(WallTime time)
{
m_lastUpdateTime = time;
forEachConnection([&](auto& connection) {
connection.setRegistrationLastUpdateTime(this->identifier(), time);
});
}
void SWServerRegistration::fireUpdateFoundEvent()
{
forEachConnection([&](auto& connection) {
connection.fireUpdateFoundEvent(this->identifier());
});
}
void SWServerRegistration::forEachConnection(const WTF::Function<void(SWServer::Connection&)>& apply)
{
for (auto connectionIdentifierWithClients : m_connectionsWithClientRegistrations.values()) {
if (auto* connection = m_server.connection(connectionIdentifierWithClients))
apply(*connection);
}
}
ServiceWorkerRegistrationData SWServerRegistration::data() const
{
std::optional<ServiceWorkerData> installingWorkerData;
if (m_installingWorker)
installingWorkerData = m_installingWorker->data();
std::optional<ServiceWorkerData> waitingWorkerData;
if (m_waitingWorker)
waitingWorkerData = m_waitingWorker->data();
std::optional<ServiceWorkerData> activeWorkerData;
if (m_activeWorker)
activeWorkerData = m_activeWorker->data();
return { m_registrationKey, identifier(), m_scopeURL, m_updateViaCache, m_lastUpdateTime, WTFMove(installingWorkerData), WTFMove(waitingWorkerData), WTFMove(activeWorkerData) };
}
void SWServerRegistration::addClientServiceWorkerRegistration(SWServerConnectionIdentifier connectionIdentifier)
{
m_connectionsWithClientRegistrations.add(connectionIdentifier);
}
void SWServerRegistration::removeClientServiceWorkerRegistration(SWServerConnectionIdentifier connectionIdentifier)
{
m_connectionsWithClientRegistrations.remove(connectionIdentifier);
}
void SWServerRegistration::addClientUsingRegistration(const ServiceWorkerClientIdentifier& clientIdentifier)
{
auto addResult = m_clientsUsingRegistration.ensure(clientIdentifier.serverConnectionIdentifier, [] {
return HashSet<DocumentIdentifier> { };
}).iterator->value.add(clientIdentifier.contextIdentifier);
ASSERT_UNUSED(addResult, addResult.isNewEntry);
}
void SWServerRegistration::removeClientUsingRegistration(const ServiceWorkerClientIdentifier& clientIdentifier)
{
auto iterator = m_clientsUsingRegistration.find(clientIdentifier.serverConnectionIdentifier);
ASSERT(iterator != m_clientsUsingRegistration.end());
if (iterator == m_clientsUsingRegistration.end())
return;
bool wasRemoved = iterator->value.remove(clientIdentifier.contextIdentifier);
ASSERT_UNUSED(wasRemoved, wasRemoved);
if (iterator->value.isEmpty())
m_clientsUsingRegistration.remove(iterator);
handleClientUnload();
}
void SWServerRegistration::notifyClientsOfControllerChange()
{
ASSERT(activeWorker());
for (auto& item : m_clientsUsingRegistration) {
if (auto* connection = m_server.connection(item.key))
connection->notifyClientsOfControllerChange(item.value, activeWorker()->data());
}
}
void SWServerRegistration::unregisterServerConnection(SWServerConnectionIdentifier serverConnectionIdentifier)
{
m_connectionsWithClientRegistrations.removeAll(serverConnectionIdentifier);
m_clientsUsingRegistration.remove(serverConnectionIdentifier);
}
bool SWServerRegistration::tryClear()
{
if (hasClientsUsingRegistration())
return false;
if (installingWorker() && installingWorker()->hasPendingEvents())
return false;
if (waitingWorker() && waitingWorker()->hasPendingEvents())
return false;
if (activeWorker() && activeWorker()->hasPendingEvents())
return false;
clear();
return true;
}
void SWServerRegistration::clear()
{
if (m_preInstallationWorker) {
ASSERT(m_preInstallationWorker->state() == ServiceWorkerState::Redundant);
m_preInstallationWorker->terminate();
m_preInstallationWorker = nullptr;
}
RefPtr<SWServerWorker> installingWorker = this->installingWorker();
if (installingWorker) {
installingWorker->terminate();
updateRegistrationState(ServiceWorkerRegistrationState::Installing, nullptr);
}
RefPtr<SWServerWorker> waitingWorker = this->waitingWorker();
if (waitingWorker) {
waitingWorker->terminate();
updateRegistrationState(ServiceWorkerRegistrationState::Waiting, nullptr);
}
RefPtr<SWServerWorker> activeWorker = this->activeWorker();
if (activeWorker) {
activeWorker->terminate();
updateRegistrationState(ServiceWorkerRegistrationState::Active, nullptr);
}
if (installingWorker)
updateWorkerState(*installingWorker, ServiceWorkerState::Redundant);
if (waitingWorker)
updateWorkerState(*waitingWorker, ServiceWorkerState::Redundant);
if (activeWorker)
updateWorkerState(*activeWorker, ServiceWorkerState::Redundant);
m_server.removeRegistration(key());
}
void SWServerRegistration::tryActivate()
{
if (!waitingWorker())
return;
if (activeWorker() && activeWorker()->state() == ServiceWorkerState::Activating)
return;
if (!activeWorker() || (!activeWorker()->hasPendingEvents() && (!hasClientsUsingRegistration() || waitingWorker()->isSkipWaitingFlagSet())))
activate();
}
void SWServerRegistration::activate()
{
if (!waitingWorker())
return;
if (auto* worker = activeWorker()) {
worker->terminate();
updateWorkerState(*worker, ServiceWorkerState::Redundant);
}
updateRegistrationState(ServiceWorkerRegistrationState::Active, waitingWorker());
updateRegistrationState(ServiceWorkerRegistrationState::Waiting, nullptr);
updateWorkerState(*activeWorker(), ServiceWorkerState::Activating);
m_server.resolveRegistrationReadyRequests(*this);
notifyClientsOfControllerChange();
ASSERT(activeWorker());
m_server.fireActivateEvent(*activeWorker());
}
void SWServerRegistration::didFinishActivation(ServiceWorkerIdentifier serviceWorkerIdentifier)
{
if (!activeWorker() || activeWorker()->identifier() != serviceWorkerIdentifier)
return;
updateWorkerState(*activeWorker(), ServiceWorkerState::Activated);
}
void SWServerRegistration::handleClientUnload()
{
if (hasClientsUsingRegistration())
return;
if (isUninstalling() && tryClear())
return;
tryActivate();
}
void SWServerRegistration::controlClient(ServiceWorkerClientIdentifier identifier)
{
ASSERT(activeWorker());
addClientUsingRegistration(identifier);
HashSet<DocumentIdentifier> identifiers;
identifiers.add(identifier.contextIdentifier);
m_server.connection(identifier.serverConnectionIdentifier)->notifyClientsOfControllerChange(identifiers, activeWorker()->data());
}
void SWServerRegistration::setIsUninstalling(bool value)
{
if (m_uninstalling == value)
return;
m_uninstalling = value;
if (!m_uninstalling && activeWorker()) {
m_server.resolveRegistrationReadyRequests(*this);
}
}
}
#endif // ENABLE(SERVICE_WORKER)