1/* 2 * Copyright (C) 2007, 2008, 2012, 2013 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 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 "Chrome.h" 35#include "ChromeClient.h" 36#include "Database.h" 37#include "DatabaseBackendBase.h" 38#include "DatabaseBackendContext.h" 39#include "DatabaseManager.h" 40#include "DatabaseManagerClient.h" 41#include "DatabaseThread.h" 42#include "FileSystem.h" 43#include "Logging.h" 44#include "OriginLock.h" 45#include "Page.h" 46#include "SecurityOrigin.h" 47#include "SecurityOriginHash.h" 48#include "SQLiteFileSystem.h" 49#include "SQLiteStatement.h" 50#include <wtf/MainThread.h> 51#include <wtf/NeverDestroyed.h> 52#include <wtf/StdLibExtras.h> 53#include <wtf/text/CString.h> 54 55#if PLATFORM(IOS) 56#include "WebCoreThread.h" 57#endif 58 59namespace WebCore { 60 61static DatabaseTracker* staticTracker = 0; 62 63void DatabaseTracker::initializeTracker(const String& databasePath) 64{ 65 ASSERT(!staticTracker); 66 if (staticTracker) 67 return; 68 69 staticTracker = new DatabaseTracker(databasePath); 70} 71 72DatabaseTracker& DatabaseTracker::tracker() 73{ 74 if (!staticTracker) 75 staticTracker = new DatabaseTracker(""); 76 77 return *staticTracker; 78} 79 80DatabaseTracker::DatabaseTracker(const String& databasePath) 81 : m_client(0) 82{ 83 setDatabaseDirectoryPath(databasePath); 84} 85 86void DatabaseTracker::setDatabaseDirectoryPath(const String& path) 87{ 88 MutexLocker lockDatabase(m_databaseGuard); 89 ASSERT(!m_database.isOpen()); 90 m_databaseDirectoryPath = path.isolatedCopy(); 91} 92 93String DatabaseTracker::databaseDirectoryPath() const 94{ 95 return m_databaseDirectoryPath.isolatedCopy(); 96} 97 98String DatabaseTracker::trackerDatabasePath() const 99{ 100 return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath.isolatedCopy(), "Databases.db"); 101} 102 103void DatabaseTracker::openTrackerDatabase(TrackerCreationAction createAction) 104{ 105 ASSERT(!m_databaseGuard.tryLock()); 106 107 if (m_database.isOpen()) 108 return; 109 110 // If createIfDoesNotExist is false, SQLiteFileSystem::ensureDatabaseFileExists() 111 // will return false if the database file does not exist. 112 // If createIfDoesNotExist is true, SQLiteFileSystem::ensureDatabaseFileExists() 113 // will attempt to create the path to the database file if it does not 114 // exists yet. It'll return true if the path already exists, or if it 115 // successfully creates the path. Else, it will return false. 116 String databasePath = trackerDatabasePath(); 117 if (!SQLiteFileSystem::ensureDatabaseFileExists(databasePath, createAction == 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 127 if (!m_database.tableExists("Origins")) { 128 if (!m_database.executeCommand("CREATE TABLE Origins (origin TEXT UNIQUE ON CONFLICT REPLACE, quota INTEGER NOT NULL ON CONFLICT FAIL);")) { 129 // FIXME: and here 130 LOG_ERROR("Failed to create Origins table"); 131 } 132 } 133 134 if (!m_database.tableExists("Databases")) { 135 if (!m_database.executeCommand("CREATE TABLE Databases (guid INTEGER PRIMARY KEY AUTOINCREMENT, origin TEXT, name TEXT, displayName TEXT, estimatedSize INTEGER, path TEXT);")) { 136 // FIXME: and here 137 LOG_ERROR("Failed to create Databases table"); 138 } 139 } 140} 141 142bool DatabaseTracker::hasAdequateQuotaForOrigin(SecurityOrigin* origin, unsigned long estimatedSize, DatabaseError& err) 143{ 144 ASSERT(!m_databaseGuard.tryLock()); 145 unsigned long long usage = usageForOrigin(origin); 146 147 // If the database will fit, allow its creation. 148 unsigned long long requirement = usage + std::max<unsigned long long>(1, estimatedSize); 149 if (requirement < usage) { 150 // The estimated size is so big it causes an overflow; don't allow creation. 151 err = DatabaseError::DatabaseSizeOverflowed; 152 return false; 153 } 154 if (requirement <= quotaForOriginNoLock(origin)) 155 return true; 156 157 err = DatabaseError::DatabaseSizeExceededQuota; 158 return false; 159} 160 161bool DatabaseTracker::canEstablishDatabase(DatabaseBackendContext* context, const String& name, unsigned long estimatedSize, DatabaseError& error) 162{ 163 error = DatabaseError::None; 164 165 MutexLocker lockDatabase(m_databaseGuard); 166 SecurityOrigin* origin = context->securityOrigin(); 167 168 if (isDeletingDatabaseOrOriginFor(origin, name)) { 169 error = DatabaseError::DatabaseIsBeingDeleted; 170 return false; 171 } 172 173 recordCreatingDatabase(origin, name); 174 175 // If a database already exists, ignore the passed-in estimated size and say it's OK. 176 if (hasEntryForDatabase(origin, name)) 177 return true; 178 179 if (hasAdequateQuotaForOrigin(origin, estimatedSize, error)) { 180 ASSERT(error == DatabaseError::None); 181 return true; 182 } 183 184 // If we get here, then we do not have enough quota for one of the 185 // following reasons as indicated by the set error: 186 // 187 // If the error is DatabaseSizeOverflowed, then this means the requested 188 // estimatedSize if so unreasonably large that it can cause an overflow in 189 // the usage budget computation. In that case, there's nothing more we can 190 // do, and there's no need for a retry. Hence, we should indicate that 191 // we're done with our attempt to create the database. 192 // 193 // If the error is DatabaseSizeExceededQuota, then we'll give the client 194 // a chance to update the quota and call retryCanEstablishDatabase() to try 195 // again. Hence, we don't call doneCreatingDatabase() yet in that case. 196 197 if (error == DatabaseError::DatabaseSizeOverflowed) 198 doneCreatingDatabase(origin, name); 199 else 200 ASSERT(error == DatabaseError::DatabaseSizeExceededQuota); 201 202 return false; 203} 204 205// Note: a thought about performance: hasAdequateQuotaForOrigin() was also 206// called in canEstablishDatabase(), and hence, we're repeating some work within 207// hasAdequateQuotaForOrigin(). However, retryCanEstablishDatabase() should only 208// be called in the rare even if canEstablishDatabase() fails. Since it is rare, 209// we should not bother optimizing it. It is more beneficial to keep 210// hasAdequateQuotaForOrigin() simple and correct (i.e. bug free), and just 211// re-use it. Also note that the path for opening a database involves IO, and 212// hence should not be a performance critical path anyway. 213bool DatabaseTracker::retryCanEstablishDatabase(DatabaseBackendContext* context, const String& name, unsigned long estimatedSize, DatabaseError& error) 214{ 215 error = DatabaseError::None; 216 217 MutexLocker lockDatabase(m_databaseGuard); 218 SecurityOrigin* origin = context->securityOrigin(); 219 220 // We have already eliminated other types of errors in canEstablishDatabase(). 221 // The only reason we're in retryCanEstablishDatabase() is because we gave 222 // the client a chance to update the quota and are rechecking it here. 223 // If we fail this check, the only possible reason this time should be due 224 // to inadequate quota. 225 if (hasAdequateQuotaForOrigin(origin, estimatedSize, error)) { 226 ASSERT(error == DatabaseError::None); 227 return true; 228 } 229 230 ASSERT(error == DatabaseError::DatabaseSizeExceededQuota); 231 doneCreatingDatabase(origin, name); 232 233 return false; 234} 235 236bool DatabaseTracker::hasEntryForOriginNoLock(SecurityOrigin* origin) 237{ 238 ASSERT(!m_databaseGuard.tryLock()); 239 openTrackerDatabase(DontCreateIfDoesNotExist); 240 if (!m_database.isOpen()) 241 return false; 242 243 SQLiteStatement statement(m_database, "SELECT origin FROM Origins where origin=?;"); 244 if (statement.prepare() != SQLResultOk) { 245 LOG_ERROR("Failed to prepare statement."); 246 return false; 247 } 248 249 statement.bindText(1, origin->databaseIdentifier()); 250 251 return statement.step() == SQLResultRow; 252} 253 254bool DatabaseTracker::hasEntryForOrigin(SecurityOrigin* origin) 255{ 256 MutexLocker lockDatabase(m_databaseGuard); 257 return hasEntryForOriginNoLock(origin); 258} 259 260bool DatabaseTracker::hasEntryForDatabase(SecurityOrigin* origin, const String& databaseIdentifier) 261{ 262 ASSERT(!m_databaseGuard.tryLock()); 263 openTrackerDatabase(DontCreateIfDoesNotExist); 264 if (!m_database.isOpen()) { 265 // No "tracker database". Hence, no entry for the database of interest. 266 return false; 267 } 268 269 // We've got a tracker database. Set up a query to ask for the db of interest: 270 SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?;"); 271 272 if (statement.prepare() != SQLResultOk) 273 return false; 274 275 statement.bindText(1, origin->databaseIdentifier()); 276 statement.bindText(2, databaseIdentifier); 277 278 return statement.step() == SQLResultRow; 279} 280 281unsigned long long DatabaseTracker::getMaxSizeForDatabase(const DatabaseBackendBase* database) 282{ 283 // The maximum size for a database is the full quota for its origin, minus the current usage within the origin, 284 // plus the current usage of the given database 285 MutexLocker lockDatabase(m_databaseGuard); 286 SecurityOrigin* origin = database->securityOrigin(); 287 288 unsigned long long quota = quotaForOriginNoLock(origin); 289 unsigned long long diskUsage = usageForOrigin(origin); 290 unsigned long long databaseFileSize = SQLiteFileSystem::getDatabaseFileSize(database->fileName()); 291 ASSERT(databaseFileSize <= diskUsage); 292 293 if (diskUsage > quota) 294 return databaseFileSize; 295 296 // A previous error may have allowed the origin to exceed its quota, or may 297 // have allowed this database to exceed our cached estimate of the origin 298 // disk usage. Don't multiply that error through integer underflow, or the 299 // effective quota will permanently become 2^64. 300 unsigned long long maxSize = quota - diskUsage + databaseFileSize; 301 if (maxSize > quota) 302 maxSize = databaseFileSize; 303 return maxSize; 304} 305 306void DatabaseTracker::interruptAllDatabasesForContext(const DatabaseBackendContext* context) 307{ 308 Vector<RefPtr<DatabaseBackendBase>> openDatabases; 309 { 310 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); 311 312 if (!m_openDatabaseMap) 313 return; 314 315 DatabaseNameMap* nameMap = m_openDatabaseMap->get(context->securityOrigin()); 316 if (!nameMap) 317 return; 318 319 DatabaseNameMap::const_iterator dbNameMapEndIt = nameMap->end(); 320 for (DatabaseNameMap::const_iterator dbNameMapIt = nameMap->begin(); dbNameMapIt != dbNameMapEndIt; ++dbNameMapIt) { 321 DatabaseSet* databaseSet = dbNameMapIt->value; 322 DatabaseSet::const_iterator dbSetEndIt = databaseSet->end(); 323 for (DatabaseSet::const_iterator dbSetIt = databaseSet->begin(); dbSetIt != dbSetEndIt; ++dbSetIt) { 324 if ((*dbSetIt)->databaseContext() == context) 325 openDatabases.append(*dbSetIt); 326 } 327 } 328 } 329 330 Vector<RefPtr<DatabaseBackendBase>>::const_iterator openDatabasesEndIt = openDatabases.end(); 331 for (Vector<RefPtr<DatabaseBackendBase>>::const_iterator openDatabasesIt = openDatabases.begin(); openDatabasesIt != openDatabasesEndIt; ++openDatabasesIt) 332 (*openDatabasesIt)->interrupt(); 333} 334 335String DatabaseTracker::originPath(SecurityOrigin* origin) const 336{ 337 return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath.isolatedCopy(), origin->databaseIdentifier()); 338} 339 340String DatabaseTracker::fullPathForDatabaseNoLock(SecurityOrigin* origin, const String& name, bool createIfNotExists) 341{ 342 ASSERT(!m_databaseGuard.tryLock()); 343 344 String originIdentifier = origin->databaseIdentifier(); 345 String originPath = this->originPath(origin); 346 347 // Make sure the path for this SecurityOrigin exists 348 if (createIfNotExists && !SQLiteFileSystem::ensureDatabaseDirectoryExists(originPath)) 349 return String(); 350 351 // See if we have a path for this database yet 352 if (!m_database.isOpen()) 353 return String(); 354 SQLiteStatement statement(m_database, "SELECT path FROM Databases WHERE origin=? AND name=?;"); 355 356 if (statement.prepare() != SQLResultOk) 357 return String(); 358 359 statement.bindText(1, originIdentifier); 360 statement.bindText(2, name); 361 362 int result = statement.step(); 363 364 if (result == SQLResultRow) 365 return SQLiteFileSystem::appendDatabaseFileNameToPath(originPath, statement.getColumnText(0)); 366 if (!createIfNotExists) 367 return String(); 368 369 if (result != SQLResultDone) { 370 LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", originIdentifier.ascii().data(), name.ascii().data()); 371 return String(); 372 } 373 statement.finalize(); 374 375 String fileName = SQLiteFileSystem::getFileNameForNewDatabase(originPath, name, originIdentifier, &m_database); 376 if (!addDatabase(origin, name, fileName)) 377 return String(); 378 379 // If this origin's quota is being tracked (open handle to a database in this origin), add this new database 380 // to the quota manager now 381 String fullFilePath = SQLiteFileSystem::appendDatabaseFileNameToPath(originPath, fileName); 382 383 return fullFilePath; 384} 385 386String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfNotExists) 387{ 388 MutexLocker lockDatabase(m_databaseGuard); 389 return fullPathForDatabaseNoLock(origin, name, createIfNotExists).isolatedCopy(); 390} 391 392void DatabaseTracker::origins(Vector<RefPtr<SecurityOrigin>>& originsResult) 393{ 394 MutexLocker lockDatabase(m_databaseGuard); 395 396 openTrackerDatabase(DontCreateIfDoesNotExist); 397 if (!m_database.isOpen()) 398 return; 399 400 SQLiteStatement statement(m_database, "SELECT origin FROM Origins"); 401 if (statement.prepare() != SQLResultOk) { 402 LOG_ERROR("Failed to prepare statement."); 403 return; 404 } 405 406 int result; 407 while ((result = statement.step()) == SQLResultRow) { 408 RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromDatabaseIdentifier(statement.getColumnText(0)); 409 originsResult.append(origin->isolatedCopy()); 410 } 411 originsResult.shrinkToFit(); 412 413 if (result != SQLResultDone) 414 LOG_ERROR("Failed to read in all origins from the database."); 415} 416 417bool DatabaseTracker::databaseNamesForOriginNoLock(SecurityOrigin* origin, Vector<String>& resultVector) 418{ 419 ASSERT(!m_databaseGuard.tryLock()); 420 openTrackerDatabase(DontCreateIfDoesNotExist); 421 if (!m_database.isOpen()) 422 return false; 423 424 SQLiteStatement statement(m_database, "SELECT name FROM Databases where origin=?;"); 425 426 if (statement.prepare() != SQLResultOk) 427 return false; 428 429 statement.bindText(1, origin->databaseIdentifier()); 430 431 int result; 432 while ((result = statement.step()) == SQLResultRow) 433 resultVector.append(statement.getColumnText(0)); 434 435 if (result != SQLResultDone) { 436 LOG_ERROR("Failed to retrieve all database names for origin %s", origin->databaseIdentifier().ascii().data()); 437 return false; 438 } 439 440 return true; 441} 442 443bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& resultVector) 444{ 445 Vector<String> temp; 446 { 447 MutexLocker lockDatabase(m_databaseGuard); 448 if (!databaseNamesForOriginNoLock(origin, temp)) 449 return false; 450 } 451 452 for (Vector<String>::iterator iter = temp.begin(); iter != temp.end(); ++iter) 453 resultVector.append(iter->isolatedCopy()); 454 return true; 455} 456 457DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin) 458{ 459 String originIdentifier = origin->databaseIdentifier(); 460 String displayName; 461 int64_t expectedUsage; 462 463 { 464 MutexLocker lockDatabase(m_databaseGuard); 465 466 openTrackerDatabase(DontCreateIfDoesNotExist); 467 if (!m_database.isOpen()) 468 return DatabaseDetails(); 469 SQLiteStatement statement(m_database, "SELECT displayName, estimatedSize FROM Databases WHERE origin=? AND name=?"); 470 if (statement.prepare() != SQLResultOk) 471 return DatabaseDetails(); 472 473 statement.bindText(1, originIdentifier); 474 statement.bindText(2, name); 475 476 int result = statement.step(); 477 if (result == SQLResultDone) 478 return DatabaseDetails(); 479 480 if (result != SQLResultRow) { 481 LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.ascii().data(), originIdentifier.ascii().data()); 482 return DatabaseDetails(); 483 } 484 displayName = statement.getColumnText(0); 485 expectedUsage = statement.getColumnInt64(1); 486 } 487 488 String path = fullPathForDatabase(origin, name, false); 489 if (path.isEmpty()) 490 return DatabaseDetails(name, displayName, expectedUsage, 0, 0, 0); 491 return DatabaseDetails(name, displayName, expectedUsage, SQLiteFileSystem::getDatabaseFileSize(path), SQLiteFileSystem::databaseCreationTime(path), SQLiteFileSystem::databaseModificationTime(path)); 492} 493 494void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize) 495{ 496 String originIdentifier = origin->databaseIdentifier(); 497 int64_t guid = 0; 498 499 MutexLocker lockDatabase(m_databaseGuard); 500 501 openTrackerDatabase(CreateIfDoesNotExist); 502 if (!m_database.isOpen()) 503 return; 504 SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?"); 505 if (statement.prepare() != SQLResultOk) 506 return; 507 508 statement.bindText(1, originIdentifier); 509 statement.bindText(2, name); 510 511 int result = statement.step(); 512 if (result == SQLResultRow) 513 guid = statement.getColumnInt64(0); 514 statement.finalize(); 515 516 if (guid == 0) { 517 if (result != SQLResultDone) 518 LOG_ERROR("Error to determing existence of database %s in origin %s in tracker database", name.ascii().data(), originIdentifier.ascii().data()); 519 else { 520 // This case should never occur - we should never be setting database details for a database that doesn't already exist in the tracker 521 // 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 522 // So we'll print an error instead 523 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", 524 name.ascii().data(), originIdentifier.ascii().data()); 525 } 526 return; 527 } 528 529 SQLiteStatement updateStatement(m_database, "UPDATE Databases SET displayName=?, estimatedSize=? WHERE guid=?"); 530 if (updateStatement.prepare() != SQLResultOk) 531 return; 532 533 updateStatement.bindText(1, displayName); 534 updateStatement.bindInt64(2, estimatedSize); 535 updateStatement.bindInt64(3, guid); 536 537 if (updateStatement.step() != SQLResultDone) { 538 LOG_ERROR("Failed to update details for database %s in origin %s", name.ascii().data(), originIdentifier.ascii().data()); 539 return; 540 } 541 542 if (m_client) 543 m_client->dispatchDidModifyDatabase(origin, name); 544} 545 546void DatabaseTracker::doneCreatingDatabase(DatabaseBackendBase* database) 547{ 548 MutexLocker lockDatabase(m_databaseGuard); 549 doneCreatingDatabase(database->securityOrigin(), database->stringIdentifier()); 550} 551 552void DatabaseTracker::addOpenDatabase(DatabaseBackendBase* database) 553{ 554 if (!database) 555 return; 556 557 { 558 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); 559 560 if (!m_openDatabaseMap) 561 m_openDatabaseMap = std::make_unique<DatabaseOriginMap>(); 562 563 String name(database->stringIdentifier()); 564 DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); 565 if (!nameMap) { 566 nameMap = new DatabaseNameMap; 567 m_openDatabaseMap->set(database->securityOrigin()->isolatedCopy(), nameMap); 568 } 569 570 DatabaseSet* databaseSet = nameMap->get(name); 571 if (!databaseSet) { 572 databaseSet = new DatabaseSet; 573 nameMap->set(name.isolatedCopy(), databaseSet); 574 } 575 576 databaseSet->add(database); 577 578 LOG(StorageAPI, "Added open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database); 579 } 580} 581 582void DatabaseTracker::removeOpenDatabase(DatabaseBackendBase* database) 583{ 584 if (!database) 585 return; 586 587 { 588 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); 589 590 if (!m_openDatabaseMap) { 591 ASSERT_NOT_REACHED(); 592 return; 593 } 594 595 String name(database->stringIdentifier()); 596 DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); 597 if (!nameMap) { 598 ASSERT_NOT_REACHED(); 599 return; 600 } 601 602 DatabaseSet* databaseSet = nameMap->get(name); 603 if (!databaseSet) { 604 ASSERT_NOT_REACHED(); 605 return; 606 } 607 608 databaseSet->remove(database); 609 610 LOG(StorageAPI, "Removed open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database); 611 612 if (!databaseSet->isEmpty()) 613 return; 614 615 nameMap->remove(name); 616 delete databaseSet; 617 618 if (!nameMap->isEmpty()) 619 return; 620 621 m_openDatabaseMap->remove(database->securityOrigin()); 622 delete nameMap; 623 } 624} 625 626void DatabaseTracker::getOpenDatabases(SecurityOrigin* origin, const String& name, HashSet<RefPtr<DatabaseBackendBase>>* databases) 627{ 628 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); 629 if (!m_openDatabaseMap) 630 return; 631 632 DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); 633 if (!nameMap) 634 return; 635 636 DatabaseSet* databaseSet = nameMap->get(name); 637 if (!databaseSet) 638 return; 639 640 for (DatabaseSet::iterator it = databaseSet->begin(); it != databaseSet->end(); ++it) 641 databases->add(*it); 642} 643 644PassRefPtr<OriginLock> DatabaseTracker::originLockFor(SecurityOrigin* origin) 645{ 646 MutexLocker lockDatabase(m_databaseGuard); 647 String databaseIdentifier = origin->databaseIdentifier(); 648 649 // The originLockMap is accessed from multiple DatabaseThreads since 650 // different script contexts can be writing to different databases from 651 // the same origin. Hence, the databaseIdentifier key needs to be an 652 // isolated copy. An isolated copy gives us a value whose refCounting is 653 // thread-safe, since our copy is guarded by the m_databaseGuard mutex. 654 databaseIdentifier = databaseIdentifier.isolatedCopy(); 655 656 OriginLockMap::AddResult addResult = 657 m_originLockMap.add(databaseIdentifier, RefPtr<OriginLock>()); 658 if (!addResult.isNewEntry) 659 return addResult.iterator->value; 660 661 String path = originPath(origin); 662 RefPtr<OriginLock> lock = adoptRef(new OriginLock(path)); 663 ASSERT(lock); 664 addResult.iterator->value = lock; 665 666 return lock.release(); 667} 668 669void DatabaseTracker::deleteOriginLockFor(SecurityOrigin* origin) 670{ 671 ASSERT(!m_databaseGuard.tryLock()); 672 673 // There is not always an instance of an OriginLock associated with an origin. 674 // For example, if the OriginLock lock file was created by a previous run of 675 // the browser which has now terminated, and the current browser process 676 // has not executed any database transactions from this origin that would 677 // have created the OriginLock instance in memory. In this case, we will have 678 // a lock file but not an OriginLock instance in memory. 679 680 // This function is only called if we are already deleting all the database 681 // files in this origin. We'll give the OriginLock one chance to do an 682 // orderly clean up first when we remove its ref from the m_originLockMap. 683 // This may or may not be possible depending on whether other threads are 684 // also using the OriginLock at the same time. After that, we will go ahead 685 // and delete the lock file. 686 687 m_originLockMap.remove(origin->databaseIdentifier()); 688 OriginLock::deleteLockFile(originPath(origin)); 689} 690 691unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin) 692{ 693 String originPath = this->originPath(origin); 694 unsigned long long diskUsage = 0; 695 Vector<String> fileNames = listDirectory(originPath, String("*.db")); 696 Vector<String>::iterator fileName = fileNames.begin(); 697 Vector<String>::iterator lastFileName = fileNames.end(); 698 for (; fileName != lastFileName; ++fileName) { 699 long long size; 700 getFileSize(*fileName, size); 701 diskUsage += size; 702 } 703 return diskUsage; 704} 705 706unsigned long long DatabaseTracker::quotaForOriginNoLock(SecurityOrigin* origin) 707{ 708 ASSERT(!m_databaseGuard.tryLock()); 709 unsigned long long quota = 0; 710 711 openTrackerDatabase(DontCreateIfDoesNotExist); 712 if (!m_database.isOpen()) 713 return quota; 714 715 SQLiteStatement statement(m_database, "SELECT quota FROM Origins where origin=?;"); 716 if (statement.prepare() != SQLResultOk) { 717 LOG_ERROR("Failed to prepare statement."); 718 return quota; 719 } 720 statement.bindText(1, origin->databaseIdentifier()); 721 722 if (statement.step() == SQLResultRow) 723 quota = statement.getColumnInt64(0); 724 725 return quota; 726} 727 728unsigned long long DatabaseTracker::quotaForOrigin(SecurityOrigin* origin) 729{ 730 MutexLocker lockDatabase(m_databaseGuard); 731 return quotaForOriginNoLock(origin); 732} 733 734void DatabaseTracker::setQuota(SecurityOrigin* origin, unsigned long long quota) 735{ 736 MutexLocker lockDatabase(m_databaseGuard); 737 738 if (quotaForOriginNoLock(origin) == quota) 739 return; 740 741 openTrackerDatabase(CreateIfDoesNotExist); 742 if (!m_database.isOpen()) 743 return; 744 745#if PLATFORM(IOS) 746 bool insertedNewOrigin = false; 747#endif 748 749 bool originEntryExists = hasEntryForOriginNoLock(origin); 750 if (!originEntryExists) { 751 SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)"); 752 if (statement.prepare() != SQLResultOk) { 753 LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data()); 754 } else { 755 statement.bindText(1, origin->databaseIdentifier()); 756 statement.bindInt64(2, quota); 757 758 if (statement.step() != SQLResultDone) 759 LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data()); 760#if PLATFORM(IOS) 761 else 762 insertedNewOrigin = true; 763#endif 764 } 765 } else { 766 SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?"); 767 bool error = statement.prepare() != SQLResultOk; 768 if (!error) { 769 statement.bindInt64(1, quota); 770 statement.bindText(2, origin->databaseIdentifier()); 771 772 error = !statement.executeCommand(); 773 } 774 775 if (error) 776 LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data()); 777 } 778 779 if (m_client) 780#if PLATFORM(IOS) 781 { 782 if (insertedNewOrigin) 783 m_client->dispatchDidAddNewOrigin(origin); 784#endif 785 m_client->dispatchDidModifyOrigin(origin); 786#if PLATFORM(IOS) 787 } 788#endif 789} 790 791bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, const String& path) 792{ 793 ASSERT(!m_databaseGuard.tryLock()); 794 openTrackerDatabase(CreateIfDoesNotExist); 795 if (!m_database.isOpen()) 796 return false; 797 798 // New database should never be added until the origin has been established 799 ASSERT(hasEntryForOriginNoLock(origin)); 800 801 SQLiteStatement statement(m_database, "INSERT INTO Databases (origin, name, path) VALUES (?, ?, ?);"); 802 803 if (statement.prepare() != SQLResultOk) 804 return false; 805 806 statement.bindText(1, origin->databaseIdentifier()); 807 statement.bindText(2, name); 808 statement.bindText(3, path); 809 810 if (!statement.executeCommand()) { 811 LOG_ERROR("Failed to add database %s to origin %s: %s\n", name.ascii().data(), origin->databaseIdentifier().ascii().data(), m_database.lastErrorMsg()); 812 return false; 813 } 814 815 if (m_client) 816 m_client->dispatchDidModifyOrigin(origin); 817 818 return true; 819} 820 821void DatabaseTracker::deleteAllDatabases() 822{ 823 Vector<RefPtr<SecurityOrigin>> originsCopy; 824 origins(originsCopy); 825 826 for (unsigned i = 0; i < originsCopy.size(); ++i) 827 deleteOrigin(originsCopy[i].get()); 828} 829 830// 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 831// taking place. 832bool DatabaseTracker::deleteOrigin(SecurityOrigin* origin) 833{ 834 Vector<String> databaseNames; 835 { 836 MutexLocker lockDatabase(m_databaseGuard); 837 openTrackerDatabase(DontCreateIfDoesNotExist); 838 if (!m_database.isOpen()) 839 return false; 840 841 if (!databaseNamesForOriginNoLock(origin, databaseNames)) { 842 LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data()); 843 return false; 844 } 845 if (!canDeleteOrigin(origin)) { 846 LOG_ERROR("Tried to delete an origin (%s) while either creating database in it or already deleting it", origin->databaseIdentifier().ascii().data()); 847 ASSERT_NOT_REACHED(); 848 return false; 849 } 850 recordDeletingOrigin(origin); 851 } 852 853 // We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock. 854 for (unsigned i = 0; i < databaseNames.size(); ++i) { 855 if (!deleteDatabaseFile(origin, databaseNames[i])) { 856 // Even if the file can't be deleted, we want to try and delete the rest, don't return early here. 857 LOG_ERROR("Unable to delete file for database %s in origin %s", databaseNames[i].ascii().data(), origin->databaseIdentifier().ascii().data()); 858 } 859 } 860 861 { 862 MutexLocker lockDatabase(m_databaseGuard); 863 deleteOriginLockFor(origin); 864 doneDeletingOrigin(origin); 865 866 SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?"); 867 if (statement.prepare() != SQLResultOk) { 868 LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); 869 return false; 870 } 871 872 statement.bindText(1, origin->databaseIdentifier()); 873 874 if (!statement.executeCommand()) { 875 LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); 876 return false; 877 } 878 879 SQLiteStatement originStatement(m_database, "DELETE FROM Origins WHERE origin=?"); 880 if (originStatement.prepare() != SQLResultOk) { 881 LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin->databaseIdentifier().ascii().data()); 882 return false; 883 } 884 885 originStatement.bindText(1, origin->databaseIdentifier()); 886 887 if (!originStatement.executeCommand()) { 888 LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); 889 return false; 890 } 891 892 SQLiteFileSystem::deleteEmptyDatabaseDirectory(originPath(origin)); 893 894 RefPtr<SecurityOrigin> originPossiblyLastReference = origin; 895 bool isEmpty = true; 896 897 openTrackerDatabase(DontCreateIfDoesNotExist); 898 if (m_database.isOpen()) { 899 SQLiteStatement statement(m_database, "SELECT origin FROM Origins"); 900 if (statement.prepare() != SQLResultOk) 901 LOG_ERROR("Failed to prepare statement."); 902 else if (statement.step() == SQLResultRow) 903 isEmpty = false; 904 } 905 906 // If we removed the last origin, do some additional deletion. 907 if (isEmpty) { 908 if (m_database.isOpen()) 909 m_database.close(); 910 SQLiteFileSystem::deleteDatabaseFile(trackerDatabasePath()); 911 SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_databaseDirectoryPath); 912 } 913 914 if (m_client) { 915 m_client->dispatchDidModifyOrigin(origin); 916#if PLATFORM(IOS) 917 m_client->dispatchDidDeleteDatabaseOrigin(); 918#endif 919 for (unsigned i = 0; i < databaseNames.size(); ++i) 920 m_client->dispatchDidModifyDatabase(origin, databaseNames[i]); 921 } 922 } 923 return true; 924} 925 926bool DatabaseTracker::isDeletingDatabaseOrOriginFor(SecurityOrigin *origin, const String& name) 927{ 928 ASSERT(!m_databaseGuard.tryLock()); 929 // Can't create a database while someone else is deleting it; there's a risk of leaving untracked database debris on the disk. 930 return isDeletingDatabase(origin, name) || isDeletingOrigin(origin); 931} 932 933void DatabaseTracker::recordCreatingDatabase(SecurityOrigin *origin, const String& name) 934{ 935 ASSERT(!m_databaseGuard.tryLock()); 936 NameCountMap* nameMap = m_beingCreated.get(origin); 937 if (!nameMap) { 938 nameMap = new NameCountMap(); 939 m_beingCreated.set(origin->isolatedCopy(), nameMap); 940 } 941 long count = nameMap->get(name); 942 nameMap->set(name.isolatedCopy(), count + 1); 943} 944 945void DatabaseTracker::doneCreatingDatabase(SecurityOrigin *origin, const String& name) 946{ 947 ASSERT(!m_databaseGuard.tryLock()); 948 NameCountMap* nameMap = m_beingCreated.get(origin); 949 ASSERT(nameMap); 950 if (!nameMap) 951 return; 952 953 long count = nameMap->get(name); 954 ASSERT(count > 0); 955 if (count <= 1) { 956 nameMap->remove(name); 957 if (nameMap->isEmpty()) { 958 m_beingCreated.remove(origin); 959 delete nameMap; 960 } 961 } else 962 nameMap->set(name, count - 1); 963} 964 965bool DatabaseTracker::creatingDatabase(SecurityOrigin *origin, const String& name) 966{ 967 ASSERT(!m_databaseGuard.tryLock()); 968 NameCountMap* nameMap = m_beingCreated.get(origin); 969 return nameMap && nameMap->get(name); 970} 971 972bool DatabaseTracker::canDeleteDatabase(SecurityOrigin *origin, const String& name) 973{ 974 ASSERT(!m_databaseGuard.tryLock()); 975 return !creatingDatabase(origin, name) && !isDeletingDatabase(origin, name); 976} 977 978void DatabaseTracker::recordDeletingDatabase(SecurityOrigin *origin, const String& name) 979{ 980 ASSERT(!m_databaseGuard.tryLock()); 981 ASSERT(canDeleteDatabase(origin, name)); 982 NameSet* nameSet = m_beingDeleted.get(origin); 983 if (!nameSet) { 984 nameSet = new NameSet(); 985 m_beingDeleted.set(origin->isolatedCopy(), nameSet); 986 } 987 ASSERT(!nameSet->contains(name)); 988 nameSet->add(name.isolatedCopy()); 989} 990 991void DatabaseTracker::doneDeletingDatabase(SecurityOrigin *origin, const String& name) 992{ 993 ASSERT(!m_databaseGuard.tryLock()); 994 NameSet* nameSet = m_beingDeleted.get(origin); 995 ASSERT(nameSet); 996 if (!nameSet) 997 return; 998 999 ASSERT(nameSet->contains(name)); 1000 nameSet->remove(name); 1001 if (nameSet->isEmpty()) { 1002 m_beingDeleted.remove(origin); 1003 delete nameSet; 1004 } 1005} 1006 1007bool DatabaseTracker::isDeletingDatabase(SecurityOrigin *origin, const String& name) 1008{ 1009 ASSERT(!m_databaseGuard.tryLock()); 1010 NameSet* nameSet = m_beingDeleted.get(origin); 1011 return nameSet && nameSet->contains(name); 1012} 1013 1014bool DatabaseTracker::canDeleteOrigin(SecurityOrigin *origin) 1015{ 1016 ASSERT(!m_databaseGuard.tryLock()); 1017 return !(isDeletingOrigin(origin) || m_beingCreated.get(origin)); 1018} 1019 1020bool DatabaseTracker::isDeletingOrigin(SecurityOrigin *origin) 1021{ 1022 ASSERT(!m_databaseGuard.tryLock()); 1023 return m_originsBeingDeleted.contains(origin); 1024} 1025 1026void DatabaseTracker::recordDeletingOrigin(SecurityOrigin *origin) 1027{ 1028 ASSERT(!m_databaseGuard.tryLock()); 1029 ASSERT(!isDeletingOrigin(origin)); 1030 m_originsBeingDeleted.add(origin->isolatedCopy()); 1031} 1032 1033void DatabaseTracker::doneDeletingOrigin(SecurityOrigin *origin) 1034{ 1035 ASSERT(!m_databaseGuard.tryLock()); 1036 ASSERT(isDeletingOrigin(origin)); 1037 m_originsBeingDeleted.remove(origin); 1038} 1039 1040bool DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name) 1041{ 1042 { 1043 MutexLocker lockDatabase(m_databaseGuard); 1044 openTrackerDatabase(DontCreateIfDoesNotExist); 1045 if (!m_database.isOpen()) 1046 return false; 1047 1048 if (!canDeleteDatabase(origin, name)) { 1049 ASSERT_NOT_REACHED(); 1050 return false; 1051 } 1052 recordDeletingDatabase(origin, name); 1053 } 1054 1055 // We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock. 1056 if (!deleteDatabaseFile(origin, name)) { 1057 LOG_ERROR("Unable to delete file for database %s in origin %s", name.ascii().data(), origin->databaseIdentifier().ascii().data()); 1058 MutexLocker lockDatabase(m_databaseGuard); 1059 doneDeletingDatabase(origin, name); 1060 return false; 1061 } 1062 1063 MutexLocker lockDatabase(m_databaseGuard); 1064 1065 SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=? AND name=?"); 1066 if (statement.prepare() != SQLResultOk) { 1067 LOG_ERROR("Unable to prepare deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data()); 1068 doneDeletingDatabase(origin, name); 1069 return false; 1070 } 1071 1072 statement.bindText(1, origin->databaseIdentifier()); 1073 statement.bindText(2, name); 1074 1075 if (!statement.executeCommand()) { 1076 LOG_ERROR("Unable to execute deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data()); 1077 doneDeletingDatabase(origin, name); 1078 return false; 1079 } 1080 1081 if (m_client) { 1082 m_client->dispatchDidModifyOrigin(origin); 1083 m_client->dispatchDidModifyDatabase(origin, name); 1084#if PLATFORM(IOS) 1085 m_client->dispatchDidDeleteDatabase(); 1086#endif 1087 } 1088 doneDeletingDatabase(origin, name); 1089 1090 return true; 1091} 1092 1093// deleteDatabaseFile has to release locks between looking up the list of databases to close and closing them. While this is in progress, the caller 1094// is responsible for making sure no new databases are opened in the file to be deleted. 1095bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& name) 1096{ 1097 String fullPath = fullPathForDatabase(origin, name, false); 1098 if (fullPath.isEmpty()) 1099 return true; 1100 1101#ifndef NDEBUG 1102 { 1103 MutexLocker lockDatabase(m_databaseGuard); 1104 ASSERT(isDeletingDatabaseOrOriginFor(origin, name)); 1105 } 1106#endif 1107 1108 Vector<RefPtr<DatabaseBackendBase>> deletedDatabases; 1109 1110 // Make sure not to hold the any locks when calling 1111 // Database::markAsDeletedAndClose(), since that can cause a deadlock 1112 // during the synchronous DatabaseThread call it triggers. 1113 { 1114 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); 1115 if (m_openDatabaseMap) { 1116 // There are some open databases, lets check if they are for this origin. 1117 DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); 1118 if (nameMap && nameMap->size()) { 1119 // There are some open databases for this origin, let's check 1120 // if they are this database by name. 1121 DatabaseSet* databaseSet = nameMap->get(name); 1122 if (databaseSet && databaseSet->size()) { 1123 // We have some database open with this name. Mark them as deleted. 1124 DatabaseSet::const_iterator end = databaseSet->end(); 1125 for (DatabaseSet::const_iterator it = databaseSet->begin(); it != end; ++it) 1126 deletedDatabases.append(*it); 1127 } 1128 } 1129 } 1130 } 1131 1132 for (unsigned i = 0; i < deletedDatabases.size(); ++i) 1133 deletedDatabases[i]->markAsDeletedAndClose(); 1134 1135#if !PLATFORM(IOS) 1136 return SQLiteFileSystem::deleteDatabaseFile(fullPath); 1137#else 1138 // On the phone, other background processes may still be accessing this database. Deleting the database directly 1139 // would nuke the POSIX file locks, potentially causing Safari/WebApp to corrupt the new db if it's running in the background. 1140 // We'll instead truncate the database file to 0 bytes. If another process is operating on this same database file after 1141 // the truncation, it should get an error since the database file is no longer valid. When Safari is launched 1142 // next time, it'll go through the database files and clean up any zero-bytes ones. 1143 SQLiteDatabase database; 1144 if (database.open(fullPath)) 1145 return SQLiteFileSystem::truncateDatabaseFile(database.sqlite3Handle()); 1146 return false; 1147#endif 1148} 1149 1150#if PLATFORM(IOS) 1151void DatabaseTracker::removeDeletedOpenedDatabases() 1152{ 1153 // This is called when another app has deleted a database. Go through all opened databases in this 1154 // tracker and close any that's no longer being tracked in the database. 1155 1156 { 1157 // Acquire the lock before calling openTrackerDatabase. 1158 MutexLocker lockDatabase(m_databaseGuard); 1159 openTrackerDatabase(DontCreateIfDoesNotExist); 1160 } 1161 1162 if (!m_database.isOpen()) 1163 return; 1164 1165 // Keep track of which opened databases have been deleted. 1166 Vector<RefPtr<Database> > deletedDatabases; 1167 typedef HashMap<RefPtr<SecurityOrigin>, Vector<String> > DeletedDatabaseMap; 1168 DeletedDatabaseMap deletedDatabaseMap; 1169 1170 // Make sure not to hold the m_openDatabaseMapGuard mutex when calling 1171 // Database::markAsDeletedAndClose(), since that can cause a deadlock 1172 // during the synchronous DatabaseThread call it triggers. 1173 { 1174 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); 1175 if (m_openDatabaseMap) { 1176 DatabaseOriginMap::const_iterator originMapEnd = m_openDatabaseMap->end(); 1177 for (DatabaseOriginMap::const_iterator originMapIt = m_openDatabaseMap->begin(); originMapIt != originMapEnd; ++originMapIt) { 1178 RefPtr<SecurityOrigin> origin = originMapIt->key; 1179 DatabaseNameMap* databaseNameMap = originMapIt->value; 1180 Vector<String> deletedDatabaseNamesForThisOrigin; 1181 1182 // Loop through all opened databases in this origin. Get the current database file path of each database and see if 1183 // it still matches the path stored in the opened database object. 1184 DatabaseNameMap::const_iterator dbNameMapEnd = databaseNameMap->end(); 1185 for (DatabaseNameMap::const_iterator dbNameMapIt = databaseNameMap->begin(); dbNameMapIt != dbNameMapEnd; ++dbNameMapIt) { 1186 String databaseName = dbNameMapIt->key; 1187 String databaseFileName; 1188 SQLiteStatement statement(m_database, "SELECT path FROM Databases WHERE origin=? AND name=?;"); 1189 if (statement.prepare() == SQLResultOk) { 1190 statement.bindText(1, origin->databaseIdentifier()); 1191 statement.bindText(2, databaseName); 1192 if (statement.step() == SQLResultRow) 1193 databaseFileName = statement.getColumnText(0); 1194 statement.finalize(); 1195 } 1196 1197 bool foundDeletedDatabase = false; 1198 DatabaseSet* databaseSet = dbNameMapIt->value; 1199 DatabaseSet::const_iterator dbEnd = databaseSet->end(); 1200 for (DatabaseSet::const_iterator dbIt = databaseSet->begin(); dbIt != dbEnd; ++dbIt) { 1201 Database* db = static_cast<Database*>(*dbIt); 1202 1203 // We are done if this database has already been marked as deleted. 1204 if (db->deleted()) 1205 continue; 1206 1207 // If this database has been deleted or if its database file no longer matches the current version, this database is no longer valid and it should be marked as deleted. 1208 if (databaseFileName.isNull() || databaseFileName != pathGetFileName(db->fileName())) { 1209 deletedDatabases.append(db); 1210 foundDeletedDatabase = true; 1211 } 1212 } 1213 1214 // If the database no longer exists, we should remember to remove it from the OriginQuotaManager later. 1215 if (foundDeletedDatabase && databaseFileName.isNull()) 1216 deletedDatabaseNamesForThisOrigin.append(databaseName); 1217 } 1218 1219 if (!deletedDatabaseNamesForThisOrigin.isEmpty()) 1220 deletedDatabaseMap.set(origin, deletedDatabaseNamesForThisOrigin); 1221 } 1222 } 1223 } 1224 1225 for (unsigned i = 0; i < deletedDatabases.size(); ++i) 1226 deletedDatabases[i]->markAsDeletedAndClose(); 1227 1228 DeletedDatabaseMap::const_iterator end = deletedDatabaseMap.end(); 1229 for (DeletedDatabaseMap::const_iterator it = deletedDatabaseMap.begin(); it != end; ++it) { 1230 SecurityOrigin* origin = it->key.get(); 1231 if (m_client) 1232 m_client->dispatchDidModifyOrigin(origin); 1233 1234 const Vector<String>& databaseNames = it->value; 1235 for (unsigned i = 0; i < databaseNames.size(); ++i) { 1236 if (m_client) 1237 m_client->dispatchDidModifyDatabase(origin, databaseNames[i]); 1238 } 1239 } 1240} 1241 1242static bool isZeroByteFile(const String& path) 1243{ 1244 long long size = 0; 1245 return getFileSize(path, size) && !size; 1246} 1247 1248bool DatabaseTracker::deleteDatabaseFileIfEmpty(const String& path) 1249{ 1250 if (!isZeroByteFile(path)) 1251 return false; 1252 1253 SQLiteDatabase database; 1254 if (!database.open(path)) 1255 return false; 1256 1257 // Specify that we want the exclusive locking mode, so after the next read, 1258 // we'll be holding the lock to this database file. 1259 SQLiteStatement lockStatement(database, "PRAGMA locking_mode=EXCLUSIVE;"); 1260 if (lockStatement.prepare() != SQLResultOk) 1261 return false; 1262 int result = lockStatement.step(); 1263 if (result != SQLResultRow && result != SQLResultDone) 1264 return false; 1265 lockStatement.finalize(); 1266 1267 // Every sqlite database has a sqlite_master table that contains the schema for the database. 1268 // http://www.sqlite.org/faq.html#q7 1269 SQLiteStatement readStatement(database, "SELECT * FROM sqlite_master LIMIT 1;"); 1270 if (readStatement.prepare() != SQLResultOk) 1271 return false; 1272 // We shouldn't expect any result. 1273 if (readStatement.step() != SQLResultDone) 1274 return false; 1275 readStatement.finalize(); 1276 1277 // At this point, we hold the exclusive lock to this file. Double-check again to make sure 1278 // it's still zero bytes. 1279 if (!isZeroByteFile(path)) 1280 return false; 1281 1282 return SQLiteFileSystem::deleteDatabaseFile(path); 1283} 1284 1285Mutex& DatabaseTracker::openDatabaseMutex() 1286{ 1287 static NeverDestroyed<Mutex> mutex; 1288 return mutex; 1289} 1290 1291void DatabaseTracker::emptyDatabaseFilesRemovalTaskWillBeScheduled() 1292{ 1293 // Lock the database from opening any database until we are done with scanning the file system for 1294 // zero byte database files to remove. 1295 openDatabaseMutex().lock(); 1296} 1297 1298void DatabaseTracker::emptyDatabaseFilesRemovalTaskDidFinish() 1299{ 1300 openDatabaseMutex().unlock(); 1301} 1302 1303void DatabaseTracker::setDatabasesPaused(bool paused) 1304{ 1305 MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); 1306 if (!m_openDatabaseMap) 1307 return; 1308 1309 // This walking is - sadly - the only reliable way to get at each open database thread. 1310 // This will be cleaner once <rdar://problem/5680441> or some other DB thread consolidation takes place. 1311 DatabaseOriginMap::iterator i = m_openDatabaseMap.get()->begin(); 1312 DatabaseOriginMap::iterator end = m_openDatabaseMap.get()->end(); 1313 1314 for (; i != end; ++i) { 1315 DatabaseNameMap* databaseNameMap = i->value; 1316 DatabaseNameMap::iterator j = databaseNameMap->begin(); 1317 DatabaseNameMap::iterator dbNameMapEnd = databaseNameMap->end(); 1318 for (; j != dbNameMapEnd; ++j) { 1319 DatabaseSet* databaseSet = j->value; 1320 DatabaseSet::iterator k = databaseSet->begin(); 1321 DatabaseSet::iterator dbSetEnd = databaseSet->end(); 1322 for (; k != dbSetEnd; ++k) { 1323 DatabaseContext* context = (*k)->databaseContext(); 1324 context->setPaused(paused); 1325 } 1326 } 1327 } 1328} 1329 1330#endif 1331 1332void DatabaseTracker::setClient(DatabaseManagerClient* client) 1333{ 1334 m_client = client; 1335} 1336 1337static Mutex& notificationMutex() 1338{ 1339 static NeverDestroyed<Mutex> mutex; 1340 return mutex; 1341} 1342 1343typedef Vector<std::pair<RefPtr<SecurityOrigin>, String>> NotificationQueue; 1344 1345static NotificationQueue& notificationQueue() 1346{ 1347 static NeverDestroyed<NotificationQueue> queue; 1348 return queue; 1349} 1350 1351void DatabaseTracker::scheduleNotifyDatabaseChanged(SecurityOrigin* origin, const String& name) 1352{ 1353 MutexLocker locker(notificationMutex()); 1354 1355 notificationQueue().append(std::pair<RefPtr<SecurityOrigin>, String>(origin->isolatedCopy(), name.isolatedCopy())); 1356 scheduleForNotification(); 1357} 1358 1359static bool notificationScheduled = false; 1360 1361void DatabaseTracker::scheduleForNotification() 1362{ 1363 ASSERT(!notificationMutex().tryLock()); 1364 1365 if (!notificationScheduled) { 1366 callOnMainThread(DatabaseTracker::notifyDatabasesChanged, 0); 1367 notificationScheduled = true; 1368 } 1369} 1370 1371void DatabaseTracker::notifyDatabasesChanged(void*) 1372{ 1373 // Note that if DatabaseTracker ever becomes non-singleton, we'll have to amend this notification 1374 // mechanism to include which tracker the notification goes out on as well. 1375 DatabaseTracker& theTracker(tracker()); 1376 1377 NotificationQueue notifications; 1378 { 1379 MutexLocker locker(notificationMutex()); 1380 1381 notifications.swap(notificationQueue()); 1382 1383 notificationScheduled = false; 1384 } 1385 1386 if (!theTracker.m_client) 1387 return; 1388 1389 for (unsigned i = 0; i < notifications.size(); ++i) 1390 theTracker.m_client->dispatchDidModifyDatabase(notifications[i].first.get(), notifications[i].second); 1391} 1392 1393 1394} // namespace WebCore 1395#endif 1396