1/*
2 * Copyright (C) 2012 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#include "config.h"
27#include "DatabaseManager.h"
28
29#if ENABLE(SQL_DATABASE)
30
31#include "AbstractDatabaseServer.h"
32#include "Database.h"
33#include "DatabaseBackend.h"
34#include "DatabaseBackendBase.h"
35#include "DatabaseBackendContext.h"
36#include "DatabaseBackendSync.h"
37#include "DatabaseCallback.h"
38#include "DatabaseContext.h"
39#include "DatabaseStrategy.h"
40#include "DatabaseSync.h"
41#include "DatabaseTask.h"
42#include "ExceptionCode.h"
43#include "InspectorDatabaseInstrumentation.h"
44#include "Logging.h"
45#include "PlatformStrategies.h"
46#include "ScriptController.h"
47#include "ScriptExecutionContext.h"
48#include "SecurityOrigin.h"
49
50namespace WebCore {
51
52DatabaseManager::ProposedDatabase::ProposedDatabase(DatabaseManager& manager,
53    SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize)
54    : m_manager(manager)
55    , m_origin(origin->isolatedCopy())
56    , m_details(name.isolatedCopy(), displayName.isolatedCopy(), estimatedSize, 0, 0, 0)
57{
58    m_manager.addProposedDatabase(this);
59}
60
61DatabaseManager::ProposedDatabase::~ProposedDatabase()
62{
63    m_manager.removeProposedDatabase(this);
64}
65
66DatabaseManager& DatabaseManager::manager()
67{
68    static DatabaseManager* dbManager = 0;
69    // FIXME: The following is vulnerable to a race between threads. Need to
70    // implement a thread safe on-first-use static initializer.
71    if (!dbManager)
72        dbManager = new DatabaseManager();
73
74    return *dbManager;
75}
76
77DatabaseManager::DatabaseManager()
78    : m_server(platformStrategies()->databaseStrategy()->getDatabaseServer())
79    , m_client(0)
80    , m_databaseIsAvailable(true)
81#if !ASSERT_DISABLED
82    , m_databaseContextRegisteredCount(0)
83    , m_databaseContextInstanceCount(0)
84#endif
85{
86    ASSERT(m_server); // We should always have a server to work with.
87}
88
89void DatabaseManager::initialize(const String& databasePath)
90{
91    m_server->initialize(databasePath);
92}
93
94void DatabaseManager::setClient(DatabaseManagerClient* client)
95{
96    m_client = client;
97    m_server->setClient(client);
98}
99
100String DatabaseManager::databaseDirectoryPath() const
101{
102    return m_server->databaseDirectoryPath();
103}
104
105void DatabaseManager::setDatabaseDirectoryPath(const String& path)
106{
107    m_server->setDatabaseDirectoryPath(path);
108}
109
110bool DatabaseManager::isAvailable()
111{
112    return m_databaseIsAvailable;
113}
114
115void DatabaseManager::setIsAvailable(bool available)
116{
117    m_databaseIsAvailable = available;
118}
119
120PassRefPtr<DatabaseContext> DatabaseManager::existingDatabaseContextFor(ScriptExecutionContext* context)
121{
122    std::lock_guard<std::mutex> lock(m_mutex);
123
124    ASSERT(m_databaseContextRegisteredCount >= 0);
125    ASSERT(m_databaseContextInstanceCount >= 0);
126    ASSERT(m_databaseContextRegisteredCount <= m_databaseContextInstanceCount);
127
128    RefPtr<DatabaseContext> databaseContext = adoptRef(m_contextMap.get(context));
129    if (databaseContext) {
130        // If we're instantiating a new DatabaseContext, the new instance would
131        // carry a new refCount of 1. The client expects this and will simply
132        // adoptRef the databaseContext without ref'ing it.
133        //     However, instead of instantiating a new instance, we're reusing
134        // an existing one that corresponds to the specified ScriptExecutionContext.
135        // Hence, that new refCount need to be attributed to the reused instance
136        // to ensure that the refCount is accurate when the client adopts the ref.
137        // We do this by ref'ing the reused databaseContext before returning it.
138        databaseContext->ref();
139    }
140    return databaseContext.release();
141}
142
143PassRefPtr<DatabaseContext> DatabaseManager::databaseContextFor(ScriptExecutionContext* context)
144{
145    RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context);
146    if (!databaseContext)
147        databaseContext = adoptRef(new DatabaseContext(context));
148    return databaseContext.release();
149}
150
151void DatabaseManager::registerDatabaseContext(DatabaseContext* databaseContext)
152{
153    std::lock_guard<std::mutex> lock(m_mutex);
154
155    ScriptExecutionContext* context = databaseContext->scriptExecutionContext();
156    m_contextMap.set(context, databaseContext);
157#if !ASSERT_DISABLED
158    m_databaseContextRegisteredCount++;
159#endif
160}
161
162void DatabaseManager::unregisterDatabaseContext(DatabaseContext* databaseContext)
163{
164    std::lock_guard<std::mutex> lock(m_mutex);
165
166    ScriptExecutionContext* context = databaseContext->scriptExecutionContext();
167    ASSERT(m_contextMap.get(context));
168#if !ASSERT_DISABLED
169    m_databaseContextRegisteredCount--;
170#endif
171    m_contextMap.remove(context);
172}
173
174#if !ASSERT_DISABLED
175void DatabaseManager::didConstructDatabaseContext()
176{
177    std::lock_guard<std::mutex> lock(m_mutex);
178
179    m_databaseContextInstanceCount++;
180}
181
182void DatabaseManager::didDestructDatabaseContext()
183{
184    std::lock_guard<std::mutex> lock(m_mutex);
185
186    m_databaseContextInstanceCount--;
187    ASSERT(m_databaseContextRegisteredCount <= m_databaseContextInstanceCount);
188}
189#endif
190
191ExceptionCode DatabaseManager::exceptionCodeForDatabaseError(DatabaseError error)
192{
193    switch (error) {
194    case DatabaseError::None:
195        return 0;
196    case DatabaseError::DatabaseIsBeingDeleted:
197    case DatabaseError::DatabaseSizeExceededQuota:
198    case DatabaseError::DatabaseSizeOverflowed:
199    case DatabaseError::GenericSecurityError:
200        return SECURITY_ERR;
201    case DatabaseError::InvalidDatabaseState:
202        return INVALID_STATE_ERR;
203    }
204    ASSERT_NOT_REACHED();
205    return 0; // Make some older compilers happy.
206}
207
208static void logOpenDatabaseError(ScriptExecutionContext* context, const String& name)
209{
210    UNUSED_PARAM(context);
211    UNUSED_PARAM(name);
212    LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(),
213        context->securityOrigin()->toString().ascii().data());
214}
215
216PassRefPtr<DatabaseBackendBase> DatabaseManager::openDatabaseBackend(ScriptExecutionContext* context,
217    DatabaseType type, const String& name, const String& expectedVersion, const String& displayName,
218    unsigned long estimatedSize, bool setVersionInNewDatabase, DatabaseError& error, String& errorMessage)
219{
220    ASSERT(error == DatabaseError::None);
221
222    RefPtr<DatabaseContext> databaseContext = databaseContextFor(context);
223    RefPtr<DatabaseBackendContext> backendContext = databaseContext->backend();
224
225    RefPtr<DatabaseBackendBase> backend = m_server->openDatabase(backendContext, type, name, expectedVersion,
226        displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage);
227
228    if (!backend) {
229        ASSERT(error != DatabaseError::None);
230
231        switch (error) {
232        case DatabaseError::DatabaseIsBeingDeleted:
233        case DatabaseError::DatabaseSizeOverflowed:
234        case DatabaseError::GenericSecurityError:
235            logOpenDatabaseError(context, name);
236            return 0;
237
238        case DatabaseError::InvalidDatabaseState:
239            logErrorMessage(context, errorMessage);
240            return 0;
241
242        case DatabaseError::DatabaseSizeExceededQuota:
243            // Notify the client that we've exceeded the database quota.
244            // The client may want to increase the quota, and we'll give it
245            // one more try after if that is the case.
246            {
247                ProposedDatabase proposedDb(*this, context->securityOrigin(), name, displayName, estimatedSize);
248                databaseContext->databaseExceededQuota(name, proposedDb.details());
249            }
250            error = DatabaseError::None;
251
252            backend = m_server->openDatabase(backendContext, type, name, expectedVersion,
253                displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage,
254                AbstractDatabaseServer::RetryOpenDatabase);
255            break;
256
257        default:
258            ASSERT_NOT_REACHED();
259        }
260
261        if (!backend) {
262            ASSERT(error != DatabaseError::None);
263
264            if (error == DatabaseError::InvalidDatabaseState) {
265                logErrorMessage(context, errorMessage);
266                return 0;
267            }
268
269            logOpenDatabaseError(context, name);
270            return 0;
271        }
272    }
273
274    return backend.release();
275}
276
277void DatabaseManager::addProposedDatabase(ProposedDatabase* proposedDb)
278{
279    std::lock_guard<std::mutex> lock(m_mutex);
280
281    m_proposedDatabases.add(proposedDb);
282}
283
284void DatabaseManager::removeProposedDatabase(ProposedDatabase* proposedDb)
285{
286    std::lock_guard<std::mutex> lock(m_mutex);
287
288    m_proposedDatabases.remove(proposedDb);
289}
290
291PassRefPtr<Database> DatabaseManager::openDatabase(ScriptExecutionContext* context,
292    const String& name, const String& expectedVersion, const String& displayName,
293    unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback,
294    DatabaseError& error)
295{
296    ScriptController::initializeThreading();
297    ASSERT(error == DatabaseError::None);
298
299    bool setVersionInNewDatabase = !creationCallback;
300    String errorMessage;
301    RefPtr<DatabaseBackendBase> backend = openDatabaseBackend(context, DatabaseType::Async, name,
302        expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage);
303    if (!backend)
304        return 0;
305
306    RefPtr<Database> database = Database::create(context, backend);
307
308    RefPtr<DatabaseContext> databaseContext = databaseContextFor(context);
309    databaseContext->setHasOpenDatabases();
310    InspectorInstrumentation::didOpenDatabase(context, database, context->securityOrigin()->host(), name, expectedVersion);
311
312    if (backend->isNew() && creationCallback.get()) {
313        LOG(StorageAPI, "Scheduling DatabaseCreationCallbackTask for database %p\n", database.get());
314        database->m_scriptExecutionContext->postTask([=] (ScriptExecutionContext&) {
315            creationCallback->handleEvent(database.get());
316        });
317    }
318
319    ASSERT(database);
320    return database.release();
321}
322
323PassRefPtr<DatabaseSync> DatabaseManager::openDatabaseSync(ScriptExecutionContext* context,
324    const String& name, const String& expectedVersion, const String& displayName,
325    unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, DatabaseError& error)
326{
327    ASSERT(context->isContextThread());
328    ASSERT(error == DatabaseError::None);
329
330    bool setVersionInNewDatabase = !creationCallback;
331    String errorMessage;
332    RefPtr<DatabaseBackendBase> backend = openDatabaseBackend(context, DatabaseType::Sync, name,
333        expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, error, errorMessage);
334    if (!backend)
335        return 0;
336
337    RefPtr<DatabaseSync> database = DatabaseSync::create(context, backend);
338
339    if (backend->isNew() && creationCallback.get()) {
340        LOG(StorageAPI, "Invoking the creation callback for database %p\n", database.get());
341        creationCallback->handleEvent(database.get());
342    }
343
344    ASSERT(database);
345    return database.release();
346}
347
348bool DatabaseManager::hasOpenDatabases(ScriptExecutionContext* context)
349{
350    RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context);
351    if (!databaseContext)
352        return false;
353    return databaseContext->hasOpenDatabases();
354}
355
356void DatabaseManager::stopDatabases(ScriptExecutionContext* context, DatabaseTaskSynchronizer* synchronizer)
357{
358    RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context);
359    if (!databaseContext || !databaseContext->stopDatabases(synchronizer))
360        if (synchronizer)
361            synchronizer->taskCompleted();
362}
363
364String DatabaseManager::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfDoesNotExist)
365{
366    {
367        std::lock_guard<std::mutex> lock(m_mutex);
368
369        for (auto* proposedDatabase : m_proposedDatabases) {
370            if (proposedDatabase->details().name() == name && proposedDatabase->origin()->equal(origin))
371                return String();
372        }
373    }
374
375    return m_server->fullPathForDatabase(origin, name, createIfDoesNotExist);
376}
377
378bool DatabaseManager::hasEntryForOrigin(SecurityOrigin* origin)
379{
380    return m_server->hasEntryForOrigin(origin);
381}
382
383void DatabaseManager::origins(Vector<RefPtr<SecurityOrigin>>& result)
384{
385    m_server->origins(result);
386}
387
388bool DatabaseManager::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& result)
389{
390    return m_server->databaseNamesForOrigin(origin, result);
391}
392
393DatabaseDetails DatabaseManager::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin)
394{
395    {
396        std::lock_guard<std::mutex> lock(m_mutex);
397
398        for (auto* proposedDatabase : m_proposedDatabases) {
399            if (proposedDatabase->details().name() == name && proposedDatabase->origin()->equal(origin)) {
400                ASSERT(proposedDatabase->details().threadID() == std::this_thread::get_id() || isMainThread());
401
402                return proposedDatabase->details();
403            }
404        }
405    }
406
407    return m_server->detailsForNameAndOrigin(name, origin);
408}
409
410unsigned long long DatabaseManager::usageForOrigin(SecurityOrigin* origin)
411{
412    return m_server->usageForOrigin(origin);
413}
414
415unsigned long long DatabaseManager::quotaForOrigin(SecurityOrigin* origin)
416{
417    return m_server->quotaForOrigin(origin);
418}
419
420void DatabaseManager::setQuota(SecurityOrigin* origin, unsigned long long quotaSize)
421{
422    m_server->setQuota(origin, quotaSize);
423}
424
425void DatabaseManager::deleteAllDatabases()
426{
427    m_server->deleteAllDatabases();
428}
429
430bool DatabaseManager::deleteOrigin(SecurityOrigin* origin)
431{
432    return m_server->deleteOrigin(origin);
433}
434
435bool DatabaseManager::deleteDatabase(SecurityOrigin* origin, const String& name)
436{
437    return m_server->deleteDatabase(origin, name);
438}
439
440void DatabaseManager::interruptAllDatabasesForContext(ScriptExecutionContext* context)
441{
442    RefPtr<DatabaseContext> databaseContext = existingDatabaseContextFor(context);
443    if (databaseContext)
444        m_server->interruptAllDatabasesForContext(databaseContext->backend().get());
445}
446
447void DatabaseManager::logErrorMessage(ScriptExecutionContext* context, const String& message)
448{
449    context->addConsoleMessage(MessageSource::Storage, MessageLevel::Error, message);
450}
451
452} // namespace WebCore
453
454#endif // ENABLE(SQL_DATABASE)
455