Modern IDB (Blob support): Write blobs to temporary files and move them to the correct location when storing them.
https://bugs.webkit.org/show_bug.cgi?id=156321
Reviewed by Alex Christensen, Andy Estes, and Darin Adler.
Source/WebCore:
No new tests (No testable change in behavior yet, current tests pass).
When asked to store a Blob (including Files) in IndexedDB, the Blob is written out to a temporary file.
Then when the putOrAdd request is received by IDBServer it includes a list of blobURLs and their mappings
to temporary files.
Finally, as part of storing the Blob value in the database, those temporary files are moved in to place
under the IndexedDB directory for storage and later retrieval.
* Modules/indexeddb/IDBValue.cpp:
(WebCore::IDBValue::IDBValue):
* Modules/indexeddb/server/IDBBackingStore.h:
(WebCore::IDBServer::IDBBackingStoreTemporaryFileHandler::~IDBBackingStoreTemporaryFileHandler):
* Modules/indexeddb/server/IDBServer.cpp:
(WebCore::IDBServer::IDBServer::create):
(WebCore::IDBServer::IDBServer::IDBServer):
(WebCore::IDBServer::IDBServer::createBackingStore):
* Modules/indexeddb/server/IDBServer.h:
* Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
(WebCore::IDBServer::blobRecordsTableSchema):
(WebCore::IDBServer::blobRecordsTableSchemaAlternate):
(WebCore::IDBServer::blobFilesTableSchema):
(WebCore::IDBServer::blobFilesTableSchemaAlternate):
(WebCore::IDBServer::SQLiteIDBBackingStore::SQLiteIDBBackingStore):
(WebCore::IDBServer::SQLiteIDBBackingStore::ensureValidBlobTables):
(WebCore::IDBServer::SQLiteIDBBackingStore::getOrEstablishDatabaseInfo):
(WebCore::IDBServer::SQLiteIDBBackingStore::addRecord):
* Modules/indexeddb/server/SQLiteIDBBackingStore.h:
(WebCore::IDBServer::SQLiteIDBBackingStore::temporaryFileHandler):
* Modules/indexeddb/server/SQLiteIDBTransaction.cpp:
(WebCore::IDBServer::SQLiteIDBTransaction::commit):
(WebCore::IDBServer::SQLiteIDBTransaction::moveBlobFilesIfNecessary):
(WebCore::IDBServer::SQLiteIDBTransaction::abort):
(WebCore::IDBServer::SQLiteIDBTransaction::reset):
(WebCore::IDBServer::SQLiteIDBTransaction::addBlobFile):
* Modules/indexeddb/server/SQLiteIDBTransaction.h:
* Modules/indexeddb/shared/InProcessIDBServer.cpp:
(WebCore::InProcessIDBServer::InProcessIDBServer):
(WebCore::InProcessIDBServer::accessToTemporaryFileComplete):
* Modules/indexeddb/shared/InProcessIDBServer.h:
* bindings/js/SerializedScriptValue.cpp:
(WebCore::SerializedScriptValue::blobURLsIsolatedCopy):
* bindings/js/SerializedScriptValue.h:
* platform/FileSystem.h:
* platform/gtk/FileSystemGtk.cpp:
(WebCore::hardLinkOrCopyFile):
* platform/posix/FileSystemPOSIX.cpp:
(WebCore::hardLinkOrCopyFile):
Source/WebKit2:
The NetworkProcess writes a blob to a temporary file, then tells the UIProcess to grant the DatabaseProcess
a Sandbox Extension to that path.
It then tells the WebProcess the paths for the temporary files, which then tells the DatabaseProcess to store
the contents of those files as blob references in the database.
Since the UIProcess had already granted it a Sandbox Extension, it is able to do so.
* DatabaseProcess/DatabaseProcess.cpp:
(WebKit::DatabaseProcess::idbServer):
(WebKit::DatabaseProcess::grantSandboxExtensionsForBlobs):
(WebKit::DatabaseProcess::prepareForAccessToTemporaryFile):
(WebKit::DatabaseProcess::accessToTemporaryFileComplete):
* DatabaseProcess/DatabaseProcess.h:
* DatabaseProcess/DatabaseProcess.messages.in:
* NetworkProcess/NetworkConnectionToWebProcess.cpp:
(WebKit::NetworkConnectionToWebProcess::writeBlobsToTemporaryFiles):
* NetworkProcess/NetworkProcess.cpp:
(WebKit::NetworkProcess::grantSandboxExtensionsToDatabaseProcessForBlobs):
(WebKit::NetworkProcess::didGrantSandboxExtensionsToDatabaseProcessForBlobs):
* NetworkProcess/NetworkProcess.h:
* NetworkProcess/NetworkProcess.messages.in:
* UIProcess/Network/NetworkProcessProxy.cpp:
(WebKit::NetworkProcessProxy::grantSandboxExtensionsToDatabaseProcessForBlobs):
* UIProcess/Network/NetworkProcessProxy.h:
* UIProcess/Network/NetworkProcessProxy.messages.in:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@199230 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 03f4828..732f38b 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,67 @@
+2016-04-08 Brady Eidson <beidson@apple.com>
+
+ Modern IDB (Blob support): Write blobs to temporary files and move them to the correct location when storing them.
+ https://bugs.webkit.org/show_bug.cgi?id=156321
+
+ Reviewed by Alex Christensen, Andy Estes, and Darin Adler.
+
+ No new tests (No testable change in behavior yet, current tests pass).
+
+ When asked to store a Blob (including Files) in IndexedDB, the Blob is written out to a temporary file.
+
+ Then when the putOrAdd request is received by IDBServer it includes a list of blobURLs and their mappings
+ to temporary files.
+
+ Finally, as part of storing the Blob value in the database, those temporary files are moved in to place
+ under the IndexedDB directory for storage and later retrieval.
+
+ * Modules/indexeddb/IDBValue.cpp:
+ (WebCore::IDBValue::IDBValue):
+
+ * Modules/indexeddb/server/IDBBackingStore.h:
+ (WebCore::IDBServer::IDBBackingStoreTemporaryFileHandler::~IDBBackingStoreTemporaryFileHandler):
+
+ * Modules/indexeddb/server/IDBServer.cpp:
+ (WebCore::IDBServer::IDBServer::create):
+ (WebCore::IDBServer::IDBServer::IDBServer):
+ (WebCore::IDBServer::IDBServer::createBackingStore):
+ * Modules/indexeddb/server/IDBServer.h:
+
+ * Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
+ (WebCore::IDBServer::blobRecordsTableSchema):
+ (WebCore::IDBServer::blobRecordsTableSchemaAlternate):
+ (WebCore::IDBServer::blobFilesTableSchema):
+ (WebCore::IDBServer::blobFilesTableSchemaAlternate):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::SQLiteIDBBackingStore):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::ensureValidBlobTables):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::getOrEstablishDatabaseInfo):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::addRecord):
+ * Modules/indexeddb/server/SQLiteIDBBackingStore.h:
+ (WebCore::IDBServer::SQLiteIDBBackingStore::temporaryFileHandler):
+
+ * Modules/indexeddb/server/SQLiteIDBTransaction.cpp:
+ (WebCore::IDBServer::SQLiteIDBTransaction::commit):
+ (WebCore::IDBServer::SQLiteIDBTransaction::moveBlobFilesIfNecessary):
+ (WebCore::IDBServer::SQLiteIDBTransaction::abort):
+ (WebCore::IDBServer::SQLiteIDBTransaction::reset):
+ (WebCore::IDBServer::SQLiteIDBTransaction::addBlobFile):
+ * Modules/indexeddb/server/SQLiteIDBTransaction.h:
+
+ * Modules/indexeddb/shared/InProcessIDBServer.cpp:
+ (WebCore::InProcessIDBServer::InProcessIDBServer):
+ (WebCore::InProcessIDBServer::accessToTemporaryFileComplete):
+ * Modules/indexeddb/shared/InProcessIDBServer.h:
+
+ * bindings/js/SerializedScriptValue.cpp:
+ (WebCore::SerializedScriptValue::blobURLsIsolatedCopy):
+ * bindings/js/SerializedScriptValue.h:
+
+ * platform/FileSystem.h:
+ * platform/gtk/FileSystemGtk.cpp:
+ (WebCore::hardLinkOrCopyFile):
+ * platform/posix/FileSystemPOSIX.cpp:
+ (WebCore::hardLinkOrCopyFile):
+
2016-04-08 Joanmarie Diggs <jdiggs@igalia.com>
AX: [ATK] Crash getting text under element in CSS table
diff --git a/Source/WebCore/Modules/indexeddb/IDBValue.cpp b/Source/WebCore/Modules/indexeddb/IDBValue.cpp
index f1dee12..921ec33 100644
--- a/Source/WebCore/Modules/indexeddb/IDBValue.cpp
+++ b/Source/WebCore/Modules/indexeddb/IDBValue.cpp
@@ -38,6 +38,7 @@
IDBValue::IDBValue(const SerializedScriptValue& scriptValue)
: m_data(ThreadSafeDataBuffer::copyVector(scriptValue.data()))
+ , m_blobURLs(scriptValue.blobURLsIsolatedCopy())
{
}
diff --git a/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h b/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h
index 5294a17..f90e4f8 100644
--- a/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h
+++ b/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h
@@ -50,6 +50,13 @@
namespace IDBServer {
+class IDBBackingStoreTemporaryFileHandler {
+public:
+ virtual ~IDBBackingStoreTemporaryFileHandler() { }
+ virtual void prepareForAccessToTemporaryFile(const String& path) = 0;
+ virtual void accessToTemporaryFileComplete(const String& path) = 0;
+};
+
class IDBBackingStore {
public:
virtual ~IDBBackingStore() { }
diff --git a/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp b/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp
index 99a3d47..d1898e1 100644
--- a/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp
+++ b/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp
@@ -39,24 +39,26 @@
namespace WebCore {
namespace IDBServer {
-Ref<IDBServer> IDBServer::create()
+Ref<IDBServer> IDBServer::create(IDBBackingStoreTemporaryFileHandler& fileHandler)
{
- return adoptRef(*new IDBServer());
+ return adoptRef(*new IDBServer(fileHandler));
}
-Ref<IDBServer> IDBServer::create(const String& databaseDirectoryPath)
+Ref<IDBServer> IDBServer::create(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler& fileHandler)
{
- return adoptRef(*new IDBServer(databaseDirectoryPath));
+ return adoptRef(*new IDBServer(databaseDirectoryPath, fileHandler));
}
-IDBServer::IDBServer()
+IDBServer::IDBServer(IDBBackingStoreTemporaryFileHandler& fileHandler)
+ : m_backingStoreTemporaryFileHandler(fileHandler)
{
Locker<Lock> locker(m_databaseThreadCreationLock);
m_threadID = createThread(IDBServer::databaseThreadEntry, this, "IndexedDatabase Server");
}
-IDBServer::IDBServer(const String& databaseDirectoryPath)
+IDBServer::IDBServer(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler& fileHandler)
: m_databaseDirectoryPath(databaseDirectoryPath)
+ , m_backingStoreTemporaryFileHandler(fileHandler)
{
LOG(IndexedDB, "IDBServer created at path %s", databaseDirectoryPath.utf8().data());
@@ -120,7 +122,7 @@
if (m_databaseDirectoryPath.isEmpty())
return MemoryIDBBackingStore::create(identifier);
- return std::make_unique<SQLiteIDBBackingStore>(identifier, m_databaseDirectoryPath);
+ return std::make_unique<SQLiteIDBBackingStore>(identifier, m_databaseDirectoryPath, m_backingStoreTemporaryFileHandler);
}
void IDBServer::openDatabase(const IDBRequestData& requestData)
diff --git a/Source/WebCore/Modules/indexeddb/server/IDBServer.h b/Source/WebCore/Modules/indexeddb/server/IDBServer.h
index 94549c8..65a02c2 100644
--- a/Source/WebCore/Modules/indexeddb/server/IDBServer.h
+++ b/Source/WebCore/Modules/indexeddb/server/IDBServer.h
@@ -49,10 +49,12 @@
namespace IDBServer {
+class IDBBackingStoreTemporaryFileHandler;
+
class IDBServer : public RefCounted<IDBServer> {
public:
- static Ref<IDBServer> create();
- WEBCORE_EXPORT static Ref<IDBServer> create(const String& databaseDirectoryPath);
+ static Ref<IDBServer> create(IDBBackingStoreTemporaryFileHandler&);
+ WEBCORE_EXPORT static Ref<IDBServer> create(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler&);
WEBCORE_EXPORT void registerConnection(IDBConnectionToClient&);
WEBCORE_EXPORT void unregisterConnection(IDBConnectionToClient&);
@@ -93,8 +95,8 @@
std::unique_ptr<IDBBackingStore> createBackingStore(const IDBDatabaseIdentifier&);
private:
- IDBServer();
- IDBServer(const String& databaseDirectoryPath);
+ IDBServer(IDBBackingStoreTemporaryFileHandler&);
+ IDBServer(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler&);
UniqueIDBDatabase& getOrCreateUniqueIDBDatabase(const IDBDatabaseIdentifier&);
@@ -117,6 +119,7 @@
HashMap<IDBResourceIdentifier, UniqueIDBDatabaseTransaction*> m_transactions;
String m_databaseDirectoryPath;
+ IDBBackingStoreTemporaryFileHandler& m_backingStoreTemporaryFileHandler;
};
} // namespace IDBServer
diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp
index e557a0e..8dba54c 100644
--- a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp
+++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp
@@ -160,9 +160,43 @@
return v2IndexRecordsTableSchemaString;
}
+static const String blobRecordsTableSchema(const String& tableName)
+{
+ return makeString("CREATE TABLE ", tableName, " (objectStoreRow INTEGER NOT NULL ON CONFLICT FAIL, blobURL TEXT NOT NULL ON CONFLICT FAIL)");
+}
-SQLiteIDBBackingStore::SQLiteIDBBackingStore(const IDBDatabaseIdentifier& identifier, const String& databaseRootDirectory)
+static const String& blobRecordsTableSchema()
+{
+ static NeverDestroyed<String> blobRecordsTableSchemaString(blobRecordsTableSchema("BlobRecords"));
+ return blobRecordsTableSchemaString;
+}
+
+static const String& blobRecordsTableSchemaAlternate()
+{
+ static NeverDestroyed<String> blobRecordsTableSchemaString(blobRecordsTableSchema("\"BlobRecords\""));
+ return blobRecordsTableSchemaString;
+}
+
+static const String blobFilesTableSchema(const String& tableName)
+{
+ return makeString("CREATE TABLE ", tableName, " (blobURL TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, fileName TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL)");
+}
+
+static const String& blobFilesTableSchema()
+{
+ static NeverDestroyed<String> blobFilesTableSchemaString(blobFilesTableSchema("BlobFiles"));
+ return blobFilesTableSchemaString;
+}
+
+static const String& blobFilesTableSchemaAlternate()
+{
+ static NeverDestroyed<String> blobFilesTableSchemaString(blobFilesTableSchema("\"BlobFiles\""));
+ return blobFilesTableSchemaString;
+}
+
+SQLiteIDBBackingStore::SQLiteIDBBackingStore(const IDBDatabaseIdentifier& identifier, const String& databaseRootDirectory, IDBBackingStoreTemporaryFileHandler& fileHandler)
: m_identifier(identifier)
+ , m_temporaryFileHandler(fileHandler)
{
m_absoluteDatabaseDirectory = identifier.databaseDirectoryRelativeToRoot(databaseRootDirectory);
}
@@ -275,6 +309,78 @@
return true;
}
+bool SQLiteIDBBackingStore::ensureValidBlobTables()
+{
+ ASSERT(m_sqliteDB);
+ ASSERT(m_sqliteDB->isOpen());
+
+ String currentSchema;
+ {
+ // Fetch the schema for an existing blob record table.
+ SQLiteStatement statement(*m_sqliteDB, "SELECT type, sql FROM sqlite_master WHERE tbl_name='BlobRecords'");
+ if (statement.prepare() != SQLITE_OK) {
+ LOG_ERROR("Unable to prepare statement to fetch schema for the BlobRecords table.");
+ return false;
+ }
+
+ int sqliteResult = statement.step();
+
+ // If there is no BlobRecords table at all, create it..
+ if (sqliteResult == SQLITE_DONE) {
+ if (!m_sqliteDB->executeCommand(blobRecordsTableSchema())) {
+ LOG_ERROR("Could not create BlobRecords table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return false;
+ }
+
+ currentSchema = blobRecordsTableSchema();
+ } else if (sqliteResult != SQLITE_ROW) {
+ LOG_ERROR("Error executing statement to fetch schema for the BlobRecords table.");
+ return false;
+ } else
+ currentSchema = statement.getColumnText(1);
+ }
+
+ if (currentSchema != blobRecordsTableSchema() && currentSchema != blobRecordsTableSchemaAlternate()) {
+ LOG_ERROR("Invalid BlobRecords table schema found");
+ return false;
+ }
+
+ {
+ // Fetch the schema for an existing blob file table.
+ SQLiteStatement statement(*m_sqliteDB, "SELECT type, sql FROM sqlite_master WHERE tbl_name='BlobFiles'");
+ if (statement.prepare() != SQLITE_OK) {
+ LOG_ERROR("Unable to prepare statement to fetch schema for the BlobFiles table.");
+ return false;
+ }
+
+ int sqliteResult = statement.step();
+
+ // If there is no BlobFiles table at all, create it and then bail.
+ if (sqliteResult == SQLITE_DONE) {
+ if (!m_sqliteDB->executeCommand(blobFilesTableSchema())) {
+ LOG_ERROR("Could not create BlobFiles table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+ return false;
+ }
+
+ return true;
+ }
+
+ if (sqliteResult != SQLITE_ROW) {
+ LOG_ERROR("Error executing statement to fetch schema for the BlobFiles table.");
+ return false;
+ }
+
+ currentSchema = statement.getColumnText(1);
+ }
+
+ if (currentSchema != blobFilesTableSchema() && currentSchema != blobFilesTableSchemaAlternate()) {
+ LOG_ERROR("Invalid BlobFiles table schema found");
+ return false;
+ }
+
+ return true;
+}
+
bool SQLiteIDBBackingStore::ensureValidRecordsTable()
{
ASSERT(m_sqliteDB);
@@ -612,6 +718,12 @@
return { IDBDatabaseException::UnknownError, ASCIILiteral("Error creating or migrating Index Records table in database") };
}
+ if (!ensureValidBlobTables()) {
+ LOG_ERROR("Error creating or confirming Blob Records tables in database");
+ m_sqliteDB = nullptr;
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Error creating or confirming Blob Records tables in database") };
+ }
+
auto databaseInfo = extractExistingDatabaseInfo();
if (!databaseInfo)
databaseInfo = createAndPopulateInitialDatabaseInfo();
@@ -1300,13 +1412,14 @@
return error;
}
-IDBError SQLiteIDBBackingStore::addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& objectStoreInfo, const IDBKeyData& keyData, const ThreadSafeDataBuffer& value, const Vector<String>&, const Vector<String>&)
+IDBError SQLiteIDBBackingStore::addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& objectStoreInfo, const IDBKeyData& keyData, const ThreadSafeDataBuffer& value, const Vector<String>& blobURLs, const Vector<String>& blobFiles)
{
LOG(IndexedDB, "SQLiteIDBBackingStore::addRecord - key %s, object store %" PRIu64, keyData.loggingString().utf8().data(), objectStoreInfo.identifier());
ASSERT(m_sqliteDB);
ASSERT(m_sqliteDB->isOpen());
ASSERT(value.data());
+ ASSERT(blobURLs.size() == blobFiles.size());
auto* transaction = m_transactions.get(transactionIdentifier);
if (!transaction || !transaction->inProgress()) {
@@ -1323,6 +1436,8 @@
LOG_ERROR("Unable to serialize IDBKey to be stored in an object store");
return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKey to be stored in an object store") };
}
+
+ int64_t recordID = 0;
{
SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO Records VALUES (?, CAST(? AS TEXT), ?, NULL);"));
if (sql.prepare() != SQLITE_OK
@@ -1333,6 +1448,8 @@
LOG_ERROR("Could not put record for object store %" PRIi64 " in Records table (%i) - %s", objectStoreInfo.identifier(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to store record in object store") };
}
+
+ recordID = m_sqliteDB->lastInsertRowID();
}
auto error = updateAllIndexesForAddRecord(objectStoreInfo, keyData, value);
@@ -1348,6 +1465,55 @@
}
}
+ for (size_t i = 0; i < blobURLs.size(); ++i) {
+ auto& url = blobURLs[i];
+ {
+ SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO BlobRecords VALUES (?, ?);"));
+ if (sql.prepare() != SQLITE_OK
+ || sql.bindInt64(1, recordID) != SQLITE_OK
+ || sql.bindText(2, url) != SQLITE_OK
+ || sql.step() != SQLITE_DONE) {
+ LOG_ERROR("Unable to record Blob record in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to record Blob record in database") };
+ }
+ }
+ int64_t potentialFileNameInteger = m_sqliteDB->lastInsertRowID();
+
+ // If we already have a file for this blobURL, nothing left to do.
+ {
+ SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT fileName FROM BlobFiles WHERE blobURL = ?;"));
+ if (sql.prepare() != SQLITE_OK
+ || sql.bindText(1, url) != SQLITE_OK) {
+ LOG_ERROR("Unable to examine Blob filenames in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to examine Blob filenames in database") };
+ }
+
+ int result = sql.step();
+ if (result != SQLITE_ROW && result != SQLITE_DONE) {
+ LOG_ERROR("Unable to examine Blob filenames in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to examine Blob filenames in database") };
+ }
+
+ if (result == SQLITE_ROW)
+ continue;
+ }
+
+ // We don't already have a file for this blobURL, so commit our file as a unique filename
+ String storedFilename = String::format("%" PRId64 ".blob", potentialFileNameInteger);
+ {
+ SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO BlobFiles VALUES (?, ?);"));
+ if (sql.prepare() != SQLITE_OK
+ || sql.bindText(1, url) != SQLITE_OK
+ || sql.bindText(2, storedFilename) != SQLITE_OK
+ || sql.step() != SQLITE_DONE) {
+ LOG_ERROR("Unable to record Blob file record in database");
+ return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to record Blob file record in database") };
+ }
+ }
+
+ transaction->addBlobFile(blobFiles[i], storedFilename);
+ }
+
transaction->notifyCursorsOfChanges(objectStoreInfo.identifier());
return error;
diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h
index 7f2392a..8e91516 100644
--- a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h
+++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h
@@ -47,7 +47,7 @@
class SQLiteIDBBackingStore : public IDBBackingStore {
public:
- SQLiteIDBBackingStore(const IDBDatabaseIdentifier&, const String& databaseRootDirectory);
+ SQLiteIDBBackingStore(const IDBDatabaseIdentifier&, const String& databaseRootDirectory, IDBBackingStoreTemporaryFileHandler&);
~SQLiteIDBBackingStore() final;
@@ -81,13 +81,17 @@
void unregisterCursor(SQLiteIDBCursor&);
+ String fullDatabaseDirectory() const;
+
+ IDBBackingStoreTemporaryFileHandler& temporaryFileHandler() const { return m_temporaryFileHandler; }
+
private:
String filenameForDatabaseName() const;
- String fullDatabaseDirectory() const;
String fullDatabasePath() const;
bool ensureValidRecordsTable();
bool ensureValidIndexRecordsTable();
+ bool ensureValidBlobTables();
std::unique_ptr<IDBDatabaseInfo> createAndPopulateInitialDatabaseInfo();
std::unique_ptr<IDBDatabaseInfo> extractExistingDatabaseInfo();
@@ -118,6 +122,8 @@
RefPtr<JSC::VM> m_vm;
JSC::Strong<JSC::JSGlobalObject> m_globalObject;
+
+ IDBBackingStoreTemporaryFileHandler& m_temporaryFileHandler;
};
} // namespace IDBServer
diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp
index dcbe331..a6a0c2b 100644
--- a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp
+++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp
@@ -27,6 +27,7 @@
#if ENABLE(INDEXED_DATABASE)
+#include "FileSystem.h"
#include "IDBCursorInfo.h"
#include "IndexedDB.h"
#include "SQLiteIDBBackingStore.h"
@@ -75,12 +76,36 @@
if (m_sqliteTransaction->inProgress())
return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to commit SQLite transaction in database backend") };
+ moveBlobFilesIfNecessary();
+
reset();
return { };
}
+void SQLiteIDBTransaction::moveBlobFilesIfNecessary()
+{
+ String databaseDirectory = m_backingStore.fullDatabaseDirectory();
+ for (auto& entry : m_blobTemporaryAndStoredFilenames) {
+ m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(entry.first);
+
+ if (!hardLinkOrCopyFile(entry.first, pathByAppendingComponent(databaseDirectory, entry.second)))
+ LOG_ERROR("Failed to link/copy temporary blob file '%s' to location '%s'", entry.first.utf8().data(), pathByAppendingComponent(databaseDirectory, entry.second).utf8().data());
+
+ m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(entry.first);
+ }
+
+ m_blobTemporaryAndStoredFilenames.clear();
+}
+
IDBError SQLiteIDBTransaction::abort()
{
+ for (auto& entry : m_blobTemporaryAndStoredFilenames) {
+ m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(entry.first);
+ m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(entry.first);
+ }
+
+ m_blobTemporaryAndStoredFilenames.clear();
+
if (!m_sqliteTransaction || !m_sqliteTransaction->inProgress())
return { IDBDatabaseException::UnknownError, ASCIILiteral("No SQLite transaction in progress to abort") };
@@ -97,6 +122,7 @@
{
m_sqliteTransaction = nullptr;
clearCursors();
+ ASSERT(m_blobTemporaryAndStoredFilenames.isEmpty());
}
std::unique_ptr<SQLiteIDBCursor> SQLiteIDBTransaction::maybeOpenBackingStoreCursor(uint64_t objectStoreID, uint64_t indexID, const IDBKeyRangeData& range)
@@ -171,6 +197,11 @@
return m_sqliteTransaction && m_sqliteTransaction->inProgress();
}
+void SQLiteIDBTransaction::addBlobFile(const String& temporaryPath, const String& storedFilename)
+{
+ m_blobTemporaryAndStoredFilenames.append({ temporaryPath, storedFilename });
+}
+
} // namespace IDBServer
} // namespace WebCore
diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.h b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.h
index 0efe744..98f8326 100644
--- a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.h
+++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.h
@@ -71,16 +71,21 @@
SQLiteTransaction* sqliteTransaction() const { return m_sqliteTransaction.get(); }
+ void addBlobFile(const String& temporaryPath, const String& storedFilename);
+
private:
void clearCursors();
void reset();
+ void moveBlobFilesIfNecessary();
+
IDBTransactionInfo m_info;
SQLiteIDBBackingStore& m_backingStore;
std::unique_ptr<SQLiteTransaction> m_sqliteTransaction;
HashMap<IDBResourceIdentifier, std::unique_ptr<SQLiteIDBCursor>> m_cursors;
HashSet<SQLiteIDBCursor*> m_backingStoreCursors;
+ Vector<std::pair<String, String>> m_blobTemporaryAndStoredFilenames;
};
} // namespace IDBServer
diff --git a/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp b/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp
index 6a53183..5dc83eb 100644
--- a/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp
+++ b/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp
@@ -28,6 +28,7 @@
#if ENABLE(INDEXED_DATABASE)
+#include "FileSystem.h"
#include "IDBConnectionToClient.h"
#include "IDBConnectionToServer.h"
#include "IDBCursorInfo.h"
@@ -56,7 +57,7 @@
}
InProcessIDBServer::InProcessIDBServer()
- : m_server(IDBServer::IDBServer::create())
+ : m_server(IDBServer::IDBServer::create(*this))
{
relaxAdoptionRequirement();
m_connectionToServer = IDBClient::IDBConnectionToServer::create(*this);
@@ -64,7 +65,7 @@
}
InProcessIDBServer::InProcessIDBServer(const String& databaseDirectoryPath)
- : m_server(IDBServer::IDBServer::create(databaseDirectoryPath))
+ : m_server(IDBServer::IDBServer::create(databaseDirectoryPath, *this))
{
relaxAdoptionRequirement();
m_connectionToServer = IDBClient::IDBConnectionToServer::create(*this);
@@ -400,6 +401,11 @@
});
}
+void InProcessIDBServer::accessToTemporaryFileComplete(const String& path)
+{
+ deleteFile(path);
+}
+
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h b/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h
index 82a2091..fe284c2 100644
--- a/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h
+++ b/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h
@@ -46,7 +46,7 @@
class IDBServer;
}
-class InProcessIDBServer final : public IDBClient::IDBConnectionToServerDelegate, public IDBServer::IDBConnectionToClientDelegate, public RefCounted<InProcessIDBServer> {
+class InProcessIDBServer final : public IDBClient::IDBConnectionToServerDelegate, public IDBServer::IDBConnectionToClientDelegate, public RefCounted<InProcessIDBServer>, public IDBServer::IDBBackingStoreTemporaryFileHandler {
public:
WEBCORE_EXPORT static Ref<InProcessIDBServer> create();
WEBCORE_EXPORT static Ref<InProcessIDBServer> create(const String& databaseDirectoryPath);
@@ -100,6 +100,9 @@
void ref() override { RefCounted<InProcessIDBServer>::ref(); }
void deref() override { RefCounted<InProcessIDBServer>::deref(); }
+ void prepareForAccessToTemporaryFile(const String&) override { }
+ void accessToTemporaryFileComplete(const String& path) override;
+
private:
InProcessIDBServer();
InProcessIDBServer(const String& databaseDirectoryPath);
diff --git a/Source/WebCore/bindings/js/SerializedScriptValue.cpp b/Source/WebCore/bindings/js/SerializedScriptValue.cpp
index 730329d..5e836d0 100644
--- a/Source/WebCore/bindings/js/SerializedScriptValue.cpp
+++ b/Source/WebCore/bindings/js/SerializedScriptValue.cpp
@@ -2748,6 +2748,16 @@
}
#if ENABLE(INDEXED_DATABASE)
+Vector<String> SerializedScriptValue::blobURLsIsolatedCopy() const
+{
+ Vector<String> result;
+ result.reserveInitialCapacity(m_blobURLs.size());
+ for (auto& url : m_blobURLs)
+ result.uncheckedAppend(url.isolatedCopy());
+
+ return result;
+}
+
void SerializedScriptValue::writeBlobsToDiskForIndexedDB(std::function<void (const IDBValue&)> completionHandler)
{
ASSERT(isMainThread());
@@ -2769,6 +2779,6 @@
completionHandler({ *this, m_blobURLs, blobFilePaths });
});
}
-#endif
+#endif // ENABLE(INDEXED_DATABASE)
} // namespace WebCore
diff --git a/Source/WebCore/bindings/js/SerializedScriptValue.h b/Source/WebCore/bindings/js/SerializedScriptValue.h
index 645dcb8..15cbf8a 100644
--- a/Source/WebCore/bindings/js/SerializedScriptValue.h
+++ b/Source/WebCore/bindings/js/SerializedScriptValue.h
@@ -84,9 +84,11 @@
const Vector<uint8_t>& data() const { return m_data; }
bool hasBlobURLs() const { return !m_blobURLs.isEmpty(); }
+
#if ENABLE(INDEXED_DATABASE)
+ Vector<String> blobURLsIsolatedCopy() const;
void writeBlobsToDiskForIndexedDB(std::function<void (const IDBValue&)> completionHandler);
-#endif
+#endif // ENABLE(INDEXED_DATABASE)
static Ref<SerializedScriptValue> createFromWireBytes(Vector<uint8_t>&& data)
{
diff --git a/Source/WebCore/platform/FileSystem.h b/Source/WebCore/platform/FileSystem.h
index 03afffe..f1a78df 100644
--- a/Source/WebCore/platform/FileSystem.h
+++ b/Source/WebCore/platform/FileSystem.h
@@ -181,6 +181,9 @@
// Returns true if the write was successful, false if it was not.
bool appendFileContentsToFileHandle(const String& path, PlatformFileHandle&);
+// Hard links a file if possible, copies it if not.
+bool hardLinkOrCopyFile(const String& source, const String& destination);
+
#if USE(FILE_LOCK)
bool lockFile(PlatformFileHandle, FileLockMode);
bool unlockFile(PlatformFileHandle);
diff --git a/Source/WebCore/platform/gtk/FileSystemGtk.cpp b/Source/WebCore/platform/gtk/FileSystemGtk.cpp
index 915ea69..1d8ed7d 100644
--- a/Source/WebCore/platform/gtk/FileSystemGtk.cpp
+++ b/Source/WebCore/platform/gtk/FileSystemGtk.cpp
@@ -364,4 +364,11 @@
return g_module_close(module);
#endif
}
+
+bool hardLinkOrCopyFile(const String&, const String&)
+{
+ // FIXME: Implement
+ return false;
+}
+
}
diff --git a/Source/WebCore/platform/posix/FileSystemPOSIX.cpp b/Source/WebCore/platform/posix/FileSystemPOSIX.cpp
index fe276b0..f92071e 100644
--- a/Source/WebCore/platform/posix/FileSystemPOSIX.cpp
+++ b/Source/WebCore/platform/posix/FileSystemPOSIX.cpp
@@ -346,4 +346,35 @@
}
#endif
+bool hardLinkOrCopyFile(const String& source, const String& destination)
+{
+ if (source.isEmpty() || destination.isEmpty())
+ return false;
+
+ CString fsSource = fileSystemRepresentation(source);
+ if (!fsSource.data())
+ return false;
+
+ CString fsDestination = fileSystemRepresentation(destination);
+ if (!fsDestination.data())
+ return false;
+
+ if (!link(fsSource.data(), fsDestination.data()))
+ return true;
+
+ // Hard link failed. Perform a copy instead.
+ auto handle = open(fsDestination.data(), O_WRONLY | O_CREAT | O_EXCL);
+ if (handle == -1)
+ return false;
+
+ bool appendResult = appendFileContentsToFileHandle(source, handle);
+ close(handle);
+
+ // If the copy failed, delete the unusable file.
+ if (!appendResult)
+ unlink(fsDestination.data());
+
+ return appendResult;
+}
+
} // namespace WebCore
diff --git a/Source/WebKit2/ChangeLog b/Source/WebKit2/ChangeLog
index 5286d4a..d1b85b3 100644
--- a/Source/WebKit2/ChangeLog
+++ b/Source/WebKit2/ChangeLog
@@ -1,3 +1,40 @@
+2016-04-08 Brady Eidson <beidson@apple.com>
+
+ Modern IDB (Blob support): Write blobs to temporary files and move them to the correct location when storing them.
+ https://bugs.webkit.org/show_bug.cgi?id=156321
+
+ Reviewed by Alex Christensen, Andy Estes, and Darin Adler.
+
+ The NetworkProcess writes a blob to a temporary file, then tells the UIProcess to grant the DatabaseProcess
+ a Sandbox Extension to that path.
+
+ It then tells the WebProcess the paths for the temporary files, which then tells the DatabaseProcess to store
+ the contents of those files as blob references in the database.
+
+ Since the UIProcess had already granted it a Sandbox Extension, it is able to do so.
+
+ * DatabaseProcess/DatabaseProcess.cpp:
+ (WebKit::DatabaseProcess::idbServer):
+ (WebKit::DatabaseProcess::grantSandboxExtensionsForBlobs):
+ (WebKit::DatabaseProcess::prepareForAccessToTemporaryFile):
+ (WebKit::DatabaseProcess::accessToTemporaryFileComplete):
+ * DatabaseProcess/DatabaseProcess.h:
+ * DatabaseProcess/DatabaseProcess.messages.in:
+
+ * NetworkProcess/NetworkConnectionToWebProcess.cpp:
+ (WebKit::NetworkConnectionToWebProcess::writeBlobsToTemporaryFiles):
+
+ * NetworkProcess/NetworkProcess.cpp:
+ (WebKit::NetworkProcess::grantSandboxExtensionsToDatabaseProcessForBlobs):
+ (WebKit::NetworkProcess::didGrantSandboxExtensionsToDatabaseProcessForBlobs):
+ * NetworkProcess/NetworkProcess.h:
+ * NetworkProcess/NetworkProcess.messages.in:
+
+ * UIProcess/Network/NetworkProcessProxy.cpp:
+ (WebKit::NetworkProcessProxy::grantSandboxExtensionsToDatabaseProcessForBlobs):
+ * UIProcess/Network/NetworkProcessProxy.h:
+ * UIProcess/Network/NetworkProcessProxy.messages.in:
+
2016-04-07 Joseph Pecoraro <pecoraro@apple.com>
Remove ENABLE(ENABLE_ES6_CLASS_SYNTAX) guards
diff --git a/Source/WebKit2/DatabaseProcess/DatabaseProcess.cpp b/Source/WebKit2/DatabaseProcess/DatabaseProcess.cpp
index 9c6ae02..1bed587 100644
--- a/Source/WebKit2/DatabaseProcess/DatabaseProcess.cpp
+++ b/Source/WebKit2/DatabaseProcess/DatabaseProcess.cpp
@@ -98,7 +98,7 @@
IDBServer::IDBServer& DatabaseProcess::idbServer()
{
if (!m_idbServer)
- m_idbServer = IDBServer::IDBServer::create(indexedDatabaseDirectory());
+ m_idbServer = IDBServer::IDBServer::create(indexedDatabaseDirectory(), DatabaseProcess::singleton());
return *m_idbServer;
}
@@ -305,6 +305,33 @@
#endif
}
+void DatabaseProcess::grantSandboxExtensionsForBlobs(const Vector<String>& paths, const SandboxExtension::HandleArray& handles)
+{
+ ASSERT(paths.size() == handles.size());
+
+ for (size_t i = 0; i < paths.size(); ++i) {
+ auto result = m_blobTemporaryFileSandboxExtensions.add(paths[i], SandboxExtension::create(handles[i]));
+ ASSERT_UNUSED(result, result.isNewEntry);
+ }
+}
+
+void DatabaseProcess::prepareForAccessToTemporaryFile(const String& path)
+{
+ if (auto extension = m_blobTemporaryFileSandboxExtensions.get(path))
+ extension->consume();
+}
+
+void DatabaseProcess::accessToTemporaryFileComplete(const String& path)
+{
+ // We've either hard linked the temporary blob file to the database directory, copied it there,
+ // or the transaction is being aborted.
+ // In any of those cases, we can delete the temporary blob file now.
+ deleteFile(path);
+
+ if (auto extension = m_blobTemporaryFileSandboxExtensions.take(path))
+ extension->revoke();
+}
+
#if ENABLE(INDEXED_DATABASE)
Vector<RefPtr<WebCore::SecurityOrigin>> DatabaseProcess::indexedDatabaseOrigins()
{
diff --git a/Source/WebKit2/DatabaseProcess/DatabaseProcess.h b/Source/WebKit2/DatabaseProcess/DatabaseProcess.h
index 689254f..4f85db9 100644
--- a/Source/WebKit2/DatabaseProcess/DatabaseProcess.h
+++ b/Source/WebKit2/DatabaseProcess/DatabaseProcess.h
@@ -29,6 +29,8 @@
#if ENABLE(DATABASE_PROCESS)
#include "ChildProcess.h"
+#include "SandboxExtension.h"
+#include <WebCore/IDBBackingStore.h>
#include <WebCore/IDBServer.h>
#include <WebCore/UniqueIDBDatabase.h>
#include <wtf/NeverDestroyed.h>
@@ -45,7 +47,7 @@
enum class WebsiteDataType;
struct DatabaseProcessCreationParameters;
-class DatabaseProcess : public ChildProcess {
+class DatabaseProcess : public ChildProcess, public WebCore::IDBServer::IDBBackingStoreTemporaryFileHandler {
WTF_MAKE_NONCOPYABLE(DatabaseProcess);
friend class NeverDestroyed<DatabaseProcess>;
public:
@@ -65,6 +67,10 @@
void postDatabaseTask(std::unique_ptr<WebCore::CrossThreadTask>);
+ // WebCore::IDBServer::IDBBackingStoreFileHandler
+ void prepareForAccessToTemporaryFile(const String& path) final;
+ void accessToTemporaryFileComplete(const String& path) final;
+
private:
DatabaseProcess();
@@ -90,6 +96,7 @@
void fetchWebsiteData(WebCore::SessionID, OptionSet<WebsiteDataType> websiteDataTypes, uint64_t callbackID);
void deleteWebsiteData(WebCore::SessionID, OptionSet<WebsiteDataType> websiteDataTypes, std::chrono::system_clock::time_point modifiedSince, uint64_t callbackID);
void deleteWebsiteDataForOrigins(WebCore::SessionID, OptionSet<WebsiteDataType> websiteDataTypes, const Vector<WebCore::SecurityOriginData>& origins, uint64_t callbackID);
+ void grantSandboxExtensionsForBlobs(const Vector<String>& paths, const SandboxExtension::HandleArray&);
#if ENABLE(INDEXED_DATABASE)
Vector<RefPtr<WebCore::SecurityOrigin>> indexedDatabaseOrigins();
@@ -109,6 +116,8 @@
String m_indexedDatabaseDirectory;
RefPtr<WebCore::IDBServer::IDBServer> m_idbServer;
+
+ HashMap<String, RefPtr<SandboxExtension>> m_blobTemporaryFileSandboxExtensions;
#endif
Deque<std::unique_ptr<WebCore::CrossThreadTask>> m_databaseTasks;
diff --git a/Source/WebKit2/DatabaseProcess/DatabaseProcess.messages.in b/Source/WebKit2/DatabaseProcess/DatabaseProcess.messages.in
index 4cb9da0..82e2764 100644
--- a/Source/WebKit2/DatabaseProcess/DatabaseProcess.messages.in
+++ b/Source/WebKit2/DatabaseProcess/DatabaseProcess.messages.in
@@ -32,6 +32,7 @@
FetchWebsiteData(WebCore::SessionID sessionID, OptionSet<WebKit::WebsiteDataType> websiteDataTypes, uint64_t callbackID)
DeleteWebsiteData(WebCore::SessionID sessionID, OptionSet<WebKit::WebsiteDataType> websiteDataTypes, std::chrono::system_clock::time_point modifiedSince, uint64_t callbackID)
DeleteWebsiteDataForOrigins(WebCore::SessionID sessionID, OptionSet<WebKit::WebsiteDataType> websiteDataTypes, Vector<WebCore::SecurityOriginData> origins, uint64_t callbackID)
+ GrantSandboxExtensionsForBlobs(Vector<String> paths, WebKit::SandboxExtension::HandleArray extensions)
}
#endif // ENABLE(DATABASE_PROCESS)
diff --git a/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.cpp b/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.cpp
index 8834af3..c001570 100644
--- a/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.cpp
+++ b/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.cpp
@@ -301,10 +301,12 @@
for (auto& file : fileReferences)
file->revokeFileAccess();
- if (!m_connection || !m_connection->isValid())
- return;
+ NetworkProcess::singleton().grantSandboxExtensionsToDatabaseProcessForBlobs(fileNames, [this, protector, requestIdentifier, fileNames]() {
+ if (!m_connection || !m_connection->isValid())
+ return;
- m_connection->send(Messages::NetworkProcessConnection::DidWriteBlobsToTemporaryFiles(requestIdentifier, fileNames), 0);
+ m_connection->send(Messages::NetworkProcessConnection::DidWriteBlobsToTemporaryFiles(requestIdentifier, fileNames), 0);
+ });
});
}
diff --git a/Source/WebKit2/NetworkProcess/NetworkProcess.cpp b/Source/WebKit2/NetworkProcess/NetworkProcess.cpp
index 4b37fac..8856536 100644
--- a/Source/WebKit2/NetworkProcess/NetworkProcess.cpp
+++ b/Source/WebKit2/NetworkProcess/NetworkProcess.cpp
@@ -293,6 +293,21 @@
SessionTracker::destroySession(sessionID);
}
+void NetworkProcess::grantSandboxExtensionsToDatabaseProcessForBlobs(const Vector<String>& filenames, std::function<void ()> completionHandler)
+{
+ static uint64_t lastRequestID;
+
+ uint64_t requestID = ++lastRequestID;
+ m_sandboxExtensionForBlobsCompletionHandlers.set(requestID, completionHandler);
+ parentProcessConnection()->send(Messages::NetworkProcessProxy::GrantSandboxExtensionsToDatabaseProcessForBlobs(requestID, filenames), 0);
+}
+
+void NetworkProcess::didGrantSandboxExtensionsToDatabaseProcessForBlobs(uint64_t requestID)
+{
+ if (auto handler = m_sandboxExtensionForBlobsCompletionHandlers.take(requestID))
+ handler();
+}
+
static void fetchDiskCacheEntries(SessionID sessionID, OptionSet<WebsiteDataFetchOption> fetchOptions, std::function<void (Vector<WebsiteData::Entry>)> completionHandler)
{
#if ENABLE(NETWORK_CACHE)
diff --git a/Source/WebKit2/NetworkProcess/NetworkProcess.h b/Source/WebKit2/NetworkProcess/NetworkProcess.h
index e07583d..c280f96 100644
--- a/Source/WebKit2/NetworkProcess/NetworkProcess.h
+++ b/Source/WebKit2/NetworkProcess/NetworkProcess.h
@@ -112,6 +112,8 @@
void ensurePrivateBrowsingSession(WebCore::SessionID);
+ void grantSandboxExtensionsToDatabaseProcessForBlobs(const Vector<String>& filenames, std::function<void ()> completionHandler);
+
private:
NetworkProcess();
~NetworkProcess();
@@ -178,6 +180,8 @@
void getNetworkProcessStatistics(uint64_t callbackID);
void clearCacheForAllOrigins(uint32_t cachesToClear);
+ void didGrantSandboxExtensionsToDatabaseProcessForBlobs(uint64_t requestID);
+
#if USE(SOUP)
void setIgnoreTLSErrors(bool);
void userPreferredLanguagesChanged(const Vector<String>&);
@@ -200,6 +204,8 @@
typedef HashMap<const char*, std::unique_ptr<NetworkProcessSupplement>, PtrHash<const char*>> NetworkProcessSupplementMap;
NetworkProcessSupplementMap m_supplements;
+ HashMap<uint64_t, std::function<void ()>> m_sandboxExtensionForBlobsCompletionHandlers;
+
#if PLATFORM(COCOA)
void platformInitializeNetworkProcessCocoa(const NetworkProcessCreationParameters&);
diff --git a/Source/WebKit2/NetworkProcess/NetworkProcess.messages.in b/Source/WebKit2/NetworkProcess/NetworkProcess.messages.in
index 3c49991..89f243e 100644
--- a/Source/WebKit2/NetworkProcess/NetworkProcess.messages.in
+++ b/Source/WebKit2/NetworkProcess/NetworkProcess.messages.in
@@ -67,4 +67,6 @@
PrepareToSuspend()
CancelPrepareToSuspend()
ProcessDidResume()
+
+ DidGrantSandboxExtensionsToDatabaseProcessForBlobs(uint64_t requestID)
}
diff --git a/Source/WebKit2/UIProcess/Network/NetworkProcessProxy.cpp b/Source/WebKit2/UIProcess/Network/NetworkProcessProxy.cpp
index bb04822..4959324 100644
--- a/Source/WebKit2/UIProcess/Network/NetworkProcessProxy.cpp
+++ b/Source/WebKit2/UIProcess/Network/NetworkProcessProxy.cpp
@@ -28,9 +28,11 @@
#include "AuthenticationChallengeProxy.h"
#include "CustomProtocolManagerProxyMessages.h"
+#include "DatabaseProcessMessages.h"
#include "DownloadProxyMessages.h"
#include "NetworkProcessCreationParameters.h"
#include "NetworkProcessMessages.h"
+#include "SandboxExtension.h"
#include "WebProcessMessages.h"
#include "WebProcessPool.h"
#include "WebsiteData.h"
@@ -269,6 +271,19 @@
callback();
}
+void NetworkProcessProxy::grantSandboxExtensionsToDatabaseProcessForBlobs(uint64_t requestID, const Vector<String>& paths)
+{
+ SandboxExtension::HandleArray extensions;
+ extensions.allocate(paths.size());
+ for (size_t i = 0; i < paths.size(); ++i) {
+ // ReadWrite is required for creating hard links as well as deleting the temporary file, which the DatabaseProcess will do.
+ SandboxExtension::createHandle(paths[i], SandboxExtension::ReadWrite, extensions[i]);
+ }
+
+ m_processPool.sendToDatabaseProcessRelaunchingIfNecessary(Messages::DatabaseProcess::GrantSandboxExtensionsForBlobs(paths, extensions));
+ connection()->send(Messages::NetworkProcess::DidGrantSandboxExtensionsToDatabaseProcessForBlobs(requestID), 0);
+}
+
void NetworkProcessProxy::didFinishLaunching(ProcessLauncher* launcher, IPC::Connection::Identifier connectionIdentifier)
{
ChildProcessProxy::didFinishLaunching(launcher, connectionIdentifier);
diff --git a/Source/WebKit2/UIProcess/Network/NetworkProcessProxy.h b/Source/WebKit2/UIProcess/Network/NetworkProcessProxy.h
index 6ae5474..0930979 100644
--- a/Source/WebKit2/UIProcess/Network/NetworkProcessProxy.h
+++ b/Source/WebKit2/UIProcess/Network/NetworkProcessProxy.h
@@ -106,6 +106,7 @@
void didFetchWebsiteData(uint64_t callbackID, const WebsiteData&);
void didDeleteWebsiteData(uint64_t callbackID);
void didDeleteWebsiteDataForOrigins(uint64_t callbackID);
+ void grantSandboxExtensionsToDatabaseProcessForBlobs(uint64_t requestID, const Vector<String>& paths);
void logSampledDiagnosticMessage(uint64_t pageID, const String& message, const String& description);
void logSampledDiagnosticMessageWithResult(uint64_t pageID, const String& message, const String& description, uint32_t result);
void logSampledDiagnosticMessageWithValue(uint64_t pageID, const String& message, const String& description, const String& value);
diff --git a/Source/WebKit2/UIProcess/Network/NetworkProcessProxy.messages.in b/Source/WebKit2/UIProcess/Network/NetworkProcessProxy.messages.in
index dc93b84..47ef674 100644
--- a/Source/WebKit2/UIProcess/Network/NetworkProcessProxy.messages.in
+++ b/Source/WebKit2/UIProcess/Network/NetworkProcessProxy.messages.in
@@ -29,6 +29,8 @@
DidDeleteWebsiteData(uint64_t callbackID)
DidDeleteWebsiteDataForOrigins(uint64_t callbackID)
+ GrantSandboxExtensionsToDatabaseProcessForBlobs(uint64_t requestID, Vector<String> paths)
+
ProcessReadyToSuspend()
SetIsHoldingLockedFiles(bool isHoldingLockedFiles)