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