+2006-08-29 Brady Eidson <beidson@apple.com>
+
+ Reviewed by Kevin Decker (Sarge)
+
+ <rdar://problem/4678414> - New IconDB needs to delete icons when asked
+ <rdar://problem/4707718> - If user's Icon directory is unwritable, Safari will crash at startup
+
+ * bridge/mac/WebCoreIconDatabaseBridge.h:
+ * bridge/mac/WebCoreIconDatabaseBridge.mm:
+ (-[WebCoreIconDatabaseBridge removeAllIcons]): Added
+ * loader/icon/IconDatabase.cpp:
+ (WebCore::IconDatabase::open): If DB file is not writeable, create an in-memory DB for this session
+ (WebCore::IconDatabase::close): Use new deleteAllPreparedStatements()
+ (WebCore::IconDatabase::removeAllIcons): Actually implemented
+ (WebCore::IconDatabase::deleteAllPreparedStatements): Added for convinience/consistency
+ (WebCore::IconDatabase::setPrivateBrowsingEnabled): Use new SQLDatabase::clearAllTables()
+ * loader/icon/IconDatabase.h:
+ * loader/icon/SQLDatabase.cpp:
+ (WebCore::SQLDatabase::clearAllTables): Moved this from IconDatabase as it actually belongs here
+ (WebCore::SQLDatabase::vacuum): Added
+ * loader/icon/SQLDatabase.h:
+ (WebCore::SQLDatabase::path): changed name from getPath()
+
2006-08-29 Brady Eidson <beidson@apple.com>
Reviewed by Alice
else
dbFilename = databasePath + "/" + defaultDatabaseFilename();
- // Now, we'll see if we can open the on-disk table
- // If we can't, this ::open() failed and we should bail now
+ // <rdar://problem/4707718> - If user's Icon directory is unwritable, Safari will crash at startup
+ // Now, we'll see if we can open the on-disk database. If we can't, we'll log the error and open it as
+ // an in-memory database so the user will have icons during their current browsing session
+
if (!m_mainDB.open(dbFilename)) {
- LOG(IconDatabase, "Unable to open icon database at path %s", dbFilename.ascii().data());
- return false;
+ LOG_ERROR("Unable to open icon database at path %s - %s", dbFilename.ascii().data(), m_mainDB.lastErrorMsg());
+ if (!m_mainDB.open(":memory:"))
+ LOG_ERROR("Unable to open in-memory database for browsing - %s", m_mainDB.lastErrorMsg());
}
if (!isValidDatabase(m_mainDB)) {
LOG(IconDatabase, "%s is missing or in an invalid state - reconstructing", dbFilename.ascii().data());
- clearDatabaseTables(m_mainDB);
+ m_mainDB.clearAllTables();
createDatabaseTables(m_mainDB);
}
// Open the in-memory table for private browsing
if (!m_privateBrowsingDB.open(":memory:"))
- LOG_ERROR("Unabled to open in-memory database for private browsing - %s", m_privateBrowsingDB.lastErrorMsg());
+ LOG_ERROR("Unable to open in-memory database for private browsing - %s", m_privateBrowsingDB.lastErrorMsg());
// Only if we successfully remained open will we start our "initial purge timer"
// rdar://4690949 - when we have deferred reads and writes all the way in, the prunetimer
void IconDatabase::close()
{
- delete m_initialPruningTransaction;
- if (m_preparedPageRetainInsertStatement)
- m_preparedPageRetainInsertStatement->finalize();
+ // This will close all the SQL statements and transactions we have open,
+ // syncing the DB at the appropriate point
+ deleteAllPreparedStatements(true);
+
+ m_mainDB.close();
+ m_privateBrowsingDB.close();
+}
+
+void IconDatabase::removeAllIcons()
+{
+ // We don't need to sync anything anymore since we're wiping everything.
+ // So we can kill the update timer, and clear all the hashes of "items that need syncing"
+ m_updateTimer.stop();
+ m_iconDataCachesPendingUpdate.clear();
+ m_pageURLsPendingAddition.clear();
+ m_pageURLsPendingDeletion.clear();
+ m_iconURLsPendingDeletion.clear();
+
+ // Now clear all in-memory URLs and Icons
+ m_pageURLToIconURLMap.clear();
+ m_pageURLToRetainCount.clear();
+ m_iconURLToRetainCount.clear();
+
+ HashMap<String, IconDataCache*>::iterator i = m_iconURLToIconDataCacheMap.begin();
+ HashMap<String, IconDataCache*>::iterator end = m_iconURLToIconDataCacheMap.end();
+ for (; i != end; ++i)
+ delete i->second;
+ m_iconURLToIconDataCacheMap.clear();
+
+ // Wipe any pre-prepared statements, otherwise resetting the SQLDatabases themselves will fail
+ deleteAllPreparedStatements(false);
+
+ // The easiest way to wipe the in-memory database is by closing and reopening it
+ m_privateBrowsingDB.close();
+ if (!m_privateBrowsingDB.open(":memory:"))
+ LOG_ERROR("Unable to open in-memory database for private browsing - %s", m_privateBrowsingDB.lastErrorMsg());
+ createDatabaseTables(m_privateBrowsingDB);
+
+ // To reset the on-disk database, we'll wipe all its tables then vacuum it
+ // This is easier and safer than closing it, deleting the file, and recreating from scratch
+ m_mainDB.clearAllTables();
+ m_mainDB.runVacuumCommand();
+ createDatabaseTables(m_mainDB);
+}
+
+// There are two instances where you'd want to deleteAllPreparedStatements - one with sync, and one without
+// A - Closing down the database on application exit - in this case, you *do* want to save the icons out
+// B - Resetting the DB via removeAllIcons() - in this case, you *don't* want to sync, because it would be a waste of time
+void IconDatabase::deleteAllPreparedStatements(bool withSync)
+{
+ // Must wipe the initial retain statement before the initial transaction
delete m_preparedPageRetainInsertStatement;
+ m_preparedPageRetainInsertStatement = 0;
+ delete m_initialPruningTransaction;
+ m_initialPruningTransaction = 0;
- syncDatabase();
+ // Sync, if desired
+ if (withSync)
+ syncDatabase();
+
+ // Order doesn't matter on these
delete m_timeStampForIconURLStatement;
+ m_timeStampForIconURLStatement = 0;
delete m_iconURLForPageURLStatement;
+ m_iconURLForPageURLStatement = 0;
delete m_hasIconForIconURLStatement;
+ m_hasIconForIconURLStatement = 0;
delete m_forgetPageURLStatement;
+ m_forgetPageURLStatement = 0;
delete m_setIconIDForPageURLStatement;
+ m_setIconIDForPageURLStatement = 0;
delete m_getIconIDForIconURLStatement;
+ m_getIconIDForIconURLStatement = 0;
delete m_addIconForIconURLStatement;
+ m_addIconForIconURLStatement = 0;
delete m_imageDataForIconURLStatement;
- m_mainDB.close();
- m_privateBrowsingDB.close();
+ m_imageDataForIconURLStatement = 0;
}
bool IconDatabase::isEmpty()
return true;
}
-void IconDatabase::clearDatabaseTables(SQLDatabase& db)
-{
- String query = "SELECT name FROM sqlite_master WHERE type='table';";
- Vector<String> tables;
- if (!SQLStatement(db, query).returnTextResults16(0, tables)) {
- LOG(IconDatabase, "Unable to retrieve list of tables from database");
- return;
- }
-
- for (Vector<String>::iterator table = tables.begin(); table != tables.end(); ++table ) {
- if (!db.executeCommand("DROP TABLE " + *table)) {
- LOG(IconDatabase, "Unable to drop table %s", (*table).ascii().data());
- }
- }
-}
-
void IconDatabase::createDatabaseTables(SQLDatabase& db)
{
if (!db.executeCommand("CREATE TABLE PageURL (url TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,iconID INTEGER NOT NULL ON CONFLICT FAIL);")) {
createDatabaseTables(m_privateBrowsingDB);
m_currentDB = &m_privateBrowsingDB;
} else {
- clearDatabaseTables(m_privateBrowsingDB);
+ m_privateBrowsingDB.clearAllTables();
m_currentDB = &m_mainDB;
}
}
bool isOpen() { return m_mainDB.isOpen() && m_privateBrowsingDB.isOpen(); }
void close();
+ void removeAllIcons();
+
bool isEmpty();
Image* iconForPageURL(const String&, const IntSize&, bool cache = true);
void forgetIconForIconURLFromDatabase(const String&);
void setIconURLForPageURLInDatabase(const String&, const String&);
-
- // Wipe all icons from the DB
- void removeAllIcons();
-
+
// Called by the startup timer, this method removes all icons that are unretained
// after initial retains are complete, and pageURLs that are dangling
void pruneUnretainedIconsOnStartup(Timer<IconDatabase>*);
// Do a quick check to make sure the database tables are in place and the db version is current
bool isValidDatabase(SQLDatabase&);
-
- // Delete all tables from the given database
- void clearDatabaseTables(SQLDatabase&);
-
+
// Create the tables and triggers for the given database.
void createDatabaseTables(SQLDatabase&);
void imageDataForIconURLQuery(SQLDatabase& db, const String& iconURL, Vector<unsigned char>& result);
SQLStatement *m_imageDataForIconURLStatement;
+ void deleteAllPreparedStatements(bool withSync);
// FIXME: This method is currently implemented in WebCoreIconDatabaseBridge so we can be in ObjC++ and fire off a loader in Webkit
// Once all of the loader logic is sufficiently moved into WebCore we need to move this implementation to IconDatabase.cpp