bed83a7e7c70df691c7a4bafa0f999f6ce20d202
[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 unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin)
482 {
483     ASSERT(currentThread() == m_thread);
484     Locker<OriginQuotaManager> locker(originQuotaManager());
485
486     // Use the OriginQuotaManager mechanism to calculate the usage
487     if (originQuotaManager().tracksOrigin(origin))
488         return originQuotaManager().diskUsage(origin);
489
490     // If the OriginQuotaManager doesn't track this origin already, prime it to do so
491     originQuotaManager().trackOrigin(origin);
492
493     Vector<String> names;
494     databaseNamesForOrigin(origin, names);
495
496     for (unsigned i = 0; i < names.size(); ++i)
497         originQuotaManager().addDatabase(origin, names[i], fullPathForDatabase(origin, names[i], false));
498
499     if (!originQuotaManager().tracksOrigin(origin))
500         return 0;
501     return originQuotaManager().diskUsage(origin);
502 }
503
504 unsigned long long DatabaseTracker::quotaForOrigin(SecurityOrigin* origin)
505 {
506     ASSERT(currentThread() == m_thread || m_quotaMap);
507     populateOrigins();
508     MutexLocker lockQuotaMap(m_quotaMapGuard);
509     return m_quotaMap->get(origin);
510 }
511
512 void DatabaseTracker::setQuota(SecurityOrigin* origin, unsigned long long quota)
513 {
514     ASSERT(currentThread() == m_thread);
515     if (quotaForOrigin(origin) == quota)
516         return;
517
518     openTrackerDatabase(true);
519     if (!m_database.isOpen())
520         return;
521
522     {
523         MutexLocker lockQuotaMap(m_quotaMapGuard);
524
525         if (!m_quotaMap->contains(origin)) {
526             SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)");
527             if (statement.prepare() != SQLResultOk) {
528                 LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
529             } else {
530                 statement.bindText(1, origin->databaseIdentifier());
531                 statement.bindInt64(2, quota);
532
533                 if (statement.step() != SQLResultDone)
534                     LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
535             }
536         } else {
537             SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?");
538             bool error = statement.prepare() != SQLResultOk;
539             if (!error) {
540                 statement.bindInt64(1, quota);
541                 statement.bindText(2, origin->databaseIdentifier());
542
543                 error = !statement.executeCommand();
544             }
545
546             if (error)
547                 LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data());
548         }
549
550         // FIXME: Is it really OK to update the quota in memory if we failed to update it on disk?
551         m_quotaMap->set(origin, quota);
552     }
553
554     if (m_client)
555         m_client->dispatchDidModifyOrigin(origin);
556 }
557
558 bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, const String& path)
559 {
560     ASSERT(currentThread() == m_thread);
561     openTrackerDatabase(true);
562     if (!m_database.isOpen())
563         return false;
564
565     // New database should never be added until the origin has been established
566     ASSERT(hasEntryForOrigin(origin));
567
568     SQLiteStatement statement(m_database, "INSERT INTO Databases (origin, name, path) VALUES (?, ?, ?);");
569
570     if (statement.prepare() != SQLResultOk)
571         return false;
572
573     statement.bindText(1, origin->databaseIdentifier());
574     statement.bindText(2, name);
575     statement.bindText(3, path);
576
577     if (!statement.executeCommand()) {
578         LOG_ERROR("Failed to add database %s to origin %s: %s\n", name.ascii().data(), origin->databaseIdentifier().ascii().data(), m_database.lastErrorMsg());
579         return false;
580     }
581
582     if (m_client)
583         m_client->dispatchDidModifyOrigin(origin);
584
585     return true;
586 }
587
588 void DatabaseTracker::deleteAllDatabases()
589 {
590     ASSERT(currentThread() == m_thread);
591
592     Vector<RefPtr<SecurityOrigin> > originsCopy;
593     origins(originsCopy);
594
595     for (unsigned i = 0; i < originsCopy.size(); ++i)
596         deleteOrigin(originsCopy[i].get());
597 }
598
599 void DatabaseTracker::deleteOrigin(SecurityOrigin* origin)
600 {
601     ASSERT(currentThread() == m_thread);
602     openTrackerDatabase(false);
603     if (!m_database.isOpen())
604         return;
605
606     Vector<String> databaseNames;
607     if (!databaseNamesForOrigin(origin, databaseNames)) {
608         LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data());
609         return;
610     }
611
612     for (unsigned i = 0; i < databaseNames.size(); ++i) {
613         if (!deleteDatabaseFile(origin, databaseNames[i])) {
614             // Even if the file can't be deleted, we want to try and delete the rest, don't return early here.
615             LOG_ERROR("Unable to delete file for database %s in origin %s", databaseNames[i].ascii().data(), origin->databaseIdentifier().ascii().data());
616         }
617     }
618
619     SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?");
620     if (statement.prepare() != SQLResultOk) {
621         LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
622         return;
623     }
624
625     statement.bindText(1, origin->databaseIdentifier());
626
627     if (!statement.executeCommand()) {
628         LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
629         return;
630     }
631
632     SQLiteStatement originStatement(m_database, "DELETE FROM Origins WHERE origin=?");
633     if (originStatement.prepare() != SQLResultOk) {
634         LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin->databaseIdentifier().ascii().data());
635         return;
636     }
637
638     originStatement.bindText(1, origin->databaseIdentifier());
639
640     if (!originStatement.executeCommand()) {
641         LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
642         return;
643     }
644
645     SQLiteFileSystem::deleteEmptyDatabaseDirectory(originPath(origin));
646
647     RefPtr<SecurityOrigin> originPossiblyLastReference = origin;
648     {
649         MutexLocker lockQuotaMap(m_quotaMapGuard);
650         m_quotaMap->remove(origin);
651
652         Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
653         originQuotaManager().removeOrigin(origin);
654
655         // If we removed the last origin, do some additional deletion.
656         if (m_quotaMap->isEmpty()) {
657             if (m_database.isOpen())
658                 m_database.close();
659            SQLiteFileSystem::deleteDatabaseFile(trackerDatabasePath());
660            SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_databaseDirectoryPath);
661         }
662     }
663
664     if (m_client) {
665         m_client->dispatchDidModifyOrigin(origin);
666         for (unsigned i = 0; i < databaseNames.size(); ++i)
667             m_client->dispatchDidModifyDatabase(origin, databaseNames[i]);
668     }
669 }
670
671 void DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name)
672 {
673     ASSERT(currentThread() == m_thread);
674     openTrackerDatabase(false);
675     if (!m_database.isOpen())
676         return;
677
678     if (!deleteDatabaseFile(origin, name)) {
679         LOG_ERROR("Unable to delete file for database %s in origin %s", name.ascii().data(), origin->databaseIdentifier().ascii().data());
680         return;
681     }
682
683     SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=? AND name=?");
684     if (statement.prepare() != SQLResultOk) {
685         LOG_ERROR("Unable to prepare deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data());
686         return;
687     }
688
689     statement.bindText(1, origin->databaseIdentifier());
690     statement.bindText(2, name);
691
692     if (!statement.executeCommand()) {
693         LOG_ERROR("Unable to execute deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data());
694         return;
695     }
696
697     {
698         Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
699         originQuotaManager().removeDatabase(origin, name);
700     }
701
702     if (m_client) {
703         m_client->dispatchDidModifyOrigin(origin);
704         m_client->dispatchDidModifyDatabase(origin, name);
705     }
706 }
707
708 bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& name)
709 {
710     ASSERT(currentThread() == m_thread);
711     String fullPath = fullPathForDatabase(origin, name, false);
712     if (fullPath.isEmpty())
713         return true;
714
715     Vector<RefPtr<Database> > deletedDatabases;
716
717     // Make sure not to hold the m_openDatabaseMapGuard mutex when calling
718     // Database::markAsDeletedAndClose(), since that can cause a deadlock
719     // during the synchronous DatabaseThread call it triggers.
720
721     {
722         MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
723         if (m_openDatabaseMap) {
724             // There are some open databases, lets check if they are for this origin.
725             DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin);
726             if (nameMap && nameMap->size()) {
727                 // There are some open databases for this origin, lets check
728                 // if they are this database by name.
729                 DatabaseSet* databaseSet = nameMap->get(name);
730                 if (databaseSet && databaseSet->size()) {
731                     // We have some database open with this name. Mark them as deleted.
732                     DatabaseSet::const_iterator end = databaseSet->end();
733                     for (DatabaseSet::const_iterator it = databaseSet->begin(); it != end; ++it)
734                         deletedDatabases.append(*it);
735                 }
736             }
737         }
738     }
739
740     for (unsigned i = 0; i < deletedDatabases.size(); ++i)
741         deletedDatabases[i]->markAsDeletedAndClose();
742
743     return SQLiteFileSystem::deleteDatabaseFile(fullPath);
744 }
745
746 void DatabaseTracker::setClient(DatabaseTrackerClient* client)
747 {
748     ASSERT(currentThread() == m_thread);
749     m_client = client;
750 }
751
752 static Mutex& notificationMutex()
753 {
754     DEFINE_STATIC_LOCAL(Mutex, mutex, ());
755     return mutex;
756 }
757
758 typedef Vector<pair<SecurityOrigin*, String> > NotificationQueue;
759
760 static NotificationQueue& notificationQueue()
761 {
762     DEFINE_STATIC_LOCAL(NotificationQueue, queue, ());
763     return queue;
764 }
765
766 void DatabaseTracker::scheduleNotifyDatabaseChanged(SecurityOrigin* origin, const String& name)
767 {
768     MutexLocker locker(notificationMutex());
769
770     notificationQueue().append(pair<SecurityOrigin*, String>(origin, name.crossThreadString()));
771     scheduleForNotification();
772 }
773
774 static bool notificationScheduled = false;
775
776 void DatabaseTracker::scheduleForNotification()
777 {
778     ASSERT(!notificationMutex().tryLock());
779
780     if (!notificationScheduled) {
781         callOnMainThread(DatabaseTracker::notifyDatabasesChanged, 0);
782         notificationScheduled = true;
783     }
784 }
785
786 void DatabaseTracker::notifyDatabasesChanged(void*)
787 {
788     // Note that if DatabaseTracker ever becomes non-singleton, we'll have to amend this notification
789     // mechanism to include which tracker the notification goes out on as well.
790     DatabaseTracker& theTracker(tracker());
791
792     NotificationQueue notifications;
793     {
794         MutexLocker locker(notificationMutex());
795
796         notifications.swap(notificationQueue());
797
798         notificationScheduled = false;
799     }
800
801     if (!theTracker.m_client)
802         return;
803
804     for (unsigned i = 0; i < notifications.size(); ++i)
805         theTracker.m_client->dispatchDidModifyDatabase(notifications[i].first, notifications[i].second);
806 }
807
808
809 } // namespace WebCore
810 #endif