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 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 63 void DatabaseTracker::initializeTracker(const String& databasePath) 64 { 65 ASSERT(!staticTracker); 66 if (staticTracker) 67 return; 68 69 staticTracker = new DatabaseTracker(databasePath); 70 } 71 72 DatabaseTracker& DatabaseTracker::tracker() 73 { 74 if (!staticTracker) 75 staticTracker = new DatabaseTracker(""); 76 77 return *staticTracker; 78 } 79 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 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 98 String DatabaseTracker::databaseDirectoryPath() const 99 { 100 return m_databaseDirectoryPath.threadsafeCopy(); 101 } 102 103 String DatabaseTracker::trackerDatabasePath() const 104 { 105 return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath, "Databases.db"); 106 } 107 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 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 193 bool DatabaseTracker::hasEntryForOriginNoLock(SecurityOrigin* origin) 194 { 195 ASSERT(!m_databaseGuard.tryLock()); 196 ASSERT(m_quotaMap); 197 return m_quotaMap->contains(origin); 198 } 199 200 bool DatabaseTracker::hasEntryForOrigin(SecurityOrigin* origin) 201 { 202 MutexLocker lockDatabase(m_databaseGuard); 203 return hasEntryForOriginNoLock(origin); 204 } 205 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 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 233 void DatabaseTracker::databaseChanged(AbstractDatabase* database) 234 { 235 Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); 236 originQuotaManager().markDatabase(database); 237 } 238 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 268 String DatabaseTracker::originPath(SecurityOrigin* origin) const 269 { 270 return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath.threadsafeCopy(), origin->databaseIdentifier()); 271 } 272 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 651 unsigned long long DatabaseTracker::quotaForOrigin(SecurityOrigin* origin) 652 { 653 MutexLocker lockDatabase(m_databaseGuard); 654 return quotaForOriginNoLock(origin); 655 } 656 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 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 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. 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 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 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 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 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 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 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 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 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 919 bool DatabaseTracker::canDeleteOrigin(SecurityOrigin *origin) 920 { 921 ASSERT(!m_databaseGuard.tryLock()); 922 return !(deletingOrigin(origin) || m_beingCreated.get(origin)); 923 } 924 925 bool DatabaseTracker::deletingOrigin(SecurityOrigin *origin) 926 { 927 ASSERT(!m_databaseGuard.tryLock()); 928 return m_originsBeingDeleted.contains(origin); 929 } 930 931 void DatabaseTracker::recordDeletingOrigin(SecurityOrigin *origin) 932 { 933 ASSERT(!m_databaseGuard.tryLock()); 934 ASSERT(!deletingOrigin(origin)); 935 m_originsBeingDeleted.add(origin->threadsafeCopy()); 936 } 937 938 void DatabaseTracker::doneDeletingOrigin(SecurityOrigin *origin) 939 { 940 ASSERT(!m_databaseGuard.tryLock()); 941 ASSERT(deletingOrigin(origin)); 942 m_originsBeingDeleted.remove(origin); 943 } 944 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. 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 1045 void DatabaseTracker::setClient(DatabaseTrackerClient* client) 1046 { 1047 m_client = client; 1048 } 1049 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 1058 static NotificationQueue& notificationQueue() 1059 { 1060 DEFINE_STATIC_LOCAL(NotificationQueue, queue, ()); 1061 return queue; 1062 } 1063 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 1074 void DatabaseTracker::scheduleForNotification() 1075 { 1076 ASSERT(!notificationMutex().tryLock()); 1077 1078 if (!notificationScheduled) { 1079 callOnMainThread(DatabaseTracker::notifyDatabasesChanged, 0); 1080 notificationScheduled = true; 1081 } 1082 } 1083 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