Start writing ServiceWorker registrations to disk.
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 Dec 2017 00:41:14 +0000 (00:41 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 Dec 2017 00:41:14 +0000 (00:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=180488

Reviewed by Chris Dumez.

No new tests (No observable behavior change).

As registrations changes occurs, we now write them to disk.
We don't re-read them in yet.

* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:

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

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

* workers/service/server/RegistrationDatabase.cpp: Added.
(WebCore::v1RecordsTableSchema):
(WebCore::v1RecordsTableSchemaAlternate):
(WebCore::databaseFilename):
(WebCore::RegistrationDatabase::RegistrationDatabase):
(WebCore::RegistrationDatabase::~RegistrationDatabase):
(WebCore::RegistrationDatabase::openSQLiteDatabase):
(WebCore::RegistrationDatabase::ensureValidRecordsTable):
(WebCore::updateViaCacheToString):
(WebCore::workerTypeToString):
(WebCore::RegistrationDatabase::pushChanges):
(WebCore::RegistrationDatabase::doPushChanges):
(WebCore::RegistrationDatabase::importRecords):
(WebCore::RegistrationDatabase::databaseFailedToOpen):
(WebCore::RegistrationDatabase::databaseOpenedAndRecordsImported):
* workers/service/server/RegistrationDatabase.h: Copied from Source/WebCore/workers/service/ServiceWorkerRegistrationData.cpp.
(WebCore::RegistrationDatabase::isClosed const):

* workers/service/server/RegistrationStore.cpp: Added.
(WebCore::RegistrationStore::RegistrationStore):
(WebCore::RegistrationStore::~RegistrationStore):
(WebCore::RegistrationStore::scheduleDatabasePushIfNecessary):
(WebCore::RegistrationStore::pushChangesToDatabase):
(WebCore::RegistrationStore::updateRegistration):
(WebCore::RegistrationStore::removeRegistration):
(WebCore::RegistrationStore::databaseFailedToOpen):
(WebCore::RegistrationStore::databaseOpenedAndRecordsImported):
* workers/service/server/RegistrationStore.h: Copied from Source/WebCore/workers/service/ServiceWorkerRegistrationData.cpp.

* workers/service/server/SWServer.cpp:
(WebCore::SWServer::removeRegistration):
(WebCore::SWServer::SWServer):
(WebCore::SWServer::updateWorker):
(WebCore::SWServer::installContextData):
* workers/service/server/SWServer.h:

* workers/service/server/SWServerRegistration.cpp:
(WebCore::SWServerRegistration::data const):
* workers/service/server/SWServerRegistration.h:
(WebCore::SWServerRegistration::setLastUpdateTime):

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

15 files changed:
Source/WebCore/ChangeLog
Source/WebCore/Sources.txt
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/workers/service/ServiceWorkerRegistrationData.cpp
Source/WebCore/workers/service/ServiceWorkerRegistrationData.h
Source/WebCore/workers/service/ServiceWorkerRegistrationKey.cpp
Source/WebCore/workers/service/ServiceWorkerRegistrationKey.h
Source/WebCore/workers/service/server/RegistrationDatabase.cpp [new file with mode: 0644]
Source/WebCore/workers/service/server/RegistrationDatabase.h [new file with mode: 0644]
Source/WebCore/workers/service/server/RegistrationStore.cpp [new file with mode: 0644]
Source/WebCore/workers/service/server/RegistrationStore.h [new file with mode: 0644]
Source/WebCore/workers/service/server/SWServer.cpp
Source/WebCore/workers/service/server/SWServer.h
Source/WebCore/workers/service/server/SWServerRegistration.cpp
Source/WebCore/workers/service/server/SWServerRegistration.h

index 87766c6..b6898d1 100644 (file)
@@ -1,3 +1,69 @@
+2017-12-06  Brady Eidson  <beidson@apple.com>
+
+        Start writing ServiceWorker registrations to disk.
+        https://bugs.webkit.org/show_bug.cgi?id=180488
+
+        Reviewed by Chris Dumez.
+
+        No new tests (No observable behavior change).
+
+        As registrations changes occurs, we now write them to disk.
+        We don't re-read them in yet.
+
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+
+        * workers/service/ServiceWorkerRegistrationData.cpp:
+        (WebCore::ServiceWorkerRegistrationData::isolatedCopy const):
+        * workers/service/ServiceWorkerRegistrationData.h:
+        (WebCore::ServiceWorkerRegistrationData::encode const):
+        (WebCore::ServiceWorkerRegistrationData::decode):
+
+        * workers/service/ServiceWorkerRegistrationKey.cpp:
+        (WebCore::ServiceWorkerRegistrationKey::toDatabaseKey const):
+        * workers/service/ServiceWorkerRegistrationKey.h:
+
+        * workers/service/server/RegistrationDatabase.cpp: Added.
+        (WebCore::v1RecordsTableSchema):
+        (WebCore::v1RecordsTableSchemaAlternate):
+        (WebCore::databaseFilename):
+        (WebCore::RegistrationDatabase::RegistrationDatabase):
+        (WebCore::RegistrationDatabase::~RegistrationDatabase):
+        (WebCore::RegistrationDatabase::openSQLiteDatabase):
+        (WebCore::RegistrationDatabase::ensureValidRecordsTable):
+        (WebCore::updateViaCacheToString):
+        (WebCore::workerTypeToString):
+        (WebCore::RegistrationDatabase::pushChanges):
+        (WebCore::RegistrationDatabase::doPushChanges):
+        (WebCore::RegistrationDatabase::importRecords):
+        (WebCore::RegistrationDatabase::databaseFailedToOpen):
+        (WebCore::RegistrationDatabase::databaseOpenedAndRecordsImported):
+        * workers/service/server/RegistrationDatabase.h: Copied from Source/WebCore/workers/service/ServiceWorkerRegistrationData.cpp.
+        (WebCore::RegistrationDatabase::isClosed const):
+
+        * workers/service/server/RegistrationStore.cpp: Added.
+        (WebCore::RegistrationStore::RegistrationStore):
+        (WebCore::RegistrationStore::~RegistrationStore):
+        (WebCore::RegistrationStore::scheduleDatabasePushIfNecessary):
+        (WebCore::RegistrationStore::pushChangesToDatabase):
+        (WebCore::RegistrationStore::updateRegistration):
+        (WebCore::RegistrationStore::removeRegistration):
+        (WebCore::RegistrationStore::databaseFailedToOpen):
+        (WebCore::RegistrationStore::databaseOpenedAndRecordsImported):
+        * workers/service/server/RegistrationStore.h: Copied from Source/WebCore/workers/service/ServiceWorkerRegistrationData.cpp.
+
+        * workers/service/server/SWServer.cpp:
+        (WebCore::SWServer::removeRegistration):
+        (WebCore::SWServer::SWServer):
+        (WebCore::SWServer::updateWorker):
+        (WebCore::SWServer::installContextData):
+        * workers/service/server/SWServer.h:
+
+        * workers/service/server/SWServerRegistration.cpp:
+        (WebCore::SWServerRegistration::data const):
+        * workers/service/server/SWServerRegistration.h:
+        (WebCore::SWServerRegistration::setLastUpdateTime):
+
 2017-12-06  Chris Dumez  <cdumez@apple.com>
 
         Unreviewed, fix build after r225577.
index 93c0207..cee3802 100644 (file)
@@ -2212,6 +2212,8 @@ workers/service/context/ServiceWorkerThread.cpp
 workers/service/context/ServiceWorkerThreadProxy.cpp
 workers/service/context/SWContextManager.cpp
 
+workers/service/server/RegistrationDatabase.cpp
+workers/service/server/RegistrationStore.cpp
 workers/service/server/SWOriginStore.cpp
 workers/service/server/SWServer.cpp
 workers/service/server/SWServerJobQueue.cpp
index 7383378..30b256a 100644 (file)
                51AF503616F100F60095B2E8 /* ResourceLoaderTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 51AF503516F100F60095B2E8 /* ResourceLoaderTypes.h */; settings = {ATTRIBUTES = (Private, ); }; };
                51B454EA1B4DAE7D0085EAA6 /* PingHandle.h in Headers */ = {isa = PBXBuildFile; fileRef = 51B454E91B4DAE7D0085EAA6 /* PingHandle.h */; settings = {ATTRIBUTES = (Private, ); }; };
                51B45D211AB8D1E200117CD2 /* ContentExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = 51B45D1F1AB8D1E200117CD2 /* ContentExtension.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               51B914101FD2782D00EE2859 /* RegistrationDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = 51B9140C1FD2782500EE2859 /* RegistrationDatabase.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               51B914111FD2782D00EE2859 /* RegistrationStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 51B9140B1FD2782500EE2859 /* RegistrationStore.h */; settings = {ATTRIBUTES = (Private, ); }; };
                51BA4AC41BBB5CD800DF3D6D /* IDBDatabaseInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 51BA4AC21BBB5CBF00DF3D6D /* IDBDatabaseInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
                51BA4ACB1BBC5BD900DF3D6D /* MemoryIDBBackingStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 51BA4AC91BBC5B9E00DF3D6D /* MemoryIDBBackingStore.h */; };
                51BA4ACC1BBC5BDD00DF3D6D /* IDBBackingStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 51BA4AC71BBC5AD600DF3D6D /* IDBBackingStore.h */; settings = {ATTRIBUTES = (Private, ); }; };
                51B454E91B4DAE7D0085EAA6 /* PingHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PingHandle.h; sourceTree = "<group>"; };
                51B45D1E1AB8D1E200117CD2 /* ContentExtension.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContentExtension.cpp; sourceTree = "<group>"; };
                51B45D1F1AB8D1E200117CD2 /* ContentExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentExtension.h; sourceTree = "<group>"; };
+               51B9140B1FD2782500EE2859 /* RegistrationStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegistrationStore.h; sourceTree = "<group>"; };
+               51B9140C1FD2782500EE2859 /* RegistrationDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegistrationDatabase.h; sourceTree = "<group>"; };
+               51B9140D1FD2782500EE2859 /* RegistrationDatabase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegistrationDatabase.cpp; sourceTree = "<group>"; };
+               51B9140E1FD2782500EE2859 /* RegistrationStore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegistrationStore.cpp; sourceTree = "<group>"; };
                51BA4AC11BBB5CBF00DF3D6D /* IDBDatabaseInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IDBDatabaseInfo.cpp; sourceTree = "<group>"; };
                51BA4AC21BBB5CBF00DF3D6D /* IDBDatabaseInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IDBDatabaseInfo.h; sourceTree = "<group>"; };
                51BA4AC71BBC5AD600DF3D6D /* IDBBackingStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IDBBackingStore.h; sourceTree = "<group>"; };
                517A52EC1F47532D00DCDC0A /* server */ = {
                        isa = PBXGroup;
                        children = (
+                               51B9140D1FD2782500EE2859 /* RegistrationDatabase.cpp */,
+                               51B9140C1FD2782500EE2859 /* RegistrationDatabase.h */,
+                               51B9140E1FD2782500EE2859 /* RegistrationStore.cpp */,
+                               51B9140B1FD2782500EE2859 /* RegistrationStore.h */,
                                4151E5B91FBA4C7600E47E2D /* SWOriginStore.cpp */,
                                4151E5B71FBA4C7500E47E2D /* SWOriginStore.h */,
                                517A52EF1F47535900DCDC0A /* SWServer.cpp */,
                                BCAB418213E356E800D8AAF3 /* Region.h in Headers */,
                                26B9998F1803AE7200D01121 /* RegisterAllocator.h in Headers */,
                                85031B4C0A44EFC700F992E0 /* RegisteredEventListener.h in Headers */,
+                               51B914101FD2782D00EE2859 /* RegistrationDatabase.h in Headers */,
+                               51B914111FD2782D00EE2859 /* RegistrationStore.h in Headers */,
                                A578F43F1DE0B630003DFC6A /* RejectedPromiseTracker.h in Headers */,
                                CDFC360618CA61C20026E56F /* RemoteCommandListener.h in Headers */,
                                CD8ACA891D237AA200ECC59E /* RemoteCommandListenerMac.h in Headers */,
index 36415dc..0ca5850 100644 (file)
@@ -37,6 +37,7 @@ ServiceWorkerRegistrationData ServiceWorkerRegistrationData::isolatedCopy() cons
         identifier,
         scopeURL.isolatedCopy(),
         updateViaCache,
+        lastUpdateTime,
         installingWorker ? std::optional<ServiceWorkerData>(installingWorker->isolatedCopy()) : std::nullopt,
         waitingWorker ? std::optional<ServiceWorkerData>(waitingWorker->isolatedCopy()) : std::nullopt,
         activeWorker ? std::optional<ServiceWorkerData>(activeWorker->isolatedCopy()) : std::nullopt,
index 3669369..7f07e20 100644 (file)
@@ -32,6 +32,7 @@
 #include "ServiceWorkerRegistrationKey.h"
 #include "ServiceWorkerTypes.h"
 #include "ServiceWorkerUpdateViaCache.h"
+#include <wtf/WallTime.h>
 
 namespace WebCore {
 
@@ -42,6 +43,7 @@ struct ServiceWorkerRegistrationData {
     ServiceWorkerRegistrationIdentifier identifier;
     URL scopeURL;
     ServiceWorkerUpdateViaCache updateViaCache;
+    WallTime lastUpdateTime;
 
     std::optional<ServiceWorkerData> installingWorker;
     std::optional<ServiceWorkerData> waitingWorker;
@@ -57,7 +59,7 @@ struct ServiceWorkerRegistrationData {
 template<class Encoder>
 void ServiceWorkerRegistrationData::encode(Encoder& encoder) const
 {
-    encoder << key << identifier << scopeURL << updateViaCache << installingWorker << waitingWorker << activeWorker;
+    encoder << key << identifier << scopeURL << updateViaCache << lastUpdateTime.secondsSinceEpoch().value() << installingWorker << waitingWorker << activeWorker;
 }
 
 template<class Decoder>
@@ -83,6 +85,11 @@ std::optional<ServiceWorkerRegistrationData> ServiceWorkerRegistrationData::deco
     if (!updateViaCache)
         return std::nullopt;
 
+    std::optional<double> rawWallTime;
+    decoder >> rawWallTime;
+    if (!rawWallTime)
+        return std::nullopt;
+
     std::optional<std::optional<ServiceWorkerData>> installingWorker;
     decoder >> installingWorker;
     if (!installingWorker)
@@ -98,7 +105,7 @@ std::optional<ServiceWorkerRegistrationData> ServiceWorkerRegistrationData::deco
     if (!activeWorker)
         return std::nullopt;
 
-    return { { WTFMove(*key), WTFMove(*identifier), WTFMove(*scopeURL), WTFMove(*updateViaCache), WTFMove(*installingWorker), WTFMove(*waitingWorker), WTFMove(*activeWorker) } };
+    return { { WTFMove(*key), WTFMove(*identifier), WTFMove(*scopeURL), WTFMove(*updateViaCache), WallTime::fromRawSeconds(*rawWallTime), WTFMove(*installingWorker), WTFMove(*waitingWorker), WTFMove(*activeWorker) } };
 }
 
 } // namespace WTF
index b2424f8..4b9aca4 100644 (file)
@@ -76,6 +76,13 @@ bool ServiceWorkerRegistrationKey::originIsMatching(const SecurityOriginData& to
     return protocolHostAndPortAreEqual(clientURL, m_scope);
 }
 
+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());
+}
+
 #ifndef NDEBUG
 String ServiceWorkerRegistrationKey::loggingString() const
 {
index cd3c5be..c17175a 100644 (file)
@@ -54,6 +54,8 @@ public:
     template<class Encoder> void encode(Encoder&) const;
     template<class Decoder> static std::optional<ServiceWorkerRegistrationKey> decode(Decoder&);
 
+    String toDatabaseKey() const;
+
 #ifndef NDEBUG
     String loggingString() const;
 #endif
diff --git a/Source/WebCore/workers/service/server/RegistrationDatabase.cpp b/Source/WebCore/workers/service/server/RegistrationDatabase.cpp
new file mode 100644 (file)
index 0000000..3b30b82
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "RegistrationDatabase.h"
+
+#if ENABLE(SERVICE_WORKER)
+
+#include "FileSystem.h"
+#include "Logging.h"
+#include "RegistrationStore.h"
+#include "SQLiteDatabase.h"
+#include "SQLiteStatement.h"
+#include "SQLiteTransaction.h"
+#include <wtf/MainThread.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/Scope.h>
+
+namespace WebCore {
+
+static const String v1RecordsTableSchema(const String& tableName)
+{
+    return makeString("CREATE TABLE ", tableName, " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, origin TEXT NOT NULL ON CONFLICT FAIL, scopeURL TEXT NOT NULL ON CONFLICT FAIL, topOrigin TEXT NOT NULL ON CONFLICT FAIL, lastUpdateCheckTime DOUBLE NOT NULL ON CONFLICT FAIL, updateViaCache TEXT NOT NULL ON CONFLICT FAIL, scriptURL TEXT NOT NULL ON CONFLICT FAIL, script TEXT NOT NULL ON CONFLICT FAIL, workerType TEXT NOT NULL ON CONFLICT FAIL)");
+}
+
+static const String v1RecordsTableSchema()
+{
+    static NeverDestroyed<WTF::String> schema(v1RecordsTableSchema("Records"));
+    return schema;
+}
+
+static const String v1RecordsTableSchemaAlternate()
+{
+    static NeverDestroyed<WTF::String> schema(v1RecordsTableSchema("\"Records\""));
+    return schema;
+}
+
+static const String& databaseFilename()
+{
+    static NeverDestroyed<String> filename = "ServiceWorkerRegistrations.sqlite3";
+    return filename;
+}
+
+RegistrationDatabase::RegistrationDatabase(RegistrationStore& store, const String& databaseDirectory)
+    : CrossThreadTaskHandler("ServiceWorker I/O Thread")
+    , m_store(store)
+    , m_databaseDirectory(databaseDirectory)
+{
+    ASSERT(isMainThread());
+
+    postTask(createCrossThreadTask(*this, &RegistrationDatabase::openSQLiteDatabase, FileSystem::pathByAppendingComponent(m_databaseDirectory, databaseFilename())));
+}
+
+RegistrationDatabase::~RegistrationDatabase()
+{
+    ASSERT(!m_database);
+    ASSERT(isMainThread());
+}
+
+void RegistrationDatabase::openSQLiteDatabase(const String& fullFilename)
+{
+    ASSERT(!isMainThread());
+    ASSERT(!m_database);
+
+    LOG(ServiceWorker, "ServiceWorker RegistrationDatabase opening file %s", fullFilename.utf8().data());
+
+    String errorMessage;
+    auto scopeExit = makeScopeExit([&, errorMessage = &errorMessage] {
+        ASSERT(!errorMessage->isNull());
+        LOG_ERROR("Failed to open Service Worker registration database: %s", errorMessage->utf8().data());
+        m_database = nullptr;
+        postTaskReply(createCrossThreadTask(*this, &RegistrationDatabase::databaseFailedToOpen));
+    });
+    
+    m_database = std::make_unique<SQLiteDatabase>();
+    if (!m_database->open(fullFilename)) {
+        errorMessage = "Failed to open registration database";
+        return;
+    }
+    
+    errorMessage = ensureValidRecordsTable();
+    if (!errorMessage.isNull())
+        return;
+    
+    errorMessage = importRecords();
+    if (!errorMessage.isNull())
+        return;
+    
+    scopeExit.release();
+    postTaskReply(createCrossThreadTask(*this, &RegistrationDatabase::databaseOpenedAndRecordsImported));
+}
+
+String RegistrationDatabase::ensureValidRecordsTable()
+{
+    ASSERT(!isMainThread());
+    ASSERT(m_database);
+    ASSERT(m_database->isOpen());
+
+    String currentSchema;
+    {
+        // Fetch the schema for an existing records table.
+        SQLiteStatement statement(*m_database, "SELECT type, sql FROM sqlite_master WHERE tbl_name='Records'");
+        if (statement.prepare() != SQLITE_OK)
+            return "Unable to prepare statement to fetch schema for the Records table.";
+
+        int sqliteResult = statement.step();
+
+        // If there is no Records table at all, create it and then bail.
+        if (sqliteResult == SQLITE_DONE) {
+            if (!m_database->executeCommand(v1RecordsTableSchema()))
+                return String::format("Could not create Records table in database (%i) - %s", m_database->lastError(), m_database->lastErrorMsg());
+            return { };
+        }
+
+        if (sqliteResult != SQLITE_ROW)
+            return "Error executing statement to fetch schema for the Records table.";
+
+        currentSchema = statement.getColumnText(1);
+    }
+
+    ASSERT(!currentSchema.isEmpty());
+    
+    if (currentSchema == v1RecordsTableSchema() || currentSchema == v1RecordsTableSchemaAlternate())
+        return { };
+
+    // This database has a Records table but it is not a schema we expect.
+    // Trying to recover by deleting the data contained within is dangerous so
+    // we should consider this an unrecoverable error.
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+static String updateViaCacheToString(ServiceWorkerUpdateViaCache update)
+{
+    switch (update) {
+    case ServiceWorkerUpdateViaCache::Imports:
+        return "Imports";
+    case ServiceWorkerUpdateViaCache::All:
+        return "All";
+    case ServiceWorkerUpdateViaCache::None:
+        return "None";
+    }
+
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+static String workerTypeToString(WorkerType workerType)
+{
+    switch (workerType) {
+    case WorkerType::Classic:
+        return "Classic";
+    case WorkerType::Module:
+        return "Module";
+    }
+
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+void RegistrationDatabase::pushChanges(Vector<ServiceWorkerContextData>&& datas)
+{
+    postTask(createCrossThreadTask(*this, &RegistrationDatabase::doPushChanges, datas));
+}
+
+void RegistrationDatabase::doPushChanges(Vector<ServiceWorkerContextData>&& datas)
+{
+    ASSERT(m_database);
+
+    SQLiteTransaction transaction(*m_database);
+    transaction.begin();
+
+    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());
+        return;
+    }
+
+    for (auto& data : datas) {
+        if (data.registration.identifier == ServiceWorkerRegistrationIdentifier()) {
+            SQLiteStatement sql(*m_database, "DELETE FROM Records WHERE key = ?");
+            if (sql.prepare() != SQLITE_OK
+                || sql.bindText(1, data.registration.key.toDatabaseKey()) != SQLITE_OK
+                || sql.step() != SQLITE_DONE) {
+                LOG_ERROR("Failed to remove registration data from records table (%i) - %s", m_database->lastError(), m_database->lastErrorMsg());
+                return;
+            }
+
+            continue;
+        }
+
+        if (sql.bindText(1, data.registration.key.toDatabaseKey()) != SQLITE_OK
+            || sql.bindText(2, data.registration.scopeURL.protocolHostAndPort()) != SQLITE_OK
+            || sql.bindText(3, data.registration.scopeURL.path()) != SQLITE_OK
+            || sql.bindText(4, data.registration.key.topOrigin().databaseIdentifier()) != SQLITE_OK
+            || sql.bindDouble(5, data.registration.lastUpdateTime.secondsSinceEpoch().value()) != SQLITE_OK
+            || sql.bindText(6, updateViaCacheToString(data.registration.updateViaCache)) != SQLITE_OK
+            || sql.bindText(7, data.scriptURL.string()) != SQLITE_OK
+            || sql.bindText(8, data.script) != SQLITE_OK
+            || sql.bindText(9, workerTypeToString(data.workerType)) != SQLITE_OK
+            || sql.step() != SQLITE_DONE) {
+            LOG_ERROR("Failed to store registration data into records table (%i) - %s", m_database->lastError(), m_database->lastErrorMsg());
+            return;
+        }
+    }
+
+    transaction.commit();
+
+    LOG(ServiceWorker, "Pushed %zu changes to ServiceWorker registration database", datas.size());
+}
+
+String RegistrationDatabase::importRecords()
+{
+    ASSERT(!isMainThread());
+    // FIXME: Implement
+    return { };
+}
+
+void RegistrationDatabase::databaseFailedToOpen()
+{
+    m_store.databaseFailedToOpen();
+}
+
+void RegistrationDatabase::databaseOpenedAndRecordsImported()
+{
+    m_store.databaseOpenedAndRecordsImported();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(SERVICE_WORKER)
diff --git a/Source/WebCore/workers/service/server/RegistrationDatabase.h b/Source/WebCore/workers/service/server/RegistrationDatabase.h
new file mode 100644 (file)
index 0000000..763fc77
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(SERVICE_WORKER)
+
+#include <wtf/CrossThreadTaskHandler.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class RegistrationStore;
+class SQLiteDatabase;
+struct ServiceWorkerContextData;
+
+class RegistrationDatabase : public CrossThreadTaskHandler {
+WTF_MAKE_FAST_ALLOCATED;
+public:
+    RegistrationDatabase(RegistrationStore&, const String& databaseDirectory);
+    ~RegistrationDatabase();
+
+    bool isClosed() const { return !m_database; }
+
+    void pushChanges(Vector<ServiceWorkerContextData>&&);
+
+private:
+    // Methods to be run on the task thread
+    void openSQLiteDatabase(const String& fullFilename);
+    String ensureValidRecordsTable();
+    String importRecords();
+    void doPushChanges(Vector<ServiceWorkerContextData>&&);
+    
+    // Replies to the main thread
+    void databaseFailedToOpen();
+    void databaseOpenedAndRecordsImported(); 
+
+    RegistrationStore& m_store;
+    String m_databaseDirectory;
+    std::unique_ptr<SQLiteDatabase> m_database;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SERVICE_WORKER)
diff --git a/Source/WebCore/workers/service/server/RegistrationStore.cpp b/Source/WebCore/workers/service/server/RegistrationStore.cpp
new file mode 100644 (file)
index 0000000..973cf40
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "RegistrationStore.h"
+
+#if ENABLE(SERVICE_WORKER)
+
+#include "SWServerRegistration.h"
+
+namespace WebCore {
+
+RegistrationStore::RegistrationStore(const String& databaseDirectory)
+    : m_database(*this, databaseDirectory)
+    , m_databasePushTimer(*this, &RegistrationStore::pushChangesToDatabase)
+{
+}
+
+RegistrationStore::~RegistrationStore()
+{
+    ASSERT(m_database.isClosed());
+}
+
+void RegistrationStore::scheduleDatabasePushIfNecessary()
+{
+    if (m_databasePushTimer.isActive())
+        return;
+    m_databasePushTimer.startOneShot(0_s);
+}
+
+void RegistrationStore::pushChangesToDatabase()
+{
+    Vector<ServiceWorkerContextData> changesToPush;
+    changesToPush.reserveInitialCapacity(m_updatedRegistrations.size());
+    for (auto& value : m_updatedRegistrations.values())
+        changesToPush.uncheckedAppend(WTFMove(value));
+
+    m_updatedRegistrations.clear();
+    m_database.pushChanges(WTFMove(changesToPush));
+}
+
+void RegistrationStore::updateRegistration(const ServiceWorkerContextData& data)
+{
+    m_updatedRegistrations.set(data.registration.key, data);
+    scheduleDatabasePushIfNecessary();
+}
+
+void RegistrationStore::removeRegistration(SWServerRegistration& registration)
+{
+    ServiceWorkerContextData contextData;
+    contextData.registration.key = registration.key();
+    m_updatedRegistrations.set(registration.key(), WTFMove(contextData));
+    scheduleDatabasePushIfNecessary();
+}
+
+void RegistrationStore::databaseFailedToOpen()
+{
+    // FIXME: Handle error in some appropriate manner.
+}
+
+void RegistrationStore::databaseOpenedAndRecordsImported()
+{
+    // FIXME: Once we actually do the imports, conclude the importing phase here.
+}
+    
+} // namespace WebCore
+
+#endif // ENABLE(SERVICE_WORKER)
diff --git a/Source/WebCore/workers/service/server/RegistrationStore.h b/Source/WebCore/workers/service/server/RegistrationStore.h
new file mode 100644 (file)
index 0000000..8dbfd23
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(SERVICE_WORKER)
+
+#include "RegistrationDatabase.h"
+#include "ServiceWorkerContextData.h"
+#include "ServiceWorkerRegistrationData.h"
+#include "ServiceWorkerRegistrationKey.h"
+#include "Timer.h"
+#include <wtf/HashMap.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class SWServerRegistration;
+
+class RegistrationStore {
+WTF_MAKE_FAST_ALLOCATED;
+public:
+    explicit RegistrationStore(const String& databaseDirectory);
+    ~RegistrationStore();
+
+    // Callbacks from the SWServer
+    void updateRegistration(const ServiceWorkerContextData&);
+    void removeRegistration(SWServerRegistration&);
+
+    // Callbacks from the database
+    void databaseFailedToOpen();
+    void databaseOpenedAndRecordsImported();
+
+private:
+    void scheduleDatabasePushIfNecessary();
+    void pushChangesToDatabase();
+
+    RegistrationDatabase m_database;
+
+    HashMap<ServiceWorkerRegistrationKey, ServiceWorkerContextData> m_updatedRegistrations;
+    Timer m_databasePushTimer;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SERVICE_WORKER)
index 1bcf979..bafc64c 100644 (file)
@@ -31,6 +31,7 @@
 #include "ExceptionCode.h"
 #include "ExceptionData.h"
 #include "Logging.h"
+#include "RegistrationStore.h"
 #include "SWOriginStore.h"
 #include "SWServerJobQueue.h"
 #include "SWServerRegistration.h"
@@ -124,6 +125,7 @@ void SWServer::removeRegistration(const ServiceWorkerRegistrationKey& key)
     ASSERT_UNUSED(wasRemoved, wasRemoved);
 
     m_originStore->remove(topOrigin);
+    m_registrationStore.removeRegistration(*registration);
 }
 
 Vector<ServiceWorkerRegistrationData> SWServer::getRegistrations(const SecurityOriginData& topOrigin, const URL& clientURL)
@@ -196,6 +198,7 @@ void SWServer::Connection::syncTerminateWorker(ServiceWorkerIdentifier identifie
 
 SWServer::SWServer(UniqueRef<SWOriginStore>&& originStore, const String& registrationDatabaseDirectory)
     : m_originStore(WTFMove(originStore))
+    , m_registrationStore(registrationDatabaseDirectory)
 {
     UNUSED_PARAM(registrationDatabaseDirectory);
     allServers().add(this);
@@ -401,9 +404,9 @@ void SWServer::removeClientServiceWorkerRegistration(Connection& connection, Ser
 
 void SWServer::updateWorker(Connection&, const ServiceWorkerJobDataIdentifier& jobDataIdentifier, SWServerRegistration& registration, const URL& url, const String& script, WorkerType type)
 {
-    auto serviceWorkerIdentifier = generateServiceWorkerIdentifier();
+    registration.setLastUpdateTime(WallTime::now());
 
-    ServiceWorkerContextData data = { jobDataIdentifier, registration.data(), serviceWorkerIdentifier, script, url, type };
+    ServiceWorkerContextData data = { jobDataIdentifier, registration.data(), generateServiceWorkerIdentifier(), script, url, type };
 
     // 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
@@ -427,6 +430,8 @@ void SWServer::serverToContextConnectionCreated()
 
 void SWServer::installContextData(const ServiceWorkerContextData& data)
 {
+    m_registrationStore.updateRegistration(data);
+
     auto* connection = SWServerToContextConnection::globalServerToContextConnection();
     ASSERT(connection);
 
index 3199a60..d7be310 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "ClientOrigin.h"
 #include "DocumentIdentifier.h"
+#include "RegistrationStore.h"
 #include "SWServerWorker.h"
 #include "ServiceWorkerClientData.h"
 #include "ServiceWorkerIdentifier.h"
@@ -213,6 +214,7 @@ private:
     Lock m_mainThreadReplyLock;
     bool m_mainThreadReplyScheduled { false };
     UniqueRef<SWOriginStore> m_originStore;
+    RegistrationStore m_registrationStore;
     Deque<ServiceWorkerContextData> m_pendingContextDatas;
 };
 
index f06f0e5..a5354e5 100644 (file)
@@ -131,7 +131,7 @@ ServiceWorkerRegistrationData SWServerRegistration::data() const
     if (m_activeWorker)
         activeWorkerData = m_activeWorker->data();
 
-    return { m_registrationKey, identifier(), m_scopeURL, m_updateViaCache, WTFMove(installingWorkerData), WTFMove(waitingWorkerData), WTFMove(activeWorkerData) };
+    return { m_registrationKey, identifier(), m_scopeURL, m_updateViaCache, m_lastUpdateTime, WTFMove(installingWorkerData), WTFMove(waitingWorkerData), WTFMove(activeWorkerData) };
 }
 
 void SWServerRegistration::addClientServiceWorkerRegistration(SWServerConnectionIdentifier connectionIdentifier)
index dc4a4b0..68ed970 100644 (file)
@@ -33,6 +33,7 @@
 #include "ServiceWorkerTypes.h"
 #include <wtf/HashCountedSet.h>
 #include <wtf/MonotonicTime.h>
+#include <wtf/WallTime.h>
 
 namespace WebCore {
 
@@ -58,7 +59,7 @@ public:
     bool isUninstalling() const { return m_uninstalling; }
     void setIsUninstalling(bool);
 
-    void setLastUpdateTime(double time) { m_lastUpdateTime = time; }
+    void setLastUpdateTime(WallTime time) { m_lastUpdateTime = time; }
     ServiceWorkerUpdateViaCache updateViaCache() const { return m_updateViaCache; }
 
     void updateRegistrationState(ServiceWorkerRegistrationState, SWServerWorker*);
@@ -104,7 +105,7 @@ private:
     RefPtr<SWServerWorker> m_waitingWorker;
     RefPtr<SWServerWorker> m_activeWorker;
 
-    double m_lastUpdateTime { 0 };
+    WallTime m_lastUpdateTime;
     
     HashCountedSet<SWServerConnectionIdentifier> m_connectionsWithClientRegistrations;
     SWServer& m_server;