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