bffc38212078291a21a63fdc159f9964e0010192
[WebKit-https.git] / Source / WebKit2 / DatabaseProcess / DatabaseProcess.cpp
1 /*
2  * Copyright (C) 2013, 2014, 2015, 2016 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 "WebCoreArgumentCoders.h"
36 #include "WebsiteData.h"
37 #include <WebCore/FileSystem.h>
38 #include <WebCore/IDBKeyData.h>
39 #include <WebCore/NotImplemented.h>
40 #include <WebCore/SessionID.h>
41 #include <WebCore/TextEncoding.h>
42 #include <wtf/CrossThreadTask.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     stopRunLoop();
80 }
81
82 void DatabaseProcess::didReceiveMessage(IPC::Connection& connection, IPC::Decoder& 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 #if ENABLE(INDEXED_DATABASE)
94 IDBServer::IDBServer& DatabaseProcess::idbServer(SessionID sessionID)
95 {
96     auto addResult = m_idbServers.add(sessionID, nullptr);
97     if (!addResult.isNewEntry) {
98         ASSERT(addResult.iterator->value);
99         return *addResult.iterator->value;
100     }
101
102     auto path = m_idbDatabasePaths.get(sessionID);
103     // There should already be a registered path for this SessionID.
104     // If there's not, then where did this SessionID come from?
105     ASSERT(!path.isEmpty());
106
107     addResult.iterator->value = IDBServer::IDBServer::create(path, DatabaseProcess::singleton());
108     return *addResult.iterator->value;
109 }
110 #endif
111
112 void DatabaseProcess::initializeWebsiteDataStore(const DatabaseProcessCreationParameters& parameters)
113 {
114 #if ENABLE(INDEXED_DATABASE)
115     // *********
116     // IMPORTANT: Do not change the directory structure for indexed databases on disk without first consulting a reviewer from Apple (<rdar://problem/17454712>)
117     // *********
118
119     auto addResult = m_idbDatabasePaths.add(parameters.sessionID, String());
120     if (!addResult.isNewEntry)
121         return;
122
123     addResult.iterator->value = parameters.indexedDatabaseDirectory;
124     SandboxExtension::consumePermanently(parameters.indexedDatabaseDirectoryExtensionHandle);
125
126     postDatabaseTask(createCrossThreadTask(*this, &DatabaseProcess::ensurePathExists, parameters.indexedDatabaseDirectory));
127 #endif
128 }
129
130 void DatabaseProcess::ensurePathExists(const String& path)
131 {
132     ASSERT(!RunLoop::isMain());
133
134     if (!makeAllDirectories(path))
135         LOG_ERROR("Failed to make all directories for path '%s'", path.utf8().data());
136 }
137
138 void DatabaseProcess::postDatabaseTask(CrossThreadTask&& task)
139 {
140     ASSERT(RunLoop::isMain());
141
142     LockHolder locker(m_databaseTaskMutex);
143
144     m_databaseTasks.append(WTFMove(task));
145
146     m_queue->dispatch([this] {
147         performNextDatabaseTask();
148     });
149 }
150
151 void DatabaseProcess::performNextDatabaseTask()
152 {
153     ASSERT(!RunLoop::isMain());
154
155     CrossThreadTask task;
156     {
157         LockHolder locker(m_databaseTaskMutex);
158         ASSERT(!m_databaseTasks.isEmpty());
159         task = m_databaseTasks.takeFirst();
160     }
161
162     task.performTask();
163 }
164
165 void DatabaseProcess::createDatabaseToWebProcessConnection()
166 {
167 #if USE(UNIX_DOMAIN_SOCKETS)
168     IPC::Connection::SocketPair socketPair = IPC::Connection::createPlatformConnection();
169     m_databaseToWebProcessConnections.append(DatabaseToWebProcessConnection::create(socketPair.server));
170     parentProcessConnection()->send(Messages::DatabaseProcessProxy::DidCreateDatabaseToWebProcessConnection(IPC::Attachment(socketPair.client)), 0);
171 #elif OS(DARWIN)
172     // Create the listening port.
173     mach_port_t listeningPort;
174     mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &listeningPort);
175
176     // Create a listening connection.
177     auto connection = DatabaseToWebProcessConnection::create(IPC::Connection::Identifier(listeningPort));
178     m_databaseToWebProcessConnections.append(WTFMove(connection));
179
180     IPC::Attachment clientPort(listeningPort, MACH_MSG_TYPE_MAKE_SEND);
181     parentProcessConnection()->send(Messages::DatabaseProcessProxy::DidCreateDatabaseToWebProcessConnection(clientPort), 0);
182 #else
183     notImplemented();
184 #endif
185 }
186
187 void DatabaseProcess::fetchWebsiteData(SessionID sessionID, OptionSet<WebsiteDataType> websiteDataTypes, uint64_t callbackID)
188 {
189 #if ENABLE(INDEXED_DATABASE)
190     auto completionHandler = [this, callbackID](const WebsiteData& websiteData) {
191         parentProcessConnection()->send(Messages::DatabaseProcessProxy::DidFetchWebsiteData(callbackID, websiteData), 0);
192     };
193
194     String path = m_idbDatabasePaths.get(sessionID);
195     if (path.isEmpty() || !websiteDataTypes.contains(WebsiteDataType::IndexedDBDatabases)) {
196         completionHandler({ });
197         return;
198     }
199
200     if (websiteDataTypes.contains(WebsiteDataType::IndexedDBDatabases)) {
201         // FIXME: Pick the right database store based on the session ID.
202         postDatabaseTask(CrossThreadTask([this, completionHandler = WTFMove(completionHandler), path = WTFMove(path)]() mutable {
203             RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), securityOrigins = indexedDatabaseOrigins(path)] {
204                 WebsiteData websiteData;
205                 for (const auto& securityOrigin : securityOrigins)
206                     websiteData.entries.append({ securityOrigin, WebsiteDataType::IndexedDBDatabases, 0 });
207
208                 completionHandler(websiteData);
209             });
210         }));
211     }
212 #endif
213 }
214
215 void DatabaseProcess::deleteWebsiteData(WebCore::SessionID sessionID, OptionSet<WebsiteDataType> websiteDataTypes, std::chrono::system_clock::time_point modifiedSince, uint64_t callbackID)
216 {
217 #if ENABLE(INDEXED_DATABASE)
218     auto completionHandler = [this, callbackID]() {
219         parentProcessConnection()->send(Messages::DatabaseProcessProxy::DidDeleteWebsiteData(callbackID), 0);
220     };
221
222     if (!websiteDataTypes.contains(WebsiteDataType::IndexedDBDatabases)) {
223         completionHandler();
224         return;
225     }
226
227     idbServer(sessionID).closeAndDeleteDatabasesModifiedSince(modifiedSince, WTFMove(completionHandler));
228 #endif
229 }
230
231 void DatabaseProcess::deleteWebsiteDataForOrigins(WebCore::SessionID sessionID, OptionSet<WebsiteDataType> websiteDataTypes, const Vector<SecurityOriginData>& securityOriginDatas, uint64_t callbackID)
232 {
233 #if ENABLE(INDEXED_DATABASE)
234     auto completionHandler = [this, callbackID]() {
235         parentProcessConnection()->send(Messages::DatabaseProcessProxy::DidDeleteWebsiteDataForOrigins(callbackID), 0);
236     };
237
238     if (!websiteDataTypes.contains(WebsiteDataType::IndexedDBDatabases)) {
239         completionHandler();
240         return;
241     }
242
243     idbServer(sessionID).closeAndDeleteDatabasesForOrigins(securityOriginDatas, WTFMove(completionHandler));
244 #endif
245 }
246
247 #if ENABLE(SANDBOX_EXTENSIONS)
248 void DatabaseProcess::grantSandboxExtensionsForBlobs(const Vector<String>& paths, const SandboxExtension::HandleArray& handles)
249 {
250     ASSERT(paths.size() == handles.size());
251
252     for (size_t i = 0; i < paths.size(); ++i) {
253         auto result = m_blobTemporaryFileSandboxExtensions.add(paths[i], SandboxExtension::create(handles[i]));
254         ASSERT_UNUSED(result, result.isNewEntry);
255     }
256 }
257 #endif
258
259 #if ENABLE(INDEXED_DATABASE)
260 void DatabaseProcess::prepareForAccessToTemporaryFile(const String& path)
261 {
262     if (auto extension = m_blobTemporaryFileSandboxExtensions.get(path))
263         extension->consume();
264 }
265
266 void DatabaseProcess::accessToTemporaryFileComplete(const String& path)
267 {
268     // We've either hard linked the temporary blob file to the database directory, copied it there,
269     // or the transaction is being aborted.
270     // In any of those cases, we can delete the temporary blob file now.
271     deleteFile(path);
272
273     if (auto extension = m_blobTemporaryFileSandboxExtensions.take(path))
274         extension->revoke();
275 }
276
277 Vector<WebCore::SecurityOriginData> DatabaseProcess::indexedDatabaseOrigins(const String& path)
278 {
279     if (path.isEmpty())
280         return { };
281
282     Vector<WebCore::SecurityOriginData> securityOrigins;
283     for (auto& originPath : listDirectory(path, "*")) {
284         String databaseIdentifier = pathGetFileName(originPath);
285
286         if (auto securityOrigin = SecurityOriginData::fromDatabaseIdentifier(databaseIdentifier))
287             securityOrigins.append(WTFMove(*securityOrigin));
288     }
289
290     return securityOrigins;
291 }
292
293 #endif
294
295 #if ENABLE(SANDBOX_EXTENSIONS)
296 void DatabaseProcess::getSandboxExtensionsForBlobFiles(const Vector<String>& filenames, std::function<void (SandboxExtension::HandleArray&&)> completionHandler)
297 {
298     static uint64_t lastRequestID;
299
300     uint64_t requestID = ++lastRequestID;
301     m_sandboxExtensionForBlobsCompletionHandlers.set(requestID, completionHandler);
302     parentProcessConnection()->send(Messages::DatabaseProcessProxy::GetSandboxExtensionsForBlobFiles(requestID, filenames), 0);
303 }
304
305 void DatabaseProcess::didGetSandboxExtensionsForBlobFiles(uint64_t requestID, SandboxExtension::HandleArray&& handles)
306 {
307     if (auto handler = m_sandboxExtensionForBlobsCompletionHandlers.take(requestID))
308         handler(WTFMove(handles));
309 }
310 #endif
311
312 #if !PLATFORM(COCOA)
313 void DatabaseProcess::initializeProcess(const ChildProcessInitializationParameters&)
314 {
315 }
316
317 void DatabaseProcess::initializeProcessName(const ChildProcessInitializationParameters&)
318 {
319 }
320
321 void DatabaseProcess::initializeSandbox(const ChildProcessInitializationParameters&, SandboxInitializationParameters&)
322 {
323 }
324 #endif
325
326 } // namespace WebKit
327
328 #endif // ENABLE(DATABASE_PROCESS)