Read registrations in from disk.
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Dec 2017 01:27:00 +0000 (01:27 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Dec 2017 01:27:00 +0000 (01:27 +0000)
https://bugs.webkit.org/show_bug.cgi?id=180543

Reviewed by Andy Estes.

No new tests (Not testable quite yet)

On launch, read through the database.

Set up enough objects in the SWServer necessary to cause fetches and postMessages to
go to a worker that has never run before.

* workers/service/ServiceWorkerContextData.cpp:
(WebCore::ServiceWorkerContextData::isolatedCopy const):
* workers/service/ServiceWorkerContextData.h:
(WebCore::ServiceWorkerContextData::encode const):
(WebCore::ServiceWorkerContextData::decode):

* workers/service/ServiceWorkerRegistrationKey.cpp:
(WebCore::ServiceWorkerRegistrationKey::toDatabaseKey const):
(WebCore::ServiceWorkerRegistrationKey::fromDatabaseKey):
* workers/service/ServiceWorkerRegistrationKey.h:

* workers/service/server/RegistrationDatabase.cpp:
(WebCore::stringToUpdateViaCache):
(WebCore::stringToWorkerType):
(WebCore::RegistrationDatabase::doPushChanges):
(WebCore::RegistrationDatabase::importRecords):
(WebCore::RegistrationDatabase::addRegistrationToStore):
* workers/service/server/RegistrationDatabase.h:

* workers/service/server/RegistrationStore.cpp:
(WebCore::RegistrationStore::RegistrationStore):
(WebCore::RegistrationStore::addRegistrationFromDatabase):
* workers/service/server/RegistrationStore.h:

* workers/service/server/SWServer.cpp:
(WebCore::SWServer::addRegistrationFromStore):
(WebCore::SWServer::SWServer):
(WebCore::SWServer::updateWorker):
(WebCore::SWServer::tryInstallContextData):
(WebCore::SWServer::installContextData):
(WebCore::generateServiceWorkerIdentifier): Deleted.
* workers/service/server/SWServer.h:

* workers/service/server/SWServerRegistration.h:

* workers/service/server/SWServerWorker.cpp:
(WebCore::SWServerWorker::contextData const):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@225661 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
Source/WebCore/ChangeLog
Source/WebCore/workers/service/ServiceWorkerContextData.cpp
Source/WebCore/workers/service/ServiceWorkerContextData.h
Source/WebCore/workers/service/ServiceWorkerRegistrationKey.cpp
Source/WebCore/workers/service/ServiceWorkerRegistrationKey.h
Source/WebCore/workers/service/server/RegistrationDatabase.cpp
Source/WebCore/workers/service/server/RegistrationDatabase.h
Source/WebCore/workers/service/server/RegistrationStore.cpp
Source/WebCore/workers/service/server/RegistrationStore.h
Source/WebCore/workers/service/server/SWServer.cpp
Source/WebCore/workers/service/server/SWServer.h
Source/WebCore/workers/service/server/SWServerRegistration.h
Source/WebCore/workers/service/server/SWServerWorker.cpp

index 584adb1..3ac2d6b 100644 (file)
@@ -1,3 +1,55 @@
+2017-12-07  Brady Eidson  <beidson@apple.com>
+
+        Read registrations in from disk.
+        https://bugs.webkit.org/show_bug.cgi?id=180543
+
+        Reviewed by Andy Estes.
+
+        No new tests (Not testable quite yet)
+
+        On launch, read through the database.
+
+        Set up enough objects in the SWServer necessary to cause fetches and postMessages to
+        go to a worker that has never run before.
+
+        * workers/service/ServiceWorkerContextData.cpp:
+        (WebCore::ServiceWorkerContextData::isolatedCopy const):
+        * workers/service/ServiceWorkerContextData.h:
+        (WebCore::ServiceWorkerContextData::encode const):
+        (WebCore::ServiceWorkerContextData::decode):
+
+        * workers/service/ServiceWorkerRegistrationKey.cpp:
+        (WebCore::ServiceWorkerRegistrationKey::toDatabaseKey const):
+        (WebCore::ServiceWorkerRegistrationKey::fromDatabaseKey):
+        * workers/service/ServiceWorkerRegistrationKey.h:
+
+        * workers/service/server/RegistrationDatabase.cpp:
+        (WebCore::stringToUpdateViaCache):
+        (WebCore::stringToWorkerType):
+        (WebCore::RegistrationDatabase::doPushChanges):
+        (WebCore::RegistrationDatabase::importRecords):
+        (WebCore::RegistrationDatabase::addRegistrationToStore):
+        * workers/service/server/RegistrationDatabase.h:
+
+        * workers/service/server/RegistrationStore.cpp:
+        (WebCore::RegistrationStore::RegistrationStore):
+        (WebCore::RegistrationStore::addRegistrationFromDatabase):
+        * workers/service/server/RegistrationStore.h:
+
+        * workers/service/server/SWServer.cpp:
+        (WebCore::SWServer::addRegistrationFromStore):
+        (WebCore::SWServer::SWServer):
+        (WebCore::SWServer::updateWorker):
+        (WebCore::SWServer::tryInstallContextData):
+        (WebCore::SWServer::installContextData):
+        (WebCore::generateServiceWorkerIdentifier): Deleted.
+        * workers/service/server/SWServer.h:
+
+        * workers/service/server/SWServerRegistration.h:
+
+        * workers/service/server/SWServerWorker.cpp:
+        (WebCore::SWServerWorker::contextData const):
+
 2017-12-07  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         Use WTF Locking primitives in WebThread and drop pthread_xxx use
index bb5eb20..7f0abbe 100644 (file)
@@ -32,7 +32,7 @@ namespace WebCore {
 
 ServiceWorkerContextData ServiceWorkerContextData::isolatedCopy() const
 {
-    return { jobDataIdentifier, registration.isolatedCopy(), serviceWorkerIdentifier, script.isolatedCopy(), scriptURL.isolatedCopy(), workerType };
+    return { jobDataIdentifier, registration.isolatedCopy(), serviceWorkerIdentifier, script.isolatedCopy(), scriptURL.isolatedCopy(), workerType, loadedFromDisk };
 }
 
 } // namespace WebCore
index 8050741..f3fe0dc 100644 (file)
@@ -42,6 +42,7 @@ struct ServiceWorkerContextData {
     String script;
     URL scriptURL;
     WorkerType workerType;
+    bool loadedFromDisk;
     
     template<class Encoder> void encode(Encoder&) const;
     template<class Decoder> static std::optional<ServiceWorkerContextData> decode(Decoder&);
@@ -52,7 +53,7 @@ struct ServiceWorkerContextData {
 template<class Encoder>
 void ServiceWorkerContextData::encode(Encoder& encoder) const
 {
-    encoder << jobDataIdentifier << registration << serviceWorkerIdentifier << script << scriptURL << workerType;
+    encoder << jobDataIdentifier << registration << serviceWorkerIdentifier << script << scriptURL << workerType << loadedFromDisk;
 }
 
 template<class Decoder>
@@ -83,8 +84,12 @@ std::optional<ServiceWorkerContextData> ServiceWorkerContextData::decode(Decoder
     WorkerType workerType;
     if (!decoder.decodeEnum(workerType))
         return std::nullopt;
-    
-    return {{ WTFMove(*jobDataIdentifier), WTFMove(*registration), WTFMove(*serviceWorkerIdentifier), WTFMove(script), WTFMove(scriptURL), workerType }};
+
+    bool loadedFromDisk;
+    if (!decoder.decode(loadedFromDisk))
+        return std::nullopt;
+
+    return {{ WTFMove(*jobDataIdentifier), WTFMove(*registration), WTFMove(*serviceWorkerIdentifier), WTFMove(script), WTFMove(scriptURL), workerType, loadedFromDisk}};
 }
 
 } // namespace WebCore
index 4b9aca4..2c89dd4 100644 (file)
@@ -80,7 +80,45 @@ static const char separatorCharacter = '_';
 
 String ServiceWorkerRegistrationKey::toDatabaseKey() const
 {
-    return makeString(m_topOrigin.protocol, separatorCharacter, m_topOrigin.host, separatorCharacter, m_topOrigin.port.value_or(0), separatorCharacter, m_scope.path());
+    if (m_topOrigin.port)
+        return makeString(m_topOrigin.protocol, separatorCharacter, m_topOrigin.host, separatorCharacter, String::number(m_topOrigin.port.value()), separatorCharacter, m_scope.string());
+    return makeString(m_topOrigin.protocol, separatorCharacter, m_topOrigin.host, separatorCharacter, separatorCharacter, m_scope.string());
+}
+
+std::optional<ServiceWorkerRegistrationKey> ServiceWorkerRegistrationKey::fromDatabaseKey(const String& key)
+{
+    auto first = key.find(separatorCharacter, 0);
+    auto second = key.find(separatorCharacter, first + 1);
+    auto third = key.find(separatorCharacter, second + 1);
+
+    if (first == second || second == third)
+        return std::nullopt;
+
+    std::optional<uint16_t> shortPort;
+
+    // If there's a gap between third and second, we expect to have a port to decode
+    if (third - second > 1) {
+        bool ok;
+        unsigned port;
+        if (key.is8Bit())
+            port = charactersToUIntStrict(key.characters8() + second + 1, third - second - 1 , &ok);
+        else
+            port = charactersToUIntStrict(key.characters16() + second + 1, third - second - 1, &ok);
+
+        if (!ok)
+            return std::nullopt;
+
+        if (port > std::numeric_limits<uint16_t>::max())
+            return std::nullopt;
+
+        shortPort = static_cast<uint16_t>(port);
+    }
+
+    auto scope = URL { URL(), key.substring(third + 1) };
+    if (!scope.isValid())
+        return std::nullopt;
+
+    return ServiceWorkerRegistrationKey { { key.substring(0, first), key.substring(first + 1, second - first - 1), shortPort }, WTFMove(scope) };
 }
 
 #ifndef NDEBUG
index c17175a..14ec5fa 100644 (file)
@@ -55,6 +55,7 @@ public:
     template<class Decoder> static std::optional<ServiceWorkerRegistrationKey> decode(Decoder&);
 
     String toDatabaseKey() const;
+    static std::optional<ServiceWorkerRegistrationKey> fromDatabaseKey(const String&);
 
 #ifndef NDEBUG
     String loggingString() const;
index 15a33e7..6615a4a 100644 (file)
@@ -165,6 +165,18 @@ static String updateViaCacheToString(ServiceWorkerUpdateViaCache update)
     RELEASE_ASSERT_NOT_REACHED();
 }
 
+static std::optional<ServiceWorkerUpdateViaCache> stringToUpdateViaCache(const String& update)
+{
+    if (update == "Imports")
+        return ServiceWorkerUpdateViaCache::Imports;
+    if (update == "All")
+        return ServiceWorkerUpdateViaCache::All;
+    if (update == "None")
+        return ServiceWorkerUpdateViaCache::None;
+
+    return std::nullopt;
+}
+
 static String workerTypeToString(WorkerType workerType)
 {
     switch (workerType) {
@@ -177,6 +189,16 @@ static String workerTypeToString(WorkerType workerType)
     RELEASE_ASSERT_NOT_REACHED();
 }
 
+static std::optional<WorkerType> stringToWorkerType(const String& type)
+{
+    if (type == "Classic")
+        return WorkerType::Classic;
+    if (type == "Module")
+        return WorkerType::Module;
+
+    return std::nullopt;
+}
+
 void RegistrationDatabase::pushChanges(Vector<ServiceWorkerContextData>&& datas)
 {
     postTask(createCrossThreadTask(*this, &RegistrationDatabase::doPushChanges, datas));
@@ -191,7 +213,7 @@ void RegistrationDatabase::doPushChanges(Vector<ServiceWorkerContextData>&& data
 
     SQLiteStatement sql(*m_database, ASCIILiteral("INSERT INTO Records VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"));
     if (sql.prepare() != SQLITE_OK) {
-        LOG_ERROR("Failed to prepared statement to store registration data into records table (%i) - %s", m_database->lastError(), m_database->lastErrorMsg());
+        LOG_ERROR("Failed to prepare statement to store registration data into records table (%i) - %s", m_database->lastError(), m_database->lastErrorMsg());
         return;
     }
 
@@ -231,10 +253,50 @@ void RegistrationDatabase::doPushChanges(Vector<ServiceWorkerContextData>&& data
 String RegistrationDatabase::importRecords()
 {
     ASSERT(!isMainThread());
-    // FIXME: Implement
+
+    SQLiteStatement sql(*m_database, ASCIILiteral("SELECT * FROM Records;"));
+    if (sql.prepare() != SQLITE_OK)
+        return String::format("Failed to prepare statement to retrieve registrations from records table (%i) - %s", m_database->lastError(), m_database->lastErrorMsg());
+
+    int result = sql.step();
+
+    for (; result == SQLITE_ROW; result = sql.step()) {
+        auto key = ServiceWorkerRegistrationKey::fromDatabaseKey(sql.getColumnText(0));
+        auto originURL = URL { URL(), sql.getColumnText(1) };
+        auto scopePath = sql.getColumnText(2);
+        auto topOrigin = SecurityOriginData::fromDatabaseIdentifier(sql.getColumnText(3));
+        auto lastUpdateCheckTime = WallTime::fromRawSeconds(sql.getColumnDouble(4));
+        auto updateViaCache = stringToUpdateViaCache(sql.getColumnText(5));
+        auto scriptURL = URL { URL(), sql.getColumnText(6) };
+        auto script = sql.getColumnText(7);
+        auto workerType = stringToWorkerType(sql.getColumnText(8));
+
+        // Validate the input for this registration.
+        // If any part of this input is invalid, let's skip this registration.
+        // FIXME: Should we return an error skipping *all* registrations?
+        if (!key || !originURL.isValid() || !topOrigin || !updateViaCache || !scriptURL.isValid() || !workerType)
+            continue;
+
+        auto workerIdentifier = generateObjectIdentifier<ServiceWorkerIdentifierType>();
+        auto registrationIdentifier = generateObjectIdentifier<ServiceWorkerRegistrationIdentifierType>();
+        auto serviceWorkerData = ServiceWorkerData { workerIdentifier, scriptURL, ServiceWorkerState::Activated, *workerType, registrationIdentifier };
+        auto registration = ServiceWorkerRegistrationData { WTFMove(*key), registrationIdentifier, URL(originURL, scopePath), *updateViaCache, lastUpdateCheckTime, std::nullopt, std::nullopt, WTFMove(serviceWorkerData) };
+        auto contextData = ServiceWorkerContextData { std::nullopt, WTFMove(registration), workerIdentifier, WTFMove(script), WTFMove(scriptURL), *workerType, true };
+
+        postTaskReply(createCrossThreadTask(*this, &RegistrationDatabase::addRegistrationToStore, WTFMove(contextData)));
+    }
+
+    if (result != SQLITE_DONE)
+        return String::format("Failed to import at least one registration from records table (%i) - %s", m_database->lastError(), m_database->lastErrorMsg());
+
     return { };
 }
 
+void RegistrationDatabase::addRegistrationToStore(ServiceWorkerContextData&& context)
+{
+    m_store.addRegistrationFromDatabase(WTFMove(context));
+}
+
 void RegistrationDatabase::databaseFailedToOpen()
 {
     m_store.databaseFailedToOpen();
index 763fc77..6364914 100644 (file)
@@ -52,8 +52,9 @@ private:
     String ensureValidRecordsTable();
     String importRecords();
     void doPushChanges(Vector<ServiceWorkerContextData>&&);
-    
+
     // Replies to the main thread
+    void addRegistrationToStore(ServiceWorkerContextData&&);
     void databaseFailedToOpen();
     void databaseOpenedAndRecordsImported(); 
 
index 973cf40..264e55e 100644 (file)
@@ -32,8 +32,9 @@
 
 namespace WebCore {
 
-RegistrationStore::RegistrationStore(const String& databaseDirectory)
-    : m_database(*this, databaseDirectory)
+RegistrationStore::RegistrationStore(SWServer& server, const String& databaseDirectory)
+    : m_server(server)
+    , m_database(*this, databaseDirectory)
     , m_databasePushTimer(*this, &RegistrationStore::pushChangesToDatabase)
 {
 }
@@ -75,6 +76,11 @@ void RegistrationStore::removeRegistration(SWServerRegistration& registration)
     scheduleDatabasePushIfNecessary();
 }
 
+void RegistrationStore::addRegistrationFromDatabase(ServiceWorkerContextData&& context)
+{
+    m_server.addRegistrationFromStore(WTFMove(context));
+}
+
 void RegistrationStore::databaseFailedToOpen()
 {
     // FIXME: Handle error in some appropriate manner.
@@ -84,7 +90,7 @@ void RegistrationStore::databaseOpenedAndRecordsImported()
 {
     // FIXME: Once we actually do the imports, conclude the importing phase here.
 }
-    
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)
index 8dbfd23..583fa51 100644 (file)
 
 namespace WebCore {
 
+class SWServer;
 class SWServerRegistration;
 
 class RegistrationStore {
 WTF_MAKE_FAST_ALLOCATED;
 public:
-    explicit RegistrationStore(const String& databaseDirectory);
+    explicit RegistrationStore(SWServer&, const String& databaseDirectory);
     ~RegistrationStore();
 
     // Callbacks from the SWServer
@@ -50,6 +51,7 @@ public:
     void removeRegistration(SWServerRegistration&);
 
     // Callbacks from the database
+    void addRegistrationFromDatabase(ServiceWorkerContextData&&);
     void databaseFailedToOpen();
     void databaseOpenedAndRecordsImported();
 
@@ -57,6 +59,7 @@ private:
     void scheduleDatabasePushIfNecessary();
     void pushChangesToDatabase();
 
+    SWServer& m_server;
     RegistrationDatabase m_database;
 
     HashMap<ServiceWorkerRegistrationKey, ServiceWorkerContextData> m_updatedRegistrations;
index a1ec027..79d1fc5 100644 (file)
 
 namespace WebCore {
 
-static ServiceWorkerIdentifier generateServiceWorkerIdentifier()
-{
-    return generateObjectIdentifier<ServiceWorkerIdentifierType>();
-}
-
 static Seconds terminationDelay { 10_s };
 
 SWServer::Connection::Connection(SWServer& server)
@@ -103,6 +98,12 @@ SWServerRegistration* SWServer::getRegistration(const ServiceWorkerRegistrationK
     return m_registrations.get(registrationKey);
 }
 
+void SWServer::addRegistrationFromStore(ServiceWorkerContextData&& data)
+{
+    addRegistration(std::make_unique<SWServerRegistration>(*this, data.registration.key, data.registration.updateViaCache, data.registration.scopeURL, data.scriptURL));
+    tryInstallContextData(WTFMove(data));
+}
+
 void SWServer::addRegistration(std::unique_ptr<SWServerRegistration>&& registration)
 {
     auto key = registration->key();
@@ -198,7 +199,7 @@ void SWServer::Connection::syncTerminateWorker(ServiceWorkerIdentifier identifie
 
 SWServer::SWServer(UniqueRef<SWOriginStore>&& originStore, const String& registrationDatabaseDirectory)
     : m_originStore(WTFMove(originStore))
-    , m_registrationStore(registrationDatabaseDirectory)
+    , m_registrationStore(*this, registrationDatabaseDirectory)
 {
     UNUSED_PARAM(registrationDatabaseDirectory);
     allServers().add(this);
@@ -405,11 +406,13 @@ void SWServer::removeClientServiceWorkerRegistration(Connection& connection, Ser
 void SWServer::updateWorker(Connection&, const ServiceWorkerJobDataIdentifier& jobDataIdentifier, SWServerRegistration& registration, const URL& url, const String& script, WorkerType type)
 {
     registration.setLastUpdateTime(WallTime::now());
+    tryInstallContextData({ jobDataIdentifier, registration.data(), generateObjectIdentifier<ServiceWorkerIdentifierType>(), script, url, type, false });
+}
 
-    ServiceWorkerContextData data = { jobDataIdentifier, registration.data(), generateServiceWorkerIdentifier(), script, url, type };
-
+void SWServer::tryInstallContextData(ServiceWorkerContextData&& data)
+{
     // Right now we only ever keep up to one connection to one SW context process.
-    // And it should always exist if we're calling updateWorker
+    // And it should always exist if we're trying to install context data.
     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
     if (!connection) {
         m_pendingContextDatas.append(WTFMove(data));
@@ -438,7 +441,8 @@ void SWServer::serverToContextConnectionCreated()
 
 void SWServer::installContextData(const ServiceWorkerContextData& data)
 {
-    m_registrationStore.updateRegistration(data);
+    if (!data.loadedFromDisk)
+        m_registrationStore.updateRegistration(data);
 
     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
     ASSERT(connection);
@@ -446,10 +450,18 @@ void SWServer::installContextData(const ServiceWorkerContextData& data)
     auto* registration = m_registrations.get(data.registration.key);
     RELEASE_ASSERT(registration);
 
-    auto result = m_runningOrTerminatingWorkers.add(data.serviceWorkerIdentifier, SWServerWorker::create(*this, *registration, connection->identifier(), data.scriptURL, data.script, data.workerType, data.serviceWorkerIdentifier));
-    ASSERT(result.isNewEntry);
+    auto worker = SWServerWorker::create(*this, *registration, connection->identifier(), data.scriptURL, data.script, data.workerType, data.serviceWorkerIdentifier);
 
-    result.iterator->value->setState(SWServerWorker::State::Running);
+    // We don't immediately launch all workers that were just read in from disk,
+    // as it is unlikely they will be needed immediately.
+    if (data.loadedFromDisk) {
+        registration->updateRegistrationState(ServiceWorkerRegistrationState::Active, worker.ptr());
+        return;
+    }
+
+    worker->setState(SWServerWorker::State::Running);
+    auto result = m_runningOrTerminatingWorkers.add(data.serviceWorkerIdentifier, WTFMove(worker));
+    ASSERT_UNUSED(result, result.isNewEntry);
 
     connection->installServiceWorkerContext(data);
 }
index 9854ebf..796ed8f 100644 (file)
@@ -168,6 +168,8 @@ public:
     void setClientActiveWorker(ServiceWorkerClientIdentifier, ServiceWorkerIdentifier);
     void resolveRegistrationReadyRequests(SWServerRegistration&);
 
+    void addRegistrationFromStore(ServiceWorkerContextData&&);
+
 private:
     void registerConnection(Connection&);
     void unregisterConnection(Connection&);
@@ -185,6 +187,7 @@ private:
     WEBCORE_EXPORT SWServerRegistration* doRegistrationMatching(const SecurityOriginData& topOrigin, const URL& clientURL);
     bool runServiceWorker(ServiceWorkerIdentifier);
 
+    void tryInstallContextData(ServiceWorkerContextData&&);
     void installContextData(const ServiceWorkerContextData&);
 
     SWServerRegistration* registrationFromServiceWorkerIdentifier(ServiceWorkerIdentifier);
index 68ed970..f14df5c 100644 (file)
@@ -42,6 +42,7 @@ class SWServerWorker;
 enum class ServiceWorkerRegistrationState;
 enum class ServiceWorkerState;
 struct ExceptionData;
+struct ServiceWorkerContextData;
 struct ServiceWorkerFetchResult;
 
 class SWServerRegistration {
index c9c9aaf..b56d724 100644 (file)
@@ -65,7 +65,7 @@ ServiceWorkerContextData SWServerWorker::contextData() const
     auto* registration = m_server.getRegistration(m_registrationKey);
     ASSERT(registration);
 
-    return { std::nullopt, registration->data(), m_data.identifier, m_script, m_data.scriptURL, m_data.type };
+    return { std::nullopt, registration->data(), m_data.identifier, m_script, m_data.scriptURL, m_data.type, false };
 }
 
 void SWServerWorker::terminate()