1/*
2 * Copyright (C) 2007, 2008 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 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "DatabaseTracker.h"
31
32#if ENABLE(DATABASE)
33
34#include "AbstractDatabase.h"
35#include "Chrome.h"
36#include "ChromeClient.h"
37#include "DatabaseThread.h"
38#include "DatabaseTrackerClient.h"
39#include "Logging.h"
40#include "OriginQuotaManager.h"
41#include "Page.h"
42#include "ScriptExecutionContext.h"
43#include "SecurityOrigin.h"
44#include "SecurityOriginHash.h"
45#include "SQLiteFileSystem.h"
46#include "SQLiteStatement.h"
47#include <wtf/MainThread.h>
48#include <wtf/StdLibExtras.h>
49#include <wtf/text/CString.h>
50
51using namespace std;
52
53static WebCore::OriginQuotaManager& originQuotaManager()
54{
55    DEFINE_STATIC_LOCAL(WebCore::OriginQuotaManager, quotaManager, ());
56    return quotaManager;
57}
58
59namespace WebCore {
60
61static DatabaseTracker* staticTracker = 0;
62
63void DatabaseTracker::initializeTracker(const String& databasePath)
64{
65    ASSERT(!staticTracker);
66    if (staticTracker)
67        return;
68
69    staticTracker = new DatabaseTracker(databasePath);
70}
71
72DatabaseTracker& DatabaseTracker::tracker()
73{
74    if (!staticTracker)
75        staticTracker = new DatabaseTracker("");
76
77    return *staticTracker;
78}
79
80DatabaseTracker::DatabaseTracker(const String& databasePath)
81    : m_client(0)
82{
83    setDatabaseDirectoryPath(databasePath);
84
85    SQLiteFileSystem::registerSQLiteVFS();
86
87    MutexLocker lockDatabase(m_databaseGuard);
88    populateOrigins();
89}
90
91void DatabaseTracker::setDatabaseDirectoryPath(const String& path)
92{
93    MutexLocker lockDatabase(m_databaseGuard);
94    ASSERT(!m_database.isOpen());
95    m_databaseDirectoryPath = path.threadsafeCopy();
96}
97
98String DatabaseTracker::databaseDirectoryPath() const
99{
100    return m_databaseDirectoryPath.threadsafeCopy();
101}
102
103String DatabaseTracker::trackerDatabasePath() const
104{
105    return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath, "Databases.db");
106}
107
108void DatabaseTracker::openTrackerDatabase(bool createIfDoesNotExist)
109{
110    ASSERT(!m_databaseGuard.tryLock());
111
112    if (m_database.isOpen())
113        return;
114
115    String databasePath = trackerDatabasePath();
116    if (!SQLiteFileSystem::ensureDatabaseFileExists(databasePath, createIfDoesNotExist))
117        return;
118
119    if (!m_database.open(databasePath)) {
120        // FIXME: What do do here?
121        LOG_ERROR("Failed to open databasePath %s.", databasePath.ascii().data());
122        return;
123    }
124    m_database.disableThreadingChecks();
125    if (!m_database.tableExists("Origins")) {
126        if (!m_database.executeCommand("CREATE TABLE Origins (origin TEXT UNIQUE ON CONFLICT REPLACE, quota INTEGER NOT NULL ON CONFLICT FAIL);")) {
127            // FIXME: and here
128            LOG_ERROR("Failed to create Origins table");
129        }
130    }
131    if (!m_database.tableExists("Databases")) {
132        if (!m_database.executeCommand("CREATE TABLE Databases (guid INTEGER PRIMARY KEY AUTOINCREMENT, origin TEXT, name TEXT, displayName TEXT, estimatedSize INTEGER, path TEXT);")) {
133            // FIXME: and here
134            LOG_ERROR("Failed to create Databases table");
135        }
136    }
137}
138
139bool DatabaseTracker::canEstablishDatabase(ScriptExecutionContext* context, const String& name, const String& displayName, unsigned long estimatedSize)
140{
141    SecurityOrigin* origin = context->securityOrigin();
142    ProposedDatabase details;
143
144    unsigned long long requirement;
145    {
146        MutexLocker lockDatabase(m_databaseGuard);
147        Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
148
149        if (!canCreateDatabase(origin, name))
150            return false;
151
152        recordCreatingDatabase(origin, name);
153
154        // Since we're imminently opening a database within this context's origin, make sure this origin is being tracked by the QuotaTracker
155        // by fetching its current usage now.
156        unsigned long long usage = usageForOriginNoLock(origin);
157
158        // If a database already exists, ignore the passed-in estimated size and say it's OK.
159        if (hasEntryForDatabase(origin, name))
160            return true;
161
162        // If the database will fit, allow its creation.
163        requirement = usage + max(1UL, estimatedSize);
164        if (requirement < usage) {
165            doneCreatingDatabase(origin, name);
166            return false; // If the estimated size is so big it causes an overflow, don't allow creation.
167        }
168        if (requirement <= quotaForOriginNoLock(origin))
169            return true;
170
171        // Give the chrome client a chance to increase the quota.
172        // Temporarily make the details of the proposed database available, so the client can get at them.
173        // FIXME: We should really just pass the details into this call, rather than using m_proposedDatabases.
174        details = ProposedDatabase(origin->threadsafeCopy(), DatabaseDetails(name.threadsafeCopy(), displayName.threadsafeCopy(), estimatedSize, 0));
175        m_proposedDatabases.add(&details);
176    }
177    // Drop all locks before calling out; we don't know what they'll do.
178    context->databaseExceededQuota(name);
179
180    MutexLocker lockDatabase(m_databaseGuard);
181
182    m_proposedDatabases.remove(&details);
183
184    // If the database will fit now, allow its creation.
185    if (requirement <= quotaForOriginNoLock(origin))
186        return true;
187
188    doneCreatingDatabase(origin, name);
189
190    return false;
191}
192
193bool DatabaseTracker::hasEntryForOriginNoLock(SecurityOrigin* origin)
194{
195    ASSERT(!m_databaseGuard.tryLock());
196    ASSERT(m_quotaMap);
197    return m_quotaMap->contains(origin);
198}
199
200bool DatabaseTracker::hasEntryForOrigin(SecurityOrigin* origin)
201{
202    MutexLocker lockDatabase(m_databaseGuard);
203    return hasEntryForOriginNoLock(origin);
204}
205
206bool DatabaseTracker::hasEntryForDatabase(SecurityOrigin* origin, const String& databaseIdentifier)
207{
208    ASSERT(!m_databaseGuard.tryLock());
209    openTrackerDatabase(false);
210    if (!m_database.isOpen())
211        return false;
212    SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?;");
213
214    if (statement.prepare() != SQLResultOk)
215        return false;
216
217    statement.bindText(1, origin->databaseIdentifier());
218    statement.bindText(2, databaseIdentifier);
219
220    return statement.step() == SQLResultRow;
221}
222
223unsigned long long DatabaseTracker::getMaxSizeForDatabase(const AbstractDatabase* database)
224{
225    // The maximum size for a database is the full quota for its origin, minus the current usage within the origin,
226    // plus the current usage of the given database
227    MutexLocker lockDatabase(m_databaseGuard);
228    Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
229    SecurityOrigin* origin = database->securityOrigin();
230    return quotaForOriginNoLock(origin) - originQuotaManager().diskUsage(origin) + SQLiteFileSystem::getDatabaseFileSize(database->fileName());
231}
232
233void DatabaseTracker::databaseChanged(AbstractDatabase* database)
234{
235    Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
236    originQuotaManager().markDatabase(database);
237}
238
239void DatabaseTracker::interruptAllDatabasesForContext(const ScriptExecutionContext* context)
240{
241    Vector<RefPtr<AbstractDatabase> > openDatabases;
242    {
243        MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
244
245        if (!m_openDatabaseMap)
246            return;
247
248        DatabaseNameMap* nameMap = m_openDatabaseMap->get(context->securityOrigin());
249        if (!nameMap)
250            return;
251
252        DatabaseNameMap::const_iterator dbNameMapEndIt = nameMap->end();
253        for (DatabaseNameMap::const_iterator dbNameMapIt = nameMap->begin(); dbNameMapIt != dbNameMapEndIt; ++dbNameMapIt) {
254            DatabaseSet* databaseSet = dbNameMapIt->second;
255            DatabaseSet::const_iterator dbSetEndIt = databaseSet->end();
256            for (DatabaseSet::const_iterator dbSetIt = databaseSet->begin(); dbSetIt != dbSetEndIt; ++dbSetIt) {
257                if ((*dbSetIt)->scriptExecutionContext() == context)
258                    openDatabases.append(*dbSetIt);
259            }
260        }
261    }
262
263    Vector<RefPtr<AbstractDatabase> >::const_iterator openDatabasesEndIt = openDatabases.end();
264    for (Vector<RefPtr<AbstractDatabase> >::const_iterator openDatabasesIt = openDatabases.begin(); openDatabasesIt != openDatabasesEndIt; ++openDatabasesIt)
265        (*openDatabasesIt)->interrupt();
266}
267
268String DatabaseTracker::originPath(SecurityOrigin* origin) const
269{
270    return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath.threadsafeCopy(), origin->databaseIdentifier());
271}
272
273String DatabaseTracker::fullPathForDatabaseNoLock(SecurityOrigin* origin, const String& name, bool createIfNotExists)
274{
275    ASSERT(!m_databaseGuard.tryLock());
276    ASSERT(!originQuotaManager().tryLock());
277
278    for (HashSet<ProposedDatabase*>::iterator iter = m_proposedDatabases.begin(); iter != m_proposedDatabases.end(); ++iter)
279        if ((*iter)->second.name() == name && (*iter)->first->equal(origin))
280            return String();
281
282    String originIdentifier = origin->databaseIdentifier();
283    String originPath = this->originPath(origin);
284
285    // Make sure the path for this SecurityOrigin exists
286    if (createIfNotExists && !SQLiteFileSystem::ensureDatabaseDirectoryExists(originPath))
287        return String();
288
289    // See if we have a path for this database yet
290    if (!m_database.isOpen())
291        return String();
292    SQLiteStatement statement(m_database, "SELECT path FROM Databases WHERE origin=? AND name=?;");
293
294    if (statement.prepare() != SQLResultOk)
295        return String();
296
297    statement.bindText(1, originIdentifier);
298    statement.bindText(2, name);
299
300    int result = statement.step();
301
302    if (result == SQLResultRow)
303        return SQLiteFileSystem::appendDatabaseFileNameToPath(originPath, statement.getColumnText(0));
304    if (!createIfNotExists)
305        return String();
306
307    if (result != SQLResultDone) {
308        LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", originIdentifier.ascii().data(), name.ascii().data());
309        return String();
310    }
311    statement.finalize();
312
313    String fileName = SQLiteFileSystem::getFileNameForNewDatabase(originPath, name, originIdentifier, &m_database);
314    if (!addDatabase(origin, name, fileName))
315        return String();
316
317    // If this origin's quota is being tracked (open handle to a database in this origin), add this new database
318    // to the quota manager now
319    String fullFilePath = SQLiteFileSystem::appendDatabaseFileNameToPath(originPath, fileName);
320    if (originQuotaManager().tracksOrigin(origin))
321        originQuotaManager().addDatabase(origin, name, fullFilePath);
322
323    return fullFilePath;
324}
325
326String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfNotExists)
327{
328    MutexLocker lockDatabase(m_databaseGuard);
329    Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
330
331    return fullPathForDatabaseNoLock(origin, name, createIfNotExists).threadsafeCopy();
332}
333
334void DatabaseTracker::populateOrigins()
335{
336    ASSERT(!m_databaseGuard.tryLock());
337    if (m_quotaMap)
338        return;
339
340    m_quotaMap = adoptPtr(new QuotaMap);
341
342    openTrackerDatabase(false);
343    if (!m_database.isOpen())
344        return;
345
346    SQLiteStatement statement(m_database, "SELECT origin, quota FROM Origins");
347
348    if (statement.prepare() != SQLResultOk) {
349        LOG_ERROR("Failed to prepare statement.");
350        return;
351    }
352
353    int result;
354    while ((result = statement.step()) == SQLResultRow) {
355        RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromDatabaseIdentifier(statement.getColumnText(0));
356        m_quotaMap->set(origin.get()->threadsafeCopy(), statement.getColumnInt64(1));
357    }
358
359    if (result != SQLResultDone)
360        LOG_ERROR("Failed to read in all origins from the database.");
361}
362
363void DatabaseTracker::origins(Vector<RefPtr<SecurityOrigin> >& result)
364{
365    MutexLocker lockDatabase(m_databaseGuard);
366    ASSERT(m_quotaMap);
367    copyKeysToVector(*m_quotaMap, result);
368}
369
370bool DatabaseTracker::databaseNamesForOriginNoLock(SecurityOrigin* origin, Vector<String>& resultVector)
371{
372    ASSERT(!m_databaseGuard.tryLock());
373    openTrackerDatabase(false);
374    if (!m_database.isOpen())
375        return false;
376
377    SQLiteStatement statement(m_database, "SELECT name FROM Databases where origin=?;");
378
379    if (statement.prepare() != SQLResultOk)
380        return false;
381
382    statement.bindText(1, origin->databaseIdentifier());
383
384    int result;
385    while ((result = statement.step()) == SQLResultRow)
386        resultVector.append(statement.getColumnText(0));
387
388    if (result != SQLResultDone) {
389        LOG_ERROR("Failed to retrieve all database names for origin %s", origin->databaseIdentifier().ascii().data());
390        return false;
391    }
392
393    return true;
394}
395
396bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& resultVector)
397{
398    Vector<String> temp;
399    {
400        MutexLocker lockDatabase(m_databaseGuard);
401        if (!databaseNamesForOriginNoLock(origin, temp))
402          return false;
403    }
404
405    for (Vector<String>::iterator iter = temp.begin(); iter != temp.end(); ++iter)
406        resultVector.append(iter->threadsafeCopy());
407    return true;
408}
409
410DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin)
411{
412    String originIdentifier = origin->databaseIdentifier();
413    String displayName;
414    int64_t expectedUsage;
415
416    {
417        MutexLocker lockDatabase(m_databaseGuard);
418
419        for (HashSet<ProposedDatabase*>::iterator iter = m_proposedDatabases.begin(); iter != m_proposedDatabases.end(); ++iter)
420            if ((*iter)->second.name() == name && (*iter)->first->equal(origin)) {
421                ASSERT((*iter)->second.thread() == currentThread());
422                return (*iter)->second;
423            }
424
425        openTrackerDatabase(false);
426        if (!m_database.isOpen())
427            return DatabaseDetails();
428        SQLiteStatement statement(m_database, "SELECT displayName, estimatedSize FROM Databases WHERE origin=? AND name=?");
429        if (statement.prepare() != SQLResultOk)
430            return DatabaseDetails();
431
432        statement.bindText(1, originIdentifier);
433        statement.bindText(2, name);
434
435        int result = statement.step();
436        if (result == SQLResultDone)
437            return DatabaseDetails();
438
439        if (result != SQLResultRow) {
440            LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.ascii().data(), originIdentifier.ascii().data());
441            return DatabaseDetails();
442        }
443        displayName = statement.getColumnText(0);
444        expectedUsage = statement.getColumnInt64(1);
445    }
446
447    return DatabaseDetails(name, displayName, expectedUsage, usageForDatabase(name, origin));
448}
449
450void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize)
451{
452    String originIdentifier = origin->databaseIdentifier();
453    int64_t guid = 0;
454
455    MutexLocker lockDatabase(m_databaseGuard);
456
457    openTrackerDatabase(true);
458    if (!m_database.isOpen())
459        return;
460    SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?");
461    if (statement.prepare() != SQLResultOk)
462        return;
463
464    statement.bindText(1, originIdentifier);
465    statement.bindText(2, name);
466
467    int result = statement.step();
468    if (result == SQLResultRow)
469        guid = statement.getColumnInt64(0);
470    statement.finalize();
471
472    if (guid == 0) {
473        if (result != SQLResultDone)
474            LOG_ERROR("Error to determing existence of database %s in origin %s in tracker database", name.ascii().data(), originIdentifier.ascii().data());
475        else {
476            // This case should never occur - we should never be setting database details for a database that doesn't already exist in the tracker
477            // But since the tracker file is an external resource not under complete control of our code, it's somewhat invalid to make this an ASSERT case
478            // So we'll print an error instead
479            LOG_ERROR("Could not retrieve guid for database %s in origin %s from the tracker database - it is invalid to set database details on a database that doesn't already exist in the tracker",
480                       name.ascii().data(), originIdentifier.ascii().data());
481        }
482        return;
483    }
484
485    SQLiteStatement updateStatement(m_database, "UPDATE Databases SET displayName=?, estimatedSize=? WHERE guid=?");
486    if (updateStatement.prepare() != SQLResultOk)
487        return;
488
489    updateStatement.bindText(1, displayName);
490    updateStatement.bindInt64(2, estimatedSize);
491    updateStatement.bindInt64(3, guid);
492
493    if (updateStatement.step() != SQLResultDone) {
494        LOG_ERROR("Failed to update details for database %s in origin %s", name.ascii().data(), originIdentifier.ascii().data());
495        return;
496    }
497
498    if (m_client)
499        m_client->dispatchDidModifyDatabase(origin, name);
500}
501
502unsigned long long DatabaseTracker::usageForDatabase(const String& name, SecurityOrigin* origin)
503{
504    String path = fullPathForDatabase(origin, name, false);
505    if (path.isEmpty())
506        return 0;
507
508    return SQLiteFileSystem::getDatabaseFileSize(path);
509}
510
511void DatabaseTracker::addOpenDatabase(AbstractDatabase* database)
512{
513    if (!database)
514        return;
515
516    {
517        MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
518
519        if (!m_openDatabaseMap)
520            m_openDatabaseMap = adoptPtr(new DatabaseOriginMap);
521
522        String name(database->stringIdentifier());
523        DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin());
524        if (!nameMap) {
525            nameMap = new DatabaseNameMap;
526            m_openDatabaseMap->set(database->securityOrigin()->threadsafeCopy(), nameMap);
527        }
528
529        DatabaseSet* databaseSet = nameMap->get(name);
530        if (!databaseSet) {
531            databaseSet = new DatabaseSet;
532            nameMap->set(name.threadsafeCopy(), databaseSet);
533        }
534
535        databaseSet->add(database);
536
537        LOG(StorageAPI, "Added open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database);
538
539        Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
540        if (!originQuotaManager().tracksOrigin(database->securityOrigin())) {
541            originQuotaManager().trackOrigin(database->securityOrigin());
542            originQuotaManager().addDatabase(database->securityOrigin(), database->stringIdentifier(), database->fileName());
543        }
544    }
545
546    MutexLocker lockDatabase(m_databaseGuard);
547    doneCreatingDatabase(database->securityOrigin(), database->stringIdentifier());
548}
549
550void DatabaseTracker::removeOpenDatabase(AbstractDatabase* database)
551{
552    if (!database)
553        return;
554
555    {
556        MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
557
558        if (!m_openDatabaseMap) {
559            ASSERT_NOT_REACHED();
560            return;
561        }
562
563        String name(database->stringIdentifier());
564        DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin());
565        if (!nameMap) {
566            ASSERT_NOT_REACHED();
567            return;
568        }
569
570        DatabaseSet* databaseSet = nameMap->get(name);
571        if (!databaseSet) {
572            ASSERT_NOT_REACHED();
573            return;
574        }
575
576        databaseSet->remove(database);
577
578        LOG(StorageAPI, "Removed open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database);
579
580        if (!databaseSet->isEmpty())
581            return;
582
583        nameMap->remove(name);
584        delete databaseSet;
585
586        if (!nameMap->isEmpty())
587            return;
588
589        m_openDatabaseMap->remove(database->securityOrigin());
590        delete nameMap;
591
592        Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
593        originQuotaManager().removeOrigin(database->securityOrigin());
594    }
595}
596
597void DatabaseTracker::getOpenDatabases(SecurityOrigin* origin, const String& name, HashSet<RefPtr<AbstractDatabase> >* databases)
598{
599    MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
600    if (!m_openDatabaseMap)
601        return;
602
603    DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin);
604    if (!nameMap)
605        return;
606
607    DatabaseSet* databaseSet = nameMap->get(name);
608    if (!databaseSet)
609        return;
610
611    for (DatabaseSet::iterator it = databaseSet->begin(); it != databaseSet->end(); ++it)
612        databases->add(*it);
613}
614
615unsigned long long DatabaseTracker::usageForOriginNoLock(SecurityOrigin* origin)
616{
617    ASSERT(!originQuotaManager().tryLock());
618
619    // Use the OriginQuotaManager mechanism to calculate the usage
620    if (originQuotaManager().tracksOrigin(origin))
621        return originQuotaManager().diskUsage(origin);
622
623    // If the OriginQuotaManager doesn't track this origin already, prime it to do so
624    originQuotaManager().trackOrigin(origin);
625
626    Vector<String> names;
627    databaseNamesForOriginNoLock(origin, names);
628
629    for (unsigned i = 0; i < names.size(); ++i)
630        originQuotaManager().addDatabase(origin, names[i], fullPathForDatabaseNoLock(origin, names[i], false));
631
632    if (!originQuotaManager().tracksOrigin(origin))
633        return 0;
634    return originQuotaManager().diskUsage(origin);
635}
636
637unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin)
638{
639    MutexLocker lockDatabase(m_databaseGuard);
640    Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
641    return usageForOriginNoLock(origin);
642}
643
644unsigned long long DatabaseTracker::quotaForOriginNoLock(SecurityOrigin* origin)
645{
646    ASSERT(!m_databaseGuard.tryLock());
647    ASSERT(m_quotaMap);
648    return m_quotaMap->get(origin);
649}
650
651unsigned long long DatabaseTracker::quotaForOrigin(SecurityOrigin* origin)
652{
653    MutexLocker lockDatabase(m_databaseGuard);
654    return quotaForOriginNoLock(origin);
655}
656
657void DatabaseTracker::setQuota(SecurityOrigin* origin, unsigned long long quota)
658{
659    MutexLocker lockDatabase(m_databaseGuard);
660
661    if (quotaForOriginNoLock(origin) == quota)
662        return;
663
664    openTrackerDatabase(true);
665    if (!m_database.isOpen())
666        return;
667
668    if (!m_quotaMap->contains(origin)) {
669        SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)");
670        if (statement.prepare() != SQLResultOk) {
671            LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
672        } else {
673            statement.bindText(1, origin->databaseIdentifier());
674            statement.bindInt64(2, quota);
675
676            if (statement.step() != SQLResultDone)
677                LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
678        }
679    } else {
680        SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?");
681        bool error = statement.prepare() != SQLResultOk;
682        if (!error) {
683            statement.bindInt64(1, quota);
684            statement.bindText(2, origin->databaseIdentifier());
685
686            error = !statement.executeCommand();
687        }
688
689        if (error)
690#if OS(WINDOWS)
691            LOG_ERROR("Failed to set quota %I64u in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data());
692#else
693            LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data());
694#endif
695    }
696
697    // FIXME: Is it really OK to update the quota in memory if we failed to update it on disk?
698    m_quotaMap->set(origin->threadsafeCopy(), quota);
699
700    if (m_client)
701        m_client->dispatchDidModifyOrigin(origin);
702}
703
704bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, const String& path)
705{
706    ASSERT(!m_databaseGuard.tryLock());
707    ASSERT(m_quotaMap);
708    openTrackerDatabase(true);
709    if (!m_database.isOpen())
710        return false;
711
712    // New database should never be added until the origin has been established
713    ASSERT(hasEntryForOriginNoLock(origin));
714
715    SQLiteStatement statement(m_database, "INSERT INTO Databases (origin, name, path) VALUES (?, ?, ?);");
716
717    if (statement.prepare() != SQLResultOk)
718        return false;
719
720    statement.bindText(1, origin->databaseIdentifier());
721    statement.bindText(2, name);
722    statement.bindText(3, path);
723
724    if (!statement.executeCommand()) {
725        LOG_ERROR("Failed to add database %s to origin %s: %s\n", name.ascii().data(), origin->databaseIdentifier().ascii().data(), m_database.lastErrorMsg());
726        return false;
727    }
728
729    if (m_client)
730        m_client->dispatchDidModifyOrigin(origin);
731
732    return true;
733}
734
735void DatabaseTracker::deleteAllDatabases()
736{
737    Vector<RefPtr<SecurityOrigin> > originsCopy;
738    origins(originsCopy);
739
740    for (unsigned i = 0; i < originsCopy.size(); ++i)
741        deleteOrigin(originsCopy[i].get());
742}
743
744// It is the caller's responsibility to make sure that nobody is trying to create, delete, open, or close databases in this origin while the deletion is
745// taking place.
746bool DatabaseTracker::deleteOrigin(SecurityOrigin* origin)
747{
748    Vector<String> databaseNames;
749    {
750        MutexLocker lockDatabase(m_databaseGuard);
751        openTrackerDatabase(false);
752        if (!m_database.isOpen())
753            return false;
754
755        if (!databaseNamesForOriginNoLock(origin, databaseNames)) {
756            LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data());
757            return false;
758        }
759        if (!canDeleteOrigin(origin)) {
760            LOG_ERROR("Tried to delete an origin (%s) while either creating database in it or already deleting it", origin->databaseIdentifier().ascii().data());
761            ASSERT(false);
762            return false;
763        }
764        recordDeletingOrigin(origin);
765    }
766
767    // We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock.
768    for (unsigned i = 0; i < databaseNames.size(); ++i) {
769        if (!deleteDatabaseFile(origin, databaseNames[i])) {
770            // Even if the file can't be deleted, we want to try and delete the rest, don't return early here.
771            LOG_ERROR("Unable to delete file for database %s in origin %s", databaseNames[i].ascii().data(), origin->databaseIdentifier().ascii().data());
772        }
773    }
774
775    {
776        MutexLocker lockDatabase(m_databaseGuard);
777        doneDeletingOrigin(origin);
778
779        SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?");
780        if (statement.prepare() != SQLResultOk) {
781            LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
782            return false;
783        }
784
785        statement.bindText(1, origin->databaseIdentifier());
786
787        if (!statement.executeCommand()) {
788            LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
789            return false;
790        }
791
792        SQLiteStatement originStatement(m_database, "DELETE FROM Origins WHERE origin=?");
793        if (originStatement.prepare() != SQLResultOk) {
794            LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin->databaseIdentifier().ascii().data());
795            return false;
796        }
797
798        originStatement.bindText(1, origin->databaseIdentifier());
799
800        if (!originStatement.executeCommand()) {
801            LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
802            return false;
803        }
804
805        SQLiteFileSystem::deleteEmptyDatabaseDirectory(originPath(origin));
806
807        RefPtr<SecurityOrigin> originPossiblyLastReference = origin;
808        m_quotaMap->remove(origin);
809
810        {
811            Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
812            originQuotaManager().removeOrigin(origin);
813        }
814
815        // If we removed the last origin, do some additional deletion.
816        if (m_quotaMap->isEmpty()) {
817            if (m_database.isOpen())
818                m_database.close();
819           SQLiteFileSystem::deleteDatabaseFile(trackerDatabasePath());
820           SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_databaseDirectoryPath);
821        }
822
823        if (m_client) {
824            m_client->dispatchDidModifyOrigin(origin);
825            for (unsigned i = 0; i < databaseNames.size(); ++i)
826                m_client->dispatchDidModifyDatabase(origin, databaseNames[i]);
827        }
828    }
829    return true;
830}
831
832bool DatabaseTracker::canCreateDatabase(SecurityOrigin *origin, const String& name)
833{
834    ASSERT(!m_databaseGuard.tryLock());
835    // Can't create a database while someone else is deleting it; there's a risk of leaving untracked database debris on the disk.
836    return !deletingDatabase(origin, name) && !deletingOrigin(origin);
837}
838
839void DatabaseTracker::recordCreatingDatabase(SecurityOrigin *origin, const String& name)
840{
841    ASSERT(!m_databaseGuard.tryLock());
842    NameCountMap* nameMap = m_beingCreated.get(origin);
843    if (!nameMap) {
844        nameMap = new NameCountMap();
845        m_beingCreated.set(origin->threadsafeCopy(), nameMap);
846    }
847    long count = nameMap->get(name);
848    nameMap->set(name.threadsafeCopy(), count + 1);
849}
850
851void DatabaseTracker::doneCreatingDatabase(SecurityOrigin *origin, const String& name)
852{
853    ASSERT(!m_databaseGuard.tryLock());
854    NameCountMap* nameMap = m_beingCreated.get(origin);
855    if (nameMap) {
856        long count = nameMap->get(name);
857        ASSERT(count > 0);
858        if (count <= 1) {
859            nameMap->remove(name);
860            if (nameMap->isEmpty()) {
861                m_beingCreated.remove(origin);
862                delete nameMap;
863            }
864        } else
865            nameMap->set(name, count - 1);
866    } else
867        ASSERT(false);
868}
869
870bool DatabaseTracker::creatingDatabase(SecurityOrigin *origin, const String& name)
871{
872    ASSERT(!m_databaseGuard.tryLock());
873    NameCountMap* nameMap = m_beingCreated.get(origin);
874    return nameMap && nameMap->get(name);
875}
876
877bool DatabaseTracker::canDeleteDatabase(SecurityOrigin *origin, const String& name)
878{
879    ASSERT(!m_databaseGuard.tryLock());
880    return !creatingDatabase(origin, name) && !deletingDatabase(origin, name);
881}
882
883void DatabaseTracker::recordDeletingDatabase(SecurityOrigin *origin, const String& name)
884{
885    ASSERT(!m_databaseGuard.tryLock());
886    ASSERT(canDeleteDatabase(origin, name));
887    NameSet* nameSet = m_beingDeleted.get(origin);
888    if (!nameSet) {
889        nameSet = new NameSet();
890        m_beingDeleted.set(origin->threadsafeCopy(), nameSet);
891    }
892    ASSERT(!nameSet->contains(name));
893    nameSet->add(name.threadsafeCopy());
894}
895
896void DatabaseTracker::doneDeletingDatabase(SecurityOrigin *origin, const String& name)
897{
898    ASSERT(!m_databaseGuard.tryLock());
899    NameSet* nameSet = m_beingDeleted.get(origin);
900    if (nameSet) {
901        ASSERT(nameSet->contains(name));
902        nameSet->remove(name);
903        if (nameSet->isEmpty()) {
904            m_beingDeleted.remove(origin);
905            delete nameSet;
906        }
907    } else {
908        ASSERT(false);
909    }
910}
911
912bool DatabaseTracker::deletingDatabase(SecurityOrigin *origin, const String& name)
913{
914    ASSERT(!m_databaseGuard.tryLock());
915    NameSet* nameSet = m_beingDeleted.get(origin);
916    return nameSet && nameSet->contains(name);
917}
918
919bool DatabaseTracker::canDeleteOrigin(SecurityOrigin *origin)
920{
921    ASSERT(!m_databaseGuard.tryLock());
922    return !(deletingOrigin(origin) || m_beingCreated.get(origin));
923}
924
925bool DatabaseTracker::deletingOrigin(SecurityOrigin *origin)
926{
927    ASSERT(!m_databaseGuard.tryLock());
928    return m_originsBeingDeleted.contains(origin);
929}
930
931void DatabaseTracker::recordDeletingOrigin(SecurityOrigin *origin)
932{
933    ASSERT(!m_databaseGuard.tryLock());
934    ASSERT(!deletingOrigin(origin));
935    m_originsBeingDeleted.add(origin->threadsafeCopy());
936}
937
938void DatabaseTracker::doneDeletingOrigin(SecurityOrigin *origin)
939{
940    ASSERT(!m_databaseGuard.tryLock());
941    ASSERT(deletingOrigin(origin));
942    m_originsBeingDeleted.remove(origin);
943}
944
945bool DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name)
946{
947    {
948        MutexLocker lockDatabase(m_databaseGuard);
949        openTrackerDatabase(false);
950        if (!m_database.isOpen())
951            return false;
952
953        if (!canDeleteDatabase(origin, name)) {
954            ASSERT(FALSE);
955            return false;
956        }
957        recordDeletingDatabase(origin, name);
958    }
959
960    // We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock.
961    if (!deleteDatabaseFile(origin, name)) {
962        LOG_ERROR("Unable to delete file for database %s in origin %s", name.ascii().data(), origin->databaseIdentifier().ascii().data());
963        MutexLocker lockDatabase(m_databaseGuard);
964        doneDeletingDatabase(origin, name);
965        return false;
966    }
967
968    MutexLocker lockDatabase(m_databaseGuard);
969
970    SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=? AND name=?");
971    if (statement.prepare() != SQLResultOk) {
972        LOG_ERROR("Unable to prepare deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data());
973        doneDeletingDatabase(origin, name);
974        return false;
975    }
976
977    statement.bindText(1, origin->databaseIdentifier());
978    statement.bindText(2, name);
979
980    if (!statement.executeCommand()) {
981        LOG_ERROR("Unable to execute deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data());
982        doneDeletingDatabase(origin, name);
983        return false;
984    }
985
986    {
987        Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
988        originQuotaManager().removeDatabase(origin, name);
989    }
990
991    if (m_client) {
992        m_client->dispatchDidModifyOrigin(origin);
993        m_client->dispatchDidModifyDatabase(origin, name);
994    }
995    doneDeletingDatabase(origin, name);
996
997    return true;
998}
999
1000// deleteDatabaseFile has to release locks between looking up the list of databases to close and closing them.  While this is in progress, the caller
1001// is responsible for making sure no new databases are opened in the file to be deleted.
1002bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& name)
1003{
1004    String fullPath = fullPathForDatabase(origin, name, false);
1005    if (fullPath.isEmpty())
1006        return true;
1007
1008#ifndef NDEBUG
1009    {
1010        MutexLocker lockDatabase(m_databaseGuard);
1011        ASSERT(deletingDatabase(origin, name) || deletingOrigin(origin));
1012    }
1013#endif
1014
1015    Vector<RefPtr<AbstractDatabase> > deletedDatabases;
1016
1017    // Make sure not to hold the any locks when calling
1018    // Database::markAsDeletedAndClose(), since that can cause a deadlock
1019    // during the synchronous DatabaseThread call it triggers.
1020    {
1021        MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
1022        if (m_openDatabaseMap) {
1023            // There are some open databases, lets check if they are for this origin.
1024            DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin);
1025            if (nameMap && nameMap->size()) {
1026                // There are some open databases for this origin, let's check
1027                // if they are this database by name.
1028                DatabaseSet* databaseSet = nameMap->get(name);
1029                if (databaseSet && databaseSet->size()) {
1030                    // We have some database open with this name. Mark them as deleted.
1031                    DatabaseSet::const_iterator end = databaseSet->end();
1032                    for (DatabaseSet::const_iterator it = databaseSet->begin(); it != end; ++it)
1033                        deletedDatabases.append(*it);
1034                }
1035            }
1036        }
1037    }
1038
1039    for (unsigned i = 0; i < deletedDatabases.size(); ++i)
1040        deletedDatabases[i]->markAsDeletedAndClose();
1041
1042    return SQLiteFileSystem::deleteDatabaseFile(fullPath);
1043}
1044
1045void DatabaseTracker::setClient(DatabaseTrackerClient* client)
1046{
1047    m_client = client;
1048}
1049
1050static Mutex& notificationMutex()
1051{
1052    DEFINE_STATIC_LOCAL(Mutex, mutex, ());
1053    return mutex;
1054}
1055
1056typedef Vector<pair<RefPtr<SecurityOrigin>, String> > NotificationQueue;
1057
1058static NotificationQueue& notificationQueue()
1059{
1060    DEFINE_STATIC_LOCAL(NotificationQueue, queue, ());
1061    return queue;
1062}
1063
1064void DatabaseTracker::scheduleNotifyDatabaseChanged(SecurityOrigin* origin, const String& name)
1065{
1066    MutexLocker locker(notificationMutex());
1067
1068    notificationQueue().append(pair<RefPtr<SecurityOrigin>, String>(origin->threadsafeCopy(), name.crossThreadString()));
1069    scheduleForNotification();
1070}
1071
1072static bool notificationScheduled = false;
1073
1074void DatabaseTracker::scheduleForNotification()
1075{
1076    ASSERT(!notificationMutex().tryLock());
1077
1078    if (!notificationScheduled) {
1079        callOnMainThread(DatabaseTracker::notifyDatabasesChanged, 0);
1080        notificationScheduled = true;
1081    }
1082}
1083
1084void DatabaseTracker::notifyDatabasesChanged(void*)
1085{
1086    // Note that if DatabaseTracker ever becomes non-singleton, we'll have to amend this notification
1087    // mechanism to include which tracker the notification goes out on as well.
1088    DatabaseTracker& theTracker(tracker());
1089
1090    NotificationQueue notifications;
1091    {
1092        MutexLocker locker(notificationMutex());
1093
1094        notifications.swap(notificationQueue());
1095
1096        notificationScheduled = false;
1097    }
1098
1099    if (!theTracker.m_client)
1100        return;
1101
1102    for (unsigned i = 0; i < notifications.size(); ++i)
1103        theTracker.m_client->dispatchDidModifyDatabase(notifications[i].first.get(), notifications[i].second);
1104}
1105
1106
1107} // namespace WebCore
1108#endif
1109