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 "Chrome.h"
     35 #include "ChromeClient.h"
     36 #include "Database.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 
     50 using namespace std;
     51 
     52 namespace WebCore {
     53 
     54 OriginQuotaManager& DatabaseTracker::originQuotaManager()
     55 {
     56     populateOrigins();
     57     ASSERT(m_quotaManager);
     58     return *m_quotaManager;
     59 }
     60 
     61 DatabaseTracker& DatabaseTracker::tracker()
     62 {
     63     DEFINE_STATIC_LOCAL(DatabaseTracker, tracker, ());
     64     return tracker;
     65 }
     66 
     67 DatabaseTracker::DatabaseTracker()
     68     : m_client(0)
     69     , m_proposedDatabase(0)
     70 #ifndef NDEBUG
     71     , m_thread(currentThread())
     72 #endif
     73 {
     74     SQLiteFileSystem::registerSQLiteVFS();
     75 }
     76 
     77 void DatabaseTracker::setDatabaseDirectoryPath(const String& path)
     78 {
     79     ASSERT(currentThread() == m_thread);
     80     ASSERT(!m_database.isOpen());
     81     m_databaseDirectoryPath = path;
     82 }
     83 
     84 const String& DatabaseTracker::databaseDirectoryPath() const
     85 {
     86     ASSERT(currentThread() == m_thread);
     87     return m_databaseDirectoryPath;
     88 }
     89 
     90 String DatabaseTracker::trackerDatabasePath() const
     91 {
     92     ASSERT(currentThread() == m_thread);
     93     return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath, "Databases.db");
     94 }
     95 
     96 void DatabaseTracker::openTrackerDatabase(bool createIfDoesNotExist)
     97 {
     98     ASSERT(currentThread() == m_thread);
     99 
    100     if (m_database.isOpen())
    101         return;
    102 
    103     String databasePath = trackerDatabasePath();
    104     if (!SQLiteFileSystem::ensureDatabaseFileExists(databasePath, createIfDoesNotExist))
    105         return;
    106 
    107     if (!m_database.open(databasePath)) {
    108         // FIXME: What do do here?
    109         return;
    110     }
    111     if (!m_database.tableExists("Origins")) {
    112         if (!m_database.executeCommand("CREATE TABLE Origins (origin TEXT UNIQUE ON CONFLICT REPLACE, quota INTEGER NOT NULL ON CONFLICT FAIL);")) {
    113             // FIXME: and here
    114         }
    115     }
    116     if (!m_database.tableExists("Databases")) {
    117         if (!m_database.executeCommand("CREATE TABLE Databases (guid INTEGER PRIMARY KEY AUTOINCREMENT, origin TEXT, name TEXT, displayName TEXT, estimatedSize INTEGER, path TEXT);")) {
    118             // FIXME: and here
    119         }
    120     }
    121 }
    122 
    123 bool DatabaseTracker::canEstablishDatabase(ScriptExecutionContext* context, const String& name, const String& displayName, unsigned long estimatedSize)
    124 {
    125     ASSERT(currentThread() == m_thread);
    126 
    127     // Populate the origins before we establish a database; this guarantees that quotaForOrigin
    128     // can run on the database thread later.
    129     populateOrigins();
    130 
    131     SecurityOrigin* origin = context->securityOrigin();
    132 
    133     // Since we're imminently opening a database within this context's origin, make sure this origin is being tracked by the QuotaTracker
    134     // by fetching it's current usage now
    135     unsigned long long usage = usageForOrigin(origin);
    136 
    137     // If a database already exists, ignore the passed-in estimated size and say it's OK.
    138     if (hasEntryForDatabase(origin, name))
    139         return true;
    140 
    141     // If the database will fit, allow its creation.
    142     unsigned long long requirement = usage + max(1UL, estimatedSize);
    143     if (requirement < usage)
    144         return false; // If the estimated size is so big it causes an overflow, don't allow creation.
    145     if (requirement <= quotaForOrigin(origin))
    146         return true;
    147 
    148     // Give the chrome client a chance to increase the quota.
    149     // Temporarily make the details of the proposed database available, so the client can get at them.
    150     pair<SecurityOrigin*, DatabaseDetails> details(origin, DatabaseDetails(name, displayName, estimatedSize, 0));
    151     m_proposedDatabase = &details;
    152     context->databaseExceededQuota(name);
    153     m_proposedDatabase = 0;
    154 
    155     // If the database will fit now, allow its creation.
    156     return requirement <= quotaForOrigin(origin);
    157 }
    158 
    159 bool DatabaseTracker::hasEntryForOrigin(SecurityOrigin* origin)
    160 {
    161     ASSERT(currentThread() == m_thread);
    162     populateOrigins();
    163     MutexLocker lockQuotaMap(m_quotaMapGuard);
    164     return m_quotaMap->contains(origin);
    165 }
    166 
    167 bool DatabaseTracker::hasEntryForDatabase(SecurityOrigin* origin, const String& databaseIdentifier)
    168 {
    169     ASSERT(currentThread() == m_thread);
    170     openTrackerDatabase(false);
    171     if (!m_database.isOpen())
    172         return false;
    173     SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?;");
    174 
    175     if (statement.prepare() != SQLResultOk)
    176         return false;
    177 
    178     statement.bindText(1, origin->databaseIdentifier());
    179     statement.bindText(2, databaseIdentifier);
    180 
    181     return statement.step() == SQLResultRow;
    182 }
    183 
    184 unsigned long long DatabaseTracker::getMaxSizeForDatabase(const Database* database)
    185 {
    186     ASSERT(currentThread() == database->scriptExecutionContext()->databaseThread()->getThreadID());
    187     // The maximum size for a database is the full quota for its origin, minus the current usage within the origin,
    188     // plus the current usage of the given database
    189     Locker<OriginQuotaManager> locker(originQuotaManager());
    190     SecurityOrigin* origin = database->securityOrigin();
    191     return quotaForOrigin(origin) - originQuotaManager().diskUsage(origin) + SQLiteFileSystem::getDatabaseFileSize(database->fileName());
    192 }
    193 
    194 String DatabaseTracker::originPath(SecurityOrigin* origin) const
    195 {
    196     ASSERT(currentThread() == m_thread);
    197     return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath, origin->databaseIdentifier());
    198 }
    199 
    200 String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfNotExists)
    201 {
    202     ASSERT(currentThread() == m_thread);
    203 
    204     if (m_proposedDatabase && m_proposedDatabase->first == origin && m_proposedDatabase->second.name() == name)
    205         return String();
    206 
    207     String originIdentifier = origin->databaseIdentifier();
    208     String originPath = this->originPath(origin);
    209 
    210     // Make sure the path for this SecurityOrigin exists
    211     if (createIfNotExists && !SQLiteFileSystem::ensureDatabaseDirectoryExists(originPath))
    212         return String();
    213 
    214     // See if we have a path for this database yet
    215     openTrackerDatabase(false);
    216     if (!m_database.isOpen())
    217         return String();
    218     SQLiteStatement statement(m_database, "SELECT path FROM Databases WHERE origin=? AND name=?;");
    219 
    220     if (statement.prepare() != SQLResultOk)
    221         return String();
    222 
    223     statement.bindText(1, originIdentifier);
    224     statement.bindText(2, name);
    225 
    226     int result = statement.step();
    227 
    228     if (result == SQLResultRow)
    229         return SQLiteFileSystem::appendDatabaseFileNameToPath(originPath, statement.getColumnText(0));
    230     if (!createIfNotExists)
    231         return String();
    232 
    233     if (result != SQLResultDone) {
    234         LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", origin->databaseIdentifier().ascii().data(), name.ascii().data());
    235         return String();
    236     }
    237     statement.finalize();
    238 
    239     String fileName = SQLiteFileSystem::getFileNameForNewDatabase(originPath, name, origin->databaseIdentifier(), &m_database);
    240     if (!addDatabase(origin, name, fileName))
    241         return String();
    242 
    243     // If this origin's quota is being tracked (open handle to a database in this origin), add this new database
    244     // to the quota manager now
    245     String fullFilePath = SQLiteFileSystem::appendDatabaseFileNameToPath(originPath, fileName);
    246     {
    247         Locker<OriginQuotaManager> locker(originQuotaManager());
    248         if (originQuotaManager().tracksOrigin(origin))
    249             originQuotaManager().addDatabase(origin, name, fullFilePath);
    250     }
    251 
    252     return fullFilePath;
    253 }
    254 
    255 void DatabaseTracker::populateOrigins()
    256 {
    257     if (m_quotaMap)
    258         return;
    259 
    260     ASSERT(currentThread() == m_thread);
    261 
    262     m_quotaMap.set(new QuotaMap);
    263     m_quotaManager.set(new OriginQuotaManager);
    264 
    265     openTrackerDatabase(false);
    266     if (!m_database.isOpen())
    267         return;
    268 
    269     SQLiteStatement statement(m_database, "SELECT origin, quota FROM Origins");
    270 
    271     if (statement.prepare() != SQLResultOk)
    272         return;
    273 
    274     int result;
    275     while ((result = statement.step()) == SQLResultRow) {
    276         RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromDatabaseIdentifier(statement.getColumnText(0));
    277         m_quotaMap->set(origin.get(), statement.getColumnInt64(1));
    278     }
    279 
    280     if (result != SQLResultDone)
    281         LOG_ERROR("Failed to read in all origins from the database");
    282 }
    283 
    284 void DatabaseTracker::origins(Vector<RefPtr<SecurityOrigin> >& result)
    285 {
    286     ASSERT(currentThread() == m_thread);
    287     populateOrigins();
    288     MutexLocker lockQuotaMap(m_quotaMapGuard);
    289     copyKeysToVector(*m_quotaMap, result);
    290 }
    291 
    292 bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& resultVector)
    293 {
    294     ASSERT(currentThread() == m_thread);
    295     openTrackerDatabase(false);
    296     if (!m_database.isOpen())
    297         return false;
    298 
    299     SQLiteStatement statement(m_database, "SELECT name FROM Databases where origin=?;");
    300 
    301     if (statement.prepare() != SQLResultOk)
    302         return false;
    303 
    304     statement.bindText(1, origin->databaseIdentifier());
    305 
    306     int result;
    307     while ((result = statement.step()) == SQLResultRow)
    308         resultVector.append(statement.getColumnText(0));
    309 
    310     if (result != SQLResultDone) {
    311         LOG_ERROR("Failed to retrieve all database names for origin %s", origin->databaseIdentifier().ascii().data());
    312         return false;
    313     }
    314 
    315     return true;
    316 }
    317 
    318 DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin)
    319 {
    320     ASSERT(currentThread() == m_thread);
    321 
    322     if (m_proposedDatabase && m_proposedDatabase->first == origin && m_proposedDatabase->second.name() == name)
    323         return m_proposedDatabase->second;
    324 
    325     String originIdentifier = origin->databaseIdentifier();
    326 
    327     openTrackerDatabase(false);
    328     if (!m_database.isOpen())
    329         return DatabaseDetails();
    330     SQLiteStatement statement(m_database, "SELECT displayName, estimatedSize FROM Databases WHERE origin=? AND name=?");
    331     if (statement.prepare() != SQLResultOk)
    332         return DatabaseDetails();
    333 
    334     statement.bindText(1, originIdentifier);
    335     statement.bindText(2, name);
    336 
    337     int result = statement.step();
    338     if (result == SQLResultDone)
    339         return DatabaseDetails();
    340 
    341     if (result != SQLResultRow) {
    342         LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.ascii().data(), originIdentifier.ascii().data());
    343         return DatabaseDetails();
    344     }
    345 
    346     return DatabaseDetails(name, statement.getColumnText(0), statement.getColumnInt64(1), usageForDatabase(name, origin));
    347 }
    348 
    349 void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize)
    350 {
    351     ASSERT(currentThread() == m_thread);
    352 
    353     String originIdentifier = origin->databaseIdentifier();
    354     int64_t guid = 0;
    355 
    356     openTrackerDatabase(true);
    357     if (!m_database.isOpen())
    358         return;
    359     SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?");
    360     if (statement.prepare() != SQLResultOk)
    361         return;
    362 
    363     statement.bindText(1, originIdentifier);
    364     statement.bindText(2, name);
    365 
    366     int result = statement.step();
    367     if (result == SQLResultRow)
    368         guid = statement.getColumnInt64(0);
    369     statement.finalize();
    370 
    371     if (guid == 0) {
    372         if (result != SQLResultDone)
    373             LOG_ERROR("Error to determing existence of database %s in origin %s in tracker database", name.ascii().data(), originIdentifier.ascii().data());
    374         else {
    375             // This case should never occur - we should never be setting database details for a database that doesn't already exist in the tracker
    376             // 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
    377             // So we'll print an error instead
    378             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",
    379                        name.ascii().data(), originIdentifier.ascii().data());
    380         }
    381         return;
    382     }
    383 
    384     SQLiteStatement updateStatement(m_database, "UPDATE Databases SET displayName=?, estimatedSize=? WHERE guid=?");
    385     if (updateStatement.prepare() != SQLResultOk)
    386         return;
    387 
    388     updateStatement.bindText(1, displayName);
    389     updateStatement.bindInt64(2, estimatedSize);
    390     updateStatement.bindInt64(3, guid);
    391 
    392     if (updateStatement.step() != SQLResultDone) {
    393         LOG_ERROR("Failed to update details for database %s in origin %s", name.ascii().data(), originIdentifier.ascii().data());
    394         return;
    395     }
    396 
    397     if (m_client)
    398         m_client->dispatchDidModifyDatabase(origin, name);
    399 }
    400 
    401 unsigned long long DatabaseTracker::usageForDatabase(const String& name, SecurityOrigin* origin)
    402 {
    403     ASSERT(currentThread() == m_thread);
    404     String path = fullPathForDatabase(origin, name, false);
    405     if (path.isEmpty())
    406         return 0;
    407 
    408     return SQLiteFileSystem::getDatabaseFileSize(path);
    409 }
    410 
    411 void DatabaseTracker::addOpenDatabase(Database* database)
    412 {
    413     if (!database)
    414         return;
    415 
    416     MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
    417 
    418     if (!m_openDatabaseMap)
    419         m_openDatabaseMap.set(new DatabaseOriginMap);
    420 
    421     String name(database->stringIdentifier());
    422     DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin());
    423     if (!nameMap) {
    424         nameMap = new DatabaseNameMap;
    425         m_openDatabaseMap->set(database->securityOrigin(), nameMap);
    426     }
    427 
    428     DatabaseSet* databaseSet = nameMap->get(name);
    429     if (!databaseSet) {
    430         databaseSet = new DatabaseSet;
    431         nameMap->set(name, databaseSet);
    432     }
    433 
    434     databaseSet->add(database);
    435 
    436     LOG(StorageAPI, "Added open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database);
    437 }
    438 
    439 void DatabaseTracker::removeOpenDatabase(Database* database)
    440 {
    441     if (!database)
    442         return;
    443 
    444     MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
    445 
    446     if (!m_openDatabaseMap) {
    447         ASSERT_NOT_REACHED();
    448         return;
    449     }
    450 
    451     String name(database->stringIdentifier());
    452     DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin());
    453     if (!nameMap) {
    454         ASSERT_NOT_REACHED();
    455         return;
    456     }
    457 
    458     DatabaseSet* databaseSet = nameMap->get(name);
    459     if (!databaseSet) {
    460         ASSERT_NOT_REACHED();
    461         return;
    462     }
    463 
    464     databaseSet->remove(database);
    465 
    466     LOG(StorageAPI, "Removed open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database);
    467 
    468     if (!databaseSet->isEmpty())
    469         return;
    470 
    471     nameMap->remove(name);
    472     delete databaseSet;
    473 
    474     if (!nameMap->isEmpty())
    475         return;
    476 
    477     m_openDatabaseMap->remove(database->securityOrigin());
    478     delete nameMap;
    479 }
    480 
    481 void DatabaseTracker::getOpenDatabases(SecurityOrigin* origin, const String& name, HashSet<RefPtr<Database> >* databases)
    482 {
    483     MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
    484     if (!m_openDatabaseMap)
    485         return;
    486 
    487     DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin);
    488     if (!nameMap)
    489         return;
    490 
    491     DatabaseSet* databaseSet = nameMap->get(name);
    492     if (!databaseSet)
    493         return;
    494 
    495     for (DatabaseSet::iterator it = databaseSet->begin(); it != databaseSet->end(); ++it)
    496         databases->add(*it);
    497 }
    498 
    499 unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin)
    500 {
    501     ASSERT(currentThread() == m_thread);
    502     Locker<OriginQuotaManager> locker(originQuotaManager());
    503 
    504     // Use the OriginQuotaManager mechanism to calculate the usage
    505     if (originQuotaManager().tracksOrigin(origin))
    506         return originQuotaManager().diskUsage(origin);
    507 
    508     // If the OriginQuotaManager doesn't track this origin already, prime it to do so
    509     originQuotaManager().trackOrigin(origin);
    510 
    511     Vector<String> names;
    512     databaseNamesForOrigin(origin, names);
    513 
    514     for (unsigned i = 0; i < names.size(); ++i)
    515         originQuotaManager().addDatabase(origin, names[i], fullPathForDatabase(origin, names[i], false));
    516 
    517     if (!originQuotaManager().tracksOrigin(origin))
    518         return 0;
    519     return originQuotaManager().diskUsage(origin);
    520 }
    521 
    522 unsigned long long DatabaseTracker::quotaForOrigin(SecurityOrigin* origin)
    523 {
    524     ASSERT(currentThread() == m_thread || m_quotaMap);
    525     populateOrigins();
    526     MutexLocker lockQuotaMap(m_quotaMapGuard);
    527     return m_quotaMap->get(origin);
    528 }
    529 
    530 void DatabaseTracker::setQuota(SecurityOrigin* origin, unsigned long long quota)
    531 {
    532     ASSERT(currentThread() == m_thread);
    533     if (quotaForOrigin(origin) == quota)
    534         return;
    535 
    536     openTrackerDatabase(true);
    537     if (!m_database.isOpen())
    538         return;
    539 
    540     {
    541         MutexLocker lockQuotaMap(m_quotaMapGuard);
    542 
    543         if (!m_quotaMap->contains(origin)) {
    544             SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)");
    545             if (statement.prepare() != SQLResultOk) {
    546                 LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
    547             } else {
    548                 statement.bindText(1, origin->databaseIdentifier());
    549                 statement.bindInt64(2, quota);
    550 
    551                 if (statement.step() != SQLResultDone)
    552                     LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
    553             }
    554         } else {
    555             SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?");
    556             bool error = statement.prepare() != SQLResultOk;
    557             if (!error) {
    558                 statement.bindInt64(1, quota);
    559                 statement.bindText(2, origin->databaseIdentifier());
    560 
    561                 error = !statement.executeCommand();
    562             }
    563 
    564             if (error)
    565                 LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data());
    566         }
    567 
    568         // FIXME: Is it really OK to update the quota in memory if we failed to update it on disk?
    569         m_quotaMap->set(origin, quota);
    570     }
    571 
    572     if (m_client)
    573         m_client->dispatchDidModifyOrigin(origin);
    574 }
    575 
    576 bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, const String& path)
    577 {
    578     ASSERT(currentThread() == m_thread);
    579     openTrackerDatabase(true);
    580     if (!m_database.isOpen())
    581         return false;
    582 
    583     // New database should never be added until the origin has been established
    584     ASSERT(hasEntryForOrigin(origin));
    585 
    586     SQLiteStatement statement(m_database, "INSERT INTO Databases (origin, name, path) VALUES (?, ?, ?);");
    587 
    588     if (statement.prepare() != SQLResultOk)
    589         return false;
    590 
    591     statement.bindText(1, origin->databaseIdentifier());
    592     statement.bindText(2, name);
    593     statement.bindText(3, path);
    594 
    595     if (!statement.executeCommand()) {
    596         LOG_ERROR("Failed to add database %s to origin %s: %s\n", name.ascii().data(), origin->databaseIdentifier().ascii().data(), m_database.lastErrorMsg());
    597         return false;
    598     }
    599 
    600     if (m_client)
    601         m_client->dispatchDidModifyOrigin(origin);
    602 
    603     return true;
    604 }
    605 
    606 void DatabaseTracker::deleteAllDatabases()
    607 {
    608     ASSERT(currentThread() == m_thread);
    609 
    610     Vector<RefPtr<SecurityOrigin> > originsCopy;
    611     origins(originsCopy);
    612 
    613     for (unsigned i = 0; i < originsCopy.size(); ++i)
    614         deleteOrigin(originsCopy[i].get());
    615 }
    616 
    617 void DatabaseTracker::deleteOrigin(SecurityOrigin* origin)
    618 {
    619     ASSERT(currentThread() == m_thread);
    620     openTrackerDatabase(false);
    621     if (!m_database.isOpen())
    622         return;
    623 
    624     Vector<String> databaseNames;
    625     if (!databaseNamesForOrigin(origin, databaseNames)) {
    626         LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data());
    627         return;
    628     }
    629 
    630     for (unsigned i = 0; i < databaseNames.size(); ++i) {
    631         if (!deleteDatabaseFile(origin, databaseNames[i])) {
    632             // Even if the file can't be deleted, we want to try and delete the rest, don't return early here.
    633             LOG_ERROR("Unable to delete file for database %s in origin %s", databaseNames[i].ascii().data(), origin->databaseIdentifier().ascii().data());
    634         }
    635     }
    636 
    637     SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?");
    638     if (statement.prepare() != SQLResultOk) {
    639         LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
    640         return;
    641     }
    642 
    643     statement.bindText(1, origin->databaseIdentifier());
    644 
    645     if (!statement.executeCommand()) {
    646         LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
    647         return;
    648     }
    649 
    650     SQLiteStatement originStatement(m_database, "DELETE FROM Origins WHERE origin=?");
    651     if (originStatement.prepare() != SQLResultOk) {
    652         LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin->databaseIdentifier().ascii().data());
    653         return;
    654     }
    655 
    656     originStatement.bindText(1, origin->databaseIdentifier());
    657 
    658     if (!originStatement.executeCommand()) {
    659         LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
    660         return;
    661     }
    662 
    663     SQLiteFileSystem::deleteEmptyDatabaseDirectory(originPath(origin));
    664 
    665     RefPtr<SecurityOrigin> originPossiblyLastReference = origin;
    666     {
    667         MutexLocker lockQuotaMap(m_quotaMapGuard);
    668         m_quotaMap->remove(origin);
    669 
    670         Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
    671         originQuotaManager().removeOrigin(origin);
    672 
    673         // If we removed the last origin, do some additional deletion.
    674         if (m_quotaMap->isEmpty()) {
    675             if (m_database.isOpen())
    676                 m_database.close();
    677            SQLiteFileSystem::deleteDatabaseFile(trackerDatabasePath());
    678            SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_databaseDirectoryPath);
    679         }
    680     }
    681 
    682     if (m_client) {
    683         m_client->dispatchDidModifyOrigin(origin);
    684         for (unsigned i = 0; i < databaseNames.size(); ++i)
    685             m_client->dispatchDidModifyDatabase(origin, databaseNames[i]);
    686     }
    687 }
    688 
    689 void DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name)
    690 {
    691     ASSERT(currentThread() == m_thread);
    692     openTrackerDatabase(false);
    693     if (!m_database.isOpen())
    694         return;
    695 
    696     if (!deleteDatabaseFile(origin, name)) {
    697         LOG_ERROR("Unable to delete file for database %s in origin %s", name.ascii().data(), origin->databaseIdentifier().ascii().data());
    698         return;
    699     }
    700 
    701     SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=? AND name=?");
    702     if (statement.prepare() != SQLResultOk) {
    703         LOG_ERROR("Unable to prepare deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data());
    704         return;
    705     }
    706 
    707     statement.bindText(1, origin->databaseIdentifier());
    708     statement.bindText(2, name);
    709 
    710     if (!statement.executeCommand()) {
    711         LOG_ERROR("Unable to execute deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data());
    712         return;
    713     }
    714 
    715     {
    716         Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
    717         originQuotaManager().removeDatabase(origin, name);
    718     }
    719 
    720     if (m_client) {
    721         m_client->dispatchDidModifyOrigin(origin);
    722         m_client->dispatchDidModifyDatabase(origin, name);
    723     }
    724 }
    725 
    726 bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& name)
    727 {
    728     ASSERT(currentThread() == m_thread);
    729     String fullPath = fullPathForDatabase(origin, name, false);
    730     if (fullPath.isEmpty())
    731         return true;
    732 
    733     Vector<RefPtr<Database> > deletedDatabases;
    734 
    735     // Make sure not to hold the m_openDatabaseMapGuard mutex when calling
    736     // Database::markAsDeletedAndClose(), since that can cause a deadlock
    737     // during the synchronous DatabaseThread call it triggers.
    738 
    739     {
    740         MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
    741         if (m_openDatabaseMap) {
    742             // There are some open databases, lets check if they are for this origin.
    743             DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin);
    744             if (nameMap && nameMap->size()) {
    745                 // There are some open databases for this origin, lets check
    746                 // if they are this database by name.
    747                 DatabaseSet* databaseSet = nameMap->get(name);
    748                 if (databaseSet && databaseSet->size()) {
    749                     // We have some database open with this name. Mark them as deleted.
    750                     DatabaseSet::const_iterator end = databaseSet->end();
    751                     for (DatabaseSet::const_iterator it = databaseSet->begin(); it != end; ++it)
    752                         deletedDatabases.append(*it);
    753                 }
    754             }
    755         }
    756     }
    757 
    758     for (unsigned i = 0; i < deletedDatabases.size(); ++i)
    759         deletedDatabases[i]->markAsDeletedAndClose();
    760 
    761     return SQLiteFileSystem::deleteDatabaseFile(fullPath);
    762 }
    763 
    764 void DatabaseTracker::setClient(DatabaseTrackerClient* client)
    765 {
    766     ASSERT(currentThread() == m_thread);
    767     m_client = client;
    768 }
    769 
    770 static Mutex& notificationMutex()
    771 {
    772     DEFINE_STATIC_LOCAL(Mutex, mutex, ());
    773     return mutex;
    774 }
    775 
    776 typedef Vector<pair<SecurityOrigin*, String> > NotificationQueue;
    777 
    778 static NotificationQueue& notificationQueue()
    779 {
    780     DEFINE_STATIC_LOCAL(NotificationQueue, queue, ());
    781     return queue;
    782 }
    783 
    784 void DatabaseTracker::scheduleNotifyDatabaseChanged(SecurityOrigin* origin, const String& name)
    785 {
    786     MutexLocker locker(notificationMutex());
    787 
    788     notificationQueue().append(pair<SecurityOrigin*, String>(origin, name.crossThreadString()));
    789     scheduleForNotification();
    790 }
    791 
    792 static bool notificationScheduled = false;
    793 
    794 void DatabaseTracker::scheduleForNotification()
    795 {
    796     ASSERT(!notificationMutex().tryLock());
    797 
    798     if (!notificationScheduled) {
    799         callOnMainThread(DatabaseTracker::notifyDatabasesChanged, 0);
    800         notificationScheduled = true;
    801     }
    802 }
    803 
    804 void DatabaseTracker::notifyDatabasesChanged(void*)
    805 {
    806     // Note that if DatabaseTracker ever becomes non-singleton, we'll have to amend this notification
    807     // mechanism to include which tracker the notification goes out on as well.
    808     DatabaseTracker& theTracker(tracker());
    809 
    810     NotificationQueue notifications;
    811     {
    812         MutexLocker locker(notificationMutex());
    813 
    814         notifications.swap(notificationQueue());
    815 
    816         notificationScheduled = false;
    817     }
    818 
    819     if (!theTracker.m_client)
    820         return;
    821 
    822     for (unsigned i = 0; i < notifications.size(); ++i)
    823         theTracker.m_client->dispatchDidModifyDatabase(notifications[i].first, notifications[i].second);
    824 }
    825 
    826 
    827 } // namespace WebCore
    828 #endif
    829