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