1/*
2 * Copyright (C) 2011 Google 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 are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "modules/webdatabase/DatabaseTracker.h"
33
34#include "core/dom/ExecutionContext.h"
35#include "core/dom/ExecutionContextTask.h"
36#include "modules/webdatabase/DatabaseBackendBase.h"
37#include "modules/webdatabase/DatabaseClient.h"
38#include "modules/webdatabase/DatabaseContext.h"
39#include "modules/webdatabase/QuotaTracker.h"
40#include "modules/webdatabase/sqlite/SQLiteFileSystem.h"
41#include "platform/weborigin/DatabaseIdentifier.h"
42#include "platform/weborigin/SecurityOrigin.h"
43#include "platform/weborigin/SecurityOriginHash.h"
44#include "public/platform/Platform.h"
45#include "public/platform/WebDatabaseObserver.h"
46#include "wtf/Assertions.h"
47#include "wtf/StdLibExtras.h"
48
49namespace WebCore {
50
51static void databaseClosed(DatabaseBackendBase* database)
52{
53    if (blink::Platform::current()->databaseObserver()) {
54        blink::Platform::current()->databaseObserver()->databaseClosed(
55            createDatabaseIdentifierFromSecurityOrigin(database->securityOrigin()),
56            database->stringIdentifier());
57    }
58}
59
60DatabaseTracker& DatabaseTracker::tracker()
61{
62    AtomicallyInitializedStatic(DatabaseTracker&, tracker = *new DatabaseTracker());
63    return tracker;
64}
65
66DatabaseTracker::DatabaseTracker()
67{
68    SQLiteFileSystem::registerSQLiteVFS();
69}
70
71bool DatabaseTracker::canEstablishDatabase(DatabaseContext* databaseContext, const String& name, const String& displayName, unsigned long estimatedSize, DatabaseError& error)
72{
73    ExecutionContext* executionContext = databaseContext->executionContext();
74    bool success = DatabaseClient::from(executionContext)->allowDatabase(executionContext, name, displayName, estimatedSize);
75    if (!success)
76        error = DatabaseError::GenericSecurityError;
77    return success;
78}
79
80String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool)
81{
82    return createDatabaseIdentifierFromSecurityOrigin(origin) + "/" + name + "#";
83}
84
85void DatabaseTracker::addOpenDatabase(DatabaseBackendBase* database)
86{
87    MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
88    if (!m_openDatabaseMap)
89        m_openDatabaseMap = adoptPtr(new DatabaseOriginMap);
90
91    String originIdentifier = createDatabaseIdentifierFromSecurityOrigin(database->securityOrigin());
92    DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier);
93    if (!nameMap) {
94        nameMap = new DatabaseNameMap();
95        m_openDatabaseMap->set(originIdentifier, nameMap);
96    }
97
98    String name(database->stringIdentifier());
99    DatabaseSet* databaseSet = nameMap->get(name);
100    if (!databaseSet) {
101        databaseSet = new DatabaseSet();
102        nameMap->set(name, databaseSet);
103    }
104
105    databaseSet->add(database);
106}
107
108class NotifyDatabaseObserverOnCloseTask FINAL : public ExecutionContextTask {
109public:
110    static PassOwnPtr<NotifyDatabaseObserverOnCloseTask> create(PassRefPtrWillBeRawPtr<DatabaseBackendBase> database)
111    {
112        return adoptPtr(new NotifyDatabaseObserverOnCloseTask(database));
113    }
114
115    virtual void performTask(ExecutionContext*) OVERRIDE
116    {
117        databaseClosed(m_database.get());
118    }
119
120    virtual bool isCleanupTask() const OVERRIDE
121    {
122        return true;
123    }
124
125private:
126    explicit NotifyDatabaseObserverOnCloseTask(PassRefPtrWillBeRawPtr<DatabaseBackendBase> database)
127        : m_database(database)
128    {
129    }
130
131    RefPtrWillBeCrossThreadPersistent<DatabaseBackendBase> m_database;
132};
133
134void DatabaseTracker::removeOpenDatabase(DatabaseBackendBase* database)
135{
136    String originIdentifier = createDatabaseIdentifierFromSecurityOrigin(database->securityOrigin());
137    MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
138    ASSERT(m_openDatabaseMap);
139    DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier);
140    if (!nameMap)
141        return;
142
143    String name(database->stringIdentifier());
144    DatabaseSet* databaseSet = nameMap->get(name);
145    if (!databaseSet)
146        return;
147
148    DatabaseSet::iterator found = databaseSet->find(database);
149    if (found == databaseSet->end())
150        return;
151
152    databaseSet->remove(found);
153    if (databaseSet->isEmpty()) {
154        nameMap->remove(name);
155        delete databaseSet;
156        if (nameMap->isEmpty()) {
157            m_openDatabaseMap->remove(originIdentifier);
158            delete nameMap;
159        }
160    }
161
162    ExecutionContext* executionContext = database->databaseContext()->executionContext();
163    if (!executionContext->isContextThread())
164        executionContext->postTask(NotifyDatabaseObserverOnCloseTask::create(database));
165    else
166        databaseClosed(database);
167}
168
169void DatabaseTracker::prepareToOpenDatabase(DatabaseBackendBase* database)
170{
171    ASSERT(database->databaseContext()->executionContext()->isContextThread());
172    if (blink::Platform::current()->databaseObserver()) {
173        blink::Platform::current()->databaseObserver()->databaseOpened(
174            createDatabaseIdentifierFromSecurityOrigin(database->securityOrigin()),
175            database->stringIdentifier(),
176            database->displayName(),
177            database->estimatedSize());
178    }
179}
180
181void DatabaseTracker::failedToOpenDatabase(DatabaseBackendBase* database)
182{
183    ExecutionContext* executionContext = database->databaseContext()->executionContext();
184    if (!executionContext->isContextThread())
185        executionContext->postTask(NotifyDatabaseObserverOnCloseTask::create(database));
186    else
187        databaseClosed(database);
188}
189
190unsigned long long DatabaseTracker::getMaxSizeForDatabase(const DatabaseBackendBase* database)
191{
192    unsigned long long spaceAvailable = 0;
193    unsigned long long databaseSize = 0;
194    QuotaTracker::instance().getDatabaseSizeAndSpaceAvailableToOrigin(
195        createDatabaseIdentifierFromSecurityOrigin(database->securityOrigin()),
196        database->stringIdentifier(), &databaseSize, &spaceAvailable);
197    return databaseSize + spaceAvailable;
198}
199
200void DatabaseTracker::interruptAllDatabasesForContext(const DatabaseContext* context)
201{
202    MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
203
204    if (!m_openDatabaseMap)
205        return;
206
207    DatabaseNameMap* nameMap = m_openDatabaseMap->get(createDatabaseIdentifierFromSecurityOrigin(context->securityOrigin()));
208    if (!nameMap)
209        return;
210
211    DatabaseNameMap::const_iterator dbNameMapEndIt = nameMap->end();
212    for (DatabaseNameMap::const_iterator dbNameMapIt = nameMap->begin(); dbNameMapIt != dbNameMapEndIt; ++dbNameMapIt) {
213        DatabaseSet* databaseSet = dbNameMapIt->value;
214        DatabaseSet::const_iterator end = databaseSet->end();
215        for (DatabaseSet::const_iterator it = databaseSet->begin(); it != end; ++it) {
216            if ((*it)->databaseContext() == context)
217                (*it)->interrupt();
218        }
219    }
220}
221
222class DatabaseTracker::CloseOneDatabaseImmediatelyTask FINAL : public ExecutionContextTask {
223public:
224    static PassOwnPtr<CloseOneDatabaseImmediatelyTask> create(const String& originIdentifier, const String& name, DatabaseBackendBase* database)
225    {
226        return adoptPtr(new CloseOneDatabaseImmediatelyTask(originIdentifier, name, database));
227    }
228
229    virtual void performTask(ExecutionContext*) OVERRIDE
230    {
231        DatabaseTracker::tracker().closeOneDatabaseImmediately(m_originIdentifier, m_name, m_database);
232    }
233
234private:
235    CloseOneDatabaseImmediatelyTask(const String& originIdentifier, const String& name, DatabaseBackendBase* database)
236        : m_originIdentifier(originIdentifier.isolatedCopy())
237        , m_name(name.isolatedCopy())
238        , m_database(database)
239    {
240    }
241
242    String m_originIdentifier;
243    String m_name;
244    DatabaseBackendBase* m_database; // Intentionally a raw pointer.
245};
246
247void DatabaseTracker::closeDatabasesImmediately(const String& originIdentifier, const String& name)
248{
249    MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
250    if (!m_openDatabaseMap)
251        return;
252
253    DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier);
254    if (!nameMap)
255        return;
256
257    DatabaseSet* databaseSet = nameMap->get(name);
258    if (!databaseSet)
259        return;
260
261    // We have to call closeImmediately() on the context thread and we cannot safely add a reference to
262    // the database in our collection when not on the context thread (which is always the case given
263    // current usage).
264    for (DatabaseSet::iterator it = databaseSet->begin(); it != databaseSet->end(); ++it)
265        (*it)->databaseContext()->executionContext()->postTask(CloseOneDatabaseImmediatelyTask::create(originIdentifier, name, *it));
266}
267
268void DatabaseTracker::closeOneDatabaseImmediately(const String& originIdentifier, const String& name, DatabaseBackendBase* database)
269{
270    // First we have to confirm the 'database' is still in our collection.
271    {
272        MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
273        if (!m_openDatabaseMap)
274            return;
275
276        DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier);
277        if (!nameMap)
278            return;
279
280        DatabaseSet* databaseSet = nameMap->get(name);
281        if (!databaseSet)
282            return;
283
284        DatabaseSet::iterator found = databaseSet->find(database);
285        if (found == databaseSet->end())
286            return;
287    }
288
289    // And we have to call closeImmediately() without our collection lock being held.
290    database->closeImmediately();
291}
292
293}
294