WebCore: Adding a way to get the set of all open database handles pointing
[WebKit-https.git] / WebCore / storage / DatabaseTracker.cpp
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