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/ScriptExecutionContext.h"
35#include "core/platform/sql/SQLiteFileSystem.h"
36#include "modules/webdatabase/DatabaseBackendBase.h"
37#include "modules/webdatabase/DatabaseBackendContext.h"
38#include "modules/webdatabase/DatabaseObserver.h"
39#include "modules/webdatabase/QuotaTracker.h"
40#include "weborigin/DatabaseIdentifier.h"
41#include "weborigin/SecurityOrigin.h"
42#include "weborigin/SecurityOriginHash.h"
43#include "wtf/Assertions.h"
44#include "wtf/StdLibExtras.h"
45#include "wtf/text/WTFString.h"
46
47namespace WebCore {
48
49DatabaseTracker& DatabaseTracker::tracker()
50{
51    AtomicallyInitializedStatic(DatabaseTracker&, tracker = *new DatabaseTracker());
52    return tracker;
53}
54
55DatabaseTracker::DatabaseTracker()
56{
57    SQLiteFileSystem::registerSQLiteVFS();
58}
59
60bool DatabaseTracker::canEstablishDatabase(DatabaseBackendContext* databaseContext, const String& name, const String& displayName, unsigned long estimatedSize, DatabaseError& error)
61{
62    ScriptExecutionContext* scriptExecutionContext = databaseContext->scriptExecutionContext();
63    bool success = DatabaseObserver::canEstablishDatabase(scriptExecutionContext, name, displayName, estimatedSize);
64    if (!success)
65        error = DatabaseError::GenericSecurityError;
66    return success;
67}
68
69String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool)
70{
71    return createDatabaseIdentifierFromSecurityOrigin(origin) + "/" + name + "#";
72}
73
74void DatabaseTracker::addOpenDatabase(DatabaseBackendBase* database)
75{
76    MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
77    if (!m_openDatabaseMap)
78        m_openDatabaseMap = adoptPtr(new DatabaseOriginMap);
79
80    String originIdentifier = createDatabaseIdentifierFromSecurityOrigin(database->securityOrigin());
81    DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier);
82    if (!nameMap) {
83        nameMap = new DatabaseNameMap();
84        m_openDatabaseMap->set(originIdentifier, nameMap);
85    }
86
87    String name(database->stringIdentifier());
88    DatabaseSet* databaseSet = nameMap->get(name);
89    if (!databaseSet) {
90        databaseSet = new DatabaseSet();
91        nameMap->set(name, databaseSet);
92    }
93
94    databaseSet->add(database);
95}
96
97class NotifyDatabaseObserverOnCloseTask : public ScriptExecutionContext::Task {
98public:
99    static PassOwnPtr<NotifyDatabaseObserverOnCloseTask> create(PassRefPtr<DatabaseBackendBase> database)
100    {
101        return adoptPtr(new NotifyDatabaseObserverOnCloseTask(database));
102    }
103
104    virtual void performTask(ScriptExecutionContext* context)
105    {
106        DatabaseObserver::databaseClosed(m_database.get());
107    }
108
109    virtual bool isCleanupTask() const
110    {
111        return true;
112    }
113
114private:
115    NotifyDatabaseObserverOnCloseTask(PassRefPtr<DatabaseBackendBase> database)
116        : m_database(database)
117    {
118    }
119
120    RefPtr<DatabaseBackendBase> m_database;
121};
122
123void DatabaseTracker::removeOpenDatabase(DatabaseBackendBase* database)
124{
125    String originIdentifier = createDatabaseIdentifierFromSecurityOrigin(database->securityOrigin());
126    MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
127    ASSERT(m_openDatabaseMap);
128    DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier);
129    if (!nameMap)
130        return;
131
132    String name(database->stringIdentifier());
133    DatabaseSet* databaseSet = nameMap->get(name);
134    if (!databaseSet)
135        return;
136
137    DatabaseSet::iterator found = databaseSet->find(database);
138    if (found == databaseSet->end())
139        return;
140
141    databaseSet->remove(found);
142    if (databaseSet->isEmpty()) {
143        nameMap->remove(name);
144        delete databaseSet;
145        if (nameMap->isEmpty()) {
146            m_openDatabaseMap->remove(originIdentifier);
147            delete nameMap;
148        }
149    }
150
151    ScriptExecutionContext* scriptExecutionContext = database->databaseContext()->scriptExecutionContext();
152    if (!scriptExecutionContext->isContextThread())
153        scriptExecutionContext->postTask(NotifyDatabaseObserverOnCloseTask::create(database));
154    else
155        DatabaseObserver::databaseClosed(database);
156}
157
158void DatabaseTracker::prepareToOpenDatabase(DatabaseBackendBase* database)
159{
160    ASSERT(database->databaseContext()->scriptExecutionContext()->isContextThread());
161    DatabaseObserver::databaseOpened(database);
162}
163
164void DatabaseTracker::failedToOpenDatabase(DatabaseBackendBase* database)
165{
166    ScriptExecutionContext* scriptExecutionContext = database->databaseContext()->scriptExecutionContext();
167    if (!scriptExecutionContext->isContextThread())
168        scriptExecutionContext->postTask(NotifyDatabaseObserverOnCloseTask::create(database));
169    else
170        DatabaseObserver::databaseClosed(database);
171}
172
173unsigned long long DatabaseTracker::getMaxSizeForDatabase(const DatabaseBackendBase* database)
174{
175    unsigned long long spaceAvailable = 0;
176    unsigned long long databaseSize = 0;
177    QuotaTracker::instance().getDatabaseSizeAndSpaceAvailableToOrigin(
178        createDatabaseIdentifierFromSecurityOrigin(database->securityOrigin()),
179        database->stringIdentifier(), &databaseSize, &spaceAvailable);
180    return databaseSize + spaceAvailable;
181}
182
183void DatabaseTracker::interruptAllDatabasesForContext(const DatabaseBackendContext* context)
184{
185    MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
186
187    if (!m_openDatabaseMap)
188        return;
189
190    DatabaseNameMap* nameMap = m_openDatabaseMap->get(createDatabaseIdentifierFromSecurityOrigin(context->securityOrigin()));
191    if (!nameMap)
192        return;
193
194    DatabaseNameMap::const_iterator dbNameMapEndIt = nameMap->end();
195    for (DatabaseNameMap::const_iterator dbNameMapIt = nameMap->begin(); dbNameMapIt != dbNameMapEndIt; ++dbNameMapIt) {
196        DatabaseSet* databaseSet = dbNameMapIt->value;
197        DatabaseSet::const_iterator end = databaseSet->end();
198        for (DatabaseSet::const_iterator it = databaseSet->begin(); it != end; ++it) {
199            if ((*it)->databaseContext() == context)
200                (*it)->interrupt();
201        }
202    }
203}
204
205class DatabaseTracker::CloseOneDatabaseImmediatelyTask : public ScriptExecutionContext::Task {
206public:
207    static PassOwnPtr<CloseOneDatabaseImmediatelyTask> create(const String& originIdentifier, const String& name, DatabaseBackendBase* database)
208    {
209        return adoptPtr(new CloseOneDatabaseImmediatelyTask(originIdentifier, name, database));
210    }
211
212    virtual void performTask(ScriptExecutionContext* context)
213    {
214        DatabaseTracker::tracker().closeOneDatabaseImmediately(m_originIdentifier, m_name, m_database);
215    }
216
217private:
218    CloseOneDatabaseImmediatelyTask(const String& originIdentifier, const String& name, DatabaseBackendBase* database)
219        : m_originIdentifier(originIdentifier.isolatedCopy())
220        , m_name(name.isolatedCopy())
221        , m_database(database)
222    {
223    }
224
225    String m_originIdentifier;
226    String m_name;
227    DatabaseBackendBase* m_database; // Intentionally a raw pointer.
228};
229
230void DatabaseTracker::closeDatabasesImmediately(const String& originIdentifier, const String& name)
231{
232    MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
233    if (!m_openDatabaseMap)
234        return;
235
236    DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier);
237    if (!nameMap)
238        return;
239
240    DatabaseSet* databaseSet = nameMap->get(name);
241    if (!databaseSet)
242        return;
243
244    // We have to call closeImmediately() on the context thread and we cannot safely add a reference to
245    // the database in our collection when not on the context thread (which is always the case given
246    // current usage).
247    for (DatabaseSet::iterator it = databaseSet->begin(); it != databaseSet->end(); ++it)
248        (*it)->databaseContext()->scriptExecutionContext()->postTask(CloseOneDatabaseImmediatelyTask::create(originIdentifier, name, *it));
249}
250
251void DatabaseTracker::closeOneDatabaseImmediately(const String& originIdentifier, const String& name, DatabaseBackendBase* database)
252{
253    // First we have to confirm the 'database' is still in our collection.
254    {
255        MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
256        if (!m_openDatabaseMap)
257            return;
258
259        DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier);
260        if (!nameMap)
261            return;
262
263        DatabaseSet* databaseSet = nameMap->get(name);
264        if (!databaseSet)
265            return;
266
267        DatabaseSet::iterator found = databaseSet->find(database);
268        if (found == databaseSet->end())
269            return;
270    }
271
272    // And we have to call closeImmediately() without our collection lock being held.
273    database->closeImmediately();
274}
275
276}
277