Home | History | Annotate | Download | only in storage
      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 
     51 using namespace std;
     52 
     53 static WebCore::OriginQuotaManager& originQuotaManager()
     54 {
     55     DEFINE_STATIC_LOCAL(WebCore::OriginQuotaManager, quotaManager, ());
     56     return quotaManager;
     57 }
     58 
     59 namespace WebCore {
     60 
     61 static DatabaseTracker* staticTracker = 0;
     62 
     63 void DatabaseTracker::initializeTracker(const String& databasePath)
     64 {
     65     ASSERT(!staticTracker);
     66     if (staticTracker)
     67         return;
     68 
     69     staticTracker = new DatabaseTracker(databasePath);
     70 }
     71 
     72 DatabaseTracker& DatabaseTracker::tracker()
     73 {
     74     if (!staticTracker)
     75         staticTracker = new DatabaseTracker("");
     76 
     77     return *staticTracker;
     78 }
     79 
     80 DatabaseTracker::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 
     91 void DatabaseTracker::setDatabaseDirectoryPath(const String& path)
     92 {
     93     MutexLocker lockDatabase(m_databaseGuard);
     94     ASSERT(!m_database.isOpen());
     95     m_databaseDirectoryPath = path.threadsafeCopy();
     96 }
     97 
     98 String DatabaseTracker::databaseDirectoryPath() const
     99 {
    100     return m_databaseDirectoryPath.threadsafeCopy();
    101 }
    102 
    103 String DatabaseTracker::trackerDatabasePath() const
    104 {
    105     return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath, "Databases.db");
    106 }
    107 
    108 void 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 
    139 bool 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 
    193 bool DatabaseTracker::hasEntryForOriginNoLock(SecurityOrigin* origin)
    194 {
    195     ASSERT(!m_databaseGuard.tryLock());
    196     ASSERT(m_quotaMap);
    197     return m_quotaMap->contains(origin);
    198 }
    199 
    200 bool DatabaseTracker::hasEntryForOrigin(SecurityOrigin* origin)
    201 {
    202     MutexLocker lockDatabase(m_databaseGuard);
    203     return hasEntryForOriginNoLock(origin);
    204 }
    205 
    206 bool 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 
    223 unsigned 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 
    233 void DatabaseTracker::databaseChanged(AbstractDatabase* database)
    234 {
    235     Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
    236     originQuotaManager().markDatabase(database);
    237 }
    238 
    239 void 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 
    268 String DatabaseTracker::originPath(SecurityOrigin* origin) const
    269 {
    270     return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath.threadsafeCopy(), origin->databaseIdentifier());
    271 }
    272 
    273 String 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 
    326 String 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 
    334 void 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 
    363 void DatabaseTracker::origins(Vector<RefPtr<SecurityOrigin> >& result)
    364 {
    365     MutexLocker lockDatabase(m_databaseGuard);
    366     ASSERT(m_quotaMap);
    367     copyKeysToVector(*m_quotaMap, result);
    368 }
    369 
    370 bool 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 
    396 bool 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 
    410 DatabaseDetails 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 
    450 void 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 
    502 unsigned 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 
    511 void 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 
    550 void 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 
    597 void 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 
    615 unsigned 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 
    637 unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin)
    638 {
    639     MutexLocker lockDatabase(m_databaseGuard);
    640     Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
    641     return usageForOriginNoLock(origin);
    642 }
    643 
    644 unsigned long long DatabaseTracker::quotaForOriginNoLock(SecurityOrigin* origin)
    645 {
    646     ASSERT(!m_databaseGuard.tryLock());
    647     ASSERT(m_quotaMap);
    648     return m_quotaMap->get(origin);
    649 }
    650 
    651 unsigned long long DatabaseTracker::quotaForOrigin(SecurityOrigin* origin)
    652 {
    653     MutexLocker lockDatabase(m_databaseGuard);
    654     return quotaForOriginNoLock(origin);
    655 }
    656 
    657 void 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 
    704 bool 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 
    735 void 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.
    746 bool 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 
    832 bool 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 
    839 void 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 
    851 void 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 
    870 bool 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 
    877 bool DatabaseTracker::canDeleteDatabase(SecurityOrigin *origin, const String& name)
    878 {
    879     ASSERT(!m_databaseGuard.tryLock());
    880     return !creatingDatabase(origin, name) && !deletingDatabase(origin, name);
    881 }
    882 
    883 void 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 
    896 void 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 
    912 bool 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 
    919 bool DatabaseTracker::canDeleteOrigin(SecurityOrigin *origin)
    920 {
    921     ASSERT(!m_databaseGuard.tryLock());
    922     return !(deletingOrigin(origin) || m_beingCreated.get(origin));
    923 }
    924 
    925 bool DatabaseTracker::deletingOrigin(SecurityOrigin *origin)
    926 {
    927     ASSERT(!m_databaseGuard.tryLock());
    928     return m_originsBeingDeleted.contains(origin);
    929 }
    930 
    931 void DatabaseTracker::recordDeletingOrigin(SecurityOrigin *origin)
    932 {
    933     ASSERT(!m_databaseGuard.tryLock());
    934     ASSERT(!deletingOrigin(origin));
    935     m_originsBeingDeleted.add(origin->threadsafeCopy());
    936 }
    937 
    938 void DatabaseTracker::doneDeletingOrigin(SecurityOrigin *origin)
    939 {
    940     ASSERT(!m_databaseGuard.tryLock());
    941     ASSERT(deletingOrigin(origin));
    942     m_originsBeingDeleted.remove(origin);
    943 }
    944 
    945 bool 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.
   1002 bool 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 
   1045 void DatabaseTracker::setClient(DatabaseTrackerClient* client)
   1046 {
   1047     m_client = client;
   1048 }
   1049 
   1050 static Mutex& notificationMutex()
   1051 {
   1052     DEFINE_STATIC_LOCAL(Mutex, mutex, ());
   1053     return mutex;
   1054 }
   1055 
   1056 typedef Vector<pair<RefPtr<SecurityOrigin>, String> > NotificationQueue;
   1057 
   1058 static NotificationQueue& notificationQueue()
   1059 {
   1060     DEFINE_STATIC_LOCAL(NotificationQueue, queue, ());
   1061     return queue;
   1062 }
   1063 
   1064 void 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 
   1072 static bool notificationScheduled = false;
   1073 
   1074 void DatabaseTracker::scheduleForNotification()
   1075 {
   1076     ASSERT(!notificationMutex().tryLock());
   1077 
   1078     if (!notificationScheduled) {
   1079         callOnMainThread(DatabaseTracker::notifyDatabasesChanged, 0);
   1080         notificationScheduled = true;
   1081     }
   1082 }
   1083 
   1084 void 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