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