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. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *
25 */
26
27#include "config.h"
28#include "WebIDBFactoryBackend.h"
29
30#include "IDBUtilities.h"
31#include "Logging.h"
32#include "WebIDBServerConnection.h"
33#include "WebProcess.h"
34#include "WebToDatabaseProcessConnection.h"
35#include <WebCore/DOMStringList.h>
36#include <WebCore/IDBCallbacks.h>
37#include <WebCore/IDBCursorBackend.h>
38#include <WebCore/IDBDatabaseCallbacks.h>
39#include <WebCore/IDBTransactionBackend.h>
40#include <WebCore/SecurityOrigin.h>
41#include <wtf/MainThread.h>
42#include <wtf/NeverDestroyed.h>
43
44#if ENABLE(INDEXED_DATABASE)
45#if ENABLE(DATABASE_PROCESS)
46
47using namespace WebCore;
48
49namespace WebKit {
50
51typedef HashMap<String, IDBDatabaseBackend*> IDBDatabaseBackendMap;
52
53static IDBDatabaseBackendMap& sharedDatabaseBackendMap()
54{
55    static NeverDestroyed<IDBDatabaseBackendMap> databaseBackendMap;
56    return databaseBackendMap;
57}
58
59static HashMap<String, HashSet<String>>& sharedRecentDatabaseNameMap()
60{
61    static NeverDestroyed<HashMap<String, HashSet<String>>> recentDatabaseNameMap;
62    return recentDatabaseNameMap;
63}
64
65static String combinedSecurityOriginIdentifier(const WebCore::SecurityOrigin& openingOrigin, const WebCore::SecurityOrigin& mainFrameOrigin)
66{
67    StringBuilder stringBuilder;
68
69    String originString = openingOrigin.toString();
70    if (originString == "null")
71        return String();
72    stringBuilder.append(originString);
73    stringBuilder.append("_");
74
75    originString = mainFrameOrigin.toString();
76    if (originString == "null")
77        return String();
78    stringBuilder.append(originString);
79
80    return stringBuilder.toString();
81}
82
83WebIDBFactoryBackend::WebIDBFactoryBackend(const String&)
84{
85}
86
87WebIDBFactoryBackend::~WebIDBFactoryBackend()
88{
89}
90
91void WebIDBFactoryBackend::getDatabaseNames(PassRefPtr<IDBCallbacks> callbacks, const SecurityOrigin& openingOrigin, const SecurityOrigin& mainFrameOrigin, ScriptExecutionContext* context, const String&)
92{
93    LOG(IDB, "WebIDBFactoryBackend::getDatabaseNames");
94
95    String securityOriginIdentifier = combinedSecurityOriginIdentifier(openingOrigin, mainFrameOrigin);
96    if (securityOriginIdentifier.isEmpty()) {
97        callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::InvalidAccessError, ASCIILiteral("Document is not allowed to use Indexed Databases")));
98        return;
99    }
100
101    auto recentNameIterator = sharedRecentDatabaseNameMap().find(securityOriginIdentifier);
102    if (recentNameIterator == sharedRecentDatabaseNameMap().end())
103        return;
104
105    RefPtr<DOMStringList> databaseNames = DOMStringList::create();
106
107    HashSet<String>& foundNames = recentNameIterator->value;
108    for (const String& name : foundNames)
109        databaseNames->append(name);
110
111    callbacks->onSuccess(databaseNames.release());
112}
113
114void WebIDBFactoryBackend::open(const String& databaseName, uint64_t version, int64_t transactionId, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks, const SecurityOrigin& openingOrigin, const SecurityOrigin& mainFrameOrigin)
115{
116    ASSERT(RunLoop::isMain());
117    LOG(IDB, "WebIDBFactoryBackend::open");
118
119    String databaseIdentifier = uniqueDatabaseIdentifier(databaseName, openingOrigin, mainFrameOrigin);
120    if (databaseIdentifier.isNull()) {
121        callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::InvalidAccessError, "Document is not allowed to use Indexed Databases"));
122        return;
123    }
124
125    String securityOriginIdentifier = combinedSecurityOriginIdentifier(openingOrigin, mainFrameOrigin);
126    ASSERT(!securityOriginIdentifier.isEmpty());
127
128    auto recentNameIterator = sharedRecentDatabaseNameMap().find(securityOriginIdentifier);
129    if (recentNameIterator == sharedRecentDatabaseNameMap().end()) {
130        HashSet<String> names;
131        names.add(databaseName);
132        sharedRecentDatabaseNameMap().set(securityOriginIdentifier, names);
133    } else
134        recentNameIterator->value.add(databaseName);
135
136    IDBDatabaseBackendMap::iterator it = sharedDatabaseBackendMap().find(databaseIdentifier);
137
138    RefPtr<IDBDatabaseBackend> databaseBackend;
139    if (it == sharedDatabaseBackendMap().end()) {
140        RefPtr<IDBServerConnection> serverConnection = WebIDBServerConnection::create(databaseName, openingOrigin, mainFrameOrigin);
141        databaseBackend = IDBDatabaseBackend::create(databaseName, databaseIdentifier, this, *serverConnection);
142        sharedDatabaseBackendMap().set(databaseIdentifier, databaseBackend.get());
143    } else
144        databaseBackend = it->value;
145
146    databaseBackend->openConnection(callbacks, databaseCallbacks, transactionId, version);
147}
148
149void WebIDBFactoryBackend::deleteDatabase(const String& databaseName, const SecurityOrigin& openingOrigin, const SecurityOrigin& mainFrameOrigin, PassRefPtr<IDBCallbacks> callbacks, ScriptExecutionContext*, const String&)
150{
151    ASSERT(RunLoop::isMain());
152    LOG(IDB, "WebIDBFactoryBackend::deleteDatabase");
153
154    String databaseIdentifier = uniqueDatabaseIdentifier(databaseName, openingOrigin, mainFrameOrigin);
155    if (databaseIdentifier.isNull()) {
156        callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::InvalidAccessError, "Document is not allowed to use Indexed Databases"));
157        return;
158    }
159
160    String securityOriginIdentifier = combinedSecurityOriginIdentifier(openingOrigin, mainFrameOrigin);
161    ASSERT(!securityOriginIdentifier.isEmpty());
162
163    auto recentNameIterator = sharedRecentDatabaseNameMap().find(securityOriginIdentifier);
164    if (recentNameIterator != sharedRecentDatabaseNameMap().end()) {
165        recentNameIterator->value.remove(databaseName);
166        if (recentNameIterator->value.isEmpty())
167            sharedRecentDatabaseNameMap().remove(recentNameIterator);
168    }
169
170    // If there's already a connection to the database, delete it directly.
171    IDBDatabaseBackendMap::iterator it = sharedDatabaseBackendMap().find(databaseIdentifier);
172    if (it != sharedDatabaseBackendMap().end()) {
173        it->value->deleteDatabase(callbacks);
174        return;
175    }
176
177    RefPtr<IDBServerConnection> serverConnection = WebIDBServerConnection::create(databaseName, openingOrigin, mainFrameOrigin);
178    RefPtr<IDBDatabaseBackend> databaseBackend = IDBDatabaseBackend::create(databaseName, databaseIdentifier, this, *serverConnection);
179
180    sharedDatabaseBackendMap().set(databaseIdentifier, databaseBackend.get());
181    databaseBackend->deleteDatabase(callbacks);
182    sharedDatabaseBackendMap().remove(databaseIdentifier);
183}
184
185void WebIDBFactoryBackend::removeIDBDatabaseBackend(const String& identifier)
186{
187    sharedDatabaseBackendMap().remove(identifier);
188}
189
190} // namespace WebKit
191
192#endif // ENABLE(DATABASE_PROCESS)
193#endif // ENABLE(INDEXED_DATABASE)
194