1/* 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "DatabaseProcess.h" 28 29#if ENABLE(DATABASE_PROCESS) 30 31#include "AsyncTask.h" 32#include "DatabaseProcessCreationParameters.h" 33#include "DatabaseProcessMessages.h" 34#include "DatabaseProcessProxyMessages.h" 35#include "DatabaseToWebProcessConnection.h" 36#include "UniqueIDBDatabase.h" 37#include "WebCrossThreadCopier.h" 38#include "WebOriginDataManager.h" 39#include "WebOriginDataManagerMessages.h" 40#include "WebOriginDataManagerProxyMessages.h" 41#include <WebCore/FileSystem.h> 42#include <WebCore/TextEncoding.h> 43#include <wtf/MainThread.h> 44 45using namespace WebCore; 46 47namespace WebKit { 48 49DatabaseProcess& DatabaseProcess::shared() 50{ 51 static NeverDestroyed<DatabaseProcess> databaseProcess; 52 return databaseProcess; 53} 54 55DatabaseProcess::DatabaseProcess() 56 : m_queue(adoptRef(*WorkQueue::create("com.apple.WebKit.DatabaseProcess").leakRef())) 57 , m_webOriginDataManager(std::make_unique<WebOriginDataManager>(this)) 58{ 59 // Make sure the UTF8Encoding encoding and the text encoding maps have been built on the main thread before a background thread needs it. 60 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=135365 - Need a more explicit way of doing this besides accessing the UTF8Encoding. 61 UTF8Encoding(); 62} 63 64DatabaseProcess::~DatabaseProcess() 65{ 66} 67 68void DatabaseProcess::initializeConnection(IPC::Connection* connection) 69{ 70 ChildProcess::initializeConnection(connection); 71} 72 73bool DatabaseProcess::shouldTerminate() 74{ 75 return true; 76} 77 78void DatabaseProcess::didClose(IPC::Connection*) 79{ 80 RunLoop::current().stop(); 81} 82 83void DatabaseProcess::didReceiveMessage(IPC::Connection* connection, IPC::MessageDecoder& decoder) 84{ 85 if (messageReceiverMap().dispatchMessage(connection, decoder)) 86 return; 87 88 if (decoder.messageReceiverName() == Messages::DatabaseProcess::messageReceiverName()) { 89 didReceiveDatabaseProcessMessage(connection, decoder); 90 return; 91 } 92} 93 94void DatabaseProcess::didReceiveInvalidMessage(IPC::Connection*, IPC::StringReference, IPC::StringReference) 95{ 96 RunLoop::current().stop(); 97} 98 99PassRefPtr<UniqueIDBDatabase> DatabaseProcess::getOrCreateUniqueIDBDatabase(const UniqueIDBDatabaseIdentifier& identifier) 100{ 101 auto addResult = m_idbDatabases.add(identifier, nullptr); 102 103 if (!addResult.isNewEntry) 104 return addResult.iterator->value; 105 106 RefPtr<UniqueIDBDatabase> database = UniqueIDBDatabase::create(identifier); 107 addResult.iterator->value = database.get(); 108 return database.release(); 109} 110 111void DatabaseProcess::removeUniqueIDBDatabase(const UniqueIDBDatabase& database) 112{ 113 const UniqueIDBDatabaseIdentifier& identifier = database.identifier(); 114 ASSERT(m_idbDatabases.contains(identifier)); 115 116 m_idbDatabases.remove(identifier); 117} 118 119void DatabaseProcess::initializeDatabaseProcess(const DatabaseProcessCreationParameters& parameters) 120{ 121 // ********* 122 // IMPORTANT: Do not change the directory structure for indexed databases on disk without first consulting a reviewer from Apple (<rdar://problem/17454712>) 123 // ********* 124 125 m_indexedDatabaseDirectory = parameters.indexedDatabaseDirectory; 126 SandboxExtension::consumePermanently(parameters.indexedDatabaseDirectoryExtensionHandle); 127 128 ensureIndexedDatabaseRelativePathExists(StringImpl::empty()); 129} 130 131void DatabaseProcess::ensureIndexedDatabaseRelativePathExists(const String& relativePath) 132{ 133 postDatabaseTask(createAsyncTask(*this, &DatabaseProcess::ensurePathExists, absoluteIndexedDatabasePathFromDatabaseRelativePath(relativePath))); 134} 135 136void DatabaseProcess::ensurePathExists(const String& path) 137{ 138 ASSERT(!RunLoop::isMain()); 139 140 if (!makeAllDirectories(path)) 141 LOG_ERROR("Failed to make all directories for path '%s'", path.utf8().data()); 142} 143 144String DatabaseProcess::absoluteIndexedDatabasePathFromDatabaseRelativePath(const String& relativePath) 145{ 146 // FIXME: pathByAppendingComponent() was originally designed to append individual atomic components. 147 // We don't have a function designed to append a multi-component subpath, but we should. 148 return pathByAppendingComponent(m_indexedDatabaseDirectory, relativePath); 149} 150 151void DatabaseProcess::postDatabaseTask(std::unique_ptr<AsyncTask> task) 152{ 153 ASSERT(RunLoop::isMain()); 154 155 MutexLocker locker(m_databaseTaskMutex); 156 157 m_databaseTasks.append(WTF::move(task)); 158 159 m_queue->dispatch(bind(&DatabaseProcess::performNextDatabaseTask, this)); 160} 161 162void DatabaseProcess::performNextDatabaseTask() 163{ 164 ASSERT(!RunLoop::isMain()); 165 166 std::unique_ptr<AsyncTask> task; 167 { 168 MutexLocker locker(m_databaseTaskMutex); 169 ASSERT(!m_databaseTasks.isEmpty()); 170 task = m_databaseTasks.takeFirst(); 171 } 172 173 task->performTask(); 174} 175 176void DatabaseProcess::createDatabaseToWebProcessConnection() 177{ 178#if OS(DARWIN) 179 // Create the listening port. 180 mach_port_t listeningPort; 181 mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &listeningPort); 182 183 // Create a listening connection. 184 RefPtr<DatabaseToWebProcessConnection> connection = DatabaseToWebProcessConnection::create(IPC::Connection::Identifier(listeningPort)); 185 m_databaseToWebProcessConnections.append(connection.release()); 186 187 IPC::Attachment clientPort(listeningPort, MACH_MSG_TYPE_MAKE_SEND); 188 parentProcessConnection()->send(Messages::DatabaseProcessProxy::DidCreateDatabaseToWebProcessConnection(clientPort), 0); 189#else 190 notImplemented(); 191#endif 192} 193 194void DatabaseProcess::getIndexedDatabaseOrigins(uint64_t callbackID) 195{ 196 postDatabaseTask(createAsyncTask(*this, &DatabaseProcess::doGetIndexedDatabaseOrigins, callbackID)); 197} 198 199void DatabaseProcess::doGetIndexedDatabaseOrigins(uint64_t callbackID) 200{ 201 Vector<SecurityOriginData> results; 202 203 if (m_indexedDatabaseDirectory.isEmpty()) { 204 send(Messages::WebOriginDataManagerProxy::DidGetOrigins(results, callbackID), 0); 205 return; 206 } 207 208 Vector<String> originPaths = listDirectory(m_indexedDatabaseDirectory, "*"); 209 for (auto& originPath : originPaths) { 210 URL url; 211 url.setProtocol(ASCIILiteral("file")); 212 url.setPath(originPath); 213 214 String databaseIdentifier = url.lastPathComponent(); 215 216 RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::maybeCreateFromDatabaseIdentifier(databaseIdentifier); 217 if (!securityOrigin) 218 continue; 219 220 results.append(SecurityOriginData::fromSecurityOrigin(securityOrigin.get())); 221 } 222 223 send(Messages::WebOriginDataManagerProxy::DidGetOrigins(results, callbackID), 0); 224} 225 226static void removeAllDatabasesForOriginPath(const String& originPath, double startDate, double endDate) 227{ 228 // FIXME: We should also close/invalidate any live handles to the database files we are about to delete. 229 // Right now: 230 // - For read-only operations, they will continue functioning as normal on the unlinked file. 231 // - For write operations, they will start producing errors as SQLite notices the missing backing store. 232 // This is tracked by https://bugs.webkit.org/show_bug.cgi?id=135347 233 234 Vector<String> databasePaths = listDirectory(originPath, "*"); 235 236 for (auto& databasePath : databasePaths) { 237 String databaseFile = pathByAppendingComponent(databasePath, "IndexedDB.sqlite3"); 238 239 if (!fileExists(databaseFile)) 240 continue; 241 242 time_t modTime; 243 getFileModificationTime(databaseFile, modTime); 244 245 if (modTime < startDate || modTime > endDate) 246 continue; 247 248 deleteFile(databaseFile); 249 deleteEmptyDirectory(databasePath); 250 } 251 252 deleteEmptyDirectory(originPath); 253} 254 255void DatabaseProcess::deleteIndexedDatabaseEntriesForOrigin(const SecurityOriginData& origin, uint64_t callbackID) 256{ 257 postDatabaseTask(createAsyncTask(*this, &DatabaseProcess::doDeleteIndexedDatabaseEntriesForOrigin, origin, callbackID)); 258} 259 260void DatabaseProcess::doDeleteIndexedDatabaseEntriesForOrigin(const SecurityOriginData& originData, uint64_t callbackID) 261{ 262 if (m_indexedDatabaseDirectory.isEmpty()) { 263 send(Messages::WebOriginDataManagerProxy::DidDeleteEntries(callbackID), 0); 264 return; 265 } 266 267 RefPtr<SecurityOrigin> origin = originData.securityOrigin(); 268 String databaseIdentifier = origin->databaseIdentifier(); 269 String originPath = pathByAppendingComponent(m_indexedDatabaseDirectory, databaseIdentifier); 270 271 removeAllDatabasesForOriginPath(originPath, std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max()); 272 273 send(Messages::WebOriginDataManagerProxy::DidDeleteEntries(callbackID), 0); 274} 275 276void DatabaseProcess::deleteIndexedDatabaseEntriesModifiedBetweenDates(double startDate, double endDate, uint64_t callbackID) 277{ 278 postDatabaseTask(createAsyncTask(*this, &DatabaseProcess::doDeleteIndexedDatabaseEntriesModifiedBetweenDates, startDate, endDate, callbackID)); 279} 280 281void DatabaseProcess::doDeleteIndexedDatabaseEntriesModifiedBetweenDates(double startDate, double endDate, uint64_t callbackID) 282{ 283 if (m_indexedDatabaseDirectory.isEmpty()) { 284 send(Messages::WebOriginDataManagerProxy::DidDeleteEntries(callbackID), 0); 285 return; 286 } 287 288 Vector<String> originPaths = listDirectory(m_indexedDatabaseDirectory, "*"); 289 for (auto& originPath : originPaths) 290 removeAllDatabasesForOriginPath(originPath, startDate, endDate); 291 292 send(Messages::WebOriginDataManagerProxy::DidDeleteEntries(callbackID), 0); 293} 294 295void DatabaseProcess::deleteAllIndexedDatabaseEntries(uint64_t callbackID) 296{ 297 postDatabaseTask(createAsyncTask(*this, &DatabaseProcess::doDeleteAllIndexedDatabaseEntries, callbackID)); 298} 299 300void DatabaseProcess::doDeleteAllIndexedDatabaseEntries(uint64_t callbackID) 301{ 302 if (m_indexedDatabaseDirectory.isEmpty()) { 303 send(Messages::WebOriginDataManagerProxy::DidDeleteAllEntries(callbackID), 0); 304 return; 305 } 306 307 Vector<String> originPaths = listDirectory(m_indexedDatabaseDirectory, "*"); 308 for (auto& originPath : originPaths) 309 removeAllDatabasesForOriginPath(originPath, std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max()); 310 311 send(Messages::WebOriginDataManagerProxy::DidDeleteAllEntries(callbackID), 0); 312} 313 314#if !PLATFORM(COCOA) 315void DatabaseProcess::initializeProcess(const ChildProcessInitializationParameters&) 316{ 317} 318 319void DatabaseProcess::initializeProcessName(const ChildProcessInitializationParameters&) 320{ 321} 322 323void DatabaseProcess::initializeSandbox(const ChildProcessInitializationParameters&, SandboxInitializationParameters&) 324{ 325} 326#endif 327 328} // namespace WebKit 329 330#endif // ENABLE(DATABASE_PROCESS) 331