Modern IDB: Rename some "UniqueIDBDatabase" classes that conflict with new classes...
[WebKit-https.git] / Source / WebKit2 / DatabaseProcess / DatabaseProcess.cpp
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 "DatabaseProcessCreationParameters.h"
32 #include "DatabaseProcessMessages.h"
33 #include "DatabaseProcessProxyMessages.h"
34 #include "DatabaseToWebProcessConnection.h"
35 #include "LegacyUniqueIDBDatabase.h"
36 #include "WebCrossThreadCopier.h"
37 #include "WebsiteData.h"
38 #include <WebCore/CrossThreadTask.h>
39 #include <WebCore/FileSystem.h>
40 #include <WebCore/NotImplemented.h>
41 #include <WebCore/SessionID.h>
42 #include <WebCore/TextEncoding.h>
43 #include <wtf/MainThread.h>
44
45 using namespace WebCore;
46
47 namespace WebKit {
48
49 DatabaseProcess& DatabaseProcess::singleton()
50 {
51     static NeverDestroyed<DatabaseProcess> databaseProcess;
52     return databaseProcess;
53 }
54
55 DatabaseProcess::DatabaseProcess()
56     : m_queue(WorkQueue::create("com.apple.WebKit.DatabaseProcess"))
57 {
58     // Make sure the UTF8Encoding encoding and the text encoding maps have been built on the main thread before a background thread needs it.
59     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=135365 - Need a more explicit way of doing this besides accessing the UTF8Encoding.
60     UTF8Encoding();
61 }
62
63 DatabaseProcess::~DatabaseProcess()
64 {
65 }
66
67 void DatabaseProcess::initializeConnection(IPC::Connection* connection)
68 {
69     ChildProcess::initializeConnection(connection);
70 }
71
72 bool DatabaseProcess::shouldTerminate()
73 {
74     return true;
75 }
76
77 void DatabaseProcess::didClose(IPC::Connection&)
78 {
79     RunLoop::current().stop();
80 }
81
82 void DatabaseProcess::didReceiveMessage(IPC::Connection& connection, IPC::MessageDecoder& decoder)
83 {
84     if (messageReceiverMap().dispatchMessage(connection, decoder))
85         return;
86
87     if (decoder.messageReceiverName() == Messages::DatabaseProcess::messageReceiverName()) {
88         didReceiveDatabaseProcessMessage(connection, decoder);
89         return;
90     }
91 }
92
93 void DatabaseProcess::didReceiveInvalidMessage(IPC::Connection&, IPC::StringReference, IPC::StringReference)
94 {
95     RunLoop::current().stop();
96 }
97
98 #if ENABLE(INDEXED_DATABASE)
99 RefPtr<LegacyUniqueIDBDatabase> DatabaseProcess::getOrCreateLegacyUniqueIDBDatabase(const LegacyUniqueIDBDatabaseIdentifier& identifier)
100 {
101     auto addResult = m_idbDatabases.add(identifier, nullptr);
102
103     if (!addResult.isNewEntry)
104         return addResult.iterator->value;
105
106     RefPtr<LegacyUniqueIDBDatabase> database = LegacyUniqueIDBDatabase::create(identifier);
107     addResult.iterator->value = database.get();
108     return database;
109 }
110
111 void DatabaseProcess::removeLegacyUniqueIDBDatabase(const LegacyUniqueIDBDatabase& database)
112 {
113     const LegacyUniqueIDBDatabaseIdentifier& identifier = database.identifier();
114     ASSERT(m_idbDatabases.contains(identifier));
115
116     m_idbDatabases.remove(identifier);
117 }
118 #endif
119
120 void DatabaseProcess::initializeDatabaseProcess(const DatabaseProcessCreationParameters& parameters)
121 {
122 #if ENABLE(INDEXED_DATABASE)
123     // *********
124     // IMPORTANT: Do not change the directory structure for indexed databases on disk without first consulting a reviewer from Apple (<rdar://problem/17454712>)
125     // *********
126
127     m_indexedDatabaseDirectory = parameters.indexedDatabaseDirectory;
128     SandboxExtension::consumePermanently(parameters.indexedDatabaseDirectoryExtensionHandle);
129
130     ensureIndexedDatabaseRelativePathExists(StringImpl::empty());
131 #endif
132 }
133
134 #if ENABLE(INDEXED_DATABASE)
135 void DatabaseProcess::ensureIndexedDatabaseRelativePathExists(const String& relativePath)
136 {
137     postDatabaseTask(createCrossThreadTask(*this, &DatabaseProcess::ensurePathExists, absoluteIndexedDatabasePathFromDatabaseRelativePath(relativePath)));
138 }
139 #endif
140
141 void DatabaseProcess::ensurePathExists(const String& path)
142 {
143     ASSERT(!RunLoop::isMain());
144
145     if (!makeAllDirectories(path))
146         LOG_ERROR("Failed to make all directories for path '%s'", path.utf8().data());
147 }
148
149 #if ENABLE(INDEXED_DATABASE)
150 String DatabaseProcess::absoluteIndexedDatabasePathFromDatabaseRelativePath(const String& relativePath)
151 {
152     // FIXME: pathByAppendingComponent() was originally designed to append individual atomic components.
153     // We don't have a function designed to append a multi-component subpath, but we should.
154     return pathByAppendingComponent(m_indexedDatabaseDirectory, relativePath);
155 }
156 #endif
157
158 void DatabaseProcess::postDatabaseTask(std::unique_ptr<CrossThreadTask> task)
159 {
160     ASSERT(RunLoop::isMain());
161
162     LockHolder locker(m_databaseTaskMutex);
163
164     m_databaseTasks.append(WTFMove(task));
165
166     m_queue->dispatch([this] {
167         performNextDatabaseTask();
168     });
169 }
170
171 void DatabaseProcess::performNextDatabaseTask()
172 {
173     ASSERT(!RunLoop::isMain());
174
175     std::unique_ptr<CrossThreadTask> task;
176     {
177         LockHolder locker(m_databaseTaskMutex);
178         ASSERT(!m_databaseTasks.isEmpty());
179         task = m_databaseTasks.takeFirst();
180     }
181
182     task->performTask();
183 }
184
185 void DatabaseProcess::createDatabaseToWebProcessConnection()
186 {
187 #if USE(UNIX_DOMAIN_SOCKETS)
188     IPC::Connection::SocketPair socketPair = IPC::Connection::createPlatformConnection();
189     m_databaseToWebProcessConnections.append(DatabaseToWebProcessConnection::create(socketPair.server));
190     parentProcessConnection()->send(Messages::DatabaseProcessProxy::DidCreateDatabaseToWebProcessConnection(IPC::Attachment(socketPair.client)), 0);
191 #elif OS(DARWIN)
192     // Create the listening port.
193     mach_port_t listeningPort;
194     mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &listeningPort);
195
196     // Create a listening connection.
197     RefPtr<DatabaseToWebProcessConnection> connection = DatabaseToWebProcessConnection::create(IPC::Connection::Identifier(listeningPort));
198     m_databaseToWebProcessConnections.append(connection.release());
199
200     IPC::Attachment clientPort(listeningPort, MACH_MSG_TYPE_MAKE_SEND);
201     parentProcessConnection()->send(Messages::DatabaseProcessProxy::DidCreateDatabaseToWebProcessConnection(clientPort), 0);
202 #else
203     notImplemented();
204 #endif
205 }
206
207 void DatabaseProcess::fetchWebsiteData(SessionID, uint64_t websiteDataTypes, uint64_t callbackID)
208 {
209     struct CallbackAggregator final : public ThreadSafeRefCounted<CallbackAggregator> {
210         explicit CallbackAggregator(std::function<void (WebsiteData)> completionHandler)
211             : m_completionHandler(WTFMove(completionHandler))
212         {
213         }
214
215         ~CallbackAggregator()
216         {
217             ASSERT(RunLoop::isMain());
218
219             auto completionHandler = WTFMove(m_completionHandler);
220             auto websiteData = WTFMove(m_websiteData);
221
222             RunLoop::main().dispatch([completionHandler, websiteData] {
223                 completionHandler(websiteData);
224             });
225         }
226
227         std::function<void (WebsiteData)> m_completionHandler;
228         WebsiteData m_websiteData;
229     };
230
231     RefPtr<CallbackAggregator> callbackAggregator = adoptRef(new CallbackAggregator([this, callbackID](WebsiteData websiteData) {
232         parentProcessConnection()->send(Messages::DatabaseProcessProxy::DidFetchWebsiteData(callbackID, websiteData), 0);
233     }));
234
235 #if ENABLE(INDEXED_DATABASE)
236     if (websiteDataTypes & WebsiteDataTypeIndexedDBDatabases) {
237         // FIXME: Pick the right database store based on the session ID.
238         postDatabaseTask(std::make_unique<CrossThreadTask>([callbackAggregator, websiteDataTypes, this] {
239
240             Vector<RefPtr<SecurityOrigin>> securityOrigins = indexedDatabaseOrigins();
241
242             RunLoop::main().dispatch([callbackAggregator, securityOrigins] {
243                 for (const auto& securityOrigin : securityOrigins)
244                     callbackAggregator->m_websiteData.entries.append(WebsiteData::Entry { securityOrigin, WebsiteDataTypeIndexedDBDatabases });
245             });
246         }));
247     }
248 #endif
249 }
250
251 void DatabaseProcess::deleteWebsiteData(WebCore::SessionID, uint64_t websiteDataTypes, std::chrono::system_clock::time_point modifiedSince, uint64_t callbackID)
252 {
253     struct CallbackAggregator final : public ThreadSafeRefCounted<CallbackAggregator> {
254         explicit CallbackAggregator(std::function<void ()> completionHandler)
255             : m_completionHandler(WTFMove(completionHandler))
256         {
257         }
258
259         ~CallbackAggregator()
260         {
261             ASSERT(RunLoop::isMain());
262
263             RunLoop::main().dispatch(WTFMove(m_completionHandler));
264         }
265
266         std::function<void ()> m_completionHandler;
267     };
268
269     RefPtr<CallbackAggregator> callbackAggregator = adoptRef(new CallbackAggregator([this, callbackID]() {
270         parentProcessConnection()->send(Messages::DatabaseProcessProxy::DidDeleteWebsiteData(callbackID), 0);
271     }));
272
273 #if ENABLE(INDEXED_DATABASE)
274     if (websiteDataTypes & WebsiteDataTypeIndexedDBDatabases) {
275         postDatabaseTask(std::make_unique<CrossThreadTask>([this, callbackAggregator, modifiedSince] {
276
277             deleteIndexedDatabaseEntriesModifiedSince(modifiedSince);
278             RunLoop::main().dispatch([callbackAggregator] { });
279         }));
280     }
281 #endif
282 }
283
284 void DatabaseProcess::deleteWebsiteDataForOrigins(WebCore::SessionID, uint64_t websiteDataTypes, const Vector<SecurityOriginData>& securityOriginDatas, uint64_t callbackID)
285 {
286     struct CallbackAggregator final : public ThreadSafeRefCounted<CallbackAggregator> {
287         explicit CallbackAggregator(std::function<void ()> completionHandler)
288             : m_completionHandler(WTFMove(completionHandler))
289         {
290         }
291
292         ~CallbackAggregator()
293         {
294             ASSERT(RunLoop::isMain());
295
296             RunLoop::main().dispatch(WTFMove(m_completionHandler));
297         }
298
299         std::function<void ()> m_completionHandler;
300     };
301
302     RefPtr<CallbackAggregator> callbackAggregator = adoptRef(new CallbackAggregator([this, callbackID]() {
303         parentProcessConnection()->send(Messages::DatabaseProcessProxy::DidDeleteWebsiteDataForOrigins(callbackID), 0);
304     }));
305
306 #if ENABLE(INDEXED_DATABASE)
307     if (websiteDataTypes & WebsiteDataTypeIndexedDBDatabases) {
308         Vector<RefPtr<WebCore::SecurityOrigin>> securityOrigins;
309         for (const auto& securityOriginData : securityOriginDatas)
310             securityOrigins.append(securityOriginData.securityOrigin());
311
312         postDatabaseTask(std::make_unique<CrossThreadTask>([this, securityOrigins, callbackAggregator] {
313             deleteIndexedDatabaseEntriesForOrigins(securityOrigins);
314
315             RunLoop::main().dispatch([callbackAggregator] { });
316         }));
317     }
318 #endif
319 }
320
321 #if ENABLE(INDEXED_DATABASE)
322 Vector<RefPtr<WebCore::SecurityOrigin>> DatabaseProcess::indexedDatabaseOrigins()
323 {
324     if (m_indexedDatabaseDirectory.isEmpty())
325         return { };
326
327     Vector<RefPtr<WebCore::SecurityOrigin>> securityOrigins;
328     for (auto& originPath : listDirectory(m_indexedDatabaseDirectory, "*")) {
329         String databaseIdentifier = pathGetFileName(originPath);
330
331         if (auto securityOrigin = SecurityOrigin::maybeCreateFromDatabaseIdentifier(databaseIdentifier))
332             securityOrigins.append(WTFMove(securityOrigin));
333     }
334
335     return securityOrigins;
336 }
337 #endif
338
339 #if ENABLE(INDEXED_DATABASE)
340 static void removeAllDatabasesForOriginPath(const String& originPath, std::chrono::system_clock::time_point modifiedSince)
341 {
342     // FIXME: We should also close/invalidate any live handles to the database files we are about to delete.
343     // Right now:
344     //     - For read-only operations, they will continue functioning as normal on the unlinked file.
345     //     - For write operations, they will start producing errors as SQLite notices the missing backing store.
346     // This is tracked by https://bugs.webkit.org/show_bug.cgi?id=135347
347
348     Vector<String> databasePaths = listDirectory(originPath, "*");
349
350     for (auto& databasePath : databasePaths) {
351         String databaseFile = pathByAppendingComponent(databasePath, "IndexedDB.sqlite3");
352
353         if (!fileExists(databaseFile))
354             continue;
355
356         if (modifiedSince > std::chrono::system_clock::time_point::min()) {
357             time_t modificationTime;
358             if (!getFileModificationTime(databaseFile, modificationTime))
359                 continue;
360
361             if (std::chrono::system_clock::from_time_t(modificationTime) < modifiedSince)
362                 continue;
363         }
364
365         deleteFile(databaseFile);
366         deleteEmptyDirectory(databasePath);
367     }
368
369     deleteEmptyDirectory(originPath);
370 }
371
372 void DatabaseProcess::deleteIndexedDatabaseEntriesForOrigins(const Vector<RefPtr<WebCore::SecurityOrigin>>& securityOrigins)
373 {
374     if (m_indexedDatabaseDirectory.isEmpty())
375         return;
376
377     for (const auto& securityOrigin : securityOrigins) {
378         String originPath = pathByAppendingComponent(m_indexedDatabaseDirectory, securityOrigin->databaseIdentifier());
379
380         removeAllDatabasesForOriginPath(originPath, std::chrono::system_clock::time_point::min());
381     }
382 }
383
384 void DatabaseProcess::deleteIndexedDatabaseEntriesModifiedSince(std::chrono::system_clock::time_point modifiedSince)
385 {
386     if (m_indexedDatabaseDirectory.isEmpty())
387         return;
388
389     Vector<String> originPaths = listDirectory(m_indexedDatabaseDirectory, "*");
390     for (auto& originPath : originPaths)
391         removeAllDatabasesForOriginPath(originPath, modifiedSince);
392 }
393 #endif
394
395 #if !PLATFORM(COCOA)
396 void DatabaseProcess::initializeProcess(const ChildProcessInitializationParameters&)
397 {
398 }
399
400 void DatabaseProcess::initializeProcessName(const ChildProcessInitializationParameters&)
401 {
402 }
403
404 void DatabaseProcess::initializeSandbox(const ChildProcessInitializationParameters&, SandboxInitializationParameters&)
405 {
406 }
407 #endif
408
409 } // namespace WebKit
410
411 #endif // ENABLE(DATABASE_PROCESS)