void DatabaseTracker::addOpenDatabase(DatabaseBackendBase* database) { if (!database) return; { MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); if (!m_openDatabaseMap) m_openDatabaseMap = adoptPtr(new DatabaseOriginMap); String name(database->stringIdentifier()); DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); if (!nameMap) { nameMap = new DatabaseNameMap; m_openDatabaseMap->set(database->securityOrigin()->isolatedCopy(), nameMap); } DatabaseSet* databaseSet = nameMap->get(name); if (!databaseSet) { databaseSet = new DatabaseSet; nameMap->set(name.isolatedCopy(), databaseSet); } databaseSet->add(database); LOG(StorageAPI, "Added open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database); } }
void DatabaseTracker::removeOpenDatabase(DatabaseBackend* database) { String originIdentifier = database->securityOrigin()->databaseIdentifier(); MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); ASSERT(m_openDatabaseMap); DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier); if (!nameMap) return; String name(database->stringIdentifier()); DatabaseSet* databaseSet = nameMap->get(name); if (!databaseSet) return; DatabaseSet::iterator found = databaseSet->find(database); if (found == databaseSet->end()) return; databaseSet->remove(found); if (databaseSet->isEmpty()) { nameMap->remove(name); delete databaseSet; if (nameMap->isEmpty()) { m_openDatabaseMap->remove(originIdentifier); delete nameMap; } } if (!database->scriptExecutionContext()->isContextThread()) database->scriptExecutionContext()->postTask(NotifyDatabaseObserverOnCloseTask::create(database)); else DatabaseObserver::databaseClosed(database); }
void DatabaseTracker::addOpenDatabase(DatabaseBackend* database) { ASSERT(database->scriptExecutionContext()->isContextThread()); MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); if (!m_openDatabaseMap) m_openDatabaseMap = adoptPtr(new DatabaseOriginMap); String originIdentifier = database->securityOrigin()->databaseIdentifier(); DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier); if (!nameMap) { nameMap = new DatabaseNameMap(); m_openDatabaseMap->set(originIdentifier, nameMap); } String name(database->stringIdentifier()); DatabaseSet* databaseSet = nameMap->get(name); if (!databaseSet) { databaseSet = new DatabaseSet(); nameMap->set(name, databaseSet); } databaseSet->add(database); DatabaseObserver::databaseOpened(database); }
void DatabaseTracker::removeOpenDatabase(AbstractDatabase* database) { if (!database->scriptExecutionContext()->isContextThread()) { database->scriptExecutionContext()->postTask(TrackerRemoveOpenDatabaseTask::create(database)); return; } MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); ASSERT(m_openDatabaseMap); DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); ASSERT(nameMap); String name(database->stringIdentifier()); DatabaseSet* databaseSet = nameMap->get(name); ASSERT(databaseSet); databaseSet->remove(database); if (databaseSet->isEmpty()) { nameMap->remove(name); delete databaseSet; if (nameMap->isEmpty()) { m_openDatabaseMap->remove(database->securityOrigin()); delete nameMap; } } DatabaseObserver::databaseClosed(database); }
void DatabaseTracker::removeOpenDatabase(Database* database) { { MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); String originString = database->getSecurityOrigin()->toRawString(); ASSERT(m_openDatabaseMap); DatabaseNameMap* nameMap = m_openDatabaseMap->get(originString); if (!nameMap) return; String name(database->stringIdentifier()); DatabaseSet* databaseSet = nameMap->get(name); if (!databaseSet) return; DatabaseSet::iterator found = databaseSet->find(database); if (found == databaseSet->end()) return; databaseSet->remove(found); if (databaseSet->isEmpty()) { nameMap->remove(name); delete databaseSet; if (nameMap->isEmpty()) { m_openDatabaseMap->remove(originString); delete nameMap; } } } databaseClosed(database); }
void DatabaseTracker::interruptAllDatabasesForContext(const ScriptExecutionContext* context) { Vector<RefPtr<AbstractDatabase> > openDatabases; { MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); if (!m_openDatabaseMap) return; DatabaseNameMap* nameMap = m_openDatabaseMap->get(context->securityOrigin()); if (!nameMap) return; DatabaseNameMap::const_iterator dbNameMapEndIt = nameMap->end(); for (DatabaseNameMap::const_iterator dbNameMapIt = nameMap->begin(); dbNameMapIt != dbNameMapEndIt; ++dbNameMapIt) { DatabaseSet* databaseSet = dbNameMapIt->second; DatabaseSet::const_iterator dbSetEndIt = databaseSet->end(); for (DatabaseSet::const_iterator dbSetIt = databaseSet->begin(); dbSetIt != dbSetEndIt; ++dbSetIt) { if ((*dbSetIt)->scriptExecutionContext() == context) openDatabases.append(*dbSetIt); } } } Vector<RefPtr<AbstractDatabase> >::const_iterator openDatabasesEndIt = openDatabases.end(); for (Vector<RefPtr<AbstractDatabase> >::const_iterator openDatabasesIt = openDatabases.begin(); openDatabasesIt != openDatabasesEndIt; ++openDatabasesIt) (*openDatabasesIt)->interrupt(); }
// deleteDatabaseFile has to release locks between looking up the list of databases to close and closing them. While this is in progress, the caller // is responsible for making sure no new databases are opened in the file to be deleted. bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& name, DeletionMode deletionMode) { String fullPath = fullPathForDatabase(origin, name, false); if (fullPath.isEmpty()) return true; #ifndef NDEBUG { LockHolder lockDatabase(m_databaseGuard); ASSERT(isDeletingDatabaseOrOriginFor(origin, name)); } #endif Vector<RefPtr<Database>> deletedDatabases; // Make sure not to hold the any locks when calling // Database::markAsDeletedAndClose(), since that can cause a deadlock // during the synchronous DatabaseThread call it triggers. { LockHolder openDatabaseMapLock(m_openDatabaseMapGuard); if (m_openDatabaseMap) { // There are some open databases, lets check if they are for this origin. DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); if (nameMap && nameMap->size()) { // There are some open databases for this origin, let's check // if they are this database by name. DatabaseSet* databaseSet = nameMap->get(name); if (databaseSet && databaseSet->size()) { // We have some database open with this name. Mark them as deleted. for (auto& database : *databaseSet) deletedDatabases.append(database); } } } } for (auto& database : deletedDatabases) database->markAsDeletedAndClose(); #if PLATFORM(IOS) if (deletionMode == DeletionMode::Deferred) { // On the phone, other background processes may still be accessing this database. Deleting the database directly // would nuke the POSIX file locks, potentially causing Safari/WebApp to corrupt the new db if it's running in the background. // We'll instead truncate the database file to 0 bytes. If another process is operating on this same database file after // the truncation, it should get an error since the database file is no longer valid. When Safari is launched // next time, it'll go through the database files and clean up any zero-bytes ones. SQLiteDatabase database; if (!database.open(fullPath)) return false; return SQLiteFileSystem::truncateDatabaseFile(database.sqlite3Handle()); } #else UNUSED_PARAM(deletionMode); #endif return SQLiteFileSystem::deleteDatabaseFile(fullPath); }
void DatabaseTracker::forEachOpenDatabaseInPage(Page* page, std::unique_ptr<DatabaseCallback> callback) { MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); if (!m_openDatabaseMap) return; for (auto& originMap : *m_openDatabaseMap) { for (auto& nameDatabaseSet : *originMap.value) { for (Database* database : *nameDatabaseSet.value) { ExecutionContext* context = database->getExecutionContext(); ASSERT(context->isDocument()); if (toDocument(context)->frame()->page() == page) (*callback)(database); } } } }
void DatabaseTracker::getOpenDatabases(SecurityOrigin* origin, const String& name, HashSet<RefPtr<Database>>* databases) { LockHolder openDatabaseMapLock(m_openDatabaseMapGuard); if (!m_openDatabaseMap) return; DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); if (!nameMap) return; DatabaseSet* databaseSet = nameMap->get(name); if (!databaseSet) return; for (auto& database : *databaseSet) databases->add(database); }
void DatabaseTracker::getOpenDatabases(SecurityOrigin* origin, const String& name, HashSet<RefPtr<AbstractDatabase> >* databases) { MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); if (!m_openDatabaseMap) return; DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); if (!nameMap) return; DatabaseSet* databaseSet = nameMap->get(name); if (!databaseSet) return; for (DatabaseSet::iterator it = databaseSet->begin(); it != databaseSet->end(); ++it) databases->add(*it); }
void DatabaseTracker::closeDatabasesImmediately(SecurityOrigin* origin, const String& name) { String originString = origin->toRawString(); MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); if (!m_openDatabaseMap) return; DatabaseNameMap* nameMap = m_openDatabaseMap->get(originString); if (!nameMap) return; DatabaseSet* databaseSet = nameMap->get(name); if (!databaseSet) return; // We have to call closeImmediately() on the context thread. for (DatabaseSet::iterator it = databaseSet->begin(); it != databaseSet->end(); ++it) (*it)->getDatabaseContext()->getExecutionContext()->postTask(BLINK_FROM_HERE, createCrossThreadTask(&DatabaseTracker::closeOneDatabaseImmediately, crossThreadUnretained(this), originString, name, *it)); }
void DatabaseTracker::closeDatabasesImmediately(const String& originIdentifier, const String& name) { MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); if (!m_openDatabaseMap) return; DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier); if (!nameMap) return; DatabaseSet* databaseSet = nameMap->get(name); if (!databaseSet) return; // We have to call closeImmediately() on the context thread and we cannot safely add a reference to // the database in our collection when not on the context thread (which is always the case given // current usage). for (DatabaseSet::iterator it = databaseSet->begin(); it != databaseSet->end(); ++it) (*it)->scriptExecutionContext()->postTask(CloseOneDatabaseImmediatelyTask::create(originIdentifier, name, *it)); }
void DatabaseTracker::removeOpenDatabase(DatabaseBackendBase* database) { if (!database) return; { MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); if (!m_openDatabaseMap) { ASSERT_NOT_REACHED(); return; } String name(database->stringIdentifier()); DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); if (!nameMap) { ASSERT_NOT_REACHED(); return; } DatabaseSet* databaseSet = nameMap->get(name); if (!databaseSet) { ASSERT_NOT_REACHED(); return; } databaseSet->remove(database); LOG(StorageAPI, "Removed open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database); if (!databaseSet->isEmpty()) return; nameMap->remove(name); delete databaseSet; if (!nameMap->isEmpty()) return; m_openDatabaseMap->remove(database->securityOrigin()); delete nameMap; } }
// deleteDatabaseFile has to release locks between looking up the list of databases to close and closing them. While this is in progress, the caller // is responsible for making sure no new databases are opened in the file to be deleted. bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& name) { String fullPath = fullPathForDatabase(origin, name, false); if (fullPath.isEmpty()) return true; #ifndef NDEBUG { MutexLocker lockDatabase(m_databaseGuard); ASSERT(isDeletingDatabaseOrOriginFor(origin, name)); } #endif Vector<RefPtr<DatabaseBackendBase>> deletedDatabases; // Make sure not to hold the any locks when calling // Database::markAsDeletedAndClose(), since that can cause a deadlock // during the synchronous DatabaseThread call it triggers. { MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); if (m_openDatabaseMap) { // There are some open databases, lets check if they are for this origin. DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); if (nameMap && nameMap->size()) { // There are some open databases for this origin, let's check // if they are this database by name. DatabaseSet* databaseSet = nameMap->get(name); if (databaseSet && databaseSet->size()) { // We have some database open with this name. Mark them as deleted. DatabaseSet::const_iterator end = databaseSet->end(); for (DatabaseSet::const_iterator it = databaseSet->begin(); it != end; ++it) deletedDatabases.append(*it); } } } } for (unsigned i = 0; i < deletedDatabases.size(); ++i) deletedDatabases[i]->markAsDeletedAndClose(); return SQLiteFileSystem::deleteDatabaseFile(fullPath); }
void DatabaseTracker::closeAllDatabases(CurrentQueryBehavior currentQueryBehavior) { Vector<Ref<Database>> openDatabases; { LockHolder openDatabaseMapLock(m_openDatabaseMapGuard); if (!m_openDatabaseMap) return; for (auto& nameMap : m_openDatabaseMap->values()) { for (auto& set : nameMap->values()) { for (auto& database : *set) openDatabases.append(*database); } } } for (auto& database : openDatabases) { if (currentQueryBehavior == CurrentQueryBehavior::Interrupt) database->interrupt(); database->close(); } }
void DatabaseTracker::interruptAllDatabasesForContext(const ScriptExecutionContext* context) { MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); if (!m_openDatabaseMap) return; DatabaseNameMap* nameMap = m_openDatabaseMap->get(context->securityOrigin()->databaseIdentifier()); if (!nameMap) return; DatabaseNameMap::const_iterator dbNameMapEndIt = nameMap->end(); for (DatabaseNameMap::const_iterator dbNameMapIt = nameMap->begin(); dbNameMapIt != dbNameMapEndIt; ++dbNameMapIt) { DatabaseSet* databaseSet = dbNameMapIt->value; DatabaseSet::const_iterator end = databaseSet->end(); for (DatabaseSet::const_iterator it = databaseSet->begin(); it != end; ++it) { if ((*it)->scriptExecutionContext() == context) (*it)->interrupt(); } } }
void DatabaseTracker::addOpenDatabase(Database* database) { MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); if (!m_openDatabaseMap) m_openDatabaseMap = wrapUnique(new DatabaseOriginMap); String originString = database->getSecurityOrigin()->toRawString(); DatabaseNameMap* nameMap = m_openDatabaseMap->get(originString); if (!nameMap) { nameMap = new DatabaseNameMap(); m_openDatabaseMap->set(originString, nameMap); } String name(database->stringIdentifier()); DatabaseSet* databaseSet = nameMap->get(name); if (!databaseSet) { databaseSet = new DatabaseSet(); nameMap->set(name, databaseSet); } databaseSet->add(database); }
void DatabaseTracker::closeOneDatabaseImmediately(const String& originIdentifier, const String& name, DatabaseBackend* database) { // First we have to confirm the 'database' is still in our collection. { MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); if (!m_openDatabaseMap) return; DatabaseNameMap* nameMap = m_openDatabaseMap->get(originIdentifier); if (!nameMap) return; DatabaseSet* databaseSet = nameMap->get(name); if (!databaseSet) return; DatabaseSet::iterator found = databaseSet->find(database); if (found == databaseSet->end()) return; } // And we have to call closeImmediately() without our collection lock being held. database->closeImmediately(); }
void DatabaseTracker::removeDeletedOpenedDatabases() { // This is called when another app has deleted a database. Go through all opened databases in this // tracker and close any that's no longer being tracked in the database. { // Acquire the lock before calling openTrackerDatabase. LockHolder lockDatabase(m_databaseGuard); openTrackerDatabase(DontCreateIfDoesNotExist); } if (!m_database.isOpen()) return; // Keep track of which opened databases have been deleted. Vector<RefPtr<Database> > deletedDatabases; typedef HashMap<RefPtr<SecurityOrigin>, Vector<String> > DeletedDatabaseMap; DeletedDatabaseMap deletedDatabaseMap; // Make sure not to hold the m_openDatabaseMapGuard mutex when calling // Database::markAsDeletedAndClose(), since that can cause a deadlock // during the synchronous DatabaseThread call it triggers. { LockHolder openDatabaseMapLock(m_openDatabaseMapGuard); if (m_openDatabaseMap) { for (auto& openDatabase : *m_openDatabaseMap) { auto& origin = openDatabase.key; DatabaseNameMap* databaseNameMap = openDatabase.value; Vector<String> deletedDatabaseNamesForThisOrigin; // Loop through all opened databases in this origin. Get the current database file path of each database and see if // it still matches the path stored in the opened database object. for (auto& databases : *databaseNameMap) { String databaseName = databases.key; String databaseFileName; SQLiteStatement statement(m_database, "SELECT path FROM Databases WHERE origin=? AND name=?;"); if (statement.prepare() == SQLITE_OK) { statement.bindText(1, origin->databaseIdentifier()); statement.bindText(2, databaseName); if (statement.step() == SQLITE_ROW) databaseFileName = statement.getColumnText(0); statement.finalize(); } bool foundDeletedDatabase = false; for (auto& db : *databases.value) { // We are done if this database has already been marked as deleted. if (db->deleted()) continue; // 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. if (databaseFileName.isNull() || databaseFileName != pathGetFileName(db->fileName())) { deletedDatabases.append(db); foundDeletedDatabase = true; } } // If the database no longer exists, we should remember to remove it from the OriginQuotaManager later. if (foundDeletedDatabase && databaseFileName.isNull()) deletedDatabaseNamesForThisOrigin.append(databaseName); } if (!deletedDatabaseNamesForThisOrigin.isEmpty()) deletedDatabaseMap.set(origin, deletedDatabaseNamesForThisOrigin); } } } for (auto& deletedDatabase : deletedDatabases) deletedDatabase->markAsDeletedAndClose(); for (auto& deletedDatabase : deletedDatabaseMap) { SecurityOrigin* origin = deletedDatabase.key.get(); if (m_client) m_client->dispatchDidModifyOrigin(origin); const Vector<String>& databaseNames = deletedDatabase.value; for (auto& databaseName : databaseNames) { if (m_client) m_client->dispatchDidModifyDatabase(origin, databaseName); } } }